﻿if (!window._) {
	window._ = {};
	_.iexplorerVersion = -1;
}

var Auto = 'auto';
var Center = Centre = Middle = 'center';
var TopLeft = LeftTop = 'topleft';
var Top = 'top';
var TopRight = RightTop = 'topright';
var Left = 'left';
var Right  = 'right';
var BottomLeft = LeftBottom = 'bottomleft';
var Bottom = 'bottom';
var BottomRight = RightBottom = 'bottomright';

window.$log = function() {
	if (window._vslog && _vslog.log) _vslog.log.apply(_vslog, arguments);
	else if (window.parent && window.parent._vslog && window.parent._vslog.log) window.parent._vslog.log.apply(window.parent._vslog, arguments);
}


//window.bDev = true;

//////	
////	Error catcher or reporter
//		
var Errors = [];
function stopError() {
	var Event = window.Event;
	if (Event) {
		Event.cancelBubble = true;
		Event.returnValue = true;
		if (Event.preventDefault) Event.preventDefault();
		if (Event.stopPropagation) Event.stopPropagation();
	}
	return true;
}
function flushErrors() {
	if (!window.bDev && window.YAHOO && YAHOO.util && YAHOO.util.Connect && YAHOO.util.Connect.asyncRequest) {
		var item;
		while (Errors.length) {
			item = Errors.shift();
			YAHOO.util.Connect.asyncRequest('GET', '/jsError_receiver.aspx?' + item, {
				'success': function(o) {
					if (o.responseText.indexOf('TRUE') == 0) console.warn('Error Sent: \n' + item.split('&').join('\n'));
					else console.warn('Error NOT Sent: \n' + item.split('&').join('\n'));
				},
				'failure': function() {
					console.warn('Error NOT Sent: \n' + item.split('&').join('\n'));
				}
			});
		}
	}
	// else if document is all loaded reditrect to error page that sends an email about there being no YAHOO
}
function logError(a, b, c, d) {
	if (window.PL) PL.log(1, 'error', a, { 'b': b, 'c': c, 'd': d });
	console.warn(a + ', ' + b + ', ' + c);
	var str = 'message=' + window.escape(a) + '&file=' + window.escape(b) + '&line=' + window.escape(c);
	var winSize = $getViewSize();
	if (d) str = str + '&bug=bug';
	str = str + '&info='
	try { str = str + escape('<p>' + window.location + '</p>') } catch (e) { }
	try { str = str + escape('<p>' + _config.siteName + ' ' + _config.vehicleType + '</p>') } catch (e) { }
	try { str = str + escape('<p>LoggedIn? ' + _config.LoggedIn + '</p>') } catch (e) { }
	try { str = str + escape('<p>' + ServerTime.Now + '</p>') } catch (e) { }
	try { str = str + escape('<p>' + _config.PersonSummary.FirstName + ' ' + _config.PersonSummary.LastName + '</p>') } catch (e) { }
	try { str = str + escape('<p>' + _config.PersonSummary.Address.Postcode + '</p>') } catch (e) { }
	try { str = str + escape('<p>' + _config.VehicleSummary.Manufacturer + ' ' + _config.VehicleSummary.Model + ' ' + _config.VehicleSummary.RegistrationNumber + '</p>') } catch (e) { }
	try { str = str + escape('<p>(' + window.screen.width + ', ' + window.screen.height + ') (' + winSize[0] + ', ' + winSize[1] + ')</p>') } catch (e) { }
	try { str = str + escape('<p>' + navigator.userAgent + '</p>') } catch (e) { }
	try { str = str + escape('<p>FriendlyRef: ' + $get('personinternalGuid').nextSibling.nextSibling.value + '</p>') } catch (e) { }
	try { str = str + escape('<p>personinternalGuid: ' + $get('personinternalGuid').value + '</p>') } catch (e) { }
	Errors.push(str);
	flushErrors();
	//return stopError(Event); // trouble getting both the event object and error object
	return true;
}

/**/

if (!window.bDev) window.onerror = logError;
else window.onerror = function() { if (window.console && console.debug) console.debug(arguments); else { window.status = 'ERROR: ' + arguments.join(', '); } return false};

// Add VSLog Data Upload on page and and certain points
// when vslog.js used
	
if (window.addEventListener) {
	//window.addEventListener('load', flushErrors, false);
	window.addEventListener('unload', flushErrors, false);
}
else if (window.attachEvent) {
	//window.attachEvent('onload', flushErrors);
	window.attachEvent('onunload', flushErrors);
}



// firebug
/*
window.fb = { 'group': function() { }, 'debug': function() { }, 'log': function() { }, 'assert': function() { }, 'info': function() { }, 'warn': function() { }, 'error': function() { }, 'dir': function() { } };
if (window.console && console.group && typeof console.group == 'function' && !console.memory) {
	fb.group = function(name) { if (name) console.group(name); else console.groupEnd(); };
	fb.log = console.log || function() { };
	fb.debug = console.debug || console.log || function() { };
	fb.info = console.info || function() { };
	fb.warn = console.warn || function() { };
	fb.error = console.error || function() { };
	fb.dir = console.dir || function() { };
}
else if (window.console && window.console.log && typeof console.log == 'function' && !console.memory) {
	fb.log = function(a) { return console.log(a); }
	fb.info = function(a) { return console.info(a); }
	fb.warn = function(a) { return console.warn(a); }
	fb.error = function(a) { return console.error(a); }
	fb.debug = function() { };
	fb.group = function() { };
	fb.dir = function() { };
}
*/

// ensure use of console does not cause errors with backup dummy functions
if (!window.console) console = {};
if (!console.log) console.log = function() { };
if (!console.info) console.info = function() { };
if (!console.warn) console.warn = function() { };
if (!console.error) console.error = function() { };
if (!console.debug) console.debug = function() { };
if (!console.group) console.group = function() { };
if (!console.groupEnd) console.groupEnd = function() { };


if (window.console && !console.group && !console.memory) { // above superseeds this??
	console.group = function() { };
	console.groupEnd = function() { };
	console.log = console.log || function() { };
	console.debug = console.debug || console.log || function() { };
	console.info = console.info || function() { };
	console.warn = console.warn || function() { };
	console.error = console.error || function() { };
	console.dir = console.dir || function() { };
}
else if (!window.console) {
	window.console = { 'group': function() { }, 'debug': function() { }, 'log': function() { }, 'assert': function() { }, 'info': function() { }, 'warn': function() { }, 'error': function() { }, 'dir': function() { } };
}

window.fb = window.console;

try {
	console.log('Created console logging aliases');
}
catch (e) {
	window.fb = { 'group': function() { }, 'debug': function() { }, 'log': function() { }, 'assert': function() { }, 'info': function() { }, 'warn': function() { }, 'error': function() { }, 'dir': function() { } };
	if (window.console && console.warn) console.warn('Failed to create console logging aliases.\n\n(Error: ' + e + ')');
}

var $debug = function() { };
var $error = function() { };
if (window.bDev) {
	if (window.console && console.debug) {
		$debug = console.debug;  //function(input) { console.debug(input); };
	}
	else if (window.console && console.log) { // safari
		$debug = function(a) { console.log(a); };  // function(input) { console.log(input); };
	}
}

////////	
//////		Page Log Reporter
////			var log = new PageLog(verbosity integer);
//				log.log(level integer, type string, name string, data object)

