var GraphicUtil = {

	// opacityを設定
	//  - div_obj : 対象となる要素または要素のID
	//  - value   : 0-1
	setOpacity: function(div_obj, opacity_value) {
		$(div_obj).setStyle({ opacity: opacity_value });
	},

	// opacityを取得 : int(0-1)
	//  - div_obj : 対象となる要素または要素のID
	getOpacity: function(div_obj) {
		return $(div_obj).getStyle('opacity');
	},

	// ブラウザ表示領域のサイズを取得 : { width: int, height: int }
	getWindowClientSize: function() {
		var win = window, body = document.body, html = document.documentElement;
		return {
			"width"  : ((win && win.innerWidth)  || (html && html.clientWidth)  || (body && body.clientWidth)  || 0),
			"height" : ((win && win.innerHeight) || (html && html.clientHeight) || (body && body.clientHeight) || 0)
		};
	},

	// 現在のスクロール位置を取得 : { x: int, y: int }
	getScrollPosition: function() {
		var win = window, body = document.body, html = document.documentElement;
		return {
			"x" : ((win && win.pageXOffset) || (html && html.scrollLeft) || (body && body.scrollLeft) || 0),
			"y" : ((win && win.pageYOffset) || (html && html.scrollTop)  || (body && body.scrollTop)  || 0)
		};
	},

	// 指定された要素の座標を取得 : { x: int, y: int }
	getElementPosition: function(element) {
		var result = Position.cumulativeOffset(element);
		return {
			"x": ((result && result[0]) || 0),
			"y": ((result && result[1]) || 0)
		};
	},

	// 指定された要素をブラウザの表示領域内に収まるよう位置を調整して移動
	//  - target_div : 対象となる要素
	//  - x          : int
	//  - y          : int
	adjustPos: function(target_div, x, y) {
		var win = GraphicUtil.getWindowClientSize();
		var scr = GraphicUtil.getScrollPosition();
		var dim = Element.getDimensions(target_div);

		// 各画面端からのoffset(px)
		var top    = y - scr.y;
		var bottom = scr.y + win.height - (y + dim.height);
		var left   = x - scr.x;
		var right  = scr.x + win.width - (x + dim.width);
		var dstyle = target_div.style;

		// 右にはみ出している場合
		if (right < 0) {
			if (win.width - dim.width > 0) {
				dstyle.left = win.width - dim.width + scr.x + "px";
			} else {
				dstyle.left = scr.x + "px";
			}

		// 左にはみ出している場合
		} else if (left < 0) {
			dstyle.left = scr.x + "px";

		// 左右にはみ出していない場合は指定の場所へ移動
		} else {
			dstyle.left = x + "px";
		}

		// 上にはみ出している場合
		if (top < 0) {
			dstyle.top = scr.y + "px";

		// 下にはみ出している場合
		} else if (bottom < 0) {
			if (win.height > dim.height) {
				dstyle.top = win.height - dim.height + scr.y + "px";
			} else {
				dstyle.top = scr.y + "px";
			}

		// 上下にはみ出していない場合は指定の場所へ移動
		} else {
			dstyle.top = y + "px";
		}
	},

	// ブラウザの表示領域内に収まるよう位置調整
	//  - target_div : 対象となる要素
	checkPos: function(target_div) {
		var pos = GraphicUtil.getElementPosition(target_div);
		GraphicUtil.adjustPos(target_div, pos.x, pos.y);
	}
};
if (Prototype.Browser.Gecko) {
	GraphicUtil.getWindowClientSize = function() {
		var body = document.body, html = document.documentElement;
		return {
			"width" : ((html && html.clientWidth)  || (body && body.clientWidth)  || 0),
			"height": ((html && html.clientHeight) || (body && body.clientHeight) || 0)
		};
	}
}

