/*
汎用JSコンポーネント1.4.2
*/
////////////////////////////////////////////////////////////////////
//スクリプトのパスを取得
function getScriptPath(fileName) {
	var script = document.getElementsByTagName("script");
	var n = script.length;
	for(var i = 0; i < n; i++) {
		if(script[i].src && script[i].src.indexOf(fileName) != -1) {
			script = script[i];
			break;
		}
	}
	return script.src.replace(fileName, "");
}

//イベントリスナの追加
function addEvent(element, type, method) {
	if(element.addEventListener) {
		addEvent = function(element, type, method) {
			element.addEventListener(type, method, false);
		};
	} else if(element.attachEvent) {
		addEvent = function(element, type, method) {
			element.attachEvent("on" + type, method);
		};
	} else {
		addEvent = function() {};
	}
	addEvent(element, type, method);
}

//イベントリスナの解除
function removeEvent(element, type, method) {
	if(element.removeEventListener) {
		removeEvent = function(element, type, method) {
			element.removeEventListener(type, method, false);
		};
	} else if(element.detachEvent) {
		removeEvent = function(element, type, method) {
			element.detachEvent("on" + type, method);
		};
	} else {
		removeEvent = function() {};
	}
	removeEvent(element, type, method);
}

//イベント通知機能の付加
//引数のobjはインスタンスを渡す。
function setEventListener(obj, handlers) {
	//イベントハンドラのリストを追加
	obj.handlers = {};
	try {
		for(var i = 0; i < handlers.length; i++) {
			obj.handlers[handlers[i]] = [];
		}
	} catch(e) {
		return e;
	}

	//addEventListenerメソッドを追加
	obj.addEventListener = function(type, method) {
		this.removeEventListener(type, method);
		this.handlers[type].push(method);
	};

	//removeEventListenerメソッドを追加
	obj.removeEventListener = function(type, method) {
		for(var i = this.handlers[type].length - 1; i >= 0; i--) {
			if(this.handlers[type][i] === method) {
				this.handlers[type].splice(i, 1);
			}
		}
	};

	//イベント通知用メソッドを追加（オブジェクト内でイベントを発生させたいタイミングでこのメソッドを呼ぶ）
	obj.notify = function(type, event) {
		//デフォルトのイベントハンドラがあれば実行
		obj["on" + type] && typeof(obj["on" + type]) == "function" && obj["on" + type].apply(obj, [event]);

		var n = this.handlers[type].length;
		for(var i = 0; i < n; i++) {
			this.handlers[type][i].apply(obj, [event]);
		}
	};
}

//イベントのデフォルト動作のキャンセル
function eventCancel(event) {
	if(event.preventDefault) {
		eventCancel = function(event) { event.preventDefault(); };
	} else {
		eventCancel = function(event) { event.returnValue = false; };
	}
	eventCancel(event);
}

//イベントバブルの禁止
function eventStop(event) {
	if(event.stopPropagation) {
		eventStop = function(event) { event.stopPropagation(); };
	} else {
		eventStop = function(event) { event.cancelBubble = true; };
	}
	eventStop(event);
}

//スタイルシートのプロパティキャメライズ、デキャメライズ
function camelize(str) {
	return str.replace(/-([a-z])/g, function($0, $1) { return $1.toUpperCase(); });
}
function decamelize(str) {
	return str.replace(/[A-Z]/g, function($0) { return "-" + $0.toLowerCase(); });
}

//スタイルシートの取得
//ドキュメントツリー上に追加されていない要素に対してこの関数を使わない事
function getStyle(elm, style) {
	if(document.defaultView && document.defaultView.getComputedStyle) {
		getStyle = function(elm, style) {
			return document.defaultView.getComputedStyle(elm, "").getPropertyValue(decamelize(style));
		};
	} else if(elm.currentStyle) {
		getStyle = function(elm, style) {
			return elm.currentStyle[camelize(style)];
		};
	} else {
		getStyle = function() {};
	}
	return getStyle(elm, style);
}

