OLD | NEW |
(Empty) | |
| 1 // Copyright 2009 Google Inc. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 |
| 16 // PopupManager is a library to facilitate integration with OpenID |
| 17 // identity providers (OP)s that support a pop-up authentication interface. |
| 18 // To create a popup window, you first construct a popupOpener customized |
| 19 // for your site and a particular identity provider, E.g.: |
| 20 // |
| 21 // var googleOpener = popupManager.createOpener(openidParams); |
| 22 // |
| 23 // where 'openidParams' are customized for Google in this instance. |
| 24 // (typically you just change the openidpoint, the version number |
| 25 // (the openid.ns parameter) and the extensions based on what |
| 26 // the OP supports. |
| 27 // OpenID libraries can often discover these properties |
| 28 // automatically from the location of an XRD document. |
| 29 // |
| 30 // Then, you can either directly call |
| 31 // googleOpener.popup(width, height), where 'width' and 'height' are your choic
es |
| 32 // for popup size, or you can display a button 'Sign in with Google' and set th
e |
| 33 //..'onclick' handler of the button to googleOpener.popup() |
| 34 |
| 35 var popupManager = {}; |
| 36 |
| 37 // Library constants |
| 38 |
| 39 popupManager.constants = { |
| 40 'darkCover' : 'popupManager_darkCover_div', |
| 41 'darkCoverStyle' : ['position:absolute;', |
| 42 'top:0px;', |
| 43 'left:0px;', |
| 44 'padding-right:0px;', |
| 45 'padding-bottom:0px;', |
| 46 'background-color:#000000;', |
| 47 'opacity:0.5;', //standard-compliant browsers |
| 48 '-moz-opacity:0.5;', // old Mozilla |
| 49 'filter:alpha(opacity=0.5);', // IE |
| 50 'z-index:10000;', |
| 51 'width:100%;', |
| 52 'height:100%;' |
| 53 ].join(''), |
| 54 'openidSpec' : { |
| 55 'identifier_select' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentif
ier_select', |
| 56 'namespace2' : 'http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0' |
| 57 } }; |
| 58 |
| 59 // Computes the size of the window contents. Returns a pair of |
| 60 // coordinates [width, height] which can be [0, 0] if it was not possible |
| 61 // to compute the values. |
| 62 popupManager.getWindowInnerSize = function() { |
| 63 var width = 0; |
| 64 var height = 0; |
| 65 var elem = null; |
| 66 if ('innerWidth' in window) { |
| 67 // For non-IE |
| 68 width = window.innerWidth; |
| 69 height = window.innerHeight; |
| 70 } else { |
| 71 // For IE, |
| 72 if (('BackCompat' === window.document.compatMode) |
| 73 && ('body' in window.document)) { |
| 74 elem = window.document.body; |
| 75 } else if ('documentElement' in window.document) { |
| 76 elem = window.document.documentElement; |
| 77 } |
| 78 if (elem !== null) { |
| 79 width = elem.offsetWidth; |
| 80 height = elem.offsetHeight; |
| 81 } |
| 82 } |
| 83 return [width, height]; |
| 84 }; |
| 85 |
| 86 // Computes the coordinates of the parent window. |
| 87 // Gets the coordinates of the parent frame |
| 88 popupManager.getParentCoords = function() { |
| 89 var width = 0; |
| 90 var height = 0; |
| 91 if ('screenLeft' in window) { |
| 92 // IE-compatible variants |
| 93 width = window.screenLeft; |
| 94 height = window.screenTop; |
| 95 } else if ('screenX' in window) { |
| 96 // Firefox-compatible |
| 97 width = window.screenX; |
| 98 height = window.screenY; |
| 99 } |
| 100 return [width, height]; |
| 101 }; |
| 102 |
| 103 // Computes the coordinates of the new window, so as to center it |
| 104 // over the parent frame |
| 105 popupManager.getCenteredCoords = function(width, height) { |
| 106 var parentSize = this.getWindowInnerSize(); |
| 107 var parentPos = this.getParentCoords(); |
| 108 var xPos = parentPos[0] + |
| 109 Math.max(0, Math.floor((parentSize[0] - width) / 2)); |
| 110 var yPos = parentPos[1] + |
| 111 Math.max(0, Math.floor((parentSize[1] - height) / 2)); |
| 112 return [xPos, yPos]; |
| 113 }; |
| 114 |
| 115 // A utility class, implements an onOpenHandler that darkens the screen |
| 116 // by overlaying it with a semi-transparent black layer. To use, ensure that |
| 117 // no screen element has a z-index at or above 10000. |
| 118 // This layer will be suppressed automatically after the screen closes. |
| 119 // |
| 120 // Note: If you want to perform other operations before opening the popup, but |
| 121 // also would like the screen to darken, you can define a custom handler |
| 122 // as such: |
| 123 // var myOnOpenHandler = function(inputs) { |
| 124 // .. do something |
| 125 // popupManager.darkenScreen(); |
| 126 // .. something else |
| 127 // }; |
| 128 // Then you pass myOnOpenHandler as input to the opener, as in: |
| 129 // var openidParams = {}; |
| 130 // openidParams.onOpenHandler = myOnOpenHandler; |
| 131 // ... other customizations |
| 132 // var myOpener = popupManager.createOpener(openidParams); |
| 133 popupManager.darkenScreen = function() { |
| 134 var darkCover = window.document.getElementById(window.popupManager.constants['
darkCover']); |
| 135 if (!darkCover) { |
| 136 darkCover = window.document.createElement('div'); |
| 137 darkCover['id'] = window.popupManager.constants['darkCover']; |
| 138 darkCover.setAttribute('style', window.popupManager.constants['darkCoverStyl
e']); |
| 139 window.document.body.appendChild(darkCover); |
| 140 } |
| 141 darkCover.style.visibility = 'visible'; |
| 142 }; |
| 143 |
| 144 // Returns a an object that can open a popup window customized for an OP & RP. |
| 145 // to use you call var opener = popupManager.cretePopupOpener(openidParams); |
| 146 // and then you can assign the 'onclick' handler of a button to |
| 147 // opener.popup(width, height), where width and height are the values of the po
pup size; |
| 148 // |
| 149 // To use it, you would typically have code such as: |
| 150 // var myLoginCheckFunction = ... some AJAXy call or page refresh operation |
| 151 // that will cause the user to see the logged-in experience in the current page
. |
| 152 // var openidParams = { realm : 'openid.realm', returnToUrl : 'openid.return_to
', |
| 153 // opEndpoint : 'openid.op_endpoint', onCloseHandler : myLoginCheckFunction, |
| 154 // shouldEncodeUrls : 'true' (default) or 'false', extensions : myOpenIDExtensi
ons }; |
| 155 // |
| 156 // Here extensions include any OpenID extensions that you support. For instance
, |
| 157 // if you support Attribute Exchange v.1.0, you can say: |
| 158 // (Example for attribute exchange request for email and name, |
| 159 // assuming that shouldEncodeUrls = 'true':) |
| 160 // var myOpenIDExtensions = { |
| 161 // 'openid.ax.ns' : 'http://openid.net/srv/ax/1.0', |
| 162 // 'openid.ax.type.email' : 'http://axschema.org/contact/email', |
| 163 // 'openid.ax.type.name1' : 'http://axschema.org/namePerson/first', |
| 164 // 'openid.ax.type.name2' : 'http://axschema.org/namePerson/last', |
| 165 // 'openid.ax.required' : 'email,name1,name2' }; |
| 166 // Note that the 'ui' namespace is reserved by this library for the OpenID |
| 167 // UI extension, and that the mode 'popup' is automatically applied. |
| 168 // If you wish to make use of the 'language' feature of the OpenID UI extension |
| 169 // simply add the following entry (example assumes the language requested |
| 170 // is Swiss French: |
| 171 // var my OpenIDExtensions = { |
| 172 // ... // other extension parameters |
| 173 // 'openid.ui.language' : 'fr_CH', |
| 174 // ... }; |
| 175 popupManager.createPopupOpener = (function(openidParams) { |
| 176 var interval_ = null; |
| 177 var popupWindow_ = null; |
| 178 var that = this; |
| 179 var shouldEscape_ = ('shouldEncodeUrls' in openidParams) ? openidParams.should
EncodeUrls : true; |
| 180 var encodeIfRequested_ = function(url) { |
| 181 return (shouldEscape_ ? encodeURIComponent(url) : url); |
| 182 }; |
| 183 var identifier_ = ('identifier' in openidParams) ? encodeIfRequested_(openidPa
rams.identifier) : |
| 184 this.constants.openidSpec.identifier_select; |
| 185 var identity_ = ('identity' in openidParams) ? encodeIfRequested_(openidParams
.identity) : |
| 186 this.constants.openidSpec.identifier_select; |
| 187 var openidNs_ = ('namespace' in openidParams) ? encodeIfRequested_(openidParam
s.namespace) : |
| 188 this.constants.openidSpec.namespace2; |
| 189 var onOpenHandler_ = (('onOpenHandler' in openidParams) && |
| 190 ('function' === typeof(openidParams.onOpenHandler))) ? |
| 191 openidParams.onOpenHandler : this.darkenScreen; |
| 192 var onCloseHandler_ = (('onCloseHandler' in openidParams) && |
| 193 ('function' === typeof(openidParams.onCloseHandler))) ? |
| 194 openidParams.onCloseHandler : null; |
| 195 var returnToUrl_ = ('returnToUrl' in openidParams) ? openidParams.returnToUrl
: null; |
| 196 var realm_ = ('realm' in openidParams) ? openidParams.realm : null; |
| 197 var endpoint_ = ('opEndpoint' in openidParams) ? openidParams.opEndpoint : nul
l; |
| 198 var extensions_ = ('extensions' in openidParams) ? openidParams.extensions : n
ull; |
| 199 |
| 200 // processes key value pairs, escaping any input; |
| 201 var keyValueConcat_ = function(keyValuePairs) { |
| 202 var result = ""; |
| 203 for (key in keyValuePairs) { |
| 204 result += ['&', key, '=', encodeIfRequested_(keyValuePairs[key])].join('')
; |
| 205 } |
| 206 return result; |
| 207 }; |
| 208 |
| 209 //Assembles the OpenID request from customizable parameters |
| 210 var buildUrlToOpen_ = function() { |
| 211 var connector = '&'; |
| 212 var encodedUrl = null; |
| 213 var urlToOpen = null; |
| 214 if ((null === endpoint_) || (null === returnToUrl_)) { |
| 215 return; |
| 216 } |
| 217 if (endpoint_.indexOf('?') === -1) { |
| 218 connector = '?'; |
| 219 } |
| 220 encodedUrl = encodeIfRequested_(returnToUrl_); |
| 221 urlToOpen = [ endpoint_, connector, |
| 222 'openid.ns=', openidNs_, |
| 223 '&openid.mode=checkid_setup', |
| 224 '&openid.claimed_id=', identifier_, |
| 225 '&openid.identity=', identity_, |
| 226 '&openid.return_to=', encodedUrl ].join(''); |
| 227 if (realm_ !== null) { |
| 228 urlToOpen += "&openid.realm=" + encodeIfRequested_(realm_); |
| 229 } |
| 230 if (extensions_ !== null) { |
| 231 urlToOpen += keyValueConcat_(extensions_); |
| 232 } |
| 233 urlToOpen += '&openid.ns.ui=' + encodeURIComponent( |
| 234 'http://specs.openid.net/extensions/ui/1.0'); |
| 235 urlToOpen += '&openid.ui.mode=popup'; |
| 236 return urlToOpen; |
| 237 }; |
| 238 |
| 239 // Tests that the popup window has closed |
| 240 var isPopupClosed_ = function() { |
| 241 return (!popupWindow_ || popupWindow_.closed); |
| 242 }; |
| 243 |
| 244 // Check to perform at each execution of the timed loop. It also triggers |
| 245 // the action that follows the closing of the popup |
| 246 var waitForPopupClose_ = function() { |
| 247 if (isPopupClosed_()) { |
| 248 popupWindow_ = null; |
| 249 var darkCover = window.document.getElementById(window.popupManager.constan
ts['darkCover']); |
| 250 if (darkCover) { |
| 251 darkCover.style.visibility = 'hidden'; |
| 252 } |
| 253 if (onCloseHandler_ !== null) { |
| 254 onCloseHandler_(); |
| 255 } |
| 256 if ((null !== interval_)) { |
| 257 window.clearInterval(interval_); |
| 258 interval_ = null; |
| 259 } |
| 260 } |
| 261 }; |
| 262 |
| 263 return { |
| 264 // Function that opens the window. |
| 265 popup: function(width, height) { |
| 266 var urlToOpen = buildUrlToOpen_(); |
| 267 if (onOpenHandler_ !== null) { |
| 268 onOpenHandler_(); |
| 269 } |
| 270 var coordinates = that.getCenteredCoords(width, height); |
| 271 popupWindow_ = window.open(urlToOpen, "", |
| 272 "width=" + width + ",height=" + height + |
| 273 ",status=1,location=1,resizable=yes" + |
| 274 ",left=" + coordinates[0] +",top=" + coordinates[1]); |
| 275 interval_ = window.setInterval(waitForPopupClose_, 80); |
| 276 return true; |
| 277 } |
| 278 }; |
| 279 }); |
OLD | NEW |