// simple route
var Router = function() {
	var _Router = function(routeMap, defaultRoute) {
		return this.setup(routeMap, defaultRoute);
	};
	_Router.prototype.setup = function(routeMap, defaultRoute) {
		var that = this;
		this.routeMap = [];
		this.defaultRoute = defaultRoute || function() {
		};
		for ( var rule in routeMap) {
			if (!routeMap.hasOwnProperty(rule))
				continue;
			that.routeMap.push({
				rule : new RegExp(rule, 'i'),
				func : routeMap[rule]
			});
		}
		return that;
	};
	_Router.prototype.start = function() {
		var hash = location.hash, route, matchResult, that = this;
		for ( var routeIndex in that.routeMap) {
			route = that.routeMap[routeIndex];
			matchResult = hash.match(route.rule);
			if (matchResult) {
				route.func.apply(window, matchResult.slice(1));
				return that;
			}
		}
		that.defaultRoute();
		return that;
	};
	return _Router;
}();
// set form data
$.fn.renderData = function(data) {
	var that = $(this), form = that.is('form') ? that : that.find('form');
	for ( var k in data) {
		var field = form.find('[name=' + k + "]");
		if (field.is(':radio') || field.is(':checkbox')) {
			field.filter('[value=' + data[k] + ']').attr('checked', true);
		} else {
			field.val(data[k]);
		}
	}
};
//form data to json
$.fn.serializeObject = function() {
	var scope = $(this), array = scope.serializeArray(), json = {};
	$.each(array, function(i, d) {
	    var type = scope.find('[name=' + d.name + ']').attr(
	            'type'),
	        v = d.value;
	    switch (type) {
	        case 'number':
	            v = Number(v);
	            break;
	        case 'date':
	            v = v ? v.getTime() : null;
	            break;
	        case 'checkbox':
	        	var isMulti = scope.find('[name=' + d.name + ']').length > 1;
	        	if (isMulti) {
	        		var _v = json[d.name] || [];
		        	_v.push(v);
		        	v = _v;
	        	} else {
	        		v = v == 'on' ? true : v;
	        	}
	            break;
	        default:
	        	break;
	    }
	    json[d.name] = v;
	});
	// TableView Data
	scope.find('table:visible').each(function(){
		var table = $(this), datas = [], key = table.attr('x-name'), fields = [];
		table.find('thead th').each(function(){
			fields.push($(this).attr('x-column'));
		});
		$.each(table.find('tbody tr'), function(i, row){
			var record = {};
			$.each($(row).find('td input'), function(j){
				record[fields[j]] = $(this).val();
			});
			datas.push(record);
		});
		json[key] = datas;
	});
	return json;
};
$.fn.scrollTo = function(){
	$("html,body").stop(true);
	$("html,body").animate({scrollTop: $(this).offset().top - 30}, 500);
};
//<--form validation Start
var toggleError = function($this, valid, text) {
	var help = $this.attr('x-help-by'), err = $this.attr('x-error-by');
	help && ($('#' + help).toggle(valid));
	err && ($('#' + err).toggle(!valid));
	text = text || $this.attr('x-error-text');
	text && ($('#' + err).html(text));
};
var getErrorText = function($this, text) {
	var tx = $this.attr('x-error-text'), err = $this.attr('x-error-by');
	return text || tx || $('#' + err).text() || 'error';
};
var parseExpression = function(exp, data) {
	return eval(exp.replace(/(\w)+/gi, function(e, c, i){
		if(exp.charAt(i-1) != "'" && exp.charAt(i+1) != "'") {
			return data[e] ? "'" + data[e] + "'" : null;
		}
		return e;
	}));
};
var checkRequired = function($this, form) {
	var val = $this.val(), r = $this.attr('required'), x = $this.attr('x-required'), b = true;
	if (r) {
		b = !!val;
	} else if (x) {
		var data = form.serializeObject();
		if(parseExpression(x, data))
			b = !!val;
	}
	return b;
};
var checkMaxLength = function($this) {
	var val = $this.val(), max = $this.attr('x-maxlength'), b = true;
	if (max) {
		b = val && String(val).length <= Number(max);
	}
	return b;
};
var checkMinLength = function($this) {
	var val = $this.val(), min = $this.attr('x-minlength'), b = true;
	if (min) {
		b = val && String(val).length >= Number(min);
	}
	return b;
};
var checkPattern = function($this) {
	var val = $this.val(), pattern = $this.attr('x-pattern'), b = true;
	if (val && pattern) {
		b = new RegExp(pattern).test(val);
	}
	return b;
};
var checkField = function($this, noCls, form, config) {
	var valid = (checkRequired($this, form) && checkMaxLength($this) && checkPattern($this) && checkMinLength($this));
	if (!noCls) {
		$this.toggleClass('x-error', !valid);
		$this.parent().next('.x-input-addon').find('.x-text-tip').hide();
		toggleError($this, valid);
		if(config) {
			var onChange = config.listeners[$this.attr('name')];
			if (onChange && typeof onChange === 'object') {
				onChange = onChange['change'];
			}
			valid && onChange && (onChange.call(null, $this, $this.val(), event));
		}
	}
	return valid;
};
var checkForm = function(form, noCls, config) {
	var valid = true;
	form.find('input').each(function(i, e){
		if (valid) {
			valid = checkField($(e), noCls, form, config);
		}
	});
	return valid;
};
$.fn.validation = function(config) {
	config = $.extend({disable: true, success: $.noop, error: $.noop, listeners: {}}, config);
	var form = $(this), submitBtn = form.find('.x-btn-submit'), disable = config.disable;
	disable && (submitBtn.addClass('disabled'));
	form.find('input').blur(function(event){
		var that = $(this);
		var fieldValid = checkField(that, false, form, config), formValid = fieldValid && checkForm(form, true, config);
		disable && (submitBtn.toggleClass('disabled', !formValid));
	});
	// 当修改input数据是触发
	// form.find('input').bind('input propertychange', function(event){
	// 	var that = $(this), listener = config.listeners[that.attr('name')];
	// 	if (checkField(that, false) && listener && listener['propertychange']) {
	// 		listener['propertychange'].call(null, that, that.val(), event);
	// 	}
	// });
	form.find('input').bind('blur', function () {
		// var that = $(this);
		// checkField(that, false, form, config);
	});
	submitBtn.click(function(){
		var valid = checkForm(form, false, config);
		if (!valid) {
			disable && (submitBtn.addClass('disabled'));
			var errEl = form.find('.x-error').eq(0);
			errEl.scrollTo();
			errEl.focus();
			config.error.call(null, form, errEl, getErrorText(errEl));
		} else {
			config.success.call(null, form, form.serializeObject());
		}
	});
};
//-->form validation End
// ajax delete
$.del = function(url, data, callback, type) {
	if ($.isFunction(data)) {
		type = type || callback;
		callback = data;
		data = undefined;
	}
	var config = $.extend({
		url : url,
		type : 'DELETE',
		dataType : type,
		data : data,
		processData : true,
		success : callback
	}, $.isPlainObject(url) && url);
	if ( config.data && config.processData && typeof config.data !== "string" ) {
		config.data = $.param( config.data, config.traditional );
	}
	// jQuery ajax query params only support GET and HEAD
	if ( config.data ) {
		config.url = ( config.url += ( (/\?/).test( config.url ) ? "&" : "?" ) + config.data );
		delete config.data;
	}
	return $.ajax(config);
};
// show modal by script template
$.fn.showModal = function(config) {
	config = $.extend(($.isFunction(config) ? {
		show : config
	} : (config || {})), {
		hidden : function() {
			$(this).remove();
		}
	});
	var listeners = {};
	$.each(config, function(e, fn) {
		listeners[e + '.bs.modal'] = fn;// ex: show.bs.modal
	});
	var html = $(this).text();
	if (config.data) {
		html = tmpl(html, config.data, config.renderer, config.tplExp);
	}
	$(document.body).append(html);
	$('.modal').on(listeners).modal({
		backdrop : 'static',
		show : true
	});
};
// template render
var tmpl = function(tpl, data, fns, exp) {
	exp = exp || '#{([^}]*)}';
	var callFn = function(scope, tplKey){
		if(fns && tplKey.indexOf('fn:') == 0) {
			var fn = fns[tplKey.substr(3)];
			if (fn) {
				return fn.call(null, scope);
			}
		}
		return null;
	}, reg = new RegExp(exp, 'g'), render = function(scope) {
		return tpl.replace(reg, function(tpl, e) {
			return e == 'this' ? scope : (callFn(scope, e) || scope[e] || '');
		});
	}, content = '';
	if (data instanceof Array) {
		$.each(data, function(i, d) {
			content += render(d);
		});
	} else {
		content = render(data);
	}
	return content;
};
$.fn.appendTpl = function(tpl, data, fns) {
	return $(this).html(tmpl(tpl, data, fns));
};
// pagination
var Pager = function() {
	var defaultSetting = {
		prevText: '上一页',
		nextText: '下一页',
		ellipsis: '...',
		ellipsisSize: 8,
		url: '',
		params: {
			pageSize: 10,
			pageNumber: 1
		},
		renderData: $.noop
	}, gotoPage = function(e) {
		var that = e.data, page = $(e.target).data('page');
		page && (render(that, {pageNumber: page}));
	}, renderPageBar = function(scope, element, setting, config) {
		var html = '', oldPage = element.find('.active a').data('page') || 0,
			oldBegin = element.find('[role=page]:first').data('page') || 0,
			oldEnd = element.find('[role=page]:last').data('page') || 0;
		if(config.pages > 1) {
			var begin = 1, end = config.pages;
			if (config.pages > setting.ellipsisSize) {
				if(oldPage && config.number >= oldBegin && config.number <= oldEnd) {
					begin = oldBegin;
					end = oldEnd;
				} else {
					if (config.number > oldEnd) {
						end = Math.min(config.number + setting.ellipsisSize - 1, config.pages);
						begin = end - setting.ellipsisSize + 1;
					} else {
						begin = Math.max(config.number - setting.ellipsisSize + 1, 1);
						end = begin + setting.ellipsisSize - 1;
					}
				}
			}
			if(config.number > 1)
				html = '<li><a data-page="' + (config.number - 1) + '">' + setting.prevText + '</a></li>';
			if(begin > 1) {
				html += '<li><a data-page="1">1</a></li>';
				html += '<li><a data-page="' + (begin - 1) + '">' + setting.ellipsis + '</a></li>';
			}
			var curr = begin;
			while (curr <= end){
				html += '<li ' + (curr == config.number ? 'class="active"' : '') + '><a role="page" data-page="' + curr + '">'+ curr++ +'</a></li>';
			}
			if(end < config.pages) {
				html += '<li><a data-page="' + (end + 1) + '">' + setting.ellipsis + '</a></li>';
				html += '<li><a data-page="' + config.pages + '">' + config.pages + '</a></li>';
			}
			if(config.number < config.pages)
				html += '<li><a data-page="' + (config.number + 1) + '">' + setting.nextText + '</a></li>';
		}
		element.html(html);
		element.find('a').click(scope, gotoPage);
	}, render = function(scope, params) {
		var setting = scope._setting, element = scope._element;
		$.get(setting.url, $.extend(setting.params, params), function(pageInfo){
			var config = pageInfo;
			config.pages = Math.ceil(config.totalElements / config.size);
			renderPageBar(scope, element, setting, config);
			setting.renderData(pageInfo.content);
		});
	};
	return function(element, setting) {
		this._element = element;
		this._setting = $.extend(defaultSetting, setting);
		render(this);
	};
}();
$.fn.pagination = function(setting) {
	return new Pager($(this), setting);
};
// date format
Date.prototype.format = function(format) {
	var value = this;
	var date = {
		"m+" : value.getMonth() + 1,
		"d+" : value.getDate(),
		"h+" : value.getHours(),
		"i+" : value.getMinutes(),
		"s+" : value.getSeconds(),
		"q+" : Math.floor((value.getMonth() + 3) / 3),
		"S+" : value.getMilliseconds()
	};
	if (/(y+)/i.test(format)) {
		format = format.replace(RegExp.$1, (value.getFullYear() + '').substr(4 - RegExp.$1.length));
	}
	for ( var k in date) {
		if (new RegExp("(" + k + ")").test(format)) {
			format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? date[k] : ("00" + date[k])
					.substr(("" + date[k]).length));
		}
	}
	return format;
};