(function($){
/**
 * AJAX执行顺序:
 * $().ajaxStart
 * beforeSend
 * $().ajaxSend
 * success | error
 * $().ajaxSuccess | $().ajaxError
 * $().ajaxStop
 * complete
 */

/**
 * get extended jquery information ( path, query )
 */
var _extInfo = (function() {
	var src = $('script:last').attr('src'), pos = src.indexOf('?'), info = {};
	if (pos == -1) pos = src.length;

	info.query = src.substr(pos);

	var file = src.substring(0, pos);
	pos = file.lastIndexOf('\/');
	info.path = file.substring(0, pos + 1);

	return info;
})();


/**
 * Extended jQuery information
 */
$.jcan = {
	version : 0.3,
	path : _extInfo.path,
	query : _extInfo.query
};

/**
 * jQuery.browser.ie6
 */
$.browser.ie6 = $.browser.msie && parseInt($.browser.version) < 7;


/**
 * 删除字符串前后所有全角与半角空格
 * @example str.trim()
 */
String.prototype.trim = function() {
	return this.replace(/^[\u3000\s]*|[\u3000\s]*$/g, "");
};

/**
 * 删除字符串前面所有全角与半角空格
 * @example str.ltrim()
 */
String.prototype.ltrim = function() {
	return this.replace(/^[\u3000\s]*/g, "");
};

/**
 * 删除字符串后面部分所有全角与半角空格
 * @example str.rtrim()
 */
String.prototype.rtrim = function() {
	return this.replace(/[\u3000\s]*$/g, "");
};

/**
 * 返回字符串的真实长度(一个全角字符的长度为2)
 * @example str.width()
 */
String.prototype.width = function() {
	return this.replace(/[^\x00-\xff]/g, "**").length;
};

/**
 * 取得字符串左边指定宽度长的子串
 * @example '我是中国人'.left(5)
 */
String.prototype.left = function(num) {
	var str = this.replace(/[^\x00-\xff]/g, "%^");
	num = str.substr(0, num).replace(/\%\^/g, "^").replace(/\%$/, "").length;
	return this.substr(0, num);
};

/**
 * @example
 * 	url = 'http://ouk.cn?id=3
 *	url.concatQuery('_ajax=1&r=' + Math.random());
 */
String.prototype.concatQuery = function(q) {
	return this + (this.indexOf('?') == -1 ? '?' : '&') + q;
};

/**
 * @see PHP repeat()
 */
String.prototype.repeat = function(num) {
	return new Array(num+1).join(this);
};

/**
 * replace("\r", "")
 */
String.prototype.equal = function(str) {
	return str.replace(/\r/g, "") == this.replace(/\r/g, "");
};

/**
 * 剔除掉数组中相同元素
 */
Array.prototype.unique = function() {
	var a={}, b=[];
	for (var i=0; i<this.length; i++)
		a[this[i]] = this[i];
	for (var c in a)
		b[b.length] = a[c];
	return b;
};

/**
 * 把4343214.4321以4,343,214.4321的形式显示
 * @param number num
 * @param int bit [optional]
 * @param string chr [optional]
 * @example 100000.format(4, ',')
 */
Number.prototype.format = function(bit, chr) {
	var num = this.toString();
	if (bit == null) bit = 3;
	if (chr == null) chr = ",";
	var re = new RegExp("(\\d+)(\\d{" + bit + "})");
	while (num.match(re)) {
		num = num.replace(re, "$1" + chr + "$2");
	}
	return num;
};

/**
 * 重写setTimeout,使其可多参数传递
 */
var _st = window.setTimeout;
window.setTimeout = function(fRef, mDelay) {
	if (typeof fRef == 'function') {
		var argu = Array.prototype.slice.call(arguments, 2);
		var f = function() {
			fRef.apply(null, argu);
		};
		return _st(f, mDelay);
	}
	return _st(fRef, mDelay);
};

/**
 * Extended jQuery Core Functions
 */
$.extend({

	/**
	 * Regular Expression
	 */
	regexp: {
		email: /^[0-9a-z\-\_\.]{1,40}@([0-9a-z\-]{1,20}\.){1,4}[a-z]{2,5}$/i,
		image: /\.(gif|p?jpe?g|png|bmp)$/i,
                phone: /^[0-9\-\40]{0,40}$/i,
                date: /^\d{2}\/\d{2}\/\d{4}$/i
	},

	/**
	 * 产生unique id
	 * 一个可能值: ad87dk1cghc (11位)
	 */
	uid: function() {
		return (new Date().getTime()*10000 + Math.random(1)*10000).toString(32);
	},

	/**
	 * 返回包含路径的JS文件
	 */
	getJsFile: function(js) {
		if (js.indexOf('\/') != -1) {
			js = $.jcan.path + js;
		}
		return js += $.jcan.query;
	},


	/**
	 * 动态加载JS
	 * @example
	 *	$.includeJs('jquery.menu.js')
	 *	$.includeJs(['jquery.menu.js', 'jquery.ajaxupload.js'])
	 *	$.includeJs('jquery.ajaxAuto.js', function(){
	 *		$('form').ajaxAuto();
	 *	});
	 *	$.includeJs(['jquery.ajaxAuto.js', 'jquery.ajaxUpload.js'], function(){ ... });
	 */
	includeJs: function(fn, jsfiles, cb) {

		if (fn && $.isFunction(fn)) {
			jsfiles && jsfiles();
			return;
		}

		if (fn) {
			cb = jsfiles;
			jsfiles = fn;
		}

		if (typeof jsfiles == 'string') {
			jsfiles = [jsfiles];
		}
		var jslen = jsfiles.length, uid = '_' + $.uid();
		$[uid] = jslen;

		if ($.loadingPlugins == undefined) {
			$.loadingPlugins = 0;
		}
		$.loadingPlugins += jslen;

		for (var i=0; i<jslen; i++) {
			$.ajax({
				url: $.getJsFile(jsfiles[i]),
				cache: true,
				global: false,
				dataType: 'script',
				success: function(){
					$.loadingPlugins--;
					!--$[uid] && cb && cb();
				}
			});
		}
	},


	/**
	 * $.run(function(){
	 *	 alert('All plugins had be loaded');
	 * });
	 */
	run : function(cb) {
		if (!$.loadingPlugins) {
			cb();
			return;
		}
		setTimeout(arguments.callee, 20, cb);
	},

	/**
	 * 相当于:
	 * $(function(){
	 *	$.run(function(){
	 *		alert('Page and all plugins had be loaded');
	 *	});
	 * });
	 */
	loadRun : function(cb) {
		$(function(){
			$.run(cb);
		});
	},

	/**
	 * dump variables, using firebug
	 */
	dump: function(variable, opts, count) {
		var s = {
			separator: "\t",
			iteration: 10
		};
		$.extend(s, opts || {});

		if (count == undefined) count = 0;

		if (typeof variable == 'object') {
			var indent = s.separator.repeat(count);

			for (var i in variable) {
				console.log(indent, i, s.separator, variable[i]);
				if (typeof variable[i] == "object" && count+1 < s.iteration) {
					arguments.callee(variable[i], s, count+1);
				}
			}
		} else {
			console.log(variable);
		}
	},

	compareColor: function(c1, c2) {
		if (!c1 || !c2) return false;
		if (c1 == c2) return true;

		var fix = function(c) {
			if (c.substr(0, 1) == '#') {
				if (c.length == 4) {
					c = c.replace(/([\da-f])/g, '$1$1');
				}
				c = 'rgb(' + parseInt(c.substr(1, 2), 16) + ', ' + parseInt(c.substr(3, 2), 16) + ', ' + parseInt(c.substr(5, 2), 16) + ')';
			}
			return c;
		};
		return fix(c1) == fix(c2);
	}
});


/**
 * Other extended jQuery functions
 */
$.extend({

	/**
	 * 与parseInt(str)的不同之处在于: 当str不能被解释成整数时, parseInt返回NaN, 而此处返回0
	 */
	intval: function(str) {
		var num = parseInt(str);
		if (isNaN(num)) num = 0;
		return num;
	},

	/**
	 * 转向某个URL, 这样做的好处是: 服务器端可以得到HTTP_REFERER
	 * @param string url 要转身的URL
	 * @param string verify 是否加入_POST['__confirm']
	 */
	redirect: function(url, verify) {
		if (verify) {
			$("<form action=\"" + url + "\" method=\"post\" style=\"display:none\"><input type=\"hidden\" name=\"__confirm\" value=\"1\"\/><\/form>").appendTo('body').submit().remove();
		} else {
			$("<form action=\"" + url + "\" method=\"get\" style=\"display:none\"\/>").appendTo('body').submit().remove();
		}
	},

	/**
	 * @example
	 * $.check(username.value, 'Please enter username', username);
	 * $.check(username.value, function(){ $.alert('Please enter username'); });
	 */
	check: function(expr, msg, obj) {

		if (expr) {
			if (msg) {
				if ($.isFunction(msg)) {
					msg();
				} else {
					$.tip(msg);
				}
			}

			if (obj) {
				if ($.isFunction(obj)) {
					obj();
				} else {
					obj.focus();
				}
			}

			//e && e.preventDefault();
		}
		return !expr;
	},

	/**
	 * 私有函数
	 */
	_ajaxLight: function(opts, success, failure, error) {
		var _this = this;

		//options
		if (!opts || $.isFunction(opts)) {
			error = failure;
			failure = success;
			success = opts;
		}
		if (typeof opts == 'string') {
			opts = {url: opts};
		}

		//AJAX settings
		var s = {
			url: this.href,
			global: false,
			type: 'post',
			data: '__confirm=1',
			cache: false
		};
		$.extend(s, opts || {});

		//无参数failure(用自定义函数处理)
		if (failure === undefined) {
			s.success = success;
		}
		//有参数failure
		else {
			s.success = function(data) {
				switch (data) {
					case '1':
						success && success.call(_this, data);
						break;
					case '0':
						failure && failure.call(_this, data);
						break;
					case '-1':
					default:
						error && error.call(_this, data);
						break;
				}
			};
		}

		$.ajax(s);
		return this;
	}
});


/**
 * Extended jQuery functions
 */
$.fn.extend({

	/**
	 * $('#btn').hoverClass('btnHover');
	 */
	hoverClass: function(c) {
		this.each(function(){
			$(this).hover(
				function() { $(this).addClass(c); },
				function() { $(this).removeClass(c); }
			);
		});
		return this;
	},

	/**
	 * $('input.tip').toggleValue('lightColor');
	 */
	toggleValue: function(nullClass, submitChk, title) {

		if (submitChk == undefined) submitChk = true;

		var these = this;
		return this.each(function(){
			var _this = this;

			if (title) {
				var _title = title;
			} else {
				var _title = this.title;
				$(this).removeAttr('title');
			}

			if (submitChk) {
				$(this).parents('form:first').submit(function(){
					if (_this.value.equal(_title)) {
						$(_this).triggerHandler('focus');
					}
				});
			}

			$(this).focus(function(){
				if (this.value.equal(_title)) {
					this.value = '';
					nullClass && $(this).removeClass(nullClass);
				}

			}).blur(function(){
				if (this.value == '' || this.value.equal(_title)) {
					this.value = _title;
					nullClass && $(this).addClass(nullClass);
				}
			}).triggerHandler('blur');
		});
	},


	ajaxLight: function(options, successFn, failureFn, errorFn)
	{
		return this.each(function(){
			$(this).click(function(){
				$._ajaxLight.call(this, options, successFn, failureFn, errorFn);
				return false;
			});
		});
	},

	/**
	 * 参数可以为函数
	 * example
	 * $('a.delete').click(function(){
	 * 	return this.href;
	 * });
	 */
	confirmLink: function(msg, successFn, failureFn, errorFn) {
		if (failureFn && !errorFn) {
			errorFn = failureFn;
			failureFn = null;
		}

		return this.each(function(){
			$(this).click(function(){
				var _this = this, confirmFn;

				//msg
				if ($.isFunction(msg)) {
					msg = msg.call(this);
				}

				//只有一个msg参数(不用ajax处理)
				if (successFn === undefined) {
					confirmFn = function(){
						$.redirect(_this.href, true);
					};
				}

				//有两个或三个参数(用ajax处理)
				else {
					//ajax request
					confirmFn = function(){
						$._ajaxLight.call(_this, successFn, failureFn, errorFn);
					};
				}

				$.confirm(msg, confirmFn);
				return false;
			});
		});
	}
});



//cls: error, loading
$.fn.note = function(str, cls, close) {
	if (str === false) {
		$$ = this;
		!this.find('> div')[$.fn.note == 'fade' ? 'fadeOut' : 'slideUp'](function(){
			$$.empty();
		}).length && $$.empty();
		return $$;
	}

	cls = cls || 'error';
	close = close === false ? false : true;
	close = close ? '<span class="close">close<\/span>' : '';

	var inner = $('<div class="' + cls + '">' + close + '<div class="tip">' + str + '<\/div><\/div>').hide();
	var close = inner.find('span.close');
	close.click(function(){
		inner[$.fn.note.fx == 'fade' ? 'fadeOut' : 'slideUp']('fast');
		//inner.fadeOut();
	});
	this.empty().prepend(inner);
	inner[$.fn.note.fx == 'fade' ? 'fadeIn' : 'slideDown']('fast');
	//inner.fadeIn('fast');
	return this;
};
$.fn.note.fx = 'fade';

$.fn.disabled = function(disabled) {
	if (disabled === false) {
		this.removeAttr('disabled').removeClass('disabled');
	} else {
		this.attr('autocomplete', 'off').attr('disabled', 'disabled').addClass('disabled');
	}
	return this;
};

$.fn.loading = function() {
	return this.note('Loading ...', 'loading', false);
};

$.fn.check = function(condition, str, obj) {
	if (condition) {
		this.note(str);
		if ($.isFunction(obj)) {
			obj();
		} else {
			obj && obj.focus();
		}
		return false;
	}
	return true;
};

$.fn.ajaxForm = function(tip, cb, appendcb) {
	var $$ = this;
	var btn = $$.find(':submit').disabled();
	tip.loading();

	$.ajax({
		type: $$.attr('method'),
		url: $$.attr('action'),
		data: $$.serialize(),
		dataType: 'json',
		success: function(data){
			if (cb) {
				cb(data);
				//to restore
				btn.disabled(false);
				return;
			}

			//no redirect
			if (data.redirect == undefined) {
				if (data.msg) {
					tip.note(data.msg, data.status);
				}
				//to restore
				btn.disabled(false);
			}
			//reidrect
			else {
				//redirect
				var redirect = function(){
					switch (data.redirect) {
						case 'reload':
							window.location = window.location.href;
							break;
						case 'referer':
							if (document.referrer) {
								window.location = document.referrer;
							} else {
								history.go(-1);
							}
							break;
						case 'back':
							history.go(-1);
							//to restore
							btn.disabled(false);
							break;
						default:
							window.location = data.redirect;
							break;
					}
				};

				if (data.msg) {
					tip.note(false);
					$.alert(data.msg, redirect);
					//to restore
					btn.disabled(false);
				} else {
					redirect();
				}
			}
			appendcb && appendcb(data);
			if (data.data) {
				var data = data.data;
				for (var str in data) {
					var obj = eval(str);
					var html = data[str];
					if (typeof obj == 'undefined') return;
					if (obj && obj.length && obj.is(':input')) {
						obj.val(html);
						obj[0].defaultValue = html;
					} else {
						if (obj !== null) obj.html(html);
					}
				}
			}
		}
	});
};

$.fn.ajaxAuto = function(opts, tip) {

	var s = $.extend({
		url: window.location.href,
		type: 'POST',
		dataType: 'json'
	}, opts);
	var userHandler = s.success;

	var defHandler = function(data) {
		//no redirect
		if (data.redirect == undefined) {
			if (data.msg) {
				tip.note(data.msg, data.status);
			} else {
				tip.note(false);
			}
		}
		//reidrect
		else {
			//redirect
			var redirect = function(){
				switch (data.redirect) {
					case 'reload':
						window.location = window.location.href;
						break;
					case 'referer':
						if (document.referrer) {
							window.location = document.referrer;
						} else {
							history.go(-1);
						}
						break;
					case 'back':
						history.go(-1);
						break;
					default:
						window.location = data.redirect;
						break;
				}
			};

			if (data.msg) {
				tip.note(false);
				$.alert(data.msg, redirect);
			} else {
				redirect();
			}
		}

		if (data.data) {
			var data = data.data;
			for (var str in data) {
				var obj = eval(str);
				var html = data[str];
				if (typeof obj == 'undefined') continue;
				if (obj && obj.length && obj.is(':input')) {
					obj.val(html);
					obj[0].defaultValue = html;
				} else {
					if (obj !== null) obj.html(html);
				}
			}
		}
	};

	return this.each(function(){
		var self = this;
		s.success = function(data) {
			defHandler.call(self, data);
			userHandler && userHandler.call(self, data);
		};
		tip.loading();
		$.ajax(s);
	});
};
})(jQuery);