//スタイルシートの設定
function setStyleRule(selector, property) {
	if(document.styleSheets[0].insertRule) {
		setStyleRule = function(selector, property) {
			var s = document.styleSheets;
			s[s.length - 1].insertRule(selector + " {" + property + "}", s[s.length - 1].cssRules.length);
		};
	} else if(document.styleSheets[0].addRule) {
		setStyleRule = function(selector, property) {
			var s = document.styleSheets;
			s[s.length - 1].addRule(selector, "{" + property + "}");
		};
	} else {
		setStyleRule = function() {};
	}
	setStyleRule(selector, property);
}

//不透明度変更（2回目以降は高速化のため、自身を書き換えて判別処理を無くす）
function setOpacity(elm, opacity) {
	if("opacity" in elm.style) {
		setOpacity = function(elm, opacity) {
			elm.style.opacity = opacity / 100;
		};
	} else if("MozOpacity" in elm.style) {
		setOpacity = function(elm, opacity) {
			elm.style.MozOpacity = opacity / 100;
		};
	} else if("filters" in elm) {
		//フィルタはどうせ不透明度くらいしか使わないと思うからこんなんでいい。
		setOpacity = function(elm, opacity) {
			elm.style.filter = "Alpha(opacity=" + opacity + ")";
		};
	} else {
		setOpacity = function() {};
	}
	setOpacity(elm, opacity);
}

//position:fixedをsetするけどIE6だけabsolute。
function setFixed(elm) {
	if(typeof elm.style.maxHeight == "undefined") {
		setFixed = function(elm) { elm.style.position = "absolute"; };
	} else {
		setFixed = function(elm) { elm.style.position = "fixed"; };
	}
	setFixed(elm);
}

//要素を中央に配置
function setCentering(elm) {
	elm.style.left = Math.round(
		(getGlobalWidth() - elm.offsetWidth) / 2 + (
			(getStyle(elm, "position") == "absolute")?
			(document.getElementsByTagName("body")[0].scrollLeft || document.documentElement.scrollLeft):
			0
		)
	) + "px";
	elm.style.top = Math.round(
		(getGlobalHeight() - elm.offsetHeight) / 2 + (
			(getStyle(elm, "position") == "absolute")?
			(document.getElementsByTagName("body")[0].scrollTop || document.documentElement.scrollTop):
			0
		)
	) + "px";
}

//閲覧領域の横幅
function getGlobalWidth() {
	if(window.innerWidth) {
		getGlobalWidth = function() {
			return window.innerWidth;
		};
	} else {
		getGlobalWidth = function() {
			return document.documentElement.clientWidth;
		};
	}
	return getGlobalWidth();
}

//閲覧領域の高さ
function getGlobalHeight() {
	if(window.innerHeight) {
		getGlobalHeight = function() {
			return window.innerHeight;
		};
	} else {
		getGlobalHeight = function() {
			return document.documentElement.clientHeight;
		};
	}
	return getGlobalHeight();
}

//要素の絶対位置の取得
function getAbsPos(elm) {
	var x = 0;
	var y = 0;

	var getOffset = function(elm) {
		x += elm.offsetLeft;
		y += elm.offsetTop;
		elm.offsetParent && getOffset(elm.offsetParent);
	};

	getOffset(elm);

	return [x, y];
}

//要素のドラッグ化
//余分な処理を省くためstyleのプロパティはtop、left決め打ちで処理。
function setDraggable(elm, key) {
	var key = key || elm;
	var d = document.documentElement;		//ショートカット
	var lastX;
	var lastY;

	//mousemoveイベント
	var mousemove = function(event) {
		elm.style.left = (event.clientX - lastX + parseInt(elm.style.left)) + "px";
		elm.style.top = (event.clientY - lastY + parseInt(elm.style.top)) + "px";
		lastX = event.clientX;
		lastY = event.clientY;
		eventCancel(event);
	};
	//mouseupイベント
	var mouseup = function() {
		removeEvent(d, "mousemove", mousemove);
		removeEvent(d, "mouseup", mouseup);
	};

	//前準備として
	var style = elm.style.position;
	style != "fixed" && style != "absolute" && (elm.style.position = "relative");
	elm.style.left = elm.style.left || "0";
	elm.style.top = elm.style.top || "0";
	key.style.cursor = "move";

	addEvent(key, "mousedown", function(event) {
		lastX = event.clientX;
		lastY = event.clientY;

		addEvent(d, "mousemove", mousemove);
		addEvent(d, "mouseup", mouseup);
		eventCancel(event);
	});
}

