| (function (global, factory) { | 
|     typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | 
|         typeof define === 'function' && define.amd ? define(factory) : | 
|             (global.wangEditor = factory()); | 
| }(this, (function () { 'use strict'; | 
|   | 
|     /* | 
|      poly-fill | 
|      */ | 
|   | 
|     var polyfill = function () { | 
|   | 
|         // Object.assign | 
|         if (typeof Object.assign != 'function') { | 
|             Object.assign = function (target, varArgs) { | 
|                 // .length of function is 2 | 
|                 if (target == null) { | 
|                     // TypeError if undefined or null | 
|                     throw new TypeError('Cannot convert undefined or null to object'); | 
|                 } | 
|   | 
|                 var to = Object(target); | 
|   | 
|                 for (var index = 1; index < arguments.length; index++) { | 
|                     var nextSource = arguments[index]; | 
|   | 
|                     if (nextSource != null) { | 
|                         // Skip over if undefined or null | 
|                         for (var nextKey in nextSource) { | 
|                             // Avoid bugs when hasOwnProperty is shadowed | 
|                             if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { | 
|                                 to[nextKey] = nextSource[nextKey]; | 
|                             } | 
|                         } | 
|                     } | 
|                 } | 
|                 return to; | 
|             }; | 
|         } | 
|   | 
|         // IE 中兼容 Element.prototype.matches | 
|         if (!Element.prototype.matches) { | 
|             Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) { | 
|                     var matches = (this.document || this.ownerDocument).querySelectorAll(s), | 
|                         i = matches.length; | 
|                     while (--i >= 0 && matches.item(i) !== this) {} | 
|                     return i > -1; | 
|                 }; | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      DOM 操作 API | 
|      */ | 
|   | 
| // 根据 html 代码片段创建 dom 对象 | 
|     function createElemByHTML(html) { | 
|         var div = void 0; | 
|         div = document.createElement('div'); | 
|         div.innerHTML = html; | 
|         return div.children; | 
|     } | 
|   | 
| // 是否是 DOM List | 
|     function isDOMList(selector) { | 
|         if (!selector) { | 
|             return false; | 
|         } | 
|         if (selector instanceof HTMLCollection || selector instanceof NodeList) { | 
|             return true; | 
|         } | 
|         return false; | 
|     } | 
|   | 
| // 封装 document.querySelectorAll | 
|     function querySelectorAll(selector) { | 
|         var result = document.querySelectorAll(selector); | 
|         if (isDOMList(result)) { | 
|             return result; | 
|         } else { | 
|             return [result]; | 
|         } | 
|     } | 
|   | 
| // 创建构造函数 | 
|     function DomElement(selector) { | 
|         if (!selector) { | 
|             return; | 
|         } | 
|   | 
|         // selector 本来就是 DomElement 对象,直接返回 | 
|         if (selector instanceof DomElement) { | 
|             return selector; | 
|         } | 
|   | 
|         this.selector = selector; | 
|   | 
|         // 根据 selector 得出的结果(如 DOM,DOM List) | 
|         var selectorResult = []; | 
|         if (selector.nodeType === 1) { | 
|             // 单个 DOM 节点 | 
|             selectorResult = [selector]; | 
|         } else if (isDOMList(selector)) { | 
|             // DOM List | 
|             selectorResult = selector; | 
|         } else if (typeof selector === 'string') { | 
|             // 字符串 | 
|             selector = selector.replace('/\n/mg', '').trim(); | 
|             if (selector.indexOf('<') === 0) { | 
|                 // 如 <div> | 
|                 selectorResult = createElemByHTML(selector); | 
|             } else { | 
|                 // 如 #id .class | 
|                 selectorResult = querySelectorAll(selector); | 
|             } | 
|         } | 
|   | 
|         var length = selectorResult.length; | 
|         if (!length) { | 
|             // 空数组 | 
|             return this; | 
|         } | 
|   | 
|         // 加入 DOM 节点 | 
|         var i = void 0; | 
|         for (i = 0; i < length; i++) { | 
|             this[i] = selectorResult[i]; | 
|         } | 
|         this.length = length; | 
|     } | 
|   | 
| // 修改原型 | 
|     DomElement.prototype = { | 
|         constructor: DomElement, | 
|   | 
|         // 类数组,forEach | 
|         forEach: function forEach(fn) { | 
|             var i = void 0; | 
|             for (i = 0; i < this.length; i++) { | 
|                 var elem = this[i]; | 
|                 var result = fn.call(elem, elem, i); | 
|                 if (result === false) { | 
|                     break; | 
|                 } | 
|             } | 
|             return this; | 
|         }, | 
|   | 
|         // 获取第几个元素 | 
|         get: function get(index) { | 
|             var length = this.length; | 
|             if (index >= length) { | 
|                 index = index % length; | 
|             } | 
|             return $(this[index]); | 
|         }, | 
|   | 
|         // 第一个 | 
|         first: function first() { | 
|             return this.get(0); | 
|         }, | 
|   | 
|         // 最后一个 | 
|         last: function last() { | 
|             var length = this.length; | 
|             return this.get(length - 1); | 
|         }, | 
|   | 
|         // 绑定事件 | 
|         on: function on(type, selector, fn) { | 
|             // selector 不为空,证明绑定事件要加代理 | 
|             if (!fn) { | 
|                 fn = selector; | 
|                 selector = null; | 
|             } | 
|   | 
|             // type 是否有多个 | 
|             var types = []; | 
|             types = type.split(/\s+/); | 
|   | 
|             return this.forEach(function (elem) { | 
|                 types.forEach(function (type) { | 
|                     if (!type) { | 
|                         return; | 
|                     } | 
|   | 
|                     if (!selector) { | 
|                         // 无代理 | 
|                         elem.addEventListener(type, fn, false); | 
|                         return; | 
|                     } | 
|   | 
|                     // 有代理 | 
|                     elem.addEventListener(type, function (e) { | 
|                         var target = e.target; | 
|                         if (target.matches(selector)) { | 
|                             fn.call(target, e); | 
|                         } | 
|                     }, false); | 
|                 }); | 
|             }); | 
|         }, | 
|   | 
|         // 取消事件绑定 | 
|         off: function off(type, fn) { | 
|             return this.forEach(function (elem) { | 
|                 elem.removeEventListener(type, fn, false); | 
|             }); | 
|         }, | 
|   | 
|         // 获取/设置 属性 | 
|         attr: function attr(key, val) { | 
|             if (val == null) { | 
|                 // 获取值 | 
|                 return this[0].getAttribute(key); | 
|             } else { | 
|                 // 设置值 | 
|                 return this.forEach(function (elem) { | 
|                     elem.setAttribute(key, val); | 
|                 }); | 
|             } | 
|         }, | 
|   | 
|         // 添加 class | 
|         addClass: function addClass(className) { | 
|             if (!className) { | 
|                 return this; | 
|             } | 
|             return this.forEach(function (elem) { | 
|                 var arr = void 0; | 
|                 if (elem.className) { | 
|                     // 解析当前 className 转换为数组 | 
|                     arr = elem.className.split(/\s/); | 
|                     arr = arr.filter(function (item) { | 
|                         return !!item.trim(); | 
|                     }); | 
|                     // 添加 class | 
|                     if (arr.indexOf(className) < 0) { | 
|                         arr.push(className); | 
|                     } | 
|                     // 修改 elem.class | 
|                     elem.className = arr.join(' '); | 
|                 } else { | 
|                     elem.className = className; | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 删除 class | 
|         removeClass: function removeClass(className) { | 
|             if (!className) { | 
|                 return this; | 
|             } | 
|             return this.forEach(function (elem) { | 
|                 var arr = void 0; | 
|                 if (elem.className) { | 
|                     // 解析当前 className 转换为数组 | 
|                     arr = elem.className.split(/\s/); | 
|                     arr = arr.filter(function (item) { | 
|                         item = item.trim(); | 
|                         // 删除 class | 
|                         if (!item || item === className) { | 
|                             return false; | 
|                         } | 
|                         return true; | 
|                     }); | 
|                     // 修改 elem.class | 
|                     elem.className = arr.join(' '); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 修改 css | 
|         css: function css(key, val) { | 
|             var currentStyle = key + ':' + val + ';'; | 
|             return this.forEach(function (elem) { | 
|                 var style = (elem.getAttribute('style') || '').trim(); | 
|                 var styleArr = void 0, | 
|                     resultArr = []; | 
|                 if (style) { | 
|                     // 将 style 按照 ; 拆分为数组 | 
|                     styleArr = style.split(';'); | 
|                     styleArr.forEach(function (item) { | 
|                         // 对每项样式,按照 : 拆分为 key 和 value | 
|                         var arr = item.split(':').map(function (i) { | 
|                             return i.trim(); | 
|                         }); | 
|                         if (arr.length === 2) { | 
|                             resultArr.push(arr[0] + ':' + arr[1]); | 
|                         } | 
|                     }); | 
|                     // 替换或者新增 | 
|                     resultArr = resultArr.map(function (item) { | 
|                         if (item.indexOf(key) === 0) { | 
|                             return currentStyle; | 
|                         } else { | 
|                             return item; | 
|                         } | 
|                     }); | 
|                     if (resultArr.indexOf(currentStyle) < 0) { | 
|                         resultArr.push(currentStyle); | 
|                     } | 
|                     // 结果 | 
|                     elem.setAttribute('style', resultArr.join('; ')); | 
|                 } else { | 
|                     // style 无值 | 
|                     elem.setAttribute('style', currentStyle); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 显示 | 
|         show: function show() { | 
|             return this.css('display', 'block'); | 
|         }, | 
|   | 
|         // 隐藏 | 
|         hide: function hide() { | 
|             return this.css('display', 'none'); | 
|         }, | 
|   | 
|         // 获取子节点 | 
|         children: function children() { | 
|             var elem = this[0]; | 
|             if (!elem) { | 
|                 return null; | 
|             } | 
|   | 
|             return $(elem.children); | 
|         }, | 
|   | 
|         // 增加子节点 | 
|         append: function append($children) { | 
|             return this.forEach(function (elem) { | 
|                 $children.forEach(function (child) { | 
|                     elem.appendChild(child); | 
|                 }); | 
|             }); | 
|         }, | 
|   | 
|         // 移除当前节点 | 
|         remove: function remove() { | 
|             return this.forEach(function (elem) { | 
|                 if (elem.remove) { | 
|                     elem.remove(); | 
|                 } else { | 
|                     var parent = elem.parentElement; | 
|                     parent && parent.removeChild(elem); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 是否包含某个子节点 | 
|         isContain: function isContain($child) { | 
|             var elem = this[0]; | 
|             var child = $child[0]; | 
|             return elem.contains(child); | 
|         }, | 
|   | 
|         // 尺寸数据 | 
|         getSizeData: function getSizeData() { | 
|             var elem = this[0]; | 
|             return elem.getBoundingClientRect(); // 可得到 bottom height left right top width 的数据 | 
|         }, | 
|   | 
|         // 封装 nodeName | 
|         getNodeName: function getNodeName() { | 
|             var elem = this[0]; | 
|             return elem.nodeName; | 
|         }, | 
|   | 
|         // 从当前元素查找 | 
|         find: function find(selector) { | 
|             var elem = this[0]; | 
|             return $(elem.querySelectorAll(selector)); | 
|         }, | 
|   | 
|         // 获取当前元素的 text | 
|         text: function text(val) { | 
|             if (!val) { | 
|                 // 获取 text | 
|                 var elem = this[0]; | 
|                 return elem.innerHTML.replace(/<.*?>/g, function () { | 
|                     return ''; | 
|                 }); | 
|             } else { | 
|                 // 设置 text | 
|                 return this.forEach(function (elem) { | 
|                     elem.innerHTML = val; | 
|                 }); | 
|             } | 
|         }, | 
|   | 
|         // 获取 html | 
|         html: function html(value) { | 
|             var elem = this[0]; | 
|             if (value == null) { | 
|                 return elem.innerHTML; | 
|             } else { | 
|                 elem.innerHTML = value; | 
|                 return this; | 
|             } | 
|         }, | 
|   | 
|         // 获取 value | 
|         val: function val() { | 
|             var elem = this[0]; | 
|             return elem.value.trim(); | 
|         }, | 
|   | 
|         // focus | 
|         focus: function focus() { | 
|             return this.forEach(function (elem) { | 
|                 elem.focus(); | 
|             }); | 
|         }, | 
|   | 
|         // parent | 
|         parent: function parent() { | 
|             var elem = this[0]; | 
|             return $(elem.parentElement); | 
|         }, | 
|   | 
|         // parentUntil 找到符合 selector 的父节点 | 
|         parentUntil: function parentUntil(selector, _currentElem) { | 
|             var results = document.querySelectorAll(selector); | 
|             var length = results.length; | 
|             if (!length) { | 
|                 // 传入的 selector 无效 | 
|                 return null; | 
|             } | 
|   | 
|             var elem = _currentElem || this[0]; | 
|             if (elem.nodeName === 'BODY') { | 
|                 return null; | 
|             } | 
|   | 
|             var parent = elem.parentElement; | 
|             var i = void 0; | 
|             for (i = 0; i < length; i++) { | 
|                 if (parent === results[i]) { | 
|                     // 找到,并返回 | 
|                     return $(parent); | 
|                 } | 
|             } | 
|   | 
|             // 继续查找 | 
|             return this.parentUntil(selector, parent); | 
|         }, | 
|   | 
|         // 判断两个 elem 是否相等 | 
|         equal: function equal($elem) { | 
|             if ($elem.nodeType === 1) { | 
|                 return this[0] === $elem; | 
|             } else { | 
|                 return this[0] === $elem[0]; | 
|             } | 
|         }, | 
|   | 
|         // 将该元素插入到某个元素前面 | 
|         insertBefore: function insertBefore(selector) { | 
|             var $referenceNode = $(selector); | 
|             var referenceNode = $referenceNode[0]; | 
|             if (!referenceNode) { | 
|                 return this; | 
|             } | 
|             return this.forEach(function (elem) { | 
|                 var parent = referenceNode.parentNode; | 
|                 parent.insertBefore(elem, referenceNode); | 
|             }); | 
|         }, | 
|   | 
|         // 将该元素插入到某个元素后面 | 
|         insertAfter: function insertAfter(selector) { | 
|             var $referenceNode = $(selector); | 
|             var referenceNode = $referenceNode[0]; | 
|             if (!referenceNode) { | 
|                 return this; | 
|             } | 
|             return this.forEach(function (elem) { | 
|                 var parent = referenceNode.parentNode; | 
|                 if (parent.lastChild === referenceNode) { | 
|                     // 最后一个元素 | 
|                     parent.appendChild(elem); | 
|                 } else { | 
|                     // 不是最后一个元素 | 
|                     parent.insertBefore(elem, referenceNode.nextSibling); | 
|                 } | 
|             }); | 
|         } | 
|     }; | 
|   | 
| // new 一个对象 | 
|     function $(selector) { | 
|         return new DomElement(selector); | 
|     } | 
|   | 
|     /* | 
|      配置信息 | 
|      */ | 
|   | 
|     var config = { | 
|   | 
|         // 默认菜单配置 | 
|         menus: ['head', 'bold', 'italic', 'underline', 'strikeThrough', 'foreColor', 'backColor', 'link', 'list', 'justify', 'quote', 'emoticon', 'image', 'table', 'video', 'code', 'undo', 'redo'], | 
|   | 
|         // 编辑区域的 z-index | 
|         zIndex: 10000, | 
|   | 
|         // 是否开启 debug 模式(debug 模式下错误会 throw error 形式抛出) | 
|         debug: false, | 
|   | 
|         // onchange 事件 | 
|         // onchange: function (html) { | 
|         //     // html 即变化之后的内容 | 
|         //     console.log(html) | 
|         // }, | 
|   | 
|         // 是否显示添加网络图片的 tab | 
|         showLinkImg: true, | 
|   | 
|         // 默认上传图片 max size: 5M | 
|         uploadImgMaxSize: 5 * 1024 * 1024, | 
|   | 
|         // 配置一次最多上传几个图片 | 
|         // uploadImgMaxLength: 5, | 
|   | 
|         // 上传图片,是否显示 base64 格式 | 
|         uploadImgShowBase64: false, | 
|   | 
|         // 上传图片,server 地址(如果有值,则 base64 格式的配置则失效) | 
|         // uploadImgServer: '/upload', | 
|   | 
|         // 自定义配置 filename | 
|         uploadFileName: '', | 
|   | 
|         // 上传图片的自定义参数 | 
|         uploadImgParams: { | 
|             token: 'abcdef12345' | 
|         }, | 
|   | 
|         // 上传图片的自定义header | 
|         uploadImgHeaders: { | 
|             // 'Accept': 'text/x-json' | 
|         }, | 
|   | 
|         // 配置 XHR withCredentials | 
|         withCredentials: false, | 
|   | 
|         // 自定义上传图片超时时间 ms | 
|         uploadImgTimeout: 5000, | 
|   | 
|         // 上传图片 hook | 
|         uploadImgHooks: { | 
|             // customInsert: function (insertLinkImg, result, editor) { | 
|             //     console.log('customInsert') | 
|             //     // 图片上传并返回结果,自定义插入图片的事件,而不是编辑器自动插入图片 | 
|             //     const data = result.data1 || [] | 
|             //     data.forEach(link => { | 
|             //         insertLinkImg(link) | 
|             //     }) | 
|             // }, | 
|             before: function before(xhr, editor, files) { | 
|                 // 图片上传之前触发 | 
|             }, | 
|             success: function success(xhr, editor, result) { | 
|                 // 图片上传并返回结果,图片插入成功之后触发 | 
|             }, | 
|             fail: function fail(xhr, editor, result) { | 
|                 // 图片上传并返回结果,但图片插入错误时触发 | 
|             }, | 
|             error: function error(xhr, editor) { | 
|                 // 图片上传出错时触发 | 
|             }, | 
|             timeout: function timeout(xhr, editor) { | 
|                 // 图片上传超时时触发 | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      工具 | 
|      */ | 
|   | 
| // 和 UA 相关的属性 | 
|     var UA = { | 
|         _ua: navigator.userAgent, | 
|   | 
|         // 是否 webkit | 
|         isWebkit: function isWebkit() { | 
|             var reg = /webkit/i; | 
|             return reg.test(this._ua); | 
|         }, | 
|   | 
|         // 是否 IE | 
|         isIE: function isIE() { | 
|             return 'ActiveXObject' in window; | 
|         } | 
|     }; | 
|   | 
| // 遍历对象 | 
|     function objForEach(obj, fn) { | 
|         var key = void 0, | 
|             result = void 0; | 
|         for (key in obj) { | 
|             if (obj.hasOwnProperty(key)) { | 
|                 result = fn.call(obj, key, obj[key]); | 
|                 if (result === false) { | 
|                     break; | 
|                 } | 
|             } | 
|         } | 
|     } | 
|   | 
| // 遍历类数组 | 
|     function arrForEach(fakeArr, fn) { | 
|         var i = void 0, | 
|             item = void 0, | 
|             result = void 0; | 
|         var length = fakeArr.length || 0; | 
|         for (i = 0; i < length; i++) { | 
|             item = fakeArr[i]; | 
|             result = fn.call(fakeArr, item, i); | 
|             if (result === false) { | 
|                 break; | 
|             } | 
|         } | 
|     } | 
|   | 
| // 获取随机数 | 
|     function getRandom(prefix) { | 
|         return prefix + Math.random().toString().slice(2); | 
|     } | 
|   | 
| // 替换 html 特殊字符 | 
|     function replaceHtmlSymbol(html) { | 
|         if (html == null) { | 
|             return ''; | 
|         } | 
|         return html.replace(/</gm, '<').replace(/>/gm, '>').replace(/"/gm, '"'); | 
|     } | 
|   | 
| // 返回百分比的格式 | 
|   | 
|     /* | 
|      bold-menu | 
|      */ | 
| // 构造函数 | 
|     function Bold(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-bold"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Bold.prototype = { | 
|         constructor: Bold, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|             var isSeleEmpty = editor.selection.isSelectionEmpty(); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 选区是空的,插入并选中一个“空白” | 
|                 editor.selection.createEmptyRange(); | 
|             } | 
|   | 
|             // 执行 bold 命令 | 
|             editor.cmd.do('bold'); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 需要将选取折叠起来 | 
|                 editor.selection.collapseRange(); | 
|                 editor.selection.restoreSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor.cmd.queryCommandState('bold')) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      droplist | 
|      */ | 
|     var _emptyFn = function _emptyFn() {}; | 
|   | 
| // 构造函数 | 
|     function DropList(menu, opt) { | 
|         var _this = this; | 
|   | 
|         // droplist 所依附的菜单 | 
|         this.menu = menu; | 
|         this.opt = opt; | 
|         // 容器 | 
|         var $container = $('<div class="w-e-droplist"></div>'); | 
|   | 
|         // 标题 | 
|         var $title = opt.$title; | 
|         if ($title) { | 
|             $title.addClass('w-e-dp-title'); | 
|             $container.append($title); | 
|         } | 
|   | 
|         var list = opt.list || []; | 
|         var type = opt.type || 'list'; // 'list' 列表形式(如“标题”菜单) / 'inline-block' 块状形式(如“颜色”菜单) | 
|         var onClick = opt.onClick || _emptyFn; | 
|   | 
|         // 加入 DOM 并绑定事件 | 
|         var $list = $('<ul class="' + (type === 'list' ? 'w-e-list' : 'w-e-block') + '"></ul>'); | 
|         $container.append($list); | 
|         list.forEach(function (item) { | 
|             var $elem = item.$elem; | 
|             var value = item.value; | 
|             var $li = $('<li class="w-e-item"></li>'); | 
|             if ($elem) { | 
|                 $li.append($elem); | 
|                 $list.append($li); | 
|                 $elem.on('click', function (e) { | 
|                     onClick(value); | 
|   | 
|                     // 隐藏 | 
|                     _this.hideTimeoutId = setTimeout(function () { | 
|                         _this.hide(); | 
|                     }, 0); | 
|                 }); | 
|             } | 
|         }); | 
|   | 
|         // 绑定隐藏事件 | 
|         $container.on('mouseleave', function (e) { | 
|             _this.hideTimeoutId = setTimeout(function () { | 
|                 _this.hide(); | 
|             }, 0); | 
|         }); | 
|   | 
|         // 记录属性 | 
|         this.$container = $container; | 
|   | 
|         // 基本属性 | 
|         this._rendered = false; | 
|         this._show = false; | 
|     } | 
|   | 
| // 原型 | 
|     DropList.prototype = { | 
|         constructor: DropList, | 
|   | 
|         // 显示(插入DOM) | 
|         show: function show() { | 
|             if (this.hideTimeoutId) { | 
|                 // 清除之前的定时隐藏 | 
|                 clearTimeout(this.hideTimeoutId); | 
|             } | 
|   | 
|             var menu = this.menu; | 
|             var $menuELem = menu.$elem; | 
|             var $container = this.$container; | 
|             if (this._show) { | 
|                 return; | 
|             } | 
|             if (this._rendered) { | 
|                 // 显示 | 
|                 $container.show(); | 
|             } else { | 
|                 // 加入 DOM 之前先定位位置 | 
|                 var menuHeight = $menuELem.getSizeData().height || 0; | 
|                 var width = this.opt.width || 100; // 默认为 100 | 
|                 $container.css('margin-top', menuHeight + 'px').css('width', width + 'px'); | 
|   | 
|                 // 加入到 DOM | 
|                 $menuELem.append($container); | 
|                 this._rendered = true; | 
|             } | 
|   | 
|             // 修改属性 | 
|             this._show = true; | 
|         }, | 
|   | 
|         // 隐藏(移除DOM) | 
|         hide: function hide() { | 
|             if (this.showTimeoutId) { | 
|                 // 清除之前的定时显示 | 
|                 clearTimeout(this.showTimeoutId); | 
|             } | 
|   | 
|             var $container = this.$container; | 
|             if (!this._show) { | 
|                 return; | 
|             } | 
|             // 隐藏并需改属性 | 
|             $container.hide(); | 
|             this._show = false; | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - header | 
|      */ | 
| // 构造函数 | 
|     function Head(editor) { | 
|         var _this = this; | 
|   | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-header"><i/></div>'); | 
|         this.type = 'droplist'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|   | 
|         // 初始化 droplist | 
|         this.droplist = new DropList(this, { | 
|             width: 100, | 
|             $title: $('<p>设置标题</p>'), | 
|             type: 'list', // droplist 以列表形式展示 | 
|             list: [{ $elem: $('<h1>H1</h1>'), value: '<h1>' }, { $elem: $('<h2>H2</h2>'), value: '<h2>' }, { $elem: $('<h3>H3</h3>'), value: '<h3>' }, { $elem: $('<h4>H4</h4>'), value: '<h4>' }, { $elem: $('<h5>H5</h5>'), value: '<h5>' }, { $elem: $('<p>正文</p>'), value: '<p>' }], | 
|             onClick: function onClick(value) { | 
|                 // 注意 this 是指向当前的 Head 对象 | 
|                 _this._command(value); | 
|             } | 
|         }); | 
|     } | 
|   | 
| // 原型 | 
|     Head.prototype = { | 
|         constructor: Head, | 
|   | 
|         // 执行命令 | 
|         _command: function _command(value) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('formatBlock', value); | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             var reg = /^h/i; | 
|             var cmdValue = editor.cmd.queryCommandValue('formatBlock'); | 
|             if (reg.test(cmdValue)) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      panel | 
|      */ | 
|   | 
|     var emptyFn = function emptyFn() {}; | 
|   | 
| // 记录已经显示 panel 的菜单 | 
|     var _isCreatedPanelMenus = []; | 
|   | 
| // 构造函数 | 
|     function Panel(menu, opt) { | 
|         this.menu = menu; | 
|         this.opt = opt; | 
|     } | 
|   | 
| // 原型 | 
|     Panel.prototype = { | 
|         constructor: Panel, | 
|   | 
|         // 显示(插入DOM) | 
|         show: function show() { | 
|             var _this = this; | 
|   | 
|             var menu = this.menu; | 
|             if (_isCreatedPanelMenus.indexOf(menu) >= 0) { | 
|                 // 该菜单已经创建了 panel 不能再创建 | 
|                 return; | 
|             } | 
|   | 
|             var editor = menu.editor; | 
|             var $body = $('body'); | 
|             var $textContainerElem = editor.$textContainerElem; | 
|             var opt = this.opt; | 
|   | 
|             // panel 的容器 | 
|             var $container = $('<div class="w-e-panel-container"></div>'); | 
|             var width = opt.width || 300; // 默认 300px | 
|             $container.css('width', width + 'px').css('margin-left', (0 - width) / 2 + 'px'); | 
|   | 
|             // 添加关闭按钮 | 
|             var $closeBtn = $('<i class="w-e-icon-close w-e-panel-close"></i>'); | 
|             $container.append($closeBtn); | 
|             $closeBtn.on('click', function () { | 
|                 _this.hide(); | 
|             }); | 
|   | 
|             // 准备 tabs 容器 | 
|             var $tabTitleContainer = $('<ul class="w-e-panel-tab-title"></ul>'); | 
|             var $tabContentContainer = $('<div class="w-e-panel-tab-content"></div>'); | 
|             $container.append($tabTitleContainer).append($tabContentContainer); | 
|   | 
|             // 设置高度 | 
|             var height = opt.height; | 
|             if (height) { | 
|                 $tabContentContainer.css('height', height + 'px').css('overflow-y', 'auto'); | 
|             } | 
|   | 
|             // tabs | 
|             var tabs = opt.tabs || []; | 
|             var tabTitleArr = []; | 
|             var tabContentArr = []; | 
|             tabs.forEach(function (tab, tabIndex) { | 
|                 if (!tab) { | 
|                     return; | 
|                 } | 
|                 var title = tab.title || ''; | 
|                 var tpl = tab.tpl || ''; | 
|   | 
|                 // 添加到 DOM | 
|                 var $title = $('<li class="w-e-item">' + title + '</li>'); | 
|                 $tabTitleContainer.append($title); | 
|                 var $content = $(tpl); | 
|                 $tabContentContainer.append($content); | 
|   | 
|                 // 记录到内存 | 
|                 $title._index = tabIndex; | 
|                 tabTitleArr.push($title); | 
|                 tabContentArr.push($content); | 
|   | 
|                 // 设置 active 项 | 
|                 if (tabIndex === 0) { | 
|                     $title._active = true; | 
|                     $title.addClass('w-e-active'); | 
|                 } else { | 
|                     $content.hide(); | 
|                 } | 
|   | 
|                 // 绑定 tab 的事件 | 
|                 $title.on('click', function (e) { | 
|                     if ($title._active) { | 
|                         return; | 
|                     } | 
|                     // 隐藏所有的 tab | 
|                     tabTitleArr.forEach(function ($title) { | 
|                         $title._active = false; | 
|                         $title.removeClass('w-e-active'); | 
|                     }); | 
|                     tabContentArr.forEach(function ($content) { | 
|                         $content.hide(); | 
|                     }); | 
|   | 
|                     // 显示当前的 tab | 
|                     $title._active = true; | 
|                     $title.addClass('w-e-active'); | 
|                     $content.show(); | 
|                 }); | 
|             }); | 
|   | 
|             // 绑定关闭事件 | 
|             $container.on('click', function (e) { | 
|                 // 点击时阻止冒泡 | 
|                 e.stopPropagation(); | 
|             }); | 
|             $body.on('click', function (e) { | 
|                 _this.hide(); | 
|             }); | 
|   | 
|             // 添加到 DOM | 
|             $textContainerElem.append($container); | 
|   | 
|             // 绑定 opt 的事件,只有添加到 DOM 之后才能绑定成功 | 
|             tabs.forEach(function (tab, index) { | 
|                 if (!tab) { | 
|                     return; | 
|                 } | 
|                 var events = tab.events || []; | 
|                 events.forEach(function (event) { | 
|                     var selector = event.selector; | 
|                     var type = event.type; | 
|                     var fn = event.fn || emptyFn; | 
|                     var $content = tabContentArr[index]; | 
|                     $content.find(selector).on(type, function (e) { | 
|                         e.stopPropagation(); | 
|                         var needToHide = fn(e); | 
|                         // 执行完事件之后,是否要关闭 panel | 
|                         if (needToHide) { | 
|                             _this.hide(); | 
|                         } | 
|                     }); | 
|                 }); | 
|             }); | 
|   | 
|             // focus 第一个 elem | 
|             var $inputs = $container.find('input[type=text],textarea'); | 
|             if ($inputs.length) { | 
|                 $inputs.get(0).focus(); | 
|             } | 
|   | 
|             // 添加到属性 | 
|             this.$container = $container; | 
|   | 
|             // 隐藏其他 panel | 
|             this._hideOtherPanels(); | 
|             // 记录该 menu 已经创建了 panel | 
|             _isCreatedPanelMenus.push(menu); | 
|         }, | 
|   | 
|         // 隐藏(移除DOM) | 
|         hide: function hide() { | 
|             var menu = this.menu; | 
|             var $container = this.$container; | 
|             if ($container) { | 
|                 $container.remove(); | 
|             } | 
|   | 
|             // 将该 menu 记录中移除 | 
|             _isCreatedPanelMenus = _isCreatedPanelMenus.filter(function (item) { | 
|                 if (item === menu) { | 
|                     return false; | 
|                 } else { | 
|                     return true; | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 一个 panel 展示时,隐藏其他 panel | 
|         _hideOtherPanels: function _hideOtherPanels() { | 
|             if (!_isCreatedPanelMenus.length) { | 
|                 return; | 
|             } | 
|             _isCreatedPanelMenus.forEach(function (menu) { | 
|                 var panel = menu.panel || {}; | 
|                 if (panel.hide) { | 
|                     panel.hide(); | 
|                 } | 
|             }); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - link | 
|      */ | 
| // 构造函数 | 
|     function Link(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-link"><i/></div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Link.prototype = { | 
|         constructor: Link, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             var editor = this.editor; | 
|             var $linkelem = void 0; | 
|   | 
|             if (this._active) { | 
|                 // 当前选区在链接里面 | 
|                 $linkelem = editor.selection.getSelectionContainerElem(); | 
|                 if (!$linkelem) { | 
|                     return; | 
|                 } | 
|                 // 将该元素都包含在选取之内,以便后面整体替换 | 
|                 editor.selection.createRangeByElem($linkelem); | 
|                 editor.selection.restoreSelection(); | 
|                 // 显示 panel | 
|                 this._createPanel($linkelem.text(), $linkelem.attr('href')); | 
|             } else { | 
|                 // 当前选区不在链接里面 | 
|                 if (editor.selection.isSelectionEmpty()) { | 
|                     // 选区是空的,未选中内容 | 
|                     this._createPanel('', ''); | 
|                 } else { | 
|                     // 选中内容了 | 
|                     this._createPanel(editor.selection.getSelectionText(), ''); | 
|                 } | 
|             } | 
|         }, | 
|   | 
|         // 创建 panel | 
|         _createPanel: function _createPanel(text, link) { | 
|             var _this = this; | 
|   | 
|             // panel 中需要用到的id | 
|             var inputLinkId = getRandom('input-link'); | 
|             var inputTextId = getRandom('input-text'); | 
|             var btnOkId = getRandom('btn-ok'); | 
|             var btnDelId = getRandom('btn-del'); | 
|   | 
|             // 是否显示“删除链接” | 
|             var delBtnDisplay = this._active ? 'inline-block' : 'none'; | 
|   | 
|             // 初始化并显示 panel | 
|             var panel = new Panel(this, { | 
|                 width: 300, | 
|                 // panel 中可包含多个 tab | 
|                 tabs: [{ | 
|                     // tab 的标题 | 
|                     title: '链接', | 
|                     // 模板 | 
|                     tpl: '<div>\n                            <input id="' + inputTextId + '" type="text" class="block" value="' + text + '" placeholder="\u94FE\u63A5\u6587\u5B57"/></td>\n                            <input id="' + inputLinkId + '" type="text" class="block" value="' + link + '" placeholder="http://..."/></td>\n                            <div class="w-e-button-container">\n                                <button id="' + btnOkId + '" class="right">\u63D2\u5165</button>\n                                <button id="' + btnDelId + '" class="gray right" style="display:' + delBtnDisplay + '">\u5220\u9664\u94FE\u63A5</button>\n                            </div>\n                        </div>', | 
|                     // 事件绑定 | 
|                     events: [ | 
|                         // 插入链接 | 
|                         { | 
|                             selector: '#' + btnOkId, | 
|                             type: 'click', | 
|                             fn: function fn() { | 
|                                 // 执行插入链接 | 
|                                 var $link = $('#' + inputLinkId); | 
|                                 var $text = $('#' + inputTextId); | 
|                                 var link = $link.val(); | 
|                                 var text = $text.val(); | 
|                                 _this._insertLink(text, link); | 
|   | 
|                                 // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                                 return true; | 
|                             } | 
|                         }, | 
|                         // 删除链接 | 
|                         { | 
|                             selector: '#' + btnDelId, | 
|                             type: 'click', | 
|                             fn: function fn() { | 
|                                 // 执行删除链接 | 
|                                 _this._delLink(); | 
|   | 
|                                 // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                                 return true; | 
|                             } | 
|                         }] | 
|                 } // tab end | 
|                 ] // tabs end | 
|             }); | 
|   | 
|             // 显示 panel | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 删除当前链接 | 
|         _delLink: function _delLink() { | 
|             if (!this._active) { | 
|                 return; | 
|             } | 
|             var editor = this.editor; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             var selectionText = editor.selection.getSelectionText(); | 
|             editor.cmd.do('insertHTML', '<span>' + selectionText + '</span>'); | 
|         }, | 
|   | 
|         // 插入链接 | 
|         _insertLink: function _insertLink(text, link) { | 
|             if (!text || !link) { | 
|                 return; | 
|             } | 
|             var editor = this.editor; | 
|             editor.cmd.do('insertHTML', '<a href="' + link + '" target="_blank">' + text + '</a>'); | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             if ($selectionELem.getNodeName() === 'A') { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      italic-menu | 
|      */ | 
| // 构造函数 | 
|     function Italic(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-italic"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Italic.prototype = { | 
|         constructor: Italic, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|             var isSeleEmpty = editor.selection.isSelectionEmpty(); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 选区是空的,插入并选中一个“空白” | 
|                 editor.selection.createEmptyRange(); | 
|             } | 
|   | 
|             // 执行 italic 命令 | 
|             editor.cmd.do('italic'); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 需要将选取折叠起来 | 
|                 editor.selection.collapseRange(); | 
|                 editor.selection.restoreSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor.cmd.queryCommandState('italic')) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      redo-menu | 
|      */ | 
| // 构造函数 | 
|     function Redo(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-redo"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Redo.prototype = { | 
|         constructor: Redo, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|   | 
|             // 执行 redo 命令 | 
|             editor.cmd.do('redo'); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      strikeThrough-menu | 
|      */ | 
| // 构造函数 | 
|     function StrikeThrough(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-strikethrough"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     StrikeThrough.prototype = { | 
|         constructor: StrikeThrough, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|             var isSeleEmpty = editor.selection.isSelectionEmpty(); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 选区是空的,插入并选中一个“空白” | 
|                 editor.selection.createEmptyRange(); | 
|             } | 
|   | 
|             // 执行 strikeThrough 命令 | 
|             editor.cmd.do('strikeThrough'); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 需要将选取折叠起来 | 
|                 editor.selection.collapseRange(); | 
|                 editor.selection.restoreSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor.cmd.queryCommandState('strikeThrough')) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      underline-menu | 
|      */ | 
| // 构造函数 | 
|     function Underline(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-underline"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Underline.prototype = { | 
|         constructor: Underline, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|             var isSeleEmpty = editor.selection.isSelectionEmpty(); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 选区是空的,插入并选中一个“空白” | 
|                 editor.selection.createEmptyRange(); | 
|             } | 
|   | 
|             // 执行 underline 命令 | 
|             editor.cmd.do('underline'); | 
|   | 
|             if (isSeleEmpty) { | 
|                 // 需要将选取折叠起来 | 
|                 editor.selection.collapseRange(); | 
|                 editor.selection.restoreSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor.cmd.queryCommandState('underline')) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      undo-menu | 
|      */ | 
| // 构造函数 | 
|     function Undo(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-undo"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Undo.prototype = { | 
|         constructor: Undo, | 
|   | 
|         // 点击事件 | 
|         onClick: function onClick(e) { | 
|             // 点击菜单将触发这里 | 
|   | 
|             var editor = this.editor; | 
|   | 
|             // 执行 undo 命令 | 
|             editor.cmd.do('undo'); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - list | 
|      */ | 
| // 构造函数 | 
|     function List(editor) { | 
|         var _this = this; | 
|   | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-list2"><i/></div>'); | 
|         this.type = 'droplist'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|   | 
|         // 初始化 droplist | 
|         this.droplist = new DropList(this, { | 
|             width: 120, | 
|             $title: $('<p>设置列表</p>'), | 
|             type: 'list', // droplist 以列表形式展示 | 
|             list: [{ $elem: $('<span><i class="w-e-icon-list-numbered"></i> 有序列表</span>'), value: 'insertOrderedList' }, { $elem: $('<span><i class="w-e-icon-list2"></i> 无序列表</span>'), value: 'insertUnorderedList' }], | 
|             onClick: function onClick(value) { | 
|                 // 注意 this 是指向当前的 List 对象 | 
|                 _this._command(value); | 
|             } | 
|         }); | 
|     } | 
|   | 
| // 原型 | 
|     List.prototype = { | 
|         constructor: List, | 
|   | 
|         // 执行命令 | 
|         _command: function _command(value) { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             editor.selection.restoreSelection(); | 
|             if (editor.cmd.queryCommandState(value)) { | 
|                 return; | 
|             } | 
|             editor.cmd.do(value); | 
|   | 
|             // 验证列表是否被包裹在 <p> 之内 | 
|             var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|             if ($selectionElem.getNodeName() === 'LI') { | 
|                 $selectionElem = $selectionElem.parent(); | 
|             } | 
|             if (/^ol|ul$/i.test($selectionElem.getNodeName()) === false) { | 
|                 return; | 
|             } | 
|             if ($selectionElem.equal($textElem)) { | 
|                 // 证明是顶级标签,没有被 <p> 包裹 | 
|                 return; | 
|             } | 
|             var $parent = $selectionElem.parent(); | 
|             if ($parent.equal($textElem)) { | 
|                 // $parent 是顶级标签,不能删除 | 
|                 return; | 
|             } | 
|   | 
|             $selectionElem.insertAfter($parent); | 
|             $parent.remove(); | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor.cmd.queryCommandState('insertUnOrderedList') || editor.cmd.queryCommandState('insertOrderedList')) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - justify | 
|      */ | 
| // 构造函数 | 
|     function Justify(editor) { | 
|         var _this = this; | 
|   | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-paragraph-left"><i/></div>'); | 
|         this.type = 'droplist'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|   | 
|         // 初始化 droplist | 
|         this.droplist = new DropList(this, { | 
|             width: 100, | 
|             $title: $('<p>对齐方式</p>'), | 
|             type: 'list', // droplist 以列表形式展示 | 
|             list: [{ $elem: $('<span><i class="w-e-icon-paragraph-left"></i> 靠左</span>'), value: 'justifyLeft' }, { $elem: $('<span><i class="w-e-icon-paragraph-center"></i> 居中</span>'), value: 'justifyCenter' }, { $elem: $('<span><i class="w-e-icon-paragraph-right"></i> 靠右</span>'), value: 'justifyRight' }], | 
|             onClick: function onClick(value) { | 
|                 // 注意 this 是指向当前的 List 对象 | 
|                 _this._command(value); | 
|             } | 
|         }); | 
|     } | 
|   | 
| // 原型 | 
|     Justify.prototype = { | 
|         constructor: Justify, | 
|   | 
|         // 执行命令 | 
|         _command: function _command(value) { | 
|             var editor = this.editor; | 
|             editor.cmd.do(value); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - backcolor | 
|      */ | 
| // 构造函数 | 
|     function BackColor(editor) { | 
|         var _this = this; | 
|   | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-pencil2"><i/></div>'); | 
|         this.type = 'droplist'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|   | 
|         // 初始化 droplist | 
|         this.droplist = new DropList(this, { | 
|             width: 120, | 
|             $title: $('<p>文字颜色</p>'), | 
|             type: 'inline-block', // droplist 内容以 block 形式展示 | 
|             list: [{ $elem: $('<i style="color:#000000;" class="w-e-icon-pencil2"></i>'), value: '#000000' }, { $elem: $('<i style="color:#eeece0;" class="w-e-icon-pencil2"></i>'), value: '#eeece0' }, { $elem: $('<i style="color:#1c487f;" class="w-e-icon-pencil2"></i>'), value: '#1c487f' }, { $elem: $('<i style="color:#4d80bf;" class="w-e-icon-pencil2"></i>'), value: '#4d80bf' }, { $elem: $('<i style="color:#c24f4a;" class="w-e-icon-pencil2"></i>'), value: '#c24f4a' }, { $elem: $('<i style="color:#8baa4a;" class="w-e-icon-pencil2"></i>'), value: '#8baa4a' }, { $elem: $('<i style="color:#7b5ba1;" class="w-e-icon-pencil2"></i>'), value: '#7b5ba1' }, { $elem: $('<i style="color:#46acc8;" class="w-e-icon-pencil2"></i>'), value: '#46acc8' }, { $elem: $('<i style="color:#f9963b;" class="w-e-icon-pencil2"></i>'), value: '#f9963b' }, { $elem: $('<i style="color:#ffffff;" class="w-e-icon-pencil2"></i>'), value: '#ffffff' }], | 
|             onClick: function onClick(value) { | 
|                 // 注意 this 是指向当前的 BackColor 对象 | 
|                 _this._command(value); | 
|             } | 
|         }); | 
|     } | 
|   | 
| // 原型 | 
|     BackColor.prototype = { | 
|         constructor: BackColor, | 
|   | 
|         // 执行命令 | 
|         _command: function _command(value) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('foreColor', value); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - forecolor | 
|      */ | 
| // 构造函数 | 
|     function ForeColor$1(editor) { | 
|         var _this = this; | 
|   | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-paint-brush"><i/></div>'); | 
|         this.type = 'droplist'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|   | 
|         // 初始化 droplist | 
|         this.droplist = new DropList(this, { | 
|             width: 120, | 
|             $title: $('<p>背景色</p>'), | 
|             type: 'inline-block', // droplist 内容以 block 形式展示 | 
|             list: [{ $elem: $('<i style="color:#000000;" class="w-e-icon-paint-brush"></i>'), value: '#000000' }, { $elem: $('<i style="color:#eeece0;" class="w-e-icon-paint-brush"></i>'), value: '#eeece0' }, { $elem: $('<i style="color:#1c487f;" class="w-e-icon-paint-brush"></i>'), value: '#1c487f' }, { $elem: $('<i style="color:#4d80bf;" class="w-e-icon-paint-brush"></i>'), value: '#4d80bf' }, { $elem: $('<i style="color:#c24f4a;" class="w-e-icon-paint-brush"></i>'), value: '#c24f4a' }, { $elem: $('<i style="color:#8baa4a;" class="w-e-icon-paint-brush"></i>'), value: '#8baa4a' }, { $elem: $('<i style="color:#7b5ba1;" class="w-e-icon-paint-brush"></i>'), value: '#7b5ba1' }, { $elem: $('<i style="color:#46acc8;" class="w-e-icon-paint-brush"></i>'), value: '#46acc8' }, { $elem: $('<i style="color:#f9963b;" class="w-e-icon-paint-brush"></i>'), value: '#f9963b' }, { $elem: $('<i style="color:#ffffff;" class="w-e-icon-paint-brush"></i>'), value: '#ffffff' }], | 
|             onClick: function onClick(value) { | 
|                 // 注意 this 是指向当前的 ForeColor 对象 | 
|                 _this._command(value); | 
|             } | 
|         }); | 
|     } | 
|   | 
| // 原型 | 
|     ForeColor$1.prototype = { | 
|         constructor: ForeColor$1, | 
|   | 
|         // 执行命令 | 
|         _command: function _command(value) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('backColor', value); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - quote | 
|      */ | 
| // 构造函数 | 
|     function Quote(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-quotes-left"><i/>\n        </div>'); | 
|         this.type = 'click'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Quote.prototype = { | 
|         constructor: Quote, | 
|   | 
|         onClick: function onClick(e) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('formatBlock', '<BLOCKQUOTE>'); | 
|         }, | 
|   | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             var reg = /^BLOCKQUOTE$/i; | 
|             var cmdValue = editor.cmd.queryCommandValue('formatBlock'); | 
|             if (reg.test(cmdValue)) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - code | 
|      */ | 
| // 构造函数 | 
|     function Code(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-terminal"><i/>\n        </div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Code.prototype = { | 
|         constructor: Code, | 
|   | 
|         onClick: function onClick(e) { | 
|             var editor = this.editor; | 
|             var $startElem = editor.selection.getSelectionStartElem(); | 
|             var $endElem = editor.selection.getSelectionEndElem(); | 
|             var isSeleEmpty = editor.selection.isSelectionEmpty(); | 
|             var selectionText = editor.selection.getSelectionText(); | 
|             var $code = void 0; | 
|   | 
|             if (!$startElem.equal($endElem)) { | 
|                 // 跨元素选择,不做处理 | 
|                 editor.selection.restoreSelection(); | 
|                 return; | 
|             } | 
|             if (!isSeleEmpty) { | 
|                 // 选取不是空,用 <code> 包裹即可 | 
|                 $code = $('<code>' + selectionText + '</code>'); | 
|                 editor.cmd.do('insertElem', $code); | 
|                 editor.selection.createRangeByElem($code, false); | 
|                 editor.selection.restoreSelection(); | 
|                 return; | 
|             } | 
|   | 
|             // 选取是空,且没有夸元素选择,则插入 <pre><code></code></prev> | 
|             if (this._active) { | 
|                 // 选中状态,将编辑内容 | 
|                 this._createPanel($startElem.html()); | 
|             } else { | 
|                 // 未选中状态,将创建内容 | 
|                 this._createPanel(); | 
|             } | 
|         }, | 
|   | 
|         _createPanel: function _createPanel(value) { | 
|             var _this = this; | 
|   | 
|             // value - 要编辑的内容 | 
|             value = value || ''; | 
|             var type = !value ? 'new' : 'edit'; | 
|             var textId = getRandom('texxt'); | 
|             var btnId = getRandom('btn'); | 
|   | 
|             var panel = new Panel(this, { | 
|                 width: 500, | 
|                 // 一个 Panel 包含多个 tab | 
|                 tabs: [{ | 
|                     // 标题 | 
|                     title: '插入代码', | 
|                     // 模板 | 
|                     tpl: '<div>\n                        <textarea id="' + textId + '" style="height:145px;;">' + value + '</textarea>\n                        <div class="w-e-button-container">\n                            <button id="' + btnId + '" class="right">\u63D2\u5165</button>\n                        </div>\n                    <div>', | 
|                     // 事件绑定 | 
|                     events: [ | 
|                         // 插入代码 | 
|                         { | 
|                             selector: '#' + btnId, | 
|                             type: 'click', | 
|                             fn: function fn() { | 
|                                 var $text = $('#' + textId); | 
|                                 var text = $text.val() || $text.html(); | 
|                                 text = replaceHtmlSymbol(text); | 
|                                 if (type === 'new') { | 
|                                     // 新插入 | 
|                                     _this._insertCode(text); | 
|                                 } else { | 
|                                     // 编辑更新 | 
|                                     _this._updateCode(text); | 
|                                 } | 
|   | 
|                                 // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                                 return true; | 
|                             } | 
|                         }] | 
|                 } // first tab end | 
|                 ] // tabs end | 
|             }); // new Panel end | 
|   | 
|             // 显示 panel | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 插入代码 | 
|         _insertCode: function _insertCode(value) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('insertHTML', '<pre><code>' + value + '</code></pre><p><br></p>'); | 
|         }, | 
|   | 
|         // 更新代码 | 
|         _updateCode: function _updateCode(value) { | 
|             var editor = this.editor; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             $selectionELem.html(value); | 
|             editor.selection.restoreSelection(); | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             var $parentElem = $selectionELem.parent(); | 
|             if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - emoticon | 
|      */ | 
| // 构造函数 | 
|     function Emoticon(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu">\n            <i class="w-e-icon-happy"><i/>\n        </div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Emoticon.prototype = { | 
|         constructor: Emoticon, | 
|   | 
|         onClick: function onClick() { | 
|             this._createPanel(); | 
|         }, | 
|   | 
|         _createPanel: function _createPanel() { | 
|             var _this = this; | 
|   | 
|             // 拼接表情字符串 | 
|             var faceHtml = ''; | 
|             var faceStr = '😀 😃 😄 😁 😆 😅 😂  😊 😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 😜 😝 😛 🤑 🤗 🤓 😎 😏 😒 😞 😔 😟 😕 🙁  😣 😖 😫 😩 😤 😠 😡 😶 😐 😑 😯 😦 😧 😮 😲 😵 😳 😱 😨 😰 😢 😥 😭 😓 😪 😴 🙄 🤔 😬 🤐'; | 
|             faceStr.split(/\s/).forEach(function (item) { | 
|                 if (item) { | 
|                     faceHtml += '<span class="w-e-item">' + item + '</span>'; | 
|                 } | 
|             }); | 
|   | 
|             var handHtml = ''; | 
|             var handStr = '🙌 👏 👋 👍 👎 👊 ✊ ️👌 ✋ 👐 💪 🙏 ️👆 👇 👈 👉 🖕 🖐 🤘 🖖'; | 
|             handStr.split(/\s/).forEach(function (item) { | 
|                 if (item) { | 
|                     handHtml += '<span class="w-e-item">' + item + '</span>'; | 
|                 } | 
|             }); | 
|   | 
|             var panel = new Panel(this, { | 
|                 width: 300, | 
|                 height: 200, | 
|                 // 一个 Panel 包含多个 tab | 
|                 tabs: [{ | 
|                     // 标题 | 
|                     title: '表情', | 
|                     // 模板 | 
|                     tpl: '<div class="w-e-emoticon-container">' + faceHtml + '</div>', | 
|                     // 事件绑定 | 
|                     events: [{ | 
|                         selector: 'span.w-e-item', | 
|                         type: 'click', | 
|                         fn: function fn(e) { | 
|                             var target = e.target; | 
|                             _this._insert(target.innerHTML); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }] | 
|                 }, // first tab end | 
|                     { | 
|                         // 标题 | 
|                         title: '手势', | 
|                         // 模板 | 
|                         tpl: '<div class="w-e-emoticon-container">' + handHtml + '</div>', | 
|                         // 事件绑定 | 
|                         events: [{ | 
|                             selector: 'span.w-e-item', | 
|                             type: 'click', | 
|                             fn: function fn(e) { | 
|                                 var target = e.target; | 
|                                 _this._insert(target.innerHTML); | 
|                                 // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                                 return true; | 
|                             } | 
|                         }] | 
|                     } // second tab end | 
|                 ] // tabs end | 
|             }); | 
|   | 
|             // 显示 panel | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 插入表情 | 
|         _insert: function _insert(emoji) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('insertHTML', '<span>' + emoji + '</span>'); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - table | 
|      */ | 
| // 构造函数 | 
|     function Table(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-table2"><i/></div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Table.prototype = { | 
|         constructor: Table, | 
|   | 
|         onClick: function onClick() { | 
|             if (this._active) { | 
|                 // 编辑现有表格 | 
|                 this._createEditPanel(); | 
|             } else { | 
|                 // 插入新表格 | 
|                 this._createInsertPanel(); | 
|             } | 
|         }, | 
|   | 
|         // 创建插入新表格的 panel | 
|         _createInsertPanel: function _createInsertPanel() { | 
|             var _this = this; | 
|   | 
|             // 用到的 id | 
|             var btnInsertId = getRandom('btn'); | 
|             var textRowNum = getRandom('row'); | 
|             var textColNum = getRandom('col'); | 
|   | 
|             var panel = new Panel(this, { | 
|                 width: 250, | 
|                 // panel 包含多个 tab | 
|                 tabs: [{ | 
|                     // 标题 | 
|                     title: '插入表格', | 
|                     // 模板 | 
|                     tpl: '<div>\n                        <p style="text-align:left; padding:5px 0;">\n                            \u521B\u5EFA\n                            <input id="' + textRowNum + '" type="text" value="5" style="width:40px;text-align:center;"/>\n                            \u884C\n                            <input id="' + textColNum + '" type="text" value="5" style="width:40px;text-align:center;"/>\n                            \u5217\u7684\u8868\u683C\n                        </p>\n                        <div class="w-e-button-container">\n                            <button id="' + btnInsertId + '" class="right">\u63D2\u5165</button>\n                        </div>\n                    </div>', | 
|                     // 事件绑定 | 
|                     events: [{ | 
|                         // 点击按钮,插入表格 | 
|                         selector: '#' + btnInsertId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             var rowNum = parseInt($('#' + textRowNum).val()); | 
|                             var colNum = parseInt($('#' + textColNum).val()); | 
|   | 
|                             if (rowNum && colNum && rowNum > 0 && colNum > 0) { | 
|                                 // form 数据有效 | 
|                                 _this._insert(rowNum, colNum); | 
|                             } | 
|   | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }] | 
|                 } // first tab end | 
|                 ] // tabs end | 
|             }); // panel end | 
|   | 
|             // 展示 panel | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 插入表格 | 
|         _insert: function _insert(rowNum, colNum) { | 
|             // 拼接 table 模板 | 
|             var r = void 0, | 
|                 c = void 0; | 
|             var html = '<table border="0" width="100%" cellpadding="0" cellspacing="0">'; | 
|             for (r = 0; r < rowNum; r++) { | 
|                 html += '<tr>'; | 
|                 if (r === 0) { | 
|                     for (c = 0; c < colNum; c++) { | 
|                         html += '<th> </th>'; | 
|                     } | 
|                 } else { | 
|                     for (c = 0; c < colNum; c++) { | 
|                         html += '<td> </td>'; | 
|                     } | 
|                 } | 
|                 html += '</tr>'; | 
|             } | 
|             html += '</table><p><br></p>'; | 
|   | 
|             // 执行命令 | 
|             var editor = this.editor; | 
|             editor.cmd.do('insertHTML', html); | 
|   | 
|             // 防止 firefox 下出现 resize 的控制点 | 
|             editor.cmd.do('enableObjectResizing', false); | 
|             editor.cmd.do('enableInlineTableEditing', false); | 
|         }, | 
|   | 
|         // 创建编辑表格的 panel | 
|         _createEditPanel: function _createEditPanel() { | 
|             var _this2 = this; | 
|   | 
|             // 可用的 id | 
|             var addRowBtnId = getRandom('add-row'); | 
|             var addColBtnId = getRandom('add-col'); | 
|             var delRowBtnId = getRandom('del-row'); | 
|             var delColBtnId = getRandom('del-col'); | 
|             var delTableBtnId = getRandom('del-table'); | 
|   | 
|             // 创建 panel 对象 | 
|             var panel = new Panel(this, { | 
|                 width: 320, | 
|                 // panel 包含多个 tab | 
|                 tabs: [{ | 
|                     // 标题 | 
|                     title: language==1?'编辑':(language==2?'Edit':'Edit'), | 
|                     // 模板 | 
|                     tpl: '<div>\n                        <div class="w-e-button-container" style="border-bottom:1px solid #f1f1f1;padding-bottom:5px;margin-bottom:5px;">\n                            <button id="' + addRowBtnId + '" class="left">\u589E\u52A0\u884C</button>\n                            <button id="' + delRowBtnId + '" class="red left">\u5220\u9664\u884C</button>\n                            <button id="' + addColBtnId + '" class="left">\u589E\u52A0\u5217</button>\n                            <button id="' + delColBtnId + '" class="red left">\u5220\u9664\u5217</button>\n                        </div>\n                        <div class="w-e-button-container">\n                            <button id="' + delTableBtnId + '" class="gray left">\u5220\u9664\u8868\u683C</button>\n                        </dv>\n                    </div>', | 
|                     // 事件绑定 | 
|                     events: [{ | 
|                         // 增加行 | 
|                         selector: '#' + addRowBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             _this2._addRow(); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }, { | 
|                         // 增加列 | 
|                         selector: '#' + addColBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             _this2._addCol(); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }, { | 
|                         // 删除行 | 
|                         selector: '#' + delRowBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             _this2._delRow(); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }, { | 
|                         // 删除列 | 
|                         selector: '#' + delColBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             _this2._delCol(); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }, { | 
|                         // 删除表格 | 
|                         selector: '#' + delTableBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             _this2._delTable(); | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }] | 
|                 }] | 
|             }); | 
|             // 显示 panel | 
|             panel.show(); | 
|         }, | 
|   | 
|         // 获取选中的单元格的位置信息 | 
|         _getLocationData: function _getLocationData() { | 
|             var result = {}; | 
|             var editor = this.editor; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             var nodeName = $selectionELem.getNodeName(); | 
|             if (nodeName !== 'TD' && nodeName !== 'TH') { | 
|                 return; | 
|             } | 
|   | 
|             // 获取 td index | 
|             var $tr = $selectionELem.parent(); | 
|             var $tds = $tr.children(); | 
|             var tdLength = $tds.length; | 
|             $tds.forEach(function (td, index) { | 
|                 if (td === $selectionELem[0]) { | 
|                     // 记录并跳出循环 | 
|                     result.td = { | 
|                         index: index, | 
|                         elem: td, | 
|                         length: tdLength | 
|                     }; | 
|                     return false; | 
|                 } | 
|             }); | 
|   | 
|             // 获取 tr index | 
|             var $tbody = $tr.parent(); | 
|             var $trs = $tbody.children(); | 
|             var trLength = $trs.length; | 
|             $trs.forEach(function (tr, index) { | 
|                 if (tr === $tr[0]) { | 
|                     // 记录并跳出循环 | 
|                     result.tr = { | 
|                         index: index, | 
|                         elem: tr, | 
|                         length: trLength | 
|                     }; | 
|                     return false; | 
|                 } | 
|             }); | 
|   | 
|             // 返回结果 | 
|             return result; | 
|         }, | 
|   | 
|         // 增加行 | 
|         _addRow: function _addRow() { | 
|             // 获取当前单元格的位置信息 | 
|             var locationData = this._getLocationData(); | 
|             if (!locationData) { | 
|                 return; | 
|             } | 
|             var trData = locationData.tr; | 
|             var $currentTr = $(trData.elem); | 
|             var tdData = locationData.td; | 
|             var tdLength = tdData.length; | 
|   | 
|             // 拼接即将插入的字符串 | 
|             var newTr = document.createElement('tr'); | 
|             var tpl = '', | 
|                 i = void 0; | 
|             for (i = 0; i < tdLength; i++) { | 
|                 tpl += '<td> </td>'; | 
|             } | 
|             newTr.innerHTML = tpl; | 
|             // 插入 | 
|             $(newTr).insertAfter($currentTr); | 
|         }, | 
|   | 
|         // 增加列 | 
|         _addCol: function _addCol() { | 
|             // 获取当前单元格的位置信息 | 
|             var locationData = this._getLocationData(); | 
|             if (!locationData) { | 
|                 return; | 
|             } | 
|             var trData = locationData.tr; | 
|             var tdData = locationData.td; | 
|             var tdIndex = tdData.index; | 
|             var $currentTr = $(trData.elem); | 
|             var $trParent = $currentTr.parent(); | 
|             var $trs = $trParent.children(); | 
|   | 
|             // 遍历所有行 | 
|             $trs.forEach(function (tr) { | 
|                 var $tr = $(tr); | 
|                 var $tds = $tr.children(); | 
|                 var $currentTd = $tds.get(tdIndex); | 
|                 var name = $currentTd.getNodeName().toLowerCase(); | 
|   | 
|                 // new 一个 td,并插入 | 
|                 var newTd = document.createElement(name); | 
|                 $(newTd).insertAfter($currentTd); | 
|             }); | 
|         }, | 
|   | 
|         // 删除行 | 
|         _delRow: function _delRow() { | 
|             // 获取当前单元格的位置信息 | 
|             var locationData = this._getLocationData(); | 
|             if (!locationData) { | 
|                 return; | 
|             } | 
|             var trData = locationData.tr; | 
|             var $currentTr = $(trData.elem); | 
|             $currentTr.remove(); | 
|         }, | 
|   | 
|         // 删除列 | 
|         _delCol: function _delCol() { | 
|             // 获取当前单元格的位置信息 | 
|             var locationData = this._getLocationData(); | 
|             if (!locationData) { | 
|                 return; | 
|             } | 
|             var trData = locationData.tr; | 
|             var tdData = locationData.td; | 
|             var tdIndex = tdData.index; | 
|             var $currentTr = $(trData.elem); | 
|             var $trParent = $currentTr.parent(); | 
|             var $trs = $trParent.children(); | 
|   | 
|             // 遍历所有行 | 
|             $trs.forEach(function (tr) { | 
|                 var $tr = $(tr); | 
|                 var $tds = $tr.children(); | 
|                 var $currentTd = $tds.get(tdIndex); | 
|                 // 删除 | 
|                 $currentTd.remove(); | 
|             }); | 
|         }, | 
|   | 
|         // 删除表格 | 
|         _delTable: function _delTable() { | 
|             var editor = this.editor; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             var $table = $selectionELem.parentUntil('table'); | 
|             if (!$table) { | 
|                 return; | 
|             } | 
|             $table.remove(); | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             var $selectionELem = editor.selection.getSelectionContainerElem(); | 
|             if (!$selectionELem) { | 
|                 return; | 
|             } | 
|             var nodeName = $selectionELem.getNodeName(); | 
|             if (nodeName === 'TD' || nodeName === 'TH') { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - video | 
|      */ | 
| // 构造函数 | 
|     function Video(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-play"><i/></div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Video.prototype = { | 
|         constructor: Video, | 
|   | 
|         onClick: function onClick() { | 
|             this._createPanel(); | 
|         }, | 
|   | 
|         _createPanel: function _createPanel() { | 
|             var _this = this; | 
|   | 
|             // 创建 id | 
|             var textValId = getRandom('text-val'); | 
|             var btnId = getRandom('btn'); | 
|   | 
|             // 创建 panel | 
|             var panel = new Panel(this, { | 
|                 width: 350, | 
|                 // 一个 panel 多个 tab | 
|                 tabs: [{ | 
|                     // 标题 | 
|                     title: '插入视频', | 
|                     // 模板 | 
|                     tpl: '<div>\n                        <input id="' + textValId + '" type="text" class="block" placeholder="\u683C\u5F0F\u5982\uFF1A<iframe src=... ></iframe>"/>\n                        <div class="w-e-button-container">\n                            <button id="' + btnId + '" class="right">\u63D2\u5165</button>\n                        </div>\n                    </div>', | 
|                     // 事件绑定 | 
|                     events: [{ | 
|                         selector: '#' + btnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             var $text = $('#' + textValId); | 
|                             var val = $text.val().trim(); | 
|   | 
|                             // 测试用视频地址 | 
|                             // <iframe height=498 width=510 src='http://player.youku.com/embed/XMjcwMzc3MzM3Mg==' frameborder=0 'allowfullscreen'></iframe> | 
|   | 
|                             if (val) { | 
|                                 // 插入视频 | 
|                                 _this._insert(val); | 
|                             } | 
|   | 
|                             // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                             return true; | 
|                         } | 
|                     }] | 
|                 } // first tab end | 
|                 ] // tabs end | 
|             }); // panel end | 
|   | 
|             // 显示 panel | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 插入视频 | 
|         _insert: function _insert(val) { | 
|             var editor = this.editor; | 
|             editor.cmd.do('insertHTML', val + '<p><br></p>'); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      menu - img | 
|      */ | 
| // 构造函数 | 
|     function Image(editor) { | 
|         this.editor = editor; | 
|         this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-image"><i/></div>'); | 
|         this.type = 'panel'; | 
|   | 
|         // 当前是否 active 状态 | 
|         this._active = false; | 
|     } | 
|   | 
| // 原型 | 
|     Image.prototype = { | 
|         constructor: Image, | 
|   | 
|         onClick: function onClick() { | 
|             if (this._active) { | 
|                 this._createEditPanel(); | 
|             } else { | 
|                 this._createInsertPanel(); | 
|             } | 
|         }, | 
|   | 
|         _createEditPanel: function _createEditPanel() { | 
|             var editor = this.editor; | 
|   | 
|             // id | 
|             var width30 = getRandom('width-30'); | 
|             var width50 = getRandom('width-50'); | 
|             var width100 = getRandom('width-100'); | 
|             var delBtn = getRandom('del-btn'); | 
|   | 
|             // tab 配置 | 
|             var tabsConfig = [{ | 
|                 title: language==1?'编辑':(language==2?'Edit':'Edit'), | 
|                 tpl: '<div>\n                    <div class="w-e-button-container" style="border-bottom:1px solid #f1f1f1;padding-bottom:5px;margin-bottom:5px;">\n                        <span style="float:left;font-size:14px;margin:4px 5px 0 5px;color:#333;">\u6700\u5927\u5BBD\u5EA6\uFF1A</span>\n                        <button id="' + width30 + '" class="left">30%</button>\n                        <button id="' + width50 + '" class="left">50%</button>\n                        <button id="' + width100 + '" class="left">100%</button>\n                    </div>\n                    <div class="w-e-button-container">\n                        <button id="' + delBtn + '" class="gray left">\u5220\u9664\u56FE\u7247</button>\n                    </dv>\n                </div>', | 
|                 events: [{ | 
|                     selector: '#' + width30, | 
|                     type: 'click', | 
|                     fn: function fn() { | 
|                         var $img = editor._selectedImg; | 
|                         if ($img) { | 
|                             $img.css('max-width', '30%'); | 
|                         } | 
|                         // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                         return true; | 
|                     } | 
|                 }, { | 
|                     selector: '#' + width50, | 
|                     type: 'click', | 
|                     fn: function fn() { | 
|                         var $img = editor._selectedImg; | 
|                         if ($img) { | 
|                             $img.css('max-width', '50%'); | 
|                         } | 
|                         // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                         return true; | 
|                     } | 
|                 }, { | 
|                     selector: '#' + width100, | 
|                     type: 'click', | 
|                     fn: function fn() { | 
|                         var $img = editor._selectedImg; | 
|                         if ($img) { | 
|                             $img.css('max-width', '100%'); | 
|                         } | 
|                         // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                         return true; | 
|                     } | 
|                 }, { | 
|                     selector: '#' + delBtn, | 
|                     type: 'click', | 
|                     fn: function fn() { | 
|                         var $img = editor._selectedImg; | 
|                         if ($img) { | 
|                             $img.remove(); | 
|                         } | 
|                         // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭 | 
|                         return true; | 
|                     } | 
|                 }] | 
|             }]; | 
|   | 
|             // 创建 panel 并显示 | 
|             var panel = new Panel(this, { | 
|                 width: 300, | 
|                 tabs: tabsConfig | 
|             }); | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         _createInsertPanel: function _createInsertPanel() { | 
|             var editor = this.editor; | 
|             var uploadImg = editor.uploadImg; | 
|             var config = editor.config; | 
|   | 
|             // id | 
|             var upTriggerId = getRandom('up-trigger'); | 
|             var upFileId = getRandom('up-file'); | 
|             var linkUrlId = getRandom('link-url'); | 
|             var linkBtnId = getRandom('link-btn'); | 
|   | 
|             // tabs 的配置 | 
|             var tabsConfig = [{ | 
|                 title: '上传图片', | 
|                 tpl: '<div class="w-e-up-img-container">\n                    <div id="' + upTriggerId + '" class="w-e-up-btn">\n                        <i class="w-e-icon-upload2"></i>\n                    </div>\n                    <div style="display:none;">\n                        <input id="' + upFileId + '" type="file" multiple="multiple" accept="image/jpg,image/jpeg,image/png,image/gif,image/bmp"/>\n                    </div>\n                </div>', | 
|                 events: [{ | 
|                     // 触发选择图片 | 
|                     selector: '#' + upTriggerId, | 
|                     type: 'click', | 
|                     fn: function fn() { | 
|                         var $file = $('#' + upFileId); | 
|                         var fileElem = $file[0]; | 
|                         if (fileElem) { | 
|                             fileElem.click(); | 
|                         } else { | 
|                             // 返回 true 可关闭 panel | 
|                             return true; | 
|                         } | 
|                     } | 
|                 }, { | 
|                     // 选择图片完毕 | 
|                     selector: '#' + upFileId, | 
|                     type: 'change', | 
|                     fn: function fn() { | 
|                         var $file = $('#' + upFileId); | 
|                         var fileElem = $file[0]; | 
|                         if (!fileElem) { | 
|                             // 返回 true 可关闭 panel | 
|                             return true; | 
|                         } | 
|   | 
|                         // 获取选中的 file 对象列表 | 
|                         var fileList = fileElem.files; | 
|                         if (fileList.length) { | 
|                             uploadImg.uploadImg(fileList); | 
|                         } | 
|   | 
|                         // 返回 true 可关闭 panel | 
|                         return true; | 
|                     } | 
|                 }] | 
|             }, // first tab end | 
|                 { | 
|                     title: '网络图片', | 
|                     tpl: '<div>\n                    <input id="' + linkUrlId + '" type="text" class="block" placeholder="\u56FE\u7247\u94FE\u63A5"/></td>\n                    <div class="w-e-button-container">\n                        <button id="' + linkBtnId + '" class="right">\u63D2\u5165</button>\n                    </div>\n                </div>', | 
|                     events: [{ | 
|                         selector: '#' + linkBtnId, | 
|                         type: 'click', | 
|                         fn: function fn() { | 
|                             var $linkUrl = $('#' + linkUrlId); | 
|                             var url = $linkUrl.val().trim(); | 
|   | 
|                             if (url) { | 
|                                 uploadImg.insertLinkImg(url); | 
|                             } | 
|   | 
|                             // 返回 true 表示函数执行结束之后关闭 panel | 
|                             return true; | 
|                         } | 
|                     }] | 
|                 } // second tab end | 
|             ]; // tabs end | 
|   | 
|             // 判断 tabs 的显示 | 
|             var tabsConfigResult = []; | 
|             if ((config.uploadImgShowBase64 || config.uploadImgServer) && window.FileReader) { | 
|                 // 显示“上传图片” | 
|                 tabsConfigResult.push(tabsConfig[0]); | 
|             } | 
|             if (config.showLinkImg) { | 
|                 // 显示“网络图片” | 
|                 tabsConfigResult.push(tabsConfig[1]); | 
|             } | 
|   | 
|             // 创建 panel 并显示 | 
|             var panel = new Panel(this, { | 
|                 width: 300, | 
|                 tabs: tabsConfigResult | 
|             }); | 
|             panel.show(); | 
|   | 
|             // 记录属性 | 
|             this.panel = panel; | 
|         }, | 
|   | 
|         // 试图改变 active 状态 | 
|         tryChangeActive: function tryChangeActive(e) { | 
|             var editor = this.editor; | 
|             var $elem = this.$elem; | 
|             if (editor._selectedImg) { | 
|                 this._active = true; | 
|                 $elem.addClass('w-e-active'); | 
|             } else { | 
|                 this._active = false; | 
|                 $elem.removeClass('w-e-active'); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      所有菜单的汇总 | 
|      */ | 
|   | 
| // 存储菜单的构造函数 | 
|     var MenuConstructors = {}; | 
|   | 
|     MenuConstructors.bold = Bold; | 
|   | 
|     MenuConstructors.head = Head; | 
|   | 
|     MenuConstructors.link = Link; | 
|   | 
|     MenuConstructors.italic = Italic; | 
|   | 
|     MenuConstructors.redo = Redo; | 
|   | 
|     MenuConstructors.strikeThrough = StrikeThrough; | 
|   | 
|     MenuConstructors.underline = Underline; | 
|   | 
|     MenuConstructors.undo = Undo; | 
|   | 
|     MenuConstructors.list = List; | 
|   | 
|     MenuConstructors.justify = Justify; | 
|   | 
|     MenuConstructors.foreColor = BackColor; | 
|   | 
|     MenuConstructors.backColor = ForeColor$1; | 
|   | 
|     MenuConstructors.quote = Quote; | 
|   | 
|     MenuConstructors.code = Code; | 
|   | 
|     MenuConstructors.emoticon = Emoticon; | 
|   | 
|     MenuConstructors.table = Table; | 
|   | 
|     MenuConstructors.video = Video; | 
|   | 
|     MenuConstructors.image = Image; | 
|   | 
|     /* | 
|      菜单集合 | 
|      */ | 
| // 构造函数 | 
|     function Menus(editor) { | 
|         this.editor = editor; | 
|         this.menus = {}; | 
|     } | 
|   | 
| // 修改原型 | 
|     Menus.prototype = { | 
|         constructor: Menus, | 
|   | 
|         // 初始化菜单 | 
|         init: function init() { | 
|             var _this = this; | 
|   | 
|             var editor = this.editor; | 
|             var config = editor.config || {}; | 
|             var configMenus = config.menus || []; // 获取配置中的菜单 | 
|   | 
|             // 根据配置信息,创建菜单 | 
|             configMenus.forEach(function (menuKey) { | 
|                 var MenuConstructor = MenuConstructors[menuKey]; | 
|                 if (MenuConstructor && typeof MenuConstructor === 'function') { | 
|                     // 创建单个菜单 | 
|                     _this.menus[menuKey] = new MenuConstructor(editor); | 
|                 } | 
|             }); | 
|   | 
|             // 添加到菜单栏 | 
|             this._addToToolbar(); | 
|   | 
|             // 绑定事件 | 
|             this._bindEvent(); | 
|         }, | 
|   | 
|         // 添加到菜单栏 | 
|         _addToToolbar: function _addToToolbar() { | 
|             var editor = this.editor; | 
|             var $toolbarElem = editor.$toolbarElem; | 
|             var menus = this.menus; | 
|             objForEach(menus, function (key, menu) { | 
|                 var $elem = menu.$elem; | 
|                 if ($elem) { | 
|                     $toolbarElem.append($elem); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 绑定菜单 click mouseenter 事件 | 
|         _bindEvent: function _bindEvent() { | 
|             var menus = this.menus; | 
|             var editor = this.editor; | 
|             objForEach(menus, function (key, menu) { | 
|                 var type = menu.type; | 
|                 if (!type) { | 
|                     return; | 
|                 } | 
|                 var $elem = menu.$elem; | 
|                 var droplist = menu.droplist; | 
|                 var panel = menu.panel; | 
|   | 
|                 // 点击类型,例如 bold | 
|                 if (type === 'click' && menu.onClick) { | 
|                     $elem.on('click', function (e) { | 
|                         if (editor.selection.getRange() == null) { | 
|                             return; | 
|                         } | 
|                         menu.onClick(e); | 
|                     }); | 
|                 } | 
|   | 
|                 // 下拉框,例如 head | 
|                 if (type === 'droplist' && droplist) { | 
|                     $elem.on('mouseenter', function (e) { | 
|                         if (editor.selection.getRange() == null) { | 
|                             return; | 
|                         } | 
|                         // 显示 | 
|                         droplist.showTimeoutId = setTimeout(function () { | 
|                             droplist.show(); | 
|                         }, 200); | 
|                     }).on('mouseleave', function (e) { | 
|                         // 隐藏 | 
|                         droplist.hideTimeoutId = setTimeout(function () { | 
|                             droplist.hide(); | 
|                         }, 0); | 
|                     }); | 
|                 } | 
|   | 
|                 // 弹框类型,例如 link | 
|                 if (type === 'panel' && menu.onClick) { | 
|                     $elem.on('click', function (e) { | 
|                         e.stopPropagation(); | 
|                         if (editor.selection.getRange() == null) { | 
|                             return; | 
|                         } | 
|                         // 在自定义事件中显示 panel | 
|                         menu.onClick(e); | 
|                     }); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 尝试修改菜单状态 | 
|         changeActive: function changeActive() { | 
|             var menus = this.menus; | 
|             objForEach(menus, function (key, menu) { | 
|                 if (menu.tryChangeActive) { | 
|                     setTimeout(function () { | 
|                         menu.tryChangeActive(); | 
|                     }, 100); | 
|                 } | 
|             }); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      粘贴信息的处理 | 
|      */ | 
|   | 
| // 获取粘贴的纯文本 | 
|     function getPasteText(e) { | 
|         var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; | 
|         var pasteText = void 0; | 
|         if (clipboardData == null) { | 
|             pasteText = window.clipboardData && window.clipboardData.getData('text'); | 
|         } else { | 
|             pasteText = clipboardData.getData('text/plain'); | 
|         } | 
|   | 
|         return replaceHtmlSymbol(pasteText); | 
|     } | 
|   | 
| // 获取粘贴的html | 
|     function getPasteHtml(e) { | 
|         var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData; | 
|         var pasteText = void 0, | 
|             pasteHtml = void 0; | 
|         if (clipboardData == null) { | 
|             pasteText = window.clipboardData && window.clipboardData.getData('text'); | 
|         } else { | 
|             pasteText = clipboardData.getData('text/plain'); | 
|             pasteHtml = clipboardData.getData('text/html'); | 
|         } | 
|         if (!pasteHtml && pasteText) { | 
|             pasteHtml = '<p>' + replaceHtmlSymbol(pasteText) + '</p>'; | 
|         } | 
|         if (!pasteHtml) { | 
|             return; | 
|         } | 
|   | 
|         // 过滤word中状态过来的无用字符 | 
|         var docSplitHtml = pasteHtml.split('</html>'); | 
|         if (docSplitHtml.length === 2) { | 
|             pasteHtml = docSplitHtml[0]; | 
|         } | 
|   | 
|         // 过滤无用标签 | 
|         pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/igm, ''); | 
|   | 
|         // 过滤样式 | 
|         pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").+?('|")/igm, ''); | 
|   | 
|         return pasteHtml; | 
|     } | 
|   | 
| // 获取粘贴的图片文件 | 
|     function getPasteImgs(e) { | 
|         var result = []; | 
|         var txt = getPasteText(e); | 
|         if (txt) { | 
|             // 有文字,就忽略图片 | 
|             return result; | 
|         } | 
|   | 
|         var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData || {}; | 
|         var items = clipboardData.items; | 
|         if (!items) { | 
|             return result; | 
|         } | 
|   | 
|         objForEach(items, function (key, value) { | 
|             var type = value.type; | 
|             if (/image/i.test(type)) { | 
|                 result.push(value.getAsFile()); | 
|             } | 
|         }); | 
|   | 
|         return result; | 
|     } | 
|   | 
|     /* | 
|      编辑区域 | 
|      */ | 
|   | 
| // 构造函数 | 
|     function Text(editor) { | 
|         this.editor = editor; | 
|     } | 
|   | 
| // 修改原型 | 
|     Text.prototype = { | 
|         constructor: Text, | 
|   | 
|         // 初始化 | 
|         init: function init() { | 
|             // 绑定事件 | 
|             this._bindEvent(); | 
|         }, | 
|   | 
|         // 清空内容 | 
|         clear: function clear() { | 
|             this.html('<p><br></p>'); | 
|         }, | 
|   | 
|         // 获取 设置 html | 
|         html: function html(val) { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             if (val == null) { | 
|                 return $textElem.html(); | 
|             } else { | 
|                 $textElem.html(val); | 
|   | 
|                 // 初始化选取,将光标定位到内容尾部 | 
|                 editor.initSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 获取 设置 text | 
|         text: function text(val) { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             if (val == null) { | 
|                 return $textElem.text(); | 
|             } else { | 
|                 $textElem.text('<p>' + val + '</p>'); | 
|   | 
|                 // 初始化选取,将光标定位到内容尾部 | 
|                 editor.initSelection(); | 
|             } | 
|         }, | 
|   | 
|         // 追加内容 | 
|         append: function append(html) { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             $textElem.append($(html)); | 
|   | 
|             // 初始化选取,将光标定位到内容尾部 | 
|             editor.initSelection(); | 
|         }, | 
|   | 
|         // 绑定事件 | 
|         _bindEvent: function _bindEvent() { | 
|             // 实时保存选取 | 
|             this._saveRangeRealTime(); | 
|   | 
|             // 按回车建时的特殊处理 | 
|             this._enterKeyHandle(); | 
|   | 
|             // 清空时保留 <p><br></p> | 
|             this._clearHandle(); | 
|   | 
|             // 粘贴事件(粘贴文字,粘贴图片) | 
|             this._pasteHandle(); | 
|   | 
|             // tab 特殊处理 | 
|             this._tabHandle(); | 
|   | 
|             // img 点击 | 
|             this._imgHandle(); | 
|         }, | 
|   | 
|         // 实时保存选取 | 
|         _saveRangeRealTime: function _saveRangeRealTime() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|   | 
|             // 保存当前的选区 | 
|             function saveRange(e) { | 
|                 // 随时保存选区 | 
|                 editor.selection.saveRange(); | 
|                 // 更新按钮 ative 状态 | 
|                 editor.menus.changeActive(); | 
|             } | 
|             // 按键后保存 | 
|             $textElem.on('keyup', saveRange); | 
|             $textElem.on('mousedown', function (e) { | 
|                 // mousedown 状态下,鼠标滑动到编辑区域外面,也需要保存选区 | 
|                 $textElem.on('mouseleave', saveRange); | 
|             }); | 
|             $textElem.on('mouseup', function (e) { | 
|                 saveRange(); | 
|                 // 在编辑器区域之内完成点击,取消鼠标滑动到编辑区外面的事件 | 
|                 $textElem.off('mouseleave', saveRange); | 
|             }); | 
|         }, | 
|   | 
|         // 按回车键时的特殊处理 | 
|         _enterKeyHandle: function _enterKeyHandle() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|   | 
|             // 将回车之后生成的非 <p> 的顶级标签,改为 <p> | 
|             function pHandle(e) { | 
|                 var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|                 var $parentElem = $selectionElem.parent(); | 
|                 if (!$parentElem.equal($textElem)) { | 
|                     // 不是顶级标签 | 
|                     return; | 
|                 } | 
|                 var nodeName = $selectionElem.getNodeName(); | 
|                 if (nodeName === 'P') { | 
|                     // 当前的标签是 P ,不用做处理 | 
|                     return; | 
|                 } | 
|   | 
|                 if ($selectionElem.text()) { | 
|                     // 有内容,不做处理 | 
|                     return; | 
|                 } | 
|   | 
|                 // 插入 <p> ,并将选取定位到 <p>,删除当前标签 | 
|                 var $p = $('<p><br></p>'); | 
|                 $p.insertBefore($selectionElem); | 
|                 editor.selection.createRangeByElem($p, true); | 
|                 editor.selection.restoreSelection(); | 
|                 $selectionElem.remove(); | 
|             } | 
|   | 
|             $textElem.on('keyup', function (e) { | 
|                 if (e.keyCode !== 13) { | 
|                     // 不是回车键 | 
|                     return; | 
|                 } | 
|                 // 将回车之后生成的非 <p> 的顶级标签,改为 <p> | 
|                 pHandle(e); | 
|             }); | 
|   | 
|             // <pre><code></code></pre> 回车时 特殊处理 | 
|             function codeHandle(e) { | 
|                 var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|                 if (!$selectionElem) { | 
|                     return; | 
|                 } | 
|                 var $parentElem = $selectionElem.parent(); | 
|                 var selectionNodeName = $selectionElem.getNodeName(); | 
|                 var parentNodeName = $parentElem.getNodeName(); | 
|   | 
|                 if (selectionNodeName !== 'CODE' || parentNodeName !== 'PRE') { | 
|                     // 不符合要求 忽略 | 
|                     return; | 
|                 } | 
|   | 
|                 if (!editor.cmd.queryCommandSupported('insertHTML')) { | 
|                     // 必须原生支持 insertHTML 命令 | 
|                     return; | 
|                 } | 
|   | 
|                 var _startOffset = editor.selection.getRange().startOffset; | 
|                 editor.cmd.do('insertHTML', '\n'); | 
|                 editor.selection.saveRange(); | 
|                 if (editor.selection.getRange().startOffset === _startOffset) { | 
|                     // 没起作用,再来一遍 | 
|                     editor.cmd.do('insertHTML', '\n'); | 
|                 } | 
|   | 
|                 // 阻止默认行为 | 
|                 e.preventDefault(); | 
|             } | 
|   | 
|             $textElem.on('keydown', function (e) { | 
|                 if (e.keyCode !== 13) { | 
|                     // 不是回车键 | 
|                     return; | 
|                 } | 
|                 // <pre><code></code></pre> 回车时 特殊处理 | 
|                 codeHandle(e); | 
|             }); | 
|         }, | 
|   | 
|         // 清空时保留 <p><br></p> | 
|         _clearHandle: function _clearHandle() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|   | 
|             $textElem.on('keydown', function (e) { | 
|                 if (e.keyCode !== 8) { | 
|                     return; | 
|                 } | 
|                 var txtHtml = $textElem.html().toLowerCase().trim(); | 
|                 if (txtHtml === '<p><br></p>') { | 
|                     // 最后剩下一个空行,就不再删除了 | 
|                     e.preventDefault(); | 
|                     return; | 
|                 } | 
|             }); | 
|   | 
|             $textElem.on('keyup', function (e) { | 
|                 if (e.keyCode !== 8) { | 
|                     return; | 
|                 } | 
|                 var $p = void 0; | 
|                 var txtHtml = $textElem.html().toLowerCase().trim(); | 
|   | 
|                 // firefox 时用 txtHtml === '<br>' 判断,其他用 !txtHtml 判断 | 
|                 if (!txtHtml || txtHtml === '<br>') { | 
|                     // 内容空了 | 
|                     $p = $('<p><br/></p>'); | 
|                     $textElem.html(''); // 一定要先清空,否则在 firefox 下有问题 | 
|                     $textElem.append($p); | 
|                     editor.selection.createRangeByElem($p, false, true); | 
|                     editor.selection.restoreSelection(); | 
|                 } | 
|             }); | 
|         }, | 
|   | 
|         // 粘贴事件(粘贴文字 粘贴图片) | 
|         _pasteHandle: function _pasteHandle() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|   | 
|             // 粘贴文字 | 
|             $textElem.on('paste', function (e) { | 
|                 if (UA.isIE()) { | 
|                     // IE 下放弃下面的判断 | 
|                     return; | 
|                 } | 
|   | 
|                 // 阻止默认行为,使用 execCommand 的粘贴命令 | 
|                 e.preventDefault(); | 
|   | 
|                 // 获取粘贴的文字 | 
|                 var pasteHtml = getPasteHtml(e); | 
|                 var pasteText = getPasteText(e); | 
|                 pasteText = pasteText.replace(/\n/gm, '<br>'); | 
|   | 
|                 var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|                 if (!$selectionElem) { | 
|                     return; | 
|                 } | 
|                 var nodeName = $selectionElem.getNodeName(); | 
|   | 
|                 // code 中粘贴忽略 | 
|                 if (nodeName === 'CODE' || nodeName === 'PRE') { | 
|                     return; | 
|                 } | 
|   | 
|                 // 表格中忽略,可能会出现异常问题 | 
|                 if (nodeName === 'TD' || nodeName === 'TH') { | 
|                     return; | 
|                 } | 
|   | 
|                 if (nodeName === 'DIV' || $textElem.html() === '<p><br></p>') { | 
|                     // 是 div,可粘贴过滤样式的文字和链接 | 
|                     if (!pasteHtml) { | 
|                         return; | 
|                     } | 
|                     try { | 
|                         // firefox 中,获取的 pasteHtml 可能是没有 <ul> 包裹的 <li> | 
|                         // 因此执行 insertHTML 会报错 | 
|                         editor.cmd.do('insertHTML', pasteHtml); | 
|                     } catch (ex) { | 
|                         // 此时使用 pasteText 来兼容一下 | 
|                         editor.cmd.do('insertHTML', '<p>' + pasteText + '</p>'); | 
|                     } | 
|                 } else { | 
|                     // 不是 div,证明在已有内容的元素中粘贴,只粘贴纯文本 | 
|                     if (!pasteText) { | 
|                         return; | 
|                     } | 
|                     editor.cmd.do('insertHTML', '<p>' + pasteText + '</p>'); | 
|                 } | 
|             }); | 
|   | 
|             // 粘贴图片 | 
|             $textElem.on('paste', function (e) { | 
|                 e.preventDefault(); | 
|   | 
|                 // 获取粘贴的图片 | 
|                 var pasteFiles = getPasteImgs(e); | 
|                 if (!pasteFiles || !pasteFiles.length) { | 
|                     return; | 
|                 } | 
|   | 
|                 // 获取当前的元素 | 
|                 var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|                 if (!$selectionElem) { | 
|                     return; | 
|                 } | 
|                 var nodeName = $selectionElem.getNodeName(); | 
|   | 
|                 // code 中粘贴忽略 | 
|                 if (nodeName === 'CODE' || nodeName === 'PRE') { | 
|                     return; | 
|                 } | 
|   | 
|                 // 上传图片 | 
|                 var uploadImg = editor.uploadImg; | 
|                 uploadImg.uploadImg(pasteFiles); | 
|             }); | 
|         }, | 
|   | 
|         // tab 特殊处理 | 
|         _tabHandle: function _tabHandle() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|   | 
|             $textElem.on('keydown', function (e) { | 
|                 if (e.keyCode !== 9) { | 
|                     return; | 
|                 } | 
|                 if (!editor.cmd.queryCommandSupported('insertHTML')) { | 
|                     // 必须原生支持 insertHTML 命令 | 
|                     return; | 
|                 } | 
|                 var $selectionElem = editor.selection.getSelectionContainerElem(); | 
|                 if (!$selectionElem) { | 
|                     return; | 
|                 } | 
|                 var $parentElem = $selectionElem.parent(); | 
|                 var selectionNodeName = $selectionElem.getNodeName(); | 
|                 var parentNodeName = $parentElem.getNodeName(); | 
|   | 
|                 if (selectionNodeName === 'CODE' && parentNodeName === 'PRE') { | 
|                     // <pre><code> 里面 | 
|                     editor.cmd.do('insertHTML', '    '); | 
|                 } else { | 
|                     // 普通文字 | 
|                     editor.cmd.do('insertHTML', '    '); | 
|                 } | 
|   | 
|                 e.preventDefault(); | 
|             }); | 
|         }, | 
|   | 
|         // img 点击 | 
|         _imgHandle: function _imgHandle() { | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             var selectedClass = 'w-e-selected'; | 
|   | 
|             // 为图片增加 selected 样式 | 
|             $textElem.on('click', 'img', function (e) { | 
|                 var img = this; | 
|                 var $img = $(img); | 
|   | 
|                 // 去掉所有图片的 selected 样式 | 
|                 $textElem.find('img').removeClass(selectedClass); | 
|   | 
|                 // 为点击的图片增加样式,并记录当前图片 | 
|                 $img.addClass(selectedClass); | 
|                 editor._selectedImg = $img; | 
|   | 
|                 // 修改选取 | 
|                 editor.selection.createRangeByElem($img); | 
|             }); | 
|   | 
|             // 去掉图片的 selected 样式 | 
|             $textElem.on('click  keyup', function (e) { | 
|                 if (e.target.matches('img')) { | 
|                     // 点击的是图片,忽略 | 
|                     return; | 
|                 } | 
|                 // 取消掉 selected 样式,并删除记录 | 
|                 $textElem.find('img').removeClass(selectedClass); | 
|                 editor._selectedImg = null; | 
|             }); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      命令,封装 document.execCommand | 
|      */ | 
|   | 
| // 构造函数 | 
|     function Command(editor) { | 
|         this.editor = editor; | 
|     } | 
|   | 
| // 修改原型 | 
|     Command.prototype = { | 
|         constructor: Command, | 
|   | 
|         // 执行命令 | 
|         do: function _do(name, value) { | 
|             var editor = this.editor; | 
|   | 
|             // 如果无选区,忽略 | 
|             if (!editor.selection.getRange()) { | 
|                 return; | 
|             } | 
|   | 
|             // 恢复选取 | 
|             editor.selection.restoreSelection(); | 
|   | 
|             // 执行 | 
|             var _name = '_' + name; | 
|             if (this[_name]) { | 
|                 // 有自定义事件 | 
|                 this[_name](value); | 
|             } else { | 
|                 // 默认 command | 
|                 this._execCommand(name, value); | 
|             } | 
|   | 
|             // 修改菜单状态 | 
|             editor.menus.changeActive(); | 
|   | 
|             // 最后,恢复选取保证光标在原来的位置闪烁 | 
|             editor.selection.saveRange(); | 
|             editor.selection.restoreSelection(); | 
|   | 
|             // 触发 onchange | 
|             editor.change && editor.change(); | 
|         }, | 
|   | 
|         // 自定义 insertHTML 事件 | 
|         _insertHTML: function _insertHTML(html) { | 
|             var editor = this.editor; | 
|             var range = editor.selection.getRange(); | 
|   | 
|             // 保证传入的参数是 html 代码 | 
|             var test = /^<.+>$/.test(html); | 
|             if (!test && !UA.isWebkit()) { | 
|                 // webkit 可以插入非 html 格式的文字 | 
|                 throw new Error('执行 insertHTML 命令时传入的参数必须是 html 格式'); | 
|             } | 
|   | 
|             if (this.queryCommandSupported('insertHTML')) { | 
|                 // W3C | 
|                 this._execCommand('insertHTML', html); | 
|             } else if (range.insertNode) { | 
|                 // IE | 
|                 range.deleteContents(); | 
|                 range.insertNode($(html)[0]); | 
|             } else if (range.pasteHTML) { | 
|                 // IE <= 10 | 
|                 range.pasteHTML(html); | 
|             } | 
|         }, | 
|   | 
|         // 插入 elem | 
|         _insertElem: function _insertElem($elem) { | 
|             var editor = this.editor; | 
|             var range = editor.selection.getRange(); | 
|   | 
|             if (range.insertNode) { | 
|                 range.deleteContents(); | 
|                 range.insertNode($elem[0]); | 
|             } | 
|         }, | 
|   | 
|         // 封装 execCommand | 
|         _execCommand: function _execCommand(name, value) { | 
|             document.execCommand(name, false, value); | 
|         }, | 
|   | 
|         // 封装 document.queryCommandValue | 
|         queryCommandValue: function queryCommandValue(name) { | 
|             return document.queryCommandValue(name); | 
|         }, | 
|   | 
|         // 封装 document.queryCommandState | 
|         queryCommandState: function queryCommandState(name) { | 
|             return document.queryCommandState(name); | 
|         }, | 
|   | 
|         // 封装 document.queryCommandSupported | 
|         queryCommandSupported: function queryCommandSupported(name) { | 
|             return document.queryCommandSupported(name); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      selection range API | 
|      */ | 
|   | 
| // 构造函数 | 
|     function API(editor) { | 
|         this.editor = editor; | 
|         this._currentRange = null; | 
|     } | 
|   | 
| // 修改原型 | 
|     API.prototype = { | 
|         constructor: API, | 
|   | 
|         // 获取 range 对象 | 
|         getRange: function getRange() { | 
|             return this._currentRange; | 
|         }, | 
|   | 
|         // 保存选区 | 
|         saveRange: function saveRange(_range) { | 
|             if (_range) { | 
|                 // 保存已有选区 | 
|                 this._currentRange = _range; | 
|                 return; | 
|             } | 
|   | 
|             // 获取当前的选区 | 
|             var selection = window.getSelection(); | 
|             if (selection.rangeCount === 0) { | 
|                 return; | 
|             } | 
|             var range = selection.getRangeAt(0); | 
|   | 
|             // 判断选区内容是否在编辑内容之内 | 
|             var $containerElem = this.getSelectionContainerElem(range); | 
|             if (!$containerElem) { | 
|                 return; | 
|             } | 
|             var editor = this.editor; | 
|             var $textElem = editor.$textElem; | 
|             if ($textElem.isContain($containerElem)) { | 
|                 // 是编辑内容之内的 | 
|                 this._currentRange = range; | 
|             } | 
|         }, | 
|   | 
|         // 折叠选区 | 
|         collapseRange: function collapseRange(toStart) { | 
|             if (toStart == null) { | 
|                 // 默认为 false | 
|                 toStart = false; | 
|             } | 
|             var range = this._currentRange; | 
|             if (range) { | 
|                 range.collapse(toStart); | 
|             } | 
|         }, | 
|   | 
|         // 选中区域的文字 | 
|         getSelectionText: function getSelectionText() { | 
|             var range = this._currentRange; | 
|             if (range) { | 
|                 return this._currentRange.toString(); | 
|             } else { | 
|                 return ''; | 
|             } | 
|         }, | 
|   | 
|         // 选区的 $Elem | 
|         getSelectionContainerElem: function getSelectionContainerElem(range) { | 
|             range = range || this._currentRange; | 
|             var elem = void 0; | 
|             if (range) { | 
|                 elem = range.commonAncestorContainer; | 
|                 return $(elem.nodeType === 1 ? elem : elem.parentNode); | 
|             } | 
|         }, | 
|         getSelectionStartElem: function getSelectionStartElem(range) { | 
|             range = range || this._currentRange; | 
|             var elem = void 0; | 
|             if (range) { | 
|                 elem = range.startContainer; | 
|                 return $(elem.nodeType === 1 ? elem : elem.parentNode); | 
|             } | 
|         }, | 
|         getSelectionEndElem: function getSelectionEndElem(range) { | 
|             range = range || this._currentRange; | 
|             var elem = void 0; | 
|             if (range) { | 
|                 elem = range.endContainer; | 
|                 return $(elem.nodeType === 1 ? elem : elem.parentNode); | 
|             } | 
|         }, | 
|   | 
|         // 选区是否为空 | 
|         isSelectionEmpty: function isSelectionEmpty() { | 
|             var range = this._currentRange; | 
|             if (range && range.startContainer) { | 
|                 if (range.startContainer === range.endContainer) { | 
|                     if (range.startOffset === range.endOffset) { | 
|                         return true; | 
|                     } | 
|                 } | 
|             } | 
|             return false; | 
|         }, | 
|   | 
|         // 恢复选区 | 
|         restoreSelection: function restoreSelection() { | 
|             var selection = window.getSelection(); | 
|             selection.removeAllRanges(); | 
|             selection.addRange(this._currentRange); | 
|         }, | 
|   | 
|         // 创建一个空白(即 ​ 字符)选区 | 
|         createEmptyRange: function createEmptyRange() { | 
|             var editor = this.editor; | 
|             var range = this.getRange(); | 
|             var $elem = void 0; | 
|   | 
|             if (!range) { | 
|                 // 当前无 range | 
|                 return; | 
|             } | 
|             if (!this.isSelectionEmpty()) { | 
|                 // 当前选区必须没有内容才可以 | 
|                 return; | 
|             } | 
|   | 
|             // 目前只支持 webkit 内核 | 
|             if (UA.isWebkit()) { | 
|                 // 插入 ​ | 
|                 editor.cmd.do('insertHTML', '​'); | 
|                 // 修改 offset 位置 | 
|                 range.setEnd(range.endContainer, range.endOffset + 1); | 
|                 // 存储 | 
|                 this.saveRange(range); | 
|             } else { | 
|                 $elem = $('<strong>​</strong>'); | 
|                 editor.cmd.do('insertElem', $elem); | 
|                 this.createRangeByElem($elem, true); | 
|             } | 
|         }, | 
|   | 
|         // 根据 $Elem 设置选区 | 
|         createRangeByElem: function createRangeByElem($elem, toStart, isContent) { | 
|             // $elem - 经过封装的 elem | 
|             // toStart - true 开始位置,false 结束位置 | 
|             // isContent - 是否选中Elem的内容 | 
|             if (!$elem.length) { | 
|                 return; | 
|             } | 
|   | 
|             var elem = $elem[0]; | 
|             var range = document.createRange(); | 
|   | 
|             if (isContent) { | 
|                 range.selectNodeContents(elem); | 
|             } else { | 
|                 range.selectNode(elem); | 
|             } | 
|   | 
|             if (typeof toStart === 'boolean') { | 
|                 range.collapse(toStart); | 
|             } | 
|   | 
|             // 存储 range | 
|             this.saveRange(range); | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      上传进度条 | 
|      */ | 
|   | 
|     function Progress(editor) { | 
|         this.editor = editor; | 
|         this._time = 0; | 
|         this._isShow = false; | 
|         this._isRender = false; | 
|         this._timeoutId = 0; | 
|         this.$textContainer = editor.$textContainerElem; | 
|         this.$bar = $('<div class="w-e-progress"></div>'); | 
|     } | 
|   | 
|     Progress.prototype = { | 
|         constructor: Progress, | 
|   | 
|         show: function show(progress) { | 
|             var _this = this; | 
|   | 
|             // 状态处理 | 
|             if (this._isShow) { | 
|                 return; | 
|             } | 
|             this._isShow = true; | 
|   | 
|             // 渲染 | 
|             var $bar = this.$bar; | 
|             if (!this._isRender) { | 
|                 var $textContainer = this.$textContainer; | 
|                 $textContainer.append($bar); | 
|             } else { | 
|                 this._isRender = true; | 
|             } | 
|   | 
|             // 改变进度(节流,100ms 渲染一次) | 
|             if (Date.now() - this._time > 100) { | 
|                 if (progress <= 1) { | 
|                     $bar.css('width', progress * 100 + '%'); | 
|                     this._time = Date.now(); | 
|                 } | 
|             } | 
|   | 
|             // 隐藏 | 
|             var timeoutId = this._timeoutId; | 
|             if (timeoutId) { | 
|                 clearTimeout(timeoutId); | 
|             } | 
|             timeoutId = setTimeout(function () { | 
|                 _this._hide(); | 
|             }, 500); | 
|         }, | 
|   | 
|         _hide: function _hide() { | 
|             var $bar = this.$bar; | 
|             $bar.remove(); | 
|   | 
|             // 修改状态 | 
|             this._time = 0; | 
|             this._isShow = false; | 
|             this._isRender = false; | 
|         } | 
|     }; | 
|   | 
|     var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { | 
|         return typeof obj; | 
|     } : function (obj) { | 
|         return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | 
|     }; | 
|   | 
|     /* | 
|      上传图片 | 
|      */ | 
|   | 
| // 构造函数 | 
|     function UploadImg(editor) { | 
|         this.editor = editor; | 
|     } | 
|   | 
| // 原型 | 
|     UploadImg.prototype = { | 
|         constructor: UploadImg, | 
|   | 
|         // 根据 debug 弹出不同的信息 | 
|         _alert: function _alert(alertInfo, debugInfo) { | 
|             var editor = this.editor; | 
|             var debug = editor.config.debug; | 
|   | 
|             if (debug) { | 
|                 throw new Error('wangEditor: ' + (debugInfo || alertInfo)); | 
|             } else { | 
|                 alert(alertInfo); | 
|             } | 
|         }, | 
|   | 
|         // 根据链接插入图片 | 
|         insertLinkImg: function insertLinkImg(link) { | 
|             var _this2 = this; | 
|   | 
|             if (!link) { | 
|                 return; | 
|             } | 
|             var editor = this.editor; | 
|   | 
|             var img = document.createElement('img'); | 
|             img.onload = function () { | 
|                 img = null; | 
|                 editor.cmd.do('insertHTML', '<img src="' + link + '" style="max-width:100%;"/>'); | 
|             }; | 
|             img.onerror = function () { | 
|                 img = null; | 
|                 // 无法成功下载图片 | 
|                 _this2._alert('插入图片错误', 'wangEditor: \u63D2\u5165\u56FE\u7247\u51FA\u9519\uFF0C\u56FE\u7247\u94FE\u63A5\u662F "' + link + '"\uFF0C\u4E0B\u8F7D\u8BE5\u94FE\u63A5\u5931\u8D25'); | 
|                 return; | 
|             }; | 
|             img.onabort = function () { | 
|                 img = null; | 
|             }; | 
|             img.src = link; | 
|         }, | 
|   | 
|         // 上传图片 | 
|         uploadImg: function uploadImg(files) { | 
|             var _this3 = this; | 
|   | 
|             if (!files || !files.length) { | 
|                 return; | 
|             } | 
|   | 
|             // ------------------------------ 获取配置信息 ------------------------------ | 
|             var editor = this.editor; | 
|             var config = editor.config; | 
|             var maxSize = config.uploadImgMaxSize; | 
|             var maxSizeM = maxSize / 1000 / 1000; | 
|             var maxLength = config.uploadImgMaxLength || 10000; | 
|             var uploadImgServer = config.uploadImgServer; | 
|             var uploadImgShowBase64 = config.uploadImgShowBase64; | 
|             var uploadFileName = config.uploadFileName || ''; | 
|             var uploadImgParams = config.uploadImgParams || {}; | 
|             var uploadImgHeaders = config.uploadImgHeaders || {}; | 
|             var hooks = config.uploadImgHooks || {}; | 
|             var timeout = config.uploadImgTimeout || 3000; | 
|             var withCredentials = config.withCredentials; | 
|             if (withCredentials == null) { | 
|                 withCredentials = false; | 
|             } | 
|   | 
|             // ------------------------------ 验证文件信息 ------------------------------ | 
|             var resultFiles = []; | 
|             var errInfo = []; | 
|             arrForEach(files, function (file) { | 
|                 var name = file.name; | 
|                 var size = file.size; | 
|                 if (/\.(jpg|jpeg|png|bmp|gif)$/i.test(name) === false) { | 
|                     // 后缀名不合法,不是图片 | 
|                     errInfo.push('\u3010' + name + '\u3011\u4E0D\u662F\u56FE\u7247'); | 
|                     return; | 
|                 } | 
|                 if (maxSize < size) { | 
|                     // 上传图片过大 | 
|                     errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M'); | 
|                     return; | 
|                 } | 
|   | 
|                 // 验证通过的加入结果列表 | 
|                 resultFiles.push(file); | 
|             }); | 
|             // 抛出验证信息 | 
|             if (errInfo.length) { | 
|                 this._alert('图片验证未通过: \n' + errInfo.join('\n')); | 
|                 return; | 
|             } | 
|             if (resultFiles.length > maxLength) { | 
|                 this._alert('一次最多上传' + maxLength + '张图片'); | 
|                 return; | 
|             } | 
|   | 
|             // 添加图片数据 | 
|             var formdata = new FormData(); | 
|             arrForEach(resultFiles, function (file) { | 
|                 var name = uploadFileName || file.name; | 
|                 formdata.append(name, file); | 
|             }); | 
|   | 
|             // ------------------------------ 上传图片 ------------------------------ | 
|             if (uploadImgServer && typeof uploadImgServer === 'string') { | 
|                 // 添加参数 | 
|                 var uploadImgServerArr = uploadImgServer.split('#'); | 
|                 uploadImgServer = uploadImgServerArr[0]; | 
|                 var uploadImgServerHash = uploadImgServerArr[1] || ''; | 
|                 objForEach(uploadImgParams, function (key, val) { | 
|                     val = encodeURIComponent(val); | 
|   | 
|                     // 第一,将参数拼接到 url 中 | 
|                     if (uploadImgServer.indexOf('?') > 0) { | 
|                         uploadImgServer += '&'; | 
|                     } else { | 
|                         uploadImgServer += '?'; | 
|                     } | 
|                     uploadImgServer = uploadImgServer + key + '=' + val; | 
|   | 
|                     // 第二,将参数添加到 formdata 中 | 
|                     formdata.append(key, val); | 
|                 }); | 
|                 if (uploadImgServerHash) { | 
|                     uploadImgServer += '#' + uploadImgServerHash; | 
|                 } | 
|   | 
|                 // 定义 xhr | 
|                 var xhr = new XMLHttpRequest(); | 
|                 xhr.open('POST', uploadImgServer); | 
|   | 
|                 // 设置超时 | 
|                 xhr.timeout = timeout; | 
|                 xhr.ontimeout = function () { | 
|                     // hook - timeout | 
|                     if (hooks.timeout && typeof hooks.timeout === 'function') { | 
|                         hooks.timeout(xhr, editor); | 
|                     } | 
|   | 
|                     _this3._alert('上传图片超时'); | 
|                 }; | 
|   | 
|                 // 监控 progress | 
|                 if (xhr.upload) { | 
|                     xhr.upload.onprogress = function (e) { | 
|                         var percent = void 0; | 
|                         // 进度条 | 
|                         var progressBar = new Progress(editor); | 
|                         if (e.lengthComputable) { | 
|                             percent = e.loaded / e.total; | 
|                             progressBar.show(percent); | 
|                         } | 
|                     }; | 
|                 } | 
|   | 
|                 // 返回数据 | 
|                 xhr.onreadystatechange = function () { | 
|                     var result = void 0; | 
|                     if (xhr.readyState === 4) { | 
|                         if (xhr.status < 200 || xhr.status >= 300) { | 
|                             // hook - error | 
|                             if (hooks.error && typeof hooks.error === 'function') { | 
|                                 hooks.error(xhr, editor); | 
|                             } | 
|   | 
|                             // xhr 返回状态错误 | 
|                             _this3._alert('上传图片发生错误', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status); | 
|                             return; | 
|                         } | 
|   | 
|                         result = xhr.responseText; | 
|                         if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') { | 
|                             try { | 
|                                 result = JSON.parse(result); | 
|                             } catch (ex) { | 
|                                 // hook - fail | 
|                                 if (hooks.fail && typeof hooks.fail === 'function') { | 
|                                     hooks.fail(xhr, editor, result); | 
|                                 } | 
|   | 
|                                 _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果是: ' + result); | 
|                                 return; | 
|                             } | 
|                         } | 
|                         if (!hooks.customInsert && result.errno != '0') { | 
|                             // hook - fail | 
|                             if (hooks.fail && typeof hooks.fail === 'function') { | 
|                                 hooks.fail(xhr, editor, result); | 
|                             } | 
|   | 
|                             // 数据错误 | 
|                             _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果 errno=' + result.errno); | 
|                         } else { | 
|                             if (hooks.customInsert && typeof hooks.customInsert === 'function') { | 
|                                 // 使用者自定义插入方法 | 
|                                 hooks.customInsert(_this3.insertLinkImg.bind(_this3), result, editor); | 
|                             } else { | 
|                                 // 将图片插入编辑器 | 
|                                 var data = result.data || []; | 
|                                 data.forEach(function (link) { | 
|                                     _this3.insertLinkImg(link); | 
|                                 }); | 
|                             } | 
|   | 
|                             // hook - success | 
|                             if (hooks.success && typeof hooks.success === 'function') { | 
|                                 hooks.success(xhr, editor, result); | 
|                             } | 
|                         } | 
|                     } | 
|                 }; | 
|   | 
|                 // hook - before | 
|                 if (hooks.before && typeof hooks.before === 'function') { | 
|                     hooks.before(xhr, editor, resultFiles); | 
|                 } | 
|   | 
|                 // 自定义 headers | 
|                 objForEach(uploadImgHeaders, function (key, val) { | 
|                     xhr.setRequestHeader(key, val); | 
|                 }); | 
|   | 
|                 // 跨域传 cookie | 
|                 xhr.withCredentials = withCredentials; | 
|   | 
|                 // 发送请求 | 
|                 xhr.send(formdata); | 
|   | 
|                 // 注意,要 return 。不去操作接下来的 base64 显示方式 | 
|                 return; | 
|             } | 
|   | 
|             // 显示 base64 格式 | 
|             if (uploadImgShowBase64) { | 
|                 arrForEach(files, function (file) { | 
|                     var _this = _this3; | 
|                     var reader = new FileReader(); | 
|                     reader.readAsDataURL(file); | 
|                     reader.onload = function () { | 
|                         _this.insertLinkImg(this.result); | 
|                     }; | 
|                 }); | 
|             } | 
|         } | 
|     }; | 
|   | 
|     /* | 
|      编辑器构造函数 | 
|      */ | 
|   | 
| // id,累加 | 
|     var editorId = 1; | 
|   | 
| // 构造函数 | 
|     function Editor(toolbarSelector, textSelector) { | 
|         if (toolbarSelector == null) { | 
|             // 没有传入任何参数,报错 | 
|             throw new Error('错误:初始化编辑器时候未传入任何参数,请查阅文档'); | 
|         } | 
|         // id,用以区分单个页面不同的编辑器对象 | 
|         this.id = 'wangEditor-' + editorId++; | 
|   | 
|         this.toolbarSelector = toolbarSelector; | 
|         this.textSelector = textSelector; | 
|   | 
|         // 自定义配置 | 
|         this.customConfig = {}; | 
|     } | 
|   | 
| // 修改原型 | 
|     Editor.prototype = { | 
|         constructor: Editor, | 
|   | 
|         // 初始化配置 | 
|         _initConfig: function _initConfig() { | 
|             // _config 是默认配置,this.customConfig 是用户自定义配置,将它们 merge 之后再赋值 | 
|             var target = {}; | 
|             this.config = Object.assign(target, config, this.customConfig); | 
|         }, | 
|   | 
|         // 初始化 DOM | 
|         _initDom: function _initDom() { | 
|             var _this = this; | 
|   | 
|             var toolbarSelector = this.toolbarSelector; | 
|             var $toolbarSelector = $(toolbarSelector); | 
|             var textSelector = this.textSelector; | 
|   | 
|             var config$$1 = this.config; | 
|             var zIndex = config$$1.zIndex || '10000'; | 
|   | 
|             // 定义变量 | 
|             var $toolbarElem = void 0, | 
|                 $textContainerElem = void 0, | 
|                 $textElem = void 0, | 
|                 $children = void 0; | 
|   | 
|             if (textSelector == null) { | 
|                 // 只传入一个参数,即是容器的选择器或元素,toolbar 和 text 的元素自行创建 | 
|                 $toolbarElem = $('<div></div>'); | 
|                 $textContainerElem = $('<div></div>'); | 
|   | 
|                 // 将编辑器区域原有的内容,暂存起来 | 
|                 $children = $toolbarSelector.children(); | 
|   | 
|                 // 添加到 DOM 结构中 | 
|                 $toolbarSelector.append($toolbarElem).append($textContainerElem); | 
|   | 
|                 // 自行创建的,需要配置默认的样式 | 
|                 $toolbarElem.css('background-color', '#f1f1f1').css('border', '1px solid #ccc'); | 
|                 $textContainerElem.css('border', '1px solid #ccc').css('border-top', 'none').css('height', '150px'); | 
|             } else { | 
|                 // toolbar 和 text 的选择器都有值,记录属性 | 
|                 $toolbarElem = $toolbarSelector; | 
|                 $textContainerElem = $(textSelector); | 
|                 // 将编辑器区域原有的内容,暂存起来 | 
|                 $children = $textContainerElem.children(); | 
|             } | 
|   | 
|             // 编辑区域 | 
|             $textElem = $('<div></div>'); | 
|             $textElem.attr('contenteditable', 'true').css('width', '100%').css('height', '100%'); | 
|   | 
|             // 初始化编辑区域内容 | 
|             if ($children && $children.length) { | 
|                 $textElem.append($children); | 
|             } else { | 
|                 $textElem.append($('<p><br></p>')); | 
|             } | 
|   | 
|             // 编辑区域加入DOM | 
|             $textContainerElem.append($textElem); | 
|   | 
|             // 设置通用的 class | 
|             $toolbarElem.addClass('w-e-toolbar'); | 
|             $textContainerElem.addClass('w-e-text-container'); | 
|             $textContainerElem.css('z-index', zIndex); | 
|             $textElem.addClass('w-e-text'); | 
|   | 
|             // 记录属性 | 
|             this.$toolbarElem = $toolbarElem; | 
|             this.$textContainerElem = $textContainerElem; | 
|             this.$textElem = $textElem; | 
|   | 
|             // 绑定 onchange | 
|             $textContainerElem.on('click keyup', function () { | 
|                 _this.change && _this.change(); | 
|             }); | 
|             $toolbarElem.on('click', function () { | 
|                 this.change && this.change(); | 
|             }); | 
|         }, | 
|   | 
|         // 封装 command | 
|         _initCommand: function _initCommand() { | 
|             this.cmd = new Command(this); | 
|         }, | 
|   | 
|         // 封装 selection range API | 
|         _initSelectionAPI: function _initSelectionAPI() { | 
|             this.selection = new API(this); | 
|         }, | 
|   | 
|         // 添加图片上传 | 
|         _initUploadImg: function _initUploadImg() { | 
|             this.uploadImg = new UploadImg(this); | 
|         }, | 
|   | 
|         // 初始化菜单 | 
|         _initMenus: function _initMenus() { | 
|             this.menus = new Menus(this); | 
|             this.menus.init(); | 
|         }, | 
|   | 
|         // 添加 text 区域 | 
|         _initText: function _initText() { | 
|             this.txt = new Text(this); | 
|             this.txt.init(); | 
|         }, | 
|   | 
|         // 初始化选区,将光标定位到内容尾部 | 
|         initSelection: function initSelection() { | 
|             var $textElem = this.$textElem; | 
|             var $children = $textElem.children(); | 
|             if (!$children.length) { | 
|                 // 如果编辑器区域无内容,添加一个空行,重新设置选区 | 
|                 $textElem.append($('<p><br></p>')); | 
|                 this.initSelection(); | 
|                 return; | 
|             } | 
|   | 
|             var $last = $children.last(); | 
|             var html = $last.html().toLowerCase(); | 
|             var nodeName = $last.getNodeName(); | 
|             if (html !== '<br>' && html !== '<br\/>' || nodeName !== 'P') { | 
|                 // 最后一个元素不是 <p><br></p>,添加一个空行,重新设置选区 | 
|                 $textElem.append($('<p><br></p>')); | 
|                 this.initSelection(); | 
|                 return; | 
|             } | 
|   | 
|             this.selection.createRangeByElem($last, true); | 
|             this.selection.restoreSelection(); | 
|         }, | 
|   | 
|         // 绑定事件 | 
|         _bindEvent: function _bindEvent() { | 
|             // -------- 绑定 onchange 事件 -------- | 
|             var onChangeTimeoutId = 0; | 
|             var beforeChangeHtml = this.txt.html(); | 
|             var config$$1 = this.config; | 
|             var onchange = config$$1.onchange; | 
|             if (onchange && typeof onchange === 'function') { | 
|                 // 触发 change 的有三个场景: | 
|                 // 1. $textContainerElem.on('click keyup') | 
|                 // 2. $toolbarElem.on('click') | 
|                 // 3. editor.cmd.do() | 
|                 this.change = function () { | 
|                     // 判断是否有变化 | 
|                     var currentHtml = this.txt.html(); | 
|                     if (currentHtml.length === beforeChangeHtml.length) { | 
|                         return; | 
|                     } | 
|   | 
|                     // 执行,使用节流 | 
|                     if (onChangeTimeoutId) { | 
|                         clearTimeout(onChangeTimeoutId); | 
|                     } | 
|                     onChangeTimeoutId = setTimeout(function () { | 
|                         // 触发配置的 onchange 函数 | 
|                         onchange(currentHtml); | 
|                         beforeChangeHtml = currentHtml; | 
|                     }, 200); | 
|                 }; | 
|             } | 
|         }, | 
|   | 
|         // 创建编辑器 | 
|         create: function create() { | 
|             // 初始化配置信息 | 
|             this._initConfig(); | 
|   | 
|             // 初始化 DOM | 
|             this._initDom(); | 
|   | 
|             // 封装 command API | 
|             this._initCommand(); | 
|   | 
|             // 封装 selection range API | 
|             this._initSelectionAPI(); | 
|   | 
|             // 添加 text | 
|             this._initText(); | 
|   | 
|             // 初始化菜单 | 
|             this._initMenus(); | 
|   | 
|             // 添加 图片上传 | 
|             this._initUploadImg(); | 
|   | 
|             // 初始化选区,将光标定位到内容尾部 | 
|             this.initSelection(); | 
|   | 
|             // 绑定事件 | 
|             this._bindEvent(); | 
|         } | 
|     }; | 
|   | 
| // 检验是否浏览器环境 | 
|     try { | 
|         document; | 
|     } catch (ex) { | 
|         throw new Error('请在浏览器环境下运行'); | 
|     } | 
|   | 
| // polyfill | 
|     polyfill(); | 
|   | 
| // 这里的 `inlinecss` 将被替换成 css 代码的内容,详情可去 ./gulpfile.js 中搜索 `inlinecss` 关键字 | 
|     var inlinecss = '.w-e-toolbar,.w-e-text-container,.w-e-menu-panel {  padding: 0;  margin: 0;  box-sizing: border-box;}.w-e-toolbar *,.w-e-text-container *,.w-e-menu-panel * {  padding: 0;  margin: 0;  box-sizing: border-box;}.w-e-clear-fix:after {  content: "";  display: table;  clear: both;}.w-e-toolbar .w-e-droplist {  position: absolute;  left: 0;  top: 0;  background-color: #fff;  border: 1px solid #f1f1f1;  border-right-color: #ccc;  border-bottom-color: #ccc;}.w-e-toolbar .w-e-droplist .w-e-dp-title {  text-align: center;  color: #999;  line-height: 2;  border-bottom: 1px solid #f1f1f1;  font-size: 13px;}.w-e-toolbar .w-e-droplist ul.w-e-list {  list-style: none;  line-height: 1;}.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item {  color: #333;  padding: 5px 0;}.w-e-toolbar .w-e-droplist ul.w-e-list li.w-e-item:hover {  background-color: #f1f1f1;}.w-e-toolbar .w-e-droplist ul.w-e-block {  list-style: none;  text-align: left;  padding: 5px;}.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item {  display: inline-block;  *display: inline;  *zoom: 1;  padding: 3px 5px;}.w-e-toolbar .w-e-droplist ul.w-e-block li.w-e-item:hover {  background-color: #f1f1f1;}@font-face {  font-family: \'icomoon\';  src: url(data:application/x-font-woff;charset=utf-8;base64,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) format(\'truetype\');  font-weight: normal;  font-style: normal;}[class^="w-e-icon-"],[class*=" w-e-icon-"] {  /* use !important to prevent issues with browser extensions that change fonts */  font-family: \'icomoon\' !important;  speak: none;  font-style: normal;  font-weight: normal;  font-variant: normal;  text-transform: none;  line-height: 1;  /* Better Font Rendering =========== */  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;}.w-e-icon-close:before {  content: "\\f00d";}.w-e-icon-upload2:before {  content: "\\e9c6";}.w-e-icon-trash-o:before {  content: "\\f014";}.w-e-icon-header:before {  content: "\\f1dc";}.w-e-icon-pencil2:before {  content: "\\e906";}.w-e-icon-paint-brush:before {  content: "\\f1fc";}.w-e-icon-image:before {  content: "\\e90d";}.w-e-icon-play:before {  content: "\\e912";}.w-e-icon-location:before {  content: "\\e947";}.w-e-icon-undo:before {  content: "\\e965";}.w-e-icon-redo:before {  content: "\\e966";}.w-e-icon-quotes-left:before {  content: "\\e977";}.w-e-icon-list-numbered:before {  content: "\\e9b9";}.w-e-icon-list2:before {  content: "\\e9bb";}.w-e-icon-link:before {  content: "\\e9cb";}.w-e-icon-happy:before {  content: "\\e9df";}.w-e-icon-bold:before {  content: "\\ea62";}.w-e-icon-underline:before {  content: "\\ea63";}.w-e-icon-italic:before {  content: "\\ea64";}.w-e-icon-strikethrough:before {  content: "\\ea65";}.w-e-icon-table2:before {  content: "\\ea71";}.w-e-icon-paragraph-left:before {  content: "\\ea77";}.w-e-icon-paragraph-center:before {  content: "\\ea78";}.w-e-icon-paragraph-right:before {  content: "\\ea79";}.w-e-icon-terminal:before {  content: "\\f120";}.w-e-icon-page-break:before {  content: "\\ea68";}.w-e-icon-cancel-circle:before {  content: "\\ea0d";}.w-e-toolbar {  display: -webkit-box;  display: -ms-flexbox;  display: flex;  padding: 0 5px;  /* 单个菜单 */}.w-e-toolbar .w-e-menu {  position: relative;  z-index: 10001;  text-align: center;  padding: 5px 10px;  cursor: pointer;}.w-e-toolbar .w-e-menu i {  color: #999;}.w-e-toolbar .w-e-menu:hover i {  color: #333;}.w-e-toolbar .w-e-active i {  color: #1e88e5;}.w-e-toolbar .w-e-active:hover i {  color: #1e88e5;}.w-e-text-container .w-e-panel-container {  position: absolute;  top: 0;  left: 50%;  border: 1px solid #ccc;  border-top: 0;  box-shadow: 1px 1px 2px #ccc;  color: #333;  background-color: #fff;  /* 为 emotion panel 定制的样式 */  /* 上传图片的 panel 定制样式 */}.w-e-text-container .w-e-panel-container .w-e-panel-close {  position: absolute;  right: 0;  top: 0;  padding: 5px;  margin: 2px 5px 0 0;  cursor: pointer;  color: #999;}.w-e-text-container .w-e-panel-container .w-e-panel-close:hover {  color: #333;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title {  list-style: none;  display: -webkit-box;  display: -ms-flexbox;  display: flex;  font-size: 14px;  margin: 2px 10px 0 10px;  border-bottom: 1px solid #f1f1f1;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-item {  padding: 3px 5px;  color: #999;  cursor: pointer;  margin: 0 3px;  position: relative;  top: 1px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-title .w-e-active {  color: #333;  border-bottom: 1px solid #333;  cursor: default;  font-weight: 700;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content {  padding: 10px 15px 10px 15px;  font-size: 16px;  /* 输入框的样式 */  /* 按钮的样式 */}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input:focus,.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus,.w-e-text-container .w-e-panel-container .w-e-panel-tab-content button:focus {  outline: none;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea {  width: 100%;  border: 1px solid #ccc;  padding: 5px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content textarea:focus {  border-color: #1e88e5;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text] {  border: none;  border-bottom: 1px solid #ccc;  font-size: 14px;  height: 20px;  color: #333;  text-align: left;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].small {  width: 30px;  text-align: center;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text].block {  display: block;  width: 100%;  margin: 10px 0;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content input[type=text]:focus {  border-bottom: 2px solid #1e88e5;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button {  font-size: 14px;  color: #1e88e5;  border: none;  padding: 5px 10px;  background-color: #fff;  cursor: pointer;  border-radius: 3px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.left {  float: left;  margin-right: 10px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.right {  float: right;  margin-left: 10px;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.gray {  color: #999;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button.red {  color: #c24f4a;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container button:hover {  background-color: #f1f1f1;}.w-e-text-container .w-e-panel-container .w-e-panel-tab-content .w-e-button-container:after {  content: "";  display: table;  clear: both;}.w-e-text-container .w-e-panel-container .w-e-emoticon-container .w-e-item {  cursor: pointer;  font-size: 18px;  padding: 0 3px;  display: inline-block;  *display: inline;  *zoom: 1;}.w-e-text-container .w-e-panel-container .w-e-up-img-container {  text-align: center;}.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn {  display: inline-block;  *display: inline;  *zoom: 1;  color: #999;  cursor: pointer;  font-size: 60px;  line-height: 1;}.w-e-text-container .w-e-panel-container .w-e-up-img-container .w-e-up-btn:hover {  color: #333;}.w-e-text-container {  position: relative;}.w-e-text-container .w-e-progress {  position: absolute;  background-color: #1e88e5;  bottom: 0;  left: 0;  height: 1px;}.w-e-text {  padding: 0 10px;  overflow-y: scroll;}.w-e-text p,.w-e-text h1,.w-e-text h2,.w-e-text h3,.w-e-text h4,.w-e-text h5,.w-e-text table,.w-e-text pre {  margin: 10px 0;  line-height: 1.5;}.w-e-text ul,.w-e-text ol {  margin: 10px 0 10px 20px;}.w-e-text blockquote {  display: block;  border-left: 8px solid #d0e5f2;  padding: 5px 10px;  margin: 10px 0;  line-height: 1.4;  font-size: 100%;  background-color: #f1f1f1;}.w-e-text code {  display: inline-block;  *display: inline;  *zoom: 1;  background-color: #f1f1f1;  border-radius: 3px;  padding: 3px 5px;  margin: 0 3px;}.w-e-text pre code {  display: block;}.w-e-text table {  border-top: 1px solid #ccc;  border-left: 1px solid #ccc;}.w-e-text table td,.w-e-text table th {  border-bottom: 1px solid #ccc;  border-right: 1px solid #ccc;  padding: 3px 5px;}.w-e-text table th {  border-bottom: 2px solid #ccc;  text-align: center;}.w-e-text:focus {  outline: none;}.w-e-text img {  cursor: pointer;}.w-e-text img:hover {  box-shadow: 0 0 5px #333;}.w-e-text img.w-e-selected {  border: 2px solid #1e88e5;}.w-e-text img.w-e-selected:hover {  box-shadow: none;}'; | 
|   | 
| // 将 css 代码添加到 <style> 中 | 
|     var style = document.createElement('style'); | 
|     style.type = 'text/css'; | 
|     style.innerHTML = inlinecss; | 
|     document.getElementsByTagName('HEAD').item(0).appendChild(style); | 
|   | 
| // 返回 | 
|     var index = window.wangEditor || Editor; | 
|   | 
|     return index; | 
|   | 
| }))); |