]/.test(html)) {
        return UE.htmlparser(html).children[0]
      } else {
        return new uNode({
          type: 'element',
          children: [],
          tagName: html
        })
      }
    }
    uNode.createText = function (data, noTrans) {
      return new UE.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, '  ')
        )
      }
    }
    function isElement(node, arr, formatter, current) {
      var attrhtml = ''
      if (node.attrs) {
        attrhtml = []
        var attrs = node.attrs
        for (var a in attrs) {
          //这里就针对
          //'
          //这里边的\"做转换,要不用innerHTML直接被截断了,属性src
          //有可能做的不够
          attrhtml.push(
            a +
              (attrs[a] !== undefined
                ? '="' +
                  (notTransAttrs[a]
                    ? utils.html(attrs[a]).replace(/["]/g, function (a) {
                        return '"'
                      })
                    : 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('')
    }
    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 { UE.uNode } 返回节点本身
       * @example
       * ```javascript
       * node.innerHTML('text ');
       * ```
       */
      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 = UE.htmlparser(htmlstr)
          for (var i = 0, ci; (ci = tmpRoot.children[i++]); ) {
            this.children.push(ci)
            ci.parentNode = this
          }
          return this
        } else {
          var tmpRoot = new UE.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 { UE.uNode } 返回节点本身
       * @example
       * ```javascript
       * node.innerText('text ');
       * ```
       */
      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 { UE.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 { UE.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 { UE.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 { UE.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 { UE.uNode } target 要替换成该节点参数
       * @param { UE.uNode } source 要被替换掉的节点
       * @return { UE.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 { UE.uNode } node 要插入的节点
       * @return { UE.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 { UE.uNode } target 要插入的节点
       * @param { UE.uNode } source 在该参数节点前面插入
       * @return { UE.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 { UE.uNode } target 要插入的节点
       * @param { UE.uNode } source 在该参数节点后面插入
       * @return { UE.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 { UE.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
          }
        }
      },
      /**
       * 获取当前节点在父节点下的位置索引
       * @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 { UE.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))
      },
      /**
       * 传入一个函数,递归遍历当前节点下的所有节点
       * @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
      }
    }
  })()
  // core/htmlparser.js
  /**
   * html字符串转换成uNode节点
   * @file
   * @module UE
   * @since 1.2.6.1
   */
  /**
   * UEditor公用空间,UEditor所有的功能都挂载在该空间下
   * @unfile
   * @module UE
   */
  /**
   * html字符串转换成uNode节点的静态方法
   * @method htmlparser
   * @param { String } htmlstr 要转换的html代码
   * @param { Boolean } ignoreBlank 若设置为true,转换的时候忽略\n\r\t等空白字符
   * @return { uNode } 给定的html片段转换形成的uNode对象
   * @example
   * ```javascript
   * var root = UE.htmlparser('htmlparser 
', true);
   * ```
   */
  var htmlparser = (UE.htmlparser = function (htmlstr, ignoreBlank) {
    //todo 原来的方式  [^"'<>\/] 有\/就不能配对上  这样的标签了
    //先去掉了,加上的原因忘了,这里先记录
    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 = UE.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
      }
      //trace:3970
      //        //如果parent下不能放elm
      //        if(dtd.$inline[parent.tagName] && dtd.$block[elm.tagName] && !dtd[parent.tagName][elm.tagName]){
      //            parent = parent.parentNode;
      //            elm.parentNode = parent;
      //        }
      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
    }
    //如果结束是文本,就有可能丢掉,所以这里手动判断一下
    //例如 sdfsdfsdf sdfsdfsdfsdf
    if (nextIndex < htmlstr.length) {
      text(currentParent, htmlstr.slice(nextIndex))
    }
    return root
  })
  // core/filternode.js
  /**
   * UE过滤节点的静态方法
   * @file
   */
  /**
   * UEditor公用空间,UEditor所有的功能都挂载在该空间下
   * @module UE
   */
  /**
   * 根据传入节点和过滤规则过滤相应节点
   * @module UE
   * @since 1.2.6.1
   * @method filterNode
   * @param { Object } root 指定root节点
   * @param { Object } rules 过滤规则json对象
   * @example
   * ```javascript
   * UE.filterNode(root,editor.options.filterRules);
   * ```
   */
  var filterNode = (UE.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
    }
  })())
  // core/plugin.js
  /**
   * Created with JetBrains PhpStorm.
   * User: campaign
   * Date: 10/8/13
   * Time: 6:15 PM
   * To change this template use File | Settings | File Templates.
   */
  UE.plugin = (function () {
    var _plugins = {}
    return {
      register: function (pluginName, fn, oldOptionName, afterDisabled) {
        if (oldOptionName && utils.isFunction(oldOptionName)) {
          afterDisabled = oldOptionName
          oldOptionName = null
        }
        _plugins[pluginName] = {
          optionName: oldOptionName || pluginName,
          execFn: fn,
          //当插件被禁用时执行
          afterDisabled: afterDisabled
        }
      },
      load: function (editor) {
        utils.each(_plugins, function (plugin) {
          var _export = plugin.execFn.call(editor)
          if (editor.options[plugin.optionName] !== false) {
            if (_export) {
              //后边需要再做扩展
              utils.each(_export, function (v, k) {
                switch (k.toLowerCase()) {
                  case 'shortcutkey':
                    editor.addshortcutkey(v)
                    break
                  case 'bindevents':
                    utils.each(v, function (fn, eventName) {
                      editor.addListener(eventName, fn)
                    })
                    break
                  case 'bindmultievents':
                    utils.each(utils.isArray(v) ? v : [v], function (event) {
                      var types = utils.trim(event.type).split(/\s+/)
                      utils.each(types, function (eventName) {
                        editor.addListener(eventName, event.handler)
                      })
                    })
                    break
                  case 'commands':
                    utils.each(v, function (execFn, execName) {
                      editor.commands[execName] = execFn
                    })
                    break
                  case 'outputrule':
                    editor.addOutputRule(v)
                    break
                  case 'inputrule':
                    editor.addInputRule(v)
                    break
                  case 'defaultoptions':
                    editor.setOpt(v)
                }
              })
            }
          } else if (plugin.afterDisabled) {
            plugin.afterDisabled.call(editor)
          }
        })
        //向下兼容
        utils.each(UE.plugins, function (plugin) {
          plugin.call(editor)
        })
      },
      run: function (pluginName, editor) {
        var plugin = _plugins[pluginName]
        if (plugin) {
          plugin.exeFn.call(editor)
        }
      }
    }
  })()
  // core/keymap.js
  var keymap = (UE.keymap = {
    Backspace: 8,
    Tab: 9,
    Enter: 13,
    Shift: 16,
    Control: 17,
    Alt: 18,
    CapsLock: 20,
    Esc: 27,
    Spacebar: 32,
    PageUp: 33,
    PageDown: 34,
    End: 35,
    Home: 36,
    Left: 37,
    Up: 38,
    Right: 39,
    Down: 40,
    Insert: 45,
    Del: 46,
    NumLock: 144,
    Cmd: 91,
    '=': 187,
    '-': 189,
    b: 66,
    i: 73,
    //回退
    z: 90,
    y: 89,
    //粘贴
    v: 86,
    x: 88,
    s: 83,
    n: 78
  })
  // core/localstorage.js
  //存储媒介封装
  var LocalStorage = (UE.LocalStorage = (function () {
    var storage = window.localStorage || getUserData() || null,
      LOCAL_FILE = 'localStorage'
    return {
      saveLocalData: function (key, data) {
        if (storage && data) {
          storage.setItem(key, data)
          return true
        }
        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 () {
    var ROOTKEY = 'ueditor_preference'
    UE.Editor.prototype.setPreferences = function (key, value) {
      var obj = {}
      if (utils.isString(key)) {
        obj[key] = value
      } else {
        obj = key
      }
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        utils.extend(data, obj)
      } else {
        data = obj
      }
      data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
    }
    UE.Editor.prototype.getPreferences = function (key) {
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        return key ? data[key] : data
      }
      return null
    }
    UE.Editor.prototype.removePreferences = function (key) {
      var data = LocalStorage.getLocalData(ROOTKEY)
      if (data && (data = utils.str2json(data))) {
        data[key] = undefined
        delete data[key]
      }
      data && LocalStorage.saveLocalData(ROOTKEY, utils.json2str(data))
    }
  })()
  // plugins/defaultfilter.js
  ///import core
  ///plugin 编辑器默认的过滤转换机制
  UE.plugins['defaultfilter'] = function () {
    var me = this
    me.setOpt({
      allowDivTransToP: true,
      disabledTableInTable: true
    })
    //默认的过滤处理
    //进入编辑器的内容处理
    me.addInputRule(function (root) {
      var allowDivTransToP = this.options.allowDivTransToP
      var val
      function tdParent(node) {
        while (node && node.type == 'element') {
          if (node.tagName == 'td') {
            return true
          }
          node = node.parentNode
        }
        return false
      }
      //进行默认的处理
      root.traversal(function (node) {
        if (node.type == 'element') {
          if (
            !dtd.$cdata[node.tagName] &&
            me.options.autoClearEmptyNode &&
            dtd.$inline[node.tagName] &&
            !dtd.$empty[node.tagName] &&
            (!node.attrs || utils.isEmptyObject(node.attrs))
          ) {
            if (!node.firstChild()) node.parentNode.removeChild(node)
            else if (node.tagName == 'span' && (!node.attrs || utils.isEmptyObject(node.attrs))) {
              node.parentNode.removeChild(node, true)
            }
            return
          }
          switch (node.tagName) {
            case 'style':
            case 'script':
              node.setAttr({
                cdata_tag: node.tagName,
                cdata_data: node.innerHTML() || '',
                _ue_custom_node_: 'true'
              })
              node.tagName = 'div'
              node.innerHTML('')
              break
            case 'a':
              if ((val = node.getAttr('href'))) {
                node.setAttr('_href', val)
              }
              break
            case 'img':
              //todo base64暂时去掉,后边做远程图片上传后,干掉这个
              if ((val = node.getAttr('src'))) {
                if (/^data:/.test(val)) {
                  node.parentNode.removeChild(node)
                  break
                }
              }
              node.setAttr('_src', node.getAttr('src'))
              break
            case 'span':
              if (browser.webkit && (val = node.getStyle('white-space'))) {
                if (/nowrap|normal/.test(val)) {
                  node.setStyle('white-space', '')
                  if (me.options.autoClearEmptyNode && utils.isEmptyObject(node.attrs)) {
                    node.parentNode.removeChild(node, true)
                  }
                }
              }
              val = node.getAttr('id')
              if (val && /^_baidu_bookmark_/i.test(val)) {
                node.parentNode.removeChild(node)
              }
              break
            case 'p':
              if ((val = node.getAttr('align'))) {
                node.setAttr('align')
                node.setStyle('text-align', val)
              }
              //trace:3431
              //                        var cssStyle = node.getAttr('style');
              //                        if (cssStyle) {
              //                            cssStyle = cssStyle.replace(/(margin|padding)[^;]+/g, '');
              //                            node.setAttr('style', cssStyle)
              //
              //                        }
              //p标签不允许嵌套
              utils.each(node.children, function (n) {
                if (n.type == 'element' && n.tagName == 'p') {
                  var next = n.nextSibling()
                  node.parentNode.insertAfter(n, node)
                  var last = n
                  while (next) {
                    var tmp = next.nextSibling()
                    node.parentNode.insertAfter(next, last)
                    last = next
                    next = tmp
                  }
                  return false
                }
              })
              if (!node.firstChild()) {
                node.innerHTML(browser.ie ? ' ' : 'CC 
   * //插入后的效果 xxxCC xxx
   * //xx|xxx
 当前选区为闭合状态
   * //插入CC
   * //结果 xx
CC
xxx
   * //xxxx
|xxx 当前选区在两个p标签之间
   * //插入 xxxx
   * //结果 xxxx
xxxx
xxx
   * ```
   */
  UE.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 = UE.htmlparser(html)
        //如果给了过滤规则就先进行过滤
        if (me.options.filterRules) {
          UE.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]' + (browser.ie ? '' : '
'
              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)
        }
      }
      //列表单独处理
      var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
      if (li) {
        var next, last
        while ((child = div.firstChild)) {
          //针对hr单独处理一下先
          while (
            child &&
            (child.nodeType == 3 || !domUtils.isBlockElm(child) || child.tagName == 'HR')
          ) {
            next = child.nextSibling
            range.insertNode(child).collapse()
            last = child
            child = next
          }
          if (child) {
            if (/^(ol|ul)$/i.test(child.tagName)) {
              while (child.firstChild) {
                last = child.firstChild
                domUtils.insertAfter(li, child.firstChild)
                li = li.nextSibling
              }
              domUtils.remove(child)
            } else {
              var tmpLi
              next = child.nextSibling
              tmpLi = me.document.createElement('li')
              domUtils.insertAfter(li, tmpLi)
              tmpLi.appendChild(child)
              last = child
              child = next
              li = tmpLi
            }
          }
        }
        li = domUtils.findParentByTagName(range.startContainer, 'li', true)
        if (domUtils.isEmptyBlock(li)) {
          domUtils.remove(li)
        }
        if (last) {
          range.setStartAfter(last).collapse(true).select(true)
        }
      } else {
        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> ==> 
|
              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 : '
'
            )
          )
        }
      },
      commands: {
        background: {
          execCommand: function (cmd, obj) {
            setBackground(obj)
          },
          queryCommandValue: function () {
            var me = this,
              styles = (utils.cssRule(cssRuleId, me.document) || '')
                .replace(/[\n\r]+/g, '')
                .match(reg)
            return styles ? stringToObj(styles[1]) : null
          },
          notNeedUndo: true
        }
      }
    }
  })
  // plugins/image.js
  /**
   * 图片插入、排版插件
   * @file
   * @since 1.2.6.1
   */
  /**
   * 图片对齐方式
   * @command imagefloat
   * @method execCommand
   * @remind 值center为独占一行居中
   * @param { String } cmd 命令字符串
   * @param { String } align 对齐方式,可传left、right、none、center
   * @remaind center表示图片独占一行
   * @example
   * ```javascript
   * editor.execCommand( 'imagefloat', 'center' );
   * ```
   */
  /**
   * 如果选区所在位置是图片区域
   * @command imagefloat
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回图片对齐方式
   * @example
   * ```javascript
   * editor.queryCommandValue( 'imagefloat' );
   * ```
   */
  UE.commands['imagefloat'] = {
    execCommand: function (cmd, align) {
      var me = this,
        range = me.selection.getRange()
      if (!range.collapsed) {
        var img = range.getClosedNode()
        if (img && img.tagName == 'IMG') {
          switch (align) {
            case 'left':
            case 'right':
            case 'none':
              var pN = img.parentNode,
                tmpNode,
                pre,
                next
              while (dtd.$inline[pN.tagName] || pN.tagName == 'A') {
                pN = pN.parentNode
              }
              tmpNode = pN
              if (tmpNode.tagName == 'P' && domUtils.getStyle(tmpNode, 'text-align') == 'center') {
                if (
                  !domUtils.isBody(tmpNode) &&
                  domUtils.getChildCount(tmpNode, function (node) {
                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                  }) == 1
                ) {
                  pre = tmpNode.previousSibling
                  next = tmpNode.nextSibling
                  if (
                    pre &&
                    next &&
                    pre.nodeType == 1 &&
                    next.nodeType == 1 &&
                    pre.tagName == next.tagName &&
                    domUtils.isBlockElm(pre)
                  ) {
                    pre.appendChild(tmpNode.firstChild)
                    while (next.firstChild) {
                      pre.appendChild(next.firstChild)
                    }
                    domUtils.remove(tmpNode)
                    domUtils.remove(next)
                  } else {
                    domUtils.setStyle(tmpNode, 'text-align', '')
                  }
                }
                range.selectNode(img).select()
              }
              domUtils.setStyle(img, 'float', align == 'none' ? '' : align)
              if (align == 'none') {
                domUtils.removeAttributes(img, 'align')
              }
              break
            case 'center':
              if (me.queryCommandValue('imagefloat') != 'center') {
                pN = img.parentNode
                domUtils.setStyle(img, 'float', '')
                domUtils.removeAttributes(img, 'align')
                tmpNode = img
                while (
                  pN &&
                  domUtils.getChildCount(pN, function (node) {
                    return !domUtils.isBr(node) && !domUtils.isWhitespace(node)
                  }) == 1 &&
                  (dtd.$inline[pN.tagName] || pN.tagName == 'A')
                ) {
                  tmpNode = pN
                  pN = pN.parentNode
                }
                range.setStartBefore(tmpNode).setCursor(false)
                pN = me.document.createElement('div')
                pN.appendChild(tmpNode)
                domUtils.setStyle(tmpNode, 'float', '')
                me.execCommand(
                  'insertHtml',
                  '' + pN.innerHTML + '
'
                )
                tmpNode = me.document.getElementById('_img_parent_tmp')
                tmpNode.removeAttribute('id')
                tmpNode = tmpNode.firstChild
                range.selectNode(tmpNode).select()
                //去掉后边多余的元素
                next = tmpNode.parentNode.nextSibling
                if (next && domUtils.isEmptyNode(next)) {
                  domUtils.remove(next)
                }
              }
              break
          }
        }
      }
    },
    queryCommandValue: function () {
      var range = this.selection.getRange(),
        startNode,
        floatStyle
      if (range.collapsed) {
        return 'none'
      }
      startNode = range.getClosedNode()
      if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
        floatStyle =
          domUtils.getComputedStyle(startNode, 'float') || startNode.getAttribute('align')
        if (floatStyle == 'none') {
          floatStyle =
            domUtils.getComputedStyle(startNode.parentNode, 'text-align') == 'center'
              ? 'center'
              : floatStyle
        }
        return {
          left: 1,
          right: 1,
          center: 1
        }[floatStyle]
          ? floatStyle
          : 'none'
      }
      return 'none'
    },
    queryCommandState: function () {
      var range = this.selection.getRange(),
        startNode
      if (range.collapsed) return -1
      startNode = range.getClosedNode()
      if (startNode && startNode.nodeType == 1 && startNode.tagName == 'IMG') {
        return 0
      }
      return -1
    }
  }
  /**
   * 插入图片
   * @command insertimage
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { Object } opt 属性键值对,这些属性都将被复制到当前插入图片
   * @remind 该命令第二个参数可接受一个图片配置项对象的数组,可以插入多张图片,
   * 此时数组的每一个元素都是一个Object类型的图片属性集合。
   * @example
   * ```javascript
   * editor.execCommand( 'insertimage', {
   *     src:'a/b/c.jpg',
   *     width:'100',
   *     height:'100'
   * } );
   * ```
   * @example
   * ```javascript
   * editor.execCommand( 'insertimage', [{
   *     src:'a/b/c.jpg',
   *     width:'100',
   *     height:'100'
   * },{
   *     src:'a/b/d.jpg',
   *     width:'100',
   *     height:'100'
   * }] );
   * ```
   */
  UE.commands['insertimage'] = {
    execCommand: function (cmd, opt) {
      opt = utils.isArray(opt) ? opt : [opt]
      if (!opt.length) {
        return
      }
      var me = this,
        range = me.selection.getRange(),
        img = range.getClosedNode()
      if (me.fireEvent('beforeinsertimage', opt) === true) {
        return
      }
      function unhtmlData(imgCi) {
        utils.each('width,height,border,hspace,vspace'.split(','), function (item) {
          if (imgCi[item]) {
            imgCi[item] = parseInt(imgCi[item], 10) || 0
          }
        })
        utils.each('src,_src'.split(','), function (item) {
          if (imgCi[item]) {
            imgCi[item] = utils.unhtmlForUrl(imgCi[item])
          }
        })
        utils.each('title,alt'.split(','), function (item) {
          if (imgCi[item]) {
            imgCi[item] = utils.unhtml(imgCi[item])
          }
        })
      }
      if (
        img &&
        /img/i.test(img.tagName) &&
        (img.className != 'edui-faked-video' || img.className.indexOf('edui-upload-video') != -1) &&
        !img.getAttribute('word_img')
      ) {
        var first = opt.shift()
        var floatStyle = first['floatStyle']
        delete first['floatStyle']
        ////                img.style.border = (first.border||0) +"px solid #000";
        ////                img.style.margin = (first.margin||0) +"px";
        //                img.style.cssText += ';margin:' + (first.margin||0) +"px;" + 'border:' + (first.border||0) +"px solid #000";
        domUtils.setAttributes(img, first)
        me.execCommand('imagefloat', floatStyle)
        if (opt.length > 0) {
          range.setStartAfter(img).setCursor(false, true)
          me.execCommand('insertimage', opt)
        }
      } else {
        var html = [],
          str = '',
          ci
        ci = opt[0]
        if (opt.length == 1) {
          unhtmlData(ci)
          str =
            '' + str + '
'
          }
          html.push(str)
        } else {
          for (var i = 0; (ci = opt[i++]); ) {
            unhtmlData(ci)
            str =
              '
'
            html.push(str)
          }
        }
        me.execCommand('insertHtml', html.join(''))
      }
      me.fireEvent('afterinsertimage', opt)
    }
  }
  // plugins/justify.js
  /**
   * 段落格式
   * @file
   * @since 1.2.6.1
   */
  /**
   * 段落对齐方式
   * @command justify
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { String } align 对齐方式:left => 居左,right => 居右,center => 居中,justify => 两端对齐
   * @example
   * ```javascript
   * editor.execCommand( 'justify', 'center' );
   * ```
   */
  /**
   * 如果选区所在位置是段落区域,返回当前段落对齐方式
   * @command justify
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回段落对齐方式
   * @example
   * ```javascript
   * editor.queryCommandValue( 'justify' );
   * ```
   */
  UE.plugins['justify'] = function () {
    var me = this,
      block = domUtils.isBlockElm,
      defaultValue = {
        left: 1,
        right: 1,
        center: 1,
        justify: 1
      },
      doJustify = function (range, style) {
        var bookmark = range.createBookmark(),
          filterFn = function (node) {
            return node.nodeType == 1
              ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node)
              : !domUtils.isWhitespace(node)
          }
        range.enlarge(true)
        var bookmark2 = range.createBookmark(),
          current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
          tmpRange = range.cloneRange(),
          tmpNode
        while (
          current &&
          !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)
        ) {
          if (current.nodeType == 3 || !block(current)) {
            tmpRange.setStartBefore(current)
            while (current && current !== bookmark2.end && !block(current)) {
              tmpNode = current
              current = domUtils.getNextDomNode(current, false, null, function (node) {
                return !block(node)
              })
            }
            tmpRange.setEndAfter(tmpNode)
            var common = tmpRange.getCommonAncestor()
            if (!domUtils.isBody(common) && block(common)) {
              domUtils.setStyles(common, utils.isString(style) ? { 'text-align': style } : style)
              current = common
            } else {
              var p = range.document.createElement('p')
              domUtils.setStyles(p, utils.isString(style) ? { 'text-align': style } : style)
              var frag = tmpRange.extractContents()
              p.appendChild(frag)
              tmpRange.insertNode(p)
              current = p
            }
            current = domUtils.getNextDomNode(current, false, filterFn)
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
      }
    UE.commands['justify'] = {
      execCommand: function (cmdName, align) {
        var range = this.selection.getRange(),
          txt
        //闭合时单独处理
        if (range.collapsed) {
          txt = this.document.createTextNode('p')
          range.insertNode(txt)
        }
        doJustify(range, align)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          domUtils.remove(txt)
        }
        range.select()
        return true
      },
      queryCommandValue: function () {
        var startNode = this.selection.getStart(),
          value = domUtils.getComputedStyle(startNode, 'text-align')
        return defaultValue[value] ? value : 'left'
      },
      queryCommandState: function () {
        var start = this.selection.getStart(),
          cell = start && domUtils.findParentByTagName(start, ['td', 'th', 'caption'], true)
        return cell ? -1 : 0
      }
    }
  }
  // plugins/font.js
  /**
   * 字体颜色,背景色,字号,字体,下划线,删除线
   * @file
   * @since 1.2.6.1
   */
  /**
   * 字体颜色
   * @command forecolor
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { String } value 色值(必须十六进制)
   * @example
   * ```javascript
   * editor.execCommand( 'forecolor', '#000' );
   * ```
   */
  /**
   * 返回选区字体颜色
   * @command forecolor
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回字体颜色
   * @example
   * ```javascript
   * editor.queryCommandValue( 'forecolor' );
   * ```
   */
  /**
   * 字体背景颜色
   * @command backcolor
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { String } value 色值(必须十六进制)
   * @example
   * ```javascript
   * editor.execCommand( 'backcolor', '#000' );
   * ```
   */
  /**
   * 返回选区字体颜色
   * @command backcolor
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回字体背景颜色
   * @example
   * ```javascript
   * editor.queryCommandValue( 'backcolor' );
   * ```
   */
  /**
   * 字体大小
   * @command fontsize
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { String } value 字体大小
   * @example
   * ```javascript
   * editor.execCommand( 'fontsize', '14px' );
   * ```
   */
  /**
   * 返回选区字体大小
   * @command fontsize
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回字体大小
   * @example
   * ```javascript
   * editor.queryCommandValue( 'fontsize' );
   * ```
   */
  /**
   * 字体样式
   * @command fontfamily
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { String } value 字体样式
   * @example
   * ```javascript
   * editor.execCommand( 'fontfamily', '微软雅黑' );
   * ```
   */
  /**
   * 返回选区字体样式
   * @command fontfamily
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 返回字体样式
   * @example
   * ```javascript
   * editor.queryCommandValue( 'fontfamily' );
   * ```
   */
  /**
   * 字体下划线,与删除线互斥
   * @command underline
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'underline' );
   * ```
   */
  /**
   * 字体删除线,与下划线互斥
   * @command strikethrough
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'strikethrough' );
   * ```
   */
  /**
   * 字体边框
   * @command fontborder
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'fontborder' );
   * ```
   */
  UE.plugins['font'] = function () {
    var me = this,
      fonts = {
        forecolor: 'color',
        backcolor: 'background-color',
        fontsize: 'font-size',
        fontfamily: 'font-family',
        underline: 'text-decoration',
        strikethrough: 'text-decoration',
        fontborder: 'border'
      },
      needCmd = { underline: 1, strikethrough: 1, fontborder: 1 },
      needSetChild = {
        forecolor: 'color',
        backcolor: 'background-color',
        fontsize: 'font-size',
        fontfamily: 'font-family'
      }
    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' }
      ],
      fontsize: [10, 11, 12, 14, 16, 18, 20, 24, 36]
    })
    function mergeWithParent(node) {
      var parent
      while ((parent = node.parentNode)) {
        if (
          parent.tagName == 'SPAN' &&
          domUtils.getChildCount(parent, function (child) {
            return !domUtils.isBookmarkNode(child) && !domUtils.isBr(child)
          }) == 1
        ) {
          parent.style.cssText += node.style.cssText
          domUtils.remove(node, true)
          node = parent
        } else {
          break
        }
      }
    }
    function mergeChild(rng, cmdName, value) {
      if (needSetChild[cmdName]) {
        rng.adjustmentBoundary()
        if (!rng.collapsed && rng.startContainer.nodeType == 1) {
          var start = rng.startContainer.childNodes[rng.startOffset]
          if (start && domUtils.isTagNode(start, 'span')) {
            var bk = rng.createBookmark()
            utils.each(domUtils.getElementsByTagName(start, 'span'), function (span) {
              if (!span.parentNode || domUtils.isBookmarkNode(span)) return
              if (
                cmdName == 'backcolor' &&
                domUtils.getComputedStyle(span, 'background-color').toLowerCase() === value
              ) {
                return
              }
              domUtils.removeStyle(span, needSetChild[cmdName])
              if (span.style.cssText.replace(/^\s+$/, '').length == 0) {
                domUtils.remove(span, true)
              }
            })
            rng.moveToBookmark(bk)
          }
        }
      }
    }
    function mergesibling(rng, cmdName, value) {
      var collapsed = rng.collapsed,
        bk = rng.createBookmark(),
        common
      if (collapsed) {
        common = bk.start.parentNode
        while (dtd.$inline[common.tagName]) {
          common = common.parentNode
        }
      } else {
        common = domUtils.getCommonAncestor(bk.start, bk.end)
      }
      utils.each(domUtils.getElementsByTagName(common, 'span'), function (span) {
        if (!span.parentNode || domUtils.isBookmarkNode(span)) return
        if (/\s*border\s*:\s*none;?\s*/i.test(span.style.cssText)) {
          if (/^\s*border\s*:\s*none;?\s*$/.test(span.style.cssText)) {
            domUtils.remove(span, true)
          } else {
            domUtils.removeStyle(span, 'border')
          }
          return
        }
        if (
          /border/i.test(span.style.cssText) &&
          span.parentNode.tagName == 'SPAN' &&
          /border/i.test(span.parentNode.style.cssText)
        ) {
          span.style.cssText = span.style.cssText.replace(/border[^:]*:[^;]+;?/gi, '')
        }
        if (!(cmdName == 'fontborder' && value == 'none')) {
          var next = span.nextSibling
          while (next && next.nodeType == 1 && next.tagName == 'SPAN') {
            if (domUtils.isBookmarkNode(next) && cmdName == 'fontborder') {
              span.appendChild(next)
              next = span.nextSibling
              continue
            }
            if (next.style.cssText == span.style.cssText) {
              domUtils.moveChild(next, span)
              domUtils.remove(next)
            }
            if (span.nextSibling === next) break
            next = span.nextSibling
          }
        }
        mergeWithParent(span)
        if (browser.ie && browser.version > 8) {
          //拷贝父亲们的特别的属性,这里只做背景颜色的处理
          var parent = domUtils.findParent(span, function (n) {
            return n.tagName == 'SPAN' && /background-color/.test(n.style.cssText)
          })
          if (parent && !/background-color/.test(span.style.cssText)) {
            span.style.backgroundColor = parent.style.backgroundColor
          }
        }
      })
      rng.moveToBookmark(bk)
      mergeChild(rng, cmdName, value)
    }
    me.addInputRule(function (root) {
      utils.each(root.getNodesByTagName('u s del font strike'), function (node) {
        if (node.tagName == 'font') {
          var cssStyle = []
          for (var p in node.attrs) {
            switch (p) {
              case 'size':
                cssStyle.push(
                  'font-size:' +
                    ({
                      1: '10',
                      2: '12',
                      3: '16',
                      4: '18',
                      5: '24',
                      6: '32',
                      7: '48'
                    }[node.attrs[p]] || node.attrs[p]) +
                    '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(';')
          }
        } else {
          var val = node.tagName == 'u' ? 'underline' : 'line-through'
          node.attrs = {
            style: (node.getAttr('style') || '') + 'text-decoration:' + val + ';'
          }
        }
        node.tagName = 'span'
      })
      //        utils.each(root.getNodesByTagName('span'), function (node) {
      //            var val;
      //            if(val = node.getAttr('class')){
      //                if(/fontstrikethrough/.test(val)){
      //                    node.setStyle('text-decoration','line-through');
      //                    if(node.attrs['class']){
      //                        node.attrs['class'] = node.attrs['class'].replace(/fontstrikethrough/,'');
      //                    }else{
      //                        node.setAttr('class')
      //                    }
      //                }
      //                if(/fontborder/.test(val)){
      //                    node.setStyle('border','1px solid #000');
      //                    if(node.attrs['class']){
      //                        node.attrs['class'] = node.attrs['class'].replace(/fontborder/,'');
      //                    }else{
      //                        node.setAttr('class')
      //                    }
      //                }
      //            }
      //        });
    })
    //    me.addOutputRule(function(root){
    //        utils.each(root.getNodesByTagName('span'), function (node) {
    //            var val;
    //            if(val = node.getStyle('text-decoration')){
    //                if(/line-through/.test(val)){
    //                    if(node.attrs['class']){
    //                        node.attrs['class'] += ' fontstrikethrough';
    //                    }else{
    //                        node.setAttr('class','fontstrikethrough')
    //                    }
    //                }
    //
    //                node.setStyle('text-decoration')
    //            }
    //            if(val = node.getStyle('border')){
    //                if(/1px/.test(val) && /solid/.test(val)){
    //                    if(node.attrs['class']){
    //                        node.attrs['class'] += ' fontborder';
    //
    //                    }else{
    //                        node.setAttr('class','fontborder')
    //                    }
    //                }
    //                node.setStyle('border')
    //
    //            }
    //        });
    //    });
    for (var p in fonts) {
      ;(function (cmd, style) {
        UE.commands[cmd] = {
          execCommand: function (cmdName, value) {
            value =
              value ||
              (this.queryCommandState(cmdName)
                ? 'none'
                : cmdName == 'underline'
                ? 'underline'
                : cmdName == 'fontborder'
                ? '1px solid #000'
                : 'line-through')
            var me = this,
              range = this.selection.getRange(),
              text
            if (value == 'default') {
              if (range.collapsed) {
                text = me.document.createTextNode('font')
                range.insertNode(text).select()
              }
              me.execCommand('removeFormat', 'span,a', style)
              if (text) {
                range.setStartBefore(text).collapse(true)
                domUtils.remove(text)
              }
              mergesibling(range, cmdName, value)
              range.select()
            } else {
              if (!range.collapsed) {
                if (needCmd[cmd] && me.queryCommandValue(cmd)) {
                  me.execCommand('removeFormat', 'span,a', style)
                }
                range = me.selection.getRange()
                range.applyInlineStyle('span', { style: style + ':' + value })
                mergesibling(range, cmdName, value)
                range.select()
              } else {
                var span = domUtils.findParentByTagName(range.startContainer, 'span', true)
                text = me.document.createTextNode('font')
                if (
                  span &&
                  !span.children.length &&
                  !span[browser.ie ? 'innerText' : 'textContent'].replace(fillCharReg, '').length
                ) {
                  //for ie hack when enter
                  range.insertNode(text)
                  if (needCmd[cmd]) {
                    range.selectNode(text).select()
                    me.execCommand('removeFormat', 'span,a', style, null)
                    span = domUtils.findParentByTagName(text, 'span', true)
                    range.setStartBefore(text)
                  }
                  span && (span.style.cssText += ';' + style + ':' + value)
                  range.collapse(true).select()
                } else {
                  range.insertNode(text)
                  range.selectNode(text).select()
                  span = range.document.createElement('span')
                  if (needCmd[cmd]) {
                    //a标签内的不处理跳过
                    if (domUtils.findParentByTagName(text, 'a', true)) {
                      range.setStartBefore(text).setCursor()
                      domUtils.remove(text)
                      return
                    }
                    me.execCommand('removeFormat', 'span,a', style)
                  }
                  span.style.cssText = style + ':' + value
                  text.parentNode.insertBefore(span, text)
                  //修复,span套span 但样式不继承的问题
                  if (!browser.ie || (browser.ie && browser.version == 9)) {
                    var spanParent = span.parentNode
                    while (!domUtils.isBlockElm(spanParent)) {
                      if (spanParent.tagName == 'SPAN') {
                        //opera合并style不会加入";"
                        span.style.cssText = spanParent.style.cssText + ';' + span.style.cssText
                      }
                      spanParent = spanParent.parentNode
                    }
                  }
                  if (opera) {
                    setTimeout(function () {
                      range.setStart(span, 0).collapse(true)
                      mergesibling(range, cmdName, value)
                      range.select()
                    })
                  } else {
                    range.setStart(span, 0).collapse(true)
                    mergesibling(range, cmdName, value)
                    range.select()
                  }
                  //trace:981
                  //domUtils.mergeToParent(span)
                }
                domUtils.remove(text)
              }
            }
            return true
          },
          queryCommandValue: function (cmdName) {
            var startNode = this.selection.getStart()
            //trace:946
            if (cmdName == 'underline' || cmdName == 'strikethrough') {
              var tmpNode = startNode,
                value
              while (tmpNode && !domUtils.isBlockElm(tmpNode) && !domUtils.isBody(tmpNode)) {
                if (tmpNode.nodeType == 1) {
                  value = domUtils.getComputedStyle(tmpNode, style)
                  if (value != 'none') {
                    return value
                  }
                }
                tmpNode = tmpNode.parentNode
              }
              return 'none'
            }
            if (cmdName == 'fontborder') {
              var tmp = startNode,
                val
              while (tmp && dtd.$inline[tmp.tagName]) {
                if ((val = domUtils.getComputedStyle(tmp, 'border'))) {
                  if (/1px/.test(val) && /solid/.test(val)) {
                    return val
                  }
                }
                tmp = tmp.parentNode
              }
              return ''
            }
            if (cmdName == 'FontSize') {
              var styleVal = domUtils.getComputedStyle(startNode, style),
                tmp = /^([\d\.]+)(\w+)$/.exec(styleVal)
              if (tmp) {
                return Math.floor(tmp[1]) + tmp[2]
              }
              return styleVal
            }
            return domUtils.getComputedStyle(startNode, style)
          },
          queryCommandState: function (cmdName) {
            if (!needCmd[cmdName]) return 0
            var val = this.queryCommandValue(cmdName)
            if (cmdName == 'fontborder') {
              return /1px/.test(val) && /solid/.test(val)
            } else {
              return cmdName == 'underline' ? /underline/.test(val) : /line\-through/.test(val)
            }
          }
        }
      })(p, fonts[p])
    }
  }
  // plugins/link.js
  /**
   * 超链接
   * @file
   * @since 1.2.6.1
   */
  /**
   * 插入超链接
   * @command link
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param { Object } options   设置自定义属性,例如:url、title、target
   * @example
   * ```javascript
   * editor.execCommand( 'link', '{
   *     url:'ueditor.baidu.com',
   *     title:'ueditor',
   *     target:'_blank'
   * }' );
   * ```
   */
  /**
   * 返回当前选中的第一个超链接节点
   * @command link
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { Element } 超链接节点
   * @example
   * ```javascript
   * editor.queryCommandValue( 'link' );
   * ```
   */
  /**
   * 取消超链接
   * @command unlink
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'unlink');
   * ```
   */
  UE.plugins['link'] = function () {
    function optimize(range) {
      var start = range.startContainer,
        end = range.endContainer
      if ((start = domUtils.findParentByTagName(start, 'a', true))) {
        range.setStartBefore(start)
      }
      if ((end = domUtils.findParentByTagName(end, 'a', true))) {
        range.setEndAfter(end)
      }
    }
    UE.commands['unlink'] = {
      execCommand: function () {
        var range = this.selection.getRange(),
          bookmark
        if (range.collapsed && !domUtils.findParentByTagName(range.startContainer, 'a', true)) {
          return
        }
        bookmark = range.createBookmark()
        optimize(range)
        range.removeInlineStyle('a').moveToBookmark(bookmark).select()
      },
      queryCommandState: function () {
        return !this.highlight && this.queryCommandValue('link') ? 0 : -1
      }
    }
    function doLink(range, opt, me) {
      var rngClone = range.cloneRange(),
        link = me.queryCommandValue('link')
      optimize((range = range.adjustmentBoundary()))
      var start = range.startContainer
      if (start.nodeType == 1 && link) {
        start = start.childNodes[range.startOffset]
        if (
          start &&
          start.nodeType == 1 &&
          start.tagName == 'A' &&
          /^(?:https?|ftp|file)\s*:\s*\/\//.test(start[browser.ie ? 'innerText' : 'textContent'])
        ) {
          start[browser.ie ? 'innerText' : 'textContent'] = utils.html(opt.textValue || opt.href)
        }
      }
      if (!rngClone.collapsed || link) {
        range.removeInlineStyle('a')
        rngClone = range.cloneRange()
      }
      if (rngClone.collapsed) {
        var a = range.document.createElement('a'),
          text = ''
        if (opt.textValue) {
          text = utils.html(opt.textValue)
          delete opt.textValue
        } else {
          text = utils.html(opt.href)
        }
        domUtils.setAttributes(a, opt)
        start = domUtils.findParentByTagName(rngClone.startContainer, 'a', true)
        if (start && domUtils.isInNodeEndBoundary(rngClone, start)) {
          range.setStartAfter(start).collapse(true)
        }
        a[browser.ie ? 'innerText' : 'textContent'] = text
        range.insertNode(a).selectNode(a)
      } else {
        range.applyInlineStyle('a', opt)
      }
    }
    UE.commands['link'] = {
      execCommand: function (cmdName, opt) {
        var range
        opt._href && (opt._href = utils.unhtml(opt._href, /[<">]/g))
        opt.href && (opt.href = utils.unhtml(opt.href, /[<">]/g))
        opt.textValue && (opt.textValue = utils.unhtml(opt.textValue, /[<">]/g))
        doLink((range = this.selection.getRange()), opt, this)
        //闭合都不加占位符,如果加了会在a后边多个占位符节点,导致a是图片背景组成的列表,出现空白问题
        range.collapse().select(true)
      },
      queryCommandValue: function () {
        var range = this.selection.getRange(),
          node
        if (range.collapsed) {
          //                    node = this.selection.getStart();
          //在ie下getstart()取值偏上了
          node = range.startContainer
          node = node.nodeType == 1 ? node : node.parentNode
          if (
            node &&
            (node = domUtils.findParentByTagName(node, 'a', true)) &&
            !domUtils.isInNodeEndBoundary(range, node)
          ) {
            return node
          }
        } else {
          //trace:1111  如果是xx 
 startContainer是p就会找不到a
          range.shrinkBoundary()
          var start =
              range.startContainer.nodeType == 3 ||
              !range.startContainer.childNodes[range.startOffset]
                ? range.startContainer
                : range.startContainer.childNodes[range.startOffset],
            end =
              range.endContainer.nodeType == 3 || range.endOffset == 0
                ? range.endContainer
                : range.endContainer.childNodes[range.endOffset - 1],
            common = range.getCommonAncestor()
          node = domUtils.findParentByTagName(common, 'a', true)
          if (!node && common.nodeType == 1) {
            var as = common.getElementsByTagName('a'),
              ps,
              pe
            for (var i = 0, ci; (ci = as[i++]); ) {
              ;(ps = domUtils.getPosition(ci, start)), (pe = domUtils.getPosition(ci, end))
              if (
                (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) &&
                (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
              ) {
                node = ci
                break
              }
            }
          }
          return node
        }
      },
      queryCommandState: function () {
        //判断如果是视频的话连接不可用
        //fix 853
        var img = this.selection.getRange().getClosedNode(),
          flag =
            img &&
            (img.className == 'edui-faked-video' ||
              img.className.indexOf('edui-upload-video') != -1)
        return flag ? -1 : 0
      }
    }
  }
  // plugins/iframe.js
  ///import core
  ///import plugins\inserthtml.js
  ///commands 插入框架
  ///commandsName  InsertFrame
  ///commandsTitle  插入Iframe
  ///commandsDialog  dialogs\insertframe
  UE.plugins['insertframe'] = function () {
    var me = this
    function deleteIframe() {
      me._iframe && delete me._iframe
    }
    me.addListener('selectionchange', function () {
      deleteIframe()
    })
  }
  // plugins/scrawl.js
  ///import core
  ///commands 涂鸦
  ///commandsName  Scrawl
  ///commandsTitle  涂鸦
  ///commandsDialog  dialogs\scrawl
  UE.commands['scrawl'] = {
    queryCommandState: function () {
      return browser.ie && browser.version <= 8 ? -1 : 0
    }
  }
  // plugins/removeformat.js
  /**
   * 清除格式
   * @file
   * @since 1.2.6.1
   */
  /**
   * 清除文字样式
   * @command removeformat
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param   {String}   tags     以逗号隔开的标签。如:strong
   * @param   {String}   style    样式如:color
   * @param   {String}   attrs    属性如:width
   * @example
   * ```javascript
   * editor.execCommand( 'removeformat', 'strong','color','width' );
   * ```
   */
  UE.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)
              if (!dtd.$empty[current.tagName.toLowerCase()] && !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上的样式去掉
                  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)
          //清除冗余的代码 ' +
          this.getContent(null, null, true) +
          '
'
      )
      d.close()
    },
    notNeedUndo: 1
  }
  // plugins/selectall.js
  /**
   * 全选
   * @file
   * @since 1.2.6.1
   */
  /**
   * 选中所有内容
   * @command selectall
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'selectall' );
   * ```
   */
  UE.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'
    })
  }
  // plugins/paragraph.js
  /**
   * 段落样式
   * @file
   * @since 1.2.6.1
   */
  /**
   * 段落格式
   * @command paragraph
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @param {String}   style               标签值为:'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
   * @param {Object}   attrs               标签的属性
   * @example
   * ```javascript
   * editor.execCommand( 'Paragraph','h1','{
   *     class:'test'
   * }' );
   * ```
   */
  /**
   * 返回选区内节点标签名
   * @command paragraph
   * @method queryCommandValue
   * @param { String } cmd 命令字符串
   * @return { String } 节点标签名
   * @example
   * ```javascript
   * editor.queryCommandValue( 'Paragraph' );
   * ```
   */
  UE.plugins['paragraph'] = function () {
    var me = this,
      block = domUtils.isBlockElm,
      notExchange = ['TD', 'LI', 'PRE'],
      doParagraph = function (range, style, attrs, sourceCmdName) {
        var bookmark = range.createBookmark(),
          filterFn = function (node) {
            return node.nodeType == 1
              ? node.tagName.toLowerCase() != 'br' && !domUtils.isBookmarkNode(node)
              : !domUtils.isWhitespace(node)
          },
          para
        range.enlarge(true)
        var bookmark2 = range.createBookmark(),
          current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
          tmpRange = range.cloneRange(),
          tmpNode
        while (
          current &&
          !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)
        ) {
          if (current.nodeType == 3 || !block(current)) {
            tmpRange.setStartBefore(current)
            while (current && current !== bookmark2.end && !block(current)) {
              tmpNode = current
              current = domUtils.getNextDomNode(current, false, null, function (node) {
                return !block(node)
              })
            }
            tmpRange.setEndAfter(tmpNode)
            para = range.document.createElement(style)
            if (attrs) {
              domUtils.setAttributes(para, attrs)
              if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
                para.style.cssText = attrs.style
              }
            }
            para.appendChild(tmpRange.extractContents())
            //需要内容占位
            if (domUtils.isEmptyNode(para)) {
              domUtils.fillChar(range.document, para)
            }
            tmpRange.insertNode(para)
            var parent = para.parentNode
            //如果para上一级是一个block元素且不是body,td就删除它
            if (
              block(parent) &&
              !domUtils.isBody(para.parentNode) &&
              utils.indexOf(notExchange, parent.tagName) == -1
            ) {
              //存储dir,style
              if (!(sourceCmdName && sourceCmdName == 'customstyle')) {
                parent.getAttribute('dir') && para.setAttribute('dir', parent.getAttribute('dir'))
                //trace:1070
                parent.style.cssText &&
                  (para.style.cssText = parent.style.cssText + ';' + para.style.cssText)
                //trace:1030
                parent.style.textAlign &&
                  !para.style.textAlign &&
                  (para.style.textAlign = parent.style.textAlign)
                parent.style.textIndent &&
                  !para.style.textIndent &&
                  (para.style.textIndent = parent.style.textIndent)
                parent.style.padding &&
                  !para.style.padding &&
                  (para.style.padding = parent.style.padding)
              }
              //trace:1706 选择的就是h1-6要删除
              if (attrs && /h\d/i.test(parent.tagName) && !/h\d/i.test(para.tagName)) {
                domUtils.setAttributes(parent, attrs)
                if (sourceCmdName && sourceCmdName == 'customstyle' && attrs.style) {
                  parent.style.cssText = attrs.style
                }
                domUtils.remove(para, true)
                para = parent
              } else {
                domUtils.remove(para.parentNode, true)
              }
            }
            if (utils.indexOf(notExchange, parent.tagName) != -1) {
              current = parent
            } else {
              current = para
            }
            current = domUtils.getNextDomNode(current, false, filterFn)
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
      }
    me.setOpt('paragraph', {
      p: '',
      h1: '',
      h2: '',
      h3: '',
      h4: '',
      h5: '',
      h6: ''
    })
    me.commands['paragraph'] = {
      execCommand: function (cmdName, style, attrs, sourceCmdName) {
        var range = this.selection.getRange()
        //闭合时单独处理
        if (range.collapsed) {
          var txt = this.document.createTextNode('p')
          range.insertNode(txt)
          //去掉冗余的fillchar
          if (browser.ie) {
            var node = txt.previousSibling
            if (node && domUtils.isWhitespace(node)) {
              domUtils.remove(node)
            }
            node = txt.nextSibling
            if (node && domUtils.isWhitespace(node)) {
              domUtils.remove(node)
            }
          }
        }
        range = doParagraph(range, style, attrs, sourceCmdName)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          pN = txt.parentNode
          domUtils.remove(txt)
          if (domUtils.isBlockElm(pN) && domUtils.isEmptyNode(pN)) {
            domUtils.fillNode(this.document, pN)
          }
        }
        if (browser.gecko && range.collapsed && range.startContainer.nodeType == 1) {
          var child = range.startContainer.childNodes[range.startOffset]
          if (child && child.nodeType == 1 && child.tagName.toLowerCase() == style) {
            range.setStart(child, 0).collapse(true)
          }
        }
        //trace:1097 原来有true,原因忘了,但去了就不能清除多余的占位符了
        range.select()
        return true
      },
      queryCommandValue: function () {
        var node = domUtils.filterNodeList(
          this.selection.getStartElementPath(),
          'p h1 h2 h3 h4 h5 h6'
        )
        return node ? node.tagName.toLowerCase() : ''
      }
    }
  }
  // plugins/directionality.js
  /**
   * 设置文字输入的方向的插件
   * @file
   * @since 1.2.6.1
   */
  ;(function () {
    var block = domUtils.isBlockElm,
      getObj = function (editor) {
        //            var startNode = editor.selection.getStart(),
        //                parents;
        //            if ( startNode ) {
        //                //查找所有的是block的父亲节点
        //                parents = domUtils.findParents( startNode, true, block, true );
        //                for ( var i = 0,ci; ci = parents[i++]; ) {
        //                    if ( ci.getAttribute( 'dir' ) ) {
        //                        return ci;
        //                    }
        //                }
        //            }
        return domUtils.filterNodeList(editor.selection.getStartElementPath(), function (n) {
          return n && n.nodeType == 1 && n.getAttribute('dir')
        })
      },
      doDirectionality = function (range, editor, forward) {
        var bookmark,
          filterFn = function (node) {
            return node.nodeType == 1
              ? !domUtils.isBookmarkNode(node)
              : !domUtils.isWhitespace(node)
          },
          obj = getObj(editor)
        if (obj && range.collapsed) {
          obj.setAttribute('dir', forward)
          return range
        }
        bookmark = range.createBookmark()
        range.enlarge(true)
        var bookmark2 = range.createBookmark(),
          current = domUtils.getNextDomNode(bookmark2.start, false, filterFn),
          tmpRange = range.cloneRange(),
          tmpNode
        while (
          current &&
          !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)
        ) {
          if (current.nodeType == 3 || !block(current)) {
            tmpRange.setStartBefore(current)
            while (current && current !== bookmark2.end && !block(current)) {
              tmpNode = current
              current = domUtils.getNextDomNode(current, false, null, function (node) {
                return !block(node)
              })
            }
            tmpRange.setEndAfter(tmpNode)
            var common = tmpRange.getCommonAncestor()
            if (!domUtils.isBody(common) && block(common)) {
              //遍历到了block节点
              common.setAttribute('dir', forward)
              current = common
            } else {
              //没有遍历到,添加一个block节点
              var p = range.document.createElement('p')
              p.setAttribute('dir', forward)
              var frag = tmpRange.extractContents()
              p.appendChild(frag)
              tmpRange.insertNode(p)
              current = p
            }
            current = domUtils.getNextDomNode(current, false, filterFn)
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        return range.moveToBookmark(bookmark2).moveToBookmark(bookmark)
      }
    /**
     * 文字输入方向
     * @command directionality
     * @method execCommand
     * @param { String } cmdName 命令字符串
     * @param { String } forward 传入'ltr'表示从左向右输入,传入'rtl'表示从右向左输入
     * @example
     * ```javascript
     * editor.execCommand( 'directionality', 'ltr');
     * ```
     */
    /**
     * 查询当前选区的文字输入方向
     * @command directionality
     * @method queryCommandValue
     * @param { String } cmdName 命令字符串
     * @return { String } 返回'ltr'表示从左向右输入,返回'rtl'表示从右向左输入
     * @example
     * ```javascript
     * editor.queryCommandValue( 'directionality');
     * ```
     */
    UE.commands['directionality'] = {
      execCommand: function (cmdName, forward) {
        var range = this.selection.getRange()
        //闭合时单独处理
        if (range.collapsed) {
          var txt = this.document.createTextNode('d')
          range.insertNode(txt)
        }
        doDirectionality(range, this, forward)
        if (txt) {
          range.setStartBefore(txt).collapse(true)
          domUtils.remove(txt)
        }
        range.select()
        return true
      },
      queryCommandValue: function () {
        var node = getObj(this)
        return node ? node.getAttribute('dir') : 'ltr'
      }
    }
  })()
  // plugins/horizontal.js
  /**
   * 插入分割线插件
   * @file
   * @since 1.2.6.1
   */
  /**
   * 插入分割线
   * @command horizontal
   * @method execCommand
   * @param { String } cmdName 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'horizontal' );
   * ```
   */
  UE.plugins['horizontal'] = function () {
    var me = this
    me.commands['horizontal'] = {
      execCommand: function (cmdName) {
        var me = this
        if (me.queryCommandState(cmdName) !== -1) {
          me.execCommand('insertHtml', '' + code + ' ',
            true
          )
          pre = me.document.getElementById('coder')
          domUtils.removeAttributes(pre, 'id')
          var tmpNode = pre.previousSibling
          if (
            tmpNode &&
            ((tmpNode.nodeType == 3 &&
              tmpNode.nodeValue.length == 1 &&
              browser.ie &&
              browser.version == 6) ||
              domUtils.isEmptyBlock(tmpNode))
          ) {
            domUtils.remove(tmpNode)
          }
          var rng = me.selection.getRange()
          if (domUtils.isEmptyBlock(pre)) {
            rng.setStart(pre, 0).setCursor(false, true)
          } else {
            rng.selectNodeContents(pre).select()
          }
        }
      },
      queryCommandValue: function () {
        var path = this.selection.getStartElementPath()
        var lang = ''
        utils.each(path, function (node) {
          if (node.nodeName == 'PRE') {
            var match = node.className.match(/brush:([^;]+)/)
            lang = match && match[1] ? match[1] : ''
            return false
          }
        })
        return lang
      }
    }
    me.addInputRule(function (root) {
      utils.each(root.getNodesByTagName('pre'), function (pre) {
        var brs = pre.getNodesByTagName('br')
        if (brs.length) {
          browser.ie &&
            browser.ie11below &&
            browser.version > 8 &&
            utils.each(brs, function (br) {
              var txt = UE.uNode.createText('\n')
              br.parentNode.insertBefore(txt, br)
              br.parentNode.removeChild(br)
            })
          return
        }
        if (browser.ie && browser.ie11below && browser.version > 8) return
        var code = pre.innerText().split(/\n/)
        pre.innerHTML('')
        utils.each(code, function (c) {
          if (c.length) {
            pre.appendChild(UE.uNode.createText(c))
          }
          pre.appendChild(UE.uNode.createElement('br'))
        })
      })
    })
    me.addOutputRule(function (root) {
      utils.each(root.getNodesByTagName('pre'), function (pre) {
        var code = ''
        utils.each(pre.children, function (n) {
          if (n.type == 'text') {
            //在ie下文本内容有可能末尾带有\n要去掉
            //trace:3396
            code += n.data.replace(/[ ]/g, ' ').replace(/\n$/, '')
          } else {
            if (n.tagName == 'br') {
              code += '\n'
            } else {
              code += !dtd.$empty[n.tagName] ? '' : n.innerText()
            }
          }
        })
        pre.innerText(code.replace(/( |\n)+$/, ''))
      })
    })
    //不需要判断highlight的command列表
    me.notNeedCodeQuery = {
      help: 1,
      undo: 1,
      redo: 1,
      source: 1,
      print: 1,
      searchreplace: 1,
      fullscreen: 1,
      preview: 1,
      insertparagraph: 1,
      elementpath: 1,
      insertcode: 1,
      inserthtml: 1,
      selectall: 1
    }
    //将queyCommamndState重置
    var orgQuery = me.queryCommandState
    me.queryCommandState = function (cmd) {
      var me = this
      if (
        !me.notNeedCodeQuery[cmd.toLowerCase()] &&
        me.selection &&
        me.queryCommandValue('insertcode')
      ) {
        return -1
      }
      return UE.Editor.prototype.queryCommandState.apply(this, arguments)
    }
    me.addListener('beforeenterkeydown', function () {
      var rng = me.selection.getRange()
      var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        me.fireEvent('saveScene')
        if (!rng.collapsed) {
          rng.deleteContents()
        }
        if (!browser.ie || browser.ie9above) {
          var tmpNode = me.document.createElement('br'),
            pre
          rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true)
          var next = tmpNode.nextSibling
          if (!next && (!browser.ie || browser.version > 10)) {
            rng.insertNode(tmpNode.cloneNode(false))
          } else {
            rng.setStartAfter(tmpNode)
          }
          pre = tmpNode.previousSibling
          var tmp
          while (pre) {
            tmp = pre
            pre = pre.previousSibling
            if (!pre || pre.nodeName == 'BR') {
              pre = tmp
              break
            }
          }
          if (pre) {
            var str = ''
            while (
              pre &&
              pre.nodeName != 'BR' &&
              new RegExp('^[\\s' + domUtils.fillChar + ']*$').test(pre.nodeValue)
            ) {
              str += pre.nodeValue
              pre = pre.nextSibling
            }
            if (pre.nodeName != 'BR') {
              var match = pre.nodeValue.match(new RegExp('^([\\s' + domUtils.fillChar + ']+)'))
              if (match && match[1]) {
                str += match[1]
              }
            }
            if (str) {
              str = me.document.createTextNode(str)
              rng.insertNode(str).setStartAfter(str)
            }
          }
          rng.collapse(true).select(true)
        } else {
          if (browser.version > 8) {
            var txt = me.document.createTextNode('\n')
            var start = rng.startContainer
            if (rng.startOffset == 0) {
              var preNode = start.previousSibling
              if (preNode) {
                rng.insertNode(txt)
                var fillchar = me.document.createTextNode(' ')
                rng
                  .setStartAfter(txt)
                  .insertNode(fillchar)
                  .setStart(fillchar, 0)
                  .collapse(true)
                  .select(true)
              }
            } else {
              rng.insertNode(txt).setStartAfter(txt)
              var fillchar = me.document.createTextNode(' ')
              start = rng.startContainer.childNodes[rng.startOffset]
              if (start && !/^\n/.test(start.nodeValue)) {
                rng.setStartBefore(txt)
              }
              rng.insertNode(fillchar).setStart(fillchar, 0).collapse(true).select(true)
            }
          } else {
            var tmpNode = me.document.createElement('br')
            rng.insertNode(tmpNode)
            rng.insertNode(me.document.createTextNode(domUtils.fillChar))
            rng.setStartAfter(tmpNode)
            pre = tmpNode.previousSibling
            var tmp
            while (pre) {
              tmp = pre
              pre = pre.previousSibling
              if (!pre || pre.nodeName == 'BR') {
                pre = tmp
                break
              }
            }
            if (pre) {
              var str = ''
              while (
                pre &&
                pre.nodeName != 'BR' &&
                new RegExp('^[ ' + domUtils.fillChar + ']*$').test(pre.nodeValue)
              ) {
                str += pre.nodeValue
                pre = pre.nextSibling
              }
              if (pre.nodeName != 'BR') {
                var match = pre.nodeValue.match(new RegExp('^([ ' + domUtils.fillChar + ']+)'))
                if (match && match[1]) {
                  str += match[1]
                }
              }
              str = me.document.createTextNode(str)
              rng.insertNode(str).setStartAfter(str)
            }
            rng.collapse(true).select()
          }
        }
        me.fireEvent('saveScene')
        return true
      }
    })
    me.addListener('tabkeydown', function (cmd, evt) {
      var rng = me.selection.getRange()
      var pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        me.fireEvent('saveScene')
        if (evt.shiftKey) {
        } else {
          if (!rng.collapsed) {
            var bk = rng.createBookmark()
            var start = bk.start.previousSibling
            while (start) {
              if (pre.firstChild === start && !domUtils.isBr(start)) {
                pre.insertBefore(me.document.createTextNode('    '), start)
                break
              }
              if (domUtils.isBr(start)) {
                pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
                break
              }
              start = start.previousSibling
            }
            var end = bk.end
            start = bk.start.nextSibling
            if (pre.firstChild === bk.start) {
              pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
            }
            while (start && start !== end) {
              if (domUtils.isBr(start) && start.nextSibling) {
                if (start.nextSibling === end) {
                  break
                }
                pre.insertBefore(me.document.createTextNode('    '), start.nextSibling)
              }
              start = start.nextSibling
            }
            rng.moveToBookmark(bk).select()
          } else {
            var tmpNode = me.document.createTextNode('    ')
            rng.insertNode(tmpNode).setStartAfter(tmpNode).collapse(true).select(true)
          }
        }
        me.fireEvent('saveScene')
        return true
      }
    })
    me.addListener('beforeinserthtml', function (evtName, html) {
      var me = this,
        rng = me.selection.getRange(),
        pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)
      if (pre) {
        if (!rng.collapsed) {
          rng.deleteContents()
        }
        var htmlstr = ''
        if (browser.ie && browser.version > 8) {
          utils.each(
            UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children,
            function (node) {
              if (node.type == 'element') {
                if (node.tagName == 'br') {
                  htmlstr += '\n'
                } else if (!dtd.$empty[node.tagName]) {
                  utils.each(node.children, function (cn) {
                    if (cn.type == 'element') {
                      if (cn.tagName == 'br') {
                        htmlstr += '\n'
                      } else if (!dtd.$empty[node.tagName]) {
                        htmlstr += cn.innerText()
                      }
                    } else {
                      htmlstr += cn.data
                    }
                  })
                  if (!/\n$/.test(htmlstr)) {
                    htmlstr += '\n'
                  }
                }
              } else {
                htmlstr += node.data + '\n'
              }
              if (!node.nextSibling() && /\n$/.test(htmlstr)) {
                htmlstr = htmlstr.replace(/\n$/, '')
              }
            }
          )
          var tmpNode = me.document.createTextNode(utils.html(htmlstr.replace(/ /g, ' ')))
          rng.insertNode(tmpNode).selectNode(tmpNode).select()
        } else {
          var frag = me.document.createDocumentFragment()
          utils.each(
            UE.filterNode(UE.htmlparser(html), me.options.filterTxtRules).children,
            function (node) {
              if (node.type == 'element') {
                if (node.tagName == 'br') {
                  frag.appendChild(me.document.createElement('br'))
                } else if (!dtd.$empty[node.tagName]) {
                  utils.each(node.children, function (cn) {
                    if (cn.type == 'element') {
                      if (cn.tagName == 'br') {
                        frag.appendChild(me.document.createElement('br'))
                      } else if (!dtd.$empty[node.tagName]) {
                        frag.appendChild(
                          me.document.createTextNode(
                            utils.html(cn.innerText().replace(/ /g, ' '))
                          )
                        )
                      }
                    } else {
                      frag.appendChild(
                        me.document.createTextNode(utils.html(cn.data.replace(/ /g, ' ')))
                      )
                    }
                  })
                  if (frag.lastChild.nodeName != 'BR') {
                    frag.appendChild(me.document.createElement('br'))
                  }
                }
              } else {
                frag.appendChild(
                  me.document.createTextNode(utils.html(node.data.replace(/ /g, ' ')))
                )
              }
              if (!node.nextSibling() && frag.lastChild.nodeName == 'BR') {
                frag.removeChild(frag.lastChild)
              }
            }
          )
          rng.insertNode(frag).select()
        }
        return true
      }
    })
    //方向键的处理
    me.addListener('keydown', function (cmd, evt) {
      var me = this,
        keyCode = evt.keyCode || evt.which
      if (keyCode == 40) {
        var rng = me.selection.getRange(),
          pre,
          start = rng.startContainer
        if (
          rng.collapsed &&
          (pre = domUtils.findParentByTagName(rng.startContainer, 'pre', true)) &&
          !pre.nextSibling
        ) {
          var last = pre.lastChild
          while (last && last.nodeName == 'BR') {
            last = last.previousSibling
          }
          if (
            last === start ||
            (rng.startContainer === pre && rng.startOffset == pre.childNodes.length)
          ) {
            me.execCommand('insertparagraph')
            domUtils.preventDefault(evt)
          }
        }
      }
    })
    //trace:3395
    me.addListener('delkeydown', function (type, evt) {
      var rng = this.selection.getRange()
      rng.txtToElmBoundary(true)
      var start = rng.startContainer
      if (domUtils.isTagNode(start, 'pre') && rng.collapsed && domUtils.isStartInblock(rng)) {
        var p = me.document.createElement('p')
        domUtils.fillNode(me.document, p)
        start.parentNode.insertBefore(p, start)
        domUtils.remove(start)
        rng.setStart(p, 0).setCursor(false, true)
        domUtils.preventDefault(evt)
        return true
      }
    })
  }
  // plugins/cleardoc.js
  /**
   * 清空文档插件
   * @file
   * @since 1.2.6.1
   */
  /**
   * 清空文档
   * @command cleardoc
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * //editor 是编辑器实例
   * editor.execCommand('cleardoc');
   * ```
   */
  UE.commands['cleardoc'] = {
    execCommand: function (cmdName) {
      var me = this,
        enterTag = me.options.enterTag,
        range = me.selection.getRange()
      if (enterTag == 'br') {
        me.body.innerHTML = '' + (ie ? '' : '
'
        range.setStart(me.body.firstChild, 0).setCursor(false, true)
      }
      setTimeout(function () {
        me.fireEvent('clearDoc')
      }, 0)
    }
  }
  // plugins/anchor.js
  /**
   * 锚点插件,为UEditor提供插入锚点支持
   * @file
   * @since 1.2.6.1
   */
  UE.plugin.register('anchor', function () {
    return {
      bindEvents: {
        ready: function () {
          utils.cssRule(
            'anchor',
            ".anchorclass{background: url('" +
              this.options.themePath +
              this.options.theme +
              "/images/anchor.gif') no-repeat scroll left center transparent;cursor: auto;display: inline-block;height: 16px;width: 15px;}",
            this.document
          )
        }
      },
      outputRule: function (root) {
        utils.each(root.getNodesByTagName('img'), function (a) {
          var val
          if ((val = a.getAttr('anchorname'))) {
            a.tagName = 'a'
            a.setAttr({
              anchorname: '',
              name: val,
              class: ''
            })
          }
        })
      },
      inputRule: function (root) {
        utils.each(root.getNodesByTagName('a'), function (a) {
          var val
          if ((val = a.getAttr('name')) && !a.getAttr('href')) {
            a.tagName = 'img'
            a.setAttr({
              anchorname: a.getAttr('name'),
              class: 'anchorclass'
            })
            a.setAttr('name')
          }
        })
      },
      commands: {
        /**
         * 插入锚点
         * @command anchor
         * @method execCommand
         * @param { String } cmd 命令字符串
         * @param { String } name 锚点名称字符串
         * @example
         * ```javascript
         * //editor 是编辑器实例
         * editor.execCommand('anchor', 'anchor1');
         * ```
         */
        anchor: {
          execCommand: function (cmd, name) {
            var range = this.selection.getRange(),
              img = range.getClosedNode()
            if (img && img.getAttribute('anchorname')) {
              if (name) {
                img.setAttribute('anchorname', name)
              } else {
                range.setStartBefore(img).setCursor()
                domUtils.remove(img)
              }
            } else {
              if (name) {
                //只在选区的开始插入
                var anchor = this.document.createElement('img')
                range.collapse(true)
                domUtils.setAttributes(anchor, {
                  anchorname: name,
                  class: 'anchorclass'
                })
                range.insertNode(anchor).setStartAfter(anchor).setCursor(false, true)
              }
            }
          }
        }
      }
    }
  })
  // plugins/wordcount.js
  ///import core
  ///commands 字数统计
  ///commandsName  WordCount,wordCount
  ///commandsTitle  字数统计
  /*
   * Created by JetBrains WebStorm.
   * User: taoqili
   * Date: 11-9-7
   * Time: 下午8:18
   * To change this template use File | Settings | File Templates.
   */
  UE.plugins['wordcount'] = function () {
    var me = this
    me.setOpt('wordCount', true)
    me.addListener('contentchange', function () {
      me.fireEvent('wordcount')
    })
    var timer
    me.addListener('ready', function () {
      var me = this
      domUtils.on(me.body, 'keyup', function (evt) {
        var code = evt.keyCode || evt.which,
          //忽略的按键,ctr,alt,shift,方向键
          ignores = { 16: 1, 18: 1, 20: 1, 37: 1, 38: 1, 39: 1, 40: 1 }
        if (code in ignores) return
        clearTimeout(timer)
        timer = setTimeout(function () {
          me.fireEvent('wordcount')
        }, 200)
      })
    })
  }
  // plugins/pagebreak.js
  /**
   * 分页功能插件
   * @file
   * @since 1.2.6.1
   */
  UE.plugins['pagebreak'] = function () {
    var me = this,
      notBreakTags = ['td']
    me.setOpt('pageBreakTag', '_ueditor_page_break_tag_')
    function fillNode(node) {
      if (domUtils.isEmptyBlock(node)) {
        var firstChild = node.firstChild,
          tmpNode
        while (firstChild && firstChild.nodeType == 1 && domUtils.isEmptyBlock(firstChild)) {
          tmpNode = firstChild
          firstChild = firstChild.firstChild
        }
        !tmpNode && (tmpNode = node)
        domUtils.fillNode(me.document, tmpNode)
      }
    }
    //分页符样式添加
    me.ready(function () {
      utils.cssRule(
        'pagebreak',
        '.pagebreak{display:block;clear:both !important;cursor:default !important;width: 100% !important;margin:0;}',
        me.document
      )
    })
    function isHr(node) {
      return node && node.nodeType == 1 && node.tagName == 'HR' && node.className == 'pagebreak'
    }
    me.addInputRule(function (root) {
      root.traversal(function (node) {
        if (node.type == 'text' && node.data == me.options.pageBreakTag) {
          var hr = UE.uNode.createElement(
            '
'
              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字符,也会产生 这里过滤掉
        html = div.innerHTML //.replace(/>(?:(\s| )*?)<');
        //过滤word粘贴过来的冗余属性
        html = UE.filterWord(html)
        //取消了忽略空白的第二个参数,粘贴过来的有些是有空白的,会被套上相关的标签
        var root = UE.htmlparser(html)
        //如果给了过滤规则就先进行过滤
        if (me.options.filterRules) {
          UE.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, true)
            }
          })
        }
        html = { html: root.toHtml() }
        me.fireEvent('beforepaste', html, root)
        //抢了默认的粘贴,那后边的内容就不执行了,比如表格粘贴
        if (!html.html) {
          return
        }
        root = UE.htmlparser(html.html, true)
        //如果开启了纯文本模式
        if (me.queryCommandState('pasteplain') === 1) {
          me.execCommand(
            'insertHtml',
            UE.filterNode(root, me.options.filterTxtRules).toHtml(),
            true
          )
        } else {
          //文本模式
          UE.filterNode(root, me.options.filterTxtRules)
          txtContent = root.toHtml()
          //完全模式
          htmlContent = html.html
          address = me.selection.getRange().createAddress(true)
          me.execCommand(
            'insertHtml',
            me.getOpt('retainOnlyLabelPasted') === true ? getPureHtml(htmlContent) : htmlContent,
            true
          )
        }
        me.fireEvent('afterpaste', html)
      }
    }
    me.addListener('pasteTransfer', function (cmd, plainType) {
      if (address && txtContent && htmlContent && txtContent != htmlContent) {
        var range = me.selection.getRange()
        range.moveToAddress(address, true)
        if (!range.collapsed) {
          while (!domUtils.isBody(range.startContainer)) {
            var start = range.startContainer
            if (start.nodeType == 1) {
              start = start.childNodes[range.startOffset]
              if (!start) {
                range.setStartBefore(range.startContainer)
                continue
              }
              var pre = start.previousSibling
              if (
                pre &&
                pre.nodeType == 3 &&
                new RegExp('^[\n\r\t ' + domUtils.fillChar + ']*$').test(pre.nodeValue)
              ) {
                range.setStartBefore(pre)
              }
            }
            if (range.startOffset == 0) {
              range.setStartBefore(range.startContainer)
            } else {
              break
            }
          }
          while (!domUtils.isBody(range.endContainer)) {
            var end = range.endContainer
            if (end.nodeType == 1) {
              end = end.childNodes[range.endOffset]
              if (!end) {
                range.setEndAfter(range.endContainer)
                continue
              }
              var next = end.nextSibling
              if (
                next &&
                next.nodeType == 3 &&
                new RegExp('^[\n\r\t' + domUtils.fillChar + ']*$').test(next.nodeValue)
              ) {
                range.setEndAfter(next)
              }
            }
            if (
              range.endOffset ==
              range.endContainer[range.endContainer.nodeType == 3 ? 'nodeValue' : 'childNodes']
                .length
            ) {
              range.setEndAfter(range.endContainer)
            } else {
              break
            }
          }
        }
        range.deleteContents()
        range.select(true)
        me.__hasEnterExecCommand = true
        var html = htmlContent
        if (plainType === 2) {
          html = getPureHtml(html)
        } else if (plainType) {
          html = txtContent
        }
        me.execCommand('inserthtml', html, true)
        me.__hasEnterExecCommand = false
        var rng = me.selection.getRange()
        while (
          !domUtils.isBody(rng.startContainer) &&
          !rng.startOffset &&
          rng.startContainer[rng.startContainer.nodeType == 3 ? 'nodeValue' : 'childNodes'].length
        ) {
          rng.setStartBefore(rng.startContainer)
        }
        var tmpAddress = rng.createAddress(true)
        address.endAddress = tmpAddress.startAddress
      }
    })
    me.addListener('ready', function () {
      domUtils.on(me.body, 'cut', function () {
        var range = me.selection.getRange()
        if (!range.collapsed && me.undoManger) {
          me.undoManger.save()
        }
      })
      //ie下beforepaste在点击右键时也会触发,所以用监控键盘才处理
      domUtils.on(me.body, browser.ie || browser.opera ? 'keydown' : 'paste', function (e) {
        if ((browser.ie || browser.opera) && ((!e.ctrlKey && !e.metaKey) || e.keyCode != '86')) {
          return
        }
        getClipboardData.call(me, function (div) {
          filter(div)
        })
      })
    })
    me.commands['paste'] = {
      execCommand: function (cmd) {
        if (browser.ie) {
          getClipboardData.call(me, function (div) {
            filter(div)
          })
          me.document.execCommand('paste')
        } else {
          alert(me.getLang('pastemsg'))
        }
      }
    }
  }
  // plugins/puretxtpaste.js
  /**
   * 纯文本粘贴插件
   * @file
   * @since 1.2.6.1
   */
  UE.plugins['pasteplain'] = function () {
    var me = this
    me.setOpt({
      pasteplain: false,
      filterTxtRules: (function () {
        function transP(node) {
          node.tagName = 'p'
          node.setStyle()
        }
        function removeNode(node) {
          node.parentNode.removeChild(node, true)
        }
        return {
          //直接删除及其字节点内容
          '-': 'script style object iframe embed input select',
          p: { $: {} },
          br: { $: {} },
          div: function (node) {
            var tmpNode,
              p = UE.uNode.createElement('p')
            while ((tmpNode = node.firstChild())) {
              if (tmpNode.type == 'text' || !UE.dom.dtd.$block[tmpNode.tagName]) {
                p.appendChild(tmpNode)
              } else {
                if (p.firstChild()) {
                  node.parentNode.insertBefore(p, node)
                  p = UE.uNode.createElement('p')
                } else {
                  node.parentNode.insertBefore(tmpNode, node)
                }
              }
            }
            if (p.firstChild()) {
              node.parentNode.insertBefore(p, node)
            }
            node.parentNode.removeChild(node)
          },
          ol: removeNode,
          ul: removeNode,
          dl: removeNode,
          dt: removeNode,
          dd: removeNode,
          li: removeNode,
          caption: transP,
          th: transP,
          tr: transP,
          h1: transP,
          h2: transP,
          h3: transP,
          h4: transP,
          h5: transP,
          h6: transP,
          td: function (node) {
            //没有内容的td直接删掉
            var txt = !!node.innerText()
            if (txt) {
              node.parentNode.insertAfter(UE.uNode.createText('    '), node)
            }
            node.parentNode.removeChild(node, node.innerText())
          }
        }
      })()
    })
    //暂时这里支持一下老版本的属性
    var pasteplain = me.options.pasteplain
    /**
     * 启用或取消纯文本粘贴模式
     * @command pasteplain
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * editor.queryCommandState( 'pasteplain' );
     * ```
     */
    /**
     * 查询当前是否处于纯文本粘贴模式
     * @command pasteplain
     * @method queryCommandState
     * @param { String } cmd 命令字符串
     * @return { int } 如果处于纯文本模式,返回1,否则,返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'pasteplain' );
     * ```
     */
    me.commands['pasteplain'] = {
      queryCommandState: function () {
        return pasteplain ? 1 : 0
      },
      execCommand: function () {
        pasteplain = !pasteplain | 0
      },
      notNeedUndo: 1
    }
  }
  // plugins/list.js
  /**
   * 有序列表,无序列表插件
   * @file
   * @since 1.2.6.1
   */
  UE.plugins['list'] = function () {
    var me = this,
      notExchange = {
        TD: 1,
        PRE: 1,
        BLOCKQUOTE: 1
      }
    var customStyle = {
      cn: 'cn-1-',
      cn1: 'cn-2-',
      cn2: 'cn-3-',
      num: 'num-1-',
      num1: 'num-2-',
      num2: 'num-3-',
      dash: 'dash',
      dot: 'dot'
    }
    me.setOpt({
      autoTransWordToList: false,
      insertorderedlist: {
        num: '',
        num1: '',
        num2: '',
        cn: '',
        cn1: '',
        cn2: '',
        decimal: '',
        'lower-alpha': '',
        'lower-roman': '',
        'upper-alpha': '',
        'upper-roman': ''
      },
      insertunorderedlist: {
        circle: '',
        disc: '',
        square: '',
        dash: '',
        dot: ''
      },
      listDefaultPaddingLeft: '30',
      listiconpath: 'http://bs.baidu.com/listicon/',
      maxListLevel: -1, //-1不限制
      disablePInList: false
    })
    function listToArray(list) {
      var arr = []
      for (var p in list) {
        arr.push(p)
      }
      return arr
    }
    var listStyle = {
      OL: listToArray(me.options.insertorderedlist),
      UL: listToArray(me.options.insertunorderedlist)
    }
    var liiconpath = me.options.listiconpath
    //根据用户配置,调整customStyle
    for (var s in customStyle) {
      if (
        !me.options.insertorderedlist.hasOwnProperty(s) &&
        !me.options.insertunorderedlist.hasOwnProperty(s)
      ) {
        delete customStyle[s]
      }
    }
    me.ready(function () {
      var customCss = []
      for (var p in customStyle) {
        if (p == 'dash' || p == 'dot') {
          customCss.push(
            'li.list-' +
              customStyle[p] +
              '{background-image:url(' +
              liiconpath +
              customStyle[p] +
              '.gif)}'
          )
          customCss.push(
            'ul.custom_' +
              p +
              '{list-style:none;}ul.custom_' +
              p +
              ' li{background-position:0 3px;background-repeat:no-repeat}'
          )
        } else {
          for (var i = 0; i < 99; i++) {
            customCss.push(
              'li.list-' +
                customStyle[p] +
                i +
                '{background-image:url(' +
                liiconpath +
                'list-' +
                customStyle[p] +
                i +
                '.gif)}'
            )
          }
          customCss.push(
            'ol.custom_' +
              p +
              '{list-style:none;}ol.custom_' +
              p +
              ' li{background-position:0 3px;background-repeat:no-repeat}'
          )
        }
        switch (p) {
          case 'cn':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
            break
          case 'cn1':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:30px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:55px}')
            break
          case 'cn2':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:40px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:55px}')
            customCss.push('li.list-' + p + '-paddingleft-3{padding-left:68px}')
            break
          case 'num':
          case 'num1':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:25px}')
            break
          case 'num2':
            customCss.push('li.list-' + p + '-paddingleft-1{padding-left:35px}')
            customCss.push('li.list-' + p + '-paddingleft-2{padding-left:40px}')
            break
          case 'dash':
            customCss.push('li.list-' + p + '-paddingleft{padding-left:35px}')
            break
          case 'dot':
            customCss.push('li.list-' + p + '-paddingleft{padding-left:20px}')
        }
      }
      customCss.push('.list-paddingleft-1{padding-left:0}')
      customCss.push(
        '.list-paddingleft-2{padding-left:' + me.options.listDefaultPaddingLeft + 'px}'
      )
      customCss.push(
        '.list-paddingleft-3{padding-left:' + me.options.listDefaultPaddingLeft * 2 + 'px}'
      )
      //如果不给宽度会在自定应样式里出现滚动条
      utils.cssRule(
        'list',
        'ol,ul{margin:0;pading:0;' +
          (browser.ie ? '' : 'width:95%') +
          '}li{clear:both;}' +
          customCss.join('\n'),
        me.document
      )
    })
    //单独处理剪切的问题
    me.ready(function () {
      domUtils.on(me.body, 'cut', function () {
        setTimeout(function () {
          var rng = me.selection.getRange(),
            li
          //trace:3416
          if (!rng.collapsed) {
            if ((li = domUtils.findParentByTagName(rng.startContainer, 'li', true))) {
              if (!li.nextSibling && domUtils.isEmptyBlock(li)) {
                var pn = li.parentNode,
                  node
                if ((node = pn.previousSibling)) {
                  domUtils.remove(pn)
                  rng.setStartAtLast(node).collapse(true)
                  rng.select(true)
                } else if ((node = pn.nextSibling)) {
                  domUtils.remove(pn)
                  rng.setStartAtFirst(node).collapse(true)
                  rng.select(true)
                } else {
                  var tmpNode = me.document.createElement('p')
                  domUtils.fillNode(me.document, tmpNode)
                  pn.parentNode.insertBefore(tmpNode, pn)
                  domUtils.remove(pn)
                  rng.setStart(tmpNode, 0).collapse(true)
                  rng.select(true)
                }
              }
            }
          }
        })
      })
    })
    function getStyle(node) {
      var cls = node.className
      if (domUtils.hasClass(node, /custom_/)) {
        return cls.match(/custom_(\w+)/)[1]
      }
      return domUtils.getStyle(node, 'list-style-type')
    }
    me.addListener('beforepaste', function (type, html) {
      var me = this,
        rng = me.selection.getRange(),
        li
      var root = UE.htmlparser(html.html, true)
      if ((li = domUtils.findParentByTagName(rng.startContainer, 'li', true))) {
        var list = li.parentNode,
          tagName = list.tagName == 'OL' ? 'ul' : 'ol'
        utils.each(root.getNodesByTagName(tagName), function (n) {
          n.tagName = list.tagName
          n.setAttr()
          if (n.parentNode === root) {
            type = getStyle(list) || (list.tagName == 'OL' ? 'decimal' : 'disc')
          } else {
            var className = n.parentNode.getAttr('class')
            if (className && /custom_/.test(className)) {
              type = className.match(/custom_(\w+)/)[1]
            } else {
              type = n.parentNode.getStyle('list-style-type')
            }
            if (!type) {
              type = list.tagName == 'OL' ? 'decimal' : 'disc'
            }
          }
          var index = utils.indexOf(listStyle[list.tagName], type)
          if (n.parentNode !== root)
            index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
          var currentStyle = listStyle[list.tagName][index]
          if (customStyle[currentStyle]) {
            n.setAttr('class', 'custom_' + currentStyle)
          } else {
            n.setStyle('list-style-type', currentStyle)
          }
        })
      }
      html.html = root.toHtml()
    })
    //导出时,去掉p标签
    me.getOpt('disablePInList') === true &&
      me.addOutputRule(function (root) {
        utils.each(root.getNodesByTagName('li'), function (li) {
          var newChildrens = [],
            index = 0
          utils.each(li.children, function (n) {
            if (n.tagName == 'p') {
              var tmpNode
              while ((tmpNode = n.children.pop())) {
                newChildrens.splice(index, 0, tmpNode)
                tmpNode.parentNode = li
                lastNode = tmpNode
              }
              tmpNode = newChildrens[newChildrens.length - 1]
              if (!tmpNode || tmpNode.type != 'element' || tmpNode.tagName != 'br') {
                var br = UE.uNode.createElement('br')
                br.parentNode = li
                newChildrens.push(br)
              }
              index = newChildrens.length
            }
          })
          if (newChildrens.length) {
            li.children = newChildrens
          }
        })
      })
    //进入编辑器的li要套p标签
    me.addInputRule(function (root) {
      utils.each(root.getNodesByTagName('li'), function (li) {
        var tmpP = UE.uNode.createElement('p')
        for (var i = 0, ci; (ci = li.children[i]); ) {
          if (ci.type == 'text' || dtd.p[ci.tagName]) {
            tmpP.appendChild(ci)
          } else {
            if (tmpP.firstChild()) {
              li.insertBefore(tmpP, ci)
              tmpP = UE.uNode.createElement('p')
              i = i + 2
            } else {
              i++
            }
          }
        }
        if ((tmpP.firstChild() && !tmpP.parentNode) || !li.firstChild()) {
          li.appendChild(tmpP)
        }
        //trace:3357
        //p不能为空
        if (!tmpP.firstChild()) {
          tmpP.innerHTML(browser.ie ? ' ' : '
'
                domUtils.fillNode(me.document, pre.firstChild)
              }
            }
            //                        }
            preventAndSave()
          }
        }
      }
      if (keyCode == 8) {
        //修中ie中li下的问题
        range = me.selection.getRange()
        if (range.collapsed && domUtils.isStartInblock(range)) {
          tmpRange = range.cloneRange().trimBoundary()
          li = domUtils.findParentByTagName(range.startContainer, 'li', true)
          //要在li的最左边,才能处理
          if (li && domUtils.isStartInblock(tmpRange)) {
            start = domUtils.findParentByTagName(range.startContainer, 'p', true)
            if (start && start !== li.firstChild) {
              var parentList = domUtils.findParentByTagName(start, ['ol', 'ul'])
              domUtils.breakParent(start, parentList)
              clearEmptySibling(start)
              me.fireEvent('contentchange')
              range.setStart(start, 0).setCursor(false, true)
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
              return
            }
            if (li && (pre = li.previousSibling)) {
              if (keyCode == 46 && li.childNodes.length) {
                return
              }
              //有可能上边的兄弟节点是个2级菜单,要追加到2级菜单的最后的li
              if (dtd.$list[pre.tagName]) {
                pre = pre.lastChild
              }
              me.undoManger && me.undoManger.save()
              first = li.firstChild
              if (domUtils.isBlockElm(first)) {
                if (domUtils.isEmptyNode(first)) {
                  //                                    range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
                  pre.appendChild(first)
                  range.setStart(first, 0).setCursor(false, true)
                  //first不是唯一的节点
                  while (li.firstChild) {
                    pre.appendChild(li.firstChild)
                  }
                } else {
                  span = me.document.createElement('span')
                  range.insertNode(span)
                  //判断pre是否是空的节点,如果是
类型的空节点,干掉p标签防止它占位
                  if (domUtils.isEmptyBlock(pre)) {
                    pre.innerHTML = ''
                  }
                  domUtils.moveChild(li, pre)
                  range.setStartBefore(span).collapse(true).select(true)
                  domUtils.remove(span)
                }
              } else {
                if (domUtils.isEmptyNode(li)) {
                  var p = me.document.createElement('p')
                  pre.appendChild(p)
                  range.setStart(p, 0).setCursor()
                  //                                    range.setEnd(pre, pre.childNodes.length).shrinkBoundary().collapse().select(true);
                } else {
                  range.setEnd(pre, pre.childNodes.length).collapse().select(true)
                  while (li.firstChild) {
                    pre.appendChild(li.firstChild)
                  }
                }
              }
              domUtils.remove(li)
              me.fireEvent('contentchange')
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
              return
            }
            //trace:980
            if (li && !li.previousSibling) {
              var parentList = li.parentNode
              var bk = range.createBookmark()
              if (domUtils.isTagNode(parentList.parentNode, 'ol ul')) {
                parentList.parentNode.insertBefore(li, parentList)
                if (domUtils.isEmptyNode(parentList)) {
                  domUtils.remove(parentList)
                }
              } else {
                while (li.firstChild) {
                  parentList.parentNode.insertBefore(li.firstChild, parentList)
                }
                domUtils.remove(li)
                if (domUtils.isEmptyNode(parentList)) {
                  domUtils.remove(parentList)
                }
              }
              range.moveToBookmark(bk).setCursor(false, true)
              me.fireEvent('contentchange')
              me.fireEvent('saveScene')
              domUtils.preventDefault(evt)
              return
            }
          }
        }
      }
    })
    me.addListener('keyup', function (type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 8) {
        var rng = me.selection.getRange(),
          list
        if ((list = domUtils.findParentByTagName(rng.startContainer, ['ol', 'ul'], true))) {
          adjustList(
            list,
            list.tagName.toLowerCase(),
            getStyle(list) || domUtils.getComputedStyle(list, 'list-style-type'),
            true
          )
        }
      }
    })
    //处理tab键
    me.addListener('tabkeydown', function () {
      var range = me.selection.getRange()
      //控制级数
      function checkLevel(li) {
        if (me.options.maxListLevel != -1) {
          var level = li.parentNode,
            levelNum = 0
          while (/[ou]l/i.test(level.tagName)) {
            levelNum++
            level = level.parentNode
          }
          if (levelNum >= me.options.maxListLevel) {
            return true
          }
        }
      }
      //只以开始为准
      //todo 后续改进
      var li = domUtils.findParentByTagName(range.startContainer, 'li', true)
      if (li) {
        var bk
        if (range.collapsed) {
          if (checkLevel(li)) return true
          var parentLi = li.parentNode,
            list = me.document.createElement(parentLi.tagName),
            index = utils.indexOf(
              listStyle[list.tagName],
              getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type')
            )
          index = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
          var currentStyle = listStyle[list.tagName][index]
          setListStyle(list, currentStyle)
          if (domUtils.isStartInblock(range)) {
            me.fireEvent('saveScene')
            bk = range.createBookmark()
            parentLi.insertBefore(list, li)
            list.appendChild(li)
            adjustList(list, list.tagName.toLowerCase(), currentStyle)
            me.fireEvent('contentchange')
            range.moveToBookmark(bk).select(true)
            return true
          }
        } else {
          me.fireEvent('saveScene')
          bk = range.createBookmark()
          for (
            var i = 0, closeList, parents = domUtils.findParents(li), ci;
            (ci = parents[i++]);
          ) {
            if (domUtils.isTagNode(ci, 'ol ul')) {
              closeList = ci
              break
            }
          }
          var current = li
          if (bk.end) {
            while (
              current &&
              !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)
            ) {
              if (checkLevel(current)) {
                current = domUtils.getNextDomNode(current, false, null, function (node) {
                  return node !== closeList
                })
                continue
              }
              var parentLi = current.parentNode,
                list = me.document.createElement(parentLi.tagName),
                index = utils.indexOf(
                  listStyle[list.tagName],
                  getStyle(parentLi) || domUtils.getComputedStyle(parentLi, 'list-style-type')
                )
              var currentIndex = index + 1 == listStyle[list.tagName].length ? 0 : index + 1
              var currentStyle = listStyle[list.tagName][currentIndex]
              setListStyle(list, currentStyle)
              parentLi.insertBefore(list, current)
              while (
                current &&
                !(domUtils.getPosition(current, bk.end) & domUtils.POSITION_FOLLOWING)
              ) {
                li = current.nextSibling
                list.appendChild(current)
                if (!li || domUtils.isTagNode(li, 'ol ul')) {
                  if (li) {
                    while ((li = li.firstChild)) {
                      if (li.tagName == 'LI') {
                        break
                      }
                    }
                  } else {
                    li = domUtils.getNextDomNode(current, false, null, function (node) {
                      return node !== closeList
                    })
                  }
                  break
                }
                current = li
              }
              adjustList(list, list.tagName.toLowerCase(), currentStyle)
              current = li
            }
          }
          me.fireEvent('contentchange')
          range.moveToBookmark(bk).select()
          return true
        }
      }
    })
    function getLi(start) {
      while (start && !domUtils.isBody(start)) {
        if (start.nodeName == 'TABLE') {
          return null
        }
        if (start.nodeName == 'LI') {
          return start
        }
        start = start.parentNode
      }
    }
    /**
     * 有序列表,与“insertunorderedlist”命令互斥
     * @command insertorderedlist
     * @method execCommand
     * @param { String } command 命令字符串
     * @param { String } style 插入的有序列表类型,值为:decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
     * @example
     * ```javascript
     * editor.execCommand( 'insertorderedlist','decimal');
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertorderedlist
     * @method queryCommandState
     * @param { String } cmd 命令字符串
     * @return { int } 如果当前选区是有序列表返回1,否则返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'insertorderedlist' );
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertorderedlist
     * @method queryCommandValue
     * @param { String } cmd 命令字符串
     * @return { String } 返回当前有序列表的类型,值为null或decimal,lower-alpha,lower-roman,upper-alpha,upper-roman,cn,cn1,cn2,num,num1,num2
     * @example
     * ```javascript
     * editor.queryCommandValue( 'insertorderedlist' );
     * ```
     */
    /**
     * 无序列表,与“insertorderedlist”命令互斥
     * @command insertunorderedlist
     * @method execCommand
     * @param { String } command 命令字符串
     * @param { String } style 插入的无序列表类型,值为:circle,disc,square,dash,dot
     * @example
     * ```javascript
     * editor.execCommand( 'insertunorderedlist','circle');
     * ```
     */
    /**
     * 查询当前是否有word文档粘贴进来的图片
     * @command insertunorderedlist
     * @method insertunorderedlist
     * @param { String } command 命令字符串
     * @return { int } 如果当前选区是无序列表返回1,否则返回0
     * @example
     * ```javascript
     * editor.queryCommandState( 'insertunorderedlist' );
     * ```
     */
    /**
     * 查询当前选区内容是否有序列表
     * @command insertunorderedlist
     * @method queryCommandValue
     * @param { String } command 命令字符串
     * @return { String } 返回当前无序列表的类型,值为null或circle,disc,square,dash,dot
     * @example
     * ```javascript
     * editor.queryCommandValue( 'insertunorderedlist' );
     * ```
     */
    me.commands['insertorderedlist'] = me.commands['insertunorderedlist'] = {
      execCommand: function (command, style) {
        if (!style) {
          style = command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc'
        }
        var me = this,
          range = this.selection.getRange(),
          filterFn = function (node) {
            return node.nodeType == 1
              ? node.tagName.toLowerCase() != 'br'
              : !domUtils.isWhitespace(node)
          },
          tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul',
          frag = me.document.createDocumentFragment()
        //去掉是因为会出现选到末尾,导致adjustmentBoundary缩到ol/ul的位置
        //range.shrinkBoundary();//.adjustmentBoundary();
        range.adjustmentBoundary().shrinkBoundary()
        var bko = range.createBookmark(true),
          start = getLi(me.document.getElementById(bko.start)),
          modifyStart = 0,
          end = getLi(me.document.getElementById(bko.end)),
          modifyEnd = 0,
          startParent,
          endParent,
          list,
          tmp
        if (start || end) {
          start && (startParent = start.parentNode)
          if (!bko.end) {
            end = start
          }
          end && (endParent = end.parentNode)
          if (startParent === endParent) {
            while (start !== end) {
              tmp = start
              start = start.nextSibling
              if (!domUtils.isBlockElm(tmp.firstChild)) {
                var p = me.document.createElement('p')
                while (tmp.firstChild) {
                  p.appendChild(tmp.firstChild)
                }
                tmp.appendChild(p)
              }
              frag.appendChild(tmp)
            }
            tmp = me.document.createElement('span')
            startParent.insertBefore(tmp, end)
            if (!domUtils.isBlockElm(end.firstChild)) {
              p = me.document.createElement('p')
              while (end.firstChild) {
                p.appendChild(end.firstChild)
              }
              end.appendChild(p)
            }
            frag.appendChild(end)
            domUtils.breakParent(tmp, startParent)
            if (domUtils.isEmptyNode(tmp.previousSibling)) {
              domUtils.remove(tmp.previousSibling)
            }
            if (domUtils.isEmptyNode(tmp.nextSibling)) {
              domUtils.remove(tmp.nextSibling)
            }
            var nodeStyle =
              getStyle(startParent) ||
              domUtils.getComputedStyle(startParent, 'list-style-type') ||
              (command.toLowerCase() == 'insertorderedlist' ? 'decimal' : 'disc')
            if (startParent.tagName.toLowerCase() == tag && nodeStyle == style) {
              for (
                var i = 0, ci, tmpFrag = me.document.createDocumentFragment();
                (ci = frag.firstChild);
              ) {
                if (domUtils.isTagNode(ci, 'ol ul')) {
                  //                                  删除时,子列表不处理
                  //                                  utils.each(domUtils.getElementsByTagName(ci,'li'),function(li){
                  //                                        while(li.firstChild){
                  //                                            tmpFrag.appendChild(li.firstChild);
                  //                                        }
                  //
                  //                                    });
                  tmpFrag.appendChild(ci)
                } else {
                  while (ci.firstChild) {
                    tmpFrag.appendChild(ci.firstChild)
                    domUtils.remove(ci)
                  }
                }
              }
              tmp.parentNode.insertBefore(tmpFrag, tmp)
            } else {
              list = me.document.createElement(tag)
              setListStyle(list, style)
              list.appendChild(frag)
              tmp.parentNode.insertBefore(list, tmp)
            }
            domUtils.remove(tmp)
            list && adjustList(list, tag, style)
            range.moveToBookmark(bko).select()
            return
          }
          //开始
          if (start) {
            while (start) {
              tmp = start.nextSibling
              if (domUtils.isTagNode(start, 'ol ul')) {
                frag.appendChild(start)
              } else {
                var tmpfrag = me.document.createDocumentFragment(),
                  hasBlock = 0
                while (start.firstChild) {
                  if (domUtils.isBlockElm(start.firstChild)) {
                    hasBlock = 1
                  }
                  tmpfrag.appendChild(start.firstChild)
                }
                if (!hasBlock) {
                  var tmpP = me.document.createElement('p')
                  tmpP.appendChild(tmpfrag)
                  frag.appendChild(tmpP)
                } else {
                  frag.appendChild(tmpfrag)
                }
                domUtils.remove(start)
              }
              start = tmp
            }
            startParent.parentNode.insertBefore(frag, startParent.nextSibling)
            if (domUtils.isEmptyNode(startParent)) {
              range.setStartBefore(startParent)
              domUtils.remove(startParent)
            } else {
              range.setStartAfter(startParent)
            }
            modifyStart = 1
          }
          if (end && domUtils.inDoc(endParent, me.document)) {
            //结束
            start = endParent.firstChild
            while (start && start !== end) {
              tmp = start.nextSibling
              if (domUtils.isTagNode(start, 'ol ul')) {
                frag.appendChild(start)
              } else {
                tmpfrag = me.document.createDocumentFragment()
                hasBlock = 0
                while (start.firstChild) {
                  if (domUtils.isBlockElm(start.firstChild)) {
                    hasBlock = 1
                  }
                  tmpfrag.appendChild(start.firstChild)
                }
                if (!hasBlock) {
                  tmpP = me.document.createElement('p')
                  tmpP.appendChild(tmpfrag)
                  frag.appendChild(tmpP)
                } else {
                  frag.appendChild(tmpfrag)
                }
                domUtils.remove(start)
              }
              start = tmp
            }
            var tmpDiv = domUtils.createElement(me.document, 'div', {
              tmpDiv: 1
            })
            domUtils.moveChild(end, tmpDiv)
            frag.appendChild(tmpDiv)
            domUtils.remove(end)
            endParent.parentNode.insertBefore(frag, endParent)
            range.setEndBefore(endParent)
            if (domUtils.isEmptyNode(endParent)) {
              domUtils.remove(endParent)
            }
            modifyEnd = 1
          }
        }
        if (!modifyStart) {
          range.setStartBefore(me.document.getElementById(bko.start))
        }
        if (bko.end && !modifyEnd) {
          range.setEndAfter(me.document.getElementById(bko.end))
        }
        range.enlarge(true, function (node) {
          return notExchange[node.tagName]
        })
        frag = me.document.createDocumentFragment()
        var bk = range.createBookmark(),
          current = domUtils.getNextDomNode(bk.start, false, filterFn),
          tmpRange = range.cloneRange(),
          tmpNode,
          block = domUtils.isBlockElm
        while (
          current &&
          current !== bk.end &&
          domUtils.getPosition(current, bk.end) & domUtils.POSITION_PRECEDING
        ) {
          if (current.nodeType == 3 || dtd.li[current.tagName]) {
            if (current.nodeType == 1 && dtd.$list[current.tagName]) {
              while (current.firstChild) {
                frag.appendChild(current.firstChild)
              }
              tmpNode = domUtils.getNextDomNode(current, false, filterFn)
              domUtils.remove(current)
              current = tmpNode
              continue
            }
            tmpNode = current
            tmpRange.setStartBefore(current)
            while (
              current &&
              current !== bk.end &&
              (!block(current) || domUtils.isBookmarkNode(current))
            ) {
              tmpNode = current
              current = domUtils.getNextDomNode(current, false, null, function (node) {
                return !notExchange[node.tagName]
              })
            }
            if (current && block(current)) {
              tmp = domUtils.getNextDomNode(tmpNode, false, filterFn)
              if (tmp && domUtils.isBookmarkNode(tmp)) {
                current = domUtils.getNextDomNode(tmp, false, filterFn)
                tmpNode = tmp
              }
            }
            tmpRange.setEndAfter(tmpNode)
            current = domUtils.getNextDomNode(tmpNode, false, filterFn)
            var li = range.document.createElement('li')
            li.appendChild(tmpRange.extractContents())
            if (domUtils.isEmptyNode(li)) {
              var tmpNode = range.document.createElement('p')
              while (li.firstChild) {
                tmpNode.appendChild(li.firstChild)
              }
              li.appendChild(tmpNode)
            }
            frag.appendChild(li)
          } else {
            current = domUtils.getNextDomNode(current, true, filterFn)
          }
        }
        range.moveToBookmark(bk).collapse(true)
        list = me.document.createElement(tag)
        setListStyle(list, style)
        list.appendChild(frag)
        range.insertNode(list)
        //当前list上下看能否合并
        adjustList(list, tag, style)
        //去掉冗余的tmpDiv
        for (
          var i = 0, ci, tmpDivs = domUtils.getElementsByTagName(list, 'div');
          (ci = tmpDivs[i++]);
        ) {
          if (ci.getAttribute('tmpDiv')) {
            domUtils.remove(ci, true)
          }
        }
        range.moveToBookmark(bko).select()
      },
      queryCommandState: function (command) {
        var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
        var path = this.selection.getStartElementPath()
        for (var i = 0, ci; (ci = path[i++]); ) {
          if (ci.nodeName == 'TABLE') {
            return 0
          }
          if (tag == ci.nodeName.toLowerCase()) {
            return 1
          }
        }
        return 0
      },
      queryCommandValue: function (command) {
        var tag = command.toLowerCase() == 'insertorderedlist' ? 'ol' : 'ul'
        var path = this.selection.getStartElementPath(),
          node
        for (var i = 0, ci; (ci = path[i++]); ) {
          if (ci.nodeName == 'TABLE') {
            node = null
            break
          }
          if (tag == ci.nodeName.toLowerCase()) {
            node = ci
            break
          }
        }
        return node ? getStyle(node) || domUtils.getComputedStyle(node, 'list-style-type') : null
      }
    }
  }
  // plugins/source.js
  /**
   * 源码编辑插件
   * @file
   * @since 1.2.6.1
   */
  ;(function () {
    var sourceEditors = {
      textarea: function (editor, holder) {
        var textarea = holder.ownerDocument.createElement('textarea')
        textarea.style.cssText =
          'position:absolute;resize:none;width:100%;height:100%;border:0;padding:0;margin:0;overflow-y:auto;'
        // 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 {
          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
          }
        }
      },
      codemirror: function (editor, holder) {
        var codeEditor = window.CodeMirror(holder, {
          mode: 'text/html',
          tabMode: 'indent',
          lineNumbers: true,
          lineWrapping: true
        })
        var dom = codeEditor.getWrapperElement()
        dom.style.cssText =
          'position:absolute;left:0;top:0;width:100%;height:100%;font-family:consolas,"Courier new",monospace;font-size:13px;'
        codeEditor.getScrollerElement().style.cssText =
          'position:absolute;left:0;top:0;width:100%;height:100%;'
        codeEditor.refresh()
        return {
          getCodeMirror: function () {
            return codeEditor
          },
          setContent: function (content) {
            codeEditor.setValue(content)
          },
          getContent: function () {
            return codeEditor.getValue()
          },
          select: function () {
            codeEditor.focus()
          },
          dispose: function () {
            holder.removeChild(dom)
            dom = null
            codeEditor = null
          }
        }
      }
    }
    UE.plugins['source'] = function () {
      var me = this
      var opt = this.options
      var sourceMode = false
      var sourceEditor
      var orgSetContent
      opt.sourceEditor = browser.ie ? 'textarea' : opt.sourceEditor || 'codemirror'
      me.setOpt({
        sourceEditorFirst: false
      })
      function createSourceEditor(holder) {
        return sourceEditors[
          opt.sourceEditor == 'codemirror' && window.CodeMirror ? 'codemirror' : 'textarea'
        ](me, holder)
      }
      var bakCssText
      //解决在源码模式下getContent不能得到最新的内容问题
      var oldGetContent, bakAddress
      /**
       * 切换源码模式和编辑模式
       * @command source
       * @method execCommand
       * @param { String } cmd 命令字符串
       * @example
       * ```javascript
       * editor.execCommand( 'source');
       * ```
       */
      /**
       * 查询当前编辑区域的状态是源码模式还是可视化模式
       * @command source
       * @method queryCommandState
       * @param { String } cmd 命令字符串
       * @return { int } 如果当前是源码编辑模式,返回1,否则返回0
       * @example
       * ```javascript
       * editor.queryCommandState( 'source' );
       * ```
       */
      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.iframe.style.cssText
            me.iframe.style.cssText += 'position:absolute;left:-32768px;top:-32768px;'
            me.fireEvent('beforegetcontent')
            var root = UE.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(/ /g, ' '))
                }
              }
            })
            me.fireEvent('aftergetcontent')
            var content = root.toHtml(true)
            sourceEditor = createSourceEditor(me.iframe.parentNode)
            sourceEditor.setContent(content)
            orgSetContent = me.setContent
            me.setContent = function (html) {
              //这里暂时不触发事件,防止报错
              var root = UE.htmlparser(html)
              me.filterInputRule(root)
              html = root.toHtml()
              sourceEditor.setContent(html)
            }
            setTimeout(function () {
              sourceEditor.select()
              me.addListener('fullscreenchanged', function () {
                try {
                  sourceEditor.getCodeMirror().refresh()
                } catch (e) {}
              })
            })
            //重置getContent,源码模式下取值也能是最新的数据
            oldGetContent = me.getContent
            me.getContent = function () {
              return sourceEditor.getContent() || '' + (browser.ie ? '' : '
'
            }
          } else {
            me.iframe.style.cssText = bakCssText
            var cont = sourceEditor.getContent() || '' + (browser.ie ? '' : '
'
            //处理掉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 = orgSetContent
            me.setContent(cont)
            sourceEditor.dispose()
            sourceEditor = null
            //还原getContent方法
            me.getContent = oldGetContent
            var first = me.body.firstChild
            //trace:1106 都删除空了,下边会报错,所以补充一个p占位
            if (!first) {
              me.body.innerHTML = '' + (browser.ie ? '' : '
'
              first = me.body.firstChild
            }
            //要在ifm为显示时ff才能取到selection,否则报错
            //这里不能比较位置了
            me.undoManger && me.undoManger.save(true)
            if (browser.gecko) {
              var input = document.createElement('input')
              input.style.cssText = 'position:absolute;left:0;top:-32768px'
              document.body.appendChild(input)
              me.body.contentEditable = false
              setTimeout(function () {
                domUtils.setViewportOffset(input, { left: -32768, top: 0 })
                input.focus()
                setTimeout(function () {
                  me.body.contentEditable = true
                  me.selection.getRange().moveToAddress(bakAddress).select(true)
                  domUtils.remove(input)
                })
              })
            } else {
              //ie下有可能报错,比如在代码顶头的情况
              try {
                me.selection.getRange().moveToAddress(bakAddress).select(true)
              } 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
            }
            ? 1
            : -1
        }
        return oldQueryCommandState.apply(this, arguments)
      }
      if (opt.sourceEditor == 'codemirror') {
        me.addListener('ready', function () {
          utils.loadFile(
            document,
            {
              src:
                opt.codeMirrorJsUrl ||
                opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.js',
              tag: 'script',
              type: 'text/javascript',
              defer: 'defer'
            },
            function () {
              if (opt.sourceEditorFirst) {
                setTimeout(function () {
                  me.execCommand('source')
                }, 0)
              }
            }
          )
          utils.loadFile(document, {
            tag: 'link',
            rel: 'stylesheet',
            type: 'text/css',
            href:
              opt.codeMirrorCssUrl || opt.UEDITOR_HOME_URL + 'third-party/codemirror/codemirror.css'
          })
        })
      }
    }
  })()
  // plugins/enterkey.js
  ///import core
  ///import plugins/undo.js
  ///commands 设置回车标签p或br
  ///commandsName  EnterKey
  ///commandsTitle  设置回车标签p或br
  /**
   * @description 处理回车
   * @author zhanyi
   */
  UE.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, '')
                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, '
')
              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)
            }
          }
        } else {
          evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
          if (!range.collapsed) {
            range.deleteContents()
            start = range.startContainer
            if (start.nodeType == 1 && (start = start.childNodes[range.startOffset])) {
              while (start.nodeType == 1) {
                if (dtd.$empty[start.tagName]) {
                  range.setStartBefore(start).setCursor()
                  if (me.undoManger) {
                    me.undoManger.save()
                  }
                  return false
                }
                if (!start.firstChild) {
                  var br = range.document.createElement('br')
                  start.appendChild(br)
                  range.setStart(start, 0).setCursor()
                  if (me.undoManger) {
                    me.undoManger.save()
                  }
                  return false
                }
                start = start.firstChild
              }
              if (start === range.startContainer.childNodes[range.startOffset]) {
                br = range.document.createElement('br')
                range.insertNode(br).setCursor()
              } else {
                range.setStart(start, 0).setCursor()
              }
            } else {
              br = range.document.createElement('br')
              range.insertNode(br).setStartAfter(br).setCursor()
            }
          } else {
            br = range.document.createElement('br')
            range.insertNode(br)
            var parent = br.parentNode
            if (parent.lastChild === br) {
              br.parentNode.insertBefore(br.cloneNode(true), br)
              range.setStartBefore(br)
            } else {
              range.setStartAfter(br)
            }
            range.setCursor()
          }
        }
      }
    })
  }
  // plugins/keystrokes.js
  /* 处理特殊键的兼容性问题 */
  UE.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]
' + (browser.ie ? '' : '
'
              rng.setStart(me.body.firstChild, 0).setCursor(false, true)
              me._selectionChange()
              return
            }
          }
        }
      }
      //处理backspace
      if (keyCode == keymap.Backspace) {
        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
          }
        }
      }
      //处理tab键的逻辑
      if (keyCode == keymap.Tab) {
        //不处理以下标签
        var excludeTagNameForTabKey = {
          ol: 1,
          ul: 1,
          table: 1
        }
        //处理组件里的tab按下事件
        if (me.fireEvent('tabkeydown', evt)) {
          domUtils.preventDefault(evt)
          return
        }
        var range = me.selection.getRange()
        me.fireEvent('saveScene')
        for (
          var i = 0,
            txt = '',
            tabSize = me.options.tabSize || 4,
            tabNode = me.options.tabNode || ' ';
          i < tabSize;
          i++
        ) {
          txt += tabNode
        }
        var span = me.document.createElement('span')
        span.innerHTML = txt + domUtils.fillChar
        if (range.collapsed) {
          range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
        } else {
          var filterFn = function (node) {
            return domUtils.isBlockElm(node) && !excludeTagNameForTabKey[node.tagName.toLowerCase()]
          }
          //普通的情况
          start = domUtils.findParent(range.startContainer, filterFn, true)
          end = domUtils.findParent(range.endContainer, filterFn, true)
          if (start && end && start === end) {
            range.deleteContents()
            range.insertNode(span.cloneNode(true).firstChild).setCursor(true)
          } else {
            var bookmark = range.createBookmark()
            range.enlarge(true)
            var bookmark2 = range.createBookmark(),
              current = domUtils.getNextDomNode(bookmark2.start, false, filterFn)
            while (
              current &&
              !(domUtils.getPosition(current, bookmark2.end) & domUtils.POSITION_FOLLOWING)
            ) {
              current.insertBefore(span.cloneNode(true).firstChild, current.firstChild)
              current = domUtils.getNextDomNode(current, false, filterFn)
            }
            range.moveToBookmark(bookmark2).moveToBookmark(bookmark).select()
          }
        }
        domUtils.preventDefault(evt)
      }
      //trace:1634
      //ff的del键在容器空的时候,也会删除
      if (browser.gecko && keyCode == 46) {
        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 == keymap.Backspace) {
        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 : ''
          break
        case 'video':
          var ext = url.substr(url.lastIndexOf('.') + 1)
          if (ext == 'ogv') ext = 'ogg'
          str =
            '' +
            ' '
          break
      }
      return str
    }
    function switchImgAndVideo(root, img2video) {
      utils.each(root.getNodesByTagName(img2video ? 'img' : 'embed video'), function (node) {
        var className = node.getAttr('class')
        if (className && className.indexOf('edui-faked-video') != -1) {
          var html = creatInsertStr(
            img2video ? node.getAttr('_url') : node.getAttr('src'),
            node.getAttr('width'),
            node.getAttr('height'),
            null,
            node.getStyle('float') || '',
            className,
            img2video ? 'embed' : 'image'
          )
          node.parentNode.replaceChild(UE.uNode.createElement(html), node)
        }
        if (className && className.indexOf('edui-upload-video') != -1) {
          var html = creatInsertStr(
            img2video ? node.getAttr('_url') : node.getAttr('src'),
            node.getAttr('width'),
            node.getAttr('height'),
            null,
            node.getStyle('float') || '',
            className,
            img2video ? 'video' : 'image'
          )
          node.parentNode.replaceChild(UE.uNode.createElement(html), node)
        }
      })
    }
    me.addOutputRule(function (root) {
      switchImgAndVideo(root, true)
    })
    me.addInputRule(function (root) {
      switchImgAndVideo(root)
    })
    /**
     * 插入视频
     * @command insertvideo
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @param { Object } videoAttr 键值对对象, 描述一个视频的所有属性
     * @example
     * ```javascript
     *
     * var videoAttr = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * };
     *
     * //editor 是编辑器实例
     * //向编辑器插入单个视频
     * editor.execCommand( 'insertvideo', videoAttr );
     * ```
     */
    /**
     * 插入视频
     * @command insertvideo
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @param { Array } videoArr 需要插入的视频的数组, 其中的每一个元素都是一个键值对对象, 描述了一个视频的所有属性
     * @example
     * ```javascript
     *
     * var videoAttr1 = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * },
     * videoAttr2 = {
     *      //视频地址
     *      url: 'http://www.youku.com/xxx',
     *      //视频宽高值, 单位px
     *      width: 200,
     *      height: 100
     * }
     *
     * //editor 是编辑器实例
     * //该方法将会向编辑器内插入两个视频
     * editor.execCommand( 'insertvideo', [ videoAttr1, videoAttr2 ] );
     * ```
     */
    /**
     * 查询当前光标所在处是否是一个视频
     * @command insertvideo
     * @method queryCommandState
     * @param { String } cmd 需要查询的命令字符串
     * @return { int } 如果当前光标所在处的元素是一个视频对象, 则返回1,否则返回0
     * @example
     * ```javascript
     *
     * //editor 是编辑器实例
     * editor.queryCommandState( 'insertvideo' );
     * ```
     */
    me.commands['insertvideo'] = {
      execCommand: function (cmd, videoObjs, type) {
        videoObjs = utils.isArray(videoObjs) ? videoObjs : [videoObjs]
        var html = [],
          id = 'tmpVedio',
          cl
        for (var i = 0, vi, len = videoObjs.length; i < len; i++) {
          vi = videoObjs[i]
          cl = type == 'upload' ? 'edui-upload-video video-js vjs-default-skin' : 'edui-faked-video'
          html.push(
            creatInsertStr(vi.url, vi.width || 420, vi.height || 280, id + i, null, cl, 'image')
          )
        }
        me.execCommand('inserthtml', html.join(''), true)
        var rng = this.selection.getRange()
        for (var i = 0, len = videoObjs.length; i < len; i++) {
          var img = this.document.getElementById('tmpVedio' + i)
          domUtils.removeAttributes(img, 'id')
          rng.selectNode(img).select()
          me.execCommand('imagefloat', videoObjs[i].align)
        }
      },
      queryCommandState: function () {
        var img = me.selection.getRange().getClosedNode(),
          flag =
            img &&
            (img.className == 'edui-faked-video' ||
              img.className.indexOf('edui-upload-video') != -1)
        return flag ? 1 : 0
      }
    }
  }
  // plugins/table.core.js
  /**
   * Created with JetBrains WebStorm.
   * User: taoqili
   * Date: 13-1-18
   * Time: 上午11:09
   * To change this template use File | Settings | File Templates.
   */
  /**
   * UE表格操作类
   * @param table
   * @constructor
   */
  ;(function () {
    var UETable = (UE.UETable = function (table) {
      this.table = table
      this.indexTable = []
      this.selectedTds = []
      this.cellsRange = {}
      this.update(table)
    })
    //===以下为静态工具方法===
    UETable.removeSelectedClass = function (cells) {
      utils.each(cells, function (cell) {
        domUtils.removeClasses(cell, 'selectTdClass')
      })
    }
    UETable.addSelectedClass = function (cells) {
      utils.each(cells, function (cell) {
        domUtils.addClass(cell, 'selectTdClass')
      })
    }
    UETable.isEmptyBlock = function (node) {
      var reg = new RegExp(domUtils.fillChar, 'g')
      if (
        node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '')
          .length > 0
      ) {
        return 0
      }
      for (var i in dtd.$isNotEmpty)
        if (dtd.$isNotEmpty.hasOwnProperty(i)) {
          if (node.getElementsByTagName(i).length) {
            return 0
          }
        }
      return 1
    }
    UETable.getWidth = function (cell) {
      if (!cell) return 0
      return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
    }
    /**
     * 获取单元格或者单元格组的“对齐”状态。 如果当前的检测对象是一个单元格组, 只有在满足所有单元格的 水平和竖直 对齐属性都相同的
     * 条件时才会返回其状态值,否则将返回null; 如果当前只检测了一个单元格, 则直接返回当前单元格的对齐状态;
     * @param table cell or table cells , 支持单个单元格dom对象 或者 单元格dom对象数组
     * @return { align: 'left' || 'right' || 'center', valign: 'top' || 'middle' || 'bottom' } 或者 null
     */
    UETable.getTableCellAlignState = function (cells) {
      !utils.isArray(cells) && (cells = [cells])
      var result = {},
        status = ['align', 'valign'],
        tempStatus = null,
        isSame = true //状态是否相同
      utils.each(cells, function (cellNode) {
        utils.each(status, function (currentState) {
          tempStatus = cellNode.getAttribute(currentState)
          if (!result[currentState] && tempStatus) {
            result[currentState] = tempStatus
          } else if (!result[currentState] || tempStatus !== result[currentState]) {
            isSame = false
            return false
          }
        })
        return isSame
      })
      return isSame ? result : null
    }
    /**
     * 根据当前选区获取相关的table信息
     * @return {Object}
     */
    UETable.getTableItemsByRange = function (editor) {
      var start = editor.selection.getStart()
      //ff下会选中bookmark
      if (
        start &&
        start.id &&
        start.id.indexOf('_baidu_bookmark_start_') === 0 &&
        start.nextSibling
      ) {
        start = start.nextSibling
      }
      //在table或者td边缘有可能存在选中tr的情况
      var cell = start && domUtils.findParentByTagName(start, ['td', 'th'], true),
        tr = cell && cell.parentNode,
        caption = start && domUtils.findParentByTagName(start, 'caption', true),
        table = caption ? caption.parentNode : tr && tr.parentNode.parentNode
      return {
        cell: cell,
        tr: tr,
        table: table,
        caption: caption
      }
    }
    UETable.getUETableBySelected = function (editor) {
      var table = UETable.getTableItemsByRange(editor).table
      if (table && table.ueTable && table.ueTable.selectedTds.length) {
        return table.ueTable
      }
      return null
    }
    UETable.getDefaultValue = function (editor, table) {
      var borderMap = {
          thin: '0px',
          medium: '1px',
          thick: '2px'
        },
        tableBorder,
        tdPadding,
        tdBorder,
        tmpValue
      if (!table) {
        table = editor.document.createElement('table')
        table.insertRow(0).insertCell(0).innerHTML = 'xxx'
        editor.body.appendChild(table)
        var td = table.getElementsByTagName('td')[0]
        tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
        tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'padding-left')
        tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
        tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        domUtils.remove(table)
        return {
          tableBorder: tableBorder,
          tdPadding: tdPadding,
          tdBorder: tdBorder
        }
      } else {
        td = table.getElementsByTagName('td')[0]
        tmpValue = domUtils.getComputedStyle(table, 'border-left-width')
        tableBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'padding-left')
        tdPadding = parseInt(borderMap[tmpValue] || tmpValue, 10)
        tmpValue = domUtils.getComputedStyle(td, 'border-left-width')
        tdBorder = parseInt(borderMap[tmpValue] || tmpValue, 10)
        return {
          tableBorder: tableBorder,
          tdPadding: tdPadding,
          tdBorder: tdBorder
        }
      }
    }
    /**
     * 根据当前点击的td或者table获取索引对象
     * @param tdOrTable
     */
    UETable.getUETable = function (tdOrTable) {
      var tag = tdOrTable.tagName.toLowerCase()
      tdOrTable =
        tag == 'td' || tag == 'th' || tag == 'caption'
          ? domUtils.findParentByTagName(tdOrTable, 'table', true)
          : tdOrTable
      if (!tdOrTable.ueTable) {
        tdOrTable.ueTable = new UETable(tdOrTable)
      }
      return tdOrTable.ueTable
    }
    UETable.cloneCell = function (cell, ignoreMerge, keepPro) {
      if (!cell || utils.isString(cell)) {
        return this.table.ownerDocument.createElement(cell || 'td')
      }
      var flag = domUtils.hasClass(cell, 'selectTdClass')
      flag && domUtils.removeClasses(cell, 'selectTdClass')
      var tmpCell = cell.cloneNode(true)
      if (ignoreMerge) {
        tmpCell.rowSpan = tmpCell.colSpan = 1
      }
      //去掉宽高
      !keepPro && domUtils.removeAttributes(tmpCell, 'width height')
      !keepPro && domUtils.removeAttributes(tmpCell, 'style')
      tmpCell.style.borderLeftStyle = ''
      tmpCell.style.borderTopStyle = ''
      tmpCell.style.borderLeftColor = cell.style.borderRightColor
      tmpCell.style.borderLeftWidth = cell.style.borderRightWidth
      tmpCell.style.borderTopColor = cell.style.borderBottomColor
      tmpCell.style.borderTopWidth = cell.style.borderBottomWidth
      flag && domUtils.addClass(cell, 'selectTdClass')
      return tmpCell
    }
    UETable.prototype = {
      getMaxRows: function () {
        var rows = this.table.rows,
          maxLen = 1
        for (var i = 0, row; (row = rows[i]); i++) {
          var currentMax = 1
          for (var j = 0, cj; (cj = row.cells[j++]); ) {
            currentMax = Math.max(cj.rowSpan || 1, currentMax)
          }
          maxLen = Math.max(currentMax + i, maxLen)
        }
        return maxLen
      },
      /**
       * 获取当前表格的最大列数
       */
      getMaxCols: function () {
        var rows = this.table.rows,
          maxLen = 0,
          cellRows = {}
        for (var i = 0, row; (row = rows[i]); i++) {
          var cellsNum = 0
          for (var j = 0, cj; (cj = row.cells[j++]); ) {
            cellsNum += cj.colSpan || 1
            if (cj.rowSpan && cj.rowSpan > 1) {
              for (var k = 1; k < cj.rowSpan; k++) {
                if (!cellRows['row_' + (i + k)]) {
                  cellRows['row_' + (i + k)] = cj.colSpan || 1
                } else {
                  cellRows['row_' + (i + k)]++
                }
              }
            }
          }
          cellsNum += cellRows['row_' + i] || 0
          maxLen = Math.max(cellsNum, maxLen)
        }
        return maxLen
      },
      getCellColIndex: function (cell) {},
      /**
       * 获取当前cell旁边的单元格,
       * @param cell
       * @param right
       */
      getHSideCell: function (cell, right) {
        try {
          var cellInfo = this.getCellInfo(cell),
            previewRowIndex,
            previewColIndex
          var len = this.selectedTds.length,
            range = this.cellsRange
          //首行或者首列没有前置单元格
          if (
            (!right && (!len ? !cellInfo.colIndex : !range.beginColIndex)) ||
            (right &&
              (!len
                ? cellInfo.colIndex == this.colsNum - 1
                : range.endColIndex == this.colsNum - 1))
          )
            return null
          previewRowIndex = !len ? cellInfo.rowIndex : range.beginRowIndex
          previewColIndex = !right
            ? !len
              ? cellInfo.colIndex < 1
                ? 0
                : cellInfo.colIndex - 1
              : range.beginColIndex - 1
            : !len
            ? cellInfo.colIndex + 1
            : range.endColIndex + 1
          return this.getCell(
            this.indexTable[previewRowIndex][previewColIndex].rowIndex,
            this.indexTable[previewRowIndex][previewColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      getTabNextCell: function (cell, preRowIndex) {
        var cellInfo = this.getCellInfo(cell),
          rowIndex = preRowIndex || cellInfo.rowIndex,
          colIndex = cellInfo.colIndex + 1 + (cellInfo.colSpan - 1),
          nextCell
        try {
          nextCell = this.getCell(
            this.indexTable[rowIndex][colIndex].rowIndex,
            this.indexTable[rowIndex][colIndex].cellIndex
          )
        } catch (e) {
          try {
            rowIndex = rowIndex * 1 + 1
            colIndex = 0
            nextCell = this.getCell(
              this.indexTable[rowIndex][colIndex].rowIndex,
              this.indexTable[rowIndex][colIndex].cellIndex
            )
          } catch (e) {}
        }
        return nextCell
      },
      /**
       * 获取视觉上的后置单元格
       * @param cell
       * @param bottom
       */
      getVSideCell: function (cell, bottom, ignoreRange) {
        try {
          var cellInfo = this.getCellInfo(cell),
            nextRowIndex,
            nextColIndex
          var len = this.selectedTds.length && !ignoreRange,
            range = this.cellsRange
          //末行或者末列没有后置单元格
          if (
            (!bottom && cellInfo.rowIndex == 0) ||
            (bottom &&
              (!len
                ? cellInfo.rowIndex + cellInfo.rowSpan > this.rowsNum - 1
                : range.endRowIndex == this.rowsNum - 1))
          )
            return null
          nextRowIndex = !bottom
            ? !len
              ? cellInfo.rowIndex - 1
              : range.beginRowIndex - 1
            : !len
            ? cellInfo.rowIndex + cellInfo.rowSpan
            : range.endRowIndex + 1
          nextColIndex = !len ? cellInfo.colIndex : range.beginColIndex
          return this.getCell(
            this.indexTable[nextRowIndex][nextColIndex].rowIndex,
            this.indexTable[nextRowIndex][nextColIndex].cellIndex
          )
        } catch (e) {
          showError(e)
        }
      },
      /**
       * 获取相同结束位置的单元格,xOrY指代了是获取x轴相同还是y轴相同
       */
      getSameEndPosCells: function (cell, xOrY) {
        try {
          var flag = xOrY.toLowerCase() === 'x',
            end =
              domUtils.getXY(cell)[flag ? 'x' : 'y'] + cell['offset' + (flag ? 'Width' : 'Height')],
            rows = this.table.rows,
            cells = null,
            returns = []
          for (var i = 0; i < this.rowsNum; i++) {
            cells = rows[i].cells
            for (var j = 0, tmpCell; (tmpCell = cells[j++]); ) {
              var tmpEnd =
                domUtils.getXY(tmpCell)[flag ? 'x' : 'y'] +
                tmpCell['offset' + (flag ? 'Width' : 'Height')]
              //对应行的td已经被上面行rowSpan了
              if (tmpEnd > end && flag) break
              if (cell == tmpCell || end == tmpEnd) {
                //只获取单一的单元格
                //todo 仅获取单一单元格在特定情况下会造成returns为空,从而影响后续的拖拽实现,修正这个。需考虑性能
                if (tmpCell[flag ? 'colSpan' : 'rowSpan'] == 1) {
                  returns.push(tmpCell)
                }
                if (flag) break
              }
            }
          }
          return returns
        } catch (e) {
          showError(e)
        }
      },
      setCellContent: function (cell, content) {
        cell.innerHTML = content || (browser.ie ? domUtils.fillChar : '')
            for (var c = 0; c < colsNum; c++) {
              html.push(
                '' +
                  (browser.ie && browser.version < 11 ? domUtils.fillChar : ' '
              )
            }
            html.push(' ')
          }
          //禁止指定table-width
          return ''
        }
        if (!opt) {
          opt = utils.extend(
            {},
            {
              numCols: this.options.defaultCols,
              numRows: this.options.defaultRows,
              tdvalign: this.options.tdvalign
            }
          )
        }
        var me = this
        var range = this.selection.getRange(),
          start = range.startContainer,
          firstParentBlock =
            domUtils.findParent(
              start,
              function (node) {
                return domUtils.isBlockElm(node)
              },
              true
            ) || me.body
        var defaultValue = getDefaultValue(me),
          tableWidth = firstParentBlock.offsetWidth,
          tdWidth = Math.floor(
            tableWidth / opt.numCols - defaultValue.tdPadding * 2 - defaultValue.tdBorder
          )
        //todo其他属性
        !opt.tdvalign && (opt.tdvalign = me.options.tdvalign)
        me.execCommand('inserthtml', createTable(opt, tdWidth))
      }
    }
    UE.commands['insertparagraphbeforetable'] = {
      queryCommandState: function () {
        return getTableItemsByRange(this).cell ? 0 : -1
      },
      execCommand: function () {
        var table = getTableItemsByRange(this).table
        if (table) {
          var p = this.document.createElement('p')
          p.innerHTML = browser.ie ? ' ' : '' +
                table.innerHTML.replace(/>\s*<').replace(/\bth\b/gi, 'td') +
                '
'
            )
          }
          me.fireEvent('contentchange')
          me.fireEvent('saveScene')
          html.html = ''
          return true
        } else {
          var div = me.document.createElement('div'),
            tables
          div.innerHTML = html.html
          tables = div.getElementsByTagName('table')
          if (domUtils.findParentByTagName(me.selection.getStart(), 'table')) {
            utils.each(tables, function (t) {
              domUtils.remove(t)
            })
            if (domUtils.findParentByTagName(me.selection.getStart(), 'caption', true)) {
              div.innerHTML = div[browser.ie ? 'innerText' : 'textContent']
            }
          } else {
            utils.each(tables, function (table) {
              removeStyleSize(table, true)
              domUtils.removeAttributes(table, ['style', 'border'])
              utils.each(domUtils.getElementsByTagName(table, 'td'), function (td) {
                if (isEmptyBlock(td)) {
                  domUtils.fillNode(me.document, td)
                }
                removeStyleSize(td, true)
                //                            domUtils.removeAttributes(td, ['style'])
              })
            })
          }
          html.html = div.innerHTML
        }
      })
      me.addListener('afterpaste', function () {
        utils.each(domUtils.getElementsByTagName(me.body, 'table'), function (table) {
          if (table.offsetWidth > me.body.offsetWidth) {
            var defaultValue = getDefaultValue(me, table)
            table.style.width =
              me.body.offsetWidth -
              (needIEHack
                ? parseInt(domUtils.getComputedStyle(me.body, 'margin-left'), 10) * 2
                : 0) -
              defaultValue.tableBorder * 2 -
              (me.options.offsetWidth || 0) +
              'px'
          }
        })
      })
      me.addListener('blur', function () {
        tableCopyList = null
      })
      var timer
      me.addListener('keydown', function () {
        clearTimeout(timer)
        timer = setTimeout(function () {
          var rng = me.selection.getRange(),
            cell = domUtils.findParentByTagName(rng.startContainer, ['th', 'td'], true)
          if (cell) {
            var table = cell.parentNode.parentNode.parentNode
            if (table.offsetWidth > table.getAttribute('width')) {
              cell.style.wordBreak = 'break-all'
            }
          }
        }, 100)
      })
      me.addListener('selectionchange', function () {
        toggleDraggableState(me, false, '', null)
      })
      //内容变化时触发索引更新
      //todo 可否考虑标记检测,如果不涉及表格的变化就不进行索引重建和更新
      me.addListener('contentchange', function () {
        var me = this
        //尽可能排除一些不需要更新的状况
        hideDragLine(me)
        if (getUETableBySelected(me)) return
        var rng = me.selection.getRange()
        var start = rng.startContainer
        start = domUtils.findParentByTagName(start, ['td', 'th'], true)
        utils.each(domUtils.getElementsByTagName(me.document, 'table'), function (table) {
          if (me.fireEvent('excludetable', table) === true) return
          table.ueTable = new UT(table)
          //trace:3742
          //                utils.each(domUtils.getElementsByTagName(me.document, 'td'), function (td) {
          //
          //                    if (domUtils.isEmptyBlock(td) && td !== start) {
          //                        domUtils.fillNode(me.document, td);
          //                        if (browser.ie && browser.version == 6) {
          //                            td.innerHTML = ' '
          //                        }
          //                    }
          //                });
          //                utils.each(domUtils.getElementsByTagName(me.document, 'th'), function (th) {
          //                    if (domUtils.isEmptyBlock(th) && th !== start) {
          //                        domUtils.fillNode(me.document, th);
          //                        if (browser.ie && browser.version == 6) {
          //                            th.innerHTML = ' '
          //                        }
          //                    }
          //                });
          table.onmouseover = function () {
            me.fireEvent('tablemouseover', table)
          }
          table.onmousemove = function () {
            me.fireEvent('tablemousemove', table)
            me.options.tableDragable && toggleDragButton(true, this, me)
            utils.defer(function () {
              me.fireEvent('contentchange', 50)
            }, true)
          }
          table.onmouseout = function () {
            me.fireEvent('tablemouseout', table)
            toggleDraggableState(me, false, '', null)
            hideDragLine(me)
          }
          table.onclick = function (evt) {
            evt = me.window.event || evt
            var target = getParentTdOrTh(evt.target || evt.srcElement)
            if (!target) return
            var ut = getUETable(target),
              table = ut.table,
              cellInfo = ut.getCellInfo(target),
              cellsRange,
              rng = me.selection.getRange()
            //                    if ("topLeft" == inPosition(table, mouseCoords(evt))) {
            //                        cellsRange = ut.getCellsRange(ut.table.rows[0].cells[0], ut.getLastCell());
            //                        ut.setSelected(cellsRange);
            //                        return;
            //                    }
            //                    if ("bottomRight" == inPosition(table, mouseCoords(evt))) {
            //
            //                        return;
            //                    }
            if (inTableSide(table, target, evt, true)) {
              var endTdCol = ut.getCell(
                ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].rowIndex,
                ut.indexTable[ut.rowsNum - 1][cellInfo.colIndex].cellIndex
              )
              if (evt.shiftKey && ut.selectedTds.length) {
                if (ut.selectedTds[0] !== endTdCol) {
                  cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdCol)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdCol).select()
                }
              } else {
                if (target !== endTdCol) {
                  cellsRange = ut.getCellsRange(target, endTdCol)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdCol).select()
                }
              }
              return
            }
            if (inTableSide(table, target, evt)) {
              var endTdRow = ut.getCell(
                ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].rowIndex,
                ut.indexTable[cellInfo.rowIndex][ut.colsNum - 1].cellIndex
              )
              if (evt.shiftKey && ut.selectedTds.length) {
                if (ut.selectedTds[0] !== endTdRow) {
                  cellsRange = ut.getCellsRange(ut.selectedTds[0], endTdRow)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdRow).select()
                }
              } else {
                if (target !== endTdRow) {
                  cellsRange = ut.getCellsRange(target, endTdRow)
                  ut.setSelected(cellsRange)
                } else {
                  rng && rng.selectNodeContents(endTdRow).select()
                }
              }
            }
          }
        })
        switchBorderColor(me, true)
      })
      domUtils.on(me.document, 'mousemove', mouseMoveEvent)
      domUtils.on(me.document, 'mouseout', function (evt) {
        var target = evt.target || evt.srcElement
        if (target.tagName == 'TABLE') {
          toggleDraggableState(me, false, '', null)
        }
      })
      /**
       * 表格隔行变色
       */
      me.addListener('interlacetable', function (type, table, classList) {
        if (!table) return
        var me = this,
          rows = table.rows,
          len = rows.length,
          getClass = function (list, index, repeat) {
            return list[index] ? list[index] : repeat ? list[index % list.length] : ''
          }
        for (var i = 0; i < len; i++) {
          rows[i].className = getClass(classList || me.options.classList, i, true)
        }
      })
      me.addListener('uninterlacetable', function (type, table) {
        if (!table) return
        var me = this,
          rows = table.rows,
          classList = me.options.classList,
          len = rows.length
        for (var i = 0; i < len; i++) {
          domUtils.removeClasses(rows[i], classList)
        }
      })
      me.addListener('mousedown', mouseDownEvent)
      me.addListener('mouseup', mouseUpEvent)
      //拖动的时候触发mouseup
      domUtils.on(me.body, 'dragstart', function (evt) {
        mouseUpEvent.call(me, 'dragstart', evt)
      })
      me.addOutputRule(function (root) {
        utils.each(root.getNodesByTagName('div'), function (n) {
          if (n.getAttr('id') == 'ue_tableDragLine') {
            n.parentNode.removeChild(n)
          }
        })
      })
      var currentRowIndex = 0
      me.addListener('mousedown', function () {
        currentRowIndex = 0
      })
      me.addListener('tabkeydown', function () {
        var range = this.selection.getRange(),
          common = range.getCommonAncestor(true, true),
          table = domUtils.findParentByTagName(common, 'table')
        if (table) {
          if (domUtils.findParentByTagName(common, 'caption', true)) {
            var cell = domUtils.getElementsByTagName(table, 'th td')
            if (cell && cell.length) {
              range.setStart(cell[0], 0).setCursor(false, true)
            }
          } else {
            var cell = domUtils.findParentByTagName(common, ['td', 'th'], true),
              ua = getUETable(cell)
            currentRowIndex = cell.rowSpan > 1 ? currentRowIndex : ua.getCellInfo(cell).rowIndex
            var nextCell = ua.getTabNextCell(cell, currentRowIndex)
            if (nextCell) {
              if (isEmptyBlock(nextCell)) {
                range.setStart(nextCell, 0).setCursor(false, true)
              } else {
                range.selectNodeContents(nextCell).select()
              }
            } else {
              me.fireEvent('saveScene')
              me.__hasEnterExecCommand = true
              this.execCommand('insertrownext')
              me.__hasEnterExecCommand = false
              range = this.selection.getRange()
              range.setStart(table.rows[table.rows.length - 1].cells[0], 0).setCursor()
              me.fireEvent('saveScene')
            }
          }
          return true
        }
      })
      browser.ie &&
        me.addListener('selectionchange', function () {
          toggleDraggableState(this, false, '', null)
        })
      me.addListener('keydown', function (type, evt) {
        var me = this
        //处理在表格的最后一个输入tab产生新的表格
        var keyCode = evt.keyCode || evt.which
        if (keyCode == 8 || keyCode == 46) {
          return
        }
        var notCtrlKey = !evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey
        notCtrlKey && removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td'))
        var ut = getUETableBySelected(me)
        if (!ut) return
        notCtrlKey && ut.clearSelected()
      })
      me.addListener('beforegetcontent', function () {
        switchBorderColor(this, false)
        browser.ie &&
          utils.each(this.document.getElementsByTagName('caption'), function (ci) {
            if (domUtils.isEmptyNode(ci)) {
              ci.innerHTML = ' '
            }
          })
      })
      me.addListener('aftergetcontent', function () {
        switchBorderColor(this, true)
      })
      me.addListener('getAllHtml', function () {
        removeSelectedClass(me.document.getElementsByTagName('td'))
      })
      //修正全屏状态下插入的表格宽度在非全屏状态下撑开编辑器的情况
      me.addListener('fullscreenchanged', function (type, fullscreen) {
        if (!fullscreen) {
          var ratio = this.body.offsetWidth / document.body.offsetWidth,
            tables = domUtils.getElementsByTagName(this.body, 'table')
          utils.each(tables, function (table) {
            if (table.offsetWidth < me.body.offsetWidth) return false
            var tds = domUtils.getElementsByTagName(table, 'td'),
              backWidths = []
            utils.each(tds, function (td) {
              backWidths.push(td.offsetWidth)
            })
            for (var i = 0, td; (td = tds[i]); i++) {
              td.setAttribute('width', Math.floor(backWidths[i] * ratio))
            }
            table.setAttribute(
              'width',
              Math.floor(getTableWidth(me, needIEHack, getDefaultValue(me)))
            )
          })
        }
      })
      //重写execCommand命令,用于处理框选时的处理
      var oldExecCommand = me.execCommand
      me.execCommand = function (cmd, datatat) {
        var me = this,
          args = arguments
        cmd = cmd.toLowerCase()
        var ut = getUETableBySelected(me),
          tds,
          range = new dom.Range(me.document),
          cmdFun = me.commands[cmd] || UE.commands[cmd],
          result
        if (!cmdFun) return
        if (ut && !commands[cmd] && !cmdFun.notNeedUndo && !me.__hasEnterExecCommand) {
          me.__hasEnterExecCommand = true
          me.fireEvent('beforeexeccommand', cmd)
          tds = ut.selectedTds
          var lastState = -2,
            lastValue = -2,
            value,
            state
          for (var i = 0, td; (td = tds[i]); i++) {
            if (isEmptyBlock(td)) {
              range.setStart(td, 0).setCursor(false, true)
            } else {
              range.selectNode(td).select(true)
            }
            state = me.queryCommandState(cmd)
            value = me.queryCommandValue(cmd)
            if (state != -1) {
              if (lastState !== state || lastValue !== value) {
                me._ignoreContentChange = true
                result = oldExecCommand.apply(me, arguments)
                me._ignoreContentChange = false
              }
              lastState = me.queryCommandState(cmd)
              lastValue = me.queryCommandValue(cmd)
              if (domUtils.isEmptyBlock(td)) {
                domUtils.fillNode(me.document, td)
              }
            }
          }
          range.setStart(tds[0], 0).shrinkBoundary(true).setCursor(false, true)
          me.fireEvent('contentchange')
          me.fireEvent('afterexeccommand', cmd)
          me.__hasEnterExecCommand = false
          me._selectionChange()
        } else {
          result = oldExecCommand.apply(me, arguments)
        }
        return result
      }
    })
    /**
     * 删除obj的宽高style,改成属性宽高
     * @param obj
     * @param replaceToProperty
     */
    function removeStyleSize(obj, replaceToProperty) {
      removeStyle(obj, 'width', true)
      removeStyle(obj, 'height', true)
    }
    function removeStyle(obj, styleName, replaceToProperty) {
      if (obj.style[styleName]) {
        replaceToProperty && obj.setAttribute(styleName, parseInt(obj.style[styleName], 10))
        obj.style[styleName] = ''
      }
    }
    function getParentTdOrTh(ele) {
      if (ele.tagName == 'TD' || ele.tagName == 'TH') return ele
      var td
      if (
        (td =
          domUtils.findParentByTagName(ele, 'td', true) ||
          domUtils.findParentByTagName(ele, 'th', true))
      )
        return td
      return null
    }
    function isEmptyBlock(node) {
      var reg = new RegExp(domUtils.fillChar, 'g')
      if (
        node[browser.ie ? 'innerText' : 'textContent'].replace(/^\s*$/, '').replace(reg, '')
          .length > 0
      ) {
        return 0
      }
      for (var n in dtd.$isNotEmpty) {
        if (node.getElementsByTagName(n).length) {
          return 0
        }
      }
      return 1
    }
    function mouseCoords(evt) {
      if (evt.pageX || evt.pageY) {
        return { x: evt.pageX, y: evt.pageY }
      }
      return {
        x: evt.clientX + me.document.body.scrollLeft - me.document.body.clientLeft,
        y: evt.clientY + me.document.body.scrollTop - me.document.body.clientTop
      }
    }
    function mouseMoveEvent(evt) {
      if (isEditorDisabled()) {
        return
      }
      try {
        //普通状态下鼠标移动
        var target = getParentTdOrTh(evt.target || evt.srcElement),
          pos
        //区分用户的行为是拖动还是双击
        if (isInResizeBuffer) {
          me.body.style.webkitUserSelect = 'none'
          if (
            Math.abs(userActionStatus.x - evt.clientX) > offsetOfTableCell ||
            Math.abs(userActionStatus.y - evt.clientY) > offsetOfTableCell
          ) {
            clearTableDragTimer()
            isInResizeBuffer = false
            singleClickState = 0
            //drag action
            tableBorderDrag(evt)
          }
        }
        //修改单元格大小时的鼠标移动
        if (onDrag && dragTd) {
          singleClickState = 0
          me.body.style.webkitUserSelect = 'none'
          me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
          pos = mouseCoords(evt)
          toggleDraggableState(me, true, onDrag, pos, target)
          if (onDrag == 'h') {
            dragLine.style.left = getPermissionX(dragTd, evt) + 'px'
          } else if (onDrag == 'v') {
            dragLine.style.top = getPermissionY(dragTd, evt) + 'px'
          }
          return
        }
        //当鼠标处于table上时,修改移动过程中的光标状态
        if (target) {
          //针对使用table作为容器的组件不触发拖拽效果
          if (me.fireEvent('excludetable', target) === true) return
          pos = mouseCoords(evt)
          var state = getRelation(target, pos),
            table = domUtils.findParentByTagName(target, 'table', true)
          if (inTableSide(table, target, evt, true)) {
            if (me.fireEvent('excludetable', table) === true) return
            me.body.style.cursor = 'url(' + me.options.cursorpath + 'h.png),pointer'
          } else if (inTableSide(table, target, evt)) {
            if (me.fireEvent('excludetable', table) === true) return
            me.body.style.cursor = 'url(' + me.options.cursorpath + 'v.png),pointer'
          } else {
            me.body.style.cursor = 'text'
            var curCell = target
            if (/\d/.test(state)) {
              state = state.replace(/\d/, '')
              target = getUETable(target).getPreviewCell(target, state == 'v')
            }
            //位于第一行的顶部或者第一列的左边时不可拖动
            toggleDraggableState(me, target ? !!state : false, target ? state : '', pos, target)
          }
        } else {
          toggleDragButton(false, table, me)
        }
      } catch (e) {
        showError(e)
      }
    }
    var dragButtonTimer
    function toggleDragButton(show, table, editor) {
      if (!show) {
        if (dragOver) return
        dragButtonTimer = setTimeout(function () {
          !dragOver &&
            dragButton &&
            dragButton.parentNode &&
            dragButton.parentNode.removeChild(dragButton)
        }, 2000)
      } else {
        createDragButton(table, editor)
      }
    }
    function createDragButton(table, editor) {
      var pos = domUtils.getXY(table),
        doc = table.ownerDocument
      if (dragButton && dragButton.parentNode) return dragButton
      dragButton = doc.createElement('div')
      dragButton.contentEditable = false
      dragButton.innerHTML = ''
      dragButton.style.cssText =
        'width:15px;height:15px;background-image:url(' +
        editor.options.UEDITOR_HOME_URL +
        'dialogs/table/dragicon.png);position: absolute;cursor:move;top:' +
        (pos.y - 15) +
        'px;left:' +
        pos.x +
        'px;'
      domUtils.unSelectable(dragButton)
      dragButton.onmouseover = function (evt) {
        dragOver = true
      }
      dragButton.onmouseout = function (evt) {
        dragOver = false
      }
      domUtils.on(dragButton, 'click', function (type, evt) {
        doClick(evt, this)
      })
      domUtils.on(dragButton, 'dblclick', function (type, evt) {
        doDblClick(evt)
      })
      domUtils.on(dragButton, 'dragstart', function (type, evt) {
        domUtils.preventDefault(evt)
      })
      var timer
      function doClick(evt, button) {
        // 部分浏览器下需要清理
        clearTimeout(timer)
        timer = setTimeout(function () {
          editor.fireEvent('tableClicked', table, button)
        }, 300)
      }
      function doDblClick(evt) {
        clearTimeout(timer)
        var ut = getUETable(table),
          start = table.rows[0].cells[0],
          end = ut.getLastCell(),
          range = ut.getCellsRange(start, end)
        editor.selection.getRange().setStart(start, 0).setCursor(false, true)
        ut.setSelected(range)
      }
      doc.body.appendChild(dragButton)
    }
    //    function inPosition(table, pos) {
    //        var tablePos = domUtils.getXY(table),
    //            width = table.offsetWidth,
    //            height = table.offsetHeight;
    //        if (pos.x - tablePos.x < 5 && pos.y - tablePos.y < 5) {
    //            return "topLeft";
    //        } else if (tablePos.x + width - pos.x < 5 && tablePos.y + height - pos.y < 5) {
    //            return "bottomRight";
    //        }
    //    }
    function inTableSide(table, cell, evt, top) {
      var pos = mouseCoords(evt),
        state = getRelation(cell, pos)
      if (top) {
        var caption = table.getElementsByTagName('caption')[0],
          capHeight = caption ? caption.offsetHeight : 0
        return state == 'v1' && pos.y - domUtils.getXY(table).y - capHeight < 8
      } else {
        return state == 'h1' && pos.x - domUtils.getXY(table).x < 8
      }
    }
    /**
     * 获取拖动时允许的X轴坐标
     * @param dragTd
     * @param evt
     */
    function getPermissionX(dragTd, evt) {
      var ut = getUETable(dragTd)
      if (ut) {
        var preTd = ut.getSameEndPosCells(dragTd, 'x')[0],
          nextTd = ut.getSameStartPosXCells(dragTd)[0],
          mouseX = mouseCoords(evt).x,
          left = (preTd ? domUtils.getXY(preTd).x : domUtils.getXY(ut.table).x) + 20,
          right = nextTd
            ? domUtils.getXY(nextTd).x + nextTd.offsetWidth - 20
            : me.body.offsetWidth + 5 || parseInt(domUtils.getComputedStyle(me.body, 'width'), 10)
        left += cellMinWidth
        right -= cellMinWidth
        return mouseX < left ? left : mouseX > right ? right : mouseX
      }
    }
    /**
     * 获取拖动时允许的Y轴坐标
     */
    function getPermissionY(dragTd, evt) {
      try {
        var top = domUtils.getXY(dragTd).y,
          mousePosY = mouseCoords(evt).y
        return mousePosY < top ? top : mousePosY
      } catch (e) {
        showError(e)
      }
    }
    /**
     * 移动状态切换
     */
    function toggleDraggableState(editor, draggable, dir, mousePos, cell) {
      try {
        editor.body.style.cursor = dir == 'h' ? 'col-resize' : dir == 'v' ? 'row-resize' : 'text'
        if (browser.ie) {
          if (dir && !mousedown && !getUETableBySelected(editor)) {
            getDragLine(editor, editor.document)
            showDragLineAt(dir, cell)
          } else {
            hideDragLine(editor)
          }
        }
        onBorder = draggable
      } catch (e) {
        showError(e)
      }
    }
    /**
     * 获取与UETable相关的resize line
     * @param uetable UETable对象
     */
    function getResizeLineByUETable() {
      var lineId = '_UETableResizeLine',
        line = this.document.getElementById(lineId)
      if (!line) {
        line = this.document.createElement('div')
        line.id = lineId
        line.contnetEditable = false
        line.setAttribute('unselectable', 'on')
        var styles = {
          width: 2 * cellBorderWidth + 1 + 'px',
          position: 'absolute',
          'z-index': 100000,
          cursor: 'col-resize',
          background: 'red',
          display: 'none'
        }
        //切换状态
        line.onmouseout = function () {
          this.style.display = 'none'
        }
        utils.extend(line.style, styles)
        this.document.body.appendChild(line)
      }
      return line
    }
    /**
     * 更新resize-line
     */
    function updateResizeLine(cell, uetable) {
      var line = getResizeLineByUETable.call(this),
        table = uetable.table,
        styles = {
          top: domUtils.getXY(table).y + 'px',
          left: domUtils.getXY(cell).x + cell.offsetWidth - cellBorderWidth + 'px',
          display: 'block',
          height: table.offsetHeight + 'px'
        }
      utils.extend(line.style, styles)
    }
    /**
     * 显示resize-line
     */
    function showResizeLine(cell) {
      var uetable = getUETable(cell)
      updateResizeLine.call(this, cell, uetable)
    }
    /**
     * 获取鼠标与当前单元格的相对位置
     * @param ele
     * @param mousePos
     */
    function getRelation(ele, mousePos) {
      var elePos = domUtils.getXY(ele)
      if (!elePos) {
        return ''
      }
      if (elePos.x + ele.offsetWidth - mousePos.x < cellBorderWidth) {
        return 'h'
      }
      if (mousePos.x - elePos.x < cellBorderWidth) {
        return 'h1'
      }
      if (elePos.y + ele.offsetHeight - mousePos.y < cellBorderWidth) {
        return 'v'
      }
      if (mousePos.y - elePos.y < cellBorderWidth) {
        return 'v1'
      }
      return ''
    }
    function mouseDownEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      userActionStatus = {
        x: evt.clientX,
        y: evt.clientY
      }
      //右键菜单单独处理
      if (evt.button == 2) {
        var ut = getUETableBySelected(me),
          flag = false
        if (ut) {
          var td = getTargetTd(me, evt)
          utils.each(ut.selectedTds, function (ti) {
            if (ti === td) {
              flag = true
            }
          })
          if (!flag) {
            removeSelectedClass(domUtils.getElementsByTagName(me.body, 'th td'))
            ut.clearSelected()
          } else {
            td = ut.selectedTds[0]
            setTimeout(function () {
              me.selection.getRange().setStart(td, 0).setCursor(false, true)
            }, 0)
          }
        }
      } else {
        tableClickHander(evt)
      }
    }
    //清除表格的计时器
    function clearTableTimer() {
      tabTimer && clearTimeout(tabTimer)
      tabTimer = null
    }
    //双击收缩
    function tableDbclickHandler(evt) {
      singleClickState = 0
      evt = evt || me.window.event
      var target = getParentTdOrTh(evt.target || evt.srcElement)
      if (target) {
        var h
        if ((h = getRelation(target, mouseCoords(evt)))) {
          hideDragLine(me)
          if (h == 'h1') {
            h = 'h'
            if (inTableSide(domUtils.findParentByTagName(target, 'table'), target, evt)) {
              me.execCommand('adaptbywindow')
            } else {
              target = getUETable(target).getPreviewCell(target)
              if (target) {
                var rng = me.selection.getRange()
                rng.selectNodeContents(target).setCursor(true, true)
              }
            }
          }
          if (h == 'h') {
            var ut = getUETable(target),
              table = ut.table,
              cells = getCellsByMoveBorder(target, table, true)
            cells = extractArray(cells, 'left')
            ut.width = ut.offsetWidth
            var oldWidth = [],
              newWidth = []
            utils.each(cells, function (cell) {
              oldWidth.push(cell.offsetWidth)
            })
            utils.each(cells, function (cell) {
              cell.removeAttribute('width')
            })
            window.setTimeout(function () {
              //是否允许改变
              var changeable = true
              utils.each(cells, function (cell, index) {
                var width = cell.offsetWidth
                if (width > oldWidth[index]) {
                  changeable = false
                  return false
                }
                newWidth.push(width)
              })
              var change = changeable ? newWidth : oldWidth
              utils.each(cells, function (cell, index) {
                cell.width = change[index] - getTabcellSpace()
              })
            }, 0)
            //                    minWidth -= cellMinWidth;
            //
            //                    table.removeAttribute("width");
            //                    utils.each(cells, function (cell) {
            //                        cell.style.width = "";
            //                        cell.width -= minWidth;
            //                    });
          }
        }
      }
    }
    function tableClickHander(evt) {
      removeSelectedClass(domUtils.getElementsByTagName(me.body, 'td th'))
      //trace:3113
      //选中单元格,点击table外部,不会清掉table上挂的ueTable,会引起getUETableBySelected方法返回值
      utils.each(me.document.getElementsByTagName('table'), function (t) {
        t.ueTable = null
      })
      startTd = getTargetTd(me, evt)
      if (!startTd) return
      var table = domUtils.findParentByTagName(startTd, 'table', true)
      ut = getUETable(table)
      ut && ut.clearSelected()
      //判断当前鼠标状态
      if (!onBorder) {
        me.document.body.style.webkitUserSelect = ''
        mousedown = true
        me.addListener('mouseover', mouseOverEvent)
      } else {
        //边框上的动作处理
        borderActionHandler(evt)
      }
    }
    //处理表格边框上的动作, 这里做延时处理,避免两种动作互相影响
    function borderActionHandler(evt) {
      if (browser.ie) {
        evt = reconstruct(evt)
      }
      clearTableDragTimer()
      //是否正在等待resize的缓冲中
      isInResizeBuffer = true
      tableDragTimer = setTimeout(function () {
        tableBorderDrag(evt)
      }, dblclickTime)
    }
    function extractArray(originArr, key) {
      var result = [],
        tmp = null
      for (var i = 0, len = originArr.length; i < len; i++) {
        tmp = originArr[i][key]
        if (tmp) {
          result.push(tmp)
        }
      }
      return result
    }
    function clearTableDragTimer() {
      tableDragTimer && clearTimeout(tableDragTimer)
      tableDragTimer = null
    }
    function reconstruct(obj) {
      var attrs = ['pageX', 'pageY', 'clientX', 'clientY', 'srcElement', 'target'],
        newObj = {}
      if (obj) {
        for (var i = 0, key, val; (key = attrs[i]); i++) {
          val = obj[key]
          val && (newObj[key] = val)
        }
      }
      return newObj
    }
    //边框拖动
    function tableBorderDrag(evt) {
      isInResizeBuffer = false
      startTd = evt.target || evt.srcElement
      if (!startTd) return
      var state = getRelation(startTd, mouseCoords(evt))
      if (/\d/.test(state)) {
        state = state.replace(/\d/, '')
        startTd = getUETable(startTd).getPreviewCell(startTd, state == 'v')
      }
      hideDragLine(me)
      getDragLine(me, me.document)
      me.fireEvent('saveScene')
      showDragLineAt(state, startTd)
      mousedown = true
      //拖动开始
      onDrag = state
      dragTd = startTd
    }
    function mouseUpEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      clearTableDragTimer()
      isInResizeBuffer = false
      if (onBorder) {
        singleClickState = ++singleClickState % 3
        userActionStatus = {
          x: evt.clientX,
          y: evt.clientY
        }
        tableResizeTimer = setTimeout(function () {
          singleClickState > 0 && singleClickState--
        }, dblclickTime)
        if (singleClickState === 2) {
          singleClickState = 0
          tableDbclickHandler(evt)
          return
        }
      }
      if (evt.button == 2) return
      var me = this
      //清除表格上原生跨选问题
      var range = me.selection.getRange(),
        start = domUtils.findParentByTagName(range.startContainer, 'table', true),
        end = domUtils.findParentByTagName(range.endContainer, 'table', true)
      if (start || end) {
        if (start === end) {
          start = domUtils.findParentByTagName(range.startContainer, ['td', 'th', 'caption'], true)
          end = domUtils.findParentByTagName(range.endContainer, ['td', 'th', 'caption'], true)
          if (start !== end) {
            me.selection.clearRange()
          }
        } else {
          me.selection.clearRange()
        }
      }
      mousedown = false
      me.document.body.style.webkitUserSelect = ''
      //拖拽状态下的mouseUP
      if (onDrag && dragTd) {
        me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
        singleClickState = 0
        dragLine = me.document.getElementById('ue_tableDragLine')
        // trace 3973
        if (dragLine) {
          var dragTdPos = domUtils.getXY(dragTd),
            dragLinePos = domUtils.getXY(dragLine)
          switch (onDrag) {
            case 'h':
              changeColWidth(dragTd, dragLinePos.x - dragTdPos.x)
              break
            case 'v':
              changeRowHeight(dragTd, dragLinePos.y - dragTdPos.y - dragTd.offsetHeight)
              break
            default:
          }
          onDrag = ''
          dragTd = null
          hideDragLine(me)
          me.fireEvent('saveScene')
          return
        }
      }
      //正常状态下的mouseup
      if (!startTd) {
        var target = domUtils.findParentByTagName(evt.target || evt.srcElement, 'td', true)
        if (!target) target = domUtils.findParentByTagName(evt.target || evt.srcElement, 'th', true)
        if (target && (target.tagName == 'TD' || target.tagName == 'TH')) {
          if (me.fireEvent('excludetable', target) === true) return
          range = new dom.Range(me.document)
          range.setStart(target, 0).setCursor(false, true)
        }
      } else {
        var ut = getUETable(startTd),
          cell = ut ? ut.selectedTds[0] : null
        if (cell) {
          range = new dom.Range(me.document)
          if (domUtils.isEmptyBlock(cell)) {
            range.setStart(cell, 0).setCursor(false, true)
          } else {
            range.selectNodeContents(cell).shrinkBoundary().setCursor(false, true)
          }
        } else {
          range = me.selection.getRange().shrinkBoundary()
          if (!range.collapsed) {
            var start = domUtils.findParentByTagName(range.startContainer, ['td', 'th'], true),
              end = domUtils.findParentByTagName(range.endContainer, ['td', 'th'], true)
            //在table里边的不能清除
            if ((start && !end) || (!start && end) || (start && end && start !== end)) {
              range.setCursor(false, true)
            }
          }
        }
        startTd = null
        me.removeListener('mouseover', mouseOverEvent)
      }
      me._selectionChange(250, evt)
    }
    function mouseOverEvent(type, evt) {
      if (isEditorDisabled()) {
        return
      }
      var me = this,
        tar = evt.target || evt.srcElement
      currentTd =
        domUtils.findParentByTagName(tar, 'td', true) ||
        domUtils.findParentByTagName(tar, 'th', true)
      //需要判断两个TD是否位于同一个表格内
      if (
        startTd &&
        currentTd &&
        ((startTd.tagName == 'TD' && currentTd.tagName == 'TD') ||
          (startTd.tagName == 'TH' && currentTd.tagName == 'TH')) &&
        domUtils.findParentByTagName(startTd, 'table') ==
          domUtils.findParentByTagName(currentTd, 'table')
      ) {
        var ut = getUETable(currentTd)
        if (startTd != currentTd) {
          me.document.body.style.webkitUserSelect = 'none'
          me.selection.getNative()[browser.ie9below ? 'empty' : 'removeAllRanges']()
          var range = ut.getCellsRange(startTd, currentTd)
          ut.setSelected(range)
        } else {
          me.document.body.style.webkitUserSelect = ''
          ut.clearSelected()
        }
      }
      evt.preventDefault ? evt.preventDefault() : (evt.returnValue = false)
    }
    function setCellHeight(cell, height, backHeight) {
      var lineHight = parseInt(domUtils.getComputedStyle(cell, 'line-height'), 10),
        tmpHeight = backHeight + height
      height = tmpHeight < lineHight ? lineHight : tmpHeight
      if (cell.style.height) cell.style.height = ''
      cell.rowSpan == 1
        ? cell.setAttribute('height', height)
        : cell.removeAttribute && cell.removeAttribute('height')
    }
    function getWidth(cell) {
      if (!cell) return 0
      return parseInt(domUtils.getComputedStyle(cell, 'width'), 10)
    }
    function changeColWidth(cell, changeValue) {
      var ut = getUETable(cell)
      if (ut) {
        //根据当前移动的边框获取相关的单元格
        var table = ut.table,
          cells = getCellsByMoveBorder(cell, table)
        table.style.width = ''
        table.removeAttribute('width')
        //修正改变量
        changeValue = correctChangeValue(changeValue, cell, cells)
        if (cell.nextSibling) {
          var i = 0
          utils.each(cells, function (cellGroup) {
            cellGroup.left.width = +cellGroup.left.width + changeValue
            cellGroup.right && (cellGroup.right.width = +cellGroup.right.width - changeValue)
          })
        } else {
          utils.each(cells, function (cellGroup) {
            cellGroup.left.width -= -changeValue
          })
        }
      }
    }
    function isEditorDisabled() {
      return me.body.contentEditable === 'false'
    }
    function changeRowHeight(td, changeValue) {
      if (Math.abs(changeValue) < 10) return
      var ut = getUETable(td)
      if (ut) {
        var cells = ut.getSameEndPosCells(td, 'y'),
          //备份需要连带变化的td的原始高度,否则后期无法获取正确的值
          backHeight = cells[0] ? cells[0].offsetHeight : 0
        for (var i = 0, cell; (cell = cells[i++]); ) {
          setCellHeight(cell, changeValue, backHeight)
        }
      }
    }
    /**
     * 获取调整单元格大小的相关单元格
     * @isContainMergeCell 返回的结果中是否包含发生合并后的单元格
     */
    function getCellsByMoveBorder(cell, table, isContainMergeCell) {
      if (!table) {
        table = domUtils.findParentByTagName(cell, 'table')
      }
      if (!table) {
        return null
      }
      //获取到该单元格所在行的序列号
      var index = domUtils.getNodeIndex(cell),
        temp = cell,
        rows = table.rows,
        colIndex = 0
      while (temp) {
        //获取到当前单元格在未发生单元格合并时的序列
        if (temp.nodeType === 1) {
          colIndex += temp.colSpan || 1
        }
        temp = temp.previousSibling
      }
      temp = null
      //记录想关的单元格
      var borderCells = []
      utils.each(rows, function (tabRow) {
        var cells = tabRow.cells,
          currIndex = 0
        utils.each(cells, function (tabCell) {
          currIndex += tabCell.colSpan || 1
          if (currIndex === colIndex) {
            borderCells.push({
              left: tabCell,
              right: tabCell.nextSibling || null
            })
            return false
          } else if (currIndex > colIndex) {
            if (isContainMergeCell) {
              borderCells.push({
                left: tabCell
              })
            }
            return false
          }
        })
      })
      return borderCells
    }
    /**
     * 通过给定的单元格集合获取最小的单元格width
     */
    function getMinWidthByTableCells(cells) {
      var minWidth = Number.MAX_VALUE
      for (var i = 0, curCell; (curCell = cells[i]); i++) {
        minWidth = Math.min(minWidth, curCell.width || getTableCellWidth(curCell))
      }
      return minWidth
    }
    function correctChangeValue(changeValue, relatedCell, cells) {
      //为单元格的paading预留空间
      changeValue -= getTabcellSpace()
      if (changeValue < 0) {
        return 0
      }
      changeValue -= getTableCellWidth(relatedCell)
      //确定方向
      var direction = changeValue < 0 ? 'left' : 'right'
      changeValue = Math.abs(changeValue)
      //只关心非最后一个单元格就可以
      utils.each(cells, function (cellGroup) {
        var curCell = cellGroup[direction]
        //为单元格保留最小空间
        if (curCell) {
          changeValue = Math.min(changeValue, getTableCellWidth(curCell) - cellMinWidth)
        }
      })
      //修正越界
      changeValue = changeValue < 0 ? 0 : changeValue
      return direction === 'left' ? -changeValue : changeValue
    }
    function getTableCellWidth(cell) {
      var width = 0,
        //偏移纠正量
        offset = 0,
        width = cell.offsetWidth - getTabcellSpace()
      //最后一个节点纠正一下
      if (!cell.nextSibling) {
        width -= getTableCellOffset(cell)
      }
      width = width < 0 ? 0 : width
      try {
        cell.width = width
      } catch (e) {}
      return width
    }
    /**
     * 获取单元格所在表格的最末单元格的偏移量
     */
    function getTableCellOffset(cell) {
      tab = domUtils.findParentByTagName(cell, 'table', false)
      if (tab.offsetVal === undefined) {
        var prev = cell.previousSibling
        if (prev) {
          //最后一个单元格和前一个单元格的width diff结果 如果恰好为一个border width, 则条件成立
          tab.offsetVal =
            cell.offsetWidth - prev.offsetWidth === UT.borderWidth ? UT.borderWidth : 0
        } else {
          tab.offsetVal = 0
        }
      }
      return tab.offsetVal
    }
    function getTabcellSpace() {
      if (UT.tabcellSpace === undefined) {
        var cell = null,
          tab = me.document.createElement('table'),
          tbody = me.document.createElement('tbody'),
          trow = me.document.createElement('tr'),
          tabcell = me.document.createElement('td'),
          mirror = null
        tabcell.style.cssText = 'border: 0;'
        tabcell.width = 1
        trow.appendChild(tabcell)
        trow.appendChild((mirror = tabcell.cloneNode(false)))
        tbody.appendChild(trow)
        tab.appendChild(tbody)
        tab.style.cssText = 'visibility: hidden;'
        me.body.appendChild(tab)
        UT.paddingSpace = tabcell.offsetWidth - 1
        var tmpTabWidth = tab.offsetWidth
        tabcell.style.cssText = ''
        mirror.style.cssText = ''
        UT.borderWidth = (tab.offsetWidth - tmpTabWidth) / 3
        UT.tabcellSpace = UT.paddingSpace + UT.borderWidth
        me.body.removeChild(tab)
      }
      getTabcellSpace = function () {
        return UT.tabcellSpace
      }
      return UT.tabcellSpace
    }
    function getDragLine(editor, doc) {
      if (mousedown) return
      dragLine = editor.document.createElement('div')
      domUtils.setAttributes(dragLine, {
        id: 'ue_tableDragLine',
        unselectable: 'on',
        contenteditable: false,
        onresizestart: 'return false',
        ondragstart: 'return false',
        onselectstart: 'return false',
        style:
          'background-color:blue;position:absolute;padding:0;margin:0;background-image:none;border:0px none;opacity:0;filter:alpha(opacity=0)'
      })
      editor.body.appendChild(dragLine)
    }
    function hideDragLine(editor) {
      if (mousedown) return
      var line
      while ((line = editor.document.getElementById('ue_tableDragLine'))) {
        domUtils.remove(line)
      }
    }
    /**
     * 依据state(v|h)在cell位置显示横线
     * @param state
     * @param cell
     */
    function showDragLineAt(state, cell) {
      if (!cell) return
      var table = domUtils.findParentByTagName(cell, 'table'),
        caption = table.getElementsByTagName('caption'),
        width = table.offsetWidth,
        height = table.offsetHeight - (caption.length > 0 ? caption[0].offsetHeight : 0),
        tablePos = domUtils.getXY(table),
        cellPos = domUtils.getXY(cell),
        css
      switch (state) {
        case 'h':
          css =
            'height:' +
            height +
            'px;top:' +
            (tablePos.y + (caption.length > 0 ? caption[0].offsetHeight : 0)) +
            'px;left:' +
            (cellPos.x + cell.offsetWidth)
          dragLine.style.cssText =
            css +
            'px;position: absolute;display:block;background-color:blue;width:1px;border:0; color:blue;opacity:.3;filter:alpha(opacity=30)'
          break
        case 'v':
          css =
            'width:' + width + 'px;left:' + tablePos.x + 'px;top:' + (cellPos.y + cell.offsetHeight)
          //必须加上border:0和color:blue,否则低版ie不支持背景色显示
          dragLine.style.cssText =
            css +
            'px;overflow:hidden;position: absolute;display:block;background-color:blue;height:1px;border:0;color:blue;opacity:.2;filter:alpha(opacity=20)'
          break
        default:
      }
    }
    /**
     * 当表格边框颜色为白色时设置为虚线,true为添加虚线
     * @param editor
     * @param flag
     */
    function switchBorderColor(editor, flag) {
      var tableArr = domUtils.getElementsByTagName(editor.body, 'table'),
        color
      for (var i = 0, node; (node = tableArr[i++]); ) {
        var td = domUtils.getElementsByTagName(node, 'td')
        if (td[0]) {
          if (flag) {
            color = td[0].style.borderColor.replace(/\s/g, '')
            if (/(#ffffff)|(rgb\(255,255,255\))/gi.test(color))
              domUtils.addClass(node, 'noBorderTable')
          } else {
            domUtils.removeClasses(node, 'noBorderTable')
          }
        }
      }
    }
    function getTableWidth(editor, needIEHack, defaultValue) {
      var body = editor.body
      return (
        body.offsetWidth -
        (needIEHack ? parseInt(domUtils.getComputedStyle(body, 'margin-left'), 10) * 2 : 0) -
        defaultValue.tableBorder * 2 -
        (editor.options.offsetWidth || 0)
      )
    }
    /**
     * 获取当前拖动的单元格
     */
    function getTargetTd(editor, evt) {
      var target = domUtils.findParentByTagName(evt.target || evt.srcElement, ['td', 'th'], true),
        dir = null
      if (!target) {
        return null
      }
      dir = getRelation(target, mouseCoords(evt))
      //如果有前一个节点, 需要做一个修正, 否则可能会得到一个错误的td
      if (!target) {
        return null
      }
      if (dir === 'h1' && target.previousSibling) {
        var position = domUtils.getXY(target),
          cellWidth = target.offsetWidth
        if (Math.abs(position.x + cellWidth - evt.clientX) > cellWidth / 3) {
          target = target.previousSibling
        }
      } else if (dir === 'v1' && target.parentNode.previousSibling) {
        var position = domUtils.getXY(target),
          cellHeight = target.offsetHeight
        if (Math.abs(position.y + cellHeight - evt.clientY) > cellHeight / 3) {
          target = target.parentNode.previousSibling.firstChild
        }
      }
      //排除了非td内部以及用于代码高亮部分的td
      return target && !(editor.fireEvent('excludetable', target) === true) ? target : null
    }
  }
  // plugins/table.sort.js
  /**
   * Created with JetBrains PhpStorm.
   * User: Jinqn
   * Date: 13-10-12
   * Time: 上午10:20
   * To change this template use File | Settings | File Templates.
   */
  UE.UETable.prototype.sortTable = function (sortByCellIndex, compareFn) {
    var table = this.table,
      rows = table.rows,
      trArray = [],
      flag = rows[0].cells[0].tagName === 'TH',
      lastRowIndex = 0
    if (this.selectedTds.length) {
      var range = this.cellsRange,
        len = range.endRowIndex + 1
      for (var i = range.beginRowIndex; i < len; i++) {
        trArray[i] = rows[i]
      }
      trArray.splice(0, range.beginRowIndex)
      lastRowIndex = range.endRowIndex + 1 === this.rowsNum ? 0 : range.endRowIndex + 1
    } else {
      for (var i = 0, len = rows.length; i < len; i++) {
        trArray[i] = rows[i]
      }
    }
    var Fn = {
      reversecurrent: function (td1, td2) {
        return 1
      },
      orderbyasc: function (td1, td2) {
        var value1 = td1.innerText || td1.textContent,
          value2 = td2.innerText || td2.textContent
        return value1.localeCompare(value2)
      },
      reversebyasc: function (td1, td2) {
        var value1 = td1.innerHTML,
          value2 = td2.innerHTML
        return value2.localeCompare(value1)
      },
      orderbynum: function (td1, td2) {
        var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
          value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        if (value1) value1 = +value1[0]
        if (value2) value2 = +value2[0]
        return (value1 || 0) - (value2 || 0)
      },
      reversebynum: function (td1, td2) {
        var value1 = td1[browser.ie ? 'innerText' : 'textContent'].match(/\d+/),
          value2 = td2[browser.ie ? 'innerText' : 'textContent'].match(/\d+/)
        if (value1) value1 = +value1[0]
        if (value2) value2 = +value2[0]
        return (value2 || 0) - (value1 || 0)
      }
    }
    //对表格设置排序的标记data-sort-type
    table.setAttribute(
      'data-sort-type',
      compareFn && typeof compareFn === 'string' && Fn[compareFn] ? compareFn : ''
    )
    //th不参与排序
    flag && trArray.splice(0, 1)
    trArray = utils.sort(trArray, function (tr1, tr2) {
      var result
      if (compareFn && typeof compareFn === 'function') {
        result = compareFn.call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
      } else if (compareFn && typeof compareFn === 'number') {
        result = 1
      } else if (compareFn && typeof compareFn === 'string' && Fn[compareFn]) {
        result = Fn[compareFn].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
      } else {
        result = Fn['orderbyasc'].call(this, tr1.cells[sortByCellIndex], tr2.cells[sortByCellIndex])
      }
      return result
    })
    var fragment = table.ownerDocument.createDocumentFragment()
    for (var j = 0, len = trArray.length; j < len; j++) {
      fragment.appendChild(trArray[j])
    }
    var tbody = table.getElementsByTagName('tbody')[0]
    if (!lastRowIndex) {
      tbody.appendChild(fragment)
    } else {
      tbody.insertBefore(fragment, rows[lastRowIndex - range.endRowIndex + range.beginRowIndex - 1])
    }
  }
  UE.plugins['tablesort'] = function () {
    var me = this,
      UT = UE.UETable,
      getUETable = function (tdOrTable) {
        return UT.getUETable(tdOrTable)
      },
      getTableItemsByRange = function (editor) {
        return UT.getTableItemsByRange(editor)
      }
    me.ready(function () {
      //添加表格可排序的样式
      utils.cssRule(
        'tablesort',
        'table.sortEnabled tr.firstRow th,table.sortEnabled tr.firstRow td{padding-right:20px;background-repeat: no-repeat;background-position: center right;' +
          '   background-image:url(' +
          me.options.themePath +
          me.options.theme +
          '/images/sortable.png);}',
        me.document
      )
      //做单元格合并操作时,清除可排序标识
      me.addListener('afterexeccommand', function (type, cmd) {
        if (cmd == 'mergeright' || cmd == 'mergedown' || cmd == 'mergecells') {
          this.execCommand('disablesort')
        }
      })
    })
    //表格排序
    UE.commands['sorttable'] = {
      queryCommandState: function () {
        var me = this,
          tableItems = getTableItemsByRange(me)
        if (!tableItems.cell) return -1
        var table = tableItems.table,
          cells = table.getElementsByTagName('td')
        for (var i = 0, cell; (cell = cells[i++]); ) {
          if (cell.rowSpan != 1 || cell.colSpan != 1) return -1
        }
        return 0
      },
      execCommand: function (cmd, fn) {
        var me = this,
          range = me.selection.getRange(),
          bk = range.createBookmark(true),
          tableItems = getTableItemsByRange(me),
          cell = tableItems.cell,
          ut = getUETable(tableItems.table),
          cellInfo = ut.getCellInfo(cell)
        ut.sortTable(cellInfo.cellIndex, fn)
        range.moveToBookmark(bk)
        try {
          range.select()
        } catch (e) {}
      }
    }
    //设置表格可排序,清除表格可排序
    UE.commands['enablesort'] = UE.commands['disablesort'] = {
      queryCommandState: function (cmd) {
        var table = getTableItemsByRange(this).table
        if (table && cmd == 'enablesort') {
          var cells = domUtils.getElementsByTagName(table, 'th td')
          for (var i = 0; i < cells.length; i++) {
            if (cells[i].getAttribute('colspan') > 1 || cells[i].getAttribute('rowspan') > 1)
              return -1
          }
        }
        return !table
          ? -1
          : (cmd == 'enablesort') ^ (table.getAttribute('data-sort') != 'sortEnabled')
          ? -1
          : 0
      },
      execCommand: function (cmd) {
        var table = getTableItemsByRange(this).table
        table.setAttribute('data-sort', cmd == 'enablesort' ? 'sortEnabled' : 'sortDisabled')
        cmd == 'enablesort'
          ? domUtils.addClass(table, 'sortEnabled')
          : domUtils.removeClasses(table, 'sortEnabled')
      }
    }
  }
  // plugins/contextmenu.js
  ///import core
  ///commands 右键菜单
  ///commandsName  ContextMenu
  ///commandsTitle  右键菜单
  /**
   * 右键菜单
   * @function
   * @name baidu.editor.plugins.contextmenu
   * @author zhanyi
   */
  UE.plugins['contextmenu'] = function () {
    var me = this
    me.setOpt('enableContextMenu', true)
    if (me.getOpt('enableContextMenu') === false) {
      return
    }
    var lang = me.getLang('contextMenu'),
      menu,
      items = me.options.contextMenu || [
        { label: lang['selectall'], cmdName: 'selectall' },
        {
          label: lang.cleardoc,
          cmdName: 'cleardoc',
          exec: function () {
            if (confirm(lang.confirmclear)) {
              this.execCommand('cleardoc')
            }
          }
        },
        '-',
        {
          label: lang.unlink,
          cmdName: 'unlink'
        },
        '-',
        {
          group: lang.paragraph,
          icon: 'justifyjustify',
          subMenu: [
            {
              label: lang.justifyleft,
              cmdName: 'justify',
              value: 'left'
            },
            {
              label: lang.justifyright,
              cmdName: 'justify',
              value: 'right'
            },
            {
              label: lang.justifycenter,
              cmdName: 'justify',
              value: 'center'
            },
            {
              label: lang.justifyjustify,
              cmdName: 'justify',
              value: 'justify'
            }
          ]
        },
        '-',
        {
          group: lang.table,
          icon: 'table',
          subMenu: [
            {
              label: lang.inserttable,
              cmdName: 'inserttable'
            },
            {
              label: lang.deletetable,
              cmdName: 'deletetable'
            },
            '-',
            {
              label: lang.deleterow,
              cmdName: 'deleterow'
            },
            {
              label: lang.deletecol,
              cmdName: 'deletecol'
            },
            {
              label: lang.insertcol,
              cmdName: 'insertcol'
            },
            {
              label: lang.insertcolnext,
              cmdName: 'insertcolnext'
            },
            {
              label: lang.insertrow,
              cmdName: 'insertrow'
            },
            {
              label: lang.insertrownext,
              cmdName: 'insertrownext'
            },
            '-',
            {
              label: lang.insertcaption,
              cmdName: 'insertcaption'
            },
            {
              label: lang.deletecaption,
              cmdName: 'deletecaption'
            },
            {
              label: lang.inserttitle,
              cmdName: 'inserttitle'
            },
            {
              label: lang.deletetitle,
              cmdName: 'deletetitle'
            },
            {
              label: lang.inserttitlecol,
              cmdName: 'inserttitlecol'
            },
            {
              label: lang.deletetitlecol,
              cmdName: 'deletetitlecol'
            },
            '-',
            {
              label: lang.mergecells,
              cmdName: 'mergecells'
            },
            {
              label: lang.mergeright,
              cmdName: 'mergeright'
            },
            {
              label: lang.mergedown,
              cmdName: 'mergedown'
            },
            '-',
            {
              label: lang.splittorows,
              cmdName: 'splittorows'
            },
            {
              label: lang.splittocols,
              cmdName: 'splittocols'
            },
            {
              label: lang.splittocells,
              cmdName: 'splittocells'
            },
            '-',
            {
              label: lang.averageDiseRow,
              cmdName: 'averagedistributerow'
            },
            {
              label: lang.averageDisCol,
              cmdName: 'averagedistributecol'
            },
            '-',
            {
              label: lang.edittd,
              cmdName: 'edittd',
              exec: function () {
                if (UE.ui['edittd']) {
                  new UE.ui['edittd'](this)
                }
                this.getDialog('edittd').open()
              }
            },
            {
              label: lang.edittable,
              cmdName: 'edittable',
              exec: function () {
                if (UE.ui['edittable']) {
                  new UE.ui['edittable'](this)
                }
                this.getDialog('edittable').open()
              }
            },
            {
              label: lang.setbordervisible,
              cmdName: 'setbordervisible'
            }
          ]
        },
        {
          group: lang.tablesort,
          icon: 'tablesort',
          subMenu: [
            {
              label: lang.enablesort,
              cmdName: 'enablesort'
            },
            {
              label: lang.disablesort,
              cmdName: 'disablesort'
            },
            '-',
            {
              label: lang.reversecurrent,
              cmdName: 'sorttable',
              value: 'reversecurrent'
            },
            {
              label: lang.orderbyasc,
              cmdName: 'sorttable',
              value: 'orderbyasc'
            },
            {
              label: lang.reversebyasc,
              cmdName: 'sorttable',
              value: 'reversebyasc'
            },
            {
              label: lang.orderbynum,
              cmdName: 'sorttable',
              value: 'orderbynum'
            },
            {
              label: lang.reversebynum,
              cmdName: 'sorttable',
              value: 'reversebynum'
            }
          ]
        },
        {
          group: lang.borderbk,
          icon: 'borderBack',
          subMenu: [
            {
              label: lang.setcolor,
              cmdName: 'interlacetable',
              exec: function () {
                this.execCommand('interlacetable')
              }
            },
            {
              label: lang.unsetcolor,
              cmdName: 'uninterlacetable',
              exec: function () {
                this.execCommand('uninterlacetable')
              }
            },
            {
              label: lang.setbackground,
              cmdName: 'settablebackground',
              exec: function () {
                this.execCommand('settablebackground', {
                  repeat: true,
                  colorList: ['#bbb', '#ccc']
                })
              }
            },
            {
              label: lang.unsetbackground,
              cmdName: 'cleartablebackground',
              exec: function () {
                this.execCommand('cleartablebackground')
              }
            },
            {
              label: lang.redandblue,
              cmdName: 'settablebackground',
              exec: function () {
                this.execCommand('settablebackground', {
                  repeat: true,
                  colorList: ['red', 'blue']
                })
              }
            },
            {
              label: lang.threecolorgradient,
              cmdName: 'settablebackground',
              exec: function () {
                this.execCommand('settablebackground', {
                  repeat: true,
                  colorList: ['#aaa', '#bbb', '#ccc']
                })
              }
            }
          ]
        },
        {
          group: lang.aligntd,
          icon: 'aligntd',
          subMenu: [
            {
              cmdName: 'cellalignment',
              value: { align: 'left', vAlign: 'top' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'center', vAlign: 'top' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'right', vAlign: 'top' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'left', vAlign: 'middle' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'center', vAlign: 'middle' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'right', vAlign: 'middle' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'left', vAlign: 'bottom' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'center', vAlign: 'bottom' }
            },
            {
              cmdName: 'cellalignment',
              value: { align: 'right', vAlign: 'bottom' }
            }
          ]
        },
        {
          group: lang.aligntable,
          icon: 'aligntable',
          subMenu: [
            {
              cmdName: 'tablealignment',
              className: 'left',
              label: lang.tableleft,
              value: 'left'
            },
            {
              cmdName: 'tablealignment',
              className: 'center',
              label: lang.tablecenter,
              value: 'center'
            },
            {
              cmdName: 'tablealignment',
              className: 'right',
              label: lang.tableright,
              value: 'right'
            }
          ]
        },
        '-',
        {
          label: lang.insertparagraphbefore,
          cmdName: 'insertparagraph',
          value: true
        },
        {
          label: lang.insertparagraphafter,
          cmdName: 'insertparagraph'
        },
        {
          label: lang['copy'],
          cmdName: 'copy'
        },
        {
          label: lang['paste'],
          cmdName: 'paste'
        }
      ]
    if (!items.length) {
      return
    }
    var uiUtils = UE.ui.uiUtils
    me.addListener('contextmenu', function (type, evt) {
      var offset = uiUtils.getViewportOffsetByEvent(evt)
      me.fireEvent('beforeselectionchange')
      if (menu) {
        menu.destroy()
      }
      for (var i = 0, ti, contextItems = []; (ti = items[i]); i++) {
        var last
        ;(function (item) {
          if (item == '-') {
            if ((last = contextItems[contextItems.length - 1]) && last !== '-') {
              contextItems.push('-')
            }
          } else if (item.hasOwnProperty('group')) {
            for (var j = 0, cj, subMenu = []; (cj = item.subMenu[j]); j++) {
              ;(function (subItem) {
                if (subItem == '-') {
                  if ((last = subMenu[subMenu.length - 1]) && last !== '-') {
                    subMenu.push('-')
                  } else {
                    subMenu.splice(subMenu.length - 1)
                  }
                } else {
                  if (
                    (me.commands[subItem.cmdName] ||
                      UE.commands[subItem.cmdName] ||
                      subItem.query) &&
                    (subItem.query ? subItem.query() : me.queryCommandState(subItem.cmdName)) > -1
                  ) {
                    subMenu.push({
                      label:
                        subItem.label ||
                        me.getLang('contextMenu.' + subItem.cmdName + (subItem.value || '')) ||
                        '',
                      className:
                        'edui-for-' +
                        subItem.cmdName +
                        (subItem.className
                          ? ' edui-for-' + subItem.cmdName + '-' + subItem.className
                          : ''),
                      onclick: subItem.exec
                        ? function () {
                            subItem.exec.call(me)
                          }
                        : function () {
                            me.execCommand(subItem.cmdName, subItem.value)
                          }
                    })
                  }
                }
              })(cj)
            }
            if (subMenu.length) {
              function getLabel() {
                switch (item.icon) {
                  case 'table':
                    return me.getLang('contextMenu.table')
                  case 'justifyjustify':
                    return me.getLang('contextMenu.paragraph')
                  case 'aligntd':
                    return me.getLang('contextMenu.aligntd')
                  case 'aligntable':
                    return me.getLang('contextMenu.aligntable')
                  case 'tablesort':
                    return lang.tablesort
                  case 'borderBack':
                    return lang.borderbk
                  default:
                    return ''
                }
              }
              contextItems.push({
                //todo 修正成自动获取方式
                label: getLabel(),
                className: 'edui-for-' + item.icon,
                subMenu: {
                  items: subMenu,
                  editor: me
                }
              })
            }
          } else {
            //有可能commmand没有加载右键不能出来,或者没有command也想能展示出来添加query方法
            if (
              (me.commands[item.cmdName] || UE.commands[item.cmdName] || item.query) &&
              (item.query ? item.query.call(me) : me.queryCommandState(item.cmdName)) > -1
            ) {
              contextItems.push({
                label: item.label || me.getLang('contextMenu.' + item.cmdName),
                className:
                  'edui-for-' + (item.icon ? item.icon : item.cmdName + (item.value || '')),
                onclick: item.exec
                  ? function () {
                      item.exec.call(me)
                    }
                  : function () {
                      me.execCommand(item.cmdName, item.value)
                    }
              })
            }
          }
        })(ti)
      }
      if (contextItems[contextItems.length - 1] == '-') {
        contextItems.pop()
      }
      menu = new UE.ui.Menu({
        items: contextItems,
        className: 'edui-contextmenu',
        editor: me
      })
      menu.render()
      menu.showAt(offset)
      me.fireEvent('aftershowcontextmenu', menu)
      domUtils.preventDefault(evt)
      if (browser.ie) {
        var ieRange
        try {
          ieRange = me.selection.getNative().createRange()
        } catch (e) {
          return
        }
        if (ieRange.item) {
          var range = new dom.Range(me.document)
          range.selectNode(ieRange.item(0)).select(true, true)
        }
      }
    })
    // 添加复制的flash按钮
    me.addListener('aftershowcontextmenu', function (type, menu) {
      if (me.zeroclipboard) {
        var items = menu.items
        for (var key in items) {
          if (items[key].className == 'edui-for-copy') {
            me.zeroclipboard.clip(items[key].getDom())
          }
        }
      }
    })
  }
  // plugins/shortcutmenu.js
  ///import core
  ///commands       弹出菜单
  // commandsName  popupmenu
  ///commandsTitle  弹出菜单
  /**
   * 弹出菜单
   * @function
   * @name baidu.editor.plugins.popupmenu
   * @author xuheng
   */
  UE.plugins['shortcutmenu'] = function () {
    var me = this,
      menu,
      items = me.options.shortcutMenu || []
    if (!items.length) {
      return
    }
    me.addListener('contextmenu mouseup', function (type, e) {
      var me = this,
        customEvt = {
          type: type,
          target: e.target || e.srcElement,
          screenX: e.screenX,
          screenY: e.screenY,
          clientX: e.clientX,
          clientY: e.clientY
        }
      setTimeout(function () {
        var rng = me.selection.getRange()
        if (rng.collapsed === false || type == 'contextmenu') {
          if (!menu) {
            menu = new baidu.editor.ui.ShortCutMenu({
              editor: me,
              items: items,
              theme: me.options.theme,
              className: 'edui-shortcutmenu'
            })
            menu.render()
            me.fireEvent('afterrendershortcutmenu', menu)
          }
          menu.show(customEvt, !!UE.plugins['contextmenu'])
        }
      })
      if (type == 'contextmenu') {
        domUtils.preventDefault(e)
        if (browser.ie9below) {
          var ieRange
          try {
            ieRange = me.selection.getNative().createRange()
          } catch (e) {
            return
          }
          if (ieRange.item) {
            var range = new dom.Range(me.document)
            range.selectNode(ieRange.item(0)).select(true, true)
          }
        }
      }
    })
    me.addListener('keydown', function (type) {
      if (type == 'keydown') {
        menu && !menu.isHidden && menu.hide()
      }
    })
  }
  // plugins/basestyle.js
  /**
   * B、I、sub、super命令支持
   * @file
   * @since 1.2.6.1
   */
  UE.plugins['basestyle'] = function () {
    /**
     * 字体加粗
     * @command bold
     * @param { String } cmd 命令字符串
     * @remind 对已加粗的文本内容执行该命令, 将取消加粗
     * @method execCommand
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行加粗操作
     * //第一次执行, 文本内容加粗
     * editor.execCommand( 'bold' );
     *
     * //第二次执行, 文本内容取消加粗
     * editor.execCommand( 'bold' );
     * ```
     */
    /**
     * 字体倾斜
     * @command italic
     * @method execCommand
     * @param { String } cmd 命令字符串
     * @remind 对已倾斜的文本内容执行该命令, 将取消倾斜
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行斜体操作
     * //第一次操作, 文本内容将变成斜体
     * editor.execCommand( 'italic' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'italic' );
     * ```
     */
    /**
     * 下标文本,与“superscript”命令互斥
     * @command subscript
     * @method execCommand
     * @remind  把选中的文本内容切换成下标文本, 如果当前选中的文本已经是下标, 则该操作会把文本内容还原成正常文本
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行下标操作
     * //第一次操作, 文本内容将变成下标文本
     * editor.execCommand( 'subscript' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'subscript' );
     * ```
     */
    /**
     * 上标文本,与“subscript”命令互斥
     * @command superscript
     * @method execCommand
     * @remind 把选中的文本内容切换成上标文本, 如果当前选中的文本已经是上标, 则该操作会把文本内容还原成正常文本
     * @param { String } cmd 命令字符串
     * @example
     * ```javascript
     * //editor是编辑器实例
     * //对当前选中的文本内容执行上标操作
     * //第一次操作, 文本内容将变成上标文本
     * editor.execCommand( 'superscript' );
     *
     * //再次对同一文本内容执行, 则文本内容将恢复正常
     * editor.execCommand( 'superscript' );
     * ```
     */
    var basestyles = {
        bold: ['strong', 'b'],
        italic: ['em', 'i'],
        subscript: ['sub'],
        superscript: ['sup']
      },
      getObj = function (editor, tagNames) {
        return domUtils.filterNodeList(editor.selection.getStartElementPath(), tagNames)
      },
      me = this
    //添加快捷键
    me.addshortcutkey({
      Bold: 'ctrl+66', //^B
      Italic: 'ctrl+73', //^I
      Underline: 'ctrl+85' //^U
    })
    me.addInputRule(function (root) {
      utils.each(root.getNodesByTagName('b i'), function (node) {
        switch (node.tagName) {
          case 'b':
            node.tagName = 'strong'
            break
          case 'i':
            node.tagName = 'em'
        }
      })
    })
    for (var style in basestyles) {
      ;(function (cmd, tagNames) {
        me.commands[cmd] = {
          execCommand: function (cmdName) {
            var range = me.selection.getRange(),
              obj = getObj(this, tagNames)
            if (range.collapsed) {
              if (obj) {
                var tmpText = me.document.createTextNode('')
                range.insertNode(tmpText).removeInlineStyle(tagNames)
                range.setStartBefore(tmpText)
                domUtils.remove(tmpText)
              } else {
                var tmpNode = range.document.createElement(tagNames[0])
                if (cmdName == 'superscript' || cmdName == 'subscript') {
                  tmpText = me.document.createTextNode('')
                  range
                    .insertNode(tmpText)
                    .removeInlineStyle(['sub', 'sup'])
                    .setStartBefore(tmpText)
                    .collapse(true)
                }
                range.insertNode(tmpNode).setStart(tmpNode, 0)
              }
              range.collapse(true)
            } else {
              if (cmdName == 'superscript' || cmdName == 'subscript') {
                if (!obj || obj.tagName.toLowerCase() != cmdName) {
                  range.removeInlineStyle(['sub', 'sup'])
                }
              }
              obj ? range.removeInlineStyle(tagNames) : range.applyInlineStyle(tagNames[0])
            }
            range.select()
          },
          queryCommandState: function () {
            return getObj(this, tagNames) ? 1 : 0
          }
        }
      })(style, basestyles[style])
    }
  }
  // plugins/elementpath.js
  /**
   * 选取路径命令
   * @file
   */
  UE.plugins['elementpath'] = function () {
    var currentLevel,
      tagNames,
      me = this
    me.setOpt('elementPathEnabled', true)
    if (!me.options.elementPathEnabled) {
      return
    }
    me.commands['elementpath'] = {
      execCommand: function (cmdName, level) {
        var start = tagNames[level],
          range = me.selection.getRange()
        currentLevel = level * 1
        range.selectNode(start).select()
      },
      queryCommandValue: function () {
        //产生一个副本,不能修改原来的startElementPath;
        var parents = [].concat(this.selection.getStartElementPath()).reverse(),
          names = []
        tagNames = parents
        for (var i = 0, ci; (ci = parents[i]); i++) {
          if (ci.nodeType == 3) {
            continue
          }
          var name = ci.tagName.toLowerCase()
          if (name == 'img' && ci.getAttribute('anchorname')) {
            name = 'anchor'
          }
          names[i] = name
          if (currentLevel == i) {
            currentLevel = -1
            break
          }
        }
        return names
      }
    }
  }
  // plugins/formatmatch.js
  /**
   * 格式刷,只格式inline的
   * @file
   * @since 1.2.6.1
   */
  /**
   * 格式刷
   * @command formatmatch
   * @method execCommand
   * @remind 该操作不能复制段落格式
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * //editor是编辑器实例
   * //获取格式刷
   * editor.execCommand( 'formatmatch' );
   * ```
   */
  UE.plugins['formatmatch'] = function () {
    var me = this,
      list = [],
      img,
      flag = 0
    me.addListener('reset', function () {
      list = []
      flag = 0
    })
    function addList(type, evt) {
      if (browser.webkit) {
        var target = evt.target.tagName == 'IMG' ? evt.target : null
      }
      function addFormat(range) {
        if (text) {
          range.selectNode(text)
        }
        return range.applyInlineStyle(list[list.length - 1].tagName, null, list)
      }
      me.undoManger && me.undoManger.save()
      var range = me.selection.getRange(),
        imgT = target || range.getClosedNode()
      if (img && imgT && imgT.tagName == 'IMG') {
        //trace:964
        imgT.style.cssText +=
          ';float:' +
          (img.style.cssFloat || img.style.styleFloat || 'none') +
          ';display:' +
          (img.style.display || 'inline')
        img = null
      } else {
        if (!img) {
          var collapsed = range.collapsed
          if (collapsed) {
            var text = me.document.createTextNode('match')
            range.insertNode(text).select()
          }
          me.__hasEnterExecCommand = true
          //不能把block上的属性干掉
          //trace:1553
          var removeFormatAttributes = me.options.removeFormatAttributes
          me.options.removeFormatAttributes = ''
          me.execCommand('removeformat')
          me.options.removeFormatAttributes = removeFormatAttributes
          me.__hasEnterExecCommand = false
          //trace:969
          range = me.selection.getRange()
          if (list.length) {
            addFormat(range)
          }
          if (text) {
            range.setStartBefore(text).collapse(true)
          }
          range.select()
          text && domUtils.remove(text)
        }
      }
      me.undoManger && me.undoManger.save()
      me.removeListener('mouseup', addList)
      flag = 0
    }
    me.commands['formatmatch'] = {
      execCommand: function (cmdName) {
        if (flag) {
          flag = 0
          list = []
          me.removeListener('mouseup', addList)
          return
        }
        var range = me.selection.getRange()
        img = range.getClosedNode()
        if (!img || img.tagName != 'IMG') {
          range.collapse(true).shrinkBoundary()
          var start = range.startContainer
          list = domUtils.findParents(start, true, function (node) {
            return !domUtils.isBlockElm(node) && node.nodeType == 1
          })
          //a不能加入格式刷, 并且克隆节点
          for (var i = 0, ci; (ci = list[i]); i++) {
            if (ci.tagName == 'A') {
              list.splice(i, 1)
              break
            }
          }
        }
        me.addListener('mouseup', addList)
        flag = 1
      },
      queryCommandState: function () {
        return flag
      },
      notNeedUndo: 1
    }
  }
  // plugins/searchreplace.js
  ///import core
  ///commands 查找替换
  ///commandsName  SearchReplace
  ///commandsTitle  查询替换
  ///commandsDialog  dialogs\searchreplace
  /**
   * @description 查找替换
   * @author zhanyi
   */
  UE.plugin.register('searchreplace', function () {
    var me = this
    var _blockElm = { table: 1, tbody: 1, tr: 1, ol: 1, ul: 1 }
    function findTextInString(textContent, opt, currentIndex) {
      var str = opt.searchStr
      if (opt.dir == -1) {
        textContent = textContent.split('').reverse().join('')
        str = str.split('').reverse().join('')
        currentIndex = textContent.length - currentIndex
      }
      var reg = new RegExp(str, 'g' + (opt.casesensitive ? '' : 'i')),
        match
      while ((match = reg.exec(textContent))) {
        if (match.index >= currentIndex) {
          return opt.dir == -1
            ? textContent.length - match.index - opt.searchStr.length
            : match.index
        }
      }
      return -1
    }
    function findTextBlockElm(node, currentIndex, opt) {
      var textContent,
        index,
        methodName = opt.all || opt.dir == 1 ? 'getNextDomNode' : 'getPreDomNode'
      if (domUtils.isBody(node)) {
        node = node.firstChild
      }
      var first = 1
      while (node) {
        textContent =
          node.nodeType == 3 ? node.nodeValue : node[browser.ie ? 'innerText' : 'textContent']
        index = findTextInString(textContent, opt, currentIndex)
        first = 0
        if (index != -1) {
          return {
            node: node,
            index: index
          }
        }
        node = domUtils[methodName](node)
        while (node && _blockElm[node.nodeName.toLowerCase()]) {
          node = domUtils[methodName](node, true)
        }
        if (node) {
          currentIndex =
            opt.dir == -1
              ? (node.nodeType == 3
                  ? node.nodeValue
                  : node[browser.ie ? 'innerText' : 'textContent']
                ).length
              : 0
        }
      }
    }
    function findNTextInBlockElm(node, index, str) {
      var currentIndex = 0,
        currentNode = node.firstChild,
        currentNodeLength = 0,
        result
      while (currentNode) {
        if (currentNode.nodeType == 3) {
          currentNodeLength = currentNode.nodeValue.replace(/(^[\t\r\n]+)|([\t\r\n]+$)/, '').length
          currentIndex += currentNodeLength
          if (currentIndex >= index) {
            return {
              node: currentNode,
              index: currentNodeLength - (currentIndex - index)
            }
          }
        } else if (!dtd.$empty[currentNode.tagName]) {
          currentNodeLength = currentNode[browser.ie ? 'innerText' : 'textContent'].replace(
            /(^[\t\r\n]+)|([\t\r\n]+$)/,
            ''
          ).length
          currentIndex += currentNodeLength
          if (currentIndex >= index) {
            result = findNTextInBlockElm(
              currentNode,
              currentNodeLength - (currentIndex - index),
              str
            )
            if (result) {
              return result
            }
          }
        }
        currentNode = domUtils.getNextDomNode(currentNode)
      }
    }
    function searchReplace(me, opt) {
      var rng = me.selection.getRange(),
        startBlockNode,
        searchStr = opt.searchStr,
        span = me.document.createElement('span')
      span.innerHTML = '$$ueditor_searchreplace_key$$'
      rng.shrinkBoundary(true)
      //判断是不是第一次选中
      if (!rng.collapsed) {
        rng.select()
        var rngText = me.selection.getText()
        if (new RegExp('^' + opt.searchStr + '$', opt.casesensitive ? '' : 'i').test(rngText)) {
          if (opt.replaceStr != undefined) {
            replaceText(rng, opt.replaceStr)
            rng.select()
            return true
          } else {
            rng.collapse(opt.dir == -1)
          }
        }
      }
      rng.insertNode(span)
      rng.enlargeToBlockElm(true)
      startBlockNode = rng.startContainer
      var currentIndex = startBlockNode[browser.ie ? 'innerText' : 'textContent'].indexOf(
        '$$ueditor_searchreplace_key$$'
      )
      rng.setStartBefore(span)
      domUtils.remove(span)
      var result = findTextBlockElm(startBlockNode, currentIndex, opt)
      if (result) {
        var rngStart = findNTextInBlockElm(result.node, result.index, searchStr)
        var rngEnd = findNTextInBlockElm(result.node, result.index + searchStr.length, searchStr)
        rng.setStart(rngStart.node, rngStart.index).setEnd(rngEnd.node, rngEnd.index)
        if (opt.replaceStr !== undefined) {
          replaceText(rng, opt.replaceStr)
        }
        rng.select()
        return true
      } else {
        rng.setCursor()
      }
    }
    function replaceText(rng, str) {
      str = me.document.createTextNode(str)
      rng.deleteContents().insertNode(str)
    }
    return {
      commands: {
        searchreplace: {
          execCommand: function (cmdName, opt) {
            utils.extend(
              opt,
              {
                all: false,
                casesensitive: false,
                dir: 1
              },
              true
            )
            var num = 0
            if (opt.all) {
              var rng = me.selection.getRange(),
                first = me.body.firstChild
              if (first && first.nodeType == 1) {
                rng.setStart(first, 0)
                rng.shrinkBoundary(true)
              } else if (first.nodeType == 3) {
                rng.setStartBefore(first)
              }
              rng.collapse(true).select(true)
              if (opt.replaceStr !== undefined) {
                me.fireEvent('saveScene')
              }
              while (searchReplace(this, opt)) {
                num++
              }
              if (num) {
                me.fireEvent('saveScene')
              }
            } else {
              if (opt.replaceStr !== undefined) {
                me.fireEvent('saveScene')
              }
              if (searchReplace(this, opt)) {
                num++
              }
              if (num) {
                me.fireEvent('saveScene')
              }
            }
            return num
          },
          notNeedUndo: 1
        }
      }
    }
  })
  // plugins/customstyle.js
  /**
   * 自定义样式
   * @file
   * @since 1.2.6.1
   */
  /**
   * 根据config配置文件里“customstyle”选项的值对匹配的标签执行样式替换。
   * @command customstyle
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * editor.execCommand( 'customstyle' );
   * ```
   */
  UE.plugins['customstyle'] = function () {
    var me = this
    me.setOpt({
      customstyle: [
        {
          tag: 'h1',
          name: 'tc',
          style:
            'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:center;margin:0 0 20px 0;'
        },
        {
          tag: 'h1',
          name: 'tl',
          style:
            'font-size:32px;font-weight:bold;border-bottom:#ccc 2px solid;padding:0 4px 0 0;text-align:left;margin:0 0 10px 0;'
        },
        {
          tag: 'span',
          name: 'im',
          style: 'font-size:16px;font-style:italic;font-weight:bold;line-height:18px;'
        },
        {
          tag: 'span',
          name: 'hi',
          style:
            'font-size:16px;font-style:italic;font-weight:bold;color:rgb(51, 153, 204);line-height:18px;'
        }
      ]
    })
    me.commands['customstyle'] = {
      execCommand: function (cmdName, obj) {
        var me = this,
          tagName = obj.tag,
          node = domUtils.findParent(
            me.selection.getStart(),
            function (node) {
              return node.getAttribute('label')
            },
            true
          ),
          range,
          bk,
          tmpObj = {}
        for (var p in obj) {
          if (obj[p] !== undefined) tmpObj[p] = obj[p]
        }
        delete tmpObj.tag
        if (node && node.getAttribute('label') == obj.label) {
          range = this.selection.getRange()
          bk = range.createBookmark()
          if (range.collapsed) {
            //trace:1732 删掉自定义标签,要有p来回填站位
            if (dtd.$block[node.tagName]) {
              var fillNode = me.document.createElement('p')
              domUtils.moveChild(node, fillNode)
              node.parentNode.insertBefore(fillNode, node)
              domUtils.remove(node)
            } else {
              domUtils.remove(node, true)
            }
          } else {
            var common = domUtils.getCommonAncestor(bk.start, bk.end),
              nodes = domUtils.getElementsByTagName(common, tagName)
            if (new RegExp(tagName, 'i').test(common.tagName)) {
              nodes.push(common)
            }
            for (var i = 0, ni; (ni = nodes[i++]); ) {
              if (ni.getAttribute('label') == obj.label) {
                var ps = domUtils.getPosition(ni, bk.start),
                  pe = domUtils.getPosition(ni, bk.end)
                if (
                  (ps & domUtils.POSITION_FOLLOWING || ps & domUtils.POSITION_CONTAINS) &&
                  (pe & domUtils.POSITION_PRECEDING || pe & domUtils.POSITION_CONTAINS)
                )
                  if (dtd.$block[tagName]) {
                    var fillNode = me.document.createElement('p')
                    domUtils.moveChild(ni, fillNode)
                    ni.parentNode.insertBefore(fillNode, ni)
                  }
                domUtils.remove(ni, true)
              }
            }
            node = domUtils.findParent(
              common,
              function (node) {
                return node.getAttribute('label') == obj.label
              },
              true
            )
            if (node) {
              domUtils.remove(node, true)
            }
          }
          range.moveToBookmark(bk).select()
        } else {
          if (dtd.$block[tagName]) {
            this.execCommand('paragraph', tagName, tmpObj, 'customstyle')
            range = me.selection.getRange()
            if (!range.collapsed) {
              range.collapse()
              node = domUtils.findParent(
                me.selection.getStart(),
                function (node) {
                  return node.getAttribute('label') == obj.label
                },
                true
              )
              var pNode = me.document.createElement('p')
              domUtils.insertAfter(node, pNode)
              domUtils.fillNode(me.document, pNode)
              range.setStart(pNode, 0).setCursor()
            }
          } else {
            range = me.selection.getRange()
            if (range.collapsed) {
              node = me.document.createElement(tagName)
              domUtils.setAttributes(node, tmpObj)
              range.insertNode(node).setStart(node, 0).setCursor()
              return
            }
            bk = range.createBookmark()
            range.applyInlineStyle(tagName, tmpObj).moveToBookmark(bk).select()
          }
        }
      },
      queryCommandValue: function () {
        var parent = domUtils.filterNodeList(this.selection.getStartElementPath(), function (node) {
          return node.getAttribute('label')
        })
        return parent ? parent.getAttribute('label') : ''
      }
    }
    //当去掉customstyle是,如果是块元素,用p代替
    me.addListener('keyup', function (type, evt) {
      var keyCode = evt.keyCode || evt.which
      if (keyCode == 32 || keyCode == 13) {
        var range = me.selection.getRange()
        if (range.collapsed) {
          var node = domUtils.findParent(
            me.selection.getStart(),
            function (node) {
              return node.getAttribute('label')
            },
            true
          )
          if (node && dtd.$block[node.tagName] && domUtils.isEmptyNode(node)) {
            var p = me.document.createElement('p')
            domUtils.insertAfter(node, p)
            domUtils.fillNode(me.document, p)
            domUtils.remove(node)
            range.setStart(p, 0).setCursor()
          }
        }
      }
    })
  }
  // plugins/catchremoteimage.js
  ///import core
  ///commands 远程图片抓取
  ///commandsName  catchRemoteImage,catchremoteimageenable
  ///commandsTitle  远程图片抓取
  /**
   * 远程图片抓取,当开启本插件时所有不符合本地域名的图片都将被抓取成为本地服务器上的图片
   */
  UE.plugins['catchremoteimage'] = function () {
    var me = this,
      ajax = UE.ajax
    /* 设置默认值 */
    if (me.options.catchRemoteImageEnable === false) return
    me.setOpt({
      catchRemoteImageEnable: false
    })
    me.addListener('afterpaste', function () {
      me.fireEvent('catchRemoteImage')
    })
    me.addListener('catchRemoteImage', function () {
      var catcherLocalDomain = me.getOpt('catcherLocalDomain'),
        catcherActionUrl = me.getActionUrl(me.getOpt('catcherActionName')),
        catcherUrlPrefix = me.getOpt('catcherUrlPrefix'),
        catcherFieldName = me.getOpt('catcherFieldName')
      var remoteImages = [],
        imgs = domUtils.getElementsByTagName(me.document, 'img'),
        test = function (src, urls) {
          if (src.indexOf(location.host) != -1 || /(^\.)|(^\/)/.test(src)) {
            return true
          }
          if (urls) {
            for (var j = 0, url; (url = urls[j++]); ) {
              if (src.indexOf(url) !== -1) {
                return true
              }
            }
          }
          return false
        }
      for (var i = 0, ci; (ci = imgs[i++]); ) {
        if (ci.getAttribute('word_img')) {
          continue
        }
        var src = ci.getAttribute('_src') || ci.src || ''
        if (/^(https?|ftp):/i.test(src) && !test(src, catcherLocalDomain)) {
          remoteImages.push(src)
        }
      }
      if (remoteImages.length) {
        catchremoteimage(remoteImages, {
          //成功抓取
          success: function (r) {
            try {
              var info = r.state !== undefined ? r : eval('(' + r.responseText + ')')
            } catch (e) {
              return
            }
            /* 获取源路径和新路径 */
            var i,
              j,
              ci,
              cj,
              oldSrc,
              newSrc,
              list = info.list
            for (i = 0; (ci = imgs[i++]); ) {
              oldSrc = ci.getAttribute('_src') || ci.src || ''
              for (j = 0; (cj = list[j++]); ) {
                if (oldSrc == cj.source && cj.state == 'SUCCESS') {
                  //抓取失败时不做替换处理
                  newSrc = catcherUrlPrefix + cj.url
                  domUtils.setAttributes(ci, {
                    src: newSrc,
                    _src: newSrc
                  })
                  break
                }
              }
            }
            me.fireEvent('catchremotesuccess')
          },
          //回调失败,本次请求超时
          error: function () {
            me.fireEvent('catchremoteerror')
          }
        })
      }
      function catchremoteimage(imgs, callbacks) {
        var params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
          url = utils.formatUrl(
            catcherActionUrl + (catcherActionUrl.indexOf('?') == -1 ? '?' : '&') + params
          ),
          isJsonp = utils.isCrossDomainUrl(url),
          opt = {
            method: 'POST',
            dataType: isJsonp ? 'jsonp' : '',
            timeout: 60000, //单位:毫秒,回调请求超时设置。目标用户如果网速不是很快的话此处建议设置一个较大的数值
            onsuccess: callbacks['success'],
            onerror: callbacks['error']
          }
        opt[catcherFieldName] = imgs
        ajax.request(url, opt)
      }
    })
  }
  // plugins/snapscreen.js
  /**
   * 截屏插件,为UEditor提供插入支持
   * @file
   * @since 1.4.2
   */
  UE.plugin.register('snapscreen', function () {
    var me = this
    var snapplugin
    function getLocation(url) {
      var search,
        a = document.createElement('a'),
        params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
      a.href = url
      if (browser.ie) {
        a.href = a.href
      }
      search = a.search
      if (params) {
        search = search + (search.indexOf('?') == -1 ? '?' : '&') + params
        search = search.replace(/[&]+/gi, '&')
      }
      return {
        port: a.port,
        hostname: a.hostname,
        path: a.pathname + search || +a.hash
      }
    }
    return {
      commands: {
        /**
         * 字体背景颜色
         * @command snapscreen
         * @method execCommand
         * @param { String } cmd 命令字符串
         * @example
         * ```javascript
         * editor.execCommand('snapscreen');
         * ```
         */
        snapscreen: {
          execCommand: function (cmd) {
            var url, local, res
            var lang = me.getLang('snapScreen_plugin')
            if (!snapplugin) {
              var container = me.container
              var doc = me.container.ownerDocument || me.container.document
              snapplugin = doc.createElement('object')
              try {
                snapplugin.type = 'application/x-pluginbaidusnap'
              } catch (e) {
                return
              }
              snapplugin.style.cssText = 'position:absolute;left:-9999px;width:0;height:0;'
              snapplugin.setAttribute('width', '0')
              snapplugin.setAttribute('height', '0')
              container.appendChild(snapplugin)
            }
            function onSuccess(rs) {
              try {
                rs = eval('(' + rs + ')')
                if (rs.state == 'SUCCESS') {
                  var opt = me.options
                  me.execCommand('insertimage', {
                    src: opt.snapscreenUrlPrefix + rs.url,
                    _src: opt.snapscreenUrlPrefix + rs.url,
                    alt: rs.title || '',
                    floatStyle: opt.snapscreenImgAlign
                  })
                } else {
                  alert(rs.state)
                }
              } catch (e) {
                alert(lang.callBackErrorMsg)
              }
            }
            url = me.getActionUrl(me.getOpt('snapscreenActionName'))
            local = getLocation(url)
            setTimeout(function () {
              try {
                res = snapplugin.saveSnapshot(local.hostname, local.path, local.port)
              } catch (e) {
                me.ui._dialogs['snapscreenDialog'].open()
                return
              }
              onSuccess(res)
            }, 50)
          },
          queryCommandState: function () {
            return navigator.userAgent.indexOf('Windows', 0) != -1 ? 0 : -1
          }
        }
      }
    }
  })
  // plugins/insertparagraph.js
  /**
   * 插入段落
   * @file
   * @since 1.2.6.1
   */
  /**
   * 插入段落
   * @command insertparagraph
   * @method execCommand
   * @param { String } cmd 命令字符串
   * @example
   * ```javascript
   * //editor是编辑器实例
   * editor.execCommand( 'insertparagraph' );
   * ```
   */
  UE.commands['insertparagraph'] = {
    execCommand: function (cmdName, front) {
      var me = this,
        range = me.selection.getRange(),
        start = range.startContainer,
        tmpNode
      while (start) {
        if (domUtils.isBody(start)) {
          break
        }
        tmpNode = start
        start = start.parentNode
      }
      if (tmpNode) {
        var p = me.document.createElement('p')
        if (front) {
          tmpNode.parentNode.insertBefore(p, tmpNode)
        } else {
          tmpNode.parentNode.insertBefore(p, tmpNode.nextSibling)
        }
        domUtils.fillNode(me.document, p)
        range.setStart(p, 0).setCursor(false, true)
      }
    }
  }
  // plugins/webapp.js
  /**
   * 百度应用
   * @file
   * @since 1.2.6.1
   */
  /**
   * 插入百度应用
   * @command webapp
   * @method execCommand
   * @remind 需要百度APPKey
   * @remind 百度应用主页: http://app.baidu.com/ 
   * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
   * height=>应用容器高度,logo=>应用logo,url=>应用地址
   * @example
   * ```javascript
   * //editor是编辑器实例
   * //在编辑器里插入一个“植物大战僵尸”的APP
   * editor.execCommand( 'webapp' , {
   *     title: '植物大战僵尸',
   *     width: 560,
   *     height: 465,
   *     logo: '应用展示的图片',
   *     url: '百度应用的地址'
   * } );
   * ```
   */
  //UE.plugins['webapp'] = function () {
  //    var me = this;
  //    function createInsertStr( obj, toIframe, addParagraph ) {
  //        return !toIframe ?
  //                (addParagraph ? '' : '') + '
' : '')
  //                :
  //                '';
  //    }
  //
  //    function switchImgAndIframe( img2frame ) {
  //        var tmpdiv,
  //                nodes = domUtils.getElementsByTagName( me.document, !img2frame ? "iframe" : "img" );
  //        for ( var i = 0, node; node = nodes[i++]; ) {
  //            if ( node.className != "edui-faked-webapp" ){
  //                continue;
  //            }
  //            tmpdiv = me.document.createElement( "div" );
  //            tmpdiv.innerHTML = createInsertStr( img2frame ? {url:node.getAttribute( "_url" ), width:node.width, height:node.height,title:node.title,logo:node.style.backgroundImage.replace("url(","").replace(")","")} : {url:node.getAttribute( "src", 2 ),title:node.title, width:node.width, height:node.height,logo:node.getAttribute("logo_url")}, img2frame ? true : false,false );
  //            node.parentNode.replaceChild( tmpdiv.firstChild, node );
  //        }
  //    }
  //
  //    me.addListener( "beforegetcontent", function () {
  //        switchImgAndIframe( true );
  //    } );
  //    me.addListener( 'aftersetcontent', function () {
  //        switchImgAndIframe( false );
  //    } );
  //    me.addListener( 'aftergetcontent', function ( cmdName ) {
  //        if ( cmdName == 'aftergetcontent' && me.queryCommandState( 'source' ) ){
  //            return;
  //        }
  //        switchImgAndIframe( false );
  //    } );
  //
  //    me.commands['webapp'] = {
  //        execCommand:function ( cmd, obj ) {
  //            me.execCommand( "inserthtml", createInsertStr( obj, false,true ) );
  //        }
  //    };
  //};
  UE.plugin.register('webapp', function () {
    var me = this
    function createInsertStr(obj, toEmbed) {
      return !toEmbed
        ? 'http://app.baidu.com/ 
         * @param { Object } appOptions 应用所需的参数项, 支持的key有: title=>应用标题, width=>应用容器宽度,
         * height=>应用容器高度,logo=>应用logo,url=>应用地址
         * @example
         * ```javascript
         * //editor是编辑器实例
         * //在编辑器里插入一个“植物大战僵尸”的APP
         * editor.execCommand( 'webapp' , {
         *     title: '植物大战僵尸',
         *     width: 560,
         *     height: 465,
         *     logo: '应用展示的图片',
         *     url: '百度应用的地址'
         * } );
         * ```
         */
        webapp: {
          execCommand: function (cmd, obj) {
            var me = this,
              str = createInsertStr(
                utils.extend(obj, {
                  align: 'none'
                }),
                false
              )
            me.execCommand('inserthtml', str)
          },
          queryCommandState: function () {
            var me = this,
              img = me.selection.getRange().getClosedNode(),
              flag = img && img.className == 'edui-faked-webapp'
            return flag ? 1 : 0
          }
        }
      }
    }
  })
  // plugins/template.js
  ///import core
  ///import plugins\inserthtml.js
  ///import plugins\cleardoc.js
  ///commands 模板
  ///commandsName  template
  ///commandsTitle  模板
  ///commandsDialog  dialogs\template
  UE.plugins['template'] = function () {
    UE.commands['template'] = {
      execCommand: function (cmd, obj) {
        obj.html && this.execCommand('inserthtml', obj.html)
      }
    }
    this.addListener('click', function (type, evt) {
      var el = evt.target || evt.srcElement,
        range = this.selection.getRange()
      var tnode = domUtils.findParent(
        el,
        function (node) {
          if (node.className && domUtils.hasClass(node, 'ue_t')) {
            return node
          }
        },
        true
      )
      tnode && range.selectNode(tnode).shrinkBoundary().select()
    })
    this.addListener('keydown', function (type, evt) {
      var range = this.selection.getRange()
      if (!range.collapsed) {
        if (!evt.ctrlKey && !evt.metaKey && !evt.shiftKey && !evt.altKey) {
          var tnode = domUtils.findParent(
            range.startContainer,
            function (node) {
              if (node.className && domUtils.hasClass(node, 'ue_t')) {
                return node
              }
            },
            true
          )
          if (tnode) {
            domUtils.removeClasses(tnode, ['ue_t'])
          }
        }
      }
    })
  }
  // plugins/music.js
  /**
   * 插入音乐命令
   * @file
   */
  UE.plugin.register('music', function () {
    var me = this
    function creatInsertStr(url, width, height, align, cssfloat, toEmbed) {
      return !toEmbed
        ? ''
    }
    return {
      outputRule: function (root) {
        utils.each(root.getNodesByTagName('img'), function (node) {
          var html
          if (node.getAttr('class') == 'edui-faked-music') {
            var cssfloat = node.getStyle('float')
            var align = node.getAttr('align')
            html = creatInsertStr(
              node.getAttr('_url'),
              node.getAttr('width'),
              node.getAttr('height'),
              align,
              cssfloat,
              true
            )
            var embed = UE.uNode.createElement(html)
            node.parentNode.replaceChild(embed, node)
          }
        })
      },
      inputRule: function (root) {
        utils.each(root.getNodesByTagName('embed'), function (node) {
          if (node.getAttr('class') == 'edui-faked-music') {
            var cssfloat = node.getStyle('float')
            var align = node.getAttr('align')
            html = creatInsertStr(
              node.getAttr('src'),
              node.getAttr('width'),
              node.getAttr('height'),
              align,
              cssfloat,
              false
            )
            var img = UE.uNode.createElement(html)
            node.parentNode.replaceChild(img, node)
          }
        })
      },
      commands: {
        /**
         * 插入音乐
         * @command music
         * @method execCommand
         * @param { Object } musicOptions 插入音乐的参数项, 支持的key有: url=>音乐地址;
         * width=>音乐容器宽度;height=>音乐容器高度;align=>音乐文件的对齐方式, 可选值有: left, center, right, none
         * @example
         * ```javascript
         * //editor是编辑器实例
         * //在编辑器里插入一个“植物大战僵尸”的APP
         * editor.execCommand( 'music' , {
         *     width: 400,
         *     height: 95,
         *     align: "center",
         *     url: "音乐地址"
         * } );
         * ```
         */
        music: {
          execCommand: function (cmd, musicObj) {
            var me = this,
              str = creatInsertStr(
                musicObj.url,
                musicObj.width || 400,
                musicObj.height || 95,
                'none',
                false
              )
            me.execCommand('inserthtml', str)
          },
          queryCommandState: function () {
            var me = this,
              img = me.selection.getRange().getClosedNode(),
              flag = img && img.className == 'edui-faked-music'
            return flag ? 1 : 0
          }
        }
      }
    }
  })
  // plugins/autoupload.js
  /**
   * @description
   * 1.拖放文件到编辑区域,自动上传并插入到选区
   * 2.插入粘贴板的图片,自动上传并插入到选区
   * @author Jinqn
   * @date 2013-10-14
   */
  UE.plugin.register('autoupload', function () {
    function sendAndInsertFile(file, editor) {
      var me = editor
      //模拟数据
      var fieldName,
        urlPrefix,
        maxSize,
        allowFiles,
        actionUrl,
        loadingHtml,
        errorHandler,
        successHandler,
        filetype = /image\/\w+/i.test(file.type) ? 'image' : 'file',
        loadingId = 'loading_' + (+new Date()).toString(36)
      fieldName = me.getOpt(filetype + 'FieldName')
      urlPrefix = me.getOpt(filetype + 'UrlPrefix')
      maxSize = me.getOpt(filetype + 'MaxSize')
      allowFiles = me.getOpt(filetype + 'AllowFiles')
      actionUrl = me.getActionUrl(me.getOpt(filetype + 'ActionName'))
      errorHandler = function (title) {
        var loader = me.document.getElementById(loadingId)
        loader && domUtils.remove(loader)
        me.fireEvent('showmessage', {
          id: loadingId,
          content: title,
          type: 'error',
          timeout: 4000
        })
      }
      if (filetype == 'image') {
        loadingHtml =
          '' +
          '
'
        successHandler = function (data) {
          var link = urlPrefix + data.url,
            loader = me.document.getElementById(loadingId)
          var rng = me.selection.getRange(),
            bk = rng.createBookmark()
          rng.selectNode(loader).select()
          me.execCommand('insertfile', { url: link })
          rng.moveToBookmark(bk).select()
        }
      }
      /* 插入loading的占位符 */
      me.execCommand('inserthtml', loadingHtml)
      /* 判断后端配置是否没有加载成功 */
      if (!me.getOpt(filetype + 'ActionName')) {
        errorHandler(me.getLang('autoupload.errorLoadConfig'))
        return
      }
      /* 判断文件大小是否超出限制 */
      if (file.size > maxSize) {
        errorHandler(me.getLang('autoupload.exceedSizeError'))
        return
      }
      /* 判断文件格式是否超出允许 */
      var fileext = file.name ? file.name.substr(file.name.lastIndexOf('.')) : ''
      if (
        (fileext && filetype != 'image') ||
        (allowFiles && (allowFiles.join('') + '.').indexOf(fileext.toLowerCase() + '.') == -1)
      ) {
        errorHandler(me.getLang('autoupload.exceedTypeError'))
        return
      }
      /* 创建Ajax并提交 */
      var xhr = new XMLHttpRequest(),
        fd = new FormData(),
        params = utils.serializeParam(me.queryCommandValue('serverparam')) || '',
        url = utils.formatUrl(actionUrl + (actionUrl.indexOf('?') == -1 ? '?' : '&') + params)
      fd.append(fieldName, file, file.name || 'blob.' + file.type.substr('image/'.length))
      fd.append('type', 'ajax')
      xhr.open('post', url, true)
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
      xhr.addEventListener('load', function (e) {
        try {
          var json = new Function('return ' + utils.trim(e.target.response))()
          if (json.state == 'SUCCESS' && json.url) {
            successHandler(json)
          } else {
            errorHandler(json.state)
          }
        } catch (er) {
          errorHandler(me.getLang('autoupload.loadError'))
        }
      })
      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
    }
    return {
      outputRule: function (root) {
        utils.each(root.getNodesByTagName('img'), function (n) {
          if (/\b(loaderrorclass)|(bloaderrorclass)\b/.test(n.getAttr('class'))) {
            n.parentNode.removeChild(n)
          }
        })
        utils.each(root.getNodesByTagName('p'), function (n) {
          if (/\bloadpara\b/.test(n.getAttr('class'))) {
            n.parentNode.removeChild(n)
          }
        })
      },
      bindEvents: {
        //插入粘贴板的图片,拖放插入图片
        ready: function (e) {
          var me = this
          if (window.FormData && window.FileReader) {
            domUtils.on(me.body, 'paste drop', function (e) {
              var hasImg = false,
                items
              //获取粘贴板文件列表或者拖放文件列表
              items = e.type == 'paste' ? getPasteImage(e) : getDropImage(e)
              if (items) {
                var len = items.length,
                  file
                while (len--) {
                  file = items[len]
                  if (file.getAsFile) file = file.getAsFile()
                  if (file && file.size > 0) {
                    sendAndInsertFile(file, me)
                    hasImg = true
                  }
                }
                hasImg && e.preventDefault()
              }
            })
            //取消拖放图片时出现的文字光标位置提示
            domUtils.on(me.body, 'dragover', function (e) {
              if (e.dataTransfer.types[0] == 'Files') {
                e.preventDefault()
              }
            })
            //设置loading的样式
            utils.cssRule(
              'loading',
              ".loadingclass{display:inline-block;cursor:default;background: url('" +
                this.options.themePath +
                this.options.theme +
                "/images/loading.gif') no-repeat center center transparent;border:1px solid #cccccc;margin-left:1px;height: 22px;width: 22px;}\n" +
                ".loaderrorclass{display:inline-block;cursor:default;background: url('" +
                this.options.themePath +
                this.options.theme +
                "/images/loaderror.png') no-repeat center center transparent;border:1px solid #cccccc;margin-right:1px;height: 22px;width: 22px;" +
                '}',
              this.document
            )
          }
        }
      }
    }
  })
  // plugins/autosave.js
  UE.plugin.register('autosave', function () {
    var me = this,
      //无限循环保护
      lastSaveTime = new Date(),
      //最小保存间隔时间
      MIN_TIME = 20,
      //auto save key
      saveKey = null
    function save(editor) {
      var saveData
      if (new Date() - lastSaveTime < MIN_TIME) {
        return
      }
      if (!editor.hasContents()) {
        //这里不能调用命令来删除, 会造成事件死循环
        saveKey && me.removePreferences(saveKey)
        return
      }
      lastSaveTime = new Date()
      editor._saveFlag = null
      saveData = me.body.innerHTML
      if (
        editor.fireEvent('beforeautosave', {
          content: saveData
        }) === false
      ) {
        return
      }
      me.setPreferences(saveKey, saveData)
      editor.fireEvent('afterautosave', {
        content: saveData
      })
    }
    return {
      defaultOptions: {
        //默认间隔时间
        saveInterval: 500,
        enableAutoSave: true // HaoChuan9421
      },
      bindEvents: {
        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
        },
        contentchange: function () {
          // HaoChuan9421
          if (!me.getOpt('enableAutoSave')) {
            return
          }
          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)
          }
        }
      },
      commands: {
        clearlocaldata: {
          execCommand: function (cmd, name) {
            if (saveKey && me.getPreferences(saveKey)) {
              me.removePreferences(saveKey)
            }
          },
          notNeedUndo: true,
          ignoreContentChange: true
        },
        getlocaldata: {
          execCommand: function (cmd, name) {
            return saveKey ? me.getPreferences(saveKey) || '' : ''
          },
          notNeedUndo: true,
          ignoreContentChange: true
        },
        drafts: {
          execCommand: function (cmd, name) {
            if (saveKey) {
              me.body.innerHTML = me.getPreferences(saveKey) || '' + domUtils.fillHtml + '
'
              me.focus(true)
            }
          },
          queryCommandState: function () {
            return saveKey ? (me.getPreferences(saveKey) === null ? -1 : 0) : -1
          },
          notNeedUndo: true,
          ignoreContentChange: true
        }
      }
    }
  })
  // plugins/charts.js
  UE.plugin.register('charts', function () {
    var me = this
    return {
      bindEvents: {
        chartserror: function () {}
      },
      commands: {
        charts: {
          execCommand: function (cmd, data) {
            var tableNode = domUtils.findParentByTagName(
                this.selection.getRange().startContainer,
                'table',
                true
              ),
              flagText = [],
              config = {}
            if (!tableNode) {
              return false
            }
            if (!validData(tableNode)) {
              me.fireEvent('chartserror')
              return false
            }
            config.title = data.title || ''
            config.subTitle = data.subTitle || ''
            config.xTitle = data.xTitle || ''
            config.yTitle = data.yTitle || ''
            config.suffix = data.suffix || ''
            config.tip = data.tip || ''
            //数据对齐方式
            config.dataFormat = data.tableDataFormat || ''
            //图表类型
            config.chartType = data.chartType || 0
            for (var key in config) {
              if (!config.hasOwnProperty(key)) {
                continue
              }
              flagText.push(key + ':' + config[key])
            }
            tableNode.setAttribute('data-chart', flagText.join(';'))
            domUtils.addClass(tableNode, 'edui-charts-table')
          },
          queryCommandState: function (cmd, name) {
            var tableNode = domUtils.findParentByTagName(
              this.selection.getRange().startContainer,
              'table',
              true
            )
            return tableNode && validData(tableNode) ? 0 : -1
          }
        }
      },
      inputRule: function (root) {
        utils.each(root.getNodesByTagName('table'), function (tableNode) {
          if (tableNode.getAttr('data-chart') !== undefined) {
            tableNode.setAttr('style')
          }
        })
      },
      outputRule: function (root) {
        utils.each(root.getNodesByTagName('table'), function (tableNode) {
          if (tableNode.getAttr('data-chart') !== undefined) {
            tableNode.setAttr('style', 'display: none;')
          }
        })
      }
    }
    function validData(table) {
      var firstRows = null,
        cellCount = 0
      //行数不够
      if (table.rows.length < 2) {
        return false
      }
      //列数不够
      if (table.rows[0].cells.length < 2) {
        return false
      }
      //第一行所有cell必须是th
      firstRows = table.rows[0].cells
      cellCount = firstRows.length
      for (var i = 0, cell; (cell = firstRows[i]); i++) {
        if (cell.tagName.toLowerCase() !== 'th') {
          return false
        }
      }
      for (var i = 1, row; (row = table.rows[i]); i++) {
        //每行单元格数不匹配, 返回false
        if (row.cells.length != cellCount) {
          return false
        }
        //第一列不是th也返回false
        if (row.cells[0].tagName.toLowerCase() !== 'th') {
          return false
        }
        for (var j = 1, cell; (cell = row.cells[j]); j++) {
          var value = utils.trim(cell.innerText || cell.textContent || '')
          value = value
            .replace(new RegExp(UE.dom.domUtils.fillChar, 'g'), '')
            .replace(/^\s+|\s+$/g, '')
          //必须是数字
          if (!/^\d*\.?\d+$/.test(value)) {
            return false
          }
        }
      }
      return true
    }
  })
  // plugins/section.js
  /**
   * 目录大纲支持插件
   * @file
   * @since 1.3.0
   */
  UE.plugin.register('section', function () {
    /* 目录节点对象 */
    function Section(option) {
      this.tag = ''
      ;(this.level = -1), (this.dom = null)
      this.nextSection = null
      this.previousSection = null
      this.parentSection = null
      this.startAddress = []
      this.endAddress = []
      this.children = []
    }
    function getSection(option) {
      var section = new Section()
      return utils.extend(section, option)
    }
    function getNodeFromAddress(startAddress, root) {
      var current = root
      for (var i = 0; i < startAddress.length; i++) {
        if (!current.childNodes) return null
        current = current.childNodes[startAddress[i]]
      }
      return current
    }
    var me = this
    return {
      bindMultiEvents: {
        type: 'aftersetcontent afterscencerestore',
        handler: function () {
          me.fireEvent('updateSections')
        }
      },
      bindEvents: {
        /* 初始化、拖拽、粘贴、执行setcontent之后 */
        ready: function () {
          me.fireEvent('updateSections')
          domUtils.on(me.body, 'drop paste', function () {
            me.fireEvent('updateSections')
          })
        },
        /* 执行paragraph命令之后 */
        afterexeccommand: function (type, cmd) {
          if (cmd == 'paragraph') {
            me.fireEvent('updateSections')
          }
        },
        /* 部分键盘操作,触发updateSections事件 */
        keyup: function (type, e) {
          var me = this,
            range = me.selection.getRange()
          if (range.collapsed != true) {
            me.fireEvent('updateSections')
          } else {
            var keyCode = e.keyCode || e.which
            if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
              me.fireEvent('updateSections')
            }
          }
        }
      },
      commands: {
        getsections: {
          execCommand: function (cmd, levels) {
            var levelFn = levels || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
            for (var i = 0; i < levelFn.length; i++) {
              if (typeof levelFn[i] == 'string') {
                levelFn[i] = (function (fn) {
                  return function (node) {
                    return node.tagName == fn.toUpperCase()
                  }
                })(levelFn[i])
              } else if (typeof levelFn[i] != 'function') {
                levelFn[i] = function (node) {
                  return null
                }
              }
            }
            function getSectionLevel(node) {
              for (var i = 0; i < levelFn.length; i++) {
                if (levelFn[i](node)) return i
              }
              return -1
            }
            var me = this,
              Directory = getSection({ level: -1, title: 'root' }),
              previous = Directory
            function traversal(node, Directory) {
              var level,
                tmpSection = null,
                parent,
                child,
                children = node.childNodes
              for (var i = 0, len = children.length; i < len; i++) {
                child = children[i]
                level = getSectionLevel(child)
                if (level >= 0) {
                  var address = me.selection
                      .getRange()
                      .selectNode(child)
                      .createAddress(true).startAddress,
                    current = getSection({
                      tag: child.tagName,
                      title: child.innerText || child.textContent || '',
                      level: level,
                      dom: child,
                      startAddress: utils.clone(address, []),
                      endAddress: utils.clone(address, []),
                      children: []
                    })
                  previous.nextSection = current
                  current.previousSection = previous
                  parent = previous
                  while (level <= parent.level) {
                    parent = parent.parentSection
                  }
                  current.parentSection = parent
                  parent.children.push(current)
                  tmpSection = previous = current
                } else {
                  child.nodeType === 1 && traversal(child, Directory)
                  tmpSection && tmpSection.endAddress[tmpSection.endAddress.length - 1]++
                }
              }
            }
            traversal(me.body, Directory)
            return Directory
          },
          notNeedUndo: true
        },
        movesection: {
          execCommand: function (cmd, sourceSection, targetSection, isAfter) {
            var me = this,
              targetAddress,
              target
            if (!sourceSection || !targetSection || targetSection.level == -1) return
            targetAddress = isAfter ? targetSection.endAddress : targetSection.startAddress
            target = getNodeFromAddress(targetAddress, me.body)
            /* 判断目标地址是否被源章节包含 */
            if (
              !targetAddress ||
              !target ||
              isContainsAddress(sourceSection.startAddress, sourceSection.endAddress, targetAddress)
            )
              return
            var startNode = getNodeFromAddress(sourceSection.startAddress, me.body),
              endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
              current,
              nextNode
            if (isAfter) {
              current = endNode
              while (
                current &&
                !(domUtils.getPosition(startNode, current) & domUtils.POSITION_FOLLOWING)
              ) {
                nextNode = current.previousSibling
                domUtils.insertAfter(target, current)
                if (current == startNode) break
                current = nextNode
              }
            } else {
              current = startNode
              while (
                current &&
                !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)
              ) {
                nextNode = current.nextSibling
                target.parentNode.insertBefore(current, target)
                if (current == endNode) break
                current = nextNode
              }
            }
            me.fireEvent('updateSections')
            /* 获取地址的包含关系 */
            function isContainsAddress(startAddress, endAddress, addressTarget) {
              var isAfterStartAddress = false,
                isBeforeEndAddress = false
              for (var i = 0; i < startAddress.length; i++) {
                if (i >= addressTarget.length) break
                if (addressTarget[i] > startAddress[i]) {
                  isAfterStartAddress = true
                  break
                } else if (addressTarget[i] < startAddress[i]) {
                  break
                }
              }
              for (var i = 0; i < endAddress.length; i++) {
                if (i >= addressTarget.length) break
                if (addressTarget[i] < startAddress[i]) {
                  isBeforeEndAddress = true
                  break
                } else if (addressTarget[i] > startAddress[i]) {
                  break
                }
              }
              return isAfterStartAddress && isBeforeEndAddress
            }
          }
        },
        deletesection: {
          execCommand: function (cmd, section, keepChildren) {
            var me = this
            if (!section) return
            function getNodeFromAddress(startAddress) {
              var current = me.body
              for (var i = 0; i < startAddress.length; i++) {
                if (!current.childNodes) return null
                current = current.childNodes[startAddress[i]]
              }
              return current
            }
            var startNode = getNodeFromAddress(section.startAddress),
              endNode = getNodeFromAddress(section.endAddress),
              current = startNode,
              nextNode
            if (!keepChildren) {
              while (
                current &&
                domUtils.inDoc(endNode, me.document) &&
                !(domUtils.getPosition(current, endNode) & domUtils.POSITION_FOLLOWING)
              ) {
                nextNode = current.nextSibling
                domUtils.remove(current)
                current = nextNode
              }
            } else {
              domUtils.remove(current)
            }
            me.fireEvent('updateSections')
          }
        },
        selectsection: {
          execCommand: function (cmd, section) {
            if (!section && !section.dom) return false
            var me = this,
              range = me.selection.getRange(),
              address = {
                startAddress: utils.clone(section.startAddress, []),
                endAddress: utils.clone(section.endAddress, [])
              }
            address.endAddress[address.endAddress.length - 1]++
            range.moveToAddress(address).select().scrollToView()
            return true
          },
          notNeedUndo: true
        },
        scrolltosection: {
          execCommand: function (cmd, section) {
            if (!section && !section.dom) return false
            var me = this,
              range = me.selection.getRange(),
              address = {
                startAddress: section.startAddress,
                endAddress: section.endAddress
              }
            address.endAddress[address.endAddress.length - 1]++
            range.moveToAddress(address).scrollToView()
            return true
          },
          notNeedUndo: true
        }
      }
    }
  })
  // plugins/simpleupload.js
  /**
   * @description
   * 简单上传:点击按钮,直接选择文件上传。
   * 原 UEditor 作者使用了 form 表单 + iframe 的方式上传
   * 但由于同源策略的限制,父页面无法访问跨域的 iframe 内容
   * 导致无法获取接口返回的数据,使得单图上传无法在跨域的情况下使用
   * 这里改为普通的XHR上传,兼容到IE10+
   * @author HaoChuan9421 
   * @date 2018-12-20
   */
  UE.plugin.register('simpleupload', function () {
    var me = this,
      containerBtn,
      timestrap = (+new Date()).toString(36)
    function initUploadBtn() {
      var w = containerBtn.offsetWidth || 20,
        h = containerBtn.offsetHeight || 20,
        btnStyle =
          'display:block;width:' +
          w +
          'px;height:' +
          h +
          'px;overflow:hidden;border:0;margin:0;padding:0;position:absolute;top:0;left:0;filter:alpha(opacity=0);-moz-opacity:0;-khtml-opacity: 0;opacity: 0;cursor:pointer;'
      var form = document.createElement('form')
      var input = document.createElement('input')
      form.id = 'edui_form_' + timestrap
      form.enctype = 'multipart/form-data'
      form.style = btnStyle
      input.id = 'edui_input_' + timestrap
      input.type = 'file'
      input.accept = 'image/*'
      input.name = me.options.imageFieldName
      input.style = btnStyle
      form.appendChild(input)
      containerBtn.appendChild(form)
      input.addEventListener('change', function (event) {
        if (!input.value) return
        var loadingId = 'loading_' + (+new Date()).toString(36)
        var imageActionUrl = me.getActionUrl(me.getOpt('imageActionName'))
        var params = utils.serializeParam(me.queryCommandValue('serverparam')) || ''
        var action = utils.formatUrl(
          imageActionUrl + (imageActionUrl.indexOf('?') == -1 ? '?' : '&') + params
        )
        var allowFiles = me.getOpt('imageAllowFiles')
        console.log(action, 'action', 'allowFiles', allowFiles)
        me.focus()
        me.execCommand(
          'inserthtml',
          '' +
                '' +
                title +
                ' ' +
                '
'
            }
            me.execCommand('insertHtml', html)
          }
        }
      }
    }
  })
  // plugins/xssFilter.js
  /**
   * @file xssFilter.js
   * @desc xss过滤器
   * @author robbenmu
   */
  UE.plugins.xssFilter = function () {
    var config = UEDITOR_CONFIG
    var whitList = config.whitList
    function filter(node) {
      var tagName = node.tagName
      var attrs = node.attrs
      if (!whitList.hasOwnProperty(tagName)) {
        node.parentNode.removeChild(node)
        return false
      }
      UE.utils.each(attrs, function (val, key) {
        if (whitList[tagName].indexOf(key) === -1) {
          node.setAttr(key)
        }
      })
    }
    // 添加inserthtml\paste等操作用的过滤规则
    if (whitList && config.xssFilterRules) {
      this.options.filterRules = (function () {
        var result = {}
        UE.utils.each(whitList, function (val, key) {
          result[key] = function (node) {
            return filter(node)
          }
        })
        return result
      })()
    }
    var tagList = []
    UE.utils.each(whitList, function (val, key) {
      tagList.push(key)
    })
    // 添加input过滤规则
    //
    if (whitList && config.inputXssFilter) {
      this.addInputRule(function (root) {
        root.traversal(function (node) {
          if (node.type !== 'element') {
            return false
          }
          filter(node)
        })
      })
    }
    // 添加output过滤规则
    //
    if (whitList && config.outputXssFilter) {
      this.addOutputRule(function (root) {
        root.traversal(function (node) {
          if (node.type !== 'element') {
            return false
          }
          filter(node)
        })
      })
    }
  }
  // ui/ui.js
  var baidu = baidu || {}
  baidu.editor = baidu.editor || {}
  UE.ui = baidu.editor.ui = {}
  // ui/uiutils.js
  ;(function () {
    var browser = baidu.editor.browser,
      domUtils = baidu.editor.dom.domUtils
    var magic = '$EDITORUI'
    var root = (window[magic] = {})
    var uidMagic = 'ID' + magic
    var uidCount = 0
    var uiUtils = (baidu.editor.ui.uiUtils = {
      uid: function (obj) {
        return obj ? obj[uidMagic] || (obj[uidMagic] = ++uidCount) : ++uidCount
      },
      hook: function (fn, callback) {
        var dg
        if (fn && fn._callbacks) {
          dg = fn
        } else {
          dg = function () {
            var q
            if (fn) {
              q = fn.apply(this, arguments)
            }
            var callbacks = dg._callbacks
            var k = callbacks.length
            while (k--) {
              var r = callbacks[k].apply(this, arguments)
              if (q === undefined) {
                q = r
              }
            }
            return q
          }
          dg._callbacks = []
        }
        dg._callbacks.push(callback)
        return dg
      },
      createElementByHtml: function (html) {
        var el = document.createElement('div')
        el.innerHTML = html
        el = el.firstChild
        el.parentNode.removeChild(el)
        return el
      },
      getViewportElement: function () {
        return browser.ie && browser.quirks ? document.body : document.documentElement
      },
      getClientRect: 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
      },
      getViewportRect: function () {
        var viewportEl = uiUtils.getViewportElement()
        var width = (window.innerWidth || viewportEl.clientWidth) | 0
        var height = (window.innerHeight || viewportEl.clientHeight) | 0
        return {
          left: 0,
          top: 0,
          height: height,
          width: width,
          bottom: height,
          right: width
        }
      },
      setViewportOffset: function (element, offset) {
        var rect
        var fixedLayer = uiUtils.getFixedLayer()
        if (element.parentNode === fixedLayer) {
          element.style.left = offset.left + 'px'
          element.style.top = offset.top + 'px'
        } else {
          domUtils.setViewportOffset(element, offset)
        }
      },
      getEventOffset: function (evt) {
        var el = evt.target || evt.srcElement
        var rect = uiUtils.getClientRect(el)
        var offset = uiUtils.getViewportOffsetByEvent(evt)
        return {
          left: offset.left - rect.left,
          top: offset.top - rect.top
        }
      },
      getViewportOffsetByEvent: function (evt) {
        var el = evt.target || evt.srcElement
        var frameEl = domUtils.getWindow(el).frameElement
        var offset = {
          left: evt.clientX,
          top: evt.clientY
        }
        if (frameEl && el.ownerDocument !== document) {
          var rect = uiUtils.getClientRect(frameEl)
          offset.left += rect.left
          offset.top += rect.top
        }
        return offset
      },
      setGlobal: function (id, obj) {
        root[id] = obj
        return magic + '["' + id + '"]'
      },
      unsetGlobal: function (id) {
        delete root[id]
      },
      copyAttributes: function (tgt, src) {
        var attributes = src.attributes
        var k = attributes.length
        while (k--) {
          var attrNode = attributes[k]
          if (
            attrNode.nodeName != 'style' &&
            attrNode.nodeName != 'class' &&
            (!browser.ie || attrNode.specified)
          ) {
            tgt.setAttribute(attrNode.nodeName, attrNode.nodeValue)
          }
        }
        if (src.className) {
          domUtils.addClass(tgt, src.className)
        }
        if (src.style.cssText) {
          tgt.style.cssText += ';' + src.style.cssText
        }
      },
      removeStyle: function (el, styleName) {
        if (el.style.removeProperty) {
          el.style.removeProperty(styleName)
        } else if (el.style.removeAttribute) {
          el.style.removeAttribute(styleName)
        } else throw ''
      },
      contains: function (elA, elB) {
        return (
          elA &&
          elB &&
          (elA === elB
            ? false
            : elA.contains
            ? elA.contains(elB)
            : elA.compareDocumentPosition(elB) & 16)
        )
      },
      startDrag: function (evt, callbacks, doc) {
        var doc = doc || document
        var startX = evt.clientX
        var startY = evt.clientY
        function handleMouseMove(evt) {
          var x = evt.clientX - startX
          var y = evt.clientY - startY
          callbacks.ondragmove(x, y, evt)
          if (evt.stopPropagation) {
            evt.stopPropagation()
          } else {
            evt.cancelBubble = true
          }
        }
        if (doc.addEventListener) {
          function handleMouseUp(evt) {
            doc.removeEventListener('mousemove', handleMouseMove, true)
            doc.removeEventListener('mouseup', handleMouseUp, true)
            window.removeEventListener('mouseup', handleMouseUp, true)
            callbacks.ondragstop()
          }
          doc.addEventListener('mousemove', handleMouseMove, true)
          doc.addEventListener('mouseup', handleMouseUp, true)
          window.addEventListener('mouseup', handleMouseUp, true)
          evt.preventDefault()
        } else {
          var elm = evt.srcElement
          elm.setCapture()
          function releaseCaptrue() {
            elm.releaseCapture()
            elm.detachEvent('onmousemove', handleMouseMove)
            elm.detachEvent('onmouseup', releaseCaptrue)
            elm.detachEvent('onlosecaptrue', releaseCaptrue)
            callbacks.ondragstop()
          }
          elm.attachEvent('onmousemove', handleMouseMove)
          elm.attachEvent('onmouseup', releaseCaptrue)
          elm.attachEvent('onlosecaptrue', releaseCaptrue)
          evt.returnValue = false
        }
        callbacks.ondragstart()
      },
      getFixedLayer: function () {
        var layer = document.getElementById('edui_fixedlayer')
        if (layer == null) {
          layer = document.createElement('div')
          layer.id = 'edui_fixedlayer'
          document.body.appendChild(layer)
          if (browser.ie && browser.version <= 8) {
            layer.style.position = 'absolute'
            bindFixedLayer()
            setTimeout(updateFixedOffset)
          } else {
            layer.style.position = 'fixed'
          }
          layer.style.left = '0'
          layer.style.top = '0'
          layer.style.width = '0'
          layer.style.height = '0'
          layer.style.zIndex = '3005'
        }
        return layer
      },
      makeUnselectable: function (element) {
        if (browser.opera || (browser.ie && browser.version < 9)) {
          element.unselectable = 'on'
          if (element.hasChildNodes()) {
            for (var i = 0; i < element.childNodes.length; i++) {
              if (element.childNodes[i].nodeType == 1) {
                uiUtils.makeUnselectable(element.childNodes[i])
              }
            }
          }
        } else {
          if (element.style.MozUserSelect !== undefined) {
            element.style.MozUserSelect = 'none'
          } else if (element.style.WebkitUserSelect !== undefined) {
            element.style.WebkitUserSelect = 'none'
          } else if (element.style.KhtmlUserSelect !== undefined) {
            element.style.KhtmlUserSelect = 'none'
          }
        }
      }
    })
    function updateFixedOffset() {
      var layer = document.getElementById('edui_fixedlayer')
      uiUtils.setViewportOffset(layer, {
        left: 0,
        top: 0
      })
      //        layer.style.display = 'none';
      //        layer.style.display = 'block';
      //#trace: 1354
      //        setTimeout(updateFixedOffset);
    }
    function bindFixedLayer(adjOffset) {
      domUtils.on(window, 'scroll', updateFixedOffset)
      domUtils.on(window, 'resize', baidu.editor.utils.defer(updateFixedOffset, 0, true))
    }
  })()
  // ui/uibase.js
  ;(function () {
    var utils = baidu.editor.utils,
      uiUtils = baidu.editor.ui.uiUtils,
      EventBase = baidu.editor.EventBase,
      UIBase = (baidu.editor.ui.UIBase = function () {})
    UIBase.prototype = {
      className: '',
      uiName: '',
      initOptions: function (options) {
        var me = this
        for (var k in options) {
          me[k] = options[k]
        }
        this.id = this.id || 'edui' + uiUtils.uid()
      },
      initUIBase: function () {
        this._globalKey = utils.unhtml(uiUtils.setGlobal(this.id, this))
      },
      render: function (holder) {
        var html = this.renderHtml()
        var el = uiUtils.createElementByHtml(html)
        //by xuheng 给每个node添加class
        var list = domUtils.getElementsByTagName(el, '*')
        var theme = 'edui-' + (this.theme || this.editor.options.theme)
        var layer = document.getElementById('edui_fixedlayer')
        for (var i = 0, node; (node = list[i++]); ) {
          domUtils.addClass(node, theme)
        }
        domUtils.addClass(el, theme)
        if (layer) {
          layer.className = ''
          domUtils.addClass(layer, theme)
        }
        var seatEl = this.getDom()
        if (seatEl != null) {
          seatEl.parentNode.replaceChild(el, seatEl)
          uiUtils.copyAttributes(el, seatEl)
        } else {
          if (typeof holder == 'string') {
            holder = document.getElementById(holder)
          }
          holder = holder || uiUtils.getFixedLayer()
          domUtils.addClass(holder, theme)
          holder.appendChild(el)
        }
        this.postRender()
      },
      getDom: function (name) {
        if (!name) {
          return document.getElementById(this.id)
        } else {
          return document.getElementById(this.id + '_' + name)
        }
      },
      postRender: function () {
        this.fireEvent('postrender')
      },
      getHtmlTpl: function () {
        return ''
      },
      formatHtml: function (tpl) {
        var prefix = 'edui-' + this.uiName
        return tpl
          .replace(/##/g, this.id)
          .replace(/%%-/g, this.uiName ? prefix + '-' : '')
          .replace(/%%/g, (this.uiName ? prefix : '') + ' ' + this.className)
          .replace(/\$\$/g, this._globalKey)
      },
      renderHtml: function () {
        return this.formatHtml(this.getHtmlTpl())
      },
      dispose: function () {
        var box = this.getDom()
        if (box) baidu.editor.dom.domUtils.remove(box)
        uiUtils.unsetGlobal(this.id)
      }
    }
    utils.inherits(UIBase, EventBase)
  })()
  // ui/separator.js
  ;(function () {
    var utils = baidu.editor.utils,
      UIBase = baidu.editor.ui.UIBase,
      Separator = (baidu.editor.ui.Separator = function (options) {
        this.initOptions(options)
        this.initSeparator()
      })
    Separator.prototype = {
      uiName: 'separator',
      initSeparator: function () {
        this.initUIBase()
      },
      getHtmlTpl: function () {
        return '
'
      }
    }
    utils.inherits(Separator, UIBase)
  })()
  // ui/mask.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      domUtils = baidu.editor.dom.domUtils,
      UIBase = baidu.editor.ui.UIBase,
      uiUtils = baidu.editor.ui.uiUtils
    var Mask = (baidu.editor.ui.Mask = function (options) {
      this.initOptions(options)
      this.initUIBase()
    })
    Mask.prototype = {
      getHtmlTpl: function () {
        return '
'
      },
      postRender: function () {
        var me = this
        domUtils.on(window, 'resize', function () {
          setTimeout(function () {
            if (!me.isHidden()) {
              me._fill()
            }
          })
        })
      },
      show: function (zIndex) {
        this._fill()
        this.getDom().style.display = ''
        this.getDom().style.zIndex = zIndex
      },
      hide: function () {
        this.getDom().style.display = 'none'
        this.getDom().style.zIndex = ''
      },
      isHidden: function () {
        return this.getDom().style.display == 'none'
      },
      _onMouseDown: function () {
        return false
      },
      _onClick: function (e, target) {
        this.fireEvent('click', e, target)
      },
      _fill: function () {
        var el = this.getDom()
        var vpRect = uiUtils.getViewportRect()
        el.style.width = vpRect.width + 'px'
        el.style.height = vpRect.height + 'px'
      }
    }
    utils.inherits(Mask, UIBase)
  })()
  // ui/popup.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      uiUtils = baidu.editor.ui.uiUtils,
      domUtils = baidu.editor.dom.domUtils,
      UIBase = baidu.editor.ui.UIBase,
      Popup = (baidu.editor.ui.Popup = function (options) {
        this.initOptions(options)
        this.initPopup()
      })
    var allPopups = []
    function closeAllPopup(evt, el) {
      for (var i = 0; i < allPopups.length; i++) {
        var pop = allPopups[i]
        if (!pop.isHidden()) {
          if (pop.queryAutoHide(el) !== false) {
            if (evt && /scroll/gi.test(evt.type) && pop.className == 'edui-wordpastepop') return
            pop.hide()
          }
        }
      }
      if (allPopups.length) pop.editor.fireEvent('afterhidepop')
    }
    Popup.postHide = closeAllPopup
    var ANCHOR_CLASSES = [
      'edui-anchor-topleft',
      'edui-anchor-topright',
      'edui-anchor-bottomleft',
      'edui-anchor-bottomright'
    ]
    Popup.prototype = {
      SHADOW_RADIUS: 5,
      content: null,
      _hidden: false,
      autoRender: true,
      canSideLeft: true,
      canSideUp: true,
      initPopup: function () {
        this.initUIBase()
        allPopups.push(this)
      },
      getHtmlTpl: function () {
        return (
          ''
        )
      },
      getContentHtmlTpl: function () {
        if (this.content) {
          if (typeof this.content == 'string') {
            return this.content
          }
          return this.content.renderHtml()
        } else {
          return ''
        }
      },
      _UIBase_postRender: UIBase.prototype.postRender,
      postRender: function () {
        if (this.content instanceof UIBase) {
          this.content.postRender()
        }
        //捕获鼠标滚轮
        if (this.captureWheel && !this.captured) {
          this.captured = true
          var winHeight =
              (document.documentElement.clientHeight || document.body.clientHeight) - 80,
            _height = this.getDom().offsetHeight,
            _top = uiUtils.getClientRect(this.combox.getDom()).top,
            content = this.getDom('content'),
            ifr = this.getDom('body').getElementsByTagName('iframe'),
            me = this
          ifr.length && (ifr = ifr[0])
          while (_top + _height > winHeight) {
            _height -= 30
          }
          content.style.height = _height + 'px'
          //同步更改iframe高度
          ifr && (ifr.style.height = _height + 'px')
          //阻止在combox上的鼠标滚轮事件, 防止用户的正常操作被误解
          if (window.XMLHttpRequest) {
            domUtils.on(
              content,
              'onmousewheel' in document.body ? 'mousewheel' : 'DOMMouseScroll',
              function (e) {
                if (e.preventDefault) {
                  e.preventDefault()
                } else {
                  e.returnValue = false
                }
                if (e.wheelDelta) {
                  content.scrollTop -= (e.wheelDelta / 120) * 60
                } else {
                  content.scrollTop -= (e.detail / -3) * 60
                }
              }
            )
          } else {
            //ie6
            domUtils.on(this.getDom(), 'mousewheel', function (e) {
              e.returnValue = false
              me.getDom('content').scrollTop -= (e.wheelDelta / 120) * 60
            })
          }
        }
        this.fireEvent('postRenderAfter')
        this.hide(true)
        this._UIBase_postRender()
      },
      _doAutoRender: function () {
        if (!this.getDom() && this.autoRender) {
          this.render()
        }
      },
      mesureSize: function () {
        var box = this.getDom('content')
        return uiUtils.getClientRect(box)
      },
      fitSize: function () {
        if (this.captureWheel && this.sized) {
          return this.__size
        }
        this.sized = true
        var popBodyEl = this.getDom('body')
        popBodyEl.style.width = ''
        popBodyEl.style.height = ''
        var size = this.mesureSize()
        if (this.captureWheel) {
          popBodyEl.style.width = -(-20 - size.width) + 'px'
          var height = parseInt(this.getDom('content').style.height, 10)
          !window.isNaN(height) && (size.height = height)
        } else {
          popBodyEl.style.width = size.width + 'px'
        }
        popBodyEl.style.height = size.height + 'px'
        this.__size = size
        this.captureWheel && (this.getDom('content').style.overflow = 'auto')
        return size
      },
      showAnchor: function (element, hoz) {
        this.showAnchorRect(uiUtils.getClientRect(element), hoz)
      },
      showAnchorRect: function (rect, hoz, adj) {
        this._doAutoRender()
        var vpRect = uiUtils.getViewportRect()
        this.getDom().style.visibility = 'hidden'
        this._show()
        var popSize = this.fitSize()
        var sideLeft, sideUp, left, top
        if (hoz) {
          sideLeft =
            this.canSideLeft &&
            rect.right + popSize.width > vpRect.right &&
            rect.left > popSize.width
          sideUp =
            this.canSideUp &&
            rect.top + popSize.height > vpRect.bottom &&
            rect.bottom > popSize.height
          left = sideLeft ? rect.left - popSize.width : rect.right
          top = sideUp ? rect.bottom - popSize.height : rect.top
        } else {
          sideLeft =
            this.canSideLeft &&
            rect.right + popSize.width > vpRect.right &&
            rect.left > popSize.width
          sideUp =
            this.canSideUp &&
            rect.top + popSize.height > vpRect.bottom &&
            rect.bottom > popSize.height
          left = sideLeft ? rect.right - popSize.width : rect.left
          top = sideUp ? rect.top - popSize.height : rect.bottom
        }
        var popEl = this.getDom()
        uiUtils.setViewportOffset(popEl, {
          left: left,
          top: top
        })
        domUtils.removeClasses(popEl, ANCHOR_CLASSES)
        popEl.className += ' ' + ANCHOR_CLASSES[(sideUp ? 1 : 0) * 2 + (sideLeft ? 1 : 0)]
        if (this.editor) {
          popEl.style.zIndex = this.editor.container.style.zIndex * 1 + 10
          baidu.editor.ui.uiUtils.getFixedLayer().style.zIndex = popEl.style.zIndex - 1
        }
        this.getDom().style.visibility = 'visible'
      },
      showAt: function (offset) {
        var left = offset.left
        var top = offset.top
        var rect = {
          left: left,
          top: top,
          right: left,
          bottom: top,
          height: 0,
          width: 0
        }
        this.showAnchorRect(rect, false, true)
      },
      _show: function () {
        if (this._hidden) {
          var box = this.getDom()
          box.style.display = ''
          this._hidden = false
          //                if (box.setActive) {
          //                    box.setActive();
          //                }
          this.fireEvent('show')
        }
      },
      isHidden: function () {
        return this._hidden
      },
      show: function () {
        this._doAutoRender()
        this._show()
      },
      hide: function (notNofity) {
        if (!this._hidden && this.getDom()) {
          this.getDom().style.display = 'none'
          this._hidden = true
          if (!notNofity) {
            this.fireEvent('hide')
          }
        }
      },
      queryAutoHide: function (el) {
        return !el || !uiUtils.contains(this.getDom(), el)
      }
    }
    utils.inherits(Popup, UIBase)
    domUtils.on(document, 'mousedown', function (evt) {
      var el = evt.target || evt.srcElement
      closeAllPopup(evt, el)
    })
    domUtils.on(window, 'scroll', function (evt, el) {
      closeAllPopup(evt, el)
    })
  })()
  // ui/colorpicker.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      UIBase = baidu.editor.ui.UIBase,
      ColorPicker = (baidu.editor.ui.ColorPicker = function (options) {
        this.initOptions(options)
        this.noColorText = this.noColorText || this.editor.getLang('clearColor')
        this.initUIBase()
      })
    ColorPicker.prototype = {
      getHtmlTpl: function () {
        return genColorPicker(this.noColorText, this.editor)
      },
      _onTableClick: function (evt) {
        var tgt = evt.target || evt.srcElement
        var color = tgt.getAttribute('data-color')
        if (color) {
          this.fireEvent('pickcolor', color)
        }
      },
      _onTableOver: function (evt) {
        var tgt = evt.target || evt.srcElement
        var color = tgt.getAttribute('data-color')
        if (color) {
          this.getDom('preview').style.backgroundColor = color
        }
      },
      _onTableOut: function () {
        this.getDom('preview').style.backgroundColor = ''
      },
      _onPickNoColor: function () {
        this.fireEvent('picknocolor')
      }
    }
    utils.inherits(ColorPicker, UIBase)
    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(',')
    function genColorPicker(noColorText, editor) {
      var html =
        '' +
        '
' +
        '
' +
        '
' +
        noColorText +
        '
' +
        '
' +
        '
' +
        '' +
        editor.getLang('themeColor') +
        '  '
      for (var i = 0; i < COLORS.length; i++) {
        if (i && i % 10 === 0) {
          html +=
            ' ' +
            (i == 60
              ? '' +
                editor.getLang('standardColor') +
                ' '
        }
        html +=
          i < 70
            ? ' 
' +
          '
' +
          '
' +
          '
' +
          '
' +
          '
' +
          '
' +
          '
' +
          '
' +
          (this.showIcon ? '
' : '') +
          (this.showText ? '
' + this.label + '
' : '') +
          '
' +
          '
' +
          '
'
          )
        )
        this.getDom().className += ' edui-colorbutton'
      },
      setColor: function (color) {
        this.getDom('colorlump').style.backgroundColor = color
        this.color = color
      },
      _onPickColor: function (color) {
        if (this.fireEvent('pickcolor', color) !== false) {
          this.setColor(color)
          this.popup.hide()
        }
      },
      _onPickNoColor: function (color) {
        if (this.fireEvent('picknocolor') !== false) {
          this.popup.hide()
        }
      }
    }
    utils.inherits(ColorButton, SplitButton)
  })()
  // ui/tablebutton.js
  ///import core
  ///import uicore
  ///import ui/popup.js
  ///import ui/tablepicker.js
  ///import ui/splitbutton.js
  ;(function () {
    var utils = baidu.editor.utils,
      Popup = baidu.editor.ui.Popup,
      TablePicker = baidu.editor.ui.TablePicker,
      SplitButton = baidu.editor.ui.SplitButton,
      TableButton = (baidu.editor.ui.TableButton = function (options) {
        this.initOptions(options)
        this.initTableButton()
      })
    TableButton.prototype = {
      initTableButton: function () {
        var me = this
        this.popup = new Popup({
          content: new TablePicker({
            editor: me.editor,
            onpicktable: function (t, numCols, numRows) {
              me._onPickTable(numCols, numRows)
            }
          }),
          editor: me.editor
        })
        this.initSplitButton()
      },
      _onPickTable: function (numCols, numRows) {
        if (this.fireEvent('picktable', numCols, numRows) !== false) {
          this.popup.hide()
        }
      }
    }
    utils.inherits(TableButton, SplitButton)
  })()
  // ui/autotypesetpicker.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      UIBase = baidu.editor.ui.UIBase
    var AutoTypeSetPicker = (baidu.editor.ui.AutoTypeSetPicker = function (options) {
      this.initOptions(options)
      this.initAutoTypeSetPicker()
    })
    AutoTypeSetPicker.prototype = {
      initAutoTypeSetPicker: function () {
        this.initUIBase()
      },
      getHtmlTpl: function () {
        var me = this.editor,
          opt = me.options.autotypeset,
          lang = me.getLang('autoTypeSet')
        var textAlignInputName = 'textAlignValue' + me.uid,
          imageBlockInputName = 'imageBlockLineValue' + me.uid,
          symbolConverInputName = 'symbolConverValue' + me.uid
        return (
          ''
        )
      },
      _UIBase_render: UIBase.prototype.render
    }
    utils.inherits(AutoTypeSetPicker, UIBase)
  })()
  // ui/autotypesetbutton.js
  ///import core
  ///import uicore
  ///import ui/popup.js
  ///import ui/autotypesetpicker.js
  ///import ui/splitbutton.js
  ;(function () {
    var utils = baidu.editor.utils,
      Popup = baidu.editor.ui.Popup,
      AutoTypeSetPicker = baidu.editor.ui.AutoTypeSetPicker,
      SplitButton = baidu.editor.ui.SplitButton,
      AutoTypeSetButton = (baidu.editor.ui.AutoTypeSetButton = function (options) {
        this.initOptions(options)
        this.initAutoTypeSetButton()
      })
    function getPara(me) {
      var opt = {},
        cont = me.getDom(),
        editorId = me.editor.uid,
        inputType = null,
        attrName = null,
        ipts = domUtils.getElementsByTagName(cont, 'input')
      for (var i = ipts.length - 1, ipt; (ipt = ipts[i--]); ) {
        inputType = ipt.getAttribute('type')
        if (inputType == 'checkbox') {
          attrName = ipt.getAttribute('name')
          opt[attrName] && delete opt[attrName]
          if (ipt.checked) {
            var attrValue = document.getElementById(attrName + 'Value' + editorId)
            if (attrValue) {
              if (/input/gi.test(attrValue.tagName)) {
                opt[attrName] = attrValue.value
              } else {
                var iptChilds = attrValue.getElementsByTagName('input')
                for (var j = iptChilds.length - 1, iptchild; (iptchild = iptChilds[j--]); ) {
                  if (iptchild.checked) {
                    opt[attrName] = iptchild.value
                    break
                  }
                }
              }
            } else {
              opt[attrName] = true
            }
          } else {
            opt[attrName] = false
          }
        } else {
          opt[ipt.getAttribute('value')] = ipt.checked
        }
      }
      var selects = domUtils.getElementsByTagName(cont, 'select')
      for (var i = 0, si; (si = selects[i++]); ) {
        var attr = si.getAttribute('name')
        opt[attr] = opt[attr] ? si.value : ''
      }
      utils.extend(me.editor.options.autotypeset, opt)
      me.editor.setPreferences('autotypeset', opt)
    }
    AutoTypeSetButton.prototype = {
      initAutoTypeSetButton: function () {
        var me = this
        this.popup = new Popup({
          //传入配置参数
          content: new AutoTypeSetPicker({ editor: me.editor }),
          editor: me.editor,
          hide: function () {
            if (!this._hidden && this.getDom()) {
              getPara(this)
              this.getDom().style.display = 'none'
              this._hidden = true
              this.fireEvent('hide')
            }
          }
        })
        var flag = 0
        this.popup.addListener('postRenderAfter', function () {
          var popupUI = this
          if (flag) return
          var cont = this.getDom(),
            btn = cont.getElementsByTagName('button')[0]
          btn.onclick = function () {
            getPara(popupUI)
            me.editor.execCommand('autotypeset')
            popupUI.hide()
          }
          domUtils.on(cont, 'click', function (e) {
            var target = e.target || e.srcElement,
              editorId = me.editor.uid
            if (target && target.tagName == 'INPUT') {
              // 点击图片浮动的checkbox,去除对应的radio
              if (
                target.name == 'imageBlockLine' ||
                target.name == 'textAlign' ||
                target.name == 'symbolConver'
              ) {
                var checked = target.checked,
                  radioTd = document.getElementById(target.name + 'Value' + editorId),
                  radios = radioTd.getElementsByTagName('input'),
                  defalutSelect = {
                    imageBlockLine: 'none',
                    textAlign: 'left',
                    symbolConver: 'tobdc'
                  }
                for (var i = 0; i < radios.length; i++) {
                  if (checked) {
                    if (radios[i].value == defalutSelect[target.name]) {
                      radios[i].checked = 'checked'
                    }
                  } else {
                    radios[i].checked = false
                  }
                }
              }
              // 点击radio,选中对应的checkbox
              if (
                target.name == 'imageBlockLineValue' + editorId ||
                target.name == 'textAlignValue' + editorId ||
                target.name == 'bdc'
              ) {
                var checkboxs = target.parentNode.previousSibling.getElementsByTagName('input')
                checkboxs && (checkboxs[0].checked = true)
              }
              getPara(popupUI)
            }
          })
          flag = 1
        })
        this.initSplitButton()
      }
    }
    utils.inherits(AutoTypeSetButton, SplitButton)
  })()
  // ui/cellalignpicker.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      Popup = baidu.editor.ui.Popup,
      Stateful = baidu.editor.ui.Stateful,
      UIBase = baidu.editor.ui.UIBase
    /**
     * 该参数将新增一个参数: selected, 参数类型为一个Object, 形如{ 'align': 'center', 'valign': 'top' }, 表示单元格的初始
     * 对齐状态为: 竖直居上,水平居中; 其中 align的取值为:'center', 'left', 'right'; valign的取值为: 'top', 'middle', 'bottom'
     * @update 2013/4/2 hancong03@baidu.com
     */
    var CellAlignPicker = (baidu.editor.ui.CellAlignPicker = function (options) {
      this.initOptions(options)
      this.initSelected()
      this.initCellAlignPicker()
    })
    CellAlignPicker.prototype = {
      //初始化选中状态, 该方法将根据传递进来的参数获取到应该选中的对齐方式图标的索引
      initSelected: function () {
        var status = {
            valign: {
              top: 0,
              middle: 1,
              bottom: 2
            },
            align: {
              left: 0,
              center: 1,
              right: 2
            },
            count: 3
          },
          result = -1
        if (this.selected) {
          this.selectedIndex =
            status.valign[this.selected.valign] * status.count + status.align[this.selected.align]
        }
      },
      initCellAlignPicker: function () {
        this.initUIBase()
        this.Stateful_init()
      },
      getHtmlTpl: function () {
        var alignType = ['left', 'center', 'right'],
          COUNT = 9,
          tempClassName = null,
          tempIndex = -1,
          tmpl = []
        for (var i = 0; i < COUNT; i++) {
          tempClassName = this.selectedIndex === i ? ' class="edui-cellalign-selected" ' : ''
          tempIndex = i % 3
          tempIndex === 0 && tmpl.push('')
          tmpl.push(
            '
 ')
        }
        return (
          ''
        )
      },
      getStateDom: function () {
        return this.target
      },
      _onClick: function (evt) {
        var target = evt.target || evt.srcElement
        if (/icon/.test(target.className)) {
          this.items[target.parentNode.getAttribute('index')].onclick()
          Popup.postHide(evt)
        }
      },
      _UIBase_render: UIBase.prototype.render
    }
    utils.inherits(CellAlignPicker, UIBase)
    utils.extend(CellAlignPicker.prototype, Stateful, true)
  })()
  // ui/pastepicker.js
  ///import core
  ///import uicore
  ;(function () {
    var utils = baidu.editor.utils,
      Stateful = baidu.editor.ui.Stateful,
      uiUtils = baidu.editor.ui.uiUtils,
      UIBase = baidu.editor.ui.UIBase
    var PastePicker = (baidu.editor.ui.PastePicker = function (options) {
      this.initOptions(options)
      this.initPastePicker()
    })
    PastePicker.prototype = {
      initPastePicker: function () {
        this.initUIBase()
        this.Stateful_init()
      },
      getHtmlTpl: function () {
        return (
          '
' +
          '' +
          '
' +
          this.editor.getLang('pasteOpt') +
          '
' +
          '
' +
          '
' +
          buff.join('') +
          '
'
        )
      },
      postRender: function () {
        var box = this.getDom()
        for (var i = 0; i < this.items.length; i++) {
          this.items[i].postRender()
        }
        uiUtils.makeUnselectable(box)
      },
      _onMouseDown: function (e) {
        var target = e.target || e.srcElement,
          tagName = target && target.tagName && target.tagName.toLowerCase()
        if (tagName == 'input' || tagName == 'object' || tagName == 'object') {
          return false
        }
      }
    }
    utils.inherits(Toolbar, UIBase)
  })()
  // ui/menu.js
  ///import core
  ///import uicore
  ///import ui\popup.js
  ///import ui\stateful.js
  ;(function () {
    var utils = baidu.editor.utils,
      domUtils = baidu.editor.dom.domUtils,
      uiUtils = baidu.editor.ui.uiUtils,
      UIBase = baidu.editor.ui.UIBase,
      Popup = baidu.editor.ui.Popup,
      Stateful = baidu.editor.ui.Stateful,
      CellAlignPicker = baidu.editor.ui.CellAlignPicker,
      Menu = (baidu.editor.ui.Menu = function (options) {
        this.initOptions(options)
        this.initMenu()
      })
    var menuSeparator = {
      renderHtml: function () {
        return ''
      },
      postRender: function () {},
      queryAutoHide: function () {
        return true
      }
    }
    Menu.prototype = {
      items: null,
      uiName: 'menu',
      initMenu: function () {
        this.items = this.items || []
        this.initPopup()
        this.initItems()
      },
      initItems: function () {
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          if (item == '-') {
            this.items[i] = this.getSeparator()
          } else if (!(item instanceof MenuItem)) {
            item.editor = this.editor
            item.theme = this.editor.options.theme
            this.items[i] = this.createItem(item)
          }
        }
      },
      getSeparator: function () {
        return menuSeparator
      },
      createItem: function (item) {
        //新增一个参数menu, 该参数存储了menuItem所对应的menu引用
        item.menu = this
        return new MenuItem(item)
      },
      _Popup_getContentHtmlTpl: Popup.prototype.getContentHtmlTpl,
      getContentHtmlTpl: function () {
        if (this.items.length == 0) {
          return this._Popup_getContentHtmlTpl()
        }
        var buff = []
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          buff[i] = item.renderHtml()
        }
        return '' + buff.join('') + '
'
      },
      _Popup_postRender: Popup.prototype.postRender,
      postRender: function () {
        var me = this
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          item.ownerMenu = this
          item.postRender()
        }
        domUtils.on(this.getDom(), 'mouseover', function (evt) {
          evt = evt || event
          var rel = evt.relatedTarget || evt.fromElement
          var el = me.getDom()
          if (!uiUtils.contains(el, rel) && el !== rel) {
            me.fireEvent('over')
          }
        })
        this._Popup_postRender()
      },
      queryAutoHide: function (el) {
        if (el) {
          if (uiUtils.contains(this.getDom(), el)) {
            return false
          }
          for (var i = 0; i < this.items.length; i++) {
            var item = this.items[i]
            if (item.queryAutoHide(el) === false) {
              return false
            }
          }
        }
      },
      clearItems: function () {
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          clearTimeout(item._showingTimer)
          clearTimeout(item._closingTimer)
          if (item.subMenu) {
            item.subMenu.destroy()
          }
        }
        this.items = []
      },
      destroy: function () {
        if (this.getDom()) {
          domUtils.remove(this.getDom())
        }
        this.clearItems()
      },
      dispose: function () {
        this.destroy()
      }
    }
    utils.inherits(Menu, Popup)
    /**
     * @update 2013/04/03 hancong03 新增一个参数menu, 该参数存储了menuItem所对应的menu引用
     * @type {Function}
     */
    var MenuItem = (baidu.editor.ui.MenuItem = function (options) {
      this.initOptions(options)
      this.initUIBase()
      this.Stateful_init()
      if (this.subMenu && !(this.subMenu instanceof Menu)) {
        if (options.className && options.className.indexOf('aligntd') != -1) {
          var me = this
          //获取单元格对齐初始状态
          this.subMenu.selected = this.editor.queryCommandValue('cellalignment')
          this.subMenu = new Popup({
            content: new CellAlignPicker(this.subMenu),
            parentMenu: me,
            editor: me.editor,
            destroy: function () {
              if (this.getDom()) {
                domUtils.remove(this.getDom())
              }
            }
          })
          this.subMenu.addListener('postRenderAfter', function () {
            domUtils.on(this.getDom(), 'mouseover', function () {
              me.addState('opened')
            })
          })
        } else {
          this.subMenu = new Menu(this.subMenu)
        }
      }
    })
    MenuItem.prototype = {
      label: '',
      subMenu: null,
      ownerMenu: null,
      uiName: 'menuitem',
      alwalysHoverable: true,
      getHtmlTpl: function () {
        return (
          '' +
          '
' +
          this.renderLabelHtml() +
          '
' +
          '
' +
          '
' +
          '' +
          (this.label || '') +
          '
'
        )
      },
      getStateDom: function () {
        return this.getDom()
      },
      queryAutoHide: function (el) {
        if (this.subMenu && this.hasState('opened')) {
          return this.subMenu.queryAutoHide(el)
        }
      },
      _onClick: function (event, this_) {
        if (this.hasState('disabled')) return
        if (this.fireEvent('click', event, this_) !== false) {
          if (this.subMenu) {
            this.showSubMenu()
          } else {
            Popup.postHide(event)
          }
        }
      },
      showSubMenu: function () {
        var rect = uiUtils.getClientRect(this.getDom())
        rect.right -= 5
        rect.left += 2
        rect.width -= 7
        rect.top -= 4
        rect.bottom += 4
        rect.height += 8
        this.subMenu.showAnchorRect(rect, true, true)
      },
      hideSubMenu: function () {
        this.subMenu.hide()
      }
    }
    utils.inherits(MenuItem, UIBase)
    utils.extend(MenuItem.prototype, Stateful, true)
  })()
  // ui/combox.js
  ///import core
  ///import uicore
  ///import ui/menu.js
  ///import ui/splitbutton.js
  ;(function () {
    // todo: menu和item提成通用list
    var utils = baidu.editor.utils,
      uiUtils = baidu.editor.ui.uiUtils,
      Menu = baidu.editor.ui.Menu,
      SplitButton = baidu.editor.ui.SplitButton,
      Combox = (baidu.editor.ui.Combox = function (options) {
        this.initOptions(options)
        this.initCombox()
      })
    Combox.prototype = {
      uiName: 'combox',
      onbuttonclick: function () {
        this.showPopup()
      },
      initCombox: function () {
        var me = this
        this.items = this.items || []
        for (var i = 0; i < this.items.length; i++) {
          var item = this.items[i]
          item.uiName = 'listitem'
          item.index = i
          item.onclick = function () {
            me.selectByIndex(this.index)
          }
        }
        this.popup = new Menu({
          items: this.items,
          uiName: 'list',
          editor: this.editor,
          captureWheel: true,
          combox: this
        })
        this.initSplitButton()
      },
      _SplitButton_postRender: SplitButton.prototype.postRender,
      postRender: function () {
        this._SplitButton_postRender()
        this.setLabel(this.label || '')
        this.setValue(this.initValue || '')
      },
      showPopup: function () {
        var rect = uiUtils.getClientRect(this.getDom())
        rect.top += 1
        rect.bottom -= 1
        rect.height -= 2
        this.popup.showAnchorRect(rect)
      },
      getValue: function () {
        return this.value
      },
      setValue: function (value) {
        var index = this.indexByValue(value)
        if (index != -1) {
          this.selectedIndex = index
          this.setLabel(this.items[index].label)
          this.value = this.items[index].value
        } else {
          this.selectedIndex = -1
          this.setLabel(this.getLabelForUnknowValue(value))
          this.value = value
        }
      },
      setLabel: function (label) {
        this.getDom('button_body').innerHTML = label
        this.label = label
      },
      getLabelForUnknowValue: function (value) {
        return value
      },
      indexByValue: function (value) {
        for (var i = 0; i < this.items.length; i++) {
          if (value == this.items[i].value) {
            return i
          }
        }
        return -1
      },
      getItem: function (index) {
        return this.items[index]
      },
      selectByIndex: function (index) {
        if (index < this.items.length && this.fireEvent('select', index) !== false) {
          this.selectedIndex = index
          this.value = this.items[index].value
          this.setLabel(this.items[index].label)
        }
      }
    }
    utils.inherits(Combox, SplitButton)
  })()
  // ui/dialog.js
  ///import core
  ///import uicore
  ///import ui/mask.js
  ///import ui/button.js
  ;(function () {
    var utils = baidu.editor.utils,
      domUtils = baidu.editor.dom.domUtils,
      uiUtils = baidu.editor.ui.uiUtils,
      Mask = baidu.editor.ui.Mask,
      UIBase = baidu.editor.ui.UIBase,
      Button = baidu.editor.ui.Button,
      Dialog = (baidu.editor.ui.Dialog = function (options) {
        if (options.name) {
          var name = options.name
          var cssRules = options.cssRules
          if (!options.className) {
            options.className = 'edui-for-' + name
          }
          if (cssRules) {
            options.cssRules =
              '.edui-default .edui-for-' + name + ' .edui-dialog-content  {' + cssRules + '}'
          }
        }
        this.initOptions(
          utils.extend(
            {
              autoReset: true,
              draggable: true,
              onok: function () {},
              oncancel: function () {},
              onclose: function (t, ok) {
                return ok ? this.onok() : this.oncancel()
              },
              //是否控制dialog中的scroll事件, 默认为不阻止
              holdScroll: false
            },
            options
          )
        )
        this.initDialog()
      })
    var modalMask
    var dragMask
    var activeDialog
    Dialog.prototype = {
      draggable: false,
      uiName: 'dialog',
      initDialog: function () {
        var me = this,
          theme = this.editor.options.theme
        if (this.cssRules) {
          utils.cssRule('edui-customize-' + this.name + '-style', this.cssRules)
        }
        this.initUIBase()
        this.modalMask =
          modalMask ||
          (modalMask = new Mask({
            className: 'edui-dialog-modalmask',
            theme: theme,
            onclick: function () {
              activeDialog && activeDialog.close(false)
            }
          }))
        this.dragMask =
          dragMask ||
          (dragMask = new Mask({
            className: 'edui-dialog-dragmask',
            theme: theme
          }))
        this.closeButton = new Button({
          className: 'edui-dialog-closebutton',
          title: me.closeDialog,
          theme: theme,
          onclick: function () {
            me.close(false)
          }
        })
        this.fullscreen && this.initResizeEvent()
        if (this.buttons) {
          for (var i = 0; i < this.buttons.length; i++) {
            if (!(this.buttons[i] instanceof Button)) {
              this.buttons[i] = new Button(
                utils.extend(
                  this.buttons[i],
                  {
                    editor: this.editor
                  },
                  true
                )
              )
            }
          }
        }
      },
      initResizeEvent: function () {
        var me = this
        domUtils.on(window, 'resize', function () {
          if (me._hidden || me._hidden === undefined) {
            return
          }
          if (me.__resizeTimer) {
            window.clearTimeout(me.__resizeTimer)
          }
          me.__resizeTimer = window.setTimeout(function () {
            me.__resizeTimer = null
            var dialogWrapNode = me.getDom(),
              contentNode = me.getDom('content'),
              wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
              contentRect = UE.ui.uiUtils.getClientRect(contentNode),
              vpRect = uiUtils.getViewportRect()
            contentNode.style.width = vpRect.width - wrapRect.width + contentRect.width + 'px'
            contentNode.style.height = vpRect.height - wrapRect.height + contentRect.height + 'px'
            dialogWrapNode.style.width = vpRect.width + 'px'
            dialogWrapNode.style.height = vpRect.height + 'px'
            me.fireEvent('resize')
          }, 100)
        })
      },
      fitSize: function () {
        var popBodyEl = this.getDom('body')
        //            if (!(baidu.editor.browser.ie && baidu.editor.browser.version == 7)) {
        //                uiUtils.removeStyle(popBodyEl, 'width');
        //                uiUtils.removeStyle(popBodyEl, 'height');
        //            }
        var size = this.mesureSize()
        popBodyEl.style.width = size.width + 'px'
        popBodyEl.style.height = size.height + 'px'
        return size
      },
      safeSetOffset: function (offset) {
        var me = this
        var el = me.getDom()
        var vpRect = uiUtils.getViewportRect()
        var rect = uiUtils.getClientRect(el)
        var left = offset.left
        if (left + rect.width > vpRect.right) {
          left = vpRect.right - rect.width
        }
        var top = offset.top
        if (top + rect.height > vpRect.bottom) {
          top = vpRect.bottom - rect.height
        }
        el.style.left = Math.max(left, 0) + 'px'
        el.style.top = Math.max(top, 0) + 'px'
      },
      showAtCenter: function () {
        var vpRect = uiUtils.getViewportRect()
        if (!this.fullscreen) {
          this.getDom().style.display = ''
          var popSize = this.fitSize()
          var titleHeight = this.getDom('titlebar').offsetHeight | 0
          var left = vpRect.width / 2 - popSize.width / 2
          var top = vpRect.height / 2 - (popSize.height - titleHeight) / 2 - titleHeight
          var popEl = this.getDom()
          this.safeSetOffset({
            left: Math.max(left | 0, 0),
            top: Math.max(top | 0, 0)
          })
          if (!domUtils.hasClass(popEl, 'edui-state-centered')) {
            popEl.className += ' edui-state-centered'
          }
        } else {
          var dialogWrapNode = this.getDom(),
            contentNode = this.getDom('content')
          dialogWrapNode.style.display = 'block'
          var wrapRect = UE.ui.uiUtils.getClientRect(dialogWrapNode),
            contentRect = UE.ui.uiUtils.getClientRect(contentNode)
          dialogWrapNode.style.left = '-100000px'
          contentNode.style.width = vpRect.width - wrapRect.width + contentRect.width + 'px'
          contentNode.style.height = vpRect.height - wrapRect.height + contentRect.height + 'px'
          dialogWrapNode.style.width = vpRect.width + 'px'
          dialogWrapNode.style.height = vpRect.height + 'px'
          dialogWrapNode.style.left = 0
          //保存环境的overflow值
          this._originalContext = {
            html: {
              overflowX: document.documentElement.style.overflowX,
              overflowY: document.documentElement.style.overflowY
            },
            body: {
              overflowX: document.body.style.overflowX,
              overflowY: document.body.style.overflowY
            }
          }
          document.documentElement.style.overflowX = 'hidden'
          document.documentElement.style.overflowY = 'hidden'
          document.body.style.overflowX = 'hidden'
          document.body.style.overflowY = 'hidden'
        }
        this._show()
      },
      getContentHtml: function () {
        var contentHtml = ''
        if (typeof this.content == 'string') {
          contentHtml = this.content
        } else if (this.iframeUrl) {
          contentHtml =
            '' +
          '
' +
          '
' +
          '
' +
          '' +
          (this.title || '') +
          ' ' +
          '
' +
          this.closeButton.renderHtml() +
          '
' +
          '
' +
          (this.autoReset ? '' : this.getContentHtml()) +
          '
' +
          footHtml +
          '
' +
          buff +
          '
'
        )
      }
    }
    utils.inherits(ShortCutMenu, UIBase)
    function hideAllMenu(e) {
      var tgt = e.target || e.srcElement,
        cur = domUtils.findParent(
          tgt,
          function (node) {
            return (
              domUtils.hasClass(node, 'edui-shortcutmenu') || domUtils.hasClass(node, 'edui-popup')
            )
          },
          true
        )
      if (!cur) {
        for (var i = 0, menu; (menu = allMenus[i++]); ) {
          menu.hide()
        }
      }
    }
    domUtils.on(document, 'mousedown', function (e) {
      hideAllMenu(e)
    })
    domUtils.on(window, 'scroll', function (e) {
      hideAllMenu(e)
    })
  })()
  // ui/breakline.js
  ;(function () {
    var utils = baidu.editor.utils,
      UIBase = baidu.editor.ui.UIBase,
      Breakline = (baidu.editor.ui.Breakline = function (options) {
        this.initOptions(options)
        this.initSeparator()
      })
    Breakline.prototype = {
      uiName: 'Breakline',
      initSeparator: function () {
        this.initUIBase()
      },
      getHtmlTpl: function () {
        return '' +
          ' 
×
' +
          ' 
' +
          ' 
' +
          ' 
' +
          ' 
' +
          '  
' +
          ' 
' +
          '
' + (this.label || '') + '
'
          }
        })
      })
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        onselect: function (t, index) {
          editor.execCommand('insertcode', this.items[index].value)
        },
        onbuttonclick: function () {
          this.showPopup()
        },
        title: title,
        initValue: title,
        className: 'edui-for-insertcode',
        indexByValue: function (value) {
          if (value) {
            for (var i = 0, ci; (ci = this.items[i]); i++) {
              if (ci.value.indexOf(value) != -1) return i
            }
          }
          return -1
        }
      })
      editorui.buttons['insertcode'] = ui
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('insertcode')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('insertcode')
            if (!value) {
              ui.setValue(title)
              return
            }
            //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
            value && (value = value.replace(/['"]/g, '').split(',')[0])
            ui.setValue(value)
          }
        }
      })
      return ui
    }
    editorui.fontfamily = function (editor, list, title) {
      list = editor.options['fontfamily'] || []
      title = editor.options.labelMap['fontfamily'] || editor.getLang('labelMap.fontfamily') || ''
      if (!list.length) return
      for (var i = 0, ci, items = []; (ci = list[i]); i++) {
        var langLabel = editor.getLang('fontfamily')[ci.name] || ''
        ;(function (key, val) {
          items.push({
            label: key,
            value: val,
            theme: editor.options.theme,
            renderLabelHtml: function () {
              return (
                '' +
                (this.label || '') +
                '
'
              )
            }
          })
        })(ci.label || langLabel, ci.val)
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        onselect: function (t, index) {
          editor.execCommand('FontFamily', this.items[index].value)
        },
        onbuttonclick: function () {
          this.showPopup()
        },
        title: title,
        initValue: title,
        className: 'edui-for-fontfamily',
        indexByValue: function (value) {
          if (value) {
            for (var i = 0, ci; (ci = this.items[i]); i++) {
              if (ci.value.indexOf(value) != -1) return i
            }
          }
          return -1
        }
      })
      editorui.buttons['fontfamily'] = ui
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('FontFamily')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('FontFamily')
            //trace:1871 ie下从源码模式切换回来时,字体会带单引号,而且会有逗号
            value && (value = value.replace(/['"]/g, '').split(',')[0])
            ui.setValue(value)
          }
        }
      })
      return ui
    }
    editorui.fontsize = function (editor, list, title) {
      title = editor.options.labelMap['fontsize'] || editor.getLang('labelMap.fontsize') || ''
      list = list || editor.options['fontsize'] || []
      if (!list.length) return
      var items = []
      for (var i = 0; i < list.length; i++) {
        var size = list[i] + 'px'
        items.push({
          label: size,
          value: size,
          theme: editor.options.theme,
          renderLabelHtml: function () {
            return (
              '' +
              (this.label || '') +
              '
'
            )
          }
        })
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        onselect: function (t, index) {
          editor.execCommand('FontSize', this.items[index].value)
        },
        onbuttonclick: function () {
          this.showPopup()
        },
        className: 'edui-for-fontsize'
      })
      editorui.buttons['fontsize'] = ui
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('FontSize')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            ui.setValue(editor.queryCommandValue('FontSize'))
          }
        }
      })
      return ui
    }
    editorui.paragraph = function (editor, list, title) {
      title = editor.options.labelMap['paragraph'] || editor.getLang('labelMap.paragraph') || ''
      list = editor.options['paragraph'] || []
      if (utils.isEmptyObject(list)) return
      var items = []
      for (var i in list) {
        items.push({
          value: i,
          label: list[i] || editor.getLang('paragraph')[i],
          theme: editor.options.theme,
          renderLabelHtml: function () {
            return (
              '' +
              (this.label || '') +
              ' 
'
            )
          }
        })
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        className: 'edui-for-paragraph',
        onselect: function (t, index) {
          editor.execCommand('Paragraph', this.items[index].value)
        },
        onbuttonclick: function () {
          this.showPopup()
        }
      })
      editorui.buttons['paragraph'] = ui
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('Paragraph')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('Paragraph')
            var index = ui.indexByValue(value)
            if (index != -1) {
              ui.setValue(value)
            } else {
              ui.setValue(ui.initValue)
            }
          }
        }
      })
      return ui
    }
    //自定义标题
    editorui.customstyle = function (editor) {
      var list = editor.options['customstyle'] || [],
        title =
          editor.options.labelMap['customstyle'] || editor.getLang('labelMap.customstyle') || ''
      if (!list.length) return
      var langCs = editor.getLang('customstyle')
      for (var i = 0, items = [], t; (t = list[i++]); ) {
        ;(function (t) {
          var ck = {}
          ck.label = t.label ? t.label : langCs[t.name]
          ck.style = t.style
          ck.className = t.className
          ck.tag = t.tag
          items.push({
            label: ck.label,
            value: ck,
            theme: editor.options.theme,
            renderLabelHtml: function () {
              return (
                '' +
                '<' +
                ck.tag +
                ' ' +
                (ck.className ? ' class="' + ck.className + '"' : '') +
                (ck.style ? ' style="' + ck.style + '"' : '') +
                '>' +
                ck.label +
                '' +
                ck.tag +
                '>' +
                '
'
              )
            }
          })
        })(t)
      }
      var ui = new editorui.Combox({
        editor: editor,
        items: items,
        title: title,
        initValue: title,
        className: 'edui-for-customstyle',
        onselect: function (t, index) {
          editor.execCommand('customstyle', this.items[index].value)
        },
        onbuttonclick: function () {
          this.showPopup()
        },
        indexByValue: function (value) {
          for (var i = 0, ti; (ti = this.items[i++]); ) {
            if (ti.label == value) {
              return i - 1
            }
          }
          return -1
        }
      })
      editorui.buttons['customstyle'] = ui
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        if (!uiReady) {
          var state = editor.queryCommandState('customstyle')
          if (state == -1) {
            ui.setDisabled(true)
          } else {
            ui.setDisabled(false)
            var value = editor.queryCommandValue('customstyle')
            var index = ui.indexByValue(value)
            if (index != -1) {
              ui.setValue(value)
            } else {
              ui.setValue(ui.initValue)
            }
          }
        }
      })
      return ui
    }
    editorui.inserttable = function (editor, iframeUrl, title) {
      title = editor.options.labelMap['inserttable'] || editor.getLang('labelMap.inserttable') || ''
      var ui = new editorui.TableButton({
        editor: editor,
        title: title,
        className: 'edui-for-inserttable',
        onpicktable: function (t, numCols, numRows) {
          editor.execCommand('InsertTable', {
            numRows: numRows,
            numCols: numCols,
            border: 1
          })
        },
        onbuttonclick: function () {
          this.showPopup()
        }
      })
      editorui.buttons['inserttable'] = ui
      editor.addListener('selectionchange', function () {
        ui.setDisabled(editor.queryCommandState('inserttable') == -1)
      })
      return ui
    }
    editorui.lineheight = function (editor) {
      var val = editor.options.lineheight || []
      if (!val.length) return
      for (var i = 0, ci, items = []; (ci = val[i++]); ) {
        items.push({
          //todo:写死了
          label: ci,
          value: ci,
          theme: editor.options.theme,
          onclick: function () {
            editor.execCommand('lineheight', this.value)
          }
        })
      }
      var ui = new editorui.MenuButton({
        editor: editor,
        className: 'edui-for-lineheight',
        title: editor.options.labelMap['lineheight'] || editor.getLang('labelMap.lineheight') || '',
        items: items,
        onbuttonclick: function () {
          var value = editor.queryCommandValue('LineHeight') || this.value
          editor.execCommand('LineHeight', value)
        }
      })
      editorui.buttons['lineheight'] = ui
      editor.addListener('selectionchange', function () {
        var state = editor.queryCommandState('LineHeight')
        if (state == -1) {
          ui.setDisabled(true)
        } else {
          ui.setDisabled(false)
          var value = editor.queryCommandValue('LineHeight')
          value && ui.setValue((value + '').replace(/cm/, ''))
          ui.setChecked(state)
        }
      })
      return ui
    }
    var rowspacings = ['top', 'bottom']
    for (var r = 0, ri; (ri = rowspacings[r++]); ) {
      ;(function (cmd) {
        editorui['rowspacing' + cmd] = function (editor) {
          var val = editor.options['rowspacing' + cmd] || []
          if (!val.length) return null
          for (var i = 0, ci, items = []; (ci = val[i++]); ) {
            items.push({
              label: ci,
              value: ci,
              theme: editor.options.theme,
              onclick: function () {
                editor.execCommand('rowspacing', this.value, cmd)
              }
            })
          }
          var ui = new editorui.MenuButton({
            editor: editor,
            className: 'edui-for-rowspacing' + cmd,
            title:
              editor.options.labelMap['rowspacing' + cmd] ||
              editor.getLang('labelMap.rowspacing' + cmd) ||
              '',
            items: items,
            onbuttonclick: function () {
              var value = editor.queryCommandValue('rowspacing', cmd) || this.value
              editor.execCommand('rowspacing', value, cmd)
            }
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function () {
            var state = editor.queryCommandState('rowspacing', cmd)
            if (state == -1) {
              ui.setDisabled(true)
            } else {
              ui.setDisabled(false)
              var value = editor.queryCommandValue('rowspacing', cmd)
              value && ui.setValue((value + '').replace(/%/, ''))
              ui.setChecked(state)
            }
          })
          return ui
        }
      })(ri)
    }
    //有序,无序列表
    var lists = ['insertorderedlist', 'insertunorderedlist']
    for (var l = 0, cl; (cl = lists[l++]); ) {
      ;(function (cmd) {
        editorui[cmd] = function (editor) {
          var vals = editor.options[cmd],
            _onMenuClick = function () {
              editor.execCommand(cmd, this.value)
            },
            items = []
          for (var i in vals) {
            items.push({
              label: vals[i] || editor.getLang()[cmd][i] || '',
              value: i,
              theme: editor.options.theme,
              onclick: _onMenuClick
            })
          }
          var ui = new editorui.MenuButton({
            editor: editor,
            className: 'edui-for-' + cmd,
            title: editor.getLang('labelMap.' + cmd) || '',
            items: items,
            onbuttonclick: function () {
              var value = editor.queryCommandValue(cmd) || this.value
              editor.execCommand(cmd, value)
            }
          })
          editorui.buttons[cmd] = ui
          editor.addListener('selectionchange', function () {
            var state = editor.queryCommandState(cmd)
            if (state == -1) {
              ui.setDisabled(true)
            } else {
              ui.setDisabled(false)
              var value = editor.queryCommandValue(cmd)
              ui.setValue(value)
              ui.setChecked(state)
            }
          })
          return ui
        }
      })(cl)
    }
    editorui.fullscreen = function (editor, title) {
      title = editor.options.labelMap['fullscreen'] || editor.getLang('labelMap.fullscreen') || ''
      var ui = new editorui.Button({
        className: 'edui-for-fullscreen',
        title: title,
        theme: editor.options.theme,
        onclick: function () {
          if (editor.ui) {
            editor.ui.setFullScreen(!editor.ui.isFullScreen())
          }
          this.setChecked(editor.ui.isFullScreen())
        }
      })
      editorui.buttons['fullscreen'] = ui
      editor.addListener('selectionchange', function () {
        var state = editor.queryCommandState('fullscreen')
        ui.setDisabled(state == -1)
        ui.setChecked(editor.ui.isFullScreen())
      })
      return ui
    }
    // 表情
    editorui['emotion'] = function (editor, iframeUrl) {
      var cmd = 'emotion'
      var ui = new editorui.MultiMenuPop({
        title: editor.options.labelMap[cmd] || editor.getLang('labelMap.' + cmd + '') || '',
        editor: editor,
        className: 'edui-for-' + cmd,
        iframeUrl: editor.ui.mapUrl(
          iframeUrl || (editor.options.iframeUrlMap || {})[cmd] || iframeUrlMap[cmd]
        )
      })
      editorui.buttons[cmd] = ui
      editor.addListener('selectionchange', function () {
        ui.setDisabled(editor.queryCommandState(cmd) == -1)
      })
      return ui
    }
    editorui.autotypeset = function (editor) {
      var ui = new editorui.AutoTypeSetButton({
        editor: editor,
        title:
          editor.options.labelMap['autotypeset'] || editor.getLang('labelMap.autotypeset') || '',
        className: 'edui-for-autotypeset',
        onbuttonclick: function () {
          editor.execCommand('autotypeset')
        }
      })
      editorui.buttons['autotypeset'] = ui
      editor.addListener('selectionchange', function () {
        ui.setDisabled(editor.queryCommandState('autotypeset') == -1)
      })
      return ui
    }
    /* 简单上传插件 */
    editorui['simpleupload'] = function (editor) {
      var name = 'simpleupload',
        ui = new editorui.Button({
          className: 'edui-for-' + name,
          title: editor.options.labelMap[name] || editor.getLang('labelMap.' + name) || '',
          onclick: function () {},
          theme: editor.options.theme,
          showText: false
        })
      editorui.buttons[name] = ui
      editor.addListener('ready', function () {
        var b = ui.getDom('body'),
          iconSpan = b.children[0]
        editor.fireEvent('simpleuploadbtnready', iconSpan)
      })
      editor.addListener('selectionchange', function (type, causeByUi, uiReady) {
        var state = editor.queryCommandState(name)
        if (state == -1) {
          ui.setDisabled(true)
          ui.setChecked(false)
        } else {
          if (!uiReady) {
            ui.setDisabled(false)
            ui.setChecked(state)
          }
        }
      })
      return ui
    }
  })()
  // adapter/editor.js
  ///import core
  ///commands 全屏
  ///commandsName FullScreen
  ///commandsTitle  全屏
  ;(function () {
    var utils = baidu.editor.utils,
      uiUtils = baidu.editor.ui.uiUtils,
      UIBase = baidu.editor.ui.UIBase,
      domUtils = baidu.editor.dom.domUtils
    var nodeStack = []
    function EditorUI(options) {
      this.initOptions(options)
      this.initEditorUI()
    }
    EditorUI.prototype = {
      uiName: 'editor',
      initEditorUI: function () {
        this.editor.ui = this
        this._dialogs = {}
        this.initUIBase()
        this._initToolbars()
        var editor = this.editor,
          me = this
        editor.addListener('ready', function () {
          //提供getDialog方法
          editor.getDialog = function (name) {
            return editor.ui._dialogs[name + 'Dialog']
          }
          domUtils.on(editor.window, 'scroll', function (evt) {
            baidu.editor.ui.Popup.postHide(evt)
          })
          //提供编辑器实时宽高(全屏时宽高不变化)
          editor.ui._actualFrameWidth = editor.options.initialFrameWidth
          UE.browser.ie &&
            UE.browser.version === 6 &&
            editor.container.ownerDocument.execCommand('BackgroundImageCache', false, true)
          //display bottom-bar label based on config
          if (editor.options.elementPathEnabled && editor.ui.getDom('elementpath')) {
            editor.ui.getDom('elementpath').innerHTML =
              '' + editor.getLang('elementPathTip') + ':
'
          }
          if (editor.options.wordCount) {
            function countFn() {
              setCount(editor, me)
              domUtils.un(editor.document, 'click', arguments.callee)
            }
            domUtils.on(editor.document, 'click', countFn)
            editor.ui.getDom('wordcount').innerHTML = editor.getLang('wordCountTip')
          }
          editor.ui._scale()
          if (editor.options.scaleEnabled) {
            if (editor.autoHeightEnabled) {
              editor.disableAutoHeight()
            }
            me.enableScale()
          } else {
            me.disableScale()
          }
          if (
            !editor.options.elementPathEnabled &&
            !editor.options.wordCount &&
            !editor.options.scaleEnabled &&
            editor.ui.getDom('elementpath')
          ) {
            editor.ui.getDom('elementpath').style.display = 'none'
            editor.ui.getDom('wordcount').style.display = 'none'
            editor.ui.getDom('scale').style.display = 'none'
          }
          if (!editor.selection.isFocus()) return
          editor.fireEvent('selectionchange', false, true)
        })
        editor.addListener('mousedown', function (t, evt) {
          var el = evt.target || evt.srcElement
          baidu.editor.ui.Popup.postHide(evt, el)
          baidu.editor.ui.ShortCutMenu.postHide(evt)
        })
        editor.addListener('delcells', function () {
          if (UE.ui['edittip']) {
            new UE.ui['edittip'](editor)
          }
          editor.getDialog('edittip').open()
        })
        var pastePop,
          isPaste = false,
          timer
        editor.addListener('afterpaste', function () {
          if (editor.queryCommandState('pasteplain')) return
          if (baidu.editor.ui.PastePicker) {
            pastePop = new baidu.editor.ui.Popup({
              content: new baidu.editor.ui.PastePicker({ editor: editor }),
              editor: editor,
              className: 'edui-wordpastepop'
            })
            pastePop.render()
          }
          isPaste = true
        })
        editor.addListener('afterinserthtml', function () {
          clearTimeout(timer)
          timer = setTimeout(function () {
            if (pastePop && (isPaste || editor.ui._isTransfer)) {
              if (pastePop.isHidden()) {
                var span = domUtils.createElement(editor.document, 'span', {
                    style: 'line-height:0px;',
                    innerHTML: '\ufeff'
                  }),
                  range = editor.selection.getRange()
                range.insertNode(span)
                var tmp = getDomNode(span, 'firstChild', 'previousSibling')
                tmp && pastePop.showAnchor(tmp.nodeType == 3 ? tmp.parentNode : tmp)
                domUtils.remove(span)
              } else {
                pastePop.show()
              }
              delete editor.ui._isTransfer
              isPaste = false
            }
          }, 200)
        })
        editor.addListener('contextmenu', function (t, evt) {
          baidu.editor.ui.Popup.postHide(evt)
        })
        editor.addListener('keydown', function (t, evt) {
          if (pastePop) pastePop.dispose(evt)
          var keyCode = evt.keyCode || evt.which
          if (evt.altKey && keyCode == 90) {
            UE.ui.buttons['fullscreen'].onclick()
          }
        })
        editor.addListener('wordcount', function (type) {
          setCount(this, me)
        })
        function setCount(editor, ui) {
          editor.setOpt({
            wordCount: true,
            maximumWords: 10000,
            wordCountMsg: editor.options.wordCountMsg || editor.getLang('wordCountMsg'),
            wordOverFlowMsg: editor.options.wordOverFlowMsg || editor.getLang('wordOverFlowMsg')
          })
          var opt = editor.options,
            max = opt.maximumWords,
            msg = opt.wordCountMsg,
            errMsg = opt.wordOverFlowMsg,
            countDom = ui.getDom('wordcount')
          if (!opt.wordCount) {
            return
          }
          var count = editor.getContentLength(true)
          if (count > max) {
            countDom.innerHTML = errMsg
            editor.fireEvent('wordcountoverflow')
          } else {
            countDom.innerHTML = msg.replace('{#leave}', max - count).replace('{#count}', count)
          }
        }
        editor.addListener('selectionchange', function () {
          if (editor.options.elementPathEnabled) {
            me[(editor.queryCommandState('elementpath') == -1 ? 'dis' : 'en') + 'ableElementPath']()
          }
          if (editor.options.scaleEnabled) {
            me[(editor.queryCommandState('scale') == -1 ? 'dis' : 'en') + 'ableScale']()
          }
        })
        var popup = new baidu.editor.ui.Popup({
          editor: editor,
          content: '',
          className: 'edui-bubble',
          _onEditButtonClick: function () {
            this.hide()
            editor.ui._dialogs.linkDialog.open()
          },
          _onImgEditButtonClick: function (name) {
            this.hide()
            editor.ui._dialogs[name] && editor.ui._dialogs[name].open()
          },
          _onImgSetFloat: function (value) {
            this.hide()
            editor.execCommand('imagefloat', value)
          },
          _setIframeAlign: function (value) {
            var frame = popup.anchorEl
            var newFrame = frame.cloneNode(true)
            switch (value) {
              case -2:
                newFrame.setAttribute('align', '')
                break
              case -1:
                newFrame.setAttribute('align', 'left')
                break
              case 1:
                newFrame.setAttribute('align', 'right')
                break
            }
            frame.parentNode.insertBefore(newFrame, frame)
            domUtils.remove(frame)
            popup.anchorEl = newFrame
            popup.showAnchor(popup.anchorEl)
          },
          _updateIframe: function () {
            var frame = (editor._iframe = popup.anchorEl)
            if (domUtils.hasClass(frame, 'ueditor_baidumap')) {
              editor.selection.getRange().selectNode(frame).select()
              editor.ui._dialogs.mapDialog.open()
              popup.hide()
            } else {
              editor.ui._dialogs.insertframeDialog.open()
              popup.hide()
            }
          },
          _onRemoveButtonClick: function (cmdName) {
            editor.execCommand(cmdName)
            this.hide()
          },
          queryAutoHide: function (el) {
            if (el && el.ownerDocument == editor.document) {
              if (
                el.tagName.toLowerCase() == 'img' ||
                domUtils.findParentByTagName(el, 'a', true)
              ) {
                return el !== popup.anchorEl
              }
            }
            return baidu.editor.ui.Popup.prototype.queryAutoHide.call(this, el)
          }
        })
        popup.render()
        if (editor.options.imagePopup) {
          editor.addListener('mouseover', function (t, evt) {
            evt = evt || window.event
            var el = evt.target || evt.srcElement
            if (editor.ui._dialogs.insertframeDialog && /iframe/gi.test(el.tagName)) {
              var html = popup.formatHtml(
                '' +
                  editor.getLang('property') +
                  ': ' +
                  editor.getLang('default') +
                  '   ' +
                  editor.getLang('justifyleft') +
                  '   ' +
                  editor.getLang('justifyright') +
                  '   ' +
                  ' ' +
                  editor.getLang('modify') +
                  '  '
              )
              if (html) {
                popup.getDom('content').innerHTML = html
                popup.anchorEl = el
                popup.showAnchor(popup.anchorEl)
              } else {
                popup.hide()
              }
            }
          })
          editor.addListener('selectionchange', function (t, causeByUi) {
            if (!causeByUi) return
            var html = '',
              str = '',
              img = editor.selection.getRange().getClosedNode(),
              dialogs = editor.ui._dialogs
            if (img && img.tagName == 'IMG') {
              var dialogName = 'insertimageDialog'
              if (
                img.className.indexOf('edui-faked-video') != -1 ||
                img.className.indexOf('edui-upload-video') != -1
              ) {
                dialogName = 'insertvideoDialog'
              }
              if (img.className.indexOf('edui-faked-webapp') != -1) {
                dialogName = 'webappDialog'
              }
              if (img.src.indexOf('http://api.map.baidu.com') != -1) {
                dialogName = 'mapDialog'
              }
              if (img.className.indexOf('edui-faked-music') != -1) {
                dialogName = 'musicDialog'
              }
              if (img.src.indexOf('http://maps.google.com/maps/api/staticmap') != -1) {
                dialogName = 'gmapDialog'
              }
              if (img.getAttribute('anchorname')) {
                dialogName = 'anchorDialog'
                html = popup.formatHtml(
                  '' +
                    editor.getLang('property') +
                    ': ' +
                    editor.getLang('modify') +
                    '   ' +
                    '' +
                    editor.getLang('delete') +
                    '  '
                )
              }
              if (img.getAttribute('word_img')) {
                //todo 放到dialog去做查询
                editor.word_img = [img.getAttribute('word_img')]
                dialogName = 'wordimageDialog'
              }
              if (
                domUtils.hasClass(img, 'loadingclass') ||
                domUtils.hasClass(img, 'loaderrorclass')
              ) {
                dialogName = ''
              }
              if (!dialogs[dialogName]) {
                return
              }
              str =
                '' +
                editor.getLang('property') +
                ': ' +
                '' +
                editor.getLang('default') +
                '   ' +
                '' +
                editor.getLang('justifyleft') +
                '   ' +
                '' +
                editor.getLang('justifyright') +
                '   ' +
                '' +
                editor.getLang('justifycenter') +
                '   ' +
                '' +
                editor.getLang('modify') +
                '  '
              !html && (html = popup.formatHtml(str))
            }
            if (editor.ui._dialogs.linkDialog) {
              var link = editor.queryCommandValue('link')
              var url
              if (link && (url = link.getAttribute('_href') || link.getAttribute('href', 2))) {
                var txt = url
                if (url.length > 30) {
                  txt = url.substring(0, 20) + '...'
                }
                if (html) {
                  html += '
'
                }
                html += popup.formatHtml(
                  '' +
                    editor.getLang('anthorMsg') +
                    ': ' +
                    txt +
                    ' ' +
                    ' ' +
                    editor.getLang('modify') +
                    ' ' +
                    '  ' +
                    editor.getLang('clear') +
                    '  '
                )
                popup.showAnchor(link)
              }
            }
            if (html) {
              popup.getDom('content').innerHTML = html
              popup.anchorEl = img || link
              popup.showAnchor(popup.anchorEl)
            } else {
              popup.hide()
            }
          })
        }
      },
      _initToolbars: function () {
        var editor = this.editor
        var toolbars = this.toolbars || []
        var toolbarUis = []
        for (var i = 0; i < toolbars.length; i++) {
          var toolbar = toolbars[i]
          var toolbarUi = new baidu.editor.ui.Toolbar({
            theme: editor.options.theme
          })
          for (var j = 0; j < toolbar.length; j++) {
            var toolbarItem = toolbar[j]
            var toolbarItemUi = null
            if (typeof toolbarItem == 'string') {
              toolbarItem = toolbarItem.toLowerCase()
              if (toolbarItem == '|') {
                toolbarItem = 'Separator'
              }
              if (toolbarItem == '||') {
                toolbarItem = 'Breakline'
              }
              if (baidu.editor.ui[toolbarItem]) {
                toolbarItemUi = new baidu.editor.ui[toolbarItem](editor)
              }
              //fullscreen这里单独处理一下,放到首行去
              if (toolbarItem == 'fullscreen') {
                if (toolbarUis && toolbarUis[0]) {
                  toolbarUis[0].items.splice(0, 0, toolbarItemUi)
                } else {
                  toolbarItemUi && toolbarUi.items.splice(0, 0, toolbarItemUi)
                }
                continue
              }
            } else {
              toolbarItemUi = toolbarItem
            }
            if (toolbarItemUi && toolbarItemUi.id) {
              toolbarUi.add(toolbarItemUi)
            }
          }
          toolbarUis[i] = toolbarUi
        }
        //接受外部定制的UI(修复因 utils.each 无法准确的循环出对象的全部元素而导致的自定义 UI 不符合预期的 BUG by HaoChuan9421)
        // utils.each(UE._customizeUI,function(obj,key){
        //     var itemUI,index;
        //     if(obj.id && obj.id != editor.key){
        //        return false;
        //     }
        //     itemUI = obj.execFn.call(editor,editor,key);
        //     if(itemUI){
        //         index = obj.index;
        //         if(index === undefined){
        //             index = toolbarUi.items.length;
        //         }
        //         toolbarUi.add(itemUI,index)
        //     }
        // });
        for (var key in UE._customizeUI) {
          var obj = UE._customizeUI[key]
          var itemUI, index
          if (!obj.id || obj.id == editor.key) {
            itemUI = obj.execFn.call(editor, editor, key)
            if (itemUI) {
              index = obj.index
              if (index === undefined) {
                index = toolbarUi.items.length
              }
              toolbarUi.add(itemUI, index)
            }
          }
        }
        this.toolbars = toolbarUis
      },
      getHtmlTpl: function () {
        return (
          '' +
          '
' +
          '
' +
          '
' +
          //modify wdcount by matao
          // '
' +
          // '
 
' +
          '
' +
          '
' +
                ci +
                ' '
            )
          }
          bottom.innerHTML =
            '' +
            this.editor.getLang('elementPathTip') +
            ': ' +
            buff.join(' > ') +
            '
'
        } else {
          bottom.style.display = 'none'
        }
      },
      disableElementPath: function () {
        var bottom = this.getDom('elementpath')
        bottom.innerHTML = ''
        bottom.style.display = 'none'
        this.elementPathEnabled = false
      },
      enableElementPath: function () {
        var bottom = this.getDom('elementpath')
        bottom.style.display = ''
        this.elementPathEnabled = true
        this._updateElementPath()
      },
      _scale: function () {
        var doc = document,
          editor = this.editor,
          editorHolder = editor.container,
          editorDocument = editor.document,
          toolbarBox = this.getDom('toolbarbox'),
          bottombar = this.getDom('bottombar'),
          scale = this.getDom('scale'),
          scalelayer = this.getDom('scalelayer')
        var isMouseMove = false,
          position = null,
          minEditorHeight = 0,
          minEditorWidth = editor.options.minFrameWidth,
          pageX = 0,
          pageY = 0,
          scaleWidth = 0,
          scaleHeight = 0
        function down() {
          position = domUtils.getXY(editorHolder)
          if (!minEditorHeight) {
            minEditorHeight =
              editor.options.minFrameHeight + toolbarBox.offsetHeight + bottombar.offsetHeight
          }
          scalelayer.style.cssText =
            'position:absolute;left:0;display:;top:0;background-color:#41ABFF;opacity:0.4;filter: Alpha(opacity=40);width:' +
            editorHolder.offsetWidth +
            'px;height:' +
            editorHolder.offsetHeight +
            'px;z-index:' +
            (editor.options.zIndex + 1)
          domUtils.on(doc, 'mousemove', move)
          domUtils.on(editorDocument, 'mouseup', up)
          domUtils.on(doc, 'mouseup', up)
        }
        var me = this
        //by xuheng 全屏时关掉缩放
        this.editor.addListener('fullscreenchanged', function (e, fullScreen) {
          if (fullScreen) {
            me.disableScale()
          } else {
            if (me.editor.options.scaleEnabled) {
              me.enableScale()
              var tmpNode = me.editor.document.createElement('span')
              me.editor.body.appendChild(tmpNode)
              me.editor.body.style.height =
                Math.max(domUtils.getXY(tmpNode).y, me.editor.iframe.offsetHeight - 20) + 'px'
              domUtils.remove(tmpNode)
            }
          }
        })
        function move(event) {
          clearSelection()
          var e = event || window.event
          pageX = e.pageX || doc.documentElement.scrollLeft + e.clientX
          pageY = e.pageY || doc.documentElement.scrollTop + e.clientY
          scaleWidth = pageX - position.x
          scaleHeight = pageY - position.y
          if (scaleWidth >= minEditorWidth) {
            isMouseMove = true
            scalelayer.style.width = scaleWidth + 'px'
          }
          if (scaleHeight >= minEditorHeight) {
            isMouseMove = true
            scalelayer.style.height = scaleHeight + 'px'
          }
        }
        function up() {
          if (isMouseMove) {
            isMouseMove = false
            editor.ui._actualFrameWidth = scalelayer.offsetWidth - 2
            editorHolder.style.width = editor.ui._actualFrameWidth + 'px'
            editor.setHeight(
              scalelayer.offsetHeight - bottombar.offsetHeight - toolbarBox.offsetHeight - 2,
              true
            )
          }
          if (scalelayer) {
            scalelayer.style.display = 'none'
          }
          clearSelection()
          domUtils.un(doc, 'mousemove', move)
          domUtils.un(editorDocument, 'mouseup', up)
          domUtils.un(doc, 'mouseup', up)
        }
        function clearSelection() {
          if (browser.ie) doc.selection.clear()
          else window.getSelection().removeAllRanges()
        }
        this.enableScale = function () {
          //trace:2868
          if (editor.queryCommandState('source') == 1) return
          if (scale && scale.style) {
            scale.style.display = ''
          }
          this.scaleEnabled = true
          domUtils.on(scale, 'mousedown', down)
        }
        this.disableScale = function () {
          if (scale && scale.style) {
            scale.style.display = 'none'
          }
          this.scaleEnabled = false
          domUtils.un(scale, 'mousedown', down)
        }
      },
      isFullScreen: function () {
        return this._fullscreen
      },
      postRender: function () {
        UIBase.prototype.postRender.call(this)
        for (var i = 0; i < this.toolbars.length; i++) {
          this.toolbars[i].postRender()
        }
        var me = this
        var timerId,
          domUtils = baidu.editor.dom.domUtils,
          updateFullScreenTime = function () {
            clearTimeout(timerId)
            timerId = setTimeout(function () {
              me._updateFullScreen()
            })
          }
        domUtils.on(window, 'resize', updateFullScreenTime)
        me.addListener('destroy', function () {
          domUtils.un(window, 'resize', updateFullScreenTime)
          clearTimeout(timerId)
        })
      },
      showToolbarMsg: function (msg, flag) {
        this.getDom('toolbarmsg_label').innerHTML = msg
        this.getDom('toolbarmsg').style.display = ''
        //
        if (!flag) {
          var w = this.getDom('upload_dialog')
          w.style.display = 'none'
        }
      },
      hideToolbarMsg: function () {
        this.getDom('toolbarmsg').style.display = 'none'
      },
      mapUrl: function (url) {
        return url ? url.replace('~/', this.editor.options.UEDITOR_HOME_URL || '') : ''
      },
      triggerLayout: function () {
        var dom = this.getDom()
        if (dom.style.zoom == '1') {
          dom.style.zoom = '100%'
        } else {
          dom.style.zoom = '1'
        }
      }
    }
    utils.inherits(EditorUI, baidu.editor.ui.UIBase)
    var instances = {}
    UE.ui.Editor = function (options) {
      var editor = new UE.Editor(options)
      editor.options.editor = editor
      utils.loadFile(document, {
        href: editor.options.themePath + editor.options.theme + '/css/ueditor.css',
        tag: 'link',
        type: 'text/css',
        rel: 'stylesheet'
      })
      var oldRender = editor.render
      editor.render = function (holder) {
        if (holder.constructor === String) {
          editor.key = holder
          instances[holder] = editor
        }
        utils.domReady(function () {
          editor.langIsReady ? renderUI() : editor.addListener('langReady', renderUI)
          function renderUI() {
            editor.setOpt({
              labelMap: editor.options.labelMap || editor.getLang('labelMap')
            })
            new EditorUI(editor.options)
            if (holder) {
              if (holder.constructor === String) {
                holder = document.getElementById(holder)
              }
              holder &&
                holder.getAttribute('name') &&
                (editor.options.textarea = holder.getAttribute('name'))
              if (holder && /script|textarea/gi.test(holder.tagName)) {
                var newDiv = document.createElement('div')
                holder.parentNode.insertBefore(newDiv, holder)
                var cont = holder.value || holder.innerHTML
                editor.options.initialContent = /^[\t\r\n ]*$/.test(cont)
                  ? editor.options.initialContent
                  : cont
                      .replace(/>[\n\r\t]+([ ]{4})+/g, '>')
                      .replace(/[\n\r\t]+([ ]{4})+[\n\r\t]+<')
                holder.className && (newDiv.className = holder.className)
                holder.style.cssText && (newDiv.style.cssText = holder.style.cssText)
                if (/textarea/i.test(holder.tagName)) {
                  editor.textarea = holder
                  editor.textarea.style.display = 'none'
                } else {
                  holder.parentNode.removeChild(holder)
                }
                if (holder.id) {
                  newDiv.id = holder.id
                  domUtils.removeAttributes(holder, 'id')
                }
                holder = newDiv
                holder.innerHTML = ''
              }
            }
            domUtils.addClass(holder, 'edui-' + editor.options.theme)
            editor.ui.render(holder)
            var opt = editor.options
            //给实例添加一个编辑器的容器引用
            editor.container = editor.ui.getDom()
            var parents = domUtils.findParents(holder, true)
            var displays = []
            for (var i = 0, ci; (ci = parents[i]); i++) {
              displays[i] = ci.style.display
              ci.style.display = 'block'
            }
            if (opt.initialFrameWidth) {
              opt.minFrameWidth = opt.initialFrameWidth
            } else {
              opt.minFrameWidth = opt.initialFrameWidth = holder.offsetWidth
              var styleWidth = holder.style.width
              if (/%$/.test(styleWidth)) {
                opt.initialFrameWidth = styleWidth
              }
            }
            if (opt.initialFrameHeight) {
              opt.minFrameHeight = opt.initialFrameHeight
            } else {
              opt.initialFrameHeight = opt.minFrameHeight = holder.offsetHeight
            }
            for (var i = 0, ci; (ci = parents[i]); i++) {
              ci.style.display = displays[i]
            }
            //编辑器最外容器设置了高度,会导致,编辑器不占位
            //todo 先去掉,没有找到原因
            if (holder.style.height) {
              holder.style.height = ''
            }
            editor.container.style.width =
              opt.initialFrameWidth + (/%$/.test(opt.initialFrameWidth) ? '' : 'px')
            editor.container.style.zIndex = opt.zIndex
            oldRender.call(editor, editor.ui.getDom('iframeholder'))
            editor.fireEvent('afteruiready')
          }
        })
      }
      return editor
    }
    /**
     * @file
     * @name UE
     * @short UE
     * @desc UEditor的顶部命名空间
     */
    /**
     * @name getEditor
     * @since 1.2.4+
     * @grammar UE.getEditor(id,[opt])  =>  Editor实例
     * @desc 提供一个全局的方法得到编辑器实例
     *
     * * ''id''  放置编辑器的容器id, 如果容器下的编辑器已经存在,就直接返回
     * * ''opt'' 编辑器的可选参数
     * @example
     *  UE.getEditor('containerId',{onready:function(){//创建一个编辑器实例
     *      this.setContent('hello')
     *  }});
     *  UE.getEditor('containerId'); //返回刚创建的实例
     *
     */
    UE.getEditor = function (id, opt) {
      var editor = instances[id]
      if (!editor) {
        editor = instances[id] = new UE.ui.Editor(opt)
        editor.render(id)
      }
      return editor
    }
    UE.delEditor = function (id) {
      var editor
      if ((editor = instances[id])) {
        editor.key && editor.destroy()
        delete instances[id]
      }
    }
    UE.registerUI = function (uiName, fn, index, editorId) {
      utils.each(uiName.split(/\s+/), function (name) {
        UE._customizeUI[name] = {
          id: editorId,
          execFn: fn,
          index: index
        }
      })
    }
  })()
  // adapter/message.js
  UE.registerUI('message', function (editor) {
    var editorui = baidu.editor.ui
    var Message = editorui.Message
    var holder
    var _messageItems = []
    var me = editor
    me.addListener('ready', function () {
      holder = document.getElementById(me.ui.id + '_message_holder')
      updateHolderPos()
      // HaoChuan9421
      // setTimeout(function(){
      //     updateHolderPos();
      // }, 500);
    })
    me.addListener('showmessage', function (type, opt) {
      opt = utils.isString(opt)
        ? {
            content: opt
          }
        : opt
      var message = new Message({
          timeout: opt.timeout,
          type: opt.type,
          content: opt.content,
          keepshow: opt.keepshow,
          editor: me
        }),
        mid = opt.id || 'msg_' + (+new Date()).toString(36)
      message.render(holder)
      _messageItems[mid] = message
      message.reset(opt)
      updateHolderPos()
      return mid
    })
    me.addListener('updatemessage', function (type, id, opt) {
      opt = utils.isString(opt)
        ? {
            content: opt
          }
        : opt
      var message = _messageItems[id]
      message.render(holder)
      message && message.reset(opt)
    })
    me.addListener('hidemessage', function (type, id) {
      var message = _messageItems[id]
      message && message.hide()
    })
    function updateHolderPos() {
      var toolbarbox = me.ui.getDom('toolbarbox')
      if (toolbarbox) {
        holder.style.top = toolbarbox.offsetHeight + 3 + 'px'
      }
      holder.style.zIndex = Math.max(me.options.zIndex, me.iframe.style.zIndex) + 1
    }
  })
  // adapter/autosave.js
  UE.registerUI('autosave', function (editor) {
    var timer = null,
      uid = null
    editor.on('afterautosave', function () {
      clearTimeout(timer)
      timer = setTimeout(function () {
        if (uid) {
          editor.trigger('hidemessage', uid)
        }
        uid = editor.trigger('showmessage', {
          content: editor.getLang('autosave.success'),
          timeout: 2000
        })
      }, 2000)
    })
  })
})()