
if (!Array.prototype.contains) {
	Array.prototype.contains = function(value) {
		for (var i = 0; i < this.length; i++) {
			if (this[i] == value) {
				return true;
			}
		}
		return false;
	};
}

DD = {};
DD.Init = function() { };
DD.addClass = function(o, classname) {
	var classes;
	if (o.className == undefined) {
		classes = [];
	} else {
		classes = o.className.split(' ');
	}
	if (!classes.contains(classname)) {
		o.className += ' ' + classname.replace(/[^a-zA-Z0-9-]/,'').toLowerCase();
	}
};
DD.removeClass = function(o, classname) {
	 var regex = new RegExp('(\\s|^)' + classname);
	o.className = o.className.replace(regex,'');
};
DD.hasClass = function(o, classname) {
	if (o.className == undefined) {
		return false;
	}
	var classes = o.className.split(' ');
	return classes.contains(classname);
};
DD.getElementsByClassName = function(o, classname) {
	var results = [];
	if (typeof(o) == typeof(document) && o.body) {
		o = o.body;
	}
	for (var i = 0; i < o.childNodes.length; i++) {
		if (DD.hasClass(o.childNodes[i], classname)) {
			results.push(o.childNodes[i]);
		}
		results = results.concat(DD.getElementsByClassName(o.childNodes[i], classname));
	}
	return results;
};
DD.addEventListener = function(o, event, handler) {
	if (o.addEventListener) {
		o.addEventListener(event, handler, false);
	} else if (o.attachEvent) {
		o.attachEvent('on' + event, handler);
	}
};
DD.removeEventListener = function(o, event, handler) {
	if (o.removeEventListener) {
		o.removeEventListener(event, handler, false);
	} else if (o.detachEvent) {
		o.detachEvent('on' + event, handler);
	}
};
DD.stopPropagation = function(event) {
	if (event.stopPropagation) {
		event.stopPropagation();
	} else {
		event.cancelBubble = true;
	}
};
DD.stopSubmit = function(event) {
	if (event.preventDefault) {
		event.preventDefault();
	} else if (window.event) {
        window.event.returnValue = false;
    }
	return false;
};

DD.Popup = function(url, override) {
	var defaults = {'status':0,'toolbar':0,'location':0,'menubar':0,'width':400,'height':400}
	for (var key in override) {
		defaults[key] = override[key];
	}
	if (!override.top) defaults.top = (screen.height / 2) - (defaults.height / 2);
	if (!override.left) defaults.left = (screen.width / 2) - (defaults.width / 2);

	var opts = [];
	for (var key in defaults) {
		opts.push(key + '=' + defaults[key]);
	}
	window.open(url,'_blank', opts.join(','));
}

DD.ResultSet = function(node, url) {
	this.root = document.getElementById(node);
	this.url = url;
	var rows = this.root.getElementsByTagName('tbody')[0].getElementsByTagName('tr');
	for (var i = 0; i < rows.length; i++) {
		this.add(rows[i]);
	}
};
DD.ResultSet.prototype.add = function(node) {
	var tmp = this;
	DD.addEventListener(node, 'mouseover', function() { DD.addClass(node, 'selected'); });
	DD.addEventListener(node, 'mouseout', function() { DD.removeClass(node, 'selected'); });
	DD.addEventListener(node, 'click', function() { document.location.href = tmp.url + '&id=' + node.id.substr('transaction-'.length); });
};

DD.Modal = function(src, opts) {
	var tmp = this;
    if (opts == null) {
		opts = {};
	}
    this.overlay = document.createElement('div');
    this.overlay.className = 'dd-overlay';
    document.body.appendChild(this.overlay);
    this.frame = document.createElement('iframe');
    if (opts.init != null) {
	    this.frame.onload = opts.init;
	}
    document.body.appendChild(this.frame);
    document.body.style.overflow = 'hidden';
    this.frame.src = src;
    this.frame.className = 'dd-popup';
    this.frame.style.width = (opts.width == null ? 550 : opts.width) + 'px';
    this.frame.style.height = (opts.height == null ? 350 : opts.height) + 'px';
	var doc = document;
	DD.addEventListener(this.frame, 'load', function() {
		tmp.frame.style.left = Math.floor((doc.body.clientWidth - tmp.frame.contentDocument.body.clientWidth) / 2) + 'px';
		tmp.frame.contentDocument.modal = tmp;
	});
};
DD.Modal.prototype.close = function() {
    document.body.removeChild(this.frame);
    document.body.removeChild(this.overlay);
	document.body.style.overflow = 'auto';
};
DD.Modal.prototype.fit = function() {
	this.frame.style.height = (this.frame.contentDocument.body.clientHeight + 8) + 'px';
}