function PageLog(verbosity, types, exclude) { // Set verbosity for RECORDING logs.
	var self = this;
	var dateCreated = new Date();
	var _instanceID = Math.random().toString().replace('.', '');
	var _instance = 'PageLog\t{' + _instanceID + '}\t\t' + dateCreated.toString();
	var _serverURL = '/PageLogReceiver.aspx?plrID=' + _instanceID;
	var _types = types || false;
	if (types) for (var t in types) types[t] = types[t].toLowerCase();
	var _exclude = exclude || false;
	if (exclude) for (var t in exclude) exclude[t] = exclude[t].toLowerCase();
	if (typeof verbosity == 'string') var _verbosity = -1;
	else var _verbosity = verbosity || 1;
	var _posted = false;
	var _clearOnPost = false;
	var _reportHeader = '\nPage Log Report' + ' (verbosity=' + _verbosity + ') on ' + dateCreated.toDateString() + '\ntime\t\t\ttype\t\t\tname\n\t\t\t\t\t\tmessage/data\n---------------------------------------------------------------------------------\n';
	var _log = [];
	var _continuation = false;
	this.toArray = function() { return _log; };
	this.log = addLogEntry;
	this.getReport = this.toString = toReport; // View Report
	this.getJson = function(v) { return toJson(v); };
	if (_ && _.EnablePLR) {
		this.send = this.Send = this.post = postReport; // Send Report to server
		this.sendJson = this.send;  // till VSLog ready //= postReportJson; // Send Report to server JSON encoded
	}
	else {
		this.send = function() { console.warn('EnablePLR is false'); console.debug(self); }
		this.sendJson = function() { console.warn('EnablePLR is false'); console.debug(self.getJson); }
	}
	
	function postReportJson(verbosity) {
		var report = toJson(verbosity) 
		var req = false;
		if (window.XMLHttpRequest) { // Mozilla/Safari
			req = new XMLHttpRequest();
		}
		else if (window.ActiveXObject) { // IE
			req = new ActiveXObject("Microsoft.XMLHTTP");
		}
		if (req && req.open) {
			req.open('POST', _serverURL, true);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			req.onreadystatechange = function() {
				if (req.readyState == 4) {
					_posted = req.responseText;
					if (_posted && _clearOnPost) {
						self._log = [];
						_count = 0;
						_continuation = true;
					}
				}
			}
			req.send($trim(report));
		}
	}

	function toJson(verbosity) { // Specify verbosity for REPORTED log entries. From 1 upwards.
		if (!verbosity) verbosity = _verbosity;
		var arr = [];
		for (var i = 0, len = _log.length; i < len; i++) {
			if (verbosity == -1 || _log[i].level <= verbosity) { // filter when report as well as when log
				//if (!_types || $inArray(_types, _log[i].type)) {
				arr.push(_log[i]);
				//}
			}
		}
		return $toJSON({ 'InstanceId': _instanceID, 'log': arr, 'ServerTime': window.ServerTime['Now'], 'JavaScript': window.ServerTime['jsStart'], 'verbosity': (verbosity || _verbosity) });
	}

	function postReport(verbosity) {
		var report = toReport(verbosity) 
		var req = false;
		if (window.XMLHttpRequest) { // Mozilla/Safari
			req = new XMLHttpRequest();
		}
		else if (window.ActiveXObject) { // IE
			req = new ActiveXObject("Microsoft.XMLHTTP");
		}
		if (req && req.open) {
			req.open('POST', _serverURL, true);
			req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			req.onreadystatechange = function() {
				if (req.readyState == 4) {
					_posted = req.responseText;
					if (_posted && _clearOnPost) {
						self._log = [];
						_count = 0;
						_continuation = true;
					}
				}
			}
			if (_continuation) req.send(' (continuation of ' + _instanceID + ')\n' + self.getReport(verbosity || _verbosity));
			else req.send('(start of ' + _instanceID + ')\nServerTime: ' + window.ServerTime['Now'] + '\nJavaScript: ' + window.ServerTime['jsStart'] + '\n' + self.getReport(verbosity || _verbosity));
		}
	}

	function toReport(verbosity) { // Specify verbosity for REPORTED log entries. From 1 upwards.
		if (!verbosity) verbosity = _verbosity;
		var report = _reportHeader;
		for (var i = 0, len = _log.length; i < len;  i++) {
			if (verbosity == -1 || _log[i].level <= verbosity) { // filter when report as well as when log
				//if (!_types || $inArray(_types.toLowerCase(), _log[i].type)) {
					report += _log[i].time + '\t' + _log[i].type + '\t\t' + ((_log[i].type.length <= 5) ? '\t' : '') + _log[i].name + '\n';
					if (_log[i].message) report += '\t\t\t\t\t\t' + _log[i].message + '\n';
				//}
			}
		}
		return report;
	}
	
	
	function addLogEntry(level, type, name, data) { // levels 0 to _verbosity will be logged; type egs: event, error, javascript, ajax, user
		if (_verbosity == -1 || level <= _verbosity) { // can check verbosity when generating as well?
			if ((!_types || $inArray(_types, type)) && (!exclude || !$inArray(_exclude, type))) {
				_log[_log.length] = createLogEntry(level, type, name, data);
			}
		}
	}
	function timestampReadable() {
		var d = new Date();
		return padLeft(d.getHours(), 2, '0') + ':' + padLeft(d.getMinutes(), 2, '0') + ':' + padLeft(d.getSeconds(), 2, '0') + ':' + padLeft(d.getMilliseconds(), 3, '0');
	}
	function createLogEntry(level, type, name, data) {
		var le = {};
		le.time = timestampReadable();
		le.level = level || 1;
		le.type = type || '-';
		le.type = le.type.toLowerCase();
		le.name = name || '-';
		le.message = '';
		for (var i in data) {
			le.message += i + ': ' + data[i] + ';\n';
		}
		return le;
	}
	addLogEntry(0, 'log', 'created');
	addLogEntry(1, 'firebug', (window.console) ? window.console.firebug : false);
}

window.PL = new PageLog(4, false, ['ajax']);
window.PL.send = window.PL.sendJson;

//\
////\
//////\
////////\

function parseTemplate(template, data) {
	function translate(match, p1) {
		if (data.hasOwnProperty(p1)) {
			return data[p1];
		}
		else return p1;
	}
	return template.replace(/\[=(.*?)=\]/igm, translate);
}


/*
function reloadStyles(n) { // optionaly choose index to switch only one stylesheet (starts with 0)
	var links = document.getElementsByTagName('LINK'), href = "";
	if (n) {
		if (links[n] && links[n].type == "text/css") {
			href = links[n].href;
			if (href.indexOf("?") >= 0) href = href.substr(0, href.indexOf("?")) + "?" + Math.random();
			else href = href + "?" + Math.random();
			links[n].href = href;
		}
	}
	else {
		for (var n in links) {
			if (links[n] && links[n].type == "text/css") {
				href = links[n].href;
				if (href.indexOf("?") >= 0) href = href.substr(0, href.indexOf("?")) + "?" + Math.random();
				else href = href + "?" + Math.random();
				links[n].href = href;
			}
		}
	}
	return links;
}
*/
/* re-style css ignition
javascript: var links = document.getElementsByTagName('LINK'); var href = ""; for (var n in links) { if (links[n].type == "text/css") { void(href = links[n].href); if (href.indexOf("?") >= 0) void(href = href.substr(0, href.indexOf("?")) + "?" + Math.random()); else void(href = href + "?" + Math.random()); void(links[n].href = href); } };
*/

//////////////////////////////////////////////////////////////////////////////////
// disable & re-enable onclick for object with given id
function disableButton(id) {
	var obj = $get(id);
	if (obj) {
		if (!obj.getAttribute('allow_click')) {
			//innit
			var oldclick = obj.onclick;
			var newclick = (function() {
				return function(Event) {
					var old = oldclick;
					var targ = $getEventTarget(Event);
					if ((targ.getAttribute('allow_click').toString().toLowerCase() == 'yes')) {
						old(Event);
					}
					else { return $stopEvent(Event); }
				}
			} ());
			obj.onclick = null;
			$addEvent(obj, 'click', newclick);
			if (!obj.getAttribute('normal_opacity')) obj.setAttribute('normal_opacity', obj.style.opacity);
		}
		// disable
		obj.style.opacity = '0.4';
		obj.setAttribute('allow_click', 'no');
	}
}

function enableButton(id) {
	var obj = $get(id);
	if (obj) {
		// enable
		obj.style.opacity = obj.getAttribute('normal_opacity');
		obj.setAttribute('allow_click', 'yes');
	}
}
//////////////////////////////////////////////////////////////////////////////////

//////// $horthand functions ////////
function $get(sId, context, tryChar) {
	if (!context || !context.getElementById) context = document;
	if (tryChar != undefined) return $getTryChar(sId, context, tryChar);
	return context.getElementById(sId);
}
var $ = $get;

function $getTryChar(sId, context, tryChar) { // get by ID and try prepending the character if not found (up to 5 times)
	var result = context.getElementById(sId);
	if (!result) result = context.getElementById(tryChar + sId);
	if (!result) result = context.getElementById(tryChar + tryChar + sId);
	if (!result) result = context.getElementById(tryChar + tryChar + tryChar + sId);
	if (!result) result = context.getElementById(tryChar + tryChar + tryChar + tryChar + sId);
	if (!result) result = context.getElementById(tryChar + tryChar + tryChar + tryChar + tryChar + sId);
	return result;
}