//FLASHの書き出し
function setFlash(src, width, height, param, xhtml, id) {
	if(!(4 in arguments) || xhtml) {		//第5引数が無ければXHTML書式
		xhtml = ' /';
	} else {
		xhtml = '';
	}
	if(5 in arguments) {
		id = ' id="' + id + '"';
	} else {
		id = '';
	}

	var str = '';
	if(typeof param == "object") {
		for(var i in param) {
			str += '<param name="' + i + '" value="' + param[i] + '"' + xhtml + '>';
		}
	}

	document.write(
		'<object data="' + src + '" type="application/x-shockwave-flash" width="' + width + '" height="' + height + '"' + id + '>' +
		'	<param name="movie" value="' + src + '"' + xhtml + '>' +
		str +
		'	<p><a href="http://get.adobe.com/jp/flashplayer/" target="_blank">GET FLASH PLAYER</a><br' + xhtml + '>' +
		'	FLASHプラグインがインストールされていないか、有効になっていません。<br' + xhtml + '>' +
		'	FLASHを表示するには上記リンクからFLASHプラグインをダウンロードし、インストールして下さい。</p>' +
		'</object>'
	);
}


////////////////////////////////////////////////////////////////////
//httpクライアントオブジェクト
function HttpClient(responseEvent) {
	//-----------------------------コンストラクタ
	this.body = [];
	this.client = (function() {
		if(window.XMLHttpRequest) {
			return new XMLHttpRequest();
		}
		if(window.ActiveXObject) {
			try {
				return new ActiveXObject("Msxml2.XMLHTTP");
			} catch(e) {
				try {
					return new ActiveXObject("Microsoft.XMLHTTP");
				} catch(e) {
					return null;
				}
			}
		}
		return null;
	})();

	setEventListener(this, ["error"]);

	//レスポンスイベントを登録
	this.setResponseEvent(arguments[0] || function() {});
}
HttpClient.prototype = {
	//プロパティ
	body:		[],			//送信データbody
	client:		null,		//httpクライアント
	xml:		false,		//レスポンスをXMLで受け取るかどうか
	interval:	12000,		//タイムアウト時間

	//リクエストメソッド
	request: function(method, url, async, user, password) {
		if((typeof async) == "undefined") { async = true; }

		//bodyを整形する
		var body = [];
		for(var i in this.body) {
			if(this.body[i] instanceof Array) {
				for(var j = 0; j < this.body[i].length; j++) {
					body.push(encodeURIComponent(i) + "=" + encodeURIComponent(this.body[i][j]));
				}
			} else {
				body.push(encodeURIComponent(i) + "=" + encodeURIComponent(this.body[i]));
			}
		}
		body = body.join("&");

		//タイムアウト処理
		var _this = this;
		this.timer = new Timer(function() {
			_this && _this.notify("error", {
				msg: "サーバからの応答がありません。",
				status: 0,
				statusText: "Timeout"
			});
		}, this, this.interval, false);

		//リクエスト
		if(method.match(/post/i)) {
			this.client.open(method, url, async, user, password);
			this.client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			this.client.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');		//IEのキャッシュ対策
			this.client.send(body);
		} else {
			if(url.indexOf("?") == -1) {
				url += "?";
				if(body.length) {
					body = "&" + body;
				}
			}
			this.client.open(method, url + body, async, user, password);
			this.client.setRequestHeader('If-Modified-Since', 'Thu, 01 Jun 1970 00:00:00 GMT');		//IEのキャッシュ対策
			this.client.send("");
		}
	},

	//レスポンスイベントの登録
	setResponseEvent: function(responseEvent) {
		var httpClient = this;

		this.client.onreadystatechange = function() {
			if(httpClient.client.readyState == 4) {
				httpClient.timer.cancel();

				if(httpClient.client.status == 200) {
					var data = httpClient.xml? httpClient.client.responseXML: httpClient.client.responseText;
					responseEvent(data);
				} else {
					var error = {
						msg: "サーバから以下のエラーが返されました。",
						status: httpClient.client.status,
						statusText: httpClient.client.statusText
					};
					httpClient.notify("error", error);
				}
			}
		};
	},

	//フォームからbodyを取得
	setBodyByForm: function(form) {
		for(i = 0; i < form.elements.length; i++) {
			var item = form.elements[i];

			switch(item.type) {
				case "text":
				case "password":
				case "hidden":
				case "textarea":
					this.body[item.name] = item.value;
					break;
				case "radio":
					if(item.checked) {
						this.body[item.name] = item.value;
					}
					break;
				case "checkbox":
					if(item.checked) {
						if(this.body[item.name]) {
							this.body[item.name].push(item.value);
						} else {
							this.body[item.name] = [item.value];
						}
					}
					break;
				case "select-one":
					for(var j = 0; j < item.options.length; j++) {
						if(item.options[j].selected) {
							this.body[item.name] = item.options[j].value;
							break;
						}
					}
					break;
				case "select-multiple":
					for(var j = 0; j < item.options.length; j++) {
						if(item.options[j].selected) {
							if(this.body[item.name]) {
								this.body[item.name].push(item.options[j].value);
							} else {
								this.body[item.name] = [item.options[j].value];
							}
						}
					}
					break;
				//このあたりは多分使わない
//				case "file":
//				case "image":
			}
		}
	},

	//エラー時に実行されるイベント。このオブジェクトのインスタンス上で実行される。
	//デフォルトではアラートでエラーメッセージを表示
	onerror: function(e) {
		alert(e.msg + "\n" + e.status + " " + e.statusText);
	}
};