DD.FileBrowser = function(source) {
	var tmp = this;
    this.source = source;
    var url = '/?type=filebrowser';
    if (this.source.value != '') {
		url += '&location=' + this.source.value;
	}
    this.modal = new DD.Modal(url, {'init': function() { tmp.init(); }});
	this.modal.frame.contentDocument.filebrowser = this;
};
DD.FileBrowser.prototype.init = function() {
	var i;
	var tmp = this;
	var anchors = this.modal.frame.contentDocument.getElementsByTagName('a');
	for (i = 0; i < anchors.length; i++) {
		var a = anchors[i];
		if (a.className == 'file') {
			a.addEventListener('click', function(e) { tmp.source.value = a.pathname;
			                                          DD.stopPropagation(e);
			                                          tmp.modal.close(); }, false);
		}
	}
	var buttons = this.modal.frame.contentDocument.getElementById('footer').getElementsByTagName('input');
	for (i = 0; i < buttons.length; i++) {
		switch (buttons[i].value) {
			case 'OK':
				buttons[i].addEventListener('click', function() { tmp.source.value = tmp.modal.frame.contentDocument.getElementById('location').innerHTML;
				                                                  tmp.modal.close(); }, false);
				break;
			case 'Cancel':
				buttons[i].addEventListener('click', function() { tmp.modal.close(); }, false);
				break;
			default:
				break;
		}
	}
};

DD.Url = function(url) {
	this.relative = false;
	this.protocol = null;
	this.domain = null;
	this.path = null;
	this.args = {};
	if (url == null) url = document.location.href;
	this.parse(url);
}
DD.Url.prototype.parse = function(url) {
	var regex = /^((([a-zA-Z]+):\/\/)(([a-zA-Z0-9_]+\.)+([a-zA-Z0-9_]+)))?((\/[^\/\?]*)*)(\?(.+))?/;
	var index = 0;
	match = regex.exec(url);
	if (match) {
		if (match.length >= 2) this.protocol = match[3];
		if (match.length >= 3) this.domain = match[4];
		if (match.length >= 6) this.path = match[7];
		if (match.length >= 9) {
			var args = match[10].split('&');
			for (var i = 0; i < args.length; i++) {
				var pair = args[i].split('=');
				this.args[pair[0]] = (pair.length > 1 ? pair[1] : null);
			}
		}
	}
	this.relative = (this.domain != null);
}
DD.Url.prototype.toString = function() {
	args = [];
	for (key in this.args) {
		args.push(key + '=' + this.args[key]);
	}
	return (this.protocol != null ? this.protocol + '://' : '') + (this.domain != null ? this.domain : '') + (this.path != null ? this.path : '') + '?' + args.join('&');
}

DD.FormValidator = function(form) {
	var tmp = this;
	this.form = form;
	this.error = 'The following problems were encountered:';
	this.elements = {};
	this.messages = {};
	this.realtime = false;
	this.form.validator = this;
	DD.addEventListener(this.form, 'submit', function(e) {
		if (!tmp.validate()) {
			tmp.alert();
			return DD.stopSubmit(e);
		}
	});
};
DD.FormValidator.prototype.alert = function() {
	if (this.error != null) {
		window.alert(this.getMessage());
	}
};
DD.FormValidator.prototype.require = function(name, regex, error) {
	if (this.form[name] == null) {
		alert('Field "' + name + '" does not exist');
	}
	if (error == null) {
		if (regex == null) {
			error = name + ' is required';
		} else {
			error = name + ' is not valid';
		}
	}
	this.elements[name] = ({'name':name,'regex':(regex==null?null:new RegExp(regex)),'error':error,'valid':true});
};
DD.FormValidator.prototype.validate = function(name) {
	if (name == null) {
		var eValid = false;
		this.valid = true;
		this.messages = {};
		for (var key in this.elements) {
			eValid = this.validate(key, true);
			if (this.realtimeCallback != null) {
				this.realtimeCallback(this.form[key], eValid, this.elements[key].error);
			}
		}
		return this.valid;
	} else {
		if ((this.elements[name].regex == null && this.form[name].value == '') || (this.elements[name].regex != null && !this.elements[name].regex.exec(this.form[name].value))) {
			this.valid = false;
			this.messages[name] = this.elements[name].error;
			return false;
		}
		this.messages[name] = null;
		return true;
	}
	return this.valid;
};
DD.FormValidator.prototype.setRealtime = function(callback)  {
	var key;

	this.realtimeCallback = callback;
	for (var key in this.elements) {
		this.addRealtime(this.form[key], callback);
	}
};
DD.FormValidator.prototype.addRealtime = function(node, callback) {
	var valid;
	var tmp = this;
	this.elements[node.name].realtimeHandler = function(e) {
		if (tmp.realtimeCallback != null) {
			valid = tmp.validate(node.name);
			tmp.realtimeCallback(node, valid, tmp.elements[node.name].error);
		}
	};
	DD.addEventListener(node, 'blur', function() { tmp.elements[node.name].realtimeHandler(); });
	if (node.tagName == 'SELECT') {
		DD.addEventListener(node, 'change', function() { tmp.elements[node.name].realtimeHandler(); });
	}
};
DD.FormValidator.prototype.getMessage = function() {
	var m = this.error;
	 for (key in this.messages) {
		if (this.messages[key] != null) {
			m += "\n - " + this.messages[key];
		}
	 }
	return m;
};

