You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by dd...@apache.org on 2012/09/04 16:01:03 UTC
svn commit: r1380656 - in /shindig/trunk: ./ features/
features/src/main/javascript/features/container/
features/src/main/javascript/features/core.util.base/
features/src/main/javascript/features/oauthpopup/
features/src/test/javascript/features/oauthp...
Author: ddumont
Date: Tue Sep 4 14:01:02 2012
New Revision: 1380656
URL: http://svn.apache.org/viewvc?rev=1380656&view=rev
Log:
SHINDIG-1864 - Refactor oauthpopup feature to use rpc to the container
Added:
shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js (with props)
Modified:
shindig/trunk/UPGRADING
shindig/trunk/features/pom.xml
shindig/trunk/features/src/main/javascript/features/container/container.js
shindig/trunk/features/src/main/javascript/features/container/feature.xml
shindig/trunk/features/src/main/javascript/features/core.util.base/base.js
shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml
shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js
shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js
Modified: shindig/trunk/UPGRADING
URL: http://svn.apache.org/viewvc/shindig/trunk/UPGRADING?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/UPGRADING (original)
+++ shindig/trunk/UPGRADING Tue Sep 4 14:01:02 2012
@@ -7,6 +7,9 @@ FROM 2.0.x TO 2.5.x
The new property allows for embedding the key directly or referencing a classpath or filesystem
resource. Please see the comments at the top of container.js and around the new property for more
details.
+* The CommonContainer class now depends on the oauthpopup feature for some reworked logic in the
+feature. If you roll your own container and do not use the CommonContainer, update your container
+dependencies to include the oauthpopup feature.
== Java Dependency Changes ==
Modified: shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/pom.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/pom.xml (original)
+++ shindig/trunk/features/pom.xml Tue Sep 4 14:01:02 2012
@@ -195,6 +195,7 @@
<source>osapi/jsonrpctransport.js</source>
<source>osapi/peoplehelpers.js</source>
<source>../../../../src/test/javascript/lib/testutils.js</source>
+ <source>oauthpopup/container_oauthpopup.js</source>
<source>oauthpopup/oauthpopup.js</source>
<source>selection/selection_container.js</source>
<source>selection/selection.js</source>
Modified: shindig/trunk/features/src/main/javascript/features/container/container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/container.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/container.js (original)
+++ shindig/trunk/features/src/main/javascript/features/container/container.js Tue Sep 4 14:01:02 2012
@@ -399,8 +399,8 @@ osapi.container.Container.addMixin = fun
if (mixins[namespace]) {
var orig = mixins[namespace];
mixins[namespace] = function(container) {
- orig.call(this, container);
- return func.call(this, container);
+ var base = orig.call(this, container);
+ return func.call(this, container, base); // pass overriding mixins the original.
};
} else {
osapi.container.Container.prototype.mixinsOrder_.push(namespace);
Modified: shindig/trunk/features/src/main/javascript/features/container/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/feature.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/feature.xml (original)
+++ shindig/trunk/features/src/main/javascript/features/container/feature.xml Tue Sep 4 14:01:02 2012
@@ -28,6 +28,7 @@ under the License.
<dependency>container.util</dependency>
<dependency>container.site.gadget</dependency>
<dependency>container.site.url</dependency>
+ <dependency>oauthpopup</dependency>
<container>
<script src="service.js"/>
<script src="container.js"/>
Modified: shindig/trunk/features/src/main/javascript/features/core.util.base/base.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/core.util.base/base.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/core.util.base/base.js (original)
+++ shindig/trunk/features/src/main/javascript/features/core.util.base/base.js Tue Sep 4 14:01:02 2012
@@ -44,18 +44,10 @@ gadgets.util = gadgets.util || {};
* @return {function()} a callback function.
*/
gadgets.util.makeClosure = function(scope, callback, var_args) {
- // arguments isn't a real array, so we copy it into one.
- var baseArgs = [];
- for (var i = 2, j = arguments.length; i < j; ++i) {
- baseArgs.push(arguments[i]);
- }
+ var baseArgs = Array.prototype.slice.call(arguments, 2);
return function() {
- // append new arguments.
- var tmpArgs = baseArgs.slice();
- for (var i = 0, j = arguments.length; i < j; ++i) {
- tmpArgs.push(arguments[i]);
- }
- return callback.apply(scope, tmpArgs);
+ var passedArgs = Array.prototype.slice.call(arguments);
+ return callback.apply(scope, baseArgs.concat(passedArgs));
};
};
Added: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js?rev=1380656&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js (added)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js Tue Sep 4 14:01:02 2012
@@ -0,0 +1,126 @@
+/*
+ * 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 API to assist with management of the OAuth popup window.
+ */
+
+(function() {
+ var timers = {},
+ cbid = 1; // start at 1 so they are always truthy
+
+ function checkClosed(win, callback, key) {
+ if (this.isClosed(win)) {
+ // setInterval, when missed, can run multiple times.
+ if (typeof(timers[key]) != 'undefined') {
+ window.clearInterval(timers[key]);
+ delete timers[key];
+ callback();
+ }
+ }
+ }
+
+ // scope
+ var oauth = {
+ /**
+ * Handles the rpc endpoint for gadgets wanting to open an oauth popup.
+ *
+ * @param {string} location Location to open
+ * @param {string} options Window open options
+ * @param {string} from Unique identifier of gadget requesting oauth
+ * @param {function(stirng)} onOpen Callback that takes the id of the close callback
+ */
+ open: function(location, options, from, onOpen) {
+ // If a popup blocker blocks the window, we do nothing. The user will
+ // need to approve the popup, then click again to open the window.
+ // Note that because we don't call window.open until the user has clicked
+ // something the popup blockers *should* let us through.
+ var key = location + ':' + from + ":" + options,
+ win = this.getWindow(location, options);
+
+ if (win) {
+ var id = cbid++,
+ callback = gadgets.util.makeClosure(this, function(id) {
+ this.closeWindow(win); // make sure it's closed
+ gadgets.rpc.call(from, 'oauth.close', null, id);
+ }, id);
+
+ // Poll every 100ms to check if the window has been closed
+ timers[key] = window.setInterval(gadgets.util.makeClosure(this, checkClosed, win, callback, key), 100);
+
+ onOpen(id);
+ } else {
+ // handle error that might be able to resume.
+ this.error(Array.prototype.slice.call(arguments));
+ }
+ },
+
+ /**
+ * Opens a window at a specific location.
+ *
+ * @param {string} location Location to open
+ * @param {string} options Window open options
+ * @param {string} from Unique identifier of gadget requesting oauth
+ * @return {Object} The opened window
+ */
+ getWindow: function(location, options, from) {
+ return window.open(location, '_blank', options);
+ },
+
+ /**
+ * Closes a window opened by
+ *
+ * @param {Object} win A window opened by getWindow()
+ */
+ closeWindow: function(win) {
+ win && win.close && win.close();
+ },
+
+ /**
+ * Check to see if window opened by getWindow() is closed.
+ *
+ * @param {Object} win A window opened by getWindow()
+ * @return {boolean} If win is closed.
+ */
+ isClosed: function(win) {
+ return !win || win.closed;
+ },
+
+ error: function(openargs) {
+ gadgets.warn('OAuth popup window was not opened.');
+ // Try again?
+ // this.open.apply(this, openargs);
+ }
+ };
+
+ // Support mixing into the container which makes js rpc tests super easy.
+ if (osapi && osapi.container && osapi.container.Container && osapi.container.Container.addMixin) {
+ osapi.container.Container.addMixin('oauth', function(container) {
+ gadgets.rpc.register('oauth.open', function(location, options) {
+ container.oauth.open(location, options, this.f, this.callback);
+ });
+
+ return oauth;
+ });
+ } else {
+ gadgets.rpc.register('oauth.open', function(location, options) {
+ oauth.open(location, options, this.f, this.callback);
+ });
+ }
+})();
\ No newline at end of file
Propchange: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml (original)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml Tue Sep 4 14:01:02 2012
@@ -18,13 +18,22 @@ specific language governing permissions
-->
<feature>
<name>oauthpopup</name>
- <dependency>globals</dependency>
+ <dependency>rpc</dependency>
+ <container>
+ <script src="container_oauthpopup.js"/>
+ <api>
+ <exports type="rpc">oauth.open</exports>
+ <uses type="rpc">oauth.close</uses>
+ </api>
+ </container>
<gadget>
<script src="oauthpopup.js"/>
<api>
<exports type="js">gadgets.oauth.Popup</exports>
<exports type="js">gadgets.oauth.Popup.prototype.createOpenerOnClick</exports>
<exports type="js">gadgets.oauth.Popup.prototype.createApprovedOnClick</exports>
+ <exports type="rpc">oauth.close</exports>
+ <uses type="rpc">oauth.open</uses>
</api>
</gadget>
</feature>
Modified: shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js (original)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js Tue Sep 4 14:01:02 2012
@@ -105,89 +105,64 @@ gadgets.oauth = gadgets.oauth || {};
* @param {string} destination Target URL for the popup window.
* @param {string} windowOptions Options for window.open, used to specify
* look and feel of the window.
- * @param {function()} openCallback Function to call when the window is opened.
- * @param {function()} closeCallback Function to call when the window is closed.
+ * @param {function()} onOpen Function to call when the window is opened.
+ * @param {function()} onClose Function to call when the window is closed.
*/
-gadgets.oauth.Popup = function(destination, windowOptions, openCallback,
- closeCallback) {
+gadgets.oauth.Popup = function(destination, windowOptions, onOpen, onClose) {
this.destination_ = destination;
this.windowOptions_ = windowOptions;
- this.openCallback_ = openCallback;
- this.closeCallback_ = closeCallback;
- this.win_ = null;
+ this.openCallback_ = onOpen;
+ this.closeCallback_ = onClose;
};
/**
* @return {function()} an onclick handler for the "open the approval window" link.
*/
-gadgets.oauth.Popup.prototype.createOpenerOnClick = function() {
- var self = this;
- return function() {
- self.onClick_();
- };
-};
-/**
- * Called when the user clicks to open the popup window.
- *
- * @return {boolean} false to prevent the default action for the click.
- * @private
- */
-gadgets.oauth.Popup.prototype.onClick_ = function() {
- // If a popup blocker blocks the window, we do nothing. The user will
- // need to approve the popup, then click again to open the window.
- // Note that because we don't call window.open until the user has clicked
- // something the popup blockers *should* let us through.
- this.win_ = window.open(this.destination_, '_blank', this.windowOptions_);
- if (this.win_) {
- // Poll every 100ms to check if the window has been closed
- var self = this;
- var closure = function() {
- self.checkClosed_();
- };
- this.timer_ = window.setInterval(closure, 100);
- this.openCallback_();
- }
- return false;
-};
-/**
- * Called at intervals to check whether the window has closed.
- * @private
- */
-gadgets.oauth.Popup.prototype.checkClosed_ = function() {
- if ((!this.win_) || this.win_.closed) {
- this.win_ = null;
- this.handleApproval_();
- }
-};
+(function() {
+ var callbacks = {};
-/**
- * Called when we recieve an indication the user has approved access, either
- * because they closed the popup window or clicked an "I've approved" button.
- * @private
- */
-gadgets.oauth.Popup.prototype.handleApproval_ = function() {
- if (this.timer_) {
- window.clearInterval(this.timer_);
- this.timer_ = null;
- }
- if (this.win_) {
- this.win_.close();
- this.win_ = null;
+ gadgets.util.registerOnLoadHandler(function() {
+ gadgets.rpc.register('oauth.close', function(cbid) {
+ if (this.f == '..') {
+ callbacks[cbid] && callbacks[cbid]();
+ }
+ });
+ });
+
+ function onOpen(cbid) {
+ this.cbid = cbid;
+ callbacks[cbid] = this.createApprovedOnClick();
+ this.openCallback_();
}
- this.closeCallback_();
- return false;
-};
-/**
- * @return {function()} an onclick handler for the "I've approved" link. This may not
- * ever be called. If we successfully detect that the window was closed,
- * this link is unnecessary.
- */
-gadgets.oauth.Popup.prototype.createApprovedOnClick = function() {
- var self = this;
- return function() {
- self.handleApproval_();
+ /**
+ * Called when the user clicks to open the popup window.
+ *
+ * @return {boolean} false to prevent the default action for the click.
+ * @private
+ */
+ gadgets.oauth.Popup.prototype.createOpenerOnClick = function() {
+ return gadgets.util.makeClosure(this, function() {
+ gadgets.rpc.call('..', 'oauth.open', gadgets.util.makeClosure(this, onOpen),
+ this.destination_, this.windowOptions_
+ );
+ return false;
+ });
};
-};
+
+ /**
+ * @return {function()} an onclick handler for the "I've approved" link. This may not
+ * ever be called. If we successfully detect that the window was closed,
+ * this link is unnecessary.
+ */
+ gadgets.oauth.Popup.prototype.createApprovedOnClick = function() {
+ return gadgets.util.makeClosure(this, function() {
+ if (this.cbid) {
+ delete callbacks[this.cbid];
+ }
+ this.closeCallback_();
+ });
+ };
+})();
\ No newline at end of file
Modified: shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js (original)
+++ shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js Tue Sep 4 14:01:02 2012
@@ -26,58 +26,76 @@ PopupTest.inherits(TestCase);
PopupTest.prototype.setUp = function() {
this.oldWindow = window;
window = new mocks.FakeWindow();
+ window.__API_URI = shindig.uri('http://shindig.com');
+ window.__CONTAINER_URI = shindig.uri('http://container.com');
+
+ this.gadgetsRpc = gadgets.rpc;
+ gadgets.rpc = {};
+ var self = this;
+ gadgets.rpc.register = function(service, callback) {
+ if (self.captures && self.captures.hasOwnProperty(service)) {
+ self.captures[service] = callback;
+ }
+ };
+ gadgets.rpc.call = function() {
+ self.rpcArguments = Array.prototype.slice.call(arguments);
+ };
};
PopupTest.prototype.tearDown = function() {
window = this.oldWindow;
+ gadgets.rpc = this.gadgetsRpc;
+ delete this.rpcArguments;
+ delete this.captures;
};
PopupTest.prototype.testPopup = function() {
- var opened = false;
- var open = function() {
- opened = true;
+ var undef, captures = this.captures = {
+ 'oauth.open': undef
};
- var closed = false;
- var close = function() {
- closed = true;
+ var container = new osapi.container.Container();
+ this.assertNotUndefined('RPC endpoint "oauth.open" was not registered.', captures['oauth.open']);
+
+ delete this.rpcArguments;
+
+ var cbid, popup, opened = false;
+ window.open = function(url, target, options) {
+ return popup = mocks.FakeWindow.prototype.open.call(this, url, target, options);
};
- // Create the popup
- var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
- var openerOnClick = popup.createOpenerOnClick();
- var closerOnClick = popup.createApprovedOnClick();
- this.assertNull('Window opened prematurely', popup.win_);
- this.assertFalse('Opener callback was called', opened);
+ this.assertUndefined('Window opened prematurely.', popup);
- // Open the window
- var ranDefaultAction = openerOnClick();
- this.assertTrue('Window not opened', opened);
- this.assertFalse('Ran browser default action on open', ranDefaultAction);
- this.assertNotNull('Window was null', popup.win_);
- this.assertEquals('Url incorrect', 'destination', popup.win_.url_);
- this.assertEquals('Target incorrect', '_blank', popup.win_.target_);
- this.assertEquals('Options incorrect', 'options', popup.win_.options_);
+ captures['oauth.open'].call({f: 'from', callback: function(id) {
+ opened = true;
+ cbid = id;
+ }}, 'destination', 'options');
+ this.assertNotUndefined('Window not opened.', popup);
+ this.assertTrue('Opened callback not fired.', opened);
+ this.assertEquals('Url incorrect.', 'destination', popup.url_);
+ this.assertEquals('Target incorrect.', '_blank', popup.target_);
+ this.assertEquals('Options incorrect.', 'options', popup.options_);
// Wait a bit for our events to run
window.incrementTime(1000);
- this.assertFalse('closer callback called early', closed);
+ this.assertUndefined('close callback called early.', this.rpcArguments);
// User or site closes window
- popup.win_.close();
+ popup.close();
window.incrementTime(100);
- this.assertTrue('Closer callback not called', closed);
+ this.assertEquals('Closer callback not called.', ['from', 'oauth.close', null, cbid], this.rpcArguments);
+
+ delete this.rpcArguments;
+ window.incrementTime(1000);
+ this.assertUndefined('Timer not cancelled.', this.rpcArguments);
};
PopupTest.prototype.testPopup_userClick = function() {
- var opened = false;
- var open = function() {
+ var opened = false, closed = false;
+ // Create the popup
+ var popup = new gadgets.oauth.Popup('destination', 'options', function() {
opened = true;
- };
- var closed = false;
- var close = function() {
+ }, function() {
closed = true;
- };
- // Create the popup
- var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
+ });
var openerOnClick = popup.createOpenerOnClick();
var closerOnClick = popup.createApprovedOnClick();
@@ -93,28 +111,3 @@ PopupTest.prototype.testPopup_userClick
this.assertFalse(ranDefaultAction);
this.assertTrue('Closer callback not called', closed);
};
-
-PopupTest.prototype.testTimerCancelled = function() {
- var open = function() {};
- var closeCount = 0;
- var close = function() {
- ++closeCount;
- };
-
- // Create the popup
- var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
- var openerOnClick = popup.createOpenerOnClick();
- var closerOnClick = popup.createApprovedOnClick();
-
- // Open the window
- openerOnClick();
-
- // Close the window
- popup.win_.close();
-
- // Wait a bit for our events to run
- window.incrementTime(1000);
- this.assertEquals('Wrong number of calls to close', 1, closeCount);
- window.incrementTime(1000);
- this.assertEquals('timer not cancelled', 1, closeCount);
-};