S8;DHvj5vg%NTS2#__lOY-^{y*Vf<6-~5
zao_A=|1|DTd)QxH1BX2Azqjv$^u9h{dhh)Ez)g7llX$0~(!>5|aIf*O|1|D(tNxx{
z|EuG_I{#PKzjycl)%EY~_5Uk-f31i2{}Z@ZdD#DGy0_2oVgI=L{OeJF9rtHF{Q3VM
z+#mI@|5@Dc^05DrbkBajhy8mO>aK2x6!`C7okhO?bBTLj{Ox<;_kauR|0Mob%p4Tw
z{^wV?mlx6hh|xB7%N+NAD{y}wj3V6s&Enmo+>7Y{1MW4s{MFjRTX8$?eQ=EWhi|EW
zfXH_))qh=o|Nk`JwTp3gVg09Z-=X|xtAD{;*aYyW_RH*lXmF|i%kj@p{}EF4PerbO
zQ9S@txYy0*uX23-QK0&tlX$ONth;(&d0zjV{)f+~d#hij`ZsEy4p95FZ1ry{2L|pZ
zp`F^tS}3Rfk!znKW}y+aXa(~>z#O>O#rgjp?iI>^DThnrZ}q;kj$Fh2XGr0n!XIK5
z8c~a;@IQ|KluOsYDe52ZE|L1zeENTuQvWrL_v6algZ>xwu27S{BaxT8i`PGA{#(6Y
zi|_wh%=llR|6!Zn9rCIA|9lUZ*MFh?&*1))zUQEin6J9|^DD$(r~dy-`d>B@z1IJ9
zj{dJfyw{lSF89Ed==dA-|DA_7=mET6@AgFhV}}0Mi}Zh-H=2ReM5zTac;7PR5N0srdE?qNZk?Jc`{-u7RP{
!-{g;ec-CsY%ey$69Flu
ziXv{uYCH0TdERG7;l#LgUTm>ry&bFV7_g(C=#vSdqMzs^2Ap`A|4}{u-HO#J#%Dy%
zxWzs{U#z#kuUK)u_v1VtztJn8d?o%)9!RyykCP`SZ+w@TKNX$4I(c^T?nGxiB(KBa
QjGNzn?2PLc>oKS0e<`?)F#rGn
literal 0
HcmV?d00001
diff --git a/zyplayer-doc-ui/wiki-ui/src/assets/img/expanded.png b/zyplayer-doc-ui/wiki-ui/src/assets/img/expanded.png
new file mode 100644
index 0000000000000000000000000000000000000000..3687303a2364c6f9b2bee6f9799bd47995c19d97
GIT binary patch
literal 331
zcmeAS@N?(olHy`uVBq!ia0vp^GC<7D!3-p$8Dv|56k~CayA#8@b22Z19F}xPUq=Rp
zjs4tz5?O)#gaDrq*Z=?j|NHm*&!0cvzJ2}n^~1-HZ{L4-{qe)gw{M=keEImngL`*x
zUA%GQ;?1k4uU|fL?ZUB3XZD{txog9rEBp6uIJ9GV?d%;ulYoW*Nvt684&Mr(ghWY@
zUocP^Ho$OJSnm~34QGKzWHAE+-(e7DJf6QI1t=Kc>Ealo5nOxTP^dwH$K~P*gCr5Q
zi!*-T4>1l1ShJ_7e(MtEC9bP3P2*b`pql%#rf^=*;X*_0$1mhIKYmfmA?+^nvqST#
k@bQ|vY{wp+k6)!PI8Wm6yFHVD7BMh*y85}Sb4q9e0BHl4egFUf
literal 0
HcmV?d00001
diff --git a/zyplayer-doc-ui/wiki-ui/src/assets/img/zan.png b/zyplayer-doc-ui/wiki-ui/src/assets/img/zan.png
new file mode 100644
index 0000000000000000000000000000000000000000..ac302f3af5c286d251e007fe36c1180594ca34a9
GIT binary patch
literal 433
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMfiU6Mw*Z=?j7a)O}ni?Rls;UY|
zHa9l|NuUUjJ89A+Afu?L2q^OM<;$|NG9Uw}_xtznU%!6+{Q2{T4IEg$ozXn>Vkgr>Co{3urRL*oumZl9Cdju`_4R
z?Ca};NHsJx)YsPoEd|N~ErMA3$<3w{=w{uLAirP+L1UNDw6yZ_j*gkDb_2oPyLaE=
z0)5=7B|weco-U3d5|X6{-ItNWUCaN&{iJ2X
z`pu`kfB0!eRv|t$@;Dq-`nJGR+E<0^#jTHzz4|l`JTo>6xy;n?
zTyk=<4uhYV&$%p)1ySmA0(0-PT&UNdzx_^zl9xBDgKvbLt7?&so}f+QV%Z1n`eO6=
Vy{`C2C@LN)D{2$
literal 0
HcmV?d00001
diff --git a/zyplayer-doc-ui/wiki-ui/src/common/config/apilist.js b/zyplayer-doc-ui/wiki-ui/src/common/config/apilist.js
index 2a0dd0d3..3f10bec6 100644
--- a/zyplayer-doc-ui/wiki-ui/src/common/config/apilist.js
+++ b/zyplayer-doc-ui/wiki-ui/src/common/config/apilist.js
@@ -4,8 +4,16 @@ var URL = {
getUserInfo: '/user/getUserInfo',
pageUpdate: '/zyplayer-doc-wiki/page/update',
pageList: '/zyplayer-doc-wiki/page/list',
+ updatePage: '/zyplayer-doc-wiki/page/update',
+ updateDetail: '/zyplayer-doc-wiki/page/detail',
spaceList: '/zyplayer-doc-wiki/space/list',
updateSpace: '/zyplayer-doc-wiki/space/update',
+
+ updatePageFile: '/zyplayer-doc-wiki/page/file/update',
+ pageCommentList: '/zyplayer-doc-wiki/page/comment/list',
+ updatePageComment: '/zyplayer-doc-wiki/page/comment/update',
+ pageZanList: '/zyplayer-doc-wiki/page/zan/list',
+ updatePageZan: '/zyplayer-doc-wiki/page/zan/update',
};
var URL1 = {};
diff --git a/zyplayer-doc-ui/wiki-ui/src/common/lib/common/common.js b/zyplayer-doc-ui/wiki-ui/src/common/lib/common/common.js
index aa10ca36..1d3e9913 100644
--- a/zyplayer-doc-ui/wiki-ui/src/common/lib/common/common.js
+++ b/zyplayer-doc-ui/wiki-ui/src/common/lib/common/common.js
@@ -19,7 +19,9 @@ export default {
return this.data.accessToken;
},
validateResult: function (res, callback) {
- if (res.data.errCode == 400) {
+ if (!!res.message) {
+ global.vue.$message('请求错误:' + res.message);
+ } else if (res.data.errCode == 400) {
global.vue.$message('请先登录');
window.location = apimix.apilist1.HOST + "/static/manage/login.html";
} else if (res.data.errCode == 402) {
diff --git a/zyplayer-doc-ui/wiki-ui/src/common/lib/wangEditor/fonts/w-e-icon.woff b/zyplayer-doc-ui/wiki-ui/src/common/lib/wangEditor/fonts/w-e-icon.woff
new file mode 100644
index 0000000000000000000000000000000000000000..6dc5b5a33ab856629748dba686540870da73e89f
GIT binary patch
literal 6224
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 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];
+ }
+}
+
+// 记录所有的事件绑定
+var eventList = [];
+
+// 创建构造函数
+function DomElement(selector) {
+ if (!selector) {
+ return;
+ }
+
+ // selector 本来就是 DomElement 对象,直接返回
+ if (selector instanceof DomElement) {
+ return selector;
+ }
+
+ this.selector = selector;
+ var nodeType = selector.nodeType;
+
+ // 根据 selector 得出的结果(如 DOM,DOM List)
+ var selectorResult = [];
+ if (nodeType === 9) {
+ // document 节点
+ selectorResult = [selector];
+ } else if (nodeType === 1) {
+ // 单个 DOM 节点
+ selectorResult = [selector];
+ } else if (isDOMList(selector) || selector instanceof Array) {
+ // DOM List 或者数组
+ selectorResult = selector;
+ } else if (typeof selector === 'string') {
+ // 字符串
+ selector = selector.replace('/\n/mg', '').trim();
+ if (selector.indexOf('<') === 0) {
+ // 如
+ 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;
+ },
+
+ // clone
+ clone: function clone(deep) {
+ var cloneList = [];
+ this.forEach(function (elem) {
+ cloneList.push(elem.cloneNode(!!deep));
+ });
+ return $(cloneList);
+ },
+
+ // 获取第几个元素
+ 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;
+ }
+
+ // 记录下,方便后面解绑
+ eventList.push({
+ elem: elem,
+ type: type,
+ fn: fn
+ });
+
+ if (!selector) {
+ // 无代理
+ elem.addEventListener(type, fn);
+ return;
+ }
+
+ // 有代理
+ elem.addEventListener(type, function (e) {
+ var target = e.target;
+ if (target.matches(selector)) {
+ fn.call(target, e);
+ }
+ });
+ });
+ });
+ },
+
+ // 取消事件绑定
+ off: function off(type, fn) {
+ return this.forEach(function (elem) {
+ elem.removeEventListener(type, fn);
+ });
+ },
+
+ // 获取/设置 属性
+ 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);
+ },
+
+ // 获取子节点(包括文本节点)
+ childNodes: function childNodes() {
+ var elem = this[0];
+ if (!elem) {
+ return null;
+ }
+
+ return $(elem.childNodes);
+ },
+
+ // 增加子节点
+ 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);
+}
+
+// 解绑所有事件,用于销毁编辑器
+$.offAll = function () {
+ eventList.forEach(function (item) {
+ var elem = item.elem;
+ var type = item.type;
+ var fn = item.fn;
+ // 解绑
+ elem.removeEventListener(type, fn);
+ });
+};
+
+/*
+ 配置信息
+*/
+
+var config = {
+
+ // 默认菜单配置
+ menus: ['head', 'bold', 'fontSize', 'fontName', 'italic', 'underline', 'strikeThrough', 'foreColor', 'backColor', 'link', 'list', 'justify', 'quote', 'emoticon', 'image', 'table', 'video', 'code', 'undo', 'redo'],
+
+ fontNames: ['宋体', '微软雅黑', 'Arial', 'Tahoma', 'Verdana'],
+
+ colors: ['#000000', '#eeece0', '#1c487f', '#4d80bf', '#c24f4a', '#8baa4a', '#7b5ba1', '#46acc8', '#f9963b', '#ffffff'],
+
+ // // 语言配置
+ // lang: {
+ // '设置标题': 'title',
+ // '正文': 'p',
+ // '链接文字': 'link text',
+ // '链接': 'link',
+ // '插入': 'insert',
+ // '创建': 'init'
+ // },
+
+ // 表情
+ emotions: [{
+ // tab 的标题
+ title: '默认',
+ // type -> 'emoji' / 'image'
+ type: 'image',
+ // content -> 数组
+ content: [{
+ alt: '[坏笑]',
+ src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/50/pcmoren_huaixiao_org.png'
+ }, {
+ alt: '[舔屏]',
+ src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/40/pcmoren_tian_org.png'
+ }, {
+ alt: '[污]',
+ src: 'http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/3c/pcmoren_wu_org.png'
+ }]
+ }, {
+ // tab 的标题
+ title: '新浪',
+ // type -> 'emoji' / 'image'
+ type: 'image',
+ // content -> 数组
+ content: [{
+ src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/7a/shenshou_thumb.gif',
+ alt: '[草泥马]'
+ }, {
+ src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/60/horse2_thumb.gif',
+ alt: '[神马]'
+ }, {
+ src: 'http://img.t.sinajs.cn/t35/style/images/common/face/ext/normal/bc/fuyun_thumb.gif',
+ alt: '[浮云]'
+ }]
+ }, {
+ // tab 的标题
+ title: 'emoji',
+ // type -> 'emoji' / 'image'
+ type: 'emoji',
+ // content -> 数组
+ content: '😀 😃 😄 😁 😆 😅 😂 😊 😇 🙂 🙃 😉 😓 😪 😴 🙄 🤔 😬 🤐'.split(/\s/)
+ }],
+
+ // 编辑区域的 z-index
+ zIndex: 10000,
+
+ // 是否开启 debug 模式(debug 模式下错误会 throw error 形式抛出)
+ debug: false,
+
+ // 插入链接时候的格式校验
+ linkCheck: function linkCheck(text, link) {
+ // text 是插入的文字
+ // link 是插入的链接
+ return true; // 返回 true 即表示成功
+ // return '校验失败' // 返回字符串即表示失败的提示信息
+ },
+
+ // 插入网络图片的校验
+ linkImgCheck: function linkImgCheck(src) {
+ // src 即图片的地址
+ return true; // 返回 true 即表示成功
+ // return '校验失败' // 返回字符串即表示失败的提示信息
+ },
+
+ // 粘贴过滤样式,默认开启
+ pasteFilterStyle: true,
+
+ // 粘贴内容时,忽略图片。默认关闭
+ pasteIgnoreImg: false,
+
+ // 对粘贴的文字进行自定义处理,返回处理后的结果。编辑器会将处理后的结果粘贴到编辑区域中。
+ // IE 暂时不支持
+ pasteTextHandle: function pasteTextHandle(content) {
+ // content 即粘贴过来的内容(html 或 纯文本),可进行自定义处理然后返回
+ return content;
+ },
+
+ // onchange 事件
+ // onchange: function (html) {
+ // // html 即变化之后的内容
+ // console.log(html)
+ // },
+
+ // 是否显示添加网络图片的 tab
+ showLinkImg: true,
+
+ // 插入网络图片的回调
+ linkImgCallback: function linkImgCallback(url) {
+ // console.log(url) // url 即插入图片的地址
+ },
+
+ // 默认上传图片 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: 10000,
+
+ // 上传图片 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) {
+ // 图片上传之前触发
+
+ // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
+ // return {
+ // prevent: true,
+ // msg: '放弃上传'
+ // }
+ },
+ success: function success(xhr, editor, result) {
+ // 图片上传并返回结果,图片插入成功之后触发
+ },
+ fail: function fail(xhr, editor, result) {
+ // 图片上传并返回结果,但图片插入错误时触发
+ },
+ error: function error(xhr, editor) {
+ // 图片上传出错时触发
+ },
+ timeout: function timeout(xhr, editor) {
+ // 图片上传超时时触发
+ }
+ },
+
+ // 是否上传七牛云,默认为 false
+ qiniu: false
+
+};
+
+/*
+ 工具
+*/
+
+// 和 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(/(\r\n|\r|\n)/g, '
');
+}
+
+// 返回百分比的格式
+
+
+// 判断是不是 function
+function isFunction(fn) {
+ return typeof fn === 'function';
+}
+
+/*
+ bold-menu
+*/
+// 构造函数
+function Bold(editor) {
+ this.editor = editor;
+ this.$elem = $('');
+ 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');
+ }
+ }
+};
+
+/*
+ 替换多语言
+ */
+
+var replaceLang = function (editor, str) {
+ var langArgs = editor.config.langArgs || [];
+ var result = str;
+
+ langArgs.forEach(function (item) {
+ var reg = item.reg;
+ var val = item.val;
+
+ if (reg.test(result)) {
+ result = result.replace(reg, function () {
+ return val;
+ });
+ }
+ });
+
+ return result;
+};
+
+/*
+ droplist
+*/
+var _emptyFn = function _emptyFn() {};
+
+// 构造函数
+function DropList(menu, opt) {
+ var _this = this;
+
+ // droplist 所依附的菜单
+ var editor = menu.editor;
+ this.menu = menu;
+ this.opt = opt;
+ // 容器
+ var $container = $('
');
+
+ // 标题
+ var $title = opt.$title;
+ var titleHtml = void 0;
+ if ($title) {
+ // 替换多语言
+ titleHtml = $title.html();
+ titleHtml = replaceLang(editor, titleHtml);
+ $title.html(titleHtml);
+
+ $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 = $('
');
+ $container.append($list);
+ list.forEach(function (item) {
+ var $elem = item.$elem;
+
+ // 替换多语言
+ var elemHtml = $elem.html();
+ elemHtml = replaceLang(editor, elemHtml);
+ $elem.html(elemHtml);
+
+ var value = item.value;
+ var $li = $('
');
+ if ($elem) {
+ $li.append($elem);
+ $list.append($li);
+ $li.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 = $('');
+ this.type = 'droplist';
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 100,
+ $title: $('
设置标题
'),
+ type: 'list', // droplist 以列表形式展示
+ list: [{ $elem: $('
H1
'), value: '
' }, { $elem: $('H2
'), value: '' }, { $elem: $('H3
'), value: '' }, { $elem: $('H4
'), value: '' }, { $elem: $('H5
'), value: '' }, { $elem: $('
正文
'), value: '
' }],
+ onClick: function onClick(value) {
+ // 注意 this 是指向当前的 Head 对象
+ _this._command(value);
+ }
+ });
+}
+
+// 原型
+Head.prototype = {
+ constructor: Head,
+
+ // 执行命令
+ _command: function _command(value) {
+ var editor = this.editor;
+
+ var $selectionElem = editor.selection.getSelectionContainerElem();
+ if (editor.$textElem.equal($selectionElem)) {
+ // 不能选中多行来设置标题,否则会出现问题
+ // 例如选中的是
xxx
yyy
来设置标题,设置之后会成为
xxx
yyy
不符合预期
+ return;
+ }
+
+ 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');
+ }
+ }
+};
+
+/*
+ menu - fontSize
+*/
+
+// 构造函数
+function FontSize(editor) {
+ var _this = this;
+
+ this.editor = editor;
+ this.$elem = $('');
+ this.type = 'droplist';
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 160,
+ $title: $('
字号
'),
+ type: 'list', // droplist 以列表形式展示
+ list: [{ $elem: $('
x-small'), value: '1' }, { $elem: $('
small'), value: '2' }, { $elem: $('
normal'), value: '3' }, { $elem: $('
large'), value: '4' }, { $elem: $('
x-large'), value: '5' }, { $elem: $('
xx-large'), value: '6' }],
+ onClick: function onClick(value) {
+ // 注意 this 是指向当前的 FontSize 对象
+ _this._command(value);
+ }
+ });
+}
+
+// 原型
+FontSize.prototype = {
+ constructor: FontSize,
+
+ // 执行命令
+ _command: function _command(value) {
+ var editor = this.editor;
+ editor.cmd.do('fontSize', value);
+ }
+};
+
+/*
+ menu - fontName
+*/
+
+// 构造函数
+function FontName(editor) {
+ var _this = this;
+
+ this.editor = editor;
+ this.$elem = $('');
+ this.type = 'droplist';
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 获取配置的字体
+ var config = editor.config;
+ var fontNames = config.fontNames || [];
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 100,
+ $title: $('
字体
'),
+ type: 'list', // droplist 以列表形式展示
+ list: fontNames.map(function (fontName) {
+ return { $elem: $('
' + fontName + ''), value: fontName };
+ }),
+ onClick: function onClick(value) {
+ // 注意 this 是指向当前的 FontName 对象
+ _this._command(value);
+ }
+ });
+}
+
+// 原型
+FontName.prototype = {
+ constructor: FontName,
+
+ _command: function _command(value) {
+ var editor = this.editor;
+ editor.cmd.do('fontName', value);
+ }
+};
+
+/*
+ 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 = $('
');
+ var width = opt.width || 300; // 默认 300px
+ $container.css('width', width + 'px').css('margin-left', (0 - width) / 2 + 'px');
+
+ // 添加关闭按钮
+ var $closeBtn = $('
');
+ $container.append($closeBtn);
+ $closeBtn.on('click', function () {
+ _this.hide();
+ });
+
+ // 准备 tabs 容器
+ var $tabTitleContainer = $('
');
+ var $tabContentContainer = $('
');
+ $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 || '';
+
+ // 替换多语言
+ title = replaceLang(editor, title);
+ tpl = replaceLang(editor, tpl);
+
+ // 添加到 DOM
+ var $title = $('
' + title + '');
+ $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 = $('');
+ 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: '
',
+ // 事件绑定
+ 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', '
' + selectionText + '');
+ },
+
+ // 插入链接
+ _insertLink: function _insertLink(text, link) {
+ var editor = this.editor;
+ var config = editor.config;
+ var linkCheck = config.linkCheck;
+ var checkResult = true; // 默认为 true
+ if (linkCheck && typeof linkCheck === 'function') {
+ checkResult = linkCheck(text, link);
+ }
+ if (checkResult === true) {
+ editor.cmd.do('insertHTML', '
' + text + '');
+ } else {
+ alert(checkResult);
+ }
+ },
+
+ // 试图改变 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 = $('');
+ 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 = $('');
+ 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 = $('');
+ 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 = $('');
+ 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 = $('');
+ 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 = $('');
+ this.type = 'droplist';
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 120,
+ $title: $('
设置列表
'),
+ type: 'list', // droplist 以列表形式展示
+ list: [{ $elem: $('
有序列表'), value: 'insertOrderedList' }, { $elem: $('
无序列表'), 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);
+
+ // 验证列表是否被包裹在
之内
+ 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)) {
+ // 证明是顶级标签,没有被
包裹
+ 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 = $('
');
+ this.type = 'droplist';
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 100,
+ $title: $('
对齐方式
'),
+ type: 'list', // droplist 以列表形式展示
+ list: [{ $elem: $('
靠左'), value: 'justifyLeft' }, { $elem: $('
居中'), value: 'justifyCenter' }, { $elem: $('
靠右'), 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 - Forecolor
+*/
+// 构造函数
+function ForeColor(editor) {
+ var _this = this;
+
+ this.editor = editor;
+ this.$elem = $('');
+ this.type = 'droplist';
+
+ // 获取配置的颜色
+ var config = editor.config;
+ var colors = config.colors || [];
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 120,
+ $title: $('
文字颜色
'),
+ type: 'inline-block', // droplist 内容以 block 形式展示
+ list: colors.map(function (color) {
+ return { $elem: $('
'), value: color };
+ }),
+ onClick: function onClick(value) {
+ // 注意 this 是指向当前的 ForeColor 对象
+ _this._command(value);
+ }
+ });
+}
+
+// 原型
+ForeColor.prototype = {
+ constructor: ForeColor,
+
+ // 执行命令
+ _command: function _command(value) {
+ var editor = this.editor;
+ editor.cmd.do('foreColor', value);
+ }
+};
+
+/*
+ menu - BackColor
+*/
+// 构造函数
+function BackColor(editor) {
+ var _this = this;
+
+ this.editor = editor;
+ this.$elem = $('');
+ this.type = 'droplist';
+
+ // 获取配置的颜色
+ var config = editor.config;
+ var colors = config.colors || [];
+
+ // 当前是否 active 状态
+ this._active = false;
+
+ // 初始化 droplist
+ this.droplist = new DropList(this, {
+ width: 120,
+ $title: $('
背景色
'),
+ type: 'inline-block', // droplist 内容以 block 形式展示
+ list: colors.map(function (color) {
+ return { $elem: $('
'), value: color };
+ }),
+ onClick: function onClick(value) {
+ // 注意 this 是指向当前的 BackColor 对象
+ _this._command(value);
+ }
+ });
+}
+
+// 原型
+BackColor.prototype = {
+ constructor: BackColor,
+
+ // 执行命令
+ _command: function _command(value) {
+ var editor = this.editor;
+ editor.cmd.do('backColor', value);
+ }
+};
+
+/*
+ menu - quote
+*/
+// 构造函数
+function Quote(editor) {
+ this.editor = editor;
+ this.$elem = $('');
+ this.type = 'click';
+
+ // 当前是否 active 状态
+ this._active = false;
+}
+
+// 原型
+Quote.prototype = {
+ constructor: Quote,
+
+ onClick: function onClick(e) {
+ var editor = this.editor;
+ var $selectionElem = editor.selection.getSelectionContainerElem();
+ var nodeName = $selectionElem.getNodeName();
+
+ if (!UA.isIE()) {
+ if (nodeName === 'BLOCKQUOTE') {
+ // 撤销 quote
+ editor.cmd.do('formatBlock', '
');
+ } else {
+ // 转换为 quote
+ editor.cmd.do('formatBlock', '
');
+ }
+ return;
+ }
+
+ // IE 中不支持 formatBlock ,要用其他方式兼容
+ var content = void 0,
+ $targetELem = void 0;
+ if (nodeName === 'P') {
+ // 将 P 转换为 quote
+ content = $selectionElem.text();
+ $targetELem = $('' + content + '
');
+ $targetELem.insertAfter($selectionElem);
+ $selectionElem.remove();
+ return;
+ }
+ if (nodeName === 'BLOCKQUOTE') {
+ // 撤销 quote
+ content = $selectionElem.text();
+ $targetELem = $('' + content + '
');
+ $targetELem.insertAfter($selectionElem);
+ $selectionElem.remove();
+ }
+ },
+
+ 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 = $('');
+ 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 = $('' + selectionText + '');
+ editor.cmd.do('insertElem', $code);
+ editor.selection.createRangeByElem($code, false);
+ editor.selection.restoreSelection();
+ return;
+ }
+
+ // 选取是空,且没有夸元素选择,则插入
+ 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: '\n
\n
\n \n
\n
',
+ // 事件绑定
+ 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', '
' + value + '
');
+ },
+
+ // 更新代码
+ _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 = $('');
+ this.type = 'panel';
+
+ // 当前是否 active 状态
+ this._active = false;
+}
+
+// 原型
+Emoticon.prototype = {
+ constructor: Emoticon,
+
+ onClick: function onClick() {
+ this._createPanel();
+ },
+
+ _createPanel: function _createPanel() {
+ var _this = this;
+
+ var editor = this.editor;
+ var config = editor.config;
+ // 获取表情配置
+ var emotions = config.emotions || [];
+
+ // 创建表情 dropPanel 的配置
+ var tabConfig = [];
+ emotions.forEach(function (emotData) {
+ var emotType = emotData.type;
+ var content = emotData.content || [];
+
+ // 这一组表情最终拼接出来的 html
+ var faceHtml = '';
+
+ // emoji 表情
+ if (emotType === 'emoji') {
+ content.forEach(function (item) {
+ if (item) {
+ faceHtml += '
' + item + '';
+ }
+ });
+ }
+ // 图片表情
+ if (emotType === 'image') {
+ content.forEach(function (item) {
+ var src = item.src;
+ var alt = item.alt;
+ if (src) {
+ // 加一个 data-w-e 属性,点击图片的时候不再提示编辑图片
+ faceHtml += '

';
+ }
+ });
+ }
+
+ tabConfig.push({
+ title: emotData.title,
+ tpl: '
' + faceHtml + '
',
+ events: [{
+ selector: 'span.w-e-item',
+ type: 'click',
+ fn: function fn(e) {
+ var target = e.target;
+ var $target = $(target);
+ var nodeName = $target.getNodeName();
+
+ var insertHtml = void 0;
+ if (nodeName === 'IMG') {
+ // 插入图片
+ insertHtml = $target.parent().html();
+ } else {
+ // 插入 emoji
+ insertHtml = '
' + $target.html() + '';
+ }
+
+ _this._insert(insertHtml);
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
+ return true;
+ }
+ }]
+ });
+ });
+
+ var panel = new Panel(this, {
+ width: 300,
+ height: 200,
+ // 一个 Panel 包含多个 tab
+ tabs: tabConfig
+ });
+
+ // 显示 panel
+ panel.show();
+
+ // 记录属性
+ this.panel = panel;
+ },
+
+ // 插入表情
+ _insert: function _insert(emotHtml) {
+ var editor = this.editor;
+ editor.cmd.do('insertHTML', emotHtml);
+ }
+};
+
+/*
+ menu - table
+*/
+// 构造函数
+function Table(editor) {
+ this.editor = editor;
+ this.$elem = $('');
+ 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: '
',
+ // 事件绑定
+ 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 = '
';
+ for (r = 0; r < rowNum; r++) {
+ html += '';
+ if (r === 0) {
+ for (c = 0; c < colNum; c++) {
+ html += '| | ';
+ }
+ } else {
+ for (c = 0; c < colNum; c++) {
+ html += ' | ';
+ }
+ }
+ html += '
';
+ }
+ html += '
';
+
+ // 执行命令
+ 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: '编辑表格',
+ // 模板
+ tpl: '
\n
\n \n \n \n \n
\n
\n \n \n
',
+ // 事件绑定
+ 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 += '
| ';
+ }
+ 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 = $('');
+ 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: '
\n
\n
\n \n
\n
',
+ // 事件绑定
+ events: [{
+ selector: '#' + btnId,
+ type: 'click',
+ fn: function fn() {
+ var $text = $('#' + textValId);
+ var val = $text.val().trim();
+
+ // 测试用视频地址
+ //
+
+ 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 + '
');
+ }
+};
+
+/*
+ menu - img
+*/
+// 构造函数
+function Image(editor) {
+ this.editor = editor;
+ var imgMenuId = getRandom('w-e-img');
+ this.$elem = $('');
+ editor.imgMenuId = imgMenuId;
+ this.type = 'panel';
+
+ // 当前是否 active 状态
+ this._active = false;
+}
+
+// 原型
+Image.prototype = {
+ constructor: Image,
+
+ onClick: function onClick() {
+ var editor = this.editor;
+ var config = editor.config;
+ if (config.qiniu) {
+ return;
+ }
+ 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: '编辑图片',
+ tpl: '
\n
\n \u6700\u5927\u5BBD\u5EA6\uFF1A\n \n \n \n
\n
\n \n \n
',
+ 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: '
',
+ 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: '
\n
\n
\n \n
\n
',
+ 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 || config.customUploadImg) && 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.fontSize = FontSize;
+
+MenuConstructors.fontName = FontName;
+
+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 = ForeColor;
+
+MenuConstructors.backColor = BackColor;
+
+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;
+ var config = editor.config;
+ // config.zIndex 是配置的编辑区域的 z-index,菜单的 z-index 得在其基础上 +1
+ var zIndex = config.zIndex + 1;
+ objForEach(menus, function (key, menu) {
+ var $elem = menu.$elem;
+ if ($elem) {
+ // 设置 z-index
+ $elem.css('z-index', zIndex);
+ $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, filterStyle, ignoreImg) {
+ 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 = '
' + replaceHtmlSymbol(pasteText) + '
';
+ }
+ if (!pasteHtml) {
+ return;
+ }
+
+ // 过滤word中状态过来的无用字符
+ var docSplitHtml = pasteHtml.split('