/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @namespace
 * マスカットフレームワークが使用するクラス、関数、オブジェクトを格納する
 * 名前空間です。
 * 
 * <p>
 * マスカットフレームワークは多数の JavaScript ライブラリと共存することを
 * 念頭に設計されているため、コアモジュールはグローバル名前空間や組み込み
 * オブジェクトの prototype プロパティを汚染しないように設計されています。
 * ただし、プラグインが読み込むサードパーティ製の JavaScript ライブラリに
 * ついてはこの限りではありません。
 * </p>
 */
var maskat = {

	/** マスカットフレームワークのバージョン識別子 */
	version: "2.1.0.v20090410",

	/** HTML 文書の読み込み完了後にコールバックする関数のリスト */
	loaders: [],

	/** HTML 文書からマスカットフレームワークへの相対パス */
	location: null,

	/** マスカットアプリケーションのインスタンスへの参照 */
	app: null,

	/** マスカットアプリケーションで最後に発生したエラーへの参照 */
	error: null,

	/**
	 * 起動ステージ 1:
	 *
	 * HTML 文書の読み込み完了後、onload イベントの発生より以前の段階で
	 * 実行されます。プラグインのインストールとアプリケーションの起動を
	 * 行います。
	 */
	bootstrap: function(){
		/* この関数が 1 回だけ実行されることを保証する */
		var self = arguments.callee;
		if (self.initialized) {
			return;
		}
		self.initialized = true;
		
		for (var i = 0; i < maskat.loaders.length; i++) {
			maskat.loaders[i].call(this);
		}
	}
};

/**
 * 起動ステージ 0:
 * 
 * この関数は maskat.js の読み込み時に実行され、HTML 文書の読み込みが
 * 完了後にできるだけ早い段階で起動ステージ 1 (bootstrap 関数) が実行
 * されるようにハンドラ関数 (bootstrap) を登録します。
 */
(function (){
	/*
	 * Internet Explorer の場合:
	 *
	 * script 要素の defer 属性と onreadystatechange イベントを利用し、
	 * onload イベントの発生前にマスカットフレームワークを初期化する
	 */
	if (navigator.userAgent.indexOf("MSIE") != -1) {
		var callback = function() {
			if (this.readyState == "complete") {
				maskat.bootstrap();
			}
		};
		document.onreadystatechange = callback;
		document.write('<script id="__init_script" defer="true" src="//:"></script>');

		if (document.getElementById) {
			var deferScript = document.getElementById("__init_script");
			if (deferScript) {
				deferScript.onreadystatechange = callback;
				/* 読み込みが既に完了しているかどうかを確認 */
				deferScript.onreadystatechange();
				deferScript = null;
			}
		}
	}
	
	/*
	 * Firefox (Gecko) の場合:
	 *
	 * DOMContentLoaded イベントを利用し、onload イベントの発生前に
	 * マスカットフレームワークを初期化する
	 */
	if (document.addEventListener) {
		document.addEventListener("DOMContentLoaded", maskat.bootstrap, false);
	}
	
	/* その他のブラウザでは onload イベントを利用 */
	window.onload = maskat.bootstrap;

	/*
	 * デフォルトエラーハンドラ関数の指定
	 *
	 * ユーザがwindow.onerrorに関数を定義することで任意のエラー
	 * 処理を行うことができます
	 */
	window.onerror = function(message, url, line) {
		var logger = maskat.log.LogFactory.getLog("maskat.error");
		/*
		 * LoadJavaScriptCommandメソッドで読み込まれるJavaScriptに
		 * 文法エラーがある場合、maskat.app.handleErrorを経由しません。
		 */
		if (maskat.error) {
			message = maskat.error.getMessages ?
				maskat.error.getMessages().join("\n  Caused by: ") :
				maskat.error.message;
		}
		if (url != "" || line != 0) {
			message += "\n  url: " + url + "\n  line: " + line;
		}
		logger.error(message, maskat.error);
		
		if (maskat.error) {
			var e = maskat.error.getCauseError ?
				maskat.error.getCauseError() : maskat.error;
				
			if (e instanceof maskat.lang.Error) {
				return true;
			}
		}
		return false;
	}
	
})();
/*
 * Copyright (c)  2006-2009 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.loaders.push(function(){

	/* フレームワークパスを自動検出して maskat.location に格納 */
	var scripts = document.getElementsByTagName("script");
	var regexp = /\/core\/maskat\.js(\.uncompressed)?$/;

	for (var i = 0; i < scripts.length; i++) {
		var src = scripts[i].getAttribute("src");
		var match = src && src.match(regexp);
		if (match) {
			maskat.location = src.substring(0, match.index + 1);
			maskat.debug = (match[1] == ".uncompressed");
			break;
		}
	}

	/* メッセージリソースの読み込み */
	maskat.util.Message.loadTemplates(maskat.location + "core/messages.json");

	/* マスカットアプリケーションの生成 */
	maskat.app = new maskat.core.Application();

	/* プラグインマネージャの起動 */
	var manager = maskat.core.PluginManager.getInstance();
	try {
		manager.loadProperties(maskat.location + "properties.json");
		manager.loadProperties("properties.json");
	} catch (e) {}

	manager.plugins.core = maskat.app;
	manager.launch(function() { maskat.app.run(); });

});
/*
 * Copyright (c)  2006-2009 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 が未定義の場合にはここで定義する */
if (typeof(maskat) == "undefined") { maskat = {}; }

/** @namespace */
maskat.lang = {

	/**
	 * @class
	 * オブジェクトの検索や生成、プロパティ操作などのユーティリティ関数を
	 * 提供します。
	 */
	Object: {
		/**
		 * スコープ内から指定したパス文字列で取得できるオブジェクトを
		 * 返します。
		 *
		 * @param path パス文字列 (プロパティを "." で区切った文字列)、
		 *             または文字列の配列
		 * @param scope スコープの起点オブジェクト、省略時は window
		 *
		 * @returns JavaScript オブジェクト、見つからなければ undefined
		 */
		find: function(path, scope){
			var segments = (path instanceof Array) ? path : path.split(".");
			scope = (arguments.length > 1) ? scope : window;

			if (scope) {
				for (var i = 0; i < segments.length; i++) {
					if (typeof(scope[segments[i]]) == "undefined") {
						return undefined;
					}
					scope = scope[segments[i]];
				}
			}
			return scope;
		},
		
		/**
		 * スコープ内の指定したパスにオブジェクトを生成します。
		 *
		 * パス上に該当するオブジェクトが存在しない場合には、それらの
		 * オブジェクトが自動的に生成されます。
		 *
		 * @param path パス文字列 (プロパティを "." で区切った文字列)、
		 *             または文字列の配列
		 * @param scope スコープの起点オブジェクト、省略時は window
		 *
		 * @returns 生成されたオブジェクト
		 */
		create: function(path, scope){
			var segments = (path instanceof Array) ? path : path.split(".");
			scope = (arguments.length > 1) ? scope : window;

			if (scope) {
				for (var i = 0; i < segments.length; i++) {
					if (!scope[segments[i]]) {
						scope[segments[i]] = {};
					}
					scope = scope[segments[i]];
				}
			}
			return scope;
		},
		
		/**
		 * あるオブジェクトのプロパティをすべて、もう一方のオブジェクトに
		 * コピーします。
		 * 
		 * @param object
		 *            コピー先のオブジェクト
		 * @param properties
		 *            コピーするプロパティを格納したオブジェクト
		 * @param deep
		 *            プロパティがオブジェクトや配列型の場合にそれらを深く
		 *            マージするかどうか指定するフラグ
		 * 
		 * @returns プロパティをコピーされたオブジェクト
		 */
		populate: function(object, properties, deep){
			if (deep && object instanceof Array && properties instanceof Array) {
				Array.prototype.push.apply(object, properties);
			} else {
				for (var key in properties) {
					if (deep && object[key] && (typeof(object[key]) == "object")) {
						this.populate(object[key], properties[key], true);
					} else {
						object[key] = properties[key];
					}
				}
			}
			return object;
		},

		/**
		 * 指定したオブジェクトのプロパティをすべて削除します。
		 *
		 * @param object プロパティを削除するオブジェクト
		 */
		dispose: function(object) {
			// TODO: Safari 2.02 以前には hasOwnProperty メソッドがない
			for (var property in object) {
				if (object.hasOwnProperty(property)) {
					delete object[property];
				}
			}
		},

		/**
		 * JSON (JavaScript Object Notation) で記述されたリテラルを評価して
		 * オブジェクトを返します。
		 *
		 * @returns オブジェクト
		 */
		parseJSON: function(text){
			/* JSON パーサが読み込まれている場合は利用する */
			if (typeof(JSON) != "undefined" && typeof(JSON.parse) == "function") {
				return JSON.parse(text);
			}
			return eval("(" + text + ")");
		},

		/**
		 * 指定されたオブジェクトを JSON 形式の文字列にエンコードして返却
		 * します。
		 *
		 * @returns JSON 形式にエンコードされた文字列
		 */
		encodeJSON: function(object) {
			/* JSON パーサが読み込まれている場合は利用する */
			if (typeof(JSON) != "undefined" && typeof(JSON.stringify) == "function") {
				return JSON.stringify(object);
			}
			var stack = [];
			
			function _indexOf(object) {
				for (var i = 0; i < stack.length; i++) {
					if (stack[i] === object) {
						return i;
					}
				}
				return -1;
			}
			
			function _isAddProperty(object) {
				if (typeof(object) == "function") {
					return false;
				}
				if (typeof(object) == "object") {
					return _indexOf(object) == -1;
				}
				return true;
			}
			
			function _encodeJSON(object) {
				var result = [];
				if (object == null) {
					result.push("null");
				} else if (typeof(object) == "undefined") {
					result.push("undefined");
				} else if (object instanceof Array) {
					result.push("[");
					var f = false;
					for (var i = 0; i < object.length; i++) {
						if (_isAddProperty(object[i])) {
							if (f) {
								result.push(", ");
							} else {
								f = true;
							}
							result.push(_encodeJSON(object[i]));
						}
					}
					result.push("]");
				} else if (typeof(object) == "object") {
					stack.push(object);
					result.push("{");
					var f = false;
					for (var j in object) {
						if (_isAddProperty(object[j])) {
							if (f) {
								result.push(", ");
							} else {
								f = true;
							}
							result.push("\"" + j + "\":" + _encodeJSON(object[j]));
						}
					}
					result.push("}");
					stack.pop(object);
				} else if (typeof(object) == "string") {
					result.push("\"" + object + "\"");
				} else if (typeof(object) != "function") {
					result.push(object.toString());
				}
				return result.join("");
			}
			return _encodeJSON(object);
		}
	},
	
	/**
	 * @class
	 * 新しいクラスを宣言する記法を提供します。
	 *
	 * <h2>クラスの宣言</h2>
	 * <p>
	 * このクラスの提供する静的メソッド {@link maskat.lang.Class.declare} を
	 * 使用すると新しいクラスを以下のような形式で宣言することができます:
	 * </p>
	 *
	 * <pre>
	 * maskat.lang.Class.declare("maskat.example.Person", {
	 *     initialize: function(name, age) {
	 *         this.name = name;
	 *         this.age = age;
	 *     },
	 * 
	 *     sayHello: function() {
	 *         alert("Hello " + this.name);
	 *     }
	 * });
	 * </pre>
	 *
	 * <h2>オブジェクトの生成</h2>
	 * <p>
	 * プロパティ名 initialize で指定された関数はコンストラクタとして使用
	 * されます。このプロパティが省略された場合には空の関数が使用されます。
	 * 宣言したクラスは new 演算子でインスタンス化することができます:
	 * </p>
	 *
	 * <pre>
	 * var alice = new maskat.example.Person("Alice", 20);
	 * alice.sayHello();
	 * </pre>
	 *
	 * <h2>クラスの継承</h2>
	 * <p>
	 * クラス宣言に続けて {@link maskat.lang.Class#extend} メソッドを使用
	 * すると、宣言したクラスを他のクラスのサブクラスにすることができます:
	 * </p>
	 *
	 * <pre> 
	 * maskat.lang.Class.declare("maskat.example.Employee")
	 *     .extend("maskat.example.Person", {
	 *
	 *     initialize: function(name, age, salary) {
	 *         this.base(name, age);
	 *         this.salary = salary;
	 *     },
	 *
	 *     getSalary: function() {
	 *         return this.saraly;
	 *     }
	 * });
	 * </pre> 
	 *
	 * <p>
	 * コンストラクタ内から呼び出している this.base メソッドは親クラスの
	 * コンストラクタを参照しています。
	 * </p>
	 *
	 * <h2>クラスの静的メンバ</h2>
	 * <p>
	 * クラスのメンバ名に <code>_static</code> を指定し、オブジェクトを値
	 * として指定すると、そのオブジェクトのプロパティがすべてクラスの静的
	 * メンバとして追加されます。プロパティ名 initialize を持つ関数が静的
	 * メンバとして指定されている場合は、クラスの宣言直後にその関数を実行
	 * します。 
	 * </p>
	 *
	 * <pre>
	 * maskat.lang.Class.declare("maskat.example.Directory", {
	 *     _static: {
	 *         directory: {},
	 * 
	 *         find: function(name) {
	 *             return this.directory[name];			
	 *         },
	 *
	 *         initialize: function() {
	 *             this.directory["alice"] =  new maskat.example.Person("alice", 20);
	 *             this.directory["bob"] = new maskat.example.Person("bob", 32);
	 *             this.directory["carol"] =  new maskat.example.Person("carol", 17);
	 *         }
	 *     },
	 * });
	 * </pre>
	 */
	Class: {
		/**
		 * 新しいクラスを宣言します。
		 *
		 * この静的メソッドは新しいクラスのコンストラクタ関数を作成し、
		 * prototype オブジェクトに指定されたプロパティを追加します。
		 *
		 * @param name クラス名を表す文字列
		 * @param properties プロパティを格納したオブジェクト
		 *
		 * @returns 新しいクラスのコンストラクタ関数
		 */
		declare: function(name, properties){
			/* サブクラスが先に宣言された場合、親クラスは存在している */
			var clazz = maskat.lang.Object.find(name);
			if (!clazz) {
				/* コンストラクタ関数を生成 */
				clazz = function() {
					this.initialize.apply(this, arguments);
				};
				maskat.lang.Object.populate(clazz, this.methods);

				/* コンストラクタ関数を名前空間に格納 */
				var index = name.lastIndexOf(".");
				var namespace = (index == -1) ? window :
					maskat.lang.Object.create(name.substring(0, index));
				var simpleName = name.substring(index + 1, name.length);
				namespace[simpleName] = clazz;
			}
			
			if (properties) {
				/* 他のクラスを継承しない (ルート階層) */
				clazz.addProperties(properties);
				
				delete clazz.deferred;
				var children = clazz.subclasses;
				if (children) {
					for (var i = 0; i < children.length; i++) {
						children[i].inherit(clazz, children[i].properties);
					}
				}
			} else {
				/* 他のクラスを継承する: プロパティ追加を遅延 */
				clazz.deferred = true;
			}
			return clazz;
		},
		
		methods: {
			/** @scope maskat.lang.Class.prototype */

			/**
			 * このクラスに指定したプロパティを追加します。
			 *
			 * @param properties プロパティを格納したオブジェクト
			 *
			 * @returns このクラス
			 */
			addProperties: function(properties){
				/* 静的プロパティをクラスに追加 */
				if (properties._static) {
					maskat.lang.Object.populate(this, properties._static);
					delete properties._static;
				}
				
				/* プロパティを prototype オブジェクトに追加 */
				maskat.lang.Object.populate(this.prototype, properties);
				
				/*
				 * コンストラクタが省略されている場合には、デフォルトの
				 * コンストラクタを定義する
				 */
				if (!this.prototype.initialize) {
					this.prototype.initialize = maskat.lang.EmptyFunction;
				}
				
				/* クラス・イニシャライザを呼び出し */
				if (this.initialize) {
					this.initialize();
				}
				return this;
			},
			
			/**
			 * このクラスを引数 name で指定したクラスのサブクラスにして、
			 * プロパティを追加します。
			 *
			 * @param name 親クラス名を表す文字列
			 * @param properties プロパティを格納したオブジェクト
			 *
			 * @returns このクラス
			 */
			extend: function(name, properties){
				var parent = maskat.lang.Object.find(name);
				if (!parent) {
					/* 親クラスが未定義の場合: クラスを宣言する */
					parent = maskat.lang.Class.declare(name);
				}
				this.superclass = parent;
				
				if (!parent.subclasses) {
					parent.subclasses = [];
				}
				parent.subclasses.push(this);
				
				if (parent.deferred) {
					/* 親クラスの初期化が完了していないため、継承を遅延 */
					this.properties = properties;
				} else {
					/* 親クラスを継承させる */
					this.inherit(parent, properties);
				}
				return this;
			},
			
			/**
			 * このクラスを引数 parent で指定したクラスのサブクラスにして、
			 * プロパティを追加します。
			 *
			 * @private
			 *
			 * @param name 親クラス名を表す文字列
			 * @param properties プロパティを格納したオブジェクト
			 */
			inherit: function(parent, properties){
				/* 親クラスの initialize 関数をダミーに置き換える */
				var base = parent.prototype.initialize;
				parent.prototype.initialize = maskat.lang.EmptyFunction;
				
				/* 親クラスを継承する */
				this.prototype = new parent();
				this.prototype.constructor = this;
				this.prototype.initialize = base;
				this.prototype.base = base;
				this.addProperties(properties);
				
				/* 親クラスの initialize 関数を元に戻す */
				parent.prototype.initialize = base;
				
				/* このクラスにサブクラスがある場合、自分を継承させる */
				var children = this.subclasses;
				if (children) {
					for (var i = 0; i < children.length; i++) {
						if (children[i].deferred) {
							children[i].inherit(this, children[i].properties);
						}
					}
				}
				delete this.deferred;
			}
		}
	},
	
	/** @scope maskat.lang */

	/** 空の関数 */
	EmptyFunction: function(){
	}
};
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XMLHttpRequest のプーリング機構を持つ通信マネージャです。
 *
 * <p>
 * 送信キューにコンテキスト (maskat.comm.HttpContext のインスタンス) を追加
 * することにより通信がスケジューリングされ、通信処理の各段階でコンテキスト
 * のコールバック関数が実行されます。
 * </p>
 * 
 * @name maskat.comm.CommunicationManager
 */