DD.Ajax = function(url) {
	this.url = url;
	this.data = {};
}
DD.Ajax.prototype.send = function() {
	var tmp = this;
	if (window.XMLHttpRequest && !(window.ActiveXObject)) {
		this.request = new XMLHttpRequest();
	} else if (window.ActiveXObject) {
		try {
			this.request = new ActiveXObject("Msxml2.XMLHTTP");
		} catch(e) {
			this.request = new ActiveXObject("Microsoft.XMLHTTP");
		}
	}
	if (this.responseHandler) {
		this.request.onreadystatechange = function(e) {
			if (tmp.request.readyState == 4) {
				tmp.responseHandler(tmp.request);
			}
		}
	}
	this.request.open("GET", this.url, true);
	this.request.send("");
}

DD.Rotator = function(node) {
	var i = document.createElement('div');
	i.appendChild(document.createElement('img')).src = '/img/v8/360-help.gif';
	i.lastChild.alt = 'Click and drag the photo to rotate it';
	i.appendChild(document.createElement('br'));
	i.appendChild(document.createTextNode('Click and drag the photo to rotate it'));
	this.images = [];       //Array of image resources
	this.loaded = 0;        //Number of images loaded
	this.onload = null;     //Function to be called when rotator becomes usable
	this.oncomplete = null; //Function to be called when rotator is completely loaded
	this.interval = null;   //The number of pixels the mouse has to traverse before the frame changes. Null indicates auto-calculate
	this.current = 0;       //The current frame
	this.origin = 0;        //The position on the screen the mouse-cursor was at the last time a frame changed
	this.root = node;       //The HTML node this rotator is attached to
	this.loadChunk = 4;     //The divisor used to interleave image loading
	this.instructions = i;
	this.root.rotator = this;
}
DD.Rotator.prototype.load = function() {
	var tmp = this;
	var done = 0; var i = 0;
	var interlace = Math.ceil(this.images.length / this.loadChunk);
	while (done < this.images.length) {
		for (j = 0; j < this.loadChunk; j++) {
			var index = i + (j * interlace);
			if (index >= this.images.length) break;
			var src = this.images[index];
			this.images[index] = new Image();
			DD.addEventListener(this.images[index], 'load', function() { tmp.imageLoaded(); });
			this.images[index].src = src;
			done++;
		}
		i++;
	}
	if (this.interval == null) {
		this.interval = Math.ceil(1000 / this.images.length);
	}
	this.help = document.createElement('span');
	DD.addClass(this.help, 'help');
	if (typeof this.instructions == 'object') {
		this.help.appendChild(this.instructions);
	} else {
		this.help.appendChild(document.createTextNode(this.instructions));
	}
	this.root.appendChild(this.help);
}
DD.Rotator.prototype.unload = function() {
	DD.removeEventListener(this.root, this.startRotation);
	if (this.help.parentNode != null) {
		this.root.removeChild(this.help);
	}
}
DD.Rotator.prototype.imageLoaded = function() {
	var tmp = this;
	this.loaded++;
	if (this.loaded == this.loadChunk) {
		this.root.style.backgroundRepeat = 'no-repeat';
		this.root.style.backgroundPosition = 'center center';
		this.rotate();
		this.startRotation = function(e) {
			tmp.spin(null, (e.clientX > tmp.root.offsetWidth / 2 ? -1 : 1), true);
			DD.stopPropagation(e);
			if (e.preventDefault) e.preventDefault();
			tmp.origin = e.screenX;
			var f = function(e) {
				tmp.rotate(e);
			}
			var g = function() {
				tmp.cancelSpin();
				DD.removeEventListener(document, 'mousemove', f);
				DD.removeEventListener(document, 'mousemove', g);
			}
			DD.addEventListener(document, 'mousemove', f);
			DD.addEventListener(document, 'mouseup', g);
		}
		DD.addEventListener(this.root, 'mousedown', this.startRotation);
		if (this.onload != null) {
			this.onload(this, this.loaded / this.images.length);
		}
	}
	if (this.loaded >= this.images.length && this.oncomplete) {
		this.oncomplete();
	}
}
DD.Rotator.prototype.rotate = function(e) {
	this.cancelSpin();
	if (e != null) {
		var movement = (e.screenX - this.origin) / this.interval;
		if (Math.abs(movement) > 1) {
			this.current -= Math.ceil(movement);
			this.origin = e.screenX;
		}
	}
	if (this.current < 0) this.current = this.images.length + this.current;
	this.current = Math.abs(this.current % this.images.length);
	if (this.images[this.current].complete) {
		this.root.style.backgroundImage = 'url(' + this.images[this.current].src + ')';
	}
}
DD.Rotator.prototype.spin = function(speed, direction, loop) {
	if (speed == null) speed = 40;
	if (direction == null) direction = 1;
	if (loop == null) loop = false;
	var tmp = this;
	if (this.spinEnd == false || this.spinEnd == null) this.spinEnd = this.current;
	this.current = (this.current + direction) % this.images.length;
	if (this.current < 0) this.current = this.images.length + this.current;
	this.root.style.backgroundImage = 'url(' + this.images[this.current].src + ')';
	if (this.spinEnd !== false && (this.current != this.spinEnd || loop == true)) {
		var timer = setTimeout(function() { tmp.spin(speed, direction, loop); }, speed);
	} else {
		this.spinEnd = false;
	}
	var cancel = this.cancelSpin;
	this.cancelSpin = function() {
		cancel();
		clearTimeout(timer);
	}
};
DD.Rotator.prototype.cancelSpin = function() {}

