/** 
* Base classes and functions for Framework controls.
*
* @import Functions.js
*/

/** Global page control for this page */
var Page = null;

/** Performs a postback */
function __doPostBack(form, controlId, param, causesValidation)
{
	if (!form) form = 0;
	if (!isObject(form)) form = document.forms[form];
	if (!form) return false;
	var formControl = Page.findControl(form.id);
	if (causesValidation && formControl) if (!formControl.validate()) return false;
	if (controlId) form.__EVENTTARGET.value = controlId;
	if (param) form.__EVENTPARAMETER.value = param;
	Page.submitting = true;
	form.submit();
	return true;
}

// Finds the specified target by searching the window and all children
function __findTarget(name, win) {
	if (!win) win = self.top;
	if (!win.frames) return null;
	if (win.frames[name]) return win.frames[name];
	for (var i = 0; i < win.frames.length; i++) {
		var frame = __findTarget(name, win.frames[i]);
		if (frame) return frame;
	}
	return null;
}

/** Initializes all the controls on the page */
function __initialize() 
{
	for (var i = 0; i < __CONTROLS.length; i++) {
		if (__CONTROLS[i].initialize) __CONTROLS[i].initialize();
	}
}

/** Base class for all controls */
with (Web_UI_Control = NewClass()) 
{
	// Construct this Control
	prototype.__construct = function() { this.events = {}; }
	
	// Get the validation value (default null)
	prototype.getValidationValue = function() { return null; };
	
	// Set this focus onto this control
	prototype.focus = function() { if (this.tag && this.tag.focus) this.tag.focus(); }

	// Loads the tag for this control if it exists
	prototype.load = function() {
		if (parent.load) parent.load.call(this);
		if (this.clientId && document.getElementById) {
			if (!this.tag) this.tag = document.getElementById(this.clientId);
			if (this.tag) for (var event in this.events) {
				if (!this.events[event].bind) continue;
				this.events[event].bind(this.tag);
			}
		}
	}

	// Loads the tag for this control if it exists
	prototype.unload = function() {
		if (parent.unload) parent.unload.call(this);
		if (this.events) {
			for (var event in this.events) {
				if (this.events[event].unbindAll) this.events[event].unbindAll();
			}
		}
		delete this.events;
		delete this.tag;
		delete this.parent;
	}

	// Loads the tag for this control if it exists
	prototype.exists = function() { return (!this.tag) ? false : true; }

	// Set and clear error on this control
	prototype.setError = function() { }
	prototype.clearError = function() { }
}

