/**
 *
 * livevalidation.js
 *
 * @author     MT312
 * @copyright  MT312
 * @license    http://www.opensource.org/licenses/mit-license.php The MIT License
 * @version    CVS: $Id:$
 * @link       http://www.mt312.com/
 * @see        prototype.js 1.6 (http://www.prototypejs.org/)
 *
 */

var validator = {};
validator.config = {
	// このスクリプトを有効化
	enabled: true,
	// 初期フォーカス
	first_focus: true,
	// テキストエリアの伸縮
	resize_textarea: true,
	// エラーの位置をフォームの上
	error_pos: true,
	// エラーとフォームを包む要素名
	wrapper_element: 'td dd div'
};
validator.register = function(def_list) {
	if (this.config.enabled == false) {
		return false;
	}
	if (typeof Prototype != 'object') {
		alert('prototype.js is not found.');
		return false;
	}
	if (def_list.length == 0) {
		return false;
	}
	this.def_list = def_list;
	Event.observe(window, 'load', this.dispatch);
	return true;
};
validator.dispatch = function() {
	var self = validator;
	var def_list = self.def_list;
	var id = def_list[0].id;
	var first_el = $(id);
	if (first_el == null) return false;
	// nameだけで取得できるIEの対策
	if (first_el.id == '') return false;
	if (first_el.tagName == 'SPAN') {
		for (var i = 0; i < 10; ++i) {
			first_el = $(id + i);
			if (first_el != null) break;
		}
		if (first_el == null) return false;
	}

	if (self.config.first_focus) {
		first_el.focus();
	}
	var form = first_el.form;
	this.form = form;

	var parentBlockNode = function(el) {
		var wrapper = self.config.wrapper_element.toUpperCase();
		do {el = el.parentNode;}
		while (wrapper.indexOf(el.tagName) == -1);
		return el;
	};

	form.onmousedown = function(ev) {
		ev = ev || window.event;
		this.ev_el = Event.element(ev);
		return true;
	};
	form.onkeydown = function(ev) {
		ev = ev || window.event;
		this.ev_el = Event.element(ev);
		if (ev.keyCode == 13) {
			var el = this.ev_el;
			if (el.tagName == 'TEXTAREA') return true;
			if (el.tagName == 'INPUT' && el.type == 'reset') return true;
			var elems = Form.getElements(this);
			var i = elems.indexOf(el);
			if (i > -1 && i < elems.length - 1) {
				var next_el = elems[i + 1];
				next_el.focus();
				this.next_el = next_el;
			}
		}
		return true;
	};
	form.onsubmit = function(ev) {
		if (this.ev_el.tagName == 'INPUT' && this.ev_el.type == 'submit') {
			var focused = false;
			el_list.each(function(el) {
				if (!el.validate(ev) && !focused) {
					Element.scrollTo(parentBlockNode(el));
					if (el.tagName == 'SPAN') {
						el = el.inner_nodes[0];
					}
					el.focus();
					focused = true;
				}
			});
			return focused == false;
		}
		// IEでボタンがフォーカスされる対策
		if ('next_el' in this) {
			this.next_el.focus();
		}
		return false;
	};
	form.onreset = function() {
		if (this.ev_el.tagName == 'INPUT' && this.ev_el.type == 'reset') {
			el_list.each(function(el) {
				var err_el = $(el.id + '_error');
				var has_err = err_el != null && err_el.tagName == 'EM';
				if (has_err) {
					Element.remove(err_el);
					Element.removeClassName(el, 'error');
					Element.removeClassName(el, 'done');
				}
			});
			Element.scrollTo(this);
			first_el.focus();
			return true;
		}
		return false;
	};

	// 有効なエレメントのリスト
	var el_list = [];
	def_list.each(function(def) {
		if (typeof def.id != 'string') return;
		var el = $(def.id);
		if (el == null) return;
		if (el.tagName == 'SPAN') {
			var name = el.id;
			var type = el.className;
			var def_type = type.charAt(0).toUpperCase();
			if (def_type == 'C') name += '[]';
			var inner_nodes = Form.getInputs(form, type, name);
			if (inner_nodes.length == 0) return;
			el.inner_nodes = inner_nodes;
			el.form = form;
		} else if (el.tagName == 'SELECT' && el.multiple) {
			var def_type = 'M';
			el.inner_nodes = $A(el.options);
		} else {
			def_type = el.tagName.charAt(0);
			// 自動リサイズ
			if (el.tagName == 'TEXTAREA' && validator.config.resize_textarea) {
				el.onfocus = resizeTextarea;
			}
		}
		var class_name = def_type + '_Define';
		if (typeof window[class_name] != 'function') return;

		var define = new window[class_name]();
		Object.extend(define, def);
		el.def = def;
		el.define = define;
		el.define.el = el;
		el.validate = function(ev) {
			ev = ev || window.event;
			// 9:TAB 13:Enter
			if (ev.keyCode == 9) return true;
			if (ev.keyCode == 13 && el.tagName != 'TEXTAREA') return true;
			var err_el = $(el.id + '_error');
			var has_err = err_el != null && err_el.tagName == 'EM';
			for (var prop in def) {
				if (define[prop] == null) continue;
				var method_name = prop + '_check';
				if (typeof define[method_name] != 'function') continue;
				var result = define[method_name]();
				if (result === null) break;
				if (result === true) continue;
				var msg = define[prop + '_error'] + '。';
				msg = msg.replace('{form}', define.name);
				msg = msg.replace('%d', define[prop]);
				// エラー要素がなければ作成
				if (!has_err) {
					err_el = document.createElement('em');
					err_el.id = el.id + '_error';
					if (validator.config.error_pos) {
						var p_node = parentBlockNode(this);
						var first_el = p_node.firstChild;
						// 他のエラーがあれば最後尾まで移動
						while (typeof first_el.tagName == 'string' && first_el.tagName == 'EM') {
							first_el = first_el.nextSibling;
						}
						p_node.insertBefore(err_el, first_el);
					} else {
						var p_node = parentBlockNode(this);
						p_node.appendChild(err_el);
					}
				}
				err_el.className = 'error';
				err_el.innerHTML = msg;
				Element.removeClassName(el, 'done');
				Element.addClassName(el, 'error');
				return false;
			}
			if (has_err) {
				err_el.className = 'done';
				err_el.innerHTML = 'Good!';
				Element.removeClassName(el, 'error');
				Element.addClassName(el, 'done');
			}
			return true;
		};

		if (el.tagName == 'SPAN') {
			el.inner_nodes.each(function(node) {
				Event.observe(node, 'click', function(ev) {return el.validate(ev);});
			});
		} else if (el.tagName == 'SELECT') {
			Event.observe(el, 'change', el.validate);
		} else {
			Event.observe(el, 'keyup', el.validate);
			// Event.observe(el, 'blur', el.validate);
		}
		el_list.push(el);
	});
	this.el_list = el_list;
	return true;
};
function Define()
{
	this.required = false;
	this.required_error = '{form}を入力して下さい';
	this.required_check = function() {
		var str = Convert.trim(this.el.value);
		if (this.required) return str != '';
		if (str == '') return null;
		return true;
	};
	this.min = 0;
	this.min_error = '{form}は%d文字以上にして下さい';
	this.min_check = function() {
		return this.el.value.length >= this.min;
	};
	this.max = 3000;
	this.max_error = '{form}は%d文字以下にして下さい';
	this.max_check = function() {
		return this.el.value.length <= this.max;
	};
}
function T_Define()
{
	this.regexp_list = {
		mailaddress: /^([a-z\d_]|\-|\.|\+)+@(([a-z\d_]|\-)+\.)+[a-z]{2,6}$/i,
		url: /^(https?|ftp):\/\/\S+$/,
		alphabet: /^[a-z]+$/i,
		alphanum: /^[a-z\d]+$/i,
		number: /^\d+$/,
		integer: /^[1-9]\d*$/,
		zipcode: /^\d{3}-\d{4}$/,
		zipcode_d: /^\d{7}$/,
		phone:   /^0[1-9]\d{0,4}-(\d{1,4}-)?\d{4}$/,
		phone_d: /^0[1-9]\d{8,9}$/,
		mbphone:   /^0[7-9]0-\d{4}-\d{4}$/,
		mbphone_d: /^0[7-9]0\d{8}$/,
		katakana: /^[ァ-ン　 ー]+$/,
		hiragana: /^[ぁ-ん　 ー]+$/
	};
	this.regexp = null;
	this.regexp_error = '{form}が正しくありません';
	this.regexp_check = function() {
		var regexp = this.regexp;
		if (regexp in this.regexp_list) {
			var str = Convert.trim(this.el.value);
			if (regexp == 'hiragana') {
				str = Convert.hiragana(str);
			} else if (regexp == 'katakana') {
				str = Convert.katakana(str);
			} else {
				str = Convert.zentohan(str);
			}
			return this.regexp_list[regexp].test(str);
		}
		return true;
	};
	this.repeat = false;
	this.repeat_error = '{form}が正しくありません';
	this.repeat_check = function() {
		var elems = Form.getElements(this.el.form);
		var n = elems.indexOf(this.el);
		return n > 0 && (elems[n - 1].value == this.el.value);
	};
}
function S_Define()
{
	this.required_error = '{form}を選択して下さい';
}
function R_Define()
{
	this.required_error = '{form}を選択して下さい';
	this.required_check = function() {
		var checked = false;
		this.el.inner_nodes.each(function(node) {
			if (node.checked) {
				if (node.value == '') {
					node.checked = false;
					return;
				}
				checked = true;
				return;
			}
		});
		if (this.required) return checked;
		if (!checked) return null;
		return true;
	};
	this.min_check = function() {
		var length = 0;
		this.el.inner_nodes.each(function(node) {
			if (node.checked) {
				length = node.value.length;
				return;
			}
		});
		return length >= this.min;
	};
	this.max_check = function() {
		var length = 0;
		this.el.inner_nodes.each(function(node) {
			if (node.checked) {
				length = node.value.length;
				return;
			}
		});
		return length <= this.max;
	};
}
function C_Define()
{
	this.check = 'checked';

	this.required_error = '{form}を選択して下さい';
	this.required_check = function() {
		var exists = false;
		var ck = this.check;
		this.el.inner_nodes.each(function(node) {
			if (node[ck]) {
				// $F(node)は<option>には使えない
				if (node.value == '') {
					node[ck] = false;
					return;
				}
				exists = true;
				return;
			}
		});
		if (this.required) return exists;
		if (!exists) return null;
		return true;
	};
	this.min_check = function() {
		var ret = true;
		var min = this.min;
		var ck = this.check;
		this.el.inner_nodes.each(function(node) {
			if (node[ck] && node.value.length < min) {
				ret = false;
				return;
			}
		});
		return ret;
	};
	this.max_check = function() {
		var ret = true;
		var max = this.max;
		var ck = this.check;
		this.el.inner_nodes.each(function(node) {
			if (node[ck] && node.value.length > max) {
				ret = false;
				return;
			}
		});
		return ret;
	};
	this.checks_min_error = '{form}は%dヶ所以上選択して下さい';
	this.checks_min_check = function() {
		if (this.checks_min < 2) return true;
		var checks = 0;
		var ck = this.check;
		this.el.inner_nodes.each(function(node) {
			if (node[ck]) ++checks;
		});
		return checks >= this.checks_min;
	};
	this.checks_max_error = '{form}は%dヶ所以下選択して下さい';
	this.checks_max_check = function() {
		if (this.checks_max < 2) return true;
		var checks = 0;
		var ck = this.check;
		this.el.inner_nodes.each(function(node) {
			if (node[ck]) ++checks;
		});
		return checks <= this.checks_max;
	};
}
function M_Define()
{
	this.check = 'selected';
}
function F_Define()
{
	this.required_error = '{form}を選択して下さい';
	this.min = null;
	this.min_check = null;
	this.max = null;
	this.max_check = null;
}
I_Define = T_Define;
I_Define.prototype = new Define();
T_Define.prototype = new Define();
R_Define.prototype = new Define();
S_Define.prototype = new Define();
C_Define.prototype = new Define();
F_Define.prototype = new Define();
M_Define.prototype = new C_Define();
var Convert = {
	trim: function(str) {
		// str = str.replace(/^[\s　]+|[\s　]+$/, '');
		str = str.replace(/^[\s　]+/, '');
		str = str.replace(/[\s　]+$/, '');
		return str;
	},
	zentohan: function(str) {
		var zen = '！”＃＄％＆’（）＝＾～／＊－＋．，？＿＠；：　';
		var han = '!"#$%&\'()=^~/*-+.,?_@;: ';
		var reg = new RegExp('[' + zen + ']', 'g');
		str = str.replace(reg, function($0) {
			return han.charAt(zen.indexOf($0));
		});
		return str.replace(/[Ａ-Ｚａ-ｚ０-９]/g, function($0) {
			return String.fromCharCode($0.charCodeAt(0) - 0xFEE0);
		});
	},
	hiragana: function(str) {
		return str.replace(/[ァ-ン]/g, function($0) {
			return String.fromCharCode($0.charCodeAt(0) - 0x0060);
		});
	},
	katakana: function(str) {
		return str.replace(/[ぁ-ん]/g, function($0) {
			return String.fromCharCode($0.charCodeAt(0) + 0x0060);
		});
	}
};
function resizeTextarea()
{
	var min_rows = this.rows;
	var max_rows = min_rows * 3;
	var resize = function() {
		var match = this.value.match(/\r\n?|\n/g);
		var lines = match == null ? 1 : match.length + 2;
		this.rows = Math.max(min_rows, Math.min(lines, max_rows));
	};
	Event.observe(this, 'focus', resize);
	Event.observe(this, 'keyup', resize);
	Event.observe(this, 'mouseup', resize);
}
function checksAll(id, checked)
{
	var el = $(id);
	if (el != null && typeof el.inner_nodes != 'undefined') {
		el.inner_nodes.each(function(node) {
			node.checked = Boolean(checked);
		});
	}
}

