var Glayer = {
	defaultId : 'glayer',
	show: function ( grayElement ) {
		grayElement = this.createElement( grayElement || this.defaultId );
		this.resetSize( grayElement );
		Element.show( grayElement );
		return grayElement;
	},
	hide: function ( grayElement ) {
		grayElement = $( grayElement || this.defaultId );
		Element.hide( grayElement );
		return grayElement;
	},
	fadeIn: function ( grayElement, fadeOptions ) {
		grayElement = this.createElement( grayElement || this.defaultId );
		this.resetSize( grayElement );
		fadeOptions = this.copyProperties( { to: this.getOpacity( grayElement ) }, fadeOptions || {} );
		new Glayer.FadeIn( grayElement, fadeOptions ).start();
		return grayElement;
	},
	fadeOut: function ( grayElement, fadeOptions ) {
		grayElement = $( grayElement || this.defaultId );
		fadeOptions = this.copyProperties( { from: this.getOpacity( grayElement ) }, fadeOptions || {} );
		new Glayer.FadeOut( grayElement, fadeOptions ).start();
		return grayElement;
	},
	showParallel: function ( otherElements, grayElement ) {
		grayElement = this.createElement( grayElement || this.defaultId );
		this.resetSize( grayElement );
		grayElement.show();
		for ( var i = 0; i < otherElements.length; i++ ) {
			otherElements[i] = $( otherElements[i] );
			otherElements[i].show();
		}
		return grayElement;
	},
	hideParallel: function ( otherElements, grayElement ) {
		grayElement = $( grayElement || this.defaultId );
		grayElement.hide();
		for ( var i = 0; i < otherElements.length; i++ ) {
			otherElements[i] = $( otherElements[i] );
			otherElements[i].hide();
		}
		return grayElement;
	},
	fadeInParallel: function ( otherElements, grayElement, fadeOptions ) {
		grayElement = this.createElement( grayElement || this.defaultId );
		this.resetSize( grayElement );
		fadeOptions = this.copyProperties( { to: this.getOpacity( grayElement ) }, fadeOptions || {} );
		var grayFade = new Glayer.FadeIn( grayElement, fadeOptions );
		var otherFades = [];
		for ( var i = 0; i < otherElements.length; i++ ) {
			otherElements[i] = $( otherElements[i] );
			otherFades.push( new Glayer.FadeIn( otherElements[i], {
				duration: fadeOptions.duration,
				to: this.getOpacity( otherElements[i] )
			} ) );
		}
		grayFade.start(function (){
			grayFade.update();
			for (var i = 0; i < otherFades.length; i++) { otherFades[i].update(); }
		} );
		return grayElement;
	},
	fadeOutParallel: function ( otherElements, grayElement, fadeOptions ) {
		grayElement = this.getElement(grayElement || this.defaultId);
		fadeOptions = this.copyProperties( { from: this.getOpacity( grayElement ) }, fadeOptions || {} );
		var grayFade = new Glayer.FadeOut( grayElement, fadeOptions );
		var otherFades = [];
		for ( var i = 0; i < otherElements.length; i++ ) {
			otherElements[i] = $( otherElements[i] );
			otherFades.push( new Glayer.FadeOut( otherElements[i], {
				duration: fadeOptions.duration,
				from: this.getOpacity( otherElements[i] )
			} ) );
		}
		grayFade.start( function () {
			grayFade.update();
			for ( var i = 0; i < otherFades.length; i++ ) { otherFades[i].update(); }
		} );
		return grayElement;
	},
	resetSize: function ( element ) {
		var position = this._getStyle( element, 'position' );
		if ( position != 'fixed' ) {
			var page = this.getPageSize();
			element.style.width = page.width + 'px';
			element.style.height = page.height + 'px';
		}
	},
	// Util
	getElement: function ( element ) {
		if ( typeof( element ) == 'string' ) {
			element = document.getElementById(element);
		}
		return element;
	},
	createElement: function ( element ) {
		var id = element;
		element = $( element );
		if ( !element ) {
			element = document.createElement( 'div' );
			element.id = id;
			Element.hide( element );
			document.body.appendChild( element );
		}
		return element;
	},
	copyProperties: function ( dest, src ) {
		for ( var property in src ) { dest[ property ] = src[ property ]; }
		return dest;
	},
	isIE : (/MSIE/.test(navigator.userAgent) && !window.opera),
	isWebKit : (navigator.userAgent.indexOf('AppleWebKit') != -1),
	// Window / Page Size
	getWindowSize: function () {
		var width;
		var height;
		if (document.compatMode == 'CSS1Compat' && !window.opera) {
			// Strict Mode && Non Opera
			width	= document.documentElement.clientWidth;
			height = document.documentElement.clientHeight;
		} else if (navigator.userAgent.indexOf('AppleWebKit') != -1){
			// Safari
			width	= window.innerWidth;
			height = window.innerHeight;
		} else {
			// other
			width	= document.body.clientWidth;
			height = document.body.clientHeight;
		}
		return {width: width, height: height};
	},
	getPageSize: function () {
		var windowSize = this.getWindowSize();
		var width	= windowSize.width;
		var height = windowSize.height;
		if (document.compatMode == 'CSS1Compat') {
			if (document.documentElement.scrollWidth > width) {
				width	= document.documentElement.scrollWidth;
			}
			if (document.documentElement.scrollHeight > height) {
				height = document.documentElement.scrollHeight;
			}
		} else {
			if (document.body.scrollWidth > width) {
				width	= document.body.scrollWidth;
			}
			if (document.body.scrollHeight > height) {
				height = document.body.scrollHeight;
			}
		}
		return {width: width, height: height};
	},
	// Styles
	getOpacity: function (element) {
		var value = this._getStyle(element, 'opacity');
		if (value) return parseFloat(value);
		if (value = (element.style.filter || '').match(/alpha\(opacity=(.*)\)/)) {
			if (value[1]) return parseFloat(value[1]) / 100;
		}
		return 1.0;
	},
	_getStyle: function (element, style) {
		var value = element.style[style];
		if (value) return value;
		if (document.defaultView && document.defaultView.getComputedStyle) {
			var oldDisplay = element.style.display;
			if (Glayer.isWebKit) element.style.display = 'block';
			var css = document.defaultView.getComputedStyle(element, null);
			if (css) value = css.getPropertyValue(style);
			if (Glayer.isWebKit) element.style.display = oldDisplay;
			return value;
		} else if (element.currentStyle) {
			return element.currentStyle[style];
		}
		return null;
	},
	// debug
	getDebugInfo: function () {
		var debugInfo = new Array();
		debugInfo.push("document.compatMode:" + document.compatMode);
		debugInfo.push("window.innerWidth:" + window.innerWidth);
		debugInfo.push("window.innerHeight:" + window.innerHeight);
		debugInfo.push("window.scrollMaxX:" + window.scrollMaxX);
		debugInfo.push("window.scrollMaxY:" + window.scrollMaxY);
		debugInfo.push("document.body.scrollWidth:" + document.body.scrollWidth);
		debugInfo.push("document.body.scrollHeight:" + document.body.scrollHeight);
		debugInfo.push("document.body.offsetWidth:" + document.body.offsetWidth);
		debugInfo.push("document.body.offsetHeight:" + document.body.offsetHeight);
		debugInfo.push("document.body.clientWidth:" + document.body.clientWidth);
		debugInfo.push("document.body.clientHeight:" + document.body.clientHeight);
		debugInfo.push("document.documentElement:" + document.documentElement);
		if (document.documentElement) {
			debugInfo.push("document.documentElement.scrollWidth:" + document.documentElement.scrollWidth);
			debugInfo.push("document.documentElement.scrollHeight:" + document.documentElement.scrollHeight);
			debugInfo.push("document.documentElement.offsetWidth:" + document.documentElement.offsetWidth);
			debugInfo.push("document.documentElement.offsetHeight:" + document.documentElement.offsetHeight);
			debugInfo.push("document.documentElement.clientWidth:" + document.documentElement.clientWidth);
			debugInfo.push("document.documentElement.clientHeight:" + document.documentElement.clientHeight);
		}
		return debugInfo;
	}
};
// Fade In/Out
Glayer.Fade = function (element, options) {
	this.setup(element, options);
};
Glayer.Fade.prototype = {
	intervalTime: 10,
	duration: 800,
	setup: function (element, options) {
		this.element = element;
		this.style = this.element.style;
		options = options || {};
		if (options.duration != undefined) this.duration = options.duration;
		if (options.from != undefined) this.from = options.from;
		if (options.to != undefined) this.to = options.to;
		if (options.callback != undefined) this.callback = options.callback;
		this.startTime = new Date().getTime();
		this.endTime = this.startTime + this.duration;
		this.range = this.to - this.from;
		if (Glayer.isIE && (!this.element.currentStyle.hasLayout)) {
			this.style['zoom'] = 1;
		}
		this.first = true;
	},
	start: function (updater) {
		var self = this;
		updater = updater || function (){ self.update(); };
		this.intervalId = setInterval(updater, this.intervalTime);
	},
	end: function () {
		if (this.intervalId != null) {
			clearInterval(this.intervalId);
			this.intervalId = null;
		}
		this.execCallback();
	},
	execCallback: function () {
		if (!this.callback) return;
		if (typeof this.callback == 'function') {
			this.callback();
		} else {
			for (var i = 0; i < this.callback.length; i++) {
				this.callback[i]();
			}
		}
	},
	update: function () {
		var nowTime = new Date().getTime();
		if (this.first) { 
			this.style.display = '';
			this.first = false;
		}
		if (nowTime >= this.endTime) {
			this.setOpacity(this.to);
			this.end();
		} else {
			this.setOpacity(this.from + (this.range * (nowTime - this.startTime) / this.duration));
		}
	},
	setOpacity: function (opacity) {
		this.style.opacity = opacity;
		if (Glayer.isIE) this.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
	}
};
Glayer.FadeIn = function () {
	Glayer.Fade.apply(this, arguments);
};
Glayer.copyProperties(Glayer.FadeIn.prototype, Glayer.Fade.prototype);
Glayer.FadeIn.prototype.from = 0.0;
Glayer.FadeIn.prototype.to = 1.0;
Glayer.FadeOut = function () {
	Glayer.Fade.apply(this, arguments);
};
Glayer.copyProperties(Glayer.FadeOut.prototype, Glayer.Fade.prototype);
Glayer.FadeOut.prototype.from = 1.0;
Glayer.FadeOut.prototype.to = 0.0;
Glayer.FadeOut.prototype.end = function () {
	this.style.display = 'none';
	this.setOpacity(this.from);
	Glayer.Fade.prototype.end.apply(this, arguments);
};
Glayer.copyProperties(Glayer, {
	//// Base ////
	boxClassName: 'glayer_box',
	createBox: function (boxElement) {
		boxElement = this.createElement(boxElement);
		boxElement.className = this.boxClassName;
		return boxElement;
	},
	showBox: function (boxElement, boxOptions, grayElement) {
		this.setBoxPosition(boxElement, boxOptions);
		return this.showParallel([boxElement], grayElement);
	},
	hideBox: function (boxElement, grayElement) {
		return this.hideParallel([boxElement], grayElement);
	},
	fadeInBox: function (boxElement, boxOptions, grayElement, fadeOption) {
		this.setBoxPosition(boxElement, boxOptions);
		return this.fadeInParallel([boxElement], grayElement, fadeOption);
	},
	fadeOutBox: function (boxElement, grayElement, fadeOption) {
		return this.fadeOutParallel([boxElement], grayElement, fadeOption);
	},
	setBoxPosition: function (boxElement, boxOptions) {
		var style = boxElement.style;
		boxOptions = boxOptions || {};
		var topRatio = boxOptions.topRatio != null ? boxOptions.topRatio : 0.5;
		var leftRatio = boxOptions.leftRatio != null ? boxOptions.leftRatio : 0.5;
		style.width = boxOptions.width || '';
		style.height = boxOptions.height || '';
		var windowTop = 0;
		var windowLeft = 0;
		var position = this._getStyle(boxElement, 'position');
		if (position != 'fixed') {
			windowTop = document.documentElement.scrollTop || document.body.scrollTop;
			windowLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
		}
		style.display = '';
		var offsetHeight = boxElement.offsetHeight;
		var offsetWidth = boxElement.offsetWidth;
		style.display = 'none';
		var windowSize = this.getWindowSize();
		style.top = ((windowSize.height * topRatio) - (offsetHeight * topRatio) + windowTop) + 'px';
		style.left = ((windowSize.width * leftRatio) - (offsetWidth * leftRatio) + windowLeft) + 'px';
	},
	//// Message ////
	defaultMessage: {
		boxId: 'glayer_message',
		textId: 'glayer_message_text'
	},
	createMessageBox: function (message, boxOptions) {
		boxOptions = boxOptions || {};
		var boxElement = boxOptions.box || this.defaultMessage.boxId;
		var boxId = boxElement.id || boxElement;
		var textElement = boxOptions.text || this.defaultMessage.textId;
		var textId = textElement.id || textElement;
		boxElement = this.getElement(boxElement);
		if (!boxElement) {
			boxElement = this.createBox(boxId);
			textElement = document.createElement('p');
			textElement.id = textId;
			boxElement.appendChild(textElement);
		} else {
			textElement = this.getElement(textElement);
		}
		textElement.innerHTML = message;
		return boxElement;
	},
	showMessage: function (message, boxOptions, grayElement) {
		return this.showBox(this.createMessageBox(message, boxOptions), boxOptions, grayElement);
	},
	hideMessage: function (boxElement, grayElement) {
		return this.hideBox(boxElement || this.defaultMessage.boxId, grayElement);
	},
	fadeInMessage: function (message, boxOptions, grayElement, fadeOption) {
		return this.fadeInBox(this.createMessageBox(message, boxOptions), boxOptions, grayElement, fadeOption);
	},
	fadeOutMessage: function (boxElement, grayElement, fadeOption) {
		return this.fadeOutBox(boxElement || this.defaultMessage.boxId, grayElement, fadeOption);
	},
	//// Alert ////
	defaultAlert: {
		boxId: 'glayer_alert',
		textId: 'glayer_alert_text',
		buttonId: 'glayer_alert_button',
		okId: 'glayer_alert_ok',
		okLabel: 'OK'
	},
	getDefaultAlertOkFunc: function () {
		return function (){ Glayer.hideAlert(); };
	},
	createAlertBox: function (message, boxOptions) {
		boxOptions = boxOptions || {};
		var boxElement = boxOptions.box || this.defaultAlert.boxId;
		var boxId = boxElement.id || boxElement;
		var textElement = boxOptions.text || this.defaultAlert.textId;
		var textId = textElement.id || textElement;
		var okElement = boxOptions.ok || this.defaultAlert.okId;
		var okId = okElement.id || okElement;
		boxElement = this.getElement(boxElement);
		if (!boxElement) {
			boxElement = this.createBox(boxId);
			textElement = document.createElement('p');
			textElement.id = textId;
			boxElement.appendChild(textElement);
			var buttonElement = document.createElement('p');
			buttonElement.id = this.defaultAlert.buttonId;
			boxElement.appendChild(buttonElement);
			okElement = document.createElement('input');
			okElement.type = 'button';
			okElement.id = okId;
			buttonElement.appendChild(okElement);
		} else {
			textElement = this.getElement(textElement);
			okElement = this.getElement(okElement);
		}
		textElement.innerHTML = message;
		okElement.value = boxOptions.okLabel || this.defaultAlert.okLabel;
		okElement.onclick = boxOptions.callback || this.getDefaultAlertOkFunc();
		return boxElement;
	},
	showAlert: function (message, boxOptions, grayElement) {
		return this.showBox(this.createAlertBox(message, boxOptions), boxOptions, grayElement);
	},
	hideAlert: function (boxElement, grayElement) {
		return this.hideBox(boxElement || this.defaultAlert.boxId, grayElement);
	},
	fadeInAlert: function (message, boxOptions, grayElement, fadeOption) {
		return this.fadeInBox(this.createAlertBox(message, boxOptions), boxOptions, grayElement, fadeOption);
	},
	fadeOutAlert: function (boxElement, grayElement, fadeOption) {
		return this.fadeOutBox(boxElement || this.defaultAlert.boxId, grayElement, fadeOption);
	},
	//// Confirm ////
	defaultConfirm: {
		boxId: 'glayer_confirm',
		textId: 'glayer_confirm_text',
		buttonId: 'glayer_confirm_button',
		okId: 'glayer_confirm_ok',
		okLabel: 'OK',
		cancelId: 'glayer_confirm_cancel',
		cancelLabel: 'Cancel'
	},
	getConfirmResultFunc: function (callback, result) {
		return function (){ callback(result); };
	},
	createConfirmBox: function (message, resultCallback, boxOptions) {
		boxOptions = boxOptions || {};
		var boxElement = boxOptions.box || this.defaultConfirm.boxId;
		var boxId = boxElement.id || boxElement;
		var textElement = boxOptions.text || this.defaultConfirm.textId;
		var textId = textElement.id || textElement;
		var okElement = boxOptions.ok || this.defaultConfirm.okId;
		var okId = okElement.id || okElement;
		var cancelElement = boxOptions.cancel || this.defaultConfirm.cancelId;
		var cancelId = cancelElement.id || cancelElement;
		boxElement = this.getElement(boxElement);
		if (!boxElement) {
			boxElement = this.createBox(boxId);
			textElement = document.createElement('p');
			textElement.id = textId;
			boxElement.appendChild(textElement);
			var buttonElement = document.createElement('p');
			buttonElement.id = this.defaultConfirm.buttonId;
			boxElement.appendChild(buttonElement);
			okElement = document.createElement('input');
			okElement.type = 'button';
			okElement.id = okId;
			buttonElement.appendChild(okElement);
			cancelElement = document.createElement('input');
			cancelElement.type = 'button';
			cancelElement.id = cancelId;
			buttonElement.appendChild(cancelElement);
		} else {
			textElement = this.getElement(textElement);
			okElement = this.getElement(okElement);
			cancelElement = this.getElement(cancelElement);
		}
		textElement.innerHTML = message;
		okElement.value = boxOptions.okLabel || this.defaultConfirm.okLabel;
		okElement.onclick = this.getConfirmResultFunc(resultCallback, true);
		cancelElement.value = boxOptions.cancelLabel || this.defaultConfirm.cancelLabel;
		cancelElement.onclick = this.getConfirmResultFunc(resultCallback, false);
		return boxElement;
	},
	showConfirm: function (message, resultCallback, boxOptions, grayElement) {
		return this.showBox(this.createConfirmBox(message, resultCallback, boxOptions), boxOptions, grayElement);
	},
	hideConfirm: function (boxElement, grayElement) {
		return this.hideBox(boxElement || this.defaultConfirm.boxId, grayElement);
	},
	fadeInConfirm: function (message, resultCallback, boxOptions, grayElement, fadeOption) {
		return this.fadeInBox(this.createConfirmBox(message, resultCallback, boxOptions), boxOptions, grayElement, fadeOption);
	},
	fadeOutConfirm: function (boxElement, grayElement, fadeOption) {
		return this.fadeOutBox(boxElement || this.defaultConfirm.boxId, grayElement, fadeOption);
	}
});