/** The page object */
with (Web_UI_Page = NewClass()) 
{
	inherits(Web_UI_Control);
	
	// Construct a new page object
	prototype.__construct = function () {
		parent.__construct.call(this);
		this.controlsByName = {};
		this.events.onLoad = new Web_UI_AsyncEvent('onload');
		this.events.onUnload = new Web_UI_Event('onunload');
		this.events.onBeforeUnload = new Web_UI_Event('onbeforeunload');		
		this.events.onResize = new Web_UI_AsyncEvent('onresize');
		this.events.onMouseOut = new Web_UI_Event();
	}
	
	// Initializes the page and map controls by name
	prototype.initialize = function () {
		Page = this;
		this.events.onLoad.bind(window).add(this.load.bind(this));
		this.events.onUnload.bind(window).add(this.unload.bind(this));
		this.events.onBeforeUnload.bind(window).add(this.beforeUnload.bind(this));
		this.events.onResize.bind(window);
		this.events.onMouseOut.add(this.onMouseOut.bind(this));
		for (var i = 0; i < __CONTROLS.length; i++) {
			if (__CONTROLS[i].clientId && __CONTROLS[i] !== this) {
				this.controlsByName[__CONTROLS[i].clientId] = __CONTROLS[i];
			}
		}
		// Get the post form and set the property
		if (this.postFormId) this.postForm = this.findControl(this.postFormId);
	}

	// Called when the page loaded; executes the load method on all controls
	prototype.load = function() {
		for (var i = 0; i < __CONTROLS.length; i++) {
			if (__CONTROLS[i].load && __CONTROLS[i] !== this) __CONTROLS[i].load();
		}
		this.render();	
		this.events.onResize.invoke();
	}

	// Called after all the controls have been loaded to perform rendering
	prototype.render = function() {
		for (var i = 0; i < __CONTROLS.length; i++) {
			if (__CONTROLS[i].render && __CONTROLS[i] !== this) __CONTROLS[i].render();
		}		
	}

	// Called when the page unloaded; executes the unload method on all controls
	prototype.unload = function() {
		for (var i = 0; i < __CONTROLS.length; i++) {
			if (__CONTROLS[i].unload &&__CONTROLS[i] !== this) __CONTROLS[i].unload();
		}
		if (parent.unload) parent.unload.call(this);
		delete Page;
		delete this.controlsByName;
		delete this.postForm;
		var frames = document.getElementsByTagName('iframe');
		for (var i = 0; i < frames.length; i++) frames[i].onmouseover = null;
		delete __CONTROLS;
	}

	// Called when the page unloaded; executes the unload method on all controls
	prototype.beforeUnload = function() {
		if (this.submitting || !this.unloadMessage) return;
		for (var i = 0; i < __CONTROLS.length; i++) {
			if (__CONTROLS[i].changed) return this.unloadMessage;
		}
	}

	// Finds a control by name; creates HTML controls for HTML elements 
	prototype.findControl = function(clientId) {
		if (!clientId) return null;
		if (!this.controlsByName[clientId]) {
			var tag = document.getElementById(clientId);
			if (!tag || !window.Web_UI_HtmlControl) return null;
			var control = new Web_UI_HtmlControl();
			control.clientId = clientId;
			control.tag = tag;
			return this.addControl(control);
		}
		return this.controlsByName[clientId];
	}

	// Adds the control to the page
	prototype.addControl = function(control) {
		if (!control.clientId) return null;
		__CONTROLS.add(control);
		this.controlsByName[control.clientId] = control;
		if (control.initialize) control.initialize();
		if (control.load) control.load();
		if (control.render) control.render();
		return control;
	}

	// Returns the client height of the window
	prototype.clientHeight = function(all) {
		if (all && window.parent && window.parent.Page && window.parent != window) {
			return window.parent.Page.clientHeight(all);
		}
		if (self.innerHeight) return self.innerHeight;
		if (document.documentElement && document.documentElement.clientHeight) {
			return document.documentElement.clientHeight;
		}
		if (document.body) return document.body.clientHeight;
	}
	
	// Returns the client width of the window
	prototype.clientWidth = function(all) {
		if (all && window.parent && window.parent.Page && window.parent != window) {
			return window.parent.Page.clientWidth(all);
		}
		if (self.innerWidth) return self.innerWidth;
		if (document.documentElement && document.documentElement.clientWidth) {
			return document.documentElement.clientWidth;
		}
		if (document.body) return document.body.clientWidth;
	}	
	
	// Refreshes the page
	prototype.refresh = function() {	
		if (this.postForm) this.postForm.refresh();
		else window.location.reload(true);
	}

	// Mouse out handler -- invokes mouseout on parent pages
	prototype.onMouseOut = function() {
		if (!window.parent) return;
		if (!window.parent.Page) return;
		if (!window.parent.Page.events) return;
		if (window.parent == window) return;
		window.parent.Page.events.onMouseOut.invoke();
	}
}

