﻿function _LightBox(id)
{
	var overlays = [],
	closeCallbacks = [];

	this.GetOverlay = function (lightboxId)
	{
		/// <summary>
		/// Retrieves an overlay element for the lightbox.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox that is owner of the overlay.</param>
		var overlay = overlays[lightboxId];

		if (overlay === undefined)
		{
			var height = null,
			adminContext = false;

			if (top.location.pathname.length > 7 && top.location.pathname.toLowerCase().substr(0, 7) == '/admin/')
			{
				height = '100%';
				adminContext = true;
			}
			else
			{
				if ($ts.exists(top.document.body))
				{
					height = $ts.getComputedStyle(top.document.body, 'height');
				}
			}

			if (height !== '100%')
			{
				height = document.documentElement.offsetHeight + 'px';
			}

			overlay = $ts.createElement('div', { "id": lightboxId + '_Overlay' },
			{
				"position": 'absolute',
				"top": '0',
				"left": '0',
				"width": '100%',
				"height": height,
				"zIndex": '10000',
				"backgroundColor": '#000000'
			});

			Tangora.PublicAnimation.AddAnimationCapabilities(overlay);
			overlay.endopacity = 50;

			overlay.AdjustSize = function (wrapper)
			{
				if (height !== '100%')
				{
					height = Math.max((document.documentElement.offsetHeight + document.documentElement.scrollTop), Math.max(top.document.body.offsetHeight, top.document.body.scrollHeight));

					this.style.top = '0px';
					this.style.width = '100%';
					this.style.height = height + 'px';
				}
			};

			$ts.addEvent(top.window, 'resize', function () { overlay.AdjustSize(GetWrapper(lightboxId)); });

			overlays[lightboxId] = overlay;
		}

		return overlay;
	};

	this.SetOverlay = function (lightboxId, color, opacity)
	{
		/// <summary>
		/// Defines the settings of the overlay.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox that is owner of the overlay.</param>
		/// <param name="color">The css color of the overlay.</param>
		/// <param name="opacity">The css opacity of the overlay.</param>
		var overlay = LightBox.GetOverlay(lightboxId);

		if (color !== undefined && color !== null)
		{
			overlay.style.backgroundColor = color;
		}

		if (opacity !== undefined && opacity !== null)
		{
			overlay.endopacity = opacity;
		}
	};

	var wrappers = [];
	var closeButtons = [];

	this.Dispose = function (lightboxId)
	{
		if ($ts.exists(wrappers[lightboxId]))
		{
			if ($ts.exists(wrappers[lightboxId].parentNode))
			{
				wrappers[lightboxId].parentNode.removeChild(wrappers[lightboxId]);
			}

			delete wrappers[lightboxId];
		}

		if ($ts.exists(overlays[lightboxId]))
		{
			if ($ts.exists(overlays[lightboxId].parentNode))
			{
				overlays[lightboxId].parentNode.removeChild(overlays[lightboxId]);
			}

			delete overlays[lightboxId];
		}

		if ($ts.exists(closeButtons[lightboxId]))
		{
			delete closeButtons[lightboxId];
		}
	};

	this.DisposeAll = function ()
	{
		for (var lightboxId in wrappers)
		{
			this.Dispose(lightboxId);
		}
	};

	var DomWrapper = (function ()
	{
		/// <summary>
		/// Creates and returns a wrapper container singleton, and appends it to the top window.
		/// </summary>
		if (!top.window.$ts.exists(top.window.domLightboxWrapperContainer))
		{
			top.window.domLightboxWrapperContainer = top.window.$ts.createElement('div', null,
			{
				"position": 'absolute',
				"top": '-1px',
				"left": '-1px',
				"width": '1px',
				"height": '1px',
				"overflow": 'hidden'
			});

			top.window.$ts.addEvent(top.window, 'load', function ()
			{
				top.window.document.body.appendChild(top.window.domLightboxWrapperContainer);

				if (top.window.$ts.exists(top.window.domLightboxWrapperContainer.callbacks))
				{
					for (var i = 0; i < top.window.domLightboxWrapperContainer.callbacks.length; i++)
					{
						top.window.domLightboxWrapperContainer.callbacks[i]();
					}
				}
			});

			top.window.domLightboxWrapperContainer.callbacks = [];
		}

		return top.window.domLightboxWrapperContainer;
	})();

	var GetWrapper = function (lightboxId, callback)
	{
		/// <summary>
		/// Retrieves a lightbox element, and appends it to DOM.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="callback">Callback function, for when the lightbox has been added to DOM.</param>
		var wrapper = wrappers[lightboxId];
		if (wrapper === undefined)
		{
			wrapper = DomWrapper.appendChild(top.window.$ts.createElement('div',
			{
				"id": lightboxId + '_Wrapper',
				"Visible": false,
				"KeepCentered": function KeepCentered()
				{
					if (wrapper.parentNode && wrapper.Visible)
					{
						Tangora.PublicAnimation.MoveToCenter(wrapper, { "Time": 0 }, null, 'x', null, wrapper.centerElement.offsetWidth, null, true);
					}
				},
				"HeadBag": []
			},
			{
				"position": 'absolute',
				"zIndex": '10001'
			}));

			if ($ts.exists(callback))
			{
				if (top.window.loaded)
				{
					callback();
				}
				else
				{
					DomWrapper.callbacks.push(callback);
				}
			}

			if (top.window.loaded)
			{
				$ts.Observer.Broadcast(lightboxId, 'AddedToDom');
			}
			else
			{
				DomWrapper.callbacks.push(function () { $ts.Observer.Broadcast(lightboxId, 'AddedToDom'); });
			}

			Tangora.PublicAnimation.AddAnimationCapabilities(wrapper);
			$ts.addEvent(top.window, 'resize', wrapper.KeepCentered);

			var overlay = LightBox.GetOverlay(lightboxId);
			// The wrapper itself is used for positioning, the inner is used for zooming/resizing contents.
			wrapper.PlaceHolder = wrapper.appendChild($ts.createElement('div'), { "id": lightboxId + '_InnerWrapper' }, { "position": 'relative' });
			Tangora.PublicAnimation.AddAnimationCapabilities(wrapper.PlaceHolder);
			wrappers[lightboxId] = wrapper;
		}

		return wrapper;
	};

	this.SetCloseButton = function (lightboxId, imgUrl, hoverImgUrl)
	{
		var wrapper = wrappers[lightboxId],
		closeButton = closeButtons[lightboxId];

		if ($ts.exists(closeButton))
		{
			wrapper.firstChild.removeChild(closeButton);
		}
		
		if (imgUrl !== 'none')
		{
			if (!$ts.exists(imgUrl) || imgUrl === '')
			{
				imgUrl = '/setup/dialog_close_outlined.png';
				hoverImgUrl = '/setup/dialog_close_outlined_hover.png';
			}

			if (!$ts.exists(hoverImgUrl) || hoverImgUrl === '')
			{
				hoverImgUrl = imgUrl;
			}

			closeButton = wrapper.firstChild.appendChild($ts.createElement('img',
			{
				"id": 'Close' + lightboxId,
				"src": imgUrl,
				"onmouseover": function () { this.src = hoverImgUrl; },
				"onmouseout": function () { this.src = imgUrl; },
				"onclick": function () { LightBox.Close(lightboxId); }
			},
			{
				"position": 'absolute',
				"zIndex": '10002',
				"right": '-18px',
				"top": '-18px',
				"cursor": 'pointer',
				"display": 'none'
			}));

			closeButtons[lightboxId] = closeButton;
		}
	};

	this.SetContents = function (lightboxId, contents, bgColor, padding, callback, IRenderResult)
	{
		/// <summary>
		/// Injects the content elements into the lightbox, and appends it to DOM.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="contents">The html contents that will be shown in the lightbox</param>
		/// <param name="bgColor">The color to show at transparent content areas.</param>
		/// <param name="padding">Padding around the contents</param>
		/// <param name="callback">Callback function, for when the lightbox has been added to DOM.</param>
		function NullOrEmpty(obj)
		{
			return obj === undefined || obj === null || obj === '';
		}

		var wrapper = GetWrapper(lightboxId, (window.loaded ? null : callback));
		var closeButton = closeButtons[lightboxId];
		while (wrapper.firstChild.lastChild != closeButton)
		{
			wrapper.firstChild.removeChild(wrapper.firstChild.lastChild);
		}

		wrapper.firstChild.appendChild(contents);

		wrapper.style.backgroundColor = !NullOrEmpty(bgColor) ? bgColor : 'transparent';

		if (!NullOrEmpty(padding))
		{
			wrapper.firstChild.style.margin = padding + 'px';
		}

		wrapper.HeadBag = [];

		// Prepare requirements to be injected on show.
		if ($ts.exists(IRenderResult))
		{
			var headBag = wrapper.HeadBag;

			if ($ts.exists(IRenderResult.Css))
			{
				var css = top.$ts.createElement('style', { "type": "text/css" });
				var rules = top.document.createTextNode(IRenderResult.Css);

				if (css.styleSheet)
				{
					css.styleSheet.cssText = rules.nodeValue;
				}
				else
				{
					css.appendChild(rules);
				}

				headBag.push(css);
			}

			if ($ts.exists(IRenderResult.Script))
			{
				headBag.push(top.$ts.createElement('script', { "type": "text/javascript", "text": IRenderResult.Script }));
			}

			if ($ts.exists(IRenderResult.ScriptIncludes))
			{
				for (var scriptInclude in IRenderResult.ScriptIncludes)
				{
					if (IRenderResult.ScriptIncludes.hasOwnProperty(scriptInclude))
					{
						headBag.push(top.$ts.createElement('script', { "src": IRenderResult.ScriptIncludes[scriptInclude], "type": "text/javascript" }));
					}
				}
			}
		}

		if (window.loaded && $ts.exists(callback))
		{
			callback();
		}

		$ts.Observer.Broadcast(lightboxId, 'ContentsSet');
	};

	this.Show = function (lightboxId, preset, srcEvent, topRef)
	{
		/// <summary>
		/// Executes the overlay- and the wrappers animations, showing them on top of everything else.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="preset">The animation presets to use (optional, default = expands from center of browser window).</param>
		/// <param name="srcEvent">If preset is set to 'fromElement', srcEvent.srcElement is used as the element to expand from.</param>
		/// <param name="topRef">The window to use as reference for absolute positioning (optional, default = window.top).</param>
		var overlay = LightBox.GetOverlay(lightboxId),
		wrapper = GetWrapper(lightboxId),
		closeButton = closeButtons[lightboxId],
		duration,
		overlayTransition;

		if (typeof Tangora.Globals.LightBoxTransitionInDuration == 'undefined')
		{
			Tangora.Globals.LightBoxTransitionInDuration = 250;
		}

		duration = Tangora.Globals.LightBoxTransitionInDuration;

		if (typeof Tangora.Globals.LightBoxOverlayTransition == 'undefined')
		{
			Tangora.Globals.LightBoxOverlayTransition = false;
		}

		overlayTransition = Tangora.Globals.LightBoxOverlayTransition;

		if (!$ts.exists(topRef))
		{
			topRef = window.top;
		}

		if (wrapper.Visible === false)
		{
			try
			{
				Tangora.PublicAnimation.SetOpacity(overlay, 0);
				overlay.style.top = topRef.document.body.scrollTop + 'px';

				topRef.window.document.body.appendChild(overlay);
				overlay.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': overlayTransition ? duration : 0, 'Easing': 'out', 'StartOpacity': 0, 'EndOpacity': overlay.endopacity}]);
				Tangora.PublicAnimation.SetOpacity(wrapper, 0);
				wrapper.style.top = '0px';
				wrapper.style.left = '0px';

				var overflowSetting = topRef.window.document.body.style.overflow + '';
				topRef.window.document.body.style.overflow = 'hidden';
				topRef.window.document.body.appendChild(wrapper);
			}
			catch (ex)
			{
				try
				{
					topRef.window.document.body.removeChild(overlay);
					console.log('Error in lightbox.show()');
				}
				catch (ex2)
				{
				}

				return;
			}

			function PreparationsComplete()
			{
				topRef.window.document.body.style.overflow = overflowSetting;

				var ie8or9 = Tangora.Browser.Name.toLowerCase() === 'internet explorer' && (Tangora.Browser.Version === '8.0' || Tangora.Browser.Version === '9.0');

				var topHead = topRef.document.getElementsByTagName('head')[0];

				for (var headElement in wrapper.HeadBag)
				{
					if (wrapper.HeadBag.hasOwnProperty(headElement))
					{
						topHead.appendChild(wrapper.HeadBag[headElement]);
					}
				}

				var width,
				endWidth,
				height = wrapper.offsetHeight >> 0,
				srcElement,
				contents = wrapper.firstChild.lastChild,
				elmWidth,
				currentElm;

				try
				{
					elmWidth = $ts.getCascadedStyle(contents, 'width');
				}
				catch (ex)
				{
					elmWidth = contents.offsetWidth + 'px';
				}

				if ($ts.exists(elmWidth) && elmWidth.match('%$') == '%')
				{
					wrapper.style.width = '100%';
					width = contents.offsetWidth;
					wrapper.style.width = null;
					wrapper.centerElement = contents;
					endWidth = elmWidth;
					contents.style.width = "100%";
					contents.style.overflow = 'hidden';

					// Remember scrollbar!

				}
				else
				{
					wrapper.centerElement = wrapper;
					width = wrapper.offsetWidth >> 0;
					endWidth = width;
				}

				if ($ts.exists(srcEvent))
				{
					srcElement = srcEvent.srcElement;

					if (!srcElement) // IE
					{
						srcElement = srcEvent.target;
					}
				}
				else if (preset === 'fromElement')
				{
					preset = '';
				}

				function ContentsFadeIn()
				{
					wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': 150, 'StartOpacity': 50, 'EndOpacity': 100}]);
					wrapper.RunAnimations();
				};

				switch (preset)
				{
					case 'flying':

						wrapper.style.top = '-' + height + 'px';
						wrapper.style.left = '-' + width + 'px';
						wrapper.QueueAnimations([{ 'Type': 'moveToCenter', 'AnimationTime': duration, 'Axis': 'both' }, { 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 0, 'EndOpacity': 100}]);

						break;

					case 'fromElement':

						if (srcElement !== undefined)
						{
							var startPos = topRef.$ts.getPosition(srcElement, true, topRef);
							var startLevel = ((100 / width) * srcElement.offsetWidth) / 100;
							if (ie8or9)
							{
								wrapper.style.width = srcElement.offsetWidth + 'px';
								wrapper.style.height = srcElement.offsetHeight + 'px';
								wrapper.style.overflow = 'hidden';
								wrapper.style.left = startPos[0] + 'px';
								wrapper.style.top = startPos[1] + 'px';
								wrapper.firstChild.style.zoom = startLevel;
								wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 0, 'EndOpacity': 50 }, { 'Type': 'moveToCenter', 'AnimationTime': duration, 'Axis': 'both', 'Width': width * startLevel, 'Height': height * startLevel, 'WindowRef': topRef }, { 'Type': 'resize', 'AnimationTime': duration, 'Width': width, 'Height': height, 'RelativeToCenter': true, 'HideOverflow': true, 'WindowRef': topRef, 'Delay': duration}]);
								$ts.Observer.Register(wrapper.id, 'ResizeStep', function () { wrapper.firstChild.style.zoom = parseInt(wrapper.style.width, 10) / width; });
							}
							else
							{
								Tangora.PublicAnimation.Zoom(wrapper, { "Time": 0 }, null, 0, startLevel, topRef);
								wrapper.style.left = Tangora.PublicAnimation.ZoomPosCorrection(wrapper, startPos[0]) + 'px';
								wrapper.style.top = Tangora.PublicAnimation.ZoomPosCorrection(wrapper, startPos[1]) + 'px';
								wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 0, 'EndOpacity': 50 }, { 'Type': 'moveToCenter', 'AnimationTime': duration, 'Axis': 'both', 'Width': width * startLevel, 'Height': height * startLevel, 'WindowRef': topRef }, { 'Type': 'zoom', 'AnimationTime': duration, 'Start': startLevel, 'End': 1, 'RelativeToCenter': true, 'WindowRef': topRef, 'Delay': duration}]);
							}
							$ts.Observer.RegisterOneShot(wrapper.id, 'AnimationsEnded', ContentsFadeIn);
						}

						break;

					default:
						Tangora.PublicAnimation.MoveToCenter(wrapper, { "Time": 0 }, null, 'both', null, width, height, true, topRef);
						//if (ie8or9)
						{
							wrapper.style.width = '1px';
							wrapper.style.height = '1px';
							wrapper.style.overflow = 'hidden';
							wrapper.firstChild.style.zoom = 0;
							wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 0, 'EndOpacity': 100 }, { 'Type': 'resize', 'AnimationTime': duration, 'Width': width, 'Height': height, 'RelativeToCenter': true, 'HideOverflow': true, 'WindowRef': topRef}]);
							$ts.Observer.Register(wrapper.id, 'ResizeStep', function () { wrapper.firstChild.style.zoom = parseInt(wrapper.style.width, 10) / width; });
						}
						//						else
						//						{
						//							Tangora.PublicAnimation.Zoom(wrapper, { "Time": 0 }, null, 0, 1, topRef);
						//							wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 0, 'EndOpacity': 100 }, { 'Type': 'zoom', 'AnimationTime': duration, 'Start': 0, 'End': 1, 'RelativeToCenter': true, 'WindowRef': topRef}]);
						//						}
				}

				$ts.Observer.RegisterOneShot(overlay.id, 'AnimationsEnded', function ()
				{
					// Ensure overlay is the same height as everything else.
					overlay.AdjustSize(wrapper);
				});

				var closeCalledWhileOpening = false,
				closeCalledCallback = null;

				$ts.Observer.RegisterOneShot(wrapper.id, 'AnimationsEnded', function ()
				{
					// Ensure overlay is the same height as everything else.
					overlay.AdjustSize(wrapper);

					wrapper.Visible = true;

					$ts.Observer.Broadcast(lightboxId, 'ShowComplete');

					LightBox.SetCloseEvents(lightboxId, true, true);

					if (ie8or9)
					{
						wrapper.style.overflow = '';
						wrapper.style.filter = 'none';
						$ts.Observer.UnRegister(wrapper.id, 'ResizeStep');
					}

					if (closeCalledWhileOpening === true)
					{
						LightBox.Close(lightboxId, closeCalledCallback);
					}

					wrapper.style.width = endWidth;
					wrapper.style.overflow = '';

					LightBox.SetCloseButton(lightboxId);
					var closeBtn = closeButtons[lightboxId];

					if ($ts.exists(closeBtn) && $ts.exists(closeBtn.parentNode))
					{
						if (closeBtn.parentNode.firstChild != closeBtn) // Move close button to the top of the wrapper
						{
							closeBtn.parentNode.insertBefore(closeBtn, closeBtn.parentNode.firstChild);
						}

						var closeBtnWrapper = closeBtn.nextSibling.insertBefore($ts.createElement('div', null, { "position": "relative" }), closeBtn.nextSibling.firstChild),
						innerWrapper = closeBtnWrapper.parentNode.appendChild($ts.createElement('div', null, { "overflow": "hidden" })),
						offSetX = closeBtnWrapper.offsetLeft - closeBtnWrapper.parentNode.offsetLeft,
						offSetY = closeBtnWrapper.offsetTop - closeBtnWrapper.parentNode.offsetTop;

						innerWrapper.appendChild(closeBtnWrapper.nextSibling);

						closeBtn.style.right = (-1 * (offSetX + 18)) + 'px';
						closeBtn.style.top = (-1 * (offSetY + 18)) + 'px';

						closeBtnWrapper.appendChild(closeBtn);

						closeBtn.style.display = '';
						closeBtnWrapper.parentNode.style.overflow = 'visible';
					}
				});

				$ts.Observer.RegisterOneShot('LightBox', 'CloseCalled', function (who, what, callback)
				{
					closeCalledWhileOpening = true;
					closeCalledCallback = callback;
				});

				overlay.RunAnimations();
				wrapper.RunAnimations();
			};

			var images = wrapper.getElementsByTagName('img'),
			imagesNotLoaded = 0;

			// #region Image preparations

			function ImageLoaded(image)
			{
				var currentView = Tangora.PublicAnimation.CurrentView(),
				maxWidth = (currentView.Width * .95) >> 0,
				maxHeight = (currentView.Height * .95) >> 0;

				image.onload = null;
				image.onerror = null;

				// #region Constrain dimensions

				var ratio = image.offsetWidth / image.offsetHeight;

				if (image.offsetWidth > maxWidth)
				{
					image.style.width = maxWidth + 'px';
					image.style.height = ((maxWidth / ratio) >> 0) + 'px';
					image.style.msInterpolationMode = 'bicubic';
				}

				if (image.offsetHeight > maxHeight)
				{
					image.style.width = ((maxHeight * ratio) >> 0) + 'px';
					image.style.height = maxHeight + 'px';
					image.style.msInterpolationMode = 'bicubic';
				}

				// #endregion

				imagesNotLoaded -= 1;

				if (imagesNotLoaded === 0)
				{
					PreparationsComplete();
				}
			};

			for (var i = 0; i < images.length; i++)
			{
				(function test(image)
				{
					if (image !== closeButtons[lightboxId] && image.complete === false)
					{
						image.onload = function () { ImageLoaded(this); };
						image.onerror = function () { ImageLoaded(this); };
						imagesNotLoaded += 1;
					}
				})(images[i]);
			}

			// #endregion

			if (imagesNotLoaded === 0)
			{
				PreparationsComplete();
			}
		}
	};

	this.SetCloseEvents = function (lightboxId, esc, overlay, custom)
	{
		/// <summary>
		/// Wires up close event handlers
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="esc">Boolean whether or not [Esc] key closes the lightbox.</param>
		/// <param name="overlay">Boolean whether or not mouse click on the overlay closes the lightbox.</param>
		/// <param name="custom">An array of (element)/(event type) pairs, that closes the lightbox. (optional)</param>

		var overlayElm = overlay === true ? top.$elm(lightboxId + '_Overlay') : null;

		closeCallbacks[lightboxId] = function (evt)
		{
			try
			{
				if (evt.type === 'mouseup' || (evt.type === 'keyup' && evt.keyCode === 27) || evt.type === 'resize') // Mouse click on overlay or [Esc]
				{
					LightBox.Close(lightboxId, evt.callback);

					top.$ts.removeEvent(top.window, 'resize', closeCallbacks[lightboxId]);

					if (esc)
					{
						top.$ts.removeEvent(top.document.body, 'keyup', closeCallbacks[lightboxId]);
						$ts.removeEvent(document.body, 'keyup', closeCallbacks[lightboxId]);
					}

					if (overlayElm !== null)
					{
						$ts.removeEvent(overlayElm, 'mouseup', closeCallbacks[lightboxId]);
					}

					if ($ts.exists(custom))
					{
						for (var elm in custom)
						{
							if (custom.hasOwnProperty(elm))
							{
								$ts.removeEvent(elm, custom[elm], closeCallbacks[lightboxId]);
							}
						}
					}
				}
			}
			catch (exc) { }
		};

		//top.$ts.addEvent(top.window, 'resize', closeCallbacks[lightboxId]);

		if (esc)
		{
			top.$ts.addEvent(top.document.body, 'keyup', closeCallbacks[lightboxId]);
			$ts.addEvent(document.body, 'keyup', closeCallbacks[lightboxId]);
		}

		if (overlayElm !== null)
		{
			$ts.addEvent(overlayElm, 'mouseup', closeCallbacks[lightboxId]);
		}

		if ($ts.exists(custom))
		{
			for (var elm in custom)
			{
				if (custom.hasOwnProperty(elm))
				{
					$ts.addEvent(elm, custom[elm], closeCallbacks[lightboxId]);
				}
			}
		}
	};

	this.Close = function (lightboxId, callback)
	{
		/// <summary>
		/// Hides both overlay and lightbox.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="callback">Callback to execute when the lightbox is closed. (optional)</param>

		if ($ts.exists(lightboxId))
		{
			var lightboxClosed = false,
			overlayRemoved = false,
			duration;

			if (typeof Tangora.Globals.LightBoxTransitionOutDuration == 'undefined')
			{
				Tangora.Globals.LightBoxTransitionOutDuration = 250;
			}

			duration = Math.floor(Tangora.Globals.LightBoxTransitionOutDuration / 2);

			function CloseSignal()
			{
				if (lightboxClosed && overlayRemoved)
				{
					$ts.Observer.Broadcast(lightboxId, 'Closed');

					if ($ts.exists(callback))
					{
						callback();
					}

					closeButtons[lightboxId].style.display = 'none';
				}
			}

			var overlay = LightBox.GetOverlay(lightboxId),
			wrapper = GetWrapper(lightboxId);

			if (wrapper.parentNode !== DomWrapper)
			{
				wrapper.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': 100, 'EndOpacity': 0}]);
				$ts.Observer.RegisterOneShot(wrapper.id, 'AnimationsEnded', function ()
				{
					DomWrapper.appendChild(wrapper);
					wrapper.Visible = false;
					clearHead();
					lightboxClosed = true;
					CloseSignal();
				});
				wrapper.RunAnimations();
			}

			function clearHead()
			{
				var topHead = top.document.getElementsByTagName('head')[0];

				for (var headElement in wrapper.HeadBag)
				{
					if (wrapper.HeadBag.hasOwnProperty(headElement))
					{
						try
						{
							topHead.removeChild(wrapper.HeadBag[headElement]);
						}
						catch (ex) { }
					}
				}
			}

			if (overlay.parentNode)
			{
				overlay.QueueAnimations([{ 'Type': 'opacityFade', 'AnimationTime': duration, 'StartOpacity': overlay.endopacity, 'EndOpacity': 0, 'Delay': 100}]);
				$ts.Observer.RegisterOneShot(overlay.id, 'AnimationsEnded', function ()
				{
					try
					{
						overlay.parentNode.removeChild(overlay);
					} catch (err) { }
					overlayRemoved = true;
					CloseSignal();
				});
				overlay.RunAnimations();
			}
		}
		else
		{
			$ts.Observer.Broadcast('LightBox', 'CloseCalled', callback);

			for (var id in closeCallbacks)
			{
				closeCallbacks[id]({ "type": 'mouseup', "callback": callback });
			}
		}
	};

	this.Init = function (lightboxId, contents, bgColor, padding, overlayColor, overlayOpacity, callback, IRenderResult)
	{
		/// <summary>
		/// Hides both overlay and lightbox.
		/// </summary>
		/// <param name="lightboxId">Id of the lightbox in question.</param>
		/// <param name="contents">The contents to show in the lightbox.</param>
		/// <param name="bgColor">The background color of the lightbox.</param>
		/// <param name="padding">Padding around the contents</param>
		/// <param name="overlayColor">The css color of the overlay.</param>
		/// <param name="overlayOpacity">The css opacity of the overlay.</param>
		/// <param name="callback">Callback function, for when the lightbox has been added to DOM.</param>
		/// <param name="IRenderResult">An object containing requirements the top document needs to fullfill, in order to show the lightbox, such ass css, scripts and includes.</param>


		if (!$ts.exists(wrappers[lightboxId]))
		{
			if (!$ts.exists(contents, 'tagName'))
			{
				contents = $ts.createElement('div', { "innerHTML": contents.replace(/\{newline\}/gi, '\r\n') });
			}
			else if (contents.tagName.toLowerCase() != 'div')
			{
				var tempParent = $ts.createElement('div');
				tempParent.appendChild(contents);
				contents = tempParent;
			}

			LightBox.SetOverlay(lightboxId, overlayColor, overlayOpacity);
			LightBox.SetContents(lightboxId, contents, bgColor, padding, callback, IRenderResult);
		}
		else
		{
			if ($ts.exists(callback))
			{
				callback();
			}
		}
	};


	/// <summary>
	/// A ListBox invocation that fetches the main pagecontent from a page, with or without a pagetemplate. The containing element will always have overflow:auto set.
	/// </summary>
	/// <param name="id">The page id</param>
	/// <param name="params">The param list to the url, without the page id. MUST start with an ampersand. (Optional, default=null)</param>
	/// <param name="excludePageTemplate">Switch that specifies wether the pagetemplate is included or not. (Optional, default = false)</param>
	/// <param name="background">Css background attribute, fx. to provide a backgroundcolor for the container element. (Optional, default = null)</param>
	/// <param name="width">The width of the container element (Optional, default = null)</param>
	/// <param name="height">The height of the container element. (Optional, default = null)</param>
	this.OpenPageContent = function (id, params, excludePageTemplate, background, width, height, lbSettings, evtSrc)
	{
		var url = '/displaypage.ashx?id=' + id + '&pagetemplatebypass=' + (excludePageTemplate ? '1' : '0') + params + '&rndkey=' + (new Date().getTime());
		var settings = { 'url': url, 'async': false, 'type': 'GET' };
		var result = $ts.ajax(settings);
		var componentParts = result.split('x#x|x#x');

		var scriptReferences = componentParts[0].split('#|#|#');
		var inlineScripts = componentParts[1].split('#|#|#');
		var cssReferences = componentParts[2].split('#|#|#');
		var cssBlock = componentParts[3];

		if (!$ts.isNullOrEmpty(cssReferences))
		{
			for (var i = 0; i < cssReferences.length; i++)
			{
				if (!$ts.isNullOrEmpty($ts.trim(cssReferences[i])))
				{
				    cssBlock = $ts.ajax({ "url": cssReferences[i], 'async': false }) + cssBlock;
				}
			}
		}

		var html = componentParts[4];

		var style = '';

		var contentView = null;

		function getContentView()
		{
			if (!$ts.exists(contentView))
			{
				contentView = {};
				var mergefield_main = $elm('mergefield_main');

				if ($ts.exists(mergefield_main))
				{
					contentView.Width = mergefield_main.offsetWidth;
					contentView.Height = mergefield_main.offsetHeight;
				}
			}

			return contentView;
		}

		if (width > 0)
		{
			style += 'width:' + width + 'px;';
		}
		else
		{
			var view = getContentView();
			if ($ts.exists(view) && $ts.exists(view.Width))
			{
				var viewWidth = (view.Width * .90) >> 0;
				style += 'max-width: ' + viewWidth + 'px;';
			}
		}

		style += 'overflow-x:hidden;';

		if (height > 0)
		{
			style += 'height:' + height + 'px;';
		}
		else
		{
			var viewHeight = (Tangora.PublicAnimation.CurrentView().Height * .90) >> 0;
			style += 'max-height: ' + viewHeight + 'px;';
		}

		style += 'overflow-y:auto;';

		if ($ts.exists(background) && background != '')
		{
			style += 'background:' + background + ';';
		}

		if ($ts.exists(lbSettings) && $ts.exists(lbSettings.Padding))
		{
			style += 'padding:' + lbSettings.Padding + 'px';
		}

		if (style !== '')
		{
			style = ' style="' + style + '"';
		}

		if ($ts.exists(lbSettings))
		{
			var styleWrapperId = lbSettings.LightBoxId + 'StyleWrapper';

			html = '<div id="' + styleWrapperId + '" ' + style + '>' + html + '</div>';

			this.Dispose(lbSettings.LightBoxId);

			LightBox.Init(lbSettings.LightBoxId, html, lbSettings.BackgroundColor, null, lbSettings.OverlayColor, lbSettings.OverlayOpacity, function ()
			{
				var srcWrapper = null;

				if ($ts.exists(evtSrc))
				{
					srcWrapper = { "srcElement": evtSrc };
				}

				LightBox.SetCloseButton(lbSettings.LightBoxId, lbSettings.CloseButtonImagePath, lbSettings.CloseButtonHoverImagePath);
				LightBox.Show(lbSettings.LightBoxId, lbSettings.Preset, srcWrapper);
			}, { "Css": cssBlock, "Script": inlineScripts, "ScriptIncludes": scriptReferences });
			return lbSettings.LightBoxId;
		}
		else
		{
			return this.Open(html, null, null, cssBlock, inlineScripts, scriptReferences);
		}
	}

	this.Open = function (contents, overlayColor, overlayOpacity, css, script, scriptIncludes, lightboxId, lbSettings)
	{
		/// <summary>
		/// The simplest form of lightbox implementation, the specified contents will immediately be shown in a lightbox, with default settings if none are provided.
		/// </summary>
		/// <param name="contents">The contents to show in the lightbox.</param>
		/// <param name="overlayColor">The css color of the overlay. (Optional, default = null)</param>
		/// <param name="overlayOpacity">The css opacity of the overlay. (Optional, default = null)</param>
		/// <param name="css">Styles that will be injected when the lightbox is opened, and removed when the lightbox is closed. (Optional, default = null)</param>
		/// <param name="script">Javascript that will be injected when the lightbox is opened, and removed when the lightbox is closed. (Optional, default = null)</param>
		/// <param name="scriptIncludes">External script files that will be injected when the lightbox is opened, and removed when the lightbox is closed. Either a single source path string, or an array of source path strings. (Optional, default = null)</param>
		/// <param name="lightboxId">Used to specify which id the lightbox will use. (Optional, default = autogenerated)</param>

		if (!$ts.exists(contents) || contents === '') { return; }

		if (!$ts.exists(lightboxId))
		{
			lightboxId = new Date().getTime() + '';
		}

		function EnsureValue(input, defValue) { return $ts.exists(input) ? input : defValue; }
		function EnsureArray(input) { return $ts.exists(input) ? (input instanceof Array ? input : [input]) : null; }

		overlayColor = EnsureValue(overlayColor, '#000000');
		overlayOpacity = EnsureValue(overlayOpacity, '50');
		css = EnsureValue(css, null);
		script = EnsureValue(script, null);
		scriptIncludes = EnsureArray(scriptIncludes);

		LightBox.Init(lightboxId, contents, null, null, overlayColor, overlayOpacity, function ()
		{
			if ($ts.exists(lbSettings))
			{
				if ($ts.exists(lbSettings.NoCloseButton) && lbSettings.NoCloseButton === true)
				{
					LightBox.SetCloseButton(lightboxId, 'none');
				}

				if ($ts.exists(lbSettings.AlternateCloseButton))
				{
					LightBox.SetCloseButton(lightboxId, lbSettings.AlternateCloseButton, lbSettings.AlternateCloseButtonHover);
				}
			}

			LightBox.Show(lightboxId);
		}, { "Css": css, "Script": script, "ScriptIncludes": scriptIncludes });

		return lightboxId;
	};

	this.IsOpen = function (lightboxId)
	{
		var wrapper = GetWrapper(lightboxId, null, true);
		return $ts.exists(wrapper) && wrapper.Visible === true;
	};

	this.SetPreview = function (id, overlayColor, overlayOpacity, url, refresh, callback)
	{
		/// <summary>
		/// Updates a lightbox preview control, with the specified settings.
		/// </summary>
		/// <param name="id">The contents to show in the lightbox.</param>
		/// <param name="overlayColor">The css color of the overlay. (Optional, default = #000)</param>
		/// <param name="overlayOpacity">The css opacity of the overlay. (Optional, default = 50)</param>
		/// <param name="url">The url to view the lightbox settings against. (Required initially - afterwards optional, default = previous)</param>
		var lightboxId = id + 'LightBox',
		parent = $elm(id),
		frame;

		if (!$ts.exists(overlayColor)) { overlayColor = $ts.exists(parent.OverlayColor) ? parent.OverlayColor : overlayColor = '#000'; }
		if (!$ts.exists(overlayOpacity)) { overlayOpacity = $ts.exists(parent.OverlayOpacity) ? parent.OverlayOpacity : 50; }

		if (!$ts.exists(parent.OverlayColor)) { parent.OverlayColor = overlayColor; }
		if (!$ts.exists(parent.OverlayOpacity)) { parent.OverlayOpacity = overlayOpacity; }

		function InjectLightBox()
		{
			frame = parent.firstChild;
			var frameWindow = frame.contentWindow;

			try
			{
				var dummyContents = frameWindow.$ts.createElement('div', null,
				{
					"background-color": '#fff',
					"width": '640px',
					"height": '480px'
				});

				frameWindow.LightBox.Init(lightboxId, dummyContents, null, null, overlayColor, overlayOpacity, function ()
				{
					frameWindow.LightBox.Show(lightboxId, null, null, frameWindow);

					if ($ts.exists(callback))
					{
						callback();
					}
				});
			}
			catch (ex) { }
		}

		if ($ts.exists(refresh) && refresh === true)
		{
			parent.firstChild.contentWindow.LightBox.Close(lightboxId, InjectLightBox);
		}
		else
		{
			if ($ts.exists(url))
			{
				while ($ts.exists(parent.firstChild)) { parent.removeChild(parent.firstChild); }
				parent.appendChild(PagePreview.Generate(url, 200, 200, 0.1953125, InjectLightBox));
			}
			else
			{
				frame = parent.firstChild;
				if ($ts.exists(frame))
				{
					var frameWindow = frame.contentWindow,
					animation = { "Time": 250 },
					overlay = frameWindow.LightBox.GetOverlay(lightboxId);
					frameWindow.Tangora.PublicAnimation.ChangeColor(overlay, animation, null, null, parent.OverlayColor, overlayColor);
					frameWindow.Tangora.PublicAnimation.OpacityFade(overlay, animation, null, parent.OverlayOpacity, overlayOpacity);
				}
			}

			parent.OverlayColor = overlayColor;
			parent.OverlayOpacity = overlayOpacity;

			if ($ts.exists(callback))
			{
				callback();
			}
		}
	};

	this.UrlDecodeRFC2396 = function (encoded)
	{
		// Replace + with ' '
		// Replace %xx with equivalent character
		// Put [ERROR] in output if %xx is invalid.
		var HEXCHARS = "0123456789ABCDEFabcdef";
		var decoded = "";
		var i = 0;
		while (i < encoded.length)
		{
			var ch = encoded.charAt(i);
			if (ch == "+")
			{
				decoded += " ";
				i++;
			}
			else if (ch == "%")
			{
				if (i < (encoded.length - 2) && HEXCHARS.indexOf(encoded.charAt(i + 1)) != -1 && HEXCHARS.indexOf(encoded.charAt(i + 2)) != -1)
				{
					decoded += unescape(encoded.substr(i, 3));
					i += 3;
				}
				else
				{
					alert('Bad escape combination near ...' + encoded.substr(i));
					decoded += "%[ERROR]";
					i++;
				}
			}
			else
			{
				decoded += ch;
				i++;
			}
		}

		return decoded;
	};

	this.EnforceOnlyOnce = function (ligtboxId)
	{
		var keyName = 'LightBoxOnlyOnce:' + ligtboxId,
		allreadyShown = $ts.getCookie(keyName) === null;
		$ts.setCookie(keyName, 'allready shown', 3650);

		return allreadyShown;
	};
}

window.LightBox = new _LightBox('LightBox');

