var ts_majorVersion = '756';
var ts_minorVersion = '0';
var buildCode = '?ts='+ts_majorVersion+'.'+ts_minorVersion;

(function ()
{
	// Private local vars
	var _htmlDecoder = null;

	function _$ts()
	{
		// Public local vars
		this.ajaxSettings = { 'url': 'blank.htm', 'async': true, 'data': '', 'type': 'GET', 'noncacheable': true };
        this.debug = {'events':{'registration':false, 'invocation':false}, 'lazyload':false, 'observer':{'broadcast':false,'registration':false}}; /*Set broadcast to false to stop spamming the event log*/
	}

	_$ts.prototype =
	{
		getComputedStyle: function (element, style)
		{
			/// <summary>
			/// Gets the current active value, for a specified style.
			/// </summary>
			/// <param name="element">The element.</param>
			/// <param name="style">The style to look up.</param>
			/// <returns>The active value of the style.</returns>
			var value = "";
            if (document.defaultView && document.defaultView.getComputedStyle)
			{
				value = document.defaultView.getComputedStyle(element, "").getPropertyValue(style);
			}
            else if (element.currentStyle)
			{
                // Fallback for IE <= 8 , though not exactly the same deal...
                // See: erik.eae.net/archives/2007/07/27/18.54.15/ for a great explanation between computed and cascaded styles.
				style = style.replace(/\-(\w)/g, function (match, capture)
				{
					return capture.toUpperCase();
				});
				value = element.currentStyle[style];
			}

			return value;
		},
		convertRGBColorToHex: function(color)
		{
			var measureDummy = document.body.appendChild($ts.createElement('div', null,
			{
				"position":"absolute",
				"top":"-1px",
				"left":"-1px",
				"width":"1px",
				"height":"1px",
				"color": color
			}));

			color = getComputedStyle(measureDummy).color;
			document.body.removeChild(measureDummy);

			var match = color.match(/.*rgb[\s]*\([\s]*(\d{1,3})[\s]*,[\s]*(\d{1,3})[\s]*,[\s]*(\d{1,3})[\s]*\)[\s]*$/im);

			if (match !== null)
			{
				function ToHex(val)
				{
					val = parseInt(val, 10).toString(16);

					if (val.length === 1)
					{
						val = '0' + val;
					}

					return val;
				};

				color = '#' + ToHex(match[1]) + ToHex(match[2]) + ToHex(match[3]);
			}

			return color;
		},
		getPosition: function (element, absolute, topRef)
		{
			/// <summary>
			/// Gets an elements correct x and y position.
			/// </summary>
			/// <param name="element">The element.</param>
			/// <param name="topRef">The window to use as reference for absolute positioning (optional, default = window.top).</param>
			/// <returns>An array [x,y].</returns>
			var curleft = 0, curtop = 0, parentFrames;

			if (!$ts.exists(topRef))
			{
				topRef = window.top;
			}

			if (element.offsetParent)
			{
				do
				{
					curleft += element.offsetLeft;
					curleft -= element.scrollLeft;
					curtop += element.offsetTop;
					curtop -= element.scrollTop;
					element = element.offsetParent;
				} while (this.exists(element));
			}

			if (absolute && window.self != topRef)
			{
				parentFrames = parent.$elms('iframe', 'frame');

				for (var i = 0; i < parentFrames.length; i++)
				{
					if (parentFrames[i].contentWindow == window)
					{
						element = parentFrames[i];

						var parentPos = parent.$ts.getPosition(element, true);

						curleft += parentPos[0];
						curtop += parentPos[1];
						break;
					}
				}
			}

			return [curleft, curtop];
		},
		hasClass: function (element, className)
		{
			/// <summary>
			/// Checks if an element impliments a specified class.
			/// </summary>
			/// <param name="element">The element.</param>
			/// <param name="className">The class to look up.</param>
			/// <returns>Boolean whether or not the class is implemented.</returns>
			if (this.exists(className) && className !== '')
			{
				return new RegExp("\\b" + className + "\\b").test(element.className);
			}
			else
			{
				throw 'className must be specified.';
			}
		},
		addClass: function (element, className)
		{
			/// <summary>
			/// Adds a specified class to the element.
			/// </summary>
			/// <param name="element">The element.</param>
			/// <param name="className">The class to add.</param>
			if (!this.hasClass(element, className))
			{
                if (element.className.length < 1) element.className = className;
                else element.className += ' ' + className;
			}
		},
		removeClass: function (element, className)
		{
			/// <summary>
			/// Removes a specified class from the element.
			/// </summary>
			/// <param name="element">The element.</param>
			/// <param name="className">The class to remove.</param>
			element.className = element.className.replace(new RegExp("\\b" + className + "\\b", 'g'), '');
			element.className = element.className.replace(/ {2,}/g, " ");
			element.className = this.trim(element.className);
		},
		createElement: function (tagName, props, styles)
		{
			/// <summary>
			/// Instanciate a dom element, with optional expandos.
			/// </summary>
			/// <param name="tagName">The element type.</param>
			/// <param name="props">A jsonized expando bag, that will be appended to the element. (optional)</param>
			/// <param name="styles">A jsonized style bag, that will be appended to the element. (optional)</param>
			/// <returns>Instanciated element.</returns>
			var element = document.createElement(tagName);

			for (var prop in props)
			{
				if (props.hasOwnProperty(prop))
				{
                    // HDN 21.12.2011:
                    // I have added this condition to support html5 data attributes.
                    if (/^data-\w/.test(prop))
                    {
                        element.setAttribute(prop, props[prop]);
                    }
                    else
                    {
					    element[prop] = props[prop];
                    }
				}
			}

			for (var style in styles)
			{
				if (styles.hasOwnProperty(style))
				{
					element.style[style] = styles[style];
				}
			}

			return element;
		},
        getChildElements: function (elm, type)
        {
            /// <summary>
			/// Returns a childnode array without textnodes, and optionally only containing types of 'type'
			/// </summary>
			/// <param name="elm">The element wich child elements to fetch.</param>			
            /// <param name="type">Only elements of type will be included. (optional)</param>			
			/// <returns>Array.</returns>
            var elements = [];
            var tagType = type ? type.toLowerCase() : null;
            for(var i=0;i<elm.childNodes.length;i++)
            {
                if (elm.childNodes[i].nodeType===3) continue;
                if (tagType!=null && elm.childNodes[i].tagName.toLowerCase()!==tagType) continue;
                elements.push(elm.childNodes[i]);
            }
            return elements;
        },
        getNextSiblingElement: function (elm)
        {
            /// <summary>
			/// Returns the next sibling
			/// </summary>
			/// <param name="elm">The element wich sibling element to fetch.</param>			
            /// <returns>DOM element or null.</returns>            
            var next = null;
            if (elm && elm.nextSibling)
            {
                while (elm.nextSibling)
                {
                    if (elm.nextSibling.nodeType == 1)
                    {
                        next = elm.nextSibling;
                        break;
                    }
                    else
                    {
                        elm = elm.nextSibling;
                    }
                }
            }
            return next;
        },
        getPreviousSiblingElement: function (elm)
        {
            /// <summary>
			/// Returns the previous sibling
			/// </summary>
			/// <param name="elm">The element wich sibling element to fetch.</param>			
            /// <returns>DOM element or null.</returns>            
            var prev = null;
            if (elm && elm.previousSibling)
            {
                while (elm.previousSibling)
                {
                    if (elm.previousSibling.nodeType == 1)
                    {
                        prev = elm.previousSibling;
                        break;
                    }
                    else
                    {
                        elm = elm.previousSibling;
                    }
                }
            }
            return prev;
        },
        isElementInArray: function (array, elm)
        {
            /// <summary>
			/// Check an array for the existense of elm
			/// </summary>
            /// <param name="array">The array.</param>			
			/// <param name="elm">The element to look for</param>			
            /// <returns>boolean</returns>
            for(var i=0;i<array.length;i++)
            {
                if (array[i]===elm) return true;
            }
            return false;
        },
        consoleWrite: function(s)
        {
            /// <summary>
			/// Writes s to the console, prefixing datetime and milliseconds 
			/// </summary>
            /// <param name="s">The string to write to the console.</param>						
            /// <returns>void</returns>
			if (typeof console !== 'undefined')
			{
				var date = new Date();
				console.log(date.toLocaleTimeString()+','+date.getMilliseconds()  + ': ' + s);
			}
        },
		addEvent: function (el, type, fn, bubbleDown)
		{            
            /// <summary>
			/// Binds a method to a DOM event.
			/// </summary>
            /// <param name="el">The element to bind to.</param>
            /// <param name="type">The event type.</param>
            /// <param name="fn">The function to be called.</param>
            /// <param name="bubbleDown">propagation or bubbling, default = true (bubbling).</param>
            /// <returns>void</returns>
            if (!$ts.exists(el)) return;
			if (!$ts.exists(bubbleDown))
			{
				bubbleDown = false;
			}

            if ($ts.debug.events.registration) 
            {
                var s = '';
                if ($ts.exists(fn, 'name')) s = fn.name;
                else
                {
                    s = 'anonymous (';
                    var t = fn.toString().split('\n');
                    if (t.length < 4) s+= fn.toString();
                    else {
                        for(var i=0; i<4;i++) s+= t[i]+'\n';
                        s+='...';
                    }
                }
                $ts.consoleWrite('addEvent \''+ type + '\' on ' + ($ts.exists(el, 'tagName') ? el.tagName : 'window') + ' ' + (bubbleDown ? 'propagation' : 'bubbeling') + ' f() = ' + s);
            }

            if ($ts.debug.events.invocation) 
            {
                //var f = function() {$ts.consoleWrite('Invoke - ' + fn.toString()); fn()};
                //fn = f;
            }

			if (el === window && type.toLowerCase() === 'load' && window.loaded === true)
			{
				fn();
				return;
			}

			if (window.addEventListener)
			{
				try
				{
					el.removeEventListener(type, fn, bubbleDown);
				}
				catch (ex) { }

				el.addEventListener(type, fn, bubbleDown);
			}
			else if (window.attachEvent) // IE 8-
			{
				if (type.match(/(?:on)?DOMContentLoaded/i))
				{
					// Diego Perini's hack
					var doc = window.document, done = false;
					init = function () { if (!done) { done = true; fn(); } };
					(function () { try { doc.documentElement.doScroll('left'); } catch (e) { setTimeout(arguments.callee, 50); return; } init(); })();
					doc.onreadystatechange = function () { if (doc.readyState === 'complete') { doc.onreadystatechange = null; init(); } };
				}
				else // Use pre IE 9 attachEvent
				{
					el.attachEvent('on' + type, fn);
				}
			}
		},
		removeEvent: function (el, type, fn, bubbleDown)
		{
            /// <summary>
			/// Unbinds a method from a DOM event.
			/// </summary>
            /// <param name="el">The element to unbind from.</param>
            /// <param name="type">The event type.</param>
            /// <param name="fn">The function.</param>
            /// <param name="bubbleDown">propagation or bubbling, default = true (bubbling).</param>
            /// <returns>void</returns>
            if (!$ts.exists(el)) return;
			if (!$ts.exists(bubbleDown))
			{
				bubbleDown = false;
			}

            if ($ts.debug.events.registration) 
            {
                var s = '';
                if ($ts.exists(fn, 'name')) s = fn.name;
                else
                {
                    s = 'anonymous (';
                    var t = fn.toString().split('\n');
                    if (t.length < 4) s+= fn.toString();
                    else {
                        for(var i=0; i<4;i++) s+= t[i]+'\n';
                        s+='...';
                    }
                }
                $ts.consoleWrite('removeEvent \''+ type + '\' from ' + ($ts.exists(el, 'tagName') ? el.tagName : 'window') + ' ' + (bubbleDown ? 'propagation' : 'bubbeling') + ' f() = ' + s);
            }

			if (window.removeEventListener)
			{
				el.removeEventListener(type, fn, bubbleDown);
			}
			else if (window.detachEvent)
			{
				el.detachEvent('on' + type, fn);
			}
		},
		cancelBubble: function (evt)
		{        
            /// <summary>
			/// Stops bubbling and propagation of the event, effectly stopping it.
			/// </summary>
            /// <param name="evt">The event to stop</param>            
            /// <returns>boolean (always false)</returns>    
			var e = evt || window.event;
			if (!e) return false;
            if ($ts.debug.events.invocation) $ts.consoleWrite('cancelBubble');
			if (e.preventDefault) e.preventDefault();
			if (e.stopPropagation) e.stopPropagation();
			e.cancelBubble = true;
			e.returnValue = false;
			return false;
		},
		loadScriptAsync: function (src, callback)
		{
			/// <summary>
			/// Loads external script files.
			/// </summary>
			/// <param name="src">The path of the script file.</param>
			/// <param name="callback">A callback to fire, when the script is loaded (optional).</param>
			src = src.toLowerCase();
			var loadedScripts = document.getElementsByTagName('script'),
			notLoaded = true;

			for (var i = 0; i < loadedScripts.length; i++)
			{
				if ($ts.exists(loadedScripts[i].src) && loadedScripts[i].src.toLowerCase().indexOf(src) !== -1)
				{
					notLoaded = false;
					break;
				}
			}

			if (notLoaded)
			{
				document.getElementsByTagName('head')[0].appendChild($ts.createElement('script',
				{
					"type": 'text/javascript', "src": src, "loaded": false,
					"onload": function () { if (!this.loaded && $ts.exists(callback)) { callback(); } this.loaded = true; },
					"onreadystatechange": function () { if (this.readyState == 'complete' || this.readyState == 'loaded') { this.onload(); } }
				}));
			}
		},
		pad: function (input, endLength, char, direction)
		{
			/// <summary>
			/// Pads a string to a specified length.
			/// </summary>
			/// <param name="input">The string to pad.</param>
			/// <param name="char">The character to pad with (optional, default = '0').</param>
			/// <param name="direction">The direction to pad, specified with 'left' or 'right' (optional, default = 'left').</param>
			/// <returns>Padded string.</returns>
			var output = input, right = direction === 'right';

			if (!this.exists(char))
			{
				char = '0';
			}

			while (output.length < endLength)
			{
				if (right)
				{
					output += char;
				}
				else
				{
					output = char + output;
				}
			}

			return output;
		},
		trim: function (input, direction, char)
		{
			/// <summary>
			/// Trims any leading and/or trailing characters.
			/// </summary>
			/// <param name="input">The string to trim.</param>
			/// <param name="direction">Leading, trailing or both, specified with 'left' or 'right' (optional, default = 'both').</param>
			/// <param name="char">The character to trim (optional, default = ' ').</param>
			/// <returns>Trimmed string.</returns>
			if (this.exists(input))
			{
				var left = true,
				right = true,
				startIndex = 0,
				endIndex = input.length;

				switch (direction)
				{
					case 'left': right = false; break;
					case 'right': left = false; break;
					default:
				}

				if (!this.exists(char)) { char = ' '; }
				if (left) { while (startIndex < input.length && input.charAt(startIndex) === char) { startIndex++; } }
				if (right) { while (endIndex > startIndex && input.charAt(endIndex - 1) === char) { endIndex--; } }

				return input.substring(startIndex, endIndex);
			}
		},
        indexOf: function (input, regex, startpos)
		{
            /// <summary>
			/// Works like native string.indexOf() but takes a regex.
			/// </summary>
			/// <param name="input">The string to search.</param>
			/// <param name="regex">The regular expression to search the input string.</param>
			/// <param name="startpos">The 0-based index from where to start searching the string.</param>
			/// <returns>The index of the first occurence of the regex. If nothing is found -1 is returned.</returns>
            var index = input.substring(startpos || 0).search(regex);
            return (index >= 0) ? (index + (startpos || 0)) : index;
        },
		htmlDecode: function(input)
		{
            /// <summary>
			/// Decodes an input string.
			/// </summary>
			/// <param name="input">The string to decode.</param>			
			/// <returns>The decoded string.</returns>
			if (!$ts.exists(_htmlDecoder))
				_htmlDecoder = $ts.createElement('span');

			_htmlDecoder.innerHTML = input;
			input = _htmlDecoder.innerText || _htmlDecoder.textContent || '';

			_htmlDecoder.innerHTML = '';

			return input;
		},
		isNullOrEmpty: function(input)
		{
            /// <summary>
			/// Checks the input for validity - null or empty string ''
			/// </summary>
			/// <param name="input">The string to check.</param>			
			/// <returns>Boolean.</returns>
			return !($ts.exists(input) && input !== '');
		},
		addThousandSeparator: function (value, separator)
		{
			/// <summary>
			/// Inserts a thousand/group separator
			/// </summary>
			/// <param name="value">The number</param>
			/// <param name="separator">The char to be inserted as separator</param>
			/// <returns></returns>
			var re = new RegExp('(-?[0-9]+)([0-9]{3})');

			while (re.test(value)) { value = value.replace(re, '$1' + separator + '$2'); }
			return value;
		},
		formatValue: function (value, decimalsign, numdecimals, extended, thousandSeparator)
		{
			/// <summary>
			/// Stig comments missing.
			/// </summary>
			/// <param name="value"></param>
			/// <param name="decimalsign"></param>
			/// <param name="numdecimals"></param>
			/// <param name="extended"></param>
			/// <returns></returns>
			if (isNaN(value)) { return value; }
			value = ($ts.formatNumber(value, numdecimals)).replace('.', decimalsign);
			extended = true;
			if (extended) { value = $ts.addThousandSeparator(value, $ts.exists(thousandSeparator) ? thousandSeparator : (decimalsign === '.' ? ',' : '.')); }
			return value;
		},
		formatNumber: function (value, numDecimals)
		{
			/// <summary>
			/// Formats a number to only have (numDecimals) after the decimal separator
			/// </summary>
			/// <param name="value">The value to format</param>
			/// <param name="numDecimals">The number of decimals</param>
			/// <returns></returns>
			if (isNaN(value)) { return value; }
			if (value === '') { return value; }
			
            result = String(Math.round(value * (Math.pow(10, numDecimals)))/ (Math.pow(10, numDecimals)));            
            if (numDecimals > 0)
			{					
					var dot = result.indexOf('.');
                    if (dot>-1) while (result.length <= dot + numDecimals) { result += '0'; }
			}			
			return result;
		},
		safeInt: function (input, defaultValue)
		{
			/// <summary>
			/// Force casts an object to integer type, with an optional default value.
			/// </summary>
			/// <param name="input">The value to force cast to an integer.</param>
			/// <param name="defaultValue">The default return value, if the cast fails (optional, default = 0).</param>
			/// <returns>Integer - either af succesful cast of the input value or the default value.</returns>
			var returnValue = parseInt(input, 10);

			if (isNaN(returnValue))
			{
				returnValue = parseInt(defaultValue, 10);

				if (isNaN(returnValue))
				{
					returnValue = 0;
				}
			}

			return returnValue;
		},
		setCookie: function (name, value, days)
		{
			/// <summary>
			/// Set a specified cookie.
			/// </summary>
			/// <param name="name">The name of the cookie.</param>
			/// <param name="value">The value to store in the cookie.</param>
			/// <param name="days">The number of days to store the cookie (optional).</param>
			var expires = '';
			if (this.exists(days))
			{
				var date = new Date();
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				expires = '; expires=' + date.toGMTString();
			}
			document.cookie = name + '=' + value + expires + '; path=/';
		},
		getCookie: function (name)
		{
			/// <summary>
			/// Retrieves a specified cookie.
			/// </summary>
			/// <param name="name">The name of the cookie.</param>
			/// <returns>The cookie object.</returns>
			var regex = new RegExp(name + '=([^;]*)', 'i');
			var obj = regex.exec(document.cookie);
			if (obj)
			{
				return obj[1];
			}
			else
			{
				return null;
			}
		},
		deleteCookie: function (name)
		{
			/// <summary>
			/// Deletes a specified cookie.
			/// </summary>
			/// <param name="name">The name of the cookie.</param>
			this.setCookie(name, '', -1);
		},
		getRootUrl: function (url, withTrailingSlash)
		{
			/// <summary>
			/// Returns the root part of the url.
			/// </summary>
			/// <param name="url">The url to analyze.</param>
			/// <param name="withTrailingSlash">Whether or not the returned url is going to end on / (optional, default false).</param>
			var match = url.match(/(.+?:\/\/[^\/]+).*/),
			root = '';

			if (match != null)
			{
				root = match[1];
			}

			if (withTrailingSlash === true)
			{
				root += '/';
			}

			return root;
		},
		getCurrentRootUrl: function (withTrailingSlash)
		{
			/// <summary>
			/// Returns the root part of the current window url.
			/// </summary>
			/// <param name="withTrailingSlash">Whether or not the returned url is going to end on / (optional, default false).</param>
			return this.getRootUrl(window.location.href, withTrailingSlash);
		},
		isUrlLocal: function (url)
		{
			/// <summary>
			/// Checks if the root of the url is the same as the current window location root.
			/// </summary>
			/// <param name="url">The url to analyze.</param>
			return ((url.substring(0, 1) === '/') ? true : (this.getRootUrl(url) === this.getCurrentRootUrl()));
		},
		toRelativeUrl: function (url)
		{
			/// <summary>
			/// Returns the url, without the root part.
			/// </summary>
			/// <param name="url">The url to analyze.</param>
			return url.replace(this.getRootUrl(url), '');
		},
		exists: function (object, property)
		{
			/// <summary>
			/// Checks whether an object, or an objects property, is defined and not null.
			/// </summary>
			/// <param name="object">The object in question.</param>
			/// <param name="property">The property of the object in question (optional).</param>
			/// <returns>Boolean - true if object is not undefined or null.</returns>
			if (object !== undefined && object !== null)
			{
				if (property !== undefined && property !== null)
				{
					return object[property] !== undefined && object[property] !== null;
				}

				return true;
			}

			return false;
		},
		visible: function (element)
		{			
            /// <summary>
			/// Checks if the DOM element is visible
			/// </summary>
			/// <param name="element">The element to check.</param>			
			/// <returns>Boolean.</returns>
			while (element.nodeName.toLowerCase() != 'body' && element.style.display.toLowerCase() != 'none' && element.style.visibility.toLowerCase() != 'hidden')
			{
				element = element.parentNode;
			}
			if (element.nodeName.toLowerCase() == 'body')
			{
				return true;
			}
			else
			{
				return false;
			}
		},
		getExpando: function (element, property)
		{
			/// <summary>
			/// Retrieves an expando.
			/// </summary>
			/// <param name="element">The element in question.</param>
			/// <param name="property">The name of the expando.</param>
			/// <returns>The value of the expando.</returns>
			return (this.exists(element, property) ? element[property] : element.getAttribute(property));
		},
		collectFormValues: function (fm)
		{
            /// <summary>
			/// Collect values from a form
			/// </summary>
			/// <param name="fm">The form to collect from (element or ID).</param>			
			/// <returns>A x-www-form-urlencoded string containing the values.</returns>
			var form = $elm(fm);
			if (!form) return;
			var p = "";
			var supressAmpersand = false;
			for (var i = 0; i < form.elements.length; i++)
			{
				var item = form.elements[i];
				if (i > 0 && !supressAmpersand)
				{
					p = p + '&';
				}
				if ((item.type == 'checkbox' || item.type == 'radio') && !item.checked) { }
				else
				{
					if (item.type == 'button' || item.type == 'submit')
					{
						supressAmpersand = true;
					} else
					{
						p = p + item.name + '=' + encodeURIComponent(item.value);
						supressAmpersand = false;
					}
				}
			}
			return p;
		},
        collectDataFromContainer: function (container, contextWindow)
        {
            /// <summary>
            /// Collects data from all form-type elements and rich-text fields (rich-text version2 only) under a given container.
            /// This method differs from collectFormValues, by collecting fields by their id, not their name, it doesn't require the fields to be wrapped in a form, and the result is in JSON.
            /// </summary>
            /// <param name="container">A DOM element containing the fields to collect data from.</param>
            /// <param name="contextWindow">Defaults to window object - use this parameter if in a different context (frames).</param>
            /// <returns>A JSON object with name/values of all data found.</returns>
            var result = {};
            if (!container) return result;
            if (!contextWindow) contextWindow = window;

            var elms = $elms('input, textarea, select, .rtefield', container); 
            if (elms)
            {
                for (var i=0; i<elms.length; i++)
                {
                    var elm = elms[i];
                    var name = elm.id;
                    var value = '';
                    switch (elm.tagName.toLowerCase())
                    {
                        case 'input':
                            value = elm.value;
	                        if (elm.type == 'checkbox') value = elm.checked;
	                        else if (elm.type == 'radio') value = elm.checked;
                            break;
                        
                        case 'textarea':
                            value = elm.value;
                            break;

                        case 'select':
                            value = '';
                            if (elm.multiple)
	                        {
	                            var selectedValues = [];
	                            for (var h = 0; h < elm.options.length; h++)
	                            {
	                                if (elm.options[h].selected)
	                                {
	                                    selectedValues.push(elm.options[h].value);
	                                }
	                            }
                                if (selectedValues.length > 0) value = selectedValues.join(',');
	                        }
	                        else
	                        {
	                            if (elm.selectedIndex > -1) value = elm[elm.selectedIndex].value;
	                        }
                            break;

                        case 'div':
                            value = contextWindow.rte(elm.id).GetContent();
                            break;
                    }

                    result[name] = value;
                }
            }
            
            return result;
        },
		ajaxSetup: function (s)
		{
			/// <summary>
			/// Redefines the default settings for ajax requests.
			/// </summary>
			/// <param name="s">JSON object with none or more of the ajax settings (url, async, type, data, complete)</param>
			/// <returns>void.</returns>
			for (var prop in s) { if (this.ajaxSettings.hasOwnProperty(prop)) { this.ajaxSettings[prop] = s[prop]; } }
		},
		ajax: function (s, useProxy)
		{
			/// <summary>
			/// Executes an ajax request based on the supplied settings.
            /// PLEASE NOTE THE PROXY USAGE IS ONLY SUPPORTED FOR SITES WITH THE INTEGRATION MODULE INSTALLED
			/// </summary>
			/// <param name="s">JSON object with none or more of the ajax settings (url, async, type, data, complete)</param>
            /// <param name="useProxy">will relay out of context calls through the server</param>
			/// <returns>responsetext if async is false, otherwise void.</returns>
			for (var prop in this.ajaxSettings) { if (this.ajaxSettings.hasOwnProperty(prop) && !$ts.exists(s, prop)) { s[prop] = this.ajaxSettings[prop]; } }
			var createRequest = function ()
			{
				if (window.XMLHttpRequest) { return new XMLHttpRequest(); }
				else if (window.ActiveXObject)
				{
					var msXMLs = ['Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];
					for (var i = 0; i < msXMLs.length; i++)
					{
						try
						{
							return new ActiveXObject(msXMLs[i]);
						}
						catch (e) { }
					}
				}
			};

			var request = createRequest();
			var url = s.url;
			if (s.type == 'GET' && $ts.exists(s, 'data') && s.data !== '') { url += s.data; }

           if (s.noncacheable == true)
			{
				var cacheKey = new Date().getTime(),
				preFix;

				if (url.indexOf('?') === -1)
				{
					preFix = '?';
				}
				else
				{
					preFix = '&';
				}

				url += preFix + 'ts' + cacheKey + '=' + cacheKey;
			}

			function callback()
			{
				if (request.readyState == 4 && s.complete) { s.complete(request, request.responseText); }
			}
			request.open(s.type, !useProxy ? url : '/services/relay.ashx', s.async);
			request.onreadystatechange = callback;			
            
            if ($ts.exists(s, 'HttpHeaders'))
            {
                for(var key in s.HttpHeaders)
                {
                    request.setRequestHeader(key, s.HttpHeaders[key]);
                }                    
            }

            if (useProxy)
            {                
                request.setRequestHeader('endpoint', url);
                if ((s.type == 'POST' && !$ts.exists('HttpHeaders')) || ($ts.exists('HttpHeaders') && !$ts.exists(s['HttpHeaders'], 'Content-Type'))) 
                    request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
            }

            if (s.type == 'POST') 
            {
                if (s.type == 'POST' && !useProxy && typeof(s.data)!='object') request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');                

                var bag = s.data;
                if (typeof(s.data)=='object')                
                {       
                    var b = '';         
                    var boundary = "---------------------------14737809831466499882746641449"
                    request.setRequestHeader('Content-Type', 'multipart/form-data; boundary='+boundary); 
                    for(var name in bag)
                    {
                        var value = bag[name];
                        b += '\r\n--' + boundary + '\r\n';
                        b += 'Content-Disposition: form-data; name="' + name + '"\r\n';
                        b += 'Content-Type: text/html\r\n\r\n';
                        b += value;
                    }
                    b += '\r\n--' + boundary + '--\r\n';
                    bag = b;
                }
                request.send(bag);
            }
			else request.send(); 

			if (!s.async) { return request.responseText; }
		},
		Observer: (function ()
		{
			/// <summary>
			/// Singleton implementation of the classic observer pattern.
			/// </summary>		    
			var observers = [];

			var _findObserverIndex = function (obs)
			{
				for (var i = 0; i < observers.length; i++)
				{
					var observer = observers[i];
					if (observer.who === obs.who && observer.what === obs.what && (observer.where === obs.where || obs.where==='*'))
					{
						return i;
					}
				}
				return -1;
			};

			var _findObservers = function (who, what)
			{                
				if (typeof (who) != 'string') { who = who.id; }
				var listeners = [];
				//for (var i = 0; i < observers.length; i++)
                for (var i = observers.length-1; i >= 0; i--)
				{
					var observer = observers[i];
					if ((observer.who == who || observer.who==='*') && (observer.what == what || observer.what ==='*'))
					{
						listeners.push(observer);

						if (observer.oneshot)
						{
							// Remove one shots.
							observers.splice(i, 1);
						}
					}
				}
				return listeners;
			};

			var _wrap = function (who, what, where)
			{
				return { 'who': who, 'what': what, 'where': where };
			};

			this.Register = function (who, what, where)
			{
				/// <summary>
				/// Registers an observer to a subject
				/// </summary>
				/// <param name="who">The subject you wish to listen for, or '*' to listen for all senders</param>
				/// <param name="what">The broadcast event name to listen for, '*' to listen for all events</param>
				/// <param name="where">The callback to invoke</param>
				
                observers.push(_wrap(who, what, where));
			};

			this.RegisterOneShot = function (who, what, where)
			{
				/// <summary>
				/// Registers an observer to a subject, but automaticly unregister after the first broadcast
				/// </summary>
				/// <param name="who">The subject you wish to listen for</param>
				/// <param name="what">The broadcast event name to listen for</param>
				/// <param name="where">The callback to invoke</param>
				observers.push({ "who": who, "what": what, "where": where, "oneshot": true });
			};

			this.UnRegister = function (who, what, where)
			{
				/// <summary>
				/// UnRegisters an observer from a subject
				/// </summary>
				/// <param name="who">The subject</param>
				/// <param name="what">The broadcast event name</param>
				/// <param name="where">callback</param>
				var idx = _findObserverIndex(_wrap(who, what, where));
				if (idx > -1)
				{
					observers.splice(idx, 1);
					return true;
				}
				return false;
			};

			this.Broadcast = function (who, what, data)
			{                
				/// <summary>
				/// Broadcasts a message to anyone who wants to listen
				/// </summary>
				/// <param name="who">The subject sending the message</param>
				/// <param name="what">The event name </param>
				/// <param name="data">An object with EventArgs</param>
				var listeners = _findObservers(who, what);

				for (var i = 0; i < listeners.length; i++)
				{
					listeners[i].where.call(who, who, what, data);
				}
			};              
            
            this.BroadcastDebug = function(who, what, args) 
            {
                var s = '';
                try
                {
                    s = JSON.stringify(args);
                }
                catch(e)
                {
                    s = '(#circular ref)';
                }
                $ts.consoleWrite('\'' + who + ' sendt \'' + what + '\' with data: ' + s); 
            }          

			return this;
		})(),
		mathExpressions: (function ()
		{
            /// <summary>
			/// A singleton object capable of doing complex math expressions
			/// </summary>						
			this._decimalSign = ",";
			this._thousandSeparator = ".";
			this._expressions = [];
			this._resolvers = [];

			this.formatNumber = function (value, numDecimals)
			{
                /// <summary>
				/// Formats a number using a fixed set of decimals, and the default decimal sign.
				/// </summary>
				/// <param name="value">The value</param>
                /// <param name="numDecimals">The number of decimals</param>
				/// <returns></returns>
				return $ts.formatNumber(value, numDecimals);
			};

			this.addThousandSeparator = function (value)
			{
				return $ts.addThousandSeparator(value, this._thousandSeparator);
			};

			this.FormatValue = function (value, decimalsign, numdecimals, extended)
			{
				return $ts.formatValue(value, decimalsign, numdecimals, extended);
			};

			this.removeFormatting = function (value)
			{
				/// <summary>
				/// Removes formatting from a value, thus retaing the actual value
				/// </summary>
				/// <param name="value">The formatted value</param>
				/// <returns>The unformatted value</returns>
				var isValue = typeof (value) == 'object';
				value = value + '';
				if (value.indexOf(this._decimalSign) > -1) { value = value.replace(this._decimalSign, '|'); }
				if (isValue) { value = value.replace('.', '|'); }
				var re = new RegExp('\\' + this._thousandSeparator, 'gi');
				value = value.replace(re, '');
				value = value.replace('|', '.');
				return this.toNumber(value);
			};

			this.toNumber = function (value)
			{
				/// <summary>
				/// Parses the input to float.
				/// </summary>
				/// <param name="value">Object to parse</param>
				/// <returns>Float</returns>
				return parseFloat(value, 10);
			};

			this.addResolver = function (name, callback)
			{
				/// <summary>
				/// Resolvers is a mergefield/callback scheme. Thus the name is used in an expression and the callback is the responsible for fetching the correct value.
				/// </summary>
				/// <param name="name">The name of the 'mergefield'</param>
				/// <param name="callback">The function that gives the correct value </param>
                /// <returns>void</returns>
				this._resolvers[name] = callback;
			};

			this.addExpression = function (name, expr)
			{
				/// <summary>
				/// Expressions is a mathematical expresing containg ordinary math expressions and optionally mergefields (resolvers or other expressions), wich will be resolved by the added resolvers.
				/// </summary>
				/// <param name="name">The name of the expression</param>
				/// <param name="expr">The expression fx. 0.25*{totalprice} </param>
                /// <returns>void</returns>
				this._expressions[name] = expr;
			};

			this.execExpression = function (name, args)
			{
				/// <summary>
				/// Executes a named expression, returning the result of the calculation
				/// </summary>
				/// <param name="name">The name of the expression</param>
				/// <param name="args">An optional named array containing mergefield results (as opposed to adding them as resolvers)</param>
				/// <returns></returns>
				var expr = this._expressions[name];
				var re = new RegExp("{.*?}", "");
				var mf = null;
				var realExpr = expr;
				mf = re.exec(expr);
				while (mf)
				{
					if (args && typeof (args[mf]) != 'undefined')
					{
						realExpr = realExpr.replace(mf, args[mf]);
					}
					else
					{
						var sMf = mf[0];
						var defValue = 0;
						if (mf[0].indexOf(',') > -1)
						{
							sMf = mf[0].split(',')[0] + '}';
							defValue = Number(this.removeFormatting(mf[0].split(',')[1].replace('}', '')));
						}
						var value = 0;
						try
						{
							var resolver = this._resolvers[sMf];
							if (resolver) { value = this.removeFormatting(resolver()); }
							else { value = this.removeFormatting(this.execExpression(sMf.replace('{', '').replace('}', ''))); }
							if (isNaN(value)) { value = defValue; }
						}
						catch (e)
						{
							value = defValue;
						}
						realExpr = realExpr.replace(mf, value);
					}
					var rc = realExpr; //RegExp.rightContext;            
					mf = re.exec(rc);
				}
				return Number(eval(realExpr));
			};

			return this;
		})(),
		urlParser: (function ()
		{
			/// <summary>
			/// Singleton object, with the function Parse, and the collection Parameters.
			/// </summary>
			this.Parameters = [];
			this.IndexedParameters = [];

			this.Parse = function (url)
			{
				/// <summary>
				/// Parses a specified url.
				/// </summary>
				/// <param name="url">The url in question.</param>
				/// <returns>An array containing the url itself, and any key/value pairs.</returns>
				var fractions = url.split('?');
				var qs = fractions.length > 1 ? fractions[1] : "";
				var params = qs.split('&');
				var pset = [];
				for (var i = 0; i < params.length; i++)
				{
					var parts = params[i].split('=');
					var name = parts[0];
					var value = parts[1];
					pset[name] = value;
					pset.length++;
				}
				return pset;
			};

			/// <summary>
			/// Parses the current url.
			/// </summary>
			/// <returns>An array containing the url itself, and any key/value pairs.</returns>
			this.Parameters = this.Parse(location.href);

			for (var i in this.Parameters)
			{
				if (this.Parameters.hasOwnProperty(i))
				{
					var a = { q: i, v: this.Parameters[i] };
					this.IndexedParameters.push(a);
				}
			}

			return this;
		})(),
        Promote2Top:function(node)
		{
            // Where are you placed in a global world
            var pos = $ts.getPosition(node, true, window.top);

           // Wrap click handler
           // $chain('a', node).each(function(elm) {var old = elm.onclick; elm.onclick = function() {old.call(window);}});            

		   // Register the node as an orphan, on the element it is snatched from.
		   if ($ts.exists(node.parentNode))
		   {
			   if (!$ts.exists(node.parentNode.orphans))
			   {
				node.parentNode.orphans = [];
			   }

			   node.parentNode.orphans.push(node);
		   }

		   node.previousParent = node.parentNode;

            // Move the node into the big world
            window.top.document.body.appendChild(node);

            // Set styles to ensure that the node is show in the original place
            node.style.position = 'absolute';
            node.style.top = pos[1] + 'px';
            node.style.left = pos[0] + 'px';
            node.style.zIndex = 5000;            
        },
        XmlDocument:(function()
        {
            /// <summary>
			/// Singleton XmlDocument factory object. Only works in Modern Browsers anno. 2011
			/// </summary>
            this.createNew = function()
            {
                /// <summary>
			    /// A representation of an Xml document.
			    /// </summary>
                var _document = null;

                this.loadXml = function(xml)
                {
                    _document = new DOMParser().parseFromString(xml, 'text/xml');
                }

                this.selectSingleNode = function(xpath)
                {
                    var result = $xpath(xpath, _document);
                    if (result.length>0) return result[0];
                }

                this.selectNodes = function(xpath)
                {                    
                    return $xpath(xpath, _document);                
                }

                this.GetXml = function()
                {
                    return new XMLSerializer().serializeToString(_document);
                }
                return this;
            };

            return this;
        })(),
		UI: (function()
		{
			this.ClearEnable = function(input)
			{
				if (typeof input === 'string')
				{
					input = $elm(input);
				}

				input.ClearButton = $ts.createElement('a', null, { "position":"absolute" });
				input.ClearButton.appendChild($ts.createElement('img', { "src":"/setup/clear.png" }, { "width":"12px", "height":"12px", "cursor":"pointer" }));

				if ($ts.getComputedStyle(input, 'float') === 'left')
				{
					// implement correction behavior
				}
				else if ($ts.getComputedStyle(input, 'float') === 'right')
				{
					// implement correction behavior
				}
				else if ($ts.getComputedStyle(input, 'position') === 'absolute')
				{
					// implement correction behavior
				}
				else
				{
					var marginTop = $ts.safeInt($ts.getComputedStyle(input, 'margin-top'), 0) + 4;
					input.ClearButton.style.marginTop = marginTop + 'px';
					input.ClearButton.style.marginLeft = '-16px';
				}

				if ($ts.exists(input.nextSibling))
				{
					input.parentNode.insertBefore(input.ClearButton, input.nextSibling);
				}
				else
				{
					input.parentNode.appendChild(input.ClearButton);
				}

				input.UpdateClearButton = function()
				{
					if (this.value === '' || ($ts.exists(this.watermarkText) && this.value === this.watermarkText))
					{
						input.ClearButton.style.display = 'none';
					}
					else
					{
						input.ClearButton.style.display = '';
					}
				};

				$ts.addEvent(input, 'keydown', input.UpdateClearButton);
				$ts.addEvent(input, 'mouseup', input.UpdateClearButton);

				input.UpdateClearButton();

				$ts.addEvent(input.ClearButton, 'click', function()
				{
					input.value = '';

					if ($ts.exists(input.WatermarkToggle))
					{
						input.WatermarkToggle();
					}

					input.UpdateClearButton();
				});
			};

			this.WatermarkEnable = function(input, watermarkText, watermarkClass)
			{
				if (typeof input === 'string')
				{
					input = $elm(input);
				}

				input.watermarkText = watermarkText;

				input.WatermarkToggle = function()
				{
					if (input.value === '')
					{
						input.value = input.watermarkText;
						$ts.addClass(input, watermarkClass);
					}

					if ($ts.exists(input.UpdateClearButton))
					{
						input.UpdateClearButton();
					}
				};

				if (!$ts.exists(watermarkClass))
				{
					watermarkClass = 'watermarked';
				}

				$ts.addEvent(input, 'blur', input.WatermarkToggle);

				$ts.addEvent(input, 'focus', function ()
				{
					if (input.value === input.watermarkText)
					{
						input.value = '';
						$ts.removeClass(input, watermarkClass);
					}
				});

				input.blur();
			};

			return this;
		})()
	};

	window.$ts = new _$ts();
	window.loaded = false;
	$ts.addEvent(window, 'load', function () { window.loaded = true; });
})();

           
if ($ts.debug.observer.broadcast) this.Register('*','*', $ts.Observer.BroadcastDebug);

if (typeof (Tangora) == 'undefined') { var Tangora = {}; }
Tangora.BroadcastController = $ts.Observer;
Tangora.CalculationLibrary = $ts.mathExpressions;
Tangora.Globals = {};

var LazyLoad = new function ()
{
	/// LazyLoad makes it possible to call functions in external scriptfiles that has not been loaded in DOM, hence allowing load on demand.

	/// Important: Remember to keep the wireups up to date, with regards to which functions exists in the scripts.

	this.WireUp = function (ns, fns, srcs)
	{
		var nsI = GetNS(ns);
		fns = fns.split(',');
		for (var i = 0; i < fns.length; i++)
		{
			var fn = fns[i];

			/// JWI 2011-08-22: Implemented check, to prevent overwriting already wired functions.
			if (typeof nsI[fn] === 'undefined')
			{
				nsI[fn] = (function (val) { return function () { return Load(srcs, ns, fns[val], arguments); }; })(i);
			}
			else if ($ts.debug.lazyload === true)
			{
				$ts.consoleWrite('LazyLoad prevented wiring up function that was allready loaded. Namespace: ' + ns + '. FunctionName: ' + fn + '. Source: ' + srcs);
			}
		}
	};

	function GetNS(ns)
	{
		if ($ts.exists(ns) && ns !== '')
		{
			var i, nsA = ns.split('.');
			ns = window;
			for (i = 0; i < nsA.length; i++)
			{
				var curNS = nsA[i] + '';
				if (!$ts.exists(ns[curNS])) { ns[curNS] = {}; }
				ns = ns[curNS];
			}
			return ns;
		}
		else { return window; }
	}

	function Load(srcs, ns, fn, args)
	{
		srcs = srcs.split(',');
		for (var i = 0; i < srcs.length; i++)
		{
			eval($ts.ajax({ "url": srcs[i] + buildCode, "async": false, "type": "get" }));
		}

		var nsI = GetNS(ns);
		if (nsI === window) { return eval(fn).apply(nsI, args); }
		else { return nsI[fn].apply(nsI, args); }
	}
};

LazyLoad.WireUp('Tangora.PublicAnimation', 'AnimationEnded,CurrentView,WireupElements,AddAnimationCapabilities,OpacityFade,Resize,ChangeColor,ChangeMargin,MoveToCenter,Move,StartTimer,Ease,SetOpacity', '/lib/tangora.public.animation.js');
LazyLoad.WireUp('LightBox', 'Dispose,SetOverlay,SetContents,Show,SetCloseEvents,Close,Init,Open,IsOpen,SetPreview,OpenPageContent', '/lib/lightbox.js');
LazyLoad.WireUp('Tangora.DOM', 'GetType,EnsureElement,CheckElements,MoveElement,CloneElement,GetCollection,GetCollectionByClassName,GetFirstCollectionMember,GetFirstCollectionMemberByClassName,MoveFirstCollectionMember,MoveFirstCollectionMemberByClassName,CloneFirstCollectionMember,CloneFirstCollectionMemberByClassName', '/lib/tslib/tdom.js');
LazyLoad.WireUp('Tangora.Browser', 'GetType,Name,Version,Safari,Gecko,Firefox,Opera,IE', '/lib/tslib/tbrowser.js');
LazyLoad.WireUp('Tangora.Common', 'GetType,DoUnEscape', '/lib/tslib/tcommon.js');
LazyLoad.WireUp('Tangora.ErrorHandler', 'GetType,Silent,ThrowError', '/lib/tslib/terrorhandler.js');
LazyLoad.WireUp(null, 'CancelBubble,TSSetCapture,TSReleaseCapture,TSSetCaptureOnFocus,TSCaptureOnclickHandler,TSCaptureOncontextmenuHandler,TSCaptureKeyHandler,addSaveKeyHandler,saveKeyHandler,CalendarHide,CalendarLoaded,getWindowHeight,getWindowWidth,get_url,TSCA_LoadContentArea,TSCA_Hover,ToLegalNumberString,GetIframeDocument,setCaretToStart,setCaretToEnd,evalExpr,TSGetSelectValues,setWindowStatus,PostFormUsingHTTPReq', '/lib/tslib/ILScriptLazy.js');

//Javascript Punycode converter derived from example in RFC3492.
//This implementation is created by some@domain.name and released into public domain
LazyLoad.WireUp('punycode', 'utf16,decode,encode,ToASCII,ToUnicode,ToSafeASCII', '/lib/tslib/punicode.js');
LazyLoad.WireUp('PagePreview', 'Generate', '/lib/PagePreview.js');

// Dummy scripts that prevents harmless errors, when script files hasn't been
window.showLoginStatus = function () { };

