You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by li...@apache.org on 2011/07/21 20:02:27 UTC

svn commit: r1149287 [1/2] - in /shindig/trunk: content/samplecontainer/examples/commoncontainer/ content/samplecontainer/examples/embeddedexperiences/ content/samplecontainer/examples/media-openGadgets/ features/ features/src/main/javascript/features/...

Author: lindner
Date: Thu Jul 21 18:02:23 2011
New Revision: 1149287

URL: http://svn.apache.org/viewvc?rev=1149287&view=rev
Log:
Patch from Jason Chiang | View Enhancements To Open Gadgets and URLs

Added:
    shindig/trunk/content/samplecontainer/examples/commoncontainer/cconviews.js
    shindig/trunk/content/samplecontainer/examples/media-openGadgets/
    shindig/trunk/content/samplecontainer/examples/media-openGadgets/Media.xml
    shindig/trunk/content/samplecontainer/examples/media-openGadgets/MediaUIOpenGadgets.js
    shindig/trunk/content/samplecontainer/examples/media-openGadgets/Social.js
    shindig/trunk/content/samplecontainer/examples/media-openGadgets/styles.css
    shindig/trunk/features/src/main/javascript/features/open-views/
    shindig/trunk/features/src/main/javascript/features/open-views/feature.xml
    shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements-container.js
    shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements.js
    shindig/trunk/features/src/test/javascript/features/open-views/
    shindig/trunk/features/src/test/javascript/features/open-views/viewEnhancements-test.js
Modified:
    shindig/trunk/content/samplecontainer/examples/commoncontainer/gadgetCollections.json
    shindig/trunk/content/samplecontainer/examples/commoncontainer/index.html
    shindig/trunk/content/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml
    shindig/trunk/content/samplecontainer/examples/embeddedexperiences/PhotoList.xml
    shindig/trunk/content/samplecontainer/examples/embeddedexperiences/index.html
    shindig/trunk/features/pom.xml
    shindig/trunk/features/src/main/javascript/features/features.txt

Added: shindig/trunk/content/samplecontainer/examples/commoncontainer/cconviews.js
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/commoncontainer/cconviews.js?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/commoncontainer/cconviews.js (added)
+++ shindig/trunk/content/samplecontainer/examples/commoncontainer/cconviews.js Thu Jul 21 18:02:23 2011
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+/**
+ * overview Container implementation of view enhancement.
+ */
+
+CommonContainer['views'] = CommonContainer['views'] || {};
+
+/**
+ * Method will be called to create the DOM element to place the Gadget Site in.
+ *
+ * @param {object} gadgetMetadata
+ *          gadget meta data for the gadget being opened in
+ *          this GadgetSite.
+ * @param {string=} opt_view
+ *          Optional parameter, the view that indicates the type of
+ *          GadgetSite.
+ * @param {string=} opt_viewTarget
+ *          Optional parameter, the view target indicates where
+ *          to open the gadget.
+ * @return {object} The DOM element to place the GadgetSite in.
+ */
+
+CommonContainer.views.createElementForGadget = function(gadgetMetadata, 
+        opt_view, opt_viewTarget) {
+  var surfaceView = 'default';
+  var viewTarget = 'default';
+
+  if (opt_view !== undefined) {
+    surfaceView = opt_view;
+  }
+  if (opt_viewTarget !== undefined) {
+    viewTarget = opt_viewTarget;
+  }
+
+  switch (viewTarget) {
+    case 'tab':
+      return openInNewTab(gadgetMetadata);
+      break;
+    case 'dialog':
+      return openInDialog(false, surfaceView, true, gadgetMetadata);
+      break;
+    case 'modalDialog':
+      return openInDialog(true, surfaceView, true, gadgetMetadata);
+      break;
+    default:
+      return openInDialog(false, surfaceView, true, gadgetMetadata);
+  }
+};
+
+
+/**
+ * Method will be called to create the DOM element to place the UrlSite in.
+ *
+ * @param {string=} opt_viewTarget
+ *          Optional parameter, the view target to open. If not
+ *          included the container should use its default view.
+ * @return {Object} The DOM element to place the UrlSite object in.
+ */
+CommonContainer.views.createElementForUrl = function(opt_viewTarget) {
+  var viewTarget = 'dialog';
+
+  if (opt_viewTarget !== undefined) {
+    viewTarget = opt_viewTarget;
+  }
+
+  switch (viewTarget) {
+    case 'tab':
+      return openInNewTab();
+      break;
+    case 'dialog':
+      return openInDialog(false, 'canvas', false);
+      break;
+    case 'modalDialog':
+      return openInDialog(true, 'canvas', false);
+      break;
+    default:
+      return openInDialog(false, 'canvas', false);
+  }
+};
+
+
+/**
+ * Method will be called when a gadget wants to close itself or the parent
+ * gadget wants to close a gadget or url site it has opened.
+ *
+ * @param {object=} site
+ *          The id of the site to close.
+ */
+CommonContainer.views.destroyElement = function(site) {
+  closeDialog(site);
+};
+
+var dialog_counter = 0;
+var tab_counter = 2;
+var newTabId;
+var $tabs;
+
+
+/**
+ * private method will be called to create the dialog DOM element.
+ * @private
+ * @param {boolean} modaldialog
+ *          true for modal dialog.
+ * @param {string} view
+ *          view type.
+ * @param {boolean} isGadget
+ *          true for gadget, false for url.
+ * @param {string} opt_gadgetMetadata
+ *          gadget metadate.
+ * @return {Object} The DOM element to place the gadget or url site object in.
+ */
+function openInDialog(modaldialog, view, isGadget, opt_gadgetMetadata) {
+
+  var dialog_width = 450; // default width
+  if (view == 'canvas') {
+    dialog_width = 675; // width for canvas
+  }
+
+  dialog_counter++;
+  var dialog = document.createElement('div');
+  dialog.id = 'dialog_' + dialog_counter;
+
+  if (opt_gadgetMetadata !== undefined) {
+    // open gadget, get the title from gadgetMetadata
+    dialog.title = opt_gadgetMetadata['modulePrefs'].title;
+  }
+
+  document.getElementById('content').appendChild(dialog);
+  if (isGadget) {
+    // use jquery to create the dialog
+    $('#dialog_' + dialog_counter).dialog({
+      resizable: false,
+      width: dialog_width, // height will be auto
+      modal: modaldialog, // set modal: true or false
+      close: function(ev, ui) {
+        var iframeid = document.getElementById('dialog_' + dialog_counter).
+            getElementsByTagName('iframe')[0].id;
+        var site = CommonContainer.getGadgetSiteByIframeId_(iframeid);
+        CommonContainer.closeGadget(site);
+        $(this).remove();
+      }
+    });
+    return dialog;
+  } else {
+    var dialog_height = 350; // default height
+    if (view == 'canvas') {
+      dialog_height = 530; // height for canvas
+    }
+    $('#dialog_' + dialog_counter).dialog({
+      resizable: false,
+      width: dialog_width,
+      height: dialog_height,
+      modal: modaldialog, // set modal: true or false
+      close: function(ev, ui) {
+        $(this).remove();
+      }
+    });
+    // Maybe an issue in jquery, we need to create another div, otherwise it
+    // will show vertical scroll bar
+    var dialog_content = document.createElement('div');
+    dialog_content.style.height = '100%';
+    dialog_content.style.width = '100%';
+    dialog.appendChild(dialog_content);
+    return dialog_content;
+  }
+}
+
+
+/**
+ * private method will be called to create the tab DOM element.
+ * @private
+ * @param {string} gadgetMetadata
+ *          gadget metadate.
+ * @return {Object} The DOM element to place the gadget or url site object in.
+ */
+function openInNewTab(gadgetMetadata) {
+
+  var tabsNode;
+
+  if (!document.getElementById('tabs')) {
+    // add the new tab the first time, will create the default tab for the
+    // current content, and add a new tab
+    var controlPanelNode = document.getElementById('controlPanel');
+    var testAreaNode = document.getElementById('testArea');
+    var contentNode = document.getElementById('content');
+
+    contentNode.removeChild(controlPanelNode);
+    contentNode.removeChild(testAreaNode);
+
+    tabsNode = document.createElement('div');
+    tabsNode.id = 'tabs';
+
+    tabsNode.innerHTML = "<ul><li><a href='#tabs-1'>Default</a></li></ul>";
+
+    var tabs_1_Node = document.createElement('div');
+    tabs_1_Node.id = 'tabs-1';
+
+    // put the default content into the tabs-1
+    tabs_1_Node.appendChild(controlPanelNode);
+    tabs_1_Node.appendChild(testAreaNode);
+
+    tabsNode.appendChild(tabs_1_Node);
+    contentNode.appendChild(tabsNode);
+    newTabId = 'tabs-2';
+
+    // use jquery to create new tab
+    $tabs = $('#tabs')
+    .tabs(
+            {
+              tabTemplate: "<li><a href='#{href}'>#{label}</a>" +
+              "<span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
+              add: function(event, ui) {
+                var tab_content_id = 'tab_content' + tab_counter;
+                var tab_content = document.createElement('div');
+                tab_content.id = tab_content_id;
+                // tab_content.className = "column";
+
+                tab_content.style.height = '1000px';
+                tab_content.style.width = '100%';
+
+                $(ui.panel).append(tab_content);
+
+                // set the focus to the new tab
+                $('#tabs').tabs('select', '#' + newTabId);
+              },
+
+          remove: function(event, ui) {
+            // If there is gadget inside, close the gadget
+            var iframes = $(this).parent().get(0)
+            .getElementsByTagName('iframe');
+            if (iframes.lenth > 0 && iframes[0].id !== undefined) {
+              var site = CommonContainer
+              .getGadgetSiteByIframeId_(iframes[0].id);
+              CommonContainer.closeGadget(site);
+            }
+          }
+            });
+
+    // close icon: removing the tab on click
+    // note: closable tabs gonna be an option in the future - see
+    // http://dev.jqueryui.com/ticket/3924
+    $('#tabs span.ui-icon-close').live('click', function() {
+      var index = $('li', $tabs).index($(this).parent());
+
+      $tabs.tabs('remove', index);
+      if ($tabs.tabs('length') < 2) {
+
+        controlPanelNode = document.getElementById('controlPanel');
+        testAreaNode = document.getElementById('testArea');
+        contentNode = document.getElementById('content');
+        tabsNode = document.getElementById('tabs');
+
+        tabs_1_Node = document.getElementById('tabs-1');
+
+        tabs_1_Node.removeChild(controlPanelNode);
+        tabs_1_Node.removeChild(testAreaNode);
+
+        contentNode.removeChild(tabsNode);
+
+        tab_counter = 2;
+        newTabId = null;
+        $tabs = null;
+
+        contentNode.appendChild(controlPanelNode);
+        contentNode.appendChild(testAreaNode);
+
+      }
+
+    });
+  }
+
+  newTabId = 'tabs-' + tab_counter;
+  var tab_content_id = 'tab_content' + tab_counter;
+
+  // add new tab with new id and new title
+  var tab_title = 'new tab ';
+  if (gadgetMetadata !== undefined &&
+          gadgetMetadata['modulePrefs'].title !== undefined) {
+    // open gadget, get the title from gadgetMetadata
+    tab_title = gadgetMetadata['modulePrefs'].title;
+    if (tab_title.length > 7) {
+      tab_title = tab_title.substring(0, 6) + '...';
+    }
+  }
+  $tabs.tabs('add', '#tabs-' + tab_counter, tab_title);
+
+  if (gadgetMetadata !== undefined) {
+    // rendering gadget's header
+    var gadgetSiteId = 'gadget-site-' + newTabId;
+    var gadgetTemplate = '<div class="portlet">' +
+        '<div class="portlet-header">sample to replace</div>' +
+        '<div id=' + gadgetSiteId +
+        ' class="portlet-content"></div>' + '</div>';
+
+    $(gadgetTemplate).appendTo($('#' + tab_content_id)).addClass(
+        'ui-widget ui-widget-content ui-helper-clearfix ui-corner-all')
+    .find('.portlet-header')
+    .addClass('ui-widget-header ui-corner-all')
+    .text(gadgetMetadata['modulePrefs'].title)
+    .append('<span id="remove" class="ui-icon ui-icon-closethick"></span>');
+  }
+
+  tab_counter++;
+
+  // return the div
+  if (gadgetMetadata !== undefined) {
+    return document.getElementById('gadget-site-' + newTabId);
+  } else {
+    return document.getElementById(tab_content_id);
+  }
+}
+
+
+/**
+ * private method will be called to destroy dialog object.
+ * @private
+ * @param {object} site
+ *          gadget site.
+ */
+function closeDialog(site) {
+  // this is the site id, we also need to find the dojo dialog widget id
+  var iframeId;
+  var widgetId = 'dialog_' + dialog_counter; //default
+
+  if (site && site.getActiveGadgetHolder()) {
+    // get iframe id
+    iframeId = site.getActiveGadgetHolder().getIframeId();
+    if (iframeId !== undefined) {
+      var iframeNode = document.getElementById(iframeId);
+      // get dialog widget id
+      widgetId = iframeNode.parentNode.id;
+    }
+  }
+  // close the gadget
+  CommonContainer.closeGadget(site);
+
+  // close the widget
+  $('#' + widgetId).dialog('close');
+
+}

