/*
PopupBox component
Aaron Beall 2009 - http://abeall.com
Version 1.1

SETUP:
- include popupBox.js on your page
- include popupBox.css on your page
- upload popupBox.skin.htm and set POPUP_SKIN_FILE below to point to same location
- modify popupBox.skin.htm/popupBox.css as desired, only requirement is an element with id "popupBoxContent" which will contain the loaded content
- modify other params below as desired

TODO:
- CSS based popup centering, get rid of positionPopupBox, onresize, onscroll, getScroll, getWindowSize
- [DONE] XMLHttpRequest to load in skin instead of iframe
- dynamically inject init code in body to prevent link race condition, onload fallback
- [DONE] capture race condition of popup request before skin load and deal with gracefully
- preload skin images (skin markup is already preloaded, but images seem to not be loaded because display:none)
- [DONE] use page anchors for deeplinking, ex: http://example.com/whatever/#some-href
- - [DONE] on page load with anchor, auto-open popup
- - prevent hash from causing history entries
- - [DONE] detect direct link to content fragment document and forward to appropriate page with popup deeplink
- - on popup close, clear hash
- double check transition algorithm to see if it's really accurate, otherwise try to figure out why IE is so much slower than FF
- convert to JS class or at least object to avoid definition collisions
- XML based content: strip out specified nodes from content and inject into specified nodes in skin, allowing complex skin files with multiple content areas
- fade in shield
- transition out
- [DONE] integrate with google analytics
- add onshow/onhide API

BUGS:
- [FIXED] horizontal scrollbar appears in FF/IE7 -- setting pixel width/height to screen size caused it, use style w/h of 100% instead
- [FIXED] opacity fade causes transparent PNGs on IE7 to fail -- alpha fade removed on completion, IE7 reverts to correct appearance after transition
- [FIXED] deeplink generated urls are absolute in IE
- setting location.hash to "" when popup is closed causes page to scroll to top
*/

// params
var POPUP_SKIN_FILE = "/includes/html/popupBox.skin.htm"; // url to the popup skin file
var TRANSITION_DURATION = .50; // transition duration in seconds, use 0 to omit transition completely
var TRANSITION_FPS = 60; // transition frames per second
var DEFAULT_WIDTH = "423px"; // default width when none specified
var DEFAULT_HEIGHT = "auto"; // default height when none specified

var CLICK_TO_CLOSE = true; // click the background to close the popup
var NO_CACHE = true; // prevent caching of loaded content

var USE_DEEP_LINKING = true; // generate unique URLs which can be used to restore popup state
var DEEP_LINK_PREFIX = "view:"; // a prefix to use in the unique URLs to identify a popup state
var DEEP_LINK_USE_SIZE = false; // whether or not to include popup size parameters in the deep link

var USE_GOOGLE_ANALYTICS = true; // generate a google analytics hit for each popup

// init events
function addEvent(elem, type, listener){
	if(elem.addEventListener) // W3C DOM
		elem.addEventListener(type, listener, false);
	else if(elem.attachEvent) // IE DOM
		elem.attachEvent("on" + type, listener);
}
addEvent(window, "load", initPopupBox);
addEvent(window, "resize", positionPopupBox);
addEvent(window, "scroll", positionPopupBox);

// initialize the popup box framework
function initPopupBox(){
	// create the popup box container div
	var box = document.createElement("div");
	box.setAttribute("id","popupBox");
	document.body.appendChild(box);
	
	// create the fade out "shield"
	var shield = document.createElement("div");
	shield.setAttribute("id","popupBoxShield");
	if(CLICK_TO_CLOSE) shield.onclick = closePopupBox;
	document.body.appendChild(shield);
	
	// load the popup box skin
	xmlRequest(noCache(POPUP_SKIN_FILE),function(responseText){
		document.popupBoxSkin = responseText;
		document.popupBoxSkinLoaded = true;
		if(document.popupBoxQueue) 
			showPopupBox(document.popupBoxQueue.an,document.popupBoxQueue.width,document.popupBoxQueue.height);
		else if(USE_DEEP_LINKING && location.hash && location.hash.indexOf(DEEP_LINK_PREFIX) == 1){
			var ar = location.hash.substr(1).split(',');
			if(!DEEP_LINK_USE_SIZE) ar[1] = ar[2] = null;
			showPopupBox({href:unescape(ar[0].substr(DEEP_LINK_PREFIX.length))},ar[1]||null,ar[2]||null);
		}
	});
	
	// find and replace all rel=popupBox links to launch popup
	var anchors = document.getElementsByTagName("a");
	for(var i=0; i<anchors.length; i++){
		if(anchors[i].getAttribute("rel") == "popupBox" && !anchors[i].getAttribute("onclick")){
			anchors[i].onclick = function(){
				if(navigator.appName == "Microsoft Internet Explorer") event.returnValue = false;
				return showPopupBox(this);
			}
			//anchors[i].setAttribute("onclick","showPopupBox(this);return false;");
		}
	}
}

