(function() {

	// Only initialize once
	if (typeof window.__EspressoLiveHasInitialized__ !== "undefined") { return; }
	window.__EspressoLiveHasInitialized__ = true;
	
	// Only served pages (project or otherwise)
	if (window.location.protocol == "file:") {
		return;
	}
	
	// Utilities
	function goodEnoughUUID() {
		var time = new Date().getTime();
		if (window.performance && typeof window.performance.now == 'function') {
			time += performance.now();
		}
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
			var r = (time + Math.random() * 16) % 16 | 0;
			time = Math.floor(time / 16);
			return (c == 'x' ? r: (r & 0x3 | 0x8)).toString(16).toUpperCase();
		});
	};
	
	function invokeAfterDOMLoads(callback) {
		if (/interactive|complete/.test(document.readyState)) {
			callback && callback();
		}
		else {
			document.addEventListener("readystatechange", function() {
				if (/interactive|complete/.test(document.readyState)) {
					callback && callback();
					callback = null;
				}
			});
		}
	};
	
	function invokeAfterResourcesLoad(callback) {
		callback = callback || function() {};
		
		if (document.readyState === "complete") {
			callback();
		}
		else {
			window.addEventListener("load", callback);
		}
	};
	
	
	// Say hello to Espresso
	var EspressoLive = window.__EspressoLive__;
	EspressoLive.identifier = goodEnoughUUID();
	EspressoLive.hello({
		id: (typeof EspressoLive.identifier !== "undefined" ? EspressoLive.identifier : ""),
		inapp: (typeof window.__EspressoLiveIsInAppPreview__ !== "undefined") ? window.__EspressoLiveIsInAppPreview__ : false,
		ua: navigator.userAgent,
		href: window.location.href,
		path: window.location.pathname
	}, function(result) {
		var prefix = result.prefix;
		if (typeof prefix == "string") {
			EspressoLive.titlePrefix = prefix;
		}
		var suffix = result.suffix;
		if (typeof suffix == "string") {
			EspressoLive.titleSuffix = suffix;
		}
		updateDocumentTitle();
	});
	
	
	// Set up title prefix/suffixing
	EspressoLive.titlePrefix = "";
	EspressoLive.titleSuffix = "";
	function updateDocumentTitle() {
		var prefix = EspressoLive.titlePrefix, suffix = EspressoLive.titleSuffix;
		var title = document.title;
		if (prefix && prefix.length > 0 && title.indexOf(prefix) !== 0) {
			document.title = EspressoLive.titlePrefix + title + EspressoLive.titleSuffix;
		}
	}
	
	invokeAfterDOMLoads(function() {
		new MutationObserver(function(mutations) {
			updateDocumentTitle();
		}).observe(document.querySelector('title'), { subtree: true, characterData: true });
	});
	
	
	// Espresso Live / client side
	function EspressoPreviewPage()
	{
		var _debugLog = console.log;
		
		var _overrides = {};
		var _overridesNeedApplying = false;
		var _activeOverrideURLs = [];
		
		var _urlFuzzyToNeedsReloadMap = {};
		var _urlIsReloadingMap = {};
		var _urlToSelectorsMap = {};
		var _urlNeedsUpdatedSelectorsMap = {};
		
		
		function _getStyleSheetURLs() {
			return Object.keys(_urlToSelectorsMap);
		}
		
 		var _isProcessingStyleSheets = 0;
 		var _receivedRecursiveProcessingRequests = false;
		var _needsXrayReloadAfterProcessingStyleSheets = false;
		function _processStyleSheets(recursive) {
			
			if (_isProcessingStyleSheets > 0) { _receivedRecursiveProcessingRequests = true; return; }
 			if (Object.keys(_urlIsReloadingMap).length) { return; }
			_isProcessingStyleSheets++;
 			
 			
			// Find and/or reload reloadable style sheets (maps to true if it actually changed)
			var reloadedFuzzies = [];
			if (_urlFuzzyToNeedsReloadMap["*"] === true) {
				//_debugLog("Reloading all style sheets...");
			}
			
			function styleSheetForNode(node) {
				var documentStyleSheets = document.styleSheets;
				for (var i = 0, length = documentStyleSheets.length; i < length; i++) {
					if (documentStyleSheets[i].ownerNode == node) {
						return documentStyleSheets[i];
					}
				}
				return null;
			}
			
			function reloadOverrideStyleNode(node, force) {
				var url = node.getAttribute("espresso-override-href");
				if (!url) {
					return;
				}
				
				var needsReloadFuzzy = _needsReloadForURL(url);
				if (needsReloadFuzzy || force) {
					reloadedFuzzies.push(needsReloadFuzzy);
				}
				else {
					return;
				}
				
				_willReloadURL(url, "override");
				EspressoLive.loadContentsForOverride(url, function(result) {
					if (result) {
						node._espresso_isOverrideActive = true;
						node.textContent = result.text;
						var imports = result.imports;
						
						var oldImportNodes = node._espresso_importNodes || [];
						var newImportNodes = [];
						if (imports) {
							
							// Build style nodes for all @imports, in reverse order so that they end up in the correct source order
							var nextImportNode = null;
							for (var i = imports.length - 1; i >= 0; i--) {
								var importURL = imports[i];
								var importNode = null;
								for (var j = 0, length = oldImportNodes.length; j < length; j++) {
									if (oldImportNodes[j].getAttribute("espresso-override-href") == importURL) {
										importNode = oldImportNodes[j];
										oldImportNodes.splice(j, 1);
										break;
									}
								}
								
								importNode = importNode || insertOverrideStyleNode(importURL, node, nextImportNode);
								if (importNode) {
									importNode._espresso_importingNode = node;
									newImportNodes.push(importNode);
								}
								
								nextImportNode = importNode;
							}
						}
						node._espresso_importNodes = newImportNodes;
						
						for (var i = 0, length = oldImportNodes.length; i < length; i++) {
							try {
								oldImportNodes[i]._espresso_importingNode = null;
								oldImportNodes[i].parentNode.removeChild(oldImportNodes[i]);
							}
							catch(e) {}
						}
					}
					_finishReloadingURL(url);
				});
			}
			
			function insertOverrideStyleNode(url, ownerNode, nextNode, preserveOnDeactivate) {
				var styleNode = document.createElement("style");
				styleNode._espresso_isOverrideActive = true;
				styleNode.setAttribute("espresso-override-href", url);
				styleNode.deactivateOverride = function(preserve) {
					//_debugLog("Deactivating override: " + this.getAttribute("espresso-override-href"));
					
					var importNodes = this._espresso_importNodes;
					this._espresso_importNodes = null;
					if (importNodes) {
						for (var i = 0, length = importNodes.length; i < length; i++) {
							importNodes[i].deactivateOverride();
						}
					}
					
					try {
						_willReloadURL(url, "override");
						
						var originalNode = this._espresso_originalLinkNode;
						if (originalNode) {
							this.parentNode.insertBefore(originalNode, this);
						}
						
						this._espresso_isOverrideActive = false;
						if (preserve || preserveOnDeactivate) {
							this.textContent = "@import url("+url+");";
						}
						else {
							this.parentNode.removeChild(this);
						}
						
						_finishReloadingURL(url);
					}
					catch(e) {}
				};
				
				try {
					ownerNode.parentNode.insertBefore(styleNode, nextNode || ownerNode);
					//_debugLog("Applied override: " + url);
				}
				catch(e) {
					console.log("Couldn't apply override for " + url + " -- " + e);
					return null;
				}
				
				reloadOverrideStyleNode(styleNode, true);
				
				var index = _activeOverrideURLs.indexOf(url);
				if (index < 0) {
					_activeOverrideURLs.push(url);
				}
				
				return styleNode;
			}
			
			function forEachImportRule(styleSheet, fn) {
				
				function absoluteURL(url) {
					var a = document.createElement('a');
					a.href = url;
					return a.cloneNode(false).href;
				}
				
				var length = 0;
				try { length = styleSheet.cssRules.length; }
				catch(e) { return false; }
				
				for (var i = 0, length = styleSheet.cssRules.length; i < length; i++) {
					var rule = styleSheet.cssRules[i];
					if (rule instanceof CSSImportRule) {
						var url = (rule.styleSheet ? rule.styleSheet.href : null) || (rule.href ? absoluteURL(rule.href) : null);
						var action = fn(rule, url);
						if (action === "delete") {
							try { styleSheet.deleteRule(i); i--; }
							catch(e) { console.log("Couldn't delete @import: " + url); }
						}
						else if (action === "refresh") {
							try {
								styleSheet.insertRule("@import url("+_refreshURL(url)+");", i);
								styleSheet.deleteRule(i+1);
							}
							catch(e) { console.log("Couldn't refresh @import: " + url); }
						}
					}
					else if (rule instanceof CSSStyleRule) {
						break; // @imports have to precede style rules
					}
				}
				
				return true;
			}
			
			var overview = [];
			var oldSelectorsMap = _urlToSelectorsMap;
			_urlToSelectorsMap = {};
			
			document.querySelectorAll("link[rel='stylesheet'], style").forEach(function(node) {
				
				var url = node.href;
				url = (url ? _defreshURL(url) : url) || node.getAttribute("espresso-override-href");
				var isOverride = node.getAttribute("espresso-override-href") != null;
				
				if (node._espresso_isReloadClone) {
					return;
				}
				
				// Reload and activate overrides
				var nodeStyleSheet = styleSheetForNode(node);
				var nodeName = node.nodeName.toLowerCase();
				if (url && nodeName == "link") {
					
					if (_shouldOverrideURL(url)) {
						// Activate override for link
						var overrideNode = insertOverrideStyleNode(url, node);
						if (overrideNode) {
							overrideNode._espresso_originalLinkNode = node;
							try { node.parentNode.removeChild(node); }
							catch(e) {}
						}
					}
					else {
						// Reload link
						var linkNeedsReloadFuzzy = _needsReloadForURL(url);
						var importsNeedReload = false;
						
						if (nodeStyleSheet) {
							forEachImportRule(nodeStyleSheet, function(rule, url) {
								if (url) {
									var importNeedsReloadFuzzy = _needsReloadForURL(url);
									if (importNeedsReloadFuzzy) {
										reloadedFuzzies.push(importNeedsReloadFuzzy);
										if (_urlFuzzyToNeedsReloadMap[linkNeedsReloadFuzzy] !== "if-override") {
											importsNeedReload = true; // Because @import rules have no support for load events, we reload the entire link to prevent flickering
										}
									}
								}
							});
						}
						
						if (linkNeedsReloadFuzzy) {
							reloadedFuzzies.push(linkNeedsReloadFuzzy);
						}
						
						if ((linkNeedsReloadFuzzy || importsNeedReload) && _urlFuzzyToNeedsReloadMap[linkNeedsReloadFuzzy] !== "if-override") {
							
							// Abort running reload (if any)
							if (node._espresso_reloadingCloneNode) {
								node._espresso_reloadingCloneNode.cleanup(true);
							}
							
							// Clone the link to 1) get load events and 2) reduce flickering between reloads
							var clone = node.cloneNode();
							clone._espresso_isReloadClone = true;
							clone._espresso_originalLinkNode = node;
							clone.href = _refreshURL(url);
							node._espresso_reloadingCloneNode = clone;
							
							clone.onerror = function() {
								this.cleanup(true);
							};
							
							clone.onload = function() {
								// Remove the original and prepare the replacement for future reloads
								try { this.parentNode.removeChild(this._espresso_originalLinkNode); }
								catch(e) { this.cleanup(true); return; }
								
								this._espresso_isReloadClone = false;
								this.cleanup();
							};
							
							clone.cleanup = function(remove) {
								this.onload = null;
								this.onerror = null;
								
								var node = this._espresso_originalLinkNode;
								if (node) {
									node._espresso_reloadingCloneNode = null;
									this._espresso_originalLinkNode = null;
								}
								
								if (remove) {
									try { this.parentNode.removeChild(this); }
									catch(e) {}
								}
								
								_finishReloadingURL(_defreshURL(this.href));
							};
							
							// Start loading
							_willReloadURL(url, "link");
							clone._espresso_originalLinkNode.parentNode.insertBefore(clone, clone._espresso_originalLinkNode);
						}
					}
				}
				else if (nodeName == "style") {
					if (isOverride) {
						// Reload or remove overrides
						if (url && _shouldOverrideURL(url)) {
							// Reload active overrides
							reloadOverrideStyleNode(node, _overridesNeedApplying);
						}
						else if (!node._espresso_importingNode) {
							// Remove root overrides
							node.deactivateOverride();
						}
						else if (node._espresso_isOverrideActive) {
							// Reset child overrides if needed (they are updated and removed by the root)
							node.deactivateOverride(true);
						}
					}
					else {
						// Activate override for @imports inside style
						forEachImportRule(nodeStyleSheet, function(rule, url) {
							if (url && _shouldOverrideURL(url)) {
								var overrideNode = insertOverrideStyleNode(url, node, null, true);
								if (overrideNode) {
									return "delete";
								}
							}
						});
					}
				}
				
				
				// Make sure we reference the correct (possibly updated or overridden) style sheet when reloading selectors below
				nodeStyleSheet = styleSheetForNode(node);
				
				
				// Generate an overview item and update selectors
				if (nodeStyleSheet) {
					
					function addToOverview(styleSheet, url, parentURL) {
						
						var canReadRules = true;
						try { styleSheet.cssRules.length; }
						catch (e) { canReadRules = false; }
						
						var info = {
							"url": url,
							"override-supported": true,
							"override-active": (styleSheet && styleSheet.ownerNode && styleSheet.ownerNode._espresso_isOverrideActive === true),
							"domreadable": canReadRules
						};
						
						if (parentURL) {
							parentURL = _defreshURL(parentURL) || parentURL;
							info["imported-by"] = parentURL;
						}
						else if (styleSheet && styleSheet.ownerNode && styleSheet.ownerNode._espresso_importingNode) {
							parentURL = styleSheet.ownerNode._espresso_importingNode.getAttribute("espresso-override-href");
							if (parentURL) {
								info["imported-by"] = parentURL;
							}
						}
						
						overview.push(info);
						
						var oldSelectors = oldSelectorsMap[url];
						_urlToSelectorsMap[url] = oldSelectors || [];
						if ((!oldSelectors || (_urlNeedsUpdatedSelectorsMap[url] === true)) && styleSheet) {
							_loadUpdatedSelectorsForLoadedStyleSheet(styleSheet);
						}
					}
					
					var isDeactivatedImportOverride = node._espresso_isOverrideActive === false && nodeName === "style";
					if (!isDeactivatedImportOverride) {
						var parentURL = url;
						forEachImportRule(nodeStyleSheet, function(rule, url) {
							if (url) {
								addToOverview(rule.styleSheet, url, parentURL);
							}
						});
					}
					
					if (url) {
						addToOverview(nodeStyleSheet, url);
					}
				}
			});
			
			// Note reloaded style sheets
			for (var i = 0, length = reloadedFuzzies.length; i < length; i++) {
				delete _urlFuzzyToNeedsReloadMap[reloadedFuzzies[i]];
			}
			
			_overridesNeedApplying = false;
			_isProcessingStyleSheets--;
			
			// Update the overview
			EspressoLive.updateStyleSheetsOverview(overview);
			
			// Update Xray
			if (_needsXrayReloadAfterProcessingStyleSheets) {
				_needsXrayReloadAfterProcessingStyleSheets = false;
				if ($xray.enabled) {
					$xray.reload();
				}
			}
			
			if (_receivedRecursiveProcessingRequests) {
				_receivedRecursiveProcessingRequests = false;
				if (!recursive) {
					_processStyleSheets(true);
				}
			}
		}
		
		function _randomRefreshToken() {
			var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
			var output = "";
			for (var i = 0; i < 7; i++) {
				output += chars[Math.floor(Math.random() * chars.length)];
			}
			
			return output;
		}
		
		function _refreshURL(url) {
			url = _defreshURL(url);
			if (url.indexOf("?") !== -1) {
				url += "&__espresso_refresh__=" + _randomRefreshToken();
			}
			else {
				url += "?__espresso_refresh__=" + _randomRefreshToken();
			}
			
			return url;
		}
		
		function _defreshURL(url) {
			if (url) {
				url = url.replace(/&*__espresso_refresh__=([A-Za-z0-9])+&*/, "");
				if (url.indexOf("?") === url.length - 1) {
					url = url.substring(0, url.length - 1);
				}
			}
			
			return url;
		}
		
		function _shouldOverrideURL(url) {
			
			// Fuzzies sent from Espresso are always unescaped
			url = unescape(url);
			
			var wildcard = _overrides[url];
			if (wildcard === true || wildcard === false) {
				return true;
			}
			
			var queryIndex = url.indexOf("?");
			if (queryIndex >= 0) {
				url = url.substring(0, queryIndex);
				wildcard = _overrides[url];
				return wildcard === true;
			}
			
			return false;
		}
		
		function _needsReloadForURL(url) {
			if (_urlIsReloadingMap[url]) {
				return null;
			}
			
			// Fuzzies sent from Espresso are always unescaped
			url = unescape(url);
			
			for (var fuzzy in _urlFuzzyToNeedsReloadMap) {
				if ((url.indexOf(fuzzy) >= 0 || fuzzy === "*")) {
					var needsReload = _urlFuzzyToNeedsReloadMap[fuzzy];
					if (needsReload === true || needsReload === "if-override") {
						return fuzzy;
					}
				}
			}
			
			return null;
		}
		
		function _willReloadURL(url, type) {
			//_debugLog("Will reload ("+type+") " + url);
			_urlIsReloadingMap[url] = true;
		}
		
		function _finishReloadingURL(url) {
			delete _urlIsReloadingMap[url];
			//_debugLog("Done reloading " + url + " -> " + JSON.stringify(_urlIsReloadingMap));
			_urlNeedsUpdatedSelectorsMap[url] = true;
			_processStyleSheets();
		}
		
		function _loadUpdatedSelectorsForLoadedStyleSheet(styleSheet) {
			var ownerNode = styleSheet.ownerNode;
			var url = _defreshURL(styleSheet.href || (ownerNode ? ownerNode.getAttribute("espresso-override-href") : null));
			if (!url) {
				return;
			}
			
			_urlNeedsUpdatedSelectorsMap[url] = false;
			
			// See if the security policy allows access to cssRules
			try { styleSheet.cssRules.length; }
			catch (e) {
				EspressoLive.loadSelectorsForStyleSheet(url, function(result) {
					//_debugLog("loadSelectorsForStyleSheet: " + result);
					_updateSelectors(url, result);
				});
				return;
			}
			
			var selectors = [];
			
			function processCSSRules(object) {
				
				var cssRules = object.cssRules;
				for (var i = 0, length = cssRules.length; i < length; i++) {
					
					var rule = cssRules[i];
					if (rule instanceof CSSImportRule) {
						
						if (rule.styleSheet) {
							_loadUpdatedSelectorsForLoadedStyleSheet(rule.styleSheet);
						}
						else {
							// We need to wait for the stylesheet to load
							var maxNumberOfTries = 100;
							var numberOfTries = 0;
							var styleSheetLoadInterval = setInterval(function() {
								numberOfTries++;
								
								if (rule.styleSheet) {
									_loadUpdatedSelectorsForLoadedStyleSheet(rule.styleSheet);
									clearInterval(styleSheetLoadInterval);
								}
								else if (numberOfTries > maxNumberOfTries) {
									clearInterval(styleSheetLoadInterval);
								}
							}, 100);
						}
					}
					else if (rule instanceof CSSMediaRule) {
						processCSSRules(rule);
					}
					else if (rule.selectorText) {
						selectors.push(rule.selectorText);
					}
				}
			}
			
			processCSSRules(styleSheet);
			
			// Deactivated override <style> elements contain a single @import pointing to the same URL. Invoking processCSSRules will find and load selectors for it, and updating the selectors found in the block (none, since there's just the import) would merely overwrite that result.
			var isDeactivatedImportOverride = ownerNode ? (ownerNode._espresso_isOverrideActive === false && ownerNode.nodeName.toLowerCase() === "style") : false;
			if (!isDeactivatedImportOverride) {
				_updateSelectors(url, selectors);
			}
		}
		
		function _updateSelectors(url, selectors) {
			url = _defreshURL(url || "");
			selectors = (selectors instanceof Array) ? selectors : [];
			_urlToSelectorsMap[url] = selectors;
			delete _urlNeedsUpdatedSelectorsMap[url];
			
			// We can now inspect the current stylesheet
			if (_isProcessingStyleSheets > 0) {
				_needsXrayReloadAfterProcessingStyleSheets = true;
			}
			else if ($xray.enabled) {
				$xray.reload();
			}
		}
		
		function _appliedSelectorsForElement(element) {
			var matchedSelectors = null;
			var test = element.matches || element.webkitMatchesSelector || element.mozMatchesSelector || element.msMatchesSelector;
			
			if (!test) {
				return null;
			}
			
			for (var url in _urlToSelectorsMap) {
				var stylesheetMatches = [];
				(_urlToSelectorsMap[url] || []).forEach(function(selector) {
					// Strip pseudo classes and elements when matching
					try {
						var testedSelector = selector.replace(/\:(hover|disabled|active|visited|focus)/g, "");
						testedSelector = testedSelector.replace(/\:\:(before|after)/g, ":not(:not(*))");
						if (test.call(element, testedSelector)) {
							stylesheetMatches.push(selector);
						}
					}
					catch(e) {}
				});
				
				if (stylesheetMatches.length > 0) {
					matchedSelectors = matchedSelectors || {};
					matchedSelectors[url] = stylesheetMatches;
				}
			}
			
			return matchedSelectors || {};
		}
		
		// Public API
		
		Object.defineProperty(this, "styleSheetURLs", {
			readonly: true,
			enumerable: true,
			get: _getStyleSheetURLs
		});
		
		Object.defineProperty(this, "overrides", {
			get: function() {
				return _overrides;
			},
			set: function(newValue) {
				var oldValue = _overrides;
				_overrides = newValue || {};
				
				if (JSON.stringify(_overrides) != JSON.stringify(oldValue)) {
					_overridesNeedApplying = true;
					_processStyleSheets();
				}
			}
		});
		
		Object.defineProperty(this, "overrideStyleSheetURLs", {
			readonly: true,
			enumerable: true,
			get: function() { return _activeOverrideURLs; }
		});
		
		this.reloadStyleSheets = function(fuzzies, overridesOnly) {
			fuzzies = (fuzzies instanceof Array) ? fuzzies : [];
			//_debugLog("reloadStyleSheets (overrides only:"+overridesOnly+"): " + fuzzies);
			for (var i = 0, length = fuzzies.length; i < length; i++) {
				_urlFuzzyToNeedsReloadMap[fuzzies[i]] = overridesOnly ? "if-override": true;
			}
			
			_processStyleSheets();
			
			//_debugLog("sending didReloadStyleSheets");
			EspressoLive.didReloadStyleSheets();
		};
		
		this.appliedSelectorsForElement = _appliedSelectorsForElement;
		
		// Finish initializing once the page and resources load
		invokeAfterDOMLoads(function() {
			updateDocumentTitle();
			_overridesNeedApplying = true;
			_processStyleSheets();
		});
		
		invokeAfterResourcesLoad(function() {
			_overridesNeedApplying = true;
			_processStyleSheets();
		});
	}
	
	
	// Espresso Live / Xray
	function EspressoXray()
	{
		// TODO: manage this color dynamically via the WebSocket so that we can match the user's selection color; this line needs to not exist at all
		// TODO: figure out how to manage the color of the "link" button when the color shifts based on the selection color
		var $SELECTION_RGB = '17, 108, 214';
		
		// Root elements
		var _pageOverlay = document.createElement("div");
		var _pageEventInterceptOverlay = document.createElement("div");
		var _linkIndicator = document.createElement("a");
		
		// _pageOverlay > _elementIndicator
		var _elementIndicator = document.createElement("div");
		_pageOverlay.appendChild(_elementIndicator);
		
		// _elementIndicator > _paddingOverlay, _marginOverlay
		var _paddingOverlay = document.createElement("div");
		var _marginOverlay = document.createElement("div");
		_elementIndicator.appendChild(_paddingOverlay);
		_elementIndicator.appendChild(_marginOverlay);
			 
			 
		var _topMarginOverlay = document.createElement("div"),
			_topMarginTail = document.createElement("div"),
			_topMarginHead = document.createElement("div");
		var _rightMarginOverlay = document.createElement("div"),
			_rightMarginTail = document.createElement("div"),
			_rightMarginHead = document.createElement("div");
		var _bottomMarginOverlay = document.createElement("div"),
			_bottomMarginTail = document.createElement("div"),
			_bottomMarginHead = document.createElement("div");
		var _leftMarginOverlay = document.createElement("div"),
			_leftMarginTail = document.createElement("div"),
			_leftMarginHead = document.createElement("div");
		_marginOverlay.appendChild(_topMarginOverlay);
		_marginOverlay.appendChild(_rightMarginOverlay);
		_marginOverlay.appendChild(_bottomMarginOverlay);
		_marginOverlay.appendChild(_leftMarginOverlay);
		
		_topMarginOverlay.appendChild(_topMarginTail);
		_topMarginTail.appendChild(_topMarginHead);
		_rightMarginOverlay.appendChild(_rightMarginTail);
		_rightMarginTail.appendChild(_rightMarginHead);
		_bottomMarginOverlay.appendChild(_bottomMarginTail);
		_bottomMarginTail.appendChild(_bottomMarginHead);
		_leftMarginOverlay.appendChild(_leftMarginTail);
		_leftMarginTail.appendChild(_leftMarginHead);
		
		var _enabled = false;
		var _dragging = false;
		var _selectedElement = null;
		var _prevElement = null;
		// Note that there's a slight timeout delay added when the animation is actually triggered, in order to ensure that it actually animates (CSS translations don't stick otherwise)
		var _fadeTransition = "opacity .1s ease-out";
		
		// The styling of our link indicator doesn't change (aside from setting display:none), so set it here
		_styleElement(_linkIndicator, {
			position: "absolute",
			width: "16px",
			height: "16px",
			"background-repeat": "no-repeat",
			"background-image": "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA2JJREFUWAm9Vz1MFEEUfjOKtxf1rhE14YjJqYUCQmFiwEZRQQ2NLSaGxsIYAlf4Q2WsiFKAEhITGwtDa2EiRD21URsThQMt1EuMh1GvAkLY88KM7+3tLnt3u3tzy+k2M/v+vrdvZt5+w0DxiV97F9X10GnGWCdIaAWQcXSNmO5LACwtAWZRntS03HT61uFFldCsklEsMb8PhLiOwc+jrVbJ3tTrjMEkMD6cGW364ufjmUAs8SYMInpTghjEAHV+QXx0eQZ8DPjijcxox6qbnWsCjf2pvYLDI3RocXMKIEtxAee+j7d8LfUtS6Chf64VuHyGhvWlxht8z4JgpxbGm2eccYoSML/87T8AtzCzWIl2ZyW4paE1N8te6y+3IGisJwxjf5lSOwHacCir1Zqb4V2HFhPLUBpLQEdNCvERJVXv9kudO2Cgeye8+rQMickFWP0jXFFLhHnG+UE6ooUKSDEUBJyCEvi2EIeetig8uLgHwlvWi1oC6nyto95CAkYdLqeHfuJctck4A8G9vkYD3BK+/rwCffe/qVRCD2m53Zzaa1BwAqWyE6j1HN2/VbUSmr6qncF6sROWc5CR1py+OFASDDo57sJDQYCdPl5JTFyIOc3K5ozJ1s3mX61MmRlrLpNVK+hqjsDlk/Uw8Tzr7ipZnLZs1F1bG+lQzy4jCfdoMqJ0Ztyd1aWRsDcMaZSIgzpcseVEMgvDj38VC+03toR7gKVxH5T1/9jgnG2mMqEGRI2IjqH1TM8u+YCjFZNpXqBRlkuw0Q2cjmX/w4xvQCnZDC6BTPpaVVB6gSt1QwkvOBFIxNAr4HiqR3sbispeTSvWwvoUJ/ZqEEhPCH/FsQPbbYMqwIEwCbtwPpC9YpS8HamKyd2nv2ElJ2AKN5xS2Qux88SYaWpTstjA/G1kwFeqwA5sikx5JHOn6SoFWO8QSJ3xPRU4qrpjimi6ZW5XgAT/g5QiE+pwXlbWK4AJGGyV8S6cevw9KM3AT1aKtW4nOEUqSoAEC2NNH4g647SWy0EXk/Yf423vCcP5lCVASqoE48tHaLPga6DTYYLQ1WyEYjnvAqbOGIr2gFNhzY3LKZJWKaEXZaq8ceOXUysBazSu57m6syA3HScmA0gmsI2bXIIt0o+FejuwtZdaKP9E9Xr+F8TJPPfHymCYAAAAAElFTkSuQmCC)",
			"background-size": "16px 16px",
			top: "0px",
			left: "0px",
			cursor: "pointer",
			display: "none",
			"z-index": 2147483647
		});
		
		// The styling of the head/tail elements never changes, so set it here
		_styleElement(_topMarginTail, {
			position: "absolute",
			left: "50%",
			top: "0px",
			bottom: "0px",
			"margin-left": "-1px",
			background: "rgb(" + $SELECTION_RGB + ")",
			width: "1px",
			overflow: "visible"
		});
		_styleElement(_topMarginHead, {
			position: "absolute",
			// Due to how the browser rounds the half pixel sizes, we have to offset the top and left heads
			top: "-.5px",
			left: "-4px",
			width: "0px",
			height: "0px",
			"border-right": "4.5px solid transparent",
			"border-left": "4.5px solid transparent",
			"border-bottom": "4.5px solid rgb(" + $SELECTION_RGB + ")"
		});
		_styleElement(_rightMarginTail, {
			position: "absolute",
			top: "50%",
			right: "0px",
			left: "0px",
			"margin-top": "-1px",
			background: "rgb(" + $SELECTION_RGB + ")",
			height: "1px",
			overflow: "visible"
		});
		_styleElement(_rightMarginHead, {
			position: "absolute",
			top: "-4px",
			right: "0px",
			width: "0px",
			height: "0px",
			"border-top": "4.5px solid transparent",
			"border-bottom": "4.5px solid transparent",
			"border-left": "4.5px solid rgb(" + $SELECTION_RGB + ")"
		});
		_styleElement(_bottomMarginTail, {
			position: "absolute",
			left: "50%",
			top: "0px",
			bottom: "0px",
			"margin-left": "-1px",
			background: "rgb(" + $SELECTION_RGB + ")",
			width: "1px",
			overflow: "visible"
		});
		_styleElement(_bottomMarginHead, {
			position: "absolute",
			bottom: "0px",
			left: "-4px",
			width: "0px",
			height: "0px",
			"border-right": "4.5px solid transparent",
			"border-left": "4.5px solid transparent",
			"border-top": "4.5px solid rgb(" + $SELECTION_RGB + ")"
		});
		_styleElement(_leftMarginTail, {
			position: "absolute",
			top: "50%",
			right: "0px",
			left: "0px",
			"margin-top": "-1px",
			background: "rgb(" + $SELECTION_RGB + ")",
			height: "1px",
			overflow: "visible"
		});
		_styleElement(_leftMarginHead, {
			position: "absolute",
			top: "-4px",
			// Due to how the browser rounds the half pixel sizes, we have to offset the top and left heads
			left: "-.5px",
			width: "0px",
			height: "0px",
			"border-top": "4.5px solid transparent",
			"border-bottom": "4.5px solid transparent",
			"border-right": "4.5px solid rgb(" + $SELECTION_RGB + ")"
		});
		
		// Public
		function reload()
		{
			var state = { enabled: _enabled};
			if (_enabled && _selectedElement) {
				state.selection = {
					path: _pathForElement(_selectedElement),
					styles: $page.appliedSelectorsForElement(_selectedElement)
				};
			}
			
			_redrawSelectedElement();
			EspressoLive.reflectXrayState(state);
		};
		this.reload = reload;
		
		function changeSelection(relativeAncestor)
		{
			relativeAncestor = parseInt(relativeAncestor, 10);
			if (isNaN(relativeAncestor)) relativeAncestor = -10000;
			
			var node = _selectedElement;
			while (relativeAncestor < 0 && node) {
				node = node.parentNode;
				relativeAncestor++;
			}
			
			_selectedElement = node;
			reload();
		}
		this.changeSelection = changeSelection;
		
		Object.defineProperty(this, "enabled", {
			get: function() { return _enabled; },
			set: function(v) {
				_enabled = v;
				_dragging = false;
				
				if (_enabled) {
					if (!_pageOverlay.parentNode) document.body.appendChild(_pageOverlay);
					if (!_pageEventInterceptOverlay.parentNode) document.body.appendChild(_pageEventInterceptOverlay);
					if (!_linkIndicator.parentNode) document.body.appendChild(_linkIndicator);
				} else if (_selectedElement) {
					// We need to fade things out before removing them; set _prevElement to false to ensure that we do a fadeout
					_prevElement = false;
					_toggleOverlays(0, function() {
						if (!_enabled) {
							_prevElement = null;
							if (_pageOverlay.parentNode) document.body.removeChild(_pageOverlay);
							if (_pageEventInterceptOverlay.parentNode) document.body.removeChild(_pageEventInterceptOverlay);
							if (_linkIndicator.parentNode) document.body.removeChild(_linkIndicator);
						}
					});
				} else {
					if (_pageOverlay.parentNode) document.body.removeChild(_pageOverlay);
					if (_pageEventInterceptOverlay.parentNode) document.body.removeChild(_pageEventInterceptOverlay);
					if (_linkIndicator.parentNode) document.body.removeChild(_linkIndicator);
				}
				
				reload();
			},
			
			enumerable: true,
			configurable: false
		});
		this.enabled = false;
					
		// Private
		
		function _styleElement(element, style)
		{
			var styleString = "";
			
			for (var name in style) {
				styleString += name + ":" + style[name] +";";
			}
			
			element.setAttribute("style", styleString);
		}
		
		function _retinaRound(num, floorIt) {
			if (window.devicePixelRatio && window.devicePixelRatio > 1) {
				var remainder = num % 1;
				if (remainder < .25 || (floorIt && remainder < .5)) {
					return Math.floor(num);
				} else if (remainder >= .25 && remainder < .75 || (floorIt && remainder >= .5)) {
					return Math.floor(num) + .5;
				} else {
					return Math.ceil(num);
				}
			} else if (floorIt) {
				return Math.floor(num);
			} else {
				return Math.round(num);
			}
		}
		
		function _toggleOverlays(targetOpacity, callback) {
			if ((targetOpacity === 1 && !_prevElement) || (targetOpacity === 0 && _prevElement !== null)) {
				_elementIndicator.style["-o-transition"] = _fadeTransition;
				_elementIndicator.style["-moz-transition"] = _fadeTransition;
				_elementIndicator.style["-webkit-transition"] = _fadeTransition;
				_elementIndicator.style.transition = _fadeTransition;
				_elementIndicator.style.opacity = (targetOpacity === 1 ? 0 : 1);
				if (_linkIndicator.style.display === "block") {
					_linkIndicator.style["-o-transition"] = _fadeTransition;
					_linkIndicator.style["-moz-transition"] = _fadeTransition;
					_linkIndicator.style["-webkit-transition"] = _fadeTransition;
					_linkIndicator.style.transition = _fadeTransition;
					_linkIndicator.style.opacity = (targetOpacity === 1 ? 0 : 1);
				}
				// Listen for the end of the transition so that we can call our callback function (if any)
				if (callback) {
					var endTransition = function() {
						callback();
						_elementIndicator.removeEventListener(_getTransitionEndEvent(), endTransition, false);
					};
					_elementIndicator.addEventListener(_getTransitionEndEvent(), endTransition, false);
				}
				// We have to delay in order to ensure that the element is rendered (otherwise, it just pops into existence the first time)
				setTimeout(function() {
					_elementIndicator.style.opacity = targetOpacity;
					_linkIndicator.style.opacity = targetOpacity;
				}, 5);
			} else {
				_elementIndicator.style["-o-transition"] = null;
				_elementIndicator.style["-moz-transition"] = null;
				_elementIndicator.style["-webkit-transition"] = null;
				_elementIndicator.style.transition = null;
				_elementIndicator.style.opacity = targetOpacity;
				_linkIndicator.style["-o-transition"] = null;
				_linkIndicator.style["-moz-transition"] = null;
				_linkIndicator.style["-webkit-transition"] = null;
				_linkIndicator.style.transition = null;
				_linkIndicator.style.opacity = targetOpacity;
				if (callback) {
					callback();
				}
			}
		}
		
		function _redrawSelectedElement()
		{
			if (_selectedElement && _enabled) {
			
				var borderWidth = 2;
				var elementStyle = {
					position: "absolute",
					background: "rgba(" + $SELECTION_RGB + ", .2)",
					border: borderWidth+"px solid rgb(" + $SELECTION_RGB + ")",
					"border-radius": borderWidth+"px",
					"box-sizing": "content-box !important",
					top: 0,
					left: 0,
					width: 0,
					height: 0,
					"z-index": 2147483647
				};
				var paddingStyle = {
					position: "absolute",
					border: "1px dashed rgb(" + $SELECTION_RGB + ")",
					top: 0,
					left: 0,
					width: 0,
					height: 0
				};
				var marginStyle = {
					position: "absolute",
					border: "1px solid rgb(" + $SELECTION_RGB + ")",
					top: 0,
					left: 0,
					right: 0,
					bottom: 0
				};
				
				var layout = _layoutForElement(_selectedElement);
				
				// Base element indicator
				elementStyle.top = (window.scrollY + layout.clientFrame.top - borderWidth) + "px";
				elementStyle.left = (window.scrollX + layout.clientFrame.left - borderWidth) + "px";
				elementStyle.width = (layout.clientFrame.width) + "px";
				elementStyle.height = (layout.clientFrame.height) + "px";
				
				// Padding
				paddingStyle.top = layout.padding.top - 1 + "px";
				paddingStyle.left = layout.padding.left - 1 + "px";
				paddingStyle.width = layout.innerFrame.width + "px";
				paddingStyle.height = layout.innerFrame.height + "px";
				
				// Ensure that borders don't show up if there is no padding (they can occasionally ghost into view due to the lack of half pixel rounding on Retina screens)
				if (layout.padding.top === 0) {
					paddingStyle["border-top-color"] = "transparent";
				}
				if (layout.padding.right === 0) {
					paddingStyle["border-right-color"] = "transparent";
				}
				if (layout.padding.bottom === 0) {
					paddingStyle["border-bottom-color"] = "transparent";
				}
				if (layout.padding.left === 0) {
					paddingStyle["border-left-color"] = "transparent";
				}
				
				// Margins
				marginStyle.top = (-layout.margin.top-1) + "px";
				marginStyle.left = (-layout.margin.left-1) + "px";
				marginStyle.right = (-layout.margin.right-1) + "px";
				marginStyle.bottom = (-layout.margin.bottom-1) + "px";
				
				// Margin markers
				if (layout.margin.top) {
					_styleElement(_topMarginOverlay, {
						position: "absolute",
						top: "0px",
						height: layout.margin.top + "px",
						left: "-1px",
						width: "100%",
						overflow: "hidden"
					});
				} else {
					_styleElement(_topMarginOverlay, {display: "none"});
				}
				if (layout.margin.right) {
					_styleElement(_rightMarginOverlay, {
						position: "absolute",
						top: "-1px",
						height: "100%",
						right: "0px",
						width: layout.margin.right + "px",
						overflow: "hidden"
					});
				} else {
					_styleElement(_rightMarginOverlay, {display: "none"});
				}
				if (layout.margin.bottom) {
					_styleElement(_bottomMarginOverlay, {
						position: "absolute",
						bottom: "0px",
						height: layout.margin.bottom + "px",
						left: "-1px",
						width: "100%",
						overflow: "hidden"
					});
				} else {
					_styleElement(_bottomMarginOverlay, {display: "none"});
				}
				if (layout.margin.left) {
					_styleElement(_leftMarginOverlay, {
						position: "absolute",
						top: "-1px",
						height: "100%",
						left: "0px",
						width: layout.margin.left + "px",
						overflow: "hidden"
					});
				} else {
					_styleElement(_leftMarginOverlay, {display: "none"});
				}
				
				// If the element is a link that leads to another page, then add the link indicator and adjust its onclick behavior
				if (_selectedElement.href && _selectedElement.href !== "#") {
					var top = _retinaRound(window.scrollY + layout.clientFrame.top + layout.clientFrame.height + 4, true);
					// Make sure that the element is not positioned outside the scrollable window
					if (top + 16 > document.documentElement.scrollHeight) {
						top = _retinaRound(window.scrollY + layout.clientFrame.top - 20, true);
					}
					if (top < 0) {
						// Below didn't work, above isn't working, so we'll have to overlap
						top = _retinaRound(window.scrollY + layout.clientFrame.top + layout.clientFrame.height - 18, true);
					}
					var left = _retinaRound(window.scrollX + layout.clientFrame.left + layout.clientFrame.width - 14, true);
					// Make sure the element is visible on the page
					if (left + 16 > document.documentElement.scrollWidth) {
						left = document.documentElement.scrollWidth - 16;
					}
					_linkIndicator.style.top = top + "px";
					_linkIndicator.style.left = left + "px";
					_linkIndicator.href = "#";
					_linkIndicator.onclick = function() { _selectedElement.click(); return false; }
					// Set the element to display
					_linkIndicator.style.display = "block";
				} else {
					_linkIndicator.style.display = "none";
					_linkIndicator.onclick = function() { return false; }
				}
				
				// Apply our styles
				_styleElement(_pageOverlay, {
					position: "absolute",
					top: "0px",
					left: "0px",
					width: (document && document.documentElement && document.documentElement.scrollWidth ? document.documentElement.scrollWidth : "0") + "px",
					height: (document && document.documentElement && document.documentElement.scrollHeight ? document.documentElement.scrollHeight : "0") + "px",
					overflow: "hidden"
				});
				_styleElement(_elementIndicator, elementStyle);
				_styleElement(_paddingOverlay, paddingStyle);
				_styleElement(_marginOverlay, marginStyle);
				
				// Show our elements
				_toggleOverlays(1);
			}
			else {
				// No element selected, so hide everything
				_toggleOverlays(0, function() {
					_pageOverlay.style.display = "none";
					_linkIndicator.style.display = "none";
					_linkIndicator.onclick = function() { return false; }
				});
			}
		}
		
		function _elementAtPoint(x,y)
		{
			var element = null;
							
			// Remove xray elements so they're not detected
			document.body.removeChild(_pageOverlay);
			document.body.removeChild(_pageEventInterceptOverlay);
			document.body.removeChild(_linkIndicator);
			
			element = document.elementFromPoint(x, y);
			
			// Add xray elements back
			document.body.appendChild(_pageOverlay);
			document.body.appendChild(_pageEventInterceptOverlay);
			document.body.appendChild(_linkIndicator);
			
			return element;
		}
		
		function _pathForElement(element)
		{
			var path = [];
			var currentElement = element;
			
			while (currentElement !== null && currentElement.nodeType == Node.ELEMENT_NODE) {
				
				var elementDescription = currentElement.nodeName.toLowerCase();
				
				if (currentElement.hasAttribute("id")) {
					elementDescription += "#" + currentElement.getAttribute("id");
				}
				
				if (currentElement.hasAttribute("class")) {
					var classList = currentElement.classList;
					for (var i = 0; i < classList.length; i++) {
						elementDescription += "." + classList[i];
					}
				}
				
				path.push(elementDescription);
				currentElement = currentElement.parentNode;
			}
			
			return path.reverse();
		}
					
		function _layoutForElement(element)
		{
			var computedStyle = getComputedStyle(element);
			var parentSize = { width: 0, height: 0 };
			
			var getPixelValue = function(styleDeclaration, property, referencePixelLength) {
				referencePixelLength = referencePixelLength || 0.0;
				
				if (styleDeclaration === null) {
					return 0.0;
				}
				
				var string = styleDeclaration.getPropertyValue(property);
				
				var percentIndex = string.indexOf("%");
				if (percentIndex >= 0) {
					return (parseFloat(string.substring(0, percentIndex)) * referencePixelLength) / 100.0;
				}
				
				var pxIndex = string.indexOf("px");
				if (pxIndex >= 0) {
					return parseFloat(string.substring(0, pxIndex));
				}
				
				return parseFloat(string);
			};
			
			if (element !== null && element.parentNode !== null && element.parentNode.nodeType === Node.ELEMENT_NODE) {
				var parentComputedStyle = getComputedStyle(element.parentNode);
				parentSize.width = getPixelValue(parentComputedStyle, "width");
				parentSize.height = getPixelValue(parentComputedStyle, "height");
			}
			
			// Get bounds (including padding and borders)
			var bounding_box = element.getBoundingClientRect();
			
			var layout = {
				innerFrame: {
					width: 0,
					height: 0
				},
				clientFrame: {
					top: _retinaRound(bounding_box.top),
					left: _retinaRound(bounding_box.left),
					width: _retinaRound(bounding_box.width),
					height: _retinaRound(bounding_box.height)
				},
				margin: {
					top: getPixelValue(computedStyle, "margin-top", parentSize.height),
					right: getPixelValue(computedStyle, "margin-right", parentSize.width),
					bottom: getPixelValue(computedStyle, "margin-bottom", parentSize.height),
					left: getPixelValue(computedStyle, "margin-left", parentSize.width)
				},
				padding: {
					top: getPixelValue(computedStyle, "padding-top", parentSize.height),
					right: getPixelValue(computedStyle, "padding-right", parentSize.width),
					bottom: getPixelValue(computedStyle, "padding-bottom", parentSize.height),
					left: getPixelValue(computedStyle, "padding-left", parentSize.width)
				},
				border: {
					top: getPixelValue(computedStyle, "border-top", parentSize.height),
					right: getPixelValue(computedStyle, "border-right", parentSize.width),
					bottom: getPixelValue(computedStyle, "border-bottom", parentSize.height),
					left: getPixelValue(computedStyle, "border-left", parentSize.width)
				}
			};
			
			layout.innerFrame.width = layout.clientFrame.width - (layout.padding.left + layout.padding.right + layout.border.left + layout.border.right);
			layout.innerFrame.height = layout.clientFrame.height - (layout.padding.top + layout.padding.bottom + layout.border.top + layout.border.bottom);
			
			return layout;
		}
		
		// Code from <http://stackoverflow.com/a/9090128/38666>
		var _transitionEndEvent = null;
		function _getTransitionEndEvent() {
			if (_transitionEndEvent) {
				return _transitionEndEvent;
			}
			var el = document.createElement("div"),
				transitions = {
					"WebkitTransition":"webkitTransitionEnd",
					"transition":"transitionend",
					"OTransition":"otransitionend",  // oTransitionEnd in very old Opera
					"MozTransition":"transitionend"
				};
			for (var i in transitions) {
				if (transitions.hasOwnProperty(i) && el.style[i] !== undefined) {
					_transitionEndEvent = transitions[i];
					break;
				}
			}
			return _transitionEndEvent;
		}
		
		// Make the container intercept all mouse clicks and covers the entire page
		_styleElement(_pageEventInterceptOverlay, {
			position: "fixed",
			top: "0px",
			left: "0px",
			width: "100%",
			height: "100%",
			"z-index": 2147483647
		});
		
		// Bind events
		function _cancelEvent(ev)
		{
			ev && ev.preventDefault && ev.preventDefault();
			ev && ev.stopPropagation && ev.stopPropagation();
		}
		
		_pageEventInterceptOverlay.oncontextmenu = function(ev) {
			return false;
		}
		
		_pageEventInterceptOverlay.onmousedown = function(ev) {
			if (ev.target !== _linkIndicator) {
				_cancelEvent(ev);
				
				var element = _elementAtPoint(ev.clientX, ev.clientY);
				
				if (element !== _selectedElement) {
					_prevElement = _selectedElement;
					_selectedElement = element;
					reload();
				}
				
				_dragging = true;
				
				// Right/middle/other click
				if (ev.button > 0 && ev.target !== _linkIndicator && _selectedElement) {
					_dragging = false;
					EspressoLive.popUpXrayContextMenu({
						path: _pathForElement(_selectedElement),
						styles: $page.appliedSelectorsForElement(_selectedElement)
					});
				}
			}
			
			return false;
		};
		
		_pageEventInterceptOverlay.onmousemove = function(ev) {
			_cancelEvent(ev);
			
			if (_dragging) {
				var element = _elementAtPoint(ev.clientX, ev.clientY);
				
				if (element !== _selectedElement) {
					_prevElement = _selectedElement;
					_selectedElement = element;
					reload();
				}
			}
		};
		
		_pageEventInterceptOverlay.onmouseup = function(ev) {
			// Only trigger for the left mouse button
			if (ev.button === 0) {
				_cancelEvent(ev);
				_dragging = false;
			}
		};
		
		window.addEventListener("mouseout", function(ev) {
			if (!_enabled) return;
			
			_cancelEvent(ev);
			_dragging = _dragging && !!document.elementFromPoint(ev.clientX, ev.clientY);
		});
		
		window.addEventListener("resize", function(ev) {
			if (!_enabled) return;
			
			_cancelEvent(ev);
			// Hard reset the overlay size so that it won't cause a never-ending growing scrolling area
			_pageOverlay.style.width = document.body.offsetWidth + "px";
			_pageOverlay.style.height = document.body.offsetHeight + "px";
			_redrawSelectedElement();
		});
	}
	
	
	// Bridge to Espresso
	var $page = new EspressoPreviewPage();
	var $xray = new EspressoXray();
	window.__EspressoLive__ = window.__EspressoLive__ || {};
	var espresso = window.__EspressoLive__ || {};
	espresso.page = $page;
	espresso.xray = $xray;
	
	
	// Load initial overrides
	document.addEventListener("DOMContentLoaded", function(event) {
		$page.overrides = window.__EspressoLiveInitialOverrides__;
	});
	
	
	// Support command-option-X to toggle Xray in browsers
	if (typeof window.__EspressoLiveIsInAppPreview__ === "undefined" || !window.__EspressoLiveIsInAppPreview__) {
		window.addEventListener("keydown", function(event) {
			if (event.keyCode == 88 && event.metaKey && event.altKey) {
				$xray.enabled = !$xray.enabled;
			}
		});
	}
	
	
})();
