
var $get = function(id) {
	return document.getElementById(id) || false;
};

var $arrayize = function(args) {
	var len = args.length, array = [];
	array.length = len;
	while (len--) {
		array[len] = args[len];
	}
	return array;
};

/**
 * @author Douglas Crockford
 */
var $purge = function(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;
            }
        }
    }
    a = d.childNodes;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            arguments.callee(d.childNodes[i]);
        }
    }
};

if (!Function.prototype.bind) {
	Function.prototype.bind = function(context) {
		var fn = this, args = $arrayize(arguments);
		args.shift();
		return function() {
			return fn.apply(context, args.concat($arrayize(arguments)));
		};
	};
}

if (!Array.prototype.each) {
	Array.prototype.each = function(fn, context) {
		if (typeof fn !== 'function') {
			throw new TypeError();
		}
		if (context) {
			fn = fn.bind(context);
		}
		for (var i = 0, j = this.length; i < j; i++) {
			fn(this[i], i, this);
		}
		return this;
	};
}

if (!String.prototype.trim) {
	String.prototype.trim = function() {
		return this.replace(/^\s+|\s+$/g, '');
	};
}

if (!Date.prototype.toUnixTimestamp) {
	Date.prototype.toUnixTimestamp = function() {
		return Math.floor(parseInt(this.getTime(), 10) / 1000);
	};
}

if (!Date.prototype.precedes) {
	Date.prototype.precedes = function(other) {
		if (other.constructor !== Date) {
			throw new TypeError(alert_msg500);
		}
		return this.toUnixTimestamp() < other.toUnixTimestamp();
	};
}

var Tool = Tool || {};

Tool.redirect = function(url) {
	window.location.replace(url);
	return false;
};

Tool.ValueSpec = function(message, check) {
	this.message = message;
	if (typeof check !== 'function') {
		throw new TypeError();
	}
	this.check = function(value) {
		return check(value) ? true : false;
	};
};

Tool.ValueSpec.prototype = {
	getErrorMessage: function(fieldName) {
		return this.message.replace(/(\%s)/, fieldName);
	}
};

Tool.valueSpecs = {
	notBlank: new Tool.ValueSpec(alert_msg510, function(value) {
		if (!arguments.length) {
			return false;
		}
		if (typeof value === 'string') {
			return value.trim().length !== 0;
		}
		return value ? true : false;
	}),
	alpha: new Tool.ValueSpec(alert_msg520, function(value) {
		return /[^a-z]/i.test(value.trim()) === false;
	}),
	alphaNum: new Tool.ValueSpec(alert_msg530, function(value) {
		return /[^a-z0-9]/i.test(value.trim()) === false;
	}),
	integer: new Tool.ValueSpec(alert_msg540, function(value) {
		var v = value.trim(), int = parseInt(v, 10);
		if (isNaN(int)) {
			return false;
		}
		return int === Math.floor(v);
	}),
	email: new Tool.ValueSpec(alert_msg550, function(b) {
		if(b.trim().length == 0) return true;	// by nixor 02/11/09
		return /[a-z0-9\.\_\%\+\-]+@[a-z0-9\.\-]+\.[a-z]{2,4}/i.test(b.trim());
	}),
	// @author Greg Jorgensen
	ccNum: new Tool.ValueSpec(alert_msg560, function(value) {
		var v = value.trim();
		var doubled = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9];
		var digits = v.replace(/[^\d]/g, '').split('');
		var d, alt = false, total = 0;
		while (digits.length) {
			d = Number(digits.pop());
			total += (alt ? doubled[d] : d);
			alt = !alt;
		}
		return total % 10 === 0;
	}),
	cvv: new Tool.ValueSpec(alert_msg570, function(value) {
		return /[0-9]{3}/.test(value.trim()) === true;
	}),
	zip: new Tool.ValueSpec(alert_msg571, function(value) {
		return /^[0-9]{5}$/.test(value.trim());
	}),
	oneDigit: new Tool.ValueSpec(alert_msg580, function(value) {
		return /[0-9]/.test(value.trim());
	}),
	oneLetter: new Tool.ValueSpec(alert_msg585, function(value) {			   //one or more letters must
		return /[a-zA-Z]/.test(value.trim());
	}),
	oneUpperLetter: new Tool.ValueSpec(alert_msg590, function(value) {
		return /[A-Z]/.test(value.trim());
	}),
	oneLowLetter: new Tool.ValueSpec(alert_msg600, function(value) {
		return /[a-z]/.test(value.trim());
	}),
	noSpace: new Tool.ValueSpec(alert_msg610, function(value) {
		return /[\s]/.test(value) === false;
	}),
	letterFirst: new Tool.ValueSpec(alert_msg620, function(value) {
		return /^[a-zA-Z]+/.test(value.trim());
	}),
	noDigits: new Tool.ValueSpec(alert_msg630, function(value) {
		return /[0-9]/.test(value.trim()) === false;
	})
};

