// 加密 sha1 md5
var encrypt = require('./encrypt.js');
// 缓存
var cache = require('./cache.js');
// 数学精确计算
var math = require('./math.js');
// 腾讯位置服务
var qqMap = require("./qqmap-wx-jssdk");
// 二维码
var qrCode = require("./weapp.qrcode.min");

module.exports = {

	/**
	 * 小程序基本配置
	 */
	config: {
		name: '嘉兴拓新',
		debug: false, //开发模式true，生产环境改为false;
		version: 'xcx',
		baseUrl: 'https://txjxjy.com/index.php',
		qqMap: false,
		loading: false
	},

	/**
	 * sha1加密
	 * @param str
	 * @param raw 加密方式 1str_sha1 0hex_sha1
	 */
	sha1: function(str, raw) {
		return encrypt.sha1(str, raw);
	},

	/**
	 * md5加密
	 * @param str
	 */
	md5: function(str) {
		return encrypt.md5(str);
	},

	/**
	 * 缓存
	 */
	cache: cache,

	/**
	 * 数字精确计算
	 */
	math: math,

	/**
	 * 腾讯地图
	 */
	qqMap: qqMap,

	/**
	 * 消息提示
	 * @param title
	 * @param icon:success,error,loading,none 
	 * @param success
	 * @param time
	 * @param mask
	 */
	info: function(title, icon, success, time, mask) {
		if (typeof icon == 'function') {
			success = icon;
			icon = "none";
		}
		title = title == undefined ? "系统繁忙" : title;
		icon = icon == undefined ? "none" : icon;
		time = time == undefined ? 800 : time;
		mask = mask == undefined ? true : mask;
		uni.showToast({
			title: title,
			icon: icon,
			mask: mask,
			duration: time,
			success: setTimeout(function() {
				success && success()
			}, time)
		});
	},

	/**
	 * 确认提示框
	 * @param {Object} content
	 * @param {Object} success
	 * @param {Object} cancel
	 * @param {Object} confirmText
	 * @param {Object} cancelText
	 */
	modal: function(content, success, cancel, confirmText, cancelText) {
		let opt = {};
		opt.title = this.config.name;
		opt.content = content;
		// 默认不显示取消
		opt.showCancel = false;
		if (typeof cancel == 'function' || (typeof cancel == 'boolean' && cancel)) {
			opt.showCancel = true;
		} else if (typeof cancel == 'string') {
			cancelText = confirmText;
			confirmText = cancel;
		}
		if (!this.isEmpty(confirmText)) {
			opt.confirmText = confirmText;
		}
		if (!this.isEmpty(cancelText)) {
			opt.cancelText = cancelText;
			opt.showCancel = true;
		}
		opt.success = function(res) {
			if (res.confirm) {
				success && success();
			} else if (res.cancel) {
				typeof cancel == 'function' && cancel();
			}
		};
		uni.showModal(opt);
	},

	/**
	 * loading 提示框
	 * @param title
	 */
	loading: function(title) {
		var that = this;
		title = typeof title == 'string' ? title : "加载中...";
		!that.config.loading && uni.showLoading({
			title: title,
			mask: true,
			success: function() {
				that.config.loading = true;
			}
		});
	},

	/**
	 * 关闭loading 提示框
	 */
	closeLoading: function() {
		var that = this;
		that.config.loading && uni.hideLoading({
			noConflict: true,
			success: function() {
				that.config.loading = false;
			}
		});
	},

	/**
	 * 判断登录
	 * @param {Object} type 0不跳转登录 1跳转登录
	 * @param {Object} success
	 */
	login: function(type, success) {
		if (typeof type == 'function') {
			success = type;
			type = 1;
		} else if (typeof type == 'undefined') {
			type = 1;
		}
		var that = this;
		/* var userInfo = this.getUserInfo();
		if (this.isEmpty(userInfo)) {
			if (type == 1) {
				that.info('请先登录', function() {
					var pages = getCurrentPages();
					var route = pages[pages.length - 1].route;
					// if (route != 'pages/login/choose') {
					// 	that.jump('/pages/login/choose');
					// }
				});
			}
			return false;
		} */
		// return success && success(userInfo) || userInfo;
		return success
	},

	/**
	 * 页面跳转
	 * @param url
	 * @param type
	 */
	jump: function(url, type, params) {
		var that = this;
		if (that.isEmpty(url) && type !== -1) return false;
		if (url === -1) {
			var delta = type || 1;
			type = url;
		} else if (type === -1) {
			var delta = 1;
			if (params && params.delta) {
				delta = params.delta;
			}
		}
		type = type || 0, //默认0
			params = params || {};
		if (that.isEmpty(params)) {
			cache.remove('jump_data');
		} else {
			cache.set('jump_data', params);
		}
		switch (type) {
			case -1:
				//关闭当前页面，返回上一页面或多级页面 delta:1 返回层数
				setTimeout(function() {
					uni.navigateBack({
						delta: delta
					});
				}, 800);
				return;
			case 0:
				//保留当前页面，跳转到应用内的某个页面，使用uni.navigateBack可以返回到原页面
				uni.navigateTo({
					url: url
				});
				return;
			case 1:
				//关闭当前页面，跳转到应用内的某个页面
				uni.redirectTo({
					url: url
				});
				return;
			case 2:
				//关闭所有页面，打开到应用内的某个页面
				uni.reLaunch({
					url: url
				});
				return;
			case 3:
				//跳转到 tabBar 页面，并关闭其他所有非 tabBar 页面
				uni.switchTab({
					url: url
				});
				return;
			default:
				return false;
		}
	},

	/**
	 * 数据请求
	 */
	req: function(option, onsuccess, onerror, oncomplete) {
		if (option == 'undefined') return false;
		var that = this,
			config = {
				sign: false, //是否签名数据
				load: false, //是否显示loading提示
				login: false, //是否需要登录
				url: false,
				data: option.data,
				header: {
					'content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
					'Authorization': uni.getStorageSync('token').token? uni.getStorageSync('token').token: ''
				},
				method: 'post',
				dataType: 'json',
				success: function(res) { //通讯成功
					// console.log(res);
					

					//获取返回数据
					res = res.data;
					// if (res.is_disable == 1) {
					// 	that.info('该账号已被禁用')
					// 	uni.removeStorageSync('token')
					// 	setTimeout(function(){
					// 		// $api.jump('/pages/login/login')
					// 	},1000)
					// }
					that.jslog('请求:' + option.url);
					that.jslog(res.data);
					//如果存在回调，优先执行回调函数
					if (onsuccess) {
						if (onsuccess(res) === false) {
							return;
						}
					}
					
					if (typeof(res) == 'object') {
						(res.info && res.info != 'success') && that.info(res.info);
						if (res.status == 98) { //未登录或账号不存在
							cache.remove('user_info');
							that.login();
						} else if (res.status == 99) { //token验证失败
							cache.remove('token');
						}
					} else {
						// that.modal('接口异常');
					}
				},
				fail: function(res) { //通讯失败
					that.jslog('请求:' + option.url);
					that.jslog(res);
					onerror && onerror();
				},
				complete: function(res) { //通讯完成
					option.load && that.closeLoading();
					oncomplete && oncomplete();
				}
			};

		option.data && (option.data = option.data);
		if (option.url) {
			option.url = that.config.baseUrl + option.url;
		} else {
			return false;
		}

		option = that.extend({}, config, option);
		if (option.login && !that.login()) {
			return false;
		}
		// if (!option.data.submit) {
		// 	option.data.submit = 'yes';
		// }
		if (option.sign) {
			that.getsign(option.data, function(res) {
				option.data = res;
				option.load && that.loading(option.load);
				uni.request(option);
			})
		} else {
			option.load && that.loading(option.load);
			uni.request(option);
		}
	},

	
	/**
	 * 获取一个Token，登录成功请清空
	 * @param {function}  callback :回调函数
	 */
	gettoken: function(callback) {
		var that = this,
			token = cache.get('token'),
			timestamp = Date.parse(new Date()) / 1000;
		// 10分钟缓冲
		if (token && token.date_expire > (timestamp + 600)) return callback({
			status: 1,
			data: token
		});
		var uuid = that.uuid(),
			//clientid=that.getClientId(),
			uid = that.getUserInfo('id', 0);
		uni.request({
			url: that.config.baseUrl + 'token',
			data: {
				uuid: uuid,
				uid: uid,
				timestamp: timestamp
			}, //clientid:clientid
			timeout: 10000, //超时时间设置为10秒
			success: function(res) {
				var res = res.data;
				if (res.status == 1) {
					cache.set('token', res.data);
				}
				return callback(res);
			},
			complete: function(res) {
				that.jslog('获取token:' + res);
			}
		});
	},

	/**
	 * 数据签名
	 * @param {string}     data:要签名的数据
	 * @param {function}   onsuccess:成功回调函数
	 * @param {function}   onerror:失败回调函数
	 */
	getsign: function(param, onsuccess, onerror) {
		var that = this,
			arr = [];
		param._sign_uuid = that.uuid();
		param._sign_uid = that.getUserInfo('id', 0);
		param._timestamp = Date.parse(new Date()) / 1000;
		param._version = that.config.version;
		that.gettoken(function(res) {
			if (res.status == 1) {
				param._sign_token = res.data.token;
				for (var i in param) {
					if (!that.isEmpty(param[i], true)) {
						arr.push(i + '=' + param[i]);
					}
				}
				arr.sort();
				param._sign = encrypt.sha1(arr.join('&') + res.data.token).toUpperCase();
				return onsuccess(param);
			} else {
				return onerror && onerror(res.info) || that.info(res.info);
			}
		});
	},

	/**
	 * 格式化参数(password加密)
	 * @param {string}     query:参数
	 */
	formatparam: function(query) {
		var json = {};
		if (typeof(query) == 'object') {
			for (var i in query) {
				if (i.indexOf('password') > -1) {
					query[i] = encrypt.md5(encrypt.sha1(query[i]));
				}
			}
			return query;
		} else {
			var vars = query.split('&');
			for (var i = 0; i < vars.length; i++) {
				var pair = vars[i].split('=');
				if (pair[0].indexOf('password') != -1) {
					pair[1] = encrypt.md5(encrypt.sha1(pair[1]));
				} else {
					pair[1] = pair[1];
				}
				if (typeof json[pair[0]] === 'undefined') {
					json[pair[0]] = pair[1];
				} else if (typeof json[pair[0]] === 'string') {
					var arr = [json[pair[0]], pair[1]];
					json[pair[0]] = arr;
				} else {
					json[pair[0]].push(pair[1]);
				}
			}
			return json;
		}
	},

	/**
	 * 判断是否数组
	 * 
	 * 1. 通过object类型的副属性class判断,
	 * 	其中函数的class是Function,结果是[object Function]
	 * 	普通的对象是Object,结果是[object Object]
	 * 	Object.prototype.toString.call(arr) === '[object Array]'	
	 * 2. 通过原型去判断
	 * 	arr.constructor === Array	
	 * 	Array.prototype.isPrototypeOf(arr)
	 * 	arr.__proto__ === Array.prototype	
	 * 	Object.getPrototypeOf(arr) === Array.prototype
	 * 	arr instanceof Array
	 * 3. es6新增方法
	 * 	Array.isArray(arr)	
	 */
	isArray: function(arr) {
		return arr && typeof(arr) == 'object' && Object.prototype.toString.call(arr) === '[object Array]';
	},

	/**
	 * 否判断指定参数是否是一个纯粹的对象
	 * 所谓"纯粹的对象",就是通过"{}"或"new Object"创建的,不包括数组等对象
	 * 
	 * 1. 通过object类型的副属性class判断
	 * 	Object.prototype.toString.call(obj) === '[object Object]'
	 * 2. 通过原型去判断
	 * 	obj.constructor === Object
	 * 	obj instanceof Object(数组也是对象，所以 arr instanceof Object 也为true)
	 * 3. typeof
	 * 	typeof obj(除了{},null和[]也是'object')
	 */
	isPlainObject: function(obj) {
		if (Object.getPrototypeOf) {
			return obj && typeof(obj) == 'object' && Object.getPrototypeOf(obj) == Object.prototype;
		} else {
			return obj && typeof(obj) == 'object' && Object.prototype.toString.call(obj) == "[object Object]" &&
				!obj.length;
		}
	},

	/**
	 * 获取对象大小
	 * @param object
	 */
	getLength: function(object) {
		return Object.keys(object).length
	},

	/**
	 * 判断两个Json（纯粹）对象是否一样
	 * @param objA
	 * @param objB
	 */
	compareJsonObj: function(objA, objB) {
		if (!this.isPlainObject(objA) || !this.isPlainObject(objB)) return false; //判断类型是否正确
		return this.compareObj(objA, objB); //默认为true
	},

	/**
	 * 判断两个对象是否一样
	 * @param objA
	 * @param objB
	 * @param flag
	 */
	compareObj: function(objA, objB, flag) {
		if (this.getLength(objA) != this.getLength(objB)) return false;
		flag = flag || true;
		for (var key in objA) {
			if (!flag) //flag为false，则跳出整个循环
				break;
			if (!objB.hasOwnProperty(key)) { //是否有自身属性，而不是继承的属性
				flag = false;
				break;
			}
			if (!this.isArray(objA[key])) { //子级不是数组时,比较属性值        	
				if (this.isPlainObject(objA[key])) {
					if (this.isPlainObject(objB[key])) {
						if (!flag) //这里跳出循环是为了不让递归继续
							break;
						flag = this.compareObj(objA[key], objB[key], flag);
					} else {
						flag = false;
						break;
					}
				} else {
					if (String(objB[key]) != String(objA[key])) { //排除数字比较的类型差异
						flag = false;
						break;
					}
				}
			} else {
				if (!this.isArray(objB[key])) {
					flag = false;
					break;
				}
				var oA = objA[key],
					oB = objB[key];
				if (oA.length != oB.length) {
					flag = false;
					break;
				}
				for (var k in oA) {
					if (!flag) //这里跳出循环是为了不让递归继续
						break;
					flag = this.compareObj(oA[k], oB[k], flag);
				}
			}
		}
		return flag;
	},

	/**
	 * 将一个或多个对象的内容合并到目标对象
	 * @param  Object target 目标对象，其他对象的成员属性将被附加到该对象上
	 * @param  Object target 目标对象，第一个被合并的对象
	 * @param  Object target 目标对象，第二个被合并的对象
	 */
	extend: Object.assign || function(target, source, deep) {
		for (key in source) {
			if (deep && (this.isPlainObject(source[key]) || this.isArray(source[key]))) {
				if (this.isPlainObject(source[key]) && !this.isPlainObject(target[key])) {
					target[key] = {};
				}
				if (this.isArray(source[key]) && !this.isArray(target[key])) {
					target[key] = [];
				}
				this.extend(target[key], source[key], deep);
			} else if (source[key] !== undefined) {
				target[key] = source[key];
			}
		}
		return target;
	},

	/**
	 * 判断一个对象是否为空
	 * @param {string}     string:要判断的参数
	 * @param {string}     is_zero:是否要判断'0'
	 */
	isEmpty: function(string, is_zero = false) {
		if (is_zero && string === '0') {
			return true;
		}
		// 0 == '',false == '',[] == ''
		if (string == '' || string == null || typeof(string) == 'undefined' || string == 'undefined') {
			return true;
		}
		// 对象 和 数组
		if (typeof(string) == 'object') {
			return this.getLength(string) ? false : true;
		}
		return false;
	},

	/**
	 * 为客户端生成一个唯一的uuid
	 */
	uuid: function() {
		var uuid,
			random = function() {
				return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
			};
		uuid = cache.get('uuid');
		if (!uuid) {
			uuid = encrypt.sha1(random() + random() + '-' + random() + '-' + random() + '-' + random() + '-' +
				random() + random() + random());
			cache.set('uuid', uuid);
		}
		return uuid;
	},

	/**
	 * 获取设备唯一标识
	 */
	getClientId: function() {
		//获取客户端ID
		var clientid = cache.get('client_id');
		if (clientid) return clientid;
		//TODO::
	},

	/**
	 * 获取用户登录信息
	 * @param k 键值
	 * @param d 入没有返回的默认值
	 */
	getUserInfo: function(k, d) {
		if (d == 'undefined') {
			d = 0;
		}
		var userInfo = cache.get('user_info');
		if (this.isEmpty(userInfo)) {
			return d;
		} else {
			if (typeof k == 'undefined') {
				return userInfo;
			} else {
				if (typeof userInfo[k] == 'undefined') {
					return d;
				} else {
					return userInfo[k];
				}
			}
		}
	},

	/**
	 * 查找指定值的元素在数组中第一次出现的位置
	 * 查找指定键的值的元素在二维数组中第一次出现的位置
	 * @param arr  数组
	 * @param val  要查找的值
	 * @param key  二维数组元素的值对应的键
	 */
	indexOf: function(arr, val, key) {
		if (this.isEmpty(arr) || this.isEmpty(val)) return -1;
		if (typeof key == 'undefined') {
			for (let i = 0; i < arr.length; i++) {
				if (arr[i] == val) {
					return i;
				}
			}
		} else {
			for (let i = 0; i < arr.length; i++) {
				if (arr[i][key] == val) {
					return i;
				}
			}
		}
		return -1;
	},

	/**
	 * (批量)删除数组中指定值的元素
	 * (批量)删除二维数组中指定键的值的元素
	 * @param {Object} arr  (二维)数组
	 * @param {Object} vals 值(数组)
	 * @param {Object} key	值(数组)组对应的键
	 */
	removeItemByVals: function(arr, vals, key) {
		var that = this;
		if (this.isEmpty(arr) || this.isEmpty(vals)) return arr;
		if (this.isArray(vals)) {
			//!倒序删除
			if (typeof key == 'undefined') {
				for (let i = arr.length - 1; i >= 0; i--) {
					let index = that.indexOf(vals, arr[i]);
					if (index > -1) {
						arr.splice(i, 1);
					}
				}
			} else {
				for (let i = arr.length - 1; i >= 0; i--) {
					let index = that.indexOf(vals, arr[i][key]);
					if (index > -1) {
						arr.splice(i, 1);
					}
				}
			}
		} else {
			let index = this.indexOf(arr, vals, key);
			if (index > -1) {
				arr.splice(index, 1);
			}
		}
		return arr;
	},

	/**
	 * 批量删除数组中指定键的元素
	 * @param {Object} arr   数组
	 * @param {Object} indexs 序号(数组)
	 */
	removeItemByIndex: function(arr, indexs) {
		var that = this;
		if (this.isEmpty(arr) || this.isEmpty(indexs)) return arr;
		if (this.isArray(indexs)) {
			//!倒序删除
			for (let i = arr.length - 1; i >= 0; i--) {
				let index = that.indexOf(indexs, i);
				if (index > -1) {
					arr.splice(i, 1);
				}
			}
		} else {
			arr.splice(indexs, 1);
		}
		return arr;
	},

	/**
	 * 返回数组中某个单一列的值
	 */
	array_column: function(arr, key) {
		if (this.isEmpty(arr)) return arr;
		if (this.isEmpty(key)) return [];
		var vals = [];
		arr.forEach(item => {
			vals.push(item[key]);
		});
		return vals;
	},

	/**
	 * 对象按照key排序
	 * @param {Object} arr
	 */
	array_ksort: function(arr) {
		let sorted = {},
			keys = Object.keys(arr);
		keys.sort();
		keys.forEach((key) => {
			sorted[key] = arr[key];
		})
		return sorted;
	},

	/**
	 * 数组按元素（指定键值）排序
	 * @param {Object} arr
	 * @param {Object} key
	 * @param {Object} sort asc desc
	 */
	array_vsort: function(arr, key, sort) {
		if (this.isEmpty(arr)) return arr;
		key = key || '';
		sort = sort || 'asc';
		if (key == '') {
			return arr.sort();
		} else {
			if (sort == 'asc') {
				return arr.sort(function(a, b) {
					return a[key] - b[key];
				});
			} else {
				return arr.sort(function(a, b) {
					return b[key] - a[key];
				});
			}
		}
	},

	/**
	 * 日期时间格式化
	 * @param {Object} date 日期对象
	 * @param {Object} type 增加加的时间间隔:y年 q季度(3个月) m月 d天
	 * @param {Object} number 增加的时间间隔的个数
	 * @param {Object} format 格式化样式
	 */
	formatDate: function({
		date = '',
		type = 'y',
		number = 0,
		format = 'Y-m-d H:i:s'
	}) {
		var that = this;
		if (that.isEmpty(date, true)) {
			date = new Date();
		} else {
			if (typeof date == 'number') {
				date = new Date(parseInt(date) * 1000);
			} else if (typeof date == 'string') {
				date = new Date(date);
			}
		}
		date = that.addDate(type, number, date);
		var zero = function(value) {
				if (value < 10) {
					return '0' + value;
				}
				return value;
			},
			year = date.getFullYear(),
			month = zero(date.getMonth() + 1),
			day = zero(date.getDate()),
			hour = zero(date.getHours()),
			minite = zero(date.getMinutes()),
			second = zero(date.getSeconds());
		return format.replace(/Y|m|d|H|i|s/ig, function(matches) {
			return ({
				Y: year,
				m: month,
				d: day,
				H: hour,
				i: minite,
				s: second,
			})[matches];
		});
	},

	/**
	 * 日期时间计算
	 * addDate(type,number,date)
	 * @param {Object} type 增加的时间间隔:y年 q季度(3个月) m月 d天
	 * @param {Object} number 增加的时间间隔的个数
	 * @param {Object} date 开始日期对象
	 * @return {type} 新的日期对象
	 * var now = new Date();
	 * var newDate = addDate( "d", 5, now);
	 */
	addDate: function(type, number, date) {
		switch (type) {
			case "y": {
				date.setFullYear(date.getFullYear() + number);
				return date;
				break;
			}
			case "q": {
				date.setMonth(date.getMonth() + number * 3);
				return date;
				break;
			}
			case "m": {
				date.setMonth(date.getMonth() + number);
				return date;
				break;
			}
			case "w": {
				date.setDate(date.getDate() + number * 7);
				return date;
				break;
			}
			case "d": {
				date.setDate(date.getDate() + number);
				return date;
				break;
			}
			case "h": {
				date.setHours(date.getHours() + number);
				return date;
				break;
			}
			case "m": {
				date.setMinutes(date.getMinutes() + number);
				return date;
				break;
			}
			case "s": {
				date.setSeconds(date.getSeconds() + number);
				return date;
				break;
			}
			default: {
				date.setDate(date.getDate() + number);
				return date;
				break;
			}
		}
	},

	/**
	 * 二维码操作
	 * @param {Object} canvas		图片路径
	 * @param {Object} action 	操作类型 
	 * 	qrcode:绘制二维码(根据内容data)
	 * 	scan:扫描二维码
	 * @param {Object} canvas 	绘制二维码canvasID
	 * @param {Object} data 	绘制二维码内容
	 * @param {Object} camera 扫描二维码是否只使用相机
	 * @param {Object} success 	成功回调
	 * @param {Object} error 	失败回调
	 */
	qrCodeOperat({
		action = 'qrcode',
		canvas,
		data,
		camera = false,
		success,
		error
	}) {
		var that = this;
		switch (action) {
			case 'qrcode':
				qrCode({
					width: 180,
					height: 180,
					x: 20,
					y: 20,
					canvasId: canvas,
					text: data,
				});
				break;
			case 'scan':
				uni.scanCode({
					onlyFromCamera: camera,
					success(res) {
						if (res.errMsg == 'scanCode:ok') {
							// 二维码内容 res.result
							success && success(res.result);
						} else {
							error && error(res);
						}
					},
					fail(res) {
						if (res.errMsg != 'scanCode:fail cancel') {
							error && error(res);
						}
					}
				});
				break;
			default:
				that.modal('请明确二维码操作类型');
		}
	},

	/**
	 * 图片操作
	 * @param {Object} path		图片路径
	 * @param {Object} action 	操作类型 
	 *  choose:选择图片
	 *  upload:图片上传
	 * 	info:图片信息 
	 * 	read:读取图片（base64）
	 *  write:保存图片（base64）
	 * 	down:下载图片
	 *  save:保存图片（临时路径）
	 *  preview:图片预览
	 * @param {Object} data 	保存图片:图片base64内容（不带 data:image/png;base64,）,
	 * 							图片预览:图片数组
	 * @param {Object} fileName 保存图片的名称
	 * @param {Object} menu 	图片预览:是否显示长按菜单
	 * @param {Object} count 	图片选择:默认图片选择数量
	 * @param {Object} success 	成功回调
	 * @param {Object} error 	失败回调
	 */
	imageOperat({
		action = 'choose',
		path,
		fileName = 'write',
		data,
		count = 1,
		menu = true,
		success,
		error
	}) {
		var that = this;
		if (action == 'choose') {
			uni.chooseImage({
				count: count,
				sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图，默认二者都有
				sourceType: ['camera', 'album'], //相册选择、使用相机
				success: (e) => {
					e.tempFilePaths.forEach((item) => {
						success && success(item);
					});
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'upload') {
			uni.uploadFile({
				url: that.config.baseUrl + '/api/Publics/uploadLocality',
				filePath: path,
				name: 'upfile',
				/* header: {
					'content-type': 'multipart/form-data'
				}, */
				formData: {
					// uid: that.getUserInfo('id', 0)
					file: path
				},
				success(e) {
					console.log(e)
					let res = JSON.parse(e.data);
					if (res.status == 1) {
						success && success(res);
					} else {
						error && error({
							errMsg: res.info
						});
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'info') {
			uni.getImageInfo({
				src: path,
				success(e) {
					if (e.errMsg == 'getImageInfo:ok') {
						//e.height e.width e.type
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'read') {
			uni.getFileSystemManager().readFile({
				filePath: path,
				encoding: 'base64',
				position: 0,
				success(e) {
					if (e.errMsg == 'readFile:ok') {
						//let img = 'data:image/png;base64,' + e.data.replace(/[\r\n]/g, "");
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e_t) {
					error && error(e);
				}
			});
		} else if (action == 'write') {
			let filePath = `${uni.env.USER_DATA_PATH}/${fileName}.png`;
			uni.getFileSystemManager().writeFile({
				filePath: filePath,
				data: data,
				encoding: 'base64',
				success(e) {
					if (e.errMsg == 'writeFile:ok') {
						success && success(filePath);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					console.log(e);
					error && error(e);
				}
			});
		} else if (action == 'down') {
			uni.downloadFile({
				url: path,
				success(e) {
					if (e.errMsg === 'downloadFile:ok') {
						//e.tempFilePath
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			})
		} else if (action == 'save') {
			let filePath = `${uni.env.USER_DATA_PATH}/${fileName}.png`;
			uni.getFileSystemManager().saveFile({
				tempFilePath: path,
				filePath: filePath,
				success(e) {
					if (e.errMsg == 'saveFile:ok') {
						success && success(filePath);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					console.log(e);
					error && error(e);
				}
			});
		} else if (action == 'preview') {
			uni.previewImage({
				currnet: path,
				urls: data,
				showmenu: menu
			});
		}
	},
	/**
	 * 视频操作
	 */
	viedoOperat({
		action = 'choose',
		count = 1, //上传视频的个数
		mediaType = ['video'], //限制上传的类型为video
		sourceType = ['album', 'camera'], //视频选择来源
		maxDuration = 58, //拍摄限制时间
		camera = 'back', //采用后置摄像头
		filePath,
		path,
		size = 20,
		success,
		error
	}) {
		var that = this
		if (action == 'choose') {
			uni.chooseMedia({
				count: count,
				mediaType: mediaType,
				sourceType: sourceType,
				maxDuration: maxDuration,
				camera: camera,
				success(e) {
					console.log(e)
					if (e.errMsg == 'chooseMedia:ok') {
						e.tempFiles.forEach((item) => {
							let sel_size = parseFloat(item.size / 1024 / 1024).toFixed(1)
							if (sel_size > size) {
								let beyongSize = sel_size - size //获取视频超出限制大小的数量
								that.info("上传的视频大小超限,超出" + beyongSize + "MB,请重新上传！")
								error && error();
							} else {
								success && success(item);
							}
						});
					} else {
						error && error(e);
					}
				},
				fail(e) {
					console.log(e);
					error && error(e);
				}
			})
		}
		//压缩视频
		if (action == 'compress') {
			uni.compressVideo({
				quality: 'high',
				src: filePath,
				success(e) {
					console.log(e)
					if (e.errMsg == 'compressVideo:ok') {
						// e.tempFiles.forEach((item) => {
						success && success(e);
						// });
					} else {
						error && error(e);
					}
				},
				fail(e) {
					console.log(e);
					error && error(e);
				}
			})
		}
	},
	/**
	 * 位置服务
	 * @param {Object} action 	操作类型 
	 * 	getLocation: 获取当前的地理位置、速度
	 *  openLocation: 使用微信内置地图查看位置
	 *  chooseLocation: 打开地图选择位置
	 *  choosePoi: 打开 POI 列表选择位置
	 * 	getLatLng: 获取指定地址经纬度
	 *  getAreaCode: 根据经纬度获取区划信息
	 *  getDistance: 计算2个经纬度之间的距离
	 * @param {Object} type 	返回坐标类型: wgs84返回gps坐标，gcj02 返回可用于 uni.openLocation 的坐标
	 * @param {Object} address 	查询地址: '北京市海淀区彩和坊路海淀西大街74号'
	 * @param {Object} from 	计算距离起点坐标，默认当前位置
	 * {latitude: 0, longitude: 0}
	 * @param {Object} to 		计算距离终点坐标
	 * [{latitude: 0, longitude: 0}]
	 * @param {Object} success 	成功回调
	 * @param {Object} error 	失败回调
	 */
	locationOperat({
		action = 'getLocation',
		type = 'gcj02',
		address,
		from = '',
		to,
		success,
		error
	}) {
		var that = this;
		if (action == 'getLocation') {
			uni.getLocation({
				type: type,
				success(e) {
					if (e.errMsg == 'getLocation:ok') {
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'openLocation') {
			var {
				latitude,
				longitude
			} = from;
			uni.openLocation({
				latitude: latitude,
				longitude: longitude,
				success(e) {
					if (e.errMsg == 'openLocation:ok') {
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'chooseLocation') {
			var {
				latitude = '',
					longitude = ''
			} = from;
			uni.chooseLocation({
				latitude: latitude,
				longitude: longitude,
				success(e) {
					if (e.errMsg == 'chooseLocation:ok') {
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'choosePoi') {
			var {
				latitude = '',
					longitude = ''
			} = from;
			uni.choosePoi({
				success(e) {
					if (e.errMsg == 'choosePoi:ok') {
						success && success(e);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'getLatLng') {
			new qqMap({
				key: that.config.qqMapKey
			}).geocoder({
				address: address,
				success(e) {
					if (e.status == 0 && e.message == 'query ok') {
						success && success(e.result);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'getAreaCode') {
			new qqMap({
				key: that.config.qqMapKey
			}).reverseGeocoder({
				success(e) {
					if (e.status == 0 && e.message == 'query ok') {
						success && success(e.result);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'getDistanceApi') {
			new qqMap({
				key: that.config.qqMapKey
			}).calculateDistance({
				from: from, //若起点有数据则采用起点坐标，若为空默认当前地址
				to: to,
				success(e) {
					if (e.status == 0 && e.message == 'query ok') {
						success && success(e.result.elements);
					} else {
						error && error(e);
					}
				},
				fail(e) {
					error && error(e);
				}
			});
		} else if (action == 'getDistanceMath') { // 返回KM
			var {
				latitude: lat1 = '',
				longitude: lng1 = ''
			} = from;
			if (lat1 == '' && lng1 == '') {
				return 0;
			}
			var {
				latitude: lat2 = '',
				longitude: lng2 = ''
			} = to;
			var toRad = function(d) {
				return d * Math.PI / 180;
			};
			var dis = 0;
			var deltaLng = toRad(lng1) - toRad(lng2);
			var radLat1 = toRad(lat1);
			var radLat2 = toRad(lat2);
			var deltaLat = radLat1 - radLat2;
			var dis = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(radLat1) * Math.cos(
				radLat2) * Math.pow(Math.sin(deltaLng / 2), 2)));
			return that.math.toFixed(dis * 6378.137, 2); // km
		}
	},

	/**
	 * 过滤emoji
	 * @param {Object} str
	 */
	filterEmoji: function(str) {
		return str.replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, '');
	},

	/**
	 * 打印对象
	 * @param obj
	 */
	jslog: function(obj) {
		this.config.debug && console.log(JSON.stringify(obj));
	},

	/**
	 * form表单数据校验
	 * @param value
	 * @param rule
	 */
	formCheck: function(value, rule, error) {
		var that = this;
		var regs = {
			required: [
				/[\s\S]+/,
				'必填项不能为空',
			],
			english: [
				/^[a-zA-Z]+$/,
				'必填项只能由大小写字母组成',
			],
			mobile: [
				/^1[3|4|5|7|8|9][0-9]\d{4,8}$/,
				'请输入正确的手机号',
			],
			phone: [
				/^(\d{3,4}-?)?\d{7,9}$/g,
				'请输入正确的电话号码',
			],
			email: [
				/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
				'请输入正确的电子邮箱'
			],
			url: [
				/(^#)|(^http(s*):\/\/[^\s]+\.[^\s]+)/,
				'请输入正确的链接'
			],
			number: [
				/^\d+$/,
				'必填项只能填数字'
			],
			code4: [
				/^\d{4}$/,
				'请输入正确的手机验证码'
			],
			code6: [
				/^\d{6}$/,
				'请输入正确的手机验证码'
			],
			captcha4: [
				/^[0-9a-zA-Z]{4}$/,
				'请输入正确的图片验证码'
			],
			date: [
				/^(\d{4})[-\/](\d{1}|0\d{1}|1[0-2])([-\/](\d{1}|0\d{1}|[1-2][0-9]|3[0-1]))*$/,
				'请输入正确的日期格式'
			],
			identity: [
				/(^\d{15}$)|(^\d{17}(x|X|\d)$)/,
				'请输入正确的身份证号'
			],
			username: [
				/^[a-zA-Z0-9\u4e00-\u9fa5-_\.]{2,12}$/,
				'请输入正确的帐号（只能包含中文、英文、数字、下划线等字符）'
			],
			password: [
				/^[a-zA-Z]+\w{5,12}$/,
				'请输入正确的密码（以字母开头，长度在6-12之间）'
			],
			passwords: [
				/^[a-zA-Z]+\w{5,12}$/,
				'请输入正确的密码（以字母开头，长度在6-12之间）'
			],
			pwd: [
				/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9a-zA-Z]{6,12}$/,
				'请输入正确的密码（6-12位数字+字母组合）'
			],
			invite: [
				/^[0-9a-zA-Z]{10}$/,
				'邀请码为8位数字或字母组合'
			],
			truename: [
				/^[\u4e00-\u9fa5]{2,4}$/,
				'请输入正确的真实姓名（2-4个汉字）'
			],
			money: [
				/((^[1-9]\d*)|^0)(\.\d{0,2}){0,1}$/,
				'请输入正确的金额'
			]
		};
		var reg = regs[rule];
		if (that.isEmpty(reg)) {
			return true;
		} else {
			if (value == '' || !RegExp(reg[0]).test(value)) {
				that.info(error || reg[1]);
				return false;
			}
			return true;
		}
	},

	/****************************************************/

	/**
	 *获取系统信息
	 * @param label 获取系统的某一项信息
	 * screenWidth		屏幕宽度，
	 * screenHeight 	屏幕高度，单位px
	 * windowWidth		单位px可使用窗口宽度，单位px
	 * windowHeight		可使用窗口高度，单位px
	 * statusBarHeight	状态栏的高度，单位px
	 * menuButton：右上角胶囊按钮的布局位置信息，坐标信息以屏幕左上角为原点
	 * width			宽度，单位：px
	 * height			高度，单位：px
	 * top				上边界坐标，单位：px
	 * right			右边界坐标，单位：px
	 * bottom			下边界坐标，单位：px
	 * left				左边界坐标，单位：px
	 */
	getSystemInfo(success) {
		// 获取系统状态栏信息
		uni.getSystemInfo({
			success: (res) => {
				if (res.errMsg == 'getSystemInfo:ok') {
					res.menuButton = uni.getMenuButtonBoundingClientRect();
					success && success(res);
				} else {
					that.modal('获取系统信息失败：' + res.errMsg);
				}
			},
			fail: (res) => {
				that.modal('获取系统信息失败：' + res.errMsg);
			}
		});
	},

	/**
	 * 检查登录
	 */
	getSessionKey: function(success) {
		let that = this;
		let data = {};
		data.openid = cache.get('open_id');
		data.session_key = cache.get('session_key');
		// 都存在
		if (!that.isEmpty(data.session_key) && !that.isEmpty(data.openid)) {
			uni.checkSession({
				success() {
					success && success(data);
				},
				fail() {
					cache.remove('open_id');
					cache.remove('session_key');
					that.getOpenId(success);
				}
			});
		} else {
			cache.remove('open_id');
			cache.remove('session_key');
			that.getOpenId(success);
		}
	},

	/**
	 * 获取openid
	 * @param success
	 */
	getOpenId: function(success, error) {
		var that = this;
		that.loading();
		uni.login({
			success: (e) => {
				that.req({
					url: 'public/getOpenid',
					data: {
						code: e.code
					},
					login: false,
					load: false
				}, function(res) {
					if (res.status == 1) {
						cache.set('open_id', res.data.openid);
						cache.set('session_key', res.data.session_key);
						success && success(res.data);
					} else {
						that.modal('微信登录失败：' + res.info, function() {
							error && error();
						});
						return false;
					}
				}, function() {}, function() {
					that.closeLoading();
				});
			},
			fail: (e) => {
				that.closeLoading();
				that.modal('微信登录失败：' + e.errMsg, function() {
					error && error();
				});
			}
		});
	},

	/**
	 * 登录
	 * @param {Object} data
	 * @param {Object} success
	 * @param {Object} error
	 */
	doLogin: function(data, success, error) {
		var that = this;
		data.loginsubmit = 'yes';
		this.req({
			url: 'account/login',
			data: data,
			login: false
		}, function(res) {
			if (res.status == 1) {
				cache.set('user_info', res.data);
				success && success(res.data);
			} else {
				error && error(res);
			}
		});
	},

	/**
	 * 注册
	 * @param {Object} data
	 * @param {Object} success
	 * @param {Object} error
	 */
	doRegist: function(data, success, error) {
		var that = this;
		data.registsubmit = 'yes';
		this.req({
			url: 'account/register',
			data: data,
			login: false
		}, function(res) {
			if (res.status == 1) {
				cache.set('user_info', res.data);
				success && success(res.data);
			} else {
				error && error(res);
			}
		});
	},

	/**
	 * 查询是否注册
	 * @param {Object} success
	 * @param {Object} error
	 */
	checkRegist: function(success, error) {
		var that = this;
		that.getSessionKey(function(data) {
			that.req({
				url: 'account/checkRegist',
				data: {
					open_id: data.openid,
					registsubmit: 'yes'
				},
				login: false,
				load: false
			}, function(res) {
				if (res.status == 1) {
					success && success();
				} else {
					error && error();
				}
			});
		});
	},

	/**
	 * 获取会员信息
	 * @param {Object} success
	 * @param {Object} error
	 */
	getMemberInfo: function(success, error) {
		var that = this;
		this.req({
			url: 'center/getMemberInfo',
			load: false
		}, function(res) {
			if (res.status == 1) {
				cache.set('user_info', res.data);
				success && success(res.data);
			} else {
				error && error(res);
			}
		});
	},

	/**
	 * 获取用户微信资料
	 * @param success
	 */
	getUserProfile: function(success, error) {
		var that = this;
		that.loading('获取微信信息中...');
		uni.getUserProfile({
			lang: 'zh_CN',
			desc: '用于完善会员资料',
			success: (res) => {
				if (res.errMsg == 'getUserProfile:ok') {
					// 过滤掉emoji
					res.userInfo.nickName = that.filterEmoji(res.userInfo.nickName);
					success(res.userInfo);
				} else {
					that.modal('获取微信信息失败：' + res.errMsg, function() {
						error && error(res);
					});
				}
			},
			fail: (res) => {
				that.modal('获取微信信息失败：' + res.errMsg, function() {
					console.log(res)
					error && error(res);
				});
			},
			complete: () => {
				that.closeLoading();
			}
		});
	},

	/**
	 * 获取配置信息
	 * @param success
	 */
	getConfigInfo: function({
		source = 'system',
		label = '',
		success
	}) {
		this.req({
			url: 'public/configInfo',
			data: {
				source: source,
				label: label
			},
			login: false,
			load: false
		}, function(res) {
			success(res.data);
		});
	},

	/**
	 * 获取微信手机号
	 * @param e
	 * @param success
	 */
	getMobile: function(e, success) {
		var that = this;
		that.getSessionKey(function(data) {
			if (e.detail.errMsg == 'getPhoneNumber:ok') {
				that.req({
					url: 'public/getMobile',
					data: {
						encryptedData: e.detail.encryptedData,
						iv: e.detail.iv,
						sessionKey: data.session_key
					},
					login: false
				}, function(res) {
					if (res.status == 1) {
						success(res.data);
					} else {
						that.modal('获取手机号失败' + res.info);
						return false;
					}
				});
			} else {
				that.modal('获取手机号失败' + e.detail.errMsg);
			}
		});
	},

	/**
	 * 拨打电话
	 * @param mobile
	 */
	makeCall: function(mobile) {
		this.modal('确定拨打' + mobile, function() {
			uni.makePhoneCall({
				phoneNumber: mobile
			});
		}, true);
	},

	/**
	 * 修改页面标题
	 * @param title
	 */
	changeNavBarTitle: function(title) {
		uni.setNavigationBarTitle({
			title: title
		});
	},

	/**
	 * 检查版本更新
	 */
	checkUpdate: function() {
		if (uni.canIUse('getUpdateManager')) {
			const updateManager = uni.getUpdateManager()
			updateManager.onCheckForUpdate(function(res) {
				if (res.hasUpdate) {
					updateManager.onUpdateReady(function() {
						uni.showModal({
							title: '更新提示',
							content: '新版本已经准备好，是否重启应用？',
							success: function(res) {
								if (res.confirm) {
									updateManager.applyUpdate()
								}
							}
						})
					})
					updateManager.onUpdateFailed(function() {
						uni.showModal({
							title: '已经有新版本了~',
							content: '新版本已经上线啦~，请您删除当前小程序，重新搜索打开~'
						})
					})
				}
			})
		} else {
			uni.showModal({
				title: '提示',
				content: '当前微信版本过低，无法使用该功能，请升级到最新微信版本后重试。'
			})
		}
	},
	/**
	 * html转义
	 * @param {object} str    //需要转义内容
	 * @param {object} success  
	 */
	escape2Html(str, success) {
		var arrEntities = {
			'lt': '<',
			'gt': '>',
			'nbsp': ' ',
			'amp': '&',
			'quot': '"'
		};
		return str.replace(/&(lt|gt|nbsp|amp|quot);/ig, function(all, t) {
			return arrEntities[t];
		}).replace(/<section/g, '<div').replace(/<u>/g, '').replace(/<u style="">/g, '').replace(/<\/u>/g, '');
	},
	/**
	 * 分享信息
	 */
	shareInfo: function() {
		var that = this;
		var userInfo = cache.get('user_info');
		if (!that.isEmpty(userInfo) && !that.isEmpty(userInfo.serial)) {
			var path = `/pages/index/index?invite_code=${userInfo.serial}`;
		} else {
			var path = '/pages/index/index';
		}
		return {
			title: that.config.name,
			path: path,
			// imageUrl: '/image/iconimg/login.png'
		};
	},
	/**
	 * 检测是否收藏
	 * @param {object} id    //检测是否收藏的商品id
	 * @param {object} success  
	 */
	checkStore(id, key, success) {
		var that = this
		var shopStore = cache.get(key);
		if (that.isEmpty(shopStore)) {
			success && success(false)
		} else {
			var have = 0
			for (let i in shopStore) {
				var d = shopStore[i];
				if (d.id == id) {
					// 有收藏
					have = 1
					success && success(true)
					break;
				}
				if (have == 0) {
					// 无收藏
					success && success(false)
				}
			}
		}
	},
	/**
	 * 收藏缓存
	 * @param {object} id    //需要收藏或取消的商品id
	 * @param {object} success  
	 */
	changeStore(data, key, success) {
		var shopStore = cache.get(key);
		var that = this
		var p = {}
		p = data
		if (that.isEmpty(shopStore)) {
			var shopStore = []
			shopStore.push(p);
			cache.set(key, shopStore);
			success && success('收藏成功')
		} else {
			var have = 0
			for (let i in shopStore) {
				var d = shopStore[i];
				console.log(d, data)
				if (d.id == data.id && d.table == data.table) {
					// 有的话取消收藏
					have = 1
					shopStore.splice(i, 1);
					cache.set(key, shopStore);
					success && success('取消成功')
					break;
				}
			}
			if (have == 0) {
				// 没有的话收藏
				shopStore.push(p);
				cache.set(key, shopStore);
				success && success('收藏成功')
			}
		}
	},

	/****************************************************/

	/**
	 * 获取购物车信息,设置导航红点信息
	 * @param index 设置信息 tabBar 序号
	 */
	getshopcar: function(success) {
		var shopCar_list = cache.get('shop_car');
		if (this.isEmpty(shopCar_list)) {
			uni.hideTabBarRedDot({ //隐藏 tabBar 某一项的右上角文本
				index: 2, //第几个 tabBar，从左边 0 算起
				success: function() {
					success && success(0);
				}
			});
		} else {
			var len = this.getLength(shopCar_list);
			uni.setTabBarBadge({ //为 tabBar 某一项的右上角添加文本
				index: 2, //第几个 tabBar，从左边 0 算起
				text: String(len), //右上角添加的文本
				success: function() {
					success && success(len);
				}
			});
		}
	},
	/**
	 * 上拉刷新加载数据
	 * @param {object} type
	 * @param {object} page
	 * @param {object} succes
	 */
	onReachFoot: function(url, data, succes) {
		//type=1时请求找工作
		if (url) {
			this.req({
				url: url,
				data: data
			}, function(res) {
				if (res.status == 1) {
					succes && succes(res.data);
				}
			})
		} else {
			succes && success(false)
		}
	},
	/**
	 * 电话聊天控制
	 * @param price 价格
	 * @param mobile 手机号
	 * @param work_id 工作或求职id
	 * @param {String} type 类型,work招工，worker求职
	 * @param {String} buy_type 购买类型 pirce电话，chat_price聊天
	 * @param send_id 被购买的用户id
	 * @param send_name 被购买的名字
	 */
	makeCall(price, mobile, work_id, type, buy_type, send_id, send_name = '') {
		var that = this
		that.modal('将自动使用会员次数或东豆进行抵扣', function(params) {
			that.req({
				url: 'center/MobileOrMessage',
				data: {
					price: price,
					mobile: mobile,
					work_id: work_id,
					type: type,
					buy_type: buy_type,
					send_id: send_id,
					send_name: send_name
				}
			}, function(params) {
				if (params.status == 1) {
					if (buy_type == 'price') {
						uni.makePhoneCall({
							phoneNumber: mobile,
						})
					} else {
						that.jump('/pages/chat/chat?id=' + send_id + '&nickname=' + send_name)
					}
				}
				if (params.status == 2) {

				}
			})
		}, true)
	},
	/**
	 * 微信小程序防抖
	 * @param func 防抖事件
	 * @param wait 阻止时间
	 */
	debounce: function(func, wait) {
		let timeout;
		return function() {
			console.log('防抖事件：' + func, '防抖时间:' + wait)
			let context = this
			let args = arguments
			let later = () => {
				timeout = null
				func.apply(context, args)
			}
			clearTimeout(timeout)
			timeout = setTimeout(later, wait)
		}
	}
}
