/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * LiveValidation プラグインのPluginクラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.LiveValidationPlugin")
	.extend("maskat.core.Plugin", {
	
	_static: {
		initialize: function() {
			maskat.core.Plugin.register(this);
		}
	},
	
	/**
	 * プラグイン識別子を返します。
	 * 
	 * @return プラグイン識別子
	 */
	getPluginId: function() {
		return "livevalidation";
	},

	/**
	 * プラグインのバージョン識別子を返します。
	 * 
	 * @return プラグインのバージョン識別子
	 */
	getVersion: function() {
		return "2.2.1.v20100928";
	},

	/**
	 * プラグインのロードが完了したかどうかを返します。
	 * 
	 * このメソッドが true を返すまでマスカットアプリケーションの実行は
	 * 開始しません。
	 * 
	 * @return ロードが完了していれば true、それ以外の場合は false
	 */
	isLoaded: function() {
		return this.loaded;
	},
	
	/**
	 * プラグインに必要なリソースをロードします。
	 * 
	 * @param app このプラグインを使用するマスカットアプリケーション
	 */
	load: function() {
		maskat.app.loadJavaScript(
			maskat.location + "livevalidation/livevalidation_standalone.js", false);
		this.setupLoadValidationCommand();
		this.loaded = true;
	},
	
	/**
	 * LiveValidation プラグインの実行を開始します。
	 */
	start: function() {
	
		// LiveValidation用のCSS定義を優先させるため、
		// start 内でスタイルシートをロードする。
		maskat.app.loadStyleSheet(
			maskat.location + "livevalidation/css/livevalidation_plugin.css");
	
		// LiveValidationライブラリのロード後に派生クラスへの継承処理を行う。
		var baseValidator =
			maskat.validator.livevalidation.custom.CustomLiveValidation;
		baseValidator.inherit(LiveValidation, baseValidator.properties); 
		
		// LiveValidation プラグインのメッセージ定義をロードする。
		maskat.util.Message.loadTemplates(
			maskat.location + "livevalidation/messages.json");
	},
	
	/**
	 * 画面遷移定義XMLでloadValidationタグを使用するための設定を行います。
	 */
	setupLoadValidationCommand: function() {
		var tagName = "loadValidation";
		var child = { property: "commands", repeat: true };
		var element = {
			type: maskat.validator.livevalidation.control.LoadValidationCommand,
			attributes: {
				xmlFile: {	type: "string",	required: true,	property: "url"	}
			}
		};

		var reader = maskat.control.TransitionXMLReader.getInstance(); 
		reader.addElementBinding("", tagName, element);
		
		var init = reader.bindings[""]["init"];
		init.addChildBinding(tagName, child);
		
		var transition = reader.bindings[""]["transition"];
		transition.addChildBinding(tagName, child);
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 画面遷移定義XMLのloadValidationタグの処理を実行するCommandクラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.control.LoadValidationCommand")
	.extend("maskat.control.Command", {
	
	/**
	 * このクラスのコンストラクタです。
	 */
	initialize: function() {
		this.url = null;
	},
	
	/**
	 * 設定ファイルのロード処理を呼び出します。
	 *
	 * @param {maskat.core.Application} app
	 *             このクラスを利用するマスカットアプリケーション
	 */
	execute: function(app) {
		maskat.validator.livevalidation.xml
			.ValidationXMLReader.getInstance().load(this.url); 
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * エラーメッセージを一括表示エリアに出力するLiveValidationクラスです。
 *
 * 本クラスを利用する際にはバリデーション定義XMLにメッセージ出力先を
 * 定義する必要があります。
 */ 
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.custom.BulkMessageLiveValidation")
	.extend("maskat.validator.livevalidation.custom.CustomLiveValidation", {
	
	_static: {
	
		/**
		 * メッセージ格納用DOMノードのIDのプレフィックス。
		 */
		MESSAGE_CONTAINER_ID_PREFIX: 'LiveValidationPlugin_bulkMessageContainer_',
		
		/**
		 * 複数項目の入力値の一括検証を行います。
		 * 
		 * 本クラスでは、メッセージエリアから全てのメッセージをいったん削除し、
		 * 検証エラーとなった項目のエラーメッセージのみ表示させます。
		 *
		 * @param {Array} validations
		 *         検証対象となるLiveValidationオブジェクトの配列
		 * @return 検証結果
		 * @type boolean
		 */
		massValidate : function (validations) {
			var returnValue = true;
			var messageContainers = {};
			for (var i=0 , length = validations.length ; i < length ; i++) {
				// 重複で処理をすることを回避するため、一時的にハッシュマップに退避
				var messageContainerNode = validations[i].messageContainerNode;
				if (messageContainerNode && messageContainerNode.id) {
					messageContainers[messageContainerNode.id] = messageContainerNode;
				}
			}
			
			for (var id in messageContainers) {
				var msgContainer = messageContainers[id];
				var deleteElementArray = [];
				
				for(var i=0, length=msgContainer.childNodes.length; i<length; i++){
					if (msgContainer.childNodes[i].className 
						&& msgContainer.childNodes[i].className.indexOf("LV_validation_message") != -1 ){
						deleteElementArray.push(msgContainer.childNodes[i]);
					}
				}
				for (var i = 0 , length = deleteElementArray.length ; i < length ; i++) {
					msgContainer.removeChild( deleteElementArray[i]);
				}
				
			}
			
			try {
				var returnValue = LiveValidation.massValidate(validations);
				return returnValue;
			} catch(e) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_MASSVALIDATE_ERROR", {}, e);
			}
		}
		
	},

	/**
	 * 適用するスタイルシートのクラス名を設定します。
	 */
	setStyleClass: function() {
		this.validClass = 'LV_valid_bulk';
	    this.invalidClass = 'LV_invalid_bulk';
	    this.messageClass = 'LV_validation_message_bulk';
	    this.validFieldClass = 'LV_valid_field_bulk';
	    this.invalidFieldClass = 'LV_invalid_field_bulk';
		this.messageContainerNodeClass = 'bulkMessageContainerNode';
	},
	
	/**
	 * LiveValidation の動作オプションを設定します。
	 *
	 * @param {Object} optionObj LiveValidationの動作オプション
	 *                             (省略可能　default:{})
	 */
	setLvOptions: function(optionsObj) {
		var options = optionsObj || {};
		this.validMessage = options.validMessage || 'Thankyou!';

		// 一括メッセージ出力先
		var params = {};
		if (this.layout && optionsObj.messageAreaLayoutName) {
			// マスカットの場合
			var layoutName = optionsObj.messageAreaLayoutName;
			var componentName = optionsObj.messageAreaComponentName;
			if (layoutName == "") {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_MISSING_LAYOUT_ID");
			}
			if (!componentName || componentName == "") {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_MISSING_WIDGET_ID");
			}
			params["layoutName"] = layoutName;
			params["componentName"] = componentName;
		
		} else if (optionsObj.messageAreaElementId) {
			// HTMLに対して適用した場合。
			var elementId = optionsObj.messageAreaElementId;
			if (!elementId || elementId == "") {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_MISSING_ELMENT_ID");
			}
			params["elementId"] = elementId;
		
		} else {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_BULKMESSAGE_MISSING_MESSAGEAREA");
		
		}
		
		this.messageContainerNode = this.getMessageContainerNode(params);
		this.insertAfterWhatNode = this.getInsertAfterWhatNode(
			this.messageContainerNode); 
			
		this.onlyOnBlur =  options.onlyOnBlur || false;
		this.wait = options.wait || 500;
		this.onlyOnSubmit = options.onlyOnSubmit || false;
	},	

	/**
	 * メッセージ格納領域のDIV要素を取得します。
	 *
	 * 存在しない場合は新たに作成して返却します。
	 *
	 * @param {Object} params メッセージ出力先情報
	 * 			マスカット連携時:
	 *          {
	 *          	layoutName: メッセージ出力先レイアウト,
	 *              componentName: メッセージ出力先マスカット部品
	 *          }
	 
	 *          単体利用時:
	 *          {
	 *               elementId: メッセージ出力先HTML要素のID属性
	 *          }
	 * @return メッセージ出力先配下のメッセージ格納領域
	 * @type HTMLElement
	 */
	getMessageContainerNode: function(params) {

		var messageAreaNode;
		var messageContainerId;
		
		if (params.layoutName) {
			var layout = maskat.app.getLayout(params.layoutName);
			if (!layout || !(layout instanceof maskat.layout.Layout)) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_INVALID_LAYOUT_ID",
					{ layout: params.layoutName });
			}
		
			var widget = layout.getWidget(params.componentName);
			if (!widget || !(widget instanceof maskat.layout.Widget)) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_INVALID_WIDGET_ID",
					{ layout: params.layoutName, widget: params.componentName });
					
			}
			try{
				if (widget.widget.getHtmlExt) {
					messageAreaNode = widget.widget.getHtmlExt().firstChild;
				} else {
					messageAreaNode = widget.widget.domNode;
				}
			} catch(e){
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_INVALID_MESSAGEAREA_MASKAT",
					{ layout: params.layoutName, widget: params.componentName });
			}
			messageContainerId = 
				maskat.validator.livevalidation.custom.BulkMessageLiveValidation.
					MESSAGE_CONTAINER_ID_PREFIX + params.layoutName + '_'
					+ params.componentName;
		} else if (params.elementId) {
			messageAreaNode = document.getElementById(params.elementId)
			messageContainerId = maskat.validator.livevalidation.custom
				.BulkMessageLiveValidation.MESSAGE_CONTAINER_ID_PREFIX
				+ params.elementId;
		}
		
		if (!messageAreaNode ) {
			if (params.layoutName) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_INVALID_MESSAGEAREA_MASKAT",
					{ layout: params.layoutName, widget: params.componentName });
			} else if (params.elementId) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_BULKMESSAGE_INVALID_MESSAGEAREA_HTML",
					{ id: params.elementId }); 
			}
		}
		
		// メッセージ格納領域を取得
		var containerNode = document.getElementById(messageContainerId);
		if (!containerNode) {
			containerNode = document.createElement('div');
			containerNode.setAttribute('id', messageContainerId);
			
			//ContainerNodeにClassNameを追加
			containerNode.className = this.messageContainerNodeClass ;
			
			try{
				messageAreaNode.appendChild(containerNode);
			} catch(e){
			
				if (params.layoutName) {
					throw new maskat.lang.Error(
						"LIVEVALIDATION_BULKMESSAGE_CREATE_MESSAGE_CONTAINER_ERROR_MASKAT",
						{ layout: params.layoutName, widget: params.componentName });
				} else if (params.elementId) {
					throw new maskat.lang.Error(
						"LIVEVALIDATION_BULKMESSAGE_CREATE_MESSAGE_CONTAINER_ERROR_HTML",
						{ id: params.elementId }); 
				}
			}
		}
		
		return containerNode;
	},
	
	/**
	 * メッセージ追加位置を示すノードを取得します。
	 *
	 * @param {HTMLElement} messageContainerNode メッセージ格納領域
	 * @return メッセージ格納領域に追加されたメッセージ追加位置を示すノード
	 * @type HTMLElement 
	 */
	getInsertAfterWhatNode: function(messageContainerNode) {
		var returnNode = document.createElement('div');
		messageContainerNode.appendChild(returnNode);
		
		return returnNode;
	},

	/**
	 * メッセージ領域を生成します。
	 *
	 * @return エラーメッセージをセットしたメッセージ領域
	 * @type HTMLElement
	 */
	createMessageArea: function() {
		var div = document.createElement('div');
		div.innerHTML = this.getMessage();
		return div;
	},
	
	/**
	 * フォーカスアウト時の処理を実行します。
	 */
	doOnBlur: function(e) {
		this.focused = false;
		this.validate(e);
	},
	
	/**
	 * フォーカスイン時の処理を実行します。
	 */
	doOnFocus: function(e) {
		this.focused = true;
		if (this.validationFailed) {
			this.validate(e);
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
/**
 * LiveValidation プラグイン用のカスタムLiveValidationクラスです。
 *
 * 独自拡張を行う場合は本クラスを継承してください。 
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.custom.CustomLiveValidation")
	.extend("LiveValidation", {
	
	_static: {
	
		/**
		 * 複数項目の入力値の一括検証を行います。
		 *
		 * @param {Array} validations
		 *         検証対象となるLiveValidationオブジェクトの配列
		 * @return 検証結果
		 * @type boolean
		 */
		massValidate: function (validations) {
			// massValidate は各クラスで実装すること。
			return LiveValidation.massValidate(validations);
		}
	},
	
	/**
	 * このクラスのコンストラクタです。
	 * 
	 * @param {HTMLElement} element 検証対象のコントロール要素
	 * @param {Object} optionsObj コンストラクタ引数
	 * @see LiveValidation#initialize(element, optionsObj)
	 */
	initialize: function(element, optionsObj){
		
		this.element = element;
		
		// コンストラクタ引数にマスカット部品の情報があればセットする。
		if (optionsObj.layout) {
			this.layout = optionsObj.layout;
			this.component = optionsObj.component;
		}

		// 適用するスタイルシートのクラス名を設定
		this.setStyleClass();

		// 検証ルール
		this.validations = [];
		
		// HTML要素の種別
		this.elementType = this.getElementType();
		
		// LiveValidationの初期化オプションの設定
		var options = optionsObj || {};
		this.setLvOptions(options);
		
		// 検証成功時/エラー時の後処理の設定
		this.setOnValidFunction(options);
		this.setOnInvalidFunction(options);
		
		// 検証実行のトリガーとなるイベントの設定
		this.setValidationEvents();
	},
	
	/**
	 * 適用するスタイルシートのクラス名を設定します。
	 */
	setStyleClass: function() {
		this.validClass = 'LV_valid';
	    this.invalidClass = 'LV_invalid';
	    this.messageClass = 'LV_validation_message';
	    this.validFieldClass = 'LV_valid_field';
	    this.invalidFieldClass = 'LV_invalid_field';
	},

	/**
	 * LiveValidation の動作オプションを設定します。
	 *
	 * @param {Object} optionObj LiveValidationの動作オプション
	 *                             (省略可能　default:{})
	 */
	setLvOptions: function(optionsObj) {
		var options = optionsObj || {};
		this.validMessage = options.validMessage || 'Thankyou!';
		this.insertAfterWhatNode = options.insertAfterWhatNode || this.element;
		this.onlyOnBlur =  options.onlyOnBlur || false;
		this.wait = options.wait || 500;
		this.onlyOnSubmit = options.onlyOnSubmit || false;
	},

	
	/**
	 * メッセージ領域を生成します。
	 *
	 * @return メッセージ領域
	 * @type HTMLElement
	 */
	createMessageArea: function() {
		var span = document.createElement('span');
		span.innerHTML = this.getMessage();
		return span;
	},
	
	/**
	 * メッセージ文字列を取得します。
	 *
	 * 改行文字(\n)が設定されている場合はbrタグに変換します。
	 *
	 * @return 改行コードの処理を行ったメッセージ文字列
	 * @type string
	 */
	getMessage: function() {
		var msg = this.message || this.validations[0].params.failureMessage;
		return msg.replace(/\\n/g, "<br />");
	},
	
	/**
	 * 入力値検証対象のHTMLElementにイベントハンドラを追加します。
	 *
	 * HTMLElementに紐づいている既存のハンドラがあれば、LiveValidation用のハンドラと
	 * 共存させます。
	 *
	 * @param eventType {string} - イベント種別(DOMレベル0)
	 * @param handler {function} - 設定したいイベントハンドラ 
	 */
	addEventHandler: function (eventType, handler) {
		var self = this;
		var originalHandler = this.element[eventType];
		this.element[eventType] = function(event) {
			try {
				handler(event);
			} catch (e) {
				// バリデーション実行時にエラーが発生した場合は
				var message = e.getMessages ? e.getMessages().join('\n') :
					e.message;
				var logger = maskat.log.LogFactory.getLog(
					"maskat.validator.livevalidation"); 
				logger.error(message);
			}
			
			if (typeof(originalHandler) == 'function') {
				originalHandler(event);
			}
			// Rialto側でCLASSを上書きしてしまう不具合に対応
			self.addFieldClass();
		}
	},
	
	/**
	 * 検証成功時の後処理を設定します。
	 *
	 * @param {Object} options オプションオブジェクト
	 */
	setOnValidFunction : function(options) {
		this.onValid = options.onValid || function () {
			this.removeMessage();
			this.addFieldClass();
			
			if (this.isMassValidate) {
				this.isMassValidate = false;
			}
			
			if (this.layout) {
				var widget = maskat.app.getLayout(this.layout).getWidget(this.component);
				if (widget.widget._setStateClass) {
					
					var state = false;
					if (widget.widget.isValid) {
						state = widget.widget.isValid();
					}
					widget.widget.state = "";
					widget.widget._setStateClass();
					
					if (this.element.type == "radio") {
						var layoutObj = maskat.app.getLayout(this.layout);
						var widget = layoutObj.getWidget(this.component);
						var group = dojo.query("INPUT[type=radio][name=" + widget.group + "]");
						for (var i = 0; i < group.length; i++){
							groupWidget = layoutObj.getWidget(group[i].parentNode._widgetName).widget;
							groupWidget.state = "";
							if (groupWidget._setStateClass) {
								groupWidget._setStateClass();
							}
							groupWidget.optionalNode[0].style.backgroundColor = "";
						}
					} else if (this.elementType == 4) {
						widget.widget.optionalNode[0].style.backgroundColor = "";
					} else {
						if (widget.widget.isValid && !state) {
							widget.widget.state = "Error";
							widget.widget._setStateClass();
						}
					}
				}
			}
		};
	},
	
	/**
	 * 検証エラー時の後処理を設定します。
	 *
	 * @param {Object} options オプションオブジェクト
	 */
	setOnInvalidFunction: function(options) {
		this.onInvalid = options.onInvalid || function () {
			this.insertMessage ( this.createMessageArea() );
			this.addFieldClass();
			
			if (this.layout) {
				var widget = maskat.app.getLayout(this.layout).getWidget(this.component);
				if ((widget.widget._setStateClass)) {
					
					widget.widget.state = "Error";
					widget.widget._setStateClass();
					
					if (this.element.type == "radio") {
						var layoutObj = maskat.app.getLayout(this.layout);
						var widget = layoutObj.getWidget(this.component);
						var group = dojo.query("INPUT[type=radio][name=" + widget.group + "]");
						for (var i = 0; i < group.length; i++){
							groupWidget = layoutObj.getWidget(group[i].parentNode._widgetName).widget;
							groupWidget.state = "";
							if (groupWidget._setStateClass) {
								groupWidget._setStateClass();
							}
							groupWidget.optionalNode[0].style.backgroundColor = "#F9F7BA";
						}
					} else if (this.elementType == 4) {
						widget.widget.optionalNode[0].style.backgroundColor = "#F9F7BA";
					}
				}
			}
		};
	},
		
	/**
	 *　入力値検証を実行するイベントの設定をします。
	 */
	setValidationEvents: function() {
		var self = this;
		var KEY_CODE_TAB = 9;
		var KEY_CODE_SHIFT = 16;
		
		if(!this.onlyOnSubmit){
			switch(this.elementType){
			case LiveValidation.CHECKBOX:
				this.addEventHandler("onclick", function(e) {
					self.validate();
				});
				this.addEventHandler("onblur", function(e) {
					self.doOnBlur();
				});
			case LiveValidation.SELECT:
				this.addEventHandler("onchange", function(e) {
					self.validate();
				});
				break;
			default:
				this.addEventHandler("onfocus", function(e) {
					self.doOnFocus();
				});
				
				if(!this.onlyOnBlur) {
					this.addEventHandler("onkeyup", function(e) {
						var theEvent = e ? e : window.event;
						switch (theEvent.keyCode) {
						case KEY_CODE_TAB:
						case KEY_CODE_SHIFT:
							// Tabキーの場合は入力値検証を行わない。
							return true;
						default:
							break;
						}
						self.deferValidation(); 
					});
				}
				this.addEventHandler("onblur", function(e) {
					self.doOnBlur();
				});
				break;
			}
		}
	},
	
	/**
	 * 入力値検証を実行します。
	 * 
	 * @return 検証結果
	 * @see LiveValidation#validate
	 */
	validate : function() {
		try {
			var self = this;
			var isValid = this.doValidations();
			if (this.layout) {
				var widget = maskat.app.getLayout(this.layout).getWidget(this.component);
				if (widget.widget._setStateClass &&(this.element.type == "radio")) {
					return setTimeout(function() {
						if(widget.getSelectedValue()){
							self.onValid();
							return true;
						} else {
							self.onInvalid(); 
							return false;
						}
					}, 0);
				} else if (widget.widget._setStateClass &&(this.elementType == 4)) {
					return setTimeout(function() {
						if(widget.getValue()){
							self.onValid();
							return true;
						} else {
							self.onInvalid(); 
							return false;
						}
					}, 0);
				}
			}
			if (isValid) {
				this.onValid();
				return true;
			} else {
				setTimeout(function() {
					self.onInvalid(); 
				}, 0);
		  		return false;
			}
		} catch (e) {
			if (this.layout) {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_COMPONENT_VALIDATION_ERROR",
					{ layout: this.layout, widget: this.component }, e);
			} else {
				throw new maskat.lang.Error(
					"LIVEVALIDATION_COMPONENT_VALIDATION_ERROR_HTML",
					{ id: this.element.id }, e);
			}
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * 検証エラー時にフロートメッセージを表示するLiveValidationクラスです。
 *
 * 本クラスでは一括検証時にフロートメッセージを表示せず、ダイアログAPIで
 * エラーメッセージを一括出力します。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.custom.FloatAndAlertMessageLiveValidation")
	.extend("maskat.validator.livevalidation.custom.FloatMessageLiveValidation", {
	
	_static: {
	
		/**
		 * 複数項目の入力値の一括検証を行います。
		 *
		 * 本クラスでは、エラーメッセージをDialogでまとめて表示します。
		 *
		 * @param {Array} validations
		 *         検証対象となるLiveValidationオブジェクトの配列
		 * @return 検証結果
		 * @type boolean
		 */	
		massValidate: function(validations) {
			var returnValue = true;
			var messageArray = [];
			for(var i = 0, len = validations.length; i < len; ++i ){
				validations[i].isMassValidate = true;
				var valid = validations[i].validate(true);
				if(returnValue) returnValue = valid;
				if(!valid) {
					messageArray.push(validations[i].getMessage());
				}
			}
			if(messageArray.length > 0) {
				maskat.ui.Dialog.openAlert("入力値検証エラー",
					messageArray.join("\n"), maskat.ui.Dialog.ERROR);
			}
			return returnValue;
		}
	},
	
	/**
	 * 検証エラー時の後処理(onInvalid)を設定します。
	 * 
	 * @param {object} options コンストラクタの第二引数(optionsObj)
	 */
	setOnInvalidFunction: function(options) {
		this.onInvalid = options.onInvalid || function(){
			if(!this.isMassValidate) {
				// 単項目チェック
				this.insertMessage( this.createMessageArea() );
			} else {
				// 一括チェック時
				this.removeMessage();
				this.isMassValidate = false;
      		}
			this.addFieldClass();
			
			if (this.layout) {
				var widget = maskat.app.getLayout(this.layout).getWidget(this.component);
				if ((widget.widget._setStateClass)) {
					
					widget.widget.state = "Error";
					widget.widget._setStateClass();
					
					if (this.element.type == "radio") {
						var layoutObj = maskat.app.getLayout(this.layout);
						var widget = layoutObj.getWidget(this.component);
						var group = dojo.query("INPUT[type=radio][name=" + widget.group + "]");
						for (var i = 0; i < group.length; i++){
							groupWidget = layoutObj.getWidget(group[i].parentNode._widgetName).widget;
							groupWidget.state = "";
							if (groupWidget._setStateClass) {
								groupWidget._setStateClass();
							}
							groupWidget.optionalNode[0].style.backgroundColor = "#F9F7BA";
						}
					} else if (this.elementType == 4) {
						widget.widget.optionalNode[0].style.backgroundColor = "#F9F7BA";
					}
				}
			}
		};
	},
	
	/**
	 * メッセージ文字列を取得します。
	 *
	 * バリデーション定義XMLのmessageタグに定義した内容を元に
	 * 下記の通り改行コードの処理をします。
	 *
	 * 即時検証: 改行文字(\n)をbrタグに変換します。
	 * 一括検証: 改行文字(\n)で文字列を改行します。
	 *
	 * @return 改行コードを処理したメッセージ文字列
	 * @type string
	 */
	getMessage: function() {
		var msg = this.message || this.validations[0].params.failureMessage;
		if (this.isMassValidate) {
			return msg.replace(/\\n/g, "\n");
		} else {
			return msg.replace(/\\n/g, "<br />");
		}
	}
	
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
/**
 * 検証エラー時にフロートメッセージを表示するLiveValidationクラスです。
 *
 * 即時検証(単項目チェック)時にフォーカスされている項目のみエラーメッセージを
 * 表示します。
 * 一括検証時には該当するすべてのエラーメッセージを表示します。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.custom.FloatMessageLiveValidation")
	.extend("maskat.validator.livevalidation.custom.CustomLiveValidation", {

	_static: {

		/**
		 * 複数項目の入力値の一括検証を行います。
		 *
		 * 本クラスではLiveValidationクラスと同じ処理をします。
		 *
		 * @param {Array} validations
		 *         検証対象となるLiveValidationオブジェクトの配列
		 * @return 検証結果
		 * @type boolean
		 */	
		massValidate: function(validations) {
			var returnValue = true;
			returnValue = LiveValidation.massValidate(validations);
			return returnValue;
		}
	},
	
	
	/**
	 * 適用するスタイルシートのクラス名を設定します。
	 */
	setStyleClass: function() {
		this.validClass = 'LV_valid_float';
	    this.invalidClass = 'LV_invalid_float';
	    this.messageClass = 'LV_validation_message_float';
	    this.validFieldClass = 'LV_valid_field_float';
	    this.invalidFieldClass = 'LV_invalid_field_float';
	},

	/**
	 * イベント起動時よりwait(msec)分遅らせて検証を実行します。 
	 */
	deferValidation: function(e) {
		if (this.wait >= 300) {
			this.removeMessageAndFieldClass();
		}
		var self = this;
	   	var selfValidate = function(){
    		if(self.focused ){ 
    			self.validate();
    		}
		}
		if (this.timeout) {
			clearTimeout(self.timeout);
		}
		this.timeout = setTimeout( function(){ selfValidate()}, self.wait); 
	}, 
	
	/**
	 * フォーカスアウト時の処理を実行します。
	 *
	 * 本クラスではフォーカスアウト時に入力値検証を実行しますが、
	 * エラーメッセージの表示は行いません。
	 */
	doOnBlur: function(e) {
		var self = this;
		this.focused = false;
		this.validate(e);
		setTimeout(function () {
			self.removeMessage();
		}, 0);
	},
	
	/**
	 * フォーカスイン時の処理を実行します。
	 *
	 * 本クラスでは入力値検証が実行されていない状態でフォーカスインした場合には
	 * 何も処理を行いません。
	 * 既に入力値検証を実行した結果、エラーとなっている項目にフォーカスインした
	 * 場合にはエラーメッセージを表示するため、validateメソッドを実行します。
	 */
	doOnFocus: function(e) {
		this.focused = true;
		if (this.validationFailed) {
			this.validate(e);
		}
	},

	/**
	 * メッセージ領域を削除します。
	 */	
	removeMessage: function() {
		if (this.messageArea && this.messageArea.parentNode ) {
			this.messageArea.parentNode.removeChild(this.messageArea);
		}
	},
	
	/**
	 * メッセージ領域をDOMに追加します。
	 *
	 * @param {HTMLElement} elementToInsert
	 *                body直下に追加されたメッセージ表示のためのDIV 
	 */
	insertMessage: function(elementToInsert) {
		this.removeMessage();
		this.messageArea = elementToInsert;
		if ((this.displayMessageWhenEmpty && 
				(this.elementType == LiveValidation.CHECKBOX || this.element.value == ''))
			|| this.element.value != ''){
			var className = this.validationFailed ? this.invalidClass : this.validClass;
			elementToInsert.className += ' ' + this.messageClass + ' ' + className;
			document.body.appendChild(this.messageArea);
		}
	},
	
	/**
	 * HTML要素の位置とメッセージ長から、メッセージの表示位置を設定します。
	 *
	 * HTML要素の横に表示できない(描画領域を超えてしまう)場合は、
	 * 検証対象となる項目の下にメッセージを表示します。
	 *
	 * @return メッセージ表示領域の位置（LEFTとTOP）
	 * @type  Object	  
	 */
	getMessagePosition: function() {
		var element = this.element;
		
		//メッセージ位置を調整するためのマージン
		if (!this.messageMargin) {
			this.messageMargin = { top: 10, left: 10, right: 20 };
			var prop = maskat.app.getProperty("maskat.core.plugins");
			if (prop && prop.livevalidation) {
				var liveValidationProp = prop.livevalidation;
				if (liveValidationProp["floatMessageMarginLeft"]) {
					this.messageMargin.left =
						liveValidationProp["floatMessageMarginLeft"];
				}
				if (liveValidationProp["floatMessageMarginRight"]) {
					this.messageMargin.right =
						liveValidationProp["floatMessageMarginRight"];
				}
				if (liveValidationProp["floatMessageMarginTop"]) {
					this.messageMargin.top =
						liveValidationProp["floatMessageMarginTop"];
				}
			} 
		}
		
		/************** メッセージ表示幅の計算 *******************************/
		var dom = this.getMessageElement();
		dom.style.position = "absolute";
		dom.style.visibility = "hidden";
		dom.style.whiteSpace = "nowrap";
		var className = this.validationFailed ? this.invalidClass :
			this.validClass;
		dom.className = this.messageClass + ' ' + className;
		document.body.appendChild(dom);
		// メッセージ表示幅
		var messageLength = dom.offsetWidth || 0;
		document.body.removeChild(dom);
		dom = null;
		
		/************** コントロール要素の表示位置とサイズの計算 *************/
		var inputWidth = element.offsetWidth || 0;
		var inputHeight = element.offsetHeight || 0;
		var elementPosition = maskat.validator.livevalidation.util
			.HTMLElementUtil.getAbsolutePosition(element);	
		var messageAreaTop = elementPosition.top || 0;
		var messageAreaLeft = elementPosition.left|| 0;
		
		// メッセージ表示位置の設定
		var bodyWidth = document.body.offsetWidth || 0;
		var messageFieldLeft = messageAreaLeft + inputWidth
			+ this.messageMargin.left;
		var messageFieldRight =  messageFieldLeft + messageLength
			+ this.messageMargin.right; 
		var messageFieldTop = messageAreaTop;
		
		// メッセージ右端がbodyからはみ出る場合は、項目下にメッセージを表示
		if (messageFieldRight > bodyWidth) {
			messageFieldTop += inputHeight + this.messageMargin.top;
			messageFieldLeft -= inputWidth;
			messageFieldRight = messageFieldLeft + messageLength;
			
			// 項目下にメッセージ表示しても画面右側にはみ出る場合、
			// メッセージの右端を画面右端にそろえる
			if (messageFieldRight > bodyWidth) {
				messageFieldLeft = bodyWidth - messageLength
					- this.messageMargin.right;
			}
		}
		return { left: messageFieldLeft + "px", top: messageFieldTop + "px"};
	},
	
	/**
	 * メッセージ領域を生成します。
	 *
	 * @return エラーメッセージをAppendしたメッセージエリア
	 * @type HTMLElement
	 */
	createMessageArea: function() {
		var self = this;
		
		var msgArea = this.getMessageElement();
		var msgPosition = this.getMessagePosition();
		var style = msgArea.style;
		style.position="absolute";
		style.top = msgPosition.top;
		style.left = msgPosition.left; 

		msgArea.onclick = function() {
			self.removeMessage();
		}

		return msgArea; 
	},
	
	/**
	 * メッセージ文字列をセットしたHTML要素を返却します。
	 *
	 * @return メッセージ文字列をセットしたHTML要素
	 * @type HTMLElement
	 */
	getMessageElement: function() {
		var element = document.createElement('div');
		element.innerHTML = this.getMessage();
		return element;
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * HTML要素に関するユーティリティクラスです。
 */
maskat.lang.Class.declare("maskat.validator.livevalidation.util.HTMLElementUtil", {

	_static: {

		/**
		 * HTML要素の描画領域中の絶対位置を取得します。
		 *
		 * @param {HTMLElement} element 絶対位置を取得したいHTML要素  
		 * @return 描画領域中での絶対位置 ({left: 左端, top: 上端}) 
		 * @type Object
		 */
		getAbsolutePosition: function (element) {
			var elem = element;
			var x = 0;
			var y = 0;
			while (elem) {
				x += elem.offsetLeft || 0;
				y += elem.offsetTop  || 0;
				elem = elem.offsetParent;
			}
			return {left: x, top: y};
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
/**
 * 文字列処理に関するユーティリティクラスです。
 */
maskat.lang.Class.declare("maskat.validator.livevalidation.util.StringUtil", {

	_static: {
		
		/**
		 * 引数で与えた文字列を区切り文字で分割し、配列に変換して返却します。
		 * 
		 * 区切り文字として扱いたくないときは'#'でエスケープしてください。
		 * '#'自身分割後の文字列に含めたい場合は'##'と設定してください。
		 * 
		 * @param {string} target 変換対象の文字列
		 * @param {string} delimiterString 区切り文字列
		 * @return 区切り文字列により分割された配列 
		 */
		split: function(target, delimiterString) {
			var delimiter = delimiterString || ',';
			var escapeChar = '#';

			// エスケープ文字のエスケープ処理
			var patternEscapeChar = new RegExp(escapeChar + escapeChar, 'g');
			var escapeStringEscapeChar = '#{ESCAPE_STRING_ESCAPE_CHAR}';
			var str = target.replace(patternEscapeChar, escapeStringEscapeChar);

			// 区切り文字のエスケープ処理
			var escapeStringDelimiter = '#{ESCAPE_STRING_DELIMITER}';
			var patternDelmiter =
				new RegExp(escapeChar + delimiter, 'g');  
			str = str.replace(patternDelmiter, escapeStringDelimiter);

			// 配列への分割とエスケープした文字の復元
			var result = str.split(delimiter);

			var patternEscapeCharReverse = new RegExp(escapeStringEscapeChar, 'g');
			var patternDelimiterReverse = new RegExp(escapeStringDelimiter, 'g');			

			for (var i = 0, length = result.length; i < length; i++) {
				var work = result[i].replace(patternDelimiterReverse, delimiter);
				work = work.replace(patternEscapeCharReverse, escapeChar);
				result[i] = work;  
			}

			return result;
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * ValidationXMLReaderでパースされた設定ファイル情報を元に
 * マスカット部品にLiveValidaitonを適用するためのクラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.xml.CustomLiveValidationInitializer")
	.extend("maskat.validator.livevalidation.xml.LiveValidationInitializer", {

	/**
	 * このクラスのコンストラクタです。
	 *
	 * @param {Object} config 設定ファイル情報
	 */	
	initialize: function(config, layout) {
		
		this.config = config;
		this.validationTargetNodeName = "components";
		this.defaultValidatorClass = 
			maskat.validator.livevalidation.custom.FloatAndAlertMessageLiveValidation;
		this.validations = {};
		this.layout = layout;
		this.logger = maskat.log.LogFactory.getLog(
			"maskat.validator.livevalidation.xml.CustomLiveValidationInitializer");
	},
	
	/**
	 * 検証対象のコントロール要素を取得します。
	 *
	 * @param id {string} component タグの id 属性値
	 * @return 検証対象のコントロール要素
	 */
	getControlElement: function(id) {
		var widget = this.layout.getWidget(id);
		
		if (!widget || !widget.getControlElement) {
			throw new maskat.lang.Error("LIVEVALIDATION_INVALID_WIDGET_ID",
				{ layout: this.config.layout, widget: id });
		}
		
		var controlElement = widget.getControlElement();
		if (!controlElement) {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_CONTROL_ELEMENT_UNDEFINED",
				{ layout: this.config.layout, widget: id });
		}
		
		return controlElement;
	},

	/**
	 * 複数項目の一括検証を実施するイベントを設定します。
	 *  
	 * @see maskat.validator.livevalidation.xml
	 *            .LiveValidationInitializer#setMassValidateEvent()
	 */	
	setMassValidationEventHandler: function(
		id, eventType, validatorClass, lvArray) {

		// massValidate イベントの実装確認
		if (!validatorClass.massValidate
				|| typeof(validatorClass.massValidate) != 'function') {
			throw new maskat.lang.Error("LIVEVALIDATION_MASSVALIDATE_UNDEFINED");
		}
		
		// イベントハンドラの取得処理
		var listeners = this.layout.listeners
		if (!listeners || !listeners.length || !listeners[0]) {
			throw new maskat.lang.Error("LIVEVALIDATION_INVALID_EVENT_HANDLER",
				{ layout: this.config.layout, widget: id, eventType: eventType });
			
		}
		
		var handlers = listeners[0].handlers[id];
		if (!handlers) {
			throw new maskat.lang.Error("LIVEVALIDATION_INVALID_EVENT_HANDLER",
				{ layout: this.config.layout, widget: id, eventType: eventType });
		}
		
		var handler = handlers[eventType];
		if (!handler) {
			throw new maskat.lang.Error("LIVEVALIDATION_INVALID_EVENT_HANDLER",
				{ layout: this.config.layout, widget: id, eventType: eventType });
		}
		
		// イベントハンドラ関数の設定
		handler.validate = function(event) {
			try {
				return validatorClass.massValidate(lvArray);
			} catch (e) {
				var message = maskat.util.Message.format(
					"LIVEVALIDATION_MASS_VALIDATION_ERROR", {}, e) + '\n';
				message += e.getMessages ? e.getMessages().join('\n') : e.message;
				var logger = maskat.log.LogFactory.getLog(
					"maskat.validator.livevalidation");
				logger.error(message);
				return false;
			} 
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * ValidationXMLReaderでパースされた設定ファイル情報を元に
 * マスカット部品にLiveValidaitonを適用するためのクラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.xml.LiveValidationInitializer", {

	/**
	 * このクラスのコンストラクタです。
	 *
	 * @param {Object} config 設定ファイル情報
	 */	
	initialize: function(config) {
		this.validations = {};
		this.config = config;
		this.validationTargetNodeName = "elements";
		this.defaultValidatorClass = LiveValidation; 
		this.logger = maskat.log.LogFactory.getLog(
			"maskat.validator.livevalidation.xml.LiveValidationInitializer");
	},
	
	/**
	 *　設定ファイル情報を元にLiveValidationを適用します。
	 */
	init: function() {
		this.initComponentValidation();
		this.initMassValidation();
	},

	/**
	 * 単項目チェックの設定を実施します。
	 */
	initComponentValidation: function () {
		var validationTargets = this.config[this.validationTargetNodeName];
		
		for (var id in validationTargets) {
			try {
				// 適用ルールを取得します。
				var rules = validationTargets[id].rules;
				if (!rules) {
					// rule タグが存在しない場合は無視します。
					continue;
				}

				// 検証対象のHTMLElementを取得します。
				var controlElement = this.getControlElement(id);
			
				// バリデータの取得
				var validator = this.getValidator(id);
				var validatorClass = validator["class"];
				var validatorOptions = validator["options"];
				var validation = new validatorClass(controlElement, validatorOptions);
		
				// 検証ルールの設定
				for (var i = 0, length = rules.length; i < length; i++) {
					this.addRule(validation, rules[i]);
				}

				this.validations[id] = validation;
			} catch (e) {
				var message;
				if (this.layout) {
					message = maskat.util.Message.format(
						"LIVEVALIDATION_INIT_FAILED_COMPONENT_VALIDATION",
						{ layout: this.config.layout, widget: id }) + '\n';
				} else {
					message = maskat.util.Message.format(
						"LIVEVALIDATION_INIT_FAILED_COMPONENT_VALIDATION_HTML",
						{ id: id }) + '\n';
				}
				message += e.getMessages ? e.getMessages().join('\n') : e.message;
				this.logger.error(message);
			}
		}
	},

	/**
	 * 一括チェックの設定を実施します。
	 */
	initMassValidation: function() {
		var validationTargets = this.config[this.validationTargetNodeName];
		
		for (var id in validationTargets) {
			try {
				var events = validationTargets[id].massValidateEvents;
				if (!events) {
					// event タグが存在しない場合は無視
					continue;
				}
			
				var validator = this.getValidator(id)
				var validatorClass = validator["class"];
			
				// イベント種別ごとに一括チェックの設定を実施します。
				for (var eventType in events) {
					var lvArray = [];
					var targets = events[eventType].targets;
					for (var validatorId in targets) {
						var lv = this.validations[validatorId];
						if (lv) {
							lvArray.push(lv);
						}
					}
					if (lvArray.length > 0) {
						this.setMassValidationEventHandler(
							id, eventType, validatorClass, lvArray);
					}
				}
			} catch (e) {
				var message;
				if (this.layout) {
					message = maskat.util.Message.format(
						"LIVEVALIDATION_INIT_FAILED_MASS_VALIDATION",
						{ layout: this.config.layout, widget: id }) + '\n';
				} else {
					message = maskat.util.Message.format(
						"LIVEVALIDATION_INIT_FAILED_MASS_VALIDATION_HTML",
						{ id: id }) + '\n';
				}
				message += e.getMessages ? e.getMessages().join('\n') : e.message;
				this.logger.error(message);
			}
		}
	},
	
	/**
	 * 検証対象のコントロール要素を取得します。
	 *
	 * @param id {string} 
	 * @return {HTMLElement}
	 */
	getControlElement: function(id) {
		var controlElement = document.getElementById(id);
		
		if (!controlElement) {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_CONTROL_ELEMENT_UNDEFINED_HTML", {id: id});
		}
		 
		return controlElement;
	},
	
	/**
	 * 適用するバリデータの情報を取得します。
	 * 
	 * @param id {string} 取得対象のelementまたはcomponentタグのid属性値
	 * @return バリデータ情報
	 *    { class: バリデータクラス, options: バリデータクラスに渡すオプション }
	 */
	getValidator: function(id) {
		var defaultValidator = this.getDefaultValidator();
		var target = this.config[this.validationTargetNodeName][id];
		
		var validator = {};
		validator.options = {};
		if (target.validator && target.validator["class"]) {
			validator["class"] = target.validator["class"];
		} else {
			validator["class"] = defaultValidator["class"];
			maskat.lang.Object.populate(validator.options,
				defaultValidator.options);
		}

		if (this.layout) {
			validator.options["layout"] = this.config.layout;
			validator.options["component"] = id;
		}

		if (!target.validator) {
			return validator;
		}
		
		for (var name in target.validator) {
			if (name == 'options') {
				maskat.lang.Object.populate(
					validator.options, target.validator.options);
			} else if (name != 'class') {
				validator.options[name] = target.validator[name];
			}	
		}
		
		return validator;
	},
	
	/**
	 * デフォルトのバリデータ情報を取得します。
	 *
	 * @return デフォルトのバリデータ情報
	 * @type Object
	 */
	getDefaultValidator: function() {
	
		// 既にデフォルトのバリデータを生成している場合はそのまま返却します。
		if (this.defaultValidator) {
			return this.defaultValidator;
		}
		
		// デフォルトのバリデータを生成していない場合は生成処理を行います。
		this.defaultValidator = {};
		this.defaultValidator["class"] = this.defaultValidatorClass || LiveValidation;
		this.defaultValidator["options"] = {};
		
		// <default>タグの子要素に<validator>タグが存在しない場合は何もしません。
		if (!this.config["default"] || !this.config["default"]["validator"]) {
			return this.defaultValidator;
		}
	
		var validator = this.config["default"]["validator"];
		// バリデータクラスの設定
		if (validator["class"]) {
			this.defaultValidator["class"] = validator["class"]; 
		}		
		
		// バリデータクラスのオプションの設定		
		for (var name in validator) {
			if (name != "options" && name != "class") {
				this.defaultValidator.options[name] = validator[name];  
			}
		}
		if (validator.options) {
			maskat.lang.Object.populate(
				this.defaultValidator.options, validator.options);
		}
		
		return this.defaultValidator;
	},
	
	
	/**
	 * バリデーション関数に既定のエラーメッセージをセットします。
	 *
	 * LiveValidation　のバリデーション関数ごとに設定すべきエラーメッセージが
	 * 異なるために設定ファイルから読込まれたエラーメッセージのマッピングが必要。
	 * 
	 * param タグ内で状況別メッセージをセット済であれば上書きしません。
	 *
	 * @param {Object} params paramタグの情報
	 * @param {function} validateFunction バリデーション関数
	 * @param {string} _message エラーメッセージ
	 */	
	setDefaultMessage: function(params, validateFunction, _message) {
	
		var message = maskat.lang.String.trim(_message);
	
		if (validateFunction == Validate.Numericality) {
			// Validate.Numericality 用
			if (!params.notANumberMessage) {
				params.notANumberMessage = message;
			}
			if (!params.notAnIntegerMessage) {
				params.notAnIntegerMessage = message;
			}
			if (!params.tooHighMessage) {
				params.tooHighMessage = message;
			}
			if (!params.tooLowMessage) {
				params.tooLowMessage = message;
			}
			if (!params.wrongNumberMessage) {
				params.wrongNumberMessage = message;
			}
		} else if (validateFunction == Validate.Length) {
			// Validate.Length 用
			if (!params.tooLongMessage) {
				params.tooLongMessage = message;
			}
			if (!params.tooShortMessage) {
				params.tooShortMessage = message;
			}
			if (!params.wrongLengthMessage) {
				params.wrongLengthMessage = message;
			}
		} else {
	    	// その他
			if (!params.failureMessage) {
				params.failureMessage = message;
			}
		}
	},
	
	
	/**
 	 * LiveValidation オブジェクトにルールを追加します。。
 	 *
 	 * @param validation {LiveValidation} ルール追加対象のLiveValidationオブジェクト
 	 * @param rule {Object} 追加するルール
 	 *        {
 	 *            name    : バリデーション関数
 	 *            message : エラーメッセージ
 	 *            params  : バリデーション関数のパラメータオブジェクト
 	 *        }
 	 */
	addRule: function(validation, rule) {
		// バリデーション関数
		var validateFunction = rule.name;

		// バリデーション関数のパラメータ
		var paramsObj = {};

		try {
			// paramタグのデータを型変換して設定する。
			if (rule.params) {
				var props =	maskat.validator.livevalidation.xml
					.ValidationRuleParamProperties.getInstance();

				props.setProperties(rule.params);
				var params = props.getProperties();

				// Validate.Inclusion, Validate.Exclusion 用
				if (params['within']) {
	    			params['within'] = maskat.validator.livevalidation
	    				.util.StringUtil.split(params['within']);
				}

				// Validate.Confirmation 用
				if (params['match']) {
	    			params['match'] = this.getControlElement(params['match']);
				}

				maskat.lang.Object.populate(paramsObj, params);
			}
		} catch (e) {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_CONVERT_RULE_PARAM_ERROR", {}, e);
		}

		// バリデーション関数別のエラーメッセージを設定
		var message = rule.message;
		if (message) {
			this.setDefaultMessage(paramsObj, validateFunction, message);
		}
		
		// ルールの追加
		validation.add(validateFunction, paramsObj);
	},
	
	/**
	 * 一括検証を行うイベントハンドラを設定します。
	 *
	 * 入力値検証に成功した場合は設定済のイベントハンドラを実行します。
	 * 
	 * @param id   {HTMLElement} 
	 * @param eventType {string} イベント種別(onclick, onblur, onsubmitなど)
	 * @param validatorClass
	 *                  massValidateメソッド実装済みのLiveValidation継承クラス
	 * @param lvArray
	 *          {Array<LiveValidation>} 一括検証を行う、LiveValidationオブジェクトの配列
	 */
	setMassValidationEventHandler: function(
		id, eventType, validatorClass, lvArray) {
		
		// 一括チェックイベントを設定するHTML要素を取得する。
		var target = document.getElementById(id);
		
		if (!target) {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_INVALID_MASS_VALIDATION_ELEMENT", {"id":id} ); 
		}
		
		if (!validatorClass.massValidate
				|| typeof(validatorClass.massValidate) != 'function') {
			throw new maskat.lang.Error("LIVEVALIDATION_MASSVALIDATE_UNDEFINED");
		}
		
		var originalHandler = target[eventType];
		target[eventType] = function(event) {
			var result = false;
			try {
				result = validatorClass.massValidate(lvArray);
			} catch (e) {
				var message = maskat.util.Message.format(
					"LIVEVALIDATION_MASS_VALIDATION_ERROR", {}, e) + '\n';
				message += e.getMessages ? e.getMessages().join('\n') : e.message;
				var logger = maskat.log.LogFactory.getLog(
					"maskat.validator.livevalidation");
				logger.error(message);
			}
			
			if (result && typeof(originalHandler) == 'function') {
				var originalHandlerResult = originalHandler(event);
				if (typeof(originalHandlerResult) == 'boolean') {
					result = originalHandlerResult;
				}
			}
			
			return result;
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * バリデーション定義XMLのparam属性で定義された検証ルールのパラメータの
 * 型変換を行うためのクラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.xml.ValidationRuleParamProperties")
	.extend("maskat.util.Properties", {
	
	_static: {
	
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @return このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = this;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},

	/**
	 * このクラスのコンストラクタです。
	 */
	initialize: function(){
		this.base(maskat.util.CrossBrowser.loadJSONFrom(
			maskat.location + "livevalidation/validationRuleParams.json"));
	},
	
	/**
	 * プロパティキーと値が対になったオブジェクトから、プロパティ値を
	 * すべて読み込みます。
	 *
	 * 値のセット時に既存のプロパティ値をクリアします。
	 *
	 * @param values プロパティキーと値が対になったオブジェクト 
	 */
	setProperties: function(values) {
		
		// value をクリアする。
		this.values = {};
		
		for (var key in values) {
			this.setProperty(key, values[key]);
		}
	}
});
/*
 * Copyright (c)  2006-2010 Maskat Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * バリデーション定義XMLをロードし、LiveValidaitionの適用処理を呼び出す
 * クラスです。
 */
maskat.lang.Class.declare(
	"maskat.validator.livevalidation.xml.ValidationXMLReader")
	.extend("maskat.xml.XMLObjectBinder", {
	
	_static: {

		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @return このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/**
	 * このクラスのコンストラクタです。
	 */
	initialize: function() {
		this.base(this.getBindingConfiguration());
	},
	
	/**
	 * バインディング設定を取得します。
	 *
	 * @return XMLObjectBinderのバインディング設定。
	 */
	getBindingConfiguration: function() {
		return {
			"#document": {
				children: {
					livevalidation: {}
				}
			},
		
			// ルートノード
			livevalidation: {
				attributes: {
					// Maskatの場合はレイアウト名を指定する。
					layout: { type: "string" }
				},
				children: {
					// 入力値検証対象の項目
					component: { property: "components", key:"id", repeat : true },
					element:   { property: "elements",   key:"id", repeat : true },
					"default":   { property: "default" }
				}
			},
			
			"default": {
				children: {
					validator: { property: "validator" }
				}
			},
		
			// バリデータクラスの設定
			validator: {
				attributes: {
					"class": { type: "function" },
					onValid: { type: "function" },
					onInvalid: { type: "function" },
					insertAfterWhatNode: { type: "HTMLElement" },
					onlyOnBlur: { type: "boolean" },
					wait: { type: "number" },
					onlyOnSubmit: { type: "boolean" },
					validMessage: { type: "string" }
				},
				children: {
					option: { property: "options", repeat: true,
						key: 'name', value: 'value' }
				}
			},

			// バリデーション関数に渡すパラメータ
			option: {
				attributes: {
					name:  { type: "string", required: true },
					value: { type: "string", required: true }
				}
			},

			// マスカット部品用
			component: {
				attributes: {
					id: { type:"string", required:true }
				},
				children : {
					// ボタン押下時などのイベント発生時に行う複数項目の一括チェック設定用
					massValidate : { property: "massValidateEvents", repeat: true, key: "eventType" },
				
					// 項目と検証ルールの紐付け用
					rule:       { property:'rules', repeat:true}, 
					
					// バリデータクラス
					validator:  { property: "validator" }
				}
			},
		
			// LiveValidation単体(生HTML)用
			element : {
				attributes : {
					id: { type:　"string", required:　true }
				},
				children : {
					// ボタン押下時などのイベント発生時に行う複数項目の一括チェック設定用
					massValidate : { property: "massValidateEvents", repeat: true, key: "eventType" },
				
					// 項目と検証ルールの紐付け用
					rule:       { property:　"rules", repeat:true}, 
					
					// バリデータクラス
					validator: { property: "validator" }
				}
			},
		
			// 一括チェック設定
			massValidate: {
				attributes: {
					eventType: { type: "string", required:true }
				},
				children: {
					target: { property: "targets", repeat: true, key: "ref" }
				}
			},
		
			// イベント時に一括入力チェックを行うコンポーネント群
			target: {
				attributes: {
					ref: { type: "string", required: true }
				}
			},
	
			// 単項目チェックの検証ルール	
			rule:{
				attributes : {
					// バリデーション関数の完全修飾名
					name : { type: "function", required: true }
				},
				children: {
					message: {},
					param: {
						property: "params",
						repeat: true,
						key: 'name',
						value: 'value'
					}
				}
			},
	
			// エラーメッセージ
			message: {
				children: { "#text": {property:'message' } }
			},
		
			// バリデーション関数に渡すパラメータ
			param: {
				attributes: {
					name:  { type: "string", required: true },
					value: { type: "string", required: true }
				}
			}
		};
	},
	
	/**
	 * バリデーション定義XMLをパースし、 LiveValidaitonを適用します。
	 *
	 * @param url {string} - バリデーション定義XMLのパス文字列
	 */
	load: function(url) {

		try {
			var doc = maskat.util.CrossBrowser.getXMLDocumentFrom(url);
			var config = this.read(doc);
			this.initValidation(config);
		} catch (e) {
			throw new maskat.lang.Error(
				"LIVEVALIDATION_LOAD_ERROR", { url: url }, e);
		}
	},
	
	/**
	 * パースされたバリデーション定義情報を元に LiveValidation を適用します。
	 *
	 * @param config {Object} パース済のバリデーション定義情報
	 */
	initValidation: function(config) {

		if (config.components) {
			// マスカットの場合 (componentタグ)
			if (!config.layout || config.layout == "") {
				throw new maskat.lang.Error("LIVEVALIDATION_MISSING_LAYOUT_ID");
			}
			
			var layout = maskat.app.getLayout(config.layout);
			
			if (!layout || !(layout instanceof maskat.layout.Layout)) {
				throw new maskat.lang.Error("LIVEVALIDATION_INVALID_LAYOUT_ID",
					{ layout: config.layout });
			}
			
			(new maskat.validator.livevalidation.xml
				.CustomLiveValidationInitializer(config, layout)).init();
				
		} else if (config.elements) {
			// LiveValidation 単体利用の場合 (elementタグ)
			(new maskat.validator.livevalidation.xml
				.LiveValidationInitializer(config)).init();
		}
	}
});
