summaryrefslogtreecommitdiff
path: root/js/dojo-1.6/dojox/data
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo-1.6/dojox/data')
-rw-r--r--js/dojo-1.6/dojox/data/AndOrReadStore.js1053
-rw-r--r--js/dojo-1.6/dojox/data/AndOrReadStore.xd.js1060
-rw-r--r--js/dojo-1.6/dojox/data/AndOrWriteStore.js820
-rw-r--r--js/dojo-1.6/dojox/data/AndOrWriteStore.xd.js825
-rw-r--r--js/dojo-1.6/dojox/data/AppStore.js841
-rw-r--r--js/dojo-1.6/dojox/data/AppStore.xd.js848
-rw-r--r--js/dojo-1.6/dojox/data/AtomReadStore.js563
-rw-r--r--js/dojo-1.6/dojox/data/AtomReadStore.xd.js570
-rw-r--r--js/dojo-1.6/dojox/data/CdfStore.js609
-rw-r--r--js/dojo-1.6/dojox/data/CdfStore.xd.js614
-rw-r--r--js/dojo-1.6/dojox/data/ClientFilter.js306
-rw-r--r--js/dojo-1.6/dojox/data/ClientFilter.xd.js311
-rw-r--r--js/dojo-1.6/dojox/data/CouchDBRestStore.js92
-rw-r--r--js/dojo-1.6/dojox/data/CouchDBRestStore.xd.js97
-rw-r--r--js/dojo-1.6/dojox/data/CssClassStore.js170
-rw-r--r--js/dojo-1.6/dojox/data/CssClassStore.xd.js175
-rw-r--r--js/dojo-1.6/dojox/data/CssRuleStore.js475
-rw-r--r--js/dojo-1.6/dojox/data/CssRuleStore.xd.js482
-rw-r--r--js/dojo-1.6/dojox/data/CsvStore.js737
-rw-r--r--js/dojo-1.6/dojox/data/CsvStore.xd.js743
-rw-r--r--js/dojo-1.6/dojox/data/FileStore.js441
-rw-r--r--js/dojo-1.6/dojox/data/FileStore.xd.js445
-rw-r--r--js/dojo-1.6/dojox/data/FlickrRestStore.js493
-rw-r--r--js/dojo-1.6/dojox/data/FlickrRestStore.xd.js498
-rw-r--r--js/dojo-1.6/dojox/data/FlickrStore.js296
-rw-r--r--js/dojo-1.6/dojox/data/FlickrStore.xd.js304
-rw-r--r--js/dojo-1.6/dojox/data/GoogleFeedStore.js88
-rw-r--r--js/dojo-1.6/dojox/data/GoogleFeedStore.xd.js93
-rw-r--r--js/dojo-1.6/dojox/data/GoogleSearchStore.js664
-rw-r--r--js/dojo-1.6/dojox/data/GoogleSearchStore.xd.js676
-rw-r--r--js/dojo-1.6/dojox/data/HtmlStore.js576
-rw-r--r--js/dojo-1.6/dojox/data/HtmlStore.xd.js583
-rw-r--r--js/dojo-1.6/dojox/data/HtmlTableStore.js482
-rw-r--r--js/dojo-1.6/dojox/data/HtmlTableStore.xd.js489
-rw-r--r--js/dojo-1.6/dojox/data/ItemExplorer.js637
-rw-r--r--js/dojo-1.6/dojox/data/ItemExplorer.xd.js649
-rw-r--r--js/dojo-1.6/dojox/data/JsonQueryRestStore.js28
-rw-r--r--js/dojo-1.6/dojox/data/JsonQueryRestStore.xd.js35
-rw-r--r--js/dojo-1.6/dojox/data/JsonRestStore.js494
-rw-r--r--js/dojo-1.6/dojox/data/JsonRestStore.xd.js500
-rw-r--r--js/dojo-1.6/dojox/data/KeyValueStore.js403
-rw-r--r--js/dojo-1.6/dojox/data/KeyValueStore.xd.js409
-rw-r--r--js/dojo-1.6/dojox/data/OpenSearchStore.js378
-rw-r--r--js/dojo-1.6/dojox/data/OpenSearchStore.xd.js385
-rw-r--r--js/dojo-1.6/dojox/data/OpmlStore.js534
-rw-r--r--js/dojo-1.6/dojox/data/OpmlStore.xd.js540
-rw-r--r--js/dojo-1.6/dojox/data/PersevereStore.js123
-rw-r--r--js/dojo-1.6/dojox/data/PersevereStore.xd.js131
-rw-r--r--js/dojo-1.6/dojox/data/PicasaStore.js284
-rw-r--r--js/dojo-1.6/dojox/data/PicasaStore.xd.js291
-rw-r--r--js/dojo-1.6/dojox/data/QueryReadStore.js530
-rw-r--r--js/dojo-1.6/dojox/data/QueryReadStore.xd.js536
-rw-r--r--js/dojo-1.6/dojox/data/README127
-rw-r--r--js/dojo-1.6/dojox/data/RailsStore.js179
-rw-r--r--js/dojo-1.6/dojox/data/RailsStore.xd.js184
-rw-r--r--js/dojo-1.6/dojox/data/S3Store.js47
-rw-r--r--js/dojo-1.6/dojox/data/S3Store.xd.js53
-rw-r--r--js/dojo-1.6/dojox/data/ServiceStore.js412
-rw-r--r--js/dojo-1.6/dojox/data/ServiceStore.xd.js416
-rw-r--r--js/dojo-1.6/dojox/data/SnapLogicStore.js339
-rw-r--r--js/dojo-1.6/dojox/data/SnapLogicStore.xd.js345
-rw-r--r--js/dojo-1.6/dojox/data/StoreExplorer.js202
-rw-r--r--js/dojo-1.6/dojox/data/StoreExplorer.xd.js210
-rw-r--r--js/dojo-1.6/dojox/data/WikipediaStore.js126
-rw-r--r--js/dojo-1.6/dojox/data/WikipediaStore.xd.js133
-rw-r--r--js/dojo-1.6/dojox/data/XmlStore.js1530
-rw-r--r--js/dojo-1.6/dojox/data/XmlStore.xd.js1538
-rw-r--r--js/dojo-1.6/dojox/data/css.js109
-rw-r--r--js/dojo-1.6/dojox/data/css.xd.js113
-rw-r--r--js/dojo-1.6/dojox/data/dom.js107
-rw-r--r--js/dojo-1.6/dojox/data/dom.xd.js112
-rw-r--r--js/dojo-1.6/dojox/data/restListener.js59
-rw-r--r--js/dojo-1.6/dojox/data/restListener.xd.js63
-rw-r--r--js/dojo-1.6/dojox/data/s3/README41
-rw-r--r--js/dojo-1.6/dojox/data/s3/proxy.example-php74
-rw-r--r--js/dojo-1.6/dojox/data/util/JsonQuery.js108
-rw-r--r--js/dojo-1.6/dojox/data/util/JsonQuery.xd.js112
77 files changed, 31145 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/data/AndOrReadStore.js b/js/dojo-1.6/dojox/data/AndOrReadStore.js
new file mode 100644
index 0000000..af2f4e3
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AndOrReadStore.js
@@ -0,0 +1,1053 @@
+/*
+ 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["dojox.data.AndOrReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AndOrReadStore"] = true;
+dojo.provide("dojox.data.AndOrReadStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+
+dojo.declare("dojox.data.AndOrReadStore", null,{
+ // summary:
+ // AndOrReadStore uses ItemFileReadStore as a base, modifying only the query (_fetchItems) section.
+ // Supports queries of the form: query:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"
+ // Includes legacy/widget support via:
+ // query:{complexQuery:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"}
+ // The ItemFileReadStore implements the dojo.data.api.Read API and reads
+ // data from JSON files that have contents in this format --
+ // { items: [
+ // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
+ // { name:'Fozzie Bear', wears:['hat', 'tie']},
+ // { name:'Miss Piggy', pets:'Foo-Foo'}
+ // ]}
+ // Note that it can also contain an 'identifer' property that specified which attribute on the items
+ // in the array of items that acts as the unique identifier for that item.
+ //
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: jsonObject}
+ // keywordParameters: {typeMap: object)
+ // The structure of the typeMap object is as follows:
+ // {
+ // type0: function || object,
+ // type1: function || object,
+ // ...
+ // typeN: function || object
+ // }
+ // Where if it is a function, it is assumed to be an object constructor that takes the
+ // value of _value as the initialization parameters. If it is an object, then it is assumed
+ // to be an object of general form:
+ // {
+ // type: function, //constructor.
+ // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
+ // }
+
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = [];
+ this._loadFinished = false;
+ this._jsonFileUrl = keywordParameters.url;
+ this._ccUrl = keywordParameters.url;
+ this.url = keywordParameters.url;
+ this._jsonData = keywordParameters.data;
+ this.data = null;
+ this._datatypeMap = keywordParameters.typeMap || {};
+ if(!this._datatypeMap['Date']){
+ //If no default mapping for dates, then set this as default.
+ //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
+ //of generically representing dates.
+ this._datatypeMap['Date'] = {
+ type: Date,
+ deserialize: function(value){
+ return dojo.date.stamp.fromISOString(value);
+ }
+ };
+ }
+ this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
+ this._itemsByIdentity = null;
+ this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
+ this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
+ this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
+ this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+
+ if(keywordParameters.urlPreventCache !== undefined){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ if(keywordParameters.hierarchical !== undefined){
+ this.hierarchical = keywordParameters.hierarchical?true:false;
+ }
+ if(keywordParameters.clearOnClose){
+ this.clearOnClose = true;
+ }
+ },
+
+ url: "", // use "" rather than undefined for the benefit of the parser (#3539)
+
+ //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
+ //when clearOnClose and close is used.
+ _ccUrl: "",
+
+ data: null, //Make this parser settable.
+
+ typeMap: null, //Make this parser settable.
+
+ //Parameter to allow users to specify if a close call should force a reload or not.
+ //By default, it retains the old behavior of not clearing if close is called. But
+ //if set true, the store will be reset to default state. Note that by doing this,
+ //all item handles will become invalid and a new fetch must be issued.
+ clearOnClose: false,
+
+ //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
+ //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
+ //Added for tracker: #6072
+ urlPreventCache: false,
+
+ //Parameter to indicate to process data from the url as hierarchical
+ //(data items can contain other data items in js form). Default is true
+ //for backwards compatibility. False means only root items are processed
+ //as items, all child objects outside of type-mapped objects and those in
+ //specific reference format, are left straight JS data objects.
+ hierarchical: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.AndOrReadStore: Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.AndOrReadStore: Invalid attribute argument.");
+ }
+ },
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; // mixed
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var arr = item[attribute] || [];
+ // Clone it before returning. refs: #10474
+ return arr.slice(0, arr.length); // Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var key in item){
+ // Save off only the real item attributes, not the special id marks for O(1) isItem.
+ if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
+ attributes.push(key);
+ }
+ }
+ return attributes; // Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ return (attribute in item);
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ return dojo.some(this.getValues(item, attribute), function(possibleValue){
+ if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
+ if(possibleValue.toString().match(regexp)){
+ return true; // Boolean
+ }
+ }else if(value === possibleValue){
+ return true; // Boolean
+ }
+ });
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeRefPropName] === this){
+ if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
+ return true;
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this._labelAttr && this.isItem(item)){
+ return this.getValue(item,this._labelAttr); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this._labelAttr){
+ return [this._labelAttr]; //array
+ }
+ return null; //null
+ },
+
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ // filter modified to permit complex queries where
+ // logical operators are case insensitive:
+ // , NOT AND OR ( ) ! && ||
+ // Note: "," included for quoted/string legacy queries.
+ var self = this;
+ var filter = function(requestArgs, arrayOfItems){
+ var items = [];
+ if(requestArgs.query){
+ //Complete copy, we may have to mess with it.
+ //Safer than clone, which does a shallow copy, I believe.
+ var query = dojo.fromJson(dojo.toJson(requestArgs.query));
+ //Okay, object form query, we have to check to see if someone mixed query methods (such as using FilteringSelect
+ //with a complexQuery). In that case, the params need to be anded to the complex query statement.
+ //See defect #7980
+ if(typeof query == "object" ){
+ var count = 0;
+ var p;
+ for(p in query){
+ count++;
+ }
+ if(count > 1 && query.complexQuery){
+ var cq = query.complexQuery;
+ var wrapped = false;
+ for(p in query){
+ if(p !== "complexQuery"){
+ //We should wrap this in () as it should and with the entire complex query
+ //Not just part of it.
+ if(!wrapped){
+ cq = "( " + cq + " )";
+ wrapped = true;
+ }
+ //Make sure strings are quoted when going into complexQuery merge.
+ var v = requestArgs.query[p];
+ if(dojo.isString(v)){
+ v = "'" + v + "'";
+ }
+ cq += " AND " + p + ":" + v;
+ delete query[p];
+
+ }
+ }
+ query.complexQuery = cq;
+ }
+ }
+
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+ //for complex queries only: pattern = query[:|=]"NOT id:23* AND (type:'test*' OR dept:'bob') && !filed:true"
+ //logical operators are case insensitive: , NOT AND OR ( ) ! && || // "," included for quoted/string legacy queries.
+ if(typeof query != "string"){
+ query = dojo.toJson(query);
+ query = query.replace(/\\\\/g,"\\"); //counter toJson expansion of backslashes, e.g., foo\\*bar test.
+ }
+ query = query.replace(/\\"/g,"\""); //ditto, for embedded \" in lieu of " availability.
+ var complexQuery = dojo.trim(query.replace(/{|}/g,"")); //we can handle these, too.
+ var pos2, i;
+ if(complexQuery.match(/"? *complexQuery *"?:/)){ //case where widget required a json object, so use complexQuery:'the real query'
+ complexQuery = dojo.trim(complexQuery.replace(/"?\s*complexQuery\s*"?:/,""));
+ var quotes = ["'",'"'];
+ var pos1,colon;
+ var flag = false;
+ for(i = 0; i<quotes.length; i++){
+ pos1 = complexQuery.indexOf(quotes[i]);
+ pos2 = complexQuery.indexOf(quotes[i],1);
+ colon = complexQuery.indexOf(":",1);
+ if(pos1 === 0 && pos2 != -1 && colon < pos2){
+ flag = true;
+ break;
+ } //first two sets of quotes don't occur before the first colon.
+ }
+ if(flag){ //dojo.toJson, and maybe user, adds surrounding quotes, which we need to remove.
+ complexQuery = complexQuery.replace(/^\"|^\'|\"$|\'$/g,"");
+ }
+ } //end query="{complexQuery:'id:1* || dept:Sales'}" parsing (for when widget required json object query).
+ var complexQuerySave = complexQuery;
+ //valid logical operators.
+ var begRegExp = /^,|^NOT |^AND |^OR |^\(|^\)|^!|^&&|^\|\|/i; //trailing space on some tokens on purpose.
+ var sQuery = ""; //will be eval'ed for each i-th candidateItem, based on query components.
+ var op = "";
+ var val = "";
+ var pos = -1;
+ var err = false;
+ var key = "";
+ var value = "";
+ var tok = "";
+ pos2 = -1;
+ for(i = 0; i < arrayOfItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfItems[i];
+ if(candidateItem === null){
+ match = false;
+ }else{
+ //process entire string for this i-th candidateItem.
+ complexQuery = complexQuerySave; //restore query for next candidateItem.
+ sQuery = "";
+ //work left to right, finding either key:value pair or logical operator at the beginning of the complexQuery string.
+ //when found, concatenate to sQuery and remove from complexQuery and loop back.
+ while(complexQuery.length > 0 && !err){
+ op = complexQuery.match(begRegExp);
+
+ //get/process/append one or two leading logical operators.
+ while(op && !err){ //look for leading logical operators.
+ complexQuery = dojo.trim(complexQuery.replace(op[0],""));
+ op = dojo.trim(op[0]).toUpperCase();
+ //convert some logical operators to their javascript equivalents for later eval.
+ op = op == "NOT" ? "!" : op == "AND" || op == "," ? "&&" : op == "OR" ? "||" : op;
+ op = " " + op + " ";
+ sQuery += op;
+ op = complexQuery.match(begRegExp);
+ }//end op && !err
+
+ //now get/process/append one key:value pair.
+ if(complexQuery.length > 0){
+ pos = complexQuery.indexOf(":");
+ if(pos == -1){
+ err = true;
+ break;
+ }else{
+ key = dojo.trim(complexQuery.substring(0,pos).replace(/\"|\'/g,""));
+ complexQuery = dojo.trim(complexQuery.substring(pos + 1));
+ tok = complexQuery.match(/^\'|^\"/); //quoted?
+ if(tok){
+ tok = tok[0];
+ pos = complexQuery.indexOf(tok);
+ pos2 = complexQuery.indexOf(tok,pos + 1);
+ if(pos2 == -1){
+ err = true;
+ break;
+ }
+ value = complexQuery.substring(pos + 1,pos2);
+ if(pos2 == complexQuery.length - 1){ //quote is last character
+ complexQuery = "";
+ }else{
+ complexQuery = dojo.trim(complexQuery.substring(pos2 + 1));
+ }
+ sQuery += self._containsValue(candidateItem, key, value, dojo.data.util.filter.patternToRegExp(value, ignoreCase));
+ }
+ else{ //not quoted, so a space, comma, or closing parens (or the end) will be the break.
+ tok = complexQuery.match(/\s|\)|,/);
+ if(tok){
+ var pos3 = new Array(tok.length);
+ for(var j = 0;j<tok.length;j++){
+ pos3[j] = complexQuery.indexOf(tok[j]);
+ }
+ pos = pos3[0];
+ if(pos3.length > 1){
+ for(var j=1;j<pos3.length;j++){
+ pos = Math.min(pos,pos3[j]);
+ }
+ }
+ value = dojo.trim(complexQuery.substring(0,pos));
+ complexQuery = dojo.trim(complexQuery.substring(pos));
+ }else{ //not a space, so must be at the end of the complexQuery.
+ value = dojo.trim(complexQuery);
+ complexQuery = "";
+ } //end inner if(tok) else
+ sQuery += self._containsValue(candidateItem, key, value, dojo.data.util.filter.patternToRegExp(value, ignoreCase));
+ } //end outer if(tok) else
+ } //end found ":"
+ } //end if(complexQuery.length > 0)
+ } //end while complexQuery.length > 0 && !err, so finished the i-th item.
+ match = eval(sQuery);
+ } //end else is non-null candidateItem.
+ if(match){
+ items.push(candidateItem);
+ }
+ } //end for/next of all items.
+ if(err){
+ //soft fail.
+ items = [];
+ console.log("The store's _fetchItems failed, probably due to a syntax error in query.");
+ }
+ findCallback(items, requestArgs);
+ }else{
+ // No query...
+ // We want a copy to pass back in case the parent wishes to sort the array.
+ // We shouldn't allow resort of the internal list, so that multiple callers
+ // can get lists and sort without affecting each other. We also need to
+ // filter out any null values that have been left as a result of deleteItem()
+ // calls in ItemFileWriteStore.
+ for(var i = 0; i < arrayOfItems.length; ++i){
+ var item = arrayOfItems[i];
+ if(item !== null){
+ items.push(item);
+ }
+ }
+ findCallback(items, requestArgs);
+ } //end if there is a query.
+ }; //end filter function
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ self._loadInProgress = false;
+
+ filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
+ self._handleQueuedFetches();
+ }catch(e){
+ self._loadFinished = true;
+ self._loadInProgress = false;
+ errorCallback(e, keywordArgs);
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ errorCallback(error, keywordArgs);
+ });
+
+ //Wire up the cancel to abort of the request
+ //This call cancel on the deferred if it hasn't been called
+ //yet and then will chain to the simple abort of the
+ //simpleFetch keywordArgs
+ var oldAbort = null;
+ if(keywordArgs.abort){
+ oldAbort = keywordArgs.abort;
+ }
+ keywordArgs.abort = function(){
+ var df = getHandler;
+ if(df && df.fired === -1){
+ df.cancel();
+ df = null;
+ }
+ if(oldAbort){
+ oldAbort.call(keywordArgs);
+ }
+ };
+ }
+ }else if(this._jsonData){
+ try{
+ this._loadFinished = true;
+ this._getItemsFromLoadedData(this._jsonData);
+ this._jsonData = null;
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ }else{
+ errorCallback(new Error("dojox.data.AndOrReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
+ }
+ } //end deferred fetching.
+ }, //end _fetchItems
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedQuery = fData.args;
+ var delayedFilter = fData.filter;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
+ }else{
+ this.fetchItemByIdentity(delayedQuery);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ _getItemsArray: function(/*object?*/queryOptions){
+ // summary:
+ // Internal function to determine which list of items to search over.
+ // queryOptions: The query options parameter, if any.
+ if(queryOptions && queryOptions.deep){
+ return this._arrayOfAllItems;
+ }
+ return this._arrayOfTopLevelItems;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ if(this.clearOnClose &&
+ this._loadFinished &&
+ !this._loadInProgress){
+ //Reset all internalsback to default state. This will force a reload
+ //on next fetch. This also checks that the data or url param was set
+ //so that the store knows it can get data. Without one of those being set,
+ //the next fetch will trigger an error.
+
+ if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
+ (this.url == "" || this.url == null)
+ ) && this.data == null){
+ console.debug("dojox.data.AndOrReadStore: WARNING! Data reload " +
+ " information has not been provided." +
+ " Please set 'url' or 'data' to the appropriate value before" +
+ " the next fetch");
+ }
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = [];
+ this._loadFinished = false;
+ this._itemsByIdentity = null;
+ this._loadInProgress = false;
+ this._queuedFetches = [];
+ }
+ },
+
+ _getItemsFromLoadedData: function(/* Object */ dataObject){
+ // summary:
+ // Function to parse the loaded data into item format and build the internal items array.
+ // description:
+ // Function to parse the loaded data into item format and build the internal items array.
+ //
+ // dataObject:
+ // The JS data object containing the raw data to convery into item format.
+ //
+ // returns: array
+ // Array of items in store item format.
+
+ // First, we define a couple little utility functions...
+
+ var self = this;
+ function valueIsAnItem(/* anything */ aValue){
+ // summary:
+ // Given any sort of value that could be in the raw json data,
+ // return true if we should interpret the value as being an
+ // item itself, rather than a literal value or a reference.
+ // example:
+ // | false == valueIsAnItem("Kermit");
+ // | false == valueIsAnItem(42);
+ // | false == valueIsAnItem(new Date());
+ // | false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
+ // | false == valueIsAnItem({_reference:'Kermit'});
+ // | true == valueIsAnItem({name:'Kermit', color:'green'});
+ // | true == valueIsAnItem({iggy:'pop'});
+ // | true == valueIsAnItem({foo:42});
+ var isItem = (
+ (aValue !== null) &&
+ (typeof aValue === "object") &&
+ (!dojo.isArray(aValue)) &&
+ (!dojo.isFunction(aValue)) &&
+ (aValue.constructor == Object) &&
+ (typeof aValue._reference === "undefined") &&
+ (typeof aValue._type === "undefined") &&
+ (typeof aValue._value === "undefined") &&
+ self.hierarchical
+ );
+ return isItem;
+ }
+
+ function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
+ self._arrayOfAllItems.push(anItem);
+ for(var attribute in anItem){
+ var valueForAttribute = anItem[attribute];
+ if(valueForAttribute){
+ if(dojo.isArray(valueForAttribute)){
+ var valueArray = valueForAttribute;
+ for(var k = 0; k < valueArray.length; ++k){
+ var singleValue = valueArray[k];
+ if(valueIsAnItem(singleValue)){
+ addItemAndSubItemsToArrayOfAllItems(singleValue);
+ }
+ }
+ }else{
+ if(valueIsAnItem(valueForAttribute)){
+ addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
+ }
+ }
+ }
+ }
+ }
+
+ this._labelAttr = dataObject.label;
+
+ // We need to do some transformations to convert the data structure
+ // that we read from the file into a format that will be convenient
+ // to work with in memory.
+
+ // Step 1: Walk through the object hierarchy and build a list of all items
+ var i;
+ var item;
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = dataObject.items;
+
+ for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
+ item = this._arrayOfTopLevelItems[i];
+ addItemAndSubItemsToArrayOfAllItems(item);
+ item[this._rootItemPropName]=true;
+ }
+
+ // Step 2: Walk through all the attribute values of all the items,
+ // and replace single values with arrays. For example, we change this:
+ // { name:'Miss Piggy', pets:'Foo-Foo'}
+ // into this:
+ // { name:['Miss Piggy'], pets:['Foo-Foo']}
+ //
+ // We also store the attribute names so we can validate our store
+ // reference and item id special properties for the O(1) isItem
+ var allAttributeNames = {};
+ var key;
+
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ for(key in item){
+ if(key !== this._rootItemPropName){
+ var value = item[key];
+ if(value !== null){
+ if(!dojo.isArray(value)){
+ item[key] = [value];
+ }
+ }else{
+ item[key] = [null];
+ }
+ }
+ allAttributeNames[key]=key;
+ }
+ }
+
+ // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
+ // This should go really fast, it will generally never even run the loop.
+ while(allAttributeNames[this._storeRefPropName]){
+ this._storeRefPropName += "_";
+ }
+ while(allAttributeNames[this._itemNumPropName]){
+ this._itemNumPropName += "_";
+ }
+ while(allAttributeNames[this._reverseRefMap]){
+ this._reverseRefMap += "_";
+ }
+
+ // Step 4: Some data files specify an optional 'identifier', which is
+ // the name of an attribute that holds the identity of each item.
+ // If this data file specified an identifier attribute, then build a
+ // hash table of items keyed by the identity of the items.
+ var arrayOfValues;
+
+ var identifier = dataObject.identifier;
+ if(identifier){
+ this._itemsByIdentity = {};
+ this._features['dojo.data.api.Identity'] = identifier;
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ arrayOfValues = item[identifier];
+ var identity = arrayOfValues[0];
+ if(!this._itemsByIdentity[identity]){
+ this._itemsByIdentity[identity] = item;
+ }else{
+ if(this._jsonFileUrl){
+ throw new Error("dojox.data.AndOrReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }else if(this._jsonData){
+ throw new Error("dojox.data.AndOrReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }
+ }
+ }
+ }else{
+ this._features['dojo.data.api.Identity'] = Number;
+ }
+
+ // Step 5: Walk through all the items, and set each item's properties
+ // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ item[this._storeRefPropName] = this;
+ item[this._itemNumPropName] = i;
+ }
+
+ // Step 6: We walk through all the attribute values of all the items,
+ // looking for type/value literals and item-references.
+ //
+ // We replace item-references with pointers to items. For example, we change:
+ // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ // into this:
+ // { name:['Kermit'], friends:[miss_piggy] }
+ // (where miss_piggy is the object representing the 'Miss Piggy' item).
+ //
+ // We replace type/value pairs with typed-literals. For example, we change:
+ // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
+ // into this:
+ // { name:['Kermit'], born:(new Date('July 18, 1918')) }
+ //
+ // We also generate the associate map for all items for the O(1) isItem function.
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ for(key in item){
+ arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
+ for(var j = 0; j < arrayOfValues.length; ++j){
+ value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
+ if(value !== null && typeof value == "object"){
+ if(("_type" in value) && ("_value" in value)){
+ var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
+ var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
+ if(!mappingObj){
+ throw new Error("dojox.data.AndOrReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
+ }else if(dojo.isFunction(mappingObj)){
+ arrayOfValues[j] = new mappingObj(value._value);
+ }else if(dojo.isFunction(mappingObj.deserialize)){
+ arrayOfValues[j] = mappingObj.deserialize(value._value);
+ }else{
+ throw new Error("dojox.data.AndOrReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
+ }
+ }
+ if(value._reference){
+ var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
+ if(!dojo.isObject(referenceDescription)){
+ // example: 'Miss Piggy'
+ // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
+ arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
+ }else{
+ // example: {name:'Miss Piggy'}
+ // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ for(var k = 0; k < this._arrayOfAllItems.length; ++k){
+ var candidateItem = this._arrayOfAllItems[k];
+ var found = true;
+ for(var refKey in referenceDescription){
+ if(candidateItem[refKey] != referenceDescription[refKey]){
+ found = false;
+ }
+ }
+ if(found){
+ arrayOfValues[j] = candidateItem;
+ }
+ }
+ }
+ if(this.referenceIntegrity){
+ var refItem = arrayOfValues[j];
+ if(this.isItem(refItem)){
+ this._addReferenceToMap(refItem, item, key);
+ }
+ }
+ }else if(this.isItem(value)){
+ //It's a child item (not one referenced through _reference).
+ //We need to treat this as a referenced item, so it can be cleaned up
+ //in a write store easily.
+ if(this.referenceIntegrity){
+ this._addReferenceToMap(value, item, key);
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
+ // summary:
+ // Method to add an reference map entry for an item and attribute.
+ // description:
+ // Method to add an reference map entry for an item and attribute. //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item that holds the new reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the new reference.
+
+ //Stub function, does nothing. Real processing is in ItemFileWriteStore.
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ var identifier = this._features['dojo.data.api.Identity'];
+ if(identifier === Number){
+ return item[this._itemNumPropName]; // Number
+ }else{
+ var arrayOfValues = item[identifier];
+ if(arrayOfValues){
+ return arrayOfValues[0]; // Object || String
+ }
+ }
+ return null; // null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ // Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ try{
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ self._loadInProgress = false;
+ var item = self._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ self._loadInProgress = false;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+
+ }else if(this._jsonData){
+ // Passed in data, no need to xhr.
+ self._getItemsFromLoadedData(self._jsonData);
+ self._jsonData = null;
+ self._loadFinished = true;
+ var item = self._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }else{
+ // Already loaded. We can just look it up and call back.
+ var item = this._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ _getItemByIdentity: function(/* Object */ identity){
+ // summary:
+ // Internal function to look an item up by its identity map.
+ var item = null;
+ if(this._itemsByIdentity){
+ item = this._itemsByIdentity[identity];
+ }else{
+ item = this._arrayOfAllItems[identity];
+ }
+ if(item === undefined){
+ item = null;
+ }
+ return item; // Object
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+
+ var identifier = this._features['dojo.data.api.Identity'];
+ if(identifier === Number){
+ // If (identifier === Number) it means getIdentity() just returns
+ // an integer item-number for each item. The dojo.data.api.Identity
+ // spec says we need to return null if the identity is not composed
+ // of attributes
+ return null; // null
+ }else{
+ return [identifier]; // Array
+ }
+ },
+
+ _forceLoad: function(){
+ // summary:
+ // Internal function to force a load of the store if it hasn't occurred yet. This is required
+ // for specific functions to work properly.
+ var self = this;
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache,
+ sync: true
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ //Check to be sure there wasn't another load going on concurrently
+ //So we don't clobber data that comes in on it. If there is a load going on
+ //then do not save this data. It will potentially clobber current data.
+ //We mainly wanted to sync/wait here.
+ //TODO: Revisit the loading scheme of this store to improve multi-initial
+ //request handling.
+ if(self._loadInProgress !== true && !self._loadFinished){
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ }else if(self._loadInProgress){
+ //Okay, we hit an error state we can't recover from. A forced load occurred
+ //while an async load was occurring. Since we cannot block at this point, the best
+ //that can be managed is to throw an error.
+ throw new Error("dojox.data.AndOrReadStore: Unable to perform a synchronous load, an async load is in progress.");
+ }
+ }catch(e){
+ console.log(e);
+ throw e;
+ }
+ });
+ getHandler.addErrback(function(error){
+ throw error;
+ });
+ }else if(this._jsonData){
+ self._getItemsFromLoadedData(self._jsonData);
+ self._jsonData = null;
+ self._loadFinished = true;
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.AndOrReadStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/AndOrReadStore.xd.js b/js/dojo-1.6/dojox/data/AndOrReadStore.xd.js
new file mode 100644
index 0000000..50f63d2
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AndOrReadStore.xd.js
@@ -0,0 +1,1060 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.AndOrReadStore"],
+["require", "dojo.data.util.filter"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.date.stamp"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.AndOrReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AndOrReadStore"] = true;
+dojo.provide("dojox.data.AndOrReadStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+
+dojo.declare("dojox.data.AndOrReadStore", null,{
+ // summary:
+ // AndOrReadStore uses ItemFileReadStore as a base, modifying only the query (_fetchItems) section.
+ // Supports queries of the form: query:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"
+ // Includes legacy/widget support via:
+ // query:{complexQuery:"id:1* OR dept:'Sales Department' || (id:2* && NOT dept:S*)"}
+ // The ItemFileReadStore implements the dojo.data.api.Read API and reads
+ // data from JSON files that have contents in this format --
+ // { items: [
+ // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
+ // { name:'Fozzie Bear', wears:['hat', 'tie']},
+ // { name:'Miss Piggy', pets:'Foo-Foo'}
+ // ]}
+ // Note that it can also contain an 'identifer' property that specified which attribute on the items
+ // in the array of items that acts as the unique identifier for that item.
+ //
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: jsonObject}
+ // keywordParameters: {typeMap: object)
+ // The structure of the typeMap object is as follows:
+ // {
+ // type0: function || object,
+ // type1: function || object,
+ // ...
+ // typeN: function || object
+ // }
+ // Where if it is a function, it is assumed to be an object constructor that takes the
+ // value of _value as the initialization parameters. If it is an object, then it is assumed
+ // to be an object of general form:
+ // {
+ // type: function, //constructor.
+ // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
+ // }
+
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = [];
+ this._loadFinished = false;
+ this._jsonFileUrl = keywordParameters.url;
+ this._ccUrl = keywordParameters.url;
+ this.url = keywordParameters.url;
+ this._jsonData = keywordParameters.data;
+ this.data = null;
+ this._datatypeMap = keywordParameters.typeMap || {};
+ if(!this._datatypeMap['Date']){
+ //If no default mapping for dates, then set this as default.
+ //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
+ //of generically representing dates.
+ this._datatypeMap['Date'] = {
+ type: Date,
+ deserialize: function(value){
+ return dojo.date.stamp.fromISOString(value);
+ }
+ };
+ }
+ this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
+ this._itemsByIdentity = null;
+ this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
+ this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
+ this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
+ this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+
+ if(keywordParameters.urlPreventCache !== undefined){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ if(keywordParameters.hierarchical !== undefined){
+ this.hierarchical = keywordParameters.hierarchical?true:false;
+ }
+ if(keywordParameters.clearOnClose){
+ this.clearOnClose = true;
+ }
+ },
+
+ url: "", // use "" rather than undefined for the benefit of the parser (#3539)
+
+ //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
+ //when clearOnClose and close is used.
+ _ccUrl: "",
+
+ data: null, //Make this parser settable.
+
+ typeMap: null, //Make this parser settable.
+
+ //Parameter to allow users to specify if a close call should force a reload or not.
+ //By default, it retains the old behavior of not clearing if close is called. But
+ //if set true, the store will be reset to default state. Note that by doing this,
+ //all item handles will become invalid and a new fetch must be issued.
+ clearOnClose: false,
+
+ //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
+ //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
+ //Added for tracker: #6072
+ urlPreventCache: false,
+
+ //Parameter to indicate to process data from the url as hierarchical
+ //(data items can contain other data items in js form). Default is true
+ //for backwards compatibility. False means only root items are processed
+ //as items, all child objects outside of type-mapped objects and those in
+ //specific reference format, are left straight JS data objects.
+ hierarchical: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.AndOrReadStore: Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.AndOrReadStore: Invalid attribute argument.");
+ }
+ },
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; // mixed
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var arr = item[attribute] || [];
+ // Clone it before returning. refs: #10474
+ return arr.slice(0, arr.length); // Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var key in item){
+ // Save off only the real item attributes, not the special id marks for O(1) isItem.
+ if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
+ attributes.push(key);
+ }
+ }
+ return attributes; // Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ return (attribute in item);
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ return dojo.some(this.getValues(item, attribute), function(possibleValue){
+ if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
+ if(possibleValue.toString().match(regexp)){
+ return true; // Boolean
+ }
+ }else if(value === possibleValue){
+ return true; // Boolean
+ }
+ });
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeRefPropName] === this){
+ if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
+ return true;
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this._labelAttr && this.isItem(item)){
+ return this.getValue(item,this._labelAttr); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this._labelAttr){
+ return [this._labelAttr]; //array
+ }
+ return null; //null
+ },
+
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ // filter modified to permit complex queries where
+ // logical operators are case insensitive:
+ // , NOT AND OR ( ) ! && ||
+ // Note: "," included for quoted/string legacy queries.
+ var self = this;
+ var filter = function(requestArgs, arrayOfItems){
+ var items = [];
+ if(requestArgs.query){
+ //Complete copy, we may have to mess with it.
+ //Safer than clone, which does a shallow copy, I believe.
+ var query = dojo.fromJson(dojo.toJson(requestArgs.query));
+ //Okay, object form query, we have to check to see if someone mixed query methods (such as using FilteringSelect
+ //with a complexQuery). In that case, the params need to be anded to the complex query statement.
+ //See defect #7980
+ if(typeof query == "object" ){
+ var count = 0;
+ var p;
+ for(p in query){
+ count++;
+ }
+ if(count > 1 && query.complexQuery){
+ var cq = query.complexQuery;
+ var wrapped = false;
+ for(p in query){
+ if(p !== "complexQuery"){
+ //We should wrap this in () as it should and with the entire complex query
+ //Not just part of it.
+ if(!wrapped){
+ cq = "( " + cq + " )";
+ wrapped = true;
+ }
+ //Make sure strings are quoted when going into complexQuery merge.
+ var v = requestArgs.query[p];
+ if(dojo.isString(v)){
+ v = "'" + v + "'";
+ }
+ cq += " AND " + p + ":" + v;
+ delete query[p];
+
+ }
+ }
+ query.complexQuery = cq;
+ }
+ }
+
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+ //for complex queries only: pattern = query[:|=]"NOT id:23* AND (type:'test*' OR dept:'bob') && !filed:true"
+ //logical operators are case insensitive: , NOT AND OR ( ) ! && || // "," included for quoted/string legacy queries.
+ if(typeof query != "string"){
+ query = dojo.toJson(query);
+ query = query.replace(/\\\\/g,"\\"); //counter toJson expansion of backslashes, e.g., foo\\*bar test.
+ }
+ query = query.replace(/\\"/g,"\""); //ditto, for embedded \" in lieu of " availability.
+ var complexQuery = dojo.trim(query.replace(/{|}/g,"")); //we can handle these, too.
+ var pos2, i;
+ if(complexQuery.match(/"? *complexQuery *"?:/)){ //case where widget required a json object, so use complexQuery:'the real query'
+ complexQuery = dojo.trim(complexQuery.replace(/"?\s*complexQuery\s*"?:/,""));
+ var quotes = ["'",'"'];
+ var pos1,colon;
+ var flag = false;
+ for(i = 0; i<quotes.length; i++){
+ pos1 = complexQuery.indexOf(quotes[i]);
+ pos2 = complexQuery.indexOf(quotes[i],1);
+ colon = complexQuery.indexOf(":",1);
+ if(pos1 === 0 && pos2 != -1 && colon < pos2){
+ flag = true;
+ break;
+ } //first two sets of quotes don't occur before the first colon.
+ }
+ if(flag){ //dojo.toJson, and maybe user, adds surrounding quotes, which we need to remove.
+ complexQuery = complexQuery.replace(/^\"|^\'|\"$|\'$/g,"");
+ }
+ } //end query="{complexQuery:'id:1* || dept:Sales'}" parsing (for when widget required json object query).
+ var complexQuerySave = complexQuery;
+ //valid logical operators.
+ var begRegExp = /^,|^NOT |^AND |^OR |^\(|^\)|^!|^&&|^\|\|/i; //trailing space on some tokens on purpose.
+ var sQuery = ""; //will be eval'ed for each i-th candidateItem, based on query components.
+ var op = "";
+ var val = "";
+ var pos = -1;
+ var err = false;
+ var key = "";
+ var value = "";
+ var tok = "";
+ pos2 = -1;
+ for(i = 0; i < arrayOfItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfItems[i];
+ if(candidateItem === null){
+ match = false;
+ }else{
+ //process entire string for this i-th candidateItem.
+ complexQuery = complexQuerySave; //restore query for next candidateItem.
+ sQuery = "";
+ //work left to right, finding either key:value pair or logical operator at the beginning of the complexQuery string.
+ //when found, concatenate to sQuery and remove from complexQuery and loop back.
+ while(complexQuery.length > 0 && !err){
+ op = complexQuery.match(begRegExp);
+
+ //get/process/append one or two leading logical operators.
+ while(op && !err){ //look for leading logical operators.
+ complexQuery = dojo.trim(complexQuery.replace(op[0],""));
+ op = dojo.trim(op[0]).toUpperCase();
+ //convert some logical operators to their javascript equivalents for later eval.
+ op = op == "NOT" ? "!" : op == "AND" || op == "," ? "&&" : op == "OR" ? "||" : op;
+ op = " " + op + " ";
+ sQuery += op;
+ op = complexQuery.match(begRegExp);
+ }//end op && !err
+
+ //now get/process/append one key:value pair.
+ if(complexQuery.length > 0){
+ pos = complexQuery.indexOf(":");
+ if(pos == -1){
+ err = true;
+ break;
+ }else{
+ key = dojo.trim(complexQuery.substring(0,pos).replace(/\"|\'/g,""));
+ complexQuery = dojo.trim(complexQuery.substring(pos + 1));
+ tok = complexQuery.match(/^\'|^\"/); //quoted?
+ if(tok){
+ tok = tok[0];
+ pos = complexQuery.indexOf(tok);
+ pos2 = complexQuery.indexOf(tok,pos + 1);
+ if(pos2 == -1){
+ err = true;
+ break;
+ }
+ value = complexQuery.substring(pos + 1,pos2);
+ if(pos2 == complexQuery.length - 1){ //quote is last character
+ complexQuery = "";
+ }else{
+ complexQuery = dojo.trim(complexQuery.substring(pos2 + 1));
+ }
+ sQuery += self._containsValue(candidateItem, key, value, dojo.data.util.filter.patternToRegExp(value, ignoreCase));
+ }
+ else{ //not quoted, so a space, comma, or closing parens (or the end) will be the break.
+ tok = complexQuery.match(/\s|\)|,/);
+ if(tok){
+ var pos3 = new Array(tok.length);
+ for(var j = 0;j<tok.length;j++){
+ pos3[j] = complexQuery.indexOf(tok[j]);
+ }
+ pos = pos3[0];
+ if(pos3.length > 1){
+ for(var j=1;j<pos3.length;j++){
+ pos = Math.min(pos,pos3[j]);
+ }
+ }
+ value = dojo.trim(complexQuery.substring(0,pos));
+ complexQuery = dojo.trim(complexQuery.substring(pos));
+ }else{ //not a space, so must be at the end of the complexQuery.
+ value = dojo.trim(complexQuery);
+ complexQuery = "";
+ } //end inner if(tok) else
+ sQuery += self._containsValue(candidateItem, key, value, dojo.data.util.filter.patternToRegExp(value, ignoreCase));
+ } //end outer if(tok) else
+ } //end found ":"
+ } //end if(complexQuery.length > 0)
+ } //end while complexQuery.length > 0 && !err, so finished the i-th item.
+ match = eval(sQuery);
+ } //end else is non-null candidateItem.
+ if(match){
+ items.push(candidateItem);
+ }
+ } //end for/next of all items.
+ if(err){
+ //soft fail.
+ items = [];
+ console.log("The store's _fetchItems failed, probably due to a syntax error in query.");
+ }
+ findCallback(items, requestArgs);
+ }else{
+ // No query...
+ // We want a copy to pass back in case the parent wishes to sort the array.
+ // We shouldn't allow resort of the internal list, so that multiple callers
+ // can get lists and sort without affecting each other. We also need to
+ // filter out any null values that have been left as a result of deleteItem()
+ // calls in ItemFileWriteStore.
+ for(var i = 0; i < arrayOfItems.length; ++i){
+ var item = arrayOfItems[i];
+ if(item !== null){
+ items.push(item);
+ }
+ }
+ findCallback(items, requestArgs);
+ } //end if there is a query.
+ }; //end filter function
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ self._loadInProgress = false;
+
+ filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
+ self._handleQueuedFetches();
+ }catch(e){
+ self._loadFinished = true;
+ self._loadInProgress = false;
+ errorCallback(e, keywordArgs);
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ errorCallback(error, keywordArgs);
+ });
+
+ //Wire up the cancel to abort of the request
+ //This call cancel on the deferred if it hasn't been called
+ //yet and then will chain to the simple abort of the
+ //simpleFetch keywordArgs
+ var oldAbort = null;
+ if(keywordArgs.abort){
+ oldAbort = keywordArgs.abort;
+ }
+ keywordArgs.abort = function(){
+ var df = getHandler;
+ if(df && df.fired === -1){
+ df.cancel();
+ df = null;
+ }
+ if(oldAbort){
+ oldAbort.call(keywordArgs);
+ }
+ };
+ }
+ }else if(this._jsonData){
+ try{
+ this._loadFinished = true;
+ this._getItemsFromLoadedData(this._jsonData);
+ this._jsonData = null;
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ }else{
+ errorCallback(new Error("dojox.data.AndOrReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
+ }
+ } //end deferred fetching.
+ }, //end _fetchItems
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedQuery = fData.args;
+ var delayedFilter = fData.filter;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
+ }else{
+ this.fetchItemByIdentity(delayedQuery);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ _getItemsArray: function(/*object?*/queryOptions){
+ // summary:
+ // Internal function to determine which list of items to search over.
+ // queryOptions: The query options parameter, if any.
+ if(queryOptions && queryOptions.deep){
+ return this._arrayOfAllItems;
+ }
+ return this._arrayOfTopLevelItems;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ if(this.clearOnClose &&
+ this._loadFinished &&
+ !this._loadInProgress){
+ //Reset all internalsback to default state. This will force a reload
+ //on next fetch. This also checks that the data or url param was set
+ //so that the store knows it can get data. Without one of those being set,
+ //the next fetch will trigger an error.
+
+ if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
+ (this.url == "" || this.url == null)
+ ) && this.data == null){
+ console.debug("dojox.data.AndOrReadStore: WARNING! Data reload " +
+ " information has not been provided." +
+ " Please set 'url' or 'data' to the appropriate value before" +
+ " the next fetch");
+ }
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = [];
+ this._loadFinished = false;
+ this._itemsByIdentity = null;
+ this._loadInProgress = false;
+ this._queuedFetches = [];
+ }
+ },
+
+ _getItemsFromLoadedData: function(/* Object */ dataObject){
+ // summary:
+ // Function to parse the loaded data into item format and build the internal items array.
+ // description:
+ // Function to parse the loaded data into item format and build the internal items array.
+ //
+ // dataObject:
+ // The JS data object containing the raw data to convery into item format.
+ //
+ // returns: array
+ // Array of items in store item format.
+
+ // First, we define a couple little utility functions...
+
+ var self = this;
+ function valueIsAnItem(/* anything */ aValue){
+ // summary:
+ // Given any sort of value that could be in the raw json data,
+ // return true if we should interpret the value as being an
+ // item itself, rather than a literal value or a reference.
+ // example:
+ // | false == valueIsAnItem("Kermit");
+ // | false == valueIsAnItem(42);
+ // | false == valueIsAnItem(new Date());
+ // | false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
+ // | false == valueIsAnItem({_reference:'Kermit'});
+ // | true == valueIsAnItem({name:'Kermit', color:'green'});
+ // | true == valueIsAnItem({iggy:'pop'});
+ // | true == valueIsAnItem({foo:42});
+ var isItem = (
+ (aValue !== null) &&
+ (typeof aValue === "object") &&
+ (!dojo.isArray(aValue)) &&
+ (!dojo.isFunction(aValue)) &&
+ (aValue.constructor == Object) &&
+ (typeof aValue._reference === "undefined") &&
+ (typeof aValue._type === "undefined") &&
+ (typeof aValue._value === "undefined") &&
+ self.hierarchical
+ );
+ return isItem;
+ }
+
+ function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
+ self._arrayOfAllItems.push(anItem);
+ for(var attribute in anItem){
+ var valueForAttribute = anItem[attribute];
+ if(valueForAttribute){
+ if(dojo.isArray(valueForAttribute)){
+ var valueArray = valueForAttribute;
+ for(var k = 0; k < valueArray.length; ++k){
+ var singleValue = valueArray[k];
+ if(valueIsAnItem(singleValue)){
+ addItemAndSubItemsToArrayOfAllItems(singleValue);
+ }
+ }
+ }else{
+ if(valueIsAnItem(valueForAttribute)){
+ addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
+ }
+ }
+ }
+ }
+ }
+
+ this._labelAttr = dataObject.label;
+
+ // We need to do some transformations to convert the data structure
+ // that we read from the file into a format that will be convenient
+ // to work with in memory.
+
+ // Step 1: Walk through the object hierarchy and build a list of all items
+ var i;
+ var item;
+ this._arrayOfAllItems = [];
+ this._arrayOfTopLevelItems = dataObject.items;
+
+ for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
+ item = this._arrayOfTopLevelItems[i];
+ addItemAndSubItemsToArrayOfAllItems(item);
+ item[this._rootItemPropName]=true;
+ }
+
+ // Step 2: Walk through all the attribute values of all the items,
+ // and replace single values with arrays. For example, we change this:
+ // { name:'Miss Piggy', pets:'Foo-Foo'}
+ // into this:
+ // { name:['Miss Piggy'], pets:['Foo-Foo']}
+ //
+ // We also store the attribute names so we can validate our store
+ // reference and item id special properties for the O(1) isItem
+ var allAttributeNames = {};
+ var key;
+
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ for(key in item){
+ if(key !== this._rootItemPropName){
+ var value = item[key];
+ if(value !== null){
+ if(!dojo.isArray(value)){
+ item[key] = [value];
+ }
+ }else{
+ item[key] = [null];
+ }
+ }
+ allAttributeNames[key]=key;
+ }
+ }
+
+ // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
+ // This should go really fast, it will generally never even run the loop.
+ while(allAttributeNames[this._storeRefPropName]){
+ this._storeRefPropName += "_";
+ }
+ while(allAttributeNames[this._itemNumPropName]){
+ this._itemNumPropName += "_";
+ }
+ while(allAttributeNames[this._reverseRefMap]){
+ this._reverseRefMap += "_";
+ }
+
+ // Step 4: Some data files specify an optional 'identifier', which is
+ // the name of an attribute that holds the identity of each item.
+ // If this data file specified an identifier attribute, then build a
+ // hash table of items keyed by the identity of the items.
+ var arrayOfValues;
+
+ var identifier = dataObject.identifier;
+ if(identifier){
+ this._itemsByIdentity = {};
+ this._features['dojo.data.api.Identity'] = identifier;
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ arrayOfValues = item[identifier];
+ var identity = arrayOfValues[0];
+ if(!this._itemsByIdentity[identity]){
+ this._itemsByIdentity[identity] = item;
+ }else{
+ if(this._jsonFileUrl){
+ throw new Error("dojox.data.AndOrReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }else if(this._jsonData){
+ throw new Error("dojox.data.AndOrReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }
+ }
+ }
+ }else{
+ this._features['dojo.data.api.Identity'] = Number;
+ }
+
+ // Step 5: Walk through all the items, and set each item's properties
+ // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i];
+ item[this._storeRefPropName] = this;
+ item[this._itemNumPropName] = i;
+ }
+
+ // Step 6: We walk through all the attribute values of all the items,
+ // looking for type/value literals and item-references.
+ //
+ // We replace item-references with pointers to items. For example, we change:
+ // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ // into this:
+ // { name:['Kermit'], friends:[miss_piggy] }
+ // (where miss_piggy is the object representing the 'Miss Piggy' item).
+ //
+ // We replace type/value pairs with typed-literals. For example, we change:
+ // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
+ // into this:
+ // { name:['Kermit'], born:(new Date('July 18, 1918')) }
+ //
+ // We also generate the associate map for all items for the O(1) isItem function.
+ for(i = 0; i < this._arrayOfAllItems.length; ++i){
+ item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ for(key in item){
+ arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
+ for(var j = 0; j < arrayOfValues.length; ++j){
+ value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
+ if(value !== null && typeof value == "object"){
+ if(("_type" in value) && ("_value" in value)){
+ var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
+ var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
+ if(!mappingObj){
+ throw new Error("dojox.data.AndOrReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
+ }else if(dojo.isFunction(mappingObj)){
+ arrayOfValues[j] = new mappingObj(value._value);
+ }else if(dojo.isFunction(mappingObj.deserialize)){
+ arrayOfValues[j] = mappingObj.deserialize(value._value);
+ }else{
+ throw new Error("dojox.data.AndOrReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
+ }
+ }
+ if(value._reference){
+ var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
+ if(!dojo.isObject(referenceDescription)){
+ // example: 'Miss Piggy'
+ // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
+ arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
+ }else{
+ // example: {name:'Miss Piggy'}
+ // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
+ for(var k = 0; k < this._arrayOfAllItems.length; ++k){
+ var candidateItem = this._arrayOfAllItems[k];
+ var found = true;
+ for(var refKey in referenceDescription){
+ if(candidateItem[refKey] != referenceDescription[refKey]){
+ found = false;
+ }
+ }
+ if(found){
+ arrayOfValues[j] = candidateItem;
+ }
+ }
+ }
+ if(this.referenceIntegrity){
+ var refItem = arrayOfValues[j];
+ if(this.isItem(refItem)){
+ this._addReferenceToMap(refItem, item, key);
+ }
+ }
+ }else if(this.isItem(value)){
+ //It's a child item (not one referenced through _reference).
+ //We need to treat this as a referenced item, so it can be cleaned up
+ //in a write store easily.
+ if(this.referenceIntegrity){
+ this._addReferenceToMap(value, item, key);
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
+ // summary:
+ // Method to add an reference map entry for an item and attribute.
+ // description:
+ // Method to add an reference map entry for an item and attribute. //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item that holds the new reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the new reference.
+
+ //Stub function, does nothing. Real processing is in ItemFileWriteStore.
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ var identifier = this._features['dojo.data.api.Identity'];
+ if(identifier === Number){
+ return item[this._itemNumPropName]; // Number
+ }else{
+ var arrayOfValues = item[identifier];
+ if(arrayOfValues){
+ return arrayOfValues[0]; // Object || String
+ }
+ }
+ return null; // null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ // Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ try{
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ self._loadInProgress = false;
+ var item = self._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ self._loadInProgress = false;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+
+ }else if(this._jsonData){
+ // Passed in data, no need to xhr.
+ self._getItemsFromLoadedData(self._jsonData);
+ self._jsonData = null;
+ self._loadFinished = true;
+ var item = self._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }else{
+ // Already loaded. We can just look it up and call back.
+ var item = this._getItemByIdentity(keywordArgs.identity);
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ _getItemByIdentity: function(/* Object */ identity){
+ // summary:
+ // Internal function to look an item up by its identity map.
+ var item = null;
+ if(this._itemsByIdentity){
+ item = this._itemsByIdentity[identity];
+ }else{
+ item = this._arrayOfAllItems[identity];
+ }
+ if(item === undefined){
+ item = null;
+ }
+ return item; // Object
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+
+ var identifier = this._features['dojo.data.api.Identity'];
+ if(identifier === Number){
+ // If (identifier === Number) it means getIdentity() just returns
+ // an integer item-number for each item. The dojo.data.api.Identity
+ // spec says we need to return null if the identity is not composed
+ // of attributes
+ return null; // null
+ }else{
+ return [identifier]; // Array
+ }
+ },
+
+ _forceLoad: function(){
+ // summary:
+ // Internal function to force a load of the store if it hasn't occurred yet. This is required
+ // for specific functions to work properly.
+ var self = this;
+ if(this._jsonFileUrl !== this._ccUrl){
+ dojo.deprecated("dojox.data.AndOrReadStore: ",
+ "To change the url, set the url property of the store," +
+ " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
+ this._ccUrl = this._jsonFileUrl;
+ this.url = this._jsonFileUrl;
+ }else if(this.url !== this._ccUrl){
+ this._jsonFileUrl = this.url;
+ this._ccUrl = this.url;
+ }
+ //See if there was any forced reset of data.
+ if(this.data != null && this._jsonData == null){
+ this._jsonData = this.data;
+ this.data = null;
+ }
+ if(this._jsonFileUrl){
+ var getArgs = {
+ url: self._jsonFileUrl,
+ handleAs: "json-comment-optional",
+ preventCache: this.urlPreventCache,
+ sync: true
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ //Check to be sure there wasn't another load going on concurrently
+ //So we don't clobber data that comes in on it. If there is a load going on
+ //then do not save this data. It will potentially clobber current data.
+ //We mainly wanted to sync/wait here.
+ //TODO: Revisit the loading scheme of this store to improve multi-initial
+ //request handling.
+ if(self._loadInProgress !== true && !self._loadFinished){
+ self._getItemsFromLoadedData(data);
+ self._loadFinished = true;
+ }else if(self._loadInProgress){
+ //Okay, we hit an error state we can't recover from. A forced load occurred
+ //while an async load was occurring. Since we cannot block at this point, the best
+ //that can be managed is to throw an error.
+ throw new Error("dojox.data.AndOrReadStore: Unable to perform a synchronous load, an async load is in progress.");
+ }
+ }catch(e){
+ console.log(e);
+ throw e;
+ }
+ });
+ getHandler.addErrback(function(error){
+ throw error;
+ });
+ }else if(this._jsonData){
+ self._getItemsFromLoadedData(self._jsonData);
+ self._jsonData = null;
+ self._loadFinished = true;
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.AndOrReadStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/AndOrWriteStore.js b/js/dojo-1.6/dojox/data/AndOrWriteStore.js
new file mode 100644
index 0000000..a2200d4
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AndOrWriteStore.js
@@ -0,0 +1,820 @@
+/*
+ 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["dojox.data.AndOrWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AndOrWriteStore"] = true;
+dojo.provide("dojox.data.AndOrWriteStore");
+dojo.require("dojox.data.AndOrReadStore");
+
+
+
+dojo.declare("dojox.data.AndOrWriteStore", dojox.data.AndOrReadStore, {
+ constructor: function(/* object */ keywordParameters){
+ // keywordParameters: {typeMap: object)
+ // The structure of the typeMap object is as follows:
+ // {
+ // type0: function || object,
+ // type1: function || object,
+ // ...
+ // typeN: function || object
+ // }
+ // Where if it is a function, it is assumed to be an object constructor that takes the
+ // value of _value as the initialization parameters. It is serialized assuming object.toString()
+ // serialization. If it is an object, then it is assumed
+ // to be an object of general form:
+ // {
+ // type: function, //constructor.
+ // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
+ // serialize: function(object) //The function that converts the object back into the proper file format form.
+ // }
+
+ // AndOrWriteStore duplicates ItemFileWriteStore, except extends AndOrReadStore, which offers complex queries.
+ // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
+ this._features['dojo.data.api.Write'] = true;
+ this._features['dojo.data.api.Notification'] = true;
+
+ // For keeping track of changes so that we can implement isDirty and revert
+ this._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+
+ if(!this._datatypeMap['Date'].serialize){
+ this._datatypeMap['Date'].serialize = function(obj){
+ return dojo.date.stamp.toISOString(obj, {zulu:true});
+ };
+ }
+ //Disable only if explicitly set to false.
+ if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
+ this.referenceIntegrity = false;
+ }
+
+ // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
+ this._saveInProgress = false;
+ },
+
+ referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
+
+ _assert: function(/* boolean */ condition){
+ if(!condition){
+ throw new Error("assertion failed in ItemFileWriteStore");
+ }
+ },
+
+ _getIdentifierAttribute: function(){
+ var identifierAttribute = this.getFeatures()['dojo.data.api.Identity'];
+ // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
+ return identifierAttribute;
+ },
+
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
+ // summary: See dojo.data.api.Write.newItem()
+
+ this._assert(!this._saveInProgress);
+
+ if(!this._loadFinished){
+ // We need to do this here so that we'll be able to find out what
+ // identifierAttribute was specified in the data file.
+ this._forceLoad();
+ }
+
+ if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
+ throw new Error("newItem() was passed something other than an object");
+ }
+ var newIdentity = null;
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(identifierAttribute === Number){
+ newIdentity = this._arrayOfAllItems.length;
+ }else{
+ newIdentity = keywordArgs[identifierAttribute];
+ if(typeof newIdentity === "undefined"){
+ throw new Error("newItem() was not passed an identity for the new item");
+ }
+ if(dojo.isArray(newIdentity)){
+ throw new Error("newItem() was not passed an single-valued identity");
+ }
+ }
+
+ // make sure this identity is not already in use by another item, if identifiers were
+ // defined in the file. Otherwise it would be the item count,
+ // which should always be unique in this case.
+ if(this._itemsByIdentity){
+ this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
+ }
+ this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
+ this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
+
+ var newItem = {};
+ newItem[this._storeRefPropName] = this;
+ newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
+ if(this._itemsByIdentity){
+ this._itemsByIdentity[newIdentity] = newItem;
+ //We have to set the identifier now, otherwise we can't look it
+ //up at calls to setValueorValues in parentInfo handling.
+ newItem[identifierAttribute] = [newIdentity];
+ }
+ this._arrayOfAllItems.push(newItem);
+
+ //We need to construct some data for the onNew call too...
+ var pInfo = null;
+
+ // Now we need to check to see where we want to assign this thingm if any.
+ if(parentInfo && parentInfo.parent && parentInfo.attribute){
+ pInfo = {
+ item: parentInfo.parent,
+ attribute: parentInfo.attribute,
+ oldValue: undefined
+ };
+
+ //See if it is multi-valued or not and handle appropriately
+ //Generally, all attributes are multi-valued for this store
+ //So, we only need to append if there are already values present.
+ var values = this.getValues(parentInfo.parent, parentInfo.attribute);
+ if(values && values.length > 0){
+ var tempValues = values.slice(0, values.length);
+ if(values.length === 1){
+ pInfo.oldValue = values[0];
+ }else{
+ pInfo.oldValue = values.slice(0, values.length);
+ }
+ tempValues.push(newItem);
+ this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
+ pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
+ }else{
+ this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
+ pInfo.newValue = newItem;
+ }
+ }else{
+ //Toplevel item, add to both top list as well as all list.
+ newItem[this._rootItemPropName]=true;
+ this._arrayOfTopLevelItems.push(newItem);
+ }
+
+ this._pending._newItems[newIdentity] = newItem;
+
+ //Clone over the properties to the new item
+ for(var key in keywordArgs){
+ if(key === this._storeRefPropName || key === this._itemNumPropName){
+ // Bummer, the user is trying to do something like
+ // newItem({_S:"foo"}). Unfortunately, our superclass,
+ // ItemFileReadStore, is already using _S in each of our items
+ // to hold private info. To avoid a naming collision, we
+ // need to move all our private info to some other property
+ // of all the items/objects. So, we need to iterate over all
+ // the items and do something like:
+ // item.__S = item._S;
+ // item._S = undefined;
+ // But first we have to make sure the new "__S" variable is
+ // not in use, which means we have to iterate over all the
+ // items checking for that.
+ throw new Error("encountered bug in ItemFileWriteStore.newItem");
+ }
+ var value = keywordArgs[key];
+ if(!dojo.isArray(value)){
+ value = [value];
+ }
+ newItem[key] = value;
+ if(this.referenceIntegrity){
+ for(var i = 0; i < value.length; i++){
+ var val = value[i];
+ if(this.isItem(val)){
+ this._addReferenceToMap(val, newItem, key);
+ }
+ }
+ }
+ }
+ this.onNew(newItem, pInfo); // dojo.data.api.Notification call
+ return newItem; // item
+ },
+
+ _removeArrayElement: function(/* Array */ array, /* anything */ element){
+ var index = dojo.indexOf(array, element);
+ if(index != -1){
+ array.splice(index, 1);
+ return true;
+ }
+ return false;
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary: See dojo.data.api.Write.deleteItem()
+ this._assert(!this._saveInProgress);
+ this._assertIsItem(item);
+
+ // Remove this item from the _arrayOfAllItems, but leave a null value in place
+ // of the item, so as not to change the length of the array, so that in newItem()
+ // we can still safely do: newIdentity = this._arrayOfAllItems.length;
+ var indexInArrayOfAllItems = item[this._itemNumPropName];
+ var identity = this.getIdentity(item);
+
+ //If we have reference integrity on, we need to do reference cleanup for the deleted item
+ if(this.referenceIntegrity){
+ //First scan all the attributes of this items for references and clean them up in the map
+ //As this item is going away, no need to track its references anymore.
+
+ //Get the attributes list before we generate the backup so it
+ //doesn't pollute the attributes list.
+ var attributes = this.getAttributes(item);
+
+ //Backup the map, we'll have to restore it potentially, in a revert.
+ if(item[this._reverseRefMap]){
+ item["backup_" + this._reverseRefMap] = dojo.clone(item[this._reverseRefMap]);
+ }
+
+ //TODO: This causes a reversion problem. This list won't be restored on revert since it is
+ //attached to the 'value'. item, not ours. Need to back tese up somehow too.
+ //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
+ //later. Or just record them and call _addReferenceToMap on them in revert.
+ dojo.forEach(attributes, function(attribute){
+ dojo.forEach(this.getValues(item, attribute), function(value){
+ if(this.isItem(value)){
+ //We have to back up all the references we had to others so they can be restored on a revert.
+ if(!item["backupRefs_" + this._reverseRefMap]){
+ item["backupRefs_" + this._reverseRefMap] = [];
+ }
+ item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
+ this._removeReferenceFromMap(value, item, attribute);
+ }
+ }, this);
+ }, this);
+
+ //Next, see if we have references to this item, if we do, we have to clean them up too.
+ var references = item[this._reverseRefMap];
+ if(references){
+ //Look through all the items noted as references to clean them up.
+ for(var itemId in references){
+ var containingItem = null;
+ if(this._itemsByIdentity){
+ containingItem = this._itemsByIdentity[itemId];
+ }else{
+ containingItem = this._arrayOfAllItems[itemId];
+ }
+ //We have a reference to a containing item, now we have to process the
+ //attributes and clear all references to the item being deleted.
+ if(containingItem){
+ for(var attribute in references[itemId]){
+ var oldValues = this.getValues(containingItem, attribute) || [];
+ var newValues = dojo.filter(oldValues, function(possibleItem){
+ return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
+ }, this);
+ //Remove the note of the reference to the item and set the values on the modified attribute.
+ this._removeReferenceFromMap(item, containingItem, attribute);
+ if(newValues.length < oldValues.length){
+ this._setValueOrValues(containingItem, attribute, newValues);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ this._arrayOfAllItems[indexInArrayOfAllItems] = null;
+
+ item[this._storeRefPropName] = null;
+ if(this._itemsByIdentity){
+ delete this._itemsByIdentity[identity];
+ }
+ this._pending._deletedItems[identity] = item;
+
+ //Remove from the toplevel items, if necessary...
+ if(item[this._rootItemPropName]){
+ this._removeArrayElement(this._arrayOfTopLevelItems, item);
+ }
+ this.onDelete(item); // dojo.data.api.Notification call
+ return true;
+ },
+
+ setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
+ // summary: See dojo.data.api.Write.set()
+ return this._setValueOrValues(item, attribute, value, true); // boolean
+ },
+
+ setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
+ // summary: See dojo.data.api.Write.setValues()
+ return this._setValueOrValues(item, attribute, values, true); // boolean
+ },
+
+ unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
+ // summary: See dojo.data.api.Write.unsetAttribute()
+ return this._setValueOrValues(item, attribute, [], true);
+ },
+
+ _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
+ this._assert(!this._saveInProgress);
+
+ // Check for valid arguments
+ this._assertIsItem(item);
+ this._assert(dojo.isString(attribute));
+ this._assert(typeof newValueOrValues !== "undefined");
+
+ // Make sure the user isn't trying to change the item's identity
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(attribute == identifierAttribute){
+ throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
+ }
+
+ // To implement the Notification API, we need to make a note of what
+ // the old attribute value was, so that we can pass that info when
+ // we call the onSet method.
+ var oldValueOrValues = this._getValueOrValues(item, attribute);
+
+ var identity = this.getIdentity(item);
+ if(!this._pending._modifiedItems[identity]){
+ // Before we actually change the item, we make a copy of it to
+ // record the original state, so that we'll be able to revert if
+ // the revert method gets called. If the item has already been
+ // modified then there's no need to do this now, since we already
+ // have a record of the original state.
+ var copyOfItemState = {};
+ for(var key in item){
+ if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
+ copyOfItemState[key] = item[key];
+ }else if(key === this._reverseRefMap){
+ copyOfItemState[key] = dojo.clone(item[key]);
+ }else{
+ copyOfItemState[key] = item[key].slice(0, item[key].length);
+ }
+ }
+ // Now mark the item as dirty, and save the copy of the original state
+ this._pending._modifiedItems[identity] = copyOfItemState;
+ }
+
+ // Okay, now we can actually change this attribute on the item
+ var success = false;
+
+ if(dojo.isArray(newValueOrValues) && newValueOrValues.length === 0){
+
+ // If we were passed an empty array as the value, that counts
+ // as "unsetting" the attribute, so we need to remove this
+ // attribute from the item.
+ success = delete item[attribute];
+ newValueOrValues = undefined; // used in the onSet Notification call below
+
+ if(this.referenceIntegrity && oldValueOrValues){
+ var oldValues = oldValueOrValues;
+ if(!dojo.isArray(oldValues)){
+ oldValues = [oldValues];
+ }
+ for(var i = 0; i < oldValues.length; i++){
+ var value = oldValues[i];
+ if(this.isItem(value)){
+ this._removeReferenceFromMap(value, item, attribute);
+ }
+ }
+ }
+ }else{
+ var newValueArray;
+ if(dojo.isArray(newValueOrValues)){
+ var newValues = newValueOrValues;
+ // Unfortunately, it's not safe to just do this:
+ // newValueArray = newValues;
+ // Instead, we need to copy the array, which slice() does very nicely.
+ // This is so that our internal data structure won't
+ // get corrupted if the user mucks with the values array *after*
+ // calling setValues().
+ newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
+ }else{
+ newValueArray = [newValueOrValues];
+ }
+
+ //We need to handle reference integrity if this is on.
+ //In the case of set, we need to see if references were added or removed
+ //and update the reference tracking map accordingly.
+ if(this.referenceIntegrity){
+ if(oldValueOrValues){
+ var oldValues = oldValueOrValues;
+ if(!dojo.isArray(oldValues)){
+ oldValues = [oldValues];
+ }
+ //Use an associative map to determine what was added/removed from the list.
+ //Should be O(n) performant. First look at all the old values and make a list of them
+ //Then for any item not in the old list, we add it. If it was already present, we remove it.
+ //Then we pass over the map and any references left it it need to be removed (IE, no match in
+ //the new values list).
+ var map = {};
+ dojo.forEach(oldValues, function(possibleItem){
+ if(this.isItem(possibleItem)){
+ var id = this.getIdentity(possibleItem);
+ map[id.toString()] = true;
+ }
+ }, this);
+ dojo.forEach(newValueArray, function(possibleItem){
+ if(this.isItem(possibleItem)){
+ var id = this.getIdentity(possibleItem);
+ if(map[id.toString()]){
+ delete map[id.toString()];
+ }else{
+ this._addReferenceToMap(possibleItem, item, attribute);
+ }
+ }
+ }, this);
+ for(var rId in map){
+ var removedItem;
+ if(this._itemsByIdentity){
+ removedItem = this._itemsByIdentity[rId];
+ }else{
+ removedItem = this._arrayOfAllItems[rId];
+ }
+ this._removeReferenceFromMap(removedItem, item, attribute);
+ }
+ }else{
+ //Everything is new (no old values) so we have to just
+ //insert all the references, if any.
+ for(var i = 0; i < newValueArray.length; i++){
+ var value = newValueArray[i];
+ if(this.isItem(value)){
+ this._addReferenceToMap(value, item, attribute);
+ }
+ }
+ }
+ }
+ item[attribute] = newValueArray;
+ success = true;
+ }
+
+ // Now we make the dojo.data.api.Notification call
+ if(callOnSet){
+ this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
+ }
+ return success; // boolean
+ },
+
+ _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
+ // summary:
+ // Method to add an reference map entry for an item and attribute.
+ // description:
+ // Method to add an reference map entry for an item and attribute. //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item that holds the new reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the new reference.
+
+ var parentId = this.getIdentity(parentItem);
+ var references = refItem[this._reverseRefMap];
+
+ if(!references){
+ references = refItem[this._reverseRefMap] = {};
+ }
+ var itemRef = references[parentId];
+ if(!itemRef){
+ itemRef = references[parentId] = {};
+ }
+ itemRef[attribute] = true;
+ },
+
+ _removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /*strin*/ attribute){
+ // summary:
+ // Method to remove an reference map entry for an item and attribute.
+ // description:
+ // Method to remove an reference map entry for an item and attribute. This will
+ // also perform cleanup on the map such that if there are no more references at all to
+ // the item, its reference object and entry are removed.
+ //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item holding a reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the reference.
+ var identity = this.getIdentity(parentItem);
+ var references = refItem[this._reverseRefMap];
+ var itemId;
+ if(references){
+ for(itemId in references){
+ if(itemId == identity){
+ delete references[itemId][attribute];
+ if(this._isEmpty(references[itemId])){
+ delete references[itemId];
+ }
+ }
+ }
+ if(this._isEmpty(references)){
+ delete refItem[this._reverseRefMap];
+ }
+ }
+ },
+
+ _dumpReferenceMap: function(){
+ // summary:
+ // Function to dump the reverse reference map of all items in the store for debug purposes.
+ // description:
+ // Function to dump the reverse reference map of all items in the store for debug purposes.
+ var i;
+ for(i = 0; i < this._arrayOfAllItems.length; i++){
+ var item = this._arrayOfAllItems[i];
+ if(item && item[this._reverseRefMap]){
+ console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + dojo.toJson(item[this._reverseRefMap]));
+ }
+ }
+ },
+
+ _getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
+ var valueOrValues = undefined;
+ if(this.hasAttribute(item, attribute)){
+ var valueArray = this.getValues(item, attribute);
+ if(valueArray.length == 1){
+ valueOrValues = valueArray[0];
+ }else{
+ valueOrValues = valueArray;
+ }
+ }
+ return valueOrValues;
+ },
+
+ _flatten: function(/* anything */ value){
+ if(this.isItem(value)){
+ var item = value;
+ // Given an item, return an serializable object that provides a
+ // reference to the item.
+ // For example, given kermit:
+ // var kermit = store.newItem({id:2, name:"Kermit"});
+ // we want to return
+ // {_reference:2}
+ var identity = this.getIdentity(item);
+ var referenceObject = {_reference: identity};
+ return referenceObject;
+ }else{
+ if(typeof value === "object"){
+ for(var type in this._datatypeMap){
+ var typeMap = this._datatypeMap[type];
+ if(dojo.isObject(typeMap) && !dojo.isFunction(typeMap)){
+ if(value instanceof typeMap.type){
+ if(!typeMap.serialize){
+ throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
+ }
+ return {_type: type, _value: typeMap.serialize(value)};
+ }
+ } else if(value instanceof typeMap){
+ //SImple mapping, therefore, return as a toString serialization.
+ return {_type: type, _value: value.toString()};
+ }
+ }
+ }
+ return value;
+ }
+ },
+
+ _getNewFileContentString: function(){
+ // summary:
+ // Generate a string that can be saved to a file.
+ // The result should look similar to:
+ // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
+ var serializableStructure = {};
+
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(identifierAttribute !== Number){
+ serializableStructure.identifier = identifierAttribute;
+ }
+ if(this._labelAttr){
+ serializableStructure.label = this._labelAttr;
+ }
+ serializableStructure.items = [];
+ for(var i = 0; i < this._arrayOfAllItems.length; ++i){
+ var item = this._arrayOfAllItems[i];
+ if(item !== null){
+ var serializableItem = {};
+ for(var key in item){
+ if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
+ var attribute = key;
+ var valueArray = this.getValues(item, attribute);
+ if(valueArray.length == 1){
+ serializableItem[attribute] = this._flatten(valueArray[0]);
+ }else{
+ var serializableArray = [];
+ for(var j = 0; j < valueArray.length; ++j){
+ serializableArray.push(this._flatten(valueArray[j]));
+ serializableItem[attribute] = serializableArray;
+ }
+ }
+ }
+ }
+ serializableStructure.items.push(serializableItem);
+ }
+ }
+ var prettyPrint = true;
+ return dojo.toJson(serializableStructure, prettyPrint);
+ },
+
+ _isEmpty: function(something){
+ // summary:
+ // Function to determine if an array or object has no properties or values.
+ // something:
+ // The array or object to examine.
+ var empty = true;
+ if(dojo.isObject(something)){
+ var i;
+ for(i in something){
+ empty = false;
+ break;
+ }
+ }else if(dojo.isArray(something)){
+ if(something.length > 0){
+ empty = false;
+ }
+ }
+ return empty; //boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary: See dojo.data.api.Write.save()
+ this._assert(!this._saveInProgress);
+
+ // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
+ this._saveInProgress = true;
+
+ var self = this;
+ var saveCompleteCallback = function(){
+ self._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+
+ self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
+ if(keywordArgs && keywordArgs.onComplete){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onComplete.call(scope);
+ }
+ };
+ var saveFailedCallback = function(){
+ self._saveInProgress = false;
+ if(keywordArgs && keywordArgs.onError){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope);
+ }
+ };
+
+ if(this._saveEverything){
+ var newFileContentString = this._getNewFileContentString();
+ this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
+ }
+ if(this._saveCustom){
+ this._saveCustom(saveCompleteCallback, saveFailedCallback);
+ }
+ if(!this._saveEverything && !this._saveCustom){
+ // Looks like there is no user-defined save-handler function.
+ // That's fine, it just means the datastore is acting as a "mock-write"
+ // store -- changes get saved in memory but don't get saved to disk.
+ saveCompleteCallback();
+ }
+ },
+
+ revert: function(){
+ // summary: See dojo.data.api.Write.revert()
+ this._assert(!this._saveInProgress);
+
+ var identity;
+ for(identity in this._pending._modifiedItems){
+ // find the original item and the modified item that replaced it
+ var copyOfItemState = this._pending._modifiedItems[identity];
+ var modifiedItem = null;
+ if(this._itemsByIdentity){
+ modifiedItem = this._itemsByIdentity[identity];
+ }else{
+ modifiedItem = this._arrayOfAllItems[identity];
+ }
+
+ // Restore the original item into a full-fledged item again, we want to try to
+ // keep the same object instance as if we don't it, causes bugs like #9022.
+ copyOfItemState[this._storeRefPropName] = this;
+ for(key in modifiedItem){
+ delete modifiedItem[key];
+ }
+ dojo.mixin(modifiedItem, copyOfItemState);
+ }
+ var deletedItem;
+ for(identity in this._pending._deletedItems){
+ deletedItem = this._pending._deletedItems[identity];
+ deletedItem[this._storeRefPropName] = this;
+ var index = deletedItem[this._itemNumPropName];
+
+ //Restore the reverse refererence map, if any.
+ if(deletedItem["backup_" + this._reverseRefMap]){
+ deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
+ delete deletedItem["backup_" + this._reverseRefMap];
+ }
+ this._arrayOfAllItems[index] = deletedItem;
+ if(this._itemsByIdentity){
+ this._itemsByIdentity[identity] = deletedItem;
+ }
+ if(deletedItem[this._rootItemPropName]){
+ this._arrayOfTopLevelItems.push(deletedItem);
+ }
+ }
+ //We have to pass through it again and restore the reference maps after all the
+ //undeletes have occurred.
+ for(identity in this._pending._deletedItems){
+ deletedItem = this._pending._deletedItems[identity];
+ if(deletedItem["backupRefs_" + this._reverseRefMap]){
+ dojo.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
+ var refItem;
+ if(this._itemsByIdentity){
+ refItem = this._itemsByIdentity[reference.id];
+ }else{
+ refItem = this._arrayOfAllItems[reference.id];
+ }
+ this._addReferenceToMap(refItem, deletedItem, reference.attr);
+ }, this);
+ delete deletedItem["backupRefs_" + this._reverseRefMap];
+ }
+ }
+
+ for(identity in this._pending._newItems){
+ var newItem = this._pending._newItems[identity];
+ newItem[this._storeRefPropName] = null;
+ // null out the new item, but don't change the array index so
+ // so we can keep using _arrayOfAllItems.length.
+ this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
+ if(newItem[this._rootItemPropName]){
+ this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
+ }
+ if(this._itemsByIdentity){
+ delete this._itemsByIdentity[identity];
+ }
+ }
+
+ this._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+ return true; // boolean
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary: See dojo.data.api.Write.isDirty()
+ if(item){
+ // return true if the item is dirty
+ var identity = this.getIdentity(item);
+ return new Boolean(this._pending._newItems[identity] ||
+ this._pending._modifiedItems[identity] ||
+ this._pending._deletedItems[identity]).valueOf(); // boolean
+ }else{
+ // return true if the store is dirty -- which means return true
+ // if there are any new items, dirty items, or modified items
+ if(!this._isEmpty(this._pending._newItems) ||
+ !this._isEmpty(this._pending._modifiedItems) ||
+ !this._isEmpty(this._pending._deletedItems)){
+ return true;
+ }
+ return false; // boolean
+ }
+ },
+
+/* dojo.data.api.Notification */
+
+ onSet: function(/* item */ item,
+ /*attribute-name-string*/ attribute,
+ /*object | array*/ oldValue,
+ /*object | array*/ newValue){
+ // summary: See dojo.data.api.Notification.onSet()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ onNew: function(/* item */ newItem, /*object?*/ parentInfo){
+ // summary: See dojo.data.api.Notification.onNew()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ onDelete: function(/* item */ deletedItem){
+ // summary: See dojo.data.api.Notification.onDelete()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ close: function(/* object? */ request){
+ // summary:
+ // Over-ride of base close function of ItemFileReadStore to add in check for store state.
+ // description:
+ // Over-ride of base close function of ItemFileReadStore to add in check for store state.
+ // If the store is still dirty (unsaved changes), then an error will be thrown instead of
+ // clearing the internal state for reload from the url.
+
+ //Clear if not dirty ... or throw an error
+ if(this.clearOnClose){
+ if(!this.isDirty()){
+ this.inherited(arguments);
+ }else{
+ //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
+ throw new Error("dojox.data.AndOrWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
+ }
+ }
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/AndOrWriteStore.xd.js b/js/dojo-1.6/dojox/data/AndOrWriteStore.xd.js
new file mode 100644
index 0000000..eeae298
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AndOrWriteStore.xd.js
@@ -0,0 +1,825 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.AndOrWriteStore"],
+["require", "dojox.data.AndOrReadStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.AndOrWriteStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AndOrWriteStore"] = true;
+dojo.provide("dojox.data.AndOrWriteStore");
+dojo.require("dojox.data.AndOrReadStore");
+
+
+
+dojo.declare("dojox.data.AndOrWriteStore", dojox.data.AndOrReadStore, {
+ constructor: function(/* object */ keywordParameters){
+ // keywordParameters: {typeMap: object)
+ // The structure of the typeMap object is as follows:
+ // {
+ // type0: function || object,
+ // type1: function || object,
+ // ...
+ // typeN: function || object
+ // }
+ // Where if it is a function, it is assumed to be an object constructor that takes the
+ // value of _value as the initialization parameters. It is serialized assuming object.toString()
+ // serialization. If it is an object, then it is assumed
+ // to be an object of general form:
+ // {
+ // type: function, //constructor.
+ // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
+ // serialize: function(object) //The function that converts the object back into the proper file format form.
+ // }
+
+ // AndOrWriteStore duplicates ItemFileWriteStore, except extends AndOrReadStore, which offers complex queries.
+ // ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
+ this._features['dojo.data.api.Write'] = true;
+ this._features['dojo.data.api.Notification'] = true;
+
+ // For keeping track of changes so that we can implement isDirty and revert
+ this._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+
+ if(!this._datatypeMap['Date'].serialize){
+ this._datatypeMap['Date'].serialize = function(obj){
+ return dojo.date.stamp.toISOString(obj, {zulu:true});
+ };
+ }
+ //Disable only if explicitly set to false.
+ if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
+ this.referenceIntegrity = false;
+ }
+
+ // this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
+ this._saveInProgress = false;
+ },
+
+ referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
+
+ _assert: function(/* boolean */ condition){
+ if(!condition){
+ throw new Error("assertion failed in ItemFileWriteStore");
+ }
+ },
+
+ _getIdentifierAttribute: function(){
+ var identifierAttribute = this.getFeatures()['dojo.data.api.Identity'];
+ // this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
+ return identifierAttribute;
+ },
+
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
+ // summary: See dojo.data.api.Write.newItem()
+
+ this._assert(!this._saveInProgress);
+
+ if(!this._loadFinished){
+ // We need to do this here so that we'll be able to find out what
+ // identifierAttribute was specified in the data file.
+ this._forceLoad();
+ }
+
+ if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
+ throw new Error("newItem() was passed something other than an object");
+ }
+ var newIdentity = null;
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(identifierAttribute === Number){
+ newIdentity = this._arrayOfAllItems.length;
+ }else{
+ newIdentity = keywordArgs[identifierAttribute];
+ if(typeof newIdentity === "undefined"){
+ throw new Error("newItem() was not passed an identity for the new item");
+ }
+ if(dojo.isArray(newIdentity)){
+ throw new Error("newItem() was not passed an single-valued identity");
+ }
+ }
+
+ // make sure this identity is not already in use by another item, if identifiers were
+ // defined in the file. Otherwise it would be the item count,
+ // which should always be unique in this case.
+ if(this._itemsByIdentity){
+ this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
+ }
+ this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
+ this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
+
+ var newItem = {};
+ newItem[this._storeRefPropName] = this;
+ newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
+ if(this._itemsByIdentity){
+ this._itemsByIdentity[newIdentity] = newItem;
+ //We have to set the identifier now, otherwise we can't look it
+ //up at calls to setValueorValues in parentInfo handling.
+ newItem[identifierAttribute] = [newIdentity];
+ }
+ this._arrayOfAllItems.push(newItem);
+
+ //We need to construct some data for the onNew call too...
+ var pInfo = null;
+
+ // Now we need to check to see where we want to assign this thingm if any.
+ if(parentInfo && parentInfo.parent && parentInfo.attribute){
+ pInfo = {
+ item: parentInfo.parent,
+ attribute: parentInfo.attribute,
+ oldValue: undefined
+ };
+
+ //See if it is multi-valued or not and handle appropriately
+ //Generally, all attributes are multi-valued for this store
+ //So, we only need to append if there are already values present.
+ var values = this.getValues(parentInfo.parent, parentInfo.attribute);
+ if(values && values.length > 0){
+ var tempValues = values.slice(0, values.length);
+ if(values.length === 1){
+ pInfo.oldValue = values[0];
+ }else{
+ pInfo.oldValue = values.slice(0, values.length);
+ }
+ tempValues.push(newItem);
+ this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
+ pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
+ }else{
+ this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
+ pInfo.newValue = newItem;
+ }
+ }else{
+ //Toplevel item, add to both top list as well as all list.
+ newItem[this._rootItemPropName]=true;
+ this._arrayOfTopLevelItems.push(newItem);
+ }
+
+ this._pending._newItems[newIdentity] = newItem;
+
+ //Clone over the properties to the new item
+ for(var key in keywordArgs){
+ if(key === this._storeRefPropName || key === this._itemNumPropName){
+ // Bummer, the user is trying to do something like
+ // newItem({_S:"foo"}). Unfortunately, our superclass,
+ // ItemFileReadStore, is already using _S in each of our items
+ // to hold private info. To avoid a naming collision, we
+ // need to move all our private info to some other property
+ // of all the items/objects. So, we need to iterate over all
+ // the items and do something like:
+ // item.__S = item._S;
+ // item._S = undefined;
+ // But first we have to make sure the new "__S" variable is
+ // not in use, which means we have to iterate over all the
+ // items checking for that.
+ throw new Error("encountered bug in ItemFileWriteStore.newItem");
+ }
+ var value = keywordArgs[key];
+ if(!dojo.isArray(value)){
+ value = [value];
+ }
+ newItem[key] = value;
+ if(this.referenceIntegrity){
+ for(var i = 0; i < value.length; i++){
+ var val = value[i];
+ if(this.isItem(val)){
+ this._addReferenceToMap(val, newItem, key);
+ }
+ }
+ }
+ }
+ this.onNew(newItem, pInfo); // dojo.data.api.Notification call
+ return newItem; // item
+ },
+
+ _removeArrayElement: function(/* Array */ array, /* anything */ element){
+ var index = dojo.indexOf(array, element);
+ if(index != -1){
+ array.splice(index, 1);
+ return true;
+ }
+ return false;
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary: See dojo.data.api.Write.deleteItem()
+ this._assert(!this._saveInProgress);
+ this._assertIsItem(item);
+
+ // Remove this item from the _arrayOfAllItems, but leave a null value in place
+ // of the item, so as not to change the length of the array, so that in newItem()
+ // we can still safely do: newIdentity = this._arrayOfAllItems.length;
+ var indexInArrayOfAllItems = item[this._itemNumPropName];
+ var identity = this.getIdentity(item);
+
+ //If we have reference integrity on, we need to do reference cleanup for the deleted item
+ if(this.referenceIntegrity){
+ //First scan all the attributes of this items for references and clean them up in the map
+ //As this item is going away, no need to track its references anymore.
+
+ //Get the attributes list before we generate the backup so it
+ //doesn't pollute the attributes list.
+ var attributes = this.getAttributes(item);
+
+ //Backup the map, we'll have to restore it potentially, in a revert.
+ if(item[this._reverseRefMap]){
+ item["backup_" + this._reverseRefMap] = dojo.clone(item[this._reverseRefMap]);
+ }
+
+ //TODO: This causes a reversion problem. This list won't be restored on revert since it is
+ //attached to the 'value'. item, not ours. Need to back tese up somehow too.
+ //Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
+ //later. Or just record them and call _addReferenceToMap on them in revert.
+ dojo.forEach(attributes, function(attribute){
+ dojo.forEach(this.getValues(item, attribute), function(value){
+ if(this.isItem(value)){
+ //We have to back up all the references we had to others so they can be restored on a revert.
+ if(!item["backupRefs_" + this._reverseRefMap]){
+ item["backupRefs_" + this._reverseRefMap] = [];
+ }
+ item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
+ this._removeReferenceFromMap(value, item, attribute);
+ }
+ }, this);
+ }, this);
+
+ //Next, see if we have references to this item, if we do, we have to clean them up too.
+ var references = item[this._reverseRefMap];
+ if(references){
+ //Look through all the items noted as references to clean them up.
+ for(var itemId in references){
+ var containingItem = null;
+ if(this._itemsByIdentity){
+ containingItem = this._itemsByIdentity[itemId];
+ }else{
+ containingItem = this._arrayOfAllItems[itemId];
+ }
+ //We have a reference to a containing item, now we have to process the
+ //attributes and clear all references to the item being deleted.
+ if(containingItem){
+ for(var attribute in references[itemId]){
+ var oldValues = this.getValues(containingItem, attribute) || [];
+ var newValues = dojo.filter(oldValues, function(possibleItem){
+ return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
+ }, this);
+ //Remove the note of the reference to the item and set the values on the modified attribute.
+ this._removeReferenceFromMap(item, containingItem, attribute);
+ if(newValues.length < oldValues.length){
+ this._setValueOrValues(containingItem, attribute, newValues);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ this._arrayOfAllItems[indexInArrayOfAllItems] = null;
+
+ item[this._storeRefPropName] = null;
+ if(this._itemsByIdentity){
+ delete this._itemsByIdentity[identity];
+ }
+ this._pending._deletedItems[identity] = item;
+
+ //Remove from the toplevel items, if necessary...
+ if(item[this._rootItemPropName]){
+ this._removeArrayElement(this._arrayOfTopLevelItems, item);
+ }
+ this.onDelete(item); // dojo.data.api.Notification call
+ return true;
+ },
+
+ setValue: function(/* item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
+ // summary: See dojo.data.api.Write.set()
+ return this._setValueOrValues(item, attribute, value, true); // boolean
+ },
+
+ setValues: function(/* item */ item, /* attribute-name-string */ attribute, /* array */ values){
+ // summary: See dojo.data.api.Write.setValues()
+ return this._setValueOrValues(item, attribute, values, true); // boolean
+ },
+
+ unsetAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
+ // summary: See dojo.data.api.Write.unsetAttribute()
+ return this._setValueOrValues(item, attribute, [], true);
+ },
+
+ _setValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
+ this._assert(!this._saveInProgress);
+
+ // Check for valid arguments
+ this._assertIsItem(item);
+ this._assert(dojo.isString(attribute));
+ this._assert(typeof newValueOrValues !== "undefined");
+
+ // Make sure the user isn't trying to change the item's identity
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(attribute == identifierAttribute){
+ throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
+ }
+
+ // To implement the Notification API, we need to make a note of what
+ // the old attribute value was, so that we can pass that info when
+ // we call the onSet method.
+ var oldValueOrValues = this._getValueOrValues(item, attribute);
+
+ var identity = this.getIdentity(item);
+ if(!this._pending._modifiedItems[identity]){
+ // Before we actually change the item, we make a copy of it to
+ // record the original state, so that we'll be able to revert if
+ // the revert method gets called. If the item has already been
+ // modified then there's no need to do this now, since we already
+ // have a record of the original state.
+ var copyOfItemState = {};
+ for(var key in item){
+ if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
+ copyOfItemState[key] = item[key];
+ }else if(key === this._reverseRefMap){
+ copyOfItemState[key] = dojo.clone(item[key]);
+ }else{
+ copyOfItemState[key] = item[key].slice(0, item[key].length);
+ }
+ }
+ // Now mark the item as dirty, and save the copy of the original state
+ this._pending._modifiedItems[identity] = copyOfItemState;
+ }
+
+ // Okay, now we can actually change this attribute on the item
+ var success = false;
+
+ if(dojo.isArray(newValueOrValues) && newValueOrValues.length === 0){
+
+ // If we were passed an empty array as the value, that counts
+ // as "unsetting" the attribute, so we need to remove this
+ // attribute from the item.
+ success = delete item[attribute];
+ newValueOrValues = undefined; // used in the onSet Notification call below
+
+ if(this.referenceIntegrity && oldValueOrValues){
+ var oldValues = oldValueOrValues;
+ if(!dojo.isArray(oldValues)){
+ oldValues = [oldValues];
+ }
+ for(var i = 0; i < oldValues.length; i++){
+ var value = oldValues[i];
+ if(this.isItem(value)){
+ this._removeReferenceFromMap(value, item, attribute);
+ }
+ }
+ }
+ }else{
+ var newValueArray;
+ if(dojo.isArray(newValueOrValues)){
+ var newValues = newValueOrValues;
+ // Unfortunately, it's not safe to just do this:
+ // newValueArray = newValues;
+ // Instead, we need to copy the array, which slice() does very nicely.
+ // This is so that our internal data structure won't
+ // get corrupted if the user mucks with the values array *after*
+ // calling setValues().
+ newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
+ }else{
+ newValueArray = [newValueOrValues];
+ }
+
+ //We need to handle reference integrity if this is on.
+ //In the case of set, we need to see if references were added or removed
+ //and update the reference tracking map accordingly.
+ if(this.referenceIntegrity){
+ if(oldValueOrValues){
+ var oldValues = oldValueOrValues;
+ if(!dojo.isArray(oldValues)){
+ oldValues = [oldValues];
+ }
+ //Use an associative map to determine what was added/removed from the list.
+ //Should be O(n) performant. First look at all the old values and make a list of them
+ //Then for any item not in the old list, we add it. If it was already present, we remove it.
+ //Then we pass over the map and any references left it it need to be removed (IE, no match in
+ //the new values list).
+ var map = {};
+ dojo.forEach(oldValues, function(possibleItem){
+ if(this.isItem(possibleItem)){
+ var id = this.getIdentity(possibleItem);
+ map[id.toString()] = true;
+ }
+ }, this);
+ dojo.forEach(newValueArray, function(possibleItem){
+ if(this.isItem(possibleItem)){
+ var id = this.getIdentity(possibleItem);
+ if(map[id.toString()]){
+ delete map[id.toString()];
+ }else{
+ this._addReferenceToMap(possibleItem, item, attribute);
+ }
+ }
+ }, this);
+ for(var rId in map){
+ var removedItem;
+ if(this._itemsByIdentity){
+ removedItem = this._itemsByIdentity[rId];
+ }else{
+ removedItem = this._arrayOfAllItems[rId];
+ }
+ this._removeReferenceFromMap(removedItem, item, attribute);
+ }
+ }else{
+ //Everything is new (no old values) so we have to just
+ //insert all the references, if any.
+ for(var i = 0; i < newValueArray.length; i++){
+ var value = newValueArray[i];
+ if(this.isItem(value)){
+ this._addReferenceToMap(value, item, attribute);
+ }
+ }
+ }
+ }
+ item[attribute] = newValueArray;
+ success = true;
+ }
+
+ // Now we make the dojo.data.api.Notification call
+ if(callOnSet){
+ this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
+ }
+ return success; // boolean
+ },
+
+ _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
+ // summary:
+ // Method to add an reference map entry for an item and attribute.
+ // description:
+ // Method to add an reference map entry for an item and attribute. //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item that holds the new reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the new reference.
+
+ var parentId = this.getIdentity(parentItem);
+ var references = refItem[this._reverseRefMap];
+
+ if(!references){
+ references = refItem[this._reverseRefMap] = {};
+ }
+ var itemRef = references[parentId];
+ if(!itemRef){
+ itemRef = references[parentId] = {};
+ }
+ itemRef[attribute] = true;
+ },
+
+ _removeReferenceFromMap: function(/* item */ refItem, /* item */ parentItem, /*strin*/ attribute){
+ // summary:
+ // Method to remove an reference map entry for an item and attribute.
+ // description:
+ // Method to remove an reference map entry for an item and attribute. This will
+ // also perform cleanup on the map such that if there are no more references at all to
+ // the item, its reference object and entry are removed.
+ //
+ // refItem:
+ // The item that is referenced.
+ // parentItem:
+ // The item holding a reference to refItem.
+ // attribute:
+ // The attribute on parentItem that contains the reference.
+ var identity = this.getIdentity(parentItem);
+ var references = refItem[this._reverseRefMap];
+ var itemId;
+ if(references){
+ for(itemId in references){
+ if(itemId == identity){
+ delete references[itemId][attribute];
+ if(this._isEmpty(references[itemId])){
+ delete references[itemId];
+ }
+ }
+ }
+ if(this._isEmpty(references)){
+ delete refItem[this._reverseRefMap];
+ }
+ }
+ },
+
+ _dumpReferenceMap: function(){
+ // summary:
+ // Function to dump the reverse reference map of all items in the store for debug purposes.
+ // description:
+ // Function to dump the reverse reference map of all items in the store for debug purposes.
+ var i;
+ for(i = 0; i < this._arrayOfAllItems.length; i++){
+ var item = this._arrayOfAllItems[i];
+ if(item && item[this._reverseRefMap]){
+ console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + dojo.toJson(item[this._reverseRefMap]));
+ }
+ }
+ },
+
+ _getValueOrValues: function(/* item */ item, /* attribute-name-string */ attribute){
+ var valueOrValues = undefined;
+ if(this.hasAttribute(item, attribute)){
+ var valueArray = this.getValues(item, attribute);
+ if(valueArray.length == 1){
+ valueOrValues = valueArray[0];
+ }else{
+ valueOrValues = valueArray;
+ }
+ }
+ return valueOrValues;
+ },
+
+ _flatten: function(/* anything */ value){
+ if(this.isItem(value)){
+ var item = value;
+ // Given an item, return an serializable object that provides a
+ // reference to the item.
+ // For example, given kermit:
+ // var kermit = store.newItem({id:2, name:"Kermit"});
+ // we want to return
+ // {_reference:2}
+ var identity = this.getIdentity(item);
+ var referenceObject = {_reference: identity};
+ return referenceObject;
+ }else{
+ if(typeof value === "object"){
+ for(var type in this._datatypeMap){
+ var typeMap = this._datatypeMap[type];
+ if(dojo.isObject(typeMap) && !dojo.isFunction(typeMap)){
+ if(value instanceof typeMap.type){
+ if(!typeMap.serialize){
+ throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
+ }
+ return {_type: type, _value: typeMap.serialize(value)};
+ }
+ } else if(value instanceof typeMap){
+ //SImple mapping, therefore, return as a toString serialization.
+ return {_type: type, _value: value.toString()};
+ }
+ }
+ }
+ return value;
+ }
+ },
+
+ _getNewFileContentString: function(){
+ // summary:
+ // Generate a string that can be saved to a file.
+ // The result should look similar to:
+ // http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
+ var serializableStructure = {};
+
+ var identifierAttribute = this._getIdentifierAttribute();
+ if(identifierAttribute !== Number){
+ serializableStructure.identifier = identifierAttribute;
+ }
+ if(this._labelAttr){
+ serializableStructure.label = this._labelAttr;
+ }
+ serializableStructure.items = [];
+ for(var i = 0; i < this._arrayOfAllItems.length; ++i){
+ var item = this._arrayOfAllItems[i];
+ if(item !== null){
+ var serializableItem = {};
+ for(var key in item){
+ if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
+ var attribute = key;
+ var valueArray = this.getValues(item, attribute);
+ if(valueArray.length == 1){
+ serializableItem[attribute] = this._flatten(valueArray[0]);
+ }else{
+ var serializableArray = [];
+ for(var j = 0; j < valueArray.length; ++j){
+ serializableArray.push(this._flatten(valueArray[j]));
+ serializableItem[attribute] = serializableArray;
+ }
+ }
+ }
+ }
+ serializableStructure.items.push(serializableItem);
+ }
+ }
+ var prettyPrint = true;
+ return dojo.toJson(serializableStructure, prettyPrint);
+ },
+
+ _isEmpty: function(something){
+ // summary:
+ // Function to determine if an array or object has no properties or values.
+ // something:
+ // The array or object to examine.
+ var empty = true;
+ if(dojo.isObject(something)){
+ var i;
+ for(i in something){
+ empty = false;
+ break;
+ }
+ }else if(dojo.isArray(something)){
+ if(something.length > 0){
+ empty = false;
+ }
+ }
+ return empty; //boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary: See dojo.data.api.Write.save()
+ this._assert(!this._saveInProgress);
+
+ // this._saveInProgress is set to true, briefly, from when save is first called to when it completes
+ this._saveInProgress = true;
+
+ var self = this;
+ var saveCompleteCallback = function(){
+ self._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+
+ self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
+ if(keywordArgs && keywordArgs.onComplete){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onComplete.call(scope);
+ }
+ };
+ var saveFailedCallback = function(){
+ self._saveInProgress = false;
+ if(keywordArgs && keywordArgs.onError){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope);
+ }
+ };
+
+ if(this._saveEverything){
+ var newFileContentString = this._getNewFileContentString();
+ this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
+ }
+ if(this._saveCustom){
+ this._saveCustom(saveCompleteCallback, saveFailedCallback);
+ }
+ if(!this._saveEverything && !this._saveCustom){
+ // Looks like there is no user-defined save-handler function.
+ // That's fine, it just means the datastore is acting as a "mock-write"
+ // store -- changes get saved in memory but don't get saved to disk.
+ saveCompleteCallback();
+ }
+ },
+
+ revert: function(){
+ // summary: See dojo.data.api.Write.revert()
+ this._assert(!this._saveInProgress);
+
+ var identity;
+ for(identity in this._pending._modifiedItems){
+ // find the original item and the modified item that replaced it
+ var copyOfItemState = this._pending._modifiedItems[identity];
+ var modifiedItem = null;
+ if(this._itemsByIdentity){
+ modifiedItem = this._itemsByIdentity[identity];
+ }else{
+ modifiedItem = this._arrayOfAllItems[identity];
+ }
+
+ // Restore the original item into a full-fledged item again, we want to try to
+ // keep the same object instance as if we don't it, causes bugs like #9022.
+ copyOfItemState[this._storeRefPropName] = this;
+ for(key in modifiedItem){
+ delete modifiedItem[key];
+ }
+ dojo.mixin(modifiedItem, copyOfItemState);
+ }
+ var deletedItem;
+ for(identity in this._pending._deletedItems){
+ deletedItem = this._pending._deletedItems[identity];
+ deletedItem[this._storeRefPropName] = this;
+ var index = deletedItem[this._itemNumPropName];
+
+ //Restore the reverse refererence map, if any.
+ if(deletedItem["backup_" + this._reverseRefMap]){
+ deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
+ delete deletedItem["backup_" + this._reverseRefMap];
+ }
+ this._arrayOfAllItems[index] = deletedItem;
+ if(this._itemsByIdentity){
+ this._itemsByIdentity[identity] = deletedItem;
+ }
+ if(deletedItem[this._rootItemPropName]){
+ this._arrayOfTopLevelItems.push(deletedItem);
+ }
+ }
+ //We have to pass through it again and restore the reference maps after all the
+ //undeletes have occurred.
+ for(identity in this._pending._deletedItems){
+ deletedItem = this._pending._deletedItems[identity];
+ if(deletedItem["backupRefs_" + this._reverseRefMap]){
+ dojo.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
+ var refItem;
+ if(this._itemsByIdentity){
+ refItem = this._itemsByIdentity[reference.id];
+ }else{
+ refItem = this._arrayOfAllItems[reference.id];
+ }
+ this._addReferenceToMap(refItem, deletedItem, reference.attr);
+ }, this);
+ delete deletedItem["backupRefs_" + this._reverseRefMap];
+ }
+ }
+
+ for(identity in this._pending._newItems){
+ var newItem = this._pending._newItems[identity];
+ newItem[this._storeRefPropName] = null;
+ // null out the new item, but don't change the array index so
+ // so we can keep using _arrayOfAllItems.length.
+ this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
+ if(newItem[this._rootItemPropName]){
+ this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
+ }
+ if(this._itemsByIdentity){
+ delete this._itemsByIdentity[identity];
+ }
+ }
+
+ this._pending = {
+ _newItems:{},
+ _modifiedItems:{},
+ _deletedItems:{}
+ };
+ return true; // boolean
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary: See dojo.data.api.Write.isDirty()
+ if(item){
+ // return true if the item is dirty
+ var identity = this.getIdentity(item);
+ return new Boolean(this._pending._newItems[identity] ||
+ this._pending._modifiedItems[identity] ||
+ this._pending._deletedItems[identity]).valueOf(); // boolean
+ }else{
+ // return true if the store is dirty -- which means return true
+ // if there are any new items, dirty items, or modified items
+ if(!this._isEmpty(this._pending._newItems) ||
+ !this._isEmpty(this._pending._modifiedItems) ||
+ !this._isEmpty(this._pending._deletedItems)){
+ return true;
+ }
+ return false; // boolean
+ }
+ },
+
+/* dojo.data.api.Notification */
+
+ onSet: function(/* item */ item,
+ /*attribute-name-string*/ attribute,
+ /*object | array*/ oldValue,
+ /*object | array*/ newValue){
+ // summary: See dojo.data.api.Notification.onSet()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ onNew: function(/* item */ newItem, /*object?*/ parentInfo){
+ // summary: See dojo.data.api.Notification.onNew()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ onDelete: function(/* item */ deletedItem){
+ // summary: See dojo.data.api.Notification.onDelete()
+
+ // No need to do anything. This method is here just so that the
+ // client code can connect observers to it.
+ },
+
+ close: function(/* object? */ request){
+ // summary:
+ // Over-ride of base close function of ItemFileReadStore to add in check for store state.
+ // description:
+ // Over-ride of base close function of ItemFileReadStore to add in check for store state.
+ // If the store is still dirty (unsaved changes), then an error will be thrown instead of
+ // clearing the internal state for reload from the url.
+
+ //Clear if not dirty ... or throw an error
+ if(this.clearOnClose){
+ if(!this.isDirty()){
+ this.inherited(arguments);
+ }else{
+ //Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
+ throw new Error("dojox.data.AndOrWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
+ }
+ }
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/AppStore.js b/js/dojo-1.6/dojox/data/AppStore.js
new file mode 100644
index 0000000..7581573
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AppStore.js
@@ -0,0 +1,841 @@
+/*
+ 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["dojox.data.AppStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AppStore"] = true;
+dojo.provide("dojox.data.AppStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.atom.io.Connection");
+
+
+
+dojo.experimental("dojox.data.AppStore");
+
+dojo.declare("dojox.data.AppStore",
+ null,{
+
+ // url: [public] string
+ // So the parser can instantiate the store via markup.
+ url: "",
+
+ // urlPreventCache: [public] boolean
+ // Whether or not to pass the preventCache parameter to the connection
+ urlPreventCache: false,
+
+ // xmethod: [public] boolean
+ // Whether to use X-Method-Override for PUT/DELETE.
+ xmethod: false,
+
+ _atomIO: null,
+ _feed: null,
+ _requests: null,
+ _processing: null,
+
+ _updates: null,
+ _adds: null,
+ _deletes: null,
+
+ constructor: function(/*Object*/args){
+ // summary:
+ // The APP data store.
+ // description:
+ // The APP Store is instantiated either in markup or programmatically by supplying a
+ // url of the Collection to be used.
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url of the Collection to load.
+ // urlPreventCache: Whether or not to append on cache prevention params (as defined by dojo.xhr*)
+
+ if(args && args.url){
+ this.url = args.url;
+ }
+ if(args && args.urlPreventCache){
+ this.urlPreventCache = args.urlPreventCache;
+ }
+ if(!this.url){
+ throw new Error("A URL is required to instantiate an APP Store object");
+ }
+ },
+
+ _setFeed: function(feed, data){
+ // summary:
+ // Sets the internal feed using a dojox.atom.io.model.Feed object.
+ // description:
+ // Sets the internal feed using a dojox.atom.io.model.Feed object. Also adds
+ // a property to the entries to track that they belong to this store. It
+ // also parses stored requests (since we were waiting on a callback) and
+ // executes those as well.
+ //
+ // feed: dojox.atom.io.model.Feed object
+ // The Feed to use for this data store.
+ // data: unused
+ // Signature for this function is defined by AtomIO.getFeed, since this is a callback.
+ this._feed = feed;
+ var i;
+ for(i=0; i<this._feed.entries.length; i++){
+ this._feed.entries[i].store = this;
+ }
+ if(this._requests){
+ for(i=0; i<this._requests.length; i++){
+ var request = this._requests[i];
+ if(request.request && request.fh && request.eh){
+ this._finishFetchItems(request.request, request.fh, request.eh);
+ }else if(request.clear){
+ this._feed = null;
+ }else if(request.add){
+ this._feed.addEntry(request.add);
+ }else if(request.remove){
+ this._feed.removeEntry(request.remove);
+ }
+ }
+ }
+ this._requests = null;
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all entries in the Feed as an array of items.
+ // description:
+ // Function to return all entries in the Feed as an array of items.
+ //
+ // returns:
+ // Array of all entries in the feed.
+ var items = [];
+ for(var i=0; i<this._feed.entries.length; i++){
+ items.push(this._feed.entries[i]);
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item is an item.
+ // description:
+ // This function tests whether the item passed in is indeed an item
+ // in the store.
+ //
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("This error message is provided when a function is called in the following form: "
+ + "getAttribute(argument, attributeName). The argument variable represents the member "
+ + "or owner of the object. The error is created when an item that does not belong "
+ + "to this store is specified as an argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item is an attribute.
+ // description:
+ // This function tests whether the item passed in is indeed a valid
+ // 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns a boolean indicating whether this is a valid attribute.
+ if(typeof attribute !== "string"){
+ throw new Error("The attribute argument must be a string. The error is created "
+ + "when a different type of variable is specified such as an array or object.");
+ }
+
+ for(var key in dojox.atom.io.model._actions){
+ if(key == attribute){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ _addUpdate: function(/* Object */ update){
+ // summary:
+ // Internal function to add an updated entry to our updates array
+ // description:
+ // Internal function to add an updated entry to our updates array
+ //
+ // update: dojox.atom.io.model.Entry object
+ // The updated Entry we've changed.
+ if(!this._updates){
+ this._updates = [update];
+ }else{
+ this._updates.push(update);
+ }
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var flag = this._assertIsAttribute(attribute);
+
+ if(flag){
+ if((attribute === "author" || attribute === "contributor" || attribute === "link") && item[attribute+"s"]){
+ return item[attribute+"s"];
+ }
+ if(attribute === "category" && item.categories){
+ return item.categories;
+ }
+ if(item[attribute]){
+ item = item[attribute];
+ if(item.declaredClass == "dojox.atom.io.model.Content"){
+ return [item.value];
+ }
+ return [item] ;
+ }
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var key in dojox.atom.io.model._actions){
+ if(this.hasAttribute(item, key)){
+ attributes.push(key);
+ }
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp,
+ /* Boolean?*/ trim){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ if(trim){
+ possibleValue = possibleValue.replace(new RegExp(/^\s+/),""); // START
+ possibleValue = possibleValue.replace(new RegExp(/\s+$/),""); // END
+ }
+ possibleValue = possibleValue.replace(/\r|\n|\r\n/g, "");
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ return something && something.store && something.store === this; //boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (Atom entries) that match to a query
+ // description:
+ // Fetch items (Atom entries) that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ if(this._feed){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ var flag = false;
+ if(!this._requests){
+ this._requests = [];
+ flag = true;
+ }
+ this._requests.push({request: request, fh: fetchHandler, eh: errorHandler});
+ if(flag){
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,this._setFeed, null, this);
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for finishing a fetch request.
+ // description:
+ // Internal function for finishing a fetch request. Needed since the feed
+ // might not have been loaded, so we finish the fetch in a callback.
+ //
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var items = null;
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key], request.trim)){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ }
+ try{
+ fetchHandler(items, request);
+ }catch(e){
+ errorHandler(e, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Write': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ this._feed = null;
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ return this.getValue(item, "title", "No Title");
+ }
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return ["title"];
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ return this.getValue(item, "id");
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ return ["id"];
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ this._fetchItems({query:{id:keywordArgs.identity}, onItem: keywordArgs.onItem, scope: keywordArgs.scope},
+ function(items, request){
+ var scope = request.scope;
+ if(!scope){
+ scope = dojo.global;
+ }
+ if(items.length < 1){
+ request.onItem.call(scope, null);
+ }else{
+ request.onItem.call(scope, items[0]);
+ }
+ }, keywordArgs.onError);
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ newItem: function(/* Object? */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Write.newItem()
+ var entry = new dojox.atom.io.model.Entry();
+ var value = null;
+ var temp = null;
+ var i;
+ for(var key in keywordArgs){
+ if(this._assertIsAttribute(key)){
+ value = keywordArgs[key];
+ switch(key){
+ case "link":
+ for(i in value){
+ temp = value[i];
+ entry.addLink(temp.href,temp.rel,temp.hrefLang,temp.title,temp.type);
+ }
+ break;
+ case "author":
+ for(i in value){
+ temp = value[i];
+ entry.addAuthor(temp.name, temp.email, temp.uri);
+ }
+ break;
+ case "contributor":
+ for(i in value){
+ temp = value[i];
+ entry.addContributor(temp.name, temp.email, temp.uri);
+ }
+ break;
+ case "category":
+ for(i in value){
+ temp = value[i];
+ entry.addCategory(temp.scheme, temp.term, temp.label);
+ }
+ break;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ entry[key] = value;
+ break;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ entry[key] = dojox.atom.io.model.util.createDate(value);
+ break;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ entry[key] = new dojox.atom.io.model.Content(key);
+ entry[key].value = value;
+ break;
+ default:
+ entry[key] = value;
+ break;
+ }
+ }
+ }
+ entry.store = this;
+ entry.isDirty = true;
+
+ if(!this._adds){
+ this._adds = [entry];
+ }else{
+ this._adds.push(entry);
+ }
+
+ if(this._feed){
+ this._feed.addEntry(entry);
+ }else{
+ if(this._requests){
+ this._requests.push({add:entry});
+ }else{
+ this._requests = [{add:entry}];
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+ }
+ }
+ return true;
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Write.deleteItem()
+ this._assertIsItem(item);
+
+ if(!this._deletes){
+ this._deletes = [item];
+ }else{
+ this._deletes.push(item);
+ }
+
+ if(this._feed){
+ this._feed.removeEntry(item);
+ }else{
+ if(this._requests){
+ this._requests.push({remove:item});
+ }else{
+ this._requests = [{remove:item}];
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+ }
+ }
+ item = null;
+ return true;
+ },
+
+ setValue: function( /* item */ item,
+ /* string */ attribute,
+ /* almost anything */ value){
+ // summary:
+ // See dojo.data.api.Write.setValue()
+ this._assertIsItem(item);
+
+ var update = {item: item};
+ if(this._assertIsAttribute(attribute)){
+ switch(attribute){
+ case "link":
+ update.links = item.links;
+ this._addUpdate(update);
+ item.links = null;
+ item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
+ item.isDirty = true;
+ return true;
+ case "author":
+ update.authors = item.authors;
+ this._addUpdate(update);
+ item.authors = null;
+ item.addAuthor(value.name, value.email, value.uri);
+ item.isDirty = true;
+ return true;
+ case "contributor":
+ update.contributors = item.contributors;
+ this._addUpdate(update);
+ item.contributors = null;
+ item.addContributor(value.name, value.email, value.uri);
+ item.isDirty = true;
+ return true;
+ case "category":
+ update.categories = item.categories;
+ this._addUpdate(update);
+ item.categories = null;
+ item.addCategory(value.scheme, value.term, value.label);
+ item.isDirty = true;
+ return true;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = value;
+ item.isDirty = true;
+ return true;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = dojox.atom.io.model.util.createDate(value);
+ item.isDirty = true;
+ return true;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = new dojox.atom.io.model.Content(attribute);
+ item[attribute].value = value;
+ item.isDirty = true;
+ return true;
+ default:
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = value;
+ item.isDirty = true;
+ return true;
+ }
+ }
+ return false;
+ },
+
+ setValues: function(/* item */ item,
+ /* string */ attribute,
+ /* array */ values){
+ // summary:
+ // See dojo.data.api.Write.setValues()
+ if(values.length === 0){
+ return this.unsetAttribute(item, attribute);
+ }
+ this._assertIsItem(item);
+
+ var update = {item: item};
+ var value;
+ var i;
+ if(this._assertIsAttribute(attribute)){
+ switch(attribute){
+ case "link":
+ update.links = item.links;
+ item.links = null;
+ for(i in values){
+ value = values[i];
+ item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
+ }
+ item.isDirty = true;
+ return true;
+ case "author":
+ update.authors = item.authors;
+ item.authors = null;
+ for(i in values){
+ value = values[i];
+ item.addAuthor(value.name, value.email, value.uri);
+ }
+ item.isDirty = true;
+ return true;
+ case "contributor":
+ update.contributors = item.contributors;
+ item.contributors = null;
+ for(i in values){
+ value = values[i];
+ item.addContributor(value.name, value.email, value.uri);
+ }
+ item.isDirty = true;
+ return true;
+ case "categories":
+ update.categories = item.categories;
+ item.categories = null;
+ for(i in values){
+ value = values[i];
+ item.addCategory(value.scheme, value.term, value.label);
+ }
+ item.isDirty = true;
+ return true;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ update[attribute] = item[attribute];
+ item[attribute] = values[0];
+ item.isDirty = true;
+ return true;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ update[attribute] = item[attribute];
+ item[attribute] = dojox.atom.io.model.util.createDate(values[0]);
+ item.isDirty = true;
+ return true;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ update[attribute] = item[attribute];
+ item[attribute] = new dojox.atom.io.model.Content(attribute);
+ item[attribute].values[0] = values[0];
+ item.isDirty = true;
+ return true;
+ default:
+ update[attribute] = item[attribute];
+ item[attribute] = values[0];
+ item.isDirty = true;
+ return true;
+ }
+ }
+ this._addUpdate(update);
+ return false;
+ },
+
+ unsetAttribute: function( /* item */ item,
+ /* string */ attribute){
+ // summary:
+ // See dojo.data.api.Write.unsetAttribute()
+ this._assertIsItem(item);
+ if(this._assertIsAttribute(attribute)){
+ if(item[attribute] !== null){
+ var update = {item: item};
+ switch(attribute){
+ case "author":
+ case "contributor":
+ case "link":
+ update[attribute+"s"] = item[attribute+"s"];
+ break;
+ case "category":
+ update.categories = item.categories;
+ break;
+ default:
+ update[attribute] = item[attribute];
+ break;
+ }
+ item.isDirty = true;
+ item[attribute] = null;
+ this._addUpdate(update);
+ return true;
+ }
+ }
+ return false; // boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Write.save()
+ // keywordArgs:
+ // {
+ // onComplete: function
+ // onError: function
+ // scope: object
+ // }
+ var i;
+ for(i in this._adds){
+ this._atomIO.addEntry(this._adds[i], null, function(){}, keywordArgs.onError, false, keywordArgs.scope);
+ }
+
+ this._adds = null;
+
+ for(i in this._updates){
+ this._atomIO.updateEntry(this._updates[i].item, function(){}, keywordArgs.onError, false, this.xmethod, keywordArgs.scope);
+ }
+
+ this._updates = null;
+
+ for(i in this._deletes){
+ this._atomIO.removeEntry(this._deletes[i], function(){}, keywordArgs.onError, this.xmethod, keywordArgs.scope);
+ }
+
+ this._deletes = null;
+
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+
+ if(keywordArgs.onComplete){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onComplete.call(scope);
+ }
+ },
+
+ revert: function(){
+ // summary:
+ // See dojo.data.api.Write.revert()
+ var i;
+ for(i in this._adds){
+ this._feed.removeEntry(this._adds[i]);
+ }
+
+ this._adds = null;
+
+ var update, item, key;
+ for(i in this._updates){
+ update = this._updates[i];
+ item = update.item;
+ for(key in update){
+ if(key !== "item"){
+ item[key] = update[key];
+ }
+ }
+ }
+ this._updates = null;
+
+ for(i in this._deletes){
+ this._feed.addEntry(this._deletes[i]);
+ }
+ this._deletes = null;
+ return true;
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary:
+ // See dojo.data.api.Write.isDirty()
+ if(item){
+ this._assertIsItem(item);
+ return item.isDirty?true:false; //boolean
+ }
+ return (this._adds !== null || this._updates !== null); //boolean
+ }
+});
+dojo.extend(dojox.data.AppStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/AppStore.xd.js b/js/dojo-1.6/dojox/data/AppStore.xd.js
new file mode 100644
index 0000000..8695e63
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AppStore.xd.js
@@ -0,0 +1,848 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.AppStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"],
+["require", "dojox.atom.io.Connection"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.AppStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AppStore"] = true;
+dojo.provide("dojox.data.AppStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.atom.io.Connection");
+
+
+
+dojo.experimental("dojox.data.AppStore");
+
+dojo.declare("dojox.data.AppStore",
+ null,{
+
+ // url: [public] string
+ // So the parser can instantiate the store via markup.
+ url: "",
+
+ // urlPreventCache: [public] boolean
+ // Whether or not to pass the preventCache parameter to the connection
+ urlPreventCache: false,
+
+ // xmethod: [public] boolean
+ // Whether to use X-Method-Override for PUT/DELETE.
+ xmethod: false,
+
+ _atomIO: null,
+ _feed: null,
+ _requests: null,
+ _processing: null,
+
+ _updates: null,
+ _adds: null,
+ _deletes: null,
+
+ constructor: function(/*Object*/args){
+ // summary:
+ // The APP data store.
+ // description:
+ // The APP Store is instantiated either in markup or programmatically by supplying a
+ // url of the Collection to be used.
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url of the Collection to load.
+ // urlPreventCache: Whether or not to append on cache prevention params (as defined by dojo.xhr*)
+
+ if(args && args.url){
+ this.url = args.url;
+ }
+ if(args && args.urlPreventCache){
+ this.urlPreventCache = args.urlPreventCache;
+ }
+ if(!this.url){
+ throw new Error("A URL is required to instantiate an APP Store object");
+ }
+ },
+
+ _setFeed: function(feed, data){
+ // summary:
+ // Sets the internal feed using a dojox.atom.io.model.Feed object.
+ // description:
+ // Sets the internal feed using a dojox.atom.io.model.Feed object. Also adds
+ // a property to the entries to track that they belong to this store. It
+ // also parses stored requests (since we were waiting on a callback) and
+ // executes those as well.
+ //
+ // feed: dojox.atom.io.model.Feed object
+ // The Feed to use for this data store.
+ // data: unused
+ // Signature for this function is defined by AtomIO.getFeed, since this is a callback.
+ this._feed = feed;
+ var i;
+ for(i=0; i<this._feed.entries.length; i++){
+ this._feed.entries[i].store = this;
+ }
+ if(this._requests){
+ for(i=0; i<this._requests.length; i++){
+ var request = this._requests[i];
+ if(request.request && request.fh && request.eh){
+ this._finishFetchItems(request.request, request.fh, request.eh);
+ }else if(request.clear){
+ this._feed = null;
+ }else if(request.add){
+ this._feed.addEntry(request.add);
+ }else if(request.remove){
+ this._feed.removeEntry(request.remove);
+ }
+ }
+ }
+ this._requests = null;
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all entries in the Feed as an array of items.
+ // description:
+ // Function to return all entries in the Feed as an array of items.
+ //
+ // returns:
+ // Array of all entries in the feed.
+ var items = [];
+ for(var i=0; i<this._feed.entries.length; i++){
+ items.push(this._feed.entries[i]);
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item is an item.
+ // description:
+ // This function tests whether the item passed in is indeed an item
+ // in the store.
+ //
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("This error message is provided when a function is called in the following form: "
+ + "getAttribute(argument, attributeName). The argument variable represents the member "
+ + "or owner of the object. The error is created when an item that does not belong "
+ + "to this store is specified as an argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item is an attribute.
+ // description:
+ // This function tests whether the item passed in is indeed a valid
+ // 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns a boolean indicating whether this is a valid attribute.
+ if(typeof attribute !== "string"){
+ throw new Error("The attribute argument must be a string. The error is created "
+ + "when a different type of variable is specified such as an array or object.");
+ }
+
+ for(var key in dojox.atom.io.model._actions){
+ if(key == attribute){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ _addUpdate: function(/* Object */ update){
+ // summary:
+ // Internal function to add an updated entry to our updates array
+ // description:
+ // Internal function to add an updated entry to our updates array
+ //
+ // update: dojox.atom.io.model.Entry object
+ // The updated Entry we've changed.
+ if(!this._updates){
+ this._updates = [update];
+ }else{
+ this._updates.push(update);
+ }
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var flag = this._assertIsAttribute(attribute);
+
+ if(flag){
+ if((attribute === "author" || attribute === "contributor" || attribute === "link") && item[attribute+"s"]){
+ return item[attribute+"s"];
+ }
+ if(attribute === "category" && item.categories){
+ return item.categories;
+ }
+ if(item[attribute]){
+ item = item[attribute];
+ if(item.declaredClass == "dojox.atom.io.model.Content"){
+ return [item.value];
+ }
+ return [item] ;
+ }
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var key in dojox.atom.io.model._actions){
+ if(this.hasAttribute(item, key)){
+ attributes.push(key);
+ }
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp,
+ /* Boolean?*/ trim){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ if(trim){
+ possibleValue = possibleValue.replace(new RegExp(/^\s+/),""); // START
+ possibleValue = possibleValue.replace(new RegExp(/\s+$/),""); // END
+ }
+ possibleValue = possibleValue.replace(/\r|\n|\r\n/g, "");
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ return something && something.store && something.store === this; //boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (Atom entries) that match to a query
+ // description:
+ // Fetch items (Atom entries) that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ if(this._feed){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ var flag = false;
+ if(!this._requests){
+ this._requests = [];
+ flag = true;
+ }
+ this._requests.push({request: request, fh: fetchHandler, eh: errorHandler});
+ if(flag){
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,this._setFeed, null, this);
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for finishing a fetch request.
+ // description:
+ // Internal function for finishing a fetch request. Needed since the feed
+ // might not have been loaded, so we finish the fetch in a callback.
+ //
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var items = null;
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key], request.trim)){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ }
+ try{
+ fetchHandler(items, request);
+ }catch(e){
+ errorHandler(e, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Write': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ this._feed = null;
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ return this.getValue(item, "title", "No Title");
+ }
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return ["title"];
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ return this.getValue(item, "id");
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ return ["id"];
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ this._fetchItems({query:{id:keywordArgs.identity}, onItem: keywordArgs.onItem, scope: keywordArgs.scope},
+ function(items, request){
+ var scope = request.scope;
+ if(!scope){
+ scope = dojo.global;
+ }
+ if(items.length < 1){
+ request.onItem.call(scope, null);
+ }else{
+ request.onItem.call(scope, items[0]);
+ }
+ }, keywordArgs.onError);
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ newItem: function(/* Object? */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Write.newItem()
+ var entry = new dojox.atom.io.model.Entry();
+ var value = null;
+ var temp = null;
+ var i;
+ for(var key in keywordArgs){
+ if(this._assertIsAttribute(key)){
+ value = keywordArgs[key];
+ switch(key){
+ case "link":
+ for(i in value){
+ temp = value[i];
+ entry.addLink(temp.href,temp.rel,temp.hrefLang,temp.title,temp.type);
+ }
+ break;
+ case "author":
+ for(i in value){
+ temp = value[i];
+ entry.addAuthor(temp.name, temp.email, temp.uri);
+ }
+ break;
+ case "contributor":
+ for(i in value){
+ temp = value[i];
+ entry.addContributor(temp.name, temp.email, temp.uri);
+ }
+ break;
+ case "category":
+ for(i in value){
+ temp = value[i];
+ entry.addCategory(temp.scheme, temp.term, temp.label);
+ }
+ break;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ entry[key] = value;
+ break;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ entry[key] = dojox.atom.io.model.util.createDate(value);
+ break;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ entry[key] = new dojox.atom.io.model.Content(key);
+ entry[key].value = value;
+ break;
+ default:
+ entry[key] = value;
+ break;
+ }
+ }
+ }
+ entry.store = this;
+ entry.isDirty = true;
+
+ if(!this._adds){
+ this._adds = [entry];
+ }else{
+ this._adds.push(entry);
+ }
+
+ if(this._feed){
+ this._feed.addEntry(entry);
+ }else{
+ if(this._requests){
+ this._requests.push({add:entry});
+ }else{
+ this._requests = [{add:entry}];
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+ }
+ }
+ return true;
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Write.deleteItem()
+ this._assertIsItem(item);
+
+ if(!this._deletes){
+ this._deletes = [item];
+ }else{
+ this._deletes.push(item);
+ }
+
+ if(this._feed){
+ this._feed.removeEntry(item);
+ }else{
+ if(this._requests){
+ this._requests.push({remove:item});
+ }else{
+ this._requests = [{remove:item}];
+ this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+ }
+ }
+ item = null;
+ return true;
+ },
+
+ setValue: function( /* item */ item,
+ /* string */ attribute,
+ /* almost anything */ value){
+ // summary:
+ // See dojo.data.api.Write.setValue()
+ this._assertIsItem(item);
+
+ var update = {item: item};
+ if(this._assertIsAttribute(attribute)){
+ switch(attribute){
+ case "link":
+ update.links = item.links;
+ this._addUpdate(update);
+ item.links = null;
+ item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
+ item.isDirty = true;
+ return true;
+ case "author":
+ update.authors = item.authors;
+ this._addUpdate(update);
+ item.authors = null;
+ item.addAuthor(value.name, value.email, value.uri);
+ item.isDirty = true;
+ return true;
+ case "contributor":
+ update.contributors = item.contributors;
+ this._addUpdate(update);
+ item.contributors = null;
+ item.addContributor(value.name, value.email, value.uri);
+ item.isDirty = true;
+ return true;
+ case "category":
+ update.categories = item.categories;
+ this._addUpdate(update);
+ item.categories = null;
+ item.addCategory(value.scheme, value.term, value.label);
+ item.isDirty = true;
+ return true;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = value;
+ item.isDirty = true;
+ return true;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = dojox.atom.io.model.util.createDate(value);
+ item.isDirty = true;
+ return true;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = new dojox.atom.io.model.Content(attribute);
+ item[attribute].value = value;
+ item.isDirty = true;
+ return true;
+ default:
+ update[attribute] = item[attribute];
+ this._addUpdate(update);
+ item[attribute] = value;
+ item.isDirty = true;
+ return true;
+ }
+ }
+ return false;
+ },
+
+ setValues: function(/* item */ item,
+ /* string */ attribute,
+ /* array */ values){
+ // summary:
+ // See dojo.data.api.Write.setValues()
+ if(values.length === 0){
+ return this.unsetAttribute(item, attribute);
+ }
+ this._assertIsItem(item);
+
+ var update = {item: item};
+ var value;
+ var i;
+ if(this._assertIsAttribute(attribute)){
+ switch(attribute){
+ case "link":
+ update.links = item.links;
+ item.links = null;
+ for(i in values){
+ value = values[i];
+ item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
+ }
+ item.isDirty = true;
+ return true;
+ case "author":
+ update.authors = item.authors;
+ item.authors = null;
+ for(i in values){
+ value = values[i];
+ item.addAuthor(value.name, value.email, value.uri);
+ }
+ item.isDirty = true;
+ return true;
+ case "contributor":
+ update.contributors = item.contributors;
+ item.contributors = null;
+ for(i in values){
+ value = values[i];
+ item.addContributor(value.name, value.email, value.uri);
+ }
+ item.isDirty = true;
+ return true;
+ case "categories":
+ update.categories = item.categories;
+ item.categories = null;
+ for(i in values){
+ value = values[i];
+ item.addCategory(value.scheme, value.term, value.label);
+ }
+ item.isDirty = true;
+ return true;
+ case "icon":
+ case "id":
+ case "logo":
+ case "xmlBase":
+ case "rights":
+ update[attribute] = item[attribute];
+ item[attribute] = values[0];
+ item.isDirty = true;
+ return true;
+ case "updated":
+ case "published":
+ case "issued":
+ case "modified":
+ update[attribute] = item[attribute];
+ item[attribute] = dojox.atom.io.model.util.createDate(values[0]);
+ item.isDirty = true;
+ return true;
+ case "content":
+ case "summary":
+ case "title":
+ case "subtitle":
+ update[attribute] = item[attribute];
+ item[attribute] = new dojox.atom.io.model.Content(attribute);
+ item[attribute].values[0] = values[0];
+ item.isDirty = true;
+ return true;
+ default:
+ update[attribute] = item[attribute];
+ item[attribute] = values[0];
+ item.isDirty = true;
+ return true;
+ }
+ }
+ this._addUpdate(update);
+ return false;
+ },
+
+ unsetAttribute: function( /* item */ item,
+ /* string */ attribute){
+ // summary:
+ // See dojo.data.api.Write.unsetAttribute()
+ this._assertIsItem(item);
+ if(this._assertIsAttribute(attribute)){
+ if(item[attribute] !== null){
+ var update = {item: item};
+ switch(attribute){
+ case "author":
+ case "contributor":
+ case "link":
+ update[attribute+"s"] = item[attribute+"s"];
+ break;
+ case "category":
+ update.categories = item.categories;
+ break;
+ default:
+ update[attribute] = item[attribute];
+ break;
+ }
+ item.isDirty = true;
+ item[attribute] = null;
+ this._addUpdate(update);
+ return true;
+ }
+ }
+ return false; // boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Write.save()
+ // keywordArgs:
+ // {
+ // onComplete: function
+ // onError: function
+ // scope: object
+ // }
+ var i;
+ for(i in this._adds){
+ this._atomIO.addEntry(this._adds[i], null, function(){}, keywordArgs.onError, false, keywordArgs.scope);
+ }
+
+ this._adds = null;
+
+ for(i in this._updates){
+ this._atomIO.updateEntry(this._updates[i].item, function(){}, keywordArgs.onError, false, this.xmethod, keywordArgs.scope);
+ }
+
+ this._updates = null;
+
+ for(i in this._deletes){
+ this._atomIO.removeEntry(this._deletes[i], function(){}, keywordArgs.onError, this.xmethod, keywordArgs.scope);
+ }
+
+ this._deletes = null;
+
+ this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
+
+ if(keywordArgs.onComplete){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onComplete.call(scope);
+ }
+ },
+
+ revert: function(){
+ // summary:
+ // See dojo.data.api.Write.revert()
+ var i;
+ for(i in this._adds){
+ this._feed.removeEntry(this._adds[i]);
+ }
+
+ this._adds = null;
+
+ var update, item, key;
+ for(i in this._updates){
+ update = this._updates[i];
+ item = update.item;
+ for(key in update){
+ if(key !== "item"){
+ item[key] = update[key];
+ }
+ }
+ }
+ this._updates = null;
+
+ for(i in this._deletes){
+ this._feed.addEntry(this._deletes[i]);
+ }
+ this._deletes = null;
+ return true;
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary:
+ // See dojo.data.api.Write.isDirty()
+ if(item){
+ this._assertIsItem(item);
+ return item.isDirty?true:false; //boolean
+ }
+ return (this._adds !== null || this._updates !== null); //boolean
+ }
+});
+dojo.extend(dojox.data.AppStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/AtomReadStore.js b/js/dojo-1.6/dojox/data/AtomReadStore.js
new file mode 100644
index 0000000..0caf57c
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AtomReadStore.js
@@ -0,0 +1,563 @@
+/*
+ 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["dojox.data.AtomReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AtomReadStore"] = true;
+dojo.provide("dojox.data.AtomReadStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+dojo.experimental("dojox.data.AtomReadStore");
+
+dojo.declare("dojox.data.AtomReadStore", null, {
+ // summary:
+ // A read only data store for Atom XML based services or documents
+ // description:
+ // A data store for Atom XML based services or documents. This store is still under development
+ // and doesn't support wildcard filtering yet. Attribute filtering is limited to category or id.
+
+ constructor: function(/* object */ args){
+ // summary:
+ // Constructor for the AtomRead store.
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url to a service or an XML document that represents the store
+ // unescapeHTML: A boolean to specify whether or not to unescape HTML text
+ // sendQuery: A boolean indicate to add a query string to the service URL
+
+ if(args){
+ this.url = args.url;
+ this.rewriteUrl = args.rewriteUrl;
+ this.label = args.label || this.label;
+ this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery);
+ this.unescapeHTML = args.unescapeHTML;
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ if(!this.url){
+ throw new Error("AtomReadStore: a URL must be specified when creating the data store");
+ }
+ },
+
+ //Values that may be set by the parser.
+ //Ergo, have to be instantiated to something
+ //So the parser knows how to set them.
+ url: "",
+
+ label: "title",
+
+ sendQuery: false,
+
+ unescapeHTML: false,
+
+ //Configurable preventCache option for the URL.
+ urlPreventCache: false,
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of an object created by the AtomReadStore instance.
+ // Accepted attributes are id, subtitle, title, summary, content, author, updated,
+ // published, category, link and alternate
+ // item:
+ // An item returned by a call to the 'fetch' method.
+ // attribute:
+ // A attribute of the Atom Entry
+ // defaultValue:
+ // A default value
+ // returns:
+ // An attribute value found, otherwise 'defaultValue'
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ this._initItem(item);
+ attribute = attribute.toLowerCase();
+ //If the attribute has previously been retrieved, then return it
+ if(!item._attribs[attribute] && !item._parsed){
+ this._parseItem(item);
+ item._parsed = true;
+ }
+ var retVal = item._attribs[attribute];
+
+ if(!retVal && attribute == "summary"){
+ var content = this.getValue(item, "content");
+ var regexp = new RegExp("/(<([^>]+)>)/g", "i");
+ var text = content.text.replace(regexp,"");
+ retVal = {
+ text: text.substring(0, Math.min(400, text.length)),
+ type: "text"
+ };
+ item._attribs[attribute] = retVal;
+ }
+
+ if(retVal && this.unescapeHTML){
+ if((attribute == "content" || attribute == "summary" || attribute == "subtitle") && !item["_"+attribute+"Escaped"]){
+ retVal.text = this._unescapeHTML(retVal.text);
+ item["_"+attribute+"Escaped"] = true;
+ }
+ }
+ return retVal ? dojo.isArray(retVal) ? retVal[0]: retVal : defaultValue;
+ },
+
+ getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of an object created by the AtomReadStore instance.
+ // Accepted attributes are id, subtitle, title, summary, content, author, updated,
+ // published, category, link and alternate
+ // item:
+ // An item returned by a call to the 'fetch' method.
+ // attribute:
+ // A attribute of the Atom Entry
+ // returns:
+ // An array of values for the attribute value found, otherwise 'defaultValue'
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ this._initItem(item);
+ attribute = attribute.toLowerCase();
+ //If the attribute has previously been retrieved, then return it
+ if(!item._attribs[attribute]){
+ this._parseItem(item);
+ }
+ var retVal = item._attribs[attribute];
+ return retVal ? ((retVal.length !== undefined && typeof(retVal) !== "string") ? retVal : [retVal]) : undefined;
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // Return an array of attribute names
+ // description:
+ // 'item' must be have been created by the AtomReadStore instance.
+ // tag names of child elements and XML attribute names of attributes
+ // specified to the element are returned along with special attribute
+ // names applicable to the element including "tagName", "childNodes"
+ // if the element has child elements, "text()" if the element has
+ // child text nodes, and attribute names in '_attributeMap' that match
+ // the tag name of the element.
+ // item:
+ // An XML element
+ // returns:
+ // An array of attributes found
+ this._assertIsItem(item);
+ if(!item._attribs){
+ this._initItem(item);
+ this._parseItem(item);
+ }
+ var attrNames = [];
+ for(var x in item._attribs){
+ attrNames.push(x);
+ }
+ return attrNames; //array
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Check whether an element has the attribute
+ // item:
+ // 'item' must be created by the AtomReadStore instance.
+ // attribute:
+ // An attribute of an Atom Entry item.
+ // returns:
+ // True if the element has the attribute, otherwise false
+ return (this.getValue(item, attribute) !== undefined); //boolean
+ },
+
+ containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value){
+ // summary:
+ // Check whether the attribute values contain the value
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the attribute values contain the value, otherwise false
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; i++){
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element)
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ if(something && something.element && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element) and loaded
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item (XML element)
+ // keywordArgs:
+ // object containing the args for loadItem. See dojo.data.api.Read.loadItem()
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ // returns:
+ // "dojo.data.api.Read" and "dojo.data.api.Write"
+ var features = {
+ "dojo.data.api.Read": true
+ };
+ return features; //array
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label && label.text){
+ return label.text;
+ }else if(label){
+ return label.toString();
+ }else{
+ return undefined;
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+ getFeedValue: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ var values = this.getFeedValues(attribute, defaultValue);
+ if(dojo.isArray(values)){
+ return values[0];
+ }
+ return values;
+ },
+
+ getFeedValues: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ if(!this.doc){
+ return defaultValue;
+ }
+ if(!this._feedMetaData){
+ this._feedMetaData = {
+ element: this.doc.getElementsByTagName("feed")[0],
+ store: this,
+ _attribs: {}
+ };
+ this._parseItem(this._feedMetaData);
+ }
+ return this._feedMetaData._attribs[attribute] || defaultValue;
+ },
+
+ _initItem: function(item){
+ // summary:
+ // Initializes an item before it can be parsed.
+ if(!item._attribs){
+ item._attribs = {};
+ }
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Retrieves the items from the Atom XML document.
+ var url = this._getFetchUrl(request);
+ if(!url){
+ errorHandler(new Error("No URL specified."));
+ return;
+ }
+ var localRequest = (!this.sendQuery ? request : null); // use request for _getItems()
+
+ var _this = this;
+ var docHandler = function(data){
+ _this.doc = data;
+ var items = _this._getItems(data, localRequest);
+ var query = request.query;
+ if(query){
+ if(query.id){
+ items = dojo.filter(items, function(item){
+ return (_this.getValue(item, "id") == query.id);
+ });
+ }else if(query.category){
+ items = dojo.filter(items, function(entry){
+ var cats = _this.getValues(entry, "category");
+ if(!cats){
+ return false;
+ }
+ return dojo.some(cats, "return item.term=='"+query.category+"'");
+ });
+ }
+ }
+
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ };
+
+ if(this.doc){
+ docHandler(this.doc);
+ }else{
+ var getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(docHandler);
+
+ getHandler.addErrback(function(data){
+ errorHandler(data, request);
+ });
+ }
+ },
+
+ _getFetchUrl: function(request){
+ if(!this.sendQuery){
+ return this.url;
+ }
+ var query = request.query;
+ if(!query){
+ return this.url;
+ }
+ if(dojo.isString(query)){
+ return this.url + query;
+ }
+ var queryString = "";
+ for(var name in query){
+ var value = query[name];
+ if(value){
+ if(queryString){
+ queryString += "&";
+ }
+ queryString += (name + "=" + value);
+ }
+ }
+ if(!queryString){
+ return this.url;
+ }
+ //Check to see if the URL already has query params or not.
+ var fullUrl = this.url;
+ if(fullUrl.indexOf("?") < 0){
+ fullUrl += "?";
+ }else{
+ fullUrl += "&";
+ }
+ return fullUrl + queryString;
+ },
+
+ _getItems: function(document, request){
+ // summary:
+ // Parses the document in a first pass
+ if(this._items){
+ return this._items;
+ }
+ var items = [];
+ var nodes = [];
+
+ if(document.childNodes.length < 1){
+ this._items = items;
+ console.log("dojox.data.AtomReadStore: Received an invalid Atom document. Check the content type header");
+ return items;
+ }
+
+ var feedNodes = dojo.filter(document.childNodes, "return item.tagName && item.tagName.toLowerCase() == 'feed'");
+
+ var query = request.query;
+
+ if(!feedNodes || feedNodes.length != 1){
+ console.log("dojox.data.AtomReadStore: Received an invalid Atom document, number of feed tags = " + (feedNodes? feedNodes.length : 0));
+ return items;
+ }
+
+ nodes = dojo.filter(feedNodes[0].childNodes, "return item.tagName && item.tagName.toLowerCase() == 'entry'");
+
+ if(request.onBegin){
+ request.onBegin(nodes.length, this.sendQuery ? request : {});
+ }
+
+ for(var i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ if(node.nodeType != 1 /*ELEMENT_NODE*/){
+ continue;
+ }
+ items.push(this._getItem(node));
+ }
+ this._items = items;
+ return items;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* internal API */
+
+ _getItem: function(element){
+ return {
+ element: element,
+ store: this
+ };
+ },
+
+ _parseItem: function(item){
+ var attribs = item._attribs;
+ var _this = this;
+ var text, type;
+
+ function getNodeText(node){
+ var txt = node.textContent || node.innerHTML || node.innerXML;
+ if(!txt && node.childNodes[0]){
+ var child = node.childNodes[0];
+ if(child && (child.nodeType == 3 || child.nodeType == 4)){
+ txt = node.childNodes[0].nodeValue;
+ }
+ }
+ return txt;
+ }
+ function parseTextAndType(node){
+ return {text: getNodeText(node),type: node.getAttribute("type")};
+ }
+ dojo.forEach(item.element.childNodes, function(node){
+ var tagName = node.tagName ? node.tagName.toLowerCase() : "";
+ switch(tagName){
+ case "title":
+ attribs[tagName] = {
+ text: getNodeText(node),
+ type: node.getAttribute("type")
+ }; break;
+ case "subtitle":
+ case "summary":
+ case "content":
+ attribs[tagName] = parseTextAndType(node);
+ break;
+ case "author":
+ var nameNode ,uriNode;
+ dojo.forEach(node.childNodes, function(child){
+ if(!child.tagName){
+ return;
+ }
+ switch(child.tagName.toLowerCase()){
+ case "name":
+ nameNode = child;
+ break;
+ case "uri":
+ uriNode = child;
+ break;
+ }
+ });
+ var author = {};
+ if(nameNode && nameNode.length == 1){
+ author.name = getNodeText(nameNode[0]);
+ }
+ if(uriNode && uriNode.length == 1){
+ author.uri = getNodeText(uriNode[0]);
+ }
+ attribs[tagName] = author;
+ break;
+ case "id":
+ attribs[tagName] = getNodeText(node);
+ break;
+ case "updated":
+ attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node) );
+ break;
+ case "published":
+ attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node));
+ break;
+ case "category":
+ if(!attribs[tagName]){
+ attribs[tagName] = [];
+ }
+ attribs[tagName].push({scheme:node.getAttribute("scheme"), term: node.getAttribute("term")});
+ break;
+ case "link":
+ if(!attribs[tagName]){
+ attribs[tagName] = [];
+ }
+ var link = {
+ rel: node.getAttribute("rel"),
+ href: node.getAttribute("href"),
+ type: node.getAttribute("type")};
+ attribs[tagName].push(link);
+
+ if(link.rel == "alternate"){
+ attribs["alternate"] = link;
+ }
+ break;
+ default:
+ break;
+ }
+ });
+ },
+
+ _unescapeHTML : function(text){
+ //Replace HTML character codes with their unencoded equivalents, e.g. &#8217; with '
+ text = text.replace(/&#8217;/m , "'").replace(/&#8243;/m , "\"").replace(/&#60;/m,">").replace(/&#62;/m,"<").replace(/&#38;/m,"&");
+ return text;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.AtomReadStore: Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.AtomReadStore: Invalid attribute argument.");
+ }
+ }
+});
+dojo.extend(dojox.data.AtomReadStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/AtomReadStore.xd.js b/js/dojo-1.6/dojox/data/AtomReadStore.xd.js
new file mode 100644
index 0000000..28a95b7
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/AtomReadStore.xd.js
@@ -0,0 +1,570 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.AtomReadStore"],
+["require", "dojo.data.util.filter"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.date.stamp"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.AtomReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.AtomReadStore"] = true;
+dojo.provide("dojox.data.AtomReadStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+dojo.experimental("dojox.data.AtomReadStore");
+
+dojo.declare("dojox.data.AtomReadStore", null, {
+ // summary:
+ // A read only data store for Atom XML based services or documents
+ // description:
+ // A data store for Atom XML based services or documents. This store is still under development
+ // and doesn't support wildcard filtering yet. Attribute filtering is limited to category or id.
+
+ constructor: function(/* object */ args){
+ // summary:
+ // Constructor for the AtomRead store.
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url to a service or an XML document that represents the store
+ // unescapeHTML: A boolean to specify whether or not to unescape HTML text
+ // sendQuery: A boolean indicate to add a query string to the service URL
+
+ if(args){
+ this.url = args.url;
+ this.rewriteUrl = args.rewriteUrl;
+ this.label = args.label || this.label;
+ this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery);
+ this.unescapeHTML = args.unescapeHTML;
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ if(!this.url){
+ throw new Error("AtomReadStore: a URL must be specified when creating the data store");
+ }
+ },
+
+ //Values that may be set by the parser.
+ //Ergo, have to be instantiated to something
+ //So the parser knows how to set them.
+ url: "",
+
+ label: "title",
+
+ sendQuery: false,
+
+ unescapeHTML: false,
+
+ //Configurable preventCache option for the URL.
+ urlPreventCache: false,
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of an object created by the AtomReadStore instance.
+ // Accepted attributes are id, subtitle, title, summary, content, author, updated,
+ // published, category, link and alternate
+ // item:
+ // An item returned by a call to the 'fetch' method.
+ // attribute:
+ // A attribute of the Atom Entry
+ // defaultValue:
+ // A default value
+ // returns:
+ // An attribute value found, otherwise 'defaultValue'
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ this._initItem(item);
+ attribute = attribute.toLowerCase();
+ //If the attribute has previously been retrieved, then return it
+ if(!item._attribs[attribute] && !item._parsed){
+ this._parseItem(item);
+ item._parsed = true;
+ }
+ var retVal = item._attribs[attribute];
+
+ if(!retVal && attribute == "summary"){
+ var content = this.getValue(item, "content");
+ var regexp = new RegExp("/(<([^>]+)>)/g", "i");
+ var text = content.text.replace(regexp,"");
+ retVal = {
+ text: text.substring(0, Math.min(400, text.length)),
+ type: "text"
+ };
+ item._attribs[attribute] = retVal;
+ }
+
+ if(retVal && this.unescapeHTML){
+ if((attribute == "content" || attribute == "summary" || attribute == "subtitle") && !item["_"+attribute+"Escaped"]){
+ retVal.text = this._unescapeHTML(retVal.text);
+ item["_"+attribute+"Escaped"] = true;
+ }
+ }
+ return retVal ? dojo.isArray(retVal) ? retVal[0]: retVal : defaultValue;
+ },
+
+ getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of an object created by the AtomReadStore instance.
+ // Accepted attributes are id, subtitle, title, summary, content, author, updated,
+ // published, category, link and alternate
+ // item:
+ // An item returned by a call to the 'fetch' method.
+ // attribute:
+ // A attribute of the Atom Entry
+ // returns:
+ // An array of values for the attribute value found, otherwise 'defaultValue'
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ this._initItem(item);
+ attribute = attribute.toLowerCase();
+ //If the attribute has previously been retrieved, then return it
+ if(!item._attribs[attribute]){
+ this._parseItem(item);
+ }
+ var retVal = item._attribs[attribute];
+ return retVal ? ((retVal.length !== undefined && typeof(retVal) !== "string") ? retVal : [retVal]) : undefined;
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // Return an array of attribute names
+ // description:
+ // 'item' must be have been created by the AtomReadStore instance.
+ // tag names of child elements and XML attribute names of attributes
+ // specified to the element are returned along with special attribute
+ // names applicable to the element including "tagName", "childNodes"
+ // if the element has child elements, "text()" if the element has
+ // child text nodes, and attribute names in '_attributeMap' that match
+ // the tag name of the element.
+ // item:
+ // An XML element
+ // returns:
+ // An array of attributes found
+ this._assertIsItem(item);
+ if(!item._attribs){
+ this._initItem(item);
+ this._parseItem(item);
+ }
+ var attrNames = [];
+ for(var x in item._attribs){
+ attrNames.push(x);
+ }
+ return attrNames; //array
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Check whether an element has the attribute
+ // item:
+ // 'item' must be created by the AtomReadStore instance.
+ // attribute:
+ // An attribute of an Atom Entry item.
+ // returns:
+ // True if the element has the attribute, otherwise false
+ return (this.getValue(item, attribute) !== undefined); //boolean
+ },
+
+ containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value){
+ // summary:
+ // Check whether the attribute values contain the value
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the attribute values contain the value, otherwise false
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; i++){
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element)
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ if(something && something.element && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element) and loaded
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item (XML element)
+ // keywordArgs:
+ // object containing the args for loadItem. See dojo.data.api.Read.loadItem()
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ // returns:
+ // "dojo.data.api.Read" and "dojo.data.api.Write"
+ var features = {
+ "dojo.data.api.Read": true
+ };
+ return features; //array
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label && label.text){
+ return label.text;
+ }else if(label){
+ return label.toString();
+ }else{
+ return undefined;
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+ getFeedValue: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ var values = this.getFeedValues(attribute, defaultValue);
+ if(dojo.isArray(values)){
+ return values[0];
+ }
+ return values;
+ },
+
+ getFeedValues: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ if(!this.doc){
+ return defaultValue;
+ }
+ if(!this._feedMetaData){
+ this._feedMetaData = {
+ element: this.doc.getElementsByTagName("feed")[0],
+ store: this,
+ _attribs: {}
+ };
+ this._parseItem(this._feedMetaData);
+ }
+ return this._feedMetaData._attribs[attribute] || defaultValue;
+ },
+
+ _initItem: function(item){
+ // summary:
+ // Initializes an item before it can be parsed.
+ if(!item._attribs){
+ item._attribs = {};
+ }
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Retrieves the items from the Atom XML document.
+ var url = this._getFetchUrl(request);
+ if(!url){
+ errorHandler(new Error("No URL specified."));
+ return;
+ }
+ var localRequest = (!this.sendQuery ? request : null); // use request for _getItems()
+
+ var _this = this;
+ var docHandler = function(data){
+ _this.doc = data;
+ var items = _this._getItems(data, localRequest);
+ var query = request.query;
+ if(query){
+ if(query.id){
+ items = dojo.filter(items, function(item){
+ return (_this.getValue(item, "id") == query.id);
+ });
+ }else if(query.category){
+ items = dojo.filter(items, function(entry){
+ var cats = _this.getValues(entry, "category");
+ if(!cats){
+ return false;
+ }
+ return dojo.some(cats, "return item.term=='"+query.category+"'");
+ });
+ }
+ }
+
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ };
+
+ if(this.doc){
+ docHandler(this.doc);
+ }else{
+ var getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(docHandler);
+
+ getHandler.addErrback(function(data){
+ errorHandler(data, request);
+ });
+ }
+ },
+
+ _getFetchUrl: function(request){
+ if(!this.sendQuery){
+ return this.url;
+ }
+ var query = request.query;
+ if(!query){
+ return this.url;
+ }
+ if(dojo.isString(query)){
+ return this.url + query;
+ }
+ var queryString = "";
+ for(var name in query){
+ var value = query[name];
+ if(value){
+ if(queryString){
+ queryString += "&";
+ }
+ queryString += (name + "=" + value);
+ }
+ }
+ if(!queryString){
+ return this.url;
+ }
+ //Check to see if the URL already has query params or not.
+ var fullUrl = this.url;
+ if(fullUrl.indexOf("?") < 0){
+ fullUrl += "?";
+ }else{
+ fullUrl += "&";
+ }
+ return fullUrl + queryString;
+ },
+
+ _getItems: function(document, request){
+ // summary:
+ // Parses the document in a first pass
+ if(this._items){
+ return this._items;
+ }
+ var items = [];
+ var nodes = [];
+
+ if(document.childNodes.length < 1){
+ this._items = items;
+ console.log("dojox.data.AtomReadStore: Received an invalid Atom document. Check the content type header");
+ return items;
+ }
+
+ var feedNodes = dojo.filter(document.childNodes, "return item.tagName && item.tagName.toLowerCase() == 'feed'");
+
+ var query = request.query;
+
+ if(!feedNodes || feedNodes.length != 1){
+ console.log("dojox.data.AtomReadStore: Received an invalid Atom document, number of feed tags = " + (feedNodes? feedNodes.length : 0));
+ return items;
+ }
+
+ nodes = dojo.filter(feedNodes[0].childNodes, "return item.tagName && item.tagName.toLowerCase() == 'entry'");
+
+ if(request.onBegin){
+ request.onBegin(nodes.length, this.sendQuery ? request : {});
+ }
+
+ for(var i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ if(node.nodeType != 1 /*ELEMENT_NODE*/){
+ continue;
+ }
+ items.push(this._getItem(node));
+ }
+ this._items = items;
+ return items;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* internal API */
+
+ _getItem: function(element){
+ return {
+ element: element,
+ store: this
+ };
+ },
+
+ _parseItem: function(item){
+ var attribs = item._attribs;
+ var _this = this;
+ var text, type;
+
+ function getNodeText(node){
+ var txt = node.textContent || node.innerHTML || node.innerXML;
+ if(!txt && node.childNodes[0]){
+ var child = node.childNodes[0];
+ if(child && (child.nodeType == 3 || child.nodeType == 4)){
+ txt = node.childNodes[0].nodeValue;
+ }
+ }
+ return txt;
+ }
+ function parseTextAndType(node){
+ return {text: getNodeText(node),type: node.getAttribute("type")};
+ }
+ dojo.forEach(item.element.childNodes, function(node){
+ var tagName = node.tagName ? node.tagName.toLowerCase() : "";
+ switch(tagName){
+ case "title":
+ attribs[tagName] = {
+ text: getNodeText(node),
+ type: node.getAttribute("type")
+ }; break;
+ case "subtitle":
+ case "summary":
+ case "content":
+ attribs[tagName] = parseTextAndType(node);
+ break;
+ case "author":
+ var nameNode ,uriNode;
+ dojo.forEach(node.childNodes, function(child){
+ if(!child.tagName){
+ return;
+ }
+ switch(child.tagName.toLowerCase()){
+ case "name":
+ nameNode = child;
+ break;
+ case "uri":
+ uriNode = child;
+ break;
+ }
+ });
+ var author = {};
+ if(nameNode && nameNode.length == 1){
+ author.name = getNodeText(nameNode[0]);
+ }
+ if(uriNode && uriNode.length == 1){
+ author.uri = getNodeText(uriNode[0]);
+ }
+ attribs[tagName] = author;
+ break;
+ case "id":
+ attribs[tagName] = getNodeText(node);
+ break;
+ case "updated":
+ attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node) );
+ break;
+ case "published":
+ attribs[tagName] = dojo.date.stamp.fromISOString(getNodeText(node));
+ break;
+ case "category":
+ if(!attribs[tagName]){
+ attribs[tagName] = [];
+ }
+ attribs[tagName].push({scheme:node.getAttribute("scheme"), term: node.getAttribute("term")});
+ break;
+ case "link":
+ if(!attribs[tagName]){
+ attribs[tagName] = [];
+ }
+ var link = {
+ rel: node.getAttribute("rel"),
+ href: node.getAttribute("href"),
+ type: node.getAttribute("type")};
+ attribs[tagName].push(link);
+
+ if(link.rel == "alternate"){
+ attribs["alternate"] = link;
+ }
+ break;
+ default:
+ break;
+ }
+ });
+ },
+
+ _unescapeHTML : function(text){
+ //Replace HTML character codes with their unencoded equivalents, e.g. &#8217; with '
+ text = text.replace(/&#8217;/m , "'").replace(/&#8243;/m , "\"").replace(/&#60;/m,">").replace(/&#62;/m,"<").replace(/&#38;/m,"&");
+ return text;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.AtomReadStore: Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.AtomReadStore: Invalid attribute argument.");
+ }
+ }
+});
+dojo.extend(dojox.data.AtomReadStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/CdfStore.js b/js/dojo-1.6/dojox/data/CdfStore.js
new file mode 100644
index 0000000..5234210
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CdfStore.js
@@ -0,0 +1,609 @@
+/*
+ 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["dojox.data.CdfStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CdfStore"] = true;
+dojo.provide("dojox.data.CdfStore");
+dojo.require("dojo.data.util.sorter");
+
+
+
+dojox.data.ASYNC_MODE = 0;
+dojox.data.SYNC_MODE = 1;
+
+dojo.declare("dojox.data.CdfStore", null, {
+ // summary:
+ // IMPORTANT: The CDF Store is designed to work with Tibco GI, and references Tibco's
+ // JSX3 JavaScript library and will not work without it.
+ //
+ // The CDF Store implements dojo.data.Read, Write, and Identity api's. It is a local
+ // (in memory) store that handles XML documents formatted according to the
+ // Common Data Format (CDF) spec:
+ // http://www.tibco.com/devnet/resources/gi/3_1/tips_and_techniques/CommonDataFormatCDF.pdf
+ //
+ // The purpose of this store is to provide a glue between a jsx3 CDF file and a Dijit.
+ //
+ // While a CDF document is an XML file, other than the initial input, all data returned
+ // from and written to this store should be in object format.
+ //
+ // identity: [const] String
+ // The unique identifier for each item. Defaults to "jsxid" which is standard for a CDF
+ // document. Should not be changed.
+ identity: "jsxid",
+ //
+ // url : String
+ // The location from which to fetch the XML (CDF) document.
+ url: "",
+ //
+ // xmlStr: String
+ // A string that can be parsed into an XML document and should be formatted according
+ // to the CDF spec.
+ // example:
+ // | '<data jsxid="jsxroot"><record jsxtext="A"/><record jsxtext="B" jsxid="2" jsxid="2"/></data>'
+ xmlStr:"",
+ //
+ // data: Object
+ // A object that will be converted into the xmlStr property, and then parsed into a CDF.
+ data:null,
+ //
+ // label: String
+ // The property within each item used to define the item.
+ label: "",
+ //
+ // mode [const]: dojox.data.ASYNC_MODE | dojox.data.SYNC_MODE
+ // This store supports syncronous fetches if this property is set to dojox.data.SYNC_MODE.
+ mode:dojox.data.ASYNC_MODE,
+
+ constructor: function(/* Object */ args){
+ // summary:
+ // Constructor for the CDF store. Instantiate a new CdfStore.
+ //
+ if(args){
+ this.url = args.url;
+ this.xmlStr = args.xmlStr || args.str;
+ if(args.data){
+ this.xmlStr = this._makeXmlString(args.data);
+ }
+ this.identity = args.identity || this.identity;
+ this.label = args.label || this.label;
+ this.mode = args.mode !== undefined ? args.mode : this.mode;
+ }
+ this._modifiedItems = {};
+
+ this.byId = this.fetchItemByIdentity;
+ },
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* value? */ defaultValue){
+ // summary:
+ // Return an property value of an item
+ //
+ return item.getAttribute(property) || defaultValue; // anything
+ },
+
+ getValues: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Return an array of values
+ //
+ // TODO!!! Can't find an example of an array in any CDF files
+ //
+ var v = this.getValue(item, property, []);
+ return dojo.isArray(v) ? v : [v];
+ },
+
+ getAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Return an array of property names
+ //
+ return item.getAttributeNames(); // Array
+ },
+
+ hasAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Check whether an item has a property
+ //
+ return (this.getValue(item, property) !== undefined); // Boolean
+ },
+
+ hasProperty: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Alias for hasAttribute
+ return this.hasAttribute(item, property);
+ },
+
+ containsValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* anything */ value){
+ // summary:
+ // Check whether an item contains a value
+ //
+ var values = this.getValues(item, property);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === null){ continue; }
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (jsx3.xml.Entity)
+ //
+ if(something.getClass && something.getClass().equals(jsx3.xml.Entity.jsxclass)){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is a jsx3.xml.Entity object and loaded
+ //
+ return this.isItem(something); // Boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item
+ // description:
+ // The store always loads all items, so if it's an item, then it's loaded.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ //
+ return {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Write": true,
+ "dojo.data.api.Identity":true
+ }; // Object
+ },
+
+ getLabel: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ //
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label){
+ return label.toString();
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // returns an array of what properties of the item that were used
+ // to generate its label
+ // See dojo.data.api.Read.getLabelAttributes()
+ //
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+
+ fetch: function(/* Object? */ request){
+ // summary:
+ // Returns an Array of items based on the request arguments.
+ // description:
+ // Returns an Array of items based on the request arguments.
+ // If the store is in ASYNC mode, the items should be expected in an onComplete
+ // method passed in the request object. If store is in SYNC mode, the items will
+ // be return directly as well as within the onComplete method.
+ // note:
+ // The mode can be set on store initialization or during a fetch as one of the
+ // parameters.
+ //
+ // query: String
+ // The items in the store are treated as objects, but this is reading an XML
+ // document. Further, the actual querying of the items takes place in Tibco GI's
+ // jsx3.xml.Entity. Therefore, we are using their syntax which is xpath.
+ // Note:
+ // As conforming to a CDF document, most, if not all nodes are considered "records"
+ // and their tagNames are as such. The root node is named "data".
+ //
+ // examples:
+ // All items:
+ // | store.fetch({query:"*"});
+ // Item with a jsxid attribute equal to "1" (note you could use byId for this)
+ // | store.fetch({query:"//record[@jsxid='1']"});
+ // All items with any jsxid attribute:
+ // | "//record[@jsxid='*']"
+ // The items with a jsxid of '1' or '4':
+ // | "//record[@jsxid='4' or @jsxid='1']"
+ // All children within a "group" node (could be multiple group nodes):
+ // "//group/record"
+ // All children within a specific group node:
+ // "//group[@name='mySecondGroup']/record"
+ // Any record, anywhere in the document:
+ // | "//record"
+ // Only the records beneath the root (data) node:
+ // | "//data/record"
+ //
+ // See:
+ // http://www.tibco.com/devnet/resources/gi/3_7/api/html/jsx3/xml/Entity.html#method:selectNodes
+ // http://www.w3.org/TR/xpath
+ // http://msdn.microsoft.com/en-us/library/ms256086.aspx
+ //
+ // See dojo.data.Read.fetch():
+ // onBegin
+ // onComplete
+ // onItem
+ // onError
+ // scope
+ // start
+ // count
+ // sort
+ //
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ if(request.mode !== undefined){
+ this.mode = request.mode;
+ }
+ var self = this;
+
+ var errorHandler = function(errorData){
+ if(request.onError){
+ var scope = request.scope || dojo.global;
+ request.onError.call(scope, errorData, request);
+ }else{
+ console.error("cdfStore Error:", errorData);
+ }
+ };
+
+ var fetchHandler = function(items, requestObject){
+ requestObject = requestObject || request;
+ var oldAbortFunction = requestObject.abort || null;
+ var aborted = false;
+
+ var startIndex = requestObject.start?requestObject.start:0;
+ var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
+
+ requestObject.abort = function(){
+ aborted = true;
+ if(oldAbortFunction){
+ oldAbortFunction.call(requestObject);
+ }
+ };
+
+ var scope = requestObject.scope || dojo.global;
+ if(!requestObject.store){
+ requestObject.store = self;
+ }
+ if(requestObject.onBegin){
+ requestObject.onBegin.call(scope, items.length, requestObject);
+ }
+ if(requestObject.sort){
+ items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
+ }
+
+ if(requestObject.onItem){
+ for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
+ var item = items[i];
+ if(!aborted){
+ requestObject.onItem.call(scope, item, requestObject);
+ }
+ }
+ }
+ if(requestObject.onComplete && !aborted){
+ if(!requestObject.onItem){
+ items = items.slice(startIndex, endIndex);
+ if(requestObject.byId){
+ items = items[0];
+ }
+ }
+ requestObject.onComplete.call(scope, items, requestObject);
+ }else{
+ items = items.slice(startIndex, endIndex);
+ if(requestObject.byId){
+ items = items[0];
+ }
+ }
+ return items;
+ };
+
+ if(!this.url && !this.data && !this.xmlStr){
+ errorHandler(new Error("No URL or data specified."));
+ return false;
+ }
+ var localRequest = request || "*"; // use request for _getItems()
+
+ if(this.mode == dojox.data.SYNC_MODE){
+ // sync mode. items returned directly
+ var res = this._loadCDF();
+ if(res instanceof Error){
+ if(request.onError){
+ request.onError.call(request.scope || dojo.global, res, request);
+ }else{
+ console.error("CdfStore Error:", res);
+ }
+ return res;
+ }
+ this.cdfDoc = res;
+
+ var items = this._getItems(this.cdfDoc, localRequest);
+ if(items && items.length > 0){
+ items = fetchHandler(items, request);
+ }else{
+ items = fetchHandler([], request);
+ }
+ return items;
+
+ }else{
+
+ // async mode. Return a Deferred.
+ var dfd = this._loadCDF();
+ dfd.addCallbacks(dojo.hitch(this, function(cdfDoc){
+ var items = this._getItems(this.cdfDoc, localRequest);
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ }),
+ dojo.hitch(this, function(err){
+ errorHandler(err, request);
+ }));
+
+ return dfd; // Object
+ }
+ },
+
+
+ _loadCDF: function(){
+ // summary:
+ // Internal method.
+ // If a cdfDoc exists, return it. Otherwise, get one from JSX3,
+ // load the data or url, and return the doc or a deferred.
+ var dfd = new dojo.Deferred();
+ if(this.cdfDoc){
+ if(this.mode == dojox.data.SYNC_MODE){
+ return this.cdfDoc; // jsx3.xml.CDF
+ }else{
+ setTimeout(dojo.hitch(this, function(){
+ dfd.callback(this.cdfDoc);
+ }), 0);
+ return dfd; // dojo.Deferred
+ }
+ }
+
+ this.cdfDoc = jsx3.xml.CDF.Document.newDocument();
+ this.cdfDoc.subscribe("response", this, function(evt){
+ dfd.callback(this.cdfDoc);
+ });
+ this.cdfDoc.subscribe("error", this, function(err){
+ dfd.errback(err);
+ });
+
+ this.cdfDoc.setAsync(!this.mode);
+ if(this.url){
+ this.cdfDoc.load(this.url);
+ }else if(this.xmlStr){
+ this.cdfDoc.loadXML(this.xmlStr);
+ if(this.cdfDoc.getError().code){
+ return new Error(this.cdfDoc.getError().description); // Error
+ }
+ }
+
+ if(this.mode == dojox.data.SYNC_MODE){
+ return this.cdfDoc; // jsx3.xml.CDF
+ }else{
+ return dfd; // dojo.Deferred
+ }
+ },
+
+ _getItems: function(/* jsx3.xml.Entity */cdfDoc, /* Object */request){
+ // summary:
+ // Internal method.
+ // Requests the items from jsx3.xml.Entity with an xpath query.
+ //
+ var itr = cdfDoc.selectNodes(request.query, false, 1);
+ var items = [];
+ while(itr.hasNext()){
+ items.push(itr.next());
+ }
+ return items;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* object? */ keywordArgs, /* object? || String? */parentInfo){
+ // summary:
+ // Creates a jsx3.xml.Entity item and inserts it either inside the
+ // parent or appends it to the root
+ //
+ keywordArgs = (keywordArgs || {});
+ if(keywordArgs.tagName){
+ // record tagName is automatic and this would add it
+ // as a property
+ if(keywordArgs.tagName!="record"){
+ // TODO: How about some sort of group?
+ console.warn("Only record inserts are supported at this time");
+ }
+ delete keywordArgs.tagName;
+ }
+ keywordArgs.jsxid = keywordArgs.jsxid || this.cdfDoc.getKey();
+ if(this.isItem(parentInfo)){
+ parentInfo = this.getIdentity(parentInfo);
+ }
+ var item = this.cdfDoc.insertRecord(keywordArgs, parentInfo);
+
+ this._makeDirty(item);
+
+ return item; // jsx3.xml.Entity
+ },
+
+ deleteItem: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Delete an jsx3.xml.Entity (wrapper to a XML element).
+ //
+ this.cdfDoc.deleteRecord(this.getIdentity(item));
+ this._makeDirty(item);
+ return true; //boolean
+ },
+
+ setValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* almost anything */ value){
+ // summary:
+ // Set an property value
+ //
+ this._makeDirty(item);
+ item.setAttribute(property, value);
+ return true; // Boolean
+ },
+
+ setValues: function(/* jsx3.xml.Entity */ item, /* String */ property, /*array*/ values){
+ // summary:
+ // Set property values
+ // TODO: Needs to be fully implemented.
+ //
+ this._makeDirty(item);
+ console.warn("cdfStore.setValues only partially implemented.");
+ return item.setAttribute(property, values);
+
+ },
+
+ unsetAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Remove an property
+ //
+ this._makeDirty(item);
+ item.removeAttribute(property);
+ return true; // Boolean
+ },
+
+ revert: function(){
+ // summary:
+ // Invalidate changes (new and/or modified elements)
+ // Resets data by simply deleting the reference to the cdfDoc.
+ // Subsequent fetches will load the new data.
+ // Note:
+ // Any items outside the store will no longer be valid and may cause errors.
+ //
+ delete this.cdfDoc;
+ this._modifiedItems = {};
+ return true; //boolean
+ },
+
+ isDirty: function(/* jsx3.xml.Entity ? */ item){
+ // summary:
+ // Check whether an item is new, modified or deleted.
+ // If no item is passed, checks if anything in the store has changed.
+ //
+ if(item){
+ return !!this._modifiedItems[this.getIdentity(item)]; // Boolean
+ }else{
+ var _dirty = false;
+ for(var nm in this._modifiedItems){ _dirty = true; break; }
+ return _dirty; // Boolean
+ }
+ },
+
+
+
+/* internal API */
+
+ _makeDirty: function(item){
+ // summary:
+ // Internal method.
+ // Marks items as modified, deleted or new.
+ var id = this.getIdentity(item);
+ this._modifiedItems[id] = item;
+ },
+
+
+ _makeXmlString: function(obj){
+ // summary:
+ // Internal method.
+ // Converts an object into an XML string.
+ //
+ var parseObj = function(obj, name){
+ var xmlStr = "";
+ var nm;
+ if(dojo.isArray(obj)){
+ for(var i=0;i<obj.length;i++){
+ xmlStr += parseObj(obj[i], name);
+ }
+ }else if(dojo.isObject(obj)){
+ xmlStr += '<'+name+' ';
+ for(nm in obj){
+ if(!dojo.isObject(obj[nm])){
+ xmlStr += nm+'="'+obj[nm]+'" ';
+ }
+ }
+ xmlStr +='>';
+ for(nm in obj){
+ if(dojo.isObject(obj[nm])){
+ xmlStr += parseObj(obj[nm], nm);
+ }
+ }
+ xmlStr += '</'+name+'>';
+ }
+ return xmlStr;
+ };
+ return parseObj(obj, "data");
+ },
+
+ /*************************************
+ * Dojo.data Identity implementation *
+ *************************************/
+ getIdentity: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Returns the identifier for an item.
+ //
+ return this.getValue(item, this.identity); // String
+ },
+
+ getIdentityAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Returns the property used for the identity.
+ //
+ return [this.identity]; // Array
+ },
+
+
+ fetchItemByIdentity: function(/* Object || String */ args){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity(keywordArgs)
+ //
+ // Note:
+ // This method can be synchronous if mode is set.
+ // Also, there is a more finger friendly alias of this method, byId();
+ if(dojo.isString(args)){
+ var id = args;
+ args = {query:"//record[@jsxid='"+id+"']", mode: dojox.data.SYNC_MODE};
+ }else{
+ if(args){
+ args.query = "//record[@jsxid='"+args.identity+"']";
+ }
+ if(!args.mode){args.mode = this.mode;}
+ }
+ args.byId = true;
+ return this.fetch(args); // dojo.Deferred || Array
+ },
+ byId: function(/* Object || String */ args){
+ // stub. See fetchItemByIdentity
+ }
+
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/CdfStore.xd.js b/js/dojo-1.6/dojox/data/CdfStore.xd.js
new file mode 100644
index 0000000..bebd06f
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CdfStore.xd.js
@@ -0,0 +1,614 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.CdfStore"],
+["require", "dojo.data.util.sorter"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.CdfStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CdfStore"] = true;
+dojo.provide("dojox.data.CdfStore");
+dojo.require("dojo.data.util.sorter");
+
+
+
+dojox.data.ASYNC_MODE = 0;
+dojox.data.SYNC_MODE = 1;
+
+dojo.declare("dojox.data.CdfStore", null, {
+ // summary:
+ // IMPORTANT: The CDF Store is designed to work with Tibco GI, and references Tibco's
+ // JSX3 JavaScript library and will not work without it.
+ //
+ // The CDF Store implements dojo.data.Read, Write, and Identity api's. It is a local
+ // (in memory) store that handles XML documents formatted according to the
+ // Common Data Format (CDF) spec:
+ // http://www.tibco.com/devnet/resources/gi/3_1/tips_and_techniques/CommonDataFormatCDF.pdf
+ //
+ // The purpose of this store is to provide a glue between a jsx3 CDF file and a Dijit.
+ //
+ // While a CDF document is an XML file, other than the initial input, all data returned
+ // from and written to this store should be in object format.
+ //
+ // identity: [const] String
+ // The unique identifier for each item. Defaults to "jsxid" which is standard for a CDF
+ // document. Should not be changed.
+ identity: "jsxid",
+ //
+ // url : String
+ // The location from which to fetch the XML (CDF) document.
+ url: "",
+ //
+ // xmlStr: String
+ // A string that can be parsed into an XML document and should be formatted according
+ // to the CDF spec.
+ // example:
+ // | '<data jsxid="jsxroot"><record jsxtext="A"/><record jsxtext="B" jsxid="2" jsxid="2"/></data>'
+ xmlStr:"",
+ //
+ // data: Object
+ // A object that will be converted into the xmlStr property, and then parsed into a CDF.
+ data:null,
+ //
+ // label: String
+ // The property within each item used to define the item.
+ label: "",
+ //
+ // mode [const]: dojox.data.ASYNC_MODE | dojox.data.SYNC_MODE
+ // This store supports syncronous fetches if this property is set to dojox.data.SYNC_MODE.
+ mode:dojox.data.ASYNC_MODE,
+
+ constructor: function(/* Object */ args){
+ // summary:
+ // Constructor for the CDF store. Instantiate a new CdfStore.
+ //
+ if(args){
+ this.url = args.url;
+ this.xmlStr = args.xmlStr || args.str;
+ if(args.data){
+ this.xmlStr = this._makeXmlString(args.data);
+ }
+ this.identity = args.identity || this.identity;
+ this.label = args.label || this.label;
+ this.mode = args.mode !== undefined ? args.mode : this.mode;
+ }
+ this._modifiedItems = {};
+
+ this.byId = this.fetchItemByIdentity;
+ },
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* value? */ defaultValue){
+ // summary:
+ // Return an property value of an item
+ //
+ return item.getAttribute(property) || defaultValue; // anything
+ },
+
+ getValues: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Return an array of values
+ //
+ // TODO!!! Can't find an example of an array in any CDF files
+ //
+ var v = this.getValue(item, property, []);
+ return dojo.isArray(v) ? v : [v];
+ },
+
+ getAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Return an array of property names
+ //
+ return item.getAttributeNames(); // Array
+ },
+
+ hasAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Check whether an item has a property
+ //
+ return (this.getValue(item, property) !== undefined); // Boolean
+ },
+
+ hasProperty: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Alias for hasAttribute
+ return this.hasAttribute(item, property);
+ },
+
+ containsValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* anything */ value){
+ // summary:
+ // Check whether an item contains a value
+ //
+ var values = this.getValues(item, property);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === null){ continue; }
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (jsx3.xml.Entity)
+ //
+ if(something.getClass && something.getClass().equals(jsx3.xml.Entity.jsxclass)){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is a jsx3.xml.Entity object and loaded
+ //
+ return this.isItem(something); // Boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item
+ // description:
+ // The store always loads all items, so if it's an item, then it's loaded.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ //
+ return {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Write": true,
+ "dojo.data.api.Identity":true
+ }; // Object
+ },
+
+ getLabel: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ //
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label){
+ return label.toString();
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // returns an array of what properties of the item that were used
+ // to generate its label
+ // See dojo.data.api.Read.getLabelAttributes()
+ //
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+
+ fetch: function(/* Object? */ request){
+ // summary:
+ // Returns an Array of items based on the request arguments.
+ // description:
+ // Returns an Array of items based on the request arguments.
+ // If the store is in ASYNC mode, the items should be expected in an onComplete
+ // method passed in the request object. If store is in SYNC mode, the items will
+ // be return directly as well as within the onComplete method.
+ // note:
+ // The mode can be set on store initialization or during a fetch as one of the
+ // parameters.
+ //
+ // query: String
+ // The items in the store are treated as objects, but this is reading an XML
+ // document. Further, the actual querying of the items takes place in Tibco GI's
+ // jsx3.xml.Entity. Therefore, we are using their syntax which is xpath.
+ // Note:
+ // As conforming to a CDF document, most, if not all nodes are considered "records"
+ // and their tagNames are as such. The root node is named "data".
+ //
+ // examples:
+ // All items:
+ // | store.fetch({query:"*"});
+ // Item with a jsxid attribute equal to "1" (note you could use byId for this)
+ // | store.fetch({query:"//record[@jsxid='1']"});
+ // All items with any jsxid attribute:
+ // | "//record[@jsxid='*']"
+ // The items with a jsxid of '1' or '4':
+ // | "//record[@jsxid='4' or @jsxid='1']"
+ // All children within a "group" node (could be multiple group nodes):
+ // "//group/record"
+ // All children within a specific group node:
+ // "//group[@name='mySecondGroup']/record"
+ // Any record, anywhere in the document:
+ // | "//record"
+ // Only the records beneath the root (data) node:
+ // | "//data/record"
+ //
+ // See:
+ // http://www.tibco.com/devnet/resources/gi/3_7/api/html/jsx3/xml/Entity.html#method:selectNodes
+ // http://www.w3.org/TR/xpath
+ // http://msdn.microsoft.com/en-us/library/ms256086.aspx
+ //
+ // See dojo.data.Read.fetch():
+ // onBegin
+ // onComplete
+ // onItem
+ // onError
+ // scope
+ // start
+ // count
+ // sort
+ //
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ if(request.mode !== undefined){
+ this.mode = request.mode;
+ }
+ var self = this;
+
+ var errorHandler = function(errorData){
+ if(request.onError){
+ var scope = request.scope || dojo.global;
+ request.onError.call(scope, errorData, request);
+ }else{
+ console.error("cdfStore Error:", errorData);
+ }
+ };
+
+ var fetchHandler = function(items, requestObject){
+ requestObject = requestObject || request;
+ var oldAbortFunction = requestObject.abort || null;
+ var aborted = false;
+
+ var startIndex = requestObject.start?requestObject.start:0;
+ var endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
+
+ requestObject.abort = function(){
+ aborted = true;
+ if(oldAbortFunction){
+ oldAbortFunction.call(requestObject);
+ }
+ };
+
+ var scope = requestObject.scope || dojo.global;
+ if(!requestObject.store){
+ requestObject.store = self;
+ }
+ if(requestObject.onBegin){
+ requestObject.onBegin.call(scope, items.length, requestObject);
+ }
+ if(requestObject.sort){
+ items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
+ }
+
+ if(requestObject.onItem){
+ for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
+ var item = items[i];
+ if(!aborted){
+ requestObject.onItem.call(scope, item, requestObject);
+ }
+ }
+ }
+ if(requestObject.onComplete && !aborted){
+ if(!requestObject.onItem){
+ items = items.slice(startIndex, endIndex);
+ if(requestObject.byId){
+ items = items[0];
+ }
+ }
+ requestObject.onComplete.call(scope, items, requestObject);
+ }else{
+ items = items.slice(startIndex, endIndex);
+ if(requestObject.byId){
+ items = items[0];
+ }
+ }
+ return items;
+ };
+
+ if(!this.url && !this.data && !this.xmlStr){
+ errorHandler(new Error("No URL or data specified."));
+ return false;
+ }
+ var localRequest = request || "*"; // use request for _getItems()
+
+ if(this.mode == dojox.data.SYNC_MODE){
+ // sync mode. items returned directly
+ var res = this._loadCDF();
+ if(res instanceof Error){
+ if(request.onError){
+ request.onError.call(request.scope || dojo.global, res, request);
+ }else{
+ console.error("CdfStore Error:", res);
+ }
+ return res;
+ }
+ this.cdfDoc = res;
+
+ var items = this._getItems(this.cdfDoc, localRequest);
+ if(items && items.length > 0){
+ items = fetchHandler(items, request);
+ }else{
+ items = fetchHandler([], request);
+ }
+ return items;
+
+ }else{
+
+ // async mode. Return a Deferred.
+ var dfd = this._loadCDF();
+ dfd.addCallbacks(dojo.hitch(this, function(cdfDoc){
+ var items = this._getItems(this.cdfDoc, localRequest);
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ }),
+ dojo.hitch(this, function(err){
+ errorHandler(err, request);
+ }));
+
+ return dfd; // Object
+ }
+ },
+
+
+ _loadCDF: function(){
+ // summary:
+ // Internal method.
+ // If a cdfDoc exists, return it. Otherwise, get one from JSX3,
+ // load the data or url, and return the doc or a deferred.
+ var dfd = new dojo.Deferred();
+ if(this.cdfDoc){
+ if(this.mode == dojox.data.SYNC_MODE){
+ return this.cdfDoc; // jsx3.xml.CDF
+ }else{
+ setTimeout(dojo.hitch(this, function(){
+ dfd.callback(this.cdfDoc);
+ }), 0);
+ return dfd; // dojo.Deferred
+ }
+ }
+
+ this.cdfDoc = jsx3.xml.CDF.Document.newDocument();
+ this.cdfDoc.subscribe("response", this, function(evt){
+ dfd.callback(this.cdfDoc);
+ });
+ this.cdfDoc.subscribe("error", this, function(err){
+ dfd.errback(err);
+ });
+
+ this.cdfDoc.setAsync(!this.mode);
+ if(this.url){
+ this.cdfDoc.load(this.url);
+ }else if(this.xmlStr){
+ this.cdfDoc.loadXML(this.xmlStr);
+ if(this.cdfDoc.getError().code){
+ return new Error(this.cdfDoc.getError().description); // Error
+ }
+ }
+
+ if(this.mode == dojox.data.SYNC_MODE){
+ return this.cdfDoc; // jsx3.xml.CDF
+ }else{
+ return dfd; // dojo.Deferred
+ }
+ },
+
+ _getItems: function(/* jsx3.xml.Entity */cdfDoc, /* Object */request){
+ // summary:
+ // Internal method.
+ // Requests the items from jsx3.xml.Entity with an xpath query.
+ //
+ var itr = cdfDoc.selectNodes(request.query, false, 1);
+ var items = [];
+ while(itr.hasNext()){
+ items.push(itr.next());
+ }
+ return items;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* object? */ keywordArgs, /* object? || String? */parentInfo){
+ // summary:
+ // Creates a jsx3.xml.Entity item and inserts it either inside the
+ // parent or appends it to the root
+ //
+ keywordArgs = (keywordArgs || {});
+ if(keywordArgs.tagName){
+ // record tagName is automatic and this would add it
+ // as a property
+ if(keywordArgs.tagName!="record"){
+ // TODO: How about some sort of group?
+ console.warn("Only record inserts are supported at this time");
+ }
+ delete keywordArgs.tagName;
+ }
+ keywordArgs.jsxid = keywordArgs.jsxid || this.cdfDoc.getKey();
+ if(this.isItem(parentInfo)){
+ parentInfo = this.getIdentity(parentInfo);
+ }
+ var item = this.cdfDoc.insertRecord(keywordArgs, parentInfo);
+
+ this._makeDirty(item);
+
+ return item; // jsx3.xml.Entity
+ },
+
+ deleteItem: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Delete an jsx3.xml.Entity (wrapper to a XML element).
+ //
+ this.cdfDoc.deleteRecord(this.getIdentity(item));
+ this._makeDirty(item);
+ return true; //boolean
+ },
+
+ setValue: function(/* jsx3.xml.Entity */ item, /* String */ property, /* almost anything */ value){
+ // summary:
+ // Set an property value
+ //
+ this._makeDirty(item);
+ item.setAttribute(property, value);
+ return true; // Boolean
+ },
+
+ setValues: function(/* jsx3.xml.Entity */ item, /* String */ property, /*array*/ values){
+ // summary:
+ // Set property values
+ // TODO: Needs to be fully implemented.
+ //
+ this._makeDirty(item);
+ console.warn("cdfStore.setValues only partially implemented.");
+ return item.setAttribute(property, values);
+
+ },
+
+ unsetAttribute: function(/* jsx3.xml.Entity */ item, /* String */ property){
+ // summary:
+ // Remove an property
+ //
+ this._makeDirty(item);
+ item.removeAttribute(property);
+ return true; // Boolean
+ },
+
+ revert: function(){
+ // summary:
+ // Invalidate changes (new and/or modified elements)
+ // Resets data by simply deleting the reference to the cdfDoc.
+ // Subsequent fetches will load the new data.
+ // Note:
+ // Any items outside the store will no longer be valid and may cause errors.
+ //
+ delete this.cdfDoc;
+ this._modifiedItems = {};
+ return true; //boolean
+ },
+
+ isDirty: function(/* jsx3.xml.Entity ? */ item){
+ // summary:
+ // Check whether an item is new, modified or deleted.
+ // If no item is passed, checks if anything in the store has changed.
+ //
+ if(item){
+ return !!this._modifiedItems[this.getIdentity(item)]; // Boolean
+ }else{
+ var _dirty = false;
+ for(var nm in this._modifiedItems){ _dirty = true; break; }
+ return _dirty; // Boolean
+ }
+ },
+
+
+
+/* internal API */
+
+ _makeDirty: function(item){
+ // summary:
+ // Internal method.
+ // Marks items as modified, deleted or new.
+ var id = this.getIdentity(item);
+ this._modifiedItems[id] = item;
+ },
+
+
+ _makeXmlString: function(obj){
+ // summary:
+ // Internal method.
+ // Converts an object into an XML string.
+ //
+ var parseObj = function(obj, name){
+ var xmlStr = "";
+ var nm;
+ if(dojo.isArray(obj)){
+ for(var i=0;i<obj.length;i++){
+ xmlStr += parseObj(obj[i], name);
+ }
+ }else if(dojo.isObject(obj)){
+ xmlStr += '<'+name+' ';
+ for(nm in obj){
+ if(!dojo.isObject(obj[nm])){
+ xmlStr += nm+'="'+obj[nm]+'" ';
+ }
+ }
+ xmlStr +='>';
+ for(nm in obj){
+ if(dojo.isObject(obj[nm])){
+ xmlStr += parseObj(obj[nm], nm);
+ }
+ }
+ xmlStr += '</'+name+'>';
+ }
+ return xmlStr;
+ };
+ return parseObj(obj, "data");
+ },
+
+ /*************************************
+ * Dojo.data Identity implementation *
+ *************************************/
+ getIdentity: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Returns the identifier for an item.
+ //
+ return this.getValue(item, this.identity); // String
+ },
+
+ getIdentityAttributes: function(/* jsx3.xml.Entity */ item){
+ // summary:
+ // Returns the property used for the identity.
+ //
+ return [this.identity]; // Array
+ },
+
+
+ fetchItemByIdentity: function(/* Object || String */ args){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity(keywordArgs)
+ //
+ // Note:
+ // This method can be synchronous if mode is set.
+ // Also, there is a more finger friendly alias of this method, byId();
+ if(dojo.isString(args)){
+ var id = args;
+ args = {query:"//record[@jsxid='"+id+"']", mode: dojox.data.SYNC_MODE};
+ }else{
+ if(args){
+ args.query = "//record[@jsxid='"+args.identity+"']";
+ }
+ if(!args.mode){args.mode = this.mode;}
+ }
+ args.byId = true;
+ return this.fetch(args); // dojo.Deferred || Array
+ },
+ byId: function(/* Object || String */ args){
+ // stub. See fetchItemByIdentity
+ }
+
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/ClientFilter.js b/js/dojo-1.6/dojox/data/ClientFilter.js
new file mode 100644
index 0000000..7c3853a
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ClientFilter.js
@@ -0,0 +1,306 @@
+/*
+ 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["dojox.data.ClientFilter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ClientFilter"] = true;
+dojo.provide("dojox.data.ClientFilter");
+dojo.require("dojo.data.util.filter");
+
+
+
+// This is an abstract data store module for adding updateable result set functionality to an existing data store class
+(function(){
+ var cf;
+ var addUpdate = function(store,create,remove){
+ // create a handler that adds to the list of notifications
+ return function(item){
+ store._updates.push({
+ create:create && item,
+ remove:remove && item
+ });
+ cf.onUpdate();
+ }
+ };
+ cf = dojo.declare("dojox.data.ClientFilter",
+ null,
+ {
+ cacheByDefault: false,
+ constructor: function(){
+ // summary:
+ // This is an abstract class that data stores can extend to add updateable result set functionality
+ // as well as client side querying capabilities. This enables
+ // widgets to be aware of how active results change in response to the modifications/notifications.
+ //
+ // description:
+ // To a update a result set after a notification (onNew, onSet, and onDelete),
+ // widgets can call the updateResultSet method. Widgets can use the updated
+ // result sets to determine how to react to notifications, and how to update their displayed results
+ // based on changes.
+ //
+ // This module will use the best available information to update result sets, using query attribute
+ // objects to determine if items are in a result set, and using the sort arrays to maintain sort
+ // information. However, queries can be opaque strings, and this module can not update
+ // results by itself in this case. In this situations, data stores can provide a isUpdateable(request) function
+ // and matchesQuery(item,request) function. If a data store can handle a query, it can return true from
+ // isUpdateable and if an item matches a query, it can return true from matchesQuery. Here is
+ // definition of isUpdateable and matchesQuery
+ // isUpdateable(request) - request is the keywords arguments as is passed to the fetch function.
+ // matchesQuery(item,request) - item is the item to test, and request is the value arguments object
+ // for the fetch function.
+ //
+ // You can define a property on this object instance "cacheByDefault" to a value of true that will
+ // cause all queries to be cached by default unless the cache queryOption is explicitly set to false.
+ // This can be defined in the constructor options for ServiceStore/JsonRestStore and subtypes.
+ //
+ // example:
+ // to make a updated-result-set data store from an existing data store:
+ // | dojo.declare("dojox.data.MyLiveDataStore",
+ // | dojox.data.MyDataStore,dojox.data.ClientFilter], // subclass LiveResultSets if available
+ // | {}
+ // | );
+ this.onSet = addUpdate(this,true,true);
+ this.onNew = addUpdate(this,true,false);
+ this.onDelete = addUpdate(this,false,true);
+ this._updates= [];
+ this._fetchCache = [];
+ },
+ clearCache: function(){
+ // summary:
+ // Clears the cache of client side queries
+ this._fetchCache = [];
+ },
+ updateResultSet: function(/*Array*/ resultSet, /*Object*/ request){
+ // summary:
+ // Attempts to update the given result set based on previous notifications
+ // resultSet:
+ // The result set array that should be updated
+ // request:
+ // This object follows the same meaning as the keywordArgs passed to a dojo.data.api.Read.fetch.
+ // description:
+ // This will attempt to update the provide result based on previous notification, adding new items
+ // from onNew calls, removing deleted items, and updating modified items, and properly removing
+ // and adding items as required by the query and sort parameters. This function will return:
+ // 0: Indicates it could not successfully update the result set
+ // 1: Indicates it could successfully handle all the notifications, but no changes were made to the result set
+ // 2: Indicates it successfully handled all the notifications and result set has been updated.
+ if(this.isUpdateable(request)){
+ // we try to avoid rerunning notification updates more than once on the same object for performance
+ for(var i = request._version || 0; i < this._updates.length;i++){
+ // for each notification,we will update the result set
+ var create = this._updates[i].create;
+ var remove = this._updates[i].remove;
+ if(remove){
+ for(var j = 0; j < resultSet.length;j++){
+ if(this.getIdentity(resultSet[j]) == this.getIdentity(remove)){
+ resultSet.splice(j--,1);
+ var updated = true;
+ }
+ }
+ }
+ if(create && this.matchesQuery(create,request) && // if there is a new/replacement item and it matches the query
+ dojo.indexOf(resultSet,create) == -1){ // and it doesn't already exist in query
+ resultSet.push(create); // should this go at the beginning by default instead?
+ updated = true;
+ }
+ }
+ if(request.sort && updated){
+ // do the sort if needed
+ resultSet.sort(this.makeComparator(request.sort.concat()));
+ }
+ resultSet._fullLength = resultSet.length;
+ if(request.count && updated && request.count !== Infinity){
+ // do we really need to do this?
+ // make sure we still find within the defined paging set
+ resultSet.splice(request.count, resultSet.length);
+ }
+ request._version = this._updates.length;
+ return updated ? 2 : 1;
+ }
+ return 0;
+ },
+ querySuperSet: function(argsSuper, argsSub){
+ // summary:
+ // Determines whether the provided arguments are super/sub sets of each other
+ // argsSuper:
+ // Dojo Data Fetch arguments
+ // argsSub:
+ // Dojo Data Fetch arguments
+ if(argsSuper.query == argsSub.query){
+ return {};
+ }
+ if(!(argsSub.query instanceof Object && // sub query must be an object
+ // super query must be non-existent or an object
+ (!argsSuper.query || typeof argsSuper.query == 'object'))){
+ return false;
+ }
+ var clientQuery = dojo.mixin({},argsSub.query);
+ for(var i in argsSuper.query){
+ if(clientQuery[i] == argsSuper.query[i]){
+ delete clientQuery[i];
+ }else if(!(typeof argsSuper.query[i] == 'string' &&
+ // if it is a pattern, we can test to see if it is a sub-pattern
+ // FIXME: This is not technically correct, but it will work for the majority of cases
+ dojo.data.util.filter.patternToRegExp(argsSuper.query[i]).test(clientQuery[i]))){
+ return false;
+ }
+ }
+ return clientQuery;
+ },
+ // This is the point in the version notification history at which it is known that the server is in sync, this should
+ // be updated to this._updates.length on commit operations.
+ serverVersion: 0,
+
+ cachingFetch: function(args){
+ var self = this;
+ for(var i = 0; i < this._fetchCache.length;i++){
+ var cachedArgs = this._fetchCache[i];
+ var clientQuery = this.querySuperSet(cachedArgs,args);
+ if(clientQuery !== false){
+ var defResult = cachedArgs._loading;
+ if(!defResult){
+ defResult = new dojo.Deferred();
+ defResult.callback(cachedArgs.cacheResults);
+ }
+ defResult.addCallback(function(results){
+ results = self.clientSideFetch(dojo.mixin(dojo.mixin({}, args),{query:clientQuery}), results);
+ defResult.fullLength = results._fullLength;
+ return results;
+ });
+ args._version = cachedArgs._version;
+ break;
+ }
+ }
+ if(!defResult){
+ var serverArgs = dojo.mixin({}, args);
+ var putInCache = (args.queryOptions || 0).cache;
+ var fetchCache = this._fetchCache;
+ if(putInCache === undefined ? this.cacheByDefault : putInCache){
+ // we are caching this request, so we want to get all the data, and page on the client side
+ if(args.start || args.count){
+ delete serverArgs.start;
+ delete serverArgs.count;
+ args.clientQuery = dojo.mixin(args.clientQuery || {}, {
+ start: args.start,
+ count: args.count
+ });
+ }
+ args = serverArgs;
+ fetchCache.push(args);
+ }
+ defResult= args._loading = this._doQuery(args);
+
+ defResult.addErrback(function(){
+ fetchCache.splice(dojo.indexOf(fetchCache, args), 1);
+ });
+ }
+ var version = this.serverVersion;
+
+ defResult.addCallback(function(results){
+ delete args._loading;
+ // update the result set in case anything changed while we were waiting for the fetch
+ if(results){
+ args._version = typeof args._version == "number" ? args._version : version;
+ self.updateResultSet(results,args);
+ args.cacheResults = results;
+ if(!args.count || results.length < args.count){
+ defResult.fullLength = ((args.start)?args.start:0) + results.length;
+ }
+ }
+ return results;
+ });
+ return defResult;
+ },
+ isUpdateable: function(/*Object*/ request){
+ // summary:
+ // Returns whether the provide fetch arguments can be used to update an existing list
+ // request:
+ // See dojo.data.api.Read.fetch request
+
+ return typeof request.query == "object";
+ },
+ clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
+ // summary:
+ // Performs a query on the client side and returns the results as an array
+ //
+ // request:
+ // See dojo.data.api.Read.fetch request
+ //
+ // baseResults:
+ // This provides the result set to start with for client side querying
+ if(request.queryOptions && request.queryOptions.results){
+ baseResults = request.queryOptions.results;
+ }
+ if(request.query){
+ // filter by the query
+ var results = [];
+ for(var i = 0; i < baseResults.length; i++){
+ var value = baseResults[i];
+ if(value && this.matchesQuery(value,request)){
+ results.push(baseResults[i]);
+ }
+ }
+ }else{
+ results = request.sort ? baseResults.concat() : baseResults; // we don't want to mutate the baseResults if we are doing a sort
+ }
+ if(request.sort){
+ // do the sort if needed
+ results.sort(this.makeComparator(request.sort.concat()));
+ }
+ return this.clientSidePaging(request, results);
+ },
+ clientSidePaging: function(/*Object*/ request,/*Array*/ baseResults){
+ var start = request.start || 0;
+ var finalResults = (start || request.count) ? baseResults.slice(start,start + (request.count || baseResults.length)) : baseResults;
+ finalResults._fullLength = baseResults.length;
+ return finalResults;
+ },
+ matchesQuery: function(item,request){
+ var query = request.query;
+ var ignoreCase = request.queryOptions && request.queryOptions.ignoreCase;
+ for(var i in query){
+ // if anything doesn't match, than this should be in the query
+ var match = query[i];
+ var value = this.getValue(item,i);
+ if((typeof match == 'string' && (match.match(/[\*\.]/) || ignoreCase)) ?
+ !dojo.data.util.filter.patternToRegExp(match, ignoreCase).test(value) :
+ value != match){
+ return false;
+ }
+ }
+ return true;
+ },
+ makeComparator: function(sort){
+ // summary:
+ // returns a comparator function for the given sort order array
+ // sort:
+ // See dojox.data.api.Read.fetch
+ var current = sort.shift();
+ if(!current){
+ // sort order for ties and no sort orders
+ return function(){
+ return 0;// keep the order unchanged
+ };
+ }
+ var attribute = current.attribute;
+ var descending = !!current.descending;
+ var next = this.makeComparator(sort);
+ var store = this;
+ return function(a,b){
+ var av = store.getValue(a,attribute);
+ var bv = store.getValue(b,attribute);
+ if(av != bv){
+ return av < bv == descending ? 1 : -1;
+ }
+ return next(a,b);
+ };
+ }
+ }
+ );
+ cf.onUpdate = function(){};
+})();
+
+}
diff --git a/js/dojo-1.6/dojox/data/ClientFilter.xd.js b/js/dojo-1.6/dojox/data/ClientFilter.xd.js
new file mode 100644
index 0000000..48d4bfd
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ClientFilter.xd.js
@@ -0,0 +1,311 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.ClientFilter"],
+["require", "dojo.data.util.filter"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.ClientFilter"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ClientFilter"] = true;
+dojo.provide("dojox.data.ClientFilter");
+dojo.require("dojo.data.util.filter");
+
+
+
+// This is an abstract data store module for adding updateable result set functionality to an existing data store class
+(function(){
+ var cf;
+ var addUpdate = function(store,create,remove){
+ // create a handler that adds to the list of notifications
+ return function(item){
+ store._updates.push({
+ create:create && item,
+ remove:remove && item
+ });
+ cf.onUpdate();
+ }
+ };
+ cf = dojo.declare("dojox.data.ClientFilter",
+ null,
+ {
+ cacheByDefault: false,
+ constructor: function(){
+ // summary:
+ // This is an abstract class that data stores can extend to add updateable result set functionality
+ // as well as client side querying capabilities. This enables
+ // widgets to be aware of how active results change in response to the modifications/notifications.
+ //
+ // description:
+ // To a update a result set after a notification (onNew, onSet, and onDelete),
+ // widgets can call the updateResultSet method. Widgets can use the updated
+ // result sets to determine how to react to notifications, and how to update their displayed results
+ // based on changes.
+ //
+ // This module will use the best available information to update result sets, using query attribute
+ // objects to determine if items are in a result set, and using the sort arrays to maintain sort
+ // information. However, queries can be opaque strings, and this module can not update
+ // results by itself in this case. In this situations, data stores can provide a isUpdateable(request) function
+ // and matchesQuery(item,request) function. If a data store can handle a query, it can return true from
+ // isUpdateable and if an item matches a query, it can return true from matchesQuery. Here is
+ // definition of isUpdateable and matchesQuery
+ // isUpdateable(request) - request is the keywords arguments as is passed to the fetch function.
+ // matchesQuery(item,request) - item is the item to test, and request is the value arguments object
+ // for the fetch function.
+ //
+ // You can define a property on this object instance "cacheByDefault" to a value of true that will
+ // cause all queries to be cached by default unless the cache queryOption is explicitly set to false.
+ // This can be defined in the constructor options for ServiceStore/JsonRestStore and subtypes.
+ //
+ // example:
+ // to make a updated-result-set data store from an existing data store:
+ // | dojo.declare("dojox.data.MyLiveDataStore",
+ // | dojox.data.MyDataStore,dojox.data.ClientFilter], // subclass LiveResultSets if available
+ // | {}
+ // | );
+ this.onSet = addUpdate(this,true,true);
+ this.onNew = addUpdate(this,true,false);
+ this.onDelete = addUpdate(this,false,true);
+ this._updates= [];
+ this._fetchCache = [];
+ },
+ clearCache: function(){
+ // summary:
+ // Clears the cache of client side queries
+ this._fetchCache = [];
+ },
+ updateResultSet: function(/*Array*/ resultSet, /*Object*/ request){
+ // summary:
+ // Attempts to update the given result set based on previous notifications
+ // resultSet:
+ // The result set array that should be updated
+ // request:
+ // This object follows the same meaning as the keywordArgs passed to a dojo.data.api.Read.fetch.
+ // description:
+ // This will attempt to update the provide result based on previous notification, adding new items
+ // from onNew calls, removing deleted items, and updating modified items, and properly removing
+ // and adding items as required by the query and sort parameters. This function will return:
+ // 0: Indicates it could not successfully update the result set
+ // 1: Indicates it could successfully handle all the notifications, but no changes were made to the result set
+ // 2: Indicates it successfully handled all the notifications and result set has been updated.
+ if(this.isUpdateable(request)){
+ // we try to avoid rerunning notification updates more than once on the same object for performance
+ for(var i = request._version || 0; i < this._updates.length;i++){
+ // for each notification,we will update the result set
+ var create = this._updates[i].create;
+ var remove = this._updates[i].remove;
+ if(remove){
+ for(var j = 0; j < resultSet.length;j++){
+ if(this.getIdentity(resultSet[j]) == this.getIdentity(remove)){
+ resultSet.splice(j--,1);
+ var updated = true;
+ }
+ }
+ }
+ if(create && this.matchesQuery(create,request) && // if there is a new/replacement item and it matches the query
+ dojo.indexOf(resultSet,create) == -1){ // and it doesn't already exist in query
+ resultSet.push(create); // should this go at the beginning by default instead?
+ updated = true;
+ }
+ }
+ if(request.sort && updated){
+ // do the sort if needed
+ resultSet.sort(this.makeComparator(request.sort.concat()));
+ }
+ resultSet._fullLength = resultSet.length;
+ if(request.count && updated && request.count !== Infinity){
+ // do we really need to do this?
+ // make sure we still find within the defined paging set
+ resultSet.splice(request.count, resultSet.length);
+ }
+ request._version = this._updates.length;
+ return updated ? 2 : 1;
+ }
+ return 0;
+ },
+ querySuperSet: function(argsSuper, argsSub){
+ // summary:
+ // Determines whether the provided arguments are super/sub sets of each other
+ // argsSuper:
+ // Dojo Data Fetch arguments
+ // argsSub:
+ // Dojo Data Fetch arguments
+ if(argsSuper.query == argsSub.query){
+ return {};
+ }
+ if(!(argsSub.query instanceof Object && // sub query must be an object
+ // super query must be non-existent or an object
+ (!argsSuper.query || typeof argsSuper.query == 'object'))){
+ return false;
+ }
+ var clientQuery = dojo.mixin({},argsSub.query);
+ for(var i in argsSuper.query){
+ if(clientQuery[i] == argsSuper.query[i]){
+ delete clientQuery[i];
+ }else if(!(typeof argsSuper.query[i] == 'string' &&
+ // if it is a pattern, we can test to see if it is a sub-pattern
+ // FIXME: This is not technically correct, but it will work for the majority of cases
+ dojo.data.util.filter.patternToRegExp(argsSuper.query[i]).test(clientQuery[i]))){
+ return false;
+ }
+ }
+ return clientQuery;
+ },
+ // This is the point in the version notification history at which it is known that the server is in sync, this should
+ // be updated to this._updates.length on commit operations.
+ serverVersion: 0,
+
+ cachingFetch: function(args){
+ var self = this;
+ for(var i = 0; i < this._fetchCache.length;i++){
+ var cachedArgs = this._fetchCache[i];
+ var clientQuery = this.querySuperSet(cachedArgs,args);
+ if(clientQuery !== false){
+ var defResult = cachedArgs._loading;
+ if(!defResult){
+ defResult = new dojo.Deferred();
+ defResult.callback(cachedArgs.cacheResults);
+ }
+ defResult.addCallback(function(results){
+ results = self.clientSideFetch(dojo.mixin(dojo.mixin({}, args),{query:clientQuery}), results);
+ defResult.fullLength = results._fullLength;
+ return results;
+ });
+ args._version = cachedArgs._version;
+ break;
+ }
+ }
+ if(!defResult){
+ var serverArgs = dojo.mixin({}, args);
+ var putInCache = (args.queryOptions || 0).cache;
+ var fetchCache = this._fetchCache;
+ if(putInCache === undefined ? this.cacheByDefault : putInCache){
+ // we are caching this request, so we want to get all the data, and page on the client side
+ if(args.start || args.count){
+ delete serverArgs.start;
+ delete serverArgs.count;
+ args.clientQuery = dojo.mixin(args.clientQuery || {}, {
+ start: args.start,
+ count: args.count
+ });
+ }
+ args = serverArgs;
+ fetchCache.push(args);
+ }
+ defResult= args._loading = this._doQuery(args);
+
+ defResult.addErrback(function(){
+ fetchCache.splice(dojo.indexOf(fetchCache, args), 1);
+ });
+ }
+ var version = this.serverVersion;
+
+ defResult.addCallback(function(results){
+ delete args._loading;
+ // update the result set in case anything changed while we were waiting for the fetch
+ if(results){
+ args._version = typeof args._version == "number" ? args._version : version;
+ self.updateResultSet(results,args);
+ args.cacheResults = results;
+ if(!args.count || results.length < args.count){
+ defResult.fullLength = ((args.start)?args.start:0) + results.length;
+ }
+ }
+ return results;
+ });
+ return defResult;
+ },
+ isUpdateable: function(/*Object*/ request){
+ // summary:
+ // Returns whether the provide fetch arguments can be used to update an existing list
+ // request:
+ // See dojo.data.api.Read.fetch request
+
+ return typeof request.query == "object";
+ },
+ clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
+ // summary:
+ // Performs a query on the client side and returns the results as an array
+ //
+ // request:
+ // See dojo.data.api.Read.fetch request
+ //
+ // baseResults:
+ // This provides the result set to start with for client side querying
+ if(request.queryOptions && request.queryOptions.results){
+ baseResults = request.queryOptions.results;
+ }
+ if(request.query){
+ // filter by the query
+ var results = [];
+ for(var i = 0; i < baseResults.length; i++){
+ var value = baseResults[i];
+ if(value && this.matchesQuery(value,request)){
+ results.push(baseResults[i]);
+ }
+ }
+ }else{
+ results = request.sort ? baseResults.concat() : baseResults; // we don't want to mutate the baseResults if we are doing a sort
+ }
+ if(request.sort){
+ // do the sort if needed
+ results.sort(this.makeComparator(request.sort.concat()));
+ }
+ return this.clientSidePaging(request, results);
+ },
+ clientSidePaging: function(/*Object*/ request,/*Array*/ baseResults){
+ var start = request.start || 0;
+ var finalResults = (start || request.count) ? baseResults.slice(start,start + (request.count || baseResults.length)) : baseResults;
+ finalResults._fullLength = baseResults.length;
+ return finalResults;
+ },
+ matchesQuery: function(item,request){
+ var query = request.query;
+ var ignoreCase = request.queryOptions && request.queryOptions.ignoreCase;
+ for(var i in query){
+ // if anything doesn't match, than this should be in the query
+ var match = query[i];
+ var value = this.getValue(item,i);
+ if((typeof match == 'string' && (match.match(/[\*\.]/) || ignoreCase)) ?
+ !dojo.data.util.filter.patternToRegExp(match, ignoreCase).test(value) :
+ value != match){
+ return false;
+ }
+ }
+ return true;
+ },
+ makeComparator: function(sort){
+ // summary:
+ // returns a comparator function for the given sort order array
+ // sort:
+ // See dojox.data.api.Read.fetch
+ var current = sort.shift();
+ if(!current){
+ // sort order for ties and no sort orders
+ return function(){
+ return 0;// keep the order unchanged
+ };
+ }
+ var attribute = current.attribute;
+ var descending = !!current.descending;
+ var next = this.makeComparator(sort);
+ var store = this;
+ return function(a,b){
+ var av = store.getValue(a,attribute);
+ var bv = store.getValue(b,attribute);
+ if(av != bv){
+ return av < bv == descending ? 1 : -1;
+ }
+ return next(a,b);
+ };
+ }
+ }
+ );
+ cf.onUpdate = function(){};
+})();
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/CouchDBRestStore.js b/js/dojo-1.6/dojox/data/CouchDBRestStore.js
new file mode 100644
index 0000000..a3d09a3
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CouchDBRestStore.js
@@ -0,0 +1,92 @@
+/*
+ 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["dojox.data.CouchDBRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CouchDBRestStore"] = true;
+dojo.provide("dojox.data.CouchDBRestStore");
+dojo.require("dojox.data.JsonRestStore");
+
+
+
+// A CouchDBRestStore is an extension of JsonRestStore to handle CouchDB's idiosyncrasies, special features,
+// and deviations from standard HTTP Rest.
+// NOTE: CouchDB is not designed to be run on a public facing network. There is no access control
+// on database documents, and you should NOT rely on client side control to implement security.
+
+
+dojo.declare("dojox.data.CouchDBRestStore",
+ dojox.data.JsonRestStore,
+ {
+ save: function(kwArgs){
+ var actions = this.inherited(arguments); // do the default save and then update for version numbers
+ var prefix = this.service.servicePath;
+ for(var i = 0; i < actions.length; i++){
+ // need to update the item's version number after it has been committed
+ (function(item,dfd){
+ dfd.addCallback(function(result){
+ if(result){
+ item.__id = prefix + result.id; // update the object with the results of the post
+ item._rev = result.rev;
+ }
+ return result;
+ });
+ })(actions[i].content,actions[i].deferred);
+ }
+ },
+ fetch: function(args){
+ // summary:
+ // This only differs from JsonRestStore in that it, will put the query string the query part of the URL and it handles start and count
+ args.query = args.query || '_all_docs?';
+ if(args.start){
+ args.query = (args.query ? (args.query + '&') : '') + 'startkey=' + args.start;
+ delete args.start;
+ }
+ if(args.count){
+ args.query = (args.query ? (args.query + '&') : '') + 'limit=' + args.count;
+ delete args.count;
+ }
+ return this.inherited(arguments);
+ },
+ _processResults: function(results){
+ var rows = results.rows;
+ if(rows){
+ var prefix = this.service.servicePath;
+ var self = this;
+ for(var i = 0; i < rows.length;i++){
+ var realItem = rows[i].value;
+ realItem.__id= prefix + rows[i].id;
+ realItem._id= rows[i].id;
+ realItem._loadObject= dojox.rpc.JsonRest._loader;
+ rows[i] = realItem;
+ }
+ return {totalCount:results.total_rows, items:results.rows};
+ }else{
+ return {items:results};
+ }
+
+ }
+ }
+);
+
+// create a set of stores
+dojox.data.CouchDBRestStore.getStores = function(couchServerUrl){
+ var dfd = dojo.xhrGet({
+ url: couchServerUrl+"_all_dbs",
+ handleAs: "json",
+ sync: true
+ });
+ var stores = {};
+ dfd.addBoth(function(dbs){
+ for(var i = 0; i < dbs.length; i++){
+ stores[dbs[i]] = new dojox.data.CouchDBRestStore({target:couchServerUrl + dbs[i],idAttribute:"_id"});
+ }
+ return stores;
+ });
+ return stores;
+};
+
+}
diff --git a/js/dojo-1.6/dojox/data/CouchDBRestStore.xd.js b/js/dojo-1.6/dojox/data/CouchDBRestStore.xd.js
new file mode 100644
index 0000000..f7db048
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CouchDBRestStore.xd.js
@@ -0,0 +1,97 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.CouchDBRestStore"],
+["require", "dojox.data.JsonRestStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.CouchDBRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CouchDBRestStore"] = true;
+dojo.provide("dojox.data.CouchDBRestStore");
+dojo.require("dojox.data.JsonRestStore");
+
+
+
+// A CouchDBRestStore is an extension of JsonRestStore to handle CouchDB's idiosyncrasies, special features,
+// and deviations from standard HTTP Rest.
+// NOTE: CouchDB is not designed to be run on a public facing network. There is no access control
+// on database documents, and you should NOT rely on client side control to implement security.
+
+
+dojo.declare("dojox.data.CouchDBRestStore",
+ dojox.data.JsonRestStore,
+ {
+ save: function(kwArgs){
+ var actions = this.inherited(arguments); // do the default save and then update for version numbers
+ var prefix = this.service.servicePath;
+ for(var i = 0; i < actions.length; i++){
+ // need to update the item's version number after it has been committed
+ (function(item,dfd){
+ dfd.addCallback(function(result){
+ if(result){
+ item.__id = prefix + result.id; // update the object with the results of the post
+ item._rev = result.rev;
+ }
+ return result;
+ });
+ })(actions[i].content,actions[i].deferred);
+ }
+ },
+ fetch: function(args){
+ // summary:
+ // This only differs from JsonRestStore in that it, will put the query string the query part of the URL and it handles start and count
+ args.query = args.query || '_all_docs?';
+ if(args.start){
+ args.query = (args.query ? (args.query + '&') : '') + 'startkey=' + args.start;
+ delete args.start;
+ }
+ if(args.count){
+ args.query = (args.query ? (args.query + '&') : '') + 'limit=' + args.count;
+ delete args.count;
+ }
+ return this.inherited(arguments);
+ },
+ _processResults: function(results){
+ var rows = results.rows;
+ if(rows){
+ var prefix = this.service.servicePath;
+ var self = this;
+ for(var i = 0; i < rows.length;i++){
+ var realItem = rows[i].value;
+ realItem.__id= prefix + rows[i].id;
+ realItem._id= rows[i].id;
+ realItem._loadObject= dojox.rpc.JsonRest._loader;
+ rows[i] = realItem;
+ }
+ return {totalCount:results.total_rows, items:results.rows};
+ }else{
+ return {items:results};
+ }
+
+ }
+ }
+);
+
+// create a set of stores
+dojox.data.CouchDBRestStore.getStores = function(couchServerUrl){
+ var dfd = dojo.xhrGet({
+ url: couchServerUrl+"_all_dbs",
+ handleAs: "json",
+ sync: true
+ });
+ var stores = {};
+ dfd.addBoth(function(dbs){
+ for(var i = 0; i < dbs.length; i++){
+ stores[dbs[i]] = new dojox.data.CouchDBRestStore({target:couchServerUrl + dbs[i],idAttribute:"_id"});
+ }
+ return stores;
+ });
+ return stores;
+};
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/CssClassStore.js b/js/dojo-1.6/dojox/data/CssClassStore.js
new file mode 100644
index 0000000..12c8756
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CssClassStore.js
@@ -0,0 +1,170 @@
+/*
+ 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["dojox.data.CssClassStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CssClassStore"] = true;
+dojo.provide("dojox.data.CssClassStore");
+dojo.require("dojox.data.CssRuleStore");
+
+
+
+dojo.declare("dojox.data.CssClassStore", dojox.data.CssRuleStore, {
+ // summary:
+ // Basic store to display CSS information.
+ // description:
+ // The CssClassStore allows users to get information about active Css classes in the page running the CssClassStore.
+ // It can also filter out classes from specific stylesheets. The attributes it exposes on classes are as follows:
+ // class: The classname, including the '.'.
+ // classSans: The classname without the '.'.
+
+ _labelAttribute: 'class', // text representation of the Item [label and identifier may need to stay due to method names]
+ _idAttribute: 'class',
+ _cName: "dojox.data.CssClassStore",
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ "dojo.data.api.Read" : true,
+ "dojo.data.api.Identity" : true
+ };
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ return ['class', 'classSans'];
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = [];
+ if(attribute === "class"){
+ value = [item.className];
+ }else if(attribute === "classSans"){
+ value = [item.className.replace(/\./g,'')];
+ }
+ return value;
+ },
+
+ _handleRule: function(rule, styleSheet, href){
+ // summary:
+ // Handles the creation of an item based on the passed rule. In this store, this implies
+ // parsing out all available class names.
+ var obj = {};
+ var s = rule['selectorText'].split(" ");
+ for(var j=0; j<s.length; j++){
+ var tmp = s[j];
+ var first = tmp.indexOf('.');
+ if(tmp && tmp.length > 0 && first !== -1){
+ var last = tmp.indexOf(',') || tmp.indexOf('[');
+ tmp = tmp.substring(first, ((last !== -1 && last > first)?last:tmp.length));
+ obj[tmp] = true;
+ }
+ }
+ for(var key in obj){
+ if(!this._allItems[key]){
+ var item = {};
+ item.className = key;
+ item[this._storeRef] = this;
+ this._allItems[key] = item;
+ }
+ }
+ },
+
+ _handleReturn: function(){
+ // summary:
+ // Handles the return from a fetching action. Delegates requests to act on the resulting
+ // item set to eitehr the _handleFetchReturn or _handleFetchByIdentityReturn depending on
+ // where the request originated.
+ var _inProgress = [];
+
+ var items = {};
+ for(var i in this._allItems){
+ items[i] = this._allItems[i];
+ }
+ var requestInfo;
+ // One-level deep clone (can't use dojo.clone, since we don't want to clone all those store refs!)
+ while(this._pending.length){
+ requestInfo = this._pending.pop();
+ requestInfo.request._items = items;
+ _inProgress.push(requestInfo);
+ }
+
+ while(_inProgress.length){
+ requestInfo = _inProgress.pop();
+ if(requestInfo.fetch){
+ this._handleFetchReturn(requestInfo.request);
+ }else{
+ this._handleFetchByIdentityReturn(requestInfo.request);
+ }
+ }
+ },
+
+ _handleFetchByIdentityReturn: function(request){
+ // summary:
+ // Handles a fetchByIdentity request by finding the correct item.
+ var items = request._items;
+ // Per https://bugs.webkit.org/show_bug.cgi?id=17935 , Safari 3.x always returns the selectorText
+ // of a rule in full lowercase.
+ var item = items[(dojo.isWebKit?request.identity.toLowerCase():request.identity)];
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(request.onItem){
+ var scope = request.scope || dojo.global;
+ request.onItem.call(scope, item);
+ }
+ },
+
+ /* Identity API */
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ return this.getValue(item, this._idAttribute);
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ this._assertIsItem(item);
+ return [this._idAttribute];
+ },
+
+ fetchItemByIdentity: function(/* request */ request){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ if(this._pending && this._pending.length > 0){
+ this._pending.push({request: request});
+ }else{
+ this._pending = [{request: request}];
+ this._fetch(request);
+ }
+ return request;
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/CssClassStore.xd.js b/js/dojo-1.6/dojox/data/CssClassStore.xd.js
new file mode 100644
index 0000000..fdd9632
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CssClassStore.xd.js
@@ -0,0 +1,175 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.CssClassStore"],
+["require", "dojox.data.CssRuleStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.CssClassStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CssClassStore"] = true;
+dojo.provide("dojox.data.CssClassStore");
+dojo.require("dojox.data.CssRuleStore");
+
+
+
+dojo.declare("dojox.data.CssClassStore", dojox.data.CssRuleStore, {
+ // summary:
+ // Basic store to display CSS information.
+ // description:
+ // The CssClassStore allows users to get information about active Css classes in the page running the CssClassStore.
+ // It can also filter out classes from specific stylesheets. The attributes it exposes on classes are as follows:
+ // class: The classname, including the '.'.
+ // classSans: The classname without the '.'.
+
+ _labelAttribute: 'class', // text representation of the Item [label and identifier may need to stay due to method names]
+ _idAttribute: 'class',
+ _cName: "dojox.data.CssClassStore",
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ "dojo.data.api.Read" : true,
+ "dojo.data.api.Identity" : true
+ };
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ return ['class', 'classSans'];
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = [];
+ if(attribute === "class"){
+ value = [item.className];
+ }else if(attribute === "classSans"){
+ value = [item.className.replace(/\./g,'')];
+ }
+ return value;
+ },
+
+ _handleRule: function(rule, styleSheet, href){
+ // summary:
+ // Handles the creation of an item based on the passed rule. In this store, this implies
+ // parsing out all available class names.
+ var obj = {};
+ var s = rule['selectorText'].split(" ");
+ for(var j=0; j<s.length; j++){
+ var tmp = s[j];
+ var first = tmp.indexOf('.');
+ if(tmp && tmp.length > 0 && first !== -1){
+ var last = tmp.indexOf(',') || tmp.indexOf('[');
+ tmp = tmp.substring(first, ((last !== -1 && last > first)?last:tmp.length));
+ obj[tmp] = true;
+ }
+ }
+ for(var key in obj){
+ if(!this._allItems[key]){
+ var item = {};
+ item.className = key;
+ item[this._storeRef] = this;
+ this._allItems[key] = item;
+ }
+ }
+ },
+
+ _handleReturn: function(){
+ // summary:
+ // Handles the return from a fetching action. Delegates requests to act on the resulting
+ // item set to eitehr the _handleFetchReturn or _handleFetchByIdentityReturn depending on
+ // where the request originated.
+ var _inProgress = [];
+
+ var items = {};
+ for(var i in this._allItems){
+ items[i] = this._allItems[i];
+ }
+ var requestInfo;
+ // One-level deep clone (can't use dojo.clone, since we don't want to clone all those store refs!)
+ while(this._pending.length){
+ requestInfo = this._pending.pop();
+ requestInfo.request._items = items;
+ _inProgress.push(requestInfo);
+ }
+
+ while(_inProgress.length){
+ requestInfo = _inProgress.pop();
+ if(requestInfo.fetch){
+ this._handleFetchReturn(requestInfo.request);
+ }else{
+ this._handleFetchByIdentityReturn(requestInfo.request);
+ }
+ }
+ },
+
+ _handleFetchByIdentityReturn: function(request){
+ // summary:
+ // Handles a fetchByIdentity request by finding the correct item.
+ var items = request._items;
+ // Per https://bugs.webkit.org/show_bug.cgi?id=17935 , Safari 3.x always returns the selectorText
+ // of a rule in full lowercase.
+ var item = items[(dojo.isWebKit?request.identity.toLowerCase():request.identity)];
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(request.onItem){
+ var scope = request.scope || dojo.global;
+ request.onItem.call(scope, item);
+ }
+ },
+
+ /* Identity API */
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ return this.getValue(item, this._idAttribute);
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ this._assertIsItem(item);
+ return [this._idAttribute];
+ },
+
+ fetchItemByIdentity: function(/* request */ request){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ if(this._pending && this._pending.length > 0){
+ this._pending.push({request: request});
+ }else{
+ this._pending = [{request: request}];
+ this._fetch(request);
+ }
+ return request;
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/CssRuleStore.js b/js/dojo-1.6/dojox/data/CssRuleStore.js
new file mode 100644
index 0000000..27eae58
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CssRuleStore.js
@@ -0,0 +1,475 @@
+/*
+ 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["dojox.data.CssRuleStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CssRuleStore"] = true;
+dojo.provide("dojox.data.CssRuleStore");
+dojo.require("dojo.data.util.sorter");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.data.css");
+
+
+
+dojo.declare("dojox.data.CssRuleStore", null, {
+ // summary:
+ // Basic store to display CSS information.
+ // description:
+ // The CssRuleStore allows users to get information about active CSS rules in the page running the CssRuleStore.
+ // It can also filter out rules from specific stylesheets. The attributes it exposes on rules are as follows:
+ // selector: The selector text.
+ // classes: An array of classes present in this selector.
+ // rule: The actual DOM Rule object.
+ // style: The actual DOM CSSStyleDeclaration object.
+ // cssText: The cssText string provided on the rule object.
+ // styleSheet: The originating DOM Stylesheet object.
+ // parentStyleSheet: The parent stylesheet to the sheet this rule originates from.
+ // parentStyleSheetHref: The href of the parent stylesheet.
+ // AND every style attribute denoted as style.*, such as style.textAlign or style.backgroundColor
+
+ _storeRef: '_S',
+ _labelAttribute: 'selector', // text representation of the Item [label and identifier may need to stay due to method names]
+
+ _cache: null,
+
+ _browserMap: null,
+
+ _cName: "dojox.data.CssRuleStore",
+
+ constructor: function(/* Object */ keywordParameters){
+ // Initializes this store
+ if(keywordParameters){
+ dojo.mixin(this, keywordParameters);
+ }
+ this._cache = {};
+ this._allItems = null;
+ this._waiting = [];
+ this.gatherHandle = null;
+ var self = this;
+ // CSS files may not be finished loading by the time the store is constructed. We need to
+ // give them a little time, so setting the stylesheet loading to retry every 250ms.
+ function gatherRules(){
+ try{
+ // Funkiness here is due to css that may still be loading. This throws an DOM Access
+ // error if css isnt completely loaded.
+ self.context = dojox.data.css.determineContext(self.context);
+ if(self.gatherHandle){
+ clearInterval(self.gatherHandle);
+ self.gatherHandle = null;
+ }
+ // Handle any fetches that have been queued while we've been waiting on the CSS files
+ // to finish
+ while(self._waiting.length){
+ var item = self._waiting.pop();
+ dojox.data.css.rules.forEach(item.forFunc, null, self.context);
+ item.finishFunc();
+ }
+ }catch(e){}
+ }
+ this.gatherHandle = setInterval(gatherRules,250);
+ },
+
+ setContext: function(/* Array */ context){
+ // Sets the context in which queries are executed
+ // context: Array - Array of CSS string paths to execute queries within
+ if(context){
+ this.close();
+ this.context = dojox.data.css.determineContext(context);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ "dojo.data.api.Read" : true
+ };
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] == this){
+ return true;
+ }
+ return false;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var attrs = this.getAttributes(item);
+ if(dojo.indexOf(attrs, attribute) != -1){
+ return true;
+ }
+ return false;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attrs = ['selector', 'classes', 'rule', 'style', 'cssText', 'styleSheet', 'parentStyleSheet', 'parentStyleSheetHref'];
+ var style = item.rule.style;
+ if(style){
+ var key;
+ for(key in style){
+ attrs.push("style." + key);
+ }
+ }
+ return attrs;
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ var value = defaultValue;
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = null;
+ if(attribute === "selector"){
+ value = item.rule["selectorText"];
+ if(value && dojo.isString(value)){
+ value = value.split(",");
+ }
+ }else if(attribute === "classes"){
+ value = item.classes;
+ }else if(attribute === "rule"){
+ value = item.rule.rule;
+ }else if(attribute === "style"){
+ value = item.rule.style;
+ }else if(attribute === "cssText"){
+ if(dojo.isIE){
+ if(item.rule.style){
+ value = item.rule.style.cssText;
+ if(value){
+ value = "{ " + value.toLowerCase() + " }";
+ }
+ }
+ }else{
+ value = item.rule.cssText;
+ if(value){
+ value = value.substring(value.indexOf("{"), value.length);
+ }
+ }
+ }else if(attribute === "styleSheet"){
+ value = item.rule.styleSheet;
+ }else if(attribute === "parentStyleSheet"){
+ value = item.rule.parentStyleSheet;
+ }else if(attribute === "parentStyleSheetHref"){
+ if(item.href){
+ value = item.href;
+ }
+ }else if(attribute.indexOf("style.") === 0){
+ var attr = attribute.substring(attribute.indexOf("."), attribute.length);
+ value = item.rule.style[attr];
+ }else{
+ value = [];
+ }
+ if(value !== undefined){
+ if(!dojo.isArray(value)){
+ value = [value];
+ }
+ }
+ return value;
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ this._assertIsItem(item);
+ return this.getValue(item, this._labelAttribute);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._labelAttribute];
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ fetch: function(request){
+ // summary:
+ // See dojo.data.api.Read.fetch()
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+
+ var scope = request.scope || dojo.global;
+ if(this._pending && this._pending.length > 0){
+ this._pending.push({request: request, fetch: true});
+ }else{
+ this._pending = [{request: request, fetch: true}];
+ this._fetch(request);
+ }
+ return request;
+ },
+
+ _fetch: function(request){
+ // summary:
+ // Populates the _allItems object with unique class names
+ var scope = request.scope || dojo.global;
+ if(this._allItems === null){
+ this._allItems = {};
+ try{
+ if(this.gatherHandle){
+ this._waiting.push({'forFunc': dojo.hitch(this, this._handleRule), 'finishFunc': dojo.hitch(this, this._handleReturn)});
+ }else{
+ dojox.data.css.rules.forEach(dojo.hitch(this, this._handleRule), null, this.context);
+ this._handleReturn();
+ }
+ }catch(e){
+ if(request.onError){
+ request.onError.call(scope, e, request);
+ }
+ }
+ }else{
+ this._handleReturn();
+ }
+ },
+
+ _handleRule: function(rule, styleSheet, href){
+ // summary:
+ // Handles the creation of an item based on the passed rule. In this store, this implies
+ // parsing out all available class names.
+ var selector = rule['selectorText'];
+ var s = selector.split(" ");
+ var classes = [];
+ for(var j=0; j<s.length; j++){
+ var tmp = s[j];
+ var first = tmp.indexOf('.');
+ if(tmp && tmp.length > 0 && first !== -1){
+ var last = tmp.indexOf(',') || tmp.indexOf('[');
+ tmp = tmp.substring(first, ((last !== -1 && last > first)?last:tmp.length));
+ classes.push(tmp);
+ }
+ }
+ var item = {};
+ item.rule = rule;
+ item.styleSheet = styleSheet;
+ item.href = href;
+ item.classes = classes;
+ item[this._storeRef] = this;
+ if(!this._allItems[selector]){
+ this._allItems[selector] = [];
+ }
+ this._allItems[selector].push(item);
+ },
+
+ _handleReturn: function(){
+ // summary:
+ // Handles the return from a fetching action. Delegates requests to act on the resulting
+ // item set to eitehr the _handleFetchReturn or _handleFetchByIdentityReturn depending on
+ // where the request originated.
+ var _inProgress = [];
+
+ var items = [];
+ var item = null;
+ for(var i in this._allItems){
+ item = this._allItems[i];
+ for(var j in item){
+ items.push(item[j]);
+ }
+ }
+
+ var requestInfo;
+ // One-level deep clone (can't use dojo.clone, since we don't want to clone all those store refs!)
+ while(this._pending.length){
+ requestInfo = this._pending.pop();
+ requestInfo.request._items = items;
+ _inProgress.push(requestInfo);
+ }
+
+ while(_inProgress.length){
+ requestInfo = _inProgress.pop();
+ this._handleFetchReturn(requestInfo.request);
+ }
+ },
+
+ _handleFetchReturn: function(/*Request */ request){
+ // summary:
+ // Handles a fetchByIdentity request by finding the correct items.
+ var scope = request.scope || dojo.global;
+ var items = [];
+ //Check to see if we've looked this query up before
+ //If so, just reuse it, much faster. Only regen if query changes.
+ var cacheKey = "all";
+ var i;
+ if(request.query){
+ cacheKey = dojo.toJson(request.query);
+ }
+ if(this._cache[cacheKey]){
+ items = this._cache[cacheKey];
+ }else if(request.query){
+ for(i in request._items){
+ var item = request._items[i];
+ // Per https://bugs.webkit.org/show_bug.cgi?id=17935 , Safari 3.x always returns the selectorText
+ // of a rule in full lowercase.
+ var ignoreCase = dojo.isWebKit ? true : (request.queryOptions ? request.queryOptions.ignoreCase : false);
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+ var match = true;
+ for(key in request.query){
+ value = request.query[key];
+ if(!this._containsValue(item, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(item);
+ }
+ }
+ this._cache[cacheKey] = items;
+ }else{
+ for(i in request._items){
+ items.push(request._items[i]);
+ }
+ }
+ var total = items.length;
+
+ //Sort it if we need to.
+ if(request.sort){
+ items.sort(dojo.data.util.sorter.createSortFunction(request.sort, this));
+ }
+ var start = 0;
+ var count = items.length;
+ if(request.start > 0 && request.start < items.length){
+ start = request.start;
+ }
+ if(request.count && request.count){
+ count = request.count;
+ }
+ var endIdx = start + count;
+ if(endIdx > items.length){
+ endIdx = items.length;
+ }
+
+ items = items.slice(start, endIdx);
+
+ if(request.onBegin){
+ request.onBegin.call(scope, total, request);
+ }
+ if(request.onItem){
+ if(dojo.isArray(items)){
+ for(i = 0; i < items.length; i++){
+ request.onItem.call(scope, items[i], request);
+ }
+ if(request.onComplete){
+ request.onComplete.call(scope, null, request);
+ }
+ }
+ }else if(request.onComplete){
+ request.onComplete.call(scope, items, request);
+ }
+ return request;
+ },
+
+ close: function(){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // Clears out the cache and allItems objects, meaning all future fetches will requery
+ // the stylesheets.
+ this._cache = {};
+ this._allItems = null;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this._cName + ": Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error(this._cName + ": Invalid attribute argument.");
+ }
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ return dojo.some(this.getValues(item, attribute), function(possibleValue){
+ if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
+ if(possibleValue.toString().match(regexp)){
+ return true; // Boolean
+ }
+ }else if(value === possibleValue){
+ return true; // Boolean
+ }
+ return false;
+ });
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/CssRuleStore.xd.js b/js/dojo-1.6/dojox/data/CssRuleStore.xd.js
new file mode 100644
index 0000000..438821e
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CssRuleStore.xd.js
@@ -0,0 +1,482 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.CssRuleStore"],
+["require", "dojo.data.util.sorter"],
+["require", "dojo.data.util.filter"],
+["require", "dojox.data.css"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.CssRuleStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CssRuleStore"] = true;
+dojo.provide("dojox.data.CssRuleStore");
+dojo.require("dojo.data.util.sorter");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.data.css");
+
+
+
+dojo.declare("dojox.data.CssRuleStore", null, {
+ // summary:
+ // Basic store to display CSS information.
+ // description:
+ // The CssRuleStore allows users to get information about active CSS rules in the page running the CssRuleStore.
+ // It can also filter out rules from specific stylesheets. The attributes it exposes on rules are as follows:
+ // selector: The selector text.
+ // classes: An array of classes present in this selector.
+ // rule: The actual DOM Rule object.
+ // style: The actual DOM CSSStyleDeclaration object.
+ // cssText: The cssText string provided on the rule object.
+ // styleSheet: The originating DOM Stylesheet object.
+ // parentStyleSheet: The parent stylesheet to the sheet this rule originates from.
+ // parentStyleSheetHref: The href of the parent stylesheet.
+ // AND every style attribute denoted as style.*, such as style.textAlign or style.backgroundColor
+
+ _storeRef: '_S',
+ _labelAttribute: 'selector', // text representation of the Item [label and identifier may need to stay due to method names]
+
+ _cache: null,
+
+ _browserMap: null,
+
+ _cName: "dojox.data.CssRuleStore",
+
+ constructor: function(/* Object */ keywordParameters){
+ // Initializes this store
+ if(keywordParameters){
+ dojo.mixin(this, keywordParameters);
+ }
+ this._cache = {};
+ this._allItems = null;
+ this._waiting = [];
+ this.gatherHandle = null;
+ var self = this;
+ // CSS files may not be finished loading by the time the store is constructed. We need to
+ // give them a little time, so setting the stylesheet loading to retry every 250ms.
+ function gatherRules(){
+ try{
+ // Funkiness here is due to css that may still be loading. This throws an DOM Access
+ // error if css isnt completely loaded.
+ self.context = dojox.data.css.determineContext(self.context);
+ if(self.gatherHandle){
+ clearInterval(self.gatherHandle);
+ self.gatherHandle = null;
+ }
+ // Handle any fetches that have been queued while we've been waiting on the CSS files
+ // to finish
+ while(self._waiting.length){
+ var item = self._waiting.pop();
+ dojox.data.css.rules.forEach(item.forFunc, null, self.context);
+ item.finishFunc();
+ }
+ }catch(e){}
+ }
+ this.gatherHandle = setInterval(gatherRules,250);
+ },
+
+ setContext: function(/* Array */ context){
+ // Sets the context in which queries are executed
+ // context: Array - Array of CSS string paths to execute queries within
+ if(context){
+ this.close();
+ this.context = dojox.data.css.determineContext(context);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ "dojo.data.api.Read" : true
+ };
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] == this){
+ return true;
+ }
+ return false;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var attrs = this.getAttributes(item);
+ if(dojo.indexOf(attrs, attribute) != -1){
+ return true;
+ }
+ return false;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attrs = ['selector', 'classes', 'rule', 'style', 'cssText', 'styleSheet', 'parentStyleSheet', 'parentStyleSheetHref'];
+ var style = item.rule.style;
+ if(style){
+ var key;
+ for(key in style){
+ attrs.push("style." + key);
+ }
+ }
+ return attrs;
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ var value = defaultValue;
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = null;
+ if(attribute === "selector"){
+ value = item.rule["selectorText"];
+ if(value && dojo.isString(value)){
+ value = value.split(",");
+ }
+ }else if(attribute === "classes"){
+ value = item.classes;
+ }else if(attribute === "rule"){
+ value = item.rule.rule;
+ }else if(attribute === "style"){
+ value = item.rule.style;
+ }else if(attribute === "cssText"){
+ if(dojo.isIE){
+ if(item.rule.style){
+ value = item.rule.style.cssText;
+ if(value){
+ value = "{ " + value.toLowerCase() + " }";
+ }
+ }
+ }else{
+ value = item.rule.cssText;
+ if(value){
+ value = value.substring(value.indexOf("{"), value.length);
+ }
+ }
+ }else if(attribute === "styleSheet"){
+ value = item.rule.styleSheet;
+ }else if(attribute === "parentStyleSheet"){
+ value = item.rule.parentStyleSheet;
+ }else if(attribute === "parentStyleSheetHref"){
+ if(item.href){
+ value = item.href;
+ }
+ }else if(attribute.indexOf("style.") === 0){
+ var attr = attribute.substring(attribute.indexOf("."), attribute.length);
+ value = item.rule.style[attr];
+ }else{
+ value = [];
+ }
+ if(value !== undefined){
+ if(!dojo.isArray(value)){
+ value = [value];
+ }
+ }
+ return value;
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ this._assertIsItem(item);
+ return this.getValue(item, this._labelAttribute);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._labelAttribute];
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ fetch: function(request){
+ // summary:
+ // See dojo.data.api.Read.fetch()
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+
+ var scope = request.scope || dojo.global;
+ if(this._pending && this._pending.length > 0){
+ this._pending.push({request: request, fetch: true});
+ }else{
+ this._pending = [{request: request, fetch: true}];
+ this._fetch(request);
+ }
+ return request;
+ },
+
+ _fetch: function(request){
+ // summary:
+ // Populates the _allItems object with unique class names
+ var scope = request.scope || dojo.global;
+ if(this._allItems === null){
+ this._allItems = {};
+ try{
+ if(this.gatherHandle){
+ this._waiting.push({'forFunc': dojo.hitch(this, this._handleRule), 'finishFunc': dojo.hitch(this, this._handleReturn)});
+ }else{
+ dojox.data.css.rules.forEach(dojo.hitch(this, this._handleRule), null, this.context);
+ this._handleReturn();
+ }
+ }catch(e){
+ if(request.onError){
+ request.onError.call(scope, e, request);
+ }
+ }
+ }else{
+ this._handleReturn();
+ }
+ },
+
+ _handleRule: function(rule, styleSheet, href){
+ // summary:
+ // Handles the creation of an item based on the passed rule. In this store, this implies
+ // parsing out all available class names.
+ var selector = rule['selectorText'];
+ var s = selector.split(" ");
+ var classes = [];
+ for(var j=0; j<s.length; j++){
+ var tmp = s[j];
+ var first = tmp.indexOf('.');
+ if(tmp && tmp.length > 0 && first !== -1){
+ var last = tmp.indexOf(',') || tmp.indexOf('[');
+ tmp = tmp.substring(first, ((last !== -1 && last > first)?last:tmp.length));
+ classes.push(tmp);
+ }
+ }
+ var item = {};
+ item.rule = rule;
+ item.styleSheet = styleSheet;
+ item.href = href;
+ item.classes = classes;
+ item[this._storeRef] = this;
+ if(!this._allItems[selector]){
+ this._allItems[selector] = [];
+ }
+ this._allItems[selector].push(item);
+ },
+
+ _handleReturn: function(){
+ // summary:
+ // Handles the return from a fetching action. Delegates requests to act on the resulting
+ // item set to eitehr the _handleFetchReturn or _handleFetchByIdentityReturn depending on
+ // where the request originated.
+ var _inProgress = [];
+
+ var items = [];
+ var item = null;
+ for(var i in this._allItems){
+ item = this._allItems[i];
+ for(var j in item){
+ items.push(item[j]);
+ }
+ }
+
+ var requestInfo;
+ // One-level deep clone (can't use dojo.clone, since we don't want to clone all those store refs!)
+ while(this._pending.length){
+ requestInfo = this._pending.pop();
+ requestInfo.request._items = items;
+ _inProgress.push(requestInfo);
+ }
+
+ while(_inProgress.length){
+ requestInfo = _inProgress.pop();
+ this._handleFetchReturn(requestInfo.request);
+ }
+ },
+
+ _handleFetchReturn: function(/*Request */ request){
+ // summary:
+ // Handles a fetchByIdentity request by finding the correct items.
+ var scope = request.scope || dojo.global;
+ var items = [];
+ //Check to see if we've looked this query up before
+ //If so, just reuse it, much faster. Only regen if query changes.
+ var cacheKey = "all";
+ var i;
+ if(request.query){
+ cacheKey = dojo.toJson(request.query);
+ }
+ if(this._cache[cacheKey]){
+ items = this._cache[cacheKey];
+ }else if(request.query){
+ for(i in request._items){
+ var item = request._items[i];
+ // Per https://bugs.webkit.org/show_bug.cgi?id=17935 , Safari 3.x always returns the selectorText
+ // of a rule in full lowercase.
+ var ignoreCase = dojo.isWebKit ? true : (request.queryOptions ? request.queryOptions.ignoreCase : false);
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+ var match = true;
+ for(key in request.query){
+ value = request.query[key];
+ if(!this._containsValue(item, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(item);
+ }
+ }
+ this._cache[cacheKey] = items;
+ }else{
+ for(i in request._items){
+ items.push(request._items[i]);
+ }
+ }
+ var total = items.length;
+
+ //Sort it if we need to.
+ if(request.sort){
+ items.sort(dojo.data.util.sorter.createSortFunction(request.sort, this));
+ }
+ var start = 0;
+ var count = items.length;
+ if(request.start > 0 && request.start < items.length){
+ start = request.start;
+ }
+ if(request.count && request.count){
+ count = request.count;
+ }
+ var endIdx = start + count;
+ if(endIdx > items.length){
+ endIdx = items.length;
+ }
+
+ items = items.slice(start, endIdx);
+
+ if(request.onBegin){
+ request.onBegin.call(scope, total, request);
+ }
+ if(request.onItem){
+ if(dojo.isArray(items)){
+ for(i = 0; i < items.length; i++){
+ request.onItem.call(scope, items[i], request);
+ }
+ if(request.onComplete){
+ request.onComplete.call(scope, null, request);
+ }
+ }
+ }else if(request.onComplete){
+ request.onComplete.call(scope, items, request);
+ }
+ return request;
+ },
+
+ close: function(){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // Clears out the cache and allItems objects, meaning all future fetches will requery
+ // the stylesheets.
+ this._cache = {};
+ this._allItems = null;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this._cName + ": Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error(this._cName + ": Invalid attribute argument.");
+ }
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ return dojo.some(this.getValues(item, attribute), function(possibleValue){
+ if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
+ if(possibleValue.toString().match(regexp)){
+ return true; // Boolean
+ }
+ }else if(value === possibleValue){
+ return true; // Boolean
+ }
+ return false;
+ });
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/CsvStore.js b/js/dojo-1.6/dojox/data/CsvStore.js
new file mode 100644
index 0000000..454c435
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CsvStore.js
@@ -0,0 +1,737 @@
+/*
+ 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["dojox.data.CsvStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CsvStore"] = true;
+dojo.provide("dojox.data.CsvStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+
+
+
+dojo.declare("dojox.data.CsvStore", null, {
+ // summary:
+ // The CsvStore implements the dojo.data.api.Read API and reads
+ // data from files in CSV (Comma Separated Values) format.
+ // All values are simple string values. References to other items
+ // are not supported as attribute values in this datastore.
+ //
+ // Example data file:
+ // name, color, age, tagline
+ // Kermit, green, 12, "Hi, I'm Kermit the Frog."
+ // Fozzie Bear, orange, 10, "Wakka Wakka Wakka!"
+ // Miss Piggy, pink, 11, "Kermie!"
+ //
+ // Note that values containing a comma must be enclosed with quotes ("")
+ // Also note that values containing quotes must be escaped with two consecutive quotes (""quoted"")
+ //
+ // examples:
+ // var csvStore = new dojox.data.CsvStore({url:"movies.csv");
+ // var csvStore = new dojox.data.CsvStore({url:"http://example.com/movies.csv");
+
+ constructor: function(/* Object */ keywordParameters){
+ // summary:
+ // initializer
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: String}
+ // keywordParameters: {label: String} The column label for the column to use for the label returned by getLabel.
+ // keywordParameters: {identifier: String} The column label for the column to use for the identity. Optional. If not set, the identity is the row number.
+
+ this._attributes = []; // e.g. ["Title", "Year", "Producer"]
+ this._attributeIndexes = {}; // e.g. {Title: 0, Year: 1, Producer: 2}
+ this._dataArray = []; // e.g. [[<Item0>],[<Item1>],[<Item2>]]
+ this._arrayOfAllItems = []; // e.g. [{_csvId:0,_csvStore:store},...]
+ this._loadFinished = false;
+ if(keywordParameters.url){
+ this.url = keywordParameters.url;
+ }
+ this._csvData = keywordParameters.data;
+ if(keywordParameters.label){
+ this.label = keywordParameters.label;
+ }else if(this.label === ""){
+ this.label = undefined;
+ }
+ this._storeProp = "_csvStore"; // Property name for the store reference on every item.
+ this._idProp = "_csvId"; // Property name for the Item Id on every item.
+ this._features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ this.identifier = keywordParameters.identifier;
+ if(this.identifier === ""){
+ delete this.identifier;
+ }else{
+ this._idMap = {};
+ }
+ if("separator" in keywordParameters){
+ this.separator = keywordParameters.separator;
+ }
+ if("urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ // url: [public] string
+ // Declarative hook for setting Csv source url.
+ url: "",
+
+ // label: [public] string
+ // Declarative hook for setting the label attribute.
+ label: "",
+
+ // identifier: [public] string
+ // Declarative hook for setting the identifier.
+ identifier: "",
+
+ // separator: [public] string
+ // Declatative and programmatic hook for defining the separator
+ // character used in the Csv style file.
+ separator: ",",
+
+ // separator: [public] string
+ // Parameter to allow specifying if preventCache should be passed to
+ // the xhrGet call or not when loading data from a url.
+ // Note this does not mean the store calls the server on each fetch,
+ // only that the data load has preventCache set as an option.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this.declaredClass + ": a function was passed an item argument that was not an item");
+ }
+ },
+
+ _getIndex: function(item){
+ // summary:
+ // Internal function to get the internal index to the item data from the item handle
+ // item:
+ // The idem handle to get the index for.
+ var idx = this.getIdentity(item);
+ if(this.identifier){
+ idx = this._idMap[idx];
+ }
+ return idx;
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ // Note that for the CsvStore, an empty string value is the same as no value,
+ // so the defaultValue would be returned instead of an empty string.
+ this._assertIsItem(item);
+ var itemValue = defaultValue;
+ if(typeof attribute === "string"){
+ var ai = this._attributeIndexes[attribute];
+ if(ai != null){
+ var itemData = this._dataArray[this._getIndex(item)];
+ itemValue = itemData[ai] || defaultValue;
+ }
+ }else{
+ throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
+ }
+ return itemValue; //String
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ // CSV syntax does not support multi-valued attributes, so this is just a
+ // wrapper function for getValue().
+ var value = this.getValue(item, attribute);
+ return (value ? [value] : []); //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ var itemData = this._dataArray[this._getIndex(item)];
+ for(var i=0; i<itemData.length; i++){
+ // Check for empty string values. CsvStore treats empty strings as no value.
+ if(itemData[i] !== ""){
+ attributes.push(this._attributes[i]);
+ }
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ // The hasAttribute test is true if attribute has an index number within the item's array length
+ // AND if the item has a value for that attribute. Note that for the CsvStore, an
+ // empty string value is the same as no value.
+ this._assertIsItem(item);
+ if(typeof attribute === "string"){
+ var attributeIndex = this._attributeIndexes[attribute];
+ var itemData = this._dataArray[this._getIndex(item)];
+ return (typeof attributeIndex !== "undefined" && attributeIndex < itemData.length && itemData[attributeIndex] !== ""); //Boolean
+ }else{
+ throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
+ }
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ // tags:
+ // private
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeProp] === this){
+ var identity = something[this._idProp];
+ //If an identifier was specified, we have to look it up via that and the mapping,
+ //otherwise, just use row number.
+ if(this.identifier){
+ var data = this._dataArray[this._idMap[identity]];
+ if(data){
+ return true;
+ }
+ }else{
+ if(identity >= 0 && identity < this._dataArray.length){
+ return true; //Boolean
+ }
+ }
+ }
+ return false; //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // The CsvStore always loads all items, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The CsvStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke
+ // the callback handlers.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.label && this.isItem(item)){
+ return this.getValue(item,this.label); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ // tags:
+ // protected
+ var self = this;
+ var filter = function(requestArgs, arrayOfAllItems){
+ var items = null;
+ if(requestArgs.query){
+ var key, value;
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(key in requestArgs.query){
+ value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in requestArgs.query){
+ value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "text",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._processData(data);
+ filter(keywordArgs, self._arrayOfAllItems);
+ self._handleQueuedFetches();
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ if(errorCallback){
+ errorCallback(error, keywordArgs);
+ }else{
+ throw error;
+ }
+ });
+ //Wire up the cancel to abort of the request
+ //This call cancel on the deferred if it hasn't been called
+ //yet and then will chain to the simple abort of the
+ //simpleFetch keywordArgs
+ var oldAbort = null;
+ if(keywordArgs.abort){
+ oldAbort = keywordArgs.abort;
+ }
+ keywordArgs.abort = function(){
+ var df = getHandler;
+ if(df && df.fired === -1){
+ df.cancel();
+ df = null;
+ }
+ if(oldAbort){
+ oldAbort.call(keywordArgs);
+ }
+ };
+ }
+ }else if(this._csvData){
+ try{
+ this._processData(this._csvData);
+ this._csvData = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ }else{
+ var error = new Error(this.declaredClass + ": No CSV source data was provided as either URL or String data input.");
+ if(errorCallback){
+ errorCallback(error, keywordArgs);
+ }else{
+ throw error;
+ }
+ }
+ }
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+
+ // -------------------------------------------------------------------
+ // Private methods
+ _getArrayOfArraysFromCsvFileContents: function(/* string */ csvFileContents){
+ // summary:
+ // Parses a string of CSV records into a nested array structure.
+ // description:
+ // Given a string containing CSV records, this method parses
+ // the string and returns a data structure containing the parsed
+ // content. The data structure we return is an array of length
+ // R, where R is the number of rows (lines) in the CSV data. The
+ // return array contains one sub-array for each CSV line, and each
+ // sub-array contains C string values, where C is the number of
+ // columns in the CSV data.
+ // example:
+ // For example, given this CSV string as input:
+ // "Title, Year, Producer \n Alien, 1979, Ridley Scott \n Blade Runner, 1982, Ridley Scott"
+ // this._dataArray will be set to:
+ // [["Alien", "1979", "Ridley Scott"],
+ // ["Blade Runner", "1982", "Ridley Scott"]]
+ // And this._attributes will be set to:
+ // ["Title", "Year", "Producer"]
+ // And this._attributeIndexes will be set to:
+ // { "Title":0, "Year":1, "Producer":2 }
+ // tags:
+ // private
+ if(dojo.isString(csvFileContents)){
+ var leadingWhiteSpaceCharacters = new RegExp("^\\s+",'g');
+ var trailingWhiteSpaceCharacters = new RegExp("\\s+$",'g');
+ var doubleQuotes = new RegExp('""','g');
+ var arrayOfOutputRecords = [];
+ var i;
+
+ var arrayOfInputLines = this._splitLines(csvFileContents);
+ for(i = 0; i < arrayOfInputLines.length; ++i){
+ var singleLine = arrayOfInputLines[i];
+ if(singleLine.length > 0){
+ var listOfFields = singleLine.split(this.separator);
+ var j = 0;
+ while(j < listOfFields.length){
+ var space_field_space = listOfFields[j];
+ var field_space = space_field_space.replace(leadingWhiteSpaceCharacters, ''); // trim leading whitespace
+ var field = field_space.replace(trailingWhiteSpaceCharacters, ''); // trim trailing whitespace
+ var firstChar = field.charAt(0);
+ var lastChar = field.charAt(field.length - 1);
+ var secondToLastChar = field.charAt(field.length - 2);
+ var thirdToLastChar = field.charAt(field.length - 3);
+ if(field.length === 2 && field == "\"\""){
+ listOfFields[j] = ""; //Special case empty string field.
+ }else if((firstChar == '"') &&
+ ((lastChar != '"') ||
+ ((lastChar == '"') && (secondToLastChar == '"') && (thirdToLastChar != '"')))){
+ if(j+1 === listOfFields.length){
+ // alert("The last field in record " + i + " is corrupted:\n" + field);
+ return; //null
+ }
+ var nextField = listOfFields[j+1];
+ listOfFields[j] = field_space + this.separator + nextField;
+ listOfFields.splice(j+1, 1); // delete element [j+1] from the list
+ }else{
+ if((firstChar == '"') && (lastChar == '"')){
+ field = field.slice(1, (field.length - 1)); // trim the " characters off the ends
+ field = field.replace(doubleQuotes, '"'); // replace "" with "
+ }
+ listOfFields[j] = field;
+ j += 1;
+ }
+ }
+ arrayOfOutputRecords.push(listOfFields);
+ }
+ }
+
+ // The first item of the array must be the header row with attribute names.
+ this._attributes = arrayOfOutputRecords.shift();
+ for(i = 0; i<this._attributes.length; i++){
+ // Store the index of each attribute
+ this._attributeIndexes[this._attributes[i]] = i;
+ }
+ this._dataArray = arrayOfOutputRecords; //Array
+ }
+ },
+
+ _splitLines: function(csvContent){
+ // summary:
+ // Function to split the CSV file contents into separate lines.
+ // Since line breaks can occur inside quotes, a Regexp didn't
+ // work as well. A quick passover parse should be just as efficient.
+ // tags:
+ // private
+ var split = [];
+ var i;
+ var line = "";
+ var inQuotes = false;
+ for(i = 0; i < csvContent.length; i++){
+ var c = csvContent.charAt(i);
+ switch(c){
+ case '\"':
+ inQuotes = !inQuotes;
+ line += c;
+ break;
+ case '\r':
+ if(inQuotes){
+ line += c;
+ }else{
+ split.push(line);
+ line = "";
+ if(i < (csvContent.length - 1) && csvContent.charAt(i + 1) == '\n'){
+ i++; //Skip it, it's CRLF
+ }
+ }
+ break;
+ case '\n':
+ if(inQuotes){
+ line += c;
+ }else{
+ split.push(line);
+ line = "";
+ }
+ break;
+ default:
+ line +=c;
+ }
+ }
+ if(line !== ""){
+ split.push(line);
+ }
+ return split;
+ },
+
+ _processData: function(/* String */ data){
+ // summary:
+ // Function for processing the string data from the server.
+ // data: String
+ // The CSV data.
+ // tags:
+ // private
+ this._getArrayOfArraysFromCsvFileContents(data);
+ this._arrayOfAllItems = [];
+
+ //Check that the specified Identifier is actually a column title, if provided.
+ if(this.identifier){
+ if(this._attributeIndexes[this.identifier] === undefined){
+ throw new Error(this.declaredClass + ": Identity specified is not a column header in the data set.");
+ }
+ }
+
+ for(var i=0; i<this._dataArray.length; i++){
+ var id = i;
+ //Associate the identifier to a row in this case
+ //for o(1) lookup.
+ if(this.identifier){
+ var iData = this._dataArray[i];
+ id = iData[this._attributeIndexes[this.identifier]];
+ this._idMap[id] = i;
+ }
+ this._arrayOfAllItems.push(this._createItemFromIdentity(id));
+ }
+ this._loadFinished = true;
+ this._loadInProgress = false;
+ },
+
+ _createItemFromIdentity: function(/* String */ identity){
+ // summary:
+ // Function for creating a new item from its identifier.
+ // identity: String
+ // The identity
+ // tags:
+ // private
+ var item = {};
+ item[this._storeProp] = this;
+ item[this._idProp] = identity;
+ return item; //Object
+ },
+
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ // tags:
+ // public
+ if(this.isItem(item)){
+ return item[this._idProp]; //String
+ }
+ return null; //null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ // tags:
+ // public
+ var item;
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ //Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._processData(data);
+ var item = self._createItemFromIdentity(keywordArgs.identity);
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ this._loadInProgress = false;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+ }else if(this._csvData){
+ try{
+ self._processData(self._csvData);
+ self._csvData = null;
+ item = self._createItemFromIdentity(keywordArgs.identity);
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }catch(e){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, e);
+ }
+ }
+ }
+ }else{
+ //Already loaded. We can just look it up and call back.
+ item = this._createItemFromIdentity(keywordArgs.identity);
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+ // tags:
+ // public
+
+ //Identity isn't a public attribute in the item, it's the row position index.
+ //So, return null.
+ if(this.identifier){
+ return [this.identifier];
+ }else{
+ return null;
+ }
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ // tags:
+ // private
+
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedFilter = fData.filter;
+ var delayedQuery = fData.args;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._arrayOfAllItems);
+ }else{
+ this.fetchItemByIdentity(fData.args);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.CsvStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/CsvStore.xd.js b/js/dojo-1.6/dojox/data/CsvStore.xd.js
new file mode 100644
index 0000000..8d0524c
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/CsvStore.xd.js
@@ -0,0 +1,743 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.CsvStore"],
+["require", "dojo.data.util.filter"],
+["require", "dojo.data.util.simpleFetch"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.CsvStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.CsvStore"] = true;
+dojo.provide("dojox.data.CsvStore");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojo.data.util.simpleFetch");
+
+
+
+dojo.declare("dojox.data.CsvStore", null, {
+ // summary:
+ // The CsvStore implements the dojo.data.api.Read API and reads
+ // data from files in CSV (Comma Separated Values) format.
+ // All values are simple string values. References to other items
+ // are not supported as attribute values in this datastore.
+ //
+ // Example data file:
+ // name, color, age, tagline
+ // Kermit, green, 12, "Hi, I'm Kermit the Frog."
+ // Fozzie Bear, orange, 10, "Wakka Wakka Wakka!"
+ // Miss Piggy, pink, 11, "Kermie!"
+ //
+ // Note that values containing a comma must be enclosed with quotes ("")
+ // Also note that values containing quotes must be escaped with two consecutive quotes (""quoted"")
+ //
+ // examples:
+ // var csvStore = new dojox.data.CsvStore({url:"movies.csv");
+ // var csvStore = new dojox.data.CsvStore({url:"http://example.com/movies.csv");
+
+ constructor: function(/* Object */ keywordParameters){
+ // summary:
+ // initializer
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: String}
+ // keywordParameters: {label: String} The column label for the column to use for the label returned by getLabel.
+ // keywordParameters: {identifier: String} The column label for the column to use for the identity. Optional. If not set, the identity is the row number.
+
+ this._attributes = []; // e.g. ["Title", "Year", "Producer"]
+ this._attributeIndexes = {}; // e.g. {Title: 0, Year: 1, Producer: 2}
+ this._dataArray = []; // e.g. [[<Item0>],[<Item1>],[<Item2>]]
+ this._arrayOfAllItems = []; // e.g. [{_csvId:0,_csvStore:store},...]
+ this._loadFinished = false;
+ if(keywordParameters.url){
+ this.url = keywordParameters.url;
+ }
+ this._csvData = keywordParameters.data;
+ if(keywordParameters.label){
+ this.label = keywordParameters.label;
+ }else if(this.label === ""){
+ this.label = undefined;
+ }
+ this._storeProp = "_csvStore"; // Property name for the store reference on every item.
+ this._idProp = "_csvId"; // Property name for the Item Id on every item.
+ this._features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ this.identifier = keywordParameters.identifier;
+ if(this.identifier === ""){
+ delete this.identifier;
+ }else{
+ this._idMap = {};
+ }
+ if("separator" in keywordParameters){
+ this.separator = keywordParameters.separator;
+ }
+ if("urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ // url: [public] string
+ // Declarative hook for setting Csv source url.
+ url: "",
+
+ // label: [public] string
+ // Declarative hook for setting the label attribute.
+ label: "",
+
+ // identifier: [public] string
+ // Declarative hook for setting the identifier.
+ identifier: "",
+
+ // separator: [public] string
+ // Declatative and programmatic hook for defining the separator
+ // character used in the Csv style file.
+ separator: ",",
+
+ // separator: [public] string
+ // Parameter to allow specifying if preventCache should be passed to
+ // the xhrGet call or not when loading data from a url.
+ // Note this does not mean the store calls the server on each fetch,
+ // only that the data load has preventCache set as an option.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this.declaredClass + ": a function was passed an item argument that was not an item");
+ }
+ },
+
+ _getIndex: function(item){
+ // summary:
+ // Internal function to get the internal index to the item data from the item handle
+ // item:
+ // The idem handle to get the index for.
+ var idx = this.getIdentity(item);
+ if(this.identifier){
+ idx = this._idMap[idx];
+ }
+ return idx;
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ // Note that for the CsvStore, an empty string value is the same as no value,
+ // so the defaultValue would be returned instead of an empty string.
+ this._assertIsItem(item);
+ var itemValue = defaultValue;
+ if(typeof attribute === "string"){
+ var ai = this._attributeIndexes[attribute];
+ if(ai != null){
+ var itemData = this._dataArray[this._getIndex(item)];
+ itemValue = itemData[ai] || defaultValue;
+ }
+ }else{
+ throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
+ }
+ return itemValue; //String
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ // CSV syntax does not support multi-valued attributes, so this is just a
+ // wrapper function for getValue().
+ var value = this.getValue(item, attribute);
+ return (value ? [value] : []); //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ var itemData = this._dataArray[this._getIndex(item)];
+ for(var i=0; i<itemData.length; i++){
+ // Check for empty string values. CsvStore treats empty strings as no value.
+ if(itemData[i] !== ""){
+ attributes.push(this._attributes[i]);
+ }
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ // The hasAttribute test is true if attribute has an index number within the item's array length
+ // AND if the item has a value for that attribute. Note that for the CsvStore, an
+ // empty string value is the same as no value.
+ this._assertIsItem(item);
+ if(typeof attribute === "string"){
+ var attributeIndex = this._attributeIndexes[attribute];
+ var itemData = this._dataArray[this._getIndex(item)];
+ return (typeof attributeIndex !== "undefined" && attributeIndex < itemData.length && itemData[attributeIndex] !== ""); //Boolean
+ }else{
+ throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
+ }
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ // tags:
+ // private
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeProp] === this){
+ var identity = something[this._idProp];
+ //If an identifier was specified, we have to look it up via that and the mapping,
+ //otherwise, just use row number.
+ if(this.identifier){
+ var data = this._dataArray[this._idMap[identity]];
+ if(data){
+ return true;
+ }
+ }else{
+ if(identity >= 0 && identity < this._dataArray.length){
+ return true; //Boolean
+ }
+ }
+ }
+ return false; //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // The CsvStore always loads all items, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The CsvStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke
+ // the callback handlers.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.label && this.isItem(item)){
+ return this.getValue(item,this.label); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+ // tags:
+ // protected
+ var self = this;
+ var filter = function(requestArgs, arrayOfAllItems){
+ var items = null;
+ if(requestArgs.query){
+ var key, value;
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(key in requestArgs.query){
+ value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in requestArgs.query){
+ value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "text",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._processData(data);
+ filter(keywordArgs, self._arrayOfAllItems);
+ self._handleQueuedFetches();
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ if(errorCallback){
+ errorCallback(error, keywordArgs);
+ }else{
+ throw error;
+ }
+ });
+ //Wire up the cancel to abort of the request
+ //This call cancel on the deferred if it hasn't been called
+ //yet and then will chain to the simple abort of the
+ //simpleFetch keywordArgs
+ var oldAbort = null;
+ if(keywordArgs.abort){
+ oldAbort = keywordArgs.abort;
+ }
+ keywordArgs.abort = function(){
+ var df = getHandler;
+ if(df && df.fired === -1){
+ df.cancel();
+ df = null;
+ }
+ if(oldAbort){
+ oldAbort.call(keywordArgs);
+ }
+ };
+ }
+ }else if(this._csvData){
+ try{
+ this._processData(this._csvData);
+ this._csvData = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }catch(e){
+ errorCallback(e, keywordArgs);
+ }
+ }else{
+ var error = new Error(this.declaredClass + ": No CSV source data was provided as either URL or String data input.");
+ if(errorCallback){
+ errorCallback(error, keywordArgs);
+ }else{
+ throw error;
+ }
+ }
+ }
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+
+ // -------------------------------------------------------------------
+ // Private methods
+ _getArrayOfArraysFromCsvFileContents: function(/* string */ csvFileContents){
+ // summary:
+ // Parses a string of CSV records into a nested array structure.
+ // description:
+ // Given a string containing CSV records, this method parses
+ // the string and returns a data structure containing the parsed
+ // content. The data structure we return is an array of length
+ // R, where R is the number of rows (lines) in the CSV data. The
+ // return array contains one sub-array for each CSV line, and each
+ // sub-array contains C string values, where C is the number of
+ // columns in the CSV data.
+ // example:
+ // For example, given this CSV string as input:
+ // "Title, Year, Producer \n Alien, 1979, Ridley Scott \n Blade Runner, 1982, Ridley Scott"
+ // this._dataArray will be set to:
+ // [["Alien", "1979", "Ridley Scott"],
+ // ["Blade Runner", "1982", "Ridley Scott"]]
+ // And this._attributes will be set to:
+ // ["Title", "Year", "Producer"]
+ // And this._attributeIndexes will be set to:
+ // { "Title":0, "Year":1, "Producer":2 }
+ // tags:
+ // private
+ if(dojo.isString(csvFileContents)){
+ var leadingWhiteSpaceCharacters = new RegExp("^\\s+",'g');
+ var trailingWhiteSpaceCharacters = new RegExp("\\s+$",'g');
+ var doubleQuotes = new RegExp('""','g');
+ var arrayOfOutputRecords = [];
+ var i;
+
+ var arrayOfInputLines = this._splitLines(csvFileContents);
+ for(i = 0; i < arrayOfInputLines.length; ++i){
+ var singleLine = arrayOfInputLines[i];
+ if(singleLine.length > 0){
+ var listOfFields = singleLine.split(this.separator);
+ var j = 0;
+ while(j < listOfFields.length){
+ var space_field_space = listOfFields[j];
+ var field_space = space_field_space.replace(leadingWhiteSpaceCharacters, ''); // trim leading whitespace
+ var field = field_space.replace(trailingWhiteSpaceCharacters, ''); // trim trailing whitespace
+ var firstChar = field.charAt(0);
+ var lastChar = field.charAt(field.length - 1);
+ var secondToLastChar = field.charAt(field.length - 2);
+ var thirdToLastChar = field.charAt(field.length - 3);
+ if(field.length === 2 && field == "\"\""){
+ listOfFields[j] = ""; //Special case empty string field.
+ }else if((firstChar == '"') &&
+ ((lastChar != '"') ||
+ ((lastChar == '"') && (secondToLastChar == '"') && (thirdToLastChar != '"')))){
+ if(j+1 === listOfFields.length){
+ // alert("The last field in record " + i + " is corrupted:\n" + field);
+ return; //null
+ }
+ var nextField = listOfFields[j+1];
+ listOfFields[j] = field_space + this.separator + nextField;
+ listOfFields.splice(j+1, 1); // delete element [j+1] from the list
+ }else{
+ if((firstChar == '"') && (lastChar == '"')){
+ field = field.slice(1, (field.length - 1)); // trim the " characters off the ends
+ field = field.replace(doubleQuotes, '"'); // replace "" with "
+ }
+ listOfFields[j] = field;
+ j += 1;
+ }
+ }
+ arrayOfOutputRecords.push(listOfFields);
+ }
+ }
+
+ // The first item of the array must be the header row with attribute names.
+ this._attributes = arrayOfOutputRecords.shift();
+ for(i = 0; i<this._attributes.length; i++){
+ // Store the index of each attribute
+ this._attributeIndexes[this._attributes[i]] = i;
+ }
+ this._dataArray = arrayOfOutputRecords; //Array
+ }
+ },
+
+ _splitLines: function(csvContent){
+ // summary:
+ // Function to split the CSV file contents into separate lines.
+ // Since line breaks can occur inside quotes, a Regexp didn't
+ // work as well. A quick passover parse should be just as efficient.
+ // tags:
+ // private
+ var split = [];
+ var i;
+ var line = "";
+ var inQuotes = false;
+ for(i = 0; i < csvContent.length; i++){
+ var c = csvContent.charAt(i);
+ switch(c){
+ case '\"':
+ inQuotes = !inQuotes;
+ line += c;
+ break;
+ case '\r':
+ if(inQuotes){
+ line += c;
+ }else{
+ split.push(line);
+ line = "";
+ if(i < (csvContent.length - 1) && csvContent.charAt(i + 1) == '\n'){
+ i++; //Skip it, it's CRLF
+ }
+ }
+ break;
+ case '\n':
+ if(inQuotes){
+ line += c;
+ }else{
+ split.push(line);
+ line = "";
+ }
+ break;
+ default:
+ line +=c;
+ }
+ }
+ if(line !== ""){
+ split.push(line);
+ }
+ return split;
+ },
+
+ _processData: function(/* String */ data){
+ // summary:
+ // Function for processing the string data from the server.
+ // data: String
+ // The CSV data.
+ // tags:
+ // private
+ this._getArrayOfArraysFromCsvFileContents(data);
+ this._arrayOfAllItems = [];
+
+ //Check that the specified Identifier is actually a column title, if provided.
+ if(this.identifier){
+ if(this._attributeIndexes[this.identifier] === undefined){
+ throw new Error(this.declaredClass + ": Identity specified is not a column header in the data set.");
+ }
+ }
+
+ for(var i=0; i<this._dataArray.length; i++){
+ var id = i;
+ //Associate the identifier to a row in this case
+ //for o(1) lookup.
+ if(this.identifier){
+ var iData = this._dataArray[i];
+ id = iData[this._attributeIndexes[this.identifier]];
+ this._idMap[id] = i;
+ }
+ this._arrayOfAllItems.push(this._createItemFromIdentity(id));
+ }
+ this._loadFinished = true;
+ this._loadInProgress = false;
+ },
+
+ _createItemFromIdentity: function(/* String */ identity){
+ // summary:
+ // Function for creating a new item from its identifier.
+ // identity: String
+ // The identity
+ // tags:
+ // private
+ var item = {};
+ item[this._storeProp] = this;
+ item[this._idProp] = identity;
+ return item; //Object
+ },
+
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ // tags:
+ // public
+ if(this.isItem(item)){
+ return item[this._idProp]; //String
+ }
+ return null; //null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ // tags:
+ // public
+ var item;
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ //Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ try{
+ self._processData(data);
+ var item = self._createItemFromIdentity(keywordArgs.identity);
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ this._loadInProgress = false;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+ }else if(this._csvData){
+ try{
+ self._processData(self._csvData);
+ self._csvData = null;
+ item = self._createItemFromIdentity(keywordArgs.identity);
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }catch(e){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, e);
+ }
+ }
+ }
+ }else{
+ //Already loaded. We can just look it up and call back.
+ item = this._createItemFromIdentity(keywordArgs.identity);
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+ // tags:
+ // public
+
+ //Identity isn't a public attribute in the item, it's the row position index.
+ //So, return null.
+ if(this.identifier){
+ return [this.identifier];
+ }else{
+ return null;
+ }
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ // tags:
+ // private
+
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedFilter = fData.filter;
+ var delayedQuery = fData.args;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._arrayOfAllItems);
+ }else{
+ this.fetchItemByIdentity(fData.args);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.CsvStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/FileStore.js b/js/dojo-1.6/dojox/data/FileStore.js
new file mode 100644
index 0000000..0dbcdb3
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FileStore.js
@@ -0,0 +1,441 @@
+/*
+ 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["dojox.data.FileStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FileStore"] = true;
+dojo.provide("dojox.data.FileStore");
+
+
+
+dojo.declare("dojox.data.FileStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // A simple store that provides a datastore interface to a filesystem.
+ // description:
+ // A simple store that provides a datastore interface to a filesystem. It takes a few parameters
+ // for initialization:
+ // url: The URL of the service which provides the file store serverside implementation.
+ // label: The attribute of the file to use as the huma-readable text. Default is 'name'.
+ // The purpose of this store is to represent a file as a datastore item. The
+ // datastore item by default has the following attributes that can be examined on it.
+ // directory: Boolean indicating if the file item represents a directory.
+ // name: The filename with no path informatiom.
+ // path: The file complete file path including name, relative to the location the
+ // file service scans from
+ // size: The size of the file, in bytes.
+ // parentDir: The parent directory path.
+ // children: Any child files contained by a directory file item.
+ //
+ // Note that the store's server call pattern is RESTlike.
+ //
+ // The store also supports the passing of configurable options to the back end service, such as
+ // expanding all child files (no lazy load), displaying hidden files, displaying only directories, and so on.
+ // These are defined through a comma-separated list in declarative, or through setting the options array in programmatic.
+ // example: options="expand,dirsOnly,showHiddenFiles"
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && args.url){
+ this.url = args.url;
+ }
+ if(args && args.options){
+ if(dojo.isArray(args.options)){
+ this.options = args.options;
+ }else{
+ if(dojo.isString(args.options)){
+ this.options = args.options.split(",");
+ }
+ }
+ }
+ if(args && args.pathAsQueryParam){
+ this.pathAsQueryParam = true;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ },
+
+ // url: [public] string
+ // The URL to the file path service.
+ url: "",
+
+ // _storeRef: [private] string
+ // Internal variable used to denote an item came from this store instance.
+ _storeRef: "_S",
+
+ // label: [public] string
+ // Default attribute to use to represent the item as a user-readable
+ // string. Public, so users can change it.
+ label: "name",
+
+ // _identifier: [private] string
+ // Default attribute to use to represent the item's identifier.
+ // Path should always be unique in the store instance.
+ _identifier: "path",
+
+ // _attributes: [private] string
+ // Internal variable of attributes all file items should have.
+ _attributes: ["children", "directory", "name", "path", "modified", "size", "parentDir"], //
+
+ // pathSeparator: [public] string
+ // The path separator to use when chaining requests for children
+ // Can be overriden by the server on initial load
+ pathSeparator: "/",
+
+ // options: [public] array
+ // Array of options to always send when doing requests.
+ // Back end service controls this, like 'dirsOnly', 'showHiddenFiles', 'expandChildren', etc.
+ options: [],
+
+ // failOk: [public] boolean
+ // Flag to pass on to xhr functions to check if we are OK to fail the call silently
+ failOk: false,
+
+ // urlPreventCache: [public] string
+ // Flag to dennote if preventCache should be passed to xhrGet.
+ urlPreventCache: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.FileStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.FileStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ pathAsQueryParam: false, //Function to switch between REST style URL lookups and passing the path to specific items as a query param: 'path'.
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true, 'dojo.data.api.Identity':true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return this._attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ return (attribute in item);
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ return this.getValue(item, this._identifier);
+ },
+
+ getIdentityAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._identifier];
+ },
+
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ var loaded = this.isItem(item);
+ if(loaded && typeof item._loaded == "boolean" && !item._loaded){
+ loaded = false;
+ }
+ return loaded;
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ var item = keywordArgs.item;
+ var self = this;
+ var scope = keywordArgs.scope || dojo.global;
+
+ var content = {};
+
+ if(this.options.length > 0){
+ content.options = dojo.toJson(this.options);
+ }
+
+ if(this.pathAsQueryParam){
+ content.path = item.parentPath + this.pathSeparator + item.name;
+ }
+ var xhrData = {
+ url: this.pathAsQueryParam? this.url : this.url + "/" + item.parentPath + "/" + item.name,
+ handleAs: "json-comment-optional",
+ content: content,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk
+ };
+
+ var deferred = dojo.xhrGet(xhrData);
+ deferred.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+
+ deferred.addCallback(function(data){
+ delete item.parentPath;
+ delete item._loaded;
+ dojo.mixin(item, data);
+ self._processItem(item);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] == value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+
+ var value = item[attribute];
+ if(typeof value !== "undefined" && !dojo.isArray(value)){
+ value = [value];
+ }else if(typeof value === "undefined"){
+ value = [];
+ }
+ return value;
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ fetch: function(request){
+ // summary:
+ // Fetch items that match to a query
+ // request:
+ // A request object
+
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ var self = this;
+ var scope = request.scope || dojo.global;
+
+ //Generate what will be sent over.
+ var reqParams = {};
+ if(request.query){
+ reqParams.query = dojo.toJson(request.query);
+ }
+
+ if(request.sort){
+ reqParams.sort = dojo.toJson(request.sort);
+ }
+
+ if(request.queryOptions){
+ reqParams.queryOptions = dojo.toJson(request.queryOptions);
+ }
+
+ if(typeof request.start == "number"){
+ reqParams.start = "" + request.start;
+ }
+ if(typeof request.count == "number"){
+ reqParams.count = "" + request.count;
+ }
+
+ if(this.options.length > 0){
+ reqParams.options = dojo.toJson(this.options);
+ }
+
+ var getArgs = {
+ url: this.url,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk,
+ handleAs: "json-comment-optional",
+ content: reqParams
+ };
+
+
+ var deferred = dojo.xhrGet(getArgs);
+
+ deferred.addCallback(function(data){self._processResult(data, request);});
+ deferred.addErrback(function(error){
+ if(request.onError){
+ request.onError.call(scope, error, request);
+ }
+ });
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ var path = keywordArgs.identity;
+ var self = this;
+ var scope = keywordArgs.scope || dojo.global;
+
+ var content = {};
+
+ if(this.options.length > 0){
+ content.options = dojo.toJson(this.options);
+ }
+
+ if(this.pathAsQueryParam){
+ content.path = path;
+ }
+ var xhrData = {
+ url: this.pathAsQueryParam? this.url : this.url + "/" + path,
+ handleAs: "json-comment-optional",
+ content: content,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk
+ };
+
+ var deferred = dojo.xhrGet(xhrData);
+ deferred.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+
+ deferred.addCallback(function(data){
+ var item = self._processItem(data);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ },
+
+ _processResult: function(data, request){
+ var scope = request.scope || dojo.global;
+ try{
+ //If the data contains a path separator, set ours
+ if(data.pathSeparator){
+ this.pathSeparator = data.pathSeparator;
+ }
+ //Invoke the onBegin handler, if any, to return the
+ //size of the dataset as indicated by the service.
+ if(request.onBegin){
+ request.onBegin.call(scope, data.total, request);
+ }
+ //Now process all the returned items thro
+ var items = this._processItemArray(data.items);
+ if(request.onItem){
+ var i;
+ for(i = 0; i < items.length; i++){
+ request.onItem.call(scope, items[i], request);
+ }
+ items = null;
+ }
+ if(request.onComplete){
+ request.onComplete.call(scope, items, request);
+ }
+ }catch (e){
+ if(request.onError){
+ request.onError.call(scope, e, request);
+ }else{
+ console.log(e);
+ }
+ }
+ },
+
+ _processItemArray: function(itemArray){
+ // summary:
+ // Internal function for processing an array of items for return.
+ var i;
+ for(i = 0; i < itemArray.length; i++){
+ this._processItem(itemArray[i]);
+ }
+ return itemArray;
+ },
+
+ _processItem: function(item){
+ // summary:
+ // Internal function for processing an item returned from the store.
+ // It sets up the store ref as well as sets up the attributes necessary
+ // to invoke a lazy load on a child, if there are any.
+ if(!item){return null;}
+ item[this._storeRef] = this;
+ if(item.children && item.directory){
+ if(dojo.isArray(item.children)){
+ var children = item.children;
+ var i;
+ for(i = 0; i < children.length; i++ ){
+ var name = children[i];
+ if(dojo.isObject(name)){
+ children[i] = this._processItem(name);
+ }else{
+ children[i] = {name: name, _loaded: false, parentPath: item.path};
+ children[i][this._storeRef] = this;
+ }
+ }
+ }else{
+ delete item.children;
+ }
+ }
+ return item;
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/FileStore.xd.js b/js/dojo-1.6/dojox/data/FileStore.xd.js
new file mode 100644
index 0000000..de126d0
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FileStore.xd.js
@@ -0,0 +1,445 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.FileStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.FileStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FileStore"] = true;
+dojo.provide("dojox.data.FileStore");
+
+
+
+dojo.declare("dojox.data.FileStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // A simple store that provides a datastore interface to a filesystem.
+ // description:
+ // A simple store that provides a datastore interface to a filesystem. It takes a few parameters
+ // for initialization:
+ // url: The URL of the service which provides the file store serverside implementation.
+ // label: The attribute of the file to use as the huma-readable text. Default is 'name'.
+ // The purpose of this store is to represent a file as a datastore item. The
+ // datastore item by default has the following attributes that can be examined on it.
+ // directory: Boolean indicating if the file item represents a directory.
+ // name: The filename with no path informatiom.
+ // path: The file complete file path including name, relative to the location the
+ // file service scans from
+ // size: The size of the file, in bytes.
+ // parentDir: The parent directory path.
+ // children: Any child files contained by a directory file item.
+ //
+ // Note that the store's server call pattern is RESTlike.
+ //
+ // The store also supports the passing of configurable options to the back end service, such as
+ // expanding all child files (no lazy load), displaying hidden files, displaying only directories, and so on.
+ // These are defined through a comma-separated list in declarative, or through setting the options array in programmatic.
+ // example: options="expand,dirsOnly,showHiddenFiles"
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && args.url){
+ this.url = args.url;
+ }
+ if(args && args.options){
+ if(dojo.isArray(args.options)){
+ this.options = args.options;
+ }else{
+ if(dojo.isString(args.options)){
+ this.options = args.options.split(",");
+ }
+ }
+ }
+ if(args && args.pathAsQueryParam){
+ this.pathAsQueryParam = true;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ },
+
+ // url: [public] string
+ // The URL to the file path service.
+ url: "",
+
+ // _storeRef: [private] string
+ // Internal variable used to denote an item came from this store instance.
+ _storeRef: "_S",
+
+ // label: [public] string
+ // Default attribute to use to represent the item as a user-readable
+ // string. Public, so users can change it.
+ label: "name",
+
+ // _identifier: [private] string
+ // Default attribute to use to represent the item's identifier.
+ // Path should always be unique in the store instance.
+ _identifier: "path",
+
+ // _attributes: [private] string
+ // Internal variable of attributes all file items should have.
+ _attributes: ["children", "directory", "name", "path", "modified", "size", "parentDir"], //
+
+ // pathSeparator: [public] string
+ // The path separator to use when chaining requests for children
+ // Can be overriden by the server on initial load
+ pathSeparator: "/",
+
+ // options: [public] array
+ // Array of options to always send when doing requests.
+ // Back end service controls this, like 'dirsOnly', 'showHiddenFiles', 'expandChildren', etc.
+ options: [],
+
+ // failOk: [public] boolean
+ // Flag to pass on to xhr functions to check if we are OK to fail the call silently
+ failOk: false,
+
+ // urlPreventCache: [public] string
+ // Flag to dennote if preventCache should be passed to xhrGet.
+ urlPreventCache: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.FileStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.FileStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ pathAsQueryParam: false, //Function to switch between REST style URL lookups and passing the path to specific items as a query param: 'path'.
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true, 'dojo.data.api.Identity':true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return this._attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ return (attribute in item);
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ return this.getValue(item, this._identifier);
+ },
+
+ getIdentityAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._identifier];
+ },
+
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ var loaded = this.isItem(item);
+ if(loaded && typeof item._loaded == "boolean" && !item._loaded){
+ loaded = false;
+ }
+ return loaded;
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ var item = keywordArgs.item;
+ var self = this;
+ var scope = keywordArgs.scope || dojo.global;
+
+ var content = {};
+
+ if(this.options.length > 0){
+ content.options = dojo.toJson(this.options);
+ }
+
+ if(this.pathAsQueryParam){
+ content.path = item.parentPath + this.pathSeparator + item.name;
+ }
+ var xhrData = {
+ url: this.pathAsQueryParam? this.url : this.url + "/" + item.parentPath + "/" + item.name,
+ handleAs: "json-comment-optional",
+ content: content,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk
+ };
+
+ var deferred = dojo.xhrGet(xhrData);
+ deferred.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+
+ deferred.addCallback(function(data){
+ delete item.parentPath;
+ delete item._loaded;
+ dojo.mixin(item, data);
+ self._processItem(item);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] == value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+
+ var value = item[attribute];
+ if(typeof value !== "undefined" && !dojo.isArray(value)){
+ value = [value];
+ }else if(typeof value === "undefined"){
+ value = [];
+ }
+ return value;
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ fetch: function(request){
+ // summary:
+ // Fetch items that match to a query
+ // request:
+ // A request object
+
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ var self = this;
+ var scope = request.scope || dojo.global;
+
+ //Generate what will be sent over.
+ var reqParams = {};
+ if(request.query){
+ reqParams.query = dojo.toJson(request.query);
+ }
+
+ if(request.sort){
+ reqParams.sort = dojo.toJson(request.sort);
+ }
+
+ if(request.queryOptions){
+ reqParams.queryOptions = dojo.toJson(request.queryOptions);
+ }
+
+ if(typeof request.start == "number"){
+ reqParams.start = "" + request.start;
+ }
+ if(typeof request.count == "number"){
+ reqParams.count = "" + request.count;
+ }
+
+ if(this.options.length > 0){
+ reqParams.options = dojo.toJson(this.options);
+ }
+
+ var getArgs = {
+ url: this.url,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk,
+ handleAs: "json-comment-optional",
+ content: reqParams
+ };
+
+
+ var deferred = dojo.xhrGet(getArgs);
+
+ deferred.addCallback(function(data){self._processResult(data, request);});
+ deferred.addErrback(function(error){
+ if(request.onError){
+ request.onError.call(scope, error, request);
+ }
+ });
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ var path = keywordArgs.identity;
+ var self = this;
+ var scope = keywordArgs.scope || dojo.global;
+
+ var content = {};
+
+ if(this.options.length > 0){
+ content.options = dojo.toJson(this.options);
+ }
+
+ if(this.pathAsQueryParam){
+ content.path = path;
+ }
+ var xhrData = {
+ url: this.pathAsQueryParam? this.url : this.url + "/" + path,
+ handleAs: "json-comment-optional",
+ content: content,
+ preventCache: this.urlPreventCache,
+ failOk: this.failOk
+ };
+
+ var deferred = dojo.xhrGet(xhrData);
+ deferred.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+
+ deferred.addCallback(function(data){
+ var item = self._processItem(data);
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ },
+
+ _processResult: function(data, request){
+ var scope = request.scope || dojo.global;
+ try{
+ //If the data contains a path separator, set ours
+ if(data.pathSeparator){
+ this.pathSeparator = data.pathSeparator;
+ }
+ //Invoke the onBegin handler, if any, to return the
+ //size of the dataset as indicated by the service.
+ if(request.onBegin){
+ request.onBegin.call(scope, data.total, request);
+ }
+ //Now process all the returned items thro
+ var items = this._processItemArray(data.items);
+ if(request.onItem){
+ var i;
+ for(i = 0; i < items.length; i++){
+ request.onItem.call(scope, items[i], request);
+ }
+ items = null;
+ }
+ if(request.onComplete){
+ request.onComplete.call(scope, items, request);
+ }
+ }catch (e){
+ if(request.onError){
+ request.onError.call(scope, e, request);
+ }else{
+ console.log(e);
+ }
+ }
+ },
+
+ _processItemArray: function(itemArray){
+ // summary:
+ // Internal function for processing an array of items for return.
+ var i;
+ for(i = 0; i < itemArray.length; i++){
+ this._processItem(itemArray[i]);
+ }
+ return itemArray;
+ },
+
+ _processItem: function(item){
+ // summary:
+ // Internal function for processing an item returned from the store.
+ // It sets up the store ref as well as sets up the attributes necessary
+ // to invoke a lazy load on a child, if there are any.
+ if(!item){return null;}
+ item[this._storeRef] = this;
+ if(item.children && item.directory){
+ if(dojo.isArray(item.children)){
+ var children = item.children;
+ var i;
+ for(i = 0; i < children.length; i++ ){
+ var name = children[i];
+ if(dojo.isObject(name)){
+ children[i] = this._processItem(name);
+ }else{
+ children[i] = {name: name, _loaded: false, parentPath: item.path};
+ children[i][this._storeRef] = this;
+ }
+ }
+ }else{
+ delete item.children;
+ }
+ }
+ return item;
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/FlickrRestStore.js b/js/dojo-1.6/dojox/data/FlickrRestStore.js
new file mode 100644
index 0000000..190dcb4
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FlickrRestStore.js
@@ -0,0 +1,493 @@
+/*
+ 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["dojox.data.FlickrRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FlickrRestStore"] = true;
+dojo.provide("dojox.data.FlickrRestStore");
+dojo.require("dojox.data.FlickrStore");
+
+
+
+dojo.declare("dojox.data.FlickrRestStore",
+ dojox.data.FlickrStore, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the FlickrRestStore store.
+ // description:
+ // The FlickrRestStore is a Datastore interface to one of the basic services
+ // of the Flickr service, the public photo feed. This does not provide
+ // access to all the services of Flickr.
+ // This store cannot do * and ? filtering as the flickr service
+ // provides no interface for wildcards.
+ if(args){
+ if(args.label){
+ this.label = args.label;
+ }
+ if(args.apikey){
+ this._apikey = args.apikey;
+ }
+ }
+ this._cache = [];
+ this._prevRequests = {};
+ this._handlers = {};
+ this._prevRequestRanges = [];
+ this._maxPhotosPerUser = {};
+ this._id = dojox.data.FlickrRestStore.prototype._id++;
+ },
+
+ // _id: Integer
+ // A unique identifier for this store.
+ _id: 0,
+
+ // _requestCount: Integer
+ // A counter for the number of requests made. This is used to define
+ // the callback function that Flickr will use.
+ _requestCount: 0,
+
+ // _flickrRestUrl: String
+ // The URL to the Flickr REST services.
+ _flickrRestUrl: "http://www.flickr.com/services/rest/",
+
+ // _apikey: String
+ // The users API key to be used when accessing Flickr REST services.
+ _apikey: null,
+
+ // _storeRef: String
+ // A key used to mark an data store item as belonging to this store.
+ _storeRef: "_S",
+
+ // _cache: Array
+ // An Array of all previously downloaded picture info.
+ _cache: null,
+
+ // _prevRequests: Object
+ // A HashMap used to record the signature of a request to prevent duplicate
+ // request being made.
+ _prevRequests: null,
+
+ // _handlers: Object
+ // A HashMap used to record the handlers registered for a single remote request. Multiple
+ // requests may be made for the same information before the first request has finished.
+ // Each element of this Object is an array of handlers to call back when the request finishes.
+ // This prevents multiple requests being made for the same information.
+ _handlers: null,
+
+ // _sortAttributes: Object
+ // A quick lookup of valid attribute names in a sort query.
+ _sortAttributes: {
+ "date-posted": true,
+ "date-taken": true,
+ "interestingness": true
+ },
+
+ _fetchItems: function( /*Object*/ request,
+ /*Function*/ fetchHandler,
+ /*Function*/ errorHandler){
+ // summary: Fetch flickr items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var query = {};
+ if(!request.query){
+ request.query = query = {};
+ } else {
+ dojo.mixin(query, request.query);
+ }
+
+ var primaryKey = [];
+ var secondaryKey = [];
+
+ //Build up the content to send the request for.
+ var content = {
+ format: "json",
+ method: "flickr.photos.search",
+ api_key: this._apikey,
+ extras: "owner_name,date_upload,date_taken"
+ };
+ var isRest = false;
+ if(query.userid){
+ isRest = true;
+ content.user_id = request.query.userid;
+ primaryKey.push("userid"+request.query.userid);
+ }
+
+ if(query.groupid){
+ isRest = true;
+ content.group_id = query.groupid;
+ primaryKey.push("groupid" + query.groupid);
+ }
+
+ if(query.apikey){
+ isRest = true;
+ content.api_key = request.query.apikey;
+ secondaryKey.push("api"+request.query.apikey);
+ }else if(content.api_key){
+ isRest = true;
+ request.query.apikey = content.api_key;
+ secondaryKey.push("api"+content.api_key);
+ }else{
+ throw Error("dojox.data.FlickrRestStore: An API key must be specified.");
+ }
+
+ request._curCount = request.count;
+
+ if(query.page){
+ content.page = request.query.page;
+ secondaryKey.push("page" + content.page);
+ }else if(("start" in request) && request.start !== null){
+ if(!request.count){
+ request.count = 20;
+ }
+ var diff = request.start % request.count;
+ var start = request.start, count = request.count;
+ // If the count does not divide cleanly into the start number,
+ // more work has to be done to figure out the best page to request
+ if(diff !== 0) {
+ if(start < count / 2){
+ // If the first record requested is less than half the
+ // amount requested, then request from 0 to the count record
+ count = start + count;
+ start = 0;
+ }else{
+ var divLimit = 20, div = 2;
+ for(var i = divLimit; i > 0; i--){
+ if(start % i === 0 && (start/i) >= count){
+ div = i;
+ break;
+ }
+ }
+ count = start/div;
+ }
+ request._realStart = request.start;
+ request._realCount = request.count;
+ request._curStart = start;
+ request._curCount = count;
+ }else{
+ request._realStart = request._realCount = null;
+ request._curStart = request.start;
+ request._curCount = request.count;
+ }
+
+ content.page = (start / count) + 1;
+ secondaryKey.push("page" + content.page);
+ }
+
+ if(request._curCount){
+ content.per_page = request._curCount;
+ secondaryKey.push("count" + request._curCount);
+ }
+
+ if(query.lang){
+ content.lang = request.query.lang;
+ primaryKey.push("lang" + request.lang);
+ }
+
+ if(query.setid){
+ content.method = "flickr.photosets.getPhotos";
+ content.photoset_id = request.query.setid;
+ primaryKey.push("set" + request.query.setid);
+ }
+
+ if(query.tags){
+ if(query.tags instanceof Array){
+ content.tags = query.tags.join(",");
+ }else{
+ content.tags = query.tags;
+ }
+ primaryKey.push("tags" + content.tags);
+
+ if(query["tag_mode"] && (query.tag_mode.toLowerCase() === "any" ||
+ query.tag_mode.toLowerCase() === "all")){
+ content.tag_mode = query.tag_mode;
+ }
+ }
+ if(query.text){
+ content.text=query.text;
+ primaryKey.push("text:"+query.text);
+ }
+
+ //The store only supports a single sort attribute, even though the
+ //Read API technically allows multiple sort attributes
+ if(query.sort && query.sort.length > 0){
+ //The default sort attribute is 'date-posted'
+ if(!query.sort[0].attribute){
+ query.sort[0].attribute = "date-posted";
+ }
+
+ //If the sort attribute is valid, check if it is ascending or
+ //descending.
+ if(this._sortAttributes[query.sort[0].attribute]) {
+ if(query.sort[0].descending){
+ content.sort = query.sort[0].attribute + "-desc";
+ }else{
+ content.sort = query.sort[0].attribute + "-asc";
+ }
+ }
+ }else{
+ //The default sort in the Dojo Data API is ascending.
+ content.sort = "date-posted-asc";
+ }
+ primaryKey.push("sort:"+content.sort);
+
+ //Generate a unique key for this request, so the store can
+ //detect duplicate requests.
+ primaryKey = primaryKey.join(".");
+ secondaryKey = secondaryKey.length > 0 ? "." + secondaryKey.join(".") : "";
+ var requestKey = primaryKey + secondaryKey;
+
+ //Make a copy of the request, in case the source object is modified
+ //before the request completes
+ request = {
+ query: query,
+ count: request._curCount,
+ start: request._curStart,
+ _realCount: request._realCount,
+ _realStart: request._realStart,
+ onBegin: request.onBegin,
+ onComplete: request.onComplete,
+ onItem: request.onItem
+ };
+
+ var thisHandler = {
+ request: request,
+ fetchHandler: fetchHandler,
+ errorHandler: errorHandler
+ };
+
+ //If the request has already been made, but not yet completed,
+ //then add the callback handler to the list of handlers
+ //for this request, and finish.
+ if(this._handlers[requestKey]){
+ this._handlers[requestKey].push(thisHandler);
+ return;
+ }
+
+ this._handlers[requestKey] = [thisHandler];
+
+ //Linking this up to Flickr is a PAIN!
+ var handle = null;
+ var getArgs = {
+ url: this._flickrRestUrl,
+ preventCache: this.urlPreventCache,
+ content: content,
+ callbackParamName: "jsoncallback"
+ };
+
+ var doHandle = dojo.hitch(this, function(processedData, data, handler){
+ var onBegin = handler.request.onBegin;
+ handler.request.onBegin = null;
+ var maxPhotos;
+ var req = handler.request;
+
+ if(("_realStart" in req) && req._realStart != null){
+ req.start = req._realStart;
+ req.count = req._realCount;
+ req._realStart = req._realCount = null;
+ }
+
+ //If the request contains an onBegin method, the total number
+ //of photos must be calculated.
+ if(onBegin){
+ var photos = null;
+ if(data){
+ photos = (data.photoset ? data.photoset : data.photos);
+ }
+ if(photos && ("perpage" in photos) && ("pages" in photos)){
+ if(photos.perpage * photos.pages <= handler.request.start + handler.request.count){
+ //If the final page of results has been received, it is possible to
+ //know exactly how many photos there are
+ maxPhotos = handler.request.start + photos.photo.length;
+ }else{
+ //If the final page of results has not yet been received,
+ //it is not possible to tell exactly how many photos exist, so
+ //return the number of pages multiplied by the number of photos per page.
+ maxPhotos = photos.perpage * photos.pages;
+ }
+ this._maxPhotosPerUser[primaryKey] = maxPhotos;
+ onBegin(maxPhotos, handler.request);
+ }else if(this._maxPhotosPerUser[primaryKey]){
+ onBegin(this._maxPhotosPerUser[primaryKey], handler.request);
+ }
+ }
+ //Call whatever functions the caller has defined on the request object, except for onBegin
+ handler.fetchHandler(processedData, handler.request);
+ if(onBegin){
+ //Replace the onBegin function, if it existed.
+ handler.request.onBegin = onBegin;
+ }
+ });
+
+ //Define a callback for the script that iterates through a list of
+ //handlers for this piece of data. Multiple requests can come into
+ //the store for the same data.
+ var myHandler = dojo.hitch(this, function(data){
+ //The handler should not be called more than once, so disconnect it.
+ //if(handle !== null){ dojo.disconnect(handle); }
+ if(data.stat != "ok"){
+ errorHandler(null, request);
+ }else{ //Process the items...
+ var handlers = this._handlers[requestKey];
+ if(!handlers){
+ console.log("FlickrRestStore: no handlers for data", data);
+ return;
+ }
+
+ this._handlers[requestKey] = null;
+ this._prevRequests[requestKey] = data;
+
+ //Process the data once.
+ var processedData = this._processFlickrData(data, request, primaryKey);
+ if(!this._prevRequestRanges[primaryKey]){
+ this._prevRequestRanges[primaryKey] = [];
+ }
+ this._prevRequestRanges[primaryKey].push({
+ start: request.start,
+ end: request.start + (data.photoset ? data.photoset.photo.length : data.photos.photo.length)
+ });
+
+ //Iterate through the array of handlers, calling each one.
+ dojo.forEach(handlers, function(i){
+ doHandle(processedData, data, i);
+ });
+ }
+ });
+
+ var data = this._prevRequests[requestKey];
+
+ //If the data was previously retrieved, there is no need to fetch it again.
+ if(data){
+ this._handlers[requestKey] = null;
+ doHandle(this._cache[primaryKey], data, thisHandler);
+ return;
+ }else if(this._checkPrevRanges(primaryKey, request.start, request.count)){
+ //If this range of data has already been retrieved, reuse it.
+ this._handlers[requestKey] = null;
+ doHandle(this._cache[primaryKey], null, thisHandler);
+ return;
+ }
+
+ var deferred = dojo.io.script.get(getArgs);
+ deferred.addCallback(myHandler);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonFlickrFeed function....
+ deferred.addErrback(function(error){
+ dojo.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [
+ "title", "author", "imageUrl", "imageUrlSmall", "imageUrlMedium",
+ "imageUrlThumb", "imageUrlLarge", "imageUrlOriginal", "link", "dateTaken", "datePublished"
+ ];
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+
+ switch(attribute){
+ case "title":
+ return [ this._unescapeHtml(item.title) ]; // String
+ case "author":
+ return [ item.ownername ]; // String
+ case "imageUrlSmall":
+ return [ item.media.s ]; // String
+ case "imageUrl":
+ return [ item.media.l ]; // String
+ case "imageUrlOriginal":
+ return [ item.media.o ]; // String
+ case "imageUrlLarge":
+ return [ item.media.l ]; // String
+ case "imageUrlMedium":
+ return [ item.media.m ]; // String
+ case "imageUrlThumb":
+ return [ item.media.t ]; // String
+ case "link":
+ return [ "http://www.flickr.com/photos/" + item.owner + "/" + item.id ]; // String
+ case "dateTaken":
+ return [ item.datetaken ];
+ case "datePublished":
+ return [ item.datepublished ];
+ default:
+ return undefined;
+ }
+
+ },
+
+ _processFlickrData: function(/* Object */data, /* Object */request, /* String */ cacheKey){
+ // summary: Processes the raw data from Flickr and updates the internal cache.
+ // data:
+ // Data returned from Flickr
+ // request:
+ // The original dojo.data.Request object passed in by the user.
+
+ // If the data contains an 'item' object, it has not come from the REST
+ // services, so process it using the FlickrStore.
+ if(data.items){
+ return dojox.data.FlickrStore.prototype._processFlickrData.apply(this,arguments);
+ }
+ var template = ["http://farm", null, ".static.flickr.com/", null, "/", null, "_", null];
+
+ var items = [];
+ var photos = (data.photoset ? data.photoset : data.photos);
+ if(data.stat == "ok" && photos && photos.photo){
+ items = photos.photo;
+
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < items.length; i++){
+ var item = items[i];
+ item[this._storeRef] = this;
+ template[1] = item.farm;
+ template[3] = item.server;
+ template[5] = item.id;
+ template[7] = item.secret;
+
+ var base = template.join("");
+ item.media = {
+ s: base + "_s.jpg",
+ m: base + "_m.jpg",
+ l: base + ".jpg",
+ t: base + "_t.jpg",
+ o: base + "_o.jpg"
+ };
+ if(!item.owner && data.photoset){
+ item.owner = data.photoset.owner;
+ }
+ }
+ }
+ var start = request.start ? request.start : 0;
+ var arr = this._cache[cacheKey];
+ if(!arr){
+ this._cache[cacheKey] = arr = [];
+ }
+ dojo.forEach(items, function(i, idx){
+ arr[idx+ start] = i;
+ });
+
+ return arr; // Array
+ },
+
+ _checkPrevRanges: function(primaryKey, start, count){
+ var end = start + count;
+ var arr = this._prevRequestRanges[primaryKey];
+ return (!!arr) && dojo.some(arr, function(item){
+ return ((start >= item.start)&&(end <= item.end));
+ });
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/FlickrRestStore.xd.js b/js/dojo-1.6/dojox/data/FlickrRestStore.xd.js
new file mode 100644
index 0000000..917255b
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FlickrRestStore.xd.js
@@ -0,0 +1,498 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.FlickrRestStore"],
+["require", "dojox.data.FlickrStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.FlickrRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FlickrRestStore"] = true;
+dojo.provide("dojox.data.FlickrRestStore");
+dojo.require("dojox.data.FlickrStore");
+
+
+
+dojo.declare("dojox.data.FlickrRestStore",
+ dojox.data.FlickrStore, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the FlickrRestStore store.
+ // description:
+ // The FlickrRestStore is a Datastore interface to one of the basic services
+ // of the Flickr service, the public photo feed. This does not provide
+ // access to all the services of Flickr.
+ // This store cannot do * and ? filtering as the flickr service
+ // provides no interface for wildcards.
+ if(args){
+ if(args.label){
+ this.label = args.label;
+ }
+ if(args.apikey){
+ this._apikey = args.apikey;
+ }
+ }
+ this._cache = [];
+ this._prevRequests = {};
+ this._handlers = {};
+ this._prevRequestRanges = [];
+ this._maxPhotosPerUser = {};
+ this._id = dojox.data.FlickrRestStore.prototype._id++;
+ },
+
+ // _id: Integer
+ // A unique identifier for this store.
+ _id: 0,
+
+ // _requestCount: Integer
+ // A counter for the number of requests made. This is used to define
+ // the callback function that Flickr will use.
+ _requestCount: 0,
+
+ // _flickrRestUrl: String
+ // The URL to the Flickr REST services.
+ _flickrRestUrl: "http://www.flickr.com/services/rest/",
+
+ // _apikey: String
+ // The users API key to be used when accessing Flickr REST services.
+ _apikey: null,
+
+ // _storeRef: String
+ // A key used to mark an data store item as belonging to this store.
+ _storeRef: "_S",
+
+ // _cache: Array
+ // An Array of all previously downloaded picture info.
+ _cache: null,
+
+ // _prevRequests: Object
+ // A HashMap used to record the signature of a request to prevent duplicate
+ // request being made.
+ _prevRequests: null,
+
+ // _handlers: Object
+ // A HashMap used to record the handlers registered for a single remote request. Multiple
+ // requests may be made for the same information before the first request has finished.
+ // Each element of this Object is an array of handlers to call back when the request finishes.
+ // This prevents multiple requests being made for the same information.
+ _handlers: null,
+
+ // _sortAttributes: Object
+ // A quick lookup of valid attribute names in a sort query.
+ _sortAttributes: {
+ "date-posted": true,
+ "date-taken": true,
+ "interestingness": true
+ },
+
+ _fetchItems: function( /*Object*/ request,
+ /*Function*/ fetchHandler,
+ /*Function*/ errorHandler){
+ // summary: Fetch flickr items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var query = {};
+ if(!request.query){
+ request.query = query = {};
+ } else {
+ dojo.mixin(query, request.query);
+ }
+
+ var primaryKey = [];
+ var secondaryKey = [];
+
+ //Build up the content to send the request for.
+ var content = {
+ format: "json",
+ method: "flickr.photos.search",
+ api_key: this._apikey,
+ extras: "owner_name,date_upload,date_taken"
+ };
+ var isRest = false;
+ if(query.userid){
+ isRest = true;
+ content.user_id = request.query.userid;
+ primaryKey.push("userid"+request.query.userid);
+ }
+
+ if(query.groupid){
+ isRest = true;
+ content.group_id = query.groupid;
+ primaryKey.push("groupid" + query.groupid);
+ }
+
+ if(query.apikey){
+ isRest = true;
+ content.api_key = request.query.apikey;
+ secondaryKey.push("api"+request.query.apikey);
+ }else if(content.api_key){
+ isRest = true;
+ request.query.apikey = content.api_key;
+ secondaryKey.push("api"+content.api_key);
+ }else{
+ throw Error("dojox.data.FlickrRestStore: An API key must be specified.");
+ }
+
+ request._curCount = request.count;
+
+ if(query.page){
+ content.page = request.query.page;
+ secondaryKey.push("page" + content.page);
+ }else if(("start" in request) && request.start !== null){
+ if(!request.count){
+ request.count = 20;
+ }
+ var diff = request.start % request.count;
+ var start = request.start, count = request.count;
+ // If the count does not divide cleanly into the start number,
+ // more work has to be done to figure out the best page to request
+ if(diff !== 0) {
+ if(start < count / 2){
+ // If the first record requested is less than half the
+ // amount requested, then request from 0 to the count record
+ count = start + count;
+ start = 0;
+ }else{
+ var divLimit = 20, div = 2;
+ for(var i = divLimit; i > 0; i--){
+ if(start % i === 0 && (start/i) >= count){
+ div = i;
+ break;
+ }
+ }
+ count = start/div;
+ }
+ request._realStart = request.start;
+ request._realCount = request.count;
+ request._curStart = start;
+ request._curCount = count;
+ }else{
+ request._realStart = request._realCount = null;
+ request._curStart = request.start;
+ request._curCount = request.count;
+ }
+
+ content.page = (start / count) + 1;
+ secondaryKey.push("page" + content.page);
+ }
+
+ if(request._curCount){
+ content.per_page = request._curCount;
+ secondaryKey.push("count" + request._curCount);
+ }
+
+ if(query.lang){
+ content.lang = request.query.lang;
+ primaryKey.push("lang" + request.lang);
+ }
+
+ if(query.setid){
+ content.method = "flickr.photosets.getPhotos";
+ content.photoset_id = request.query.setid;
+ primaryKey.push("set" + request.query.setid);
+ }
+
+ if(query.tags){
+ if(query.tags instanceof Array){
+ content.tags = query.tags.join(",");
+ }else{
+ content.tags = query.tags;
+ }
+ primaryKey.push("tags" + content.tags);
+
+ if(query["tag_mode"] && (query.tag_mode.toLowerCase() === "any" ||
+ query.tag_mode.toLowerCase() === "all")){
+ content.tag_mode = query.tag_mode;
+ }
+ }
+ if(query.text){
+ content.text=query.text;
+ primaryKey.push("text:"+query.text);
+ }
+
+ //The store only supports a single sort attribute, even though the
+ //Read API technically allows multiple sort attributes
+ if(query.sort && query.sort.length > 0){
+ //The default sort attribute is 'date-posted'
+ if(!query.sort[0].attribute){
+ query.sort[0].attribute = "date-posted";
+ }
+
+ //If the sort attribute is valid, check if it is ascending or
+ //descending.
+ if(this._sortAttributes[query.sort[0].attribute]) {
+ if(query.sort[0].descending){
+ content.sort = query.sort[0].attribute + "-desc";
+ }else{
+ content.sort = query.sort[0].attribute + "-asc";
+ }
+ }
+ }else{
+ //The default sort in the Dojo Data API is ascending.
+ content.sort = "date-posted-asc";
+ }
+ primaryKey.push("sort:"+content.sort);
+
+ //Generate a unique key for this request, so the store can
+ //detect duplicate requests.
+ primaryKey = primaryKey.join(".");
+ secondaryKey = secondaryKey.length > 0 ? "." + secondaryKey.join(".") : "";
+ var requestKey = primaryKey + secondaryKey;
+
+ //Make a copy of the request, in case the source object is modified
+ //before the request completes
+ request = {
+ query: query,
+ count: request._curCount,
+ start: request._curStart,
+ _realCount: request._realCount,
+ _realStart: request._realStart,
+ onBegin: request.onBegin,
+ onComplete: request.onComplete,
+ onItem: request.onItem
+ };
+
+ var thisHandler = {
+ request: request,
+ fetchHandler: fetchHandler,
+ errorHandler: errorHandler
+ };
+
+ //If the request has already been made, but not yet completed,
+ //then add the callback handler to the list of handlers
+ //for this request, and finish.
+ if(this._handlers[requestKey]){
+ this._handlers[requestKey].push(thisHandler);
+ return;
+ }
+
+ this._handlers[requestKey] = [thisHandler];
+
+ //Linking this up to Flickr is a PAIN!
+ var handle = null;
+ var getArgs = {
+ url: this._flickrRestUrl,
+ preventCache: this.urlPreventCache,
+ content: content,
+ callbackParamName: "jsoncallback"
+ };
+
+ var doHandle = dojo.hitch(this, function(processedData, data, handler){
+ var onBegin = handler.request.onBegin;
+ handler.request.onBegin = null;
+ var maxPhotos;
+ var req = handler.request;
+
+ if(("_realStart" in req) && req._realStart != null){
+ req.start = req._realStart;
+ req.count = req._realCount;
+ req._realStart = req._realCount = null;
+ }
+
+ //If the request contains an onBegin method, the total number
+ //of photos must be calculated.
+ if(onBegin){
+ var photos = null;
+ if(data){
+ photos = (data.photoset ? data.photoset : data.photos);
+ }
+ if(photos && ("perpage" in photos) && ("pages" in photos)){
+ if(photos.perpage * photos.pages <= handler.request.start + handler.request.count){
+ //If the final page of results has been received, it is possible to
+ //know exactly how many photos there are
+ maxPhotos = handler.request.start + photos.photo.length;
+ }else{
+ //If the final page of results has not yet been received,
+ //it is not possible to tell exactly how many photos exist, so
+ //return the number of pages multiplied by the number of photos per page.
+ maxPhotos = photos.perpage * photos.pages;
+ }
+ this._maxPhotosPerUser[primaryKey] = maxPhotos;
+ onBegin(maxPhotos, handler.request);
+ }else if(this._maxPhotosPerUser[primaryKey]){
+ onBegin(this._maxPhotosPerUser[primaryKey], handler.request);
+ }
+ }
+ //Call whatever functions the caller has defined on the request object, except for onBegin
+ handler.fetchHandler(processedData, handler.request);
+ if(onBegin){
+ //Replace the onBegin function, if it existed.
+ handler.request.onBegin = onBegin;
+ }
+ });
+
+ //Define a callback for the script that iterates through a list of
+ //handlers for this piece of data. Multiple requests can come into
+ //the store for the same data.
+ var myHandler = dojo.hitch(this, function(data){
+ //The handler should not be called more than once, so disconnect it.
+ //if(handle !== null){ dojo.disconnect(handle); }
+ if(data.stat != "ok"){
+ errorHandler(null, request);
+ }else{ //Process the items...
+ var handlers = this._handlers[requestKey];
+ if(!handlers){
+ console.log("FlickrRestStore: no handlers for data", data);
+ return;
+ }
+
+ this._handlers[requestKey] = null;
+ this._prevRequests[requestKey] = data;
+
+ //Process the data once.
+ var processedData = this._processFlickrData(data, request, primaryKey);
+ if(!this._prevRequestRanges[primaryKey]){
+ this._prevRequestRanges[primaryKey] = [];
+ }
+ this._prevRequestRanges[primaryKey].push({
+ start: request.start,
+ end: request.start + (data.photoset ? data.photoset.photo.length : data.photos.photo.length)
+ });
+
+ //Iterate through the array of handlers, calling each one.
+ dojo.forEach(handlers, function(i){
+ doHandle(processedData, data, i);
+ });
+ }
+ });
+
+ var data = this._prevRequests[requestKey];
+
+ //If the data was previously retrieved, there is no need to fetch it again.
+ if(data){
+ this._handlers[requestKey] = null;
+ doHandle(this._cache[primaryKey], data, thisHandler);
+ return;
+ }else if(this._checkPrevRanges(primaryKey, request.start, request.count)){
+ //If this range of data has already been retrieved, reuse it.
+ this._handlers[requestKey] = null;
+ doHandle(this._cache[primaryKey], null, thisHandler);
+ return;
+ }
+
+ var deferred = dojo.io.script.get(getArgs);
+ deferred.addCallback(myHandler);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonFlickrFeed function....
+ deferred.addErrback(function(error){
+ dojo.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [
+ "title", "author", "imageUrl", "imageUrlSmall", "imageUrlMedium",
+ "imageUrlThumb", "imageUrlLarge", "imageUrlOriginal", "link", "dateTaken", "datePublished"
+ ];
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+
+ switch(attribute){
+ case "title":
+ return [ this._unescapeHtml(item.title) ]; // String
+ case "author":
+ return [ item.ownername ]; // String
+ case "imageUrlSmall":
+ return [ item.media.s ]; // String
+ case "imageUrl":
+ return [ item.media.l ]; // String
+ case "imageUrlOriginal":
+ return [ item.media.o ]; // String
+ case "imageUrlLarge":
+ return [ item.media.l ]; // String
+ case "imageUrlMedium":
+ return [ item.media.m ]; // String
+ case "imageUrlThumb":
+ return [ item.media.t ]; // String
+ case "link":
+ return [ "http://www.flickr.com/photos/" + item.owner + "/" + item.id ]; // String
+ case "dateTaken":
+ return [ item.datetaken ];
+ case "datePublished":
+ return [ item.datepublished ];
+ default:
+ return undefined;
+ }
+
+ },
+
+ _processFlickrData: function(/* Object */data, /* Object */request, /* String */ cacheKey){
+ // summary: Processes the raw data from Flickr and updates the internal cache.
+ // data:
+ // Data returned from Flickr
+ // request:
+ // The original dojo.data.Request object passed in by the user.
+
+ // If the data contains an 'item' object, it has not come from the REST
+ // services, so process it using the FlickrStore.
+ if(data.items){
+ return dojox.data.FlickrStore.prototype._processFlickrData.apply(this,arguments);
+ }
+ var template = ["http://farm", null, ".static.flickr.com/", null, "/", null, "_", null];
+
+ var items = [];
+ var photos = (data.photoset ? data.photoset : data.photos);
+ if(data.stat == "ok" && photos && photos.photo){
+ items = photos.photo;
+
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < items.length; i++){
+ var item = items[i];
+ item[this._storeRef] = this;
+ template[1] = item.farm;
+ template[3] = item.server;
+ template[5] = item.id;
+ template[7] = item.secret;
+
+ var base = template.join("");
+ item.media = {
+ s: base + "_s.jpg",
+ m: base + "_m.jpg",
+ l: base + ".jpg",
+ t: base + "_t.jpg",
+ o: base + "_o.jpg"
+ };
+ if(!item.owner && data.photoset){
+ item.owner = data.photoset.owner;
+ }
+ }
+ }
+ var start = request.start ? request.start : 0;
+ var arr = this._cache[cacheKey];
+ if(!arr){
+ this._cache[cacheKey] = arr = [];
+ }
+ dojo.forEach(items, function(i, idx){
+ arr[idx+ start] = i;
+ });
+
+ return arr; // Array
+ },
+
+ _checkPrevRanges: function(primaryKey, start, count){
+ var end = start + count;
+ var arr = this._prevRequestRanges[primaryKey];
+ return (!!arr) && dojo.some(arr, function(item){
+ return ((start >= item.start)&&(end <= item.end));
+ });
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/FlickrStore.js b/js/dojo-1.6/dojox/data/FlickrStore.js
new file mode 100644
index 0000000..b96c8e7
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FlickrStore.js
@@ -0,0 +1,296 @@
+/*
+ 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["dojox.data.FlickrStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FlickrStore"] = true;
+dojo.provide("dojox.data.FlickrStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.io.script");
+dojo.require("dojo.date.stamp");
+dojo.require("dojo.AdapterRegistry");
+
+
+
+dojo.declare("dojox.data.FlickrStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the FlickrStore store.
+ // description:
+ // The FlickrStore is a Datastore interface to one of the basic services
+ // of the Flickr service, the public photo feed. This does not provide
+ // access to all the services of Flickr.
+ // This store cannot do * and ? filtering as the flickr service
+ // provides no interface for wildcards.
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ },
+
+ _storeRef: "_S",
+
+ label: "title",
+
+ //Flag to allor control of if cache prevention is enabled or not.
+ urlPreventCache: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.FlickrStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.FlickrStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [
+ "title", "description", "author", "datePublished", "dateTaken",
+ "imageUrl", "imageUrlSmall", "imageUrlMedium", "tags", "link"
+ ];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ var v = this.getValue(item,attribute);
+ if(v || v === "" || v === false){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var u = d.hitch(this, "_unescapeHtml");
+ var s = d.hitch(d.date.stamp, "fromISOString");
+ switch(attribute){
+ case "title":
+ return [ u(item.title) ];
+ case "author":
+ return [ u(item.author) ];
+ case "datePublished":
+ return [ s(item.published) ];
+ case "dateTaken":
+ return [ s(item.date_taken) ];
+ case "imageUrlSmall":
+ return [ item.media.m.replace(/_m\./, "_s.") ];
+ case "imageUrl":
+ return [ item.media.m.replace(/_m\./, ".") ];
+ case "imageUrlMedium":
+ return [ item.media.m ];
+ case "link":
+ return [ item.link ];
+ case "tags":
+ return item.tags.split(" ");
+ case "description":
+ return [ u(item.description) ];
+ default:
+ return [];
+ }
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch flickr items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ var rq = request.query = request.query || {};
+
+ //Build up the content to send the request for.
+ var content = {
+ format: "json",
+ tagmode:"any"
+ };
+
+ d.forEach(
+ [ "tags", "tagmode", "lang", "id", "ids" ],
+ function(i){
+ if(rq[i]){ content[i] = rq[i]; }
+ }
+ );
+
+ content.id = rq.id || rq.userid || rq.groupid;
+
+ if(rq.userids){
+ content.ids = rq.userids;
+ }
+
+ //Linking this up to Flickr is a PAIN!
+ var handle = null;
+ var getArgs = {
+ url: dojox.data.FlickrStore.urlRegistry.match(request),
+ preventCache: this.urlPreventCache,
+ content: content
+ };
+ var myHandler = d.hitch(this, function(data){
+ if(!!handle){
+ d.disconnect(handle);
+ }
+
+ //Process the items...
+ fetchHandler(this._processFlickrData(data), request);
+ });
+ handle = d.connect("jsonFlickrFeed", myHandler);
+ var deferred = d.io.script.get(getArgs);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonFlickrFeed function....
+ deferred.addErrback(function(error){
+ d.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ _processFlickrData: function(data){
+ var items = [];
+ if(data.items){
+ items = data.items;
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < data.items.length; i++){
+ var item = data.items[i];
+ item[this._storeRef] = this;
+ }
+ }
+ return items;
+ },
+
+ _unescapeHtml: function(/*String*/ str){
+ // summary:
+ // Utility function to un-escape XML special characters in an
+ // HTML string.
+ // str: String.
+ // The string to un-escape
+ // returns:
+ // HTML String converted back to the normal text (unescaped)
+ // characters (<,>,&, ", etc,).
+
+ //TODO:
+ // Check to see if theres already compatible escape() in
+ // dojo.string or dojo.html
+ return str.replace(/&amp;/gm, "&").
+ replace(/&lt;/gm, "<").
+ replace(/&gt;/gm, ">").
+ replace(/&quot;/gm, "\"").
+ replace(/&#39;/gm, "'");
+ }
+});
+
+dojo.extend(dojox.data.FlickrStore, dojo.data.util.simpleFetch);
+
+var feedsUrl = "http://api.flickr.com/services/feeds/";
+
+var reg = dojox.data.FlickrStore.urlRegistry = new d.AdapterRegistry(true);
+
+reg.register("group pool",
+ function(request){ return !!request.query["groupid"]; },
+ feedsUrl+"groups_pool.gne"
+);
+
+reg.register("default",
+ function(request){ return true; },
+ feedsUrl+"photos_public.gne"
+);
+
+//We have to define this because of how the Flickr API works.
+//This somewhat stinks, but what can you do?
+if(!jsonFlickrFeed){
+ var jsonFlickrFeed = function(data){};
+}
+
+}
diff --git a/js/dojo-1.6/dojox/data/FlickrStore.xd.js b/js/dojo-1.6/dojox/data/FlickrStore.xd.js
new file mode 100644
index 0000000..0b0be94
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/FlickrStore.xd.js
@@ -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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.FlickrStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.io.script"],
+["require", "dojo.date.stamp"],
+["require", "dojo.AdapterRegistry"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.FlickrStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.FlickrStore"] = true;
+dojo.provide("dojox.data.FlickrStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.io.script");
+dojo.require("dojo.date.stamp");
+dojo.require("dojo.AdapterRegistry");
+
+
+
+dojo.declare("dojox.data.FlickrStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the FlickrStore store.
+ // description:
+ // The FlickrStore is a Datastore interface to one of the basic services
+ // of the Flickr service, the public photo feed. This does not provide
+ // access to all the services of Flickr.
+ // This store cannot do * and ? filtering as the flickr service
+ // provides no interface for wildcards.
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ },
+
+ _storeRef: "_S",
+
+ label: "title",
+
+ //Flag to allor control of if cache prevention is enabled or not.
+ urlPreventCache: true,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.FlickrStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.FlickrStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [
+ "title", "description", "author", "datePublished", "dateTaken",
+ "imageUrl", "imageUrlSmall", "imageUrlMedium", "tags", "link"
+ ];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ var v = this.getValue(item,attribute);
+ if(v || v === "" || v === false){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var u = d.hitch(this, "_unescapeHtml");
+ var s = d.hitch(d.date.stamp, "fromISOString");
+ switch(attribute){
+ case "title":
+ return [ u(item.title) ];
+ case "author":
+ return [ u(item.author) ];
+ case "datePublished":
+ return [ s(item.published) ];
+ case "dateTaken":
+ return [ s(item.date_taken) ];
+ case "imageUrlSmall":
+ return [ item.media.m.replace(/_m\./, "_s.") ];
+ case "imageUrl":
+ return [ item.media.m.replace(/_m\./, ".") ];
+ case "imageUrlMedium":
+ return [ item.media.m ];
+ case "link":
+ return [ item.link ];
+ case "tags":
+ return item.tags.split(" ");
+ case "description":
+ return [ u(item.description) ];
+ default:
+ return [];
+ }
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch flickr items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ var rq = request.query = request.query || {};
+
+ //Build up the content to send the request for.
+ var content = {
+ format: "json",
+ tagmode:"any"
+ };
+
+ d.forEach(
+ [ "tags", "tagmode", "lang", "id", "ids" ],
+ function(i){
+ if(rq[i]){ content[i] = rq[i]; }
+ }
+ );
+
+ content.id = rq.id || rq.userid || rq.groupid;
+
+ if(rq.userids){
+ content.ids = rq.userids;
+ }
+
+ //Linking this up to Flickr is a PAIN!
+ var handle = null;
+ var getArgs = {
+ url: dojox.data.FlickrStore.urlRegistry.match(request),
+ preventCache: this.urlPreventCache,
+ content: content
+ };
+ var myHandler = d.hitch(this, function(data){
+ if(!!handle){
+ d.disconnect(handle);
+ }
+
+ //Process the items...
+ fetchHandler(this._processFlickrData(data), request);
+ });
+ handle = d.connect("jsonFlickrFeed", myHandler);
+ var deferred = d.io.script.get(getArgs);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonFlickrFeed function....
+ deferred.addErrback(function(error){
+ d.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ _processFlickrData: function(data){
+ var items = [];
+ if(data.items){
+ items = data.items;
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < data.items.length; i++){
+ var item = data.items[i];
+ item[this._storeRef] = this;
+ }
+ }
+ return items;
+ },
+
+ _unescapeHtml: function(/*String*/ str){
+ // summary:
+ // Utility function to un-escape XML special characters in an
+ // HTML string.
+ // str: String.
+ // The string to un-escape
+ // returns:
+ // HTML String converted back to the normal text (unescaped)
+ // characters (<,>,&, ", etc,).
+
+ //TODO:
+ // Check to see if theres already compatible escape() in
+ // dojo.string or dojo.html
+ return str.replace(/&amp;/gm, "&").
+ replace(/&lt;/gm, "<").
+ replace(/&gt;/gm, ">").
+ replace(/&quot;/gm, "\"").
+ replace(/&#39;/gm, "'");
+ }
+});
+
+dojo.extend(dojox.data.FlickrStore, dojo.data.util.simpleFetch);
+
+var feedsUrl = "http://api.flickr.com/services/feeds/";
+
+var reg = dojox.data.FlickrStore.urlRegistry = new d.AdapterRegistry(true);
+
+reg.register("group pool",
+ function(request){ return !!request.query["groupid"]; },
+ feedsUrl+"groups_pool.gne"
+);
+
+reg.register("default",
+ function(request){ return true; },
+ feedsUrl+"photos_public.gne"
+);
+
+//We have to define this because of how the Flickr API works.
+//This somewhat stinks, but what can you do?
+if(!jsonFlickrFeed){
+ var jsonFlickrFeed = function(data){};
+}
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/GoogleFeedStore.js b/js/dojo-1.6/dojox/data/GoogleFeedStore.js
new file mode 100644
index 0000000..b394ece
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/GoogleFeedStore.js
@@ -0,0 +1,88 @@
+/*
+ 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["dojox.data.GoogleFeedStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.GoogleFeedStore"] = true;
+dojo.provide("dojox.data.GoogleFeedStore");
+dojo.require("dojox.data.GoogleSearchStore");
+
+
+
+dojo.experimental("dojox.data.GoogleFeedStore");
+
+dojo.declare("dojox.data.GoogleFeedStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving RSS and Atom feeds from Google. The
+ // feeds can come from any source, which is specified in the "url"
+ // parameter of the query passed to the "fetch" function.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The feed entry title.</li>
+ // <li>link - The URL for the HTML version of the feed entry.</li>
+ // <li>content - The full content of the blog post, in HTML format</li>
+ // <li>summary - A snippet of information about the feed entry, in plain text</li>
+ // <li>published - The string date on which the entry was published.
+ // You can parse the date with new Date(store.getValue(item, "published")</li>
+ // <li>categories - An array of string tags for the entry</li>
+ // </ul>
+ // The query accepts one parameter: url - The URL of the feed to retrieve
+ _type: "",
+ _googleUrl: "http://ajax.googleapis.com/ajax/services/feed/load",
+ _attributes: ["title", "link", "author", "published",
+ "content", "summary", "categories"],
+ _queryAttrs: {
+ "url":"q"
+ },
+
+ getFeedValue: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ var values = this.getFeedValues(attribute, defaultValue);
+ if(dojo.isArray(values)){
+ return values[0];
+ }
+ return values;
+ },
+
+ getFeedValues: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ if(!this._feedMetaData){
+ return defaultValue;
+ }
+ return this._feedMetaData[attribute] || defaultValue;
+ },
+
+ _processItem: function(item, request) {
+ this.inherited(arguments);
+ item["summary"] = item["contentSnippet"];
+ item["published"] = item["publishedDate"];
+ },
+
+ _getItems: function(data){
+ if(data['feed']){
+ this._feedMetaData = {
+ title: data.feed.title,
+ desc: data.feed.description,
+ url: data.feed.link,
+ author: data.feed.author
+ };
+ return data.feed.entries;
+ }
+ return null;
+ },
+
+ _createContent: function(query, callback, request){
+ var cb = this.inherited(arguments);
+ cb.num = (request.count || 10) + (request.start || 0);
+ return cb;
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/GoogleFeedStore.xd.js b/js/dojo-1.6/dojox/data/GoogleFeedStore.xd.js
new file mode 100644
index 0000000..dba864e
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/GoogleFeedStore.xd.js
@@ -0,0 +1,93 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.GoogleFeedStore"],
+["require", "dojox.data.GoogleSearchStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.GoogleFeedStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.GoogleFeedStore"] = true;
+dojo.provide("dojox.data.GoogleFeedStore");
+dojo.require("dojox.data.GoogleSearchStore");
+
+
+
+dojo.experimental("dojox.data.GoogleFeedStore");
+
+dojo.declare("dojox.data.GoogleFeedStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving RSS and Atom feeds from Google. The
+ // feeds can come from any source, which is specified in the "url"
+ // parameter of the query passed to the "fetch" function.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The feed entry title.</li>
+ // <li>link - The URL for the HTML version of the feed entry.</li>
+ // <li>content - The full content of the blog post, in HTML format</li>
+ // <li>summary - A snippet of information about the feed entry, in plain text</li>
+ // <li>published - The string date on which the entry was published.
+ // You can parse the date with new Date(store.getValue(item, "published")</li>
+ // <li>categories - An array of string tags for the entry</li>
+ // </ul>
+ // The query accepts one parameter: url - The URL of the feed to retrieve
+ _type: "",
+ _googleUrl: "http://ajax.googleapis.com/ajax/services/feed/load",
+ _attributes: ["title", "link", "author", "published",
+ "content", "summary", "categories"],
+ _queryAttrs: {
+ "url":"q"
+ },
+
+ getFeedValue: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ var values = this.getFeedValues(attribute, defaultValue);
+ if(dojo.isArray(values)){
+ return values[0];
+ }
+ return values;
+ },
+
+ getFeedValues: function(attribute, defaultValue){
+ // summary:
+ // Non-API method for retrieving values regarding the Atom feed,
+ // rather than the Atom entries.
+ if(!this._feedMetaData){
+ return defaultValue;
+ }
+ return this._feedMetaData[attribute] || defaultValue;
+ },
+
+ _processItem: function(item, request) {
+ this.inherited(arguments);
+ item["summary"] = item["contentSnippet"];
+ item["published"] = item["publishedDate"];
+ },
+
+ _getItems: function(data){
+ if(data['feed']){
+ this._feedMetaData = {
+ title: data.feed.title,
+ desc: data.feed.description,
+ url: data.feed.link,
+ author: data.feed.author
+ };
+ return data.feed.entries;
+ }
+ return null;
+ },
+
+ _createContent: function(query, callback, request){
+ var cb = this.inherited(arguments);
+ cb.num = (request.count || 10) + (request.start || 0);
+ return cb;
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/GoogleSearchStore.js b/js/dojo-1.6/dojox/data/GoogleSearchStore.js
new file mode 100644
index 0000000..53d52a8
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/GoogleSearchStore.js
@@ -0,0 +1,664 @@
+/*
+ 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["dojox.data.GoogleSearchStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.GoogleSearchStore"] = true;
+dojo.provide("dojox.data.GoogleSearchStore");
+dojo.require("dojo.io.script");
+
+
+
+dojo.provide("dojox.data.GoogleWebSearchStore");
+dojo.provide("dojox.data.GoogleBlogSearchStore");
+dojo.provide("dojox.data.GoogleLocalSearchStore");
+dojo.provide("dojox.data.GoogleVideoSearchStore");
+dojo.provide("dojox.data.GoogleNewsSearchStore");
+dojo.provide("dojox.data.GoogleBookSearchStore");
+dojo.provide("dojox.data.GoogleImageSearchStore");
+
+dojo.experimental("dojox.data.GoogleSearchStore");
+
+dojo.declare("dojox.data.GoogleSearchStore",null,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // This data store acts as a base class for Google searches,
+ // and has a number of child data stores that implement different
+ // searches. This store defaults to searching the web, and is functionally
+ // identical to the dojox.data.GoogleWebSearchStore object.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>visibleUrl - The URL with no protocol specified.
+ // <li>cacheUrl - The URL to the copy of the document cached by Google
+ // <li>title - The page title in HTML format.</li>
+ // <li>titleNoFormatting - The page title in plain text</li>
+ // <li>content - A snippet of information about the page</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the GoogleSearchStore store.
+ // description:
+ // The GoogleSearchStore is a Datastore interface to
+ // the Google search service. The constructor accepts the following arguments:
+ // <ul>
+ // <li>label - the label attribute to use. Defaults to titleNoFormatting</li>
+ // <li>key - The API key to use. This is optional</li>
+ // <li>lang - The language locale to use. Defaults to the browser locale</li>
+ // </ul>
+
+ if(args){
+ if(args.label){
+ this.label = args.label;
+ }
+ if(args.key){
+ this._key = args.key;
+ }
+ if(args.lang){
+ this._lang = args.lang;
+ }
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ this._id = dojox.data.GoogleSearchStore.prototype._id++;
+ },
+
+ // _id: Integer
+ // A unique identifier for this store.
+ _id: 0,
+
+ // _requestCount: Integer
+ // A counter for the number of requests made. This is used to define
+ // the callback function that GoogleSearchStore will use.
+ _requestCount: 0,
+
+ // _googleUrl: String
+ // The URL to Googles search web service.
+ _googleUrl: "http://ajax.googleapis.com/ajax/services/search/",
+
+ // _storeRef: String
+ // The internal reference added to each item pointing at the store which owns it.
+ _storeRef: "_S",
+
+ // _attributes: Array
+ // The list of attributes that this store supports
+ _attributes: [ "unescapedUrl", "url", "visibleUrl", "cacheUrl", "title",
+ "titleNoFormatting", "content", "estimatedResultCount"],
+
+ // _aggregtedAttributes: Hash
+ // Maps per-query aggregated attributes that this store supports to the result keys that they come from.
+ _aggregatedAttributes: {
+ estimatedResultCount: "cursor.estimatedResultCount"
+ },
+
+ // label: String
+ // The default attribute which acts as a label for each item.
+ label: "titleNoFormatting",
+
+ // type: String
+ // The type of search. Valid values are "web", "local", "video", "blogs", "news", "books", "images".
+ // This should not be set directly. Instead use one of the child classes.
+ _type: "web",
+
+ // urlPreventCache: boolean
+ // Sets whether or not to pass preventCache to dojo.io.script.
+ urlPreventCache: true,
+
+
+ // _queryAttrs: Hash
+ // Maps query hash keys to Google query parameters.
+ _queryAttrs: {
+ text: 'q'
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.GoogleSearchStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.GoogleSearchStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return this._attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var val = item[attribute];
+ if(dojo.isArray(val)) {
+ return val;
+ }else if(val !== undefined){
+ return [val];
+ }else{
+ return [];
+ }
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _format: function(item, name){
+ return item;//base implementation does not format any items
+ },
+
+ fetch: function(request){
+ // summary:
+ // Fetch Google search items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ request = request || {};
+
+ var scope = request.scope || dojo.global;
+
+ if(!request.query){
+ if(request.onError){
+ request.onError.call(scope, new Error(this.declaredClass +
+ ": A query must be specified."));
+ return;
+ }
+ }
+ //Make a copy of the request object, in case it is
+ //modified outside the store in the middle of a request
+ var query = {};
+ for(var attr in this._queryAttrs) {
+ query[attr] = request.query[attr];
+ }
+ request = {
+ query: query,
+ onComplete: request.onComplete,
+ onError: request.onError,
+ onItem: request.onItem,
+ onBegin: request.onBegin,
+ start: request.start,
+ count: request.count
+ };
+
+ //Google's web api will only return a max of 8 results per page.
+ var pageSize = 8;
+
+ //Generate a unique function to be called back
+ var callbackFn = "GoogleSearchStoreCallback_" + this._id + "_" + (++this._requestCount);
+
+ //Build up the content to send the request for.
+ //rsz is the result size, "large" gives 8 results each time
+ var content = this._createContent(query, callbackFn, request);
+
+ var firstRequest;
+
+ if(typeof(request.start) === "undefined" || request.start === null){
+ request.start = 0;
+ }
+
+ if(!request.count){
+ request.count = pageSize;
+ }
+ firstRequest = {start: request.start - request.start % pageSize};
+
+ var _this = this;
+ var searchUrl = this._googleUrl + this._type;
+
+ var getArgs = {
+ url: searchUrl,
+ preventCache: this.urlPreventCache,
+ content: content
+ };
+
+ var items = [];
+ var successfulReq = 0;
+ var finished = false;
+ var lastOnItem = request.start -1;
+ var numRequests = 0;
+ var scriptIds = [];
+
+ // Performs the remote request.
+ function doRequest(req){
+ //Record how many requests have been made.
+ numRequests ++;
+ getArgs.content.context = getArgs.content.start = req.start;
+
+ var deferred = dojo.io.script.get(getArgs);
+ scriptIds.push(deferred.ioArgs.id);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonp callback function....
+ deferred.addErrback(function(error){
+ if(request.onError){
+ request.onError.call(scope, error, request);
+ }
+ });
+ }
+
+ // Function to handle returned data.
+ var myHandler = function(start, data){
+ if (scriptIds.length > 0) {
+ // Delete the script node that was created.
+ dojo.query("#" + scriptIds.splice(0,1)).forEach(dojo.destroy);
+ }
+ if(finished){return;}
+
+ var results = _this._getItems(data);
+ var cursor = data ? data['cursor']: null;
+
+ if(results){
+ //Process the results, adding the store reference to them
+ for(var i = 0; i < results.length && i + start < request.count + request.start; i++) {
+ _this._processItem(results[i], data);
+ items[i + start] = results[i];
+ }
+ successfulReq ++;
+ if(successfulReq == 1){
+ // After the first request, we know how many results exist.
+ // So perform any follow up requests to retrieve more data.
+ var pages = cursor ? cursor.pages : null;
+ var firstStart = pages ? Number(pages[pages.length - 1].start) : 0;
+
+ //Call the onBegin method if it exists
+ if (request.onBegin){
+ var est = cursor ? cursor.estimatedResultCount : results.length;
+ var total = est ? Math.min(est, firstStart + results.length) : firstStart + results.length;
+ request.onBegin.call(scope, total, request);
+ }
+
+ // Request the next pages.
+ var nextPage = (request.start - request.start % pageSize) + pageSize;
+ var page = 1;
+ while(pages){
+ if(!pages[page] || Number(pages[page].start) >= request.start + request.count){
+ break;
+ }
+ if(Number(pages[page].start) >= nextPage) {
+ doRequest({start: pages[page].start});
+ }
+ page++;
+ }
+ }
+
+ // Call the onItem function on all retrieved items.
+ if(request.onItem && items[lastOnItem + 1]){
+ do{
+ lastOnItem++;
+ request.onItem.call(scope, items[lastOnItem], request);
+ }while(items[lastOnItem + 1] && lastOnItem < request.start + request.count);
+ }
+
+ //If this is the last request, call final fetch handler.
+ if(successfulReq == numRequests){
+ //Process the items...
+ finished = true;
+ //Clean up the function, it should never be called again
+ dojo.global[callbackFn] = null;
+ if(request.onItem){
+ request.onComplete.call(scope, null, request);
+ }else{
+ items = items.slice(request.start, request.start + request.count);
+ request.onComplete.call(scope, items, request);
+ }
+
+ }
+ }
+ };
+
+ var callbacks = [];
+ var lastCallback = firstRequest.start - 1;
+
+ // Attach a callback function to the global namespace, where Google can call it.
+ dojo.global[callbackFn] = function(start, data, responseCode, errorMsg){
+ try {
+ if(responseCode != 200){
+ if(request.onError){
+ request.onError.call(scope, new Error("Response from Google was: " + responseCode), request);
+ }
+ dojo.global[callbackFn] = function(){};//an error occurred, do not return anything else.
+ return;
+ }
+
+ if(start == lastCallback + 1){
+ myHandler(Number(start), data);
+ lastCallback += pageSize;
+
+ //make sure that the callbacks happen in the correct sequence
+ if(callbacks.length > 0){
+ callbacks.sort(_this._getSort());
+ //In case the requsts do not come back in order, sort the returned results.
+ while(callbacks.length > 0 && callbacks[0].start == lastCallback + 1){
+ myHandler(Number(callbacks[0].start), callbacks[0].data);
+ callbacks.splice(0,1);
+ lastCallback += pageSize;
+ }
+ }
+ }else{
+ callbacks.push({start:start, data: data});
+ }
+ } catch (e) {
+ request.onError.call(scope, e, request);
+ }
+ };
+
+ // Perform the first request. When this has finished
+ // we will have a list of pages, which can then be
+ // gone through
+ doRequest(firstRequest);
+ },
+
+ _getSort: function() {
+ return function(a,b){
+ if(a.start < b.start){return -1;}
+ if(b.start < a.start){return 1;}
+ return 0;
+ };
+ },
+
+ _processItem: function(item, data) {
+ item[this._storeRef] = this;
+ // Copy aggregated attributes from query results to the item.
+ for(var attribute in this._aggregatedAttributes) {
+ item[attribute] = dojo.getObject(this._aggregatedAttributes[attribute], false, data);
+ }
+ },
+
+ _getItems: function(data){
+ return data['results'] || data;
+ },
+
+ _createContent: function(query, callback, request){
+ var content = {
+ v: "1.0",
+ rsz: "large",
+ callback: callback,
+ key: this._key,
+ hl: this._lang
+ };
+ for(var attr in this._queryAttrs) {
+ content[this._queryAttrs[attr]] = query[attr];
+ }
+ return content;
+ }
+});
+
+dojo.declare("dojox.data.GoogleWebSearchStore", dojox.data.GoogleSearchStore,{
+ // Summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The page title in HTML format.</li>
+ // <li>titleNoFormatting - The page title in plain text</li>
+ // <li>content - A snippet of information about the page</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>visibleUrl - The URL with no protocol specified.</li>
+ // <li>cacheUrl - The URL to the copy of the document cached by Google</li>
+ // <li>estimatedResultCount - (aggregated per-query) estimated number of results</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+});
+
+dojo.declare("dojox.data.GoogleBlogSearchStore", dojox.data.GoogleSearchStore,{
+ // Summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>blogUrl - The URL for the blog</li>
+ // <li>postUrl - The URL for the a single blog post</li>
+ // <li>visibleUrl - The URL with no protocol specified.
+ // <li>cacheUrl - The URL to the copy of the document cached by Google
+ // <li>author - The author of the blog post</li>
+ // <li>publishedDate - The published date, in RFC-822 format</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "blogs",
+ _attributes: ["blogUrl", "postUrl", "title", "titleNoFormatting", "content",
+ "author", "publishedDate"],
+ _aggregatedAttributes: { }
+});
+
+
+dojo.declare("dojox.data.GoogleLocalSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>url - The URL for the item</li>
+ // <li>lat - The latitude.</li>
+ // <li>lng - The longtitude.</li>
+ // <li>streetAddress - The street address</li>
+ // <li>city - The city</li>
+ // <li>region - The region</li>
+ // <li>country - The country</li>
+ // <li>phoneNumbers - Phone numbers associated with this address. Can be one or more.</li>
+ // <li>ddUrl - A URL that can be used to provide driving directions from the center of the search results to this search results</li>
+ // <li>ddUrlToHere - A URL that can be used to provide driving directions from this search result to a user specified location</li>
+ // <li>staticMapUrl - The published date, in RFC-822 format</li>
+ // <li>viewport - Recommended viewport for the query results (same for all results in a query)
+ // <ul>
+ // <li>center - contains lat, lng properties</li>
+ // <li>span - lat, lng properties for the viewport span</li>
+ // <li>ne, sw - lat, lng properties for the viewport corners<li>
+ // </ul>
+ // </li>
+ // </ul>
+ // The query accepts the following parameters:
+ // <ul>
+ // <li>text - The string to search for</li>
+ // <li>centerLatLong - Comma-separated lat & long for the center of the search (e.g. "48.8565,2.3509")</li>
+ // <li>searchSpan - Comma-separated lat & long degrees indicating the size of the desired search area (e.g. "0.065165,0.194149")</li>
+ // </ul>
+ _type: "local",
+ _attributes: ["title", "titleNoFormatting", "url", "lat", "lng", "streetAddress",
+ "city", "region", "country", "phoneNumbers", "ddUrl", "ddUrlToHere",
+ "ddUrlFromHere", "staticMapUrl", "viewport"],
+ _aggregatedAttributes: {
+ viewport: "viewport"
+ },
+ _queryAttrs: {
+ text: 'q',
+ centerLatLong: 'sll',
+ searchSpan: 'sspn'
+ }
+});
+
+dojo.declare("dojox.data.GoogleVideoSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>url - The URL for the item</li>
+ // <li>published - The published date, in RFC-822 format.</li>
+ // <li>publisher - The name of the publisher.</li>
+ // <li>duration - The approximate duration, in seconds, of the video.</li>
+ // <li>tbWidth - The width in pixels of the video.</li>
+ // <li>tbHeight - The height in pixels of the video</li>
+ // <li>tbUrl - The URL to a thumbnail representation of the video.</li>
+ // <li>playUrl - If present, supplies the url of the flash version of the video that can be played inline on your page. To play this video simply create and <embed> element on your page using this value as the src attribute and using application/x-shockwave-flash as the type attribute. If you want the video to play right away, make sure to append &autoPlay=true to the url..</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "video",
+ _attributes: ["title", "titleNoFormatting", "content", "url", "published", "publisher",
+ "duration", "tbWidth", "tbHeight", "tbUrl", "playUrl"],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleNewsSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The news story title in HTML format.</li>
+ // <li>titleNoFormatting - The news story title in plain text</li>
+ // <li>content - A snippet of information about the news story</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>publisher - The name of the publisher</li>
+ // <li>clusterUrl - A URL pointing to a page listing related storied.</li>
+ // <li>location - The location of the news story.</li>
+ // <li>publishedDate - The date of publication, in RFC-822 format.</li>
+ // <li>relatedStories - An optional array of objects specifying related stories.
+ // Each object has the following subset of properties:
+ // "title", "titleNoFormatting", "url", "unescapedUrl", "publisher", "location", "publishedDate".
+ // </li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "news",
+ _attributes: ["title", "titleNoFormatting", "content", "url", "unescapedUrl", "publisher",
+ "clusterUrl", "location", "publishedDate", "relatedStories" ],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleBookSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The book title in HTML format.</li>
+ // <li>titleNoFormatting - The book title in plain text</li>
+ // <li>authors - An array of authors</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>bookId - An identifier for the book, usually an ISBN.</li>
+ // <li>pageCount - The number of pages in the book.</li>
+ // <li>publishedYear - The year of publication.</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "books",
+ _attributes: ["title", "titleNoFormatting", "authors", "url", "unescapedUrl", "bookId",
+ "pageCount", "publishedYear"],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleImageSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The image title in HTML format.</li>
+ // <li>titleNoFormatting - The image title in plain text</li>
+ // <li>url - The URL for the image</li>
+ // <li>unescapedUrl - The URL for the image, with URL escaping. This is often more readable</li>
+ // <li>tbUrl - The URL for the image thumbnail</li>
+ // <li>visibleUrl - A shortened version of the URL associated with the result, stripped of a protocol and path</li>
+ // <li>originalContextUrl - The URL of the page containing the image.</li>
+ // <li>width - The width of the image in pixels.</li>
+ // <li>height - The height of the image in pixels.</li>
+ // <li>tbWidth - The width of the image thumbnail in pixels.</li>
+ // <li>tbHeight - The height of the image thumbnail in pixels.</li>
+ // <li>content - A snippet of information about the image, in HTML format</li>
+ // <li>contentNoFormatting - A snippet of information about the image, in plain text</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "images",
+ _attributes: ["title", "titleNoFormatting", "visibleUrl", "url", "unescapedUrl",
+ "originalContextUrl", "width", "height", "tbWidth", "tbHeight",
+ "tbUrl", "content", "contentNoFormatting"],
+ _aggregatedAttributes: { }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/GoogleSearchStore.xd.js b/js/dojo-1.6/dojox/data/GoogleSearchStore.xd.js
new file mode 100644
index 0000000..971c6bc
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/GoogleSearchStore.xd.js
@@ -0,0 +1,676 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.GoogleSearchStore"],
+["require", "dojo.io.script"],
+["provide", "dojox.data.GoogleWebSearchStore"],
+["provide", "dojox.data.GoogleBlogSearchStore"],
+["provide", "dojox.data.GoogleLocalSearchStore"],
+["provide", "dojox.data.GoogleVideoSearchStore"],
+["provide", "dojox.data.GoogleNewsSearchStore"],
+["provide", "dojox.data.GoogleBookSearchStore"],
+["provide", "dojox.data.GoogleImageSearchStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.GoogleSearchStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.GoogleSearchStore"] = true;
+dojo.provide("dojox.data.GoogleSearchStore");
+dojo.require("dojo.io.script");
+
+
+
+dojo.provide("dojox.data.GoogleWebSearchStore");
+dojo.provide("dojox.data.GoogleBlogSearchStore");
+dojo.provide("dojox.data.GoogleLocalSearchStore");
+dojo.provide("dojox.data.GoogleVideoSearchStore");
+dojo.provide("dojox.data.GoogleNewsSearchStore");
+dojo.provide("dojox.data.GoogleBookSearchStore");
+dojo.provide("dojox.data.GoogleImageSearchStore");
+
+dojo.experimental("dojox.data.GoogleSearchStore");
+
+dojo.declare("dojox.data.GoogleSearchStore",null,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // This data store acts as a base class for Google searches,
+ // and has a number of child data stores that implement different
+ // searches. This store defaults to searching the web, and is functionally
+ // identical to the dojox.data.GoogleWebSearchStore object.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>visibleUrl - The URL with no protocol specified.
+ // <li>cacheUrl - The URL to the copy of the document cached by Google
+ // <li>title - The page title in HTML format.</li>
+ // <li>titleNoFormatting - The page title in plain text</li>
+ // <li>content - A snippet of information about the page</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the GoogleSearchStore store.
+ // description:
+ // The GoogleSearchStore is a Datastore interface to
+ // the Google search service. The constructor accepts the following arguments:
+ // <ul>
+ // <li>label - the label attribute to use. Defaults to titleNoFormatting</li>
+ // <li>key - The API key to use. This is optional</li>
+ // <li>lang - The language locale to use. Defaults to the browser locale</li>
+ // </ul>
+
+ if(args){
+ if(args.label){
+ this.label = args.label;
+ }
+ if(args.key){
+ this._key = args.key;
+ }
+ if(args.lang){
+ this._lang = args.lang;
+ }
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ this._id = dojox.data.GoogleSearchStore.prototype._id++;
+ },
+
+ // _id: Integer
+ // A unique identifier for this store.
+ _id: 0,
+
+ // _requestCount: Integer
+ // A counter for the number of requests made. This is used to define
+ // the callback function that GoogleSearchStore will use.
+ _requestCount: 0,
+
+ // _googleUrl: String
+ // The URL to Googles search web service.
+ _googleUrl: "http://ajax.googleapis.com/ajax/services/search/",
+
+ // _storeRef: String
+ // The internal reference added to each item pointing at the store which owns it.
+ _storeRef: "_S",
+
+ // _attributes: Array
+ // The list of attributes that this store supports
+ _attributes: [ "unescapedUrl", "url", "visibleUrl", "cacheUrl", "title",
+ "titleNoFormatting", "content", "estimatedResultCount"],
+
+ // _aggregtedAttributes: Hash
+ // Maps per-query aggregated attributes that this store supports to the result keys that they come from.
+ _aggregatedAttributes: {
+ estimatedResultCount: "cursor.estimatedResultCount"
+ },
+
+ // label: String
+ // The default attribute which acts as a label for each item.
+ label: "titleNoFormatting",
+
+ // type: String
+ // The type of search. Valid values are "web", "local", "video", "blogs", "news", "books", "images".
+ // This should not be set directly. Instead use one of the child classes.
+ _type: "web",
+
+ // urlPreventCache: boolean
+ // Sets whether or not to pass preventCache to dojo.io.script.
+ urlPreventCache: true,
+
+
+ // _queryAttrs: Hash
+ // Maps query hash keys to Google query parameters.
+ _queryAttrs: {
+ text: 'q'
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.GoogleSearchStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.GoogleSearchStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return this._attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var val = item[attribute];
+ if(dojo.isArray(val)) {
+ return val;
+ }else if(val !== undefined){
+ return [val];
+ }else{
+ return [];
+ }
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _format: function(item, name){
+ return item;//base implementation does not format any items
+ },
+
+ fetch: function(request){
+ // summary:
+ // Fetch Google search items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ request = request || {};
+
+ var scope = request.scope || dojo.global;
+
+ if(!request.query){
+ if(request.onError){
+ request.onError.call(scope, new Error(this.declaredClass +
+ ": A query must be specified."));
+ return;
+ }
+ }
+ //Make a copy of the request object, in case it is
+ //modified outside the store in the middle of a request
+ var query = {};
+ for(var attr in this._queryAttrs) {
+ query[attr] = request.query[attr];
+ }
+ request = {
+ query: query,
+ onComplete: request.onComplete,
+ onError: request.onError,
+ onItem: request.onItem,
+ onBegin: request.onBegin,
+ start: request.start,
+ count: request.count
+ };
+
+ //Google's web api will only return a max of 8 results per page.
+ var pageSize = 8;
+
+ //Generate a unique function to be called back
+ var callbackFn = "GoogleSearchStoreCallback_" + this._id + "_" + (++this._requestCount);
+
+ //Build up the content to send the request for.
+ //rsz is the result size, "large" gives 8 results each time
+ var content = this._createContent(query, callbackFn, request);
+
+ var firstRequest;
+
+ if(typeof(request.start) === "undefined" || request.start === null){
+ request.start = 0;
+ }
+
+ if(!request.count){
+ request.count = pageSize;
+ }
+ firstRequest = {start: request.start - request.start % pageSize};
+
+ var _this = this;
+ var searchUrl = this._googleUrl + this._type;
+
+ var getArgs = {
+ url: searchUrl,
+ preventCache: this.urlPreventCache,
+ content: content
+ };
+
+ var items = [];
+ var successfulReq = 0;
+ var finished = false;
+ var lastOnItem = request.start -1;
+ var numRequests = 0;
+ var scriptIds = [];
+
+ // Performs the remote request.
+ function doRequest(req){
+ //Record how many requests have been made.
+ numRequests ++;
+ getArgs.content.context = getArgs.content.start = req.start;
+
+ var deferred = dojo.io.script.get(getArgs);
+ scriptIds.push(deferred.ioArgs.id);
+
+ //We only set up the errback, because the callback isn't ever really used because we have
+ //to link to the jsonp callback function....
+ deferred.addErrback(function(error){
+ if(request.onError){
+ request.onError.call(scope, error, request);
+ }
+ });
+ }
+
+ // Function to handle returned data.
+ var myHandler = function(start, data){
+ if (scriptIds.length > 0) {
+ // Delete the script node that was created.
+ dojo.query("#" + scriptIds.splice(0,1)).forEach(dojo.destroy);
+ }
+ if(finished){return;}
+
+ var results = _this._getItems(data);
+ var cursor = data ? data['cursor']: null;
+
+ if(results){
+ //Process the results, adding the store reference to them
+ for(var i = 0; i < results.length && i + start < request.count + request.start; i++) {
+ _this._processItem(results[i], data);
+ items[i + start] = results[i];
+ }
+ successfulReq ++;
+ if(successfulReq == 1){
+ // After the first request, we know how many results exist.
+ // So perform any follow up requests to retrieve more data.
+ var pages = cursor ? cursor.pages : null;
+ var firstStart = pages ? Number(pages[pages.length - 1].start) : 0;
+
+ //Call the onBegin method if it exists
+ if (request.onBegin){
+ var est = cursor ? cursor.estimatedResultCount : results.length;
+ var total = est ? Math.min(est, firstStart + results.length) : firstStart + results.length;
+ request.onBegin.call(scope, total, request);
+ }
+
+ // Request the next pages.
+ var nextPage = (request.start - request.start % pageSize) + pageSize;
+ var page = 1;
+ while(pages){
+ if(!pages[page] || Number(pages[page].start) >= request.start + request.count){
+ break;
+ }
+ if(Number(pages[page].start) >= nextPage) {
+ doRequest({start: pages[page].start});
+ }
+ page++;
+ }
+ }
+
+ // Call the onItem function on all retrieved items.
+ if(request.onItem && items[lastOnItem + 1]){
+ do{
+ lastOnItem++;
+ request.onItem.call(scope, items[lastOnItem], request);
+ }while(items[lastOnItem + 1] && lastOnItem < request.start + request.count);
+ }
+
+ //If this is the last request, call final fetch handler.
+ if(successfulReq == numRequests){
+ //Process the items...
+ finished = true;
+ //Clean up the function, it should never be called again
+ dojo.global[callbackFn] = null;
+ if(request.onItem){
+ request.onComplete.call(scope, null, request);
+ }else{
+ items = items.slice(request.start, request.start + request.count);
+ request.onComplete.call(scope, items, request);
+ }
+
+ }
+ }
+ };
+
+ var callbacks = [];
+ var lastCallback = firstRequest.start - 1;
+
+ // Attach a callback function to the global namespace, where Google can call it.
+ dojo.global[callbackFn] = function(start, data, responseCode, errorMsg){
+ try {
+ if(responseCode != 200){
+ if(request.onError){
+ request.onError.call(scope, new Error("Response from Google was: " + responseCode), request);
+ }
+ dojo.global[callbackFn] = function(){};//an error occurred, do not return anything else.
+ return;
+ }
+
+ if(start == lastCallback + 1){
+ myHandler(Number(start), data);
+ lastCallback += pageSize;
+
+ //make sure that the callbacks happen in the correct sequence
+ if(callbacks.length > 0){
+ callbacks.sort(_this._getSort());
+ //In case the requsts do not come back in order, sort the returned results.
+ while(callbacks.length > 0 && callbacks[0].start == lastCallback + 1){
+ myHandler(Number(callbacks[0].start), callbacks[0].data);
+ callbacks.splice(0,1);
+ lastCallback += pageSize;
+ }
+ }
+ }else{
+ callbacks.push({start:start, data: data});
+ }
+ } catch (e) {
+ request.onError.call(scope, e, request);
+ }
+ };
+
+ // Perform the first request. When this has finished
+ // we will have a list of pages, which can then be
+ // gone through
+ doRequest(firstRequest);
+ },
+
+ _getSort: function() {
+ return function(a,b){
+ if(a.start < b.start){return -1;}
+ if(b.start < a.start){return 1;}
+ return 0;
+ };
+ },
+
+ _processItem: function(item, data) {
+ item[this._storeRef] = this;
+ // Copy aggregated attributes from query results to the item.
+ for(var attribute in this._aggregatedAttributes) {
+ item[attribute] = dojo.getObject(this._aggregatedAttributes[attribute], false, data);
+ }
+ },
+
+ _getItems: function(data){
+ return data['results'] || data;
+ },
+
+ _createContent: function(query, callback, request){
+ var content = {
+ v: "1.0",
+ rsz: "large",
+ callback: callback,
+ key: this._key,
+ hl: this._lang
+ };
+ for(var attr in this._queryAttrs) {
+ content[this._queryAttrs[attr]] = query[attr];
+ }
+ return content;
+ }
+});
+
+dojo.declare("dojox.data.GoogleWebSearchStore", dojox.data.GoogleSearchStore,{
+ // Summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The page title in HTML format.</li>
+ // <li>titleNoFormatting - The page title in plain text</li>
+ // <li>content - A snippet of information about the page</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>visibleUrl - The URL with no protocol specified.</li>
+ // <li>cacheUrl - The URL to the copy of the document cached by Google</li>
+ // <li>estimatedResultCount - (aggregated per-query) estimated number of results</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+});
+
+dojo.declare("dojox.data.GoogleBlogSearchStore", dojox.data.GoogleSearchStore,{
+ // Summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>blogUrl - The URL for the blog</li>
+ // <li>postUrl - The URL for the a single blog post</li>
+ // <li>visibleUrl - The URL with no protocol specified.
+ // <li>cacheUrl - The URL to the copy of the document cached by Google
+ // <li>author - The author of the blog post</li>
+ // <li>publishedDate - The published date, in RFC-822 format</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "blogs",
+ _attributes: ["blogUrl", "postUrl", "title", "titleNoFormatting", "content",
+ "author", "publishedDate"],
+ _aggregatedAttributes: { }
+});
+
+
+dojo.declare("dojox.data.GoogleLocalSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>url - The URL for the item</li>
+ // <li>lat - The latitude.</li>
+ // <li>lng - The longtitude.</li>
+ // <li>streetAddress - The street address</li>
+ // <li>city - The city</li>
+ // <li>region - The region</li>
+ // <li>country - The country</li>
+ // <li>phoneNumbers - Phone numbers associated with this address. Can be one or more.</li>
+ // <li>ddUrl - A URL that can be used to provide driving directions from the center of the search results to this search results</li>
+ // <li>ddUrlToHere - A URL that can be used to provide driving directions from this search result to a user specified location</li>
+ // <li>staticMapUrl - The published date, in RFC-822 format</li>
+ // <li>viewport - Recommended viewport for the query results (same for all results in a query)
+ // <ul>
+ // <li>center - contains lat, lng properties</li>
+ // <li>span - lat, lng properties for the viewport span</li>
+ // <li>ne, sw - lat, lng properties for the viewport corners<li>
+ // </ul>
+ // </li>
+ // </ul>
+ // The query accepts the following parameters:
+ // <ul>
+ // <li>text - The string to search for</li>
+ // <li>centerLatLong - Comma-separated lat & long for the center of the search (e.g. "48.8565,2.3509")</li>
+ // <li>searchSpan - Comma-separated lat & long degrees indicating the size of the desired search area (e.g. "0.065165,0.194149")</li>
+ // </ul>
+ _type: "local",
+ _attributes: ["title", "titleNoFormatting", "url", "lat", "lng", "streetAddress",
+ "city", "region", "country", "phoneNumbers", "ddUrl", "ddUrlToHere",
+ "ddUrlFromHere", "staticMapUrl", "viewport"],
+ _aggregatedAttributes: {
+ viewport: "viewport"
+ },
+ _queryAttrs: {
+ text: 'q',
+ centerLatLong: 'sll',
+ searchSpan: 'sspn'
+ }
+});
+
+dojo.declare("dojox.data.GoogleVideoSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The blog post title in HTML format.</li>
+ // <li>titleNoFormatting - The blog post title in plain text</li>
+ // <li>content - A snippet of information about the blog post</li>
+ // <li>url - The URL for the item</li>
+ // <li>published - The published date, in RFC-822 format.</li>
+ // <li>publisher - The name of the publisher.</li>
+ // <li>duration - The approximate duration, in seconds, of the video.</li>
+ // <li>tbWidth - The width in pixels of the video.</li>
+ // <li>tbHeight - The height in pixels of the video</li>
+ // <li>tbUrl - The URL to a thumbnail representation of the video.</li>
+ // <li>playUrl - If present, supplies the url of the flash version of the video that can be played inline on your page. To play this video simply create and <embed> element on your page using this value as the src attribute and using application/x-shockwave-flash as the type attribute. If you want the video to play right away, make sure to append &autoPlay=true to the url..</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "video",
+ _attributes: ["title", "titleNoFormatting", "content", "url", "published", "publisher",
+ "duration", "tbWidth", "tbHeight", "tbUrl", "playUrl"],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleNewsSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The news story title in HTML format.</li>
+ // <li>titleNoFormatting - The news story title in plain text</li>
+ // <li>content - A snippet of information about the news story</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>publisher - The name of the publisher</li>
+ // <li>clusterUrl - A URL pointing to a page listing related storied.</li>
+ // <li>location - The location of the news story.</li>
+ // <li>publishedDate - The date of publication, in RFC-822 format.</li>
+ // <li>relatedStories - An optional array of objects specifying related stories.
+ // Each object has the following subset of properties:
+ // "title", "titleNoFormatting", "url", "unescapedUrl", "publisher", "location", "publishedDate".
+ // </li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "news",
+ _attributes: ["title", "titleNoFormatting", "content", "url", "unescapedUrl", "publisher",
+ "clusterUrl", "location", "publishedDate", "relatedStories" ],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleBookSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The book title in HTML format.</li>
+ // <li>titleNoFormatting - The book title in plain text</li>
+ // <li>authors - An array of authors</li>
+ // <li>url - The URL for the item</li>
+ // <li>unescapedUrl - The URL for the item, with URL escaping. This is often more readable</li>
+ // <li>bookId - An identifier for the book, usually an ISBN.</li>
+ // <li>pageCount - The number of pages in the book.</li>
+ // <li>publishedYear - The year of publication.</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "books",
+ _attributes: ["title", "titleNoFormatting", "authors", "url", "unescapedUrl", "bookId",
+ "pageCount", "publishedYear"],
+ _aggregatedAttributes: { }
+});
+
+dojo.declare("dojox.data.GoogleImageSearchStore", dojox.data.GoogleSearchStore,{
+ // summary:
+ // A data store for retrieving search results from Google.
+ // The following attributes are supported on each item:
+ // <ul>
+ // <li>title - The image title in HTML format.</li>
+ // <li>titleNoFormatting - The image title in plain text</li>
+ // <li>url - The URL for the image</li>
+ // <li>unescapedUrl - The URL for the image, with URL escaping. This is often more readable</li>
+ // <li>tbUrl - The URL for the image thumbnail</li>
+ // <li>visibleUrl - A shortened version of the URL associated with the result, stripped of a protocol and path</li>
+ // <li>originalContextUrl - The URL of the page containing the image.</li>
+ // <li>width - The width of the image in pixels.</li>
+ // <li>height - The height of the image in pixels.</li>
+ // <li>tbWidth - The width of the image thumbnail in pixels.</li>
+ // <li>tbHeight - The height of the image thumbnail in pixels.</li>
+ // <li>content - A snippet of information about the image, in HTML format</li>
+ // <li>contentNoFormatting - A snippet of information about the image, in plain text</li>
+ // </ul>
+ // The query accepts one parameter: text - The string to search for
+ _type: "images",
+ _attributes: ["title", "titleNoFormatting", "visibleUrl", "url", "unescapedUrl",
+ "originalContextUrl", "width", "height", "tbWidth", "tbHeight",
+ "tbUrl", "content", "contentNoFormatting"],
+ _aggregatedAttributes: { }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/HtmlStore.js b/js/dojo-1.6/dojox/data/HtmlStore.js
new file mode 100644
index 0000000..24e8d69
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/HtmlStore.js
@@ -0,0 +1,576 @@
+/*
+ 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["dojox.data.HtmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.HtmlStore"] = true;
+dojo.provide("dojox.data.HtmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.declare("dojox.data.HtmlStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the HTML table store.
+ // description:
+ // The HtmlStore can be created in one of two ways: a) by parsing an existing
+ // table or list DOM node on the current page or b) by referencing an external url and giving
+ // the id of the table or list in that page. The remote url will be parsed as an html page.
+ //
+ // The HTML table or list should be of the following form:
+ //
+ // | <table id="myTable">
+ // | <thead>
+ // | <tr>
+ // | <th>Attribute1</th>
+ // | <th>Attribute2</th>
+ // | </tr>
+ // | </thead>
+ // | <tbody>
+ // | <tr>
+ // | <td>Value1.1</td>
+ // | <td>Value1.2</td>
+ // | </tr>
+ // | <tr>
+ // | <td>Value2.1</td>
+ // | <td>Value2.2</td>
+ // | </tr>
+ // | </tbody>
+ // | </table>
+ //
+ // -or-
+ //
+ // | <ul id="myUnorderedList">
+ // | <li>Value.1</li>
+ // | <li>Value.2</li>
+ // | </ul>
+ //
+ // -or-
+ //
+ // | <ol id="myOrderedList">
+ // | <li>Value.1</li>
+ // | <li>Value.2</li>
+ // | </ol>
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // dataId: The id of the HTML table to use.
+ // OR
+ // url: The url of the remote page to load
+ // dataId: The id of the table element in the remote page
+ // and the option:
+ // trimWhitespace: Trim off any surrounding whitespace from the headers (attribute
+ // names) and text content of the items in question. Default is false for
+ // backwards compatibility.
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ if(args && "trimWhitespace" in args){
+ this.trimWhitespace = args.trimWhitespace?true:false;
+ }
+ if(args.url){
+ if(!args.dataId){
+ throw new Error("dojo.data.HtmlStore: Cannot instantiate using url without an id!");
+ }
+ this.url = args.url;
+ this.dataId = args.dataId;
+ }else{
+ if(args.dataId){
+ this.dataId = args.dataId;
+ }
+ }
+ if(args && "fetchOnCreate" in args){
+ this.fetchOnCreate = args.fetchOnCreate?true:false;
+ }
+ if(this.fetchOnCreate && this.dataId){
+ this.fetch();
+ }
+ },
+
+ // url: [public] string
+ // The URL from which to load an HTML document for data loading
+ url: "",
+
+ // dataId: [public] string
+ // The id in the document for an element from which to get the data.
+ dataId: "",
+
+ // trimWhitepace: [public] boolean
+ // Boolean flag to denote if the store should trim whitepace around
+ // header and data content of a node. This matters if reformatters
+ // alter the white spacing around the tags. The default is false for
+ // backwards compat.
+ trimWhitespace: false,
+
+ // urlPreventCache: [public] boolean
+ // Flag to denote if peventCache should be used on xhrGet calls.
+ urlPreventCache: false,
+
+ // fetchOnCreate: [public] boolean
+ // Flag to denote if it should try to load from a data id (nested in the page)
+ // The moment the store is created, instead of waiting for first
+ // fetch call.
+ fetchOnCreate: false,
+
+ _indexItems: function(){
+ // summary:
+ // Function to index items found under the id.
+ // tags:
+ // private
+ this._getHeadings();
+ if(this._rootNode.rows){//tables
+ if(this._rootNode.tBodies && this._rootNode.tBodies.length > 0){
+ this._rootNode = this._rootNode.tBodies[0];
+ }
+ var i;
+ for(i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i]._ident = i+1;
+ }
+ }else{//lists
+ var c=1;
+ for(i=0; i<this._rootNode.childNodes.length; i++){
+ if(this._rootNode.childNodes[i].nodeType === 1){
+ this._rootNode.childNodes[i]._ident = c;
+ c++;
+ }
+ }
+ }
+ },
+
+ _getHeadings: function(){
+ // summary:
+ // Function to load the attribute names from the table header so that the
+ // attributes (cells in a row), can have a reasonable name.
+ // For list items, returns single implicit heading, ["name"]
+ this._headings = [];
+ if(this._rootNode.tHead){
+ dojo.forEach(this._rootNode.tHead.rows[0].cells, dojo.hitch(this, function(th){
+ var text = dojox.xml.parser.textContent(th);
+ this._headings.push(this.trimWhitespace?dojo.trim(text):text);
+ }));
+ }else{
+ this._headings = ["name"];
+ }
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all rows in the table as an array of items.
+ var items = [];
+ var i;
+ if(this._rootNode.rows){//table
+ for(i=0; i<this._rootNode.rows.length; i++){
+ items.push(this._rootNode.rows[i]);
+ }
+ }else{ //list
+ for(i=0; i<this._rootNode.childNodes.length; i++){
+ if(this._rootNode.childNodes[i].nodeType === 1){
+ items.push(this._rootNode.childNodes[i]);
+ }
+ }
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.HtmlStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns the index (column) that the attribute resides in the row.
+ if(typeof attribute !== "string"){
+ throw new Error("dojo.data.HtmlStore: a function was passed an attribute argument that was not an attribute name string");
+ return -1;
+ }
+ return dojo.indexOf(this._headings, attribute); //int
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var index = this._assertIsAttribute(attribute);
+ if(index>-1){
+ var text;
+ if(item.cells){
+ text = dojox.xml.parser.textContent(item.cells[index]);
+ }else{//return Value for lists
+ text = dojox.xml.parser.textContent(item);
+ }
+ return [this.trimWhitespace?dojo.trim(text):text];
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var i=0; i<this._headings.length; i++){
+ if(this.hasAttribute(item, this._headings[i]))
+ attributes.push(this._headings[i]);
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ return something && dojo.isDescendant(something, this._rootNode);
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If '_fetchUrl' is specified, it is used to load an XML document
+ // with a query string.
+ // Otherwise and if 'url' is specified, the XML document is
+ // loaded and list XML elements that match to a query (set of element
+ // names and their text attribute values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If '_rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(this._rootNode){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ if(!this.url){
+ this._rootNode = dojo.byId(this.dataId);
+ this._indexItems();
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text",
+ preventCache: this.urlPreventCache
+ };
+ var self = this;
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.dataId);
+ self._indexItems();
+ self._finishFetchItems(request, fetchHandler, errorHandler);
+ });
+ getHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for processing the passed in request and locating the requested items.
+ var items = [];
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ fetchHandler(items, request);
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ fetchHandler(items, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ if(item.cells){
+ return "Item #" + this.getIdentity(item);
+ }else{
+ return this.getValue(item,"name");
+ }
+ }
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(item.cells){
+ return null;
+ }else{
+ return ["name"];
+ }
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ if(this.hasAttribute(item, "name")){
+ return this.getValue(item,"name");
+ }else{
+ return item._ident;
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ //Identity isn't taken from a public attribute.
+ return null;
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ var identity = keywordArgs.identity;
+ var self = this;
+ var item = null;
+ var scope = null;
+ if(!this._rootNode){
+ if(!this.url){
+ this._rootNode = dojo.byId(this.dataId);
+ this._indexItems();
+ if(self._rootNode.rows){ //Table
+ item = this._rootNode.rows[identity + 1];
+ }else{ //Lists
+ for(var i = 0; i < self._rootNode.childNodes.length; i++){
+ if(self._rootNode.childNodes[i].nodeType === 1 && identity === dojox.xml.parser.textContent(self._rootNode.childNodes[i])){
+ item = self._rootNode.childNodes[i];
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.dataId);
+ self._indexItems();
+ if(self._rootNode.rows && identity <= self._rootNode.rows.length){ //Table
+ item = self._rootNode.rows[identity-1];
+ }else{ //List
+ for(var i = 0; i < self._rootNode.childNodes.length; i++){
+ if(self._rootNode.childNodes[i].nodeType === 1 && identity === dojox.xml.parser.textContent(self._rootNode.childNodes[i])){
+ item = self._rootNode.childNodes[i];
+ break;
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ getHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+
+ }
+ });
+ }
+ }else{
+ if(this._rootNode.rows[identity+1]){
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.HtmlStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/HtmlStore.xd.js b/js/dojo-1.6/dojox/data/HtmlStore.xd.js
new file mode 100644
index 0000000..c20347a
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/HtmlStore.xd.js
@@ -0,0 +1,583 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.HtmlStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"],
+["require", "dojox.xml.parser"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.HtmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.HtmlStore"] = true;
+dojo.provide("dojox.data.HtmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.declare("dojox.data.HtmlStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the HTML table store.
+ // description:
+ // The HtmlStore can be created in one of two ways: a) by parsing an existing
+ // table or list DOM node on the current page or b) by referencing an external url and giving
+ // the id of the table or list in that page. The remote url will be parsed as an html page.
+ //
+ // The HTML table or list should be of the following form:
+ //
+ // | <table id="myTable">
+ // | <thead>
+ // | <tr>
+ // | <th>Attribute1</th>
+ // | <th>Attribute2</th>
+ // | </tr>
+ // | </thead>
+ // | <tbody>
+ // | <tr>
+ // | <td>Value1.1</td>
+ // | <td>Value1.2</td>
+ // | </tr>
+ // | <tr>
+ // | <td>Value2.1</td>
+ // | <td>Value2.2</td>
+ // | </tr>
+ // | </tbody>
+ // | </table>
+ //
+ // -or-
+ //
+ // | <ul id="myUnorderedList">
+ // | <li>Value.1</li>
+ // | <li>Value.2</li>
+ // | </ul>
+ //
+ // -or-
+ //
+ // | <ol id="myOrderedList">
+ // | <li>Value.1</li>
+ // | <li>Value.2</li>
+ // | </ol>
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // dataId: The id of the HTML table to use.
+ // OR
+ // url: The url of the remote page to load
+ // dataId: The id of the table element in the remote page
+ // and the option:
+ // trimWhitespace: Trim off any surrounding whitespace from the headers (attribute
+ // names) and text content of the items in question. Default is false for
+ // backwards compatibility.
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ if(args && "trimWhitespace" in args){
+ this.trimWhitespace = args.trimWhitespace?true:false;
+ }
+ if(args.url){
+ if(!args.dataId){
+ throw new Error("dojo.data.HtmlStore: Cannot instantiate using url without an id!");
+ }
+ this.url = args.url;
+ this.dataId = args.dataId;
+ }else{
+ if(args.dataId){
+ this.dataId = args.dataId;
+ }
+ }
+ if(args && "fetchOnCreate" in args){
+ this.fetchOnCreate = args.fetchOnCreate?true:false;
+ }
+ if(this.fetchOnCreate && this.dataId){
+ this.fetch();
+ }
+ },
+
+ // url: [public] string
+ // The URL from which to load an HTML document for data loading
+ url: "",
+
+ // dataId: [public] string
+ // The id in the document for an element from which to get the data.
+ dataId: "",
+
+ // trimWhitepace: [public] boolean
+ // Boolean flag to denote if the store should trim whitepace around
+ // header and data content of a node. This matters if reformatters
+ // alter the white spacing around the tags. The default is false for
+ // backwards compat.
+ trimWhitespace: false,
+
+ // urlPreventCache: [public] boolean
+ // Flag to denote if peventCache should be used on xhrGet calls.
+ urlPreventCache: false,
+
+ // fetchOnCreate: [public] boolean
+ // Flag to denote if it should try to load from a data id (nested in the page)
+ // The moment the store is created, instead of waiting for first
+ // fetch call.
+ fetchOnCreate: false,
+
+ _indexItems: function(){
+ // summary:
+ // Function to index items found under the id.
+ // tags:
+ // private
+ this._getHeadings();
+ if(this._rootNode.rows){//tables
+ if(this._rootNode.tBodies && this._rootNode.tBodies.length > 0){
+ this._rootNode = this._rootNode.tBodies[0];
+ }
+ var i;
+ for(i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i]._ident = i+1;
+ }
+ }else{//lists
+ var c=1;
+ for(i=0; i<this._rootNode.childNodes.length; i++){
+ if(this._rootNode.childNodes[i].nodeType === 1){
+ this._rootNode.childNodes[i]._ident = c;
+ c++;
+ }
+ }
+ }
+ },
+
+ _getHeadings: function(){
+ // summary:
+ // Function to load the attribute names from the table header so that the
+ // attributes (cells in a row), can have a reasonable name.
+ // For list items, returns single implicit heading, ["name"]
+ this._headings = [];
+ if(this._rootNode.tHead){
+ dojo.forEach(this._rootNode.tHead.rows[0].cells, dojo.hitch(this, function(th){
+ var text = dojox.xml.parser.textContent(th);
+ this._headings.push(this.trimWhitespace?dojo.trim(text):text);
+ }));
+ }else{
+ this._headings = ["name"];
+ }
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all rows in the table as an array of items.
+ var items = [];
+ var i;
+ if(this._rootNode.rows){//table
+ for(i=0; i<this._rootNode.rows.length; i++){
+ items.push(this._rootNode.rows[i]);
+ }
+ }else{ //list
+ for(i=0; i<this._rootNode.childNodes.length; i++){
+ if(this._rootNode.childNodes[i].nodeType === 1){
+ items.push(this._rootNode.childNodes[i]);
+ }
+ }
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.HtmlStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns the index (column) that the attribute resides in the row.
+ if(typeof attribute !== "string"){
+ throw new Error("dojo.data.HtmlStore: a function was passed an attribute argument that was not an attribute name string");
+ return -1;
+ }
+ return dojo.indexOf(this._headings, attribute); //int
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var index = this._assertIsAttribute(attribute);
+ if(index>-1){
+ var text;
+ if(item.cells){
+ text = dojox.xml.parser.textContent(item.cells[index]);
+ }else{//return Value for lists
+ text = dojox.xml.parser.textContent(item);
+ }
+ return [this.trimWhitespace?dojo.trim(text):text];
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var i=0; i<this._headings.length; i++){
+ if(this.hasAttribute(item, this._headings[i]))
+ attributes.push(this._headings[i]);
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ return something && dojo.isDescendant(something, this._rootNode);
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If '_fetchUrl' is specified, it is used to load an XML document
+ // with a query string.
+ // Otherwise and if 'url' is specified, the XML document is
+ // loaded and list XML elements that match to a query (set of element
+ // names and their text attribute values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If '_rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(this._rootNode){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ if(!this.url){
+ this._rootNode = dojo.byId(this.dataId);
+ this._indexItems();
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text",
+ preventCache: this.urlPreventCache
+ };
+ var self = this;
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.dataId);
+ self._indexItems();
+ self._finishFetchItems(request, fetchHandler, errorHandler);
+ });
+ getHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for processing the passed in request and locating the requested items.
+ var items = [];
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var key;
+ var value;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ fetchHandler(items, request);
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ fetchHandler(items, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ if(item.cells){
+ return "Item #" + this.getIdentity(item);
+ }else{
+ return this.getValue(item,"name");
+ }
+ }
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(item.cells){
+ return null;
+ }else{
+ return ["name"];
+ }
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ if(this.hasAttribute(item, "name")){
+ return this.getValue(item,"name");
+ }else{
+ return item._ident;
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ //Identity isn't taken from a public attribute.
+ return null;
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ var identity = keywordArgs.identity;
+ var self = this;
+ var item = null;
+ var scope = null;
+ if(!this._rootNode){
+ if(!this.url){
+ this._rootNode = dojo.byId(this.dataId);
+ this._indexItems();
+ if(self._rootNode.rows){ //Table
+ item = this._rootNode.rows[identity + 1];
+ }else{ //Lists
+ for(var i = 0; i < self._rootNode.childNodes.length; i++){
+ if(self._rootNode.childNodes[i].nodeType === 1 && identity === dojox.xml.parser.textContent(self._rootNode.childNodes[i])){
+ item = self._rootNode.childNodes[i];
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.dataId);
+ self._indexItems();
+ if(self._rootNode.rows && identity <= self._rootNode.rows.length){ //Table
+ item = self._rootNode.rows[identity-1];
+ }else{ //List
+ for(var i = 0; i < self._rootNode.childNodes.length; i++){
+ if(self._rootNode.childNodes[i].nodeType === 1 && identity === dojox.xml.parser.textContent(self._rootNode.childNodes[i])){
+ item = self._rootNode.childNodes[i];
+ break;
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ getHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+
+ }
+ });
+ }
+ }else{
+ if(this._rootNode.rows[identity+1]){
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.HtmlStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/HtmlTableStore.js b/js/dojo-1.6/dojox/data/HtmlTableStore.js
new file mode 100644
index 0000000..1f5595c
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/HtmlTableStore.js
@@ -0,0 +1,482 @@
+/*
+ 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["dojox.data.HtmlTableStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.HtmlTableStore"] = true;
+dojo.provide("dojox.data.HtmlTableStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.declare("dojox.data.HtmlTableStore", null, {
+ constructor: function(/*Object*/args){
+ dojo.deprecated("dojox.data.HtmlTableStore", "Please use dojox.data.HtmlStore");
+ // summary:
+ // Initializer for the HTML table store.
+ // description:
+ // The HtmlTableStore can be created in one of two ways: a) by parsing an existing
+ // table DOM node on the current page or b) by referencing an external url and giving
+ // the id of the table in that page. The remote url will be parsed as an html page.
+ //
+ // The HTML table should be of the following form:
+ // <table id="myTable">
+ // <thead>
+ // <tr>
+ // <th>Attribute1</th>
+ // <th>Attribute2</th>
+ // </tr>
+ // </thead>
+ // <tbody>
+ // <tr>
+ // <td>Value1.1</td>
+ // <td>Value1.2</td>
+ // </tr>
+ // <tr>
+ // <td>Value2.1</td>
+ // <td>Value2.2</td>
+ // </tr>
+ // </tbody>
+ // </table>
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // tableId: The id of the HTML table to use.
+ // OR
+ // url: The url of the remote page to load
+ // tableId: The id of the table element in the remote page
+
+ if(args.url){
+ if(!args.tableId)
+ throw new Error("dojo.data.HtmlTableStore: Cannot instantiate using url without an id!");
+ this.url = args.url;
+ this.tableId = args.tableId;
+ }else{
+ if(args.tableId){
+ this._rootNode = dojo.byId(args.tableId);
+ this.tableId = this._rootNode.id;
+ }else{
+ this._rootNode = dojo.byId(this.tableId);
+ }
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ }
+ },
+
+ // url: [public] string
+ // The URL from which to load an HTML document for data loading
+ url: "",
+
+ // tableId: [public] string
+ // The id of the table to load as store contents.
+ tableId: "",
+
+ _getHeadings: function(){
+ // summary:
+ // Function to load the attribute names from the table header so that the
+ // attributes (cells in a row), can have a reasonable name.
+ this._headings = [];
+ dojo.forEach(this._rootNode.tHead.rows[0].cells, dojo.hitch(this, function(th){
+ this._headings.push(dojox.xml.parser.textContent(th));
+ }));
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all rows in the table as an array of items.
+ var items = [];
+ for(var i=1; i<this._rootNode.rows.length; i++){
+ items.push(this._rootNode.rows[i]);
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.HtmlTableStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns the index (column) that the attribute resides in the row.
+ if(typeof attribute !== "string"){
+ throw new Error("dojo.data.HtmlTableStore: a function was passed an attribute argument that was not an attribute name string");
+ return -1;
+ }
+ return dojo.indexOf(this._headings, attribute); //int
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var index = this._assertIsAttribute(attribute);
+
+ if(index>-1){
+ return [dojox.xml.parser.textContent(item.cells[index])] ;
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var i=0; i<this._headings.length; i++){
+ if(this.hasAttribute(item, this._headings[i]))
+ attributes.push(this._headings[i]);
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If '_fetchUrl' is specified, it is used to load an XML document
+ // with a query string.
+ // Otherwise and if 'url' is specified, the XML document is
+ // loaded and list XML elements that match to a query (set of element
+ // names and their text attribute values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If '_rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(this._rootNode){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ if(!this.url){
+ this._rootNode = dojo.byId(this.tableId);
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var self = this;
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.tableId);
+ self._getHeadings.call(self);
+ for(var i=0; i<self._rootNode.rows.length; i++){
+ self._rootNode.rows[i].store = self;
+ }
+ self._finishFetchItems(request, fetchHandler, errorHandler);
+ });
+ getHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for processing the passed in request and locating the requested items.
+ var items = null;
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var value;
+ var key;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ fetchHandler(items, request);
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ fetchHandler(items, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item))
+ return "Table Row #" + this.getIdentity(item);
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ //Opera doesn't support the sectionRowIndex,
+ //So, have to call the indexOf to locate it.
+ //Blah.
+ if(!dojo.isOpera){
+ return item.sectionRowIndex; // int
+ }else{
+ return (dojo.indexOf(this._rootNode.rows, item) - 1) // int
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ //Identity isn't taken from a public attribute.
+ return null;
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ var identity = keywordArgs.identity;
+ var self = this;
+ var item = null;
+ var scope = null;
+
+ if(!this._rootNode){
+ if(!this.url){
+ this._rootNode = dojo.byId(this.tableId);
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.tableId);
+ self._getHeadings.call(self);
+ for(var i=0; i<self._rootNode.rows.length; i++){
+ self._rootNode.rows[i].store = self;
+ }
+ item = self._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ getHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+
+ }
+ });
+ }
+ }else{
+ if(this._rootNode.rows[identity+1]){
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.HtmlTableStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/HtmlTableStore.xd.js b/js/dojo-1.6/dojox/data/HtmlTableStore.xd.js
new file mode 100644
index 0000000..91a7920
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/HtmlTableStore.xd.js
@@ -0,0 +1,489 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.HtmlTableStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"],
+["require", "dojox.xml.parser"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.HtmlTableStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.HtmlTableStore"] = true;
+dojo.provide("dojox.data.HtmlTableStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.declare("dojox.data.HtmlTableStore", null, {
+ constructor: function(/*Object*/args){
+ dojo.deprecated("dojox.data.HtmlTableStore", "Please use dojox.data.HtmlStore");
+ // summary:
+ // Initializer for the HTML table store.
+ // description:
+ // The HtmlTableStore can be created in one of two ways: a) by parsing an existing
+ // table DOM node on the current page or b) by referencing an external url and giving
+ // the id of the table in that page. The remote url will be parsed as an html page.
+ //
+ // The HTML table should be of the following form:
+ // <table id="myTable">
+ // <thead>
+ // <tr>
+ // <th>Attribute1</th>
+ // <th>Attribute2</th>
+ // </tr>
+ // </thead>
+ // <tbody>
+ // <tr>
+ // <td>Value1.1</td>
+ // <td>Value1.2</td>
+ // </tr>
+ // <tr>
+ // <td>Value2.1</td>
+ // <td>Value2.2</td>
+ // </tr>
+ // </tbody>
+ // </table>
+ //
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // tableId: The id of the HTML table to use.
+ // OR
+ // url: The url of the remote page to load
+ // tableId: The id of the table element in the remote page
+
+ if(args.url){
+ if(!args.tableId)
+ throw new Error("dojo.data.HtmlTableStore: Cannot instantiate using url without an id!");
+ this.url = args.url;
+ this.tableId = args.tableId;
+ }else{
+ if(args.tableId){
+ this._rootNode = dojo.byId(args.tableId);
+ this.tableId = this._rootNode.id;
+ }else{
+ this._rootNode = dojo.byId(this.tableId);
+ }
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ }
+ },
+
+ // url: [public] string
+ // The URL from which to load an HTML document for data loading
+ url: "",
+
+ // tableId: [public] string
+ // The id of the table to load as store contents.
+ tableId: "",
+
+ _getHeadings: function(){
+ // summary:
+ // Function to load the attribute names from the table header so that the
+ // attributes (cells in a row), can have a reasonable name.
+ this._headings = [];
+ dojo.forEach(this._rootNode.tHead.rows[0].cells, dojo.hitch(this, function(th){
+ this._headings.push(dojox.xml.parser.textContent(th));
+ }));
+ },
+
+ _getAllItems: function(){
+ // summary:
+ // Function to return all rows in the table as an array of items.
+ var items = [];
+ for(var i=1; i<this._rootNode.rows.length; i++){
+ items.push(this._rootNode.rows[i]);
+ }
+ return items; //array
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.HtmlTableStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ //
+ // returns:
+ // Returns the index (column) that the attribute resides in the row.
+ if(typeof attribute !== "string"){
+ throw new Error("dojo.data.HtmlTableStore: a function was passed an attribute argument that was not an attribute name string");
+ return -1;
+ }
+ return dojo.indexOf(this._headings, attribute); //int
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+
+ this._assertIsItem(item);
+ var index = this._assertIsAttribute(attribute);
+
+ if(index>-1){
+ return [dojox.xml.parser.textContent(item.cells[index])] ;
+ }
+ return []; //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ for(var i=0; i<this._headings.length; i++){
+ if(this.hasAttribute(item, this._headings[i]))
+ attributes.push(this._headings[i]);
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.getValues(item, attribute).length > 0;
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ this._assertIsItem(keywordArgs.item);
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If '_fetchUrl' is specified, it is used to load an XML document
+ // with a query string.
+ // Otherwise and if 'url' is specified, the XML document is
+ // loaded and list XML elements that match to a query (set of element
+ // names and their text attribute values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If '_rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(this._rootNode){
+ this._finishFetchItems(request, fetchHandler, errorHandler);
+ }else{
+ if(!this.url){
+ this._rootNode = dojo.byId(this.tableId);
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var self = this;
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.tableId);
+ self._getHeadings.call(self);
+ for(var i=0; i<self._rootNode.rows.length; i++){
+ self._rootNode.rows[i].store = self;
+ }
+ self._finishFetchItems(request, fetchHandler, errorHandler);
+ });
+ getHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ }
+ }
+ },
+
+ _finishFetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Internal function for processing the passed in request and locating the requested items.
+ var items = null;
+ var arrayOfAllItems = this._getAllItems();
+ if(request.query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ items = [];
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ var value;
+ var key;
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(key in request.query){
+ value = request.query[key]+'';
+ if(!this._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ fetchHandler(items, request);
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get listsand sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ fetchHandler(items, request);
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // nothing to do here!
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item))
+ return "Table Row #" + this.getIdentity(item);
+ return undefined;
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ this._assertIsItem(item);
+ //Opera doesn't support the sectionRowIndex,
+ //So, have to call the indexOf to locate it.
+ //Blah.
+ if(!dojo.isOpera){
+ return item.sectionRowIndex; // int
+ }else{
+ return (dojo.indexOf(this._rootNode.rows, item) - 1) // int
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ //Identity isn't taken from a public attribute.
+ return null;
+ },
+
+ fetchItemByIdentity: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ var identity = keywordArgs.identity;
+ var self = this;
+ var item = null;
+ var scope = null;
+
+ if(!this._rootNode){
+ if(!this.url){
+ this._rootNode = dojo.byId(this.tableId);
+ this._getHeadings();
+ for(var i=0; i<this._rootNode.rows.length; i++){
+ this._rootNode.rows[i].store = this;
+ }
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+
+ }else{
+ var getArgs = {
+ url: this.url,
+ handleAs: "text"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var findNode = function(node, id){
+ if(node.id == id){
+ return node; //object
+ }
+ if(node.childNodes){
+ for(var i=0; i<node.childNodes.length; i++){
+ var returnNode = findNode(node.childNodes[i], id);
+ if(returnNode){
+ return returnNode; //object
+ }
+ }
+ }
+ return null; //null
+ }
+ var d = document.createElement("div");
+ d.innerHTML = data;
+ self._rootNode = findNode(d, self.tableId);
+ self._getHeadings.call(self);
+ for(var i=0; i<self._rootNode.rows.length; i++){
+ self._rootNode.rows[i].store = self;
+ }
+ item = self._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ });
+ getHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+
+ }
+ });
+ }
+ }else{
+ if(this._rootNode.rows[identity+1]){
+ item = this._rootNode.rows[identity+1];
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.HtmlTableStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/ItemExplorer.js b/js/dojo-1.6/dojox/data/ItemExplorer.js
new file mode 100644
index 0000000..64a1bbb
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ItemExplorer.js
@@ -0,0 +1,637 @@
+/*
+ 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["dojox.data.ItemExplorer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ItemExplorer"] = true;
+dojo.provide("dojox.data.ItemExplorer");
+dojo.require("dijit.Tree");
+dojo.require("dijit.Dialog");
+dojo.require("dijit.Menu");
+dojo.require("dijit.form.ValidationTextBox");
+dojo.require("dijit.form.Textarea");
+dojo.require("dijit.form.Button");
+dojo.require("dijit.form.CheckBox");
+dojo.require("dijit.form.FilteringSelect");
+
+(function(){
+ var getValue = function(store, item, prop){
+ var value = store.getValues(item, prop);
+ if(value.length < 2){
+ value = store.getValue(item, prop);
+ }
+ return value;
+ }
+
+dojo.declare("dojox.data.ItemExplorer", dijit.Tree, {
+ useSelect: false,
+ refSelectSearchAttr: null,
+ constructor: function(options){
+ dojo.mixin(this, options);
+ var self = this;
+ var initialRootValue = {};
+ var root = this.rootModelNode = {value:initialRootValue,id:"root"};
+
+ this._modelNodeIdMap = {};
+ this._modelNodePropMap = {};
+ var nextId = 1;
+ this.model = {
+ getRoot: function(onItem){
+ onItem(root);
+ },
+ mayHaveChildren: function(modelNode){
+ return modelNode.value && typeof modelNode.value == 'object' && !(modelNode.value instanceof Date);
+ },
+ getChildren: function(parentModelNode, onComplete, onError){
+ var keys, parent, item = parentModelNode.value;
+ var children = [];
+ if(item == initialRootValue){
+ onComplete([]);
+ return;
+ }
+ var isItem = self.store && self.store.isItem(item, true);
+ if(isItem && !self.store.isItemLoaded(item)){
+ // if it is not loaded, do so now.
+ self.store.loadItem({
+ item:item,
+ onItem:function(loadedItem){
+ item = loadedItem;
+ enumerate();
+ }
+ });
+ }else{
+ enumerate();
+ }
+ function enumerate(){
+ // once loaded, enumerate the keys
+ if(isItem){
+ // get the properties through the dojo data API
+ keys = self.store.getAttributes(item);
+ parent = item;
+ }else if(item && typeof item == 'object'){
+ parent = parentModelNode.value;
+ keys = [];
+ // also we want to be able to drill down into plain JS objects/arrays
+ for(var i in item){
+ if(item.hasOwnProperty(i) && i != '__id' && i != '__clientId'){
+ keys.push(i);
+ }
+ }
+ }
+ if(keys){
+ for(var key, k=0; key = keys[k++];){
+ children.push({
+ property:key,
+ value: isItem ? getValue(self.store, item, key) : item[key],
+ parent: parent});
+ }
+ children.push({addNew:true, parent: parent, parentNode : parentModelNode});
+ }
+ onComplete(children);
+ }
+ },
+ getIdentity: function(modelNode){
+ if(!modelNode.id){
+ if(modelNode.addNew){
+ modelNode.property = "--addNew";
+ }
+ modelNode.id = nextId++;
+ if(self.store){
+ if(self.store.isItem(modelNode.value)){
+ var identity = self.store.getIdentity(modelNode.value);
+ (self._modelNodeIdMap[identity] = self._modelNodeIdMap[identity] || []).push(modelNode);
+ }
+ if(modelNode.parent){
+ identity = self.store.getIdentity(modelNode.parent) + '.' + modelNode.property;
+ (self._modelNodePropMap[identity] = self._modelNodePropMap[identity] || []).push(modelNode);
+ }
+ }
+ }
+ return modelNode.id;
+ },
+ getLabel: function(modelNode){
+ return modelNode === root ?
+ "Object Properties" :
+ modelNode.addNew ? (modelNode.parent instanceof Array ? "Add new value" : "Add new property") :
+ modelNode.property + ": " +
+ (modelNode.value instanceof Array ? "(" + modelNode.value.length + " elements)" : modelNode.value);
+ },
+ onChildrenChange: function(modelNode){
+ },
+ onChange: function(modelNode){
+ }
+ };
+ },
+ postCreate: function(){
+ this.inherited(arguments);
+ // handle the clicking on the "add new property item"
+ dojo.connect(this, "onClick", function(modelNode, treeNode){
+ this.lastFocused = treeNode;
+ if(modelNode.addNew){
+ //this.focusNode(treeNode.getParent());
+ this._addProperty();
+ }else{
+ this._editProperty();
+ }
+ });
+ var contextMenu = new dijit.Menu({
+ targetNodeIds: [this.rootNode.domNode],
+ id: "contextMenu"
+ });
+ dojo.connect(contextMenu, "_openMyself", this, function(e){
+ var node = dijit.getEnclosingWidget(e.target);
+ if(node){
+ var item = node.item;
+ if(this.store.isItem(item.value, true) && !item.parent){
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Add"));
+ });
+ this.lastFocused = node;
+ // TODO: Root Node - allow Edit when mutli-value editing is possible
+ }else if(item.value && typeof item.value == 'object' && !(item.value instanceof Date)){
+ // an object that's not a Date - could be a store item
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Add") && (widget.label != "Delete"));
+ });
+ this.lastFocused = node;
+ // TODO: Object - allow Edit when mutli-value editing is possible
+ }else if(item.property && dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){ // id node
+ this.focusNode(node);
+ alert("Cannot modify an Identifier node.");
+ }else if(item.addNew){
+ this.focusNode(node);
+ }else{
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Edit") && (widget.label != "Delete"));
+ })
+ // this won't focus the node but gives us a way to reference the node
+ this.lastFocused = node;
+ }
+ }
+ });
+ contextMenu.addChild(new dijit.MenuItem({label: "Add", onClick: dojo.hitch(this, "_addProperty")}));
+ contextMenu.addChild(new dijit.MenuItem({label: "Edit", onClick: dojo.hitch(this, "_editProperty")}));
+ contextMenu.addChild(new dijit.MenuItem({label: "Delete", onClick: dojo.hitch(this, "_destroyProperty")}));
+ contextMenu.startup();
+ },
+ store: null,
+ setStore: function(store){
+ this.store = store;
+ var self = this;
+ if(this._editDialog){
+ this._editDialog.destroyRecursive();
+ delete this._editDialog;
+ }
+ // i think we should just destroy this._editDialog and let _createEditDialog take care of everything
+ // once it gets called again by either _editProperty or _addProperty - it will create everything again
+ // using the new store. this way we don't need to keep track of what is in the dialog if we change it.
+ /*if(this._editDialog && this.useSelect){
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("store", store);
+ });
+ }*/
+ dojo.connect(store, "onSet", function(item, attribute, oldValue, newValue){
+ var nodes, i, identity = self.store.getIdentity(item);
+ nodes = self._modelNodeIdMap[identity];
+
+ if(nodes &&
+ (oldValue === undefined || newValue === undefined ||
+ oldValue instanceof Array || newValue instanceof Array || typeof oldValue == 'object' || typeof newValue == 'object')){
+ for(i = 0; i < nodes.length; i++){
+ (function(node){
+ self.model.getChildren(node, function(children){
+ self.model.onChildrenChange(node, children);
+ });
+ })(nodes[i]);
+ }
+ }
+ nodes = self._modelNodePropMap[identity + "." + attribute];
+
+ if(nodes){
+ for(i = 0; i < nodes.length; i++){
+ nodes[i].value = newValue;
+ self.model.onChange(nodes[i]);
+ }
+ }
+ });
+ this.rootNode.setChildItems([]);
+ },
+ setItem: function(item){
+ // this is called to show a different item
+
+ // reset the maps, for the root getIdentity is not called, so we pre-initialize it here
+ (this._modelNodeIdMap = {})[this.store.getIdentity(item)] = [this.rootModelNode];
+ this._modelNodePropMap = {};
+
+ this.rootModelNode.value = item;
+ var self = this;
+ this.model.getChildren(this.rootModelNode, function(children){
+ self.rootNode.setChildItems(children);
+ });
+
+ },
+ refreshItem: function(){
+ this.setItem(this.rootModelNode.value);
+ },
+ _createEditDialog: function(){
+ this._editDialog = new dijit.Dialog({
+ title: "Edit Property",
+ execute: dojo.hitch(this, "_updateItem"),
+ preload: true
+ });
+ this._editDialog.placeAt(dojo.body());
+ this._editDialog.startup();
+
+ // handle for dialog content
+ var pane = dojo.doc.createElement('div');
+
+ // label for property
+ var labelProp = dojo.doc.createElement('label');
+ dojo.attr(labelProp, "for", "property");
+ dojo.style(labelProp, "fontWeight", "bold");
+ dojo.attr(labelProp, "innerHTML", "Property:")
+ pane.appendChild(labelProp);
+
+ // property name field
+ var propName = new dijit.form.ValidationTextBox({
+ name: "property",
+ value: "",
+ required: true,
+ disabled: true
+ }).placeAt(pane);
+
+ pane.appendChild(dojo.doc.createElement("br"));
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // radio button for "value"
+ var value = new dijit.form.RadioButton({
+ name: "itemType",
+ value: "value",
+ onClick: dojo.hitch(this, function(){this._enableFields("value");})
+ }).placeAt(pane);
+
+ // label for value
+ var labelVal = dojo.doc.createElement('label');
+ dojo.attr(labelVal, "for", "value");
+ dojo.attr(labelVal, "innerHTML", "Value (JSON):")
+ pane.appendChild(labelVal);
+
+ // container for value fields
+ var valueDiv = dojo.doc.createElement("div");
+ dojo.addClass(valueDiv, "value");
+
+ // textarea
+ var textarea = new dijit.form.Textarea({
+ name: "jsonVal"
+ }).placeAt(valueDiv);
+ pane.appendChild(valueDiv);
+
+ // radio button for "reference"
+ var reference = new dijit.form.RadioButton({
+ name: "itemType",
+ value: "reference",
+ onClick: dojo.hitch(this, function(){this._enableFields("reference");})
+ }).placeAt(pane);
+
+ // label for reference
+ var labelRef = dojo.doc.createElement('label');
+ dojo.attr(labelRef, "for", "_reference");
+ dojo.attr(labelRef, "innerHTML", "Reference (ID):")
+ pane.appendChild(labelRef);
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // container for reference fields
+ var refDiv = dojo.doc.createElement("div");
+ dojo.addClass(refDiv, "reference");
+
+ if(this.useSelect){
+ // filteringselect
+ // TODO: see if there is a way to sort the items in this list
+ var refSelect = new dijit.form.FilteringSelect({
+ name: "_reference",
+ store: this.store,
+ searchAttr: this.refSelectSearchAttr || this.store.getIdentityAttributes()[0],
+ required: false,
+ value: null, // need to file a ticket about the fetch that happens when declared with value: null
+ pageSize: 10
+ }).placeAt(refDiv);
+ }else{
+ var refTextbox = new dijit.form.ValidationTextBox({
+ name: "_reference",
+ value: "",
+ promptMessage: "Enter the ID of the item to reference",
+ isValid: dojo.hitch(this, function(isFocused){
+ // don't validate while it's focused
+ return true;//isFocused || this.store.getItemByIdentity(this._editDialog.attr("value")._reference);
+ })
+ }).placeAt(refDiv);
+ }
+ pane.appendChild(refDiv);
+ pane.appendChild(dojo.doc.createElement("br"));
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // buttons
+ var buttons = document.createElement('div');
+ buttons.setAttribute("dir", "rtl");
+ var cancelButton = new dijit.form.Button({type: "reset", label: "Cancel"}).placeAt(buttons);
+ cancelButton.onClick = dojo.hitch(this._editDialog, "onCancel");
+ var okButton = new dijit.form.Button({type: "submit", label: "OK"}).placeAt(buttons);
+ pane.appendChild(buttons);
+
+ this._editDialog.attr("content", pane);
+ },
+ _enableFields: function(selection){
+ // enables/disables fields based on whether the value in this._editDialog is a reference or a primitive value
+ switch(selection){
+ case "reference":
+ dojo.query(".value [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", true);
+ });
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", false);
+ });
+ break;
+ case "value":
+ dojo.query(".value [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", false);
+ });
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", true);
+ });
+ break;
+ }
+ },
+ _updateItem: function(vals){
+ // a single "execute" function that handles adding and editing of values and references.
+ var node, item, val, storeItemVal, editingItem = this._editDialog.attr("title") == "Edit Property";
+ var editDialog = this._editDialog;
+ var store = this.store;
+ function setValue(){
+ try{
+ var itemVal, propPath = [];
+ var prop = vals.property;
+ if(editingItem){
+ while(!store.isItem(item.parent, true)){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ if(propPath.length == 0){
+ // working with an item attribute already
+ store.setValue(item.parent, item.property, val);
+ }else{
+ // need to walk back down the item property to the object
+ storeItemVal = getValue(store, item.parent, item.property);
+ if(storeItemVal instanceof Array){
+ // create a copy for modification
+ storeItemVal = storeItemVal.concat();
+ }
+ itemVal = storeItemVal;
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ itemVal[propPath] = val; // this change is reflected in storeItemVal as well
+ store.setValue(item.parent, item.property, storeItemVal);
+ }
+ }else{
+ // adding a property
+ if(store.isItem(value, true)){
+ // adding a top-level property to an item
+ if(!store.isItemLoaded(value)){
+ // fetch the value and see if it is an array
+ store.loadItem({
+ item: value,
+ onItem: function(loadedItem){
+ if(loadedItem instanceof Array){
+ prop = loadedItem.length;
+ }
+ store.setValue(loadedItem, prop, val);
+ }
+ });
+ }else{
+ if(value instanceof Array){
+ prop = value.length;
+ }
+ store.setValue(value, prop, val);
+ }
+ }else{
+ // adding a property to a lower level in an item
+ if(item.value instanceof Array){
+ propPath.push(item.value.length);
+ }else{
+ propPath.push(vals.property);
+ }
+ while(!store.isItem(item.parent, true)){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ storeItemVal = getValue(store, item.parent, item.property);
+ itemVal = storeItemVal;
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ itemVal[propPath] = val;
+ store.setValue(item.parent, item.property, storeItemVal);
+ }
+ }
+ }catch(e){
+ alert(e);
+ }
+ }
+
+ if(editDialog.validate()){
+ node = this.lastFocused;
+ item = node.item;
+ var value = item.value;
+ // var property = null;
+ if(item.addNew){
+ // we are adding a property to the parent item
+ // the real value of the parent is in the parent property of the lastFocused item
+ // this.lastFocused.getParent().item.value may be a reference to an item
+ value = node.item.parent;
+ node = node.getParent();
+ item = node.item;
+ }
+ val = null;
+ switch(vals.itemType){
+ case "reference":
+ this.store.fetchItemByIdentity({identity:vals._reference,
+ onItem:function(item){
+ val = item;
+ setValue();
+ },
+ onError:function(){
+ alert("The id could not be found");
+ }
+ });
+ break;
+ case "value":
+ var jsonVal = vals.jsonVal;
+ val = dojo.fromJson(jsonVal);
+ // ifit is a function we want to preserve the source (comments, et al)
+ if(typeof val == 'function'){
+ val.toString = function(){
+ return jsonVal;
+ }
+ }
+ setValue();
+ break;
+ }
+ }else{
+ // the form didn't validate - show it again.
+ editDialog.show();
+ }
+ },
+ _editProperty: function(){
+ // this mixin stops us polluting the tree item with jsonVal etc.
+ // FIXME: if a store identifies items by instanceof checks, this will fail
+ var item = dojo.mixin({}, this.lastFocused.item);
+ // create the dialog or reset it if it already exists
+ if(!this._editDialog){
+ this._createEditDialog();
+ }else{
+ this._editDialog.reset();
+ }
+ // not allowed to edit an item's id - so check for that and stop it.
+ if(dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot Edit an Identifier!");
+ }else{
+ this._editDialog.attr("title", "Edit Property");
+ // make sure the property input is disabled
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", true);
+ if(this.store.isItem(item.value, true)){
+ // root node || Item reference
+ if(item.parent){
+ // Item reference
+ item.itemType = "reference";
+ this._enableFields(item.itemType);
+ item._reference = this.store.getIdentity(item.value);
+ this._editDialog.attr("value", item);
+ this._editDialog.show();
+ } // else root node
+ }else{
+ if(item.value && typeof item.value == 'object' && !(item.value instanceof Date)){
+ // item.value is an object but it's NOT an item from the store - no-op
+ // only allow editing on a property not on the node that represents the object/array
+ }else{
+ // this is a primitive
+ item.itemType = "value";
+ this._enableFields(item.itemType);
+ item.jsonVal = typeof item.value == 'function' ?
+ // use the plain toString for functions, dojo.toJson doesn't support functions
+ item.value.toString() :
+ item.value instanceof Date ?
+ // A json-ish form of a date:
+ 'new Date("' + item.value + '")' :
+ dojo.toJson(item.value);
+ this._editDialog.attr("value", item);
+ this._editDialog.show();
+ }
+ }
+ }
+ },
+ _destroyProperty: function(){
+ var node = this.lastFocused;
+ var item = node.item;
+ var propPath = [];
+ // we have to walk up the tree to the item before we can know if we're working with the identifier
+ while(!this.store.isItem(item.parent, true) || item.parent instanceof Array){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ // this will prevent any part of the identifier from being changed
+ if(dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot Delete an Identifier!");
+ }else{
+ try{
+ if(propPath.length > 0){
+ // not deleting a top-level property of an item so get the top-level store item to change
+ var itemVal, storeItemVal = getValue(this.store, item.parent, item.property);
+ itemVal = storeItemVal;
+ // walk back down the object if needed
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ // delete the property
+ if(dojo.isArray(itemVal)){
+ // the value being deleted represents an array element
+ itemVal.splice(propPath, 1);
+ }else{
+ // object property
+ delete itemVal[propPath];
+ }
+ // save it back to the store
+ this.store.setValue(item.parent, item.property, storeItemVal);
+ }else{
+ // deleting an item property
+ this.store.unsetAttribute(item.parent, item.property);
+ }
+ }catch(e){
+ alert(e);
+ }
+ }
+ },
+ _addProperty: function(){
+ // item is what we are adding a property to
+ var item = this.lastFocused.item;
+ // value is the real value of the item - not a reference to a store item
+ var value = item.value;
+ var showDialog = dojo.hitch(this, function(){
+ var property = null;
+ if(!this._editDialog){
+ this._createEditDialog();
+ }else{
+ this._editDialog.reset();
+ }
+ // are we adding another item to an array?
+ if(value instanceof Array){
+ // preset the property to the next index in the array and disable the property field
+ property = value.length;
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", true);
+ }else{
+ // enable the property TextBox
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", false);
+ }
+ this._editDialog.attr("title", "Add Property");
+ // default to a value type
+ this._enableFields("value");
+ this._editDialog.attr("value", {itemType: "value", property: property});
+ this._editDialog.show();
+ });
+
+ if(item.addNew){
+ // we are adding a property to the parent item
+ item = this.lastFocused.getParent().item;
+ // the real value of the parent is in the parent property of the lastFocused item
+ // this.lastFocused.getParent().item.value may be a reference to an item
+ value = this.lastFocused.item.parent;
+ }
+ if(item.property && dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot add properties to an ID node!");
+ }else{
+ // ifthe value is an item then we need to get the item's value
+ if(this.store.isItem(value, true) && !this.store.isItemLoaded(value)){
+ // fetch the value and see if it is an array
+ this.store.loadItem({
+ item: value,
+ onItem: function(loadedItem){
+ value = loadedItem;
+ showDialog();
+ }
+ });
+ }else{
+ showDialog();
+ }
+//
+ }
+ }
+});
+})();
+
+
+}
diff --git a/js/dojo-1.6/dojox/data/ItemExplorer.xd.js b/js/dojo-1.6/dojox/data/ItemExplorer.xd.js
new file mode 100644
index 0000000..0d69b43
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ItemExplorer.xd.js
@@ -0,0 +1,649 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.ItemExplorer"],
+["require", "dijit.Tree"],
+["require", "dijit.Dialog"],
+["require", "dijit.Menu"],
+["require", "dijit.form.ValidationTextBox"],
+["require", "dijit.form.Textarea"],
+["require", "dijit.form.Button"],
+["require", "dijit.form.CheckBox"],
+["require", "dijit.form.FilteringSelect"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.ItemExplorer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ItemExplorer"] = true;
+dojo.provide("dojox.data.ItemExplorer");
+dojo.require("dijit.Tree");
+dojo.require("dijit.Dialog");
+dojo.require("dijit.Menu");
+dojo.require("dijit.form.ValidationTextBox");
+dojo.require("dijit.form.Textarea");
+dojo.require("dijit.form.Button");
+dojo.require("dijit.form.CheckBox");
+dojo.require("dijit.form.FilteringSelect");
+
+(function(){
+ var getValue = function(store, item, prop){
+ var value = store.getValues(item, prop);
+ if(value.length < 2){
+ value = store.getValue(item, prop);
+ }
+ return value;
+ }
+
+dojo.declare("dojox.data.ItemExplorer", dijit.Tree, {
+ useSelect: false,
+ refSelectSearchAttr: null,
+ constructor: function(options){
+ dojo.mixin(this, options);
+ var self = this;
+ var initialRootValue = {};
+ var root = this.rootModelNode = {value:initialRootValue,id:"root"};
+
+ this._modelNodeIdMap = {};
+ this._modelNodePropMap = {};
+ var nextId = 1;
+ this.model = {
+ getRoot: function(onItem){
+ onItem(root);
+ },
+ mayHaveChildren: function(modelNode){
+ return modelNode.value && typeof modelNode.value == 'object' && !(modelNode.value instanceof Date);
+ },
+ getChildren: function(parentModelNode, onComplete, onError){
+ var keys, parent, item = parentModelNode.value;
+ var children = [];
+ if(item == initialRootValue){
+ onComplete([]);
+ return;
+ }
+ var isItem = self.store && self.store.isItem(item, true);
+ if(isItem && !self.store.isItemLoaded(item)){
+ // if it is not loaded, do so now.
+ self.store.loadItem({
+ item:item,
+ onItem:function(loadedItem){
+ item = loadedItem;
+ enumerate();
+ }
+ });
+ }else{
+ enumerate();
+ }
+ function enumerate(){
+ // once loaded, enumerate the keys
+ if(isItem){
+ // get the properties through the dojo data API
+ keys = self.store.getAttributes(item);
+ parent = item;
+ }else if(item && typeof item == 'object'){
+ parent = parentModelNode.value;
+ keys = [];
+ // also we want to be able to drill down into plain JS objects/arrays
+ for(var i in item){
+ if(item.hasOwnProperty(i) && i != '__id' && i != '__clientId'){
+ keys.push(i);
+ }
+ }
+ }
+ if(keys){
+ for(var key, k=0; key = keys[k++];){
+ children.push({
+ property:key,
+ value: isItem ? getValue(self.store, item, key) : item[key],
+ parent: parent});
+ }
+ children.push({addNew:true, parent: parent, parentNode : parentModelNode});
+ }
+ onComplete(children);
+ }
+ },
+ getIdentity: function(modelNode){
+ if(!modelNode.id){
+ if(modelNode.addNew){
+ modelNode.property = "--addNew";
+ }
+ modelNode.id = nextId++;
+ if(self.store){
+ if(self.store.isItem(modelNode.value)){
+ var identity = self.store.getIdentity(modelNode.value);
+ (self._modelNodeIdMap[identity] = self._modelNodeIdMap[identity] || []).push(modelNode);
+ }
+ if(modelNode.parent){
+ identity = self.store.getIdentity(modelNode.parent) + '.' + modelNode.property;
+ (self._modelNodePropMap[identity] = self._modelNodePropMap[identity] || []).push(modelNode);
+ }
+ }
+ }
+ return modelNode.id;
+ },
+ getLabel: function(modelNode){
+ return modelNode === root ?
+ "Object Properties" :
+ modelNode.addNew ? (modelNode.parent instanceof Array ? "Add new value" : "Add new property") :
+ modelNode.property + ": " +
+ (modelNode.value instanceof Array ? "(" + modelNode.value.length + " elements)" : modelNode.value);
+ },
+ onChildrenChange: function(modelNode){
+ },
+ onChange: function(modelNode){
+ }
+ };
+ },
+ postCreate: function(){
+ this.inherited(arguments);
+ // handle the clicking on the "add new property item"
+ dojo.connect(this, "onClick", function(modelNode, treeNode){
+ this.lastFocused = treeNode;
+ if(modelNode.addNew){
+ //this.focusNode(treeNode.getParent());
+ this._addProperty();
+ }else{
+ this._editProperty();
+ }
+ });
+ var contextMenu = new dijit.Menu({
+ targetNodeIds: [this.rootNode.domNode],
+ id: "contextMenu"
+ });
+ dojo.connect(contextMenu, "_openMyself", this, function(e){
+ var node = dijit.getEnclosingWidget(e.target);
+ if(node){
+ var item = node.item;
+ if(this.store.isItem(item.value, true) && !item.parent){
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Add"));
+ });
+ this.lastFocused = node;
+ // TODO: Root Node - allow Edit when mutli-value editing is possible
+ }else if(item.value && typeof item.value == 'object' && !(item.value instanceof Date)){
+ // an object that's not a Date - could be a store item
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Add") && (widget.label != "Delete"));
+ });
+ this.lastFocused = node;
+ // TODO: Object - allow Edit when mutli-value editing is possible
+ }else if(item.property && dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){ // id node
+ this.focusNode(node);
+ alert("Cannot modify an Identifier node.");
+ }else if(item.addNew){
+ this.focusNode(node);
+ }else{
+ dojo.forEach(contextMenu.getChildren(), function(widget){
+ widget.attr("disabled", (widget.label != "Edit") && (widget.label != "Delete"));
+ })
+ // this won't focus the node but gives us a way to reference the node
+ this.lastFocused = node;
+ }
+ }
+ });
+ contextMenu.addChild(new dijit.MenuItem({label: "Add", onClick: dojo.hitch(this, "_addProperty")}));
+ contextMenu.addChild(new dijit.MenuItem({label: "Edit", onClick: dojo.hitch(this, "_editProperty")}));
+ contextMenu.addChild(new dijit.MenuItem({label: "Delete", onClick: dojo.hitch(this, "_destroyProperty")}));
+ contextMenu.startup();
+ },
+ store: null,
+ setStore: function(store){
+ this.store = store;
+ var self = this;
+ if(this._editDialog){
+ this._editDialog.destroyRecursive();
+ delete this._editDialog;
+ }
+ // i think we should just destroy this._editDialog and let _createEditDialog take care of everything
+ // once it gets called again by either _editProperty or _addProperty - it will create everything again
+ // using the new store. this way we don't need to keep track of what is in the dialog if we change it.
+ /*if(this._editDialog && this.useSelect){
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("store", store);
+ });
+ }*/
+ dojo.connect(store, "onSet", function(item, attribute, oldValue, newValue){
+ var nodes, i, identity = self.store.getIdentity(item);
+ nodes = self._modelNodeIdMap[identity];
+
+ if(nodes &&
+ (oldValue === undefined || newValue === undefined ||
+ oldValue instanceof Array || newValue instanceof Array || typeof oldValue == 'object' || typeof newValue == 'object')){
+ for(i = 0; i < nodes.length; i++){
+ (function(node){
+ self.model.getChildren(node, function(children){
+ self.model.onChildrenChange(node, children);
+ });
+ })(nodes[i]);
+ }
+ }
+ nodes = self._modelNodePropMap[identity + "." + attribute];
+
+ if(nodes){
+ for(i = 0; i < nodes.length; i++){
+ nodes[i].value = newValue;
+ self.model.onChange(nodes[i]);
+ }
+ }
+ });
+ this.rootNode.setChildItems([]);
+ },
+ setItem: function(item){
+ // this is called to show a different item
+
+ // reset the maps, for the root getIdentity is not called, so we pre-initialize it here
+ (this._modelNodeIdMap = {})[this.store.getIdentity(item)] = [this.rootModelNode];
+ this._modelNodePropMap = {};
+
+ this.rootModelNode.value = item;
+ var self = this;
+ this.model.getChildren(this.rootModelNode, function(children){
+ self.rootNode.setChildItems(children);
+ });
+
+ },
+ refreshItem: function(){
+ this.setItem(this.rootModelNode.value);
+ },
+ _createEditDialog: function(){
+ this._editDialog = new dijit.Dialog({
+ title: "Edit Property",
+ execute: dojo.hitch(this, "_updateItem"),
+ preload: true
+ });
+ this._editDialog.placeAt(dojo.body());
+ this._editDialog.startup();
+
+ // handle for dialog content
+ var pane = dojo.doc.createElement('div');
+
+ // label for property
+ var labelProp = dojo.doc.createElement('label');
+ dojo.attr(labelProp, "for", "property");
+ dojo.style(labelProp, "fontWeight", "bold");
+ dojo.attr(labelProp, "innerHTML", "Property:")
+ pane.appendChild(labelProp);
+
+ // property name field
+ var propName = new dijit.form.ValidationTextBox({
+ name: "property",
+ value: "",
+ required: true,
+ disabled: true
+ }).placeAt(pane);
+
+ pane.appendChild(dojo.doc.createElement("br"));
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // radio button for "value"
+ var value = new dijit.form.RadioButton({
+ name: "itemType",
+ value: "value",
+ onClick: dojo.hitch(this, function(){this._enableFields("value");})
+ }).placeAt(pane);
+
+ // label for value
+ var labelVal = dojo.doc.createElement('label');
+ dojo.attr(labelVal, "for", "value");
+ dojo.attr(labelVal, "innerHTML", "Value (JSON):")
+ pane.appendChild(labelVal);
+
+ // container for value fields
+ var valueDiv = dojo.doc.createElement("div");
+ dojo.addClass(valueDiv, "value");
+
+ // textarea
+ var textarea = new dijit.form.Textarea({
+ name: "jsonVal"
+ }).placeAt(valueDiv);
+ pane.appendChild(valueDiv);
+
+ // radio button for "reference"
+ var reference = new dijit.form.RadioButton({
+ name: "itemType",
+ value: "reference",
+ onClick: dojo.hitch(this, function(){this._enableFields("reference");})
+ }).placeAt(pane);
+
+ // label for reference
+ var labelRef = dojo.doc.createElement('label');
+ dojo.attr(labelRef, "for", "_reference");
+ dojo.attr(labelRef, "innerHTML", "Reference (ID):")
+ pane.appendChild(labelRef);
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // container for reference fields
+ var refDiv = dojo.doc.createElement("div");
+ dojo.addClass(refDiv, "reference");
+
+ if(this.useSelect){
+ // filteringselect
+ // TODO: see if there is a way to sort the items in this list
+ var refSelect = new dijit.form.FilteringSelect({
+ name: "_reference",
+ store: this.store,
+ searchAttr: this.refSelectSearchAttr || this.store.getIdentityAttributes()[0],
+ required: false,
+ value: null, // need to file a ticket about the fetch that happens when declared with value: null
+ pageSize: 10
+ }).placeAt(refDiv);
+ }else{
+ var refTextbox = new dijit.form.ValidationTextBox({
+ name: "_reference",
+ value: "",
+ promptMessage: "Enter the ID of the item to reference",
+ isValid: dojo.hitch(this, function(isFocused){
+ // don't validate while it's focused
+ return true;//isFocused || this.store.getItemByIdentity(this._editDialog.attr("value")._reference);
+ })
+ }).placeAt(refDiv);
+ }
+ pane.appendChild(refDiv);
+ pane.appendChild(dojo.doc.createElement("br"));
+ pane.appendChild(dojo.doc.createElement("br"));
+
+ // buttons
+ var buttons = document.createElement('div');
+ buttons.setAttribute("dir", "rtl");
+ var cancelButton = new dijit.form.Button({type: "reset", label: "Cancel"}).placeAt(buttons);
+ cancelButton.onClick = dojo.hitch(this._editDialog, "onCancel");
+ var okButton = new dijit.form.Button({type: "submit", label: "OK"}).placeAt(buttons);
+ pane.appendChild(buttons);
+
+ this._editDialog.attr("content", pane);
+ },
+ _enableFields: function(selection){
+ // enables/disables fields based on whether the value in this._editDialog is a reference or a primitive value
+ switch(selection){
+ case "reference":
+ dojo.query(".value [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", true);
+ });
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", false);
+ });
+ break;
+ case "value":
+ dojo.query(".value [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", false);
+ });
+ dojo.query(".reference [widgetId]", this._editDialog.containerNode).forEach(function(node){
+ dijit.getEnclosingWidget(node).attr("disabled", true);
+ });
+ break;
+ }
+ },
+ _updateItem: function(vals){
+ // a single "execute" function that handles adding and editing of values and references.
+ var node, item, val, storeItemVal, editingItem = this._editDialog.attr("title") == "Edit Property";
+ var editDialog = this._editDialog;
+ var store = this.store;
+ function setValue(){
+ try{
+ var itemVal, propPath = [];
+ var prop = vals.property;
+ if(editingItem){
+ while(!store.isItem(item.parent, true)){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ if(propPath.length == 0){
+ // working with an item attribute already
+ store.setValue(item.parent, item.property, val);
+ }else{
+ // need to walk back down the item property to the object
+ storeItemVal = getValue(store, item.parent, item.property);
+ if(storeItemVal instanceof Array){
+ // create a copy for modification
+ storeItemVal = storeItemVal.concat();
+ }
+ itemVal = storeItemVal;
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ itemVal[propPath] = val; // this change is reflected in storeItemVal as well
+ store.setValue(item.parent, item.property, storeItemVal);
+ }
+ }else{
+ // adding a property
+ if(store.isItem(value, true)){
+ // adding a top-level property to an item
+ if(!store.isItemLoaded(value)){
+ // fetch the value and see if it is an array
+ store.loadItem({
+ item: value,
+ onItem: function(loadedItem){
+ if(loadedItem instanceof Array){
+ prop = loadedItem.length;
+ }
+ store.setValue(loadedItem, prop, val);
+ }
+ });
+ }else{
+ if(value instanceof Array){
+ prop = value.length;
+ }
+ store.setValue(value, prop, val);
+ }
+ }else{
+ // adding a property to a lower level in an item
+ if(item.value instanceof Array){
+ propPath.push(item.value.length);
+ }else{
+ propPath.push(vals.property);
+ }
+ while(!store.isItem(item.parent, true)){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ storeItemVal = getValue(store, item.parent, item.property);
+ itemVal = storeItemVal;
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ itemVal[propPath] = val;
+ store.setValue(item.parent, item.property, storeItemVal);
+ }
+ }
+ }catch(e){
+ alert(e);
+ }
+ }
+
+ if(editDialog.validate()){
+ node = this.lastFocused;
+ item = node.item;
+ var value = item.value;
+ // var property = null;
+ if(item.addNew){
+ // we are adding a property to the parent item
+ // the real value of the parent is in the parent property of the lastFocused item
+ // this.lastFocused.getParent().item.value may be a reference to an item
+ value = node.item.parent;
+ node = node.getParent();
+ item = node.item;
+ }
+ val = null;
+ switch(vals.itemType){
+ case "reference":
+ this.store.fetchItemByIdentity({identity:vals._reference,
+ onItem:function(item){
+ val = item;
+ setValue();
+ },
+ onError:function(){
+ alert("The id could not be found");
+ }
+ });
+ break;
+ case "value":
+ var jsonVal = vals.jsonVal;
+ val = dojo.fromJson(jsonVal);
+ // ifit is a function we want to preserve the source (comments, et al)
+ if(typeof val == 'function'){
+ val.toString = function(){
+ return jsonVal;
+ }
+ }
+ setValue();
+ break;
+ }
+ }else{
+ // the form didn't validate - show it again.
+ editDialog.show();
+ }
+ },
+ _editProperty: function(){
+ // this mixin stops us polluting the tree item with jsonVal etc.
+ // FIXME: if a store identifies items by instanceof checks, this will fail
+ var item = dojo.mixin({}, this.lastFocused.item);
+ // create the dialog or reset it if it already exists
+ if(!this._editDialog){
+ this._createEditDialog();
+ }else{
+ this._editDialog.reset();
+ }
+ // not allowed to edit an item's id - so check for that and stop it.
+ if(dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot Edit an Identifier!");
+ }else{
+ this._editDialog.attr("title", "Edit Property");
+ // make sure the property input is disabled
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", true);
+ if(this.store.isItem(item.value, true)){
+ // root node || Item reference
+ if(item.parent){
+ // Item reference
+ item.itemType = "reference";
+ this._enableFields(item.itemType);
+ item._reference = this.store.getIdentity(item.value);
+ this._editDialog.attr("value", item);
+ this._editDialog.show();
+ } // else root node
+ }else{
+ if(item.value && typeof item.value == 'object' && !(item.value instanceof Date)){
+ // item.value is an object but it's NOT an item from the store - no-op
+ // only allow editing on a property not on the node that represents the object/array
+ }else{
+ // this is a primitive
+ item.itemType = "value";
+ this._enableFields(item.itemType);
+ item.jsonVal = typeof item.value == 'function' ?
+ // use the plain toString for functions, dojo.toJson doesn't support functions
+ item.value.toString() :
+ item.value instanceof Date ?
+ // A json-ish form of a date:
+ 'new Date("' + item.value + '")' :
+ dojo.toJson(item.value);
+ this._editDialog.attr("value", item);
+ this._editDialog.show();
+ }
+ }
+ }
+ },
+ _destroyProperty: function(){
+ var node = this.lastFocused;
+ var item = node.item;
+ var propPath = [];
+ // we have to walk up the tree to the item before we can know if we're working with the identifier
+ while(!this.store.isItem(item.parent, true) || item.parent instanceof Array){
+ node = node.getParent();
+ propPath.push(item.property);
+ item = node.item;
+ }
+ // this will prevent any part of the identifier from being changed
+ if(dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot Delete an Identifier!");
+ }else{
+ try{
+ if(propPath.length > 0){
+ // not deleting a top-level property of an item so get the top-level store item to change
+ var itemVal, storeItemVal = getValue(this.store, item.parent, item.property);
+ itemVal = storeItemVal;
+ // walk back down the object if needed
+ while(propPath.length > 1){
+ itemVal = itemVal[propPath.pop()];
+ }
+ // delete the property
+ if(dojo.isArray(itemVal)){
+ // the value being deleted represents an array element
+ itemVal.splice(propPath, 1);
+ }else{
+ // object property
+ delete itemVal[propPath];
+ }
+ // save it back to the store
+ this.store.setValue(item.parent, item.property, storeItemVal);
+ }else{
+ // deleting an item property
+ this.store.unsetAttribute(item.parent, item.property);
+ }
+ }catch(e){
+ alert(e);
+ }
+ }
+ },
+ _addProperty: function(){
+ // item is what we are adding a property to
+ var item = this.lastFocused.item;
+ // value is the real value of the item - not a reference to a store item
+ var value = item.value;
+ var showDialog = dojo.hitch(this, function(){
+ var property = null;
+ if(!this._editDialog){
+ this._createEditDialog();
+ }else{
+ this._editDialog.reset();
+ }
+ // are we adding another item to an array?
+ if(value instanceof Array){
+ // preset the property to the next index in the array and disable the property field
+ property = value.length;
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", true);
+ }else{
+ // enable the property TextBox
+ dijit.getEnclosingWidget(dojo.query("input", this._editDialog.containerNode)[0]).attr("disabled", false);
+ }
+ this._editDialog.attr("title", "Add Property");
+ // default to a value type
+ this._enableFields("value");
+ this._editDialog.attr("value", {itemType: "value", property: property});
+ this._editDialog.show();
+ });
+
+ if(item.addNew){
+ // we are adding a property to the parent item
+ item = this.lastFocused.getParent().item;
+ // the real value of the parent is in the parent property of the lastFocused item
+ // this.lastFocused.getParent().item.value may be a reference to an item
+ value = this.lastFocused.item.parent;
+ }
+ if(item.property && dojo.indexOf(this.store.getIdentityAttributes(), item.property) >= 0){
+ alert("Cannot add properties to an ID node!");
+ }else{
+ // ifthe value is an item then we need to get the item's value
+ if(this.store.isItem(value, true) && !this.store.isItemLoaded(value)){
+ // fetch the value and see if it is an array
+ this.store.loadItem({
+ item: value,
+ onItem: function(loadedItem){
+ value = loadedItem;
+ showDialog();
+ }
+ });
+ }else{
+ showDialog();
+ }
+//
+ }
+ }
+});
+})();
+
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/JsonQueryRestStore.js b/js/dojo-1.6/dojox/data/JsonQueryRestStore.js
new file mode 100644
index 0000000..c4f96b0
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/JsonQueryRestStore.js
@@ -0,0 +1,28 @@
+/*
+ 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["dojox.data.JsonQueryRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.JsonQueryRestStore"] = true;
+dojo.provide("dojox.data.JsonQueryRestStore");
+dojo.require("dojox.data.JsonRestStore");
+dojo.require("dojox.data.util.JsonQuery");
+
+
+
+dojo.requireIf(!!dojox.data.ClientFilter,"dojox.json.query"); // this is so we can perform queries locally
+
+// this is an extension of JsonRestStore to convert object attribute queries to
+// JSONQuery/JSONPath syntax to be sent to the server. This also enables
+// JSONQuery/JSONPath queries to be performed locally if dojox.data.ClientFilter
+// has been loaded
+dojo.declare("dojox.data.JsonQueryRestStore",[dojox.data.JsonRestStore,dojox.data.util.JsonQuery],{
+ matchesQuery: function(item,request){
+ return item.__id && (item.__id.indexOf("#") == -1) && this.inherited(arguments);
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/JsonQueryRestStore.xd.js b/js/dojo-1.6/dojox/data/JsonQueryRestStore.xd.js
new file mode 100644
index 0000000..752249e
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/JsonQueryRestStore.xd.js
@@ -0,0 +1,35 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.JsonQueryRestStore"],
+["require", "dojox.data.JsonRestStore"],
+["require", "dojox.data.util.JsonQuery"],
+["requireIf", !!dojox.data.ClientFilter,"dojox.json.query"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.JsonQueryRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.JsonQueryRestStore"] = true;
+dojo.provide("dojox.data.JsonQueryRestStore");
+dojo.require("dojox.data.JsonRestStore");
+dojo.require("dojox.data.util.JsonQuery");
+
+
+
+dojo.requireIf(!!dojox.data.ClientFilter,"dojox.json.query"); // this is so we can perform queries locally
+
+// this is an extension of JsonRestStore to convert object attribute queries to
+// JSONQuery/JSONPath syntax to be sent to the server. This also enables
+// JSONQuery/JSONPath queries to be performed locally if dojox.data.ClientFilter
+// has been loaded
+dojo.declare("dojox.data.JsonQueryRestStore",[dojox.data.JsonRestStore,dojox.data.util.JsonQuery],{
+ matchesQuery: function(item,request){
+ return item.__id && (item.__id.indexOf("#") == -1) && this.inherited(arguments);
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/JsonRestStore.js b/js/dojo-1.6/dojox/data/JsonRestStore.js
new file mode 100644
index 0000000..1bec8a3
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/JsonRestStore.js
@@ -0,0 +1,494 @@
+/*
+ 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["dojox.data.JsonRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.JsonRestStore"] = true;
+dojo.provide("dojox.data.JsonRestStore");
+dojo.require("dojox.rpc.JsonRest");
+dojo.require("dojox.data.ServiceStore");
+
+
+
+dojo.declare("dojox.data.JsonRestStore",
+ dojox.data.ServiceStore,
+ {
+ constructor: function(options){
+ //summary:
+ // JsonRestStore is a Dojo Data store interface to JSON HTTP/REST web
+ // storage services that support read and write through GET, PUT, POST, and DELETE.
+ // options:
+ // Keyword arguments
+ //
+ // The *schema* parameter
+ // This is a schema object for this store. This should be JSON Schema format.
+ //
+ // The *service* parameter
+ // This is the service object that is used to retrieve lazy data and save results
+ // The function should be directly callable with a single parameter of an object id to be loaded
+ // The function should also have the following methods:
+ // put(id,value) - puts the value at the given id
+ // post(id,value) - posts (appends) the value at the given id
+ // delete(id) - deletes the value corresponding to the given id
+ // Note that it is critical that the service parses responses as JSON.
+ // If you are using dojox.rpc.Service, the easiest way to make sure this
+ // happens is to make the responses have a content type of
+ // application/json. If you are creating your own service, make sure you
+ // use handleAs: "json" with your XHR requests.
+ //
+ // The *target* parameter
+ // This is the target URL for this Service store. This may be used in place
+ // of a service parameter to connect directly to RESTful URL without
+ // using a dojox.rpc.Service object.
+ //
+ // The *idAttribute* parameter
+ // Defaults to 'id'. The name of the attribute that holds an objects id.
+ // This can be a preexisting id provided by the server.
+ // If an ID isn't already provided when an object
+ // is fetched or added to the store, the autoIdentity system
+ // will generate an id for it and add it to the index.
+ //
+ // The *syncMode* parameter
+ // Setting this to true will set the store to using synchronous calls by default.
+ // Sync calls return their data immediately from the calling function, so
+ // callbacks are unnecessary
+ //
+ // description:
+ // The JsonRestStore will cause all saved modifications to be sent to the server using Rest commands (PUT, POST, or DELETE).
+ // When using a Rest store on a public network, it is important to implement proper security measures to
+ // control access to resources.
+ // On the server side implementing a REST interface means providing GET, PUT, POST, and DELETE handlers.
+ // GET - Retrieve an object or array/result set, this can be by id (like /table/1) or with a
+ // query (like /table/?name=foo).
+ // PUT - This should modify a object, the URL will correspond to the id (like /table/1), and the body will
+ // provide the modified object
+ // POST - This should create a new object. The URL will correspond to the target store (like /table/)
+ // and the body should be the properties of the new object. The server's response should include a
+ // Location header that indicates the id of the newly created object. This id will be used for subsequent
+ // PUT and DELETE requests. JsonRestStore also includes a Content-Location header that indicates
+ // the temporary randomly generated id used by client, and this location is used for subsequent
+ // PUT/DELETEs if no Location header is provided by the server or if a modification is sent prior
+ // to receiving a response from the server.
+ // DELETE - This should delete an object by id.
+ // These articles include more detailed information on using the JsonRestStore:
+ // http://www.sitepen.com/blog/2008/06/13/restful-json-dojo-data/
+ // http://blog.medryx.org/2008/07/24/jsonreststore-overview/
+ //
+ // example:
+ // A JsonRestStore takes a REST service or a URL and uses it the remote communication for a
+ // read/write dojo.data implementation. A JsonRestStore can be created with a simple URL like:
+ // | new JsonRestStore({target:"/MyData/"});
+ // example:
+ // To use a JsonRestStore with a service, you should create a
+ // service with a REST transport. This can be configured with an SMD:
+ // | {
+ // | services: {
+ // | jsonRestStore: {
+ // | transport: "REST",
+ // | envelope: "URL",
+ // | target: "store.php",
+ // | contentType:"application/json",
+ // | parameters: [
+ // | {name: "location", type: "string", optional: true}
+ // | ]
+ // | }
+ // | }
+ // | }
+ // The SMD can then be used to create service, and the service can be passed to a JsonRestStore. For example:
+ // | var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd"));
+ // | var jsonStore = new dojox.data.JsonRestStore({service:myServices.jsonRestStore});
+ // example:
+ // The JsonRestStore also supports lazy loading. References can be made to objects that have not been loaded.
+ // For example if a service returned:
+ // | {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}}
+ // And this object has accessed using the dojo.data API:
+ // | var obj = jsonStore.getValue(myObject,"lazyLoadedObject");
+ // The object would automatically be requested from the server (with an object id of "obj2").
+ //
+
+ dojo.connect(dojox.rpc.Rest._index,"onUpdate",this,function(obj,attrName,oldValue,newValue){
+ var prefix = this.service.servicePath;
+ if(!obj.__id){
+ console.log("no id on updated object ", obj);
+ }else if(obj.__id.substring(0,prefix.length) == prefix){
+ this.onSet(obj,attrName,oldValue,newValue);
+ }
+ });
+ this.idAttribute = this.idAttribute || 'id';// no options about it, we have to have identity
+
+ if(typeof options.target == 'string'){
+ options.target = options.target.match(/\/$/) || this.allowNoTrailingSlash ? options.target : (options.target + '/');
+ if(!this.service){
+ this.service = dojox.rpc.JsonRest.services[options.target] ||
+ dojox.rpc.Rest(options.target, true);
+ // create a default Rest service
+ }
+ }
+
+ dojox.rpc.JsonRest.registerService(this.service, options.target, this.schema);
+ this.schema = this.service._schema = this.schema || this.service._schema || {};
+ // wrap the service with so it goes through JsonRest manager
+ this.service._store = this;
+ this.service.idAsRef = this.idAsRef;
+ this.schema._idAttr = this.idAttribute;
+ var constructor = dojox.rpc.JsonRest.getConstructor(this.service);
+ var self = this;
+ this._constructor = function(data){
+ constructor.call(this, data);
+ self.onNew(this);
+ }
+ this._constructor.prototype = constructor.prototype;
+ this._index = dojox.rpc.Rest._index;
+ },
+
+ // summary:
+ // Will load any schemas referenced content-type header or in Link headers
+ loadReferencedSchema: true,
+ // summary:
+ // Treat objects in queries as partially loaded objects
+ idAsRef: false,
+ referenceIntegrity: true,
+ target:"",
+ // summary:
+ // Allow no trailing slash on target paths. This is generally discouraged since
+ // it creates prevents simple scalar values from being used a relative URLs.
+ // Disabled by default.
+ allowNoTrailingSlash: false,
+ //Write API Support
+ newItem: function(data, parentInfo){
+ // summary:
+ // adds a new item to the store at the specified point.
+ // Takes two parameters, data, and options.
+ //
+ // data: /* object */
+ // The data to be added in as an item.
+ data = new this._constructor(data);
+ if(parentInfo){
+ // get the previous value or any empty array
+ var values = this.getValue(parentInfo.parent,parentInfo.attribute,[]);
+ // set the new value
+ values = values.concat([data]);
+ data.__parent = values;
+ this.setValue(parentInfo.parent, parentInfo.attribute, values);
+ }
+ return data;
+ },
+ deleteItem: function(item){
+ // summary:
+ // deletes item and any references to that item from the store.
+ //
+ // item:
+ // item to delete
+ //
+
+ // If the desire is to delete only one reference, unsetAttribute or
+ // setValue is the way to go.
+ var checked = [];
+ var store = dojox.data._getStoreForItem(item) || this;
+ if(this.referenceIntegrity){
+ // cleanup all references
+ dojox.rpc.JsonRest._saveNotNeeded = true;
+ var index = dojox.rpc.Rest._index;
+ var fixReferences = function(parent){
+ var toSplice;
+ // keep track of the checked ones
+ checked.push(parent);
+ // mark it checked so we don't run into circular loops when encountering cycles
+ parent.__checked = 1;
+ for(var i in parent){
+ if(i.substring(0,2) != "__"){
+ var value = parent[i];
+ if(value == item){
+ if(parent != index){ // make sure we are just operating on real objects
+ if(parent instanceof Array){
+ // mark it as needing to be spliced, don't do it now or it will mess up the index into the array
+ (toSplice = toSplice || []).push(i);
+ }else{
+ // property, just delete it.
+ (dojox.data._getStoreForItem(parent) || store).unsetAttribute(parent, i);
+ }
+ }
+ }else{
+ if((typeof value == 'object') && value){
+ if(!value.__checked){
+ // recursively search
+ fixReferences(value);
+ }
+ if(typeof value.__checked == 'object' && parent != index){
+ // if it is a modified array, we will replace it
+ (dojox.data._getStoreForItem(parent) || store).setValue(parent, i, value.__checked);
+ }
+ }
+ }
+ }
+ }
+ if(toSplice){
+ // we need to splice the deleted item out of these arrays
+ i = toSplice.length;
+ parent = parent.__checked = parent.concat(); // indicates that the array is modified
+ while(i--){
+ parent.splice(toSplice[i], 1);
+ }
+ return parent;
+ }
+ return null;
+ };
+ // start with the index
+ fixReferences(index);
+ dojox.rpc.JsonRest._saveNotNeeded = false;
+ var i = 0;
+ while(checked[i]){
+ // remove the checked marker
+ delete checked[i++].__checked;
+ }
+ }
+ dojox.rpc.JsonRest.deleteObject(item);
+
+ store.onDelete(item);
+ },
+ changing: function(item,_deleting){
+ // summary:
+ // adds an item to the list of dirty items. This item
+ // contains a reference to the item itself as well as a
+ // cloned and trimmed version of old item for use with
+ // revert.
+ dojox.rpc.JsonRest.changing(item,_deleting);
+ },
+
+ setValue: function(item, attribute, value){
+ // summary:
+ // sets 'attribute' on 'item' to 'value'
+
+ var old = item[attribute];
+ var store = item.__id ? dojox.data._getStoreForItem(item) : this;
+ if(dojox.json.schema && store.schema && store.schema.properties){
+ // if we have a schema and schema validator available we will validate the property change
+ dojox.json.schema.mustBeValid(dojox.json.schema.checkPropertyChange(value,store.schema.properties[attribute]));
+ }
+ if(attribute == store.idAttribute){
+ throw new Error("Can not change the identity attribute for an item");
+ }
+ store.changing(item);
+ item[attribute]=value;
+ if(value && !value.__parent){
+ value.__parent = item;
+ }
+ store.onSet(item,attribute,old,value);
+ },
+ setValues: function(item, attribute, values){
+ // summary:
+ // sets 'attribute' on 'item' to 'value' value
+ // must be an array.
+
+
+ if(!dojo.isArray(values)){
+ throw new Error("setValues expects to be passed an Array object as its value");
+ }
+ this.setValue(item,attribute,values);
+ },
+
+ unsetAttribute: function(item, attribute){
+ // summary:
+ // unsets 'attribute' on 'item'
+
+ this.changing(item);
+ var old = item[attribute];
+ delete item[attribute];
+ this.onSet(item,attribute,old,undefined);
+ },
+ save: function(kwArgs){
+ // summary:
+ // Saves the dirty data using REST Ajax methods. See dojo.data.api.Write for API.
+ //
+ // kwArgs.global:
+ // This will cause the save to commit the dirty data for all
+ // JsonRestStores as a single transaction.
+ //
+ // kwArgs.revertOnError
+ // This will cause the changes to be reverted if there is an
+ // error on the save. By default a revert is executed unless
+ // a value of false is provide for this parameter.
+ //
+ // kwArgs.incrementalUpdates
+ // For items that have been updated, if this is enabled, the server will be sent a POST request
+ // with a JSON object containing the changed properties. By default this is
+ // not enabled, and a PUT is used to deliver an update, and will include a full
+ // serialization of all the properties of the item/object.
+ // If this is true, the POST request body will consist of a JSON object with
+ // only the changed properties. The incrementalUpdates parameter may also
+ // be a function, in which case it will be called with the updated and previous objects
+ // and an object update representation can be returned.
+ //
+ // kwArgs.alwaysPostNewItems
+ // If this is true, new items will always be sent with a POST request. By default
+ // this is not enabled, and the JsonRestStore will send a POST request if
+ // the item does not include its identifier (expecting server assigned location/
+ // identifier), and will send a PUT request if the item does include its identifier
+ // (the PUT will be sent to the URI corresponding to the provided identifier).
+
+ if(!(kwArgs && kwArgs.global)){
+ (kwArgs = kwArgs || {}).service = this.service;
+ }
+ if("syncMode" in kwArgs ? kwArgs.syncMode : this.syncMode){
+ dojox.rpc._sync = true;
+ }
+
+ var actions = dojox.rpc.JsonRest.commit(kwArgs);
+ this.serverVersion = this._updates && this._updates.length;
+ return actions;
+ },
+
+ revert: function(kwArgs){
+ // summary
+ // returns any modified data to its original state prior to a save();
+ //
+ // kwArgs.global:
+ // This will cause the revert to undo all the changes for all
+ // JsonRestStores in a single operation.
+ dojox.rpc.JsonRest.revert(kwArgs && kwArgs.global && this.service);
+ },
+
+ isDirty: function(item){
+ // summary
+ // returns true if the item is marked as dirty.
+ return dojox.rpc.JsonRest.isDirty(item);
+ },
+ isItem: function(item, anyStore){
+ // summary:
+ // Checks to see if a passed 'item'
+ // really belongs to this JsonRestStore.
+ //
+ // item: /* object */
+ // The value to test for being an item
+ // anyStore: /* boolean*/
+ // If true, this will return true if the value is an item for any JsonRestStore,
+ // not just this instance
+ return item && item.__id && (anyStore || this.service == dojox.rpc.JsonRest.getServiceAndId(item.__id).service);
+ },
+ _doQuery: function(args){
+ var query= typeof args.queryStr == 'string' ? args.queryStr : args.query;
+ var deferred = dojox.rpc.JsonRest.query(this.service,query, args);
+ var self = this;
+ if(this.loadReferencedSchema){
+ deferred.addCallback(function(result){
+ var contentType = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Content-Type");
+ var schemaRef = contentType && contentType.match(/definedby\s*=\s*([^;]*)/);
+ if(contentType && !schemaRef){
+ schemaRef = deferred.ioArgs.xhr.getResponseHeader("Link");
+ schemaRef = schemaRef && schemaRef.match(/<([^>]*)>;\s*rel="?definedby"?/);
+ }
+ schemaRef = schemaRef && schemaRef[1];
+ if(schemaRef){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId((self.target + schemaRef).replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,"$2$3"));
+ var schemaDeferred = dojox.rpc.JsonRest.byId(serviceAndId.service, serviceAndId.id);
+ schemaDeferred.addCallbacks(function(newSchema){
+ dojo.mixin(self.schema, newSchema);
+ return result;
+ }, function(error){
+ console.error(error); // log it, but don't let it cause the main request to fail
+ return result;
+ });
+ return schemaDeferred;
+ }
+ return undefined;//don't change anything, and deal with the stupid post-commit lint complaints
+ });
+ }
+ return deferred;
+ },
+ _processResults: function(results, deferred){
+ // index the results
+ var count = results.length;
+ // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets
+ return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: results};
+ },
+
+ getConstructor: function(){
+ // summary:
+ // Gets the constructor for objects from this store
+ return this._constructor;
+ },
+ getIdentity: function(item){
+ var id = item.__clientId || item.__id;
+ if(!id){
+ return id;
+ }
+ var prefix = this.service.servicePath.replace(/[^\/]*$/,'');
+ // support for relative or absolute referencing with ids
+ return id.substring(0,prefix.length) != prefix ? id : id.substring(prefix.length); // String
+ },
+ fetchItemByIdentity: function(args){
+ var id = args.identity;
+ var store = this;
+ // if it is an absolute id, we want to find the right store to query
+ if(id.toString().match(/^(\w*:)?\//)){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId(id);
+ store = serviceAndId.service._store;
+ args.identity = serviceAndId.id;
+ }
+ args._prefix = store.service.servicePath.replace(/[^\/]*$/,'');
+ return store.inherited(arguments);
+ },
+ //Notifcation Support
+
+ onSet: function(){},
+ onNew: function(){},
+ onDelete: function(){},
+
+ getFeatures: function(){
+ // summary:
+ // return the store feature set
+ var features = this.inherited(arguments);
+ features["dojo.data.api.Write"] = true;
+ features["dojo.data.api.Notification"] = true;
+ return features;
+ },
+
+ getParent: function(item){
+ // summary:
+ // Returns the parent item (or query) for the given item
+ // item:
+ // The item to find the parent of
+
+ return item && item.__parent;
+ }
+
+
+ }
+);
+dojox.data.JsonRestStore.getStore = function(options, Class){
+ // summary:
+ // Will retrieve or create a store using the given options (the same options
+ // that are passed to JsonRestStore constructor. Returns a JsonRestStore instance
+ // options:
+ // See the JsonRestStore constructor
+ // Class:
+ // Constructor to use (for creating stores from JsonRestStore subclasses).
+ // This is optional and defaults to JsonRestStore.
+ if(typeof options.target == 'string'){
+ options.target = options.target.match(/\/$/) || options.allowNoTrailingSlash ?
+ options.target : (options.target + '/');
+ var store = (dojox.rpc.JsonRest.services[options.target] || {})._store;
+ if(store){
+ return store;
+ }
+ }
+ return new (Class || dojox.data.JsonRestStore)(options);
+};
+dojox.data._getStoreForItem = function(item){
+ if(item.__id){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId(item.__id);
+ if(serviceAndId && serviceAndId.service._store){
+ return serviceAndId.service._store;
+ }else{
+ var servicePath = item.__id.toString().match(/.*\//)[0];
+ return new dojox.data.JsonRestStore({target:servicePath});
+ }
+ }
+ return null;
+};
+dojox.json.ref._useRefs = true; // Use referencing when identifiable objects are referenced
+
+}
diff --git a/js/dojo-1.6/dojox/data/JsonRestStore.xd.js b/js/dojo-1.6/dojox/data/JsonRestStore.xd.js
new file mode 100644
index 0000000..d534931
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/JsonRestStore.xd.js
@@ -0,0 +1,500 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.JsonRestStore"],
+["require", "dojox.rpc.JsonRest"],
+["require", "dojox.data.ServiceStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.JsonRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.JsonRestStore"] = true;
+dojo.provide("dojox.data.JsonRestStore");
+dojo.require("dojox.rpc.JsonRest");
+dojo.require("dojox.data.ServiceStore");
+
+
+
+dojo.declare("dojox.data.JsonRestStore",
+ dojox.data.ServiceStore,
+ {
+ constructor: function(options){
+ //summary:
+ // JsonRestStore is a Dojo Data store interface to JSON HTTP/REST web
+ // storage services that support read and write through GET, PUT, POST, and DELETE.
+ // options:
+ // Keyword arguments
+ //
+ // The *schema* parameter
+ // This is a schema object for this store. This should be JSON Schema format.
+ //
+ // The *service* parameter
+ // This is the service object that is used to retrieve lazy data and save results
+ // The function should be directly callable with a single parameter of an object id to be loaded
+ // The function should also have the following methods:
+ // put(id,value) - puts the value at the given id
+ // post(id,value) - posts (appends) the value at the given id
+ // delete(id) - deletes the value corresponding to the given id
+ // Note that it is critical that the service parses responses as JSON.
+ // If you are using dojox.rpc.Service, the easiest way to make sure this
+ // happens is to make the responses have a content type of
+ // application/json. If you are creating your own service, make sure you
+ // use handleAs: "json" with your XHR requests.
+ //
+ // The *target* parameter
+ // This is the target URL for this Service store. This may be used in place
+ // of a service parameter to connect directly to RESTful URL without
+ // using a dojox.rpc.Service object.
+ //
+ // The *idAttribute* parameter
+ // Defaults to 'id'. The name of the attribute that holds an objects id.
+ // This can be a preexisting id provided by the server.
+ // If an ID isn't already provided when an object
+ // is fetched or added to the store, the autoIdentity system
+ // will generate an id for it and add it to the index.
+ //
+ // The *syncMode* parameter
+ // Setting this to true will set the store to using synchronous calls by default.
+ // Sync calls return their data immediately from the calling function, so
+ // callbacks are unnecessary
+ //
+ // description:
+ // The JsonRestStore will cause all saved modifications to be sent to the server using Rest commands (PUT, POST, or DELETE).
+ // When using a Rest store on a public network, it is important to implement proper security measures to
+ // control access to resources.
+ // On the server side implementing a REST interface means providing GET, PUT, POST, and DELETE handlers.
+ // GET - Retrieve an object or array/result set, this can be by id (like /table/1) or with a
+ // query (like /table/?name=foo).
+ // PUT - This should modify a object, the URL will correspond to the id (like /table/1), and the body will
+ // provide the modified object
+ // POST - This should create a new object. The URL will correspond to the target store (like /table/)
+ // and the body should be the properties of the new object. The server's response should include a
+ // Location header that indicates the id of the newly created object. This id will be used for subsequent
+ // PUT and DELETE requests. JsonRestStore also includes a Content-Location header that indicates
+ // the temporary randomly generated id used by client, and this location is used for subsequent
+ // PUT/DELETEs if no Location header is provided by the server or if a modification is sent prior
+ // to receiving a response from the server.
+ // DELETE - This should delete an object by id.
+ // These articles include more detailed information on using the JsonRestStore:
+ // http://www.sitepen.com/blog/2008/06/13/restful-json-dojo-data/
+ // http://blog.medryx.org/2008/07/24/jsonreststore-overview/
+ //
+ // example:
+ // A JsonRestStore takes a REST service or a URL and uses it the remote communication for a
+ // read/write dojo.data implementation. A JsonRestStore can be created with a simple URL like:
+ // | new JsonRestStore({target:"/MyData/"});
+ // example:
+ // To use a JsonRestStore with a service, you should create a
+ // service with a REST transport. This can be configured with an SMD:
+ // | {
+ // | services: {
+ // | jsonRestStore: {
+ // | transport: "REST",
+ // | envelope: "URL",
+ // | target: "store.php",
+ // | contentType:"application/json",
+ // | parameters: [
+ // | {name: "location", type: "string", optional: true}
+ // | ]
+ // | }
+ // | }
+ // | }
+ // The SMD can then be used to create service, and the service can be passed to a JsonRestStore. For example:
+ // | var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd"));
+ // | var jsonStore = new dojox.data.JsonRestStore({service:myServices.jsonRestStore});
+ // example:
+ // The JsonRestStore also supports lazy loading. References can be made to objects that have not been loaded.
+ // For example if a service returned:
+ // | {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}}
+ // And this object has accessed using the dojo.data API:
+ // | var obj = jsonStore.getValue(myObject,"lazyLoadedObject");
+ // The object would automatically be requested from the server (with an object id of "obj2").
+ //
+
+ dojo.connect(dojox.rpc.Rest._index,"onUpdate",this,function(obj,attrName,oldValue,newValue){
+ var prefix = this.service.servicePath;
+ if(!obj.__id){
+ console.log("no id on updated object ", obj);
+ }else if(obj.__id.substring(0,prefix.length) == prefix){
+ this.onSet(obj,attrName,oldValue,newValue);
+ }
+ });
+ this.idAttribute = this.idAttribute || 'id';// no options about it, we have to have identity
+
+ if(typeof options.target == 'string'){
+ options.target = options.target.match(/\/$/) || this.allowNoTrailingSlash ? options.target : (options.target + '/');
+ if(!this.service){
+ this.service = dojox.rpc.JsonRest.services[options.target] ||
+ dojox.rpc.Rest(options.target, true);
+ // create a default Rest service
+ }
+ }
+
+ dojox.rpc.JsonRest.registerService(this.service, options.target, this.schema);
+ this.schema = this.service._schema = this.schema || this.service._schema || {};
+ // wrap the service with so it goes through JsonRest manager
+ this.service._store = this;
+ this.service.idAsRef = this.idAsRef;
+ this.schema._idAttr = this.idAttribute;
+ var constructor = dojox.rpc.JsonRest.getConstructor(this.service);
+ var self = this;
+ this._constructor = function(data){
+ constructor.call(this, data);
+ self.onNew(this);
+ }
+ this._constructor.prototype = constructor.prototype;
+ this._index = dojox.rpc.Rest._index;
+ },
+
+ // summary:
+ // Will load any schemas referenced content-type header or in Link headers
+ loadReferencedSchema: true,
+ // summary:
+ // Treat objects in queries as partially loaded objects
+ idAsRef: false,
+ referenceIntegrity: true,
+ target:"",
+ // summary:
+ // Allow no trailing slash on target paths. This is generally discouraged since
+ // it creates prevents simple scalar values from being used a relative URLs.
+ // Disabled by default.
+ allowNoTrailingSlash: false,
+ //Write API Support
+ newItem: function(data, parentInfo){
+ // summary:
+ // adds a new item to the store at the specified point.
+ // Takes two parameters, data, and options.
+ //
+ // data: /* object */
+ // The data to be added in as an item.
+ data = new this._constructor(data);
+ if(parentInfo){
+ // get the previous value or any empty array
+ var values = this.getValue(parentInfo.parent,parentInfo.attribute,[]);
+ // set the new value
+ values = values.concat([data]);
+ data.__parent = values;
+ this.setValue(parentInfo.parent, parentInfo.attribute, values);
+ }
+ return data;
+ },
+ deleteItem: function(item){
+ // summary:
+ // deletes item and any references to that item from the store.
+ //
+ // item:
+ // item to delete
+ //
+
+ // If the desire is to delete only one reference, unsetAttribute or
+ // setValue is the way to go.
+ var checked = [];
+ var store = dojox.data._getStoreForItem(item) || this;
+ if(this.referenceIntegrity){
+ // cleanup all references
+ dojox.rpc.JsonRest._saveNotNeeded = true;
+ var index = dojox.rpc.Rest._index;
+ var fixReferences = function(parent){
+ var toSplice;
+ // keep track of the checked ones
+ checked.push(parent);
+ // mark it checked so we don't run into circular loops when encountering cycles
+ parent.__checked = 1;
+ for(var i in parent){
+ if(i.substring(0,2) != "__"){
+ var value = parent[i];
+ if(value == item){
+ if(parent != index){ // make sure we are just operating on real objects
+ if(parent instanceof Array){
+ // mark it as needing to be spliced, don't do it now or it will mess up the index into the array
+ (toSplice = toSplice || []).push(i);
+ }else{
+ // property, just delete it.
+ (dojox.data._getStoreForItem(parent) || store).unsetAttribute(parent, i);
+ }
+ }
+ }else{
+ if((typeof value == 'object') && value){
+ if(!value.__checked){
+ // recursively search
+ fixReferences(value);
+ }
+ if(typeof value.__checked == 'object' && parent != index){
+ // if it is a modified array, we will replace it
+ (dojox.data._getStoreForItem(parent) || store).setValue(parent, i, value.__checked);
+ }
+ }
+ }
+ }
+ }
+ if(toSplice){
+ // we need to splice the deleted item out of these arrays
+ i = toSplice.length;
+ parent = parent.__checked = parent.concat(); // indicates that the array is modified
+ while(i--){
+ parent.splice(toSplice[i], 1);
+ }
+ return parent;
+ }
+ return null;
+ };
+ // start with the index
+ fixReferences(index);
+ dojox.rpc.JsonRest._saveNotNeeded = false;
+ var i = 0;
+ while(checked[i]){
+ // remove the checked marker
+ delete checked[i++].__checked;
+ }
+ }
+ dojox.rpc.JsonRest.deleteObject(item);
+
+ store.onDelete(item);
+ },
+ changing: function(item,_deleting){
+ // summary:
+ // adds an item to the list of dirty items. This item
+ // contains a reference to the item itself as well as a
+ // cloned and trimmed version of old item for use with
+ // revert.
+ dojox.rpc.JsonRest.changing(item,_deleting);
+ },
+
+ setValue: function(item, attribute, value){
+ // summary:
+ // sets 'attribute' on 'item' to 'value'
+
+ var old = item[attribute];
+ var store = item.__id ? dojox.data._getStoreForItem(item) : this;
+ if(dojox.json.schema && store.schema && store.schema.properties){
+ // if we have a schema and schema validator available we will validate the property change
+ dojox.json.schema.mustBeValid(dojox.json.schema.checkPropertyChange(value,store.schema.properties[attribute]));
+ }
+ if(attribute == store.idAttribute){
+ throw new Error("Can not change the identity attribute for an item");
+ }
+ store.changing(item);
+ item[attribute]=value;
+ if(value && !value.__parent){
+ value.__parent = item;
+ }
+ store.onSet(item,attribute,old,value);
+ },
+ setValues: function(item, attribute, values){
+ // summary:
+ // sets 'attribute' on 'item' to 'value' value
+ // must be an array.
+
+
+ if(!dojo.isArray(values)){
+ throw new Error("setValues expects to be passed an Array object as its value");
+ }
+ this.setValue(item,attribute,values);
+ },
+
+ unsetAttribute: function(item, attribute){
+ // summary:
+ // unsets 'attribute' on 'item'
+
+ this.changing(item);
+ var old = item[attribute];
+ delete item[attribute];
+ this.onSet(item,attribute,old,undefined);
+ },
+ save: function(kwArgs){
+ // summary:
+ // Saves the dirty data using REST Ajax methods. See dojo.data.api.Write for API.
+ //
+ // kwArgs.global:
+ // This will cause the save to commit the dirty data for all
+ // JsonRestStores as a single transaction.
+ //
+ // kwArgs.revertOnError
+ // This will cause the changes to be reverted if there is an
+ // error on the save. By default a revert is executed unless
+ // a value of false is provide for this parameter.
+ //
+ // kwArgs.incrementalUpdates
+ // For items that have been updated, if this is enabled, the server will be sent a POST request
+ // with a JSON object containing the changed properties. By default this is
+ // not enabled, and a PUT is used to deliver an update, and will include a full
+ // serialization of all the properties of the item/object.
+ // If this is true, the POST request body will consist of a JSON object with
+ // only the changed properties. The incrementalUpdates parameter may also
+ // be a function, in which case it will be called with the updated and previous objects
+ // and an object update representation can be returned.
+ //
+ // kwArgs.alwaysPostNewItems
+ // If this is true, new items will always be sent with a POST request. By default
+ // this is not enabled, and the JsonRestStore will send a POST request if
+ // the item does not include its identifier (expecting server assigned location/
+ // identifier), and will send a PUT request if the item does include its identifier
+ // (the PUT will be sent to the URI corresponding to the provided identifier).
+
+ if(!(kwArgs && kwArgs.global)){
+ (kwArgs = kwArgs || {}).service = this.service;
+ }
+ if("syncMode" in kwArgs ? kwArgs.syncMode : this.syncMode){
+ dojox.rpc._sync = true;
+ }
+
+ var actions = dojox.rpc.JsonRest.commit(kwArgs);
+ this.serverVersion = this._updates && this._updates.length;
+ return actions;
+ },
+
+ revert: function(kwArgs){
+ // summary
+ // returns any modified data to its original state prior to a save();
+ //
+ // kwArgs.global:
+ // This will cause the revert to undo all the changes for all
+ // JsonRestStores in a single operation.
+ dojox.rpc.JsonRest.revert(kwArgs && kwArgs.global && this.service);
+ },
+
+ isDirty: function(item){
+ // summary
+ // returns true if the item is marked as dirty.
+ return dojox.rpc.JsonRest.isDirty(item);
+ },
+ isItem: function(item, anyStore){
+ // summary:
+ // Checks to see if a passed 'item'
+ // really belongs to this JsonRestStore.
+ //
+ // item: /* object */
+ // The value to test for being an item
+ // anyStore: /* boolean*/
+ // If true, this will return true if the value is an item for any JsonRestStore,
+ // not just this instance
+ return item && item.__id && (anyStore || this.service == dojox.rpc.JsonRest.getServiceAndId(item.__id).service);
+ },
+ _doQuery: function(args){
+ var query= typeof args.queryStr == 'string' ? args.queryStr : args.query;
+ var deferred = dojox.rpc.JsonRest.query(this.service,query, args);
+ var self = this;
+ if(this.loadReferencedSchema){
+ deferred.addCallback(function(result){
+ var contentType = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Content-Type");
+ var schemaRef = contentType && contentType.match(/definedby\s*=\s*([^;]*)/);
+ if(contentType && !schemaRef){
+ schemaRef = deferred.ioArgs.xhr.getResponseHeader("Link");
+ schemaRef = schemaRef && schemaRef.match(/<([^>]*)>;\s*rel="?definedby"?/);
+ }
+ schemaRef = schemaRef && schemaRef[1];
+ if(schemaRef){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId((self.target + schemaRef).replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,"$2$3"));
+ var schemaDeferred = dojox.rpc.JsonRest.byId(serviceAndId.service, serviceAndId.id);
+ schemaDeferred.addCallbacks(function(newSchema){
+ dojo.mixin(self.schema, newSchema);
+ return result;
+ }, function(error){
+ console.error(error); // log it, but don't let it cause the main request to fail
+ return result;
+ });
+ return schemaDeferred;
+ }
+ return undefined;//don't change anything, and deal with the stupid post-commit lint complaints
+ });
+ }
+ return deferred;
+ },
+ _processResults: function(results, deferred){
+ // index the results
+ var count = results.length;
+ // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets
+ return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: results};
+ },
+
+ getConstructor: function(){
+ // summary:
+ // Gets the constructor for objects from this store
+ return this._constructor;
+ },
+ getIdentity: function(item){
+ var id = item.__clientId || item.__id;
+ if(!id){
+ return id;
+ }
+ var prefix = this.service.servicePath.replace(/[^\/]*$/,'');
+ // support for relative or absolute referencing with ids
+ return id.substring(0,prefix.length) != prefix ? id : id.substring(prefix.length); // String
+ },
+ fetchItemByIdentity: function(args){
+ var id = args.identity;
+ var store = this;
+ // if it is an absolute id, we want to find the right store to query
+ if(id.toString().match(/^(\w*:)?\//)){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId(id);
+ store = serviceAndId.service._store;
+ args.identity = serviceAndId.id;
+ }
+ args._prefix = store.service.servicePath.replace(/[^\/]*$/,'');
+ return store.inherited(arguments);
+ },
+ //Notifcation Support
+
+ onSet: function(){},
+ onNew: function(){},
+ onDelete: function(){},
+
+ getFeatures: function(){
+ // summary:
+ // return the store feature set
+ var features = this.inherited(arguments);
+ features["dojo.data.api.Write"] = true;
+ features["dojo.data.api.Notification"] = true;
+ return features;
+ },
+
+ getParent: function(item){
+ // summary:
+ // Returns the parent item (or query) for the given item
+ // item:
+ // The item to find the parent of
+
+ return item && item.__parent;
+ }
+
+
+ }
+);
+dojox.data.JsonRestStore.getStore = function(options, Class){
+ // summary:
+ // Will retrieve or create a store using the given options (the same options
+ // that are passed to JsonRestStore constructor. Returns a JsonRestStore instance
+ // options:
+ // See the JsonRestStore constructor
+ // Class:
+ // Constructor to use (for creating stores from JsonRestStore subclasses).
+ // This is optional and defaults to JsonRestStore.
+ if(typeof options.target == 'string'){
+ options.target = options.target.match(/\/$/) || options.allowNoTrailingSlash ?
+ options.target : (options.target + '/');
+ var store = (dojox.rpc.JsonRest.services[options.target] || {})._store;
+ if(store){
+ return store;
+ }
+ }
+ return new (Class || dojox.data.JsonRestStore)(options);
+};
+dojox.data._getStoreForItem = function(item){
+ if(item.__id){
+ var serviceAndId = dojox.rpc.JsonRest.getServiceAndId(item.__id);
+ if(serviceAndId && serviceAndId.service._store){
+ return serviceAndId.service._store;
+ }else{
+ var servicePath = item.__id.toString().match(/.*\//)[0];
+ return new dojox.data.JsonRestStore({target:servicePath});
+ }
+ }
+ return null;
+};
+dojox.json.ref._useRefs = true; // Use referencing when identifiable objects are referenced
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/KeyValueStore.js b/js/dojo-1.6/dojox/data/KeyValueStore.js
new file mode 100644
index 0000000..f39e801
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/KeyValueStore.js
@@ -0,0 +1,403 @@
+/*
+ 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["dojox.data.KeyValueStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.KeyValueStore"] = true;
+dojo.provide("dojox.data.KeyValueStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+
+
+
+dojo.declare("dojox.data.KeyValueStore", null, {
+ // summary:
+ // This is a dojo.data store implementation. It can take in either a Javascript
+ // array, JSON string, or URL as the data source. Data is expected to be in the
+ // following format:
+ // [
+ // { "key1": "value1" },
+ // { "key2": "value2" }
+ // ]
+ // This is to mimic the Java Properties file format. Each 'item' from this store
+ // is a JS object representing a key-value pair. If an item in the above array has
+ // more than one key/value pair, only the first will be used/accessed.
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: string}
+ // keywordParameters: {dataVar: jsonObject}
+ if(keywordParameters.url){
+ this.url = keywordParameters.url;
+ }
+ this._keyValueString = keywordParameters.data;
+ this._keyValueVar = keywordParameters.dataVar;
+ this._keyAttribute = "key";
+ this._valueAttribute = "value";
+ this._storeProp = "_keyValueStore";
+ this._features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ if(keywordParameters && "urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ url: "",
+ data: "",
+
+ //urlPreventCache: boolean
+ //Controls if urlPreventCache should be used with underlying xhrGet.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.KeyValueStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* item */ item, /* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(!dojo.isString(attribute)){
+ throw new Error("dojox.data.KeyValueStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string");
+ }
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(item, attribute);
+ var value;
+ if(attribute == this._keyAttribute){ // Looking for key
+ value = item[this._keyAttribute];
+ }else{
+ value = item[this._valueAttribute]; // Otherwise, attribute == ('value' || the actual key )
+ }
+ if(value === undefined){
+ value = defaultValue;
+ }
+ return value;
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ // Key/Value syntax does not support multi-valued attributes, so this is just a
+ // wrapper function for getValue().
+ var value = this.getValue(item, attribute);
+ return (value ? [value] : []); //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [this._keyAttribute, this._valueAttribute, item[this._keyAttribute]];
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(item, attribute);
+ return (attribute == this._keyAttribute || attribute == this._valueAttribute || attribute == item[this._keyAttribute]);
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeProp] === this){
+ return true; //Boolean
+ }
+ return false; //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // The KeyValueStore always loads all items, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The KeyValueStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke
+ // the callback handlers.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return item[this._keyAttribute];
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._keyAttribute];
+ },
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+
+ var self = this;
+
+ var filter = function(requestArgs, arrayOfAllItems){
+ var items = null;
+ if(requestArgs.query){
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else if(requestArgs.identity){
+ items = [];
+ var item;
+ for(var key in arrayOfAllItems){
+ item = arrayOfAllItems[key];
+ if(item[self._keyAttribute] == requestArgs.identity){
+ items.push(item);
+ break;
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "json-comment-filtered",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ self._processData(data);
+ filter(keywordArgs, self._arrayOfAllItems);
+ self._handleQueuedFetches();
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ throw error;
+ });
+ }
+ }else if(this._keyValueString){
+ this._processData(eval(this._keyValueString));
+ this._keyValueString = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else if(this._keyValueVar){
+ this._processData(this._keyValueVar);
+ this._keyValueVar = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ throw new Error("dojox.data.KeyValueStore: No source data was provided as either URL, String, or Javascript variable data input.");
+ }
+ }
+
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedFilter = fData.filter;
+ var delayedQuery = fData.args;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._arrayOfAllItems);
+ }else{
+ this.fetchItemByIdentity(fData.args);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ _processData: function(/* Array */ data){
+ this._arrayOfAllItems = [];
+ for(var i=0; i<data.length; i++){
+ this._arrayOfAllItems.push(this._createItem(data[i]));
+ }
+ this._loadFinished = true;
+ this._loadInProgress = false;
+ },
+
+ _createItem: function(/* Object */ something){
+ var item = {};
+ item[this._storeProp] = this;
+ for(var i in something){
+ item[this._keyAttribute] = i;
+ item[this._valueAttribute] = something[i];
+ break;
+ }
+ return item; //Object
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ if(this.isItem(item)){
+ return item[this._keyAttribute]; //String
+ }
+ return null; //null
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+ return [this._keyAttribute];
+ },
+
+ fetchItemByIdentity: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ keywordArgs.oldOnItem = keywordArgs.onItem;
+ keywordArgs.onItem = null;
+ keywordArgs.onComplete = this._finishFetchItemByIdentity ;
+ this.fetch(keywordArgs);
+ },
+
+ _finishFetchItemByIdentity: function(/* Array */ items, /* object */ request){
+ var scope = request.scope || dojo.global;
+ if(items.length){
+ request.oldOnItem.call(scope, items[0]);
+ }else{
+ request.oldOnItem.call(scope, null);
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.KeyValueStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/KeyValueStore.xd.js b/js/dojo-1.6/dojox/data/KeyValueStore.xd.js
new file mode 100644
index 0000000..de62301
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/KeyValueStore.xd.js
@@ -0,0 +1,409 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.KeyValueStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.KeyValueStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.KeyValueStore"] = true;
+dojo.provide("dojox.data.KeyValueStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+
+
+
+dojo.declare("dojox.data.KeyValueStore", null, {
+ // summary:
+ // This is a dojo.data store implementation. It can take in either a Javascript
+ // array, JSON string, or URL as the data source. Data is expected to be in the
+ // following format:
+ // [
+ // { "key1": "value1" },
+ // { "key2": "value2" }
+ // ]
+ // This is to mimic the Java Properties file format. Each 'item' from this store
+ // is a JS object representing a key-value pair. If an item in the above array has
+ // more than one key/value pair, only the first will be used/accessed.
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String}
+ // keywordParameters: {data: string}
+ // keywordParameters: {dataVar: jsonObject}
+ if(keywordParameters.url){
+ this.url = keywordParameters.url;
+ }
+ this._keyValueString = keywordParameters.data;
+ this._keyValueVar = keywordParameters.dataVar;
+ this._keyAttribute = "key";
+ this._valueAttribute = "value";
+ this._storeProp = "_keyValueStore";
+ this._features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ if(keywordParameters && "urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ url: "",
+ data: "",
+
+ //urlPreventCache: boolean
+ //Controls if urlPreventCache should be used with underlying xhrGet.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.KeyValueStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* item */ item, /* String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(!dojo.isString(attribute)){
+ throw new Error("dojox.data.KeyValueStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string");
+ }
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(item, attribute);
+ var value;
+ if(attribute == this._keyAttribute){ // Looking for key
+ value = item[this._keyAttribute];
+ }else{
+ value = item[this._valueAttribute]; // Otherwise, attribute == ('value' || the actual key )
+ }
+ if(value === undefined){
+ value = defaultValue;
+ }
+ return value;
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ // Key/Value syntax does not support multi-valued attributes, so this is just a
+ // wrapper function for getValue().
+ var value = this.getValue(item, attribute);
+ return (value ? [value] : []); //Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return [this._keyAttribute, this._valueAttribute, item[this._keyAttribute]];
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ this._assertIsItem(item);
+ this._assertIsAttribute(item, attribute);
+ return (attribute == this._keyAttribute || attribute == this._valueAttribute || attribute == item[this._keyAttribute]);
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(something && something[this._storeProp] === this){
+ return true; //Boolean
+ }
+ return false; //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // The KeyValueStore always loads all items, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The KeyValueStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke
+ // the callback handlers.
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return this._features; //Object
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return item[this._keyAttribute];
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this._keyAttribute];
+ },
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+
+ var self = this;
+
+ var filter = function(requestArgs, arrayOfAllItems){
+ var items = null;
+ if(requestArgs.query){
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfAllItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfAllItems[i];
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else if(requestArgs.identity){
+ items = [];
+ var item;
+ for(var key in arrayOfAllItems){
+ item = arrayOfAllItems[key];
+ if(item[self._keyAttribute] == requestArgs.identity){
+ items.push(item);
+ break;
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ if(arrayOfAllItems.length> 0){
+ items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
+ }
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "json-comment-filtered",
+ preventCache: this.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ self._processData(data);
+ filter(keywordArgs, self._arrayOfAllItems);
+ self._handleQueuedFetches();
+ });
+ getHandler.addErrback(function(error){
+ self._loadInProgress = false;
+ throw error;
+ });
+ }
+ }else if(this._keyValueString){
+ this._processData(eval(this._keyValueString));
+ this._keyValueString = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else if(this._keyValueVar){
+ this._processData(this._keyValueVar);
+ this._keyValueVar = null;
+ filter(keywordArgs, this._arrayOfAllItems);
+ }else{
+ throw new Error("dojox.data.KeyValueStore: No source data was provided as either URL, String, or Javascript variable data input.");
+ }
+ }
+
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedFilter = fData.filter;
+ var delayedQuery = fData.args;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._arrayOfAllItems);
+ }else{
+ this.fetchItemByIdentity(fData.args);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ _processData: function(/* Array */ data){
+ this._arrayOfAllItems = [];
+ for(var i=0; i<data.length; i++){
+ this._arrayOfAllItems.push(this._createItem(data[i]));
+ }
+ this._loadFinished = true;
+ this._loadInProgress = false;
+ },
+
+ _createItem: function(/* Object */ something){
+ var item = {};
+ item[this._storeProp] = this;
+ for(var i in something){
+ item[this._keyAttribute] = i;
+ item[this._valueAttribute] = something[i];
+ break;
+ }
+ return item; //Object
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ if(this.isItem(item)){
+ return item[this._keyAttribute]; //String
+ }
+ return null; //null
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+ return [this._keyAttribute];
+ },
+
+ fetchItemByIdentity: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+ keywordArgs.oldOnItem = keywordArgs.onItem;
+ keywordArgs.onItem = null;
+ keywordArgs.onComplete = this._finishFetchItemByIdentity ;
+ this.fetch(keywordArgs);
+ },
+
+ _finishFetchItemByIdentity: function(/* Array */ items, /* object */ request){
+ var scope = request.scope || dojo.global;
+ if(items.length){
+ request.oldOnItem.call(scope, items[0]);
+ }else{
+ request.oldOnItem.call(scope, null);
+ }
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.KeyValueStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/OpenSearchStore.js b/js/dojo-1.6/dojox/data/OpenSearchStore.js
new file mode 100644
index 0000000..8d5d6d1
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/OpenSearchStore.js
@@ -0,0 +1,378 @@
+/*
+ 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["dojox.data.OpenSearchStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.OpenSearchStore"] = true;
+dojo.provide("dojox.data.OpenSearchStore");
+
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojox.xml.DomParser");
+dojo.require("dojox.xml.parser");
+
+dojo.experimental("dojox.data.OpenSearchStore");
+
+dojo.declare("dojox.data.OpenSearchStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the OpenSearchStore store.
+ // description:
+ // The OpenSearchStore is a Datastore interface to any search
+ // engine that implements the open search specifications.
+ if(args){
+ this.label = args.label;
+ this.url = args.url;
+ this.itemPath = args.itemPath;
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ var def = dojo.xhrGet({
+ url: this.url,
+ handleAs: "xml",
+ sync: true,
+ preventCache: this.urlPreventCache
+ });
+ def.addCallback(this, "_processOsdd");
+ def.addErrback(function(){
+ throw new Error("Unable to load OpenSearch Description document from " . args.url);
+ });
+ },
+
+ // URL to the open search description document
+ url: "",
+ itemPath: "",
+ _storeRef: "_S",
+ urlElement: null,
+ iframeElement: null,
+
+ //urlPreventCache: boolean
+ //Flag denoting if xhrGet calls should use the preventCache option.
+ urlPreventCache: true,
+
+ ATOM_CONTENT_TYPE: 3,
+ ATOM_CONTENT_TYPE_STRING: "atom",
+ RSS_CONTENT_TYPE: 2,
+ RSS_CONTENT_TYPE_STRING: "rss",
+ XML_CONTENT_TYPE: 1,
+ XML_CONTENT_TYPE_STRING: "xml",
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.OpenSearchStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.OpenSearchStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return ["content"];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return undefined;
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = this.processItem(item, attribute);
+ if(value){
+ return [value];
+ }
+ return undefined;
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ process: function(data){
+ // This should return an array of items. This would be the function to override if the
+ // developer wanted to customize the processing/parsing of the entire batch of search
+ // results.
+ return this["_processOSD"+this.contentType](data);
+ },
+
+ processItem: function(item, attribute){
+ // This returns the text that represents the item. If a developer wanted to customize
+ // how an individual item is rendered/parsed, they'd override this function.
+ return this["_processItem"+this.contentType](item.node, attribute);
+ },
+
+ _createSearchUrl: function(request){
+ var template = this.urlElement.attributes.getNamedItem("template").nodeValue;
+ var attrs = this.urlElement.attributes;
+ var index = template.indexOf("{searchTerms}");
+ template = template.substring(0, index) + request.query.searchTerms + template.substring(index+13);
+
+ dojo.forEach([ {'name': 'count', 'test': request.count, 'def': '10'},
+ {'name': 'startIndex', 'test': request.start, 'def': this.urlElement.attributes.getNamedItem("indexOffset")?this.urlElement.attributes.getNamedItem("indexOffset").nodeValue:0},
+ {'name': 'startPage', 'test': request.startPage, 'def': this.urlElement.attributes.getNamedItem("pageOffset")?this.urlElement.attributes.getNamedItem("pageOffset").nodeValue:0},
+ {'name': 'language', 'test': request.language, 'def': "*"},
+ {'name': 'inputEncoding', 'test': request.inputEncoding, 'def': 'UTF-8'},
+ {'name': 'outputEncoding', 'test': request.outputEncoding, 'def': 'UTF-8'}
+ ], function(item){
+ template = template.replace('{'+item.name+'}', item.test || item.def);
+ template = template.replace('{'+item.name+'?}', item.test || item.def);
+ });
+ return template;
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch OpenSearch items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(!request.query){
+ request.query={};
+ }
+
+ //Build up the content using information from the request
+ var self = this;
+ var url = this._createSearchUrl(request);
+ var getArgs = {
+ url: url,
+ preventCache: this.urlPreventCache
+ };
+
+ // Change to fetch the query results.
+ var xhr = dojo.xhrGet(getArgs);
+
+ xhr.addErrback(function(error){
+ errorHandler(error, request);
+ });
+
+ xhr.addCallback(function(data){
+ var items = [];
+ if(data){
+ //Process the items...
+ items = self.process(data);
+ for(var i=0; i < items.length; i++){
+ items[i] = {node: items[i]};
+ items[i][self._storeRef] = self;
+ }
+ }
+ fetchHandler(items, request);
+ });
+ },
+
+ _processOSDxml: function(data){
+ var div = dojo.doc.createElement("div");
+ div.innerHTML = data;
+ return dojo.query(this.itemPath, div);
+ },
+
+ _processItemxml: function(item, attribute){
+ if(attribute === "content"){
+ return item.innerHTML;
+ }
+ return undefined;
+ },
+
+ _processOSDatom: function(data){
+ return this._processOSDfeed(data, "entry");
+ },
+
+ _processItematom: function(item, attribute){
+ return this._processItemfeed(item, attribute, "content");
+ },
+
+ _processOSDrss: function(data){
+ return this._processOSDfeed(data, "item");
+ },
+
+ _processItemrss: function(item, attribute){
+ return this._processItemfeed(item, attribute, "description");
+ },
+
+ _processOSDfeed: function(data, type){
+ data = dojox.xml.parser.parse(data);
+ var items = [];
+ var nodeList = data.getElementsByTagName(type);
+ for(var i=0; i<nodeList.length; i++){
+ items.push(nodeList.item(i));
+ }
+ return items;
+ },
+
+ _processItemfeed: function(item, attribute, type){
+ if(attribute === "content"){
+ var content = item.getElementsByTagName(type).item(0);
+ return this._getNodeXml(content, true);
+ }
+ return undefined;
+ },
+
+ _getNodeXml: function(node, skipFirst){
+ var i;
+ switch(node.nodeType){
+ case 1:
+ var xml = [];
+ if(!skipFirst){
+ xml.push("<"+node.tagName);
+ var attr;
+ for(i=0; i<node.attributes.length; i++){
+ attr = node.attributes.item(i);
+ xml.push(" "+attr.nodeName+"=\""+attr.nodeValue+"\"");
+ }
+ xml.push(">");
+ }
+ for(i=0; i<node.childNodes.length; i++){
+ xml.push(this._getNodeXml(node.childNodes.item(i)));
+ }
+ if(!skipFirst){
+ xml.push("</"+node.tagName+">\n");
+ }
+ return xml.join("");
+ case 3:
+ case 4:
+ return node.nodeValue;
+ }
+ return undefined;
+ },
+
+ _processOsdd: function(doc){
+ var urlnodes = doc.getElementsByTagName("Url");
+ //TODO: Check all the urlnodes and determine what our best one is...
+ var types = [];
+ var contentType;
+ var i;
+ for(i=0; i<urlnodes.length; i++){
+ contentType = urlnodes[i].attributes.getNamedItem("type").nodeValue;
+ switch(contentType){
+ case "application/rss+xml":
+ types[i] = this.RSS_CONTENT_TYPE;
+ break;
+ case "application/atom+xml":
+ types[i] = this.ATOM_CONTENT_TYPE;
+ break;
+ default:
+ types[i] = this.XML_CONTENT_TYPE;
+ break;
+ }
+ }
+ var index = 0;
+ var currentType = types[0];
+ for(i=1; i<urlnodes.length; i++){
+ if(types[i]>currentType){
+ index = i;
+ currentType = types[i];
+ }
+ }
+
+ // We'll be using urlnodes[index] as it's the best option (ATOM > RSS > XML)
+ var label = urlnodes[index].nodeName.toLowerCase();
+ if(label == 'url'){
+ var urlattrs = urlnodes[index].attributes;
+ this.urlElement = urlnodes[index];
+ switch(types[index]){
+ case this.ATOM_CONTENT_TYPE:
+ this.contentType = this.ATOM_CONTENT_TYPE_STRING;
+ break;
+ case this.RSS_CONTENT_TYPE:
+ this.contentType = this.RSS_CONTENT_TYPE_STRING;
+ break;
+ case this.XML_CONTENT_TYPE:
+ this.contentType = this.XML_CONTENT_TYPE_STRING;
+ break;
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.OpenSearchStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/OpenSearchStore.xd.js b/js/dojo-1.6/dojox/data/OpenSearchStore.xd.js
new file mode 100644
index 0000000..50c8519
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/OpenSearchStore.xd.js
@@ -0,0 +1,385 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.OpenSearchStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojox.xml.DomParser"],
+["require", "dojox.xml.parser"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.OpenSearchStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.OpenSearchStore"] = true;
+dojo.provide("dojox.data.OpenSearchStore");
+
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojox.xml.DomParser");
+dojo.require("dojox.xml.parser");
+
+dojo.experimental("dojox.data.OpenSearchStore");
+
+dojo.declare("dojox.data.OpenSearchStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the OpenSearchStore store.
+ // description:
+ // The OpenSearchStore is a Datastore interface to any search
+ // engine that implements the open search specifications.
+ if(args){
+ this.label = args.label;
+ this.url = args.url;
+ this.itemPath = args.itemPath;
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ var def = dojo.xhrGet({
+ url: this.url,
+ handleAs: "xml",
+ sync: true,
+ preventCache: this.urlPreventCache
+ });
+ def.addCallback(this, "_processOsdd");
+ def.addErrback(function(){
+ throw new Error("Unable to load OpenSearch Description document from " . args.url);
+ });
+ },
+
+ // URL to the open search description document
+ url: "",
+ itemPath: "",
+ _storeRef: "_S",
+ urlElement: null,
+ iframeElement: null,
+
+ //urlPreventCache: boolean
+ //Flag denoting if xhrGet calls should use the preventCache option.
+ urlPreventCache: true,
+
+ ATOM_CONTENT_TYPE: 3,
+ ATOM_CONTENT_TYPE_STRING: "atom",
+ RSS_CONTENT_TYPE: 2,
+ RSS_CONTENT_TYPE_STRING: "rss",
+ XML_CONTENT_TYPE: 1,
+ XML_CONTENT_TYPE_STRING: "xml",
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.OpenSearchStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.OpenSearchStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return ["content"];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return undefined;
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var value = this.processItem(item, attribute);
+ if(value){
+ return [value];
+ }
+ return undefined;
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ process: function(data){
+ // This should return an array of items. This would be the function to override if the
+ // developer wanted to customize the processing/parsing of the entire batch of search
+ // results.
+ return this["_processOSD"+this.contentType](data);
+ },
+
+ processItem: function(item, attribute){
+ // This returns the text that represents the item. If a developer wanted to customize
+ // how an individual item is rendered/parsed, they'd override this function.
+ return this["_processItem"+this.contentType](item.node, attribute);
+ },
+
+ _createSearchUrl: function(request){
+ var template = this.urlElement.attributes.getNamedItem("template").nodeValue;
+ var attrs = this.urlElement.attributes;
+ var index = template.indexOf("{searchTerms}");
+ template = template.substring(0, index) + request.query.searchTerms + template.substring(index+13);
+
+ dojo.forEach([ {'name': 'count', 'test': request.count, 'def': '10'},
+ {'name': 'startIndex', 'test': request.start, 'def': this.urlElement.attributes.getNamedItem("indexOffset")?this.urlElement.attributes.getNamedItem("indexOffset").nodeValue:0},
+ {'name': 'startPage', 'test': request.startPage, 'def': this.urlElement.attributes.getNamedItem("pageOffset")?this.urlElement.attributes.getNamedItem("pageOffset").nodeValue:0},
+ {'name': 'language', 'test': request.language, 'def': "*"},
+ {'name': 'inputEncoding', 'test': request.inputEncoding, 'def': 'UTF-8'},
+ {'name': 'outputEncoding', 'test': request.outputEncoding, 'def': 'UTF-8'}
+ ], function(item){
+ template = template.replace('{'+item.name+'}', item.test || item.def);
+ template = template.replace('{'+item.name+'?}', item.test || item.def);
+ });
+ return template;
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch OpenSearch items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(!request.query){
+ request.query={};
+ }
+
+ //Build up the content using information from the request
+ var self = this;
+ var url = this._createSearchUrl(request);
+ var getArgs = {
+ url: url,
+ preventCache: this.urlPreventCache
+ };
+
+ // Change to fetch the query results.
+ var xhr = dojo.xhrGet(getArgs);
+
+ xhr.addErrback(function(error){
+ errorHandler(error, request);
+ });
+
+ xhr.addCallback(function(data){
+ var items = [];
+ if(data){
+ //Process the items...
+ items = self.process(data);
+ for(var i=0; i < items.length; i++){
+ items[i] = {node: items[i]};
+ items[i][self._storeRef] = self;
+ }
+ }
+ fetchHandler(items, request);
+ });
+ },
+
+ _processOSDxml: function(data){
+ var div = dojo.doc.createElement("div");
+ div.innerHTML = data;
+ return dojo.query(this.itemPath, div);
+ },
+
+ _processItemxml: function(item, attribute){
+ if(attribute === "content"){
+ return item.innerHTML;
+ }
+ return undefined;
+ },
+
+ _processOSDatom: function(data){
+ return this._processOSDfeed(data, "entry");
+ },
+
+ _processItematom: function(item, attribute){
+ return this._processItemfeed(item, attribute, "content");
+ },
+
+ _processOSDrss: function(data){
+ return this._processOSDfeed(data, "item");
+ },
+
+ _processItemrss: function(item, attribute){
+ return this._processItemfeed(item, attribute, "description");
+ },
+
+ _processOSDfeed: function(data, type){
+ data = dojox.xml.parser.parse(data);
+ var items = [];
+ var nodeList = data.getElementsByTagName(type);
+ for(var i=0; i<nodeList.length; i++){
+ items.push(nodeList.item(i));
+ }
+ return items;
+ },
+
+ _processItemfeed: function(item, attribute, type){
+ if(attribute === "content"){
+ var content = item.getElementsByTagName(type).item(0);
+ return this._getNodeXml(content, true);
+ }
+ return undefined;
+ },
+
+ _getNodeXml: function(node, skipFirst){
+ var i;
+ switch(node.nodeType){
+ case 1:
+ var xml = [];
+ if(!skipFirst){
+ xml.push("<"+node.tagName);
+ var attr;
+ for(i=0; i<node.attributes.length; i++){
+ attr = node.attributes.item(i);
+ xml.push(" "+attr.nodeName+"=\""+attr.nodeValue+"\"");
+ }
+ xml.push(">");
+ }
+ for(i=0; i<node.childNodes.length; i++){
+ xml.push(this._getNodeXml(node.childNodes.item(i)));
+ }
+ if(!skipFirst){
+ xml.push("</"+node.tagName+">\n");
+ }
+ return xml.join("");
+ case 3:
+ case 4:
+ return node.nodeValue;
+ }
+ return undefined;
+ },
+
+ _processOsdd: function(doc){
+ var urlnodes = doc.getElementsByTagName("Url");
+ //TODO: Check all the urlnodes and determine what our best one is...
+ var types = [];
+ var contentType;
+ var i;
+ for(i=0; i<urlnodes.length; i++){
+ contentType = urlnodes[i].attributes.getNamedItem("type").nodeValue;
+ switch(contentType){
+ case "application/rss+xml":
+ types[i] = this.RSS_CONTENT_TYPE;
+ break;
+ case "application/atom+xml":
+ types[i] = this.ATOM_CONTENT_TYPE;
+ break;
+ default:
+ types[i] = this.XML_CONTENT_TYPE;
+ break;
+ }
+ }
+ var index = 0;
+ var currentType = types[0];
+ for(i=1; i<urlnodes.length; i++){
+ if(types[i]>currentType){
+ index = i;
+ currentType = types[i];
+ }
+ }
+
+ // We'll be using urlnodes[index] as it's the best option (ATOM > RSS > XML)
+ var label = urlnodes[index].nodeName.toLowerCase();
+ if(label == 'url'){
+ var urlattrs = urlnodes[index].attributes;
+ this.urlElement = urlnodes[index];
+ switch(types[index]){
+ case this.ATOM_CONTENT_TYPE:
+ this.contentType = this.ATOM_CONTENT_TYPE_STRING;
+ break;
+ case this.RSS_CONTENT_TYPE:
+ this.contentType = this.RSS_CONTENT_TYPE_STRING;
+ break;
+ case this.XML_CONTENT_TYPE:
+ this.contentType = this.XML_CONTENT_TYPE_STRING;
+ break;
+ }
+ }
+ }
+});
+dojo.extend(dojox.data.OpenSearchStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/OpmlStore.js b/js/dojo-1.6/dojox/data/OpmlStore.js
new file mode 100644
index 0000000..b808f3e
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/OpmlStore.js
@@ -0,0 +1,534 @@
+/*
+ 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["dojox.data.OpmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.OpmlStore"] = true;
+dojo.provide("dojox.data.OpmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+
+
+
+dojo.declare("dojox.data.OpmlStore", null, {
+ /* summary:
+ * The OpmlStore implements the dojo.data.api.Read API.
+ */
+
+ /* examples:
+ * var opmlStore = new dojo.data.OpmlStore({url:"geography.xml"});
+ * var opmlStore = new dojo.data.OpmlStore({url:"http://example.com/geography.xml"});
+ */
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String, label: String} Where label is optional and configures what should be used as the return from getLabel()
+ this._xmlData = null;
+ this._arrayOfTopLevelItems = [];
+ this._arrayOfAllItems = [];
+ this._metadataNodes = null;
+ this._loadFinished = false;
+ this.url = keywordParameters.url;
+ this._opmlData = keywordParameters.data; // XML DOM Document
+ if(keywordParameters.label){
+ this.label = keywordParameters.label;
+ }
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ this._identityMap = {};
+ this._identCount = 0;
+ this._idProp = "_I";
+ if(keywordParameters && "urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ // label: [public] string
+ // The attribute of the Opml item to act as a label.
+ label: "text",
+
+ // url: [public] string
+ // The location from which to fetch the Opml document.
+ url: "",
+
+ // urlPreventCache: [public] boolean
+ // Flag to denote if the underlying xhrGet call should set preventCache.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.OpmlStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* item || String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(!dojo.isString(attribute)){
+ throw new Error("dojox.data.OpmlStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string");
+ }
+ },
+
+ _removeChildNodesThatAreNotElementNodes: function(/* node */ node, /* boolean */ recursive){
+ var childNodes = node.childNodes;
+ if(childNodes.length === 0){
+ return;
+ }
+ var nodesToRemove = [];
+ var i, childNode;
+ for(i = 0; i < childNodes.length; ++i){
+ childNode = childNodes[i];
+ if(childNode.nodeType != 1){
+ nodesToRemove.push(childNode);
+ }
+ }
+ for(i = 0; i < nodesToRemove.length; ++i){
+ childNode = nodesToRemove[i];
+ node.removeChild(childNode);
+ }
+ if(recursive){
+ for(i = 0; i < childNodes.length; ++i){
+ childNode = childNodes[i];
+ this._removeChildNodesThatAreNotElementNodes(childNode, recursive);
+ }
+ }
+ },
+
+ _processRawXmlTree: function(/* xmlDoc */ rawXmlTree){
+ this._loadFinished = true;
+ this._xmlData = rawXmlTree;
+ var headNodes = rawXmlTree.getElementsByTagName('head');
+ var headNode = headNodes[0];
+ if(headNode){
+ this._removeChildNodesThatAreNotElementNodes(headNode);
+ this._metadataNodes = headNode.childNodes;
+ }
+ var bodyNodes = rawXmlTree.getElementsByTagName('body');
+ var bodyNode = bodyNodes[0];
+ if(bodyNode){
+ this._removeChildNodesThatAreNotElementNodes(bodyNode, true);
+
+ var bodyChildNodes = bodyNodes[0].childNodes;
+ for(var i = 0; i < bodyChildNodes.length; ++i){
+ var node = bodyChildNodes[i];
+ if(node.tagName == 'outline'){
+ this._identityMap[this._identCount] = node;
+ this._identCount++;
+ this._arrayOfTopLevelItems.push(node);
+ this._arrayOfAllItems.push(node);
+ this._checkChildNodes(node);
+ }
+ }
+ }
+ },
+
+ _checkChildNodes: function(node /*Node*/){
+ // summary:
+ // Internal function to recurse over all child nodes from the store and add them
+ // As non-toplevel items
+ // description:
+ // Internal function to recurse over all child nodes from the store and add them
+ // As non-toplevel items
+ //
+ // node:
+ // The child node to walk.
+ if(node.firstChild){
+ for(var i = 0; i < node.childNodes.length; i++){
+ var child = node.childNodes[i];
+ if(child.tagName == 'outline'){
+ this._identityMap[this._identCount] = child;
+ this._identCount++;
+ this._arrayOfAllItems.push(child);
+ this._checkChildNodes(child);
+ }
+ }
+ }
+ },
+
+ _getItemsArray: function(/*object?*/queryOptions){
+ // summary:
+ // Internal function to determine which list of items to search over.
+ // queryOptions: The query options parameter, if any.
+ if(queryOptions && queryOptions.deep){
+ return this._arrayOfAllItems;
+ }
+ return this._arrayOfTopLevelItems;
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ if(attribute == 'children'){
+ return (item.firstChild || defaultValue); //Object
+ }else{
+ var value = item.getAttribute(attribute);
+ return (value !== undefined) ? value : defaultValue; //Object
+ }
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var array = [];
+ if(attribute == 'children'){
+ for(var i = 0; i < item.childNodes.length; ++i){
+ array.push(item.childNodes[i]);
+ }
+ } else if(item.getAttribute(attribute) !== null){
+ array.push(item.getAttribute(attribute));
+ }
+ return array; // Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ var xmlNode = item;
+ var xmlAttributes = xmlNode.attributes;
+ for(var i = 0; i < xmlAttributes.length; ++i){
+ var xmlAttribute = xmlAttributes.item(i);
+ attributes.push(xmlAttribute.nodeName);
+ }
+ if(xmlNode.childNodes.length > 0){
+ attributes.push('children');
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return (this.getValues(item, attribute).length > 0); //Boolean
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ // description:
+ // Four things are verified to ensure that "something" is an item:
+ // something can not be null, the nodeType must be an XML Element,
+ // the tagName must be "outline", and the node must be a member of
+ // XML document for this datastore.
+ return (something &&
+ something.nodeType == 1 &&
+ something.tagName == 'outline' &&
+ something.ownerDocument === this._xmlData); //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // OpmlStore loads every item, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The OpmlStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke the callback handlers.
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ return this.getValue(item,this.label); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label]; //array
+ },
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+
+ var self = this;
+ var filter = function(requestArgs, arrayOfItems){
+ var items = null;
+ if(requestArgs.query){
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfItems[i];
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ if(arrayOfItems.length> 0){
+ items = arrayOfItems.slice(0,arrayOfItems.length);
+ }
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ if(this.url !== ""){
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ self._processRawXmlTree(data);
+ filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
+ self._handleQueuedFetches();
+ });
+ getHandler.addErrback(function(error){
+ throw error;
+ });
+ }else if(this._opmlData){
+ this._processRawXmlTree(this._opmlData);
+ this._opmlData = null;
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+ throw new Error("dojox.data.OpmlStore: No OPML source data was provided as either URL or XML data input.");
+ }
+ }
+ }
+ },
+
+ getFeatures: function(){
+ // summary: See dojo.data.api.Read.getFeatures()
+ var features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ return features; //Object
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ if(this.isItem(item)){
+ //No ther way to do this other than O(n) without
+ //complete rework of how the tree stores nodes.
+ for(var i in this._identityMap){
+ if(this._identityMap[i] === item){
+ return i;
+ }
+ }
+ }
+ return null; //null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ //Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "xml"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ try{
+ self._processRawXmlTree(data);
+ var item = self._identityMap[keywordArgs.identity];
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ this._loadInProgress = false;
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+ }else if(this._opmlData){
+ this._processRawXmlTree(this._opmlData);
+ this._opmlData = null;
+ var item = this._identityMap[keywordArgs.identity];
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }else{
+ //Already loaded. We can just look it up and call back.
+ var item = this._identityMap[keywordArgs.identity];
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+
+ //Identity isn't a public attribute in the item, it's the node count.
+ //So, return null.
+ return null;
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedQuery = fData.args;
+ var delayedFilter = fData.filter;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
+ }else{
+ this.fetchItemByIdentity(delayedQuery);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.OpmlStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/OpmlStore.xd.js b/js/dojo-1.6/dojox/data/OpmlStore.xd.js
new file mode 100644
index 0000000..dff711d
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/OpmlStore.xd.js
@@ -0,0 +1,540 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.OpmlStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.OpmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.OpmlStore"] = true;
+dojo.provide("dojox.data.OpmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+
+
+
+dojo.declare("dojox.data.OpmlStore", null, {
+ /* summary:
+ * The OpmlStore implements the dojo.data.api.Read API.
+ */
+
+ /* examples:
+ * var opmlStore = new dojo.data.OpmlStore({url:"geography.xml"});
+ * var opmlStore = new dojo.data.OpmlStore({url:"http://example.com/geography.xml"});
+ */
+ constructor: function(/* Object */ keywordParameters){
+ // summary: constructor
+ // keywordParameters: {url: String, label: String} Where label is optional and configures what should be used as the return from getLabel()
+ this._xmlData = null;
+ this._arrayOfTopLevelItems = [];
+ this._arrayOfAllItems = [];
+ this._metadataNodes = null;
+ this._loadFinished = false;
+ this.url = keywordParameters.url;
+ this._opmlData = keywordParameters.data; // XML DOM Document
+ if(keywordParameters.label){
+ this.label = keywordParameters.label;
+ }
+ this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
+ this._queuedFetches = [];
+ this._identityMap = {};
+ this._identCount = 0;
+ this._idProp = "_I";
+ if(keywordParameters && "urlPreventCache" in keywordParameters){
+ this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
+ }
+ },
+
+ // label: [public] string
+ // The attribute of the Opml item to act as a label.
+ label: "text",
+
+ // url: [public] string
+ // The location from which to fetch the Opml document.
+ url: "",
+
+ // urlPreventCache: [public] boolean
+ // Flag to denote if the underlying xhrGet call should set preventCache.
+ urlPreventCache: false,
+
+ _assertIsItem: function(/* item */ item){
+ if(!this.isItem(item)){
+ throw new Error("dojo.data.OpmlStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* item || String */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(!dojo.isString(attribute)){
+ throw new Error("dojox.data.OpmlStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string");
+ }
+ },
+
+ _removeChildNodesThatAreNotElementNodes: function(/* node */ node, /* boolean */ recursive){
+ var childNodes = node.childNodes;
+ if(childNodes.length === 0){
+ return;
+ }
+ var nodesToRemove = [];
+ var i, childNode;
+ for(i = 0; i < childNodes.length; ++i){
+ childNode = childNodes[i];
+ if(childNode.nodeType != 1){
+ nodesToRemove.push(childNode);
+ }
+ }
+ for(i = 0; i < nodesToRemove.length; ++i){
+ childNode = nodesToRemove[i];
+ node.removeChild(childNode);
+ }
+ if(recursive){
+ for(i = 0; i < childNodes.length; ++i){
+ childNode = childNodes[i];
+ this._removeChildNodesThatAreNotElementNodes(childNode, recursive);
+ }
+ }
+ },
+
+ _processRawXmlTree: function(/* xmlDoc */ rawXmlTree){
+ this._loadFinished = true;
+ this._xmlData = rawXmlTree;
+ var headNodes = rawXmlTree.getElementsByTagName('head');
+ var headNode = headNodes[0];
+ if(headNode){
+ this._removeChildNodesThatAreNotElementNodes(headNode);
+ this._metadataNodes = headNode.childNodes;
+ }
+ var bodyNodes = rawXmlTree.getElementsByTagName('body');
+ var bodyNode = bodyNodes[0];
+ if(bodyNode){
+ this._removeChildNodesThatAreNotElementNodes(bodyNode, true);
+
+ var bodyChildNodes = bodyNodes[0].childNodes;
+ for(var i = 0; i < bodyChildNodes.length; ++i){
+ var node = bodyChildNodes[i];
+ if(node.tagName == 'outline'){
+ this._identityMap[this._identCount] = node;
+ this._identCount++;
+ this._arrayOfTopLevelItems.push(node);
+ this._arrayOfAllItems.push(node);
+ this._checkChildNodes(node);
+ }
+ }
+ }
+ },
+
+ _checkChildNodes: function(node /*Node*/){
+ // summary:
+ // Internal function to recurse over all child nodes from the store and add them
+ // As non-toplevel items
+ // description:
+ // Internal function to recurse over all child nodes from the store and add them
+ // As non-toplevel items
+ //
+ // node:
+ // The child node to walk.
+ if(node.firstChild){
+ for(var i = 0; i < node.childNodes.length; i++){
+ var child = node.childNodes[i];
+ if(child.tagName == 'outline'){
+ this._identityMap[this._identCount] = child;
+ this._identCount++;
+ this._arrayOfAllItems.push(child);
+ this._checkChildNodes(child);
+ }
+ }
+ }
+ },
+
+ _getItemsArray: function(/*object?*/queryOptions){
+ // summary:
+ // Internal function to determine which list of items to search over.
+ // queryOptions: The query options parameter, if any.
+ if(queryOptions && queryOptions.deep){
+ return this._arrayOfAllItems;
+ }
+ return this._arrayOfTopLevelItems;
+ },
+
+/***************************************
+ dojo.data.api.Read API
+***************************************/
+ getValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* value? */ defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ if(attribute == 'children'){
+ return (item.firstChild || defaultValue); //Object
+ }else{
+ var value = item.getAttribute(attribute);
+ return (value !== undefined) ? value : defaultValue; //Object
+ }
+ },
+
+ getValues: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.getValues()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var array = [];
+ if(attribute == 'children'){
+ for(var i = 0; i < item.childNodes.length; ++i){
+ array.push(item.childNodes[i]);
+ }
+ } else if(item.getAttribute(attribute) !== null){
+ array.push(item.getAttribute(attribute));
+ }
+ return array; // Array
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ var attributes = [];
+ var xmlNode = item;
+ var xmlAttributes = xmlNode.attributes;
+ for(var i = 0; i < xmlAttributes.length; ++i){
+ var xmlAttribute = xmlAttributes.item(i);
+ attributes.push(xmlAttribute.nodeName);
+ }
+ if(xmlNode.childNodes.length > 0){
+ attributes.push('children');
+ }
+ return attributes; //Array
+ },
+
+ hasAttribute: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return (this.getValues(item, attribute).length > 0); //Boolean
+ },
+
+ containsValue: function(/* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var regexp = undefined;
+ if(typeof value === "string"){
+ regexp = dojo.data.util.filter.patternToRegExp(value, false);
+ }
+ return this._containsValue(item, attribute, value, regexp); //boolean.
+ },
+
+ _containsValue: function( /* item */ item,
+ /* attribute || attribute-name-string */ attribute,
+ /* anything */ value,
+ /* RegExp?*/ regexp){
+ // summary:
+ // Internal function for looking at the values contained by the item.
+ // description:
+ // Internal function for looking at the values contained by the item. This
+ // function allows for denoting if the comparison should be case sensitive for
+ // strings or not (for handling filtering cases where string case should not matter)
+ //
+ // item:
+ // The data item to examine for attribute values.
+ // attribute:
+ // The attribute to inspect.
+ // value:
+ // The value to match.
+ // regexp:
+ // Optional regular expression generated off value if value was of string type to handle wildcarding.
+ // If present and attribute values are string, then it can be used for comparison instead of 'value'
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; ++i){
+ var possibleValue = values[i];
+ if(typeof possibleValue === "string" && regexp){
+ return (possibleValue.match(regexp) !== null);
+ }else{
+ //Non-string matching.
+ if(value === possibleValue){
+ return true; // Boolean
+ }
+ }
+ }
+ return false; // Boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ // description:
+ // Four things are verified to ensure that "something" is an item:
+ // something can not be null, the nodeType must be an XML Element,
+ // the tagName must be "outline", and the node must be a member of
+ // XML document for this datastore.
+ return (something &&
+ something.nodeType == 1 &&
+ something.tagName == 'outline' &&
+ something.ownerDocument === this._xmlData); //Boolean
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ // OpmlStore loads every item, so if it's an item, then it's loaded.
+ return this.isItem(something); //Boolean
+ },
+
+ loadItem: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ // description:
+ // The OpmlStore always loads all items, so if it's an item, then it's loaded.
+ // From the dojo.data.api.Read.loadItem docs:
+ // If a call to isItemLoaded() returns true before loadItem() is even called,
+ // then loadItem() need not do any work at all and will not even invoke the callback handlers.
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this.isItem(item)){
+ return this.getValue(item,this.label); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label]; //array
+ },
+
+ // The dojo.data.api.Read.fetch() function is implemented as
+ // a mixin from dojo.data.util.simpleFetch.
+ // That mixin requires us to define _fetchItems().
+ _fetchItems: function( /* Object */ keywordArgs,
+ /* Function */ findCallback,
+ /* Function */ errorCallback){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch()
+
+ var self = this;
+ var filter = function(requestArgs, arrayOfItems){
+ var items = null;
+ if(requestArgs.query){
+ items = [];
+ var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+
+ for(var i = 0; i < arrayOfItems.length; ++i){
+ var match = true;
+ var candidateItem = arrayOfItems[i];
+ for(var key in requestArgs.query){
+ var value = requestArgs.query[key];
+ if(!self._containsValue(candidateItem, key, value, regexpList[key])){
+ match = false;
+ }
+ }
+ if(match){
+ items.push(candidateItem);
+ }
+ }
+ }else{
+ // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
+ // of the internal list so that multiple callers can get lists and sort without affecting each other.
+ if(arrayOfItems.length> 0){
+ items = arrayOfItems.slice(0,arrayOfItems.length);
+ }
+ }
+ findCallback(items, requestArgs);
+ };
+
+ if(this._loadFinished){
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs, filter: filter});
+ }else{
+ if(this.url !== ""){
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ self._processRawXmlTree(data);
+ filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
+ self._handleQueuedFetches();
+ });
+ getHandler.addErrback(function(error){
+ throw error;
+ });
+ }else if(this._opmlData){
+ this._processRawXmlTree(this._opmlData);
+ this._opmlData = null;
+ filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
+ }else{
+ throw new Error("dojox.data.OpmlStore: No OPML source data was provided as either URL or XML data input.");
+ }
+ }
+ }
+ },
+
+ getFeatures: function(){
+ // summary: See dojo.data.api.Read.getFeatures()
+ var features = {
+ 'dojo.data.api.Read': true,
+ 'dojo.data.api.Identity': true
+ };
+ return features; //Object
+ },
+
+/***************************************
+ dojo.data.api.Identity API
+***************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ if(this.isItem(item)){
+ //No ther way to do this other than O(n) without
+ //complete rework of how the tree stores nodes.
+ for(var i in this._identityMap){
+ if(this._identityMap[i] === item){
+ return i;
+ }
+ }
+ }
+ return null; //null
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ //Hasn't loaded yet, we have to trigger the load.
+ if(!this._loadFinished){
+ var self = this;
+ if(this.url !== ""){
+ //If fetches come in before the loading has finished, but while
+ //a load is in progress, we have to defer the fetching to be
+ //invoked in the callback.
+ if(this._loadInProgress){
+ this._queuedFetches.push({args: keywordArgs});
+ }else{
+ this._loadInProgress = true;
+ var getArgs = {
+ url: self.url,
+ handleAs: "xml"
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ try{
+ self._processRawXmlTree(data);
+ var item = self._identityMap[keywordArgs.identity];
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ self._handleQueuedFetches();
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ });
+ getHandler.addErrback(function(error){
+ this._loadInProgress = false;
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ }
+ }else if(this._opmlData){
+ this._processRawXmlTree(this._opmlData);
+ this._opmlData = null;
+ var item = this._identityMap[keywordArgs.identity];
+ if(!self.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }else{
+ //Already loaded. We can just look it up and call back.
+ var item = this._identityMap[keywordArgs.identity];
+ if(!this.isItem(item)){
+ item = null;
+ }
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentifierAttributes()
+
+ //Identity isn't a public attribute in the item, it's the node count.
+ //So, return null.
+ return null;
+ },
+
+ _handleQueuedFetches: function(){
+ // summary:
+ // Internal function to execute delayed request in the store.
+ //Execute any deferred fetches now.
+ if(this._queuedFetches.length > 0){
+ for(var i = 0; i < this._queuedFetches.length; i++){
+ var fData = this._queuedFetches[i];
+ var delayedQuery = fData.args;
+ var delayedFilter = fData.filter;
+ if(delayedFilter){
+ delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
+ }else{
+ this.fetchItemByIdentity(delayedQuery);
+ }
+ }
+ this._queuedFetches = [];
+ }
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ }
+});
+//Mix in the simple fetch implementation to this class.
+dojo.extend(dojox.data.OpmlStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/PersevereStore.js b/js/dojo-1.6/dojox/data/PersevereStore.js
new file mode 100644
index 0000000..1b0ecf9
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/PersevereStore.js
@@ -0,0 +1,123 @@
+/*
+ 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["dojox.data.PersevereStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.PersevereStore"] = true;
+dojo.provide("dojox.data.PersevereStore");
+dojo.require("dojox.data.JsonQueryRestStore");
+dojo.require("dojox.rpc.Client");
+
+
+
+// PersevereStore is an extension of JsonRestStore to handle Persevere's special features
+
+dojox.json.ref.serializeFunctions = true; // Persevere supports persisted functions
+
+dojo.declare("dojox.data.PersevereStore",dojox.data.JsonQueryRestStore,{
+ useFullIdInQueries: true, // in JSONQuerys use the full id
+ jsonQueryPagination: false // use the Range headers instead
+});
+
+dojox.data.PersevereStore.getStores = function(/*String?*/path,/*Boolean?*/sync){
+ // summary:
+ // Creates Dojo data stores for all the table/classes on a Persevere server
+ // path:
+ // URL of the Persevere server's root, this normally just "/"
+ // which is the default value if the target is not provided
+ // sync:
+ // Indicates that the operation should happen synchronously.
+ // return:
+ // A map/object of datastores will be returned if it is performed asynchronously,
+ // otherwise it will return a Deferred object that will provide the map/object.
+ // The name of each property is a the name of a store,
+ // and the value is the actual data store object.
+ path = (path && (path.match(/\/$/) ? path : (path + '/'))) || '/';
+ if(path.match(/^\w*:\/\//)){
+ // if it is cross-domain, we will use window.name for communication
+ dojo.require("dojox.io.xhrScriptPlugin");
+ dojox.io.xhrScriptPlugin(path, "callback", dojox.io.xhrPlugins.fullHttpAdapter);
+ }
+ var plainXhr = dojo.xhr;
+ dojo.xhr = function(method,args){
+ (args.headers = args.headers || {})['Server-Methods'] = "false";
+ return plainXhr.apply(dojo,arguments);
+ }
+ var rootService= dojox.rpc.Rest(path,true);
+ dojox.rpc._sync = sync;
+ var dfd = rootService("Class/");//dojo.xhrGet({url: target, sync:!callback, handleAs:'json'});
+ var results;
+ var stores = {};
+ var callId = 0;
+ dfd.addCallback(function(schemas){
+ dojox.json.ref.resolveJson(schemas, {
+ index: dojox.rpc.Rest._index,
+ idPrefix: "/Class/",
+ assignAbsoluteIds: true
+ });
+ function setupHierarchy(schema){
+ if(schema['extends'] && schema['extends'].prototype){
+ if(!schema.prototype || !schema.prototype.isPrototypeOf(schema['extends'].prototype)){
+ setupHierarchy(schema['extends']);
+ dojox.rpc.Rest._index[schema.prototype.__id] = schema.prototype = dojo.mixin(dojo.delegate(schema['extends'].prototype), schema.prototype);
+ }
+ }
+ }
+ function setupMethods(methodsDefinitions, methodsTarget){
+ if(methodsDefinitions && methodsTarget){
+ for(var j in methodsDefinitions){
+ var methodDef = methodsDefinitions[j];
+ // if any method definitions indicate that the method should run on the server, than add
+ // it to the prototype as a JSON-RPC method
+ if(methodDef.runAt != "client" && !methodsTarget[j]){
+ methodsTarget[j] = (function(methodName){
+ return function(){
+ // execute a JSON-RPC call
+ var deferred = dojo.rawXhrPost({
+ url: this.__id,
+ // the JSON-RPC call
+ postData: dojox.json.ref.toJson({
+ method: methodName,
+ id: callId++,
+ params: dojo._toArray(arguments)
+ }),
+ handleAs: "json"
+ });
+ deferred.addCallback(function(response){
+ // handle the response
+ return response.error ?
+ new Error(response.error) :
+ response.result;
+ });
+ return deferred;
+ }
+ })(j);
+ }
+ }
+ }
+ }
+ for(var i in schemas){
+ if(typeof schemas[i] == 'object'){
+ var schema = schemas[i];
+ setupHierarchy(schema);
+ setupMethods(schema.methods, schema.prototype = schema.prototype || {});
+ setupMethods(schema.staticMethods, schema);
+ stores[schemas[i].id] = new dojox.data.PersevereStore({target:new dojo._Url(path,schemas[i].id) + '/',schema:schema});
+ }
+ }
+ return (results = stores);
+ });
+ dojo.xhr = plainXhr;
+ return sync ? results : dfd;
+};
+dojox.data.PersevereStore.addProxy = function(){
+ // summary:
+ // Invokes the XHR proxy plugin. Call this if you will be using x-site data.
+ dojo.require("dojox.io.xhrPlugins"); // also not necessary, but we can register that Persevere supports proxying
+ dojox.io.xhrPlugins.addProxy("/proxy/");
+};
+
+}
diff --git a/js/dojo-1.6/dojox/data/PersevereStore.xd.js b/js/dojo-1.6/dojox/data/PersevereStore.xd.js
new file mode 100644
index 0000000..d2fb8de
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/PersevereStore.xd.js
@@ -0,0 +1,131 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.PersevereStore"],
+["require", "dojox.data.JsonQueryRestStore"],
+["require", "dojox.rpc.Client"],
+["require", "dojox.io.xhrScriptPlugin"],
+["require", "dojox.io.xhrPlugins"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.PersevereStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.PersevereStore"] = true;
+dojo.provide("dojox.data.PersevereStore");
+dojo.require("dojox.data.JsonQueryRestStore");
+dojo.require("dojox.rpc.Client");
+
+
+
+// PersevereStore is an extension of JsonRestStore to handle Persevere's special features
+
+dojox.json.ref.serializeFunctions = true; // Persevere supports persisted functions
+
+dojo.declare("dojox.data.PersevereStore",dojox.data.JsonQueryRestStore,{
+ useFullIdInQueries: true, // in JSONQuerys use the full id
+ jsonQueryPagination: false // use the Range headers instead
+});
+
+dojox.data.PersevereStore.getStores = function(/*String?*/path,/*Boolean?*/sync){
+ // summary:
+ // Creates Dojo data stores for all the table/classes on a Persevere server
+ // path:
+ // URL of the Persevere server's root, this normally just "/"
+ // which is the default value if the target is not provided
+ // sync:
+ // Indicates that the operation should happen synchronously.
+ // return:
+ // A map/object of datastores will be returned if it is performed asynchronously,
+ // otherwise it will return a Deferred object that will provide the map/object.
+ // The name of each property is a the name of a store,
+ // and the value is the actual data store object.
+ path = (path && (path.match(/\/$/) ? path : (path + '/'))) || '/';
+ if(path.match(/^\w*:\/\//)){
+ // if it is cross-domain, we will use window.name for communication
+ dojo.require("dojox.io.xhrScriptPlugin");
+ dojox.io.xhrScriptPlugin(path, "callback", dojox.io.xhrPlugins.fullHttpAdapter);
+ }
+ var plainXhr = dojo.xhr;
+ dojo.xhr = function(method,args){
+ (args.headers = args.headers || {})['Server-Methods'] = "false";
+ return plainXhr.apply(dojo,arguments);
+ }
+ var rootService= dojox.rpc.Rest(path,true);
+ dojox.rpc._sync = sync;
+ var dfd = rootService("Class/");//dojo.xhrGet({url: target, sync:!callback, handleAs:'json'});
+ var results;
+ var stores = {};
+ var callId = 0;
+ dfd.addCallback(function(schemas){
+ dojox.json.ref.resolveJson(schemas, {
+ index: dojox.rpc.Rest._index,
+ idPrefix: "/Class/",
+ assignAbsoluteIds: true
+ });
+ function setupHierarchy(schema){
+ if(schema['extends'] && schema['extends'].prototype){
+ if(!schema.prototype || !schema.prototype.isPrototypeOf(schema['extends'].prototype)){
+ setupHierarchy(schema['extends']);
+ dojox.rpc.Rest._index[schema.prototype.__id] = schema.prototype = dojo.mixin(dojo.delegate(schema['extends'].prototype), schema.prototype);
+ }
+ }
+ }
+ function setupMethods(methodsDefinitions, methodsTarget){
+ if(methodsDefinitions && methodsTarget){
+ for(var j in methodsDefinitions){
+ var methodDef = methodsDefinitions[j];
+ // if any method definitions indicate that the method should run on the server, than add
+ // it to the prototype as a JSON-RPC method
+ if(methodDef.runAt != "client" && !methodsTarget[j]){
+ methodsTarget[j] = (function(methodName){
+ return function(){
+ // execute a JSON-RPC call
+ var deferred = dojo.rawXhrPost({
+ url: this.__id,
+ // the JSON-RPC call
+ postData: dojox.json.ref.toJson({
+ method: methodName,
+ id: callId++,
+ params: dojo._toArray(arguments)
+ }),
+ handleAs: "json"
+ });
+ deferred.addCallback(function(response){
+ // handle the response
+ return response.error ?
+ new Error(response.error) :
+ response.result;
+ });
+ return deferred;
+ }
+ })(j);
+ }
+ }
+ }
+ }
+ for(var i in schemas){
+ if(typeof schemas[i] == 'object'){
+ var schema = schemas[i];
+ setupHierarchy(schema);
+ setupMethods(schema.methods, schema.prototype = schema.prototype || {});
+ setupMethods(schema.staticMethods, schema);
+ stores[schemas[i].id] = new dojox.data.PersevereStore({target:new dojo._Url(path,schemas[i].id) + '/',schema:schema});
+ }
+ }
+ return (results = stores);
+ });
+ dojo.xhr = plainXhr;
+ return sync ? results : dfd;
+};
+dojox.data.PersevereStore.addProxy = function(){
+ // summary:
+ // Invokes the XHR proxy plugin. Call this if you will be using x-site data.
+ dojo.require("dojox.io.xhrPlugins"); // also not necessary, but we can register that Persevere supports proxying
+ dojox.io.xhrPlugins.addProxy("/proxy/");
+};
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/PicasaStore.js b/js/dojo-1.6/dojox/data/PicasaStore.js
new file mode 100644
index 0000000..2ff89bb
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/PicasaStore.js
@@ -0,0 +1,284 @@
+/*
+ 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["dojox.data.PicasaStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.PicasaStore"] = true;
+dojo.provide("dojox.data.PicasaStore");
+dojo.require("dojo.io.script");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+
+dojo.declare("dojox.data.PicasaStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the PicasaStore store.
+ // description:
+ // The PicasaStore is a Datastore interface to one of the basic services
+ // of the Picasa service, the public photo feed. This does not provide
+ // access to all the services of Picasa.
+ // This store cannot do * and ? filtering as the picasa service
+ // provides no interface for wildcards.
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ if(args && "maxResults" in args){
+ this.maxResults = parseInt(args.maxResults);
+ if(!this.maxResults){
+ this.maxResults = 20;
+ }
+ }
+ },
+
+ _picasaUrl: "http://picasaweb.google.com/data/feed/api/all",
+
+ _storeRef: "_S",
+
+ //label: string
+ //The attribute to use from the picasa item as its label.
+ label: "title",
+
+ //urlPreventCache: boolean
+ //Flag denoting if preventCache should be passed to dojo.io.script.
+ urlPreventCache: false,
+
+ //maxResults: Define out how many results to return for a fetch.
+ maxResults: 20,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.PicasaStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.PicasaStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return ["id", "published", "updated", "category", "title$type", "title",
+ "summary$type", "summary", "rights$type", "rights", "link", "author",
+ "gphoto$id", "gphoto$name", "location", "imageUrlSmall", "imageUrlMedium",
+ "imageUrl", "datePublished", "dateTaken","description"];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ if(attribute === "title"){
+ return [this._unescapeHtml(item.title)];
+ }else if(attribute === "author"){
+ return [this._unescapeHtml(item.author[0].name)];
+ }else if(attribute === "datePublished"){
+ return [dojo.date.stamp.fromISOString(item.published)];
+ }else if(attribute === "dateTaken"){
+ return [dojo.date.stamp.fromISOString(item.published)];
+ }else if(attribute === "updated"){
+ return [dojo.date.stamp.fromISOString(item.updated)];
+ }else if(attribute === "imageUrlSmall"){
+ return [item.media.thumbnail[1].url];
+ }else if(attribute === "imageUrl"){
+ return [item.content$src];
+ }else if(attribute === "imageUrlMedium"){
+ return [item.media.thumbnail[2].url];
+ }else if(attribute === "link"){
+ return [item.link[1]];
+ }else if(attribute === "tags"){
+ return item.tags.split(" ");
+ }else if(attribute === "description"){
+ return [this._unescapeHtml(item.summary)];
+ }
+ return [];
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch picasa items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(!request.query){
+ request.query={};
+ }
+
+ //Build up the content to send the request for.
+ var content = {alt: "jsonm", pp: "1", psc: "G"};
+
+ content['start-index'] = "1";
+ if(request.query.start){
+ content['start-index'] = request.query.start;
+ }
+ if(request.query.tags){
+ content.q = request.query.tags;
+ }
+ if(request.query.userid){
+ content.uname = request.query.userid;
+ }
+ if(request.query.userids){
+ content.ids = request.query.userids;
+ }
+ if(request.query.lang){
+ content.hl = request.query.lang;
+ }
+ content['max-results'] = this.maxResults;
+
+ //Linking this up to Picasa is a JOY!
+ var self = this;
+ var handle = null;
+ var myHandler = function(data){
+ if(handle !== null){
+ dojo.disconnect(handle);
+ }
+
+ //Process the items...
+ fetchHandler(self._processPicasaData(data), request);
+ };
+ var getArgs = {
+ url: this._picasaUrl,
+ preventCache: this.urlPreventCache,
+ content: content,
+ callbackParamName: 'callback',
+ handle: myHandler
+ };
+ var deferred = dojo.io.script.get(getArgs);
+
+ deferred.addErrback(function(error){
+ dojo.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ _processPicasaData: function(data){
+ var items = [];
+ if(data.feed){
+ items = data.feed.entry;
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < items.length; i++){
+ var item = items[i];
+ item[this._storeRef] = this;
+ }
+ }
+ return items;
+ },
+
+ _unescapeHtml: function(str){
+ // summary: Utility function to un-escape XML special characters in an HTML string.
+ // description: Utility function to un-escape XML special characters in an HTML string.
+ // str: String.
+ // The string to un-escape
+ // returns: HTML String converted back to the normal text (unescaped) characters (<,>,&, ", etc,).
+ //
+ //TODO: Check to see if theres already compatible escape() in dojo.string or dojo.html
+ if(str){
+ str = str.replace(/&amp;/gm, "&").replace(/&lt;/gm, "<").replace(/&gt;/gm, ">").replace(/&quot;/gm, "\"");
+ str = str.replace(/&#39;/gm, "'");
+ }
+ return str;
+ }
+});
+dojo.extend(dojox.data.PicasaStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/PicasaStore.xd.js b/js/dojo-1.6/dojox/data/PicasaStore.xd.js
new file mode 100644
index 0000000..38dc04b
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/PicasaStore.xd.js
@@ -0,0 +1,291 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.PicasaStore"],
+["require", "dojo.io.script"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.date.stamp"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.PicasaStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.PicasaStore"] = true;
+dojo.provide("dojox.data.PicasaStore");
+dojo.require("dojo.io.script");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.date.stamp");
+
+
+
+dojo.declare("dojox.data.PicasaStore", null, {
+ constructor: function(/*Object*/args){
+ // summary:
+ // Initializer for the PicasaStore store.
+ // description:
+ // The PicasaStore is a Datastore interface to one of the basic services
+ // of the Picasa service, the public photo feed. This does not provide
+ // access to all the services of Picasa.
+ // This store cannot do * and ? filtering as the picasa service
+ // provides no interface for wildcards.
+ if(args && args.label){
+ this.label = args.label;
+ }
+ if(args && "urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ if(args && "maxResults" in args){
+ this.maxResults = parseInt(args.maxResults);
+ if(!this.maxResults){
+ this.maxResults = 20;
+ }
+ }
+ },
+
+ _picasaUrl: "http://picasaweb.google.com/data/feed/api/all",
+
+ _storeRef: "_S",
+
+ //label: string
+ //The attribute to use from the picasa item as its label.
+ label: "title",
+
+ //urlPreventCache: boolean
+ //Flag denoting if preventCache should be passed to dojo.io.script.
+ urlPreventCache: false,
+
+ //maxResults: Define out how many results to return for a fetch.
+ maxResults: 20,
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.PicasaStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.PicasaStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ var values = this.getValues(item, attribute);
+ if(values && values.length > 0){
+ return values[0];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ return ["id", "published", "updated", "category", "title$type", "title",
+ "summary$type", "summary", "rights$type", "rights", "link", "author",
+ "gphoto$id", "gphoto$name", "location", "imageUrlSmall", "imageUrlMedium",
+ "imageUrl", "datePublished", "dateTaken","description"];
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ if(this.getValue(item,attribute)){
+ return true;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item);
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return this.getValue(item,this.label);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return [this.label];
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ var values = this.getValues(item,attribute);
+ for(var i = 0; i < values.length; i++){
+ if(values[i] === value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ if(attribute === "title"){
+ return [this._unescapeHtml(item.title)];
+ }else if(attribute === "author"){
+ return [this._unescapeHtml(item.author[0].name)];
+ }else if(attribute === "datePublished"){
+ return [dojo.date.stamp.fromISOString(item.published)];
+ }else if(attribute === "dateTaken"){
+ return [dojo.date.stamp.fromISOString(item.published)];
+ }else if(attribute === "updated"){
+ return [dojo.date.stamp.fromISOString(item.updated)];
+ }else if(attribute === "imageUrlSmall"){
+ return [item.media.thumbnail[1].url];
+ }else if(attribute === "imageUrl"){
+ return [item.content$src];
+ }else if(attribute === "imageUrlMedium"){
+ return [item.media.thumbnail[2].url];
+ }else if(attribute === "link"){
+ return [item.link[1]];
+ }else if(attribute === "tags"){
+ return item.tags.split(" ");
+ }else if(attribute === "description"){
+ return [this._unescapeHtml(item.summary)];
+ }
+ return [];
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item[this._storeRef] === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch picasa items that match to a query
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+
+ if(!request.query){
+ request.query={};
+ }
+
+ //Build up the content to send the request for.
+ var content = {alt: "jsonm", pp: "1", psc: "G"};
+
+ content['start-index'] = "1";
+ if(request.query.start){
+ content['start-index'] = request.query.start;
+ }
+ if(request.query.tags){
+ content.q = request.query.tags;
+ }
+ if(request.query.userid){
+ content.uname = request.query.userid;
+ }
+ if(request.query.userids){
+ content.ids = request.query.userids;
+ }
+ if(request.query.lang){
+ content.hl = request.query.lang;
+ }
+ content['max-results'] = this.maxResults;
+
+ //Linking this up to Picasa is a JOY!
+ var self = this;
+ var handle = null;
+ var myHandler = function(data){
+ if(handle !== null){
+ dojo.disconnect(handle);
+ }
+
+ //Process the items...
+ fetchHandler(self._processPicasaData(data), request);
+ };
+ var getArgs = {
+ url: this._picasaUrl,
+ preventCache: this.urlPreventCache,
+ content: content,
+ callbackParamName: 'callback',
+ handle: myHandler
+ };
+ var deferred = dojo.io.script.get(getArgs);
+
+ deferred.addErrback(function(error){
+ dojo.disconnect(handle);
+ errorHandler(error, request);
+ });
+ },
+
+ _processPicasaData: function(data){
+ var items = [];
+ if(data.feed){
+ items = data.feed.entry;
+ //Add on the store ref so that isItem can work.
+ for(var i = 0; i < items.length; i++){
+ var item = items[i];
+ item[this._storeRef] = this;
+ }
+ }
+ return items;
+ },
+
+ _unescapeHtml: function(str){
+ // summary: Utility function to un-escape XML special characters in an HTML string.
+ // description: Utility function to un-escape XML special characters in an HTML string.
+ // str: String.
+ // The string to un-escape
+ // returns: HTML String converted back to the normal text (unescaped) characters (<,>,&, ", etc,).
+ //
+ //TODO: Check to see if theres already compatible escape() in dojo.string or dojo.html
+ if(str){
+ str = str.replace(/&amp;/gm, "&").replace(/&lt;/gm, "<").replace(/&gt;/gm, ">").replace(/&quot;/gm, "\"");
+ str = str.replace(/&#39;/gm, "'");
+ }
+ return str;
+ }
+});
+dojo.extend(dojox.data.PicasaStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/QueryReadStore.js b/js/dojo-1.6/dojox/data/QueryReadStore.js
new file mode 100644
index 0000000..7762c57
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/QueryReadStore.js
@@ -0,0 +1,530 @@
+/*
+ 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["dojox.data.QueryReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.QueryReadStore"] = true;
+dojo.provide("dojox.data.QueryReadStore");
+dojo.require("dojo.data.util.sorter");
+dojo.require("dojo.string");
+
+
+
+dojo.declare("dojox.data.QueryReadStore",
+ null,
+ {
+ // summary:
+ // This class provides a store that is mainly intended to be used
+ // for loading data dynamically from the server, used i.e. for
+ // retreiving chunks of data from huge data stores on the server (by server-side filtering!).
+ // Upon calling the fetch() method of this store the data are requested from
+ // the server if they are not yet loaded for paging (or cached).
+ //
+ // For example used for a combobox which works on lots of data. It
+ // can be used to retreive the data partially upon entering the
+ // letters "ac" it returns only items like "action", "acting", etc.
+ //
+ // note:
+ // The field name "id" in a query is reserved for looking up data
+ // by id. This is necessary as before the first fetch, the store
+ // has no way of knowing which field the server will declare as
+ // identifier.
+ //
+ // example:
+ // | // The parameter "query" contains the data that are sent to the server.
+ // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
+ // | store.fetch({query:{name:'a'}, queryOptions:{ignoreCase:false}});
+ //
+ // | // Since "serverQuery" is given, it overrules and those data are
+ // | // sent to the server.
+ // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
+ // | store.fetch({serverQuery:{name:'a'}, queryOptions:{ignoreCase:false}});
+ //
+ // | <div dojoType="dojox.data.QueryReadStore"
+ // | jsId="store2"
+ // | url="../tests/stores/QueryReadStore.php"
+ // | requestMethod="post"></div>
+ // | <div dojoType="dojox.grid.data.DojoData"
+ // | jsId="model2"
+ // | store="store2"
+ // | sortFields="[{attribute: 'name', descending: true}]"
+ // | rowsPerPage="30"></div>
+ // | <div dojoType="dojox.Grid" id="grid2"
+ // | model="model2"
+ // | structure="gridLayout"
+ // | style="height:300px; width:800px;"></div>
+
+ //
+ // todo:
+ // - there is a bug in the paging, when i set start:2, count:5 after an initial fetch() and doClientPaging:true
+ // it returns 6 elemetns, though count=5, try it in QueryReadStore.html
+ // - add optional caching
+ // - when the first query searched for "a" and the next for a subset of
+ // the first, i.e. "ab" then we actually dont need a server request, if
+ // we have client paging, we just need to filter the items we already have
+ // that might also be tooo much logic
+
+ url:"",
+ requestMethod:"get",
+ //useCache:false,
+
+ // We use the name in the errors, once the name is fixed hardcode it, may be.
+ _className:"dojox.data.QueryReadStore",
+
+ // This will contain the items we have loaded from the server.
+ // The contents of this array is optimized to satisfy all read-api requirements
+ // and for using lesser storage, so the keys and their content need some explaination:
+ // this._items[0].i - the item itself
+ // this._items[0].r - a reference to the store, so we can identify the item
+ // securly. We set this reference right after receiving the item from the
+ // server.
+ _items:[],
+
+ // Store the last query that triggered xhr request to the server.
+ // So we can compare if the request changed and if we shall reload
+ // (this also depends on other factors, such as is caching used, etc).
+ _lastServerQuery:null,
+
+ // Store how many rows we have so that we can pass it to a clientPaging handler
+ _numRows:-1,
+
+ // Store a hash of the last server request. Actually I introduced this
+ // for testing, so I can check if no unnecessary requests were issued for
+ // client-side-paging.
+ lastRequestHash:null,
+
+ // summary:
+ // By default every request for paging is sent to the server.
+ doClientPaging:false,
+
+ // summary:
+ // By default all the sorting is done serverside before the data is returned
+ // which is the proper place to be doing it for really large datasets.
+ doClientSorting:false,
+
+ // Items by identify for Identify API
+ _itemsByIdentity:null,
+
+ // Identifier used
+ _identifier:null,
+
+ _features: {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true},
+
+ _labelAttr: "label",
+
+ constructor: function(/* Object */ params){
+ dojo.mixin(this,params);
+ },
+
+ getValue: function(/* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){
+ // According to the Read API comments in getValue() and exception is
+ // thrown when an item is not an item or the attribute not a string!
+ this._assertIsItem(item);
+ if(!dojo.isString(attribute)){
+ throw new Error(this._className+".getValue(): Invalid attribute, string expected!");
+ }
+ if(!this.hasAttribute(item, attribute)){
+ // read api says: return defaultValue "only if *item* does not have a value for *attribute*."
+ // Is this the case here? The attribute doesn't exist, but a defaultValue, sounds reasonable.
+ if(defaultValue){
+ return defaultValue;
+ }
+ }
+ return item.i[attribute];
+ },
+
+ getValues: function(/* item */ item, /* attribute-name-string */ attribute){
+ this._assertIsItem(item);
+ var ret = [];
+ if(this.hasAttribute(item, attribute)){
+ ret.push(item.i[attribute]);
+ }
+ return ret;
+ },
+
+ getAttributes: function(/* item */ item){
+ this._assertIsItem(item);
+ var ret = [];
+ for(var i in item.i){
+ ret.push(i);
+ }
+ return ret;
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.isItem(item) && typeof item.i[attribute]!="undefined";
+ },
+
+ containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value){
+ var values = this.getValues(item, attribute);
+ var len = values.length;
+ for(var i=0; i<len; i++){
+ if(values[i] == value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ isItem: function(/* anything */ something){
+ // Some basic tests, that are quick and easy to do here.
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem("");
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem({});
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem(0);
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem({name:"me", label:"me too"});
+ // false
+ //
+ if(something){
+ return typeof something.r != "undefined" && something.r == this;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // Currently we dont have any state that tells if an item is loaded or not
+ // if the item exists its also loaded.
+ // This might change when we start working with refs inside items ...
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* object */ args){
+ if(this.isItemLoaded(args.item)){
+ return;
+ }
+ // Actually we have nothing to do here, or at least I dont know what to do here ...
+ },
+
+ fetch:function(/* Object? */ request){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch() this is just a copy and I adjusted
+ // only the paging, since it happens on the server if doClientPaging is
+ // false, thx to http://trac.dojotoolkit.org/ticket/4761 reporting this.
+ // Would be nice to be able to use simpleFetch() to reduce copied code,
+ // but i dont know how yet. Ideas please!
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ var self = this;
+
+ var _errorHandler = function(errorData, requestObject){
+ if(requestObject.onError){
+ var scope = requestObject.scope || dojo.global;
+ requestObject.onError.call(scope, errorData, requestObject);
+ }
+ };
+
+ var _fetchHandler = function(items, requestObject, numRows){
+ var oldAbortFunction = requestObject.abort || null;
+ var aborted = false;
+
+ var startIndex = requestObject.start?requestObject.start:0;
+ if(self.doClientPaging == false){
+ // For client paging we dont need no slicing of the result.
+ startIndex = 0;
+ }
+ var endIndex = requestObject.count?(startIndex + requestObject.count):items.length;
+
+ requestObject.abort = function(){
+ aborted = true;
+ if(oldAbortFunction){
+ oldAbortFunction.call(requestObject);
+ }
+ };
+
+ var scope = requestObject.scope || dojo.global;
+ if(!requestObject.store){
+ requestObject.store = self;
+ }
+ if(requestObject.onBegin){
+ requestObject.onBegin.call(scope, numRows, requestObject);
+ }
+ if(requestObject.sort && self.doClientSorting){
+ items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
+ }
+ if(requestObject.onItem){
+ for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
+ var item = items[i];
+ if(!aborted){
+ requestObject.onItem.call(scope, item, requestObject);
+ }
+ }
+ }
+ if(requestObject.onComplete && !aborted){
+ var subset = null;
+ if(!requestObject.onItem){
+ subset = items.slice(startIndex, endIndex);
+ }
+ requestObject.onComplete.call(scope, subset, requestObject);
+ }
+ };
+ this._fetchItems(request, _fetchHandler, _errorHandler);
+ return request; // Object
+ },
+
+ getFeatures: function(){
+ return this._features;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // I have no idea if this is really needed ...
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this._labelAttr && this.isItem(item)){
+ return this.getValue(item, this._labelAttr); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this._labelAttr){
+ return [this._labelAttr]; //array
+ }
+ return null; //null
+ },
+
+ _xhrFetchHandler: function(data, request, fetchHandler, errorHandler){
+ data = this._filterResponse(data);
+ if(data.label){
+ this._labelAttr = data.label;
+ }
+ var numRows = data.numRows || -1;
+
+ this._items = [];
+ // Store a ref to "this" in each item, so we can simply check if an item
+ // really origins form here (idea is from ItemFileReadStore, I just don't know
+ // how efficient the real storage use, garbage collection effort, etc. is).
+ dojo.forEach(data.items,function(e){
+ this._items.push({i:e, r:this});
+ },this);
+
+ var identifier = data.identifier;
+ this._itemsByIdentity = {};
+ if(identifier){
+ this._identifier = identifier;
+ var i;
+ for(i = 0; i < this._items.length; ++i){
+ var item = this._items[i].i;
+ var identity = item[identifier];
+ if(!this._itemsByIdentity[identity]){
+ this._itemsByIdentity[identity] = item;
+ }else{
+ throw new Error(this._className+": The json data as specified by: [" + this.url + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }
+ }
+ }else{
+ this._identifier = Number;
+ for(i = 0; i < this._items.length; ++i){
+ this._items[i].n = i;
+ }
+ }
+
+ // TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize
+ // (does it really sanititze them) and store the data optimal. should we? for security reasons???
+ numRows = this._numRows = (numRows === -1) ? this._items.length : numRows;
+ fetchHandler(this._items, request, numRows);
+ this._numRows = numRows;
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // The request contains the data as defined in the Read-API.
+ // Additionally there is following keyword "serverQuery".
+ //
+ // The *serverQuery* parameter, optional.
+ // This parameter contains the data that will be sent to the server.
+ // If this parameter is not given the parameter "query"'s
+ // data are sent to the server. This is done for some reasons:
+ // - to specify explicitly which data are sent to the server, they
+ // might also be a mix of what is contained in "query", "queryOptions"
+ // and the paging parameters "start" and "count" or may be even
+ // completely different things.
+ // - don't modify the request.query data, so the interface using this
+ // store can rely on unmodified data, as the combobox dijit currently
+ // does it, it compares if the query has changed
+ // - request.query is required by the Read-API
+ //
+ // I.e. the following examples might be sent via GET:
+ // fetch({query:{name:"abc"}, queryOptions:{ignoreCase:true}})
+ // the URL will become: /url.php?name=abc
+ //
+ // fetch({serverQuery:{q:"abc", c:true}, query:{name:"abc"}, queryOptions:{ignoreCase:true}})
+ // the URL will become: /url.php?q=abc&c=true
+ // // The serverQuery-parameter has overruled the query-parameter
+ // // but the query parameter stays untouched, but is not sent to the server!
+ // // The serverQuery contains more data than the query, so they might differ!
+ //
+
+ var serverQuery = request.serverQuery || request.query || {};
+ //Need to add start and count
+ if(!this.doClientPaging){
+ serverQuery.start = request.start || 0;
+ // Count might not be sent if not given.
+ if(request.count){
+ serverQuery.count = request.count;
+ }
+ }
+ if(!this.doClientSorting && request.sort){
+ var sortInfo = [];
+ dojo.forEach(request.sort, function(sort){
+ if(sort && sort.attribute){
+ sortInfo.push((sort.descending ? "-" : "") + sort.attribute);
+ }
+ });
+ serverQuery.sort = sortInfo.join(',');
+ }
+ // Compare the last query and the current query by simply json-encoding them,
+ // so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()???
+ if(this.doClientPaging && this._lastServerQuery !== null &&
+ dojo.toJson(serverQuery) == dojo.toJson(this._lastServerQuery)
+ ){
+ this._numRows = (this._numRows === -1) ? this._items.length : this._numRows;
+ fetchHandler(this._items, request, this._numRows);
+ }else{
+ var xhrFunc = this.requestMethod.toLowerCase() == "post" ? dojo.xhrPost : dojo.xhrGet;
+ var xhrHandler = xhrFunc({url:this.url, handleAs:"json-comment-optional", content:serverQuery, failOk: true});
+ request.abort = function(){
+ xhrHandler.cancel();
+ };
+ xhrHandler.addCallback(dojo.hitch(this, function(data){
+ this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
+ }));
+ xhrHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ // Generate the hash using the time in milliseconds and a randon number.
+ // Since Math.randon() returns something like: 0.23453463, we just remove the "0."
+ // probably just for esthetic reasons :-).
+ this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2);
+ this._lastServerQuery = dojo.mixin({}, serverQuery);
+ }
+ },
+
+ _filterResponse: function(data){
+ // summary:
+ // If the data from servers needs to be processed before it can be processed by this
+ // store, then this function should be re-implemented in subclass. This default
+ // implementation just return the data unchanged.
+ // data:
+ // The data received from server
+ return data;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // It throws an error if item is not valid, so you can call it in every method that needs to
+ // throw an error when item is invalid.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this._className+": Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error(this._className+": Invalid attribute argument ('"+attribute+"').");
+ }
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ // See if we have already loaded the item with that id
+ // In case there hasn't been a fetch yet, _itemsByIdentity is null
+ // and thus a fetch will be triggered below.
+ if(this._itemsByIdentity){
+ var item = this._itemsByIdentity[keywordArgs.identity];
+ if(!(item === undefined)){
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ keywordArgs.onItem.call(scope, {i:item, r:this});
+ }
+ return;
+ }
+ }
+
+ // Otherwise we need to go remote
+ // Set up error handler
+ var _errorHandler = function(errorData, requestObject){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, errorData);
+ }
+ };
+
+ // Set up fetch handler
+ var _fetchHandler = function(items, requestObject){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ try{
+ // There is supposed to be only one result
+ var item = null;
+ if(items && items.length == 1){
+ item = items[0];
+ }
+
+ // If no item was found, item is still null and we'll
+ // fire the onItem event with the null here
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ };
+
+ // Construct query
+ var request = {serverQuery:{id:keywordArgs.identity}};
+
+ // Dispatch query
+ this._fetchItems(request, _fetchHandler, _errorHandler);
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ var identifier = null;
+ if(this._identifier === Number){
+ identifier = item.n; // Number
+ }else{
+ identifier = item.i[this._identifier];
+ }
+ return identifier;
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ return [this._identifier];
+ }
+ }
+);
+
+}
diff --git a/js/dojo-1.6/dojox/data/QueryReadStore.xd.js b/js/dojo-1.6/dojox/data/QueryReadStore.xd.js
new file mode 100644
index 0000000..4da81cd
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/QueryReadStore.xd.js
@@ -0,0 +1,536 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.QueryReadStore"],
+["require", "dojo.data.util.sorter"],
+["require", "dojo.string"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.QueryReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.QueryReadStore"] = true;
+dojo.provide("dojox.data.QueryReadStore");
+dojo.require("dojo.data.util.sorter");
+dojo.require("dojo.string");
+
+
+
+dojo.declare("dojox.data.QueryReadStore",
+ null,
+ {
+ // summary:
+ // This class provides a store that is mainly intended to be used
+ // for loading data dynamically from the server, used i.e. for
+ // retreiving chunks of data from huge data stores on the server (by server-side filtering!).
+ // Upon calling the fetch() method of this store the data are requested from
+ // the server if they are not yet loaded for paging (or cached).
+ //
+ // For example used for a combobox which works on lots of data. It
+ // can be used to retreive the data partially upon entering the
+ // letters "ac" it returns only items like "action", "acting", etc.
+ //
+ // note:
+ // The field name "id" in a query is reserved for looking up data
+ // by id. This is necessary as before the first fetch, the store
+ // has no way of knowing which field the server will declare as
+ // identifier.
+ //
+ // example:
+ // | // The parameter "query" contains the data that are sent to the server.
+ // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
+ // | store.fetch({query:{name:'a'}, queryOptions:{ignoreCase:false}});
+ //
+ // | // Since "serverQuery" is given, it overrules and those data are
+ // | // sent to the server.
+ // | var store = new dojox.data.QueryReadStore({url:'/search.php'});
+ // | store.fetch({serverQuery:{name:'a'}, queryOptions:{ignoreCase:false}});
+ //
+ // | <div dojoType="dojox.data.QueryReadStore"
+ // | jsId="store2"
+ // | url="../tests/stores/QueryReadStore.php"
+ // | requestMethod="post"></div>
+ // | <div dojoType="dojox.grid.data.DojoData"
+ // | jsId="model2"
+ // | store="store2"
+ // | sortFields="[{attribute: 'name', descending: true}]"
+ // | rowsPerPage="30"></div>
+ // | <div dojoType="dojox.Grid" id="grid2"
+ // | model="model2"
+ // | structure="gridLayout"
+ // | style="height:300px; width:800px;"></div>
+
+ //
+ // todo:
+ // - there is a bug in the paging, when i set start:2, count:5 after an initial fetch() and doClientPaging:true
+ // it returns 6 elemetns, though count=5, try it in QueryReadStore.html
+ // - add optional caching
+ // - when the first query searched for "a" and the next for a subset of
+ // the first, i.e. "ab" then we actually dont need a server request, if
+ // we have client paging, we just need to filter the items we already have
+ // that might also be tooo much logic
+
+ url:"",
+ requestMethod:"get",
+ //useCache:false,
+
+ // We use the name in the errors, once the name is fixed hardcode it, may be.
+ _className:"dojox.data.QueryReadStore",
+
+ // This will contain the items we have loaded from the server.
+ // The contents of this array is optimized to satisfy all read-api requirements
+ // and for using lesser storage, so the keys and their content need some explaination:
+ // this._items[0].i - the item itself
+ // this._items[0].r - a reference to the store, so we can identify the item
+ // securly. We set this reference right after receiving the item from the
+ // server.
+ _items:[],
+
+ // Store the last query that triggered xhr request to the server.
+ // So we can compare if the request changed and if we shall reload
+ // (this also depends on other factors, such as is caching used, etc).
+ _lastServerQuery:null,
+
+ // Store how many rows we have so that we can pass it to a clientPaging handler
+ _numRows:-1,
+
+ // Store a hash of the last server request. Actually I introduced this
+ // for testing, so I can check if no unnecessary requests were issued for
+ // client-side-paging.
+ lastRequestHash:null,
+
+ // summary:
+ // By default every request for paging is sent to the server.
+ doClientPaging:false,
+
+ // summary:
+ // By default all the sorting is done serverside before the data is returned
+ // which is the proper place to be doing it for really large datasets.
+ doClientSorting:false,
+
+ // Items by identify for Identify API
+ _itemsByIdentity:null,
+
+ // Identifier used
+ _identifier:null,
+
+ _features: {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true},
+
+ _labelAttr: "label",
+
+ constructor: function(/* Object */ params){
+ dojo.mixin(this,params);
+ },
+
+ getValue: function(/* item */ item, /* attribute-name-string */ attribute, /* value? */ defaultValue){
+ // According to the Read API comments in getValue() and exception is
+ // thrown when an item is not an item or the attribute not a string!
+ this._assertIsItem(item);
+ if(!dojo.isString(attribute)){
+ throw new Error(this._className+".getValue(): Invalid attribute, string expected!");
+ }
+ if(!this.hasAttribute(item, attribute)){
+ // read api says: return defaultValue "only if *item* does not have a value for *attribute*."
+ // Is this the case here? The attribute doesn't exist, but a defaultValue, sounds reasonable.
+ if(defaultValue){
+ return defaultValue;
+ }
+ }
+ return item.i[attribute];
+ },
+
+ getValues: function(/* item */ item, /* attribute-name-string */ attribute){
+ this._assertIsItem(item);
+ var ret = [];
+ if(this.hasAttribute(item, attribute)){
+ ret.push(item.i[attribute]);
+ }
+ return ret;
+ },
+
+ getAttributes: function(/* item */ item){
+ this._assertIsItem(item);
+ var ret = [];
+ for(var i in item.i){
+ ret.push(i);
+ }
+ return ret;
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute-name-string */ attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttribute()
+ return this.isItem(item) && typeof item.i[attribute]!="undefined";
+ },
+
+ containsValue: function(/* item */ item, /* attribute-name-string */ attribute, /* anything */ value){
+ var values = this.getValues(item, attribute);
+ var len = values.length;
+ for(var i=0; i<len; i++){
+ if(values[i] == value){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ isItem: function(/* anything */ something){
+ // Some basic tests, that are quick and easy to do here.
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem("");
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem({});
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem(0);
+ // false
+ //
+ // >>> var store = new dojox.data.QueryReadStore({});
+ // >>> store.isItem({name:"me", label:"me too"});
+ // false
+ //
+ if(something){
+ return typeof something.r != "undefined" && something.r == this;
+ }
+ return false;
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // Currently we dont have any state that tells if an item is loaded or not
+ // if the item exists its also loaded.
+ // This might change when we start working with refs inside items ...
+ return this.isItem(something);
+ },
+
+ loadItem: function(/* object */ args){
+ if(this.isItemLoaded(args.item)){
+ return;
+ }
+ // Actually we have nothing to do here, or at least I dont know what to do here ...
+ },
+
+ fetch:function(/* Object? */ request){
+ // summary:
+ // See dojo.data.util.simpleFetch.fetch() this is just a copy and I adjusted
+ // only the paging, since it happens on the server if doClientPaging is
+ // false, thx to http://trac.dojotoolkit.org/ticket/4761 reporting this.
+ // Would be nice to be able to use simpleFetch() to reduce copied code,
+ // but i dont know how yet. Ideas please!
+ request = request || {};
+ if(!request.store){
+ request.store = this;
+ }
+ var self = this;
+
+ var _errorHandler = function(errorData, requestObject){
+ if(requestObject.onError){
+ var scope = requestObject.scope || dojo.global;
+ requestObject.onError.call(scope, errorData, requestObject);
+ }
+ };
+
+ var _fetchHandler = function(items, requestObject, numRows){
+ var oldAbortFunction = requestObject.abort || null;
+ var aborted = false;
+
+ var startIndex = requestObject.start?requestObject.start:0;
+ if(self.doClientPaging == false){
+ // For client paging we dont need no slicing of the result.
+ startIndex = 0;
+ }
+ var endIndex = requestObject.count?(startIndex + requestObject.count):items.length;
+
+ requestObject.abort = function(){
+ aborted = true;
+ if(oldAbortFunction){
+ oldAbortFunction.call(requestObject);
+ }
+ };
+
+ var scope = requestObject.scope || dojo.global;
+ if(!requestObject.store){
+ requestObject.store = self;
+ }
+ if(requestObject.onBegin){
+ requestObject.onBegin.call(scope, numRows, requestObject);
+ }
+ if(requestObject.sort && self.doClientSorting){
+ items.sort(dojo.data.util.sorter.createSortFunction(requestObject.sort, self));
+ }
+ if(requestObject.onItem){
+ for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
+ var item = items[i];
+ if(!aborted){
+ requestObject.onItem.call(scope, item, requestObject);
+ }
+ }
+ }
+ if(requestObject.onComplete && !aborted){
+ var subset = null;
+ if(!requestObject.onItem){
+ subset = items.slice(startIndex, endIndex);
+ }
+ requestObject.onComplete.call(scope, subset, requestObject);
+ }
+ };
+ this._fetchItems(request, _fetchHandler, _errorHandler);
+ return request; // Object
+ },
+
+ getFeatures: function(){
+ return this._features;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // I have no idea if this is really needed ...
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if(this._labelAttr && this.isItem(item)){
+ return this.getValue(item, this._labelAttr); //String
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this._labelAttr){
+ return [this._labelAttr]; //array
+ }
+ return null; //null
+ },
+
+ _xhrFetchHandler: function(data, request, fetchHandler, errorHandler){
+ data = this._filterResponse(data);
+ if(data.label){
+ this._labelAttr = data.label;
+ }
+ var numRows = data.numRows || -1;
+
+ this._items = [];
+ // Store a ref to "this" in each item, so we can simply check if an item
+ // really origins form here (idea is from ItemFileReadStore, I just don't know
+ // how efficient the real storage use, garbage collection effort, etc. is).
+ dojo.forEach(data.items,function(e){
+ this._items.push({i:e, r:this});
+ },this);
+
+ var identifier = data.identifier;
+ this._itemsByIdentity = {};
+ if(identifier){
+ this._identifier = identifier;
+ var i;
+ for(i = 0; i < this._items.length; ++i){
+ var item = this._items[i].i;
+ var identity = item[identifier];
+ if(!this._itemsByIdentity[identity]){
+ this._itemsByIdentity[identity] = item;
+ }else{
+ throw new Error(this._className+": The json data as specified by: [" + this.url + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
+ }
+ }
+ }else{
+ this._identifier = Number;
+ for(i = 0; i < this._items.length; ++i){
+ this._items[i].n = i;
+ }
+ }
+
+ // TODO actually we should do the same as dojo.data.ItemFileReadStore._getItemsFromLoadedData() to sanitize
+ // (does it really sanititze them) and store the data optimal. should we? for security reasons???
+ numRows = this._numRows = (numRows === -1) ? this._items.length : numRows;
+ fetchHandler(this._items, request, numRows);
+ this._numRows = numRows;
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // The request contains the data as defined in the Read-API.
+ // Additionally there is following keyword "serverQuery".
+ //
+ // The *serverQuery* parameter, optional.
+ // This parameter contains the data that will be sent to the server.
+ // If this parameter is not given the parameter "query"'s
+ // data are sent to the server. This is done for some reasons:
+ // - to specify explicitly which data are sent to the server, they
+ // might also be a mix of what is contained in "query", "queryOptions"
+ // and the paging parameters "start" and "count" or may be even
+ // completely different things.
+ // - don't modify the request.query data, so the interface using this
+ // store can rely on unmodified data, as the combobox dijit currently
+ // does it, it compares if the query has changed
+ // - request.query is required by the Read-API
+ //
+ // I.e. the following examples might be sent via GET:
+ // fetch({query:{name:"abc"}, queryOptions:{ignoreCase:true}})
+ // the URL will become: /url.php?name=abc
+ //
+ // fetch({serverQuery:{q:"abc", c:true}, query:{name:"abc"}, queryOptions:{ignoreCase:true}})
+ // the URL will become: /url.php?q=abc&c=true
+ // // The serverQuery-parameter has overruled the query-parameter
+ // // but the query parameter stays untouched, but is not sent to the server!
+ // // The serverQuery contains more data than the query, so they might differ!
+ //
+
+ var serverQuery = request.serverQuery || request.query || {};
+ //Need to add start and count
+ if(!this.doClientPaging){
+ serverQuery.start = request.start || 0;
+ // Count might not be sent if not given.
+ if(request.count){
+ serverQuery.count = request.count;
+ }
+ }
+ if(!this.doClientSorting && request.sort){
+ var sortInfo = [];
+ dojo.forEach(request.sort, function(sort){
+ if(sort && sort.attribute){
+ sortInfo.push((sort.descending ? "-" : "") + sort.attribute);
+ }
+ });
+ serverQuery.sort = sortInfo.join(',');
+ }
+ // Compare the last query and the current query by simply json-encoding them,
+ // so we dont have to do any deep object compare ... is there some dojo.areObjectsEqual()???
+ if(this.doClientPaging && this._lastServerQuery !== null &&
+ dojo.toJson(serverQuery) == dojo.toJson(this._lastServerQuery)
+ ){
+ this._numRows = (this._numRows === -1) ? this._items.length : this._numRows;
+ fetchHandler(this._items, request, this._numRows);
+ }else{
+ var xhrFunc = this.requestMethod.toLowerCase() == "post" ? dojo.xhrPost : dojo.xhrGet;
+ var xhrHandler = xhrFunc({url:this.url, handleAs:"json-comment-optional", content:serverQuery, failOk: true});
+ request.abort = function(){
+ xhrHandler.cancel();
+ };
+ xhrHandler.addCallback(dojo.hitch(this, function(data){
+ this._xhrFetchHandler(data, request, fetchHandler, errorHandler);
+ }));
+ xhrHandler.addErrback(function(error){
+ errorHandler(error, request);
+ });
+ // Generate the hash using the time in milliseconds and a randon number.
+ // Since Math.randon() returns something like: 0.23453463, we just remove the "0."
+ // probably just for esthetic reasons :-).
+ this.lastRequestHash = new Date().getTime()+"-"+String(Math.random()).substring(2);
+ this._lastServerQuery = dojo.mixin({}, serverQuery);
+ }
+ },
+
+ _filterResponse: function(data){
+ // summary:
+ // If the data from servers needs to be processed before it can be processed by this
+ // store, then this function should be re-implemented in subclass. This default
+ // implementation just return the data unchanged.
+ // data:
+ // The data received from server
+ return data;
+ },
+
+ _assertIsItem: function(/* item */ item){
+ // summary:
+ // It throws an error if item is not valid, so you can call it in every method that needs to
+ // throw an error when item is invalid.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error(this._className+": Invalid item argument.");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error(this._className+": Invalid attribute argument ('"+attribute+"').");
+ }
+ },
+
+ fetchItemByIdentity: function(/* Object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity()
+
+ // See if we have already loaded the item with that id
+ // In case there hasn't been a fetch yet, _itemsByIdentity is null
+ // and thus a fetch will be triggered below.
+ if(this._itemsByIdentity){
+ var item = this._itemsByIdentity[keywordArgs.identity];
+ if(!(item === undefined)){
+ if(keywordArgs.onItem){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ keywordArgs.onItem.call(scope, {i:item, r:this});
+ }
+ return;
+ }
+ }
+
+ // Otherwise we need to go remote
+ // Set up error handler
+ var _errorHandler = function(errorData, requestObject){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, errorData);
+ }
+ };
+
+ // Set up fetch handler
+ var _fetchHandler = function(items, requestObject){
+ var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global;
+ try{
+ // There is supposed to be only one result
+ var item = null;
+ if(items && items.length == 1){
+ item = items[0];
+ }
+
+ // If no item was found, item is still null and we'll
+ // fire the onItem event with the null here
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, item);
+ }
+ }catch(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ }
+ };
+
+ // Construct query
+ var request = {serverQuery:{id:keywordArgs.identity}};
+
+ // Dispatch query
+ this._fetchItems(request, _fetchHandler, _errorHandler);
+ },
+
+ getIdentity: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentity()
+ var identifier = null;
+ if(this._identifier === Number){
+ identifier = item.n; // Number
+ }else{
+ identifier = item.i[this._identifier];
+ }
+ return identifier;
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Identity.getIdentityAttributes()
+ return [this._identifier];
+ }
+ }
+);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/README b/js/dojo-1.6/dojox/data/README
new file mode 100644
index 0000000..4641983
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/README
@@ -0,0 +1,127 @@
+-------------------------------------------------------------------------------
+DojoX Data
+-------------------------------------------------------------------------------
+Version 1.1
+Release date: 03/18/2008
+-------------------------------------------------------------------------------
+Project state: production
+-------------------------------------------------------------------------------
+Project authors
+ Jared Jurkiewicz (jared.jurkiewicz@gmail.com) (FileStore, HtmlStore, XmlStore, FlickrStore, CssRuleStore, CssClassStore, AppStore, others).
+ Shane O'Sullivan (shaneosullivan1@gmail.com) (FlickrRestStore, AtomReadStore, GoogleSeachStore, GoogleFeedStore)
+ Wolfram Kriesing (wolfram@kriesing.de) (QueryReadStore)
+ Dustin Machi (dmachi@dojotolkit.org) (jsonPathStore);
+ Russell Jones (KeyValueStore) (CLA)
+ Benjamin Schell (KeyValueStore, CssRuleStore, CssClassStore, AppStore, OpenSearchStore) (Corporate CLA)
+ Kurt Stutsman (kurt@snaplogic.org) (SnapLogicStore)
+ Kris Zyp (kzyp@dojotoolkit.org) (JsonRestStore, PersevereStore, S3JsonRestStore, CouchDBRestStore)
+ Frank Fortson (frank.fortson@equorum.com) (AndOrReadStore, AndOrWriteStore)
+
+
+-------------------------------------------------------------------------------
+Project description
+
+The DojoX Data project is a container for extensions and extra example stores
+that implement the dojo.data APIs. It may also contain utility functions for
+working with specific types of data.
+
+-------------------------------------------------------------------------------
+Dependencies:
+
+DojoX Data has dependencies on core dojo (dojo.data), dojox.xml for XmlStore
+and dojox.data.dom(deprecated) and the D.O.H. unit test framework
+-------------------------------------------------------------------------------
+Documentation:
+
+See the Dojo API tool (http://dojotoolkit.org/api)
+-------------------------------------------------------------------------------
+Contributions:
+
+For contributions to be committed into the dojox repository, the datastore
+should have basic unit tests that exercise the API's that the store declares it
+implements. Documentation and demos are a plus, but unit tests are required
+to be committed into this sub-package. This is necessary to help keep the
+provided datastores as stable as possible.
+
+-------------------------------------------------------------------------------
+Installation instructions
+
+Grab the following from the Dojo SVN Repository:
+http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/data/*
+
+Install into the following directory structure:
+/dojox/data/
+
+...which should be at the same level as your Dojo checkout.
+
+/dojox/data/*
+
+Require in the dojox.data stores you wish to use.
+-------------------------------------------------------------------------------
+Additional Notes:
+ dojox.data.AtomReadStore - Reads Atom XML documents.
+
+ dojox.data.CvsStore - comma-separated (spreadsheet output)
+ datastore implementation
+
+ dojox.data.FlickrRestStore - advanced version of: dojox.data.FlickrStore
+ (Caching + user key support)
+
+ dojox.data.FlickrStore - data store driven by Flickr.com public API.
+
+ dojox.data.HtmlTableStore - Implementation of an HTML Table reading
+ datastore
+
+ dojox.data.HtmlStore - Implementation of an HTML reading datastore. Can
+ handle tables, ordered and un-ordered lists, and lists of divs.
+
+ dojox.data.OpmlStore - Store for reading OMPL formatted data
+
+ dojox.data.XmlStore - datastore for XML based services or
+ documents.
+
+ dojox.data.QueryReadStore - datastore to provide serverside URL query
+ matching. Similar to the 0.4.X ComboBox dataUrl parameter.
+
+ dojox.data.jsonPathStore - datastore that takes an arbitrary js object
+ and uses it as the store. Pre-Alpha at the moment.
+
+ dojox.data.KeyValueStore - datastore that mimics a key/value property
+ file format.
+
+ dojox.data.SnapLogicStore - Store to interface to SnapLogic data services.
+
+ dojox.data.JsonRestStore - Store to interface with RESTful HTTP/JSON web services.
+ dojox.data.PersevereStore - Extension of JsonRestStore for Persevere
+ dojox.data.CouchDBRestStore - Extension of JsonRestStore for CouchDB
+ dojox.data.S3JsonRestStore - Extension of JsonRestStore for Amazon S3
+ dojox.data.GoogleSearchStore - Store to interface Google's AJAX search services.
+ There are many subclasses of this store for particular types of searches:
+ dojox.data.GoogleWebSearchStore
+ dojox.data.GoogleBlogSearchStore
+ dojox.data.GoogleLocalSearchStore
+ dojox.data.GoogleVideoSearchStore
+ dojox.data.GoogleNewsSearchStore
+ dojox.data.GoogleBookSearchStore
+ dojox.data.GoogleImageSearchStore
+
+ dojox.data.AndOrReadStore - Demonstrating a more complex query format allowing AND/OR.
+ Based directly on dojo.data.ItemFileReadStore.
+
+ dojox.data.AndOrWriteStore - Demonstrating a more complex query format allowing AND/OR.
+ Based directly on dojo.data.ItemFileWriteStore.
+
+ dojox.data.FileStore - A lazy-loading store designed for searching filesystems with a provided
+ PHP back end. Implements dojo.data.api.Read and dojo.data.api.Identity
+
+ dojox.data.CssRuleStore - A store that allows searching/querying over Css rules loaded in the page in
+ the browser.
+
+ dojox.data.CssClassStore - A store that allows searching/querying over what classes are defined in the page in
+ the browser.
+
+ dojox.data.AppStore - A store that implements full read, write, and identity APIs for working with ATOM documents.
+ The store uses the full APP protocol.
+
+ dojox.data.OpenSearchStore - A store that implements OpenSearch provider search capability.
+
diff --git a/js/dojo-1.6/dojox/data/RailsStore.js b/js/dojo-1.6/dojox/data/RailsStore.js
new file mode 100644
index 0000000..02d5c14
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/RailsStore.js
@@ -0,0 +1,179 @@
+/*
+ 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["dojox.data.RailsStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.RailsStore"] = true;
+dojo.provide("dojox.data.RailsStore");
+dojo.require("dojox.data.JsonRestStore");
+
+
+
+// Contains code donated by Travis Tilley under CLA
+dojo.declare("dojox.data.RailsStore", dojox.data.JsonRestStore, {
+ constructor: function(){
+ // summary:
+ // RailsStore is a data store for interacting with RESTful Rails controllers
+ },
+ preamble: function(options){
+ if(typeof options.target == 'string' && !options.service){
+ var target = options.target.replace(/\/$/g, '');
+
+ // Special getRequest handler for handling content type negotiation via
+ // the Rails format extension, as well as properly setting the ID param
+ // in the URL.
+ var getRequest = function(id, args){
+ args = args || {};
+ var url = target;
+ var query;
+ var ident;
+
+ if(dojo.isObject(id)){
+ ident = '';
+ query = '?' + dojo.objectToQuery(id);
+ }else if(args.queryStr && args.queryStr.indexOf('?') != -1){
+ ident = args.queryStr.replace(/\?.*/, '');
+ query = args.queryStr.replace(/[^?]*\?/g, '?');
+ }else if(dojo.isString(args.query) && args.query.indexOf('?') != -1){
+ ident = args.query.replace(/\?.*/, '');
+ query = args.query.replace(/[^?]*\?/g, '?');
+ }else{
+ ident = id ? id.toString() : '';
+ query = '';
+ }
+
+ if(ident.indexOf('=') != -1){
+ query = ident;
+ ident = '';
+ }
+
+ if(ident){
+ url = url + '/' + ident + '.json' + query;
+ }else{
+ url = url + '.json' + query;
+ }
+
+ var isSync = dojox.rpc._sync;
+ dojox.rpc._sync = false;
+
+ return {
+ url : url,
+ handleAs : 'json',
+ contentType : 'application/json',
+ sync : isSync,
+ headers : {
+ Accept : 'application/json,application/javascript',
+ Range : args && (args.start >= 0 || args.count >= 0)
+ ? "items="
+ + (args.start || '0')
+ + '-'
+ + ((args.count && (args.count
+ + (args.start || 0) - 1)) || '')
+ : undefined
+ }
+ };
+ };
+
+ options.service = dojox.rpc.Rest(this.target, true, null,
+ getRequest);
+ }
+ },
+ fetch: function(args){
+ args = args || {};
+ function addToQueryStr(obj){
+ function buildInitialQueryString(){
+ if(args.queryStr == null){
+ args.queryStr = '';
+ }
+ if(dojo.isObject(args.query)){
+ args.queryStr = '?' + dojo.objectToQuery(args.query);
+ }else if(dojo.isString(args.query)){
+ args.queryStr = args.query;
+ }
+ }
+ function separator(){
+ if(args.queryStr.indexOf('?') == -1){
+ return '?';
+ }else{
+ return '&';
+ }
+ }
+ if(args.queryStr == null){
+ buildInitialQueryString();
+ }
+ args.queryStr = args.queryStr + separator() + dojo.objectToQuery(obj);
+ }
+ if(args.start || args.count){
+ // in addition to the content range headers, also provide query parameters for use
+ // with the will_paginate plugin if so desired.
+ if((args.start || 0) % args.count){
+ throw new Error("The start parameter must be a multiple of the count parameter");
+ }
+ addToQueryStr({
+ page: ((args.start || 0) / args.count) + 1,
+ per_page: args.count
+ });
+ }
+ if(args.sort){
+ // make the sort into query parameters
+ var queryObj = {
+ sortBy : [],
+ sortDir : []
+ };
+
+ dojo.forEach(args.sort, function(item){
+ queryObj.sortBy.push(item.attribute);
+ queryObj.sortDir.push(!!item.descending ? 'DESC' : 'ASC');
+ });
+
+ addToQueryStr(queryObj);
+ delete args.sort;
+ }
+
+ return this.inherited(arguments);
+ },
+ _processResults: function(results, deferred){
+ var items;
+
+ /*
+ * depending on the ActiveRecord::Base.include_root_in_json setting,
+ * you might get back an array of attribute objects, or an array of
+ * objects with the attribute object nested under an attribute having
+ * the same name as the (remote and unguessable) model class.
+ *
+ * 'Example' without root_in_json: [{'id':1, 'text':'first'}]
+ * 'Example' with root_in_json: [{'example':{'id':1, 'text':'first'}}]
+ */
+ if((typeof this.rootAttribute == 'undefined') && results[0]){
+ if(results[0][this.idAttribute]){
+ this.rootAttribute = false;
+ console.debug('RailsStore: without root_in_json');
+ }else{
+ for(var attribute in results[0]){
+ if(results[0][attribute][this.idAttribute]){
+ this.rootAttribute = attribute;
+ console.debug('RailsStore: with root_in_json, attribute: ' + attribute);
+ }
+ }
+ }
+ }
+
+ if(this.rootAttribute){
+ items = dojo.map(results, function(item){
+ return item[this.rootAttribute];
+ }, this);
+ }else{
+ items = results;
+ }
+
+ // index the results
+ var count = results.length;
+ // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets
+ return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: items};
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/RailsStore.xd.js b/js/dojo-1.6/dojox/data/RailsStore.xd.js
new file mode 100644
index 0000000..66db50a
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/RailsStore.xd.js
@@ -0,0 +1,184 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.RailsStore"],
+["require", "dojox.data.JsonRestStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.RailsStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.RailsStore"] = true;
+dojo.provide("dojox.data.RailsStore");
+dojo.require("dojox.data.JsonRestStore");
+
+
+
+// Contains code donated by Travis Tilley under CLA
+dojo.declare("dojox.data.RailsStore", dojox.data.JsonRestStore, {
+ constructor: function(){
+ // summary:
+ // RailsStore is a data store for interacting with RESTful Rails controllers
+ },
+ preamble: function(options){
+ if(typeof options.target == 'string' && !options.service){
+ var target = options.target.replace(/\/$/g, '');
+
+ // Special getRequest handler for handling content type negotiation via
+ // the Rails format extension, as well as properly setting the ID param
+ // in the URL.
+ var getRequest = function(id, args){
+ args = args || {};
+ var url = target;
+ var query;
+ var ident;
+
+ if(dojo.isObject(id)){
+ ident = '';
+ query = '?' + dojo.objectToQuery(id);
+ }else if(args.queryStr && args.queryStr.indexOf('?') != -1){
+ ident = args.queryStr.replace(/\?.*/, '');
+ query = args.queryStr.replace(/[^?]*\?/g, '?');
+ }else if(dojo.isString(args.query) && args.query.indexOf('?') != -1){
+ ident = args.query.replace(/\?.*/, '');
+ query = args.query.replace(/[^?]*\?/g, '?');
+ }else{
+ ident = id ? id.toString() : '';
+ query = '';
+ }
+
+ if(ident.indexOf('=') != -1){
+ query = ident;
+ ident = '';
+ }
+
+ if(ident){
+ url = url + '/' + ident + '.json' + query;
+ }else{
+ url = url + '.json' + query;
+ }
+
+ var isSync = dojox.rpc._sync;
+ dojox.rpc._sync = false;
+
+ return {
+ url : url,
+ handleAs : 'json',
+ contentType : 'application/json',
+ sync : isSync,
+ headers : {
+ Accept : 'application/json,application/javascript',
+ Range : args && (args.start >= 0 || args.count >= 0)
+ ? "items="
+ + (args.start || '0')
+ + '-'
+ + ((args.count && (args.count
+ + (args.start || 0) - 1)) || '')
+ : undefined
+ }
+ };
+ };
+
+ options.service = dojox.rpc.Rest(this.target, true, null,
+ getRequest);
+ }
+ },
+ fetch: function(args){
+ args = args || {};
+ function addToQueryStr(obj){
+ function buildInitialQueryString(){
+ if(args.queryStr == null){
+ args.queryStr = '';
+ }
+ if(dojo.isObject(args.query)){
+ args.queryStr = '?' + dojo.objectToQuery(args.query);
+ }else if(dojo.isString(args.query)){
+ args.queryStr = args.query;
+ }
+ }
+ function separator(){
+ if(args.queryStr.indexOf('?') == -1){
+ return '?';
+ }else{
+ return '&';
+ }
+ }
+ if(args.queryStr == null){
+ buildInitialQueryString();
+ }
+ args.queryStr = args.queryStr + separator() + dojo.objectToQuery(obj);
+ }
+ if(args.start || args.count){
+ // in addition to the content range headers, also provide query parameters for use
+ // with the will_paginate plugin if so desired.
+ if((args.start || 0) % args.count){
+ throw new Error("The start parameter must be a multiple of the count parameter");
+ }
+ addToQueryStr({
+ page: ((args.start || 0) / args.count) + 1,
+ per_page: args.count
+ });
+ }
+ if(args.sort){
+ // make the sort into query parameters
+ var queryObj = {
+ sortBy : [],
+ sortDir : []
+ };
+
+ dojo.forEach(args.sort, function(item){
+ queryObj.sortBy.push(item.attribute);
+ queryObj.sortDir.push(!!item.descending ? 'DESC' : 'ASC');
+ });
+
+ addToQueryStr(queryObj);
+ delete args.sort;
+ }
+
+ return this.inherited(arguments);
+ },
+ _processResults: function(results, deferred){
+ var items;
+
+ /*
+ * depending on the ActiveRecord::Base.include_root_in_json setting,
+ * you might get back an array of attribute objects, or an array of
+ * objects with the attribute object nested under an attribute having
+ * the same name as the (remote and unguessable) model class.
+ *
+ * 'Example' without root_in_json: [{'id':1, 'text':'first'}]
+ * 'Example' with root_in_json: [{'example':{'id':1, 'text':'first'}}]
+ */
+ if((typeof this.rootAttribute == 'undefined') && results[0]){
+ if(results[0][this.idAttribute]){
+ this.rootAttribute = false;
+ console.debug('RailsStore: without root_in_json');
+ }else{
+ for(var attribute in results[0]){
+ if(results[0][attribute][this.idAttribute]){
+ this.rootAttribute = attribute;
+ console.debug('RailsStore: with root_in_json, attribute: ' + attribute);
+ }
+ }
+ }
+ }
+
+ if(this.rootAttribute){
+ items = dojo.map(results, function(item){
+ return item[this.rootAttribute];
+ }, this);
+ }else{
+ items = results;
+ }
+
+ // index the results
+ var count = results.length;
+ // if we don't know the length, and it is partial result, we will guess that it is twice as big, that will work for most widgets
+ return {totalCount:deferred.fullLength || (deferred.request.count == count ? (deferred.request.start || 0) + count * 2 : count), items: items};
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/S3Store.js b/js/dojo-1.6/dojox/data/S3Store.js
new file mode 100644
index 0000000..0e01255
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/S3Store.js
@@ -0,0 +1,47 @@
+/*
+ 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["dojox.data.S3Store"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.S3Store"] = true;
+dojo.provide("dojox.data.S3Store");
+dojo.require("dojox.data.JsonRestStore");
+dojo.require("dojox.rpc.ProxiedPath");
+
+
+
+// S3JsonRestStore is an extension of JsonRestStore to handle
+// Amazon's S3 service using JSON data
+
+dojo.declare("dojox.data.S3Store",
+ dojox.data.JsonRestStore,
+ {
+ _processResults : function(results){
+ // unfortunately, S3 returns query results in XML form
+ var keyElements = results.getElementsByTagName("Key");
+ var jsResults = [];
+ var self = this;
+ for(var i=0; i <keyElements.length;i++){
+ var keyElement = keyElements[i];
+ // manually create lazy loaded Deferred items for each item in the result array
+ var val = {
+ _loadObject: (function(key,val){
+ return function(callback){
+ // when a callback is added we will fetch it
+ delete this._loadObject;
+ self.service(key).addCallback(callback);
+ };
+ })(keyElement.firstChild.nodeValue,val)
+ };
+ jsResults.push(val);
+ }
+
+ return {totalCount:jsResults.length, items: jsResults};
+ }
+ }
+);
+
+}
diff --git a/js/dojo-1.6/dojox/data/S3Store.xd.js b/js/dojo-1.6/dojox/data/S3Store.xd.js
new file mode 100644
index 0000000..f7d9971
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/S3Store.xd.js
@@ -0,0 +1,53 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.S3Store"],
+["require", "dojox.data.JsonRestStore"],
+["require", "dojox.rpc.ProxiedPath"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.S3Store"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.S3Store"] = true;
+dojo.provide("dojox.data.S3Store");
+dojo.require("dojox.data.JsonRestStore");
+dojo.require("dojox.rpc.ProxiedPath");
+
+
+
+// S3JsonRestStore is an extension of JsonRestStore to handle
+// Amazon's S3 service using JSON data
+
+dojo.declare("dojox.data.S3Store",
+ dojox.data.JsonRestStore,
+ {
+ _processResults : function(results){
+ // unfortunately, S3 returns query results in XML form
+ var keyElements = results.getElementsByTagName("Key");
+ var jsResults = [];
+ var self = this;
+ for(var i=0; i <keyElements.length;i++){
+ var keyElement = keyElements[i];
+ // manually create lazy loaded Deferred items for each item in the result array
+ var val = {
+ _loadObject: (function(key,val){
+ return function(callback){
+ // when a callback is added we will fetch it
+ delete this._loadObject;
+ self.service(key).addCallback(callback);
+ };
+ })(keyElement.firstChild.nodeValue,val)
+ };
+ jsResults.push(val);
+ }
+
+ return {totalCount:jsResults.length, items: jsResults};
+ }
+ }
+);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/ServiceStore.js b/js/dojo-1.6/dojox/data/ServiceStore.js
new file mode 100644
index 0000000..54ae0d8
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ServiceStore.js
@@ -0,0 +1,412 @@
+/*
+ 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["dojox.data.ServiceStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ServiceStore"] = true;
+dojo.provide("dojox.data.ServiceStore");
+
+
+
+// note that dojox.rpc.Service is not required, you can create your own services
+
+// A ServiceStore is a readonly data store that provides a data.data interface to an RPC service.
+// var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd"));
+// var serviceStore = new dojox.data.ServiceStore({service:myServices.ServiceStore});
+//
+// The ServiceStore also supports lazy loading. References can be made to objects that have not been loaded.
+// For example if a service returned:
+// {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}}
+//
+// And this object has accessed using the dojo.data API:
+// var obj = serviceStore.getValue(myObject,"lazyLoadedObject");
+// The object would automatically be requested from the server (with an object id of "obj2").
+//
+
+dojo.declare("dojox.data.ServiceStore",
+ // ClientFilter is intentionally not required, ServiceStore does not need it, and is more
+ // lightweight without it, but if it is provided, the ServiceStore will use it.
+ dojox.data.ClientFilter||null,{
+ service: null,
+ constructor: function(options){
+ //summary:
+ // ServiceStore constructor, instantiate a new ServiceStore
+ // A ServiceStore can be configured from a JSON Schema. Queries are just
+ // passed through to the underlying services
+ //
+ // options:
+ // Keyword arguments
+ // The *schema* parameter
+ // This is a schema object for this store. This should be JSON Schema format.
+ //
+ // The *service* parameter
+ // This is the service object that is used to retrieve lazy data and save results
+ // The function should be directly callable with a single parameter of an object id to be loaded
+ //
+ // The *idAttribute* parameter
+ // Defaults to 'id'. The name of the attribute that holds an objects id.
+ // This can be a preexisting id provided by the server.
+ // If an ID isn't already provided when an object
+ // is fetched or added to the store, the autoIdentity system
+ // will generate an id for it and add it to the index.
+ //
+ // The *estimateCountFactor* parameter
+ // This parameter is used by the ServiceStore to estimate the total count. When
+ // paging is indicated in a fetch and the response includes the full number of items
+ // requested by the fetch's count parameter, then the total count will be estimated
+ // to be estimateCountFactor multiplied by the provided count. If this is 1, then it is assumed that the server
+ // does not support paging, and the response is the full set of items, where the
+ // total count is equal to the numer of items returned. If the server does support
+ // paging, an estimateCountFactor of 2 is a good value for estimating the total count
+ // It is also possible to override _processResults if the server can provide an exact
+ // total count.
+ //
+ // The *syncMode* parameter
+ // Setting this to true will set the store to using synchronous calls by default.
+ // Sync calls return their data immediately from the calling function, so
+ // callbacks are unnecessary. This will only work with a synchronous capable service.
+ //
+ // description:
+ // ServiceStore can do client side caching and result set updating if
+ // dojox.data.ClientFilter is loaded. Do this add:
+ // | dojo.require("dojox.data.ClientFilter")
+ // prior to loading the ServiceStore (ClientFilter must be loaded before ServiceStore).
+ // To utilize client side filtering with a subclass, you can break queries into
+ // client side and server side components by putting client side actions in
+ // clientFilter property in fetch calls. For example you could override fetch:
+ // | fetch: function(args){
+ // | // do the sorting and paging on the client side
+ // | args.clientFilter = {start:args.start, count: args.count, sort: args.sort};
+ // | // args.query will be passed to the service object for the server side handling
+ // | return this.inherited(arguments);
+ // | }
+ // When extending this class, if you would like to create lazy objects, you can follow
+ // the example from dojox.data.tests.stores.ServiceStore:
+ // | var lazyItem = {
+ // | _loadObject: function(callback){
+ // | this.name="loaded";
+ // | delete this._loadObject;
+ // | callback(this);
+ // | }
+ // | };
+ //setup a byId alias to the api call
+ this.byId=this.fetchItemByIdentity;
+ this._index = {};
+ // if the advanced json parser is enabled, we can pass through object updates as onSet events
+ if(options){
+ dojo.mixin(this,options);
+ }
+ // We supply a default idAttribute for parser driven construction, but if no id attribute
+ // is supplied, it should be null so that auto identification takes place properly
+ this.idAttribute = (options && options.idAttribute) || (this.schema && this.schema._idAttr);
+ },
+ schema: null,
+ idAttribute: "id",
+ labelAttribute: "label",
+ syncMode: false,
+ estimateCountFactor: 1,
+ getSchema: function(){
+ return this.schema;
+ },
+
+ loadLazyValues:true,
+
+ getValue: function(/*Object*/ item, /*String*/property, /*value?*/defaultValue){
+ // summary:
+ // Gets the value of an item's 'property'
+ //
+ // item:
+ // The item to get the value from
+ // property:
+ // property to look up value for
+ // defaultValue:
+ // the default value
+
+ var value = item[property];
+ return value || // return the plain value since it was found;
+ (property in item ? // a truthy value was not found, see if we actually have it
+ value : // we do, so we can return it
+ item._loadObject ? // property was not found, maybe because the item is not loaded, we will try to load it synchronously so we can get the property
+ (dojox.rpc._sync = true) && arguments.callee.call(this,dojox.data.ServiceStore.prototype.loadItem({item:item}) || {}, property, defaultValue) : // load the item and run getValue again
+ defaultValue);// not in item -> return default value
+ },
+ getValues: function(item, property){
+ // summary:
+ // Gets the value of an item's 'property' and returns
+ // it. If this value is an array it is just returned,
+ // if not, the value is added to an array and that is returned.
+ //
+ // item: /* object */
+ // property: /* string */
+ // property to look up value for
+
+ var val = this.getValue(item,property);
+ if(val instanceof Array){
+ return val;
+ }
+ if(!this.isItemLoaded(val)){
+ dojox.rpc._sync = true;
+ val = this.loadItem({item:val});
+ }
+ return val instanceof Array ? val : val === undefined ? [] : [val];
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // Gets the available attributes of an item's 'property' and returns
+ // it as an array.
+ //
+ // item: /* object */
+
+ var res = [];
+ for(var i in item){
+ if(item.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){
+ res.push(i);
+ }
+ }
+ return res;
+ },
+
+ hasAttribute: function(item,attribute){
+ // summary:
+ // Checks to see if item has attribute
+ //
+ // item: /* object */
+ // attribute: /* string */
+ return attribute in item;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // Checks to see if 'item' has 'value' at 'attribute'
+ //
+ // item: /* object */
+ // attribute: /* string */
+ // value: /* anything */
+ return dojo.indexOf(this.getValues(item,attribute),value) > -1;
+ },
+
+
+ isItem: function(item){
+ // summary:
+ // Checks to see if the argument is an item
+ //
+ // item: /* object */
+ // attribute: /* string */
+
+ // we have no way of determining if it belongs, we just have object returned from
+ // service queries
+ return (typeof item == 'object') && item && !(item instanceof Date);
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // Checks to see if the item is loaded.
+ //
+ // item: /* object */
+
+ return item && !item._loadObject;
+ },
+
+ loadItem: function(args){
+ // summary:
+ // Loads an item and calls the callback handler. Note, that this will call the callback
+ // handler even if the item is loaded. Consequently, you can use loadItem to ensure
+ // that an item is loaded is situations when the item may or may not be loaded yet.
+ // If you access a value directly through property access, you can use this to load
+ // a lazy value as well (doesn't need to be an item).
+ //
+ // example:
+ // store.loadItem({
+ // item: item, // this item may or may not be loaded
+ // onItem: function(item){
+ // // do something with the item
+ // }
+ // });
+
+ var item;
+ if(args.item._loadObject){
+ args.item._loadObject(function(result){
+ item = result; // in synchronous mode this can allow loadItem to return the value
+ delete item._loadObject;
+ var func = result instanceof Error ? args.onError : args.onItem;
+ if(func){
+ func.call(args.scope, result);
+ }
+ });
+ }else if(args.onItem){
+ // even if it is already loaded, we will use call the callback, this makes it easier to
+ // use when it is not known if the item is loaded (you can always safely call loadItem).
+ args.onItem.call(args.scope, args.item);
+ }
+ return item;
+ },
+ _currentId : 0,
+ _processResults : function(results, deferred){
+ // this should return an object with the items as an array and the total count of
+ // items (maybe more than currently in the result set).
+ // for example:
+ // | {totalCount:10, items: [{id:1},{id:2}]}
+
+ // index the results, assigning ids as necessary
+
+ if(results && typeof results == 'object'){
+ var id = results.__id;
+ if(!id){// if it hasn't been assigned yet
+ if(this.idAttribute){
+ // use the defined id if available
+ id = results[this.idAttribute];
+ }else{
+ id = this._currentId++;
+ }
+ if(id !== undefined){
+ var existingObj = this._index[id];
+ if(existingObj){
+ for(var j in existingObj){
+ delete existingObj[j]; // clear it so we can mixin
+ }
+ results = dojo.mixin(existingObj,results);
+ }
+ results.__id = id;
+ this._index[id] = results;
+ }
+ }
+ for(var i in results){
+ results[i] = this._processResults(results[i], deferred).items;
+ }
+ var count = results.length;
+ }
+ return {totalCount: deferred.request.count == count ? (deferred.request.start || 0) + count * this.estimateCountFactor : count, items: results};
+ },
+ close: function(request){
+ return request && request.abort && request.abort();
+ },
+ fetch: function(args){
+ // summary:
+ // See dojo.data.api.Read.fetch
+ //
+ // The *queryOptions.cache* parameter
+ // If true, indicates that the query result should be cached for future use. This is only available
+ // if dojox.data.ClientFilter has been loaded before the ServiceStore
+ //
+ // The *syncMode* parameter
+ // Indicates that the call should be fetch synchronously if possible (this is not always possible)
+ //
+ // The *clientFetch* parameter
+ // This is a fetch keyword argument for explicitly doing client side filtering, querying, and paging
+
+ args = args || {};
+
+ if("syncMode" in args ? args.syncMode : this.syncMode){
+ dojox.rpc._sync = true;
+ }
+ var self = this;
+
+ var scope = args.scope || self;
+ var defResult = this.cachingFetch ? this.cachingFetch(args) : this._doQuery(args);
+ defResult.request = args;
+ defResult.addCallback(function(results){
+ if(args.clientFetch){
+ results = self.clientSideFetch({query:args.clientFetch,sort:args.sort,start:args.start,count:args.count},results);
+ }
+ var resultSet = self._processResults(results, defResult);
+ results = args.results = resultSet.items;
+ if(args.onBegin){
+ args.onBegin.call(scope, resultSet.totalCount, args);
+ }
+ if(args.onItem){
+ for(var i=0; i<results.length;i++){
+ args.onItem.call(scope, results[i], args);
+ }
+ }
+ if(args.onComplete){
+ args.onComplete.call(scope, args.onItem ? null : results, args);
+ }
+ return results;
+ });
+ defResult.addErrback(args.onError && function(err){
+ return args.onError.call(scope, err, args);
+ });
+ args.abort = function(){
+ // abort the request
+ defResult.cancel();
+ };
+ args.store = this;
+ return args;
+ },
+ _doQuery: function(args){
+ var query= typeof args.queryStr == 'string' ? args.queryStr : args.query;
+ return this.service(query);
+ },
+ getFeatures: function(){
+ // summary:
+ // return the store feature set
+
+ return {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Identity": true,
+ "dojo.data.api.Schema": this.schema
+ };
+ },
+
+ getLabel: function(item){
+ // summary
+ // returns the label for an item. Just gets the "label" attribute.
+ //
+ return this.getValue(item,this.labelAttribute);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // returns an array of attributes that are used to create the label of an item
+ return [this.labelAttribute];
+ },
+
+ //Identity API Support
+
+
+ getIdentity: function(item){
+ return item.__id;
+ },
+
+ getIdentityAttributes: function(item){
+ // summary:
+ // returns the attributes which are used to make up the
+ // identity of an item. Basically returns this.idAttribute
+
+ return [this.idAttribute];
+ },
+
+ fetchItemByIdentity: function(args){
+ // summary:
+ // fetch an item by its identity, by looking in our index of what we have loaded
+ var item = this._index[(args._prefix || '') + args.identity];
+ if(item){
+ // the item exists in the index
+ if(item._loadObject){
+ // we have a handle on the item, but it isn't loaded yet, so we need to load it
+ args.item = item;
+ return this.loadItem(args);
+ }else if(args.onItem){
+ // it's already loaded, so we can immediately callback
+ args.onItem.call(args.scope, item);
+ }
+ }else{
+ // convert the different spellings
+ return this.fetch({
+ query: args.identity,
+ onComplete: args.onItem,
+ onError: args.onError,
+ scope: args.scope
+ }).results;
+ }
+ return item;
+ }
+
+ }
+);
+
+}
diff --git a/js/dojo-1.6/dojox/data/ServiceStore.xd.js b/js/dojo-1.6/dojox/data/ServiceStore.xd.js
new file mode 100644
index 0000000..a8118d0
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/ServiceStore.xd.js
@@ -0,0 +1,416 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.ServiceStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.ServiceStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.ServiceStore"] = true;
+dojo.provide("dojox.data.ServiceStore");
+
+
+
+// note that dojox.rpc.Service is not required, you can create your own services
+
+// A ServiceStore is a readonly data store that provides a data.data interface to an RPC service.
+// var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd"));
+// var serviceStore = new dojox.data.ServiceStore({service:myServices.ServiceStore});
+//
+// The ServiceStore also supports lazy loading. References can be made to objects that have not been loaded.
+// For example if a service returned:
+// {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}}
+//
+// And this object has accessed using the dojo.data API:
+// var obj = serviceStore.getValue(myObject,"lazyLoadedObject");
+// The object would automatically be requested from the server (with an object id of "obj2").
+//
+
+dojo.declare("dojox.data.ServiceStore",
+ // ClientFilter is intentionally not required, ServiceStore does not need it, and is more
+ // lightweight without it, but if it is provided, the ServiceStore will use it.
+ dojox.data.ClientFilter||null,{
+ service: null,
+ constructor: function(options){
+ //summary:
+ // ServiceStore constructor, instantiate a new ServiceStore
+ // A ServiceStore can be configured from a JSON Schema. Queries are just
+ // passed through to the underlying services
+ //
+ // options:
+ // Keyword arguments
+ // The *schema* parameter
+ // This is a schema object for this store. This should be JSON Schema format.
+ //
+ // The *service* parameter
+ // This is the service object that is used to retrieve lazy data and save results
+ // The function should be directly callable with a single parameter of an object id to be loaded
+ //
+ // The *idAttribute* parameter
+ // Defaults to 'id'. The name of the attribute that holds an objects id.
+ // This can be a preexisting id provided by the server.
+ // If an ID isn't already provided when an object
+ // is fetched or added to the store, the autoIdentity system
+ // will generate an id for it and add it to the index.
+ //
+ // The *estimateCountFactor* parameter
+ // This parameter is used by the ServiceStore to estimate the total count. When
+ // paging is indicated in a fetch and the response includes the full number of items
+ // requested by the fetch's count parameter, then the total count will be estimated
+ // to be estimateCountFactor multiplied by the provided count. If this is 1, then it is assumed that the server
+ // does not support paging, and the response is the full set of items, where the
+ // total count is equal to the numer of items returned. If the server does support
+ // paging, an estimateCountFactor of 2 is a good value for estimating the total count
+ // It is also possible to override _processResults if the server can provide an exact
+ // total count.
+ //
+ // The *syncMode* parameter
+ // Setting this to true will set the store to using synchronous calls by default.
+ // Sync calls return their data immediately from the calling function, so
+ // callbacks are unnecessary. This will only work with a synchronous capable service.
+ //
+ // description:
+ // ServiceStore can do client side caching and result set updating if
+ // dojox.data.ClientFilter is loaded. Do this add:
+ // | dojo.require("dojox.data.ClientFilter")
+ // prior to loading the ServiceStore (ClientFilter must be loaded before ServiceStore).
+ // To utilize client side filtering with a subclass, you can break queries into
+ // client side and server side components by putting client side actions in
+ // clientFilter property in fetch calls. For example you could override fetch:
+ // | fetch: function(args){
+ // | // do the sorting and paging on the client side
+ // | args.clientFilter = {start:args.start, count: args.count, sort: args.sort};
+ // | // args.query will be passed to the service object for the server side handling
+ // | return this.inherited(arguments);
+ // | }
+ // When extending this class, if you would like to create lazy objects, you can follow
+ // the example from dojox.data.tests.stores.ServiceStore:
+ // | var lazyItem = {
+ // | _loadObject: function(callback){
+ // | this.name="loaded";
+ // | delete this._loadObject;
+ // | callback(this);
+ // | }
+ // | };
+ //setup a byId alias to the api call
+ this.byId=this.fetchItemByIdentity;
+ this._index = {};
+ // if the advanced json parser is enabled, we can pass through object updates as onSet events
+ if(options){
+ dojo.mixin(this,options);
+ }
+ // We supply a default idAttribute for parser driven construction, but if no id attribute
+ // is supplied, it should be null so that auto identification takes place properly
+ this.idAttribute = (options && options.idAttribute) || (this.schema && this.schema._idAttr);
+ },
+ schema: null,
+ idAttribute: "id",
+ labelAttribute: "label",
+ syncMode: false,
+ estimateCountFactor: 1,
+ getSchema: function(){
+ return this.schema;
+ },
+
+ loadLazyValues:true,
+
+ getValue: function(/*Object*/ item, /*String*/property, /*value?*/defaultValue){
+ // summary:
+ // Gets the value of an item's 'property'
+ //
+ // item:
+ // The item to get the value from
+ // property:
+ // property to look up value for
+ // defaultValue:
+ // the default value
+
+ var value = item[property];
+ return value || // return the plain value since it was found;
+ (property in item ? // a truthy value was not found, see if we actually have it
+ value : // we do, so we can return it
+ item._loadObject ? // property was not found, maybe because the item is not loaded, we will try to load it synchronously so we can get the property
+ (dojox.rpc._sync = true) && arguments.callee.call(this,dojox.data.ServiceStore.prototype.loadItem({item:item}) || {}, property, defaultValue) : // load the item and run getValue again
+ defaultValue);// not in item -> return default value
+ },
+ getValues: function(item, property){
+ // summary:
+ // Gets the value of an item's 'property' and returns
+ // it. If this value is an array it is just returned,
+ // if not, the value is added to an array and that is returned.
+ //
+ // item: /* object */
+ // property: /* string */
+ // property to look up value for
+
+ var val = this.getValue(item,property);
+ if(val instanceof Array){
+ return val;
+ }
+ if(!this.isItemLoaded(val)){
+ dojox.rpc._sync = true;
+ val = this.loadItem({item:val});
+ }
+ return val instanceof Array ? val : val === undefined ? [] : [val];
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // Gets the available attributes of an item's 'property' and returns
+ // it as an array.
+ //
+ // item: /* object */
+
+ var res = [];
+ for(var i in item){
+ if(item.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){
+ res.push(i);
+ }
+ }
+ return res;
+ },
+
+ hasAttribute: function(item,attribute){
+ // summary:
+ // Checks to see if item has attribute
+ //
+ // item: /* object */
+ // attribute: /* string */
+ return attribute in item;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // Checks to see if 'item' has 'value' at 'attribute'
+ //
+ // item: /* object */
+ // attribute: /* string */
+ // value: /* anything */
+ return dojo.indexOf(this.getValues(item,attribute),value) > -1;
+ },
+
+
+ isItem: function(item){
+ // summary:
+ // Checks to see if the argument is an item
+ //
+ // item: /* object */
+ // attribute: /* string */
+
+ // we have no way of determining if it belongs, we just have object returned from
+ // service queries
+ return (typeof item == 'object') && item && !(item instanceof Date);
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // Checks to see if the item is loaded.
+ //
+ // item: /* object */
+
+ return item && !item._loadObject;
+ },
+
+ loadItem: function(args){
+ // summary:
+ // Loads an item and calls the callback handler. Note, that this will call the callback
+ // handler even if the item is loaded. Consequently, you can use loadItem to ensure
+ // that an item is loaded is situations when the item may or may not be loaded yet.
+ // If you access a value directly through property access, you can use this to load
+ // a lazy value as well (doesn't need to be an item).
+ //
+ // example:
+ // store.loadItem({
+ // item: item, // this item may or may not be loaded
+ // onItem: function(item){
+ // // do something with the item
+ // }
+ // });
+
+ var item;
+ if(args.item._loadObject){
+ args.item._loadObject(function(result){
+ item = result; // in synchronous mode this can allow loadItem to return the value
+ delete item._loadObject;
+ var func = result instanceof Error ? args.onError : args.onItem;
+ if(func){
+ func.call(args.scope, result);
+ }
+ });
+ }else if(args.onItem){
+ // even if it is already loaded, we will use call the callback, this makes it easier to
+ // use when it is not known if the item is loaded (you can always safely call loadItem).
+ args.onItem.call(args.scope, args.item);
+ }
+ return item;
+ },
+ _currentId : 0,
+ _processResults : function(results, deferred){
+ // this should return an object with the items as an array and the total count of
+ // items (maybe more than currently in the result set).
+ // for example:
+ // | {totalCount:10, items: [{id:1},{id:2}]}
+
+ // index the results, assigning ids as necessary
+
+ if(results && typeof results == 'object'){
+ var id = results.__id;
+ if(!id){// if it hasn't been assigned yet
+ if(this.idAttribute){
+ // use the defined id if available
+ id = results[this.idAttribute];
+ }else{
+ id = this._currentId++;
+ }
+ if(id !== undefined){
+ var existingObj = this._index[id];
+ if(existingObj){
+ for(var j in existingObj){
+ delete existingObj[j]; // clear it so we can mixin
+ }
+ results = dojo.mixin(existingObj,results);
+ }
+ results.__id = id;
+ this._index[id] = results;
+ }
+ }
+ for(var i in results){
+ results[i] = this._processResults(results[i], deferred).items;
+ }
+ var count = results.length;
+ }
+ return {totalCount: deferred.request.count == count ? (deferred.request.start || 0) + count * this.estimateCountFactor : count, items: results};
+ },
+ close: function(request){
+ return request && request.abort && request.abort();
+ },
+ fetch: function(args){
+ // summary:
+ // See dojo.data.api.Read.fetch
+ //
+ // The *queryOptions.cache* parameter
+ // If true, indicates that the query result should be cached for future use. This is only available
+ // if dojox.data.ClientFilter has been loaded before the ServiceStore
+ //
+ // The *syncMode* parameter
+ // Indicates that the call should be fetch synchronously if possible (this is not always possible)
+ //
+ // The *clientFetch* parameter
+ // This is a fetch keyword argument for explicitly doing client side filtering, querying, and paging
+
+ args = args || {};
+
+ if("syncMode" in args ? args.syncMode : this.syncMode){
+ dojox.rpc._sync = true;
+ }
+ var self = this;
+
+ var scope = args.scope || self;
+ var defResult = this.cachingFetch ? this.cachingFetch(args) : this._doQuery(args);
+ defResult.request = args;
+ defResult.addCallback(function(results){
+ if(args.clientFetch){
+ results = self.clientSideFetch({query:args.clientFetch,sort:args.sort,start:args.start,count:args.count},results);
+ }
+ var resultSet = self._processResults(results, defResult);
+ results = args.results = resultSet.items;
+ if(args.onBegin){
+ args.onBegin.call(scope, resultSet.totalCount, args);
+ }
+ if(args.onItem){
+ for(var i=0; i<results.length;i++){
+ args.onItem.call(scope, results[i], args);
+ }
+ }
+ if(args.onComplete){
+ args.onComplete.call(scope, args.onItem ? null : results, args);
+ }
+ return results;
+ });
+ defResult.addErrback(args.onError && function(err){
+ return args.onError.call(scope, err, args);
+ });
+ args.abort = function(){
+ // abort the request
+ defResult.cancel();
+ };
+ args.store = this;
+ return args;
+ },
+ _doQuery: function(args){
+ var query= typeof args.queryStr == 'string' ? args.queryStr : args.query;
+ return this.service(query);
+ },
+ getFeatures: function(){
+ // summary:
+ // return the store feature set
+
+ return {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Identity": true,
+ "dojo.data.api.Schema": this.schema
+ };
+ },
+
+ getLabel: function(item){
+ // summary
+ // returns the label for an item. Just gets the "label" attribute.
+ //
+ return this.getValue(item,this.labelAttribute);
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // returns an array of attributes that are used to create the label of an item
+ return [this.labelAttribute];
+ },
+
+ //Identity API Support
+
+
+ getIdentity: function(item){
+ return item.__id;
+ },
+
+ getIdentityAttributes: function(item){
+ // summary:
+ // returns the attributes which are used to make up the
+ // identity of an item. Basically returns this.idAttribute
+
+ return [this.idAttribute];
+ },
+
+ fetchItemByIdentity: function(args){
+ // summary:
+ // fetch an item by its identity, by looking in our index of what we have loaded
+ var item = this._index[(args._prefix || '') + args.identity];
+ if(item){
+ // the item exists in the index
+ if(item._loadObject){
+ // we have a handle on the item, but it isn't loaded yet, so we need to load it
+ args.item = item;
+ return this.loadItem(args);
+ }else if(args.onItem){
+ // it's already loaded, so we can immediately callback
+ args.onItem.call(args.scope, item);
+ }
+ }else{
+ // convert the different spellings
+ return this.fetch({
+ query: args.identity,
+ onComplete: args.onItem,
+ onError: args.onError,
+ scope: args.scope
+ }).results;
+ }
+ return item;
+ }
+
+ }
+);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/SnapLogicStore.js b/js/dojo-1.6/dojox/data/SnapLogicStore.js
new file mode 100644
index 0000000..e16b407
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/SnapLogicStore.js
@@ -0,0 +1,339 @@
+/*
+ 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["dojox.data.SnapLogicStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.SnapLogicStore"] = true;
+dojo.provide("dojox.data.SnapLogicStore");
+dojo.require("dojo.io.script");
+dojo.require("dojo.data.util.sorter");
+
+
+
+dojo.declare("dojox.data.SnapLogicStore", null, {
+ Parts: {
+ DATA: "data",
+ COUNT: "count"
+ },
+
+ url: "",
+
+ constructor: function(/* Object */args){
+ // summary:
+ // Initialize a SnapLogicStore object.
+ // args:
+ // An object that contains properties for initializing the new data store object. The
+ // following properties are understood:
+ // url:
+ // A URL to the SnapLogic pipeline's output routed through PipeToHttp. Typically, this
+ // will look like "http://<server-host>:<port>/pipe/<pipeline-url>/<pipeline-output-view>".
+ // parameters:
+ // An object whose properties define parameters to the pipeline. The values of these
+ // properties will be sent to the pipeline as parameters when it run.
+ //
+ if(args.url){
+ this.url = args.url;
+ }
+ this._parameters = args.parameters;
+ },
+
+ _assertIsItem: function(/* item */item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.SnapLogicStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.SnapLogicStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var i = dojo.indexOf(item.attributes, attribute);
+ if(i !== -1){
+ return item.values[i];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ return item.attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ for(var i = 0; i < item.attributes.length; ++i){
+ if(attribute == item.attributes[i]){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item); // Boolean
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return undefined;
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ return this.getValue(item, attribute) === value; // Boolean
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var i = dojo.indexOf(item.attributes, attribute);
+ if(i !== -1){
+ return [item.values[i]]; // Array
+ }
+ return [];
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item._store === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchHandler: function(/* Object */request){
+ // summary:
+ // Process data retrieved via fetch and send it back to requester.
+ // response:
+ // The data returend from the I/O transport. In the normal case, it will be an array of result rows
+ // from the pipeline. In the special case for record count optimization, response will be an array
+ // with a single element containing the total pipeline result row count. See fetch() for details
+ // on this optimization.
+
+ var scope = request.scope || dojo.global;
+
+ if(request.onBegin){
+ // Check for the record count optimization
+ request.onBegin.call(scope, request._countResponse[0], request);
+ }
+
+ if(request.onItem || request.onComplete){
+ var response = request._dataResponse;
+
+ if(!response.length){
+ request.onError.call(scope,
+ new Error("dojox.data.SnapLogicStore: invalid response of length 0"),
+ request);
+ return;
+ }else if(request.query != 'record count'){
+ //If this was not a record count request, the first element returned will contain
+ //the field names.
+ var field_names = response.shift();
+
+ var items = [];
+ for(var i = 0; i < response.length; ++i){
+ if(request._aborted){
+ break;
+ }
+
+ items.push({attributes: field_names, values: response[i], _store: this});
+ }
+
+ if(request.sort && !request._aborted){
+ items.sort(dojo.data.util.sorter.createSortFunction(request.sort, self));
+ }
+ }else{
+ //This is a record count request, so manually set the field names.
+ items = [({attributes: ['count'], values: response, _store: this})];
+ }
+
+ if(request.onItem){
+ for(var i = 0; i < items.length; ++i){
+ if(request._aborted){
+ break;
+ }
+ request.onItem.call(scope, items[i], request);
+ }
+ items = null;
+ }
+
+ if(request.onComplete && !request._aborted){
+ request.onComplete.call(scope, items, request);
+ }
+ }
+ },
+
+ _partHandler: function(/* Object */request, /* String */part, /* Object */response){
+ // summary:
+ // Handle the individual replies for both data and length requests.
+ // request:
+ // The request/handle object used with the original fetch() call.
+ // part:
+ // A value indicating which request this handler call is for (this.Parts).
+ // response:
+ // Response received from the underlying IO transport.
+
+ if(response instanceof Error){
+ if(part == this.Parts.DATA){
+ request._dataHandle = null;
+ }else{
+ request._countHandle = null;
+ }
+ request._aborted = true;
+ if(request.onError){
+ request.onError.call(request.scope, response, request);
+ }
+ }else{
+ if(request._aborted){
+ return;
+ }
+ if(part == this.Parts.DATA){
+ request._dataResponse = response;
+ }else{
+ request._countResponse = response;
+ }
+ if((!request._dataHandle || request._dataResponse !== null) &&
+ (!request._countHandle || request._countResponse !== null)){
+ this._fetchHandler(request);
+ }
+ }
+ },
+
+ fetch: function(/* Object */request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // request:
+ // See dojo.data.api.Read.close() for generic interface.
+ //
+ // In addition to the standard Read API fetch support, this store supports an optimization for
+ // for retrieving the total count of records in the Pipeline without retrieving the data. To
+ // use this optimization, simply provide an onBegin handler without an onItem or onComplete handler.
+
+ request._countResponse = null;
+ request._dataResponse = null;
+ request._aborted = false;
+ request.abort = function(){
+ if(!request._aborted){
+ request._aborted = true;
+ if(request._dataHandle && request._dataHandle.cancel){
+ request._dataHandle.cancel();
+ }
+ if(request._countHandle && request._countHandle.cancel){
+ request._countHandle.cancel();
+ }
+ }
+ };
+
+ // Only make the call for data if onItem or onComplete is used. Otherwise, onBegin will only
+ // require the total row count.
+ if(request.onItem || request.onComplete){
+ var content = this._parameters || {};
+ if(request.start){
+ if(request.start < 0){
+ throw new Error("dojox.data.SnapLogicStore: request start value must be 0 or greater");
+ }
+ content['sn.start'] = request.start + 1;
+ }
+ if(request.count){
+ if(request.count < 0){
+ throw new Error("dojox.data.SnapLogicStore: request count value 0 or greater");
+ }
+ content['sn.limit'] = request.count;
+ }
+
+ content['sn.content_type'] = 'application/javascript';
+
+ var store = this;
+ var handler = function(response, ioArgs){
+ if(response instanceof Error){
+ store._fetchHandler(response, request);
+ }
+ };
+
+ var getArgs = {
+ url: this.url,
+ content: content,
+ // preventCache: true,
+ timeout: 60000, //Starting a pipeline can take a long time.
+ callbackParamName: "sn.stream_header",
+ handle: dojo.hitch(this, "_partHandler", request, this.Parts.DATA)
+ };
+
+ request._dataHandle = dojo.io.script.get(getArgs);
+ }
+
+ if(request.onBegin){
+ var content = {};
+ content['sn.count'] = 'records';
+ content['sn.content_type'] = 'application/javascript';
+
+ var getArgs = {
+ url: this.url,
+ content: content,
+ timeout: 60000,
+ callbackParamName: "sn.stream_header",
+ handle: dojo.hitch(this, "_partHandler", request, this.Parts.COUNT)
+ };
+
+ request._countHandle = dojo.io.script.get(getArgs);
+ }
+
+ return request; // Object
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/SnapLogicStore.xd.js b/js/dojo-1.6/dojox/data/SnapLogicStore.xd.js
new file mode 100644
index 0000000..a836028
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/SnapLogicStore.xd.js
@@ -0,0 +1,345 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.SnapLogicStore"],
+["require", "dojo.io.script"],
+["require", "dojo.data.util.sorter"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.SnapLogicStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.SnapLogicStore"] = true;
+dojo.provide("dojox.data.SnapLogicStore");
+dojo.require("dojo.io.script");
+dojo.require("dojo.data.util.sorter");
+
+
+
+dojo.declare("dojox.data.SnapLogicStore", null, {
+ Parts: {
+ DATA: "data",
+ COUNT: "count"
+ },
+
+ url: "",
+
+ constructor: function(/* Object */args){
+ // summary:
+ // Initialize a SnapLogicStore object.
+ // args:
+ // An object that contains properties for initializing the new data store object. The
+ // following properties are understood:
+ // url:
+ // A URL to the SnapLogic pipeline's output routed through PipeToHttp. Typically, this
+ // will look like "http://<server-host>:<port>/pipe/<pipeline-url>/<pipeline-output-view>".
+ // parameters:
+ // An object whose properties define parameters to the pipeline. The values of these
+ // properties will be sent to the pipeline as parameters when it run.
+ //
+ if(args.url){
+ this.url = args.url;
+ }
+ this._parameters = args.parameters;
+ },
+
+ _assertIsItem: function(/* item */item){
+ // summary:
+ // This function tests whether the item passed in is indeed an item in the store.
+ // item:
+ // The item to test for being contained by the store.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.SnapLogicStore: a function was passed an item argument that was not an item");
+ }
+ },
+
+ _assertIsAttribute: function(/* attribute-name-string */ attribute){
+ // summary:
+ // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
+ // attribute:
+ // The attribute to test for being contained by the store.
+ if(typeof attribute !== "string"){
+ throw new Error("dojox.data.SnapLogicStore: a function was passed an attribute argument that was not an attribute name string");
+ }
+ },
+
+ getFeatures: function(){
+ // summary:
+ // See dojo.data.api.Read.getFeatures()
+ return {
+ 'dojo.data.api.Read': true
+ };
+ },
+
+ getValue: function(item, attribute, defaultValue){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var i = dojo.indexOf(item.attributes, attribute);
+ if(i !== -1){
+ return item.values[i];
+ }
+ return defaultValue;
+ },
+
+ getAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getAttributes()
+ this._assertIsItem(item);
+ return item.attributes;
+ },
+
+ hasAttribute: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.hasAttributes()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ for(var i = 0; i < item.attributes.length; ++i){
+ if(attribute == item.attributes[i]){
+ return true;
+ }
+ }
+ return false;
+ },
+
+ isItemLoaded: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItemLoaded()
+ return this.isItem(item); // Boolean
+ },
+
+ loadItem: function(keywordArgs){
+ // summary:
+ // See dojo.data.api.Read.loadItem()
+ },
+
+ getLabel: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ return undefined;
+ },
+
+ getLabelAttributes: function(item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ return null;
+ },
+
+ containsValue: function(item, attribute, value){
+ // summary:
+ // See dojo.data.api.Read.containsValue()
+ return this.getValue(item, attribute) === value; // Boolean
+ },
+
+ getValues: function(item, attribute){
+ // summary:
+ // See dojo.data.api.Read.getValue()
+ this._assertIsItem(item);
+ this._assertIsAttribute(attribute);
+ var i = dojo.indexOf(item.attributes, attribute);
+ if(i !== -1){
+ return [item.values[i]]; // Array
+ }
+ return [];
+ },
+
+ isItem: function(item){
+ // summary:
+ // See dojo.data.api.Read.isItem()
+ if(item && item._store === this){
+ return true;
+ }
+ return false;
+ },
+
+ close: function(request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+ _fetchHandler: function(/* Object */request){
+ // summary:
+ // Process data retrieved via fetch and send it back to requester.
+ // response:
+ // The data returend from the I/O transport. In the normal case, it will be an array of result rows
+ // from the pipeline. In the special case for record count optimization, response will be an array
+ // with a single element containing the total pipeline result row count. See fetch() for details
+ // on this optimization.
+
+ var scope = request.scope || dojo.global;
+
+ if(request.onBegin){
+ // Check for the record count optimization
+ request.onBegin.call(scope, request._countResponse[0], request);
+ }
+
+ if(request.onItem || request.onComplete){
+ var response = request._dataResponse;
+
+ if(!response.length){
+ request.onError.call(scope,
+ new Error("dojox.data.SnapLogicStore: invalid response of length 0"),
+ request);
+ return;
+ }else if(request.query != 'record count'){
+ //If this was not a record count request, the first element returned will contain
+ //the field names.
+ var field_names = response.shift();
+
+ var items = [];
+ for(var i = 0; i < response.length; ++i){
+ if(request._aborted){
+ break;
+ }
+
+ items.push({attributes: field_names, values: response[i], _store: this});
+ }
+
+ if(request.sort && !request._aborted){
+ items.sort(dojo.data.util.sorter.createSortFunction(request.sort, self));
+ }
+ }else{
+ //This is a record count request, so manually set the field names.
+ items = [({attributes: ['count'], values: response, _store: this})];
+ }
+
+ if(request.onItem){
+ for(var i = 0; i < items.length; ++i){
+ if(request._aborted){
+ break;
+ }
+ request.onItem.call(scope, items[i], request);
+ }
+ items = null;
+ }
+
+ if(request.onComplete && !request._aborted){
+ request.onComplete.call(scope, items, request);
+ }
+ }
+ },
+
+ _partHandler: function(/* Object */request, /* String */part, /* Object */response){
+ // summary:
+ // Handle the individual replies for both data and length requests.
+ // request:
+ // The request/handle object used with the original fetch() call.
+ // part:
+ // A value indicating which request this handler call is for (this.Parts).
+ // response:
+ // Response received from the underlying IO transport.
+
+ if(response instanceof Error){
+ if(part == this.Parts.DATA){
+ request._dataHandle = null;
+ }else{
+ request._countHandle = null;
+ }
+ request._aborted = true;
+ if(request.onError){
+ request.onError.call(request.scope, response, request);
+ }
+ }else{
+ if(request._aborted){
+ return;
+ }
+ if(part == this.Parts.DATA){
+ request._dataResponse = response;
+ }else{
+ request._countResponse = response;
+ }
+ if((!request._dataHandle || request._dataResponse !== null) &&
+ (!request._countHandle || request._countResponse !== null)){
+ this._fetchHandler(request);
+ }
+ }
+ },
+
+ fetch: function(/* Object */request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ // request:
+ // See dojo.data.api.Read.close() for generic interface.
+ //
+ // In addition to the standard Read API fetch support, this store supports an optimization for
+ // for retrieving the total count of records in the Pipeline without retrieving the data. To
+ // use this optimization, simply provide an onBegin handler without an onItem or onComplete handler.
+
+ request._countResponse = null;
+ request._dataResponse = null;
+ request._aborted = false;
+ request.abort = function(){
+ if(!request._aborted){
+ request._aborted = true;
+ if(request._dataHandle && request._dataHandle.cancel){
+ request._dataHandle.cancel();
+ }
+ if(request._countHandle && request._countHandle.cancel){
+ request._countHandle.cancel();
+ }
+ }
+ };
+
+ // Only make the call for data if onItem or onComplete is used. Otherwise, onBegin will only
+ // require the total row count.
+ if(request.onItem || request.onComplete){
+ var content = this._parameters || {};
+ if(request.start){
+ if(request.start < 0){
+ throw new Error("dojox.data.SnapLogicStore: request start value must be 0 or greater");
+ }
+ content['sn.start'] = request.start + 1;
+ }
+ if(request.count){
+ if(request.count < 0){
+ throw new Error("dojox.data.SnapLogicStore: request count value 0 or greater");
+ }
+ content['sn.limit'] = request.count;
+ }
+
+ content['sn.content_type'] = 'application/javascript';
+
+ var store = this;
+ var handler = function(response, ioArgs){
+ if(response instanceof Error){
+ store._fetchHandler(response, request);
+ }
+ };
+
+ var getArgs = {
+ url: this.url,
+ content: content,
+ // preventCache: true,
+ timeout: 60000, //Starting a pipeline can take a long time.
+ callbackParamName: "sn.stream_header",
+ handle: dojo.hitch(this, "_partHandler", request, this.Parts.DATA)
+ };
+
+ request._dataHandle = dojo.io.script.get(getArgs);
+ }
+
+ if(request.onBegin){
+ var content = {};
+ content['sn.count'] = 'records';
+ content['sn.content_type'] = 'application/javascript';
+
+ var getArgs = {
+ url: this.url,
+ content: content,
+ timeout: 60000,
+ callbackParamName: "sn.stream_header",
+ handle: dojo.hitch(this, "_partHandler", request, this.Parts.COUNT)
+ };
+
+ request._countHandle = dojo.io.script.get(getArgs);
+ }
+
+ return request; // Object
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/StoreExplorer.js b/js/dojo-1.6/dojox/data/StoreExplorer.js
new file mode 100644
index 0000000..11726b6
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/StoreExplorer.js
@@ -0,0 +1,202 @@
+/*
+ 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["dojox.data.StoreExplorer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.StoreExplorer"] = true;
+dojo.provide("dojox.data.StoreExplorer");
+dojo.require("dojox.grid.DataGrid");
+dojo.require("dojox.data.ItemExplorer");
+dojo.require("dijit.layout.BorderContainer");
+dojo.require("dijit.layout.ContentPane");
+
+dojo.declare("dojox.data.StoreExplorer", dijit.layout.BorderContainer, {
+ constructor: function(options){
+ dojo.mixin(this, options);
+ },
+ store: null,
+ columnWidth: '',
+ stringQueries: false,
+ showAllColumns: false,
+ postCreate: function(){
+ var self = this;
+ this.inherited(arguments);
+ var contentPane = new dijit.layout.ContentPane({
+ region:'top'
+ }).placeAt(this);
+ function addButton(name, action){
+ var button = new dijit.form.Button({label: name});
+ contentPane.containerNode.appendChild(button.domNode);
+ button.onClick = action;
+ return button;
+ }
+ var queryText = contentPane.containerNode.appendChild(document.createElement("span"));
+ queryText.innerHTML = "Enter query: &nbsp;";
+ queryText.id = "queryText";
+ var queryTextBox = contentPane.containerNode.appendChild(document.createElement("input"));
+ queryTextBox.type = "text";
+ queryTextBox.id = "queryTextBox";
+ addButton("Query",function(){
+ var query = queryTextBox.value;
+ self.setQuery(self.stringQueries ? query : dojo.fromJson(query));
+ });
+ contentPane.containerNode.appendChild(document.createElement("span")).innerHTML = "&nbsp;&nbsp;&nbsp;";
+ var createNewButton = addButton("Create New", dojo.hitch(this, "createNew"));
+ var deleteButton = addButton("Delete",function(){
+ var items = grid.selection.getSelected();
+ for(var i = 0; i < items.length; i++){
+ self.store.deleteItem(items[i]);
+ }
+ });
+ this.setItemName = function(name){
+ createNewButton.attr('label',"<img style='width:12px; height:12px' src='" + dojo.moduleUrl("dijit.themes.tundra.images","dndCopy.png") + "' /> Create New " + name);
+ deleteButton.attr('label',"Delete " + name);
+ };
+ addButton("Save",function(){
+ self.store.save({onError:function(error){
+ alert(error);
+ }});
+ //refresh the tree
+ self.tree.refreshItem();
+ });
+ addButton("Revert",function(){
+ self.store.revert();
+ });
+ addButton("Add Column", function(){
+ var columnName = prompt("Enter column name:","property");
+ if(columnName){
+ self.gridLayout.push({
+ field: columnName,
+ name: columnName,
+ formatter: dojo.hitch(self,"_formatCell"),
+ editable: true
+ });
+ self.grid.attr("structure",self.gridLayout);
+ }
+ });
+ var centerCP = new dijit.layout.ContentPane({
+ region:'center'
+ }).placeAt(this);
+ var grid = this.grid = new dojox.grid.DataGrid(
+ {store: this.store}
+ );
+ centerCP.attr("content", grid);
+ grid.canEdit = function(inCell, inRowIndex){
+ var value = this._copyAttr(inRowIndex, inCell.field);
+ return !(value && typeof value == 'object') || value instanceof Date;
+ }
+
+ var trailingCP = new dijit.layout.ContentPane({
+ region: 'trailing',
+ splitter: true,
+ style: "width: 300px"
+ }).placeAt(this);
+
+ var tree = this.tree = new dojox.data.ItemExplorer({
+ store: this.store}
+ );
+ trailingCP.attr("content", tree);
+
+ dojo.connect(grid, "onCellClick", function(){
+ var selected = grid.selection.getSelected()[0];
+ tree.setItem(selected);
+ });
+
+ this.gridOnFetchComplete = grid._onFetchComplete;
+ this.setStore(this.store);
+ },
+ setQuery: function(query, options){
+ this.grid.setQuery(query, options);
+ },
+ _formatCell: function(value){
+ if(this.store.isItem(value)){
+ return this.store.getLabel(value) || this.store.getIdentity(value);
+ }
+ return value;
+ },
+ setStore: function(store){
+ this.store = store;
+ var self = this;
+ var grid = this.grid;
+ grid._pending_requests[0] = false;
+ function formatCell(value){
+ return self._formatCell(value);
+ }
+ var defaultOnComplete = this.gridOnFetchComplete;
+ grid._onFetchComplete = function(items, req){
+ var layout = self.gridLayout = [];
+ var column, key, item, i, j, k, idAttributes = store.getIdentityAttributes();
+ for(i = 0; i < idAttributes.length; i++){
+ key = idAttributes[i];
+ layout.push({
+ field: key,
+ name: key,
+ _score: 100,
+ formatter: formatCell,
+ editable: false
+ });
+
+ }
+ for(i=0; item = items[i++];){
+ var keys = store.getAttributes(item);
+ for(k=0; key = keys[k++];){
+ var found = false;
+ for(j=0; column = layout[j++];){
+ if(column.field == key){
+ column._score++;
+ found = true;
+ break;
+ }
+ }
+ if(!found){
+ layout.push({
+ field: key,
+ name: key,
+ _score: 1,
+ formatter: formatCell,
+ styles: "white-space:nowrap; ",
+ editable: true
+ });
+ }
+ }
+ }
+ layout = layout.sort(function(a, b){
+ return b._score - a._score;
+ });
+ if(!self.showAllColumns){
+ for(j=0; column=layout[j]; j++){
+ if(column._score < items.length/40 * j) {
+ layout.splice(j, layout.length-j);
+ break;
+ }
+ }
+ }
+ for(j=0; column = layout[j++];){
+ column.width=self.columnWidth || Math.round(100/layout.length) + '%';
+ }
+ grid._onFetchComplete = defaultOnComplete;
+ grid.attr("structure",layout);
+ var retValue = defaultOnComplete.apply(this, arguments);
+
+ }
+ grid.setStore(store);
+ this.queryOptions = {cache:true};
+ this.tree.setStore(store);
+ },
+ createNew: function(){
+ var props = prompt("Enter any properties (in JSON literal form) to put in the new item (passed to the newItem constructor):","{ }");
+ if(props){
+ try{
+ this.store.newItem(dojo.fromJson(props));
+ }catch(e){
+ alert(e);
+ }
+
+ }
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/StoreExplorer.xd.js b/js/dojo-1.6/dojox/data/StoreExplorer.xd.js
new file mode 100644
index 0000000..82d76ba
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/StoreExplorer.xd.js
@@ -0,0 +1,210 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.StoreExplorer"],
+["require", "dojox.grid.DataGrid"],
+["require", "dojox.data.ItemExplorer"],
+["require", "dijit.layout.BorderContainer"],
+["require", "dijit.layout.ContentPane"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.StoreExplorer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.StoreExplorer"] = true;
+dojo.provide("dojox.data.StoreExplorer");
+dojo.require("dojox.grid.DataGrid");
+dojo.require("dojox.data.ItemExplorer");
+dojo.require("dijit.layout.BorderContainer");
+dojo.require("dijit.layout.ContentPane");
+
+dojo.declare("dojox.data.StoreExplorer", dijit.layout.BorderContainer, {
+ constructor: function(options){
+ dojo.mixin(this, options);
+ },
+ store: null,
+ columnWidth: '',
+ stringQueries: false,
+ showAllColumns: false,
+ postCreate: function(){
+ var self = this;
+ this.inherited(arguments);
+ var contentPane = new dijit.layout.ContentPane({
+ region:'top'
+ }).placeAt(this);
+ function addButton(name, action){
+ var button = new dijit.form.Button({label: name});
+ contentPane.containerNode.appendChild(button.domNode);
+ button.onClick = action;
+ return button;
+ }
+ var queryText = contentPane.containerNode.appendChild(document.createElement("span"));
+ queryText.innerHTML = "Enter query: &nbsp;";
+ queryText.id = "queryText";
+ var queryTextBox = contentPane.containerNode.appendChild(document.createElement("input"));
+ queryTextBox.type = "text";
+ queryTextBox.id = "queryTextBox";
+ addButton("Query",function(){
+ var query = queryTextBox.value;
+ self.setQuery(self.stringQueries ? query : dojo.fromJson(query));
+ });
+ contentPane.containerNode.appendChild(document.createElement("span")).innerHTML = "&nbsp;&nbsp;&nbsp;";
+ var createNewButton = addButton("Create New", dojo.hitch(this, "createNew"));
+ var deleteButton = addButton("Delete",function(){
+ var items = grid.selection.getSelected();
+ for(var i = 0; i < items.length; i++){
+ self.store.deleteItem(items[i]);
+ }
+ });
+ this.setItemName = function(name){
+ createNewButton.attr('label',"<img style='width:12px; height:12px' src='" + dojo.moduleUrl("dijit.themes.tundra.images","dndCopy.png") + "' /> Create New " + name);
+ deleteButton.attr('label',"Delete " + name);
+ };
+ addButton("Save",function(){
+ self.store.save({onError:function(error){
+ alert(error);
+ }});
+ //refresh the tree
+ self.tree.refreshItem();
+ });
+ addButton("Revert",function(){
+ self.store.revert();
+ });
+ addButton("Add Column", function(){
+ var columnName = prompt("Enter column name:","property");
+ if(columnName){
+ self.gridLayout.push({
+ field: columnName,
+ name: columnName,
+ formatter: dojo.hitch(self,"_formatCell"),
+ editable: true
+ });
+ self.grid.attr("structure",self.gridLayout);
+ }
+ });
+ var centerCP = new dijit.layout.ContentPane({
+ region:'center'
+ }).placeAt(this);
+ var grid = this.grid = new dojox.grid.DataGrid(
+ {store: this.store}
+ );
+ centerCP.attr("content", grid);
+ grid.canEdit = function(inCell, inRowIndex){
+ var value = this._copyAttr(inRowIndex, inCell.field);
+ return !(value && typeof value == 'object') || value instanceof Date;
+ }
+
+ var trailingCP = new dijit.layout.ContentPane({
+ region: 'trailing',
+ splitter: true,
+ style: "width: 300px"
+ }).placeAt(this);
+
+ var tree = this.tree = new dojox.data.ItemExplorer({
+ store: this.store}
+ );
+ trailingCP.attr("content", tree);
+
+ dojo.connect(grid, "onCellClick", function(){
+ var selected = grid.selection.getSelected()[0];
+ tree.setItem(selected);
+ });
+
+ this.gridOnFetchComplete = grid._onFetchComplete;
+ this.setStore(this.store);
+ },
+ setQuery: function(query, options){
+ this.grid.setQuery(query, options);
+ },
+ _formatCell: function(value){
+ if(this.store.isItem(value)){
+ return this.store.getLabel(value) || this.store.getIdentity(value);
+ }
+ return value;
+ },
+ setStore: function(store){
+ this.store = store;
+ var self = this;
+ var grid = this.grid;
+ grid._pending_requests[0] = false;
+ function formatCell(value){
+ return self._formatCell(value);
+ }
+ var defaultOnComplete = this.gridOnFetchComplete;
+ grid._onFetchComplete = function(items, req){
+ var layout = self.gridLayout = [];
+ var column, key, item, i, j, k, idAttributes = store.getIdentityAttributes();
+ for(i = 0; i < idAttributes.length; i++){
+ key = idAttributes[i];
+ layout.push({
+ field: key,
+ name: key,
+ _score: 100,
+ formatter: formatCell,
+ editable: false
+ });
+
+ }
+ for(i=0; item = items[i++];){
+ var keys = store.getAttributes(item);
+ for(k=0; key = keys[k++];){
+ var found = false;
+ for(j=0; column = layout[j++];){
+ if(column.field == key){
+ column._score++;
+ found = true;
+ break;
+ }
+ }
+ if(!found){
+ layout.push({
+ field: key,
+ name: key,
+ _score: 1,
+ formatter: formatCell,
+ styles: "white-space:nowrap; ",
+ editable: true
+ });
+ }
+ }
+ }
+ layout = layout.sort(function(a, b){
+ return b._score - a._score;
+ });
+ if(!self.showAllColumns){
+ for(j=0; column=layout[j]; j++){
+ if(column._score < items.length/40 * j) {
+ layout.splice(j, layout.length-j);
+ break;
+ }
+ }
+ }
+ for(j=0; column = layout[j++];){
+ column.width=self.columnWidth || Math.round(100/layout.length) + '%';
+ }
+ grid._onFetchComplete = defaultOnComplete;
+ grid.attr("structure",layout);
+ var retValue = defaultOnComplete.apply(this, arguments);
+
+ }
+ grid.setStore(store);
+ this.queryOptions = {cache:true};
+ this.tree.setStore(store);
+ },
+ createNew: function(){
+ var props = prompt("Enter any properties (in JSON literal form) to put in the new item (passed to the newItem constructor):","{ }");
+ if(props){
+ try{
+ this.store.newItem(dojo.fromJson(props));
+ }catch(e){
+ alert(e);
+ }
+
+ }
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/WikipediaStore.js b/js/dojo-1.6/dojox/data/WikipediaStore.js
new file mode 100644
index 0000000..4388ced
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/WikipediaStore.js
@@ -0,0 +1,126 @@
+/*
+ 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["dojox.data.WikipediaStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.WikipediaStore"] = true;
+dojo.provide("dojox.data.WikipediaStore");
+dojo.require("dojo.io.script");
+dojo.require("dojox.rpc.Service");
+dojo.require("dojox.data.ServiceStore");
+
+
+
+dojo.experimental("dojox.data.WikipediaStore");
+
+dojo.declare("dojox.data.WikipediaStore", dojox.data.ServiceStore,{
+ // summary:
+ // Initializer for the Wikipedia data store interface.
+ // description:
+ // The WikipediaStore is a data store interface to Wikipedia, using the
+ // Wikipedia SMD spec from dojox.rpc. It currently is useful only for
+ // finding articles that contain some particular text or grabbing single
+ // articles by full name; no wildcards or other filtering are supported.
+ // example:
+ // | var store = new dojox.data.WikipediaStore();
+ // | store.fetch({
+ // | query: {title:"Dojo Toolkit"},
+ // | onItem: function(item){
+ // | dojo.byId("somediv").innerHTML = item.text["*"];
+ // | }
+ // | });
+ constructor: function(options){
+ if(options && options.service){
+ this.service = options.service;
+ }else{
+ var svc = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd"));
+ this.service = svc.query;
+ }
+
+ this.idAttribute = this.labelAttribute = "title";
+ },
+
+ fetch: function(/* object */ request){
+ // summary:
+ // Fetch a page or some partially-loaded search results from
+ // Wikipedia. Note that there isn't a way to sort data coming
+ // in from the API, so we just ignore the *sort* parameter.
+ // example:
+ // Loading a page:
+ // | store.fetch({
+ // | query: {title:"Dojo Toolkit"},
+ // | // define your handlers here
+ // | });
+ // example:
+ // Searching for pages containing "dojo":
+ // | store.fetch({
+ // | query: {
+ // | action: "query",
+ // | text: "dojo"
+ // | },
+ // | // define your handlers here
+ // | });
+ // example:
+ // Searching for the next 50 pages containing "dojo":
+ // | store.fetch({
+ // | query: {
+ // | action: "query",
+ // | text: "dojo",
+ // | start: 10,
+ // | count: 50 // max 500; will be capped if necessary
+ // | },
+ // | // define your handlers here
+ // | });
+ var rq = dojo.mixin({}, request.query);
+ if(rq && (!rq.action || rq.action === "parse")){
+ // default to a single page fetch
+ rq.action = "parse";
+ rq.page = rq.title;
+ delete rq.title;
+
+ }else if(rq.action === "query"){
+ // perform a full text search on page content
+ rq.list = "search";
+ rq.srwhat = "text";
+ rq.srsearch = rq.text;
+ if(request.start){
+ rq.sroffset = request.start-1;
+ }
+ if(request.count){
+ rq.srlimit = request.count >= 500 ? 500 : request.count;
+ }
+ delete rq.text;
+ }
+ request.query = rq;
+ return this.inherited(arguments);
+ },
+
+ _processResults: function(results, def){
+ if(results.parse){
+ // loading a complete page
+ results.parse.title = dojo.queryToObject(def.ioArgs.url.split("?")[1]).page;
+ results = [results.parse];
+
+ }else if(results.query && results.query.search){
+ // loading some search results; all we have here is page titles,
+ // so we mark our items as incomplete
+ results = results.query.search;
+ var _thisStore = this;
+ for(var i in results){
+ results[i]._loadObject = function(callback){
+ _thisStore.fetch({
+ query: { action:"parse", title:this.title },
+ onItem: callback
+ });
+ delete this._loadObject;
+ }
+ }
+ }
+ return this.inherited(arguments);
+ }
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/WikipediaStore.xd.js b/js/dojo-1.6/dojox/data/WikipediaStore.xd.js
new file mode 100644
index 0000000..9e1b314
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/WikipediaStore.xd.js
@@ -0,0 +1,133 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.WikipediaStore"],
+["require", "dojo.io.script"],
+["require", "dojox.rpc.Service"],
+["require", "dojox.data.ServiceStore"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.WikipediaStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.WikipediaStore"] = true;
+dojo.provide("dojox.data.WikipediaStore");
+dojo.require("dojo.io.script");
+dojo.require("dojox.rpc.Service");
+dojo.require("dojox.data.ServiceStore");
+
+
+
+dojo.experimental("dojox.data.WikipediaStore");
+
+dojo.declare("dojox.data.WikipediaStore", dojox.data.ServiceStore,{
+ // summary:
+ // Initializer for the Wikipedia data store interface.
+ // description:
+ // The WikipediaStore is a data store interface to Wikipedia, using the
+ // Wikipedia SMD spec from dojox.rpc. It currently is useful only for
+ // finding articles that contain some particular text or grabbing single
+ // articles by full name; no wildcards or other filtering are supported.
+ // example:
+ // | var store = new dojox.data.WikipediaStore();
+ // | store.fetch({
+ // | query: {title:"Dojo Toolkit"},
+ // | onItem: function(item){
+ // | dojo.byId("somediv").innerHTML = item.text["*"];
+ // | }
+ // | });
+ constructor: function(options){
+ if(options && options.service){
+ this.service = options.service;
+ }else{
+ var svc = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd"));
+ this.service = svc.query;
+ }
+
+ this.idAttribute = this.labelAttribute = "title";
+ },
+
+ fetch: function(/* object */ request){
+ // summary:
+ // Fetch a page or some partially-loaded search results from
+ // Wikipedia. Note that there isn't a way to sort data coming
+ // in from the API, so we just ignore the *sort* parameter.
+ // example:
+ // Loading a page:
+ // | store.fetch({
+ // | query: {title:"Dojo Toolkit"},
+ // | // define your handlers here
+ // | });
+ // example:
+ // Searching for pages containing "dojo":
+ // | store.fetch({
+ // | query: {
+ // | action: "query",
+ // | text: "dojo"
+ // | },
+ // | // define your handlers here
+ // | });
+ // example:
+ // Searching for the next 50 pages containing "dojo":
+ // | store.fetch({
+ // | query: {
+ // | action: "query",
+ // | text: "dojo",
+ // | start: 10,
+ // | count: 50 // max 500; will be capped if necessary
+ // | },
+ // | // define your handlers here
+ // | });
+ var rq = dojo.mixin({}, request.query);
+ if(rq && (!rq.action || rq.action === "parse")){
+ // default to a single page fetch
+ rq.action = "parse";
+ rq.page = rq.title;
+ delete rq.title;
+
+ }else if(rq.action === "query"){
+ // perform a full text search on page content
+ rq.list = "search";
+ rq.srwhat = "text";
+ rq.srsearch = rq.text;
+ if(request.start){
+ rq.sroffset = request.start-1;
+ }
+ if(request.count){
+ rq.srlimit = request.count >= 500 ? 500 : request.count;
+ }
+ delete rq.text;
+ }
+ request.query = rq;
+ return this.inherited(arguments);
+ },
+
+ _processResults: function(results, def){
+ if(results.parse){
+ // loading a complete page
+ results.parse.title = dojo.queryToObject(def.ioArgs.url.split("?")[1]).page;
+ results = [results.parse];
+
+ }else if(results.query && results.query.search){
+ // loading some search results; all we have here is page titles,
+ // so we mark our items as incomplete
+ results = results.query.search;
+ var _thisStore = this;
+ for(var i in results){
+ results[i]._loadObject = function(callback){
+ _thisStore.fetch({
+ query: { action:"parse", title:this.title },
+ onItem: callback
+ });
+ delete this._loadObject;
+ }
+ }
+ }
+ return this.inherited(arguments);
+ }
+});
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/XmlStore.js b/js/dojo-1.6/dojox/data/XmlStore.js
new file mode 100644
index 0000000..44c228a
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/XmlStore.js
@@ -0,0 +1,1530 @@
+/*
+ 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["dojox.data.XmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.XmlStore"] = true;
+dojo.provide("dojox.data.XmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.provide("dojox.data.XmlItem");
+
+dojo.declare("dojox.data.XmlStore", null, {
+ // summary:
+ // A data store for XML based services or documents
+ // description:
+ // A data store for XML based services or documents
+
+ constructor: function(/* object */ args){
+ // summary:
+ // Constructor for the XML store.
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url to a service or an XML document that represents the store
+ // rootItem: A tag name for root items
+ // keyAttribute: An attribute name for a key or an identity (unique identifier)
+ // Required for serverside fetchByIdentity, etc. Not required for
+ // client side fetchItemBIdentity, as it will use an XPath-like
+ // structure if keyAttribute was not specified. Recommended to always
+ // set this, though, for consistent identity behavior.
+ // attributeMap: An anonymous object contains properties for attribute mapping,
+ // {"tag_name.item_attribute_name": "@xml_attribute_name", ...}
+ // sendQuery: A boolean indicate to add a query string to the service URL.
+ // Default is false.
+ // urlPreventCache: Parameter to indicate whether or not URL calls should apply
+ // the preventCache option to the xhr request.
+ if(args){
+ this.url = args.url;
+ this.rootItem = (args.rootItem || args.rootitem || this.rootItem);
+ this.keyAttribute = (args.keyAttribute || args.keyattribute || this.keyAttribute);
+ this._attributeMap = (args.attributeMap || args.attributemap);
+ this.label = args.label || this.label;
+ this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery);
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ this._newItems = [];
+ this._deletedItems = [];
+ this._modifiedItems = [];
+ },
+
+ //Values that may be set by the parser.
+ //Ergo, have to be instantiated to something
+ //So the parser knows how to set them.
+ url: "",
+
+ // A tag name for XML tags to be considered root items in the hierarchy
+ rootItem: "",
+
+ // An attribute name for a key or an identity (unique identifier)
+ // Required for serverside fetchByIdentity, etc. Not required for
+ // client side fetchItemBIdentity, as it will use an XPath-like
+ // structure if keyAttribute was not specified. Recommended to always
+ // set this, though, for consistent identity behavior.
+ keyAttribute: "",
+
+ // An attribute of the item to use as the label.
+ label: "",
+
+ // A boolean indicate to add a query string to the service URL.
+ // Default is false.
+ sendQuery: false,
+
+ // 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.
+ attributeMap: null,
+
+ // Parameter to indicate whether or not URL calls should apply the preventCache option to the xhr request.
+ urlPreventCache: true,
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", the tag name of the element is
+ // returned.
+ // If 'attribute' specifies "childNodes", the first element child is
+ // returned.
+ // If 'attribute' specifies "text()", the value of the first text
+ // child is returned.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value of the XML
+ // attribute is returned.
+ // Otherwise, the first child element of the tag name specified with
+ // 'attribute' is returned.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // defaultValue:
+ // A default value
+ // returns:
+ // An attribute value found, otherwise 'defaultValue'
+ var element = item.element;
+ var i;
+ var node;
+ if(attribute === "tagName"){
+ return element.nodeName;
+ }else if(attribute === "childNodes"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ return this._getItem(node); //object
+ }
+ }
+ return defaultValue;
+ }else if(attribute === "text()"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 3 /*TEXT_NODE*/ ||
+ node.nodeType === 4 /*CDATA_SECTION_NODE*/){
+ return node.nodeValue; //string
+ }
+ }
+ return defaultValue;
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ var value = element.getAttribute(name);
+ //Note that getAttribute will return null or empty string for undefined/unset
+ //attributes, therefore, we should just check the return was valid
+ //non-empty string and not null.
+ return (value) ? value : defaultValue; //object
+ }else{
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ return this._getItem(node); //object
+ }
+ }
+ return defaultValue; //object
+ }
+ }
+ },
+
+ getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Return an array of attribute values
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", the tag name of the element is
+ // returned.
+ // If 'attribute' specifies "childNodes", child elements are returned.
+ // If 'attribute' specifies "text()", the values of child text nodes
+ // are returned.
+ // For generic attributes, if 'attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value of the XML
+ // attribute is returned.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are returned.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, An XML attribute name or one of
+ // special names
+ // returns:
+ // An array of attribute values found, otherwise an empty array
+ var element = item.element;
+ var values = [];
+ var i;
+ var node;
+ if(attribute === "tagName"){
+ return [element.nodeName];
+ }else if(attribute === "childNodes"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ values.push(this._getItem(node));
+ }
+ }
+ return values; //array
+ }else if(attribute === "text()"){
+ var ec = element.childNodes;
+ for(i = 0; i < ec.length; i++){
+ node = ec[i];
+ if(node.nodeType === 3 || node.nodeType === 4){
+ values.push(node.nodeValue);
+ }
+ }
+ return values; //array
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ var value = element.getAttribute(name);
+ return (value !== undefined) ? [value] : []; //array
+ }else{
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ values.push(this._getItem(node));
+ }
+ }
+ return values; //array
+ }
+ }
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // Return an array of attribute names
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // tag names of child elements and XML attribute names of attributes
+ // specified to the element are returned along with special attribute
+ // names applicable to the element including "tagName", "childNodes"
+ // if the element has child elements, "text()" if the element has
+ // child text nodes, and attribute names in '_attributeMap' that match
+ // the tag name of the element.
+ // item:
+ // An XML element
+ // returns:
+ // An array of attributes found
+ var element = item.element;
+ var attributes = [];
+ var i;
+ attributes.push("tagName");
+ if(element.childNodes.length > 0){
+ var names = {};
+ var childNodes = true;
+ var text = false;
+ for(i = 0; i < element.childNodes.length; i++){
+ var node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ var name = node.nodeName;
+ if(!names[name]){
+ attributes.push(name);
+ names[name] = name;
+ }
+ childNodes = true;
+ }else if(node.nodeType === 3){
+ text = true;
+ }
+ }
+ if(childNodes){
+ attributes.push("childNodes");
+ }
+ if(text){
+ attributes.push("text()");
+ }
+ }
+ for(i = 0; i < element.attributes.length; i++){
+ attributes.push("@" + element.attributes[i].nodeName);
+ }
+ if(this._attributeMap){
+ for(var key in this._attributeMap){
+ i = key.indexOf('.');
+ if(i > 0){
+ var tagName = key.substring(0, i);
+ if(tagName === element.nodeName){
+ attributes.push(key.substring(i + 1));
+ }
+ }else{ // global attribute
+ attributes.push(key);
+ }
+ }
+ }
+ return attributes; //array
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Check whether an element has the attribute
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the element has the attribute, otherwise false
+ return (this.getValue(item, attribute) !== undefined); //boolean
+ },
+
+ containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value){
+ // summary:
+ // Check whether the attribute values contain the value
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the attribute values contain the value, otherwise false
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; i++){
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element)
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ if(something && something.element && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element) and loaded
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item (XML element)
+ // keywordArgs:
+ // object containing the args for loadItem. See dojo.data.api.Read.loadItem()
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ // returns:
+ // "dojo.data.api.Read" and "dojo.data.api.Write"
+ var features = {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Write": true
+ };
+
+ //Local XML parsing can implement Identity fairly simple via
+ if(!this.sendQuery || this.keyAttribute !== ""){
+ features["dojo.data.api.Identity"] = true;
+ }
+ return features; //array
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label){
+ return label.toString();
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If 'sendQuery' is true, an XML document is loaded from
+ // 'url' with a query string.
+ // Otherwise, an XML document is loaded and list XML elements that
+ // match to a query (set of element names and their text attribute
+ // values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If 'rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var url = this._getFetchUrl(request);
+ console.log("XmlStore._fetchItems(): url=" + url);
+ if(!url){
+ errorHandler(new Error("No URL specified."));
+ return;
+ }
+ var localRequest = (!this.sendQuery ? request : {}); // use request for _getItems()
+
+ var self = this;
+ var getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var items = self._getItems(data, localRequest);
+ console.log("XmlStore._fetchItems(): length=" + (items ? items.length : 0));
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ });
+ getHandler.addErrback(function(data){
+ errorHandler(data, request);
+ });
+ },
+
+ _getFetchUrl: function(request){
+ // summary:
+ // Generate a URL for fetch
+ // description:
+ // This default implementation generates a query string in the form of
+ // "?name1=value1&name2=value2..." off properties of 'query' object
+ // specified in 'request' and appends it to 'url', if 'sendQuery'
+ // is set to false.
+ // Otherwise, 'url' is returned as is.
+ // Sub-classes may override this method for the custom URL generation.
+ // request:
+ // A request object
+ // returns:
+ // A fetch URL
+ if(!this.sendQuery){
+ return this.url;
+ }
+ var query = request.query;
+ if(!query){
+ return this.url;
+ }
+ if(dojo.isString(query)){
+ return this.url + query;
+ }
+ var queryString = "";
+ for(var name in query){
+ var value = query[name];
+ if(value){
+ if(queryString){
+ queryString += "&";
+ }
+ queryString += (name + "=" + value);
+ }
+ }
+ if(!queryString){
+ return this.url;
+ }
+ //Check to see if the URL already has query params or not.
+ var fullUrl = this.url;
+ if(fullUrl.indexOf("?") < 0){
+ fullUrl += "?";
+ }else{
+ fullUrl += "&";
+ }
+ return fullUrl + queryString;
+ },
+
+ _getItems: function(document, request){
+ // summary:
+ // Fetch items (XML elements) in an XML document based on a request
+ // description:
+ // This default implementation walks through child elements of
+ // the document element to see if all properties of 'query' object
+ // match corresponding attributes of the element (item).
+ // If 'request' is not specified, all child elements are returned.
+ // Sub-classes may override this method for the custom search in
+ // an XML document.
+ // document:
+ // An XML document
+ // request:
+ // A request object
+ // returns:
+ // An array of items
+ var query = null;
+ if(request){
+ query = request.query;
+ }
+ var items = [];
+ var nodes = null;
+
+ if(this.rootItem !== ""){
+ nodes = dojo.query(this.rootItem, document);
+ }else{
+ nodes = document.documentElement.childNodes;
+ }
+
+ var deep = request.queryOptions ? request.queryOptions.deep : false;
+ if(deep){
+ nodes = this._flattenNodes(nodes);
+ }
+ for(var i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ if(node.nodeType != 1 /*ELEMENT_NODE*/){
+ continue;
+ }
+ var item = this._getItem(node);
+ if(query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ var value;
+ var match = false;
+ var j;
+ var emptyQuery = true;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in query){
+ value = query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+ for(var attribute in query){
+ emptyQuery = false;
+ var values = this.getValues(item, attribute);
+ for(j = 0; j < values.length; j++){
+ value = values[j];
+ if(value){
+ var queryValue = query[attribute];
+ if((typeof value) === "string" &&
+ (regexpList[attribute])){
+ if((value.match(regexpList[attribute])) !== null){
+ match = true;
+ }else{
+ match = false;
+ }
+ }else if((typeof value) === "object"){
+ if( value.toString &&
+ (regexpList[attribute])){
+ var stringValue = value.toString();
+ if((stringValue.match(regexpList[attribute])) !== null){
+ match = true;
+ }else{
+ match = false;
+ }
+ }else{
+ if(queryValue === "*" || queryValue === value){
+ match = true;
+ }else{
+ match = false;
+ }
+ }
+ }
+ }
+ //One of the multiValue values matched,
+ //so quit looking.
+ if(match){
+ break;
+ }
+ }
+ if(!match){
+ break;
+ }
+ }
+ //Either the query was an empty object {}, which is match all, or
+ //was an actual match.
+ if(emptyQuery || match){
+ items.push(item);
+ }
+ }else{
+ //No query, everything matches.
+ items.push(item);
+ }
+ }
+ dojo.forEach(items,function(item){
+ if(item.element.parentNode){
+ item.element.parentNode.removeChild(item.element); // make it root
+ }
+ },this);
+ return items;
+ },
+
+ _flattenNodes: function(nodes){
+ // Summary:
+ // Function used to flatten a hierarchy of XML nodes into a single list for
+ // querying over. Used when deep = true;
+ var flattened = [];
+ if(nodes){
+ var i;
+ for(i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ flattened.push(node);
+ if(node.childNodes && node.childNodes.length > 0){
+ flattened = flattened.concat(this._flattenNodes(node.childNodes));
+ }
+ }
+ }
+ return flattened;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* object? */ keywordArgs, parentInfo){
+ // summary:
+ // Return a new dojox.data.XmlItem
+ // description:
+ // At least, 'keywordArgs' must contain "tagName" to be used for
+ // the new element.
+ // Other attributes in 'keywordArgs' are set to the new element,
+ // including "text()", but excluding "childNodes".
+ // keywordArgs:
+ // An object containing initial attributes
+ // returns:
+ // An XML element
+ console.log("XmlStore.newItem()");
+ keywordArgs = (keywordArgs || {});
+ var tagName = keywordArgs.tagName;
+ if(!tagName){
+ tagName = this.rootItem;
+ if(tagName === ""){
+ return null;
+ }
+ }
+
+ var document = this._getDocument();
+ var element = document.createElement(tagName);
+ for(var attribute in keywordArgs){
+ var text;
+ if(attribute === "tagName"){
+ continue;
+ }else if(attribute === "text()"){
+ text = document.createTextNode(keywordArgs[attribute]);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(tagName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, keywordArgs[attribute]);
+ }else{
+ var child = document.createElement(attribute);
+ text = document.createTextNode(keywordArgs[attribute]);
+ child.appendChild(text);
+ element.appendChild(child);
+ }
+ }
+ }
+
+ var item = this._getItem(element);
+ this._newItems.push(item);
+
+ var pInfo = null;
+ if(parentInfo && parentInfo.parent && parentInfo.attribute){
+ pInfo = {
+ item: parentInfo.parent,
+ attribute: parentInfo.attribute,
+ oldValue: undefined
+ };
+
+ //See if it is multi-valued or not and handle appropriately
+ //Generally, all attributes are multi-valued for this store
+ //So, we only need to append if there are already values present.
+ var values = this.getValues(parentInfo.parent, parentInfo.attribute);
+ if(values && values.length > 0){
+ var tempValues = values.slice(0, values.length);
+ if(values.length === 1){
+ pInfo.oldValue = values[0];
+ }else{
+ pInfo.oldValue = values.slice(0, values.length);
+ }
+ tempValues.push(item);
+ this.setValues(parentInfo.parent, parentInfo.attribute, tempValues);
+ pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
+ }else{
+ this.setValues(parentInfo.parent, parentInfo.attribute, item);
+ pInfo.newValue = item;
+ }
+ }
+ return item; //object
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary:
+ // Delete an dojox.data.XmlItem (wrapper to a XML element).
+ // item:
+ // An XML element to delete
+ // returns:
+ // True
+ console.log("XmlStore.deleteItem()");
+ var element = item.element;
+ if(element.parentNode){
+ this._backupItem(item);
+ element.parentNode.removeChild(element);
+ return true;
+ }
+ this._forgetItem(item);
+ this._deletedItems.push(item);
+ return true; //boolean
+ },
+
+ setValue: function(/* item */ item, /* attribute || string */ attribute, /* almost anything */ value){
+ // summary:
+ // Set an attribute value
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", nothing is set and false is
+ // returned.
+ // If 'attribute' specifies "childNodes", the value (XML element) is
+ // added to the element.
+ // If 'attribute' specifies "text()", a text node is created with
+ // the value and set it to the element as a child.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value is set to the XML
+ // attribute.
+ // Otherwise, a text node is created with the value and set it to
+ // the first child element of the tag name specified with 'attribute'.
+ // If the child element does not exist, it is created.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // value:
+ // A attribute value to set
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ var child;
+ var text;
+ if(attribute === "childNodes"){
+ child = value.element;
+ element.appendChild(child);
+ }else if(attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ text = this._getDocument(element).createTextNode(value);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, value);
+ }else{
+ for(var i = 0; i < element.childNodes.length; i++){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ child = node;
+ break;
+ }
+ }
+ var document = this._getDocument(element);
+ if(child){
+ while(child.firstChild){
+ child.removeChild(child.firstChild);
+ }
+ }else{
+ child = document.createElement(attribute);
+ element.appendChild(child);
+ }
+ text = document.createTextNode(value);
+ child.appendChild(text);
+ }
+ }
+ return true; //boolean
+ },
+
+ setValues: function(/* item */ item, /* attribute || string */ attribute, /*array*/ values){
+ // summary:
+ // Set attribute values
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", nothing is set and false is
+ // returned.
+ // If 'attribute' specifies "childNodes", the value (array of XML
+ // elements) is set to the element's childNodes.
+ // If 'attribute' specifies "text()", a text node is created with
+ // the values and set it to the element as a child.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the first value is set to
+ // the XML attribute.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are replaced with new child elements and their
+ // child text nodes of values.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, an XML attribute name or one of
+ // special names
+ // value:
+ // A attribute value to set
+ // notify:
+ // A non-API optional argument, used to indicate if notification API should be called
+ // or not.
+
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ var i;
+ var child;
+ var text;
+ if(attribute === "childNodes"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ for(i = 0; i < values.length; i++){
+ child = values[i].element;
+ element.appendChild(child);
+ }
+ }else if(attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ var value = "";
+ for(i = 0; i < values.length; i++){
+ value += values[i];
+ }
+ text = this._getDocument(element).createTextNode(value);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, values[0]);
+ }else{
+ for(i = element.childNodes.length - 1; i >= 0; i--){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ element.removeChild(node);
+ }
+ }
+ var document = this._getDocument(element);
+ for(i = 0; i < values.length; i++){
+ child = document.createElement(attribute);
+ text = document.createTextNode(values[i]);
+ child.appendChild(text);
+ element.appendChild(child);
+ }
+ }
+ }
+ return true; //boolean
+ },
+
+ unsetAttribute: function(/* item */ item, /* attribute || string */ attribute){
+ // summary:
+ // Remove an attribute
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // 'attribute' can be an XML attribute name of the element or one of
+ // special names described below.
+ // If 'attribute' specifies "tagName", nothing is removed and false is
+ // returned.
+ // If 'attribute' specifies "childNodes" or "text()", all child nodes
+ // are removed.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the XML attribute is removed.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are removed.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, an XML attribute name or one of
+ // special names
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ if(attribute === "childNodes" || attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.removeAttribute(name);
+ }else{
+ for(var i = element.childNodes.length - 1; i >= 0; i--){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ element.removeChild(node);
+ }
+ }
+ }
+ }
+ return true; //boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary:
+ // Save new and/or modified items (XML elements)
+ // description:
+ // 'url' is used to save XML documents for new, modified and/or
+ // deleted XML elements.
+ // keywordArgs:
+ // An object for callbacks
+ if(!keywordArgs){
+ keywordArgs = {};
+ }
+ var i;
+ for(i = 0; i < this._modifiedItems.length; i++){
+ this._saveItem(this._modifiedItems[i], keywordArgs, "PUT");
+ }
+ for(i = 0; i < this._newItems.length; i++){
+ var item = this._newItems[i];
+ if(item.element.parentNode){ // reparented
+ this._newItems.splice(i, 1);
+ i--;
+ continue;
+ }
+ this._saveItem(this._newItems[i], keywordArgs, "POST");
+ }
+ for(i = 0; i < this._deletedItems.length; i++){
+ this._saveItem(this._deletedItems[i], keywordArgs, "DELETE");
+ }
+ },
+
+ revert: function(){
+ // summary:
+ // Invalidate changes (new and/or modified elements)
+ // returns:
+ // True
+ console.log("XmlStore.revert() _newItems=" + this._newItems.length);
+ console.log("XmlStore.revert() _deletedItems=" + this._deletedItems.length);
+ console.log("XmlStore.revert() _modifiedItems=" + this._modifiedItems.length);
+ this._newItems = [];
+ this._restoreItems(this._deletedItems);
+ this._deletedItems = [];
+ this._restoreItems(this._modifiedItems);
+ this._modifiedItems = [];
+ return true; //boolean
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary:
+ // Check whether an item is new, modified or deleted
+ // description:
+ // If 'item' is specified, true is returned if the item is new,
+ // modified or deleted.
+ // Otherwise, true is returned if there are any new, modified
+ // or deleted items.
+ // item:
+ // An item (XML element) to check
+ // returns:
+ // True if an item or items are new, modified or deleted, otherwise
+ // false
+ if(item){
+ var element = this._getRootElement(item.element);
+ return (this._getItemIndex(this._newItems, element) >= 0 ||
+ this._getItemIndex(this._deletedItems, element) >= 0 ||
+ this._getItemIndex(this._modifiedItems, element) >= 0); //boolean
+ }else{
+ return (this._newItems.length > 0 ||
+ this._deletedItems.length > 0 ||
+ this._modifiedItems.length > 0); //boolean
+ }
+ },
+
+ _saveItem: function(item, keywordArgs, method){
+ var url;
+ var scope;
+ if(method === "PUT"){
+ url = this._getPutUrl(item);
+ }else if(method === "DELETE"){
+ url = this._getDeleteUrl(item);
+ }else{ // POST
+ url = this._getPostUrl(item);
+ }
+ if(!url){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope, new Error("No URL for saving content: " + this._getPostContent(item)));
+ }
+ return;
+ }
+
+ var saveArgs = {
+ url: url,
+ method: (method || "POST"),
+ contentType: "text/xml",
+ handleAs: "xml"
+ };
+ var saveHandler;
+ if(method === "PUT"){
+ saveArgs.putData = this._getPutContent(item);
+ saveHandler = dojo.rawXhrPut(saveArgs);
+ }else if(method === "DELETE"){
+ saveHandler = dojo.xhrDelete(saveArgs);
+ }else{ // POST
+ saveArgs.postData = this._getPostContent(item);
+ saveHandler = dojo.rawXhrPost(saveArgs);
+ }
+ scope = (keywordArgs.scope || dojo.global);
+ var self = this;
+ saveHandler.addCallback(function(data){
+ self._forgetItem(item);
+ if(keywordArgs.onComplete){
+ keywordArgs.onComplete.call(scope);
+ }
+ });
+ saveHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ },
+
+ _getPostUrl: function(item){
+ // summary:
+ // Generate a URL for post
+ // description:
+ // This default implementation just returns 'url'.
+ // Sub-classes may override this method for the custom URL.
+ // item:
+ // An item to save
+ // returns:
+ // A post URL
+ return this.url; //string
+ },
+
+ _getPutUrl: function(item){
+ // summary:
+ // Generate a URL for put
+ // description:
+ // This default implementation just returns 'url'.
+ // Sub-classes may override this method for the custom URL.
+ // item:
+ // An item to save
+ // returns:
+ // A put URL
+ return this.url; //string
+ },
+
+ _getDeleteUrl: function(item){
+ // summary:
+ // Generate a URL for delete
+ // description:
+ // This default implementation returns 'url' with 'keyAttribute'
+ // as a query string.
+ // Sub-classes may override this method for the custom URL based on
+ // changes (new, deleted, or modified).
+ // item:
+ // An item to delete
+ // returns:
+ // A delete URL
+ var url = this.url;
+ if(item && this.keyAttribute !== ""){
+ var value = this.getValue(item, this.keyAttribute);
+ if(value){
+ var key = this.keyAttribute.charAt(0) ==='@' ? this.keyAttribute.substring(1): this.keyAttribute;
+ url += url.indexOf('?') < 0 ? '?' : '&';
+ url += key + '=' + value;
+ }
+ }
+ return url; //string
+ },
+
+ _getPostContent: function(item){
+ // summary:
+ // Generate a content to post
+ // description:
+ // This default implementation generates an XML document for one
+ // (the first only) new or modified element.
+ // Sub-classes may override this method for the custom post content
+ // generation.
+ // item:
+ // An item to save
+ // returns:
+ // A post content
+ var element = item.element;
+ var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?
+ return declaration + dojox.xml.parser.innerXML(element); //XML string
+ },
+
+ _getPutContent: function(item){
+ // summary:
+ // Generate a content to put
+ // description:
+ // This default implementation generates an XML document for one
+ // (the first only) new or modified element.
+ // Sub-classes may override this method for the custom put content
+ // generation.
+ // item:
+ // An item to save
+ // returns:
+ // A post content
+ var element = item.element;
+ var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?
+ return declaration + dojox.xml.parser.innerXML(element); //XML string
+ },
+
+/* internal API */
+
+ _getAttribute: function(tagName, attribute){
+ if(this._attributeMap){
+ var key = tagName + "." + attribute;
+ var value = this._attributeMap[key];
+ if(value){
+ attribute = value;
+ }else{ // look for global attribute
+ value = this._attributeMap[attribute];
+ if(value){
+ attribute = value;
+ }
+ }
+ }
+ return attribute; //object
+ },
+
+ _getItem: function(element){
+ try{
+ var q = null;
+ //Avoid function call if possible.
+ if(this.keyAttribute === ""){
+ q = this._getXPath(element);
+ }
+ return new dojox.data.XmlItem(element, this, q); //object
+ }catch (e){
+ console.log(e);
+ }
+ return null;
+ },
+
+ _getItemIndex: function(items, element){
+ for(var i = 0; i < items.length; i++){
+ if(items[i].element === element){
+ return i; //int
+ }
+ }
+ return -1; //int
+ },
+
+ _backupItem: function(item){
+ var element = this._getRootElement(item.element);
+ if( this._getItemIndex(this._newItems, element) >= 0 ||
+ this._getItemIndex(this._modifiedItems, element) >= 0){
+ return; // new or already modified
+ }
+ if(element != item.element){
+ item = this._getItem(element);
+ }
+ item._backup = element.cloneNode(true);
+ this._modifiedItems.push(item);
+ },
+
+ _restoreItems: function(items){
+
+ dojo.forEach(items,function(item){
+ if(item._backup){
+ item.element = item._backup;
+ item._backup = null;
+ }
+ },this);
+ },
+
+ _forgetItem: function(item){
+ var element = item.element;
+ var index = this._getItemIndex(this._newItems, element);
+ if(index >= 0){
+ this._newItems.splice(index, 1);
+ }
+ index = this._getItemIndex(this._deletedItems, element);
+ if(index >= 0){
+ this._deletedItems.splice(index, 1);
+ }
+ index = this._getItemIndex(this._modifiedItems, element);
+ if(index >= 0){
+ this._modifiedItems.splice(index, 1);
+ }
+ },
+
+ _getDocument: function(element){
+ if(element){
+ return element.ownerDocument; //DOMDocument
+ }else if(!this._document){
+ return dojox.xml.parser.parse(); // DOMDocument
+ }
+ return null; //null
+ },
+
+ _getRootElement: function(element){
+ while(element.parentNode){
+ element = element.parentNode;
+ }
+ return element; //DOMElement
+ },
+
+ _getXPath: function(element){
+ // summary:
+ // A function to compute the xpath of a node in a DOM document.
+ // description:
+ // A function to compute the xpath of a node in a DOM document. Used for
+ // Client side query handling and identity.
+ var xpath = null;
+ if(!this.sendQuery){
+ //xpath should be null for any server queries, as we don't have the entire
+ //XML dom to figure it out.
+ var node = element;
+ xpath = "";
+ while(node && node != element.ownerDocument){
+ var pos = 0;
+ var sibling = node;
+ var name = node.nodeName;
+ while(sibling){
+ sibling = sibling.previousSibling;
+ if(sibling && sibling.nodeName === name){
+ pos++;
+ }
+ }
+ var temp = "/" + name + "[" + pos + "]";
+ if(xpath){
+ xpath = temp + xpath;
+ }else{
+ xpath = temp;
+ }
+ node = node.parentNode;
+ }
+ }
+ return xpath; //string
+ },
+
+ /*************************************
+ * Dojo.data Identity implementation *
+ *************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // Returns a unique identifier for an item.
+ // item:
+ // The XML Item from the store from which to obtain its identifier.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.XmlStore: Object supplied to getIdentity is not an item");
+ }else{
+ var id = null;
+ if(this.sendQuery && this.keyAttribute !== ""){
+ id = this.getValue(item, this.keyAttribute).toString();
+ }else if(!this.serverQuery){
+ if(this.keyAttribute !== ""){
+ id = this.getValue(item,this.keyAttribute).toString();
+ }else{
+ //No specified identity, so return the dojo.query/xpath
+ //for the node as fallback.
+ id = item.q;
+ }
+ }
+ return id; //String.
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // Returns an array of attribute names that are used to generate the identity.
+ // description:
+ // For XmlStore, if sendQuery is false and no keyAttribute was set, then this function
+ // returns null, as xpath is used for the identity, which is not a public attribute of
+ // the item. If sendQuery is true and keyAttribute is set, then this function
+ // returns an array of one attribute name: keyAttribute. This means the server side
+ // implementation must apply a keyAttribute to a returned node that always allows
+ // it to be looked up again.
+ // item:
+ // The item from the store from which to obtain the array of public attributes that
+ // compose the identifier, if any.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.XmlStore: Object supplied to getIdentity is not an item");
+ }else{
+ if(this.keyAttribute !== ""){
+ return [this.keyAttribute]; //array
+ }else{
+ //Otherwise it's either using xpath (not an attribute), or the remote store
+ //doesn't support identity.
+ return null; //null
+ }
+ }
+ },
+
+
+ fetchItemByIdentity: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity(keywordArgs)
+ var handleDocument = null;
+ var scope = null;
+ var self = this;
+ var url = null;
+ var getArgs = null;
+ var getHandler = null;
+
+ if(!self.sendQuery){
+ handleDocument = function(data){
+ if(data){
+ if(self.keyAttribute !== ""){
+ //We have a key attribute specified. So ... we can process the items and locate the item
+ //that contains a matching key attribute. Its identity, as it were.
+ var request = {};
+ request.query={};
+ request.query[self.keyAttribute] = keywordArgs.identity;
+ request.queryOptions = {deep: true};
+ var items = self._getItems(data,request);
+ scope = keywordArgs.scope || dojo.global;
+ if(items.length === 1){
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, items[0]);
+ }
+ }else if(items.length === 0){
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, null);
+ }
+ }else{
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, new Error("Items array size for identity lookup greater than 1, invalid keyAttribute."));
+ }
+ }
+ }else{
+ //Since dojo.query doesn't really support the functions needed
+ //to do child node selection on IE well and since xpath support
+ //is flakey across browsers, it's simpler to implement a
+ //pseudo-xpath parser here.
+ var qArgs = keywordArgs.identity.split("/");
+ var i;
+ var node = data;
+ for(i = 0; i < qArgs.length; i++){
+ if(qArgs[i] && qArgs[i] !== ""){
+ var section = qArgs[i];
+ section = section.substring(0,section.length - 1);
+ var vals = section.split("[");
+ var tag = vals[0];
+ var index = parseInt(vals[1], 10);
+ var pos = 0;
+ if(node){
+ var cNodes = node.childNodes;
+ if(cNodes){
+ var j;
+ var foundNode = null;
+ for(j = 0; j < cNodes.length; j++){
+ var pNode = cNodes[j];
+ if(pNode.nodeName === tag){
+ if(pos < index){
+ pos++;
+ }else{
+ foundNode = pNode;
+ break;
+ }
+ }
+ }
+ if(foundNode){
+ node = foundNode;
+ }else{
+ node = null;
+ }
+ }else{
+ node = null;
+ }
+ }else{
+ break;
+ }
+ }
+ }
+ //Return what we found, if any.
+ var item = null;
+ if(node){
+ item = self._getItem(node);
+ if(item.element.parentNode){
+ item.element.parentNode.removeChild(item.element);
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ };
+ url = this._getFetchUrl(null);
+ getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ getHandler = dojo.xhrGet(getArgs);
+
+ //Add in the callbacks for completion of data load.
+ getHandler.addCallback(handleDocument);
+ if(keywordArgs.onError){
+ getHandler.addErrback(function(error){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, error);
+ });
+ }
+ }else{
+ //Server side querying, so need to pass the keyAttribute back to the server and let it return
+ //what it will. It SHOULD be only one item.
+ if(self.keyAttribute !== ""){
+ var request = {query:{}};
+ request.query[self.keyAttribute] = keywordArgs.identity;
+ url = this._getFetchUrl(request);
+ handleDocument = function(data){
+ var item = null;
+ if(data){
+ var items = self._getItems(data, {});
+ if(items.length === 1){
+ item = items[0];
+ }else{
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope, new Error("More than one item was returned from the server for the denoted identity"));
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ };
+
+ getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ getHandler = dojo.xhrGet(getArgs);
+
+ //Add in the callbacks for completion of data load.
+ getHandler.addCallback(handleDocument);
+ if(keywordArgs.onError){
+ getHandler.addErrback(function(error){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, error);
+ });
+ }
+ }else{
+ if(keywordArgs.onError){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, new Error("XmlStore is not told that the server to provides identity support. No keyAttribute specified."));
+ }
+ }
+ }
+ }
+});
+
+dojo.declare("dojox.data.XmlItem", null, {
+ constructor: function(element, store, query){
+ // summary:
+ // Initialize with an XML element
+ // element:
+ // An XML element
+ // store:
+ // The containing store, if any.
+ // query:
+ // The query to use to look up a specific element.
+ // Usually an XPath or dojo.query statement.
+ this.element = element;
+ this.store = store;
+ this.q = query;
+ },
+ // summary:
+ // A data item of 'XmlStore'
+ // description:
+ // This class represents an item of 'XmlStore' holding an XML element.
+ // 'element'
+ // element:
+ // An XML element
+ toString: function(){
+ // summary:
+ // Return a value of the first text child of the element
+ // returns:
+ // a value of the first text child of the element
+ var str = "";
+ if(this.element){
+ for(var i = 0; i < this.element.childNodes.length; i++){
+ var node = this.element.childNodes[i];
+ if(node.nodeType === 3 || node.nodeType === 4){
+ str += node.nodeValue;
+ }
+ }
+ }
+ return str; //String
+ }
+});
+dojo.extend(dojox.data.XmlStore,dojo.data.util.simpleFetch);
+
+}
diff --git a/js/dojo-1.6/dojox/data/XmlStore.xd.js b/js/dojo-1.6/dojox/data/XmlStore.xd.js
new file mode 100644
index 0000000..317ca5b
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/XmlStore.xd.js
@@ -0,0 +1,1538 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.XmlStore"],
+["require", "dojo.data.util.simpleFetch"],
+["require", "dojo.data.util.filter"],
+["require", "dojox.xml.parser"],
+["provide", "dojox.data.XmlItem"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.XmlStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.XmlStore"] = true;
+dojo.provide("dojox.data.XmlStore");
+dojo.require("dojo.data.util.simpleFetch");
+dojo.require("dojo.data.util.filter");
+dojo.require("dojox.xml.parser");
+
+
+
+dojo.provide("dojox.data.XmlItem");
+
+dojo.declare("dojox.data.XmlStore", null, {
+ // summary:
+ // A data store for XML based services or documents
+ // description:
+ // A data store for XML based services or documents
+
+ constructor: function(/* object */ args){
+ // summary:
+ // Constructor for the XML store.
+ // args:
+ // An anonymous object to initialize properties. It expects the following values:
+ // url: The url to a service or an XML document that represents the store
+ // rootItem: A tag name for root items
+ // keyAttribute: An attribute name for a key or an identity (unique identifier)
+ // Required for serverside fetchByIdentity, etc. Not required for
+ // client side fetchItemBIdentity, as it will use an XPath-like
+ // structure if keyAttribute was not specified. Recommended to always
+ // set this, though, for consistent identity behavior.
+ // attributeMap: An anonymous object contains properties for attribute mapping,
+ // {"tag_name.item_attribute_name": "@xml_attribute_name", ...}
+ // sendQuery: A boolean indicate to add a query string to the service URL.
+ // Default is false.
+ // urlPreventCache: Parameter to indicate whether or not URL calls should apply
+ // the preventCache option to the xhr request.
+ if(args){
+ this.url = args.url;
+ this.rootItem = (args.rootItem || args.rootitem || this.rootItem);
+ this.keyAttribute = (args.keyAttribute || args.keyattribute || this.keyAttribute);
+ this._attributeMap = (args.attributeMap || args.attributemap);
+ this.label = args.label || this.label;
+ this.sendQuery = (args.sendQuery || args.sendquery || this.sendQuery);
+ if("urlPreventCache" in args){
+ this.urlPreventCache = args.urlPreventCache?true:false;
+ }
+ }
+ this._newItems = [];
+ this._deletedItems = [];
+ this._modifiedItems = [];
+ },
+
+ //Values that may be set by the parser.
+ //Ergo, have to be instantiated to something
+ //So the parser knows how to set them.
+ url: "",
+
+ // A tag name for XML tags to be considered root items in the hierarchy
+ rootItem: "",
+
+ // An attribute name for a key or an identity (unique identifier)
+ // Required for serverside fetchByIdentity, etc. Not required for
+ // client side fetchItemBIdentity, as it will use an XPath-like
+ // structure if keyAttribute was not specified. Recommended to always
+ // set this, though, for consistent identity behavior.
+ keyAttribute: "",
+
+ // An attribute of the item to use as the label.
+ label: "",
+
+ // A boolean indicate to add a query string to the service URL.
+ // Default is false.
+ sendQuery: false,
+
+ // 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.
+ attributeMap: null,
+
+ // Parameter to indicate whether or not URL calls should apply the preventCache option to the xhr request.
+ urlPreventCache: true,
+
+ /* dojo.data.api.Read */
+
+ getValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* value? */ defaultValue){
+ // summary:
+ // Return an attribute value
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", the tag name of the element is
+ // returned.
+ // If 'attribute' specifies "childNodes", the first element child is
+ // returned.
+ // If 'attribute' specifies "text()", the value of the first text
+ // child is returned.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value of the XML
+ // attribute is returned.
+ // Otherwise, the first child element of the tag name specified with
+ // 'attribute' is returned.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // defaultValue:
+ // A default value
+ // returns:
+ // An attribute value found, otherwise 'defaultValue'
+ var element = item.element;
+ var i;
+ var node;
+ if(attribute === "tagName"){
+ return element.nodeName;
+ }else if(attribute === "childNodes"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ return this._getItem(node); //object
+ }
+ }
+ return defaultValue;
+ }else if(attribute === "text()"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 3 /*TEXT_NODE*/ ||
+ node.nodeType === 4 /*CDATA_SECTION_NODE*/){
+ return node.nodeValue; //string
+ }
+ }
+ return defaultValue;
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ var value = element.getAttribute(name);
+ //Note that getAttribute will return null or empty string for undefined/unset
+ //attributes, therefore, we should just check the return was valid
+ //non-empty string and not null.
+ return (value) ? value : defaultValue; //object
+ }else{
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ return this._getItem(node); //object
+ }
+ }
+ return defaultValue; //object
+ }
+ }
+ },
+
+ getValues: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Return an array of attribute values
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", the tag name of the element is
+ // returned.
+ // If 'attribute' specifies "childNodes", child elements are returned.
+ // If 'attribute' specifies "text()", the values of child text nodes
+ // are returned.
+ // For generic attributes, if 'attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value of the XML
+ // attribute is returned.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are returned.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, An XML attribute name or one of
+ // special names
+ // returns:
+ // An array of attribute values found, otherwise an empty array
+ var element = item.element;
+ var values = [];
+ var i;
+ var node;
+ if(attribute === "tagName"){
+ return [element.nodeName];
+ }else if(attribute === "childNodes"){
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ values.push(this._getItem(node));
+ }
+ }
+ return values; //array
+ }else if(attribute === "text()"){
+ var ec = element.childNodes;
+ for(i = 0; i < ec.length; i++){
+ node = ec[i];
+ if(node.nodeType === 3 || node.nodeType === 4){
+ values.push(node.nodeValue);
+ }
+ }
+ return values; //array
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ var value = element.getAttribute(name);
+ return (value !== undefined) ? [value] : []; //array
+ }else{
+ for(i = 0; i < element.childNodes.length; i++){
+ node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ values.push(this._getItem(node));
+ }
+ }
+ return values; //array
+ }
+ }
+ },
+
+ getAttributes: function(/* item */ item){
+ // summary:
+ // Return an array of attribute names
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // tag names of child elements and XML attribute names of attributes
+ // specified to the element are returned along with special attribute
+ // names applicable to the element including "tagName", "childNodes"
+ // if the element has child elements, "text()" if the element has
+ // child text nodes, and attribute names in '_attributeMap' that match
+ // the tag name of the element.
+ // item:
+ // An XML element
+ // returns:
+ // An array of attributes found
+ var element = item.element;
+ var attributes = [];
+ var i;
+ attributes.push("tagName");
+ if(element.childNodes.length > 0){
+ var names = {};
+ var childNodes = true;
+ var text = false;
+ for(i = 0; i < element.childNodes.length; i++){
+ var node = element.childNodes[i];
+ if(node.nodeType === 1 /*ELEMENT_NODE*/){
+ var name = node.nodeName;
+ if(!names[name]){
+ attributes.push(name);
+ names[name] = name;
+ }
+ childNodes = true;
+ }else if(node.nodeType === 3){
+ text = true;
+ }
+ }
+ if(childNodes){
+ attributes.push("childNodes");
+ }
+ if(text){
+ attributes.push("text()");
+ }
+ }
+ for(i = 0; i < element.attributes.length; i++){
+ attributes.push("@" + element.attributes[i].nodeName);
+ }
+ if(this._attributeMap){
+ for(var key in this._attributeMap){
+ i = key.indexOf('.');
+ if(i > 0){
+ var tagName = key.substring(0, i);
+ if(tagName === element.nodeName){
+ attributes.push(key.substring(i + 1));
+ }
+ }else{ // global attribute
+ attributes.push(key);
+ }
+ }
+ }
+ return attributes; //array
+ },
+
+ hasAttribute: function(/* item */ item, /* attribute || attribute-name-string */ attribute){
+ // summary:
+ // Check whether an element has the attribute
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the element has the attribute, otherwise false
+ return (this.getValue(item, attribute) !== undefined); //boolean
+ },
+
+ containsValue: function(/* item */ item, /* attribute || attribute-name-string */ attribute, /* anything */ value){
+ // summary:
+ // Check whether the attribute values contain the value
+ // item:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // returns:
+ // True if the attribute values contain the value, otherwise false
+ var values = this.getValues(item, attribute);
+ for(var i = 0; i < values.length; i++){
+ if((typeof value === "string")){
+ if(values[i].toString && values[i].toString() === value){
+ return true;
+ }
+ }else if(values[i] === value){
+ return true; //boolean
+ }
+ }
+ return false;//boolean
+ },
+
+ isItem: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element)
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ if(something && something.element && something.store && something.store === this){
+ return true; //boolean
+ }
+ return false; //boolran
+ },
+
+ isItemLoaded: function(/* anything */ something){
+ // summary:
+ // Check whether the object is an item (XML element) and loaded
+ // item:
+ // An object to check
+ // returns:
+ // True if the object is an XML element, otherwise false
+ return this.isItem(something); //boolean
+ },
+
+ loadItem: function(/* object */ keywordArgs){
+ // summary:
+ // Load an item (XML element)
+ // keywordArgs:
+ // object containing the args for loadItem. See dojo.data.api.Read.loadItem()
+ },
+
+ getFeatures: function(){
+ // summary:
+ // Return supported data APIs
+ // returns:
+ // "dojo.data.api.Read" and "dojo.data.api.Write"
+ var features = {
+ "dojo.data.api.Read": true,
+ "dojo.data.api.Write": true
+ };
+
+ //Local XML parsing can implement Identity fairly simple via
+ if(!this.sendQuery || this.keyAttribute !== ""){
+ features["dojo.data.api.Identity"] = true;
+ }
+ return features; //array
+ },
+
+ getLabel: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabel()
+ if((this.label !== "") && this.isItem(item)){
+ var label = this.getValue(item,this.label);
+ if(label){
+ return label.toString();
+ }
+ }
+ return undefined; //undefined
+ },
+
+ getLabelAttributes: function(/* item */ item){
+ // summary:
+ // See dojo.data.api.Read.getLabelAttributes()
+ if(this.label !== ""){
+ return [this.label]; //array
+ }
+ return null; //null
+ },
+
+ _fetchItems: function(request, fetchHandler, errorHandler){
+ // summary:
+ // Fetch items (XML elements) that match to a query
+ // description:
+ // If 'sendQuery' is true, an XML document is loaded from
+ // 'url' with a query string.
+ // Otherwise, an XML document is loaded and list XML elements that
+ // match to a query (set of element names and their text attribute
+ // values that the items to contain).
+ // A wildcard, "*" can be used to query values to match all
+ // occurrences.
+ // If 'rootItem' is specified, it is used to fetch items.
+ // request:
+ // A request object
+ // fetchHandler:
+ // A function to call for fetched items
+ // errorHandler:
+ // A function to call on error
+ var url = this._getFetchUrl(request);
+ console.log("XmlStore._fetchItems(): url=" + url);
+ if(!url){
+ errorHandler(new Error("No URL specified."));
+ return;
+ }
+ var localRequest = (!this.sendQuery ? request : {}); // use request for _getItems()
+
+ var self = this;
+ var getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ var getHandler = dojo.xhrGet(getArgs);
+ getHandler.addCallback(function(data){
+ var items = self._getItems(data, localRequest);
+ console.log("XmlStore._fetchItems(): length=" + (items ? items.length : 0));
+ if(items && items.length > 0){
+ fetchHandler(items, request);
+ }else{
+ fetchHandler([], request);
+ }
+ });
+ getHandler.addErrback(function(data){
+ errorHandler(data, request);
+ });
+ },
+
+ _getFetchUrl: function(request){
+ // summary:
+ // Generate a URL for fetch
+ // description:
+ // This default implementation generates a query string in the form of
+ // "?name1=value1&name2=value2..." off properties of 'query' object
+ // specified in 'request' and appends it to 'url', if 'sendQuery'
+ // is set to false.
+ // Otherwise, 'url' is returned as is.
+ // Sub-classes may override this method for the custom URL generation.
+ // request:
+ // A request object
+ // returns:
+ // A fetch URL
+ if(!this.sendQuery){
+ return this.url;
+ }
+ var query = request.query;
+ if(!query){
+ return this.url;
+ }
+ if(dojo.isString(query)){
+ return this.url + query;
+ }
+ var queryString = "";
+ for(var name in query){
+ var value = query[name];
+ if(value){
+ if(queryString){
+ queryString += "&";
+ }
+ queryString += (name + "=" + value);
+ }
+ }
+ if(!queryString){
+ return this.url;
+ }
+ //Check to see if the URL already has query params or not.
+ var fullUrl = this.url;
+ if(fullUrl.indexOf("?") < 0){
+ fullUrl += "?";
+ }else{
+ fullUrl += "&";
+ }
+ return fullUrl + queryString;
+ },
+
+ _getItems: function(document, request){
+ // summary:
+ // Fetch items (XML elements) in an XML document based on a request
+ // description:
+ // This default implementation walks through child elements of
+ // the document element to see if all properties of 'query' object
+ // match corresponding attributes of the element (item).
+ // If 'request' is not specified, all child elements are returned.
+ // Sub-classes may override this method for the custom search in
+ // an XML document.
+ // document:
+ // An XML document
+ // request:
+ // A request object
+ // returns:
+ // An array of items
+ var query = null;
+ if(request){
+ query = request.query;
+ }
+ var items = [];
+ var nodes = null;
+
+ if(this.rootItem !== ""){
+ nodes = dojo.query(this.rootItem, document);
+ }else{
+ nodes = document.documentElement.childNodes;
+ }
+
+ var deep = request.queryOptions ? request.queryOptions.deep : false;
+ if(deep){
+ nodes = this._flattenNodes(nodes);
+ }
+ for(var i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ if(node.nodeType != 1 /*ELEMENT_NODE*/){
+ continue;
+ }
+ var item = this._getItem(node);
+ if(query){
+ var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
+ var value;
+ var match = false;
+ var j;
+ var emptyQuery = true;
+
+ //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
+ //same value for each item examined. Much more efficient.
+ var regexpList = {};
+ for(var key in query){
+ value = query[key];
+ if(typeof value === "string"){
+ regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
+ }
+ }
+ for(var attribute in query){
+ emptyQuery = false;
+ var values = this.getValues(item, attribute);
+ for(j = 0; j < values.length; j++){
+ value = values[j];
+ if(value){
+ var queryValue = query[attribute];
+ if((typeof value) === "string" &&
+ (regexpList[attribute])){
+ if((value.match(regexpList[attribute])) !== null){
+ match = true;
+ }else{
+ match = false;
+ }
+ }else if((typeof value) === "object"){
+ if( value.toString &&
+ (regexpList[attribute])){
+ var stringValue = value.toString();
+ if((stringValue.match(regexpList[attribute])) !== null){
+ match = true;
+ }else{
+ match = false;
+ }
+ }else{
+ if(queryValue === "*" || queryValue === value){
+ match = true;
+ }else{
+ match = false;
+ }
+ }
+ }
+ }
+ //One of the multiValue values matched,
+ //so quit looking.
+ if(match){
+ break;
+ }
+ }
+ if(!match){
+ break;
+ }
+ }
+ //Either the query was an empty object {}, which is match all, or
+ //was an actual match.
+ if(emptyQuery || match){
+ items.push(item);
+ }
+ }else{
+ //No query, everything matches.
+ items.push(item);
+ }
+ }
+ dojo.forEach(items,function(item){
+ if(item.element.parentNode){
+ item.element.parentNode.removeChild(item.element); // make it root
+ }
+ },this);
+ return items;
+ },
+
+ _flattenNodes: function(nodes){
+ // Summary:
+ // Function used to flatten a hierarchy of XML nodes into a single list for
+ // querying over. Used when deep = true;
+ var flattened = [];
+ if(nodes){
+ var i;
+ for(i = 0; i < nodes.length; i++){
+ var node = nodes[i];
+ flattened.push(node);
+ if(node.childNodes && node.childNodes.length > 0){
+ flattened = flattened.concat(this._flattenNodes(node.childNodes));
+ }
+ }
+ }
+ return flattened;
+ },
+
+ close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
+ // summary:
+ // See dojo.data.api.Read.close()
+ },
+
+/* dojo.data.api.Write */
+
+ newItem: function(/* object? */ keywordArgs, parentInfo){
+ // summary:
+ // Return a new dojox.data.XmlItem
+ // description:
+ // At least, 'keywordArgs' must contain "tagName" to be used for
+ // the new element.
+ // Other attributes in 'keywordArgs' are set to the new element,
+ // including "text()", but excluding "childNodes".
+ // keywordArgs:
+ // An object containing initial attributes
+ // returns:
+ // An XML element
+ console.log("XmlStore.newItem()");
+ keywordArgs = (keywordArgs || {});
+ var tagName = keywordArgs.tagName;
+ if(!tagName){
+ tagName = this.rootItem;
+ if(tagName === ""){
+ return null;
+ }
+ }
+
+ var document = this._getDocument();
+ var element = document.createElement(tagName);
+ for(var attribute in keywordArgs){
+ var text;
+ if(attribute === "tagName"){
+ continue;
+ }else if(attribute === "text()"){
+ text = document.createTextNode(keywordArgs[attribute]);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(tagName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, keywordArgs[attribute]);
+ }else{
+ var child = document.createElement(attribute);
+ text = document.createTextNode(keywordArgs[attribute]);
+ child.appendChild(text);
+ element.appendChild(child);
+ }
+ }
+ }
+
+ var item = this._getItem(element);
+ this._newItems.push(item);
+
+ var pInfo = null;
+ if(parentInfo && parentInfo.parent && parentInfo.attribute){
+ pInfo = {
+ item: parentInfo.parent,
+ attribute: parentInfo.attribute,
+ oldValue: undefined
+ };
+
+ //See if it is multi-valued or not and handle appropriately
+ //Generally, all attributes are multi-valued for this store
+ //So, we only need to append if there are already values present.
+ var values = this.getValues(parentInfo.parent, parentInfo.attribute);
+ if(values && values.length > 0){
+ var tempValues = values.slice(0, values.length);
+ if(values.length === 1){
+ pInfo.oldValue = values[0];
+ }else{
+ pInfo.oldValue = values.slice(0, values.length);
+ }
+ tempValues.push(item);
+ this.setValues(parentInfo.parent, parentInfo.attribute, tempValues);
+ pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
+ }else{
+ this.setValues(parentInfo.parent, parentInfo.attribute, item);
+ pInfo.newValue = item;
+ }
+ }
+ return item; //object
+ },
+
+ deleteItem: function(/* item */ item){
+ // summary:
+ // Delete an dojox.data.XmlItem (wrapper to a XML element).
+ // item:
+ // An XML element to delete
+ // returns:
+ // True
+ console.log("XmlStore.deleteItem()");
+ var element = item.element;
+ if(element.parentNode){
+ this._backupItem(item);
+ element.parentNode.removeChild(element);
+ return true;
+ }
+ this._forgetItem(item);
+ this._deletedItems.push(item);
+ return true; //boolean
+ },
+
+ setValue: function(/* item */ item, /* attribute || string */ attribute, /* almost anything */ value){
+ // summary:
+ // Set an attribute value
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", nothing is set and false is
+ // returned.
+ // If 'attribute' specifies "childNodes", the value (XML element) is
+ // added to the element.
+ // If 'attribute' specifies "text()", a text node is created with
+ // the value and set it to the element as a child.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the value is set to the XML
+ // attribute.
+ // Otherwise, a text node is created with the value and set it to
+ // the first child element of the tag name specified with 'attribute'.
+ // If the child element does not exist, it is created.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of a child element, An XML attribute name or one of
+ // special names
+ // value:
+ // A attribute value to set
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ var child;
+ var text;
+ if(attribute === "childNodes"){
+ child = value.element;
+ element.appendChild(child);
+ }else if(attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ text = this._getDocument(element).createTextNode(value);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, value);
+ }else{
+ for(var i = 0; i < element.childNodes.length; i++){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ child = node;
+ break;
+ }
+ }
+ var document = this._getDocument(element);
+ if(child){
+ while(child.firstChild){
+ child.removeChild(child.firstChild);
+ }
+ }else{
+ child = document.createElement(attribute);
+ element.appendChild(child);
+ }
+ text = document.createTextNode(value);
+ child.appendChild(text);
+ }
+ }
+ return true; //boolean
+ },
+
+ setValues: function(/* item */ item, /* attribute || string */ attribute, /*array*/ values){
+ // summary:
+ // Set attribute values
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // If 'attribute' specifies "tagName", nothing is set and false is
+ // returned.
+ // If 'attribute' specifies "childNodes", the value (array of XML
+ // elements) is set to the element's childNodes.
+ // If 'attribute' specifies "text()", a text node is created with
+ // the values and set it to the element as a child.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the first value is set to
+ // the XML attribute.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are replaced with new child elements and their
+ // child text nodes of values.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, an XML attribute name or one of
+ // special names
+ // value:
+ // A attribute value to set
+ // notify:
+ // A non-API optional argument, used to indicate if notification API should be called
+ // or not.
+
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ var i;
+ var child;
+ var text;
+ if(attribute === "childNodes"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ for(i = 0; i < values.length; i++){
+ child = values[i].element;
+ element.appendChild(child);
+ }
+ }else if(attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ var value = "";
+ for(i = 0; i < values.length; i++){
+ value += values[i];
+ }
+ text = this._getDocument(element).createTextNode(value);
+ element.appendChild(text);
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.setAttribute(name, values[0]);
+ }else{
+ for(i = element.childNodes.length - 1; i >= 0; i--){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ element.removeChild(node);
+ }
+ }
+ var document = this._getDocument(element);
+ for(i = 0; i < values.length; i++){
+ child = document.createElement(attribute);
+ text = document.createTextNode(values[i]);
+ child.appendChild(text);
+ element.appendChild(child);
+ }
+ }
+ }
+ return true; //boolean
+ },
+
+ unsetAttribute: function(/* item */ item, /* attribute || string */ attribute){
+ // summary:
+ // Remove an attribute
+ // description:
+ // 'item' must be an instance of a dojox.data.XmlItem from the store instance.
+ // 'attribute' can be an XML attribute name of the element or one of
+ // special names described below.
+ // If 'attribute' specifies "tagName", nothing is removed and false is
+ // returned.
+ // If 'attribute' specifies "childNodes" or "text()", all child nodes
+ // are removed.
+ // For generic attributes, if '_attributeMap' is specified,
+ // an actual attribute name is looked up with the tag name of
+ // the element and 'attribute' (concatenated with '.').
+ // Then, if 'attribute' starts with "@", the XML attribute is removed.
+ // Otherwise, child elements of the tag name specified with
+ // 'attribute' are removed.
+ // item:
+ // An XML element that holds the attribute
+ // attribute:
+ // A tag name of child elements, an XML attribute name or one of
+ // special names
+ // returns:
+ // False for "tagName", otherwise true
+ if(attribute === "tagName"){
+ return false; //boolean
+ }
+
+ this._backupItem(item);
+
+ var element = item.element;
+ if(attribute === "childNodes" || attribute === "text()"){
+ while(element.firstChild){
+ element.removeChild(element.firstChild);
+ }
+ }else{
+ attribute = this._getAttribute(element.nodeName, attribute);
+ if(attribute.charAt(0) === '@'){
+ var name = attribute.substring(1);
+ element.removeAttribute(name);
+ }else{
+ for(var i = element.childNodes.length - 1; i >= 0; i--){
+ var node = element.childNodes[i];
+ if( node.nodeType === 1 /*ELEMENT_NODE*/ &&
+ node.nodeName === attribute){
+ element.removeChild(node);
+ }
+ }
+ }
+ }
+ return true; //boolean
+ },
+
+ save: function(/* object */ keywordArgs){
+ // summary:
+ // Save new and/or modified items (XML elements)
+ // description:
+ // 'url' is used to save XML documents for new, modified and/or
+ // deleted XML elements.
+ // keywordArgs:
+ // An object for callbacks
+ if(!keywordArgs){
+ keywordArgs = {};
+ }
+ var i;
+ for(i = 0; i < this._modifiedItems.length; i++){
+ this._saveItem(this._modifiedItems[i], keywordArgs, "PUT");
+ }
+ for(i = 0; i < this._newItems.length; i++){
+ var item = this._newItems[i];
+ if(item.element.parentNode){ // reparented
+ this._newItems.splice(i, 1);
+ i--;
+ continue;
+ }
+ this._saveItem(this._newItems[i], keywordArgs, "POST");
+ }
+ for(i = 0; i < this._deletedItems.length; i++){
+ this._saveItem(this._deletedItems[i], keywordArgs, "DELETE");
+ }
+ },
+
+ revert: function(){
+ // summary:
+ // Invalidate changes (new and/or modified elements)
+ // returns:
+ // True
+ console.log("XmlStore.revert() _newItems=" + this._newItems.length);
+ console.log("XmlStore.revert() _deletedItems=" + this._deletedItems.length);
+ console.log("XmlStore.revert() _modifiedItems=" + this._modifiedItems.length);
+ this._newItems = [];
+ this._restoreItems(this._deletedItems);
+ this._deletedItems = [];
+ this._restoreItems(this._modifiedItems);
+ this._modifiedItems = [];
+ return true; //boolean
+ },
+
+ isDirty: function(/* item? */ item){
+ // summary:
+ // Check whether an item is new, modified or deleted
+ // description:
+ // If 'item' is specified, true is returned if the item is new,
+ // modified or deleted.
+ // Otherwise, true is returned if there are any new, modified
+ // or deleted items.
+ // item:
+ // An item (XML element) to check
+ // returns:
+ // True if an item or items are new, modified or deleted, otherwise
+ // false
+ if(item){
+ var element = this._getRootElement(item.element);
+ return (this._getItemIndex(this._newItems, element) >= 0 ||
+ this._getItemIndex(this._deletedItems, element) >= 0 ||
+ this._getItemIndex(this._modifiedItems, element) >= 0); //boolean
+ }else{
+ return (this._newItems.length > 0 ||
+ this._deletedItems.length > 0 ||
+ this._modifiedItems.length > 0); //boolean
+ }
+ },
+
+ _saveItem: function(item, keywordArgs, method){
+ var url;
+ var scope;
+ if(method === "PUT"){
+ url = this._getPutUrl(item);
+ }else if(method === "DELETE"){
+ url = this._getDeleteUrl(item);
+ }else{ // POST
+ url = this._getPostUrl(item);
+ }
+ if(!url){
+ if(keywordArgs.onError){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope, new Error("No URL for saving content: " + this._getPostContent(item)));
+ }
+ return;
+ }
+
+ var saveArgs = {
+ url: url,
+ method: (method || "POST"),
+ contentType: "text/xml",
+ handleAs: "xml"
+ };
+ var saveHandler;
+ if(method === "PUT"){
+ saveArgs.putData = this._getPutContent(item);
+ saveHandler = dojo.rawXhrPut(saveArgs);
+ }else if(method === "DELETE"){
+ saveHandler = dojo.xhrDelete(saveArgs);
+ }else{ // POST
+ saveArgs.postData = this._getPostContent(item);
+ saveHandler = dojo.rawXhrPost(saveArgs);
+ }
+ scope = (keywordArgs.scope || dojo.global);
+ var self = this;
+ saveHandler.addCallback(function(data){
+ self._forgetItem(item);
+ if(keywordArgs.onComplete){
+ keywordArgs.onComplete.call(scope);
+ }
+ });
+ saveHandler.addErrback(function(error){
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, error);
+ }
+ });
+ },
+
+ _getPostUrl: function(item){
+ // summary:
+ // Generate a URL for post
+ // description:
+ // This default implementation just returns 'url'.
+ // Sub-classes may override this method for the custom URL.
+ // item:
+ // An item to save
+ // returns:
+ // A post URL
+ return this.url; //string
+ },
+
+ _getPutUrl: function(item){
+ // summary:
+ // Generate a URL for put
+ // description:
+ // This default implementation just returns 'url'.
+ // Sub-classes may override this method for the custom URL.
+ // item:
+ // An item to save
+ // returns:
+ // A put URL
+ return this.url; //string
+ },
+
+ _getDeleteUrl: function(item){
+ // summary:
+ // Generate a URL for delete
+ // description:
+ // This default implementation returns 'url' with 'keyAttribute'
+ // as a query string.
+ // Sub-classes may override this method for the custom URL based on
+ // changes (new, deleted, or modified).
+ // item:
+ // An item to delete
+ // returns:
+ // A delete URL
+ var url = this.url;
+ if(item && this.keyAttribute !== ""){
+ var value = this.getValue(item, this.keyAttribute);
+ if(value){
+ var key = this.keyAttribute.charAt(0) ==='@' ? this.keyAttribute.substring(1): this.keyAttribute;
+ url += url.indexOf('?') < 0 ? '?' : '&';
+ url += key + '=' + value;
+ }
+ }
+ return url; //string
+ },
+
+ _getPostContent: function(item){
+ // summary:
+ // Generate a content to post
+ // description:
+ // This default implementation generates an XML document for one
+ // (the first only) new or modified element.
+ // Sub-classes may override this method for the custom post content
+ // generation.
+ // item:
+ // An item to save
+ // returns:
+ // A post content
+ var element = item.element;
+ var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?
+ return declaration + dojox.xml.parser.innerXML(element); //XML string
+ },
+
+ _getPutContent: function(item){
+ // summary:
+ // Generate a content to put
+ // description:
+ // This default implementation generates an XML document for one
+ // (the first only) new or modified element.
+ // Sub-classes may override this method for the custom put content
+ // generation.
+ // item:
+ // An item to save
+ // returns:
+ // A post content
+ var element = item.element;
+ var declaration = "<?xml version=\"1.0\"?>"; // FIXME: encoding?
+ return declaration + dojox.xml.parser.innerXML(element); //XML string
+ },
+
+/* internal API */
+
+ _getAttribute: function(tagName, attribute){
+ if(this._attributeMap){
+ var key = tagName + "." + attribute;
+ var value = this._attributeMap[key];
+ if(value){
+ attribute = value;
+ }else{ // look for global attribute
+ value = this._attributeMap[attribute];
+ if(value){
+ attribute = value;
+ }
+ }
+ }
+ return attribute; //object
+ },
+
+ _getItem: function(element){
+ try{
+ var q = null;
+ //Avoid function call if possible.
+ if(this.keyAttribute === ""){
+ q = this._getXPath(element);
+ }
+ return new dojox.data.XmlItem(element, this, q); //object
+ }catch (e){
+ console.log(e);
+ }
+ return null;
+ },
+
+ _getItemIndex: function(items, element){
+ for(var i = 0; i < items.length; i++){
+ if(items[i].element === element){
+ return i; //int
+ }
+ }
+ return -1; //int
+ },
+
+ _backupItem: function(item){
+ var element = this._getRootElement(item.element);
+ if( this._getItemIndex(this._newItems, element) >= 0 ||
+ this._getItemIndex(this._modifiedItems, element) >= 0){
+ return; // new or already modified
+ }
+ if(element != item.element){
+ item = this._getItem(element);
+ }
+ item._backup = element.cloneNode(true);
+ this._modifiedItems.push(item);
+ },
+
+ _restoreItems: function(items){
+
+ dojo.forEach(items,function(item){
+ if(item._backup){
+ item.element = item._backup;
+ item._backup = null;
+ }
+ },this);
+ },
+
+ _forgetItem: function(item){
+ var element = item.element;
+ var index = this._getItemIndex(this._newItems, element);
+ if(index >= 0){
+ this._newItems.splice(index, 1);
+ }
+ index = this._getItemIndex(this._deletedItems, element);
+ if(index >= 0){
+ this._deletedItems.splice(index, 1);
+ }
+ index = this._getItemIndex(this._modifiedItems, element);
+ if(index >= 0){
+ this._modifiedItems.splice(index, 1);
+ }
+ },
+
+ _getDocument: function(element){
+ if(element){
+ return element.ownerDocument; //DOMDocument
+ }else if(!this._document){
+ return dojox.xml.parser.parse(); // DOMDocument
+ }
+ return null; //null
+ },
+
+ _getRootElement: function(element){
+ while(element.parentNode){
+ element = element.parentNode;
+ }
+ return element; //DOMElement
+ },
+
+ _getXPath: function(element){
+ // summary:
+ // A function to compute the xpath of a node in a DOM document.
+ // description:
+ // A function to compute the xpath of a node in a DOM document. Used for
+ // Client side query handling and identity.
+ var xpath = null;
+ if(!this.sendQuery){
+ //xpath should be null for any server queries, as we don't have the entire
+ //XML dom to figure it out.
+ var node = element;
+ xpath = "";
+ while(node && node != element.ownerDocument){
+ var pos = 0;
+ var sibling = node;
+ var name = node.nodeName;
+ while(sibling){
+ sibling = sibling.previousSibling;
+ if(sibling && sibling.nodeName === name){
+ pos++;
+ }
+ }
+ var temp = "/" + name + "[" + pos + "]";
+ if(xpath){
+ xpath = temp + xpath;
+ }else{
+ xpath = temp;
+ }
+ node = node.parentNode;
+ }
+ }
+ return xpath; //string
+ },
+
+ /*************************************
+ * Dojo.data Identity implementation *
+ *************************************/
+ getIdentity: function(/* item */ item){
+ // summary:
+ // Returns a unique identifier for an item.
+ // item:
+ // The XML Item from the store from which to obtain its identifier.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.XmlStore: Object supplied to getIdentity is not an item");
+ }else{
+ var id = null;
+ if(this.sendQuery && this.keyAttribute !== ""){
+ id = this.getValue(item, this.keyAttribute).toString();
+ }else if(!this.serverQuery){
+ if(this.keyAttribute !== ""){
+ id = this.getValue(item,this.keyAttribute).toString();
+ }else{
+ //No specified identity, so return the dojo.query/xpath
+ //for the node as fallback.
+ id = item.q;
+ }
+ }
+ return id; //String.
+ }
+ },
+
+ getIdentityAttributes: function(/* item */ item){
+ // summary:
+ // Returns an array of attribute names that are used to generate the identity.
+ // description:
+ // For XmlStore, if sendQuery is false and no keyAttribute was set, then this function
+ // returns null, as xpath is used for the identity, which is not a public attribute of
+ // the item. If sendQuery is true and keyAttribute is set, then this function
+ // returns an array of one attribute name: keyAttribute. This means the server side
+ // implementation must apply a keyAttribute to a returned node that always allows
+ // it to be looked up again.
+ // item:
+ // The item from the store from which to obtain the array of public attributes that
+ // compose the identifier, if any.
+ if(!this.isItem(item)){
+ throw new Error("dojox.data.XmlStore: Object supplied to getIdentity is not an item");
+ }else{
+ if(this.keyAttribute !== ""){
+ return [this.keyAttribute]; //array
+ }else{
+ //Otherwise it's either using xpath (not an attribute), or the remote store
+ //doesn't support identity.
+ return null; //null
+ }
+ }
+ },
+
+
+ fetchItemByIdentity: function(/* object */ keywordArgs){
+ // summary:
+ // See dojo.data.api.Identity.fetchItemByIdentity(keywordArgs)
+ var handleDocument = null;
+ var scope = null;
+ var self = this;
+ var url = null;
+ var getArgs = null;
+ var getHandler = null;
+
+ if(!self.sendQuery){
+ handleDocument = function(data){
+ if(data){
+ if(self.keyAttribute !== ""){
+ //We have a key attribute specified. So ... we can process the items and locate the item
+ //that contains a matching key attribute. Its identity, as it were.
+ var request = {};
+ request.query={};
+ request.query[self.keyAttribute] = keywordArgs.identity;
+ request.queryOptions = {deep: true};
+ var items = self._getItems(data,request);
+ scope = keywordArgs.scope || dojo.global;
+ if(items.length === 1){
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, items[0]);
+ }
+ }else if(items.length === 0){
+ if(keywordArgs.onItem){
+ keywordArgs.onItem.call(scope, null);
+ }
+ }else{
+ if(keywordArgs.onError){
+ keywordArgs.onError.call(scope, new Error("Items array size for identity lookup greater than 1, invalid keyAttribute."));
+ }
+ }
+ }else{
+ //Since dojo.query doesn't really support the functions needed
+ //to do child node selection on IE well and since xpath support
+ //is flakey across browsers, it's simpler to implement a
+ //pseudo-xpath parser here.
+ var qArgs = keywordArgs.identity.split("/");
+ var i;
+ var node = data;
+ for(i = 0; i < qArgs.length; i++){
+ if(qArgs[i] && qArgs[i] !== ""){
+ var section = qArgs[i];
+ section = section.substring(0,section.length - 1);
+ var vals = section.split("[");
+ var tag = vals[0];
+ var index = parseInt(vals[1], 10);
+ var pos = 0;
+ if(node){
+ var cNodes = node.childNodes;
+ if(cNodes){
+ var j;
+ var foundNode = null;
+ for(j = 0; j < cNodes.length; j++){
+ var pNode = cNodes[j];
+ if(pNode.nodeName === tag){
+ if(pos < index){
+ pos++;
+ }else{
+ foundNode = pNode;
+ break;
+ }
+ }
+ }
+ if(foundNode){
+ node = foundNode;
+ }else{
+ node = null;
+ }
+ }else{
+ node = null;
+ }
+ }else{
+ break;
+ }
+ }
+ }
+ //Return what we found, if any.
+ var item = null;
+ if(node){
+ item = self._getItem(node);
+ if(item.element.parentNode){
+ item.element.parentNode.removeChild(item.element);
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ }
+ }
+ };
+ url = this._getFetchUrl(null);
+ getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ getHandler = dojo.xhrGet(getArgs);
+
+ //Add in the callbacks for completion of data load.
+ getHandler.addCallback(handleDocument);
+ if(keywordArgs.onError){
+ getHandler.addErrback(function(error){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, error);
+ });
+ }
+ }else{
+ //Server side querying, so need to pass the keyAttribute back to the server and let it return
+ //what it will. It SHOULD be only one item.
+ if(self.keyAttribute !== ""){
+ var request = {query:{}};
+ request.query[self.keyAttribute] = keywordArgs.identity;
+ url = this._getFetchUrl(request);
+ handleDocument = function(data){
+ var item = null;
+ if(data){
+ var items = self._getItems(data, {});
+ if(items.length === 1){
+ item = items[0];
+ }else{
+ if(keywordArgs.onError){
+ var scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(scope, new Error("More than one item was returned from the server for the denoted identity"));
+ }
+ }
+ }
+ if(keywordArgs.onItem){
+ scope = keywordArgs.scope || dojo.global;
+ keywordArgs.onItem.call(scope, item);
+ }
+ };
+
+ getArgs = {
+ url: url,
+ handleAs: "xml",
+ preventCache: self.urlPreventCache
+ };
+ getHandler = dojo.xhrGet(getArgs);
+
+ //Add in the callbacks for completion of data load.
+ getHandler.addCallback(handleDocument);
+ if(keywordArgs.onError){
+ getHandler.addErrback(function(error){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, error);
+ });
+ }
+ }else{
+ if(keywordArgs.onError){
+ var s = keywordArgs.scope || dojo.global;
+ keywordArgs.onError.call(s, new Error("XmlStore is not told that the server to provides identity support. No keyAttribute specified."));
+ }
+ }
+ }
+ }
+});
+
+dojo.declare("dojox.data.XmlItem", null, {
+ constructor: function(element, store, query){
+ // summary:
+ // Initialize with an XML element
+ // element:
+ // An XML element
+ // store:
+ // The containing store, if any.
+ // query:
+ // The query to use to look up a specific element.
+ // Usually an XPath or dojo.query statement.
+ this.element = element;
+ this.store = store;
+ this.q = query;
+ },
+ // summary:
+ // A data item of 'XmlStore'
+ // description:
+ // This class represents an item of 'XmlStore' holding an XML element.
+ // 'element'
+ // element:
+ // An XML element
+ toString: function(){
+ // summary:
+ // Return a value of the first text child of the element
+ // returns:
+ // a value of the first text child of the element
+ var str = "";
+ if(this.element){
+ for(var i = 0; i < this.element.childNodes.length; i++){
+ var node = this.element.childNodes[i];
+ if(node.nodeType === 3 || node.nodeType === 4){
+ str += node.nodeValue;
+ }
+ }
+ }
+ return str; //String
+ }
+});
+dojo.extend(dojox.data.XmlStore,dojo.data.util.simpleFetch);
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/css.js b/js/dojo-1.6/dojox/data/css.js
new file mode 100644
index 0000000..eb262ff
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/css.js
@@ -0,0 +1,109 @@
+/*
+ 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["dojox.data.css"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.css"] = true;
+dojo.provide("dojox.data.css");
+
+
+
+dojox.data.css.rules = {};
+
+dojox.data.css.rules.forEach = function(fn,ctx,context){
+ if(context){
+ var _processSS = function(styleSheet){
+ //iterate across rules in the stylesheet
+ dojo.forEach(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(!rule.type || rule.type !== 3){// apply fn to current rule with approp ctx. rule is arg (all browsers)
+ var href = "";
+ if(styleSheet && styleSheet.href){
+ href = styleSheet.href;
+ }
+ fn.call(ctx?ctx:this,rule, styleSheet, href);
+ }
+ });
+ //process any child stylesheets
+ };
+ dojo.forEach(context,_processSS);
+ }
+};
+dojox.data.css.findStyleSheets = function(sheets){
+ // Takes an array of stylesheet paths and finds the currently loaded StyleSheet objects matching
+ // those names
+ var sheetObjects = [];
+ var _processSS = function(styleSheet){
+ var s = dojox.data.css.findStyleSheet(styleSheet);
+ if(s){
+ dojo.forEach(s, function(sheet){
+ if(dojo.indexOf(sheetObjects, sheet) === -1){
+ sheetObjects.push(sheet);
+ }
+ });
+ }
+ };
+ dojo.forEach(sheets, _processSS);
+ return sheetObjects;
+};
+dojox.data.css.findStyleSheet = function(sheet){
+ // Takes a stylesheet path and finds the currently loaded StyleSheet objects matching
+ // those names (and it's parent(s), if it is imported from another)
+ var sheetObjects = [];
+ if(sheet.charAt(0) === '.'){
+ sheet = sheet.substring(1);
+ }
+ var _processSS = function(styleSheet){
+ if(styleSheet.href && styleSheet.href.match(sheet)){
+ sheetObjects.push(styleSheet);
+ return true;
+ }
+ if(styleSheet.imports){
+ return dojo.some(styleSheet.imports, function(importedSS){ //IE stylesheet has imports[] containing @import'ed rules
+ //console.debug("Processing IE @import rule",importedSS);
+ return _processSS(importedSS);
+ });
+ }
+ //iterate across rules in the stylesheet
+ return dojo.some(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(rule.type && rule.type === 3 && _processSS(rule.styleSheet)){// CSSImportRule (firefox)
+ //sheetObjects.push(styleSheet);
+ return true;
+ }
+ return false;
+ });
+ };
+ dojo.some(document.styleSheets, _processSS);
+ return sheetObjects;
+};
+dojox.data.css.determineContext = function(initialStylesheets){
+ // Takes an array of stylesheet paths and returns an array of all stylesheets that fall in the
+ // given context. If no paths are given, all stylesheets are returned.
+ var ret = [];
+ if(initialStylesheets && initialStylesheets.length > 0){
+ initialStylesheets = dojox.data.css.findStyleSheets(initialStylesheets);
+ }else{
+ initialStylesheets = document.styleSheets;
+ }
+ var _processSS = function(styleSheet){
+ ret.push(styleSheet);
+ if(styleSheet.imports){
+ dojo.forEach(styleSheet.imports, function(importedSS){ //IE stylesheet has imports[] containing @import'ed rules
+ //console.debug("Processing IE @import rule",importedSS);
+ _processSS(importedSS);
+ });
+ }
+ //iterate across rules in the stylesheet
+ dojo.forEach(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(rule.type && rule.type === 3){// CSSImportRule (firefox)
+ _processSS(rule.styleSheet);
+ }
+ });
+ };
+ dojo.forEach(initialStylesheets,_processSS);
+ return ret;
+};
+
+}
diff --git a/js/dojo-1.6/dojox/data/css.xd.js b/js/dojo-1.6/dojox/data/css.xd.js
new file mode 100644
index 0000000..1c94fdb
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/css.xd.js
@@ -0,0 +1,113 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.css"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.css"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.css"] = true;
+dojo.provide("dojox.data.css");
+
+
+
+dojox.data.css.rules = {};
+
+dojox.data.css.rules.forEach = function(fn,ctx,context){
+ if(context){
+ var _processSS = function(styleSheet){
+ //iterate across rules in the stylesheet
+ dojo.forEach(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(!rule.type || rule.type !== 3){// apply fn to current rule with approp ctx. rule is arg (all browsers)
+ var href = "";
+ if(styleSheet && styleSheet.href){
+ href = styleSheet.href;
+ }
+ fn.call(ctx?ctx:this,rule, styleSheet, href);
+ }
+ });
+ //process any child stylesheets
+ };
+ dojo.forEach(context,_processSS);
+ }
+};
+dojox.data.css.findStyleSheets = function(sheets){
+ // Takes an array of stylesheet paths and finds the currently loaded StyleSheet objects matching
+ // those names
+ var sheetObjects = [];
+ var _processSS = function(styleSheet){
+ var s = dojox.data.css.findStyleSheet(styleSheet);
+ if(s){
+ dojo.forEach(s, function(sheet){
+ if(dojo.indexOf(sheetObjects, sheet) === -1){
+ sheetObjects.push(sheet);
+ }
+ });
+ }
+ };
+ dojo.forEach(sheets, _processSS);
+ return sheetObjects;
+};
+dojox.data.css.findStyleSheet = function(sheet){
+ // Takes a stylesheet path and finds the currently loaded StyleSheet objects matching
+ // those names (and it's parent(s), if it is imported from another)
+ var sheetObjects = [];
+ if(sheet.charAt(0) === '.'){
+ sheet = sheet.substring(1);
+ }
+ var _processSS = function(styleSheet){
+ if(styleSheet.href && styleSheet.href.match(sheet)){
+ sheetObjects.push(styleSheet);
+ return true;
+ }
+ if(styleSheet.imports){
+ return dojo.some(styleSheet.imports, function(importedSS){ //IE stylesheet has imports[] containing @import'ed rules
+ //console.debug("Processing IE @import rule",importedSS);
+ return _processSS(importedSS);
+ });
+ }
+ //iterate across rules in the stylesheet
+ return dojo.some(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(rule.type && rule.type === 3 && _processSS(rule.styleSheet)){// CSSImportRule (firefox)
+ //sheetObjects.push(styleSheet);
+ return true;
+ }
+ return false;
+ });
+ };
+ dojo.some(document.styleSheets, _processSS);
+ return sheetObjects;
+};
+dojox.data.css.determineContext = function(initialStylesheets){
+ // Takes an array of stylesheet paths and returns an array of all stylesheets that fall in the
+ // given context. If no paths are given, all stylesheets are returned.
+ var ret = [];
+ if(initialStylesheets && initialStylesheets.length > 0){
+ initialStylesheets = dojox.data.css.findStyleSheets(initialStylesheets);
+ }else{
+ initialStylesheets = document.styleSheets;
+ }
+ var _processSS = function(styleSheet){
+ ret.push(styleSheet);
+ if(styleSheet.imports){
+ dojo.forEach(styleSheet.imports, function(importedSS){ //IE stylesheet has imports[] containing @import'ed rules
+ //console.debug("Processing IE @import rule",importedSS);
+ _processSS(importedSS);
+ });
+ }
+ //iterate across rules in the stylesheet
+ dojo.forEach(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){
+ if(rule.type && rule.type === 3){// CSSImportRule (firefox)
+ _processSS(rule.styleSheet);
+ }
+ });
+ };
+ dojo.forEach(initialStylesheets,_processSS);
+ return ret;
+};
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/dom.js b/js/dojo-1.6/dojox/data/dom.js
new file mode 100644
index 0000000..20089c4
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/dom.js
@@ -0,0 +1,107 @@
+/*
+ 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["dojox.data.dom"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.dom"] = true;
+dojo.provide("dojox.data.dom");
+dojo.require("dojox.xml.parser");
+
+
+
+//DOM type to int value for reference.
+//Ints make for more compact code than full constant names.
+//ELEMENT_NODE = 1;
+//ATTRIBUTE_NODE = 2;
+//TEXT_NODE = 3;
+//CDATA_SECTION_NODE = 4;
+//ENTITY_REFERENCE_NODE = 5;
+//ENTITY_NODE = 6;
+//PROCESSING_INSTRUCTION_NODE = 7;
+//COMMENT_NODE = 8;
+//DOCUMENT_NODE = 9;
+//DOCUMENT_TYPE_NODE = 10;
+//DOCUMENT_FRAGMENT_NODE = 11;
+//NOTATION_NODE = 12;
+
+//This file contains internal/helper APIs as holders for people who used them. They have been migrated to
+//a better project, dojox.xml and experimental has been removed there. Please update usage to the new package.
+dojo.deprecated("dojox.data.dom", "Use dojox.xml.parser instead.", "2.0");
+
+dojox.data.dom.createDocument = function(/*string?*/ str, /*string?*/ mimetype){
+ // summary:
+ // cross-browser implementation of creating an XML document object.
+ //
+ // str:
+ // Optional text to create the document from. If not provided, an empty XML document will be created.
+ // If str is empty string "", then a new empty document will be created.
+ // mimetype:
+ // Optional mimetype of the text. Typically, this is text/xml. Will be defaulted to text/xml if not provided.
+ dojo.deprecated("dojox.data.dom.createDocument()", "Use dojox.xml.parser.parse() instead.", "2.0");
+ try{
+ return dojox.xml.parser.parse(str,mimetype); //DOMDocument.
+ }catch(e){
+ /*Squeltch errors like the old parser did.*/
+ return null;
+ }
+};
+
+dojox.data.dom.textContent = function(/*Node*/node, /*string?*/text){
+ // summary:
+ // Implementation of the DOM Level 3 attribute; scan node for text
+ // description:
+ // Implementation of the DOM Level 3 attribute; scan node for text
+ // This function can also update the text of a node by replacing all child
+ // content of the node.
+ // node:
+ // The node to get the text off of or set the text on.
+ // text:
+ // Optional argument of the text to apply to the node.
+ dojo.deprecated("dojox.data.dom.textContent()", "Use dojox.xml.parser.textContent() instead.", "2.0");
+ if(arguments.length> 1){
+ return dojox.xml.parser.textContent(node, text); //string
+ }else{
+ return dojox.xml.parser.textContent(node); //string
+ }
+};
+
+dojox.data.dom.replaceChildren = function(/*Element*/node, /*Node || array*/ newChildren){
+ // summary:
+ // Removes all children of node and appends newChild. All the existing
+ // children will be destroyed.
+ // description:
+ // Removes all children of node and appends newChild. All the existing
+ // children will be destroyed.
+ // node:
+ // The node to modify the children on
+ // newChildren:
+ // The children to add to the node. It can either be a single Node or an
+ // array of Nodes.
+ dojo.deprecated("dojox.data.dom.replaceChildren()", "Use dojox.xml.parser.replaceChildren() instead.", "2.0");
+ dojox.xml.parser.replaceChildren(node, newChildren);
+};
+
+dojox.data.dom.removeChildren = function(/*Element*/node){
+ // summary:
+ // removes all children from node and returns the count of children removed.
+ // The children nodes are not destroyed. Be sure to call dojo._destroyElement on them
+ // after they are not used anymore.
+ // node:
+ // The node to remove all the children from.
+ dojo.deprecated("dojox.data.dom.removeChildren()", "Use dojox.xml.parser.removeChildren() instead.", "2.0");
+ return dojox.xml.parser.removeChildren(node); //int
+};
+
+dojox.data.dom.innerXML = function(/*Node*/node){
+ // summary:
+ // Implementation of MS's innerXML function.
+ // node:
+ // The node from which to generate the XML text representation.
+ dojo.deprecated("dojox.data.dom.innerXML()", "Use dojox.xml.parser.innerXML() instead.", "2.0");
+ return dojox.xml.parser.innerXML(node); //string||null
+};
+
+}
diff --git a/js/dojo-1.6/dojox/data/dom.xd.js b/js/dojo-1.6/dojox/data/dom.xd.js
new file mode 100644
index 0000000..d2bd8cc
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/dom.xd.js
@@ -0,0 +1,112 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.dom"],
+["require", "dojox.xml.parser"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.dom"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.dom"] = true;
+dojo.provide("dojox.data.dom");
+dojo.require("dojox.xml.parser");
+
+
+
+//DOM type to int value for reference.
+//Ints make for more compact code than full constant names.
+//ELEMENT_NODE = 1;
+//ATTRIBUTE_NODE = 2;
+//TEXT_NODE = 3;
+//CDATA_SECTION_NODE = 4;
+//ENTITY_REFERENCE_NODE = 5;
+//ENTITY_NODE = 6;
+//PROCESSING_INSTRUCTION_NODE = 7;
+//COMMENT_NODE = 8;
+//DOCUMENT_NODE = 9;
+//DOCUMENT_TYPE_NODE = 10;
+//DOCUMENT_FRAGMENT_NODE = 11;
+//NOTATION_NODE = 12;
+
+//This file contains internal/helper APIs as holders for people who used them. They have been migrated to
+//a better project, dojox.xml and experimental has been removed there. Please update usage to the new package.
+dojo.deprecated("dojox.data.dom", "Use dojox.xml.parser instead.", "2.0");
+
+dojox.data.dom.createDocument = function(/*string?*/ str, /*string?*/ mimetype){
+ // summary:
+ // cross-browser implementation of creating an XML document object.
+ //
+ // str:
+ // Optional text to create the document from. If not provided, an empty XML document will be created.
+ // If str is empty string "", then a new empty document will be created.
+ // mimetype:
+ // Optional mimetype of the text. Typically, this is text/xml. Will be defaulted to text/xml if not provided.
+ dojo.deprecated("dojox.data.dom.createDocument()", "Use dojox.xml.parser.parse() instead.", "2.0");
+ try{
+ return dojox.xml.parser.parse(str,mimetype); //DOMDocument.
+ }catch(e){
+ /*Squeltch errors like the old parser did.*/
+ return null;
+ }
+};
+
+dojox.data.dom.textContent = function(/*Node*/node, /*string?*/text){
+ // summary:
+ // Implementation of the DOM Level 3 attribute; scan node for text
+ // description:
+ // Implementation of the DOM Level 3 attribute; scan node for text
+ // This function can also update the text of a node by replacing all child
+ // content of the node.
+ // node:
+ // The node to get the text off of or set the text on.
+ // text:
+ // Optional argument of the text to apply to the node.
+ dojo.deprecated("dojox.data.dom.textContent()", "Use dojox.xml.parser.textContent() instead.", "2.0");
+ if(arguments.length> 1){
+ return dojox.xml.parser.textContent(node, text); //string
+ }else{
+ return dojox.xml.parser.textContent(node); //string
+ }
+};
+
+dojox.data.dom.replaceChildren = function(/*Element*/node, /*Node || array*/ newChildren){
+ // summary:
+ // Removes all children of node and appends newChild. All the existing
+ // children will be destroyed.
+ // description:
+ // Removes all children of node and appends newChild. All the existing
+ // children will be destroyed.
+ // node:
+ // The node to modify the children on
+ // newChildren:
+ // The children to add to the node. It can either be a single Node or an
+ // array of Nodes.
+ dojo.deprecated("dojox.data.dom.replaceChildren()", "Use dojox.xml.parser.replaceChildren() instead.", "2.0");
+ dojox.xml.parser.replaceChildren(node, newChildren);
+};
+
+dojox.data.dom.removeChildren = function(/*Element*/node){
+ // summary:
+ // removes all children from node and returns the count of children removed.
+ // The children nodes are not destroyed. Be sure to call dojo._destroyElement on them
+ // after they are not used anymore.
+ // node:
+ // The node to remove all the children from.
+ dojo.deprecated("dojox.data.dom.removeChildren()", "Use dojox.xml.parser.removeChildren() instead.", "2.0");
+ return dojox.xml.parser.removeChildren(node); //int
+};
+
+dojox.data.dom.innerXML = function(/*Node*/node){
+ // summary:
+ // Implementation of MS's innerXML function.
+ // node:
+ // The node from which to generate the XML text representation.
+ dojo.deprecated("dojox.data.dom.innerXML()", "Use dojox.xml.parser.innerXML() instead.", "2.0");
+ return dojox.xml.parser.innerXML(node); //string||null
+};
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/restListener.js b/js/dojo-1.6/dojox/data/restListener.js
new file mode 100644
index 0000000..1c5d340
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/restListener.js
@@ -0,0 +1,59 @@
+/*
+ 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["dojox.data.restListener"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.restListener"] = true;
+dojo.provide("dojox.data.restListener");
+
+dojox.data.restListener = function(message){
+ // summary:
+ // this function can be used to receive REST notifications, from Comet or from another frame
+ // description:
+ // Example:
+ // | dojo.connect(window,"onMessage",null,function(event) {
+ // | var data = dojo.fromJson(event.data);
+ // | dojox.restListener(data);
+ // | });
+ var channel = message.channel;
+ var jr = dojox.rpc.JsonRest;
+ var service = jr.getServiceAndId(channel).service;
+ var result = dojox.json.ref.resolveJson(message.result, {
+ defaultId: message.event == 'put' && channel,
+ index: dojox.rpc.Rest._index,
+ idPrefix: service.servicePath.replace(/[^\/]*$/,''),
+ idAttribute: jr.getIdAttribute(service),
+ schemas: jr.schemas,
+ loader: jr._loader,
+ assignAbsoluteIds: true
+ });
+ var target = dojox.rpc.Rest._index && dojox.rpc.Rest._index[channel];
+ var onEvent = 'on' + message.event.toLowerCase();
+ var store = service && service._store;
+ if(target){
+ if(target[onEvent]){
+ target[onEvent](result); // call the REST handler if available
+ return;
+ }
+ }
+ // this is how we respond to different events
+ if(store){
+ switch(onEvent){
+ case 'onpost':
+ store.onNew(result); // call onNew for the store;
+ break;
+ case 'ondelete':
+ store.onDelete(target);
+ break;
+ // put is handled by JsonReferencing
+ //TODO: we may want to bring the JsonReferencing capability into here...
+ // that is really tricky though because JsonReferencing handles sub object,
+ // it would be expensive to do full object graph searches from here
+ }
+ }
+};
+
+}
diff --git a/js/dojo-1.6/dojox/data/restListener.xd.js b/js/dojo-1.6/dojox/data/restListener.xd.js
new file mode 100644
index 0000000..e7c9f6e
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/restListener.xd.js
@@ -0,0 +1,63 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.restListener"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.restListener"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.restListener"] = true;
+dojo.provide("dojox.data.restListener");
+
+dojox.data.restListener = function(message){
+ // summary:
+ // this function can be used to receive REST notifications, from Comet or from another frame
+ // description:
+ // Example:
+ // | dojo.connect(window,"onMessage",null,function(event) {
+ // | var data = dojo.fromJson(event.data);
+ // | dojox.restListener(data);
+ // | });
+ var channel = message.channel;
+ var jr = dojox.rpc.JsonRest;
+ var service = jr.getServiceAndId(channel).service;
+ var result = dojox.json.ref.resolveJson(message.result, {
+ defaultId: message.event == 'put' && channel,
+ index: dojox.rpc.Rest._index,
+ idPrefix: service.servicePath.replace(/[^\/]*$/,''),
+ idAttribute: jr.getIdAttribute(service),
+ schemas: jr.schemas,
+ loader: jr._loader,
+ assignAbsoluteIds: true
+ });
+ var target = dojox.rpc.Rest._index && dojox.rpc.Rest._index[channel];
+ var onEvent = 'on' + message.event.toLowerCase();
+ var store = service && service._store;
+ if(target){
+ if(target[onEvent]){
+ target[onEvent](result); // call the REST handler if available
+ return;
+ }
+ }
+ // this is how we respond to different events
+ if(store){
+ switch(onEvent){
+ case 'onpost':
+ store.onNew(result); // call onNew for the store;
+ break;
+ case 'ondelete':
+ store.onDelete(target);
+ break;
+ // put is handled by JsonReferencing
+ //TODO: we may want to bring the JsonReferencing capability into here...
+ // that is really tricky though because JsonReferencing handles sub object,
+ // it would be expensive to do full object graph searches from here
+ }
+ }
+};
+
+}
+
+}};});
diff --git a/js/dojo-1.6/dojox/data/s3/README b/js/dojo-1.6/dojox/data/s3/README
new file mode 100644
index 0000000..cfa2347
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/s3/README
@@ -0,0 +1,41 @@
+Using Amazon S3 with Dojo has the following prerequisites:
+ * You must be signed up to use Amazon S3. You can sign up for Amazon S3 at http://aws.amazon.com/s3.
+ * Use the provided proxy (/dojox/rpc/s3/proxy.php) with PHP 5.
+ * proxy.php requires the following modules:
+ o Crypt_HMAC
+ o HTTP_Request
+
+
+To use S3 from Dojo, you need a proxy. You can use the provided proxy example file by renaming
+proxy.example-php to proxy.php and then you must enter your Amazon access key and secret access key
+into the proxy.php file on line 3 and 4:
+
+$accessKey = "access key";
+$secretAccessKey = "secret access key";
+
+You then use the Dojo RPC service with the "PROXIED-PATH" envelope:
+
+dojo.require("dojox.rpc.Service");
+dojo.require("dojox.rpc.ProxiedPath");
+var s3Buckets = new dojox.rpc.Service({
+ target:"http://s3.amazonaws.com/",
+ proxyUrl:"../s3/proxy.php", // the path to the proxy
+ transport:"REST",
+ envelope:"PROXIED-PATH",
+ contentType:"application/json",
+ services:{
+ myBucket:{
+ target:"myBucket",
+ parameters:[{type:"string"}]
+ }
+ }
+});
+
+
+To use the S3 as a Dojo data store you can use the S3JsonRestStore module. First setup an RPC service
+as shown above and then pass the RPC service to the S3JsonRestStore:
+
+dojo.require("dojox.data.S3JsonRestStore");
+s3Store = new dojox.data.S3JsonRestStore({service:s3Buckets.myBucket}); // and create a store for it
+
+You can then use the s3Store as a normal Read/Write Dojo Data store.
diff --git a/js/dojo-1.6/dojox/data/s3/proxy.example-php b/js/dojo-1.6/dojox/data/s3/proxy.example-php
new file mode 100644
index 0000000..e5b57e1
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/s3/proxy.example-php
@@ -0,0 +1,74 @@
+<?php
+// enter your Amazon S3 secret key and access key here:
+$accessKey = "access key";
+$secretAccessKey = "secret access key";
+
+
+
+$TARGET_WS = "http://s3.amazonaws.com";
+
+ob_start();
+
+require_once 'Crypt/HMAC.php';
+require_once 'HTTP/Request.php';
+
+$method = $_SERVER["REQUEST_METHOD"];
+if ($method == "PUT") {
+ $contentType = $_SERVER['CONTENT_TYPE'];
+}
+else {
+ $contentType ='';
+}
+$resource = str_replace($TARGET_WS, '', $_REQUEST['url']);
+$queryIndex = strpos($resource,'?'); // remove the query string
+if ($queryIndex) {
+ $resource = substr($resource,0,$queryIndex);
+}
+
+if (substr($resource,strlen($resource)-1,strlen($resource)) == '/') {
+ // remove the last slash
+ $resource = substr($resource,0,strlen($resource)-1);
+}
+$content = file_get_contents('php://input');
+
+$httpDate = gmdate("D, d M Y H:i:s T");
+$acl = "private";
+$stringToSign = "$method\n\n$contentType\n$httpDate\nx-amz-acl:$acl\n$resource";
+$hashObj =& new Crypt_HMAC($secretAccessKey, "sha1");
+$signature = hexTob64($hashObj->hash($stringToSign));
+
+$req =& new HTTP_Request($TARGET_WS . $resource);
+$req->setMethod($method);
+$req->addHeader("content-type", $contentType);
+$req->addHeader("Date", $httpDate);
+$req->addHeader("x-amz-acl", $acl);
+$req->addHeader("Authorization", "AWS " . $accessKey . ":" . $signature);
+if ($content != "") {
+ $req->setBody($content);
+}
+
+$req->sendRequest();
+
+$contentType = $req->getResponseHeader("content-type");
+header("content-type: $contentType");
+header('HTTP/1.1 ' . $req->getResponseCode() . ' Ok');
+
+ob_end_flush();
+
+$content = $req->getResponseBody();
+if ($content) {
+ print($content);
+}
+else {
+ print("\"success\"");
+}
+
+function hexTob64($str) {
+ $raw = '';
+ for ($i=0; $i < strlen($str); $i+=2) {
+ $raw .= chr(hexdec(substr($str, $i, 2)));
+ }
+ return base64_encode($raw);
+}
+
+?> \ No newline at end of file
diff --git a/js/dojo-1.6/dojox/data/util/JsonQuery.js b/js/dojo-1.6/dojox/data/util/JsonQuery.js
new file mode 100644
index 0000000..911015b
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/util/JsonQuery.js
@@ -0,0 +1,108 @@
+/*
+ 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["dojox.data.util.JsonQuery"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.util.JsonQuery"] = true;
+dojo.provide("dojox.data.util.JsonQuery");
+
+
+
+// this is a mixin to convert object attribute queries to
+// JSONQuery/JSONPath syntax to be sent to the server.
+dojo.declare("dojox.data.util.JsonQuery", null, {
+ useFullIdInQueries: false,
+ _toJsonQuery: function(args, jsonQueryPagination){
+ var first = true;
+ var self = this;
+ function buildQuery(path, query){
+ var isDataItem = query.__id;
+ if(isDataItem){
+ // it is a reference to a persisted object, need to make it a query by id
+ var newQuery = {};
+ newQuery[self.idAttribute] = self.useFullIdInQueries ? query.__id : query[self.idAttribute];
+ query = newQuery;
+ }
+ for(var i in query){
+ // iterate through each property, adding them to the overall query
+ var value = query[i];
+ var newPath = path + (/^[a-zA-Z_][\w_]*$/.test(i) ? '.' + i : '[' + dojo._escapeString(i) + ']');
+ if(value && typeof value == "object"){
+ buildQuery(newPath, value);
+ }else if(value!="*"){ // full wildcards can be ommitted
+ jsonQuery += (first ? "" : "&") + newPath +
+ ((!isDataItem && typeof value == "string" && args.queryOptions && args.queryOptions.ignoreCase) ? "~" : "=") +
+ (self.simplifiedQuery ? encodeURIComponent(value) : dojo.toJson(value));
+ first = false;
+ }
+ }
+ }
+ // performs conversion of Dojo Data query objects and sort arrays to JSONQuery strings
+ if(args.query && typeof args.query == "object"){
+ // convert Dojo Data query objects to JSONQuery
+ var jsonQuery = "[?(";
+ buildQuery("@", args.query);
+ if(!first){
+ // use ' instead of " for quoting in JSONQuery, and end with ]
+ jsonQuery += ")]";
+ }else{
+ jsonQuery = "";
+ }
+ args.queryStr = jsonQuery.replace(/\\"|"/g,function(t){return t == '"' ? "'" : t;});
+ }else if(!args.query || args.query == '*'){
+ args.query = "";
+ }
+
+ var sort = args.sort;
+ if(sort){
+ // if we have a sort order, add that to the JSONQuery expression
+ args.queryStr = args.queryStr || (typeof args.query == 'string' ? args.query : "");
+ first = true;
+ for(i = 0; i < sort.length; i++){
+ args.queryStr += (first ? '[' : ',') + (sort[i].descending ? '\\' : '/') + "@[" + dojo._escapeString(sort[i].attribute) + "]";
+ first = false;
+ }
+ args.queryStr += ']';
+ }
+ // this is optional because with client side paging JSONQuery doesn't yield the total count
+ if(jsonQueryPagination && (args.start || args.count)){
+ // pagination
+ args.queryStr = (args.queryStr || (typeof args.query == 'string' ? args.query : "")) +
+ '[' + (args.start || '') + ':' + (args.count ? (args.start || 0) + args.count : '') + ']';
+ }
+ if(typeof args.queryStr == 'string'){
+ args.queryStr = args.queryStr.replace(/\\"|"/g,function(t){return t == '"' ? "'" : t;});
+ return args.queryStr;
+ }
+ return args.query;
+ },
+ jsonQueryPagination: true,
+ fetch: function(args){
+ this._toJsonQuery(args, this.jsonQueryPagination);
+ return this.inherited(arguments);
+ },
+ isUpdateable: function(){
+ return true;
+ },
+ matchesQuery: function(item,request){
+ request._jsonQuery = request._jsonQuery || dojox.json.query(this._toJsonQuery(request));
+ return request._jsonQuery([item]).length;
+ },
+ clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
+ request._jsonQuery = request._jsonQuery || dojox.json.query(this._toJsonQuery(request));
+ // we use client side paging function here instead of JSON Query because we must also determine the total count
+ return this.clientSidePaging(request, request._jsonQuery(baseResults));
+ },
+ querySuperSet: function(argsSuper,argsSub){
+ if(!argsSuper.query){
+ return argsSub.query;
+ }
+ return this.inherited(arguments);
+ }
+
+});
+
+}
diff --git a/js/dojo-1.6/dojox/data/util/JsonQuery.xd.js b/js/dojo-1.6/dojox/data/util/JsonQuery.xd.js
new file mode 100644
index 0000000..bd2f20c
--- /dev/null
+++ b/js/dojo-1.6/dojox/data/util/JsonQuery.xd.js
@@ -0,0 +1,112 @@
+/*
+ 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
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.data.util.JsonQuery"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.data.util.JsonQuery"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.data.util.JsonQuery"] = true;
+dojo.provide("dojox.data.util.JsonQuery");
+
+
+
+// this is a mixin to convert object attribute queries to
+// JSONQuery/JSONPath syntax to be sent to the server.
+dojo.declare("dojox.data.util.JsonQuery", null, {
+ useFullIdInQueries: false,
+ _toJsonQuery: function(args, jsonQueryPagination){
+ var first = true;
+ var self = this;
+ function buildQuery(path, query){
+ var isDataItem = query.__id;
+ if(isDataItem){
+ // it is a reference to a persisted object, need to make it a query by id
+ var newQuery = {};
+ newQuery[self.idAttribute] = self.useFullIdInQueries ? query.__id : query[self.idAttribute];
+ query = newQuery;
+ }
+ for(var i in query){
+ // iterate through each property, adding them to the overall query
+ var value = query[i];
+ var newPath = path + (/^[a-zA-Z_][\w_]*$/.test(i) ? '.' + i : '[' + dojo._escapeString(i) + ']');
+ if(value && typeof value == "object"){
+ buildQuery(newPath, value);
+ }else if(value!="*"){ // full wildcards can be ommitted
+ jsonQuery += (first ? "" : "&") + newPath +
+ ((!isDataItem && typeof value == "string" && args.queryOptions && args.queryOptions.ignoreCase) ? "~" : "=") +
+ (self.simplifiedQuery ? encodeURIComponent(value) : dojo.toJson(value));
+ first = false;
+ }
+ }
+ }
+ // performs conversion of Dojo Data query objects and sort arrays to JSONQuery strings
+ if(args.query && typeof args.query == "object"){
+ // convert Dojo Data query objects to JSONQuery
+ var jsonQuery = "[?(";
+ buildQuery("@", args.query);
+ if(!first){
+ // use ' instead of " for quoting in JSONQuery, and end with ]
+ jsonQuery += ")]";
+ }else{
+ jsonQuery = "";
+ }
+ args.queryStr = jsonQuery.replace(/\\"|"/g,function(t){return t == '"' ? "'" : t;});
+ }else if(!args.query || args.query == '*'){
+ args.query = "";
+ }
+
+ var sort = args.sort;
+ if(sort){
+ // if we have a sort order, add that to the JSONQuery expression
+ args.queryStr = args.queryStr || (typeof args.query == 'string' ? args.query : "");
+ first = true;
+ for(i = 0; i < sort.length; i++){
+ args.queryStr += (first ? '[' : ',') + (sort[i].descending ? '\\' : '/') + "@[" + dojo._escapeString(sort[i].attribute) + "]";
+ first = false;
+ }
+ args.queryStr += ']';
+ }
+ // this is optional because with client side paging JSONQuery doesn't yield the total count
+ if(jsonQueryPagination && (args.start || args.count)){
+ // pagination
+ args.queryStr = (args.queryStr || (typeof args.query == 'string' ? args.query : "")) +
+ '[' + (args.start || '') + ':' + (args.count ? (args.start || 0) + args.count : '') + ']';
+ }
+ if(typeof args.queryStr == 'string'){
+ args.queryStr = args.queryStr.replace(/\\"|"/g,function(t){return t == '"' ? "'" : t;});
+ return args.queryStr;
+ }
+ return args.query;
+ },
+ jsonQueryPagination: true,
+ fetch: function(args){
+ this._toJsonQuery(args, this.jsonQueryPagination);
+ return this.inherited(arguments);
+ },
+ isUpdateable: function(){
+ return true;
+ },
+ matchesQuery: function(item,request){
+ request._jsonQuery = request._jsonQuery || dojox.json.query(this._toJsonQuery(request));
+ return request._jsonQuery([item]).length;
+ },
+ clientSideFetch: function(/*Object*/ request,/*Array*/ baseResults){
+ request._jsonQuery = request._jsonQuery || dojox.json.query(this._toJsonQuery(request));
+ // we use client side paging function here instead of JSON Query because we must also determine the total count
+ return this.clientSidePaging(request, request._jsonQuery(baseResults));
+ },
+ querySuperSet: function(argsSuper,argsSub){
+ if(!argsSuper.query){
+ return argsSub.query;
+ }
+ return this.inherited(arguments);
+ }
+
+});
+
+}
+
+}};});