text: /*!
   * UEditor Mini版本
   * version: 1.2.2
   * build: Mon Jul 25 2016 21:30:23 GMT+0800 (中国标准时间)
   */
  
  (function($){
  
  UMEDITOR_CONFIG = window.UMEDITOR_CONFIG || {};
  
  window.UM = {
      plugins : {},
  
      commands : {},
  
      I18N : {},
  
      version : "1.2.2"
  };
  
  var dom = UM.dom = {};
  /**
   * 浏览器判断模块
   * @file
   * @module UE.browser
   * @since 1.2.6.1
   */
  
  /**
   * 提供浏览器检测的模块
   * @unfile
   * @module UE.browser
   */
  var browser = UM.browser = function(){
      var agent = navigator.userAgent.toLowerCase(),
          opera = window.opera,
          browser = {
              /**
               * @property {boolean} ie 检测当前浏览器是否为IE
               * @example
               * ```javascript
               * if ( UE.browser.ie ) {
           *     console.log( '当前浏览器是IE' );
           * }
               * ```
               */
              ie		:  /(msie\s|trident.*rv:)([\w.]+)/.test(agent),
  
              /**
               * @property {boolean} opera 检测当前浏览器是否为Opera
               * @example
               * ```javascript
               * if ( UE.browser.opera ) {
           *     console.log( '当前浏览器是Opera' );
           * }
               * ```
               */
              opera	: ( !!opera && opera.version ),
  
              /**
               * @property {boolean} webkit 检测当前浏览器是否是webkit内核的浏览器
               * @example
               * ```javascript
               * if ( UE.browser.webkit ) {
           *     console.log( '当前浏览器是webkit内核浏览器' );
           * }
               * ```
               */
              webkit	: ( agent.indexOf( ' applewebkit/' ) > -1 ),
  
              /**
               * @property {boolean} mac 检测当前浏览器是否是运行在mac平台下
               * @example
               * ```javascript
               * if ( UE.browser.mac ) {
           *     console.log( '当前浏览器运行在mac平台下' );
           * }
               * ```
               */
              mac	: ( agent.indexOf( 'macintosh' ) > -1 ),
  
              /**
               * @property {boolean} quirks 检测当前浏览器是否处于“怪异模式”下
               * @example
               * ```javascript
               * if ( UE.browser.quirks ) {
           *     console.log( '当前浏览器运行处于“怪异模式”' );
           * }
               * ```
               */
              quirks : ( document.compatMode == 'BackCompat' )
          };
  
      /**
       * @property {boolean} gecko 检测当前浏览器内核是否是gecko内核
       * @example
       * ```javascript
       * if ( UE.browser.gecko ) {
      *     console.log( '当前浏览器内核是gecko内核' );
      * }
       * ```
       */
      browser.gecko =( navigator.product == 'Gecko' && !browser.webkit && !browser.opera && !browser.ie);
  
      var version = 0;
  
      // Internet Explorer 6.0+
      if ( browser.ie ){
  
  
          var v1 =  agent.match(/(?:msie\s([\w.]+))/);
          var v2 = agent.match(/(?:trident.*rv:([\w.]+))/);
          if(v1 && v2 && v1[1] && v2[1]){
              version = Math.max(v1[1]*1,v2[1]*1);
          }else if(v1 && v1[1]){
              version = v1[1]*1;
          }else if(v2 && v2[1]){
              version = v2[1]*1;
          }else{
              version = 0;
          }
  
          browser.ie11Compat = document.documentMode == 11;
          /**
           * @property { boolean } ie9Compat 检测浏览器模式是否为 IE9 兼容模式
           * @warning 如果浏览器不是IE， 则该值为undefined
           * @example
           * ```javascript
           * if ( UE.browser.ie9Compat ) {
           *     console.log( '当前浏览器运行在IE9兼容模式下' );
           * }
           * ```
           */
          browser.ie9Compat = document.documentMode == 9;
  
          /**
           * @property { boolean } ie8 检测浏览器是否是IE8浏览器
           * @warning 如果浏览器不是IE， 则该值为undefined
           * @example
           * ```javascript
           * if ( UE.browser.ie8 ) {
           *     console.log( '当前浏览器是IE8浏览器' );
           * }
           * ```
           */
          browser.ie8 = !!document.documentMode;
  
          /**
           * @property { boolean } ie8Compat 检测浏览器模式是否为 IE8 兼容模式
           * @warning 如果浏览器不是IE， 则该值为undefined
           * @example
           * ```javascript
           * if ( UE.browser.ie8Compat ) {
           *     console.log( '当前浏览器运行在IE8兼容模式下' );
           * }
           * ```
           */
          browser.ie8Compat = document.documentMode == 8;
  
          /**
           * @property { boolean } ie7Compat 检测浏览器模式是否为 IE7 兼容模式
           * @warning 如果浏览器不是IE， 则该值为undefined
           * @example
           * ```javascript
           * if ( UE.browser.ie7Compat ) {
           *     console.log( '当前浏览器运行在IE7兼容模式下' );
           * }
           * ```
           */
          browser.ie7Compat = ( ( version == 7 && !document.documentMode )
              || document.documentMode == 7 );
  
          /**
           * @property { boolean } ie6Compat 检测浏览器模式是否为 IE6 模式 或者怪异模式
           * @warning 如果浏览器不是IE， 则该值为undefined
           * @example
           * ```javascript
           * if ( UE.browser.ie6Compat ) {
           *     console.log( '当前浏览器运行在IE6模式或者怪异模式下' );
           * }
           * ```
           */
          browser.ie6Compat = ( version < 7 || browser.quirks );
  
          browser.ie9above = version > 8;
  
          browser.ie9below = version < 9;
  
      }
  
      // Gecko.
      if ( browser.gecko ){
          var geckoRelease = agent.match( /rv:([\d\.]+)/ );
          if ( geckoRelease )
          {
              geckoRelease = geckoRelease[1].split( '.' );
              version = geckoRelease[0] * 10000 + ( geckoRelease[1] || 0 ) * 100 + ( geckoRelease[2] || 0 ) * 1;
          }
      }
  
      /**
       * @property { Number } chrome 检测当前浏览器是否为Chrome, 如果是，则返回Chrome的大版本号
       * @warning 如果浏览器不是chrome， 则该值为undefined
       * @example
       * ```javascript
       * if ( UE.browser.chrome ) {
       *     console.log( '当前浏览器是Chrome' );
       * }
       * ```
       */
      if (/chrome\/(\d+\.\d)/i.test(agent)) {
          browser.chrome = + RegExp['\x241'];
      }
  
      /**
       * @property { Number } safari 检测当前浏览器是否为Safari, 如果是，则返回Safari的大版本号
       * @warning 如果浏览器不是safari， 则该值为undefined
       * @example
       * ```javascript
       * if ( UE.browser.safari ) {
       *     console.log( '当前浏览器是Safari' );
       * }
       * ```
       */
      if(/(\d+\.\d)?(?:\.\d)?\s+safari\/?(\d+\.\d+)?/i.test(agent) && !/chrome/i.test(agent)){
          browser.safari = + (RegExp['\x241'] || RegExp['\x242']);
      }
  
  
      // Opera 9.50+
      if ( browser.opera )
          version = parseFloat( opera.version() );
  
      // WebKit 522+ (Safari 3+)
      if ( browser.webkit )
          version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[1] );
  
      /**
       * @property { Number } version 检测当前浏览器版本号
       * @remind
       * <ul>
       *     <li>IE系列返回值为5,6,7,8,9,10等</li>
       *     <li>gecko系列会返回10900，158900等</li>
       *     <li>webkit系列会返回其build号 (如 522等)</li>
       * </ul>
       * @example
       * ```javascript
       * console.log( '当前浏览器版本号是： ' + UE.browser.version );
       * ```
       */
      browser.version = version;
  
      /**
       * @property { boolean } isCompatible 检测当前浏览器是否能够与UEditor良好兼容
       * @example
       * ```javascript
       * if ( UE.browser.isCompatible ) {
       *     console.log( '浏览器与UEditor能够良好兼容' );
       * }
       * ```
       */
      browser.isCompatible =
          !browser.mobile && (
              ( browser.ie && version >= 6 ) ||
                  ( browser.gecko && version >= 10801 ) ||
                  ( browser.opera && version >= 9.5 ) ||
                  ( browser.air && version >= 1 ) ||
                  ( browser.webkit && version >= 522 ) ||
                  false );
      return browser;
  }();
  //快捷方式
  var ie = browser.ie,
      webkit = browser.webkit,
      gecko = browser.gecko,
      opera = browser.opera;
  /**
   * @file
   * @name UM.Utils
   * @short Utils
   * @desc UEditor封装使用的静态工具函数
   * @import editor.js
   */
  var utils = UM.utils = {
      /**
       * 遍历数组，对象，nodeList
       * @name each
       * @grammar UM.utils.each(obj,iterator,[context])
       * @since 1.2.4+
       * @desc
       * * obj 要遍历的对象
       * * iterator 遍历的方法,方法的第一个是遍历的值，第二个是索引，第三个是obj
       * * context  iterator的上下文
       * @example
       * UM.utils.each([1,2],function(v,i){
       *     console.log(v)//值
       *     console.log(i)//索引
       * })
       * UM.utils.each(document.getElementsByTagName('*'),function(n){
       *     console.log(n.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;
                  }
              }
          }
      },
  
      makeInstance:function (obj) {
          var noop = new Function();
          noop.prototype = obj;
          obj = new noop;
          noop.prototype = null;
          return obj;
      },
      /**
       * 将source对象中的属性扩展到target对象上
       * @name extend
       * @grammar UM.utils.extend(target,source)  => Object  //覆盖扩展
       * @grammar UM.utils.extend(target,source,true)  ==> Object  //保留扩展
       */
      extend:function (t, s, b) {
          if (s) {
              for (var k in s) {
                  if (!b || !t.hasOwnProperty(k)) {
                      t[k] = s[k];
                  }
              }
          }
          return t;
      },
      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
       * @name inherits
       * @grammar UM.utils.inherits(subClass,superClass) => subClass
       * @example
       * function SuperClass(){
       *     this.name = "小李";
       * }
       * SuperClass.prototype = {
       *     hello:function(str){
       *         console.log(this.name + str);
       *     }
       * }
       * function SubClass(){
       *     this.name = "小张";
       * }
       * UM.utils.inherits(SubClass,SuperClass);
       * var sub = new SubClass();
       * 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上下文，也就是this
       * @name bind
       * @grammar UM.utils.bind(fn,context)  =>  fn
       */
      bind:function (fn, context) {
          return function () {
              return fn.apply(context, arguments);
          };
      },
  
      /**
       * 创建延迟delay执行的函数fn
       * @name defer
       * @grammar UM.utils.defer(fn,delay)  =>fn   //延迟delay毫秒执行fn，返回fn
       * @grammar UM.utils.defer(fn,delay,exclusion)  =>fn   //延迟delay毫秒执行fn，若exclusion为真，则互斥执行fn
       * @example
       * function test(){
       *     console.log("延迟输出！");
       * }
       * //非互斥延迟执行
       * var testDefer = UM.utils.defer(test,1000);
       * testDefer();   =>  "延迟输出！";
       * testDefer();   =>  "延迟输出！";
       * //互斥延迟执行
       * var testDefer1 = UM.utils.defer(test,1000,true);
       * testDefer1();   =>  //本次不执行
       * testDefer1();   =>  "延迟输出！";
       */
      defer:function (fn, delay, exclusion) {
          var timerID;
          return function () {
              if (exclusion) {
                  clearTimeout(timerID);
              }
              timerID = setTimeout(fn, delay);
          };
      },
  
      /**
       * 查找元素item在数组array中的索引, 若找不到返回-1
       * @name indexOf
       * @grammar UM.utils.indexOf(array,item)  => index|-1  //默认从数组开头部开始搜索
       * @grammar UM.utils.indexOf(array,item,start)  => index|-1  //start指定开始查找的位置
       */
      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
       * @name removeItem
       * @grammar UM.utils.removeItem(array,item)
       */
      removeItem:function (array, item) {
          for (var i = 0, l = array.length; i < l; i++) {
              if (array[i] === item) {
                  array.splice(i, 1);
                  i--;
              }
          }
      },
  
      /**
       * 删除字符串str的首尾空格
       * @name trim
       * @grammar UM.utils.trim(str) => String
       */
      trim:function (str) {
          return str.replace(/(^[ \t\n\r]+)|([ \t\n\r]+$)/g, '');
      },
  
      /**
       * 将字符串list(以','分隔)或者数组list转成哈希对象
       * @name listToMap
       * @grammar UM.utils.listToMap(list)  => Object  //Object形如{test:1,br:1,textarea:1}
       */
      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符号转义,默认将转义''&<">''四个字符，可自定义reg来确定需要转义的字符
       * @name unhtml
       * @grammar UM.utils.unhtml(str);  => String
       * @grammar UM.utils.unhtml(str,reg)  => String
       * @example
       * var html = '<body>You say:"你好！Baidu & UEditor!"</body>';
       * UM.utils.unhtml(html);   ==>  &lt;body&gt;You say:&quot;你好！Baidu &amp; UEditor!&quot;&lt;/body&gt;
       * UM.utils.unhtml(html,/[<>]/g)  ==>  &lt;body&gt;You say:"你好！Baidu & UEditor!"&lt;/body&gt;
       */
      unhtml:function (str, reg) {
          return str ? str.replace(reg || /[&<">'](?:(amp|lt|quot|gt|#39|nbsp);)?/g, function (a, b) {
              if (b) {
                  return a;
              } else {
                  return {
                      '<':'&lt;',
                      '&':'&amp;',
                      '"':'&quot;',
                      '>':'&gt;',
                      "'":'&#39;'
                  }[a]
              }
  
          }) : '';
      },
      /**
       * 将str中的转义字符还原成html字符
       * @name html
       * @grammar UM.utils.html(str)  => String   //详细参见<code><a href = '#unhtml'>unhtml</a></code>
       */
      html:function (str) {
          return str ? str.replace(/&((g|l|quo)t|amp|#39);/g, function (m) {
              return {
                  '&lt;':'<',
                  '&amp;':'&',
                  '&quot;':'"',
                  '&gt;':'>',
                  '&#39;':"'"
              }[m]
          }) : '';
      },
      /**
       * 将css样式转换为驼峰的形式。如font-size => fontSize
       * @name cssStyleToDomStyle
       * @grammar UM.utils.cssStyleToDomStyle(cssName)  => String
       */
      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中，并依据obj来设置属性，加载成功后执行回调函数fn
       * @name loadFile
       * @grammar UM.utils.loadFile(doc,obj)
       * @grammar UM.utils.loadFile(doc,obj,fn)
       * @example
       * //指定加载到当前document中一个script文件，加载成功后执行function
       * 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 umeditor.config.js ')
              };
              doc.getElementsByTagName("head")[0].appendChild(element);
          }
      }(),
      /**
       * 判断obj对象是否为空
       * @name isEmptyObject
       * @grammar UM.utils.isEmptyObject(obj)  => true|false
       * @example
       * UM.utils.isEmptyObject({}) ==>true
       * UM.utils.isEmptyObject([]) ==>true
       * UM.utils.isEmptyObject("") ==>true
       */
      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;
      },
  
      /**
       * 统一将颜色值使用16进制形式表示
       * @name fixColor
       * @grammar UM.utils.fixColor(name,value) => value
       * @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;
      },
  
      /**
       * 深度克隆对象，从source到target
       * @name clone
       * @grammar UM.utils.clone(source) => anthorObj 新的对象是完整的source的副本
       * @grammar UM.utils.clone(source,target) => target包含了source的所有内容，重名会覆盖
       */
      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
       * @name transUnitToPx
       * @grammar UM.utils.transUnitToPx('20pt') => '27px'
       * @grammar UM.utils.transUnitToPx('0pt') => '0'
       */
      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' : '');
      },
      /**
       * 动态添加css样式
       * @name cssRule
       * @grammar UM.utils.cssRule('添加的样式的节点名称',['样式'，'放到哪个document上'])
       * @grammar UM.utils.cssRule('body','body{background:#ccc}') => null  //给body添加背景颜色
       * @grammar UM.utils.cssRule('body') =>样式的字符串  //取得key值为body的样式的内容,如果没有找到key值先关的样式将返回空，例如刚才那个背景颜色，将返回 body{background:#ccc}
       * @grammar UM.utils.cssRule('body','') =>null //清空给定的key值的背景颜色
       */
      cssRule:browser.ie && browser.version != 11 ? function (key, style, doc) {
          var indexList, index;
          doc = doc || document;
          if (doc.indexList) {
              indexList = doc.indexList;
          } else {
              indexList = doc.indexList = {};
          }
          var sheetStyle;
          if (!indexList[key]) {
              if (style === undefined) {
                  return ''
              }
              sheetStyle = doc.createStyleSheet('', index = doc.styleSheets.length);
              indexList[key] = index;
          } else {
              sheetStyle = doc.styleSheets[indexList[key]];
          }
          if (style === undefined) {
              return sheetStyle.cssText
          }
          sheetStyle.cssText = style || ''
      } : function (key, style, doc) {
          doc = doc || document;
          var head = doc.getElementsByTagName('head')[0], node;
          if (!(node = doc.getElementById(key))) {
              if (style === undefined) {
                  return ''
              }
              node = doc.createElement('style');
              node.id = key;
              head.appendChild(node)
          }
          if (style === undefined) {
              return node.innerHTML
          }
          if (style !== '') {
              node.innerHTML = style;
          } else {
              head.removeChild(node)
          }
      }
  
  };
  /**
   * 判断str是否为字符串
   * @name isString
   * @grammar UM.utils.isString(str) => true|false
   */
  /**
   * 判断array是否为数组
   * @name isArray
   * @grammar UM.utils.isArray(obj) => true|false
   */
  /**
   * 判断obj对象是否为方法
   * @name isFunction
   * @grammar UM.utils.isFunction(obj)  => true|false
   */
  /**
   * 判断obj对象是否为数字
   * @name isNumber
   * @grammar UM.utils.isNumber(obj)  => true|false
   */
  utils.each(['String', 'Function', 'Array', 'Number', 'RegExp', 'Object'], function (v) {
      UM.utils['is' + v] = function (obj) {
          return Object.prototype.toString.apply(obj) == '[object ' + v + ']';
      }
  });
  /**
   * @file
   * @name UM.EventBase
   * @short EventBase
   * @import editor.js,core/utils.js
   * @desc UE采用的事件基类，继承此类的对应类将获取addListener,removeListener,fireEvent方法。
   * 在UE中，Editor以及所有ui实例都继承了该类，故可以在对应的ui对象以及editor对象上使用上述方法。
   */
  var EventBase = UM.EventBase = function () {};
  
  EventBase.prototype = {
      /**
       * 注册事件监听器
       * @name addListener
       * @grammar editor.addListener(types,fn)  //types为事件名称，多个可用空格分隔
       * @example
       * editor.addListener('selectionchange',function(){
       *      console.log("选区已经变化！");
       * })
       * editor.addListener('beforegetcontent aftergetcontent',function(type){
       *         if(type == 'beforegetcontent'){
       *             //do something
       *         }else{
       *             //do something
       *         }
       *         console.log(this.getContent) // this是注册的事件的编辑器实例
       * })
       */
      addListener:function (types, listener) {
          types = utils.trim(types).split(' ');
          for (var i = 0, ti; ti = types[i++];) {
              getListener(this, ti, true).push(listener);
          }
      },
      /**
       * 移除事件监听器
       * @name removeListener
       * @grammar editor.removeListener(types,fn)  //types为事件名称，多个可用空格分隔
       * @example
       * //changeCallback为方法体
       * editor.removeListener("selectionchange",changeCallback);
       */
      removeListener:function (types, listener) {
          types = utils.trim(types).split(' ');
          for (var i = 0, ti; ti = types[i++];) {
              utils.removeItem(getListener(this, ti) || [], listener);
          }
      },
      /**
       * 触发事件
       * @name fireEvent
       * @grammar editor.fireEvent(types)  //types为事件名称，多个可用空格分隔
       * @example
       * editor.fireEvent("selectionchange");
       */
      fireEvent:function () {
          var types = arguments[0];
          types = utils.trim(types).split(' ');
          for (var i = 0, ti; ti = types[i++];) {
              var listeners = getListener(this, ti),
                  r, t, k;
              if (listeners) {
                  k = listeners.length;
                  while (k--) {
                      if(!listeners[k])continue;
                      t = listeners[k].apply(this, arguments);
                      if(t === true){
                          return t;
                      }
                      if (t !== undefined) {
                          r = t;
                      }
                  }
              }
              if (t = this['on' + ti.toLowerCase()]) {
                  r = t.apply(this, arguments);
              }
          }
          return r;
      }
  };
  /**
   * 获得对象所拥有监听类型的所有监听器
   * @public
   * @function
   * @param {Object} obj  查询监听器的对象
   * @param {String} type 事件类型
   * @param {Boolean} force  为true且当前所有type类型的侦听器不存在时，创建一个空监听器数组
   * @returns {Array} 监听器数组
   */
  function getListener(obj, type, force) {
      var allListeners;
      type = type.toLowerCase();
      return ( ( allListeners = ( obj.__allListeners || force && ( obj.__allListeners = {} ) ) )
          && ( allListeners[type] || force && ( allListeners[type] = [] ) ) );
  }
  
  
  ///import editor.js
  ///import core/dom/dom.js
  ///import core/utils.js
  /**
   * dtd html语义化的体现类
   * @constructor
   * @namespace dtd
   */
  var dtd = dom.dtd = (function() {
      function _( s ) {
          for (var k in s) {
              s[k.toUpperCase()] = s[k];
          }
          return s;
      }
      var X = utils.extend2;
      var A = _({isindex:1,fieldset:1}),
          B = _({input:1,button:1,select:1,textarea:1,label:1}),
          C = X( _({a:1}), B ),
          D = X( {iframe:1}, C ),
          E = _({hr:1,ul:1,menu:1,div:1,blockquote:1,noscript:1,table:1,center:1,address:1,dir:1,pre:1,h5:1,dl:1,h4:1,noframes:1,h6:1,ol:1,h1:1,h3:1,h2:1}),
          F = _({ins:1,del:1,script:1,style:1}),
          G = X( _({b:1,acronym:1,bdo:1,'var':1,'#':1,abbr:1,code:1,br:1,i:1,cite:1,kbd:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,dfn:1,span:1}), F ),
          H = X( _({sub:1,img:1,embed:1,object:1,sup:1,basefont:1,map:1,applet:1,font:1,big:1,small:1}), G ),
          I = X( _({p:1}), H ),
          J = X( _({iframe:1}), H, B ),
          K = _({img:1,embed:1,noscript:1,br:1,kbd:1,center:1,button:1,basefont:1,h5:1,h4:1,samp:1,h6:1,ol:1,h1:1,h3:1,h2:1,form:1,font:1,'#':1,select:1,menu:1,ins:1,abbr:1,label:1,code:1,table:1,script:1,cite:1,input:1,iframe:1,strong:1,textarea:1,noframes:1,big:1,small:1,span:1,hr:1,sub:1,bdo:1,'var':1,div:1,object:1,sup:1,strike:1,dir:1,map:1,dl:1,applet:1,del:1,isindex:1,fieldset:1,ul:1,b:1,acronym:1,a:1,blockquote:1,i:1,u:1,s:1,tt:1,address:1,q:1,pre:1,p:1,em:1,dfn:1}),
  
          L = X( _({a:0}), J ),//a不能被切开，所以把他
          M = _({tr:1}),
          N = _({'#':1}),
          O = X( _({param:1}), K ),
          P = X( _({form:1}), A, D, E, I ),
          Q = _({li:1,ol:1,ul:1}),
          R = _({style:1,script:1}),
          S = _({base:1,link:1,meta:1,title:1}),
          T = X( S, R ),
          U = _({head:1,body:1}),
          V = _({html:1});
  
      var block = _({address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1,progress:1}),
  
          empty =  _({area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1});
  
      return  _({
  
          // $ 表示自定的属性
  
          // body外的元素列表.
          $nonBodyContent: X( V, U, S ),
  
          //块结构元素列表
          $block : block,
  
          //内联元素列表
          $inline : L,
  
          $inlineWithA : X(_({a:1}),L),
  
          $body : X( _({script:1,style:1}), block ),
  
          $cdata : _({script:1,style:1}),
  
          //自闭和元素
          $empty : empty,
  
          //不是自闭合，但不能让range选中里边
          $nonChild : _({iframe:1,textarea:1}),
          //列表元素列表
          $listItem : _({dd:1,dt:1,li:1}),
  
          //列表根元素列表
          $list: _({ul:1,ol:1,dl:1}),
  
          //不能认为是空的元素
          $isNotEmpty : _({table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1}),
  
          //如果没有子节点就可以删除的元素列表，像span,a
          $removeEmpty : _({a:1,abbr:1,acronym:1,address:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1}),
  
          $removeEmptyBlock : _({'p':1,'div':1}),
  
          //在table元素里的元素列表
          $tableContent : _({caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1,table:1}),
          //不转换的标签
          $notTransContent : _({pre:1,script:1,style:1,textarea:1}),
          html: U,
          head: T,
          style: N,
          script: N,
          body: P,
          base: {},
          link: {},
          meta: {},
          title: N,
          col : {},
          tr : _({td:1,th:1}),
          img : {},
          embed: {},
          colgroup : _({thead:1,col:1,tbody:1,tr:1,tfoot:1}),
          noscript : P,
          td : P,
          br : {},
          th : P,
          center : P,
          kbd : L,
          button : X( I, E ),
          basefont : {},
          h5 : L,
          h4 : L,
          samp : L,
          h6 : L,
          ol : Q,
          h1 : L,
          h3 : L,
          option : N,
          h2 : L,
          form : X( A, D, E, I ),
          select : _({optgroup:1,option:1}),
          font : L,
          ins : L,
          menu : Q,
          abbr : L,
          label : L,
          table : _({thead:1,col:1,tbody:1,tr:1,colgroup:1,caption:1,tfoot:1}),
          code : L,
          tfoot : M,
          cite : L,
          li : P,
          input : {},
          iframe : P,
          strong : L,
          textarea : N,
          noframes : P,
          big : L,
          small : L,
          //trace:
          span :_({'#':1,br:1,b:1,strong:1,u:1,i:1,em:1,sub:1,sup:1,strike:1,span:1}),
          hr : L,
          dt : L,
          sub : L,
          optgroup : _({option:1}),
          param : {},
          bdo : L,
          'var' : L,
          div : P,
          object : O,
          sup : L,
          dd : P,
          strike : L,
          area : {},
          dir : Q,
          map : X( _({area:1,form:1,p:1}), A, F, E ),
          applet : O,
          dl : _({dt:1,dd:1}),
          del : L,
          isindex : {},
          fieldset : X( _({legend:1}), K ),
          thead : M,
          ul : Q,
          acronym : L,
          b : L,
          a : X( _({a:1}), J ),
          blockquote :X(_({td:1,tr:1,tbody:1,li:1}),P),
          caption : L,
          i : L,
          u : L,
          tbody : M,
          s : L,
          address : X( D, I ),
          tt : L,
          legend : L,
          q : L,
          pre : X( G, C ),
          p : X(_({'a':1}),L),
          em :L,
          dfn : L
      });
  })();
  
  /**
   * @file
   * @name UM.dom.domUtils
   * @short DomUtils
   * @import editor.js, core/utils.js,core/browser.js,core/dom/dtd.js
   * @desc UEditor封装的底层dom操作库
   */
  
  function getDomNode(node, start, ltr, startFromChild, fn, guard) {
      var tmpNode = startFromChild && node[start],
          parent;
      !tmpNode && (tmpNode = node[ltr]);
      while (!tmpNode && (parent = (parent || node).parentNode)) {
          if (parent.tagName == 'BODY' || guard && !guard(parent)) {
              return null;
          }
          tmpNode = parent[ltr];
      }
      if (tmpNode && fn && !fn(tmpNode)) {
          return  getDomNode(tmpNode, start, ltr, false, fn);
      }
      return tmpNode;
  }
  var attrFix = ie && browser.version < 9 ? {
          tabindex: "tabIndex",
          readonly: "readOnly",
          "for": "htmlFor",
          "class": "className",
          maxlength: "maxLength",
          cellspacing: "cellSpacing",
          cellpadding: "cellPadding",
          rowspan: "rowSpan",
          colspan: "colSpan",
          usemap: "useMap",
          frameborder: "frameBorder"
      } : {
          tabindex: "tabIndex",
          readonly: "readOnly"
      },
      styleBlock = utils.listToMap([
          '-webkit-box', '-moz-box', 'block' ,
          'list-item' , 'table' , 'table-row-group' ,
          'table-header-group', 'table-footer-group' ,
          'table-row' , 'table-column-group' , 'table-column' ,
          'table-cell' , 'table-caption'
      ]);
  var domUtils = dom.domUtils = {
      //节点常量
      NODE_ELEMENT: 1,
      NODE_DOCUMENT: 9,
      NODE_TEXT: 3,
      NODE_COMMENT: 8,
      NODE_DOCUMENT_FRAGMENT: 11,
  
      //位置关系
      POSITION_IDENTICAL: 0,
      POSITION_DISCONNECTED: 1,
      POSITION_FOLLOWING: 2,
      POSITION_PRECEDING: 4,
      POSITION_IS_CONTAINED: 8,
      POSITION_CONTAINS: 16,
      //ie6使用其他的会有一段空白出现
      fillChar: ie && browser.version == '6' ? '\ufeff' : '\u200B',
      //-------------------------Node部分--------------------------------
      keys: {
          /*Backspace*/ 8: 1, /*Delete*/ 46: 1,
          /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1,
          37: 1, 38: 1, 39: 1, 40: 1,
          13: 1 /*enter*/
      },
      breakParent:function (node, parent) {
          var tmpNode,
              parentClone = node,
              clone = node,
              leftNodes,
              rightNodes;
          do {
              parentClone = parentClone.parentNode;
              if (leftNodes) {
                  tmpNode = parentClone.cloneNode(false);
                  tmpNode.appendChild(leftNodes);
                  leftNodes = tmpNode;
                  tmpNode = parentClone.cloneNode(false);
                  tmpNode.appendChild(rightNodes);
                  rightNodes = tmpNode;
              } else {
                  leftNodes = parentClone.cloneNode(false);
                  rightNodes = leftNodes.cloneNode(false);
              }
              while (tmpNode = clone.previousSibling) {
                  leftNodes.insertBefore(tmpNode, leftNodes.firstChild);
              }
              while (tmpNode = clone.nextSibling) {
                  rightNodes.appendChild(tmpNode);
              }
              clone = parentClone;
          } while (parent !== parentClone);
          tmpNode = parent.parentNode;
          tmpNode.insertBefore(leftNodes, parent);
          tmpNode.insertBefore(rightNodes, parent);
          tmpNode.insertBefore(node, rightNodes);
          domUtils.remove(parent);
          return node;
      },
      trimWhiteTextNode:function (node) {
          function remove(dir) {
              var child;
              while ((child = node[dir]) && child.nodeType == 3 && domUtils.isWhitespace(child)) {
                  node.removeChild(child);
              }
          }
          remove('firstChild');
          remove('lastChild');
      },
      /**
       * 获取节点A相对于节点B的位置关系
       * @name getPosition
       * @grammar UM.dom.domUtils.getPosition(nodeA,nodeB)  =>  Number
       * @example
       *  switch (returnValue) {
       *      case 0: //相等，同一节点
       *      case 1: //无关，节点不相连
       *      case 2: //跟随，即节点A头部位于节点B头部的后面
       *      case 4: //前置，即节点A头部位于节点B头部的前面
       *      case 8: //被包含，即节点A被节点B包含
       *      case 10://组合类型，即节点A满足跟随节点B且被节点B包含。实际上，如果被包含，必定跟随，所以returnValue事实上不会存在8的情况。
       *      case 16://包含，即节点A包含节点B
       *      case 20://组合类型，即节点A满足前置节点A且包含节点B。同样，如果包含，必定前置，所以returnValue事实上也不会存在16的情况
       *  }
       */
      getPosition: function (nodeA, nodeB) {
          // 如果两个节点是同一个节点
          if (nodeA === nodeB) {
              // domUtils.POSITION_IDENTICAL
              return 0;
          }
          var node,
              parentsA = [nodeA],
              parentsB = [nodeB];
          node = nodeA;
          while (node = node.parentNode) {
              // 如果nodeB是nodeA的祖先节点
              if (node === nodeB) {
                  // domUtils.POSITION_IS_CONTAINED + domUtils.POSITION_FOLLOWING
                  return 10;
              }
              parentsA.push(node);
          }
          node = nodeB;
          while (node = node.parentNode) {
              // 如果nodeA是nodeB的祖先节点
              if (node === nodeA) {
                  // domUtils.POSITION_CONTAINS + domUtils.POSITION_PRECEDING
                  return 20;
              }
              parentsB.push(node);
          }
          parentsA.reverse();
          parentsB.reverse();
          if (parentsA[0] !== parentsB[0]) {
              // domUtils.POSITION_DISCONNECTED
              return 1;
          }
          var i = -1;
          while (i++, parentsA[i] === parentsB[i]) {
          }
          nodeA = parentsA[i];
          nodeB = parentsB[i];
          while (nodeA = nodeA.nextSibling) {
              if (nodeA === nodeB) {
                  // domUtils.POSITION_PRECEDING
                  return 4
              }
          }
          // domUtils.POSITION_FOLLOWING
          return  2;
      },
  
      /**
       * 返回节点node在父节点中的索引位置
       * @name getNodeIndex
       * @grammar UM.dom.domUtils.getNodeIndex(node)  => Number  //索引值从0开始
       */
      getNodeIndex: function (node, ignoreTextNode) {
          var preNode = node,
              i = 0;
          while (preNode = preNode.previousSibling) {
              if (ignoreTextNode && preNode.nodeType == 3) {
                  if (preNode.nodeType != preNode.nextSibling.nodeType) {
                      i++;
                  }
                  continue;
              }
              i++;
          }
          return i;
      },
  
      /**
       * 检测节点node是否在节点doc的树上，实质上是检测是否被doc包含
       * @name inDoc
       * @grammar UM.dom.domUtils.inDoc(node,doc)   =>  true|false
       */
      inDoc: function (node, doc) {
          return domUtils.getPosition(node, doc) == 10;
      },
      /**
       * 查找node节点的祖先节点
       * @name findParent
       * @grammar UM.dom.domUtils.findParent(node)  => Element  // 直接返回node节点的父节点
       * @grammar UM.dom.domUtils.findParent(node,filterFn)  => Element  //filterFn为过滤函数，node作为参数，返回true时才会将node作为符合要求的节点返回
       * @grammar UM.dom.domUtils.findParent(node,filterFn,includeSelf)  => Element  //includeSelf指定是否包含自身
       */
      findParent: function (node, filterFn, includeSelf) {
          if (node && !domUtils.isBody(node)) {
              node = includeSelf ? node : node.parentNode;
              while (node) {
                  if (!filterFn || filterFn(node) || domUtils.isBody(node)) {
                      return filterFn && !filterFn(node) && domUtils.isBody(node) ? null : node;
                  }
                  node = node.parentNode;
              }
          }
          return null;
      },
      /**
       * 通过tagName查找node节点的祖先节点
       * @name findParentByTagName
       * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames)   =>  Element  //tagNames支持数组，区分大小写
       * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf)   =>  Element  //includeSelf指定是否包含自身
       * @grammar UM.dom.domUtils.findParentByTagName(node,tagNames,includeSelf,excludeFn)   =>  Element  //excludeFn指定例外过滤条件，返回true时忽略该节点
       */
      findParentByTagName: function (node, tagNames, includeSelf, excludeFn) {
          tagNames = utils.listToMap(utils.isArray(tagNames) ? tagNames : [tagNames]);
          return domUtils.findParent(node, function (node) {
              return tagNames[node.tagName] && !(excludeFn && excludeFn(node));
          }, includeSelf);
      },
      /**
       * 查找节点node的祖先节点集合
       * @name findParents
       * @grammar UM.dom.domUtils.findParents(node)  => Array  //返回一个祖先节点数组集合，不包含自身
       * @grammar UM.dom.domUtils.findParents(node,includeSelf)  => Array  //返回一个祖先节点数组集合，includeSelf指定是否包含自身
       * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn)  => Array  //返回一个祖先节点数组集合，filterFn指定过滤条件，返回true的node将被选取
       * @grammar UM.dom.domUtils.findParents(node,includeSelf,filterFn,closerFirst)  => Array  //返回一个祖先节点数组集合，closerFirst为true的话，node的直接父亲节点是数组的第0个
       */
      findParents: function (node, includeSelf, filterFn, closerFirst) {
          var parents = includeSelf && ( filterFn && filterFn(node) || !filterFn ) ? [node] : [];
          while (node = domUtils.findParent(node, filterFn)) {
              parents.push(node);
          }
          return closerFirst ? parents : parents.reverse();
      },
  
      /**
       * 在节点node后面插入新节点newNode
       * @name insertAfter
       * @grammar UM.dom.domUtils.insertAfter(node,newNode)  => newNode
       */
      insertAfter: function (node, newNode) {
          return node.parentNode.insertBefore(newNode, node.nextSibling);
      },
  
      /**
       * 删除节点node，并根据keepChildren指定是否保留子节点
       * @name remove
       * @grammar UM.dom.domUtils.remove(node)  =>  node
       * @grammar UM.dom.domUtils.remove(node,keepChildren)  =>  node
       */
      remove: function (node, keepChildren) {
  
          var parent = node.parentNode,
              child;
          if (parent) {
              if (keepChildren && node.hasChildNodes()) {
                  while (child = node.firstChild) {
                      parent.insertBefore(child, node);
                  }
              }
              parent.removeChild(node);
          }
          return node;
      },
  
  
      /**
       * 取得node节点的下一个兄弟节点， 如果该节点其后没有兄弟节点， 则递归查找其父节点之后的第一个兄弟节点，
       * 直到找到满足条件的节点或者递归到BODY节点之后才会结束。
       * @method getNextDomNode
       * @param { Node } node 需要获取其后的兄弟节点的节点对象
       * @return { Node | NULL } 如果找满足条件的节点， 则返回该节点， 否则返回NULL
       * @example
       * ```html
       *     <body>
       *      <div id="test">
       *          <span></span>
       *      </div>
       *      <i>xxx</i>
       * </body>
       * <script>
       *
       *     //output: i节点
       *     console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
       *
       * </script>
       * ```
       * @example
       * ```html
       * <body>
       *      <div>
       *          <span></span>
       *          <i id="test">xxx</i>
       *      </div>
       *      <b>xxx</b>
       * </body>
       * <script>
       *
       *     //由于id为test的i节点之后没有兄弟节点， 则查找其父节点（div）后面的兄弟节点
       *     //output: b节点
       *     console.log( UE.dom.domUtils.getNextDomNode( document.getElementById( "test" ) ) );
       *
       * </script>
       * ```
       */
  
      /**
       * 取得node节点的下一个兄弟节点， 如果startFromChild的值为ture，则先获取其子节点，
       * 如果有子节点则直接返回第一个子节点；如果没有子节点或者startFromChild的值为false，
       * 则执行<a href="#UE.dom.domUtils.getNextDomNode(Node)">getNextDomNode(Node node)</a>的查找过程。
       * @method getNextDomNode
       * @param { Node } node 需要获取其后的兄弟节点的节点对象
       * @param { Boolean } startFromChild 查找过程是否从其子节点开始
       * @return { Node | NULL } 如果找满足条件的节点， 则返回该节点， 否则返回NULL
       * @see UE.dom.domUtils.getNextDomNode(Node)
       */
      getNextDomNode:function (node, startFromChild, filterFn, guard) {
          return getDomNode(node, 'firstChild', 'nextSibling', startFromChild, filterFn, guard);
      },
      getPreDomNode:function (node, startFromChild, filterFn, guard) {
          return getDomNode(node, 'lastChild', 'previousSibling', startFromChild, filterFn, guard);
      },
  
      /**
       * 检测节点node是否属于bookmark节点
       * @name isBookmarkNode
       * @grammar UM.dom.domUtils.isBookmarkNode(node)  => true|false
       */
      isBookmarkNode: function (node) {
          return node.nodeType == 1 && node.id && /^_baidu_bookmark_/i.test(node.id);
      },
      /**
       * 获取节点node所在的window对象
       * @name  getWindow
       * @grammar UM.dom.domUtils.getWindow(node)  => window对象
       */
      getWindow: function (node) {
          var doc = node.ownerDocument || node;
          return doc.defaultView || doc.parentWindow;
      },
  
      /**
       * 获取离nodeA与nodeB最近的公共的祖先节点
       * @method  getCommonAncestor
       * @param { Node } nodeA 第一个节点
       * @param { Node } nodeB 第二个节点
       * @remind 如果给定的两个节点是同一个节点， 将直接返回该节点。
       * @return { Node | NULL } 如果未找到公共节点， 返回NULL， 否则返回最近的公共祖先节点。
       * @example
       * ```javascript
       * var commonAncestor = UE.dom.domUtils.getCommonAncestor( document.body, document.body.firstChild );
       * //output: true
       * console.log( commonAncestor.tagName.toLowerCase() === 'body' );
       * ```
       */
      getCommonAncestor:function (nodeA, nodeB) {
          if (nodeA === nodeB)
              return nodeA;
          var parentsA = [nodeA] , parentsB = [nodeB], parent = nodeA, i = -1;
          while (parent = parent.parentNode) {
              if (parent === nodeB) {
                  return parent;
              }
              parentsA.push(parent);
          }
          parent = nodeB;
          while (parent = parent.parentNode) {
              if (parent === nodeA)
                  return parent;
              parentsB.push(parent);
          }
          parentsA.reverse();
          parentsB.reverse();
          while (i++, parentsA[i] === parentsB[i]) {
          }
          return i == 0 ? null : parentsA[i - 1];
  
      },
      /**
       * 清除node节点左右连续为空的兄弟inline节点
       * @method clearEmptySibling
       * @param { Node } node 执行的节点对象， 如果该节点的左右连续的兄弟节点是空的inline节点，
       * 则这些兄弟节点将被删除
       * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext)  //ignoreNext指定是否忽略右边空节点
       * @grammar UE.dom.domUtils.clearEmptySibling(node,ignoreNext,ignorePre)  //ignorePre指定是否忽略左边空节点
       * @example
       * ```html
       * <body>
       *     <div></div>
       *     <span id="test"></span>
       *     <i></i>
       *     <b></b>
       *     <em>xxx</em>
       *     <span></span>
       * </body>
       * <script>
       *
       *      UE.dom.domUtils.clearEmptySibling( document.getElementById( "test" ) );
       *
       *      //output: <div></div><span id="test"></span><em>xxx</em><span></span>
       *      console.log( document.body.innerHTML );
       *
       * </script>
       * ```
       */
  
      /**
       * 清除node节点左右连续为空的兄弟inline节点， 如果ignoreNext的值为true，
       * 则忽略对右边兄弟节点的操作。
       * @method clearEmptySibling
       * @param { Node } node 执行的节点对象， 如果该节点的左右连续的兄弟节点是空的inline节点，
       * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
       * 则这些兄弟节点将被删除
       * @see UE.dom.domUtils.clearEmptySibling(Node)
       */
  
      /**
       * 清除node节点左右连续为空的兄弟inline节点， 如果ignoreNext的值为true，
       * 则忽略对右边兄弟节点的操作， 如果ignorePre的值为true，则忽略对左边兄弟节点的操作。
       * @method clearEmptySibling
       * @param { Node } node 执行的节点对象， 如果该节点的左右连续的兄弟节点是空的inline节点，
       * @param { Boolean } ignoreNext 是否忽略忽略对右边的兄弟节点的操作
       * @param { Boolean } ignorePre 是否忽略忽略对左边的兄弟节点的操作
       * 则这些兄弟节点将被删除
       * @see UE.dom.domUtils.clearEmptySibling(Node)
       */
      clearEmptySibling:function (node, ignoreNext, ignorePre) {
          function clear(next, dir) {
              var tmpNode;
              while (next && !domUtils.isBookmarkNode(next) && (domUtils.isEmptyInlineElement(next)
                  //这里不能把空格算进来会吧空格干掉，出现文字间的空格丢掉了
                  || !new RegExp('[^\t\n\r' + domUtils.fillChar + ']').test(next.nodeValue) )) {
                  tmpNode = next[dir];
                  domUtils.remove(next);
                  next = tmpNode;
              }
          }
          !ignoreNext && clear(node.nextSibling, 'nextSibling');
          !ignorePre && clear(node.previousSibling, 'previousSibling');
      },
  
      /**
       * 将一个文本节点node拆分成两个文本节点，offset指定拆分位置
       * @name split
       * @grammar UM.dom.domUtils.split(node,offset)  =>  TextNode  //返回从切分位置开始的后一个文本节点
       */
      split: function (node, offset) {
          var doc = node.ownerDocument;
          if (browser.ie && offset == node.nodeValue.length) {
              var next = doc.createTextNode('');
              return domUtils.insertAfter(node, next);
          }
          var retval = node.splitText(offset);
          //ie8下splitText不会跟新childNodes,我们手动触发他的更新
          if (browser.ie8) {
              var tmpNode = doc.createTextNode('');
              domUtils.insertAfter(retval, tmpNode);
              domUtils.remove(tmpNode);
          }
          return retval;
      },
  
      /**
       * 检测节点node是否为空节点（包括空格、换行、占位符等字符）
       * @name  isWhitespace
       * @grammar  UM.dom.domUtils.isWhitespace(node)  => true|false
       */
      isWhitespace: function (node) {
          return !new RegExp('[^ \t\n\r' + domUtils.fillChar + ']').test(node.nodeValue);
      },
      /**
       * 获取元素element相对于viewport的位置坐标
       * @name getXY
       * @grammar UM.dom.domUtils.getXY(element)  => Object //返回坐标对象{x:left,y:top}
       */
      getXY: function (element) {
          var x = 0, y = 0;
          while (element.offsetParent) {
              y += element.offsetTop;
              x += element.offsetLeft;
              element = element.offsetParent;
          }
          return { 'x': x, 'y': y};
      },
      /**
       * 检查节点node是否是空inline节点
       * @name  isEmptyInlineElement
       * @grammar   UM.dom.domUtils.isEmptyInlineElement(node)  => 1|0
       * @example
       * <b><i></i></b> => 1
       * <b><i></i><u></u></b> => 1
       * <b></b> => 1
       * <b>xx<i></i></b> => 0
       */
      isEmptyInlineElement: function (node) {
          if (node.nodeType != 1 || !dtd.$removeEmpty[ node.tagName ]) {
              return 0;
          }
          node = node.firstChild;
          while (node) {
              //如果是创建的bookmark就跳过
              if (domUtils.isBookmarkNode(node)) {
                  return 0;
              }
              if (node.nodeType == 1 && !domUtils.isEmptyInlineElement(node) ||
                  node.nodeType == 3 && !domUtils.isWhitespace(node)
                  ) {
                  return 0;
              }
              node = node.nextSibling;
          }
          return 1;
  
      },
  
  
      /**
       * 检查节点node是否为块元素
       * @name isBlockElm
       * @grammar UM.dom.domUtils.isBlockElm(node)  => true|false
       */
      isBlockElm: function (node) {
          return node.nodeType == 1 && (dtd.$block[node.tagName] || styleBlock[domUtils.getComputedStyle(node, 'display')]) && !dtd.$nonChild[node.tagName];
      },
  
  
      /**
       * 原生方法getElementsByTagName的封装
       * @name getElementsByTagName
       * @grammar UM.dom.domUtils.getElementsByTagName(node,tagName)  => Array  //节点集合数组
       */
      getElementsByTagName: function (node, name, filter) {
          if (filter && utils.isString(filter)) {
              var className = filter;
              filter = function (node) {
                  var result = false;
                  $.each(utils.trim(className).replace(/[ ]{2,}/g, ' ').split(' '), function (i, v) {
                      if ($(node).hasClass(v)) {
                          result = true;
                          return false;
                      }
                  })
                  return result;
              }
          }
          name = utils.trim(name).replace(/[ ]{2,}/g, ' ').split(' ');
          var arr = [];
          for (var n = 0, ni; ni = name[n++];) {
              var list = node.getElementsByTagName(ni);
              for (var i = 0, ci; ci = list[i++];) {
                  if (!filter || filter(ci))
                      arr.push(ci);
              }
          }
          return arr;
      },
  
  
      /**
       * 设置节点node及其子节点不会被选中
       * @name unSelectable
       * @grammar UM.dom.domUtils.unSelectable(node)
       */
      unSelectable: ie && browser.ie9below || browser.opera ? function (node) {
          //for ie9
          node.onselectstart = function () {
              return false;
          };
          node.onclick = node.onkeyup = node.onkeydown = function () {
              return false;
          };
          node.unselectable = 'on';
          node.setAttribute("unselectable", "on");
          for (var i = 0, ci; ci = node.all[i++];) {
              switch (ci.tagName.toLowerCase()) {
                  case 'iframe' :
                  case 'textarea' :
                  case 'input' :
                  case 'select' :
                      break;
                  default :
                      ci.unselectable = 'on';
                      node.setAttribute("unselectable", "on");
              }
          }
      } : function (node) {
          node.style.MozUserSelect =
              node.style.webkitUserSelect =
                      node.style.msUserSelect =
                          node.style.KhtmlUserSelect = 'none';
      },
      /**
       * 删除节点node上的属性attrNames，attrNames为属性名称数组
       * @name  removeAttributes
       * @grammar UM.dom.domUtils.removeAttributes(node,attrNames)
       * @example
       * //Before remove
       * <span style="font-size:14px;" id="test" name="followMe">xxxxx</span>
       * //Remove
       * UM.dom.domUtils.removeAttributes(node,["id","name"]);
       * //After remove
       * <span style="font-size:14px;">xxxxx</span>
       */
      removeAttributes: function (node, attrNames) {
          attrNames = utils.isArray(attrNames) ? attrNames : utils.trim(attrNames).replace(/[ ]{2,}/g, ' ').split(' ');
          for (var i = 0, ci; ci = attrNames[i++];) {
              ci = attrFix[ci] || ci;
              switch (ci) {
                  case 'className':
                      node[ci] = '';
                      break;
                  case 'style':
                      node.style.cssText = '';
                      if(node.getAttributeNode('style') !== null) {
                      	!browser.ie && node.removeAttributeNode(node.getAttributeNode('style'));
                      }
              }
              node.removeAttribute(ci);
          }
      },
      /**
       * 在doc下创建一个标签名为tag，属性为attrs的元素
       * @name createElement
       * @grammar UM.dom.domUtils.createElement(doc,tag,attrs)  =>  Node  //返回创建的节点
       */
      createElement: function (doc, tag, attrs) {
          return domUtils.setAttributes(doc.createElement(tag), attrs)
      },
      /**
       * 为节点node添加属性attrs，attrs为属性键值对
       * @name setAttributes
       * @grammar UM.dom.domUtils.setAttributes(node,attrs)  => node
       */
      setAttributes: function (node, attrs) {
          for (var attr in attrs) {
              if (attrs.hasOwnProperty(attr)) {
                  var value = attrs[attr];
                  switch (attr) {
                      case 'class':
                          //ie下要这样赋值，setAttribute不起作用
                          node.className = value;
                          break;
                      case 'style' :
                          node.style.cssText = node.style.cssText + ";" + value;
                          break;
                      case 'innerHTML':
                          node[attr] = value;
                          break;
                      case 'value':
                          node.value = value;
                          break;
                      default:
                          node.setAttribute(attrFix[attr] || attr, value);
                  }
              }
          }
          return node;
      },
  
      /**
       * 获取元素element的计算样式
       * @name getComputedStyle
       * @grammar UM.dom.domUtils.getComputedStyle(element,styleName)  => String //返回对应样式名称的样式值
       * @example
       * getComputedStyle(document.body,"font-size")  =>  "15px"
       * getComputedStyle(form,"color")  =>  "#ffccdd"
       */
      getComputedStyle: function (element, styleName) {
          return utils.transUnitToPx(utils.fixColor(styleName, $(element).css(styleName)));
      },
  
      /**
       * 阻止事件默认行为
       * @param {Event} evt    需要组织的事件对象
       */
      preventDefault: function (evt) {
          evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false);
      },
  
      /**
       * 删除元素element指定的样式
       * @method removeStyle
       * @param { Element } element 需要删除样式的元素
       * @param { String } styleName 需要删除的样式名
       * @example
       * ```html
       * <span id="test" style="color: red; background: blue;"></span>
       *
       * <script>
       *
       *     var testNode = document.getElementById("test");
       *
       *     UE.dom.domUtils.removeStyle( testNode, 'color' );
       *
       *     //output: background: blue;
       *     console.log( testNode.style.cssText );
       *
       * </script>
       * ```
       */
      removeStyle:function (element, name) {
          if(browser.ie ){
              //针对color先单独处理一下
              if(name == 'color'){
                  name = '(^|;)' + name;
              }
              element.style.cssText = element.style.cssText.replace(new RegExp(name + '[^:]*:[^;]+;?','ig'),'')
          }else{
              if (element.style.removeProperty) {
                  element.style.removeProperty (name);
              }else {
                  element.style.removeAttribute (utils.cssStyleToDomStyle(name));
              }
          }
  
  
          if (!element.style.cssText) {
              domUtils.removeAttributes(element, ['style']);
          }
      },
  
      /**
       * 获取元素element的某个样式值
       * @name getStyle
       * @grammar UM.dom.domUtils.getStyle(element,name)  => String
       */
      getStyle: function (element, name) {
          var value = element.style[ utils.cssStyleToDomStyle(name) ];
          return utils.fixColor(name, value);
      },
      /**
       * 为元素element设置样式属性值
       * @name setStyle
       * @grammar UM.dom.domUtils.setStyle(element,name,value)
       */
      setStyle: function (element, name, value) {
          element.style[utils.cssStyleToDomStyle(name)] = value;
          if (!utils.trim(element.style.cssText)) {
              this.removeAttributes(element, 'style')
          }
      },
  
      /**
       * 删除_moz_dirty属性
       * @function
       */
      removeDirtyAttr: function (node) {
          for (var i = 0, ci, nodes = node.getElementsByTagName('*'); ci = nodes[i++];) {
              ci.removeAttribute('_moz_dirty');
          }
          node.removeAttribute('_moz_dirty');
      },
      /**
       * 返回子节点的数量
       * @function
       * @param {Node}    node    父节点
       * @param  {Function}    fn    过滤子节点的规则，若为空，则得到所有子节点的数量
       * @return {Number}    符合条件子节点的数量
       */
      getChildCount: function (node, fn) {
          var count = 0, first = node.firstChild;
          fn = fn || function () {
              return 1;
          };
          while (first) {
              if (fn(first)) {
                  count++;
              }
              first = first.nextSibling;
          }
          return count;
      },
  
      /**
       * 判断是否为空节点
       * @function
       * @param {Node}    node    节点
       * @return {Boolean}    是否为空节点
       */
      isEmptyNode: function (node) {
          return !node.firstChild || domUtils.getChildCount(node, function (node) {
              return  !domUtils.isBr(node) && !domUtils.isBookmarkNode(node) && !domUtils.isWhitespace(node)
          }) == 0
      },
  
      /**
       * 判断节点是否为br
       * @function
       * @param {Node}    node   节点
       */
      isBr: function (node) {
          return node.nodeType == 1 && node.tagName == 'BR';
      },
      isFillChar: function (node, isInStart) {
          return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
      },
  
      isEmptyBlock: function (node, reg) {
          if (node.nodeType != 1)
              return 0;
          reg = reg || new RegExp('[ \t\r\n' + domUtils.fillChar + ']', 'g');
          if (node[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').length > 0) {
              return 0;
          }
          for (var n in dtd.$isNotEmpty) {
              if (node.getElementsByTagName(n).length) {
                  return 0;
              }
          }
          return 1;
      },
  
      //判断是否是编辑器自定义的参数
      isCustomeNode: function (node) {
          return node.nodeType == 1 && node.getAttribute('_ue_custom_node_');
      },
      fillNode: function (doc, node) {
          var tmpNode = browser.ie ? doc.createTextNode(domUtils.fillChar) : doc.createElement('br');
          node.innerHTML = '';
          node.appendChild(tmpNode);
      },
      isBoundaryNode: function (node, dir) {
          var tmp;
          while (!domUtils.isBody(node)) {
              tmp = node;
              node = node.parentNode;
              if (tmp !== node[dir]) {
                  return false;
              }
          }
          return true;
      },
      isFillChar: function (node, isInStart) {
          return node.nodeType == 3 && !node.nodeValue.replace(new RegExp((isInStart ? '^' : '' ) + domUtils.fillChar), '').length
      },
      isBody: function(node){
          return $(node).hasClass('edui-body-container');
      }
  };
  var fillCharReg = new RegExp(domUtils.fillChar, 'g');
  ///import editor.js
  ///import core/utils.js
  ///import core/browser.js
  ///import core/dom/dom.js
  ///import core/dom/dtd.js
  ///import core/dom/domUtils.js
  /**
   * @file
   * @name UM.dom.Range
   * @anthor zhanyi
   * @short Range
   * @import editor.js,core/utils.js,core/browser.js,core/dom/domUtils.js,core/dom/dtd.js
   * @desc Range范围实现类，本类是UEditor底层核心类，统一w3cRange和ieRange之间的差异，包括接口和属性
   */
  (function () {
      var guid = 0,
          fillChar = domUtils.fillChar,
          fillData;
  
      /**
       * 更新range的collapse状态
       * @param  {Range}   range    range对象
       */
      function updateCollapse(range) {
          range.collapsed =
              range.startContainer && range.endContainer &&
                  range.startContainer === range.endContainer &&
                  range.startOffset == range.endOffset;
      }
  
      function selectOneNode(rng){
          return !rng.collapsed && rng.startContainer.nodeType == 1 && rng.startContainer === rng.endContainer && rng.endOffset - rng.startOffset == 1
      }
      function setEndPoint(toStart, node, offset, range) {
          //如果node是自闭合标签要处理
          if (node.nodeType == 1 && (dtd.$empty[node.tagName] || dtd.$nonChild[node.tagName])) {
              offset = domUtils.getNodeIndex(node) + (toStart ? 0 : 1);
              node = node.parentNode;
          }
          if (toStart) {
              range.startContainer = node;
              range.startOffset = offset;
              if (!range.endContainer) {
                  range.collapse(true);
              }
          } else {
              range.endContainer = node;
              range.endOffset = offset;
              if (!range.startContainer) {
                  range.collapse(false);
              }
          }
          updateCollapse(range);
          return range;
      }
  
  
      /**
       * @name Range
       * @grammar new UM.dom.Range(document)  => Range 实例
       * @desc 创建一个跟document绑定的空的Range实例
       * - ***startContainer*** 开始边界的容器节点,可以是elementNode或者是textNode
       * - ***startOffset*** 容器节点中的偏移量，如果是elementNode就是childNodes中的第几个，如果是textNode就是nodeValue的第几个字符
       * - ***endContainer*** 结束边界的容器节点,可以是elementNode或者是textNode
       * - ***endOffset*** 容器节点中的偏移量，如果是elementNode就是childNodes中的第几个，如果是textNode就是nodeValue的第几个字符
       * - ***document*** 跟range关联的document对象
       * - ***collapsed*** 是否是闭合状态
       */
      var Range = dom.Range = function (document,body) {
          var me = this;
          me.startContainer =
              me.startOffset =
                  me.endContainer =
                      me.endOffset = null;
          me.document = document;
          me.collapsed = true;
          me.body = body;
      };
  
      /**
       * 删除fillData
       * @param doc
       * @param excludeNode
       */
      function removeFillData(doc, excludeNode) {
          try {
              if (fillData && domUtils.inDoc(fillData, doc)) {
                  if (!fillData.nodeValue.replace(fillCharReg, '').length) {
                      var tmpNode = fillData.parentNode;
                      domUtils.remove(fillData);
                      while (tmpNode && domUtils.isEmptyInlineElement(tmpNode) &&
                          //safari的contains有bug
                          (browser.safari ? !(domUtils.getPosition(tmpNode,excludeNode) & domUtils.POSITION_CONTAINS) : !tmpNode.contains(excludeNode))
                          ) {
                          fillData = tmpNode.parentNode;
                          domUtils.remove(tmpNode);
                          tmpNode = fillData;
                      }
                  } else {
                      fillData.nodeValue = fillData.nodeValue.replace(fillCharReg, '');
                  }
              }
          } catch (e) {
          }
      }
  
      /**
       *
       * @param node
       * @param dir
       */
      function mergeSibling(node, dir) {
          var tmpNode;
          node = node[dir];
          while (node && domUtils.isFillChar(node)) {
              tmpNode = node[dir];
              domUtils.remove(node);
              node = tmpNode;
          }
      }
  
      function execContentsAction(range, action) {
          //调整边界
          //range.includeBookmark();
          var start = range.startContainer,
              end = range.endContainer,
              startOffset = range.startOffset,
              endOffset = range.endOffset,
              doc = range.document,
              frag = doc.createDocumentFragment(),
              tmpStart, tmpEnd;
          if (start.nodeType == 1) {
              start = start.childNodes[startOffset] || (tmpStart = start.appendChild(doc.createTextNode('')));
          }
          if (end.nodeType == 1) {
              end = end.childNodes[endOffset] || (tmpEnd = end.appendChild(doc.createTextNode('')));
          }
          if (start === end && start.nodeType == 3) {
              frag.appendChild(doc.createTextNode(start.substringData(startOffset, endOffset - startOffset)));
              //is not clone
              if (action) {
                  start.deleteData(startOffset, endOffset - startOffset);
                  range.collapse(true);
              }
              return frag;
          }
          var current, currentLevel, clone = frag,
              startParents = domUtils.findParents(start, true), endParents = domUtils.findParents(end, true);
          for (var i = 0; startParents[i] == endParents[i];) {
              i++;
          }
          for (var j = i, si; si = startParents[j]; j++) {
              current = si.nextSibling;
              if (si == start) {
                  if (!tmpStart) {
                      if (range.startContainer.nodeType == 3) {
                          clone.appendChild(doc.createTextNode(start.nodeValue.slice(startOffset)));
                          //is not clone
                          if (action) {
                              start.deleteData(startOffset, start.nodeValue.length - startOffset);
                          }
                      } else {
                          clone.appendChild(!action ? start.cloneNode(true) : start);
                      }
                  }
              } else {
                  currentLevel = si.cloneNode(false);
                  clone.appendChild(currentLevel);
              }
              while (current) {
                  if (current === end || current === endParents[j]) {
                      break;
                  }
                  si = current.nextSibling;
                  clone.appendChild(!action ? current.cloneNode(true) : current);
                  current = si;
              }
              clone = currentLevel;
          }
          clone = frag;
          if (!startParents[i]) {
              clone.appendChild(startParents[i - 1].cloneNode(false));
              clone = clone.firstChild;
          }
          for (var j = i, ei; ei = endParents[j]; j++) {
              current = ei.previousSibling;
              if (ei == end) {
                  if (!tmpEnd && range.endContainer.nodeType == 3) {
                      clone.appendChild(doc.createTextNode(end.substringData(0, endOffset)));
                      //is not clone
                      if (action) {
                          end.deleteData(0, endOffset);
                      }
                  }
              } else {
                  currentLevel = ei.cloneNode(false);
                  clone.appendChild(currentLevel);
              }
              //如果两端同级，右边第一次已经被开始做了
              if (j != i || !startParents[i]) {
                  while (current) {
                      if (current === start) {
                          break;
                      }
                      ei = current.previousSibling;
                      clone.insertBefore(!action ? current.cloneNode(true) : current, clone.firstChild);
                      current = ei;
                  }
              }
              clone = currentLevel;
          }
          if (action) {
              range.setStartBefore(!endParents[i] ? endParents[i - 1] : !startParents[i] ? startParents[i - 1] : endParents[i]).collapse(true);
          }
          tmpStart && domUtils.remove(tmpStart);
          tmpEnd && domUtils.remove(tmpEnd);
          return frag;
      }
      Range.prototype = {
          /**
           * @name deleteContents
           * @grammar range.deleteContents()  => Range
           * @desc 删除当前选区范围中的所有内容并返回range实例，这时的range已经变成了闭合状态
           * @example
           * DOM Element :
           * <b>x<i>x[x<i>xx]x</b>
           * //执行方法后
           * <b>x<i>x<i>|x</b>
           * 注意range改变了
           * range.startContainer => b
           * range.startOffset  => 2
           * range.endContainer => b
           * range.endOffset => 2
           * range.collapsed => true
           */
          deleteContents:function () {
              var txt;
              if (!this.collapsed) {
                  execContentsAction(this, 1);
              }
              if (browser.webkit) {
                  txt = this.startContainer;
                  if (txt.nodeType == 3 && !txt.nodeValue.length) {
                      this.setStartBefore(txt).collapse(true);
                      domUtils.remove(txt);
                  }
              }
              return this;
          },
          inFillChar : function(){
              var start = this.startContainer;
              if(this.collapsed && start.nodeType == 3
                  && start.nodeValue.replace(new RegExp('^' + domUtils.fillChar),'').length + 1 == start.nodeValue.length
                  ){
                  return true;
              }
              return false;
          },
          /**
           * @name  setStart
           * @grammar range.setStart(node,offset)  => Range
           * @desc    设置range的开始位置位于node节点内，偏移量为offset
           * 如果node是elementNode那offset指的是childNodes中的第几个，如果是textNode那offset指的是nodeValue的第几个字符
           */
          setStart:function (node, offset) {
              return setEndPoint(true, node, offset, this);
          },
          /**
           * 设置range的结束位置位于node节点，偏移量为offset
           * 如果node是elementNode那offset指的是childNodes中的第几个，如果是textNode那offset指的是nodeValue的第几个字符
           * @name  setEnd
           * @grammar range.setEnd(node,offset)  => Range
           */
          setEnd:function (node, offset) {
              return setEndPoint(false, node, offset, this);
          },
          /**
           * 将Range开始位置设置到node节点之后
           * @name  setStartAfter
           * @grammar range.setStartAfter(node)  => Range
           * @example
           * <b>xx<i>x|x</i>x</b>
           * 执行setStartAfter(i)后
           * range.startContainer =>b
           * range.startOffset =>2
           */
          setStartAfter:function (node) {
              return this.setStart(node.parentNode, domUtils.getNodeIndex(node) + 1);
          },
          
          
          extractContents:function () {
              return this.collapsed ? null : execContentsAction(this, 2);
          },
          
          /**
           * 将Range开始位置设置到node节点之前
           * @name  setStartBefore
           * @grammar range.setStartBefore(node)  => Range
           * @example
           * <b>xx<i>x|x</i>x</b>
           * 执行setStartBefore(i)后
           * range.startContainer =>b
           * range.startOffset =>1
           */
          setStartBefore:function (node) {
              return this.setStart(node.parentNode, domUtils.getNodeIndex(node));
          },
          /**
           * 将Range结束位置设置到node节点之后
           * @name  setEndAfter
           * @grammar range.setEndAfter(node)  => Range
           * @example
           * <b>xx<i>x|x</i>x</b>
           * setEndAfter(i)后
           * range.endContainer =>b
           * range.endtOffset =>2
           */
          setEndAfter:function (node) {
              return this.setEnd(node.parentNode, domUtils.getNodeIndex(node) + 1);
          },
          /**
           * 将Range结束位置设置到node节点之前
           * @name  setEndBefore
           * @grammar range.setEndBefore(node)  => Range
           * @example
           * <b>xx<i>x|x</i>x</b>
           * 执行setEndBefore(i)后
           * range.endContainer =>b
           * range.endtOffset =>1
           */
          setEndBefore:function (node) {
              return this.setEnd(node.parentNode, domUtils.getNodeIndex(node));
          },
          /**
           * 将Range开始位置设置到node节点内的开始位置
           * @name  setStartAtFirst
           * @grammar range.setStartAtFirst(node)  => Range
           */
          setStartAtFirst:function (node) {
              return this.setStart(node, 0);
          },
          /**
           * 将Range开始位置设置到node节点内的结束位置
           * @name  setStartAtLast
           * @grammar range.setStartAtLast(node)  => Range
           */
          setStartAtLast:function (node) {
              return this.setStart(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
          },
          /**
           * 将Range结束位置设置到node节点内的开始位置
           * @name  setEndAtFirst
           * @grammar range.setEndAtFirst(node)  => Range
           */
          setEndAtFirst:function (node) {
              return this.setEnd(node, 0);
          },
          /**
           * 将Range结束位置设置到node节点内的结束位置
           * @name  setEndAtLast
           * @grammar range.setEndAtLast(node)  => Range
           */
          setEndAtLast:function (node) {
              return this.setEnd(node, node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length);
          },
  
          /**
           * 选中完整的指定节点,并返回包含该节点的range
           * @name  selectNode
           * @grammar range.selectNode(node)  => Range
           */
          selectNode:function (node) {
              return this.setStartBefore(node).setEndAfter(node);
          },
          /**
           * 选中node内部的所有节点，并返回对应的range
           * @name selectNodeContents
           * @grammar range.selectNodeContents(node)  => Range
           * @example
           * <b>xx[x<i>xxx</i>]xxx</b>
           * 执行后
           * <b>[xxx<i>xxx</i>xxx]</b>
           * range.startContainer =>b
           * range.startOffset =>0
           * range.endContainer =>b
           * range.endOffset =>3
           */
          selectNodeContents:function (node) {
              return this.setStart(node, 0).setEndAtLast(node);
          },
  
          /**
           * 克隆一个新的range对象
           * @name  cloneRange
           * @grammar range.cloneRange() => Range
           */
          cloneRange:function () {
              var me = this;
              return new Range(me.document).setStart(me.startContainer, me.startOffset).setEnd(me.endContainer, me.endOffset);
  
          },
  
          /**
           * 让选区闭合到尾部，若toStart为真，则闭合到头部
           * @name  collapse
           * @grammar range.collapse() => Range
           * @grammar range.collapse(true) => Range   //闭合选区到头部
           */
          collapse:function (toStart) {
              var me = this;
              if (toStart) {
                  me.endContainer = me.startContainer;
                  me.endOffset = me.startOffset;
              } else {
                  me.startContainer = me.endContainer;
                  me.startOffset = me.endOffset;
              }
              me.collapsed = true;
              return me;
          },
  
          /**
           * 调整range的边界，使其"收缩"到最小的位置
           * @name  shrinkBoundary
           * @grammar range.shrinkBoundary()  => Range  //range开始位置和结束位置都调整，参见<code><a href="#adjustmentboundary">adjustmentBoundary</a></code>
           * @grammar range.shrinkBoundary(true)  => Range  //仅调整开始位置，忽略结束位置
           * @example
           * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
           * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx]</b><i>xxx</i>
           * [<b><i>xxxx</i>xxxxxxx</b>] ==> <b><i>[xxxx</i>xxxxxxx]</b>
           */
          shrinkBoundary:function (ignoreEnd) {
              var me = this, child,
                  collapsed = me.collapsed;
              function check(node){
                  return node.nodeType == 1 && !domUtils.isBookmarkNode(node) && !dtd.$empty[node.tagName] && !dtd.$nonChild[node.tagName]
              }
              while (me.startContainer.nodeType == 1 //是element
                  && (child = me.startContainer.childNodes[me.startOffset]) //子节点也是element
                  && check(child)) {
                  me.setStart(child, 0);
              }
              if (collapsed) {
                  return me.collapse(true);
              }
              if (!ignoreEnd) {
                  while (me.endContainer.nodeType == 1//是element
                      && me.endOffset > 0 //如果是空元素就退出 endOffset=0那么endOffst-1为负值，childNodes[endOffset]报错
                      && (child = me.endContainer.childNodes[me.endOffset - 1]) //子节点也是element
                      && check(child)) {
                      me.setEnd(child, child.childNodes.length);
                  }
              }
              return me;
          },
  
          /**
           * 调整边界容器，如果是textNode,就调整到elementNode上
           * @name trimBoundary
           * @grammar range.trimBoundary([ignoreEnd])  => Range //true忽略结束边界
           * @example
           * DOM Element :
           * <b>|xxx</b>
           * startContainer = xxx; startOffset = 0
           * //执行后本方法后
           * startContainer = <b>;  startOffset = 0
           * @example
           * Dom Element :
           * <b>xx|x</b>
           * startContainer = xxx;  startOffset = 2
           * //执行本方法后，xxx被实实在在地切分成两个TextNode
           * startContainer = <b>; startOffset = 1
           */
          trimBoundary:function (ignoreEnd) {
              this.txtToElmBoundary();
              var start = this.startContainer,
                  offset = this.startOffset,
                  collapsed = this.collapsed,
                  end = this.endContainer;
              if (start.nodeType == 3) {
                  if (offset == 0) {
                      this.setStartBefore(start);
                  } else {
                      if (offset >= start.nodeValue.length) {
                          this.setStartAfter(start);
                      } else {
                          var textNode = domUtils.split(start, offset);
                          //跟新结束边界
                          if (start === end) {
                              this.setEnd(textNode, this.endOffset - offset);
                          } else if (start.parentNode === end) {
                              this.endOffset += 1;
                          }
                          this.setStartBefore(textNode);
                      }
                  }
                  if (collapsed) {
                      return this.collapse(true);
                  }
              }
              if (!ignoreEnd) {
                  offset = this.endOffset;
                  end = this.endContainer;
                  if (end.nodeType == 3) {
                      if (offset == 0) {
                          this.setEndBefore(end);
                      } else {
                          offset < end.nodeValue.length && domUtils.split(end, offset);
                          this.setEndAfter(end);
                      }
                  }
              }
              return this;
          },
          /**
           * 如果选区在文本的边界上，就扩展选区到文本的父节点上
           * @name  txtToElmBoundary
           * @example
           * Dom Element :
           * <b> |xxx</b>
           * startContainer = xxx;  startOffset = 0
           * //本方法执行后
           * startContainer = <b>; startOffset = 0
           * @example
           * Dom Element :
           * <b> xxx| </b>
           * startContainer = xxx; startOffset = 3
           * //本方法执行后
           * startContainer = <b>; startOffset = 1
           */
          txtToElmBoundary:function (ignoreCollapsed) {
              function adjust(r, c) {
                  var container = r[c + 'Container'],
                      offset = r[c + 'Offset'];
                  if (container.nodeType == 3) {
                      if (!offset) {
                          r['set' + c.replace(/(\w)/, function (a) {
                              return a.toUpperCase();
                          }) + 'Before'](container);
                      } else if (offset >= container.nodeValue.length) {
                          r['set' + c.replace(/(\w)/, function (a) {
                              return a.toUpperCase();
                          }) + 'After' ](container);
                      }
                  }
              }
  
              if (ignoreCollapsed || !this.collapsed) {
                  adjust(this, 'start');
                  adjust(this, 'end');
              }
              return this;
          },
  
          /**
           * 在当前选区的开始位置前插入一个节点或者fragment，range的开始位置会在插入节点的前边
           * @name  insertNode
           * @grammar range.insertNode(node)  => Range //node可以是textNode,elementNode,fragment
           * @example
           * Range :
           * xxx[x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
           * 待插入Node :
           * <p>ssss</p>
           * 执行本方法后的Range :
           * xxx[<p>ssss</p>x<p>xxxx</p>xxxx]x<p>sdfsdf</p>
           */
          insertNode:function (node) {
              var first = node, length = 1;
              if (node.nodeType == 11) {
                  first = node.firstChild;
                  length = node.childNodes.length;
              }
              this.trimBoundary(true);
              var start = this.startContainer,
                  offset = this.startOffset;
              var nextNode = start.childNodes[ offset ];
              if (nextNode) {
                  start.insertBefore(node, nextNode);
              } else {
                  start.appendChild(node);
              }
              if (first.parentNode === this.endContainer) {
                  this.endOffset = this.endOffset + length;
              }
              return this.setStartBefore(first);
          },
          /**
           * 设置光标闭合位置,toEnd设置为true时光标将闭合到选区的结尾
           * @name  setCursor
           * @grammar range.setCursor([toEnd])  =>  Range   //toEnd为true时，光标闭合到选区的末尾
           */
          setCursor:function (toEnd, noFillData) {
              return this.collapse(!toEnd).select(noFillData);
          },
          /**
           * 创建当前range的一个书签，记录下当前range的位置，方便当dom树改变时，还能找回原来的选区位置
           * @name createBookmark
           * @grammar range.createBookmark([serialize])  => Object  //{start:开始标记,end:结束标记,id:serialize} serialize为真时，开始结束标记是插入节点的id，否则是插入节点的引用
           */
          createBookmark:function (serialize, same) {
              var endNode,
                  startNode = this.document.createElement('span');
              startNode.style.cssText = 'display:none;line-height:0px;';
              startNode.appendChild(this.document.createTextNode('\u200D'));
              startNode.id = '_baidu_bookmark_start_' + (same ? '' : guid++);
  
              if (!this.collapsed) {
                  endNode = startNode.cloneNode(true);
                  endNode.id = '_baidu_bookmark_end_' + (same ? '' : guid++);
              }
              this.insertNode(startNode);
              if (endNode) {
                  this.collapse().insertNode(endNode).setEndBefore(endNode);
              }
              this.setStartAfter(startNode);
              return {
                  start:serialize ? startNode.id : startNode,
                  end:endNode ? serialize ? endNode.id : endNode : null,
                  id:serialize
              }
          },
          /**
           *  移动边界到书签位置，并删除插入的书签节点
           *  @name  moveToBookmark
           *  @grammar range.moveToBookmark(bookmark)  => Range //让当前的range选到给定bookmark的位置,bookmark对象是由range.createBookmark创建的
           */
          moveToBookmark:function (bookmark) {
              var start = bookmark.id ? this.document.getElementById(bookmark.start) : bookmark.start,
                  end = bookmark.end && bookmark.id ? this.document.getElementById(bookmark.end) : bookmark.end;
              this.setStartBefore(start);
              domUtils.remove(start);
              if (end) {
                  this.setEndBefore(end);
                  domUtils.remove(end);
              } else {
                  this.collapse(true);
              }
              return this;
          },
  
          /**
           * 调整Range的边界，使其"缩小"到最合适的位置
           * @name adjustmentBoundary
           * @grammar range.adjustmentBoundary() => Range   //参见<code><a href="#shrinkboundary">shrinkBoundary</a></code>
           * @example
           * <b>xx[</b>xxxxx] ==> <b>xx</b>[xxxxx]
           * <b>x[xx</b><i>]xxx</i> ==> <b>x[xx</b>]<i>xxx</i>
           */
          adjustmentBoundary:function () {
              if (!this.collapsed) {
                  while (!domUtils.isBody(this.startContainer) &&
                      this.startOffset == this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length &&
                      this.startContainer[this.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
                      ) {
  
                      this.setStartAfter(this.startContainer);
                  }
                  while (!domUtils.isBody(this.endContainer) && !this.endOffset &&
                      this.endContainer[this.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
                      ) {
                      this.setEndBefore(this.endContainer);
                  }
              }
              return this;
          },
  
          /**
           * 得到一个自闭合的节点,常用于获取自闭和的节点，例如图片节点
           * @name  getClosedNode
           * @grammar range.getClosedNode()  => node|null
           * @example
           * <b>xxxx[<img />]xxx</b>
           */
          getClosedNode:function () {
              var node;
              if (!this.collapsed) {
                  var range = this.cloneRange().adjustmentBoundary().shrinkBoundary();
                  if (selectOneNode(range)) {
                      var child = range.startContainer.childNodes[range.startOffset];
                      if (child && child.nodeType == 1 && (dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName])) {
                          node = child;
                      }
                  }
              }
              return node;
          },
          /**
           * 根据当前range选中内容节点（在页面上表现为反白显示）
           * @name select
           * @grammar range.select();  => Range
           */
          select:browser.ie ? function (noFillData, textRange) {
              var nativeRange;
              if (!this.collapsed)
                  this.shrinkBoundary();
              var node = this.getClosedNode();
              if (node && !textRange) {
                  try {
                      nativeRange = this.document.body.createControlRange();
                      nativeRange.addElement(node);
                      nativeRange.select();
                  } catch (e) {}
                  return this;
              }
              var bookmark = this.createBookmark(),
                  start = bookmark.start,
                  end;
              nativeRange = this.document.body.createTextRange();
              nativeRange.moveToElementText(start);
              nativeRange.moveStart('character', 1);
              if (!this.collapsed) {
                  var nativeRangeEnd = this.document.body.createTextRange();
                  end = bookmark.end;
                  nativeRangeEnd.moveToElementText(end);
                  nativeRange.setEndPoint('EndToEnd', nativeRangeEnd);
              } else {
                  if (!noFillData && this.startContainer.nodeType != 3) {
                      //使用<span>|x<span>固定住光标
                      var tmpText = this.document.createTextNode(fillChar),
                          tmp = this.document.createElement('span');
                      tmp.appendChild(this.document.createTextNode(fillChar));
                      start.parentNode.insertBefore(tmp, start);
                      start.parentNode.insertBefore(tmpText, start);
                      //当点b,i,u时，不能清除i上边的b
                      removeFillData(this.document, tmpText);
                      fillData = tmpText;
                      mergeSibling(tmp, 'previousSibling');
                      mergeSibling(start, 'nextSibling');
                      nativeRange.moveStart('character', -1);
                      nativeRange.collapse(true);
                  }
              }
              this.moveToBookmark(bookmark);
              tmp && domUtils.remove(tmp);
              //IE在隐藏状态下不支持range操作，catch一下
              try {
                  nativeRange.select();
              } catch (e) {
              }
              return this;
          } : function (notInsertFillData) {
              function checkOffset(rng){
  
                  function check(node,offset,dir){
                      if(node.nodeType == 3 && node.nodeValue.length < offset){
                          rng[dir + 'Offset'] = node.nodeValue.length
                      }
                  }
                  check(rng.startContainer,rng.startOffset,'start');
                  check(rng.endContainer,rng.endOffset,'end');
              }
              var win = domUtils.getWindow(this.document),
                  sel = win.getSelection(),
                  txtNode;
              //FF下关闭自动长高时滚动条在关闭dialog时会跳
              //ff下如果不body.focus将不能定位闭合光标到编辑器内
              browser.gecko ? this.body.focus() : win.focus();
              if (sel) {
                  sel.removeAllRanges();
                  // trace:870 chrome/safari后边是br对于闭合得range不能定位 所以去掉了判断
                  // this.startContainer.nodeType != 3 &&! ((child = this.startContainer.childNodes[this.startOffset]) && child.nodeType == 1 && child.tagName == 'BR'
                  if (this.collapsed && !notInsertFillData) {
  //                    //opear如果没有节点接着，原生的不能够定位,不能在body的第一级插入空白节点
  //                    if (notInsertFillData && browser.opera && !domUtils.isBody(this.startContainer) && this.startContainer.nodeType == 1) {
  //                        var tmp = this.document.createTextNode('');
  //                        this.insertNode(tmp).setStart(tmp, 0).collapse(true);
  //                    }
  //
                      //处理光标落在文本节点的情况
                      //处理以下的情况
                      //<b>|xxxx</b>
                      //<b>xxxx</b>|xxxx
                      //xxxx<b>|</b>
                      var start = this.startContainer,child = start;
                      if(start.nodeType == 1){
                          child = start.childNodes[this.startOffset];
  
                      }
                      if( !(start.nodeType == 3 && this.startOffset)  &&
                          (child ?
                              (!child.previousSibling || child.previousSibling.nodeType != 3)
                              :
                              (!start.lastChild || start.lastChild.nodeType != 3)
                              )
                          ){
                          txtNode = this.document.createTextNode(fillChar);
                          //跟着前边走
                          this.insertNode(txtNode);
                          removeFillData(this.document, txtNode);
                          mergeSibling(txtNode, 'previousSibling');
                          mergeSibling(txtNode, 'nextSibling');
                          fillData = txtNode;
                          this.setStart(txtNode, browser.webkit ? 1 : 0).collapse(true);
                      }
                  }
                  var nativeRange = this.document.createRange();
                  if(this.collapsed && browser.opera && this.startContainer.nodeType == 1){
                      var child = this.startContainer.childNodes[this.startOffset];
                      if(!child){
                          //往前靠拢
                          child = this.startContainer.lastChild;
                          if( child && domUtils.isBr(child)){
                              this.setStartBefore(child).collapse(true);
                          }
                      }else{
                          //向后靠拢
                          while(child && domUtils.isBlockElm(child)){
                              if(child.nodeType == 1 && child.childNodes[0]){
                                  child = child.childNodes[0]
                              }else{
                                  break;
                              }
                          }
                          child && this.setStartBefore(child).collapse(true)
                      }
  
                  }
                  //是createAddress最后一位算的不准，现在这里进行微调
                  checkOffset(this);
                  nativeRange.setStart(this.startContainer, this.startOffset);
                  nativeRange.setEnd(this.endContainer, this.endOffset);
                  sel.addRange(nativeRange);
              }
              return this;
          },
        
  
          createAddress : function(ignoreEnd,ignoreTxt){
              var addr = {},me = this;
  
              function getAddress(isStart){
                  var node = isStart ? me.startContainer : me.endContainer;
                  var parents = domUtils.findParents(node,true,function(node){return !domUtils.isBody(node)}),
                      addrs = [];
                  for(var i = 0,ci;ci = parents[i++];){
                      addrs.push(domUtils.getNodeIndex(ci,ignoreTxt));
                  }
                  var firstIndex = 0;
  
                  if(ignoreTxt){
                      if(node.nodeType == 3){
                          var tmpNode = node.previousSibling;
                          while(tmpNode && tmpNode.nodeType == 3){
                              firstIndex += tmpNode.nodeValue.replace(fillCharReg,'').length;
                              tmpNode = tmpNode.previousSibling;
                          }
                          firstIndex +=  (isStart ? me.startOffset : me.endOffset)// - (fillCharReg.test(node.nodeValue) ? 1 : 0 )
                      }else{
                          node =  node.childNodes[ isStart ? me.startOffset : me.endOffset];
                          if(node){
                              firstIndex = domUtils.getNodeIndex(node,ignoreTxt);
                          }else{
                              node = isStart ? me.startContainer : me.endContainer;
                              var first = node.firstChild;
                              while(first){
                                  if(domUtils.isFillChar(first)){
                                      first = first.nextSibling;
                                      continue;
                                  }
                                  firstIndex++;
                                  if(first.nodeType == 3){
                                      while( first && first.nodeType == 3){
                                          first = first.nextSibling;
                                      }
                                  }else{
                                      first = first.nextSibling;
                                  }
                              }
                          }
                      }
  
                  }else{
                      firstIndex = isStart ? domUtils.isFillChar(node) ? 0 : me.startOffset  : me.endOffset
                  }
                  if(firstIndex < 0){
                      firstIndex = 0;
                  }
                  addrs.push(firstIndex);
                  return addrs;
              }
              addr.startAddress = getAddress(true);
              if(!ignoreEnd){
                  addr.endAddress = me.collapsed ? [].concat(addr.startAddress) : getAddress();
              }
              return addr;
          },
          moveToAddress : function(addr,ignoreEnd){
              var me = this;
              function getNode(address,isStart){
                  var tmpNode = me.body,
                      parentNode,offset;
                  for(var i= 0,ci,l=address.length;i<l;i++){
                      ci = address[i];
                      parentNode = tmpNode;
                      tmpNode = tmpNode.childNodes[ci];
                      if(!tmpNode){
                          offset = ci;
                          break;
                      }
                  }
                  if(isStart){
                      if(tmpNode){
                          me.setStartBefore(tmpNode)
                      }else{
                          me.setStart(parentNode,offset)
                      }
                  }else{
                      if(tmpNode){
                          me.setEndBefore(tmpNode)
                      }else{
                          me.setEnd(parentNode,offset)
                      }
                  }
              }
              getNode(addr.startAddress,true);
              !ignoreEnd && addr.endAddress &&  getNode(addr.endAddress);
              return me;
          },
          equals : function(rng){
              for(var p in this){
                  if(this.hasOwnProperty(p)){
                      if(this[p] !== rng[p])
                          return false
                  }
              }
              return true;
  
          },
          scrollIntoView : function(){
              var $span = $('<span style="padding:0;margin:0;display:block;border:0">&nbsp;</span>');
              this.cloneRange().insertNode($span.get(0));
              var winScrollTop = $(window).scrollTop(),
                  winHeight = $(window).height(),
                  spanTop = $span.offset().top;
              if(spanTop < winScrollTop-winHeight || spanTop > winScrollTop + winHeight ){
                  if(spanTop > winScrollTop + winHeight){
                      window.scrollTo(0,spanTop - winHeight + $span.height())
                  }else{
                      window.scrollTo(0,winScrollTop - spanTop)
                  }
  
              }
              $span.remove();
          },
          getOffset : function(){
              var bk = this.createBookmark();
              var offset = $(bk.start).css('display','inline-block').offset();
              this.moveToBookmark(bk);
              return offset
          }
      };
  })();
  ///import editor.js
  ///import core/browser.js
  ///import core/dom/dom.js
  ///import core/dom/dtd.js
  ///import core/dom/domUtils.js
  ///import core/dom/Range.js
  /**
   * @class UM.dom.Selection    Selection类
   */
  (function () {
  
      function getBoundaryInformation( range, start ) {
          var getIndex = domUtils.getNodeIndex;
          range = range.duplicate();
          range.collapse( start );
          var parent = range.parentElement();
          //如果节点里没有子节点，直接退出
          if ( !parent.hasChildNodes() ) {
              return  {container:parent, offset:0};
          }
          var siblings = parent.children,
              child,
              testRange = range.duplicate(),
              startIndex = 0, endIndex = siblings.length - 1, index = -1,
              distance;
          while ( startIndex <= endIndex ) {
              index = Math.floor( (startIndex + endIndex) / 2 );
              child = siblings[index];
              testRange.moveToElementText( child );
              var position = testRange.compareEndPoints( 'StartToStart', range );
              if ( position > 0 ) {
                  endIndex = index - 1;
              } else if ( position < 0 ) {
                  startIndex = index + 1;
              } else {
                  //trace:1043
                  return  {container:parent, offset:getIndex( child )};
              }
          }
          if ( index == -1 ) {
              testRange.moveToElementText( parent );
              testRange.setEndPoint( 'StartToStart', range );
              distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
              siblings = parent.childNodes;
              if ( !distance ) {
                  child = siblings[siblings.length - 1];
                  return  {container:child, offset:child.nodeValue.length};
              }
  
              var i = siblings.length;
              while ( distance > 0 ){
                  distance -= siblings[ --i ].nodeValue.length;
              }
              return {container:siblings[i], offset:-distance};
          }
          testRange.collapse( position > 0 );
          testRange.setEndPoint( position > 0 ? 'StartToStart' : 'EndToStart', range );
          distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length;
          if ( !distance ) {
              return  dtd.$empty[child.tagName] || dtd.$nonChild[child.tagName] ?
              {container:parent, offset:getIndex( child ) + (position > 0 ? 0 : 1)} :
              {container:child, offset:position > 0 ? 0 : child.childNodes.length}
          }
          while ( distance > 0 ) {
              try {
                  var pre = child;
                  child = child[position > 0 ? 'previousSibling' : 'nextSibling'];
                  distance -= child.nodeValue.length;
              } catch ( e ) {
                  return {container:parent, offset:getIndex( pre )};
              }
          }
          return  {container:child, offset:position > 0 ? -distance : child.nodeValue.length + distance}
      }
  
      /**
       * 将ieRange转换为Range对象
       * @param {Range}   ieRange    ieRange对象
       * @param {Range}   range      Range对象
       * @return  {Range}  range       返回转换后的Range对象
       */
      function transformIERangeToRange( ieRange, range ) {
          if ( ieRange.item ) {
              range.selectNode( ieRange.item( 0 ) );
          } else {
              var bi = getBoundaryInformation( ieRange, true );
              range.setStart( bi.container, bi.offset );
              if ( ieRange.compareEndPoints( 'StartToEnd', ieRange ) != 0 ) {
                  bi = getBoundaryInformation( ieRange, false );
                  range.setEnd( bi.container, bi.offset );
              }
          }
          return range;
      }
  
      /**
       * 获得ieRange
       * @param {Selection} sel    Selection对象
       * @return {ieRange}    得到ieRange
       */
      function _getIERange( sel,txtRange ) {
          var ieRange;
          //ie下有可能报错
          try {
              ieRange = sel.getNative(txtRange).createRange();
          } catch ( e ) {
              return null;
          }
          var el = ieRange.item ? ieRange.item( 0 ) : ieRange.parentElement();
          if ( ( el.ownerDocument || el ) === sel.document ) {
              return ieRange;
          }
          return null;
      }
  
      var Selection = dom.Selection = function ( doc,body ) {
          var me = this;
          me.document = doc;
          me.body = body;
          if ( browser.ie9below ) {
              $( body).on('beforedeactivate', function () {
                  me._bakIERange = me.getIERange();
              } ).on('activate', function () {
                  try {
                      var ieNativRng =  _getIERange( me );
                      if ( (!ieNativRng || !me.rangeInBody(ieNativRng)) && me._bakIERange ) {
                          me._bakIERange.select();
                      }
                  } catch ( ex ) {
                  }
                  me._bakIERange = null;
              } );
          }
      };
  
      Selection.prototype = {
          hasNativeRange : function(){
              var rng;
              if(!browser.ie || browser.ie9above){
                  var nativeSel = this.getNative();
                  if(!nativeSel.rangeCount){
                      return false;
                  }
                  rng = nativeSel.getRangeAt(0);
              }else{
                  rng = _getIERange(this);
              }
              return this.rangeInBody(rng);
          },
          /**
           * 获取原生seleciton对象
           * @public
           * @function
           * @name    UM.dom.Selection.getNative
           * @return {Selection}    获得selection对象
           */
          getNative:function (txtRange) {
              var doc = this.document;
              try {
                  return !doc ? null : browser.ie9below || txtRange? doc.selection : domUtils.getWindow( doc ).getSelection();
              } catch ( e ) {
                  return null;
              }
          },
          /**
           * 获得ieRange
           * @public
           * @function
           * @name    UM.dom.Selection.getIERange
           * @return {ieRange}    返回ie原生的Range
           */
          getIERange:function (txtRange) {
              var ieRange = _getIERange( this,txtRange );
              if ( !ieRange  || !this.rangeInBody(ieRange,txtRange)) {
                  if ( this._bakIERange ) {
                      return this._bakIERange;
                  }
              }
              return ieRange;
          },
          rangeInBody : function(rng,txtRange){
              var node = browser.ie9below || txtRange ? rng.item ? rng.item() : rng.parentElement() : rng.startContainer;
  
              return node === this.body || domUtils.inDoc(node,this.body);
          },
          /**
           * 缓存当前选区的range和选区的开始节点
           * @public
           * @function
           * @name    UM.dom.Selection.cache
           */
          cache:function () {
              this.clear();
              this._cachedRange = this.getRange();
              this._cachedStartElement = this.getStart();
              this._cachedStartElementPath = this.getStartElementPath();
          },
  
          getStartElementPath:function () {
              if ( this._cachedStartElementPath ) {
                  return this._cachedStartElementPath;
              }
              var start = this.getStart();
              if ( start ) {
                  return domUtils.findParents( start, true, null, true )
              }
              return [];
          },
          /**
           * 清空缓存
           * @public
           * @function
           * @name    UM.dom.Selection.clear
           */
          clear:function () {
              this._cachedStartElementPath = this._cachedRange = this._cachedStartElement = null;
          },
          /**
           * 编辑器是否得到了选区
           */
          isFocus:function () {
              return this.hasNativeRange()
  
          },
          /**
           * 获取选区对应的Range
           * @public
           * @function
           * @name    UM.dom.Selection.getRange
           * @returns {UM.dom.Range}    得到Range对象
           */
          getRange:function () {
              var me = this;
              function optimze( range ) {
                  var child = me.body.firstChild,
                      collapsed = range.collapsed;
                  while ( child && child.firstChild ) {
                      range.setStart( child, 0 );
                      child = child.firstChild;
                  }
                  if ( !range.startContainer ) {
                      range.setStart( me.body, 0 )
                  }
                  if ( collapsed ) {
                      range.collapse( true );
                  }
              }
  
              if ( me._cachedRange != null ) {
                  return this._cachedRange;
              }
              var range = new dom.Range( me.document,me.body );
              if ( browser.ie9below ) {
                  var nativeRange = me.getIERange();
                  if ( nativeRange  && this.rangeInBody(nativeRange)) {
  
                      try{
                          transformIERangeToRange( nativeRange, range );
                      }catch(e){
                          optimze( range );
                      }
  
                  } else {
                      optimze( range );
                  }
              } else {
                  var sel = me.getNative();
                  if ( sel && sel.rangeCount && me.rangeInBody(sel.getRangeAt( 0 ))) {
                      var firstRange = sel.getRangeAt( 0 );
                      var lastRange = sel.getRangeAt( sel.rangeCount - 1 );
                      range.setStart( firstRange.startContainer, firstRange.startOffset ).setEnd( lastRange.endContainer, lastRange.endOffset );
                      if ( range.collapsed && domUtils.isBody( range.startContainer ) && !range.startOffset ) {
                          optimze( range );
                      }
                  } else {
                      //trace:1734 有可能已经不在dom树上了，标识的节点
                      if ( this._bakRange && (this._bakRange.startContainer === this.body || domUtils.inDoc( this._bakRange.startContainer, this.body )) ){
                          return this._bakRange;
                      }
                      optimze( range );
                  }
              }
  
              return this._bakRange = range;
          },
  
          /**
           * 获取开始元素，用于状态反射
           * @public
           * @function
           * @name    UM.dom.Selection.getStart
           * @return {Element}     获得开始元素
           */
          getStart:function () {
              if ( this._cachedStartElement ) {
                  return this._cachedStartElement;
              }
              var range = browser.ie9below ? this.getIERange() : this.getRange(),
                  tmpRange,
                  start, tmp, parent;
              if ( browser.ie9below ) {
                  if ( !range ) {
                      //todo 给第一个值可能会有问题
                      return this.document.body.firstChild;
                  }
                  //control元素
                  if ( range.item ){
                      return range.item( 0 );
                  }
                  tmpRange = range.duplicate();
                  //修正ie下<b>x</b>[xx] 闭合后 <b>x|</b>xx
                  tmpRange.text.length > 0 && tmpRange.moveStart( 'character', 1 );
                  tmpRange.collapse( 1 );
                  start = tmpRange.parentElement();
                  parent = tmp = range.parentElement();
                  while ( tmp = tmp.parentNode ) {
                      if ( tmp == start ) {
                          start = parent;
                          break;
                      }
                  }
              } else {
                  start = range.startContainer;
                  if ( start.nodeType == 1 && start.hasChildNodes() ){
                      start = start.childNodes[Math.min( start.childNodes.length - 1, range.startOffset )];
                  }
                  if ( start.nodeType == 3 ){
                      return start.parentNode;
                  }
              }
              return start;
          },
          /**
           * 得到选区中的文本
           * @public
           * @function
           * @name    UM.dom.Selection.getText
           * @return  {String}    选区中包含的文本
           */
          getText:function () {
              var nativeSel, nativeRange;
              if ( this.isFocus() && (nativeSel = this.getNative()) ) {
                  nativeRange = browser.ie9below ? nativeSel.createRange() : nativeSel.getRangeAt( 0 );
                  return browser.ie9below ? nativeRange.text : nativeRange.toString();
              }
              return '';
          }
      };
  })();
  /**
   * @file
   * @name UM.Editor
   * @short Editor
   * @import editor.js,core/utils.js,core/EventBase.js,core/browser.js,core/dom/dtd.js,core/dom/domUtils.js,core/dom/Range.js,core/dom/Selection.js,plugins/serialize.js
   * @desc 编辑器主类，包含编辑器提供的大部分公用接口
   */
  (function () {
      var uid = 0, _selectionChangeTimer;
  
      /**
       * @private
       * @ignore
       * @param form  编辑器所在的form元素
       * @param editor  编辑器实例对象
       */
      function setValue(form, editor) {
          var textarea;
          if (editor.textarea) {
              if (utils.isString(editor.textarea)) {
                  for (var i = 0, ti, tis = domUtils.getElementsByTagName(form, 'textarea'); ti = tis[i++];) {
                      if (ti.id == 'umeditor_textarea_' + editor.options.textarea) {
                          textarea = ti;
                          break;
                      }
                  }
              } else {
                  textarea = editor.textarea;
              }
          }
          if (!textarea) {
              form.appendChild(textarea = domUtils.createElement(document, 'textarea', {
                  'name': editor.options.textarea,
                  'id': 'umeditor_textarea_' + editor.options.textarea,
                  'style': "display:none"
              }));
              //不要产生多个textarea
              editor.textarea = textarea;
          }
          textarea.value = editor.hasContents() ?
              (editor.options.allHtmlEnabled ? editor.getAllHtml() : editor.getContent(null, null, true)) :
              ''
      }
      function loadPlugins(me){
          //初始化插件
          for (var pi in UM.plugins) {
              if(me.options.excludePlugins.indexOf(pi) == -1){
                  UM.plugins[pi].call(me);
                  me.plugins[pi] = 1;
              }
          }
          me.langIsReady = true;
  
          me.fireEvent("langReady");
      }
      function checkCurLang(I18N){
          for(var lang in I18N){
              return lang
          }
      }
      /**
       * UEditor编辑器类
       * @name Editor
       * @desc 创建一个跟编辑器实例
       * - ***container*** 编辑器容器对象
       * - ***iframe*** 编辑区域所在的iframe对象
       * - ***window*** 编辑区域所在的window
       * - ***document*** 编辑区域所在的document对象
       * - ***body*** 编辑区域所在的body对象
       * - ***selection*** 编辑区域的选区对象
       */
      var Editor = UM.Editor = function (options) {
          var me = this;
          me.uid = uid++;
          EventBase.call(me);
          me.commands = {};
          me.options = utils.extend(utils.clone(options || {}), UMEDITOR_CONFIG, true);
          me.shortcutkeys = {};
          me.inputRules = [];
          me.outputRules = [];
          //设置默认的常用属性
          me.setOpt({
              isShow: true,
              initialContent: '',
              initialStyle:'',
              autoClearinitialContent: false,
              textarea: 'editorValue',
              focus: false,
              focusInEnd: true,
              autoClearEmptyNode: true,
              fullscreen: false,
              readonly: false,
              zIndex: 999,
              enterTag: 'p',
              lang: 'zh-cn',
              langPath: me.options.UMEDITOR_HOME_URL + 'lang/',
              theme: 'default',
              themePath: me.options.UMEDITOR_HOME_URL + 'themes/',
              allHtmlEnabled: false,
              autoSyncData : true,
              autoHeightEnabled : true,
              excludePlugins:''
          });
          me.plugins = {};
          if(!utils.isEmptyObject(UM.I18N)){
              //修改默认的语言类型
              me.options.lang = checkCurLang(UM.I18N);
              loadPlugins(me)
          }else{
              utils.loadFile(document, {
                  src: me.options.langPath + me.options.lang + "/" + me.options.lang + ".js",
                  tag: "script",
                  type: "text/javascript",
                  defer: "defer"
              }, function () {
                  loadPlugins(me)
              });
          }
  
      };
      Editor.prototype = {
          /**
           * 当编辑器ready后执行传入的fn,如果编辑器已经完成ready，就马上执行fn，fn的中的this是编辑器实例。
           * 大部分的实例接口都需要放在该方法内部执行，否则在IE下可能会报错。
           * @name ready
           * @grammar editor.ready(fn) fn是当编辑器渲染好后执行的function
           * @example
           * var editor = new UM.ui.Editor();
           * editor.render("myEditor");
           * editor.ready(function(){
           *     editor.setContent("欢迎使用UEditor！");
           * })
           */
          ready: function (fn) {
              var me = this;
              if (fn) {
                  me.isReady ? fn.apply(me) : me.addListener('ready', fn);
              }
          },
          /**
           * 为编辑器设置默认参数值。若用户配置为空，则以默认配置为准
           * @grammar editor.setOpt(key,value);      //传入一个键、值对
           * @grammar editor.setOpt({ key:value});   //传入一个json对象
           */
          setOpt: function (key, val) {
              var obj = {};
              if (utils.isString(key)) {
                  obj[key] = val
              } else {
                  obj = key;
              }
              utils.extend(this.options, obj, true);
          },
          getOpt:function(key){
              return this.options[key] || ''
          },
          /**
           * 销毁编辑器实例对象
           * @name destroy
           * @grammar editor.destroy();
           */
          destroy: function () {
  
              var me = this;
              me.fireEvent('destroy');
              var container = me.container.parentNode;
              if(container === document.body){
                  container = me.container;
              }
              var textarea = me.textarea;
              if (!textarea) {
                  textarea = document.createElement('textarea');
                  container.parentNode.insertBefore(textarea, container);
              } else {
                  textarea.style.display = ''
              }
  
              textarea.style.width = me.body.offsetWidth + 'px';
              textarea.style.height = me.body.offsetHeight + 'px';
              textarea.value = me.getContent();
              textarea.id = me.key;
              if(container.contains(textarea)){
                  $(textarea).insertBefore(container);
              }
              container.innerHTML = '';
  
              domUtils.remove(container);
              UM.clearCache(me.id);
              //trace:2004
              for (var p in me) {
                  if (me.hasOwnProperty(p)) {
                      delete this[p];
                  }
              }
  
          },
          initialCont : function(holder){
  
              if(holder){
                  holder.getAttribute('name') && ( this.options.textarea = holder.getAttribute('name'));
                  if (holder && /script|textarea/ig.test(holder.tagName)) {
                      var newDiv = document.createElement('div');
                      holder.parentNode.insertBefore(newDiv, holder);
                      this.options.initialContent = UM.htmlparser(holder.value || holder.innerHTML|| this.options.initialContent).toHtml();
                      holder.className && (newDiv.className = holder.className);
                      holder.style.cssText && (newDiv.style.cssText = holder.style.cssText);
  
                      if (/textarea/i.test(holder.tagName)) {
                          this.textarea = holder;
                          this.textarea.style.display = 'none';
  
                      } else {
                          holder.parentNode.removeChild(holder);
                          holder.id && (newDiv.id = holder.id);
                      }
                      holder = newDiv;
                      holder.innerHTML = '';
                  }
                  return holder;
              }else{
                  return null;
              }
  
          },
          /**
           * 渲染编辑器的DOM到指定容器，必须且只能调用一次
           * @name render
           * @grammar editor.render(containerId);    //可以指定一个容器ID
           * @grammar editor.render(containerDom);   //也可以直接指定容器对象
           */
          render: function (container) {
              var me = this,
                  options = me.options,
                  getStyleValue=function(attr){
                      return parseInt($(container).css(attr));
                  };
  
              if (utils.isString(container)) {
                  container = document.getElementById(container);
              }
              if (container) {
                  this.id = container.getAttribute('id');
                  UM.setEditor(this);
                  utils.cssRule('edui-style-body',me.options.initialStyle,document);
  
                  container = this.initialCont(container);
  
                  container.className += ' edui-body-container';
  
                  if(options.initialFrameWidth){
                      options.minFrameWidth = options.initialFrameWidth
                  }else{
                      //都没给值，先写死了
                      options.minFrameWidth = options.initialFrameWidth = $(container).width() || UM.defaultWidth;
                  }
                  if(options.initialFrameHeight){
                      options.minFrameHeight = options.initialFrameHeight
                  }else{
  
                      options.initialFrameHeight = options.minFrameHeight = $(container).height() || UM.defaultHeight;
                  }
  
                  container.style.width = /%$/.test(options.initialFrameWidth) ?  '100%' : options.initialFrameWidth -
                      getStyleValue("padding-left")-
                      getStyleValue("padding-right")  +'px';
  
                  var height = /%$/.test(options.initialFrameHeight) ?  '100%' : (options.initialFrameHeight - getStyleValue("padding-top")- getStyleValue("padding-bottom") );
                  if(this.options.autoHeightEnabled){
                      container.style.minHeight = height +'px';
                      container.style.height = '';
                      if(browser.ie && browser.version <= 6){
                          container.style.height = height ;
                          container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
                      }
                  }else{
                      $(container).height(height)
                  }
                  container.style.zIndex = options.zIndex;
                  this._setup(container);
  
              }
          },
          /**
           * 编辑器初始化
           * @private
           * @ignore
           * @param {Element} doc 编辑器Iframe中的文档对象
           */
          _setup: function (cont) {
              var me = this,
                  options = me.options;
  
              cont.contentEditable = true;
              document.body.spellcheck = false;
  
              me.document = document;
              me.window = document.defaultView || document.parentWindow;
              me.body = cont;
              me.$body = $(cont);
              me.selection = new dom.Selection(document,me.body);
              me._isEnabled = false;
              //gecko初始化就能得到range,无法判断isFocus了
              var geckoSel;
              if (browser.gecko && (geckoSel = this.selection.getNative())) {
                  geckoSel.removeAllRanges();
              }
              this._initEvents();
              //为form提交提供一个隐藏的textarea
              for (var form = cont.parentNode; form && !domUtils.isBody(form); form = form.parentNode) {
                  if (form.tagName == 'FORM') {
                      me.form = form;
                      if(me.options.autoSyncData){
                          $(cont).on('blur',function(){
                              setValue(form,me);
                          })
                      }else{
                          $(form).on('submit', function () {
                              setValue(this, me);
                          })
                      }
                      break;
                  }
              }
              if (options.initialContent) {
                  if (options.autoClearinitialContent) {
                      var oldExecCommand = me.execCommand;
                      me.execCommand = function () {
                          me.fireEvent('firstBeforeExecCommand');
                          return oldExecCommand.apply(me, arguments);
                      };
                      this._setDefaultContent(options.initialContent);
                  } else
                      this.setContent(options.initialContent, false, true);
              }
  
              //编辑器不能为空内容
  
              if (domUtils.isEmptyNode(me.body)) {
                  me.body.innerHTML = '<p>' + (browser.ie ? '' : '<br/>') + '</p>';
              }
              //如果要求focus, 就把光标定位到内容开始
              if (options.focus) {
                  setTimeout(function () {
                      me.focus(me.options.focusInEnd);
                      //如果自动清除开着，就不需要做selectionchange;
                      !me.options.autoClearinitialContent && me._selectionChange();
                  }, 0);
              }
              if (!me.container) {
                  me.container = cont.parentNode;
              }
  
              me._bindshortcutKeys();
              me.isReady = 1;
              me.fireEvent('ready');
              options.onready && options.onready.call(me);
              if(!browser.ie || browser.ie9above){
  
                  $(me.body).on( 'blur focus', function (e) {
                      var nSel = me.selection.getNative();
                      //chrome下会出现alt+tab切换时，导致选区位置不对
                      if (e.type == 'blur') {
                          if(nSel.rangeCount > 0 ){
                              me._bakRange = nSel.getRangeAt(0);
                          }
                      } else {
                          try {
                              me._bakRange && nSel.addRange(me._bakRange)
                          } catch (e) {
                          }
                          me._bakRange = null;
                      }
                  });
              }
  
              !options.isShow && me.setHide();
              options.readonly && me.setDisabled();
          },
          /**
           * 同步编辑器的数据，为提交数据做准备，主要用于你是手动提交的情况
           * @name sync
           * @grammar editor.sync(); //从编辑器的容器向上查找，如果找到就同步数据
           * @grammar editor.sync(formID); //formID制定一个要同步数据的form的id,编辑器的数据会同步到你指定form下
           * @desc
           * 后台取得数据得键值使用你容器上得''name''属性，如果没有就使用参数传入的''textarea''
           * @example
           * editor.sync();
           * form.sumbit(); //form变量已经指向了form元素
           *
           */
          sync: function (formId) {
              var me = this,
                  form = formId ? document.getElementById(formId) :
                      domUtils.findParent(me.body.parentNode, function (node) {
                          return node.tagName == 'FORM'
                      }, true);
              form && setValue(form, me);
          },
          /**
           * 设置编辑器高度
           * @name setHeight
           * @grammar editor.setHeight(number);  //纯数值，不带单位
           */
          setHeight: function (height,notSetHeight) {
              !notSetHeight && (this.options.initialFrameHeight = height);
              if(this.options.autoHeightEnabled){
                  $(this.body).css({
                      'min-height':height + 'px'
                  });
                  if(browser.ie && browser.version <= 6 && this.container){
                      this.container.style.height = height ;
                      this.container.style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
                  }
              }else{
                  $(this.body).height(height)
              }
              this.fireEvent('resize');
          },
          /**
           * 设置编辑器宽度
           * @name setWidth
           * @grammar editor.setWidth(number);  //纯数值，不带单位
           */
          setWidth:function(width){
              this.$container && this.$container.width(width);
              $(this.body).width(width - $(this.body).css('padding-left').replace('px','') * 1 - $(this.body).css('padding-right').replace('px','') * 1);
              this.fireEvent('resize');
          },
          addshortcutkey: function (cmd, keys) {
              var obj = {};
              if (keys) {
                  obj[cmd] = keys
              } else {
                  obj = cmd;
              }
              utils.extend(this.shortcutkeys, obj)
          },
          _bindshortcutKeys: function () {
              var me = this, shortcutkeys = this.shortcutkeys;
              me.addListener('keydown', function (type, e) {
                  var keyCode = e.keyCode || e.which;
                  for (var i in shortcutkeys) {
                      var tmp = shortcutkeys[i].split(',');
                      for (var t = 0, ti; ti = tmp[t++];) {
                          ti = ti.split(':');
                          var key = ti[0], param = ti[1];
                          if (/^(ctrl)(\+shift)?\+(\d+)$/.test(key.toLowerCase()) || /^(\d+)$/.test(key)) {
                              if (( (RegExp.$1 == 'ctrl' ? (e.ctrlKey || e.metaKey) : 0)
                                  && (RegExp.$2 != "" ? e[RegExp.$2.slice(1) + "Key"] : 1)
                                  && keyCode == RegExp.$3
                                  ) ||
                                  keyCode == RegExp.$1
                                  ) {
                                  if (me.queryCommandState(i,param) != -1)
                                      me.execCommand(i, param);
                                  domUtils.preventDefault(e);
                              }
                          }
                      }
  
                  }
              });
          },
          /**
           * 获取编辑器内容
           * @name getContent
           * @grammar editor.getContent()  => String //若编辑器中只包含字符"&lt;p&gt;&lt;br /&gt;&lt;/p/&gt;"会返回空。
           * @grammar editor.getContent(fn)  => String
           * @example
           * getContent默认是会现调用hasContents来判断编辑器是否为空，如果是，就直接返回空字符串
           * 你也可以传入一个fn来接替hasContents的工作，定制判断的规则
           * editor.getContent(function(){
           *     return false //编辑器没有内容 ，getContent直接返回空
           * })
           */
          getContent: function (cmd, fn,notSetCursor,ignoreBlank,formatter) {
              var me = this;
              if (cmd && utils.isFunction(cmd)) {
                  fn = cmd;
                  cmd = '';
              }
              if (fn ? !fn() : !this.hasContents()) {
                  return '';
              }
              me.fireEvent('beforegetcontent');
              var root = UM.htmlparser(me.body.innerHTML,ignoreBlank);
              me.filterOutputRule(root);
              me.fireEvent('aftergetcontent',root);
              return  root.toHtml(formatter);
          },
          /**
           * 取得完整的html代码，可以直接显示成完整的html文档
           * @name getAllHtml
           * @grammar editor.getAllHtml()  => String
           */
          getAllHtml: function () {
              var me = this,
                  headHtml = [],
                  html = '';
              me.fireEvent('getAllHtml', headHtml);
              if (browser.ie && browser.version > 8) {
                  var headHtmlForIE9 = '';
                  utils.each(me.document.styleSheets, function (si) {
                      headHtmlForIE9 += ( si.href ? '<link rel="stylesheet" type="text/css" href="' + si.href + '" />' : '<style>' + si.cssText + '</style>');
                  });
                  utils.each(me.document.getElementsByTagName('script'), function (si) {
                      headHtmlForIE9 += si.outerHTML;
                  });
              }
              return '<html><head>' + (me.options.charset ? '<meta http-equiv="Content-Type" content="text/html; charset=' + me.options.charset + '"/>' : '')
                  + (headHtmlForIE9 || me.document.getElementsByTagName('head')[0].innerHTML) + headHtml.join('\n') + '</head>'
                  + '<body ' + (ie && browser.version < 9 ? 'class="view"' : '') + '>' + me.getContent(null, null, true) + '</body></html>';
          },
          /**
           * 得到编辑器的纯文本内容，但会保留段落格式
           * @name getPlainTxt
           * @grammar editor.getPlainTxt()  => String
           */
          getPlainTxt: function () {
              var reg = new RegExp(domUtils.fillChar, 'g'),
                  html = this.body.innerHTML.replace(/[\n\r]/g, '');//ie要先去了\n在处理
              html = html.replace(/<(p|div)[^>]*>(<br\/?>|&nbsp;)<\/\1>/gi, '\n')
                  .replace(/<br\/?>/gi, '\n')
                  .replace(/<[^>/]+>/g, '')
                  .replace(/(\n)?<\/([^>]+)>/g, function (a, b, c) {
                      return dtd.$block[c] ? '\n' : b ? b : '';
                  });
              //取出来的空格会有c2a0会变成乱码，处理这种情况\u00a0
              return html.replace(reg, '').replace(/\u00a0/g, ' ').replace(/&nbsp;/g, ' ');
          },
  
          /**
           * 获取编辑器中的纯文本内容,没有段落格式
           * @name getContentTxt
           * @grammar editor.getContentTxt()  => String
           */
          getContentTxt: function () {
              var reg = new RegExp(domUtils.fillChar, 'g');
              //取出来的空格会有c2a0会变成乱码，处理这种情况\u00a0
              return this.body[browser.ie ? 'innerText' : 'textContent'].replace(reg, '').replace(/\u00a0/g, ' ');
          },
  
          /**
           * 将html设置到编辑器中, 如果是用于初始化时给编辑器赋初值，则必须放在ready方法内部执行
           * @name setContent
           * @grammar editor.setContent(html)
           * @example
           * var editor = new UM.ui.Editor()
           * editor.ready(function(){
           *     //需要ready后执行，否则可能报错
           *     editor.setContent("欢迎使用UEditor！");
           * })
           */
          setContent: function (html, isAppendTo, notFireSelectionchange) {
              var me = this;
  
              me.fireEvent('beforesetcontent', html);
              var root = UM.htmlparser(html);
              me.filterInputRule(root);
              html = root.toHtml();
  
  
              me.body.innerHTML = (isAppendTo ? me.body.innerHTML : '') + html;
  
  
              function isCdataDiv(node){
                  return  node.tagName == 'DIV' && node.getAttribute('cdata_tag');
              }
              //给文本或者inline节点套p标签
              if (me.options.enterTag == 'p') {
  
                  var child = this.body.firstChild, tmpNode;
                  if (!child || child.nodeType == 1 &&
                      (dtd.$cdata[child.tagName] || isCdataDiv(child) ||
                          domUtils.isCustomeNode(child)
                          )
                      && child === this.body.lastChild) {
                      this.body.innerHTML = '<p>' + (browser.ie ? '&nbsp;' : '<br/>') + '</p>' + this.body.innerHTML;
  
                  } else {
                      var p = me.document.createElement('p');
                      while (child) {
                          while (child && (child.nodeType == 3 || child.nodeType == 1 && dtd.p[child.tagName] && !dtd.$cdata[child.tagName])) {
                              tmpNode = child.nextSibling;
                              p.appendChild(child);
                              child = tmpNode;
                          }
                          if (p.firstChild) {
                              if (!child) {
                                  me.body.appendChild(p);
                                  break;
                              } else {
                                  child.parentNode.insertBefore(p, child);
                                  p = me.document.createElement('p');
                              }
                          }
                          child = child.nextSibling;
                      }
                  }
              }
              me.fireEvent('aftersetcontent');
              me.fireEvent('contentchange');
  
              !notFireSelectionchange && me._selectionChange();
              //清除保存的选区
              me._bakRange = me._bakIERange = me._bakNativeRange = null;
              //trace:1742 setContent后gecko能得到焦点问题
              var geckoSel;
              if (browser.gecko && (geckoSel = this.selection.getNative())) {
                  geckoSel.removeAllRanges();
              }
              if(me.options.autoSyncData){
                  me.form && setValue(me.form,me);
              }
          },
  
          /**
           * 让编辑器获得焦点，toEnd确定focus位置
           * @name focus
           * @grammar editor.focus([toEnd])   //默认focus到编辑器头部，toEnd为true时focus到内容尾部
           */
          focus: function (toEnd) {
              try {
                  var me = this,
                      rng = me.selection.getRange();
                  if (toEnd) {
                      rng.setStartAtLast(me.body.lastChild).setCursor(false, true);
                  } else {
                      rng.select(true);
                  }
                  this.fireEvent('focus');
              } catch (e) {
              }
          },
          /**
           * 使编辑区域失去焦点
           */
          blur:function(){
              var sel = this.selection.getNative();
              sel.empty ? sel.empty() : sel.removeAllRanges();
              this.fireEvent('blur')
          },
          /**
           * 判断编辑器当前是否获得了焦点
           */
          isFocus : function(){
              if(this.fireEvent('isfocus')===true){
                  return true;
              }
              return this.selection.isFocus();
          },
  
          /**
           * 初始化UE事件及部分事件代理
           * @private
           * @ignore
           */
          _initEvents: function () {
              var me = this,
                  cont = me.body,
                  _proxyDomEvent = function(){
                      me._proxyDomEvent.apply(me, arguments);
                  };
  
              $(cont)
                  .on( 'click contextmenu mousedown keydown keyup keypress mouseup mouseover mouseout selectstart', _proxyDomEvent)
                  .on( 'focus blur', _proxyDomEvent)
                  .on('mouseup keydown', function (evt) {
                      //特殊键不触发selectionchange
                      if (evt.type == 'keydown' && (evt.ctrlKey || evt.metaKey || evt.shiftKey || evt.altKey)) {
                          return;
                      }
                      if (evt.button == 2)return;
                      me._selectionChange(250, evt);
                  });
          },
          /**
           * 触发事件代理
           * @private
           * @ignore
           */
          _proxyDomEvent: function (evt) {
              return this.fireEvent(evt.type.replace(/^on/, ''), evt);
          },
          /**
           * 变化选区
           * @private
           * @ignore
           */
          _selectionChange: function (delay, evt) {
              var me = this;
              //有光标才做selectionchange 为了解决未focus时点击source不能触发更改工具栏状态的问题（source命令notNeedUndo=1）
  //            if ( !me.selection.isFocus() ){
  //                return;
  //            }
  
  
              var hackForMouseUp = false;
              var mouseX, mouseY;
              if (browser.ie && browser.version < 9 && evt && evt.type == 'mouseup') {
                  var range = this.selection.getRange();
                  if (!range.collapsed) {
                      hackForMouseUp = true;
                      mouseX = evt.clientX;
                      mouseY = evt.clientY;
                  }
              }
              clearTimeout(_selectionChangeTimer);
              _selectionChangeTimer = setTimeout(function () {
                  if (!me.selection.getNative()) {
                      return;
                  }
                  //修复一个IE下的bug: 鼠标点击一段已选择的文本中间时，可能在mouseup后的一段时间内取到的range是在selection的type为None下的错误值.
                  //IE下如果用户是拖拽一段已选择文本，则不会触发mouseup事件，所以这里的特殊处理不会对其有影响
                  var ieRange;
                  if (hackForMouseUp && me.selection.getNative().type == 'None') {
                      ieRange = me.document.body.createTextRange();
                      try {
                          ieRange.moveToPoint(mouseX, mouseY);
                      } catch (ex) {
                          ieRange = null;
                      }
                  }
                  var bakGetIERange;
                  if (ieRange) {
                      bakGetIERange = me.selection.getIERange;
                      me.selection.getIERange = function () {
                          return ieRange;
                      };
                  }
                  me.selection.cache();
                  if (bakGetIERange) {
                      me.selection.getIERange = bakGetIERange;
                  }
                  if (me.selection._cachedRange && me.selection._cachedStartElement) {
                      me.fireEvent('beforeselectionchange');
                      // 第二个参数causeByUi为true代表由用户交互造成的selectionchange.
                      me.fireEvent('selectionchange', !!evt);
                      me.fireEvent('afterselectionchange');
                      me.selection.clear();
                  }
              }, delay || 50);
          },
          _callCmdFn: function (fnName, args) {
              args = Array.prototype.slice.call(args,0);
              var cmdName = args.shift().toLowerCase(),
                  cmd, cmdFn;
              cmd = this.commands[cmdName] || UM.commands[cmdName];
              cmdFn = cmd && cmd[fnName];
              //没有querycommandstate或者没有command的都默认返回0
              if ((!cmd || !cmdFn) && fnName == 'queryCommandState') {
                  return 0;
              } else if (cmdFn) {
                  return cmdFn.apply(this, [cmdName].concat(args));
              }
          },
  
          /**
           * 执行编辑命令cmdName，完成富文本编辑效果
           * @name execCommand
           * @grammar editor.execCommand(cmdName)   => {*}
           */
          execCommand: function (cmdName) {
              if(!this.isFocus()){
                  var bakRange = this.selection._bakRange;
                  if(bakRange){
                      bakRange.select()
                  }else{
                      this.focus(true)
                  }
  
              }
              cmdName = cmdName.toLowerCase();
              var me = this,
                  result,
                  cmd = me.commands[cmdName] || UM.commands[cmdName];
              if (!cmd || !cmd.execCommand) {
                  return null;
              }
              if (!cmd.notNeedUndo && !me.__hasEnterExecCommand) {
                  me.__hasEnterExecCommand = true;
                  if (me.queryCommandState.apply(me,arguments) != -1) {
                      me.fireEvent('saveScene');
                      me.fireEvent('beforeexeccommand', cmdName);
                      result = this._callCmdFn('execCommand', arguments);
                      (!cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange');
                      me.fireEvent('afterexeccommand', cmdName);
                      me.fireEvent('saveScene');
                  }
                  me.__hasEnterExecCommand = false;
              } else {
                  result = this._callCmdFn('execCommand', arguments);
                  (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me.fireEvent('contentchange')
              }
              (!me.__hasEnterExecCommand && !cmd.ignoreContentChange && !me._ignoreContentChange) && me._selectionChange();
              return result;
          },
          /**
           * 根据传入的command命令，查选编辑器当前的选区，返回命令的状态
           * @name  queryCommandState
           * @grammar editor.queryCommandState(cmdName)  => (-1|0|1)
           * @desc
           * * ''-1'' 当前命令不可用
           * * ''0'' 当前命令可用
           * * ''1'' 当前命令已经执行过了
           */
          queryCommandState: function (cmdName) {
              try{
                  return this._callCmdFn('queryCommandState', arguments);
              }catch(e){
                  return 0
              }
  
          },
  
          /**
           * 根据传入的command命令，查选编辑器当前的选区，根据命令返回相关的值
           * @name  queryCommandValue
           * @grammar editor.queryCommandValue(cmdName)  =>  {*}
           */
          queryCommandValue: function (cmdName) {
              try{
                  return this._callCmdFn('queryCommandValue', arguments);
              }catch(e){
                  return null
              }
          },
          /**
           * 检查编辑区域中是否有内容，若包含tags中的节点类型，直接返回true
           * @name  hasContents
           * @desc
           * 默认有文本内容，或者有以下节点都不认为是空
           * <code>{table:1,ul:1,ol:1,dl:1,iframe:1,area:1,base:1,col:1,hr:1,img:1,embed:1,input:1,link:1,meta:1,param:1}</code>
           * @grammar editor.hasContents()  => (true|false)
           * @grammar editor.hasContents(tags)  =>  (true|false)  //若文档中包含tags数组里对应的tag，直接返回true
           * @example
           * editor.hasContents(['span']) //如果编辑器里有这些，不认为是空
           */
          hasContents: function (tags) {
              if (tags) {
                  for (var i = 0, ci; ci = tags[i++];) {
                      if (this.body.getElementsByTagName(ci).length > 0) {
                          return true;
                      }
                  }
              }
              if (!domUtils.isEmptyBlock(this.body)) {
                  return true
              }
              //随时添加,定义的特殊标签如果存在，不能认为是空
              tags = ['div'];
              for (i = 0; ci = tags[i++];) {
                  var nodes = domUtils.getElementsByTagName(this.body, ci);
                  for (var n = 0, cn; cn = nodes[n++];) {
                      if (domUtils.isCustomeNode(cn)) {
                          return true;
                      }
                  }
              }
              return false;
          },
          /**
           * 重置编辑器，可用来做多个tab使用同一个编辑器实例
           * @name  reset
           * @desc
           * * 清空编辑器内容
           * * 清空回退列表
           * @grammar editor.reset()
           */
          reset: function () {
              this.fireEvent('reset');
          },
          isEnabled: function(){
              return this._isEnabled != true;
          },
  
          setEnabled: function () {
              var me = this, range;
  
              me.body.contentEditable = true;
  
              /* 恢复选区 */
              if (me.lastBk) {
                  range = me.selection.getRange();
                  try {
                      range.moveToBookmark(me.lastBk);
                      delete me.lastBk
                  } catch (e) {
                      range.setStartAtFirst(me.body).collapse(true)
                  }
                  range.select(true);
              }
  
              /* 恢复query函数 */
              if (me.bkqueryCommandState) {
                  me.queryCommandState = me.bkqueryCommandState;
                  delete me.bkqueryCommandState;
              }
  
              /* 恢复原生事件 */
              if (me._bkproxyDomEvent) {
                  me._proxyDomEvent = me._bkproxyDomEvent;
                  delete me._bkproxyDomEvent;
              }
  
              /* 触发事件 */
              me.fireEvent('setEnabled');
          },
          /**
           * 设置当前编辑区域可以编辑
           * @name enable
           * @grammar editor.enable()
           */
          enable: function () {
              return this.setEnabled();
          },
          setDisabled: function (except, keepDomEvent) {
              var me = this;
  
              me.body.contentEditable = false;
              me._except = except ? utils.isArray(except) ? except : [except] : [];
  
              /* 备份最后的选区 */
              if (!me.lastBk) {
                  me.lastBk = me.selection.getRange().createBookmark(true);
              }
  
              /* 备份并重置query函数 */
              if(!me.bkqueryCommandState) {
                  me.bkqueryCommandState = me.queryCommandState;
                  me.queryCommandState = function (type) {
                      if (utils.indexOf(me._except, type) != -1) {
                          return me.bkqueryCommandState.apply(me, arguments);
                      }
                      return -1;
                  };
              }
  
              /* 备份并墙原生事件 */
              if(!keepDomEvent && !me._bkproxyDomEvent) {
                  me._bkproxyDomEvent = me._proxyDomEvent;
                  me._proxyDomEvent = function () {
                      return false;
                  };
              }
  
              /* 触发事件 */
              me.fireEvent('selectionchange');
              me.fireEvent('setDisabled', me._except);
          },
          /** 设置当前编辑区域不可编辑,except中的命令除外
           * @name disable
           * @grammar editor.disable()
           * @grammar editor.disable(except)  //例外的命令，也即即使设置了disable，此处配置的命令仍然可以执行
           * @example
           * //禁用工具栏中除加粗和插入图片之外的所有功能
           * editor.disable(['bold','insertimage']);//可以是单一的String,也可以是Array
           */
          disable: function (except) {
              return this.setDisabled(except);
          },
          /**
           * 设置默认内容
           * @ignore
           * @private
           * @param  {String} cont 要存入的内容
           */
          _setDefaultContent: function () {
              function clear() {
                  var me = this;
                  if (me.document.getElementById('initContent')) {
                      me.body.innerHTML = '<p>' + (ie ? '' : '<br/>') + '</p>';
                      me.removeListener('firstBeforeExecCommand focus', clear);
                      setTimeout(function () {
                          me.focus();
                          me._selectionChange();
                      }, 0)
                  }
              }
  
              return function (cont) {
                  var me = this;
                  me.body.innerHTML = '<p id="initContent">' + cont + '</p>';
  
                  me.addListener('firstBeforeExecCommand focus', clear);
              }
          }(),
          /**
           * show方法的兼容版本
           * @private
           * @ignore
           */
          setShow: function () {
              var me = this, range = me.selection.getRange();
              if (me.container.style.display == 'none') {
                  //有可能内容丢失了
                  try {
                      range.moveToBookmark(me.lastBk);
                      delete me.lastBk
                  } catch (e) {
                      range.setStartAtFirst(me.body).collapse(true)
                  }
                  //ie下focus实效，所以做了个延迟
                  setTimeout(function () {
                      range.select(true);
                  }, 100);
                  me.container.style.display = '';
              }
  
          },
          /**
           * 显示编辑器
           * @name show
           * @grammar editor.show()
           */
          show: function () {
              return this.setShow();
          },
          /**
           * hide方法的兼容版本
           * @private
           * @ignore
           */
          setHide: function () {
              var me = this;
              if (!me.lastBk) {
                  me.lastBk = me.selection.getRange().createBookmark(true);
              }
              me.container.style.display = 'none'
          },
          /**
           * 隐藏编辑器
           * @name hide
           * @grammar editor.hide()
           */
          hide: function () {
              return this.setHide();
          },
          /**
           * 根据制定的路径，获取对应的语言资源
           * @name  getLang
           * @grammar editor.getLang(path)  =>  （JSON|String) 路径根据的是lang目录下的语言文件的路径结构
           * @example
           * editor.getLang('contextMenu.delete') //如果当前是中文，那返回是的是删除
           */
          getLang: function (path) {
              var lang = UM.I18N[this.options.lang];
              if (!lang) {
                  throw Error("not import language file");
              }
              path = (path || "").split(".");
              for (var i = 0, ci; ci = path[i++];) {
                  lang = lang[ci];
                  if (!lang)break;
              }
              return lang;
          },
          /**
           * 计算编辑器当前内容的长度
           * @name  getContentLength
           * @grammar editor.getContentLength(ingoneHtml,tagNames)  =>
           * @example
           * editor.getLang(true)
           */
          getContentLength: function (ingoneHtml, tagNames) {
              var count = this.getContent(false,false,true).length;
              if (ingoneHtml) {
                  tagNames = (tagNames || []).concat([ 'hr', 'img', 'iframe']);
                  count = this.getContentTxt().replace(/[\t\r\n]+/g, '').length;
                  for (var i = 0, ci; ci = tagNames[i++];) {
                      count += this.body.getElementsByTagName(ci).length;
                  }
              }
              return count;
          },
          addInputRule: function (rule,ignoreUndo) {
              rule.ignoreUndo = ignoreUndo;
              this.inputRules.push(rule);
          },
          filterInputRule: function (root,isUndoLoad) {
              for (var i = 0, ci; ci = this.inputRules[i++];) {
                  if(isUndoLoad && ci.ignoreUndo){
                      continue;
                  }
                  ci.call(this, root)
              }
          },
          addOutputRule: function (rule,ignoreUndo) {
              rule.ignoreUndo = ignoreUndo;
              this.outputRules.push(rule)
          },
          filterOutputRule: function (root,isUndoLoad) {
              for (var i = 0, ci; ci = this.outputRules[i++];) {
                  if(isUndoLoad && ci.ignoreUndo){
                      continue;
                  }
                  ci.call(this, root)
              }
          }
      };
      utils.inherits(Editor, EventBase);
  })();
  
  /**
   * @file
   * @name UM.filterWord
   * @short filterWord
   * @desc 用来过滤word粘贴过来的字符串
   * @import editor.js,core/utils.js
   * @anthor zhanyi
   */
  var filterWord = UM.filterWord = function () {
  
      //是否是word过来的内容
      function isWordDocument( str ) {
          return /(class="?Mso|style="[^"]*\bmso\-|w:WordDocument|<(v|o):|lang=)/ig.test( str );
      }
      //去掉小数
      function transUnit( v ) {
          v = v.replace( /[\d.]+\w+/g, function ( m ) {
              return utils.transUnitToPx(m);
          } );
          return v;
      }
  
      function filterPasteWord( str ) {
          return str.replace(/[\t\r\n]+/g,' ')
              .replace( /<!--[\s\S]*?-->/ig, "" )
              //转换图片
              .replace(/<v:shape [^>]*>[\s\S]*?.<\/v:shape>/gi,function(str){
                  //opera能自己解析出image所这里直接返回空
                  if(browser.opera){
                      return '';
                  }
                  try{
                      //有可能是bitmap占为图，无用，直接过滤掉，主要体现在粘贴excel表格中
                      if(/Bitmap/i.test(str)){
                          return '';
                      }
                      var width = str.match(/width:([ \d.]*p[tx])/i)[1],
                          height = str.match(/height:([ \d.]*p[tx])/i)[1],
                          src =  str.match(/src=\s*"([^"]*)"/i)[1];
                      return '<img width="'+ transUnit(width) +'" height="'+transUnit(height) +'" src="' + src + '" />';
                  } catch(e){
                      return '';
                  }
              })
              //针对wps添加的多余标签处理
              .replace(/<\/?div[^>]*>/g,'')
              //去掉多余的属性
              .replace( /v:\w+=(["']?)[^'"]+\1/g, '' )
              .replace( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|xml|meta|link|style|\w+:\w+)(?=[\s\/>]))[^>]*>/gi, "" )
              .replace( /<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>" )
              //去掉多余的属性
              .replace( /\s+(class|lang|align)\s*=\s*(['"]?)([\w-]+)\2/ig, function(str,name,marks,val){
                  //保留list的标示
                  return name == 'class' && val == 'MsoListParagraph' ? str : ''
              })
              //清除多余的font/span不能匹配&nbsp;有可能是空格
              .replace( /<(font|span)[^>]*>(\s*)<\/\1>/gi, function(a,b,c){
                  return c.replace(/[\t\r\n ]+/g,' ')
              })
              //处理style的问题
              .replace( /(<[a-z][^>]*)\sstyle=(["'])([^\2]*?)\2/gi, function( str, tag, tmp, style ) {
                  var n = [],
                      s = style.replace( /^\s+|\s+$/, '' )
                          .replace(/&#39;/g,'\'')
                          .replace( /&quot;/gi, "'" )
                          .split( /;\s*/g );
  
                  for ( var i = 0,v; v = s[i];i++ ) {
  
                      var name, value,
                          parts = v.split( ":" );
  
                      if ( parts.length == 2 ) {
                          name = parts[0].toLowerCase();
                          value = parts[1].toLowerCase();
                          if(/^(background)\w*/.test(name) && value.replace(/(initial|\s)/g,'').length == 0
                              ||
                              /^(margin)\w*/.test(name) && /^0\w+$/.test(value)
                              ){
                              continue;
                          }
  
                          switch ( name ) {
                              case "mso-padding-alt":
                              case "mso-padding-top-alt":
                              case "mso-padding-right-alt":
                              case "mso-padding-bottom-alt":
                              case "mso-padding-left-alt":
                              case "mso-margin-alt":
                              case "mso-margin-top-alt":
                              case "mso-margin-right-alt":
                              case "mso-margin-bottom-alt":
                              case "mso-margin-left-alt":
                              //ie下会出现挤到一起的情况
                              //case "mso-table-layout-alt":
                              case "mso-height":
                              case "mso-width":
                              case "mso-vertical-align-alt":
                                  //trace:1819 ff下会解析出padding在table上
                                  if(!/<table/.test(tag))
                                      n[i] = name.replace( /^mso-|-alt$/g, "" ) + ":" + transUnit( value );
                                  continue;
                              case "horiz-align":
                                  n[i] = "text-align:" + value;
                                  continue;
  
                              case "vert-align":
                                  n[i] = "vertical-align:" + value;
                                  continue;
  
                              case "font-color":
                              case "mso-foreground":
                                  n[i] = "color:" + value;
                                  continue;
  
                              case "mso-background":
                              case "mso-highlight":
                                  n[i] = "background:" + value;
                                  continue;
  
                              case "mso-default-height":
                                  n[i] = "min-height:" + transUnit( value );
                                  continue;
  
                              case "mso-default-width":
                                  n[i] = "min-width:" + transUnit( value );
                                  continue;
  
                              case "mso-padding-between-alt":
                                  n[i] = "border-collapse:separate;border-spacing:" + transUnit( value );
                                  continue;
  
                              case "text-line-through":
                                  if ( (value == "single") || (value == "double") ) {
                                      n[i] = "text-decoration:line-through";
                                  }
                                  continue;
                              case "mso-zero-height":
                                  if ( value == "yes" ) {
                                      n[i] = "display:none";
                                  }
                                  continue;
  //                                case 'background':
  //                                    break;
                              case 'margin':
                                  if ( !/[1-9]/.test( value ) ) {
                                      continue;
                                  }
  
                          }
  
                          if ( /^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?:decor|trans)|top-bar|version|vnd|word-break)/.test( name )
                              ||
                              /text\-indent|padding|margin/.test(name) && /\-[\d.]+/.test(value)
                              ) {
                              continue;
                          }
  
                          n[i] = name + ":" + parts[1];
                      }
                  }
                  return tag + (n.length ? ' style="' + n.join( ';').replace(/;{2,}/g,';') + '"' : '');
              })
              .replace(/[\d.]+(cm|pt)/g,function(str){
                  return utils.transUnitToPx(str)
              })
  
      }
  
      return function ( html ) {
          return (isWordDocument( html ) ? filterPasteWord( html ) : html);
      };
  }();
  ///import editor.js
  ///import core/utils.js
  ///import core/dom/dom.js
  ///import core/dom/dtd.js
  ///import core/htmlparser.js
  //模拟的节点类
  //by zhanyi
  (function () {
      var uNode = UM.uNode = function (obj) {
          this.type = obj.type;
          this.data = obj.data;
          this.tagName = obj.tagName;
          this.parentNode = obj.parentNode;
          this.attrs = obj.attrs || {};
          this.children = obj.children;
      };
      var notTransAttrs = {
          'href':1,
          'src':1,
          '_src':1,
          '_href':1,
          'cdata_data':1
      };
  
      var notTransTagName = {
          style:1,
          script:1
      };
  
      var indentChar = '    ',
          breakChar = '\n';
  
      function insertLine(arr, current, begin) {
          arr.push(breakChar);
          return current + (begin ? 1 : -1);
      }
  
      function insertIndent(arr, current) {
          //插入缩进
          for (var i = 0; i < current; i++) {
              arr.push(indentChar);
          }
      }
  
      //创建uNode的静态方法
      //支持标签和html
      uNode.createElement = function (html) {
          if (/[<>]/.test(html)) {
              return UM.htmlparser(html).children[0]
          } else {
              return new uNode({
                  type:'element',
                  children:[],
                  tagName:html
              })
          }
      };
      uNode.createText = function (data,noTrans) {
          return new UM.uNode({
              type:'text',
              'data':noTrans ? data : utils.unhtml(data || '')
          })
      };
      function nodeToHtml(node, arr, formatter, current) {
          switch (node.type) {
              case 'root':
                  for (var i = 0, ci; ci = node.children[i++];) {
                      //插入新行
                      if (formatter && ci.type == 'element' && !dtd.$inlineWithA[ci.tagName] && i > 1) {
                          insertLine(arr, current, true);
                          insertIndent(arr, current)
                      }
                      nodeToHtml(ci, arr, formatter, current)
                  }
                  break;
              case 'text':
                  isText(node, arr);
                  break;
              case 'element':
                  isElement(node, arr, formatter, current);
                  break;
              case 'comment':
                  isComment(node, arr, formatter);
          }
          return arr;
      }
  
      function isText(node, arr) {
          if(node.parentNode.tagName == 'pre'){
              //源码模式下输入html标签，不能做转换处理，直接输出
              arr.push(node.data)
          }else{
              arr.push(notTransTagName[node.parentNode.tagName] ? utils.html(node.data) : node.data.replace(/[ ]{2}/g,' &nbsp;'))
          }
  
      }
  
      function isElement(node, arr, formatter, current) {
          var attrhtml = '';
          if (node.attrs) {
              attrhtml = [];
              var attrs = node.attrs;
              for (var a in attrs) {
                  //这里就针对
                  //<p>'<img src='http://nsclick.baidu.com/u.gif?&asdf=\"sdf&asdfasdfs;asdf'></p>
                  //这里边的\"做转换，要不用innerHTML直接被截断了，属性src
                  //有可能做的不够
                  attrhtml.push(a + (attrs[a] !== undefined ? '="' + (notTransAttrs[a] ? utils.html(attrs[a]).replace(/["]/g, function (a) {
                      return '&quot;'
                  }) : utils.unhtml(attrs[a])) + '"' : ''))
              }
              attrhtml = attrhtml.join(' ');
          }
          arr.push('<' + node.tagName +
              (attrhtml ? ' ' + attrhtml  : '') +
              (dtd.$empty[node.tagName] ? '\/' : '' ) + '>'
          );
          //插入新行
          if (formatter  &&  !dtd.$inlineWithA[node.tagName] && node.tagName != 'pre') {
              if(node.children && node.children.length){
                  current = insertLine(arr, current, true);
                  insertIndent(arr, current)
              }
  
          }
          if (node.children && node.children.length) {
              for (var i = 0, ci; ci = node.children[i++];) {
                  if (formatter && ci.type == 'element' &&  !dtd.$inlineWithA[ci.tagName] && i > 1) {
                      insertLine(arr, current);
                      insertIndent(arr, current)
                  }
                  nodeToHtml(ci, arr, formatter, current)
              }
          }
          if (!dtd.$empty[node.tagName]) {
              if (formatter && !dtd.$inlineWithA[node.tagName]  && node.tagName != 'pre') {
  
                  if(node.children && node.children.length){
                      current = insertLine(arr, current);
                      insertIndent(arr, current)
                  }
              }
              arr.push('<\/' + node.tagName + '>');
          }
  
      }
  
      function isComment(node, arr) {
          arr.push('<!--' + node.data + '-->');
      }
  
      function getNodeById(root, id) {
          var node;
          if (root.type == 'element' && root.getAttr('id') == id) {
              return root;
          }
          if (root.children && root.children.length) {
              for (var i = 0, ci; ci = root.children[i++];) {
                  if (node = getNodeById(ci, id)) {
                      return node;
                  }
              }
          }
      }
  
      function getNodesByTagName(node, tagName, arr) {
          if (node.type == 'element' && node.tagName == tagName) {
              arr.push(node);
          }
          if (node.children && node.children.length) {
              for (var i = 0, ci; ci = node.children[i++];) {
                  getNodesByTagName(ci, tagName, arr)
              }
          }
      }
      function nodeTraversal(root,fn){
          if(root.children && root.children.length){
              for(var i= 0,ci;ci=root.children[i];){
                  nodeTraversal(ci,fn);
                  //ci被替换的情况，这里就不再走 fn了
                  if(ci.parentNode ){
                      if(ci.children && ci.children.length){
                          fn(ci)
                      }
                      if(ci.parentNode) i++
                  }
              }
          }else{
              fn(root)
          }
  
      }
      uNode.prototype = {
  
          /**
           * 当前节点对象，转换成html文本
           * @method toHtml
           * @return { String } 返回转换后的html字符串
           * @example
           * ```javascript
           * node.toHtml();
           * ```
           */
  
          /**
           * 当前节点对象，转换成html文本
           * @method toHtml
           * @param { Boolean } formatter 是否格式化返回值
           * @return { String } 返回转换后的html字符串
           * @example
           * ```javascript
           * node.toHtml( true );
           * ```
           */
          toHtml:function (formatter) {
              var arr = [];
              nodeToHtml(this, arr, formatter, 0);
              return arr.join('')
          },
  
          /**
           * 获取节点的html内容
           * @method innerHTML
           * @warning 假如节点的type不是'element'，或节点的标签名称不在dtd列表里，直接返回当前节点
           * @return { String } 返回节点的html内容
           * @example
           * ```javascript
           * var htmlstr = node.innerHTML();
           * ```
           */
  
          /**
           * 设置节点的html内容
           * @method innerHTML
           * @warning 假如节点的type不是'element'，或节点的标签名称不在dtd列表里，直接返回当前节点
           * @param { String } htmlstr 传入要设置的html内容
           * @return { UM.uNode } 返回节点本身
           * @example
           * ```javascript
           * node.innerHTML('<span>text</span>');
           * ```
           */
          innerHTML:function (htmlstr) {
              if (this.type != 'element' || dtd.$empty[this.tagName]) {
                  return this;
              }
              if (utils.isString(htmlstr)) {
                  if(this.children){
                      for (var i = 0, ci; ci = this.children[i++];) {
                          ci.parentNode = null;
                      }
                  }
                  this.children = [];
                  var tmpRoot = UM.htmlparser(htmlstr);
                  for (var i = 0, ci; ci = tmpRoot.children[i++];) {
                      this.children.push(ci);
                      ci.parentNode = this;
                  }
                  return this;
              } else {
                  var tmpRoot = new UM.uNode({
                      type:'root',
                      children:this.children
                  });
                  return tmpRoot.toHtml();
              }
          },
  
          /**
           * 获取节点的纯文本内容
           * @method innerText
           * @warning 假如节点的type不是'element'，或节点的标签名称不在dtd列表里，直接返回当前节点
           * @return { String } 返回节点的存文本内容
           * @example
           * ```javascript
           * var textStr = node.innerText();
           * ```
           */
  
          /**
           * 设置节点的纯文本内容
           * @method innerText
           * @warning 假如节点的type不是'element'，或节点的标签名称不在dtd列表里，直接返回当前节点
           * @param { String } textStr 传入要设置的文本内容
           * @return { UM.uNode } 返回节点本身
           * @example
           * ```javascript
           * node.innerText('<span>text</span>');
           * ```
           */
          innerText:function (textStr,noTrans) {
              if (this.type != 'element' || dtd.$empty[this.tagName]) {
                  return this;
              }
              if (textStr) {
                  if(this.children){
                      for (var i = 0, ci; ci = this.children[i++];) {
                          ci.parentNode = null;
                      }
                  }
                  this.children = [];
                  this.appendChild(uNode.createText(textStr,noTrans));
                  return this;
              } else {
                  return this.toHtml().replace(/<[^>]+>/g, '');
              }
          },
  
          /**
           * 获取当前对象的data属性
           * @method getData
           * @return { Object } 若节点的type值是elemenet，返回空字符串，否则返回节点的data属性
           * @example
           * ```javascript
           * node.getData();
           * ```
           */
          getData:function () {
              if (this.type == 'element')
                  return '';
              return this.data
          },
  
          /**
           * 获取当前节点下的第一个子节点
           * @method firstChild
           * @return { UM.uNode } 返回第一个子节点
           * @example
           * ```javascript
           * node.firstChild(); //返回第一个子节点
           * ```
           */
          firstChild:function () {
  //            if (this.type != 'element' || dtd.$empty[this.tagName]) {
  //                return this;
  //            }
              return this.children ? this.children[0] : null;
          },
  
          /**
           * 获取当前节点下的最后一个子节点
           * @method lastChild
           * @return { UM.uNode } 返回最后一个子节点
           * @example
           * ```javascript
           * node.lastChild(); //返回最后一个子节点
           * ```
           */
          lastChild:function () {
  //            if (this.type != 'element' || dtd.$empty[this.tagName] ) {
  //                return this;
  //            }
              return this.children ? this.children[this.children.length - 1] : null;
          },
  
          /**
           * 获取和当前节点有相同父亲节点的前一个节点
           * @method previousSibling
           * @return { UM.uNode } 返回前一个节点
           * @example
           * ```javascript
           * node.children[2].previousSibling(); //返回子节点node.children[1]
           * ```
           */
          previousSibling : function(){
              var parent = this.parentNode;
              for (var i = 0, ci; ci = parent.children[i]; i++) {
                  if (ci === this) {
                      return i == 0 ? null : parent.children[i-1];
                  }
              }
  
          },
  
          /**
           * 获取和当前节点有相同父亲节点的后一个节点
           * @method nextSibling
           * @return { UM.uNode } 返回后一个节点,找不到返回null
           * @example
           * ```javascript
           * node.children[2].nextSibling(); //如果有，返回子节点node.children[3]
           * ```
           */
          nextSibling : function(){
              var parent = this.parentNode;
              for (var i = 0, ci; ci = parent.children[i++];) {
                  if (ci === this) {
                      return parent.children[i];
                  }
              }
          },
  
          /**
           * 用新的节点替换当前节点
           * @method replaceChild
           * @param { UM.uNode } target 要替换成该节点参数
           * @param { UM.uNode } source 要被替换掉的节点
           * @return { UM.uNode } 返回替换之后的节点对象
           * @example
           * ```javascript
           * node.replaceChild(newNode, childNode); //用newNode替换childNode,childNode是node的子节点
           * ```
           */
          replaceChild:function (target, source) {
              if (this.children) {
                  if(target.parentNode){
                      target.parentNode.removeChild(target);
                  }
                  for (var i = 0, ci; ci = this.children[i]; i++) {
                      if (ci === source) {
                          this.children.splice(i, 1, target);
                          source.parentNode = null;
                          target.parentNode = this;
                          return target;
                      }
                  }
              }
          },
  
          /**
           * 在节点的子节点列表最后位置插入一个节点
           * @method appendChild
           * @param { UM.uNode } node 要插入的节点
           * @return { UM.uNode } 返回刚插入的子节点
           * @example
           * ```javascript
           * node.appendChild( newNode ); //在node内插入子节点newNode
           * ```
           */
          appendChild:function (node) {
              if (this.type == 'root' || (this.type == 'element' && !dtd.$empty[this.tagName])) {
                  if (!this.children) {
                      this.children = []
                  }
                  if(node.parentNode){
                      node.parentNode.removeChild(node);
                  }
                  for (var i = 0, ci; ci = this.children[i]; i++) {
                      if (ci === node) {
                          this.children.splice(i, 1);
                          break;
                      }
                  }
                  this.children.push(node);
                  node.parentNode = this;
                  return node;
              }
  
  
          },
  
          /**
           * 在传入节点的前面插入一个节点
           * @method insertBefore
           * @param { UM.uNode } target 要插入的节点
           * @param { UM.uNode } source 在该参数节点前面插入
           * @return { UM.uNode } 返回刚插入的子节点
           * @example
           * ```javascript
           * node.parentNode.insertBefore(newNode, node); //在node节点后面插入newNode
           * ```
           */
          insertBefore:function (target, source) {
              if (this.children) {
                  if(target.parentNode){
                      target.parentNode.removeChild(target);
                  }
                  for (var i = 0, ci; ci = this.children[i]; i++) {
                      if (ci === source) {
                          this.children.splice(i, 0, target);
                          target.parentNode = this;
                          return target;
                      }
                  }
  
              }
          },
  
          /**
           * 在传入节点的后面插入一个节点
           * @method insertAfter
           * @param { UM.uNode } target 要插入的节点
           * @param { UM.uNode } source 在该参数节点后面插入
           * @return { UM.uNode } 返回刚插入的子节点
           * @example
           * ```javascript
           * node.parentNode.insertAfter(newNode, node); //在node节点后面插入newNode
           * ```
           */
          insertAfter:function (target, source) {
              if (this.children) {
                  if(target.parentNode){
                      target.parentNode.removeChild(target);
                  }
                  for (var i = 0, ci; ci = this.children[i]; i++) {
                      if (ci === source) {
                          this.children.splice(i + 1, 0, target);
                          target.parentNode = this;
                          return target;
                      }
  
                  }
              }
          },
  
          /**
           * 从当前节点的子节点列表中，移除节点
           * @method removeChild
           * @param { UM.uNode } node 要移除的节点引用
           * @param { Boolean } keepChildren 是否保留移除节点的子节点，若传入true，自动把移除节点的子节点插入到移除的位置
           * @return { * } 返回刚移除的子节点
           * @example
           * ```javascript
           * node.removeChild(childNode,true); //在node的子节点列表中移除child节点，并且吧child的子节点插入到移除的位置
           * ```
           */
          removeChild:function (node,keepChildren) {
              if (this.children) {
                  for (var i = 0, ci; ci = this.children[i]; i++) {
                      if (ci === node) {
                          this.children.splice(i, 1);
                          ci.parentNode = null;
                          if(keepChildren && ci.children && ci.children.length){
                              for(var j= 0,cj;cj=ci.children[j];j++){
                                  this.children.splice(i+j,0,cj);
                                  cj.parentNode = this;
  
                              }
                          }
                          return ci;
                      }
                  }
              }
          },
  
          /**
           * 获取当前节点所代表的元素属性，即获取attrs对象下的属性值
           * @method getAttr
           * @param { String } attrName 要获取的属性名称
           * @return { * } 返回attrs对象下的属性值
           * @example
           * ```javascript
           * node.getAttr('title');
           * ```
           */
          getAttr:function (attrName) {
              return this.attrs && this.attrs[attrName.toLowerCase()]
          },
  
          /**
           * 设置当前节点所代表的元素属性，即设置attrs对象下的属性值
           * @method setAttr
           * @param { String } attrName 要设置的属性名称
           * @param { * } attrVal 要设置的属性值，类型视设置的属性而定
           * @return { * } 返回attrs对象下的属性值
           * @example
           * ```javascript
           * node.setAttr('title','标题');
           * ```
           */
          setAttr:function (attrName, attrVal) {
              if (!attrName) {
                  delete this.attrs;
                  return;
              }
              if(!this.attrs){
                  this.attrs = {};
              }
              if (utils.isObject(attrName)) {
                  for (var a in attrName) {
                      if (!attrName[a]) {
                          delete this.attrs[a]
                      } else {
                          this.attrs[a.toLowerCase()] = attrName[a];
                      }
                  }
              } else {
                  if (!attrVal) {
                      delete this.attrs[attrName]
                  } else {
                      this.attrs[attrName.toLowerCase()] = attrVal;
                  }
  
              }
          },
          hasAttr: function( attrName ){
              var attrVal = this.getAttr( attrName );
              return ( attrVal !== null ) && ( attrVal !== undefined );
          },
          /**
           * 获取当前节点在父节点下的位置索引
           * @method getIndex
           * @return { Number } 返回索引数值，如果没有父节点，返回-1
           * @example
           * ```javascript
           * node.getIndex();
           * ```
           */
          getIndex:function(){
              var parent = this.parentNode;
              for(var i= 0,ci;ci=parent.children[i];i++){
                  if(ci === this){
                      return i;
                  }
              }
              return -1;
          },
  
          /**
           * 在当前节点下，根据id查找节点
           * @method getNodeById
           * @param { String } id 要查找的id
           * @return { UM.uNode } 返回找到的节点
           * @example
           * ```javascript
           * node.getNodeById('textId');
           * ```
           */
          getNodeById:function (id) {
              var node;
              if (this.children && this.children.length) {
                  for (var i = 0, ci; ci = this.children[i++];) {
                      if (node = getNodeById(ci, id)) {
                          return node;
                      }
                  }
              }
          },
  
          /**
           * 在当前节点下，根据元素名称查找节点列表
           * @method getNodesByTagName
           * @param { String } tagNames 要查找的元素名称
           * @return { Array } 返回找到的节点列表
           * @example
           * ```javascript
           * node.getNodesByTagName('span');
           * ```
           */
          getNodesByTagName:function (tagNames) {
              tagNames = utils.trim(tagNames).replace(/[ ]{2,}/g, ' ').split(' ');
              var arr = [], me = this;
              utils.each(tagNames, function (tagName) {
                  if (me.children && me.children.length) {
                      for (var i = 0, ci; ci = me.children[i++];) {
                          getNodesByTagName(ci, tagName, arr)
                      }
                  }
              });
              return arr;
          },
  
          /**
           * 根据样式名称，获取节点的样式值
           * @method getStyle
           * @param { String } name 要获取的样式名称
           * @return { String } 返回样式值
           * @example
           * ```javascript
           * node.getStyle('font-size');
           * ```
           */
          getStyle:function (name) {
              var cssStyle = this.getAttr('style');
              if (!cssStyle) {
                  return ''
              }
              var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+)','i');
              var match = cssStyle.match(reg);
              if (match && match[0]) {
                  return match[2]
              }
              return '';
          },
  
          /**
           * 给节点设置样式
           * @method setStyle
           * @param { String } name 要设置的的样式名称
           * @param { String } val 要设置的的样值
           * @example
           * ```javascript
           * node.setStyle('font-size', '12px');
           * ```
           */
          setStyle:function (name, val) {
              function exec(name, val) {
                  var reg = new RegExp('(^|;)\\s*' + name + ':([^;]+;?)', 'gi');
                  cssStyle = cssStyle.replace(reg, '$1');
                  if (val) {
                      cssStyle = name + ':' + utils.unhtml(val) + ';' + cssStyle
                  }
  
              }
  
              var cssStyle = this.getAttr('style');
              if (!cssStyle) {
                  cssStyle = '';
              }
              if (utils.isObject(name)) {
                  for (var a in name) {
                      exec(a, name[a])
                  }
              } else {
                  exec(name, val)
              }
              this.setAttr('style', utils.trim(cssStyle))
          },
          hasClass: function( className ){
              if( this.hasAttr('class') ) {
                  var classNames = this.getAttr('class').split(/\s+/),
                      hasClass = false;
                  $.each(classNames, function(key, item){
                      if( item === className ) {
                          hasClass = true;
                      }
                  });
                  return hasClass;
              } else {
                  return false;
              }
          },
          addClass: function( className ){
  
              var classes = null,
                  hasClass = false;
  
              if( this.hasAttr('class') ) {
  
                  classes = this.getAttr('class');
                  classes = classes.split(/\s+/);
  
                  classes.forEach( function( item ){
  
                      if( item===className ) {
                          hasClass = true;
                          return;
                      }
  
                  } );
  
                  !hasClass && classes.push( className );
  
                  this.setAttr('class', classes.join(" "));
  
              } else {
                  this.setAttr('class', className);
              }
  
          },
          removeClass: function( className ){
              if( this.hasAttr('class') ) {
                  var cl = this.getAttr('class');
                  cl = cl.replace(new RegExp('\\b' + className + '\\b', 'g'),'');
                  this.setAttr('class', utils.trim(cl).replace(/[ ]{2,}/g,' '));
              }
          },
          /**
           * 传入一个函数，递归遍历当前节点下的所有节点
           * @method traversal
           * @param { Function } fn 遍历到节点的时，传入节点作为参数，运行此函数
           * @example
           * ```javascript
           * traversal(node, function(){
           *     console.log(node.type);
           * });
           * ```
           */
          traversal:function(fn){
              if(this.children && this.children.length){
                  nodeTraversal(this,fn);
              }
              return this;
          }
      }
  })();
  
  //html字符串转换成uNode节点
  //by zhanyi
  var htmlparser = UM.htmlparser = function (htmlstr,ignoreBlank) {
      //todo 原来的方式  [^"'<>\/] 有\/就不能配对上 <TD vAlign=top background=../AAA.JPG> 这样的标签了
      //先去掉了，加上的原因忘了，这里先记录
      var re_tag = /<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\s\/>]+)\s*((?:(?:"[^"]*")|(?:'[^']*')|[^"'<>])*)\/?>))/g,
          re_attr = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g;
  
      //ie下取得的html可能会有\n存在，要去掉，在处理replace(/[\t\r\n]*/g,'');代码高量的\n不能去除
      var allowEmptyTags = {
          b:1,code:1,i:1,u:1,strike:1,s:1,tt:1,strong:1,q:1,samp:1,em:1,span:1,
          sub:1,img:1,sup:1,font:1,big:1,small:1,iframe:1,a:1,br:1,pre:1
      };
      htmlstr = htmlstr.replace(new RegExp(domUtils.fillChar, 'g'), '');
      if(!ignoreBlank){
          htmlstr = htmlstr.replace(new RegExp('[\\r\\t\\n'+(ignoreBlank?'':' ')+']*<\/?(\\w+)\\s*(?:[^>]*)>[\\r\\t\\n'+(ignoreBlank?'':' ')+']*','g'), function(a,b){
              //br暂时单独处理
              if(b && allowEmptyTags[b.toLowerCase()]){
                  return a.replace(/(^[\n\r]+)|([\n\r]+$)/g,'');
              }
              return a.replace(new RegExp('^[\\r\\n'+(ignoreBlank?'':' ')+']+'),'').replace(new RegExp('[\\r\\n'+(ignoreBlank?'':' ')+']+$'),'');
          });
      }
  
      var notTransAttrs = {
          'href':1,
          'src':1
      };
  
      var uNode = UM.uNode,
          needParentNode = {
              'td':'tr',
              'tr':['tbody','thead','tfoot'],
              'tbody':'table',
              'th':'tr',
              'thead':'table',
              'tfoot':'table',
              'caption':'table',
              'li':['ul', 'ol'],
              'dt':'dl',
              'dd':'dl',
              'option':'select'
          },
          needChild = {
              'ol':'li',
              'ul':'li'
          };
  
      function text(parent, data) {
  
          if(needChild[parent.tagName]){
              var tmpNode = uNode.createElement(needChild[parent.tagName]);
              parent.appendChild(tmpNode);
              tmpNode.appendChild(uNode.createText(data));
              parent = tmpNode;
          }else{
  
              parent.appendChild(uNode.createText(data));
          }
      }
  
      function element(parent, tagName, htmlattr) {
          var needParentTag;
          if (needParentTag = needParentNode[tagName]) {
              var tmpParent = parent,hasParent;
              while(tmpParent.type != 'root'){
                  if(utils.isArray(needParentTag) ? utils.indexOf(needParentTag, tmpParent.tagName) != -1 : needParentTag == tmpParent.tagName){
                      parent = tmpParent;
                      hasParent = true;
                      break;
                  }
                  tmpParent = tmpParent.parentNode;
              }
              if(!hasParent){
                  parent = element(parent, utils.isArray(needParentTag) ? needParentTag[0] : needParentTag)
              }
          }
          //按dtd处理嵌套
  //        if(parent.type != 'root' && !dtd[parent.tagName][tagName])
  //            parent = parent.parentNode;
          var elm = new uNode({
              parentNode:parent,
              type:'element',
              tagName:tagName.toLowerCase(),
              //是自闭合的处理一下
              children:dtd.$empty[tagName] ? null : []
          });
          //如果属性存在，处理属性
          if (htmlattr) {
              var attrs = {}, match;
              while (match = re_attr.exec(htmlattr)) {
                  attrs[match[1].toLowerCase()] = notTransAttrs[match[1].toLowerCase()] ? (match[2] || match[3] || match[4]) : utils.unhtml(match[2] || match[3] || match[4])
              }
              elm.attrs = attrs;
          }
  
          parent.children.push(elm);
          //如果是自闭合节点返回父亲节点
          return  dtd.$empty[tagName] ? parent : elm
      }
  
      function comment(parent, data) {
          parent.children.push(new uNode({
              type:'comment',
              data:data,
              parentNode:parent
          }));
      }
  
      var match, currentIndex = 0, nextIndex = 0;
      //设置根节点
      var root = new uNode({
          type:'root',
          children:[]
      });
      var currentParent = root;
  
      while (match = re_tag.exec(htmlstr)) {
          currentIndex = match.index;
          try{
              if (currentIndex > nextIndex) {
                  //text node
                  text(currentParent, htmlstr.slice(nextIndex, currentIndex));
              }
              if (match[3]) {
  
                  if(dtd.$cdata[currentParent.tagName]){
                      text(currentParent, match[0]);
                  }else{
                      //start tag
                      currentParent = element(currentParent, match[3].toLowerCase(), match[4]);
                  }
  
  
              } else if (match[1]) {
                  if(currentParent.type != 'root'){
                      if(dtd.$cdata[currentParent.tagName] && !dtd.$cdata[match[1]]){
                          text(currentParent, match[0]);
                      }else{
                          var tmpParent = currentParent;
                          while(currentParent.type == 'element' && currentParent.tagName != match[1].toLowerCase()){
                              currentParent = currentParent.parentNode;
                              if(currentParent.type == 'root'){
                                  currentParent = tmpParent;
                                  throw 'break'
                              }
                          }
                          //end tag
                          currentParent = currentParent.parentNode;
                      }
  
                  }
  
              } else if (match[2]) {
                  //comment
                  comment(currentParent, match[2])
              }
          }catch(e){}
  
          nextIndex = re_tag.lastIndex;
  
      }
      //如果结束是文本，就有可能丢掉，所以这里手动判断一下
      //例如 <li>sdfsdfsdf<li>sdfsdfsdfsdf
      if (nextIndex < htmlstr.length) {
          text(currentParent, htmlstr.slice(nextIndex));
      }
      return root;
  };
  /**
   * @file
   * @name UM.filterNode
   * @short filterNode
   * @desc 根据给定的规则过滤节点
   * @import editor.js,core/utils.js
   * @anthor zhanyi
   */
  var filterNode = UM.filterNode = function () {
      function filterNode(node,rules){
          switch (node.type) {
              case 'text':
                  break;
              case 'element':
                  var val;
                  if(val = rules[node.tagName]){
                      if(val === '-'){
                          node.parentNode.removeChild(node)
                      }else if(utils.isFunction(val)){
                          var parentNode = node.parentNode,
                              index = node.getIndex();
                          val(node);
                          if(node.parentNode){
                              if(node.children){
                                  for(var i = 0,ci;ci=node.children[i];){
                                      filterNode(ci,rules);
                                      if(ci.parentNode){
                                          i++;
                                      }
                                  }
                              }
                          }else{
                              for(var i = index,ci;ci=parentNode.children[i];){
                                  filterNode(ci,rules);
                                  if(ci.parentNode){
                                      i++;
                                  }
                              }
                          }
  
  
                      }else{
                          var attrs = val['$'];
                          if(attrs && node.attrs){
                              var tmpAttrs = {},tmpVal;
                              for(var a in attrs){
                                  tmpVal = node.getAttr(a);
                                  //todo 只先对style单独处理
                                  if(a == 'style' && utils.isArray(attrs[a])){
                                      var tmpCssStyle = [];
                                      utils.each(attrs[a],function(v){
                                          var tmp;
                                          if(tmp = node.getStyle(v)){
                                              tmpCssStyle.push(v + ':' + tmp);
                                          }
                                      });
                                      tmpVal = tmpCssStyle.join(';')
                                  }
                                  if(tmpVal){
                                      tmpAttrs[a] = tmpVal;
                                  }
  
                              }
                              node.attrs = tmpAttrs;
                          }
                          if(node.children){
                              for(var i = 0,ci;ci=node.children[i];){
                                  filterNode(ci,rules);
                                  if(ci.parentNode){
                                      i++;
                                  }
                              }
                          }
                      }
                  }else{
                      //如果不在名单里扣出子节点并删除该节点,cdata除外
                      if(dtd.$cdata[node.tagName]){
                          node.parentNode.removeChild(node)
                      }else{
                          var parentNode = node.parentNode,
                              index = node.getIndex();
                          node.parentNode.removeChild(node,true);
                          for(var i = index,ci;ci=parentNode.children[i];){
                              filterNode(ci,rules);
                              if(ci.parentNode){
                                  i++;
                              }
                          }
                      }
                  }
                  break;
              case 'comment':
                  node.parentNode.removeChild(node)
          }
  
      }
      return function(root,rules){
          if(utils.isEmptyObject(rules)){
              return root;
          }
          var val;
          if(val = rules['-']){
              utils.each(val.split(' '),function(k){
                  rules[k] = '-'
              })
          }
          for(var i= 0,ci;ci=root.children[i];){
              filterNode(ci,rules);
              if(ci.parentNode){
                  i++;
              }
          }
          return root;
      }
  }();
  ///import core
  /**
   * @description 插入内容
   * @name baidu.editor.execCommand
   * @param   {String}   cmdName     inserthtml插入内容的命令
   * @param   {String}   html                要插入的内容
   * @author zhanyi
   */
  UM.commands['inserthtml'] = {
      execCommand: function (command,html,notNeedFilter){
          var me = this,
              range,
              div;
          if(!html){
              return;
          }
          if(me.fireEvent('beforeinserthtml',html) === true){
              return;
          }
          range = me.selection.getRange();
          div = range.document.createElement( 'div' );
          div.style.display = 'inline';
  
          if (!notNeedFilter) {
              var root = UM.htmlparser(html);
              //如果给了过滤规则就先进行过滤
              if(me.options.filterRules){
                  UM.filterNode(root,me.options.filterRules);
              }
              //执行默认的处理
              me.filterInputRule(root);
              html = root.toHtml()
          }
          div.innerHTML = utils.trim( html );
  
          if ( !range.collapsed ) {
              var tmpNode = range.startContainer;
              if(domUtils.isFillChar(tmpNode)){
                  range.setStartBefore(tmpNode)
              }
              tmpNode = range.endContainer;
              if(domUtils.isFillChar(tmpNode)){
                  range.setEndAfter(tmpNode)
              }
              range.txtToElmBoundary();
              //结束边界可能放到了br的前边，要把br包含进来
              // x[xxx]<br/>
              if(range.endContainer && range.endContainer.nodeType == 1){
                  tmpNode = range.endContainer.childNodes[range.endOffset];
                  if(tmpNode && domUtils.isBr(tmpNode)){
                      range.setEndAfter(tmpNode);
                  }
              }
              if(range.startOffset == 0){
                  tmpNode = range.startContainer;
                  if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
                      tmpNode = range.endContainer;
                      if(range.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
                          me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
                          range.setStart(me.body.firstChild,0).collapse(true)
  
                      }
                  }
              }
              !range.collapsed && range.deleteContents();
              if(range.startContainer.nodeType == 1){
                  var child = range.startContainer.childNodes[range.startOffset],pre;
                  if(child && domUtils.isBlockElm(child) && (pre = child.previousSibling) && domUtils.isBlockElm(pre)){
                      range.setEnd(pre,pre.childNodes.length).collapse();
                      while(child.firstChild){
                          pre.appendChild(child.firstChild);
                      }
                      domUtils.remove(child);
                  }
              }
  
          }
  
  
          var child,parent,pre,tmp,hadBreak = 0, nextNode;
          //如果当前位置选中了fillchar要干掉，要不会产生空行
          if(range.inFillChar()){
              child = range.startContainer;
              if(domUtils.isFillChar(child)){
                  range.setStartBefore(child).collapse(true);
                  domUtils.remove(child);
              }else if(domUtils.isFillChar(child,true)){
                  child.nodeValue = child.nodeValue.replace(fillCharReg,'');
                  range.startOffset--;
                  range.collapsed && range.collapse(true)
              }
          }
          while ( child = div.firstChild ) {
              if(hadBreak){
                  var p = me.document.createElement('p');
                  while(child && (child.nodeType == 3 || !dtd.$block[child.tagName])){
                      nextNode = child.nextSibling;
                      p.appendChild(child);
                      child = nextNode;
                  }
                  if(p.firstChild){
  
                      child = p
                  }
              }
              range.insertNode( child );
              nextNode = child.nextSibling;
              if ( !hadBreak && child.nodeType == domUtils.NODE_ELEMENT && domUtils.isBlockElm( child ) ){
  
                  parent = domUtils.findParent( child,function ( node ){ return domUtils.isBlockElm( node ); } );
                  if ( parent && parent.tagName.toLowerCase() != 'body' && !(dtd[parent.tagName][child.nodeName] && child.parentNode === parent)){
                      if(!dtd[parent.tagName][child.nodeName]){
                          pre = parent;
                      }else{
                          tmp = child.parentNode;
                          while (tmp !== parent){
                              pre = tmp;
                              tmp = tmp.parentNode;
  
                          }
                      }
  
  
                      domUtils.breakParent( child, pre || tmp );
                      //去掉break后前一个多余的节点  <p>|<[p> ==> <p></p><div></div><p>|</p>
                      var pre = child.previousSibling;
                      domUtils.trimWhiteTextNode(pre);
                      if(!pre.childNodes.length){
                          domUtils.remove(pre);
                      }
                      //trace:2012,在非ie的情况，切开后剩下的节点有可能不能点入光标添加br占位
  
                      if(!browser.ie &&
                          (next = child.nextSibling) &&
                          domUtils.isBlockElm(next) &&
                          next.lastChild &&
                          !domUtils.isBr(next.lastChild)){
                          next.appendChild(me.document.createElement('br'));
                      }
                      hadBreak = 1;
                  }
              }
              var next = child.nextSibling;
              if(!div.firstChild && next && domUtils.isBlockElm(next)){
  
                  range.setStart(next,0).collapse(true);
                  break;
              }
              range.setEndAfter( child ).collapse();
  
          }
  
          child = range.startContainer;
  
          if(nextNode && domUtils.isBr(nextNode)){
              domUtils.remove(nextNode)
          }
          //用chrome可能有空白展位符
          if(domUtils.isBlockElm(child) && domUtils.isEmptyNode(child)){
              if(nextNode = child.nextSibling){
                  domUtils.remove(child);
                  if(nextNode.nodeType == 1 && dtd.$block[nextNode.tagName]){
  
                      range.setStart(nextNode,0).collapse(true).shrinkBoundary()
                  }
              }else{
  
                  try{
                      child.innerHTML = browser.ie ? domUtils.fillChar : '<br/>';
                  }catch(e){
                      range.setStartBefore(child);
                      domUtils.remove(child)
                  }
  
              }
  
          }
          //加上true因为在删除表情等时会删两次，第一次是删的fillData
          try{
              if(browser.ie9below && range.startContainer.nodeType == 1 && !range.startContainer.childNodes[range.startOffset]){
                  var start = range.startContainer,pre = start.childNodes[range.startOffset-1];
                  if(pre && pre.nodeType == 1 && dtd.$empty[pre.tagName]){
                      var txt = this.document.createTextNode(domUtils.fillChar);
                      range.insertNode(txt).setStart(txt,0).collapse(true);
                  }
              }
              setTimeout(function(){
                  range.select(true);
              })
  
          }catch(e){}
  
  
          setTimeout(function(){
              range = me.selection.getRange();
              range.scrollIntoView();
              me.fireEvent('afterinserthtml');
          },200);
      }
  };
  
  ///import core
  ///import plugins\inserthtml.js
  ///commands 插入图片，操作图片的对齐方式
  ///commandsName  InsertImage,ImageNone,ImageLeft,ImageRight,ImageCenter
  ///commandsTitle  图片,默认,居左,居右,居中
  ///commandsDialog  dialogs\image
  /**
   * Created by .
   * User: zhanyi
   * for image
   */
  UM.commands['insertimage'] = {
      execCommand:function (cmd, opt) {
          opt = utils.isArray(opt) ? opt : [opt];
          if (!opt.length) {
              return;
          }
          var me = this;
          var html = [], str = '', ci;
          ci = opt[0];
          if (opt.length == 1) {
              str = '<img src="' + ci.src + '" ' + (ci._src ? ' _src="' + ci._src + '" ' : '') +
                  (ci.width ? 'width="' + ci.width + '" ' : '') +
                  (ci.height ? ' height="' + ci.height + '" ' : '') +
                  (ci['floatStyle'] == 'left' || ci['floatStyle'] == 'right' ? ' style="float:' + ci['floatStyle'] + ';"' : '') +
                  (ci.title && ci.title != "" ? ' title="' + ci.title + '"' : '') +
                  (ci.border && ci.border != "0" ? ' border="' + ci.border + '"' : '') +
                  (ci.alt && ci.alt != "" ? ' alt="' + ci.alt + '"' : '') +
                  (ci.hspace && ci.hspace != "0" ? ' hspace = "' + ci.hspace + '"' : '') +
                  (ci.vspace && ci.vspace != "0" ? ' vspace = "' + ci.vspace + '"' : '') + '/>';
              if (ci['floatStyle'] == 'center') {
                  str = '<p style="text-align: center">' + str + '</p>';
              }
              html.push(str);
  
          } else {
              for (var i = 0; ci = opt[i++];) {
                  str = '<p ' + (ci['floatStyle'] == 'center' ? 'style="text-align: center" ' : '') + '><img src="' + ci.src + '" ' +
                      (ci.width ? 'width="' + ci.width + '" ' : '') + (ci._src ? ' _src="' + ci._src + '" ' : '') +
                      (ci.height ? ' height="' + ci.height + '" ' : '') +
                      ' style="' + (ci['floatStyle'] && ci['floatStyle'] != 'center' ? 'float:' + ci['floatStyle'] + ';' : '') +
                      (ci.border || '') + '" ' +
                      (ci.title ? ' title="' + ci.title + '"' : '') + ' /></p>';
                  html.push(str);
              }
          }
  
          me.execCommand('insertHtml', html.join(''), true);
      }
  };
  ///import core
  ///commands 段落格式,居左,居右,居中,两端对齐
  ///commandsName  JustifyLeft,JustifyCenter,JustifyRight,JustifyJustify
  ///commandsTitle  居左对齐,居中对齐,居右对齐,两端对齐
  /**
   * @description 居左右中
   * @name UM.execCommand
   * @param   {String}   cmdName     justify执行对齐方式的命令
   * @param   {String}   align               对齐方式：left居左，right居右，center居中，justify两端对齐
   * @author zhanyi
   */
  UM.plugins['justify']=function(){
      var me = this;
      $.each('justifyleft justifyright justifycenter justifyfull'.split(' '),function(i,cmdName){
          me.commands[cmdName] = {
              execCommand:function (cmdName) {
                  return this.document.execCommand(cmdName);
              },
              queryCommandValue: function (cmdName) {
                  var val = this.document.queryCommandValue(cmdName);
                  return   val === true || val === 'true' ? cmdName.replace(/justify/,'') : '';
              },
              queryCommandState: function (cmdName) {
                  return this.document.queryCommandState(cmdName) ? 1 : 0
              }
          };
      })
  };
  
  ///import core
  ///import plugins\removeformat.js
  ///commands 字体颜色,背景色,字号,字体,下划线,删除线
  ///commandsName  ForeColor,BackColor,FontSize,FontFamily,Underline,StrikeThrough
  ///commandsTitle  字体颜色,背景色,字号,字体,下划线,删除线
  /**
   * @description 字体
   * @name UM.execCommand
   * @param {String}     cmdName    执行的功能名称
   * @param {String}    value             传入的值
   */
  UM.plugins['font'] = function () {
      var me = this,
          fonts = {
              'forecolor': 'forecolor',
              'backcolor': 'backcolor',
              'fontsize': 'fontsize',
              'fontfamily': 'fontname'
          },
          cmdNameToStyle = {
              'forecolor': 'color',
              'backcolor': 'background-color',
              'fontsize': 'font-size',
              'fontfamily': 'font-family'
          },
          cmdNameToAttr = {
              'forecolor': 'color',
              'fontsize': 'size',
              'fontfamily': 'face'
          };
      me.setOpt({
          'fontfamily': [
              { name: 'songti', val: '宋体,SimSun'},
              { name: 'yahei', val: '微软雅黑,Microsoft YaHei'},
              { name: 'kaiti', val: '楷体,楷体_GB2312, SimKai'},
              { name: 'heiti', val: '黑体, SimHei'},
              { name: 'lishu', val: '隶书, SimLi'},
              { name: 'andaleMono', val: 'andale mono'},
              { name: 'arial', val: 'arial, helvetica,sans-serif'},
              { name: 'arialBlack', val: 'arial black,avant garde'},
              { name: 'comicSansMs', val: 'comic sans ms'},
              { name: 'impact', val: 'impact,chicago'},
              { name: 'timesNewRoman', val: 'times new roman'},
              { name: 'sans-serif',val:'sans-serif'}
          ],
          'fontsize': [12, 14,  16, 18,24, 32]
      });
  
      me.addOutputRule(function (root) {
          utils.each(root.getNodesByTagName('font'), function (node) {
              if (node.tagName == 'font') {
                  var cssStyle = [];
                  for (var p in node.attrs) {
                      switch (p) {
                          case 'size':
                              var val =  node.attrs[p];
                              $.each({
                                  '10':'1',
                                  '12':'2',
                                  '16':'3',
                                  '18':'4',
                                  '24':'5',
                                  '32':'6',
                                  '48':'7'
                              },function(k,v){
                                  if(v == val){
                                      val = k;
                                      return false;
                                  }
                              });
                              cssStyle.push('font-size:' + val + 'px');
                              break;
                          case 'color':
                              cssStyle.push('color:' + node.attrs[p]);
                              break;
                          case 'face':
                              cssStyle.push('font-family:' + node.attrs[p]);
                              break;
                          case 'style':
                              cssStyle.push(node.attrs[p]);
                      }
                  }
                  node.attrs = {
                      'style': cssStyle.join(';')
                  };
              }
              node.tagName = 'span';
              if(node.parentNode.tagName == 'span' && node.parentNode.children.length == 1){
                  $.each(node.attrs,function(k,v){
  
                      node.parentNode.attrs[k] = k == 'style' ? node.parentNode.attrs[k] + v : v;
                  })
                  node.parentNode.removeChild(node,true);
              }
          });
      });
      for(var p in fonts){
          (function (cmd) {
              me.commands[cmd] = {
                  execCommand: function (cmdName,value) {
                      if(value == 'transparent'){
                          return;
                      }
                      var rng = this.selection.getRange();
                      if(rng.collapsed){
                          var span = $('<span></span>').css(cmdNameToStyle[cmdName],value)[0];
                          rng.insertNode(span).setStart(span,0).setCursor();
                      }else{
                          if(cmdName == 'fontsize'){
                              value  = {
                                  '12':'1',
                                  '14':'2',
                                  '16':'3',
                                  '18':'4',
                                  '24':'5',
                                  '48':'7'
                              }[(value+"").replace(/px/,'')]
                          }
                          this.document.execCommand(fonts[cmdName],false, value);
                          if(browser.gecko){
                              $.each(this.$body.find('a'),function(i,a){
                                  var parent = a.parentNode;
                                  if(parent.lastChild === parent.firstChild && /FONT|SPAN/.test(parent.tagName)){
                                      var cloneNode = parent.cloneNode(false);
                                      cloneNode.innerHTML = a.innerHTML;
                                      $(a).html('').append(cloneNode).insertBefore(parent);
  
                                      $(parent).remove();
                                  }
                              });
                          }
                          if(!browser.ie){
                              var nativeRange = this.selection.getNative().getRangeAt(0);
                              var common = nativeRange.commonAncestorContainer;
                              var rng = this.selection.getRange(),
                                  bk = rng.createBookmark(true);
  
                              $(common).find('a').each(function(i,n){
                                  var parent = n.parentNode;
                                  if(parent.nodeName == 'FONT'){
                                      var font = parent.cloneNode(false);
                                      font.innerHTML = n.innerHTML;
                                      $(n).html('').append(font);
                                  }
                              });
                              rng.moveToBookmark(bk).select()
                          }
                          return true
                      }
  
                  },
                  queryCommandValue: function (cmdName) {
                      var start = me.selection.getStart();
                      var val = $(start).css(cmdNameToStyle[cmdName]);
                      if(val === undefined){
                          val = $(start).attr(cmdNameToAttr[cmdName])
                      }
                      return val ? utils.fixColor(cmdName,val).replace(/px/,'') : '';
                  },
                  queryCommandState: function (cmdName) {
                      return this.queryCommandValue(cmdName)
                  }
              };
          })(p);
      }
  };
  ///import core
  ///commands 超链接,取消链接
  ///commandsName  Link,Unlink
  ///commandsTitle  超链接,取消链接
  ///commandsDialog  dialogs\link
  /**
   * 超链接
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     link插入超链接
   * @param   {Object}  options         url地址，title标题，target是否打开新页
   * @author zhanyi
   */
  /**
   * 取消链接
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     unlink取消链接
   * @author zhanyi
   */
  
  UM.plugins['link'] = function(){
      var me = this;
  
      me.setOpt('autourldetectinie',false);
      //在ie下禁用autolink
      if(browser.ie && this.options.autourldetectinie === false){
          this.addListener('keyup',function(cmd,evt){
              var me = this,keyCode = evt.keyCode;
              if(keyCode == 13 || keyCode == 32){
                  var rng = me.selection.getRange();
                  var start = rng.startContainer;
                  if(keyCode == 13){
                      if(start.nodeName == 'P'){
                          var pre = start.previousSibling;
                          if(pre && pre.nodeType == 1){
                              var pre = pre.lastChild;
                              if(pre && pre.nodeName == 'A' && !pre.getAttribute('_href')){
                                  domUtils.remove(pre,true);
                              }
                          }
                      }
                  }else if(keyCode == 32){
                     if(start.nodeType == 3 && /^\s$/.test(start.nodeValue)){
                         start = start.previousSibling;
                         if(start && start.nodeName == 'A' && !start.getAttribute('_href')){
                             domUtils.remove(start,true);
                         }
                     }
                  }
  
              }
  
  
          });
      }
  
      this.addOutputRule(function(root){
          $.each(root.getNodesByTagName('a'),function(i,a){
              var _href = utils.html(a.getAttr('_href'));
              if(!/^(ftp|https?|\/|file)/.test(_href)){
                  _href = 'http://' + _href;
              }
              a.setAttr('href', _href);
              a.setAttr('_href')
              if(a.getAttr('title')==''){
                  a.setAttr('title')
              }
          })
      });
      this.addInputRule(function(root){
          $.each(root.getNodesByTagName('a'),function(i,a){
              a.setAttr('_href', utils.html(a.getAttr('href')));
          })
      });
      me.commands['link'] = {
          execCommand : function( cmdName, opt ) {
  
              var me = this;
              var rng = me.selection.getRange();
              if(rng.collapsed){
                  var start = rng.startContainer;
                  if(start = domUtils.findParentByTagName(start,'a',true)){
                      $(start).attr(opt);
                      rng.selectNode(start).select()
                  }else{
                      rng.insertNode($('<a>' +opt.href+'</a>').attr(opt)[0]).select()
  
                  }
  
              }else{
                  me.document.execCommand('createlink',false,'_umeditor_link');
                  utils.each(domUtils.getElementsByTagName(me.body,'a',function(n){
  
                      return n.getAttribute('href') == '_umeditor_link'
                  }),function(l){
                      if($(l).text() == '_umeditor_link'){
                          $(l).text(opt.href);
                      }
                      domUtils.setAttributes(l,opt);
                      rng.selectNode(l).select()
                  })
              }
  
          },
          queryCommandState:function(){
              return this.queryCommandValue('link') ? 1 : 0;
          },
          queryCommandValue:function(){
              var path = this.selection.getStartElementPath();
              var result;
              $.each(path,function(i,n){
                  if(n.nodeName == "A"){
                      result = n;
                      return false;
                  }
              })
              return result;
          }
      };
      me.commands['unlink'] = {
          execCommand : function() {
              this.document.execCommand('unlink');
          }
      };
  };
  ///import core
  ///commands 打印
  ///commandsName  Print
  ///commandsTitle  打印
  /**
   * @description 打印
   * @name baidu.editor.execCommand
   * @param   {String}   cmdName     print打印编辑器内容
   * @author zhanyi
   */
  UM.commands['print'] = {
      execCommand : function(){
          var me = this,
              id = 'editor_print_' + +new Date();
  
          $('<iframe src="" id="' + id + '" name="' + id + '" frameborder="0"></iframe>').attr('id', id)
              .css({
                  width:'0px',
                  height:'0px',
                  'overflow':'hidden',
                  'float':'left',
                  'position':'absolute',
                  top:'-10000px',
                  left:'-10000px'
              })
              .appendTo(me.$container.find('.edui-dialog-container'));
  
          var w = window.open('', id, ''),
              d = w.document;
          d.open();
          d.write('<html><head></head><body><div>'+this.getContent(null,null,true)+'</div><script>' +
              "setTimeout(function(){" +
              "window.print();" +
              "setTimeout(function(){" +
              "window.parent.$('#" + id + "').remove();" +
              "},100);" +
              "},200);" +
              '</script></body></html>');
          d.close();
      },
      notNeedUndo : 1
  };
  ///import core
  ///commands 格式
  ///commandsName  Paragraph
  ///commandsTitle  段落格式
  /**
   * 段落样式
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     paragraph插入段落执行命令
   * @param   {String}   style               标签值为：'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
   * @param   {String}   attrs               标签的属性
   * @author zhanyi
   */
  UM.plugins['paragraph'] = function() {
      var me = this;
      me.setOpt('paragraph',{'p':'', 'h6':'', 'h5':'', 'h4':'', 'h3':'', 'h2':'', 'h1':''});
      me.commands['paragraph'] = {
          execCommand : function( cmdName, style ) {
              return this.document.execCommand('formatBlock',false,'<' + style + '>');
          },
          queryCommandValue : function() {
              try{
                  var  val = this.document.queryCommandValue('formatBlock')
              }catch(e){
              }
              return val ;
          }
      };
  };
  
  ///import core
  ///import plugins\inserthtml.js
  ///commands 分割线
  ///commandsName  Horizontal
  ///commandsTitle  分隔线
  /**
   * 分割线
   * @function
   * @name UM.execCommand
   * @param {String}     cmdName    horizontal插入分割线
   */
  UM.plugins['horizontal'] = function(){
      var me = this;
      me.commands['horizontal'] = {
          execCommand : function(  ) {
              this.document.execCommand('insertHorizontalRule');
              var rng = me.selection.getRange().txtToElmBoundary(true),
                  start = rng.startContainer;
              if(domUtils.isBody(rng.startContainer)){
                  var next = rng.startContainer.childNodes[rng.startOffset];
                  if(!next){
                      next = $('<p></p>').appendTo(rng.startContainer).html(browser.ie ? '&nbsp;' : '<br/>')[0]
                  }
                  rng.setStart(next,0).setCursor()
              }else{
  
                  while(dtd.$inline[start.tagName] && start.lastChild === start.firstChild){
  
                      var parent = start.parentNode;
                      parent.appendChild(start.firstChild);
                      parent.removeChild(start);
                      start = parent;
                  }
                  while(dtd.$inline[start.tagName]){
                      start = start.parentNode;
                  }
                  if(start.childNodes.length == 1 && start.lastChild.nodeName == 'HR'){
                      var hr = start.lastChild;
                      $(hr).insertBefore(start);
                      rng.setStart(start,0).setCursor();
                  }else{
                      hr = $('hr',start)[0];
                      domUtils.breakParent(hr,start);
                      var pre = hr.previousSibling;
                      if(pre && domUtils.isEmptyBlock(pre)){
                          $(pre).remove()
                      }
                      rng.setStart(hr.nextSibling,0).setCursor();
                  }
  
              }
          }
      };
  
  };
  
  
  ///import core
  ///commands 清空文档
  ///commandsName  ClearDoc
  ///commandsTitle  清空文档
  /**
   *
   * 清空文档
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     cleardoc清空文档
   */
  
  UM.commands['cleardoc'] = {
      execCommand : function() {
          var me = this,
              range = me.selection.getRange();
          me.body.innerHTML = "<p>"+(ie ? "" : "<br/>")+"</p>";
          range.setStart(me.body.firstChild,0).setCursor(false,true);
          setTimeout(function(){
              me.fireEvent("clearDoc");
          },0);
  
      }
  };
  
  
  ///import core
  ///commands 撤销和重做
  ///commandsName  Undo,Redo
  ///commandsTitle  撤销,重做
  /**
   * @description 回退
   * @author zhanyi
   */
  
  UM.plugins['undo'] = function () {
      var saveSceneTimer;
      var me = this,
          maxUndoCount = me.options.maxUndoCount || 20,
          maxInputCount = me.options.maxInputCount || 20,
          fillchar = new RegExp(domUtils.fillChar + '|<\/hr>', 'gi');// ie会产生多余的</hr>
      var noNeedFillCharTags = {
          ol:1,ul:1,table:1,tbody:1,tr:1,body:1
      };
      var orgState = me.options.autoClearEmptyNode;
      function compareAddr(indexA, indexB) {
          if (indexA.length != indexB.length)
              return 0;
          for (var i = 0, l = indexA.length; i < l; i++) {
              if (indexA[i] != indexB[i])
                  return 0
          }
          return 1;
      }
  
      function compareRangeAddress(rngAddrA, rngAddrB) {
          if (rngAddrA.collapsed != rngAddrB.collapsed) {
              return 0;
          }
          if (!compareAddr(rngAddrA.startAddress, rngAddrB.startAddress) || !compareAddr(rngAddrA.endAddress, rngAddrB.endAddress)) {
              return 0;
          }
          return 1;
      }
  
      function UndoManager() {
          this.list = [];
          this.index = 0;
          this.hasUndo = false;
          this.hasRedo = false;
          this.undo = function () {
              if (this.hasUndo) {
                  if (!this.list[this.index - 1] && this.list.length == 1) {
                      this.reset();
                      return;
                  }
                  while (this.list[this.index].content == this.list[this.index - 1].content) {
                      this.index--;
                      if (this.index == 0) {
                          return this.restore(0);
                      }
                  }
                  this.restore(--this.index);
              }
          };
          this.redo = function () {
              if (this.hasRedo) {
                  while (this.list[this.index].content == this.list[this.index + 1].content) {
                      this.index++;
                      if (this.index == this.list.length - 1) {
                          return this.restore(this.index);
                      }
                  }
                  this.restore(++this.index);
              }
          };
  
          this.restore = function () {
              var me = this.editor;
              var scene = this.list[this.index];
              var root = UM.htmlparser(scene.content.replace(fillchar, ''));
              me.options.autoClearEmptyNode = false;
              me.filterInputRule(root,true);
              me.options.autoClearEmptyNode = orgState;
              //trace:873
              //去掉展位符
              me.body.innerHTML = root.toHtml();
              me.fireEvent('afterscencerestore');
              //处理undo后空格不展位的问题
              if (browser.ie) {
                  utils.each(domUtils.getElementsByTagName(me.document,'td th caption p'),function(node){
                      if(domUtils.isEmptyNode(node)){
                          domUtils.fillNode(me.document, node);
                      }
                  })
              }
  
              try{
                  var rng = new dom.Range(me.document,me.body).moveToAddress(scene.address);
                  if(browser.ie && rng.collapsed && rng.startContainer.nodeType == 1){
                      var tmpNode = rng.startContainer.childNodes[rng.startOffset];
                      if( !tmpNode || tmpNode.nodeType == 1 && dtd.$empty[tmpNode]){
                          rng.insertNode(me.document.createTextNode(' ')).collapse(true);
                      }
                  }
                  rng.select(noNeedFillCharTags[rng.startContainer.nodeName.toLowerCase()]);
              }catch(e){}
  
              this.update();
              this.clearKey();
              //不能把自己reset了
              me.fireEvent('reset', true);
          };
  
          this.getScene = function () {
              var me = this.editor;
              var rng = me.selection.getRange(),
                  rngAddress = rng.createAddress(false,true);
              me.fireEvent('beforegetscene');
              var root = UM.htmlparser(me.body.innerHTML,true);
              me.options.autoClearEmptyNode = false;
              me.filterOutputRule(root,true);
              me.options.autoClearEmptyNode = orgState;
              var cont = root.toHtml();
              browser.ie && (cont = cont.replace(/>&nbsp;</g, '><').replace(/\s*</g, '<').replace(/>\s*/g, '>'));
              me.fireEvent('aftergetscene');
              return {
                  address:rngAddress,
                  content:cont
              }
          };
          this.save = function (notCompareRange,notSetCursor) {
              clearTimeout(saveSceneTimer);
              var currentScene = this.getScene(notSetCursor),
                  lastScene = this.list[this.index];
              //内容相同位置相同不存
              if (lastScene && lastScene.content == currentScene.content &&
                  ( notCompareRange ? 1 : compareRangeAddress(lastScene.address, currentScene.address) )
                  ) {
                  return;
              }
              this.list = this.list.slice(0, this.index + 1);
              this.list.push(currentScene);
              //如果大于最大数量了，就把最前的剔除
              if (this.list.length > maxUndoCount) {
                  this.list.shift();
              }
              this.index = this.list.length - 1;
              this.clearKey();
              //跟新undo/redo状态
              this.update();
  
          };
          this.update = function () {
              this.hasRedo = !!this.list[this.index + 1];
              this.hasUndo = !!this.list[this.index - 1];
          };
          this.reset = function () {
              this.list = [];
              this.index = 0;
              this.hasUndo = false;
              this.hasRedo = false;
              this.clearKey();
          };
          this.clearKey = function () {
              keycont = 0;
              lastKeyCode = null;
          };
      }
  
      me.undoManger = new UndoManager();
      me.undoManger.editor = me;
      function saveScene() {
          this.undoManger.save();
      }
  
      me.addListener('saveScene', function () {
          var args = Array.prototype.splice.call(arguments,1);
          this.undoManger.save.apply(this.undoManger,args);
      });
  
      me.addListener('beforeexeccommand', saveScene);
      me.addListener('afterexeccommand', saveScene);
  
      me.addListener('reset', function (type, exclude) {
          if (!exclude) {
              this.undoManger.reset();
          }
      });
      me.commands['redo'] = me.commands['undo'] = {
          execCommand:function (cmdName) {
              this.undoManger[cmdName]();
          },
          queryCommandState:function (cmdName) {
              return this.undoManger['has' + (cmdName.toLowerCase() == 'undo' ? 'Undo' : 'Redo')] ? 0 : -1;
          },
          notNeedUndo:1
      };
  
      var keys = {
              //  /*Backspace*/ 8:1, /*Delete*/ 46:1,
              /*Shift*/ 16:1, /*Ctrl*/ 17:1, /*Alt*/ 18:1,
              37:1, 38:1, 39:1, 40:1
  
          },
          keycont = 0,
          lastKeyCode;
      //输入法状态下不计算字符数
      var inputType = false;
      me.addListener('ready', function () {
          $(this.body).on('compositionstart', function () {
              inputType = true;
          }).on('compositionend', function () {
              inputType = false;
          })
      });
      //快捷键
      me.addshortcutkey({
          "Undo":"ctrl+90", //undo
          "Redo":"ctrl+89,shift+ctrl+z" //redo
  
      });
      var isCollapsed = true;
      me.addListener('keydown', function (type, evt) {
  
          var me = this;
          var keyCode = evt.keyCode || evt.which;
          if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
              if (inputType)
                  return;
  
              if(!me.selection.getRange().collapsed){
                  me.undoManger.save(false,true);
                  isCollapsed = false;
                  return;
              }
              if (me.undoManger.list.length == 0) {
                  me.undoManger.save(true);
              }
              clearTimeout(saveSceneTimer);
              function save(cont){
  
                  if (cont.selection.getRange().collapsed)
                      cont.fireEvent('contentchange');
                  cont.undoManger.save(false,true);
                  cont.fireEvent('selectionchange');
              }
              saveSceneTimer = setTimeout(function(){
                  if(inputType){
                      var interalTimer = setInterval(function(){
                          if(!inputType){
                              save(me);
                              clearInterval(interalTimer)
                          }
                      },300)
                      return;
                  }
                  save(me);
              },200);
  
              lastKeyCode = keyCode;
              keycont++;
              if (keycont >= maxInputCount ) {
                  save(me)
              }
          }
      });
      me.addListener('keyup', function (type, evt) {
          var keyCode = evt.keyCode || evt.which;
          if (!keys[keyCode] && !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
              if (inputType)
                  return;
              if(!isCollapsed){
                  this.undoManger.save(false,true);
                  isCollapsed = true;
              }
          }
      });
  
  };
  
  ///import core
  ///import plugins/inserthtml.js
  ///import plugins/undo.js
  ///import plugins/serialize.js
  ///commands 粘贴
  ///commandsName  PastePlain
  ///commandsTitle  纯文本粘贴模式
  /**
   * @description 粘贴
   * @author zhanyi
   */
  UM.plugins['paste'] = function () {
      function getClipboardData(callback) {
          var doc = this.document;
          if (doc.getElementById('baidu_pastebin')) {
              return;
          }
          var range = this.selection.getRange(),
              bk = range.createBookmark(),
          //创建剪贴的容器div
              pastebin = doc.createElement('div');
          pastebin.id = 'baidu_pastebin';
          // Safari 要求div必须有内容，才能粘贴内容进来
          browser.webkit && pastebin.appendChild(doc.createTextNode(domUtils.fillChar + domUtils.fillChar));
          this.body.appendChild(pastebin);
          //trace:717 隐藏的span不能得到top
          //bk.start.innerHTML = '&nbsp;';
          bk.start.style.display = '';
  
          // axiuno: white-space:nowrap; 去掉，粘贴文本会不换行。
          pastebin.style.cssText = "position:absolute;width:1px;height:1px;overflow:hidden;left:-1000px;top:" +
          //要在现在光标平行的位置加入，否则会出现跳动的问题
          $(bk.start).position().top  + 'px';
  
          range.selectNodeContents(pastebin).select(true);
  
          setTimeout(function () {
              if (browser.webkit) {
                  for (var i = 0, pastebins = doc.querySelectorAll('#baidu_pastebin'), pi; pi = pastebins[i++];) {
                      if (domUtils.isEmptyNode(pi)) {
                          domUtils.remove(pi);
                      } else {
                          pastebin = pi;
                          break;
                      }
                  }
              }
              try {
                  pastebin.parentNode.removeChild(pastebin);
              } catch (e) {
              }
              range.moveToBookmark(bk).select(true);
              callback(pastebin);
          }, 0);
      }
  
      var me = this;
  
  
      function filter(div) {
          var html;
          if (div.firstChild) {
              //去掉cut中添加的边界值
              var nodes = domUtils.getElementsByTagName(div, 'span');
              for (var i = 0, ni; ni = nodes[i++];) {
                  if (ni.id == '_baidu_cut_start' || ni.id == '_baidu_cut_end') {
                      domUtils.remove(ni);
                  }
              }
  
              if (browser.webkit) {
  
                  var brs = div.querySelectorAll('div br');
                  for (var i = 0, bi; bi = brs[i++];) {
                      var pN = bi.parentNode;
                      if (pN.tagName == 'DIV' && pN.childNodes.length == 1) {
                          pN.innerHTML = '<p><br/></p>';
                          domUtils.remove(pN);
                      }
                  }
                  var divs = div.querySelectorAll('#baidu_pastebin');
                  for (var i = 0, di; di = divs[i++];) {
                      var tmpP = me.document.createElement('p');
                      di.parentNode.insertBefore(tmpP, di);
                      while (di.firstChild) {
                          tmpP.appendChild(di.firstChild);
                      }
                      domUtils.remove(di);
                  }
  
                  var metas = div.querySelectorAll('meta');
                  for (var i = 0, ci; ci = metas[i++];) {
                      domUtils.remove(ci);
                  }
  
                  var brs = div.querySelectorAll('br');
                  for (i = 0; ci = brs[i++];) {
                      if (/^apple-/i.test(ci.className)) {
                          domUtils.remove(ci);
                      }
                  }
              }
              if (browser.gecko) {
                  var dirtyNodes = div.querySelectorAll('[_moz_dirty]');
                  for (i = 0; ci = dirtyNodes[i++];) {
                      ci.removeAttribute('_moz_dirty');
                  }
              }
              if (!browser.ie) {
                  var spans = div.querySelectorAll('span.Apple-style-span');
                  for (var i = 0, ci; ci = spans[i++];) {
                      domUtils.remove(ci, true);
                  }
              }
  
              //ie下使用innerHTML会产生多余的\r\n字符，也会产生&nbsp;这里过滤掉
              html = div.innerHTML;//.replace(/>(?:(\s|&nbsp;)*?)</g,'><');
  
              //过滤word粘贴过来的冗余属性
              html = UM.filterWord(html);
              //取消了忽略空白的第二个参数，粘贴过来的有些是有空白的，会被套上相关的标签
              var root = UM.htmlparser(html);
              //如果给了过滤规则就先进行过滤
              if (me.options.filterRules) {
                  UM.filterNode(root, me.options.filterRules);
              }
              //执行默认的处理，直接清理掉节点
              me.filterInputRule(root);
              //针对chrome的处理
              if (browser.webkit) {
                  var br = root.lastChild();
                  if (br && br.type == 'element' && br.tagName == 'br') {
                      root.removeChild(br)
                  }
                  utils.each(me.body.querySelectorAll('div'), function (node) {
                      if (domUtils.isEmptyBlock(node)) {
                          domUtils.remove(node)
                      }
                  })
              }
              html = {'html': root.toHtml()};
              me.fireEvent('beforepaste', html, root);
              //抢了默认的粘贴，那后边的内容就不执行了，比如表格粘贴
              if(!html.html){
                  return;
              }
  
              me.execCommand('insertHtml', html.html, true);
              me.fireEvent("afterpaste", html);
          }
      }
  
  
      me.addListener('ready', function () {
          $(me.body).on( 'cut', function () {
              var range = me.selection.getRange();
              if (!range.collapsed && me.undoManger) {
                  me.undoManger.save();
              }
          }).on(browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
              //ie下beforepaste在点击右键时也会触发，所以用监控键盘才处理
              if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
                  return;
              }
              getClipboardData.call(me, function (div) {
                  filter(div);
              });
          });
  
      });
  };
  
  
  ///import core
  ///commands 有序列表,无序列表
  ///commandsName  InsertOrderedList,InsertUnorderedList
  ///commandsTitle  有序列表,无序列表
  /**
   * 有序列表
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     insertorderlist插入有序列表
   * @param   {String}   style               值为：decimal,lower-alpha,lower-roman,upper-alpha,upper-roman
   * @author zhanyi
   */
  /**
   * 无序链接
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     insertunorderlist插入无序列表
   * * @param   {String}   style            值为：circle,disc,square
   * @author zhanyi
   */
  
  UM.plugins['list'] = function () {
      var me = this;
  
      me.setOpt( {
          'insertorderedlist':{
              'decimal':'',
              'lower-alpha':'',
              'lower-roman':'',
              'upper-alpha':'',
              'upper-roman':''
          },
          'insertunorderedlist':{
              'circle':'',
              'disc':'',
              'square':''
          }
      } );
  
      this.addInputRule(function(root){
          utils.each(root.getNodesByTagName('li'), function (node) {
              if(node.children.length == 0){
                  node.parentNode.removeChild(node);
              }
          })
      });
      me.commands['insertorderedlist'] =
      me.commands['insertunorderedlist'] = {
              execCommand:function (cmdName) {
                  this.document.execCommand(cmdName);
                  var rng = this.selection.getRange(),
                      bk = rng.createBookmark(true);
  
                  this.$body.find('ol,ul').each(function(i,n){
                      var parent = n.parentNode;
                      if(parent.tagName == 'P' && parent.lastChild === parent.firstChild){
                          $(n).children().each(function(j,li){
                              var p = parent.cloneNode(false);
                              $(p).append(li.innerHTML);
                              $(li).html('').append(p);
                          });
                          $(n).insertBefore(parent);
                          $(parent).remove();
                      }
  
                      if(dtd.$inline[parent.tagName]){
                          if(parent.tagName == 'SPAN'){
  
                              $(n).children().each(function(k,li){
                                  var span = parent.cloneNode(false);
                                  if(li.firstChild.nodeName != 'P'){
  
                                      while(li.firstChild){
                                          span.appendChild(li.firstChild)
                                      };
                                      $('<p></p>').appendTo(li).append(span);
                                  }else{
                                      while(li.firstChild){
                                          span.appendChild(li.firstChild)
                                      };
                                      $(li.firstChild).append(span);
                                  }
                              })
  
                          }
                          domUtils.remove(parent,true)
                      }
                  });
  
  
  
  
                  rng.moveToBookmark(bk).select();
                  return true;
              },
              queryCommandState:function (cmdName) {
                  return this.document.queryCommandState(cmdName);
              }
          };
  };
  
  
  ///import core
  ///import plugins/serialize.js
  ///import plugins/undo.js
  ///commands 查看源码
  ///commandsName  Source
  ///commandsTitle  查看源码
  (function (){
      var sourceEditors = {
          textarea: function (editor, holder){
              var textarea = holder.ownerDocument.createElement('textarea');
              textarea.style.cssText = 'resize:none;border:0;padding:0;margin:0;overflow-y:auto;outline:0';
              // todo: IE下只有onresize属性可用... 很纠结
              if (browser.ie && browser.version < 8) {
  
                  textarea.style.width = holder.offsetWidth + 'px';
                  textarea.style.height = holder.offsetHeight + 'px';
                  holder.onresize = function (){
                      textarea.style.width = holder.offsetWidth + 'px';
                      textarea.style.height = holder.offsetHeight + 'px';
                  };
              }
              holder.appendChild(textarea);
              return {
                  container : textarea,
                  setContent: function (content){
                      textarea.value = content;
                  },
                  getContent: function (){
                      return textarea.value;
                  },
                  select: function (){
                      var range;
                      if (browser.ie) {
                          range = textarea.createTextRange();
                          range.collapse(true);
                          range.select();
                      } else {
                          //todo: chrome下无法设置焦点
                          textarea.setSelectionRange(0, 0);
                          textarea.focus();
                      }
                  },
                  dispose: function (){
                      holder.removeChild(textarea);
                      // todo
                      holder.onresize = null;
                      textarea = null;
                      holder = null;
                  }
              };
          }
      };
  
      UM.plugins['source'] = function (){
          var me = this;
          var opt = this.options;
          var sourceMode = false;
          var sourceEditor;
  
          opt.sourceEditor = 'textarea';
  
          me.setOpt({
              sourceEditorFirst:false
          });
          function createSourceEditor(holder){
              return sourceEditors.textarea(me, holder);
          }
  
          var bakCssText;
          //解决在源码模式下getContent不能得到最新的内容问题
          var oldGetContent = me.getContent,
              bakAddress;
  
          me.commands['source'] = {
              execCommand: function (){
  
                  sourceMode = !sourceMode;
                  if (sourceMode) {
                      bakAddress = me.selection.getRange().createAddress(false,true);
                      me.undoManger && me.undoManger.save(true);
                      if(browser.gecko){
                          me.body.contentEditable = false;
                      }
  
  //                    bakCssText = me.body.style.cssText;
                      me.body.style.cssText += ';position:absolute;left:-32768px;top:-32768px;';
  
  
                      me.fireEvent('beforegetcontent');
                      var root = UM.htmlparser(me.body.innerHTML);
                      me.filterOutputRule(root);
                      root.traversal(function (node) {
                          if (node.type == 'element') {
                              switch (node.tagName) {
                                  case 'td':
                                  case 'th':
                                  case 'caption':
                                      if(node.children && node.children.length == 1){
                                          if(node.firstChild().tagName == 'br' ){
                                              node.removeChild(node.firstChild())
                                          }
                                      };
                                      break;
                                  case 'pre':
                                      node.innerText(node.innerText().replace(/&nbsp;/g,' '))
  
                              }
                          }
                      });
  
                      me.fireEvent('aftergetcontent');
  
                      var content = root.toHtml(true);
  
                      sourceEditor = createSourceEditor(me.body.parentNode);
  
                      sourceEditor.setContent(content);
  
                      var getStyleValue=function(attr){
                          return parseInt($(me.body).css(attr));
                      };
                      $(sourceEditor.container).width($(me.body).width()+getStyleValue("padding-left")+getStyleValue("padding-right"))
                          .height($(me.body).height());
                      setTimeout(function (){
                          sourceEditor.select();
                      });
                      //重置getContent，源码模式下取值也能是最新的数据
                      me.getContent = function (){
                          return sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
                      };
                  } else {
                      me.$body.css({
                          'position':'',
                          'left':'',
                          'top':''
                      });
  //                    me.body.style.cssText = bakCssText;
                      var cont = sourceEditor.getContent() || '<p>' + (browser.ie ? '' : '<br/>')+'</p>';
                      //处理掉block节点前后的空格,有可能会误命中，暂时不考虑
                      cont = cont.replace(new RegExp('[\\r\\t\\n ]*<\/?(\\w+)\\s*(?:[^>]*)>','g'), function(a,b){
                          if(b && !dtd.$inlineWithA[b.toLowerCase()]){
                              return a.replace(/(^[\n\r\t ]*)|([\n\r\t ]*$)/g,'');
                          }
                          return a.replace(/(^[\n\r\t]*)|([\n\r\t]*$)/g,'')
                      });
                      me.setContent(cont);
                      sourceEditor.dispose();
                      sourceEditor = null;
                      //还原getContent方法
                      me.getContent = oldGetContent;
                      var first = me.body.firstChild;
                      //trace:1106 都删除空了，下边会报错，所以补充一个p占位
                      if(!first){
                          me.body.innerHTML = '<p>'+(browser.ie?'':'<br/>')+'</p>';
                      }
                      //要在ifm为显示时ff才能取到selection,否则报错
                      //这里不能比较位置了
                      me.undoManger && me.undoManger.save(true);
                      if(browser.gecko){
                          me.body.contentEditable = true;
                      }
                      try{
                          me.selection.getRange().moveToAddress(bakAddress).select();
                      }catch(e){}
  
                  }
                  this.fireEvent('sourcemodechanged', sourceMode);
              },
              queryCommandState: function (){
                  return sourceMode|0;
              },
              notNeedUndo : 1
          };
          var oldQueryCommandState = me.queryCommandState;
  
  
          me.queryCommandState = function (cmdName){
              cmdName = cmdName.toLowerCase();
              if (sourceMode) {
                  //源码模式下可以开启的命令
                  return cmdName in {
                      'source' : 1,
                      'fullscreen' : 1
                  } ? oldQueryCommandState.apply(this, arguments)  : -1
              }
              return oldQueryCommandState.apply(this, arguments);
          };
  
      };
  
  })();
  ///import core
  ///import plugins/undo.js
  ///commands 设置回车标签p或br
  ///commandsName  EnterKey
  ///commandsTitle  设置回车标签p或br
  /**
   * @description 处理回车
   * @author zhanyi
   */
  UM.plugins['enterkey'] = function() {
      var hTag,
          me = this,
          tag = me.options.enterTag;
      me.addListener('keyup', function(type, evt) {
  
          var keyCode = evt.keyCode || evt.which;
          if (keyCode == 13) {
              var range = me.selection.getRange(),
                  start = range.startContainer,
                  doSave;
  
              //修正在h1-h6里边回车后不能嵌套p的问题
              if (!browser.ie) {
  
                  if (/h\d/i.test(hTag)) {
                      if (browser.gecko) {
                          var h = domUtils.findParentByTagName(start, [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption','table'], true);
                          if (!h) {
                              me.document.execCommand('formatBlock', false, '<p>');
                              doSave = 1;
                          }
                      } else {
                          //chrome remove div
                          if (start.nodeType == 1) {
                              var tmp = me.document.createTextNode(''),div;
                              range.insertNode(tmp);
                              div = domUtils.findParentByTagName(tmp, 'div', true);
                              if (div) {
                                  var p = me.document.createElement('p');
                                  while (div.firstChild) {
                                      p.appendChild(div.firstChild);
                                  }
                                  div.parentNode.insertBefore(p, div);
                                  domUtils.remove(div);
                                  range.setStartBefore(tmp).setCursor();
                                  doSave = 1;
                              }
                              domUtils.remove(tmp);
  
                          }
                      }
  
                      if (me.undoManger && doSave) {
                          me.undoManger.save();
                      }
                  }
                  //没有站位符，会出现多行的问题
                  browser.opera &&  range.select();
              }else{
                  me.fireEvent('saveScene',true,true)
              }
          }
      });
  
      me.addListener('keydown', function(type, evt) {
          var keyCode = evt.keyCode || evt.which;
          if (keyCode == 13) {//回车
              if(me.fireEvent('beforeenterkeydown')){
                  domUtils.preventDefault(evt);
                  return;
              }
              me.fireEvent('saveScene',true,true);
              hTag = '';
  
  
              var range = me.selection.getRange();
  
              if (!range.collapsed) {
                  //跨td不能删
                  var start = range.startContainer,
                      end = range.endContainer,
                      startTd = domUtils.findParentByTagName(start, 'td', true),
                      endTd = domUtils.findParentByTagName(end, 'td', true);
                  if (startTd && endTd && startTd !== endTd || !startTd && endTd || startTd && !endTd) {
                      evt.preventDefault ? evt.preventDefault() : ( evt.returnValue = false);
                      return;
                  }
              }
              if (tag == 'p') {
  
  
                  if (!browser.ie) {
  
                      start = domUtils.findParentByTagName(range.startContainer, ['ol','ul','p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6','blockquote','caption'], true);
  
                      //opera下执行formatblock会在table的场景下有问题，回车在opera原生支持很好，所以暂时在opera去掉调用这个原生的command
                      //trace:2431
                      if (!start && !browser.opera) {
  
                          me.document.execCommand('formatBlock', false, '<p>');
  
                          if (browser.gecko) {
                              range = me.selection.getRange();
                              start = domUtils.findParentByTagName(range.startContainer, 'p', true);
                              start && domUtils.removeDirtyAttr(start);
                          }
  
  
                      } else {
                          hTag = start.tagName;
                          start.tagName.toLowerCase() == 'p' && browser.gecko && domUtils.removeDirtyAttr(start);
                      }
  
                  }
  
              }
  
          }
      });
  
      browser.ie && me.addListener('setDisabled',function(){
          $(me.body).find('p').each(function(i,p){
              if(domUtils.isEmptyBlock(p)){
                  p.innerHTML = '&nbsp;'
              }
          })
      })
  };
  
  ///import core
  ///commands 预览
  ///commandsName  Preview
  ///commandsTitle  预览
  /**
   * 预览
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName     preview预览编辑器内容
   */
  UM.commands['preview'] = {
      execCommand : function(){
          var w = window.open('', '_blank', ''),
              d = w.document,
              c = this.getContent(null,null,true),
              path = this.getOpt('UMEDITOR_HOME_URL'),
              formula = c.indexOf('mathquill-embedded-latex')!=-1 ?
                  '<link rel="stylesheet" href="' + path + 'third-party/mathquill/mathquill.css"/>' +
                  '<script src="' + path + 'third-party/jquery.min.js"></script>' +
                  '<script src="' + path + 'third-party/mathquill/mathquill.min.js"></script>':'';
          d.open();
          d.write('<html><head>' + formula + '</head><body><div>'+c+'</div></body></html>');
          d.close();
      },
      notNeedUndo : 1
  };
  
  ///import core
  ///commands 加粗,斜体,上标,下标
  ///commandsName  Bold,Italic,Subscript,Superscript
  ///commandsTitle  加粗,加斜,下标,上标
  /**
   * b u i等基础功能实现
   * @function
   * @name UM.execCommands
   * @param    {String}    cmdName    bold加粗。italic斜体。subscript上标。superscript下标。
  */
  UM.plugins['basestyle'] = function(){
      var basestyles = ['bold','underline','superscript','subscript','italic','strikethrough'],
          me = this;
      //添加快捷键
      me.addshortcutkey({
          "Bold" : "ctrl+66",//^B
          "Italic" : "ctrl+73", //^I
          "Underline" : "ctrl+shift+85",//^U
          "strikeThrough" : 'ctrl+shift+83' //^s
      });
      //过滤最后的产出数据
      me.addOutputRule(function(root){
          $.each(root.getNodesByTagName('b i u strike s'),function(i,node){
              switch (node.tagName){
                  case 'b':
                      node.tagName = 'strong';
                      break;
                  case 'i':
                      node.tagName = 'em';
                      break;
                  case 'u':
                      node.tagName = 'span';
                      node.setStyle('text-decoration','underline');
                      break;
                  case 's':
                  case 'strike':
                      node.tagName = 'span';
                      node.setStyle('text-decoration','line-through')
              }
          });
      });
      $.each(basestyles,function(i,cmd){
          me.commands[cmd] = {
              execCommand : function( cmdName ) {
                  var rng = this.selection.getRange();
                  if(rng.collapsed && this.queryCommandState(cmdName) != 1){
                      var node = this.document.createElement({
                          'bold':'strong',
                          'underline':'u',
                          'superscript':'sup',
                          'subscript':'sub',
                          'italic':'em',
                          'strikethrough':'strike'
                      }[cmdName]);
                      rng.insertNode(node).setStart(node,0).setCursor(false);
                      return true;
                  }else{
                      return this.document.execCommand(cmdName)
                  }
  
              },
              queryCommandState : function(cmdName) {
                  if(browser.gecko){
                      return this.document.queryCommandState(cmdName)
                  }
                  var path = this.selection.getStartElementPath(),result = false;
                  $.each(path,function(i,n){
                      switch (cmdName){
                          case 'bold':
                              if(n.nodeName == 'STRONG' || n.nodeName == 'B'){
                                  result = 1;
                                  return false;
                              }
                              break;
                          case 'underline':
                              if(n.nodeName == 'U' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'underline'){
                                  result = 1;
                                  return false;
                              }
                              break;
                          case 'superscript':
                              if(n.nodeName == 'SUP'){
                                  result = 1;
                                  return false;
                              }
                              break;
                          case 'subscript':
                              if(n.nodeName == 'SUB'){
                                  result = 1;
                                  return false;
                              }
                              break;
                          case 'italic':
                              if(n.nodeName == 'EM' || n.nodeName == 'I'){
                                  result = 1;
                                  return false;
                              }
                              break;
                          case 'strikethrough':
                              if(n.nodeName == 'S' || n.nodeName == 'STRIKE' || n.nodeName == 'SPAN' && $(n).css('text-decoration') == 'line-through'){
                                  result = 1;
                                  return false;
                              }
                              break;
                      }
                  })
                  return result
              }
          };
      })
  };
  
  
  ///import core
  ///import plugins/inserthtml.js
  ///commands 视频
  ///commandsName InsertVideo
  ///commandsTitle  插入视频
  ///commandsDialog  dialogs\video
  UM.plugins['video'] = function (){
      var me =this,
          div;
  
      /**
       * 创建插入视频字符窜
       * @param url 视频地址
       * @param width 视频宽度
       * @param height 视频高度
       * @param align 视频对齐
       * @param toEmbed 是否以flash代替显示
       * @param addParagraph  是否需要添加P 标签
       */
      function creatInsertStr(url,width,height,id,align,toEmbed){
      	  if((url.indexOf('youku') != -1 || url.indexOf('youtu') != -1)) {
      	  	// http://player.youku.com/embed/XMTY5NjY0NTM5Mg==
      	  	var matches = url.match(/youku\.com\/player\.php\/sid\/([\w=]+)/);
      	  	var newurl = 'http://player.youku.com/embed/'+matches[1];
      	  	var s = '<iframe width="'+width+'" height="'+height+'" src="'+newurl+'" frameborder="0" allowfullscreen="1"></iframe>';
      	  	return s;
      	  }
          return  !toEmbed ?
  
                  '<img ' + (id ? 'id="' + id+'"' : '') + ' width="'+ width +'" height="' + height + '" _url="'+url+'" class="edui-faked-video"'  +
                  ' src="' + me.options.UMEDITOR_HOME_URL+'themes/default/images/spacer.gif" style="background:url('+me.options.UMEDITOR_HOME_URL+'themes/default/images/videologo.gif) no-repeat center center; border:1px solid gray;'+(align ? 'float:' + align + ';': '')+'" />'
  
                  :
                  '<embed type="application/x-shockwave-flash" class="edui-faked-video" pluginspage="http://www.macromedia.com/go/getflashplayer"' +
                  ' src="' + url + '" width="' + width  + '" height="' + height  + '"'  + (align ? ' style="float:' + align + '"': '') +
                  ' wmode="transparent" play="true" loop="false" menu="false" allowscriptaccess="never" allowfullscreen="true" >';
      }
  
      function switchImgAndEmbed(root,img2embed){
          utils.each(root.getNodesByTagName(img2embed ? 'img' : 'embed'),function(node){
              if(node.getAttr('class') == 'edui-faked-video'){
  
                  var html = creatInsertStr( img2embed ? node.getAttr('_url') : node.getAttr('src'),node.getAttr('width'),node.getAttr('height'),null,node.getStyle('float') || '',img2embed);
                  node.parentNode.replaceChild(UM.uNode.createElement(html),node)
              }
          })
      }
  
      me.addOutputRule(function(root){
          switchImgAndEmbed(root,true)
      });
      me.addInputRule(function(root){
          switchImgAndEmbed(root)
      });
  
      me.commands["insertvideo"] = {
          execCommand: function (cmd, videoObjs){
              videoObjs = utils.isArray(videoObjs)?videoObjs:[videoObjs];
              var html = [],id = 'tmpVedio';
              for(var i=0,vi,len = videoObjs.length;i<len;i++){
                   vi = videoObjs[i];
                   html.push(creatInsertStr( vi.url, vi.width || 420,  vi.height || 280, id + i,vi.align,false));
              }
              me.execCommand("inserthtml",html.join(""),true);
  
          },
          queryCommandState : function(){
              var img = me.selection.getRange().getClosedNode(),
                  flag = img && (img.className == "edui-faked-video");
              return flag ? 1 : 0;
          }
      };
  };
  ///import core
  ///commands 全选
  ///commandsName  SelectAll
  ///commandsTitle  全选
  /**
   * 选中所有
   * @function
   * @name UM.execCommand
   * @param   {String}   cmdName    selectall选中编辑器里的所有内容
   * @author zhanyi
  */
  UM.plugins['selectall'] = function(){
      var me = this;
      me.commands['selectall'] = {
          execCommand : function(){
              //去掉了原生的selectAll,因为会出现报错和当内容为空时，不能出现闭合状态的光标
              var me = this,body = me.body,
                  range = me.selection.getRange();
              range.selectNodeContents(body);
              if(domUtils.isEmptyBlock(body)){
                  //opera不能自动合并到元素的里边，要手动处理一下
                  if(browser.opera && body.firstChild && body.firstChild.nodeType == 1){
                      range.setStartAtFirst(body.firstChild);
                  }
                  range.collapse(true);
              }
              range.select(true);
          },
          notNeedUndo : 1
      };
  
  
      //快捷键
      me.addshortcutkey({
           "selectAll" : "ctrl+65"
      });
  };
  
  //UM.plugins['removeformat'] = function () {
  //    var me = this;
  //    me.commands['removeformat'] = {
  //        execCommand: function () {
  //            me.document.execCommand('removeformat');
  //
  //            /* 处理ie8和firefox选区有链接时,清除格式的bug */
  //            if (browser.gecko || browser.ie8 || browser.webkit) {
  //                var nativeRange = this.selection.getNative().getRangeAt(0),
  //                    common = nativeRange.commonAncestorContainer,
  //                    rng = me.selection.getRange(),
  //                    bk = rng.createBookmark();
  //
  //                function isEleInBookmark(node, bk){
  //                    if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_FOLLOWING) &&
  //                        (domUtils.getPosition(bk.end, node) & domUtils.POSITION_FOLLOWING) ) {
  //                        return true;
  //                    } else if ( (domUtils.getPosition(node, bk.start) & domUtils.POSITION_CONTAINS) ||
  //                        (domUtils.getPosition(node, bk.end) & domUtils.POSITION_CONTAINS) ) {
  //                        return true;
  //                    }
  //                    return false;
  //                }
  //
  //                $(common).find('a').each(function (k, a) {
  //                    if ( isEleInBookmark(a, bk) ) {
  //                        a.removeAttribute('style');
  //                    }
  //                });
  //
  //            }
  //        }
  //    };
  //
  //};
  //
  
  
  UM.plugins['removeformat'] = function(){
      var me = this;
      me.setOpt({
          'removeFormatTags': 'b,big,code,del,dfn,em,font,i,ins,kbd,q,samp,small,span,strike,strong,sub,sup,tt,u,var',
          'removeFormatAttributes':'class,style,lang,width,height,align,hspace,valign'
      });
      me.commands['removeformat'] = {
          execCommand : function( cmdName, tags, style, attrs,notIncludeA ) {
  
              var tagReg = new RegExp( '^(?:' + (tags || this.options.removeFormatTags).replace( /,/g, '|' ) + ')$', 'i' ) ,
                  removeFormatAttributes = style ? [] : (attrs || this.options.removeFormatAttributes).split( ',' ),
                  range = new dom.Range( this.document ),
                  bookmark,node,parent,
                  filter = function( node ) {
                      return node.nodeType == 1;
                  };
  
              function isRedundantSpan (node) {
                  if (node.nodeType == 3 || node.tagName.toLowerCase() != 'span'){
                      return 0;
                  }
                  if (browser.ie) {
                      //ie 下判断实效，所以只能简单用style来判断
                      //return node.style.cssText == '' ? 1 : 0;
                      var attrs = node.attributes;
                      if ( attrs.length ) {
                          for ( var i = 0,l = attrs.length; i<l; i++ ) {
                              if ( attrs[i].specified ) {
                                  return 0;
                              }
                          }
                          return 1;
                      }
                  }
                  return !node.attributes.length;
              }
              
              function doRemove( range ) {
  
                  var bookmark1 = range.createBookmark();
                  if ( range.collapsed ) {
                      range.enlarge( true );
                  }
  
                  //不能把a标签切了
                  if(!notIncludeA){
                      var aNode = domUtils.findParentByTagName(range.startContainer,'a',true);
                      if(aNode){
                          range.setStartBefore(aNode);
                      }
  
                      aNode = domUtils.findParentByTagName(range.endContainer,'a',true);
                      if(aNode){
                          range.setEndAfter(aNode);
                      }
  
                  }
  
                  bookmark = range.createBookmark();
  
                  node = bookmark.start;
  
                  //切开始
                  while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
                      domUtils.breakParent( node, parent );
                      domUtils.clearEmptySibling( node );
                  }
                  if ( bookmark.end ) {
                      //切结束
                      node = bookmark.end;
                      while ( (parent = node.parentNode) && !domUtils.isBlockElm( parent ) ) {
                          domUtils.breakParent( node, parent );
                          domUtils.clearEmptySibling( node );
                      }
  
                      //开始去除样式
                      var current = domUtils.getNextDomNode( bookmark.start, false, filter ),
                          next;
                          
                      while ( current ) {
                          if ( current == bookmark.end ) {
                              break;
                          }
  
                          next = domUtils.getNextDomNode( current, true, filter );
                          
                          // 去除所有空节点上的 style 样式，粘贴的时候的垃圾代码
                          // by axiuno:
  			//  !dtd.$empty[current.tagName.toLowerCase()] && 
  			
                          if (!domUtils.isBookmarkNode( current )) {
                              if ( tagReg.test( current.tagName ) ) {
                                  if ( style ) {
                                      domUtils.removeStyle( current, style );
                                      if ( isRedundantSpan( current ) && style != 'text-decoration'){
                                          domUtils.remove( current, true );
                                      }
                                  } else {
                                      domUtils.remove( current, true );
                                  }
                              } else {
                                  //trace:939  不能把list上的样式去掉
                                  /* fixed by axiuno: 去掉 ol li ul 标签的格式 */
                                 // if(!dtd.$tableContent[current.tagName] && !dtd.$list[current.tagName]){
                                   
                                      domUtils.removeAttributes( current, removeFormatAttributes );
                                      if ( isRedundantSpan( current ) ){
                                          domUtils.remove( current, true );
                                      }
                                     
                                 // }
  
                              }
                          }
                          current = next;
                      }
                  }
                  //trace:1035
                  //trace:1096 不能把td上的样式去掉，比如边框
                  var pN = bookmark.start.parentNode;
                  if(domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName] && !dtd.$list[pN.tagName]){
                      domUtils.removeAttributes(  pN,removeFormatAttributes );
                  }
                  pN = bookmark.end.parentNode;
                  if(bookmark.end && domUtils.isBlockElm(pN) && !dtd.$tableContent[pN.tagName]&& !dtd.$list[pN.tagName]){
                      domUtils.removeAttributes(  pN,removeFormatAttributes );
                  }
                  range.moveToBookmark( bookmark ).moveToBookmark(bookmark1);
                  //清除冗余的代码 <b><bookmark></b>
                  var node = range.startContainer,
                      tmp,
                      collapsed = range.collapsed;
                  while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
                      tmp = node.parentNode;
                      range.setStartBefore(node);
                      //trace:937
                      //更新结束边界
                      if(range.startContainer === range.endContainer){
                          range.endOffset--;
                      }
                      domUtils.remove(node);
                      node = tmp;
                  }
  
                  if(!collapsed){
                      node = range.endContainer;
                      while(node.nodeType == 1 && domUtils.isEmptyNode(node) && dtd.$removeEmpty[node.tagName]){
                          tmp = node.parentNode;
                          range.setEndBefore(node);
                          domUtils.remove(node);
  
                          node = tmp;
                      }
                  }
              }
  
  
  
              range = this.selection.getRange();
              if(!range.collapsed) {
                  doRemove( range );
                  range.select();
              }
  
          }
  
      };
  
  };
  /*
   *   处理特殊键的兼容性问题
   */
  UM.plugins['keystrokes'] = function() {
      var me = this;
      var collapsed = true;
      me.addListener('keydown', function(type, evt) {
          var keyCode = evt.keyCode || evt.which,
              rng = me.selection.getRange();
  
          //处理全选的情况
          if(!rng.collapsed && !(evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey) && (keyCode >= 65 && keyCode <=90
              || keyCode >= 48 && keyCode <= 57 ||
              keyCode >= 96 && keyCode <= 111 || {
              13:1,
              8:1,
              46:1
          }[keyCode])
              ){
  
              var tmpNode = rng.startContainer;
              if(domUtils.isFillChar(tmpNode)){
                  rng.setStartBefore(tmpNode)
              }
              tmpNode = rng.endContainer;
              if(domUtils.isFillChar(tmpNode)){
                  rng.setEndAfter(tmpNode)
              }
              rng.txtToElmBoundary();
              //结束边界可能放到了br的前边，要把br包含进来
              // x[xxx]<br/>
              if(rng.endContainer && rng.endContainer.nodeType == 1){
                  tmpNode = rng.endContainer.childNodes[rng.endOffset];
                  if(tmpNode && domUtils.isBr(tmpNode)){
                      rng.setEndAfter(tmpNode);
                  }
              }
              if(rng.startOffset == 0){
                  tmpNode = rng.startContainer;
                  if(domUtils.isBoundaryNode(tmpNode,'firstChild') ){
                      tmpNode = rng.endContainer;
                      if(rng.endOffset == (tmpNode.nodeType == 3 ? tmpNode.nodeValue.length : tmpNode.childNodes.length) && domUtils.isBoundaryNode(tmpNode,'lastChild')){
                          me.fireEvent('saveScene');
                          me.body.innerHTML = '<p>'+(browser.ie ? '' : '<br/>')+'</p>';
                          rng.setStart(me.body.firstChild,0).setCursor(false,true);
                          me._selectionChange();
                          return;
                      }
                  }
              }
          }
  
          //处理backspace
          if (keyCode == 8) {
              rng = me.selection.getRange();
              collapsed = rng.collapsed;
              if(me.fireEvent('delkeydown',evt)){
                  return;
              }
              var start,end;
              //避免按两次删除才能生效的问题
              if(rng.collapsed && rng.inFillChar()){
                  start = rng.startContainer;
  
                  if(domUtils.isFillChar(start)){
                      rng.setStartBefore(start).shrinkBoundary(true).collapse(true);
                      domUtils.remove(start)
                  }else{
                      start.nodeValue = start.nodeValue.replace(new RegExp('^' + domUtils.fillChar ),'');
                      rng.startOffset--;
                      rng.collapse(true).select(true)
                  }
              }
              //解决选中control元素不能删除的问题
              if (start = rng.getClosedNode()) {
                  me.fireEvent('saveScene');
                  rng.setStartBefore(start);
                  domUtils.remove(start);
                  rng.setCursor();
                  me.fireEvent('saveScene');
                  domUtils.preventDefault(evt);
                  return;
              }
              //阻止在table上的删除
              if (!browser.ie) {
                  start = domUtils.findParentByTagName(rng.startContainer, 'table', true);
                  end = domUtils.findParentByTagName(rng.endContainer, 'table', true);
                  if (start && !end || !start && end || start !== end) {
                      evt.preventDefault();
                      return;
                  }
              }
              start = rng.startContainer;
              if(rng.collapsed && start.nodeType == 1){
                  var currentNode = start.childNodes[rng.startOffset-1];
                  if(currentNode && currentNode.nodeType == 1 && currentNode.tagName == 'BR'){
                      me.fireEvent('saveScene');
                      rng.setStartBefore(currentNode).collapse(true);
                      domUtils.remove(currentNode);
                      rng.select();
                      me.fireEvent('saveScene');
                  }
              }
  
              //trace:3613
              if(browser.chrome){
                  if(rng.collapsed){
  
                      while(rng.startOffset == 0 && !domUtils.isEmptyBlock(rng.startContainer)){
                          rng.setStartBefore(rng.startContainer)
                      }
                      var pre = rng.startContainer.childNodes[rng.startOffset-1];
                      if(pre && pre.nodeName == 'BR'){
                          rng.setStartBefore(pre);
                          me.fireEvent('saveScene');
                          $(pre).remove();
                          rng.setCursor();
                          me.fireEvent('saveScene');
                      }
  
                  }
              }
          }
          //trace:1634
          //ff的del键在容器空的时候，也会删除
          if(browser.gecko && keyCode == 46){
              var range = me.selection.getRange();
              if(range.collapsed){
                  start = range.startContainer;
                  if(domUtils.isEmptyBlock(start)){
                      var parent = start.parentNode;
                      while(domUtils.getChildCount(parent) == 1 && !domUtils.isBody(parent)){
                          start = parent;
                          parent = parent.parentNode;
                      }
                      if(start === parent.lastChild)
                          evt.preventDefault();
                      return;
                  }
              }
          }
      });
      me.addListener('keyup', function(type, evt) {
          var keyCode = evt.keyCode || evt.which,
              rng,me = this;
          if(keyCode == 8){
              if(me.fireEvent('delkeyup')){
                  return;
              }
              rng = me.selection.getRange();
              if(rng.collapsed){
                  var tmpNode,
                      autoClearTagName = ['h1','h2','h3','h4','h5','h6'];
                  if(tmpNode = domUtils.findParentByTagName(rng.startContainer,autoClearTagName,true)){
                      if(domUtils.isEmptyBlock(tmpNode)){
                          var pre = tmpNode.previousSibling;
                          if(pre && pre.nodeName != 'TABLE'){
                              domUtils.remove(tmpNode);
                              rng.setStartAtLast(pre).setCursor(false,true);
                              return;
                          }else{
                              var next = tmpNode.nextSibling;
                              if(next && next.nodeName != 'TABLE'){
                                  domUtils.remove(tmpNode);
                                  rng.setStartAtFirst(next).setCursor(false,true);
                                  return;
                              }
                          }
                      }
                  }
                  //处理当删除到body时，要重新给p标签展位
                  if(domUtils.isBody(rng.startContainer)){
                      var tmpNode = domUtils.createElement(me.document,'p',{
                          'innerHTML' : browser.ie ? domUtils.fillChar : '<br/>'
                      });
                      rng.insertNode(tmpNode).setStart(tmpNode,0).setCursor(false,true);
                  }
              }
  
  
              //chrome下如果删除了inline标签，浏览器会有记忆，在输入文字还是会套上刚才删除的标签，所以这里再选一次就不会了
              if( !collapsed && (rng.startContainer.nodeType == 3 || rng.startContainer.nodeType == 1 && domUtils.isEmptyBlock(rng.startContainer))){
                  if(browser.ie){
                      var span = rng.document.createElement('span');
                      rng.insertNode(span).setStartBefore(span).collapse(true);
                      rng.select();
                      domUtils.remove(span)
                  }else{
                      rng.select()
                  }
  
              }
          }
  
      })
  };
  /**
   * 自动保存草稿
   */
  UM.plugins['autosave'] = function() {
  
  
      var me = this,
          //无限循环保护
          lastSaveTime = new Date(),
          //最小保存间隔时间
          MIN_TIME = 20,
          //auto save key
          saveKey = null;
  
  
      //默认间隔时间
      me.setOpt('saveInterval', 500);
  
      //存储媒介封装
      var LocalStorage = UM.LocalStorage = ( function () {
  
          var storage = window.localStorage || getUserData() || null,
              LOCAL_FILE = "localStorage";
  
          return {
  
              saveLocalData: function ( key, data ) {
  
                  if ( storage && data) {
                  	try {
  	                    storage.setItem( key, data  );
  	                    return true;
                  	} catch(e) {
                      	 return false;
                      }
                  }
  
                  return false;
  
              },
  
              getLocalData: function ( key ) {
  
                  if ( storage ) {
                      return storage.getItem( key );
                  }
  
                  return null;
  
              },
  
              removeItem: function ( key ) {
  
                  storage && storage.removeItem( key );
  
              }
  
          };
  
          function getUserData () {
  
              var container = document.createElement( "div" );
              container.style.display = "none";
  
              if( !container.addBehavior ) {
                  return null;
              }
  
              container.addBehavior("#default#userdata");
  
              return {
  
                  getItem: function ( key ) {
  
                      var result = null;
  
                      try {
                          document.body.appendChild( container );
                          container.load( LOCAL_FILE );
                          result = container.getAttribute( key );
                          document.body.removeChild( container );
                      } catch ( e ) {
                      }
  
                      return result;
  
                  },
  
                  setItem: function ( key, value ) {
  
                      document.body.appendChild( container );
                      container.setAttribute( key, value );
                      container.save( LOCAL_FILE );
                      document.body.removeChild( container );
  
                  },
  //               暂时没有用到
  //                clear: function () {
  //
  //                    var expiresTime = new Date();
  //                    expiresTime.setFullYear( expiresTime.getFullYear() - 1 );
  //                    document.body.appendChild( container );
  //                    container.expires = expiresTime.toUTCString();
  //                    container.save( LOCAL_FILE );
  //                    document.body.removeChild( container );
  //
  //                },
  
                  removeItem: function ( key ) {
  
                      document.body.appendChild( container );
                      container.removeAttribute( key );
                      container.save( LOCAL_FILE );
                      document.body.removeChild( container );
  
                  }
  
              };
  
          }
  
      } )();
  
      function save ( editor ) {
  
          var saveData = null;
  
          if ( new Date() - lastSaveTime < MIN_TIME ) {
              return;
          }
  
          if ( !editor.hasContents() ) {
              //这里不能调用命令来删除， 会造成事件死循环
              saveKey && LocalStorage.removeItem( saveKey );
              return;
          }
  
          lastSaveTime = new Date();
  
          editor._saveFlag = null;
  
          saveData = me.body.innerHTML;
  
          if ( editor.fireEvent( "beforeautosave", {
              content: saveData
          } ) === false ) {
              return;
          }
  
          LocalStorage.saveLocalData( saveKey, saveData );
  
          editor.fireEvent( "afterautosave", {
              content: saveData
          } );
  
      }
  
      me.addListener('ready', function(){
          var _suffix = "-drafts-data",
              key = null;
  
          if ( me.key ) {
              key = me.key + _suffix;
          } else {
              key = ( me.container.parentNode.id || 'ue-common' ) + _suffix;
          }
  
          //页面地址+编辑器ID 保持唯一
          saveKey = ( location.protocol + location.host + location.pathname ).replace( /[.:\/]/g, '_' ) + key;
      });
  
      me.addListener('contentchange', function(){
  
          if ( !saveKey ) {
              return;
          }
  
          if ( me._saveFlag ) {
              window.clearTimeout( me._saveFlag );
          }
  
          if ( me.options.saveInterval > 0 ) {
  
              me._saveFlag = window.setTimeout( function () {
  
                  save( me );
  
              }, me.options.saveInterval );
  
          } else {
  
              save(me);
  
          }
  
      })
  
  
      me.commands['clearlocaldata'] = {
          execCommand:function (cmd, name) {
              if ( saveKey && LocalStorage.getLocalData( saveKey ) ) {
                  LocalStorage.removeItem( saveKey )
              }
          },
          notNeedUndo: true,
          ignoreContentChange:true
      };
  
      me.commands['getlocaldata'] = {
          execCommand:function (cmd, name) {
              return saveKey ? LocalStorage.getLocalData( saveKey ) || '' : '';
          },
          notNeedUndo: true,
          ignoreContentChange:true
      };
  
      me.commands['drafts'] = {
          execCommand:function (cmd, name) {
              if ( saveKey ) {
                  me.body.innerHTML = LocalStorage.getLocalData( saveKey ) || '<p>'+(browser.ie ? '&nbsp;' : '<br/>')+'</p>';
                  me.focus(true);
              }
          },
          queryCommandState: function () {
              return saveKey ? ( LocalStorage.getLocalData( saveKey ) === null ? -1 : 0 ) : -1;
          },
          notNeedUndo: true,
          ignoreContentChange:true
      }
  
  };
  
  /**
   * @description
   * 1.拖放文件到编辑区域，自动上传并插入到选区
   * 2.插入粘贴板的图片，自动上传并插入到选区
   * @author Jinqn
   * @date 2013-10-14
   */
  UM.plugins['autoupload'] = function () {
  
      var me = this;
  
      me.setOpt('pasteImageEnabled', true);
      me.setOpt('dropFileEnabled', true);
      var sendAndInsertImage = function (file, editor) {
          //模拟数据
          var fd = new FormData();
          fd.append(editor.options.imageFieldName || 'upfile', file, file.name || ('blob.' + file.type.substr('image/'.length)));
          fd.append('type', 'ajax');
          var xhr = new XMLHttpRequest();
          xhr.open("post", me.options.imageUrl, true);
          xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
          xhr.addEventListener('load', function (e) {
              try {
                  var json = eval('('+e.target.response+')'),
                      link = json.url,
                      picLink = me.options.imagePath + link;
                  editor.execCommand('insertimage', {
                      src: picLink,
                      _src: picLink
                  });
              } catch (er) {
              }
          });
          xhr.send(fd);
      };
  
      function getPasteImage(e) {
          return e.clipboardData && e.clipboardData.items && e.clipboardData.items.length == 1 && /^image\//.test(e.clipboardData.items[0].type) ? e.clipboardData.items : null;
      }
  
      function getDropImage(e) {
          return  e.dataTransfer && e.dataTransfer.files ? e.dataTransfer.files : null;
      }
  
      me.addListener('ready', function () {
          if (window.FormData && window.FileReader) {
          	/*
              var autoUploadHandler = function (e) {
                  var hasImg = false,
                      items;
                  //获取粘贴板文件列表或者拖放文件列表
                  items = e.type == 'paste' ? getPasteImage(e.originalEvent) : getDropImage(e.originalEvent);
                  if (items) {
                      var len = items.length,
                          file;
                      while (len--) {
                          file = items[len];
                          if (file.getAsFile) file = file.getAsFile();
                          if (file && file.size > 0 && /image\/\w+/i.test(file.type)) {
                              sendAndInsertImage(file, me);
                              hasImg = true;
                          }
                      }
                      if (hasImg) return false;
                  }
  
              };
              me.getOpt('pasteImageEnabled') && me.$body.on('paste', autoUploadHandler);
              me.getOpt('dropFileEnabled') && me.$body.on('drop', autoUploadHandler);
  		*/
              
              //取消拖放图片时出现的文字光标位置提示
              me.$body.on('dragover', function (e) {
                  if (e.originalEvent.dataTransfer.types[0] == 'Files') {
                      return false;
                  }
              });
          }
      });
  
  };
  /**
   * 公式插件
   */
  UM.plugins['formula'] = function () {
      var me = this;
  
      function getActiveIframe() {
          return me.$body.find('iframe.edui-formula-active')[0] || null;
      }
  
      function blurActiveIframe(){
          var iframe = getActiveIframe();
          iframe && iframe.contentWindow.formula.blur();
      }
  
      me.addInputRule(function (root) {
          $.each(root.getNodesByTagName('span'), function (i, node) {
              if (node.hasClass('mathquill-embedded-latex')) {
                  var firstChild, latex = '';
                  while(firstChild = node.firstChild()){
                      latex += firstChild.data;
                      node.removeChild(firstChild);
                  }
                  node.tagName = 'iframe';
                  node.setAttr({
                      'frameborder': '0',
                      'src': me.getOpt('UMEDITOR_HOME_URL') + 'dialogs/formula/formula.html',
                      'data-latex': utils.unhtml(latex)
                  });
              }
          });
      });
      me.addOutputRule(function (root) {
          $.each(root.getNodesByTagName('iframe'), function (i, node) {
              if (node.hasClass('mathquill-embedded-latex')) {
                  node.tagName = 'span';
                  node.appendChild(UM.uNode.createText(node.getAttr('data-latex')));
                  node.setAttr({
                      'frameborder': '',
                      'src': '',
                      'data-latex': ''
                  });
              }
          });
      });
      me.addListener('click', function(){
          blurActiveIframe();
      });
      me.addListener('afterexeccommand', function(type, cmd){
          if(cmd != 'formula') {
              blurActiveIframe();
          }
      });
  
      me.commands['formula'] = {
          execCommand: function (cmd, latex) {
              var iframe = getActiveIframe();
              if (iframe) {
                  iframe.contentWindow.formula.insertLatex(latex);
              } else {
                  me.execCommand('inserthtml', '<span class="mathquill-embedded-latex">' + latex + '</span>');
                  browser.ie && browser.ie9below && setTimeout(function(){
                      var rng = me.selection.getRange(),
                          startContainer = rng.startContainer;
                      if(startContainer.nodeType == 1 && !startContainer.childNodes[rng.startOffset]){
                          rng.insertNode(me.document.createTextNode(' '));
                          rng.setCursor()
                      }
                  },100)
              }
          },
          queryCommandState: function (cmd) {
              return 0;
          },
          queryCommandValue: function (cmd) {
              var iframe = getActiveIframe();
              return iframe && iframe.contentWindow.formula.getLatex();
          }
      }
  
  };
  
  (function ($) {
      //对jquery的扩展
      $.parseTmpl = function parse(str, data) {
          var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' + 'with(obj||{}){__p.push(\'' + str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function (match, code) {
              return "'," + code.replace(/\\'/g, "'") + ",'";
          }).replace(/<%([\s\S]+?)%>/g,function (match, code) {
                  return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "__p.push('";
              }).replace(/\r/g, '\\r').replace(/\n/g, '\\n').replace(/\t/g, '\\t') + "');}return __p.join('');";
          var func = new Function('obj', tmpl);
          return data ? func(data) : func;
      };
      $.extend2 = function (t, s) {
          var a = arguments,
              notCover = $.type(a[a.length - 1]) == 'boolean' ? a[a.length - 1] : false,
              len = $.type(a[a.length - 1]) == 'boolean' ? a.length - 1 : a.length;
          for (var i = 1; i < len; i++) {
              var x = a[i];
              for (var k in x) {
                  if (!notCover || !t.hasOwnProperty(k)) {
                      t[k] = x[k];
                  }
              }
          }
          return t;
      };
  
      $.IE6 = !!window.ActiveXObject && parseFloat(navigator.userAgent.match(/msie (\d+)/i)[1]) == 6;
  
      //所有ui的基类
      var _eventHandler = [];
      var _widget = function () {
      };
      var _prefix = 'edui';
      _widget.prototype = {
          on: function (ev, cb) {
              this.root().on(ev, $.proxy(cb, this));
              return this;
          },
          off: function (ev, cb) {
              this.root().off(ev, $.proxy(cb, this));
              return this;
          },
          trigger: function (ev, data) {
              return  this.root().trigger(ev, data) === false ? false : this;
          },
          root: function ($el) {
              return this._$el || (this._$el = $el);
          },
          destroy: function () {
  
          },
          data: function (key, val) {
              if (val !== undefined) {
                  this.root().data(_prefix + key, val);
                  return this;
              } else {
                  return this.root().data(_prefix + key)
              }
          },
          register: function (eventName, $el, fn) {
              _eventHandler.push({
                  'evtname': eventName,
                  '$els': $.isArray($el) ? $el : [$el],
                  handler: $.proxy(fn, $el)
              })
          }
      };
  
      //从jq实例上拿到绑定的widget实例
      $.fn.edui = function (obj) {
          return obj ? this.data('eduiwidget', obj) : this.data('eduiwidget');
      };
  
      function _createClass(ClassObj, properties, supperClass) {
          ClassObj.prototype = $.extend2(
              $.extend({}, properties),
              (UM.ui[supperClass] || _widget).prototype,
              true
          );
          ClassObj.prototype.supper = (UM.ui[supperClass] || _widget).prototype;
          //父class的defaultOpt 合并
          if( UM.ui[supperClass] && UM.ui[supperClass].prototype.defaultOpt ) {
  
              var parentDefaultOptions = UM.ui[supperClass].prototype.defaultOpt,
                  subDefaultOptions = ClassObj.prototype.defaultOpt;
  
              ClassObj.prototype.defaultOpt = $.extend( {}, parentDefaultOptions, subDefaultOptions || {} );
  
          }
          return ClassObj
      }
  
      var _guid = 1;
  
      function mergeToJQ(ClassObj, className) {
          $[_prefix + className] = ClassObj;
          $.fn[_prefix + className] = function (opt) {
              var result, args = Array.prototype.slice.call(arguments, 1);
  
              this.each(function (i, el) {
                  var $this = $(el);
                  var obj = $this.edui();
                  if (!obj) {
                      ClassObj(!opt || !$.isPlainObject(opt) ? {} : opt, $this);
                      $this.edui(obj)
                  }
                  if ($.type(opt) == 'string') {
                      if (opt == 'this') {
                          result = obj;
                      } else {
                          result = obj[opt].apply(obj, args);
                          if (result !== obj && result !== undefined) {
                              return false;
                          }
                          result = null;
                      }
  
                  }
              });
  
              return result !== null ? result : this;
          }
      }
  
      UM.ui = {
          define: function (className, properties, supperClass) {
              var ClassObj = UM.ui[className] = _createClass(function (options, $el) {
                      var _obj = function () {
                      };
                      $.extend(_obj.prototype, ClassObj.prototype, {
                              guid: className + _guid++,
                              widgetName: className
                          }
                      );
                      var obj = new _obj;
                      if ($.type(options) == 'string') {
                          obj.init && obj.init({});
                          obj.root().edui(obj);
                          obj.root().find('a').click(function (evt) {
                              evt.preventDefault()
                          });
                          return obj.root()[_prefix + className].apply(obj.root(), arguments)
                      } else {
                          $el && obj.root($el);
                          obj.init && obj.init(!options || $.isPlainObject(options) ? $.extend2(options || {}, obj.defaultOpt || {}, true) : options);
                          try{
                              obj.root().find('a').click(function (evt) {
                                  evt.preventDefault()
                              });
                          }catch(e){
                          }
  
                          return obj.root().edui(obj);
                      }
  
                  },properties, supperClass);
  
              mergeToJQ(ClassObj, className);
          }
      };
  
      $(function () {
          $(document).on('click mouseup mousedown dblclick mouseover', function (evt) {
              $.each(_eventHandler, function (i, obj) {
                  if (obj.evtname == evt.type) {
                      $.each(obj.$els, function (i, $el) {
                          if ($el[0] !== evt.target && !$.contains($el[0], evt.target)) {
                              obj.handler(evt);
                          }
                      })
                  }
              })
          })
      })
  })(jQuery);
  //button 类
  UM.ui.define('button', {
  	// axiuno: 如果不是 texttype: <div class="edui-btn edui-btn-icon"..>，否则 <a class="edui-text-btn" ..>， 如果有 caret, 填补空白
  	//tpl: '{if(!texttype)} <div class="edui-btn edui-btn-{=icon}" {if(name)} edui-btn-name={name}'
      tpl: '<<%if(!texttype){%>div class="edui-btn edui-btn-<%=icon%> <%if(name){%>edui-btn-name-<%=name%><%}%>" unselectable="on" onmousedown="return false" <%}else{%>a class="edui-text-btn"<%}%>  <% if(title) {%> data-original-title="<%=title%>" <%};%>> ' +
          '<% if(icon) {%><div unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><% }; %><%if(text) {%><span unselectable="on" onmousedown="return false" class="edui-button-label"><%=text%></span><%}%>' +
          '<%if(html){%><%=html%><%}%>' +
          '<%if(caret && text){%><span class="edui-button-spacing"></span><%}%>' +
          '<% if(caret) {%><span unselectable="on" onmousedown="return false" class="edui-caret"></span><% };%></<%if(!texttype){%>div<%}else{%>a<%}%>>',
      defaultOpt: {
          text: '',
          title: '',
          html: '',	// axiuno: 增加一个参数，支持插入 HTML，比如 <input type="file" ..>
          icon: '',
          width: '',
          caret: false,
          texttype: false,
          click: function () {
          }
      },
      init: function (options) {
          var me = this;
  
          me.root($($.parseTmpl(me.tpl, options)))
              .click(function (evt) {
                  me.wrapclick(options.click, evt)
              });
  
          me.root().hover(function () {
              if(!me.root().hasClass("edui-disabled")){
                  me.root().toggleClass('edui-hover')
              }
          })
  
          return me;
      },
      wrapclick: function (fn, evt) {
          if (!this.disabled()) {
              this.root().trigger('wrapclick');
              $.proxy(fn, this, evt)()
          }
          return this;
      },
      label: function (text) {
          if (text === undefined) {
              return this.root().find('.edui-button-label').text();
          } else {
              this.root().find('.edui-button-label').text(text);
              return this;
          }
      },
      disabled: function (state) {
          if (state === undefined) {
              return this.root().hasClass('edui-disabled')
          }
          this.root().toggleClass('edui-disabled', state);
          if(this.root().hasClass('edui-disabled')){
              this.root().removeClass('edui-hover')
          }
          return this;
      },
      active: function (state) {
          if (state === undefined) {
              return this.root().hasClass('edui-active')
          }
          this.root().toggleClass('edui-active', state)
  
          return this;
      },
      mergeWith: function ($obj) {
          var me = this;
          me.data('$mergeObj', $obj);
          $obj.edui().data('$mergeObj', me.root());
          if (!$.contains(document.body, $obj[0])) {
              $obj.appendTo(me.root());
          }
          me.on('click',function () {
              me.wrapclick(function () {
                  $obj.edui().show();
              })
          }).register('click', me.root(), function (evt) {
                  $obj.hide()
              });
      }
  });
  //toolbar 类
  (function () {
      UM.ui.define('toolbar', {
          tpl: '<div class="edui-toolbar"  ><div class="edui-btn-toolbar" unselectable="on" onmousedown="return false"  ></div></div>'
            ,
          init: function () {
              var $root = this.root($(this.tpl));
              this.data('$btnToolbar', $root.find('.edui-btn-toolbar'))
          },
          appendToBtnmenu : function(data){
              var $cont = this.data('$btnToolbar');
              data = $.isArray(data) ? data : [data];
              $.each(data,function(i,$item){
                  $cont.append($item)
              })
          }
      });
  })();
  
  //menu 类
  UM.ui.define('menu',{
      show : function($obj,dir,fnname,topOffset,leftOffset){
  
          fnname = fnname || 'position';
          if(this.trigger('beforeshow') === false){
              return;
          }else{
              this.root().css($.extend({display:'block'},$obj ? {
                  top : $obj[fnname]().top + ( dir == 'right' ? 0 : $obj.outerHeight()) - (topOffset || 0),
                  left : $obj[fnname]().left + (dir == 'right' ?  $obj.outerWidth() : 0) -  (leftOffset || 0)
              }:{}))
              this.trigger('aftershow');
          }
      },
      hide : function(all){
          var $parentmenu;
          if(this.trigger('beforehide') === false){
              return;
          } else {
  
              if($parentmenu = this.root().data('parentmenu')){
                  if($parentmenu.data('parentmenu')|| all)
                      $parentmenu.edui().hide();
              }
              this.root().css('display','none');
              this.trigger('afterhide');
          }
      },
      attachTo : function($obj){
          var me = this;
          if(!$obj.data('$mergeObj')){
              $obj.data('$mergeObj',me.root());
              $obj.on('wrapclick',function(evt){
                  me.show()
              });
              me.register('click',$obj,function(evt){
                 me.hide()
              });
              me.data('$mergeObj',$obj)
          }
      }
  });
  //dropmenu 类
  UM.ui.define('dropmenu', {
      tmpl: '<ul class="edui-dropdown-menu" aria-labelledby="dropdownMenu" >' +
          '<%for(var i=0,ci;ci=data[i++];){%>' +
          '<%if(ci.divider){%><li class="edui-divider"></li><%}else{%>' +
          '<li <%if(ci.active||ci.disabled){%>class="<%= ci.active|| \'\' %> <%=ci.disabled||\'\' %>" <%}%> data-value="<%= ci.value%>">' +
          '<a href="#" tabindex="-1"><em class="edui-dropmenu-checkbox"><i class="edui-icon-ok"></i></em><%= ci.label%></a>' +
          '</li><%}%>' +
          '<%}%>' +
          '</ul>',
      defaultOpt: {
          data: [],
          click: function () {
  
          }
      },
      init: function (options) {
          var me = this;
          var eventName = {
              click: 1,
              mouseover: 1,
              mouseout: 1
          };
  
          this.root($($.parseTmpl(this.tmpl, options))).on('click', 'li[class!="edui-disabled edui-divider edui-dropdown-submenu"]',function (evt) {
              $.proxy(options.click, me, evt, $(this).data('value'), $(this))()
          }).find('li').each(function (i, el) {
                  var $this = $(this);
                  if (!$this.hasClass("edui-disabled edui-divider edui-dropdown-submenu")) {
                      var data = options.data[i];
                      $.each(eventName, function (k) {
                          data[k] && $this[k](function (evt) {
                              $.proxy(data[k], el)(evt, data, me.root)
                          })
                      })
                  }
              })
  
      },
      disabled: function (cb) {
          $('li[class!=edui-divider]', this.root()).each(function () {
              var $el = $(this);
              if (cb === true) {
                  $el.addClass('edui-disabled')
              } else if ($.isFunction(cb)) {
                  $el.toggleClass('edui-disabled', cb(li))
              } else {
                  $el.removeClass('edui-disabled')
              }
  
          });
      },
      val: function (val) {
          var currentVal;
          $('li[class!="edui-divider edui-disabled edui-dropdown-submenu"]', this.root()).each(function () {
              var $el = $(this);
              if (val === undefined) {
                  if ($el.find('em.edui-dropmenu-checked').length) {
                      currentVal = $el.data('value');
                      return false
                  }
              } else {
                  $el.find('em').toggleClass('edui-dropmenu-checked', $el.data('value') == val)
              }
          });
          if (val === undefined) {
              return currentVal
          }
      },
      addSubmenu: function (label, menu, index) {
          index = index || 0;
  
          var $list = $('li[class!=edui-divider]', this.root());
          var $node = $('<li class="edui-dropdown-submenu"><a tabindex="-1" href="#">' + label + '</a></li>').append(menu);
  
          if (index >= 0 && index < $list.length) {
              $node.insertBefore($list[index]);
          } else if (index < 0) {
              $node.insertBefore($list[0]);
          } else if (index >= $list.length) {
              $node.appendTo($list);
          }
      }
  }, 'menu');
  //splitbutton 类
  ///import button
  UM.ui.define('splitbutton',{
      tpl :'<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>"  unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn"  unselectable="on" ><%if(icon){%><div  unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><%if(text){%><%=text%><%}%></div>'+
              '<div  unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
                  '<div  unselectable="on" class="edui-caret"><\/div>'+
              '</div>'+
          '</div>',
      defaultOpt:{
          text:'',
          title:'',
          click:function(){}
      },
      init : function(options){
          var me = this;
          me.root( $($.parseTmpl(me.tpl,options)));
          me.root().find('.edui-btn:first').click(function(evt){
              if(!me.disabled()){
                  $.proxy(options.click,me)();
              }
          });
          me.root().find('.edui-dropdown-toggle').click(function(){
              if(!me.disabled()){
                  me.trigger('arrowclick')
              }
          });
          me.root().hover(function () {
              if(!me.root().hasClass("edui-disabled")){
                  me.root().toggleClass('edui-hover')
              }
          });
  
          return me;
      },
      wrapclick:function(fn,evt){
          if(!this.disabled()){
              $.proxy(fn,this,evt)()
          }
          return this;
      },
      disabled : function(state){
          if(state === undefined){
              return this.root().hasClass('edui-disabled')
          }
          this.root().toggleClass('edui-disabled',state).find('.edui-btn').toggleClass('edui-disabled',state);
          return this;
      },
      active:function(state){
          if(state === undefined){
              return this.root().hasClass('edui-active')
          }
          this.root().toggleClass('edui-active',state).find('.edui-btn:first').toggleClass('edui-active',state);
          return this;
      },
      mergeWith:function($obj){
          var me = this;
          me.data('$mergeObj',$obj);
          $obj.edui().data('$mergeObj',me.root());
          if(!$.contains(document.body,$obj[0])){
              $obj.appendTo(me.root());
          }
          me.root().delegate('.edui-dropdown-toggle','click',function(){
              me.wrapclick(function(){
                  $obj.edui().show();
              })
          });
          me.register('click',me.root().find('.edui-dropdown-toggle'),function(evt){
              $obj.hide()
          });
      }
  });
  /**
   * Created with JetBrains PhpStorm.
   * User: hn
   * Date: 13-7-10
   * Time: 下午3:07
   * To change this template use File | Settings | File Templates.
   */
  UM.ui.define('colorsplitbutton',{
  
      tpl : '<div class="edui-splitbutton <%if (name){%>edui-splitbutton-<%= name %><%}%>"  unselectable="on" <%if(title){%>data-original-title="<%=title%>"<%}%>><div class="edui-btn"  unselectable="on" ><%if(icon){%><div  unselectable="on" class="edui-icon-<%=icon%> edui-icon"></div><%}%><div class="edui-splitbutton-color-label" <%if (color) {%>style="background: <%=color%>"<%}%>></div><%if(text){%><%=text%><%}%></div>'+
              '<div  unselectable="on" class="edui-btn edui-dropdown-toggle" >'+
              '<div  unselectable="on" class="edui-caret"><\/div>'+
              '</div>'+
              '</div>',
      defaultOpt: {
          color: ''
      },
      init: function( options ){
  
          var me = this;
  
          me.supper.init.call( me, options );
  
      },
      colorLabel: function(){
          return this.root().find('.edui-splitbutton-color-label');
      }
  
  }, 'splitbutton');
  //popup 类
  UM.ui.define('popup', {
      tpl: '<div class="edui-dropdown-menu edui-popup"'+
          '<%if(!<%=stopprop%>){%>onmousedown="return false"<%}%>'+
          '><div class="edui-popup-body" unselectable="on" onmousedown="return false"><%=subtpl%></div>' +
          '<div class="edui-popup-caret"></div>' +
          '</div>',
      defaultOpt: {
          stopprop:false,
          subtpl: '',
          width: '',
          height: ''
      },
      init: function (options) {
          this.root($($.parseTmpl(this.tpl, options)));
          return this;
      },
      mergeTpl: function (data) {
          return $.parseTmpl(this.tpl, {subtpl: data});
      },
      show: function ($obj, posObj) {
          if (!posObj) posObj = {};
  
          var fnname = posObj.fnname || 'position';
          if (this.trigger('beforeshow') === false) {
              return;
          } else {
              this.root().css($.extend({display: 'block'}, $obj ? {
                  top: $obj[fnname]().top + ( posObj.dir == 'right' ? 0 : $obj.outerHeight()) - (posObj.offsetTop || 0),
                  left: $obj[fnname]().left + (posObj.dir == 'right' ? $obj.outerWidth() : 0) - (posObj.offsetLeft || 0),
                  position: 'absolute'
              } : {}));
  
              this.root().find('.edui-popup-caret').css({
                  top: posObj.caretTop || 0,
                  left: posObj.caretLeft || 0,
                  position: 'absolute'
              }).addClass(posObj.caretDir || "up")
  
          }
          this.trigger("aftershow");
      },
      hide: function () {
          this.root().css('display', 'none');
          this.trigger('afterhide')
      },
      attachTo: function ($obj, posObj) {
          var me = this
          if (!$obj.data('$mergeObj')) {
              $obj.data('$mergeObj', me.root());
              $obj.on('wrapclick', function (evt) {
                  me.show($obj, posObj)
              });
              me.register('click', $obj, function (evt) {
                  me.hide()
              });
              me.data('$mergeObj', $obj)
          }
      },
      getBodyContainer: function () {
          return this.root().find(".edui-popup-body");
      }
  });
  //scale 类
  UM.ui.define('scale', {
      tpl: '<div class="edui-scale" unselectable="on">' +
          '<span class="edui-scale-hand0"></span>' +
          '<span class="edui-scale-hand1"></span>' +
          '<span class="edui-scale-hand2"></span>' +
          '<span class="edui-scale-hand3"></span>' +
          '<span class="edui-scale-hand4"></span>' +
          '<span class="edui-scale-hand5"></span>' +
          '<span class="edui-scale-hand6"></span>' +
          '<span class="edui-scale-hand7"></span>' +
          '</div>',
      defaultOpt: {
          $doc: $(document),
          $wrap: $(document)
      },
      init: function (options) {
          if(options.$doc) this.defaultOpt.$doc = options.$doc;
          if(options.$wrap) this.defaultOpt.$wrap = options.$wrap;
          this.root($($.parseTmpl(this.tpl, options)));
          this.initStyle();
          this.startPos = this.prePos = {x: 0, y: 0};
          this.dragId = -1;
          return this;
      },
      initStyle: function () {
          utils.cssRule('edui-style-scale', '.edui-scale{display:none;position:absolute;border:1px solid #38B2CE;cursor:hand;}' +
              '.edui-scale span{position:absolute;left:0;top:0;width:7px;height:7px;overflow:hidden;font-size:0px;display:block;background-color:#3C9DD0;}'
              + '.edui-scale .edui-scale-hand0{cursor:nw-resize;top:0;margin-top:-4px;left:0;margin-left:-4px;}'
              + '.edui-scale .edui-scale-hand1{cursor:n-resize;top:0;margin-top:-4px;left:50%;margin-left:-4px;}'
              + '.edui-scale .edui-scale-hand2{cursor:ne-resize;top:0;margin-top:-4px;left:100%;margin-left:-3px;}'
              + '.edui-scale .edui-scale-hand3{cursor:w-resize;top:50%;margin-top:-4px;left:0;margin-left:-4px;}'
              + '.edui-scale .edui-scale-hand4{cursor:e-resize;top:50%;margin-top:-4px;left:100%;margin-left:-3px;}'
              + '.edui-scale .edui-scale-hand5{cursor:sw-resize;top:100%;margin-top:-3px;left:0;margin-left:-4px;}'
              + '.edui-scale .edui-scale-hand6{cursor:s-resize;top:100%;margin-top:-3px;left:50%;margin-left:-4px;}'
              + '.edui-scale .edui-scale-hand7{cursor:se-resize;top:100%;margin-top:-3px;left:100%;margin-left:-3px;}');
      },
      _eventHandler: function (e) {
          var me = this,
              $doc = me.defaultOpt.$doc;
              
              
              
          switch (e.type) {
              case 'mousedown':
                  var hand = e.target || e.srcElement, hand;
                  if (hand.className.indexOf('edui-scale-hand') != -1) {
                      me.dragId = hand.className.slice(-1);
                      me.startPos.x = me.prePos.x = e.clientX;
                      me.startPos.y = me.prePos.y = e.clientY;
                      $doc.bind('mousemove', $.proxy(me._eventHandler, me));
                  }
                  break;
              case 'mousemove':
              	return;
                  if (me.dragId != -1) {
                      var pos = {x: e.clientX - me.prePos.x, y: e.clientY - me.prePos.y};
                     // console.log(pos, e.clientX, e.clientY, me.prePos.x, me.prePos.y);
                      me.updateContainerStyle(me.dragId, pos);
                      me.prePos.x = e.clientX;
                      me.prePos.y = e.clientY;
                      me.updateTargetElement();
                  }
                  break;
              case 'mouseup':
                  if (me.dragId != -1) {
                      me.dragId = -1;
                      me.updateTargetElement();
                      var $target = me.data('$scaleTarget');
                      if ($target.parent()) me.attachTo(me.data('$scaleTarget'));
                  }
                  $doc.unbind('mousemove', $.proxy(me._eventHandler, me));
                  break;
              default:
                  break;
          }
      },
      updateTargetElement: function () {
          var me = this,
              $root = me.root(),
              $target = me.data('$scaleTarget');
          $target.css({width: $root.width(), height: $root.height()});
          me.attachTo($target);
      },
      updateContainerStyle: function (dir, offset) {
      	return;
          var me = this,
              $dom = me.root(),
              tmp,
              rect = [
                  //[left, top, width, height]
                  [0, 0, -1, -1],
                  [0, 0, 0, -1],
                  [0, 0, 1, -1],
                  [0, 0, -1, 0],
                  [0, 0, 1, 0],
                  [0, 0, -1, 1],
                  [0, 0, 0, 1],
                  [0, 0, 1, 1]
              ];
              
          // 等比缩放，by axiuno@gmail.com
          // 缩放的 img 标签，等比缩放需要参考它！
          /*
          var $target = me.data('$scaleTarget');
          var $target_scale = me.data('$target_scale');
          if(!$target_scale) {
          	
  //        	var img = new Image();
  //        	img.src = $target.attr('src');
  //        	var $target_width = img.width;
  //        	var $target_height = img.height;
          	
          	var $target_width = $target.width();
  	        var $target_height = $target.height();
  	        var $target_scale =  $target_height / $target_width;
  	        //alert($target_scale);
  	      
  	        me.data('$target_scale', $target_scale);
          }
          */
         // $target.css({width: $root.width(), height: $root.height()});
  
          if (rect[dir][0] != 0) {
              tmp = parseInt($dom.offset().left) + offset.x;
              $dom.css('left', me._validScaledProp('left', tmp));
          }
          if (rect[dir][1] != 0) {
              tmp = parseInt($dom.offset().top) + offset.y;
              $dom.css('top', me._validScaledProp('top', tmp));
          }
          if (rect[dir][2] != 0) {
              tmp = $dom.width() + rect[dir][2] * offset.x;
              $dom.css('width', me._validScaledProp('width', tmp));
          }
          if (rect[dir][3] != 0) {
             tmp = $dom.height() + rect[dir][3] * offset.y;
              //tmp = ($dom.width() * $target_scale)  + rect[dir][3] * offset.y; // 按照比例走
              //console.log(tmp);
              $dom.css('height', me._validScaledProp('height', tmp));
          }
      },
      _validScaledProp: function (prop, value) {
          var $ele = this.root(),
              $wrap = this.defaultOpt.$doc,
              calc = function(val, a, b){
                  return (val + a) > b ? b - a : value;
              };
  
          value = isNaN(value) ? 0 : value;
          switch (prop) {
              case 'left':
                  return value < 0 ? 0 : calc(value, $ele.width(), $wrap.width());
              case 'top':
                  return value < 0 ? 0 : calc(value, $ele.height(),$wrap.height());
              case 'width':
                  return value <= 0 ? 1 : calc(value, $ele.offset().left, $wrap.width());
              case 'height':
                  return value <= 0 ? 1 : calc(value, $ele.offset().top, $wrap.height());
          }
      },
      show: function ($obj) {
          var me = this;
          if ($obj) me.attachTo($obj);
          me.root().bind('mousedown', $.proxy(me._eventHandler, me));
          me.defaultOpt.$doc.bind('mouseup', $.proxy(me._eventHandler, me));
          me.root().show();
          me.trigger("aftershow");
      },
      hide: function () {
          var me = this;
          me.root().unbind('mousedown', $.proxy(me._eventHandler, me));
          me.defaultOpt.$doc.unbind('mouseup', $.proxy(me._eventHandler, me));
          me.root().hide();
          me.trigger('afterhide')
      },
      attachTo: function ($obj) {
          var me = this,
              imgPos = $obj.offset(),
              $root = me.root(),
              $wrap = me.defaultOpt.$wrap,
              posObj = $wrap.offset();
  
          me.data('$scaleTarget', $obj);
          me.root().css({
              position: 'absolute',
              width: $obj.width(),
              height: $obj.height(),
              left: imgPos.left - posObj.left - parseInt($wrap.css('border-left-width')) - parseInt($root.css('border-left-width')),
              top: imgPos.top - posObj.top - parseInt($wrap.css('border-top-width')) - parseInt($root.css('border-top-width'))
          });
      },
      getScaleTarget: function () {
          return this.data('$scaleTarget')[0];
      }
  });
  //colorpicker 类
  UM.ui.define('colorpicker', {
      tpl: function (opt) {
          var COLORS = (
              'ffffff,000000,eeece1,1f497d,4f81bd,c0504d,9bbb59,8064a2,4bacc6,f79646,' +
                  'f2f2f2,7f7f7f,ddd9c3,c6d9f0,dbe5f1,f2dcdb,ebf1dd,e5e0ec,dbeef3,fdeada,' +
                  'd8d8d8,595959,c4bd97,8db3e2,b8cce4,e5b9b7,d7e3bc,ccc1d9,b7dde8,fbd5b5,' +
                  'bfbfbf,3f3f3f,938953,548dd4,95b3d7,d99694,c3d69b,b2a2c7,92cddc,fac08f,' +
                  'a5a5a5,262626,494429,17365d,366092,953734,76923c,5f497a,31859b,e36c09,' +
                  '7f7f7f,0c0c0c,1d1b10,0f243e,244061,632423,4f6128,3f3151,205867,974806,' +
                  'c00000,ff0000,ffc000,ffff00,92d050,00b050,00b0f0,0070c0,002060,7030a0,').split(',');
  
          var html = '<div unselectable="on" onmousedown="return false" class="edui-colorpicker<%if (name){%> edui-colorpicker-<%=name%><%}%>" >' +
              '<table unselectable="on" onmousedown="return false">' +
              '<tr><td colspan="10">'+opt.lang_themeColor+'</td> </tr>' +
              '<tr class="edui-colorpicker-firstrow" >';
  
          for (var i = 0; i < COLORS.length; i++) {
              if (i && i % 10 === 0) {
                  html += '</tr>' + (i == 60 ? '<tr><td colspan="10">'+opt.lang_standardColor+'</td></tr>' : '') + '<tr' + (i == 60 ? ' class="edui-colorpicker-firstrow"' : '') + '>';
              }
              html += i < 70 ? '<td><a unselectable="on" onmousedown="return false" title="' + COLORS[i] + '" class="edui-colorpicker-colorcell"' +
                  ' data-color="#' + COLORS[i] + '"' +
                  ' style="background-color:#' + COLORS[i] + ';border:solid #ccc;' +
                  (i < 10 || i >= 60 ? 'border-width:1px;' :
                      i >= 10 && i < 20 ? 'border-width:1px 1px 0 1px;' :
                          'border-width:0 1px 0 1px;') +
                  '"' +
                  '></a></td>' : '';
          }
          html += '</tr></table></div>';
          return html;
      },
      init: function (options) {
          var me = this;
          me.root($($.parseTmpl(me.supper.mergeTpl(me.tpl(options)),options)));
  
          me.root().on("click",function (e) {
              me.trigger('pickcolor',  $(e.target).data('color'));
          });
      }
  }, 'popup');
  /**
   * Created with JetBrains PhpStorm.
   * User: hn
   * Date: 13-5-29
   * Time: 下午8:01
   * To change this template use File | Settings | File Templates.
   */
  
  (function(){
  
      var widgetName = 'combobox',
          itemClassName = 'edui-combobox-item',
          HOVER_CLASS = 'edui-combobox-item-hover',
          ICON_CLASS = 'edui-combobox-checked-icon',
          labelClassName = 'edui-combobox-item-label';
  
      UM.ui.define( widgetName, ( function(){
  
          return {
              tpl: "<ul class=\"dropdown-menu edui-combobox-menu<%if (comboboxName!=='') {%> edui-combobox-<%=comboboxName%><%}%>\" unselectable=\"on\" onmousedown=\"return false\" role=\"menu\" aria-labelledby=\"dropdownMenu\">" +
                  "<%if(autoRecord) {%>" +
                  "<%for( var i=0, len = recordStack.length; i<len; i++ ) {%>" +
                  "<%var index = recordStack[i];%>" +
                  "<li class=\"<%=itemClassName%><%if( selected == index ) {%> edui-combobox-checked<%}%>\" data-item-index=\"<%=index%>\" unselectable=\"on\" onmousedown=\"return false\">" +
                  "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
                  "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ index ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=items[index]%></label>" +
                  "</li>" +
                  "<%}%>" +
                  "<%if( i ) {%>" +
                  "<li class=\"edui-combobox-item-separator\"></li>" +
                  "<%}%>" +
                  "<%}%>" +
                  "<%for( var i=0, label; label = items[i]; i++ ) {%>" +
                  "<li class=\"<%=itemClassName%><%if( selected == i ) {%> edui-combobox-checked<%}%> edui-combobox-item-<%=i%>\" data-item-index=\"<%=i%>\" unselectable=\"on\" onmousedown=\"return false\">" +
                  "<span class=\"edui-combobox-icon\" unselectable=\"on\" onmousedown=\"return false\"></span>" +
                  "<label class=\"<%=labelClassName%>\" style=\"<%=itemStyles[ i ]%>\" unselectable=\"on\" onmousedown=\"return false\"><%=label%></label>" +
                  "</li>" +
                  "<%}%>" +
                  "</ul>",
              defaultOpt: {
                  //记录栈初始列表
                  recordStack: [],
                  //可用项列表
                  items: [],
  		        //item对应的值列表
                  value: [],
                  comboboxName: '',
                  selected: '',
                  //自动记录
                  autoRecord: true,
                  //最多记录条数
                  recordCount: 5
              },
              init: function( options ){
  
                  var me = this;
  
                  $.extend( me._optionAdaptation( options ), me._createItemMapping( options.recordStack, options.items ), {
                      itemClassName: itemClassName,
                      iconClass: ICON_CLASS,
                      labelClassName: labelClassName
                  } );
  
                  this._transStack( options );
  
                  me.root( $( $.parseTmpl( me.tpl, options ) ) );
  
                  this.data( 'options', options ).initEvent();
  
              },
              initEvent: function(){
  
                  var me = this;
  
                  me.initSelectItem();
  
                  this.initItemActive();
  
              },
              /**
               * 初始化选择项
               */
              initSelectItem: function(){
  
                  var me = this,
                      labelClass = "."+labelClassName;
  
                  me.root().delegate('.' + itemClassName, 'click', function(){
  
                      var $li = $(this),
                          index = $li.attr('data-item-index');
  
                      me.trigger('comboboxselect', {
                          index: index,
                          label: $li.find(labelClass).text(),
                          value: me.data('options').value[ index ]
                      }).select( index );
  
                      me.hide();
  
                      return false;
  
                  });
  
              },
              initItemActive: function(){
                  var fn = {
                      mouseenter: 'addClass',
                      mouseleave: 'removeClass'
                  };
                  if ($.IE6) {
                      this.root().delegate( '.'+itemClassName,  'mouseenter mouseleave', function( evt ){
                          $(this)[ fn[ evt.type ] ]( HOVER_CLASS );
                      }).one('afterhide', function(){
                      });
                  }
              },
              /**
               * 选择给定索引的项
               * @param index 项索引
               * @returns {*} 如果存在对应索引的项，则返回该项；否则返回null
               */
              select: function( index ){
  
                  var itemCount = this.data('options').itemCount,
                      items = this.data('options').autowidthitem;
  
                  if ( items && !items.length ) {
                      items = this.data('options').items;
                  }
  
                  if( itemCount == 0 ) {
                      return null;
                  }
  
                  if( index < 0 ) {
  
                      index = itemCount + index % itemCount;
  
                  } else if ( index >= itemCount ) {
  
                      index = itemCount-1;
  
                  }
  
                  this.trigger( 'changebefore', items[ index ] );
  
                  this._update( index );
  
                  this.trigger( 'changeafter', items[ index ] );
  
                  return null;
  
              },
              selectItemByLabel: function( label ){
  
                  var itemMapping = this.data('options').itemMapping,
                      me = this,
                      index = null;
  
                  !$.isArray( label ) && ( label = [ label ] );
  
                  $.each( label, function( i, item ){
  
                      index = itemMapping[ item ];
  
                      if( index !== undefined ) {
  
                          me.select( index );
                          return false;
  
                      }
  
                  } );
  
              },
              /**
               * 转换记录栈
               */
              _transStack: function( options ) {
  
                  var temp = [],
                      itemIndex = -1,
                      selected = -1;
  
                  $.each( options.recordStack, function( index, item ){
  
                      itemIndex = options.itemMapping[ item ];
  
                      if( $.isNumeric( itemIndex ) ) {
  
                          temp.push( itemIndex );
  
                          //selected的合法性检测
                          if( item == options.selected ) {
                              selected = itemIndex;
                          }
  
                      }
  
                  } );
  
                  options.recordStack = temp;
                  options.selected = selected;
                  temp = null;
  
              },
              _optionAdaptation: function( options ) {
  
                  if( !( 'itemStyles' in options ) ) {
  
                      options.itemStyles = [];
  
                      for( var i = 0, len = options.items.length; i < len; i++ ) {
                          options.itemStyles.push('');
                      }
  
                  }
  
                  options.autowidthitem = options.autowidthitem || options.items;
                  options.itemCount = options.items.length;
  
                  return options;
  
              },
              _createItemMapping: function( stackItem, items ){
  
                  var temp = {},
                      result = {
                          recordStack: [],
                          mapping: {}
                      };
  
                  $.each( items, function( index, item ){
                      temp[ item ] = index;
                  } );
  
                  result.itemMapping = temp;
  
                  $.each( stackItem, function( index, item ){
  
                      if( temp[ item ] !== undefined ) {
                          result.recordStack.push( temp[ item ] );
                          result.mapping[ item ] = temp[ item ];
                      }
  
                  } );
  
                  return result;
  
              },
              _update: function ( index ) {
  
                  var options = this.data("options"),
                      newStack = [],
                      newChilds = null;
  
                  $.each( options.recordStack, function( i, item ){
  
                      if( item != index ) {
                          newStack.push( item );
                      }
  
                  } );
  
                  //压入最新的记录
                  newStack.unshift( index );
  
                  if( newStack.length > options.recordCount ) {
                      newStack.length = options.recordCount;
                  }
  
                  options.recordStack = newStack;
                  options.selected = index;
  
                  newChilds = $( $.parseTmpl( this.tpl, options ) );
  
                  //重新渲染
                  this.root().html( newChilds.html() );
  
                  newChilds = null;
                  newStack = null;
  
              }
          };
  
      } )(), 'menu' );
  
  })();
  
  /**
   * Combox 抽象基类
   * User: hn
   * Date: 13-5-29
   * Time: 下午8:01
   * To change this template use File | Settings | File Templates.
   */
  
  (function(){
  
      var widgetName = 'buttoncombobox';
  
      UM.ui.define( widgetName, ( function(){
  
          return {
              defaultOpt: {
                  //按钮初始文字
                  label: '',
                  title: ''
              },
              init: function( options ) {
  
                  var me = this;
  
                  var btnWidget = $.eduibutton({
                      caret: true,
                      name: options.comboboxName,
                      title: options.title,
                      text: options.label,
                      click: function(){
                          me.show( this.root() );
                      }
                  });
  
                  me.supper.init.call( me, options );
  
                  //监听change， 改变button显示内容
                  me.on('changebefore', function( e, label ){
                      btnWidget.eduibutton('label', label );
                  });
  
                  me.data( 'button', btnWidget );
  
                  me.attachTo(btnWidget)
  
              },
              button: function(){
                  return this.data( 'button' );
              }
          }
  
      } )(), 'combobox' );
  
  })();
  
  /*modal 类*/
  UM.ui.define('modal', {
      tpl: '<div class="edui-modal" tabindex="-1" >' +
          '<div class="edui-modal-header">' +
          '<div class="edui-close" data-hide="modal"></div>' +
          '<h3 class="edui-title"><%=title%></h3>' +
          '</div>' +
          '<div class="edui-modal-body"  style="<%if(width){%>width:<%=width%>px;<%}%>' +
          '<%if(height){%>height:<%=height%>px;<%}%>">' +
          ' </div>' +
          '<% if(cancellabel || oklabel) {%>' +
          '<div class="edui-modal-footer">' +
          '<div class="edui-modal-tip"></div>' +
          '<%if(oklabel){%><div class="edui-btn edui-btn-primary" data-ok="modal"><%=oklabel%></div><%}%>' +
          '<%if(cancellabel){%><div class="edui-btn" data-hide="modal"><%=cancellabel%></div><%}%>' +
          '</div>' +
          '<%}%></div>',
      defaultOpt: {
          title: "",
          cancellabel: "",
          oklabel: "",
          width: '',
          height: '',
          backdrop: true,
          keyboard: true
      },
      init: function (options) {
          var me = this;
  
          me.root($($.parseTmpl(me.tpl, options || {})));
  
          me.data("options", options);
          if (options.okFn) {
              me.on('ok', $.proxy(options.okFn, me))
          }
          if (options.cancelFn) {
              me.on('beforehide', $.proxy(options.cancelFn, me))
          }
  
          me.root().delegate('[data-hide="modal"]', 'click', $.proxy(me.hide, me))
              .delegate('[data-ok="modal"]', 'click', $.proxy(me.ok, me));
  
          $('[data-hide="modal"],[data-ok="modal"]',me.root()).hover(function(){
              $(this).toggleClass('edui-hover')
          });
      },
      toggle: function () {
          var me = this;
          return me[!me.data("isShown") ? 'show' : 'hide']();
      },
      show: function () {
  
          var me = this;
  
          me.trigger("beforeshow");
  
          if (me.data("isShown")) return;
  
          me.data("isShown", true);
  
          me.escape();
  
          me.backdrop(function () {
              me.autoCenter();
              me.root()
                  .show()
                  .focus()
                  .trigger('aftershow');
          })
      },
      showTip: function ( text ) {
          $( '.edui-modal-tip', this.root() ).html( text ).fadeIn();
      },
      hideTip: function ( text ) {
          $( '.edui-modal-tip', this.root() ).fadeOut( function (){
              $(this).html('');
          } );
      },
      autoCenter: function () {
          //ie6下不用处理了
          !$.IE6 && this.root().css("margin-left", -(this.root().width() / 2));
      },
      hide: function () {
          var me = this;
  
          me.trigger("beforehide");
  
          if (!me.data("isShown")) return;
  
          me.data("isShown", false);
  
          me.escape();
  
          me.hideModal();
      },
      escape: function () {
          var me = this;
          if (me.data("isShown") && me.data("options").keyboard) {
              me.root().on('keyup', function (e) {
                  e.which == 27 && me.hide();
              })
          }
          else if (!me.data("isShown")) {
              me.root().off('keyup');
          }
      },
      hideModal: function () {
          var me = this;
          me.root().hide();
          me.backdrop(function () {
              me.removeBackdrop();
              me.trigger('afterhide');
          })
      },
      removeBackdrop: function () {
          this.$backdrop && this.$backdrop.remove();
          this.$backdrop = null;
      },
      backdrop: function (callback) {
          var me = this;
          if (me.data("isShown") && me.data("options").backdrop) {
              me.$backdrop = $('<div class="edui-modal-backdrop" />').click(
                  me.data("options").backdrop == 'static' ?
                      $.proxy(me.root()[0].focus, me.root()[0])
                      : $.proxy(me.hide, me)
              )
          }
          me.trigger('afterbackdrop');
          callback && callback();
  
      },
      attachTo: function ($obj) {
          var me = this
          if (!$obj.data('$mergeObj')) {
  
              $obj.data('$mergeObj', me.root());
              $obj.on('click', function () {
                  me.toggle($obj)
              });
              me.data('$mergeObj', $obj)
          }
      },
      ok: function () {
          var me = this;
          me.trigger('beforeok');
          if (me.trigger("ok", me) === false) {
              return;
          }
          me.hide();
      },
      getBodyContainer: function () {
          return this.root().find('.edui-modal-body')
      }
  });
  
  
  /*tooltip 类*/
  UM.ui.define('tooltip', {
      tpl: '<div class="edui-tooltip" unselectable="on" onmousedown="return false">' +
          '<div class="edui-tooltip-arrow" unselectable="on" onmousedown="return false"></div>' +
          '<div class="edui-tooltip-inner" unselectable="on" onmousedown="return false"></div>' +
          '</div>',
      init: function (options) {
          var me = this;
          me.root($($.parseTmpl(me.tpl, options || {})));
      },
      content: function (e) {
          var me = this,
              title = $(e.currentTarget).attr("data-original-title");
  
          me.root().find('.edui-tooltip-inner')['text'](title);
      },
      position: function (e) {
          var me = this,
              $obj = $(e.currentTarget);
  
          me.root().css($.extend({display: 'block'}, $obj ? {
              top: $obj.outerHeight(),
              left: (($obj.outerWidth() - me.root().outerWidth()) / 2)
          } : {}))
      },
      show: function (e) {
          if ($(e.currentTarget).hasClass('edui-disabled')) return;
  
          var me = this;
          me.content(e);
          me.root().appendTo($(e.currentTarget));
          me.position(e);
          me.root().css('display', 'block');
      },
      hide: function () {
          var me = this;
          me.root().css('display', 'none')
      },
      attachTo: function ($obj) {
          var me = this;
  
          function tmp($obj) {
              var me = this;
  
              if (!$.contains(document.body, me.root()[0])) {
                  me.root().appendTo($obj);
              }
  
              me.data('tooltip', me.root());
  
              $obj.each(function () {
                  if ($(this).attr("data-original-title")) {
                      $(this).on('mouseenter', $.proxy(me.show, me))
                          .on('mouseleave click', $.proxy(me.hide, me))
  
                  }
              });
  
          }
  
          if ($.type($obj) === "undefined") {
              $("[data-original-title]").each(function (i, el) {
                  tmp.call(me, $(el));
              })
  
          } else {
              if (!$obj.data('tooltip')) {
                  tmp.call(me, $obj);
              }
          }
      }
  });
  
  /*tab 类*/
  UM.ui.define('tab', {
      init: function (options) {
          var me = this,
              slr = options.selector;
  
          if ($.type(slr)) {
              me.root($(slr, options.context));
              me.data("context", options.context);
  
              $(slr, me.data("context")).on('click', function (e) {
                  me.show(e);
              });
          }
      },
      show: function (e) {
  
          var me = this,
              $cur = $(e.target),
              $ul = $cur.closest('ul'),
              selector,
              previous,
              $target,
              e;
  
          selector = $cur.attr('data-context');
          selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '');
  
          var $tmp = $cur.parent('li');
  
          if (!$tmp.length || $tmp.hasClass('edui-active')) return;
  
          previous = $ul.find('.edui-active:last a')[0];
  
          e = $.Event('beforeshow', {
              target: $cur[0],
              relatedTarget: previous
          });
  
          me.trigger(e);
  
          if (e.isDefaultPrevented()) return;
  
          $target = $(selector, me.data("context"));
  
          me.activate($cur.parent('li'), $ul);
          me.activate($target, $target.parent(), function () {
              me.trigger({
                  type: 'aftershow', relatedTarget: previous
              })
          });
      },
      activate: function (element, container, callback) {
          if (element === undefined) {
              return $(".edui-tab-item.edui-active",this.root()).index();
          }
  
          var $active = container.find('> .edui-active');
  
          $active.removeClass('edui-active');
  
          element.addClass('edui-active');
  
          callback && callback();
      }
  });
  
  
  //button 类
  UM.ui.define('separator', {
      tpl: '<div class="edui-separator" unselectable="on" onmousedown="return false" ></div>',
      init: function (options) {
          var me = this;
          me.root($($.parseTmpl(me.tpl, options)));
          return me;
      }
  });
  /**
   * @file adapter.js
   * @desc adapt ui to editor
   * @import core/Editor.js, core/utils.js
   */
  
  (function () {
      var _editorUI = {},
          _editors = {},
          _readyFn = [],
          _activeWidget = null,
          _widgetData = {},
          _widgetCallBack = {},
          _cacheUI = {},
          _maxZIndex = null;
  
          // axiuno: 对 UM 进行扩展，增加一些方法 
      utils.extend(UM, {
          defaultWidth : 500,
          defaultHeight : 500,
          registerUI: function (name, fn) {
              utils.each(name.split(/\s+/), function (uiname) {
                  _editorUI[uiname] = fn;
              })
          },
  
          setEditor : function(editor){
              !_editors[editor.id] && (_editors[editor.id] = editor);
          },
          registerWidget : function(name,pro,cb){
              _widgetData[name] = $.extend2(pro,{
                  $root : '',
                  _preventDefault:false,
                  root:function($el){
                      return this.$root || (this.$root = $el);
                  },
                  preventDefault:function(){
                      this._preventDefault = true;
                  },
                  clear:false
              });
              if(cb){
                  _widgetCallBack[name] = cb;
              }
          },
          getWidgetData : function(name){
              return _widgetData[name]
          },
          setWidgetBody : function(name,$widget,editor){
              if(!editor._widgetData){
  
                  utils.extend(editor,{
                      _widgetData : {},
                      getWidgetData : function(name){
                          return this._widgetData[name];
                      },
                      getWidgetCallback : function(widgetName){
                          var me = this;
                          return function(){
                              return  _widgetCallBack[widgetName].apply(me,[me,$widget].concat(Array.prototype.slice.call(arguments,0)))
                          }
                      }
                  })
  
              }
              var pro = _widgetData[name];
              if(!pro){
                  return null;
              }
              pro = editor._widgetData[name];
              if(!pro){
                  pro = _widgetData[name];
                  pro = editor._widgetData[name] = $.type(pro) == 'function' ? pro : utils.clone(pro);
              }
  
              pro.root($widget.edui().getBodyContainer());
  
              pro.initContent(editor,$widget);
              if(!pro._preventDefault){
                  pro.initEvent(editor,$widget);
              }
  
              pro.width &&  $widget.width(pro.width);
  
  
          },
          setActiveWidget : function($widget){
              _activeWidget = $widget;
          },
          // axiuno: 此处 new 编辑器
          getEditor: function (id, options) {
              var editor = _editors[id] || (_editors[id] = this.createEditor(id, options));
              _maxZIndex = _maxZIndex ? Math.max(editor.getOpt('zIndex'), _maxZIndex):editor.getOpt('zIndex');
              return editor;
          },
          setTopEditor: function(editor){
              $.each(_editors, function(i, o){
                  if(editor == o) {
                      //editor.$container && editor.$container.css('zIndex', _maxZIndex + 1);
                  } else {
                      o.$container && o.$container.css('zIndex', o.getOpt('zIndex'));
                  }
              });
          },
          clearCache : function(id){
              if ( _editors[id]) {
                  delete  _editors[id]
              }
          },
          delEditor: function (id) {
              var editor;
              if (editor = _editors[id]) {
                  editor.destroy();
              }
          },
          ready: function( fn ){
              _readyFn.push( fn );
          },
          createEditor: function (id, opt) {
              var editor = new UM.Editor(opt);
              var T = this;
  
              editor.langIsReady ? $.proxy(renderUI,T)() : editor.addListener("langReady", $.proxy(renderUI,T));
              function renderUI(){
  
  
                  var $container = this.createUI('#' + id, editor);
                  editor.key=id;
                  editor.ready(function(){
                      $.each( _readyFn, function( index, fn ){
                          $.proxy( fn, editor )();
                      } );
                  });
                  var options = editor.options;
                  if(options.initialFrameWidth){
                      options.minFrameWidth = options.initialFrameWidth
                  }else{
                  	// axiuno: width() -> outerWidth()
                  	var editor_body_width = editor.$body.css('width').indexOf('%') == -1 ? editor.$body.outerWidth() : editor.$body.css('width');
                  	editor_body_width = '100%';
                      options.minFrameWidth = options.initialFrameWidth = editor_body_width || UM.defaultWidth;
                  }
  		// axiuno: 编辑器改为百分之百的宽度
                  $container.css({
                      width: options.initialFrameWidth,
                      zIndex:editor.getOpt('zIndex')
                  });
  
                  //ie6下缓存图片
                  UM.browser.ie && UM.browser.version === 6 && document.execCommand("BackgroundImageCache", false, true);
  
                  editor.render(id);
  
  
                  //添加tooltip;
                  $.eduitooltip && $.eduitooltip('attachTo', $("[data-original-title]",$container)).css('z-index',editor.getOpt('zIndex')+1);
  
                  $container.find('a').click(function(evt){
                      evt.preventDefault()
                  });
  
                  editor.fireEvent("afteruiready");
              }
  
              return editor;
  
          },
          createUI: function (id, editor) {
              var $editorCont = $(id),
                  $container = $('<div class="edui-container"><div class="edui-editor-body message"></div></div>').insertBefore($editorCont);
              editor.$container = $container;
              editor.container = $container[0];
  
              editor.$body = $editorCont;
  
              //修正在ie9+以上的版本中，自动长高收起时的，残影问题
              if(browser.ie && browser.ie9above){
                  var $span = $('<span style="padding:0;margin:0;height:0;width:0"></span>');
                  $span.insertAfter($container);
              }
              //初始化注册的ui组件
              $.each(_editorUI,function(n,v){
                  var widget = v.call(editor,n);
                  if(widget){
                      _cacheUI[n] = widget;
                  }
  
              });
  
              $container.find('.edui-editor-body').append($editorCont).before(this.createToolbar(editor.options, editor));
  
              $container.find('.edui-toolbar').append($('<div class="edui-dialog-container"></div>'));
  
  
              return $container;
          },
          createToolbar: function (options, editor) {
              var $toolbar = $.eduitoolbar(), toolbar = $toolbar.edui();
              //创建下来菜单列表
  
              if (options.toolbar && options.toolbar.length) {
                  var btns = [];
                  $.each(options.toolbar,function(i,uiNames){
                      $.each(uiNames.split(/\s+/),function(index,name){
                          if(name == '|'){
                                  $.eduiseparator && btns.push($.eduiseparator());
                          }else{
                              var ui = _cacheUI[name];
                              if(name=="fullscreen"){
                                  ui&&btns.unshift(ui);
                              }else{
                                  ui && btns.push(ui);
                              }
                          }
  
                      });
                      btns.length && toolbar.appendToBtnmenu(btns);
                  });
              } else {
                  $toolbar.find('.edui-btn-toolbar').remove()
              }
              return $toolbar;
          }
  
      })
  
  
  })();
  
  
  
  UM.registerUI('bold italic redo undo underline strikethrough superscript subscript insertorderedlist insertunorderedlist ' +
      'cleardoc selectall link unlink print preview justifyleft justifycenter justifyright justifyfull removeformat horizontal drafts',
      function(name) {
          var me = this;
          var $btn = $.eduibutton({
              icon : name,
              click : function(){
                  me.execCommand(name);
              },
              title: this.getLang('labelMap')[name] || ''
          });
  
          this.addListener('selectionchange',function(){
              var state = this.queryCommandState(name);
              $btn.edui().disabled(state == -1).active(state == 1)
          });
          return $btn;
      }
  );
  
  
  /**
   * 全屏组件
   */
  
  (function(){
  
      //状态缓存
      var STATUS_CACHE = {},
      //状态值列表
          STATUS_LIST = [ 'width', 'height', 'position', 'top', 'left', 'margin', 'padding', 'overflowX', 'overflowY' ],
          CONTENT_AREA_STATUS = {},
      //页面状态
          DOCUMENT_STATUS = {},
          DOCUMENT_ELEMENT_STATUS = {},
  
          FULLSCREENS = {};
  
  
      UM.registerUI('fullscreen', function( name ){
  
          var me = this,
              $button = $.eduibutton({
                  'icon': 'fullscreen',
                  'title': (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
                  'click': function(){
                      //切换
                      me.execCommand( name );
                      UM.setTopEditor(me);
                  }
              });
  
          me.addListener( "selectionchange", function () {
  
              var state = this.queryCommandState( name );
              $button.edui().disabled( state == -1 ).active( state == 1 );
  
          } );
  
          //切换至全屏
          me.addListener('ready', function(){
  
              me.options.fullscreen && Fullscreen.getInstance( me ).toggle();
  
          });
  
          return $button;
  
      });
  
      UM.commands[ 'fullscreen' ] = {
  
          execCommand: function (cmdName) {
  
              Fullscreen.getInstance( this ).toggle();
  
          },
          queryCommandState: function (cmdName) {
  
              return this._edui_fullscreen_status;
          },
          notNeedUndo: 1
  
      };
  
      function Fullscreen( editor ) {
  
          var me = this;
  
          if( !editor ) {
              throw new Error('invalid params, notfound editor');
          }
  
          me.editor = editor;
  
          //记录初始化的全屏组件
          FULLSCREENS[ editor.uid ] = this;
  
          editor.addListener('destroy', function(){
              delete FULLSCREENS[ editor.uid ];
              me.editor = null;
          });
  
      }
  
      Fullscreen.prototype = {
  
          /**
           * 全屏状态切换
           */
          toggle: function(){
  
              var editor = this.editor,
              //当前编辑器的缩放状态
                  _edui_fullscreen_status = this.isFullState();
              editor.fireEvent('beforefullscreenchange', !_edui_fullscreen_status );
  
              //更新状态
              this.update( !_edui_fullscreen_status );
  
              !_edui_fullscreen_status ? this.enlarge() : this.revert();
  
              editor.fireEvent('afterfullscreenchange', !_edui_fullscreen_status );
              if(editor.body.contentEditable === 'true'){
                  editor.fireEvent( 'fullscreenchanged', !_edui_fullscreen_status );
              }
  
              editor.fireEvent( 'selectionchange' );
  
          },
          /**
           * 执行放大
           */
          enlarge: function(){
  
              this.saveSataus();
  
              this.setDocumentStatus();
  
              this.resize();
              
              $(this.editor.container).css('zIndex', 2000);
              
          },
          /**
           * 全屏还原
           */
          revert: function(){
  
              //还原CSS表达式
              var options = this.editor.options,
                  height = /%$/.test(options.initialFrameHeight) ?  '100%' : (options.initialFrameHeight - this.getStyleValue("padding-top")- this.getStyleValue("padding-bottom") - this.getStyleValue('border-width'));
  
              $.IE6 && this.getEditorHolder().style.setExpression('height', 'this.scrollHeight <= ' + height + ' ? "' + height + 'px" : "auto"');
  
              //还原容器状态
              this.revertContainerStatus();
  
              this.revertContentAreaStatus();
  
              this.revertDocumentStatus();
              
              $(this.editor.container).css('zIndex', this.editor.getOpt('zIndex'));
  
          },
          /**
           * 更新状态
           * @param isFull 当前状态是否是全屏状态
           */
          update: function( isFull ) {
              this.editor._edui_fullscreen_status = isFull;
          },
          /**
           * 调整当前编辑器的大小, 如果当前编辑器不处于全屏状态， 则不做调整
           */
          resize: function(){
  
              var $win = null,
                  height = 0,
                  width = 0,
                  borderWidth = 0,
                  paddingWidth = 0,
                  editor = this.editor,
                  me = this,
                  bound = null,
                  editorBody = null;
  
              if( !this.isFullState() ) {
                  return;
              }
  
              $win = $( window );
              width = $win.width();
              height = $win.height();
              editorBody = this.getEditorHolder();
              //文本编辑区border宽度
              borderWidth = parseInt( domUtils.getComputedStyle( editorBody, 'border-width' ), 10 ) || 0;
              //容器border宽度
              borderWidth += parseInt( domUtils.getComputedStyle( editor.container, 'border-width' ), 10 ) || 0;
              //容器padding
              paddingWidth += parseInt( domUtils.getComputedStyle( editorBody, 'padding-left' ), 10 ) + parseInt( domUtils.getComputedStyle( editorBody, 'padding-right' ), 10 ) || 0;
  
              //干掉css表达式
              $.IE6 && editorBody.style.setExpression( 'height', null );
  
              bound = this.getBound();
  
              // fix by axiuno: zIndex 调高
              $( editor.container ).css( {
                  width: width + 'px',
                  height: height + 'px',
                  position: !$.IE6 ? 'fixed' : 'absolute',
                  top: bound.top,
                  left: bound.left,
                  margin: 0,
                  padding: 0,
                  overflowX: 'hidden',
                  overflowY: 'hidden'
              } );
              // axiuno:
              //$(editor.container).css('z-index', 3000);
  
              $( editorBody ).css({
                  width: width - 2*borderWidth - paddingWidth + 'px',
                  height: height - 2*borderWidth - ( editor.options.withoutToolbar ? 0 : $( '.edui-toolbar', editor.container ).outerHeight() ) - $( '.edui-bottombar', editor.container).outerHeight() + 'px',
                  overflowX: 'hidden',
                  overflowY: 'auto'
              });
              
              
  
          },
          /**
           * 保存状态
           */
          saveSataus: function(){
  
              var styles = this.editor.container.style,
                  tmp = null,
                  cache = {};
  
              for( var i= 0, len = STATUS_LIST.length; i<len; i++ ) {
  
                  tmp = STATUS_LIST[ i ];
                  cache[ tmp ] = styles[ tmp ];
  
              }
  
              STATUS_CACHE[ this.editor.uid ] = cache;
  
              this.saveContentAreaStatus();
              this.saveDocumentStatus();
  
          },
          saveContentAreaStatus: function(){
  
              var $holder = $(this.getEditorHolder());
  
              CONTENT_AREA_STATUS[ this.editor.uid ] = {
                  width: $holder.css("width"),
                  overflowX: $holder.css("overflowX"),
                  overflowY: $holder.css("overflowY"),
                  height: $holder.css("height")
              };
  
          },
          /**
           * 保存与指定editor相关的页面的状态
           */
          saveDocumentStatus: function(){
  
              var $doc = $( this.getEditorDocumentBody() );
  
              DOCUMENT_STATUS[ this.editor.uid ] = {
                  overflowX: $doc.css( 'overflowX' ),
                  overflowY: $doc.css( 'overflowY' )
              };
              DOCUMENT_ELEMENT_STATUS[ this.editor.uid ] = {
                  overflowX: $( this.getEditorDocumentElement() ).css( 'overflowX'),
                  overflowY: $( this.getEditorDocumentElement() ).css( 'overflowY' )
              };
  
          },
          /**
           * 恢复容器状态
           */
          revertContainerStatus: function(){
              $( this.editor.container ).css( this.getEditorStatus() );
          },
          /**
           * 恢复编辑区状态
           */
          revertContentAreaStatus: function(){
              var holder = this.getEditorHolder(),
                  state = this.getContentAreaStatus();
  
              if ( this.supportMin() ) {
                  delete state.height;
                  holder.style.height = null;
              }
  
              $( holder ).css( state );
          },
          /**
           * 恢复页面状态
           */
          revertDocumentStatus: function() {
  
              var status = this.getDocumentStatus();
            //  $( this.getEditorDocumentBody() ).css( 'overflowX', status.body.overflowX );
             //$( this.getEditorDocumentElement() ).css( 'overflowY', status.html.overflowY );
             //$( this.getEditorDocumentElement() ).css( 'overflow', 'auto');
             $('body').css('overflow', 'auto');
  
          },
          setDocumentStatus: function(){
              $(this.getEditorDocumentBody()).css( {
                  overflowX: 'hidden',
                  overflowY: 'hidden'
              } );
              $(this.getEditorDocumentElement()).css( {
                  overflowX: 'hidden',
                  overflowY: 'hidden'
              } );
          },
          /**
           * 检测当前编辑器是否处于全屏状态全屏状态
           * @returns {boolean} 是否处于全屏状态
           */
          isFullState: function(){
              return !!this.editor._edui_fullscreen_status;
          },
          /**
           * 获取编辑器状态
           */
          getEditorStatus: function(){
              return STATUS_CACHE[ this.editor.uid ];
          },
          getContentAreaStatus: function(){
              return CONTENT_AREA_STATUS[ this.editor.uid ];
          },
          getEditorDocumentElement: function(){
              return this.editor.container.ownerDocument.documentElement;
          },
          getEditorDocumentBody: function(){
              return this.editor.container.ownerDocument.body;
          },
          /**
           * 获取编辑区包裹对象
           */
          getEditorHolder: function(){
              return this.editor.body;
          },
          /**
           * 获取编辑器状态
           * @returns {*}
           */
          getDocumentStatus: function(){
              return {
                  'body': DOCUMENT_STATUS[ this.editor.uid ],
                  'html': DOCUMENT_ELEMENT_STATUS[ this.editor.uid ]
              };
          },
          supportMin: function () {
  
              var node = null;
  
              if ( !this._support ) {
  
                  node = document.createElement("div");
  
                  this._support = "minWidth" in node.style;
  
                  node = null;
  
              }
  
              return this._support;
  
          },
          getBound: function () {
  
              var tags = {
                      html: true,
                      body: true
                  },
                  result = {
                      top: 0,
                      left: 0
                  },
                  offsetParent = null;
  
              if ( !$.IE6 ) {
                  return result;
              }
  
              offsetParent = this.editor.container.offsetParent;
  
              if( offsetParent && !tags[ offsetParent.nodeName.toLowerCase() ] ) {
                  tags = offsetParent.getBoundingClientRect();
                  result.top = -tags.top;
                  result.left = -tags.left;
              }
  
              return result;
  
          },
          getStyleValue: function (attr) {
              return parseInt(domUtils.getComputedStyle( this.getEditorHolder() ,attr));
          }
      };
  
  
      $.extend( Fullscreen, {
          /**
           * 监听resize
           */
          listen: function(){
  
              var timer = null;
  
              if( Fullscreen._hasFullscreenListener ) {
                  return;
              }
  
              Fullscreen._hasFullscreenListener = true;
  
              $( window ).on( 'resize', function(){
  
                  if( timer !== null ) {
                      window.clearTimeout( timer );
                      timer = null;
                  }
  
                  timer = window.setTimeout(function(){
  
                      for( var key in FULLSCREENS ) {
                          FULLSCREENS[ key ].resize();
                      }
  
                      timer = null;
  
                  }, 50);
  
              } );
  
          },
  
          getInstance: function ( editor ) {
  
              if ( !FULLSCREENS[editor.uid  ] ) {
                  new Fullscreen( editor );
              }
  
              return FULLSCREENS[editor.uid  ];
  
          }
  
      });
  
      //开始监听
      Fullscreen.listen();
  
  })();
  UM.registerUI('link image video map formula',function(name){
  
      var me = this, currentRange, $dialog,
          opt = {
              title: (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
              url: me.options.UMEDITOR_HOME_URL + 'dialogs/' + name + '/' + name + '.js' + (!!debug ? '?'+Math.random() : '')
          };
  
      var $btn = $.eduibutton({
          icon: name,
          title: this.getLang('labelMap')[name] || ''
      });
      //加载模版数据
      utils.loadFile(document,{
          src: opt.url,
          tag: "script",
          type: "text/javascript",
          defer: "defer"
      },function(){
          //调整数据
          var data = UM.getWidgetData(name);
          if(!data) return;
          if(data.buttons){
              var ok = data.buttons.ok;
              if(ok){
                  opt.oklabel = ok.label || me.getLang('ok');
                  if(ok.exec){
                      opt.okFn = function(){
                          return $.proxy(ok.exec,null,me,$dialog)()
                      }
                  }
              }
              var cancel = data.buttons.cancel;
              if(cancel){
                  opt.cancellabel = cancel.label || me.getLang('cancel');
                  if(cancel.exec){
                      opt.cancelFn = function(){
                          return $.proxy(cancel.exec,null,me,$dialog)()
                      }
                  }
              }
          }
          data.width && (opt.width = data.width);
          data.height && (opt.height = data.height);
  
          $dialog = $.eduimodal(opt);
  
          $dialog.attr('id', 'edui-dialog-' + name).addClass('edui-dialog-' + name)
              .find('.edui-modal-body').addClass('edui-dialog-' + name + '-body');
  
          $dialog.edui().on('beforehide',function () {
              var rng = me.selection.getRange();
              if (rng.equals(currentRange)) {
                  rng.select()
              }
          }).on('beforeshow', function () {
                  var $root = this.root(),
                      win = null,
                      offset = null;
                  currentRange = me.selection.getRange();
                  if (!$root.parent()[0]) {
                      me.$container.find('.edui-dialog-container').append($root);
                  }
  
                  //IE6下 特殊处理, 通过计算进行定位
                  if( $.IE6 ) {
  
                      win = {
                          width: $( window ).width(),
                          height: $( window ).height()
                      };
                      offset = $root.parents(".edui-toolbar")[0].getBoundingClientRect();
                      $root.css({
                          position: 'absolute',
                          margin: 0,
                          left: ( win.width - $root.width() ) / 2 - offset.left,
                          top: 100 - offset.top
                      });
  
                  }
                  UM.setWidgetBody(name,$dialog,me);
                  UM.setTopEditor(me);
          }).on('afterbackdrop',function(){
              this.$backdrop.css('zIndex',me.getOpt('zIndex')+1).appendTo(me.$container.find('.edui-dialog-container'))
              $dialog.css('zIndex',me.getOpt('zIndex')+2)
          }).on('beforeok',function(){
              try{
                  currentRange.select()
              }catch(e){}
          }).attachTo($btn)
      });
  
  
  
  
      me.addListener('selectionchange', function () {
          var state = this.queryCommandState(name);
          $btn.edui().disabled(state == -1).active(state == 1)
      });
      return $btn;
  });
  UM.registerUI( 'emotion formula', function( name ){
      var me = this,
          url  = me.options.UMEDITOR_HOME_URL + 'dialogs/' +name+ '/'+name+'.js';
  
      var $btn = $.eduibutton({
          icon: name,
          title: this.getLang('labelMap')[name] || ''
      });
  
      //加载模版数据
      utils.loadFile(document,{
          src: url,
          tag: "script",
          type: "text/javascript",
          defer: "defer"
      },function(){
          var opt = {
              url : url
          };
          //调整数据
          var data = UM.getWidgetData(name);
  
          data.width && (opt.width = data.width);
          data.height && (opt.height = data.height);
  
          $.eduipopup(opt).css('zIndex',me.options.zIndex + 1)
              .addClass('edui-popup-' + name)
              .edui()
              .on('beforeshow',function(){
                  var $root = this.root();
                  if(!$root.parent().length){
                      me.$container.find('.edui-dialog-container').append($root);
                  }
                  UM.setWidgetBody(name,$root,me);
                  UM.setTopEditor(me);
              }).attachTo($btn,{
                  offsetTop:-5,
                  offsetLeft:10,
                  caretLeft:11,
                  caretTop:-8
              });
          me.addListener('selectionchange', function () {
              var state = this.queryCommandState(name);
              $btn.edui().disabled(state == -1).active(state == 1);
          });
      });
      return $btn;
  
  } );
  UM.registerUI('imagescale',function () {
      var me = this,
          $imagescale;
  
      me.setOpt('imageScaleEnabled', true);
  
      if (browser.webkit && me.getOpt('imageScaleEnabled')) {
  
          me.addListener('click', function (type, e) {
              var range = me.selection.getRange(),
                  img = range.getClosedNode(),
                  target = e.target;
  
              /* 点击第一个图片的后面,八个角不消失 fix:3652 */
              if (img && img.tagName == 'IMG' && target == img) {
  
                  if (!$imagescale) {
                      $imagescale = $.eduiscale({'$wrap':me.$container}).css('zIndex', me.options.zIndex);
                      me.$container.append($imagescale);
  
                      var _keyDownHandler = function () {
                          $imagescale.edui().hide();
                      }, _mouseDownHandler = function (e) {
                          var ele = e.target || e.srcElement;
                          if (ele && ele.className.indexOf('edui-scale') == -1) {
                              _keyDownHandler(e);
                          }
                      }, timer;
  
                      $imagescale.edui()
                          .on('aftershow', function () {
                              $(document).bind('keydown', _keyDownHandler);
                              $(document).bind('mousedown', _mouseDownHandler);
                              me.selection.getNative().removeAllRanges();
                          })
                          .on('afterhide', function () {
                              $(document).unbind('keydown', _keyDownHandler);
                              $(document).unbind('mousedown', _mouseDownHandler);
                              var target = $imagescale.edui().getScaleTarget();
                              if (target.parentNode) {
                                  me.selection.getRange().selectNode(target).select();
                              }
                          })
                          .on('mousedown', function (e) {
                              me.selection.getNative().removeAllRanges();
                              var ele = e.target || e.srcElement;
                              if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
                                  timer = setTimeout(function() {
                                      $imagescale.edui().hide();
                                  }, 200);
                              }
                          })
                          .on('mouseup', function (e) {
                              var ele = e.target || e.srcElement;
                              if (ele && ele.className.indexOf('edui-scale-hand') == -1) {
                                  clearTimeout(timer);
                              }
                          });
                  }
                  $imagescale.edui().show($(img));
  
              } else {
                  if ($imagescale && $imagescale.css('display') != 'none') $imagescale.edui().hide();
  
              }
          });
  
          me.addListener('click', function (type, e) {
              if (e.target.tagName == 'IMG') {
                  var range = new dom.Range(me.document, me.body);
                  range.selectNode(e.target).select();
              }
          });
  
      }
  });
  UM.registerUI('autofloat',function(){
      var me = this,
          lang = me.getLang();
      me.setOpt({
          autoFloatEnabled: true,
          topOffset: 0
      });
      var optsAutoFloatEnabled = me.options.autoFloatEnabled !== false,
          topOffset = me.options.topOffset;
  
  
      //如果不固定toolbar的位置，则直接退出
      if(!optsAutoFloatEnabled){
          return;
      }
      me.ready(function(){
          var LteIE6 = browser.ie && browser.version <= 6,
              quirks = browser.quirks;
  
          function checkHasUI(){
              if(!UM.ui){
                  alert(lang.autofloatMsg);
                  return 0;
              }
              return 1;
          }
          function fixIE6FixedPos(){
              var docStyle = document.body.style;
              docStyle.backgroundImage = 'url("about:blank")';
              docStyle.backgroundAttachment = 'fixed';
          }
          var	bakCssText,
              placeHolder = document.createElement('div'),
              toolbarBox,orgTop,
              getPosition=function(element){
                  var bcr;
                  //trace  IE6下在控制编辑器显隐时可能会报错，catch一下
                  try{
                      bcr = element.getBoundingClientRect();
                  }catch(e){
                      bcr={left:0,top:0,height:0,width:0}
                  }
                  var rect = {
                      left: Math.round(bcr.left),
                      top: Math.round(bcr.top),
                      height: Math.round(bcr.bottom - bcr.top),
                      width: Math.round(bcr.right - bcr.left)
                  };
                  var doc;
                  while ((doc = element.ownerDocument) !== document &&
                      (element = domUtils.getWindow(doc).frameElement)) {
                      bcr = element.getBoundingClientRect();
                      rect.left += bcr.left;
                      rect.top += bcr.top;
                  }
                  rect.bottom = rect.top + rect.height;
                  rect.right = rect.left + rect.width;
                  return rect;
              };
          var isFullScreening = false;
          function setFloating(){
              if(isFullScreening){
                  return;
              }
              var toobarBoxPos = domUtils.getXY(toolbarBox),
                  origalFloat = domUtils.getComputedStyle(toolbarBox,'position'),
                  origalLeft = domUtils.getComputedStyle(toolbarBox,'left');
              toolbarBox.style.width = toolbarBox.offsetWidth + 'px';
              toolbarBox.style.zIndex = me.options.zIndex * 1 + 1;
              toolbarBox.parentNode.insertBefore(placeHolder, toolbarBox);
              if (LteIE6 || (quirks && browser.ie)) {
                  if(toolbarBox.style.position != 'absolute'){
                      toolbarBox.style.position = 'absolute';
                  }
                  toolbarBox.style.top = (document.body.scrollTop||document.documentElement.scrollTop) - orgTop + topOffset  + 'px';
              } else {
                  if(toolbarBox.style.position != 'fixed'){
                      toolbarBox.style.position = 'fixed';
                      toolbarBox.style.top = topOffset +"px";
                      ((origalFloat == 'absolute' || origalFloat == 'relative') && parseFloat(origalLeft)) && (toolbarBox.style.left = toobarBoxPos.x + 'px');
                  }
              }
          }
          function unsetFloating(){
  
              if(placeHolder.parentNode){
                  placeHolder.parentNode.removeChild(placeHolder);
              }
              toolbarBox.style.cssText = bakCssText;
          }
  
          function updateFloating(){
              var rect3 = getPosition(me.container);
              var offset=me.options.toolbarTopOffset||0;
              if (rect3.top < 0 && rect3.bottom - toolbarBox.offsetHeight > offset) {
                  setFloating();
              }else{
                  unsetFloating();
              }
          }
          var defer_updateFloating = utils.defer(function(){
              updateFloating();
          },browser.ie ? 200 : 100,true);
  
          me.addListener('destroy',function(){
              $(window).off('scroll resize',updateFloating);
              me.removeListener('keydown', defer_updateFloating);
          });
  
          if(checkHasUI(me)){
              toolbarBox = $('.edui-toolbar',me.container)[0];
              me.addListener("afteruiready",function(){
                  setTimeout(function(){
                      orgTop = $(toolbarBox).offset().top;
                  },100);
              });
              bakCssText = toolbarBox.style.cssText;
              placeHolder.style.height = toolbarBox.offsetHeight + 'px';
              if(LteIE6){
                  fixIE6FixedPos();
              }
  
              $(window).on('scroll resize',updateFloating);
              me.addListener('keydown', defer_updateFloating);
              me.addListener('resize', function(){
                  unsetFloating();
                  placeHolder.style.height = toolbarBox.offsetHeight + 'px';
                  updateFloating();
              });
  
              me.addListener('beforefullscreenchange', function (t, enabled){
                  if (enabled) {
                      unsetFloating();
                      isFullScreening = enabled;
                  }
              });
              me.addListener('fullscreenchanged', function (t, enabled){
                  if (!enabled) {
                      updateFloating();
                  }
                  isFullScreening = enabled;
              });
              me.addListener('sourcemodechanged', function (t, enabled){
                  setTimeout(function (){
                      updateFloating();
                  },0);
              });
              me.addListener("clearDoc",function(){
                  setTimeout(function(){
                      updateFloating();
                  },0);
  
              })
          }
      })
  
  
  });
  UM.registerUI('source',function(name){
      var me = this;
      me.addListener('fullscreenchanged',function(){
          me.$container.find('textarea').width(me.$body.width() - 10).height(me.$body.height())
  
      });
      var $btn = $.eduibutton({
          icon : name,
          click : function(){
              me.execCommand(name);
              UM.setTopEditor(me);
          },
          title: this.getLang('labelMap')[name] || ''
      });
  
      this.addListener('selectionchange',function(){
          var state = this.queryCommandState(name);
          $btn.edui().disabled(state == -1).active(state == 1)
      });
      return $btn;
  });
  
  UM.registerUI('paragraph fontfamily fontsize', function( name ) {
  
      var me = this,
          label = (me.options.labelMap && me.options.labelMap[name]) || me.getLang("labelMap." + name),
          options = {
              label: label,
              title: label,
              comboboxName: name,
              items: me.options[ name ] || [],
              itemStyles: [],
              value: [],
              autowidthitem: []
          },
          $combox = null,
          comboboxWidget = null;
      if(options.items.length == 0){
          return null;
      }
      switch ( name ) {
  
          case 'paragraph':
              options = transForParagraph( options );
              break;
  
          case 'fontfamily':
              options = transForFontfamily( options );
              break;
  
          case 'fontsize':
              options = transForFontsize( options );
              break;
  
      }
  
      //实例化
      $combox =  $.eduibuttoncombobox(options).css('zIndex',me.getOpt('zIndex') + 1);
      comboboxWidget =  $combox.edui();
  
      comboboxWidget.on('comboboxselect', function( evt, res ){
                          me.execCommand( name, res.value );
                      }).on("beforeshow", function(){
                          if( $combox.parent().length === 0 ) {
                              $combox.appendTo(  me.$container.find('.edui-dialog-container') );
                          }
                          UM.setTopEditor(me);
                      });
  
  
      //状态反射
      this.addListener('selectionchange',function( evt ){
  
          var state  = this.queryCommandState( name ),
              value = this.queryCommandValue( name );
  
          //设置按钮状态
          comboboxWidget.button().edui().disabled( state == -1 ).active( state == 1 );
          if(value){
              //设置label
              value = value.replace(/['"]/g, '').toLowerCase().split(/['|"]?\s*,\s*[\1]?/);
  
              comboboxWidget.selectItemByLabel( value );
          }
  
  
      });
  
      return comboboxWidget.button().addClass('edui-combobox');
  
      /**
       * 宽度自适应工具函数
       * @param word 单词内容
       * @param hasSuffix 是否含有后缀
       */
      function wordCountAdaptive ( word, hasSuffix ) {
  
          var $tmpNode = $('<span>' ).html( word ).css( {
                  display: 'inline',
                  position: 'absolute',
                  top: -10000000,
                  left: -100000
              } ).appendTo( document.body),
              width = $tmpNode.width();
  
          $tmpNode.remove();
          $tmpNode = null;
  
          if( width < 50 ) {
  
              return word;
  
          } else {
  
              word = word.slice( 0, hasSuffix ? -4 : -1 );
  
              if( !word.length ) {
                  return '...';
              }
  
              return wordCountAdaptive( word + '...', true );
  
          }
  
      }
  
  
      //段落参数转换
      function transForParagraph ( options ) {
  
          var tempItems = [];
  
          for( var key in options.items ) {
  
              options.value.push( key );
              tempItems.push( key );
              options.autowidthitem.push( wordCountAdaptive( key ) );
  
          }
  
          options.items = tempItems;
          options.autoRecord = false;
  
          return options;
  
      }
  
      //字体参数转换
      function transForFontfamily ( options ) {
  
          var temp = null,
              tempItems = [];
  
          for( var i = 0, len = options.items.length; i < len; i++ ) {
  
              temp = options.items[ i ].val;
              tempItems.push( temp.split(/\s*,\s*/)[0] );
              options.itemStyles.push('font-family: ' + temp);
              options.value.push( temp );
              options.autowidthitem.push( wordCountAdaptive( tempItems[ i ] ) );
  
          }
  
          options.items = tempItems;
  
          return options;
  
      }
  
      //字体大小参数转换
      function transForFontsize ( options ) {
  
          var temp = null,
              tempItems = [];
  
          options.itemStyles = [];
          options.value = [];
  
          for( var i = 0, len = options.items.length; i < len; i++ ) {
  
              temp = options.items[ i ];
              tempItems.push( temp );
              options.itemStyles.push('font-size: ' + temp +'px');
  
          }
  
          options.value = options.items;
          options.items = tempItems;
          options.autoRecord = false;
  
          return options;
  
      }
  
  });
  
  
  UM.registerUI('forecolor backcolor', function( name ) {
      function getCurrentColor() {
          return domUtils.getComputedStyle( $colorLabel[0], 'background-color' );
      }
  
      var me = this,
          $colorPickerWidget = null,
          $colorLabel = null,
          $btn = null;
  
      //querycommand
      this.addListener('selectionchange', function(){
  
          var state = this.queryCommandState( name );
          $btn.edui().disabled( state == -1 ).active( state == 1 );
  
      });
  
      $btn = $.eduicolorsplitbutton({
          icon: name,
          caret: true,
          name: name,
          title: me.getLang("labelMap")[name],
          click: function() {
              me.execCommand( name, getCurrentColor() );
          }
      });
  
      $colorLabel = $btn.edui().colorLabel();
  
      $colorPickerWidget = $.eduicolorpicker({
          name: name,
          lang_clearColor: me.getLang('clearColor') || '',
          lang_themeColor: me.getLang('themeColor') || '',
          lang_standardColor: me.getLang('standardColor') || ''
      })
          .on('pickcolor', function( evt, color ){
              window.setTimeout( function(){
                  $colorLabel.css("backgroundColor", color);
                  me.execCommand( name, color );
              }, 0 );
          })
          .on('show',function(){
              UM.setActiveWidget( colorPickerWidget.root() );
          }).css('zIndex',me.getOpt('zIndex') + 1);
  
      $btn.edui().on('arrowclick',function(){
          if(!$colorPickerWidget.parent().length){
              me.$container.find('.edui-dialog-container').append($colorPickerWidget);
          }
          $colorPickerWidget.edui().show($btn,{
              caretDir:"down",
              offsetTop:-5,
              offsetLeft:8,
              caretLeft:11,
              caretTop:-8
          });
          UM.setTopEditor(me);
      }).register('click', $btn, function () {
              $colorPickerWidget.edui().hide()
          });
  
      return $btn;
  
  });
  
  })(jQuery)