/**
 * class that implements div-decorator functionality
 usage:
<script>
var loaderObj = new DivDecorator();
loaderObj.setDivWidth(200);
loaderObj.setDivHeight(100);
loaderObj.setDivLeft(400);
loaderObj.setDivTop(10);
loaderObj.setDivBorder('solid 2px blue');
</script>

...
<td><input type="button" onClick="loaderObj.onStartAction();" value="Start Action"></td>
<td><input type="button" onClick="loaderObj.onFinishAction();" value="Finish Action"></td>

 */
var DivDecorator = Window.extend({

	/**
	 * constructor
	 * initialize parent class (Window) 
	 * set up parameters for overriding part of page DIV element
	 *
	 * @param width
	 * @param height
	 * @param left
	 * @param top
	 * @param border
	 *
	 * @return void
	 */
	construct: function(width, height, left, top, border) {
		this.initBrowserDetection();
		this.setDivWidth(width);
		this.setDivHeight(height);
		this.setDivLeft(left);
		this.setDivTop(top);
		this.setDivBorder(border);
		this.elMgr = new ElementManager();
		this.imageSrc = 'empty.gif';
	},
	
	/**
	 * set image source 
	 *
	 * @param src
	 *
	 * @return void
	 */
	setImgSrc: function(src)  {
		this.imageSrc = src;
	},

	/**
	 * set alternate text for image
	 *
	 * @param text
	 *
	 * @return void
	 */	
	setImgAltText: function(text) {
		this.imgAltText = text;
	},

	/**
	 * set div width
	 *
	 * @param width
	 *
	 * @return void
	 */
	setDivWidth: function(width) {
		this.divWidth = width;
	},
	
	/**
	 * set div height
	 *
	 * @param height
	 *
	 * @return void
	 */	setDivHeight: function (height) {
		this.divHeight = height;
	},
	
	/**
	 * set div left
	 *
	 * @param left
	 *
	 * @return void
	 */	setDivLeft: function(left) {
		this.divLeft = left;
	},

	/**
	 * set div top
	 *
	 * @param top
	 *
	 * @return void
	 */	setDivTop: function(top) {
		this.divTop = top;
	},

	/**
	 * set div border
	 *
	 * @param border
	 *
	 * @return void
	 */
	setDivBorder: function(border) {
		this.divBorder = border;
	},

	/**
	 * public function - initialize start of action
	 * appearing div with setuped parameters
	 *
	 * return void
	 */
	onStartAction: function() {
		if (document.getElementById("decoreDiv")) { return; }
		if (typeof(this.parent) != 'object') { this.setParent(); }
		this.processStartAction();
	},
	
	/**
	 * signalize that action is finished and DIV should be hiding (removing)
	 *
	 * @return void
	 */
	onFinishAction: function() {
		this.rollBack();
	},

	/**
	 * set parent element to the DIV
	 *
	 * @param parent (element)
	 *
	 * @return void
	 */
	setParent: function(parent) {
		if (parent) {
			this.parent = parent;
		} else {
			this.parent = document.getElementsByTagName('BODY')[0];
		}
	},
	
/*----------------------------------------- private section --------------------------------------------------*/


	/**
	 * start overriding page by DIV
	 *
	 * @return void
	 */
	processStartAction: function() {
		if (this.isIE()) {
			eventMgr = new EventManager();
//			this.startScrollPosY = eventMgr.getScrollYPos();
//			this.startScrollBottom = eventMgr.getScrollBottom();
		}
		this.decorateDiv = this.elMgr.createDiv("decoreDiv", '');
		this.elMgr.setElementParams(this.decorateDiv, this.divLeft, this.divTop, this.divWidth, this.divHeight);
		this.elMgr.setBorder(this.decorateDiv, this.divBorder);
		this.parent.appendChild(this.decorateDiv);
		if (this.isIE() || this.isOpera()) {
			var img = this.elMgr.createImg(this.imageSrc, 'alternate text');
			this.elMgr.setElementParams(img, this.divLeft, this.divTop, this.divWidth, this.divHeight);
			this.decorateDiv.appendChild(img);

//			this.hiddenDiv = this.elMgr.createDiv("hiddenDiv", 'none');
//			this.parent.appendChild(this.hiddenDiv);
			if (this.isIE()) {
				this.doChangeElements();
//				this.finishScrollPosY = eventMgr.getScrollYPos();
//				this.finishScrollBottom = eventMgr.getScrollBottom();
//				if (this.finishScrollPosY == this.startScrollPosY) {
//					eventMgr.scrollTo(this.startScrollPosY + (this.finishScrollBottom - this.startScrollBottom));
//				}
			}
		}
	},

	/**
	 * roll back all operations with DOM model
	 *
	 * @return void
	 */
	rollBack: function() {
		this.elMgr.removeElement(this.decorateDiv);
		if (this.isIE()) {
			this.undoChangeElements();
			this.elMgr.removeElement(this.hiddenDiv);
		}
	},
	
	/**
	 * change elements <select> to <input> for IE
	 *
	 * @return void
	 */
	doChangeElements: function() {
		this.selectAllCandidates();
		this.applyChanges();
	},
	
	/**
	 * roll back elements changing <input> to <select> for IE
	 *
	 * @return void
	 */
	undoChangeElements: function() {
		if (typeof(this.changed) == 'undefined') { return; }
		for (i = 0; i < this.changed.length; i++) {
			this.show(this.changed[i]);
		}
	},

	/**
	 * select all elements (<select>) from document
	 *
	 * @return void
	 */
	selectAllCandidates: function() {
		var elements = document.getElementsByTagName('select');
		this.potencialChange = new Array();
		for (i = 0; i < elements.length; i++) {
			var parent  = elements[i].parentNode;
			var pair = new Array(elements[i], parent);
			this.potencialChange[i] = pair;
		}
	},
	
	/**
	 * apply changes for overriding by DIV <select> elements
	 *
	 * @return void
	 */
	applyChanges: function() {
		this.changed = new Array();
		var index = 0;
		for (i = 0; i < this.potencialChange.length; i++)  {
			var viewedPair = this.potencialChange[i];
			var LM = new LayerManager(viewedPair[0], this.decorateDiv);
			if (LM.isElementsOverriding()) {
				this.changed[index] = new Array(this.hide(viewedPair[0]), viewedPair[1]);
				index++;
			}
		}
	},
	
	/**
	 * do hide element
	 * remove it displaying and create <input> on it place
	 *
	 * @param element
	 *
	 * @return created input element
	 */
	hide: function(element) {
		var input = this.createInput(element);
		element.parentNode.insertBefore(input, element);
		element.style.display = 'none';

		return element;
	},
	
	/**
	 * make input clone (mean as parameters) from <select> element
	 *
	 * @param element
	 *
	 * @return <input> element
	 */
	createInput: function(element) {
		var input = document.createElement('INPUT');
		id = element.id + 'test_id';
		input.id = id;

		input.style.width  = element.style.width;
		input.style.height = element.offsetHeight;

		input.style.height = (parseInt(input.style.height) - 2) + 'px';
		input.style.margin = '0px'; 
		input.style.padding = '0px';
		try {
			input.value = element.options[element.selected].value;
		} catch(e) {
			try {
				input.value = element.options[element.selectedIndex].value;
			} catch(e) {
				input.value = element.options[0].value;
			}
		}
	
		return input;	
	},

	/**
	 * show elements (do display hided <select>) and remove created <input>
	 *
	 * @param pair - array of elements - array(element, element.parentNode)
	 */
	show: function(pair) {
//		pair[1].appendChild(pair[0]);
		pair[0].style.display = '';
		this.removeInput(pair[0]);
	},
	
	/**
	 * do remove created <input>
	 *
	 * @param element - element for which this <input> was created
	 *
	 * @return void
	 */
	removeInput: function(element) {
		var input = document.getElementById(element.id + 'test_id');
		if (input) { 
			this.elMgr.removeElement(input);
		}
	}
});


