/* To-dos
	trigger alert for dom.get if dom is not ready
	dom.create accept attributes for elem
	LSD.animate.grow_x and LSD.element.width need updates from sister y axis functions
	add array iteration to LSD.events.add
	LSD.ajax.send add use 'this' if param 'elem' is not specified
*/
LSD = {
	dom : {
		ready : function(f) {
			if (LSD.dom.ready.done) return f();
			if (LSD.dom.ready.timer) {
				LSD.dom.ready.ready.push(f);
			} else {
				LSD.events.add(window, 'load', LSD.dom.is_ready);
				LSD.dom.ready.ready = [f];
				LSD.dom.ready.timer = setInterval(LSD.dom.is_ready, 13);
			}
		},
		is_ready : function() {
			if (LSD.dom.ready.done) return false;
			if (document && document.getElementsByTagName && document.getElementById && document.body) {
				clearInterval(LSD.dom.ready.timer);
				LSD.dom.ready.timer = null;
				for (var i = 0 ; i < LSD.dom.ready.ready.length ; i++) {
					LSD.dom.ready.ready[i]();
				}
				LSD.dom.ready.ready = null;
				LSD.dom.ready.done = true;
			}
		},
		previous_sibling : function(elem) {
			do {
				var elem = elem.previousSibling;
			} while (elem && elem.nodeType != 1);
			return elem;
		},
		next_sibling : function(elem) {
			do {
				var elem = elem.nextSibling;
			} while (elem && elem.nodeType != 1);
			return elem;
		},
		first_child : function(elem) {
			var elem = elem.firstChild;
			return elem && elem.nodType != 1 ? LSD.dom.next_sibling(elem) : elem;
		},
		last_child : function(elem) {
			var elem = elem.lastChild;
			return elem && elem.nodType != 1 ? LSD.dom.previous_sibling(elem) : elem;
		},
		parent : function(elem, generation) {
			generation = generation || 1;
			for (var i = 0 ; i < generation ; i++)
				if (elem != null) elem = elem.parentNode;
			return elem;
		},
		children : function(elem) {
			var currentChild = LSD.dom.first_child(elem);
			var children = new Array();
			var counter = 0;
				children[counter++] = currentChild;
			while (currentChild != LSD.dom.last_child(elem)) {
					children[counter++] = currentChild = LSD.dom.next_sibling(currentChild);
			}
			return children;
		},
		insert_before : function(insert, before, parent) {
			var parent = parent || before.parentNode;
			var insert = LSD.dom.prepare_insert(insert);
			for (var i = 0 ; i < insert.length ; i++) {
				parent.insertBefore(insert[i], before);
			}
		},
		insert_after : function(insert, after, parent) {
			var parent = parent || after.parentNode;
			var insert = LSD.dom.prepare_insert(insert);
			for (var i = insert.length-1 ; i >= 0 ; i--) {
				parent.insertBefore(insert[i], LSD.dom.next_sibling(after));
			}
		},
		insert_first : function(insert, parent) {
			var insert = LSD.dom.prepare_insert(insert);
			for (var i = insert.length-1 ; i >= 0 ; i--) {
				parent.insertBefore(insert[i], LSD.dom.first_child(parent));
			}
		},
		insert_last : function(insert, parent) {
			var insert = LSD.dom.prepare_insert(insert);
			for (var i = 0 ; i < insert.length ; i++) {
				parent.appendChild(insert[i]);
			}
		},
		prepare_insert : function(a) {
			var r = [];
			if (a.constructor != Array) a = [a];
			for (var i = 0 ; i < a.length ; i++) {
				if (a[i].constructor == String) {
					var div = document.createElement('div');
					div.innerHTML = a[i];
					for (var j = 0 ; j < div.childNodes.length ; j++) {
						r.push(div.childNodes[j])
					}
				} else if (a[i].length) {
					for (var j = 0 ; j < a[i].length ; j++) {
						r.push(a[i][j]);
					}
				} else {
					r.push(a[i]);
				}
			}
			return r;
		},
		create : function(elem) {
			return document.createElementNS ?
				document.createElementNS('http://www.w3.org/1999/xhtml', elem) :
				document.createElement(elem);
		},
		remove : function(elem) {
			if (elem) elem.parentNode.removeChild(elem);
		},
		remove_children : function(elem) {
			while (elem.firstChild) {
				LSD.dom.remove(elem.firstChild);
			}
		},
		text : function(elem) {
			r = '';
			elem = elem.childNodes || elem;
			for (var i = 0 ; i < elem.length ; i++) {
				r += elem[i].nodeType != 1?elem[i].nodeValue:LSD.dom.text(elem[i].childNodes);
			}
			return r
		},
		get : function(attribute, value, tag, parent) {
			
			if (attribute == 'tag') return (tag || document).getElementsByTagName(value);
			if (attribute == 'id') return (tag || document).getElementById(value);
			var attribute = {'for' : 'htmlFor', 'class' : 'className'}[attribute] || attribute;
			if (attribute == 'className') {
				var regex = new RegExp("(^|\\s)"+value+"(\\s|$)");
			} else {
				var regex = new RegExp("^"+value+"$");
			}
			var elements = [];
			var allTags = (parent || document).getElementsByTagName(tag||"*");
			for (var i = 0 ; i < allTags.length ; i++) {
				if (regex.test(allTags[i][attribute])) {
					elements.push(allTags[i]);
				}
			}
			return elements.length>0?elements:false;
		}
	},
	events : {
		add : function(elem, type, handler) {
			if (!LSD.events.add.guid) LSD.events.add.guid = 1;
			if (!handler.$$guid) handler.$$guid = LSD.events.add.guid++;
			if (!elem.events) elem.events = {};
			var handlers = elem.events[type];
			if (!handlers) {
				handlers = elem.events[type] = {};
				if (elem['on' + type]) {
					handlers[0] = elem['on' + type];
				}
			}
			handlers[handler.$$guid] = handler;
			elem['on' + type] = LSD.events.handle_event;
		},
		remove : function(elem, type, handler, all_events) {
			if (elem.events && elem.events[type]) {
				if (all_events === true) {
					delete elem.events[type];
				} else {
					delete elem.events[type][handler.$$guid];
				}
			}
		},
		handle_event : function(event) {
			var return_value = true;
			event = event || LSD.events.fix_event(window.event);
			var handlers = this.events[event.type];
			for (var i in handlers) {
				this.$$handle_event = handlers[i];
				if (this.$$handle_event(event) === false) {
					return_value = false
				}
			}
			return return_value;
		},
		fix_event : function(event) {
			event.preventDefault = function() {
				this.returnValue = false;
			}
			event.stopPropagation = function() {
				this.cancelBubble = true;
			}
			return event;
		}
	},
	form : {
		validate : function(elem) {
			LSD.dom.ready(function() {
				if (document.getElementById(elem)) {
					elem = [document.getElementById(elem)];
				} else if (LSD.dom.get('class', elem).length > 0) {
					elem = LSD.dom.get('class', elem);
				} else if (LSD.dom.get('tag', 'form').length > 0) {
					elem = LSD.dom.get('tag', 'form');
				} else { return; }
				for (var i = 0 ; i < elem.length ; i++) {
					LSD.form.set_up(elem[i]);
				}
			});
		},
		set_up : function(form) {
			LSD.form.validate_form(form, true);
			LSD.events.add(form, 'submit', function() { return LSD.form.validate_form(form); });
			for (var i = 0 ; i < form.elements.length ; i++) {
				LSD.events.add(form.elements[i], 'change', function() { LSD.form.validate_field(this); });
//				LSD.events.add(form.elements[i], 'blur', function() { LSD.form.hide_errors(this); LSD.form.validate_field(this); });
			}
		},
		validate_form : function(form, load) {
			var valid = true;
			for (var i = 0 ; i < form.elements.length ; i++) {
				LSD.form.hide_errors(form.elements[i]);
				if (LSD.form.validate_field(form.elements[i], load)) valid = false;
			}
			if (valid && !load && form.ajax) {
				return LSD.ajax.send(form);
			} else {
				return valid;
			}
		},
		validate_field : function(elem, load) {
			var errors = [];
			for (var name in LSD.form.form_error) {
				var regex = new RegExp('(^|\\s)' + name + '(\\s|$)');
				if (regex.test(elem.className) && !LSD.form.form_error[name].test(elem, load)) {
					errors.push(LSD.form.form_error[name].message);
				}
			}
			if (errors.length) {
				if (elem.type == "checkbox" || elem.type == "radio") elem = elem.parentNode.parentNode;
				LSD.form.show_errors(elem, errors);
			}
			return errors.length > 0;
		},
		get_inputs_by_name : function(name) {
			var results = LSD.dom.get('name', name, 'input');
			results.num_checked = 0;
			for (var i = 0 ; i < results.length ; i++) {
				if (results[i].checked) results.num_checked++;
			}
			return results;
		},
		hide_errors : function(elem) {
			var next = elem.nextSibling;
			if (next && next.nodeName == "UL" && next.className == 'errors') {
				elem.parentNode.removeChild(next);
			}
		},
		show_errors : function(elem, errors) {
			var next = elem.nextSibling;
			if (next && (next.nodeName != 'UL' || next.className != 'errors')) {
				next = document.createElement('ul');
				next.className = 'errors';
				next.style.display = 'none';
				elem.parentNode.insertBefore(next, elem.nextSibling);
			}
			for (var i = 0 ; i < errors.length ; i++) {
				LSD.dom.remove_children(next);
				var li = document.createElement('li');
				li.innerHTML = errors[i];
				next.appendChild(li);
			}
			LSD.animate.fade_in(next);
		},
		form_error : {
			required : {
				message : "The field above is required.",
				test : function (elem, load) {
					if (elem.type == "checkbox" || elem.type == "radio") {
						return LSD.form.get_inputs_by_name(elem.name).num_checked > 0 || load;
					}
					return elem.value.length > 0 || load || (elem.value == elem.defaultValue && elem.value != '');
				}
			},
			email : {
				message : "Please double check the email address.",
				test : function (elem) {
					return !elem.value || /^[a-z0-9_+.-]+\@([a-z0-9-]+\.)+[a-z0-9]{2,4}$/i.test(elem.value);
				}
			},
			phone : {
				message : "Please double check the phone number, should be 7 digits.",
				test : function (elem) {
					var m = /(\d{3}).*(\d{3}).*(\d{4})/.exec(elem.value);
					if (m) elem.value = m[1] + '-' + m[2] + '-' + m[3];
					return !elem.value || m;
				}
			},
			date : {
				message : "Not a valid date, should be DD/MM/YYYY.",
				test : function(elem) {
					return !elem.value || /^\d{2}\/\d{2}\/\d{2,4}$/.test(elem.value)
				}
			},
			url : {
				message : "Please double check the web address (url).",
				test : function (elem) {
					return !elem.value || elem.value == 'http://' || /^https?:\/\/([a-z0-9-]+\.)+[a-z0-9]{2,4}.*$/.test(elem.value);
				}
			},
			password : {
				message : "Password must be 8 characters and contain both numbers and letter.",
				test : function (elem) {
					return true;  // choke
					return !elem.value || (elem.value.length > 7 && (/[0-9]/.test(elem.value) && /[a-z]/i.test(elem.value)));
				}
			},
			pass_confirm : {
				message : "Confirmation does not match the password entered.",
				test : function (elem) {
					var input = elem.parentNode.getElementsByTagName('input');
					for (var i = 0 ; i < input.length ; i++) {
						if (/(^|\s)password(\s|$)/.test(input[i].className)) var password = input[i].value;
						if (/(^|\s)pass_confirm(\s|$)/.test(input[i].className)) var pass_confirm = input[i].value;
					}
					return (!password && !elem.value) || (password && pass_confirm && password == pass_confirm);
				}
			},
			asbru : {
				message : '',
				test : function(elem) {
					var content = elem.innerHTML;
					var name = elem.name;
					var content = content.replace(/&gt;/gi, '>');
					var content = content.replace(/&lt;/gi, '<');
	var div = LSD.dom.create('div');
	div.style.paddingLeft = '7em';
	div.style.width = '80%';
	LSD.dom.insert_last('<p></p><p></p><p></p>', div);
	elem.parentNode.replaceChild(div, elem);
	var editor = div.getElementsByTagName('p');

	WebEditorToolbar(name, {toolbar2:"bold italic underline insertmedia createlink unlink mailto insertorderedlist insertunorderedlist", toolbar1:"cut copy paste undo redo specialcharacter viewsource", container:editor[0], toolbar:name});
	WebEditor(name, content, {container:editor[1], height:"300px", format:'xhtml', onEnter:'<p>', onShiftEnter:'<br>'});
	WebEditorDOMInspector(name, editor[2]);


/*					var table = LSD.dom.create('table');
					table.style.width = '80%';
					table.style.zIndex = '10000';
					for (var i = 0 ; i < 2 ; i++) {
						tr = LSD.dom.create('tr');
//						tr.width = "100%";
						var td = LSD.dom.create('td');
//						td.width = "100%";
						tr.appendChild(td);
						table.appendChild(tr);
					}
					elem.parentNode.replaceChild(table, elem);
					var editor = table.getElementsByTagName('td');
					WebEditor(name, content, {container:editor[1]});
					WebEditorToolbar(name, {toolbar2:"bold italic underline insertmedia createlink unlink mailto insertorderedlist insertunorderedlist", toolbar1:"cut copy paste undo redo specialcharacter viewsource", container:editor[0]});
//					WebEditorDOMInspector(name, editor[2]);*/
					return true;
				}
			}
		}
	},
	style : {
		get : function(elem, name) {
			if (elem.style[name]) {
				return elem.style[name];
			} else if (elem.currentStyle) {  // for IE
				return elem.currentStyle[name];
			} else if (document.defaultView && document.defaultView.getComputedStyle) {  // for FF, safari
				if (name == 'padding') {
					return LSD.style.get(elem, "padding-top")+' '+LSD.style.get(elem, "padding-right")+' '+LSD.style.get(elem, "padding-bottom")+' '+LSD.style.get(elem, "padding-left");
				}
				name = name.replace(/(A-Z)/g, "-$1");
				name = name.toLowerCase(); 
				try {
					return document.defaultView.getComputedStyle(elem, null).getPropertyValue(name);
				} catch(e) { // For safari 2 display:hidden bug
					var old = LSD.style.reset_css(elem, { display : 'block', visibility : 'hidden', position: 'absolute' });
					var r = document.defaultView.getComputedStyle(elem, null).getPropertyValue(name);
					LSD.style.restore_css(elem, old);
					return r;
				}
			} else {
				return null;
			}
		},
		reset_css : function(elem, prop) {
			var old = {};
			for (var i in prop) {
				old[i] = elem.style[i];
				elem.style[i] = prop[i];
			}
			return old;
		},
		restore_css : function(elem, prop) {
			for (var i in prop) {
				elem.style[i] = prop[i];
			}
		}
	},
	element : {
		attribute : function(elem, attribute, value) {
			if (!attribute || attribute.constructor != String) return '';
			attribute = {'for' : 'htmlFor', 'class' : 'className'}[attribute] || attribute;
			if (typeof value != 'undefined') {
				elem[attribute] = value;
				if (elem.setAttribute) {
					elem.setAttribute(attribute, value);
				}
			}
			return elem[attribute] || elem.getAttribute(attribute) || '';
		},
		x : function(elem) {
			return elem.offsetParent ?
				elem.offsetLeft + LSD.element.x(elem.offsetParent) :
				elem.offsetLeft;
		},
		y : function(elem) {
			return elem.offsetParent ?
				elem.offsetTop + LSD.element.y(elem.offsetParent) :
				elem.offsetTop;
		},
		to_parent_x : function(elem) {
			return elem.parentNode == elem.offsetParent ?
				elem.offsetLeft :
				LSD.element.x(elem) - LSD.element.x(elem.parentNode);
		},
		to_parent_y : function(elem) {
			return elem.parentNode == elem.offsetParent ?
				elem.offsetTop :
				LSD.element.y(elem) - LSD.element.y(elem.parentNode);
		},
		css_x : function(elem) {
			return parseInt(LSD.style.get(elem, 'left'));
		},
		css_y : function(elem) {
			return parseInt(LSD.style.get(elem, 'top'));
		},
		set_x : function(elem, pos) {
			elem.style.left = pos+'px';
		},
		set_y : function(elem, pos) {
			elem.style.top = pos+'px';
		},
		add_x : function(elem, pos) {
			LSD.position.set_x(LSD.element.css_x(elem)+pos);
		},
		add_y : function(elem, pos) {
			LSD.position.set_y(LSD.element.css_y(elem)+pos);
		},
		get_height : function(elem) {
			return parseInt(LSD.style.get(elem, 'height'))
		},
		height : function(elem) {
			var padding = LSD.style.get(elem, 'padding');
			var matches = padding.match(/(\d+)/g);
			var padding = parseInt(matches[0] || 0)+parseInt(matches[2] || 0);
			if (LSD.style.get(elem, 'display') != 'none') {
				var r = elem.offsetHeight || LSD.element.get_height(elem);
				return r-padding;
			}
			var old = LSD.style.reset_css(elem, { display : 'block', visibility : 'hidden', position: 'absolute' });
			var r = elem.clientHeight || LSD.element.get_height(elem);
			LSD.style.restore_css(elem, old);
			return r-padding;
		},
		get_width : function(elem) {
			return parseInt(LSD.style.get(elem, 'height'))
		},
		width : function(elem) {
			if (LSD.style.get(elem, 'display') != 'none') {
				return elem.offsetWidth || LSD.element.get_width(elem);
			}
			var old = LSD.style.reset_css(elem, { display : '', visibility : 'hidden', position: 'absolute' });
			var r = elem.clientWidth || LSD.element.get_width(elem);
			LSD.style.restore_css(elem, old);
			return r;
		},
		adjust_size : function(elem) {
			if (!elem) return;
			var h = Math.max(LSD.page.height(), LSD.page.window_height());
			var w = Math.max(LSD.page.width(), LSD.page.window_width());
			elem.style.width = w+'px';
			elem.style.height = h+'px';
//			LSD.events.add(window, 'resize', function(){ LSD.element.adjust_size(elem); });
		},
		adjust_position : function(elem) {
			if (!elem) return;
			var h = LSD.element.height(elem);
			var w = LSD.element.width(elem);
//			var t = LSD.page.scroll_y() + (LSD.page.window_height()/2) - (h/2);
//			if (t < 0) t = 0;
			var l = LSD.page.scroll_x() + (LSD.page.window_width()/2) - (w/2);
			if (l < 0) l = 0;
			LSD.element.set_y(elem, 20);
			LSD.element.set_x(elem, l);
//			LSD.events.add(window, 'resize', function(){ LSD.element.adjust_position(elem); });
//			LSD.events.add(document, 'scroll', function(){ LSD.element.adjust_position(elem); });
		}
	},
	animate : {
		hide : function(elem) {
			var cur_display = LSD.style.get(elem, 'display');
			if (cur_display != 'none') {
				elem.old_display = cur_display;
				elem.style.display = 'none';
			}
		},
		show : function(elem) {
			elem.style.display = elem.old_display || 'block';
		},
		opacity : function(elem, level) {
			if (window.opera) return '';
			elem.style.MozOpacity = level/100;
			elem.style.opacity = level/100;
			if (elem.filters) {
				if (LSD.style.get(elem, 'width') == 'auto' && LSD.style.get(elem, 'height') == 'auto') {
					elem.style.width = LSD.position.width(elem);
				}
				elem.style.filter = 'alpha(opacity='+level+')';
			}
		},
		grow_y : function(elem) {
			if (!elem.height) elem.height = LSD.element.height(elem);
			var h = elem.height || LSD.element.height(elem);
			elem.style.height = '0px';
			elem.style.overflow = 'hidden';
			LSD.animate.show(elem);
			for (var i = 0 ; i <= 100 ; i += 5) {
				(function(){
					var pos = i;
					setTimeout(function(){
						elem.style.height = ((pos/100)*h)+'px';
					}, (pos+1)*10);
				})();
			}
			setTimeout(function() { elem.style.overflow = "visible"; }, 1100)
		},
		shrink_y : function(elem) {
			var h = elem.height || LSD.element.height(elem);
			elem.style.overflow = 'hidden';
			for (var i = 0 ; i <= 100 ; i += 5) {
				(function(){
					var pos = i;
					setTimeout(function(){
						elem.style.height = (h-(pos/100)*h)+'px';
					}, (pos+1)*10);
				})();
			}
			setTimeout(function() { LSD.animate.hide(elem); }, 1100);
		},
		grow_x : function(elem) {
			var w = LSD.element.width(elem);
			elem.style.width = '0px';
			elem.style.overflow = 'hidden';
			LSD.animate.show(elem);
			for (var i = 0 ; i <= 100 ; i += 5) {
				(function(){
					var pos = i;
					setTimeout(function(){
						elem.style.width = ((pos/100)*w)+'px';
					}, (pos+1)*10);
				})();
			}
		},
		fade_in : function(elem, end) {
			var end = (end < 100)?end:99;
			LSD.animate.opacity(elem, 0);
			LSD.animate.show(elem);
			for (var i = 4 ; i <= end ; i +=5) {
				(function(){
					var pos = i;
					setTimeout(function(){
						LSD.animate.opacity(elem, pos);
					}, (pos+1)*10);
				})();
			}
		},
		fade_out : function(elem) {
			// fade_out does not work
			alert('fade_out does not work');
			return;
			LSD.animate.opacity(elem, 100);
			LSD.animate.show(elem);
			for (var i = 100 ; i >= 0 ; i -= 5) {
				(function(){
					var pos = i;
					setTimeout(function(){
						LSD.animate.opacity(elem, pos);
					}, (pos+1)*10);
				})();
			}
		}
	},
	mouse : {
// Needs testing get mouse relative to page
		x : function(e) {
			e = e || window.event;
			return e.pageX || e.clentX + document.body.scrollLeft;
		},
		y : function(e) {
			e = e || window.event;
			return e.pageY || e.clentY + document.body.scrollLeft;
		},
// Needs testing get mouse relative to element
		element_x : function (e) {
			return (e && e.layerX) || window.event.offsetX;
		},
		element_y : function (e) {
			return (e && e.layerY) || window.event.offsetY;
		}
	},
	page : {
// Needs testing finds page height
		height : function() {
			return document.body.scrollHeight;
		},
		width : function() {
			return document.body.scrollWidth;
		},
// Needs testing, finds how far the browser is scrolled
		scroll_x : function() {
			var de = document.documentElement;
			return self.pageXOffset || (de && de.scrollLeft) || document.body.scrollLeft;
		},
		scroll_y : function() {
			var de = document.documentElement;
			return self.pageYOffset || (de && de.scrollTop) || document.body.scrollTop;
		},
// Needs testing viewport height/width
		window_height : function() {
			var de = document.documentElement;
			return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
		},
		window_width : function() {
			var de = document.documentElement;
			return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
		}
	},
	drag : function() {
// Drag and drop
/*
var Drag = {

    // The current element being dragged
    obj: null,

    // The initalization function for the drag object
    // o = The element to act as the drag handle
    // oRoot = The element to be dragged, if not specified, 
    //               the handle will be the element dragged.
    // minX, maxX, minY, maxY = The min and max coordinates allowed for the element
    // bSwapHorzRef = Toggle the horizontal coordinate system
    // bSwapVertRef = Toggle the vertical coordinate system
    // fxMapper, fyMapper =  Functions for mapping x and y coordinates to others
    init: function(o, oRoot, minX, maxX, minY, 
            maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper) {

        // Watch for the drag event to start
        o.onmousedown = Drag.start;

        // Figure out which coordinate system is being used
        o.hmode = bSwapHorzRef ? false : true ;
        o.vmode = bSwapVertRef ? false : true ;

        // Figure out which element is acting as the draggable ‘handle’
        o.root = oRoot && oRoot != null ? oRoot : o ;

        // Initalize the specified coordinate system
        if (o.hmode && isNaN(parseInt(o.root.style.left ))) o.root.style.left   = "0px";
        if (o.vmode && isNaN(parseInt(o.root.style.top ))) o.root.style.top    = "0px";
        if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
        if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

        // Look to see if the user provided min/max x/y coordinates
        o.minX = typeof minX != 'undefined' ? minX : null;
        o.minY = typeof minY != 'undefined' ? minY : null;
        o.maxX = typeof maxX != 'undefined' ? maxX : null;
        o.maxY = typeof maxY != 'undefined' ? maxY : null;

        // Check for any specified x and y coordinate mappers
        o.xMapper = fXMapper ? fXMapper : null;
        o.yMapper = fYMapper ? fYMapper : null;

        // Add shells for all the user-defined functions
        o.root.onDragStart = new Function();
        o.root.onDragEnd  = new Function();
        o.root.onDrag = new Function();

    },

    start: function(e) {
        // Figure out the object that’s being dragged
        var o = Drag.obj = this;

        // Normalize the event object
        e = Drag.fixE(e);

        // Get the current x and y coordinates
        var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
        var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );

        // Call the user’s function with the current x and y coordinates
        o.root.onDragStart(x, y);

        // Remember the starting mouse position
        o.lastMouseX = e.clientX;
        o.lastMouseY = e.clientY;

        // If we’re using the CSS coordinate system
        if (o.hmode) {
            // set the min and max coordiantes, where applicable
            if (o.minX != null) o.minMouseX    = e.clientX - x + o.minX;
            if (o.maxX != null) o.maxMouseX    = o.minMouseX + o.maxX - o.minX;

        // Otherwise, we’re using a traditional mathematical coordinate system
        } else {
            if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
            if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
        }

        // If we’re using the CSS coordinate system
        if (o.vmode) {
            // set the min and max coordiantes, where applicable
            if (o.minY != null) o.minMouseY    = e.clientY - y + o.minY;
            if (o.maxY != null) o.maxMouseY    = o.minMouseY + o.maxY - o.minY;

        // Otherwise, we’re using a traditional mathematical coordinate system
        } else {
            if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
            if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
        }

        // Watch for ‘dragging’ and ‘drag end’ events
        document.onmousemove = Drag.drag;
        document.onmouseup = Drag.end;

        return false;
    },

    // A function to watch for all movements of the mouse during the drag event
    drag: function(e) {
        // Normalize the event object
        e = Drag.fixE(e);

        // Get our reference to the element being dragged
        var o = Drag.obj;

        // Get the position of the mouse within the window
        var ey = e.clientY;
        var ex = e.clientX;

        // Get the current x and y coordinates
        var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
        var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
        var nx, ny;

        // If a minimum X position was set, make sure it doesn’t go past that
        if (o.minX != null) ex = o.hmode ? 
            Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);

        // If a maximum X position was set, make sure it doesn’t go past that
        if (o.maxX != null) ex = o.hmode ? 
            Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);

        // If a minimum Y position was set, make sure it doesn’t go past that
        if (o.minY != null) ey = o.vmode ? 
            Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);

        // If a maximum Y position was set, make sure it doesn’t go past that
        if (o.maxY != null) ey = o.vmode ? 
            Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

        // Figure out the newly translated x and y coordinates
        nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
        ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

        // and translate them using an x or y mapper function (if provided)
        if (o.xMapper) nx = o.xMapper(y)
        else if (o.yMapper) ny = o.yMapper(x)

        // Set the new x and y coordinates onto the element
        Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
        Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";

        // and remember  the last position of the mouse
        Drag.obj.lastMouseX = ex;
        Drag.obj.lastMouseY = ey;

        // Call the user’s onDrag  function with the current x and y coordinates
        Drag.obj.root.onDrag(nx, ny);

        return false;
    },

    // Function that handles the end of a drag event
    end: function() {
        // No longer watch for mouse events (as the drag is done)
        document.onmousemove = null;
        document.onmouseup = null;

        // Call our special onDragEnd function with the x and y coordinates
        // of the element at the end of the drag event
        Drag.obj.root.onDragEnd( 
            parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
            parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
        // No longer watch the object for drags
        Drag.obj = null;
    },

    // A function for normalizes the event object
    fixE: function(e) {
        // If no event object exists, then this is IE, so provide IE’s event object
        if (typeof e == 'undefined') e = window.event;

        // If the layer properties aren’t set, get the values from the equivalent
        // offset properties
        if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
        if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;

        return e;
    }
};
*/
	},
	shadow : {
		show : function(trigger) {
			if (!document.getElementById('LSD_shadow_overlay')) LSD.shadow.create_overlay();
			var overlay = document.getElementById('LSD_shadow_overlay');
			
			overlay.style.height = Math.max(LSD.page.height(), LSD.page.window_height())+'px';
			overlay.style.width = Math.max(LSD.page.width(), LSD.page.window_width())+'px';
			LSD.animate.fade_in(overlay, 60);
			LSD.element.adjust_size(overlay);
		},
		hide : function() {
			LSD.animate.hide(document.getElementById('LSD_shadow_overlay'));
		},
		create_overlay : function() {
			var overlay = LSD.dom.create('div');
			overlay.id = 'LSD_shadow_overlay';
			overlay.style.background = "#000";
			overlay.style.opacity = "0.5";
			overlay.style.display = "none";
			overlay.style.position = "absolute";
			overlay.style.top = "0";
			overlay.style.left = "0";
			overlay.style.width = "100%";
			overlay.style.height = "100%";
			overlay.style.zIndex = "1000";
//			overlay.title = 'Click to return to site.';
//			LSD.events.add(overlay, 'click', LSD.shadow.hide);
			document.body.appendChild(overlay);
		}
	},
	inHouse : {
		
	},
	ajax : {
		send : function (elem) {
			var url = elem.url || elem.action || "";
			var method = elem.method.toUpperCase() || "POST";
			var parameters = elem.elements?LSD.ajax.serialize_input(elem):LSD.ajax.serialize_input(elem.parameters);
			var finished = elem.finished || function(){};
			var success = elem.success || function(){};
			var failure = elem.failure || function(){};
			if (LSD.ajax.isFileUpload(elem)) {
				LSD.shadow.show();
				var iframe = LSD.ajax.createIframe();
					var input = LSD.dom.create('input');
					input.type = 'hidden';
					input.name = 'LSD_ajax';
					input.value = 1;
					LSD.dom.insert_last(input, elem);
				elem.target = iframe.name;
				var elem = elem;
				LSD.events.remove(iframe, 'load', null, true);
				if (!iframe.onload) {
					LSD.events.add(iframe, 'load', function(){
						var results = LSD.ajax.iframeContents(elem);
						try {
							success(eval("(" + results + ")"));
						} catch(e) {
							failure(results);
						}
						finished();
						LSD.shadow.hide();
						var elem = document.getElementById(elem.id);
						setTimeout(function() { LSD.ajax.removeIframe(elem); }, 5000);
					});
				} else {
					iframe.onreadystatechange = function() {
						if (iframe.readyState == 'complete') {
							var results = LSD.ajax.iframeContents(elem);
							try {
								success(eval("(" + results + ")"));
							} catch(e) {
								failure(results);
							}
							finished();
							LSD.shadow.hide();
							var elem = document.getElementById(elem.id);
							setTimeout(function() { LSD.ajax.removeIframe(elem); }, 5000);
						}
					}
				}
				return true;
			}  // End Submit file
			try {
				var xml = new XMLHttpRequest();
			} catch (e) {
				try {
					var xml = new ActiveXObject("Msxml2.XMLHTTP");
				} catch (e) {
					try {
						var xml = new ActiveXObject("Microsoft.XMLHTTP");
					} catch (e) {
						return true;	
					}
				}
			}
//			var elem = elem;
			xml.onreadystatechange = function() {
				if (xml.readyState == 4) {
					try {
						success(eval("(" + xml.responseText + ")"));
					} catch(e) {
						failure(xml.responseText);
					}
					finished();
					xml = null;
					LSD.shadow.hide();
				}
			}
			xml.open('POST', url, true);
			xml.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
			xml.setRequestHeader("Content-length", parameters.length);
			xml.setRequestHeader("Connection", "close");
			xml.send(parameters);
			LSD.shadow.show();
			return false;
		},
		serialize_input : function(input) {
			if (input == '') return;
			if (input.constructor == String) return input;
			var f = [];
			if (input.elements) {
				for (var i = 0 ; i < input.elements.length ; i++) {
					if (input.elements[i].value) {
						f.push(input.elements[i])
					}
				}
				var input = f;
			}
			var r = [];
			if (input.constructor == Array) {
				for (var i = 0 ; i < input.length ; i++) {
					r.push(input[i].name+'='+encodeURIComponent(input[i].value));
				}
			} else {
				for (var j in input)
					r.push(j+'='+encodeURIComponent(input[j]));
			}
			r.push('LSD_ajax=1');
			return r.join('&');
		},
		isFileUpload : function(elem) {
			if (elem.nodeName == 'FORM' && elem.elements) {
				var file = LSD.dom.get('type', 'file', 'input', elem);
				if (file.length > 0) {
					for (var i = 0 ; i < file.length ; i++) {
						if (file[i] && file[i].value != '') {
							return true;
						}
					}
				}
			}
			return false;
		},
		createIframe : function() {
			var iframe = LSD.dom.create('iframe');
			iframe.style.position = "absolute";
			iframe.style.left = "-9000px";
			var counter = 0;
			while(LSD.dom.get('name', 'LSD_iframe'+counter, 'iframe')) {
				counter++;
				if (counter > 10) {
					throw("createIframe counter error");
				}
			}
			iframe.name = 'LSD_iframe'+counter;
			iframe.id = 'LSD_iframe'+counter;
			LSD.dom.insert_last(iframe, document.body);
			if(self.frames['LSD_iframe'+counter].name != 'LSD_iframe'+counter) {
				self.frames['LSD_iframe'+counter].name = 'LSD_iframe'+counter;
			}
			return iframe;
		},
		removeIframe : function(elem) {
			LSD.dom.remove(elem);
		},
		iframeContents : function(elem){
			if (!document.getElementById(elem)) var elem = elem.id;
			if (document.getElementById(elem).contentDocument) {
				return document.getElementById(elem).contentDocument.body.innerHTML;
			} else {
				return window.frames[elem].document.body.innerHTML;
			}
			return '';
/*			var serializer = new XMLSerializer();
			var xml = serializer.serializeToString(d);
			var parser = new DOMParser();
			var doc = parser.parseFromString(xml, 'text/xml');
			var r = new String();
			r.fullText = xml;
			try {
				var text = doc.getElementsByTagName('lsd_text')[0].firstChild.nodeValue;
				var script = doc.getElementsByTagName('lsd_script')[0].firstChild.nodeValue;
				var r = doc;
				r.text = text;
				r.script = script;
				return r;
			} catch(e) {}
			return r;*/
		}
	}
};
//LSD.events.add.guid = 1;
document.documentElement.className = 'javascript';