////////////////////////////////////////////////////////////////////
//タイマオブジェクト
function Timer(method, owner, interval, repeat, now) {
	//引数があればデフォルト値を上書き
	if(0 in arguments) { this.method = method; }
	if(1 in arguments) { this.owner = owner; }
	if(2 in arguments) { this.interval = interval; }
	if(3 in arguments) { this.repeat = repeat; }

	//-----------------------------コンストラクタ
	var now = (4 in arguments)? now: true;
	now && this.start();
}
Timer.prototype = {
	//プロパティ
	method:		function() {},	//タイマイベント
	owner:		null,			//タイマイベントを保持するオブジェクト
	interval:	24,				//タイマ周期
	repeat:		true,			//繰り返すかどうか
	tid:		null,			//タイマID

	//タイマイベントの登録
	setMethod: function(owner) {
		var method = this.method;

		return function() {
			method.apply(owner);
		};
	},

	//タイマの開始
	start: function() {
		if(this.tid !== null) { this.cancel(); }
		if(this.repeat) {
			this.tid = setInterval(this.setMethod(this.owner), this.interval);
		} else {
			this.tid = setTimeout(this.setMethod(this.owner), this.interval);
		}
	},

	//タイマの解除
	cancel: function() {
		if(this.repeat) {
			clearInterval(this.tid);
		} else {
			clearTimeout(this.tid);
		}
		this.tid = null;
	}
};


////////////////////////////////////////////////////////////////////
//クッキーオブジェクト
//newした時点で引数に指定した名前のクッキーを読みに行く
function Cookie(name) {
	//引数があればデフォルト値を上書き
	if(0 in arguments) { this.name = name; }

	//コンストラクタ
	if(document.cookie) {
		var cookie = document.cookie.split(/; ?/);
		for(var i = 0; i < cookie.length; i++) {
			var tmp = cookie[i].split('=');
			if(tmp[0] === this.name || tmp[0] === encodeURIComponent(this.name)) {
				this.value = tmp[1];
				this.exist = true;
				break;
			}
		}
	}
}
Cookie.prototype = {
	//プロパティ
	name:		"cookie",		//クッキー名
	value:		"",				//値
	exist:		false,			//この名前のクッキーが存在するかどうか

	//デコードされた値を返す
	getValue: function() {
		return decodeURIComponent(this.value);
	},

	//クッキーの書き込み
	setCookie: function(expires, domain, path, secure) {
		var expires = expires || null;		//有効期限（Dateオブジェクトのインスタンス）
		var domain = domain || null;		//ドメイン
		var path = path || null;			//パス
		var secure = secure || false;		//セキュア接続かどうか

		document.cookie = encodeURIComponent(this.name) + "=" + encodeURIComponent(this.value) +
			((expires && typeof(expires) == "object" && (expires instanceof Date))? ("; expires=" + expires.toGMTString()): "") +
			(domain? ("; domain=" + domain): "") +
			(path? ("; path=" + path): "") +
			(secure? "; secure": "");
	}
};