function $F(sId, context) {
	var obj = $get(sId, context);
	if (obj) return obj.value; //? Value ???
	else {
		if (forms[context]) {
			return forms[context][sID]; //? Not Value ???
		}
	}
}

// find closest parent element with given tagName (optional), don't try any higher than oTopNode (default documentElement)
function $getParent(oChild, sNodeName, oTopNode, bCaseSensative) {
	if (oChild) {
		if (!oTopNode || !oTopNode.getElementById) oTopNode = document.documentElement;
		if (sNodeName == undefined || sNodeName == null || sNodeName == '') sNodeName = '*';
		else if (!bCaseSensative) sNodeName = ('' + sNodeName).toUpperCase();
		var oP = oChild.parentNode;
		if (sNodeName == '*') {
			if (oP) return oP;
			else return null;
		}
		else {
			if (!bCaseSensative) if (oP && oP.nodeName.toUpperCase() == sNodeName) return oP;
			else if (oP && oP.nodeName == sNodeName) return oP;
			if (oP && oP.parentNode && oP != oTopNode) return $getParent(oP, sNodeName, oTopNode);
			else return null;
		}
	}
	else return null;
}

/*
function $getPreviousSibling(oContext, sNodeName, bCaseSensative) {
}
*/

// cut a node (returns node rather than saving to clipboard)
function $cut(sId, context) {
	if (!context || !context.getElementById) context = document;
	var copy = null, found = context.getElementById(sId);
	//console.log(found)
	if (found) {
		//console.log(found.parentNode)
		found.parentNode.removeChild(found);
		copy = found.cloneNode(true);
	}
	return copy;
}

function $getTags(name, context) {
	if (!context || !context.getElementsByTagName) context = document;
	return context.getElementsByTagName(name);
}

function areYouMyOffspring(me, you, depth) { // similar to $contains (below) but searches UP fromthe child, and if false, will search all the way to 'context' 
	if (!depth) depth = 0;
	if (you && me && you.parentNode == me) return true; //depth; // 1 = child, 2+ = grandchildren
	else return (you.parentNode == document.documentElement) ? false : areYouMyOffspring(me, you.parentNode, depth);
}


function $contains(obj, child, maxLevels) { // returns true/false if the child is a childNode of obj, searches sub-children down to level on maxLevels
	var result = false;
	if (obj && obj.childNodes && child && child.nodeType) {
		/*if (maxLevels <= 0 && obj == child) {
			result = true;
		}
		else if (child.id.length > 0) {
			result = ($get(child.id, obj) != null);
		}*/
		//else {
			//console.debug(maxLevels, '\n', obj.id, '\n', child.id, '\n');
			if (!maxLevels) maxLevels = 1;
			for (var i = 0, len = $count(obj.childNodes); i < len; i++) {
				if (obj.childNodes[i] == child) {
					result = true;
					break;
				}
				if (maxLevels != 0) $contains(obj.childNodes[i], child, maxLevels - 1);
			}
		//}
	}
	return result;
}

function $hasClass(oElement, sClass, bCaseSensitive) { // returns true / false. (bCaseSensitive optional)
	if (!oElement || !sClass || oElement.nodeType != 1) return undefined;
	var flags = (bCaseSensitive) ? 'i' : ''; // (bCaseSensitive !== false) if (x)html classNames are case-sensitive?
	var rex = new RegExp("\\b" + sClass + "\\b", flags);
	return rex.test(" " + oElement.className + " ");
}
function $addClass(oElement, sClass) { //returns  new className, or oElement if oElement.className is not a string
	sClass = $trim(sClass);
	if (typeof oElement == 'string') oElement = $get(oElement);
	if (oElement && oElement.tagName) {
		if (!$hasClass(oElement, sClass)) {
			if (oElement && typeof oElement.className == 'string') {
				return oElement.className += ' ' + sClass;
			}
			else {
				console.warn('typeof ' + oElement + '.className is ' + typeof oElement.className);
				return oElement;
			}
		}
		return oElement.className;
	}
	else {
		console.warn('Element not found in $addClass');
		return false;
	}
}
function $removeClass(oElement, sClass, bCaseSensitive) { // removes all exact occurances of sClass and returns updated className
	if (!oElement || oElement.className == undefined) {
		console.warn('className not found for ' + oElement);
		return;
	}
	var changed = false, arr = oElement.className.split(/\s+/);
	var fCompare = (bCaseSensitive) ? function(a, b) { if (a && b) return (a == b); else return; } : function(a, b) { if (a && a.toLowerCase && b && b.toLowerCase) return (a.toLowerCase() == b.toLowerCase()); else return; };
	sClass = $trim(sClass);
	for (var i in arr) {
		if (fCompare(arr[i], sClass)) {
			arr[i] = undefined;
			changed = true;
		}
	}
	if (changed) return oElement.className = arr.join(' ');
	else return oElement.className;
}
function $swapClass(oElement, sClassOld, sClassNew, bCaseSensitive) { // removes all instances of sClassOld, finds/adds sClassNew, returns new className
	sClassOld = $trim(sClassOld);
	sClassNew = $trim(sClassNew);
	if (oElement) {
		if (sClassOld == sClassNew) return oElement.className; // no swapping involved here!
		if (!$hasClass(oElement, sClassOld)) return $addClass(oElement, sClassNew); // add sClassNew if sClassOld not found.
		var old = oElement.className, changed = false, arr = oElement.className.split(/\s+/);
		for (var i = 0, len = arr.length; i < len;  i++) {
			if (arr[i] == sClassOld) {
				arr[i] = (!changed) ? sClassNew : undefined; // swap or remove
				changed = true;
			}
		}
		if (changed) return oElement.className = $trim(arr.join(' '));
	}
	else {
		console.warn('className not found for ' + oElement);
		return oElement;
	}

}

function $att(oElement, sName, Value) {
	if (Value != undefined) {
		if (oElement && oElement.setAttribute) {
			oElement.setAttribute(sName, Value);
			return oElement.getAttribute(sName);
		}
		else {
			console.warn('$att - could not find element (' + oElement + ') or ' + oElement + '.setAttribute');
			return null;
		}
	}
	else {
		if (oElement && oElement.getAttribute) {
			return oElement.getAttribute(sName);
		}
		else {
			console.warn('$att - could not find element (' + oElement + ') or ' + oElement + '.getAttribute');
			return null;
		}
	}
}

/*f/
	$make -> DOM Element node
	required string tagName
	optional object attributes e.g. ({'style': 'font-size: 30px;' , 'id':'added'})
	optional string/object/array content (e.g. "<h1>test</h1>" e.g. Dom Node (element or text node), [Dom Node, Dom Node, ...])
/f*/
function $make(tagName, attributes, content) {
	var element = null;
	try {
		element = document.createElement(tagName.toUpperCase());
	}
	catch (e) {
		console.warn('error in $make, could not create element (' + tagName.toUpperCase() + '): ' + e);
		return false;
	}
	if (element) {
		for (var a in attributes) {
			if (a && attributes[a] != undefined) {
				if (a == 'id' && !!$get(attributes[a])) console.warn('elemtent with id "' + attributes[a] + '" already exists');
				try {
					if (_.iexplorerVersion == 6) {
						if (a == 'style') element.style.cssText = attributes[a];
						else if (a == 'class') element.className = attributes[a];
						else element.setAttribute(a, attributes[a]);
					}
					else {
						if (a == 'style') element.style.cssText = attributes[a];
						else if (a == 'className') element.setAttribute('class', attributes[a]);
						else element.setAttribute(a, attributes[a]);
					}
				}
				catch (e) {
					console.warn('$make: problem with attribute: ' + a);
				}
			}
		}
		/*try {*/
			if (content) {
				if (typeof content == 'string') {
					element.innerHTML = content;
				}
				else if (content.nodeType && (content.nodeType == 1 || content.nodeType == 3)) {
					element.appendChild(content);
				}
				else if ($isArray(content)) {
					//console.dir(content);
					for (var i = 0, len = content.length; i < len; i++) {
						element.appendChild(content[i]);
					}
				} 
			}
		/*}
		catch (e) {
			console.warn('error in $make, could not add content (' + content + '): ' + e);
			console.debug(e);
		}*/
	}
	return element;
}