Modified: shindig/trunk/content/samplecontainer/examples/commoncontainer/gadgetCollections.json
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/commoncontainer/gadgetCollections.json?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/commoncontainer/gadgetCollections.json (original)
+++ shindig/trunk/content/samplecontainer/examples/commoncontainer/gadgetCollections.json Thu Jul 21 18:02:23 2011
@@ -31,6 +31,13 @@
                       ]
              },
              {
+             "name": "Sample Media Items Gadget with openGadget API",
+             "Description": "Sample gadget used in the view enhancements sample",
+             "apps" : [
+                     {"name": "publisher", "url": "/samplecontainer/examples/media-openGadgets/Media.xml"}
+                     ]
+             },
+             {
              "name": "OpenSearch Demo",
              "Description": "Sample gadgets used for opensearch",
              "apps" : [

Modified: shindig/trunk/content/samplecontainer/examples/commoncontainer/index.html
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/commoncontainer/index.html?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/commoncontainer/index.html (original)
+++ shindig/trunk/content/samplecontainer/examples/commoncontainer/index.html Thu Jul 21 18:02:23 2011
@@ -23,7 +23,7 @@
 	<!-- My OpenSocial Beginnings -->
 	<link rel="stylesheet" href="../../../container/gadgets.css">
 	<script type="text/javascript"
-		src="../../../gadgets/js/container:views:opensearch:rpc:pubsub-2.js?c=1&debug=1&container=default""></script>
+		src="../../../gadgets/js/container:open-views:opensearch:rpc:pubsub-2.js?c=1&debug=1&container=default""></script>
 	<script
 		src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
 	<script
@@ -165,7 +165,7 @@ Enter the event payload
 <div id="results"></div>
 </div>
 </div>
-</div>
+
 <span class="ui-icon ui-icon-grip-dotted-horizontal"
 	style="margin: 2px auto;"></span></div>
 
@@ -183,6 +183,6 @@ Enter the event payload
 <span class="ui-icon ui-icon-grip-dotted-horizontal"
 	style="margin: 2px auto;"></span></div>
 </div>
-
+</div>
 </body>
 </html>

Modified: shindig/trunk/content/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml (original)
+++ shindig/trunk/content/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml Thu Jul 21 18:02:23 2011
@@ -21,7 +21,7 @@
 	<ModulePrefs title="Photo Album Viewer" description="View Photos From An Album" height="400" width="650">
 		<Require feature="embedded-experiences"></Require>
         <Require feature="dynamic-height"></Require>
-        <Require feature="views"></Require>
+        <Require feature="open-views"></Require>
 	</ModulePrefs>
 	<Content type="html" view="embedded, default">
    <![CDATA[
@@ -50,8 +50,8 @@
 			clear: both;
 		} 	
      </style>
-
-
+     
+ 
      <script type="text/javascript">     	
      	function initAlbum(){
      		opensocial.data.getDataContext().registerListener('org.opensocial.ee.context', function(key){
@@ -72,7 +72,7 @@
      	
      	gadgets.util.registerOnLoadHandler(initAlbum);
    	 </script>
-
+	 
 	 <div id="wrapper">
 	 	<div id="header"></div>
 	 	<div id="album"></div>

Modified: shindig/trunk/content/samplecontainer/examples/embeddedexperiences/PhotoList.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/embeddedexperiences/PhotoList.xml?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/embeddedexperiences/PhotoList.xml (original)
+++ shindig/trunk/content/samplecontainer/examples/embeddedexperiences/PhotoList.xml Thu Jul 21 18:02:23 2011
@@ -20,7 +20,7 @@
 <Module>
     <ModulePrefs title="Photo List" description="View Photos From An Album" height="400" width="650">
         <Require feature="embedded-experiences"></Require>
-        <Require feature="views"></Require>
+        <Require feature="open-views"></Require>
     </ModulePrefs>
     <Content type="html" view="embedded, default">
    <![CDATA[
@@ -34,25 +34,27 @@
             padding: 2px 2px 2px 2px;
             border: 1px solid black;
         }
-
+        
         #wrapper{
             border: 1px solid black;
         }
-
+        
         #header{
             font-size: 120%;
             padding: 10px 10px 10px 10px;
             color: #0F67A1;
-        }
-
+        }         
+        
         .clear{
             clear: both;
-        }
+        }     
      </style>
-
-     <script type="text/javascript">
-
+     
+ 
+     <script type="text/javascript"> 
+     
          var currentSite;
+                 
          function initAlbum(){
              opensocial.data.getDataContext().registerListener('org.opensocial.ee.context', function(key){
                  createAlbumHTML(opensocial.data.getDataContext().getDataSet(key));
@@ -70,24 +72,24 @@
              document.getElementById('header').innerHTML = context.albumName;
              document.getElementById("album").innerHTML = result;
          };
-
+         
          gadgets.util.registerOnLoadHandler(initAlbum);
-
+         
          function showPreviewPhoto(index) {
-
+                  
             var eeDataModel = {
                 'gadget' : 'http://localhost:8080/samplecontainer/examples/embeddedexperiences/AlbumViewer.xml',
-                'context' : {
-                  "albumName": context.albumName,
+                'context' : {              
+                  "albumName": context.albumName, 
                   "photoUrls": [context.photoUrls[index]]
               }
             };
-
-            var navigateCallback = function(site, metadata){currentSite = site; console.log("Nagivate callback");};
+            
+            var navigateCallback = function(site, metadata){currentSite = site; console.log("Nagivate callback");};  
             var returnCallback = function(returnValue){console.log("Return Value: " + returnValue);};
             gadgets.views.openEmbeddedExperience(returnCallback, navigateCallback, eeDataModel, {'viewTarget' : 'preview'});
          };
-
+         
          function closePreview(){
             if(currentSite != null){
                 gadgets.views.close(currentSite);
@@ -95,7 +97,7 @@
             return false;
          };
         </script>
-
+     
      <div id="wrapper">
          <div id="header"></div>
          <div id="album"></div>

Modified: shindig/trunk/content/samplecontainer/examples/embeddedexperiences/index.html
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/embeddedexperiences/index.html?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/embeddedexperiences/index.html (original)
+++ shindig/trunk/content/samplecontainer/examples/embeddedexperiences/index.html Thu Jul 21 18:02:23 2011
@@ -45,7 +45,7 @@
 		</style>
 		<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
         <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
-        <script type="text/javascript" src="/gadgets/js/core:container:rpc:views.js?c=1&debug=1&container=default"></script>
+        <script type="text/javascript" src="/gadgets/js/core:container:rpc:open-views.js?c=1&debug=1&container=default"></script>
         <script type="text/javascript" src="EEContainer.js"></script>
     </head>
     <body style="font-size:62.5%;">

Added: shindig/trunk/content/samplecontainer/examples/media-openGadgets/Media.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/media-openGadgets/Media.xml?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/media-openGadgets/Media.xml (added)
+++ shindig/trunk/content/samplecontainer/examples/media-openGadgets/Media.xml Thu Jul 21 18:02:23 2011
@@ -0,0 +1,184 @@
+<?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="Albums and MediaItems">
+        <Require feature="osapi" />
+        <Require feature="dynamic-height" />
+        <Require feature="open-views" />
+    </ModulePrefs>
+
+    <Content type="html" view="default"><![CDATA[
+        <html>
+            <head>
+                <!-- Source imports --><script src='http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js' type='text/javascript' djConfig='parseOnLoad:true, isDebug:true'></script>
+                <script src='Social.js' type='text/javascript'></script>
+                <script src='MediaUIOpenGadgets.js' type='text/javascript'></script>
+                
+                <!-- Styling -->
+                <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/tundra/tundra.css"></link>
+                <link rel="stylesheet" type="text/css" href="styles.css">
+                
+                <!-- DOJO requires -->
+                <script type='text/javascript'>
+                    dojo.require('dijit.form.Button');
+                    dojo.require('dijit.form.Form');
+                    dojo.require('dijit.form.TextBox');
+                    dojo.require('dijit.form.ValidationTextBox');
+                    dojo.require('dijit.Dialog');
+                    dojo.require('dijit.form.Textarea');
+                    dojo.require('dijit.layout.ContentPane');
+                    dojo.require('dijit.layout.TabContainer');
+                </script>
+                
+                <!-- JavaScript -->
+                <script type="text/javascript"> 
+                <!-- Entry point to the gadget -->
+                    function init() {
+                        new MediaUI(new SocialWrapper()).init();
+                    }
+                
+                <!-- Register entry point -->
+                    gadgets.util.registerOnLoadHandler(function() {
+                        dojo.addOnLoad(init);
+                    });
+                </script>
+            </head><body class="tundra"></body>
+        </html>
+    ]]></Content>
+
+    <Content type="html" view="albumFullView"><![CDATA[
+        <html>
+            <head>
+                <!-- Source imports -->
+                <script src='http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js' type='text/javascript' djConfig='parseOnLoad:true, isDebug:true'></script>
+                <script src='Social.js' type='text/javascript'></script>
+                <script src='MediaUIOpenGadgets.js' type='text/javascript'></script>
+                
+                <!-- Styling -->
+                <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/tundra/tundra.css"></link>
+                <link rel="stylesheet" type="text/css" href="styles.css">
+                
+                <!-- DOJO requires -->
+                <script type='text/javascript'>
+                    dojo.require('dijit.form.Button');
+                    dojo.require('dijit.form.Form');
+                    dojo.require('dijit.form.TextBox');
+                    dojo.require('dijit.form.ValidationTextBox');
+                    dojo.require('dijit.Dialog');
+                    dojo.require('dijit.form.Textarea');
+                    dojo.require('dijit.layout.ContentPane');
+                    dojo.require('dijit.layout.TabContainer');
+                </script>
+                
+                <!-- JavaScript -->
+                <script type="text/javascript"> 
+                <!-- Entry point to the gadget -->
+                    function init() {
+                        var params = gadgets.views.getParams();
+                        var m = new MediaUI(new SocialWrapper());
+                        m.openAlbum(params['viewerId'],params['data']);
+                    }
+                
+                <!-- Register entry point -->
+                    gadgets.util.registerOnLoadHandler(function() {
+                        dojo.addOnLoad(init);
+                    });
+                </script>
+            </head><body class="tundra"></body>
+        </html>
+    ]]></Content>
+
+    <Content type="html" view="editAlbum"><![CDATA[
+        <html>
+            <head>
+                <!-- Source imports -->
+                <script src='http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js' type='text/javascript' djConfig='parseOnLoad:true, isDebug:true'></script>
+                <script src='Social.js' type='text/javascript'></script>
+                <script src='MediaUIOpenGadgets.js' type='text/javascript'></script>
+                
+                <!-- Styling -->
+                <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/tundra/tundra.css"></link>
+                <link rel="stylesheet" type="text/css" href="styles.css">
+                
+                <!-- DOJO requires -->
+                <script type='text/javascript'>
+                    dojo.require('dijit.form.Button');
+                    dojo.require('dijit.form.Form');
+                    dojo.require('dijit.form.TextBox');
+                    dojo.require('dijit.form.ValidationTextBox');
+                    dojo.require('dijit.Dialog');
+                    dojo.require('dijit.form.Textarea');
+                    dojo.require('dijit.layout.ContentPane');
+                    dojo.require('dijit.layout.TabContainer');
+                </script>
+                
+                <!-- JavaScript -->
+                <script type="text/javascript"> 
+                <!-- Entry point to the gadget -->
+                    function init() {
+                        var params = gadgets.views.getParams();
+                        var data = params['data'];
+                        new MediaUI(new SocialWrapper()).editAlbum(data);
+                    }
+                    
+                <!-- Register entry point -->
+                    gadgets.util.registerOnLoadHandler(function() {
+                        dojo.addOnLoad(init);
+                    });
+                </script>
+            </head><body class="tundra"></body>
+        </html>
+    ]]></Content>
+
+    <Content type="html" view="editMediaItem"><![CDATA[
+        <html>
+            <head>
+                <!-- Source imports -->
+                <script src='http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js' type='text/javascript' djConfig='parseOnLoad:true, isDebug:true'></script>
+                <script src='Social.js' type='text/javascript'></script>
+                <script src='MediaUIOpenGadgets.js' type='text/javascript'></script>
+                
+                <!-- Styling -->
+                <link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/tundra/tundra.css"></link>
+                <link rel="stylesheet" type="text/css" href="styles.css">
+                
+                <!-- DOJO requires -->
+                <script type='text/javascript'>
+                    dojo.require('dijit.form.Button');
+                    dojo.require('dijit.form.Form');
+                    dojo.require('dijit.form.TextBox');
+                    dojo.require('dijit.form.ValidationTextBox');
+                    dojo.require('dijit.Dialog');
+                    dojo.require('dijit.form.Textarea');
+                    dojo.require('dijit.layout.ContentPane');
+                    dojo.require('dijit.layout.TabContainer');
+                </script>
+                
+                <!-- JavaScript -->
+                <script type="text/javascript"> 
+                <!-- Entry point to the gadget -->
+                    function init() {
+                        var params = gadgets.views.getParams();
+                        new MediaUI(new SocialWrapper()).editMediaItem(params['data'].album,params['data'].mediaItem);
+                    }
+                
+                <!-- Register entry point -->
+                    gadgets.util.registerOnLoadHandler(function() {
+                        dojo.addOnLoad(init);
+                    });
+                </script>
+            </head><body class="tundra"></body>
+        </html>
+    ]]></Content>
+
+</Module>
\ No newline at end of file

Added: shindig/trunk/content/samplecontainer/examples/media-openGadgets/MediaUIOpenGadgets.js
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/media-openGadgets/MediaUIOpenGadgets.js?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/media-openGadgets/MediaUIOpenGadgets.js (added)
+++ shindig/trunk/content/samplecontainer/examples/media-openGadgets/MediaUIOpenGadgets.js Thu Jul 21 18:02:23 2011
@@ -0,0 +1,645 @@
+/*
+ * 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.
+ */
+
+/*
+ * The User Interface for the Albums & MediaItems gadget.
+ *
+ * SHINDIG TODOS set ownerId automatically? delete children mediaitems when
+ * album deleted? update only updates given fields? update album mediaitem
+ * count when inserting/removing mediaitem?
+ *
+ * GADGET TODOS album info such as how many albums are contained fix auto
+ * height for edit album popup thumnail pictures
+ */
+function MediaUI(social) {
+  var viewer = null;
+  var divManager = null;
+
+  var folderUrl = 'http://www.clker.com/cliparts/2/b/b/3/' +
+      '1194983972976950993blue_folder_seth_yastrov_01.svg.med.png';
+  var docUrl = 'http://www.plastyc.com/images/document-icon.png';
+
+  /*
+   * Pre-load data for gadget.
+   */
+  function loadData(callback) {
+    social.getViewer(function(data) {
+      viewer = data;
+      callback();
+    });
+  }
+
+  /*
+   * Manages the gadgets main DIV elements.
+   */
+  function DivManager() {
+    var divs = [];
+
+    this.init = function() {
+      addDiv('albumsDiv');
+      addDiv('mediaItemsDiv');
+      addDiv('mediaItemDiv');
+      hideAll();
+    }
+
+    this.showAlbums = function() {
+      hideAll();
+      divs['albumsDiv'].style.display = 'block';
+      this.refreshWindow();
+    }
+
+    this.showMediaItems = function() {
+      hideAll();
+      divs['mediaItemsDiv'].style.display = 'block';
+      this.refreshWindow();
+    }
+
+    this.showMediaItem = function() {
+      hideAll();
+      divs['mediaItemDiv'].style.display = 'block';
+      this.refreshWindow();
+    }
+
+    this.refreshWindow = function() {
+      gadgets.window.adjustHeight(350);
+    }
+
+    function hideAll() {
+      for (key in divs) { divs[key].style.display = 'none'; }
+    }
+
+    function addDiv(id) { divs[id] = dojo.create('div', {id: id}, dojo.body());}
+  }
+
+  /*
+   * Renders a list of the given albums.
+   */
+  function renderAlbums(albums) {
+
+    dojo.empty('albumsDiv');
+    var albumsDiv = dojo.byId('albumsDiv');
+
+    var albumsBanner = dojo.create('div', null, albumsDiv);
+    var table = dojo.create('table', null, albumsBanner);
+    var tbody = dojo.create('tbody', null, table);
+    var tr = dojo.create('tr', null, tbody);
+    dojo.create('td', {innerHTML: viewer.name.formatted + "'s Albums",
+      className: 'albumsTitle'}, tr);
+    dojo.create('td', null, tr).appendChild(new dijit.form.Button(
+        {label: '+ New Album', onClick: dojo.hitch(
+        this, editAlbumPopup, null)}).domNode);
+
+    var albumsList = dojo.create('div', null, albumsDiv);
+    if (albums.length > 0) {
+      var table = dojo.create('table', {className: 'albumsTable'}, albumsList);
+      var tbody = dojo.create('tbody', null, table);
+      for (i = 0; i < albums.length; i++) {
+        var albumRow = dojo.create('tr', null, tbody);
+        var albumLeft = dojo.create('td', {className: 'albumListThumbnail'},
+            albumRow);
+        var imgLink = dojo.create('a', {href: 'javascript:;',
+          onclick: dojo.hitch(this, onClickAlbum, viewer.id, albums[i])},
+        albumLeft);
+        dojo.create('img', {src: albums[i].thumbnailUrl,
+          onerror: "this.src='" + folderUrl + "';", width: '100%'}, imgLink);
+        var albumRight = dojo.create('td', {className: 'albumListRight'},
+            albumRow);
+        var albumTitleRow = dojo.create('tr', null, albumRight);
+        var titleTd = dojo.create('td', {className: 'albumListTitle'},
+            albumTitleRow);
+        dojo.create('a', {innerHTML: albums[i].title, href: 'javascript:;',
+          onclick: dojo.hitch(this, onClickAlbum, viewer.id, albums[i])},
+        titleTd);
+        var editTd = dojo.create('td', {className: 'actionLinks',
+          style: 'text-align: right'}, albumTitleRow);
+        dojo.create('a', {innerHTML: 'edit', href: 'javascript:;',
+          onclick: dojo.hitch(this, editAlbumPopup, albums[i])}, editTd);
+        editTd.appendChild(dojo.doc.createTextNode(' | '));
+        dojo.create('a', {innerHTML: 'delete', href: 'javascript:;',
+          onclick: dojo.hitch(this, deleteAlbumPopup, albums[i])}, editTd);
+
+        var openTabButton = new dijit.form.Button({label: 'Open in New Tab',
+          onClick: dojo.hitch(this, openAlbumNewTab, albums[i], null)});
+        editTd.appendChild(openTabButton.domNode);
+
+        if (albums[i].description) {
+          var albumDescription = dojo.create('tr', null, albumRight);
+          dojo.create('td', {innerHTML: albums[i].description,
+            className: 'albumListDescription', colspan: '2'}, albumDescription);
+        }
+      }
+    } else {
+      albumsDiv.appendChild(dojo.doc.createTextNode('No albums found.'));
+    }
+    divManager.refreshWindow();
+
+    // Handles when user clicks an album
+    function onClickAlbum(userId, album) {
+      social.getMediaItemsByAlbum(userId, album.id, function(response) {
+        renderMediaItems(album, response.list);
+        divManager.showMediaItems();
+      });
+    }
+  }
+
+  /*
+   * Convenience function to retrieve albums and render.
+   */
+  function renderAlbumsByUser(userId, callback) {
+    social.getAlbumsByUser(userId, function(response) {
+      renderAlbums(response.list);
+      divManager.showAlbums();
+      if (callback !== null) callback();
+    });
+  }
+
+  /*
+   * Renders a grid of the given MediaItems.
+   *
+   */
+  function renderMediaItems(album, mediaItems) {
+    dojo.empty('mediaItemsDiv');
+    var mediaItemsDiv = dojo.byId('mediaItemsDiv');
+    var numCols = 5;
+
+    // Div to display navation bar and Create button
+    var topDiv = dojo.create('div', null, mediaItemsDiv);
+    var table = dojo.create('table', null, topDiv);
+    var tbody = dojo.create('tbody', null, table);
+    var tr = dojo.create('tr', null, tbody);
+    var td = dojo.create('td', {style: 'width:100%'}, tr);
+    dojo.create('a', {innerHTML: 'Albums', href: 'javascript:;',
+      onclick: dojo.hitch(this, renderAlbumsByUser, viewer.id, null)}, td);
+    td.appendChild(dojo.doc.createTextNode(' > ' + album.title));
+    td = dojo.create('td', {style: 'width:100%'}, tr);
+    var createButton = new dijit.form.Button({label: '+ New MediaItem',
+      onClick: dojo.hitch(this, editMediaItemPopupInGadget, album, null)});
+    td.appendChild(createButton.domNode);
+
+    // Div to display MediaItems in a grid
+    var gridDiv = dojo.create('div', null, mediaItemsDiv);
+    if (mediaItems.length > 0) {
+      var table = dojo.create('table', null, gridDiv);
+      var tbody = dojo.create('tbody', null, table);
+      var tr = null;
+      for (i = 0; i < mediaItems.length; i++) {
+        if (i % numCols == 0) {
+          tr = dojo.create('tr', null, tbody);
+        }
+        var td = dojo.create('td', {className: 'mediaItemBox'}, tr);
+        var imageTd = dojo.create('tr', null, td).appendChild(dojo.create('td',
+            {className: 'mediaItemThumbnail'}));
+        if (mediaItems[i].url) {
+          var imageLink = dojo.create('a', {href: 'javascript:;',
+            onclick: dojo.hitch(this, renderMediaItemInDialog, album,
+                mediaItems[i])}, imageTd);
+          imageLink.appendChild(dojo.create('img',
+              {src: mediaItems[i].thumbnailUrl,
+                onerror: "this.src='" + docUrl + "';",
+                style: 'height:100px;'}));
+        } else {
+          dojo.create('img', {src: mediaItems[i].thumbnailUrl,
+            onerror: "this.src='" + docUrl + "';",
+            style: 'height:100px;'}, imageTd);
+        }
+        var titleTd = dojo.create('tr', null, td).appendChild(
+            dojo.create('td', {
+              style: 'text-align:center;' +
+                  "font-family:'comic sans ms';white-space:nowrap;"}));
+        titleTd.appendChild(dojo.doc.createTextNode(mediaItems[i].title));
+        var actionsTd = dojo.create('tr', null, td).appendChild(
+            dojo.create('td', {className: 'actionLinks',
+              style: 'text-align: center;'}));
+        dojo.create('a', {innerHTML: 'edit', href: 'javascript:;',
+          onclick: dojo.hitch(this, editMediaItemPopupInGadget,
+              album, mediaItems[i])}, actionsTd);
+        actionsTd.appendChild(dojo.doc.createTextNode(' | '));
+        dojo.create('a', {innerHTML: 'delete', href: 'javascript:;',
+          onclick: dojo.hitch(this, deleteMediaItemPopup, album,
+              mediaItems[i])}, actionsTd);
+      }
+    } else {
+      gridDiv.appendChild(dojo.doc.createTextNode('Album is empty'));
+    }
+    divManager.refreshWindow();
+  }
+
+  /*
+   * Convenience function to retriev & render MediaItems by Album.
+   */
+  function retrieveAndRenderMediaItems(album) {
+    social.getMediaItemsByAlbum(viewer.id, album.id, function(response) {
+      divManager.showMediaItems();
+      renderMediaItems(album, response.list);
+    });
+  }
+
+  /*
+   * Renders the view for a single MediaItem.
+   */
+  function renderMediaItem(album, mediaItem) {
+    dojo.empty('mediaItemDiv');
+    var mediaItemDiv = dojo.byId('mediaItemDiv');
+
+    // Div to display navation bar and Create button
+    var topDiv = dojo.create('div', null, mediaItemDiv);
+    var table = dojo.create('table', null, topDiv);
+    var tbody = dojo.create('tbody', null, table);
+    var tr = dojo.create('tr', null, tbody);
+    var td = dojo.create('td', {style: 'width:100%'}, tr);
+    dojo.create('a', {innerHTML: 'Albums', href: 'javascript:;',
+      onclick: dojo.hitch(this, renderAlbumsByUser, viewer.id, null)}, td);
+    td.appendChild(dojo.doc.createTextNode(' > '));
+    dojo.create('a', {innerHTML: album.title, href: 'javascript:;',
+      onclick: dojo.hitch(this, retrieveAndRenderMediaItems, album)}, td);
+    td.appendChild(dojo.doc.createTextNode(' > ' + mediaItem.title));
+
+    // Div to show MediaItem
+    var itemDiv = dojo.create('div', null, mediaItemDiv);
+    var table = dojo.create('table', null, itemDiv);
+    var tbody = dojo.create('tbody', null, table);
+    var tr = dojo.create('tr', null, tbody);
+    var td = dojo.create('td', null, tr);
+    dojo.create('img', {src: mediaItem.url}, td);
+    if (mediaItem.description) {
+      tr = dojo.create('tr', null, tbody);
+      td = dojo.create('td', null, tr);
+      td.appendChild(dojo.doc.createTextNode(mediaItem.description));
+    }
+
+    divManager.showMediaItem();
+  }
+
+
+
+  function renderMediaItemInDialog(album, mediaItem) {
+
+    var url = mediaItem.url;
+    var viewTarget = 'dialog';
+
+    function navigateCallback(site) {
+      console.log('navigateCallback ');
+    }
+    gadgets.views.openUrl(url, navigateCallback, viewTarget);
+
+  }
+
+  /*
+   * Render album gadget in new tab
+   */
+  function openAlbumNewTab(album) {
+
+    function callback(album) {}
+    function navigateCallback(site, metadata) {}
+
+    var viewParams = {'viewerId': viewer.id, 'data': album};
+
+    var opt_params = {};
+    opt_params.view = 'albumFullView';
+    opt_params.viewTarget = 'tab';
+    opt_params.viewParams = viewParams;
+    gadgets.views.openGadget(callback, navigateCallback, opt_params);
+
+  }
+
+  /*
+   * Popup to edit album.
+   */
+  function editAlbumPopup(album) {
+    var opt_view = 'default.modalDialog';
+
+    function callback(album) {
+      social.updateAlbum(viewer.id, album.id, album, function(response) {
+        renderAlbumsByUser(viewer.id);
+      });
+    }
+
+    function navigateCallback(site, metadata) {
+      console.log('navigateCallback');
+    }
+
+    var viewParams = {'data': album};
+
+    var opt_params = {};
+    opt_params.view = 'editAlbum';
+    opt_params.viewTarget = 'modalDialog';
+    opt_params.viewParams = viewParams;
+    gadgets.views.openGadget(callback, navigateCallback, opt_params);
+
+  };
+
+  /*
+   * Popup to edit MediaItem.
+   */
+  function editMediaItemPopupInGadget(album, mediaItem) {
+
+    function callback(newMediaItem) {
+      var albumId = mediaItem == null ? album.id : mediaItem.albumId;
+
+      social.updateMediaItem(viewer.id, albumId, mediaItem.id, newMediaItem,
+          function(response) {
+            // publish("org.apache.shindig.mediaItem.updated", newMediaItem);
+            social.getMediaItemsByAlbum(viewer.id, album.id,
+                function(response) {
+                  renderMediaItems(album, response.list);
+                });
+          });
+    }
+
+    function navigateCallback(site, metadata) {
+      console.log('navigateCallback');
+    }
+
+    var viewParams = {'data': {'album': album, 'mediaItem': mediaItem}};
+    var opt_params = {};
+    opt_params.view = 'editMediaItem';
+    opt_params.viewTarget = 'modalDialog';
+    opt_params.viewParams = viewParams;
+    gadgets.views.openGadget(callback, navigateCallback, opt_params);
+
+  }
+
+
+  /*
+   * Popup to confirm that the user wants to delete album.
+   */
+  function deleteAlbumPopup(album) {
+    if (confirm("Delete '" + album.title + "'?")) {
+      social.deleteAlbum(viewer.id, album.id, function(response) {
+        publish('org.apache.shindig.album.deleted', album);
+        console.log('delete album response: ' + JSON.stringify(response));
+        renderAlbumsByUser(viewer.id);
+      });
+    }
+  }
+
+  /*
+   * Popup to confirm user wants to delete MediaItem.
+   */
+  function deleteMediaItemPopup(album, mediaItem) {
+    var albumId = mediaItem.albumId;
+    if (confirm("Delete '" + mediaItem.title + "'?")) {
+      social.deleteMediaItem(viewer.id, albumId, mediaItem.id,
+          function(response) {
+            publish('org.apache.shindig.mediaItem.deleted', mediaItem);
+            console.log('delete mediaItem response: ' +
+                    JSON.stringify(response));
+            social.getMediaItemsByAlbum(viewer.id, albumId, function(response) {
+              renderMediaItems(album, response.list);
+            });
+          });
+    }
+  }
+
+  /*
+   * Publishers.
+   */
+  function publish(topic, payload) {
+    gadgets.Hub.publish(topic, payload);
+  }
+
+
+  return {
+
+    /*
+     * Initializes the gadget.
+     */
+    init: function() {
+
+      // Manages high-level divs
+      divManager = new DivManager();
+      divManager.init();
+
+      // Load data and render
+      loadData(function() {
+        social.getAlbumsByUser(viewer.id, function(response) {
+          renderAlbums(response.list);
+          divManager.showAlbums();
+        });
+      });
+    },
+
+    openAlbum: function(userId, album) {
+
+      // Manages high-level divs
+      divManager = new DivManager();
+      divManager.init();
+
+      loadData(function() {
+        social.getMediaItemsByAlbum(userId, album.id, function(response) {
+          renderMediaItems(album, response.list);
+          divManager.showMediaItems();
+        });
+      });
+
+    },
+
+    editAlbum: function(album) {
+
+      if (dojo.query('editAlbumFormDiv')) {
+        dojo.destroy('editAlbumFormDiv');
+      }
+
+      var formDiv = dojo.create('div', {id: 'editAlbumFormDiv'});
+
+      var form = new dijit.form.Form({id: 'editAlbumForm'});
+      formDiv.appendChild(form.domNode);
+
+      var table = dojo.create('table', null, form.domNode);
+      var tbody = dojo.create('tbody', null, table);
+
+      var tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Title', for: 'title'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'title',
+            value: album == null ? '' : album.title
+          }).domNode
+      );
+
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Thumnail URL', for: 'thumbnail'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'thumbnail',
+            value: album == null ? '' : album.thumbnailUrl
+          }).domNode
+      );
+
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Description', for: 'description'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.Textarea({
+            name: 'description',
+            value: album == null ? '' : album.description
+          }).domNode
+      );
+
+      tr = dojo.create('tr', null, tbody);
+      var buttonTd = dojo.create('td', {colspan: '2', align: 'center'}, tr);
+      buttonTd.appendChild(new dijit.form.Button({
+        label: 'Save',
+        onClick: saveForm
+      }).domNode
+      );
+
+      buttonTd.appendChild(new dijit.form.Button({
+        label: 'Cancel',
+        onClick: destroyDialog
+      }).domNode
+      );
+
+      dojo.body().appendChild(formDiv);
+      gadgets.window.adjustHeight();
+
+      function saveForm() {
+        var values = form.get('value');
+
+        album.title = values.title;
+        album.thumbnailUrl = values.thumbnail;
+        album.description = values.description;
+
+        gadgets.views.setReturnValue(album);
+        destroyDialog();
+      }
+
+      function destroyDialog() {
+        gadgets.views.close('');
+      }
+    },
+
+    editMediaItem: function(album, mediaItem) {
+
+      var albumId = mediaItem == null ? album.id : mediaItem.albumId;
+      var title = (mediaItem == null ? 'Create' : 'Edit') + ' MediaItem';
+
+      if (dojo.query('editMediaItemDialogDiv')) {
+        dojo.destroy('editMediaItemDialogDiv');
+      }
+      // Form div
+      var formDiv = dojo.create('div', {id: 'editMediaItemFormDiv'});
+      var form = new dijit.form.Form({id: 'editMediaItemForm'});
+      formDiv.appendChild(form.domNode);
+      var table = dojo.create('table', null, form.domNode);
+      var tbody = dojo.create('tbody', null, table);
+      var tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Title', for: 'title'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'title',
+            value: mediaItem == null ? '' : mediaItem.title
+          }).domNode
+      );
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Description', for: 'description'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.Textarea({
+            name: 'description',
+            value: mediaItem == null ? '' : mediaItem.description
+          }).domNode
+      );
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Type', for: 'type'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'type',
+            value: mediaItem == null ? '' : mediaItem.type
+          }).domNode
+      );
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'Thumnail URL', for: 'thumbnailUrl'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'thumbnailUrl',
+            value: mediaItem == null ? '' : mediaItem.thumbnailUrl
+          }).domNode
+      );
+      tr = dojo.create('tr', null, tbody);
+      dojo.create('td', null, tr).appendChild(dojo.create('label',
+          {innerHTML: 'URL', for: 'url'}));
+      dojo.create('td', null, tr).appendChild(
+          new dijit.form.ValidationTextBox({
+            name: 'url',
+            value: mediaItem == null ? '' : mediaItem.url
+          }).domNode
+      );
+      tr = dojo.create('tr', null, tbody);
+      var buttonTd = dojo.create('td', {colspan: '2', align: 'center'}, tr);
+      buttonTd.appendChild(new dijit.form.Button({
+        label: 'Save',
+        onClick: saveForm
+      }).domNode
+      );
+      buttonTd.appendChild(new dijit.form.Button({
+        label: 'Cancel',
+        onClick: destroyDialog
+      }).domNode
+      );
+
+      // Textarea div for JSON
+      var textAreaDiv = dojo.create('div',
+          {style: 'width:100%; height:100%;', id: 'textAreaDiv'});
+      var textArea = new dijit.form.Textarea({value: JSON.stringify(mediaItem),
+        rows: '20'});
+      textAreaDiv.appendChild(textArea.domNode);
+
+      // Put divs together
+      var tabContainer = new dijit.layout.TabContainer(
+          {style: 'width:400px; height:275px;'});
+      var formContentPane = new dijit.layout.ContentPane(
+          {title: 'Form', content: formDiv});
+      tabContainer.addChild(formContentPane);
+      var textAreaContentPane = new dijit.layout.ContentPane(
+          {title: 'JSON', content: textAreaDiv});
+      tabContainer.addChild(textAreaContentPane);
+      tabContainer.startup();
+      var dialogDiv = dojo.create('div', {id: 'editMediaItemDialogDiv'});
+      dialogDiv.appendChild(tabContainer.domNode);
+
+      dojo.body().appendChild(dialogDiv);
+      gadgets.window.adjustHeight();
+
+      function saveForm() {
+        var values = form.get('value');
+
+        var newMediaItem = {
+          title: values.title,
+          description: values.description,
+          type: values.type,
+          thumbnailUrl: values.thumbnailUrl,
+          url: values.url
+        };
+
+        gadgets.views.setReturnValue(newMediaItem);
+        destroyDialog();
+      }
+
+      function destroyDialog() {
+        gadgets.views.close('');
+      }
+    }
+  };
+}