var FloatWindow = Class.create();
FloatWindow.prototype = {
	float_iframe     : null,
	float_win        : null,
	window_width     : 0,
	window_height    : 0,
	target_opacity   : 0.0,
	spaTimer         : null,
	vaniTimer        : null,
	fade_flg         : true,
	at_center        : false,
	visible          : false,
	dragControl      : null,
	onClose          : null,
	CloseOnClickOut  : true,
	bSynch           : false,

	// FloatWindowのコンストラクタ
	// - float_div_id : string ウィンドウのDIVのID名(必須)
	// - options      : 省略可能なオプションパラメータ
	//   - width           : int
	//   - height          : int
	//   - top             : int
	//   - left            : int
	//   - url             : string   AJAXで表示内容を取得する際のリクエスト先となるURL
	//   - display_div_id  : string   表示領域となる要素のID（省略時はfloat_div_idが代替として使用される）
	//   - draggable       : boolean  ドラッグ可能にするかどうか
	//   - drag_div_id     : string   ドラッグ開始可能エリアのID（指定しない場合はウインドウ全体がドラッグ可能となる）
	//   - class_name      : string   float_div_idで指定された要素に対して設定されるスタイルシートクラス名
	//   - default_focus   : string   初期状態でフォーカスをあてるフィールド名
	//   - fade_flg        : boolean  フェードイン/アウト効果をつけるかどうか
	//   - at_center       : boolean  画面中心に表示するかどうか
	//   - force_iframe    : boolean  IE 以外でも強制的に iframe を使用するかどうか
	//   - onClose         : function ウインドウが閉じられる際に実行されるcallback関数
	//   - CloseOnClickOut : boolean  フロートウィンドウの外側をクリックしたときに閉じるかどうか
	//
	initialize: function(float_div_id, options) {
		this.options = options = options || {};
		this.float_win = $(float_div_id);

		// Using IFrame to fix IE's Select Control bug.
		// IFrame.ID = IFrame.Name =  "iframe_" + div.ID;
		if (Prototype.Browser.IE || this.options.force_iframe) {
			this._createIFrame(float_div_id);
			this.float_iframe = $("iframe_" + float_div_id);
		}

		// 指定された id をもつ要素が存在しない場合はここで作成
		if (this.float_win == null) {
			new Insertion.Bottom(document.body, "<div id='" + float_div_id + "'></div>");
			this.float_win = $(float_div_id);
		}

		this.dOpac   = Prototype.Browser.IE? 0.24: 0.15;
		this.nOpac   = 16;
		this.dVanish = Prototype.Browser.IE? 0.20: 0.2;
		this.nVanish = 16;

		this.float_win.style.display  = "none";
		this.float_win.style.position = "absolute";
		this.fade_flg = options.fade_flg != null ? options.fade_flg : this.fade_flg;

		// Synchronize mode
		this.bSynch = this.options.fnCounter != null;	// Synchronize flag.
		if (this.bSynch) {
			this.fnCounter = this.options.fnCounter;
		}

		if (options.class_name)
			this.float_win.className = options.class_name;
		else
			this.float_win.style.backgroundColor = "#ffffff";

		if (options.width)
			this.window_width = options.width + "px";
		if (options.height)
			this.window_height = options.height + "px";
		if (options.top)
			this.float_win.style.top = options.top + "px";
		if (options.left)
			this.float_win.style.left = options.left + "px";
		if (this.float_win.style.width)
			this.window_width = this.float_win.style.width;
		if (this.float_win.style.height)
			this.window_height = this.float_win.style.height;

		if (options.display_div_id)
			this.display_div_id = options.display_div_id;
		else
			this.display_div_id = float_div_id;

		//ウインドウが閉じられる際に実行されるcallback関数を設定
		if (options.onClose)
			this.onClose = options.onClose;

		//ウィンドウの外がクリックされたときに閉じるかどうかを設定
		if (options.CloseOnClickOut != null)
			this.CloseOnClickOut = options.CloseOnClickOut;

		GraphicUtil.setOpacity(this.float_win, 0.0);

		//URLが指定されている場合はAjaxで読み込む
		if (options.url)
			this.getWebContent(options.url);
	},

	updateIFrame: function() {
		if (this.float_iframe) {
			var wstyle = this.float_win.style;
			var fstyle = this.float_iframe.style;
			fstyle.top        = wstyle.top;
			fstyle.left       = wstyle.left;
			fstyle.width      = wstyle.width;
			fstyle.height     = wstyle.height;
			fstyle.visibility = wstyle.visibility;
		}
	},

	/**
	 * 指定された位置にウインドウを開く
	 *
	 * left       : left (px)
	 * top        : top (px)
	 * url        : 指定されている場合はAJAXリクエストで取得したレスポンスを表示
	 * parameters : urlに対してAJAXリクエストで送信するデータ
	 */
	openByPos: function(left, top, url, parameters) {
		var fstyle = this.float_win.style;

		// at_centerパラメータがtrueであれば、_resize()でtopとleftが設定される。
		if (!this.options.at_center) {
			fstyle.top  = top  + "px";
			fstyle.left = left + "px";
		}
		this.target_opacity = 1.0;
		GraphicUtil.setOpacity(this.float_win, 0.0);

		if (this.window_width && this.window_height) {
			fstyle.width  = this.window_width;
			fstyle.height = this.window_height;
		}

		fstyle.zIndex = '10001';

		// onDocumentBodyClickをイベント登録
		if (this.CloseOnClickOut) {
			this.onDocumentBodyClick = this._onDocumentBodyClick.bindAsEventListener(this);
			Event.observe(document, 'mousedown', this.onDocumentBodyClick);
		}

		if (url) {
			this.getWebContent(url, true, parameters);
		} else {
			if (this.dragControl) {
				this.dragControl.reset();
				this.dragControl = null;
			}
			this._resize();

			// ドラッグ可能にする
			if (this.options.draggable != false) {
				this.dragControl = new DraggableUtil(
					this.float_win, $(this.options.drag_div_id) || null, this.float_iframe);
			}

			this.spark();
		}
	},

	// クリックされた場所で開く
	//  - event      : マウスのクリックイベント
	//  - url        : AJAXで表示内容を取得する場合のみ指定
	//  - parameters : urlが指定されている場合にAJAXリクエストでPOSTするデータ
	openWin: function(event, url, parameters) {
		this.openByPos(Event.pointerX(event), Event.pointerY(event), url, parameters);
	},

	// 指定された要素の上で開く
	//  - obj        : FloatWindowを開く位置を決定する為の要素
	//  - url        : AJAXで表示内容を取得する場合のみ指定
	//  - parameters : urlが指定されている場合にAJAXリクエストでPOSTするデータ
	openWinByElement: function(obj, url, parameters) {
		var e_pos = GraphicUtil.getElementPosition(obj);
		this.openByPos(e_pos.x, e_pos.y, url, parameters);
	},

	// FloatWindowを閉じる
	closeWin: function() {
		if (this.onClose) this.onClose();

		this.target_opacity = 0.0;

		if (this.dragControl) {
			this.dragControl.reset();
			this.dragControl = null;
		}

		if (Prototype.Browser.IE || this.options.force_iframe) {
			this.float_win.style.display    = "none";
			this.float_win.style.visibility = "hidden";
			clearTimeout(this.vaniTimer);

			// display: block にはしない (次回open時に問題が発生するため)
			if (this.float_iframe)
				this.float_iframe.style.visibility = "hidden";
		} else {
			this.vanish();
		}
		this.visible = false;

		if (this.onDocumentBodyClick) {
			Event.stopObserving(document, 'mousedown', this.onDocumentBodyClick);
		}
	},

	// FloatWindowの外側がクリックされたら閉じる(CloseOnClickOutオプションがtrueの場合のみ)
	_onDocumentBodyClick: function(event) {
		if (this.visible
			&& !Position.within(this.float_win, Event.pointerX(event), Event.pointerY(event))
			&& Event.isLeftClick(event)) {
			this.closeWin();
		}
		// テキストフィールドでFEP(IME)が無効になるIE7のバグ対策
		var src = Event.element(event);
		if (src.focus) src.focus();
	},

	// 指定された位置へFloatWindowを移動
	//  - top  : int
	//  - left : int
	moveWin: function(top, left) {
		if (top)  this.float_win.style.top = top + "px";
		if (left) this.float_win.style.left = left + "px";
		this.updateIFrame();
	},

	// 指定された要素の位置へFloatWindowを移動
	//  - obj     : 移動先の要素
	//  - options : マージン設定（省略された場合は top:0, left:0）
	//    - offsetX : int
	//    - offsetY : int
	moveWinByTargetObject: function(obj, options) {
		var p = GraphicUtil.getElementPosition(obj);
		var targetX = p.x;
		var targetY = p.y;
		if (options.offsetX) targetX += options.offsetX;
		if (options.offsetY) targetY += options.offsetY;
		this.moveWin(targetY, targetX);
	},

	// FloatWindowのサイズを表示内容に合わせて調整
	adjust: function() {
		var style = this.float_win.style;
		style.visibility = "hidden";
		style.display    = "block";
		style.width      = "";
		style.height     = "";

		var dim = Element.getDimensions(this.float_win);
		style.width      = this.window_width  = dim.width + "px";
		style.height     = this.window_height = dim.height + "px";
		style.visibility = "visible";
		GraphicUtil.checkPos(this.float_win);
		this.updateIFrame();
	},

	// IEのバグ回避のためFloatWindowの裏に配置するIFrameを作成
	_createIFrame: function(id) {
		var sFrame = "iframe_" + id, oFrame = $(sFrame);
		if (Prototype.Browser.IE && !oFrame) {
			new Insertion.Top(document.body, [
				"<iframe id='", sFrame, "' name='", sFrame, "' scrolling='no' frameborder='0' ",
				"style='position:absolute;width:0;height:0;top:0;left:0;border:none;display:block;z-index:0;visibility:hidden;'></iframe>"
			].join(''));
		} else if (oFrame) {
			Element.remove(oFrame);
		}
	},

	// サイズ・位置調整
	_resize: function() {
		var wstyle = this.float_win.style;
		wstyle.visibility = "hidden";
		wstyle.overflow   = "visible"; // Default. Content is not clipped and scroll bars are not added.
		wstyle.display    = "block";

		wstyle.width  = "";
		wstyle.height = "";

		// 画面中心に表示
		var dim = Element.getDimensions(this.float_win);
		if (this.options.at_center) {
			var win = GraphicUtil.getWindowClientSize();
			var scr = GraphicUtil.getScrollPosition();
			wstyle.top	= Math.round((win.height - dim.height)  + scr.y) + "px";
			wstyle.left	= Math.round((win.width - dim.width)  + scr.x) + "px";
		}

		this.window_width  = dim.width + "px";
		this.window_height = dim.height + "px";
		wstyle.width  = this.window_width;
		wstyle.height = this.window_height;

		wstyle.visibility = "visible";

		GraphicUtil.checkPos(this.float_win);

		this.updateIFrame();
		this.visible = true;
	},

	// AJAXリクエストにより表示内容を取得
	getWebContent: function(url, spark_flg, parameters) {
		parameters 　　= parameters || {};
		var self 　　　　= this;
		var nCounter = 0;

		if (this.bSynch)
			nCounter = this.fnCounter();

		new Ajax.Request(url, {
			method: 'GET',
			parameters: parameters,
			onSuccess: function(response) {
				if (self.bSynch && nCounter < self.fnCounter()) return;

				$(self.display_div_id).update(response.responseText);
				self._resize();
				if (self.dragControl) {
					self.dragControl.reset();
					self.dragControl = null;
				}

				// ドラッグ可能にする
				if (self.options.draggable != false && !self.dragControl) {
					self.dragControl = new DraggableUtil(
						self.float_win, $(self.options.drag_div_id) || null, self.float_iframe);
				}

				if (spark_flg) {
					self.spark();
				} else {
					if ($(self.options.default_focus))
						$(self.options.default_focus).activate();
				}
			},
			onFailure: function(response) {
				alert(response.responseText);
				self.closeWin();
			}
		});
	},

	// フェードインで開く
	spark: function() {
		var self = this;

		clearTimeout(this.vaniTimer);

		var current_opacity = GraphicUtil.getOpacity(this.float_win);

		if (!this.fade_flg)
			current_opacity = this.target_opacity;

		GraphicUtil.setOpacity(this.float_win, current_opacity + (this.target_opacity - current_opacity) * this.dOpac);
		if (current_opacity >= this.target_opacity - 0.05) {
			GraphicUtil.setOpacity(this.float_win, this.target_opacity);
			clearTimeout(this.spaTimer);
			if ($(this.options.default_focus))
				try { $(this.options.default_focus).activate() } catch(e) {};
		} else {
			clearTimeout(this.spaTimer);
			this.spaTimer = setTimeout(function() { self.spark() }, this.nOpac);
		}
	},

	// フェードアウトで閉じる
	vanish: function() {
		var self = this;

		clearTimeout(this.spaTimer);

		var current_opacity = GraphicUtil.getOpacity(this.float_win);

		if (!this.fade_flg)
			current_opacity = this.target_opacity;

		GraphicUtil.setOpacity(this.float_win, current_opacity + (this.target_opacity - current_opacity) * this.dVanish);

		if (current_opacity <= this.target_opacity + 0.1) {
			GraphicUtil.setOpacity(this.float_win, this.target_opacity);
			this.float_win.style.display    = "none";
			this.float_win.style.visibility = "hidden";
			clearTimeout(this.vaniTimer);

			if ((Prototype.Browser.IE || this.options.force_iframe) && this.float_iframe)
				this.float_iframe.style.visibility = "hidden";
		} else {
			this.vaniTimer = setTimeout(function() { self.vanish() }, this.nVanish);
		}
	}
};