function appendOuterHtml(parent, child) { // uses outerHTML concat to innerHTML else appendChild
	if (parent && parent.nodeType && parent.nodeType == 1) {
		if (child && child.nodeType && child.nodeType == 1) {
			if (child.outerHTML) {
				parent.innerHTML += child.outerHTML;
			}
			else {
				parent.appendChild(child);
			}
		}
	}
}

function $addScript(src) {
	console.info('Adding script: ' + src);
	try {
		$add($make('script', { 'src': src }), document.documentElement.firstChild);
	}
	catch (e) {
		console.warn('error while adding script ' + src + '\n' + e);
	}
}

function $add(content, id, context) {
	return $put(content, id, context, true);
}

function $put(content, id, context, add) { // replaces content (children or value or text) of id (id is string or element) and returns id (object)
	//console.dir([content, target, context]);
	if (typeof id == 'string') {
		var target = $get(id, context);
	}
	else if (id && id.innerHTML) {
		var target = id;
	}
	if (target && content != undefined) {
		if (target.nodeType == 1) {
			if (target.value != undefined) {
				// value
				try {
					if (add) target.value += content
					else target.value = content;
				}
				catch (e) {
					console.warn('Could not $put ' + conent + ' in #' + id + '.value');
					console.warn(e);
					return false;
				}
			}
			else {
				// innerHTML
				if (typeof content == 'string') {
					// string
					try {
						if (add) target.innerHTML += content; // !re-renders entire element HTML??
						else target.innerHTML = content;
					}
					catch (e) {
						console.warn('Could not $put ' + conent + ' (as String) in #' + id);
						console.warn(e);
						return false;
					}
				}
				else if (content.appendChild) {
					// child
					try {
						if (!add) target.innerHTML = '';
						target.appendChild(content);
					}
					catch (e) {
						console.warn('Could not $put ' + content + ' (as Node) in #' + id);
						console.warn(e);
						return false;
					}
				}
				else if ($isArray(content)) {
					// children
					try {
						if (!add) target.innerHTML = '';
						for (var x = 0; x < content.length; x++) {
							if (content[x].appendChild) {
								target.appendChild(content[x]);
							}
							else if (typeof content[x] == 'string') {
								target.innerHTML += content[x];
							}
							else console.warn('$put content[' + x + '] not added (' + content[x] + ' should be a DOM Node or String)');
						}
					}
					catch (e) {
						console.warn('Could not $put ' + content + ' (as Array) in #' + id);
						console.warn(e);
						return false;
					}
				} 
			}
		}
		if (target.nodeType == 3) {
			// data
			if (typeof content == 'string') {
				try {
					if (add) target.data += content;
					else target.data = content;
				}
				catch (e) {
					console.warn('Could not $put ' + content + ' (as String) in #' + id + '.data');
					return false;
				}
			}
		}
	}
	else {
		console.warn('Could not $put ' + content + ' in ' + id);
		return false;
	}
	return target;
}


// Add Script
function $addScript(url, id) {
	var obj = document.createElement('SCRIPT');
	if (id) obj.id = id;
	obj.type = 'text/javascript';
	obj.src = url;
	document.body.appendChild(obj);
}

// Reload Script
function $reloadScript(id) {
	var obj = document.createElement('SCRIPT');
	obj.type = 'text/javascript';
	//obj.src = $get(id).src;
	document.body.appendChild(obj);
}


// Event listner functions
var $addEvent, $removeEvent;
	// based on http://dean.edwards.name/weblog/2005/12/js-tip1/
if (document.addEventListener) {
  $addEvent = function(element, type, handler) {
	if (!element) return false;
	return element.addEventListener(type, handler, false);
  };
  $removeEvent = function(element, type, handler) {
	return element.removeEventListener(type, handler, false);
  };
}
else if (document.attachEvent) {
  $addEvent = function(element, type, handler) {
	if (!element) return false;
	return element.attachEvent("on" + type, handler);
  };
  $removeEvent = function(element, type, handler) {
	return element.detachEvent("on" + type, handler);
  };
}
else {
  $addEvent = function(element, type, handler) {
	return 'error';
  };
  $removeEvent = $addEvent;
}
 
function $stopEvent(e) {
	e.cancelBubble = true;
	e.returnValue = false;
	if (e.preventDefault) e.preventDefault();
	if (e.stopPropagation) e.stopPropagation();
	return false;
}

function $getEventTarget(e) {
	// http://www.quirksmode.org/js/events_properties.html
	var targ;
	if (!e) var e = window.event;
	if (e.target) targ = e.target;
	else if (e.srcElement) targ = e.srcElement;
	if (targ && targ.nodeType == 3) targ = targ.parentNode;	// defeat Safari bug
	return targ;
}


// $horthand for YAHOO JSON
if (window.YAHOO && YAHOO.lang && YAHOO.lang.JSON) {
	function $toJSON(obj) {
		var result = "";
		try {
			result = YAHOO.lang.JSON.stringify(obj);
		}
		catch (err) {
			console.warn("ERROR [$toJSON]: " + err.message);
			result = null;
		}
		return result;
	}
	function $fromJSON(str) {
		var result = {};
		try {
			result = YAHOO.lang.JSON.parse(str);
		}
		catch (err) {
			console.warn("ERROR [$fromJSON]: " + err.message);
			result = null;
		}
		return result;
	}
}
else {
	console.warn('Failed: $toJSON(obj) and $fromJSON(str)');
	window.$toJSON = function() {
		return "JSON Functions are not currently available!";
	}
	window.$fromJSON = function() {
		return "JSON Functions are not currently available!";
	}
}

function postFailed(o) {
	console.warn('AJAX HTTP Request Failed / Aborted');
	console.debug(o);
}

// $horthand for YAHOO AJAX
if (window.YAHOO && YAHOO.util && YAHOO.util.Connect) {
	window.ajaxRequestsObjects = [];
	if ($addEvent) $addEvent(window, 'unload', function() {
		// TODO: generate report with time on end page, how many tabs, how many quotes, how many ajax requests made etc.
		for (var obj in ajaxRequestsObjects) {
			$abortAJAX(ajaxRequestsObjects[obj]);
		}
	});
	function $getAJAX(url, onComplete, onFail) {
		if (!onFail) onFail = postFailed;
		return ajaxRequestsObjects.push(YAHOO.util.Connect.asyncRequest('GET', url, {success: onComplete, failure: onFail}) ); 
	}
	function $postAJAX(url, onComplete, body, onFail) {
		console.log('$postAJAX body: ' + body);
		if (!onFail) onFail = postFailed;
		return ajaxRequestsObjects.push(YAHOO.util.Connect.asyncRequest('POST', url, {success: onComplete, failure: onFail}, body));
	}
	function $abortAJAX(obj) {
		return YAHOO.util.Connect.abort(obj);
	}
}
else {
	console.warn('Failed: $addEvent, $getAJAX, $postAJAX and $abortAJAX');
	window.$getAJAX = function() {
		return "AJAX Functions are not currently available!";
	}
	window.$postAJAX = function() {
		return "AJAX Functions are not currently available!";
	}
	window.$abortAJAX = function() {
		return "AJAX Functions are not currently available!";
	}
}

function $toPOST(data, glue) {
	var post = '';
	if (!glue) glue = '\n';
	for (var x in data) post += escape(x) + '=' + escape(data[x]) + glue;
	return $trim(post, glue);
}
function $fromPOST(post, glue) {
	var data = {};
	if (!glue) glue = '\n';
	var rows = $trim(post, glue).split(glue);
	var e = 0;
	for (var r in rows) {
		if (rows[r].indexOf) {
			e = rows[r].indexOf('=');
			if (e > 0) data[unescape(rows[r].substr(0, e))] = unescape(rows[r].substr(e + 1));
		}
	}
	return data;
}