////////////////////////////////////////////////////////////////////
//アラートオブジェクト
//newした段階では文書内に要素は追加されない事に注意。インスタンス作成後は手動で任意の位置にappendChildしてやる事。
//また、デフォルトではボタンアクションは割り当てられていないので、自前でcloseメソッドを呼び出してやる事。
function xAlert(id) {
	//引数があればデフォルト値を上書き
	if(0 in arguments) { this.id = id; }

	//-----------------------------コンストラクタ
	//コンテナ生成
	this.container = document.createElement("div");
	this.container.id = this.id;

	var o = this.container;
	o.style.display = "none";
	o.style.zIndex = "100";
	o.style.overflow = "hidden";
	setFixed(o);
	setDraggable(o);

	//コンテナに子要素を追加
	o.appendChild(document.createElement("p"));
	var a = [];
	for(var i = 0; i < 3; i++) {
		a[i] = o.appendChild(document.createElement("a"));
		a[i].style.cursor = "pointer";
	}
	a[0].innerHTML = "閉じる";
	this.closeButton = a[0];
	a[1].innerHTML = "はい";
	this.yesButton = a[1];
	a[2].innerHTML = "いいえ";
	this.noButton = a[2];
}
xAlert.prototype = {
	//プロパティ
	id:				"x_alert",		//アラート格納用コンテナのid属性
	container:		null,			//アラート格納用コンテナ
	closeButton:	null,			//「閉じる」ボタン
	yesButton:		null,			//「はい」ボタン
	noButton:		null,			//「いいえ」ボタン
	status:			false,			//アラートの状態　表示中：true　非表示中：false
	yesAction:		null,			//「はい」をクリックした時の動作

	//アラートの表示
	open: function(message, method, owner) {
		if(this.status) { return; }
		this.status = true;

		var o = this.container;
		o.getElementsByTagName("p")[0].innerHTML = message;

		if(arguments.length == 1) {
			o.getElementsByTagName("a")[0].style.display = "inline";
			o.getElementsByTagName("a")[1].style.display = "none";
			o.getElementsByTagName("a")[2].style.display = "none";
		} else {
			o.getElementsByTagName("a")[0].style.display = "none";
			o.getElementsByTagName("a")[1].style.display = "inline";
			o.getElementsByTagName("a")[2].style.display = "inline";

			var xAlert = this;
			this.yesAction = function() { method.apply(owner); };
			addEvent(o.getElementsByTagName("a")[1], "click", this.yesAction);
		}

		o.style.display = "block";
		setCentering(o);
	},

	//アラートの非表示
	close: function() {
		this.container.style.display = "none";
		this.status = false;
	}
};