Tool.FormInput = function(id, name) {
	this.id = id || null;
	this.specs = [];
	this.forceValid = false;
	this.name = name || 'Field';
	return this;
};

Tool.FormInput.prototype = {
	isPassword: function() {
		return /^password/.test(this.getElement().type);
	},
	isText: function() {
		return this.isPassword() || /^text/.test(this.getElement().type);
	},
	getElement: function() {
		return $get(this.id) || false;
	},
	getValue: function() {
		var i, vals, el = this.getElement();
		if (el) {
			switch (el.type) {
				case 'text':
				case 'textarea':
				case 'password':
				case 'hidden': 
					return el.value;

				case 'radio':
					for (i = 0; i < el.length; i++) {
						if (el[i].checked) {
							return el.value;
						}
					}
					return null;

				case 'checkbox':
					return el.checked;

				case 'select-one':
					i = el.options.selectedIndex;
					return el.options[i].value;// || el.options[i].text;

				case 'select-multiple':
					vals = [];
					el.options.each(function(opt) {
						if (opt.selected) {
							vals.push(opt.value || opt.text);
						}
					});
					return vals;
			}
		}
		return null;
	},
	addSpec: function(spec) {
		if (spec.check) {
			if (typeof spec.check === 'function') {
				this.specs.push(spec);
			}
		}
		return this;
	},
	isValid: function() {
		var valid = true, errorMessages = [], yud = YAHOO.util.Dom;
		if (!this.forceValid) {
			this.specs.each(function(spec) {
				if (!spec.check(this.getValue())) {
					errorMessages.push(spec.getErrorMessage(this.name));
					valid = false;
				}
			}, this);
			this.getErrorMessages = function() {
				return errorMessages;
			};
		}
		if (!valid && !this.forceValid) {
			this.paintError();
		} else {
			this.unpaintError();
		}
		return valid;
	},
	paintError: function() {
		if(this.id=="recaptcha_response_field") {
			$get(this.id).style.border="3px solid #b1240f";
		}
		else
		YAHOO.util.Dom.addClass(this.getElement(), 'error');
		return this;
	},
	unpaintError: function() {
		if(this.id=="recaptcha_response_field") {
			$get(this.id).style.border="1px solid gray";
		}
		else
		YAHOO.util.Dom.removeClass(this.getElement(), 'error');
		return this;
	},
	addEvent: function(type, callback, context) {
		var el = this.getElement();
		if (el) {
			YAHOO.util.Event.addListener(el, type, callback, context || this);
		}
		return this;
	},
	removeEvents: function() {
		var el = this.getElement();
		if (el) {
			YAHOO.util.Event.purgeElement(el);
		}
		return this;
	},
	getErrorMessages: function() {
		return [];
	},
	getValueObject: function() {
		var obj = {};
		obj[this.id] = this.getValue();
		return obj;
	},
	serialize: function() {
		return YAHOO.lang.JSON.stringify(this.getValueObject());
	},
	isCompound: function() {
		return false;
	}
};

Tool.SelectBox = function(id, name) {
	this.id = id || null;
	this.specs = [];
	this.name = name || 'Field';
};

Tool.SelectBox.prototype = new Tool.FormInput();

Tool.SelectBox.prototype.choose = function(idx) {
	var i, attr, opt, el = this.getElement();
	idx += '';
	for (i = 0; i < el.options.length; i++) {
		opt = el.options[i];
		attr = opt.value || opt.text;
		opt.selected = attr === idx;
	}
	return this;
};

Tool.SelectBox.prototype.setOptions = function(opts) {
	var el = this.getElement();
	el.innerHTML = '';
	if (opts.constructor === String) {
		return arguments.callee([opts]);
	} else if (opts.constructor === Array){
		opts.each(function(opt) {
			el.appendChild(opt);
		}, this);
	} else {
		throw new TypeError();
	}
	return this;
};

Tool.CompoundSelectBox = function(selects, name) {
	this.specs = [];
	this.selects = selects && selects.constructor === Array ? selects : [];
	this.id = this.selects.length ? this.selects[0].getElement().id: false;
	this.name = name || 'Fields';
};