Added: shindig/trunk/content/samplecontainer/examples/media-openGadgets/Social.js
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/media-openGadgets/Social.js?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/media-openGadgets/Social.js (added)
+++ shindig/trunk/content/samplecontainer/examples/media-openGadgets/Social.js Thu Jul 21 18:02:23 2011
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+/*
+ * Defines high level functionality to interact with the OpenSocial API.
+ */
+function SocialWrapper() {
+
+  /*
+   * Retrieves the current viewer.
+   */
+  this.getViewer = function(callback) {
+    osapi.people.getViewer().execute(callback);
+  }
+
+  /*
+   * Retrieves the current owner.
+   */
+  this.getOwner = function(callback) {
+    osapi.people.getOwner().execute(callback);
+  }
+
+  // ------------------------ ALBUMS ----------------------
+  /*
+   * Retrieves albums by ID(s).
+   */
+  this.getAlbumsById = function(userId, albumId, callback) {
+    var params = {userId: userId, albumId: albumId};
+    osapi.albums.get(params).execute(callback);
+  }
+
+  /*
+   * Retrieves albums by user.
+   */
+  this.getAlbumsByUser = function(userId, callback) {
+    osapi.albums.get({userId: userId}).execute(callback);
+  }
+
+  /*
+   * Retrieves albums by group.
+   */
+  this.getAlbumsByGroup = function(userId, groupId, callback) {
+    osapi.albums.get({userId: userId, groupId: groupId}).execute(callback);
+  }
+
+  /*
+   * Creates an album for the given user.
+   */
+  this.createAlbum = function(userId, album, callback) {
+    var params = {
+      userId: userId,
+      album: album
+    };
+    osapi.albums.create(params).execute(callback);
+  }
+
+  /*
+   * Updates an album by ID.
+   */
+  this.updateAlbum = function(userId, albumId, album, callback) {
+    var params = {
+      userId: userId,
+      albumId: albumId,
+      album: album
+    };
+    osapi.albums.update(params).execute(callback);
+  }
+
+  /*
+   * Deletes an album by ID.
+   */
+  this.deleteAlbum = function(userId, albumId, callback) {
+    var params = {userId: userId, albumId: albumId};
+    osapi.albums.delete(params).execute(callback);
+  }
+
+  // ------------------------------- MEDIAITEMS ----------------------------
+  /*
+   * Creates a MediaItem.
+   */
+  this.createMediaItem = function(userId, albumId, mediaItem, callback) {
+    var params = {
+      userId: userId,
+      albumId: albumId,
+      mediaItem: mediaItem
+    };
+    osapi.mediaItems.create(params).execute(callback);
+  }
+
+  /*
+   * Updates a MediaItem by ID.
+   */
+  this.updateMediaItem = function(userId, albumId, mediaItemId, mediaItem,
+          callback) {
+    var params = {
+      userId: userId,
+      albumId: albumId,
+      mediaItemId: mediaItemId,
+      mediaItem: mediaItem
+    };
+    console.log('PARAMS: ' + JSON.stringify(params));
+    osapi.mediaItems.update(params).execute(callback);
+  }
+
+  /*
+   * Retrieves MediaItems by ID(s).
+   */
+  this.getMediaItemsById = function(userId, albumId, mediaItemId, callback) {
+    var params = {
+      userId: userId,
+      albumId: albumId,
+      mediaItemId: mediaItemId
+    };
+    osapi.mediaItems.get(params).execute(callback);
+  }
+
+  /*
+   * Retrieves MediaItems by album.
+   */
+  this.getMediaItemsByAlbum = function(userId, albumId, callback) {
+    osapi.mediaItems.get({userId: userId, albumId: albumId}).execute(callback);
+  }
+
+  /*
+   * Retrieves MediaItems by user and group.
+   */
+  this.getMediaItemsByUser = function(userId, groupId, callback) {
+    osapi.mediaItems.get({userId: userId, groupId: groupId}).execute(callback);
+  }
+
+  /*
+   * Deletes a MediaItem by ID.
+   */
+  this.deleteMediaItem = function(userId, albumId, mediaItemId, callback) {
+    var params = {
+      userId: userId,
+      albumId: albumId,
+      mediaItemId: mediaItemId
+    };
+    osapi.mediaItems.delete(params).execute(callback);
+  }
+}

