PageSourceSearch

https://www.kjq123.com/source/plugin/tb_cus_navigation/static/js/xiuno.js

js kjq123.com collected 2026-10-10 01:58:11 UTC 58,518 bytes, 1,959 lines download raw bytes

1/*
2* xiuno.js 封装了部分 PHP 常用的函数,便于代码移植和使用。
3* 技术支持: http://bbs.xiuno.com/
4*/
5
6/********************* 对 window 对象进行扩展 ************************/
7// 兼容 ie89
8if(!Object.keys) {
9	Object.keys = function(o) {
10		var arr = [];
11		for(var k in o) {
12			if(o.hasOwnProperty(k)) arr.push(k);
13		}
14		return arr;
15	}
16}
17if(!Object.values) {
18	Object.values = function(o) {
19		var arr = [];
20		if(!o) return arr;
21		for(var k in o) {
22			if(o.hasOwnProperty(k)) arr.push(o[k]);
23		}
24		return arr;
25	}
26}
27Array.values = function(arr) {
28	return xn.array_filter(arr);
29};
30
31Object.first = function(obj) {
32	for(var k in obj) return obj[k];
33};
34Object.last = function(obj) {
35	for(var k in obj);
36	return obj[k];
37};
38Object.length = function(obj) {
39	var n = 0;
40	for(var k in obj) n++;
41	return n;
42};
43Object.count = function(obj) {
44	if(!obj) return 0;
45	if(obj.length) return obj.length;
46	var n = 0;
47	for(k in obj) {
48		if(obj.hasOwnProperty(k)) n++;
49	}
50	return n;
51};
52Object.sum = function(obj) {
53	var sum = 0;
54	jQuery.each(obj, function(k, v) {sum += intval(v)});
55	return sum;
56};
57if(typeof console == 'undefined') {
58	console = {};
59	console.log = function() {};
60}
61
62/********************* xn 模拟 php 函数 ************************/
63
64// var xn = window; // browser, 如果要兼容以前的版本,请开启这里。
65// var xn = global; // nodejs
66var xn = {}; // 避免冲突,自己的命名空间。
67
68// 针对国内的山寨套壳浏览器检测不准确
69xn.is_ie = (!!document.all) ? true : false;// ie6789
70xn.is_ie_10 = navigator.userAgent.indexOf('Trident') != -1;
71xn.is_ff = navigator.userAgent.indexOf('Firefox') != -1;
72xn.in_mobile = (jQuery(window).width() < 1140);
73xn.options = {}; // 全局配置
74xn.options.water_image_url = 'view/img/water-small.png';// 默认水印路径
75
76xn.htmlspecialchars = function(s) {
77	s = s.replace(/</g, "&lt;");
78	s = s.replace(/>/g, "&gt;");
79	return s;
80};
81
82// 标准的 urlencode()
83xn._urlencode = function(s) {
84	s = encodeURIComponent(s);
85	s = xn.strtolower(s);
86	return s;
87};
88
89// 标准的 urldecode()
90xn._urldecode = function(s) {
91	s = decodeURIComponent(s);
92	return s;
93};
94
95xn.urlencode = function(s) {
96	s = encodeURIComponent(s);
97	s = s.replace(/_/g, "%5f");
98	s = s.replace(/\-/g, "%2d");
99	s = s.replace(/\./g, "%2e");
100	s = s.replace(/\~/g, "%7e");
101	s = s.replace(/\!/g, "%21");
102	s = s.replace(/\*/g, "%2a");
103	s = s.replace(/\(/g, "%28");
104	s = s.replace(/\)/g, "%29");
105	//s = s.replace(/\+/g, "%20");
106	s = s.replace(/\%/g, "_");
107	return s;
108};
109
110xn.urldecode = function(s) {
111	s = s.replace(/_/g, "%");
112	s = decodeURIComponent(s);
113	return s;
114};
115
116// 兼容 3.0
117xn.xn_urlencode = xn.urlencode_safe;
118xn.xn_urldecode = xn.urldecode_safe;
119
120xn.nl2br = function(s) {
121	s = s.replace(/\r\n/g, "\n");
122	s = s.replace(/\n/g, "<br>");
123	s = s.replace(/\t/g, "&nbsp; &nbsp; &nbsp; &nbsp; ");
124	return s;
125};
126
127xn.time = function() {
128	return xn.intval(Date.now() / 1000);
129};
130
131xn.intval = function(s) {
132	var i = parseInt(s);
133	return isNaN(i) ? 0 : i;
134};
135
136xn.floatval = function(s) {
137	if(!s) return 0;
138	if(s.constructor === Array) {
139		for(var i=0; i<s.length; i++) {
140			s[i] = xn.floatval(s[i]);
141		}
142		return s;
143	}
144	var r = parseFloat(s);
145	return isNaN(r) ? 0 : r;
146};
147
148xn.isset = function(k) {
149	var t = typeof k;
150	return t != 'undefined' && t != 'unknown';
151};
152
153xn.empty = function(s) {
154	if(s == '0') return true;
155	if(!s) {
156		return true;
157	} else {
158		//jQuery.isPlainObject
159		if(s.constructor === Object) {
160			return Object.keys(s).length == 0;
161		} else if(s.constructor === Array) {
162			return s.length == 0;
163		}
164		return false;
165	}
166};
167
168xn.ceil = Math.ceil;
169xn.round = Math.round;
170xn.floor = Math.floor;
171xn.f2y = function(i, callback) {
172	if(!callback) callback = round;
173	var r = i / 100;
174	return callback(r);
175};
176xn.y2f = function(s) {
177	var r = xn.round(xn.intval(s) * 100);
178	return r;
179};
180xn.strtolower = function(s) {
181	s += '';
182	return s.toLowerCase();
183};
184xn.strtoupper = function(s) {
185	s += '';
186	return s.toUpperCase();
187};
188
189xn.json_type = function(o) {
190	var _toS = Object.prototype.toString;
191	var _types = {
192		'undefined': 'undefined',
193		'number': 'number',
194		'boolean': 'boolean',
195		'string': 'string',
196		'[object Function]': 'function',
197		'[object RegExp]': 'regexp',
198		'[object Array]': 'array',
199		'[object Date]': 'date',
200		'[object Error]': 'error'
201	};
202	return _types[typeof o] || _types[_toS.call(o)] || (o ? 'object' : 'null');
203};
204
205xn.json_encode = function(o) {
206	var json_replace_chars = function(chr) {
207		var specialChars = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };
208		return specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
209	};
210
211	var s = [];
212	switch (xn.json_type(o)) {
213		case 'undefined':
214			return 'undefined';
215			break;
216		case 'null':
217			return 'null';
218			break;
219		case 'number':
220		case 'boolean':
221		case 'date':
222		case 'function':
223			return o.toString();
224			break;
225		case 'string':
226			return '"' + o.replace(/[\x00-\x1f\\"]/g, json_replace_chars) + '"';
227			break;
228		case 'array':
229			for (var i = 0, l = o.length; i < l; i++) {
230				s.push(xn.json_encode(o[i]));
231			}
232			return '[' + s.join(',') + ']';
233			break;
234		case 'error':
235		case 'object':
236			for (var p in o) {
237				s.push('"' + p + '"' + ':' + xn.json_encode(o[p]));
238			}
239			return '{' + s.join(',') + '}';
240			break;
241		default:
242			return '';
243			break;
244	}
245};
246
247xn.json_decode = function(s) {
248	if(!s) return null;
249	try {
250		// 去掉广告代码。这行代码挺无语的,为了照顾国内很多人浏览器中广告病毒的事实。
251		// s = s.replace(/\}\s*<script[^>]*>[\s\S]*?<\/script>\s*jQuery/ig, '}');
252		if(s.match(/^<!DOCTYPE/i)) return null;
253		var json = jQuery.parseJSON(s);
254		return json;
255	} catch(e) {
256		//alert('JSON格式错误:' + s);
257		//window.json_error_string = s;	// 记录到全局
258		return null;
259	}
260};
261
262xn.clone = function(obj) {
263        return xn.json_decode(xn.json_encode(obj));
264}
265
266// 方便移植 PHP 代码
267xn.min = function() {return Math.min.apply(this, arguments);}
268xn.max = function() {return Math.max.apply(this, arguments);}
269xn.str_replace = function(s, d, str) {var p = new RegExp(s, 'g'); return str.replace(p, d);}
270xn.strrpos = function(str, s) {return str.lastIndexOf(s);}
271xn.strpos = function(str, s) {return str.indexOf(s);}
272xn.substr = function(str, start, len) {
273	// 支持负数
274	if(!str) return '';
275	var end = length;
276	var length = str.length;
277	if(start < 0) start = length + start;
278	if(!len) {
279		end = length;
280	} else if(len > 0) {
281		end = start + len;
282	} else {
283		end = length + len;
284	}
285	return str.substring(start, end);
286};
287xn.explode = function(sep, s) {return s.split(sep);}
288xn.implode = function(glur, arr) {return arr.join(glur);}
289xn.array_merge = function(arr1, arr2) {return arr1 && arr1.__proto__ === Array.prototype && arr2 && arr2.__proto__ === Array.prototype ? arr1.concat(arr2) : jQuery.extend(arr1, arr2);}
290// 比较两个数组的差异,在 arr1 之中,但是不在 arr2 中。返回差异结果集的新数组,
291xn.array_diff = function(arr1, arr2) {
292	if(arr1.__proto__ === Array.prototype) {
293		var o = {};
294		for(var i = 0, len = arr2.length; i < len; i++) o[arr2[i]] = true;
295		var r = [];
296		for(i = 0, len = arr1.length; i < len; i++) {
297			var v = arr1[i];
298			if(o[v]) continue;
299			r.push(v);
300		}
301		return r;
302	} else {
303		var r = {};
304		for(k in arr1) {
305			if(!arr2[k]) r[k] = arr1[k];
306		}
307		return r;
308	}
309};
310// 过滤空值,可以用于删除
311/*
312	// 第一种用法:
313	var arr = [0,1,2,3];
314	delete arr[1];
315	delete arr[2];
316	arr = array_filter(arr);
317	
318	// 第二种:
319	var arr = [0,1,2,3];
320	array_filter(arr, function(k,v) { k == 1} );
321*/
322xn.array_filter = function(arr, callback) {
323	var newarr = [];
324	for(var k in arr) {
325		var v = arr[k];
326		if(callback && callback(k, v)) continue;
327		// if(!callback && v === undefined) continue; // 默认过滤空值
328		newarr.push(v);
329	}
330	return newarr;
331};
332xn.array_keys = function(obj) {
333	var arr = [];
334	jQuery.each(obj, function(k) {arr.push(k);});
335	return arr;
336};
337xn.array_values = function(obj) {
338	var arr = [];
339	jQuery.each(obj, function(k, v) {arr.push(v);});
340	return arr;
341};
342xn.in_array = function(v, arr) { return jQuery.inArray(v, arr) != -1;}
343
344xn.rand = function(n) {
345	var str = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
346	var r = '';
347	for (i = 0; i < n; i++) {
348		r += str.charAt(Math.floor(Math.random() * str.length));
349	}
350	return r;
351};
352
353xn.random = function(min, max) {
354	var num = Math.random()*(max-min + 1) + min;
355	var r = Math.ceil(num);
356	return r;
357};
358
359// 所谓的 js 编译模板,不过是一堆效率低下的正则替换,这种东西根据自己喜好用吧。
360xn.template = function(s, json) {
361	//console.log(json);
362	for(k in json) {
363		var r = new RegExp('\{('+k+')\}', 'g');
364		s = s.replace(r, function(match, name) {
365			return json[name];
366		});
367	}
368	return s;
369};
370
371xn.is_mobile = function(s) {
372	var r = /^\d{11}jQuery/;
373	if(!s) {
374		return false;
375	} else if(!r.test(s)) {
376		return false;
377	}
378	return true;
379};
380
381xn.is_email = function(s) {
382	var r = /^[\w\-\.]+@[\w\-\.]+(\.\w+)+jQuery/i
383	if(!s) {
384		return false;
385	} else if(!r.test(s)) {
386		return false;
387	}
388	return true;
389};
390
391xn.is_string = function(obj) {return Object.prototype.toString.apply(obj) == '[object String]';};
392xn.is_function = function(obj) {return Object.prototype.toString.apply(obj) == '[object Function]';};
393xn.is_array = function(obj) {return Object.prototype.toString.apply(obj) == '[object Array]';};
394xn.is_number = function(obj) {return Object.prototype.toString.apply(obj) == '[object Number]' || /^\d+jQuery/.test(obj);};
395xn.is_regexp = function(obj) {return Object.prototype.toString.apply(obj) == '[object RegExp]';};
396xn.is_object = function(obj) {return Object.prototype.toString.apply(obj) == '[object Object]';};
397xn.is_element = function(obj) {return !!(obj && obj.nodeType === 1);};
398
399xn.lang = function(key, arr) {
400	var r = lang[key] ? lang[key] : "lang["+key+"]";
401	if(arr) {
402		jQuery.each(arr, function(k, v) { r = xn.str_replace("{"+k+"}", v, r);});	
403	}
404	return r;
405};
406
407/* 
408	js 版本的翻页函数
409*/
410// 用例:pages('user-list-{page}.htm', 100, 10, 5);
411xn.pages = function (url, totalnum, page, pagesize) {
412	if(!page) page = 1;
413	if(!pagesize) pagesize = 20;
414	var totalpage = xn.ceil(totalnum / pagesize);
415	if(totalpage < 2) return '';
416	page = xn.min(totalpage, page);
417	var shownum = 5;	// 显示多少个页 * 2
418
419	var start = xn.max(1, page - shownum);
420	var end = xn.min(totalpage, page + shownum);
421
422	// 不足 jQueryshownum,补全左右两侧
423	var right = page + shownum - totalpage;
424	if(right > 0) start = xn.max(1, start -= right);
425	left = page - shownum;
426	if(left < 0) end = xn.min(totalpage, end -= left);
427
428	var s = '';
429	if(page != 1) s += '<a href="'+xn.str_replace('{page}', page-1, url)+'">?</a>';
430	if(start > 1) s += '<a href="'+xn.str_replace('{page}', 1, url)+'">1 '+(start > 2 ? '... ' : '')+'</a>';
431	for(i=start; i<=end; i++) {
432		if(i == page) {
433			s += '<a href="'+xn.str_replace('{page}', i, url)+'" class="active">'+i+'</a>';// active
434		} else {
435			s += '<a href="'+xn.str_replace('{page}', i, url)+'">'+i+'</a>';
436		}
437	}
438	if(end != totalpage) s += '<a href="'+xn.str_replace('{page}', totalpage, url)+'">'+(totalpage - end > 1 ? '... ' : '')+totalpage+'</a>';
439	if(page != totalpage) s += '<a href="'+xn.str_replace('{page}', page+1, url)+'">?</a>';
440	return s;
441};
442
443xn.parse_url = function(url) {
444	if(url.match(/^(([a-z]+):)\/\//i)) {
445		var arr = url.match(/^(([a-z]+):\/\/)?([^\/\?#]+)\/*([^\?#]*)\??([^#]*)#?(\w*)jQuery/i);
446		if(!arr) return null;
447		var r = {
448			'schema': arr[2],
449			'host': arr[3],
450			'path': arr[4],
451			'query': arr[5],
452			'anchor': arr[6],
453			'requesturi': arr[4] + (arr[5] ? '?'+arr[5] : '') + (arr[6] ? '#'+arr[6] : '')
454		};
455		console.log(r);
456		return r;
457	} else {
458		
459		var arr = url.match(/^([^\?#]*)\??([^#]*)#?(\w*)jQuery/i);
460		if(!arr) return null;
461		var r = {
462			'schema': '',
463			'host': '',
464			'path': arr[1],
465			'query': arr[2],
466			'anchor': arr[3],
467			'requesturi': arr[1] + (arr[2] ? '?'+arr[2] : '')  + (arr[3] ? '#'+arr[3] : '')
468		};
469		console.log(r);
470		return r;
471	}
472};
473
474xn.parse_str = function (str){
475	var sep1 = '=';
476	var sep2 = '&';
477	var arr = str.split(sep2);
478	var arr2 = {};
479	for(var x=0; x < arr.length; x++){
480		var tmp = arr[x].split(sep1);
481		arr2[unescape(tmp[0])] = unescape(tmp[1]).replace(/[+]/g, ' ');
482	}
483	return arr2;
484};
485
486// 解析 url 参数获取 jQuery_GET 变量
487xn.parse_url_param = function(url) {
488	var arr = xn.parse_url(url);
489	var q = arr.path;
490	var pos = xn.strrpos(q, '/');
491	q = xn.substr(q, pos + 1);
492	var r = [];
493	if(xn.substr(q, -4) == '.htm') {
494		q = xn.substr(q, 0, -4);
495		r = xn.explode('-', q);
496	// 首页
497	} else if (url && url != window.location && url != '.' && url != '/' && url != './'){
498		r = ['thread', 'seo', url];
499	}
500
501	// 将 xxx.htm?a=b&c=d 后面的正常的 _GET 放到 jQuery_SERVER['_GET']
502	if(!empty(arr['query'])) {
503		var arr2 = xn.parse_str(arr['query']);
504		r = xn.array_merge(r, arr2);
505	}
506	return r;
507};
508
509// 从参数里获取数据
510xn.param = function(key) {
511
512};
513
514// 模拟服务端 url() 函数
515
516xn.url = function(u, url_rewrite) {
517	var on = window.url_rewrite_on || url_rewrite;
518	if(xn.strpos(u, '/') != -1) {
519		var path = xn.substr(u, 0, xn.strrpos(u, '/') + 1);
520		var query = xn.substr(u, xn.strrpos(u, '/') + 1);
521	} else {
522		var path = '';
523		var query = u;
524	}
525	var r = '';
526	if(!on) {
527		r = path + '?' + query + '.htm';
528	} else if(on == 1) {
529		r = path + query + ".htm";
530	} else if(on == 2) {
531		r = path + '?' + xn.str_replace('-', '/', query);
532	} else if(on == 3) {
533		r = path + xn.str_replace('-', '/', query);
534	}
535	return r;
536};
537
538// 将参数添加到 URL
539xn.url_add_arg = function(url, k, v) {
540	var pos = xn.strpos(url, '.htm');
541	if(pos === false) {
542		return xn.strpos(url, '?') === false ? url + "&" + k + "=" + v :  url + "?" + k + "=" + v;
543	} else {
544		return xn.substr(url, 0, pos) + '-' + v + xn.substr(url, pos);
545	}
546};
547
548// 页面跳转的时间
549//xn.jumpdelay = xn.debug ? 20000000 : 2000;
550
551
552/********************* 对 JQuery 进行扩展 ************************/
553
554jQuery.location = function(url, seconds) {
555	if(seconds === undefined) seconds = 1;
556	setTimeout(function() {window.location='./';}, seconds * (debug ? 2000 : 1000));
557};
558
559// 二级数组排序
560/*Array.prototype.proto_sort = Array.prototype.sort;
561Array.prototype.sort = function(arg) {
562	if(arg === undefined) {
563		return this.proto_sort();
564	} else if(arg.constructor === Function) {
565		return this.proto_sort(arg);
566	} else if(arg.constructor === Object) {
567		var k = Object.first(arg);
568		var v = arg[k];
569		return this.proto_sort(function(a, b) {return v == 1 ? a[k] > b[k] : a[k] < b[k];});
570	} else {
571		return this;
572	}
573}*/
574
575xn.arrlist_values = function(arrlist, key) {
576	var r = [];
577	arrlist.map(function(arr) { r.push(arr[key]); });
578	return r;
579};
580
581xn.arrlist_key_values = function(arrlist, key, val, pre) {
582	var r = {};
583	var pre = pre || '';
584	arrlist.map(function(arr) { r[arr[pre+key]] = arr[val]; });
585	return r;
586};
587
588xn.arrlist_keep_keys = function(arrlist, keys) {
589	if(!xn.is_array(keys)) keys = [keys];
590	for(k in arrlist) {
591		var arr = arrlist[k];
592		var newarr = {};
593		for(k2 in keys) {
594			var key = keys[k2];
595			newarr[key] = arr[key];
596		}
597		arrlist[k] = newarr;
598	}
599	return arrlist;
600}
601/*var arrlist = [
602	{uid:1, gid:3},
603	{uid:2, gid:2},
604	{uid:3, gid:1},
605];
606var arrlist2 = xn.arrlist_keep_keys(arrlist, 'gid');
607console.log(arrlist2);*/
608
609xn.arrlist_multisort = function(arrlist, k, asc) {
610	var arrlist = arrlist.sort(function(a, b) {
611		if(a[k] == b[k]) return 0;
612		var r = a[k] > b[k];
613		r = asc ? r : !r;
614		return r ? 1 : -1;
615	});
616	return arrlist;
617}
618/*
619var arrlist = [
620	{uid:1, gid:3},
621	{uid:2, gid:2},
622	{uid:3, gid:1},
623];
624var arrlist2 = xn.arrlist_multisort(arrlist, 'gid', false);
625console.log(arrlist2);
626*/
627
628// if(xn.is_ie) document.documentElement.addBehavior("#default#userdata");
629
630jQuery.pdata = function(key, value) {
631	var r = '';
632	if(typeof value != 'undefined') {
633		value = xn.json_encode(value);
634	}
635
636	// HTML 5
637	try {
638		// ie10 需要 try 一下
639		if(window.localStorage){
640			if(typeof value == 'undefined') {
641				r = localStorage.getItem(key);
642				return xn.json_decode(r);
643			} else {
644				return localStorage.setItem(key, value);
645			}
646		}
647	} catch(e) {}
648
649	// HTML 4
650	if(xn.is_ie && (!document.documentElement || typeof document.documentElement.load == 'unknown' || !document.documentElement.load)) {
651		return '';
652	}
653	// get
654	if(typeof value == 'undefined') {
655		if(xn.is_ie) {
656			try {
657				document.documentElement.load(key);
658				r = document.documentElement.getAttribute(key);
659			} catch(e) {
660				//alert('jQuery.pdata:' + e.message);
661				r = '';
662			}
663		} else {
664			try {
665				r = sessionStorage.getItem(key) && sessionStorage.getItem(key).toString().length == 0 ? '' : (sessionStorage.getItem(key) == null ? '' : sessionStorage.getItem(key));
666			} catch(e) {
667				r = '';
668			}
669		}
670		return xn.json_decode(r);
671	// set
672	} else {
673		if(xn.is_ie){
674			try {
675				// fix: IE TEST for ie6 崩溃
676				document.documentElement.load(key);
677				document.documentElement.setAttribute(key, value);
678				document.documentElement.save(key);
679				return  document.documentElement.getAttribute(key);
680			} catch(error) {/*alert('setdata:'+error.message);*/}
681		} else {
682			try {
683				return sessionStorage.setItem(key, value);
684			} catch(error) {/*alert('setdata:'+error.message);*/}
685		}
686	}
687};
688
689// time 单位为秒,与php setcookie, 和  misc::setcookie() 的 time 参数略有差异。
690jQuery.cookie = function(name, value, time, path) {
691	if(typeof value != 'undefined') {
692		if (value === null) {
693			var value = '';
694			var time = -1;
695		}
696		if(typeof time != 'undefined') {
697			date = new Date();
698			date.setTime(date.getTime() + (time * 1000));
699			var time = '; expires=' + date.toUTCString();
700		} else {
701			var time = '';
702		}
703		var path = path ? '; path=' + path : '';
704		//var domain = domain ? '; domain=' + domain : '';
705		//var secure = secure ? '; secure' : '';
706		document.cookie = name + '=' + encodeURIComponent(value) + time + path;
707	} else {
708		var v = '';
709		if(document.cookie && document.cookie != '') {
710			var cookies = document.cookie.split(';');
711			for(var i = 0; i < cookies.length; i++) {
712				var cookie = jQuery.trim(cookies[i]);
713				if(cookie.substring(0, name.length + 1) == (name + '=')) {
714					v = decodeURIComponent(cookie.substring(name.length + 1)) + '';
715					break;
716				}
717			}
718		}
719		return v;
720	}
721};
722
723
724// 改变Location URL ?
725jQuery.xget = function(url, callback, retry) {
726	if(retry === undefined) retry = 1;
727	jQuery.ajax({
728		type: 'GET',
729		url: url,
730		dataType: 'text',
731		timeout: 15000,
732		xhrFields: {withCredentials: true},
733		success: function(r){
734			if(!r) return callback(-100, 'Server Response Empty!');
735			var s = xn.json_decode(r);
736			if(!s) {
737				return callback(-101, r); // 'Server Response xn.json_decode() failed:'+
738			}
739			if(s.code === undefined) {
740				if(jQuery.isPlainObject(s)) {
741					return callback(0, s);
742				} else {
743					return callback(-102, r); // 'Server Response Not JSON 2:'+
744				}
745			} else if(s.code == 0) {
746				return callback(0, s.message);
747			//系统错误
748			} else if(s.code < 0) {
749				return callback(s.code, s.message);
750			//业务逻辑错误
751			} else {
752				return callback(s.code, s.message);
753			
754			}
755		},
756		// 网络错误,重试
757		error: function(xhr, type) {
758			if(retry > 1) {
759				jQuery.xget(url, callback, retry - 1);
760			} else {
761				if((type != 'abort' && type != 'error') || xhr.status == 403 || xhr.status == 404) {
762					return callback(-1000, "xhr.responseText:"+xhr.responseText+', type:'+type);
763				} else {
764					return callback(-1001, "xhr.responseText:"+xhr.responseText+', type:'+type);
765					console.log("xhr.responseText:"+xhr.responseText+', type:'+type);
766				}
767			}
768		}
769	});
770};
771
772// ajax progress plugin
773(function(jQuery, window, undefined) {
774	//is onprogress supported by browser?
775	var hasOnProgress = ("onprogress" in jQuery.ajaxSettings.xhr());
776
777	//If not supported, do nothing
778	if (!hasOnProgress) {
779		return;
780	}
781	
782	//patch ajax settings to call a progress callback
783	var oldXHR = jQuery.ajaxSettings.xhr;
784	jQuery.ajaxSettings.xhr = function() {
785		var xhr = oldXHR();
786		if(xhr instanceof window.XMLHttpRequest) {
787			xhr.addEventListener('progress', this.progress, false);
788		}
789		
790		if(xhr.upload) {
791			xhr.upload.addEventListener('progress', this.progress, false);
792		}
793		
794		return xhr;
795	};
796})(jQuery, window);
797
798
799jQuery.unparam = function(str) {
800	return str.split('&').reduce(function (params, param) {
801		var paramSplit = param.split('=').map(function (value) {
802			return decodeURIComponent(value.replace('+', ' '));
803		});
804		params[paramSplit[0]] = paramSplit[1];
805		return params;
806	}, {});
807}
808
809jQuery.xpost = function(url, postdata, callback, progress_callback) {
810	if(jQuery.isFunction(postdata)) {
811		callback = postdata;
812		postdata = null;
813	}
814	
815	jQuery.ajax({
816		type: 'POST',
817		url: url,
818		data: postdata,
819		dataType: 'text',
820		timeout: 6000000,
821		progress: function(e) {
822			if (e.lengthComputable) {
823				if(progress_callback) progress_callback(e.loaded / e.total * 100);
824				//console.log('progress1:'+e.loaded / e.total * 100 + '%');
825			}
826		},
827		success: function(r){
828			if(!r) return callback(-1, 'Server Response Empty!');
829			var s = xn.json_decode(r);
830			if(!s || s.code === undefined) return callback(-1, 'Server Response Not JSON:'+r);
831			if(s.code == 0) {
832				return callback(0, s.message);
833			//系统错误
834			} else if(s.code < 0) {
835				return callback(s.code, s.message);
836			} else {
837				return callback(s.code, s.message);
838			}
839		},
840		error: function(xhr, type) {
841			if(type != 'abort' && type != 'error' || xhr.status == 403) {
842				return callback(-1000, "xhr.responseText:"+xhr.responseText+', type:'+type);
843			} else {
844				return callback(-1001, "xhr.responseText:"+xhr.responseText+', type:'+type);
845				console.log("xhr.responseText:"+xhr.responseText+', type:'+type);
846			}
847		}
848	});
849};
850
851/*
852jQuery.xpost = function(url, postdata, callback, progress_callback) {
853	//构造表单数据
854	if(xn.is_string(postdata)) {
855		postdata = xn.is_string(postdata) ? jQuery.unparam(postdata) : postdata;
856	}
857	var formData = new FormData();
858	for(k in postdata) {
859		formData.append(k, postdata[k]);
860	}
861	
862	//创建xhr对象 
863	var xhr = new XMLHttpRequest();
864	
865	//设置xhr请求的超时时间
866	xhr.timeout = 6000000;
867	
868	//设置响应返回的数据格式
869	xhr.responseType = "text";
870	
871	//创建一个 post 请求,采用异步
872	xhr.open('POST', url, true);
873	
874	xhr.setRequestHeader("Content_type", "application/x-www-form-urlencoded"); 
875	xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 
876	
877	//注册相关事件回调处理函数
878	xhr.onload = function(e) { 
879		if(this.status == 200 || this.status == 304) {
880			var r = this.response;
881			if(!r) return callback(-1, 'Server Response Empty!');
882			var s = xn.json_decode(r);
883			if(!s || s.code === undefined) return callback(-1, 'Server Response Not JSON:'+r);
884			if(s.code == 0) {
885				return callback(0, s.message);
886			//系统错误
887			} else if(s.code < 0) {
888				return callback(s.code, s.message);
889			} else {
890				return callback(s.code, s.message);
891			}
892		} else {
893			console.log(e);
894		}
895	};
896	xhr.ontimeout = function(e) { 
897		console.log(e);
898		return callback(-1, 'Ajax request timeout:'+url);
899		
900	};
901	xhr.onerror = function(e) { 
902		console.log(e);
903		return callback(-1, 'Ajax request error');
904	};
905	xhr.upload.onprogress = function(e) { 
906		if (e.lengthComputable) {
907			if(progress_callback) progress_callback(xn.intval(e.loaded / e.total * 100));
908			//console.log('progress1:'+e.loaded / e.total * 100 + '%');
909		}
910	};
911	
912	//发送数据
913	xhr.send(formData);
914};
915*/
916
917/*
918	功能:
919		异步加载 js, 加载成功以后 callback
920	用法:
921		jQuery.require('1.js', '2.js', function() {
922			alert('after all loaded');
923		});
924		jQuery.require(['1.js', '2.js' function() {
925			alert('after all loaded');
926		}]);
927*/
928// 区别于全局的 node.js require 关键字
929jQuery.required = [];
930jQuery.require = function() {
931	var args = null;
932	if(arguments[0] && typeof arguments[0] == 'object') { // 如果0 为数组
933		args = arguments[0];
934		if(arguments[1]) args.push(arguments[1]);
935	} else {
936		args = arguments;
937	}
938	this.load = function(args, i) {
939		var _this = this;
940		if(args[i] === undefined) return;
941		if(typeof args[i] == 'string') {
942			var js = args[i];
943			// 避免重复加载
944			if(jQuery.inArray(js, jQuery.required) != -1) {
945				if(i < args.length) this.load(args, i+1);
946				return;
947			}
948			jQuery.required.push(js);
949			var script = document.createElement("script");
950			script.src = js;
951			script.onerror = function() {
952				console.log('script load error:'+js);
953				_this.load(args, i+1);
954			};
955			if(xn.is_ie) {
956				script.onreadystatechange = function() {
957					if(script.readyState == 'loaded' || script.readyState == 'complete') {
958						_this.load(args, i+1);
959						script.onreadystatechange = null;
960					}
961				};
962			} else {
963				script.onload = function() { _this.load(args, i+1); };
964			}
965			document.getElementsByTagName('head')[0].appendChild(script);
966		} else if(typeof args[i] == 'function'){
967			var f = args[i];
968			f();
969			if(i < args.length) this.load(args, i+1);
970		} else {
971			_this.load(args, i+1);
972		}
973	};
974	this.load(args, 0);
975};
976
977jQuery.require_css = function(filename) {
978	// 判断重复加载
979	var tags = document.getElementsByTagName('link');
980	for(var i=0; i<tags.length; i++) {
981		if(tags[i].href.indexOf(filename) != -1) {
982			return false;
983		}
984	}
985	
986	var link = document.createElement("link");
987	link.rel = "stylesheet";
988	link.type = "text/css";
989	link.href = filename;
990	document.getElementsByTagName('head')[0].appendChild(link);
991};
992
993// 在节点上显示 loading 图标
994jQuery.fn.loading = function(action) {
995	return this.each(function() {
996		var jthis = jQuery(this);
997		jthis.css('position', 'relative');
998		if(!this.jloading) this.jloading = jQuery('<div class="loading"><img src="static/loading.gif" /></div>').appendTo(jthis);
999		var jloading = this.jloading.show();
1000		if(!action) {
1001			var offset = jthis.position();
1002			var left = offset.left;
1003			var top = offset.top;
1004			var w = jthis.width();
1005			var h = xn.min(jthis.height(), jQuery(window).height());
1006			var left = w / 2 - jloading.width() / 2;
1007			var top = (h / 2 -  jloading.height() / 2) * 2 / 3;
1008			jloading.css('position', 'absolute').css('left', left).css('top', top);
1009		} else if(action == 'close') {
1010			jloading.remove();
1011			this.jloading = null;
1012		}
1013	});
1014};
1015
1016// 对图片进行缩略,裁剪,然后 base64 存入 form 隐藏表单,name 与 file 控件相同
1017// 上传过程中,禁止 button,对图片可以缩略
1018jQuery.fn.base64_encode_file = function(width, height, action) {
1019	var action = action || 'thumb';
1020	var jform = jQuery(this);
1021	var jsubmit = jform.find('input[type="submit"]');
1022	jform.on('change', 'input[type="file"]', function(e) {
1023		var jfile = jQuery(this);
1024		var jassoc = jfile.data('assoc') ? jQuery('#'+jfile.data('assoc')) : null;
1025		var obj = e.target;
1026		jsubmit.button('disabled');
1027		var file = obj.files[0];
1028
1029       		// 创建一个隐藏域,用来保存 base64 数据
1030		var jhidden = jQuery('<input type="hidden" name="'+obj.name+'" />').appendTo(jform);
1031		obj.name = '';
1032
1033		var reader = new FileReader();
1034		reader.readAsDataURL(file);
1035		reader.onload = function(e) {
1036			// 如果是图片,并且设置了,宽高,和剪切模式
1037			if(width && height && xn.substr(this.result, 0, 10) == 'data:image') {
1038				xn.image_resize(this.result, function(code, message) {
1039					if(code == 0) {
1040						if(jassoc) jassoc.attr('src', message.data);
1041						// jhidden.val(message.data); // base64
1042					} else {
1043						alert(message);
1044					}
1045					jsubmit.button('reset');
1046				}, {width: width, height: height, action: action});
1047			} else {
1048				if(jassoc) jassoc.attr('src', this.result);
1049				jhidden.val(this.result);
1050				jsubmit.button('reset');
1051			}
1052		}
1053	});
1054};
1055
1056xn.base64_data_image_type = function(s) {
1057	//data:image/png;base64
1058	r = s.match(/^data:image\/(\w+);/i);
1059	return r[1];
1060};
1061
1062// 图片背景透明算法 by [email protected],只能处理小图片,效率做过改进,目前速度还不错。
1063xn.image_background_opacity = function(data, width, height, callback) {
1064	var x = 0;
1065	var y = 0;
1066	//var map = {}; // 图片的状态位: 0: 未检测,1:检测过是背景,2:检测过不是背景
1067	//var unmap = {}; // 未检测过的 map 
1068	var checked = {'0-0':1}; // 检测过的点
1069	var unchecked = {}; // 未检测过的点,会不停得将新的未检测的点放进来,检测过的移动到 checked;
1070	var unchecked_arr = []; // 用来加速
1071	// 从四周遍历
1072	/*
1073		*************************************
1074		*                                   *
1075		*                                   *
1076		*                                   *
1077		*                                   *
1078		*                                   *
1079		*                                   *
1080		*                                   *
1081		*                                   *
1082		*                                   *
1083		*                                   *
1084		*                                   *
1085		*************************************
1086	*/
1087	for(var i = 0; i < width; i++) {
1088		var k1 = i + '-0';
1089		var k2 = i + '-' + (height - 1);
1090		unchecked[k1] = 1;
1091		unchecked[k2] = 1;
1092		unchecked_arr.push(k1);
1093		unchecked_arr.push(k2);
1094	}
1095	for(var i = 1; i < height - 1; i++) {
1096		var k1 ='0-' + i;
1097		var k2 = (width - 1) + '-' + i;
1098		unchecked[k1] = 1;
1099		unchecked[k2] = 1;
1100		unchecked_arr.push(k1);
1101		unchecked_arr.push(k2);
1102	}
1103	
1104	var bg = [data[0], data[1], data[2], data[3]];
1105	// 如果不是纯黑,纯白,则返回。
1106	if(!((bg[0] == 0 && bg[1] == 0 && bg[2] == 0) || (bg[0] == 255 && bg[1] == 255 && bg[2] == 255))) return;
1107	// 判断该点是否被检测过。
1108	/*
1109	function is_checked(x, y) {
1110		return checked[x+'-'+y] ? true : false;
1111	}
1112	function is_unchecked(x, y) {
1113		return unchecked[x+'-'+y] ? true : false;
1114	}*/
1115	
1116	function get_one_unchecked() {
1117		if(unchecked_arr.length == 0) return false;
1118		var k = unchecked_arr.pop();
1119		var r = xn.explode('-', k);
1120		return r;
1121	}
1122	function checked_push(x, y) {
1123		var k = x+'-'+y;
1124		if(checked[k] === undefined) checked[k] = 1;
1125	}
1126	function unchecked_push(x, y) {
1127		var k = x+'-'+y;
1128		if(checked[k] === undefined && unchecked[k] === undefined) {
1129			unchecked[k] = 1;
1130			unchecked_arr.push(k);
1131		}
1132	}
1133	
1134	var n = 0;
1135	while(1) {
1136		//if(k++ > 100000) break;
1137		//if(checked.length > 10000) return;
1138		//(n++ % 10000 == 0) {
1139			//alert(n);
1140			//console.log(unchecked_arr);
1141			//console.log(unchecked);
1142			//break;
1143		//}
1144		// 遍历未检测的区域,并且不在 checked 列表的,放进去。
1145		var curr = get_one_unchecked();
1146		//if(unchecked.length > 1000) return;
1147		// 遍历完毕,终止遍历
1148		if(!curr) break;
1149		var x = xn.intval(curr[0]);
1150		var y = xn.intval(curr[1]);
1151		
1152		// 在 data 中的偏移量应该 * 4, rgba 各占一位。
1153		var pos = 4 * ((y * width) + x);
1154		var r = data[pos];
1155		var g = data[pos + 1];
1156		var b = data[pos + 2];
1157		var a = data[pos + 3];
1158		
1159		if(Math.abs(r - bg[0]) < 2 && Math.abs(g == bg[1]) < 2 && Math.abs(b == bg[2]) < 2) {
1160			
1161			if(!callback) {
1162				data[pos + 0] = 0; // 处理为透明
1163				data[pos + 1] = 0; // 处理为透明
1164				data[pos + 2] = 0; // 处理为透明
1165				data[pos + 3] = 0; // 处理为透明
1166			} else {
1167				callback(data, pos);
1168			}			
1169		
1170			// 检测边距
1171			if(y > 0) unchecked_push(x, y-1);	 // 上
1172			if(x < width - 1) unchecked_push(x+1, y); // 右
1173			if(y < height - 1) unchecked_push(x, y+1); // 下
1174			if(x > 0) unchecked_push(x-1, y); 	// å·¦
1175		}
1176		
1177		checked_push(x, y); // 保存
1178	}
1179};
1180
1181xn.image_file_type = function(file_base64_data) {
1182	var pre = xn.substr(file_base64_data, 0, 14);
1183	if(pre == 'data:image/gif') {
1184		return 'gif';
1185	} else if(pre == 'data:image/jpe' || pre == 'data:image/jpg') {
1186		return 'jpg';
1187	} else if(pre == 'data:image/png') {
1188		return 'png';
1189	}
1190	return 'jpg';
1191}
1192
1193//对图片进行裁切,缩略,对黑色背景,透明化处理
1194xn.image_resize = function(file_base64_data, callback, options) {
1195	var thumb_width = options.width || 2560;
1196	var thumb_height = options.height || 4960;
1197	var action = options.action || 'thumb';
1198	var filetype = options.filetype || xn.image_file_type(file_base64_data);//xn.base64_data_image_type(file_base64_data);
1199	var qulity = options.qulity || 1; // 图片质量, 1 为无损
1200	
1201	if(thumb_width < 1) return callback(-1, '缩略图宽度不能小于 1 / thumb image width length is less 1 pix');
1202	if(xn.substr(file_base64_data, 0, 10) != 'data:image') return callback(-1, '传入的 base64 数据有问题 / deformed base64 data');
1203	// && xn.substr(file_base64_data, 0, 14) != 'data:image/gif' gif 不支持\
1204	
1205	var img = new Image();
1206	img.onload = function() {
1207		
1208		var water_img_onload = function(water_on,orientation) { //qiukong.com //uids.net
1209			var canvas = document.createElement('canvas');
1210			// 等比缩放
1211			var width = 0, height = 0, canvas_width = 0, canvas_height = 0;
1212			var dx = 0, dy = 0;
1213			
1214			var img_width = img.width;
1215			var img_height = img.height;
1216			var qkswap=false;if(orientation==6 || orientation==8){img_width=img.height;img_height=img.width;qkswap=true;}; //qiukong.com //uids.net
1217			
1218			if(xn.substr(file_base64_data, 0, 14) == 'data:image/gif') return callback(0, {width: img_width, height: img_height, data: file_base64_data});
1219			
1220			// width, height: 计算出来的宽高(求)
1221			// thumb_width, thumb_height: 要求的缩略宽高
1222			// img_width, img_height: 原始图片宽高
1223			// canvas_width, canvas_height: 画布宽高
1224			if(action == 'thumb') {
1225				if(img_width < thumb_width && img_height && thumb_height) {
1226					width = img_width;
1227					height = img_height;
1228				} else {
1229					// 横形
1230					if(img_width / img_height > thumb_width / thumb_height) {
1231						var width = thumb_width; // 以缩略图宽度为准,进行缩放
1232						var height = Math.ceil((thumb_width / img_width) * img_height);
1233					// 竖形
1234					} else {
1235						var height = thumb_height; // 以缩略图宽度为准,进行缩放
1236						var width = Math.ceil((img_width / img_height) * thumb_height);
1237					}
1238				}
1239				canvas_width = width;
1240				canvas_height = height;
1241			} else if(action == 'clip') {
1242				if(img_width < thumb_width && img_height && thumb_height) {
1243					if(img_height > thumb_height) {
1244						thumb_width = width = img_width;
1245						// thumb_height = height = thumb_height;
1246					} else {
1247						thumb_width = width = img_width;
1248						thumb_height = height = img_height;
1249					}
1250				} else {
1251					// 横形
1252					if(img_width / img_height > thumb_width / thumb_height) {
1253						var height = thumb_height; // 以缩略图宽度为准,进行缩放
1254						var width = Math.ceil((img_width / img_height) * thumb_height);
1255						var dx = -((width - thumb_width) / 2);
1256						var dy = 0;
1257					// 竖形
1258					} else {
1259						var width = thumb_width; // 以缩略图宽度为准,进行缩放
1260						var height = Math.ceil((img_height / img_width) * thumb_width);
1261						dx = 0;
1262						dy = -((height - thumb_height) / 2);
1263					}
1264				}
1265				canvas_width = thumb_width;
1266				canvas_height = thumb_height;
1267			}
1268			canvas.width = canvas_width;
1269			canvas.height = canvas_height;
1270			var ctx = canvas.getContext("2d"); 
1271	
1272			//ctx.fillStyle = 'rgb(255,255,255)';
1273			//ctx.fillRect(0,0,width,height);
1274
1275			switch(orientation){case 3:ctx.translate(width,height);ctx.rotate(180*Math.PI/180);break;case 6:ctx.translate(width,0);ctx.rotate(90*Math.PI/180);break;case 8:ctx.translate(0,height);ctx.rotate(-90*Math.PI/180);break;default:break;}; //qiukong.com //uids.net
1276	
1277			ctx.clearRect(0, 0, width, height); 			// canvas清屏
1278			ctx.drawImage(img, 0, 0, img.width, img.height, qkswap?dy:dx, qkswap?dx:dy, qkswap?height:width, qkswap?width:height); //qiukong.com //uids.net
1279			
1280			
1281			if(water_on) {
1282				var water_width = water_img.width;
1283				var water_height = water_img.height;
1284				if(img_width > 400 && img_width > water_width && water_width > 4) {
1285					var x =  img_width - water_width - 16;
1286					var y = img_height - water_height - 16;
1287					
1288					// 参数参考:https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
1289					ctx.globalAlpha = 0.3; // 水印透明度
1290					ctx.beginPath();
1291					ctx.drawImage(water_img, 0, 0, water_width, water_height, x, y, water_width, water_height);	// 将水印图像绘制到canvas上 
1292					ctx.closePath();
1293					ctx.save();
1294				}
1295			}
1296			
1297			
1298			var imagedata = ctx.getImageData(0, 0, canvas_width, canvas_height);
1299			var data = imagedata.data;
1300			// 判断与 [0,0] 值相同的并且连续的像素为背景
1301	
1302			//xn.image_background_opacity(data, canvas_width, canvas_height);
1303	
1304			// 将修改后的代码复制回画布中
1305			ctx.putImageData(imagedata, 0, 0);
1306	
1307			//filetype = 'png';
1308			if(filetype == 'jpg') filetype = 'jpeg';
1309			var s = canvas.toDataURL('image/'+filetype, qulity);
1310			if(callback) callback(0, {width: width, height: height, data: s});
1311		
1312		};
1313		
1314		var water_img = new Image();
1315		water_img.onload = function() {
1316			var reader=new FileReader();reader.onload=function(e){var view=new DataView(e.target.result);if(view.getUint16(0,false)!=0xFFD8){water_img_onload(true,-2);return;};var length=view.byteLength,offset=2;while(offset<length){if(view.getUint16(offset+2,false)<=8){water_img_onload(true,-1);return;};var marker=view.getUint16(offset,false);offset+=2;if(marker==0xFFE1){if(view.getUint32(offset+=2,false)!=0x45786966){water_img_onload(true,-1);return;};var little=view.getUint16(offset+=6,false)==0x4949;offset+=view.getUint32(offset+4,little);var tags=view.getUint16(offset, little);offset+=2;for(var i=0;i<tags;i++){if(view.getUint16(offset+(i*12),little)==0x0112){water_img_onload(true,view.getUint16(offset+(i*12)+8,little));return;}}}else if((marker&0xFF00)!=0xFF00){break;}else{offset+=view.getUint16(offset,false);};};water_img_onload(true,-1);return;};var dataarr=file_base64_data.split(','),mime=dataarr[0].match(/:(.*?);/)[1],bstr=atob(dataarr[1]),n=bstr.length,u8arr=new Uint8Array(n);while(n--){u8arr[n]=bstr.charCodeAt(n);};reader.readAsArrayBuffer(new Blob([u8arr],{type:mime})); //qiukong.com //stackoverflow.com/a/32490603
1317		};
1318		water_img.onerror = function() {
1319			water_img_onload(false,0); //qiukong.com //uids.net
1320		};
1321		water_img.src = options.water_image_url || xn.options.water_image_url;
1322		if(!water_img.src) {
1323			water_img_onload(false,0); //qiukong.com //uids.net
1324		}
1325	};
1326	img.onerror = function(e) {
1327		console.log(e);
1328		alert(e);
1329	};
1330	img.src = file_base64_data;
1331};
1332
1333/*
1334	用法:
1335	var file = e.target.files[0]; // 文件控件 onchange 后触发的 event;
1336	var upload_url = 'xxx.php'; // 服务端地址
1337	var postdata = {width: 2048, height: 4096, action: 'thumb', filetype: 'jpg'}; // postdata|options 公用,一起传给服务端。
1338	var progress = function(percent) { console.log('progress:'+ percent); }}; // 如果是图片,会根据此项设定进行缩略和剪切 thumb|clip
1339	xn.upload_file(file, upload_url, postdata, function(code, json) {
1340		// 成功
1341		if(code == 0) {
1342			console.log(json.url);
1343			console.log(json.width);
1344			console.log(json.height);
1345		} else {
1346			alert(json);
1347		}
1348	}, progress);
1349*/
1350xn.upload_file = function(file, upload_url, postdata, complete_callback, progress_callback, thumb_callback) {
1351	postdata = postdata || {};
1352	postdata.width = postdata.width || 2560;
1353	postdata.height = postdata.height || 4960;
1354	
1355	var ajax_upload_file = function(base64_data) {
1356		var ajax_upload = function(upload_url, postdata, complete_callback) {
1357			jQuery.xpost(upload_url, postdata, function(code, message) {
1358				if(code != 0) return complete_callback(code, message);
1359				if(complete_callback) complete_callback(0, message);
1360			}, function(percent) {
1361				if(progress_callback) progress_callback(percent);
1362			});
1363		};
1364		
1365		// gif 直接上传
1366		// 图片进行缩放,然后上传
1367		//  && xn.substr(base64_data, 0, 14) != 'data:image/gif'
1368		if(xn.substr(base64_data, 0, 10) == 'data:image') {
1369			var filename = file.name ? file.name : (file.type == 'image/png' ? 'capture.png' : 'capture.jpg');
1370			xn.image_resize(base64_data, function(code, message) {
1371				if(code != 0) return alert(message);
1372				// message.width, message.height 是缩略后的宽度和高度
1373				postdata.name = filename;
1374				postdata.data = message.data;
1375				postdata.width = message.width;
1376				postdata.height = message.height;
1377				ajax_upload(upload_url, postdata, complete_callback);
1378			}, postdata);
1379		// 文件直接上传, 不缩略
1380		} else {
1381			var filename = file.name ? file.name : '';
1382			postdata.name = filename;
1383			postdata.data = base64_data;
1384			postdata.width = 0;
1385			postdata.height = 0;
1386			ajax_upload(upload_url, postdata, complete_callback);
1387		}
1388	};
1389		
1390	// 如果为 base64 则不需要 new FileReader()
1391	if(xn.is_string(file) && xn.substr(file, 0, 10) == 'data:image') {
1392		var base64_data = file;
1393		if(thumb_callback) thumb_callback(base64_data);
1394		ajax_upload_file(base64_data);
1395	} else {
1396		var reader = new FileReader();
1397			reader.readAsDataURL(file);
1398			reader.onload = function() {
1399				var base64_data = this.result;
1400				if(thumb_callback) thumb_callback(base64_data);
1401			    ajax_upload_file(base64_data);
1402			}
1403	}
1404	
1405};
1406
1407// 从事件对象中查找 file 对象,兼容 jquery event, clipboard, file.onchange
1408xn.get_files_from_event = function(e) {
1409	function get_paste_files(e) {
1410		return e.clipboardData && e.clipboardData.items ? e.clipboardData.items : null;
1411	}
1412	function get_drop_files(e) {
1413		return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
1414	}
1415	if(e.originalEvent) e = e.originalEvent;
1416	if(e.type == 'change' && e.target && e.target.files && e.target.files.length > 0) return e.target.files;
1417	var files = e.type == 'paste' ? get_paste_files(e) : get_drop_files(e);
1418	return files;
1419};
1420
1421// 获取所有的 父节点集合,一直到最顶层节点为止。, IE8 没有 HTMLElement
1422xn.nodeHasParent = function(node, topNode) {
1423	if(!topNode) topNode = document.body;
1424	var pnode = node.parentNode;
1425	while(pnode) {
1426		if(pnode == topNode) return true;
1427		pnode = pnode.parentNode;
1428	};
1429	return false;
1430};
1431
1432// 表单提交碰到错误的时候,依赖此处,否则错误会直接跳过,不利于发现错误
1433window.onerror = function(msg, url, line) {
1434	if(!window.debug) return;
1435	alert("error: "+msg+"\r\n line: "+line+"\r\n url: "+url);
1436	// 阻止所有的 form 提交动作
1437	return false;
1438};
1439
1440// remove() 并不清除子节点事件!!用来替代 remove(),避免内存泄露
1441jQuery.fn.removeDeep = function() {
1442	 this.each(function() {
1443		jQuery(this).find('*').off();
1444	});
1445	this.off();
1446	this.remove();
1447	return this;
1448};
1449
1450// empty 清楚子节点事件,释放内存。
1451jQuery.fn.emptyDeep = function() {
1452	this.each(function() {
1453		jQuery(this).find('*').off();
1454	});
1455	this.empty();
1456	return this;
1457};
1458
1459jQuery.fn.son = jQuery.fn.children;
1460
1461/*
1462	用来增强 jQuery.fn.val()
1463	
1464	用来选中和获取 select radio checkbox 的值,用法:
1465	jQuery('#select1').checked(1);			// 设置 value="1" 的 option 为选中状态
1466	jQuery('#select1').checked();			// 返回选中的值。
1467	jQuery('input[type="checkbox"]').checked([2,3,4]);	// 设置 value="2" 3 4 的 checkbox 为选中状态
1468	jQuery('input[type="checkbox"]').checked();		// 获取选中状态的 checkbox 的值,返回 []
1469	jQuery('input[type="radio"]').checked(2);		// 设置 value="2" 的 radio 为选中状态
1470	jQuery('input[type="radio"]').checked();		// 返回选中状态的 radio 的值。
1471*/
1472jQuery.fn.checked = function(v) {
1473	// 转字符串
1474	if(v) v = v instanceof Array ? v.map(function(vv) {return vv+""}) : v + "";
1475	var filter = function() {return !(v instanceof Array) ? (this.value == v) : (jQuery.inArray(this.value, v) != -1)};
1476	// 设置
1477	if(v) {
1478		this.each(function() {
1479			if(xn.strtolower(this.tagName) == 'select') {
1480				jQuery(this).find('option').filter(filter).prop('selected', true);
1481			} else if(xn.strtolower(this.type) == 'checkbox' || strtolower(this.type) == 'radio') {
1482				// console.log(v);
1483				jQuery(this).filter(filter).prop('checked', true);
1484			}
1485		});
1486		return this;
1487	// 获取,值用数组的方式返回
1488	} else {
1489		if(this.length == 0) return [];
1490		var tagtype = xn.strtolower(this[0].tagName) == 'select' ? 'select' : xn.strtolower(this[0].type);
1491		var r = (tagtype == 'checkbox' ? [] : '');
1492		for(var i=0; i<this.length; i++) {
1493			var tag = this[i];
1494			if(tagtype == 'select') {
1495				var joption = jQuery(tag).find('option').filter(function() {return this.selected == true});
1496				if(joption.length > 0) return joption.attr('value');
1497			} else if(tagtype == 'checkbox') {
1498				if(tag.checked) r.push(tag.value);
1499			} else if(tagtype == 'radio') {
1500				if(tag.checked) return tag.value;
1501			}
1502		}
1503		return r;
1504	}
1505};
1506
1507// 支持连续操作 jsubmit.button(message).delay(1000).button('reset');
1508jQuery.fn.button = function(status) {
1509	return this.each(function() {
1510		var jthis = jQuery(this);
1511		jthis.queue(function (next) {
1512			var loading_text = jthis.attr('loading-text') || jthis.data('loading-text');
1513			if(status == 'loading') {
1514				jthis.prop('disabled', true).addClass('disabled').attr('default-text', jthis.text());
1515				jthis.html(loading_text);
1516			} else if(status == 'disabled') {
1517				jthis.prop('disabled', true).addClass('disabled');
1518			} else if(status == 'enable') {
1519				jthis.prop('disabled', false).removeClass('disabled');
1520			} else if(status == 'reset') {
1521				jthis.prop('disabled', false).removeClass('disabled');
1522				if(jthis.attr('default-text')) {
1523					jthis.text(jthis.attr('default-text'));
1524				}
1525			} else {
1526				jthis.text(status);
1527			}
1528			next();
1529		});
1530	});
1531};
1532
1533// 支持连续操作 jsubmit.button(message).delay(1000).button('reset').delay(1000).location('http://xxxx');
1534jQuery.fn.location = function(href) {
1535	var jthis = this;
1536	jthis.queue(function(next) {
1537		if(!href) {
1538			window.location.reload();
1539		} else {
1540			window.location = href;
1541		}
1542		next();
1543	});
1544};
1545
1546// 在控件上方提示错误信息,如果为手机版,则调用 toast
1547jQuery.fn.alert = function(message) {
1548	var jthis = jQuery(this);
1549	jpthis = jthis.parent('.form-group');
1550	jpthis.addClass('has-danger');
1551	jthis.addClass('form-control-danger');
1552	//if(in_mobile) alert(message);
1553	jthis.data('title', message).tooltip('show');
1554	return this;
1555};
1556
1557jQuery.fn.serializeObject = function() {
1558	var self = this,
1559		json = {},
1560		push_counters = {},
1561		patterns = {
1562			"validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*jQuery/,
1563			"key":	  /[a-zA-Z0-9_]+|(?=\[\])/g,
1564			"push":	 /^jQuery/,
1565			"fixed":	/^\d+jQuery/,
1566			"named":	/^[a-zA-Z0-9_]+jQuery/
1567		};
1568
1569	this.build = function(base, key, value){
1570		base[key] = value;
1571		return base;
1572	};
1573
1574	this.push_counter = function(key){
1575		if(push_counters[key] === undefined){
1576			push_counters[key] = 0;
1577		}
1578		return push_counters[key]++;
1579	};
1580
1581	jQuery.each(jQuery(this).serializeArray(), function(){
1582
1583		// skip invalid keys
1584		if(!patterns.validate.test(this.name)){
1585			return;
1586		}
1587
1588		var k,
1589			keys = this.name.match(patterns.key),
1590			merge = this.value,
1591			reverse_key = this.name;
1592
1593		while((k = keys.pop()) !== undefined){
1594
1595			// adjust reverse_key
1596			reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]jQuery"), '');
1597
1598			// push
1599			if(k.match(patterns.push)){
1600				merge = self.build([], self.push_counter(reverse_key), merge);
1601			}
1602
1603			// fixed
1604			else if(k.match(patterns.fixed)){
1605				merge = self.build([], k, merge);
1606			}
1607
1608			// named
1609			else if(k.match(patterns.named)){
1610				merge = self.build({}, k, merge);
1611			}
1612		}
1613
1614		json = jQuery.extend(true, json, merge);
1615	});
1616
1617	return json;
1618};
1619
1620/*
1621jQuery.fn.serializeObject = function() {
1622	var o = {};
1623	var a = this.serializeArray();
1624	jQuery.each(a, function() {
1625		if(o[this.name]) {
1626			if(!o[this.name].push) {
1627				o[this.name] = [o[this.name]];
1628			}
1629			o[this.name].push(this.value || '');
1630		} else {
1631			o[this.name] = this.value || '';
1632		}
1633	});
1634	return o;
1635};*/
1636 
1637/*
1638jQuery.fn.serializeObject = function() {
1639	var formobj = {};
1640	jQuery([].slice.call(this.get(0).elements)).each(function() {
1641		var jthis = jQuery(this);
1642		var type = jthis.attr('type');
1643		var name = jthis.attr('name');
1644		if (name && xn.strtolower(this.nodeName) != 'fieldset' && !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
1645		((type != 'radio' && type != 'checkbox') || this.checked)) {
1646			// 还有一些情况没有考虑, 比如: hidden 或 text 类型使用 name 数组时
1647			if(type == 'radio' || type == 'checkbox') {
1648				if(!formobj[name]) formobj[name] = [];
1649				formobj[name].push(jthis.val());
1650			}else{
1651				formobj[name] = jthis.val();
1652			}
1653		}
1654	})
1655	return formobj;
1656}*/
1657
1658// 批量修改 input name="gid[123]" 中的 123 的值
1659jQuery.fn.attr_name_index = function(rowid) {
1660	return this.each(function() {
1661		var jthis = jQuery(this);
1662		var name = jthis.attr('name');
1663		name = name.replace(/\[(\d*)\]/, function(all, oldid) {
1664			var newid = rowid === undefined ? xn.intval(oldid) + 1 : rowid;
1665			return '[' + newid + ']';
1666		});
1667		jthis.attr('name', name);
1668	});
1669};
1670
1671// 重置 form 状态
1672jQuery.fn.reset = function() {
1673	var jform = jQuery(this);
1674	jform.find('input[type="submit"]').button('reset');
1675	jform.find('input').tooltip('dispose');
1676};
1677
1678// 用来代替 <base href="../" /> 的功能
1679jQuery.fn.base_href = function(base) {
1680	function replace_url(url) {
1681		if(url.match('/^https?:\/\//i')) {
1682			return url;
1683		} else {
1684			return base + url;
1685		}
1686	}
1687	this.find('img').each(function() {
1688		var jthis = jQuery(this);
1689		var src = jthis.attr('src');
1690		if(src) jthis.attr('src', replace_url(src));
1691	});
1692	this.find('a').each(function() {
1693		var jthis = jQuery(this);
1694		var href = jthis.attr('href');
1695		if(href) jthis.attr('href', replace_url(href));
1696	});
1697	return this;
1698};
1699
1700// jQuery.each() 的串行版本,用法:
1701/*
1702	jQuery.each_sync(items, function(i, callback) {
1703		var item = items[i];
1704		jQuery.post(url, function() {
1705			// ...
1706			callback();
1707		});
1708	});
1709*/
1710jQuery.each_sync = function(array, func, callback){
1711	async.series((function(){
1712		var func_arr = [];
1713		for(var i = 0; i< array.length; i++){
1714			var f = function(i){
1715				return function(callback){
1716					func(i, callback);
1717					/*
1718					setTimeout(function() {
1719						func(i, callback);
1720					}, 2000);*/
1721					
1722				}
1723			};
1724			func_arr.push(f(i))
1725		}
1726		return func_arr;
1727	})(), function(error, results) {
1728		if(callback) callback(null, "complete");
1729	});
1730};
1731
1732// 定位
1733/*
1734         11      12      1
1735        --------------------
1736     10 |  -11   -12   -1  | 2
1737        |                  |
1738      9 |  -9    0     -3  | 3
1739        |                  |
1740      8 |  -7    -6    -5  | 4
1741        --------------------
1742         7        6       5
1743
1744     将菜单定位于自己的周围:
1745     jQuery(this).xn_position(jQuery('#menuid'), 6);
1746
1747*/
1748// 将菜单定位于自己的周围
1749jQuery.fn.xn_position = function(jfloat, pos, offset) {
1750	var jthis = jQuery(this);
1751	var jparent = jthis.offsetParent();
1752	var pos = pos || 0;
1753	var offset = offset || {left: 0, top: 0};
1754	offset.left = offset.left || 0;
1755	offset.top = offset.top || 0;
1756	
1757	// 如果 menu 藏的特别深,把它移动出来。
1758	if(jfloat.offsetParent().get(0) != jthis.offsetParent().get(0)) {
1759		jfloat.appendTo(jthis.offsetParent());
1760	}
1761	
1762	// 设置菜单为绝对定位
1763	jfloat.css('position', 'absolute').css('z-index', jthis.css('z-index') + 1);
1764	
1765	var p = jthis.position();
1766	p.w = jthis.outerWidth();
1767	p.h = jthis.outerHeight();
1768	var m = {left: 0, top: 0};
1769	m.w = jfloat.outerWidth();
1770	m.h = jfloat.outerHeight();
1771	p.margin = {
1772		left: xn.floatval(jthis.css('margin-left')),
1773		top: xn.floatval(jthis.css('margin-top')),
1774		right: xn.floatval(jthis.css('margin-right')),
1775		bottom: xn.floatval(jthis.css('margin-bottom')),
1776	};
1777	p.border = {
1778		left: xn.floatval(jthis.css('border-left-width')),
1779		top: xn.floatval(jthis.css('border-top-width')),
1780		right: xn.floatval(jthis.css('border-right-width')),
1781		bottom: xn.floatval(jthis.css('border-bottom-width')),
1782	};
1783	//alert('margin-top:'+p.margin.top+', border-top:'+p.border.top);
1784	
1785	if(pos == 12) {
1786		m.left = p.left + ((p.w - m.w) / 2);
1787		m.top = p.top - m.h ;
1788	} else if(pos == 1) {
1789		m.left = p.left + (p.w - m.w);
1790		m.top = p.top - m.h;
1791	} else if(pos == 11) {
1792		m.left = p.left;
1793		m.top = p.top - m.h;
1794	} else if(pos == 2) {
1795		m.left = p.left + p.w;
1796		m.top = p.top;
1797	} else if(pos == 3) {
1798		m.left = p.left + p.w;
1799		m.top = p.top + ((p.h - m.h) / 2);
1800	} else if(pos == 4) {
1801		m.left = p.left + p.w;
1802		m.top = p.top + (p.h - m.h);
1803	} else if(pos == 5) {
1804		m.left = p.left + (p.w - m.w);
1805		m.top = p.top + p.h;
1806	} else if(pos == 6) {
1807		m.left = p.left + ((p.w - m.w) / 2);
1808		m.top = p.top + p.h;
1809	} else if(pos == 7) {
1810		m.left = p.left;
1811		m.top = p.top + p.h;
1812	} else if(pos == 8) {
1813		m.left = p.left - m.w;
1814		m.top = p.top + (p.h - m.h);
1815	} else if(pos == 9) {
1816		m.left = p.left - m.w;
1817		m.top = p.top + ((p.h - m.h) / 2);
1818	} else if(pos == 10) {
1819		m.left = p.left - m.w;
1820		m.top = p.top;
1821	} else if(pos == -12) {
1822		m.left = p.left + ((p.w - m.w) / 2);
1823		m.top = p.top;
1824	} else if(pos == -1) {
1825		m.left = p.left + (p.w - m.w);
1826		m.top = p.top;
1827	} else if(pos == -3) {
1828		m.left = p.left + p.w - m.w;
1829		m.top = p.top + ((p.h - m.h) / 2);
1830	} else if(pos == -5) {
1831		m.left = p.left + (p.w - m.w);
1832		m.top = p.top + p.h - m.h;
1833	} else if(pos == -6) {
1834		m.left = p.left + ((p.w - m.w) / 2);
1835		m.top = p.top + p.h - m.h;
1836	} else if(pos == -7) {
1837		m.left = p.left;
1838		m.top = p.top + p.h - m.h;
1839	} else if(pos == -9) {
1840		m.left = p.left;
1841		m.top = p.top + ((p.h - m.h) / 2);
1842	} else if(pos == -11) {
1843		m.left = p.left;
1844		m.top = p.top - m.h + m.h;
1845	} else if(pos == 0) {
1846		m.left = p.left + ((p.w - m.w) / 2);
1847		m.top = p.top + ((p.h - m.h) / 2);
1848	}
1849	jfloat.css({left: m.left + offset.left, top: m.top + offset.top});
1850};
1851
1852// 菜单定位
1853/*
1854         11        12     1
1855        --------------------
1856     10 |                  | 2
1857        |                  |
1858      9 |        0         | 3
1859        |                  |
1860      8 |                  | 4
1861        --------------------
1862         7        6       5
1863
1864	弹出菜单:
1865	jQuery(this).xn_menu(jQuery('#menuid'), 6);
1866*/
1867jQuery.fn.xn_menu = function(jmenu, pos, option) {
1868	// 生成一个箭头放到菜单的周围
1869	var jthis = jQuery(this);
1870	var pos = pos || 6;
1871	var offset = {};
1872	var option = option || {hidearrow: 0};
1873	var jparent = jmenu.offsetParent();
1874	if(!jmenu.jarrow && !option.hidearrow) jmenu.jarrow = jQuery('<div class="arrow arrow-up" style="display: none;"><div class="arrow-box"></div></div>').insertAfter(jthis);
1875	if(!option.hidearrow) {
1876		if(pos == 2 || pos == 3 || pos == 4) {
1877			jmenu.jarrow.addClass('arrow-left');
1878			offset.left = 7;
1879		} else if(pos == 5 || pos == 6 || pos == 7) {
1880			jmenu.jarrow.addClass('arrow-up');
1881			offset.top = 7;
1882		} else if(pos == 8 || pos == 9 || pos == 10) {
1883			jmenu.jarrow.addClass('arrow-right');
1884			offset.left = -7;
1885		} else if(pos == 11 || pos == 12 || pos == 1) {
1886			jmenu.jarrow.addClass('arrow-down');
1887			offset.top = -7;
1888		}
1889	}
1890	var arr_pos_map = {2: 10, 3: 9, 4: 8, 5: 1, 6: 12, 7: 11, 8: 4, 8: 3, 10: 2, 11: 7, 12: 6, 1: 5};
1891	var arr_offset_map = {
1892		2: {left: -1, top: 10},
1893		3: {left: -1, top: 0},
1894		4: {left: -1, top: -10},
1895		5: {left: -10, top: -1},
1896		6: {left: 0, top: -1},
1897		7: {left: 10, top: -1},
1898		8: {left: 1, top: -10},
1899		9: {left: 1, top: 0},
1900		10: {left: 1, top: 10},
1901		11: {left: 10, top: 1},
1902		12: {left: 0, top: 1},
1903		1: {left: -10, top: 1},
1904	};
1905	jthis.xn_position(jmenu, pos, offset);
1906	jmenu.toggle();
1907	
1908	// arrow
1909	var mpos = arr_pos_map[pos];
1910	if(!option.hidearrow) jmenu.xn_position(jmenu.jarrow, mpos, arr_offset_map[mpos]);
1911	if(!option.hidearrow) jmenu.jarrow.toggle();
1912	var menu_hide = function(e) {
1913		if(jmenu.is(":hidden")) return;
1914		jmenu.toggle();
1915		if(!option.hidearrow) jmenu.jarrow.hide();
1916		jQuery('body').off('click', menu_hide);
1917	};
1918	
1919	jQuery('body').off('click', menu_hide).on('click', menu_hide);
1920};
1921
1922
1923jQuery.fn.xn_dropdown = function() {
1924	return this.each(function() {
1925		var jthis = jQuery(this);
1926		var jtoggler = jthis.find('.dropdown-toggle');
1927		var jdropmenu = jthis.find('.dropdown-menu');
1928		var pos = jthis.data('pos') || 5;
1929		var hidearrow = !!jthis.data('hidearrow');
1930		jtoggler.on('click', function() {
1931			jtoggler.xn_menu(jdropmenu, pos, {hidearrow: hidearrow});
1932			return false;
1933		});
1934	});
1935};
1936
1937jQuery.fn.xn_toggle = function() {
1938	return this.each(function() {
1939		var jthis = jQuery(this);
1940		var jtarget = jQuery(jthis.data('target'));
1941		var target_hide = function(e) {
1942			if(jtarget.is(":hidden")) return;
1943			jtarget.slideToggle('fast');
1944			jQuery('body').off('click', target_hide);
1945		};
1946		jthis.on('click', function() {
1947			jtarget.slideToggle('fast');
1948			jQuery('body').off('click', target_hide).on('click', target_hide);
1949			return false;
1950		});
1951	});
1952};
1953
1954jQuery('.xn-dropdown').xn_dropdown();
1955jQuery('.xn-toggle').xn_toggle();
1956
1957//console.log('xiuno.js loaded');
1958
1959

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.