DD.FileUploader = function(node) {
	if (typeof node == 'string') {
		node = document.getElementById(node);
	}
	this.root = node.parentNode;
	DD.addClass(this.root, 'dd-file');
	this.workarea = document.createElement('span');
	this.list = document.createElement('ul');
	this.root.appendChild(this.list);
	this.name = node.name;
	this.add(node);
	node.setAttribute('multiple','true');
	this.deleted = document.createElement('span');
	this.root.appendChild(this.deleted);
	this.multi = false;
	this.maximum = null;
	this.confirm = 'Are you sure you want to remove this file from the upload list?';
}
DD.FileUploader.prototype.set = function(values) {
	for (var i = 0; i < values.length; i++) {
		var item = document.createElement('li');
		item.appendChild(document.createTextNode(values[i]));
		this.list.appendChild(item);
		this.addDeleteHandler(item);
		if (this.isFull()) {
			break;
		}
	}
}
DD.FileUploader.prototype.addDeleteHandler = function(item) {
	var tmp = this;
	DD.addEventListener(item, 'click', function(e) {
		if (tmp.confirm == false || confirm(tmp.confirm)) {
			tmp.workarea.innerHTML = '<input type="hidden" />';
			var delitem = tmp.workarea.firstChild;
			delitem.name = tmp.name + '-deleted[]';
			delitem.value = item.innerHTML;
			tmp.deleted.appendChild(delitem);
			tmp.list.removeChild(item);
			tmp.isFull();
		}
	});
}
DD.FileUploader.prototype.add = function(node) {
	var tmp = this;
	if (!node) {
		this.workarea.innerHTML = '<input type="file />';
		node = this.workarea.firstChild;
		if (this.root.childNodes.length > 0) {
			this.root.insertBefore(node, this.root.firstChild);
		} else {
			this.root.parentNode.appendChild(node);
		}
	}
	node.name = this.name + (this.name.substr(0,-2) != '[]' ? '[]' : '');
	DD.addEventListener(node, 'change', function(e) {
		if (tmp.multi && !tmp.isFull()) {
			var item = document.createElement('li');
			item.appendChild(document.createTextNode(node.value));
			item.appendChild(node);
			tmp.list.appendChild(item);
			DD.addEventListener(item, 'click', function(e) {
				if (tmp.confirm == false || confirm(tmp.confirm)) {
					tmp.list.removeChild(item);
				}
			});
			node.style.display = 'none';
			var last = tmp.add();
			tmp.isFull();
		}
	});
	return node;
}
DD.FileUploader.prototype.isFull = function() {
	var current = this.list.getElementsByTagName('li').length;
	var full = (this.maximum != null && current >= this.maximum);
	this.root.firstChild.disabled = full;
	return full;
}