Added: shindig/trunk/content/samplecontainer/examples/media-openGadgets/styles.css
URL: http://svn.apache.org/viewvc/shindig/trunk/content/samplecontainer/examples/media-openGadgets/styles.css?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/content/samplecontainer/examples/media-openGadgets/styles.css (added)
+++ shindig/trunk/content/samplecontainer/examples/media-openGadgets/styles.css Thu Jul 21 18:02:23 2011
@@ -0,0 +1,109 @@
+/**
+ * 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.
+ */
+/* ============ ROUND 2 ============== */
+td.albumsTitle {
+  font-family: 'comic sans ms';
+  width: 100%;
+  font-size: 24px;
+}
+
+td.albumListThumbnail {
+  width: 10%;
+  height: 75px;
+}
+
+td.albumListRight {
+  width: 90%;
+}
+
+td.actionLinks {
+  width: 100%;
+  font-size: 12px;
+}
+
+td.albumListTitle {
+  font-size: 24px;
+  white-space: nowrap;
+  font-family: 'comic sans ms';
+}
+
+td.albumListDescription {
+  font-family: 'comic sans ms';
+}
+
+.albumsTable {
+  width: 100%;
+  border-style: solid;
+  background-color: #b0c4de;
+}
+
+td.mediaItemThumbnail {
+  height: 100%;
+}
+
+td.mediaItemBox {
+  width: 150px;
+  height: 100px;
+}
+
+.mediaItemControls {
+  
+}
+
+/* ============ ROUND 1 ============== */
+.temp1 {
+  background-color: #6495ed;
+}
+
+.temp2 {
+  background-color: #e0ffff;
+}
+
+.temp3 {
+  background-color: #b0c4de;
+  background-image: url("img_flwr.png");
+  background-repeat: no-repeat;
+  background-position: top right;
+}
+
+td2 {
+  border-style: solid;
+}
+
+td.albumLeft {
+  width: 10%;
+}
+
+td.albumRight {
+  width: 90%;
+}
+
+td.albumEdit {
+  width: 100%;
+  text-align: right;
+  vertical-align: middle;
+  font-size: 12px;
+}
+
+.albumTitleStyle {
+  color: blue;
+}
+
+.albumElement {
+  background-color: #b0c4de;
+}
\ No newline at end of file