maskat.lang.Class.declare("maskat.comm.CommunicationManager", {
	
	_static: {
		/** @scope maskat.comm.CommunicationManager */

		/** 送信キューを処理する間隔 (ms) */
		sendInterval: 100,

		/** HTTP 応答待ちのタイムアウトを確認する間隔 (ms) */
		timeoutInterval: 500,

		/** XMLHttpRequest のプールサイズ */
		poolSize: 2,

		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this(this.sendInterval,
					this.timeoutInterval, this.poolSize);
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.comm.CommunicationManager.prototype */
	
	/**
	 * コンストラクタです。
	 *
	 * @param sendInterval 送信キューを処理する間隔 (ms)
	 * @param timeoutInterval HTTP 応答待ちのタイムアウトを確認する間隔 (ms)
	 * @param poolSize XMLHttpRequest のプールサイズ
	 */
	initialize: function(sendInterval, timeoutInterval, poolSize){
		this.queue = [];
		this.waitings = [];
		this.completed = [];
		this.locked = false;

		/* HTTP 要求送信スレッドの生成 */		
		this.senderThread = new maskat.lang.Thread(
			this,
			this.processRequest,
			null,
			sendInterval || 100);

		/* タイムアウト確認スレッドの生成 */		
		this.timeoutThread = new maskat.lang.Thread(
			this,
			this.checkTimeout,
			null,
			timeoutInterval || 500);

		/* XMLHttpRequest プールの生成 */		
		this.initPool(poolSize || 2);
	},
	
	/**
	 * XMLHttpRequest をプールから取得します。
	 * プールがすべて利用されている場合には新しいインスタンスを生成します。
	 *
	 * @returns XMLHttpRequest のインスタンス
	 */
	getXMLHttpRequest: function() {
		if (this.pool.length) {
			return this.pool.shift();
		}
		return maskat.util.CrossBrowser.createXMLHttpRequest();
	},

	/**
	 * XMLHttpRequest のプールをパラメータで指定されたサイズで初期化します。
	 *
	 * @param size XMLHttpRequest のプールサイズ 
	 */
	initPool: function(size) {
		this.poolSize = size;
		this.pool = [];
		for (var i = 0; i < size; i++) {
			this.pool.push(maskat.util.CrossBrowser.createXMLHttpRequest());
		}
	},
	
	/**
	 * XMLHttpRequest のプールサイズをパラメータで指定されているサイズまで
	 * 縮小します。
	 */
	shrinkPool: function() {
		for (var i = this.poolSize; i < this.pool.length; i++) {
			delete this.pool[i];
		}
		this.pool.length = this.poolSize;
	},
	
	/**
	 * HTTP コンテキストを送信キューに追加し、通信をスケジュールします。
	 *
	 * @param context 送信コンテキスト
	 */
	enqueue: function(context){
		this.queue.push(context);
		/*
		 * 送信キューが空の場合は送信スレッドは停止状態しているため、
		 * スレッドを開始する
		 */
		if (this.queue.length == 1) {
			this.senderThread.start();
		}
	},

	/**
	 * 送信待ちキューからコンテキストを取得し、HTTP 要求を実行します。
	 *
	 * このメソッドは HTTP 要求送信スレッドから、パラメータで指定された
	 * インターバルで起動されます。
	 */
	processRequest: function() {
		/* 他のコンテキストが同期通信中の場合はロック開放まで待機 */
		if (this.locked) {
			return;
		}

		/* 同期通信の場合はロックを取得 */
		var context = this.queue.shift();
		this.locked = !context.async;

		/* 送信キューが空になった場合、送信スレッドを停止する */
		if (!this.queue.length) {
			this.senderThread.stop();
		}

		/* XMLHttpRequest にパラメータとコールバック関数を設定 */
		try {
			var xhr = this.getXMLHttpRequest();
			context.xhr = xhr;
			context.setStatus(maskat.comm.HttpContext.READY);
			
			var self = this;
			xhr.open(context.method, context.url, true);
			xhr.onreadystatechange = function(){
				self.processResponse(context);
			};
		} catch (e) {
			this.finishProcess(context, e);
			return;
		}
		
		/*
		 * リクエストヘッダの設定:
		 * 
		 * XHR をプールから再利用した場合でも、open() メソッドを実行すると
		 * 以前に設定されていたリクエストヘッダはクリアされる
		 */
		var headers = context.requestHeaders;
		if (headers) {
			for (var n in headers) {
				xhr.setRequestHeader(n, headers[n]);
			}
		}

		/*
		 * 応答待ちリストが空の場合、タイムアウト監視スレッドは停止状態なので
		 * スレッドを開始する
		 */
		if (!this.waitings.length) {
			this.timeoutThread.start();
		}

		/* HTTP 要求を送信し、コンテキスト応答待ちリストに追加する */
		this.waitings.push(context);
		context.sentAt = (new Date()).getTime();
		
		xhr.send(context.requestMessage);
        
		try {
			context.setStatus(maskat.comm.HttpContext.WAIT);
			this.processResponse(context);
		} catch (e) {
			/*
			 * openされたXHRはsend完了もしくはabortされるまで占有されます。
			 * FireFoxではopen後のabortで発生するreadyStateはcomplete(4)
			 */
			if (context.xhr) {
				try {
					context.xhr.onreadystatechange = null;
				} catch (ex) {
					context.xhr.onreadystatechange = function() {};
				}
				context.xhr.abort();
			}
			this.finishProcess(context, e);
			return;
		}
		
		/*
		 * Firefox の同期通信は送信処理が終わるまで send 関数から復帰せず、
		 * onreadystatechange 関数を呼び出さない
		 */
/*
		if (navigator.appName == "Netscape" && !context.async) {
			if (context.status == maskat.comm.HttpContext.WAIT) {
				this.processResponse(context);
			}
		}
 */
	},
	
	/**
	 * サーバからの HTTP 応答時に通信ステータスを確認し、送信コンテキストの
	 * コールバック関数を実行します。
	 *
	 * @param context 送信コンテキスト
	 */
	processResponse: function(context) {
		var xhr = context.xhr;
		/*
		 * FireBugを利用している場合、処理済リクエストのonreadystatechange
		 * が再度呼び出される。context.xhrは既に削除されており存在しない。
		 */
		if (!xhr || context.status != maskat.comm.HttpContext.WAIT) {
			return;
		}

		if (xhr.readyState == 4) {
			try {
				if (xhr.status == 200) {
					maskat.util.CrossBrowser.checkParseError(xhr.responseXML);

					context.responseMessage = xhr.responseText;
					context.responseXML = xhr.responseXML;
					context.responseHeaders = this.parseResponseHeaders(
						xhr.getAllResponseHeaders());
					
					/*
					 * ロック中 (同期通信中) に非同期処理が完了した場合には
					 * 同期通信の完了を待つ
					 */
					if (context.async && this.locked) {
						this.completed.push(context);
					} else {
						context.setStatus(maskat.comm.HttpContext.COMPLETE);
					}
				} else if (xhr.status != 0) {
					maskat.util.CrossBrowser.checkHttpError(xhr, { url: context.url });
				}
			} catch (e) {
				var error = e;
				if (!(e instanceof maskat.lang.Error)) {
					error = maskat.util.CrossBrowser.getHttpError(xhr);
					if (!error) {
						error = e;
					}
				}
			} finally {
				this.finishProcess(context, error);
			}
		}
	},
	
	/**
	 * レスポンスヘッダ文字列を解析し、ヘッダ名と値のペアを要素とする配列を
	 * 返します。
	 *
	 * @param headers すべてのレスポンスヘッダを含んだ文字列
	 * <pre>
	 *     "name1:value1\nname2:value2\n..."
	 * </pre>
	 *
	 * @returns レスポンスヘッダの解析済み配列
	 * <pre>
	 *     {
	 *         name1: "value1",
	 *         name2: "value2",
	 *         ...
	 *     }
	 * </pre>
	 */
	parseResponseHeaders: function(headers) {
		if (!headers) {
			return null;
		}

		var result = {};
		var lines = headers.split(/\r?\n/);
		var pair;
		for (var i = 0; i < lines.length; i++) {
			if (lines[i].length > 1) {
				/* レスポンスヘッダ行を名前／値ペアに分割する */
				pair = lines[i].split(/\s*:\s*/);
				if (typeof(pair[1]) == "undefined") {
					pair[1] = "";
				}
				if (result[pair[0]]) {
					result[pair[0]] = result[pair[0]] + " " +
						pair[1].replace(/, /g, " ");
				} else {
					result[pair[0]] = pair[1].replace(/, /g, " ");
				}
			}
		}
		return result;
	},

	/**
	 * 指定した送信コンテキストに関する通信処理を終了し、使用していたリソース
	 * を解放します。
	 *
	 * @param context 通信が終了したコンテキスト
	 * @param error 発生したエラー（正常時はundefined)
	 */
	finishProcess: function(context, error) {
		
		/* XMLHttpRequest をプールに戻す */
		if (context.xhr) {
			this.pool.push(context.xhr);
			delete context.xhr;
		}
		/* 応答待ちリストからコンテキストを削除 */
		for (var i = 0; i < this.waitings.length; i++) {
			if (context === this.waitings[i]) {
				this.waitings.splice(i, 1);
				break;
			}
		}
		
		/* 応答待ちリストが空の場合、タイムアウト監視スレッドを停止 */
		if (!this.waitings.length) {
			this.waitings.length = 0;
			this.timeoutThread.stop();
			this.shrinkPool();
		}

		/*
		 * 同期通信が終了した場合、ロックフラグの解除を待っている非同期通信の
		 * 結果をリリースする
		 */
		if (!context.async) {
			for (var i = 0; i < this.completed.length; i++) {
				try {
					this.completed[i].setStatus(maskat.comm.HttpContext.COMPLETE);
				} catch (e) {
					this.completed[i].error = e;
					try {
						this.completed[i].setStatus(maskat.comm.HttpContext.ERROR);
					} catch (ex) { /* suppress */ }
				}
			}
			this.completed.length = 0;
			this.locked = false;
		}
		/*
		 * エラー発生による finishProcess 呼び出し
		 * 処理中断 (InterruptedError) による呼び出しはステータス変更を行わない
		 */
		if (error && !(error instanceof maskat.lang.InterruptedError)) {
			context.error = error;
			try {
				context.setStatus(maskat.comm.HttpContext.ERROR);
			} catch (e) { /* suppress */ }
		}
	},

	/**
	 * HTTP 応答待ちのキューを走査し、タイムアウト時間が経過している場合に
	 * タイムアウト処理を発生させます。
	 *
	 * <p>
	 * このメソッドはタイムアウト処理スレッドから、パラメータで指定された
	 * インターバルで起動されます。
	 * </p>
	 */
	checkTimeout: function() {
		var context;
		for (var i = 0; i < this.waitings.length; i++) {
			context = this.waitings[i];
			if (context.isTimeout()) {
				try {
					context.setStatus(maskat.comm.HttpContext.TIMEOUT);
				} catch (e) {
					context.error = e;
				}

				/*
				 * Internet Explorer で abort メソッドを実行した場合、
				 * onreadystatechange 関数が呼び出されます。このとき xhr の
				 * status プロパティは 0 に設定されます。
				 *
				 * FireFox では NS_ERROR_NOT_AVAILABLE エラーが発生するため、
				 * onreadystatechange関数を無効にしてから abort メソッドを
				 * 実行します。
				 */
				try {
					context.xhr.onreadystatechange = null;
				} catch (ex) {
					context.xhr.onreadystatechange = function() {};
				}
				context.xhr.abort();
				this.finishProcess(context);
			}
		}
	}
});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * Ajax クライアントの内部で実行される個々の HTTP 要求・応答の組み合わせと
 * 通信状態をオブジェクト (送信コンテキスト) として表現します。
 *
 * <p>
 * 送信コンテキストは以下のいずれかの状態を持ちます:
 * </p>
 * <ul>
 *   <li>INIT: 初期状態</li>
 *   <li>READY: HTTP 要求の送信準備が完了した状態</li>
 *   <li>WAIT: HTTP 要求の応答待ちの状態</li>
 *   <li>COMPLETE: HTTP 要求の応答を受信した状態を表す定数</li>
 *   <li>ERROR: HTTP 要求の処理時にエラーが発生した状態</li>
 * </ul>
 *
 * <p>
 * 送信コンテキストの使用例を以下に示します。この例ではサーバに対してテキスト
 * ファイルの取得を非同期で要求し、応答の受信時にアラードダイアログでその内容
 * を表示します。
 * </p>
 * <pre>
 * var context = new maskat.comm.HttpContext("GET", "/foo.txt", { async: true });
 * context.onComplete = function() {
 *     alert(this.responseMessage);
 * };
 * context.send();
 * </pre>
 *
 * @name maskat.comm.HttpContext
 */ 
maskat.lang.Class.declare("maskat.comm.HttpContext", {

	_static: {
		/** @scope maskat.comm.HttpContext */

		/** 初期状態を表す定数 */
		INIT: 1,

		/** HTTP 要求の送信準備が完了した状態を表す定数 */
		READY: 2,

		/** HTTP 要求の応答待ちの状態を表す定数 */
		WAIT: 3,

		/** HTTP 要求の応答を受信した状態を表す定数 */
		COMPLETE: 4,

		/** HTTP 要求の処理時にエラーが発生した状態を表す定数 */
		ERROR: 5,

		/** HTTP 要求の応答待ちタイムアウトが発生した状態を表す定数 */
		TIMEOUT: 6
	},
	
	/** @scope maskat.comm.HttpContext.prototype */
	
	/**
	 * コンストラクタです。
	 *
	 * @param method HTTP 要求メソッド ("GET", "POST", "UPDATE", "DELETE" など)
	 * @param url 接続先の URL
	 * @param param 通信パラメータ
	 * <pre>
	 * {
	 *     async:
	 *         同期・非同期通信を制御するフラグ (true: 非同期／false: 同期) 
	 *     timeout:
	 *         タイムアウト時間(ms)
	 *         0 または未指定の場合はタイムアウトを発生させない
	 *     requestHeaders:
	 *         HTTP 要求ヘッダを格納したハッシュ
	 * }
	 * </pre>
	 */
	initialize: function(method, url, param){
		this.status = maskat.comm.HttpContext.INIT;
		
		this.url = url;
		this.method = method || "GET";
		this.async = param.async;
		this.sentAt = null;
		this.timeout = param.timeout? param.timeout : 0;
		this.requestHeaders = {};
		this.requestMessage = param.request;
		this.responseHeaders = [];
		this.responseMessage = "";
		this.error = null;
		
		this.requestHeaders["Content-Type"] = "application/xml";
		this.requestHeaders["If-Modified-Since"] = "Thu, 01 Jun 1970 00:00:00 GMT";

		var headers = param.headers;
		if (headers) {
			for (var i = 0; i < headers.length; i++) {
				this.requestHeaders[headers[i].name] = headers[i].value;
			}
		}
	},

	/**
	 * HTTP 要求をサーバに送信します。
	 */
	send: function() {
		maskat.comm.CommunicationManager.getInstance().enqueue(this);
	},

	/**
	 * コンテキストの通信ステータスを設定します。
	 *
	 * @param status 新しい通信ステータス
	 */
	setStatus: function(status) {
		if (this.status != status) {
			try {
				this.stateChanged(status);
			} finally {
				this.status = status;
			}
		}
	},

	/**
	 * このコンテキストの通信ステータスが変更されたことを通知します。
	 *
	 * @param status 新しい通信ステータス
	 */
	stateChanged: function(status){
		switch (status) {
		case maskat.comm.HttpContext.READY:
			this.onReady();
			break;
		case maskat.comm.HttpContext.WAIT:
			this.onWait();
			break;
		case maskat.comm.HttpContext.COMPLETE:
			this.onComplete();
			break;
		case maskat.comm.HttpContext.TIMEOUT:
			this.onTimeout();
			break;
		case maskat.comm.HttpContext.ERROR:
			this.onError();
			break;
		default:
			break;
		}
	},

	/**
	 * 送信キューがキューイングされた場合に呼び出されるコールバックメソッドです。
	 */
	onReady: function(){
		/* NOP */
	},
	
	/**
	 * HTTP 要求を送信する直前に呼び出されるコールバックメソッドです。
	 */
	onWait: function(){
		/* NOP */
	},
	
	/**
	 * HTTP 応答を受信した直後に呼び出されるコールバックメソッドです。
	 */
	onComplete: function(){
		/* NOP */
	},
	
	/**
	 * HTTP 要求の応答待ちがタイムアウトした場合に呼び出されるコールバック
	 * メソッドです。
	 */
	onTimeout: function(){
		/* NOP */
	},

	/**
	 * 通信エラー発生時に呼び出されるコールバックメソッドです。
	 *
	 * このメソッドの実行時は、コンテキストの error プロパティで発生した
	 * エラーを参照することができます。
	 */
	onError: function(){
		/* NOP */
	},

	/**
	 * HTTP 要求の応答待ちがタイムアウトしているかどうかを返します。
	 *
	 * @returns 応答待ちがタイムアウトしている場合は true
	 */
	isTimeout: function(){
		if (!this.sentAt || !this.timeout) {
			return false;
		}
		var now = (new Date()).getTime();
		return (now - this.sentAt) >= this.timeout;
	}
	
});


/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class イベント発生元のマスカット部品名、およびイベントの種類から、適切なコマンドをアプリケーションに追加するクラスです。
 *
 * @name maskat.control.ApplicationController
 */ 
maskat.lang.Class.declare("maskat.control.ApplicationController", {

	/** @scope maskat.control.ApplicationController.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.app = null;
		this.init = null;
		this.commands = {};
	},

	setApplication: function(app){
		this.app = app;
	},

	getCommand: function(event){
		var path = [event.layoutId, event.widgetId, event.type, (event.branch || "")];
		var command = maskat.lang.Object.find(path, this.commands);
		
		if (!command) {
			path[0] = "";
			command = maskat.lang.Object.find(path, this.commands);
		}
		return command;
	},

	addCommand: function(command){
		var branch = command.branch || "";
		var scope = maskat.lang.Object.create(
			[command.layoutId, command.widgetId, command.eventType], this.commands);
		scope[branch] = command;
	},

	start: function() {
		if (this.init) {
			this.init.execute(this.app);
		}
	},

	handleEvent: function(event){
		var command = this.getCommand(event);
		if (command) {
			command.execute(this.app);
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 実行する関数を持つクラスです。
 *
 * @name maskat.control.CallFunctionCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.CallFunctionCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.CallFunctionCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(name){
		this.name = name;
	},
	
	execute: function(){
		this.func();
		return false;
	}
});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class イベント発生元のマスカット部品名、およびイベントの種類のコマンドを定義するクラスです。
 *
 * @name maskat.control.Command
 */
maskat.lang.Class.declare("maskat.control.Command", {

	/* @scope maskat.control.Command.prototype */

	execute: function(app) {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class イベント発生元のマスカット部品名、およびイベントの種類から、適切なコマンド集合を実行するクラスです。
 *  
 * @name maskat.control.CommandSet
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.CommandSet")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.CommandSet.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function() {
		this.priorCommands = null;
		this.commands = null;
	},

	execute: function(app) {
		/* 優先コマンド (JavaScript/CSS ロード) の実行 */
		if (this.priorCommands) {
			for (var i = 0; i < this.priorCommands.length; i++) {
				this.executeCommand(app, this.priorCommands[i]);
			}
		}	

		/* 通常コマンドの実行 */
		if (this.commands) {
			for (var j = 0; j < this.commands.length; j++) {
				this.executeCommand(app, this.commands[j]);
			}
		}	
	
	},

	executeCommand: function(app, command){
		try {
			command.execute(app);
		} catch (e) {
			maskat.app.handleError(e);
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定したレイアウトを非表示にするクラスです。
 *
 * @name maskat.control.HideLayoutCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.HideLayoutCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.HideLayoutCommand.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(layoutId) {
		this.layoutId = layoutId;
	},

	execute: function(app) {
		var layout = app.getLayout(this.layoutId);
		if (layout) {
			layout.hide();
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定した JavaScript をコンテナ HTML に読み込むクラスです。
 *
 * @name maskat.control.LoadJavaScriptCommand
 */ 
maskat.lang.Class.declare("maskat.control.LoadJavaScriptCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.LoadJavaScriptCommand.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function() {
		this.url = null;
		this.async = false;
	},

	execute: function(app) {
		if (!this.url.match(/.js$/)) {
			this.url = this.url + ".js";
		}

		app.loadJavaScript(this.url, this.async);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定したキーバインドを読み込むクラスです。
 *
 * @name maskat.control.LoadKeyBindingCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.LoadKeyBindingCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.LoadKeyBindingCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.url = null;
	},

	execute: function(app){
		app.loadKeyBinding(this.url);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定した URL からレイアウト定義およびイベント定義 XMLを読み込むクラスです。
 *
 * @name maskat.control.LoadLayoutCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.LoadLayoutCommand")
	.extend("maskat.control.Command", {
	
	/** @scope maskat.control.LoadLayoutCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function() {
		this.layoutURL = null;
		this.eventURL = null;
		this.elementId = null;
		this.visible = null;
	},

	execute: function(app) {
		if (!this.eventURL) {
			/*
			 * レイアウト定義 XML の URL を以下の規則で書き換えてイベント定義
			 * XML の URL を決定する
			 * 
			 * 1) レイアウト定義 XML の拡張子の直前に "_e" を挿入
			 *    layout.xml    -> layout_e.xml
			 *    layout.xml.gz -> layout_e.xml.gz
			 *    layout.do     -> layout_e.do
			 *    
			 * 2) レイアウト定義 XML に拡張子がない場合、末尾に "_e" を追加
			 *    layout -> layout_e 
			 */
			this.eventURL = this.layoutURL.replace(/(\.([^/]*))?$/, "_e$1");
		}
		
		var element = document.getElementById(this.elementId);
		app.loadLayout(this.layoutURL, this.eventURL, element, this.visible);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定した CSS スタイルシートをコンテナ HTML に読み込むクラスです。
 *
 * @name maskat.control.LoadStyleSheetCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.LoadStyleSheetCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.LoadStyleSheetCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function() {
		this.url = null;
	},

	execute: function(app) {
		if (!this.url.match(/.css$/)) {
			this.url = this.url + ".css";
		}

		app.loadStyleSheet(this.url);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 対象にフォーカスをあてるターゲットを持つクラスです。
 *
 * @name maskat.control.MaskatEventCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.MaskatEventCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.MaskatEventCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(target, type){
		this.target = target;
		this.type = type;
	},
	
	execute: function(){
		this.target.notifyEvent(this.type);
		return false;
	}
	
});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class フォーカスを移動する種別を持つクラスです。
 *
 * @name maskat.control.MoveFocusCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.MoveFocusCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.MoveFocusCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(type){
		this.type = type;
	},
	
	execute: function(event){
		maskat.key.KeyEventManager.getInstance().moveFocus(event, this.type);
		return false;
	}

});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 利用可能状態を変更するターゲットと変更内容を持つクラスです。
 *
 * @name maskat.control.SetEnableCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.SetEnabledCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.SetEnableCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(target, enabled){
		this.target = target;
		this.enabled = enabled;
	},
	
	execute: function(){
		this.target.setEnabled(this.enabled);
		return false;
	}

});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class フォーカスをあてるターゲットを持つクラスです。
 *
 * @name maskat.control.SetFocusCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.SetFocusCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.SetFocusCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(target){
		this.target = target;
	},
	
	execute: function(){
		this.target.setFocus();
		return false;
	}

});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 可視化を変更するターゲットと変更内容を持つクラスです。
 *
 * @name maskat.control.SetVisibleCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.SetVisibleCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.SetVisibleCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(target, visible){
		this.target = target;
		this.visible = visible;
	},
	
	execute: function(){
		this.target.setVisible(this.visible);
		return false;
	}

});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定したレイアウトを表示するクラスです。
 *
 * @name maskat.control.ShowLayoutCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.ShowLayoutCommand")
	.extend("maskat.control.Command", {

	/** @scope maskat.control.ShowLayoutCommand.prototype  */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(layoutId) {
		this.layoutId = layoutId;
	},

	execute: function(app) {
		var layout = app.getLayout(this.layoutId);
		if (layout) {
			layout.show();
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 画面遷移定義 XML からコントローラを読み込んで実行するクラスです。
 *
 * @name maskat.control.TransitionXMLReader
 * @extends maskat.xml.XMLObjectBinder
 */ 
maskat.lang.Class.declare("maskat.control.TransitionXMLReader")
	.extend("maskat.xml.XMLObjectBinder", {

	_static: {
	
		/** @scope maskat.control.TransitionXMLReader */
		
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.control.TransitionXMLReader.prototype  */
	
	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.base({
			"#document": {
				children: {
					transitionDef: {}
				}
			},

			transitionDef: {
				type: maskat.control.ApplicationController,
				children: {
					init: { property: "init" },
					transition: {
						property: "transitions",
						method: "addCommand",
						repeat: true
					}
				}
			},

			init: {
				type: maskat.control.CommandSet,
				children: {
					loadJS: { property: "priorCommands", repeat: true },
					loadCSS: { property: "priorCommands", repeat: true },
					loadLayout: { property: "commands", repeat: true },
					loadKeyBinding: { property: "commands", repeat: true }
				}
			},

			transition: {
				type: maskat.control.CommandSet,
				attributes: {
					layout: { type: "string", defaultValue: "", property: "layoutId" },
					component: { type: "string", required: true, property: "widgetId" },
					event: { type: "string", required: true, property: "eventType" },
					branch: { type: "string" }
				},
				children: {
					loadJS: { property: "priorCommands", repeat: true },
					loadCSS: { property: "priorCommands", repeat: true },
					loadLayout: { property: "commands", repeat: true },
					removeLayout: { property: "commands", repeat: true },
					showLayout: { property: "commands", repeat: true },
					hideLayout: { property: "commands", repeat: true },
					loadKeyBinding: { property: "commands", repeat: true }
				}
			},
			
			loadLayout: {
				type: maskat.control.LoadLayoutCommand,
				attributes: {
					xmlFile: { type: "string", required: true, property: "layoutURL" },
					eventXmlFile: { type: "string", property: "eventURL" },
					target: { type: "string", required: true, property: "elementId" },
					show: { type: "boolean", defaultValue: false, property: "visible" }
				}
			},

			loadJS: {
				type: maskat.control.LoadJavaScriptCommand,
				attributes: {
					fileName: { type: "string", required: true, property: "url"  }
				}
			},

			loadCSS: {
				type: maskat.control.LoadStyleSheetCommand,
				attributes: {
					fileName: { type: "string", required: true, property: "url"  }
				}
			},

			showLayout: {
				type: maskat.control.ShowLayoutCommand,
				attributes: {
					layout: { type: "string", required: true, property: "layoutId"  }
				}
			},

			hideLayout: {
				type: maskat.control.HideLayoutCommand,
				attributes: {
					layout: { type: "string", required: true, property: "layoutId"  }
				}
			},

			removeLayout: {
				type: maskat.control.UnloadLayoutCommand,
				attributes: {
					layout: { type: "string", required: true, property: "layoutId" }
				}
			},

			loadKeyBinding: {
				type: maskat.control.LoadKeyBindingCommand,
				attributes: {
					url: { type: "string", required: true }
				}
			}
		});
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 指定したレイアウトを解放するクラスです。
 *
 * @name maskat.control.UnloadLayoutCommand
 * @extends maskat.control.Command
 */ 
maskat.lang.Class.declare("maskat.control.UnloadLayoutCommand")
	.extend("maskat.control.Command", {
	
	/** @scope maskat.control.UnloadLayoutCommand.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(layoutId) {
		this.layoutId = layoutId;
	},

	execute: function(app) {
		app.unloadLayout(this.layoutId);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * マスカットアプリケーションの実行状態を管理するクラスです。
 *
 * <p>
 * マスカットアプリケーションの起動プロセス (maskat.bootstrap) によって、
 * コンテナ HTML の読み込み完了時にこのクラスの唯一のインスタンスが生成
 * されて maskat.app プロパティへ格納されます。
 * </p>
 * <p>
 * ユーザ定義の JavaScript 関数では、この maskat.app オブジェクトを経由
 * してレイアウトやマスカット部品を操作することができます。
 * </p>
 *
 * @name maskat.core.Application
 */ 
maskat.lang.Class.declare("maskat.core.Application")
	.extend("maskat.core.Plugin", {

	_static: {
		/** @scope maskat.core.Application */
		
		/**
		 * クラスの静的イニシャライザです。
		 *
		 * Application はマスカットのプラグインとして実装されているため、
		 * プラグインマネージャにクラスを登録します。
		 */
		initialize: function() {
			maskat.core.Plugin.register(this);
		}
	},

	/** @scope maskat.core.Application.prototype */

	/**
	 * 新しいアプリケーションのインスタンスを生成します。
	 */
	initialize: function(){
		this.base();
	
		this.layouts = {};
		this.stylesheets = null;
		this.scripts = null;
		this.controller = null;
		this.error = null;
		
		this.properties = new maskat.util.Properties({
			"comm.sendInterval": {
				type: "number",
				defaultValue: 100
			},
			"comm.timeoutInterval": {
				type: "number",
				defaultValue: 500
			},
			"comm.poolSize": {
				type: "number",
				defaultValue: 2
			},
			"controller.src": {
				type: "string",
				defaultValue: "transition.xml"
			},
			"dialog.factory": {
				type: "string",
				defaultValue: "maskat.ui.JavaScriptDialogFactory"
			},
			"log.popup.focus": {
				type: "boolean",
				defaultValue: false
			},
			"log.default.level": {
				type: "string",
				defaultValue: "INFO"
			},
			"log.factory": {
				type: "string",
				defaultValue: "maskat.log.SimpleLogFactory"
			},
			"variable.scope": {
				type: "enum",
				defaultValue: "global",
				values: [ "global", "layout" ]
			},
			"variable.widget": {
				type: "enum",
				defaultValue: "unwrapped",
				values: [ "wrapped", "unwrapped", "none" ]
			}
		});
	},

	/**
	 * プラグイン識別子を返します。
	 * 
	 * @returns プラグイン識別子
	 */
	getPluginId: function() {
		return "core";
	},

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

	isLoaded: function(){
		return this.loaded;
	},

	load: function(){
		this.loaded = true;
	
		/* 通信マネージャのパラメータを設定 */
		var comm = maskat.comm.CommunicationManager;
		comm.sendInterval = this.getProperty("comm.sendInterval");
		comm.timeoutInterval = this.getProperty("comm.timeoutInterval");
		comm.poolSize = this.getProperty("comm.poolSize");

		/* キーイベントハンドラを設定 */
		var keyHandler = function(event) {
			maskat.key.KeyEventManager.getInstance().handle(event);
		};
		maskat.util.CrossBrowser.addEventListener(document, "keydown", keyHandler);
		maskat.util.CrossBrowser.addEventListener(document, "keypress", keyHandler);
		maskat.util.CrossBrowser.addEventListener(document, "keyup", keyHandler);
	},
	
	start: function() {
		/* ログファクトリを設定 */
		var className, type;
		try {
			className = this.getProperty("log.factory");
			type = maskat.util.Converter.convert("function", className);
			maskat.log.LogFactory.factory = new type();
		} catch (e) {
			throw new maskat.lang.Error("LOG_FACTORY_INIT_FAILED",
				{ className: className }, e);
		}

		/* ダイアログファクトリを設定 */
		try {
			className = this.getProperty("dialog.factory");
			type = maskat.util.Converter.convert("function", className);
			maskat.ui.Dialog.factory = new type();
		} catch (e) {
			throw new maskat.lang.Error("DIALOG_FACTORY_INIT_FAILED",
				{ className: className }, e);
		}
	},

	/**
	 * アプリケーションの実行を開始します。
	 */
	run: function(){
		/* アプリケーションコントローラを起動 */
		try {
			this.loadController(this.getProperty("controller.src"));
		} catch (e) {
			maskat.app.handleError(e);
		}
		if (this.controller) {
			this.controller.start();
		}
	},

	/**
	 * 画面遷移定義 XML を読み込んで、アプリケーションコントローラを
	 * 構成します。
	 * 
	 * @param url 画面遷移定義 XML の URL
	 */
	loadController: function(url){
		var controller;
		try {
			controller = maskat.control.TransitionXMLReader.getInstance().load(url);
			controller.setApplication(this);
			this.controller = controller;
		} catch (e) {
			var cause = e.cause;
			if (e.cause && e.cause.key == "HTTP_404") {
				/*
				 * 画面遷移定義 XML が見つからない (HTTP ステータス 404) の
				 * 場合はエラーではなく、警告メッセージを表示
				 */
				var logger = maskat.log.LogFactory.getLog("maskat.core");
				logger.warn(e.getMessages ? e.getMessages().join("\n") : e.message);
			} else {
				/* その他の例外は呼び出し元にスロー */
				throw e;
			}
		}
	},
	
	/**
	 * 指定したレイアウト ID に対応するレイアウトを取得します。
	 * 
	 * @param layoutId レイアウト ID
	 * 
	 * @returns 指定したレイアウト ID に対応するレイアウト、
	 *          そのようなレイアウトが存在しない場合は undefined
	 */
	getLayout: function(layoutId){
		return this.layouts ? this.layouts[layoutId] : undefined;
	},
	
	/**
	 * 指定したレイアウト ID に対応するレイアウトをロードします。
	 * 
	 * @param layoutURL レイアウト定義 XML の URL
	 * @param eventURL イベント定義 XML の URL
	 * @param element レイアウトをロードする HTML 要素
	 * @param visible レイアウトを表示する場合は true、非表示の場合は false
	 * 
	 * @returns ロードされたレイアウト
	 */
	loadLayout: function(layoutURL, eventURL, element, visible){
		var layout;

		/* すでにロード済みの場合、レイアウトの表示／非表示を設定する */
		for (var name in this.layouts) {
			layout = this.layouts[name];
			if (layout.url == layoutURL) {
				if (visible) {
					layout.show();
				} else {
					layout.hide();
				}
				return layout;
			}
		}	

		/* レイアウトを新規にロードする */
		layout = maskat.layout.LayoutXMLReader.getInstance().load(layoutURL);
		layout.url = layoutURL;
		this.layouts[layout.getWidgetId()] = layout;

		/* レイアウト変数のスコープを設定 */
		switch (this.getProperty("variable.scope")) {
		case "global":
			layout.scope = window;
			break;
		case "layout":
			layout.scope = {};
			break;
		}

		/* レイアウト変数へのマスカット部品の登録方法を設定 */
		layout.widgetVariableType = this.getProperty("variable.widget");

		if (eventURL) {
			var dispatcher = maskat.event.EventXMLReader.getInstance().load(eventURL);
			layout.addEventListener(dispatcher);
			dispatcher.setController(this.controller);
		}

		layout.load(element, visible);
		return layout;
	},
	
	/**
	 * 指定したレイアウト ID に対応するレイアウトをアンロードします。
	 * 
	 * @param layoutId レイアウト ID
	 */
	unloadLayout: function(layoutId){
		var layout = this.getLayout(layoutId);
		if (layout) {
			layout.unload();
			delete this.layouts[layoutId];
		}
	},
	
	/**
	 * 現在の HTML 文書に新しい CSS スタイルシートを読み込みます。
	 * 
	 * @param url CSS スタイルシート (*.css) の URL
	 */
	loadStyleSheet: function(url){
		if (this.stylesheets && this.stylesheets[url]) {
			return;
		}
		
		/* head 要素の子要素として style 要素を追加 */
		var link = document.createElement("link");
		link.rel = "stylesheet";
		link.type = "text/css";
		link.href = url;
		
		var head = document.getElementsByTagName("head")[0];
		head.appendChild(link);
		
		if (!this.stylesheets) {
			this.stylesheets = {};
		}
		this.stylesheets[url] = link;
	},
	
	/**
	 * 現在の HTML 文書に新しい JavaScript ファイルを読み込みます。
	 *
	 * <p> 
	 * 非同期読み込みの場合、このメソッドの呼び出し直後には JavaScript が
	 * 解析されていないため、読み込んだスクリプトの内容を利用できません。
	 * このため、setTimeout 関数などを用いて解析を待つ必要があります。
	 * </p>
	 * 
	 * @param url JavaScript ファイル (*.js) の URL
	 * @param async 非同期読み込みの場合は true
	 */
	loadJavaScript: function(url, async){
		if (async) {
			if (this.scripts && this.scripts[url]) {
				return;
			}

			/* head 要素の子要素として script 要素を追加 */
			var script = document.createElement("script");
			script.type = "text/javascript";
			script.src = url;
			var head = document.getElementsByTagName("head")[0];
			head.appendChild(script);
			
			if (!this.scripts) {
				this.scripts = {};
			}
			this.scripts[url] = script;
		} else {
			/*
			 * HTTP GET メソッドで取得した JavaScript ソースコードを
			 * グローバルスコープで評価する
			 */
			try {
				var source = maskat.util.CrossBrowser.getTextFrom(url);
				this.loadJavaScriptFromString(source);
			} catch (e) {}
		}
	},

	/**
	 * 現在の HTML 文書に JavaScript ソース文字列を読み込みます。
	 * 
	 * @param source JavaScript ソースコード文字列
	 */
	loadJavaScriptFromString: function(source){
		/* head 要素の子要素として script 要素を追加 */
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.text = source;
		var head = document.getElementsByTagName("head")[0];
		head.appendChild(script);
	},

	/**
	 * キーバインド定義 XML を読み込んで、レイアウトにキーイベントハンドラ
	 * を設定します。
	 * 
	 * @param url キーバインド定義XMLファイル の URL
	 */
	loadKeyBinding: function(url){
		var keybinding = maskat.key.KeyBindingXMLReader.getInstance().load(url);
		var layout = maskat.app.getLayout(keybinding.layout);

		/* デフォルト (グローバル) のキーバインドを設定 */
		var binds = keybinding["default"] && keybinding["default"].binds; 
		if (typeof(layout) == "undefined" ){
			throw new maskat.lang.Error("KEYBINDING_ERROR", { widget: keybinding.layout });
		} else if (typeof(binds) != "undefined") {
			for (var i = 0; i < binds.length; i++) {
				layout.addKeybind(binds[i]);
			}
		}
		
		/* コンポーネントごとのキーバインドを設定 */
		if (typeof(keybinding.components) != "undefined") {
			for (var widgetId in keybinding.components) {
				var widget = layout.getWidget(widgetId);
				var binds = keybinding.components[widgetId].binds;

				if (typeof(widget) == "undefined" ){
					throw new maskat.lang.Error("KEYBINDING_ERROR", { widget: widgetId });
				} else if(typeof(binds) != "undefined"){
					for (var j = 0; j < binds.length; j++) {
						widget.addKeybind(binds[j]);
					}
				}
			}
		}
	},

	/**
	 * マスカットアプリケーションの実行中に発生したエラーを処理します。
	 *
	 * @param error エラーオブジェクト
	 */
	handleError: function(error) {
		if (maskat.error && maskat.error instanceof maskat.lang.Error) {
			if (maskat.error.getCauseError() != error) {
				/* エラーを maskat.error プロパティに記憶 */
				maskat.error = error;
			}
		} else {
			/* エラーを maskat.error プロパティに記憶 */
			maskat.error = error;
		}
		/* エラーの根本原因を再スローし、window.onerror 関数へ引き継ぐ */
		var e = error;
		while (e.cause) {
			e = e.cause;
		}
		throw e;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * マスカットフレームワークにおけるプラグインを定義する抽象クラスです。
 * プラグインは以下の規約に従って作成する必要があります:
 *
 * <h2>プラグイン識別子</h2>
 * <p>
 * プラグインを一意に識別する任意の文字列です。プラグイン実装クラスの
 * getPluginId() メソッドはプラグイン識別子を返します。"core" という
 * 文字列はマスカットフレームワークのために予約されており、プラグイン
 * 識別子として利用することはできません。
 * </p>
 *
 * <h2>プラグインの格納位置</h2>
 * <p>
 * マスカットフレームワークのフォルダの直下にプラグイン識別子と同じ名前
 * を持つフォルダを作成し、このフォルダ内に plugin.js というファイル名で
 * JavaScript ファイルを配置します。
 * </p>
 * <pre>
 *   例) プラグイン識別子 "myplugin" の格納位置
 *       maskat/myplugin/plugin.js
 * </pre>
 *
 * <h2>プラグイン実装クラス</h2>
 * <p>
 * プラグイン実装クラスは {@link maskat.core.Plugin} クラスを継承して実装
 * します。プラグイン実装者は plugin.js が読み込まれたさいに、
 * <code>maskat.core.Plugin</code> クラスのプラグインレジストリ (ハッシュ)
 * にプラグインを登録する必要があります。
 * </p>
 *
 * <h2>プラグインの動作状態</h2>
 * <p>プラグイン実装クラスのインスタンスはプラグインマネージャ
 * {@link maskat.core.PluginManager} によって管理され、動作状態に応じて
 * 以下のステータスが setStatus() メソッド経由で設定されます。
 * </p>
 * <ul>
 *   <li>INIT: プラグイン実装クラスをインスタンス化した直後の初期状態</li>
 *   <li>INSTALL: プラグインをプラグインマネージャに登録した直後の状態</li>
 *   <li>LOADING: プラグインの実行に必要なリソースを読み込んでいる状態</li>
 *   <li>READY: リソースの読み込みが完了し、実行可能な状態</li>
 *   <li>RUNNING: プラグインを実行中の状態</li>
 * </ul>
 * <p>
 * これらのステータスはプラグイン実装クラスの getStatus() メソッドで取得
 * できます。なお、setStatus() メソッドはプラグインマネージャ以外のクラス
 * から実行されることを想定していないプライベートな API です。
 * </p>
 *
 * @name maskat.core.Plugin
 */ 
maskat.lang.Class.declare("maskat.core.Plugin", {

	_static: {
		
		/** @scope maskat.core.Plugin */
	
		/** 初期状態 */
		INIT: 1,

		/** プラグインマネージャに登録した直後の状態を表す定数 */
		INSTALL: 2,

		/** リソース読み込み中の状態を表す定数 */
		LOADING: 3,

		/** 実行可能な状態を表す定数 */
		READY: 4,

		/** 実行中の状態を表す定数 */
		RUNNING: 5,

		/** プラグインのクラスを登録するレジストリ */
		registry: {},		

		/**
		 * レジストリにプラグインの実装クラスを登録します。
		 *
		 * プラグインの実装クラスは maskat.core.Plugin クラスを継承し、引数
		 * なしのコンストラクタを持っている必要があります。
		 * 
		 * @param clazz プラグインの実装クラス
		 */
		register: function(clazz) {
			var pluginId = clazz.prototype.getPluginId();
			this.registry[pluginId] = clazz;
		}
	},

	/** @scope maskat.core.Plugin.prototype */

	/**
	 * コンストラクタです。
	 */
	initialize: function() {
		this.status = maskat.core.Plugin.INIT;
		this.properties = new maskat.util.Properties();
	},

	/**
	 * プラグインの実行状態を返します。
	 *
	 * @returns プラグインの実行状態
	 */
	getStatus: function() {
		return this.status;
	},

	/**
	 * プラグインの実行状態を設定します。
	 *
	 * @param status プラグインの実行状態
	 */
	setStatus: function(status) {
		this.status = status;
	},

	/**
	 * プラグインのプロパティ値を取得します。
	 *
	 * @param key プロパティキー
	 * @returns 指定したキーに対応するプロパティ値 
	 */
	getProperty: function(key) {
		return this.properties.getProperty(key);
	}, 

	/**
	 * プラグインのプロパティを設定します。
	 *
	 * @param key プロパティキー
	 * @param value プロパティの値
	 */
	setProperty: function(key, value) {
		this.properties.setProperty(key, value);
	}, 

	/**
	 * プラグインのプロパティをプロパティキーと値が対になったオブジェクトで
	 * まとめて設定します。
	 *
	 * @param values プロパティキーと値が対になったオブジェクト 
	 */
	setProperties: function(values) {
		this.properties.setProperties(values);
	}, 

	/**
	 * プラグイン識別子を返します。
	 * 
	 * @returns プラグイン識別子
	 */
	getPluginId: function() {
		return null;
	},

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

	/**
	 * このプラグインの読み込みや実行が、別のプラグインの読み込みや実行に
	 * 依存している場合、依存先となるプラグインの識別子を配列で返します。
	 *
	 * @returns プラグイン識別子を要素とする配列、依存関係がなければ null
	 */
	getDependencies: function() {
		return null;
	},

	/**
	 * load() メソッドによるリソースの読み込みが完了しているかどうかを判定
	 * します。
	 * 
	 * </p>
	 * このメソッドはプラグインマネージャから一定の間隔で呼び出されるため、
	 * プラグインやアプリケーションの状態を変更するような副作用を持つべき
	 * ではありません。
	 * </p>
	 * 
	 * @returns ロードが完了していれば true、それ以外の場合は false
	 */
	isLoaded: function() {
		return true;
	},

	/**
	 * プラグインの実行に必要なリソースを読み込みます。
	 *
	 * <p>
	 * プラグイン実装クラスではこのメソッドを実装して、プラグインの実行に
	 * 必要な JavaScript や CSS スタイルシートなどのリソースを現在の HTML
	 * 文書に追加します。
	 * </p>
	 * <p>
	 * この API が呼ばれる段階では HTML 文書の読み込みが完了しているため、
	 * 以下の制約があります。
	 * </p>
	 * <ul>
	 *   <li>document.write(), document.writeln() を利用したドキュメントへの
	 *   書き込みはできない</li>
	 *   <li>window や document の onload イベントはすでに発生している</li>
	 * </ul>
	 *
	 * @see maskat.core.Application#loadStyleSheet
	 * @see maskat.core.Application#loadJavaScript
	 */
	load: function() {
		/* NOP */
	},

	/**
	 * プラグインの実行を開始します。
	 *
	 * <p>
	 * サードパーティ製の JavaScript ライブラリをプラグインで読み込んだ場合、
	 * start() メソッド内で onload イベントに対するハンドラ関数を呼び出して
	 * イベントをエミュレートする必要があるかもしれません。
	 * </p>
	 *
	 */
	start: function() {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * プラグインの実行状態を管理するクラスです。
 *
 * @name maskat.core.PluginManager
 */ 
maskat.lang.Class.declare("maskat.core.PluginManager", {

	_static: {
	
		/** @scope maskat.core.PluginManager */
	
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.core.PluginManager.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param properties プロパティ
	 */
	initialize: function(properties) {
		this.plugins = {};
		this.properties = properties || {};
		this.loader = new maskat.lang.Thread(this, this.process, null, 10);
	},

	/**
	 * プラグインごとのプロパティを指定した URL から JSON フォーマットで
	 * 読み込みます。
	 *
	 * <p> 
	 * プラグインのプロパティは {@link #launch} メソッドを実行する前に
	 * 設定する必要があります。
	 * </p>
	 *
	 * @param url プロパティファイル (JSON 形式) の URL
	 */
	loadProperties: function(url){
		var properties = maskat.util.CrossBrowser.loadJSONFrom(url);

		/* マスカット 2.0 形式のプロパティ形式をサポート */
		if (properties["maskat.core.plugins"]) {
			var newprop = properties["maskat.core.plugins"];
			delete properties["maskat.core.plugins"];
			
			newprop.core = { enabled: true };
			if (properties["maskat.ui.dialog.factory"]) {
				newprop.core["dialog.factory"] = properties["maskat.ui.dialog.factory"];
				delete properties["maskat.ui.dialog.factory"];
			}

			for (var key in properties) {
				newprop.core[key.substring(7)] = properties[key];
				delete properties[key];
			}
			properties = newprop;
		}

		for (var pluginId in properties) {
			this.setProperties(pluginId, properties[pluginId]);
		}
	},

	/**
	 * 指定したプラグインのプロパティを設定します。
	 *
	 * <p> 
	 * プラグインのプロパティは {@link #launch} メソッドを実行する前に
	 * 設定する必要があります。
	 * </p>
	 * 
	 * @param pluginId プラグイン識別子
	 * @param properties プラグインのプロパティ
	 */
	setProperties: function(pluginId, properties){
		if (!this.properties[pluginId]) {
			this.properties[pluginId] = properties;
		} else {
			maskat.lang.Object.populate(this.properties[pluginId], properties);
		}
	},

	/**
	 * プロパティで有効化されているすべてのプラグインをインストールします。
	 * それらのプラグインがすべて実行状態になった段階で、引数で指定された
	 * コールバック関数を実行します。
	 *
	 * @param onReady コールバック関数
	 */
	launch: function(onReady) {
		/* 有効化されているすべてのプラグインをインストール */
		for (var pluginId in this.properties) {
			if (this.properties[pluginId].enabled) {
				this.installPlugin(pluginId);
			}
		}

		/* プラグインローダーのスレッドを起動 */
		if (!this.loader.isRunning()) {
			if (typeof(onReady) == "function") {
				this.onReady = onReady;
			}
			this.loader.start();
		}
	},

	/**
	 * プラグインローダーのスレッドから一定間隔で実行される内部メソッドです。
	 *
	 * <p>
	 * プラグインマネージャに登録されているすべてのプラグイン識別子について
	 * 以下の処理を実行します。
	 * </p>
	 * <ol>
	 *   <li>プラグインマネージャにプラグイン識別子に対応するプラグインの
	 *   インスタンスが登録されていない場合、プラグインのインストール処理
	 *   を行います。</li>
	 *   <li>プラグインがインストール済み状態 (INSTALL) の場合、プラグイン
	 *   のロード処理を実行します。</li>
	 *   <li>プラグインがロード中 (LOADING) の場合、読み込みが完了している
	 *   かどうかを確認します。読み込みが完了していれば、プラグインの状態
	 *   を READY に設定します。</li>
	 *   <li>登録されているすべてのプラグインが実行可能状態 (READY) に到達
	 *   した場合、プラグインローダーのスレッドを停止し、onReady() メソッド
	 *   をコールバックします。</li>
	 * </ol>
	 */
	process: function() {
		try {
			/* 各プラグインの状態によって処理を分岐 */
			for (var pluginId in this.plugins) {
				var plugin = this.getPlugin(pluginId); 
				if (plugin) {
					switch (plugin.getStatus()) {
					case maskat.core.Plugin.INSTALL:
						/* インストール済みの場合はロード処理を実行 */
						this.loadPlugin(pluginId);
						break;
			
					case maskat.core.Plugin.LOADING:
						/* ロード中の場合はロードが完了しているか確認 */
						if (plugin.isLoaded()) {
							plugin.setStatus(maskat.core.Plugin.READY); 
						}
						break;
					}
				} else {
					/*
					 * プラグイン (plugin.js) の自動読み込み中の場合は
					 *インストール処理を再試行
					 */
					this.installPlugin(pluginId);
				}
			}
			
			/* すべてのプラグインのロードが完了した場合、スレッドを停止 */
			if (this.isReady()) {
				this.loader.stop();
				for (var pluginId in this.plugins) {
					this.startPlugin(pluginId);
				}
			}
		} catch (e) {
			this.loader.stop();
			maskat.app.handleError(e);
		}
		if (this.isReady()) {
			this.onReady();
		}
	},

	/**
	 * すべてのプラグインのロードが完了しているかどうかを返します。
	 *
	 * @returns ロードが完了している場合は true, それ以外の場合は false
	 */
	isReady: function() {
		for (var pluginId in this.plugins) {
			var plugin = this.getPlugin(pluginId);
			if (!plugin || plugin.getStatus() < maskat.core.Plugin.READY) {
				return false;
			}
		}
		return true;
	},

	/**
	 * すべてのプラグインのロード完了時に実行されるコールバックメソッドです。
	 *
	 * <p>
	 * このメソッドはサブクラスやインスタンスメソッドでオーバーライドされる
	 * ことを意図しています。
	 * </p>
	 */
	onReady: function() {
		/* NOP */
	},

	/**
	 * 指定されたプラグイン識別子を持つプラグインを検索し、プラグインの
	 * インスタンスを返却します。
	 *
	 * @param pluginId プラグイン識別子
	 * @returns プラグインのインスタンス
	 */
	getPlugin: function(pluginId) {
		return this.plugins[pluginId];
	},

	/**
	 * 新しいプラグインをインストールします。
	 *
	 * <p>
	 * 指定されたプラグイン識別子に対応するプラグイン実装クラスがプラグイン
	 * レジストリに見つからない場合、plugin.js を現在の HTML 文書に読み込み、
	 * プラグインレジストリにプラグイン実装クラスが登録されるのを待ちます。
	 * </p>
	 *
	 * <p>
	 * プラグイン実装クラスが登録されている場合は、プラグインをインスタンス
	 * 化し、プラグインの状態をインストール済み状態 (INSTALL) に設定します。
	 * 指定されたプラグインが他のプラグインに依存している場合には、それらの
	 * プラグイン識別子をプラグインマネージャに登録します。
	 * </p>
	 *
	 * @param pluginId プラグイン識別子
	 */
	installPlugin: function(pluginId) {
		var pluginClass = maskat.core.Plugin.registry[pluginId]; 
		var plugin = this.getPlugin(pluginId);

		/* プラグインクラスが未登録の場合、plugin.js を自動的に読み込む */		
 		if (!pluginClass) {
 			if (typeof(plugin) == "undefined") {
 				var url = maskat.location + pluginId + "/plugin.js";
 				if (maskat.debug) {
 					url = url + ".uncompressed";
 				}
				maskat.app.loadJavaScript(url, true);
				this.plugins[pluginId] = false;
			}
			return;
		}

		/* プラグインを取得 (または生成) する */
		if (!plugin) {
			plugin = new pluginClass();
			this.plugins[pluginId] = plugin; 
		}

		/* すでにインストール処理を開始済みの場合は何もしない */
		if (plugin.getStatus() >= maskat.core.Plugin.INSTALL) {
			return;
		}
		plugin.setStatus(maskat.core.Plugin.INSTALL);
		plugin.setProperties(this.properties[pluginId]);

		/* 依存関係にあるプラグインをインストール */
		var dependencies = plugin.getDependencies();
		if (dependencies) {
			for (var i = 0; i < dependencies.length; i++) {
				this.installPlugin(dependencies[i]);
			}
		}
	},

	/**
	 * プラグインのロード処理を実行します。
	 *
	 * <p>
	 * 指定されたプラグインが他のプラグインに依存している場合は依存先のプラ
	 * グインのロードを先に行い、その完了を待ちます。プラグインマネージャは
	 * プラグイン実装クラスの load() メソッドを実行し、プラグインの動作状態
	 * を LOADING に設定します。
	 * <p>
	 *
	 * @param pluginId プラグイン識別子
	 */
	loadPlugin: function(pluginId) {
		var plugin = this.getPlugin(pluginId);

		/* すでにロード処理を開始済みの場合は何もしない */
		if (plugin.getStatus() >= maskat.core.Plugin.LOADING) {
			return;
		}

		/* 依存関係にあるプラグインのロード完了まで待機する */
		var dependencies = plugin.getDependencies();
		if (dependencies) {
			for (var i = 0; i < dependencies.length; i++) {
				var dependee = this.getPlugin(dependencies[i]);
				if (!dependee || dependee.getStatus() < maskat.core.Plugin.READY) {
					return;
				}
			}
		}

		/* プラグインのロード処理を実行 */
		if (!plugin.isLoaded()) {
			plugin.load();
		}
		plugin.setStatus(maskat.core.Plugin.LOADING);
	},

	/**
	 * 指定されたプラグインの実行を開始します。
	 *
	 * <p>
	 * 指定されたプラグインが他のプラグインに依存している場合、依存先のプラ
	 * グインの実行を先に行います。プラグインマネージャはプラグインの start()
	 * メソッドを実行し、動作状態を RUNNING に設定します。
	 * </p>
	 *
	 * @param pluginId プラグイン識別子
	 */
	startPlugin: function(pluginId){
		var plugin = this.getPlugin(pluginId);

		/* すでに実行中の場合は何もしない */
		if (plugin.getStatus() >= maskat.core.Plugin.RUNNING) {
			return;
		}
		plugin.setStatus(maskat.core.Plugin.RUNNING);

		/* 依存関係にあるプラグインを再帰的に実行 */
		var dependencies = plugin.getDependencies();
		if (dependencies) {
			for (var i = 0; i < dependencies.length; i++) {
				this.startPlugin(dependencies[i]);
			}
		}

		/* プラグインを実行 */
		plugin.start(this);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class マスカット部品で発生したイベントのクラスです。
 *
 * @name maskat.event.Event
 */ 
maskat.lang.Class.declare("maskat.event.Event", {

	/** @scope maskat.event.Event.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param layout イベントが発生したレイアウト
	 * @param widget イベントが発生したマスカット部品
	 * @param type イベントタイプ
	 */
	initialize: function(layout, widget, type){
		this.layout = layout;
		this.layoutId = layout.getWidgetId();
		this.widget = widget;
		this.widgetId = widget.getWidgetId();
		this.type = type;
		this.branch = null;
		this.cancel = false;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class マスカット部品で発生したイベントをディスパッチするクラスです。
 *
 * @name maskat.event.EventDispatcher
 */ 
maskat.lang.Class.declare("maskat.event.EventDispatcher", {
	
	/** @scope maskat.event.EventDispatcher.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.url = null;
		this.headers = null;
		this.handlers = {};
		this.controller = null;
		this.layout = null;
		this.logger = maskat.log.LogFactory.getLog("maskat.event");
	},

	setController: function(controller){
		this.controller = controller;
	},

	setLayout: function(layout){
		/* レイアウトのイベントハンドラを登録 */
		this.handlers[layout.getWidgetId()] = this.events;

		var widget;
		var handler;
		for (var widgetId in this.handlers) {
			/*
			 * 参照整合性チェック:
			 *
			 * イベント定義 XML に記述されたマスカット部品がレイアウトに
			 * 存在しない場合には警告メッセージを出力する
			 */
			widget = layout.getWidget(widgetId) || layout.getVariable(widgetId);
			if (typeof(widget) == "undefined") {
				this.logger.warn(maskat.util.Message.format("MISSING_WIDGET", {
					layoutId: layout.getWidgetId(),
					widgetId: widgetId
				}));
			}

			/* イベントハンドラの参照を解決する */
			for (var eventType in this.handlers[widgetId]) {
				handler = this.handlers[widgetId][eventType];
				handler.setDispatcher(this);
				if (handler.ref) {
					this.resolveHandlerReference(handler);
				}
			}
		}
	},

	resolveHandlerReference: function(handler){
		var eventRef = this.eventRefs[handler.ref];

		if (eventRef.headers) {
			handler.headers = eventRef.headers.concat(handler.headers || []);
		}

		if (!handler.marshaller) {
			handler.marshaller = eventRef.marshaller;
		}

		if (!handler.unmarshaller) {
			handler.unmarshaller = eventRef.unmarshaller;
		}
	},

	handleEvent: function(event){
		if (event.widget == event.layout && event.type == "onload") {
			this.setLayout(event.layout);
		}

		var handler = maskat.lang.Object.find([event.widgetId, event.type],
			this.handlers);
			
		if (handler) {
			try {
				handler.startHandle(event);
			} catch (e) {
				event.cancel = true;
				if (e instanceof maskat.lang.InterruptedError) {
					var msg = e.getMessages ? e.getMessages().join("\n") : e.message;
					this.logger.info(msg);
				} else {
					maskat.app.handleError(e);
				}
			}
		}
	},

	finishHandle: function(event) {
		if (!event.cancel && this.controller) {
			this.controller.handleEvent(event);
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class イベント発生元のマスカット部品名、およびイベントの種類から、適切なイベントハンドラを起動するクラスです。
 *
 * @name maskat.event.EventHandler
 */ 
maskat.lang.Class.declare("maskat.event.EventHandler", {
	
	/** @scope maskat.event.EventHandler.prototype */
	
	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.dispatcher = null;
		this.onStart = null;
		this.onFinish = null;
		this.logger = maskat.log.LogFactory.getLog("maskat.event");
	},

	/**
	 * このイベントハンドラを管理するイベントディスパッチャを設定します。
	 * マスカット部品で発生したイベントは最初にイベントディスパッチャに
	 * 通知され、適切なハンドラが起動されます。
	 *
	 * イベントディスパッチャはレイアウト内のイベント処理に共通する設定を
	 * 保持しています。
	 *
	 * @param dispatcher イベントディスパッチャ
	 */
	setDispatcher: function(dispatcher){
		this.dispatcher = dispatcher;
	},

	/**
	 * イベントハンドラの開始処理を実行します。
	 *
	 * イベントハンドラ開始時のコールバック関数を実行し、確認ダイアログが
	 * 設定されている場合はダイアログを表示します。コールバック関数や確認
	 * ダイアログで処理の中止が要求された場合、それ以降のイベントの処理は
	 * 行われません。
	 * 
	 * @param event マスカット部品で発生したイベント
	 */
	startHandle: function(event){
		
		this.logger.info(maskat.util.Message.format("EVENT_START", {
			widget: event.widgetId,
			type: event.type
		}));
		
		/* コールバック関数 (start) を実行 */
		if (this.onStart) {
			event.stopProcess = false;
			try {
				this.logger.debug(maskat.util.Message.format(
					"START_HANDLE", {functionName: this.onStart.functionName}));
					
				this.onStart(event);
			} catch (e) {
				if (!(e instanceof maskat.lang.InterruptedError)) {
					throw new maskat.lang.Error("START_HANDLE_ERROR",
							{functionName: this.onStart.functionName}, e);
				} else {
					throw e;
				}
			}
			if (event.stopProcess) {
				throw new maskat.lang.InterruptedError("INTERRUPTION_ERROR");
			}
		}

		/* 入力値検証を実行 */
		if (!this.validate(event)) {
			event.cancel = true;
			return;
		}

		if (this.confirmDialog) {
			/* 確認ダイアログを表示 */
			var self = this;
			var dialog = event.layout.getWidget(this.confirmDialog);
			dialog.proceed = function (e) { self.handle(event); }
			dialog.setVisible(true);
		} else {
			this.handle(event);
		}
	},

	/**
	 * マスカット部品で発生したイベントの処理を行います。
	 *
	 * イベントハンドラのサブクラスでは、このメソッドをオーバーライドして
	 * イベント処理を実装する必要があります。なお、イベント処理の完了後に
	 * finishHandle(event) メソッドが実行されるように実装してください。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handle: function(event){
		this.finishHandle(event);
	},

	/**
	 * イベントハンドラの終了処理を実行します。
	 *
	 * イベントハンドラ終了時のコールバック関数を起動し、終了ダイアログが
	 * 設定されている場合はダイアログを表示します。
	 * 
	 * @param event マスカット部品で発生したイベント
	 */
	finishHandle: function(event){
		/* コールバック関数 (finish) を実行 */
		if (this.onFinish) {
			try {
				this.logger.debug(maskat.util.Message.format(
					"FINISH_HANDLE", {functionName: this.onFinish.functionName}));
					
				this.onFinish(event);
			} catch (e) {
				throw new maskat.lang.Error("FINISH_HANDLE_ERROR",
					{functionName: this.onFinish.functionName}, e);
			}
		}

		/* 終了ダイアログを表示 */
		if (this.endDialog) {
			var dialog = event.layout.getWidget(this.endDialog);
			dialog.setVisible(true);
		}

		/* 画面遷移の分岐先を Ajax 部品から取得 (マスカット 1.4.4 互換) */
		var widget = event.widget.unwrap();
		if (!event.branch && widget) {
			event.branch = widget.branch || "";
		}

		if (this.dispatcher) {
			this.dispatcher.finishHandle(event);
		}
		this.logger.info(maskat.util.Message.format("EVENT_FINISH", {
			widget: event.widgetId,
			type: event.type
		}));
	},

	/**
	 * 入力データを検証します。
	 *
	 * このメソッドはイベントハンドラの処理が開始する前に呼び出されます。
	 * 検証に失敗した場合、ハンドラは実行されません。エラーメッセージを
	 * 表示する責任は、validate メソッドの実装側にあります。
	 *
	 * @returns 入力値検証に成功した場合は true、失敗した場合は false
	 */
	validate: function(event){
		return true;
	}
	
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * イベント定義 XML を解析してイベントディスパッチャを構成します。
 *
 * @name maskat.event.EventXMLReader
 * @extends maskat.xml.XMLObjectBinder
 */ 
maskat.lang.Class.declare("maskat.event.EventXMLReader")
	.extend("maskat.xml.XMLObjectBinder", {

	_static: {
	
		/** @scope maskat.event.EventXMLReader */
	
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.event.EventXMLReader.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.base({
			"#document": {
				children: {
					eventDef: {}
				}
			},

			eventDef: {
				type: maskat.event.EventDispatcher,
				attributes: {},
				children: {
					remoteUrl: {},
					header: { property: "headers", repeat: true },
					component: { property: "handlers", repeat: true, key: "id", value: "events" },
					event: { property: "events", repeat: true, key: "id" },
					eventRef: {  property: "eventRefs", repeat: true, key: "id" },
					desc: {}
				}
			},
	
			remoteUrl: {
				attributes: {
					url: { type: "string", required: true }
				}
			},
	
			header: {
				attributes: {
					name: { type: "string", required: true },
					value: { type: "string", required: true }
				}
			},
	
			component: {
				attributes: {
					id: { type: "string", required: true }
				},
				children: {
					event: { property: "events", repeat: true, key: "id" },
					desc: {}
				}
			},
	
			event: {
				attributes: {
					id: { type: "string", required: true },
					remoteUrl: { type: "string", property: "url" },
					type: {
						type: "enum",
						values: [ "local", "remote" ],
						defaultValue: "remote"
					},
					async: { type: "boolean", defaultValue: true },
					ref: { type: "string" },
					marshal: { type: "function" },
					unmarshal: { type: "function" },
					start: { type: "function", property: "onStart" },
					before: { type: "function", property: "onBeforeRequest" },
					after: { type: "function", property: "onAfterResponse" },
					finish: { type: "function", property: "onFinish" },
					timeout: { type: "number" },
					onTimeoutError: { type: "function", property: "onRequestTimeout" },
					confirmDialog: { type: "string" },
					endDialog: { type: "string" }
				},
				children: {
					header: { property: "headers", repeat: true },
					param: { property: "marshaller" },
					result: { property: "unmarshaller" },
					desc: {}
				}
			},
	
			eventRef: {
				attributes: {
					id: { type: "string", required: true }
				},
				children: {
					header: { property: "headers", repeat: true },
					param: { property: "marshaller" },
					result: { property: "unmarshaller" },
					desc: {}
				}
			},
	
			param: {
				type: maskat.event.RequestMarshaller,
				attributes: {
					rootNode: { type: "string" },
					ns: { type: "string" },
					soap: { type: "boolean", defaultValue: false }
				},
				children: {
					source: { property: "sources", repeat: true },
					desc: {}
				}
			},
	
			source: {
				attributes: {
					obj: { type: "string", required: true },
					node: { type: "string" },
					childNode: { type: "string" },
					idxRef: { type: "string" },
					fromkey: { type: "string" },
					type: { type: "string" },
					regexp: { type: "string" },
					min: { type: "number" },
					max: { type: "number" },
					desc: { type: "string" },
					sendBlankElement: { type: "boolean", defaultValue: false },
					teleType: { type: "string" }
				},
				children: {
					bind: { property: "binds", repeat: true },
					desc: {}
				}
			},
	
			result: {
				type: maskat.event.ResponseUnmarshaller,
				attributes: {
					rootNode: { type: "string" },
					ns: { type: "string" },
					onErrorTele: { type: "function" },
					soap: { type: "boolean", defaultValue: false }
				},
				children: {
					target: { property: "targets", repeat: true },
					desc: {}
				}
			},
	
			target: {
				attributes: {
					out: { type: "string", property: "widgetId", required: true },
					"in": { type: "string", property: "node" },
					inkey: { type: "string", property: "childNode" },
					type: {
						type: "enum",
						values: [ "local", "remote" ]
					},
					teleType: { type: "string" },
					workType: { type: "function" }
				},
				children: {
					bind: { property: "binds", repeat: true },
					desc: {}
				}
			},
	
			bind: {
				attributes: {
					node: { type: "string", required: true },
					fromkey: { type: "string", property: "property" },
					tokey: { type: "string", property: "property" }
				}
			},
	
			desc: {
				children: {
					"#text": {}
				}
			}
		});
	},

	createObject: function(element){
		switch (element.nodeName) {
		case "event":
			/* イベント種別に応じてハンドラクラスを生成する */
			if (element.getAttribute("type") == "local") {
				return new maskat.event.LocalEventHandler();
			} else {
				return new maskat.event.RemoteEventHandler();
			}

		case "target":
			/*
			 * type 属性のデフォルト値はローカルイベントとリモートイベント
			 * の場合で異なる
			 */
			var event = element.parentNode.parentNode;
			var binder = new maskat.xml.XMLObjectBinder();
			binder.type = event.getAttribute("type") || "remote";
			return binder;

		case "bind":
			var parentName = element.parentNode.nodeName;
			/* 親要素が source の場合、fromkey 属性が必須 */
			if (parentName == "source" && !element.getAttribute("fromkey")) {
				throw new maskat.lang.Error("MISSING_ATTRIBUTE",
					{ attributeName: "fromkey" });
			}
			/* 親要素が target の場合、tokey 属性が必須 */
			if (parentName == "target" && !element.getAttribute("tokey")) {
				throw new maskat.lang.Error("MISSING_ATTRIBUTE",
					{ attributeName: "tokey" });
			}
			/* falls through */
		default:
			return {};
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class ローカルデータバインディングを実行するクラスです。
 * 処理開始時にコールバックメソッドを実行します。
 * 処理終了時にコールバックメソッドを実行します。
 *
 * @name maskat.event.LocalEventHandler
 * @extends maskat.event.EventHandler
 */ 
maskat.lang.Class.declare("maskat.event.LocalEventHandler")
	.extend("maskat.event.EventHandler", {

	/** @scope maskat.event.LocalEventHandler.prototype */

	/**
	 * コンストラクタです。
	 */
	initialize: function() {
		this.base.apply(this, arguments);
	},

	/**
	 * ローカルイベントの処理を行います。
	 *
	 * ローカルイベントハンドラはブラウザ上の JavaScript 関数によってイベント
	 * を処理します。レイアウト内のマスカット部品や変数のデータを相互に代入
	 * したり、任意の JavaScript 関数を実行することが可能です。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handle: function(event){
		/* ローカルデータバインディングを実行 */
		if (this.unmarshaller) {
			this.unmarshaller.unmarshal(null, event.layout);
		}

		/* イベントハンドラの終了処理を実行 */
		this.finishHandle(event);
	}
	
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class リモートイベントでコールバックメソッドを実行するクラスです。
 *
 * @name maskat.event.RemoteEventContext
 * @extends maskat.comm.HttpContext
 */ 
maskat.lang.Class.declare("maskat.event.RemoteEventContext")
	.extend("maskat.comm.HttpContext", {
	
	/** @scope maskat.event.RemoteEventContext.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(handler, event){
		this.base(handler.method, handler.url, handler);
		this.handler = handler;
		this.event = event;

		if (!this.requestHeaders["maskat_layoutID"]) {
			this.requestHeaders["maskat_layoutID"] = event.layoutId;
		}
		if (!this.requestHeaders["maskat_componentID"]) {
			this.requestHeaders["maskat_componentID"] = event.widgetId;
		}
		if (!this.requestHeaders["maskat_eventID"]) {
			this.requestHeaders["maskat_eventID"] = event.type;
		}
	},
	
	onReady: function(){
		if (this.handler.onBeforeRequest) {
			this.event.stopProcess = false;
			this.event.sendXML = this.requestMessage;
			this.event.xhr = this.xhr;

			try {
				this.handler.logger.debug(maskat.util.Message.format(
					"BEFORE_HANDLE",
					{functionName: this.handler.onBeforeRequest.functionName}));
				try {
					this.handler.onBeforeRequest(this.event, this);
				} catch (e) {
					if (!(e instanceof maskat.lang.InterruptedError)) {
						throw new maskat.lang.Error("BEFORE_HANDLE_ERROR",
							{functionName: this.handler.onBeforeRequest.functionName}, e);
					}
					throw e;
				}
				if (this.event.stopProcess) {
					throw new maskat.lang.InterruptedError("INTERRUPTION_ERROR");
				}
				
			} catch (e) {
				this.event.cancel = true;
				if (e instanceof maskat.lang.InterruptedError) {
					var msg = e.getMessages ? e.getMessages().join("\n") : e.message;
					this.handler.logger.info(msg);
				}
				throw e;
			}
		}
		this.handler.logger.debug(maskat.util.Message.format(
				"REQUEST_MESSAGE", {url: this.url, message: this.requestMessage}));
	},
	
	onWait: function(){
		if (!this.async) {
			this.dialogThread = new maskat.lang.Thread(
				this,
				this.monitorProgress,
				null,
				200);
			
			this.dialogThread.start();
		}
	},

	monitorProgress: function(){
		/*
		 * 初回でWAIT状態の場合
		 */
		if (!this.dialog && this.status == maskat.comm.HttpContext.WAIT) {
			this.dialog = maskat.ui.Dialog.openProgress(
				"",
				maskat.util.Message.format("SYNC_EVENT_IN_PROGRESS"),
				Infinity);
		/*
		 * COMPLETE, ERROR, TIMEOUTの場合
		 */
		} else if (this.status > maskat.comm.HttpContext.WAIT) {
			if (this.dialog) {
				this.dialog.done();
			}
			this.dialogThread.stop();
		}
	},

	onComplete: function(){
		try {
			var time = (new Date()).getTime() - this.sentAt;
			this.handler.logger.debug(maskat.util.Message.format(
				"RESPONSE_MESSAGE", {message: this.responseMessage, time: time}));

			if (this.handler.onAfterResponse) {
				this.event.stopProcess = false;
				this.event.recvDOM = this.responseXML;
				this.event.xhr = this.xhr;

				this.handler.logger.debug(maskat.util.Message.format(
					"AFTER_HANDLE",
					{functionName: this.handler.onAfterResponse.functionName}));
					
				try {
					this.handler.onAfterResponse(this.event, this);

				} catch (e) {
					if (!(e instanceof maskat.lang.InterruptedError)) {
						throw new maskat.lang.Error("AFTER_HANDLE_ERROR",
							{functionName: this.handler.onAfterResponse.functionName}, e);
					}
					throw e;
				}
				if (this.event.stopProcess) {
					throw new maskat.lang.InterruptedError("INTERRUPTION_ERROR");
				}
			}
			this.handler.handleResponse(this.event, this);
			
		} catch (e) {
			this.event.cancel = true;
			if (e instanceof maskat.lang.InterruptedError) {
				var msg = e.getMessages ? e.getMessages().join("\n") : e.message;
				this.handler.logger.info(msg);
			} else {
				throw e;
			}
		}
	},
	
	onTimeout: function(){
		this.handler.handleRequestTimeout(this.event, this);
	},

	onError: function(){
		this.handler.handleError(this.event, this.error);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class リモートイベントで行われるローカルデータバインディングを実行するクラスです。
 *
 * @name maskat.event.RemoteEventHandler
 * @extends maskat.event.EventHandler
 */ 
maskat.lang.Class.declare("maskat.event.RemoteEventHandler")
	.extend("maskat.event.EventHandler", {
	
	/** @scope maskat.event.RemoteEventHandler.prototype */
	
	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.base.apply(this, arguments);
		this.url = null;
		this.method = "POST";
		this.headers = null;
		this.onBeforeRequest = null;
		this.onAfterResponse = null;
		this.onRequestTimeout = null;
		this.marshaller = null;
		this.unmarshaller = null;
	},

	/**
	 * このイベントハンドラを管理するイベントディスパッチャを設定します。
	 * マスカット部品で発生したイベントは最初にイベントディスパッチャに
	 * 通知され、適切なハンドラが起動されます。
	 *
	 * イベントディスパッチャはレイアウト内のイベント処理に共通する設定を
	 * 保持しています。
	 *
	 * @param dispatcher イベントディスパッチャ
	 */
	setDispatcher: function(dispatcher){
		this.dispatcher = dispatcher;

		/* リモート URL の省略時はデフォルト URL を使用 */
		if (!this.url) {
			this.url = dispatcher.url;
		}

		/* グローバルヘッダを追加 */
		if (dispatcher.headers) {
			this.headers = dispatcher.headers.concat(this.headers || []);
		}
	},

	/**
	 * リモートイベントの処理を行います。
	 *
	 * リモートイベントハンドラはサーバとの XML HTTP 通信によってイベントを
	 * 処理します。レイアウト内のマスカット部品や変数のデータを抽出して要求
	 * メッセージを生成し、サーバに HTTP 要求を送信します。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handle: function(event) {
		try {
			/* 要求メッセージのデータバインディングを実行 */
			var context = new maskat.event.RemoteEventContext(this, event);
			context.requestMessage = this.marshal(event.layout);
			context.send();
		} catch (e) {
			event.cancel = true;
			if (!(e instanceof maskat.lang.InterruptedError)) {
				throw new maskat.lang.Error("REQUEST_BIND_ERROR", {
					widgetId: event.widgetId,
					type: event.type
				}, e);
			}
		}
	},

	marshal: function(layout) {
		if (this.marshaller) {
			return this.marshaller.marshal(layout);
		}
		return undefined;
	},

	/**
	 * ドキュメントからJSON型のエラーオブジェクトに変換します。
	 * 
	 * @param doc エラーノードを含むドキュメント
	 * @return JSON型のエラーオブジェクト
	 *
	 * 変換例）変換前
	 * <errors>
	 *   <error name="name1">message-1</error>
	 *   <error name="name2, unknown="node">message-2</error>
	 * </errors>
	 *
	 * 変換例）変換後
	 * [
	 *    {"name": "name1", "message": "message-1"},
	 *    {"name": "name2", "message": "message-2" unkown: "node"}
	 * ]
	 */
	convertErrorObject: function(doc) {
		var binder = new maskat.xml.XMLObjectBinder({
				"#document": {
					children: {
						errors: {}
					}
				},
				errors: {
					children:{
						error: {property: "errors", repeat: true }
					}
				},
				error: {
					attributes: {"*": {}},
					children: {
						"#text": {property: "message"}
					}
				}
		});
		var object = binder.read(doc);
		return  (object && object.errors) ? object.errors : "";
	},

	/**
	 * 応答メッセージの受信処理を行います。
	 *
	 * リモートイベントハンドラは応答メッセージの情報を抽出し、レイアウト内の
	 * マスカット部品や変数に格納します。
	 *
	 * @param event マスカット部品で発生したイベント
	 * @param context 通信終了後のコンテキスト
	 */
	handleResponse: function(event, context){
		/* 応答メッセージのデータバインディングを実行 */
		try {
			var doc = context.responseXML || context.responseMessage;
			if (doc && doc.documentElement && doc.documentElement.nodeName == "errors") {
				var errors = this.convertErrorObject(doc);
				
				if (this.unmarshaller && this.unmarshaller.onErrorTele) {
					try {
						this.logger.debug(maskat.util.Message.format(
							"HANDLE_ERRORMESSAGE",
							{functionName: this.unmarshaller.onErrorTele.functionName,
							 message: maskat.lang.Object.encodeJSON(errors)}));
							
						context.errorMessages = errors;
						this.unmarshaller.onErrorTele(event, context);
					} catch (e) {
						if (!(e instanceof maskat.lang.InterruptedError)) {
							throw new maskat.lang.Error("HANDLE_ERRORMESSAGE_ERROR",
								{functionName: this.unmarshaller.onErrorTele.functionName}, e);
						}
						throw e;
					}
				} else {
					throw new maskat.lang.InterruptedError("ERROR_MESSAGE_RECEIVED",
						{message: maskat.lang.Object.encodeJSON(errors)});
				}
			} else {
				this.unmarshal(doc, event.layout);
			}
			
		} catch (e) {
			event.cancel = true;
			if (!(e instanceof maskat.lang.InterruptedError)) {
				throw new maskat.lang.Error("RESPONSE_BIND_ERROR", {
					widgetId: event.widgetId,
					type: event.type
				}, e);
			}
			throw e;
		}
		/* イベントハンドラの終了処理を実行 */
		this.finishHandle(event);
	},

	unmarshal: function(doc, layout) {
		if (doc && this.unmarshaller) {
			this.unmarshaller.unmarshal(doc, layout);
		}
	},

	/**
	 * 応答メッセージ待ちのタイムアウト処理を行います。
	 *
	 * 応答メッセージ待ち状態のまま指定されたタイムアウト時間が経過した場合、
	 * タイムアウト処理が起動されます。
	 *
	 * @param event マスカット部品で発生したイベント
	 * @param context 通信終了後のコンテキスト
	 */
	handleRequestTimeout: function(event, context) {
		event.cancel = true;
		if (this.onRequestTimeout) {
			try {
				this.logger.debug(maskat.util.Message.format(
					"REQUEST_TIMEOUT_HANDLE",
						{functionName: this.onRequestTimeout.functionName}));
					
				/* タイムアウト発生時のコールバック関数を実行 */
				this.onRequestTimeout(event, context);
			} catch (e) {
				context.error = e;
				this.handleError(event,
					new maskat.lang.Error("REQUEST_TIMEOUT_ERROR",
					{functionName: this.onRequestTimeout.functionName}, e));
			}
		} else {
			/* コールバック関数が指定されていない場合はログを出力 */
			this.handleError(event,
				new maskat.lang.Error("REQUEST_TIMEOUT", {
					widgetId: event.widgetId,
					type: event.type
				}));
		}
	},

	/**
	 * エラー処理を行います。
	 *
	 * @param event マスカット部品で発生したイベント
	 */
	handleError: function(event, error) {
		event.cancel = true;
		/**
		 * FirefoxではXHRのonreadystatechange関数内でエラーを
		 * throwしてもwindow.onerror関数に通知されません。
		 * setTimeout関数を利用することでwindow.onerror関数に
		 * 通知されます。
		 * https://bugzilla.mozilla.org/show_bug.cgi?id=377347
		 */
		setTimeout(function() {
			maskat.app.handleError(error);
		}, 0);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class レイアウト内のマスカット部品から取得した値を用いて HTTP 要求メッセージ (XML 文書) を送信するクラスです。
 *
 * @name maskat.event.RequestMarshaller
 * @extends maskat.xml.XMLObjectBinder
 */ 
maskat.lang.Class.declare("maskat.event.RequestMarshaller")
	.extend("maskat.xml.XMLObjectBinder", {
	/** @scope maskat.event.RequestMarshaller.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.base();
		this.rootNode = null;
		this.ns = "";
		this.soap = false;
		this.sources = [];
		this.configured = false;
		this.logger = maskat.log.LogFactory.getLog("maskat.event");
	},

	marshal: function(layout) {
		if (!this.rootNode) {
			return null;
		}
		if (!this.configured) {
			this.configure();
		}

		var values = [];
		for (var i = 0; i < this.sources.length; i++) {
			var source = this.sources[i];
			var widgetId = source.obj;

			var widget = layout.getWidget(widgetId);
			var value;

			if (source.idxRef) {
				var reference = layout.getWidget(source.idxRef);
				if (reference) {
					this.logger.trace(maskat.util.Message.format("GET_WIDGET_VALUE", {
						widget: source.idxRef,
						method: source.teleType ? source.teleType : ""}));

					value = reference.getValue(source.teleType);
				} else {
					this.logger.trace(maskat.util.Message.format("GET_VARIABLE_VALUE", {
						variable: source.idxRef}));

					value = layout.getVariable(source.idxRef);
				}
			} else {
				if (widget) {
					this.logger.trace(maskat.util.Message.format("GET_WIDGET_VALUE", {
						widget: widgetId,
						method: source.teleType ? source.teleType : ""}));
						
					value = widget.getValue(source.teleType);
	            } else {
					this.logger.trace(maskat.util.Message.format("GET_VARIABLE_VALUE", {
						variable: widgetId}));

	            	value = layout.getVariable(widgetId);
				}
			}
			if (typeof(source.fromkey) != "undefined") {
			    /* 選択されたセルを送信 */
				this.logger.trace(maskat.util.Message.format("GET_WIDGET_VALUE", {
						widget: widgetId, method: "selectedIndex"}));
				var index = widget.getValue("selectedIndex");
				values[i] = value[index][source.fromkey];

			} else if (typeof(source.idxRef) != "undefined") {
                if (typeof(source.childNode) == "undefined") {
				    /* 選択された単一行を送信 */
					this.logger.trace(maskat.util.Message.format("GET_WIDGET_VALUE", {
							widget: widgetId, method: "selectedIndex"}));
                    var index = widget.getValue("selectedIndex");
                    values[i] = value[index];

                } else {
				    /* 選択された複数行を送信 */
					this.logger.trace(maskat.util.Message.format("GET_WIDGET_VALUE", {
							widget: widgetId, method: "selectedIndexes"}));
					var indexes = widget.getValue("selectedIndexes");
					var rows = [];
					for (var j = 0; j < indexes.length; j++) {
						rows.push(value[indexes[j]]);
					}
					values[i] = rows;
				}
			} else {
				/* マスカット部品の値をそのまま使用する */
				values[i] = value;
			}

			/* sendBlankElement 属性への対応 */
			switch (typeof(values[i])) {
			case "string":
				/* 空文字列の場合 */
				if (values[i] == "" && !source.sendBlankElement) {
					values[i] = undefined;
				}
				break;
				
			case "object":
				/* null または空の配列の場合 */
				if (values[i] == null ||
				    values[i] instanceof Array && values[i].length == 0) {
					values[i] = source.sendBlankElement ? null : undefined;
				}
				break;

			case "undefined":
				/* 未定義の場合 */
				values[i] = source.sendBlankElement ? null : undefined;
				break;
			}
			this.logger.trace(maskat.util.Message.format("GET_VALUE", {
					value: maskat.lang.Object.encodeJSON(values[i])}));
		}

		return this.write(values);
	},

	configure: function() {
		this.configured = true;

		if (this.soap) {
			// TODO: add soap envelope
		} else {
			this.setRootElement(this.ns, this.rootNode);
		}

		if (this.ns) {
			this.addPrefixMapping("", this.ns);
		}

		/* 応答メッセージのルート要素に対応するバインディングを生成 */
		var root = this.addElementBinding(this.ns, this.rootNode);

		for (var i = 0; i < this.sources.length; i++) {
			var source = this.sources[i];
			var wrapper;
			if (source.node) {
				/*
				 * マーシャル対象のデータをルート要素の子要素 (wrapper) の
				 * 内部に出力する
				 */
				root.addChildBinding(source.node, { property: i });
				wrapper = this.addElementBinding(this.ns, source.node);
			}

			var object;
			if (source.childNode) {
				/*
				 * 繰り返し要素が定義されている場合、マスカット部品または変数
				 * から配列を取得し、配列の各要素を XML 要素へバインドする
				 */
				if (source.node) {
					wrapper.addChildBinding(source.childNode, { repeat: true });
				} else {
					/*
					 * childNode 属性が指定された場合は node 属性は省略可能
					 * この場合、ルート要素の直下に繰り返し要素を出力する
					 */
					root.addChildBinding(source.childNode, { property: i, repeat: true });
				}
				object = this.addElementBinding(this.ns, source.childNode);
			} else if (source.binds) {
				/*
				 * 繰り返し要素は未定義だが子要素へのバインドが定義されている
				 * 場合、マスカット部品または変数からオブジェクトを取得し、
				 * そのプロパティを子要素へバインドする
				 */
				object = wrapper;
			} else {
				/* 文字列からテキストノードへのバインドを定義 */
				wrapper.addChildBinding("#text", {});
			}
	
			if (object && source.binds) {
				/* 子要素へのバインドを定義 */
				for (var j = 0; j < source.binds.length; j++) {
					var bind = source.binds[j];
					var property = this.addElementBinding(this.ns, bind.node);
					property.addChildBinding("#text", { property: bind.property });
					object.addChildBinding(bind.node);
				}
			}
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class HTTP 応答メッセージ (XML 文書) から取得した値をレイアウト内のマスカット部品に設定するクラスです。
 *
 * @name maskat.event.ResponseUnmarshaller
 */ 
maskat.lang.Class.declare("maskat.event.ResponseUnmarshaller", {

	/** @scope maskat.event.ResponseUnmarshaller.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.soap = false;
		this.ns = "";
		this.rootNode = null;
		this.targets = [];
		this.configured = false;
		this.logger = maskat.log.LogFactory.getLog("maskat.event");
	},

	unmarshal: function(doc, layout) {
		if (!this.configured) {
			this.configure();
		}

		for (var i = 0; i < this.targets.length; i++) {
			var target = this.targets[i];
			switch (target.type) {
			case "local":
				/* ローカルデータバインディングを実行 */
				var src = layout.getWidget(target.node);
				var value;
				if (src) {
					value = src.getValue();
				} else {
					value = layout.getVariable(target.node);
				}
				
				var dst = layout.getWidget(target.widgetId);
				if (target.workType) {
					var arg1 = src ? src.unwrap() : value;
					var arg2 = dst ? dst.unwrap() : layout.getVariable(target.widgetId);

					this.logger.trace(maskat.util.Message.format("BINDING_HANDLE", {
							functionName: target.workType.functionName,
							src: target.node, dst: target.widgetId}));

					target.workType(arg1, arg2);
				} else {
					this.logger.trace(maskat.util.Message.format("LOCAL_BINDING_START", {
							src: target.node, dst: target.widgetId}));

					if (dst) {
						dst.setValue(value);
					} else {
						layout.setVariable(target.widgetId, value);
					}
					this.logger.trace(maskat.util.Message.format("BINDING_VALUE", {
							value: maskat.lang.Object.encodeJSON(value)}));
				}
				break;

			case "remote":
				/* 応答メッセージのバインディングを実行 */
				var values = doc ? target.read(doc) : null;
				var widget = layout.getWidget(target.widgetId);
				if (widget) {
					try {
						widget.setValue(values[i], target.teleType);
					} finally {
						this.logger.trace(maskat.util.Message.format("BINDING_WIDGET", {
							widget: target.widgetId,
							method: target.teleType ? target.teleType : "",
							value: maskat.lang.Object.encodeJSON(values[i])}));
					}
				} else {
					layout.setVariable(target.widgetId, values[i]);
					this.logger.trace(maskat.util.Message.format("BINDING_VARIABLE", {
						variable: target.widgetId,
						value: maskat.lang.Object.encodeJSON(values[i])}));
				}
				break;
			}
		}
	},

	configure: function() {
		this.configured = true;

		for (var i = 0; i < this.targets.length; i++) {
			var target = this.targets[i];
			if (target.type == "local") {
				continue;
			}
			
			if (this.soap) {
				// TODO: add soap envelope
			} else {
				target.setRootElement(this.ns, this.rootNode);
			}
	
			/* 応答メッセージのルート要素に対応するバインディングを生成 */
			var root = target.addElementBinding(this.ns, this.rootNode);

			var wrapper;
			if (target.node) {
				/*
				 * アンマーシャル対象のデータはルート要素の子要素 (wrapper) の
				 * 内部に格納されている
				 */
				wrapper = target.addElementBinding(this.ns, target.node);
				root.addChildBinding(target.node);
				root.addChildBinding("*", { validate: false });
			} else {
				/* アンマーシャル対象のデータはルート要素の内部に格納されている */
				wrapper = root;
			}
	
			var object;
			if (target.childNode) {
				/*
				 * 繰り返し要素が定義されている場合、繰り返し単位となる個々の
				 * 要素をオブジェクトへバインドし、それらを要素とする配列を
				 * マスカット部品または変数にバインドする
				 */
				object = target.addElementBinding(this.ns, target.childNode);
				wrapper.addChildBinding(target.childNode, { property: i, repeat: true });
				wrapper.addChildBinding("*", { validate: false });
			} else if (target.binds) {
				/*
				 * 繰り返し要素は未定義だが、プロパティへのバインドが定義されて
				 * いる場合、単一のオブジェクトへのバインドを定義
				 */
				root.addChildBinding(target.node, { property: i });
				object = wrapper;
			} else {
				/* テキスト要素から文字列へのバインドを定義 */
				wrapper.addChildBinding("#text", { property: i });
				object = null;
			}
	
			if (object && target.binds) {
				/* オブジェクトのプロパティへのバインドを定義 */
				for (var j = 0; j < target.binds.length; j++) {
					var bind = target.binds[j];
					var property = target.addElementBinding(this.ns, bind.node);
					property.addChildBinding("#text", { property: bind.property });
					object.addChildBinding(bind.node);
				}
				object.addChildBinding("*", { validate: false });
			}
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * 特定のレイアウトやマスカット部品にフォーカスが置かれた状況におけるキー
 * 操作の組み合わせを、コマンドの系列と関連付けます。
 *
 * @name maskat.key.KeyBind
 * @extends maskat.control.CommandSet
 */ 
maskat.lang.Class.declare("maskat.key.KeyBind")
	.extend("maskat.control.CommandSet", {

	/** @scope maskat.key.KeyBind.prototype  */
	
	/**
	 * コンストラクタです。 
	 * 
	 * @param key キー操作の組み合わせを表す文字列
	 * @param type キーイベントの種類 (keypress, keyup, keydown)
	 */
	initialize: function(key, type) {
		this.key = key;
		this.type = type;
		this.shift = false;
		this.ctrl = false;
		this.alt = false;
		this.meta = false;
		this.charCode = false;
		this.commands = [];
	},

	/** 特殊キーを表す文字列とキーコードの対応関係 */
	specialKeys: {
		"ESC": 27,
		"ESCAPE": 27,
		"TAB": 9,
		"SPACE": 32,
		"RETURN": 13,
		"ENTER": 13,
		"BACKSPACE": 8,

		"SCROLLLOCK": 145,
		"SCROLL_LOCK": 145,
		"SCROLL": 145,
		"CAPSLOCK": 20,
		"CAPS_LOCK": 20,
		"CAPS": 20,
		"NUMLOCK": 144,
		"NUM_LOCK": 144,
		"NUM": 144,
		
		"PAUSE": 19,
		"BREAK": 19,
		
		"INSERT": 45,
		"HOME": 36,
		"DELETE": 46,
		"END": 35,
		
		"PAGEUP": 33,
		"PAGE_UP": 33,
		"PU": 33,

		"PAGEDOWN": 34,
		"PAGE_DOWN": 34,
		"PD": 34,

		"LEFT": 37,
		"UP": 38,
		"RIGHT": 39,
		"DOWN": 40,

		"F1": 112,
		"F2": 113,
		"F3": 114,
		"F4": 115,
		"F5": 116,
		"F6": 117,
		"F7": 118,
		"F8": 119,
		"F9": 120,
		"F10": 121,
		"F11": 122,
		"F12": 123
	},

	/**
	 * キー操作の組み合わせを表す文字列を解析し、このオブジェクトに修飾キー
	 * とキーコードを設定します。
	 */
	decode: function() {
		/* キー文字列を大文字に変換して空白を除去し、"+" で分割 */
		var keys = this.key.toUpperCase().replace(/ /g, "").split("+");

		for (var i = 0; i < keys.length; i++) {
			switch (keys[i]) {
			case "CTRL":
			case "CONTROL":
				this.ctrl = true;
				break;
			case "SHIFT":
				this.shift = true;
				break;
			case "ALT":
				this.alt = true;
				break;
			case "META":
				this.meta = true;
				break;
			default:
				this.charCode = this.specialKeys[keys[i]];
				if (typeof(this.charCode) == "undefined") {
					this.charCode = keys[i].charCodeAt();
				}
			}
		}
		
		/* TODO  IEでヘルプを表示しない処理（1回実行すれば良いだけだが、ここだと複数回呼ばれる） */
		if (this.charCode == 112) {
			document.onhelp = function() { return false; };
		}
	},
	
	/**
	 * 引数で与えられたイベントが、このキーバインドで指定されているキー操作の
	 * 組み合わせに該当するかどうかを確認します。
	 * 
	 * @param e
	 *            イベント
	 * @returns このキーバインドで指定されたキー操作の組み合わせに該当する
	 *           場合は true、 該当しない場合は false
	 */
	match : function(e) {
		return (this.type == e.type)
				&& (this.ctrl == (e.ctrlKey || false))
				&& (this.shift == (e.shiftKey || false))
				&& (this.alt == (e.altKey || false))
				&& (this.meta == (e.metaKey || false))
				&& (this.charCode == (e.keyCode || e.charCode));
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * キーバインド定義 XML を解析してキーイベントハンドラを構成します。
 *
 * @name maskat.key.KeyBindingXMLReader
 * @extends maskat.xml.XMLObjectBinder
 */ 
maskat.lang.Class.declare("maskat.key.KeyBindingXMLReader")
	.extend("maskat.xml.XMLObjectBinder", {

	_static: {
		/** @scope maskat.key.KeyBindingXMLReader */
	
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.key.KeyBindingXMLReader.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function(){
		this.base({
			"#document": {
				children: {
					keybinding: {}
				}
			},
		
			/* ルート要素 */
			keybinding: {
				attributes: {
					layout: { type: "string", required: true }
				},
				children: {
					"default": { property: "default" },
					component: { property: "components", repeat: true, key: "id" }
				}
			},
			
			/* レイアウト全体で有効なキーバインドの設定 */
			"default": {
				children: {
					bind: { property: "binds", repeat: true }
				}
			},
		
			/* 特定の部品にフォーカスがある場合ののキーバインドの設定 */
			"component": {
				attributes: {
					id: { type: "string", required: true }
				},
				children: {
					bind: { property: "binds", repeat: true }
				}
			},
		
			/* キー操作の組み合わせとタイミングの設定 */
			bind: {
				type: maskat.key.KeyBind,
				attributes: {
					key: { type: "string", required: true },
					type: { type: "enum",
							values: [ "keydown", "keypress", "keyup"],
							defaultValue: "keydown" }
				},
				children: {
					"*": { property: "commands", repeat: true }
				}
			},

			/* キーイベントに対応して実行されるコマンドの指定 */
			"set-focus": {
				type: maskat.control.SetFocusCommand,
				attributes:{
					target: { type: "string", required: true }
				}
			},

			"move-focus": {
				type: maskat.control.MoveFocusCommand,
				attributes:{
					type: {
						type: "enum",
						values: [ "next", "previous", "first", "last" ],
						defaultValue: "next"
					}
				}
			},
			
			"set-visible": {
				type: maskat.control.SetVisibleCommand,
				attributes:{
					target: { type: "string", required: true },
					visible: { type: "boolean", required: true }
				}
			},

			"show": {
				type: maskat.control.SetVisibleCommand,
				attributes:{
					target: { type: "string", required: true },
					visible: { type: "boolean", defaultValue: true }
				}
			},

			"hide": {
				type: maskat.control.SetVisibleCommand,
				attributes:{
					target: { type: "string", required: true },
					visible: { type: "boolean", defaultValue: false }
				}
			},

			"enable": {
				type: maskat.control.SetEnabledCommand,
				attributes:{
					target: { type: "string", required: true },
					enabled: { type: "boolean", defaultValue: true }
				}
			},

			"disable": {
				type: maskat.control.SetEnabledCommand,
				attributes:{
					target: { type: "string", required: true },
					enabled: { type: "boolean", defaultValue: false }
				}
			},

			"function": {
				type: maskat.control.CallFunctionCommand,
				attributes:{
					name: { type: "function", required: true, property: "func" }
				}
			},

			"maskat-event": {
				type: maskat.control.MaskatEventCommand,
				attributes:{
					type: { type: "string", required: true },
					target: { type: "string", required: true }
				}
			}
		});
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class キーボードイベントの処理をマスカット部品で管理するクラスです。
 *
 * @name maskat.key.KeyEventManager
 */
 maskat.lang.Class.declare("maskat.key.KeyEventManager", {
	
	_static: {
	
		/** @scope maskat.key.KeyEventManager */
	
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.key.KeyEventManager.prototype */

	/**
	 * フォーカスを移動します。
	 *
	 * @param event 発生したイベント
	 * @param type フォーカスの種別
	 */
	moveFocus: function(event, type){
		var element = event.target || event.srcElement;
		var widget = this.getWidget(this.getRootElement(element));

		var activeLayout = widget.getLayout();
		var tabIndexes = activeLayout.getTabIndexes();

		var pos = -1;
		for (var i = 0; i < tabIndexes.length; i++) {
			var name = widget.getTabIndexGroupName() || widget.name;
			if (tabIndexes[i] == name) {
				pos = i;
				break;
			}
		}

		var next = "";
		for (var j = 0; j < tabIndexes.length - 1; j++) {
			switch (type) {
				case "next":
					pos++;
					if (pos >= tabIndexes.length) pos = 0;
					break;
				case "previous":
					pos--;
					if (pos < 0) pos = tabIndexes.length - 1;
					break;
				case "first":
					type = "next";
					pos = 0;
					break;
				case "last":
					type = "previous";
					pos = tabIndexes.length - 1;
					break;
			}

			next = activeLayout.getWidget(tabIndexes[pos]);
			if (next.canFocus() && this.isVisible(next)) {
				try {
					next.setFocus();
					return false;
				} catch (e) {}
				return true;
			}
		}
		return false;
	},
	
	handle: function(event) {
		var element = event.target || event.srcElement;
		var widget = this.getWidget(this.getRootElement(element));

		/* widgetが存在しない場合は、処理を行なわない */
		if (!widget) {
			return;
		}
		
		/* 部品のキーイベントを処理する */
		var propagate = true;
		var layout = widget.getLayout();

		/* キーバインド定義 XML で定義された部品のキーバインドを実行 */
		if (widget != layout) {
			var keybind = widget.getKeybind(event);
			if (typeof(keybind) != "undefined") {
				keybind.execute(event);
				propagate = false;
			}
		}

		if (propagate && event.type == "keydown") {
			propagate = widget.handleKeyEvent(event);
		}

		/* キーバインド定義 XML で定義されたレイアウトのキーバインドを実行 */
		if (propagate) {
			var keybind = layout.getKeybind(event);
			if (typeof(keybind) != "undefined") {
				keybind.execute(event);
				propagate = false;
			}
		}
		
		/* タブキーによるフォーカス遷移を実行 */
		if (propagate && event.type == "keydown" && event.keyCode == 9) {
			propagate = this.moveFocus(event, event.shiftKey ? "previous" : "next");
		}

		if (!propagate) {
			/* ブラウザのデフォルト処理を停止 */
			if (event.preventDefault) {
				event.preventDefault(); /* W3C 準拠 (Firefox) */
			} else {
				event.returnValue = false; /* IE */
				event.keyCode = null;
			}

			/* イベント伝播を停止 */
			if (event.stopPropagation) {
				event.stopPropagation(); /* W3C 準拠 (Firefox) */
			} else {
				event.cancelBubble  = true; /* IE */
			}
		}
	},
	
	getRootElement: function(element) {
		while (element && !element._layoutId) {
			element = element.parentNode;
		}
		return element;
	},
	
	getWidget: function(element) {
		if (element) {
			var layout = maskat.app.getLayout(element._layoutId);
			return layout.getWidget(element._widgetName);
		}
		return null;
	},
	
	nextWidget: function(widget, shift) {
		var activeLayout = widget.getLayout();
		var tabIndexes = activeLayout.getTabIndexes();
		var pos = -1;
		for (var i = 0; i < tabIndexes.length; i++) {
			var name = widget.getTabIndexGroupName() || widget.name;
			if (tabIndexes[i] == name) {
				pos = i;
				break;
			}
		}
		for (var j = 0; j < tabIndexes.length - 1; j++) {
			if (!shift) {
				pos++;
				if (pos >= tabIndexes.length) pos = 0;
			} else {
				pos--;
				if (pos < 0) pos = tabIndexes.length - 1;
			}
			var next = activeLayout.getWidget(tabIndexes[pos]);
			if (next.canFocus() && this.isVisible(next)) {
				return next;
			}
		}
		return widget;
	},
	
	isVisible: function(widget) {
		do {
			if (!widget.isVisible() || !widget.isEnabled()) {
				return false;
			}
			widget = widget.parent;
		} while (widget);
		return true;
    }
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class マスカットフレームワーク用のエラークラスです。
 *
 * <p>
 * マスカットフレームワークで新しいエラークラスを定義する場合、このクラスを
 * 継承する必要があります。
 * </p>
 *
 * @name maskat.lang.Error
 * @extends Error
 */
maskat.lang.Class.declare("maskat.lang.Error").extend("Error", {

	/** @scope maskat.lang.Error.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param key エラーメッセージのメッセージキー
	 * @param param エラーのパラメータ
	 * @param cause このエラーの原因となった下位レベルのエラー
	 */
	initialize: function(key, param, cause){
		this.message = maskat.util.Message.format(key, param);
		this.key = key;
		this.param = param;
		this.cause = cause;
	},

	/**
	 * エラーメッセージの配列を返します。
	 *
	 * このエラーが下位レベルのエラーを含んでいる場合、エラーチェーンの
	 * 順番に従ってメッセージが格納されます。
	 *
	 * @returns エラーメッセージの配列
	 */
	getMessages: function() {
		var messages = [];

		var error = this;
		do {
			messages.push(error.message);
			error = error.cause;
		} while (error);

		return messages;
	},

	/**
	 * 原因となるエラーを返します。
	 * 
	 * @return 原因となるエラー
	 */
	getCauseError: function() {
		var e = this;
		if (e) {
			while (e.cause) {
				e = e.cause;
			}
		}
		return e;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * マスカットフレームワーク内部で利用される割り込みエラークラスです。
 * 呼び出し元の関数の処理に割り込んで、処理を中断または分岐させたい場合に
 * スローします。
 *
 * <p>
 * 一般に、try-catch ブロックによる例外処理をプログラムの実行フロー制御に
 * 使用するとプログラムの可読性が下がり、保守が困難になります。マスカット
 * フレームワークで規定された利用法を除いて、このクラスをアプリケーション
 * 内で利用することは推奨しません。
 * </p>
 *
 * @name maskat.lang.InterruptedError
 * @extends maskat.lang.Error
 */
maskat.lang.Class.declare("maskat.lang.InterruptedError").extend("maskat.lang.Error", {

	/** @scope maskat.lang.InterruptedError.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param key エラーメッセージのメッセージキー
	 * @param param エラーのパラメータ
	 * @param cause このエラーの原因となった下位レベルのエラー
	 */
	initialize: function(key, param, cause){
		this.base(key, param, cause);
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * 文字列を操作するためのユーティリティメソッドを提供します。
 *
 * <p>
 * このクラスは static メソッドのみを持つユーティリティクラスであり、
 * インスタンス化する必要はありません。
 * </p>
 *
 * @name maskat.lang.String
 */
maskat.lang.Class.declare("maskat.lang.String", {

	_static: {
		/** @scope maskat.lang.String.prototype */

		/**
		 * 指定した文字列に含まれている HTML 特殊文字を HTML エンティティに
		 * 置換した文字列を返します。
		 *
		 * @param string 文字列
		 *
		 * @returns HTML 特殊文字を HTML エンティティに置換した文字列
		 */
		escapeHTML: function(string) {
			var div = document.createElement("div");
			var text = document.createTextNode(string);
			div.appendChild(text);
			return div.innerHTML;
		},
	
		/**
		 * 指定した文字列に含まれている XML 特殊文字を XML エンティティに
		 * 置換した文字列を返します。
		 *
		 * @param string 文字列
		 *
		 * @returns XML 特殊文字を XML エンティティに置換した文字列
		 */
		escapeXML: function(string) {
			if (typeof(string) == "string" && string.length > 0) {
				string = string.replace(/&/g, "&amp;")
					.replace(/</g, "&lt;")
					.replace(/>/g, "&gt;")
					.replace(/"/g, "&quot;")
					.replace(/'/g, "&apos;");
			}
			return string;
		},

		/**
		 * 指定した文字列から、先頭と末尾の空白を取り除いた新しい文字列を
		 * 返します。
		 *
		 * @param string 文字列
		 *
		 * @returns 文字列の先頭と末尾の空白を取り除いた文字列
		 */
		trim: function(string){
			return string.replace(/^\s+|\s+$/g, "");
		}
	}

});
 /*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * setInterval 関数を利用した擬似スレッド処理を行うクラスです。
 * 指定されたオブジェクトのメソッドを、一定間隔で繰り返し実行します。
 *
 * @name maskat.lang.Thread
 */
maskat.lang.Class.declare("maskat.lang.Thread", {

	/** @scope maskat.lang.Thread.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param object オブジェクト
	 * @param method メソッドとして呼び出す関数、またはメソッド名の文字列
	 * @param args メソッド実行時に引数として与える配列
	 * @param interval 呼び出し間隔
	 */
	initialize: function(object, method, args, interval) {
		this.object = object;
		
		switch (typeof(method)) {
		case "string":
			this.method = object[method];
			break;
		case "function":
			this.method = method;
			break;
		}
		
		this.args = args;
		this.interval = interval;
		this.intervalId = undefined;
	},

	/**
	 * スレッドの実行を開始します。
	 */
	start: function() {
		if (!this.isRunning()) {
			var self = this;
			this.intervalId = setInterval(function() {
				self.method.apply(self.object, self.args || []);
			}, this.interval);
		}
	},
	
	/**
	 * スレッドを停止します。
	 */
	stop: function() {
		if (this.isRunning()) {
			clearInterval(this.intervalId);
			delete this.intervalId;
		}
	},

	/**
	 * スレッドが実行中かどうかを返します。
	 *
	 * @returns スレッドが実行中の場合は インターバルID、それ以外の場合は undefined
	 */
	isRunning: function() {
		return this.intervalId;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * データバインドで JavaScript 式をサポートするための疑似部品です。 
 *
 * <h2>データの取得</h2>
 * <p>
 * 要求メッセージのデータ取得元にこの部品を指定し、データ取得メソッド名に
 * 任意の JavaScript 式を指定します。
 * </p>
 *
 * <h2>データの格納</h2>
 * <p>
 * 応答メッセージのデータ格納先にこの部品を指定し、データ設定メソッド名に
 * 任意の JavaScript 式を指定します。
 * </p>
 *
 * @name maskat.layout.ExpressionEvaluator
 * @extends maskat.layout.Widget
 */ 
maskat.lang.Class.declare("maskat.layout.ExpressionEvaluator")
	.extend("maskat.layout.Widget", {

	/** @scope maskat.layout.ExpressionEvaluator.prototype */

    createWidget: function(parent) {
    	/* NOP */
    },

	getWidgetId: function(){
		return this.name;
	},

	getValue: function(key){
		return eval(key);
	},
	
	setValue: function(value, key){
		eval(key + " = value");
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * マスカットにおける画面の描画単位であるレイアウトを表現します。
 *
 * @name maskat.layout.Layout
 * @extends maskat.layout.Widget
 */
maskat.lang.Class.declare("maskat.layout.Layout")
	.extend("maskat.layout.Widget", {
	
	/** @scope maskat.layout.Layout.prototype */
	
	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.base();
		this.layoutId = null;
		this.div = null;
		this.widgets = {};
		this.scope = window;
		this.widgetVariableType = "wrapped";
		this.listeners = null;
		this.tabIndexes = [];
	},
	
	/**
	 * このレイアウトを指定された HTML 要素の子要素に描画します。
	 *
	 * @param element レイアウトを読み込む先の HTML 要素
	 * @param visible 読み込み後にレイアウト表示する場合は true、
	 *                非表示の場合は false
	 */
	load: function(element, visible) {
		this.div = document.createElement("div");
		this.div.id = this.layoutId;
		this.div.style.position = "absolute";
		this.div.style.width = "100%";
		this.div.style.zIndex = element.childNodes.length + 1;
		element.appendChild(this.div);

		this.setVisible(false);
        this.accept(new maskat.layout.WidgetCreationVisitor(this));
		this.tabIndexes = this.createTabIndexes();

		if (visible) {
			this.setVisible(true);
		}

		this.loaded = true;
		this.notifyEvent("onload");
	},

	/**
	 * このレイアウトをメモリから解放します。
	 */
	unload: function() {
		this.notifyEvent("onunload");
		this.loaded = false;
		this.setVisible(false);
        this.accept(new maskat.layout.WidgetDisposalVisitor());
	},

	/**
	 * このレイアウトを表示します。
	 */
	show: function() {
		this.setVisible(true);
        this.accept(new maskat.layout.WidgetVisibilityVisitor(true));
		this.notifyEvent("onshow");
	},

	/**
	 * このレイアウトを非表示にします。
	 */
	hide: function() {
		this.notifyEvent("onhide");
		this.setVisible(false);
        this.accept(new maskat.layout.WidgetVisibilityVisitor(false));
	},

	/**
	 * このレイアウトにイベントリスナを追加します。
	 * 
	 * @param listener イベントリスナ
	 */
	addEventListener: function(listener) {
		if (!this.listeners) {
			this.listeners = [];
		}
		this.listeners.push(listener);
	},

	/**
	 * このレイアウトに登録されたイベントリスナにマスカット部品で発生した
	 * イベントをディスパッチします。
	 * 
	 * @param event イベント
	 */
	dispatchEvent: function(event) {
		if (!this.listeners || !this.loaded) {
			return;
		}
		for (var i = 0; i < this.listeners.length; i++) {
			this.listeners[i].handleEvent(event);
		}
	},

	/**
	 * このレイアウトのレイアウト ID を取得します。
	 * 
	 * <p>
	 * レイアウト ID はマスカットアプリケーション内で一意の値となる必要が
	 * あります。
	 * </p>
	 *
	 * @returns レイアウト ID
	 */
	getWidgetId: function(){
		return this.layoutId;
	},

	/**
	 * このレイアウトに含まれるマスカット部品を取得します。
	 *
	 * @param widgetId マスカット部品 ID
	 * @returns 指定した部品 ID に対応するマスカット部品、
	 *          そのようなマスカット部品が存在しない場合は undefined
	 */
	getWidget: function(widgetId) {
		return this.widgets[widgetId];
	},

	/**
	 * このマスカット部品が参照している Ajax 部品を返します。
	 *
	 * @returns このマスカット部品が参照している Ajax 部品
	 */
	unwrap: function(){
		return this;
	},
	
	/**
	 * このレイアウトに含まれるマスカット部品を登録します。
	 *
	 * @param widget マスカット部品
	 */
	addWidget: function(widget) {
		var widgetId = widget.getWidgetId();
		if (typeof(widgetId) == "undefined") {
			return;
		}

		/* レイアウトにマスカット部品を登録 */
		this.widgets[widgetId] = widget;

		/* レイアウトにマスカット部品を登録 (マスカット 1.4.4 互換) */
		if (!this.components) {
			this.components = {};
		}
		if (widget !== this) {
			this.components[widgetId] = widget.unwrap();
		}

		/* レイアウト変数にマスカット部品を登録 */
		switch (this.widgetVariableType) {
		case "wrapped":
			this.scope[widgetId] = widget;
			break;
		case "unwrapped":
			this.scope[widgetId] = widget.unwrap();
			break;	
		}
		if (widget != this) {
			this.tabIndexes.push(widget);
		}
	},

	/**
	 * レイアウトに新しい変数を宣言します。
	 *
	 * @param name 変数名
	 * @param value 初期値
	 * @throws {maskat.lang.Error} 同じ名前の変数がすでに宣言されている場合
	 */
	defineVariable: function(name, value) {
		if (name in this.scope) {
			throw new maskat.lang.Error("DUPLICATED_VARIABLE",
				{ layoutId: this.layoutId, name: name });	
		}
		this.scope[name] = value;
	},

	/**
	 * 変数の値を取得します。
	 *
	 * @param name 変数名
	 * @param value 初期値
	 * @throws {maskat.lang.Error} 変数が未定義の場合
	 */
	getVariable: function(name) {
		if (!(name in this.scope)) {
			throw new maskat.lang.Error("UNDEFINED_VARIABLE",
				{ layoutId: this.layoutId, name: name });	
		}
		return this.scope[name];
	},

	/**
	 * 変数の値を設定します。
	 *
	 * @param name 変数名
	 * @param value 値
	 * @throws {maskat.lang.Error} 変数が未定義の場合
	 */
	setVariable: function(name, value) {
		if (!(name in this.scope)) {
			throw new maskat.lang.Error("UNDEFINED_VARIABLE",
				{ layoutId: this.layoutId, name: name });	
		}
		this.scope[name] = value;
	},

	/**
	 * このレイアウト自体への参照を返します。
	 *
	 * @returns このレイアウト自体への参照
	 */
	getLayout: function(){
		return this;
	},
	
	/**
	 * このレイアウトが参照している HTML 要素を返します。
	 *
	 * @returns このマスカット部品が参照している HTML 要素
	 */
	getElement: function(){
		return this.div;
	},

	/**
	 * このレイアウトが保持している情報を破棄します。
	 */
	dispose: function() {
		this.div.parentNode.removeChild(this.div);
		delete this.div;
		delete this.tabIndexes;
	},
	
	/**
	 * このレイアウトに含まれるマスカット部品のうち、タブインデックスを
	 * 持つものについて、タブインデックスの順序にソートされた配列を生成
	 * します。
	 */
	createTabIndexes: function() {
		var indexes = [];
		var widget = null;
		var index = null;
		
		for (var i = 0; i < this.tabIndexes.length; i++) {
			widget = this.tabIndexes[i];
			index = widget.getTabIndex();
			if (widget && index > -1) {
				if (!indexes[index]) {
					indexes[index] = [];
				}
				indexes[index].push(widget.getWidgetId());
			}
		}
		var tabIndexes = [];
		while (indexes.length > 0) {
			index = indexes.shift();
			if (index) {
				Array.prototype.push.apply(tabIndexes, index);
			}
		}
		return tabIndexes;
	},

	/**
	 * このレイアウトに含まれるマスカット部品のうち、タブインデックスを
	 * 持つものについて、タブインデックスの順序にソートされた配列を返却
	 * します。
	 */
	getTabIndexes: function() {
		return this.tabIndexes;
	},

	/**
	 * このマスカット部品の表示、非表示を定義します。
	 *
	 * @param enable 表示の場合は true、非表示の場合は false
	 */
	setVisible: function(visible){
		var element = this.getElement();
		if (element && element.style) {
			element.style.visibility = visible ? "inherit" : "hidden";
		}
		if (visible) {
			var maxZIndex = 0;
			var zIndex = element.style.zIndex;
			var children = element.parentNode.childNodes;
			
			for (var i = 0; i < children.length; i++) {
				if (children[i].style) {
					var index = children[i].style.zIndex;
					if (index) {
						if (index > maxZIndex) {
							maxZIndex = index;
						}
						if (index > zIndex) {
							children[i].style.zIndex--;
						}
					}
				}
			}
			element.style.zIndex = maxZIndex;
		}
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class レイアウトのインスタンスを生成するためのクラスです。
 *
 * @name maskat.layout.LayoutFactory
 * @extends maskat.layout.WidgetLibrary
 */
maskat.lang.Class.declare("maskat.layout.LayoutFactory")
	.extend("maskat.layout.WidgetLibrary", {

	/** @scope maskat.layout.LayoutFactory.prototype */

	config: {
		"#document": {
			children: {
				layoutDef: {}
			}
		},

		layoutDef: {
			type: maskat.layout.Layout,
			children: {
				layout: {},
				javaScriptGlobal: { property: "children", repeat: true }
			}
		},
		
		layout: {
			attributes: {
				name: { type: "string", required: true, property: "layoutId" },
				refParentHTML: { type: "string" }
			},
			children: {
				"*": { property: "children", repeat: true }
			}
		},
		
		evaluator: {
			type: maskat.layout.ExpressionEvaluator,
			attributes: {
				name: { type: "string", required: true }
			}
		},
		
		desc: {
			children: {
				"#text": {}
			}
		}
	},

	getBindingConfiguration: function() {
		return this.config;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * レイアウト定義 XML を読み込みます。
 *
 * <p>
 * LayoutXMLReader に部品ライブラリを登録することにより、レイアウト定義 XML
 * に記述可能なタグを追加できます。
 * </p>
 *
 * @name maskat.layout.LayoutXMLReader
 * @extends maskat.xml.XMLObjectBinder
 */
maskat.lang.Class.declare("maskat.layout.LayoutXMLReader")
	.extend("maskat.xml.XMLObjectBinder", {

	_static: {
		/** @scope maskat.layout.LayoutXMLReader */
		
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	/** @scope maskat.layout.LayoutXMLReader.prototype */
	
	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.base.apply(this, arguments);
		this.libraries = null;
		this.addWidgetLibrary(new maskat.layout.LayoutFactory());
	},

	createObject: function(element){
		var uri = element.namespaceURI || "";
		var widget;
		if (this.libraries[uri]) {
			widget = this.libraries[uri].create(element);
		}
		return widget || {};
	},

	/**
	 * 部品ライブラリを登録します。
	 *
	 * @param library 部品ライブラリ
	 */
	addWidgetLibrary: function(library) {
		var uri = library.getNamespaceURI() || "";
		if (!this.libraries) {
			this.libraries = {};
		}
		this.libraries[uri] = library;

		var config = library.getBindingConfiguration();
		this.addBindingConfiguration(uri, config);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * すべてのマスカット部品の基底クラスです。
 *
 * <p>
 * マスカット部品のインスタンスはアプリケーション画面に表示される個々の部品
 * と対応し、レイアウトをルートとするツリー構造を構成します。通常、この構造
 * はレイアウト定義 XML を読み込むことで生成されます。
 * </p>
 * 
 * <h2>Ajax 部品のラッピング</h2>
 * <p>
 * マスカットフレームワークでは Ajax アプリケーションの画面で利用する部品
 * を独自に開発・提供することが目的ではないため、サードパーティ製の Ajax
 * ライブラリを薄くラップしたものを部品として利用します。
 * Ajax 部品をラップするためのクラス (Ajax 部品ラッパー) は Widget クラス
 * を継承して実装する必要があり、Ajax 部品に対して以下の機能を追加します。
 * </p>
 * <ul>
 *   <li>レイアウト定義 XML に記述された設定値やタグの親子関係を利用して
 *   Ajax 部品を生成する機能</li>
 *   <li>イベント定義 XML に記述された設定値を利用して Ajax 部品に対する
 *   データの設定・取得を行う機能</li>
 * </ul>
 *
 * <h2>Ajax 部品の生成</h2>
 * <p>
 * 部品の生成時には {@link maskat.layout.Layout#load} メソッドが生成した
 * {@link maskat.layout.WidgetCreationVisitor} オブジェクトにより、以下の
 * 順番で部品の生成メソッドが実行されます。
 * </p>
 * 
 * <ol>
 *   <li>createWidtet(parent)</li>
 *   <li>setParent(parent)</li>
 *   <li>(… XML の子要素として記述した部品の生成処理 …)</li>
 *   <li>postCreateWidget()</li>
 * </ol>
 * 
 * <p>
 * 1. および 2. では親となるマスカット部品が引数 parent として渡されます。
 * マスカット部品の実装クラスでは、利用する Ajax ライブラリの特性に応じて
 * HTML DOM の親子関係をいずれかのタイミングで接続します。
 * </p>
 * 
 * <ul>
 *   <li>多くのライブラリでは、DOM の親子関係を接続した状態で Ajax 部品の
 *   コンストラクタに HTML 要素を渡すことを要求します。この場合には 1. で
 *   HTML 要素の生成と親要素への接続を行い、Ajax 部品を生成します。</li>
 *   <li>Ajax 部品の生成後、親となる Ajax 部品に対して追加メソッドを実行
 *   することで、ライブラリ側で DOM の親子関係を設定する場合もあります。
 *   このような場合、1. で Ajax 部品の生成を行い、2. で部品の追加メソッド
 *   を実行します。</li>
 * </ul>
 * 
 * <p>
 * 生成した Ajax 部品は、マスカット部品の実装クラスの unwrap() メソッドに
 * よって取得可能です。
 * </p>
 *
 * @name maskat.layout.Widget
 */
maskat.lang.Class.declare("maskat.layout.Widget", {

	/** @scope maskat.layout.Widget.prototype */

	/** デフォルトのデータ取得元となるメソッド名またはプロパティ名 */
	defaultGetter: "value",

	/** デフォルトのデータ格納先となるメソッド名またはプロパティ名 */
	defaultSetter: "value",

	/**
	 * コンストラクタです。
	 */
	initialize: function(){
		this.parent = null;
		this.children = null;
		this.tabIndex = -1;
		this.keybinds = null;
	},
	
	/**
	 * このマスカット部品を格納しているレイアウトを取得します。
	 *
	 * <p>
	 * マスカット部品はツリー構造による包含関係の階層を持っており、この
	 * ツリー構造におけるルートノードがレイアウトとなります。
	 * </p>
	 *
	 * @returns このマスカット部品を格納しているレイアウト
	 */
	getLayout: function(){
		var parent = this.getParent();
		return parent ? parent.getLayout() : null;
	},
	
	/**
	 * このマスカット部品の親となるマスカット部品を取得します。
	 *
	 * @returns このマスカット部品の親となるマスカット部品
	 */
	getParent: function(){
		return this.parent;
	},
	
	/**
	 * このマスカット部品の親となるマスカット部品を設定します。
	 *
	 * @param parent このマスカット部品の親となるマスカット部品
	 */
	setParent: function(parent){
		this.parent = parent;
	},
	
	/**
	 * このマスカット部品に子ノードとなるマスカット部品を追加します。
	 *
	 * @param parent 子ノードとなるマスカット部品
	 */
	addChild: function(child){
		if (!this.children) {
			this.children = [];
		}
		this.children.push(child);
		child.setParent(this);
	},
	
	/**
	 * このマスカット部品の子ノードの配列を取得します。
	 *
	 * @returns このマスカット部品の子ノードの配列
	 */
	getChildren: function(){
		return this.children;
	},
	
	/**
	 * このマスカット部品の部品 ID を取得します。
	 * 
	 * <p>
	 * 部品 ID はレイアウト内で一意の値となる必要があります。
	 * </p>
	 *
	 * @returns このマスカット部品の部品 ID
	 */
	getWidgetId: function(){
		return null;
	},
	
	/**
	 * このマスカット部品が参照している Ajax 部品を返します。
	 * 
	 * @deprecated このメソッドは以前のバージョンとの互換性のために残されて
	 *               おり、将来的に廃止される予定です。unwrap メソッドの使用
	 *               を推奨します。
	 * @returns このマスカット部品が参照している Ajax 部品
	 */
	getWidget: function(){
		/*
		 * サブクラスが unwrap() メソッドをオーバーライドしている場合、
		 * unwrap() メソッドへ処理を移譲する
		 */
		if (this.unwrap != maskat.layout.Widget.prototype.unwrap) {
			return this.unwrap();
		}
		return null;
	},
	
	/**
	 * このマスカット部品が参照している Ajax 部品を返します。
	 *
	 * @returns このマスカット部品が参照している Ajax 部品
	 */
	unwrap: function(){
		return this.getWidget();
	},
	
	/**
	 * このマスカット部品が参照する Ajax 部品を生成し、その Ajax 部品への
	 * 参照を返します。
	 *
	 * @param parent このマスカット部品の親となるマスカット部品
	 * @returns 生成した Ajax 部品
	 */
	createWidget: function(parent){
		return null;
	},
	
	/**
	 * Ajax 部品を生成後の後処理を行います。
	 */
	postCreateWidget: function(){
	},
	
	/**
	 * このマスカット部品が使用しているリソースを開放します。
	 */
	dispose: function(){
		maskat.lang.Object.dispose(this);
	},
	
	/**
	 * このマスカット部品が参照している HTML 要素を返します。
	 *
	 * @returns このマスカット部品が参照している HTML 要素
	 */
	getElement: function(){
		return null;
	},
	
	/**
	 * このマスカット部品の内部にある DOM ツリーに対し、指定した HTML 要素を
	 * 適切な位置へ格納します。
	 *
	 * @param element 追加する HTML 要素
	 */
	appendChildElement: function(element){
		var parent = this.getElement();
		if (parent) {
			parent.appendChild(element);
		}
	},
	
	/**
	 * このマスカット部品に含まれるコントロール要素を返します。
	 *
	 * </p>
	 * コントロール要素は HTML DOM 要素のうち、以下のいずれかのクラスに属する
	 * ものです。
	 * </p>
	 *
	 * <ul>
	 *   <li>HTMLAnchorElement</li>
	 *   <li>HTMLInputElement</li>
	 *   <li>HTMLSelectElement</li>
	 *   <li>HTMLTextAreaElement</li>
	 * </ul>
	 *
	 * <p>
	 * 詳細については Document Object Model (DOM) Level 2 Specification を
	 * 参照してください。
	 * </p>
	 *
	 * @returns このマスカット部品に含まれるコントロール要素
	 * @see <a href="http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510/html.html">
	 *      Document Object Model (DOM) Level 2 Specification</a>
	 */
	getControlElement: function(){
		return null;
	},
	
	/**
	 * このマスカット部品が保持しているデータ値を取得します。
	 *
	 * @param key データ取得キー
	 * @returns このマスカット部品が保持しているデータ値
	 */
	getValue: function(key){
		if (typeof(key) == "undefined") {
			key = this.defaultGetter;
		}
	
		if (typeof(this[key]) == "function") {
			return this[key]();
		} else {
			return this[key];
		}
	},
	
	/**
	 * このマスカット部品にデータ値を格納します。
	 *
	 * @param value このマスカット部品に格納するデータ値
	 * @param key データ設定キー
	 */
	setValue: function(value, key){
		if (typeof(key) == "undefined") {
			key = this.defaultSetter;
		}

		if (typeof(this[key]) == "function") {
			this[key](value);
		} else {
			this[key] = value;
		}
	},

	/**
	 * このマスカット部品にデータ値をリセットします。
	 */
	clear: function(){
		this.setValue(undefined);
	},
	
	/**
	 * このマスカット部品に対するキーイベントを処理します。
	 *
	 * @param event キーイベント
	 *
	 * @returns 部品がキーイベントを処理した場合は false、それ以外は true
	 */
	handleKeyEvent: function(event){
		return true;
	},

	/**
	 * このマスカット部品が、引数で指定されたキーイベントに対応するキー
	 * バインドを持っている場合、そのキーバインドを返します。
	 * 
	 * @param event
	 *            キーイベント
	 * @returns キーイベントに対応するキーバインド 
	 */
	getKeybind: function(event) {
		if (this.keybinds) {
			for (var i = 0; i < this.keybinds.length; i++) {
				var keybind = this.keybinds[i];
				if (keybind.match(event)) {
					return keybind;
				}
			}
		}
		return undefined;
	},

	/**
	 * このマスカット部品にキーバインド追加します。
	 * 
	 * @param keybind
	 *            キーバインド
	 */
	addKeybind: function(keybind) {
		var layout = this.getLayout();
		
		/* キーバインド文字列を解析してキーコードを生成 */
		keybind.decode();

		/* コマンドと部品を接続する */
		if (keybind.commands) {
			for (var i = 0; i < keybind.commands.length; i++) {
				var command = keybind.commands[i];
				if (command.target) {
					var targetWidgetId = command.target;
					command.target = layout.getWidget(targetWidgetId);
					if (typeof(command.target) == "undefined") {
						throw new maskat.lang.Error("KEYBINDING_ERROR", { widget: targetWidgetId });
					}
				}
			}
		}

		if (!this.keybinds) {
			this.keybinds = [];
		}
		this.keybinds.push(keybind);
	},

	/**
	 * このマスカット部品にフォーカスを設定します。
	 */
	setFocus: function(){
		var element = this.getControlElement();
		if (element && typeof(element.focus) == "function") {
			element.focus();
		}
	},
	
	/**
	 * このマスカット部品のフォーカス移動が可能かを返します。
	 *
	 * @returns 部品にフォーカスを移動できる場合はtrue、できない場合はfalse
	 */
	canFocus: function(){
		return this.tabIndex > -1 && this.isEnabled();
	},
	
	/**
	 * このマスカット部品が表示されているかどうかを返します。
	 *
	 * @returns 表示の場合は true、非表示の場合は false
	 */
	isVisible: function(){
		var element = this.getElement();
		var visibility;

		while (element && (!visibility || visibility == "inherit")) {
			if (element && element.style && element.style.visibility) {
				/* 要素に可視性が設定されている場合、その値を取得 */
				visibility = element.style.visibility;
			} else {
				/* 要素の可視性を計算する */
				var style = element.currentStyle || document.defaultView.getComputedStyle(element, null);
				visibility = style["visibility"];
			}
			element = element.parentNode;
			
			if (!element ||
				element.nodeName == "#document" ||
				element.nodeName == "#document-fragment") {
				return true;
			}
		}
		return visibility == "visible";
	},
	
	/**
	 * このマスカット部品の表示、非表示を定義します。
	 *
	 * @param enable 表示の場合は true、非表示の場合は false
	 */
	setVisible: function(visible){
		var element = this.getElement();
		if (element && element.style) {
			element.style.visibility = visible ? "inherit" : "hidden";
		}
	},

	/**
	 * このマスカット部品が有効かどうかを返します。
	 *
	 * @returns 有効の場合はtrue、無効の場合は false
	 */
	isEnabled: function(){
		var element = this.getControlElement();
		if (element && typeof(element.disabled) != "undefined") {
			return element.disabled;
		}
		return true;
	},
	
	/**
	 * このマスカット部品が有効かどうかを設定します。
	 *
	 * @param enable 有効の場合は true、無効の場合は false
	 */
	setEnabled: function(enabled){
		var element = this.getControlElement();
		if (element && typeof(element.disabled) != "undefined") {
			element.disabled = enabled;
		}
	},
	
	/**
	 * このマスカット部品のタブインデックスを返します。
	 *
	 * @returns タブインデックス
	 */
	getTabIndex: function(){
		return this.tabIndex;
	},
	
	/**
	 * このマスカット部品のタブインデックスグループ名を返します。
	 *
	 * @returns タブインデックスグループ名
	 */
	getTabIndexGroupName: function(){
		return this.tabIndexGroupName;
	},
	
	/**
	 * このマスカット部品で発生したイベントをリスナに通知します。
	 *
	 * @param eventType イベントタイプ
	 */
	notifyEvent: function(eventType){
		var layout = this.getLayout();
		var event = new maskat.event.Event(layout, this, eventType);
		layout.dispatchEvent(event);
	},
	
	/**
	 * マスカット部品のツリー構造を走査する WidgetVisitor を受け入れます。
	 *
	 * @param visitor 受け入れる WidgetVisitor のインスタンス
	 */
	accept: function(visitor){
		var visitChildren = visitor.visit(this);
		var children = this.getChildren();
		if (visitChildren && children) {
			for (var i = 0; i < children.length; i++) {
				if (children[i] instanceof maskat.layout.Widget) {
					children[i].accept(visitor);
				}
			}
		}
		visitor.postVisit(this);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
 /**
 * @class
 * レイアウトのツリー構造を走査し、上位の要素から順番にマスカット部品の
 * 生成処理を実行します。
 *
 * @name maskat.layout.WidgetCreationVisitor
 * @extends maskat.layout.WidgetVisitor
 */
maskat.lang.Class.declare("maskat.layout.WidgetCreationVisitor")
	.extend("maskat.layout.WidgetVisitor", {

	/** @scope maskat.layout.WidgetCreationVisitor.prototype */
	
	/**
	 * コンストラクタです。
	 *
	 * @param layout マスカット部品の生成処理を実行するレイアウト 
	 */
	initialize: function(layout) {
		this.layout = layout;
		this.stack = [];
	},

	visit: function(widget) {
		/*
		 * スタックの内容はレイアウトからのパス (マスカット部品の列) であり、
		 * この部品の親はスタックのトップから取得できる
		 */
		var parent;
		if (this.stack.length > 0) {
			parent = this.stack[this.stack.length - 1];
		}
	
		/* マスカット部品の生成処理を行い、レイアウトに登録する */
		widget.createWidget(parent);
		widget.setParent(parent);
		this.layout.addWidget(widget);

		/* 訪問したマスカット部品をスタックに積む */
		this.stack.push(widget);
		return true;
	},
	
	postVisit: function(widget) {
		widget.postCreateWidget();
		var element = widget.getElement();
		if (element) {
			element._layoutId = widget.getLayout().getWidgetId();
			element._widgetName = widget.getWidgetId();
		}
		this.stack.pop();
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * レイアウトのツリー構造を走査し、下位の要素から順番にマスカット部品の
 * 破棄処理を実行します。
 *
 * @name maskat.layout.WidgetDisposalVisitor
 * @extends maskat.layout.WidgetVisitor
 */
maskat.lang.Class.declare("maskat.layout.WidgetDisposalVisitor")
	.extend("maskat.layout.WidgetVisitor", {

	/** @scope maskat.layout.WidgetDisposalVisitor.prototype */

	postVisit: function(widget) {
		try {
			widget.dispose();
		} catch (e) { /* suppress */ }
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * 部品ライブラリを定義します。
 *
 * <p>
 * 部品ライブラリはレイアウト定義 XML に記述可能なタグを追加するための
 * 拡張メカニズムであり、JSP におけるタグライブラリの概念と似ています。
 * 部品ライブラリを利用すると、サードパーティ製の Ajax ツールキットや 
 * JavaScript ライブラリの持つ機能をマスカットに取り込むことができます。
 * </p>
 *
 * @name maskat.layout.WidgetLibrary
 */
maskat.lang.Class.declare("maskat.layout.WidgetLibrary", {

	/** @scope maskat.layout.WidgetLibrary.prototype */
	
	/**
	 * この部品ライブラリがデフォルトで使用する名前空間接頭辞を返します。
	 *
	 * @returns デフォルトの名前空間接頭辞
	 */
	getPrefix: function() {
		return null;
	},

	/**
	 * この部品ライブラリの名前空間 URI を返します。
	 *
	 * @returns 部品ライブラリの名前空間 URI
	 */
	getNamespaceURI: function() {
		return null;
	},

	/**
	 * この部品ライブラリのバインディング設定を返します。
	 *
	 * @returns 部品ライブラリのバインディング設定
	 */
	getBindingConfiguration: function() {
		return null;
	},

	/**
	 * 指定した XML 要素をオブジェクトに変換します。
	 *
	 * <p>
	 * このメソッドを実装することにより、XMLObjectBinder のデフォルトの
	 * オブジェクト生成方法を変更することができます。
	 * </p>
	 *
	 * @returns XML 要素から変換されたオブジェクト
	 */
	create: function(element) {
		return null;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
 /**
 * @class
 * レイアウトのツリー構造を走査し、上位の要素から順番にマスカット部品の
 * 表示非表示処理を実行します。
 *
 * @name maskat.layout.WidgetVisibilityVisitor
 * @extends maskat.layout.WidgetVisitor
 */
maskat.lang.Class.declare("maskat.layout.WidgetVisibilityVisitor")
	.extend("maskat.layout.WidgetVisitor", {

	/** @scope maskat.layout.WidgetVisibilityVisitor.prototype */
	
	/**
	 * コンストラクタです。
	 *
	 * @param enable 表示の場合は true、非表示の場合は false
	 */
    initialize: function(visible) {
		this.visible = visible;
	},
	
	postVisit: function(widget) {
		widget.setVisible(this.visible);
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * レイアウトのツリー構造を走査して処理を実行します。
 *
 * <p>
 * Visitor パターンを利用することにより、レイアウトやマスカット部品を
 * 変更せずに新しい処理を定義することが可能です。
 * </p>
 *
 * @name maskat.layout.WidgetVisitor
 */
maskat.lang.Class.declare("maskat.layout.WidgetVisitor", {

	/** @scope maskat.layout.WidgetVisitor.prototype */
	
	/**
	 * 指定されたマスカット部品を訪問します。
	 *
	 * <p>
	 * このメソッドはレイアウトのツリー構造の走査において行きがけ順
	 * (preorder) で呼び出されます。
	 * </p>
	 *
	 * @param widget 訪問するマスカット部品
	 * @returns 子要素を走査する場合は true, それ以外の場合は false
	 */
	visit: function(widget) {
		return true;
	},
	
	/**
	 * 指定されたマスカット部品を訪問を終了します。
	 *
	 * <p>
	 * このメソッドはレイアウトのツリー構造の走査において帰りがけ順
	 * (postorder) で呼び出されます。
	 * </p>
	 *
	 * @param widget 訪問するマスカット部品
	 */
	postVisit: function(widget) {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ログ出力 API を定義する抽象クラスです。
 *
 * @name maskat.log.Log
 */
maskat.lang.Class.declare("maskat.log.Log", {
	
	_static: {
		/** @scope maskat.log.Log */
		
		/** トレースのログレベルを表す定数 */
		TRACE: 1,

		/** デバッグのログレベルを表す定数 */
		DEBUG: 2,

		/** 情報のログレベルを表す定数 */
		INFO: 3,

		/** 警告のログレベルを表す定数 */
		WARN: 4,

		/** エラーのログレベルを表す定数 */
		ERROR: 5,

		/** 致命的エラーのログレベルを表す定数 */
		FATAL: 6
	},
	
	/** @scope maskat.log.Log.prototype */
	
	/**
	 * コンストラクタです。 
	 */
	initialize: function() {
		/* NOP */
	},

	/**
	 * トレースログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	trace: function(message, error) {
		/* NOP */
	},

	/**
	 * デバッグログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	debug: function(message, error) {
		/* NOP */
	},

	/**
	 * 情報ログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	info: function(message, error) {
		/* NOP */
	},

	/**
	 * 警告ログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	warn: function(message, error) {
		/* NOP */
	},

	/**
	 * エラーログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	error: function(message, error) {
		/* NOP */
	},

	/**
	 * 致命的エラーログを記録します。
	 *
	 * @param message ログメッセージ
	 * @param error エラーオブジェクト (省略可能)
	 */
	fatal: function(message, error) {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ログ出力に使用するロギングフレームワークを切り替えるためのファクトリを
 * 定義する抽象クラスです。
 *
 * <p>
 * ログファクトリには指定された名前をもとに、適切なロガーオブジェクトを
 * 生成して返却する責任があります。
 * </p>
 *
 * @name maskat.log.LogFactory
 */
maskat.lang.Class.declare("maskat.log.LogFactory", {

	_static: {
		/** @scope maskat.log.LogFactory */
	
		/** ロガーオブジェクトのキャッシュ */
		cache: {},

		/**
		 * ロギング API を提供するロガーオブジェクトを取得します。
		 *
		 * @param name ロガーオブジェクトの名前
		 *
		 * @returns ロガーオブジェクト
		 */
		getLog: function(name) {
			/* ロガーがキャッシュされている場合はそれを返却する */
			if (this.cache[name]) {
				return this.cache[name];
			}

			/* ログファクトリ経由でロガーを生成 */
			var log = this.getFactory().createLog(name);
			this.cache[name] = log;
			return log;
		},

		/** ロガーの生成に用いるログファクトリ */
		factory: null,

		/**
		 * ロガーの生成に用いるログファクトリを返却します。
		 *
		 * @returns ログファクトリ
		 */
		getFactory: function() {
			/* ログファクトリが未定義の場合は SimpleLogFactory を使用 */
			if (!this.factory) {
				this.factory = new maskat.log.SimpleLogFactory();
			}
			return this.factory;
		}
	},

	/** @scope maskat.log.LogFactory.prototype */

	/**
	 * ロガーオブジェクトを生成します。
	 *
	 * このメソッドは抽象メソッドです。サブクラスはこのメソッドを実装し、
	 * ロガーを返却する必要があります。
	 *
	 * @param name ロガーオブジェクトの名前
	 */
	createLog: function(name) {
		return null;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ログ出力を全く行わないダミーのログ実装クラスです。
 *
 * @name maskat.log.NullLog
 * @extends maskat.log.Log
 */
 maskat.lang.Class.declare("maskat.log.NullLog").extend("maskat.log.Log", {

	_static: {
		/** @scope maskat.log.NullLog */
	
		/**
		 * このクラスの唯一のインスタンスを返します。(Singleton パターン)
		 *
		 * @returns このクラスの唯一のインスタンス
		 */
		getInstance: function() {
			var self = arguments.callee;
			if (!self.instance) {
				self.instance = new this();
			}
			return self.instance;
		}
	},
	
	trace: function(message, error) {
		/* NOP */
	},

	debug: function(message, error) {
		/* NOP */
	},

	info: function(message, error) {
		/* NOP */
	},

	warn: function(message, error) {
		/* NOP */
	},

	error: function(message, error) {
		/* NOP */
	},

	fatal: function(message, error) {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ログ出力 API として {@link maskat.log.NullLog} クラスを利用するための
 * ログファクトリの実装クラスです。
 *
 * @name maskat.log.NullLogFactory
 * @extends maskat.log.LogFactory
 */
maskat.lang.Class.declare("maskat.log.NullLogFactory")
	.extend("maskat.log.LogFactory", {

	/** @scope maskat.log.NullLogFactory.prototype */

	/**
	 * ログ出力を全く行わないダミーのログ実装を返します。
	 *
	 * @param name ロガーオブジェクトの名前
	 */
	createLog: function(name) {
		return maskat.log.NullLog.getInstance();
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * 簡易的なログ出力 API の実装クラスです。
 * ログ出力時にポップアップウインドウを開いてログメッセージを表示します。
 *
 * <p>
 * このクラスはマスカットフレームワークのデフォルトのログ出力 API として
 * 利用されます。
 * </p>
 *
 * @name maskat.log.SimpleLog
 * @extends maskat.log.Log
 */
maskat.lang.Class.declare("maskat.log.SimpleLog").extend("maskat.log.Log", {

	/** @scope maskat.log.SimpleLog.prototype */

	initialize: function(name, level, focus) {
		this.base.apply(this, arguments);
		this.name = name;
		this.level = level || maskat.log.Log.WARN;
		this.focus = focus || false;
	},

	trace: function(message, error) {
		if (this.level <= maskat.log.Log.TRACE) {
			this.log("TRACE", message, "#666666");
		}
	},

	debug: function(message, error) {
		if (this.level <= maskat.log.Log.DEBUG) {
			this.log("DEBUG", message, "green");
		}
	},

	info: function(message, error) {
		if (this.level <= maskat.log.Log.INFO) {
			this.log("INFO", message, "#000099");
		}
	},

	warn: function(message, error) {
		if (this.level <= maskat.log.Log.WARN) {
			this.log("WARN", message, "#999900");
		}
	},

	error: function(message, error) {
		if (this.level <= maskat.log.Log.ERROR) {
			this.log("ERROR", message, "red");
		}
	},

	fatal: function(message, error) {
		if (this.level <= maskat.log.Log.FATAL) {
			this.log("FATAL", message, "#660066");
		}
	},
	
	console: null,

	lineCounter: 0,

	log: function(level, message, color) {
		var doc = this.getLogDocument();
		var msgDiv = doc.createElement("div");
		// TODO: エラー処理　各ブラウザごとに改行させる
		// word-breakはIEのみ有効
		//"white-space:pre-wrap;"       /* css-3 */
		//"white-space:-moz-pre-wrap;"  /* Mozilla, since 1999 */
		//"white-space:-pre-wrap;"      /* Opera 4-6, FireFox3 */
		//"white-space:-o-pre-wrap;"    /* Opera 7 */
		//"word-break:break-all;"       /* IE5.5+ */
		var wrap;
		if (navigator.userAgent.indexOf("MSIE") != -1) {
			wrap = "word-break:break-all;";
		} else {
			wrap = "white-space:pre-wrap;white-space:-moz-pre-wrap;";
		}
		var style = "border-top:1px solid #cecece;margin-top:5px;padding-left:5px;font-size:12px;" + wrap;
		var colorStyle = (this.lineCounter++ % 2) == 0 ? "#eeeeee" : "white";
		var timeStyle = "'float:left;width:28%;background-color:#525D76;color:white;" + style + "'";
		var msgStyle =  "'float:left;width:70%;background-color:" + colorStyle + ";" + style + "min-height:2.5em;_height:3em;'";
		var levelMsg = "\n<span style='color:while;font-weight:bold;background-color:" +
				color + ";'> " + level + " </span>";
				
		message = maskat.lang.String.escapeHTML(message);
		msgDiv.innerHTML =
			"<pre><span style=" + timeStyle + ">" + (new Date()).toLocaleString() + levelMsg + "</span>" +
			"<span style=" + msgStyle + ">" + message + "</span></pre>";
			
		var div = this.getLogElement(doc);
		if (!div) {
			if (!this.fragment) {
				this.fragment = doc.createDocumentFragment();
			}
			this.fragment.appendChild(msgDiv);
			
			if (!this.documentStatus) {
				var self = this;
				doc.onreadystatechange = function() {
					if (this.readyState == "complete") {
						self.checkBodyElement();
						self.documentStatus = false;
					}
				}
				this.documentStatus = true;
				doc.onreadystatechange();
			}
		} else {
			div.appendChild(msgDiv);
			this.adjustMessagePosition(div);
		}
	},	
	
	adjustMessagePosition: function(div) {
		div.scrollTop = div.scrollHeight - div.offsetHeight;
		if (this.focus) {
			if (navigator.appName == "Netscape") {
				window.blur();
			}
			this.console.focus();
		}
	},
	
	getLogDocument: function() {
		if (!this.console || this.console.closed) {
			var prop = "width=700,height=200,titlebar=no,toolbar=no,status=no,resizable,dependent=yes";
			this.console = window.open("", "maskatSimpleLog", prop);
		}
		return this.console.document;
	},

	getLogElement: function(doc) {
		var element = doc.getElementById("maskat.log.SimpleLog");
		if (!element) {
			var body = doc.getElementsByTagName("body");
			if (!body || !(body.item(0))) {
				return null;
			}
			element = doc.createElement("div");
			element.setAttribute("id", "maskat.log.SimpleLog");
			element.innerHTML = "<div style='position:absolute;height:95%;width:99%;overflow:auto;'/>";
			body.item(0).appendChild(element);
		}
		return element.getElementsByTagName("div").item(0);
	},

	checkBodyElement: function() {
		var div = this.getLogElement(this.getLogDocument());
		if (this.fragment) {
			div.appendChild(this.fragment);
			this.adjustMessagePosition(div);
			this.fragment = null;
		}
	}
});

/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ログ出力 API として {@link maskat.log.SimpleLog} クラスを利用するための
 * ログファクトリの実装クラスです。
 *
 * <p>
 * このファクトリはマスカットのプロパティファイルの設定値に応じて出力する
 * ログレベルを切り替える機能を持っています。
 * </p>
 *
 * @name maskat.log.SimpleLogFactory
 * @extends maskat.log.LogFactory
 */
maskat.lang.Class.declare("maskat.log.SimpleLogFactory")
	.extend("maskat.log.LogFactory", {

	/** @scope maskat.log.SimpleLogFactory.prototype */

	/**
	 * コンストラクタです。
	 */
	initialize: function() {
		/* ログレベルの文字列 ("INFO", "DEBUG" 等) を取得 */
		this.defaultLevel = maskat.app.getProperty("log.default.level");
	},

	/**
	 * デフォルトのロガーオブジェクトを生成します。
	 *
	 * @param name ロガーオブジェクトの名前
	 */
	createLog: function(name) {
		/* ログレベルの文字列 ("INFO", "DEBUG" 等) を取得 */
		var level = maskat.app.getProperty("log." + name + ".level")
			|| this.defaultLevel;
		var focus = maskat.app.getProperty("log.popup.focus");
		return new maskat.log.SimpleLog(name, maskat.log.Log[level], focus);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * メッセージダイアログの API を規定する抽象クラスです。
 *
 * <p>
 * メッセージダイアログはユーザにメッセージを表示し、ダイアログが閉じられた
 * さいにコールバック関数を実行します。
 * </p>
 *
 * @name maskat.ui.AlertDialog
 */ 
maskat.lang.Class.declare("maskat.ui.AlertDialog", {

	/** @scope maskat.ui.AlertDialog.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param title タイトル文字列
	 * @param message メッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時に呼び出されるコールバック関数
	 */
	initialize: function (title, message, icon, onClick) {
		this.title = title;
		this.message = message;
		this.icon = icon;
		this.onClick = onClick;

		/* コールバック関数が未指定の場合は何もしない */
		if (typeof(this.onClick) != "function") {
			this.onClick = maskat.lang.EmptyFunction;
		}
	},

	/**
	 * ダイアログを表示します。
	 */
	open: function () {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 確認ダイアログを表示するクラスです。
 *
 * ダイアログが閉じられたときにコールバック関数を実行します。
 *
 * @name maskat.ui.ConfirmDialog
 */
maskat.lang.Class.declare("maskat.ui.ConfirmDialog", {

	/** @scope maskat.ui.ConfirmDialog.prototype */

	/**
	 * javascriptの確認ダイアログコンストラクタです。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	initialize: function (title, message, icon, onClick) {
		this.title = title;
		this.message = message;
		this.icon = icon;
		this.onClick = onClick;
		/* 戻り関数の存在チェック、無いときはEmpty関数をセット*/
		if (typeof(this.onClick) != "function") {
			this.onClick = maskat.lang.EmptyFunction;
		}
	},

	/**
	 * 確認ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
		/* NOP */
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * ダイアログの表示方法を切り替えるためのファクトリです。
 *
 * @name maskat.ui.Dialog
 */ 
maskat.lang.Class.declare("maskat.ui.Dialog", {

	_static: {
		/** @scope maskat.ui.Dialog */
		
		/** 「はい」ボタンを表す定数値 */
		YES: "YES",

		/** 「いいえ」ボタンを表す定数値 */
		NO: "NO",

		/** 「OK」ボタンを表す定数値 */
		OK: "OK",

		/** 「キャンセル」ボタンを表す定数値 */
		CANCEL: "CANCEL",

		/** ダイアログの生成に用いるダイアログファクトリ */
		factory: null,

		/**
		 * ダイアログの生成に用いるダイアログファクトリを取得します。
		 *
		 * @returns ダイアログファクトリ
		 */
		getFactory: function() {
			/* ダイアログファクトリが未定義の場合は JavaScriptDialogFactory を使用 */
			if (!this.factory) {
				this.factory = new maskat.ui.JavaScriptDialogFactory();
			}
			return this.factory;
		},
		
		/**
		 * 確認ダイアログをPOPUPして表示します。
		 *
		 * @param title ダイアログのタイトル文字列
		 * @param message ダイアログメッセージ文字列
		 * @param icon アイコン種別
		 * @param onClick ボタン押下時の呼び出し関数
		 */
		openMessage: function (title, message, icon, onClick)  {
			return this.openConfirm(title, message, icon, onClick);
		},
		
		/**
		 * 確認ダイアログをPOPUPして表示します。
		 *
		 * @param title ダイアログのタイトル文字列
		 * @param message ダイアログメッセージ文字列
		 * @param icon アイコン種別
		 * @param onClick ボタン押下時の呼び出し関数
		 */
		openConfirm: function (title, message, icon, onClick)  {
			/* メッセージ表示クラスをファクトリ経由で生成 */
			var dialog = this.getFactory().createConfirm(title, message, icon, onClick);
			dialog.open();

			return dialog;
		},
		
		/**
		 * 入力ダイアログをPOPUPして表示します。
		 *
		 * @param title ダイアログのタイトル文字列
		 * @param message ダイアログメッセージ文字列
		 * @param icon アイコン種別
		 * @param onClick ボタン押下時の呼び出し関数
		 */
		openInput: function (title, message, icon, onClick) {
			/* 入力ダイアログクラスをファクトリ経由で生成 */
			var dialog = this.getFactory().createInput(title, message, icon, onClick);
			dialog.open ();

			return dialog;
		},
		
		/**
		 * アラートをPOPUPして表示します。
		 *
		 * @param title ダイアログのタイトル文字列
		 * @param message ダイアログメッセージ文字列
		 * @param icon アイコン種別
		 * @param onClick ボタン押下時の呼び出し関数
		 */
		openAlert: function (title, message, icon, onClick) {
			/* アラート表示ダイアログクラスをファクトリ経由で生成 */
			var dialog = this.getFactory().createAlert(title, message, icon, onClick);
			dialog.open ();

			return dialog;
		},
		
		/**
		 * 進捗状況ウィンドウをPOPUPして表示します。
		 *
		 * @param title ダイアログのタイトル文字列
		 * @param message ダイアログメッセージ文字列
		 * @param goalValue 終了進捗値(オプション)
		 * @returns POPUPのインスタンス
		 */
		openProgress: function (title, message, goalValue)  {
			/* 進捗表示ダイアログクラスをファクトリ経由で生成 */
			var dialog = this.getFactory().createProgress(title, message, goalValue);
			dialog.open ();

			return dialog;
		}
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class ダイアログ API を切り替えるためのファクトリを定義するクラスです。
 *
 * ダイアログファクトはロードされているプログインをもとに、適切なダイアログオブジェクトを
 * 生成して返却します。
 *
 * @name maskat.ui.DialogFactory
 */
maskat.lang.Class.declare("maskat.ui.DialogFactory", {

	/** @scope maskat.ui.DialogFactory.prototype */

	/**
	 * 確認ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createConfirm: function (title, message, icon, onClick) {
		/* NOP */
	},
	
	/**
	 * アラートダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createAlert: function (title, message, icon, onClick) {
		/* NOP */
	},
	
	/**
	 * 入力ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createInput: function (title, message, icon, onClick) {
		/* NOP */
	},
	
	/**
	 * 進捗表示ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 */
	createProgress: function (title, message) {
		/* NOP */
	}
	
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 入力ダイアログを表示するクラスです。
 *
 * ダイアログが閉じられたときにコールバック関数を実行します。
 *
 * @name maskat.ui.InputDialog
 */ 
maskat.lang.Class.declare("maskat.ui.InputDialog", {

	/** @scope maskat.ui.InputDialog.prototype */

	/**
	 * javascriptの入力ダイアログコンストラクタです。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	initialize: function (title, message, icon, onClick) {
		this.title = title;
		this.message = message;
		this.icon = icon;
		this.onClick = onClick;
		/* 戻り関数の存在チェック、無いときは何もしない*/
		if (typeof(this.onClick) != "function") {
			this.onClick = maskat.lang.EmptyFunction;
		}
	},

	/**
	 * 入力ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
		/* NOP */
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class JavaScriptのメッセージダイアログを表示するクラスです。
 *
 * @name maskat.ui.JavaScriptAlertDialog
 * @extends maskat.ui.AlertDialog
 */
maskat.lang.Class.declare("maskat.ui.JavaScriptAlertDialog")
	.extend("maskat.ui.AlertDialog", {

	/** @scope maskat.ui.JavaScriptAlertDialog.prototype */

	/**
	 * javascriptのアラートダイアログコンストラクタです。
	 * 引数のtitle, iconは無視されます
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	initialize: function (title, message, icon, onClick) {
		/* 引数を内部メンバとして設定 */
		this.base.apply(this, arguments);
	},

	/**
	 * javascriptのアラートダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
	
		// ダイアログボックス表示
		alert(this.message);
	
		// 関数の実行
		this.onClick();

	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class JavaScriptの確認ダイアログを表示するクラスです。
 *
 * @name maskat.ui.JavaScriptConfirmDialog
 * @extends maskat.ui.ConfirmDialog
 */
maskat.lang.Class.declare("maskat.ui.JavaScriptConfirmDialog")
	.extend("maskat.ui.ConfirmDialog", {

	/** @scope maskat.ui.JavaScriptConfirmDialog.prototype */

	/**
	 * javascriptの確認ダイアログコンストラクタです。
	 * 引数のtitle, iconは無視されます
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	initialize: function (title, message, icon, onClick) {
		/* 引数を内部メンバとして設定 */
		this.base.apply(this, arguments);
	},

	/**
	 * javascriptの確認ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
	
		/* ダイアログボックス表示 */
		var returnValue = confirm(this.message);
		
		/* 関数の実行 */
		this.onClick (returnValue ? maskat.ui.Dialog.OK : maskat.ui.Dialog.CANCEL);
		
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class JavaScriptのダイアログ表示クラスのインスタンスを生成するためのクラスです。
 *
 * @name maskat.ui.JavaScriptDialogFactory
 * @extends maskat.ui.DialogFactory
 */
maskat.lang.Class.declare("maskat.ui.JavaScriptDialogFactory")
	.extend("maskat.ui.DialogFactory", {
	
	/** @scope maskat.ui.JavaScriptDialogFactory.prototype */

	/**
	 * 確認ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createConfirm: function (title, message, icon, onClick) {
		return new maskat.ui.JavaScriptConfirmDialog (title, message, icon, onClick);
	},
	
	/**
	 * アラートダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createAlert: function (title, message, icon, onClick) {
		return new maskat.ui.JavaScriptAlertDialog (title, message, icon, onClick);
	},
	
	/**
	 * 入力ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	createInput: function (title, message, icon, onClick) {
		return new maskat.ui.JavaScriptInputDialog (title, message, icon, onClick);
	},
	
	/**
	 * 進捗表示ダイアログを表示するためのクラスを生成します。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param goalValue 終了進捗値(オプション)
	 */
	createProgress: function (title, message, goalValue) {
		return new maskat.ui.JavaScriptProgressDialog (title, message, goalValue);
	}
	
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class JavaScriptの入力ダイアログを表示するクラスです。
 *
 * @name maskat.ui.JavaScriptInputDialog
 * @extends maskat.ui.InputDialog
 */
maskat.lang.Class.declare("maskat.ui.JavaScriptInputDialog")
	.extend("maskat.ui.InputDialog", {

	/** @scope maskat.ui.JavaScriptInputDialog.prototype */

	/**
	 * javascriptの入力ダイアログコンストラクタです。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param icon アイコン種別
	 * @param onClick ボタン押下時の呼び出し先関数名
	 */
	initialize: function (title, message, icon, onClick) {
		/* 引数を内部メンバとして設定 */
		this.base.apply(this, arguments);
	},

	/**
	 * javascriptの入力ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
	
		/* ダイアログボックス表示と関数の実行 <CANCEL時はnull> */
		this.onClick (prompt (this.message,"") );

	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class JavaScriptの進捗ダイアログを表示するクラスです。
 *
 * @name maskat.ui.JavaScriptProgressDialog
 * @extends maskat.ui.ProgressDialog
 */
maskat.lang.Class.declare("maskat.ui.JavaScriptProgressDialog")
	.extend("maskat.ui.ProgressDialog", {

	/** @scope maskat.ui.JavaScriptProgressDialog.prototype */

	/**
	 * javascriptの進捗ダイアログコンストラクタです。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param total 終了進捗値(オプション、正数) 、または、
	 *              doneを実行するまで終了しない処理中表示(POSITIVE_INFINITY)
	 */
	initialize: function(title, message, total) {
	
		/* 引数を内部メンバとして設定 */
		this.base.apply(this, arguments);
		
		/* 表示タイプの判定 */
		if (this.total == Number.POSITIVE_INFINITY) {
			this.endlessMode = true;
		} else {
			this.endlessMode = false;
		}
		
		/* totalのデフォルト設定 */
		if (!this.total) {
			this.total = 100;
		}
		
		/* 改行文字列の置換 */
		this.setMessage(this.message);
		
		/* ウインドウサイズを取得 (最小サイズ幅:250 高:50)  */
		this.getOffset(250, 50);
		
		/* カレントの進捗値 */
		this.count = 0;
	},

	/**
	 * javascriptの進捗ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
	
		/* ダイアログボックス表示 */
		if (!this.popup || this.popup.closed) {
			var windowParameter = "width=" + this.width;
			windowParameter += ",height=" + this.height;
			windowParameter += ",resizable=no,scrollbars=no,directories=no,location=no,menubar=no,status=no,toolbar=no";
			this.popup = window.open("", this.title, windowParameter); 
		} else {
		    /*  既にウィンドウが開いている場合はフォーカスを当てる */
		    this.popup.focus();
		    return;
		}
		
		var doc = this.popup.document;

		/* Title, Bodyの設定 */
		doc.open();
		doc.write("<head><title>" + this.title + "</title></head>");
		doc.close();
		doc.bgColor = "#DCDCDC"
		
		/* メッセージの表示 */
		this.divMsg = doc.createElement ("div");
		this.divMsg.id = "processingMessage";
		this.setMessage(this.message);
		doc.body.appendChild(this.divMsg);
		
		/* 進捗バーの位置・サイズ算出 */
		this.barWidth = this.width * 0.9;
		var barHeight = 16; 
		var barTop = this.height - (barHeight + 20);
		var barLeft = this.width * 0.02;
		
		/* 進捗バー表示枠の設定(共通) */
		var progressFrame = doc.createElement("div");
		progressFrame.id = "progressFrame";
		progressFrame.style.top = barTop;
		progressFrame.style.position = "absolute";
		
		if (this.endlessMode) {
			/* 進捗バー表示枠の設定 */
			progressFrame.style.left = (this.width - this.barWidth) / 2;

			/* 進捗バーの設定 */
			var imgTag = doc.createElement("img");
			imgTag.src = maskat.location + "core/images/endless_progress.gif";
			imgTag.width = this.barWidth;
			imgTag.height = 14;
			
			/* 進捗バーの追加 */
			progressFrame.appendChild(imgTag);
			doc.body.appendChild(progressFrame);
		} else {
			/* 進捗率の設定 */
			var rateMargin = 35;
			var rateText = doc.createElement("div");
			rateText.id = "rate";
			rateText.style.position = "absolute";
			rateText.style.top = barTop;
			rateText.style.left = barLeft;
			rateText.innerHTML = "0%";
			
			/* 進捗率表示領域の確保 */
			this.barWidth = this.barWidth - rateMargin;
			
			/* 進捗バー表示枠の設定 */
			progressFrame.style.left = barLeft + rateMargin;
			progressFrame.style.width = this.barWidth;
			progressFrame.style.height = barHeight;
			progressFrame.style.backgroundColor = "white"; 
			progressFrame.style.borderColor = "black";
			progressFrame.style.borderStyle = "solid";
			progressFrame.style.borderWidth = "1px"; 
			progressFrame.className = "progressFrame";
			
			/* 進捗バーの設定 */
			var progressBar = doc.createElement("div");
			progressBar.id = "progressBar";
			progressBar.style.top = barTop;
			progressBar.style.left = barLeft + rateMargin;
			progressBar.style.width = 0;
			progressBar.style.height = barHeight;
			var barColor = "#003399";
			progressBar.style.backgroundColor = barColor;
			progressBar.style.borderColor = barColor;
			progressBar.style.borderStyle = "solid";
			progressBar.style.borderWidth = "1px"; 
			progressBar.style.position = "absolute";
			progressBar.className = "progressBar";
			
			/* 進捗率、進捗バーの追加 */
			doc.body.appendChild(rateText);
			doc.body.appendChild(progressFrame);
			doc.body.appendChild(progressBar);
		}
	},
	
	/**
	 * javascriptの進捗状況ウィンドウの進捗状況を更新します。
	 *
	 * @param count 進捗総数（正数） 
	 */
	setProgress: function(count) {
		
		/* 値の補正 */
		if (count < 0) {
			this.count = 0;
		} else if (count > this.total) {
			this.count = this.total;
		} else {
			this.count = count;
		}
		
		/* 表示の更新 */
		this.updateProgress();
		
		/* 表示の終了 */
		if (this.count >= this.total) {
		   this.close();
		}
		
	},
	
	/**
	 * javascriptの進捗状況ウィンドウの進捗状況を加算更新します。
	 *
	 * @param addValue 進捗数（正数）
	 */
	worked: function(addValue) {
	    this.setProgress(Number(addValue) + Number(this.count));
	},
	
	/**
	 * 進捗状況ウィンドウを閉じるための前処理を実行します。
	 *
	 */
	done: function() {
		if (this.endlessMode) {
			this.close();
		} else {
			this.setProgress(this.total);
		}
	},
	
	
	/**
	 * 進捗状況ウィンドウを閉じます。
	 *
	 */
	 close: function() {
		/* 更新対象の存在チェック */
		if (!this.popup || this.popup.closed) {
		    return;
		}
	    
		/* 進捗状況ウィンドウの破棄 */
		this.popup.window.close();
		this.popup = null;
		this.count = 0;
	 },
	
	
	/**
	 * 進捗状況ウィンドウの進捗状況を更新します。
	 *
	 */
	updateProgress: function() {
	
		/* 進捗率表示無し */
		if (this.endlessMode) {
			return;
		}
		
		/* 更新対象の存在チェック */
		if (!this.popup || this.popup.closed) {
		    return;
		}
		
		/* 進捗率の計算 */
		var rate = this.count * 100 / this.total;
		this.popup.document.getElementById("rate").innerHTML 
					= Math.round(rate) + "%"; 
		
		/* 進捗バーの更新 */
		this.popup.document.getElementById("progressBar").style.width 
					= (this.barWidth * rate) / 100;
	    this.popup.focus();
	},
	
	/**
	 * メッセージを表示するdivを作成し、ダイアログサイズを算出します
	 *
	 * @param minWidth 最小幅
	 * @param minHeight 最小高さ
	 */
	getOffset: function(minWidth, minHeight) {
		var div = document.createElement("div");
		div.innerHTML = this.message;
		div.style.position = "absolute";
		div.style.visibility = "hidden";
		div.style.whiteSpace = "nowrap";
		div.id = "processingMessage";
		
		document.body.appendChild(div);
		
		this.width = div.offsetWidth < minWidth ? minWidth
		                 : div.offsetWidth;
		this.width *= 1.05;
		this.height = div.offsetHeight < minHeight ? minHeight
		                 : div.offsetHeight;
		this.height += 50;
		
		document.body.removeChild(div);
		div = null;
	},

	/**
	 * 表示中のメッセージを置き換えます
	 * 制限として、メッセージの長さによるウィンドウのリサイズは行いません
	 *
	 * @param message 置き換え後のメッセージ文字列
	 */
	setMessage: function(message) {
		
		this.message = message.replace(/\r?\n/g, "<br>"); 
		
		/* ウィンドウが存在し、閉じれれていない場合 */
		if (this.popup && !this.popup.closed) {
			this.divMsg.innerHTML = this.message;
		    /*  フォーカスを当てる */
	    	this.popup.focus();
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class 進捗ダイアログを表示するクラスです。
 *
 * @name maskat.ui.ProgressDialog
 */
maskat.lang.Class.declare("maskat.ui.ProgressDialog", {

	/** @scope maskat.ui.ProgressDialog.prototype */

	/**
	 * javascriptの進捗ダイアログコンストラクタです。
	 *
	 * @param title ダイアログのタイトル文字列
	 * @param message ダイアログメッセージ文字列
	 * @param total 終了進捗値(オプション)
	 */
	initialize: function (title, message, total) {
		this.title = title;
		this.message = message;
		this.total = total;
	},

	/**
	 * 進捗ダイアログをPOPUPして表示します。
	 *
	 */
	open: function () {
		/* NOP */
	},
	/**
	 * javascriptの進捗状況ウィンドウの進捗状況を更新します。
	 *
	 * @param count 進捗総数
	 */
	setProgress:function (count) {
		/* NOP */
	},
	/**
	 * javascriptの進捗状況ウィンドウの進捗状況を加算更新します。
	 *
	 * @param count 進捗数
	 */
	worked:function (count) {
		/* NOP */
	},
	/**
	 * 進捗ダイアログを閉じます。
	 *
	 */
	done: function () {
		/* NOP */
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * JavaScript におけるデータ型の変換を行うコンバータです。
 *
 * <p>
 * このクラスは static メソッドのみを持つユーティリティクラスであり、
 * インスタンス化する必要はありません。
 * </p>
 *
 * <h2>使用方法</h2>
 * <p>
 * コンバータの最も一般的な利用方法は文字列を他のデータ型に変換する
 * ことです。コンバータは {@link maskat.util.Converter.convert} 関数
 * を経由して実行します。使用例を以下に示します:
 * </p>
 *
 * <pre>
 * var input = prompt("How old are you ?", "");
 * // 入力された文字列を数値に変換 
 * var age = maskat.util.Converter.convert("number", input);
 * </pre>
 *
 * <p>
 * 変換先の型によっては、文字列以外の値を入力値として受け取ることが
 * 可能です。デフォルトで利用可能な変換先の型は以下のとおりです。
 * </p>
 * <ul>
 *   <li>boolean : 真偽値</li>
 *   <li>number : 数値 (整数または浮動小数)</li>
 *   <li>string : 文字列</li>
 *   <li>enum: 列挙値</li>
 *   <li>json: JSON 形式の値をパースしたオブジェクト</li>
 *   <li>object: オブジェクト</li>
 *   <li>function: 関数オブジェクト</li>
 *   <li>RegExp: 正規表現オブジェクト</li>
 *   <li>HTMLElement: HTML 要素</li>
 * </ul>
 *
 * <h2>データ型の追加</h2>
 * <p>
 * {@link maskat.util.Converter.converters} オブジェクトにコンバータ
 * 関数を追加することにより、変換可能なデータ型の種類を追加できます。
 * コンバータ関数は以下のシグネチャに従います:
 * </p>
 *
 * <dl>
 *   <dt>書式<dt>
 *   <dd><code>function converter(value, options)</code><dd>
 *   <dt>引数</dt>
 *   <dd>
 *     <ul>
 *       <li><code>value</code> - 変換元の値</li>
 *       <li><code>options</code> - 変換オプション</li>
 *     </ul>
 *   </dd>
 *   <dt>返却値<dt>
 *   <dd>変換された値</dd>
 * </dl>
 *
 * @name maskat.util.Converter
 */
maskat.lang.Class.declare("maskat.util.Converter", {

	_static: {
		/** @scope maskat.util.Converter */
	
		/** コンバータ関数を登録するレジストリ */
		converters: {
			"boolean": function(value){
				switch (typeof(value)) {
				case "boolean":
					return value;
				case "string":
					value = maskat.lang.String.trim(value).toLowerCase();
					if (value == "true") {
						return true;
					} else if (value == "false") {
						return false;
					}
				default:
					return undefined;
				} 
			},
			
			"number": function(value){
				switch (typeof(value)) {
				case "number":
					return value;
				case "string":
					if (isNaN(value)) {
						return undefined;
					}
					if (value.indexOf(".") == -1) {
						return parseInt(value, 10);
					} else {
						return parseFloat(value);
					}
				default:
					return undefined;
				} 
			},
			
			"string": function(value){
				if (typeof(value) == "string") {
					return value;
				} else {
					return value.toString();
				}
			},
			
			"enum": function(value, options){
				if (typeof(value) == "string" && options.values) {
					if (options.values instanceof Array) {
						for (var i = 0; i < options.values.length; i++) {
							if (value == options.values[i]) {
								return value;
							}
						}
					} else {
						for (var key in options.values) {
							if (value == key) {
								return options.values[key];
							}
						}
					}
				}
				return undefined;
			},
			
			"json": function(value){
				switch (typeof(value)) {
				case "boolean":
				case "number":
				case "object":
					return value;
				case "string":
					return maskat.lang.Object.parseJSON(value);
				default:
					return undefined;
				} 
			},
			
			"object": function(value){
				switch (typeof(value)) {
				case "object":
					return value;
				case "string":
					return maskat.lang.Object.find(value);
				default:
					return undefined;
				} 
			},
			
			"function": function(value){
				switch (typeof(value)) { 
				case "function":
					return value;
				case "string":
					var func = maskat.lang.Object.find(value);
					if (func && typeof(func) == "function") {
						func.functionName = value;
						return func;
					}
					throw new maskat.lang.Error("MISSING_FUNCTION",
						{ functionName: value });
				default:
					return undefined;
				}
			},

			"RegExp": function(value){
				switch (typeof(value)) {
				case "object":
				case "function":
					return (value instanceof RegExp) ? value : undefined;
				case "string":
					return new RegExp(value);
				default:
					return undefined;
				} 
			},

			"HTMLElement": function(value){
				switch (typeof(value)) {
				case "string":
					return document.getElementById(value);
				default:
					return undefined;
				} 
			}
		},
		
		/**
		 * 指定された値 (オブジェクト) を指定した型に変換します。
		 *
		 * @param type 変換先の型
		 * @param value 変換元の値
		 * @param options 変換オプション
		 * @throws {maskat.lang.Error} データ型の変換中にエラーが発生した場合
		 *
		 * @returns 変換された値, 変換に失敗した場合は undefined
		 */
		convert: function(type, value, options){
			if (!this.converters[type]) {
				throw new maskat.lang.Error("UNKNOWN_CONVERTER_TYPE",
					{ type: type });
			}

			var result = this.converters[type](value, options);
			if (result === undefined) {
				if (value instanceof Object && !(window.Node && value instanceof Node)) {
					value = maskat.lang.Object.encodeJSON(value);
				} else if (value == undefined) {
					value = "undefined";
				}
				throw new maskat.lang.Error("CONVERSION_ERROR",
					{ type: type, value: value });
			}
			return result;
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * Web ブラウザの実装に依存しやすい DOM や XMLHttpRequest に関連する処理を
 * カプセル化し、コードの移植性を高めます。
 *
 * <p>
 * このクラスは static メソッドのみを持つユーティリティクラスであり、
 * インスタンス化する必要はありません。
 * </p>
 *
 * @name maskat.util.CrossBrowser
 */
maskat.lang.Class.declare("maskat.util.CrossBrowser", {

	_static: {
		
		/** @scope maskat.util.CrossBrowser */
	
		/**
		 * 新しい XMLHttpRequest を生成します。
		 *
		 * @returns XMLHttpRequest のインスタンス
		 */
		createXMLHttpRequest: function(){
			var xhr;
			
			if (window.XMLHttpRequest &&
				(navigator.appName != "Microsoft Internet Explorer" ||
				 window.location.protocol != "file:")) {
				
				xhr = new XMLHttpRequest();
			} else if (window.ActiveXObject) {
				try {
					xhr = new ActiveXObject("Msxml2.XMLHTTP");
				} catch (e) {
					try {
						xhr = new ActiveXObject("Microsoft.XMLHTTP");
					} catch (e) { /* suppress */ }
				}
			}

			if (!xhr) {
				throw new maskat.lang.Error("CREATE_XHR_ERROR",
					{ agent: navigator.userAgent });
			}
			return xhr;
		},

		/**
		 * XMLHttpRequest に通信エラーが発生しているかどうかを確認し、通信
		 * エラーの場合には例外をスローします。
		 *
		 * @throws {maskat.lang.Error} 通信エラーが発生している場合
		 */
		checkHttpError: function(xhr, param) {
			var err = this.getHttpError(xhr, param);
			if (err) {
				throw err;
			}
		},
		
		/**
		 * XMLHttpRequest に通信エラーが発生しているかどうかを確認し、通信
		 * エラーの場合には例外を返します。
		 *
		 * @return {maskat.lang.Error} 通信エラーが発生している場合
		 */
		getHttpError: function(xhr, param) {
			try {
				if (xhr.readyState != 4 || xhr.status != 200) {
					switch (xhr.status) {
					case 0:
						return undefined;
					case 403:
					case 404:
					case 500:
						var text = xhr.statusText;
						if (text == "Not Found") {
							text = xhr.responseText;
							if (param && param.url) {
								text = param.url + text;
							}
						}
						return new maskat.lang.Error("HTTP_" + xhr.status,
							{ url: text });
					default:
						/*
						 * IE独自のエラー(12002-12152)が発生した場合、
						 * statusTextには "Unknown"が格納される。
						 */
						var url = xhr.statusText != "Unknown" ? xhr.statusText : "";
						return new maskat.lang.Error("HTTP_DEFAULT",
							{ status: xhr.status, url: url });
					}
				}
			} catch (e) {
				/*
				 * Firefox ではサーバサイドがダウンしている状態で send した
				 * XHRの status, statueTextにアクセスするとエラーが発生する。
				 * そのための catch 処理
				 */
				return new maskat.lang.Error("HTTP_ERROR");
			}
		},

		/**
		 * HTTP GET メソッドを用いた同期通信で XML 文書を取得し、Document
		 * ノードを返します。
		 *
		 * @returns 取得した XML 文書の Document ノード
		 */
		getXMLDocumentFrom: function(url){
			var xhr = this.createXMLHttpRequest();
			
			try {
				xhr.open("GET", url, false);
				xhr.send(null);
				
			} catch (e) {
				throw new maskat.lang.Error("HTTP_404",
					{ url: url });
			}
			if (xhr.readyState == 4) {
				switch (xhr.status) {
				case 200:
					this.checkParseError(xhr.responseXML);
					if (xhr.responseXML && xhr.responseXML.nodeType == 9) {
						return xhr.responseXML;
					}
					break;
				case 0:
					/* ローカル環境の場合 responseText から DOM を生成 */
					if (document.location.protocol == "file:" && xhr.responseText) {
						var doc = this.parseXMLDocument(xhr.responseText);
						this.checkParseError(doc);
						return doc;
					}
					break;
				default:
					this.checkHttpError(xhr);
				}
			}
			return null;
		},

		/**
		 * HTTP GET メソッドを用いた同期通信でテキストを取得します。
		 *
		 * @returns 取得したテキスト
		 */
		getTextFrom: function(url){
			var xhr = this.createXMLHttpRequest();
			try {
				xhr.open("GET", url, false);
				xhr.send(null);
			} catch (e) {
				throw new maskat.lang.Error("HTTP_404",
					{ url: url });
			}
			this.checkHttpError(xhr);
			return xhr.responseText;
		},

		/**
		 * XML 文書の文字列表現から DOM オブジェクトを構築し、Document
		 * ノードを返します。
		 *
		 * @returns XML 文書の Document ノード
		 */
		parseXMLDocument: function(source){
			var doc;
			if (window.DOMParser) {
				doc = (new DOMParser()).parseFromString(source, "text/xml");
			} else if (window.ActiveXObject) {
				doc = new ActiveXObject("MSXML2.DOMDocument");
				doc.async = false;
				doc.resolveExternals = false;
				doc.validateOnParse = false;
				doc.loadXML(source);
			}
			this.checkParseError(doc);
			return doc;
		},
		
		/**
		 * XML 文書に解析エラーが発生しているかどうかを確認し、解析エラーの
		 * 場合には例外をスローします。
		 *
		 * @throws {maskat.lang.Error} 解析エラーが発生している場合
		 */
		checkParseError: function(xml) {
			if (!xml) {
				return;
			}
			if (xml.parseError && xml.parseError.errorCode < 0) {
				/* IE */
				var p = xml.parseError;
				var param = {
					msg: p.reason,
					line: p.line,
					pos: p.linepos,
					text: p.srcText
				};
				throw new maskat.lang.Error("PARSEERROR_IE", param);
			} else if (xml.documentElement &&
					   xml.documentElement.tagName == "parsererror") {
				/* Firefox */
				throw new maskat.lang.Error("PARSEERROR_DEFAULT",
					{ msg: xml.documentElement.textContent });
			}
		},

		/**
		 * HTTP GET メソッドを用いた同期通信で JSON 形式のリテラル文字列を
		 * 取得し、それを評価したオブジェクトを返します。
		 *
		 * @returns オブジェクト
		 */
		loadJSONFrom: function(url){
			return maskat.lang.Object.parseJSON(this.getTextFrom(url));
		},

		/**
		 * HTML 要素にイベントリスナを追加します。
		 *
		 * <p>
		 * ブラウザ間の互換性を確保するため、このメソッドによって追加された
		 * イベントリスナはバブリング段階で実行されます。この制約が発生する
		 * 理由を以下で説明します。
		 * </p>
		 *
		 * <ul>
		 *   <li>DOM Level 2 仕様ではイベントリスナを活性化するタイミングを
		 *   キャプチャ段階またはバブリング段階から選択できます。Internet
		 *   Explorer を除くブラウザの多くはこの仕様を実装しています。</li>
		 *   <li>Internet Explorer は独自の attachEvent 関数でイベントリスナ
		 *   を登録します。この関数ではバブリング段階で活性化するリスナのみ
		 *   サポートされます。</li>
		 * </ul>
		 *
		 * @param node リスナを追加する HTML 要素
		 * @param eventType イベントタイプ
		 *        ("click", "focus", "blur" など、先頭に "on" を含まない文字列)
		 * @param listener イベントリスナとして追加される関数
		 *
		 * @returns XML 文書の Document ノード
		 * @see <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow">DOM Level 2 Events</a>
		 */
		addEventListener: function(node, eventType, listener){
			if (node.addEventListener) {
				node.addEventListener(eventType, listener, false);
			} else if (node.attachEvent) {
				node.attachEvent("on" + eventType, listener);
			} else if (node["on" + eventType]) {
				node["on" + eventType] = listener;
			}
		}
	}
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * メッセージテンプレートと置換パラメータを合成し、メッセージ文字列を動的に
 * 生成する機能を持つユーティリティクラスです。
 *
 * <p>
 * このクラスは static メソッドのみを持つユーティリティクラスであり、
 * インスタンス化する必要はありません。
 * </p>
 *
 * @name maskat.util.Message
 */
maskat.lang.Class.declare("maskat.util.Message", {

	_static: {
		/** @scope maskat.util.Message */	
		
		/**
		 * メッセージテンプレートを格納するハッシュ
		 *
		 * <ul>
		 *   <li>プロパティ名: メッセージキー</li>
		 *   <li>プロパティ値: 置換パラメータを含むテンプレート文字列</li>
		 * </ul>
		 */
		templates: {},

		/** 
		 * 指定された URL から JSON 形式で記述されたメッセージテンプレートの
		 * リストを読み込みます。
		 *
		 * <p>
		 * メッセージテンプレートのリストの定義例を以下に示します:
		 * </p>
		 * <pre>
		 * {
		 *     "メッセージキー1": "テンプレート文字列1",
		 *     "メッセージキー2": "テンプレート文字列2",
		 *     ...
		 * }
		 * </pre>
		 *
		 * <p>
		 * 読み込まれたリストは既存のメッセージテンプレートに追加されます。
		 * 既存のメッセージテンプレートとキーが重複する場合には、読み込まれた
		 * 値で上書きされます。
		 * </p>
		 *
		 * @param url メッセージテンプレートを取得する URL
		 */
		loadTemplates: function(url) {
			maskat.lang.Object.populate(this.templates,
				maskat.util.CrossBrowser.loadJSONFrom(url)); 
		},

		/** 
		 * メッセージを指定されたパラメータでフォーマットして返します。
		 *
		 * @param key メッセージキー
		 * @param values 置換パラメータの値を含んだオブジェクト
		 * @returns フォーマットされたメッセージ文字列
		 */
		format: function(key, values) {
			return this.merge(this.templates[key], values);
		},
		
		/** 
		 * メッセージを指定されたパラメータでフォーマットして返します。
		 *
		 * @param template メッセージテンプレート
		 * @param values 置換パラメータの値を含んだオブジェクト
		 * @returns フォーマットされたメッセージ文字列
		 */
		merge: function(template, values) {
			if (template && values) {
				replacer = function(str, p1, offset, s){ return values[p1]; };
				template = template.replace(/#{(.*?)}/g, replacer);
			}
			return template;
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * キーと値が対になったプロパティを取得・格納する機能を持ったクラスです。
 * プロパティにはキーごとに型とデフォルト値を定義できます。
 *
 * <h2>プロパティの定義</h2>
 * <p>
 * コンストラクタ引数や defineProperty メソッドでプロパティを定義します。
 * 未定義のプロパティについても値の格納や取得はできますが、その場合には
 * 型変換やデフォルト値の機能が利用できません。
 * </p>
 * <pre>
 * var prop = new maskat.util.Properties({
 *     itemCode: { type: "string" },
 *     name: { type: "string" },
 *     size: { type: "enum", values: [ "large", "medium", "small" ] },
 *     price: { type: "number" },
 *     discontinued: { type: "boolean", defaultValue: false }
 * });
 * </pre>
 *
 * <h2>プロパティ値の格納</h2>
 * <p>
 * データ型が指定されたプロパティでは、値の格納時に型の変換が行われます。
 * 未定義の場合は指定された値をそのまま格納します。
 * </p>
 * <pre>
 * prop.setProperty("price", "500"); // 数値の 500 に変換される
 * </pre>
 *
 * <h2>プロパティ値の取得</h2>
 * <p>
 * プロパティに値が格納されていない場合、デフォルト値が指定されていれば
 * その値を返却します。未定義の場合は undefined を返します。
 * </p>
 * <pre>
 * var discontinued = prop.getProperty("discontinued"); // false
 * if (discontinued) {
 *     ...
 * }
 * </pre>
 *
 * @name maskat.util.Properties
 */
maskat.lang.Class.declare("maskat.util.Properties", {

	/** @scope maskat.util.Properties.prototype */

	/**
	 * コンストラクタです。
	 *
	 * @param descriptors プロパティ記述子
	 * <pre>
	 * {
	 *     プロパティキー1: { 
	 *         type: プロパティの型を表す文字列
	 *         defaultValue: デフォルト値
	 *     },
	 *
	 *     プロパティキー2: { ... },
	 *     プロパティキー3: { ... },
	 *     ...
	 * }
	 * </pre>
	 */
	initialize: function(descriptors) {
		this.descriptors = {};
		this.values = {};

		for (var key in descriptors) {
			var desc = descriptors[key];
			this.defineProperty(key, desc.type, desc.defaultValue, desc);
		}
	},

	/**
	 * 新しいプロパティキーを宣言します。
	 *
	 * @param key プロパティキー
	 * @param type プロパティの型を表す文字列
	 *        {@link maskat.util.Converter} がサポートする型を指定します。
	 * @param defaultValue デフォルト値
	 * @param options 変換オプション
	 */
	defineProperty: function(key, type, defaultValue, options) {
		/* デフォルト値が指定された場合、型を変換する */		
		if (typeof(defaultValue) != "undefined") {
			defaultValue = maskat.util.Converter.convert(type, defaultValue, options);
		}

		this.descriptors[key] = {
			type: type,
			defaultValue: defaultValue,
			options: options
		};

		/* プロパティの値がすでに格納されている場合は型を変換 */		
		if (typeof(this.values[key]) != "undefined") {
			this.setProperty(key, this.values[key]);
		}
	},

	/**
	 * 指定したキーに対応するプロパティ値を格納します。
	 *
	 * @param key プロパティキー
	 * @param value プロパティの値
	 */
	setProperty: function(key, value) {
		var desc = this.descriptors[key];
		if (desc) {
			value = maskat.util.Converter.convert(desc.type, value, desc.options);
		}
		this.values[key] = value;
	},

	/**
	 * プロパティキーと値が対になったオブジェクトから、プロパティ値を
	 * すべて読み込みます。
	 *
	 * @param values プロパティキーと値が対になったオブジェクト 
	 */
	setProperties: function(values) {
		for (var key in values) {
			this.setProperty(key, values[key]);
		}
	},

	/**
	 * 指定された URL から HTTP GET メソッドで JSON 形式のリテラル文字列を
	 * 取得し、そこからプロパティキーと値を読み込みます。
	 *
	 * @param url JSON ファイルを取得する URL
	 */
	load: function(url) {
		this.setProperties(maskat.util.CrossBrowser.loadJSONFrom(url));
	},

	/**
	 * 指定したキーに対応するプロパティ値を返します。
	 *
 	 * @returns 指定したキーに対応するプロパティ値 
	 */
	getProperty: function(key) {
		var value = this.values[key];
		if (typeof(value) == "undefined" && this.descriptors[key]) {
			value = this.descriptors[key].defaultValue;
		}
		return value;
	},

	/**
	 * すべてのプロパティキーと値が対になったオブジェクトを返します。
	 *
	 * @returns プロパティキーと値が対になったオブジェクト 
	 */
	getProperties: function() {
		var result = {};
		var key;

		/* デフォルト値を result オブジェクトに格納 */
		for (key in this.descriptors) {
			result[key] = this.descriptors[key].defaultValue;
		} 

		/* プロパティ値を result オブジェクトに格納 */
		for (key in this.values) {
			result[key] = this.values[key];
		} 

		return result;
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 属性とオブジェクトのプロパティをバインドします。
 * 
 * @name maskat.xml.AttributeBinding
 */
maskat.lang.Class.declare("maskat.xml.AttributeBinding", {

	/** @scope maskat.xml.AttributeBinding.prototype */

	/**
	 * コンストラクタです。
	 * 
	 * @param name
	 *            XML 属性のローカル名
	 * @param config
	 *            バインディング設定
	 * <pre>
	 * {
	 *     type:
	 *         属性のデータ型を表す文字列 (&quot;string&quot;, &quot;number&quot;, &quot;boolean&quot; など)
	 *         指定可能なデータ型は {@link maskat.util.Converter} を参照してください。
	 * 
	 *     required:
	 *         必須属性の場合 true、省略可能の場合は false
	 * 
	 *     defaultValue:
	 *         属性値が省略された場合のデフォルト値
	 * 
	 *     property:
	 *         属性名と対応するオブジェクトのプロパティ名
	 * 
	 *     value:
	 *         属性値のリテラル
	 *         読み込み時は常に一定の属性値が記述されていることを要求し、
	 *         書き込み時は固定値で属性値を書き出します。
	 * }
	 * </pre>
	 */
	initialize: function(name, config) {
		this.name = name;
		this.type = config.type || "string";
		this.required = config.required || false;
		this.property = config.property || name;
		this.value = config.value;
		this.options = config;

		if (typeof (config.defaultValue) != "undefined") {
			this.defaultValue = maskat.util.Converter.convert(this.type,
					config.defaultValue, this.options);
		}
	},

	/**
	 * オブジェクトのプロパティに XML 属性の値を読み込みます。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param attribute
	 *            XML 属性ノード
	 */
	read: function(context, attribute) {
		var value = this.value || (attribute && attribute.nodeValue);
		if (value || value === "") {
			var replacer = function(str, p1, offset, s) {
				return maskat.util.Message.format(p1);
			};
			value = value.replace(/#{(.*?)}/g, replacer);
			value = maskat.util.Converter.convert(this.type, value,
					this.options);
			context.setProperty(this.property, value);
		} else if (this.required) {
			throw new maskat.lang.Error("MISSING_ATTRIBUTE", {
				attributeName :this.name
			});
		} else if (typeof (this.defaultValue) != "undefined") {
			context.setProperty(this.property, this.defaultValue);
		}
	},

	/**
	 * オブジェクトのプロパティ値を XML 属性の形式で書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		var value = this.value || context.getProperty(this.property);
		if (typeof (value) != "undefined") {
			if (this.defaultValue !== value) {
				if (typeof (value) == "function") {
					value = value.toString().match(/function (\w+)/);
					value = value ? value[1] : "[function]";
				}
				value = maskat.lang.String.escapeXML(value);
				context.writeAttribute(this.name, value);
			}
		} else if (this.required) {
			throw new maskat.lang.Error("MISSING_ATTRIBUTE", {
				attributeName :this.name
			});
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 文書の読み込み・書き出し処理の中間状態 (コンテキスト) を保持します。
 * {@link maskat.xml.XMLObjectBinder} は XML 文書の処理要求ごとに新しい
 * コンテキストを生成して使用します。
 *
 * @see maskat.xml.XMLObjectBinder#createContext
 * @name maskat.xml.BindingContext
 */ 
maskat.lang.Class.declare("maskat.xml.BindingContext", {

	/** @scope maskat.xml.BindingContext.prototype */

	/**
	 * コンストラクタです。
	 * 
	 * @param binder
	 *            このコンテキストを使用する XMLObjectBinder
	 */
	initialize: function(binder) {
		this.binder = binder;
		this.object = null;
		this.stack = [];
		this.emptyElement = false;
		this.nodeNames = [];
		this.buffer = [];
		this.variables = null;
	},

	/**
	 * 指定した名前空間 URI に対応する接頭辞を返します。
	 * 
	 * @param uri
	 *            名前空間 URI
	 * @returns 名前空間 URI に対応する接頭辞
	 */
	getPrefix: function(uri) {
		return this.binder.getPrefix(uri);
	},

	/**
	 * 名前空間 URI と接頭辞のマッピングを追加します。
	 * 
	 * @param prefix
	 *            接頭辞
	 * @param uri
	 *            名前空間 URI
	 */
	addPrefixMapping : function(prefix, uri) {
		this.binder.addPrefixMapping(prefix, uri);
	},

	getElementBinding: function(uri, name) {
		if (typeof(uri) == "object") {
			var qname = uri;
			uri = qname.namespaceURI;
			name = qname.localName || qname.baseName || qname.nodeName;
		}

		uri = uri || "";
		var bindings = this.binder.bindings;
		if (!bindings[uri]) {
			throw new maskat.lang.Error("UNKNOWN_NS_URI", { uri: uri });
		}
		if (!bindings[uri][name]) {
			throw new maskat.lang.Error("UNKNOWN_ELEMENT", { elementName: name });
		}
		return bindings[uri][name];
	},
	
	/**
	 * 指定された XML 要素を読み込むための新しいオブジェクトを生成し、
	 * スタックの頂上に追加します。
	 * 
	 * @param element
	 *            XML 書き出しに使用するコンテキスト
	 */
	createObject: function(element) {
		this.pushObject(this.binder.createObject(element));
	},

	/**
	 * スタックの頂上にあるオブジェクトを返します。
	 * 
	 * @returns スタックの頂上にあるオブジェクト 
	 */
	getObject: function() {
		return this.object;
	},
	
	/**
	 * スタックの頂上に指定されたオブジェクトを追加します。
	 * 
	 * @param object
	 *            オブジェクト
	 */
	pushObject: function(object) {
		this.object = object;
		this.stack.push(object);
	},

	/**
	 * スタックの頂上からオブジェクトを削除し、そのオブジェクトを返します。
	 * 
	 * @returns スタックの頂上から削除されたオブジェクト 
	 */
	popObject: function() {
		var result = this.stack.pop(); 
		this.object = this.stack[this.stack.length - 1];
		return result;
	},

	/**
	 * スタックの頂上にあるオブジェクトから指定されたプロパティの値を取得し、
	 * その値を返します。
	 * 
	 * @returns プロパティの値
	 */
	getProperty: function(name) {
		return this.object[name];
	},
	
	/**
	 * スタックの頂上にあるオブジェクトにプロパティを設定します。
	 * 
	 * @param name
	 *            プロパティ名
	 * @param value
	 *            プロパティの値
	 */
	setProperty : function(name, value) {
		this.object[name] = value;
	},

	/**
	 * スタックの頂上にあるオブジェクトのプロパティに値を追加します。
	 * プロパティは配列型である必要があります。
	 * 
	 * @param name
	 *            プロパティ名
	 * @param value
	 *            プロパティの値
	 */
	addProperty: function(name, value) {
		if (!this.object[name]) {
			this.object[name] = [];
		}
		this.object[name].push(value);
	},

	/**
	 * スタックの頂上にあるオブジェクトに対し、複数のプロパティを一括して
	 * 設定します。
	 * 
	 * @param properties
	 *            設定対象のプロパティを格納したオブジェクト
	 */
	addProperties: function(properties) {
		maskat.lang.Object.populate(this.object, properties, true);
	},

	/**
	 * スタックの頂上にあるオブジェクトのプロパティに値を追加します。
	 * プロパティは連想配列型 (オブジェクト) である必要があります。
	 * 
	 * @param name
	 *            プロパティ名
	 * @param key
	 *            連想配列のキー
	 * @param value
	 *            プロパティの値
	 */
	putProperty: function(name, key, value) {
		if (!this.object[name]) {
			this.object[name] = {};
		}
		this.object[name][key] = value;
	},

	/**
	 * スタックの頂上にあるオブジェクトに対し、指定したメソッドを実行します。
	 * 
	 * @param name
	 *            メソッド名
	 * @param argument
	 *            メソッド呼び出し時に与える引数
	 */
	callMethod: function(name, argument) {
		this.object[name](argument);
	},
	
	/**
	 * XML 文書内に文字列を書き出します。
	 * 
	 * @param string
	 *            書き出し対象の文字列
	 */
	write: function(string) {
		this.buffer.push(string);
	},

	/**
	 * XML 文書内に XML 宣言を書き出します。
	 */
	writeXMLDeclaration: function() {
		this.buffer.push("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
	},

	/**
	 * XML 文書内に要素の開始を書き出します。
	 * 
	 * @param name
	 *            要素名
	 */
	writeStartElement: function(uri, name) {
		var prefix = this.getPrefix(uri);
		var nodeName = prefix ? (prefix + ":" + name) : name; 

		/* 開始タグを出力 */
		this.nodeNames.push(nodeName);
		this.buffer.push("<" + nodeName);
		this.buffer.push(">");

		/* 空要素であると仮定してフラグを立てる */
		this.emptyElement = true;
	},

	/**
	 * XML 文書内に要素の終了を書き出します。
	 * 
	 * @param name
	 *            要素名
	 */
	writeEndElement: function() {
		/* 終了タグを出力 */
		var nodeName = this.nodeNames.pop();
		if (this.emptyElement) {
			this.buffer.pop();
			this.buffer.push("/>");
		} else {
			this.buffer.push("</" + nodeName + ">");
		}

		/* 子要素を出力したため、親要素は空要素ではない */
		this.emptyElement = false;
	},

	/**
	 * XML 文書内に属性を書き出します。
	 * 
	 * @param name
	 *            属性名
	 * @param value
	 *            属性値
	 */
	writeAttribute: function(name, value) {
		this.buffer.pop();
		this.buffer.push(" " + name + "=\"" + value + "\"");
		this.buffer.push(">");
	},

	/**
	 * XML 文書内に CDATA セクションを書き出します。
	 * 
	 * @param string
	 *            書き出し対象の文字列
	 */
	writeCDATASection: function(string) {
		this.buffer.push("<![CDATA[" + string + "]]>");

		/* CDATA セクションを出力したため、親要素は空要素ではない */
		this.emptyElement = false;
	},

	/**
	 * XML 文書内にテキストノードを書き出します。
	 * 
	 * @param string
	 *            書き出し対象の文字列
	 */
	writeText: function(string) {
		this.buffer.push(maskat.lang.String.escapeXML(string));

		/* テキストノードを出力したため、親要素は空要素ではない */
		this.emptyElement = false;
	},

	/**
	 * XML 文書を文字列として取得します。
	 * 
	 * @returns XML 文書を表す文字列
	 */
	getXMLString: function() {
		/* ルート要素に名前空間宣言を出力する */
		if (this.binder.namespaces && this.buffer.length > 2) {
			var attributes = [];
			for (var prefix in this.binder.namespaces) {
				attributes.push(" xmlns");
				if (prefix) {
					attributes.push(":");
					attributes.push(prefix);
				}
				attributes.push("=\"");
				attributes.push(this.binder.namespaces[prefix]);
				attributes.push("\"");
			}
			this.buffer.splice(2, 0, attributes.join(""));
		}

		return this.buffer.join("");
	}
	
});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * CDATA セクションと文字列をバインドします。
 *
 * @name maskat.xml.CDATASectionBinding
 */ 
maskat.lang.Class.declare("maskat.xml.CDATASectionBinding", {

	/** @scope maskat.xml.CDATASectionBinding.prototype */ 

	/**
	 * CDATA セクションに含まれる文字列を読み込みます。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param cdata
	 *            CDATA セクションノード
	 */
	read: function(context, cdata) {
		context.pushObject(cdata.nodeValue);
	},

	/**
	 * オブジェクトを CDATA セクションの形式で書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		context.writeCDATASection(context.popObject());
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 要素の親子関係とオブジェクト間の参照関係をバインドします。
 *
 * @name maskat.xml.ChildNodeBinding
 */ 
maskat.lang.Class.declare("maskat.xml.ChildNodeBinding", {

	/** @scope maskat.xml.ChildNodeBinding.prototype */

	/**
	 * 子要素のバインディング設定を定義します。
	 * 
	 * <p>
	 * この XML 要素に対応するオブジェクト (親オブジェクト) は指定された
	 * プロパティを用いて子要素に対応するオブジェクト (子オブジェクト) を
	 * 参照します。
	 * </p>
	 * 
	 * @param name
	 *            XML 要素のローカル名
	 * @param config
	 *            バインディング設定
	 * <pre>
	 * {
	 *     property:
	 *         親オブジェクトから子オブジェクトを参照するプロパティ名
	 *         property を省略した場合、親要素と子要素は同じオブジェクト
	 *         にマッピングされます。
	 * 
	 *     required:
	 *         子要素の存在が必須の場合 true、省略可能の場合は false
	 * 
	 *     repeat:
	 *         子要素が繰り返し出現可能の場合は true
	 *         repeat が指定された場合、親オブジェクトのプロパティは
	 *         子オブジェクトの配列にマッピングされます。
	 * 
	 *     key:
	 *         子要素が繰り返し出現する場合に、子オブジェクトを一意に識別
	 *         するためのインデックスとして使用するプロパティ名
	 *         repeat と組み合わせて指定します。key が指定された場合には
	 *         親オブジェクトのプロパティはハッシュにマッピングされます。
	 * 
	 *     value:
	 *         プロパティの値となる子オブジェクトのプロパティ名
	 *         value が指定されている場合、親オブジェクトのプロパティは
	 *         子オブジェクトのプロパティへの参照となります。
	 * }
	 * </pre>
	 */
	initialize: function(uri, name, config) {
		this.uri = uri;
		this.name = name;

		config = config || {};
		this.property = config.property;
		this.method = config.method;
		this.validate = config.validate !== false;
		this.required = config.required || false;
		this.repeat = config.repeat || false;
		this.key = config.key;
		this.value = config.value;
	},

	/**
	 * 指定された子ノードからオブジェクトを読み込み、それをコンテキスト上の
	 * オブジェクトのプロパティに格納します。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param element
	 *            子ノード
	 */
	read: function(context, element) {
		try {
			var binding = context.getElementBinding(element);
		} catch (e) {
			if (this.validate) {
				throw e;
			} else {
				return;
			}
		}

		binding.read(context, element);
		var value = context.popObject();

		if (this.method) {
			context.callMethod(this.method, value);
		} else if (typeof(this.property) == "undefined") {
			context.addProperties(value);
		} else if (this.repeat) {
			if (this.key) {
				/* プロパティがハッシュ型の場合 */
				var key = value[this.key];
				if (this.value) {
					context.putProperty(this.property, key, value[this.value]);
 				} else {
 					context.putProperty(this.property, key, value);
 				}
			} else {
				/* プロパティが配列型の場合 */
				if (this.value) {
					context.addProperty(this.property, value[this.value]);
				} else {
					context.addProperty(this.property, value);
				}
			}
		} else {
			/* プロパティが単純型の場合 */
			context.setProperty(this.property, value);
		}
	},

	/**
	 * コンテキスト上のオブジェクトからプロパティを取得し、それを XML 子要素
	 * として書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		var object;
		if (typeof(this.property) == "undefined") {
			object = context.getObject();
		} else {
			object = context.getProperty(this.property);
		}
		if (typeof(object) == "undefined") {
			return;
		}

		var binding = context.getElementBinding(this.uri, this.name); 
		if (this.repeat) {
			if (object instanceof Array) {
				/* プロパティが配列型の場合 */
				for (var i = 0; i < object.length; i++) {
					if (this.value) {
						var wrapper = {};
						wrapper[this.value] = object[i];
						context.pushObject(wrapper);
					} else {
						context.pushObject(object[i]);
					}
					binding.write(context);
				}
			} else if (this.key) {
				/* プロパティがハッシュ型の場合 */
				for (prop in object) {
					if (this.value) {
						var wrapper = {};
						wrapper[this.key] = prop;
						wrapper[this.value] = object[prop];
						context.pushObject(wrapper);
					} else {
						context.pushObject(object[prop]);
					}
					binding.write(context);
				}
			}
		} else {
			/* プロパティが単純型の場合 */
			context.pushObject(object);
			binding.write(context);
		}
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 文書のルート要素とオブジェクトをバインドします。
 *
 * @name maskat.xml.DocumentBinding
 */ 
maskat.lang.Class.declare("maskat.xml.DocumentBinding", {

	/** @scope maskat.xml.DocumentBinding.prototype */

	/**
	 * コンストラクタです。
	 * 
	 * @param uri
	 *            ルート要素の名前空間 URI
	 * @param name
	 *            ルート要素のローカル名
	 */
	initialize: function(uri, name) {
		this.setRootElement(uri, name);
	},

	/**
	 * XML 文書のルート要素を設定します。
	 * 
	 * @param uri
	 *            ルート要素の名前空間 URI
	 * @param name
	 *            ルート要素のローカル名
	 */
	setRootElement : function(uri, name) {
		this.uri = uri || "";
		this.name = name;
	},

	/**
	 * XML 文書をオブジェクトに読み込みます。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param doc
	 *            XML 文書
	 */
	read: function(context, doc) {
		var element = doc.documentElement;
		var nodeName = element.localName || element.baseName;
		if (nodeName != this.name) {
			throw new maskat.lang.Error("INVALID_ROOT_NODE",
				{ elementName: nodeName });
		}
		
		var binding = context.getElementBinding(element);
		binding.read(context, element);
	},

	/**
	 * オブジェクトを XML 文書の形式で書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		context.writeXMLDeclaration();
		var binding = context.getElementBinding(this.uri, this.name);
		binding.write(context);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 要素とオブジェクトをバインドします。
 *
 * @name maskat.xml.ElementBinding
 */ 
maskat.lang.Class.declare("maskat.xml.ElementBinding", {

	/** @scope maskat.xml.ElementBinding.prototype */

	/**
	 * コンストラクタです。
	 * 
	 * @param name
	 *            XML 要素の名前空間 URI
	 * @param name
	 *            XML 要素のローカル名
	 * @param config
	 *            バインディング設定
	 * <pre>
	 * {
	 *     type: XML 要素の読み込み時に生成するオブジェクトの型
	 * 
	 *     attributes: {
	 *         属性名: 属性のバインディング設定
	 *                 ({@link maskat.xml.AttributeBinding#initialize} を参照)
	 *         ...
	 *     }
	 * 
	 *     children: {
	 *         要素名: 子要素のバインディング設定 
	 *                 ({@link maskat.xml.ChildNodeBinding#initialize} を参照)
	 *         ...
	 *     }
	 * }
	 * </pre>
	 */
	initialize: function(uri, name, config) {
		this.uri = uri || "";
		this.name = name;
		this.type = config && config.type;
		this.children = {};
		this.attributes = {};

		if (config && config.attributes) {
			for (var attrName in config.attributes) {
				this.addAttributeBinding(attrName, config.attributes[attrName]);
			}
		}

		if (config && config.children) {
			for (var childName in config.children) {
				this.addChildBinding(childName, config.children[childName]);
			}
		}
	},

	addAttributeBinding: function(name, config) {
		var binding = new maskat.xml.AttributeBinding(name, config);
		this.attributes[name] = binding;
	},

	getChildBinding: function(uri, name) {
		if (typeof(uri) == "object") {
			var qname = uri;
			uri = qname.namespaceURI;
			name = qname.localName || qname.baseName || qname.nodeName;
		}

		var binding = this.children[name] || this.children["*"];
		if (!binding) {
			throw new maskat.lang.Error("INVALID_CHILD_ELEMENT",
				{ elementName: this.name, childName: name });
		}
		return binding;
	},

	addChildBinding: function(name, config) {
		var uri = (name.charAt(0) == "#") ? "" : this.uri;
		var binding = new maskat.xml.ChildNodeBinding(uri, name, config);
		this.children[name] = binding;
	},

	/**
	 * XML 要素をオブジェクトに読み込みます。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param element
	 *            XML 要素
	 */
	read: function(context, element) {
		/* XML 要素の読み込み先となる新しいオブジェクトを生成 */
		try {
			if (this.type) {
				context.pushObject(new this.type());
			} else {
				context.createObject(element);
			}
		} catch (e) {
			throw new maskat.lang.Error("ELEMENT_READ_ERROR", {
				elementName: this.name
			}, e);
		}

		/* 属性の読み込み */
		var attributes = element.attributes;
		for (var i = 0; i < attributes.length; i++) {
			var attrName = attributes[i].localName || attributes[i].baseName;
			if (attributes[i].prefix == "xmlns" && attrName != "xmlns") {
				/* XML 名前空間宣言を登録する */
				context.addPrefixMapping(attrName, attributes[i].nodeValue);
			} else if (attributes[i].nodeName == "xmlns") {
				/* デフォルトの XML 名前空間宣言を登録する */
				context.addPrefixMapping("", attributes[i].nodeValue);
			} else {
				/* その他の属性を読み込み */
				try {
					this.readAttribute(context, attributes[i]);
				} catch (e) {
					throw new maskat.lang.Error("ATTRIBUTE_READ_ERROR", {
						elementName: this.name,
						attributeName: attributes[i].nodeName
					}, e);
				}
			}
		}

		/* 省略された属性の読み込み (必須チェック／デフォルト値) */
		for (var name in this.attributes) {
			if (name != "*" && !element.getAttribute(name)) {
				try {
					this.attributes[name].read(context, null);
				} catch (e) {
					throw new maskat.lang.Error("ATTRIBUTE_READ_ERROR", {
						elementName: this.name,
						attributeName: name
					}, e);
				}
			}
		}

		/* 子要素の読み込み */
		var children = element.childNodes;
		var occurrence = {};
		for (var j = 0; j < children.length; j++) {
			/* 子要素の出現回数をカウントする */
			var childName = children[j].localName || children[j].baseName || children[j].nodeName;
			occurrence[childName] = (occurrence[childName] || 0) + 1;
		
			switch (children[j].nodeType) {
			case 1: /* Node.ELEMENT_NODE */
			case 4: /* Node.CDATA_SECTION_NODE */
				this.readChildElement(context, children[j]);
				break;
			case 3: /* Node.TEXT_NODE */
				if (!children[j].nodeValue.match(/^\s*$/)) {
					this.readChildElement(context, children[j]);
				}
				break;
			}
		}

		/* 子要素の出現回数のチェック */
		for (var name in this.children) {
			var config = this.children[name];
			if (config.required && !occurrence[name]) {
				/* 必須の子要素が省略されている場合はエラー */
				throw new maskat.lang.Error("MISSING_CHILD_ELEMENT", {
					elementName: this.name, childName: name	});
			}
			if (!config.repeat && occurrence[name] > 1) {
				/* 繰り返しできない子要素が繰り返された場合はエラー */
				throw new maskat.lang.Error("DUPLICATED_CHILD_ELEMENT", {
					elementName: this.name, childName: name	});
			}
		}
	},

	readAttribute: function(context, attribute) {
		var name = attribute.localName || attribute.baseName;
		var binding = this.attributes[name];
		if (binding) {
			binding.read(context, attribute);
		} else if (this.attributes["*"]) {
			context.setProperty(name, attribute.nodeValue);
		} else {
			throw new maskat.lang.Error("UNKNOWN_ATTRIBUTE",
				{ elementName: this.name, attributeName: name });
		}
	},

	readChildElement: function(context, element) {
		var binding = this.getChildBinding(element);
		binding.read(context, element);
	},

	/**
	 * オブジェクトを XML 要素の形式で書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		/* XML 要素の開始タグを出力 */
		context.writeStartElement(this.uri, this.name);
		
		/* 属性を出力 */
		for (var attrName in this.attributes) {
			this.writeAttribute(context, attrName);
		}

		/* 子要素を出力 */
		for (var name in this.children) {
			if (this.children[name].required && !context.getProperty(name)) {
				throw new maskat.lang.Error("MISSING_CHILD_ELEMENT", {
					elementName: this.name, childName: name	});
			}
			this.writeChildElement(context, name);
		}

		/* XML 要素の終了タグを出力 */
		context.writeEndElement();
		context.popObject();
	},
	
	writeAttribute: function(context, name) {
		var binding = this.attributes[name];
		if (!binding) {
			throw new maskat.lang.Error("UNKNOWN_ATTRIBUTE",
				{ elementName: this.name, attributeName: name });
		}
		binding.write(context);
	},

	writeChildElement: function(context, name) {
		var binding = this.getChildBinding("", name);
		binding.write(context);
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * テキストノードと文字列をバインドします。
 *
 * @name maskat.xml.TextBinding
 */ 
maskat.lang.Class.declare("maskat.xml.TextBinding", {

	/** @scope maskat.xml.TextBinding.prototype */

	/**
	 * テキストノードに含まれる文字列を返します。
	 * 
	 * @param context
	 *            XML 読み込みに使用するコンテキスト
	 * @param text
	 *            テキストノード
	 */
	read: function(context, text) {
		context.pushObject(text.nodeValue);
	},

	/**
	 * オブジェクトをテキストノードの形式で書き出します。
	 * 
	 * @param context
	 *            XML 書き出しに使用するコンテキスト
	 */
	write: function(context) {
		context.writeText(context.popObject());
	}

});
/*
 * Copyright (c)  2006-2009 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.
 */
/**
 * @class
 * XML 文書とオブジェクトの相互変換を行います。
 *
 * @name maskat.xml.XMLObjectBinder
 */
maskat.lang.Class.declare("maskat.xml.XMLObjectBinder", {

	/** @scope maskat.xml.XMLObjectBinder.prototype */

	initialize: function(config, factory) {
		this.document = new maskat.xml.DocumentBinding(this);
		this.bindings = {
			"": {
				"#document": this.document,
				"#text": new maskat.xml.TextBinding(),
				"#cdata-section": new maskat.xml.CDATASectionBinding()
			}
		};
		this.addBindingConfiguration("", config);
		this.factory = factory;
	},

	addBindingConfiguration: function(uri, config){
		if (!config) {
			return;
		}

		uri = uri || "";
		for (var name in config) {
			if (name == "#document") {
				for (var root in config[name].children) {
					this.setRootElement(uri, root);
				}
			} else {
				this.addElementBinding(uri, name, config[name]);
			}
		}
	},

	/**
	 * XML 文書のルート要素を設定します。
	 * 
	 * @param uri
	 *            ルート要素の名前空間 URI
	 * @param name
	 *            ルート要素のローカル名
	 */
	setRootElement: function(uri, name) {
		this.document.setRootElement(uri, name);
	},

	/**
	 * 指定した名前空間 URI に対応する接頭辞を返します。
	 * 
	 * @param uri
	 *            名前空間 URI
	 * @returns 名前空間 URI に対応する接頭辞
	 */
	getPrefix: function(uri) {
		for (var prefix in this.namespaces) {
			if (this.namespaces[prefix] == uri) {
				return prefix;
			}
		}
		return undefined;
	},

	/**
	 * 名前空間 URI と接頭辞のマッピングを追加します。
	 * 
	 * @param prefix
	 *            接頭辞
	 * @param uri
	 *            名前空間 URI
	 */
	addPrefixMapping: function(prefix, uri) {
		if (!this.namespaces) {
			this.namespaces = {};
		}
		this.namespaces[prefix] = uri;
	},

	addElementBinding: function(uri, name, config) {
		uri = uri || "";
		var binding = new maskat.xml.ElementBinding(uri, name, config);
		if (!this.bindings[uri]) {
			this.bindings[uri] = {};
		}
		this.bindings[uri][name] = binding;
		return binding;
	},

	/**
	 * XML 文書の読み込み・書き出し処理のために利用する新しいコンテキストを
	 * 生成します。
	 * 
	 * @returns 新しく生成されたコンテキスト
	 */
	createContext: function() {
		return new maskat.xml.BindingContext(this);
	},

	/**
	 * HTTP GET メソッドで取得した XML 文書の情報を オブジェクトに読み込み、
	 * そのオブジェクトを返します。
	 * 
	 * @param url
	 *            XML 文書を取得する URL
	 * 
	 * @returns XML 文書の情報を読み込んだオブジェクト
	 */
	load: function(url) {
		try {
			return this.read(maskat.util.CrossBrowser.getXMLDocumentFrom(url));
		} catch (e) {
			throw new maskat.lang.Error("XML_LOAD_ERROR", { url: url }, e);
		}
	},

	/**
	 * XML 文書の情報をオブジェクトに読み込み、そのオブジェクトを返します。
	 * 
	 * @param doc
	 *            XML 文書
	 * 
	 * @returns XML 文書の情報を読み込んだオブジェクト
	 */
	read: function(doc) {
		var context = this.createContext();
		this.document.read(context, doc);
		return context.popObject();
	},

	createObject: function(element) {
		return this.factory ? this.factory.create(element) : {};
	},

	/**
	 * オブジェクトを XML 文書に変換し、文字列として返します。
	 * 
	 * @param object
	 *            オブジェクト
	 * @returns XML 文書形式の文字列
	 */
	write: function(object) {
		var context = this.createContext();
		context.pushObject(object);
		this.document.write(context);
		return context.getXMLString();
	}

});
