docs: function(window, parent) {
  
  		var _this = this;
  		var p;
  		var location = window.location;
  
  		// Is this an auth relay message which needs to call the proxy?
  		p = _this.param(location.search);
  
  		// OAuth2 or OAuth1 server response?
  		if (p && p.state && (p.code || p.oauth_token)) {
  
  			var state = JSON.parse(p.state);
  
  			// Add this path as the redirect_uri
  			p.redirect_uri = state.redirect_uri || location.href.replace(/[\?\#].*$/, '');
  
  			// Redirect to the host
  			var path = _this.qs(state.oauth_proxy, p);
  
  			location.assign(path);
  
  			return;
  		}
  
  		// Save session, from redirected authentication
  		// #access_token has come in?
  		//
  		// FACEBOOK is returning auth errors within as a query_string... thats a stickler for consistency.
  		// SoundCloud is the state in the querystring and the token in the hashtag, so we'll mix the two together
  
  		p = _this.merge(_this.param(location.search || ''), _this.param(location.hash || ''));
  
  		// If p.state
  		if (p && 'state' in p) {
  
  			// Remove any addition information
  			// E.g. p.state = 'facebook.page';
  			try {
  				var a = JSON.parse(p.state);
  				_this.extend(p, a);
  			}
  			catch (e) {
  				var stateDecoded = decodeURIComponent(p.state);
  				try {
  					var b = JSON.parse(stateDecoded);
  					_this.extend(p, b);
  				}
  				catch (e) {
  					console.error('Could not decode state parameter');
  				}
  			}
  
  			// Access_token?
  			if (('access_token' in p && p.access_token) && p.network) {
  
  				if (!p.expires_in || parseInt(p.expires_in, 10) === 0) {
  					// If p.expires_in is unset, set to 0
  					p.expires_in = 0;
  				}
  
  				p.expires_in = parseInt(p.expires_in, 10);
  				p.expires = ((new Date()).getTime() / 1e3) + (p.expires_in || (60 * 60 * 24 * 365));
  
  				// Lets use the "state" to assign it to one of our networks
  				authCallback(p, window, parent);
  			}
  
  			// Error=?
  			// &error_description=?
  			// &state=?
  			else if (('error' in p && p.error) && p.network) {
  
  				p.error = {
  					code: p.error,
  					message: p.error_message || p.error_description
  				};
  
  				// Let the state handler handle it
  				authCallback(p, window, parent);
  			}
  
  			// API call, or a cancelled login
  			// Result is serialized JSON string
  			else if (p.callback && p.callback in parent) {
  
  				// Trigger a function in the parent
  				var res = 'result' in p && p.result ? JSON.parse(p.result) : false;
  
  				// Trigger the callback on the parent
  				callback(parent, p.callback)(res);
  				closeWindow();
  			}
  
  			// If this page is still open
  			if (p.page_uri) {
  				location.assign(p.page_uri);
  			}
  		}
  
  		// OAuth redirect, fixes URI fragments from being lost in Safari
  		// (URI Fragments within 302 Location URI are lost over HTTPS)
  		// Loading the redirect.html before triggering the OAuth Flow seems to fix it.
  		else if ('oauth_redirect' in p) {
  
  			location.assign(decodeURIComponent(p.oauth_redirect));
  			return;
  		}
  
  		// Trigger a callback to authenticate
  		function authCallback(obj, window, parent) {
  
  			var cb = obj.callback;
  			var network = obj.network;
  
  			// Trigger the callback on the parent
  			_this.store(network, obj);
  
  			// If this is a page request it has no parent or opener window to handle callbacks
  			if (('display' in obj) && obj.display === 'page') {
  				return;
  			}
  
  			// Remove from session object
  			if (parent && cb && cb in parent) {
  
  				try {
  					delete obj.callback;
  				}
  				catch (e) {}
  
  				// Update store
  				_this.store(network, obj);
  
  				// Call the globalEvent function on the parent
  				// It's safer to pass back a string to the parent,
  				// Rather than an object/array (better for IE8)
  				var str = JSON.stringify(obj);
  
  				try {
  					callback(parent, cb)(str);
  				}
  				catch (e) {
  					// Error thrown whilst executing parent callback
  				}
  			}
  
  			closeWindow();
  		}
  
  		function callback(parent, callbackID) {
  			if (callbackID.indexOf('_hellojs_') !== 0) {
  				return function() {
  					throw 'Could not execute callback ' + callbackID;
  				};
  			}
  
  			return parent[callbackID];
  		}
  
  		function closeWindow() {
  
  			if (window.frameElement) {
  				// Inside an iframe, remove from parent
  				parent.document.body.removeChild(window.frameElement);
  			}
  			else {
  				// Close this current window
  				try {
  					window.close();
  				}
  				catch (e) {}
  
  				// IOS bug wont let us close a popup if still loading
  				if (window.addEventListener) {
  					window.addEventListener('load', function() {
  						window.close();
  					});
  				}
  			}
  
  		}
  	}