Modified: shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/pom.xml?rev=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/features/pom.xml (original)
+++ shindig/trunk/features/pom.xml Thu Jul 21 18:02:23 2011
@@ -184,6 +184,8 @@
                 <source>opensearch/opensearch.js</source>
                 <source>embeddedexperiences/constant.js</source>
                 <source>embeddedexperiences/embedded_experiences_container.js</source>
+                <source>open-views/viewenhancements-container.js</source>
+                <source>open-views/viewenhancements.js</source>
               </sources>
               <testSourceDirectory>${basedir}/src/test/javascript/features</testSourceDirectory>
               <testSuites>

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=1149287&r1=1149286&r2=1149287&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/features.txt (original)
+++ shindig/trunk/features/src/main/javascript/features/features.txt Thu Jul 21 18:02:23 2011
@@ -73,6 +73,7 @@ features/opensocial-data/feature.xml
 features/opensocial-jsonrpc/feature.xml
 features/opensocial-reference/feature.xml
 features/opensocial-templates/feature.xml
+features/open-views/feature.xml
 features/osapi.base/feature.xml
 features/osapi/feature.xml
 features/osml/feature.xml

Added: shindig/trunk/features/src/main/javascript/features/open-views/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/open-views/feature.xml?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/open-views/feature.xml (added)
+++ shindig/trunk/features/src/main/javascript/features/open-views/feature.xml Thu Jul 21 18:02:23 2011
@@ -0,0 +1,46 @@
+<?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>
+<!--
+Required configuration:
+A map of view names to view attributes. Examples:
+
+
+-->
+  <name>open-views</name>
+  <dependency>globals</dependency>
+  <dependency>rpc</dependency>
+  <dependency>container.url</dependency>
+  <dependency>views</dependency>
+  <dependency>embedded-experiences</dependency>
+  <gadget>
+    <script src="viewenhancements.js"/>
+    <api>
+      <exports type="js">gadgets.views.openEmbeddedExperience</exports>
+      <exports type="js">gadgets.views.openGadget</exports>
+      <exports type="js">gadgets.views.openUrl</exports>
+      <exports type="js">gadgets.views.close</exports>
+      <exports type="js">gadgets.views.setReturnValue</exports>
+      <exports type="js">gadgets.window.getContainerDimensions</exports>
+    </api>
+  </gadget>
+  <container>
+    <script src="viewenhancements-container.js"/>
+  </container>
+</feature>

