text: /**
   * 工具函数包
   * @file
   * @module UE.utils
   * @since 1.2.6.1
   */
  
  /**
   * UEditor封装使用的静态工具函数
   * @module UE.utils
   * @unfile
   */
  
  var utils = UE.utils = {
  
      /**
       * 用给定的迭代器遍历对象
       * @method each
       * @param { Object } obj 需要遍历的对象
       * @param { Function } iterator 迭代器， 该方法接受两个参数， 第一个参数是当前所处理的value， 第二个参数是当前遍历对象的key
       * @example
       * ```javascript
       * var demoObj = {
       *     key1: 1,
       *     key2: 2
       * };
       *
       * //output: key1: 1, key2: 2
       * UE.utils.each( demoObj, funciton ( value, key ) {
       *
       *     console.log( key + ":" + value );
       *
       * } );
       * ```
       */
  
      /**
       * 用给定的迭代器遍历数组或类数组对象
       * @method each
       * @param { Array } array 需要遍历的数组或者类数组
       * @param { Function } iterator 迭代器， 该方法接受两个参数， 第一个参数是当前所处理的value， 第二个参数是当前遍历对象的key
       * @example
       * ```javascript
       * var divs = document.getElmentByTagNames( "div" );
       *
       * //output: 0: DIV, 1: DIV ...
       * UE.utils.each( divs, funciton ( value, key ) {
       *
       *     console.log( key + ":" + value.tagName );
       *
       * } );
       * ```
       */
      each : function(obj, iterator, context) {
          if (obj == null) return;
          if (obj.length === +obj.length) {
              for (var i = 0, l = obj.length; i < l; i++) {
                  if(iterator.call(context, obj[i], i, obj) === false)
                      return false;
              }
          } else {
              for (var key in obj) {
                  if (obj.hasOwnProperty(key)) {
                      if(iterator.call(context, obj[key], key, obj) === false)
                          return false;
                  }
              }
          }
      },
  
      /**
       * 以给定对象作为原型创建一个新对象
       * @method makeInstance
       * @param { Object } protoObject 该对象将作为新创建对象的原型
       * @return { Object } 新的对象， 该对象的原型是给定的protoObject对象
       * @example
       * ```javascript
       *
       * var protoObject = { sayHello: function () { console.log('Hello UEditor!'); } };
       *
       * var newObject = UE.utils.makeInstance( protoObject );
       * //output: Hello UEditor!
       * newObject.sayHello();
       * ```
       */
      makeInstance:function (obj) {
          var noop = new Function();
          noop.prototype = obj;
          obj = new noop;
          noop.prototype = null;
          return obj;
      },
  
      /**
       * 将source对象中的属性扩展到target对象上
       * @method extend
       * @remind 该方法将强制把source对象上的属性复制到target对象上
       * @see UE.utils.extend(Object,Object,Boolean)
       * @param { Object } target 目标对象， 新的属性将附加到该对象上
       * @param { Object } source 源对象， 该对象的属性会被附加到target对象上
       * @return { Object } 返回target对象
       * @example
       * ```javascript
       *
       * var target = { name: 'target', sex: 1 },
       *      source = { name: 'source', age: 17 };
       *
       * UE.utils.extend( target, source );
       *
       * //output: { name: 'source', sex: 1, age: 17 }
       * console.log( target );
       *
       * ```
       */
  
      /**
       * 将source对象中的属性扩展到target对象上， 根据指定的isKeepTarget值决定是否保留目标对象中与
       * 源对象属性名相同的属性值。
       * @method extend
       * @param { Object } target 目标对象， 新的属性将附加到该对象上
       * @param { Object } source 源对象， 该对象的属性会被附加到target对象上
       * @param { Boolean } isKeepTarget 是否保留目标对象中与源对象中属性名相同的属性
       * @return { Object } 返回target对象
       * @example
       * ```javascript
       *
       * var target = { name: 'target', sex: 1 },
       *      source = { name: 'source', age: 17 };
       *
       * UE.utils.extend( target, source, true );
       *
       * //output: { name: 'target', sex: 1, age: 17 }
       * console.log( target );
       *
       * ```
       */
      extend:function (t, s, b) {
          if (s) {
              for (var k in s) {
                  if (!b || !t.hasOwnProperty(k)) {
                      t[k] = s[k];
                  }
              }
          }
          return t;
      },
  
      /**
       * 将给定的多个对象的属性复制到目标对象target上
       * @method extend2
       * @remind 该方法将强制把源对象上的属性复制到target对象上
       * @remind 该方法支持两个及以上的参数， 从第二个参数开始， 其属性都会被复制到第一个参数上。 如果遇到同名的属性，
       *          将会覆盖掉之前的值。
       * @param { Object } target 目标对象， 新的属性将附加到该对象上
       * @param { Object... } source 源对象， 支持多个对象， 该对象的属性会被附加到target对象上
       * @return { Object } 返回target对象
       * @example
       * ```javascript
       *
       * var target = {},
       *     source1 = { name: 'source', age: 17 },
       *     source2 = { title: 'dev' };
       *
       * UE.utils.extend2( target, source1, source2 );
       *
       * //output: { name: 'source', age: 17, title: 'dev' }
       * console.log( target );
       *
       * ```
       */
      extend2:function (t) {
          var a = arguments;
          for (var i = 1; i < a.length; i++) {
              var x = a[i];
              for (var k in x) {
                  if (!t.hasOwnProperty(k)) {
                      t[k] = x[k];
                  }
              }
          }
          return t;
      },
  
      /**
       * 模拟继承机制， 使得subClass继承自superClass
       * @method inherits
       * @param { Object } subClass 子类对象
       * @param { Object } superClass 超类对象
       * @warning 该方法只能让subClass继承超类的原型， subClass对象自身的属性和方法不会被继承
       * @return { Object } 继承superClass后的子类对象
       * @example
       * ```javascript
       * function SuperClass(){
       *     this.name = "小李";
       * }
       *
       * SuperClass.prototype = {
       *     hello:function(str){
       *         console.log(this.name + str);
       *     }
       * }
       *
       * function SubClass(){
       *     this.name = "小张";
       * }
       *
       * UE.utils.inherits(SubClass,SuperClass);
       *
       * var sub = new SubClass();
       * //output: '小张早上好!
       * sub.hello("早上好!");
       * ```
       */
      inherits:function (subClass, superClass) {
          var oldP = subClass.prototype,
              newP = utils.makeInstance(superClass.prototype);
          utils.extend(newP, oldP, true);
          subClass.prototype = newP;
          return (newP.constructor = subClass);
      },
  
      /**
       * 用指定的context对象作为函数fn的上下文
       * @method bind
       * @param { Function } fn 需要绑定上下文的函数对象
       * @param { Object } content 函数fn新的上下文对象
       * @return { Function } 一个新的函数， 该函数作为原始函数fn的代理， 将完成fn的上下文调换工作。
       * @example
       * ```javascript
       *
       * var name = 'window',
       *     newTest = null;
       *
       * function test () {
       *     console.log( this.name );
       * }
       *
       * newTest = UE.utils.bind( test, { name: 'object' } );
       *
       * //output: object
       * newTest();
       *
       * //output: window
       * test();
       *
       * ```
       */
      bind:function (fn, context) {
          return function () {
              return fn.apply(context, arguments);
          };
      },
  
      /**
       * 创建延迟指定时间后执行的函数fn
       * @method defer
       * @param { Function } fn 需要延迟执行的函数对象
       * @param { int } delay 延迟的时间， 单位是毫秒
       * @warning 该方法的时间控制是不精确的，仅仅只能保证函数的执行是在给定的时间之后，
       *           而不能保证刚好到达延迟时间时执行。
       * @return { Function } 目标函数fn的代理函数， 只有执行该函数才能起到延时效果
       * @example
       * ```javascript
       * var start = 0;
       *
       * function test(){
       *     console.log( new Date() - start );
       * }
       *
       * var testDefer = UE.utils.defer( test, 1000 );
       * //
       * start = new Date();
       * //output: (大约在1000毫秒之后输出) 1000
       * testDefer();
       * ```
       */
  
      /**
       * 创建延迟指定时间后执行的函数fn, 如果在延迟时间内再次执行该方法， 将会根据指定的exclusion的值，
       * 决定是否取消前一次函数的执行， 如果exclusion的值为true， 则取消执行，反之，将继续执行前一个方法。
       * @method defer
       * @param { Function } fn 需要延迟执行的函数对象
       * @param { int } delay 延迟的时间， 单位是毫秒
       * @param { Boolean } exclusion 如果在延迟时间内再次执行该函数，该值将决定是否取消执行前一次函数的执行，
       *                     值为true表示取消执行， 反之则将在执行前一次函数之后才执行本次函数调用。
       * @warning 该方法的时间控制是不精确的，仅仅只能保证函数的执行是在给定的时间之后，
       *           而不能保证刚好到达延迟时间时执行。
       * @return { Function } 目标函数fn的代理函数， 只有执行该函数才能起到延时效果
       * @example
       * ```javascript
       *
       * function test(){
       *     console.log(1);
       * }
       *
       * var testDefer = UE.utils.defer( test, 1000, true );
       *
       * //output: (两次调用仅有一次输出) 1
       * testDefer();
       * testDefer();
       * ```
       */
      defer:function (fn, delay, exclusion) {
          var timerID;
          return function () {
              if (exclusion) {
                  clearTimeout(timerID);
              }
              timerID = setTimeout(fn, delay);
          };
      },
  
      /**
       * 获取元素item在数组array中首次出现的位置, 如果未找到item， 则返回-1
       * @method indexOf
       * @remind 该方法的匹配过程使用的是恒等“===”
       * @param { Array } array 需要查找的数组对象
       * @param { * } item 需要在目标数组中查找的值
       * @return { int } 返回item在目标数组array中首次出现的位置， 如果在数组中未找到item， 则返回-1
       * @example
       * ```javascript
       * var item = 1,
       *     arr = [ 3, 4, 6, 8, 1, 1, 2 ];
       *
       * //output: 4
       * console.log( UE.utils.indexOf( arr, item ) );
       * ```
       */
  
      /**
       * 获取元素item数组array中首次出现的位置, 如果未找到item， 则返回-1。通过start的值可以指定搜索的起始位置。
       * @method indexOf
       * @remind 该方法的匹配过程使用的是恒等“===”
       * @param { Array } array 需要查找的数组对象
       * @param { * } item 需要在目标数组中查找的值
       * @param { int } start 搜索的起始位置
       * @return { int } 返回item在目标数组array中的start位置之后首次出现的位置， 如果在数组中未找到item， 则返回-1
       * @example
       * ```javascript
       * var item = 1,
       *     arr = [ 3, 4, 6, 8, 1, 2, 8, 3, 2, 1, 1, 4 ];
       *
       * //output: 9
       * console.log( UE.utils.indexOf( arr, item, 5 ) );
       * ```
       */
      indexOf:function (array, item, start) {
          var index = -1;
          start = this.isNumber(start) ? start : 0;
          this.each(array, function (v, i) {
              if (i >= start && v === item) {
                  index = i;
                  return false;
              }
          });
          return index;
      },
  
      /**
       * 移除数组array中所有的元素item
       * @method removeItem
       * @param { Array } array 要移除元素的目标数组
       * @param { * } item 将要被移除的元素
       * @remind 该方法的匹配过程使用的是恒等“===”
       * @example
       * ```javascript
       * var arr = [ 4, 5, 7, 1, 3, 4, 6 ];
       *
       * UE.utils.removeItem( arr, 4 );
       * //output: [ 5, 7, 1, 3, 6 ]
       * console.log( arr );
       *
       * ```
       */
      removeItem:function (array, item) {
          for (var i = 0, l = array.length; i < l; i++) {
              if (array[i] === item) {
                  array.splice(i, 1);
                  i--;
              }
          }
      },
  
      /**
       * 删除字符串str的首尾空格
       * @method trim
       * @param { String } str 需要删除首尾空格的字符串
       * @return { String } 删除了首尾的空格后的字符串
       * @example
       * ```javascript
       *
       * var str = " UEdtior ";
       *
       * //output: 9
       * console.log( str.length );
       *
       * //output: 7
       * console.log( UE.utils.trim( " UEdtior " ).length );
       *
       * //output: 9
       * console.log( str.length );
       *
       *  ```
       */
      trim:function (str) {
          return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
      },
  
      /**
       * 将字符串str以','分隔成数组后，将该数组转换成哈希对象， 其生成的hash对象的key为数组中的元素， value为1
       * @method listToMap
       * @warning 该方法在生成的hash对象中，会为每一个key同时生成一个另一个全大写的key。
       * @param { String } str 该字符串将被以','分割为数组， 然后进行转化
       * @return { Object } 转化之后的hash对象
       * @example
       * ```javascript
       *
       * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
       * console.log( UE.utils.listToMap( 'UEdtior,Hello' ) );
       *
       * ```
       */
  
      /**
       * 将字符串数组转换成哈希对象， 其生成的hash对象的key为数组中的元素， value为1
       * @method listToMap
       * @warning 该方法在生成的hash对象中，会为每一个key同时生成一个另一个全大写的key。
       * @param { Array } arr 字符串数组
       * @return { Object } 转化之后的hash对象
       * @example
       * ```javascript
       *
       * //output: Object {UEdtior: 1, UEDTIOR: 1, Hello: 1, HELLO: 1}
       * console.log( UE.utils.listToMap( [ 'UEdtior', 'Hello' ] ) );
       *
       * ```
       */
      listToMap:function (list) {
          if (!list)return {};
          list = utils.isArray(list) ? list : list.split(',');
          for (var i = 0, ci, obj = {}; ci = list[i++];) {
              obj[ci.toUpperCase()] = obj[ci] = 1;
          }
          return obj;
      },
  
      /**
       * 将str中的html符号转义,将转义“'，&，<，"，>”五个字符
       * @method unhtml
       * @param { String } str 需要转义的字符串
       * @return { String } 转义后的字符串
       * @example
       * ```javascript
       * var html = '<body>&</body>';
       *
       * //output: &lt;body&gt;&amp;&lt;/body&gt;
       * console.log( UE.utils.unhtml( html ) );
       *
       * ```
       */
      unhtml:function (str, reg) {
          return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp|#\d+);)?/g, function (a, b) {
              if (b) {
                  return a;
              } else {
                  return {
                      '<':'&lt;',
                      '&':'&amp;',
                      '"':'&quot;',
                      '>':'&gt;',
                      "'":'&#39;'
                  }[a]
              }
  
          }) : '';
      },
  
      /**
       * 将str中的转义字符还原成html字符
       * @see UE.utils.unhtml(String);
       * @method html
       * @param { String } str 需要逆转义的字符串
       * @return { String } 逆转义后的字符串
       * @example
       * ```javascript
       *
       * var str = '&lt;body&gt;&amp;&lt;/body&gt;';
       *
       * //output: <body>&</body>
       * console.log( UE.utils.html( str ) );
       *
       * ```
       */
      html:function (str) {
          return str ? str.replace(/&((g|l|quo)t|amp|#39|nbsp);/g, function (m) {
              return {
                  '&lt;':'<',
                  '&amp;':'&',
                  '&quot;':'"',
                  '&gt;':'>',
                  '&#39;':"'",
                  '&nbsp;':' '
              }[m]
          }) : '';
      },
  
      /**
       * 将css样式转换为驼峰的形式
       * @method cssStyleToDomStyle
       * @param { String } cssName 需要转换的css样式名
       * @return { String } 转换成驼峰形式后的css样式名
       * @example
       * ```javascript
       *
       * var str = 'border-top';
       *
       * //output: borderTop
       * console.log( UE.utils.cssStyleToDomStyle( str ) );
       *
       * ```
       */
      cssStyleToDomStyle:function () {
          var test = document.createElement('div').style,
              cache = {
                  'float':test.cssFloat != undefined ? 'cssFloat' : test.styleFloat != undefined ? 'styleFloat' : 'float'
              };
  
          return function (cssName) {
              return cache[cssName] || (cache[cssName] = cssName.toLowerCase().replace(/-./g, function (match) {
                  return match.charAt(1).toUpperCase();
              }));
          };
      }(),
  
      /**
       * 动态加载文件到doc中
       * @method loadFile
       * @param { DomDocument } document 需要加载资源文件的文档对象
       * @param { Object } options 加载资源文件的属性集合， 取值请参考代码示例
       * @example
       * ```javascript
       *
       * UE.utils.loadFile( document, {
       *     src:"test.js",
       *     tag:"script",
       *     type:"text/javascript",
       *     defer:"defer"
       * } );
       *
       * ```
       */
  
      /**
       * 动态加载文件到doc中，加载成功后执行的回调函数fn
       * @method loadFile
       * @param { DomDocument } document 需要加载资源文件的文档对象
       * @param { Object } options 加载资源文件的属性集合， 该集合支持的值是script标签和style标签支持的所有属性。
       * @param { Function } fn 资源文件加载成功之后执行的回调
       * @warning 对于在同一个文档中多次加载同一URL的文件， 该方法会在第一次加载之后缓存该请求，
       *           在此之后的所有同一URL的请求， 将会直接触发回调。
       * @example
       * ```javascript
       *
       * UE.utils.loadFile( document, {
       *     src:"test.js",
       *     tag:"script",
       *     type:"text/javascript",
       *     defer:"defer"
       * }, function () {
       *     console.log('加载成功');
       * } );
       *
       * ```
       */
      loadFile:function () {
          var tmpList = [];
  
          function getItem(doc, obj) {
              try {
                  for (var i = 0, ci; ci = tmpList[i++];) {
                      if (ci.doc === doc && ci.url == (obj.src || obj.href)) {
                          return ci;
                      }
                  }
              } catch (e) {
                  return null;
              }
  
          }
  
          return function (doc, obj, fn) {
              var item = getItem(doc, obj);
              if (item) {
                  if (item.ready) {
                      fn && fn();
                  } else {
                      item.funs.push(fn)
                  }
                  return;
              }
              tmpList.push({
                  doc:doc,
                  url:obj.src || obj.href,
                  funs:[fn]
              });
              if (!doc.body) {
                  var html = [];
                  for (var p in obj) {
                      if (p == 'tag')continue;
                      html.push(p + '="' + obj[p] + '"')
                  }
                  doc.write('<' + obj.tag + ' ' + html.join(' ') + ' ></' + obj.tag + '>');
                  return;
              }
              if (obj.id && doc.getElementById(obj.id)) {
                  return;
              }
              var element = doc.createElement(obj.tag);
              delete obj.tag;
              for (var p in obj) {
                  element.setAttribute(p, obj[p]);
              }
              element.onload = element.onreadystatechange = function () {
                  if (!this.readyState || /loaded|complete/.test(this.readyState)) {
                      item = getItem(doc, obj);
                      if (item.funs.length > 0) {
                          item.ready = 1;
                          for (var fi; fi = item.funs.pop();) {
                              fi();
                          }
                      }
                      element.onload = element.onreadystatechange = null;
                  }
              };
              element.onerror = function () {
                  throw Error('The load ' + (obj.href || obj.src) + ' fails,check the url settings of file ueditor.config.js ')
              };
              doc.getElementsByTagName("head")[0].appendChild(element);
          }
      }(),
  
      /**
       * 判断obj对象是否为空
       * @method isEmptyObject
       * @param { * } obj 需要判断的对象
       * @remind 如果判断的对象是NULL， 将直接返回true， 如果是数组且为空， 返回true， 如果是字符串， 且字符串为空，
       *          返回true， 如果是普通对象， 且该对象没有任何实例属性， 返回true
       * @return { Boolean } 对象是否为空
       * @example
       * ```javascript
       *
       * //output: true
       * console.log( UE.utils.isEmptyObject( {} ) );
       *
       * //output: true
       * console.log( UE.utils.isEmptyObject( [] ) );
       *
       * //output: true
       * console.log( UE.utils.isEmptyObject( "" ) );
       *
       * //output: false
       * console.log( UE.utils.isEmptyObject( { key: 1 } ) );
       *
       * //output: false
       * console.log( UE.utils.isEmptyObject( [1] ) );
       *
       * //output: false
       * console.log( UE.utils.isEmptyObject( "1" ) );
       *
       * ```
       */
      isEmptyObject:function (obj) {
          if (obj == null) return true;
          if (this.isArray(obj) || this.isString(obj)) return obj.length === 0;
          for (var key in obj) if (obj.hasOwnProperty(key)) return false;
          return true;
      },
  
      /**
       * 把rgb格式的颜色值转换成16进制格式
       * @method fixColor
       * @param { String } rgb格式的颜色值
       * @param { String }
       * @example
       * rgb(255,255,255)  => "#ffffff"
       */
      fixColor:function (name, value) {
          if (/color/i.test(name) && /rgba?/.test(value)) {
              var array = value.split(",");
              if (array.length > 3)
                  return "";
              value = "#";
              for (var i = 0, color; color = array[i++];) {
                  color = parseInt(color.replace(/[^\d]/gi, ''), 10).toString(16);
                  value += color.length == 1 ? "0" + color : color;
              }
              value = value.toUpperCase();
          }
          return  value;
      },
      /**
       * 只针对border,padding,margin做了处理，因为性能问题
       * @public
       * @function
       * @param {String}    val style字符串
       */
      optCss:function (val) {
          var padding, margin, border;
          val = val.replace(/(padding|margin|border)\-([^:]+):([^;]+);?/gi, function (str, key, name, val) {
              if (val.split(' ').length == 1) {
                  switch (key) {
                      case 'padding':
                          !padding && (padding = {});
                          padding[name] = val;
                          return '';
                      case 'margin':
                          !margin && (margin = {});
                          margin[name] = val;
                          return '';
                      case 'border':
                          return val == 'initial' ? '' : str;
                  }
              }
              return str;
          });
  
          function opt(obj, name) {
              if (!obj) {
                  return '';
              }
              var t = obj.top , b = obj.bottom, l = obj.left, r = obj.right, val = '';
              if (!t || !l || !b || !r) {
                  for (var p in obj) {
                      val += ';' + name + '-' + p + ':' + obj[p] + ';';
                  }
              } else {
                  val += ';' + name + ':' +
                      (t == b && b == l && l == r ? t :
                          t == b && l == r ? (t + ' ' + l) :
                              l == r ? (t + ' ' + l + ' ' + b) : (t + ' ' + r + ' ' + b + ' ' + l)) + ';'
              }
              return val;
          }
  
          val += opt(padding, 'padding') + opt(margin, 'margin');
          return val.replace(/^[ \n\r\t;]*|[ \n\r\t]*$/, '').replace(/;([ \n\r\t]+)|\1;/g, ';')
              .replace(/(&((l|g)t|quot|#39))?;{2,}/g, function (a, b) {
                  return b ? b + ";;" : ';'
              });
      },
  
      /**
       * 克隆对象
       * @method clone
       * @param { Object } source 源对象
       * @return { Object } source的一个副本
       */
  
      /**
       * 深度克隆对象，将source的属性克隆到target对象， 会覆盖target重名的属性。
       * @method clone
       * @param { Object } source 源对象
       * @param { Object } target 目标对象
       * @return { Object } 附加了source对象所有属性的target对象
       */
      clone:function (source, target) {
          var tmp;
          target = target || {};
          for (var i in source) {
              if (source.hasOwnProperty(i)) {
                  tmp = source[i];
                  if (typeof tmp == 'object') {
                      target[i] = utils.isArray(tmp) ? [] : {};
                      utils.clone(source[i], target[i])
                  } else {
                      target[i] = tmp;
                  }
              }
          }
          return target;
      },
  
      /**
       * 把cm／pt为单位的值转换为px为单位的值
       * @method transUnitToPx
       * @param { String } 待转换的带单位的字符串
       * @return { String } 转换为px为计量单位的值的字符串
       * @example
       * ```javascript
       *
       * //output: 500px
       * console.log( UE.utils.transUnitToPx( '20cm' ) );
       *
       * //output: 27px
       * console.log( UE.utils.transUnitToPx( '20pt' ) );
       *
       * ```
       */
      transUnitToPx:function (val) {
          if (!/(pt|cm)/.test(val)) {
              return val
          }
          var unit;
          val.replace(/([\d.]+)(\w+)/, function (str, v, u) {
              val = v;
              unit = u;
          });
          switch (unit) {
              case 'cm':
                  val = parseFloat(val) * 25;
                  break;
              case 'pt':
                  val = Math.round(parseFloat(val) * 96 / 72);
          }
          return val + (val ? 'px' : '');
      },
  
      /**
       * 在dom树ready之后执行给定的回调函数
       * @method domReady
       * @remind 如果在执行该方法的时候， dom树已经ready， 那么回调函数将立刻执行
       * @param { Function } fn dom树ready之后的回调函数
       * @example
       * ```javascript
       *
       * UE.utils.domReady( function () {
       *
       *     console.log('123');
       *
       * } );
       *
       * ```
       */
      domReady:function () {
  
          var fnArr = [];
  
          function doReady(doc) {
              //确保onready只执行一次
              doc.isReady = true;
              for (var ci; ci = fnArr.pop(); ci()) {
              }
          }
  
          return function (onready, win) {
              win = win || window;
              var doc = win.document;
              onready && fnArr.push(onready);
              if (doc.readyState === "complete") {
                  doReady(doc);
              } else {
                  doc.isReady && doReady(doc);
                  if (browser.ie && browser.version != 11) {
                      (function () {
                          if (doc.isReady) return;
                          try {
                              doc.documentElement.doScroll("left");
                          } catch (error) {
                              setTimeout(arguments.callee, 0);
                              return;
                          }
                          doReady(doc);
                      })();
                      win.attachEvent('onload', function () {
                          doReady(doc)
                      });
                  } else {
                      doc.addEventListener("DOMContentLoaded", function () {
                          doc.removeEventListener("DOMContentLoaded", arguments.callee, false);
                          doReady(doc);
                      }, false);
                      win.addEventListener('load', function () {
                          doReady(doc)
                      }, false);
                  }
              }
  
          }
      }(),
  
      /**
       * 动态添加css样式
       * @method cssRule
       * @param { String } 节点名称
       * @grammar UE.utils.cssRule('添加的样式的节点名称',['样式'，'放到哪个document上'])
       * @grammar UE.utils.cssRule('body','body{background:#ccc}') => null  //给body添加背景颜色
       * @grammar UE.utils.cssRule('body') =>样式的字符串  //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空，例如刚才那个背景颜色，将返回 body{background:#ccc}
       * @grammar UE.utils.cssRule('body',document) => 返回指定key的样式，并且指定是哪个document
       * @grammar UE.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
       */
      cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
          var indexList, index;
          if(style === undefined || style && style.nodeType && style.nodeType == 9){
              //获取样式
              doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
              indexList = doc.indexList || (doc.indexList = {});
              index = indexList[key];
              if(index !==  undefined){
                  return doc.styleSheets[index].cssText
              }
              return undefined;
          }
          doc = doc || document;
          indexList = doc.indexList || (doc.indexList = {});
          index = indexList[key];
          //清除样式
          if(style === ''){
              if(index!== undefined){
                  doc.styleSheets[index].cssText = '';
                  delete indexList[key];
                  return true
              }
              return false;
          }
  
          //添加样式
          if(index!== undefined){
              sheetStyle =  doc.styleSheets[index];
          }else{
              sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
              indexList[key] = index;
          }
          sheetStyle.cssText = style;
      }: function (key, style, doc) {
          var head, node;
          if(style === undefined || style && style.nodeType && style.nodeType == 9){
              //获取样式
              doc = style && style.nodeType && style.nodeType == 9 ? style : (doc || document);
              node = doc.getElementById(key);
              return node ? node.innerHTML : undefined;
          }
          doc = doc || document;
          node = doc.getElementById(key);
  
          //清除样式
          if(style === ''){
              if(node){
                  node.parentNode.removeChild(node);
                  return true
              }
              return false;
          }
  
          //添加样式
          if(node){
              node.innerHTML = style;
          }else{
              node = doc.createElement('style');
              node.id = key;
              node.innerHTML = style;
              doc.getElementsByTagName('head')[0].appendChild(node);
          }
      },
      sort:function(array,compareFn){
          compareFn = compareFn || function(item1, item2){ return item1.localeCompare(item2);};
          for(var i= 0,len = array.length; i<len; i++){
              for(var j = i,length = array.length; j<length; j++){
                  if(compareFn(array[i], array[j]) > 0){
                      var t = array[i];
                      array[i] = array[j];
                      array[j] = t;
                  }
              }
          }
          return array;
      },
      serializeParam:function (json) {
          var strArr = [];
          for (var i in json) {
              //忽略默认的几个参数
              if(i=="method" || i=="timeout" || i=="async") continue;
              //传递过来的对象和函数不在提交之列
              if (!((typeof json[i]).toLowerCase() == "function" || (typeof json[i]).toLowerCase() == "object")) {
                  strArr.push( encodeURIComponent(i) + "="+encodeURIComponent(json[i]) );
              } else if (utils.isArray(json[i])) {
                  //支持传数组内容
                  for(var j = 0; j < json[i].length; j++) {
                      strArr.push( encodeURIComponent(i) + "[]="+encodeURIComponent(json[i][j]) );
                  }
              }
          }
          return strArr.join("&");
      },
      formatUrl:function (url) {
          var u = url.replace(/&&/g, '&');
          u = u.replace(/\?&/g, '?');
          u = u.replace(/&$/g, '');
          u = u.replace(/&#/g, '#');
          u = u.replace(/&+/g, '&');
          return u;
      },
      isCrossDomainUrl:function (url) {
          var a = document.createElement('a');
          a.href = url;
          if (browser.ie) {
              a.href = a.href;
          }
          return !(a.protocol == location.protocol && a.hostname == location.hostname &&
          (a.port == location.port || (a.port == '80' && location.port == '') || (a.port == '' && location.port == '80')));
      },
      clearEmptyAttrs : function(obj){
          for(var p in obj){
              if(obj[p] === ''){
                  delete obj[p]
              }
          }
          return obj;
      },
      str2json : function(s){
  
          if (!utils.isString(s)) return null;
          if (window.JSON) {
              return JSON.parse(s);
          } else {
              return (new Function("return " + utils.trim(s || '')))();
          }
  
      },
      json2str : (function(){
  
          if (window.JSON) {
  
              return JSON.stringify;
  
          } else {
  
              var escapeMap = {
                  "\b": '\\b',
                  "\t": '\\t',
                  "\n": '\\n',
                  "\f": '\\f',
                  "\r": '\\r',
                  '"' : '\\"',
                  "\\": '\\\\'
              };
  
              function encodeString(source) {
                  if (/["\\\x00-\x1f]/.test(source)) {
                      source = source.replace(
                          /["\\\x00-\x1f]/g,
                          function (match) {
                              var c = escapeMap[match];
                              if (c) {
                                  return c;
                              }
                              c = match.charCodeAt();
                              return "\\u00"
                              + Math.floor(c / 16).toString(16)
                              + (c % 16).toString(16);
                          });
                  }
                  return '"' + source + '"';
              }
  
              function encodeArray(source) {
                  var result = ["["],
                      l = source.length,
                      preComma, i, item;
  
                  for (i = 0; i < l; i++) {
                      item = source[i];
  
                      switch (typeof item) {
                          case "undefined":
                          case "function":
                          case "unknown":
                              break;
                          default:
                              if(preComma) {
                                  result.push(',');
                              }
                              result.push(utils.json2str(item));
                              preComma = 1;
                      }
                  }
                  result.push("]");
                  return result.join("");
              }
  
              function pad(source) {
                  return source < 10 ? '0' + source : source;
              }
  
              function encodeDate(source){
                  return '"' + source.getFullYear() + "-"
                  + pad(source.getMonth() + 1) + "-"
                  + pad(source.getDate()) + "T"
                  + pad(source.getHours()) + ":"
                  + pad(source.getMinutes()) + ":"
                  + pad(source.getSeconds()) + '"';
              }
  
              return function (value) {
                  switch (typeof value) {
                      case 'undefined':
                          return 'undefined';
  
                      case 'number':
                          return isFinite(value) ? String(value) : "null";
  
                      case 'string':
                          return encodeString(value);
  
                      case 'boolean':
                          return String(value);
  
                      default:
                          if (value === null) {
                              return 'null';
                          } else if (utils.isArray(value)) {
                              return encodeArray(value);
                          } else if (utils.isDate(value)) {
                              return encodeDate(value);
                          } else {
                              var result = ['{'],
                                  encode = utils.json2str,
                                  preComma,
                                  item;
  
                              for (var key in value) {
                                  if (Object.prototype.hasOwnProperty.call(value, key)) {
                                      item = value[key];
                                      switch (typeof item) {
                                          case 'undefined':
                                          case 'unknown':
                                          case 'function':
                                              break;
                                          default:
                                              if (preComma) {
                                                  result.push(',');
                                              }
                                              preComma = 1;
                                              result.push(encode(key) + ':' + encode(item));
                                      }
                                  }
                              }
                              result.push('}');
                              return result.join('');
                          }
                  }
              };
          }
  
      })()
  
  };
  /**
   * 判断给定的对象是否是字符串
   * @method isString
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是字符串
   */
  
  /**
   * 判断给定的对象是否是数组
   * @method isArray
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是数组
   */
  
  /**
   * 判断给定的对象是否是一个Function
   * @method isFunction
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是Function
   */
  
  /**
   * 判断给定的对象是否是Number
   * @method isNumber
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是Number
   */
  
  /**
   * 判断给定的对象是否是一个正则表达式
   * @method isRegExp
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是正则表达式
   */
  
  /**
   * 判断给定的对象是否是一个普通对象
   * @method isObject
   * @param { * } object 需要判断的对象
   * @return { Boolean } 给定的对象是否是普通对象
   */
  utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object', 'Date'], function (v) {
      UE.utils['is' + v] = function (obj) {
          return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
      }
  });