diff options
Diffstat (limited to 'js/dojo-1.7.2/dojox/data')
106 files changed, 18961 insertions, 0 deletions
diff --git a/js/dojo-1.7.2/dojox/data/AndOrReadStore.js b/js/dojo-1.7.2/dojox/data/AndOrReadStore.js new file mode 100644 index 0000000..6c4122d --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/AndOrReadStore.js @@ -0,0 +1,1047 @@ +//>>built +define("dojox/data/AndOrReadStore", ["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/lang", "dojo/data/util/filter", "dojo/data/util/simpleFetch", + "dojo/_base/array", "dojo/date/stamp", "dojo/_base/json", "dojo/_base/window", "dojo/_base/xhr"], + function(kernel, declare, lang, filterUtil, simpleFetch, array, dateStamp, json, winUtil, xhr) { + +var AndOrReadStore = 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 dateStamp.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 = filterUtil.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 array.some(this.getValues(item, attribute), function(possibleValue){ + if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){ + if(possibleValue.toString().match(regexp)){ + return true; // Boolean + } + } else if(value === possibleValue){ + return true; // Boolean + } else { + return false; + } + }); + }, + + 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 = json.fromJson(json.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(lang.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 = json.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 = lang.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 = lang.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 = lang.trim(complexQuery.replace(op[0],"")); + op = lang.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 = lang.trim(complexQuery.substring(0,pos).replace(/\"|\'/g,"")); + complexQuery = lang.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 = lang.trim(complexQuery.substring(pos2 + 1)); + } + sQuery += self._containsValue(candidateItem, key, value, filterUtil.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 = lang.trim(complexQuery.substring(0,pos)); + complexQuery = lang.trim(complexQuery.substring(pos)); + }else{ //not a space, so must be at the end of the complexQuery. + value = lang.trim(complexQuery); + complexQuery = ""; + } //end inner if(tok) else + sQuery += self._containsValue(candidateItem, key, value, filterUtil.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){ + kernel.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 = xhr.get(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") && + (!lang.isArray(aValue)) && + (!lang.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(lang.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(!lang.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(lang.isFunction(mappingObj)){ + arrayOfValues[j] = new mappingObj(value._value); + }else if(lang.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(!lang.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){ + kernel.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 = xhr.get(getArgs); + getHandler.addCallback(function(data){ + var scope = keywordArgs.scope?keywordArgs.scope:winUtil.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:winUtil.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:winUtil.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:winUtil.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){ + kernel.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 = xhr.get(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. +lang.extend(AndOrReadStore, simpleFetch); + +return AndOrReadStore; +}); + + diff --git a/js/dojo-1.7.2/dojox/data/AndOrWriteStore.js b/js/dojo-1.7.2/dojox/data/AndOrWriteStore.js new file mode 100644 index 0000000..6a884c4 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/AndOrWriteStore.js @@ -0,0 +1,812 @@ +//>>built +define("dojox/data/AndOrWriteStore", ["dojo/_base/declare","dojo/_base/lang","dojo/_base/array", "dojo/_base/json", "dojo/date/stamp", + "dojo/_base/window", "./AndOrReadStore"], + function(declare, lang, arrayUtil, json, dateStamp, winUtil, AndOrReadStore) { +/*===== var AndOrReadStore = dojox.data.AndOrReadStore; =====*/ + +return declare("dojox.data.AndOrWriteStore", 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 dateStamp.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(lang.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(!lang.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 = arrayUtil.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] = lang.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. + arrayUtil.forEach(attributes, function(attribute){ + arrayUtil.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 = arrayUtil.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(lang.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] = lang.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(lang.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(!lang.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(lang.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(!lang.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 = {}; + arrayUtil.forEach(oldValues, function(possibleItem){ + if(this.isItem(possibleItem)){ + var id = this.getIdentity(possibleItem); + map[id.toString()] = true; + } + }, this); + arrayUtil.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: " + json.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(lang.isObject(typeMap) && !lang.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 json.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(lang.isObject(something)){ + var i; + for(i in something){ + empty = false; + break; + } + }else if(lang.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 || winUtil.global; + keywordArgs.onComplete.call(scope); + } + }; + var saveFailedCallback = function(){ + self._saveInProgress = false; + if(keywordArgs && keywordArgs.onError){ + var scope = keywordArgs.scope || winUtil.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]; + } + lang.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]){ + arrayUtil.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.7.2/dojox/data/AppStore.js b/js/dojo-1.7.2/dojox/data/AppStore.js new file mode 100644 index 0000000..b3c8a94 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/AppStore.js @@ -0,0 +1,829 @@ +//>>built +define("dojox/data/AppStore", ["dojo", "dojox", "dojo/data/util/simpleFetch", "dojo/data/util/filter", "dojox/atom/io/Connection"], function(dojo, dojox) { + +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.nodeType == "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); + +return dojox.data.AppStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/AtomReadStore.js b/js/dojo-1.7.2/dojox/data/AtomReadStore.js new file mode 100644 index 0000000..0c16131 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/AtomReadStore.js @@ -0,0 +1,551 @@ +//>>built +define("dojox/data/AtomReadStore", ["dojo", "dojox", "dojo/data/util/filter", "dojo/data/util/simpleFetch", "dojo/date/stamp"], function(dojo, dojox) { +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. ’ with ' + text = text.replace(/’/m , "'").replace(/″/m , "\"").replace(/</m,">").replace(/>/m,"<").replace(/&/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); + +return dojox.data.AtomReadStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/CdfStore.js b/js/dojo-1.7.2/dojox/data/CdfStore.js new file mode 100644 index 0000000..d702da3 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/CdfStore.js @@ -0,0 +1,600 @@ +//>>built +define("dojox/data/CdfStore", ["dojo", "dojox", "dojo/data/util/sorter"], function(dojo, dojox) { + +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 + } + +}); + +return dojox.data.CdfStore; +}); + diff --git a/js/dojo-1.7.2/dojox/data/ClientFilter.js b/js/dojo-1.7.2/dojox/data/ClientFilter.js new file mode 100644 index 0000000..042466b --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/ClientFilter.js @@ -0,0 +1,293 @@ +//>>built +define("dojox/data/ClientFilter", ["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/Deferred", "dojo/data/util/filter"], + function(declare, lang, array, Deferred, filter) { + +// This is an abstract data store module for adding updateable result set functionality to an existing data store class + + 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 + }); + ClientFilter.onUpdate(); + } + }; + var ClientFilter = 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 + array.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 = lang.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 + 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 Deferred(); + defResult.callback(cachedArgs.cacheResults); + } + defResult.addCallback(function(results){ + results = self.clientSideFetch(lang.mixin(lang.mixin({}, args),{query:clientQuery}), results); + defResult.fullLength = results._fullLength; + return results; + }); + args._version = cachedArgs._version; + break; + } + } + if(!defResult){ + var serverArgs = lang.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 = lang.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(array.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)) ? + !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); + }; + } + } + ); + ClientFilter.onUpdate = function(){}; + + return ClientFilter; +}); diff --git a/js/dojo-1.7.2/dojox/data/CouchDBRestStore.js b/js/dojo-1.7.2/dojox/data/CouchDBRestStore.js new file mode 100644 index 0000000..c11b204 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/CouchDBRestStore.js @@ -0,0 +1,83 @@ +//>>built +define("dojox/data/CouchDBRestStore", ["dojo", "dojox", "dojox/data/JsonRestStore"], function(dojo, dojox) { + +// 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; +}; + +return dojox.data.CouchDBRestStore; + +}); diff --git a/js/dojo-1.7.2/dojox/data/CssClassStore.js b/js/dojo-1.7.2/dojox/data/CssClassStore.js new file mode 100644 index 0000000..ab554cf --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/CssClassStore.js @@ -0,0 +1,162 @@ +//>>built +define("dojox/data/CssClassStore", ["dojo/_base/declare","dojox/data/CssRuleStore"], + function(declare, CssRuleStore) { + +/*===== var CssRuleStore = dojox.data.CssRuleStore =====*/ + +return declare("dojox.data.CssClassStore", 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[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.7.2/dojox/data/CssRuleStore.js b/js/dojo-1.7.2/dojox/data/CssRuleStore.js new file mode 100644 index 0000000..9bb5f74 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/CssRuleStore.js @@ -0,0 +1,462 @@ +//>>built +define("dojox/data/CssRuleStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/_base/json","dojo/_base/window", "dojo/_base/sniff", "dojo/data/util/sorter", "dojo/data/util/filter", "./css"], + function(lang, declare, array, jsonUtil, winUtil, has, sorter, filter, css) { + +return 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){ + lang.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 = 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(); + 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 = 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(array.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 && lang.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(has("ie")){ + 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(!lang.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 = 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 || winUtil.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 || winUtil.global; + if(this._allItems === null){ + this._allItems = {}; + try{ + if(this.gatherHandle){ + this._waiting.push({'forFunc': lang.hitch(this, this._handleRule), 'finishFunc': lang.hitch(this, this._handleReturn)}); + }else{ + css.rules.forEach(lang.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 || winUtil.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 = jsonUtil.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 = (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] = 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(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(lang.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 array.some(this.getValues(item, attribute), function(possibleValue){ + if(possibleValue !== null && !lang.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.7.2/dojox/data/CsvStore.js b/js/dojo-1.7.2/dojox/data/CsvStore.js new file mode 100644 index 0000000..65e838c --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/CsvStore.js @@ -0,0 +1,727 @@ +//>>built +define("dojox/data/CsvStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/xhr", "dojo/_base/window","dojo/data/util/filter", "dojo/data/util/simpleFetch"], + function(lang, declare, xhr, winUtil, filterUtil, simpleFetch) { + +var CsvStore = 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 = filterUtil.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] = filterUtil.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 = xhr.get(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(lang.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:winUtil.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 = xhr.get(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. +lang.extend(CsvStore, simpleFetch); + +return CsvStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/FileStore.js b/js/dojo-1.7.2/dojox/data/FileStore.js new file mode 100644 index 0000000..c3b6568 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/FileStore.js @@ -0,0 +1,431 @@ +//>>built +define("dojox/data/FileStore", ["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/window", "dojo/_base/json", "dojo/_base/xhr"], + function(declare, lang, winUtil, jsonUtil, xhr) { + +return 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(lang.isArray(args.options)){ + this.options = args.options; + }else{ + if(lang.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 || winUtil.global; + + var content = {}; + + if(this.options.length > 0){ + content.options = jsonUtil.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 = xhr.get(xhrData); + deferred.addErrback(function(error){ + if(keywordArgs.onError){ + keywordArgs.onError.call(scope, error); + } + }); + + deferred.addCallback(function(data){ + delete item.parentPath; + delete item._loaded; + lang.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" && !lang.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 || winUtil.global; + + //Generate what will be sent over. + var reqParams = {}; + if(request.query){ + reqParams.query = jsonUtil.toJson(request.query); + } + + if(request.sort){ + reqParams.sort = jsonUtil.toJson(request.sort); + } + + if(request.queryOptions){ + reqParams.queryOptions = jsonUtil.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 = jsonUtil.toJson(this.options); + } + + var getArgs = { + url: this.url, + preventCache: this.urlPreventCache, + failOk: this.failOk, + handleAs: "json-comment-optional", + content: reqParams + }; + + + var deferred = xhr.get(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 || winUtil.global; + + var content = {}; + + if(this.options.length > 0){ + content.options = jsonUtil.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 = xhr.get(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 || winUtil.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(lang.isArray(item.children)){ + var children = item.children; + var i; + for(i = 0; i < children.length; i++ ){ + var name = children[i]; + if(lang.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.7.2/dojox/data/FlickrRestStore.js b/js/dojo-1.7.2/dojox/data/FlickrRestStore.js new file mode 100644 index 0000000..3d96b41 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/FlickrRestStore.js @@ -0,0 +1,486 @@ +//>>built +define("dojox/data/FlickrRestStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/io/script", "dojox/data/FlickrStore", "dojo/_base/connect"], + function(lang, declare, array, scriptIO, FlickrStore, connect) { + +/*===== var FlickrStore = dojox.data.FlickrStore; =====*/ + +var FlickrRestStore = declare("dojox.data.FlickrRestStore", + 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 = 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 { + lang.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 = lang.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 = lang.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. + array.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 = scriptIO.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){ + connect.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 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 = []; + } + array.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) && array.some(arr, function(item){ + return ((start >= item.start)&&(end <= item.end)); + }); + } +}); +return FlickrRestStore; +}); + diff --git a/js/dojo-1.7.2/dojox/data/FlickrStore.js b/js/dojo-1.7.2/dojox/data/FlickrStore.js new file mode 100644 index 0000000..e020956 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/FlickrStore.js @@ -0,0 +1,285 @@ +//>>built +define("dojox/data/FlickrStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/array", "dojo/data/util/simpleFetch", "dojo/io/script", + "dojo/_base/connect", "dojo/date/stamp", "dojo/AdapterRegistry"], + function(lang, declare, array, simpleFetch, scriptIO, connect, dateStamp, AdapterRegistry) { + +var FlickrStore = 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 = lang.hitch(this, "_unescapeHtml"); + var s = lang.hitch(dateStamp, "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" + }; + + array.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 = lang.hitch(this, function(data){ + if(!!handle){ + connect.disconnect(handle); + } + + //Process the items... + fetchHandler(this._processFlickrData(data), request); + }); + handle = connect.connect("jsonFlickrFeed", myHandler); + var deferred = scriptIO.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){ + connect.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(/&/gm, "&"). + replace(/</gm, "<"). + replace(/>/gm, ">"). + replace(/"/gm, "\""). + replace(/'/gm, "'"); + } +}); + +lang.extend(FlickrStore, simpleFetch); + +var feedsUrl = "http://api.flickr.com/services/feeds/"; + +var reg = FlickrStore.urlRegistry = new 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){}; +} + +return FlickrStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/GoogleFeedStore.js b/js/dojo-1.7.2/dojox/data/GoogleFeedStore.js new file mode 100644 index 0000000..2b5c320 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/GoogleFeedStore.js @@ -0,0 +1,81 @@ +//>>built +define("dojox/data/GoogleFeedStore", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojox/data/GoogleSearchStore"], + function(dojo, lang, declare, GoogleSearchStore) { + +dojo.experimental("dojox.data.GoogleFeedStore"); + +/*===== var Search = dojox.data.GoogleSearchStore =====*/ +var Search = GoogleSearchStore.Search; + +return declare("dojox.data.GoogleFeedStore", Search,{ + // 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(lang.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.7.2/dojox/data/GoogleSearchStore.js b/js/dojo-1.7.2/dojox/data/GoogleSearchStore.js new file mode 100644 index 0000000..b6f1674 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/GoogleSearchStore.js @@ -0,0 +1,657 @@ +//>>built +define("dojox/data/GoogleSearchStore", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/query", + "dojo/dom-construct","dojo/io/script"], + function(dojo, lang, declare, winUtil, domQuery, domConstruct, scriptIO) { + +dojo.experimental("dojox.data.GoogleSearchStore"); + +var SearchStore = 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(lang.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 || winUtil.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 = scriptIO.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. + domQuery("#" + scriptIds.splice(0,1)).forEach(domConstruct.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 + winUtil.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. + winUtil.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); + } + winUtil.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] = lang.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; + } +}); + +var WebSearchStore = declare("dojox.data.GoogleWebSearchStore", SearchStore,{ + // 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 +}); + +var BlogSearchStore = declare("dojox.data.GoogleBlogSearchStore", SearchStore,{ + // 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: { } +}); + + +var LocalSearchStore = declare("dojox.data.GoogleLocalSearchStore", SearchStore,{ + // 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' + } +}); + +var VideoSearchStore = declare("dojox.data.GoogleVideoSearchStore", SearchStore,{ + // 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: { } +}); + +var NewsSearchStore = declare("dojox.data.GoogleNewsSearchStore", SearchStore,{ + // 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: { } +}); + +var BookSearchStore = declare("dojox.data.GoogleBookSearchStore", SearchStore,{ + // 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: { } +}); + +var ImageSearchStore = declare("dojox.data.GoogleImageSearchStore", SearchStore,{ + // 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: { } +}); + +return { + Search: SearchStore, + ImageSearch: ImageSearchStore, + BookSearch: BookSearchStore, + NewsSearch: NewsSearchStore, + VideoSearch: VideoSearchStore, + LocalSearch: LocalSearchStore, + BlogSearch: BlogSearchStore, + WebSearch: WebSearchStore + } +}); diff --git a/js/dojo-1.7.2/dojox/data/HtmlStore.js b/js/dojo-1.7.2/dojox/data/HtmlStore.js new file mode 100644 index 0000000..a5689d2 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/HtmlStore.js @@ -0,0 +1,565 @@ +//>>built +define("dojox/data/HtmlStore", ["dojo/_base/declare", "dojo/_base/array", "dojo/_base/lang", "dojo/dom", "dojo/_base/xhr", "dojo/_base/window", + "dojo/data/util/simpleFetch", "dojo/data/util/filter", "dojox/xml/parser"], + function(declare, array, lang, dom, xhr, winUtil, simpleFetch, filter, xmlParser) { + +var HtmlStore = 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){ + array.forEach(this._rootNode.tHead.rows[0].cells, lang.hitch(this, function(th){ + var text = xmlParser.textContent(th); + this._headings.push(this.trimWhitespace?lang.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 array.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 = xmlParser.textContent(item.cells[index]); + }else{//return Value for lists + text = xmlParser.textContent(item); + } + return [this.trimWhitespace?lang.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 = 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 && dom.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 = dom.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 = xhr.get(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] = 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 = dom.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 === xmlParser.textContent(self._rootNode.childNodes[i])){ + item = self._rootNode.childNodes[i]; + } + } + } + if(keywordArgs.onItem){ + scope = keywordArgs.scope?keywordArgs.scope:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + + }else{ + var getArgs = { + url: this.url, + handleAs: "text" + }; + var getHandler = xhr.get(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 === xmlParser.textContent(self._rootNode.childNodes[i])){ + item = self._rootNode.childNodes[i]; + break; + } + } + } + if(keywordArgs.onItem){ + scope = keywordArgs.scope?keywordArgs.scope:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + }); + getHandler.addErrback(function(error){ + if(keywordArgs.onError){ + scope = keywordArgs.scope?keywordArgs.scope:winUtil.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:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + } + } + } +}); +lang.extend(HtmlStore, simpleFetch); +return HtmlStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/HtmlTableStore.js b/js/dojo-1.7.2/dojox/data/HtmlTableStore.js new file mode 100644 index 0000000..a0e9b7b --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/HtmlTableStore.js @@ -0,0 +1,473 @@ +//>>built +define("dojox/data/HtmlTableStore", ["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/lang", "dojo/dom", "dojo/_base/array", + "dojo/_base/xhr", "dojo/_base/sniff", "dojo/_base/window", "dojo/data/util/simpleFetch", + "dojo/data/util/filter", "dojox/xml/parser"], + function(kernel, declare, lang, dom, array, xhr, has, winUtil, simpleFetch, filter, xmlParser) { + +var HtmlTableStore = declare("dojox.data.HtmlTableStore", null, { + constructor: function(/*Object*/args){ + kernel.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 = dom.byId(args.tableId); + this.tableId = this._rootNode.id; + }else{ + this._rootNode = dom.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 = []; + array.forEach(this._rootNode.tHead.rows[0].cells, lang.hitch(this, function(th){ + this._headings.push(xmlParser.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 array.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 [xmlParser.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 = 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 = dom.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 = xhr.get(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] = 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(!has("opera")){ + return item.sectionRowIndex; // int + }else{ + return (array.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 = dom.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:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + + }else{ + var getArgs = { + url: this.url, + handleAs: "text" + }; + var getHandler = xhr.get(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:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + }); + getHandler.addErrback(function(error){ + if(keywordArgs.onError){ + scope = keywordArgs.scope?keywordArgs.scope:winUtil.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:winUtil.global; + keywordArgs.onItem.call(scope, item); + } + } + } + } +}); +lang.extend(HtmlTableStore,simpleFetch); + +return HtmlTableStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/ItemExplorer.js b/js/dojo-1.7.2/dojox/data/ItemExplorer.js new file mode 100644 index 0000000..fcc6343 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/ItemExplorer.js @@ -0,0 +1,631 @@ +//>>built +// wrapped by build app +define("dojox/data/ItemExplorer", ["dijit","dojo","dojox","dojo/require!dijit/Tree,dijit/Dialog,dijit/Menu,dijit/form/ValidationTextBox,dijit/form/Textarea,dijit/form/Button,dijit/form/RadioButton,dijit/form/FilteringSelect"], function(dijit,dojo,dojox){ +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.RadioButton"); +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.7.2/dojox/data/JsonQueryRestStore.js b/js/dojo-1.7.2/dojox/data/JsonQueryRestStore.js new file mode 100644 index 0000000..125fffa --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/JsonQueryRestStore.js @@ -0,0 +1,15 @@ +//>>built +define("dojox/data/JsonQueryRestStore", ["dojo", "dojox", "dojox/data/JsonRestStore", "dojox/data/util/JsonQuery", "dojox/data/ClientFilter", "dojox/json/query"], function(dojo, dojox) { + +// 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); + } +}); + +return dojox.data.JsonQueryRestStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/JsonRestStore.js b/js/dojo-1.7.2/dojox/data/JsonRestStore.js new file mode 100644 index 0000000..27e16f6 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/JsonRestStore.js @@ -0,0 +1,510 @@ +//>>built +define("dojox/data/JsonRestStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojox/rpc/Rest", + "dojox/rpc/JsonRest", "dojox/json/schema", "dojox/data/ServiceStore"], + function(lang, declare, connect, rpcRest, rpcJsonRest, jsonSchema, ServiceStore) { + +/*===== +var ServiceStore = dojox.data.ServiceStore; +=====*/ + +var JsonRestStore = declare("dojox.data.JsonRestStore", 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"). + // + + connect.connect(rpcRest._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 = rpcJsonRest.services[options.target] || + rpcRest(options.target, true); + // create a default Rest service + } + } + + rpcJsonRest.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 = rpcJsonRest.getConstructor(this.service); + var self = this; + this._constructor = function(data){ + constructor.call(this, data); + self.onNew(this); + } + this._constructor.prototype = constructor.prototype; + this._index = rpcRest._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 = dataExtCfg._getStoreForItem(item) || this; + if(this.referenceIntegrity){ + // cleanup all references + rpcJsonRest._saveNotNeeded = true; + var index = rpcRest._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. + (dataExtCfg._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 + (dataExtCfg._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); + rpcJsonRest._saveNotNeeded = false; + var i = 0; + while(checked[i]){ + // remove the checked marker + delete checked[i++].__checked; + } + } + rpcJsonRest.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. + rpcJsonRest.changing(item,_deleting); + }, + cancelChanging : function(object){ + // summary: + // Removes an object from the list of dirty objects + // This will prevent that object from being saved to the server on the next save + // object: + // The item to cancel changes on + if(!object.__id){ + return; + } + dirtyObjects = dirty=rpcJsonRest.getDirtyObjects(); + for(var i=0; i<dirtyObjects.length; i++){ + var dirty = dirtyObjects[i]; + if(object==dirty.object){ + dirtyObjects.splice(i, 1); + return; + } + } + + }, + + setValue: function(item, attribute, value){ + // summary: + // sets 'attribute' on 'item' to 'value' + + var old = item[attribute]; + var store = item.__id ? dataExtCfg._getStoreForItem(item) : this; + if(jsonSchema && store.schema && store.schema.properties){ + // if we have a schema and schema validator available we will validate the property change + jsonSchema.mustBeValid(jsonSchema.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(!lang.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){ + rpcConfig._sync = true; + } + + var actions = rpcJsonRest.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. + rpcJsonRest.revert(kwArgs && kwArgs.global && this.service); + }, + + isDirty: function(item){ + // summary + // returns true if the item is marked as dirty. + return rpcJsonRest.isDirty(item, this); + }, + 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 == rpcJsonRest.getServiceAndId(item.__id).service); + }, + _doQuery: function(args){ + var query= typeof args.queryStr == 'string' ? args.queryStr : args.query; + var deferred = rpcJsonRest.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 = rpcJsonRest.getServiceAndId((self.target + schemaRef).replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,"$2$3")); + var schemaDeferred = rpcJsonRest.byId(serviceAndId.service, serviceAndId.id); + schemaDeferred.addCallbacks(function(newSchema){ + lang.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 = rpcJsonRest.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; + } + + + } +); +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 = (rpcJsonRest.services[options.target] || {})._store; + if(store){ + return store; + } + } + return new (Class || JsonRestStore)(options); +}; + +var dataExtCfg = lang.getObject("dojox.data",true); +dataExtCfg._getStoreForItem = function(item){ + if(item.__id){ + var serviceAndId = rpcJsonRest.getServiceAndId(item.__id); + if(serviceAndId && serviceAndId.service._store){ + return serviceAndId.service._store; + }else{ + var servicePath = item.__id.toString().match(/.*\//)[0]; + return new JsonRestStore({target:servicePath}); + } + } + return null; +}; +var jsonRefConfig = lang.getObject("dojox.json.ref", true); +jsonRefConfig._useRefs = true; // Use referencing when identifiable objects are referenced + +return JsonRestStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/KeyValueStore.js b/js/dojo-1.7.2/dojox/data/KeyValueStore.js new file mode 100644 index 0000000..83af824 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/KeyValueStore.js @@ -0,0 +1,393 @@ +//>>built +define("dojox/data/KeyValueStore", ["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/xhr", "dojo/_base/window", + "dojo/data/util/simpleFetch", "dojo/data/util/filter"], + function(declare, lang, xhr, winUtil, simpleFetch, filterUtil) { + +var KeyValueStore = 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(!lang.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 = filterUtil.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] = filterUtil.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 = xhr.get(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 || winUtil.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. +lang.extend(KeyValueStore,simpleFetch); +return KeyValueStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/OpenSearchStore.js b/js/dojo-1.7.2/dojox/data/OpenSearchStore.js new file mode 100644 index 0000000..1009e57 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/OpenSearchStore.js @@ -0,0 +1,373 @@ +//>>built +define("dojox/data/OpenSearchStore", [ + "dojo/_base/kernel", // dojo.experimental + "dojo/_base/lang", // dojo.extend + "dojo/_base/declare", // dojo.declare + "dojo/_base/xhr", // dojo.xhrGet + "dojo/_base/array", // dojo.forEach + "dojo/_base/window", // dojo.doc + "dojo/query", + "dojo/data/util/simpleFetch", + "dojox/xml/parser"], function (kernel, lang, declare, dxhr, array, window, query, simpleFetch, parser) { +kernel.experimental("dojox.data.OpenSearchStore"); + +var OpenSearchStore = 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 = dxhr.get({ + 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); + + array.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 = dxhr.get(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 = window.doc.createElement("div"); + div.innerHTML = data; + return 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; + } + } + } +}); +return lang.extend(OpenSearchStore,simpleFetch); +});
\ No newline at end of file diff --git a/js/dojo-1.7.2/dojox/data/OpmlStore.js b/js/dojo-1.7.2/dojox/data/OpmlStore.js new file mode 100644 index 0000000..b3d6111 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/OpmlStore.js @@ -0,0 +1,526 @@ +//>>built +define("dojox/data/OpmlStore", ["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/xhr", "dojo/data/util/simpleFetch", "dojo/data/util/filter", + "dojo/_base/window"], + function(declare, lang, xhr, simpleFetch, filterUtil, winUtil) { + +var OpmlStore = 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(!lang.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 = filterUtil.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] = filterUtil.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 = xhr.get(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 = xhr.get(getArgs); + getHandler.addCallback(function(data){ + var scope = keywordArgs.scope ? keywordArgs.scope : winUtil.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 : winUtil.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 : winUtil.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 : winUtil.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. +lang.extend(OpmlStore, simpleFetch); + +return OpmlStore; +}); + diff --git a/js/dojo-1.7.2/dojox/data/PersevereStore.js b/js/dojo-1.7.2/dojox/data/PersevereStore.js new file mode 100644 index 0000000..9818001 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/PersevereStore.js @@ -0,0 +1,113 @@ +//>>built +define("dojox/data/PersevereStore", ["dojo", "dojox", "require", "dojox/data/JsonQueryRestStore", "dojox/rpc/Client", "dojo/_base/url"], function(dojo, dojox, require) { + +// 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 + 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. + require("dojox/io/xhrPlugins"); // also not necessary, but we can register that Persevere supports proxying + dojox.io.xhrPlugins.addProxy("/proxy/"); +}; + +return dojox.data.PersevereStore; + +}); diff --git a/js/dojo-1.7.2/dojox/data/PicasaStore.js b/js/dojo-1.7.2/dojox/data/PicasaStore.js new file mode 100644 index 0000000..12527f1 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/PicasaStore.js @@ -0,0 +1,274 @@ +//>>built +define("dojox/data/PicasaStore", ["dojo/_base/lang","dojo/_base/declare", "dojo/_base/connect", "dojo/io/script", "dojo/data/util/simpleFetch", "dojo/date/stamp"], + function(lang, declare, connect, scriptIO, simpleFetch, dateStamp) { + +var PicasaStore = 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 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 [dateAtamp.fromISOString(item.published)]; + }else if(attribute === "dateTaken"){ + return [dateStamp.fromISOString(item.published)]; + }else if(attribute === "updated"){ + return [dateStamp.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){ + connect.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 = scriptIO.get(getArgs); + + deferred.addErrback(function(error){ + connect.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(/&/gm, "&").replace(/</gm, "<").replace(/>/gm, ">").replace(/"/gm, "\""); + str = str.replace(/'/gm, "'"); + } + return str; + } +}); +lang.extend(PicasaStore, simpleFetch); + +return PicasaStore; + +}); diff --git a/js/dojo-1.7.2/dojox/data/QueryReadStore.js b/js/dojo-1.7.2/dojox/data/QueryReadStore.js new file mode 100644 index 0000000..372048a --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/QueryReadStore.js @@ -0,0 +1,519 @@ +//>>built +define("dojox/data/QueryReadStore", ["dojo", "dojox", "dojo/data/util/sorter", "dojo/string"], function(dojo, dojox) { + +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]; + } + } +); + +return dojox.data.QueryReadStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/README b/js/dojo-1.7.2/dojox/data/README new file mode 100644 index 0000000..4641983 --- /dev/null +++ b/js/dojo-1.7.2/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.7.2/dojox/data/RailsStore.js b/js/dojo-1.7.2/dojox/data/RailsStore.js new file mode 100644 index 0000000..74ece87 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/RailsStore.js @@ -0,0 +1,169 @@ +//>>built +define("dojox/data/RailsStore", ["dojo", "dojox", "dojox/data/JsonRestStore"], function(dojo, dojox) { + +// 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}; + } +}); + +return dojox.data.RailsStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/S3Store.js b/js/dojo-1.7.2/dojox/data/S3Store.js new file mode 100644 index 0000000..0a32385 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/S3Store.js @@ -0,0 +1,35 @@ +//>>built +define("dojox/data/S3Store", ["dojo/_base/declare", "dojox/data/JsonRestStore", "dojox/rpc/ProxiedPath"], + function(declare, JsonRestStore, ProxiedPath) { + +// S3JsonRestStore is an extension of JsonRestStore to handle +// Amazon's S3 service using JSON data +/*===== var JsonRestStore = dojox.data.JsonRestStore =====*/ +return declare("dojox.data.S3Store", 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.7.2/dojox/data/ServiceStore.js b/js/dojo-1.7.2/dojox/data/ServiceStore.js new file mode 100644 index 0000000..1fe2b33 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/ServiceStore.js @@ -0,0 +1,395 @@ +//>>built +define("dojox/data/ServiceStore", ["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array"], + function(declare, lang, array) { + +// 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"). +// + +return 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. + lang.getObject("dojox.data.ClientFilter", 0)||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){ + lang.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); + 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 array.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 = lang.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.7.2/dojox/data/SnapLogicStore.js b/js/dojo-1.7.2/dojox/data/SnapLogicStore.js new file mode 100644 index 0000000..993f361 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/SnapLogicStore.js @@ -0,0 +1,329 @@ +//>>built +define("dojox/data/SnapLogicStore", ["dojo", "dojox", "dojo/io/script", "dojo/data/util/sorter"], function(dojo, dojox) { + +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 + } +}); + +return dojox.data.SnapLogicStore; +}); + diff --git a/js/dojo-1.7.2/dojox/data/StoreExplorer.js b/js/dojo-1.7.2/dojox/data/StoreExplorer.js new file mode 100644 index 0000000..14581b6 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/StoreExplorer.js @@ -0,0 +1,196 @@ +//>>built +// wrapped by build app +define("dojox/data/StoreExplorer", ["dijit","dojo","dojox","dojo/require!dojox/grid/DataGrid,dojox/data/ItemExplorer,dijit/layout/BorderContainer,dijit/layout/ContentPane"], function(dijit,dojo,dojox){ +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: "; + 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 = " "; + 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.7.2/dojox/data/WikipediaStore.js b/js/dojo-1.7.2/dojox/data/WikipediaStore.js new file mode 100644 index 0000000..d38d4c5 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/WikipediaStore.js @@ -0,0 +1,118 @@ +//>>built +define("dojox/data/WikipediaStore", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/io/script", + "dojo/io-query", "dojox/rpc/Service", "dojox/data/ServiceStore"], + function(kernel, lang, declare, scriptIO, ioQuery, Service, ServiceStore) { + +kernel.experimental("dojox.data.WikipediaStore"); + +/*===== var ServiceStore = dojox.data.ServiceStore; =====*/ + +return declare("dojox.data.WikipediaStore", 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 Service(require.toUrl("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 = lang.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 = ioQuery.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.7.2/dojox/data/XmlItem.js b/js/dojo-1.7.2/dojox/data/XmlItem.js new file mode 100644 index 0000000..ff08964 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/XmlItem.js @@ -0,0 +1,44 @@ +//>>built +define("dojox/data/XmlItem", ["dojo/_base/declare"], + function(declare) { + +return 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 + } +}); +}); diff --git a/js/dojo-1.7.2/dojox/data/XmlStore.js b/js/dojo-1.7.2/dojox/data/XmlStore.js new file mode 100644 index 0000000..d9dc91a --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/XmlStore.js @@ -0,0 +1,1472 @@ +//>>built +define("dojox/data/XmlStore", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/xhr", "dojo/data/util/simpleFetch", + "dojo/_base/query", "dojo/_base/array", "dojo/_base/window", "dojo/data/util/filter", "dojox/xml/parser", + "dojox/data/XmlItem"], + function(lang, declare, xhr, simpleFetch, domQuery, array, winUtil, filter, xmlParser, XmlItem) { + +var XmlStore = 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); + if(!url){ + errorHandler(new Error("No URL specified."), request); + return; + } + var localRequest = (!this.sendQuery ? request : {}); // use request for _getItems() + + var self = this; + var getArgs = { + url: url, + handleAs: "xml", + preventCache: self.urlPreventCache + }; + var getHandler = xhr.get(getArgs); + getHandler.addCallback(function(data){ + var items = self._getItems(data, localRequest); + 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(lang.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 = domQuery(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] = filter.patternToRegExp(value, ignoreCase); + }else if(value){ + // It's an object, possibly regexp, so treat it as one. + regexpList[key] = value; + } + } + 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); + } + } + array.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 + 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.setValue(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 + 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 + 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 || winUtil.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 = xhr.put(saveArgs); + }else if(method === "DELETE"){ + saveHandler = xhr.del(saveArgs); + }else{ // POST + saveArgs.postData = this._getPostContent(item); + saveHandler = xhr.post(saveArgs); + } + scope = (keywordArgs.scope || winUtil. 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 + return "<?xml version=\'1.0\'?>" + xmlParser.innerXML(item.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 + return "<?xml version='1.0'?>" + xmlParser.innerXML(item.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 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){ + + array.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 xmlParser.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 || winUtil.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 || winUtil.global; + keywordArgs.onItem.call(scope, item); + } + } + } + }; + url = this._getFetchUrl(null); + getArgs = { + url: url, + handleAs: "xml", + preventCache: self.urlPreventCache + }; + getHandler = xhr.get(getArgs); + + //Add in the callbacks for completion of data load. + getHandler.addCallback(handleDocument); + if(keywordArgs.onError){ + getHandler.addErrback(function(error){ + var s = keywordArgs.scope || winUtil.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 || winUtil.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 || winUtil.global; + keywordArgs.onItem.call(scope, item); + } + }; + + getArgs = { + url: url, + handleAs: "xml", + preventCache: self.urlPreventCache + }; + getHandler = xhr.get(getArgs); + + //Add in the callbacks for completion of data load. + getHandler.addCallback(handleDocument); + if(keywordArgs.onError){ + getHandler.addErrback(function(error){ + var s = keywordArgs.scope || winUtil.global; + keywordArgs.onError.call(s, error); + }); + } + }else{ + if(keywordArgs.onError){ + var s = keywordArgs.scope || winUtil.global; + keywordArgs.onError.call(s, new Error("XmlStore is not told that the server to provides identity support. No keyAttribute specified.")); + } + } + } + } +}); + +lang.extend(XmlStore,simpleFetch); + +return XmlStore; +}); diff --git a/js/dojo-1.7.2/dojox/data/css.js b/js/dojo-1.7.2/dojox/data/css.js new file mode 100644 index 0000000..790ad96 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/css.js @@ -0,0 +1,107 @@ +//>>built +define("dojox/data/css", ["dojo/_base/lang", "dojo/_base/array"], + function(lang, array) { + +var css = lang.getObject("dojox.data.css",true) + +css.rules = {}; + +css.rules.forEach = function(fn,ctx,context){ + if(context){ + var _processSS = function(styleSheet){ + //iterate across rules in the stylesheet + array.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 + }; + array.forEach(context,_processSS); + } +}; + +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 = css.findStyleSheet(styleSheet); + if(s){ + array.forEach(s, function(sheet){ + if(array.indexOf(sheetObjects, sheet) === -1){ + sheetObjects.push(sheet); + } + }); + } + }; + array.forEach(sheets, _processSS); + return sheetObjects; +}; + +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 array.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 array.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; + }); + }; + array.some(document.styleSheets, _processSS); + return sheetObjects; +}; + +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 = css.findStyleSheets(initialStylesheets); + }else{ + initialStylesheets = document.styleSheets; + } + var _processSS = function(styleSheet){ + ret.push(styleSheet); + if(styleSheet.imports){ + array.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 + array.forEach(styleSheet[styleSheet.cssRules?"cssRules":"rules"], function(rule){ + if(rule.type && rule.type === 3){// CSSImportRule (firefox) + _processSS(rule.styleSheet); + } + }); + }; + array.forEach(initialStylesheets,_processSS); + return ret; +}; + +return css; + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleFeedTemplate.html b/js/dojo-1.7.2/dojox/data/demos/GoogleFeedTemplate.html new file mode 100644 index 0000000..2d18fca --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleFeedTemplate.html @@ -0,0 +1,26 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +<ul> +{% for item in google %} +<li class="search-result"> + <div> + <a href="{{item.link}}">{{item.title}}</a> + <div style="display:none" class="content"> + <h2>{{item.title}}</h2> + {{item.content}} + </div> + </div> + <div class="summary"> + {{item.summary}} + </div> + <div class="tags"> + <!--{% if item.categories %}--> + Tags: + <!--{% for cat in item.categories %}--> + {{cat}} + <!--{% endfor %}--> + <!--{% endif %}--> + </div> +</li> +{% endfor %} +</ul>
\ No newline at end of file diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleTemplate.html b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplate.html new file mode 100644 index 0000000..a87d107 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplate.html @@ -0,0 +1,7 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +{% for item in google %} +<span class="search-result"> + <a href="{{item.unescapedUrl}}">{{item.titleNoFormatting}}</a> +</span> +{% endfor %} diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateBlog.html b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateBlog.html new file mode 100644 index 0000000..4d67548 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateBlog.html @@ -0,0 +1,7 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +{% for item in google %} +<span class="search-result"> + <a href="{{item.postUrl}}">{{item.titleNoFormatting}}</a> +</span> +{% endfor %} diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateImage.html b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateImage.html new file mode 100644 index 0000000..37088d8 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateImage.html @@ -0,0 +1,7 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +{% for item in google %} +<span class="search-result"> + <a href="{{item.unescapedUrl}}"><img src="{{item.tbUrl}}" alt="{{item.contentNoFormatting}}"></a> +</span> +{% endfor %} diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateLocal.html b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateLocal.html new file mode 100644 index 0000000..da37f90 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateLocal.html @@ -0,0 +1,7 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +{% for item in google %} +<span class="search-result"> + <a href="{{item.url}}"><img src="{{item.staticMapUrl}}"></a> +</span> +{% endfor %} diff --git a/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateVideo.html b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateVideo.html new file mode 100644 index 0000000..0600db0 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/GoogleTemplateVideo.html @@ -0,0 +1,7 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as google %} +{% for item in google %} +<span class="search-result"> + <a href="{{item.url}}"><img src="{{item.tbUrl}}" width="100" height="75"></a> +</span> +{% endfor %} diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_CssStores_combo_tree_grid.html b/js/dojo-1.7.2/dojox/data/demos/demo_CssStores_combo_tree_grid.html new file mode 100644 index 0000000..22cf65a --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_CssStores_combo_tree_grid.html @@ -0,0 +1,94 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<!-- + Demo application showing The CssClassStore. +--> +<html> +<head> + <title>Demo: dojox.data.CssClassStore</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + @import "../../grid/resources/tundraGrid.css"; + </style> + + <!-- + The following script tag instantiates the dojo library and sets some basic properties. In this case, the application + is told that debug mode is off, and to parse all dojoType widgets when it has fully loaded. + --> + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: false, parseOnLoad: true"></script> + <script> + dojo.require("dijit.Tree"); + dojo.require("dijit.tree.ForestStoreModel"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dojox.data.CssRuleStore"); + dojo.require("dojox.data.CssClassStore"); + dojo.require("dojox.grid.DataGrid"); + + var layoutCss = [ + [ + { field: "selector", name: "Selector", width: 20 }, + { field: "parentStyleSheetHref", name: "StyleSheet", width: 20 }, + { field: "cssText", name: "CSS", width: 'auto' } + ] + ]; + + function init() { + var combo = dijit.byId("classCombo"); + var grid = dijit.byId("cssGrid"); + + function requery() { + var val = combo.getValue(); + val = "." + val; + var query = { + selector: "*" + val + "*" + } + grid.filter(query,true); + } + dojo.connect(combo, "onChange", requery); + } + dojo.addOnLoad(init); + + </script> +</head> + +<body class="tundra"> + <h1> + Demo: Demo of using the Css Stores to browse the loaded CSS classes and associated rules. + </h1> + <p>This demo demonstrates hooking up the Css*Stores to the various digit widgets to browse the Css loaded for the page.</p> + <hr> + + <div dojoType="dojox.data.CssRuleStore" jsId="ruleStore"></div> + <div dojoType="dojox.data.CssClassStore" jsId="classStore"></div> + + + <div dojoType="dijit.tree.ForestStoreModel" + jsId="classModel" + store="classStore" + query="{}" + rootId="Loaded Classes" + rootLabel="Loaded Classes" + childrenAttrs="children"> + </div> + + <h2>dojox.data.CssClassStore connected to ComboBox and querying on classSans:</h2> + <div id="classCombo" dojoType="dijit.form.ComboBox" store="classStore" searchAttr="classSans"></div> + + <h2>dojox.data.CssRuleStore:</h2> + <i>Displays the list of CSS rules filtered by the class selected in the ComboBox.</i> + <div id="cssGrid" + jsId="cssGrid" + dojoType="dojox.grid.DataGrid" + query="{}" + rowsPerPage="20" + store="ruleStore" + structure="layoutCss" + style="width: 100%; height: 400px;"> + </div> + + <h2>dojox.data.CssClassStore connected to Tree:</h2> + <span id="tree" dojoType="dijit.Tree" model="classModel"> </span> + <hr> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_DataDemoTable.html b/js/dojo-1.7.2/dojox/data/demos/demo_DataDemoTable.html new file mode 100644 index 0000000..9c594c1 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_DataDemoTable.html @@ -0,0 +1,143 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Dojo Visual Loader Test</title> + <style type="text/css"> + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dijit/themes/dijit.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + + .oddRow { background-color: #f2f5f9; } + .population { text-align: right; } + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" + djConfig="isDebug: false, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dijit.dijit"); + dojo.require("dojo.parser"); + dojo.require("dijit.Declaration"); + dojo.require("dojo.data.ItemFileReadStore"); + dojo.require("dojox.data.FlickrStore"); + </script> +</head> +<body class="tundra"> + <span dojoType="dojo.data.ItemFileReadStore" + jsId="continentStore" + url="../../../dijit/tests/_data/countries.json"></span> + <span dojoType="dojox.data.FlickrStore" jsId="flickrStore"></span> + + + <h1 class="testTitle">Dojox Data Demo Table</h1> + + <table dojoType="dijit.Declaration" + widgetClass="demo.Table" class="dojoTabular" + defaults="{ store: null, query: { query: { name: '*' } }, columns: [ { name: 'Name', attribute: 'name' } ] }"> + <thead dojoAttachPoint="head"> + <tr dojoAttachPoint="headRow"></tr> + </thead> + <tbody dojoAttachPoint="body"> + <tr dojoAttachPoint="row"> + </tr> + </tbody> + + <script type="dojo/method"> + dojo.forEach(this.columns, function(item, idx){ + var icn = item.className||""; + // add a header for each column + var tth = document.createElement("th"); + tth.innerHTML = item.name; + tth.className = icn; + dojo.connect(tth, "onclick", dojo.hitch(this, "onSort", idx)); + this.headRow.appendChild(tth); + + // and fill in the column cell in the template row + this.row.appendChild(document.createElement("td")); + this.row.lastChild.className = icn; + }, this); + this.runQuery(); + </script> + <script type="dojo/method" event="onSort" args="index"> + var ca = this.columns[index].attribute; + var qs = this.query.sort; + // clobber an existing sort arrow + dojo.query("> th", this.headRow).style("background", "").style("paddingRight", ""); + if(qs && qs[0].attribute == ca){ + qs[0].descending = !qs[0].descending; + }else{ + this.query.sort = [{ + attribute: ca, + descending: false + }]; + } + var th = dojo.query("> th", this.headRow)[index]; + th.style.paddingRight = "16px"; // space for the sort arrow + th.style.background = "url(\""+require.toUrl("dijit/themes/tundra/images/arrow"+(this.query.sort[0].descending ? "Up" : "Down")+((dojo.isIE == 6) ? ".gif" : ".png")) + "\") no-repeat 98% 4px"; + this.runQuery(); + </script> + <script type="dojo/method" event="runQuery"> + this.query.onBegin = dojo.hitch(this, function(){ dojo.query("tr", this.body).orphan(); }); + this.query.onItem = dojo.hitch(this, "onItem"); + this.query.onComplete = dojo.hitch(this, function(){ + dojo.query("tr:nth-child(odd)", this.body).addClass("oddRow"); + dojo.query("tr:nth-child(even)", this.body).removeClass("oddRow"); + }); + this.store.fetch(this.query); + </script> + <script type="dojo/method" event="onItem" args="item"> + var tr = this.row.cloneNode(true); + dojo.query("td", tr).forEach(function(n, i, a){ + var tc = this.columns[i]; + var tv = this.store.getValue(item, tc.attribute)||""; + if(tc.format){ tv = tc.format(tv, item, this.store); } + n.innerHTML = tv; + }, this); + this.body.appendChild(tr); + </script> + </table> + + <span dojoType="demo.Table" store="continentStore" + query="{ query: { type: 'country' }, sort: [ { attribute: 'name', descending: true } ] }" + id="foo"> + <script type="dojo/method" event="preamble"> + this.columns = [ + { name: "Name", attribute: "name" }, + { name: "Population", + attribute: "population", + className: "population" + } + ]; + </script> + </span> + <span dojoType="demo.Table" store="continentStore" + query="{ query: { name: 'A*' } }"></span> + <span dojoType="demo.Table" store="flickrStore" + query="{ query: { groupid: '27475260@N00' } }"> + <script type="dojo/method" event="preamble"> + this.columns = [ + { name: "", attribute: "imageUrlSmall", + format: function(value, item, store){ + return (value.length) ? "<img src='"+value+"'>" : ""; + } + }, + { name: "Title", attribute: "title" } + ]; + </script> + </span> + <span dojoType="demo.Table" store="flickrStore" + query="{ query: { tags: 'dojotoolkit' } }"> + <script type="dojo/method" event="preamble"> + this.columns = [ + { name: "", attribute: "imageUrlSmall", + format: function(value, item, store){ + return (value.length) ? "<img src='"+value+"'>" : ""; + } + }, + { name: "Title", attribute: "title" } + ]; + </script> + </span> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojotree.html b/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojotree.html new file mode 100644 index 0000000..e134361 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojotree.html @@ -0,0 +1,119 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<!-- + Demo application showing LazyLoading File store. +--> +<html> +<head> + <title>Demo: dojox.data.FileStore</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + + .fileView { + margin: 5px; + width: 100%; + + } + .fileView .fileViewTitle{ + color: white; + background-color: black; + font-size: larger; + font-weight: bold; + + } + + .fileView .fileViewTable { + border-width: 2px; + border-style: solid; + width: 100%; + } + + .fileView .fileViewTable tr td { + border-width: 1px; + border-style: solid; + border-color: lightgray; + width: 50%; + vertical-align: top; + } + + .fileView .fileName { + background-color: lightgray; + } + + </style> + + <!-- + The following script tag instantiates the dojo library and sets some basic properties. In this case, the application + is told that debug mode is off, and to parse all dojoType widgets when it has fully loaded. + --> + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: false, parseOnLoad: true, useCommentedJson: true"></script> + <script> + dojo.require("dijit.Tree"); + dojo.require("dijit.tree.ForestStoreModel"); + dojo.require("dojox.data.FileStore"); + dojo.require("dojox.data.demos.widgets.FileView"); + </script> +</head> + +<body class="tundra"> + <h1> + Demo: Lazy Loading File Browsing Store + </h1> + <p>The tree below uses the dojox.data.FileStore and a PHP implementation for the serverside to browse the dojo tree hierarchy in a lazy-load fashion.</p> + <p><i><b>This demo must be run from a web-server with PHP support enabled. Without PHP support, this demo cannot function. The Demo also requires PHP + support for json_encode and json_decode. Please be sure to have those packages installed in your PHP environment.</b></i></p> + <hr> + <i>Clicking on a file in the tree will display the details about that file.</i> + <div dojoType="dojox.data.FileStore" url="stores/filestore_dojotree.php" jsId="fileStore" pathAsQueryParam="true"></div> + <div dojoType="dijit.tree.ForestStoreModel" jsId="fileModel" + store="fileStore" query="{}" + rootId="DojoFiles" rootLabel="Dojo Files" childrenAttrs="children"></div> + + <table style="width: 100%;"> + <tbody> + <tr style="width: 100%;"> + <td style="width: 50%; vertical-align: top;"> + <span id="tree" dojoType="dijit.Tree" model="fileModel" > + <script type="dojo/method" event="onClick" args="item"> + if (fileStore.isItem(item)){ + var attachPt = dojo.byId("fileInfo"); + if (attachPt) { + while(attachPt.firstChild) { + attachPt.removeChild(attachPt.firstChild); + } + var newArgs = {}; + newArgs.name = fileStore.getValue(item, "name"); + newArgs.path = fileStore.getValue(item, "path"); + newArgs.size = fileStore.getValue(item, "size"); + newArgs.directory = fileStore.getValue(item, "directory"); + newArgs.parentDir = fileStore.getValue(item, "parentDir"); + var children = fileStore.getValues(item, "children"); + if (children && children.length > 0) { + newArgs.children = []; + var i; + for (i = 0; i < children.length; i++) { + //Note here that even though the store is lazy-loading, the unloaded items for children still + //have the 'name' attribute, since it is used as part of the info to load the full item. Generally + //you should not access properties of an item that has not been fully inflated yet. It just works + //well in this case for this store. + newArgs.children.push(fileStore.getValue(children[i], "name")); + } + } + var fInfo = new dojox.data.demos.widgets.FileView(newArgs); + attachPt.appendChild(fInfo.domNode); + fInfo.startup(); + } + } + </script> + </span> + </td> + <td id="fileInfo" STYLE="width: 50%; vertical-align: top;"> + </td> + </tr> + + </tbody> + </table> + <hr> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojoxdata_combo_grid.html b/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojoxdata_combo_grid.html new file mode 100644 index 0000000..c4eae5a --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_FileStore_dojoxdata_combo_grid.html @@ -0,0 +1,157 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<!-- + Demo application showing LazyLoading File store. +--> +<html> +<head> + <title>Demo: dojox.data.FileStore</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + @import "../../../dojox/grid/resources/tundraGrid.css"; + + .fileView { + margin: 5px; + width: 100%; + + } + .fileView .fileViewTitle{ + color: white; + background-color: black; + font-size: larger; + font-weight: bold; + + } + + .fileView .fileViewTable { + border-width: 2px; + border-style: solid; + width: 100%; + } + + .fileView .fileViewTable tr td { + border-width: 1px; + border-style: solid; + border-color: lightgray; + width: 50%; + vertical-align: top; + } + + .fileView .fileName { + background-color: lightgray; + } + + .tundra .dojoxGrid-cell { + text-indent: 3px; + } + </style> + + <!-- + The following script tag instantiates the dojo library and sets some basic properties. In this case, the application + is told that debug mode is off, and to parse all dojoType widgets when it has fully loaded. + --> + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: false, parseOnLoad: true, useCommentedJson: true"></script> + <script> + dojo.require("dijit.Tree"); + dojo.require("dijit.tree.ForestStoreModel"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dojox.data.FileStore"); + dojo.require("dojox.grid.DataGrid"); + dojo.require("dojox.data.demos.widgets.FileView"); + + var layoutFiles = [ + [ + { field: "name", name: "Filename", width: 20 }, + { field: "size", name: "File Size (bytes)", width: 10 }, + { field: "directory", name: "Is Directory", width: 10 }, + { field: "path", name: "Path", width: 'auto' } + ] + ]; + + </script> +</head> + +<body class="tundra"> + <h1> + Demo: Lazy Loading File Browsing Store connected to multiple widgets + </h1> + <p>All the widgets used in this demo connect to the same filestore instance. It is talking to a filestore rooted in the dojox/data/ sub-directory to make it fast handling when querying across all files.</p> + <p><i><b>This demo must be run from a web-server with PHP support enabled. Without PHP support, this demo cannot function. The Demo also requires PHP + support for json_encode and json_decode. Please be sure to have those packages installed in your PHP environment.</b></i></p> + <hr> + + <div dojoType="dojox.data.FileStore" url="stores/filestore_dojoxdata.php" jsId="fileStore" pathAsQueryParam="true"></div> + <div dojoType="dijit.tree.ForestStoreModel" jsId="fileModel" + store="fileStore" query="{}" + rootId="./dojox/data" rootLabel="./dojox/data" childrenAttrs="children"></div> + + + <h2>dojox.data.FileStore connected to ComboBox and querying on path:</h2> + <div dojoType="dijit.form.ComboBox" store="fileStore" searchAttr="path" value="./demos"></div> + + + <h2>dojox.data.FileStore connected to Grid and displaying all files:</h2> + <div style="width: 100%; height: 300px;"> + <div id="grid" + dojoType="dojox.grid.DataGrid" + store="fileStore" + structure="layoutFiles" + queryOptions="{deep:true}" + query="{}" + sortFields="[{'attribute':'path', 'descending': false}]" + rowsPerPage="40"> + + </div> + </div> + + + <h2>dojox.data.FileStore connected to Tree:</h2> + <i>Clicking on a file in the tree will display the details about that file.</i> + <table style="width: 100%;"> + <tbody> + <tr style="width: 100%;"> + <td style="width: 50%; vertical-align: top;"> + <span id="tree" dojoType="dijit.Tree" model="fileModel"> + <script type="dojo/method" event="onClick" args="item"> + if (fileStore.isItem(item)){ + var attachPt = dojo.byId("fileInfo"); + if (attachPt) { + while(attachPt.firstChild) { + attachPt.removeChild(attachPt.firstChild); + } + var newArgs = {}; + newArgs.name = fileStore.getValue(item, "name"); + newArgs.path = fileStore.getValue(item, "path"); + newArgs.size = fileStore.getValue(item, "size"); + newArgs.directory = fileStore.getValue(item, "directory"); + newArgs.parentDir = fileStore.getValue(item, "parentDir"); + var children = fileStore.getValues(item, "children"); + if (children && children.length > 0) { + newArgs.children = []; + var i; + for (i = 0; i < children.length; i++) { + //Note here that even though the store is lazy-loading, the unloaded items for children still + //have the 'name' attribute, since it is used as part of the info to load the full item. Generally + //you should not access properties of an item that has not been fully inflated yet. It just works + //well in this case for this store. + newArgs.children.push(fileStore.getValue(children[i], "name")); + } + } + var fInfo = new dojox.data.demos.widgets.FileView(newArgs); + attachPt.appendChild(fInfo.domNode); + fInfo.startup(); + } + } + </script> + </span> + </td> + <td id="fileInfo" STYLE="width: 50%; vertical-align: top;"> + </td> + </tr> + + </tbody> + </table> + <hr> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_FlickrRestStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_FlickrRestStore.html new file mode 100644 index 0000000..5774c76 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_FlickrRestStore.html @@ -0,0 +1,236 @@ +<!-- + This file is a demo of the FlickrStore, a simple wrapper to the public feed service + of Flickr. This just does very basic queries against Flickr and loads the results + into a list viewing widget. +--> +<html> +<head> + <title>Demo of FlickrRestStore</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" + djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.Button"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.NumberSpinner"); + dojo.require("dojox.data.FlickrStore"); + dojo.require("dojox.data.FlickrRestStore"); + dojo.require("dojox.data.demos.widgets.FlickrViewList"); + + function init(){ + //Function to invoke the search of the FlickrStore + function invokeSearch(){ + var request = { + query: { + apikey: "8c6803164dbc395fb7131c9d54843627" + } + }; + + if(idWidget){ + var userid = idWidget.getValue(); + if(userid && userid !== ""){ + request.query.userid = userid; + } + } + if(tagsWidget){ + request.query.tags = (tagsWidget.getValue()||"").split(" ").join(","); + } + if(tagmodeWidget){ + request.query.tagmode = tagmodeWidget.getValue()||""; + } + + if(setIdWidget){ + var setid = setIdWidget.getValue(); + if(setid != ""){ + request.query.setid = setid; + } + } + + if(fullTextWidget){ + var fullText = fullTextWidget.getValue(); + if(fullText != ""){ + request.query.text = fullText; + } + } + + if(sortTypeWidget && sortDirWidget){ + var sortType = sortTypeWidget.getValue(); + var sortDirection = sortDirWidget.getValue(); + + if(sortType != "" && sortDirection != ""){ + request.query.sort = [ + { + attribute: sortType, + descending: (sortDirection.toLowerCase() == "descending") + } + ]; + } + } + + if(countWidget){ + request.count = countWidget.getValue(); + } + if(pageWidget){ + request.start = request.count * (pageWidget.getValue() -1); + } + + if(statusWidget){ + statusWidget.setValue("PROCESSING REQUEST"); + } + + // flickrStore.fetch(request); + flickrViewsWidget.fetch(request); + } + + //Lastly, link up the search event. + var button = dijit.byId("searchButton"); + dojo.connect(button, "onClick", invokeSearch); + } + dojo.addOnLoad(init); + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: FlickrRestStore Search + </h1> + <hr> + <h3> + Description: + </h3> + <p> + This simple demo shows how services, such as Flickr, can be wrapped by the datastore API. + In this demo, you can search public Flickr images through a FlickrRestStore by specifying + a series of tags (separated by spaces) to search on. The results will be displayed below the search box. + </p> + <p> + For fun, search on the 3dny tag! + </p> + + <blockquote> + + <!-- + The store instance used by this demo. + --> + <table> + <tbody> + <tr> + <td> + <b>Status:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="status" jsId="statusWidget" disabled="true"></div> + </td> + <td></td> + <td></td> + </tr> + <tr> + <td> + <b>User ID:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="userid" jsId="idWidget" value="44153025@N00"></div> + </td> + <td> + <b>Set ID</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="setid" jsId="setIdWidget"></div> + </td> + </tr> + <tr> + <td> + <b>Tags:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="tags" jsId="tagsWidget" value="rollingstones,kinsale"></div> + </td> + <td> + <b>Full Text</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="fulltext" jsId="fullTextWidget"></div> + </td> + </tr> + <tr> + <td> + <b>Tagmode:</b> + </td> + <td> + <select id="tagmode" + jsId="tagmodeWidget" + dojoType="dijit.form.ComboBox" + autocomplete="false" + value="any" + > + <option>any</option> + <option>all</option> + </select> + </td> + <td> + <b>Sort</b> + </td> + <td> + <select dojoType="dijit.form.ComboBox" size="15" id="sorttype" jsId="sortTypeWidget"> + <option>date-posted</option> + <option>date-taken</option> + <option>interestingness</option> + </select> + <select dojoType="dijit.form.ComboBox" size="15" id="sortdirection" jsId="sortDirWidget"> + <option>ascending</option> + <option>descending</option> + </select> + </td> + </tr> + <tr> + <td> + <b>Number of Pictures:</b> + </td> + <td> + <div + id="count" + jsId="countWidget" + dojoType="dijit.form.NumberSpinner" + value="20" + constraints="{min:1,max:20,places:0}" + ></div> + </td> + <td> + <b>Page:</b> + </td> + <td> + <div + id="page" + jsId="pageWidget" + dojoType="dijit.form.NumberSpinner" + value="1" + constraints="{min:1,max:5,places:0}" + ></div> + </td> + </tr> + <tr> + <td> + </td> + <td> + <div dojoType="dijit.form.Button" label="Search" id="searchButton" jsId="searchButtonWidget"></div> + </td> + </tr> + </tbody> + </table> + <hr/> + </blockquote> + <div dojoType="dojox.data.FlickrRestStore" jsId="flickrStore" label="title"></div> + <div dojoType="dojox.data.demos.widgets.FlickrViewList" + store="flickrStore" + id="flickrViews" + jsId="flickrViewsWidget"></div> + +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_FlickrStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_FlickrStore.html new file mode 100644 index 0000000..0adf8d2 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_FlickrStore.html @@ -0,0 +1,161 @@ +<html> +<head> + <!-- + This file is a demo of the FlickrStore, a simple wrapper to the public + feed service of Flickr. This just does very basic queries against + Flickr and loads the results into a list viewing widget. + --> + <title>Demo of FlickrStore</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" + djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.Button"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.NumberSpinner"); + dojo.require("dojox.data.FlickrStore"); + dojo.require("dojox.data.demos.widgets.FlickrViewList"); + + function init(){ + //Function to invoke the search of the FlickrStore + var invokeSearch = function(){ + var request = { query: {} }; + + if(idWidget){ + var id = idWidget.getValue() || ""; + if(id && id.length){ + request.query.userid = id; + } + } + + if(tagsWidget){ + request.query.tags = (tagsWidget.getValue()||"").split(" ").join(","); + } + + + if(tagmodeWidget){ + request.query.tagmode = tagmodeWidget.getValue()||""; + } + + if(countWidget){ + request.count = countWidget.getValue(); + } + flickrViewsWidget.fetch(request); + } + + dojo.connect(flickrViewsWidget, "fetch", function() { + statusWidget.setValue("PROCESSING REQUEST"); + }); + + dojo.connect(flickrViewsWidget, "onComplete", function() { + statusWidget.setValue("PROCESSING COMPLETE."); + }); + + dojo.connect(flickrViewsWidget, "onError", function() { + statusWidget.setValue("ERROR!"); + }); + + //Lastly, link up the search event. + var button = dijit.byId("searchButton"); + dojo.connect(button, "onClick", invokeSearch); + } + dojo.addOnLoad(init); + dojo.addOnLoad(function(){ dijit.byId("searchButton").onClick(); }); + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: FlickrStore Search + </h1> + <p> + This simple demo shows how services, such as Flickr, can be wrapped by + the datastore API. In this demo, you can search public Flickr images + through a simple FlickrStore by specifying a series of tags (separated + by spaces) to search on. The results will be displayed below the + search box. + </p> + <table> + <tbody> + <tr> + <td> + <b>Status:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="status" jsId="statusWidget" disabled="true"></div> + </td> + </tr> + <tr> + <td> + <b>ID:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="userid" jsId="idWidget"></div> + </td> + </tr> + <tr> + <td> + <b>Tags:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="tags" jsId="tagsWidget" value="nature"></div> + </td> + </tr> + <tr> + <td> + <b>Tagmode:</b> + </td> + <td> + <select id="tagmode" + jsId="tagmodeWidget" + dojoType="dijit.form.ComboBox" + autocomplete="false" + value="any" + > + <option>any</option> + <option>all</option> + </select> + </td> + </tr> + <tr> + <td> + <b>Number of Pictures:</b> + </td> + <td> + <div + id="count" + jsId="countWidget" + dojoType="dijit.form.NumberSpinner" + value="20" + constraints="{min:1,max:20,places:0}" + ></div> + </td> + </tr> + <tr> + <td> + </td> + <td> + <div dojoType="dijit.form.Button" label="Search" + id="searchButton" jsId="searchButtonWidget"></div> + </td> + </tr> + </tbody> + </table> + <!-- + The store instance used by this demo. + --> + <div dojoType="dojox.data.FlickrStore" jsId="flickrStore" label="title"></div> + <div dojoType="dojox.data.demos.widgets.FlickrViewList" + store="flickrStore" + id="flickrViews" + jsId="flickrViewsWidget"></div> + +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_GoogleFeedStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleFeedStore.html new file mode 100644 index 0000000..6a4d115 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleFeedStore.html @@ -0,0 +1,113 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <style type="text/css"> + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dijit/themes/tundra/tundra_rtl.css"; + + #output { + width: 400px; + position: absolute; + top: 50px; + } + #entryContent { + margin-left: 450px; + position: absolute; + top: 50px; + background-color: #eee; + height: 800px; + padding: 2px; + } + .search-result { + width: 100%; + border: 2px dashed; + padding: 4px; + } + #entryContent h2 { + text-decoration: underline; + } + .summary { + font-weight: bolder; + } + .tags { + background-color: lightGrey; + } + </style> + + <title>Google Feed Store</title> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.FilteringSelect"); + dojo.require("dojox.dtl"); + dojo.require("dojox.dtl.ext-dojo.NodeList"); + dojo.require("dojox.data.GoogleSearchStore"); + dojo.require("dojox.data.GoogleFeedStore"); + + dojo.addOnLoad(function(){ + dojo.connect(dojo.byId("output"), "onclick", function(evt) { + if(evt.target.tagName != "A"){return true;} + dojo.stopEvent(evt); + + dojo.byId("entryContent").innerHTML = dojo.query(".content", evt.target.parentNode)[0].innerHTML; + + return false; + }) + + }); + + function doSearch() { + var query = { + url: dojo.byId("searchText").value + }; + var request = {query:query}; + + var itemBuffer = []; + var maxBufSize = 8; + var outNode = dojo.byId("output"); + outNode.innerHTML = "Searching..."; + var count = 0; + var template = "GoogleFeedTemplate.html"; + testStore = new dojox.data.GoogleFeedStore(); + function doAppend(items){ + var node = document.createElement("div"); + node.id = "res" + (count++); + outNode.appendChild(node); + dojo.query("#"+node.id).dtl(template, { items: items , store: testStore}); + } + + request.onBegin = function(numItems){ + outNode.innerHTML += ".. found " + numItems + " results"; + }; + + request.onComplete = doAppend; + + var count = dojo.byId("count").value; + request.count = count ? Number(count) : 8; + + testStore.fetch(request); + } + </script> +</head> +<body class="tundra" style="margin:20px;"> + <form> + Text: <select id="searchText" > + <option value="http://shaneosullivan.wordpress.com/feed/">http://shaneosullivan.wordpress.com/feed/</option> + <option value="http://dojocampus.org/content/category/dojo-cookies/feed/">http://dojocampus.org/content/category/dojo-cookies/feed/</option> + <option value="http://www.dojotoolkit.org/aggregator/rss">http://www.dojotoolkit.org/aggregator/rss</option> + </select> + Count: <input id="count" type="text" value="10" width=20/> + <input id="searchButton" type="button" value="store.fetch()" onclick="doSearch()" /> + + <div id="output"> + + </div> + <div id="entryContent"> + + </div> + </form> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore.html new file mode 100644 index 0000000..b82b334 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore.html @@ -0,0 +1,125 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <style type="text/css"> + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dijit/themes/tundra/tundra_rtl.css"; + + .search-result { + float: left; + width: 150px; + border: 2px dashed; + padding: 4px; + } + </style> + + <title>Google Search store</title> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojox.data.GoogleSearchStore"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.FilteringSelect"); + dojo.require("dojox.dtl"); + dojo.require("dojox.dtl.ext-dojo.NodeList"); + + function doSearch() { + var queryOptions = {}; + + var query = {}; + query.text = dojo.byId("searchText").value; + query.type = dojo.byId("typeText").value; + var request = {query:query}; + + var itemBuffer = []; + var maxBufSize = 8; + var outNode = dojo.byId("output"); + outNode.innerHTML = "Searching..."; + var count = 0; + var template = "GoogleTemplate.html"; + switch(query.type) { + case "web" : + testStore = new dojox.data.GoogleSearchStore(); + break; + case "blogs": + testStore = new dojox.data.GoogleBlogSearchStore(); + template = "GoogleTemplateBlog.html"; + break; + case "local": + testStore = new dojox.data.GoogleLocalSearchStore(); + template = "GoogleTemplateLocal.html"; + break; + case "video": + testStore = new dojox.data.GoogleVideoSearchStore(); + template = "GoogleTemplateVideo.html"; + break; + case "news": + testStore = new dojox.data.GoogleNewsSearchStore(); + break; + case "books": + testStore = new dojox.data.GoogleBookSearchStore(); + break; + case "images": + testStore = new dojox.data.GoogleImageSearchStore(); + template = "GoogleTemplateImage.html"; + break; + } + + function doAppend(){ + var node = document.createElement("span"); + node.id = "res" + (count++); + outNode.appendChild(node); + dojo.query("#"+node.id).dtl(template, { items: itemBuffer , store: testStore}); + } + + request.onBegin = function(numItems){ + outNode.innerHTML += ".. found " + numItems + " results"; + }; + + request.onItem = function(item){ + itemBuffer.push(item); + if(itemBuffer.length >= maxBufSize){ + console.log("onItem, buffer length = " + itemBuffer.length + " & maxBufSize = " + maxBufSize); + doAppend(); + itemBuffer = []; + } else { + console.log("onItem, buffer length = " + itemBuffer.length); + } + }; + + request.onComplete = function (items) { + if (itemBuffer.length > 0) { + doAppend(); + } + }; + + var count = dojo.byId("count").value; + request.count = count ? Number(count) : 8; + + testStore.fetch(request); + } + </script> +</head> +<body class="tundra" style="margin:20px;"> + <form> + Text: <input id="searchText" type="text" value="dojo ajax"/> + Count: <input id="count" type="text" value="8" width=20/> + <input id="searchButton" type="button" value="store.fetch()" onclick="doSearch()" /> + Type + <select id="typeText" name="typeText"> + <option selected value="web">Web</option> + <option value="local">Local</option> + <option value="video">Video</option> + <option value="blogs">Blogs</option> + <option value="news">News</option> + <option value="books">Books</option> + <option value="images">Images</option> + </select> + <div id="output"> + + </div> + </form> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore_Grid.html b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore_Grid.html new file mode 100644 index 0000000..99dacaa --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_GoogleSearchStore_Grid.html @@ -0,0 +1,71 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <style type="text/css"> + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dijit/themes/tundra/tundra_rtl.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + @import "../../../dojox/grid/resources/tundraGrid.css"; + </style> + + <title>Google Search Store with Grid</title> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojox.grid.DataGrid"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.Button"); + dojo.require("dojox.wire.ml.Invocation"); + dojo.require("dojox.wire.ml.Transfer"); + dojo.require("dojox.wire.ml.Action"); + dojo.require("dojox.data.GoogleSearchStore"); + + var href = function(value) { + return "<a href=\"" + value + "\" target=\"_blank\">" + value + "</a>"; + } + var layoutResults = [ + [ + { name: "Title", field: "title", width: 10 }, + { name: "Summary", field: "content", width: "auto"}, + { name: "URL", field: "url", width: 20, formatter: href } + ] + ]; + var newQuery = {text: 'dojo ajax' }; + </script> +</head> +<body class="tundra" style="margin:20px;"> + <h1>dojox.data.GoogleSearchStore:</h1> + <i>Displays a list of results from a google query.</i> + <div dojoType="dojox.data.GoogleSearchStore" jsId="googleStore"></div> + <br> + <br> + <div dojoType="dijit.form.TextBox" id="searchText" value="dojo ajax"></div> + <button dojoType="dijit.form.Button" id="searchButton">Search</button> + <br> + <br> + <br> + + <div dojoType="dojox.grid.DataGrid" + rowsPerPage="8" + store="googleStore" + structure="layoutResults" + query="{ text: 'dojo ajax' }" + jsId="grid" + style="width: 800px; height: 500px;" + > + </div> + + <!-- + Link the button to updating the query, then triggering the refresh of the grid. + Handy use of dojox.wire! + --> + <div dojoType="dojox.wire.ml.Action" + trigger="searchButton" + triggerEvent="onClick"> + <div dojoType="dojox.wire.ml.Transfer" source="searchText.value" target="newQuery.text"></div> + <div dojoType="dojox.wire.ml.Invocation" object="grid" method="setQuery" parameters="newQuery"></div> + </div> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_LazyLoad.html b/js/dojo-1.7.2/dojox/data/demos/demo_LazyLoad.html new file mode 100644 index 0000000..358ce84 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_LazyLoad.html @@ -0,0 +1,66 @@ +<!-- + This file is a simple loader for the Lazy Load demo of a Datastore. In this + Example, a simple extension of ItemFileReadStore that can do rudimentary lazy-loading + of items into the store is used to showcase how Datastores can hide how data + is loaded from the widget. As long as the widget implements to the Dojo.data API + spec, then it should be able to use most datastores as input sources for its + values. +--> +<html> +<head> + <title>Demo of Lazy Loading Datastore</title> + <style type="text/css"> + + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true, usePlainJson: true"></script> + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dojox.data.demos.stores.LazyLoadJSIStore"); + dojo.require("dijit.Tree"); + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: Lazy Loading Datastore used by dijit.Tree + </h1> + <hr> + <h3> + Description: + </h3> + <p> + This simple demo shows how the dijit.Tree widget can work with a Datastore that does lazy-loading of values into the tree. + In this demo, the Datastore is an extension of ItemFileReadStore that overrides the <i>isItemLoaded()</i> and <i>loadItem()</i> functions of + with ones that can detect 'stub' items and use the data in the stub item to load the real data for that item when it + is required. In this demo, the real data is required when one of the tree nodes is expanded. + </p> + <p> + The key thing to note is that all the lazy-loading logic (how to locate the data from the backend and so forth) is encapsulated + into the store functions. The dijit.Tree widget only knows about and uses the dojo.data.Read API interfaces to call to the store to + get items, test if child items are fully loaded or not, and to invoke the <i>loadItem()</i> function on items that are not yet fully + loaded but have been requested to be expanded into view. It has no knowledge of how the store actually goes and gets the data. + </p> + + <blockquote> + + <!-- + The store instance used by this demo. + --> + <div dojoType="dojox.data.demos.stores.LazyLoadJSIStore" jsId="continentStore" + url="geography/root.json"></div> + + <!-- + Display the toplevel tree with items that have an attribute of 'type', + with value of 'contintent' + --> + <b>Continents</b> + <div dojoType="dijit.Tree" id=tree label="Continents" store="continentStore" query="{type:'continent'}" + labelAttr="name" typeAttr="type"></div> + </blockquote> + +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_MultiStores.html b/js/dojo-1.7.2/dojox/data/demos/demo_MultiStores.html new file mode 100644 index 0000000..29ede27 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_MultiStores.html @@ -0,0 +1,116 @@ +<!-- + This file is a demo of multiple dojo.data aware widgets using different datastore implementations for displaying data. +--> +<html> +<head> + <title>Demo of Multiple Widgets using different Datastores</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.FilteringSelect"); + dojo.require("dijit.Tree"); + dojo.require("dijit.tree.ForestStoreModel"); + + dojo.require("dojox.data.OpmlStore"); + dojo.require("dojox.data.XmlStore"); + dojo.require("dojo.data.ItemFileReadStore"); + + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: Multiple DataStore implementations with dojo.data aware Widgets + </h1> + <hr> + <h3> + Description: + </h3> + <p> + This simple demo shows how widgets which know only the dojo.data interfaces can work with data sources of varying formats. In this case an OpmlStore + and a ItemFileReadStore are used to house the same data in different formats. + </p> + + <blockquote> + + <!-- + The store instances used by this demo. + --> + <div dojoType="dojo.data.ItemFileReadStore" url="geography.json" jsId="ifrGeoStore"></div> + <div dojoType="dojox.data.OpmlStore" url="geography.xml" label="text" jsId="opmlGeoStore"></div> + <div dojoType="dojox.data.XmlStore" url="geography2.xml" label="text" attributeMap="{'text': '@text'}" jsId="xmlGeoStore"></div> + + <h3> + Widgets using OpmlStore: + </h3> + <blockquote> + <b>ComboBox:</b><br> + <input dojoType="dijit.form.ComboBox" id="combo1" name="combo1" class="medium" store="opmlGeoStore" searchAttr="text" query="{}"></input> + <br> + <br> + <b>Filtering Select:</b><br> + <input dojoType="dijit.form.FilteringSelect" id="fs1" name="fs1" class="medium" store="opmlGeoStore" searchAttr="text" query="{}"></input> + <br> + <br> + + <b>Tree:</b><br> + <div dojoType="dijit.tree.ForestStoreModel" jsId="opmlModel" + store="opmlGeoStore" query="{}" + rootId="Continents" rootLabel="Continents" childrenAttrs="children"> + </div> + <div dojoType="dijit.Tree" id="tree1" model="opmlModel"></div> + </blockquote> + + <h3> + Widgets using ItemFileReadStore: + </h3> + <blockquote> + <b>ComboBox:</b><br> + <input dojoType="dijit.form.ComboBox" id="combo2" name="combo2" class="medium" store="ifrGeoStore" searchAttr="name" query="{}"></input> + <br> + <br> + + <b>Filtering Select:</b><br> + <input dojoType="dijit.form.FilteringSelect" id="fs2" name="fs2" class="medium" store="ifrGeoStore" searchAttr="text" query="{}"></input> + <br> + <br> + + <b>Tree:</b><br> + <div dojoType="dijit.tree.ForestStoreModel" jsId="ifrModel" + store="ifrGeoStore" query="{}" + rootId="Continents" rootLabel="Continents" childrenAttrs="children"> + </div> + <div dojoType="dijit.Tree" id="tree2" model="ifrModel"></div> + </blockquote> + + <h3> + Widgets using XmlStore: + </h3> + <blockquote> + <b>ComboBox:</b><br> + <input dojoType="dijit.form.ComboBox" id="combo3" name="combo3" class="medium" store="xmlGeoStore" searchAttr="text" query="{}"></input> + <br> + <br> + + <b>Filtering Select:</b><br> + <input dojoType="dijit.form.FilteringSelect" id="fs3" name="fs3" class="medium" store="xmlGeoStore" searchAttr="text" query="{}"></input> + <br> + <br> + + <b>Tree:</b><br> + <div dojoType="dijit.tree.ForestStoreModel" jsId="xmlModel" + store="xmlGeoStore" query="{}" + rootId="Continents" rootLabel="Continents" childrenAttrs="childNodes"> + </div> + <div dojoType="dijit.Tree" id="tree3" model="xmlModel"></div> + </blockquote> + </blockquote> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_OpenSearchStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_OpenSearchStore.html new file mode 100644 index 0000000..e8bcc13 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_OpenSearchStore.html @@ -0,0 +1,215 @@ +<!-- + This file is a demo of the OpenSearchStore, a simple wrapper to any OpenSearch compliant + search engine. + + Note, the simple proxy requires a curl-enabled PHP install +--> +<html> +<head> + <title>Demo of OpenSearchStore</title> + <style type="text/css"> + + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + @import "./openSearchDemo.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.Button"); + dojo.require("dijit.form.FilteringSelect"); + dojo.require("dijit.form.CheckBox"); + dojo.require("dijit.form.NumberSpinner"); + dojo.require("dijit.Tree"); + dojo.require("dojox.data.OpenSearchStore"); + + function init(){ + var fViewWidgets = []; + + //Set up an onComplete handler for OpenSearchData + function onComplete(items, request){ + if(items.length > 0){ + var ul = dojo.byId("searchResults"); + var test; + var li; + for(var i=0; i<items.length; i++){ + li = dojo.doc.createElement("li"); + li.innerHTML = openSearchStore.getValue(items[i], "content"); + ul.appendChild(li); + } + } + statusWidget.attr('value', "PROCESSING COMPLETE."); + } + //What to do if a search fails... + function onError(error, request){ + statusWidget.attr('value', "PROCESSING ERROR."); + } + + //Function to invoke the search of the openSearchStore + function invokeSearch(){ + var tbody = dojo.byId("searchResults"); + while(tbody.childNodes.length){ + var node = tbody.childNodes.item(0); + node.parentNode.removeChild(node); + } + var request = { + query: {}, + onComplete: onComplete, + onError: onError + }; + if(searchTermsWidget){ + var searchTerms = searchTermsWidget.attr('value'); + if(searchTerms && searchTerms !== ""){ + var searchTermsArray = searchTerms.split(" "); + searchTerms = ""; + for(var i = 0; i < searchTermsArray.length; i++){ + searchTerms = searchTerms + searchTermsArray[i]; + if(i < (searchTermsArray.length - 1)){ + searchTerms += "," + } + } + request.query.searchTerms = searchTerms; + } + } + + if(countWidget){ + request.count = countWidget.attr('value'); + } + + if(statusWidget){ + statusWidget.attr('value', "PROCESSING REQUEST"); + } + + openSearchStore.fetch(request); + } + + //Lastly, link up the search event. + dojo.connect(dijit.byId("searchButton"), 'onClick', invokeSearch); + var currentArgs = {url: 'http://intertwingly.net/search/'}; + var oldProcess = null; + function setTransform(state){ + if(state){ + oldProcess = openSearchStore.processItem; + switch(currentArgs.url){ + case 'http://intertwingly.net/search/': + openSearchStore.processItem = intertwinglyTransform; + break; + case 'http://www.shutterpoint.com/opensearch.xml': + openSearchStore.processItem = shutterpointTransform; + break; + case 'http://technorati.com/osd.xml': + openSearchStore.processItem = technoratiTransform; + break; + } + }else if(oldProcess !== null){ + openSearchStore.processItem = oldProcess; + } + } + dojo.connect(dijit.byId('transformItem'), 'onChange', function(state){ + setTransform(state); + }); + dojo.connect(dijit.byId('urlSelector'), 'onChange', function(args){ + currentArgs = dojo.fromJson(args); + currentArgs.url = 'openSearchProxy.php?osd=true&url='+currentArgs.url; + openSearchStore.close(); + openSearchStore = new dojox.data.OpenSearchStore(currentArgs); + if(dijit.byId('transformItem').checked){ + setTransform(true); + } + }); + + var intertwinglyTransform = function(item, attribute){ + function removeAll(/*NodeList*/list){ + while(list.length) { + var node = list.item(0); + node.parentNode.removeChild(node); + } + } + var content = item.node.getElementsByTagName("content").item(0); + // Remove all blockquote elements + removeAll(content.getElementsByTagName("blockquote")); + // Remove all pre-formatted elements + removeAll(content.getElementsByTagName("pre")); + return openSearchStore._getNodeXml(content, true); + }; + + var shutterpointTransform = function(item, attribute){ + var description = item.node.getElementsByTagName("description").item(0); + var div = dojo.doc.createElement("div"); + div.innerHTML = description.childNodes.item(0).nodeValue; + //Of the description children, remove the divs (to only leave the images) + for(var i=0; i<div.childNodes.length; i++){ + var node = div.childNodes.item(i); + if(node.tagName.toLowerCase() === "div") + node.parentNode.removeChild(node); + } + return openSearchStore._getNodeXml(div, true); + }; + + var technoratiTransform = function(item, attribute){ + function removeAll(/*NodeList*/list){ + while(list.length) { + var node = list.item(0); + node.parentNode.removeChild(node); + } + } + removeAll(item.node.getElementsByTagName("blockquote")); + return item.innerHTML; + }; + } + dojo.addOnLoad(init); + </script> +</head> + +<body class="tundra"> + <h1>DEMO: OpenSearchStore Search</h1> + <hr /> + <h3>Description:</h3> + <p> + This simple demo shows how services, such as an OpenSearch compliant search service, can be wrapped by the datastore API. In this demo, you can search public search engines through a simple OpenSearchStore by specifying a series of search terms (separated by spaces) to search on. The results will be displayed below the search box. + </p> + <p> + <b>NOTE: This demo makes use of a simple PHP based proxy script. The proxy script requires cURL support in PHP. Without cURL support, the demo will throw errors.</b> + </p> + <label for="urlSelector">URL of OpenSearchDocument:</label> + <select dojoType="dijit.form.FilteringSelect" + id="urlSelector" + name="urlSelector" + autoComplete="true"> + <option value="{url: 'http://intertwingly.net/search/'}">http://intertwingly.net/search/</option> + <option value="{url: 'http://www.shutterpoint.com/opensearch.xml'}">http://www.shutterpoint.com/opensearch.xml</option> + <option value="{url: 'http://technorati.com/osd.xml', itemPath: '.hentry'}">http://technorati.com/osd.xml</option> + </select> + <label for="transformItem">Apply transform function?</label> + <input dojoType="dijit.form.CheckBox" + type="checkbox" + id="transformItem" + name="transformItem"> + </input> + <hr /> + <label for="status">Status:</label> + <div dojoType="dijit.form.TextBox" maxLength="50" id="status" name="status" jsId="statusWidget" disabled="true"></div> + <label for="searchTerms">Search For:</label> + <div dojoType="dijit.form.TextBox" maxLength="50" id="searchTerms" name="searchTerms" jsId="searchTermsWidget" value="javascript"></div> + <label for="count">Number of Results:</label> + <div id="count" + name="count" + jsId="countWidget" + dojoType="dijit.form.NumberSpinner" + value="20" + constraints="{min:1,max:20}"> + </div> + <div dojoType="dijit.form.Button" label="Search" id="searchButton" jsId="searchButtonWidget"></div> + <hr/> + <div dojoType="dojox.data.OpenSearchStore" + url="openSearchProxy.php?osd=true&url=http://intertwingly.net/search/" + jsId="openSearchStore"> + </div> + <ul id="searchResults"></ul> + +</body> +</html>
\ No newline at end of file diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_PicasaStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_PicasaStore.html new file mode 100644 index 0000000..47f73a4 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_PicasaStore.html @@ -0,0 +1,190 @@ +<!-- + This file is a demo of the PicasaStore, a simple wrapper to the public feed service + of Picasa. This just does very basic queries against Picasa and loads the results + into a list viewing widget. +--> +<html> +<head> + <title>Demo of PicasaStore</title> + <style type="text/css"> + + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + @import "./picasaDemo.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojo.parser"); + dojo.require("dijit.form.TextBox"); + dojo.require("dijit.form.Button"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.NumberSpinner"); + dojo.require("dijit.Tree"); + dojo.require("dojox.data.PicasaStore"); + dojo.require("dojox.data.demos.widgets.PicasaViewList"); + dojo.require("dojox.data.demos.widgets.PicasaView"); + + function init(){ + var fViewWidgets = []; + + //Set up an onComplete handler for picasaData + function onComplete(items, request){ + picasaViewsWidget.clearList(); + if(items.length > 0){ + for(var i = 0; i < items.length; i++){ + var picasaData = { + title: picasaStore.getValue(items[i],"title"), + author: picasaStore.getValue(items[i],"author"), + description: picasaStore.getValue(items[i],"description"), + iconUrl: picasaStore.getValue(items[i],"imageUrlSmall"), + imageUrl: picasaStore.getValue(items[i],"imageUrl") + } + picasaViewsWidget.addView(picasaData); + } + } + statusWidget.attr("value", "PROCESSING COMPLETE."); + + } + //What to do if a search fails... + function onError(error, request){ + console.debug(error); + picasaViewsWidget.clearList(); + statusWidget.attr("value", "PROCESSING ERROR."); + } + + //Function to invoke the search of the PicasaStore + function invokeSearch(){ + var request = { + query: {}, + onComplete: onComplete, + onError: onError + }; + + if(idWidget){ + var userid = idWidget.attr("value"); + if(userid && userid !== ""){ + request.query.userid = userid; + } + } + if(tagsWidget){ + var tags = tagsWidget.attr("value"); + if(tags && tags !== ""){ + var tagsArray = tags.split(" "); + tags = ""; + for(var i = 0; i < tagsArray.length; i++){ + tags = tags + tagsArray[i]; + if(i < (tagsArray.length - 1)){ + tags += "," + } + } + request.query.tags = tags; + } + } + if(countWidget){ + request.count = countWidget.attr("value"); + } + + if(startWidget){ + request.query.start = startWidget.attr("value"); + } + + if(statusWidget){ + statusWidget.attr("value","PROCESSING REQUEST"); + } + + picasaStore.fetch(request); + } + + //Lastly, link up the search event. + var button = dijit.byId("searchButton"); + dojo.connect(button, "onClick", invokeSearch); + } + dojo.addOnLoad(init); + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: PicasaStore Search + </h1> + <hr> + <h3> + Description: + </h3> + <p> + This simple demo shows how services, such as Picasa, can be wrapped by the datastore API. In this demo, you can search public Picasa images through a simple PicasaStore by specifying a series of tags (separated by spaces) to search on. The results will be displayed below the search box. + </p> + <p> + For fun, search on the 3dny tag! + </p> + + <blockquote> + + <!-- + The store instance used by this demo. + --> + <table> + <tbody> + <tr> + <td> + <b>Status:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="status" jsId="statusWidget" disabled="true"></div> + </td> + </tr> + <tr> + <td> + <b>ID:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="userid" jsId="idWidget"></div> + </td> + </tr> + <tr> + <td> + <b>Query:</b> + </td> + <td> + <div dojoType="dijit.form.TextBox" size="50" id="tags" jsId="tagsWidget" value="flower"></div> + </td> + </tr> + <tr> + <td> + <b>Number of Pictures:</b> + </td> + <td> + <div + id="start" + jsId="startWidget" + dojoType="dijit.form.NumberSpinner" + value="1" + constraints="{min:1,places:0}" + ></div> + <div + id="count" + jsId="countWidget" + dojoType="dijit.form.NumberSpinner" + value="20" + constraints="{min:1,max:100,places:0}" + ></div> + </td> + </tr> + <tr> + <td> + </td> + <td> + <div dojoType="dijit.form.Button" label="Search" id="searchButton" jsId="searchButtonWidget"></div> + </td> + </tr> + </tbody> + </table> + <hr/> + </blockquote> + <div dojoType="dojox.data.PicasaStore" jsId="picasaStore" label="title"></div> + <div dojoType="dojox.data.demos.widgets.PicasaViewList" id="picasaViews" jsId="picasaViewsWidget"></div> + +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_ComboBox.html b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_ComboBox.html new file mode 100644 index 0000000..6be9265 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_ComboBox.html @@ -0,0 +1,58 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Dojox QueryReadStore+ComboBox Demo</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug:true, parseOnLoad: true"></script> +</head> +<body class="tundra"> + + <h1 class="testTitle">Dojox QueryReadStore + ComboBox demo</h1> + + <h2>Everything is created ONLY in markup</h2> + <div class="dijitContainer"> + <div style="float:left;"> + <div dojoType="dojox.data.QueryReadStore" + jsId="store1" + url="../tests/stores/QueryReadStore.php" + requestMethod="post"></div> + <div dojoType="dijit.form.ComboBox" id="cb1" store="store1" pageSize="10" autoComplete="false"></div> + <button dojoType="dijit.form.Button" onclick="dijit.byId('cb1').reset()">reset</button> + </div> + <div style="float:left; margin-left:5em;"> + var w = dijit.byId("cb1"); + <br /><input id="value1" type="text" /> = w.value + <br /><input id="itemId1" type="text" /> = w.item ? w.store.getValue(w.item, "id") : "-" + <br /><input id="displayedValue1" type="text" /> = w.attr("displayedValue") + <br /><input id="isValid1" type="text" /> = w.isValid() + <br /><button dojoType="dijit.form.Button" onclick="refresh1()">refresh</button> + </div> + </div> + + <script type="text/javascript"> + dojo.require("dojox.data.QueryReadStore"); + dojo.require("dijit.form.ComboBox"); + dojo.require("dijit.form.Button"); + + var w = null; + var refresh1 = function() { + dojo.byId("value1").value = w.value; + dojo.byId("itemId1").value = w.item ? w.store.getValue(w.item, "id") : "-"; + dojo.byId("displayedValue1").value = w.attr("displayedValue"); + dojo.byId("isValid1").value = w.isValid(); + }; + dojo.addOnLoad(function() { + w = dijit.byId("cb1"); + dojo.connect(w.domNode, "onkeyup", refresh1); + dojo.connect(w, "onBlur", refresh1); + dojo.connect(w, "onChange", refresh1); + refresh1(); + }); + </script> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_FilteringSelect.html b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_FilteringSelect.html new file mode 100644 index 0000000..6f89167 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_FilteringSelect.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Dojox QueryReadStore+FilteringSelect Demo</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug:true, parseOnLoad: true"></script> +</head> +<body class="tundra"> + + <h1 class="testTitle">Dojox QueryReadStore + FilteringSelect demo</h1> + + <h2>Everything is created ONLY in markup</h2> + <div style="float:left;"> + <div dojoType="dojox.data.QueryReadStore" + jsId="store1" + url="../tests/stores/QueryReadStore.php" + requestMethod="post"></div> + <div dojoType="dijit.form.FilteringSelect" id="fs1" store="store1" pageSize="10" autoComplete="false"></div> + <button dojoType="dijit.form.Button" onclick="dijit.byId('fs1').reset()">reset</button> + </div> + <div style="float:left; margin-left:5em;"> + var w = dijit.byId("fs1"); + <br /><input id="value1" type="text" /> = w.value + <br /><input id="itemId1" type="text" /> = w.item ? w.store.getValue(w.item, "id") : "-" + <br /><input id="displayedValue1" type="text" /> = w.getDisplayedValue() + <br /><input id="isValid1" type="text" /> = w.isValid() + <br /><button dojoType="dijit.form.Button" onclick="refresh1()">refresh</button> + </div> + + <script type="text/javascript"> + dojo.require("dojox.data.QueryReadStore"); + dojo.require("dijit.form.FilteringSelect"); + dojo.require("dijit.form.Button"); + + var w = null; + var refresh1 = function() { + dojo.byId("value1").value = w.value; + dojo.byId("itemId1").value = w.item ? w.store.getValue(w.item, "id") : "-"; + dojo.byId("displayedValue1").value = w.getDisplayedValue(); + dojo.byId("isValid1").value = w.isValid(); + }; + dojo.addOnLoad(function() { + w = dijit.byId("fs1"); + dojo.connect(w.domNode, "onkeyup", refresh1); + dojo.connect(w, "onBlur", refresh1); + dojo.connect(w, "onChange", refresh1); + refresh1(); + }); + </script> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_grid.html b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_grid.html new file mode 100644 index 0000000..7035ebd --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_QueryReadStore_grid.html @@ -0,0 +1,110 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Dojox QueryReadStore+grid Demo</title> + <style type="text/css"> + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + @import "../../../dijit/tests/css/dijitTests.css"; + /* BE SURE TO NEVER FORGET IMPORTING THE GRID's CSS, or you will wonder why the + grid looks so strange (or even think that it doesnt work) */ + @import "../../../dojox/grid/resources/tundraGrid.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug:true, parseOnLoad: true, useCommentedJson: true"></script> + <script type="text/javascript"> + dojo.require("dojox.grid.DataGrid"); + dojo.require("dojox.data.QueryReadStore"); + dojo.require("dojo.parser"); // scan page for widgets and instantiate them + var gridLayout = [ + new dojox.grid.cells.RowIndex({ name: "row #", width: 5, styles: "text-align: right;" }), + { + name: "id", + field: "id", + styles: "text-align:right;", + width:5 + }, + { + name: "Name", + field: "name", + width:20 + //formatter: rs.chunk.adminUser.grid.formatUser + }, + { + name: "Capital", + field: "capital", + width:20 + //formatter: rs.chunk.adminUser.grid.formatUser + }, + { + name: "Label", + width:20, + //styles: "text-align:right;", + field: "label" + //formatter: phpr.grid.formatDate + }, + { + name: "Abbrev.", + width:5, + //styles: "text-align:right;", + field: "abbreviation" + //formatter: phpr.grid.formatDate + } + ]; + // Connect the store AFTER the page is loaded, since we can only access + // the widget then, since it will be created just before dojo.addOnLoad() is called. + var store = null; + dojo.addOnLoad(function() { + store = new dojox.data.QueryReadStore({ + url:"../tests/stores/QueryReadStore.php", + requestMethod:"post" + }); + grid1.setStore(store); + grid1.setStructure(gridLayout); + }); + + var lastSearchValue = ""; + function doSearch(el) { + if (el.value!=lastSearchValue) { + lastSearchValue = el.value; + grid1.filter({name:el.value}); + grid2.filter({name:el.value}); + } + } + </script> +</head> +<body class="tundra"> + + <h1 class="testTitle">Dojox QueryReadStore + Grid demo - paging, sortable and filterable all server-side</h1> + + <h2>The grid is in HTML, store, etc. are JS, sorting is added by extending the model class</h2> + <b>Capabilities:</b> load data from server, show data, paging (30 rows at a time), sort, filter<br /> + You can see that data are loaded upon demand by scrolling down in the grid below line #30, + open FireBug and you see a server request being issued, to retreive another 30 rows/items.<br /> + <br /><br /> + <input type="text" onkeyup="doSearch(this)" /> + <div id="grid1" jsid="grid1" dojoType="dojox.grid.DataGrid" query="{ name: '*' }" rowsPerPage="30" style="height:300px; width:800px;"></div> + + <h2>The store and grid are "generated" and connected in HTML, filtering is done via JS</h2> + This store is by default sorted descending by name (not as the one above, which is ascending). + <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: 'capital', descending: true}]" + rowsPerPage="30"></div>--> + <div dojoType="dojox.grid.DataGrid" + id="grid2" jsid="grid2" + store="store2" + query="{ name: '*' }" + rowsPerPage="30" + structure="gridLayout" + style="height:300px; width:800px;"></div> + + + +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/demo_WikipediaStore.html b/js/dojo-1.7.2/dojox/data/demos/demo_WikipediaStore.html new file mode 100644 index 0000000..bc290b0 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/demo_WikipediaStore.html @@ -0,0 +1,82 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> +<title>Wikipedia Data Store</title> +<style type="text/css"> +@import "../../../dojo/resources/dojo.css"; +@import "../../../dijit/themes/tundra/tundra.css"; +h1 { margin-bottom: 1em; } +</style> +<script type="text/javascript"> +//<![CDATA[ +djConfig = { isDebug: true }; +//]]> +</script> +<script type="text/javascript" src="../../../dojo/dojo.js"></script> +<script type="text/javascript"> +//<![CDATA[ +dojo.require("dojox.data.WikipediaStore"); +var store = new dojox.data.WikipediaStore(); + +function doSearch(){ + var outNode = dojo.byId("output"); + outNode.innerHTML = "Searching..."; + + function loadArticle(article){ + var request = { + query: { + title: article + }, + onItem: function(item, req){ + var title = store.getValue(item, "title"); + var text = store.getValue(item, "text")["*"]; + outNode.innerHTML = "<h1>" + title + "</h1>" + text; + } + }; + console.log("Article request: ", request); + store.fetch(request); + } + + + var request = { + query: { + action: "query", + text: dojo.byId("searchText").value + }, + count: dojo.byId("count").value, + onBegin: function(count){ + outNode.innerHTML += " found " + count + " results.<br>Click one to load the article."; + }, + onItem: function(item, req){ + console.debug(item); + var node = document.createElement("a"); + node.href = "#"; + node.onclick = function(){ + console.log("clicked ", this.innerHTML); + loadArticle(this.innerHTML); + }; + node.style.padding = "6px"; + node.style.display = "block"; + node.innerHTML = store.getValue(item, "title"); + outNode.appendChild(node); + } + }; + console.log("Request: ", request); + store.fetch(request); +} +//]]> +</script> +</head> +<body class="tundra" style="margin:20px;"> + <form action="#"> + <p> + Text: <input id="searchText" type="text" value="dojo toolkit"> + Count: <input id="count" type="text" value="8" size="3"> + <input id="searchButton" type="button" value="store.fetch()" onclick="doSearch()"> + </p> + + <div id="output" style="padding:0 20px;"> + </div> + </form> +</body> +</html> diff --git a/js/dojo-1.7.2/dojox/data/demos/geography.json b/js/dojo-1.7.2/dojox/data/demos/geography.json new file mode 100644 index 0000000..c2f01bb --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography.json @@ -0,0 +1,45 @@ +{ identifier: 'name', + label: 'name', + items: [ + { name:'Africa', type:'continent', children:[ + { name:'Egypt', type:'country' }, + { name:'Kenya', type:'country', children:[ + { name:'Nairobi', type:'city' }, + { name:'Mombasa', type:'city' } ] + }, + { name:'Sudan', type:'country', children: + { name:'Khartoum', type:'city' } + } ] + }, + { name:'Asia', type:'continent', children:[ + { name:'China', type:'country' }, + { name:'India', type:'country' }, + { name:'Russia', type:'country' }, + { name:'Mongolia', type:'country' } ] + }, + { name:'Australia', type:'continent', population:'21 million', children: + { name:'Commonwealth of Australia', type:'country', population:'21 million'} + }, + { name:'Europe', type:'continent', children:[ + { name:'Germany', type:'country' }, + { name:'France', type:'country' }, + { name:'Spain', type:'country' }, + { name:'Italy', type:'country' } ] + }, + { name:'North America', type:'continent', children:[ + { name:'Mexico', type:'country', population:'108 million', area:'1,972,550 sq km', children:[ + { name:'Mexico City', type:'city', population:'19 million', timezone:'-6 UTC'}, + { name:'Guadalajara', type:'city', population:'4 million', timezone:'-6 UTC' } ] + }, + { name:'Canada', type:'country', population:'33 million', area:'9,984,670 sq km', children:[ + { name:'Ottawa', type:'city', population:'0.9 million', timezone:'-5 UTC'}, + { name:'Toronto', type:'city', population:'2.5 million', timezone:'-5 UTC' }] + }, + { name:'United States of America', type:'country' } ] + }, + { name:'South America', type:'continent', children:[ + { name:'Brazil', type:'country', population:'186 million' }, + { name:'Argentina', type:'country', population:'40 million' } ] + } ] +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography.xml b/js/dojo-1.7.2/dojox/data/demos/geography.xml new file mode 100644 index 0000000..070a8c1 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<opml version="1.0"> + <head> + <title>geography.opml</title> + <dateCreated>2006-11-10</dateCreated> + <dateModified>2006-11-13</dateModified> + <ownerName>Magellan, Ferdinand</ownerName> + </head> + <body> + <outline text="Africa" type="continent"> + <outline text="Egypt" type="country"/> + <outline text="Kenya" type="country"> + <outline text="Nairobi" type="city"/> + <outline text="Mombasa" type="city"/> + </outline> + <outline text="Sudan" type="country"> + <outline text="Khartoum" type="city"/> + </outline> + </outline> + <outline text="Asia" type="continent"> + <outline text="China" type="country"/> + <outline text="India" type="country"/> + <outline text="Russia" type="country"/> + <outline text="Mongolia" type="country"/> + </outline> + <outline text="Australia" type="continent" population="21 million"> + <outline text="Australia" type="country" population="21 million"/> + </outline> + <outline text="Europe" type="continent"> + <outline text="Germany" type="country"/> + <outline text="France" type="country"/> + <outline text="Spain" type="country"/> + <outline text="Italy" type="country"/> + </outline> + <outline text="North America" type="continent"> + <outline text="Mexico" type="country" population="108 million" area="1,972,550 sq km"> + <outline text="Mexico City" type="city" population="19 million" timezone="-6 UTC"/> + <outline text="Guadalajara" type="city" population="4 million" timezone="-6 UTC"/> + </outline> + <outline text="Canada" type="country" population="33 million" area="9,984,670 sq km"> + <outline text="Ottawa" type="city" population="0.9 million" timezone="-5 UTC"/> + <outline text="Toronto" type="city" population="2.5 million" timezone="-5 UTC"/> + </outline> + <outline text="United States of America" type="country"/> + </outline> + <outline text="South America" type="continent"> + <outline text="Brazil" type="country" population="186 million"/> + <outline text="Argentina" type="country" population="40 million"/> + </outline> + </body> +</opml> diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Argentina/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Argentina/data.json new file mode 100644 index 0000000..17ba291 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Argentina/data.json @@ -0,0 +1,5 @@ +{ + name:'Argentina', + type:'country', + population:'40 million' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Brazil/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Brazil/data.json new file mode 100644 index 0000000..a326c24 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Brazil/data.json @@ -0,0 +1,5 @@ +{ + name:'Brazil', + type:'country', + population:'186 million' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Ottawa/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Ottawa/data.json new file mode 100644 index 0000000..df3bbc8 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Ottawa/data.json @@ -0,0 +1,6 @@ +{ + name:'Ottawa', + type:'city', + population:'0.9 million', + timezone:'-5 UTC' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Toronto/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Toronto/data.json new file mode 100644 index 0000000..534409b --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/Toronto/data.json @@ -0,0 +1,6 @@ +{ + name:'Toronto', + type:'city', + population:'2.5 million', + timezone:'-5 UTC' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Canada/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/data.json new file mode 100644 index 0000000..6ef34ed --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Canada/data.json @@ -0,0 +1,10 @@ +{ + name:'Canada', + type:'country', + population:'33 million', area:'9,984,670 sq km', + children:[ + {stub:'Ottawa'}, + {stub:'Toronto'} + ] +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/China/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/China/data.json new file mode 100644 index 0000000..72c29cc --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/China/data.json @@ -0,0 +1,4 @@ +{ + name:'China', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Commonwealth of Australia/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Commonwealth of Australia/data.json new file mode 100644 index 0000000..e093295 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Commonwealth of Australia/data.json @@ -0,0 +1,5 @@ +{ + name:'Commonwealth of Australia', + type:'country', + population:'21 million' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Egypt/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Egypt/data.json new file mode 100644 index 0000000..d355537 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Egypt/data.json @@ -0,0 +1,5 @@ +{ + name:'Egypt', + type:'country' +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/France/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/France/data.json new file mode 100644 index 0000000..5b5f3c3 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/France/data.json @@ -0,0 +1,4 @@ +{ + name:'France', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Germany/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Germany/data.json new file mode 100644 index 0000000..1656257 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Germany/data.json @@ -0,0 +1,4 @@ +{ + name:'Germany', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/India/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/India/data.json new file mode 100644 index 0000000..3103f89 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/India/data.json @@ -0,0 +1,4 @@ +{ + name:'India', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Italy/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Italy/data.json new file mode 100644 index 0000000..6e6b076 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Italy/data.json @@ -0,0 +1,4 @@ +{ + name:'Italy', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Mombasa/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Mombasa/data.json new file mode 100644 index 0000000..28aa849 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Mombasa/data.json @@ -0,0 +1,5 @@ +{ + name:'Mombasa', + type:'city', + population: "Unknown" +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Nairobi/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Nairobi/data.json new file mode 100644 index 0000000..f5658ec --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/Nairobi/data.json @@ -0,0 +1,5 @@ +{ + name:'Nairobi', + type:'city', + population: "Unknown" +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/data.json new file mode 100644 index 0000000..9253c25 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Kenya/data.json @@ -0,0 +1,9 @@ +{ + name:'Kenya', + type:'country', + children:[ + {stub:'Nairobi'}, + {stub:'Mombasa'} + ] +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Guadalajara/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Guadalajara/data.json new file mode 100644 index 0000000..059fc82 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Guadalajara/data.json @@ -0,0 +1,7 @@ +{ + name:'Guadalajara', + type:'city', + population:'4 million', + timezone:'-6 UTC' +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Mexico City/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Mexico City/data.json new file mode 100644 index 0000000..8c67622 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/Mexico City/data.json @@ -0,0 +1,6 @@ +{ + name:'Mexico City', + type:'city', + population:'19 million', + timezone:'-6 UTC' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/data.json new file mode 100644 index 0000000..aa381e4 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Mexico/data.json @@ -0,0 +1,10 @@ +{ + name:'Mexico', + type:'country', + population:'108 million', + area:'1,972,550 sq km', + children:[ + {stub:'Mexico City'}, + {stub:'Guadalajara'} + ] +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Mongolia/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Mongolia/data.json new file mode 100644 index 0000000..4c60b22 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Mongolia/data.json @@ -0,0 +1,4 @@ +{ + name:'Mongolia', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Russia/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Russia/data.json new file mode 100644 index 0000000..5d9a6ba --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Russia/data.json @@ -0,0 +1,4 @@ +{ + name:'Russia', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Spain/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Spain/data.json new file mode 100644 index 0000000..d9a1210 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Spain/data.json @@ -0,0 +1,4 @@ +{ + name:'Spain', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/Khartoum/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/Khartoum/data.json new file mode 100644 index 0000000..befa3c7 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/Khartoum/data.json @@ -0,0 +1,5 @@ +{ + name:'Khartoum', + type:'city' +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/data.json new file mode 100644 index 0000000..fe7585b --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/Sudan/data.json @@ -0,0 +1,6 @@ +{ + name:'Sudan', + type:'country', + children:{stub:'Khartoum'} +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/United States of America/data.json b/js/dojo-1.7.2/dojox/data/demos/geography/United States of America/data.json new file mode 100644 index 0000000..7dbdd61 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/United States of America/data.json @@ -0,0 +1,4 @@ +{ + name:'United States of America', + type:'country' +} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography/root.json b/js/dojo-1.7.2/dojox/data/demos/geography/root.json new file mode 100644 index 0000000..dda74f5 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography/root.json @@ -0,0 +1,39 @@ +{ + identifier: 'name', + label: 'name', + items: [ + { name:'Africa', type:'continent', + children:[{_reference:'Egypt'}, {_reference:'Kenya'}, {_reference:'Sudan'}] }, + { name:'Egypt', type:'stub', parent: 'geography'}, + { name:'Kenya', type:'stub', parent: 'geography'}, + { name:'Sudan', type:'stub', parent: 'geography'}, + + { name:'Asia', type:'continent', + children:[{_reference:'China'}, {_reference:'India'}, {_reference:'Russia'}, {_reference:'Mongolia'}] }, + { name:'China', type:'stub', parent: 'geography'}, + { name:'India', type:'stub', parent: 'geography'}, + { name:'Russia', type:'stub', parent: 'geography'}, + { name:'Mongolia', type:'stub', parent: 'geography'}, + + { name:'Australia', type:'continent', population:'21 million', + children:{_reference:'Commonwealth of Australia'}}, + { name:'Commonwealth of Australia', type:'stub', parent:'geography'}, + + { name:'Europe', type:'continent', + children:[{_reference:'Germany'}, {_reference:'France'}, {_reference:'Spain'}, {_reference:'Italy'}] }, + { name:'Germany', type:'stub', parent: 'geography'}, + { name:'France', type:'stub', parent: 'geography'}, + { name:'Spain', type:'stub', parent: 'geography'}, + { name:'Italy', type:'stub', parent: 'geography'}, + + { name:'North America', type:'continent', + children:[{_reference:'Mexico'}, {_reference:'Canada'}, {_reference:'United States of America'}] }, + { name:'Mexico', type:'stub', parent: 'geography'}, + { name:'Canada', type:'stub', parent: 'geography'}, + { name:'United States of America', type:'stub', parent: 'geography'}, + + { name:'South America', type:'continent', + children:[{_reference:'Brazil'}, {_reference:'Argentina'}] }, + { name:'Brazil', type:'stub', parent: 'geography'}, + { name:'Argentina', type:'stub', parent: 'geography'} +]} diff --git a/js/dojo-1.7.2/dojox/data/demos/geography2.xml b/js/dojo-1.7.2/dojox/data/demos/geography2.xml new file mode 100644 index 0000000..b82c110 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/geography2.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<geography> + <outline text="Africa" type="continent"> + <outline text="Egypt" type="country"/> + <outline text="Kenya" type="country"> + <outline text="Nairobi" type="city"/> + <outline text="Mombasa" type="city"/> + </outline> + <outline text="Sudan" type="country"> + <outline text="Khartoum" type="city"/> + </outline> + </outline> + <outline text="Asia" type="continent"> + <outline text="China" type="country"/> + <outline text="India" type="country"/> + <outline text="Russia" type="country"/> + <outline text="Mongolia" type="country"/> + </outline> + <outline text="Australia" type="continent" population="21 million"> + <outline text="Australia" type="country" population="21 million"/> + </outline> + <outline text="Europe" type="continent"> + <outline text="Germany" type="country"/> + <outline text="France" type="country"/> + <outline text="Spain" type="country"/> + <outline text="Italy" type="country"/> + </outline> + <outline text="North America" type="continent"> + <outline text="Mexico" type="country" population="108 million" area="1,972,550 sq km"> + <outline text="Mexico City" type="city" population="19 million" timezone="-6 UTC"/> + <outline text="Guadalajara" type="city" population="4 million" timezone="-6 UTC"/> + </outline> + <outline text="Canada" type="country" population="33 million" area="9,984,670 sq km"> + <outline text="Ottawa" type="city" population="0.9 million" timezone="-5 UTC"/> + <outline text="Toronto" type="city" population="2.5 million" timezone="-5 UTC"/> + </outline> + <outline text="United States of America" type="country"/> + </outline> + <outline text="South America" type="continent"> + <outline text="Brazil" type="country" population="186 million"/> + <outline text="Argentina" type="country" population="40 million"/> + </outline> +</geography> + diff --git a/js/dojo-1.7.2/dojox/data/demos/openSearchProxy.php b/js/dojo-1.7.2/dojox/data/demos/openSearchProxy.php new file mode 100644 index 0000000..4f66307 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/openSearchProxy.php @@ -0,0 +1,33 @@ +<?php +// A simple proxy for testing the OpenSearchStore +// Note, this simple proxy requires a curl-enabled PHP install +if(!$_GET['url']){ return; } + +$url = str_replace(array(';;;;', '%%%%'), array('?', '&'), $_GET['url']); +if(stripos($url, "http://intertwingly.net/") === 0 || + stripos($url, "http://www.intertwingly.net/") === 0 || + stripos($url, "http://www.shutterpoint.com/") === 0 || + stripos($url, "http://technorati.com/") === 0){ + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $results = curl_exec($ch); + header('HTTP/1.1 ' . curl_getinfo($ch, CURLINFO_HTTP_CODE) . ' OK'); + if($_GET['osd'] === 'true'){ + $xml = new SimpleXMLElement($results); + if($xml->Url){ + foreach($xml->Url as $url){ + $url['template'] = $_SERVER['SCRIPT_NAME'].'?url='.str_replace(array('?', '&'), array(';;;;', '%%%%'), $url['template']); + } + header('Content-Type: text/xml'); + print $xml->asXML(); + } + }else{ + header('Content-Type: '.curl_getinfo($ch, CURLINFO_CONTENT_TYPE)); + print $results; + } +}else{ + header("HTTP/1.0 403 Forbidden"); + header("Status: 403 Forbidden"); + print "Provided URL not allowed by this demo proxy."; +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/picasaDemo.css b/js/dojo-1.7.2/dojox/data/demos/picasaDemo.css new file mode 100644 index 0000000..e274f87 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/picasaDemo.css @@ -0,0 +1,44 @@ +.picasaView { + padding: 3 3 3 3; + border-width: 1px; + border-style: solid; + border-color: #000000; + border-collapse: separate; + width: 100%; +} + +.picasaView th { + text-align: left; +} + +.picasaView tr { + padding: 3 3 3 3; + border-width: 1px; + border-style: solid; + border-color: #000000; +} + +.picasaView tr td { + padding: 3 3 3 3; + border-width: 1px; + border-style: solid; + border-color: #000000; +} + +.picasaView { + background-color: #EFEFEF; + float: left; + width: 250px; + height: 250px; +} + +.picasaSummary { + width: 250px; + height: 30px; + overflow: hidden; + } + +.picasaTitle { + background-color: #CCCCCC; +} + diff --git a/js/dojo-1.7.2/dojox/data/demos/stores/LazyLoadJSIStore.js b/js/dojo-1.7.2/dojox/data/demos/stores/LazyLoadJSIStore.js new file mode 100644 index 0000000..7a867a4 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/stores/LazyLoadJSIStore.js @@ -0,0 +1,143 @@ +//>>built +// wrapped by build app +define("dojox/data/demos/stores/LazyLoadJSIStore", ["dijit","dojo","dojox","dojo/require!dojo/data/ItemFileReadStore"], function(dijit,dojo,dojox){ +dojo.provide("dojox.data.demos.stores.LazyLoadJSIStore"); +dojo.require("dojo.data.ItemFileReadStore"); + +dojo.declare("dojox.data.demos.stores.LazyLoadJSIStore", dojo.data.ItemFileReadStore, { + constructor: function(/* object */ keywordParameters){ + // LazyLoadJSIStore extends ItemFileReadStore to implement an + // example of lazy-loading/faulting in items on-demand. + // Note this is certianly not a perfect implementation, it is + // an example. + }, + + isItemLoaded: function(/*object*/ item) { + // summary: + // Overload of the isItemLoaded function to look for items of type 'stub', which indicate + // the data hasn't been loaded in yet. + // + // item: + // The item to examine. + + //For this store, if it has the value of stub for its type attribute, + //then the item basn't been fully loaded yet. It's just a placeholder. + if(this.getValue(item, "type") === "stub"){ + return false; + } + return true; + }, + + loadItem: function(keywordArgs){ + // summary: + // Overload of the loadItem function to fault in items. This assumes the data for an item is laid out + // in a RESTful sort of pattern name0/name1/data.json and so on and uses that to load the data. + // It will also detect stub items in the newly loaded item and insert the stubs into the ItemFileReadStore + // list so they can also be loaded in on-demand. + // + // item: + // The item to examine. + + var item = keywordArgs.item; + this._assertIsItem(item); + + //Build the path to the data.json for this item + //The path consists of where its parent was loaded from + //plus the item name. + var itemName = this.getValue(item, "name"); + var parent = this.getValue(item, "parent"); + var dataUrl = ""; + if (parent){ + dataUrl += (parent + "/"); + } + + //For this store, all child input data is loaded from a url that ends with data.json + dataUrl += itemName + "/data.json"; + + //Need a reference to the store to call back to its structures. + var self = this; + + // Callback for handling a successful load. + var gotData = function(data){ + //Now we need to modify the existing item a bit to take it out of stub state + //Since we extend the store and have knowledge of the internal + //structure, this can be done here. Now, is we extended + //a write store, we could call the write APIs to do this too + //But for a simple demo the diretc modification in the store function + //is sufficient. + + //Clear off the stub indicators. + delete item.type; + delete item.parent; + + //Set up the loaded values in the format ItemFileReadStore uses for attributes. + for (var i in data) { + if (dojo.isArray(data[i])) { + item[i] = data[i]; + }else{ + item[i] = [data[i]]; + } + } + + //Reset the item in the reference. + self._arrayOfAllItems[item[self._itemNumPropName]] = item; + + //Scan the new values in the item for extra stub items we need to + //add to the items array of the store so they can be lazy-loaded later... + var attributes = self.getAttributes(item); + for(i in attributes){ + var values = item[attributes[i]]; + for (var j = 0; j < values.length; j++) { + var value = values[j]; + + if(typeof value === "object"){ + if(value["stub"] ){ + //We have a stub reference here, we need to create the stub item + var stub = { + type: ["stub"], + name: [value["stub"]], // + parent: [itemName] //The child stub item is parented by this item name... + }; + if (parent) { + //Add in any parents to your parent so URL construstruction is accurate. + stub.parent[0] = parent + "/" + stub.parent[0]; + } + //Finalize the addition of the new stub item into the ItemFileReadStore list. + self._arrayOfAllItems.push(stub); + stub[self._storeRefPropName] = self; + stub[self._itemNumPropName] = (self._arrayOfAllItems.length - 1); //Last one pushed in should be the item + values[j] = stub; //Set the stub item back in its place and replace the stub notation. + } + } + } + } + + //Done processing! Call the onItem, if any. + if(keywordArgs.onItem){ + var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global; + keywordArgs.onItem.call(scope, item); + } + }; + + //Callback for any errors that occur during load. + var gotError = function(error){ + //Call the onComplete, if any + if(keywordArgs.onError){ + var scope = keywordArgs.scope ? keywordArgs.scope : dojo.global; + keywordArgs.onError.call(scope, error); + } + }; + + //Fire the get and pass the proper callbacks to the deferred. + var xhrArgs = { + url: dataUrl, + handleAs: "json-comment-optional" + }; + var d = dojo.xhrGet(xhrArgs); + d.addCallback(gotData); + d.addErrback(gotError); + } +}); + + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojotree.php b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojotree.php new file mode 100644 index 0000000..5e19385 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojotree.php @@ -0,0 +1,179 @@ +<?php + //Define the root directory to use for this service. + //All file lookups are relative to this path. + $rootDir = "../../../.."; + + require_once("filestore_funcs.php"); + + //Extract the query, if any. + $query = false; + if (array_key_exists("query", $_GET)) { + $query = $_GET['query']; + $query = str_replace("\\\"", "\"", $query); + $query = json_decode($query, true); + } + //Extract relevant query options. + $queryOptions = json_decode("{}"); + $deep = false; + $ignoreCase = false; + if (array_key_exists("queryOptions", $_GET)) { + $queryOptions = $_GET['queryOptions']; + $queryOptions = str_replace("\\\"", "\"", $queryOptions); + $queryOptions = json_decode($queryOptions); + if (property_exists($queryOptions, "deep")) { + $deep = $queryOptions->deep; + } + if (property_exists($queryOptions, "ignoreCase")) { + $ignoreCase = $queryOptions->ignoreCase; + } + } + + //Extract non-dojo.data spec config options. + $expand = false; + $dirsOnly = false; + $showHiddenFiles = false; + $options = array(); + if (array_key_exists("options", $_GET)) { + $options = $_GET['options']; + $options = str_replace("\\\"", "\"", $options); + $options = json_decode($options); + if (array_search("expand", $options) > -1) { + $expand = true; + } + if (array_search("dirsOnly", $options) > -1) { + $dirsOnly = true; + } + if (array_search("showHiddenFiles", $options) > -1) { + $showHiddenFiles = true; + } + } + + + //See if a specific file was requested, or if it is just a query for files. + $path = false; + if (array_key_exists("path", $_GET)) { + $path = $_GET['path']; + } + + if (!is_string($path)) { + + $files = array(); + + //Handle query for files. Must try to generate patterns over the query + //attributes. + $patterns = array(); + if (is_array($query)) { + //Generate a series of RegExp patterns as necessary. + $keys = array_keys($query); + $total = count($keys); + if ($total > 0) { + for ($i = 0; $i < $total; $i++) { + $key = $keys[$i]; + $pattern = $query[$key]; + if (is_string($pattern)) { + $patterns[$key] = patternToRegExp($pattern); + } + } + $files = matchFiles($query, $patterns, $ignoreCase, ".", $rootDir, $deep, $dirsOnly, $expand, $showHiddenFiles); + } else { + $files = getAllFiles(".",$rootDir,$deep,$dirsOnly,$expand,$showHiddenFiles); + } + }else{ + $files = getAllFiles(".",$rootDir,$deep,$dirsOnly,$expand,$showHiddenFiles); + } + + $total = count($files); + + //Handle the sorting and paging. + $sortSpec = false; + if (array_key_exists("sort", $_GET)) { + $sortSpec = $_GET['sort']; + $sortSpec = str_replace("\\\"", "\"", $sortSpec); + $sortSpec = json_decode($sortSpec); + } + + if ($sortSpec != null) { + $comparator = createComparator($sortSpec); + usort($files,array($comparator, "compare")); + } + + //Page, if necessary. + if (array_key_exists("start", $_GET)) { + $start = $_GET['start']; + if (!is_numeric($start)) { + $start = 0; + } + $files = array_slice($files, $start); + } + if (array_key_exists("count", $_GET)) { + $count = $_GET['count']; + if (!is_numeric($count)) { + $count = $total; + } + $files = array_slice($files, 0, $count); + } + + $result = new stdClass(); + $result->total = $total; + $result->items = $files; + header("Content-Type", "text/json"); + print("/* ".json_encode($result)." */"); + } else { + //Query of a specific file (useful for fetchByIdentity and loadItem) + + //Make sure the path isn't trying to walk out of the rooted directory + //As defined by $rootDir in the top of the php script. + $rootPath = realPath($rootDir); + $fullPath = realPath($rootPath."/".$path); + + if ($fullPath !== false) { + if (strpos($fullPath,$rootPath) === 0) { + //Root the path into the tree cleaner. + if (strlen($fullPath) == strlen($rootPath)) { + $path = "."; + } else { + //Fix the path to relative of root and put back into UNIX style (even if windows). + $path = substr($fullPath,(strlen($rootPath) + 1),strlen($fullPath)); + $path = str_replace("\\", "/", $path); + } + + if (file_exists($fullPath)) { + $arr = explode("/", $path); + $size = count($arr); + + if ($size > 0) { + $fName = $arr[$size - 1]; + if ($size == 1) { + print("Setting path to: ."); + $path = "."; + } else { + $path = $arr[0]; + } + for ($i = 1; $i < ($size - 1); $i++) { + $path = $path."/".$arr[$i]; + } + $file = generateFileObj($fName, $path, $rootDir, $expand,$showHiddenFiles); + header("Content-Type", "text/json"); + print("/* ".json_encode($file)." */"); + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } else { + header("HTTP/1.0 403 Forbidden"); + header("Status: 403 Forbidden"); + print("<b>Cannot access file: [".htmlentities($path)."]. It is outside of the root of the file service.<b>"); + } + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } +?> diff --git a/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojoxdata.php b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojoxdata.php new file mode 100644 index 0000000..0360049 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_dojoxdata.php @@ -0,0 +1,179 @@ +<?php + //Define the root directory to use for this service. + //All file lookups are relative to this path. + $rootDir = "../.."; + + require_once("filestore_funcs.php"); + + //Extract the query, if any. + $query = false; + if (array_key_exists("query", $_GET)) { + $query = $_GET['query']; + $query = str_replace("\\\"", "\"", $query); + $query = json_decode($query, true); + } + //Extract relevant query options. + $queryOptions = json_decode("{}"); + $deep = false; + $ignoreCase = false; + if (array_key_exists("queryOptions", $_GET)) { + $queryOptions = $_GET['queryOptions']; + $queryOptions = str_replace("\\\"", "\"", $queryOptions); + $queryOptions = json_decode($queryOptions); + if (property_exists($queryOptions, "deep")) { + $deep = $queryOptions->deep; + } + if (property_exists($queryOptions, "ignoreCase")) { + $ignoreCase = $queryOptions->ignoreCase; + } + } + + //Extract non-dojo.data spec config options. + $expand = false; + $dirsOnly = false; + $showHiddenFiles = false; + $options = array(); + if (array_key_exists("options", $_GET)) { + $options = $_GET['options']; + $options = str_replace("\\\"", "\"", $options); + $options = json_decode($options); + if (array_search("expand", $options) > -1) { + $expand = true; + } + if (array_search("dirsOnly", $options) > -1) { + $dirsOnly = true; + } + if (array_search("showHiddenFiles", $options) > -1) { + $showHiddenFiles = true; + } + } + + + //See if a specific file was requested, or if it is just a query for files. + $path = false; + if (array_key_exists("path", $_GET)) { + $path = $_GET['path']; + } + + if (!is_string($path)) { + + $files = array(); + + //Handle query for files. Must try to generate patterns over the query + //attributes. + $patterns = array(); + if (is_array($query)) { + //Generate a series of RegExp patterns as necessary. + $keys = array_keys($query); + $total = count($keys); + if ($total > 0) { + for ($i = 0; $i < $total; $i++) { + $key = $keys[$i]; + $pattern = $query[$key]; + if (is_string($pattern)) { + $patterns[$key] = patternToRegExp($pattern); + } + } + $files = matchFiles($query, $patterns, $ignoreCase, ".", $rootDir, $deep, $dirsOnly, $expand, $showHiddenFiles); + } else { + $files = getAllFiles(".",$rootDir,$deep,$dirsOnly,$expand,$showHiddenFiles); + } + }else{ + $files = getAllFiles(".",$rootDir,$deep,$dirsOnly,$expand,$showHiddenFiles); + } + + $total = count($files); + + //Handle the sorting and paging. + $sortSpec = false; + if (array_key_exists("sort", $_GET)) { + $sortSpec = $_GET['sort']; + $sortSpec = str_replace("\\\"", "\"", $sortSpec); + $sortSpec = json_decode($sortSpec); + } + + if ($sortSpec != null) { + $comparator = createComparator($sortSpec); + usort($files,array($comparator, "compare")); + } + + //Page, if necessary. + if (array_key_exists("start", $_GET)) { + $start = $_GET['start']; + if (!is_numeric($start)) { + $start = 0; + } + $files = array_slice($files, $start); + } + if (array_key_exists("count", $_GET)) { + $count = $_GET['count']; + if (!is_numeric($count)) { + $count = $total; + } + $files = array_slice($files, 0, $count); + } + + $result; + $result->total = $total; + $result->items = $files; + header("Content-Type", "text/json"); + print("/* ".json_encode($result)." */"); + } else { + //Query of a specific file (useful for fetchByIdentity and loadItem) + + //Make sure the path isn't trying to walk out of the rooted directory + //As defined by $rootDir in the top of the php script. + $rootPath = realPath($rootDir); + $fullPath = realPath($rootPath."/".$path); + + if ($fullPath !== false) { + if (strpos($fullPath,$rootPath) === 0) { + //Root the path into the tree cleaner. + if (strlen($fullPath) == strlen($rootPath)) { + $path = "."; + } else { + //Fix the path to relative of root and put back into UNIX style (even if windows). + $path = substr($fullPath,(strlen($rootPath) + 1),strlen($fullPath)); + $path = str_replace("\\", "/", $path); + } + + if (file_exists($fullPath)) { + $arr = split("/", $path); + $size = count($arr); + + if ($size > 0) { + $fName = $arr[$size - 1]; + if ($size == 1) { + print("Setting path to: ."); + $path = "."; + } else { + $path = $arr[0]; + } + for ($i = 1; $i < ($size - 1); $i++) { + $path = $path."/".$arr[$i]; + } + $file = generateFileObj($fName, $path, $rootDir, $expand,$showHiddenFiles); + header("Content-Type", "text/json"); + print("/* ".json_encode($file)." */"); + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } else { + header("HTTP/1.0 403 Forbidden"); + header("Status: 403 Forbidden"); + print("<b>Cannot access file: [".htmlentities($path)."]. It is outside of the root of the file service.<b>"); + } + } else { + header("HTTP/1.0 404 Not Found"); + header("Status: 404 Not Found"); + print("<b>Cannot access file: [".htmlentities($path)."]<b>"); + } + } +?> diff --git a/js/dojo-1.7.2/dojox/data/demos/stores/filestore_funcs.php b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_funcs.php new file mode 100644 index 0000000..ff61139 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/stores/filestore_funcs.php @@ -0,0 +1,366 @@ +<?php + /** + * Helper function to convert a simple pattern to a regular expression for matching. + * + * Returns a regular expression object that conforms to the defined conversion rules. + * For example: + * ca* -> /^ca.*$/ + * *ca* -> /^.*ca.*$/ + * *c\*a* -> /^.*c\*a.*$/ + * *c\*a?* -> /^.*c\*a..*$/ + * and so on. + * + * @param pattern: string + * A simple matching pattern to convert that follows basic rules: + * * Means match anything, so ca* means match anything starting with ca + * ? Means match single character. So, b?b will match to bob and bab, and so on. + * \ is an escape character. So for example, \* means do not treat * as a match, but literal character *. + * To use a \ as a character in the string, it must be escaped. So in the pattern it should be + * represented by \\ to be treated as an ordinary \ character instead of an escape. + */ + function patternToRegExp(/*String*/$pattern){ + $rxp = "^"; + $c = ""; + $len = strlen($pattern); + for ($i = 0; $i < $len; $i++) { + $c = $pattern[$i]; + switch ($c) { + case '\\': + $rxp = $rxp.$c; + $i++; + $rxp = $rxp.$pattern[$i]; + break; + case '*': + $rxp = $rxp.".*"; break; + case '?': + $rxp = $rxp."."; break; + case '$': + case '^': + case '/': + case '+': + case '.': + case '|': + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + $rxp = $rxp."\\"; //fallthrough + default: + $rxp = $rxp.$c; + } + } + return "(".$rxp."$)"; + } + + /** + * Function to load all file info from a particular directory. + * + * @param $dir The dir to seach from, relative to $rootDir. + * @param $rootDir The directory where the file service is rooted, used as separate var to allow easier checking and prevention of ../ing out of the tree. + * @param $recurse Whether or not to deep scan the dir and return all subfiles, or just return the toplevel files. + * @param $dirsOnly boolean to enote to only return directory names, not filenames. + * @param $expand boolean to indicate whether or not to inflate all children files along a path/file, or leave them as stubs. + * @param $showHiddenFiles boolean to indicate to return hidden files as part of the list. + */ + function getAllfiles($dir, $rootDir, $recurse, $dirsOnly, $expand, $showHiddenFiles) { + // summary: + // A function to obtain all the files in a particular directory (file or dir) + $files = array(); + $dirHandle = opendir($rootDir."/".$dir); + if ($dirHandle) { + while($file = readdir($dirHandle)) { + if ($file) { + if ($file != ".." && $file != ".") { + $path = $dir."/".$file; + $fileObj = generateFileObj($file, $dir, $rootDir,$expand,$showHiddenFiles); + if (is_dir($rootDir."/".$path)) { + if ($recurse) { + if ($showHiddenFiles || $fileObj["name"][0] != '.') { + $subfiles = getAllfiles($path,$rootDir,$recurse,$dirsOnly,$expand,$showHiddenFiles); + $length = count($subfiles); + for ($i = 0; $i < $length; $i++) { + $files[] = $subfiles[$i]; + } + } + } + } + if (!$dirsOnly || $fileObj["directory"]) { + if ($showHiddenFiles || $fileObj["name"][0] !== '.') { + $files[] = $fileObj; + } + } + } + } + } + } + closedir($dirHandle); + return $files; + } + + /** + * Function to generate an associative map of data about a specific file. + * @param $file The name of the file this object represents. + * @param $dir The sub-path that contains the file defined by $file + * @param $rootDir The directory from which to append dir and name to get the full path to the file. + * @param $expand boolean to denote that if the file is a directory, expand all children in the children attribute + * to a a full object + * @param $showHiddenFiles boolean to denote if hidden files should be shown in-view or not. + * + * @return Associative Map. The details about the file: + * $file["name"] - Returns the shortname of the file. + * $file["parentDir"] - Returns the relative path from the service root for the parent directory containing file $file["name"] + * $file["path"] - The relative path to the file. + * $file["directory"] - Boolean indicator if the file represents a directory. + * $file["size"] - The size of the file, in bytes. + * $file["modified] - The modified date of the file in milliseconds since Jan 1st, 1970. + * $file["children"] - Children files of a directory. Empty if a standard file. + */ + function generateFileObj($file, $dir, $rootDir, $expand, $showHiddenFiles) { + // summary: + // Function to generate an object representation of a disk file. + $path = $file; + if ($dir != "." && $dir != "./") { + $path = $dir."/".$file; + } + + $fullPath = $rootDir."/".$path; + + $atts = stat($fullPath); + + $rootPath = realPath($rootDir); + $resolvedDir = realPath($rootDir."/".$dir); + $resolvedFullPath = realPath($fullPath); + + //Try to normalize down the paths so it does a consistent return. + if (strcmp($rootPath, $resolvedDir) === 0) { + $dir = "."; + } else { + $dir = substr($resolvedDir, (strlen($rootPath) + 1), strlen($resolvedDir)); + $dir = "./".str_replace("\\","/",$dir); + } + if (strcmp($rootPath, $resolvedFullPath) === 0) { + $path = "."; + } else { + $path = substr($resolvedFullPath, (strlen($rootPath) + 1), strlen($resolvedFullPath)); + $path = "./".str_replace("\\","/",$path); + } + + $fObj = array(); + $fObj["name"] = $file; + $fObj["parentDir"] = $dir; + $fObj["path"] = $path; + $fObj["directory"] = is_dir($fullPath); + $fObj["size"] = filesize($fullPath); + $fObj["modified"] = $atts[9]; + + if (is_dir($fullPath)) { + $children = array(); + $dirHandle = opendir($fullPath); + while($cFile = readdir($dirHandle)) { + if ($cFile) { + if ($cFile != ".." && $cFile != ".") { + if ($showHiddenFiles || $cFile[0] != '.') { + if (!$expand) { + $children[] = $cFile; + }else{ + $children[] = generateFileObj($cFile, $path, $rootDir, $expand, $showHiddenFiles); + } + } + } + } + } + closedir($dirHandle); + $fObj["children"] = $children; + } + return $fObj; + } + + /** + * A field comparator class, whose role it is to define which fields on an associaive map to compare on + * and provide the comparison function to do so. + */ + class FieldComparator { + var $field; + var $descending = false; + + /** + * Constructor. + * @param $f The field of the item to compare. + * @param $d Parameter denoting whether it should be ascending or descending. Default is ascending. + */ + function FieldComparator($f, $d) { + $this->field = $f; + $this->descending = $d; + } + + /** + * Function to compare file objects A and B on the field defined by $this->field. + * @param $fileA The first file to compare. + * @param #fileB The second file to compare. + */ + function compare($fileA,$fileB){ + $f = $this->field; + $a = $fileA[$f]; + $b = $fileB[$f]; + + $ret = 0; + if (is_string($a) && is_string($b)) { + $ret = strcmp($a,$b); + } else if($a > $b || $a === null){ + $ret = 1; + }else if($a < $b || $b === null){ + $ret = -1; + } + + if (property_exists($this, "descending") && $this->descending == true) { + $ret = $ret * -1; + } + + if ($ret > 0) { + $ret = 1; + } else if ($ret < 0) { + $ret = -1; + } + return $ret; //int, {-1,0,1} + } + } + + /** + * A compound comparator class, whose role it is to sequentially call a set of comparators on two objects and + * return the combined result of the comparison. + */ + class CompoundComparator { + //Comparator chain. + var $comparators = array(); + + /** + * Function to compare two objects $a and $b, using the chain of comparators. + * @param $a The first object to compare. + * @param $b The second object to compare. + * @returns -1, 0, 1. -1 if a < b, 1 if a > b, and 0 if a = b. + */ + function compare($a, $b) { + $ret = 0; + $size = count($this->comparators); + for ($i = 0; $i < $size; $i++) { + $comp = $this->comparators[$i]; + $ret = $comp->compare($a, $b); + if ($ret != 0) { + break; + } + } + return $ret; + } + + /** + * Function to add a comparator to the chain. + * @param $comp The comparator to add. + */ + function addComparator($comp){ + $this->comparators[] = $comp; + } + } + + /** + * A function to create a Comparator class with chained comparators based off the sort specification passed into the store. + * @param $sortSpec The Sort specification, which is an array of sort objects containing ( attribute: "someStr": descending: true|fase} + * @returns The constructed comparator. + */ + function createComparator($sortSpec) { + //Function to construct the class that handles chained comparisons. + $comparator = new CompoundComparator(); + $size = count($sortSpec); + for ($i = 0; $i < $size; $i++) { + $sort = $sortSpec[$i]; + $desc = false; + if(property_exists($sort, "descending")){ + $desc = $sort->descending; + } + $fileComp = new FieldComparator($sort->attribute,$desc); + $comparator->addComparator($fileComp); + } + return $comparator; + } + + /** + * Function to match a set of queries against a directory and possibly all subfiles. + * @param query The Query send in to process and test against. + * @param patterns The set of regexp patterns generated off the query. + * @param dir the directory to search in. + * @param recurse Whether or not to recurse into subdirs and test files there too. + * + * @return Array. Returns an array of all matches of the query. + */ + function matchFiles($query, $patterns, $ignoreCase, $dir, $rootDir, $recurse, $dirsOnly, $expand, $showHiddenFiles) { + $files = array(); + $fullDir = $rootDir."/".$dir; + + if ($fullDir != null && is_dir($fullDir)) { + + $dirHandle = opendir($fullDir); + while ($file = readdir($dirHandle)) { + if ($file != "." && $file != "..") { + $item = generateFileObj($file, $dir, $rootDir, $expand,$showHiddenFiles); + $keys = array_keys($patterns); + $total = count($keys); + for ($i = 0; $i < $total; $i++) { + $key = $keys[$i]; + $pattern = $query[$key]; + $matched = containsValue($item,$key,$query[$key],$patterns[$key], $ignoreCase); + if (!$matched) { + break; + } + } + if ($matched) { + if (!$dirsOnly || $item["directory"]) { + if ($showHiddenFiles || $item["name"][0] != '.') { + $files[] = $item; + } + } + } + + if (is_dir($rootDir."/".$item["path"]) && $recurse) { + if ($showHiddenFiles || $item["name"][0] != '.') { + $files = array_merge($files, matchFiles($query, $patterns, $ignoreCase, $item["path"], $rootDir, $recurse, $dirsOnly, $expand, $showHiddenFiles)); + } + } + } + } + closedir($dirHandle); + } + return $files; + } + + /** + * Function to handle comparing the value of an attribute on a file item. + * @param item The item to examine. + * @param attr The attribute of the tem to examine. + * @parma value The value to compare it to. + * @param rExp A regular Expression pattern object generated off 'value' if any. + * + * @returns boolean denoting if the value was matched or not. + */ + function containsValue($item, $attr, $value, $rExp, $ignoreCase) { + $matched = false; + $possibleValue = $item[$attr]; + if ($possibleValue === null && $value === null) { + $matched = true; + } else { + if ($rExp != null && is_string($possibleValue)) { + if ($ignoreCase) { + $matched = eregi($rExp, $possibleValue); + } else { + $matched = ereg($rExp, $possibleValue); + } + + } else { + if ($value != null && $possibleValue != null) { + $matched = ($value == $possibleValue); + } + } + } + return $matched; + } +// No closing PHP tag on purpose. Do not want it to print whitepace and thus not allow setting headers later. diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/FileView.js b/js/dojo-1.7.2/dojox/data/demos/widgets/FileView.js new file mode 100644 index 0000000..5fa9e46 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/FileView.js @@ -0,0 +1,45 @@ +//>>built +// wrapped by build app +define("dojox/data/demos/widgets/FileView", ["dijit","dojo","dojox","dojo/require!dijit/_Templated,dijit/_Widget"], function(dijit,dojo,dojox){ +dojo.provide("dojox.data.demos.widgets.FileView"); +dojo.require("dijit._Templated"); +dojo.require("dijit._Widget"); + +dojo.declare("dojox.data.demos.widgets.FileView", [dijit._Widget, dijit._Templated], { + //Simple demo widget for representing a view of a Flickr Item. + + templateString: dojo.cache("dojox", "data/demos/widgets/templates/FileView.html", "<div class=\"fileView\">\n\t<div class=\"fileViewTitle\">File Details:</div>\n\t<table class=\"fileViewTable\">\n\t\t<tbody>\n\t\t\t<tr class=\"fileName\">\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tName:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td dojoAttachPoint=\"nameNode\">\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tPath:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td dojoAttachPoint=\"pathNode\">\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tSize:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td>\n\t\t\t\t\t<span dojoAttachPoint=\"sizeNode\"></span> bytes.\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tIs Directory:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td dojoAttachPoint=\"directoryNode\">\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tParent Directory:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td dojoAttachPoint=\"parentDirNode\">\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td>\n\t\t\t\t\t<b>\n\t\t\t\t\t\tChildren:\n\t\t\t\t\t</b>\n\t\t\t\t</td>\n\t\t\t\t<td dojoAttachPoint=\"childrenNode\">\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</tbody>\n\t</table>\n</div>\n"), + + //Attach points for reference. + titleNode: null, + descriptionNode: null, + imageNode: null, + authorNode: null, + + name: "", + path: "", + size: 0, + directory: false, + parentDir: "", + children: [], + + postCreate: function(){ + this.nameNode.appendChild(document.createTextNode(this.name)); + this.pathNode.appendChild(document.createTextNode(this.path)); + this.sizeNode.appendChild(document.createTextNode(this.size)); + this.directoryNode.appendChild(document.createTextNode(this.directory)); + this.parentDirNode.appendChild(document.createTextNode(this.parentDir)); + if (this.children && this.children.length > 0) { + var i; + for (i = 0; i < this.children.length; i++) { + var tNode = document.createTextNode(this.children[i]); + this.childrenNode.appendChild(tNode); + if (i < (this.children.length - 1)) { + this.childrenNode.appendChild(document.createElement("br")); + } + } + } + } +}); + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/FlickrViewList.js b/js/dojo-1.7.2/dojox/data/demos/widgets/FlickrViewList.js new file mode 100644 index 0000000..294a7d6 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/FlickrViewList.js @@ -0,0 +1,35 @@ +//>>built +// wrapped by build app +define("dojox/data/demos/widgets/FlickrViewList", ["dijit","dojo","dojox","dojo/require!dojox/dtl/_Templated,dijit/_Widget"], function(dijit,dojo,dojox){ +dojo.provide("dojox.data.demos.widgets.FlickrViewList"); +dojo.require("dojox.dtl._Templated"); +dojo.require("dijit._Widget"); + +dojo.declare("dojox.data.demos.widgets.FlickrViewList", + [ dijit._Widget, dojox.dtl._Templated ], + { + store: null, + items: null, + + templateString: dojo.cache("dojox", "data/demos/widgets/templates/FlickrViewList.html", "{% load dojox.dtl.contrib.data %}\n{% bind_data items to store as flickr %}\n<div dojoAttachPoint=\"list\">\n\t{% for item in flickr %}\n\t<div style=\"display: inline-block; align: top;\">\n\t\t<h5>{{ item.title }}</h5>\n\t\t<a href=\"{{ item.link }}\" style=\"border: none;\">\n\t\t\t<img src=\"{{ item.imageUrlMedium }}\">\n\t\t</a>\n\t\t<p>{{ item.author }}</p>\n\n\t\t<!--\n\t\t<img src=\"{{ item.imageUrl }}\">\n\t\t<p>{{ item.imageUrl }}</p>\n\t\t<img src=\"{{ item.imageUrlSmall }}\">\n\t\t-->\n\t</div>\n\t{% endfor %}\n</div>\n\n"), + + fetch: function(request){ + request.onComplete = dojo.hitch(this, "onComplete"); + request.onError = dojo.hitch(this, "onError"); + return this.store.fetch(request); + }, + + onError: function(){ + console.trace(); + this.items = []; + this.render(); + }, + + onComplete: function(items, request){ + this.items = items||[]; + this.render(); + } + } +); + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaView.js b/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaView.js new file mode 100644 index 0000000..79b54bf --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaView.js @@ -0,0 +1,38 @@ +//>>built +// wrapped by build app +define("dojox/data/demos/widgets/PicasaView", ["dijit","dojo","dojox","dojo/require!dijit/_Templated,dijit/_Widget"], function(dijit,dojo,dojox){ +dojo.provide("dojox.data.demos.widgets.PicasaView"); +dojo.require("dijit._Templated"); +dojo.require("dijit._Widget"); + +dojo.declare("dojox.data.demos.widgets.PicasaView", [dijit._Widget, dijit._Templated], { + //Simple demo widget for representing a view of a Picasa Item. + + templateString: dojo.cache("dojox", "data/demos/widgets/templates/PicasaView.html", "<table class=\"picasaView\">\n\t<tbody>\n\t\t<tr class=\"picasaTitle\">\n\t\t\t<td>\n\t\t\t\t<b>\n\t\t\t\t\tTitle:\n\t\t\t\t</b>\n\t\t\t</td>\n\t\t\t<td dojoAttachPoint=\"titleNode\">\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t<b>\n\t\t\t\t\tAuthor:\n\t\t\t\t</b>\n\t\t\t</td>\n\t\t\t<td dojoAttachPoint=\"authorNode\">\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td colspan=\"2\">\n\t\t\t\t<b>\n\t\t\t\t\tSummary:\n\t\t\t\t</b>\n\t\t\t\t<span class=\"picasaSummary\" dojoAttachPoint=\"descriptionNode\"></span>\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td dojoAttachPoint=\"imageNode\" colspan=\"2\">\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n"), + + //Attach points for reference. + titleNode: null, + descriptionNode: null, + imageNode: null, + authorNode: null, + + title: "", + author: "", + imageUrl: "", + iconUrl: "", + + postCreate: function(){ + this.titleNode.appendChild(document.createTextNode(this.title)); + this.authorNode.appendChild(document.createTextNode(this.author)); + this.descriptionNode.appendChild(document.createTextNode(this.description)); + var href = document.createElement("a"); + href.setAttribute("href", this.imageUrl); + href.setAttribute("target", "_blank"); + var imageTag = document.createElement("img"); + imageTag.setAttribute("src", this.iconUrl); + href.appendChild(imageTag); + this.imageNode.appendChild(href); + } +}); + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaViewList.js b/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaViewList.js new file mode 100644 index 0000000..36b26dd --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/PicasaViewList.js @@ -0,0 +1,38 @@ +//>>built +// wrapped by build app +define("dojox/data/demos/widgets/PicasaViewList", ["dijit","dojo","dojox","dojo/require!dijit/_Templated,dijit/_Widget,dojox/data/demos/widgets/PicasaView"], function(dijit,dojo,dojox){ +dojo.provide("dojox.data.demos.widgets.PicasaViewList"); +dojo.require("dijit._Templated"); +dojo.require("dijit._Widget"); +dojo.require("dojox.data.demos.widgets.PicasaView"); + +dojo.declare("dojox.data.demos.widgets.PicasaViewList", [dijit._Widget, dijit._Templated], { + //Simple demo widget that is just a list of PicasaView Widgets. + + templateString: dojo.cache("dojox", "data/demos/widgets/templates/PicasaViewList.html", "<div dojoAttachPoint=\"list\"></div>\n\n"), + + //Attach points for reference. + listNode: null, + + postCreate: function(){ + this.fViewWidgets = []; + }, + + clearList: function(){ + while(this.list.firstChild){ + this.list.removeChild(this.list.firstChild); + } + for(var i = 0; i < this.fViewWidgets.length; i++){ + this.fViewWidgets[i].destroy(); + } + this.fViewWidgets = []; + }, + + addView: function(viewData){ + var newView = new dojox.data.demos.widgets.PicasaView(viewData); + this.fViewWidgets.push(newView); + this.list.appendChild(newView.domNode); + } +}); + +}); diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FileView.html b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FileView.html new file mode 100644 index 0000000..a83fd28 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FileView.html @@ -0,0 +1,62 @@ +<div class="fileView"> + <div class="fileViewTitle">File Details:</div> + <table class="fileViewTable"> + <tbody> + <tr class="fileName"> + <td> + <b> + Name: + </b> + </td> + <td dojoAttachPoint="nameNode"> + </td> + </tr> + <tr> + <td> + <b> + Path: + </b> + </td> + <td dojoAttachPoint="pathNode"> + </td> + </tr> + <tr> + <td> + <b> + Size: + </b> + </td> + <td> + <span dojoAttachPoint="sizeNode"></span> bytes. + </td> + </tr> + <tr> + <td> + <b> + Is Directory: + </b> + </td> + <td dojoAttachPoint="directoryNode"> + </td> + </tr> + <tr> + <td> + <b> + Parent Directory: + </b> + </td> + <td dojoAttachPoint="parentDirNode"> + </td> + </tr> + <tr> + <td> + <b> + Children: + </b> + </td> + <td dojoAttachPoint="childrenNode"> + </td> + </tr> + </tbody> + </table> +</div> diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FlickrViewList.html b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FlickrViewList.html new file mode 100644 index 0000000..e9fe02f --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/FlickrViewList.html @@ -0,0 +1,20 @@ +{% load dojox.dtl.contrib.data %} +{% bind_data items to store as flickr %} +<div dojoAttachPoint="list"> + {% for item in flickr %} + <div style="display: inline-block; align: top;"> + <h5>{{ item.title }}</h5> + <a href="{{ item.link }}" style="border: none;"> + <img src="{{ item.imageUrlMedium }}"> + </a> + <p>{{ item.author }}</p> + + <!-- + <img src="{{ item.imageUrl }}"> + <p>{{ item.imageUrl }}</p> + <img src="{{ item.imageUrlSmall }}"> + --> + </div> + {% endfor %} +</div> + diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaView.html b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaView.html new file mode 100644 index 0000000..88dbb31 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaView.html @@ -0,0 +1,35 @@ +<table class="picasaView"> + <tbody> + <tr class="picasaTitle"> + <td> + <b> + Title: + </b> + </td> + <td dojoAttachPoint="titleNode"> + </td> + </tr> + <tr> + <td> + <b> + Author: + </b> + </td> + <td dojoAttachPoint="authorNode"> + </td> + </tr> + <tr> + <td colspan="2"> + <b> + Summary: + </b> + <span class="picasaSummary" dojoAttachPoint="descriptionNode"></span> + </td> + </tr> + <tr> + <td dojoAttachPoint="imageNode" colspan="2"> + </td> + </tr> + </tbody> +</table> + diff --git a/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaViewList.html b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaViewList.html new file mode 100644 index 0000000..3a9f565 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/demos/widgets/templates/PicasaViewList.html @@ -0,0 +1,2 @@ +<div dojoAttachPoint="list"></div> + diff --git a/js/dojo-1.7.2/dojox/data/dom.js b/js/dojo-1.7.2/dojox/data/dom.js new file mode 100644 index 0000000..a9d7c08 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/dom.js @@ -0,0 +1,102 @@ +//>>built +define("dojox/data/dom", ["dojo/_base/kernel", "dojo/_base/lang", "dojox/xml/parser"], + function(kernel, lang, xmlParser) { + +//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"); + +var dataDom = lang.getObject("dojox.data.dom",true); + +dataDom.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 xmlParser.parse(str,mimetype); //DOMDocument. + }catch(e){ + /*Squeltch errors like the old parser did.*/ + return null; + } +}; + +dataDom.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 xmlParser.textContent(node, text); //string + }else{ + return xmlParser.textContent(node); //string + } +}; + +dataDom.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"); + xmlParser.replaceChildren(node, newChildren); +}; + +dataDom.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 +}; + +dataDom.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 xmlParser.innerXML(node); //string||null +}; + +return dataDom; + +}); + diff --git a/js/dojo-1.7.2/dojox/data/restListener.js b/js/dojo-1.7.2/dojox/data/restListener.js new file mode 100644 index 0000000..4086c9f --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/restListener.js @@ -0,0 +1,53 @@ +//>>built +// wrapped by build app +define("dojox/data/restListener", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +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.7.2/dojox/data/s3/README b/js/dojo-1.7.2/dojox/data/s3/README new file mode 100644 index 0000000..cfa2347 --- /dev/null +++ b/js/dojo-1.7.2/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.7.2/dojox/data/s3/proxy.example-php b/js/dojo-1.7.2/dojox/data/s3/proxy.example-php new file mode 100644 index 0000000..e5b57e1 --- /dev/null +++ b/js/dojo-1.7.2/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.7.2/dojox/data/util/JsonQuery.js b/js/dojo-1.7.2/dojox/data/util/JsonQuery.js new file mode 100644 index 0000000..bd90f35 --- /dev/null +++ b/js/dojo-1.7.2/dojox/data/util/JsonQuery.js @@ -0,0 +1,99 @@ +//>>built +define("dojox/data/util/JsonQuery", ["dojo", "dojox"], function(dojo, dojox) { + +// 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); + } + +}); + +return dojox.data.util.JsonQuery; +}); |
