Ext.namespace('Jsui');

/**
 * Static functions
 */
Jsui = {
	_topmost_zindex: 45000,
	
	/**
	 * Retrieves the size of the browser window in cross-browser compatible fashion
	 *
	 * @returns {object} .w - width, .h - height
	 */
	getWindowSize: function() {
		var w = null;
		var h = null;
		
		if (Ext.isIE)
		{
			if (Ext.isStrict)
			{
				w = document.documentElement.clientWidth;
				h = document.documentElement.clientHeight;
			}
			else
			{
				w = document.body.clientWidth;
				h = document.body.clientHeight;
			}
		}
		else
		{
			w = window.innerWidth;
			h = window.innerHeight;
		}
		
		return {w: w, h: h};
	},
	
	
	/**
	 * An alert message box
	 */
	messagebox: function(title, body, handler) {
		var win = new Jsui.Window({
			title: title,
			html: '',
			width: 300,
			autoHeight: true,
			html: '<center>' + body + '</center>'
		});
		
		Jsui._topmost_zindex++;
		win.el.setStyle('z-index', Jsui._topmost_zindex);
		
		var btn = new Jsui.Button({
			text: 'OK',
			appendTo: win.el.child('div.jsui-window-footer-container'),
			handler: function(e, t, o) {
				win.hide();
				if (handler)
				{
					handler.call();
				}
			}
		});
		
		win.show();
	},
	
	/**
	 * A Yes/No confirm window
	 */
	confirm: function(title, body, handler) {
		var win = new Jsui.Window({
			title: title,
			html: '',
			width: 400,
			autoHeight: true,
			html: '<center>' + body + '</center>'
		});
		
		Jsui._topmost_zindex++;
		win.el.setStyle('z-index', Jsui._topmost_zindex);
		
		var btn1 = new Jsui.Button({
			text: 'Yes',
			appendTo: win.el.child('div.jsui-window-footer-container'),
			handler: function(e, t, o) {
				win.hide();
				if (handler)
				{
					handler.call(win, 'yes');
				}
			}
		});
		
		var btn2 = new Jsui.Button({
			text: 'No',
			appendTo: win.el.child('div.jsui-window-footer-container'),
			handler: function(e, t, o) {
				win.hide();
				if (handler)
				{
					handler.call(win, 'no');
				}
			}
		});
		
		win.show();
	},
	
	
	/**
	 * Finds a form and wraps it in a Jsui.FormWrapper
	 */
	getForm: function(id) {
		var fe = Ext.get(id);
		var f = new Jsui.FormWrapper(fe);
		return f;
	},
	
	
	/**
	* Decodes JSON response data
	*/
	safeDecode: function(text) {
		var data = {};
		
		try {
			data = Ext.decode('(' + text + ')');
		}
		catch (e) {
		}
		
		return data;
	},
	
	
	
	/**
	* Returns the X and Y scroll offsets
	*
	* @return array 0 - X, 1 - Y
	*/
	getScrollOffsets: function() {
		var scr_x = 0, scr_y = 0;
		if(typeof(window.pageYOffset) == 'number' )
		{
			// Netscape-style
			scr_y = window.pageYOffset;
			scr_x = window.pageXOffset;
		}
		else if (document.body && (document.body.scrollLeft || document.body.scrollTop))
		{
			// DOM-style
			scr_y = document.body.scrollTop;
			scr_x = document.body.scrollLeft;
		}
		else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop))
		{
			// IE6-style
			scr_y = document.documentElement.scrollTop;
			scr_x = document.documentElement.scrollLeft;
		}
		
		return [scr_x, scr_y];
	},
	
	/**
	* Iterates a function over the properties of an object
	*
	* @param o {object} Object to iterate
	* @param fn {function} Function to call
	* 						prop {string} Property name
	* 						value {string} Property value
	* @param scope {object} Scope to execute the function in (defaults to object)
	*/
	iterate: function(o, fn, scope) {
		if (Ext.isArray(o)) {
			Ext.each(o, fn, scope);
			return;
		} else if (Ext.isObject(o)) {
			for (var prop in o) {
				if (o.hasOwnProperty(prop)) {
					if (fn.call(scope || o, prop, o[prop]) === false) {
						return;
					};
				}
			}
		}
	}
}


/**
 * Window / Form button
 */
