/**
 * common.js
 * 這裡是用來存放一些跟Service沒有相關(相依性較低)的Common Function
 * 日後可以直接整個Copy至其它Service使用
 */
// Creating Global variable
var global = (function() {
	return this;
}());

function namespace(naming) {
	var parts = naming.split('.');
	var root = parts[0];
	var parent = global[root] || {
		naming : root
	};
	global[root] = parent;

	if (parts.length <= 1) {
		return parent;
	}

	for (var i = 1; i < parts.length; i += 1) {
		// create a property if it doesn't exist
		if (typeof parent[parts[i]] === 'undefined') {
			parent[parts[i]] = {
				naming : parts[i]
			};
		}
		parent = parent[parts[i]];
	}
	return parent;
}

// Creating Namespace
namespace('Message');
Message.system = {
	fail : 'Our system is temporarily busy, please try again later.',
	wait : 'Please wait...'
};

//Creating Namespace
namespace('Common');
Common = {
	isDebugMode : function() {
		return (global.console != null);
	}
};

/**
 * Module: Utils
 * 提供各種共同的 Utility Methods
 */
namespace('Common.Utils');
Common.Utils = (function()  {
	// static private methods

	return { // Public members.
		tostr : function(data, depth) {
			if (!Common.isDebugMode()) {
				return '[unknown]';
			}
			return $.dump(data, depth);
		},
		log : function(msg, doTransform) {
			if (Common.isDebugMode()) {
				if (doTransform && msg != null) {
					msg = $.dump(msg);
				}
				console.log(msg);
			}
		},
		info : function(msg) {
			if (Common.isDebugMode()) {
				console.info(msg);
			}
		},
		warn : function(msg) {
			if (Common.isDebugMode()) {
				console.warn(msg);
			}
		},
		error : function(msg) {
			if (Common.isDebugMode()) {
				console.error(msg);
			}
		},
		startsWith : function(mystr, str) {
			return (mystr.match('^' + str) == str);
		},
		endsWith : function(mystr, str) {
			return (mystr.match(str + '$') == str);
		},
		traceFunc : function(func, expanded) {
			if (!Common.isDebugMode()) {
				return '[unknown]';
			}

			var detail = {};
			if (func != null && $.isFunction(func)) {
				var name = /\W*function\s+([\w\$]+)\(/.exec(func);
				if (!name) {
					detail.name = 'Anonymous';
				} else {
					detail.name = name[1];
				}
				detail.caller = func.caller;

				if (expanded) {
					detail.code = func.toString();
				} else {
					var code = func.toString();
					var limitLength = 100;
					if (code.length > limitLength) {
						code = code.substring(0, limitLength) + '...';
					}
					detail.code = code;
				}
			} else {
				detail.name = 'It\'s not function';
			}

			return $.dump(detail);
		},
		random: function(max) {
			return Math.round(Math.random() * (max - 1));
		},
		/**
		 *  round a number to a specified number of decimal places
		 */
		roundNumber: function(number, decimals) {
			decimals = decimals || 0;
			// the first way to round a number
			var newnumber = Math.round(number * Math.pow(10, decimals))
					/ Math.pow(10, decimals);
			// the second way to round a number:
			// var newnumber = new Number(number+'').toFixed(parseInt(decimals));

			var roundedNumber = parseFloat(newnumber);

			return roundedNumber;
		},
		trim : function(str) {
			// Please refer to: http://blog.stevenlevithan.com/archives/faster-trim-javascript
			return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
		},
		fastTrim : function(str) {
			// Please refer to: http://blog.stevenlevithan.com/archives/faster-trim-javascript
			str = str.replace(/^\s+/, '');
			for (var i = str.length - 1; i >= 0; i -= 1) {
				if (/\S/.test(str.charAt(i))) {
					str = str.substring(0, i + 1);
					break;
				}
			}
			return str;
		},
		isIE67 : function() { // check the current browser is IE6 or IE7?
			return ($.browser.msie && document.documentMode < 8);
		},
		isIE: function() {
			return $.browser.msie;
		},
		isFirefox: function() {
			return $.browser.mozilla;
		},
		isChrome: function() {
			var ua = navigator.userAgent.toLowerCase();
			return (ua.indexOf('chrome') >= 0);
		},
		isSafari: function() {
			var ua = navigator.userAgent.toLowerCase();
			return ($.browser.safari || ua.indexOf('safari') >= 0 ) && (ua.indexOf('chrome') == -1);
		},
		isOpera: function() {
			var ua = navigator.userAgent.toLowerCase();
			return ($.browser.opera || ua.indexOf('opera') >= 0 );
		},
		isObject : function(data) {
  			return (data && typeof data === 'object');
		},
		getScreenWidth: function() {
			return document.documentElement.clientWidth;
		},
		getScreenHeight: function() {
			return document.documentElement.clientHeight;
		},
		getScrollLeft: function () {
			var sl = window.pageXOffset ||
				document.body.scrollLeft ||
				document.documentElement.scrollLeft;

			return sl ? sl : 0;
		},
		getScrollTop: function() {
			var st = window.pageYOffset ||
				document.body.scrollTop ||
				document.documentElement.scrollTop;

			return st ? st : 0;
		},
		getMessage : function(key, params) {
			var fullKey = 'Message.' + key;
			try {
				var message = eval(fullKey);
				return this.replaceMessage(message, params);
			} catch (e) {
				this.warn('?' + fullKey);
				return '?' + fullKey;
			}
		},
		replaceMessage: function(message, params) {
			if (this.isObject(params)) {
				for (var i = 0, max = params.length; i < max; i += 1) {
					if (params[i] != null) {
						var replaceStr = '{' + i + '}';
						while (message.indexOf(replaceStr) != -1) {
							message = message.replace(replaceStr, params[i]);
						}
					}
				}
			}
			return message;
		},
		showMessage : function(key, lang) {
			var fullKey = key + '_' + lang;
			try {
				var message = eval(fullKey);
				alert(message);
			} catch (e) {
				alert('?' + fullKey);
			}
		},
		loadCaptcha : function() {
			var now = new Date();
			var captchaImage = document.getElementById('captchaImage');
			if (captchaImage == null) {
				captchaImage = new Image();
				captchaImage.id = 'captchaImage';
				captchaImage.name = 'captchaImage';
				captchaImage.style.cursor = 'pointer';
				$('#randomCaptcha').prepend(captchaImage);
				$(captchaImage).click(Common.Utils.loadCaptcha);
			}

			window.setTimeout(function() {
				var captchaImage = document.getElementById('captchaImage');
				captchaImage.src = '/captcha/secure.captcha?rand=' + now.getTime();
				Common.Utils.log('Generate new CAPTCHA image at ' + now.getTime());
			}, 100);
		},
		/**
		 * show different format based on the assigned pattern and given the
		 * timezone's UTC offset. this function need to be dependency on
		 * date.format.js
		 */
		formatDate : function(dateTime, pattern, offset) {
		    // convert to msec
		    // add local time zone offset
		    // get UTC time in msec
		    var utc = dateTime.getTime() + (dateTime.getTimezoneOffset() * 60000);

		    // create new Date object for different timezone
    		// using supplied offset
    		var localTime = new Date(utc + (3600000 * offset));
    		var display = localTime.format(pattern);

    		// return time as a string
    		return display;
		},
		initTimeZoneCookie : function() {
			var tzoCookie = $.cookie('timezone_offset');
			if (tzoCookie != null) {
				return true;
			}

			var hostname = window.location.hostname;
			if ('localhost' == hostname) {
				// It's so strang! only word 'localhost' is not able to be assign.
				hostname = '';
			}

			// get the timezone offset (hour) in client side and keep it in browser cookies firstly.
			var offsetHours = -((new Date().getTimezoneOffset()) / 60);
			// this cookie will be pass to the server per HTTP request.
			$.cookie('timezone_offset', offsetHours, {
				expires: 1,
				path: '/',
				domain: hostname
			});

			this.log('Cookie timezone_offset is created. Offset: ' + offsetHours +
				' hours, Domain: ' + hostname);
		},
		getUid: function() {
			// apply Self-Defining Function pattern
			var uid = 1;
			this.getUid = function() {
				// if getUid(0) then uid will be reset to zero
				if (arguments[0] === 0) {
					uid = 0;
				}
				var result = uid;
				uid += 1;
				return result;
			};
			var result = uid;
			uid += 1;
			return result;
		}
	};
})();

/**
 * Module: Object
 * 提供各種處理Javascript OOP的Methods
 */
Common.Object = (function()  {
	// static private methods

	return { // static public methods
		inherit: function(parent, child) { // global inherit method
			var target = parent;
			// hook target object's 'prototype' property as current parent
			target.prototype = {
				prototype: parent.prototype,
				child: target
			};
			// re-assign parent's parent's child property
			if (target.prototype.prototype) {
				target.prototype.prototype.child = target.prototype;
			} else { // if parent's parent doesn't exist, then delete it
				delete target.prototype.prototype;
			}
			// re-construct target's parent object properties
			for (var property in parent) {
				if (property != 'prototype' && property != 'child') {
					target.prototype[property] = parent[property];
				}
			}
			// re-construct target object properties
			for (var property in child) {
				if (property != 'prototype' && property != 'child') {
					target[property] = child[property];
				}
			}
			return target;
		}
	}
})();

$.fn.clearForm = function(ignoredNames) {
	log('Ignored fields in form: \n' + $.dump(ignoredNames));

	return this.each(function(){
		var type = this.type;
		var name = this.name;
		var tag = this.tagName.toLowerCase();

		if (tag == 'form') {
			return $(':input', this).clearForm(ignoredNames);
		}

		if (ignoredNames != null) {
			for (var i = 0; i < ignoredNames.length; i++) {
				log('element name: ' + name + ', ignored names: ' + ignoredNames[i]);
				if (name == ignoredNames[i]) {
					return true;
				}
			}
		}

		if (type == 'text' || type == 'password' || tag == 'textarea') {
			this.value = '';
		} else {
			if (type == 'checkbox' || type == 'radio') {
				this.checked = false;
			} else {
				if (tag == 'select') {
					this.selectedIndex = -1;
				}
			}
		}
	});
};

/**
 * Module: AJAX
 * 提供各種共同的 AJAX Methods
 * syntax:
 * 		Common.AJAX.getInstance().get(url, param, callback, sync);
 */
namespace('Common.AJAX');
Common.AJAX = (function() {
	// dependencies
	var utils = Common.Utils;
	var ui = Common.UI;
	var log = Common.Utils.log;
	var info = Common.Utils.info;

	/**
	 * Be delegated to call the callback after the response is returned.
	 * @param data the data returned from the server.
	 * @param textStatus a string describing the status.
	 */
	function delegate(data, textStatus, uuid) {
		if (Common.isDebugMode()) {
			log('Return from the server:\n' + $.dump(data, 5));
		}

		if (data.type === 'WARN' || data.type === 'ERROR') {
			if (typeof jAlert === 'undefined' || jAlert === null) {
				alert(data.message);
			} else {
				jAlert(data.message);
			}
			return false;
		}

		return true;
	}

	/**
	 * The pre-callback before the request is sent that is integrated with $ jGrowl plug-in.
	 */
	function growlBeforeSend(XMLHttpRequest, param) {
		var message = 'Loading...';
		if (param) {
			var target;
			if (typeof param === 'string' && param.lastIndexOf('target') > 0) {
				var start = param.lastIndexOf('target=');
				if (start >= 0) {
					var end = param.indexOf('&', start + 7);
					if (end >= 0) {
						target = param.substring(start + 7, end);
						if (target.indexOf('%') >= 0) {
							target = decodeURIComponent(target);
						}
						message = utils.getMessage('load.growlMsg', [target]);
					}
				}
			} else {
				if (utils.isObject(param) && param.target) {
					target = param.target;
					message = utils.getMessage('load.growlMsg', [target]);
				}
			}
		}

		ui = ui || Common.UI;
		ui.growlMessage(message);
	}

	/**
	 * The post-callback after the request is sent that is integrated with $ jGrowl plug-in.
	 */
	function closeAlert(XMLHttpRequest, textStatus) {
	}

	/**
	 * The pre-callback before the request is sent that is integrated with $ BlockUI plug-in.
	 */
	function blockBeforeSend(XMLHttpRequest) {
		var waitMsg = utils.getMessage('system.wait');
		if (waitMsg == null) {
			waitMsg = 'Please wait...';
		}

		$.blockUI({
			message: '<img src="/js/jquery/loading.gif" /><font size="2">' + waitMsg + '</font>'
		});
	}

	/**
	 * The post-callback after the request is sent that is integrated with $ BlockUI plug-in.
	 */
	function closeBlock(XMLHttpRequest, textStatus) {
		$.unblockUI();
	}

	/**
	 * The callback if the request fails.
	 */
	function error(XMLHttpRequest, textStatus, errorThrown) {
		var message = utils.getMessage('system.fail');
		alert(message);
		log('Request fail! \nstatus: ' + XMLHttpRequest.status+
			'\nresponse text: \n' + XMLHttpRequest.responseText);
		$.unblockUI();
	}

	function showAJAXInfo(url, param, callback, sync) {
		if (Common.isDebugMode()) {
			var detail = '========================[AJAX detail Begin]========================\n';
			detail += '=====[url]=====: ' + url;
			detail += '\n=====[param]=====:\n' + $.dump(param);
			detail += '\n=====[callback]=====:\n' + utils.traceFunc(callback);
			detail += '\n=====[sync]=====: ' + sync + '\n';
			detail += '========================[End AJAX detail]========================';
			log(detail);
		}
	}

	/**
	 * Send a GET request to server via AJAX implementation without the UI effect.
	 * P.S. Indepenecy with any extra $ UI effect plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function get(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			cache : false,
			async : !sync,
			type : 'GET',
			url : url,
			data : param,
			dataType : 'json',
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			}
		});
	}

	/**
	 * Send a POST request to server via AJAX implementation without the UI effect.
	 * P.S. Indepenecy with any extra $ UI effect plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function post(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			cache : false,
			async : !sync,
			type : 'POST',
			url : url,
			data : param,
			dataType : 'json',
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			}
		});
	}

	/**
	 * Send a GET request to server via AJAX implementation with the UI effect showed by $ jGrowl plug-in.
	 * P.S. Depenecy with $ jGrowl plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function getOnAlert(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			global : false,
			cache : false,
			async : !sync,
			type : 'GET',
			url : url,
			data : param,
			dataType : 'json',
			beforeSend : function(XMLHttpRequest) {
				growlBeforeSend(XMLHttpRequest, param);
			},
			complete : closeAlert,
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			},
			error : error
		});
	}

	/**
	 * Send a POST request to server via AJAX implementation with the UI effect showed by $ jGrowl plug-in.
	 * P.S. Depenecy with $ jGrowl plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function postOnAlert(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			global : false,
			cache : false,
			async : !sync,
			type : 'POST',
			url : url,
			data : param,
			dataType : 'json',
			beforeSend : function(XMLHttpRequest) {
				growlBeforeSend(XMLHttpRequest, param);
			},
			complete : closeAlert,
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			},
			error : error
		});
	}

	/**
	 * Send a GET request to server via AJAX implementation with the UI effect showed by $ BlockUI plug-in.
	 * P.S. Depenecy with $ BlockUI plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function getOnBlock(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			global : false,
			cache : false,
			async : !sync,
			type : 'GET',
			url : url,
			data : param,
			dataType : 'json',
			beforeSend : blockBeforeSend,
			complete : closeBlock,
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			},
			error : error
		});
	}

	/**
	 * Send a GET request to server via AJAX implementation with the UI effect showed by $ BlockUI plug-in.
	 * P.S. Depenecy with $ BlockUI plug-in.
	 * @param url the requested URL.
	 * @param param the data are sent to the server.
	 * @param callback the function to be called if the request succeds.
	 * @param sync true if the request is sent synchronously, or false.
	 */
	function postOnBlock(url, param, callback, sync) {
		param = param || {};
		sync = sync || false;
		callback = callback || false;
		showAJAXInfo(url, param, callback, sync);

		$.ajax({
			global : false,
			cache : false,
			async : !sync,
			type : 'POST',
			url : url,
			data : param,
			dataType : 'json',
			beforeSend : blockBeforeSend,
			complete : closeBlock,
			success : function (data, textStatus) {
				var done = delegate(data, textStatus);
				if (done && callback) {
					callback.apply(null, [data, textStatus]);
				}
			},
			error : error
		});
	}

	// public API -- constructor
	function Constr() {
		if (!(this instanceof Constr)) {
			return new Constr();
		}

		// apply Singleton pattern with a Closure
		var instance;
		Constr = function() {
			info('Return singleton instance of Common.AJAX');
			return instance;
		};
		// carry over the prototype properties
		Constr.prototype = this;

		// new only instance
		instance = new Constr();
		// reset the constructor pointer
		instance.constructor = Constr;

		info('Create only one instance of Common.AJAX');

		return instance;
	}

	// a convenience static method
	Constr.getInstance = function() {
		return new Constr();
	};

	// public API -- prototype
	Constr.prototype = {
		// apply Revealing Module pattern
		get : get,
		post : post,
		getOnAlert : getOnAlert,
		postOnAlert : postOnAlert,
		getOnBlock : getOnBlock,
		postOnBlock : postOnBlock
	};

	// return the constructor
	// to be assigned to the new namespace
	return Constr;
}());

/**
 * Module: UI
 * 提供各種共同的 UI Methods
 */
namespace('Common.UI');
Common.UI = (function()  {
	// static private methods

	return { // static public methods
		openDialog : function(dialog) {
			dialog.overlay.slideDown('normal', function () {
				dialog.container.fadeIn('normal');
				dialog.data.show();
			});
		},
		closeDialog : function(dialog) {
			dialog.container.slideUp('normal', function () {
				dialog.overlay.fadeOut('normal', function () {
					$.modal.close();
				});
			});
		},
		confirmModal : function(message, callback) {
			$('#confirm').modal({
				close : true,
				position : ['15%', '50%'],
				overlayId : 'confirmModalOverlay',
				containerId : 'confirmModalContainer',
				onOpen : this.openDialog,
				onClose : this.closeDialog,
				onShow : function(dialog) {
					dialog.data.find('.confirmmessage').append(message);
					dialog.data.find('#ButtonYes').click(function() {
						$.modal.close();
						if ($.isFunction(callback)) {
							callback.call(this);
						}
					});
				}
			});
		},
		pressEscKey : function() {
			$(document.body).bind('keypress', function(evt) {
				var key = evt.which || evt.keyCode;
				if (key == 27) {
					// close modal dialog UI
					$.modal.close();
					// unblock fool-proof UI
					$.unblockUI();
					$(document.body).unbind('keypress');
				}
			});
		},
		growlMessage : function(message) {
			$.jGrowl(message, {
				life: 1200,
				speed: 'fast',
				easing: 'linear',
				corners: '14px'
			});
		},
		// open a browser window and align center
		openWindow: function(url, w, h) {
			width = width || 800;
			height = height || 600;
			var clientWidth = document.documentElement.clientWidth;
			var clientHeight = document.documentElement.clientHeight;
			var left = (clientWidth - width) / 2;
			var top = (clientHeight - height) / 2;
			var wnd = window.open(url, null,
				'height=' + height + ',width=' + width + ',top=' + top + ',left=' + left
				+ ',status=yes,toolbar=no,menubar=no,location=no,resize=yes,scrollable=yes,resizable=1,scrollbars=yes');
			if (wnd.opener == null) {
				wnd.opener = self;
			}
			return wnd;
		}
	};
})();

/**
 * Module: Validate
 * 提供各種共同的 Validate Methods
 */
namespace('Common.Validate');
Common.Validate = (function()  {
	// static private methods

	return { // static public methods
	    FORMAT : {
			REGNOW : /^[a-zA-Z0-9_-]{5,}$/,
			ACCOUNT : /^[a-zA-Z0-9._-]{6,12}$/,
	    	PASSWORD : /^[a-zA-Z0-9]{6,12}$/,
	    	EMAIL : new RegExp('^[\\w\\.-]+@[a-zA-Z0-9][\\w\\.-]*\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$'),
	    	CAPTCHA : /^[a-zA-Z0-9]{4}$/,
	    	VERSION : new RegExp('^[0-9]+(\\.[0-9]+)+$'),
	    	BIRTHDAY : /^(19|20)\d\d([-])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])$/
	    },
		validateFormat : function(regex, source) {
			return regex.test(source);
		}
	};
})();

// alias
var utils = Common.Utils;
var log = Common.Utils.log;
var info = Common.Utils.info;
var warn = Common.Utils.warn;
var error = Common.Utils.error;