/**
 * class - loading div-decorator
 *
 * apply div-decorator to whole document
 */
var DivLoading = DivDecorator.extend({ 


	/**
	 * start action initializator
	 *
	 * @param objName - object name that implements this class
	 * @param attachedElementId - attached element id
	 * @param attachedBorder - attached element border properties
	 *
	 * @return void
	 */
	onStartAction: function(objName, attachedElementId, attachedBorder) {
		if (document.getElementById("decoreDiv")) { return; }

		this.attachedBorder = attachedBorder;
		this.initBrowserDetection();
		this.attachedElementId = attachedElementId;

		this.setWindowParams();
		this.applyDivFullScreen();
		
		if (typeof(this.parent) != 'object') { this.setParent(); }

		this.addScrollHandler(objName);

		this.processStartAction();
		this.decorateDiv.style.border = '';
		this.decorateDiv.className = 'loading';

		this.appendAttached();
	},
	
	/**
	 * set up timer for displaying 'loading...' text (dot appearing)
	 *
	 * @param timer - int (miliseconds)
	 *
	 * @return void
	 */
	setTimer: function(timer) {
		this.timer = timer;
	},

	
	/**
	 * apply div parameters for current usage (full document width and height)
	 *
	 * @return void
	 */
	applyDivFullScreen: function() {
		this.divLeft   = 0;
		this.divTop    = 0;
		this.divWidth  = this.getWinWidth();
		this.divHeight = this.getWinHeight();
	},
	
	/**
	 * finished div overriding functionality
	 * (make all roll back actioons)
	 *
	 * @return void
	 */
	onFinishAction: function() {
		if (!document.getElementById("decoreDiv")) { return; }
		this.removeAttached();
		this.removeScrollHandler();
		this.rollBack();
	},

	/**
	 * add scroll handler (add function to 'onscroll')
	 *
	 * @return void
	 */
	addScrollHandler: function(objName) {
		this.objName = objName;
		this.funcName = 'scroll';
		this.createScrollFunc();
		this.eventMgr = new EventManager("onscroll", this.funcName, objName);
		this.eventMgr.addEvent(this.parent);
	},

	/**
	 * do remove scroll handler
	 *
	 * @return void
	 */
	removeScrollHandler: function() {
		this.eventMgr.removeEvent(this.parent);
		this.removeScrollFunc();
	},
	
	/**
	 * create scroll function (for IE only)
	 * this function create JavaScript function in the document body
	 * created function contain this class implementor object wich call target to handle event function (actually this.scroll)
	 * it made for fixing event handling by IE (event should get only function-pointer, it can't contain 'this', etc)
	 *
	 * @param funcName - name of function that should be created in the DOM
	 *
	 * @return void
	 */
	createScrollFunc: function(funcName) {
		var script = document.createElement('SCRIPT');
		this.parent.appendChild(script);
		var textText = 'function ' + this.funcName + '() { ' + this.objName + '.scroll(); }';
		var text = document.createTextNode(textText);
		try {		
			script.appendChild(text);
		} catch(e) {
			script.text = textText;
		}
	},
	
	/**
	 * remove scroll function from DOM
	 *
	 * @return void
	 */
	removeScrollFunc: function() {
		var scripts = document.getElementsByTagName('SCRIPT');
		script = scripts[scripts.length-1];
		this.parent.removeChild(script);
	},
	
	/**
	 * function to handle scroll action
	 *
	 * @return void
	 */
	scroll: function() {
		this.removeAttached();
		this.appendAttached();
//		this.applyAttachedPosition();
	},
	
	/**
	 * set window params in the inner class functions
	 *
	 * @return void
	 */
	setWindowParams: function() {
	    if( typeof( window.innerWidth ) == 'number' ) {
	        //Non-IE
	        this.winWidth = window.innerWidth;
	        this.winHeight = window.innerHeight;
	    } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
	        //IE 6+ in 'standards compliant mode'
	        this.winWidth = document.documentElement.clientWidth;
	        this.winHeight = document.documentElement.clientHeight;
	    } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
	        //IE 4 compatible
	        this.winWidth = document.body.clientWidth;
	        this.winHeight = document.body.clientHeight;
	    }
	},

	/**
	 * set up attached elements parameters like center placement on the page
	 *
	 * @return void
	 */
	setAttachecdParams: function() {
		this.attachedWidth = 0;
		if (this.attachedElement.style.width) {
			this.attachedWidth = this.attachedElement.style.width/2;
		}
		if (!this.attachedWidth) {
			this.attachedElement.style.display 	= '';
			this.attachedWidth = this.attachedElement.offsetWidth;
			this.attachedElement.style.display 	= 'none';
		}
		
		this.attachedHeight = 0;
		if (this.attachedElement.style.height) {
			this.attachedHeight = this.attachedElement.style.height/2;
		}
		if (!this.attachedHeight) {
			this.attachedElement.style.display 	= '';
			this.attachedHeight = this.attachedElement.offsetHeight;
			this.attachedElement.style.display 	= 'none';
		}		
	},
	
	/**
	 * apply attached element right position (center of screen)
	 *
	 * @return void
	 */
	applyAttachedPosition: function() {
		this.setAttachecdParams();
		this.setWindowParams();
		this.attachedElement.style.top = this.eventMgr.getScrollYPos() + Math.round((this.winHeight - this.attachedHeight)/2);
		this.attachedElement.style.left = Math.round((this.winWidth - this.attachedWidth)/2);
		this.attachedElement.style.display 	= '';
		this.attachedElement.style.border 	= this.attachedBorder;
		this.attachedElement.style.textAlign = 'center';
	},
	
	/**
	 * append attached element to decorate (overrriding) DIV
	 * (apply position and make append)
	 *
	 * @return void
	 */
	appendAttached: function() {
		this.attachedElement = document.getElementById(this.attachedElementId);
		if ((typeof(this.attachedElement)!='object')) { return; }

		this.applyAttachedPosition();
//		this.decorateDiv.appendChild(this.attachedElement);
	},
	
	/**
	 * return back to document.body DOM place attached element
	 *
	 * @return void
	 */
	removeAttached: function() {
		this.attachedElement = document.getElementById(this.attachedElementId);
		if ((typeof(this.attachedElement)!='object')) { return; }
		this.attachedElement.style.display = 'none';
		this.parent.appendChild(this.attachedElement);
	}
});