/** A user interface event */
with (Web_UI_Event = NewClass()) 
{
	// Constructs a new event; takes name and arguments class
	prototype.__construct = function(name, eventArgsClass) {
		this.name = name;
		this.eventArgsClass = eventArgsClass;
		this._handlers = [];
		this._elements = [];
		this._events = [];
	}	

	// Adds function to event handler list
	prototype.add = function(func) { 
		for (var i = 0; i < this._events.length; i++) this._events[i].add(func);
		if (this._handlers.contains(func)) return this;
		if (this._handlers.length == 0) {
			for (var i = 0; i < this._elements.length; i++) this._bindElement(this._elements[i]);
		}
		this._handlers.add(func);
		return this;
	}

	// Adds function to event handler list
	prototype.push = function(func) { 
		for (var i = 0; i < this._events.length; i++) this._events[i].push(func);
		if (this._handlers.contains(func)) return this;
		if (this._handlers.length == 0) {
			for (var i = 0; i < this._elements.length; i++) this._bindElement(this._elements[i]);
		}
		this._handlers.insert(0, func);
		return this;
	}

	// Removes function from event handler list; unbinds if all handlers removed	
	prototype.remove = function(func) { 
		for (var i = 0; i < this._events.length; i++) this._events[i].remove(func);
		this._handlers.remove(func);
		if (this._handlers.length == 0) {
			for (var i = 0; i < this._elements.length; i++) this._unbindElement(this._elements[i]);
		}
		return this;
	}

	// Binds an event to this event
	prototype.bindToEvent = function(event) { 
		if (this._events.contains(event)) return this;
		for (var i = 0; i < this._handlers.length; i++) event.add(this._handlers[i]);
		this._events.add(event); 
		return this;
	};

	// Binds a document element to this event
	prototype.bind = function(element) { 
		if (this._elements.contains(element)) return this;
		this._elements.add(element);
		if (this._handlers.length > 0) this._bindElement(element);
		return this;
	};

	// Unbinds a document element from this event
	prototype.unbind = function(element) { 
		if (!this._elements.contains(element)) return this;
		this._elements.remove(element);
		if (this._handlers.length > 0) this._unbindElement(element);
		return this;
	};

	// Unbinds all document elements from this event
	prototype.unbindAll = function() { 
		for (var i = 0; i < this._elements.length; i++) {
			this._unbindElement(this._elements[i]);
		}
		this._elements.clear();
		this._handlers.clear();
		this._events.clear();
	}
		
	// Invokes all the event handlers returns a bool
	prototype.invoke = function() {
		var result = undefined;
		var handlers = this._handlers.copy();
		for (var i = 0; i < handlers.length; i++) {
			if (!isFunction(handlers[i])) continue;
			var handlerResult = handlers[i].apply({}, arguments);
			if (handlerResult === false) return false;
			else if (handlerResult === null) result = false;
			else result = handlerResult;
		}
		
		return result;
	}

	// Binds this event to the specified HTML element
	prototype._bindElement = function(element) {
		if (!this.name && !element) return this;
		element[this.name] = this.__handleEvent.bindAsEventListener(this);
		return this;
	}

	// Unbinds this event from the specified HTML element
	prototype._unbindElement = function(element) {
		if (!this.name || !element) return this;
		element[this.name] = null;
		return this;
	}

	// Handles the incoming HTML element event
	prototype.__handleEvent = function(e) {
		if (e && this.eventArgsClass) e = new this.eventArgsClass(e);
		return this.invoke(e);
	}	
}

/** An asynchronis user interface event */
with (Web_UI_AsyncEvent = NewClass()) 
{
	inherits(Web_UI_Event);

	// Invokes all the event handlers asynchonrisely; always returns true.
	prototype.invoke = function() {
		// Local copy of variables for threading
		var handlers = this._handlers;
		var args = Array.prototype.slice.call(arguments, 0);
		var i = 0; 
		var loop = null;
		
		// Main thread loop
		loop = function() {
			var handlerResult = null;
			if (isFunction(handlers[i])) handlerResult = handlers[i].apply({}, args); 
			i++; 
			if (handlerResult === false || i >= handlers.length) loop = null;
			else window.setTimeout(loop, 0);
		};
		loop();
		return true;
	}
}

/** Base EventArgs class */
with (Web_UI_EventArgs = NewClass()) 
{
	prototype.__construct = function(e) {
		this.timeStamp = new Date();
		this.eventType = e.type;
	}
}


/** EventArgs for mouse movement */
with (Web_UI_MouseEventArgs = NewClass()) 
{
	inherits(Web_UI_EventArgs);
	
	// Calculates the mouse position in a browser-independent way
	prototype.__construct = function(e) {
		parent.__construct.call(this, e);
		this.x = 0;
		this.y = 0;
		if (e.pageX || e.pageY) { this.x = e.PageX; this.y = e.PageY; }
		else if (e.clientX || e.clientY) {
			this.x = e.clientX + document.body.scrollLeft;
			this.y = e.clientY + document.body.scrollTop;
		}
		this.button = e.button;
	}	
}

/** EventArgs for key events */
with (Web_UI_KeyEventArgs = NewClass()) 
{
	inherits(Web_UI_EventArgs);
	
	// Calculates the key code in a browser-independent way
	prototype.__construct = function(e) {
		parent.__construct.call(this, e);
		if (e.keyCode) this.keyCode = e.keyCode;
		else if (e.which) this.keyCode = e.which;
		else this.keyCode = null;
		if (this.keyCode != null) this.keyChar = String.fromCharCode(this.keyCode);		
		this.altKey = this.altKey;
		this.ctrlKey = this.ctrlKey;
		this.shiftKey = this.shiftKey;
	}	
}

