/* -------------------------------------------------------------------------- */
/**
 *    @fileoverview
 *       Pseudo Dialog.
 *
 *    @version rev2.1.20090810
 *    @requires jquery.js
 *    @requires jquery.easing.js
 *    @requires jquery.mousewheel.js
 *    @requires bajl.js
 *    @requires bajl.balloon.js
 *    @requires bajl.fontSizeObserver.js    (optional)
 *    @requires bajl.keyEquiv.js            (optional)
 *    @requires bajl.processingInfo.js      (optional)
 *    @requires bajl.pseudoDialogContent.js (in content frame page)
 *    @requires bajl.pseudoDialog.css
 */
/* -------------------------------------------------------------------------- */
(function($) {



/* -------------------- Settings for BAJL.PseudoDialog -------------------- */
/** 
 * settings for {@link BAJL.PseudoDialog}
 * @namespace settings for {@link BAJL.PseudoDialog}
 * @fieldOf BAJL.settings
 */
BAJL.settings.PseudoDialog = {
	  'id'           : 'BAJLPseudoDialog'
	, 'template'     : '<div id="BAJLPseudoDialogBody"></div>'
	                 + '<div id="BAJLPseudoDialogFrame">'
	                 + '<iframe name="BAJLPseudoDialogFrame" border="0" frameborder="0" marginheight="0" marginwidth="0" scrolling="no"></iframe>'
	                 + '</div>'
	, 'cbodyExpr'    : '#BAJLPseudoDialogBody'
	, 'ignoreX'      : false
	, 'ignoreY'      : false
	, 'activedCName' : 'BAJLPseudoDialogIsActive'
	, 'closeBtnExpr' : 'a.BAJLPseudoDialogCloseBtn'
	, 'loading'      : {
	                   	  'startDelay' : 500
	                   	, 'timeout'    : 15000
	                   }
	, 'contentFrame' : {
	                   	  'name'    : 'BAJLPseudoDialogFrame'
	                   	, 'maxSize' : { 'W' : 950 , 'H' : 700  }
	                   }
	, 'useShield'    : {
	                   	  'click'  : true
	                   	, 'focus'  : true
	                   	, 'scroll' : true
	                   }
	, 'effect'       : {
	                   	  'dialogResizeDuration' : 500
	                   	, 'shieldFadeDuration'   : 750
	                   	, 'shieldMaxOpacity'     : 0.5
	                   }
	, 'autoSetup'    : {
	                   	  'enabled'    : true
	                   	, 'linkTarget' : 'pseudoDialog'
	                   }
};



/* --------------- Class : BAJL.PseudoDialog --------------- */
/**
 * provides 'pseudo dialog' (use this as single object!).
 * @class pseudo dialog
 * @extends BAJL.Balloon
 * @constructor
 */
BAJL.PseudoDialog = function() {
	/** dialog content body node.
	    @type Element
	    @private */
	this.contentBody    = null;
	/** dialog content frame controller.
	    @type BAJL.PseudoDialogCotentFrame */
	this.contentFrame   = null;
	/** close button node.
	    @type Element
	    @private */
	this.closeButton    = null;
	/** an assciative array {'click', 'focus', 'scroll'} of the 'shield' instances.
		@type Object
	    @private */
	this.shields        = {};
	/** flag for prevent close function of the dialog.
		@type Boolean
	    @private */
	this.closeAllowed   = true;
	/** processing information display.
	    @type BAJL.ProcessingInfo
	    @private */
	this.processingInfo = null;

	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.PseudoDialog.prototype = new BAJL.Balloon;

/** @private */
BAJL.PseudoDialog.prototype.initSuper = BAJL.PseudoDialog.prototype.init;

/**
 * initialize dialog balloon.
 * @private
 */
BAJL.PseudoDialog.prototype.init = function() {
	var setting = BAJL.settings.PseudoDialog;

	// initialize.
	this.initSuper(setting);
	this.moveTo(-10000, -10000);

	// set events.
	$(window).resize(BAJL.Delegate(function() { if (this.active) this.moveToCenter() }, this));

	// prepare content frame.
	this.contentFrame = BAJL.Singleton(BAJL.PseudoDialogContentFrame);
	this.contentFrame.addCallback('onLoadTimeout'   , function() { this.allowClose(); this.close(); }, this);
	this.contentFrame.addCallback('onCloseRequested', function() { this.allowClose(); this.close(); }, this);

	// set key equivalents.
	if (typeof BAJL.KeyEquiv != 'undefined') {
		BAJL.Singleton(BAJL.KeyEquiv).addKey('!', this.close, this);
	}

	// create processing info display
	if (typeof BAJL.ProcessingInfo != 'undefined') {
		this.processingInfo = new BAJL.ProcessingInfo;
	}

	// observe font size changing.
	if (typeof BAJL.FontSizeObserver != 'undefined') {
		BAJL.Singleton(BAJL.FontSizeObserver).addCallback('onChange', function() {
			if (this.contentFrame.hasContent()) {
				this.contentFrame.adjustSize();
			}
			this.adjustSize();
		}, this);
	};

	// register shields.
	if (setting.useShield.click ) this.shields.click  = BAJL.Singleton(BAJL.ClickShield   );
	if (setting.useShield.focus ) this.shields.focus  = BAJL.Singleton(BAJL.TabFocusShield);
	if (setting.useShield.scroll) this.shields.scroll = BAJL.Singleton(BAJL.ScrollShield  );

	// event handling of the shields.
	if (this.shields.click) {
		this.addCallback('onResize', this.shields.click.adjustSize, this.shields.click);
		this.shields.click.addCallback('onPrevented', this.close, this);
	}
	if (this.shields.focus) {
		this.shields.focus.addCallback('onPrevented', this.setDefaultFocus, this);
	}
	if (this.shields.scroll) {
	}
}

/**
 * get current content in the dialog body. (override {@link BAJL.Balloon.getContent})
 * @return current content in the dialog body.
 * @type Node[]
 */
BAJL.PseudoDialog.prototype.getContent = function() {
	return $(this.contentBody || this.node).contents().get();
}

/**
 * update dialog content.
 * @param {NodeList|Element|Element[]|jQuery|String} content    content for dialog content.
 * @return current content in the dialog body
 * @type Node[]
 */
BAJL.PseudoDialog.prototype.update = function(content) {
	$(this.contentBody || this.node).empty().append(content);

	// initialize close button.
	var $btn = $(this.node).find(BAJL.settings.PseudoDialog.closeBtnExpr).eq(0);
	var flag = 'BAJL.PseudoDialog.closeButton.inited';
	if (!$btn.data(flag)) {
		$btn.data(flag, true).click(BAJL.Delegate(function(e) {
			e.preventDefault();
			this.close();
		}, this));
	}
	this.closeButton = $btn.get(0);

	if (this.shields.focus) {
		this.shields.focus.setIsolation(this.contentBody);
	}

	// post process
	this.adjustSize();
	this.doCallbackByName('onContentChange');

	return this.getContent();
}

/**
 * open the dialog.
 */
BAJL.PseudoDialog.prototype.open = function() {
	if (!this.active) {
		// set document status to 'dialog is shown'
		$(document.body).addClass(this.activedCName);

		// hide processing info display.
		if (this.processingInfo) {
			this.processingInfo.hide();
		}

		// pre process for open dialog
		this.enableShield();
		this.show();
		this.moveToCenter();
		this.setOffset(0, (BAJL.GetGeometry().scrollY - this.getGeometry().posY) * 0.3);
		$(this.node).stop().hide();

		// show dialog
		if (!this.contentFrame.hasContent()) {
			// in the case of the dialog shows contents of the same page.
			if (this.shields.click) {
				this.shields.click.addCallback('onEnabled', _show, this, 'disposable');
			} else {
				_show.call(this);
			}
		} else {
			// in the case of the dialog shows contents in the iframe.
			_show.call(this);
		}
	}

	// show dialog with slideDown effect
	function _show() {
		var duration = BAJL.settings.PseudoDialog.effect.dialogResizeDuration;
		var callback = BAJL.Delegate(_postProcess, this);
		$(this.node).slideDown(duration, callback);
	}

	function _postProcess() {
		this.setDefaultFocus();
		this.doCallbackByName('onOpen');
	}

}

/**
 * open specified url in the dialog.
 * @param {String} url    url to load
 */
BAJL.PseudoDialog.prototype.openURL = function(url) {
	if (typeof url != 'string') {
		throw new URIError('BAJL.PseudoDialog.openURL: first argument must be a string (URL).');
	} else {
		$(document.body).addClass(this.activedCName);

		this.disallowClose();
		this.enableShield();
		if (this.processingInfo) {
			this.processingInfo.show();
		}

		this.contentFrame.addCallback('onLoaded', function() {
			this.allowClose();
			this.update();
			this.open();
		}, this, 'disposable');

		var loading = BAJL.settings.PseudoDialog.loading;
		new BAJL.Timeout(function() { this.contentFrame.load(url, loading.timeout) }, loading.startDelay, this);
	}
}

/**
 * close the dialog.
 */
BAJL.PseudoDialog.prototype.close = function() {
	if (this.closeAllowed) {
		if (this.processingInfo.isActive()) {
			this.processingInfo.hide();
		}
		if (!this.active) {   // in the case of loading timeout of pseudo dialog content
			this.disableShield();
		} else {
			var duration = BAJL.settings.PseudoDialog.effect.dialogResizeDuration;
			var callback = BAJL.Delegate(_hide, this);
			$(this.node).slideUp(duration, callback);
		}
	}

	function _hide() {
		this.hide();
		this.moveTo(-10000, -10000);
		this.resizeTo(0, 0);
		this.contentFrame.unload();
		this.disableShield();
		if (this.shields.click) {
			this.shields.click.addCallback('onDisabled', _postProcess, this, 'disposable');
		} else {
			_postProcess.call();
		}
	}

	function _postProcess() {
		$(document.body).removeClass(this.activedCName);
		this.doCallbackByName('onClose');
	}
}

/**
 * set callback function for 'onClose'.
 * @param {Function} func             callback function/method
 * @param {Object}   [aThisObject]    object that will be a global object ('this') in func
 * @return delegated callback function.
 * @type Function
 */
BAJL.PseudoDialog.prototype.setCloseCallback = function(func, aThisObject) {
	this.removeDisposableCallback('onClose');
	return this.addCallback('onClose', func, aThisObject, 'disposable');
}

/**
 * enable close function
 */
BAJL.PseudoDialog.prototype.allowClose = function() {
	this.closeAllowed = true;
	if (this.closeButton) {
		$(this.closeButton).css('visibility', 'visible');
	}
}

/**
 * disable close function.
 */
BAJL.PseudoDialog.prototype.disallowClose = function() {
	this.closeAllowed = false;
	if (this.closeButton) {
		$(this.closeButton).css('visibility', 'hidden');
	}
}

/**
 * enable all shields.
 * @private
 */
BAJL.PseudoDialog.prototype.enableShield = function() {
	if (this.shields.scroll) this.shields.scroll.enable();
	if (this.shields.click ) this.shields.click .enable();
	if (this.shields.focus ) this.shields.focus .enable();
}

/**
 * disalbe all shields.
 * @private
 */
BAJL.PseudoDialog.prototype.disableShield = function() {
	if (this.shields.click) {
		if (this.shields.scroll) {
			this.shields.click.addCallback('onDisabled', this.shields.scroll.disable, this.shields.scroll, 'disposable');
		}
		this.shields.click .disable();
	} else if (this.shields.scroll) {
		this.shields.scroll.disable();
	}
	if (this.shields.focus) this.shields.focus.disable();
}

/**
 * adjust dialog size.
 */
BAJL.PseudoDialog.prototype.adjustSize = function() {
//	if (this.templateNode && this.active) {
//		var geom = this.getGeometry();
//		this.resizeTo(0, 0); // set width and height to 'auto'
//		var width  = this.templateNode.offsetWidth;
//		var height = this.templateNode.offsetHeight;
//		this.resizeTo(geom.width, geom.height);
//		if (!this.effect) {
//			this.resizeTo(width, height);
//			this.moveToCenter();
//		} else {
//			this.effect.open();
//		}
//	}

	// @@@@@@@@@@@@@ temporary @@@@@@@@@@@@@@@@@@
	this.moveToCenter();
}

/**
 * set default focus into dialog.
 */
BAJL.PseudoDialog.prototype.setDefaultFocus = function() {
	var $input  = $(this.node).find('input:text').eq(0);
	var $anchor = (!this.closeButton) ? $([]) : $(this.closeButton).find('a').andSelf().filter('a');
	$input .focus();
	$anchor.focus();
	this.contentFrame.setDefaultFocus();
}



/* --------------- Class : BAJL.PseudoDialogContentFrame --------------- */
/**
 * provides 'content frame' for {@link BAJL.PseudoDialog}.
 * @class pseudo dialog content frame
 * @extends BAJL.Observable
 * @constructor
 */
BAJL.PseudoDialogContentFrame = function() {
	/** iframe element node.
		@type Element
	    @private */
	this.node      = null;
	/** iframe (window) object.
		@type Window
	    @private */
	this.frame     = null;
	/** url of the page currently loaded in the iframe
	    @type String */
	this.url       = '';
	/** iframe loading timeout timer.
		@type BAJL.Timeout
	    @private */
	this.timer     = null;
	/** an Image, or an instance of 'dialog content object' (created in pseudoDialogContent.js in the iframe)
		@type Image|BAJL.PseudoDialogContent
	    @private */
	this.content   = null;
	/** flag for prevent dialog from opening without intending.
		@type Boolean
	    @private */
	this.allowOpen = false;

	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.PseudoDialogContentFrame.prototype = new BAJL.Observable;

/**
 * initialize
 * @private
 */
BAJL.PseudoDialogContentFrame.prototype.init = function() {
	var name   = BAJL.settings.PseudoDialog.contentFrame.name;
	this.node  = $('iframe[name="' + name + '"]').get(0);
	this.frame = window.frames[name];

	this.unload();
}

/**
 * show the iframe
 */
BAJL.PseudoDialogContentFrame.prototype.show = function() {
	if (this.node) {
		$(this.node).css({ position : 'static', top : '0px', left : '0px' });
	}
	this.adjustSize();
}

/**
 * hide the iframe
 */
BAJL.PseudoDialogContentFrame.prototype.hide = function() {
	if (this.node && !(BAJL.ua.isSafari && BAJL.ua.version < 522 /* Safari 2.0.x or ealier */)) {
		$(this.node).css({ position : 'absolute', top : '-10000px', left : '-10000px' });
	}
	this.adjustSize(1, 1);
}

/**
 * load specified url in the iframe.
 * @param {String} url          url to load
 * @param {Number} [timeout]    miliseconds to timeout
 */
BAJL.PseudoDialogContentFrame.prototype.load = function(url, timeout) {
	this.clearTimer();
	if (typeof url != 'string') {
		throw new URIError('BAJL.PseudoDialogContentFrame.load: first argument must be a string (URL).');
	} else if (!this.frame) {
		this.url = '';
		this.doCallbackByName('onCloseRequested');
	} else {
		this.url = url;

		// load iframe content (both an image and an external html page).
		// 'this.setContent()' will be called from 'pseudoDialogContent.js'.
		if (BAJL.ua.isSafari && BAJL.ua.version < 522 /* Safari 2.0.x or ealier */) {
			this.node.src = this.url;
		} else {
			this.frame.location.replace(this.url);
		}

		// set timeout callback.
		if (timeout > 0) {
			this.timer = new BAJL.Timeout(function() { this.doCallbackByName('onLoadTimeout') }, timeout, this);
		}

		// when an external image is to be loaded.
		if (/\.(jpe?g|gif|png)$/i.test(this.url)) {
			var img = new Image();
			img.src = this.url;
			if (img.complete) {
				this.setContent(img);
			} else {
				img.onload = BAJL.Delegate(function() { this.setContent(img) }, this);
			}
		}
	}
}

/**
 * unload page in the iframe.
 */
BAJL.PseudoDialogContentFrame.prototype.unload = function() {
	this.hide();
	this.load('about:blank');
	this.content = null;
}

/**
 * process for iframe loaded;
 * this method is only called from below;
 *  - dialog content page's function    (it is in pseudoDialogContent.js)
 *  - BAJL.PseudoDialogContentFrame.load() (when the content is an image)
 * @param {Image|BAJL.PseudoDialogContent} content    an Image, or a 'BAJL.PseudoDialogContent' instance of the iframe content page
 */
BAJL.PseudoDialogContentFrame.prototype.setContent = function(content) {
	if (!content) {
		throw new TypeError('BAJL.PseudoDialog.setContent: first argument must be a BAJL.PseudoDialogContent instance.');
	} else {
		// preparations.
		this.clearTimer();
		this.content = content;

		// add callbacks to dialog content object.
		if (this.content.addCallback) {
			this.content.addCallback('onDefaultBtnClicked', function() { this.doCallbackByName('onCloseRequested') }, this); // temporary
			this.content.addCallback('onCloseBtnClicked'  , function() { this.doCallbackByName('onCloseRequested') }, this);
		}

		// show the iframe.
		this.show();
		
		new BAJL.Timeout(function() {
		this.doCallbackByName('onLoaded');
		}, 100, this);
	}
}

/**
 * does the iframe have the content? (the content is loaded)?
 * @return true if the iframe has the content (the content is loaded).
 * @type Boolean
 */
BAJL.PseudoDialogContentFrame.prototype.hasContent = function() {
	return Boolean(this.content);
}

/**
 * clear timeout timer.
 * @private
 */
BAJL.PseudoDialogContentFrame.prototype.clearTimer = function() {
	if (this.timer) {
		this.timer.clear();
	}
	this.timer = null;
}

/**
 * set size of the iframe (MacIE / Safari 1.x / Old Opera not work).
 * both width and height not given, iframe size is set to 'fit' as content page automatically.
 * @param {Number} width     new width  (px)
 * @param {Number} height    new height (px)
 */
BAJL.PseudoDialogContentFrame.prototype.adjustSize = function(width, height) {
	if (!this.node)    return;
	if (BAJL.ua.isIE && BAJL.ua.isMac) return;
	if (BAJL.ua.isSafari && BAJL.ua.version <= 100) return;
	if (isNaN(width) || isNaN(height)) {
		this.adjustSize(1, 1);
		var maxSize = BAJL.settings.PseudoDialog.contentFrame.maxSize;
		var geom;
		if (this.content.getGeometry) {
			geom = this.content.getGeometry();
		} else {
			geom = { pageW : this.content.width, pageH : this.content.height };
		}
		width  = (maxSize.W > 0) ? Math.min(geom.pageW, maxSize.W) : geom.pageW;
		height = (maxSize.H > 0) ? Math.min(geom.pageH, maxSize.H) : geom.pageH;
	}
	this.node.width  = this.node.style.width  = width  + 'px';
	this.node.height = this.node.style.height = height + 'px';
}

/**
 * set focus to default button node in the iframe.
 */
BAJL.PseudoDialogContentFrame.prototype.setDefaultFocus = function() {
	if (this.content && this.content.setDefaultFocus) {
		this.content.setDefaultFocus();
	}
}

/**
 * process callback.
 * @param {String} name    callback name (preferred to start with 'on')
 * @private
 */
BAJL.PseudoDialogContentFrame.prototype.doCallbackByName = function(name) {
	this.doCallback(name, this.url);
}



/* --------------- Class : BAJL.ClickShield --------------- */
/**
 * provides 'click shield'; as the backdrop of the pseudo dialog (use this as single object!).
 * @class click shield
 * @extends BAJL.Balloon
 * @constructor
 */
BAJL.ClickShield = function() {
	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.ClickShield.prototype = new BAJL.Balloon;

/** @private */
BAJL.ClickShield.prototype.initSuper = BAJL.ClickShield.prototype.init;

/**
 * initialize, setup event handler.
 * @private
 */
BAJL.ClickShield.prototype.init = function() {
	this.initSuper({ id : 'BAJLClickShield' });
	this.moveTo(0, 0);
	this.hide();
	$(this.node).click (BAJL.Delegate(this.eventPreventer, this));
	$(window   ).resize(BAJL.Delegate(this.adjustSize    , this));

	if (typeof BAJL.FontSizeObserver != 'undefined') {
		BAJL.Singleton(BAJL.FontSizeObserver).addCallback('onChange', this.adjustSize, this);
	};
}

/**
 * preventer body (event handler).
 * @type {Event} e    event object
 * @private
 */
BAJL.ClickShield.prototype.eventPreventer = function(e) {
	if (this.active) {
		this.doCallback('onPrevented');
	}
}

/**
 * adjust shield size.
 */
BAJL.ClickShield.prototype.adjustSize = function() {
	if (this.active) {
		var geom   = BAJL.GetGeometry();
		var width  = geom.pageW;
		var height = Math.max(geom.pageH, geom.windowH) + ((BAJL.ua.isIE && BAJL.ua.version < 7) ? -4 : 0);
		this.resizeTo(width, height);
	}
}

/**
 * enable (visible) the shield.
 */
BAJL.ClickShield.prototype.enable = function() {
	if (!this.active) {
		this.show();
		this.adjustSize();
		$(this.node).css('opacity', '0').fadeTo(
			  BAJL.settings.PseudoDialog.effect.shieldFadeDuration
			, BAJL.settings.PseudoDialog.effect.shieldMaxOpacity
			, BAJL.Delegate(_postProcess, this)
		);
	}

	function _postProcess() {
		this.doCallback('onEnabled');
	}
}

/**
 * disable (hide) the shield.
 */
BAJL.ClickShield.prototype.disable = function() {
	if (this.active) {
		$(this.node).fadeOut(
			  BAJL.settings.PseudoDialog.effect.shieldFadeDuration
			, BAJL.Delegate(_postProcess, this)
		);
	}

	function _postProcess() {
		this.hide();
		this.resizeTo(0, 0);
		this.doCallback('onDisabled');
	}
}



/* --------------- Class : BAJL.TabFocusShield --------------- */
/**
 * provides 'tab focus shield' (use this as single object!).
 * @class tab focus shield
 * @extends BAJL.Observable
 * @constructor
 */
/*!
 * arguments of callback function 'onPrevented()' :
 *     {Void}
 */
BAJL.TabFocusShield = function() {
	/** flag of activity of shield.
	    @type Boolean
	    @private */
	this.active         = false;
	/** elements isolated from tabfocus preventer.
	    @type jQuery
	    @private */
	this.isolations     = null;
	/** expression of focus-acceptable elements.
	    @type String
	    @private
	    @constant */
	this.focusablesExpr = 'a, area, input, textarea, select, button';

	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.TabFocusShield.prototype = new BAJL.Observable;

/**
 * initialize, setup event handler.
 * @private
 */
BAJL.TabFocusShield.prototype.init = function() {
	$(this.focusablesExpr).eq(0)
		.focus(BAJL.Delegate(function(e) {
			if (typeof $(e.currentTarget).data('BJL.TabFocusShield.isFocused') != 'boolean') {
				this.eventPreventer();
			}
		}, this));
}

/**
 * find and isolate focus-acceptable elements in specified element node.
 * @param {Element|jQuery|String} node    base element node to find
 */
BAJL.TabFocusShield.prototype.setIsolation = function(node) {
	var $node = $(node);
	
	if (!$node.BAJL_HasElement()) {
		throw new ReferenceError('BAJL.TabFocusShield.setIsolation: base node to find isolations is not found.');
	} else {
		this.isolations = $node.find(this.focusablesExpr)
			.focus(BAJL.Delegate(function(e) {
				$(e.currentTarget).data('BJL.TabFocusShield.isFocused', true);
			}, this))
			.blur (BAJL.Delegate(function(e) {
				$(e.currentTarget).data('BJL.TabFocusShield.isFocused', false);
				new BAJL.Timeout(function() {
					if (this.isolations.filter(function() { return $(this).data('BJL.TabFocusShield.isFocused') }).length == 0) {
						this.eventPreventer();
					}
				}, 1, this);
			}, this));
	}
}

/**
 * preventer body.
 * @private
 */
BAJL.TabFocusShield.prototype.eventPreventer = function() {
	if (this.active) {
		this.doCallback('onPrevented');
	}
}

/**
 * enable the shield.
 */
BAJL.TabFocusShield.prototype.enable = function() {
	if (!this.active) {
		this.active = true;
	}
}

/**
 * disable the shield.
 */
BAJL.TabFocusShield.prototype.disable = function() {
	if (this.active) {
		this.active = false;
	}
}



/* --------------- Class : BAJL.ScrollShield --------------- */
/**
 * provides 'scroll shield' (use this as single object!).
 * @class scroll shield
 * @extends BAJL.Observable
 * @constructor
 */
BAJL.ScrollShield = function() {
	/** flag of activity of the shield.
	    @type Boolean
	    @private */
	this.active      = false;
	/** scroll position when this was become effectively - associative array { X:number, Y:number }.
	    @type Object
	    @private */
	this.scrollPos   = { X : 0, Y : 0 };
	/** original style storage.
	    @type Array
	    @private */
	this.storedStyle = [];

	if (BAJL.env.isDOMReady) {
		this.init();
	}
}

BAJL.ScrollShield.prototype = new BAJL.Observable;

/**
 * initialize, setup event handler.
 * @private
 */
BAJL.ScrollShield.prototype.init = function() {
	$(window  ).scroll    (BAJL.Delegate(this.eventPreventer, this));
	$(document).mousewheel(BAJL.Delegate(this.eventPreventer, this));
}

/**
 * preventer body (event handler).
 * @type {Event} e    event object
 * @private
 */
BAJL.ScrollShield.prototype.eventPreventer = function(e) {
	if (this.active) {
		e.preventDefault();
		if (e.type == 'scroll') {
			window.scrollTo(this.scrollPos.X, this.scrollPos.Y);
			this.doCallback('onPrevented');
		}
	}
}

/**
 * enable the shield.
 */
BAJL.ScrollShield.prototype.enable = function() {
	if (!this.active) {
		this.active = true;

		var geom = BAJL.GetGeometry();
		var node;

		// style for 'body' (or 'html' in Safari)
		node = (!BAJL.ua.isSafari) ? document.body : document.documentElement;
		this.storedStyle.push({
			  node  : node
			, style : {
			  	  borderRightWidth : $(node).css('borderRightWidth')
			  	, borderRightStyle : $(node).css('borderRightStyle')
			  	, borderRightColor : $(node).css('borderRightColor')
			}
		});

		if (geom.windowH < geom.pageH || BAJL.ua.isIE) {
			$(node).css('borderRight', geom.scrollBar + 'px solid white');
		}

		// style for 'html'
		node = document.documentElement;
		this.storedStyle.push({
			  node  : node
			, style : {
			  	  overflow  : $(node).css('overflow' )
			  	, overflowX : $(node).css('overflowX')
			  	, overflowY : $(node).css('overflowY')
			}
		});
		$(node).css({ 'overflow' : 'hidden', 'overflowX' : 'hidden', 'overflowY' : 'hidden' });

		this.scrollPos = { X : geom.scrollX, Y : geom.scrollY };
	}
}

/**
 * disable the shield.
 */
BAJL.ScrollShield.prototype.disable = function() {
	if (this.active) {
		this.active = false;
		this.storedStyle.forEach(function(stored) { $(stored.node).css(stored.style) });

		if (BAJL.ua.isSafari) {
			var geom = BAJL.GetGeometry();
			var node = document.documentElement;
			if (geom.windowW < geom.pageW && $(node).css('overflowX') == 'visible') $(node).css('overflowX', 'scroll');
			if (geom.windowH < geom.pageH && $(node).css('overflowY') == 'visible') $(node).css('overflowY', 'scroll');
		}

		window.scrollTo(this.scrollPos.X, this.scrollPos.Y);
	}
}



/* -------------------- AutoSetup -------------------- */

$(function() {
	if (!BAJL.settings.PseudoDialog.autoSetup.enabled) return;

	/** pseudo dialog instance (single object).
	    @type BAJL.PseudoDialog
	    @inner */
	var pseudoDialog    = BAJL.Singleton(BAJL.PseudoDialog);

	/** clicked node to open the dialog.
	    @type Element
	    @inner */
	var lastFocusAnchor = null;

	/** source node for dialog content.
	    @type Element
	    @inner */
	var contentSource   = null;
	
	/**
	 * validate the clicked anchor whether it should open the dialog.
	 * @param {Element|jQuery|String} anchor    anchor element (a, area)
	 * @return true if it should open the dialog
	 * @type Boolean
	 * @inner
	 */
	function _validateAnchor(anchor) {
		return ($(anchor).attr('target') == BAJL.settings.PseudoDialog.autoSetup.linkTarget);
	}

	/**
	 * return an element which is linked from clicked anchor;
	 * @param {Element|jQuery|String} node    anchor element which clicked (a, area)
	 * @return link target element
	 * @type jQuery
	 * @inner
	 */
	function _getLinkTarget(anchor) {
		var anchor = $(anchor).get(0);
		if (anchor) {
			var href = anchor.getAttribute('href') || '';
			var hash = anchor.hash;
			var path = href.replace(hash, '');
			if (hash && (!path || path == location.href.split('#')[0])) {
				return $(hash);
			}
		}
		return $([]);
	}

	/**
	 * open dialog from clicked anchor.
	 * @param {Event} e         event object.
	 * @event
	 * @inner
	 */
	function _openDialog(e) {
		if (!_validateAnchor(this)) return;

		e.preventDefault();

		if (pseudoDialog.isActive()) return;

		var $anchor = $(this);
		var $target = _getLinkTarget($anchor);

		if ($target.BAJL_HasElement()) {
			pseudoDialog.update($target.contents());
			pseudoDialog.open();
			contentSource = $target.get(0);
		} else {
			pseudoDialog.openURL($anchor.attr('href'));
			contentSource = null;
		}
		pseudoDialog.setCloseCallback(_closePostProcess);
		lastFocusAnchor = $anchor.get(0);
	}


	/**
	 * post process for closed dialog.
	 * @private
	 */
	function _closePostProcess() {
		$(lastFocusAnchor).focus();
		$(contentSource).append(pseudoDialog.getContent());
	}

	$('a, area').live('click', _openDialog);
});



/* -------------------- for JSDoc toolkit output -------------------- */
/**
 * callback functions for {@link BAJL.PseudoDialog}
 * @name BAJL.PseudoDialog.callback
 * @namespace callback functions for {@link BAJL.PseudoDialog}
 */
/**
 * a callback for when the dialog is shown - inherited from BAJL.Balloon
 * @name BAJL.PseudoDialog.callback.onShow
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog is moved position - inherited from BAJL.Balloon
 * @name BAJL.PseudoDialog.callback.onMove
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog balloon's size is changed - inherited from BAJL.Balloon
 * @name BAJL.PseudoDialog.callback.onResize
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog is hidden - inherited from BAJL.Balloon
 * @name BAJL.PseudoDialog.callback.onHide
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog is opened completely
 * @name BAJL.PseudoDialog.callback.onOpen
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog content is changed.
 * @name BAJL.PseudoDialog.callback.onContentChange
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */
/**
 * a callback for when the dialog is closed completely
 * @name BAJL.PseudoDialog.callback.onClose
 * @function
 * @param {BAJL.Balloon.geometry} geom    an associative array of balloon geometry
 */


/**
 * callback functions for {@link BAJL.PseudoDialogContentFrame}
 * @name BAJL.PseudoDialogContentFrame.callback
 * @namespace callback functions for {@link BAJL.PseudoDialogContentFrame}
 */
/**
 * a callback for when the iframe completed loading content 
 * @name BAJL.PseudoDialogContentFrame.callback.onLoaded
 * @function
 * @param {String} url    url of the page currently loaded in the iframe
 */
/**
 * a callback for when the loading of iframe is timeout.
 * @name BAJL.PseudoDialogContentFrame.callback.onLoadTimeout
 * @function
 * @param {String} url    url of the page currently loaded in the iframe
 */
/**
 * a callback for when it requested to close the dialog.
 * @name BAJL.PseudoDialogContentFrame.callback.onCloseRequested
 * @function
 * @param {String} url    url of the page currently loaded in the iframe
 */

/**
 * callback functions for {@link BAJL.ClickShield}
 * @name BAJL.ClickShield.callback
 * @namespace callback functions for {@link BAJL.ClickShield}
 */
/**
 * a callback for when the click shield is shown completely
 * @name BAJL.ClickShield.callback.onEnabled
 * @function
 */
/**
 * a callback for when the click shield is hidden completely
 * @name BAJL.ClickShield.callback.onDisabled
 * @function
 */
/**
 * a callback for when the shield prevents user action
 * @name BAJL.ClickShield.callback.onPrevented
 * @function
 */

/**
 * callback functions for {@link BAJL.TabFocusShield}
 * @name BAJL.TabFocusShield.callback
 * @namespace callback functions for {@link BAJL.TabFocusShield}
 */
/**
 * a callback for when the shield prevents user action
 * @name BAJL.TabFocusShield.callback.onPrevented
 * @function
 */

/**
 * callback functions for {@link BAJL.ScrollShield}
 * @name BAJL.ScrollShield.callback
 * @namespace callback functions for {@link BAJL.ScrollShield}
 */
/**
 * a callback for when the shield prevents user action
 * @name BAJL.ScrollShield.callback.onPrevented
 * @function
 */



})(jQuery);