////////////////////////////////////////////////////////////////////
//トランジションオブジェクト
//onstart、oncompletedのイベントを持つ。イベントはこのオブジェクトのインスタンス上で実行され、
//イベントオブジェクトとしてelementが渡される。
function Transition(element, interval, type, option) {
	//引数があればデフォルト値を上書き
	if(0 in arguments) { this.element = element; }
	if(1 in arguments) { this.interval = interval; }

	//コンストラクタ
	this.layers = [];
	this.add(type, option);
	setEventListener(this, ["start", "completed"]);

	//対象要素のメンバに自身を追加
	if(this.element && typeof(this.element) == "object") {
		this.element.transitions? this.element.transitions.push(this): (this.element.transitions = [this]);
	}
}
Transition.prototype = {
	//プロパティ
	element:		null,		//トランジションする要素
	interval:		24,			//インターバル
	status:			false,		//トランジションの動作状態　true:動作中、false:停止中
	timer:			null,		//タイマオブジェクト
	layers:			[],			//各トランジションレイヤのtype、optionを保持する配列
	chain:			null,		//連鎖先のトランジションオブジェクトのインスタンス

	//トランジションの開始
	start: function() {
		this.notify("start", this.element);
		this.status = true;

		//対象のノードに別の動作中トランジションがあれば強制終了
		var t = this.element.transitions;
		for(var i = 0; i < t.length; i++) {
			if(t[i] !== this && t[i].status) {
				t[i].stop();
			}
		}

		var transition = this;
		var layers = [];
		for(var i = 0; i < this.layers.length; i++) {
			layers.push(this[this.layers[i].type](this.layers[i].option, i));
		}

		var method = function() {
			for(var i = layers.length - 1; i >= 0; i--) {	//ループ中にspliceすると配列の長さが変わるので、逆順でループさせる
				//返り値があればそのトランジションレイヤは終了したという事なので、配列から除去する
				typeof(layers[i]()) != "undefined" && layers.splice(i, 1);
			}
			if(!layers.length) { transition.stop(); }
		};
		this.timer = new Timer(method, this, this.interval, true);
	},

	//トランジションの終了
	stop: function() {
		this.timer.cancel();
		this.status = false;

		//トランジションチェーンの開始
		if(typeof(this.chain) == "object" && this.chain instanceof Transition) {
			this.chain.start();
		}
		this.notify("completed", this.element);
	},

	//トランジションレイヤの追加
	add: function(type, option) {
		var type = type || "fade";
		var option = option || [];
		this.layers.push({type: type, option: option});
	},

	//トランジション要素の変更
	setElement: function(element) {
		if(!this.element) {
			this.element = element;
			element.transitions? element.transitions.push(this): (element.transitions = [this]);
			return;
		}

		var n = this.element.transitions.length;
		for(var i = 0; i < n; i++) {
			if(this.element.transitions[i] === this) {
				delete this.element.transitions[i];
				this.element.transitions.splice(i, 1);
				element.transitions? element.transitions.push(this): (element.transitions = [this]);
				this.element = element;
				break;
			}
		}
	},

	/////////////////////////////////////////////////////////////
	//各種トランジション
	//「※」が付いているoptionは必須です！！
	//トランジション終了時は、第2引数で指定されたIDを返して終了を通知する

	//フェード
	//単純な不透明度の遷移。
	//option[開始時の不透明度, 終了時の不透明度, 不透明度の増分]
	fade: function(option, id) {
		var transition = this;
		var opacity = (0 in option)? option[0]: 0;
		var finish = (1 in option)? option[1]: 100;
		var step = (2 in option)? option[2]: 10;
		var direction = (opacity < finish)? step: -step;

		return function() {
			opacity += direction;
			setOpacity(transition.element, opacity);
			if(opacity == finish) { return id; }
		};
	},

	//点滅
	//背景色の入れ替えを行う。リピート回数が0の場合は無限ループ。
	//option[リピート回数※, 背景色※ [, 背景色2, 背景色3 ...]]
	//IE対策のため、透明色を指定する時はtransparentではなく空文字を指定する事！！
	blink: function(option, id) {
		var transition = this;
		var bg = [];
		var index = 0;

		var n = option.length;
		for(var i = 1; i < n; i++) {
			bg.push(option[i]);
		}

		if(option[0]) {
			var counts = option[0];
			return function() {
				if(counts == 0) { return id; }
				transition.element.style.backgroundColor = bg[index];
				index++;
				if(bg.length == index) { index = 0; }
				counts--;
			};
		} else {
			return function() {
				transition.element.style.backgroundColor = bg[index];
				index++;
				if(bg.length == index) { index = 0; }
			};
		}
	},

	//文字の点滅
	//文字色の入れ替えを行う。リピート回数が0の場合は無限ループ。
	//option[リピート回数※, 文字色※ [, 文字色2, 文字色3 ...]]
	text_blink: function(option, id) {
		var transition = this;
		var bg = [];
		var index = 0;

		var n = option.length;
		for(var i = 1; i < n; i++) {
			bg.push(option[i]);
		}

		if(option[0]) {
			var counts = option[0];
			return function() {
				if(counts == 0) { return id; }
				transition.element.style.color = bg[index];
				index++;
				if(bg.length == index) { index = 0; }
				counts--;
			};
		} else {
			return function() {
				transition.element.style.color = bg[index];
				index++;
				if(bg.length == index) { index = 0; }
			};
		}
	},

	//長さの変化
	//汎用的に使えるCSSプロパティの長さ変化。
	//option[長さの指定できるCSSプロパティ, ステップ数, 最終的な長さ]
	//要素のスタイルにはあらかじめ適切な値を与えておく事。
	stretch: function(option, id) {
		var e = this.element;
		var direction = (0 in option)? option[0]: "left";
		var count = (1 in option)? option[1]: 5;
		var finish = (2 in option)? option[2]: 100;
		var step = Math.floor((finish - parseInt(getStyle(e, direction))) / count);

		return function() {
			if(!--count) {
				e.style[direction] = finish + "px";
				return id;
			}
			e.style[direction] = (parseInt(getStyle(e, direction)) + step) + "px";
		};
	},

	//加速度付きの長さ変化
	//上記のトランジションに加速度の付いた変化を与える
	//option[長さの指定できるCSSプロパティ, ステップ数, 最終的な長さ, 加速度]
	//加速度は負の値も取り得る。負の値の場合は徐々に減速していく。
	accelerate: function(option, id) {
		var e = this.element;
		var prop = (0 in option)? option[0]: "left";
		var total = (1 in option)? option[1]: 5;
		var finish = (2 in option)? option[2]: 100;
		var accel = (3 in option)? option[3]: 1;
		var start = parseInt(getStyle(e, prop));
		var range = finish - start;
		var count = 0;

		return function() {
			if(++count == total) {
				e.style[prop] = finish + "px";
				return id;
			}
			var ratio = count / total;
			if(accel < 0) {
				ratio = 1 - Math.pow(1 - ratio, -accel + 1);
			} else if(accel > 0) {
				ratio = Math.pow(ratio, accel + 1);
			}
			e.style[prop] = Math.round(ratio * range + start) + "px";
		};
	},

	//跳ね返り付きの長さ変化
	//上記のトランジションに壁を与える。
	//option[長さの指定できるCSSプロパティ, 重さ, 壁の位置]
	bound: function(option, id) {
		var e = this.element;
		var prop = (0 in option)? option[0]: "left";
		var weight = (1 in option)? option[1]: 0.5;
		var wall = (2 in option)? option[2]: 100;
		var direction = (parseInt(getStyle(e, prop)) > wall)? true: false;
		var inc = 0;

		return function() {
			if(direction) {
				var pos = Math.round(parseInt(getStyle(e, prop)) + --inc);
				if(pos <= wall) {
					pos = wall;
					inc = -inc - weight + 1;
					if(inc <= 0) {
						e.style[prop] = pos + "px";
						return id;
					}
				}
			} else {
				var pos = Math.round(parseInt(getStyle(e, prop)) + ++inc);
				if(pos >= wall) {
					pos = wall;
					inc = -inc + weight - 1;
					if(inc >= 0) {
						e.style[prop] = pos + "px";
						return id;
					}
				}
			}
			e.style[prop] = pos + "px";
		};
	},
	/////////////////////////////////////////////////////////////

	//トランジション開始時に呼ばれるイベントハンドラ
	onstart: function() {},

	//トランジション終了時に呼ばれるイベントハンドラ
	oncompleted: function() {}
};


