shindig-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wood...@apache.org
Subject svn commit: r1146859 - in /shindig/trunk/features: ./ src/main/javascript/features/ src/main/javascript/features/actions/ src/main/javascript/features/selection/ src/test/javascript/features/actions/ src/test/javascript/features/selection/
Date Thu, 14 Jul 2011 19:17:37 GMT
Author: woodser
Date: Thu Jul 14 19:17:36 2011
New Revision: 1146859

URL: http://svn.apache.org/viewvc?rev=1146859&view=rev
Log:
Applying Action + Selection patch (post gjslint): http://codereview.appspot.com/4645056/

Added:
    shindig/trunk/features/src/main/javascript/features/actions/
    shindig/trunk/features/src/main/javascript/features/actions/actions.js
    shindig/trunk/features/src/main/javascript/features/actions/actions_container.js
    shindig/trunk/features/src/main/javascript/features/actions/feature.xml
    shindig/trunk/features/src/main/javascript/features/actions/taming.js
    shindig/trunk/features/src/main/javascript/features/selection/
    shindig/trunk/features/src/main/javascript/features/selection/feature.xml
    shindig/trunk/features/src/main/javascript/features/selection/selection.js
    shindig/trunk/features/src/main/javascript/features/selection/selection_container.js
    shindig/trunk/features/src/main/javascript/features/selection/taming.js
    shindig/trunk/features/src/test/javascript/features/actions/
    shindig/trunk/features/src/test/javascript/features/actions/actions_test.js
    shindig/trunk/features/src/test/javascript/features/selection/
    shindig/trunk/features/src/test/javascript/features/selection/selection_test.js
Modified:
    shindig/trunk/features/pom.xml
    shindig/trunk/features/src/main/javascript/features/features.txt

Modified: shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/pom.xml?rev=1146859&r1=1146858&r2=1146859&view=diff
==============================================================================
--- shindig/trunk/features/pom.xml (original)
+++ shindig/trunk/features/pom.xml Thu Jul 14 19:17:36 2011
@@ -177,6 +177,10 @@
                 <source>osapi/peoplehelpers.js</source>
                 <source>../../../../src/test/javascript/lib/testutils.js</source>
                 <source>oauthpopup/oauthpopup.js</source>
+                <source>selection/selection_container.js</source>
+                <source>selection/selection.js</source>
+                <source>actions/actions_container.js</source>
+                <source>actions/actions.js</source>
                 <source>opensearch/opensearch.js</source>
               </sources>
               <testSourceDirectory>${basedir}/src/test/javascript/features</testSourceDirectory>

Added: shindig/trunk/features/src/main/javascript/features/actions/actions.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/actions/actions.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/actions/actions.js (added)
+++ shindig/trunk/features/src/main/javascript/features/actions/actions.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,212 @@
+/*
+ * 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 Provides facilities for registering action callback
+ *               functions to actions that may be rendered anywhere in
+ *               the container.  Available to every gadget.
+ */
+gadgets['actions'] = (function() {
+
+  /**
+   * Runs the callback function associated with the specified action id.
+   *
+   * Example:
+   *
+   * <pre>
+   * gadgets.actions.runAction(actionId);
+   * </pre>
+   *
+   * @param {String}
+   *          actionId The action identifier.
+   *
+   * @member gadgets.actions
+   */
+  function runAction(actionData) {
+    var actionId = actionData.actionId;
+    // optional
+    var selectionObj = actionData.selectionObj;
+
+    var callback = callbackRegistry.getCallback(actionId);
+    if (callback) {
+      var args = selectionObj ? [selectionObj] : null;
+      callback.apply(this, args);
+    }
+  };
+
+  /**
+   * @constructor Object that maps action ids to callback functions.
+   */
+  function ActionCallbackRegistry() {
+    this.registryById = {};
+    this.addAction = function(actionId, callbackFn) {
+      this.registryById[actionId] = callbackFn;
+    };
+    this.removeAction = function(actionId) {
+      delete this.registryById[actionId];
+    };
+    this.getCallback = function(actionId) {
+      return this.registryById[actionId];
+    };
+  };
+
+  // router function called to run actions
+  function router(channel, object) {
+    var actionData = object;
+    if (channel == 'runAction') {
+      runAction(actionData);
+    }
+  };
+
+  // create the callback registry and
+  // initialize the rpc router
+  var callbackRegistry = new ActionCallbackRegistry();
+  var _init;
+  var init = function() {
+    if (!_init) {
+      gadgets.rpc.register('actions', router);
+      _init = true;
+    }
+  };
+
+  return /** @scope gadgets.actions */ {
+    /**
+     * Registers an action with the actions feature.
+     *
+     * Example:
+     *
+     * <pre>
+     * gadgets.actions.addAction(actionObj);
+     * </pre>
+     *
+     * @param {function(Object)}
+     *          actionObj The action object.
+     *
+     * @member gadgets.actions
+     */
+    addAction: function(actionObj) {
+      init();
+      var actionId = actionObj.id;
+      var actionCallback = actionObj.callback;
+      callbackRegistry.addAction(actionId, actionCallback);
+
+      // notify the container that an action has been added.
+      gadgets.rpc.call('..', 'actions', null, 'bindAction', actionObj);
+    },
+
+    /**
+     * Updates an action that has already been registered.
+     *
+     * Example:
+     *
+     * <pre>
+     * gadgets.actions.updateAction(actionObj);
+     * </pre>
+     *
+     * @param {function(Object)}
+     *          actionObj The action object.
+     *
+     * @member gadgets.actions
+     */
+    updateAction: function(actionObj) {
+      // TODO for now we only support updating the callback
+      // to support the declaratively contributed actions,
+      // we need to support updating the label as well.
+      init();
+      var actionId = actionObj.id;
+      var actionCallback = actionObj.callback;
+      callbackRegistry.addAction(actionId, actionCallback);
+
+      // notify the container that an action has been added.
+      gadgets.rpc.call('..', 'actions', null, 'bindAction', actionObj);
+    },
+
+    /**
+     * Removes the association of a callback function with an action id.
+     *
+     * Example:
+     *
+     * <pre>
+     * gadgets.actions.removeAction(actionId);
+     * </pre>
+     *
+     * @param {string}
+     *          actionId The action identifier.
+     *
+     * @member gadgets.actions
+     */
+    removeAction: function(actionId) {
+      init();
+      callbackRegistry.removeAction(actionId);
+
+      // notify the container to remove action from its UI
+      gadgets.rpc.call('..', 'actions', null, 'removeAction', actionId);
+    },
+
+    /**
+     * Gets array of actions at the specified path and passes the result
+     * to the callback function.
+     *
+     * Example:
+     *
+     * <pre>
+     * var callback = function(actions){
+     *  ...
+     * }
+     * gadgets.actions.getActionsByPath("container/navigationLinks", callback);
+     * </pre>
+     *
+     * @param {string}
+     *          path The path to the actions.
+     * @param {function}
+     *          callback A callback function to handle the returned actions
+     *          array.
+     *
+     * @member gadgets.actions
+     */
+    getActionsByPath: function(path, callback) {
+      gadgets.rpc.call('..', 'actions', callback, 'getActionsByPath', path);
+    },
+
+    /**
+     * Gets array of actions for the specified data type and passes the result
+     * to the callback function.
+     *
+     * Example:
+     *
+     * <pre>
+     * var callback = function(actions){
+     *  ...
+     * }
+     * gadgets.actions.getActionsByDataType("opensocial.Person", callback);
+     * </pre>
+     *
+     * @param {string}
+     *          dataType The String representation of an OpenSocial data type.
+     * @param {function}
+     *          callback A callback function to handle the returned actions
+     *          array.
+     *
+     * @member gadgets.actions
+     */
+    getActionsByDataType: function(dataType, callback) {
+      gadgets.rpc.call('..', 'actions', callback, 'getActionsByDataType',
+          dataType);
+    }
+  };
+})();