Added: shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements-container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements-container.js?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements-container.js (added)
+++ shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements-container.js Thu Jul 21 18:02:23 2011
@@ -0,0 +1,417 @@
+/*
+ * 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 view enhancements.
+ */
+
+(function() {
+
+  var context;
+
+  // Mapping between id and callback function
+  var resultCallbackMap;
+
+  // Mapping between id and return value
+  var returnValueMap;
+
+  // mapping between iframe id and site
+  var iframeSiteMap;
+
+  function init(container) {
+
+    context = container;
+
+    gadgets.rpc.register('gadgets.views.openGadget', openGadget);
+
+    gadgets.rpc.register('gadgets.views.openEmbeddedExperience', openEE);
+
+    gadgets.rpc.register('gadgets.views.openUrl', openUrl);
+
+    gadgets.rpc.register('gadgets.views.close', close);
+
+    gadgets.rpc.register('gadgets.views.setReturnValue', setReturnValue);
+
+    gadgets.rpc.register('gadgets.window.getContainerDimensions',
+        getContainerDimensions);
+
+    resultCallbackMap = {};
+
+    returnValueMap = {};
+
+    iframeSiteMap = {};
+  };
+  /**
+   * Opens a gadget in the container UI. The location of the gadget site in the
+   * container will be determined by the view target passed in. The container
+   * would open the view in a dialog, if view target is dialog or the gadgets
+   * view in a tab for view target is tab.
+   *
+   * @param {function}
+   *          resultCallback: Callback function to be called when the gadget
+   *          closes. The function will be called with the return value as a
+   *          parameter.
+   * @param {function}
+   *          navigateCallback: Callback function to be called with the the
+   *          Site which has been opened and metadata.
+   * @param {Object.<string, string|Object>=}
+   *          opt_params: These are optional parameters which can be used to
+   *          open gadgets. The following parameters may be included in this
+   *          object. {string} view: The view to render. Should be one of the
+   *          views returned by calling gadgets.views.getSupportedViews. If the
+   *          view is not included the default view will be rendered. {string}
+   *          viewTarget: The view that indicates where to open the gadget. For
+   *          example, tab, dialog or modaldialog {Object} viewParams: View
+   *          parameters for the view being rendered.
+   */
+  function openGadget(resultCallback, navigateCallback, opt_params) {
+
+    var gadgetUrl = '';
+
+    var orig_site = context.getGadgetSiteByIframeId_(this.f);
+
+    if (orig_site !== undefined &&
+            orig_site.getActiveGadgetHolder() !== undefined) {
+      // get url through gadget holder
+      gadgetUrl = orig_site.getActiveGadgetHolder().getUrl();
+    }
+
+    var view = '';
+    var viewTarget = '';
+    var viewParams = {};
+    if (opt_params !== undefined) {
+      if (opt_params.view !== undefined)
+        view = opt_params.view;
+      if (opt_params.viewTarget !== undefined)
+        viewTarget = opt_params.viewTarget;
+      if (opt_params.viewParams !== undefined)
+        viewParams = opt_params.viewParams;
+    }
+    context.preloadGadget(gadgetUrl, function(result) {
+      /*
+       * result[gadgetUrl] : metadata
+       */
+      var metadata = {};
+      if (result !== undefined && result[gadgetUrl] !== undefined) {
+        if (result[gadgetUrl].error) {
+          gadgets.error('Failed to preload gadget : ' + gadgetUrl);
+          if (navigateCallback != null) {
+            navigateCallback(null, result[gadgetUrl]);
+          }
+          return;
+        }else {
+          metadata = result[gadgetUrl];
+        }
+      }
+      var content_div = context.views.createElementForGadget(metadata, view,
+          viewTarget);
+      var site = context.newGadgetSite(content_div);
+
+      var renderParams = {};
+
+      if (view !== undefined && view !== '') {
+        renderParams[osapi.container.RenderParam.VIEW] = view;
+      }
+      renderParams[osapi.container.RenderParam.WIDTH] = '100%';
+      renderParams[osapi.container.RenderParam.HEIGHT] = '100%';
+
+      context.navigateGadget(site, gadgetUrl, viewParams, renderParams,
+          function(metadata) {
+            if (metadata != null) {
+              processSiteAndCallbackInfo(site, resultCallback);
+            }
+            if (navigateCallback != null) {
+              navigateCallback(site, metadata);
+            }
+          });
+
+    });
+  }
+
+  /**
+   * Processes the site and callback information and stores it in global maps.
+   * @param {osapi.container.GadgetSite | osapi.container.UrlSite} site the site
+   * that was created for the gadget.
+   * @param {Function} resultCallback called with any result the gadget chooses
+   * to set.
+   */
+  function processSiteAndCallbackInfo(site, resultCallback) {
+    var iframeId;
+    if (site && site.getActiveGadgetHolder()) {
+      iframeId = site.getActiveGadgetHolder().getIframeId();
+    }
+
+    iframeSiteMap[iframeId] = site;
+
+    // use the site id as key
+    if (typeof site.getId() !== 'undefined' && resultCallback != null) {
+      resultCallbackMap[site.getId()] = resultCallback;
+    }
+  }
+
+  /**
+   * Opens an embedded experience in the container UI. The location of the site
+   * in the container will be determined by the view target passed in. The
+   * container would open the embedded experience in a dialog, if view target is
+   * dialog or the embedded experience view in a tab for view target is tab.
+   *
+   * @param {Function}
+   *          resultCallback: Callback function to be called when the embedded
+   *          experience closes. The function will be called with the return
+   *          value as a parameter.
+   * @param {Function}
+   *          navigateCallback: Callback function to be called with the embedded
+   *          experience has rendered.
+   * @param {Object}
+   *          opt_params: These are optional parameters which can be used to
+   *          open gadgets. The following parameters may be included in this
+   *          object. {string} viewTarget: The view that indicates where to open
+   *          the gadget. For example, tab, dialog or modaldialog {Object}
+   *          viewParams: View parameters for the view being rendered.
+   */
+  function openEE(resultCallback, navigateCallback, dataModel, opt_params) {
+    var gadgetUrl = dataModel.gadget;
+    if (gadgetUrl) {
+      //Check to make sure we can actually reach the gadget we are going to try
+      //to render before we do anything else
+      context.preloadGadget(gadgetUrl, function(result) {
+        if (result[gadgetUrl] == null ||
+                (result[gadgetUrl] != null && result[gadgetUrl].error)) {
+          //There was an error, check to see if there is still the option to
+          //render the url, else just call the navigateCallback
+          if (!dataModel.url) {
+            if (navigateCallback != null) {
+              navigateCallback(null, result[gadgetUrl]);
+            }
+            return;
+          }
+        }
+
+        var viewTarget = '';
+        var viewParams = {};
+        if (opt_params != undefined) {
+          if (opt_params.viewTarget != undefined)
+            viewTarget = opt_params.viewTarget;
+          if (opt_params.viewParams != undefined)
+            viewParams = opt_params.viewParams;
+        }
+
+        var element = context.views.createElementForEmbeddedExperience(
+            viewTarget);
+
+        var gadgetRenderParams = {};
+        gadgetRenderParams[osapi.container.RenderParam.VIEW] =
+            osapi.container.ee.RenderParams.EMBEDDED;
+        gadgetRenderParams[osapi.container.RenderParam.WIDTH] = '100%';
+        gadgetRenderParams[osapi.container.RenderParam.HEIGHT] = '100%';
+
+        var urlRenderParams = {};
+        urlRenderParams[osapi.container.RenderParam.WIDTH] = '100%';
+        urlRenderParams[osapi.container.RenderParam.HEIGHT] = '100%';
+
+        var eeRenderParams = {};
+        eeRenderParams[osapi.container.ee.RenderParams.GADGET_RENDER_PARAMS] =
+            gadgetRenderParams;
+        eeRenderParams[osapi.container.ee.RenderParams.URL_RENDER_PARAMS] =
+            urlRenderParams;
+        eeRenderParams[osapi.container.ee.RenderParams.GADGET_VIEW_PARAMS] =
+            viewParams;
+
+        context.ee.navigate(element, dataModel, eeRenderParams, function(site,
+                metadata) {
+              if (metadata != null) {
+                processSiteAndCallbackInfo(site, resultCallback);
+              }
+              if (navigateCallback != null) {
+                navigateCallback(site, metadata);
+              }
+            });
+      });
+    }
+  }
+
+
+  /**
+   * Opens a URL in the container UI. The location of the URL site will be
+   * determined by the container based on the target view. The container would
+   * open the view in a dialog, if opt_viewTarget=dialog or the gadgets view in
+   * a tab for opt_viewTarget=tab
+   *
+   * @param {string}
+   *          url: URL to a web page to open in a URL site in the container.
+   *          (Note this should not be a URL to a gadget definition.).
+   * @param {function}
+   *          navigateCallback: Callback function to be called with the site
+   *          which has been opened.
+   * @param {string=}
+   *          opt_viewTarget: Optional parameter,the view that indicates where
+   *          to open the URL.
+   */
+  function openUrl(url, navigateCallback, opt_viewTarget) {
+    var content_div = context.views.createElementForUrl(opt_viewTarget);
+
+    var site = context.newUrlSite(content_div);
+
+    var renderParams = {}; // (height, width, class,userPrefsObject)
+    renderParams[osapi.container.RenderParam.WIDTH] = '100%';
+    renderParams[osapi.container.RenderParam.HEIGHT] = '100%';
+
+    context.navigateUrl(site, url, renderParams);
+
+    if (navigateCallback !== undefined) {
+      navigateCallback(site);
+    }
+  }
+
+  /**
+   * Closes an opened site. If the opt_id parameter is null the container will
+   * close the calling site.
+   *
+   * @param {object=}
+   *          opt_site: Optional parameter which specifies what site to close.
+   *          If null it will close the current gadget site.
+   */
+  function close(opt_site) {
+    // this.f is the frame id
+    var iframeId = this.f;
+    var site;
+
+    if (opt_site == undefined || opt_site == '') {
+      site = iframeSiteMap[iframeId];
+    }
+    else {
+      site = opt_site;
+    }
+
+    if (site != null) {
+      var siteId = site.getId();
+
+      if (siteId !== undefined && resultCallbackMap[siteId] !== undefined &&
+              returnValueMap[siteId] !== undefined) {
+        var returnValue = returnValueMap[siteId];
+        // execute the result callback function with return value as parameter
+        resultCallbackMap[siteId](returnValue);
+      }
+    }
+
+    context.views.destroyElement(site);
+  }
+
+  /**
+   * Sets the return value for the current window. This method should only be
+   * called inside those secondary view types defined in gadgets.views.ViewType.
+   * For example, DIALOG or MODALDIALOG
+   *
+   * @param {object}
+   *          returnValue: Return value for this window.
+   */
+  function setReturnValue(returnValue) {
+    if (returnValue !== undefined && iframeSiteMap[this.f] !== undefined) {
+      var siteId = iframeSiteMap[this.f].getId();
+      // use the site id as key
+      if (siteId !== undefined) {
+        returnValueMap[siteId] = returnValue;
+      }
+    }
+  }
+
+  /**
+   * Gets the dimensions of the container displaying the gadget.
+   */
+  function getContainerDimensions() {
+    var el = document.documentElement; // Container
+    // element
+    if (el !== undefined)
+      // return client width and client height
+      return {
+        'width' : el.clientWidth,
+        'height' : el.clientHeight
+      };
+    else
+      return {
+        'width' : -1,
+        'height' : -1
+      };
+  }
+
+  osapi.container.Container.addMixin('views', function(container) {
+
+    init(container);
+
+    return { // this is a map of the public API in the namespace
+      /**
+       * Method will be called to create the DOM element to place the Gadget
+       * Site in.
+       *
+       * @param {Object}
+       *          metadata: Gadget meta data for the gadget being opened in
+       *          this GadgetSite.
+       * @param {string=}
+       *          opt_view: Optional parameter, the view that indicates the
+       *          type of GadgetSite.
+       * @param {string=}
+       *          opt_viewTarget: Optional parameter, the view target indicates
+       *          where to open the gadget.
+       * @return {Object} The DOM element to place the GadgetSite in.
+       */
+      'createElementForGadget' : function(metadata, opt_view, opt_viewTarget) {
+        console.log('container need to define createElementForGadget function');
+      },
+
+      /**
+       * Method will be called to create the DOM element to place the embedded
+       * experience in.
+       *
+       * @param {string=}
+       *          opt_viewTarget:  Optional parameter, the view target indicates
+       *          where to open.
+       * @return {Object} The DOM element to place the embedded experience in.
+       */
+
+      'createElementForEmbeddedExperience' : function(opt_viewTarget) {
+        console.log('container need to define ' +
+                'createElementForEmbeddedExperience function');
+      },
+
+      /**
+       * Method will be called to create the DOM element to place the UrlSite
+       * in.
+       *
+       * @param {string=}
+       *          opt_view: Optional parameter, the view to open. If not
+       *          included the container should use its default view.
+       * @return {Object} The DOM element to place the UrlSite object in.
+       */
+
+      'createElementForUrl' : function(opt_viewTarget) {
+        console.log('container need to define createElementForUrl function');
+      },
+
+      /**
+       * Method will be called when a gadget wants to close itself or the
+       * parent gadget wants to close a gadget or url site it has opened.
+       *
+       * @param {Object}
+       *          site: The site to close.
+       */
+      'destroyElement' : function(site) {
+        console.log('container need to define destroyElement function');
+      }
+    };
+  }); //end addMixin
+}());