function $dataString(data, format) {
	if (data && typeof data == 'object') {
		if (!format || !format.toLowerCase) format = 'get';
		else format = format.toLowerCase()
		var post = '';
		var glue = '\n';
		switch (format) {
			case 'get':
				glue = '&';
			case 'post':
				for (var x in data) post += x + '=' + data[x] + glue;
				post = $trim(post, glue);
				break;
			case 'json':
				post = '{\n';
				for (var x in data) {
					switch (typeof data[x]) {
						case 'object':
							if (data[x].constructor.toString() == [].constructor.toString()) {
								// Array
								post += '"{Array}"';
							}
							else {
								// Object
								post += '"{Object}"';
							}
							break;
						case 'function':
							post += '"{function: ' + data[x].toString().replace('"', '\"') + '}"';
						case 'string':
							post += '"' + data[x].toString().replace('"', '\"') + '"';
					}
					post = post + ',\n';
				}
				post = post.substr(post.length - 4) + '}';
				break;
		}
		return post;
	}
}

// validations

function RegExSubmit(form, errorClass) {
	if (CheckFormRegEx(form, errorClass)) {
		form.submit();
		return true;
	}
	else {
		// make popup instead
		//console.info('RegExSubmit', $isArray(form.RegExErrors), form.RegExErrors);
		if (window.popupError) popupError($createClone(form.RegExErrors));
		else if (parent.popupError) parent.popupError($createClone(form.RegExErrors));
		return form.RegExErrors;
	}
}

function CheckFormRegEx(form, errorClass) { // the <form> to search in.
//	console.group('CheckFormRegEx');
	form.RegExStatus = true; // default successfull validation
	if (form.tagName && form.tagName == 'FORM') {
		form.RegExStatus = true;
		form.RegExErrors = [];
		var re, regex, input;
		for (var i = 0; i < form.elements.length; i++) { //
			input = form.elements[i];
			if (input.getAttribute('validate') == 'false') {
				//console.debug('(' + input.type + ')');
				continue;
			}
			//else console.debug(input.type);
			if (input.type == 'hidden') {
				yesno = $get(input.id + '_yes');
				if (yesno) {
					re = yesno.getAttribute('regex');
				}
				else {
					yesno = $get(input.id.substring(1) + '_yes');
					if (yesno) re = yesno.getAttribute('regex');
					else re = null;
				}
				//console.debug(re + ' (' + input.value + ') id=' + input.id);
			}
			else {
				re = input.getAttribute('regex');
			}
			var filter = $att(input, 'filter_char');
			if (filter) {
				var re_filter = new RegExp(filter, 'g');
				input.value = input.value.replace(re_filter, '');
			}
			var trim = $att(input, 'trim_char');
			if (trim) {
				var trimLeft = new RegExp('^' + trim, '');
				var trimRight = new RegExp(trim + '$', '');
				input.value = input.value.replace(trimLeft, '').replace(trimRight, '');
			}
			else {
				console.info(input.value);
				var trimLeft = new RegExp('^\\s+', '');
				var trimRight = new RegExp('\\s+$', '');
				input.value = input.value.replace(trimLeft, '').replace(trimRight, '');
				console.info(input.value);
			}
			
		//	console.log(re);
		//	console.log(input.value);
			if (re) {
				try {
					re = eval(re);
				}
				catch (e) {
					console.warn(re);
					continue;
				}
				for (var e = 0; e < re.length; e++) {
					regex = re[e].regex;
					input.setAttribute('errorClass', 'error');
					if (regex.test && regex.test(input.value) == false) {
			//			console.log('fail');
						form.RegExStatus = false;
						form.RegExErrors.push((re[e].emsg) ? '' + re[e].emsg : 'Error');
						if (errorClass) {
							$addClass(input, errorClass);
						}
						$addEvent(input, 'change', CheckRegexThis);
						break; // skip to next input box
					}
					else {
			//			console.log('pass');
						if (errorClass) $removeClass(input, errorClass);
						$removeEvent(input, 'change', CheckRegexThis);
					}
				}
			}
			re = null;
			regex = null;
		}
		//console.log('return value: ' + form.RegExStatus + '\n\n' + form.RegExErrors.length + ' errors:\n\n' + form.RegExErrors.join('\n'));
//	console.groupEnd();
		return form.RegExStatus;
	}
//		console.groupEnd();
		return form.RegExStatus;
	}


function CheckRegexThis() {
	if (this && this.nodeType) return CheckRegEx(this, this.getAttribute('regex'), this.getAttribute('errorClass'));
}

function CheckRegexById(id, errorClass) {
	var obj = $get(id);
	if (obj) return CheckRegEx(obj, obj.getAttribute('regex'), errorClass);
}

function CheckRegEx(obj, regEx, invalidClass, validClass) { // the <input> to check.
	//console.group('CheckRegEx');
	//console.log(['CheckRegEx', obj, regEx, invalidClass, validClass]);
	var RegExStatus = true; // default successfull validation
	if (obj.tagName) {
		obj.setAttribute('RegExError', '');
		re = (regEx) ? regEx : obj.getAttribute('regex');
		if (re) {
			try {
				re = eval(re); // EVIL!
			}
			catch (e) {
				//console.warn(re);
				re = false;
			}
		}
		//console.log(re);
		if (re) {
			//console.log(re.length);
			for (var e = 0; e < re.length; e++) {
				//console.log((!!re[e] && !!re[e].regex && !!re[e].regex.test));
				//console.log(re[e].regex + '.test(' + obj.value + ') => ' + re[e].regex.test(obj.value));
				if (re[e] && re[e].regex && re[e].regex.test && re[e].regex.test(obj.value) == false) {
					//console.log(re[e].emsg);
					RegExStatus = false; // failed validation
					//obj.RegExErrors.push ((re[e].emsg) ? '' + re[e].emsg : 'Error');
					obj.setAttribute('RegExError', obj.getAttribute('RegExError') + ((re[e].emsg) ? '' + re[e].emsg : 'Error') + '\n');
					if (validClass) { $removeClass(obj, validClass); }
					if (invalidClass) { $addClass(obj, invalidClass); }
					//console.log('fail ' + e);
					break; // failed so stop here
				}
				else {
					obj.setAttribute('RegExError', '');
					if (invalidClass) { $removeClass(obj, invalidClass); }
					if (validClass) { $addClass(obj, validClass); }
					//console.log('pass ' + e);
				}
			}
		}
	}
	//console.groupEnd();
	return RegExStatus;
}
// var example = [{ regex: /.+/, emsg: 'Telephone No cannot be empty' }, { regex: /^(01|02|03|05|07)[0-9]{7,9}$/, emsg: 'Telephone No is invalid.'}]
// ?email? /^([a-zA-Z0-9_\-\.]+)@((\[^\d{1,7}${1,3}\.^\d{1,7}${1,3}\.^\d{1,7}${1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|^\d{1,7}${1,3})(\]?)$/;

function validateAsYouType(obj) {
	
}