Added: shindig/trunk/features/src/main/javascript/features/actions/actions_container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/actions/actions_container.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/actions/actions_container.js (added)
+++ shindig/trunk/features/src/main/javascript/features/actions/actions_container.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,734 @@
+/*
+ * 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 Provides facilities for contributing actions to various parts
+ *               of the UI. Available to the common container.
+ */
+(function() {
+
+  /**
+   * @constructor Object that tracks the actions currently registered with the
+   *              container.
+   */
+  function ActionRegistry() {
+
+    // maps action ids to action objects
+    this.registryById = {};
+
+    // maps actions by contribution path
+    this.registryByPath = {};
+
+    // maps actions to OS data types
+    this.registryByDataType = {};
+
+    // maps actions to URL of the contributor
+    this.registryByUrl = {};
+
+    // one-to-many association of urls to gadget sites
+    this.urlToSite = {};
+
+    // one-to-one relationship of each url to the gadget metadata
+    this.urlToMetadata = {};
+
+    // one-to-one relationship of each action to the url
+    this.actionToUrl = {};
+
+    /**
+     * Adds an action object to the registry
+     *
+     * @param {Object}
+     *          actionObj JSON object that represents an action.
+     * @param {String}
+     *          url gadget spec URL, from which the action contribution
+     *          originated.
+     */
+    this.addAction = function(actionObj, url) {
+      var id = actionObj.id;
+      if (!id) { /* invalid object */
+        return;
+      }
+
+      var path = actionObj.path;
+      if (path) {
+        /**
+         * We maintain a tree of arrays for actions that are contributed
+         * to paths.  This is necessary to realize actions in hierarchical
+         * menus, sub-menus and drop-down toolbar buttons.
+         */
+        var partsOfPath = path.split('/');
+        var parent = this.registryByPath;
+        for (var i in partsOfPath) {
+          var currentNode = partsOfPath[i];
+          if (!parent[currentNode]) {
+            parent[currentNode] = {};
+          }
+          parent = parent[currentNode];
+        }
+        // store actions as array under attribute "@actions"
+        var actionsAtPath = parent['@actions'];
+        if (!actionsAtPath) {
+          parent['@actions'] = [actionObj];
+        } else {
+          parent['@actions'] = actionsAtPath.concat(actionObj);
+        }
+      } else if (actionObj.dataType) {
+        /**
+         * We maintain a simple map for actions that are bound to an
+         * OpenSocial data object type such as the person object.
+         */
+        var dataType = actionObj.dataType;
+        this.registryByDataType[dataType] =
+          this.registryByDataType[dataType] ?
+              this.registryByDataType[dataType].concat(actionObj) :
+                [actionObj];
+      } else {
+        // invalid object, no valid path or dataType to bind action
+        return;
+      }
+
+      // add action to id registry
+      this.registryById[id] = actionObj;
+
+      // map actions to url, used by runAction to render gadget
+      if (url) {
+        this.actionToUrl[actionObj.id] = url;
+        this.registryByUrl[url] =
+          this.registryByUrl[url] ?
+              this.registryByUrl[url].concat(actionObj) :
+                [actionObj];
+      }
+    };
+
+    /**
+     * Removes an action object from the registry
+     *
+     * @param {String}
+     *          actionId unique identifier for the action, as specified in the
+     *          action object.
+     */
+    this.removeAction = function(actionId) {
+      var actionObj = this.registryById[actionId];
+
+      // remove from registryById
+      delete this.registryById[actionId];
+
+      // remove from the other registries
+      var path = actionObj.path;
+      if (path) { // remove from registryByPath
+          var actionsAtPath = this.getActionsByPath(path);
+          var i = actionsAtPath.indexOf(actionObj);
+          if (i != -1) {
+            actionsAtPath.splice(i, 1);
+          }
+      } else { // remove from registryByDataType
+        var dataType = actionObj.dataType;
+        var actionsForDataType = this.registryByDataType[dataType];
+        var actionIndex = actionsForDataType.indexOf(actionObj);
+        actionsForDataType.splice(actionIndex, 1);
+        if (actionsForDataType.length == 0) {
+          delete this.registryByDataType[dataType];
+        }
+      }
+
+      // remove from url mappings
+      var url = this.actionToUrl[actionId];
+      if (url) {
+        delete this.actionToUrl[actionId];
+        var actionsForUrl = this.registryByUrl[url];
+        var actionIndex = actionsForUrl.indexOf(actionObj);
+        actionsForUrl.splice(actionIndex, 1);
+        if (actionsForUrl.length == 0) {
+          delete this.registryByUrl[url];
+        }
+      }
+    };
+
+    /**
+     * Returns the action associated with the specified id
+     *
+     * @param {String}
+     *          id Unique identifier for the action object.
+     */
+    this.getItemById = function(id) {
+      var children = this.registryById ? this.registryById : {};
+      return children[id];
+    };
+
+    /**
+     * Returns all actions in the registry
+     */
+    this.getAllActions = function() {
+      var actions = [];
+      for (actionId in this.registryById) {
+        actions = actions.concat(this.registryById[actionId]);
+      }
+      return actions;
+    };
+
+    /**
+     * Returns all items associated with the given path
+     *
+     * @param {String}
+     *          path Navigation path to the action, as specified in the action
+     *          object.
+     */
+    this.getActionsByPath = function(path) {
+      var actions = [];
+      var partsOfPath = path.split('/');
+      var children = this.registryByPath ? this.registryByPath : {};
+      for (var i in partsOfPath) {
+        var currentNode = partsOfPath[i];
+        if (children[currentNode]) {
+          children = children[currentNode];
+        } else {
+          // if path doesn't exist, return empty array
+          return actions;
+        }
+      }
+      if (children) {
+        actions = children['@actions'];
+      }
+      return actions;
+    };
+
+    /**
+     * Returns the actions associated with the specified data object
+     *
+     * @param {String}
+     *          dataType The Open Social data type associated with the action.
+     */
+    this.getActionsByDataType = function(dataType) {
+      var actions = [];
+      if (this.registryByDataType[dataType]) {
+        actions = this.registryByDataType[dataType];
+      }
+      return actions;
+    };
+
+    /**
+     * Returns the actions associated with the specified url
+     *
+     * @param {String}
+     *          url The gadget spec url associated with the action(s).
+     */
+    this.getActionsByUrl = function(url) {
+      var children = [];
+      if (this.registryByUrl[url]) {
+        children = children.concat(this.registryByUrl[url]);
+      }
+      return children;
+    };
+
+    /**
+     * Adds a new active gadget site to the registry
+     *
+     * @param {String}
+     *          url The gadget spec url associated with the gadget site.
+     * @param {osapi.container.GadgetSite}
+     *          site The instance of the gadget site.
+     */
+    this.addGadgetSite = function(url, site) {
+      var existingSite = this.urlToSite[url];
+      if (existingSite) {
+        this.urlToSite[url] = existingSite.concat(site);
+      } else {
+        this.urlToSite[url] = [site];
+      }
+    };
+
+    /**
+     * Removes a gadget site from the registry
+     *
+     * @param {String}
+     *          siteId The unique identifier for the gadget site instance.
+     */
+    this.removeGadgetSite = function(siteId) {
+      for (var url in this.urlToSite) {
+        var sites = this.urlToSite[url];
+        for (var i in sites) {
+          var site = sites[i];
+          if (site && site.getId() == siteId) {
+            sites.splice(i, 1);
+            if (sites.length == 0) {
+              delete this.urlToSite[url];
+            }
+          }
+        }
+      }
+    };
+
+    /**
+     * Returns the gadget site associated with the specified action object.
+     *
+     * @param {Object}
+     *          actionId The id of the action.
+     * @return {osapi.container.GadgetSite} The gadget site instance associated
+     *         with the action object.
+     */
+    this.getGadgetSite = function(actionId) {
+      var url = this.actionToUrl[actionId];
+      var sites = this.urlToSite[url];
+      return sites;
+    };
+
+    /**
+     * Returns the url associated with an action
+     *
+     * @param {String}
+     *          actionId The id of the action.
+     * @return {String} url Gadget spec url associated with the action.
+     */
+    this.getUrl = function(actionId) {
+      return this.actionToUrl[actionId];
+    };
+
+    /**
+     * Saves the metadata associated with a url
+     *
+     * @param {String}
+     *          url Gadget spec url.
+     * @param {Object}
+     *          metadata Metadata of the gadget.
+     */
+    this.setGadgetMetadata = function(url, metadata) {
+      this.urlToMetadata[url] = metadata;
+    };
+
+    /**
+     * Returns the metadata associated with a url
+     *
+     * @param {String}
+     *          url Gadget spec url.
+     * @return {Object} metadata Metadata for a gadget.
+     */
+    this.getGadgetMetadata = function(url) {
+      return this.urlToMetadata[url];
+    };
+  };
+
+  /**
+   * Utility function for converting a string representation of XML to a DOM
+   * object
+   *
+   * @param {String}
+   *          xmlString String representation of a valid XML object.
+   * @return {Object} response JSON object whose "data" field will contain the
+   *          DOM object, otherwise, the "errors" field will contain a string
+   *          description.
+   */
+  function createDom(xmlString) {
+    var response = {};
+    var dom;
+    if (typeof ActiveXObject != 'undefined') {
+      dom = new ActiveXObject('Microsoft.XMLDOM');
+      dom.async = false;
+      dom.validateOnParse = false;
+      dom.resolveExternals = false;
+      if (!dom.loadXML(xmlString)) {
+        response['errors'].push('500 Failed to parse XML');
+        response['rc'] = 500;
+      } else {
+        response['data'] = dom;
+      }
+    } else {
+      var parser = new DOMParser();
+      dom = parser.parseFromString(xmlString, 'text/xml');
+      if ('parsererror' === dom.documentElement.nodeName) {
+        response['errors'].push('500 Failed to parse XML');
+        response['rc'] = 500;
+      } else {
+        response['data'] = dom;
+      }
+    }
+    return response;
+  };
+
+  /**
+   * Container handling of an action that has been programmatically added via
+   * gadgets.actions.addAction() API
+   *
+   * @param {Object}
+   *          actionObj The action object coming from the gadget side.
+   *
+   */
+  function bindAction(actionObj) {
+    var actionId = actionObj.id;
+    var containerActionObj = registry.getItemById(actionId);
+    // if action is not in registry, then this is a programmatic add
+    if (!containerActionObj) {
+      addAction(actionObj);
+    } else {
+      // check if this action needs to be run
+      var pendingAction = pendingActions[actionId];
+      if (pendingAction) {
+        runAction(actionId, pendingAction.selection);
+        delete pendingActions[actionId];
+      }
+    }
+  };
+
+  /**
+   * Adds the action to the action registry, and renders the action in the
+   * container UI.
+   *
+   * @param {Object}
+   *          actionObj The action object with id, label, title, icon, and any
+   *          other information needed to render the action in the container's
+   *          UI.
+   * @param {String}
+   *          url Optional value needed to be passed in when adding action via
+   *          preload listener (for subsequent loading of the gadget).
+   *
+   */
+  function addAction(actionObj, url) {
+    registry.addAction(actionObj, url);
+    // notify the container to display the action
+    showActionHandler(actionObj);
+  };
+
+  /**
+   * Removes the action from the action registry, and removes the action from
+   * the container UI.
+   *
+   * @param {String}
+   *          The action id.
+   *
+   */
+  function removeAction(id) {
+    var actionObj = registry.getItemById(id);
+    registry.removeAction(id);
+    // notify the container to hide the action
+    hideActionHandler(actionObj);
+  };
+
+  /**
+   * Runs the action associated with the specified actionId. If the gadget has
+   * not yet been rendered, renders the gadget first, then runs the action.
+   *
+   * @param {String}
+   *          The unique identifier for the action.
+   *
+   */
+  function runAction(actionId, selection) {
+    var actionData = {};
+    actionData.actionId = actionId;
+    actionData.selectionObj = selection;
+    if (!selection && container_ && container_.selection) {
+      actionData.selectionObj = container_.selection.getSelection();
+    }
+    // make rpc call to get gadget to run callback based on action id
+    var gadgetSites = registry.getGadgetSite(actionId);
+    if (gadgetSites && gadgetSites.length > 0) {
+      var frameId = gadgetSites[0].getActiveGadgetHolder().getIframeId();
+    }
+    gadgets.rpc.call(frameId, 'actions', null, 'runAction', actionData);
+  };
+
+  /**
+   * Callback for loading actions after gadget has been preloaded.
+   *
+   * @param {Array}
+   *          Response from container's lifecycle handling of preloading the
+   *          gadget.
+   */
+  var preloadCallback = function(response) {
+    for (var url in response) {
+      var metadata = response[url];
+      if (!metadata.error) {
+        registry.setGadgetMetadata(url, metadata);
+        if (metadata.modulePrefs) {
+          var feature = metadata.modulePrefs.features['actions'];
+          if (feature && feature.params) {
+            var desc = feature.params['action-contributions'];
+            if (desc) {
+              var domResponse = createDom(desc);
+              if (domResponse && !domResponse['errors']) {
+                var jsonDesc = gadgets.json.xml
+                  .convertXmlToJson(domResponse['data']);
+                var actionsJson = jsonDesc['actions'];
+                if (actionsJson) {
+                  var actions = actionsJson['action'];
+                  if (!(actions instanceof Array)) {
+                    actions = [actions];
+                  }
+                  for (var i in actions) {
+                    var actionObj = actions[i];
+                    // replace @ for attribute keys;
+                    for (itemAttr in actionObj) {
+                      var attrStr = itemAttr.substring(1);
+                      var attrVal = actionObj[itemAttr];
+                      actionObj[attrStr] = attrVal;
+                      delete actionObj[itemAttr];
+                    }
+                    // check if action already exists
+                    if (!registry.getItemById(actionObj.id)) {
+                      addAction(actionObj, url);
+                    }
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  };
+
+  /**
+   * Callback for when gadget site has been navigated.
+   *
+   * @param {Object}
+   *          Gadget site that has been navigated.
+   */
+  var navigatedCallback = function(site) {
+    var gadgetHolder = site.getActiveGadgetHolder();
+    if (gadgetHolder) {
+      var url = gadgetHolder.getUrl();
+      registry.addGadgetSite(url, site);
+    }
+  };
+
+  /**
+   * Callback for when a gadget site has been closed.
+   *
+   * @param {Object}
+   *          Gadget site that has been closed.
+   */
+  var closedCallback = function(site) {
+    var siteId = site.getId();
+    registry.removeGadgetSite(siteId);
+  };
+
+  /**
+   * Callback for when a gadget has been unloaded.
+   *
+   * @param {String}
+   *          Gadget spec url for the gadget that has been unloaded.
+   */
+  var unloadedCallback = function(url) {
+    var actionsForUrl = registry.getActionsByUrl(url);
+    for (var i in actionsForUrl) {
+      var action = actionsForUrl[i];
+      removeAction(action.id);
+    }
+  };
+
+  // Object containing gadget lifecycle listeners
+  var actionsLifecycleCallback = {};
+  actionsLifecycleCallback[osapi.container.CallbackType.ON_PRELOADED] =
+    preloadCallback;
+  actionsLifecycleCallback[osapi.container.CallbackType.ON_NAVIGATED] =
+    navigatedCallback;
+  actionsLifecycleCallback[osapi.container.CallbackType.ON_CLOSED] =
+    closedCallback;
+  actionsLifecycleCallback[osapi.container.CallbackType.ON_UNLOADED] =
+    unloadedCallback;
+
+  // Function to handle RPC calls from the gadgets side
+  function router(channel, object) {
+    var actionObj = object;
+    switch (channel) {
+    case 'bindAction':
+      bindAction(actionObj);
+      break;
+    case 'removeAction':
+      hideActionHandler(object);
+      break;
+    case 'getActionsByPath':
+      return container_.actions.getActionsByPath(object);
+    case 'getActionsByDataType':
+      return container_.actions.getActionsByDataType(object);
+    }
+  };
+
+  /**
+   * Function that renders actions in the container's UI
+   *
+   * @param {Object}
+   *          actionObj The object with id, label, tooltip, icon and any other
+   *          information for the container to use to render the action.
+   */
+  var showActionHandler = function(actionObj) {};
+
+  /**
+   * Function that hides actions from the container's UI
+   *
+   * @param {Object}
+   *          actionObj The object with id, label, tooltip, icon and any other
+   *          information for the container to use to render the action.
+   */
+  var hideActionHandler = function(actionObj) {};
+
+  /**
+   * Function that renders gadgets in container's UI
+   *
+   * @param {String}
+   *          gadgetSpecUrl The gadget spec url.
+   * @param {Object}
+   *          gadgetMetadata  The gadget meta data.
+   */
+  var renderGadgetInContainer = function(gadgetSpecUrl, gadgetMetadata) {};
+
+  // instantiate the singleton action registry
+  var registry = new ActionRegistry();
+
+  // a map to track actions that are scheduled to run after
+  // pre-loaded gadget has been rendered
+  var pendingActions = {};
+
+  // container instance
+  var container_ = null;
+
+  /**
+   * Add the Container API for the action service.
+   */
+  osapi.container.Container.addMixin('actions', function(container) {
+    container_ = container;
+    gadgets.rpc.register('actions', router);
+
+    if (container.addGadgetLifecycleCallback) {
+      container.addGadgetLifecycleCallback('actions',
+          actionsLifecycleCallback);
+    }
+
+    return /** @scope osapi.container.actions */ {
+
+      /**
+       * Registers a function to display actions in the container.
+       *
+       * @param {function}
+       *          The container's function to render actions
+       *          in its UI. The function takes the action object as
+       *          a parameter.
+       */
+      registerShowActionHandler: function(handler) {
+        if (typeof handler === 'function') {
+          showActionHandler = handler;
+        }
+      },
+
+      /**
+       * Registers a function to hide (remove) actions in the container.
+       *
+       * @param {function}
+       *          The container's function to hide (remove) actions
+       *          in its UI. The function takes the action object as
+       *          a parameter.
+       */
+      registerHideActionHandler: function(handler) {
+        if (typeof handler === 'function') {
+          hideActionHandler = handler;
+        }
+      },
+
+      /**
+       * Registers a function to render gadgets in the container.
+       *
+       * @param {function}
+       *          The container's function to render gadgets in its UI.
+       *          The function takes in two parameters: the gadget spec
+       *          url and the gadget metadata.
+       */
+      registerNavigateGadgetHandler: function(renderGadgetFunction) {
+        if (typeof renderGadgetFunction === 'function') {
+          renderGadgetInContainer = renderGadgetFunction;
+        }
+      },
+
+      /*
+       * Uncomment the below two functions to run full jsunit tests.
+       */
+      //addAction : function(actionObj) { addAction(actionObj); },
+      //removeAction : function(actionId) { removeAction(actionId); },
+
+      /**
+       * Executes the action associated with the action id.
+       *
+       * @param {String}
+       *          The action id.
+       */
+      runAction: function(actionId) {
+        if (registry.getItemById(actionId)) {
+          // if gadget site has not been registered yet
+          // the gadget needs to be rendered
+          var gadgetSite = registry.getGadgetSite(actionId);
+          if (!gadgetSite) {
+            var gadgetUrl = registry.getUrl(actionId);
+            var gadgetMetadata = registry.getGadgetMetadata(gadgetUrl);
+            pendingActions[actionId] = {
+              selection: container_.selection.getSelection()
+            };
+            renderGadgetInContainer(gadgetUrl, gadgetMetadata);
+          } else {
+            runAction(actionId);
+          }
+        }
+      },
+
+      /**
+       * Gets the action object from the registry based on the action id.
+       *
+       * @param {String}
+       *          id The action id.
+       * @return {Object} The action object.
+       */
+      getAction: function(id) {
+        return registry.getItemById(id);
+      },
+
+      /**
+       * Gets all action objects in the registry.
+       *
+       * @return {Array} An array with any action objects in the
+       *         registry.
+       */
+      getAllActions: function() {
+        return registry.getAllActions();
+      },
+
+      /**
+       * Gets action object from registry based on the path.
+       *
+       * @param {String}
+       *          The path for the action.
+       * @return {Array} An array with any action objects in the
+       *         specified path.
+       */
+      getActionsByPath: function(path) {
+        var actions = [];
+        actions = actions.concat(registry.getActionsByPath(path));
+        return actions;
+      },
+
+      /**
+       * Gets action object from registry based on the dataType.
+       *
+       * @param {String}
+       *          The String representation of the Open Social data type.
+       * @return {Array} An array of action objects bound to the specified
+       *         data type.
+       */
+      getActionsByDataType: function(dataType) {
+        var actions = [];
+        actions = actions.concat(registry.getActionsByDataType(dataType));
+        return actions;
+      }
+    };
+  });
+})();

Added: shindig/trunk/features/src/main/javascript/features/actions/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/actions/feature.xml?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/actions/feature.xml (added)
+++ shindig/trunk/features/src/main/javascript/features/actions/feature.xml Thu Jul 14 19:17:36 2011
@@ -0,0 +1,20 @@
+<feature>
+  <name>actions</name>
+  <dependency>globals</dependency>
+  <dependency>rpc</dependency>
+  <dependency>gadgets.json.ext</dependency>
+  <gadget>
+    <script src="actions.js"/>
+    <script src="taming.js"/>
+    <api>
+      <exports type="js">gadgets.actions.addAction</exports>
+      <exports type="js">gadgets.actions.updateAction</exports>
+      <exports type="js">gadgets.actions.removeAction</exports>
+      <exports type="js">gadgets.actions.getActionsByPath</exports>
+      <exports type="js">gadgets.actions.getActionsByDataType</exports>
+    </api>
+  </gadget>
+  <container>
+    <script src="actions_container.js"/>
+  </container>
+</feature>
\ No newline at end of file

Added: shindig/trunk/features/src/main/javascript/features/actions/taming.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/actions/taming.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/actions/taming.js (added)
+++ shindig/trunk/features/src/main/javascript/features/actions/taming.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,8 @@
+var tamings___ = tamings___ || [];
+tamings___.push(function(imports) {
+  ___.grantRead(gadgets.actions, 'addAction');
+  ___.grantRead(gadgets.actions, 'updateAction');
+  ___.grantRead(gadgets.actions, 'removeAction');
+  ___.grantRead(gadgets.actions, 'getActionsByPath');
+  ___.grantRead(gadgets.actions, 'getActionsByDataType');
+});

Modified: shindig/trunk/features/src/main/javascript/features/features.txt
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/features.txt?rev=1146859&r1=1146858&r2=1146859&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/features.txt (original)
+++ shindig/trunk/features/src/main/javascript/features/features.txt Thu Jul 14 19:17:36 2011
@@ -19,6 +19,7 @@
 # List each feature you want to support here
 
 features/globals/feature.xml
+features/actions/feature.xml
 features/analytics/feature.xml
 features/auth-refresh/feature.xml
 features/caja/feature.xml
@@ -77,6 +78,7 @@ features/osml/feature.xml
 features/pubsub/feature.xml
 features/rpc/feature.xml
 features/security-token/feature.xml
+features/selection/feature.xml
 features/setprefs/feature.xml
 features/settitle/feature.xml
 features/shindig.auth/feature.xml

Added: shindig/trunk/features/src/main/javascript/features/selection/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/selection/feature.xml?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/selection/feature.xml (added)
+++ shindig/trunk/features/src/main/javascript/features/selection/feature.xml Thu Jul 14 19:17:36 2011
@@ -0,0 +1,36 @@
+<?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>selection</name>
+  <dependency>globals</dependency>
+  <dependency>rpc</dependency>
+  <gadget>
+    <script src="selection.js"/>
+    <script src="taming.js"/>
+    <api>
+      <exports type="js">gadgets.selection.setSelection</exports>
+      <exports type="js">gadgets.selection.getSelection</exports>
+      <exports type="js">gadgets.selection.addSelectionListener</exports>
+      <exports type="js">gadgets.selection.removeSelectionListener</exports>
+    </api>
+  </gadget>
+  <container>
+    <script src="selection_container.js"/>
+  </container>
+</feature>

Added: shindig/trunk/features/src/main/javascript/features/selection/selection.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/selection/selection.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/selection/selection.js (added)
+++ shindig/trunk/features/src/main/javascript/features/selection/selection.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,86 @@
+/*
+ * 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 Gadget-side library for participating in selection eventing.
+ */
+
+/**
+ * @static
+ * @class Selection class for gadgets.
+ * Provides framework for selection eventing.
+ * @name gadgets.selection
+ */
+gadgets['selection'] = function() {
+
+  var listeners = new Array();
+  var currentSelection = null;
+  var selectionChangedFunc = null;
+
+  return /** @scope gadgets.selection */ {
+    /**
+     * Sets the current selection.
+     * @param {string} selection Selected object.
+     */
+    setSelection: function(selection) {
+      currentSelection = selection;
+      gadgets.rpc.call('..', 'gadgets.selection', null, 'set', selection);
+    },
+
+    /**
+     * Gets the current selection.
+     * @return {Object} the current selection.
+     */
+    getSelection: function() {
+      return currentSelection;
+    },
+
+    /**
+     * Registers a listener for selection.
+     * @param {function} listener The listener to remove.
+     */
+    addSelectionListener: function(listener) {
+      if (typeof listener === 'function') {
+        // add the listener to the list
+        listeners.push(listener);
+        // lazily create and add the callback
+        if (selectionChangedFunc == null) {
+          selectionChangedFunc = function(selection) {
+            currentSelection = selection;
+            listeners.forEach(function(listener) {
+              listener(selection);
+            });
+          };
+          gadgets.rpc.call('..', 'gadgets.selection', null, 'add',
+              selectionChangedFunc);
+        }
+      }
+    },
+
+    /**
+     * Removes a listener for selection.
+     * @param {function} listener The listener to remove.
+     */
+    removeSelectionListener: function(listener) {
+      var index = listeners.indexOf(listener);
+      if (index != -1) {
+        listeners.splice(index, 1);
+      }
+    }
+  };
+}();

Added: shindig/trunk/features/src/main/javascript/features/selection/selection_container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/selection/selection_container.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/selection/selection_container.js (added)
+++ shindig/trunk/features/src/main/javascript/features/selection/selection_container.js Thu Jul 14 19:17:36 2011
@@ -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.
+ */
+
+/**
+ * @fileoverview Container-side selection manager.
+ */
+
+/**
+ * @static
+ * @class Manages selection and selection listeners.
+ * @name gadgets.selectionmanager
+ */
+(function() {
+
+  var listeners = new Array();
+  var _selection;
+
+  function notifySelection(selection) {
+    _selection = selection;
+    listeners.forEach(function(listener) {
+      listener(selection);
+    });
+  }
+
+  function addSelectionListener(listener) {
+    listeners.push(listener);
+  }
+
+  function router(command, param) {
+    switch (command) {
+    case 'set':
+      notifySelection(param);
+      break;
+    case 'add':
+      addSelectionListener(param);
+      break;
+    default:
+      throw new Error('Unknown selection command');
+    }
+  }
+
+  osapi.container.Container.addMixin('selection', function(context) {
+    gadgets.rpc.register('gadgets.selection', router);
+    return /** @scope gadgets.selection */ {
+      /**
+       * Sets the current selection.
+       * @param {string} selection Selected object.
+       */
+      setSelection: function(selection) {
+        notifySelection(selection);
+      },
+      /**
+       * Gets the current selection.
+       * @return {Object} the current selection.
+       */
+      getSelection: function() {
+        return _selection;
+      }
+    };
+  });
+
+})();

Added: shindig/trunk/features/src/main/javascript/features/selection/taming.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/selection/taming.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/selection/taming.js (added)
+++ shindig/trunk/features/src/main/javascript/features/selection/taming.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,7 @@
+var tamings___ = tamings___ || [];
+tamings___.push(function(imports) {
+  ___.grantRead(gadgets.selection, 'setSelection');
+  ___.grantRead(gadgets.selection, 'getSelection');
+  ___.grantRead(gadgets.selection, 'addSelectionListener');
+  ___.grantRead(gadgets.selection, 'removeSelectionListener');
+});

Added: shindig/trunk/features/src/test/javascript/features/actions/actions_test.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/actions/actions_test.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/actions/actions_test.js (added)
+++ shindig/trunk/features/src/test/javascript/features/actions/actions_test.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,224 @@
+/*
+ * 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 Tests for actions feature
+ */
+
+function DeclarativeActionsTest(name) {
+  TestCase.call(this, name);
+}
+
+DeclarativeActionsTest.inherits(TestCase);
+
+(function() {
+  
+DeclarativeActionsTest.prototype.setUp = function() {
+    this.apiUri = window.__API_URI;
+    window.__API_URI = shindig.uri('http://shindig.com');
+    this.containerUri = window.__CONTAINER_URI;
+    window.__CONTAINER_URI = shindig.uri('http://container.com');
+
+    this.gadgetsRpc = gadgets.rpc;
+    var that = this;
+    gadgets.rpc = {};
+    gadgets.rpc.register = function() {};
+    gadgets.rpc.call = function() {
+      that.rpcArguments = Array.prototype.slice.call(arguments);
+    };
+};
+
+DeclarativeActionsTest.prototype.tearDown = function() {
+    window.__API_URI = this.apiUri;
+    window.__CONTAINER_URI = this.containerUri;
+    
+    gadgets.rpc = this.gadgetsRpc;
+    this.rpcArguments = undefined; 
+};
+
+DeclarativeActionsTest.prototype.testGadgetsAddAction = function() {
+  var actionId = "testAction";
+  var callbackFn = function(){};
+  var _actionObj = {
+      id: actionId,
+      label:"Test Action",
+      path:"container/navigationLinks",
+      callback: callbackFn
+    };
+  gadgets.actions.addAction(_actionObj);
+  this.assertRpcCalled('..', 'actions', null, 'bindAction', _actionObj);
+};
+
+DeclarativeActionsTest.prototype.testGadgetsRemoveAction = function() {
+  var actionId = "testAction";
+  gadgets.actions.removeAction(actionId);
+  this.assertRpcCalled('..', 'actions', null, 
+      'removeAction', actionId);
+};
+
+
+DeclarativeActionsTest.prototype.testContainerGetAction = function() {
+  var container = new osapi.container.Container({});
+  var actionId = "testAction";
+  var actionObj = container.actions.getAction(actionId);
+  // registry is empty
+  this.assertUndefined(actionObj);
+};
+
+
+DeclarativeActionsTest.prototype.testContainerGetActionsByPath = function() {
+  var container = new osapi.container.Container();
+  var actionId = "testAction";
+  var actionsArray = container.actions
+    .getActionsByPath("container/navigationLinks");
+  //registry is empty
+  this.assertEquals(actionsArray, []);
+};
+
+DeclarativeActionsTest.prototype.testContainerGetActionsByDataType = 
+  function(){
+    var container = new osapi.container.Container();
+    var actionId = "testAction";
+    var actionsArray = container.actions
+      .getActionsByDataType("opensocial.Person");
+    //registry is empty
+    this.assertEquals(actionsArray, []);
+  };
+
+/**
+ * Uncomment following _Full tests once addAction() and removeAction() 
+ * functions in actions_container.js are uncommented
+ */
+/* FULL TESTS
+DeclarativeActionsTest.prototype.testContainerGetAction_Full = function() {
+  var container = new osapi.container.Container({});
+  var actionId = "testAction";
+  var actionObj_ = {
+          id: actionId,
+          label: "Test Action",
+          path: "container/navigationLinks"
+  }
+  container.actions.addAction(actionObj_);
+  var actionObj = container.actions.getAction(actionId);
+  this.assertEquals(actionObj_, actionObj);
+  
+  container.actions.removeAction(actionId);
+  actionObj = container.actions.getAction(actionId);
+  this.assertUndefined(actionObj);
+
+};
+
+DeclarativeActionsTest.prototype.testContainerGetActions_Full = function() {
+  var container = new osapi.container.Container({});
+  var actionId = "testAction";
+  var actions = [{
+    id: "test1",
+    label: "Test Action1",
+    path: "container/navigationLinks"
+  },
+  {
+    id: "test2",
+    label: "Test Action2",
+    path: "container/navigationLinks"
+  },
+  {
+    id: "test3",
+    label: "Test Action3",
+    dataType: "opensocial.Person"
+  },
+  {
+    id: "test4",
+    label: "Test Action4",
+    dataType: "opensocial.Person"
+  }
+  ];
+  for (actionIndex in actions) {
+    container.actions.addAction(actions[actionIndex]);
+  }
+  
+  var allActions = container.actions.getAllActions();
+  this.assertEquals(actions, allActions);
+  
+  for (actionIndex in actions) {
+    container.actions.removeAction(actions[actionIndex].id);
+  }
+  
+  allActions = container.actions.getAllActions();
+  this.assertEquals([], allActions);
+
+};
+DeclarativeActionsTest.prototype.testContainerGetActionsByPath_Full = 
+  function(){
+    var container = new osapi.container.Container();
+    var actionId = "testAction";
+    var actionObj_ = {
+            id: actionId,
+            label: "Test Action",
+            path: "container/navigationLinks"
+    }
+    container.actions.addAction(actionObj_);
+    var actionsArray = container.actions
+      .getActionsByPath("container/navigationLinks");
+    this.assertEquals(actionsArray, [ actionObj_ ]);
+    
+    container.actions.removeAction(actionId);
+    actionsArray = container.actions
+      .getActionsByPath("container/navigationLinks");
+    this.assertEquals(actionsArray, []);
+  };
+
+DeclarativeActionsTest.prototype.testContainerGetActionsByDataType_Full = 
+  function() {
+    var container = new osapi.container.Container();
+    var actionId = "testAction";
+    var actionObj_ = {
+            id: actionId,
+            label: "Test Action",
+            dataType: "opensocial.Person"
+    };
+    
+    container.actions.addAction(actionObj_);
+    var actionsArray = container.actions
+      .getActionsByDataType("opensocial.Person");
+    this.assertEquals([ actionObj_ ], actionsArray);
+    
+    container.actions.removeAction(actionId);
+    actionsArray = container.actions
+      .getActionsByDataType("opensocial.Person");
+    this.assertEquals([], actionsArray);
+  
+  };
+
+ FULL TESTS */
+
+/**
+ * Asserts gadgets.rpc.call() is called with the expected arguments given.
+ */
+DeclarativeActionsTest.prototype.assertRpcCalled = function() {
+  this.assertNotUndefined("RPC was not called.", this.rpcArguments);
+  this.assertEquals("RPC argument list not valid length.", arguments.length,
+      this.rpcArguments.length);
+
+  for ( var i = 0; i < arguments.length; i++) {
+    this.assertEquals(arguments[i], this.rpcArguments[i]);
+  }
+  this.rpcArguments = undefined;
+};
+
+})();
\ No newline at end of file

Added: shindig/trunk/features/src/test/javascript/features/selection/selection_test.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/selection/selection_test.js?rev=1146859&view=auto
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/selection/selection_test.js (added)
+++ shindig/trunk/features/src/test/javascript/features/selection/selection_test.js Thu Jul 14 19:17:36 2011
@@ -0,0 +1,117 @@
+/*
+ * 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 Tests for selection
+ */
+
+function SelectionTest(name) {
+  TestCase.call(this, name);
+}
+
+SelectionTest.inherits(TestCase);
+
+(function() {
+
+  SelectionTest.prototype.setUp = function() {
+    this.apiUri = window.__API_URI;
+    window.__API_URI = shindig.uri('http://shindig.com');
+    this.containerUri = window.__CONTAINER_URI;
+    window.__CONTAINER_URI = shindig.uri('http://container.com');
+    this.shindigContainerGadgetSite = osapi.container.GadgetSite;
+
+    this.gadgetsRpc = gadgets.rpc;
+    var that = this;
+    gadgets.rpc = {};
+    gadgets.rpc.register = function() {
+    };
+    gadgets.rpc.call = function() {
+      that.rpcArguments = Array.prototype.slice.call(arguments);
+    };
+  };
+
+  SelectionTest.prototype.tearDown = function() {
+    window.__API_URI = this.apiUri;
+    window.__CONTAINER_URI = this.containerUri;
+    osapi.container.GadgetSite = this.shindigContainerGadgetSite;
+    gadgets.rpc = this.gadgetsRpc;
+    this.rpcArguments = undefined;
+  };
+
+  SelectionTest.prototype.testContainerSetGetSelection = function() {
+    var container = new osapi.container.Container({});
+    var _token = "hello";
+    container.selection.setSelection(_token);
+    var token = container.selection.getSelection();
+    this.assertEquals(_token, token);
+  };
+
+  SelectionTest.prototype.testGadgetSetGetSelection = function() {
+    var container = new osapi.container.Container({});
+    var token = "hello";
+    gadgets.selection.setSelection(token);
+    this.assertRpcCalled("..", "gadgets.selection", null, "set", token);
+  };
+
+  SelectionTest.prototype.testGadgetGetSelection = function() {
+    var container = new osapi.container.Container({});
+    var token = "hello";
+    gadgets.selection.setSelection(token);
+    var _token = gadgets.selection.getSelection();
+    this.assertEquals(token, _token);
+  };
+
+  SelectionTest.prototype.testGadgetAddSelectionListener = function() {
+    var container = new osapi.container.Container({});
+    var callback = function() {
+    };
+    gadgets.selection.addSelectionListener(callback);
+    this.assertRpcCalled("..", "gadgets.selection", null, "add", callback);
+  };
+
+  /**
+   * Asserts gadgets.rpc.call() is called with the expected arguments given.
+   * Note that it resets this.rpcArguments for next RPC call assertion.
+   */
+  SelectionTest.prototype.assertRpcCalled = function() {
+    this.assertNotUndefined("RPC was not called.", this.rpcArguments);
+    this.assertEquals("RPC argument list not valid length.", arguments.length,
+        this.rpcArguments.length);
+
+    for ( var i = 0; i < arguments.length; i++) {
+      this.assertEquals(arguments[i], this.rpcArguments[i]);
+    }
+    this.resetRpc();
+  };
+
+  /**
+   * Resets this.rpcArguments.
+   */
+  SelectionTest.prototype.resetRpc = function() {
+    this.rpcArguments = undefined;
+  };
+
+  /**
+   * Asserts that no gadgets.rpc.call() is called.
+   */
+  SelectionTest.prototype.assertNoRpcCalled = function() {
+    this.assertUndefined("RPC was called.", this.rpcArguments);
+  };
+
+})();
\ No newline at end of file



Mime
View raw message