Author: evan
Date: Fri Jan 30 01:07:19 2009
New Revision: 739109
URL: http://svn.apache.org/viewvc?rev=739109&view=rev
Log:
Patch for SHINDIG-877
Added:
incubator/shindig/trunk/features/opensocial-data/
incubator/shindig/trunk/features/opensocial-data/data.js
incubator/shindig/trunk/features/opensocial-data/feature.xml
incubator/shindig/trunk/features/xmlutil/
incubator/shindig/trunk/features/xmlutil/feature.xml
incubator/shindig/trunk/features/xmlutil/xmlutil.js
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js
Modified:
incubator/shindig/trunk/features/features.txt
incubator/shindig/trunk/features/opensocial-reference/datarequest.js
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
Modified: incubator/shindig/trunk/features/features.txt
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/features.txt?rev=739109&r1=739108&r2=739109&view=diff
==============================================================================
--- incubator/shindig/trunk/features/features.txt (original)
+++ incubator/shindig/trunk/features/features.txt Fri Jan 30 01:07:19 2009
@@ -14,6 +14,7 @@
features/opensocial-0.8/feature.xml
features/opensocial-base/feature.xml
features/opensocial-current/feature.xml
+features/opensocial-data/feature.xml
features/opensocial-jsonrpc/feature.xml
features/opensocial-reference/feature.xml
features/opensocial-rest/feature.xml
@@ -25,3 +26,4 @@
features/tabs/feature.xml
features/views/feature.xml
features/pubsub/feature.xml
+features/xmlutil/feature.xml
Added: incubator/shindig/trunk/features/opensocial-data/data.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data/data.js?rev=739109&view=auto
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data/data.js (added)
+++ incubator/shindig/trunk/features/opensocial-data/data.js Fri Jan 30
01:07:19 2009
@@ -0,0 +1,721 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * @fileoverview Implements the global implicit data context for containers.
+ *
+ * TODO:
+ * Variable substitution in markup.
+ * Support cross-cutting predicates (page, sort, search).
+ * URL parameter support.
+ */
+
+var opensocial = window['opensocial'] || function() {};
+
+/**
+ * @type {Object} The namespace declaration for this file.
+ */
+opensocial.data = opensocial.data || {};
+
+var osd = opensocial.data;
+
+/**
+ * @type {string} The key attribute constant.
+ */
+osd.ATTR_KEY = "key";
+
+/**
+ * @type {string} The type of script tags that contain data markup.
+ */
+osd.SCRIPT_TYPE = "text/os-data";
+
+osd.NSMAP = {};
+
+osd.VAR_REGEX = /^([\w\W]*?)(\$\{[^\}]*\})([\w\W]*)$/;
+
+/**
+ * A RequestDescriptor is a wrapper for an XML tag specifying a data request.
+ * This object can be used to access attributes of the request - performing
+ * necessary variable substitutions from the global DataContext. An instance of
+ * this object will be passed to the Data Request Handler so it can obtain its
+ * parameters through it.
+ * @constructor
+ * @param {Element} xmlNode An XML DOM node representing the request.
+ */
+osd.RequestDescriptor = function(xmlNode) {
+ this.tagName = xmlNode.tagName;
+ this.tagParts = this.tagName.split(":");
+ this.attributes = {};
+
+ // Flag to indicate that this request depends on other requests.
+ this.dependencies = false;
+
+ for (var i = 0; i < xmlNode.attributes.length; ++i) {
+ var name = xmlNode.attributes[i].nodeName;
+ if (name) {
+ var value = xmlNode.getAttribute(name);
+ if (name && value) {
+ this.attributes[name] = value;
+ // TODO: This attribute may not be used by the handler.
+ this.computeNeededKeys_(value);
+ }
+ }
+ }
+
+ this.key = this.attributes[osd.ATTR_KEY];
+ this.register_();
+};
+
+
+/**
+ * Checks if an attribute has been specified for this tag.
+ * @param {string} name The attribute name
+ * @return {boolean} The attribute is set.
+ */
+osd.RequestDescriptor.prototype.hasAttribute = function(name) {
+ return !!this.attributes[name];
+};
+
+
+/**
+ * Returns the value of a specified attribute. If the attribute includes
+ * variable substitutions, they will be evaluated against the DataContext and
+ * the result returned.
+ *
+ * @param {string} name The attribute name to look up.
+ * @return {Object} The result of evaluation.
+ */
+osd.RequestDescriptor.prototype.getAttribute = function(name) {
+ var attrExpression = this.attributes[name];
+ if (!attrExpression) {
+ return attrExpression;
+ }
+ // TODO: Don't do this every time - cache the result.
+ var expression = opensocial.data.parseExpression_(attrExpression);
+ if (!expression) {
+ return attrExpression;
+ }
+ return opensocial.data.DataContext.evalExpression(expression);
+};
+
+
+osd.parseExpression_ = function(value) {
+ if (!value.length) {
+ return null;
+ }
+ var substRex = opensocial.data.VAR_REGEX;
+ var text = value;
+ var parts = [];
+ var match = text.match(substRex);
+ if (!match) {
+ return null;
+ }
+ while (match) {
+ if (match[1].length > 0) {
+ parts.push(opensocial.data.transformLiteral_(match[1]));
+ }
+ var expr = match[2].substring(2, match[2].length - 1);
+ parts.push('(' + expr + ')');
+ text = match[3];
+ match = text.match(substRex);
+ }
+ if (text.length > 0) {
+ parts.push(opensocial.data.transformLiteral_(text));
+ }
+ return parts.join('+');
+};
+
+
+/**
+ * Transforms a literal string for inclusion into a variable evaluation:
+ * - Escapes single quotes.
+ * - Replaces newlines with spaces.
+ * - Addes single quotes around the string.
+ */
+osd.transformLiteral_ = function(string) {
+ return "'" + string.replace(/'/g, "\\'").
+ replace(/\n/g, " ") + "'";
+};
+
+
+/**
+ * Sends this request off to be fulfilled. The current DataContext state will
+ * be used to reslove any variable references.
+ */
+osd.RequestDescriptor.prototype.sendRequest = function() {
+ var ns = opensocial.data.NSMAP[this.tagParts[0]];
+ var handler = null;
+ if (ns) {
+ handler = ns[this.tagParts[1]];
+ }
+ if (!handler) {
+ throw "Data handler undefined for " + this.tagName;
+ }
+ handler(this);
+};
+
+
+/**
+ * Creates a closure to this RequestDescriptor's sendRequest() method.
+ */
+osd.RequestDescriptor.prototype.getSendRequestClosure = function() {
+ var self = this;
+ return function() {
+ self.sendRequest();
+ }
+};
+
+
+/**
+ * Computes the keys needed by an attribute by looking for variable
substitution
+ * markup. For example if the attribute is "http://example.com/${user.id}", the
+ * "user" key is needed. The needed keys are set as properties into a member of
+ * this RequestDescriptor.
+ * @param {string} attribute The value of the attribute to inspect.
+ * @private
+ */
+osd.RequestDescriptor.prototype.computeNeededKeys_ = function(attribute) {
+ var substRex = opensocial.data.VAR_REGEX;
+ var match = attribute.match(substRex);
+ while (match) {
+ var token = match[2].substring(2, match[2].length - 1);
+ var key = token.split(".")[0];
+ if (!this.neededKeys) {
+ this.neededKeys = {};
+ }
+ this.neededKeys[key] = true;
+ match = match[3].match(substRex);
+ }
+};
+
+
+/**
+ * Registers this RequestDescriptor using its key.
+ * @private
+ */
+osd.RequestDescriptor.prototype.register_ = function() {
+ opensocial.data.registerRequestDescriptor(this);
+};
+
+
+/**
+ * @type {Object} Global DataContext to contain requested data sets.
+ */
+osd.DataContext = {};
+
+osd.DataContext.listeners_ = [];
+
+osd.DataContext.dataSets_ = {};
+
+/**
+ * Registers a callback listener for a given set of keys.
+ * @param {string|Array.<string>} keys Key or set of keys to listen on.
+ * @param {Function(string)} callback Function to call when a listener is
fired.
+ * TODO: Should return a value that can later be used to return
+ * a value.
+ */
+osd.DataContext.registerListener = function(keys, callback) {
+ var listener = {};
+ listener.keys = {};
+
+ if (typeof(keys) == 'object') {
+ for (var i in keys) {
+ listener.keys[keys[i]] = true;
+ }
+ } else {
+ listener.keys[keys] = true;
+ }
+
+ listener.callback = callback;
+ opensocial.data.DataContext.listeners_.push(listener);
+
+ // Check to see if this one should fire immediately.
+ if (osd.DataContext.isDataReady(listener.keys)) {
+ window.setTimeout(function() {
+ listener.callback()
+ }, 1);
+ }
+};
+
+
+/**
+ * Retrieve a data set for a given key.
+ * @param {string} key Key for the requested data set.
+ * @return {Object} The data set object.
+ */
+osd.DataContext.getDataSet = function(key) {
+ return opensocial.data.DataContext.dataSets_[key];
+};
+
+
+/**
+ * Checks if the data for a map of keys is available.
+ * @param {Object<string, ?>} An map of keys to check.
+ * @return {boolean} Data for all the keys is present.
+ */
+osd.DataContext.isDataReady = function(keys) {
+ for (var key in keys) {
+ if (osd.DataContext.getDataSet(key) == null) {
+ return false;
+ }
+ }
+ return true;
+};
+
+
+/**
+ * Puts a data set into the global DataContext object. Fires listeners
+ * if they are satisfied by the associated key being inserted.
+ *
+ * Note that if this is passed a ResponseItem object, it will crack it open
+ * and extract the JSON payload of the wrapped API Object. This includes
+ * iterating over an array of API objects and extracting their JSON into a
+ * simple array structure.
+ *
+ * @param {string} key The key to associate with this object.
+ * @param {ResponseItem|Object} obj The data object.
+ */
+osd.DataContext.putDataSet = function(key, obj) {
+ var data = obj;
+ if (typeof(data) == "undefined" || data === null) {
+ return;
+ }
+
+ // NOTE: This is ugly, but required since we need to get access
+ // to the JSON/Array payload of API responses.
+ // This will crack the actual API objects and extract their JSON payloads.
+ if (data.getData) {
+ data = data.getData();
+ if (data.array_) {
+ var out = [];
+ for (var i = 0; i < data.array_.length; i++) {
+ out.push(data.array_[i].fields_);
+ }
+ data = out;
+ } else {
+ data = data.fields_ || data;
+ }
+ }
+
+ opensocial.data.DataContext.dataSets_[key] = data;
+ opensocial.data.DataContext.fireCallbacks_(key);
+};
+
+
+/**
+ * Evaluates a JS expression against the DataContext.
+ * @param {string} expr The expression to evaluate.
+ * @return {Object} The result of evaluation.
+ */
+osd.DataContext.evalExpression = function(expr) {
+ return (new Function("context", "with (context) return " + expr))
+ (opensocial.data.DataContext.dataSets_);
+};
+
+
+/**
+ * Fires a listener for a key, but only if the data is ready for other
+ * keys this listener is bound to.
+ * @param {Object} listener The listener object.
+ * @param {string} key The key that this listener is being fired for.
+ */
+osd.DataContext.maybeFireListener_ = function(listener, key) {
+ if (osd.DataContext.isDataReady(listener.keys)) {
+ listener.callback(key);
+ }
+};
+
+
+/**
+ * Scans all active listeners and fires off any callbacks that inserting this
+ * key satisfies.
+ * @param {string} key The key that was updated.
+ * @private
+ */
+osd.DataContext.fireCallbacks_ = function(key) {
+ for (var i = 0; i < opensocial.data.DataContext.listeners_.length; ++i) {
+ var listener = opensocial.data.DataContext.listeners_[i];
+ if (listener.keys[key] != null) {
+ opensocial.data.DataContext.maybeFireListener_(listener, key);
+ }
+ }
+};
+
+
+/**
+ * Accessor to the static DataContext object. At a later date multiple
+ * DataContexts may be used.
+ */
+osd.getDataContext = function() {
+ return opensocial.data.DataContext;
+};
+
+
+/**
+ * @type {Object} Map of currently registered RequestDescriptors (by key).
+ * @private
+ */
+osd.requests_ = {};
+
+
+/**
+ * Registers a RequestDescriptor by key in the global registry.
+ * @param {RequestDescriptor} requestDescriptor The RequestDescriptor to
+ * register.
+ */
+osd.registerRequestDescriptor = function(requestDescriptor) {
+ if (osd.requests_[requestDescriptor.key]) {
+ throw "Request already registered for " + requestDescriptor.key;
+ }
+ opensocial.data.requests_[requestDescriptor.key] = requestDescriptor;
+};
+
+
+/**
+ * @type {DataRequest} A shared DataRequest object for batching OS API data
+ * calls.
+ * @private
+ */
+osd.currentAPIRequest_ = null;
+
+
+/**
+ * @type {Array<string>} An array of keys requested by the shared DataRequest.
+ * @private
+ */
+osd.currentAPIRequestKeys_ = null;
+
+
+/**
+ * @type {Object<string, Function(Object)>} A map of custom callbacks for the
+ * keys in the shared DataRequest.
+ * @private
+ */
+osd.currentAPIRequestCallbacks_ = null;
+
+
+/**
+ * Gets the shared DataRequest, constructing it lazily when needed.
+ * Access to this object is provided so that various sub-requests can be
+ * constructed (i.e. via newFetchPersonRequest()). Neither add() nor send()
+ * should be called on this object - doing so will lead to undefined behavior.
+ * Use opensocial.data.addToCurrentAPIRequest() instead.
+ * TODO: Create a wrapper that doesn't support add() and send().
+ * @return {DataRequest} The shared DataRequest.
+ */
+osd.getCurrentAPIRequest = function() {
+ if (!osd.currentAPIRequest_) {
+ opensocial.data.currentAPIRequest_ = opensocial.newDataRequest();
+ opensocial.data.currentAPIRequestKeys_ = [];
+ opensocial.data.currentAPIRequestCallbacks_ = {};
+ }
+ return opensocial.data.currentAPIRequest_;
+};
+
+
+/**
+ * Adds a request to the current shared DataRequest object. Any requests
+ * added in a synchronous block of code will be batched. The requests will be
+ * automatically sent once the syncronous block is done executing.
+ * @param {Object} request Specifies data to fetch
+ * (constructed via DataRequest's newFetch???Request() methods)
+ * @param {String} key The key to map generated response data to
+ * @param {Function(string, ResponseItem)} opt_callback An optional callback
+ * function to pass the returned ResponseItem to. If present, the function will
+ * be called with the key and ResponseItem as params. If this is omitted, the
+ * ResponseItem will be passed to putDataSet() with the specified key.
+ */
+osd.addToCurrentAPIRequest = function(request, key, opt_callback) {
+ opensocial.data.getCurrentAPIRequest().add(request, key);
+ opensocial.data.currentAPIRequestKeys_.push(key);
+
+ if (opt_callback) {
+ opensocial.data.currentAPIRequestCallbacks_[key] = opt_callback;
+ }
+
+ window.setTimeout(osd.sendCurrentAPIRequest_, 0);
+};
+
+
+/**
+ * Sends out the current shared DataRequest. The reference is removed, so that
+ * when new requests are added, a new shared DataRequest object will be
+ * constructed.
+ * @private
+ */
+osd.sendCurrentAPIRequest_ = function() {
+ if (osd.currentAPIRequest_) {
+
opensocial.data.currentAPIRequest_.send(osd.createSharedRequestCallback_());
+ opensocial.data.currentAPIRequest_ = null;
+ }
+};
+
+
+/**
+ * Creates a callback closure for processing a DataResponse. The closure
+ * remembers which keys were requested, and what custom callbacks need to be
+ * called.
+ * @return {Function(DataResponse)} a handler for DataResponse.
+ * @private
+ */
+osd.createSharedRequestCallback_ = function() {
+ var keys = opensocial.data.currentAPIRequestKeys_;
+ var callbacks = opensocial.data.currentAPIRequestCallbacks_;
+ return function(data) {
+ opensocial.data.onAPIResponse(data, keys, callbacks);
+ }
+};
+
+
+/**
+ * Processes a response to the shared API DataRequest by looping through
+ * requested keys and notifying appropriate parties of the received data.
+ * @param {DataResonse} data Data received from the server
+ * @param {Array<string>} keys The list of keys that were requested
+ * @param {Object<string, Function(string, ResponseItem)>} callbacks A map of
+ * any custom callbacks by key.
+ */
+osd.onAPIResponse = function(data, keys, callbacks) {
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var item = data.get(key);
+ if (callbacks[key]) {
+ callbacks[key](key, item);
+ } else {
+ opensocial.data.DataContext.putDataSet(key, item);
+ }
+ }
+};
+
+
+/**
+ * Registers a tag as a data request handler.
+ * @param {string} name Prefixed tag name.
+ * @param {Function} handler Method to call when this tag is invoked.
+ *
+ * TODO: Store these tag handlers separately from the ones for UI tags.
+ * TODO: Formalize the callback interface.
+ */
+osd.registerRequestHandler = function(name, handler) {
+ var tagParts = name.split(':');
+ var ns = opensocial.data.NSMAP[tagParts[0]];
+ if (!ns) {
+ if (!opensocial.xmlutil.NSMAP[tagParts[0]]) {
+ opensocial.xmlutil.NSMAP[tagParts[0]] = null;
+ }
+ ns = opensocial.data.NSMAP[tagParts[0]] = {};
+ } else if (ns[tagParts[1]]) {
+ throw 'Request handler ' + tagParts[1] + ' is already defined.';
+ }
+
+ ns[tagParts[1]] = handler;
+};
+
+
+/**
+ * Loads and executes all inline data request sections.
+ * @param {Object} opt_doc Optional document to use instead of window.document.
+ * TODO: Currently this processes all 'script' blocks together,
+ * instead of collecting them all and then processing together. Not sure
+ * which is preferred yet.
+ * TODO: Figure out a way to pass in params used only for data
+ * and not for template rendering.
+ */
+osd.processDocumentMarkup = function(opt_doc) {
+ var doc = opt_doc || document;
+ var nodes = doc.getElementsByTagName("script");
+ for (var i = 0; i < nodes.length; ++i) {
+ var node = nodes[i];
+ if (node.type == opensocial.data.SCRIPT_TYPE) {
+ opensocial.data.loadRequests(node);
+ }
+ }
+ opensocial.data.registerRequestDependencies();
+ opensocial.data.executeRequests();
+};
+
+
+/**
+ * Process the document when it's ready.
+ */
+if (window['gadgets'] && window['gadgets']['util']) {
+ gadgets.util.registerOnLoadHandler(osd.processDocumentMarkup);
+}
+
+
+/**
+ * Parses XML data and constructs the pending request list.
+ * @param {Element|string} xml A DOM element or string containing XML.
+ */
+osd.loadRequests = function(xml) {
+ if (typeof(xml) == 'string') {
+ opensocial.data.loadRequestsFromMarkup_(xml);
+ return;
+ }
+ var node = xml;
+ xml = node.value || node.innerHTML;
+ opensocial.data.loadRequestsFromMarkup_(xml);
+};
+
+
+/**
+ * Parses XML data and constructs the pending request list.
+ * @param {string} xml A string containing XML markup.
+ */
+osd.loadRequestsFromMarkup_ = function(xml) {
+ xml = opensocial.xmlutil.prepareXML(xml);
+ var doc = opensocial.xmlutil.parseXML(xml);
+
+ // Find the <root> node (skip DOCTYPE).
+ var node = doc.firstChild;
+ while (node.nodeType != 1) {
+ node = node.nextSibling;
+ }
+
+ opensocial.data.processDataNode_(node);
+};
+
+
+/**
+ * Processes a data request node for data sets.
+ * @param {Node} node The node to process.
+ * @private
+ */
+osd.processDataNode_ = function(node) {
+ for (var child = node.firstChild; child; child = child.nextSibling) {
+ if (child.nodeType == 1) {
+ var requestDescriptor = new opensocial.data.RequestDescriptor(child);
+ }
+ }
+};
+
+
+osd.registerRequestDependencies = function() {
+ for (var key in opensocial.data.requests_) {
+ var request = opensocial.data.requests_[key];
+ var neededKeys = request.neededKeys;
+ var dependencies = [];
+ for (var neededKey in neededKeys) {
+ if (osd.DataContext.getDataSet(neededKey) == null &&
+ opensocial.data.requests_[neededKey]) {
+ dependencies.push(neededKey);
+ }
+ }
+ if (dependencies.length > 0) {
+ opensocial.data.DataContext.registerListener(dependencies,
+ request.getSendRequestClosure());
+ request.dependencies = true;
+ }
+ }
+};
+
+
+osd.executeRequests = function() {
+ for (var key in opensocial.data.requests_) {
+ var request = opensocial.data.requests_[key];
+ if (!request.dependencies) {
+ request.sendRequest();
+ }
+ }
+};
+
+
+/**
+ * Transforms "@"-based special values such as "@owner" into uppercase
+ * keywords like "OWNER".
+ * @param {string} value The value to transform.
+ * @return {string} Transformed or original value.
+ */
+osd.transformSpecialValue = function(value) {
+ if (value.substring(0, 1) == '@') {
+ return value.substring(1).toUpperCase();
+ }
+ return value;
+};
+
+
+/**
+ * Anonymous function defines OpenSocial specific requests.
+ * Automatically called when this file is loaded.
+ */
+(function() {
+ osd.registerRequestHandler("os:ViewerRequest", function(descriptor) {
+ var req =
opensocial.data.getCurrentAPIRequest().newFetchPersonRequest("VIEWER");
+ // TODO: Support @fields param.
+ opensocial.data.addToCurrentAPIRequest(req, descriptor.key);
+ });
+
+ osd.registerRequestHandler("os:OwnerRequest", function(descriptor) {
+ var req =
opensocial.data.getCurrentAPIRequest().newFetchPersonRequest("OWNER");
+ // TODO: Support @fields param.
+ opensocial.data.addToCurrentAPIRequest(req, descriptor.key);
+ });
+
+ osd.registerRequestHandler("os:PeopleRequest", function(descriptor) {
+ var userId = descriptor.getAttribute("userId");
+ var groupId = descriptor.getAttribute("groupId") || "@self";
+ var idSpec = {};
+ idSpec.userId = opensocial.data.transformSpecialValue(userId);
+ if (groupId != "@self") {
+ idSpec.groupId = opensocial.data.transformSpecialValue(groupId);
+ }
+ // TODO: Support other params.
+ var req = opensocial.data.getCurrentAPIRequest().newFetchPeopleRequest(
+ opensocial.newIdSpec(idSpec));
+ // TODO: Annotate with the @ids property.
+ opensocial.data.addToCurrentAPIRequest(req, descriptor.key);
+ });
+
+ osd.registerRequestHandler("os:ActivitiesRequest", function(descriptor) {
+ var userId = descriptor.getAttribute("userId");
+ var groupId = descriptor.getAttribute("groupId") || "@self";
+ var idSpec = {};
+ idSpec.userId = opensocial.data.transformSpecialValue(userId);
+ if (groupId != "@self") {
+ idSpec.groupId = opensocial.data.transformSpecialValue(groupId);
+ }
+ // TODO: Support other params.
+ var req = opensocial.data.getCurrentAPIRequest().newFetchActivitiesRequest(
+ opensocial.newIdSpec(idSpec));
+ opensocial.data.addToCurrentAPIRequest(req, descriptor.key);
+ });
+
+ osd.registerRequestHandler("os:DataRequest", function(descriptor) {
+ var href = descriptor.getAttribute('href');
+ var format = descriptor.getAttribute('format') || "json";
+ var params = {};
+ params[gadgets.io.RequestParameters.CONTENT_TYPE] =
+ format.toLowerCase() == "text" ? gadgets.io.ContentType.TEXT :
+ gadgets.io.ContentType.JSON;
+ params[gadgets.io.RequestParameters.METHOD] =
+ gadgets.io.MethodType.GET;
+ gadgets.io.makeRequest(href, function(obj) {
+ opensocial.data.DataContext.putDataSet(descriptor.key, obj.data);
+ }, params);
+ });
+})();
+
+
+/**
+ * Pre-populate a Data Set based on application's URL parameters.
+ */
+(osd.populateParams_ = function() {
+ if (window["gadgets"] && gadgets.util.hasFeature("views")) {
+ opensocial.data.DataContext.putDataSet("ViewParams",
gadgets.views.getParams());
+ }
+})();
\ No newline at end of file
Added: incubator/shindig/trunk/features/opensocial-data/feature.xml
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data/feature.xml?rev=739109&view=auto
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data/feature.xml (added)
+++ incubator/shindig/trunk/features/opensocial-data/feature.xml Fri Jan 30
01:07:19 2009
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<feature>
+ <name>opensocial-data</name>
+ <dependency>opensocial-0.8</dependency>
+ <dependency>xmlutil</dependency>
+ <gadget>
+ <script src="data.js"></script>
+ </gadget>
+</feature>
Modified: incubator/shindig/trunk/features/opensocial-reference/datarequest.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-reference/datarequest.js?rev=739109&r1=739108&r2=739109&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-reference/datarequest.js
(original)
+++ incubator/shindig/trunk/features/opensocial-reference/datarequest.js Fri
Jan 30 01:07:19 2009
@@ -489,7 +489,7 @@
opt_params) {
opt_params = opt_params || {};
- var fields = opensocial.DataRequest.PeopleRequestFields;
+ var fields = opensocial.DataRequest.ActivityRequestFields;
this.addDefaultParam(opt_params, fields.FIRST, 0);
this.addDefaultParam(opt_params, fields.MAX, 20);
Added: incubator/shindig/trunk/features/xmlutil/feature.xml
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/xmlutil/feature.xml?rev=739109&view=auto
==============================================================================
--- incubator/shindig/trunk/features/xmlutil/feature.xml (added)
+++ incubator/shindig/trunk/features/xmlutil/feature.xml Fri Jan 30 01:07:19
2009
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<feature>
+ <name>xmlutil</name>
+ <gadget>
+ <script src="xmlutil.js"></script>
+ </gadget>
+</feature>
Added: incubator/shindig/trunk/features/xmlutil/xmlutil.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/xmlutil/xmlutil.js?rev=739109&view=auto
==============================================================================
--- incubator/shindig/trunk/features/xmlutil/xmlutil.js (added)
+++ incubator/shindig/trunk/features/xmlutil/xmlutil.js Fri Jan 30 01:07:19 2009
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var opensocial = window['opensocial'] || {};
+
+opensocial.xmlutil = opensocial.xmlutil || {};
+
+/**
+ * Cached DOMParser objects on browsers that support it.
+ */
+opensocial.xmlutil.parser_ = null;
+
+
+/**
+ * Parses an XML string into an XML Document.
+ * @param {string} str A string of well-formed XML.
+ * @return {Document} XML document.
+ */
+opensocial.xmlutil.parseXML = function(str) {
+ if (typeof(DOMParser) != "undefined") {
+ opensocial.xmlutil.parser_ = opensocial.xmlutil.parser_ || new DOMParser();
+ var doc = opensocial.xmlutil.parser_.parseFromString(str, "text/xml");
+ if (doc.firstChild && doc.firstChild.tagName == 'parsererror') {
+ throw doc.firstChild.firstChild.nodeValue;
+ }
+ return doc;
+ } else {
+ var doc = new ActiveXObject("MSXML2.DomDocument");
+ doc.validateOnParse = false;
+ doc.loadXML(str);
+ if (doc.parseError && doc.parseError.errorCode) {
+ throw doc.parseError.reason;
+ }
+ return doc;
+ }
+};
+
+
+/**
+ * Map of Namespace prefixes to their respective URLs.
+ * @type Map<string, string>
+ */
+opensocial.xmlutil.NSMAP = {
+ "os": "http://opensocial.org/"
+};
+
+
+/**
+ * Returns the XML namespace declarations that need to be injected into a
+ * particular XML-like snippet to make it valid XML. Uses the defined
+ * namespaces to see which are available, and checks that they are used in
+ * the supplied code. An empty string is returned if no injection is needed.
+ *
+ * @param {string} xml XML-like source code.
+ * @return {string} A string of xmlns delcarations required for this XML.
+ */
+opensocial.xmlutil.getRequiredNamespaces = function(xml) {
+ var codeToInject = [];
+ for (var ns in opensocial.xmlutil.NSMAP) {
+ if (xml.indexOf("<" + ns + ":") >= 0 &&
+ xml.indexOf("xmlns:" + ns + ":") < 0) {
+ codeToInject.push(" xmlns:");
+ codeToInject.push(ns);
+ codeToInject.push("=\"");
+ codeToInject.push(opensocial.xmlutil.NSMAP[ns]);
+ codeToInject.push("\"");
+ }
+ }
+ return codeToInject.join("");
+};
+
+
+/**
+ * XHTML Entities we need to support in XML, definted in DOCTYPE declaration.
+ *
+ * TODO: A better way to do this.
+ */
+opensocial.xmlutil.ENTITIES = "<!ENTITY nbsp \" \">";
+
+
+/**
+ * Prepares an XML-like string to be parsed by browser parser. Injects a
DOCTYPE
+ * with entities and a top-level <root> element to encapsulate the code.
+ * @param {string} xml XML string to be prepared.
+ * @return {string} XML string prepared for client-side parsing.
+ */
+opensocial.xmlutil.prepareXML = function(xml) {
+ var namespaces = opensocial.xmlutil.getRequiredNamespaces(xml);
+ return "<!DOCTYPE root [" + opensocial.xmlutil.ENTITIES +
+ "]><root xml:space=\"preserve\"" +
+ namespaces + ">" + xml + "</root>";
+};
\ No newline at end of file
Modified:
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java?rev=739109&r1=739108&r2=739109&view=diff
==============================================================================
---
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
(original)
+++
incubator/shindig/trunk/java/server/src/test/java/org/apache/shindig/server/endtoend/EndToEndTest.java
Fri Jan 30 01:07:19 2009
@@ -114,6 +114,11 @@
}
@Test
+ public void testData() throws Exception {
+ executeAllPageTests("opensocial-data/data_test");
+ }
+
+ @Test
public void testTemplates() throws Exception {
executeAllPageTests("opensocial-templates/ost_test");
}
Added:
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js?rev=739109&view=auto
==============================================================================
---
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js
(added)
+++
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.js
Fri Jan 30 01:07:19 2009
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+
+function testParseExpression() {
+ var expressions = [
+ [ "Hello world", null ],
+ [ "${foo}", "(foo)" ],
+ [ "Hello ${foo} world", "'Hello '+(foo)+' world'" ],
+ [ "${foo} ${bar}", "(foo)+' '+(bar)" ]
+ ];
+ for (var i = 0; i < expressions.length; i++) {
+ assertEquals(
+ expressions[i][1],
+ opensocial.data.parseExpression_(expressions[i][0])
+ );
+ }
+};
+
+function testEvalExpression() {
+ var data = {
+ 'foo': 'Hello',
+ 'bar': 'World'
+ };
+ data.name = {
+ 'first': 'John',
+ 'last': 'Doe'
+ };
+ opensocial.data.DataContext.putDataSet("test", data);
+ var expressions = [
+ [ opensocial.data.parseExpression_("Test: ${test.foo}"), "Test: Hello" ],
+ [ opensocial.data.parseExpression_("${test.foo} ${test.bar}!"), "Hello
World!" ],
+ [ opensocial.data.parseExpression_("${test.name.first}
${test.name.last}"), "John Doe" ]
+ ];
+ for (var i = 0; i < expressions.length; i++) {
+ assertEquals(
+ expressions[i][1],
+ opensocial.data.DataContext.evalExpression(expressions[i][0])
+ );
+ }
+};
+
+/**
+ * Unit test to test data result handlers.
+ */
+function testPutDataSet() {
+ var key = 'test1';
+ var value = 'foo';
+ opensocial.data.DataContext.putDataSet(key, value);
+ assertEquals(value, opensocial.data.DataContext.getDataSet(key));
+};
+
+function registerNS(prefix) {
+ opensocial.xmlutil.NSMAP[prefix] = "#" + prefix;
+};
+
+/**
+ * Unit test to check full functionality of a request handler.
+ */
+function testRequestHandler() {
+ registerNS("test");
+ var results = {};
+ opensocial.data.registerRequestHandler('test:request', function(descriptor) {
+ results[descriptor.key] = descriptor.getAttribute('data');
+ });
+ var xmlData =
+ '<test:request key="first" data="testData"/>' +
+ '<test:request key="second" data="${foo}"/>';
+
+ opensocial.data.loadRequests(xmlData);
+ assertNotNull(opensocial.data.requests_['first']);
+ assertNotNull(opensocial.data.requests_['second']);
+
+ opensocial.data.DataContext.putDataSet("foo", "bar");
+ opensocial.data.executeRequests();
+
+ assertEquals('testData', results['first']);
+ assertEquals('bar', results['second']);
+};
+
+/**
+ * Unit test to test listener functionality when a data key is put.
+ */
+function testListener() {
+ var fired = false;
+ opensocial.data.DataContext.registerListener('testKey', function() {
+ fired = true;
+ });
+ opensocial.data.DataContext.putDataSet('testKey', {});
+ assertEquals(true, fired);
+}
+
Added:
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml?rev=739109&view=auto
==============================================================================
---
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml
(added)
+++
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/data_test.xml
Fri Jan 30 01:07:19 2009
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<Module>
+ <ModulePrefs title="TemplatesEndToEndTest">
+ <Require feature="opensocial-data"/>
+ </ModulePrefs>
+ <Content type="html">
+ <![CDATA[
+ <script type="text/javascript"
src="/opensocial-data/testadapter.js"></script>
+ <!-- the JsUnit tests -->
+ <script type="text/javascript"
src="/opensocial-data/data_test.js"></script>
+ ]]>
+ </Content>
+</Module>
Added:
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js?rev=739109&view=auto
==============================================================================
---
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js
(added)
+++
incubator/shindig/trunk/java/server/src/test/resources/endtoend/opensocial-data/testadapter.js
Fri Jan 30 01:07:19 2009
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+function findTests() {
+ // The following tests fail due to HtmlUnit limitation.
+ // If a name exists in this object, it is excluded from testing.
+ var excludedTests = {
+ testEventHandlers : 1,
+ testInjectStyle : 1,
+ testLoadContent : 1,
+ testInjectJavaScript : 1,
+ testRegisterTemplates : 1
+ };
+
+ var testSource = typeof RuntimeObject != 'undefined' ?
+ RuntimeObject('test' + '*') : self;
+ var tests = {};
+ for (i in testSource) {
+ if (i.substring(0, 4) == 'test' && typeof(testSource[i]) == 'function'
+ && ! (i in excludedTests)) {
+ tests[i] = testSource[i];
+ }
+ }
+ return tests;
+}
+
+function assertTrue(value) {
+ if (!value) {
+ throw "assertTrue() failed: ";
+ }
+}
+
+function assertFalse(value) {
+ if (value) {
+ throw "assertFalse() failed: ";
+ }
+}
+
+function assertEquals(a, b) {
+ if (a != b) {
+ throw "assertEquals() failed: " +
+ "\nExpected \"" + a + "\", was \"" + b + "\"";
+ }
+}
+
+function assertNotNull(value) {
+ if (value === null) {
+ throw "assertTrue() failed: ";
+ }
+}
+
+function allTests() {
+ var tests = findTests();
+ for (var testMethod in tests) {
+ alert(testMethod);
+ tests[testMethod]();
+ alert("FINISHED");
+ }
+}
+
+gadgets.util.registerOnLoadHandler(allTests);