////////////////////////////////////////////////////////////////////
//カレンダーオブジェクト
function Calendar() {
	//コンストラクタ
	this.date = new Date();
}
Calendar.prototype = {
	//プロパティ
	date:			null,		//Dateオブジェクトのインスタンス
	node:			null,		//このオブジェクトから生成されたDOMノード
	month:						//月名の配列
					["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
	week:						//週の配列
					["Sun.", "Mon.", "Tue.", "Wed.", "Thu.", "Fri.", "Sat."],

	//DOMノードの出力
	getNode: function(id, method) {
		//変数初期化
		var id = id || "calendar";
		var tmp = new Date();
		var y = tmp.getFullYear();
		var m = tmp.getMonth();
		var d = tmp.getDate();
		tmp.setFullYear(this.date.getFullYear());
		tmp.setMonth(this.date.getMonth());
		tmp.setDate(1);
		var seek = -tmp.getDay();
		tmp.setMonth(tmp.getMonth() + 1);
		tmp.setDate(0);
		var end = tmp.getDate();

		//主要ノード
		var table = document.createElement("table");
		var th = document.createElement("th");
		var prev = document.createElement("div");
		var next = document.createElement("div");
		var year = document.createElement("span");
		var month = document.createElement("span");
		var week = document.createElement("tr");

		//見出し部分生成
		table.appendChild(document.createElement("thead"));
		table.lastChild.appendChild(document.createElement("tr"));
		table.lastChild.lastChild.appendChild(th);
		th.appendChild(prev);
		th.appendChild(next);
		th.appendChild(month);
		th.appendChild(year);
		prev.appendChild(document.createTextNode("←"));
		next.appendChild(document.createTextNode("→"));
		year.appendChild(document.createTextNode(", " + this.date.getFullYear()));
		month.appendChild(document.createTextNode(this.month[this.date.getMonth()]));

		table.appendChild(document.createElement("tbody"));

		//週
		table.lastChild.appendChild(week);
		for(var i = 0; i < 7; i++) {
			week.appendChild(document.createElement("td"));
			week.lastChild.appendChild(document.createTextNode(this.week[i]));
		}

		//日付部分生成
		table.day = [];
		while(seek < end) {
			table.lastChild.appendChild(document.createElement("tr"));
			for(var i = 0; i < 7; i++) {
				var td = document.createElement("td");

				//日付の入るセル
				if(++seek > 0 && seek <= end) {
					td.appendChild(document.createTextNode(seek));
					td.date = seek;			//日付を保持するメンバを追加
					table.day.push(td);
				}

				//class設定
				var td_class = [];
				if(i == 0) {
					td_class.push("sunday");
				} else if(i == 6) {
					td_class.push("saturday");
				}
				if(this.date.getFullYear() == y && this.date.getMonth() == m && seek == d) {
					td_class.push("today");
				}
				td_class.length && (td.className = td_class.join(" "));

				table.lastChild.lastChild.appendChild(td);
			}
		}

		//属性など
		table.id = id;
		th.setAttribute("colSpan", "7");
		prev.className = "prev";
		next.className = "next";
		year.className = "year";
		month.className = "month";
		week.className = "week";
		week.firstChild.className = "sunday";
		week.lastChild.className = "saturday";

		//各ノードにすぐにアクセス出来るように
		table.heading = th;
		table.prev = prev;
		table.next = next;
		table.year = year;
		table.month = month;
		table.week = week;

		//前月、次月クリック処理
		var _this = this;		//クロージャ内で使う
		addEvent(prev, "click", function(e) {
			var c = document.getElementById(id);
//			_this.date.setMonth(_this.date.getMonth() - 1);
			//↑Safariでは1月の場合によきに計らってくれない。
			if(_this.date.getMonth() == 0) {
				_this.date.setFullYear(_this.date.getFullYear() - 1);
				_this.date.setMonth(11);
			} else {
				_this.date.setMonth(_this.date.getMonth() - 1);
			}
			c.parentNode.replaceChild(_this.getNode(id, method), c);
		});
		addEvent(next, "click", function(e) {
			var c = document.getElementById(id);
			_this.date.setMonth(_this.date.getMonth() + 1);
			c.parentNode.replaceChild(_this.getNode(id, method), c);
		});

		this.node = table;
		method && typeof(method) == "function" && method.apply(this);		//追加の処理があれば自身をthisにして実行

		return table;
	}
};


////////////////////////////////////////////////////////////////////
//要素のクラス名管理
function ClassName(elm) {
	//コンストラクタ
	this.element = elm;
}
ClassName.prototype = {
	//プロパティ
	element:			null,		//要素

	//クラス名を配列で取得
	getClassName: function() {
		return this.element.className.split(" ");
	},

	//クラス名を追加
	add: function(name) {
		var cn = this.getClassName();
		cn.push(name);
		this.element.className = cn.join(" ");
	},

	//クラス名を削除
	remove: function(name) {
		var cn = this.getClassName();
		var n = cn.length;
		for(var i = cn.length - 1; i >= 0; i--) {
			cn[i] == name && cn.splice(i, 1);
		}
		this.element.className = cn.join(" ");
	}
};
