text: /*
   * xiuno.js 封装了部分 PHP 常用的函数，便于代码移植和使用。
   * 技术支持： http://bbs.xiuno.com/
   * wellcms.cn 二次修改
   */
  
  /********************* 对 window 对象进行扩展 ************************/
  /*兼容 ie89*/
  if (!Object.keys) {
      Object.keys = function (o) {
          var arr = [];
          for (var k in o) {
              if (o.hasOwnProperty(k)) arr.push(k);
          }
          return arr;
      }
  }
  if (!Object.values) {
      Object.values = function (o) {
          var arr = [];
          if (!o) return arr;
          for (var k in o) {
              if (o.hasOwnProperty(k)) arr.push(o[k]);
          }
          return arr;
      }
  }
  Array.values = function (arr) {
      return xn.array_filter(arr);
  };
  
  Object.first = function (obj) {
      for (var k in obj) return obj[k];
  };
  Object.last = function (obj) {
      for (var k in obj);
      return obj[k];
  };
  Object.length = function (obj) {
      var n = 0;
      for (var k in obj) n++;
      return n;
  };
  Object.count = function (obj) {
      if (!obj) return 0;
      if (obj.length) return obj.length;
      var n = 0;
      for (k in obj) {
          if (obj.hasOwnProperty(k)) n++;
      }
      return n;
  };
  Object.sum = function (obj) {
      var sum = 0;
      $.each(obj, function (k, v) {
          sum += intval(v)
      });
      return sum;
  };
  if (typeof console == 'undefined') {
      console = {};
      console.log = function () {
      };
  }
  
  var well = {}; /*避免冲突，wellcms的命名空间*/
  /*从参数里获取数据 defval=0 整数型 空 字符串*/
  well.param = function (key, defval) {
      var reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)');
      var r = window.location.search.substr(1).match(reg);
      if (r != null) {
          if (defval == 0) {
              var i = parseInt(r[2]);
              return isNaN(i) ? 0 : i;
          } else {
              return decodeURI(r[2]);
          }
      }
      return '';
  };
  
  /********************* xn 模拟 php 函数 ************************/
  
  /*var xn = window; /!*browser， 如果要兼容以前的版本，请开启这里。*!/
   var xn = global; /!*nodejs*!/*/
  var xn = {}; /*避免冲突，自己的命名空间。*/
  
  /*针对国内的山寨套壳浏览器检测不准确*/
  xn.is_ie = (!!document.all) ? true : false;/*ie6789*/
  xn.is_ie_10 = navigator.userAgent.indexOf('Trident') != -1;
  xn.is_ff = navigator.userAgent.indexOf('Firefox') != -1;
  xn.in_mobile = ($(window).width() < 1140);
  xn.options = {}; /*全局配置*/
  xn.options.water_image_url = 'view/img/water-small.png';/*默认水印路径*/
  
  xn.htmlspecialchars = function (s) {
      s = s.replace(/</g, "&lt;");
      s = s.replace(/>/g, "&gt;");
      return s;
  };
  
  /*标准的 urlencode()*/
  xn._urlencode = function (s) {
      s = encodeURIComponent(s);
      s = xn.strtolower(s);
      return s;
  };
  
  /*标准的 urldecode()*/
  xn._urldecode = function (s) {
      s = decodeURIComponent(s);
      return s;
  };
  
  xn.urlencode = function (s) {
      s = encodeURIComponent(s);
      s = s.replace(/_/g, "%5f");
      s = s.replace(/\-/g, "%2d");
      s = s.replace(/\./g, "%2e");
      s = s.replace(/\~/g, "%7e");
      s = s.replace(/\!/g, "%21");
      s = s.replace(/\*/g, "%2a");
      s = s.replace(/\(/g, "%28");
      s = s.replace(/\)/g, "%29");
      /*s = s.replace(/\+/g, "%20");*/
      s = s.replace(/\%/g, "_");
      return s;
  };
  
  xn.urldecode = function (s) {
      s = s.replace(/_/g, "%");
      s = decodeURIComponent(s);
      return s;
  };
  
  /*兼容 3.0*/
  xn.xn_urlencode = xn.urlencode_safe;
  xn.xn_urldecode = xn.urldecode_safe;
  
  xn.nl2br = function (s) {
      s = s.replace(/\r\n/g, "\n");
      s = s.replace(/\n/g, "<br>");
      s = s.replace(/\t/g, "&nbsp; &nbsp; &nbsp; &nbsp; ");
      return s;
  };
  
  xn.time = function () {
      return xn.intval(Date.now() / 1000);
  };
  
  xn.intval = function (s) {
      var i = parseInt(s);
      return isNaN(i) ? 0 : i;
  };
  
  xn.floatval = function (s) {
      if (!s) return 0;
      if (s.constructor === Array) {
          for (var i = 0; i < s.length; i++) {
              s[i] = xn.floatval(s[i]);
          }
          return s;
      }
      var r = parseFloat(s);
      return isNaN(r) ? 0 : r;
  };
  
  xn.isset = function (k) {
      var t = typeof k;
      return t != 'undefined' && t != 'unknown';
  };
  
  xn.empty = function (s) {
      if (s == '0') return true;
      if (!s) {
          return true;
      } else {
          /*$.isPlainObject*/
          if (s.constructor === Object) {
              return Object.keys(s).length == 0;
          } else if (s.constructor === Array) {
              return s.length == 0;
          }
          return false;
      }
  };
  
  xn.ceil = Math.ceil;
  xn.round = Math.round;
  xn.floor = Math.floor;
  xn.f2y = function (i, callback) {
      if (!callback) callback = round;
      var r = i / 100;
      return callback(r);
  };
  xn.y2f = function (s) {
      var r = xn.round(xn.intval(s) * 100);
      return r;
  };
  xn.strtolower = function (s) {
      s += '';
      return s.toLowerCase();
  };
  xn.strtoupper = function (s) {
      s += '';
      return s.toUpperCase();
  };
  
  xn.json_type = function (o) {
      var _toS = Object.prototype.toString;
      var _types = {
          'undefined': 'undefined',
          'number': 'number',
          'boolean': 'boolean',
          'string': 'string',
          '[object Function]': 'function',
          '[object RegExp]': 'regexp',
          '[object Array]': 'array',
          '[object Date]': 'date',
          '[object Error]': 'error'
      };
      return _types[typeof o] || _types[_toS.call(o)] || (o ? 'object' : 'null');
  };
  
  xn.json_encode = function (o) {
      var json_replace_chars = function (chr) {
          var specialChars = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\'};
          return specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
      };
  
      var s = [];
      switch (xn.json_type(o)) {
          case 'undefined':
              return 'undefined';
              break;
          case 'null':
              return 'null';
              break;
          case 'number':
          case 'boolean':
          case 'date':
          case 'function':
              return o.toString();
              break;
          case 'string':
              return '"' + o.replace(/[\x00-\x1f\\"]/g, json_replace_chars) + '"';
              break;
          case 'array':
              for (var i = 0, l = o.length; i < l; i++) {
                  s.push(xn.json_encode(o[i]));
              }
              return '[' + s.join(',') + ']';
              break;
          case 'error':
          case 'object':
              for (var p in o) {
                  s.push('"' + p + '"' + ':' + xn.json_encode(o[p]));
              }
              return '{' + s.join(',') + '}';
              break;
          default:
              return '';
              break;
      }
  };
  
  xn.json_decode = function (s) {
      if (!s) return null;
      try {
          /*去掉广告代码。这行代码挺无语的，为了照顾国内很多人浏览器中广告病毒的事实。*/
          /*s = s.replace(/\}\s*<script[^>]*>[\s\S]*?<\/script>\s*$/ig, '}');*/
          if (s.match(/^<!DOCTYPE/i)) return null;
          var json = $.parseJSON(s);
          return json;
      } catch (e) {
          /*alert('JSON格式错误：' + s);*/
          /*window.json_error_string = s;	// 记录到全局*/
          return null;
      }
  };
  
  xn.clone = function (obj) {
      return xn.json_decode(xn.json_encode(obj));
  };
  
  /*方便移植 PHP 代码*/
  xn.min = function () {
      return Math.min.apply(this, arguments);
  };
  xn.max = function () {
      return Math.max.apply(this, arguments);
  };
  xn.str_replace = function (s, d, str) {
      var p = new RegExp(s, 'g');
      return str.replace(p, d);
  };
  xn.strrpos = function (str, s) {
      return str.lastIndexOf(s);
  };
  xn.strpos = function (str, s) {
      return str.indexOf(s);
  };
  xn.substr = function (str, start, len) {
      /*支持负数*/
      if (!str) return '';
      var end = length;
      var length = str.length;
      if (start < 0) start = length + start;
      if (!len) {
          end = length;
      } else if (len > 0) {
          end = start + len;
      } else {
          end = length + len;
      }
      return str.substring(start, end);
  };
  xn.explode = function (sep, s) {
      return s.split(sep);
  };
  xn.implode = function (glur, arr) {
      return arr.join(glur);
  };
  xn.array_merge = function (arr1, arr2) {
      return arr1 && arr1.__proto__ === Array.prototype && arr2 && arr2.__proto__ === Array.prototype ? arr1.concat(arr2) : $.extend(arr1, arr2);
  };
  /*比较两个数组的差异，在 arr1 之中，但是不在 arr2 中。返回差异结果集的新数组*/
  xn.array_diff = function (arr1, arr2) {
      if (arr1.__proto__ === Array.prototype) {
          var o = {};
          for (var i = 0, len = arr2.length; i < len; i++) o[arr2[i]] = true;
          var r = [];
          for (i = 0, len = arr1.length; i < len; i++) {
              var v = arr1[i];
              if (o[v]) continue;
              r.push(v);
          }
          return r;
      } else {
          var r = {};
          for (k in arr1) {
              if (!arr2[k]) r[k] = arr1[k];
          }
          return r;
      }
  };
  /*过滤空值，可以用于删除*/
  /*
   // 第一种用法：
   var arr = [0,1,2,3];
   delete arr[1];
   delete arr[2];
   arr = array_filter(arr);
  
   // 第二种：
   var arr = [0,1,2,3];
   array_filter(arr, function(k,v) { k == 1} );
   */
  xn.array_filter = function (arr, callback) {
      var newarr = [];
      for (var k in arr) {
          var v = arr[k];
          if (callback && callback(k, v)) continue;
          /*默认过滤空值*/
          /*if(!callback && v === undefined) continue;*/
          newarr.push(v);
      }
      return newarr;
  };
  xn.array_keys = function (obj) {
      var arr = [];
      $.each(obj, function (k) {
          arr.push(k);
      });
      return arr;
  };
  xn.array_values = function (obj) {
      var arr = [];
      $.each(obj, function (k, v) {
          arr.push(v);
      });
      return arr;
  };
  xn.in_array = function (v, arr) {
      return $.inArray(v, arr) != -1;
  };
  
  xn.rand = function (n) {
      var str = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
      var r = '';
      for (i = 0; i < n; i++) {
          r += str.charAt(Math.floor(Math.random() * str.length));
      }
      return r;
  };
  
  xn.random = function (min, max) {
      var num = Math.random() * (max - min + 1) + min;
      var r = Math.ceil(num);
      return r;
  };
  
  /*所谓的 js 编译模板，不过是一堆效率低下的正则替换，这种东西根据自己喜好用吧。*/
  xn.template = function (s, json) {
      /*console.log(json);*/
      for (k in json) {
          var r = new RegExp('\{(' + k + ')\}', 'g');
          s = s.replace(r, function (match, name) {
              return json[name];
          });
      }
      return s;
  };
  
  xn.is_mobile = function (s) {
      var r = /^\d{11}$/;
      if (!s) {
          return false;
      } else if (!r.test(s)) {
          return false;
      }
      return true;
  };
  
  xn.is_email = function (s) {
      var r = /^[\w\-\.]+@[\w\-\.]+(\.\w+)+$/i;
      if (!s) {
          return false;
      } else if (!r.test(s)) {
          return false;
      }
      return true;
  };
  
  xn.is_string = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object String]';
  };
  xn.is_function = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object Function]';
  };
  xn.is_array = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object Array]';
  };
  xn.is_number = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object Number]' || /^\d+$/.test(obj);
  };
  xn.is_regexp = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object RegExp]';
  };
  xn.is_object = function (obj) {
      return Object.prototype.toString.apply(obj) == '[object Object]';
  };
  xn.is_element = function (obj) {
      return !!(obj && obj.nodeType === 1);
  };
  
  xn.lang = function (key, arr) {
      var r = lang[key] ? lang[key] : "lang[" + key + "]";
      if (arr) {
          $.each(arr, function (k, v) {
              r = xn.str_replace("{" + k + "}", v, r);
          });
      }
      return r;
  };
  
  /* 
   js 版本的翻页函数
   */
  /*用例：pages('user-list-{page}.html', 100, 10, 5);*/
  xn.pages = function (url, totalnum, page, pagesize) {
      if (!page) page = 1;
      if (!pagesize) pagesize = 20;
      var totalpage = xn.ceil(totalnum / pagesize);
      if (totalpage < 2) return '';
      page = xn.min(totalpage, page);
      var shownum = 5;	/*显示多少个页 * 2*/
  
      var start = xn.max(1, page - shownum);
      var end = xn.min(totalpage, page + shownum);
  
      /*不足 $shownum，补全左右两侧*/
      var right = page + shownum - totalpage;
      if (right > 0) start = xn.max(1, start -= right);
      left = page - shownum;
      if (left < 0) end = xn.min(totalpage, end -= left);
  
      var s = '';
      if (page != 1) s += '<a href="' + xn.str_replace('{page}', page - 1, url) + '">◀</a>';
      if (start > 1) s += '<a href="' + xn.str_replace('{page}', 1, url) + '">1 ' + (start > 2 ? '... ' : '') + '</a>';
      for (i = start; i <= end; i++) {
          if (i == page) {
              s += '<a href="' + xn.str_replace('{page}', i, url) + '" class="active">' + i + '</a>';
          } else {
              s += '<a href="' + xn.str_replace('{page}', i, url) + '">' + i + '</a>';
          }
      }
      if (end != totalpage) s += '<a href="' + xn.str_replace('{page}', totalpage, url) + '">' + (totalpage - end > 1 ? '... ' : '') + totalpage + '</a>';
      if (page != totalpage) s += '<a href="' + xn.str_replace('{page}', page + 1, url) + '">▶</a>';
      return s;
  };
  
  xn.parse_url = function (url) {
      if (url.match(/^(([a-z]+):)\/\//i)) {
          var arr = url.match(/^(([a-z]+):\/\/)?([^\/\?#]+)\/*([^\?#]*)\??([^#]*)#?(\w*)$/i);
          if (!arr) return null;
          var r = {
              'schema': arr[2],
              'host': arr[3],
              'path': arr[4],
              'query': arr[5],
              'anchor': arr[6],
              'requesturi': arr[4] + (arr[5] ? '?' + arr[5] : '') + (arr[6] ? '#' + arr[6] : '')
          };
          console.log(r);
          return r;
      } else {
  
          var arr = url.match(/^([^\?#]*)\??([^#]*)#?(\w*)$/i);
          if (!arr) return null;
          var r = {
              'schema': '',
              'host': '',
              'path': arr[1],
              'query': arr[2],
              'anchor': arr[3],
              'requesturi': arr[1] + (arr[2] ? '?' + arr[2] : '') + (arr[3] ? '#' + arr[3] : '')
          };
          console.log(r);
          return r;
      }
  };
  
  xn.parse_str = function (str) {
      var sep1 = '=';
      var sep2 = '&';
      var arr = str.split(sep2);
      var arr2 = {};
      for (var x = 0; x < arr.length; x++) {
          var tmp = arr[x].split(sep1);
          arr2[unescape(tmp[0])] = unescape(tmp[1]).replace(/[+]/g, ' ');
      }
      return arr2;
  };
  
  /*解析 url 参数获取 $_GET 变量*/
  xn.parse_url_param = function (url) {
      var arr = xn.parse_url(url);
      var q = arr.path;
      var pos = xn.strrpos(q, '/');
      q = xn.substr(q, pos + 1);
      var r = [];
      if (xn.substr(q, -4) == '.html') {
          q = xn.substr(q, 0, -4);
          r = xn.explode('-', q);
          /*首页*/
      } else if (url && url != window.location && url != '.' && url != '/' && url != './') {
          r = ['thread', 'seo', url];
      }
  
      /*将 xxx.html?a=b&c=d 后面的正常的 _GET 放到 $_SERVER['_GET']*/
      if (!empty(arr['query'])) {
          var arr2 = xn.parse_str(arr['query']);
          r = xn.array_merge(r, arr2);
      }
      return r;
  };
  
  /*
   模拟服务端 url() 函数 extra = {k1:v,k2:v}
   url_access = true后台链接 2后台调用前台链接 3不受限后台链接(不受过滤限制)
   */
  xn.url = function (u, extra, url_access) {
      let on = window.url_rewrite_on || 0;
      url_access = url_access || window.url_access || false;
  
      if (xn.strpos(u, '/') != -1) {
          var path = xn.substr(u, 0, xn.strrpos(u, '/') + 1);
          var query = xn.substr(u, xn.strrpos(u, '/') + 1);
      } else {
          var path = '';
          var query = u;
      }
  
      var r = '';
      if (!on) {
          r = path + '?' + query + '.html';
      } else if (1 == on) {
          r = path + query + '.html';
      } else if (2 == on || 3 == on) {
          var url_path = window.url_path || '/';
          r = url_path + xn.str_replace('-', '/', query);
          if (2 == on) r = r + '.html';
      }
  
      var arr = xn.explode('-', query);
      var url_filter = window.url_filter || ['operate', 'attach', 'read', 'category', 'list', 'my', 'forum', 'thread'];
  
      if ((true === url_access && false === xn.in_array(arr[0], url_filter)) || 3 === url_access) {
          var str = '';
          $.each(arr, function (k, v) {
              str += '&' + k + '=' + v;
          });
          r = 'index.php?' + str.replace('&','');
      }
  
      if(2 === url_access && 2 > on) r = '../' + r;
  
      if (extra) {
          var args = '';
          $.each(extra, function (k, v) {
              args += '&' + k + '=' + v;
          });
          args = args.replace('&','');
          var sep = r.indexOf('?') == -1 ? '?' : '&';
          r += sep + args;
      }
  
      return r;
  };
  
  /*将参数添加到 URL*/
  xn.url_add_arg = function (url, k, v) {
      var pos = xn.strpos(url, '.html');
      if (pos === false) {
          return xn.strpos(url, '?') === false ? url + "&" + k + "=" + v : url + "?" + k + "=" + v;
      } else {
          return xn.substr(url, 0, pos) + '-' + v + xn.substr(url, pos);
      }
  };
  
  /*页面跳转的时间*/
  /*xn.jumpdelay = xn.debug ? 20000000 : 2000;*/
  
  /********************* 对 JQuery 进行扩展 ************************/
  
  $.location = function (url, seconds) {
      if (seconds === undefined) seconds = 1;
      setTimeout(function () {
          window.location = './';
      }, seconds * (debug ? 2000 : 1000));
  };
  
  /*二级数组排序*/
  /*Array.prototype.proto_sort = Array.prototype.sort;
   Array.prototype.sort = function(arg) {
   if(arg === undefined) {
   return this.proto_sort();
   } else if(arg.constructor === Function) {
   return this.proto_sort(arg);
   } else if(arg.constructor === Object) {
   var k = Object.first(arg);
   var v = arg[k];
   return this.proto_sort(function(a, b) {return v == 1 ? a[k] > b[k] : a[k] < b[k];});
   } else {
   return this;
   }
   }*/
  
  xn.arrlist_values = function (arrlist, key) {
      var r = [];
      arrlist.map(function (arr) {
          r.push(arr[key]);
      });
      return r;
  };
  
  xn.arrlist_key_values = function (arrlist, key, val, pre) {
      var r = {};
      var pre = pre || '';
      arrlist.map(function (arr) {
          r[arr[pre + key]] = arr[val];
      });
      return r;
  };
  
  xn.arrlist_keep_keys = function (arrlist, keys) {
      if (!xn.is_array(keys)) keys = [keys];
      for (k in arrlist) {
          var arr = arrlist[k];
          var newarr = {};
          for (k2 in keys) {
              var key = keys[k2];
              newarr[key] = arr[key];
          }
          arrlist[k] = newarr;
      }
      return arrlist;
  };
  /*var arrlist = [
   {uid:1, gid:3},
   {uid:2, gid:2},
   {uid:3, gid:1},
   ];
   var arrlist2 = xn.arrlist_keep_keys(arrlist, 'gid');
   console.log(arrlist2);*/
  
  xn.arrlist_multisort = function (arrlist, k, asc) {
      var arrlist = arrlist.sort(function (a, b) {
          if (a[k] == b[k]) return 0;
          var r = a[k] > b[k];
          r = asc ? r : !r;
          return r ? 1 : -1;
      });
      return arrlist;
  };
  /*
   var arrlist = [
   {uid:1, gid:3},
   {uid:2, gid:2},
   {uid:3, gid:1},
   ];
   var arrlist2 = xn.arrlist_multisort(arrlist, 'gid', false);
   console.log(arrlist2);
   */
  
  /*if(xn.is_ie) document.documentElement.addBehavior("#default#userdata");*/
  /*
   storage 比cookie储存更多
   */
  $.pdata = function (key, value) {
      var r = '';
      if (typeof value != 'undefined') {
          value = xn.json_encode(value);
      }
  
      /*HTML 5*/
      try {
          /*ie10 需要 try 一下*/
          if (window.localStorage) {
              if (typeof value == 'undefined') {
                  r = localStorage.getItem(key);
                  return xn.json_decode(r);
              } else {
                  return localStorage.setItem(key, value);
              }
          }
      } catch (e) {
      }
  
      /*HTML 4*/
      if (xn.is_ie && (!document.documentElement || typeof document.documentElement.load == 'unknown' || !document.documentElement.load)) {
          return '';
      }
      /*get*/
      if (typeof value == 'undefined') {
          if (xn.is_ie) {
              try {
                  document.documentElement.load(key);
                  r = document.documentElement.getAttribute(key);
              } catch (e) {
                  /*alert('$.pdata:' + e.message);*/
                  r = '';
              }
          } else {
              try {
                  r = sessionStorage.getItem(key) && sessionStorage.getItem(key).toString().length == 0 ? '' : (sessionStorage.getItem(key) == null ? '' : sessionStorage.getItem(key));
              } catch (e) {
                  r = '';
              }
          }
          return xn.json_decode(r);
          /*set*/
      } else {
          if (xn.is_ie) {
              try {
                  /*fix: IE TEST for ie6 崩溃*/
                  document.documentElement.load(key);
                  document.documentElement.setAttribute(key, value);
                  document.documentElement.save(key);
                  return document.documentElement.getAttribute(key);
              } catch (error) {/*alert('setdata:'+error.message);*/
              }
          } else {
              try {
                  return sessionStorage.setItem(key, value);
              } catch (error) {/*alert('setdata:'+error.message);*/
              }
          }
      }
  };
  
  /*time 单位为秒，与php setcookie, 和  misc::setcookie() 的 time 参数略有差异。*/
  $.cookie = function (name, value, time, path) {
      if (typeof value != 'undefined') {
          if (value === null) {
              var value = '';
              var time = -1;
          }
          if (typeof time != 'undefined') {
              date = new Date();
              date.setTime(date.getTime() + (time * 1000));
              var time = '; expires=' + date.toUTCString();
          } else {
              var time = '';
          }
          var path = path ? '; path=' + path : '';
          /*var domain = domain ? '; domain=' + domain : '';*/
          /*var secure = secure ? '; secure' : '';*/
          document.cookie = name + '=' + encodeURIComponent(value) + time + path;
      } else {
          var v = '';
          if (document.cookie && document.cookie != '') {
              var cookies = document.cookie.split(';');
              for (var i = 0; i < cookies.length; i++) {
                  var cookie = $.trim(cookies[i]);
                  if (cookie.substring(0, name.length + 1) == (name + '=')) {
                      v = decodeURIComponent(cookie.substring(name.length + 1)) + '';
                      break;
                  }
              }
          }
          return v;
      }
  };
  
  /*改变Location URL ?*/
  $.xget = function (url, callback, retry) {
      if (retry === undefined) retry = 1;
      $.ajax({
          type: 'GET',
          url: url,
          dataType: 'text',
          timeout: 15000,
          xhrFields: {withCredentials: true},
          success: function (r) {
              if (!r) return callback(-100, 'Server Response Empty!');
              var s = xn.json_decode(r);
              if (!s) {
                  return callback(-101, r); /*'Server Response xn.json_decode() failed：'+*/
              }
              if (s.code === undefined) {
                  if ($.isPlainObject(s)) {
                      return callback(0, s);
                  } else {
                      return callback(-102, r); /*'Server Response Not JSON 2：'+*/
                  }
              } else if (s.code == 0) {
                  return callback(0, s.message);
                  /*系统错误*/
              } else if (s.code < 0) {
                  return callback(s.code, s.message);
                  /*业务逻辑错误*/
              } else {
                  return callback(s.code, s.message);
  
              }
          },
          /*网络错误，重试*/
          error: function (xhr, type) {
              if (retry > 1) {
                  $.xget(url, callback, retry - 1);
              } else {
                  if ((type != 'abort' && type != 'error') || xhr.status == 403 || xhr.status == 404) {
                      return callback(-1000, "xhr.responseText:" + xhr.responseText + ', type:' + type);
                  } else {
                      return callback(-1001, "xhr.responseText:" + xhr.responseText + ', type:' + type);
                      console.log("xhr.responseText:" + xhr.responseText + ', type:' + type);
                  }
              }
          }
      });
  };
  
  /*ajax progress plugin*/
  (function ($, window, undefined) {
      /*is onprogress supported by browser?*/
      var hasOnProgress = ("onprogress" in $.ajaxSettings.xhr());
  
      /*If not supported, do nothing*/
      if (!hasOnProgress) {
          return;
      }
  
      /*patch ajax settings to call a progress callback*/
      var oldXHR = $.ajaxSettings.xhr;
      $.ajaxSettings.xhr = function () {
          var xhr = oldXHR();
          if (xhr instanceof window.XMLHttpRequest) {
              xhr.addEventListener('progress', this.progress, false);
          }
  
          if (xhr.upload) {
              xhr.upload.addEventListener('progress', this.progress, false);
          }
  
          return xhr;
      };
  })(jQuery, window);
  
  $.unparam = function (str) {
      return str.split('&').reduce(function (params, param) {
          var paramSplit = param.split('=').map(function (value) {
              return decodeURIComponent(value.replace('+', ' '));
          });
          params[paramSplit[0]] = paramSplit[1];
          return params;
      }, {});
  };
  
  $.xpost = function (url, postdata, callback, progress_callback) {
      if ($.isFunction(postdata)) {
          callback = postdata;
          postdata = null;
      }
  
      $.ajax({
          type: 'POST',
          url: url,
          data: postdata,
          dataType: 'text',
          timeout: 6000000,
          progress: function (e) {
              if (e.lengthComputable) {
                  if (progress_callback) progress_callback(e.loaded / e.total * 100);
              }
          },
          success: function (r) {
              if (!r) return callback(-1, 'Server Response Empty!');
              var s = xn.json_decode(r);
              if (!s || s.code === undefined) return callback(-1, 'Server Response Not JSON：' + r);
              if (s.code == 0) {
                  return callback(0, s.message);
              } else if (s.code < 0) {
                  return callback(s.code, s.message);
              } else {
                  return callback(s.code, s.message);
              }
          },
          error: function (xhr, type) {
              if (type != 'abort' && type != 'error' || xhr.status == 403) {
                  return callback(-1000, "xhr.responseText:" + xhr.responseText + ', type:' + type);
              } else {
                  return callback(-1001, "xhr.responseText:" + xhr.responseText + ', type:' + type);
                  console.log("xhr.responseText:" + xhr.responseText + ', type:' + type);
              }
          }
      });
  };
  
  /*
   $.xpost = function(url, postdata, callback, progress_callback) {
   //构造表单数据
   if(xn.is_string(postdata)) {
   postdata = xn.is_string(postdata) ? $.unparam(postdata) : postdata;
   }
   var formData = new FormData();
   for(k in postdata) {
   formData.append(k, postdata[k]);
   }
  
   //创建xhr对象
   var xhr = new XMLHttpRequest();
  
   //设置xhr请求的超时时间
   xhr.timeout = 6000000;
  
   //设置响应返回的数据格式
   xhr.responseType = "text";
  
   //创建一个 post 请求，采用异步
   xhr.open('POST', url, true);
  
   xhr.setRequestHeader("Content_type", "application/x-www-form-urlencoded");
   xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
  
   //注册相关事件回调处理函数
   xhr.onload = function(e) {
   if(this.status == 200 || this.status == 304) {
   var r = this.response;
   if(!r) return callback(-1, 'Server Response Empty!');
   var s = xn.json_decode(r);
   if(!s || s.code === undefined) return callback(-1, 'Server Response Not JSON：'+r);
   if(s.code == 0) {
   return callback(0, s.message);
   //系统错误
   } else if(s.code < 0) {
   return callback(s.code, s.message);
   } else {
   return callback(s.code, s.message);
   }
   } else {
   console.log(e);
   }
   };
   xhr.ontimeout = function(e) {
   console.log(e);
   return callback(-1, 'Ajax request timeout:'+url);
  
   };
   xhr.onerror = function(e) {
   console.log(e);
   return callback(-1, 'Ajax request error');
   };
   xhr.upload.onprogress = function(e) {
   if (e.lengthComputable) {
   if(progress_callback) progress_callback(xn.intval(e.loaded / e.total * 100));
   //console.log('progress1:'+e.loaded / e.total * 100 + '%');
   }
   };
  
   //发送数据
   xhr.send(formData);
   };
   */
  
  /*
   功能：
   异步加载 js, 加载成功以后 callback
   用法：
   $.require('1.js', '2.js', function() {
   alert('after all loaded');
   });
   $.require(['1.js', '2.js' function() {
   alert('after all loaded');
   }]);
   */
  /*区别于全局的 node.js require 关键字*/
  $.required = [];
  $.require = function () {
      var args = null;
      if (arguments[0] && typeof arguments[0] == 'object') { /*如果0 为数组*/
          args = arguments[0];
          if (arguments[1]) args.push(arguments[1]);
      } else {
          args = arguments;
      }
      this.load = function (args, i) {
          var _this = this;
          if (args[i] === undefined) return;
          if (typeof args[i] == 'string') {
              var js = args[i];
              /*避免重复加载*/
              if ($.inArray(js, $.required) != -1) {
                  if (i < args.length) this.load(args, i + 1);
                  return;
              }
              $.required.push(js);
              var script = document.createElement("script");
              script.src = js;
              script.onerror = function () {
                  console.log('script load error:' + js);
                  _this.load(args, i + 1);
              };
              if (xn.is_ie) {
                  script.onreadystatechange = function () {
                      if (script.readyState == 'loaded' || script.readyState == 'complete') {
                          _this.load(args, i + 1);
                          script.onreadystatechange = null;
                      }
                  };
              } else {
                  script.onload = function () {
                      _this.load(args, i + 1);
                  };
              }
              document.getElementsByTagName('head')[0].appendChild(script);
          } else if (typeof args[i] == 'function') {
              var f = args[i];
              f();
              if (i < args.length) this.load(args, i + 1);
          } else {
              _this.load(args, i + 1);
          }
      };
      this.load(args, 0);
  };
  
  $.require_css = function (filename) {
      /*判断重复加载*/
      var tags = document.getElementsByTagName('link');
      for (var i = 0; i < tags.length; i++) {
          if (tags[i].href.indexOf(filename) != -1) {
              return false;
          }
      }
  
      var link = document.createElement("link");
      link.rel = "stylesheet";
      link.type = "text/css";
      link.href = filename;
      document.getElementsByTagName('head')[0].appendChild(link);
  };
  
  /*在节点上显示 loading 图标*/
  $.fn.loading = function (action) {
      return this.each(function () {
          var jthis = $(this);
          jthis.css('position', 'relative');
          if (!this.jloading) this.jloading = $('<div class="loading"><img src="static/loading.gif" /></div>').appendTo(jthis);
          var jloading = this.jloading.show();
          if (!action) {
              var offset = jthis.position();
              var left = offset.left;
              var top = offset.top;
              var w = jthis.width();
              var h = xn.min(jthis.height(), $(window).height());
              var left = w / 2 - jloading.width() / 2;
              var top = (h / 2 - jloading.height() / 2) * 2 / 3;
              jloading.css('position', 'absolute').css('left', left).css('top', top);
          } else if (action == 'close') {
              jloading.remove();
              this.jloading = null;
          }
      });
  };
  
  /*对图片进行缩略，裁剪，然后 base64 存入 form 隐藏表单，name 与 file 控件相同*/
  /*上传过程中，禁止 button，对图片可以缩略*/
  $.fn.base64_encode_file = function (width, height, action) {
      var action = action || 'thumb';
      var jform = $(this);
      var jsubmit = jform.find('input[type="submit"]');
      jform.on('change', 'input[type="file"]', function (e) {
  
          var jfile = $(this);
          var jassoc = jfile.data('assoc') ? $('#' + jfile.data('assoc')) : null;
          var obj = e.target;
          jsubmit.button('disabled');
          var file = obj.files[0];
  
          /*燃烧的冰20181111 删除同一个域*/
          $('.input_' + jassoc.attr('id')).removeDeep();
          /*燃烧的冰20181111 创建一个隐藏域，用来保存 base64 数据*/
          var jhidden = $('<input class="input_' + jassoc.attr('id') + '" type="hidden" name="' + obj.name + '" />').appendTo(jform);
  
          var reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = function (e) {
              /*如果是图片，并且设置了，宽高，和剪切模式*/
              if (width && height && xn.substr(this.result, 0, 10) == 'data:image') {
                  xn.image_resize(this.result, function (code, message) {
                      if (code == 0) {
                          if (jassoc) jassoc.attr('src', message.data);
                          jhidden.val(message.data); /*base64*/
                      } else {
                          alert(message);
                      }
                      jsubmit.button('reset');
                  }, {width: width, height: height, action: action});
              } else {
                  if (jassoc) jassoc.attr('src', this.result);
                  jhidden.val(this.result);
                  jsubmit.button('reset');
              }
          }
      });
  };
  
  xn.base64_data_image_type = function (s) {
      /*data:image/png;base64*/
      r = s.match(/^data:image\/(\w+);/i);
      return r[1];
  };
  
  /*图片背景透明算法 by axiuno@gmail.com，只能处理小图片，效率做过改进，目前速度还不错*/
  xn.image_background_opacity = function (data, width, height, callback) {
      var x = 0;
      var y = 0;
      /*var map = {};*/ /*图片的状态位： 0: 未检测，1:检测过是背景，2：检测过不是背景*/
      /*var unmap = {};*/ /*未检测过的 map*/
      var checked = {'0-0': 1}; /*检测过的点*/
      var unchecked = {}; /*未检测过的点，会不停得将新的未检测的点放进来，检测过的移动到 checked;*/
      var unchecked_arr = []; /*用来加速*/
      /*从四周遍历*/
      /*
       *************************************
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *                                   *
       *************************************
       */
      for (var i = 0; i < width; i++) {
          var k1 = i + '-0';
          var k2 = i + '-' + (height - 1);
          unchecked[k1] = 1;
          unchecked[k2] = 1;
          unchecked_arr.push(k1);
          unchecked_arr.push(k2);
      }
      for (var i = 1; i < height - 1; i++) {
          var k1 = '0-' + i;
          var k2 = (width - 1) + '-' + i;
          unchecked[k1] = 1;
          unchecked[k2] = 1;
          unchecked_arr.push(k1);
          unchecked_arr.push(k2);
      }
  
      var bg = [data[0], data[1], data[2], data[3]];
      /*如果不是纯黑，纯白，则返回。*/
      if (!((bg[0] == 0 && bg[1] == 0 && bg[2] == 0) || (bg[0] == 255 && bg[1] == 255 && bg[2] == 255))) return;
      /*判断该点是否被检测过。*/
      /*
       function is_checked(x, y) {
       return checked[x+'-'+y] ? true : false;
       }
       function is_unchecked(x, y) {
       return unchecked[x+'-'+y] ? true : false;
       }*/
  
      function get_one_unchecked() {
          if (unchecked_arr.length == 0) return false;
          var k = unchecked_arr.pop();
          var r = xn.explode('-', k);
          return r;
      }
  
      function checked_push(x, y) {
          var k = x + '-' + y;
          if (checked[k] === undefined) checked[k] = 1;
      }
  
      function unchecked_push(x, y) {
          var k = x + '-' + y;
          if (checked[k] === undefined && unchecked[k] === undefined) {
              unchecked[k] = 1;
              unchecked_arr.push(k);
          }
      }
  
      var n = 0;
      while (1) {
          /*//if(k++ > 100000) break;
           //if(checked.length > 10000) return;
           //(n++ % 10000 == 0) {
           //alert(n);
           //console.log(unchecked_arr);
           //console.log(unchecked);
           //break;
           //}
           // 遍历未检测的区域，并且不在 checked 列表的，放进去。*/
          var curr = get_one_unchecked();
          /*//if(unchecked.length > 1000) return;
           // 遍历完毕，终止遍历*/
          if (!curr) break;
          var x = xn.intval(curr[0]);
          var y = xn.intval(curr[1]);
  
          /*// 在 data 中的偏移量应该 * 4, rgba 各占一位。*/
          var pos = 4 * ((y * width) + x);
          var r = data[pos];
          var g = data[pos + 1];
          var b = data[pos + 2];
          var a = data[pos + 3];
  
          if (Math.abs(r - bg[0]) < 2 && Math.abs(g == bg[1]) < 2 && Math.abs(b == bg[2]) < 2) {
  
              if (!callback) {
                  data[pos + 0] = 0; /*处理为透明*/
                  data[pos + 1] = 0; /*处理为透明*/
                  data[pos + 2] = 0; /*处理为透明*/
                  data[pos + 3] = 0; /*处理为透明*/
              } else {
                  callback(data, pos);
              }
  
              /*检测边距*/
              if (y > 0) unchecked_push(x, y - 1);	 /*top*/
              if (x < width - 1) unchecked_push(x + 1, y); /*right*/
              if (y < height - 1) unchecked_push(x, y + 1); /*bottom*/
              if (x > 0) unchecked_push(x - 1, y); 	/*left*/
          }
  
          checked_push(x, y); /*save*/
      }
  };
  
  xn.image_file_type = function (file_base64_data) {
      var pre = xn.substr(file_base64_data, 0, 14);
      if (pre == 'data:image/gif') {
          return 'gif';
      } else if (pre == 'data:image/jpe' || pre == 'data:image/jpg') {
          return 'jpg';
      } else if (pre == 'data:image/png') {
          return 'png';
      }
      return 'jpg';
  };
  
  /*对图片进行裁切，缩略，对黑色背景，透明化处理*/
  xn.image_resize = function (file_base64_data, callback, options) {
      var thumb_width = options.width || 2560;
      var thumb_height = options.height || 4960;
      var action = options.action || 'thumb';
      var filetype = options.filetype || xn.image_file_type(file_base64_data);/*xn.base64_data_image_type(file_base64_data);*/
      var qulity = options.qulity || 0.75; /*图片质量, 1 为无损*/
  
      if (thumb_width < 1) return callback(-1, '缩略图宽度不能小于 1 / thumb image width length is less 1 pix');
      if (xn.substr(file_base64_data, 0, 10) != 'data:image') return callback(-1, '传入的 base64 数据有问题 / deformed base64 data');
      /*&& xn.substr(file_base64_data, 0, 14) != 'data:image/gif' gif 不支持\*/
  
      var img = new Image();
      img.onload = function () {
  
          var water_img_onload = function (water_on) {
              var canvas = document.createElement('canvas');
              /*等比缩放*/
              var width = 0, height = 0, canvas_width = 0, canvas_height = 0;
              var dx = 0, dy = 0;
  
              var img_width = img.width;
              var img_height = img.height;
  
              if (xn.substr(file_base64_data, 0, 14) == 'data:image/gif') return callback(0, {
                  width: img_width,
                  height: img_height,
                  data: file_base64_data
              });
  
              /*// width, height: 计算出来的宽高（求）
               // thumb_width, thumb_height: 要求的缩略宽高
               // img_width, img_height: 原始图片宽高
               // canvas_width, canvas_height: 画布宽高*/
              if (action == 'thumb') {
                  if (img_width < thumb_width && img_height && thumb_height) {
                      width = img_width;
                      height = img_height;
                  } else {
                      /*// 横形*/
                      if (img_width / img_height > thumb_width / thumb_height) {
                          var width = thumb_width; /*// 以缩略图宽度为准，进行缩放*/
                          var height = Math.ceil((thumb_width / img_width) * img_height);
                          /*// 竖形*/
                      } else {
                          var height = thumb_height; /*// 以缩略图宽度为准，进行缩放*/
                          var width = Math.ceil((img_width / img_height) * thumb_height);
                      }
                  }
                  canvas_width = width;
                  canvas_height = height;
              } else if (action == 'clip') {
                  /*原始宽<裁切宽*/
                  if (img_width < thumb_width && img_height && thumb_height) {
                      /*原始高>裁切高 以宽度为标准*/
                      if (img_height > thumb_height) {
                          thumb_width = width = img_width;
                          /*thumb_height = height = thumb_height;*/
                      } else {
                          /*原始高<裁切高*/
                          thumb_width = width = img_width;
                          thumb_height = height = img_height;
                      }
                  } else {
                      /*原始宽<裁切宽 横形图*/
                      if (img_width / img_height > thumb_width / thumb_height) {
                          /*原始资源比 > 裁切资源比 以宽度作为标准裁切缩放*/
                          var height = thumb_height; /*// 以缩略图宽度为准，进行缩放*/
                          var width = Math.ceil((img_width / img_height) * thumb_height);
                          var dx = -((width - thumb_width) / 2);
                          var dy = 0;
  
                      } else {
                          /*竖形*/
                          var width = thumb_width; /*// 以缩略图宽度为准，进行缩放*/
                          var height = Math.ceil((img_height / img_width) * thumb_width);
                          dx = 0;
                          dy = -((height - thumb_height) / 2);
                      }
                  }
                  canvas_width = thumb_width;
                  canvas_height = thumb_height;
              }
              canvas.width = canvas_width;
              canvas.height = canvas_height;
              var ctx = canvas.getContext("2d");
  
              /*//ctx.fillStyle = 'rgb(255,255,255)';
               //ctx.fillRect(0,0,width,height);*/
  
              ctx.clearRect(0, 0, width, height); /*canvas清屏*/
              ctx.drawImage(img, 0, 0, img_width, img_height, dx, dy, width, height); /*将图像绘制到canvas上*/
  
              if (water_on) {
                  var water_width = water_img.width;
                  var water_height = water_img.height;
                  if (img_width > 400 && img_width > water_width && water_width > 4) {
                      var x = img_width - water_width - 16;
                      var y = img_height - water_height - 16;
  
                      /*// 参数参考：https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage*/
                      ctx.globalAlpha = 0.3; /*// 水印透明度*/
                      ctx.beginPath();
                      ctx.drawImage(water_img, 0, 0, water_width, water_height, x, y, water_width, water_height); /*// 将水印图像绘制到canvas上*/
                      ctx.closePath();
                      ctx.save();
                  }
              }
  
              var imagedata = ctx.getImageData(0, 0, canvas_width, canvas_height);
              var data = imagedata.data;
              /*// 判断与 [0,0] 值相同的并且连续的像素为背景
  
               //xn.image_background_opacity(data, canvas_width, canvas_height);
  
               // 将修改后的代码复制回画布中*/
              ctx.putImageData(imagedata, 0, 0);
  
              /*//filetype = 'png';*/
              if (filetype == 'jpg') filetype = 'jpeg';
              var s = canvas.toDataURL('image/' + filetype, qulity);
              if (callback) callback(0, {width: width, height: height, data: s});
  
          };
  
          var water_img = new Image();
          water_img.onload = function () {
              water_img_onload(true);
          };
          water_img.onerror = function () {
              water_img_onload(false);
          };
          water_img.src = options.water_image_url || xn.options.water_image_url;
          if (!water_img.src) {
              water_img_onload(false);
          }
      };
      img.onerror = function (e) {
          console.log(e);
          alert(e);
      };
      img.src = file_base64_data;
  };
  
  /*
   用法：
   var file = e.target.files[0]; // 文件控件 onchange 后触发的 event;
   var upload_url = 'xxx.php'; // 服务端地址
   var postdata = {width: 2048, height: 4096, action: 'thumb', filetype: 'jpg'}; // postdata|options 公用，一起传给服务端。
   var progress = function(percent) { console.log('progress:'+ percent); }}; // 如果是图片，会根据此项设定进行缩略和剪切 thumb|clip
   xn.upload_file(file, upload_url, postdata, function(code, json) {
   // 成功
   if(code == 0) {
   console.log(json.url);
   console.log(json.width);
   console.log(json.height);
   } else {
   alert(json);
   }
   }, progress);
   */
  xn.upload_file = function (file, upload_url, postdata, complete_callback, progress_callback, thumb_callback) {
      postdata = postdata || {};
      postdata.width = postdata.width || 2560;
      postdata.height = postdata.height || 4960;
  
      var ajax_upload_file = function (base64_data) {
          var ajax_upload = function (upload_url, postdata, complete_callback) {
              $.xpost(upload_url, postdata, function (code, message) {
                  if (code != 0) return complete_callback(code, message);
                  if (complete_callback) complete_callback(0, message);
              }, function (percent) {
                  if (progress_callback) progress_callback(percent);
              });
          };
  
          /*// gif 直接上传
           // 图片进行缩放，然后上传
           //  && xn.substr(base64_data, 0, 14) != 'data:image/gif'*/
          if (xn.substr(base64_data, 0, 10) == 'data:image') {
              var filename = file.name ? file.name : (file.type == 'image/png' ? 'capture.png' : 'capture.jpg');
              xn.image_resize(base64_data, function (code, message) {
                  if (code != 0) return alert(message);
                  /*// message.width, message.height 是缩略后的宽度和高度*/
                  postdata.name = filename;
                  postdata.data = message.data;
                  postdata.width = message.width;
                  postdata.height = message.height;
                  ajax_upload(upload_url, postdata, complete_callback);
              }, postdata);
              /*// 文件直接上传， 不缩略*/
          } else {
              var filename = file.name ? file.name : '';
              postdata.name = filename;
              postdata.data = base64_data;
              postdata.width = 0;
              postdata.height = 0;
              ajax_upload(upload_url, postdata, complete_callback);
          }
      };
  
      /*// 如果为 base64 则不需要 new FileReader()*/
      if (xn.is_string(file) && xn.substr(file, 0, 10) == 'data:image') {
          var base64_data = file;
          if (thumb_callback) thumb_callback(base64_data);
          ajax_upload_file(base64_data);
      } else {
          var reader = new FileReader();
          reader.readAsDataURL(file);
          reader.onload = function () {
              var base64_data = this.result;
              if (thumb_callback) thumb_callback(base64_data);
              ajax_upload_file(base64_data);
          }
      }
  
  };
  
  /*从事件对象中查找 file 对象，兼容 jquery event, clipboard, file.onchange*/
  xn.get_files_from_event = function (e) {
      function get_paste_files(e) {
          return e.clipboardData && e.clipboardData.items ? e.clipboardData.items : null;
      }
  
      function get_drop_files(e) {
          return e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
      }
  
      if (e.originalEvent) e = e.originalEvent;
      if (e.type == 'change' && e.target && e.target.files && e.target.files.length > 0) return e.target.files;
      var files = e.type == 'paste' ? get_paste_files(e) : get_drop_files(e);
      return files;
  };
  
  /*获取所有的 父节点集合，一直到最顶层节点为止。, IE8 没有 HTMLElement*/
  xn.nodeHasParent = function (node, topNode) {
      if (!topNode) topNode = document.body;
      var pnode = node.parentNode;
      while (pnode) {
          if (pnode == topNode) return true;
          pnode = pnode.parentNode;
      }
      return false;
  };
  
  /*表单提交碰到错误的时候，依赖此处，否则错误会直接跳过，不利于发现错误*/
  window.onerror = function (msg, url, line) {
      if (!window.debug) return;
      alert("error: " + msg + "\r\n line: " + line + "\r\n url: " + url);
      /*阻止所有的 form 提交动作*/
      return false;
  };
  
  /*remove() 并不清除子节点事件！！用来替代 remove()，避免内存泄露*/
  $.fn.removeDeep = function () {
      this.each(function () {
          $(this).find('*').off();
      });
      this.off();
      this.remove();
      return this;
  };
  
  /*empty 清楚子节点事件，释放内存。*/
  $.fn.emptyDeep = function () {
      this.each(function () {
          $(this).find('*').off();
      });
      this.empty();
      return this;
  };
  
  $.fn.son = $.fn.children;
  
  /*
   用来增强 $.fn.val()
   用来选中和获取 select radio checkbox 的值，用法：
   $('#select1').checked(1);			// 设置 value="1" 的 option 为选中状态
   $('#select1').checked();			// 返回选中的值。
   $('input[type="checkbox"]').checked([2,3,4]);	// 设置 value="2" 3 4 的 checkbox 为选中状态
   $('input[type="checkbox"]').checked();		// 获取选中状态的 checkbox 的值，返回 []
   $('input[type="radio"]').checked(2);		// 设置 value="2" 的 radio 为选中状态
   $('input[type="radio"]').checked();		// 返回选中状态的 radio 的值。
   */
  $.fn.checked = function (v) {
      /*转字符串*/
      if (v) v = v instanceof Array ? v.map(function (vv) {
          return vv + ""
      }) : v + "";
      var filter = function () {
          return !(v instanceof Array) ? (this.value == v) : ($.inArray(this.value, v) != -1)
      };
      /*设置*/
      if (v) {
          this.each(function () {
              if (xn.strtolower(this.tagName) == 'select') {
                  $(this).find('option').filter(filter).prop('selected', true);
              } else if (xn.strtolower(this.type) == 'checkbox' || strtolower(this.type) == 'radio') {
                  /*console.log(v);*/
                  $(this).filter(filter).prop('checked', true);
              }
          });
          return this;
          /*获取，值用数组的方式返回*/
      } else {
          if (this.length == 0) return [];
          var tagtype = xn.strtolower(this[0].tagName) == 'select' ? 'select' : xn.strtolower(this[0].type);
          var r = (tagtype == 'checkbox' ? [] : '');
          for (var i = 0; i < this.length; i++) {
              var tag = this[i];
              if (tagtype == 'select') {
                  var joption = $(tag).find('option').filter(function () {
                      return this.selected == true
                  });
                  if (joption.length > 0) return joption.attr('value');
              } else if (tagtype == 'checkbox') {
                  if (tag.checked) r.push(tag.value);
              } else if (tagtype == 'radio') {
                  if (tag.checked) return tag.value;
              }
          }
          return r;
      }
  };
  
  /*支持连续操作 jsubmit.button(message).delay(1000).button('reset');*/
  $.fn.button = function (status) {
      return this.each(function () {
          var jthis = $(this);
          jthis.queue(function (next) {
              var loading_text = jthis.attr('loading-text') || jthis.data('loading-text');
              if (status == 'loading') {
                  jthis.prop('disabled', true).addClass('disabled').attr('default-text', jthis.text());
                  jthis.html(loading_text);
              } else if (status == 'disabled') {
                  jthis.prop('disabled', true).addClass('disabled');
              } else if (status == 'enable') {
                  jthis.prop('disabled', false).removeClass('disabled');
              } else if (status == 'reset') {
                  jthis.prop('disabled', false).removeClass('disabled');
                  if (jthis.attr('default-text')) {
                      jthis.text(jthis.attr('default-text'));
                  }
              } else {
                  jthis.text(status);
              }
              next();
          });
      });
  };
  
  /*支持连续操作 jsubmit.button(message).delay(1000).button('reset').delay(1000).location('http://xxxx');*/
  $.fn.location = function (href) {
      var jthis = this;
      jthis.queue(function (next) {
          if (!href) {
              window.location.reload();
          } else {
              window.location = href;
          }
          next();
      });
  };
  
  /*在控件上方提示错误信息，如果为手机版，则调用 toast*/
  $.fn.alert = function (message) {
      var jthis = $(this);
      jpthis = jthis.parent('.form-group');
      jpthis.addClass('has-danger');
      jthis.addClass('form-control-danger');
      jthis.data('title', message).tooltip('show');
      return this;
  };
  
  $.fn.serializeObject = function () {
      var self = this,
          json = {},
          push_counters = {},
          patterns = {
              "validate": /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
              "key": /[a-zA-Z0-9_]+|(?=\[\])/g,
              "push": /^$/,
              "fixed": /^\d+$/,
              "named": /^[a-zA-Z0-9_]+$/
          };
  
      this.build = function (base, key, value) {
          base[key] = value;
          return base;
      };
  
      this.push_counter = function (key) {
          if (push_counters[key] === undefined) {
              push_counters[key] = 0;
          }
          return push_counters[key]++;
      };
  
      $.each($(this).serializeArray(), function () {
          if (!patterns.validate.test(this.name)) {
              return;
          }
  
          var k,
              keys = this.name.match(patterns.key),
              merge = this.value,
              reverse_key = this.name;
  
          while ((k = keys.pop()) !== undefined) {
              reverse_key = reverse_key.replace(new RegExp("\\[" + k + "\\]$"), '');
              if (k.match(patterns.push)) {
                  merge = self.build([], self.push_counter(reverse_key), merge);
              } else if (k.match(patterns.fixed)) {
                  merge = self.build([], k, merge);
              } else if (k.match(patterns.named)) {
                  merge = self.build({}, k, merge);
              }
          }
  
          json = $.extend(true, json, merge);
      });
  
      return json;
  };
  
  /*
   $.fn.serializeObject = function() {
   var o = {};
   var a = this.serializeArray();
   $.each(a, function() {
   if(o[this.name]) {
   if(!o[this.name].push) {
   o[this.name] = [o[this.name]];
   }
   o[this.name].push(this.value || '');
   } else {
   o[this.name] = this.value || '';
   }
   });
   return o;
   };*/
  
  /*
   $.fn.serializeObject = function() {
   var formobj = {};
   $([].slice.call(this.get(0).elements)).each(function() {
   var jthis = $(this);
   var type = jthis.attr('type');
   var name = jthis.attr('name');
   if (name && xn.strtolower(this.nodeName) != 'fieldset' && !this.disabled && type != 'submit' && type != 'reset' && type != 'button' &&
   ((type != 'radio' && type != 'checkbox') || this.checked)) {
   // 还有一些情况没有考虑, 比如: hidden 或 text 类型使用 name 数组时
   if(type == 'radio' || type == 'checkbox') {
   if(!formobj[name]) formobj[name] = [];
   formobj[name].push(jthis.val());
   }else{
   formobj[name] = jthis.val();
   }
   }
   })
   return formobj;
   }*/
  
  /*批量修改 input name="gid[123]" 中的 123 的值*/
  $.fn.attr_name_index = function (rowid) {
      return this.each(function () {
          var jthis = $(this);
          var name = jthis.attr('name');
          name = name.replace(/\[(\d*)\]/, function (all, oldid) {
              var newid = rowid === undefined ? xn.intval(oldid) + 1 : rowid;
              return '[' + newid + ']';
          });
          jthis.attr('name', name);
      });
  };
  
  /*重置 form 状态*/
  $.fn.reset = function () {
      var jform = $(this);
      jform.find('input[type="submit"]').button('reset');
      jform.find('input').tooltip('dispose');
  };
  
  /*用来代替 <base href="../" /> 的功能*/
  $.fn.base_href = function (base) {
      function replace_url(url) {
          if (url.match('/^https?:\/\//i')) {
              return url;
          } else {
              return base + url;
          }
      }
  
      this.find('img').each(function () {
          var jthis = $(this);
          var src = jthis.attr('src');
          if (src) jthis.attr('src', replace_url(src));
      });
      this.find('a').each(function () {
          var jthis = $(this);
          var href = jthis.attr('href');
          if (href) jthis.attr('href', replace_url(href));
      });
      return this;
  };
  
  /*$.each() 的串行版本，用法：*/
  /*
   $.each_sync(items, function(i, callback) {
   var item = items[i];
   $.post(url, function() {
   // ...
   callback();
   });
   });
   */
  $.each_sync = function (array, func, callback) {
      async.series((function () {
          var func_arr = [];
          for (var i = 0; i < array.length; i++) {
              var f = function (i) {
                  return function (callback) {
                      func(i, callback);
                      /*
                       setTimeout(function() {
                       func(i, callback);
                       }, 2000);*/
                  }
              };
              func_arr.push(f(i))
          }
          return func_arr;
      })(), function (error, results) {
          if (callback) callback(null, "complete");
      });
  };
  
  /*定位*/
  /*
   11      12      1
   --------------------
   10 |  -11   -12   -1  | 2
   |                  |
   9 |  -9    0     -3  | 3
   |                  |
   8 |  -7    -6    -5  | 4
   --------------------
   7        6       5
  
   将菜单定位于自己的周围：
   $(this).xn_position($('#menuid'), 6);
  
   */
  /*将菜单定位于自己的周围*/
  $.fn.xn_position = function (jfloat, pos, offset) {
      var jthis = $(this);
      var jparent = jthis.offsetParent();
      var pos = pos || 0;
      var offset = offset || {left: 0, top: 0};
      offset.left = offset.left || 0;
      offset.top = offset.top || 0;
  
      /*如果 menu 藏的特别深，把它移动出来。*/
      if (jfloat.offsetParent().get(0) != jthis.offsetParent().get(0)) {
          jfloat.appendTo(jthis.offsetParent());
      }
  
      /*设置菜单为绝对定位*/
      jfloat.css('position', 'absolute').css('z-index', jthis.css('z-index') + 1);
  
      var p = jthis.position();
      p.w = jthis.outerWidth();
      p.h = jthis.outerHeight();
      var m = {left: 0, top: 0};
      m.w = jfloat.outerWidth();
      m.h = jfloat.outerHeight();
      p.margin = {
          left: xn.floatval(jthis.css('margin-left')),
          top: xn.floatval(jthis.css('margin-top')),
          right: xn.floatval(jthis.css('margin-right')),
          bottom: xn.floatval(jthis.css('margin-bottom')),
      };
      p.border = {
          left: xn.floatval(jthis.css('border-left-width')),
          top: xn.floatval(jthis.css('border-top-width')),
          right: xn.floatval(jthis.css('border-right-width')),
          bottom: xn.floatval(jthis.css('border-bottom-width')),
      };
      if (pos == 12) {
          m.left = p.left + ((p.w - m.w) / 2);
          m.top = p.top - m.h;
      } else if (pos == 1) {
          m.left = p.left + (p.w - m.w);
          m.top = p.top - m.h;
      } else if (pos == 11) {
          m.left = p.left;
          m.top = p.top - m.h;
      } else if (pos == 2) {
          m.left = p.left + p.w;
          m.top = p.top;
      } else if (pos == 3) {
          m.left = p.left + p.w;
          m.top = p.top + ((p.h - m.h) / 2);
      } else if (pos == 4) {
          m.left = p.left + p.w;
          m.top = p.top + (p.h - m.h);
      } else if (pos == 5) {
          m.left = p.left + (p.w - m.w);
          m.top = p.top + p.h;
      } else if (pos == 6) {
          m.left = p.left + ((p.w - m.w) / 2);
          m.top = p.top + p.h;
      } else if (pos == 7) {
          m.left = p.left;
          m.top = p.top + p.h;
      } else if (pos == 8) {
          m.left = p.left - m.w;
          m.top = p.top + (p.h - m.h);
      } else if (pos == 9) {
          m.left = p.left - m.w;
          m.top = p.top + ((p.h - m.h) / 2);
      } else if (pos == 10) {
          m.left = p.left - m.w;
          m.top = p.top;
      } else if (pos == -12) {
          m.left = p.left + ((p.w - m.w) / 2);
          m.top = p.top;
      } else if (pos == -1) {
          m.left = p.left + (p.w - m.w);
          m.top = p.top;
      } else if (pos == -3) {
          m.left = p.left + p.w - m.w;
          m.top = p.top + ((p.h - m.h) / 2);
      } else if (pos == -5) {
          m.left = p.left + (p.w - m.w);
          m.top = p.top + p.h - m.h;
      } else if (pos == -6) {
          m.left = p.left + ((p.w - m.w) / 2);
          m.top = p.top + p.h - m.h;
      } else if (pos == -7) {
          m.left = p.left;
          m.top = p.top + p.h - m.h;
      } else if (pos == -9) {
          m.left = p.left;
          m.top = p.top + ((p.h - m.h) / 2);
      } else if (pos == -11) {
          m.left = p.left;
          m.top = p.top - m.h + m.h;
      } else if (pos == 0) {
          m.left = p.left + ((p.w - m.w) / 2);
          m.top = p.top + ((p.h - m.h) / 2);
      }
      jfloat.css({left: m.left + offset.left, top: m.top + offset.top});
  };
  
  /*菜单定位*/
  /*
   11        12     1
   --------------------
   10 |                  | 2
   |                  |
   9 |        0         | 3
   |                  |
   8 |                  | 4
   --------------------
   7        6       5
  
   弹出菜单：
   $(this).xn_menu($('#menuid'), 6);
   */
  $.fn.xn_menu = function (jmenu, pos, option) {
      /*生成一个箭头放到菜单的周围*/
      var jthis = $(this);
      var pos = pos || 6;
      var offset = {};
      var option = option || {hidearrow: 0};
      var jparent = jmenu.offsetParent();
      if (!jmenu.jarrow && !option.hidearrow) jmenu.jarrow = $('<div class="arrow arrow-up" style="display: none;"><div class="arrow-box"></div></div>').insertAfter(jthis);
      if (!option.hidearrow) {
          if (pos == 2 || pos == 3 || pos == 4) {
              jmenu.jarrow.addClass('arrow-left');
              offset.left = 7;
          } else if (pos == 5 || pos == 6 || pos == 7) {
              jmenu.jarrow.addClass('arrow-up');
              offset.top = 7;
          } else if (pos == 8 || pos == 9 || pos == 10) {
              jmenu.jarrow.addClass('arrow-right');
              offset.left = -7;
          } else if (pos == 11 || pos == 12 || pos == 1) {
              jmenu.jarrow.addClass('arrow-down');
              offset.top = -7;
          }
      }
      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};
      var arr_offset_map = {
          2: {left: -1, top: 10},
          3: {left: -1, top: 0},
          4: {left: -1, top: -10},
          5: {left: -10, top: -1},
          6: {left: 0, top: -1},
          7: {left: 10, top: -1},
          8: {left: 1, top: -10},
          9: {left: 1, top: 0},
          10: {left: 1, top: 10},
          11: {left: 10, top: 1},
          12: {left: 0, top: 1},
          1: {left: -10, top: 1},
      };
      jthis.xn_position(jmenu, pos, offset);
      jmenu.toggle();
      var mpos = arr_pos_map[pos];
      if (!option.hidearrow) jmenu.xn_position(jmenu.jarrow, mpos, arr_offset_map[mpos]);
      if (!option.hidearrow) jmenu.jarrow.toggle();
      var menu_hide = function (e) {
          if (jmenu.is(":hidden")) return;
          jmenu.toggle();
          if (!option.hidearrow) jmenu.jarrow.hide();
          $('body').off('click', menu_hide);
      };
  
      $('body').off('click', menu_hide).on('click', menu_hide);
  };
  
  
  $.fn.xn_dropdown = function () {
      return this.each(function () {
          var jthis = $(this);
          var jtoggler = jthis.find('.dropdown-toggle');
          var jdropmenu = jthis.find('.dropdown-menu');
          var pos = jthis.data('pos') || 5;
          var hidearrow = !!jthis.data('hidearrow');
          jtoggler.on('click', function () {
              jtoggler.xn_menu(jdropmenu, pos, {hidearrow: hidearrow});
              return false;
          });
      });
  };
  
  $.fn.xn_toggle = function () {
      return this.each(function () {
          var jthis = $(this);
          var jtarget = $(jthis.data('target'));
          var target_hide = function (e) {
              if (jtarget.is(":hidden")) return;
              jtarget.slideToggle('fast');
              $('body').off('click', target_hide);
          };
          jthis.on('click', function () {
              jtarget.slideToggle('fast');
              $('body').off('click', target_hide).on('click', target_hide);
              return false;
          });
      });
  };
  
  $('.xn-dropdown').xn_dropdown();
  $('.xn-toggle').xn_toggle();
  
  console.log('xiuno.js loaded');