function isValidEmail(str) {
	//var pattern = new RegExp("^[_0-9a-zA-Z-]+([_0-9a-zA-Z+-]*|\.[_0-9a-zA-Z-]+)*@([0-9a-zA-Z][0-9a-zA-Z-]+\.)+[a-zA-Z]{2,6}$");
	//var pattern = new RegExp("^([a-zA-Z0-9_\-\.!#$'\*\^`\|~\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)");
	return /^([a-zA-Z0-9_\-\.!#$'\*\^`\|~\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)/.test(str);
}

// Do not extend Object.prototype as we need to be able to use for(var x in y) later on!!!

function $isArray(subject) {
	return (subject && subject.constructor && subject.constructor.toString() == [].constructor.toString())
}
function $isObject(subject) {
	return (subject && (subject).constructor && (subject).constructor.prototype == ({}).constructor.prototype);
}
function $isString(subject) {
	return (subject && (subject).constructor && (subject).constructor.prototype == ('').constructor.prototype);
}
function $isNumber(subject) {
	return (subject && (subject).constructor && (subject).constructor.prototype == (0).constructor.prototype);
}
function $isBool(subject) {
	return (subject && (subject).constructor && (subject).constructor.prototype == (true).constructor.prototype);
}
function $trim(str, c, where) {
	if (!c || c.length != 1) var c = " ";
	if (!str || typeof str != 'string') str = this;
	if (!str || typeof str != 'string') return '';
	if (typeof str != 'string') return arguments[0];
	var x = 0, y = str.length;
	if (!where) where = 'both';
	if (where == 'both' || where == 'left') while (str.charAt(x) == c && x <= y) x++;
	if (where == 'both' || where == 'right') while (str.charAt(y-1) == c && x <= y) y--;
	if (y < x) return '';
	return str.substring(x, y);
}

function $indexOf(arr, value) { // returns index of first occurance of value in array; or false. (index may be 0, compare result using ===)
	if (value == undefined || arr == undefined || arr.length <= 0) return false;
	for (var i = 0, len = arr.length; i < len; i++) {
		if (arr[i] == value) return i;
	}
	return false;
}

function $inArray(arr, value) { // true / false
	return ($indexOf(arr, value) !== false) ? true : false;
}

function $allIndexOf(arr, value, prop, partial) { // returns array with indexes of all matching values
	if (value == undefined || arr == undefined || arr.length <= 0) return false;
	var indexes = [];
	if (partial) {
		if (prop) {
			for (var i = 0, len = arr.length; i < len; i++) {
				if (arr[i][pop] && arr[i][prop].indexOf(value) >= 0) indexes[indexes.length] = i;
			}
		}
		else  {
			for (var i = 0, len = arr.length; i < len; i++) {
				if (arr[i].indexOf(value) >= 0) indexes[indexes.length] = i;
			}
		}
	}
	else {
		if (prop) {
			for (var i = 0, len = arr.length; i < len; i++) {
				if (arr[i][pop] && arr[i][pop] == value) indexes[indexes.length] = i;
			}
		}
		else  {
			for (var i = 0, len = arr.length; i < len; i++) {
				if (arr[i] == value) indexes[indexes.length] = i;
			}
		}
	}
	return indexes;
}

// <option>s
function removeAllOptions(obj) {
	if (obj != null && obj.options) {
		var i = obj.options.length;
		while (i >= 0) {
			obj.options[i--] = null;
		}
		obj.selectedIndex = -1;
	}
}

function addOption(obj,text,value,selected) {
	if (obj!=null && obj.options!=null) {
		obj.options[obj.options.length] = new Option(text, value, false, selected);
	}
	if (selected) obj.options[obj.options.length - 1].value = value; // because some IE don't support selecting an option at the same time as adding.
	return obj.options[obj.options.length - 1];
}

function selectByValue(obj, val) {
	var result = null;
	if (typeof obj == 'string') obj = $get(obj);
	if (obj && obj.tagName && obj.tagName.toLowerCase() == 'select') {
		result = obj.selectedIndex;
		if (obj.value == val) return result;
		for (var o = 0, option; option = obj.options[o]; o++) {
			if (option.value && option.value.toLowerCase() == val.toLowerCase()) {
				option.selected = true;
				result = obj.selectedIndex;
				break;
			}
		}
		return result;
	}
	else return result;
}

// dates //

Date.msSecond = 1000;
Date.msMinute = Date.msSecond * 60;
Date.msHour = Date.msMinute * 60;
Date.msDay = Date.msHour * 24;
Date.msWeek = Date.msDay * 7;
Date.DayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
Date.MonthName = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
Date.Leap = function(y) { //returns true if year y is a leap year else false
	if (!(y % 4 == 0)) return false;
	else if (y % 100 == 0) return (y % 400 == 0); 
	else return true;
}
Date.formatDay = function(val) {
	switch (val) {
		case 1: case 21: case 31:
			return val + "st";
			break;
		case 2: case 22:
			return val + "nd";
			break;
		case 3: case 23:
			return val + "rd";
			break;
		default:
			return val + "th";
	}
}
Date.DaysInMonth = function(m, y) { //returns number of days from 28/28/30/31 in month m for year y.
	// Returns 31 by default.
	// Thirty days has November, April, June, and September
	// All the rest have 31 except
	// February that has 28, or 29 on a leap year
	var retval = 31;
	if (typeof m == 'string') m = m.substr(0, 3).toLowerCase();
	switch (m) {
		case 'apr':
		case 4:
		case 'jun':
		case 6:
		case 'sep':
		case 9:
		case 'nov':
		case 11:
			retval = 30;
			break;
		case 'feb':
		case 2:
			// check for leapyear
			if (Date.Leap(y)) retval = 29;
			else retval = 28;
			break;
	}
	return retval;
}
Date.prototype.add = function(dAdd, field) {
	// Returns new date
	// accepts a Date object OR a number and 'y', 'm' or 'd' to signify if the number is years/months/days to be added.
	if (dAdd.getTime) {
		try {
			return new Date(this.getTime() + dAdd.getTime());
		}
		catch (e) {
			console.warn('Error in Date.prototype.add: ' + e);
			return false;
		}
	}
	else if (field) {
		dAdd = parseFloat(dAdd);
		//console.debug(field, dAdd);
		//console.log(field.toLowerCase().charAt(0));
		if (!isNaN(dAdd)) {
			switch (field.charAt(0).toLowerCase()) {
				// Note: http://www.digital-web.com/articles/javascript_date_object_with_user_methods/  
				// "JavaScript will overflow (or underflow) dates created with otherwise invalid values." 
				case 'd':
					return new Date(this.getFullYear(), this.getMonth(), this.getDate() + dAdd);
					break;
				case 'm':
					//return this.addMonths(dAdd);
					return new Date(this.getFullYear(), this.getMonth() + dAdd, this.getDate());
					break;
				case 'y':
					return new Date(this.getFullYear() + dAdd, this.getMonth(), this.getDate());
					break;
				default:
					return false;
			}
		} else return false;
	}
	else return false;
}

Date.prototype.age = function(when) { // calculate the number of years old a date is on the date passed in (default to today)
	if (!when || when.getFullYear() == NaN) when = new Date();
	var years = when.getFullYear() - this.getFullYear();
	var months = when.getMonth() - this.getMonth();
	var days = when.getDate() - this.getDate(); // doesn't allow for 30/31 days
	if (months < 0) return years - 1;
	else if (months == 0 && days < 0) return years - 1;
	else return years;
}

Date.prototype.vbStringDate = function() {
	return '' +  this.getDate() + ' ' + Date.MonthName[this.getMonth()] + ' ' +this.getFullYear() + '';
}

Date.prototype.dateString = function() {
	return '' + Date.formatDay(this.getDate()) + ' ' + Date.MonthName[this.getMonth()] + ' ' +this.getFullYear() + '';
}


/*
Date.prototype.addMonths = function(amount) { // increase date by amount months (+/- integer), returns true/false
	amount = Math.floor(parseFloat(amount));
	if (this.getDate && !isNaN(amount)) {
		try {
			var old = this.getDate();
			var newMonth = (old + amount) % 12;
			var addYears = ((old + amount) - newMonth) / 12;
			this.setDate(m);
			if (addYears) this.setYear(this.getYear() + addYears);
		}
		catch (e) {
			console.warn('Error in Date.prototype.addMonths: ' + e);
			return false;
		}
	}
	else return false;
}

Date.prototype.addDays = function(amount) { // increase date by amount days (+/- integer), returns true/false
	// TODO
	amount = Math.floor(parseFloat(amount));
	if (this.getDate && !isNaN(amount)) {
		try {
			// add milliseconds and work out difference in days months and years???
			var ms = this.getTime();
			ms += amount * Date.msDay;
			this.setDate(ms);
		}
		catch (e) {
			console.warn('Error in Date.prototype.addMonths: ' + e);
			return false;
		}
	}
	else return false;
}
*/


function $addItem(arr, value) { // push() if not already in array, returns bool.
	// Warning! will delete old item, and add the value onto the end of the array,
	// effectivly MOVES the value to the back fo the queue.
	// (This should be concidered an error)
	if (!value || !arr) return false;
	$removeItem(arr, value);
	arr.push(value);
	return true;
}
function $removeItem(arr, value) { // removes all item of array with specified value, returns false or number of items removed
	var index, num = 0;
	while (false !== (index = $indexOf(arr, value))) {
		arr.splice(index, 1);
		num++;
	}
	return (num) ? num : false;
}
function $keyOf(obj, value) { // returns key or false
	if (!value) return false;
	for (var i in obj) if (obj[i] == value) return i;
	return false;
}
function $getKeys(obj) { // returns list of keys
	var out = [];
	for (var key in obj) out.push(key);
	return out;
}
function $count(obj) { // returns number of properties
	var out = 0;
	for (var key in obj) out++;
	return out;
}
function $isEmpty(obj) { // returns true if obj has no own properties
	var out = true;
	for (var key in obj) {
		if (obj.hasOwnProperty(key)) {
			out = false;
			break;
		}
	}
	return out;
}
function $joinObject(obj, delim) { // returns list of values
	var out = [];
	if (!delim) delim = '|';
	for (var key in obj) out.push(obj[key]);
	return out.join(delim);
}
function $createClone(obj) {
	// http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
	if(obj == null || typeof(obj) != 'object') return obj;
	var temp = new obj.constructor();
	for(var key in obj) temp[key] = $createClone(obj[key]);
	return temp;
}
function $combineObjects() { // any number of object arguments, earlier objects take precendence
	var output = {};
	for (var a = 0, l = arguments.length; a < l; a++) {
		for (var x in arguments[a]) {
			if (arguments[a].hasOwnProperty(x) && output[x] === undefined) output[x] = arguments[a][x];
		}
	}
	return output;
}

function $combineArrays() { // any number of array arguments, earlier arrays take precendence
	var output = [], n = 0;
	for (var a = 0, l = arguments.length; a < l; a++) {
		for (var x in arguments[a]) {
			if (arguments[a].hasOwnProperty(x) && !$inArray(output, arguments[a][x])) output[n++] = arguments[a][x];
		}
	}
	return output;
}


// take HTMLCollection objects and convert to 1D array of node references
function $nodelistToArray() {
	var list = [];
	for (var a = 0; a < arguments.length; a++) {
		for (var n = 0; n < arguments[a].length; n++) {
			list[list.length] = arguments[a][n];
		}
	}
	return list;
}


// random / generic


function swapIDs(list) { // takes list of pairs of elements and swaps the IDs of the pairs.
	if ($isArray(list)) {
		var temp;
		for (var z in list) {
			try {
				temp = list[z][0].id;
				list[z][0].id = list[z][1].id;
				list[z][1].id = temp;
			}
			catch (e) {
				console.warn('error swapping IDs [' + z + '] ' + e);
			}
		}
	}
	else console.warn('invalid input: swapIDs accepts an  n-by-2 array where each member of a pair is a DOM element.');
}



function padLeft(str, len, c) {
	if (parseInt(len) == NaN) return str;
	if (!c) c = ' ';
	if (str == c) {
		var result = '';
		for (var i = 0; i < len; i++) {
			result += c;
		}
		return result;
	}
	else if (str == "" || str == undefined) return str;
	var temp = '' + str;
	while (temp.length < len) temp = c + temp;
	return temp;
}

function padRight(str, len, c) {
	if (parseInt(len) == NaN) return str;
	if (!c) c = ' ';
	if (str == c) {
		var result = '';
		for (var i = 0; i < len; i++) {
			result = result + c;
		}
		return result;
	}
	else if (str == "" || str == undefined) return str;
	var temp = '' + str;
	while (temp.length < len) temp = c + temp;
	return temp;
}



function $arrayWithoutDuplicates(arr) {
	var out = [];
	for (var x in arr) if ($indexOf(out, arr[x]) === false) out[x] = arr[x];
	return out;
}

function toHash(list) { // returns hash object with keys taken from input values
	var obj = new Object();
	for (var p in list) {
		obj[list[p]] = [];
	}
	return obj;
}
function joinObject(obj, prop, delim) {
	//console.log('joinObject', obj, prop, delim)
	var retVal = '';
	delim = delim || '|';
	if (prop != undefined) for (var x in obj) retVal += obj[x][prop] + delim;
	else for (var x in obj) retVal += obj[x][prop] + delim;
	return retVal.substr(0, retVal.length - 1);
}
function splitObject(str, delim) {
	var retVal = {};
	if ( ! (delim != undefined) ) delim = '|';
	var arr = str.split(delim);
	for (var x in arr) retVal[x] = arr[x];
	return retVal.substr(0, retVal.length-1);
}


function $getPosition(obj) {
	if (!obj || obj.offsetLeft == undefined) return null;
	var curleft = curtop = 0;
	do {
		curleft += obj.offsetLeft;
		curtop += obj.offsetTop;
	}
	while (obj = obj.offsetParent); // problem with position: fixed;
	return [curleft, curtop];
}

function $getPositionFixed(obj) {
	var curleft = curtop = 0, parent = null;
	do {
		curleft += obj.offsetLeft;
		curtop += obj.offsetTop;
		if (obj.offsetParent) parent = obj.offsetParent
		else {
			if (obj.parentNode && $getStyle(obj, "position") == "fixed") parent = obj.parentNode;
			else parent = null;
		}
	}
	while (obj = parent);
	return [curleft, curtop];
}

//function $getStyle(obj, styleProp) {
//  if (obj.currentStyle) {
//    var y = obj.currentStyle[styleProp];
//  } else if (window.getComputedStyle)
//    var y = window.getComputedStyle(obj, null)[styleProp];
//  return y;
//}

function $getSize(obj, newSize) {
	// get size
	var oldSize = [obj.offsetWidth, obj.offsetHeight];
	if (newSize) {
		// set size
		if (newSize[0] != null) obj.style.width = newSize[0];
		if (newSize[1] != null) obj.style.height = newSize[1];
	}
	// return original
	return oldSize;
}

function $getViewSize() {
	var out = [-1, -1];
	out[0] = document.documentElement.clientWidth;
	out[1] = document.documentElement.clientHeight;
	
	return out;
}

function $getScroll() {
	if (window._ && _.webkit) {
		// webkit
		return [document.body.scrollLeft, document.body.scrollTop];
	}
	else {
		// firefox & IE
		return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
	}
}


////// Image Pre-Loader ///////
// window.onload or after
function preloadImages(images, delay) {
	//console.group('preloadImages');
	var div;
	if ( !(div = $get('image-preload-div')) ) {
		div = document.createElement('DIV');
		div.id = 'image-preload-div';
		div.style.display = 'none';
		document.body.appendChild(div);
	}
	var img;
	div = $get('image-preload-div');
	if (delay == undefined) delay = 1;
	setTimeout(function() {
		preloadImages_process(images, div, delay);
		
	}, delay * 1000);
	//console.groupEnd();
}

function preloadImages_process(images, div, delay) {
	var src = images.pop();
	if (src && src != 'undefined' && !(src.indexOf('undefined') >= 0)) {
		//console.log('preload: ' + src);
		img = document.createElement('IMG');
		img.src = src;
		div.appendChild(img);
		div.appendChild(document.createElement('BR'));
		if (images.length > 0) setTimeout(function() {
			preloadImages_process(images, div);
		}, delay * 1000);
	}
}
///////////




if (window.YAHOO && YAHOO.util.Dom && YAHOO.util.Dom.getStyle) var $getStyle = YAHOO.util.Dom.getStyle;
else var $getStyle = function $getStyle(oElm, strCssRule) {
	// Get the rendered style of an element
	// http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
	var strValue = "";
	if(document.defaultView && document.defaultView.getComputedStyle){
		strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
	}
	else if(oElm.currentStyle) {
		strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
			return p1.toUpperCase();
		});
		strValue = oElm.currentStyle[strCssRule];
	}
	return strValue;
}