Tool.CompoundSelectBox.prototype = {
	setId: function(id) {
		this.id = id;
		return this;
	},
	addSpec: Tool.FormInput.prototype.addSpec,
	isValid: Tool.FormInput.prototype.isValid,
	getErrorMessages: Tool.FormInput.prototype.getErrorMessages,
	getValueObject: Tool.FormInput.prototype.getValueObject,
	serialize: Tool.FormInput.prototype.serialize,
	setEvaluator: function(evaluator) {
		if (typeof evaluator === 'function') {
			this.evaluator = evaluator;
		}
		return this;
	},
	getValue: function() {
		if (!this.evaluator) {
			throw new Error('Missing evaluator callback');
		}
		return this.evaluator(this.selects);
	},
	getValueObject: function() {
		var obj = {}, value = this.getValue();
		if (value && value.constructor === Date) {
			obj[this.id] = this.getValue();
		}
		return obj;
	},
	paintError: function() {
		this.selects.each(function(selekt) {
			selekt.paintError();
		});
		return this;
	},
	unpaintError: function() {
		this.selects.each(function(selekt) {
			selekt.unpaintError();
		});
		return this;
	},
	addEvent: function(type, callback) {
		this.selects.each(function(selekt) {
			YAHOO.util.Event.addListener(selekt.id, type, callback);
		});
	},
	removeEvents: function() {
		return this;
	},
	isText: function() {
		return false;
	},
	isCompound: function() {
		return true;
	}
};

Tool.RadioGroup = function(radios, name) {
	this.specs = [];
	this.radios = radios && radios.constructor === Array ? radios : [];
	this.id = this.radios.length ? this.radios[0].getElement().name : false;
	this.name = name || 'Field';
};

Tool.RadioGroup.prototype = new Tool.FormInput();

Tool.RadioGroup.prototype.getElements = function() {
	var els = [];
	this.radios.each(function(radio) {
		els.push(radio.getElement());
	});
	return els;
};

Tool.RadioGroup.prototype.getElement = Tool.RadioGroup.prototype.getElements;

Tool.RadioGroup.prototype.getValue = function() {
	var value = null;
	this.radios.each(function(radio) {
		if (radio.getElement().checked) {
			value = radio.getElement().value;
			return;
		}
	});
	return value;
};

Tool.RadioGroup.prototype.serialize = function() {
	var name, serializable = {};
	if (this.radios.length) {
		name = this.radios[0].getElement().name;
		serializable[name] = this.getValue();
	}
	return YAHOO.lang.JSON.stringify(serializable);
};

Tool.RadioGroup.prototype.addEvent = function() {
	return this;
};

Tool.RadioGroup.prototype.removeEvents = function() {
	return this;
};

Tool.FormMessage = function(id, text) {
	this.show = function() {
		var el = $get(id);
		if (el) {
			el.innerHTML = text || '&nbsp;';
		}
		return this;
	};
	this.hide = function() {
		var el = $get(id);
		if (el) {
			el.innerHTML = '&nbsp;';
		}
		return this;
	};
};

Tool.Form = function() {
	this.fields = {};
};

Tool.Form.prototype = {

	addField: function(field) {
		if (field) {
			if (field.input) {
				this.fields[field.input.id] = field;
			}
		}
		return this;
	},
	validator: function() {
		var p, f, valid = true;
		for (p in this.fields) {
			f = this.fields[p];
			if(!f.isValid()) {
				if(valid == true) { $get(f.input.id).focus(); }
				valid = false;
			}
		}
		
		return valid;
	},
	setValidator: function(validator) {
		if (typeof validator !== 'function') {
			throw new TypeError(alert_msg640);
		}
		this.validator = validator.bind(this);
		return this;
	},
	isValid: function() {
		return this.validator() ? true : false;
	},
	serialize: function() {
		var p, f, serializable = {};
		for (p in this.fields) {
			f = this.fields[p];
			if (f && f.input && f.input.getValue) {
				if (typeof f.input.getValue === 'function') {
					serializable[p] = f.input.getValue();
				}
			}
		}
		return YAHOO.lang.JSON.stringify(serializable);
	},
	setSubmitButton: function(id) {
		var dis = this;
		var el = $get(id), y = YAHOO.util.Event;
		if (el) {
			if (this.submitBtn) {
				y.purgeElement(this.submitBtn);
			}
			this.submitBtn = el;
			y.addListener(el, 'click', this.submit.bind(this));
			y.addListener(el, 'keypress', this.submit.bind(this));
		}
		return this;
	},

	setHandler: function(handler) {
		if (typeof handler === 'function') {
			this.handler = handler;
		}
		return this;
	},

	submit: function(evt) {
		if (evt) {
			if (evt.preventDefault) {
				evt.preventDefault();
				evt.stopPropagation();
			}
			if (evt.type === 'keypress') {
				if (evt.which !== 13 && evt.which !== 32) {
					return this;
				}
			}
		} else {
			console.log('No event!');
		}
		if (this.handler) {
			this.handler();
		} else {
			console.log('Form is not registered; cannot submit form');
		}
		return this;
	}
};

