diff options
Diffstat (limited to 'js/dojo/dojox/rpc')
25 files changed, 3034 insertions, 0 deletions
diff --git a/js/dojo/dojox/rpc/Client.js b/js/dojo/dojox/rpc/Client.js new file mode 100644 index 0000000..2da1981 --- /dev/null +++ b/js/dojo/dojox/rpc/Client.js @@ -0,0 +1,29 @@ +//>>built +define("dojox/rpc/Client", ["dojo", "dojox"], function(dojo, dojox) { + + dojo.getObject("rpc.Client", true, dojox); + + // Provide extra headers for robust client and server communication + + dojo._defaultXhr = dojo.xhr; + dojo.xhr = function(method,args){ + var headers = args.headers = args.headers || {}; + // set the client id, this can be used by servers to maintain state information with the + // a specific client. Many servers rely on sessions for this, but sessions are shared + // between tabs/windows, so this is not appropriate for application state, it + // really only useful for storing user authentication + headers["Client-Id"] = dojox.rpc.Client.clientId; + // set the sequence id. HTTP is non-deterministic, message can arrive at the server + // out of order. In complex Ajax applications, it may be more to ensure that messages + // can be properly sequenced deterministically. This applies a sequency id to each + // XHR request so that the server can order them. + headers["Seq-Id"] = dojox._reqSeqId = (dojox._reqSeqId||0)+1; + return dojo._defaultXhr.apply(dojo,arguments); + } + + // initiate the client id to a good random number + dojox.rpc.Client.clientId = (Math.random() + '').substring(2,14) + (new Date().getTime() + '').substring(8,13); + + return dojox.rpc.Client; + +}); diff --git a/js/dojo/dojox/rpc/JsonRPC.js b/js/dojo/dojox/rpc/JsonRPC.js new file mode 100644 index 0000000..248afcd --- /dev/null +++ b/js/dojo/dojox/rpc/JsonRPC.js @@ -0,0 +1,55 @@ +//>>built +define("dojox/rpc/JsonRPC", ["dojo", "dojox", "dojox/rpc/Service"], function(dojo, dojox) { + + function jsonRpcEnvelope(version){ + return { + serialize: function(smd, method, data, options){ + //not converted to json it self. This will be done, if + //appropriate, at the transport level + + var d = { + id: this._requestId++, + method: method.name, + params: data + }; + if(version){ + d.jsonrpc = version; + } + return { + data: dojo.toJson(d), + handleAs:'json', + contentType: 'application/json', + transport:"POST" + }; + }, + + deserialize: function(obj){ + if ('Error' == obj.name){ + obj = dojo.fromJson(obj.responseText); + } + if(obj.error) { + var e = new Error(obj.error.message || obj.error); + e._rpcErrorObject = obj.error; + return e; + } + return obj.result; + } + }; + } + dojox.rpc.envelopeRegistry.register( + "JSON-RPC-1.0", + function(str){ + return str == "JSON-RPC-1.0"; + }, + dojo.mixin({namedParams:false},jsonRpcEnvelope()) // 1.0 will only work with ordered params + ); + + dojox.rpc.envelopeRegistry.register( + "JSON-RPC-2.0", + function(str){ + return str == "JSON-RPC-2.0"; + }, + jsonRpcEnvelope("2.0") + ); + +}); diff --git a/js/dojo/dojox/rpc/JsonRest.js b/js/dojo/dojox/rpc/JsonRest.js new file mode 100644 index 0000000..f6bae55 --- /dev/null +++ b/js/dojo/dojox/rpc/JsonRest.js @@ -0,0 +1,458 @@ +//>>built +define("dojox/rpc/JsonRest", ["dojo", "dojox", "dojox/json/ref", "dojox/rpc/Rest"], function(dojo, dojox) { + var dirtyObjects = []; + var Rest = dojox.rpc.Rest; + var jr; + function resolveJson(service, deferred, value, defaultId){ + var timeStamp = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Last-Modified"); + if(timeStamp && Rest._timeStamps){ + Rest._timeStamps[defaultId] = timeStamp; + } + var hrefProperty = service._schema && service._schema.hrefProperty; + if(hrefProperty){ + dojox.json.ref.refAttribute = hrefProperty; + } + value = value && dojox.json.ref.resolveJson(value, { + defaultId: defaultId, + index: Rest._index, + timeStamps: timeStamp && Rest._timeStamps, + time: timeStamp, + idPrefix: service.servicePath.replace(/[^\/]*$/,''), + idAttribute: jr.getIdAttribute(service), + schemas: jr.schemas, + loader: jr._loader, + idAsRef: service.idAsRef, + assignAbsoluteIds: true + }); + dojox.json.ref.refAttribute = "$ref"; + return value; + } + jr = dojox.rpc.JsonRest={ + serviceClass: dojox.rpc.Rest, + conflictDateHeader: "If-Unmodified-Since", + commit: function(kwArgs){ + // summary: + // Saves the dirty data using REST Ajax methods + + kwArgs = kwArgs || {}; + var actions = []; + var alreadyRecorded = {}; + var savingObjects = []; + for(var i = 0; i < dirtyObjects.length; i++){ + var dirty = dirtyObjects[i]; + var object = dirty.object; + var old = dirty.old; + var append = false; + if(!(kwArgs.service && (object || old) && + (object || old).__id.indexOf(kwArgs.service.servicePath)) && dirty.save){ + delete object.__isDirty; + if(object){ + if(old){ + // changed object + var pathParts; + if((pathParts = object.__id.match(/(.*)#.*/))){ // it is a path reference + // this means it is a sub object, we must go to the parent object and save it + object = Rest._index[pathParts[1]]; + } + if(!(object.__id in alreadyRecorded)){// if it has already been saved, we don't want to repeat it + // record that we are saving + alreadyRecorded[object.__id] = object; + if(kwArgs.incrementalUpdates + && !pathParts){ // I haven't figured out how we would do incremental updates on sub-objects yet + // make an incremental update using a POST + var incremental = (typeof kwArgs.incrementalUpdates == 'function' ? + kwArgs.incrementalUpdates : function(){ + incremental = {}; + for(var j in object){ + if(object.hasOwnProperty(j)){ + if(object[j] !== old[j]){ + incremental[j] = object[j]; + } + }else if(old.hasOwnProperty(j)){ + // we can't use incremental updates to remove properties + return null; + } + } + return incremental; + })(object, old); + } + + if(incremental){ + actions.push({method:"post",target:object, content: incremental}); + } + else{ + actions.push({method:"put",target:object,content:object}); + } + } + }else{ + // new object + var service = jr.getServiceAndId(object.__id).service; + var idAttribute = jr.getIdAttribute(service); + if((idAttribute in object) && !kwArgs.alwaysPostNewItems){ + // if the id attribute is specified, then we should know the location + actions.push({method:"put",target:object, content:object}); + }else{ + actions.push({method:"post",target:{__id:service.servicePath}, + content:object}); + } + } + }else if(old){ + // deleted object + actions.push({method:"delete",target:old}); + }//else{ this would happen if an object is created and then deleted, don't do anything + savingObjects.push(dirty); + dirtyObjects.splice(i--,1); + } + } + dojo.connect(kwArgs,"onError",function(){ + if(kwArgs.revertOnError !== false){ + var postCommitDirtyObjects = dirtyObjects; + dirtyObjects = savingObjects; + var numDirty = 0; // make sure this does't do anything if it is called again + jr.revert(); // revert if there was an error + dirtyObjects = postCommitDirtyObjects; + } + else{ + dirtyObjects = dirtyObject.concat(savingObjects); + } + }); + jr.sendToServer(actions, kwArgs); + return actions; + }, + sendToServer: function(actions, kwArgs){ + var xhrSendId; + var plainXhr = dojo.xhr; + var left = actions.length;// this is how many changes are remaining to be received from the server + var i, contentLocation; + var timeStamp; + var conflictDateHeader = this.conflictDateHeader; + // add headers for extra information + dojo.xhr = function(method,args){ + // keep the transaction open as we send requests + args.headers = args.headers || {}; + // the last one should commit the transaction + args.headers['Transaction'] = actions.length - 1 == i ? "commit" : "open"; + if(conflictDateHeader && timeStamp){ + args.headers[conflictDateHeader] = timeStamp; + } + if(contentLocation){ + args.headers['Content-ID'] = '<' + contentLocation + '>'; + } + return plainXhr.apply(dojo,arguments); + }; + for(i =0; i < actions.length;i++){ // iterate through the actions to execute + var action = actions[i]; + dojox.rpc.JsonRest._contentId = action.content && action.content.__id; // this is used by OfflineRest + var isPost = action.method == 'post'; + timeStamp = action.method == 'put' && Rest._timeStamps[action.content.__id]; + if(timeStamp){ + // update it now + Rest._timeStamps[action.content.__id] = (new Date()) + ''; + } + // send the content location to the server + contentLocation = isPost && dojox.rpc.JsonRest._contentId; + var serviceAndId = jr.getServiceAndId(action.target.__id); + var service = serviceAndId.service; + var dfd = action.deferred = service[action.method]( + serviceAndId.id.replace(/#/,''), // if we are using references, we need eliminate # + dojox.json.ref.toJson(action.content, false, service.servicePath, true) + ); + (function(object, dfd, service){ + dfd.addCallback(function(value){ + try{ + // Implements id assignment per the HTTP specification + var newId = dfd.ioArgs.xhr && dfd.ioArgs.xhr.getResponseHeader("Location"); + //TODO: match URLs if the servicePath is relative... + if(newId){ + // if the path starts in the middle of an absolute URL for Location, we will use the just the path part + var startIndex = newId.match(/(^\w+:\/\/)/) && newId.indexOf(service.servicePath); + newId = startIndex > 0 ? newId.substring(startIndex) : (service.servicePath + newId). + // now do simple relative URL resolution in case of a relative URL. + replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,'$2$3'); + object.__id = newId; + Rest._index[newId] = object; + } + value = resolveJson(service, dfd, value, object && object.__id); + }catch(e){} + if(!(--left)){ + if(kwArgs.onComplete){ + kwArgs.onComplete.call(kwArgs.scope, actions); + } + } + return value; + }); + })(action.content, dfd, service); + + dfd.addErrback(function(value){ + + // on an error we want to revert, first we want to separate any changes that were made since the commit + left = -1; // first make sure that success isn't called + kwArgs.onError.call(kwArgs.scope, value); + }); + } + // revert back to the normal XHR handler + dojo.xhr = plainXhr; + + }, + getDirtyObjects: function(){ + return dirtyObjects; + }, + revert: function(service){ + // summary: + // Reverts all the changes made to JSON/REST data + for(var i = dirtyObjects.length; i > 0;){ + i--; + var dirty = dirtyObjects[i]; + var object = dirty.object; + var old = dirty.old; + var store = dojox.data._getStoreForItem(object || old); + + if(!(service && (object || old) && + (object || old).__id.indexOf(service.servicePath))){ + // if we are in the specified store or if this is a global revert + if(object && old){ + // changed + for(var j in old){ + if(old.hasOwnProperty(j) && object[j] !== old[j]){ + if(store){ + store.onSet(object, j, object[j], old[j]); + } + object[j] = old[j]; + } + } + for(j in object){ + if(!old.hasOwnProperty(j)){ + if(store){ + store.onSet(object, j, object[j]); + } + delete object[j]; + } + } + }else if(!old){ + // was an addition, remove it + if(store){ + store.onDelete(object); + } + }else{ + // was a deletion, we will add it back + if(store){ + store.onNew(old); + } + } + delete (object || old).__isDirty; + dirtyObjects.splice(i, 1); + } + } + }, + changing: function(object,_deleting){ + // summary: + // adds an object to the list of dirty objects. This object + // contains a reference to the object itself as well as a + // cloned and trimmed version of old object for use with + // revert. + if(!object.__id){ + return; + } + object.__isDirty = true; + //if an object is already in the list of dirty objects, don't add it again + //or it will overwrite the premodification data set. + for(var i=0; i<dirtyObjects.length; i++){ + var dirty = dirtyObjects[i]; + if(object==dirty.object){ + if(_deleting){ + // we are deleting, no object is an indicator of deletiong + dirty.object = false; + if(!this._saveNotNeeded){ + dirty.save = true; + } + } + return; + } + } + var old = object instanceof Array ? [] : {}; + for(i in object){ + if(object.hasOwnProperty(i)){ + old[i] = object[i]; + } + } + dirtyObjects.push({object: !_deleting && object, old: old, save: !this._saveNotNeeded}); + }, + deleteObject: function(object){ + // summary: + // deletes an object + // object: + // object to delete + this.changing(object,true); + }, + getConstructor: function(/*Function|String*/service, schema){ + // summary: + // Creates or gets a constructor for objects from this service + if(typeof service == 'string'){ + var servicePath = service; + service = new dojox.rpc.Rest(service,true); + this.registerService(service, servicePath, schema); + } + if(service._constructor){ + return service._constructor; + } + service._constructor = function(data){ + // summary: + // creates a new object for this table + // + // data: + // object to mixed in + var self = this; + var args = arguments; + var properties; + var initializeCalled; + function addDefaults(schema){ + if(schema){ + addDefaults(schema['extends']); + properties = schema.properties; + for(var i in properties){ + var propDef = properties[i]; + if(propDef && (typeof propDef == 'object') && ("default" in propDef)){ + self[i] = propDef["default"]; + } + } + } + if(schema && schema.prototype && schema.prototype.initialize){ + initializeCalled = true; + schema.prototype.initialize.apply(self, args); + } + } + addDefaults(service._schema); + if(!initializeCalled && data && typeof data == 'object'){ + dojo.mixin(self,data); + } + var idAttribute = jr.getIdAttribute(service); + Rest._index[this.__id = this.__clientId = + service.servicePath + (this[idAttribute] || + Math.random().toString(16).substring(2,14) + '@' + ((dojox.rpc.Client && dojox.rpc.Client.clientId) || "client"))] = this; + if(dojox.json.schema && properties){ + dojox.json.schema.mustBeValid(dojox.json.schema.validate(this, service._schema)); + } + dirtyObjects.push({object:this, save: true}); + }; + return dojo.mixin(service._constructor, service._schema, {load:service}); + }, + fetch: function(absoluteId){ + // summary: + // Fetches a resource by an absolute path/id and returns a dojo.Deferred. + var serviceAndId = jr.getServiceAndId(absoluteId); + return this.byId(serviceAndId.service,serviceAndId.id); + }, + getIdAttribute: function(service){ + // summary: + // Return the ids attribute used by this service (based on it's schema). + // Defaults to "id", if not other id is defined + var schema = service._schema; + var idAttr; + if(schema){ + if(!(idAttr = schema._idAttr)){ + for(var i in schema.properties){ + if(schema.properties[i].identity || (schema.properties[i].link == "self")){ + schema._idAttr = idAttr = i; + } + } + } + } + return idAttr || 'id'; + }, + getServiceAndId: function(/*String*/absoluteId){ + // summary: + // Returns the REST service and the local id for the given absolute id. The result + // is returned as an object with a service property and an id property + // absoluteId: + // This is the absolute id of the object + var serviceName = ''; + + for(var service in jr.services){ + if((absoluteId.substring(0, service.length) == service) && (service.length >= serviceName.length)){ + serviceName = service; + } + } + if (serviceName){ + return {service: jr.services[serviceName], id:absoluteId.substring(serviceName.length)}; + } + var parts = absoluteId.match(/^(.*\/)([^\/]*)$/); + return {service: new jr.serviceClass(parts[1], true), id:parts[2]}; + }, + services:{}, + schemas:{}, + registerService: function(/*Function*/ service, /*String*/ servicePath, /*Object?*/ schema){ + // summary: + // Registers a service for as a JsonRest service, mapping it to a path and schema + // service: + // This is the service to register + // servicePath: + // This is the path that is used for all the ids for the objects returned by service + // schema: + // This is a JSON Schema object to associate with objects returned by this service + servicePath = service.servicePath = servicePath || service.servicePath; + service._schema = jr.schemas[servicePath] = schema || service._schema || {}; + jr.services[servicePath] = service; + }, + byId: function(service, id){ + // if caching is allowed, we look in the cache for the result + var deferred, result = Rest._index[(service.servicePath || '') + id]; + if(result && !result._loadObject){// cache hit + deferred = new dojo.Deferred(); + deferred.callback(result); + return deferred; + } + return this.query(service, id); + }, + query: function(service, id, args){ + var deferred = service(id, args); + + deferred.addCallback(function(result){ + if(result.nodeType && result.cloneNode){ + // return immediately if it is an XML document + return result; + } + return resolveJson(service, deferred, result, typeof id != 'string' || (args && (args.start || args.count)) ? undefined: id); + }); + return deferred; + }, + _loader: function(callback){ + // load a lazy object + var serviceAndId = jr.getServiceAndId(this.__id); + var self = this; + jr.query(serviceAndId.service, serviceAndId.id).addBoth(function(result){ + // if they are the same this means an object was loaded, otherwise it + // might be a primitive that was loaded or maybe an error + if(result == self){ + // we can clear the flag, so it is a loaded object + delete result.$ref; + delete result._loadObject; + }else{ + // it is probably a primitive value, we can't change the identity of an object to + // the loaded value, so we will keep it lazy, but define the lazy loader to always + // return the loaded value + self._loadObject = function(callback){ + callback(result); + }; + } + callback(result); + }); + }, + isDirty: function(item, store){ + // summary + // returns true if the item is marked as dirty or true if there are any dirty items + if(!item){ + if(store){ + return dojo.some(dirtyObjects, function(dirty){ + return dojox.data._getStoreForItem(dirty.object || dirty.old) == store; + }); + } + return !!dirtyObjects.length; + } + return item.__isDirty; + } + + }; + + return dojox.rpc.JsonRest; +}); + diff --git a/js/dojo/dojox/rpc/OfflineRest.js b/js/dojo/dojox/rpc/OfflineRest.js new file mode 100644 index 0000000..c18954d --- /dev/null +++ b/js/dojo/dojox/rpc/OfflineRest.js @@ -0,0 +1,257 @@ +//>>built +define("dojox/rpc/OfflineRest", ["dojo", "dojox", "dojox/data/ClientFilter", "dojox/rpc/Rest", "dojox/storage"], function(dojo, dojox) { +// summary: +// Makes the REST service be able to store changes in local +// storage so it can be used offline automatically. + var Rest = dojox.rpc.Rest; + var namespace = "dojox_rpc_OfflineRest"; + var loaded; + var index = Rest._index; + dojox.storage.manager.addOnLoad(function(){ + // now that we are loaded we need to save everything in the index + loaded = dojox.storage.manager.available; + for(var i in index){ + saveObject(index[i], i); + } + }); + var dontSave; + function getStorageKey(key){ + // returns a key that is safe to use in storage + return key.replace(/[^0-9A-Za-z_]/g,'_'); + } + function saveObject(object,id){ + // save the object into local storage + + if(loaded && !dontSave && (id || (object && object.__id))){ + dojox.storage.put( + getStorageKey(id||object.__id), + typeof object=='object'?dojox.json.ref.toJson(object):object, // makeshift technique to determine if the object is json object or not + function(){}, + namespace); + } + } + function isNetworkError(error){ + // determine if the error was a network error and should be saved offline + // or if it was a server error and not a result of offline-ness + return error instanceof Error && (error.status == 503 || error.status > 12000 || !error.status); // TODO: Make the right error determination + } + function sendChanges(){ + // periodical try to save our dirty data + if(loaded){ + var dirty = dojox.storage.get("dirty",namespace); + if(dirty){ + for (var dirtyId in dirty){ + commitDirty(dirtyId,dirty); + } + } + } + } + var OfflineRest; + function sync(){ + OfflineRest.sendChanges(); + OfflineRest.downloadChanges(); + } + var syncId = setInterval(sync,15000); + dojo.connect(document, "ononline", sync); + OfflineRest = dojox.rpc.OfflineRest = { + turnOffAutoSync: function(){ + clearInterval(syncId); + }, + sync: sync, + sendChanges: sendChanges, + downloadChanges: function(){ + + }, + addStore: function(/*data-store*/store,/*query?*/baseQuery){ + // summary: + // Adds a store to the monitored store for local storage + // store: + // Store to add + // baseQuery: + // This is the base query to should be used to load the items for + // the store. Generally you want to load all the items that should be + // available when offline. + OfflineRest.stores.push(store); + store.fetch({queryOptions:{cache:true},query:baseQuery,onComplete:function(results,args){ + store._localBaseResults = results; + store._localBaseFetch = args; + }}); + + } + }; + OfflineRest.stores = []; + var defaultGet = Rest._get; + Rest._get = function(service, id){ + // We specifically do NOT want the paging information to be used by the default handler, + // this is because online apps want to minimize the data transfer, + // but an offline app wants the opposite, as much data as possible transferred to + // the client side + try{ + // if we are reloading the application with local dirty data in an online environment + // we want to make sure we save the changes first, so that we get up-to-date + // information from the server + sendChanges(); + if(window.navigator && navigator.onLine===false){ + // we force an error if we are offline in firefox, otherwise it will silently load it from the cache + throw new Error(); + } + var dfd = defaultGet(service, id); + }catch(e){ + dfd = new dojo.Deferred(); + dfd.errback(e); + } + var sync = dojox.rpc._sync; + dfd.addCallback(function(result){ + saveObject(result, service._getRequest(id).url); + return result; + }); + dfd.addErrback(function(error){ + if(loaded){ + // if the storage is loaded, we can go ahead and get the object out of storage + if(isNetworkError(error)){ + var loadedObjects = {}; + // network error, load from local storage + var byId = function(id,backup){ + if(loadedObjects[id]){ + return backup; + } + var result = dojo.fromJson(dojox.storage.get(getStorageKey(id),namespace)) || backup; + + loadedObjects[id] = result; + for(var i in result){ + var val = result[i]; // resolve references if we can + id = val && val.$ref; + if (id){ + if(id.substring && id.substring(0,4) == "cid:"){ + // strip the cid scheme, we should be able to resolve it locally + id = id.substring(4); + } + result[i] = byId(id,val); + } + } + if (result instanceof Array){ + //remove any deleted items + for (i = 0;i<result.length;i++){ + if (result[i]===undefined){ + result.splice(i--,1); + } + } + } + return result; + }; + dontSave = true; // we don't want to be resaving objects when loading from local storage + //TODO: Should this reuse something from dojox.rpc.Rest + var result = byId(service._getRequest(id).url); + + if(!result){// if it is not found we have to just return the error + return error; + } + dontSave = false; + return result; + } + else{ + return error; // server error, let the error propagate + } + } + else{ + if(sync){ + return new Error("Storage manager not loaded, can not continue"); + } + // we are not loaded, so we need to defer until we are loaded + dfd = new dojo.Deferred(); + dfd.addCallback(arguments.callee); + dojox.storage.manager.addOnLoad(function(){ + dfd.callback(); + }); + return dfd; + } + }); + return dfd; + }; + function changeOccurred(method, absoluteId, contentId, serializedContent, service){ + if(method=='delete'){ + dojox.storage.remove(getStorageKey(absoluteId),namespace); + } + else{ + // both put and post should store the actual object + dojox.storage.put(getStorageKey(contentId), serializedContent, function(){ + },namespace); + } + var store = service && service._store; + // record all the updated queries + if(store){ + store.updateResultSet(store._localBaseResults, store._localBaseFetch); + dojox.storage.put(getStorageKey(service._getRequest(store._localBaseFetch.query).url),dojox.json.ref.toJson(store._localBaseResults),function(){ + },namespace); + + } + + } + dojo.addOnLoad(function(){ + dojo.connect(dojox.data, "restListener", function(message){ + var channel = message.channel; + var method = message.event.toLowerCase(); + var service = dojox.rpc.JsonRest && dojox.rpc.JsonRest.getServiceAndId(channel).service; + changeOccurred( + method, + channel, + method == "post" ? channel + message.result.id : channel, + dojo.toJson(message.result), + service + ); + + }); + }); + //FIXME: Should we make changes after a commit to see if the server rejected the change + // or should we come up with a revert mechanism? + var defaultChange = Rest._change; + Rest._change = function(method,service,id,serializedContent){ + if(!loaded){ + return defaultChange.apply(this,arguments); + } + var absoluteId = service._getRequest(id).url; + changeOccurred(method, absoluteId, dojox.rpc.JsonRest._contentId, serializedContent, service); + var dirty = dojox.storage.get("dirty",namespace) || {}; + if (method=='put' || method=='delete'){ + // these supersede so we can overwrite anything using this id + var dirtyId = absoluteId; + } + else{ + dirtyId = 0; + for (var i in dirty){ + if(!isNaN(parseInt(i))){ + dirtyId = i; + } + } // get the last dirtyId to make a unique id for non-idempotent methods + dirtyId++; + } + dirty[dirtyId] = {method:method,id:absoluteId,content:serializedContent}; + return commitDirty(dirtyId,dirty); + }; + function commitDirty(dirtyId, dirty){ + var dirtyItem = dirty[dirtyId]; + var serviceAndId = dojox.rpc.JsonRest.getServiceAndId(dirtyItem.id); + var deferred = defaultChange(dirtyItem.method,serviceAndId.service,serviceAndId.id,dirtyItem.content); + // add it to our list of dirty objects + dirty[dirtyId] = dirtyItem; + dojox.storage.put("dirty",dirty,function(){},namespace); + deferred.addBoth(function(result){ + if (isNetworkError(result)){ + // if a network error (offlineness) was the problem, we leave it + // dirty, and return to indicate successfulness + return null; + } + // it was successful or the server rejected it, we remove it from the dirty list + var dirty = dojox.storage.get("dirty",namespace) || {}; + delete dirty[dirtyId]; + dojox.storage.put("dirty",dirty,function(){},namespace); + return result; + }); + return deferred; + } + + dojo.connect(index,"onLoad",saveObject); + dojo.connect(index,"onUpdate",saveObject); + + return dojox.rpc.OfflineRest; +}); diff --git a/js/dojo/dojox/rpc/ProxiedPath.js b/js/dojo/dojox/rpc/ProxiedPath.js new file mode 100644 index 0000000..7e3db26 --- /dev/null +++ b/js/dojo/dojox/rpc/ProxiedPath.js @@ -0,0 +1,29 @@ +//>>built +define("dojox/rpc/ProxiedPath", ["dojo", "dojox", "dojox/rpc/Service"], function(dojo, dojox) { + +dojox.rpc.envelopeRegistry.register( + "PROXIED-PATH",function(str){return str == "PROXIED-PATH"},{ + serialize:function(smd, method, data){ + var i; + var target = dojox.rpc.getTarget(smd, method); + if(dojo.isArray(data)){ + for(i = 0; i < data.length;i++){ + target += '/' + (data[i] == null ? "" : data[i]); + } + }else{ + for(i in data){ + target += '/' + i + '/' + data[i]; + } + } + return { + data:'', + target: (method.proxyUrl || smd.proxyUrl) + "?url=" + encodeURIComponent(target) + }; + }, + deserialize:function(results){ + return results; + } + } +); + +}); diff --git a/js/dojo/dojox/rpc/README b/js/dojo/dojox/rpc/README new file mode 100644 index 0000000..af38011 --- /dev/null +++ b/js/dojo/dojox/rpc/README @@ -0,0 +1,56 @@ +------------------------------------------------------------------------------- +DojoX RPC +------------------------------------------------------------------------------- +Version 1.0 +Release date: 07/01/2007 +------------------------------------------------------------------------------- +Project state: +yahoo.smd: production (but for old dojo.rpc, not the dojox.service.Rpc) +Service.js: beta - this will become dojo.rpc.Service eventually and replace + the existing rpc system +JsonRPC.js: beta - plugins for json-rpc for the rpc system +Rest.js: beta - plugin for REST style services for the rpc system +JsonRest.js: beta - A module for tracking and persisting changes to JSON objects +through REST. +OfflineRest.js: alpha - A module for storing REST changes locally as write- +back cache for offline and intermittent connection situations +ProxiedPath.js: beta - plugin for the PATH envelope to work with a simple PHP proxy +Client.js: alpha - A module for maintaining per page/tab client sessions and +deterministic message sequencing with a server. + +SMDLibrary contains smd files representing external services. + +------------------------------------------------------------------------------- +Project authors + Dustin Machi + Kris Zyp + Revin Guillen +------------------------------------------------------------------------------- +Project description + +Extra utilities for use with dojo.rpc as well as additional smd descriptions +for specific services. +------------------------------------------------------------------------------- +Dependencies: + +Dojo Core (package loader, dojo.rpc). +------------------------------------------------------------------------------- +Documentation + +See RPC documentation at http://dojotoolkit.org/ + +------------------------------------------------------------------------------- +Installation instructions + +Grab the following from the Dojo SVN Repository: +http://svn.dojotoolkit.org/var/src/dojo/dojox/trunk/rpc/* + +Install into the following directory structure: +/dojox/rpc/ + +...which should be at the same level as your Dojo checkout. + +The use of the actual utilities varies, but typically additional software +will be included with dojo.require() and smd files for services can be loaded +manually with an xhr request or can be provided by url to the service when created + diff --git a/js/dojo/dojox/rpc/Rest.js b/js/dojo/dojox/rpc/Rest.js new file mode 100644 index 0000000..1964479 --- /dev/null +++ b/js/dojo/dojox/rpc/Rest.js @@ -0,0 +1,147 @@ +//>>built +define("dojox/rpc/Rest", ["dojo", "dojox"], function(dojo, dojox) { +// Note: This doesn't require dojox.rpc.Service, and if you want it you must require it +// yourself, and you must load it prior to dojox.rpc.Rest. + +// summary: +// This provides a HTTP REST service with full range REST verbs include PUT,POST, and DELETE. +// description: +// A normal GET query is done by using the service directly: +// | var restService = dojox.rpc.Rest("Project"); +// | restService("4"); +// This will do a GET for the URL "/Project/4". +// | restService.put("4","new content"); +// This will do a PUT to the URL "/Project/4" with the content of "new content". +// You can also use the SMD service to generate a REST service: +// | var services = dojox.rpc.Service({services: {myRestService: {transport: "REST",... +// | services.myRestService("parameters"); +// +// The modifying methods can be called as sub-methods of the rest service method like: +// | services.myRestService.put("parameters","data to put in resource"); +// | services.myRestService.post("parameters","data to post to the resource"); +// | services.myRestService['delete']("parameters"); + + dojo.getObject("rpc.Rest", true, dojox); + + if(dojox.rpc && dojox.rpc.transportRegistry){ + // register it as an RPC service if the registry is available + dojox.rpc.transportRegistry.register( + "REST", + function(str){return str == "REST";}, + { + getExecutor : function(func,method,svc){ + return new dojox.rpc.Rest( + method.name, + (method.contentType||svc._smd.contentType||"").match(/json|javascript/), // isJson + null, + function(id, args){ + var request = svc._getRequest(method,[id]); + request.url= request.target + (request.data ? '?'+ request.data : ''); + if(args && (args.start >= 0 || args.count >= 0)){ + request.headers = request.headers || {}; + request.headers.Range = "items=" + (args.start || '0') + '-' + + (("count" in args && args.count != Infinity) ? + (args.count + (args.start || 0) - 1) : ''); + } + return request; + } + ); + } + } + ); + } + var drr; + + function index(deferred, service, range, id){ + deferred.addCallback(function(result){ + if(deferred.ioArgs.xhr && range){ + // try to record the total number of items from the range header + range = deferred.ioArgs.xhr.getResponseHeader("Content-Range"); + deferred.fullLength = range && (range=range.match(/\/(.*)/)) && parseInt(range[1]); + } + return result; + }); + return deferred; + } + drr = dojox.rpc.Rest = function(/*String*/path, /*Boolean?*/isJson, /*Object?*/schema, /*Function?*/getRequest){ + // summary: + // Creates a REST service using the provided path. + var service; + // it should be in the form /Table/ + service = function(id, args){ + return drr._get(service, id, args); + }; + service.isJson = isJson; + service._schema = schema; + // cache: + // This is an object that provides indexing service + // This can be overriden to take advantage of more complex referencing/indexing + // schemes + service.cache = { + serialize: isJson ? ((dojox.json && dojox.json.ref) || dojo).toJson : function(result){ + return result; + } + }; + // the default XHR args creator: + service._getRequest = getRequest || function(id, args){ + if(dojo.isObject(id)){ + id = dojo.objectToQuery(id); + id = id ? "?" + id: ""; + } + if(args && args.sort && !args.queryStr){ + id += (id ? "&" : "?") + "sort(" + for(var i = 0; i<args.sort.length; i++){ + var sort = args.sort[i]; + id += (i > 0 ? "," : "") + (sort.descending ? '-' : '+') + encodeURIComponent(sort.attribute); + } + id += ")"; + } + var request = { + url: path + (id == null ? "" : id), + handleAs: isJson ? 'json' : 'text', + contentType: isJson ? 'application/json' : 'text/plain', + sync: dojox.rpc._sync, + headers: { + Accept: isJson ? 'application/json,application/javascript' : '*/*' + } + }; + if(args && (args.start >= 0 || args.count >= 0)){ + request.headers.Range = "items=" + (args.start || '0') + '-' + + (("count" in args && args.count != Infinity) ? + (args.count + (args.start || 0) - 1) : ''); + } + dojox.rpc._sync = false; + return request; + }; + // each calls the event handler + function makeRest(name){ + service[name] = function(id,content){ + return drr._change(name,service,id,content); // the last parameter is to let the OfflineRest know where to store the item + }; + } + makeRest('put'); + makeRest('post'); + makeRest('delete'); + // record the REST services for later lookup + service.servicePath = path; + return service; + }; + + drr._index={};// the map of all indexed objects that have gone through REST processing + drr._timeStamps={}; + // these do the actual requests + drr._change = function(method,service,id,content){ + // this is called to actually do the put, post, and delete + var request = service._getRequest(id); + request[method+"Data"] = content; + return index(dojo.xhr(method.toUpperCase(),request,true),service); + }; + + drr._get= function(service,id, args){ + args = args || {}; + // this is called to actually do the get + return index(dojo.xhrGet(service._getRequest(id, args)), service, (args.start >= 0 || args.count >= 0), id); + }; + + return dojox.rpc.Rest; +}); diff --git a/js/dojo/dojox/rpc/SMDLibrary/dojo-api.smd b/js/dojo/dojox/rpc/SMDLibrary/dojo-api.smd new file mode 100644 index 0000000..1b2db85 --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/dojo-api.smd @@ -0,0 +1,30 @@ +{ + envelope: "URL", + transport: "JSONP", + callbackParamName: "callback", + services: { + get: { + target: "http://api.dojotoolkit.org/jsdoc/jsonp", + parameters: [ + { name: "name", type: "string", optional: false }, + { name: "exact", type: "boolean", optional: true }, + { name: "recursion", type: "boolean", optional: true }, + { name: "resource", type: "string", optional: true }, + { name: "project", type: "string", optional: true }, + { name: "version", type: "string", optional: true }, + { name: "attributes", type: "array", optional: true } + ] + }, + batch: { + target: "http://api.dojotoolkit.org/jsdoc/jsonp/batch", + parameters: [ + { name: "names", type: "array", optional: false }, + { name: "exact", type: "boolean", optional: true }, + { name: "recursion", type: "boolean", optional: true }, + { name: "project", type: "string", optional: true }, + { name: "version", type: "string", optional: true }, + { name: "attributes", type: "array", optional: false } + ] + } + } +}
\ No newline at end of file diff --git a/js/dojo/dojox/rpc/SMDLibrary/friendfeed.smd b/js/dojo/dojox/rpc/SMDLibrary/friendfeed.smd new file mode 100644 index 0000000..a18aa8e --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/friendfeed.smd @@ -0,0 +1,49 @@ +{ + "SMDVersion": "2.0", + "id": "http://friendfeed.com/api", + "description": "Friendfeed's API - documentation at http://code.google.com/p/friendfeed-api/wiki/ApiDocumentation", + + transport: "JSONP", + envelope: "URL", + + additionalParameters: true, + parameters: [ + { name: "format", optional: false, "default": "json" }, + { name: "service", optional: true }, + { name: "start", optional: true, type: "integer" }, + { name: "num", optional: true, type: "integer" } + ], + + services: { + users: { + target: "http://friendfeed.com/api/feed/user", + parameters: [ + { name: "nickname", type: "string", optional: false, "default": "" } + ] + }, + entry: { + target: "http://friendfeed.com/api/feed/entry", + parameters: [ + { name: "entry_id", type: "string", optional: false, "default": "" } + ] + }, + search: { + target: "http://friendfeed.com/api/feed/search", + parameters: [ + { name: "q", type: "string", optional: false, "default": "" } + ] + }, + url: { + target: "http://friendfeed.com/api/feed/url", + parameters: [ + { name: "url", type: "string", optional: false, "default": "" } + ] + }, + domain: { + target: "http://friendfeed.com/api/feed/domain", + parameters: [ + { name: "domain", type: "string", optional: false, "default":"" } + ] + } + } +} diff --git a/js/dojo/dojox/rpc/SMDLibrary/geonames.smd b/js/dojo/dojox/rpc/SMDLibrary/geonames.smd new file mode 100644 index 0000000..04f04db --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/geonames.smd @@ -0,0 +1,263 @@ +{ + + // JSON REST services provided by geonames.org + // adapted from: http://www.geonames.org/export/JSON-webservices.html + // http://creativecommons.org/licenses/by/3.0/ + // INCOMPLETE IMPLEMENTATION + + "SMDVersion":"2.0", + "id":"/geonames", + "transport":"JSONP", + "envelope":"URL", + "target": "http://ws.geonames.org/", + + "parameters" : [ + {name:"type", optional: false, "default":"JSON"} + ], + + "services":{ + getCountryInfo:{ + // Result : returns info about a country such as name, population, etc. + // If country parameter is not specified, that information is returned for + // every country + "target": "countryInfoJSON", + "parameters": [ + { name: "country", type:"STRING", optional: true }, + { name: "lang", type:"STRING"}, // default: "en" + { name: "maxRows", type:"INTEGER", optional: true } // default: 10 + ] + }, + + getCities:{ + // Result : returns a list of cities and placenames in the bounding box, + // ordered by relevancy (capital/population). Placenames close together + // are filterered out and only the larger name is included in the resulting list. + "target": "citiesJSON", + "parameters": [ + { name: "north", type:"FLOAT"}, + { name: "south", type:"FLOAT"}, + { name: "east", type:"FLOAT"}, + { name: "west", type:"FLOAT"}, + { name: "lang", type:"STRING"}, // default: "en" + { name: "maxRows", type:"INTEGER", optional: true } // deault: 10 + ] + }, + + "getQuakes":{ + // Result : returns a list of earthquakes, ordered by magnitude + "target" : "earthquakesJSON", + "parameters" : [ + // bounding box coords: + { name: "north", type:"FLOAT" }, + { name: "south", type:"FLOAT" }, + { name: "east", type:"FLOAT" }, + { name: "west", type:"FLOAT" }, + { name: "date", type:"STRING",optional: true }, // yyyy-MM-dd + { name: "minMagniture", type:"INTERGER",optional: true }, + { name: "maxRows", type:"INTEGER", optional: true } // deault: 10 + ] + }, + + "getWeather":{ + // Weather data is provided in the METAR (METeorological Aerodrome Report) format. + // Result : returns a list of weather stations with the most recent weather observation + "target" : "weatherJSON", + "parameters" : [ + { name: "north", type:"FLOAT" }, + { name: "south", type:"FLOAT" }, + { name: "east", type:"FLOAT" }, + { name: "west", type:"FLOAT" }, + { name: "maxRows", type:"INTEGER",optional:true } // deault: 10 + ] + }, + + "getWeatherByICAO":{ + // Result : returns the weather station and the most recent weather observation for the ICAO code + "target": "weatherIcaoJSON", + "parameters": [ + { name:"ICAO", type:"STRING" } + ] + }, + + "getWeatherByCoords":{ + // Result : returns a weather station with the most recent weather observation + "target": "findNearByWeatherJSON", + "parameters": [ + { name:"lat", type:"FLOAT" }, + { name:"lng", type:"FLOAT" } + ] + }, + + "getChildren":{ + // Returns the children for a given geonameId. The children are the + // administrative divisions within an other administrative division. + // Like the counties (ADM2) in a state (ADM1) or also the countries + // in a continent. + "target": "childrenJSON", + "parameters": [ + { name:"geonameId", type:"INTEGER" } + ] + }, + + "getHierarchy":{ + // Result : returns a list of GeoName records, ordered by hierarchy + // level. The top hierarchy (continent) is the first element in the list + "target": "hierarchyJSON", + "parameters": [ + { name:"geonameId", type:"INTEGER" } + ] + }, + + "getNeighbours":{ + // The neighbourhood for US cities. Data provided by Zillow under cc-by-sa license. + "target":"neighboursJSON", + "parameters": [ + { name:"geonameId", type:"INTEGER" } + //{ name:"lat", type:"FLOAT" }, + //{ name:"long", type:"FLOAT" } + ] + }, + + "getNeighbourhood":{ + // returns the neighbourhood for the given latitude/longitude + // Example http://ws.geonames.org/neighbourhood?lat=40.78343&lng=-73.96625 + "target":"neighbourhoodJSON", + "parameters":[ + { name:"lat", type:"FLOAT" }, + { name:"lng", type:"FLOAT" }, + { name:"radius", type:"FLOAT" } + ] + }, + + "getSiblings":{ + // Result : returns a list of GeoNames records that have the same + // administrative level and the same father + "target":"siblingsJSON", + "parameters": [ + { name:"geonameId", type:"INTEGER" } + ] + }, + + "getCountryCode":{ + // returns the iso country code for the given latitude/longitude + // With the parameter type=xml this service returns an xml document + // with iso country code and country name. The optional parameter + // lang can be used to specify the language the country name should + // be in. JSON output is produced with type=JSON + "target":"countryCode", + "parameters": [ + { name:"lat", type:"FLOAT" }, + { name:"lng", type:"FLOAT" }, + { name:"lang", type:"STRING",optional:true }, + { name:"radius", type:"FLOAT" } + ] + }, + + "getCountrySubdivision":{ + // returns the country and the administrative subdivison (state, province,...) for the given latitude/longitude + // Example http://ws.geonames.org/countrySubdivisionJSON?lat=47.03&lng=10.2 + "target":"countrySubdivisionJSON", + "parameters":[ + { name:"lat", type:"FLOAT" }, + { name:"long", type:"FLOAT" }, + { name:"radius", type:"FLOAT" } + ] + }, + + "getWikipediaBoundingBox":{ + // returns the wikipedia entries within the bounding box as xml document + // Example http://ws.geonames.org/wikipediaBoundingBoxJSON?north=44.1&south=-9.9&east=-22.4&west=55.2 + "target":"wikipediaBoundingBoxJSON", + "parameters":[ + { name: "north", type:"FLOAT" }, + { name: "south", type:"FLOAT" }, + { name: "east", type:"FLOAT" }, + { name: "west", type:"FLOAT" }, + { name: "lang", type:"STRING",optional:true }, // default: "en" + { name: "maxRows", type:"INTEGER",optional:true } // default: 10 + ] + }, + + "searchWikipedia":{ + // returns the wikipedia entries found for the searchterm as xml document + // Example http://ws.geonames.org/wikipediaSearchJSON?q=london&maxRows=10 + "target":"wikipediaSearchJSON", + "parameters":[ + { name: "q", type:"STRING" }, // place name? + { name: "title", type:"STRING" }, // optional + { name: "lang", type:"FLOAT",optional:true }, // de or en + { name: "maxRows", type:"INTEGER",optional:true } // default: 10 + ] + }, + + "getTimezone":{ + // the timezone at the lat/lng with gmt offset (1. January) and dst offset (1. July) + "target":"timezoneJSON", + "parameters":[ + { name:"lat", type:"FLOAT" }, + { name:"lng", type:"FLOAT" } + ] + }, + + "search":{ + // returns the names found for the searchterm as xml or json document, + // the search is using an AND operator + "target":"searchJSON", + "parameters":[ + // exhaustive list. see: http://www.geonames.org/export/geonames-search.html + { name:"q", type:"STRING" }, + { name:"name", type:"STRING" }, + { name:"name_equals", type:"STRING" }, + // optional: + { name:"maxRows", type:"INTEGER",optional:true }, // def: 100, max:1000 + { name:"startRow", type:"INTEGER",optional:true }, // def: 0 + { name:"country", type:"STRING",optional:true }, // iso-3166, def: all + /* name:"adminCode..." */ + { name:"featureClass", type:"STRING",optional:true}, // ? multi, enum(a,h,l,p,r,s,t,u,v) + { name:"featureCode", type:"STRING",optional:true}, + { name:"lang", type:"STRING",optional:true}, // iso-636 + { name:"type", type:"STRING",optional:true}, // xml | json | rdf, def: xml + { name:"style", type:"STRING",optional:true }, // SHORT,MEDIUM,LONG,FULL def: MEDIUM + { name:"isNamedRequired", type:"BOOLEAN", optional:true } + ] + }, + + "postalCodeLookup":{ + // returns a list of places for the given postalcode in JSON format + // Example http://ws.geonames.org/postalCodeLookupJSON?postalcode=6600&country=AT + "target":"postalCodeLookupJSON", + "parameters":[ + { name:"postalcode", type:"STRING" }, + { name:"country", type:"STRING",optional:true}, + { name:"maxRows", type:"INTEGER", optional:true }, // def: 20 + { name:"charset", type:"STRING",optional:true } // def: utf-8 + ] + }, + + "postalCodeSearch":{ + "target":"postalCodeSearchJSON", + "parameters":[ + { name:"postalcode", type:"STRING",optional:true}, // one or the other + { name:"placename", type:"STRING",optional:true }, // one or the other + { name:"country", type:"STRING",optional:true}, + { name:"style", type:"STRING",optional:true}, // def: MEDIUM + { name:"maxRows", type:"INTEGER",optional:true}, // def: 20 + { name:"charset", type:"STRING",optional:true} // def: utf-8 + ] + } + + // TODO: implement: + // findNearby, + // findNearbyPlaceName + // findNearbyPostalCodes, + // findNearbyStreets, + // findNearByWeather + // findNearByWikipedia + // findNeareastAddress + // findNearestInterestion + // get + // gtop30 + // srtm3 + + } // end services +} diff --git a/js/dojo/dojox/rpc/SMDLibrary/google.smd b/js/dojo/dojox/rpc/SMDLibrary/google.smd new file mode 100644 index 0000000..82af928 --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/google.smd @@ -0,0 +1,129 @@ +{ + // Google Ajax API SMD as defined by: + // http://code.google.com/apis/ajaxsearch/documentation/reference.html + // PLEASE Read the Google Terms of Service: + // http://code.google.com/apis/ajaxsearch/terms.html + + "SMDVersion": "2.0", + "id": "http://ajax.googleapis.com/ajax/services/search/", + "description": "Google Ajax API", + + "transport": "JSONP", + "envelope": "URL", + "additionalParameters": true, + + "parameters": [ + // the most important param, the search query: + { "name": "q", optional: false, "default":"" }, + + // Google doesn't require, but appreciates, you using an API key + // see: http://code.google.com/apis/ajaxsearch/signup.html + // { "name": "key", optional: true, "default": "[enter your key]" }, + + // result size: large | small (8 or 4 per page) + { "name": "rsz", optional:true, "default": "small" }, + + // language selection: + { "name": "hl", optional:true, "default": "en" }, + + // starting offset + { "name": "start", optional:true, "default": 0 }, + + { "name": "alt", optional:false, "default": "json" }, + + // API version: only 1.0 supported + { "name": "v", optional:false, "default": "1.0" } + ], + + "services": { + + "webSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/web", + "parameters": [ + { "name": "cx", "type":"string", "optional":true }, // unique id + { "name": "cref", "type":"string", "optional":true }, // linked custom search engine + { "name": "safe", "type":"string", "optional":true }, // active | moderate | off + // lr: see: http://www.google.com/coop/docs/cse/resultsxml.html#languageCollections + { "name": "lr", "type":"string", "optional":true } // restrict to lang + ] + }, + + "localSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/local", + "parameters": [ + // comma separated lat,long value to use for center + { "name": "sll", "type": "string", "optional": true }, + { "name": "sspn", "type":"string", "optional": true } // bounding box? + ] + }, + + "videoSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/video", + "parameters": [ + // ordering, set "d" to order by date, "default": by relevance + { "name": "scoring", "type": "string", "optional": true } + ] + }, + + "blogSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/blogs", + "parameters": [ + // see videoSearch params + { "name": "scoring", "type": "string", "optional": true } + ] + }, + + "newsSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/news", + "parameters": [ + { "name": "scoring", "type": "string", "optional": true }, + // geo: scope search to location. supply city, state, country, or zipcode + // using geo makes "q" param optional + { "name": "geo", "type":"string", optional:true } + ] + }, + + "bookSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/books", + "parameters": [ + // set to "1" to only include full books + { "name":"as_brr", "optional": true, "type":"number" }, + // restrict search to user-defined library + { "name":"as_list", "optional": true, "type":"string" } + ] + }, + + "imageSearch": { + "target": "http://ajax.googleapis.com/ajax/services/search/images", + "parameters": [ + // safe: active | moderate | off + { "name": "safe", "type":"string", "optional":true }, + // limit image size: one of: icon, small|medium|large|xlarge, xxlarge, huge + { "name": "imgsz", "type":"string", "optional": true }, + // limit image color: one of: mono|gray|color + { "name": "imgc", "type":"string", "optional": true }, + // use "face" to limit to mugshots + { "name": "imgtype", "type":"string", "optional": true }, + // limit to file "type": jpg|png|gif|bmp + { "name": "as_filetype", "type": "string", "optional": true }, + // limit results to domain: + { "name": "as_sitesearch", "type": "string", "optional": true } + ] + }, + + "getFeed": { + "target": "http://ajax.googleapis.com/ajax/services/feed/load", + // pass a feed URL in the q parameter + "parameters": [] + }, + + "translate" : { + "target" : "http://ajax.googleapis.com/ajax/services/language/translate", + "parameters": [ + // A String like "en|es" (for english -> spanish) + { "name":"langpair", "type":"string", optional:false } + ] + } + + } +} diff --git a/js/dojo/dojox/rpc/SMDLibrary/twitter.smd b/js/dojo/dojox/rpc/SMDLibrary/twitter.smd new file mode 100644 index 0000000..6c19b3a --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/twitter.smd @@ -0,0 +1,35 @@ +{
+
+ "SMDVersion": "2.0",
+ "id": "http://apiwiki.twitter.com/w/page/22554756/Twitter-Search-API-Method:-search",
+ "description": "Twitter Search API",
+
+ "transport": "JSONP",
+ "envelope": "URL",
+ "additionalParameters": true,
+
+ "parameters": [
+ // the most important param, the search query:
+ { "name": "q", optional: false, "default":"" },
+
+
+ // result size: large | small (8 or 4 per page)
+ { "name": "rpp", optional:true, "default": 10 },
+
+ // language selection:
+ { "name": "lang", optional:true, "default": "en" },
+
+ // starting page
+ { "name": "page", optional:true, "default": 0 }
+
+ ],
+
+ "services": {
+
+ "search": {
+ "target": "http://search.twitter.com/search.json",
+ "parameters": [
+ ]
+ }
+ }
+}
diff --git a/js/dojo/dojox/rpc/SMDLibrary/wikipedia.smd b/js/dojo/dojox/rpc/SMDLibrary/wikipedia.smd new file mode 100644 index 0000000..d91b2c3 --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/wikipedia.smd @@ -0,0 +1,49 @@ +{ + "SMDVersion": "2.0", + "id": "http://en.wikipedia.org/w/api.php", + "description": "Wikipedia API", + + transport: "JSONP", + envelope: "URL", + additionalParameters: true, + target: "http://en.wikipedia.org/w/api.php", + parameters: [ + { name: "format", optional: false, "default": "json" } + ], + + services: { + + /* + * This is a very very basic spec that just gives us a free-form API + * endpoint to talk to. See http://en.wikipedia.org/w/api.php for + * details on what parameters to supply and what sort of data you + * should expect to see. Note that returned JSON is often somewhat + * ... interesting ...: + * + * { + * "parse": { + * "text": { + * "*": "<table ..." + * }, + * "langlinks": [ + * { + * "lang": "simple", + * "*": "" + * }, + * // etc. + * ] + * } + * } + * + * Not sure why we'd ever want an element to be named "*", but that's + * how it is. So take care. + * + */ + query: { + parameters: [ + { name: "action", type: "string", "default": "parse" } + ] + } + + } +} diff --git a/js/dojo/dojox/rpc/SMDLibrary/yahoo.smd b/js/dojo/dojox/rpc/SMDLibrary/yahoo.smd new file mode 100644 index 0000000..831e0b1 --- /dev/null +++ b/js/dojo/dojox/rpc/SMDLibrary/yahoo.smd @@ -0,0 +1,519 @@ +{ + "SMDVersion": "2.0", + // FIXME: is this the kind of value we're supposed to use here? + "id": "http://developer.yahoo.com/search/", + "description": "Yahoo's search API", + + transport: "JSONP", + envelope: "URL", + additionalParameters: true, + parameters: [ + { name: "appid", optional: false, "default": "dojotoolkit" }, + { name: "output", optional: false, "default": "json" } + ], + + // FIXME: Quite a few of these APIs can take multiple entries for the same parameter, to behave + // as multi-select options. How should we handle these? + + // ANSWER: JSON Schema defines this aspect of SMD, enumeration of possible values is handled + // with an "options" attribute. For example: + // { name: "search_in", type: "string", optional: true, "default": "all", options:["all", "question", "best_answer"]}, // can be "all", "question", "best_answer" + + services: { + // + // ANSWERS + // + // FIXME: Some of these API endpoints' names only make sense when you know they're in the + // Yahoo Answers part of the API; just reading a flat listing of methods in this SMD + // likely won't have enough information about what they do. Should we split this up? + + // http://developer.yahoo.com/answers/V1/questionSearch.html + questionSearch: { + target: "http://answers.yahooapis.com/AnswersService/V1/questionSearch", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "search_in", type: "string", optional: true, "default": "all" }, // can be "all", "question", "best_answer" + { name: "category_id", type: "integer", optional: true, "default": null }, // one of (category_id, category_name) is required + { name: "category_name", type: "string", optional: true, "default": null }, + { name: "region", type: "string", optional: true, "default": "us" }, // can be "us", "uk", "ca", "au", "in", "es", "br", "ar", "mx", "e1", "it", "de", "fr", "sg" + { name: "date_range", type: "string", optional: true, "default": "all" }, // can be "all", "7", "7-30", "30-60", "60-90", "more90" + { name: "sort", type: "string", optional: true, "default": "relevance" }, // can be "relevance", "date_desc", "date_asc" + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "resolved", "open", "undecided" + { name: "start", type: "integer", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 } // max 50 + ] + }, + + // http://developer.yahoo.com/answers/V1/getByCategory.html + getByCategory: { + target: "http://answers.yahooapis.com/AnswersService/V1/getByCategory", + parameters: [ + { name: "category_id", type: "integer", optional: true, "default": null }, // one of (category_id, category_name) is required + { name: "category_name", type: "string", optional: true, "default": null }, + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "resolved", "open", "undecided" + { name: "region", type: "string", optional: true, "default": "us" }, // can be "us", "uk", "ca", "au", "in", "es", "br", "ar", "mx", "e1", "it", "de", "fr", "sg" + { name: "sort", type: "string", optional: true, "default": "date_desc" }, // can be "date_desc", "date_asc", "ans_count_desc", "ans_count_asc" + { name: "start", type: "integer", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 } // max 50 + ] + }, + + // http://developer.yahoo.com/answers/V1/getQuestion.html + getQuestion: { + target: "http://answers.yahooapis.com/AnswersService/V1/getQuestion", + parameters: [ + { name: "question_id", type: "string", optional: true, "default": null } + ] + }, + + // http://developer.yahoo.com/answers/V1/getByUser.html + getByUser: { + target: "http://answers.yahooapis.com/AnswersService/V1/getByUser", + parameters: [ + { name: "user_id", type: "string", optional: false, "default": "" }, + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "resolved", "open", "undecided" + { name: "filter", type: "string", optional: true, "default": "question" }, // can be "question", "answer", "best_answer" + { name: "sort", type: "string", optional: true, "default": "date_desc" }, // can be "date_desc", "date_asc", "ans_count_desc", "ans_count_asc" + { name: "start", type: "integer", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 } // max 50 + ] + }, + + // + // AUDIO SEARCH + // + + // http://developer.yahoo.com/search/audio/V1/artistSearch.html + artistSearch: { + target: "http://search.yahooapis.com/AudioSearchService/V1/artistSearch", + parameters: [ + { name: "artist", type: "string", optional: true, "default": "" }, // one of (artist, artistid) is required + { name: "artistid", type: "string", optional: true, "default": "" }, + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/search/audio/V1/albumSearch.html + albumSearch: { + target: "http://search.yahooapis.com/AudioSearchService/V1/albumSearch", + parameters: [ + { name: "artist", type: "string", optional: true, "default": "" }, + { name: "artistid", type: "string", optional: true, "default": "" }, + { name: "album", type: "string", optional: true, "default": "" }, + { name: "albumid", type: "string", optional: true, "default": "" }, + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/search/audio/V1/songSearch.html + songSearch: { + // beware, this method has returned many a JSON string containing syntax error(s) + target: "http://search.yahooapis.com/AudioSearchService/V1/songSearch", + parameters: [ + { name: "artist", type: "string", optional: true, "default": "" }, + { name: "artistid", type: "string", optional: true, "default": "" }, + { name: "album", type: "string", optional: true, "default": "" }, + { name: "albumid", type: "string", optional: true, "default": "" }, + { name: "song", type: "string", optional: true, "default": "" }, + { name: "songid", type: "string", optional: true, "default": "" }, + { name: "type", type: "string", optional: true, "default": "all" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/search/audio/V1/songDownloadLocation.html + songDownloadLocation: { + target: "http://search.yahooapis.com/AudioSearchService/V1/songDownloadLocation", + parameters: [ + { name: "songid", type: "string", optional: false, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "source", type: "string", optional: true, "default": "" } // can be "audiolunchbox", "artistdirect", "buymusic", "dmusic", "emusic", "epitonic", "garageband", "itunes", "yahoo", "livedownloads", "mp34u", "msn", "musicmatch", "mapster", "passalong", "rhapsody", "soundclick", "theweb" + ] + }, + + // + // CONTENT ANALYSIS + // + + // http://developer.yahoo.com/search/content/V1/termExtraction.html + termExtraction: { + // FIXME: the API docs say to submit this as a POST, but we need JSONP for cross-domain, right? + // transport: "POST", + target: "http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction", + parameters: [ + { name: "context", type: "string", optional: false, "default": "" }, + { name: "query", type: "string", optional: true, "default": "" } + ] + }, + + // + // CONTEXT SEARCH + // + + // http://developer.yahoo.com/search/content/V1/contextSearch.html + contextSearch: { + target: "http://search.yahooapis.com/WebSearchService/V1/contextSearch", + parameters: [ + { name: "query", type: "string", optional: true, "default": "" }, + { name: "context", type: "string", optional: false, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 100 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "format", type: "string", optional: true, "default": "any" }, // can be "any", "html", "msword", "pdf", "ppt", "rss", "txt", "xls" + { name: "adult_ok", type: "boolean", optional: true, "default": null }, + { name: "similar_ok", type: "boolean", optional: true, "default": null }, + { name: "language", type: "string", optional: true, "default": null }, + { name: "country", type: "string", optional: true, "default": null }, + { name: "site", type: "string", optional: true, "default": null }, + { name: "license", type: "string", optional: true, "default": "any" } // can be "any", "cc_any", "cc_commercial", "cc_modifiable" + ] + }, + + // + // IMAGE SEARCH + // + + // http://developer.yahoo.com/search/image/V1/imageSearch.html + imageSearch: { + target: "http://search.yahooapis.com/ImageSearchService/V1/imageSearch", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "type", type: "string", optional: true, "default": "any" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "format", type: "string", optional: true, "default": "any" }, // can be "any", "bmp", "gif", "jpeg", "png" + { name: "adult_ok", type: "boolean", optional: true, "default": null }, + { name: "coloration", type: "string", optional: true, "default": "any" }, // can be "any", "color", "bw" + { name: "site", type: "string", optional: true, "default": null } + ] + }, + + // + // LOCAL SEARCH + // + + // http://developer.yahoo.com/search/local/V3/localSearch.html + localSearch: { + target: "http://local.yahooapis.com/LocalSearchService/V3/localSearch", + parameters: [ + { name: "query", type: "string", optional: true, "default": "" }, // optional, but one of (query, listing_id) is required + { name: "listing_id", type: "string", optional: true, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 20 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "sort", type: "string", optional: true, "default": "relevance" }, // can be "relevance", "title", "distance", "rating" + { name: "radius", type: "float", optional: true }, // the default varies according to location + { name: "street", type: "string", optional: true, "default": null }, + { name: "city", type: "string", optional: true, "default": null }, + { name: "state", type: "string", optional: true, "default": null }, // full name or two-letter abbreviation + { name: "zip", type: "any", optional: true, "default": null }, // ddddd or ddddd-dddd format + { name: "location", type: "string", optional: true, "default": null }, // free text, supersedes the street, city, state, zip fields + { name: "latitude", type: "float", optional: true }, // -90 to 90 + { name: "longitude", type: "float", optional: true }, // -180 to 180 + { name: "category", type: "integer", optional: true }, + { name: "omit_category", type: "integer", optional: true }, + { name: "minimum_rating", type: "integer", optional: true } + ] + }, + + // http://developer.yahoo.com/local/V1/collectionSearch.html + collectionSearch: { + target: "http://collections.local.yahooapis.com/LocalSearchService/V1/collectionSearch", + parameters: [ + { name: "query", type: "string", optional: true, "default": "" }, // optional, but at least one of (query, username) is required + { name: "username", type: "string", optional: true, "default": "" }, + { name: "city", type: "string", optional: true, "default": null }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/local/V1/getCollection.html + getCollection: { + target: "http://collections.local.yahooapis.com/LocalSearchService/V1/getCollection", + parameters: [ + { name: "collection_id", type: "integer", optional: false, "default": "" } + ] + }, + + // + // MY WEB 2.0 + // + + // http://developer.yahoo.com/search/myweb/V1/urlSearch.html + urlSearch: { + target: "http://search.yahooapis.com/MyWebService/V1/urlSearch", + parameters: [ + { name: "tag", type: "string", optional: true, "default": "" }, + { name: "yahooid", type: "string", optional: true, "default": "" }, + { name: "sort", type: "string", optional: true, "default": "date" }, // can be "date", "title", "url" + { name: "reverse_sort", type: "boolean", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/search/myweb/V1/tagSearch.html + tagSearch: { + target: "http://search.yahooapis.com/MyWebService/V1/tagSearch", + parameters: [ + { name: "url", type: "string", optional: true, "default": "" }, + { name: "yahooid", type: "string", optional: true, "default": "" }, + { name: "sort", type: "string", optional: true, "default": "popularity" }, // can be "popularity", "tag", "date" + { name: "reverse_sort", type: "boolean", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/search/myweb/V1/relatedTags.html + relatedTags: { + target: "http://search.yahooapis.com/MyWebService/V1/relatedTags", + parameters: [ + { name: "tag", type: "string", optional: false, "default": "" }, + { name: "yahooid", type: "string", optional: true, "default": "" }, + { name: "sort", type: "string", optional: true, "default": "popularity" }, // can be "popularity", "tag", "date" + { name: "reverse_sort", type: "boolean", optional: true, "default": 0 }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // + // NEWS SEARCH + // + + // http://developer.yahoo.com/search/news/V1/newsSearch.html + newsSearch: { + target: "http://search.yahooapis.com/NewsSearchService/V1/newsSearch", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "type", type: "string", optional: true, "default": "any" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "sort", type: "string", optional: true, "default": "rank" }, // can be "rank", "date" + { name: "language", type: "string", optional: true, "default": null }, + { name: "site", type: "string", optional: true, "default": null } + ] + }, + + // + // SHOPPING + // + + // http://developer.yahoo.com/shopping/V2/catalogListing.html + catalogListing: { + target: "http://shopping.yahooapis.com/ShoppingService/V2/catalogListing", + parameters: [ + { name: "catalogid", type: "integer", optional: true, "default": null }, // required if idtype,idvalue are not specified + { name: "getlisting", type: "boolean", optional: true, "default": 1 }, + { name: "getreview", type: "boolean", optional: true, "default": 0 }, + { name: "getspec", type: "boolean", optional: true, "default": 0 }, + { name: "idtype", type: "string", optional: true, "default": null }, // can be "upc", "brand,model", "brand,partnum"; required if catalogid is not specified + { name: "idvalue", type: "string", optional: true, "default": null }, // required if catalogid is not specified + { name: "onlynew", type: "boolean", optional: true, "default": 1 }, + { name: "reviewstart", type: "integer", optional: true, "default": 1 }, + { name: "reviewsort", type: "string", optional: true, "default": "mostRecommended_descending" }, // can be "mostRecommended_descending", "mostRecommended_ascending", "latest_descending", "latest_ascending", "highestRated_descending", "highestRated_ascending" + { name: "zip", type: "string", optional: true, "default": null } + ] + }, + + + // http://developer.yahoo.com/shopping/V1/merchantSearch.html + merchantSearch: { + target: "http://api.shopping.yahoo.com/ShoppingService/V1/merchantSearch", + parameters: [ + { name: "merchantid", type: "integer", optional: false, "default": null } + ] + }, + + + // http://developer.yahoo.com/shopping/V3/productSearch.html + productSearch: { + target: "http://shopping.yahooapis.com/ShoppingService/V3/productSearch", + parameters: [ + { name: "query", type: "string", optional: true, "default": "" }, // required if category is not specified + { name: "category", type: "any", optional: true, "default": "" }, // required if query is not specified + { name: "class", type: "string", optional: true, "default": null }, // can be "catalogs", "freeoffers", "paidoffers"; defaults to all three of these + { name: "department", type: "integer", optional: true, "default": null }, + { name: "highestprice", type: "float", optional: true, "default": null }, + { name: "lowestprice", type: "float", optional: true, "default": null }, + { name: "merchantId", type: "integer", optional: true, "default": null }, + { name: "refinement", type: "string", optional: true, "default": null }, // used only if category is specified + { name: "results", type: "integer", optional: true, "default": 10 }, // 1-50 + { name: "show_numratings", type: "boolean", optional: true, "default": 0 }, + { name: "show_narrowing", type: "boolean", optional: true, "default": 1 }, + { name: "sort", type: "string", optional: true }, // can be "price_ascending", "price_descending", "userrating_ascending", "userrating_descending"; omitted, the default is to sort by relevance + { name: "start", type: "integer", optional: true, "default": 1 } // 1-300 + ] + }, + + // + // SITE EXPLORER + // + + // http://developer.yahoo.com/search/siteexplorer/V1/inlinkData.html + inlinkData: { + target: "http://search.yahooapis.com/SiteExplorerService/V1/inlinkData", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 50 }, // max 100 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "entire_site", type: "boolean", optional: true, "default": null }, + { name: "omit_inlinks", type: "string", optional: true, "default": "none" } // can be "none", "domain", "subdomain" + ] + }, + + // http://developer.yahoo.com/search/siteexplorer/V1/pageData.html + pageData: { + target: "http://search.yahooapis.com/SiteExplorerService/V1/pageData", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 50 }, // max 100 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "domain_only", type: "boolean", optional: true, "default": null } + ] + }, + + // http://developer.yahoo.com/search/siteexplorer/V1/ping.html + ping: { + target: "http://search.yahooapis.com/SiteExplorerService/V1/ping", + parameters: [ + { name: "sitemap", type: "string", optional: false, "default": "" } + ] + }, + + // http://developer.yahoo.com/search/siteexplorer/V1/updateNotification.html + updateNotification: { + target: "http://search.yahooapis.com/SiteExplorerService/V1/updateNotification", + parameters: [ + { name: "url", type: "string", optional: false, "default": "" } + ] + }, + + // + // TRAFFIC + // + + // http://developer.yahoo.com/traffic/rest/V1/index.html + trafficData: { + target: "http://local.yahooapis.com/MapsService/V1/trafficData", + parameters: [ + { name: "street", type: "string", optional: true, "default": "" }, + { name: "city", type: "string", optional: true, "default": "" }, + { name: "state", type: "string", optional: true, "default": null }, // full name or two-letter abbreviation + { name: "zip", type: "any", optional: true, "default": null }, // ddddd or ddddd-dddd format + { name: "location", type: "string", optional: true, "default": null }, // free text, supersedes the street, city, state, zip fields + { name: "latitude", type: "float", optional: true }, // -90 to 90 + { name: "longitude", type: "float", optional: true }, // -180 to 180 + { name: "severity", type: "integer", optional: true, "default": 1 }, // can be 1-5 + { name: "zoom", type: "integer", optional: true, "default": 6 }, // can be 1-12 + { name: "radius", type: "float", optional: true }, // in miles, default varies with location; ignored if zoom is specified + { name: "include_map", type: "boolean", optional: true, "default": 0 }, + { name: "image_type", type: "string", optional: true, "default": "png" }, // can be "png" or "gif" + { name: "image_height", type: "integer", optional: true, "default": 500 }, // in pixels, can be 10-2000 + { name: "image_width", type: "integer", optional: true, "default": 620 } // in pixels, can be 10-2000 + ] + }, + + // + // TRAVEL + // + + // http://developer.yahoo.com/travel/tripservice/V1.1/tripSearch.html + tripSearch: { + target: "http://travel.yahooapis.com/TripService/V1.1/tripSearch", + parameters: [ + { name: "query", type: "string", optional: true, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 } + ] + }, + + // http://developer.yahoo.com/travel/tripservice/V1.1/getTrip.html + getTrip: { + target: "http://travel.yahooapis.com/TripService/V1.1/getTrip", + parameters: [ + { name: "id", type: "integer", optional: false, "default": null } + ] + }, + + // + // UTILITY SERVICES + // + + // http://developer.yahoo.com/util/timeservice/V1/getTime.html + /* RGG: commented out because it refuses to return JSON format even when you tell it + to do so (it returns a <script> tag) + getTime: { + target: "http://developer.yahooapis.com/TimeService/V1/getTime", + parameters: [ + { name: "format", type: "string", optional: true, "default": "unix" } // can be "unix" for unix timestamp, "ms" for milliseconds + ] + }, + */ + + // + // VIDEO SEARCH + // + + // http://developer.yahoo.com/search/video/V1/videoSearch.html + videoSearch: { + target: "http://search.yahooapis.com/VideoSearchService/V1/videoSearch", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "type", type: "string", optional: true, "default": "any" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 50 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "format", type: "string", optional: true, "default": "any" }, // can be "any", "avi", "flash", "mpeg", "msmedia", "quicktime", "realmedia" + { name: "adult_ok", type: "boolean", optional: true, "default": null }, + { name: "site", type: "string", optional: true, "default": null } + ] + }, + + // + // WEB SEARCH + // + + // http://developer.yahoo.com/search/web/V1/webSearch.html + webSearch: { + target: "http://search.yahooapis.com/WebSearchService/V1/webSearch", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, // must be less than 1kb + { name: "region", type: "string", optional: true, "default": "us" }, + { name: "type", type: "string", optional: true, "default": "any" }, // can be "all", "any", "phrase" + { name: "results", type: "integer", optional: true, "default": 10 }, // max 100 + { name: "start", type: "integer", optional: true, "default": 1 }, + { name: "format", type: "string", optional: true, "default": "any" }, // can be "any", "html", "msword", "pdf", "ppt", "rss", "txt", "xls" + { name: "adult_ok", type: "boolean", optional: true, "default": null }, + { name: "similar_ok", type: "boolean", optional: true, "default": null }, + { name: "language", type: "string", optional: true, "default": null }, + { name: "country", type: "string", optional: true, "default": null }, + { name: "site", type: "string", optional: true, "default": null }, + { name: "subscription", type: "string", optional: true, "default": null }, + { name: "license", type: "string", optional: true, "default": "any" } // can be "any", "cc_any", "cc_commercial", "cc_modifiable" + ] + }, + + // http://developer.yahoo.com/search/web/V1/spellingSuggestion.html + spellingSuggestion: { + target: "http://search.yahooapis.com/WebSearchService/V1/spellingSuggestion", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" } + ] + }, + + // http://developer.yahoo.com/search/web/V1/relatedSuggestion.html + relatedSuggestion: { + target: "http://search.yahooapis.com/WebSearchService/V1/relatedSuggestion", + parameters: [ + { name: "query", type: "string", optional: false, "default": "" }, + { name: "results", type: "integer", optional: true, "default": 10 } // max 50 + ] + } + } +} diff --git a/js/dojo/dojox/rpc/Service.js b/js/dojo/dojox/rpc/Service.js new file mode 100644 index 0000000..15b7d60 --- /dev/null +++ b/js/dojo/dojox/rpc/Service.js @@ -0,0 +1,323 @@ +//>>built +define("dojox/rpc/Service", ["dojo", "dojox", "dojo/AdapterRegistry", "dojo/_base/url"], function(dojo, dojox) { + +dojo.declare("dojox.rpc.Service", null, { + constructor: function(smd, options){ + // summary: + // Take a string as a url to retrieve an smd or an object that is an smd or partial smd to use + // as a definition for the service + // + // smd: object + // Takes a number of properties as kwArgs for defining the service. It also + // accepts a string. When passed a string, it is treated as a url from + // which it should synchronously retrieve an smd file. Otherwise it is a kwArgs + // object. It accepts serviceUrl, to manually define a url for the rpc service + // allowing the rpc system to be used without an smd definition. strictArgChecks + // forces the system to verify that the # of arguments provided in a call + // matches those defined in the smd. smdString allows a developer to pass + // a jsonString directly, which will be converted into an object or alternatively + // smdObject is accepts an smdObject directly. + // + // description: + // dojox.rpc.Service must be loaded prior to any plugin services like dojox.rpc.Rest + // dojox.rpc.JsonRpc in order for them to register themselves, otherwise you get + // a "No match found" error. + var url; + var self = this; + function processSmd(smd){ + smd._baseUrl = new dojo._Url((dojo.isBrowser ? location.href : dojo.config.baseUrl) ,url || '.') + ''; + self._smd = smd; + + //generate the methods + for(var serviceName in self._smd.services){ + var pieces = serviceName.split("."); // handle "namespaced" services by breaking apart by . + var current = self; + for(var i=0; i< pieces.length-1; i++){ + // create or reuse each object as we go down the chain + current = current[pieces[i]] || (current[pieces[i]] = {}); + } + current[pieces[pieces.length-1]]= self._generateService(serviceName, self._smd.services[serviceName]); + } + } + if(smd){ + //ifthe arg is a string, we assume it is a url to retrieve an smd definition from + if( (dojo.isString(smd)) || (smd instanceof dojo._Url)){ + if(smd instanceof dojo._Url){ + url = smd + ""; + }else{ + url = smd; + } + + var text = dojo._getText(url); + if(!text){ + throw new Error("Unable to load SMD from " + smd); + }else{ + processSmd(dojo.fromJson(text)); + } + }else{ + processSmd(smd); + } + } + + this._options = (options ? options : {}); + this._requestId = 0; + }, + + _generateService: function(serviceName, method){ + if(this[method]){ + throw new Error("WARNING: "+ serviceName+ " already exists for service. Unable to generate function"); + } + method.name = serviceName; + var func = dojo.hitch(this, "_executeMethod",method); + var transport = dojox.rpc.transportRegistry.match(method.transport || this._smd.transport); + if(transport.getExecutor){ + func = transport.getExecutor(func,method,this); + } + var schema = method.returns || (method._schema = {}); // define the schema + var servicePath = '/' + serviceName +'/'; + // schemas are minimally used to track the id prefixes for the different services + schema._service = func; + func.servicePath = servicePath; + func._schema = schema; + func.id = dojox.rpc.Service._nextId++; + return func; + }, + _getRequest: function(method,args){ + var smd = this._smd; + var envDef = dojox.rpc.envelopeRegistry.match(method.envelope || smd.envelope || "NONE"); + var parameters = (method.parameters || []).concat(smd.parameters || []); + if(envDef.namedParams){ + // the serializer is expecting named params + if((args.length==1) && dojo.isObject(args[0])){ + // looks like we have what we want + args = args[0]; + }else{ + // they provided ordered, must convert + var data={}; + for(var i=0;i<method.parameters.length;i++){ + if(typeof args[i] != "undefined" || !method.parameters[i].optional){ + data[method.parameters[i].name]=args[i]; + } + } + args = data; + } + if(method.strictParameters||smd.strictParameters){ + //remove any properties that were not defined + for(i in args){ + var found=false; + for(var j=0; j<parameters.length;j++){ + if(parameters[i].name==i){ found=true; } + } + if(!found){ + delete args[i]; + } + } + + } + // setting default values + for(i=0; i< parameters.length; i++){ + var param = parameters[i]; + if(!param.optional && param.name && !args[param.name]){ + if(param["default"]){ + args[param.name] = param["default"]; + }else if(!(param.name in args)){ + throw new Error("Required parameter " + param.name + " was omitted"); + } + } + } + }else if(parameters && parameters[0] && parameters[0].name && (args.length==1) && dojo.isObject(args[0])){ + // looks like named params, we will convert + if(envDef.namedParams === false){ + // the serializer is expecting ordered params, must be ordered + args = dojox.rpc.toOrdered(parameters, args); + }else{ + // named is ok + args = args[0]; + } + } + + if(dojo.isObject(this._options)){ + args = dojo.mixin(args, this._options); + } + + var schema = method._schema || method.returns; // serialize with the right schema for the context; + var request = envDef.serialize.apply(this, [smd, method, args]); + request._envDef = envDef;// save this for executeMethod + var contentType = (method.contentType || smd.contentType || request.contentType); + + // this allows to mandate synchronous behavior from elsewhere when necessary, this may need to be changed to be one-shot in FF3 new sync handling model + return dojo.mixin(request, { + sync: dojox.rpc._sync, + contentType: contentType, + headers: method.headers || smd.headers || request.headers || {}, + target: request.target || dojox.rpc.getTarget(smd, method), + transport: method.transport || smd.transport || request.transport, + envelope: method.envelope || smd.envelope || request.envelope, + timeout: method.timeout || smd.timeout, + callbackParamName: method.callbackParamName || smd.callbackParamName, + rpcObjectParamName: method.rpcObjectParamName || smd.rpcObjectParamName, + schema: schema, + handleAs: request.handleAs || "auto", + preventCache: method.preventCache || smd.preventCache, + frameDoc: this._options.frameDoc || undefined + }); + }, + _executeMethod: function(method){ + var args = []; + var i; + for(i=1; i< arguments.length; i++){ + args.push(arguments[i]); + } + var request = this._getRequest(method,args); + var deferred = dojox.rpc.transportRegistry.match(request.transport).fire(request); + + deferred.addBoth(function(results){ + return request._envDef.deserialize.call(this,results); + }); + return deferred; + } +}); + +dojox.rpc.getTarget = function(smd, method){ + var dest=smd._baseUrl; + if(smd.target){ + dest = new dojo._Url(dest,smd.target) + ''; + } + if(method.target){ + dest = new dojo._Url(dest,method.target) + ''; + } + return dest; +}; + +dojox.rpc.toOrdered=function(parameters, args){ + if(dojo.isArray(args)){ return args; } + var data=[]; + for(var i=0;i<parameters.length;i++){ + data.push(args[parameters[i].name]); + } + return data; +}; + +dojox.rpc.transportRegistry = new dojo.AdapterRegistry(true); +dojox.rpc.envelopeRegistry = new dojo.AdapterRegistry(true); +//Built In Envelopes + +dojox.rpc.envelopeRegistry.register( + "URL", + function(str){ return str == "URL"; }, + { + serialize:function(smd, method, data ){ + var d = dojo.objectToQuery(data); + return { + data: d, + transport:"POST" + }; + }, + deserialize:function(results){ + return results; + }, + namedParams: true + } +); + +dojox.rpc.envelopeRegistry.register( + "JSON", + function(str){ return str == "JSON"; }, + { + serialize: function(smd, method, data){ + var d = dojo.toJson(data); + + return { + data: d, + handleAs: 'json', + contentType : 'application/json' + }; + }, + deserialize: function(results){ + return results; + } + } +); +dojox.rpc.envelopeRegistry.register( + "PATH", + function(str){ return str == "PATH"; }, + { + serialize:function(smd, method, data){ + var i; + var target = dojox.rpc.getTarget(smd, method); + if(dojo.isArray(data)){ + for(i = 0; i < data.length;i++){ + target += '/' + data[i]; + } + }else{ + for(i in data){ + target += '/' + i + '/' + data[i]; + } + } + + return { + data:'', + target: target + }; + }, + deserialize:function(results){ + return results; + } + } +); + + + +//post is registered first because it is the default; +dojox.rpc.transportRegistry.register( + "POST", + function(str){ return str == "POST"; }, + { + fire:function(r){ + r.url = r.target; + r.postData = r.data; + return dojo.rawXhrPost(r); + } + } +); + +dojox.rpc.transportRegistry.register( + "GET", + function(str){ return str == "GET"; }, + { + fire: function(r){ + r.url= r.target + (r.data ? '?' + ((r.rpcObjectParamName) ? r.rpcObjectParamName + '=' : '') + r.data : ''); + return dojo.xhrGet(r); + } + } +); + + +//only works ifyou include dojo.io.script +dojox.rpc.transportRegistry.register( + "JSONP", + function(str){ return str == "JSONP"; }, + { + fire: function(r){ + r.url = r.target + ((r.target.indexOf("?") == -1) ? '?' : '&') + ((r.rpcObjectParamName) ? r.rpcObjectParamName + '=' : '') + r.data; + r.callbackParamName = r.callbackParamName || "callback"; + return dojo.io.script.get(r); + } + } +); +dojox.rpc.Service._nextId = 1; + +dojo._contentHandlers.auto = function(xhr){ + // automatically choose the right handler based on the returned content type + var handlers = dojo._contentHandlers; + var retContentType = xhr.getResponseHeader("Content-Type"); + var results = !retContentType ? handlers.text(xhr) : + retContentType.match(/\/.*json/) ? handlers.json(xhr) : + retContentType.match(/\/javascript/) ? handlers.javascript(xhr) : + retContentType.match(/\/xml/) ? handlers.xml(xhr) : handlers.text(xhr); + return results; +}; + +return dojox.rpc.Service; + +}); diff --git a/js/dojo/dojox/rpc/demos/demo_GoogleAjax.html b/js/dojo/dojox/rpc/demos/demo_GoogleAjax.html new file mode 100644 index 0000000..fdd224f --- /dev/null +++ b/js/dojo/dojox/rpc/demos/demo_GoogleAjax.html @@ -0,0 +1,122 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Demo of Google SMD</title> + <style type="text/css"> + @import "../../image/resources/image.css"; + body, html { + width:100%; + margin:0; padding:0; + } + .logo { + border:0; vertical-align:middle; + } + .summary { + color:#666; + font:8pt Arial,sans-serif; + } + #container { + width:720px; + margin:0 auto; + } + #loader { + position:absolute; + top:3px; + left:3px; + visibility:hidden; + height:50px; + width:50px; + background:url("../../../dojox/image/resources/images/loading.gif") no-repeat center center; + } + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojox.rpc.Service"); + dojo.require("dojo.io.script"); + + var google, tehloader = null; + function init(){ + + google = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc","SMDLibrary/google.smd")); + + tehloader = dojo.partial(dojo.style,"loader","visibility"); + var getResults = function(e){ + + tehloader("visible"); + + dojo.query("li","top").forEach(function(item){ + dojo.byId("archive").appendChild(item); + }); + + var searchType = dojo.byId("searchType").value || "webSearch"; + google[searchType]({ q: dojo.byId("test").value }) + // generic succes handler: + .addCallback(function(returned){ + var ret = returned.responseData; + var info = ret.cursor; + var data = ret.results || []; + dojo.forEach(data,function(item){ + var li = dojo.doc.createElement('li'); + li.innerHTML = "<a target='_new' hr"+"ef='"+ (item.unescapedUrl || item.url) +"'>" + item.title + "</a><br />" + + "<span class='summary'>" + (item.content || item.streetAddress || "unknown") + "</span>"; + + console.log(item); + dojo.byId("top").appendChild(li); + }); + tehloader("hidden"); + }) + // something bad happened: + .addErrback(function(err){ + console.warn('ooops', err); + tehloader("hidden"); + }); + }; + + dojo.connect(dojo.byId("doit"), "onclick", getResults); + + } + dojo.addOnLoad(init); + + </script> +</head> + +<body> + <div id="loader"></div> + <div id="container"> + <h2><a href="http://google.com"><img class="logo" src="http://www.google.com/logos/Logo_40wht.gif" alt="Google" /></a> Ajax API SMD</h2> + + <input id="test" name="test" type="text" size="30" value="The Dojo Toolkit"/> + <select id="searchType"> + <option value="webSearch">web</option> + <option value="localSearch">local</option> + <option value="videoSearch">videos</option> + <option value="blogSearch">blog</option> + <option value="newsSearch">news</option> + <option value="bookSearch">books</option> + <option value="imageSearch">images</option> + </select> + <button id="doit" type="submit" value="search">Search</button> + + <div id="current"> + <h3>Results:</h3> + <ul id="top"></ul> + </div> + + <div id="past"> + <h3>Old Searches:</h3> + <ul id="archive"></ul> + </div> + + <hr> + + <p>There are many per-serivce options available. This demo just pulls data and tries ...</p> + <p>That's all folks. Unit tests to come when we address the io.script issue.</p> + + + </div> + + +</body> +</html> diff --git a/js/dojo/dojox/rpc/demos/demo_GoogleAjaxTranslate.html b/js/dojo/dojox/rpc/demos/demo_GoogleAjaxTranslate.html new file mode 100644 index 0000000..d6d0d5f --- /dev/null +++ b/js/dojo/dojox/rpc/demos/demo_GoogleAjaxTranslate.html @@ -0,0 +1,155 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> + <title>Demo of Google SMD / Translate test</title> + <style type="text/css"> + body, html { + width:100%; + margin:0; padding:0; + } + .logo { + border:0; vertical-align:middle; + } + .summary { + color:#666; + font:8pt Arial,sans-serif; + } + #container { + width:760px; + margin:0 auto; + } + #loader { + position:absolute; + top:3px; + left:3px; + visibility:hidden; + height:50px; + width:50px; + background:url("../../../dojox/image/resources/images/loading.gif") no-repeat center center; + } + .inputBox { + width:300px; + } + .warn { color:magenta; } + .over { color:red !important; } + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojox.rpc.Service"); + dojo.require("dojo.io.script"); + + var google, showLoader, hideLoader = null; + function init(){ + + var s = dojo.query("select.clone")[0]; + var n = dojo.clone(s); + dojo.attr(s,"id","fromLang"); + dojo.attr(n,"id","toLang"); + dojo.place(n, "target", "after"); + + google = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc","SMDLibrary/google.smd")); + + showLoader = dojo.hitch(dojo,"style","loader","visibility","visible"); + hideLoader = dojo.hitch(dojo,"style","loader","visibility","hidden"); + + dojo.connect(dojo.byId("test"),"onkeypress",function(e){ + var n = dojo.byId('test'); + var len = n.value.length + 1; + if(len >= 400 && len < 500){ + if(!dojo.hasClass("progress","warn")){ dojo.addClass("progress","warn"); } + }else if(len >= 501){ + n.value = n.value.substr(0, 500) + if(!dojo.hasClass("progress","over")){ dojo.addClass("progress","over"); } + return false; + }else{ + dojo.removeClass("progress","over"); + dojo.removeClass("progress","warn"); + } + dojo.byId("used").innerHTML = len + ""; + + }); + + var runner = function(e){ + var pair = dojo.byId("fromLang").value + "|" + dojo.byId("toLang").value; + google.translate({ q: dojo.byId("test").value, langpair:pair }) + .addBoth(function(response){ + var r = response.responseData; + if(r && r.translatedText){ + dojo.byId("response").value = r.translatedText; + } + dojo.byId("flop").disabled = false; + }); + }; + + dojo.connect(dojo.byId("doit"),"onclick", runner); + + dojo.connect(dojo.byId("flop"),"onclick", function(e){ + // take the value from the translated text, dump it in the incoming + // and set the values opposite + var input = dojo.byId("test"); + var resp = dojo.byId("response"); + var trans = resp.value; + + resp.value = ""; + input.value = trans; + + var cur = dojo.byId("fromLang").selectedIndex; + dojo.query("#toLang option").map(function(n){ return (n.selected) ? n : false; }) + .forEach(function(n,i){ + if(n){ + dojo.byId("fromLang").selectedIndex = i; + dojo.byId("toLang").selectedIndex = cur; + } + }); + dojo.byId("flop").disabled = true; + runner(); + + }); + + } + dojo.addOnLoad(init); + + </script> +</head> + +<body> + <div id="loader"></div> + <div id="container"> + <h2><a href="http://google.com"><img class="logo" src="http://www.google.com/logos/Logo_40wht.gif" alt="Google" /></a> Ajax API SMD / Translation Test</h2> + + <div id="progress">Used: <span id="used">?</span> of 500 max.</div> + <textarea id="test" style="float:left; width:300px; height:200px; border:1px solid #666; background:#ededed; color:#666"></textarea> + <textarea id="response" style="float:right; width:300px; height:200px; border:1px solid #666; background:#ededed; color:#666"></textarea> + + <br style="clear:both"> + + <div style="padding-top:15px;"> + From: + <select class="clone"> + <option value="ar">Arabic</option><option value="bg">Bulgarian</option> + <option value="zh-CN">Chinese (Simplified)</option><option value="zh-TW">Chinese (Traditional)</option> + <option value="hr">Croatian</option><option value="cs">Czech</option><option value="da">Danish</option> + <option value="nl">Dutch</option><option SELECTED value="en">English</option><option value="fi">Finnish</option> + <option value="fr">French</option><option value="de">German</option><option value="el">Greek</option> + <option value="hi">Hindi</option><option value="it">Italian</option><option value="ja">Japanese</option> + <option value="ko">Korean</option><option value="no">Norwegian</option><option value="pl">Polish</option> + <option value="pt">Portuguese</option><option value="ro">Romanian</option><option value="ru">Russian</option> + <option value="es">Spanish</option><option value="sv">Swedish</option> + </select> + + <span id="target">To: </span> + + <button id="doit">Translate</button> + <button id="flop" disabled="disabled">Flip Back</button> + + </div> + + + + </div> + + +</body> +</html> diff --git a/js/dojo/dojox/rpc/demos/demo_GoogleFeed.html b/js/dojo/dojox/rpc/demos/demo_GoogleFeed.html new file mode 100644 index 0000000..5ec3d0a --- /dev/null +++ b/js/dojo/dojox/rpc/demos/demo_GoogleFeed.html @@ -0,0 +1,97 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<html> +<head> +<title>Demo of Google SMD Feed Service</title> +<style type="text/css"> +th { + text-align:left; +} +table { + border-collapse:collapse; +} +th, td { + border:1px solid #ddd; +} +</style> +<script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> +<script type="text/javascript"> + dojo.require("dojox.rpc.Service"); + dojo.require("dojo.io.script"); + + function init(){ + var google = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc","SMDLibrary/google.smd")); + dojo.connect(dojo.byId("go"),"onclick", function(e){ + + var resultsNode = dojo.byId("feedData"); + resultsNode.innnerHTML = "Loading..."; + + var url = dojo.byId("feedUrl").value; + google.getFeed({ + q: url + }).addCallback(function(response){ + console.log(response); + var feed = response.responseData.feed; + + dojo.byId("feedTitleNode").innerHTML = feed.title; + dojo.byId("feedAuthorNode").innerHTML = feed.author; + dojo.byId("feedLinkNode").innerHTML = feed.link; + dojo.byId("feedTypeNode").innerHTML = feed.type; + dojo.byId("feedDescriptionNode").innerHTML = feed.description; + + var feedEntriesNode = dojo.byId("feedEntriesNode"); + dojo.forEach(feed.entries, function(entry){ + var entryNode = document.createElement("li"); + entryNode.innerHTML = entry.title + ", <i>" + entry.link + "</i>"; + feedEntriesNode.appendChild(entryNode); + }); + + }).addErrback(function(err){ + console.error("Error getting feed from " + url, err); + }); + }); + + } + dojo.addOnLoad(init); + +</script> +</head> + +<body> + <h2>Google API SMD - Feed Reader</h2> + + <input id="feedUrl" name="feedUrl" type="text" size="60" value="http://dojotoolkit.org/aggregator/rss"> + <button id="go" type="submit" value="Read Feed">Read Feed</button> + + <p>Click the Read Feed button to load the feed data.</p> + + <div id="feedData"> + <table> + <tr> + <th>Title</th> + <td id="feedTitleNode"><i>none yet</i></td> + </tr> + <tr> + <th>Author</th> + <td id="feedAuthorNode"><i>none yet</i></td> + </tr> + <tr> + <th>Link</th> + <td id="feedLinkNode"><i>none yet</i></td> + </tr> + <tr> + <th>Type</th> + <td id="feedTypeNode"><i>none yet</i></td> + </tr> + <tr> + <th>Description</th> + <td id="feedDescriptionNode"><i>none yet</i></td> + </tr> + </table> + + <p><b>Entries:</b></p> + + <ul id="feedEntriesNode"> + </ul> + </div> +</body> +</html> diff --git a/js/dojo/dojox/rpc/demos/demo_jabsorb.html b/js/dojo/dojox/rpc/demos/demo_jabsorb.html new file mode 100644 index 0000000..8452341 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/demo_jabsorb.html @@ -0,0 +1,70 @@ +<!-- +This file is a demo of the Dojo + jabsorb + +You must have jabsorb installed in order for this to run. +This assumes that jabsorb is at /jabsorb-1.3/, but you +can change the target if it as a different location. + +This uses the Hello example from the jabsorb framework (Hello.java and hello.jsp) +--> +<html> +<head> + <title>Demo of Dojo + jabsorb</title> + <style type="text/css"> + + @import "../../../dijit/themes/tundra/tundra.css"; + @import "../../../dojo/resources/dojo.css"; + </style> + + <script type="text/javascript" src="../../../dojo/dojo.js" djConfig="isDebug: true, parseOnLoad: true"></script> + <script type="text/javascript"> + dojo.require("dojox.rpc.Service"); + dojo.require("dojox.rpc.JsonRPC"); + var services = new dojox.rpc.Service({ + target:"/jabsorb-1.3/JSON-RPC", + transport:"POST", + envelope:"JSON-RPC-1.0", + contentType:"application/json", + services:{ + "hello.sayHello":{ + returns:{"type":"string"}, + parameters:[{"type":"string"}] + } + } + }); + function clickHello() { + var whoInput = document.getElementById("who"); + var deferred = services["hello.sayHello"](whoInput.value); + deferred.addCallback(function(result) { + alert(result); + return result; + }) + } + + </script> +</head> + +<body class="tundra"> + <h1> + DEMO: jabsorb + </h1> + <hr> + <h3> + Description: + </h3> + <p> + This simple demo shows how to connect to a <a href="http://jabsorb.org/">jabsorb server</a> + </p> +<p>The jabsorb <em>Hello World</em> application.</p> + +<p> + <strong>Who:</strong> + <input type="text" id="who" size="30" value="Michael"/> + + + <input type="button" value="Say Hello" onclick="clickHello()"/> +</p> +<body> + <hr/> +</body> +</html> diff --git a/js/dojo/dojox/rpc/demos/documentation.html b/js/dojo/dojox/rpc/demos/documentation.html new file mode 100644 index 0000000..b5a2a44 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/documentation.html @@ -0,0 +1,33 @@ +<html> + <head> + <script src="../../../dojo/dojo.js" djConfig="parseOnLoad: true"></script> + <script> + dojo.require("dojo.io.script") + dojo.require("dojox.rpc.Service"); + dojo.require("dijit._Widget"); + dojo.require("dojox.dtl._Templated"); + dojo.require("dojo.parser"); + + dojo.declare("API", [dijit._Widget, dojox.dtl._Templated], { + constructor: function(params, node){ + this.jsonp = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc", "SMDLibrary/dojo-api.smd")); + }, + onSearch: function(e){ + if(e.keyCode == dojo.keys.ENTER){ + this.jsonp.get({ + name: e.target.value + }).addCallback(this, function(results){ + console.debug(results); + this.results = results; + this.render(); + }); + } + }, + templateString: dojo.cache("dojox.rpc.demos.templates", "documentation.html") + }); + </script> + </head> + <body> + <div dojoType="API"></div> + </body> +</html>
\ No newline at end of file diff --git a/js/dojo/dojox/rpc/demos/templates/documentation.html b/js/dojo/dojox/rpc/demos/templates/documentation.html new file mode 100644 index 0000000..d14cf16 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/templates/documentation.html @@ -0,0 +1,8 @@ +<div> + <input dojotAttachPoint="search" dojoAttachEvent="onkeyup: onSearch"> + <ul> + {% for result in results %} + <li>{% if result.type %}<i>{{ result.type }}</i> {% endif %}{% ifequal result.type "Function" %}{% if not result.resources %}function {% endif %}{% endifequal %}{{ result.name }}{% ifequal result.type "Function" %}{% if not result.resources %}({% for parameter in result.parameters %}{% if not forloop.first %}, {% endif %}{% if parameter.types %}<i>{% for type in parameter.types %}{% if not forloop.first %}|{% endif %}{{ type.title }}{% endfor %}{% if parameter.optional %}?{% endif %}{% if parameter.repeating %}...{% endif %}</i> {% endif %}{{ parameter.name }}{% endfor %}{% endif %}{% endifequal %}{% ifequal result.type "Function" %}{% if not result.resources %}){% endif %}{% endifequal %}</li> + {% endfor %} + </ul> +</div>
\ No newline at end of file diff --git a/js/dojo/dojox/rpc/demos/templates/wikipedia.html b/js/dojo/dojox/rpc/demos/templates/wikipedia.html new file mode 100644 index 0000000..e923a0b --- /dev/null +++ b/js/dojo/dojox/rpc/demos/templates/wikipedia.html @@ -0,0 +1,10 @@ +<div> + <input dojotAttachPoint="search" dojoAttachEvent="onkeyup: onSearch"> + {% if parsedText %} + <div style="border:2px solid #ddd; padding:20px;"> + {{ parsedText }} + </div> + {% endif %} + + {% if article.revid %}<p>Revision ID: {{ article.revid }}</p>{% endif %} +</div> diff --git a/js/dojo/dojox/rpc/demos/templates/yahoo.html b/js/dojo/dojox/rpc/demos/templates/yahoo.html new file mode 100644 index 0000000..04339d2 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/templates/yahoo.html @@ -0,0 +1,31 @@ +<div> + <input dojotAttachPoint="search" dojoAttachEvent="onkeyup: onSearch"> + <ul> + {% for result in results.Result %} + <li><a href="{{ result.Url }}">{{ result.Title }}</a><br/>{{ result.Summary }}</li> + {% endfor %} + </ul> + <table> + {% if results.firstResultPosition %} + <tr> + <th align="left">First Result</th> + <td>{{ results.firstResultPosition }}</td> + </tr> + {% endif %}{% if results.totalResultsAvailable %} + <tr> + <th align="left">Total Results Available</th> + <td>{{ results.totalResultsAvailable }}</td> + </tr> + {% endif %}{% if results.totalResultsReturned %} + <tr> + <th align="left">Results Returned</th> + <td>{{ results.totalResultsReturned }}</td> + </tr> + {% endif %}{% if results.type %} + <tr> + <th align="left">Type</th> + <td>{{ results.type }}</td> + </tr> + {% endif %} + </table> +</div> diff --git a/js/dojo/dojox/rpc/demos/wikipedia.html b/js/dojo/dojox/rpc/demos/wikipedia.html new file mode 100644 index 0000000..4aeb233 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/wikipedia.html @@ -0,0 +1,41 @@ +<html> + <head> + <title> + Wikipedia Article Grabber Demo + </title> + <script src="../../../dojo/dojo.js" type="text/javascript" + djConfig="parseOnLoad: true, isDebug: true"></script> + <script type="text/javascript"> + dojo.require("dojo.io.script") + dojo.require("dojox.rpc.Service"); + dojo.require("dijit._Widget"); + dojo.require("dojox.dtl._Templated"); + dojo.require("dojo.parser"); + + dojo.declare("WikipediaPageGrabber", [dijit._Widget, dojox.dtl._Templated], { + constructor: function(params, node){ + var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd"); + this.wikipedia = new dojox.rpc.Service(mu); + }, + onSearch: function(e){ + if(e.keyCode == dojo.keys.ENTER){ + this.wikipedia.query({ + action: "parse", + page: e.target.value + }).addCallback(this, function(article){ + console.debug(article.parse); + this.article = article.parse; + this.parsedText = article.parse.text["*"]; // ugh, crazy JSON structure here + this.render(); + }); + } + }, + templateString: dojo.cache("dojox.rpc.demos.templates", "wikipedia.html") + }); + </script> + </head> + <body> + <h1>Wikipedia Page Grabber</h1> + <div dojoType="WikipediaPageGrabber"></div> + </body> +</html> diff --git a/js/dojo/dojox/rpc/demos/yahoo.html b/js/dojo/dojox/rpc/demos/yahoo.html new file mode 100644 index 0000000..4f1d705 --- /dev/null +++ b/js/dojo/dojox/rpc/demos/yahoo.html @@ -0,0 +1,39 @@ +<html> + <head> + <title> + Yahoo Search Demo + </title> + <script src="../../../dojo/dojo.js" type="text/javascript" + djConfig="parseOnLoad: true, isDebug: true"></script> + <script type="text/javascript"> + dojo.require("dojo.io.script") + dojo.require("dojox.rpc.Service"); + dojo.require("dijit._Widget"); + dojo.require("dojox.dtl._Templated"); + dojo.require("dojo.parser"); + + dojo.declare("YahooWebSearchApiWidget", [dijit._Widget, dojox.dtl._Templated], { + constructor: function(params, node){ + var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "yahoo.smd"); + this.yahoo = new dojox.rpc.Service(mu); + }, + onSearch: function(e){ + if(e.keyCode == dojo.keys.ENTER){ + this.yahoo.webSearch({ + query: e.target.value + }).addCallback(this, function(results){ + console.debug(results.ResultSet); + this.results = results.ResultSet; + this.render(); + }); + } + }, + templateString: dojo.cache("dojox.rpc.demos.templates", "yahoo.html") + }); + </script> + </head> + <body> + <h1>Yahoo Web Search</h1> + <div dojoType="YahooWebSearchApiWidget"></div> + </body> +</html> |