Jsui.Button = Ext.extend(Ext.util.Observable, {
	handler: null,
	scope: null,
	initialConfig: {},
	id: null,
	size: 'small',
	
	el: null,
	//Extra class to add if browser is IE6
	ie6: '',
	
	constructor: function(config) {
		config = config || {};
		config.listeners = config.listeners | {};
		
		Ext.apply(this, config);
		Ext.apply(this.initialConfig, config);
		
		// Add the events
		this.addEvents({
			'click': true
		});
		
		if (this.id === null)
		{
			this.id = Ext.id(null, 'jsui-gen');
		}
		
		/*if (Ext.isIE6)
		{
			this.ie6 = 'ie6';
		}*/
		
		if (!Jsui.Button.ButtonTemplate)
		{
			/*Jsui.Button.ButtonTemplate = new Ext.Template(
				'<table id={id} class="jsui-btn-wrap jsui-btn" cellspacing="0" cellpadding="0" border="0" style="width: auto;">',
					'<tr>',
						'<td class="jsui-btn-left"> </td>',
						'<td class="jsui-btn-center">',
							'<em unselectable="on">',
								'<button type="button" class="jsui-button-text" style="width: {width_str};">{text}</button>',
							'</em>',
						'</td>',
						'<td class="jsui-btn-right"> </td>',
					'</tr>',
				'</table>'
			);*/
			Jsui.Button.ButtonTemplate = new Ext.Template(
				'<a href="#" onclick="return false;" id="{id}" class="jsui-btn {size}">{text}</a>'
			);
		}
		
		if (this.appendTo !== null)
		{
			this.el = Jsui.Button.ButtonTemplate.append(this.appendTo, this, true);
		}
		
		// Hook handler
		if (this.el)
		{
			//this.el.child('button').addListener('click', function(e, t, o) {
			this.el.addListener('click', function(e, t, o) {
				if(this.handler)
				{
					this.handler.call(this.scope || this, this, e);
				}
			}, this);
		}
	},
	
	renderTo: function(el) {
		Jsui.Button.ButtonTemplate.append(el, this, true);
	}
});

/**
 * A lightweight pop-up DIV window
 */
Jsui.Window = Ext.extend(Ext.util.Observable, {
	
	id: null,
	border: true,
	title: null,
	html: null,
	autoLoad: false,
	autoHeight: false,
	zIndex: null,
	
	initialConfig: {},
	
	_rendered: false,
	_markup: null,	// Generated window markup
	el: null,
	_updater: null,
	
	constructor: function(config) {
		config = config || {};
		config.listeners = config.listeners | {};
		
		Ext.apply(this, config);
		Ext.apply(this.initialConfig, config);
		
		// Add the events
		this.addEvents({
			'show': true,
			'hide': true,
			'render': true
		});
		
		if (this.id === null)
		{
			this.id = Ext.id(null, 'jsui-gen');
		}
		
		if (this.zIndex === null)
		{
			Jsui._topmost_zindex++;
			this.zIndex = Jsui._topmost_zindex;
		}
		
		this.render();
		
		this._container_el.addListener('DOMSubtreeModified', function(ev, el, o) {
			this.doLayout();
		}, this);
	},
	
	getUpdater: function() {
		return this._updater;
	},
	
	setContent: function(html) {
		this._container_el.dom.innerHTML = this.html;
		
		// Calculate the client container height
		this.doLayout();
		
		this.heightChanged();
	},
	
	render: function() {
		if (this._rendered)
		{
			// Already rendered
			return;
		}
		
		// Create the outside window container
		this.el = document.createElement('div');
		this.el.id = this.id || Ext.id();
		this.el = Ext.get(this.el);
		
		this.el.setStyle('z-index', this.zIndex);
		
		//this.el.addClass('jsui-hide');
		this.el.setVisibilityMode(Ext.Element.VISIBILITY);
		this.el.hide(false);
		
		Ext.getBody().insertFirst(this.el);
		this._rendered = true;
		
		this.el.addClass('jsui-window');
		
		
		// Create the inside window container
		this._inner_el = this.el.createChild({cls: 'jsui-window-inner'})
		this._inner_el.setWidth(this.width);
		
		if (this.autoHeight !== true)
		{
			this._inner_el.setHeight(this.height);
		}
		
		if (this.border)
		{
			this._inner_el.addClass('jsui-win-border');
		}
		
		// Create the window titlebar
		this._title_bar_el = this._inner_el.createChild({cls: 'jsui-win-title', html: '<div></div>' + this.title + ''});
		
		// Create the window toolcontainer
		this._tool_container_el = this._title_bar_el.first();
		this._tool_container_el.addClass('jsui-win-tools');
		
		// Add the close button
		this._toolbtn_close = this._tool_container_el.insertHtml('afterBegin', '<img src="' + Jsui.BLANK_IMAGE + '" class="jsui-tool-button jsui-tool-close" />', true);
		this._toolbtn_close.addListener('click', this.onCloseToolClick, this);
		
		// Create the titlebar icon
		this._title_bar_icon_el = this._title_bar_el.insertHtml('afterBegin', '<div class="jsui-win-icon"></div>', true);
		
		// Content container
		this._container_el = this._inner_el.createChild({cls: 'jsui-window-container'});
		
		// Buttons container
		this._footer_el = this._inner_el.createChild({cls: 'jsui-window-footer-container'});
		
		// Content updater
		this._updater = new Jsui.Updater({
			el: this._container_el,
			owner: this,
			onSuccess: function() {
				this.owner.doLayout();
			}
		});
		
		// Set content
		if (this.html !== null)
		{
			this._container_el.dom.innerHTML = this.html;
		}
		
		// Autoload content if needed
		if (this.autoLoad !== false)
		{
			this.getUpdater().update({
				url: this.autoLoad.url
			});
		}
		
		// Calculate the client container height
		this.doLayout();
	},
	
	doLayout: function() {
		if (this.autoHeight)
		{
			var win_size = Jsui.getWindowSize();
			this._inner_el.setHeight(Math.min(win_size.h, this._container_el.getHeight() + this._footer_el.getHeight() + this._container_el.getOffsetsTo(this._inner_el)[1] + 5));
		}
		else
		{
			this._container_el.setHeight(this._inner_el.getHeight() - this._container_el.getOffsetsTo(this._inner_el)[1] - 5);
		}
		
		this.heightChanged();
	},
	
	onCloseToolClick: function(e, el, o){
		this.hide();
	},
	
	hide: function() {
		this.el.hide(true);
	},
	
	show: function() {
		this.el.show(true);
	},
	
	heightChanged: function() {
		// Get browser window size
		var win_size = Jsui.getWindowSize();
		
		var scr = Jsui.getScrollOffsets();
		
		// Calculate the X coord
		this.x = this.initialConfig.x || Math.max(0, (win_size.w - this.width) / 2);
		
		// Calculate the Y coord
		var jw_h = this._inner_el.getHeight();
		this.y = this.initialConfig.y || Math.max(0, scr[1] + (win_size.h - jw_h) / 2);
		//alert(jw_h + ' / ' + win_size.h + ' / ' + this.y);
		
		// Set new position
		this.el.setXY([this.x, this.y]);
	}
});

