• 对wangeditor进行扩展---- 源代码(上)


    看到有人对我做的WangEditor比较感兴趣,问了一些问题。但由于我并不常来,所以就没能及时答复,抱歉了。

    未避免以后类似问题发生,我将我修改的wangeditor.js直接发在这里,有兴趣的可以下载后自己分析。希望能帮到需要的人。

    扩展后的界面如下:

    全部代码5525行。

    1. (function (global, factory) {
    2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    3. typeof define === 'function' && define.amd ? define(factory) :
    4. (global.wangEditor = factory());
    5. }(this, (function () { 'use strict';
    6. /*
    7. poly-fill
    8. */
    9. var polyfill = function () {
    10. // Object.assign
    11. if (typeof Object.assign != 'function') {
    12. Object.assign = function (target, varArgs) {
    13. // .length of function is 2
    14. if (target == null) {
    15. // TypeError if undefined or null
    16. throw new TypeError('Cannot convert undefined or null to object');
    17. }
    18. var to = Object(target);
    19. for (var index = 1; index < arguments.length; index++) {
    20. var nextSource = arguments[index];
    21. if (nextSource != null) {
    22. // Skip over if undefined or null
    23. for (var nextKey in nextSource) {
    24. // Avoid bugs when hasOwnProperty is shadowed
    25. if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
    26. to[nextKey] = nextSource[nextKey];
    27. }
    28. }
    29. }
    30. }
    31. return to;
    32. };
    33. }
    34. // IE 中兼容 Element.prototype.matches
    35. if (!Element.prototype.matches) {
    36. Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) {
    37. var matches = (this.document || this.ownerDocument).querySelectorAll(s),
    38. i = matches.length;
    39. while (--i >= 0 && matches.item(i) !== this) {}
    40. return i > -1;
    41. };
    42. }
    43. };
    44. /*
    45. DOM 操作 API
    46. */
    47. // 根据 html 代码片段创建 dom 对象
    48. function createElemByHTML(html) {
    49. var div = void 0;
    50. div = document.createElement('div');
    51. div.innerHTML = html;
    52. return div.children;
    53. }
    54. // 是否是 DOM List
    55. function isDOMList(selector) {
    56. if (!selector) {
    57. return false;
    58. }
    59. if (selector instanceof HTMLCollection || selector instanceof NodeList) {
    60. return true;
    61. }
    62. return false;
    63. }
    64. // 封装 document.querySelectorAll
    65. function querySelectorAll(selector) {
    66. var result = document.querySelectorAll(selector);
    67. if (isDOMList(result)) {
    68. return result;
    69. } else {
    70. return [result];
    71. }
    72. }
    73. // 记录所有的事件绑定
    74. var eventList = [];
    75. // 创建构造函数
    76. function DomElement(selector) {
    77. if (!selector) {
    78. return;
    79. }
    80. // selector 本来就是 DomElement 对象,直接返回
    81. if (selector instanceof DomElement) {
    82. return selector;
    83. }
    84. this.selector = selector;
    85. var nodeType = selector.nodeType;
    86. // 根据 selector 得出的结果(如 DOM,DOM List)
    87. var selectorResult = [];
    88. if (nodeType === 9) {
    89. // document 节点
    90. selectorResult = [selector];
    91. } else if (nodeType === 1) {
    92. // 单个 DOM 节点
    93. selectorResult = [selector];
    94. } else if (isDOMList(selector) || selector instanceof Array) {
    95. // DOM List 或者数组
    96. selectorResult = selector;
    97. } else if (typeof selector === 'string') {
    98. // 字符串
    99. selector = selector.replace('/\n/mg', '').trim();
    100. if (selector.indexOf('<') === 0) {
    101. // 如
    102. selectorResult = createElemByHTML(selector);
    103. } else {
    104. // 如 #id .class
    105. selectorResult = querySelectorAll(selector);
    106. }
    107. }
    108. var length = selectorResult.length;
    109. if (!length) {
    110. // 空数组
    111. return this;
    112. }
    113. // 加入 DOM 节点
    114. var i = void 0;
    115. for (i = 0; i < length; i++) {
    116. this[i] = selectorResult[i];
    117. }
    118. this.length = length;
    119. }
    120. // 修改原型
    121. DomElement.prototype = {
    122. constructor: DomElement,
    123. // 类数组,forEach
    124. forEach: function forEach(fn) {
    125. var i = void 0;
    126. for (i = 0; i < this.length; i++) {
    127. var elem = this[i];
    128. var result = fn.call(elem, elem, i);
    129. if (result === false) {
    130. break;
    131. }
    132. }
    133. return this;
    134. },
    135. // clone
    136. clone: function clone(deep) {
    137. var cloneList = [];
    138. this.forEach(function (elem) {
    139. cloneList.push(elem.cloneNode(!!deep));
    140. });
    141. return $(cloneList);
    142. },
    143. // 获取第几个元素
    144. get: function get(index) {
    145. var length = this.length;
    146. if (index >= length) {
    147. index = index % length;
    148. }
    149. return $(this[index]);
    150. },
    151. // 第一个
    152. first: function first() {
    153. return this.get(0);
    154. },
    155. // 最后一个
    156. last: function last() {
    157. var length = this.length;
    158. return this.get(length - 1);
    159. },
    160. // 绑定事件
    161. on: function on(type, selector, fn) {
    162. // selector 不为空,证明绑定事件要加代理
    163. if (!fn) {
    164. fn = selector;
    165. selector = null;
    166. }
    167. // type 是否有多个
    168. var types = [];
    169. types = type.split(/\s+/);
    170. return this.forEach(function (elem) {
    171. types.forEach(function (type) {
    172. if (!type) {
    173. return;
    174. }
    175. // 记录下,方便后面解绑
    176. eventList.push({
    177. elem: elem,
    178. type: type,
    179. fn: fn
    180. });
    181. if (!selector) {
    182. // 无代理
    183. elem.addEventListener(type, fn);
    184. return;
    185. }
    186. // 有代理
    187. elem.addEventListener(type, function (e) {
    188. var target = e.target;
    189. if (target.matches(selector)) {
    190. fn.call(target, e);
    191. }
    192. });
    193. });
    194. });
    195. },
    196. // 取消事件绑定
    197. off: function off(type, fn) {
    198. return this.forEach(function (elem) {
    199. elem.removeEventListener(type, fn);
    200. });
    201. },
    202. // 获取/设置 属性
    203. attr: function attr(key, val) {
    204. if (val == null) {
    205. // 获取值
    206. return this[0].getAttribute(key);
    207. } else {
    208. // 设置值
    209. return this.forEach(function (elem) {
    210. elem.setAttribute(key, val);
    211. });
    212. }
    213. },
    214. // 添加 class
    215. addClass: function addClass(className) {
    216. if (!className) {
    217. return this;
    218. }
    219. return this.forEach(function (elem) {
    220. var arr = void 0;
    221. if (elem.className) {
    222. // 解析当前 className 转换为数组
    223. arr = elem.className.split(/\s/);
    224. arr = arr.filter(function (item) {
    225. return !!item.trim();
    226. });
    227. // 添加 class
    228. if (arr.indexOf(className) < 0) {
    229. arr.push(className);
    230. }
    231. // 修改 elem.class
    232. elem.className = arr.join(' ');
    233. } else {
    234. elem.className = className;
    235. }
    236. });
    237. },
    238. // 删除 class
    239. removeClass: function removeClass(className) {
    240. if (!className) {
    241. return this;
    242. }
    243. return this.forEach(function (elem) {
    244. var arr = void 0;
    245. if (elem.className) {
    246. // 解析当前 className 转换为数组
    247. arr = elem.className.split(/\s/);
    248. arr = arr.filter(function (item) {
    249. item = item.trim();
    250. // 删除 class
    251. if (!item || item === className) {
    252. return false;
    253. }
    254. return true;
    255. });
    256. // 修改 elem.class
    257. elem.className = arr.join(' ');
    258. }
    259. });
    260. },
    261. // 修改 css
    262. css: function css(key, val) {
    263. var currentStyle = key + ':' + val + ';';
    264. return this.forEach(function (elem) {
    265. var style = (elem.getAttribute('style') || '').trim();
    266. var styleArr = void 0,
    267. resultArr = [];
    268. if (style) {
    269. // 将 style 按照 ; 拆分为数组
    270. styleArr = style.split(';');
    271. styleArr.forEach(function (item) {
    272. // 对每项样式,按照 : 拆分为 key 和 value
    273. var arr = item.split(':').map(function (i) {
    274. return i.trim();
    275. });
    276. if (arr.length === 2) {
    277. resultArr.push(arr[0] + ':' + arr[1]);
    278. }
    279. });
    280. // 替换或者新增
    281. resultArr = resultArr.map(function (item) {
    282. if (item.indexOf(key) === 0) {
    283. return currentStyle;
    284. } else {
    285. return item;
    286. }
    287. });
    288. if (resultArr.indexOf(currentStyle) < 0) {
    289. resultArr.push(currentStyle);
    290. }
    291. // 结果
    292. elem.setAttribute('style', resultArr.join('; '));
    293. } else {
    294. // style 无值
    295. elem.setAttribute('style', currentStyle);
    296. }
    297. });
    298. },
    299. // 显示
    300. show: function show() {
    301. return this.css('display', 'block');
    302. },
    303. // 隐藏
    304. hide: function hide() {
    305. return this.css('display', 'none');
    306. },
    307. // 获取子节点
    308. children: function children() {
    309. var elem = this[0];
    310. if (!elem) {
    311. return null;
    312. }
    313. return $(elem.children);
    314. },
    315. // 获取子节点(包括文本节点)
    316. childNodes: function childNodes() {
    317. var elem = this[0];
    318. if (!elem) {
    319. return null;
    320. }
    321. return $(elem.childNodes);
    322. },
    323. // 增加子节点
    324. append: function append($children) {
    325. return this.forEach(function (elem) {
    326. $children.forEach(function (child) {
    327. elem.appendChild(child);
    328. });
    329. });
    330. },
    331. // 移除当前节点
    332. remove: function remove() {
    333. return this.forEach(function (elem) {
    334. if (elem.remove) {
    335. elem.remove();
    336. } else {
    337. var parent = elem.parentElement;
    338. parent && parent.removeChild(elem);
    339. }
    340. });
    341. },
    342. // 是否包含某个子节点
    343. isContain: function isContain($child) {
    344. var elem = this[0];
    345. var child = $child[0];
    346. return elem.contains(child);
    347. },
    348. // 尺寸数据
    349. getSizeData: function getSizeData() {
    350. var elem = this[0];
    351. return elem.getBoundingClientRect(); // 可得到 bottom height left right top width 的数据
    352. },
    353. // 封装 nodeName
    354. getNodeName: function getNodeName() {
    355. var elem = this[0];
    356. return elem.nodeName;
    357. },
    358. // 从当前元素查找
    359. find: function find(selector) {
    360. var elem = this[0];
    361. return $(elem.querySelectorAll(selector));
    362. },
    363. // 获取当前元素的 text
    364. text: function text(val) {
    365. if (!val) {
    366. // 获取 text
    367. var elem = this[0];
    368. return elem.innerHTML.replace(/<.*?>/g, function () {
    369. return '';
    370. });
    371. } else {
    372. // 设置 text
    373. return this.forEach(function (elem) {
    374. elem.innerHTML = val;
    375. });
    376. }
    377. },
    378. // 获取 html
    379. html: function html(value) {
    380. var elem = this[0];
    381. if (value == null) {
    382. return elem.innerHTML;
    383. } else {
    384. elem.innerHTML = value;
    385. return this;
    386. }
    387. },
    388. // 获取 value
    389. val: function val() {
    390. var elem = this[0];
    391. return elem.value.trim();
    392. },
    393. // focus
    394. focus: function focus() {
    395. return this.forEach(function (elem) {
    396. elem.focus();
    397. });
    398. },
    399. // parent
    400. parent: function parent() {
    401. var elem = this[0];
    402. return $(elem.parentElement);
    403. },
    404. // parentUntil 找到符合 selector 的父节点
    405. parentUntil: function parentUntil(selector, _currentElem) {
    406. var results = document.querySelectorAll(selector);
    407. var length = results.length;
    408. if (!length) {
    409. // 传入的 selector 无效
    410. return null;
    411. }
    412. var elem = _currentElem || this[0];
    413. if (elem.nodeName === 'BODY') {
    414. return null;
    415. }
    416. var parent = elem.parentElement;
    417. var i = void 0;
    418. for (i = 0; i < length; i++) {
    419. if (parent === results[i]) {
    420. // 找到,并返回
    421. return $(parent);
    422. }
    423. }
    424. // 继续查找
    425. return this.parentUntil(selector, parent);
    426. },
    427. // 判断两个 elem 是否相等
    428. equal: function equal($elem) {
    429. if ($elem.nodeType === 1) {
    430. return this[0] === $elem;
    431. } else {
    432. return this[0] === $elem[0];
    433. }
    434. },
    435. // 将该元素插入到某个元素前面
    436. insertBefore: function insertBefore(selector) {
    437. var $referenceNode = $(selector);
    438. var referenceNode = $referenceNode[0];
    439. if (!referenceNode) {
    440. return this;
    441. }
    442. return this.forEach(function (elem) {
    443. var parent = referenceNode.parentNode;
    444. parent.insertBefore(elem, referenceNode);
    445. });
    446. },
    447. // 将该元素插入到某个元素后面
    448. insertAfter: function insertAfter(selector) {
    449. var $referenceNode = $(selector);
    450. var referenceNode = $referenceNode[0];
    451. if (!referenceNode) {
    452. return this;
    453. }
    454. return this.forEach(function (elem) {
    455. var parent = referenceNode.parentNode;
    456. if (parent.lastChild === referenceNode) {
    457. // 最后一个元素
    458. parent.appendChild(elem);
    459. } else {
    460. // 不是最后一个元素
    461. parent.insertBefore(elem, referenceNode.nextSibling);
    462. }
    463. });
    464. }
    465. };
    466. // new 一个对象
    467. function $(selector) {
    468. return new DomElement(selector);
    469. }
    470. // 解绑所有事件,用于销毁编辑器
    471. $.offAll = function () {
    472. eventList.forEach(function (item) {
    473. var elem = item.elem;
    474. var type = item.type;
    475. var fn = item.fn;
    476. // 解绑
    477. elem.removeEventListener(type, fn);
    478. });
    479. };
    480. /*
    481. 配置信息
    482. */
    483. var config = {
    484. // 默认菜单配置 (: 修改菜单顺序)
    485. menus: [ 'bold','italic', 'underline', 'underdot', 'strikeThrough','Sup','Sub','Middle','HRline', 'head', 'fontSize', 'fontName', 'foreColor', 'backColor',
    486. 'list', 'justify', 'emoticon', 'image', 'table','mathsymbol', 'Latex', 'Bracket','Fraction','Sqrt','Answer','Folder', 'link', 'video','undo', 'redo',
    487. 'a01', 'a02', 'a03', 'a04', 'a05', 'a06', 'a07', 'a08', 'a09', 'a10', 'a11','a12','a13','a14','a15','a16','a17','a18','a19','a20','a21','a22','a23','a24'],
    488. fontNames: ['宋体', '微软雅黑', 'Arial', 'Tahoma', 'Verdana'],
    489. colors: ['#000000', '#666666','#cccccc','#ffffff' , '#ff0000', '#ff7e72', '#005500', '#00ff00', '#00007f', '#0000ff', '#ffff00', '#aa007f'],
    490. // // 语言配置
    491. // lang: {
    492. // '设置标题': 'title',
    493. // '正文': 'p',
    494. // '链接文字': 'link text',
    495. // '链接': 'link',
    496. // '插入': 'insert',
    497. // '创建': 'init'
    498. // },
    499. // 表情
    500. emotions: [{
    501. title: '数学',
    502. type: 'image',
    503. content: [ //文件名对应的图像不能改变!!!!!!!!!!
    504. {"src": "/static/png/math/01.png","alt": "1/2",},
    505. {"src": "/static/png/math/10.png","alt": "√2",},
    506. {"src": "/static/png/math/11.png","alt": "√3",},
    507. {"src": "/static/png/math/12.png","alt": "√5",},
    508. {"src": "/static/png/math/13.png","alt": "√6",},
    509. {"src": "/static/png/math/14.png","alt": "求根公式",},
    510. {"src": "/static/svg/1_2.svg","alt": "1/2",},
    511. ]
    512. },{
    513. title: 'emoji',
    514. type: 'emoji',
    515. content: '😀 😃 😄 😁 😆 😅 😂 😊 😇 🙂 🙃 😉 😓 😪 😴 🙄 🤔 😬 🤐'.split(/\s/)
    516. },{
    517. title: '动图',
    518. type: 'image',
    519. content: [
    520. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/0.gif', alt: '[微笑]' },
    521. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/1.gif', alt: '[撇嘴]' },
    522. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/2.gif', alt: '[色]' },
    523. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/3.gif', alt: '[发呆]' },
    524. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/4.gif', alt: '[得意]' },
    525. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/5.gif', alt: '[流泪]' },
    526. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/6.gif', alt: '[害羞]' },
    527. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/7.gif', alt: '[闭嘴]' },
    528. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/8.gif', alt: '[睡]' },
    529. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/9.gif', alt: '[大哭]' },
    530. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/10.gif', alt: '[尴尬]' },
    531. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/11.gif', alt: '[发怒]' },
    532. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/12.gif', alt: '[调皮]' },
    533. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/13.gif', alt: '[呲牙]' },
    534. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/14.gif', alt: '[惊讶]' },
    535. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/15.gif', alt: '[难过]' },
    536. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/16.gif', alt: '[酷]' },
    537. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/17.gif', alt: '[冷汗]' },
    538. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/18.gif', alt: '[抓狂]' },
    539. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/19.gif', alt: '[吐]' },
    540. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/20.gif', alt: '[偷笑]' },
    541. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/21.gif', alt: '[愉快]' },
    542. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/22.gif', alt: '[白眼]' },
    543. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/23.gif', alt: '[傲慢]' },
    544. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/24.gif', alt: '[饥饿]' },
    545. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/25.gif', alt: '[困]' },
    546. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/26.gif', alt: '[惊恐]' },
    547. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/27.gif', alt: '[流汗]' },
    548. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/28.gif', alt: '[憨笑]' },
    549. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/29.gif', alt: '[悠闲]' },
    550. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/30.gif', alt: '[奋斗]' },
    551. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/31.gif', alt: '[咒骂]' },
    552. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/32.gif', alt: '[疑问]' },
    553. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/33.gif', alt: '[嘘]' },
    554. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/34.gif', alt: '[晕]' },
    555. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/35.gif', alt: '[疯了]' },
    556. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/36.gif', alt: '[衰]' },
    557. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/37.gif', alt: '[骷髅]' },
    558. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/38.gif', alt: '[敲打]' },
    559. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/39.gif', alt: '[再见]' },
    560. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/40.gif', alt: '[擦汗]' },
    561. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/41.gif', alt: '[抠鼻]' },
    562. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/42.gif', alt: '[鼓掌]' },
    563. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/43.gif', alt: '[糗大了]' },
    564. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/44.gif', alt: '[坏笔]' },
    565. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/45.gif', alt: '[左哼哼]' },
    566. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/46.gif', alt: '[右哼哼]' },
    567. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/47.gif', alt: '[哈欠]' },
    568. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/48.gif', alt: '[鄙视]' },
    569. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/49.gif', alt: '[委屈]' },
    570. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/50.gif', alt: '[快哭了]' },
    571. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/51.gif', alt: '[阴险]' },
    572. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/52.gif', alt: '[亲亲]' },
    573. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/53.gif', alt: '[吓]' },
    574. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/54.gif', alt: '[可怜]' },
    575. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/55.gif', alt: '[菜刀]' },
    576. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/56.gif', alt: '[西瓜]' },
    577. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/57.gif', alt: '[啤酒]' },
    578. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/58.gif', alt: '[篮球]' },
    579. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/59.gif', alt: '[乒乓]' },
    580. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/60.gif', alt: '[咖啡]' },
    581. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/61.gif', alt: '[饭]' },
    582. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/62.gif', alt: '[猪头]' },
    583. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/63.gif', alt: '[玫瑰]' },
    584. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/64.gif', alt: '[凋谢]' },
    585. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/65.gif', alt: '[嘴唇]' },
    586. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/66.gif', alt: '[爱心]' },
    587. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/67.gif', alt: '[心碎]' },
    588. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/68.gif', alt: '[蛋糕]' },
    589. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/69.gif', alt: '[闪电]' },
    590. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/70.gif', alt: '[炸弹]' },
    591. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/71.gif', alt: '[刀]' },
    592. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/72.gif', alt: '[足球]' },
    593. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/73.gif', alt: '[瓢虫]' },
    594. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/74.gif', alt: '[便便]' },
    595. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/75.gif', alt: '[月亮]' },
    596. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/76.gif', alt: '[太阳]' },
    597. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/77.gif', alt: '[礼物]' },
    598. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/78.gif', alt: '[拥抱]' },
    599. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/79.gif', alt: '[强]' },
    600. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/80.gif', alt: '[弱]' },
    601. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/81.gif', alt: '[握手]' },
    602. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/82.gif', alt: '[胜利]' },
    603. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/83.gif', alt: '[抱拳]' },
    604. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/84.gif', alt: '[勾引]' },
    605. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/85.gif', alt: '[拳头]' },
    606. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/86.gif', alt: '[差劲]' },
    607. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/87.gif', alt: '[爱你]' },
    608. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/88.gif', alt: '[NO]' },
    609. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/89.gif', alt: '[OK]' },
    610. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/90.gif', alt: '[爱情]' },
    611. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/91.gif', alt: '[飞吻]' },
    612. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/92.gif', alt: '[跳跳]' },
    613. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/93.gif', alt: '[发抖]' },
    614. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/94.gif', alt: '[怄火]' },
    615. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/95.gif', alt: '[转圈]' },
    616. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/96.gif', alt: '[磕头]' },
    617. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/97.gif', alt: '[回头]' },
    618. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/98.gif', alt: '[跳绳]' },
    619. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/99.gif', alt: '[投降]' },
    620. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/100.gif', alt: '[激动]' },
    621. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/101.gif', alt: '[乱舞]' },
    622. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/102.gif', alt: '[献吻]' },
    623. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/103.gif', alt: '[左太极]' },
    624. { src: 'https://res.wx.qq.com/mpres/htmledition/images/icon/emotion/104.gif', alt: '[右太极]' },
    625. ]
    626. },{
    627. title: '静图',
    628. type: 'image',
    629. content: [
    630. {"src": "/static/png/2018new_weixioa02_org.png","alt": "[微笑]",},
    631. {"src": "/static/png/2018new_keai_org.png","alt": "[可爱]",},
    632. {"src": "/static/png/2018new_taikaixin_org.png","alt": "[太开心]",},
    633. {"src": "/static/png/2018new_guzhang_org.png","alt": "[鼓掌]",},
    634. {"src": "/static/png/2018new_xixi_org.png","alt": "[嘻嘻]",},
    635. {"src": "/static/png/2018new_haha_org.png","alt": "[哈哈]",},
    636. {"src": "/static/png/2018new_xiaoku_thumb.png","alt": "[笑cry]",},
    637. {"src": "/static/png/2018new_jiyan_org.png","alt": "[挤眼]",},
    638. {"src": "/static/png/2018new_chanzui_org.png","alt": "[馋嘴]",},
    639. {"src": "/static/png/2018new_heixian_org.png","alt": "[黑线]",},
    640. {"src": "/static/png/2018new_han_org.png","alt": "[汗]",},
    641. {"src": "/static/png/2018new_wabi_thumb.png","alt": "[挖鼻]",},
    642. {"src": "/static/png/2018new_heng_org.png","alt": "[哼]",},
    643. {"src": "/static/png/2018new_nu_org.png","alt": "[怒]",},
    644. {"src": "/static/png/2018new_weiqu_org.png","alt": "[委屈]",},
    645. {"src": "/static/png/2018new_kelian_org.png","alt": "[可怜]",},
    646. {"src": "/static/png/2018new_shiwang_org.png","alt": "[失望]",},
    647. {"src": "/static/png/2018new_beishang_org.png","alt": "[悲伤]",},
    648. {"src": "/static/png/2018new_leimu_org.png","alt": "[泪]",},
    649. {"src": "/static/png/2018new_kuxiao_org.png","value": "[允悲]",},
    650. {"src": "/static/png/2018new_haixiu_org.png","alt": "[害羞]",},
    651. {"src": "/static/png/2018new_wu_org.png","alt": "[污]",},
    652. {"src": "/static/png/2018new_aini_org.png","alt": "[爱你]",},
    653. {"src": "https://math.now.sh?from=\log\prod^N_{i}x_{i}=\sum^N_i\log{x_i}","alt": "[API公式]",}
    654. ]
    655. },
    656. ],
    657. // 编辑区域的 z-index
    658. zIndex: 10000,
    659. // 是否开启 debug 模式(debug 模式下错误会 throw error 形式抛出)
    660. debug: false,
    661. // 插入链接时候的格式校验
    662. linkCheck: function linkCheck(text, link) {
    663. // text 是插入的文字
    664. // link 是插入的链接
    665. return true; // 返回 true 即表示成功
    666. // return '校验失败' // 返回字符串即表示失败的提示信息
    667. },
    668. // 插入网络图片的校验
    669. linkImgCheck: function linkImgCheck(src) {
    670. // src 即图片的地址
    671. return true; // 返回 true 即表示成功
    672. // return '校验失败' // 返回字符串即表示失败的提示信息
    673. },
    674. // 粘贴过滤样式,默认开启
    675. pasteFilterStyle: true,
    676. // 粘贴内容时,忽略图片。默认关闭
    677. pasteIgnoreImg: false,
    678. // 对粘贴的文字进行自定义处理,返回处理后的结果。编辑器会将处理后的结果粘贴到编辑区域中。
    679. // IE 暂时不支持
    680. pasteTextHandle: function pasteTextHandle(content) {
    681. // content 即粘贴过来的内容(html 或 纯文本),可进行自定义处理然后返回
    682. return content;
    683. },
    684. // onchange 事件
    685. // onchange: function (html) {
    686. // // html 即变化之后的内容
    687. // console.log(html)
    688. // },
    689. // 是否显示添加网络图片的 tab
    690. showLinkImg: true,
    691. // 插入网络图片的回调
    692. linkImgCallback: function linkImgCallback(url) {
    693. // console.log(url) // url 即插入图片的地址
    694. },
    695. // 默认上传图片 max size: 5M
    696. uploadImgMaxSize: 5 * 1024 * 1024,
    697. // 配置一次最多上传几个图片
    698. // uploadImgMaxLength: 5,
    699. // 上传图片,是否显示 base64 格式
    700. uploadImgShowBase64: true,
    701. // 上传图片,server 地址(如果有值,则 base64 格式的配置则失效)
    702. // uploadImgServer: '/upload',
    703. // 自定义配置 filename
    704. uploadFileName: '',
    705. // 上传图片的自定义参数
    706. uploadImgParams: {
    707. // token: 'abcdef12345'
    708. },
    709. // 上传图片的自定义header
    710. uploadImgHeaders: {
    711. // 'Accept': 'text/x-json'
    712. },
    713. // 配置 XHR withCredentials
    714. withCredentials: false,
    715. // 自定义上传图片超时时间 ms
    716. uploadImgTimeout: 10000,
    717. // 上传图片 hook
    718. uploadImgHooks: {
    719. // customInsert: function (insertLinkImg, result, editor) {
    720. // console.log('customInsert')
    721. // // 图片上传并返回结果,自定义插入图片的事件,而不是编辑器自动插入图片
    722. // const data = result.data1 || []
    723. // data.forEach(link => {
    724. // insertLinkImg(link)
    725. // })
    726. // },
    727. before: function before(xhr, editor, files) {
    728. // 图片上传之前触发
    729. // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
    730. // return {
    731. // prevent: true,
    732. // msg: '放弃上传'
    733. // }
    734. },
    735. success: function success(xhr, editor, result) {
    736. // 图片上传并返回结果,图片插入成功之后触发
    737. },
    738. fail: function fail(xhr, editor, result) {
    739. // 图片上传并返回结果,但图片插入错误时触发
    740. },
    741. error: function error(xhr, editor) {
    742. // 图片上传出错时触发
    743. },
    744. timeout: function timeout(xhr, editor) {
    745. // 图片上传超时时触发
    746. }
    747. },
    748. // 是否上传七牛云,默认为 false
    749. qiniu: false
    750. };
    751. /*
    752. 工具
    753. */
    754. // 和 UA 相关的属性
    755. var UA = {
    756. _ua: navigator.userAgent,
    757. // 是否 webkit
    758. isWebkit: function isWebkit() {
    759. var reg = /webkit/i;
    760. return reg.test(this._ua);
    761. },
    762. // 是否 IE
    763. isIE: function isIE() {
    764. return 'ActiveXObject' in window;
    765. }
    766. };
    767. // 遍历对象
    768. function objForEach(obj, fn) {
    769. var key = void 0,
    770. result = void 0;
    771. for (key in obj) {
    772. if (obj.hasOwnProperty(key)) {
    773. result = fn.call(obj, key, obj[key]);
    774. if (result === false) {
    775. break;
    776. }
    777. }
    778. }
    779. }
    780. // 遍历类数组
    781. function arrForEach(fakeArr, fn) {
    782. var i = void 0,
    783. item = void 0,
    784. result = void 0;
    785. var length = fakeArr.length || 0;
    786. for (i = 0; i < length; i++) {
    787. item = fakeArr[i];
    788. result = fn.call(fakeArr, item, i);
    789. if (result === false) {
    790. break;
    791. }
    792. }
    793. }
    794. // 获取随机数
    795. function getRandom(prefix) {
    796. return prefix + Math.random().toString().slice(2);
    797. }
    798. // 替换 html 特殊字符
    799. function replaceHtmlSymbol(html) {
    800. if (html == null) {
    801. return '';
    802. }
    803. return html.replace(/, '<').replace(/>/gm, '>').replace(/"/gm, '"').replace(/(\r\n|\r|\n)/g, '
      '
      );
    804. }
    805. // 返回百分比的格式
    806. // 判断是不是 function
    807. function isFunction(fn) {
    808. return typeof fn === 'function';
    809. }
    810. //======================= 原系统功能 ==================================================
    811. /*
    812. bold-menu
    813. */
    814. // 构造函数
    815. function Bold(editor) {
    816. this.editor = editor;
    817. this.$elem = $('
      \n \n
      '
      );
    818. this.type = 'click';
    819. // 当前是否 active 状态
    820. this._active = false;
    821. }
    822. // 原型
    823. Bold.prototype = {
    824. constructor: Bold,
    825. // 点击事件
    826. onClick: function onClick(e) {
    827. // 点击菜单将触发这里
    828. var editor = this.editor;
    829. var isSeleEmpty = editor.selection.isSelectionEmpty();
    830. if (isSeleEmpty) {
    831. // 选区是空的,插入并选中一个“空白”
    832. editor.selection.createEmptyRange();
    833. }
    834. // 执行 bold 命令
    835. editor.cmd.do('bold');
    836. if (isSeleEmpty) {
    837. // 需要将选取折叠起来
    838. editor.selection.collapseRange();
    839. editor.selection.restoreSelection();
    840. }
    841. },
    842. //======================= 新增加功能 ==================================================
    843. // 试图改变 active 状态
    844. tryChangeActive: function tryChangeActive(e) {
    845. var editor = this.editor;
    846. var $elem = this.$elem;
    847. if (editor.cmd.queryCommandState('bold')) {
    848. this._active = true;
    849. $elem.addClass('w-e-active');
    850. } else {
    851. this._active = false;
    852. $elem.removeClass('w-e-active');
    853. }
    854. }
    855. };
    856. /*
    857. Sup-menu 上标
    858. */
    859. // 新构造函数
    860. function Sup(editor) {
    861. this.editor = editor;
    862. this.$elem = $('
      '
      );
    863. this.type = 'click';
    864. // 当前是否 active 状态
    865. this._active = false;
    866. }
    867. //
    868. Sup.prototype = {
    869. constructor: Sup,
    870. // 点击事件
    871. onClick: function onClick(e) {
    872. // 点击菜单将触发这里
    873. var editor = this.editor;
    874. var isSeleEmpty = editor.selection.isSelectionEmpty();
    875. if (isSeleEmpty) {
    876. // 选区是空的,插入并选中一个“空白”
    877. editor.selection.createEmptyRange();
    878. }
    879. // 执行 bold 命令
    880. editor.cmd.do('insertHTML',''+editor.selection.getSelectionText()+'');
    881. if (isSeleEmpty) {
    882. // 需要将选取折叠起来
    883. editor.selection.collapseRange();
    884. editor.selection.restoreSelection();
    885. }
    886. },
    887. // 试图改变 active 状态
    888. tryChangeActive: function tryChangeActive(e) {
    889. var editor = this.editor;
    890. var $elem = this.$elem;
    891. if (editor.cmd.queryCommandState('Sup')) {
    892. this._active = true;
    893. $elem.addClass('w-e-active');
    894. } else {
    895. this._active = false;
    896. $elem.removeClass('w-e-active');
    897. }
    898. }
    899. };
    900. /*
    901. Sub-menu 下标
    902. */
    903. // 新构造函数
    904. function Sub(editor) {
    905. this.editor = editor;
    906. this.$elem = $('
      '
      );
    907. this.type = 'click';
    908. // 当前是否 active 状态
    909. this._active = false;
    910. }
    911. // 原型
    912. Sub.prototype = {
    913. constructor: Sub,
    914. // 点击事件
    915. onClick: function onClick(e) {
    916. // 点击菜单将触发这里
    917. var editor = this.editor;
    918. console.log(editor.selection)
    919. var isSeleEmpty = editor.selection.isSelectionEmpty();
    920. if (isSeleEmpty) {
    921. // 选区是空的,插入并选中一个“空白”
    922. editor.selection.createEmptyRange();
    923. }
    924. // 执行 bold 命令
    925. editor.cmd.do('insertHTML',''+editor.selection.getSelectionText()+'');
    926. if (isSeleEmpty) {
    927. // 需要将选取折叠起来
    928. editor.selection.collapseRange();
    929. editor.selection.restoreSelection();
    930. }
    931. },
    932. // 试图改变 active 状态
    933. tryChangeActive: function tryChangeActive(e) {
    934. var editor = this.editor;
    935. var $elem = this.$elem;
    936. if (editor.cmd.queryCommandState('Sub')) {
    937. this._active = true;
    938. $elem.addClass('w-e-active');
    939. } else {
    940. this._active = false;
    941. $elem.removeClass('w-e-active');
    942. }
    943. }
    944. };
    945. /*
    946. 替换多语言
    947. */
    948. var replaceLang = function (editor, str) {
    949. var langArgs = editor.config.langArgs || [];
    950. var result = str;
    951. langArgs.forEach(function (item) {
    952. var reg = item.reg;
    953. var val = item.val;
    954. if (reg.test(result)) {
    955. result = result.replace(reg, function () {
    956. return val;
    957. });
    958. }
    959. });
    960. return result;
    961. };
    962. /*
    963. droplist
    964. */
    965. var _emptyFn = function _emptyFn() {};
    966. // 构造函数
    967. function DropList(menu, opt) {
    968. var _this = this;
    969. // droplist 所依附的菜单
    970. var editor = menu.editor;
    971. this.menu = menu;
    972. this.opt = opt;
    973. // 容器
    974. var $container = $('
      '
      );
    975. // 标题
    976. var $title = opt.$title;
    977. var titleHtml = void 0;
    978. if ($title) {
    979. // 替换多语言
    980. titleHtml = $title.html();
    981. titleHtml = replaceLang(editor, titleHtml);
    982. $title.html(titleHtml);
    983. $title.addClass('w-e-dp-title');
    984. $container.append($title);
    985. }
    986. var list = opt.list || [];
    987. var type = opt.type || 'list'; // 'list' 列表形式(如“标题”菜单) / 'inline-block' 块状形式(如“颜色”菜单)
    988. var onClick = opt.onClick || _emptyFn;
    989. // 加入 DOM 并绑定事件
    990. var $list = $('
        'list' ? 'w-e-list' : 'w-e-block') + '">
      '
      );
    991. $container.append($list);
    992. list.forEach(function (item) {
    993. var $elem = item.$elem;
    994. // 替换多语言
    995. var elemHtml = $elem.html();
    996. elemHtml = replaceLang(editor, elemHtml);
    997. $elem.html(elemHtml);
    998. var value = item.value;
    999. var $li = $('
    1000. ');
  • 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: '
    \n '" type="text" class="block" value="' + text + '" placeholder="\u94FE\u63A5\u6587\u5B57"/>\n '" type="text" class="block" value="' + link + '" placeholder="http://..."/>\n
    \n \n \n
    \n
    '
    ,
  • // 事件绑定
  • 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', ''" target="_blank">' + 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 = $('
    \n \n
    '
    );
  • 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 = $('
    \n \n
    '
    );
  • 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 = $('
    \n \n
    '
    );
  • 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 = $('
    \n \n
    '
    );
  • 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'); 原来的代码
  • editor.cmd.do('insertHTML',''+editor.selection.getSelectionText()+'');
  • 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');
  • }
  • }
  • };
  • /*
  • underdot-menu 下点 2021-09-29
  • */
  • // 构造函数
  • function underdot(editor) {
  • this.editor = editor;
  • this.$elem = $('
    ...
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • underdot.prototype = {
  • constructor: underdot,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • console.log(editor.selection)
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 bold 命令
  • editor.cmd.do('insertHTML',''+editor.selection.getSelectionText()+'');
  • 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('underdot')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*
  • 水平线-menu 2021-07-24
  • */
  • // 构造函数
  • function HRline(editor) {
  • this.editor = editor;
  • this.$elem = $('
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • HRline.prototype = {
  • constructor: HRline,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','
    '
    );
  • 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('HRline')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*
  • 大括号-menu 2021-10-22
  • */
  • // 构造函数
  • function Bracket(editor) {
  • this.editor = editor;
  • this.$elem = $('
    {
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Bracket.prototype = {
  • constructor: Bracket,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','\\begin{cases}
    \\\\
    \\\\

    \\end{cases}
    '
    );
  • 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('Bracket')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*
  • 分数-menu 2021-10-22
  • */
  • // 构造函数
  • function Fraction(editor) {
  • this.editor = editor;
  • this.$elem = $('
    ½
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Fraction.prototype = {
  • constructor: Fraction,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','\\frac{1}{2}');
  • 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('Fraction')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • //======================================================================================================
  • /*
  • 分数-menu 2021-10-22
  • */
  • // 构造函数
  • function Sqrt(editor) {
  • this.editor = editor;
  • this.$elem = $('
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Sqrt.prototype = {
  • constructor: Sqrt,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','\\sqrt{2}');
  • 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('Sqrt')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • //============================折叠区域 Folder===================================================
  • /*
  • Folder -menu 2021-10-22
  • */
  • // 构造函数
  • function Folder(editor) {
  • this.editor = editor;
  • this.$elem = $('
    折叠
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Folder.prototype = {
  • constructor: Folder,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • alert("折叠区必须先指定一个独一无二的id"); return;
  • // editor.selection.createEmptyRange();
  • }
  • // 执行命令
  • editor.cmd.do('insertHTML', '
    selection.getSelectionText()+'\').style.display=(document.getElementById(\''+editor.selection.getSelectionText()+'\').style.display==\'none\')?\'block\':\'none\';">


    selection.getSelectionText()+'" style="background-color: #ddd;">


    '
    );
  • 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('Folder')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*
  • 图片垂直居中-menu 2021-08-01
  • */
  • // 构造函数
  • function Middle(editor) {
  • this.editor = editor;
  • this.$elem = $('
    居中
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Middle.prototype = {
  • constructor: Middle,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • // 选区是空的,插入并选中一个“空白”
  • editor.selection.createEmptyRange();
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML',' style="vertical-align: middle;" ');
  • 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('Middle')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*特殊字符-menu 2021-07-24*/
  • // '△', '⊙', '∠', '⊥', '≥', '≤', '≠', '≌', '±', '÷', '∵', '∴', '°', '℃'],
  • function a01(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a01.prototype = {constructor: a01,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','△');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a01')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a02(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a02.prototype = {constructor: a02,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','⊙');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a02')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a03(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a03.prototype = {constructor: a03,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','∠');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a03')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a04(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a04.prototype = {constructor: a04,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','⊥');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a04')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a05(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a05.prototype = {constructor: a05,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≥');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a05')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a06(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a06.prototype = {constructor: a06,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≤');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a06')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a07(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a07.prototype = {constructor: a07,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≠');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a07')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a08(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a08.prototype = {constructor: a08,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≮');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a08')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a09(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a09.prototype = {constructor: a09,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≯');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a09')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a10(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a10.prototype = {constructor: a10,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≌');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a10')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a11(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a11.prototype = {constructor: a11,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','≡');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a11')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a12(editor) {this.editor = editor;this.$elem = $('
    ±
    '
    );this.type = 'click';this._active = false;} a12.prototype = {constructor: a12,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','±');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a12')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a13(editor) {this.editor = editor;this.$elem = $('
    ÷
    '
    );this.type = 'click';this._active = false;} a13.prototype = {constructor: a13,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','÷');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a13')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a14(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a14.prototype = {constructor: a14,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','∵');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a14')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a15(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a15.prototype = {constructor: a15,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','∴');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a15')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a16(editor) {this.editor = editor;this.$elem = $('
    °
    '
    );this.type = 'click';this._active = false;} a16.prototype = {constructor: a16,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','°');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a16')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a17(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a17.prototype = {constructor: a17,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','℃');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a17')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a18(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a18.prototype = {constructor: a18,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','℉');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a18')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a19(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a19.prototype = {constructor: a19,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','﹢');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a19')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a20(editor) {this.editor = editor;this.$elem = $('
    ×
    '
    );this.type = 'click';this._active = false;} a20.prototype = {constructor: a20,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','×');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a20')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a21(editor) {this.editor = editor;this.$elem = $('
    '
    );this.type = 'click';this._active = false;} a21.prototype = {constructor: a21,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','·');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a21')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a22(editor) {this.editor = editor;this.$elem = $('
    ¹
    '
    );this.type = 'click';this._active = false;} a22.prototype = {constructor: a22,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','¹');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a22')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a23(editor) {this.editor = editor;this.$elem = $('
    ²
    '
    );this.type = 'click';this._active = false;} a23.prototype = {constructor: a23,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','²');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a23')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • function a24(editor) {this.editor = editor;this.$elem = $('
    ³
    '
    );this.type = 'click';this._active = false;} a24.prototype = {constructor: a24,onClick: function onClick(e) {var editor = this.editor;var isSeleEmpty = editor.selection.isSelectionEmpty();if (isSeleEmpty) {editor.selection.createEmptyRange();}editor.cmd.do('insertHTML','³');if (isSeleEmpty) { editor.selection.collapseRange();editor.selection.restoreSelection();}},tryChangeActive: function tryChangeActive(e) {var editor = this.editor;var $elem = this.$elem;if (editor.cmd.queryCommandState('a24')) {this._active = true;$elem.addClass('w-e-active');} else {this._active = false;$elem.removeClass('w-e-active');}}};
  • /*
  • Latex-menu 2021-07-24
  • */
  • // 构造函数
  • function Latex(editor) {
  • this.editor = editor;
  • this.$elem = $('
    Latex
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Latex.prototype = {
  • constructor: Latex,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • alert("请选择Latex公式内容"); return;
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','selection.getSelectionText()+'" alt="[API公式]" style="vertical-align: middle;" data-w-e="1">');
  • 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('Latex')) {
  • this._active = true;
  • $elem.addClass('w-e-active');
  • } else {
  • this._active = false;
  • $elem.removeClass('w-e-active');
  • }
  • }
  • };
  • /*
  • Answer-menu 看答案 2021-07-24
  • */
  • // 构造函数
  • function Answer(editor) {
  • this.editor = editor;
  • this.$elem = $('
    答案
    '
    );
  • this.type = 'click';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Answer.prototype = {
  • constructor: Answer,
  • // 点击事件
  • onClick: function onClick(e) {
  • // 点击菜单将触发这里
  • var editor = this.editor;
  • var isSeleEmpty = editor.selection.isSelectionEmpty();
  • if (isSeleEmpty) {
  • alert("请选择答案"); return;
  • }
  • // 执行 underline 命令
  • editor.cmd.do('insertHTML','selection.getSelectionText()+'\':\' \'; ">');
  • 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('Latex')) {
  • 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 = $('
    \n \n
    '
    );
  • 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: $('';" class="w-e-icon-pencil2">'), 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: $('';" class="w-e-icon-paint-brush">'), 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 = $('
    \n \n
    '
    );
  • 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 = $('
    \n \n
    '
    );
  • 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 = $('
    \n \n
    '
    );
  • 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 += ''" alt="' + alt + '" data-w-e="1"/ style="vertical-align:middle">'; //:加入图片高度居中
  • }
  • });
  • }
  • 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: '
    \n

    \n \u521B\u5EFA\n '" type="text" value="1" style="width:40px;text-align:center;"/>\n \u884C\n '" type="text" value="2" style="width:40px;text-align:center;"/>\n \u5217\u7684\u8868\u683C\n

    \n
    \n \n
    \n
    '
    ,
  • // 事件绑定
  • 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 w=0;
  • var html = ''" width="94%" height="50%" cellpadding="0" cellspacing="0">';
  • 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 '" type="text" class="block" placeholder="\u683C\u5F0F\u5982\uFF1A"/>\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 - mathsymbol 2021.5.30添加数学公式输入菜单
  • */
  • // 构造函数
  • function Mathsymbol(editor) {
  • this.editor = editor;
  • this.$elem = $('
    公式
    '
    );
  • this.type = 'panel';
  • // 当前是否 active 状态
  • this._active = false;
  • }
  • // 原型
  • Mathsymbol.prototype = {
  • constructor: Mathsymbol,
  • 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: '',
  • // 事件绑定
  • 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() {
  • console.log("图片")
  • 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 \u81ea\u5b9a\u4e49\u5bbd\u5ea6%\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: '#selfSize', //:添加自定义图片显示比例功能,注意不会改变图片实际大小
  • type: 'input',
  • fn: function fn() {
  • var $img = editor._selectedImg;
  • if ($img) {
  • var size = $("#selfSize").val();
  • $img.css('max-width', size+'%');
  • }
  • // 返回 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: '
    \n
    '" class="w-e-up-btn">\n \n
    \n
    \n '" type="file" multiple="multiple" accept="image/jpg,image/jpeg,image/png,image/gif,image/bmp"/>\n
    \n
    '
    ,
  • 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 '" type="text" class="block" placeholder="\u56FE\u7247\u94FE\u63A5"/>\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.strikeThrough = StrikeThrough;
  • MenuConstructors.fontSize = FontSize;
  • MenuConstructors.fontName = FontName;
  • MenuConstructors.link = Link;
  • MenuConstructors.italic = Italic;
  • MenuConstructors.redo = Redo;
  • 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;
  • MenuConstructors.underline = Underline;//
  • MenuConstructors.underdot = underdot;//
  • MenuConstructors.HRline = HRline; //
  • MenuConstructors.Sup = Sup; //
  • MenuConstructors.Sub = Sub; //
  • MenuConstructors.Middle = Middle; //
  • MenuConstructors.mathsymbol = Mathsymbol;
  • MenuConstructors.Latex = Latex; //
  • MenuConstructors.Bracket = Bracket; //
  • MenuConstructors.Fraction = Fraction; //
  • MenuConstructors.Sqrt =Sqrt; //
  • MenuConstructors.Answer = Answer; //
  • MenuConstructors.Folder =Folder ;
  • // '△', '⊙', '∠', '⊥', '≥', '≤', '≠', '≌', '±', '÷', '∵', '∴', '°', '℃', '﹢', '×'],
  • MenuConstructors.a01 =a01 ;
  • MenuConstructors.a02 =a02 ;
  • MenuConstructors.a03 =a03 ;
  • MenuConstructors.a04 =a04 ;
  • MenuConstructors.a05 =a05 ;
  • MenuConstructors.a06 =a06 ;
  • MenuConstructors.a07 =a07 ;
  • MenuConstructors.a08 =a08 ;
  • MenuConstructors.a09 =a09 ;
  • MenuConstructors.a10 =a10 ;
  • MenuConstructors.a11 =a11 ;
  • MenuConstructors.a12 =a12 ;
  • MenuConstructors.a13 =a13 ;
  • MenuConstructors.a14 =a14 ;
  • MenuConstructors.a15 =a15 ;
  • MenuConstructors.a16 =a16 ;
  • MenuConstructors.a17 =a17 ;
  • MenuConstructors.a18 =a18 ;
  • MenuConstructors.a19 =a19 ;
  • MenuConstructors.a20 =a20 ;
  • MenuConstructors.a21 =a21 ;
  • MenuConstructors.a22 =a22 ;
  • MenuConstructors.a23 =a23 ;
  • MenuConstructors.a24 =a24 ;
  • /*
  • 菜单集合
  • */
  • // 构造函数
  • 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('');
  • if (docSplitHtml.length === 2) {
  • pasteHtml = docSplitHtml[0];
  • }
  • // 过滤无用标签
  • pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/igm, '');
  • // 去掉注释
  • pasteHtml = pasteHtml.replace(//mg, '');
  • // 过滤 data-xxx 属性
  • pasteHtml = pasteHtml.replace(/\s?data-.+?=('|").+?('|")/igm, '');
  • if (ignoreImg) {
  • // 忽略图片
  • pasteHtml = pasteHtml.replace(//igm, '');
  • }
  • if (filterStyle) {
  • // 过滤样式
  • pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").*?('|")/igm, '');
  • } else {
  • // 保留样式
  • pasteHtml = pasteHtml.replace(/\s?class=('|").*?('|")/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;
  • }
  • /*
  • 编辑区域
  • */
  • // 获取一个 elem.childNodes 的 JSON 数据
  • function getChildrenJSON($elem) {
  • var result = [];
  • var $children = $elem.childNodes() || []; // 注意 childNodes() 可以获取文本节点
  • $children.forEach(function (curElem) {
  • var elemResult = void 0;
  • var nodeType = curElem.nodeType;
  • // 文本节点
  • if (nodeType === 3) {
  • elemResult = curElem.textContent;
  • elemResult = replaceHtmlSymbol(elemResult);
  • }
  • // 普通 DOM 节点
  • if (nodeType === 1) {
  • elemResult = {};
  • // tag
  • elemResult.tag = curElem.nodeName.toLowerCase();
  • // attr
  • var attrData = [];
  • var attrList = curElem.attributes || {};
  • var attrListLength = attrList.length || 0;
  • for (var i = 0; i < attrListLength; i++) {
  • var attr = attrList[i];
  • attrData.push({
  • name: attr.name,
  • value: attr.value
  • });
  • }
  • elemResult.attrs = attrData;
  • // children(递归)
  • elemResult.children = getChildrenJSON($(curElem));
  • }
  • result.push(elemResult);
  • });
  • return result;
  • }
  • // 构造函数
  • function Text(editor) {
  • this.editor = editor;
  • }
  • // 修改原型
  • Text.prototype = {
  • constructor: Text,
  • // 初始化
  • init: function init() {
  • // 绑定事件
  • this._bindEvent();
  • },
  • // 清空内容
  • clear: function clear() {
  • this.html('


    '
    );
  • },
  • // 获取 设置 html
  • html: function html(val) {
  • var editor = this.editor;
  • var $textElem = editor.$textElem;
  • var html = void 0;
  • if (val == null) {
  • html = $textElem.html();
  • // 未选中任何内容的时候点击“加粗”或者“斜体”等按钮,就得需要一个空的占位符 ​ ,这里替换掉
  • html = html.replace(/\u200b/gm, '');
  • return html;
  • } else {
  • $textElem.html(val);
  • // 初始化选取,将光标定位到内容尾部
  • editor.initSelection();
  • }
  • },
  • // 获取 JSON
  • getJSON: function getJSON() {
  • var editor = this.editor;
  • var $textElem = editor.$textElem;
  • return getChildrenJSON($textElem);
  • },
  • // 获取 设置 text
  • text: function text(val) {
  • var editor = this.editor;
  • var $textElem = editor.$textElem;
  • var text = void 0;
  • if (val == null) {
  • text = $textElem.text();
  • // 未选中任何内容的时候点击“加粗”或者“斜体”等按钮,就得需要一个空的占位符 ​ ,这里替换掉
  • text = text.replace(/\u200b/gm, '');
  • return text;
  • } else {
  • $textElem.text('

    ' + val + '

    '
    );
  • // 初始化选取,将光标定位到内容尾部
  • 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();
  • // 清空时保留


  • this._clearHandle();
  • // 粘贴事件(粘贴文字,粘贴图片)
  • this._pasteHandle();
  • // tab 特殊处理
  • this._tabHandle();
  • // img 点击
  • this._imgHandle();
  • // 拖拽事件
  • this._dragHandle();
  • },
  • // 实时保存选取
  • _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;
  • function insertEmptyP($selectionElem) {
  • var $p = $('


    '
    );
  • $p.insertBefore($selectionElem);
  • editor.selection.createRangeByElem($p, true);
  • editor.selection.restoreSelection();
  • $selectionElem.remove();
  • }
  • // 将回车之后生成的非

    的顶级标签,改为

  • function pHandle(e) {
  • var $selectionElem = editor.selection.getSelectionContainerElem();
  • var $parentElem = $selectionElem.parent();
  • if ($parentElem.html() === '
    '
    ) {
  • // 回车之前光标所在一个

    .....

    ,忽然回车生成一个空的


  • // 而且继续回车跳不出去,因此只能特殊处理
  • insertEmptyP($selectionElem);
  • return;
  • }
  • if (!$parentElem.equal($textElem)) {
  • // 不是顶级标签
  • return;
  • }
  • var nodeName = $selectionElem.getNodeName();
  • if (nodeName === 'P') {
  • // 当前的标签是 P ,不用做处理
  • return;
  • }
  • if ($selectionElem.text()) {
  • // 有内容,不做处理
  • return;
  • }
  • // 插入

    ,并将选取定位到

    ,删除当前标签

  • insertEmptyP($selectionElem);
  • }
  • $textElem.on('keyup', function (e) {
  • if (e.keyCode !== 13) {
  • // 不是回车键
  • return;
  • }
  • // 将回车之后生成的非

    的顶级标签,改为

  • pHandle(e);
  • });
  • //
    回车时 特殊处理
  • 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;
  • }
  • // 处理:光标定位到代码末尾,联系点击两次回车,即跳出代码块
  • if (editor._willBreakCode === true) {
  • // 此时可以跳出代码块
  • // 插入

    ,并将选取定位到

  • var $p = $('


    '
    );
  • $p.insertAfter($parentElem);
  • editor.selection.createRangeByElem($p, true);
  • editor.selection.restoreSelection();
  • // 修改状态
  • editor._willBreakCode = false;
  • e.preventDefault();
  • return;
  • }
  • var _startOffset = editor.selection.getRange().startOffset;
  • // 处理:回车时,不能插入
    而是插入 \n ,因为是在 pre 标签里面
  • editor.cmd.do('insertHTML', '\n');
  • editor.selection.saveRange();
  • if (editor.selection.getRange().startOffset === _startOffset) {
  • // 没起作用,再来一遍
  • editor.cmd.do('insertHTML', '\n');
  • }
  • var codeLength = $selectionElem.html().length;
  • if (editor.selection.getRange().startOffset + 1 === codeLength) {
  • // 说明光标在代码最后的位置,执行了回车操作
  • // 记录下来,以便下次回车时候跳出 code
  • editor._willBreakCode = true;
  • }
  • // 阻止默认行为
  • e.preventDefault();
  • }
  • $textElem.on('keydown', function (e) {
  • if (e.keyCode !== 13) {
  • // 不是回车键
  • // 取消即将跳转代码块的记录
  • editor._willBreakCode = false;
  • return;
  • }
  • //
    回车时 特殊处理
  • codeHandle(e);
  • });
  • },
  • // 清空时保留


  • _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 === '


    '
    ) {
  • // 最后剩下一个空行,就不再删除了
  • 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 === '
    ' 判断,其他用 !txtHtml 判断
  • if (!txtHtml || txtHtml === '
    '
    ) {
  • // 内容空了
  • $p = $('


    '
    );
  • $textElem.html(''); // 一定要先清空,否则在 firefox 下有问题
  • $textElem.append($p);
  • editor.selection.createRangeByElem($p, false, true);
  • editor.selection.restoreSelection();
  • }
  • });
  • },
  • // 粘贴事件(粘贴文字 粘贴图片)
  • _pasteHandle: function _pasteHandle() {
  • var editor = this.editor;
  • var config = editor.config;
  • var pasteFilterStyle = config.pasteFilterStyle;
  • var pasteTextHandle = config.pasteTextHandle;
  • var ignoreImg = config.pasteIgnoreImg;
  • var $textElem = editor.$textElem;
  • // 粘贴图片、文本的事件,每次只能执行一个
  • // 判断该次粘贴事件是否可以执行
  • var pasteTime = 0;
  • function canDo() {
  • var now = Date.now();
  • var flag = false;
  • if (now - pasteTime >= 100) {
  • // 间隔大于 100 ms ,可以执行
  • flag = true;
  • }
  • pasteTime = now;
  • return flag;
  • }
  • function resetTime() {
  • pasteTime = 0;
  • }
  • // 粘贴文字
  • $textElem.on('paste', function (e) {
  • if (UA.isIE()) {
  • return;
  • } else {
  • // 阻止默认行为,使用 execCommand 的粘贴命令
  • e.preventDefault();
  • }
  • // 粘贴图片和文本,只能同时使用一个
  • if (!canDo()) {
  • return;
  • }
  • // 获取粘贴的文字
  • var pasteHtml = getPasteHtml(e, pasteFilterStyle, ignoreImg);
  • var pasteText = getPasteText(e);
  • pasteText = pasteText.replace(/\n/gm, '
    '
    );
  • var $selectionElem = editor.selection.getSelectionContainerElem();
  • if (!$selectionElem) {
  • return;
  • }
  • var nodeName = $selectionElem.getNodeName();
  • // code 中只能粘贴纯文本
  • if (nodeName === 'CODE' || nodeName === 'PRE') {
  • if (pasteTextHandle && isFunction(pasteTextHandle)) {
  • // 用户自定义过滤处理粘贴内容
  • pasteText = '' + (pasteTextHandle(pasteText) || '');
  • }
  • editor.cmd.do('insertHTML', '

    ' + pasteText + '

    '
    );
  • return;
  • }
  • // 先放开注释,有问题再追查 ————
  • // // 表格中忽略,可能会出现异常问题
  • // if (nodeName === 'TD' || nodeName === 'TH') {
  • // return
  • // }
  • if (!pasteHtml) {
  • // 没有内容,可继续执行下面的图片粘贴
  • resetTime();
  • return;
  • }
  • try {
  • // firefox 中,获取的 pasteHtml 可能是没有
      包裹的
  • // 因此执行 insertHTML 会报错
  • if (pasteTextHandle && isFunction(pasteTextHandle)) {
  • // 用户自定义过滤处理粘贴内容
  • pasteHtml = '' + (pasteTextHandle(pasteHtml) || '');
  • }
  • editor.cmd.do('insertHTML', pasteHtml);
  • } catch (ex) {
  • // 此时使用 pasteText 来兼容一下
  • if (pasteTextHandle && isFunction(pasteTextHandle)) {
  • // 用户自定义过滤处理粘贴内容
  • pasteText = '' + (pasteTextHandle(pasteText) || '');
  • }
  • editor.cmd.do('insertHTML', '

    ' + pasteText + '

    '
    );
  • }
  • });
  • // 粘贴图片
  • $textElem.on('paste', function (e) {
  • if (UA.isIE()) {
  • return;
  • } else {
  • e.preventDefault();
  • }
  • // 粘贴图片和文本,只能同时使用一个
  • if (!canDo()) {
  • return;
  • }
  • // 获取粘贴的图片
  • 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') {
  • //
     里面
  • editor.cmd.do('insertHTML', ' ');
  • } else {
  • // 普通文字
  • editor.cmd.do('insertHTML', '    ');
  • }
  • e.preventDefault();
  • });
  • },
  • // img 点击
  • _imgHandle: function _imgHandle() {
  • var editor = this.editor;
  • var $textElem = editor.$textElem;
  • // 为图片增加 selected 样式
  • $textElem.on('click', 'img', function (e) {
  • var img = this;
  • var $img = $(img);
  • if ($img.attr('data-w-e') === '1') {
  • // 是表情图片,忽略
  • return;
  • }
  • // 记录当前点击过的图片
  • editor._selectedImg = $img;
  • // 修改选区并 restore ,防止用户此时点击退格键,会删除其他内容
  • editor.selection.createRangeByElem($img);
  • editor.selection.restoreSelection();
  • });
  • // 去掉图片的 selected 样式
  • $textElem.on('click keyup', function (e) {
  • if (e.target.matches('img')) {
  • // 点击的是图片,忽略
  • return;
  • }
  • // 删除记录
  • editor._selectedImg = null;
  • });
  • },
  • // 拖拽事件
  • _dragHandle: function _dragHandle() {
  • var editor = this.editor;
  • // 禁用 document 拖拽事件
  • var $document = $(document);
  • $document.on('dragleave drop dragenter dragover', function (e) {
  • e.preventDefault();
  • });
  • // 添加编辑区域拖拽事件
  • var $textElem = editor.$textElem;
  • $textElem.on('drop', function (e) {
  • e.preventDefault();
  • var files = e.dataTransfer && e.dataTransfer.files;
  • if (!files || !files.length) {
  • return;
  • }
  • // 上传图片
  • var uploadImg = editor.uploadImg;
  • uploadImg.uploadImg(files);
  • });
  • }
  • };
  • /*
  • 命令,封装 document.execCommand
  • */
  • // 构造函数
  • function Command(editor) {
  • this.editor = editor;
  • }
  • // 修改原型
  • Command.prototype = {
  • constructor: Command,
  • // 执行命令
  • do: function _do(name, value) {
  • var editor = this.editor;
  • // 使用 styleWithCSS
  • if (!editor._useStyleWithCSS) {
  • document.execCommand('styleWithCSS', null, true);
  • editor._useStyleWithCSS = true;
  • }
  • // 如果无选区,忽略
  • 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();
  • 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;
  • }
  • // 判断选区内容是否在不可编辑区域之内
  • if ($containerElem.attr('contenteditable') === 'false' || $containerElem.parentUntil('[contenteditable=false]')) {
  • 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;
  • }
  • try {
  • // 目前只支持 webkit 内核
  • if (UA.isWebkit()) {
  • // 插入 ​
  • editor.cmd.do('insertHTML', '​');
  • // 修改 offset 位置
  • range.setEnd(range.endContainer, range.endOffset + 1);
  • // 存储
  • this.saveRange(range);
  • } else {
  • $elem = $('');
  • editor.cmd.do('insertElem', $elem);
  • this.createRangeByElem($elem, true);
  • }
  • } catch (ex) {
  • // 部分情况下会报错,兼容一下
  • }
  • },
  • // 根据 $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 = $('
    '
    );
  • }
  • 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;
  • var customAlert = editor.config.customAlert;
  • if (debug) {
  • throw new Error('wangEditor: ' + (debugInfo || alertInfo));
  • } else {
  • if (customAlert && typeof customAlert === 'function') {
  • customAlert(alertInfo);
  • } else {
  • alert(alertInfo);
  • }
  • }
  • },
  • // 根据链接插入图片
  • insertLinkImg: function insertLinkImg(link) {
  • var _this2 = this;
  • if (!link) {
  • return;
  • }
  • var editor = this.editor;
  • var config = editor.config;
  • // 校验格式
  • var linkImgCheck = config.linkImgCheck;
  • var checkResult = void 0;
  • if (linkImgCheck && typeof linkImgCheck === 'function') {
  • checkResult = linkImgCheck(link);
  • if (typeof checkResult === 'string') {
  • // 校验失败,提示信息
  • alert(checkResult);
  • return;
  • }
  • }
  • editor.cmd.do('insertHTML', ''" style="max-width:100%;"/>');
  • // 验证图片 url 是否有效,无效的话给出提示
  • var img = document.createElement('img');
  • img.onload = function () {
  • var callback = config.linkImgCallback;
  • if (callback && typeof callback === 'function') {
  • callback(link);
  • }
  • img = null;
  • };
  • 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 uploadImgServer = config.uploadImgServer;
  • var uploadImgShowBase64 = config.uploadImgShowBase64;
  • var maxSize = config.uploadImgMaxSize;
  • var maxSizeM = maxSize / 1024 / 1024;
  • var maxLength = config.uploadImgMaxLength || 10000;
  • var uploadFileName = config.uploadFileName || '';
  • var uploadImgParams = config.uploadImgParams || {};
  • var uploadImgParamsWithUrl = config.uploadImgParamsWithUrl;
  • var uploadImgHeaders = config.uploadImgHeaders || {};
  • var hooks = config.uploadImgHooks || {};
  • var timeout = config.uploadImgTimeout || 3000;
  • var withCredentials = config.withCredentials;
  • if (withCredentials == null) {
  • withCredentials = false;
  • }
  • var customUploadImg = config.customUploadImg;
  • if (!customUploadImg) {
  • // 没有 customUploadImg 的情况下,需要如下两个配置才能继续进行图片上传
  • if (!uploadImgServer && !uploadImgShowBase64) {
  • return;
  • }
  • }
  • // ------------------------------ 验证文件信息 ------------------------------
  • var resultFiles = [];
  • var errInfo = [];
  • arrForEach(files, function (file) {
  • var name = file.name;
  • var size = file.size;
  • // chrome 低版本 name === undefined
  • if (!name || !size) {
  • return;
  • }
  • if (/\.(jpg|jpeg|png|bmp|gif|webp)$/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;
  • }
  • // ------------------------------ 自定义上传 ------------------------------
  • if (customUploadImg && typeof customUploadImg === 'function') {
  • customUploadImg(resultFiles, this.insertLinkImg.bind(this));
  • // 阻止以下代码执行
  • 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) {
  • // 因使用者反应,自定义参数不能默认 encode ,由 v3.1.1 版本开始注释掉
  • // val = encodeURIComponent(val)
  • // 第一,将参数拼接到 url 中
  • if (uploadImgParamsWithUrl) {
  • 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') {
  • var beforeResult = hooks.before(xhr, editor, resultFiles);
  • if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') {
  • if (beforeResult.prevent) {
  • // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
  • this._alert(beforeResult.msg);
  • return;
  • }
  • }
  • }
  • // 自定义 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);
  • // 将语言配置,生成正则表达式
  • var langConfig = this.config.lang || {};
  • var langArgs = [];
  • objForEach(langConfig, function (key, val) {
  • // key 即需要生成正则表达式的规则,如“插入链接”
  • // val 即需要被替换成的语言,如“insert link”
  • langArgs.push({
  • reg: new RegExp(key, 'img'),
  • val: val
  • });
  • });
  • this.config.langArgs = langArgs;
  • },
  • // 初始化 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;
  • // 定义变量
  • var $toolbarElem = void 0,
  • $textContainerElem = void 0,
  • $textElem = void 0,
  • $children = void 0;
  • if (textSelector == null) {
  • // 只传入一个参数,即是容器的选择器或元素,toolbar 和 text 的元素自行创建
  • $toolbarElem = $('
    '
    );
  • $textContainerElem = $('
    '
    );
  • // 将编辑器区域原有的内容,暂存起来
  • $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', '800px'); //: 改大编辑器的高度
  • } else {
  • // toolbar 和 text 的选择器都有值,记录属性
  • $toolbarElem = $toolbarSelector;
  • $textContainerElem = $(textSelector);
  • // 将编辑器区域原有的内容,暂存起来
  • $children = $textContainerElem.children();
  • }
  • // 编辑区域
  • $textElem = $('
    '
    );
  • $textElem.attr('contenteditable', 'true').css('width', '100%').css('height', '100%');
  • // 初始化编辑区域内容
  • if ($children && $children.length) {
  • $textElem.append($children);
  • } else {
  • $textElem.append($('


    '
    ));
  • }
  • // 编辑区域加入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');
  • // 添加 ID
  • var toolbarElemId = getRandom('toolbar-elem');
  • $toolbarElem.attr('id', toolbarElemId);
  • var textElemId = getRandom('text-elem');
  • $textElem.attr('id', textElemId);
  • // 记录属性
  • this.$toolbarElem = $toolbarElem;
  • this.$textContainerElem = $textContainerElem;
  • this.$textElem = $textElem;
  • this.toolbarElemId = toolbarElemId;
  • this.textElemId = textElemId;
  • // 记录输入法的开始和结束
  • var compositionEnd = true;
  • $textContainerElem.on('compositionstart', function () {
  • // 输入法开始输入
  • compositionEnd = false;
  • });
  • $textContainerElem.on('compositionend', function () {
  • // 输入法结束输入
  • compositionEnd = true;
  • });
  • // 绑定 onchange
  • $textContainerElem.on('click keyup', function () {
  • // 输入法结束才出发 onchange
  • compositionEnd && _this.change && _this.change();
  • });
  • $toolbarElem.on('click', function () {
  • this.change && this.change();
  • });
  • //绑定 onfocus 与 onblur 事件
  • if (config$$1.onfocus || config$$1.onblur) {
  • // 当前编辑器是否是焦点状态
  • this.isFocus = false;
  • $(document).on('click', function (e) {
  • //判断当前点击元素是否在编辑器内
  • var isChild = $textElem.isContain($(e.target));
  • //判断当前点击元素是否为工具栏
  • var isToolbar = $toolbarElem.isContain($(e.target));
  • var isMenu = $toolbarElem[0] == e.target ? true : false;
  • if (!isChild) {
  • //若为选择工具栏中的功能,则不视为成blur操作
  • if (isToolbar && !isMenu) {
  • return;
  • }
  • if (_this.isFocus) {
  • _this.onblur && _this.onblur();
  • }
  • _this.isFocus = false;
  • } else {
  • if (!_this.isFocus) {
  • _this.onfocus && _this.onfocus();
  • }
  • _this.isFocus = true;
  • }
  • });
  • }
  • },
  • // 封装 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(newLine) {
  • var $textElem = this.$textElem;
  • var $children = $textElem.children();
  • if (!$children.length) {
  • // 如果编辑器区域无内容,添加一个空行,重新设置选区
  • $textElem.append($('


    '
    ));
  • this.initSelection();
  • return;
  • }
  • var $last = $children.last();
  • if (newLine) {
  • // 新增一个空行
  • var html = $last.html().toLowerCase();
  • var nodeName = $last.getNodeName();
  • if (html !== '
    '
    && html !== '' || nodeName !== 'P') {
  • // 最后一个元素不是


    ,添加一个空行,重新设置选区
  • $textElem.append($('


    '
    ));
  • this.initSelection();
  • return;
  • }
  • }
  • this.selection.createRangeByElem($last, false, true);
  • this.selection.restoreSelection();
  • },
  • // 绑定事件
  • _bindEvent: function _bindEvent() {
  • // -------- 绑定 onchange 事件 --------
  • var onChangeTimeoutId = 0;
  • var beforeChangeHtml = this.txt.html();
  • var config$$1 = this.config;
  • // onchange 触发延迟时间
  • var onchangeTimeout = config$$1.onchangeTimeout;
  • onchangeTimeout = parseInt(onchangeTimeout, 10);
  • if (!onchangeTimeout || onchangeTimeout <= 0) {
  • onchangeTimeout = 200;
  • }
  • 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) {
  • // 需要比较每一个字符
  • if (currentHtml === beforeChangeHtml) {
  • return;
  • }
  • }
  • // 执行,使用节流
  • if (onChangeTimeoutId) {
  • clearTimeout(onChangeTimeoutId);
  • }
  • onChangeTimeoutId = setTimeout(function () {
  • // 触发配置的 onchange 函数
  • onchange(currentHtml);
  • beforeChangeHtml = currentHtml;
  • }, onchangeTimeout);
  • };
  • }
  • // -------- 绑定 onblur 事件 --------
  • var onblur = config$$1.onblur;
  • if (onblur && typeof onblur === 'function') {
  • this.onblur = function () {
  • var currentHtml = this.txt.html();
  • onblur(currentHtml);
  • };
  • }
  • // -------- 绑定 onfocus 事件 --------
  • var onfocus = config$$1.onfocus;
  • if (onfocus && typeof onfocus === 'function') {
  • this.onfocus = function () {
  • onfocus();
  • };
  • }
  • },
  • // 创建编辑器
  • 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(true);
  • // 绑定事件
  • this._bindEvent();
  • },
  • // 解绑所有事件(暂时不对外开放)
  • _offAllEvent: function _offAllEvent() {
  • $.offAll();
  • }
  • };
  • // 检验是否浏览器环境
  • 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: \'w-e-icon\'; 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: \'w-e-icon\' !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-icon-font:before { content: "\\ea5c";}.w-e-icon-text-heigh:before { content: "\\ea5f";}.w-e-toolbar { display: -webkit-box; display: -ms-flexbox; display: flex; padding: 0 5px; /* flex-wrap: wrap; */ /* 单个菜单 */}.w-e-toolbar .w-e-menu { position: relative; 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;}';
  • // 将 css 代码添加到