text: (function (factory) {
      if (typeof window.define === 'function') {
          if (window.define.amd) {
              // AMD模式
              window.define('wangEditor', ["jquery"], factory);
          } else if (window.define.cmd) {
              // CMD模式
              window.define(function (require, exports, module) {
                  return factory;
              });
          } else {
              // 全局模式
              factory(window.jQuery);
          }
      } else if (typeof module === "object" && typeof module.exports === "object") {
          // commonjs
  
          // 引用 css —— webapck
          window.wangEditorCssPath ? require(window.wangEditorCssPath) : require('../css/wangEditor.css');
          module.exports = factory(
              // 传入 jquery ，支持使用 npm 方式或者自己定义jquery的路径
              window.wangEditorJQueryPath ? require(window.wangEditorJQueryPath) : require('jquery')
          );
      } else {
          // 全局模式
          factory(window.jQuery);
      }
  })(function($){
      
      // 验证是否引用jquery
      if (!$ || !$.fn || !$.fn.jquery) {
          alert('在引用wangEditor.js之前，先引用jQuery，否则无法使用 wangEditor');
          return;
      }
  
      // 定义扩展函数
      var _e = function (fn) {
          var E = window.wangEditor;
          if (E) {
              // 执行传入的函数
              fn(E, $);
          }
      };
  // 定义构造函数
  (function (window, $) {
      if (window.wangEditor) {
          // 重复引用
          alert('一个页面不能重复引用 wangEditor.js 或 wangEditor.min.js ！！！');
          return;
      }
  
      // 编辑器（整体）构造函数
      var E = function (elem) {
          // 支持 id 和 element 两种形式
          if (typeof elem === 'string') {
              elem = '#' + elem;
          }
  
          // ---------------获取基本节点------------------
          var $elem = $(elem);
          if ($elem.length !== 1) {
              return;
          }
          var nodeName = $elem[0].nodeName;
          if (nodeName !== 'TEXTAREA' && nodeName !== 'DIV') {
              // 只能是 textarea 和 div ，其他类型的元素不行
              return;   
          }
          this.valueNodeName = nodeName.toLowerCase();
          this.$valueContainer = $elem;
  
          // 记录 elem 的 prev 和 parent（最后渲染 editor 要用到）
          this.$prev = $elem.prev();
          this.$parent = $elem.parent();
  
          // ------------------初始化------------------
          this.init();
      };
  
      E.fn = E.prototype;
  
      E.$body = $('body');
      E.$document = $(document);
      E.$window = $(window);
      E.userAgent = navigator.userAgent;
      E.getComputedStyle = window.getComputedStyle;
      E.w3cRange = typeof document.createRange === 'function';
      E.hostname = location.hostname.toLowerCase();
      E.websiteHost = 'wangeditor.github.io';
      E.isOnWebsite = E.hostname === E.websiteHost;
      E.docsite = 'http://www.kancloud.cn/wangfupeng/wangeditor2/113961';
  
      // 暴露给全局对象
      window.wangEditor = E;
  
      // 注册 plugin 事件，用于用户自定义插件
      // 用户在引用 wangEditor.js 之后，还可以通过 E.plugin() 注入自定义函数，
      // 该函数将会在 editor.create() 方法的最后一步执行
      E.plugin = function (fn) {
          if (!E._plugins) {
              E._plugins = [];
          }
  
          if (typeof fn === 'function') {
              E._plugins.push(fn);
          }
      };
  
  })(window, $);
  // editor 绑定事件
  _e(function (E, $) {
  
      E.fn.init = function () {
  
          // 初始化 editor 默认配置
          this.initDefaultConfig();
  
          // 增加container
          this.addEditorContainer();
  
          // 增加编辑区域
          this.addTxt();
  
          // 增加menuContainer
          this.addMenuContainer();
  
          // 初始化菜单集合
          this.menus = {};
  
          // 初始化commandHooks
          this.commandHooks();
  
      };
  
  });
  // editor api
  _e(function (E, $) {
  
      // 预定义 ready 事件
      E.fn.ready = function (fn) {
  
          if (!this.readyFns) {
              this.readyFns = [];
          }
  
          this.readyFns.push(fn);
      };
  
      // 处理ready事件
      E.fn.readyHeadler = function () {
          var fns = this.readyFns;
  
          while (fns.length) {
              fns.shift().call(this);
          }
      };
  
      // 更新内容到 $valueContainer
      E.fn.updateValue = function () {
          var editor = this;
          var $valueContainer = editor.$valueContainer;
          var $txt = editor.txt.$txt;
  
          if ($valueContainer === $txt) {
              // 传入生成编辑器的div，即是编辑区域
              return;
          }
  
          var value = $txt.html();
          $valueContainer.val(value);
      };
  
      // 获取初始化的内容
      E.fn.getInitValue = function () {
          var editor = this;
          var $valueContainer = editor.$valueContainer;
          var currentValue = '';
          var nodeName = editor.valueNodeName;
          if (nodeName === 'div') {
              currentValue = $valueContainer.html();
          } else if (nodeName === 'textarea') {
              currentValue = $valueContainer.val();
          }
  
          return currentValue;
      };
  
      // 触发菜单updatestyle
      E.fn.updateMenuStyle = function () {
          var menus = this.menus;
  
          $.each(menus, function (k, menu) {
              menu.updateSelected();
          });
      };
  
      // 除了传入的 menuIds，其他全部启用
      E.fn.enableMenusExcept = function (menuIds) {
          if (this._disabled) {
              // 编辑器处于禁用状态，则不执行改操作
              return;
          }
          // menuIds参数：支持数组和字符串
          menuIds = menuIds || [];
          if (typeof menuIds === 'string') {
              menuIds = [menuIds];
          }
  
          $.each(this.menus, function (k, menu) {
              if (menuIds.indexOf(k) >= 0) {
                  return;
              }
              menu.disabled(false);
          });
      };
  
      // 除了传入的 menuIds，其他全部禁用
      E.fn.disableMenusExcept = function (menuIds) {
          if (this._disabled) {
              // 编辑器处于禁用状态，则不执行改操作
              return;
          }
          // menuIds参数：支持数组和字符串
          menuIds = menuIds || [];
          if (typeof menuIds === 'string') {
              menuIds = [menuIds];
          }
  
          $.each(this.menus, function (k, menu) {
              if (menuIds.indexOf(k) >= 0) {
                  return;
              }
              menu.disabled(true);
          });
      };
  
      // 隐藏所有 dropPanel droplist modal
      E.fn.hideDropPanelAndModal = function () {
          var menus = this.menus;
  
          $.each(menus, function (k, menu) {
              var m = menu.dropPanel || menu.dropList || menu.modal;
              if (m && m.hide) {
                  m.hide();
              }
          });
      };
  
  });
  // selection range API
  _e(function (E, $) {
  
      // 用到 w3c range 的函数，如果检测到浏览器不支持 w3c range，则赋值为空函数
      var ieRange = !E.w3cRange;
      function emptyFn() {}
  
      // 设置或读取当前的range
      E.fn.currentRange = function (cr){
          if (cr) {
              this._rangeData = cr;
          } else {
              return this._rangeData;
          }
      };
  
      // 将当前选区折叠
      E.fn.collapseRange = function (range, opt) {
          // opt 参数说明：'start'-折叠到开始; 'end'-折叠到结束
          opt = opt || 'end';
          opt = opt === 'start' ? true : false;
  
          range = range || this.currentRange();
          
          if (range) {
              // 合并，保存
              range.collapse(opt);
              this.currentRange(range);
          }
      };
  
      // 获取选区的文字
      E.fn.getRangeText = ieRange ? emptyFn : function (range) {
          range = range || this.currentRange();
          if (!range) {
              return;
          }
          return range.toString();
      };
  
      // 获取选区对应的DOM对象
      E.fn.getRangeElem = ieRange ? emptyFn : function (range) {
          range = range || this.currentRange();
          var dom = range.commonAncestorContainer;
  
          if (dom.nodeType === 1) {
              return dom;
          } else {
              return dom.parentNode;
          }
      };
  
      // 选区内容是否为空？
      E.fn.isRangeEmpty = ieRange ? emptyFn : function (range) {
          range = range || this.currentRange();
  
          if (range && range.startContainer) {
              if (range.startContainer === range.endContainer) {
                  if (range.startOffset === range.endOffset) {
                      return true;
                  }
              }
          }
  
          return false;
      };
  
      // 保存选区数据
      E.fn.saveSelection = ieRange ? emptyFn : function (range) {
          var self = this,
              _parentElem,
              selection,
              txt = self.txt.$txt.get(0);
  
          if (range) {
              _parentElem = range.commonAncestorContainer;
          } else {
              selection = document.getSelection();
              if (selection.getRangeAt && selection.rangeCount) {
                  range = document.getSelection().getRangeAt(0);
                  _parentElem = range.commonAncestorContainer;
              }
          }
          // 确定父元素一定要包含在编辑器区域内
          if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
              // 保存选择区域
              self.currentRange(range);
          }
      };
  
      // 恢复选中区域
      E.fn.restoreSelection = ieRange ? emptyFn : function (range) {
          var selection;
  
          range = range || this.currentRange();
  
          if (!range) {
              return;
          }
  
          // 使用 try catch 来防止 IE 某些情况报错
          try {
              selection = document.getSelection();
              selection.removeAllRanges();
              selection.addRange(range);
          } catch (ex) {
              E.error('执行 editor.restoreSelection 时，IE可能会有异常，不影响使用');
          }
      };
  
      // 根据elem恢复选区
      E.fn.restoreSelectionByElem = ieRange ? emptyFn : function (elem, opt) {
          // opt参数说明：'start'-折叠到开始，'end'-折叠到结束，'all'-全部选中
          if (!elem) {
              return;
          }
          opt = opt || 'end'; // 默认为折叠到结束
  
          // 根据elem获取选区
          this.setRangeByElem(elem);
  
          // 根据 opt 折叠选区
          if (opt === 'start') {
              this.collapseRange(this.currentRange(), 'start');
          }
          if (opt === 'end') {
              this.collapseRange(this.currentRange(), 'end');
          }
          
          // 恢复选区
          this.restoreSelection();
      };
  
      // 初始化选区
      E.fn.initSelection = ieRange ? emptyFn : function () {
          var editor = this;
          if( editor.currentRange() ){
              //如果currentRange有值，则不用再初始化
              return;
          }
  
          var range;
          var $txt = editor.txt.$txt;
          var $firstChild = $txt.children().first();
          
          if ($firstChild.length) {
              editor.restoreSelectionByElem($firstChild.get(0));
          }
      };
  
      // 根据元素创建选区
      E.fn.setRangeByElem = ieRange ? emptyFn : function (elem) {
          var editor = this;
          var txtElem = editor.txt.$txt.get(0);
          if (!elem || !$.contains(txtElem, elem)) {
              return;
          }
  
          // 找到elem的第一个 textNode 和 最后一个 textNode
          var firstTextNode = elem.firstChild;
          while (firstTextNode) {
              if (firstTextNode.nodeType === 3) {
                  break;
              }
              // 继续向下
              firstTextNode = firstTextNode.firstChild;
          }
          var lastTextNode = elem.lastChild;
          while (lastTextNode) {
              if (lastTextNode.nodeType === 3) {
                  break;
              }
              // 继续向下
              lastTextNode = lastTextNode.lastChild;
          }
          
          var range = document.createRange();
          if (firstTextNode && lastTextNode) {
              // 说明 elem 有内容，能取到子元素
              range.setStart(firstTextNode, 0);
              range.setEnd(lastTextNode, lastTextNode.textContent.length);
          } else {
              // 说明 elem 无内容
              range.setStart(elem, 0);
              range.setEnd(elem, 0);
          }
  
          // 保存选区
          editor.saveSelection(range);
      };
  
  });
  // selection range API - IE8及以下
  _e(function (E, $) {
  
      if (E.w3cRange) {
          // 说明支持 W3C 的range方法
          return;
      }
  
      // -----------------IE8时，需要重写以下方法-------------------
  
      // 获取选区的文字
      E.fn.getRangeText = function (range) {
          range = range || this.currentRange();
          if (!range) {
              return;
          }
          return range.text;
      };
  
      // 获取选区对应的DOM对象
      E.fn.getRangeElem = function (range) {
          range = range || this.currentRange();
          if (!range) {
              return;
          }
          var dom = range.parentElement();
  
          if (dom.nodeType === 1) {
              return dom;
          } else {
              return dom.parentNode;
          }
      };
  
      // 选区内容是否为空？
      E.fn.isRangeEmpty = function (range) {
          range = range || this.currentRange();
  
          if (range && range.text) {
              return false;
          }
  
          return true;
      };
  
      // 保存选区数据
      E.fn.saveSelection = function (range) {
          var self = this,
              _parentElem,
              selection,
              txt = self.txt.$txt.get(0);
  
          if (range) {
              _parentElem = range.parentElement();
          } else {
              range = document.selection.createRange();
              if(typeof range.parentElement === 'undefined'){
                  //IE6、7中，insertImage后会执行此处
                  //由于找不到range.parentElement，所以干脆将_parentElem赋值为null
                  _parentElem = null;
              }else{
                  _parentElem = range.parentElement();
              }
          }
  
          // 确定父元素一定要包含在编辑器区域内
          if (_parentElem && ($.contains(txt, _parentElem) || txt === _parentElem) ) {
              // 保存选择区域
              self.currentRange(range);
          }
      };
  
      // 恢复选中区域
      E.fn.restoreSelection = function (currentRange){
          var editor = this,
              selection,
              range;
  
          currentRange = currentRange || editor.currentRange();
          if(!currentRange){
              return;
          }
  
          range = document.selection.createRange();
          try {
              // 此处，plupload上传上传图片时，IE8-会报一个『参数无效』的错误
              range.setEndPoint('EndToEnd', currentRange);
          } catch (ex) {
  
          }
          
          if(currentRange.text.length === 0){
              try {
                  // IE8 插入表情会报错
                  range.collapse(false);
              } catch (ex) {
                  
              }
              
          }else{
              range.setEndPoint('StartToStart', currentRange);
          }
          range.select();
      };
  
  });
  // editor command hooks
  _e(function (E, $) {
      
      E.fn.commandHooks = function () {
          var editor = this;
          var commandHooks = {};
          
          // insertHtml
          commandHooks.insertHtml = function (html) {
              var $elem = $(html);
              var rangeElem = editor.getRangeElem();
              var targetElem;
              
              targetElem = editor.getLegalTags(rangeElem);
              if (!targetElem) {
                  return;
              }
  
              $(targetElem).after($elem);
          };
  
          // 保存到对象
          editor.commandHooks = commandHooks;
      };
  
  });
  // editor command API
  _e(function (E, $) {
  
      // 基本命令
      E.fn.command = function (e, commandName, commandValue, callback) {
          var editor = this;
          var hooks;
          
          function commandFn() {
              if (!commandName) {
                  return;
              }
              if (editor.queryCommandSupported(commandName)) {
                  // 默认命令
                  document.execCommand(commandName, false, commandValue);
              } else {
                  // hooks 命令
                  hooks = editor.commandHooks;
                  if (commandName in hooks) {
                      hooks[commandName](commandValue);
                  }
              }
          }
  
          this.customCommand(e, commandFn, callback);
      };
  
      // 针对一个elem对象执行基础命令
      E.fn.commandForElem = function (elemOpt, e, commandName, commandValue, callback) {
          // 取得查询elem的查询条件和验证函数
          var selector;
          var check;
          if (typeof elemOpt === 'string') {
              selector = elemOpt;
          } else {
              selector = elemOpt.selector;
              check = elemOpt.check;
          }
  
          // 查询elem
          var rangeElem = this.getRangeElem();
          rangeElem = this.getSelfOrParentByName(rangeElem, selector, check);
  
          // 根据elem设置range
          if (rangeElem) {
              this.setRangeByElem(rangeElem);
          }
  
          // 然后执行基础命令
          this.command(e, commandName, commandValue, callback);
      };
  
      // 自定义命令
      E.fn.customCommand = function (e, commandFn, callback) {
          var editor = this;
          var range = editor.currentRange();
  
          if (!range) {
              // 目前没有选区，则无法执行命令
              e && e.preventDefault();
              return;
          }
          // 记录内容，以便撤销（执行命令之前就要记录）
          editor.undoRecord();
  
          // 恢复选区（有 range 参数）
          this.restoreSelection(range);
  
          // 执行命令事件
          commandFn.call(editor);
  
          // 保存选区（无参数，要从浏览器直接获取range信息）
          this.saveSelection();
          // 重新恢复选区（无参数，要取得刚刚从浏览器得到的range信息）
          this.restoreSelection();
  
          // 执行 callback
          if (callback && typeof callback === 'function') {
              callback.call(editor);
          }
  
          // 最后插入空行
          editor.txt.insertEmptyP();
  
          // 包裹暴露的img和text
          editor.txt.wrapImgAndText();
  
          // 更新内容
          editor.updateValue();
  
          // 更新菜单样式
          editor.updateMenuStyle();
  
          // 隐藏 dropPanel dropList modal  设置 200ms 间隔
          function hidePanelAndModal() {
              editor.hideDropPanelAndModal();
          } 
          setTimeout(hidePanelAndModal, 200);
  
          if (e) {
              e.preventDefault();
          }
      };
  
      // 封装 document.queryCommandValue 函数
      // IE8 直接执行偶尔会报错，因此直接用 try catch 封装一下
      E.fn.queryCommandValue = function (commandName) {
          var result = '';
          try {
              result = document.queryCommandValue(commandName);
          } catch (ex) {
  
          }
          return result;
      };
  
      // 封装 document.queryCommandState 函数
      // IE8 直接执行偶尔会报错，因此直接用 try catch 封装一下
      E.fn.queryCommandState = function (commandName) {
          var result = false;
          try {
              result = document.queryCommandState(commandName);
          } catch (ex) {
  
          }
          return result;
      };
  
      // 封装 document.queryCommandSupported 函数
      E.fn.queryCommandSupported = function (commandName) {
          var result = false;
          try {
              result = document.queryCommandSupported(commandName);
          } catch (ex) {
  
          }
          return result;
      };
  
  });
  // dom selector
  _e(function (E, $) {
  
      var matchesSelector;
  
      // matchesSelector hook
      function _matchesSelectorForIE(selector) {
          var elem = this;
          var $elems = $(selector);
          var result = false;
  
          // 用jquery查找 selector 所有对象，如果其中有一个和传入 elem 相同，则证明 elem 符合 selector
          $elems.each(function () {
              if (this === elem) {
                  result = true;
                  return false;
              }
          });
  
          return result;
      }
  
      // 从当前的elem，往上去查找合法标签 如 p head table blockquote ul ol 等
      E.fn.getLegalTags = function (elem) {
          var legalTags = this.config.legalTags;
          if (!legalTags) {
              E.error('配置项中缺少 legalTags 的配置');
              return;
          }
          return this.getSelfOrParentByName(elem, legalTags);
      };
  
      // 根据条件，查询自身或者父元素，符合即返回
      E.fn.getSelfOrParentByName = function (elem, selector, check) {
  
          if (!elem || !selector) {
              return;
          }
  
          if (!matchesSelector) {
              // 定义 matchesSelector 函数
              matchesSelector = elem.webkitMatchesSelector || 
                                elem.mozMatchesSelector ||
                                elem.oMatchesSelector || 
                                elem.matchesSelector;
          }
          if (!matchesSelector) {
              // 如果浏览器本身不支持 matchesSelector 则使用自定义的hook
              matchesSelector = _matchesSelectorForIE;
          }
  
          var txt = this.txt.$txt.get(0);
  
          while (elem && txt !== elem && $.contains(txt, elem)) {
              if (matchesSelector.call(elem, selector)) {
                  // 符合 selector 查询条件
  
                  if (!check) {
                      // 没有 check 验证函数，直接返回即可
                      return elem;
                  }
  
                  if (check(elem)) {
                      // 如果有 check 验证函数，还需 check 函数的确认
                      return elem;
                  }
              }
  
              // 如果上一步没经过验证，则将跳转到父元素
              elem = elem.parentNode;
          }
  
          return;
      };
  
  });
  // undo redo
  _e(function (E, $) {
  
      var redoList = [];
      var undoList = [];
      var length = 20;  // 缓存的最大长度
  
      // 数据处理
      function _handle(editor, data, type) {
          // var range = data.range;
          // var range2 = range.cloneRange && range.cloneRange();
          var val = data.val;
          var html = editor.txt.$txt.html();
  
          if(val == null) {
              return;
          }
  
          if (val === html) {
              if (type === 'redo') { 
                  editor.redo();
                  return;
              } else if (type === 'undo') {
                  editor.undo();
                  return;
              } else {
                  return;
              }
          }
  
          // 保存数据
          editor.txt.$txt.html(val);
          // 更新数据到textarea（有必要的话）
          editor.updateValue();
  
          // onchange 事件
          if (editor.onchange && typeof editor.onchange === 'function') {
              editor.onchange.call(editor);
          }
  
          // ?????
          // 注释：$txt 被重新赋值之后，range会被重置，cloneRange() 也不好使
          // // 重置选区
          // if (range2) {
          //     editor.restoreSelection(range2);
          // }
      }
  
      // 记录
      E.fn.undoRecord = function () {
          var editor = this;
          var $txt = editor.txt.$txt;
          var val = $txt.html();
          var currentVal = undoList.length ? undoList[0] : '';
  
          if (val === currentVal.val) {
              return;
          }
  
          // 清空 redolist
          if (redoList.length) {
              redoList = [];
          }
  
          // 添加数据到 undoList
          undoList.unshift({
              range: editor.currentRange(),  // 将当前的range也记录下
              val: val
          });
  
          // 限制 undoList 长度
          if (undoList.length > length) {
              undoList.pop();
          }
      };
  
      // undo 操作
      E.fn.undo = function () {
  
          if (!undoList.length) {
              return;
          }
  
          // 取出 undolist 第一个值，加入 redolist
          var data = undoList.shift();
          redoList.unshift(data);
  
          // 并修改编辑器的内容
          _handle(this, data, 'undo');
      };
  
      // redo 操作
      E.fn.redo = function () {
          if (!redoList.length) {
              return;
          }
  
          // 取出 redolist 第一个值，加入 undolist
          var data = redoList.shift();
          undoList.unshift(data);
  
          // 并修改编辑器的内容
          _handle(this, data, 'redo');
      };
  });
  // 暴露给用户的 API
  _e(function (E, $) {
  
      // 创建编辑器
      E.fn.create = function () {
          var editor = this;
  
          // 检查 E.$body 是否有值
          // 如果在 body 之前引用了 js 文件，body 尚未加载，可能没有值
          if (!E.$body || E.$body.length === 0) {
              E.$body = $('body');
              E.$document = $(document);
              E.$window = $(window);
          }
  
          // 执行 addMenus 之前：
          // 1. 允许用户修改 editor.UI 自定义配置UI
          // 2. 允许用户通过修改 editor.menus 来自定义配置菜单
          // 因此要在 create 时执行，而不是 init           
          editor.addMenus();
  
          // 渲染
          editor.renderMenus();
          editor.renderMenuContainer();
          editor.renderTxt();
          editor.renderEditorContainer();
  
          // 绑定事件
          editor.eventMenus();
          editor.eventMenuContainer();
          editor.eventTxt();
  
          // 处理ready事件
          editor.readyHeadler();
  
          // 初始化选区
          editor.initSelection();
  
          // $txt 快捷方式
          editor.$txt = editor.txt.$txt;
  
          // 执行用户自定义事件，通过 E.ready() 添加
          var _plugins = E._plugins;
          if (_plugins && _plugins.length) {
              $.each(_plugins, function (k, val) {
                  val.call(editor);
              });
          }
      };
  
      // 禁用编辑器
      E.fn.disable = function () {
          this.txt.$txt.removeAttr('contenteditable');
          this.disableMenusExcept();
  
          // 先禁用，再记录状态
          this._disabled = true;
      };
      // 启用编辑器
      E.fn.enable = function () {
          // 先解除状态记录，再启用
          this._disabled = false;
          this.txt.$txt.attr('contenteditable', 'true');
          this.enableMenusExcept();
      };
  
      // 销毁编辑器
      E.fn.destroy = function () {
          var self = this;
          var $valueContainer = self.$valueContainer;
          var $editorContainer = self.$editorContainer;
          var valueNodeName = self.valueNodeName;
  
          if (valueNodeName === 'div') {
              // div 生成的编辑器
              $valueContainer.removeAttr('contenteditable');
              $editorContainer.after($valueContainer);
              $editorContainer.hide();
          } else {
              // textarea 生成的编辑器
              $valueContainer.show();
              $editorContainer.hide();
          }
      };
  
      // 撤销 销毁编辑器
      E.fn.undestroy = function () {
          var self = this;
          var $valueContainer = self.$valueContainer;
          var $editorContainer = self.$editorContainer;
          var $menuContainer = self.menuContainer.$menuContainer;
          var valueNodeName = self.valueNodeName;
  
          if (valueNodeName === 'div') {
              // div 生成的编辑器
              $valueContainer.attr('contenteditable', 'true');
              $menuContainer.after($valueContainer);
              $editorContainer.show();
          } else {
              // textarea 生成的编辑器
              $valueContainer.hide();
              $editorContainer.show();
          }
      };
  
      // 清空内容的快捷方式
      E.fn.clear = function () {
          var editor = this;
          var $txt = editor.txt.$txt;
          $txt.html('<p><br></p>');
          editor.restoreSelectionByElem($txt.find('p').get(0));
      };
  
  });
  // menuContainer 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var MenuContainer = function (editor) {
          this.editor = editor;
          this.init();
      };
  
      MenuContainer.fn = MenuContainer.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.MenuContainer = MenuContainer;
  
  });
  // MenuContainer.fn bind fn
  _e(function (E, $) {
  
      var MenuContainer = E.MenuContainer;
  
      // 初始化
      MenuContainer.fn.init = function () {
          var self = this;
          var $menuContainer = $('<div class="wangEditor-menu-container clearfix"></div>');
  
          self.$menuContainer = $menuContainer;
  
          // change shadow
          self.changeShadow();
      };
  
      // 编辑区域滚动时，增加shadow
      MenuContainer.fn.changeShadow = function () {
          var $menuContainer = this.$menuContainer;
          var editor = this.editor;
          var $txt = editor.txt.$txt;
  
          $txt.on('scroll', function () {
              if ($txt.scrollTop() > 10) {
                  $menuContainer.addClass('wangEditor-menu-shadow');
              } else {
                  $menuContainer.removeClass('wangEditor-menu-shadow');
              }
          });
      };
  
  });
  // MenuContainer.fn API
  _e(function (E, $) {
  
      var MenuContainer = E.MenuContainer;
  
      MenuContainer.fn.render = function () {
          var $menuContainer = this.$menuContainer;
          var $editorContainer = this.editor.$editorContainer;
  
          $editorContainer.append($menuContainer);
      };
      
      // 获取菜单栏的高度
      MenuContainer.fn.height = function () {
          var $menuContainer = this.$menuContainer;
          return $menuContainer.height();
      };
  
      // 添加菜单
      MenuContainer.fn.appendMenu = function (groupIdx, menu) {
          // 判断是否需要新增一个菜单组
          this._addGroup(groupIdx);
          // 增加菜单（返回 $menuItem）
          return this._addOneMenu(menu);
      };
      MenuContainer.fn._addGroup = function (groupIdx) {
          var $menuContainer = this.$menuContainer;
          var $menuGroup;
          if (!this.$currentGroup || this.currentGroupIdx !== groupIdx) {
              $menuGroup = $('<div class="menu-group clearfix"></div>');
              $menuContainer.append($menuGroup);
  
              this.$currentGroup = $menuGroup;
              this.currentGroupIdx = groupIdx;
          }
      };
      MenuContainer.fn._addOneMenu = function (menu) {
          var $menuNormal = menu.$domNormal;
          var $menuSelected = menu.$domSelected;
  
          var $menuGroup = this.$currentGroup;
          var $item = $('<div class="menu-item clearfix"></div>');
          $menuSelected.hide();
          $item.append($menuNormal).append($menuSelected);
          $menuGroup.append($item);
  
          return $item;
      };
  
  });
  // menu 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var Menu = function (opt) {
          this.editor = opt.editor;
          this.id = opt.id;
          this.title = opt.title;
          this.$domNormal = opt.$domNormal;
          this.$domSelected = opt.$domSelected || opt.$domNormal;
  
          // document.execCommand 的参数
          this.commandName = opt.commandName;
          this.commandValue = opt.commandValue;
          this.commandNameSelected = opt.commandNameSelected || opt.commandName;
          this.commandValueSelected = opt.commandValueSelected || opt.commandValue;
      };
  
      Menu.fn = Menu.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.Menu = Menu;
  });
  // Menu.fn 初始化绑定的事件
  _e(function (E, $) {
  
      var Menu = E.Menu;
  
      // 初始化UI
      Menu.fn.initUI = function () {
          var editor = this.editor;
          var uiConfig = editor.UI.menus;
          var menuId = this.id;
          var menuUI = uiConfig[menuId];
  
          if (this.$domNormal && this.$domSelected) {
              // 自定义的菜单中，已经传入了 $dom 无需从配置文件中查找生成
              return;
          }
  
          if (menuUI == null) {
              E.warn('editor.UI配置中，没有菜单 "' + menuId + '" 的UI配置，只能取默认值');
              
              // 必须写成 uiConfig['default'];
              // 写成 uiConfig.default IE8会报错
              menuUI = uiConfig['default'];
          }
  
          // 正常状态
          this.$domNormal = $(menuUI.normal);
  
          // 选中状态
          if (/^\./.test(menuUI.selected)) {
              // 增加一个样式
              this.$domSelected = this.$domNormal.clone().addClass(menuUI.selected.slice(1));
          } else {
              // 一个新的dom对象
              this.$domSelected = $(menuUI.selected);
          }
      };
  
  });
  // Menu.fn API
  _e(function (E, $) {
  
      var Menu = E.Menu;
  
      // 渲染菜单
      Menu.fn.render = function (groupIdx) {
          // 渲染UI
          this.initUI();
          
          var editor = this.editor;
          var menuContainer = editor.menuContainer;
          var $menuItem = menuContainer.appendMenu(groupIdx, this);
          var onRender = this.onRender;
  
          // 渲染tip
          this._renderTip($menuItem);
  
          // 执行 onRender 函数
          if (onRender && typeof onRender === 'function') {
              onRender.call(this);
          }
      };
      Menu.fn._renderTip = function ($menuItem) {
          var self = this;
          var editor = self.editor;
          var title = self.title;
          var $tip = $('<div class="menu-tip"></div>');
          // var $triangle = $('<i class="tip-triangle"></i>'); // 小三角
  
          // 计算 tip 宽度
          var $tempDiv;
          if (!self.tipWidth) {
              // 设置一个纯透明的 p（absolute;top:-10000px;不会显示在内容区域）
              // 内容赋值为 title ，为了计算tip宽度
              $tempDiv = $('<p style="opacity:0; filter:Alpha(opacity=0); position:absolute;top:-10000px;">' + title + '</p>');
              // 先添加到body，计算完再 remove
              E.$body.append($tempDiv);
              editor.ready(function () {
                  var editor = this;
                  var titleWidth = $tempDiv.outerWidth() + 5; // 多出 5px 的冗余
                  var currentWidth = $tip.outerWidth();
                  var currentMarginLeft = parseFloat($tip.css('margin-left'), 10);
                  // 计算完，拿到数据，则弃用
                  $tempDiv.remove();
                  $tempDiv = null;
  
                  // 重新设置样式
                  $tip.css({
                      width: titleWidth,
                      'margin-left': currentMarginLeft + (currentWidth - titleWidth)/2
                  });
  
                  // 存储
                  self.tipWidth = titleWidth;
              });
          }
  
          // $tip.append($triangle);
          $tip.append(title);
          $menuItem.append($tip);
  
          function show() {
              $tip.show();
          }
          function hide() {
              $tip.hide();
          }
  
          var timeoutId;
          $menuItem.find('a').on('mouseenter', function (e) {
              if (!self.active() && !self.disabled()) {
                  timeoutId = setTimeout(show, 200);
              }
          }).on('mouseleave', function (e) {
              timeoutId && clearTimeout(timeoutId);
              hide();
          }).on('click', hide);
      };
  
      // 绑定事件
      Menu.fn.bindEvent = function () {
          var self = this;
  
          var $domNormal = self.$domNormal;
          var $domSelected = self.$domSelected;
  
          // 试图获取该菜单定义的事件（未selected），没有则自己定义
          var clickEvent = self.clickEvent;
          if (!clickEvent) {
              clickEvent = function (e) {
                  // -----------dropPanel dropList modal-----------
                  var dropObj = self.dropPanel || self.dropList || self.modal;
                  if (dropObj && dropObj.show) {
                      if (dropObj.isShowing) {
                          dropObj.hide();
                      } else {
                          dropObj.show();
                      }
                      return;
                  }
  
                  // -----------command-----------
                  var editor = self.editor;
                  var commandName;
                  var commandValue;
  
                  var selected = self.selected;
                  if (selected) {
                      commandName = self.commandNameSelected;
                      commandValue = self.commandValueSelected;
                  } else {
                      commandName = self.commandName;
                      commandValue = self.commandValue;
                  }
  
                  if (commandName) {
                      // 执行命令
                      editor.command(e, commandName, commandValue);
                  } else {
                      // 提示
                      E.warn('菜单 "' + self.id + '" 未定义click事件');
                      e.preventDefault();
                  }
              };
          }
          // 获取菜单定义的selected情况下的点击事件
          var clickEventSelected = self.clickEventSelected || clickEvent;
  
          // 将事件绑定到菜单dom上
          $domNormal.click(function (e) {
              if (!self.disabled()) {
                  clickEvent.call(self, e);
                  self.updateSelected();
              }
              e.preventDefault();
          });
          $domSelected.click(function (e) {
              if (!self.disabled()) {
                  clickEventSelected.call(self, e);
                  self.updateSelected();
              }
              e.preventDefault();
          });
      };
  
      // 更新选中状态
      Menu.fn.updateSelected = function () {
          var self = this;
          var editor = self.editor;
  
          // 试图获取用户自定义的判断事件
          var updateSelectedEvent = self.updateSelectedEvent;
          if (!updateSelectedEvent) {
              // 用户未自定义，则设置默认值
              updateSelectedEvent = function () {
                  var self = this;
                  var editor = self.editor;
                  var commandName = self.commandName;
                  var commandValue = self.commandValue;
  
                  if (commandValue) {
                      if (editor.queryCommandValue(commandName).toLowerCase() === commandValue.toLowerCase()) {
                          return true;
                      }
                  } else if (editor.queryCommandState(commandName)) {
                      return true;
                  }
  
                  return false;
              };
          }
  
          // 获取结果
          var result = updateSelectedEvent.call(self);
          result = !!result;
  
          // 存储结果、显示效果
          self.changeSelectedState(result);
      };
  
      // 切换选中状态、显示效果
      Menu.fn.changeSelectedState = function (state) {
          var self = this;
          var selected = self.selected;
  
          if (state != null && typeof state === 'boolean') {
              if (selected === state) {
                  // 计算结果和当前的状态一样
                  return;
              }
              // 存储结果
              self.selected = state;
  
              // 切换菜单的显示
              if (state) {
                  // 选中
                  self.$domNormal.hide();
                  self.$domSelected.show();
              } else {
                  // 未选中
                  self.$domNormal.show();
                  self.$domSelected.hide();
              }
          } // if
      };
  
      // 点击菜单，显示了 dropPanel modal 时，菜单的状态 
      Menu.fn.active = function (active) {
          if (active == null) {
              return this._activeState;
          }
          this._activeState = active;
      };
      Menu.fn.activeStyle = function (active) {
          var selected = this.selected;
          var $dom = this.$domNormal;
          var $domSelected = this.$domSelected;
  
          if (active) {
              $dom.addClass('active');
              $domSelected.addClass('active');
          } else {
              $dom.removeClass('active');
              $domSelected.removeClass('active');
          }
  
          // 记录状态 （ menu hover 时会取状态用 ）
          this.active(active);
      };
  
      // 菜单的启用和禁用
      Menu.fn.disabled = function (opt) {
          // 参数为空，取值
          if (opt == null) {
              return !!this._disabled;
          }
  
          if (this._disabled === opt) {
              // 要设置的参数值和当前参数只一样，无需再次设置
              return;
          }
  
          var $dom = this.$domNormal;
          var $domSelected = this.$domSelected;
  
          // 设置样式
          if (opt) {
              $dom.addClass('disable');
              $domSelected.addClass('disable');
          } else {
              $dom.removeClass('disable');
              $domSelected.removeClass('disable');
          }
  
          // 存储
          this._disabled = opt;
      };
  
  });
  // dropList 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var DropList = function (editor, menu, opt) {
          this.editor = editor;
          this.menu = menu;
  
          // list 的数据源，格式 {'commandValue': 'title', ...}
          this.data = opt.data;
          // 要为每个item自定义的模板
          this.tpl = opt.tpl;
          // 为了执行 editor.commandForElem 而传入的elem查询方式
          this.selectorForELemCommand = opt.selectorForELemCommand;
  
          // 执行事件前后的钩子
          this.beforeEvent = opt.beforeEvent;
          this.afterEvent = opt.afterEvent;
  
          // 初始化
          this.init();
      };
  
      DropList.fn = DropList.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.DropList = DropList;
  });
  // dropList fn bind
  _e(function (E, $) {
  
      var DropList = E.DropList;
  
      // init
      DropList.fn.init = function () {
          var self = this;
  
          // 生成dom对象
          self.initDOM();
  
          // 绑定command事件
          self.bindEvent();
  
          // 声明隐藏的事件
          self.initHideEvent();
      };
  
      // 初始化dom结构
      DropList.fn.initDOM = function () {
          var self = this;
          var data = self.data;
          var tpl = self.tpl || '<span>{#title}</span>';
          var $list = $('<div class="wangEditor-drop-list clearfix"></div>');
  
          var itemContent;
          var $item;
          $.each(data, function (commandValue, title) {
              itemContent = tpl.replace(/{#commandValue}/ig, commandValue).replace(/{#title}/ig, title);
              $item = $('<a href="#" commandValue="' + commandValue + '"></a>');
              $item.append(itemContent);
              $list.append($item);
          });
  
          self.$list = $list;
      };
  
      // 绑定事件
      DropList.fn.bindEvent = function () {
          var self = this;
          var editor = self.editor;
          var menu = self.menu;
          var commandName = menu.commandName;
          var selectorForELemCommand = self.selectorForELemCommand;
          var $list = self.$list;
  
          // 执行事件前后的钩子函数
          var beforeEvent = self.beforeEvent;
          var afterEvent = self.afterEvent;
  
          $list.on('click', 'a[commandValue]', function (e) {
              // 正式命令执行之前
              if (beforeEvent && typeof beforeEvent === 'function') {
                  beforeEvent.call(e);
              }
  
              // 执行命令
              var commandValue = $(e.currentTarget).attr('commandValue');
              if (menu.selected && editor.isRangeEmpty() && selectorForELemCommand) {
                  // 当前处于选中状态，并且选中内容为空
                  editor.commandForElem(selectorForELemCommand, e, commandName, commandValue);
              } else {
                  // 当前未处于选中状态，或者有选中内容。则执行默认命令
                  editor.command(e, commandName, commandValue);
              }
  
              // 正式命令之后的钩子
              if (afterEvent && typeof afterEvent === 'function') {
                  afterEvent.call(e);
              }
          });
      };
  
      // 点击其他地方，立即隐藏 droplist
      DropList.fn.initHideEvent = function () {
          var self = this;
  
          // 获取 list elem
          var thisList = self.$list.get(0);
  
          E.$body.on('click', function (e) {
              if (!self.isShowing) {
                  return;
              }
              var trigger = e.target;
  
              // 获取菜单elem
              var menu = self.menu;
              var menuDom;
              if (menu.selected) {
                  menuDom = menu.$domSelected.get(0);
              } else {
                  menuDom = menu.$domNormal.get(0);
              }
  
              if (menuDom === trigger || $.contains(menuDom, trigger)) {
                  // 说明由本菜单点击触发的
                  return;
              }
  
              if (thisList === trigger || $.contains(thisList, trigger)) {
                  // 说明由本list点击触发的
                  return;
              }
  
              // 其他情况，隐藏 list
              self.hide();
          });
  
          E.$window.scroll(function () {
              self.hide();
          });
  
          E.$window.on('resize', function () {
              self.hide();
          });
      };
  
  });
  // dropListfn api
  _e(function (E, $) {
      
      var DropList = E.DropList;
  
      // 渲染
      DropList.fn._render = function () {
          var self = this;
          var editor = self.editor;
          var $list = self.$list;
  
          // 渲染到页面
          editor.$editorContainer.append($list);
  
          // 记录状态
          self.rendered = true;
      };
  
      // 定位
      DropList.fn._position = function () {
          var self = this;
          var $list = self.$list;
          var editor = self.editor;
          var menu = self.menu;
          var $menuContainer = editor.menuContainer.$menuContainer;
          var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
          // 注意这里的 offsetParent() 要返回 .menu-item 的 position
          // 因为 .menu-item 是 position:relative
          var menuPosition = $menuDom.offsetParent().position();
  
          // 取得 menu 的位置、尺寸属性
          var menuTop = menuPosition.top;
          var menuLeft = menuPosition.left;
          var menuHeight = $menuDom.offsetParent().height();
          var menuWidth = $menuDom.offsetParent().width();
  
          // 取得 list 的尺寸属性
          var listWidth = $list.outerWidth();
          // var listHeight = $list.outerHeight();
  
          // 取得 $txt 的尺寸
          var txtWidth = editor.txt.$txt.outerWidth();
  
          // ------------开始计算-------------
  
          // 初步计算 list 位置属性
          var top = menuTop + menuHeight;
          var left = menuLeft + menuWidth/2;
          var marginLeft = 0 - menuWidth/2;
  
          // 如果超出了有边界，则要左移（且和右侧有间隙）
          var valWithTxt = (left + listWidth) - txtWidth;
          if (valWithTxt > -10) {
              marginLeft = marginLeft - valWithTxt - 10;
          }
          // 设置样式
          $list.css({
              top: top,
              left: left,
              'margin-left': marginLeft
          });
  
          // 如果因为向下滚动而导致菜单fixed，则再加一步处理
          if (editor._isMenufixed) {
              top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $list.offset().top);
  
              // 重新设置top
              $list.css({
                  top: top
              });
          }
      };
  
      // 显示
      DropList.fn.show = function () {
          var self = this;
          var menu = self.menu;
          if (!self.rendered) {
              // 第一次show之前，先渲染
              self._render();
          }
  
          if (self.isShowing) {
              return;
          }
  
          var $list = self.$list;
          $list.show();
  
          // 定位
          self._position();
  
          // 记录状态
          self.isShowing = true;
  
          // 菜单状态
          menu.activeStyle(true);
      };
  
      // 隐藏
      DropList.fn.hide = function () {
          var self = this;
          var menu = self.menu;
          if (!self.isShowing) {
              return;
          }
  
          var $list = self.$list;
          $list.hide();
  
          // 记录状态
          self.isShowing = false;
  
          // 菜单状态
          menu.activeStyle(false);
      };
  });
  // dropPanel 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var DropPanel = function (editor, menu, opt) {
          this.editor = editor;
          this.menu = menu;
          this.$content = opt.$content;
          this.width = opt.width || 200;
          this.height = opt.height;
          this.onRender = opt.onRender;
  
          // init
          this.init();
      };
  
      DropPanel.fn = DropPanel.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.DropPanel = DropPanel;
  });
  // dropPanel fn bind
  _e(function (E, $) {
  
      var DropPanel = E.DropPanel;
  
      // init
      DropPanel.fn.init = function () {
          var self = this;
  
          // 生成dom对象
          self.initDOM();
  
          // 声明隐藏的事件
          self.initHideEvent();
      };
  
      // init DOM
      DropPanel.fn.initDOM = function () {
          var self = this;
          var $content = self.$content;
          var width = self.width;
          var height = self.height;
          var $panel = $('<div class="wangEditor-drop-panel clearfix"></div>');
          var $triangle = $('<div class="tip-triangle"></div>');
  
          $panel.css({
              width: width,
              height: height ? height : 'auto'
          });
          $panel.append($triangle);
          $panel.append($content);
  
          // 添加对象数据
          self.$panel = $panel;
          self.$triangle = $triangle;
      };
  
      // 点击其他地方，立即隐藏 dropPanel
      DropPanel.fn.initHideEvent = function () {
          var self = this;
  
          // 获取 panel elem
          var thisPanle = self.$panel.get(0);
  
          E.$body.on('click', function (e) {
              if (!self.isShowing) {
                  return;
              }
              var trigger = e.target;
  
              // 获取菜单elem
              var menu = self.menu;
              var menuDom;
              if (menu.selected) {
                  menuDom = menu.$domSelected.get(0);
              } else {
                  menuDom = menu.$domNormal.get(0);
              }
  
              if (menuDom === trigger || $.contains(menuDom, trigger)) {
                  // 说明由本菜单点击触发的
                  return;
              }
  
              if (thisPanle === trigger || $.contains(thisPanle, trigger)) {
                  // 说明由本panel点击触发的
                  return;
              }
  
              // 其他情况，隐藏 panel
              self.hide();
          });
  
          E.$window.scroll(function (e) {
              self.hide();
          });
  
          E.$window.on('resize', function () {
              self.hide();
          });
      };
  
  });
  // dropPanel fn api
  _e(function (E, $) {
     
      var DropPanel = E.DropPanel;
  
      // 渲染
      DropPanel.fn._render = function () {
          var self = this;
          var onRender = self.onRender;
          var editor = self.editor;
          var $panel = self.$panel;
  
          // 渲染到页面
          editor.$editorContainer.append($panel);
  
          // 渲染后的回调事件
          onRender && onRender.call(self);
  
          // 记录状态
          self.rendered = true;
      };
  
      // 定位
      DropPanel.fn._position = function () {
          var self = this;
          var $panel = self.$panel;
          var $triangle = self.$triangle;
          var editor = self.editor;
          var $menuContainer = editor.menuContainer.$menuContainer;
          var menu = self.menu;
          var $menuDom = menu.selected ? menu.$domSelected : menu.$domNormal;
          // 注意这里的 offsetParent() 要返回 .menu-item 的 position
          // 因为 .menu-item 是 position:relative
          var menuPosition = $menuDom.offsetParent().position();
  
          // 取得 menu 的位置、尺寸属性
          var menuTop = menuPosition.top;
          var menuLeft = menuPosition.left;
          var menuHeight = $menuDom.offsetParent().height();
          var menuWidth = $menuDom.offsetParent().width();
  
          // 取得 panel 的尺寸属性
          var panelWidth = $panel.outerWidth();
          // var panelHeight = $panel.outerHeight();
  
          // 取得 $txt 的尺寸
          var txtWidth = editor.txt.$txt.outerWidth();
  
          // ------------开始计算-------------
  
          // 初步计算 panel 位置属性
          var top = menuTop + menuHeight;
          var left = menuLeft + menuWidth/2;
          var marginLeft = 0 - panelWidth/2;
          var marginLeft2 = marginLeft;  // 下文用于和 marginLeft 比较，来设置三角形tip的位置
  
          // 如果超出了左边界，则移动回来（要和左侧有10px间隙）
          if ((0 - marginLeft) > (left - 10)) {
              marginLeft = 0 - (left - 10);
          }
  
          // 如果超出了有边界，则要左移（且和右侧有10px间隙）
          var valWithTxt = (left + panelWidth + marginLeft) - txtWidth;
          if (valWithTxt > -10) {
              marginLeft = marginLeft - valWithTxt - 10;
          }
  
          // 设置样式
          $panel.css({
              top: top,
              left: left,
              'margin-left': marginLeft
          });
  
          // 如果因为向下滚动而导致菜单fixed，则再加一步处理
          if (editor._isMenufixed) {
              top = top + (($menuContainer.offset().top + $menuContainer.outerHeight()) - $panel.offset().top);
  
              // 重新设置top
              $panel.css({
                  top: top
              });
          }
  
          // 设置三角形 tip 的位置
          $triangle.css({
              'margin-left': marginLeft2 - marginLeft - 5
          });
      };
  
      // focus 第一个 input
      DropPanel.fn.focusFirstInput = function () {
          var self = this;
          var $panel = self.$panel;
          $panel.find('input[type=text],textarea').each(function () {
              var $input = $(this);
              if ($input.attr('disabled') == null) {
                  $input.focus();
                  return false;
              }
          });
      };
  
      // 显示
      DropPanel.fn.show = function () {
          var self = this;
          var menu = self.menu;
          if (!self.rendered) {
              // 第一次show之前，先渲染
              self._render();
          }
  
          if (self.isShowing) {
              return;
          }
  
          var $panel = self.$panel;
          $panel.show();
  
          // 定位
          self._position();
  
          // 记录状态
          self.isShowing = true;
  
          // 菜单状态
          menu.activeStyle(true);
  
          if (E.w3cRange) {
              // 高级浏览器
              self.focusFirstInput();
          } else {
              // 兼容 IE8 input placeholder
              E.placeholderForIE8($panel);
          }
      };
  
      // 隐藏
      DropPanel.fn.hide = function () {
          var self = this;
          var menu = self.menu;
          if (!self.isShowing) {
              return;
          }
  
          var $panel = self.$panel;
          $panel.hide();
  
          // 记录状态
          self.isShowing = false;
  
          // 菜单状态
          menu.activeStyle(false);
      };
  
  });
  // modal 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var Modal = function (editor, menu, opt) {
          this.editor = editor;
          this.menu = menu;
          this.$content = opt.$content;
  
          this.init();
      };
  
      Modal.fn = Modal.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.Modal = Modal;
  });
  // modal fn bind
  _e(function (E, $) {
  
      var Modal = E.Modal;
  
      Modal.fn.init = function () {
          var self = this;
  
          // 初始化dom
          self.initDom();
  
          // 初始化隐藏事件
          self.initHideEvent();
      };
  
      // 初始化dom
      Modal.fn.initDom = function () {
          var self = this;
          var $content = self.$content;
          var $modal = $('<div class="wangEditor-modal"></div>');
          var $close = $('<div class="wangEditor-modal-close"><i class="wangeditor-menu-img-cancel-circle"></i></div>');
  
          $modal.append($close);
          $modal.append($content);
  
          // 记录数据
          self.$modal = $modal;
          self.$close = $close;
      };
  
      // 初始化隐藏事件
      Modal.fn.initHideEvent = function () {
          var self = this;
          var $close = self.$close;
          var modal = self.$modal.get(0);
  
          // 点击 $close 按钮，隐藏
          $close.click(function () {
              self.hide();
          });
  
          // 点击其他部分，隐藏
          E.$body.on('click', function (e) {
              if (!self.isShowing) {
                  return;
              }
              var trigger = e.target;
  
              // 获取菜单elem
              var menu = self.menu;
              var menuDom;
              if (menu) {
                  if (menu.selected) {
                      menuDom = menu.$domSelected.get(0);
                  } else {
                      menuDom = menu.$domNormal.get(0);
                  }
  
                  if (menuDom === trigger || $.contains(menuDom, trigger)) {
                      // 说明由本菜单点击触发的
                      return;
                  }
              }
  
              if (modal === trigger || $.contains(modal, trigger)) {
                  // 说明由本panel点击触发的
                  return;
              }
  
              // 其他情况，隐藏 panel
              self.hide();
          });
      };
  });
  // modal fn api
  _e(function (E, $) {
  
      var Modal = E.Modal;
  
      // 渲染
      Modal.fn._render = function () {
          var self = this;
          var editor = self.editor;
          var $modal = self.$modal;
  
          // $modal的z-index，在配置的z-index基础上再 +10
          $modal.css('z-index', editor.config.zindex + 10 + '');
  
          // 渲染到body最后面
          E.$body.append($modal);
  
          // 记录状态
          self.rendered = true;
      };
  
      // 定位
      Modal.fn._position = function () {
          var self = this;
          var $modal = self.$modal;
          var top = $modal.offset().top;
          var width = $modal.outerWidth();
          var height = $modal.outerHeight();
          var marginLeft = 0 - (width / 2);
          var marginTop = 0 - (height / 2);
          var sTop = E.$window.scrollTop();
  
          // 保证modal最顶部，不超过浏览器上边框
          if ((height / 2) > top) {
              marginTop = 0 - top;
          }
  
          $modal.css({
              'margin-left': marginLeft + 'px',
              'margin-top': (marginTop + sTop) + 'px'
          });
      };
  
      // 显示
      Modal.fn.show = function () {
          var self = this;
          var menu = self.menu;
          if (!self.rendered) {
              // 第一次show之前，先渲染
              self._render();
          }
  
          if (self.isShowing) {
              return;
          }
          // 记录状态
          self.isShowing = true;
  
          var $modal = self.$modal;
          $modal.show();
  
          // 定位
          self._position();
  
          // 激活菜单状态
          menu && menu.activeStyle(true);
      };
  
      // 隐藏
      Modal.fn.hide = function () {
          var self = this;
          var menu = self.menu;
          if (!self.isShowing) {
              return;
          }
          // 记录状态
          self.isShowing = false;
  
          // 隐藏
          var $modal = self.$modal;
          $modal.hide();
  
          // 菜单状态
          menu && menu.activeStyle(false);
      };
  });
  // txt 构造函数
  _e(function (E, $) {
  
      // 定义构造函数
      var Txt = function (editor) {
          this.editor = editor;
  
          // 初始化
          this.init();
      };
  
      Txt.fn = Txt.prototype;
  
      // 暴露给 E 即 window.wangEditor
      E.Txt = Txt;
  });
  // Txt.fn bind fn
  _e(function (E, $) {
  
      var Txt = E.Txt;
  
      // 初始化
      Txt.fn.init = function () {
          var self = this;
          var editor = self.editor;
          var $valueContainer = editor.$valueContainer;
          var currentValue = editor.getInitValue();
          var $txt;
  
          if ($valueContainer.get(0).nodeName === 'DIV') {
              // 如果传入生成编辑器的元素就是div，则直接使用
              $txt = $valueContainer;
              $txt.addClass("wangEditor-txt");
              $txt.attr('contentEditable', 'true');
          } else {
              // 如果不是div（是textarea），则创建一个div
              $txt = $(
                  '<div class="wangEditor-txt" contentEditable="true">' +
                      currentValue +
                  '</div>'
              );
          }
  
          // 试图最后插入一个空行，ready之后才行
          editor.ready(function () {
              self.insertEmptyP();
          });
  
          self.$txt = $txt;
  
          // 删除时，如果没有内容了，就添加一个 <p><br></p>
          self.contentEmptyHandle();
  
          // enter时，不能使用 div 换行
          self.bindEnterForDiv();
  
          // enter时，用 p 包裹 text
          self.bindEnterForText();
  
          // tab 插入4个空格
          self.bindTabEvent();
  
          // 处理粘贴内容
          self.bindPasteFilter();
  
          // $txt.formatText() 方法
          self.bindFormatText();
  
          // 定义 $txt.html() 方法
          self.bindHtml();
      };
  
      // 删除时，如果没有内容了，就添加一个 <p><br></p>
      Txt.fn.contentEmptyHandle = function () {
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
          var $p;
  
          $txt.on('keydown', function (e) {
              if (e.keyCode !== 8) {
                  return;
              }
              var txtHtml = $.trim($txt.html().toLowerCase());
              if (txtHtml === '<p><br></p>') {
                  // 如果最后还剩余一个空行，就不再继续删除了
                  e.preventDefault();
                  return;
              }
          });
  
          $txt.on('keyup', function (e) {
              if (e.keyCode !== 8) {
                  return;
              }
              var txtHtml = $.trim($txt.html().toLowerCase());
              // ff时用 txtHtml === '<br>' 判断，其他用 !txtHtml 判断
              if (!txtHtml || txtHtml === '<br>') {
                  // 内容空了
                  $p = $('<p><br/></p>');
                  $txt.html(''); // 一定要先清空，否则在 ff 下有问题
                  $txt.append($p);
                  editor.restoreSelectionByElem($p.get(0));
              }
          });
      };
  
      // enter时，不能使用 div 换行
      Txt.fn.bindEnterForDiv = function () {
          var tags = E.config.legalTags; // 配置中编辑器要求的合法标签，如 p head table blockquote ul ol 等
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
  
          var $keydownDivElem;
          function divHandler() {
              if (!$keydownDivElem) {
                  return;
              }
  
              var $pElem = $('<p>' + $keydownDivElem.html() + '</p>');
              $keydownDivElem.after($pElem);
              $keydownDivElem.remove();
          }
  
          $txt.on('keydown keyup', function (e) {
              if (e.keyCode !== 13) {
                  return;
              }
              // 查找合法标签
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getLegalTags(rangeElem);
              var $targetElem;
              var $pElem;
  
              if (!targetElem) {
                  // 没找到合法标签，就去查找 div
                  targetElem = editor.getSelfOrParentByName(rangeElem, 'div');
                  if (!targetElem) {
                      return;
                  }
                  $targetElem = $(targetElem);
  
                  if (e.type === 'keydown') {
                      // 异步执行（同步执行会出现问题）
                      $keydownDivElem = $targetElem;
                      setTimeout(divHandler, 0);
                  }
  
                  if (e.type === 'keyup') {
                      // 将 div 的内容移动到 p 里面，并移除 div
                      $pElem = $('<p>' + $targetElem.html() + '</p>');
                      $targetElem.after($pElem);
                      $targetElem.remove();
  
                      // 如果是回车结束，将选区定位到行首
                      editor.restoreSelectionByElem($pElem.get(0), 'start');
                  }
              }
          });
      };
  
      // enter时，用 p 包裹 text
      Txt.fn.bindEnterForText = function () {
          var self = this;
          var $txt = self.$txt;
          var handle;
          $txt.on('keyup', function (e) {
              if (e.keyCode !== 13) {
                  return;
              }
              if (!handle) {
                  handle = function() {
                      self.wrapImgAndText();
                  };
              }
              setTimeout(handle);
          });
      };
  
      // tab 时，插入4个空格
      Txt.fn.bindTabEvent = function () {
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
  
          $txt.on('keydown', function (e) {
              if (e.keyCode !== 9) {
                  // 只监听 tab 按钮
                  return;
              }
              // 如果浏览器支持 insertHtml 则插入4个空格。如果不支持，就不管了
              if (editor.queryCommandSupported('insertHtml')) {
                  editor.command(e, 'insertHtml', '&nbsp;&nbsp;&nbsp;&nbsp;');
              }
          });
      };
  
      // 处理粘贴内容
      Txt.fn.bindPasteFilter = function () {
          var self = this;
          var editor = self.editor;
          var resultHtml = '';  //存储最终的结果
          var $txt = self.$txt;
          var legalTags = editor.config.legalTags;
          var legalTagArr = legalTags.split(',');
  
          $txt.on('paste', function (e) {
              if (!editor.config.pasteFilter) {
                  // 配置中取消了粘贴过滤
                  return;
              }
  
              var currentNodeName = editor.getRangeElem().nodeName;
              if (currentNodeName === 'TD' || currentNodeName === 'TH') {
                  // 在表格的单元格中粘贴，忽略所有内容。否则会出现异常情况
                  return;
              }
  
              resultHtml = ''; // 先清空 resultHtml
  
              var pasteHtml, $paste;
              var data = e.clipboardData || e.originalEvent.clipboardData;
              var ieData = window.clipboardData;
  
              if (editor.config.pasteText) {
                  // 只粘贴纯文本
  
                  if (data && data.getData) {
                      // w3c
                      pasteHtml = data.getData('text/plain');
                  } else if (ieData && ieData.getData) {
                      // IE
                      pasteHtml = ieData.getData('text');
                  } else {
                      // 其他情况
                      return;
                  }
  
                  // 拼接为 <p> 标签
                  if (pasteHtml) {
                      resultHtml = '<p>' + pasteHtml + '</p>';
                  }
  
              } else {
                  // 粘贴过滤了样式的、只有标签的 html
  
                  if (data && data.getData) {
                      // w3c
  
                      // 获取粘贴过来的html
                      pasteHtml = data.getData('text/html');
                      if (pasteHtml) {
                          // 创建dom
                          $paste = $('<div>' + pasteHtml + '</div>');
                          // 处理，并将结果存储到 resultHtml 『全局』变量
                          handle($paste.get(0));
                      } else {
                          // 得不到html，试图获取text
                          pasteHtml = data.getData('text/plain');
                          if (pasteHtml) {
                              // 替换特殊字符
                              pasteHtml = pasteHtml.replace(/[ ]/g, '&nbsp;')
                                                   .replace(/</g, '&lt;')
                                                   .replace(/>/g, '&gt;')
                                                   .replace(/\n/g, '</p><p>');
                              // 拼接
                              resultHtml = '<p>' + pasteHtml + '</p>';
  
                              // 查询链接
                              resultHtml = resultHtml.replace(/<p>(https?:\/\/.*?)<\/p>/ig, function (match, link) {
                                  return '<p><a href="' + link + '" target="_blank">' + link + '</p>';
                              });
                          }
                      }
                      
                  } else if (ieData && ieData.getData) {
                      // IE 直接从剪切板中取出纯文本格式
                      resultHtml = ieData.getData('text');
                      if (!resultHtml) {
                          return;
                      }
                      // 拼接为 <p> 标签
                      resultHtml = '<p>' + resultHtml + '</p>';
                      resultHtml = resultHtml.replace((new RegExp('\n', 'g'), '</p><p>'));
                  } else {
                      // 其他情况
                      return;
                  }
              }
  
              // 执行命令
              if (resultHtml) {
                  editor.command(e, 'insertHtml', resultHtml);
  
                  // 删除内容为空的 p 和嵌套的 p
                  self.clearEmptyOrNestP();
              }
          });
  
          // 处理粘贴的内容
          function handle(elem) {
              if (!elem || !elem.nodeType || !elem.nodeName) {
                  return;
              }
              var $elem;
              var nodeName = elem.nodeName.toLowerCase();
              var nodeType = elem.nodeType;
  
              // 只处理文本和普通node标签
              if (nodeType !== 3 && nodeType !== 1) {
                  return;
              }
  
              $elem = $(elem);
  
              // 如果是容器，则继续深度遍历
              if (nodeName === 'div') {
                  $.each(elem.childNodes, function () {
                      // elem.childNodes 可获取TEXT节点，而 $elem.children() 就获取不到
                      handle(this);
                  });
                  return;
              }
              
              if (legalTagArr.indexOf(nodeName) >= 0) {
                  // 如果是合法标签之内的，则根据元素类型，获取值
                  resultHtml += getResult(elem);
              } else if (nodeType === 3) {
                  // 如果是文本，则直接插入 p 标签
                  resultHtml += '<p>' + elem.textContent + '</p>';
              } else if (nodeName === 'br') {
                  // <br>保留
                  resultHtml += '<br/>';
              }
              else {
                  // 忽略的标签
                  if (['meta', 'style', 'script', 'object', 'form', 'iframe', 'hr'].indexOf(nodeName) >= 0) {
                      return;
                  }
                  // 其他标签，移除属性，插入 p 标签
                  $elem = $(removeAttrs(elem));
                  // 注意，这里的 clone() 是必须的，否则会出错
                  resultHtml += $('<div>').append($elem.clone()).html();
              }
          }
  
          // 获取元素的结果
          function getResult(elem) {
              var nodeName = elem.nodeName.toLowerCase();
              var $elem;
              var htmlForP = '';
              var htmlForLi = '';
  
              if (['blockquote'].indexOf(nodeName) >= 0) {
  
                  // 直接取出元素text即可
                  $elem = $(elem);
                  return '<' + nodeName + '>' + $elem.text() + '</' + nodeName + '>';
  
              } else if (['p', 'h1', 'h2', 'h3', 'h4', 'h5'].indexOf(nodeName) >= 0) {
  
                  //p head 取出 text 和链接
                  elem = removeAttrs(elem);
                  $elem = $(elem);
                  htmlForP = $elem.html();
  
                  // 剔除 a img 之外的元素
                  htmlForP = htmlForP.replace(/<.*?>/ig, function (tag) {
                      if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
                          return tag;
                      } else {
                          return '';
                      }
                  });
  
                  return '<' + nodeName + '>' + htmlForP + '</' + nodeName + '>';
  
              } else if (['ul', 'ol'].indexOf(nodeName) >= 0) {
                  
                  // ul ol元素，获取子元素（li元素）的text link img，再拼接
                  $elem = $(elem);
                  $elem.children().each(function () {
                      var $li = $(removeAttrs(this));
                      var html = $li.html();
  
                      html = html.replace(/<.*?>/ig, function (tag) {
                          if (tag === '</a>' || tag.indexOf('<a ') === 0 || tag.indexOf('<img ') === 0) {
                              return tag;
                          } else {
                              return '';
                          }
                      });
  
                      htmlForLi += '<li>' + html + '</li>';
                  });
                  return '<' + nodeName + '>' + htmlForLi + '</' + nodeName + '>';
              
              } else {
                  
                  // 其他元素，移除元素属性
                  $elem = $(removeAttrs(elem));
                  return $('<div>').append($elem).html();
              }
          }
  
          // 移除一个元素（子元素）的attr
          function removeAttrs(elem) {
              var attrs = elem.attributes || [];
              var attrNames = [];
              var exception = ['href', 'target', 'src', 'alt', 'rowspan', 'colspan']; //例外情况
  
              // 先存储下elem中所有 attr 的名称
              $.each(attrs, function (key, attr) {
                  if (attr && attr.nodeType === 2) {
                      attrNames.push(attr.nodeName);
                  }
              });
              // 再根据名称删除所有attr
              $.each(attrNames, function (key, attr) {
                  if (exception.indexOf(attr) < 0) {
                      // 除了 exception 规定的例外情况，删除其他属性
                      elem.removeAttribute(attr);
                  }
              });
  
  
              // 递归子节点
              var children = elem.childNodes;
              if (children.length) {
                  $.each(children, function (key, value) {
                      removeAttrs(value);
                  });
              }
  
              return elem;
          }
      };
  
      // 绑定 $txt.formatText() 方法
      Txt.fn.bindFormatText = function () {
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
          var legalTags = E.config.legalTags;
          var legalTagArr = legalTags.split(',');
          var length = legalTagArr.length;
          var regArr = [];
  
          // 将 E.config.legalTags 配置的有效字符，生成正则表达式
          $.each(legalTagArr, function (k, tag) {
              var reg = '\>\\s*\<(' + tag + ')\>';
              regArr.push(new RegExp(reg, 'ig'));
          });
  
          // 增加 li 
          regArr.push(new RegExp('\>\\s*\<(li)\>', 'ig'));
  
          // 增加 tr
          regArr.push(new RegExp('\>\\s*\<(tr)\>', 'ig'));
  
          // 增加 code
          regArr.push(new RegExp('\>\\s*\<(code)\>', 'ig'));
  
          // 生成 formatText 方法
          $txt.formatText = function () {
              var $temp = $('<div>');
              var html = $txt.html();
  
              // 去除空格
              html = html.replace(/\s*</ig, '<');
  
              // 段落、表格之间换行
              $.each(regArr, function (k, reg) {
                  if (!reg.test(html)) {
                      return;
                  }
                  html = html.replace(reg, function (matchStr, tag) {
                      return '>\n<' + tag + '>';
                  });
              });
  
              $temp.html(html);
              return $temp.text();
          };
      };
  
      // 定制 $txt.html 方法
      Txt.fn.bindHtml = function () {
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
          var $valueContainer = editor.$valueContainer;
          var valueNodeName = editor.valueNodeName;
  
          $txt.html = function (html) {
              var result;
  
              if (valueNodeName === 'div') {
                  // div 生成的编辑器，取值、赋值，都直接触发jquery的html方法
                  result = $.fn.html.call($txt, html);
              }
  
              // textarea 生成的编辑器，则需要考虑赋值时，也给textarea赋值
  
              if (html === undefined) {
                  // 取值，直接触发jquery原生html方法
                  result = $.fn.html.call($txt);
  
                  // 替换 html 中，src和href属性中的 & 字符。
                  // 因为 .html() 或者 .innerHTML 会把所有的 & 字符都改成 &amp; 但是 src 和 href 中的要保持 &
                  result = result.replace(/(href|src)\=\"(.*)\"/igm, function (a, b, c) {
                      return b + '="' + c.replace('&amp;', '&') + '"';
                  });
              } else {
                  // 赋值，需要同时给 textarea 赋值
                  result = $.fn.html.call($txt, html);
                  $valueContainer.val(html);
              }
  
              if (html === undefined) {
                  return result;
              } else {
                  // 手动触发 change 事件，因为 $txt 监控了 change 事件来判断是否需要执行 editor.onchange 
                  $txt.change();
              }
          };
      };
  });
  // Txt.fn api
  _e(function (E, $) {
  
      var Txt = E.Txt;
  
      var txtChangeEventNames = 'propertychange change click keyup input paste';
  
      // 渲染
      Txt.fn.render = function () {
          var $txt = this.$txt;
          var $editorContainer = this.editor.$editorContainer;
          $editorContainer.append($txt);
      };
  
      // 计算高度
      Txt.fn.initHeight = function () {
          var editor = this.editor;
          var $txt = this.$txt;
          var valueContainerHeight = editor.$valueContainer.height();
          var menuHeight = editor.menuContainer.height();
          var txtHeight = valueContainerHeight - menuHeight;
  
          // 限制最小为 50px
          txtHeight = txtHeight < 50 ? 50 : txtHeight;
  
          $txt.height(txtHeight);
  
          // 记录原始高度
          editor.valueContainerHeight = valueContainerHeight;
  
          // 设置 max-height
          this.initMaxHeight(txtHeight, menuHeight);
      };
  
      // 计算最大高度
      Txt.fn.initMaxHeight = function (txtHeight, menuHeight) {
          var editor = this.editor;
          var $menuContainer = editor.menuContainer.$menuContainer;
          var $txt = this.$txt;
          var $wrap = $('<div>');
  
          // 需要浏览器支持 max-height，否则不管
          if (window.getComputedStyle && 'max-height'in window.getComputedStyle($txt.get(0))) {
              // 获取 max-height 并判断是否有值
              var maxHeight = parseInt(editor.$valueContainer.css('max-height'));
              if (isNaN(maxHeight)) {
                  return;
              }
  
              // max-height 和『全屏』暂时有冲突
              if (editor.menus.fullscreen) {
                  E.warn('max-height和『全屏』菜单一起使用时，会有一些问题尚未解决，请暂时不要两个同时使用');
                  return;
              }
  
              // 标记
              editor.useMaxHeight = true;
  
              // 设置maxheight
              $wrap.css({
                  'max-height': (maxHeight - menuHeight) + 'px',
                  'overflow-y': 'auto'
              });
              $txt.css({
                  'height': 'auto',
                  'overflow-y': 'visible',
                  'min-height': txtHeight + 'px'
              });
  
              // 滚动式，菜单阴影
              $wrap.on('scroll', function () {
                  if ($txt.parent().scrollTop() > 10) {
                      $menuContainer.addClass('wangEditor-menu-shadow');
                  } else {
                      $menuContainer.removeClass('wangEditor-menu-shadow');
                  }
              });
  
              // 需在编辑器区域外面再包裹一层
              $txt.wrap($wrap);
          }
      };
  
      // 保存选区
      Txt.fn.saveSelectionEvent = function () {
          var $txt = this.$txt;
          var editor = this.editor;
          var timeoutId;
          var dt = Date.now();
  
          function save() {
              editor.saveSelection();
          }
  
          // 同步保存选区
          function saveSync() {
              // 100ms之内，不重复保存
              if (Date.now() - dt < 100) {
                  return;
              }
  
              dt = Date.now();
              save();
          }
  
          // 异步保存选区
          function saveAync() {
              // 节流，防止高频率重复操作
              if (timeoutId) {
                  clearTimeout(timeoutId);
              }
              timeoutId = setTimeout(save, 300);
          }
  
          // txt change 、focus、blur 时随时保存选区
          $txt.on(txtChangeEventNames + ' focus blur', function (e) {
              // 先同步保存选区，为了让接下来就马上要执行 editor.getRangeElem() 的程序
              // 能够获取到正确的 rangeElem
              saveSync();
  
              // 再异步保存选区，为了确定更加准确的选区，为后续的操作做准备
              saveAync();
          });
  
          // 鼠标拖拽选择时，可能会拖拽到编辑器区域外面再松手，此时 $txt 就监听不到 click事件了
          $txt.on('mousedown', function () {
              $txt.on('mouseleave.saveSelection', function (e) {
                  // 先同步后异步，如上述注释
                  saveSync();
                  saveAync();
  
                  // 顺道吧菜单状态也更新了
                  editor.updateMenuStyle();
              });
          }).on('mouseup', function () {
              $txt.off('mouseleave.saveSelection');
          });
          
      };
  
      // 随时更新 value
      Txt.fn.updateValueEvent = function () {
          var $txt = this.$txt;
          var editor = this.editor;
          var timeoutId, oldValue;
  
          // 触发 onchange 事件
          function doOnchange() {
              var val = $txt.html();
              if (oldValue === val) {
                  // 无变化
                  return;
              }
  
              // 触发 onchange 事件
              if (editor.onchange && typeof editor.onchange === 'function') {
                  editor.onchange.call(editor);
              }
  
              // 更新内容
              editor.updateValue();
  
              // 记录最新内容
              oldValue = val;
          }
  
          // txt change 时随时更新内容
          $txt.on(txtChangeEventNames, function (e) {
              // 初始化
              if (oldValue == null) {
                  oldValue = $txt.html();
              }
  
              // 监控内容变化（停止操作 100ms 之后立即执行）
              if (timeoutId) {
                  clearTimeout(timeoutId);
              }
              timeoutId = setTimeout(doOnchange, 100);
          });
      };
  
      // 随时更新 menustyle
      Txt.fn.updateMenuStyleEvent = function () {
          var $txt = this.$txt;
          var editor = this.editor;
  
          // txt change 时随时更新内容
          $txt.on(txtChangeEventNames, function (e) {
              editor.updateMenuStyle();
          });
      };
  
      // 最后插入试图插入 <p><br><p>
      Txt.fn.insertEmptyP = function () {
          var $txt = this.$txt;
          var $children = $txt.children();
  
          if ($children.length === 0) {
              $txt.append($('<p><br></p>'));
              return;
          }
  
          if ($.trim($children.last().html()).toLowerCase() !== '<br>') {
              $txt.append($('<p><br></p>'));
          }
      };
  
      // 将编辑器暴露出来的文字和图片，都用 p 来包裹
      Txt.fn.wrapImgAndText = function () {
          var $txt = this.$txt;
          var $imgs = $txt.children('img');
          var txt = $txt[0];
          var childNodes = txt.childNodes;
          var childrenLength = childNodes.length;
          var i, childNode, p;
  
          // 处理图片
          $imgs.length && $imgs.each(function () {
              $(this).wrap('<p>');
          });
  
          // 处理文字
          for (i = 0; i < childrenLength; i++) {
              childNode = childNodes[i];
              if (childNode.nodeType === 3 && childNode.textContent && $.trim(childNode.textContent)) {
                  $(childNode).wrap('<p>');
              }
          }
      };
  
      // 清空内容为空的<p>，以及重复包裹的<p>（在windows下的chrome粘贴文字之后，会出现上述情况）
      Txt.fn.clearEmptyOrNestP = function () {
          var $txt = this.$txt;
          var $pList = $txt.find('p');
  
          $pList.each(function () {
              var $p = $(this);
              var $children = $p.children();
              var childrenLength = $children.length;
              var $firstChild;
              var content = $.trim($p.html());
  
              // 内容为空的p
              if (!content) {
                  $p.remove();
                  return;
              }
  
              // 嵌套的p
              if (childrenLength === 1) {
                  $firstChild = $children.first();
                  if ($firstChild.get(0) && $firstChild.get(0).nodeName === 'P') {
                      $p.html( $firstChild.html() );
                  }
              }
          });
      };
  
      // 获取 scrollTop
      Txt.fn.scrollTop = function (val) {
          var self = this;
          var editor = self.editor;
          var $txt = self.$txt;
  
          if (editor.useMaxHeight) {
              return $txt.parent().scrollTop(val);
          } else {
              return $txt.scrollTop(val);
          }
      };
  
      // 鼠标hover时候，显示p、head的高度
      Txt.fn.showHeightOnHover = function () {
          var editor = this.editor;
          var $editorContainer = editor.$editorContainer;
          var menuContainer = editor.menuContainer;
          var $txt = this.$txt;
          var $tip = $('<i class="height-tip"><i>');
          var isTipInTxt = false;
  
          function addAndShowTip($target) {
              if (!isTipInTxt) {
                  $editorContainer.append($tip);
                  isTipInTxt = true;
              }
  
              var txtTop = $txt.position().top;
              var txtHeight = $txt.outerHeight();
  
              var height = $target.height();
              var top = $target.position().top;
              var marginTop = parseInt($target.css('margin-top'), 10);
              var paddingTop = parseInt($target.css('padding-top'), 10);
              var marginBottom = parseInt($target.css('margin-bottom'), 10);
              var paddingBottom = parseInt($target.css('padding-bottom'), 10);
  
              // 计算初步的结果
              var resultHeight = height + paddingTop + marginTop + paddingBottom + marginBottom;
              var resultTop = top + menuContainer.height();
              
              // var spaceValue;
  
              // // 判断是否超出下边界
              // spaceValue = (resultTop + resultHeight) - (txtTop + txtHeight);
              // if (spaceValue > 0) {
              //     resultHeight = resultHeight - spaceValue;
              // }
  
              // // 判断是否超出了下边界
              // spaceValue = txtTop > resultTop;
              // if (spaceValue) {
              //     resultHeight = resultHeight - spaceValue;
              //     top = top + spaceValue;
              // }
  
              // 按照最终结果渲染
              $tip.css({
                  height: height + paddingTop + marginTop + paddingBottom + marginBottom,
                  top: top + menuContainer.height()
              });
          }
          function removeTip() {
              if (!isTipInTxt) {
                  return;
              }
              $tip.remove();
              isTipInTxt = false;
          }
  
          $txt.on('mouseenter', 'ul,ol,blockquote,p,h1,h2,h3,h4,h5,table,pre', function (e) {
              addAndShowTip($(e.currentTarget));
          }).on('mouseleave', function () {
              removeTip();
          });
      };
  
  });
  // 工具函数
  _e(function (E, $) {
  
      // IE8 [].indexOf()
      if(!Array.prototype.indexOf){
          //IE低版本不支持 arr.indexOf 
          Array.prototype.indexOf = function(elem){
              var i = 0,
                  length = this.length;
              for(; i<length; i++){
                  if(this[i] === elem){
                      return i;
                  }
              }
              return -1;
          };
          //IE低版本不支持 arr.lastIndexOf
          Array.prototype.lastIndexOf = function(elem){
              var length = this.length;
              for(length = length - 1; length >= 0; length--){
                  if(this[length] === elem){
                      return length;
                  }
              }
              return -1;
          };
      }
  
      // IE8 Date.now()
      if (!Date.now) {
          Date.now = function () {
              return new Date().valueOf(); 
          };
      }
  
      // console.log && console.warn && console.error
      var console = window.console;
      var emptyFn = function () {};
      $.each(['info', 'log', 'warn', 'error'], function (key, value) {
          if (console == null) {
              E[value] = emptyFn;
          } else {
              E[value] = function (info) {
                  // 通过配置来控制打印输出
                  if (E.config && E.config.printLog) {
                      console[value]('wangEditor提示: ' + info);
                  }
              };
          }
      });
  
      // 获取随机数
      E.random = function () {
          return Math.random().toString().slice(2);
      };
  
      // 浏览器是否支持 placeholder
      E.placeholder = 'placeholder' in document.createElement('input');
  
      // 兼容IE8的 input placeholder
      E.placeholderForIE8 = function ($container) {
          if (E.placeholder) {
              return;
          }
          $container.find('input[placeholder]').each(function () {
              var $input = $(this);
              var placeholder = $input.attr('placeholder');
  
              if ($input.val() === '') {
                  $input.css('color', '#666');
                  $input.val(placeholder);
  
                  $input.on('focus.placeholder click.placeholder', function () {
                      $input.val('');
                      $input.css('color', '#333');
                      $input.off('focus.placeholder click.placeholder');
                  });
              }
          });
      };
  });
  // 语言包
  _e(function (E, $) {
      E.langs = {};
      
      // 中文
      E.langs['zh-cn'] = {
          bold: '粗体',
          underline: '下划线',
          italic: '斜体',
          forecolor: '文字颜色',
          bgcolor: '背景色',
          strikethrough: '删除线',
          eraser: '清空格式',
          source: '源码',
          quote: '引用',
          fontfamily: '字体',
          fontsize: '字号',
          head: '标题',
          orderlist: '有序列表',
          unorderlist: '无序列表',
          alignleft: '左对齐',
          aligncenter: '居中',
          alignright: '右对齐',
          link: '链接',
          text: '文本',
          submit: '提交',
          cancel: '取消',
          unlink: '取消链接',
          table: '表格',
          emotion: '表情',
          img: '图片',
          video: '视频',
          'width': '宽',
          'height': '高',
          location: '位置',
          loading: '加载中',
          searchlocation: '搜索位置',
          dynamicMap: '动态地图',
          clearLocation: '清除位置',
          langDynamicOneLocation: '动态地图只能显示一个位置',
          insertcode: '插入代码',
          undo: '撤销',
          redo: '重复',
          fullscreen: '全屏',
          openLink: '打开链接'
      };
  
      // 英文
      E.langs.en = {
          bold: 'Bold',
          underline: 'Underline',
          italic: 'Italic',
          forecolor: 'Color',
          bgcolor: 'Backcolor',
          strikethrough: 'Strikethrough',
          eraser: 'Eraser',
          source: 'Codeview',
          quote: 'Quote',
          fontfamily: 'Font family',
          fontsize: 'Font size',
          head: 'Head',
          orderlist: 'Ordered list',
          unorderlist: 'Unordered list',
          alignleft: 'Align left',
          aligncenter: 'Align center',
          alignright: 'Align right',
          link: 'Insert link',
          text: 'Text',
          submit: 'Submit',
          cancel: 'Cancel',
          unlink: 'Unlink',
          table: 'Table',
          emotion: 'Emotions',
          img: 'Image',
          video: 'Video',
          'width': 'width',
          'height': 'height',
          location: 'Location',
          loading: 'Loading',
          searchlocation: 'search',
          dynamicMap: 'Dynamic',
          clearLocation: 'Clear',
          langDynamicOneLocation: 'Only one location in dynamic map',
          insertcode: 'Insert Code',
          undo: 'Undo',
          redo: 'Redo',
          fullscreen: 'Full screnn',
          openLink: 'open link'
      };
  });
  // 全局配置
  _e(function (E, $) {
  
      E.config = {};
  
      // 全屏时的 z-index
      E.config.zindex = 10000;
  
      // 是否打印log
      E.config.printLog = true;
  
      // 菜单吸顶：false - 不吸顶；number - 吸顶，值为top值
      E.config.menuFixed = 0;
  
      // 编辑源码时，过滤 javascript
      E.config.jsFilter = true;
  
      // 编辑器允许的标签
      E.config.legalTags = 'p,h1,h2,h3,h4,h5,h6,blockquote,table,ul,ol,pre';
  
      // 语言包
      E.config.lang = E.langs['zh-cn'];
  
      // 菜单配置
      E.config.menus = [
          'source',
          '|',
          'bold',
          'underline',
          'italic',
          'strikethrough',
          'eraser',
          'forecolor',
          'bgcolor',
          '|',
          'quote',
          'fontfamily',
          'fontsize',
          'head',
          'unorderlist',
          'orderlist',
          'alignleft',
          'aligncenter',
          'alignright',
          '|',
          'link',
          'unlink',
          'table',
          'emotion',
          '|',
          'img',
          'video',
          'location',
          'insertcode',
          '|',
          'undo',
          'redo',
          'fullscreen'
      ];
  
      // 颜色配置
      E.config.colors = {
          // 'value': 'title'
          '#880000': '暗红色',
          '#800080': '紫色',
          '#ff0000': '红色',
          '#ff00ff': '鲜粉色',
          '#000080': '深蓝色',
          '#0000ff': '蓝色',
          '#00ffff': '湖蓝色',
          '#008080': '蓝绿色',
          '#008000': '绿色',
          '#808000': '橄榄色',
          '#00ff00': '浅绿色',
          '#ffcc00': '橙黄色',
          '#808080': '灰色',
          '#c0c0c0': '银色',
          '#000000': '黑色',
          '#ffffff': '白色'
      };
  
      // 字体
      E.config.familys = [
          '宋体', '黑体', '楷体', '微软雅黑',
          'Arial', 'Verdana', 'Georgia',
          'Times New Roman', 'Microsoft JhengHei',
          'Trebuchet MS', 'Courier New', 'Impact', 'Comic Sans MS'
      ];
  
      // 字号
      E.config.fontsizes = {
          // 格式：'value': 'title'
          1: '10px',
          2: '13px',
          3: '16px',
          4: '19px',
          5: '22px',
          6: '25px',
          7: '28px'
      };
  
      // 表情包
      E.config.emotionsShow = 'icon'; // 显示项，默认为'icon'，也可以配置成'value'
      E.config.emotions = {
          'default': {
              title: '默认',
              data: '../emotions.data'
          },
          'weibo': {
              title: '微博表情',
              data: [
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif',
                      value: '[草泥马]'    
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif',
                      value: '[神马]'    
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif',
                      value: '[浮云]'    
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c9/geili_thumb.gif',
                      value: '[给力]'    
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/f2/wg_thumb.gif',
                      value: '[围观]'    
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/70/vw_thumb.gif',
                      value: '[威武]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/6e/panda_thumb.gif',
                      value: '[熊猫]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/81/rabbit_thumb.gif',
                      value: '[兔子]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/otm_thumb.gif',
                      value: '[奥特曼]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/15/j_thumb.gif',
                      value: '[囧]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/89/hufen_thumb.gif',
                      value: '[互粉]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/c4/liwu_thumb.gif',
                      value: '[礼物]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/ac/smilea_thumb.gif',
                      value: '[呵呵]'
                  },
                  {
                      icon: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/0b/tootha_thumb.gif',
                      value: '[哈哈]'
                  }
              ]
          }
      };
  
      // 百度地图的key
      E.config.mapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
  
      // 上传图片的配置
      // server地址
      E.config.uploadImgUrl = '';
      // 超时时间
      E.config.uploadTimeout = 20 * 1000;
      // 用于存储上传回调事件
      E.config.uploadImgFns = {};
      // 自定义上传图片的filename
      // E.config.uploadImgFileName = 'customFileName';
  
      // 自定义上传，设置为 true 之后，显示上传图标
      E.config.customUpload = false;
      // 自定义上传的init事件
      // E.config.customUploadInit = function () {....};
  
      // 自定义上传时传递的参数（如 token）
      E.config.uploadParams = {
          /* token: 'abcdef12345' */
      };
  
      // 自定义上传是的header参数
      E.config.uploadHeaders = {
           /* 'Accept' : 'text/x-json' */
      };
  
      // 是否过滤粘贴内容
      E.config.pasteFilter = true;
  
      // 是否粘贴纯文本，当 editor.config.pasteFilter === false 时候，此配置将失效
      E.config.pasteText = false;
  
  });
  // 全局UI
  _e(function (E, $) {
  
       E.UI = {};
  
       // 为菜单自定义配置的UI
       E.UI.menus = {
          // 这个 default 不加引号，在 IE8 会报错
          'default': {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-command"></i></a>',
              selected: '.selected'
          },
          bold: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-bold"></i></a>',
              selected: '.selected'
          },
          underline: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-underline"></i></a>',
              selected: '.selected'
          },
          italic: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-italic"></i></a>',
              selected: '.selected'
          },
          forecolor: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-pencil"></i></a>',
              selected: '.selected'
          },
          bgcolor: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-brush"></i></a>',
              selected: '.selected'
          },
          strikethrough: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-strikethrough"></i></a>',
              selected: '.selected'
          },
          eraser: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-eraser"></i></a>',
              selected: '.selected'
          },
          quote: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-quotes-left"></i></a>',
              selected: '.selected'
          },
          source: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-code"></i></a>',
              selected: '.selected'
          },
          fontfamily: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-font2"></i></a>',
              selected: '.selected'
          },
          fontsize: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-text-height"></i></a>',
              selected: '.selected'
          },
          head: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-header"></i></a>',
              selected: '.selected'
          },
          orderlist: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-numbered"></i></a>',
              selected: '.selected'
          },
          unorderlist: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-list-bullet"></i></a>',
              selected: '.selected'
          },
          alignleft: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-left"></i></a>',
              selected: '.selected'
          },
          aligncenter: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-center"></i></a>',
              selected: '.selected'
          },
          alignright: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-align-right"></i></a>',
              selected: '.selected'
          },
          link: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-link"></i></a>',
              selected: '.selected'
          },
          unlink: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-unlink"></i></a>',
              selected: '.selected'
          },
          table: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-table"></i></a>',
              selected: '.selected'
          },
          emotion: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-happy"></i></a>',
              selected: '.selected'
          },
          img: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-picture"></i></a>',
              selected: '.selected'
          },
          video: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-play"></i></a>',
              selected: '.selected'
          },
          location: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-location"></i></a>',
              selected: '.selected'
          },
          insertcode: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-terminal"></i></a>',
              selected: '.selected'
          },
          undo: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-ccw"></i></a>',
              selected: '.selected'
          },
          redo: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-cw"></i></a>',
              selected: '.selected'
          },
          fullscreen: {
              normal: '<a href="#" tabindex="-1"><i class="wangeditor-menu-img-enlarge2"></i></a>',
              selected: '<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-shrink2"></i></a>'
          }
       };
       
  });
  // 对象配置
  _e(function (E, $) {
  
      E.fn.initDefaultConfig = function () {
          var editor = this;
          editor.config = $.extend({}, E.config);
          editor.UI = $.extend({}, E.UI);
      };
  
  });
  // 增加 container
  _e(function (E, $) {
  
      E.fn.addEditorContainer = function () {
          this.$editorContainer = $('<div class="wangEditor-container"></div>');
      };
  
  });
  // 增加编辑区域对象
  _e(function (E, $) {
  
      E.fn.addTxt = function () {
          var editor = this;
          var txt = new E.Txt(editor);
  
          editor.txt = txt;
      };
  
  });
  // 增加menuContainer对象
  _e(function (E, $) {
  
      E.fn.addMenuContainer = function () {
          var editor = this;
          editor.menuContainer = new E.MenuContainer(editor);
      };
  
  });
  // 增加menus
  _e(function (E, $) {
  
      // 存储创建菜单的函数
      E.createMenuFns = [];
      E.createMenu = function (fn) {
          E.createMenuFns.push(fn);
      };
  
      // 创建所有菜单
      E.fn.addMenus = function () {
          var editor = this;
          var menuIds = editor.config.menus;
  
          // 检验 menuId 是否在配置中存在
          function check(menuId) {
              if (menuIds.indexOf(menuId) >= 0) {
                  return true;
              }
              return false;
          }
  
          // 遍历所有的菜单创建函数，并执行
          $.each(E.createMenuFns, function (k, createMenuFn) {
              createMenuFn.call(editor, check);
          });
      };
  
  });
  // bold菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'bold';
          if (!check(menuId)) {
              return;
          }
  
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.bold,
              commandName: 'Bold'
          });
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 如果选区有内容，则执行基础命令
                  editor.command(e, 'Bold');
              } else {
                  // 如果选区没有内容
                  editor.commandForElem('b,strong,h1,h2,h3,h4,h5', e, 'Bold');
              }
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // underline菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'underline';
          if (!check(menuId)) {
              return;
          }
  
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.underline,
              commandName: 'Underline'
          });
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 如果选区有内容，则执行基础命令
                  editor.command(e, 'Underline');
              } else {
                  // 如果选区没有内容
                  editor.commandForElem('u,a', e, 'Underline');
              }
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // italic 菜单
  _e(function (E, $) {
      
      E.createMenu(function (check) {
          var menuId = 'italic';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.italic,
              commandName: 'Italic'
          });
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 如果选区有内容，则执行基础命令
                  editor.command(e, 'Italic');
              } else {
                  // 如果选区没有内容
                  editor.commandForElem('i', e, 'Italic');
              }
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // forecolor 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'forecolor';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var configColors = editor.config.colors;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.forecolor
          });
  
          // 创建 dropPanel
          var $content = $('<div></div>');
          $.each(configColors, function (k, v) {
              $content.append(
                  [
                      '<a href="#" class="color-item"',
                      '    title="' + v + '" commandValue="' + k + '" ',
                      '    style="color: ' + k + '" ',
                      '><i class="wangeditor-menu-img-pencil"></i></a>'
                  ].join('')
              );
          });
          $content.on('click', 'a[commandValue]', function (e) {
              // 执行命令
              var $elem = $(this);
              var commandValue = $elem.attr('commandValue');
  
              if (menu.selected && editor.isRangeEmpty()) {
                  // 当前处于选中状态，并且选中内容为空
                  editor.commandForElem('font[color]', e, 'forecolor', commandValue);
              } else {
                  // 当前未处于选中状态，或者有选中内容。则执行默认命令
                  editor.command(e, 'forecolor', commandValue);
              }
          });
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 125
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[color]');
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // bgcolor 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'bgcolor';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var configColors = editor.config.colors;
  
          // 检查元素是否有 background-color: 内联样式
          function checkElemFn(elem) {
              var cssText;
              if (elem && elem.style && elem.style.cssText != null) {
                  cssText = elem.style.cssText;
                  if (cssText && cssText.indexOf('background-color:') >= 0) {
                      return true;
                  }
              }
              return false;
          }
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.bgcolor
          });
  
          // 创建 dropPanel
          var $content = $('<div></div>');
          $.each(configColors, function (k, v) {
              $content.append(
                  [
                      '<a href="#" class="color-item"',
                      '    title="' + v + '" commandValue="' + k + '" ',
                      '    style="color: ' + k + '" ',
                      '><i class="wangeditor-menu-img-brush"></i></a>'
                  ].join('')
              );
          });
          $content.on('click', 'a[commandValue]', function (e) {
              // 执行命令
  
              var $elem = $(this);
              var commandValue = $elem.attr('commandValue');
  
              if (menu.selected && editor.isRangeEmpty()) {
                  // 当前处于选中状态，并且选中内容为空。使用 commandForElem 执行命令
                  editor.commandForElem({
                      selector: 'span,font',
                      check: checkElemFn
                  }, e, 'BackColor', commandValue);
              } else {
                  // 当前未处于选中状态，或者有选中内容。则执行默认命令
                  editor.command(e, 'BackColor', commandValue);
              }
          });
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 125
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'span,font', checkElemFn);
              
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // strikethrough 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'strikethrough';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.strikethrough,
              commandName: 'StrikeThrough'
          });
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 如果选区有内容，则执行基础命令
                  editor.command(e, 'StrikeThrough');
              } else {
                  // 如果选区没有内容
                  editor.commandForElem('strike', e, 'StrikeThrough');
              }
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // eraser 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'eraser';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.eraser,
              commandName: 'RemoveFormat'
          });
  
          // 定义点击事件
          menu.clickEvent = function (e) {
              var isRangeEmpty = editor.isRangeEmpty();
  
              if (!isRangeEmpty) {
                  // 选区不是空的，则执行默认命令
                  editor.command(e, 'RemoveFormat');
                  return;
              }
  
              var $clearElem;
  
              // 自定义的命令函数
              function commandFn() {
                  var editor = this;
                  var rangeElem;
                  var pElem, $pElem;
                  var quoteElem, $quoteElem;
                  var listElem, $listElem;
  
                  // 获取选区 elem
                  rangeElem = editor.getRangeElem();
                  // 第一步，获取 quote 父元素
                  quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
                  if (quoteElem) {
                      $quoteElem = $(quoteElem);
                      $clearElem = $('<p>' + $quoteElem.text() + '</p>');
                      $quoteElem.after($clearElem).remove();
                  }
                  // 第二步，获取 p h 父元素
                  pElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5');
                  if (pElem) {
                      $pElem = $(pElem);
                      $clearElem = $('<p>' + $pElem.text() + '</p>');
                      $pElem.after($clearElem).remove();
                  }
                  // 第三步，获取list
                  listElem = editor.getSelfOrParentByName(rangeElem, 'ul,ol');
                  if (listElem) {
                      $listElem = $(listElem);
                      $clearElem = $('<p>' + $listElem.text() + '</p>');
                      $listElem.after($clearElem).remove();
                  }
              }
  
              // 自定义 callback 事件
              function callback() {
                  // callback中，设置range为clearElem
                  var editor = this;
                  if ($clearElem) {
                      editor.restoreSelectionByElem($clearElem.get(0));
                  }
              }
  
              // 执行自定义命令
              editor.customCommand(e, commandFn, callback);
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // source 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'source';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var txtHtml;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.source
          });
  
          menu.isShowCode = false;
  
          // 更新内容
          function updateValue() {
              var $code = menu.$codeTextarea;
              var $txt = editor.txt.$txt;
              var value = $.trim($code.val()); // 取值
  
              if (!value) {
                  value = '<p><br></p>';
              }
              
              // 过滤js代码
              if (editor.config.jsFilter) {
                  
                  value = value.replace(/<script[\s\S]*?<\/script>/ig, '');
              }
              // 赋值
              $txt.html(value);
          }
  
          // 定义click事件
          menu.clickEvent = function (e) {
              var self = this;
              var editor = self.editor;
              var $txt = editor.txt.$txt;
              var txtOuterHeight = $txt.outerHeight();
              var txtHeight = $txt.height();
  
              if (!self.$codeTextarea) {
                  self.$codeTextarea = $('<textarea class="code-textarea"></textarea>');
              }
              var $code = self.$codeTextarea;
              $code.css({
                  height: txtHeight,
                  'margin-top': txtOuterHeight - txtHeight
              });
  
              // 赋值
              $code.val($txt.html());
  
              // 渲染
              $txt.after($code).hide();
              $code.show();
  
              // 更新状态
              menu.isShowCode = true;
  
              // 执行 updateSelected 事件
              this.updateSelected();
  
              // 禁用其他菜单
              editor.disableMenusExcept('source');
  
              // 记录当前html值
              txtHtml = $txt.html();
          };
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var self = this;
              var editor = self.editor;
              var $txt = editor.txt.$txt;
              var $code = self.$codeTextarea;
              var value;
  
              if (!$code) {
                  return;
              }
  
              // 更新内容
              updateValue();
  
              // 渲染
              $code.after($txt).hide();
              $txt.show();
  
              // 更新状态
              menu.isShowCode = false;
  
              // 执行 updateSelected 事件
              this.updateSelected();
  
              // 启用其他菜单
              editor.enableMenusExcept('source');
  
              // 判断是否执行 onchange 事件
              if ($txt.html() !== txtHtml) {
                  if (editor.onchange && typeof editor.onchange === 'function') {
                      editor.onchange.call(editor);
                  }
              }
          };
  
          // 定义切换选中状态事件
          menu.updateSelectedEvent = function () {
              return this.isShowCode;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // quote 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'quote';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.quote,
              commandName: 'formatBlock',
              commandValue: 'blockquote'
          });
  
          // 定义click事件
          menu.clickEvent = function (e) {
              var rangeElem = editor.getRangeElem();
              var $rangeElem;
              if (!rangeElem) {
                  e.preventDefault();
                  return;
              }
              var currentQuote = editor.getSelfOrParentByName(rangeElem, 'blockquote');
              var $quote;
  
              if (currentQuote) {
                  // 说明当前在quote之内，不做任何处理
                  e.preventDefault();
                  return;
              }
  
              rangeElem = editor.getLegalTags(rangeElem);
              $rangeElem = $(rangeElem);
  
              // 无文字，则不允许执行引用
              if (!$rangeElem.text()) {
                  return;
              }
  
  
              if (!rangeElem) {
                  // 执行默认命令
                  // IE8 下执行此处（不过，经测试代码无效，也不报错）
                  editor.command(e, 'formatBlock', 'blockquote');
                  return;
              }
  
              // 自定义command事件
              function commandFn() {
                  $quote = $('<p>' + $rangeElem.text() + '</p>');
                  $rangeElem.after($quote).remove();
                  $quote.wrap('<blockquote>');
              }
  
              // 自定义 callback 事件
              function callback() {
                  // callback中，设置range为quote
                  var editor = this;
                  if ($quote) {
                      editor.restoreSelectionByElem($quote.get(0));
                  }
              }
  
              // 执行自定义命令
              editor.customCommand(e, commandFn, callback);
          };
  
          // 定义选中状态下的click事件
          menu.clickEventSelected = function (e) {
              var rangeElem;
              var quoteElem;
              var $lastChild;
  
              // 获取当前选区的elem，并试图往上找 quote 元素
              rangeElem = editor.getRangeElem();
              quoteElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
              if (!quoteElem) {
                  // 没找到，则返回
                  e.preventDefault();
                  return;
              }
  
              // 自定义的command事件
              function commandFn() {
                  var $quoteElem;
                  var $children;
  
                  $quoteElem = $(quoteElem);
                  $children = $quoteElem.children();
                  if ($children.length) {
                      $children.each(function (k) {
                          var $item = $(this);
                          if ($item.get(0).nodeName === 'P') {
                              $quoteElem.after($item);
                          } else {
                              $quoteElem.after('<p>' + $item.text() + '</p>');
                          }
                          $lastChild = $item;  // 记录最后一个子元素，用于callback中的range定位
                      });
                      $quoteElem.remove();
                      return;
                  }
              }
  
              // 自定义的callback函数
              function callback() {
                  // callback中，设置range为lastChild
                  var editor = this;
                  if ($lastChild) {
                      editor.restoreSelectionByElem($lastChild.get(0));
                  }
              }
  
              // 执行自定义命令
              editor.customCommand(e, commandFn, callback);
          };
  
          // 定义更新选中状态的事件
          menu.updateSelectedEvent = function () {
              var self = this; //菜单对象
              var editor = self.editor;
              var rangeElem;
  
              rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
  
              if (rangeElem) {
                  return true;
              }
  
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
  
          // --------------- 两次点击 enter 跳出引用 ---------------
          editor.ready(function () {
              var editor = this;
              var $txt = editor.txt.$txt;
              var isPrevEnter = false;  // 是不是刚刚在quote中按了 enter 键
              $txt.on('keydown', function (e) {
                  if (e.keyCode !== 13) {
                      // 不是 enter 键
                      isPrevEnter = false;
                      return;
                  }
  
                  var rangeElem = editor.getRangeElem();
                  rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
                  if (!rangeElem) {
                      // 选区不是 quote
                      isPrevEnter = false;
                      return;
                  }
  
                  if (!isPrevEnter) {
                      // 最近没有在qote中按enter键
                      isPrevEnter = true;
                      return;
                  }
  
                  var currentRangeElem = editor.getRangeElem();
                  var $currentRangeElem = $(currentRangeElem);
                  if ($currentRangeElem.length) {
                      $currentRangeElem.parent().after($currentRangeElem);
                  }
  
                  // 设置选区
                  editor.restoreSelectionByElem(currentRangeElem, 'start');
  
                  isPrevEnter = false;
                  // 阻止默认行文
                  e.preventDefault();
  
              });
          }); // editor.ready(
  
          // --------------- 处理quote中无内容时不能删除的问题 ---------------
          editor.ready(function () {
              var editor = this;
              var $txt = editor.txt.$txt;
              var $rangeElem;
  
              function commandFn() {
                  $rangeElem && $rangeElem.remove();
              }
              function callback() {
                  if (!$rangeElem) {
                      return;
                  }
                  var $prev = $rangeElem.prev();
                  if ($prev.length) {
                      // 有 prev 则定位到 prev 最后
                      editor.restoreSelectionByElem($prev.get(0));
                  } else {
                      // 无 prev 则初始化选区
                      editor.initSelection();
                  }
              }
  
              $txt.on('keydown', function (e) {
                  if (e.keyCode !== 8) {
                      // 不是 backspace 键
                      return;
                  }
  
                  var rangeElem = editor.getRangeElem();
                  rangeElem = editor.getSelfOrParentByName(rangeElem, 'blockquote');
                  if (!rangeElem) {
                      // 选区不是 quote
                      return;
                  }
                  $rangeElem = $(rangeElem);
  
                  var text = $rangeElem.text();
                  if (text) {
                      // quote 中还有内容
                      return;
                  }
                  editor.customCommand(e, commandFn, callback);
  
              }); // $txt.on
          }); // editor.ready(
      });
  
  });
  // 字体 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'fontfamily';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var configFamilys = editor.config.familys;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.fontfamily,
              commandName: 'fontName'
          });
  
          // 初始化数据
          var data  = {};
          /*
              data 需要的结构
              {
                  'commandValue': 'title'
                  ...
              }
          */
          $.each(configFamilys, function (k, v) {
              // configFamilys 是数组，data 是对象
              data[v] = v;
          });
  
          // 创建droplist
          var tpl = '<span style="font-family:{#commandValue};">{#title}</span>';
          menu.dropList = new E.DropList(editor, menu, {
              data: data,
              tpl: tpl,
              selectorForELemCommand: 'font[face]'  // 为了执行 editor.commandForElem 而传入的elem查询方式
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[face]');
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  });
  // 字号 菜单
  _e(function (E, $) {
      E.createMenu(function (check) {
          var menuId = 'fontsize';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var configSize = editor.config.fontsizes;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.fontsize,
              commandName: 'fontSize'
          });
  
          // 初始化数据
          var data  = configSize;
          /*
              data 需要的结构
              {
                  'commandValue': 'title'
                  ...
              }
          */
  
          // 创建droplist
          var tpl = '<span style="font-size:{#title};">{#title}</span>';
          menu.dropList = new E.DropList(editor, menu, {
              data: data,
              tpl: tpl,
              selectorForELemCommand: 'font[size]'  // 为了执行 editor.commandForElem 而传入的elem查询方式
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'font[size]');
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  });
  // head 菜单
  _e(function (E, $) {
      E.createMenu(function (check) {
          var menuId = 'head';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.head,
              commandName: 'formatBlock'
          });
  
          // 初始化数据
          var data  = {
              '<h1>': '标题1',
              '<h2>': '标题2',
              '<h3>': '标题3',
              '<h4>': '标题4',
              '<h5>': '标题5'
          };
          /*
              data 需要的结构
              {
                  'commandValue': 'title'
                  ...
              }
          */
  
          var isOrderedList;
          function beforeEvent(e) {
              if (editor.queryCommandState('InsertOrderedList')) {
                  isOrderedList = true;
  
                  // 先取消有序列表
                  editor.command(e, 'InsertOrderedList');
              } else {
                  isOrderedList = false;
              }
          }
  
          function afterEvent(e) {
              if (isOrderedList) {
                  // 再设置有序列表
                  editor.command(e, 'InsertOrderedList');
              }
          }
  
          // 创建droplist
          var tpl = '{#commandValue}{#title}';
          menu.dropList = new E.DropList(editor, menu, {
              data: data,
              tpl: tpl,
              // 对 ol 直接设置 head，会出现每个 li 的 index 都变成 1 的问题，因此要先取消 ol，然后设置 head，最后再增加上 ol
              beforeEvent: beforeEvent,
              afterEvent: afterEvent
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'h1,h2,h3,h4,h5');
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  });
  // unorderlist 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'unorderlist';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.unorderlist,
              commandName: 'InsertUnorderedList'
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // orderlist 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'orderlist';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.orderlist,
              commandName: 'InsertOrderedList'
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // alignleft 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'alignleft';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.alignleft,
              commandName: 'JustifyLeft'
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
                  var cssText;
                  if (elem && elem.style && elem.style.cssText != null) {
                      cssText = elem.style.cssText;
                      if (cssText && /text-align:\s*left;/.test(cssText)) {
                          return true;
                      }
                  }
                  if ($(elem).attr('align') === 'left') {
                      // ff 中，设置align-left之后，会是 <p align="left">xxx</p>
                      return true;
                  }
                  return false;
              });
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // aligncenter 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'aligncenter';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.aligncenter,
              commandName: 'JustifyCenter'
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
                  var cssText;
                  if (elem && elem.style && elem.style.cssText != null) {
                      cssText = elem.style.cssText;
                      if (cssText && /text-align:\s*center;/.test(cssText)) {
                          return true;
                      }
                  }
                  if ($(elem).attr('align') === 'center') {
                      // ff 中，设置align-center之后，会是 <p align="center">xxx</p>
                      return true;
                  }
                  return false;
              });
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // alignright 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'alignright';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.alignright,
              commandName: 'JustifyRight'
          });
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,li', function (elem) {
                  var cssText;
                  if (elem && elem.style && elem.style.cssText != null) {
                      cssText = elem.style.cssText;
                      if (cssText && /text-align:\s*right;/.test(cssText)) {
                          return true;
                      }
                  }
                  if ($(elem).attr('align') === 'right') {
                      // ff 中，设置align-right之后，会是 <p align="right">xxx</p>
                      return true;
                  }
                  return false;
              });
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // link 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'link';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.link
          });
  
          // 创建 dropPanel
          var $content = $('<div></div>');
          var $div1 = $('<div style="margin:20px 10px;" class="clearfix"></div>');
          var $div2 = $div1.clone();
          var $div3 = $div1.clone().css('margin', '0 10px');
          var $textInput = $('<input type="text" class="block" placeholder="' + lang.text + '"/>');
          var $urlInput = $('<input type="text" class="block" placeholder="' + lang.link + '"/>');
          var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
          var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  
          $div1.append($textInput);
          $div2.append($urlInput);
          $div3.append($btnSubmit).append($btnCancel);
          $content.append($div1).append($div2).append($div3);
          
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 300
          });
  
          // 定义click事件
          menu.clickEvent = function (e) {
              var menu = this;
              var dropPanel = menu.dropPanel;
  
              // -------------隐藏----------------
              if (dropPanel.isShowing) {
                  dropPanel.hide();
                  return;
              }
  
              // -------------显示----------------
  
              // 重置 input
              $textInput.val('');
              $urlInput.val('');
  
              // 获取url
              var url = '';
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
              if (rangeElem) {
                  url = rangeElem.href || '';
              }
  
              // 获取 text
              var text = '';
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 选区不是空
                  text = editor.getRangeText() || '';
              } else if (rangeElem) {
                  // 如果选区空，并且在 a 标签之内
                  text = rangeElem.textContent || rangeElem.innerHTML;
              }
  
              // 设置 url 和 text
              url && $urlInput.val(url);
              text && $textInput.val(text);
  
              // 如果有选区内容，textinput 不能修改
              if (!isRangeEmpty) {
                  $textInput.attr('disabled', true);
              } else {
                  $textInput.removeAttr('disabled');
              }
  
              // 显示（要设置好了所有input的值和属性之后再显示）
              dropPanel.show();
          };
  
          // 定义 update selected 事件
          menu.updateSelectedEvent = function () {
              var rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'a');
              if (rangeElem) {
                  return true;
              }
              return false;
          };
  
          // 『取消』 按钮
          $btnCancel.click(function (e) {
              e.preventDefault();
              menu.dropPanel.hide();
          });
  
          // 『确定』按钮
          $btnSubmit.click(function (e) {
              e.preventDefault();
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getSelfOrParentByName(rangeElem, 'a');
              var isRangeEmpty = editor.isRangeEmpty();
  
              var $linkElem, linkHtml;
              var commandFn, callback;
              var $txt = editor.txt.$txt;
              var $oldLinks, $newLinks;
              var uniqId = 'link' + E.random();
  
              // 获取数据
              var url = $.trim($urlInput.val());
              var text = $.trim($textInput.val());
  
              if (!url) {
                  menu.dropPanel.focusFirstInput();
                  return;
              }
              if (!text) {
                  text = url;
              }
  
              if (!isRangeEmpty) {
                  // 选中区域有内容，则执行默认命令
  
                  // 获取目前 txt 内所有链接，并为当前链接做一个标记
                  $oldLinks = $txt.find('a');
                  $oldLinks.attr(uniqId, '1');
  
                  // 执行命令 
                  editor.command(e, 'createLink', url);
  
                  // 去的没有标记的链接，即刚刚插入的链接
                  $newLinks = $txt.find('a').not('[' + uniqId + ']');
                  $newLinks.attr('target', '_blank'); // 增加 _blank
  
                  // 去掉之前做的标记
                  $oldLinks.removeAttr(uniqId);
  
              } else if (targetElem) {
                  // 无选中区域，在 a 标签之内，修改该 a 标签的内容和链接
                  $linkElem = $(targetElem);
                  commandFn = function () {
                      $linkElem.attr('href', url);
                      $linkElem.text(text);
                  };
                  callback = function () {
                      var editor = this;
                      editor.restoreSelectionByElem(targetElem);
                  };
                  // 执行命令
                  editor.customCommand(e, commandFn, callback);
              } else {
                  // 无选中区域，不在 a 标签之内，插入新的链接
  
                  linkHtml = '<a href="' + url + '" target="_blank">' + text + '</a>';
                  if (E.userAgent.indexOf('Firefox') > 0) {
                      linkHtml += '<span>&nbsp;</span>';
                  }
                  editor.command(e, 'insertHtml', linkHtml);
              }
  
          });
          
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // unlink 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'unlink';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.unlink,
              commandName: 'unLink'
          });
  
          // click 事件
          menu.clickEvent = function  (e) {
              var isRangeEmpty = editor.isRangeEmpty();
              if (!isRangeEmpty) {
                  // 有选中区域，或者IE8，执行默认命令
                  editor.command(e, 'unLink');
                  return;
              }
  
              // 无选中区域...
  
              var rangeElem = editor.getRangeElem();
              var aElem = editor.getSelfOrParentByName(rangeElem, 'a');
              if (!aElem) {
                  // 不在 a 之内，返回
                  e.preventDefault();
                  return;
              }
  
              // 在 a 之内
              var $a = $(aElem);
              var $span = $('<span>' + $a.text() + '</span>');
              function commandFn() {
                  $a.after($span).remove();
              }
              function callback() {
                  editor.restoreSelectionByElem($span.get(0));
              }
              editor.customCommand(e, commandFn, callback);
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // table 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'table';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.table
          });
  
          // dropPanel 内容
          var $content = $('<div style="font-size: 14px; color: #666; text-align:right;"></div>');
          var $table = $('<table class="choose-table" style="margin-bottom:10px;margin-top:5px;">');
          var $row = $('<span>0</span>');
          var $rowspan = $('<span> 行 </span>');
          var $col = $('<span>0</span>');
          var $colspan = $('<span> 列</span>');
          var $tr;
          var i, j;
  
          // 创建一个n行n列的表格
          for (i = 0; i < 15; i++) {
              $tr = $('<tr index="' + (i + 1) + '">');
              for (j = 0; j < 20; j++) {
                  $tr.append($('<td index="' + (j + 1) + '">'));
              }
              $table.append($tr);
          }
          $content.append($table);
          $content.append($row).append($rowspan).append($col).append($colspan);
  
          // 定义table事件
          $table.on('mouseenter', 'td', function (e) {
              var $currentTd = $(e.currentTarget);
              var currentTdIndex = $currentTd.attr('index');
              var $currentTr = $currentTd.parent();
              var currentTrIndex = $currentTr.attr('index');
  
              // 显示
              $row.text(currentTrIndex);
              $col.text(currentTdIndex);
  
              // 遍历设置背景颜色
              $table.find('tr').each(function () {
                  var $tr = $(this);
                  var trIndex = $tr.attr('index');
                  if (parseInt(trIndex, 10) <= parseInt(currentTrIndex, 10)) {
                      // 该行需要可能需要设置背景色
                      $tr.find('td').each(function () {
                          var $td = $(this);
                          var tdIndex = $td.attr('index');
                          if (parseInt(tdIndex, 10) <= parseInt(currentTdIndex, 10)) {
                              // 需要设置背景色
                              $td.addClass('active');
                          } else {
                              // 需要移除背景色
                              $td.removeClass('active');
                          }
                      });
                  } else {
                      // 改行不需要设置背景色
                      $tr.find('td').removeClass('active');
                  }
              });
          }).on('mouseleave', function (e) {
              // mouseleave 删除背景色
              $table.find('td').removeClass('active');
  
              $row.text(0);
              $col.text(0);
          });
  
          // 插入表格
          $table.on('click', 'td', function (e) {
              var $currentTd = $(e.currentTarget);
              var currentTdIndex = $currentTd.attr('index');
              var $currentTr = $currentTd.parent();
              var currentTrIndex = $currentTr.attr('index');
  
              var rownum = parseInt(currentTrIndex, 10);
              var colnum = parseInt(currentTdIndex, 10);
  
              // -------- 拼接tabel html --------
  
              var i, j;
              var tableHtml = '<table>';
              for (i = 0; i < rownum; i++) {
                  tableHtml += '<tr>';
  
                  for (j = 0; j < colnum; j++) {
                      tableHtml += '<td><span>&nbsp;</span></td>';
                  }
                  tableHtml += '</tr>';
              }
              tableHtml += '</table>';
  
              // -------- 执行命令 --------
              editor.command(e, 'insertHtml', tableHtml);
          });
  
          // 创建 panel
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 262
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // emotion 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'emotion';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var config = editor.config;
          var lang = config.lang;
          var configEmotions = config.emotions;
          var emotionsShow = config.emotionsShow;
  
          // 记录每一个表情图片的地址
          editor.emotionUrls = [];
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.emotion
          });
  
          // 添加表情图片的函数
          function insertEmotionImgs(data, $tabContent) {
              // 添加表情图片
              $.each(data, function (k, emotion) {
                  var src = emotion.icon || emotion.url;
                  var value = emotion.value || emotion.title;
                  // 通过配置 editor.config.emotionsShow 的值来修改插入到编辑器的内容（图片/value）
                  var commandValue = emotionsShow === 'icon' ? src : value;
                  var $command = $('<a href="#" commandValue="' + commandValue + '"></a>');
                  var $img = $('<img>');
                  $img.attr('_src', src);  // 先将 src 复制到 '_src' 属性，先不加载
  
                  $command.append($img);
                  $tabContent.append($command);
  
                  // 记录下每一个表情图片的地址
                  editor.emotionUrls.push(src);
              });
          }
  
          // 拼接 dropPanel 内容
          var $panelContent = $('<div class="panel-tab"></div>');
          var $tabContainer = $('<div class="tab-container"></div>');
          var $contentContainer = $('<div class="content-container emotion-content-container"></div>');
          $.each(configEmotions, function (k, emotion) {
              var title = emotion.title;
              var data = emotion.data;
  
              E.log('正在处理 ' + title + ' 表情的数据...');
  
              // 增加该组表情的tab和content
              var $tab = $('<a href="#">' + title +' </a>');
              $tabContainer.append($tab);
              var $tabContent = $('<div class="content"></div>');
              $contentContainer.append($tabContent);
  
              // tab 切换事件
              $tab.click(function (e) {
                  $tabContainer.children().removeClass('selected');
                  $contentContainer.children().removeClass('selected');
                  $tabContent.addClass('selected');
                  $tab.addClass('selected');
                  e.preventDefault();
              });
  
              // 处理data
              if (typeof data === 'string') {
                  // url 形式，需要通过ajax从该url获取数据
                  E.log('将通过 ' + data + ' 地址ajax下载表情包');
                  $.get(data, function (result) {
                      result = $.parseJSON(result);
                      E.log('下载完毕，得到 ' + result.length + ' 个表情');
                      insertEmotionImgs(result, $tabContent);
                  });
                  
              } else if ( Object.prototype.toString.call(data).toLowerCase().indexOf('array') > 0 ) {
                  // 数组，即 data 直接就是表情包数据
                  insertEmotionImgs(data, $tabContent);
              } else {
                  // 其他情况，data格式不对
                  E.error('data 数据格式错误，请修改为正确格式，参考文档：' + E.docsite);
                  return;
              }
          });
          $panelContent.append($tabContainer).append($contentContainer);
  
          // 默认显示第一个tab
          $tabContainer.children().first().addClass('selected');
          $contentContainer.children().first().addClass('selected');
  
          // 插入表情command事件
          $contentContainer.on('click', 'a[commandValue]', function (e) {
              var $a = $(e.currentTarget);
              var commandValue = $a.attr('commandValue');
              var img;
  
              // commandValue 有可能是图片url，也有可能是表情的 value，需要区别对待
  
              if (emotionsShow === 'icon') {
                  // 插入图片
                  editor.command(e, 'InsertImage', commandValue);
              } else {
                  // 插入value
                  editor.command(e, 'insertHtml', '<span>' + commandValue + '</span>');
              }
  
              e.preventDefault();
          });
  
          // 添加panel
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $panelContent,
              width: 350
          });
  
          // 定义click事件（异步加载表情图片）
          menu.clickEvent = function (e) {
              var menu = this;
              var dropPanel = menu.dropPanel;
  
              // -------------隐藏-------------
              if (dropPanel.isShowing) {
                  dropPanel.hide();
                  return;
              }
  
              // -------------显示-------------
              dropPanel.show();
  
              // 异步加载图片
              if (menu.imgLoaded) {
                  return;
              }
              $contentContainer.find('img').each(function () {
                  var $img = $(this);
                  var _src = $img.attr('_src');
                  $img.on('error', function () {
                      E.error('加载不出表情图片 ' + _src);
                  });
                  $img.attr('src', _src);
                  $img.removeAttr('_src');
              });
              menu.imgLoaded = true;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // img 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'img';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.img
          });
  
          // 创建 panel content
          var $panelContent = $('<div class="panel-tab"></div>');
          var $tabContainer = $('<div class="tab-container"></div>');
          var $contentContainer = $('<div class="content-container"></div>');
          $panelContent.append($tabContainer).append($contentContainer);
  
          // tab
          var $uploadTab = $('<a href="#">上传图片</a>');
          var $linkTab = $('<a href="#">网络图片</a>');
          $tabContainer.append($uploadTab).append($linkTab);
  
          // 上传图片 content
          var $uploadContent = $('<div class="content"></div>');
          $contentContainer.append($uploadContent);
  
          // 网络图片 content
          var $linkContent = $('<div class="content"></div>');
          $contentContainer.append($linkContent);
          linkContentHandler(editor, menu, $linkContent);
  
          // 添加panel
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $panelContent,
              width: 400,
              onRender: function () {
                  // 渲染后的回调事件，用于执行自定义上传的init
                  // 因为渲染之后，上传面板的dom才会被渲染到页面，才能让第三方空间获取到
                  var init = editor.config.customUploadInit;
                  init && init.call(editor);
              }
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
  
          // tab 切换事件
          function tabToggle() {
              $uploadTab.click(function (e) {
                  $tabContainer.children().removeClass('selected');
                  $contentContainer.children().removeClass('selected');
                  $uploadContent.addClass('selected');
                  $uploadTab.addClass('selected');
                  e.preventDefault();
              });
              $linkTab.click(function (e) {
                  $tabContainer.children().removeClass('selected');
                  $contentContainer.children().removeClass('selected');
                  $linkContent.addClass('selected');
                  $linkTab.addClass('selected');
                  e.preventDefault();
  
                  // focus input
                  if (E.placeholder) {
                      $linkContent.find('input[type=text]').focus();
                  }
              });
  
              // 默认情况
              // $uploadTab.addClass('selected');
              // $uploadContent.addClass('selected');
              $uploadTab.click();
          }
  
          // 隐藏上传图片
          function hideUploadImg() {
              $tabContainer.remove();
              $uploadContent.remove();
              $linkContent.addClass('selected');
          }
  
          // 判断用户是否配置了上传图片
          editor.ready(function () {
              var editor = this;
              var config = editor.config;
              var uploadImgUrl = config.uploadImgUrl;
              var customUpload = config.customUpload;
              var $uploadImgPanel;
  
              if (uploadImgUrl || customUpload) {
                  // 第一，暴露出 $uploadContent 以便用户自定义 ！！！重要
                  editor.$uploadContent = $uploadContent;
  
                  // 第二，绑定tab切换事件
                  tabToggle();
              } else {
                  // 未配置上传图片功能
                  hideUploadImg();
              }
  
              // 点击 $uploadContent 立即隐藏 dropPanel
              // 为了兼容IE8、9的上传，因为IE8、9使用 modal 上传
              // 这里使用异步，为了不妨碍高级浏览器通过点击 $uploadContent 选择文件
              function hidePanel() {
                  menu.dropPanel.hide();
              }
              $uploadContent.click(function () {
                  setTimeout(hidePanel);
              });
          });
      });
  
      // --------------- 处理网络图片content ---------------
      function linkContentHandler (editor, menu, $linkContent) {
          var lang = editor.config.lang;
          var $urlContainer = $('<div style="margin:20px 10px 10px 10px;"></div>');
          var $urlInput = $('<input type="text" class="block" placeholder="http://"/>');
          $urlContainer.append($urlInput);
          var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
          var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  
          $linkContent.append($urlContainer).append($btnSubmit).append($btnCancel);
  
          // 取消
          $btnCancel.click(function (e) {
              e.preventDefault();
              menu.dropPanel.hide();
          });
  
          // callback 
          function callback() {
              $urlInput.val('');
          }
  
          // 确定
          $btnSubmit.click(function (e) {
              e.preventDefault();
              var url = $.trim($urlInput.val());
              if (!url) {
                  // 无内容
                  $urlInput.focus();
                  return;
              }
  
              var imgHtml = '<img style="max-width:100%;" src="' + url + '"/>';
              editor.command(e, 'insertHtml', imgHtml, callback);
          });
      }
  
  });
  // video 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'video';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
          var reg = /^<(iframe)|(embed)/i;  // <iframe... 或者 <embed... 格式
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.video
          });
  
          // 创建 panel 内容
          var $content = $('<div></div>');
          var $linkInputContainer = $('<div style="margin:20px 10px;"></div>');
          var $linkInput = $('<input type="text" class="block" placeholder=\'格式如：<iframe src="..." frameborder=0 allowfullscreen></iframe>\'/>');
          $linkInputContainer.append($linkInput);
          var $sizeContainer = $('<div style="margin:20px 10px;"></div>');
          var $widthInput = $('<input type="text" value="640" style="width:50px;text-align:center;"/>');
          var $heightInput = $('<input type="text" value="498" style="width:50px;text-align:center;"/>');
          $sizeContainer.append('<span> ' + lang.width + ' </span>')
                        .append($widthInput)
                        .append('<span> px &nbsp;&nbsp;&nbsp;</span>')
                        .append('<span> ' + lang.height + ' </span>')
                        .append($heightInput)
                        .append('<span> px </span>');
          var $btnContainer = $('<div></div>');
          var $howToCopy = $('<a href="http://www.kancloud.cn/wangfupeng/wangeditor2/134973" target="_blank" style="display:inline-block;margin-top:10px;margin-left:10px;color:#999;">如何复制视频链接？</a>');
          var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
          var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
          $btnContainer.append($howToCopy).append($btnSubmit).append($btnCancel);
          $content.append($linkInputContainer).append($sizeContainer).append($btnContainer);
  
          // 取消按钮
          $btnCancel.click(function (e) {
              e.preventDefault();
              $linkInput.val('');
              menu.dropPanel.hide();
          });
  
          // 确定按钮
          $btnSubmit.click(function (e) {
              e.preventDefault();
              var link = $.trim($linkInput.val());
              var $link;
              var width = parseInt($widthInput.val());
              var height = parseInt($heightInput.val());
              var $div = $('<div>');
              var html = '<p style="text-align:center;">{content}</p>';
  
              // 验证数据
              if (!link) {
                  menu.dropPanel.focusFirstInput();
                  return;
              }
  
              if (!reg.test(link)) {
                  alert('视频链接格式错误！');
                  menu.dropPanel.focusFirstInput();
                  return;
              }
  
              if (isNaN(width) || isNaN(height)) {
                  alert('宽度或高度不是数字！');
                  return;
              }
  
              $link = $(link);
  
              // 设置高度和宽度
              $link.attr('width', width)
                   .attr('height', height);
  
              // 拼接字符串
              html = html.replace('{content}', $div.append($link).html());
  
              // 执行命令
              editor.command(e, 'insertHtml', html);
              $linkInput.val('');
          });
  
          // 创建panel
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 400
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // location 菜单
  _e(function (E, $) {
  
      // 判断浏览器的 input 是否支持 keyup
      var inputKeyup = (function (input) {
          return 'onkeyup' in input;
      })(document.createElement('input'));
  
      // 百度地图的key
      E.baiduMapAk = 'TVhjYjq1ICT2qqL5LdS8mwas';
  
      // 一个页面中，如果有多个编辑器，地图会出现问题。这个参数记录一下，如果超过 1 就提示
      E.numberOfLocation = 0;
  
      E.createMenu(function (check) {
          var menuId = 'location';
          if (!check(menuId)) {
              return;
          }
  
          if (++E.numberOfLocation > 1) {
              E.error('目前不支持在一个页面多个编辑器上同时使用地图，可通过自定义菜单配置去掉地图菜单');
              return;
          }
  
          var editor = this;
          var config = editor.config;
          var lang = config.lang;
          var ak = config.mapAk;
  
          // 地图的变量存储到这个地方
          editor.mapData = {};
          var mapData = editor.mapData;
  
          // ---------- 地图事件 ----------
          mapData.markers = [];
          mapData.mapContainerId = 'map' + E.random();
  
          mapData.clearLocations = function () {
              var map = mapData.map;
              if (!map) {
                  return;
              }
              map.clearOverlays();
  
              //同时，清空marker数组
              mapData.markers = [];
          };
  
          mapData.searchMap = function () {
              var map = mapData.map;
              if (!map) {
                  return;
              }
  
              var BMap = window.BMap;
              var cityName = $cityInput.val();
              var locationName = $searchInput.val();
              var myGeo, marker;
  
              if(cityName !== ''){
                  if(!locationName || locationName === ''){
                      map.centerAndZoom(cityName, 11);
                  }
  
                  //地址解析
                  if(locationName && locationName !== ''){
                      myGeo = new BMap.Geocoder();
                      // 将地址解析结果显示在地图上,并调整地图视野
                      myGeo.getPoint(locationName, function(point){
                          if (point) {
                              map.centerAndZoom(point, 13);
                              marker = new BMap.Marker(point);
                              map.addOverlay(marker);
                              marker.enableDragging();  //允许拖拽
                              mapData.markers.push(marker);  //将marker加入到数组中
                          }else{
                              // alert('未找到');
                              map.centerAndZoom(cityName, 11);  //找不到则重新定位到城市
                          }
                      }, cityName);
                  }
              } // if(cityName !== '')
          };
  
          // load script 之后的 callback
          var hasCallback = false;
          window.baiduMapCallBack = function(){
              // 避免重复加载
              if (hasCallback) {
                  return;
              } else {
                  hasCallback = true;
              }
  
              var BMap = window.BMap;
              if (!mapData.map) {
                  // 创建Map实例
                  mapData.map = new BMap.Map(mapData.mapContainerId);
              }
              var map = mapData.map;
  
              map.centerAndZoom(new BMap.Point(116.404, 39.915), 11);  // 初始化地图,设置中心点坐标和地图级别
              map.addControl(new BMap.MapTypeControl());   //添加地图类型控件
              map.setCurrentCity("北京");          // 设置地图显示的城市 此项是必须设置的
              map.enableScrollWheelZoom(true);     //开启鼠标滚轮缩放
  
              //根据IP定位
              function locationFun(result){
                  var cityName = result.name;
                  map.setCenter(cityName);
  
                  // 设置城市名称
                  $cityInput.val(cityName);
                  if (E.placeholder) {
                      $searchInput.focus();
                  }
                  var timeoutId, searchFn;
                  if (inputKeyup) {
                     // 并绑定搜索事件 - input 支持 keyup
                     searchFn = function (e) {
                         if (e.type === 'keyup' && e.keyCode === 13) {
                             e.preventDefault();
                         }
                         if (timeoutId) {
                             clearTimeout(timeoutId);
                         }
                         timeoutId = setTimeout(mapData.searchMap, 500);
                     };
                     $cityInput.on('keyup change paste', searchFn);
                     $searchInput.on('keyup change paste', searchFn); 
                  } else {
                      // 并绑定搜索事件 - input 不支持 keyup
                      searchFn = function () {
                          if (!$content.is(':visible')) {
                              // panel 不显示了，就不用再监控了
                              clearTimeout(timeoutId);
                              return;
                          }
  
                          var currentCity = '';
                          var currentSearch = '';
                          var city = $cityInput.val();
                          var search = $searchInput.val();
  
                          if (city !== currentCity || search !== currentSearch) {
                              // 刚获取的数据和之前的数据不一致，执行查询
                              mapData.searchMap();
                              // 更新数据
                              currentCity = city;
                              currentSearch = search;
                          }
  
                          // 继续监控
                          if (timeoutId) {
                              clearTimeout(timeoutId);
                          }
                          timeoutId = setTimeout(searchFn, 1000);
                      };
                      // 开始监控
                      timeoutId = setTimeout(searchFn, 1000);
                  }
              }
              var myCity = new BMap.LocalCity();
              myCity.get(locationFun);
  
              //鼠标点击，创建位置
              map.addEventListener("click", function(e){
                  var marker = new BMap.Marker(new BMap.Point(e.point.lng, e.point.lat)); 
                  map.addOverlay(marker);  
                  marker.enableDragging();
                  mapData.markers.push(marker);  //加入到数组中
              }, false);
          };
  
          mapData.loadMapScript = function () {
              var script = document.createElement("script");
              script.type = "text/javascript";
              script.src = "http://api.map.baidu.com/api?v=2.0&ak=" + ak + "&callback=baiduMapCallBack";  // baiduMapCallBack是一个本地函数
              try {
                  // IE10- 报错
                  document.body.appendChild(script);
              } catch (ex) {
                  E.error('加载地图过程中发生错误');
              }
          };
  
          // 初始化地图
          mapData.initMap = function () {
              if (window.BMap) {
                  // 不是第一次，直接处理地图即可
                  window.baiduMapCallBack();
              } else {
                  // 第一次，先加载地图 script，再处理地图（script加载完自动执行处理）
                  mapData.loadMapScript();
              }
          };
  
          // ---------- 创建 menu 对象 ----------
  
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.location
          });
  
          editor.menus[menuId] = menu;
  
          // ---------- 构建UI ----------
  
          // panel content 
          var $content = $('<div></div>');
  
          // 搜索框
          var $inputContainer = $('<div style="margin:10px 0;"></div>');
          var $cityInput = $('<input type="text"/>');
          $cityInput.css({
              width: '80px',
              'text-align': 'center'
          });
          var $searchInput = $('<input type="text"/>');
          $searchInput.css({
              width: '300px',
              'margin-left': '10px'
          }).attr('placeholder', lang.searchlocation);
          var $clearBtn = $('<button class="right link">' + lang.clearLocation + '</button>');
          $inputContainer.append($clearBtn)
                         .append($cityInput)
                         .append($searchInput);
          $content.append($inputContainer);
  
          // 清除位置按钮
          $clearBtn.click(function (e) {
              $searchInput.val('');
              $searchInput.focus();
              mapData.clearLocations();
              e.preventDefault();
          });
  
          // 地图
          var $map = $('<div id="' + mapData.mapContainerId + '"></div>');
          $map.css({
              height: '260px',
              width: '100%',
              position: 'relative',
              'margin-top': '10px',
              border: '1px solid #f1f1f1'
          });
          var $mapLoading = $('<span>' + lang.loading + '</span>');
          $mapLoading.css({
              position: 'absolute',
              width: '100px',
              'text-align': 'center',
              top: '45%',
              left: '50%',
              'margin-left': '-50px'
          });
          $map.append($mapLoading);
          $content.append($map);
  
          // 按钮
          var $btnContainer = $('<div style="margin:10px 0;"></div>');
          var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
          var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
          var $checkLabel = $('<label style="display:inline-block;margin-top:10px;color:#666;"></label>');
          var $check = $('<input type="checkbox">');
          $checkLabel.append($check).append('<span style="display:inline-block;margin-left:5px;">  ' + lang.dynamicMap + '</span>');
          $btnContainer.append($checkLabel)
                       .append($btnSubmit)
                       .append($btnCancel);
          $content.append($btnContainer);
  
          function callback() {
              $searchInput.val('');
          }
  
          // 『取消』按钮事件
          $btnCancel.click(function (e) {
              e.preventDefault();
              callback();
              menu.dropPanel.hide();
          });
  
          // 『确定』按钮事件
          $btnSubmit.click(function (e) {
              e.preventDefault();
              var map = mapData.map,
                  isDynamic = $check.is(':checked'),
                  markers =  mapData.markers,
  
                  center = map.getCenter(),
                  centerLng = center.lng,
                  centerLat = center.lat,
  
                  zoom = map.getZoom(),
  
                  size = map.getSize(),
                  sizeWidth = size.width,
                  sizeHeight = size.height,
  
                  position,
                  src,
                  iframe;
  
              if(isDynamic){
                  //动态地址
                  src = 'http://ueditor.baidu.com/ueditor/dialogs/map/show.html#';
              }else{
                  //静态地址
                  src = 'http://api.map.baidu.com/staticimage?';
              }
  
              //src参数
              src = src +'center=' + centerLng + ',' + centerLat +
                  '&zoom=' + zoom +
                  '&width=' + sizeWidth +
                  '&height=' + sizeHeight;
              if(markers.length > 0){
                  src = src + '&markers=';
  
                  //添加所有的marker
                  $.each(markers, function(key, value){
                      position = value.getPosition();
                      if(key > 0){
                          src = src + '|';
                      }
                      src = src + position.lng + ',' + position.lat;
                  });
              }
  
              if(isDynamic){
                  if(markers.length > 1){
                      alert( lang.langDynamicOneLocation );
                      return;
                  }
  
                  src += '&markerStyles=l,A';
  
                  //插入iframe
                  iframe = '<iframe class="ueditor_baidumap" src="{src}" frameborder="0" width="' + sizeWidth + '" height="' + sizeHeight + '"></iframe>';
                  iframe = iframe.replace('{src}', src);
                  editor.command(e, 'insertHtml', iframe, callback);
              }else{
                  //插入图片
                  editor.command(e, 'insertHtml', '<img style="max-width:100%;" src="' + src + '"/>', callback);
              }
          });
  
          // 根据 UI 创建菜单 panel
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 500
          });
  
          // ---------- 事件 ----------
  
          // render 时执行事件
          menu.onRender = function () {
              if (ak === E.baiduMapAk) {
                  E.warn('建议在配置中自定义百度地图的mapAk，否则可能影响地图功能，文档：' + E.docsite);
              }
          };
  
          // click 事件
          menu.clickEvent = function (e) {
              var menu = this;
              var dropPanel = menu.dropPanel;
              var firstTime = false;
  
              // -------------隐藏-------------
              if (dropPanel.isShowing) {
                  dropPanel.hide();
                  return;
              }
  
              // -------------显示-------------
              if (!mapData.map) {
                  // 第一次，先加载地图
                  firstTime = true;
              }
              mapData.initMap();
              dropPanel.show();
  
              if (!firstTime) {
                  $searchInput.focus();
              }
          };
  
      });
  
  });
  // insertcode 菜单
  _e(function (E, $) {
  
      // 加载 highlightjs 代码
      function loadHljs() {
          if (E.userAgent.indexOf('MSIE 8') > 0) {
              // 不支持 IE8
              return;
          }
          if (window.hljs) {
              // 不要重复加载
              return;
          }
          var script = document.createElement("script");
          script.type = "text/javascript";
          script.src = "//cdn.bootcss.com/highlight.js/9.2.0/highlight.min.js";
          document.body.appendChild(script);
      }
      
  
      E.createMenu(function (check) {
          var menuId = 'insertcode';
          if (!check(menuId)) {
              return;
          }
  
          // 加载 highlightjs 代码
          setTimeout(loadHljs, 0);
  
          var editor = this;
          var lang = editor.config.lang;
          var $txt = editor.txt.$txt;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.insertcode
          });
  
          // click 事件
          menu.clickEvent = function (e) {
              var menu = this;
              var dropPanel = menu.dropPanel;
  
              // 隐藏
              if (dropPanel.isShowing) {
                  dropPanel.hide();
                  return;
              }
  
              // 显示
              $textarea.val('');
              dropPanel.show();
  
              // highlightjs 语言列表
              var hljs = window.hljs;
              if (hljs && hljs.listLanguages) {
                  if ($langSelect.children().length !== 0) {
                      return;
                  }
                  $langSelect.css({
                      'margin-top': '9px',
                      'margin-left': '5px'
                  });
                  $.each(hljs.listLanguages(), function (key, lang) {
                      $langSelect.append('<option value="' + lang + '">' + lang + '</option>');
                  });
              } else {
                  $langSelect.hide();
              }
          };
  
          // 选中状态下的 click 事件
          menu.clickEventSelected = function (e) {
              var menu = this;
              var dropPanel = menu.dropPanel;
  
              // 隐藏
              if (dropPanel.isShowing) {
                  dropPanel.hide();
                  return;
              }
  
              // 显示
              dropPanel.show();
  
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
              var $targetElem;
              var className;
              if (targetElem) {
                  // 确定找到 pre 之后，再找 code
                  targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
              }
              if (!targetElem) {
                  return;
              }
              $targetElem = $(targetElem);
  
              // 赋值内容
              $textarea.val($targetElem.text());
              if ($langSelect) {
                  // 赋值语言
                  className = $targetElem.attr('class');
                  if (className) {
                      $langSelect.val(className.split(' ')[0]);
                  }
              }
          };
  
          // 定义更新选中状态的事件
          menu.updateSelectedEvent = function () {
              var self = this; //菜单对象
              var editor = self.editor;
              var rangeElem;
  
              rangeElem = editor.getRangeElem();
              rangeElem = editor.getSelfOrParentByName(rangeElem, 'pre');
  
              if (rangeElem) {
                  return true;
              }
  
              return false;
          };
  
          // 创建 panel
          var $content = $('<div></div>');
          var $textarea = $('<textarea></textarea>');
          var $langSelect = $('<select></select>');
          contentHandle($content);
          menu.dropPanel = new E.DropPanel(editor, menu, {
              $content: $content,
              width: 500
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
  
          // ------ 增加 content 内容 ------
          function contentHandle($content) {
              // textarea 区域
              var $textareaContainer = $('<div></div>');
              $textareaContainer.css({
                  margin: '15px 5px 5px 5px',
                  height: '160px',
                  'text-align': 'center'
              });
              $textarea.css({
                  width: '100%',
                  height: '100%',
                  padding: '10px'
              });
              $textarea.on('keydown', function (e) {
                  // 取消 tab 键默认行为
                  if (e.keyCode === 9) {
                      e.preventDefault();
                  }
              });
              $textareaContainer.append($textarea);
              $content.append($textareaContainer);
  
              // 按钮区域
              var $btnContainer = $('<div></div>');
              var $btnSubmit = $('<button class="right">' + lang.submit + '</button>');
              var $btnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  
              $btnContainer.append($btnSubmit).append($btnCancel).append($langSelect);
              $content.append($btnContainer);
  
              // 取消按钮
              $btnCancel.click(function (e) {
                  e.preventDefault();
                  menu.dropPanel.hide();
              });
  
              // 确定按钮
              var codeTpl = '<pre style="max-width:100%;overflow-x:auto;"><code{#langClass}>{#content}</code></pre>';
              $btnSubmit.click(function (e) {
                  e.preventDefault();
                  var val = $textarea.val();
                  if (!val) {
                      // 无内容
                      $textarea.focus();
                      return;
                  }
  
                  var rangeElem = editor.getRangeElem();
                  if ($.trim($(rangeElem).text())) {
                      codeTpl = '<p><br></p>' + codeTpl;
                  }
  
                  var lang = $langSelect ? $langSelect.val() : ''; // 获取高亮语言
                  var langClass = '';
                  var doHightlight = function () {
                      $txt.find('pre code').each(function (i, block) {
                          var $block = $(block);
                          if ($block.attr('codemark')) {
                              // 有 codemark 标记的代码块，就不再重新格式化了
                              return;
                          } else if (window.hljs) {
                              // 新代码块，格式化之后，立即标记 codemark
                              window.hljs.highlightBlock(block);
                              $block.attr('codemark', '1');
                          }
                      });
                  };
  
                  // 语言高亮样式
                  if (lang) {
                      langClass = ' class="' + lang + ' hljs"';
                  }
  
                  // 替换标签
                  val = val.replace(/&/gm, '&amp;')
                           .replace(/</gm, '&lt;')
                           .replace(/>/gm, '&gt;');
  
                  // ---- menu 未选中状态 ----
                  if (!menu.selected) {
                      // 拼接html
                      var html = codeTpl.replace('{#langClass}', langClass).replace('{#content}', val);
                      editor.command(e, 'insertHtml', html, doHightlight);
                      return;
                  }
  
                  // ---- menu 选中状态 ----
                  var targetElem = editor.getSelfOrParentByName(rangeElem, 'pre');
                  var $targetElem;
                  if (targetElem) {
                      // 确定找到 pre 之后，再找 code
                      targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
                  }
                  if (!targetElem) {
                      return;
                  }
                  $targetElem = $(targetElem);
  
                  function commandFn() {
                      var className;
                      if (lang) {
                          className = $targetElem.attr('class');
                          if (className !== lang + ' hljs') {
                              $targetElem.attr('class', lang + ' hljs');
                          }
                      }
                      $targetElem.html(val);
                  }
                  function callback() {
                      editor.restoreSelectionByElem(targetElem);
                      doHightlight();
                  }
                  editor.customCommand(e, commandFn, callback);
              });
          }
  
          // ------ enter 时，不另起标签，只换行 ------
          $txt.on('keydown', function (e) {
              if (e.keyCode !== 13) {
                  return;
              }
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
              if (!targetElem) {
                  return;
              }
  
              editor.command(e, 'insertHtml', '\n');
          });
  
          // ------ 点击时，禁用其他标签 ------
          function updateMenu() {
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getSelfOrParentByName(rangeElem, 'code');
              if (targetElem) {
                  // 在 code 之内，禁用其他菜单
                  editor.disableMenusExcept('insertcode');
              } else {
                  // 不是在 code 之内，启用其他菜单
                  editor.enableMenusExcept('insertcode');
              }
          }
          $txt.on('keydown click', function (e) {
              // 此处必须使用 setTimeout 异步处理，否则不对
              setTimeout(updateMenu);
          });
      });
  
  });
  // undo 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'undo';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.undo
          });
  
          // click 事件
          menu.clickEvent = function (e) {
              editor.undo();
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
  
  
          // ------------ 初始化时、enter 时、打字中断时，做记录 ------------
          // ------------ ctrl + z 是调用记录撤销，而不是使用浏览器默认的撤销 ------------
          editor.ready(function () {
              var editor = this;
              var $txt = editor.txt.$txt;
              var timeoutId;
  
              // 执行undo记录
              function undo() {
                  editor.undoRecord();
              }
  
              $txt.on('keydown', function (e) {
                  var keyCode = e.keyCode;
  
                  // 撤销 ctrl + z
                  if (e.ctrlKey && keyCode === 90) {
                      editor.undo();
                      return;
                  }
  
                  if (keyCode === 13) {
                      // enter 做记录
                      undo();
                  } else {
                      // keyup 之后 1s 之内不操作，则做一次记录
                      if (timeoutId) {
                          clearTimeout(timeoutId);
                      }
                      timeoutId = setTimeout(undo, 1000);
                  }
              });
  
              // 初始化做记录
              editor.undoRecord();
          });
      });
  
  });
  // redo 菜单
  _e(function (E, $) {
  
      E.createMenu(function (check) {
          var menuId = 'redo';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var lang = editor.config.lang;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.redo
          });
  
          // click 事件
          menu.clickEvent = function (e) {
              editor.redo();
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // 全屏 菜单
  _e(function (E, $) {
  
      // 记录全屏时的scrollTop
      var scrollTopWhenFullScreen;
  
      E.createMenu(function (check) {
          var menuId = 'fullscreen';
          if (!check(menuId)) {
              return;
          }
          var editor = this;
          var $txt = editor.txt.$txt;
          var config = editor.config;
          var zIndexConfig = config.zindex || 10000;
          var lang = config.lang;
  
          var isSelected = false;
          var zIndex;
  
          var maxHeight;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,
              id: menuId,
              title: lang.fullscreen
          });
  
          // 定义click事件
          menu.clickEvent = function (e) {
              // 增加样式
              var $editorContainer = editor.$editorContainer;
              $editorContainer.addClass('wangEditor-fullscreen');
  
              // （先保存当前的）再设置z-index
              zIndex = $editorContainer.css('z-index');
              $editorContainer.css('z-index', zIndexConfig);
  
              var $wrapper;
              var txtHeight = $txt.height();
              var txtOuterHeight = $txt.outerHeight();
  
              if (editor.useMaxHeight) {
                  // 记录 max-height，并暂时去掉maxheight
                  maxHeight = $txt.css('max-height');
                  $txt.css('max-height', 'none');
  
                  // 如果使用了maxHeight， 将$txt从它的父元素中移出来
                  $wrapper = $txt.parent();
                  $wrapper.after($txt);
                  $wrapper.remove();
                  $txt.css('overflow-y', 'auto');
              }
  
              // 设置高度到全屏
              var menuContainer = editor.menuContainer;
              $txt.height(
                  E.$window.height() - 
                  menuContainer.height() - 
                  (txtOuterHeight - txtHeight)  // 去掉内边距和外边距
              );
  
              // 取消menuContainer的内联样式（menu吸顶时，会为 menuContainer 设置一些内联样式）
              editor.menuContainer.$menuContainer.attr('style', '');
  
              // 保存状态
              isSelected = true;
  
              // 记录编辑器是否全屏
              editor.isFullScreen = true;
  
              // 记录设置全屏时的高度
              scrollTopWhenFullScreen = E.$window.scrollTop();
          };
  
          // 定义选中状态的 click 事件
          menu.clickEventSelected = function (e) {
              // 取消样式
              var $editorContainer = editor.$editorContainer;
              $editorContainer.removeClass('wangEditor-fullscreen');
              $editorContainer.css('z-index', zIndex);
  
              // 还原height
              if (editor.useMaxHeight) {
                  $txt.css('max-height', maxHeight);
              } else {
                  // editor.valueContainerHeight 在 editor.txt.initHeight() 中事先保存了
                  editor.$valueContainer.css('height', editor.valueContainerHeight);
              }
  
              // 重新计算高度
              editor.txt.initHeight();
  
              // 保存状态
              isSelected = false;
  
              // 记录编辑器是否全屏
              editor.isFullScreen = false;
  
              // 还原scrollTop
              if (scrollTopWhenFullScreen != null) {
                  E.$window.scrollTop(scrollTopWhenFullScreen);
              }
          };
  
          // 定义选中事件
          menu.updateSelectedEvent = function (e) {
              return isSelected;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // 渲染menus
  _e(function (E, $) {
  
      E.fn.renderMenus = function () {
  
          var editor = this;
          var menus = editor.menus;
          var menuIds = editor.config.menus;
          var menuContainer = editor.menuContainer;
  
          var menu;
          var groupIdx = 0;
          $.each(menuIds, function (k, v) {
              if (v === '|') {
                  groupIdx++;
                  return;
              }
  
              menu = menus[v];
              if (menu) {
                  menu.render(groupIdx);
              }
          });
      };
  
  });
  // 渲染menus
  _e(function (E, $) {
  
      E.fn.renderMenuContainer = function () {
  
          var editor = this;
          var menuContainer = editor.menuContainer;
          var $editorContainer = editor.$editorContainer;
  
          menuContainer.render();
  
      };
  
  });
  // 渲染 txt
  _e(function (E, $) {
  
      E.fn.renderTxt = function () {
  
          var editor = this;
          var txt = editor.txt;
  
          txt.render();
  
          // ready 时候，计算txt的高度
          editor.ready(function () {
              txt.initHeight();
          });
      };
  
  });
  // 渲染 container
  _e(function (E, $) {
  
      E.fn.renderEditorContainer = function () {
  
          var editor = this;
          var $valueContainer = editor.$valueContainer;
          var $editorContainer = editor.$editorContainer;
          var $txt = editor.txt.$txt;
          var $prev, $parent;
  
          // 将编辑器渲染到页面中
          if ($valueContainer === $txt) {
              $prev = editor.$prev;
              $parent = editor.$parent;
  
              if ($prev && $prev.length) {
                  // 有前置节点，就插入到前置节点的后面
                  $prev.after($editorContainer);
              } else {
                  // 没有前置节点，就直接插入到父元素
                  $parent.prepend($editorContainer);
              }
  
          } else {
              $valueContainer.after($editorContainer);
              $valueContainer.hide();
          }
  
          // 设置宽度（这样设置宽度有问题）
          // $editorContainer.css('width', $valueContainer.css('width'));
      };
  
  });
  // 菜单事件
  _e(function (E, $) {
  
      // 绑定每个菜单的click事件
      E.fn.eventMenus = function () {
  
          var menus = this.menus;
  
          // 绑定菜单的点击事件
          $.each(menus, function (k, v) {
              v.bindEvent();
          });
  
      };
  
  });
  // 菜单container事件
  _e(function (E, $) {
  
      E.fn.eventMenuContainer = function () {
  
      };
  
  });
  // 编辑区域事件
  _e(function (E, $) {
  
      E.fn.eventTxt = function () {
  
          var txt = this.txt;
  
          // txt内容变化时，保存选区
          txt.saveSelectionEvent();
  
          // txt内容变化时，随时更新 value
          txt.updateValueEvent();
  
          // txt内容变化时，随时更新 menu style
          txt.updateMenuStyleEvent();
  
          // // 鼠标hover时，显示 p head 高度（暂时关闭这个功能）
          // if (!/ie/i.test(E.userAgent)) {
          //     // 暂时不支持IE
          //     txt.showHeightOnHover();
          // }
      };
  
  });
  // 上传图片事件
  _e(function (E, $) {
  
      E.plugin(function () {
          var editor = this;
          var fns = editor.config.uploadImgFns; // editor.config.uploadImgFns = {} 在config文件中定义了
  
          // -------- 定义load函数 --------
          fns.onload || (fns.onload = function (resultText, xhr) {
              E.log('上传结束，返回结果为 ' + resultText);
  
              var editor = this;
              var originalName = editor.uploadImgOriginalName || '';  // 上传图片时，已经将图片的名字存在 editor.uploadImgOriginalName
              var img;
              if (resultText.indexOf('error|') === 0) {
                  // 提示错误
                  E.warn('上传失败：' + resultText.split('|')[1]);
                  alert(resultText.split('|')[1]);
              } else {
                  E.log('上传成功，即将插入编辑区域，结果为：' + resultText);
  
                  // 将结果插入编辑器
                  img = document.createElement('img');
                  img.onload = function () {
                      var html = '<img src="' + resultText + '" alt="' + originalName + '" style="max-width:100%;"/>';
                      editor.command(null, 'insertHtml', html);
  
                      E.log('已插入图片，地址 ' + resultText);
                      img = null;
                  };
                  img.onerror = function () {
                      E.error('使用返回的结果获取图片，发生错误。请确认以下结果是否正确：' + resultText);
                      img = null;
                  };
                  img.src = resultText;
              }
  
          });
  
          // -------- 定义tiemout函数 --------
          fns.ontimeout || (fns.ontimeout = function (xhr) {
              E.error('上传图片超时');
              alert('上传图片超时');
          });
  
          // -------- 定义error函数 --------
          fns.onerror || (fns.onerror = function (xhr) {
              E.error('上传上图片发生错误');
              alert('上传上图片发生错误');
          });
  
      });
  });
  // xhr 上传图片
  _e(function (E, $) {
  
      if (!window.FileReader || !window.FormData) {
          // 如果不支持html5的文档操作，直接返回
          return;
      }
  
      E.plugin(function () {
  
          var editor = this;
          var config = editor.config;
          var uploadImgUrl = config.uploadImgUrl;
          var uploadTimeout = config.uploadTimeout;
  
          // 获取配置中的上传事件
          var uploadImgFns = config.uploadImgFns;
          var onload = uploadImgFns.onload;
          var ontimeout = uploadImgFns.ontimeout;
          var onerror = uploadImgFns.onerror;
  
          if (!uploadImgUrl) {
              return;
          }
  
          // -------- 将以base64的图片url数据转换为Blob --------
          function convertBase64UrlToBlob(urlData, filetype){
              //去掉url的头，并转换为byte
              var bytes = window.atob(urlData.split(',')[1]);
              
              //处理异常,将ascii码小于0的转换为大于0
              var ab = new ArrayBuffer(bytes.length);
              var ia = new Uint8Array(ab);
              var i;
              for (i = 0; i < bytes.length; i++) {
                  ia[i] = bytes.charCodeAt(i);
              }
  
              return new Blob([ab], {type : filetype});
          }
  
          // -------- 插入图片的方法 --------
          function insertImg(src, event) {
              var img = document.createElement('img');
              img.onload = function () {
                  var html = '<img src="' + src + '" style="max-width:100%;"/>';
                  editor.command(event, 'insertHtml', html);
  
                  E.log('已插入图片，地址 ' + src);
                  img = null;
              };
              img.onerror = function () {
                  E.error('使用返回的结果获取图片，发生错误。请确认以下结果是否正确：' + src);
                  img = null;
              };
              img.src = src;
          }
  
          // -------- onprogress 事件 --------
          function updateProgress(e) {
              if (e.lengthComputable) {
                  var percentComplete = e.loaded / e.total;
                  editor.showUploadProgress(percentComplete * 100);
              }
          }
  
          // -------- xhr 上传图片 --------
          editor.xhrUploadImg = function (opt) {
              // opt 数据
              var event = opt.event;
              var fileName = opt.filename || '';
              var base64 = opt.base64;
              var fileType = opt.fileType || 'image/png'; // 无扩展名则默认使用 png
              var name = opt.name || 'wangEditor_upload_file';
              var loadfn = opt.loadfn || onload;
              var errorfn = opt.errorfn || onerror;
              var timeoutfn = opt.timeoutfn || ontimeout;
  
              // 上传参数（如 token）
              var params = editor.config.uploadParams || {};
  
              // headers
              var headers = editor.config.uploadHeaders || {};
  
              // 获取文件扩展名
              var fileExt = 'png';  // 默认为 png
              if (fileName.indexOf('.') > 0) {
                  // 原来的文件名有扩展名
                  fileExt = fileName.slice(fileName.lastIndexOf('.') - fileName.length + 1);
              } else if (fileType.indexOf('/') > 0 && fileType.split('/')[1]) {
                  // 文件名没有扩展名，通过类型获取，如从 'image/png' 取 'png'
                  fileExt = fileType.split('/')[1];
              }
  
              // ------------ begin 预览模拟上传 ------------
              if (E.isOnWebsite) {
                  E.log('预览模拟上传');
                  insertImg(base64, event);
                  return;
              }
              // ------------ end 预览模拟上传 ------------
  
              // 变量声明
              var xhr = new XMLHttpRequest();
              var timeoutId;
              var src;
              var formData = new FormData();
  
              // 超时处理
              function timeoutCallback() {
                  if (timeoutId) {
                      clearTimeout(timeoutId);
                  }
                  if (xhr && xhr.abort) {
                      xhr.abort();
                  }
  
                  // 超时了就阻止默认行为
                  event.preventDefault();
  
                  // 执行回调函数，提示什么内容，都应该在回调函数中定义
                  timeoutfn && timeoutfn.call(editor, xhr);
  
                  // 隐藏进度条
                  editor.hideUploadProgress();
              }
  
              xhr.onload = function () {
                  if (timeoutId) {
                      clearTimeout(timeoutId);
                  }
  
                  // 记录文件名到 editor.uploadImgOriginalName ，插入图片时，可做 alt 属性用
                  editor.uploadImgOriginalName = fileName;
                  if (fileName.indexOf('.') > 0) {
                      editor.uploadImgOriginalName = fileName.split('.')[0];
                  }
  
                  // 执行load函数，任何操作，都应该在load函数中定义
                  loadfn && loadfn.call(editor, xhr.responseText, xhr);
  
                  // 隐藏进度条
                  editor.hideUploadProgress();
              };
              xhr.onerror = function () {
                  if (timeoutId) {
                      clearTimeout(timeoutId);
                  }
  
                  // 超时了就阻止默认行为
                  event.preventDefault();
  
                  // 执行error函数，错误提示，应该在error函数中定义
                  errorfn && errorfn.call(editor, xhr);
  
                  // 隐藏进度条
                  editor.hideUploadProgress();
              };
              // xhr.onprogress = updateProgress;
              xhr.upload.onprogress = updateProgress;
  
              // 填充数据
              formData.append(name, convertBase64UrlToBlob(base64, fileType), E.random() + '.' + fileExt);
  
              // 添加参数
              $.each(params, function (key, value) {
                  formData.append(key, value);
              });
  
              // 开始上传
              xhr.open('POST', uploadImgUrl, true);
              // xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");  // 将参数解析成传统form的方式上传
  
              // 修改自定义配置的headers
              $.each(headers, function (key, value) {
                  xhr.setRequestHeader(key, value);
              });
  
              // 发送数据
              xhr.send(formData);
              timeoutId = setTimeout(timeoutCallback, uploadTimeout);
  
              E.log('开始上传...并开始超时计算');
          };
      });
  });
  // 进度条
  _e(function (E, $) {
  
      E.plugin(function () {
  
          var editor = this;
          var menuContainer = editor.menuContainer;
          var menuHeight = menuContainer.height();
          var $editorContainer = editor.$editorContainer;
          var width = $editorContainer.width();
          var $progress = $('<div class="wangEditor-upload-progress"></div>');
  
          // 渲染事件
          var isRender = false;
          function render() {
              if (isRender) {
                  return;
              }
              isRender = true;
  
              $progress.css({
                  top: menuHeight + 'px'
              });
              $editorContainer.append($progress);
          }
  
          // ------ 显示进度 ------
          editor.showUploadProgress = function (progress) {
              if (timeoutId) {
                  clearTimeout(timeoutId);
              }
  
              // 显示之前，先判断是否渲染
              render();
  
              $progress.show();
              $progress.width(progress * width / 100);
          };
  
          // ------ 隐藏进度条 ------
          var timeoutId;
          function hideProgress() {
              $progress.hide();
              timeoutId = null;
          }
          editor.hideUploadProgress = function (time) {
              if (timeoutId) {
                  clearTimeout(timeoutId);
              }
              time = time || 750;
              timeoutId = setTimeout(hideProgress, time);
          };
      });
  });
  // upload img 插件
  _e(function (E, $) {
  
      E.plugin(function () {
          var editor = this;
          var config = editor.config;
          var uploadImgUrl = config.uploadImgUrl;
          var uploadTimeout = config.uploadTimeout;
          var event;
  
          if (!uploadImgUrl) {
              return;
          }
  
          // 获取editor的上传dom
          var $uploadContent = editor.$uploadContent;
          if (!$uploadContent) {
              return;
          }
  
          // 自定义UI，并添加到上传dom节点上
          var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
          $uploadContent.append($uploadIcon);
  
          // ---------- 构建上传对象 ----------
          var upfile = new E.UploadFile({
              editor: editor,
              uploadUrl: uploadImgUrl,
              timeout: uploadTimeout,
              fileAccept: 'image/*'    // 只允许选择图片 
          });
  
          // 选择本地文件，上传
          $uploadIcon.click(function (e) {
              event = e;
              upfile.selectFiles();
          });
      });
  });
  // h5 方式上传图片
  _e(function (E, $) {
  
      if (!window.FileReader || !window.FormData) {
          // 如果不支持html5的文档操作，直接返回
          return;
      }
  
      // 构造函数
      var UploadFile = function (opt) {
          this.editor = opt.editor;
          this.uploadUrl = opt.uploadUrl;
          this.timeout = opt.timeout;
          this.fileAccept = opt.fileAccept;
          this.multiple = true;
      };
  
      UploadFile.fn = UploadFile.prototype;
  
      // clear
      UploadFile.fn.clear = function () {
          this.$input.val('');
          E.log('input value 已清空');
      };
  
      // 渲染
      UploadFile.fn.render = function () {
          var self = this;
          if (self._hasRender) {
              // 不要重复渲染
              return;
          }
  
          E.log('渲染dom');
  
          var fileAccept = self.fileAccept;
          var acceptTpl = fileAccept ? 'accept="' + fileAccept + '"' : '';
          var multiple = self.multiple;
          var multipleTpl = multiple ? 'multiple="multiple"' : '';
          var $input = $('<input type="file" ' + acceptTpl + ' ' + multipleTpl + '/>');
          var $container = $('<div style="visibility:hidden;"></div>');
  
          $container.append($input);
          E.$body.append($container);
  
          // onchange 事件
          $input.on('change', function (e) {
              self.selected(e, $input.get(0));
          });
  
          // 记录对象数据
          self.$input = $input;
  
          // 记录
          self._hasRender = true;
      };
  
      // 选择
      UploadFile.fn.selectFiles = function () {
          var self = this;
  
          E.log('使用 html5 方式上传');
  
          // 先渲染
          self.render();
  
          // 选择
          E.log('选择文件');
          self.$input.click();
      };
  
      // 选中文件之后
      UploadFile.fn.selected = function (e, input) {
          var self = this;
          var files = input.files || [];
          if (files.length === 0) {
              return;
          }
  
          E.log('选中 ' + files.length + ' 个文件');
  
          // 遍历选中的文件，预览、上传
          $.each(files, function (key, value) {
              self.upload(value);
          });
      };
  
      // 上传单个文件
      UploadFile.fn.upload = function (file) {
          var self = this;
          var editor = self.editor;
          var filename = file.name || '';
          var fileType = file.type || '';
          var uploadImgFns = editor.config.uploadImgFns;
          var uploadFileName = editor.config.uploadImgFileName || 'wangEditorH5File';
          var onload = uploadImgFns.onload;
          var ontimeout = uploadImgFns.ontimeout;
          var onerror = uploadImgFns.onerror;
          var reader = new FileReader();
  
          if (!onload || !ontimeout || !onerror) {
              E.error('请为编辑器配置上传图片的 onload ontimeout onerror 回调事件');
              return;
          }
  
  
          E.log('开始执行 ' + filename + ' 文件的上传');
  
          // 清空 input 数据
          function clearInput() {
              self.clear();
          }
  
          // onload事件
          reader.onload = function (e) {
              E.log('已读取' + filename + '文件');
  
              var base64 = e.target.result || this.result;
              editor.xhrUploadImg({
                  event: e,
                  filename: filename,
                  base64: base64,
                  fileType: fileType,
                  name: uploadFileName,
                  loadfn: function (resultText, xhr) {
                      clearInput();
                      // 执行配置中的方法
                      var editor = this;
                      onload.call(editor, resultText, xhr);
                  },
                  errorfn: function (xhr) {
                      clearInput();
                      if (E.isOnWebsite) {
                          alert('wangEditor官网暂时没有服务端，因此报错。实际项目中不会发生');
                      }
                      // 执行配置中的方法
                      var editor = this;
                      onerror.call(editor, xhr);
                  },
                  timeoutfn: function (xhr) {
                      clearInput();
                      if (E.isOnWebsite) {
                          alert('wangEditor官网暂时没有服务端，因此超时。实际项目中不会发生');
                      }
                      // 执行配置中的方法
                      var editor = this;
                      ontimeout(editor, xhr);
                  }
              });
          };
  
          // 开始取文件
          reader.readAsDataURL(file);
      };
  
      // 暴露给 E
      E.UploadFile = UploadFile;
  
  });
  // form方式上传图片
  _e(function (E, $) {
  
      if (window.FileReader && window.FormData) {
          // 如果支持 html5 上传，则返回
          return;
      }
      
      // 构造函数
      var UploadFile = function (opt) {
          this.editor = opt.editor;
          this.uploadUrl = opt.uploadUrl;
          this.timeout = opt.timeout;
          this.fileAccept = opt.fileAccept;
          this.multiple = false;
      };
  
      UploadFile.fn = UploadFile.prototype;
  
      // clear
      UploadFile.fn.clear = function () {
          this.$input.val('');
          E.log('input value 已清空');
      };
  
      // 隐藏modal
      UploadFile.fn.hideModal = function () {
          this.modal.hide();
      };
  
      // 渲染
      UploadFile.fn.render = function () {
          var self = this;
          var editor = self.editor;
          var uploadFileName = editor.config.uploadImgFileName || 'wangEditorFormFile';
          if (self._hasRender) {
              // 不要重复渲染
              return;
          }
  
          // 服务器端路径
          var uploadUrl = self.uploadUrl;
  
          E.log('渲染dom');
  
          // 创建 form 和 iframe
          var iframeId = 'iframe' + E.random();
          var $iframe = $('<iframe name="' + iframeId + '" id="' + iframeId + '" frameborder="0" width="0" height="0"></iframe>');
          var multiple = self.multiple;
          var multipleTpl = multiple ? 'multiple="multiple"' : '';
          var $p = $('<p>选择图片并上传</p>');
          var $input = $('<input type="file" ' + multipleTpl + ' name="' + uploadFileName + '"/>');
          var $btn = $('<input type="submit" value="上传"/>');
          var $form = $('<form enctype="multipart/form-data" method="post" action="' + uploadUrl + '" target="' + iframeId + '"></form>');
          var $container = $('<div style="margin:10px 20px;"></div>');
  
          $form.append($p).append($input).append($btn);
  
          // 增加用户配置的参数，如 token
          $.each(editor.config.uploadParams, function (key, value) {
              $form.append( $('<input type="hidden" name="' + key + '" value="' + value + '"/>') );
          });
  
          $container.append($form);
          $container.append($iframe);
  
          self.$input = $input;
          self.$iframe = $iframe;
  
          // 生成 modal
          var modal = new E.Modal(editor, undefined, {
              $content: $container
          });
          self.modal = modal;
  
          // 记录
          self._hasRender = true;
      };
  
      // 绑定 iframe load 事件
      UploadFile.fn.bindLoadEvent = function () {
          var self = this;
          if (self._hasBindLoad) {
              // 不要重复绑定
              return;
          }
  
          var editor = self.editor;
          var $iframe = self.$iframe;
          var iframe = $iframe.get(0);
          var iframeWindow = iframe.contentWindow;
          var onload = editor.config.uploadImgFns.onload;
  
          // 定义load事件
          function onloadFn() {
              var resultText = $.trim(iframeWindow.document.body.innerHTML);
              if (!resultText) {
                  return;
              }
  
              // 获取文件名
              var fileFullName = self.$input.val();  // 结果如 C:\folder\abc.png 格式
              var fileOriginalName = fileFullName;
              if (fileFullName.lastIndexOf('\\') >= 0) {
                  // 获取 abc.png 格式
                  fileOriginalName = fileFullName.slice(fileFullName.lastIndexOf('\\') + 1);
                  if (fileOriginalName.indexOf('.') > 0) {
                      // 获取 abc （即不带扩展名的文件名）
                      fileOriginalName = fileOriginalName.split('.')[0];
                  }
              }
  
              // 将文件名暂存到 editor.uploadImgOriginalName ，插入图片时，可作为 alt 属性来用
              editor.uploadImgOriginalName = fileOriginalName;
  
              // 执行load函数，插入图片的操作，应该在load函数中执行
              onload.call(editor, resultText);
  
              // 清空 input 数据
              self.clear();
  
              // 隐藏modal
              self.hideModal();
          }
  
          // 绑定 load 事件
          if (iframe.attachEvent) {
              iframe.attachEvent('onload', onloadFn);
          } else {
              iframe.onload = onloadFn;
          }
  
          // 记录
          self._hasBindLoad = true;
      };
  
      UploadFile.fn.show = function () {
          var self = this;
          var modal = self.modal;
  
          function show() {
              modal.show();
              self.bindLoadEvent();
          }
          setTimeout(show);
      };
  
      // 选择
      UploadFile.fn.selectFiles = function () {
          var self = this;
  
          E.log('使用 form 方式上传');
  
          // 先渲染
          self.render();
  
          // 先清空
          self.clear();
  
          // 显示
          self.show();
      };
  
      // 暴露给 E
      E.UploadFile = UploadFile;
  
  });
  // upload img 插件 粘贴图片
  _e(function (E, $) {
      
      E.plugin(function () {
          var editor = this;
          var txt = editor.txt;
          var $txt = txt.$txt;
          var config = editor.config;
          var uploadImgUrl = config.uploadImgUrl;
          var uploadFileName = config.uploadImgFileName || 'wangEditorPasteFile';
          var pasteEvent;
          var $imgsBeforePaste;
  
          // 未配置上传图片url，则忽略
          if (!uploadImgUrl) {
              return;
          }
  
          // -------- 非 chrome 下，通过查找粘贴的图片的方式上传 --------
          function findPasteImgAndUpload() {
              var reg = /^data:(image\/\w+);base64/;
              var $imgs = $txt.find('img');
  
              E.log('粘贴后，检查到编辑器有' + $imgs.length + '个图片。开始遍历图片，试图找到刚刚粘贴过来的图片');
  
              $.each($imgs, function () {
                  var img = this;
                  var $img = $(img);
                  var flag;
                  var base64 = $img.attr('src');
                  var type;
  
                  // 判断当前图片是否是粘贴之前的
                  $imgsBeforePaste.each(function () {
                      if (img === this) {
                          // 当前图片是粘贴之前的
                          flag = true;
                          return false;
                      }
                  });
  
                  // 当前图片是粘贴之前的，则忽略
                  if (flag) {
                      return;
                  }
  
                  E.log('找到一个粘贴过来的图片');
  
                  if (reg.test(base64)) {
                      // 得到的粘贴的图片是 base64 格式，符合要求
                      E.log('src 是 base64 格式，可以上传');
                      type = base64.match(reg)[1];
                      editor.xhrUploadImg({
                          event: pasteEvent,
                          base64: base64,
                          fileType: type,
                          name: uploadFileName
                      });
                  } else {
                      E.log('src 为 ' + base64 + ' ，不是 base64 格式，暂时不支持上传');
                  }
  
                  // 最终移除原图片
                  $img.remove();
              });
  
              E.log('遍历结束');
          }
  
          // 开始监控粘贴事件
          $txt.on('paste', function (e) {
              pasteEvent = e;
              var data = pasteEvent.clipboardData || pasteEvent.originalEvent.clipboardData;
              var text;
              var items;
  
              // -------- 试图获取剪切板中的文字，有文字的情况下，就不处理图片粘贴 --------
              if (data == null) {
                  text = window.clipboardData && window.clipboardData.getData('text');
              } else {
                  text = data.getData('text/plain') || data.getData('text/html');
              }
              if (text) {
                  return;
              }
  
              items = data && data.items;
              if (items) {
                  // -------- chrome 可以用 data.items 取出图片 -----
                  E.log('通过 data.items 得到了数据');
  
                  $.each(items, function (key, value) {
                      var fileType = value.type || '';
                      if(fileType.indexOf('image') < 0) {
                          // 不是图片
                          return;
                      }
  
                      var file = value.getAsFile();
                      var reader = new FileReader();
  
                      E.log('得到一个粘贴图片');
  
                      reader.onload = function (e) {
                          E.log('读取到粘贴的图片');
  
                          // 执行上传
                          var base64 = e.target.result || this.result;
                          editor.xhrUploadImg({
                              event: pasteEvent,
                              base64: base64,
                              fileType: fileType,
                              name: uploadFileName
                          });
                      };
  
                      //读取粘贴的文件
                      reader.readAsDataURL(file);
                  });
              } else {
                  // -------- 非 chrome 不能用 data.items 取图片 -----
  
                  E.log('未从 data.items 得到数据，使用检测粘贴图片的方式');
  
                  // 获取
                  $imgsBeforePaste = $txt.find('img');
                  E.log('粘贴前，检查到编辑器有' + $imgsBeforePaste.length + '个图片');
  
                  // 异步上传找到的图片
                  setTimeout(findPasteImgAndUpload, 0);
              }
          });
  
      });
  });
  // 拖拽上传图片 插件 
  _e(function (E, $) {
  
      E.plugin(function () {
  
          var editor = this;
          var txt = editor.txt;
          var $txt = txt.$txt;
          var config = editor.config;
          var uploadImgUrl = config.uploadImgUrl;
          var uploadFileName = config.uploadImgFileName || 'wangEditorDragFile';
  
          // 未配置上传图片url，则忽略
          if (!uploadImgUrl) {
              return;
          }
  
          // 阻止浏览器默认行为
          E.$document.on('dragleave drop dragenter dragover', function (e) {
              e.preventDefault();
          });
  
          // 监控 $txt drop 事件
          $txt.on('drop', function (dragEvent) {
              dragEvent.preventDefault();
  
              var originalEvent = dragEvent.originalEvent;
              var files = originalEvent.dataTransfer && originalEvent.dataTransfer.files;
  
              if (!files || !files.length) {
                  return;
              }
  
              $.each(files, function (k, file) {
                  var type = file.type;
                  var name = file.name;
  
                  if (type.indexOf('image/') < 0) {
                      // 只接收图片
                      return;
                  }
  
                  E.log('得到图片 ' + name);
  
                  var reader = new FileReader();
                  reader.onload = function (e) {
                      E.log('读取到图片 ' + name);
  
                      // 执行上传
                      var base64 = e.target.result || this.result;
                      editor.xhrUploadImg({
                          event: dragEvent,
                          base64: base64,
                          fileType: type,
                          name: uploadFileName
                      });
                  };
  
                  //读取粘贴的文件
                  reader.readAsDataURL(file);
  
              });
          });
      });
  
  });
  // 编辑器区域 table toolbar
  _e(function (E, $) {
  
      E.plugin(function () {
          var editor = this;
          var txt = editor.txt;
          var $txt = txt.$txt;
          // 说明：设置了 max-height 之后，$txt.parent() 负责滚动处理
          var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
          var $currentTable;
  
          // 用到的dom节点
          var isRendered = false;
          var $toolbar = $('<div class="txt-toolbar"></div>');
          var $triangle = $('<div class="tip-triangle"></div>');
          var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
          var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
          var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
  
          // 渲染到页面
          function render() {
              if (isRendered) {
                  return;
              }
              
              // 绑定事件
              bindEvent();
  
              // 拼接 渲染到页面上
              $toolbar.append($triangle)
                      .append($delete)
                      .append($zoomSmall)
                      .append($zoomBig);
              editor.$editorContainer.append($toolbar);
              isRendered = true;
          }
  
          // 绑定事件
          function bindEvent() {
              // 统一执行命令的方法
              var commandFn;
              function command(e, callback) {
                  if (commandFn) {
                      editor.customCommand(e, commandFn, callback);
                  }
              }
  
              // 删除
              $delete.click(function (e) {
                  commandFn = function () {
                      $currentTable.remove();
                  };
                  command(e, function () {
                      setTimeout(hide, 100);
                  });
              });
  
              // 放大
              $zoomBig.click(function (e) {
                  commandFn = function () {
                      $currentTable.css({
                          width: '100%'
                      });
                  };
                  command(e, function () {
                      setTimeout(show);
                  });
              });
  
              // 缩小
              $zoomSmall.click(function (e) {
                  commandFn = function () {
                      $currentTable.css({
                          width: 'auto'
                      });
                  };
                  command(e, function () {
                      setTimeout(show);
                  });
              });
          }
  
          // 显示 toolbar
          function show() {
              if (editor._disabled) {
                  // 编辑器已经被禁用，则不让显示
                  return;
              }
              if ($currentTable == null) {
                  return;
              }
              $currentTable.addClass('clicked');
              var tablePosition = $currentTable.position();
              var tableTop = tablePosition.top;
              var tableLeft = tablePosition.left;
              var tableHeight = $currentTable.outerHeight();
              var tableWidth = $currentTable.outerWidth();
  
              // --- 定位 toolbar ---
  
              // 计算初步结果
              var top = tableTop + tableHeight;
              var left = tableLeft;
              var marginLeft = 0;
  
              var txtTop = $currentTxt.position().top;
              var txtHeight = $currentTxt.outerHeight();
              if (top > (txtTop + txtHeight)) {
                  // top 不得超出编辑范围
                  top = txtTop + txtHeight;
              }
  
              // 显示（方便计算 margin）
              $toolbar.show();
  
              // 计算 margin
              var width = $toolbar.outerWidth();
              marginLeft = tableWidth / 2 - width / 2;
  
              // 定位
              $toolbar.css({
                  top: top + 5,
                  left: left,
                  'margin-left': marginLeft
              });
              // 如果定位太靠左了
              if (marginLeft < 0) {
                  // 得到三角形的margin-left
                  $toolbar.css('margin-left', '0');
                  $triangle.hide();
              } else {
                  $triangle.show();
              }
          }
          
          // 隐藏 toolbar
          function hide() {
              if ($currentTable == null) {
                  return;
              }
              $currentTable.removeClass('clicked');
              $currentTable = null;
              $toolbar.hide();
          }
  
          // click table 事件
          $currentTxt.on('click', 'table', function (e) {
              var $table = $(e.currentTarget);
  
              // 渲染
              render();
  
              if ($currentTable && ($currentTable.get(0) === $table.get(0))) {
                  setTimeout(hide, 100);
                  return;
              }
  
              // 显示 toolbar
              $currentTable = $table;
              show();
  
              // 阻止冒泡
              e.preventDefault();
              e.stopPropagation();
              
          }).on('click keydown scroll', function (e) {
              setTimeout(hide, 100);
          });
          E.$body.on('click keydown scroll', function (e) {
              setTimeout(hide, 100);
          });
      });
  
  });
  // 编辑器区域 img toolbar
  _e(function (E, $) {
  
      if (E.userAgent.indexOf('MSIE 8') > 0) {
          return;
      }
      
      E.plugin(function () {
          var editor = this;
          var lang = editor.config.lang;
          var txt = editor.txt;
          var $txt = txt.$txt;
          // 说明：设置了 max-height 之后，$txt.parent() 负责滚动处理
          var $currentTxt = editor.useMaxHeight ? $txt.parent() : $txt;
          var $editorContainer = editor.$editorContainer;
          var $currentImg;
          var currentLink = '';
  
          // 用到的dom节点
          var isRendered = false;
          var $dragPoint = $('<div class="img-drag-point"></div>');
  
          var $toolbar = $('<div class="txt-toolbar"></div>');
          var $triangle = $('<div class="tip-triangle"></div>');
  
          var $menuContainer = $('<div></div>');
          var $delete = $('<a href="#"><i class="wangeditor-menu-img-trash-o"></i></a>');
          var $zoomSmall = $('<a href="#"><i class="wangeditor-menu-img-search-minus"></i></a>');
          var $zoomBig = $('<a href="#"><i class="wangeditor-menu-img-search-plus"></i></a>');
          // var $floatLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
          // var $noFloat = $('<a href="#"><i class="wangeditor-menu-img-align-justify"></i></a>');
          // var $floatRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
          var $alignLeft = $('<a href="#"><i class="wangeditor-menu-img-align-left"></i></a>');
          var $alignCenter = $('<a href="#"><i class="wangeditor-menu-img-align-center"></i></a>');
          var $alignRight = $('<a href="#"><i class="wangeditor-menu-img-align-right"></i></a>');
          var $link = $('<a href="#"><i class="wangeditor-menu-img-link"></i></a>');
          var $unLink = $('<a href="#"><i class="wangeditor-menu-img-unlink"></i></a>');
  
          var $linkInputContainer = $('<div style="display:none;"></div>');
          var $linkInput = $('<input type="text" style="height:26px; margin-left:10px; width:200px;"/>');
          var $linkBtnSubmit = $('<button class="right">' + lang.submit + '</button>');
          var $linkBtnCancel = $('<button class="right gray">' + lang.cancel + '</button>');
  
          // 记录是否正在拖拽
          var isOnDrag = false;
  
          // 获取 / 设置 链接
          function imgLink(e, url) {
              if (!$currentImg) {
                  return;
              }
              var commandFn;
              var callback = function () {
                  // 及时保存currentLink
                  if (url != null) {
                      currentLink = url;
                  }
              };
              var $link;
              var inLink = false;
              var $parent = $currentImg.parent();
              if ($parent.get(0).nodeName.toLowerCase() === 'a') {
                  // 父元素就是图片链接
                  $link = $parent;
                  inLink = true;
              } else {
                  // 父元素不是图片链接，则重新创建一个链接
                  $link = $('<a target="_blank"></a>');
              }
  
              if (url == null) {
                  // url 无值，是获取链接
                  return $link.attr('href') || '';
              } else if (url === '') {
                  // url 是空字符串，是取消链接
                  if (inLink) {
                      commandFn = function () {
                          $currentImg.unwrap();
                      };
                  }
              } else {
                  // url 有值，是设置链接
                  if (url === currentLink) {
                      return;
                  }
                  commandFn = function () {
                      $link.attr('href', url);
  
                      if (!inLink) {
                          // 当前图片未包含在链接中，则包含进来
                          $currentImg.wrap($link);
                      }
                  };
              }
  
              // 执行命令
              if (commandFn) {
                  editor.customCommand(e, commandFn, callback);
              }
          }
  
          // 渲染到页面
          function render() {
              if (isRendered) {
                  return;
              }
              
              // 绑定事件
              bindToolbarEvent();
              bindDragEvent();
  
              // 菜单放入 container
              $menuContainer.append($delete)
                              .append($zoomSmall)
                              .append($zoomBig)
                              // .append($floatLeft)
                              // .append($noFloat)
                              // .append($floatRight);
                              .append($alignLeft)
                              .append($alignCenter)
                              .append($alignRight)
                              .append($link)
                              .append($unLink);
  
              // 链接input放入container
              $linkInputContainer.append($linkInput)
                                 .append($linkBtnCancel)
                                 .append($linkBtnSubmit);
  
              // 拼接 渲染到页面上
              $toolbar.append($triangle)
                      .append($menuContainer)
                      .append($linkInputContainer);
                      
              editor.$editorContainer.append($toolbar).append($dragPoint);
              isRendered = true;
          }
  
          // 绑定toolbar事件
          function bindToolbarEvent() {
              // 统一执行命令的方法
              var commandFn;
              function customCommand(e, callback) {
                  if (commandFn) {
                      editor.customCommand(e, commandFn, callback);
                  }
              }
  
              // 删除
              $delete.click(function (e) {
                  // 删除之前先unlink
                  imgLink(e, '');
  
                  // 删除图片
                  commandFn = function () {
                      $currentImg.remove();
                  };
                  customCommand(e, function () {
                      setTimeout(hide, 100);
                  });
              });
  
              // 放大
              $zoomBig.click(function (e) {
                  commandFn = function () {
                      var img = $currentImg.get(0);
                      var width = img.width;
                      var height = img.height;
                      width = width * 1.1;
                      height = height * 1.1;
  
                      $currentImg.css({
                          width: width + 'px',
                          height: height + 'px'
                      });
                  };
                  customCommand(e, function () {
                      setTimeout(show);
                  });
              });
  
              // 缩小
              $zoomSmall.click(function (e) {
                  commandFn = function () {
                      var img = $currentImg.get(0);
                      var width = img.width;
                      var height = img.height;
                      width = width * 0.9;
                      height = height * 0.9;
  
                      $currentImg.css({
                          width: width + 'px',
                          height: height + 'px'
                      });
                  };
                  customCommand(e, function () {
                      setTimeout(show);
                  });
              });
  
              // // 左浮动
              // $floatLeft.click(function (e) {
              //     commandFn = function () {
              //         $currentImg.css({
              //             float: 'left'
              //         });
              //     };
              //     customCommand(e, function () {
              //         setTimeout(hide, 100);
              //     });
              // });
  
              // alignLeft
              $alignLeft.click(function (e) {
                  commandFn = function () {
                      // 如果 img 增加了链接，那么 img.parent() 就是 a 标签，设置 align 没用的，因此必须找到 P 父节点来设置 align
                      $currentImg.parents('p').css({
                          'text-align': 'left'
                      }).attr('align', 'left');
                  };
                  customCommand(e, function () {
                      setTimeout(hide, 100);
                  });
              });
  
              // // 右浮动
              // $floatRight.click(function (e) {
              //     commandFn = function () {
              //         $currentImg.css({
              //             float: 'right'
              //         });
              //     };
              //     customCommand(e, function () {
              //         setTimeout(hide, 100);
              //     });
              // });
  
              // alignRight
              $alignRight.click(function (e) {
                  commandFn = function () {
                      // 如果 img 增加了链接，那么 img.parent() 就是 a 标签，设置 align 没用的，因此必须找到 P 父节点来设置 align
                      $currentImg.parents('p').css({
                          'text-align': 'right'
                      }).attr('align', 'right');
                  };
                  customCommand(e, function () {
                      setTimeout(hide, 100);
                  });
              });
  
              // // 无浮动
              // $noFloat.click(function (e) {
              //     commandFn = function () {
              //         $currentImg.css({
              //             float: 'none'
              //         });
              //     };
              //     customCommand(e, function () {
              //         setTimeout(hide, 100);
              //     });
              // });
  
              // alignCenter
              $alignCenter.click(function (e) {
                  commandFn = function () {
                      // 如果 img 增加了链接，那么 img.parent() 就是 a 标签，设置 align 没用的，因此必须找到 P 父节点来设置 align
                      $currentImg.parents('p').css({
                          'text-align': 'center'
                      }).attr('align', 'center');
                  };
                  customCommand(e, function () {
                      setTimeout(hide, 100);
                  });
              });
  
              // link
              // 显示链接input
              $link.click(function (e) {
                  e.preventDefault();
  
                  // 获取当前链接，并显示
                  currentLink = imgLink(e);
                  $linkInput.val(currentLink);
  
                  $menuContainer.hide();
                  $linkInputContainer.show();
              });
              // 设置链接
              $linkBtnSubmit.click(function (e) {
                  e.preventDefault();
  
                  var url = $.trim($linkInput.val());
                  if (url) {
                      // 设置链接，同时会自动更新 currentLink 的值
                      imgLink(e, url);
                  }
  
                  // 隐藏 toolbar
                  setTimeout(hide);
              });
              // 取消设置链接
              $linkBtnCancel.click(function (e) {
                  e.preventDefault();
  
                  // 重置链接 input
                  $linkInput.val(currentLink);
  
                  $menuContainer.show();
                  $linkInputContainer.hide();
              });
  
              // unlink
              $unLink.click(function (e) {
                  e.preventDefault();
  
                  // 执行 unlink
                  imgLink(e, '');
  
                  // 隐藏 toolbar
                  setTimeout(hide);
              });
          }
  
          // 绑定drag事件
          function bindDragEvent() {
              var _x, _y;
              var dragMarginLeft, dragMarginTop;
              var imgWidth, imgHeight;
  
              function mousemove (e) {
                  var diffX, diffY;
  
                  // 计算差额
                  diffX = e.pageX - _x;
                  diffY = e.pageY - _y;
  
                  // --------- 计算拖拽点的位置 ---------
                  var currentDragMarginLeft = dragMarginLeft + diffX;
                  var currentDragMarginTop = dragMarginTop + diffY;
                  $dragPoint.css({
                      'margin-left': currentDragMarginLeft,
                      'margin-top': currentDragMarginTop
                  });
  
                  // --------- 计算图片的大小 ---------
                  var currentImgWidth = imgWidth + diffX;
                  var currentImggHeight = imgHeight + diffY;
                  $currentImg && $currentImg.css({
                      width: currentImgWidth,
                      height: currentImggHeight
                  });
              }
  
              $dragPoint.on('mousedown', function(e){
                  if (!$currentImg) {
                      return;
                  }
                  // 当前鼠标位置
                  _x = e.pageX;
                  _y = e.pageY;
  
                  // 当前拖拽点的位置
                  dragMarginLeft = parseFloat($dragPoint.css('margin-left'), 10);
                  dragMarginTop = parseFloat($dragPoint.css('margin-top'), 10);
  
                  // 当前图片的大小
                  imgWidth = $currentImg.width();
                  imgHeight = $currentImg.height();
  
                  // 隐藏 $toolbar
                  $toolbar.hide();
  
                  // 绑定计算事件
                  E.$document.on('mousemove._dragResizeImg', mousemove);
                  E.$document.on('mouseup._dragResizeImg', function (e) {
                      // 取消绑定
                      E.$document.off('mousemove._dragResizeImg');
                      E.$document.off('mouseup._dragResizeImg');
  
                      // 隐藏，并还原拖拽点的位置
                      hide();
                      $dragPoint.css({
                          'margin-left': dragMarginLeft,
                          'margin-top': dragMarginTop
                      });
  
                      // 记录
                      isOnDrag = false;
                  });
  
                  // 记录
                  isOnDrag = true;
              });
          }
  
          // 显示 toolbar
          function show() {
              if (editor._disabled) {
                  // 编辑器已经被禁用，则不让显示
                  return;
              }
              if ($currentImg == null) {
                  return;
              }
              $currentImg.addClass('clicked');
              var imgPosition = $currentImg.position();
              var imgTop = imgPosition.top;
              var imgLeft = imgPosition.left;
              var imgHeight = $currentImg.outerHeight();
              var imgWidth = $currentImg.outerWidth();
  
  
              // --- 定位 dragpoint ---
              $dragPoint.css({
                  top: imgTop + imgHeight,
                  left: imgLeft + imgWidth
              });
  
              // --- 定位 toolbar ---
  
              // 计算初步结果
              var top = imgTop + imgHeight;
              var left = imgLeft;
              var marginLeft = 0;
  
              var txtTop = $currentTxt.position().top;
              var txtHeight = $currentTxt.outerHeight();
              if (top > (txtTop + txtHeight)) {
                  // top 不得超出编辑范围
                  top = txtTop + txtHeight;
              } else {
                  // top 超出编辑范围，dragPoint就不显示了
                  $dragPoint.show();
              }
  
              // 显示（方便计算 margin）
              $toolbar.show();
  
              // 计算 margin
              var width = $toolbar.outerWidth();
              marginLeft = imgWidth / 2 - width / 2;
  
              // 定位
              $toolbar.css({
                  top: top + 5,
                  left: left,
                  'margin-left': marginLeft
              });
              // 如果定位太靠左了
              if (marginLeft < 0) {
                  // 得到三角形的margin-left
                  $toolbar.css('margin-left', '0');
                  $triangle.hide();
              } else {
                  $triangle.show();
              }
  
              // disable 菜单
              editor.disableMenusExcept();
          }
          
          // 隐藏 toolbar
          function hide() {
              if ($currentImg == null) {
                  return;
              }
              $currentImg.removeClass('clicked');
              $currentImg = null;
  
              $toolbar.hide();
              $dragPoint.hide();
  
              // enable 菜单
              editor.enableMenusExcept();
          }
  
          // 判断img是否是一个表情
          function isEmotion(imgSrc) {
              var result = false;
              if (!editor.emotionUrls) {
                  return result;
              }
              $.each(editor.emotionUrls, function (index, url) {
                  var flag = false;
                  if (imgSrc === url) {
                      result = true;
                      flag = true;
                  }
                  if (flag) {
                      return false;  // break 循环
                  }
              });
              return result;
          }
  
          // click img 事件
          $currentTxt.on('mousedown', 'img', function (e) {
              e.preventDefault();
          }).on('click', 'img', function (e) {
              var $img = $(e.currentTarget);
              var src = $img.attr('src');
  
              if (!src || isEmotion(src)) {
                  // 是一个表情图标
                  return;
              }
  
              // ---------- 不是表情图标 ---------- 
  
              // 渲染
              render();
  
              if ($currentImg && ($currentImg.get(0) === $img.get(0))) {
                  setTimeout(hide, 100);
                  return;
              }
  
              // 显示 toolbar
              $currentImg = $img;
              show();
  
              // 默认显示menuContainer，其他默认隐藏
              $menuContainer.show();
              $linkInputContainer.hide();
  
              // 阻止冒泡
              e.preventDefault();
              e.stopPropagation();
              
          }).on('click keydown scroll', function (e) {
              if (!isOnDrag) {
                  setTimeout(hide, 100);
              }
          });
  
      });
  
  });
  // 编辑区域 link toolbar
  _e(function (E, $) {
      E.plugin(function () {
          var editor = this;
          var lang = editor.config.lang;
          var $txt = editor.txt.$txt;
  
          // 当前命中的链接
          var $currentLink;
  
          var $toolbar = $('<div class="txt-toolbar"></div>');
          var $triangle = $('<div class="tip-triangle"></div>');
          var $triggerLink = $('<a href="#" target="_blank"><i class="wangeditor-menu-img-link"></i> ' + lang.openLink + '</a>');
          var isRendered;
  
          // 记录当前的显示/隐藏状态
          var isShow = false;
  
          var showTimeoutId, hideTimeoutId;
          var showTimeoutIdByToolbar, hideTimeoutIdByToolbar;
  
          // 渲染 dom
          function render() {
              if (isRendered) {
                  return;
              }
  
              $toolbar.append($triangle)
                      .append($triggerLink);
  
              editor.$editorContainer.append($toolbar);
  
              isRendered = true;
          }
  
          // 定位
          function setPosition() {
              if (!$currentLink) {
                  return;
              }
  
              var position = $currentLink.position();
              var left = position.left;
              var top = position.top;
              var height = $currentLink.height();
  
              // 初步计算top值
              var topResult = top + height + 5;
  
              // 判断 toolbar 是否超过了编辑器区域的下边界
              var menuHeight = editor.menuContainer.height();
              var txtHeight = editor.txt.$txt.outerHeight();
              if (topResult > menuHeight + txtHeight) {
                  topResult = menuHeight + txtHeight + 5;
              }
  
              // 最终设置
              $toolbar.css({
                  top: topResult,
                  left: left
              });
          }
  
          // 显示 toolbar
          function show() {
              if (isShow) {
                  return;
              }
  
              if (!$currentLink) {
                  return;
              }
  
              render();
  
              $toolbar.show();
  
              // 设置链接
              var href = $currentLink.attr('href');
              $triggerLink.attr('href', href);
  
              // 定位
              setPosition();
  
              isShow = true;
          }
  
          // 隐藏 toolbar
          function hide() {
              if (!isShow) {
                  return;
              }
  
              if (!$currentLink) {
                  return;
              }
  
              $toolbar.hide();
              isShow = false;
          }
  
          // $txt 绑定事件
          $txt.on('mouseenter', 'a', function (e) {
              // 延时 500ms 显示toolbar
              if (showTimeoutId) {
                  clearTimeout(showTimeoutId);
              }
              showTimeoutId = setTimeout(function () {
                  var a = e.currentTarget;
                  var $a = $(a);
                  $currentLink = $a;
  
                  var $img = $a.children('img');
                  if ($img.length) {
                      // 该链接下包含一个图片
  
                      // 图片点击时，隐藏toolbar
                      $img.click(function (e) {
                          hide();
                      });
  
                      if ($img.hasClass('clicked')) {
                          // 图片还处于clicked状态，则不显示toolbar
                          return;
                      }
                  }
  
                  // 显示toolbar
                  show();
              }, 500);
          }).on('mouseleave', 'a', function (e) {
              // 延时 500ms 隐藏toolbar
              if (hideTimeoutId) {
                  clearTimeout(hideTimeoutId);
              }
              hideTimeoutId = setTimeout(hide, 500);
          }).on('click keydown scroll', function (e) {
              setTimeout(hide, 100);
          });
          // $toolbar 绑定事件
          $toolbar.on('mouseenter', function (e) {
              // 先中断掉 $txt.mouseleave 导致的隐藏
              if (hideTimeoutId) {
                  clearTimeout(hideTimeoutId);
              }
          }).on('mouseleave', function (e) {
              // 延时 500ms 显示toolbar
              if (showTimeoutIdByToolbar) {
                  clearTimeout(showTimeoutIdByToolbar);
              }
              showTimeoutIdByToolbar = setTimeout(hide, 500);
          });
      });
  });
  // menu吸顶
  _e(function (E, $) {
  
      E.plugin(function () {
          var editor = this;
          var menuFixed = editor.config.menuFixed;
          if (menuFixed === false || typeof menuFixed !== 'number') {
              // 没有配置菜单吸顶
              return;
          }
          var bodyMarginTop = parseFloat(E.$body.css('margin-top'), 10);
          if (isNaN(bodyMarginTop)) {
              bodyMarginTop = 0;
          }
  
          var $editorContainer = editor.$editorContainer;
          var editorTop = $editorContainer.offset().top;
          var editorHeight = $editorContainer.outerHeight();
          
          var $menuContainer = editor.menuContainer.$menuContainer;
          var menuCssPosition = $menuContainer.css('position');
          var menuCssTop = $menuContainer.css('top');
          var menuTop = $menuContainer.offset().top;
          var menuHeight = $menuContainer.outerHeight();
          
          var $txt = editor.txt.$txt;
  
          E.$window.scroll(function () {
              //全屏模式不支持
              if (editor.isFullScreen) {
                  return;
              }
  
              var sTop = E.$window.scrollTop();
  
              // 需要重新计算宽度，因为浏览器可能此时出现滚动条
              var menuWidth = $menuContainer.width();
  
              // 如果 menuTop === 0 说明此前编辑器一直隐藏，后来显示出来了，要重新计算相关数据
              if (menuTop === 0) {
                  menuTop = $menuContainer.offset().top;
                  editorTop = $editorContainer.offset().top;
                  editorHeight = $editorContainer.outerHeight();
                  menuHeight = $menuContainer.outerHeight();
              }
  
              if (sTop >= menuTop && sTop + menuFixed + menuHeight + 30 < editorTop + editorHeight) {
                  // 吸顶
                  $menuContainer.css({
                      position: 'fixed',
                      top: menuFixed
                  });
  
                  // 固定宽度
                  $menuContainer.width(menuWidth);
  
                  // 增加body margin-top
                  E.$body.css({
                      'margin-top': bodyMarginTop + menuHeight
                  });
  
                  // 记录
                  if (!editor._isMenufixed) {
                      editor._isMenufixed = true;
                  }
              } else {
                  // 取消吸顶
                  $menuContainer.css({
                      position: menuCssPosition,
                      top: menuCssTop
                  });
  
                  // 取消宽度固定
                  $menuContainer.css('width', '100%');
  
                  // 还原 body margin-top
                  E.$body.css({
                      'margin-top': bodyMarginTop
                  });
  
                  // 撤销记录
                  if (editor._isMenufixed) {
                      editor._isMenufixed = false;
                  }
              }
          });
      });
  
  });
  // 缩进 菜单插件
  _e(function (E, $) {
  
      // 用 createMenu 方法创建菜单
      E.createMenu(function (check) {
  
          // 定义菜单id，不要和其他菜单id重复。编辑器自带的所有菜单id，可通过『参数配置-自定义菜单』一节查看
          var menuId = 'indent';
  
          // check将检查菜单配置（『参数配置-自定义菜单』一节描述）中是否该菜单id，如果没有，则忽略下面的代码。
          if (!check(menuId)) {
              return;
          }
  
          // this 指向 editor 对象自身
          var editor = this;
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,  // 编辑器对象
              id: menuId,  // 菜单id
              title: '缩进', // 菜单标题
  
              // 正常状态和选中装下的dom对象，样式需要自定义
              $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-indent-left"></i></a>'),
              $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-indent-left"></i></a>')
          });
  
          // 菜单正常状态下，点击将触发该事件
          menu.clickEvent = function (e) {
              var elem = editor.getRangeElem();
              var p = editor.getSelfOrParentByName(elem, 'p');
              var $p;
  
              if (!p) {
                  // 未找到 p 元素，则忽略
                  return e.preventDefault();
              }
              $p = $(p);
  
              // 使用自定义命令
              function commandFn() {
                  $p.css('text-indent', '2em');
              }
              editor.customCommand(e, commandFn);
          };
  
          // 菜单选中状态下，点击将触发该事件
          menu.clickEventSelected = function (e) {
              var elem = editor.getRangeElem();
              var p = editor.getSelfOrParentByName(elem, 'p');
              var $p;
  
              if (!p) {
                  // 未找到 p 元素，则忽略
                  return e.preventDefault();
              }
              $p = $(p);
  
              // 使用自定义命令
              function commandFn() {
                  $p.css('text-indent', '0');
              }
              editor.customCommand(e, commandFn);
          };
  
          // 根据当前选区，自定义更新菜单的选中状态或者正常状态
          menu.updateSelectedEvent = function () {
              // 获取当前选区所在的父元素
              var elem = editor.getRangeElem();
              var p = editor.getSelfOrParentByName(elem, 'p');
              var $p;
              var indent;
  
              if (!p) {
                  // 未找到 p 元素，则标记为未处于选中状态
                  return false;
              }
              $p = $(p);
              indent = $p.css('text-indent');
  
              if (!indent || indent === '0px') {
                  // 得到的p，text-indent 属性是 0，则标记为未处于选中状态
                  return false;
              }
  
              // 找到 p 元素，并且 text-indent 不是 0，则标记为选中状态
              return true;
          };
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
      });
  
  });
  // 行高 菜单插件
  _e(function (E, $) {
  
      // 用 createMenu 方法创建菜单
      E.createMenu(function (check) {
  
          // 定义菜单id，不要和其他菜单id重复。编辑器自带的所有菜单id，可通过『参数配置-自定义菜单』一节查看
          var menuId = 'lineheight';
  
          // check将检查菜单配置（『参数配置-自定义菜单』一节描述）中是否该菜单id，如果没有，则忽略下面的代码。
          if (!check(menuId)) {
              return;
          }
  
          // this 指向 editor 对象自身
          var editor = this;
  
          // 由于浏览器自身不支持 lineHeight 命令，因此要做一个hook
          editor.commandHooks.lineHeight = function (value) {
              var rangeElem = editor.getRangeElem();
              var targetElem = editor.getSelfOrParentByName(rangeElem, 'p,h1,h2,h3,h4,h5,pre');
              if (!targetElem) {
                  return;
              }
              $(targetElem).css('line-height', value + '');
          };
  
          // 创建 menu 对象
          var menu = new E.Menu({
              editor: editor,  // 编辑器对象
              id: menuId,  // 菜单id
              title: '行高', // 菜单标题
              commandName: 'lineHeight', // 命令名称
  
              // 正常状态和选中装下的dom对象，样式需要自定义
              $domNormal: $('<a href="#" tabindex="-1"><i class="wangeditor-menu-img-arrows-v"></i></a>'),
              $domSelected: $('<a href="#" tabindex="-1" class="selected"><i class="wangeditor-menu-img-arrows-v"></i></a>')
          });
  
          // 数据源
          var data  = {
              // 格式： 'value' : 'title'
              '1.0': '1.0倍',
              '1.5': '1.5倍',
              '1.8': '1.8倍',
              '2.0': '2.0倍',
              '2.5': '2.5倍',
              '3.0': '3.0倍'
          };
  
          // 为menu创建droplist对象
          var tpl = '<span style="line-height:{#commandValue}">{#title}</span>';
          menu.dropList = new E.DropList(editor, menu, {
              data: data,  // 传入数据源
              tpl: tpl  // 传入模板
          });
  
          // 增加到editor对象中
          editor.menus[menuId] = menu;
  
      });
  
  });
  // 自定义上传
  _e(function (E, $) {
  
      E.plugin(function () {
  
          var editor = this;
          var customUpload = editor.config.customUpload;
          if (!customUpload) {
              return;
          } else if (editor.config.uploadImgUrl) {
              alert('自定义上传无效，详看浏览器日志console.log');
              E.error('已经配置了 uploadImgUrl ，就不能再配置 customUpload ，两者冲突。将导致自定义上传无效。');
              return;
          }
  
          var $uploadContent = editor.$uploadContent;
          if (!$uploadContent) {
              E.error('自定义上传，无法获取 editor.$uploadContent');
          }
  
          // UI
          var $uploadIcon = $('<div class="upload-icon-container"><i class="wangeditor-menu-img-upload"></i></div>');
          $uploadContent.append($uploadIcon);
  
          // 设置id，并暴露
          var btnId = 'upload' + E.random();
          var containerId = 'upload' + E.random();
          $uploadIcon.attr('id', btnId);
          $uploadContent.attr('id', containerId);
  
          editor.customUploadBtnId = btnId;
          editor.customUploadContainerId = containerId;
      });
  
  });
  // 版权提示
  _e(function (E, $) {
      E.info('本页面富文本编辑器由 wangEditor 提供 http://wangeditor.github.io/ ');
  });
      
      // 最终返回wangEditor构造函数
      return window.wangEditor;
  });