| // Copyright 2009 Google Inc. |
| // |
| // Licensed 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. |
| |
| |
| // PopupManager is a library to facilitate integration with OpenID |
| // identity providers (OP)s that support a pop-up authentication interface. |
| // To create a popup window, you first construct a popupOpener customized |
| // for your site and a particular identity provider, E.g.: |
| // |
| // var googleOpener = popupManager.createOpener(openidParams); |
| // |
| // where 'openidParams' are customized for Google in this instance. |
| // (typically you just change the openidpoint, the version number |
| // (the openid.ns parameter) and the extensions based on what |
| // the OP supports. |
| // OpenID libraries can often discover these properties |
| // automatically from the location of an XRD document. |
| // |
| // Then, you can either directly call |
| // googleOpener.popup(width, height), where 'width' and 'height' are your choices |
| // for popup size, or you can display a button 'Sign in with Google' and set the |
| //..'onclick' handler of the button to googleOpener.popup() |
| |
| var popupManager = {}; |
| |
| // Library constants |
| |
| popupManager.constants = { |
| 'darkCover' : 'popupManager_darkCover_div', |
| 'darkCoverStyle' : ['position:absolute;', |
| 'top:0px;', |
| 'left:0px;', |
| 'padding-right:0px;', |
| 'padding-bottom:0px;', |
| 'background-color:#000000;', |
| 'opacity:0.5;', //standard-compliant browsers |
| '-moz-opacity:0.5;', // old Mozilla |
| 'filter:alpha(opacity=0.5);', // IE |
| 'z-index:10000;', |
| 'width:100%;', |
| 'height:100%;' |
| ].join(''), |
| 'openidSpec' : { |
| 'identifier_select' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select', |
| 'namespace2' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0' |
| } }; |
| |
| // Computes the size of the window contents. Returns a pair of |
| // coordinates [width, height] which can be [0, 0] if it was not possible |
| // to compute the values. |
| popupManager.getWindowInnerSize = function() { |
| var width = 0; |
| var height = 0; |
| var elem = null; |
| if ('innerWidth' in window) { |
| // For non-IE |
| width = window.innerWidth; |
| height = window.innerHeight; |
| } else { |
| // For IE, |
| if (('BackCompat' === window.document.compatMode) |
| && ('body' in window.document)) { |
| elem = window.document.body; |
| } else if ('documentElement' in window.document) { |
| elem = window.document.documentElement; |
| } |
| if (elem !== null) { |
| width = elem.offsetWidth; |
| height = elem.offsetHeight; |
| } |
| } |
| return [width, height]; |
| }; |
| |
| // Computes the coordinates of the parent window. |
| // Gets the coordinates of the parent frame |
| popupManager.getParentCoords = function() { |
| var width = 0; |
| var height = 0; |
| if ('screenLeft' in window) { |
| // IE-compatible variants |
| width = window.screenLeft; |
| height = window.screenTop; |
| } else if ('screenX' in window) { |
| // Firefox-compatible |
| width = window.screenX; |
| height = window.screenY; |
| } |
| return [width, height]; |
| }; |
| |
| // Computes the coordinates of the new window, so as to center it |
| // over the parent frame |
| popupManager.getCenteredCoords = function(width, height) { |
| var parentSize = this.getWindowInnerSize(); |
| var parentPos = this.getParentCoords(); |
| var xPos = parentPos[0] + |
| Math.max(0, Math.floor((parentSize[0] - width) / 2)); |
| var yPos = parentPos[1] + |
| Math.max(0, Math.floor((parentSize[1] - height) / 2)); |
| return [xPos, yPos]; |
| }; |
| |
| // A utility class, implements an onOpenHandler that darkens the screen |
| // by overlaying it with a semi-transparent black layer. To use, ensure that |
| // no screen element has a z-index at or above 10000. |
| // This layer will be suppressed automatically after the screen closes. |
| // |
| // Note: If you want to perform other operations before opening the popup, but |
| // also would like the screen to darken, you can define a custom handler |
| // as such: |
| // var myOnOpenHandler = function(inputs) { |
| // .. do something |
| // popupManager.darkenScreen(); |
| // .. something else |
| // }; |
| // Then you pass myOnOpenHandler as input to the opener, as in: |
| // var openidParams = {}; |
| // openidParams.onOpenHandler = myOnOpenHandler; |
| // ... other customizations |
| // var myOpener = popupManager.createOpener(openidParams); |
| popupManager.darkenScreen = function() { |
| var darkCover = window.document.getElementById(window.popupManager.constants['darkCover']); |
| if (!darkCover) { |
| darkCover = window.document.createElement('div'); |
| darkCover['id'] = window.popupManager.constants['darkCover']; |
| darkCover.setAttribute('style', window.popupManager.constants['darkCoverStyle']); |
| window.document.body.appendChild(darkCover); |
| } |
| darkCover.style.visibility = 'visible'; |
| }; |
| |
| // Returns a an object that can open a popup window customized for an OP & RP. |
| // to use you call var opener = popupManager.cretePopupOpener(openidParams); |
| // and then you can assign the 'onclick' handler of a button to |
| // opener.popup(width, height), where width and height are the values of the popup size; |
| // |
| // To use it, you would typically have code such as: |
| // var myLoginCheckFunction = ... some AJAXy call or page refresh operation |
| // that will cause the user to see the logged-in experience in the current page. |
| // var openidParams = { realm : 'openid.realm', returnToUrl : 'openid.return_to', |
| // opEndpoint : 'openid.op_endpoint', onCloseHandler : myLoginCheckFunction, |
| // shouldEncodeUrls : 'true' (default) or 'false', extensions : myOpenIDExtensions }; |
| // |
| // Here extensions include any OpenID extensions that you support. For instance, |
| // if you support Attribute Exchange v.1.0, you can say: |
| // (Example for attribute exchange request for email and name, |
| // assuming that shouldEncodeUrls = 'true':) |
| // var myOpenIDExtensions = { |
| // 'openid.ax.ns' : 'http://openid.net/srv/ax/1.0', |
| // 'openid.ax.type.email' : 'http://axschema.org/contact/email', |
| // 'openid.ax.type.name1' : 'http://axschema.org/namePerson/first', |
| // 'openid.ax.type.name2' : 'http://axschema.org/namePerson/last', |
| // 'openid.ax.required' : 'email,name1,name2' }; |
| // Note that the 'ui' namespace is reserved by this library for the OpenID |
| // UI extension, and that the mode 'popup' is automatically applied. |
| // If you wish to make use of the 'language' feature of the OpenID UI extension |
| // simply add the following entry (example assumes the language requested |
| // is Swiss French: |
| // var my OpenIDExtensions = { |
| // ... // other extension parameters |
| // 'openid.ui.language' : 'fr_CH', |
| // ... }; |
| popupManager.createPopupOpener = (function(openidParams) { |
| var interval_ = null; |
| var popupWindow_ = null; |
| var that = this; |
| var shouldEscape_ = ('shouldEncodeUrls' in openidParams) ? openidParams.shouldEncodeUrls : true; |
| var encodeIfRequested_ = function(url) { |
| return (shouldEscape_ ? encodeURIComponent(url) : url); |
| }; |
| var identifier_ = ('identifier' in openidParams) ? encodeIfRequested_(openidParams.identifier) : |
| this.constants.openidSpec.identifier_select; |
| var identity_ = ('identity' in openidParams) ? encodeIfRequested_(openidParams.identity) : |
| this.constants.openidSpec.identifier_select; |
| var openidNs_ = ('namespace' in openidParams) ? encodeIfRequested_(openidParams.namespace) : |
| this.constants.openidSpec.namespace2; |
| var onOpenHandler_ = (('onOpenHandler' in openidParams) && |
| ('function' === typeof(openidParams.onOpenHandler))) ? |
| openidParams.onOpenHandler : this.darkenScreen; |
| var onCloseHandler_ = (('onCloseHandler' in openidParams) && |
| ('function' === typeof(openidParams.onCloseHandler))) ? |
| openidParams.onCloseHandler : null; |
| var returnToUrl_ = ('returnToUrl' in openidParams) ? openidParams.returnToUrl : null; |
| var realm_ = ('realm' in openidParams) ? openidParams.realm : null; |
| var endpoint_ = ('opEndpoint' in openidParams) ? openidParams.opEndpoint : null; |
| var extensions_ = ('extensions' in openidParams) ? openidParams.extensions : null; |
| |
| // processes key value pairs, escaping any input; |
| var keyValueConcat_ = function(keyValuePairs) { |
| var result = ""; |
| for (key in keyValuePairs) { |
| result += ['&', key, '=', encodeIfRequested_(keyValuePairs[key])].join(''); |
| } |
| return result; |
| }; |
| |
| //Assembles the OpenID request from customizable parameters |
| var buildUrlToOpen_ = function() { |
| var connector = '&'; |
| var encodedUrl = null; |
| var urlToOpen = null; |
| if ((null === endpoint_) || (null === returnToUrl_)) { |
| return; |
| } |
| if (endpoint_.indexOf('?') === -1) { |
| connector = '?'; |
| } |
| encodedUrl = encodeIfRequested_(returnToUrl_); |
| urlToOpen = [ endpoint_, connector, |
| 'openid.ns=', openidNs_, |
| '&openid.mode=checkid_setup', |
| '&openid.claimed_id=', identifier_, |
| '&openid.identity=', identity_, |
| '&openid.return_to=', encodedUrl ].join(''); |
| if (realm_ !== null) { |
| urlToOpen += "&openid.realm=" + encodeIfRequested_(realm_); |
| } |
| if (extensions_ !== null) { |
| urlToOpen += keyValueConcat_(extensions_); |
| } |
| urlToOpen += '&openid.ns.ui=' + encodeURIComponent( |
| 'http://specs.openid.net/extensions/ui/1.0'); |
| urlToOpen += '&openid.ui.mode=popup'; |
| return urlToOpen; |
| }; |
| |
| // Tests that the popup window has closed |
| var isPopupClosed_ = function() { |
| return (!popupWindow_ || popupWindow_.closed); |
| }; |
| |
| // Check to perform at each execution of the timed loop. It also triggers |
| // the action that follows the closing of the popup |
| var waitForPopupClose_ = function() { |
| if (isPopupClosed_()) { |
| popupWindow_ = null; |
| var darkCover = window.document.getElementById(window.popupManager.constants['darkCover']); |
| if (darkCover) { |
| darkCover.style.visibility = 'hidden'; |
| } |
| if (onCloseHandler_ !== null) { |
| onCloseHandler_(); |
| } |
| if ((null !== interval_)) { |
| window.clearInterval(interval_); |
| interval_ = null; |
| } |
| } |
| }; |
| |
| return { |
| // Function that opens the window. |
| popup: function(width, height) { |
| var urlToOpen = buildUrlToOpen_(); |
| if (onOpenHandler_ !== null) { |
| onOpenHandler_(); |
| } |
| var coordinates = that.getCenteredCoords(width, height); |
| popupWindow_ = window.open(urlToOpen, "", |
| "width=" + width + ",height=" + height + |
| ",status=1,location=1,resizable=yes" + |
| ",left=" + coordinates[0] +",top=" + coordinates[1]); |
| interval_ = window.setInterval(waitForPopupClose_, 80); |
| return true; |
| } |
| }; |
| }); |