Added: shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements.js?rev=1149287&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements.js (added)
+++ shindig/trunk/features/src/main/javascript/features/open-views/viewenhancements.js Thu Jul 21 18:02:23 2011
@@ -0,0 +1,144 @@
+/*
+ * 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 view enhancement library for gadgets.
+ */
+
+gadgets['window'] = gadgets['window'] || {};
+
+(function() {
+
+  /**
+   * Opens a gadget in the container UI. The location of the gadget site in the
+   * container will be determined by the view target passed in. The container
+   * would open the view in a dialog, if view target is dialog or the gadgets
+   * view in a tab for view target is tab
+   *
+   * @param {function}
+   *          resultCallback: Callback function to be called when the gadget
+   *          closes. The function will be called with the return value as a
+   *          parameter.
+   * @param {function}
+   *          idCallback: Callback function to be called with the id of the
+   *          Site which has been opened.
+   * @param {Object}
+   *          opt_params: These are optional parameters which can be used to
+   *          open gadgets. The following parameters may be included in this
+   *          object. {string} view: The view to render. Should be one of the
+   *          views returned by calling gadgets.views.getSupportedViews. If the
+   *          view is not included the default view will be rendered. {string}
+   *          viewTarget: The view that indicates where to open the gadget. For
+   *          example, tab, dialog or modaldialog {Object} viewParams: View
+   *          parameters for the view being rendered.
+   */
+
+  gadgets.views.openGadget = function(resultCallback, idCallback, opt_params) {
+    gadgets.rpc.call('..', 'gadgets.views.openGadget', null, resultCallback,
+        idCallback, opt_params);
+  };
+
+  /**
+   * Opens an embedded experience in the container UI. The location of the
+   * gadget site in the container will be determined by the view target passed
+   * in. The container would open the view in a dialog, if view target is dialog
+   * or the gadgets view in a tab for view target is tab.
+   *
+   * @param {function}
+   *          resultCallback: Callback function to be called when the gadget
+   *          closes. The function will be called with the return value as a
+   *          parameter.
+   * @param {function}
+   *          navigateCallback: Callback function to be called with the site and
+   *          gadget metadata.
+   * @param {function}
+   *          dataModel: The embedded experiences data model.
+   * @param {Object}
+   *          opt_params: These are optional parameters which can be used to
+   *          open gadgets. The following parameters may be included in this
+   *          object. {string} viewTarget: The view that indicates where to open
+   *          the gadget. For example, tab, dialog or modaldialog {Object}
+   *          viewParams: View parameters for the view being rendered.
+   */
+  gadgets.views.openEmbeddedExperience = function(resultCallback,
+          navigateCallback, dataModel, opt_params) {
+    gadgets.rpc
+    .call('..', 'gadgets.views.openEmbeddedExperience', null, resultCallback,
+            navigateCallback, dataModel, opt_params);
+  };
+
+  /**
+   * Opens a URL in the container UI. The location of the URL site will be
+   * determined by the container based on the target view. The container would
+   * open the view in a dialog, if opt_viewTarget=dialog or the gadgets view in
+   * a tab for opt_viewTarget=tab
+   *
+   * @param {string}
+   *          url: URL to a web page to open in a URL site in the container.
+   *          (Note this should not be a URL to a gadget definition.).
+   * @param {function}
+   *          idCallback: Callback function to be called with the id of the
+   *          site which has been opened.
+   * @param {string=}
+   *          opt_viewTarget: Optional parameter,the view that indicates where
+   *          to open the URL.
+   */
+  gadgets.views.openUrl = function(url, idCallback, opt_viewTarget) {
+    gadgets.rpc.call('..', 'gadgets.views.openUrl', null, url, idCallback,
+        opt_viewTarget);
+  }
+
+  /**
+   * Closes an opened site. If the opt_id parameter is null the container will
+   * close the calling site.
+   *
+   * @param {string}
+   *          opt_id: Optional parameter which specifies what site to close.
+   *          If null it will close the current gadget site.
+   */
+  gadgets.views.close = function(id) {
+    gadgets.rpc.call('..', 'gadgets.views.close', null, id);
+  };
+
+  /**
+   * Sets the return value for the current window. This method should only be
+   * called inside those secondary view types defined in gadgets.views.ViewType.
+   * For example, DIALOG or MODALDIALOG
+   *
+   * @param {object}
+   *          returnValue: Return value for this window.
+   */
+  gadgets.views.setReturnValue = function(returnValue) {
+    gadgets.rpc.call('..', 'gadgets.views.setReturnValue', null, returnValue);
+  };
+
+  /**
+   * Gets the dimensions of the container displaying this gadget through
+   * callback function which will be called with the return value as a
+   * parameter.
+   *
+   * @param {function}
+   *          resultCallback: Callback function will be called with the return
+   *          value as a parameter.
+   */
+  gadgets.window.getContainerDimensions = function(resultCallback) {
+    gadgets.rpc.call('..', 'gadgets.window.getContainerDimensions',
+        resultCallback, null);
+  }
+
+}());