// show the popup box with specified content
function showPopupBox(an,width,height){
	
	var href = an.href;
	var box = document.getElementById("popupBox");
	var boxSkin = document.popupBoxSkin;//document.getElementById("popupBoxSkin");
	
	// capture popup box request launched before popup box skin has loaded
	if(!document.popupBoxSkinLoaded){
		return false;
		document.popupBoxQueue = {an:an,width:width,height:height};
	}
	
	// transfer the skin file markup into the popup box container div
	box.innerHTML = boxSkin;//boxSkin.contentWindow.document.getElementsByTagName("body")[0].innerHTML;
	
	// size the popup box
	box.style.width = width ? width : DEFAULT_WIDTH;
	box.style.height = height ? height : DEFAULT_HEIGHT;
	
	// load the XML content
	xmlRequest(noCache(href),function(responseText){
		document.getElementById("popupBoxContent").innerHTML = responseText;
		positionPopupBox();
	});
	
	// toggle the box
	box.style.display = box.style.display == "block" ? "none" : "block";
	document.getElementById("popupBoxShield").style.display = box.style.display;
	if(box.style.display == "block")
		popupBoxFadeIn();
	
	// position the box
	positionPopupBox();
	
	// create a deeplink by adding hash to url
	if(USE_DEEP_LINKING)
		window.location.hash = "#" + (DEEP_LINK_PREFIX||"") + escape(an.getAttribute?an.getAttribute("href",2):an.href) + (DEEP_LINK_USE_SIZE ? "," + String(width||"") + "," + String(height||"") : "");
	
	// google analytics page tracking
	if(USE_GOOGLE_ANALYTICS && pageTracker && pageTracker._trackPageview)
		pageTracker._trackPageview(window.location.pathname);
	
	// return false to prevent links from changing the page
	return false;
}

// position the popup box
function positionPopupBox(){
	var windowSize = getWindowSize();
	var scrolloffset = getScroll();
	
	// center the popup box on screen
	var box = document.getElementById("popupBox");
	box.style.top = scrolloffset.y + Math.round(windowSize.height/2 - box.clientHeight/2) + "px";
	box.style.left = scrolloffset.x + Math.round(windowSize.width/2 - box.clientWidth/2) + "px";
	
	// cover the whole screen
	var shield = document.getElementById("popupBoxShield");
	shield.style.top = scrolloffset.y;
	shield.style.left = scrolloffset.x;
	//shield.style.width = windowSize.width + "px";
	//shield.style.height = windowSize.height + "px";
}

// close the popup
function closePopupBox(){
	document.getElementById("popupBox").style.display = "none";
	document.getElementById("popupBoxShield").style.display = "none";
	//if(USE_DEEP_LINKING) window.location.hash = ""; // BUG: sends window to top
	//location.replace(location.href.split('#')[0]+'#');
	return false;
}

// get window size, accounting for browser stupidity (IE)
function getWindowSize(){
	if(typeof(window.innerHeight) == 'number')
		return {height:window.innerHeight, width:window.innerWidth};
	else{
		if(document.documentElement && document.documentElement.clientHeight)
			return {height:document.documentElement.clientHeight, width:document.documentElement.clientWidth}
		else
			if(document.body && document.body.clientHeight)
				return {height:document.body.clientHeight, width:document.body.clientWidth}
	}
}

// fade in transition
function popupBoxFadeIn(){
	if(TRANSITION_DURATION <= 0) return;
		
	var box = document.getElementById("popupBox");
	//var speed = 100/(TRANSITION_DURATION*(1000/TRANSITION_FPS)); // I don't think this is right...
	//var speed = 100*((1000/TRANSITION_FPS)/(TRANSITION_DURATION*1000));
	var startTime = (new Date().getTime());
	if(popupBoxFadeIn.interval)
		clearInterval(popupBoxFadeIn.interval);
	popupBoxFadeIn.opacity = 0;
	setOpacity(box,popupBoxFadeIn.opacity);
	popupBoxFadeIn.interval = setInterval(function(){
		popupBoxFadeIn.opacity = 100*((new Date().getTime() - startTime)/(TRANSITION_DURATION*1000))
		//popupBoxFadeIn.opacity += speed;
		setOpacity(box,popupBoxFadeIn.opacity);
		if(popupBoxFadeIn.opacity >= 100){
			setOpacity(box,popupBoxFadeIn.opacity = 100);
			clearInterval(popupBoxFadeIn.interval);
		}
		
	},1000/TRANSITION_FPS);
	
	function setOpacity(elem,val){
		elem.style.opacity = val/100;
		elem.style.filter = val == 100 ? "" : "alpha(opacity=" + val + ")";
		//elem.style.visibility = val != 0 ? "visible" : "hidden";
	}
}

// get the scroll position, accounting for browser stupidity (IE)
function getScroll(){
	var iebody = (document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body
	var dsocleft = document.all ? iebody.scrollLeft : pageXOffset
	var dsoctop = document.all ? iebody.scrollTop : pageYOffset
	return {x:dsocleft,y:dsoctop};
}

// send an XML request
function xmlRequest(url,responseHandler){
	var xmlhttp = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
	xmlhttp.onreadystatechange = function(){
		if(xmlhttp.readyState == 4)
			responseHandler(xmlhttp.responseText);
	}
	xmlhttp.open("GET",noCache(url),true);
	xmlhttp.send(null);
}

// create a timestamp to avoid browser caching
function noCache(url){
	return url + (NO_CACHE ? "?timestamp="+escape(new Date().toString()) : "");
}