// ドラッグ機能を提供するユーティリティ
// 使用例： new DraggableUtil(div_obj, drag_bar_obj);
var DraggableUtil = Class.create();
DraggableUtil.prototype = {
	float_iframe : null,
	drag_flg     : false,
	target_obj   : null,
	target_div   : null,
	drag_bar_obj : null,

	// DraggableUtilのコンストラクタ
	//  - div_obj      : ドラッグ可能にするDIV要素
	//  - drag_bar_obj : ドラッグ開始位置となる要素
	//  - float_iframe : IFRAME要素(IEの不具合対策としてIFRAMEを使用する場合のみ)
	initialize: function(div_obj, drag_bar_obj, float_iframe) {
		this.target_div   = div_obj;
		this.drag_bar_obj = drag_bar_obj || div_obj;
		this.float_iframe = float_iframe;

		this.eventMouseDown = this.onMouseDown.bindAsEventListener(this);
		this.eventMouseUp   = this.onMouseUp.bindAsEventListener(this);
		this.eventMouseMove = this.onMouseMove.bindAsEventListener(this);

		Event.observe(this.drag_bar_obj, "mousedown", this.eventMouseDown);
	},

	updateIFrame: function() {
		if (this.float_iframe) {
			var fstyle = this.float_iframe.style;
			var dstyle = this.target_div.style;
			fstyle.top        = dstyle.top;
			fstyle.left       = dstyle.left;
			fstyle.width      = dstyle.width;
			fstyle.height     = dstyle.height;
			fstyle.visibility = dstyle.visibility;
		}
	},

	onMouseDown: function(event) {
		this.drag_flg = true;

		var pos = Position.cumulativeOffset(this.drag_bar_obj);

		this.mouseX = Event.pointerX(event);
		this.mouseY = Event.pointerY(event);

		this.divPosX = this.mouseX - pos[0];
		this.divPosY = this.mouseY - pos[1];

		Event.observe(document, "mouseup", this.eventMouseUp);
		Event.observe(document, "mousemove", this.eventMouseMove);
		Event.stop(event);
	},

	onMouseMove: function(event) {
		if (!this.drag_flg) return true;

		var x = parseInt(Event.pointerX(event) - this.divPosX);
		var y = parseInt(Event.pointerY(event) - this.divPosY);
		GraphicUtil.adjustPos(this.target_div, x, y);

		this.updateIFrame();

		Event.stop(event);
	},

	onMouseUp: function(event) {
		this.drag_flg = false;
		this.target_div.style.cursor = "auto";
		this.updateIFrame();
		Event.stopObserving(document, "mouseup", this.eventMouseUp);
		Event.stopObserving(document, "mousemove", this.eventMouseMove);
		Event.stop(event);
	},

	onMouseOut: function(event) {
		 this.drag_flg = false;
		 this.target_div.style.cursor = "auto";
		 this.updateIFrame();
	},

	reset: function() {
		Event.stopObserving(document, "mouseup", this.eventMouseUp);
		Event.stopObserving(document, "mousemove", this.eventMouseMove);
		Event.stopObserving(this.drag_bar_obj, "mousedown", this.eventMouseDown);
	}
};