// getElementsByClassName
var $getClassNames = function (className, tag, elm){
	/*  Developed by Robert Nyman, http://www.robertnyman.com
		Code/licensing: http://code.google.com/p/getelementsbyclassname/ */	
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) { // FF3
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace) ? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};


//<!-- Cookie script - Scott Andrew -->
//<!-- For more free scripts go to http://www.sivamdesign.com/scripts/ -->

function newCookie(name, value, days) {
	if (value == undefined) value = '';
	var expires = "";
	if (days == undefined) days = 1;
	else if (days) {
		var date = new Date();
		date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
		var expires = "; expires=" + date.toGMTString();
	}
	document.cookie = name + "=" + value + expires + "; path=/";
}

function readCookie(name) {
	var nameSG = name + "=";
	var nuller = '';
	if (document.cookie.indexOf(nameSG) == -1)
		return nuller;

	var ca = document.cookie.split(';');
	for (var i = 0; i < ca.length; i++) {
		var c = ca[i];
		while (c.charAt(0) == ' ') c = c.substring(1, c.length);
		if (c.indexOf(nameSG) == 0) return c.substring(nameSG.length, c.length);
	}
	return null;
}

function eraseCookie(name) { newCookie(name, "", 1); }

///////////////////////////////////////////////

function round(x, z) {
	// Round x to the nearest multiple of z
	var y = x / z;
	y = Math.round(y);
	y = y * z;
	return y;
}