Tool.Form.Field = function(input, remote, tip) {
	this.remote = remote ? true : false;
	this.cold = true;
	this.input = input;
	this.tip = tip instanceof Tool.Form.Tip ? tip : false;
	var dis = this;
	var timeout;
	var lastvalue = this.input.getValue();

	if (input.isText()) {
		input.addEvent('keyup', function(evt) {
			if (!dis.cold) {
				if (dis.input.getValue() !== lastvalue) {
					lastvalue = dis.input.getValue();
					if (timeout) clearTimeout(timeout);
					timeout = setTimeout(function() {
						dis.input.forceValid = false;
						dis.isValid();
					}, 300);
				}
			}
		});
		input.addEvent('change', function(evt) {
			lastvalue = dis.input.getValue();
		});
	}

	var setCaretPosition = function(id, pos) {
		var range, el = $get(id);
		if (el) {
			if (el.createTextRange) {
				range = el.createTextRange();
				range.move('character', pos);
				range.select();
			}
			else {
				if (el.selectionStart) {
					el.setSelectionRange(pos, pos);
				}
				el.focus();
			}
		}
		return false;
	};

	if (!input.isCompound()) {
		input.addEvent('change', function(evt) {
			dis.input.forceValid = false;
			dis.thaw().isValid();
		});
		input.addEvent('focus', function(evt) {
			v = dis.input.getValue();
			if (v) {
				dis.thaw();
				if (dis.input.isText()) {
					setCaretPosition(dis.input.id, v.length);
				}
			}
			if (dis.tip) {
				dis.tip.enable().show();
			}
		});
		input.addEvent('blur', function(evt) {
			dis.isValid();
			if (dis.tip) {
				dis.tip.hide().disable();
			}
		});
	}

	// Disable CR on Safari
	if (/safari/i.test(navigator.userAgent)) {
		YAHOO.util.Event.addListener(input.id, 'keypress', function(evt) {
			if (evt.which && evt.which === 13) {
				evt.preventDefault();
			}
		});
	}

	return this;
};

Tool.Form.Field.prototype = {
	thaw: function() {
		if (!this.input.isText()) {
			return this;
		}
		var yue = YAHOO.util.Event, el = this.input.getElement();
		this.cold = false;
		if (el) {
			yue.removeListener(el, 'change');
		}
		return this;
	},
	isValid: function(evt) {
		var valid = this.input.isValid();

		if (this.tip) {
			if (valid) {
				this.tip.hide().revert();
			} else {
				this.tip.setErrors(this.input.getErrorMessages()).show();
			}
		}

		return valid;
	}
};

Tool.Form.Tip = function(tipNodeId) {
	if (!tipNodeId) {
		return this;
	}
	var yud = YAHOO.util.Dom;
	var enabled = true;
	var el = $get(tipNodeId);
	if (!el) {
		return this;
	}
	var textNode = yud.getFirstChild(el);
	var effect = new YAHOO.util.Anim(el, {
		opacity: {
			from: 0,
			to: 0.99999
		}
	}, 0.3);
	var defaultText = textNode.innerHTML;
	var visible = false;
	this.setErrors = function(errorText) {
		if (errorText) {
			if (typeof errorText === 'string') {
				return arguments.callee([errorText]);
			}
			var html = ['<ul>'];
			errorText.each(function(text) {
				html.push('<li>' + (text || '&nbsp;') + '</li>');
			});
			html.push('</ul>');
			textNode.innerHTML = html.join('\n');
//			textNode.innerHTML = errorText;
			yud.addClass(el, 'tip_error');
			yud.removeClass(el, 'tip');
		}
		return this;
	};
	this.revert = function() {
		yud.addClass(el, 'tip');
		yud.removeClass(el, 'tip_error');
		textNode.innerHTML = defaultText;
		return this;
	};
	this.show = function(evt) {
		if (enabled && !visible) {
			visible = true;
			yud.setStyle(el, 'display', 'inline');
			effect.animate();
		}
		return this;
	};
	this.hide = function(evt) {
		visible = false;
		effect.stop();
		yud.setStyle(el, 'display', 'none');
		yud.setStyle(el, 'opacity', 0);
		return this;
	};
	this.disable = function() {
		enabled = false;
		return this;
	};
	this.enable = function() {
		enabled = true;
		return this;
	};
	this.isVisible = function() {
		return visible;
	};

	// Initial state is hidden and disabled
	this.hide().disable();
};


Tool.date = {
	months: {
		long: [
			alert_msg650,
			alert_msg660,
			alert_msg670,
			alert_msg680,
			alert_msg690,
			alert_msg700,
			alert_msg710,
			alert_msg720,
			alert_msg730,
			alert_msg740,
			alert_msg750,
			alert_msg760
		],
		short: [
			alert_msg650,
			alert_msg660,
			alert_msg670,
			alert_msg680,
			alert_msg690,
			alert_msg700,
			alert_msg710,
			alert_msg720,
			alert_msg730,
			alert_msg740,
			alert_msg750,
			alert_msg760
		]
	}
};