/**
 * An Ext-style updater
 */
Jsui.Updater = Ext.extend(Ext.util.Observable, {
	el: null,
	url: null,
	owner: null,
	onSuccess: null,	// ()
	onFailure: null,	// ()
	onComplete: null,	// (success:bool)
	
	constructor: function(config) {
		config = config || {};
		config.listeners = config.listeners | {};
		
		Ext.apply(this, config);
		Ext.apply(this.initialConfig, config);
	},
	
	update: function(options) {
		Ext.apply(this, options);
		
		if ((options.el || this.el) === null)
		{
			throw new Error("An updater requires an associated element to update");
			return;
		}
		
		if ((options.url || this.url) === null)
		{
			throw new Error("No URL set for updater");
			return;
		}
		
		Ext.Ajax.request({
			url: options.url || this.url,
			success: function(response, opts) {
				this.el.dom.innerHTML = response.responseText;
				
				// Call custom handlers if set
				if (opts.onComplete)
				{
					opts.onComplete(true);
				}
				if (opts.onSuccess)
				{
					opts.onSuccess();
				}
			},
			failure: function(response, opts) {
				// Call custom handlers if set
				if (opts.onComplete)
				{
					opts.onComplete(false);
				}
				if (opts.onFailure)
				{
					opts.onFailure();
				}
				
				throw new Error("Error `" + response.statusText + "` encountered while updating from URL " + opts.url);
			},
			scope: this
		});
	}
});

/**
 * A form wrapper
 */
Jsui.FormWrapper = Ext.extend(Ext.Element, {
	url: null,
	method: 'POST',
	callback: null,
	success: null,
	failure: null,
	timeout: 20000,
	
	constructor: function(config) {
		Ext.applyIf(this, config);
	},
	
	configure: function(cfg) {
		Ext.apply(this, cfg);
	},
	
	getValues: function() {
		var dom = this.dom;
		//alert(dom);
		var serialize = Ext.Ajax.serializeForm(dom);
		//alert(serialize);
		var decode = Ext.urlDecode(serialize);
		//console.dir(decode);
		return decode;
	},
	
	submit: function(opt) {
		this.configure(opt);
		
		var req = {
			url: this.url,
			params: this.params,
			method: this.method,
			callback: this.callback,
			success: function(r, opt) {
				var data = Jsui.safeDecode(r.responseText);
				
				if (data.success === true)
				{
					this.success.call(this, r, opt, data);
				}
				else
				{
					this.failure.call(this, r, opt, data);
				}
			},
			failure: function(r, opt) {
				this.failure.call(this, r, opt, {});
			},
			scope: this,
			timeout: this.timeout,
			form: this
		};
		
		/*if (console)
		{
			console.log(req);
		}*/
		
		Ext.Ajax.request(req);
	}
});

/**
* Validation helper
*/
Jsui.Validation = {
	email_regex: /^[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}$/i,
	
	/**
	* Checks if the passed value is a valid email (covers most, but not all cases)
	*
	* @param email {string} An email string
	*
	* @returns {bool}
	*/
	validate_email: function(email) {
		var result = Jsui.Validation.email_regex.test(email);
		return result;
	}
};