

bbc.guidance.overlay = function() {

	var modalMaskOpacity = 70,
		opacityStep = 15,
		animateDelay = 30,
		modalWidth = 300,
		modalHeight = 400,

		forcedHeight,//using public method setHeight, the use can force the height of the document - so that it is not automatically cenetered vertically

		doc = document,
		IElt7 = -1,
		modalData = {
			mask: null, dialog: null,
			width: 0, height: 0,
			interval: -1, intervalScroll: -1,
			dir: 1, level: 0, visible: false,
			selectBoxList: null, swfList: null
		};


	function isIElt7() {

		if (IElt7 != -1) return IElt7;
		IElt7 = false;

		var ua = navigator.userAgent.toLowerCase(),ver;
		if ((/msie/.test(ua)) && (ver = ua.match(/msie ([\d]+)/))) IElt7 = (ver[1] < 7);
		return IElt7;
	}

	function getWindowDimensions() {

		var winX,winY,
			docEl = doc.documentElement || null;

		if (window.innerWidth) {
			// w3 mode
			winX = docEl.clientWidth || window.innerWidth;
			winX = window.innerWidth;
			winY = window.innerHeight;

		} else if (docEl && docEl.clientHeight) {
			// ie6+ standards compliant
			winX = docEl.clientWidth;
			winY = docEl.clientHeight;

		} else if (doc.body) {
			// older ie
			var docBody = doc.body;
			winX = docBody.clientWidth;
			winY = docBody.clientHeight;
		}

		return { width: winX, height: winY };
	}

	function getDocumentDimensions() {

		var pageX = 0,pageY = 0,
			docBody = doc.body || null;

		if (window.innerHeight && window.scrollMaxY) {
			pageX = docBody.scrollWidth;
			pageY = window.innerHeight + window.scrollMaxY;

		} else if (docBody && (docBody.scrollHeight > docBody.offsetHeight)) {
			pageX = docBody.scrollWidth;
			pageY = docBody.scrollHeight;

		} else if (docBody) {
			pageX = docBody.offsetWidth;
			pageY = docBody.offsetHeight;
		}

		return { width: pageX, height: pageY };
	}

	function getPageScroll(){

		// w3 mode
		if (window.pageYOffset) return window.pageYOffset;

		// ie6+ standards compliant
		var docEl = doc.documentElement || null;
		if (docEl && docEl.scrollTop) return docEl.scrollTop;

		// older ie
		if (doc.body) return doc.body.scrollTop;

		return 0;
	}

	function setOpacity(el,opacity) {

		el.style.display = (opacity <= 0) ? "none" : "block";
		el.style.opacity = Math.round((opacity / 100) * 1000) / 1000; // round to 3 dec places
		el.style.filter = "alpha(opacity=" + opacity + ")";
	}

	function updateModalVisual() {

		// only update modal if visible
		if (!modalData.visible) return;

		var win = getWindowDimensions(),
			doc = getDocumentDimensions(),
			maskEl = modalData.mask;

		// update mask sizing
		if (isIElt7()) maskEl.style.width = Math.max(doc.width,win.width);
		maskEl.style.height = Math.max(doc.height,win.height) + "px";

		// re-center modal dialog
		positionDialog(win.width,forcedHeight || win.height);
	}

	function updateModalTopIE() {

		// only update modal if visible
		if (!modalData.visible) return;
		positionDialog(-1,getWindowDimensions().height);
	}

	function positionDialog(winWidth,winHeight) {

		var dialogEl = modalData.dialog,
			topOffset = (isIElt7()) ? getPageScroll() : 0;

		dialogEl.style.top = (Math.max(0,Math.floor((winHeight / 2) - (modalData.height / 2))) + topOffset) + "px";
		if (winWidth < 0) return;
		dialogEl.style.left = Math.max(0,Math.floor((winWidth / 2) - (modalData.width / 2))) + "px";
	}

	function showModal() {

		// no modal shown while in motion, or already shown
		if ((modalData.interval != -1) || (modalData.visible)) return;

		var maskEl = modalData.mask;
			dialogEl = modalData.dialog;

		if (!modalData.mask) {
			// create mask & attach browser resize handler
			maskEl = doc.createElement("div");
			maskEl.id = "pg-mask";
			doc.body.appendChild(maskEl);
			modalData.mask = maskEl;

			var oldresize = window.onresize;
			if (typeof window.onresize != "function") {
				window.onresize = updateModalVisual;
			} else {
				window.onresize = function() { oldresize(); updateModalVisual(); };
			}
		}

		// adjust element styles for non IE6 browsers
		if (!isIElt7()) {
			maskEl.style.width = "100%";
			dialogEl.style.position = "fixed";
		}

		// get width/height of dialog
		dialogEl.style.display = "block";
		modalData.width = dialogEl.offsetWidth;
		modalData.height = dialogEl.offsetHeight;
		dialogEl.style.display = "none";

		// start modal animation
		setOpacity(maskEl,0);
		setOpacity(dialogEl,0);

		modalData.visible = true;
		startModalAnimation(1);

		if (isIElt7()) {
			// setup interval to monitor page scroll for IE6 (since it can't do position:fixed)
			// and remove <select> boxes since they bleed through the mask layer
			modalData.intervalScroll = window.setInterval(updateModalTopIE,200);
			updateSelectBoxes(true);
		}

		// remove flash embeds and update modal mask/dialog centering
		updateFlashEmbeds(true);
		updateModalVisual();
	}

	function updateSelectBoxes(hidden) {

		var list = (hidden) ? doc.getElementsByTagName("select") : modalData.selectBoxList;
		for (var i = 0,j = list.length;i < j;i++) {
			list[i].style.visibility = (hidden) ? "hidden" : "visible";
		}

		modalData.selectBoxList = list;
	}

	function updateFlashEmbeds(hidden) {

		if (hidden) {
			var list,
				swfList = [],
				swfUrlRegexp = /.swf($|\?)/i;

			for (var t = 0;t <= 1;t++) {
				list = doc.getElementsByTagName(t ? "object" : "embed");

				for (var i = 0,j = list.length;i < j;i++) {
					var item = list[i];
					if (
						(item.getAttribute("type") == "application/x-shockwave-flash") ||
						((item.getAttribute("classid") || "").toLowerCase() == "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000") ||
						(swfUrlRegexp.test(item.getAttribute("data") || item.getAttribute("src") || ""))
					) {
						swfList[swfList.length] = item;
					}
				}
			}

			modalData.swfList = swfList;
		}

		if (!modalData.swfList.length) return;
		for (var i = 0,j = modalData.swfList.length;i < j;i++) {
			modalData.swfList[i].style.visibility = (hidden) ? "hidden" : "visible";
		}
	}

	// hideModal() returns true/false if in a state to start hiding itself
	function hideModal() {

		// no modal hidden while in motion, or not currently shown
		if ((modalData.interval != -1) || (!modalData.visible)) return false;

		// start modal animation
		modalData.visible = false;
		startModalAnimation(-1);
		return true;
	}

	function startModalAnimation(dir) {

		if (modalData.interval != -1) return;
		modalData.dir = dir;
		modalData.interval = window.setInterval(animateModal,animateDelay);
	}

	function animateModal() {

		modalData.level += (modalData.dir * opacityStep);
		if ((modalData.level <= 0) || (modalData.level >= 100)) {
			// animation done
			window.clearInterval(modalData.interval);
			modalData.interval = -1;
			modalData.level = (modalData.visible) ? 100 : 0;

			if (!modalData.visible) {
				// redisplay select boxes for IE6
				if (isIElt7()) updateSelectBoxes();
				updateFlashEmbeds();

				// remove IE6 scroll position timer
				if (modalData.intervalScroll != -1) {
					window.clearInterval(modalData.intervalScroll);
					modalData.intervalScroll = -1;
				}

				// fire callback event that overlay has closed
				if (bbc.guidance.overlay.closed) bbc.guidance.overlay.closed();
			}
		}

		// update mask and modal popup opacity
		setOpacity(modalData.mask,(modalData.level >= modalMaskOpacity) ? modalMaskOpacity : modalData.level);
		setOpacity(modalData.dialog,modalData.level);
	}

	return {
		show: function(dialog) { modalData.dialog = dialog; showModal(); },
		hide: function(dialog) { modalData.dialog = dialog; return hideModal(); },
		setHeight: function(height) { forcedHeight = height; },
		setOpacityMask: function(level) { modalMaskOpacity = level; },
		setOpacityStep: function(step) { opacityStep = step; }
	};
}();