///////////////////////////////////////////////
function popUp(url, windowfeatures, under) {
	if (!windowfeatures) windowfeatures = "width=1024,height=768,scrollbars=1,resizable=1";
	var popupwin = window.open(url, "popup", windowfeatures);
	if (under) {
		popupwin.blur();
		window.focus();
	}
}



function setBgImg(obj, url, full) {
	console.log('setBgImg', obj, url, full);
	if (obj && obj.style) {
		if (url && url != 'undefined') {
			if (!full) return obj.style.backgroundImage = 'url(' + url + ')';
			else return obj.style.background = url;
		}
		else {
			if (!full) return obj.style.backgroundImage = '';
			else return obj.style.background = '';
		}
	}
	return false;
}

///////////////////////////////////////////////
// Spotlight tag parse&insert

function $spot(id, dealGuid, optID, override_values) {
	console.info('spot', id, dealGuid, optID);
	if (!window.spotvars) window.spotvars = {};
	if (!override_values) override_values = {};
	if (window.Tabs && window.Deals) { // endpage
		if (!optID) optID = _.optionID;
		spotvars.brandname = $getPartner(dealGuid, 'BrokerName') || '(error)';
		spotvars.premium = $getPrice(dealGuid, optID) || '';
		spotvars.position = $indexOf($getRankList(optID), dealGuid) || ''; //$indexOf(Tabs[optID].WeightedRanks[Tabs[optID].featuresID], dealGuid) || '(error)';
		if (spotvars.position !== false) spotvars.position += 1;
		spotlight_parse_insert(id);
	}
	else if (window._ && _.BestQuotes && _.BestQuotes[dealGuid] && _.BestQuotes[dealGuid]) { // retrieve saved quotes page
		spotvars.quoteGuid = dealGuid || '(error)';
		spotvars.brandname = (_.BestQuotes[dealGuid].Partner) ?_.BestQuotes[dealGuid].Partner.BrokerName : '(error)';
		spotvars.premium = (_.BestQuotes[dealGuid].Quote) ? _.BestQuotes[dealGuid].Quote.Premium : '(error)';
		spotvars.position = override_values['[*result position*]'];
		if (!spotvars.position) spotvars.position = $indexOf($getKeys(_.BestQuotes), dealGuid) + 1;
		if (!spotvars.position) spotvars.position = '(error)';
		spotlight_parse_insert(id);
	}
}

function spotlight_parse_insert(id) {
	console.info('spotlight_parse_insert', id);
	var spot = $get('spotlight_html_' + id);
	if (spot) var html = spot.value || spot.innerHTML;
	if (html) {
		console.log(html);
		var matches = (new RegExp("<iframe\s*.*?\s*src=\"([^\"]+)\"[^>]*>", "igm")).exec(html);
		var src = matches[1];
		console.info(src);
		var matches = src.match(/\[\*[^*]*\*\]/gm);
		for (var i = 0; i < matches.length;  i++) {
			src = src.replace(matches[i], getSpotlightCvar(matches[i]));
		}
		console.log(src);
		var spots_iframe = $get('spots_iframe');
		if (spots_iframe) {
			spots_iframe.src = src;
		}
		else {
			spots_div = $get('spots');
			if (!spots_div) {
				spots_div = $add($make('div', { 'id': 'spots', 'class': 'invis' }), document.body);
				spots_div = $get('spots');
			}
			spots_iframe = $add($make('iframe', { 'src': '' + src, 'id': 'spots_iframe', 'class': 'invis' }), spots_div);
		}
	}
}


function getSpotlightCvar(cvar) {
	if (window.spotvars && cvar.toLowerCase) {
		switch (cvar.toLowerCase()) {
			case '[*random number*]':
				return Math.random().toString().replace('.', '');
				break;
			case '[*quote ref*]':
				return spotvars.quoteGuid;
				break;
			case '[*result brand*]':
				return spotvars.brandname;
				break;
			case '[*result price*]':
				return spotvars.premium;
				break;
			case '[*result position*]':
				return spotvars.position;
				break;
			default:
				return '';
		}
	}
	else return '(error)';
}


///////////////////////////////////////////////
function AddRoll(tableid) {
	var table = $get(tableid), makeOver, makeOut;
	//console.log('AddRoll(' + tableid + ') ' + table);
	if (table) {
		var Trs = $getTags("TR", table);
		for (var x = 0; x < Trs.length; x++) {
			makeOver = function() {
				var obj = Trs[x];
				return function() { $addClass(obj, 'rowHover'); }
			};
			makeOut = function() {
				var obj = Trs[x];
				return function() { $removeClass(obj, 'rowHover'); }
			};
			$addEvent(Trs[x], 'mouseover', makeOver());
			$addEvent(Trs[x], 'mouseout', makeOut());
		}
	}
}

//
// IE 6 PNG alpha channel fix 
// for <img> using DirectX Filter
//

function pngIE6(trans_src) {
	if (!window.trans_src) window.trans_src = '/images/transparent_pixel.gif';
	var list = $getClassNames('pngIE6', 'img'); //
	var pngSrc, pngSize;
	for (var x in list) {
		pngSrc = '' + list[x].src;
		if (window.DD_belatedPNG) {
			DD_belatedPNG.fixPng(list[x]);
		}
		else {
			list[x].src = trans_src;
			pngSize = $getSize(list[x]);
			list[x].width = pngSize[0];
			list[x].height = pngSize[1];
			list[x].style.width = pngSize[0];
			list[x].style.height = pngSize[1];
			list[x].style.background = 'transparent';
			list[x].style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + pngSrc + "', sizingMethod='scale')";
			//list[x].style.border = 'dashed red 2px';
		}
	}
}


//
// Text Dropdowns
// IE 6 Replace SELECT boxes with special INPUTs
//

function createTextDropdownObject(id, options, allowCustom, saveCustom) {
	var input = $make('input', {
		'id': id,
		'type': 'text',
		'class': 'text',
		'style': '\
			padding-right: 20px;\
			background: transparent url("/images/dropdownarrow.gif") no-repeat 99% 50%;\
			border: solid blue 2px;\
		'
	});
	return input;
}

function replaceSelect(obj, extraOptions, allowCustom, saveCustom) {
	if (obj && obj.tagName && obj.tagName == 'SELECT') {
		//console.debug(obj);
		var options = obj.options; //.concat(extraOptions);
		//console.dir(options);
		var newObj = $make('input', {
			'id': obj.id.toString(),
			'name': obj.name.toString(),
			'type': 'text',
			'class': obj.className.toString(),
			'style': obj.style.cssText + ' \
				padding-right: 20px;\
				width: ' + $getSize(obj)[0] - 20 + 'px\
				background: transparent url("/images/dropdownarrow.gif") no-repeat 99% 50%;\
			',
			'readonly': 'readonly',
			'regex': obj.getAttribute('regex'),
			'action': obj.getAttribute('action'),
			'onchange': obj.getAttribute('onchange'),
			'onblur': obj.getAttribute('onblur'),
			'tabindex': obj.tabindex,
			'disabled': obj.disabled
		});
		var oldFocus = obj.onfocus;
		$addEvent(newObj, 'focus', function() {
			obj.onfocus()
		});
		//console.debug(newObj);
		obj.parentNode.replaceChild(newObj, obj);
	}
}

if (_.iexplorerVersion == 6) {
	//$addEvent(window, 'load', function() { replaceSelect($get('VanProposal.Business.TradingStatus')); });
}


function setBgImage(obj, url) {
	if (obj.style) return obj.style.backgroundImage = "url('" + url + "')";
	else return false;
}


////////////////////////////////////////////////

//Menu drop down functions
if (window.jQuery) {
	var timeout	= 500;
	var closetimer = 0;
	var ddmenuitem = 0;
		   
	function menu_open() {
		menu_canceltimer();
		menu_close();
		ddmenuitem = jQuery(this).find('ul').css('visibility', 'visible');
	}

	function menu_close() {
		if(ddmenuitem) ddmenuitem.css('visibility', 'hidden');
	}

	function menu_timer() {
		closetimer = window.setTimeout(menu_close, timeout);
	}

	function menu_canceltimer() {
		if(closetimer) {
			window.clearTimeout(closetimer);
			closetimer = null;
		}
	}
	jQuery(document).ready(function() {
		jQuery('#menu > li').bind('mouseover', menu_open)
		jQuery('#menu > li').bind('mouseout',  menu_timer)
	});

	document.onclick = menu_close;
}



