wangeditor_rails 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06cc74c13d8db55716e2d656feccbecdc39c331f
4
- data.tar.gz: 284df17541a238a99ca1c0d1e0dc6a31406b68c2
3
+ metadata.gz: 787c6640855400af65680b1da14f4ed84de66a45
4
+ data.tar.gz: dc86a8f5b1e85561425b46eba7de3fd9e9ff8c76
5
5
  SHA512:
6
- metadata.gz: 2c80f94aad64ef543fe1fe00ff9b78f1a10d30e47531508ef6a309167b667c8c7682320db10e935e45b5c127ea4c616f7040dc0a7684558075951e13d1d4be63
7
- data.tar.gz: e721de9b2aceb85a9dc93c5fd762a89363a9ba5ba7fa4e5dd470a3c7e1f0acdc0aecc3b73f1e0c50b840a1adbc9225bafb119ea9b82361e4746f299685578b45
6
+ metadata.gz: 90c0b4cfee4cf3ad95f4ae1142c55e861be8483a3353452625774a923a48d061cb8c0969b1fe9ef02e60fd198973eb1be7fc35239c4c377cb4dafc22ea4718ce
7
+ data.tar.gz: 21c40d3b47e47d9f5c4bb686cd4271e9a4b0eb6e98c49fdef22eb266aedbf35a93ee012c2c1ae8dd4e02c95754e27758b6bcbb8a9279ce64e3ed6fc6a54bc66c
data/README.md CHANGED
@@ -14,7 +14,7 @@ gem 'wangeditor_rails'
14
14
 
15
15
  2. Run install generator
16
16
  ```
17
- rails generate wangeditor_rails:install
17
+ rails generate wangeditor:install
18
18
  ```
19
19
 
20
20
  3. Add below line to your application.js
@@ -25,7 +25,7 @@ rails generate wangeditor_rails:install
25
25
  ## Usage:
26
26
  ```
27
27
  <%= form_for @post do |f| %>
28
- <%= f.text_field :title %>
28
+ <%= f.text_field :body %>
29
29
  <%= f.wangeditor :body %>
30
30
  <%= f.submit %>
31
31
  <% end %>
@@ -0,0 +1,4220 @@
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
+ /*
8
+ poly-fill
9
+ */
10
+
11
+ var polyfill = function () {
12
+
13
+ // Object.assign
14
+ if (typeof Object.assign != 'function') {
15
+ Object.assign = function (target, varArgs) {
16
+ // .length of function is 2
17
+ if (target == null) {
18
+ // TypeError if undefined or null
19
+ throw new TypeError('Cannot convert undefined or null to object');
20
+ }
21
+
22
+ var to = Object(target);
23
+
24
+ for (var index = 1; index < arguments.length; index++) {
25
+ var nextSource = arguments[index];
26
+
27
+ if (nextSource != null) {
28
+ // Skip over if undefined or null
29
+ for (var nextKey in nextSource) {
30
+ // Avoid bugs when hasOwnProperty is shadowed
31
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
32
+ to[nextKey] = nextSource[nextKey];
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return to;
38
+ };
39
+ }
40
+
41
+ // IE 中兼容 Element.prototype.matches
42
+ if (!Element.prototype.matches) {
43
+ Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function (s) {
44
+ var matches = (this.document || this.ownerDocument).querySelectorAll(s),
45
+ i = matches.length;
46
+ while (--i >= 0 && matches.item(i) !== this) {}
47
+ return i > -1;
48
+ };
49
+ }
50
+ };
51
+
52
+ /*
53
+ DOM 操作 API
54
+ */
55
+
56
+ // 根据 html 代码片段创建 dom 对象
57
+ function createElemByHTML(html) {
58
+ var div = void 0;
59
+ div = document.createElement('div');
60
+ div.innerHTML = html;
61
+ return div.children;
62
+ }
63
+
64
+ // 是否是 DOM List
65
+ function isDOMList(selector) {
66
+ if (!selector) {
67
+ return false;
68
+ }
69
+ if (selector instanceof HTMLCollection || selector instanceof NodeList) {
70
+ return true;
71
+ }
72
+ return false;
73
+ }
74
+
75
+ // 封装 document.querySelectorAll
76
+ function querySelectorAll(selector) {
77
+ var result = document.querySelectorAll(selector);
78
+ if (isDOMList(result)) {
79
+ return result;
80
+ } else {
81
+ return [result];
82
+ }
83
+ }
84
+
85
+ // 创建构造函数
86
+ function DomElement(selector) {
87
+ if (!selector) {
88
+ return;
89
+ }
90
+
91
+ // selector 本来就是 DomElement 对象,直接返回
92
+ if (selector instanceof DomElement) {
93
+ return selector;
94
+ }
95
+
96
+ this.selector = selector;
97
+ var nodeType = selector.nodeType;
98
+
99
+ // 根据 selector 得出的结果(如 DOM,DOM List)
100
+ var selectorResult = [];
101
+ if (nodeType === 9) {
102
+ // document 节点
103
+ selectorResult = [selector];
104
+ } else if (nodeType === 1) {
105
+ // 单个 DOM 节点
106
+ selectorResult = [selector];
107
+ } else if (isDOMList(selector)) {
108
+ // DOM List
109
+ selectorResult = selector;
110
+ } else if (typeof selector === 'string') {
111
+ // 字符串
112
+ selector = selector.replace('/\n/mg', '').trim();
113
+ if (selector.indexOf('<') === 0) {
114
+ // 如 <div>
115
+ selectorResult = createElemByHTML(selector);
116
+ } else {
117
+ // 如 #id .class
118
+ selectorResult = querySelectorAll(selector);
119
+ }
120
+ }
121
+
122
+ var length = selectorResult.length;
123
+ if (!length) {
124
+ // 空数组
125
+ return this;
126
+ }
127
+
128
+ // 加入 DOM 节点
129
+ var i = void 0;
130
+ for (i = 0; i < length; i++) {
131
+ this[i] = selectorResult[i];
132
+ }
133
+ this.length = length;
134
+ }
135
+
136
+ // 修改原型
137
+ DomElement.prototype = {
138
+ constructor: DomElement,
139
+
140
+ // 类数组,forEach
141
+ forEach: function forEach(fn) {
142
+ var i = void 0;
143
+ for (i = 0; i < this.length; i++) {
144
+ var elem = this[i];
145
+ var result = fn.call(elem, elem, i);
146
+ if (result === false) {
147
+ break;
148
+ }
149
+ }
150
+ return this;
151
+ },
152
+
153
+ // 获取第几个元素
154
+ get: function get(index) {
155
+ var length = this.length;
156
+ if (index >= length) {
157
+ index = index % length;
158
+ }
159
+ return $(this[index]);
160
+ },
161
+
162
+ // 第一个
163
+ first: function first() {
164
+ return this.get(0);
165
+ },
166
+
167
+ // 最后一个
168
+ last: function last() {
169
+ var length = this.length;
170
+ return this.get(length - 1);
171
+ },
172
+
173
+ // 绑定事件
174
+ on: function on(type, selector, fn) {
175
+ // selector 不为空,证明绑定事件要加代理
176
+ if (!fn) {
177
+ fn = selector;
178
+ selector = null;
179
+ }
180
+
181
+ // type 是否有多个
182
+ var types = [];
183
+ types = type.split(/\s+/);
184
+
185
+ return this.forEach(function (elem) {
186
+ types.forEach(function (type) {
187
+ if (!type) {
188
+ return;
189
+ }
190
+
191
+ if (!selector) {
192
+ // 无代理
193
+ elem.addEventListener(type, fn, false);
194
+ return;
195
+ }
196
+
197
+ // 有代理
198
+ elem.addEventListener(type, function (e) {
199
+ var target = e.target;
200
+ if (target.matches(selector)) {
201
+ fn.call(target, e);
202
+ }
203
+ }, false);
204
+ });
205
+ });
206
+ },
207
+
208
+ // 取消事件绑定
209
+ off: function off(type, fn) {
210
+ return this.forEach(function (elem) {
211
+ elem.removeEventListener(type, fn, false);
212
+ });
213
+ },
214
+
215
+ // 获取/设置 属性
216
+ attr: function attr(key, val) {
217
+ if (val == null) {
218
+ // 获取值
219
+ return this[0].getAttribute(key);
220
+ } else {
221
+ // 设置值
222
+ return this.forEach(function (elem) {
223
+ elem.setAttribute(key, val);
224
+ });
225
+ }
226
+ },
227
+
228
+ // 添加 class
229
+ addClass: function addClass(className) {
230
+ if (!className) {
231
+ return this;
232
+ }
233
+ return this.forEach(function (elem) {
234
+ var arr = void 0;
235
+ if (elem.className) {
236
+ // 解析当前 className 转换为数组
237
+ arr = elem.className.split(/\s/);
238
+ arr = arr.filter(function (item) {
239
+ return !!item.trim();
240
+ });
241
+ // 添加 class
242
+ if (arr.indexOf(className) < 0) {
243
+ arr.push(className);
244
+ }
245
+ // 修改 elem.class
246
+ elem.className = arr.join(' ');
247
+ } else {
248
+ elem.className = className;
249
+ }
250
+ });
251
+ },
252
+
253
+ // 删除 class
254
+ removeClass: function removeClass(className) {
255
+ if (!className) {
256
+ return this;
257
+ }
258
+ return this.forEach(function (elem) {
259
+ var arr = void 0;
260
+ if (elem.className) {
261
+ // 解析当前 className 转换为数组
262
+ arr = elem.className.split(/\s/);
263
+ arr = arr.filter(function (item) {
264
+ item = item.trim();
265
+ // 删除 class
266
+ if (!item || item === className) {
267
+ return false;
268
+ }
269
+ return true;
270
+ });
271
+ // 修改 elem.class
272
+ elem.className = arr.join(' ');
273
+ }
274
+ });
275
+ },
276
+
277
+ // 修改 css
278
+ css: function css(key, val) {
279
+ var currentStyle = key + ':' + val + ';';
280
+ return this.forEach(function (elem) {
281
+ var style = (elem.getAttribute('style') || '').trim();
282
+ var styleArr = void 0,
283
+ resultArr = [];
284
+ if (style) {
285
+ // 将 style 按照 ; 拆分为数组
286
+ styleArr = style.split(';');
287
+ styleArr.forEach(function (item) {
288
+ // 对每项样式,按照 : 拆分为 key 和 value
289
+ var arr = item.split(':').map(function (i) {
290
+ return i.trim();
291
+ });
292
+ if (arr.length === 2) {
293
+ resultArr.push(arr[0] + ':' + arr[1]);
294
+ }
295
+ });
296
+ // 替换或者新增
297
+ resultArr = resultArr.map(function (item) {
298
+ if (item.indexOf(key) === 0) {
299
+ return currentStyle;
300
+ } else {
301
+ return item;
302
+ }
303
+ });
304
+ if (resultArr.indexOf(currentStyle) < 0) {
305
+ resultArr.push(currentStyle);
306
+ }
307
+ // 结果
308
+ elem.setAttribute('style', resultArr.join('; '));
309
+ } else {
310
+ // style 无值
311
+ elem.setAttribute('style', currentStyle);
312
+ }
313
+ });
314
+ },
315
+
316
+ // 显示
317
+ show: function show() {
318
+ return this.css('display', 'block');
319
+ },
320
+
321
+ // 隐藏
322
+ hide: function hide() {
323
+ return this.css('display', 'none');
324
+ },
325
+
326
+ // 获取子节点
327
+ children: function children() {
328
+ var elem = this[0];
329
+ if (!elem) {
330
+ return null;
331
+ }
332
+
333
+ return $(elem.children);
334
+ },
335
+
336
+ // 增加子节点
337
+ append: function append($children) {
338
+ return this.forEach(function (elem) {
339
+ $children.forEach(function (child) {
340
+ elem.appendChild(child);
341
+ });
342
+ });
343
+ },
344
+
345
+ // 移除当前节点
346
+ remove: function remove() {
347
+ return this.forEach(function (elem) {
348
+ if (elem.remove) {
349
+ elem.remove();
350
+ } else {
351
+ var parent = elem.parentElement;
352
+ parent && parent.removeChild(elem);
353
+ }
354
+ });
355
+ },
356
+
357
+ // 是否包含某个子节点
358
+ isContain: function isContain($child) {
359
+ var elem = this[0];
360
+ var child = $child[0];
361
+ return elem.contains(child);
362
+ },
363
+
364
+ // 尺寸数据
365
+ getSizeData: function getSizeData() {
366
+ var elem = this[0];
367
+ return elem.getBoundingClientRect(); // 可得到 bottom height left right top width 的数据
368
+ },
369
+
370
+ // 封装 nodeName
371
+ getNodeName: function getNodeName() {
372
+ var elem = this[0];
373
+ return elem.nodeName;
374
+ },
375
+
376
+ // 从当前元素查找
377
+ find: function find(selector) {
378
+ var elem = this[0];
379
+ return $(elem.querySelectorAll(selector));
380
+ },
381
+
382
+ // 获取当前元素的 text
383
+ text: function text(val) {
384
+ if (!val) {
385
+ // 获取 text
386
+ var elem = this[0];
387
+ return elem.innerHTML.replace(/<.*?>/g, function () {
388
+ return '';
389
+ });
390
+ } else {
391
+ // 设置 text
392
+ return this.forEach(function (elem) {
393
+ elem.innerHTML = val;
394
+ });
395
+ }
396
+ },
397
+
398
+ // 获取 html
399
+ html: function html(value) {
400
+ var elem = this[0];
401
+ if (value == null) {
402
+ return elem.innerHTML;
403
+ } else {
404
+ elem.innerHTML = value;
405
+ return this;
406
+ }
407
+ },
408
+
409
+ // 获取 value
410
+ val: function val() {
411
+ var elem = this[0];
412
+ return elem.value.trim();
413
+ },
414
+
415
+ // focus
416
+ focus: function focus() {
417
+ return this.forEach(function (elem) {
418
+ elem.focus();
419
+ });
420
+ },
421
+
422
+ // parent
423
+ parent: function parent() {
424
+ var elem = this[0];
425
+ return $(elem.parentElement);
426
+ },
427
+
428
+ // parentUntil 找到符合 selector 的父节点
429
+ parentUntil: function parentUntil(selector, _currentElem) {
430
+ var results = document.querySelectorAll(selector);
431
+ var length = results.length;
432
+ if (!length) {
433
+ // 传入的 selector 无效
434
+ return null;
435
+ }
436
+
437
+ var elem = _currentElem || this[0];
438
+ if (elem.nodeName === 'BODY') {
439
+ return null;
440
+ }
441
+
442
+ var parent = elem.parentElement;
443
+ var i = void 0;
444
+ for (i = 0; i < length; i++) {
445
+ if (parent === results[i]) {
446
+ // 找到,并返回
447
+ return $(parent);
448
+ }
449
+ }
450
+
451
+ // 继续查找
452
+ return this.parentUntil(selector, parent);
453
+ },
454
+
455
+ // 判断两个 elem 是否相等
456
+ equal: function equal($elem) {
457
+ if ($elem.nodeType === 1) {
458
+ return this[0] === $elem;
459
+ } else {
460
+ return this[0] === $elem[0];
461
+ }
462
+ },
463
+
464
+ // 将该元素插入到某个元素前面
465
+ insertBefore: function insertBefore(selector) {
466
+ var $referenceNode = $(selector);
467
+ var referenceNode = $referenceNode[0];
468
+ if (!referenceNode) {
469
+ return this;
470
+ }
471
+ return this.forEach(function (elem) {
472
+ var parent = referenceNode.parentNode;
473
+ parent.insertBefore(elem, referenceNode);
474
+ });
475
+ },
476
+
477
+ // 将该元素插入到某个元素后面
478
+ insertAfter: function insertAfter(selector) {
479
+ var $referenceNode = $(selector);
480
+ var referenceNode = $referenceNode[0];
481
+ if (!referenceNode) {
482
+ return this;
483
+ }
484
+ return this.forEach(function (elem) {
485
+ var parent = referenceNode.parentNode;
486
+ if (parent.lastChild === referenceNode) {
487
+ // 最后一个元素
488
+ parent.appendChild(elem);
489
+ } else {
490
+ // 不是最后一个元素
491
+ parent.insertBefore(elem, referenceNode.nextSibling);
492
+ }
493
+ });
494
+ }
495
+ };
496
+
497
+ // new 一个对象
498
+ function $(selector) {
499
+ return new DomElement(selector);
500
+ }
501
+
502
+ /*
503
+ 配置信息
504
+ */
505
+
506
+ var config = {
507
+
508
+ // 默认菜单配置
509
+ menus: ['head', 'bold', 'italic', 'underline', 'strikeThrough', 'foreColor', 'backColor', 'link', 'list', 'justify', 'quote', 'emoticon', 'image', 'table', 'video', 'code', 'undo', 'redo'],
510
+
511
+ // // 语言配置
512
+ // lang: {
513
+ // '设置标题': 'title',
514
+ // '正文': 'p',
515
+ // '链接文字': 'link text',
516
+ // '链接': 'link',
517
+ // '插入': 'insert',
518
+ // '创建': 'init'
519
+ // },
520
+
521
+ // 编辑区域的 z-index
522
+ zIndex: 10000,
523
+
524
+ // 是否开启 debug 模式(debug 模式下错误会 throw error 形式抛出)
525
+ debug: false,
526
+
527
+ // 插入链接时候的格式校验
528
+ linkCheck: function linkCheck(text, link) {
529
+ // text 是插入的文字
530
+ // link 是插入的链接
531
+ return true; // 返回 true 即表示成功
532
+ // return '校验失败' // 返回字符串即表示失败的提示信息
533
+ },
534
+
535
+ // 粘贴过滤样式,默认开启
536
+ pasteFilterStyle: true,
537
+
538
+ // onchange 事件
539
+ // onchange: function (html) {
540
+ // // html 即变化之后的内容
541
+ // console.log(html)
542
+ // },
543
+
544
+ // 是否显示添加网络图片的 tab
545
+ showLinkImg: true,
546
+
547
+ // 插入网络图片的回调
548
+ linkImgCallback: function linkImgCallback(url) {
549
+ // console.log(url) // url 即插入图片的地址
550
+ },
551
+
552
+ // 默认上传图片 max size: 5M
553
+ uploadImgMaxSize: 5 * 1024 * 1024,
554
+
555
+ // 配置一次最多上传几个图片
556
+ // uploadImgMaxLength: 5,
557
+
558
+ // 上传图片,是否显示 base64 格式
559
+ uploadImgShowBase64: false,
560
+
561
+ // 上传图片,server 地址(如果有值,则 base64 格式的配置则失效)
562
+ // uploadImgServer: '/upload',
563
+
564
+ // 自定义配置 filename
565
+ uploadFileName: '',
566
+
567
+ // 上传图片的自定义参数
568
+ uploadImgParams: {
569
+ // token: 'abcdef12345'
570
+ },
571
+
572
+ // 上传图片的自定义header
573
+ uploadImgHeaders: {
574
+ // 'Accept': 'text/x-json'
575
+ },
576
+
577
+ // 配置 XHR withCredentials
578
+ withCredentials: false,
579
+
580
+ // 自定义上传图片超时时间 ms
581
+ uploadImgTimeout: 10000,
582
+
583
+ // 上传图片 hook
584
+ uploadImgHooks: {
585
+ // customInsert: function (insertLinkImg, result, editor) {
586
+ // console.log('customInsert')
587
+ // // 图片上传并返回结果,自定义插入图片的事件,而不是编辑器自动插入图片
588
+ // const data = result.data1 || []
589
+ // data.forEach(link => {
590
+ // insertLinkImg(link)
591
+ // })
592
+ // },
593
+ before: function before(xhr, editor, files) {
594
+ // 图片上传之前触发
595
+
596
+ // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
597
+ // return {
598
+ // prevent: true,
599
+ // msg: '放弃上传'
600
+ // }
601
+ },
602
+ success: function success(xhr, editor, result) {
603
+ // 图片上传并返回结果,图片插入成功之后触发
604
+ },
605
+ fail: function fail(xhr, editor, result) {
606
+ // 图片上传并返回结果,但图片插入错误时触发
607
+ },
608
+ error: function error(xhr, editor) {
609
+ // 图片上传出错时触发
610
+ },
611
+ timeout: function timeout(xhr, editor) {
612
+ // 图片上传超时时触发
613
+ }
614
+ },
615
+
616
+ // 是否上传七牛云,默认为 false
617
+ qiniu: false
618
+
619
+ };
620
+
621
+ /*
622
+ 工具
623
+ */
624
+
625
+ // 和 UA 相关的属性
626
+ var UA = {
627
+ _ua: navigator.userAgent,
628
+
629
+ // 是否 webkit
630
+ isWebkit: function isWebkit() {
631
+ var reg = /webkit/i;
632
+ return reg.test(this._ua);
633
+ },
634
+
635
+ // 是否 IE
636
+ isIE: function isIE() {
637
+ return 'ActiveXObject' in window;
638
+ }
639
+ };
640
+
641
+ // 遍历对象
642
+ function objForEach(obj, fn) {
643
+ var key = void 0,
644
+ result = void 0;
645
+ for (key in obj) {
646
+ if (obj.hasOwnProperty(key)) {
647
+ result = fn.call(obj, key, obj[key]);
648
+ if (result === false) {
649
+ break;
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ // 遍历类数组
656
+ function arrForEach(fakeArr, fn) {
657
+ var i = void 0,
658
+ item = void 0,
659
+ result = void 0;
660
+ var length = fakeArr.length || 0;
661
+ for (i = 0; i < length; i++) {
662
+ item = fakeArr[i];
663
+ result = fn.call(fakeArr, item, i);
664
+ if (result === false) {
665
+ break;
666
+ }
667
+ }
668
+ }
669
+
670
+ // 获取随机数
671
+ function getRandom(prefix) {
672
+ return prefix + Math.random().toString().slice(2);
673
+ }
674
+
675
+ // 替换 html 特殊字符
676
+ function replaceHtmlSymbol(html) {
677
+ if (html == null) {
678
+ return '';
679
+ }
680
+ return html.replace(/</gm, '&lt;').replace(/>/gm, '&gt;').replace(/"/gm, '&quot;');
681
+ }
682
+
683
+ // 返回百分比的格式
684
+
685
+ /*
686
+ bold-menu
687
+ */
688
+ // 构造函数
689
+ function Bold(editor) {
690
+ this.editor = editor;
691
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-bold"><i/>\n </div>');
692
+ this.type = 'click';
693
+
694
+ // 当前是否 active 状态
695
+ this._active = false;
696
+ }
697
+
698
+ // 原型
699
+ Bold.prototype = {
700
+ constructor: Bold,
701
+
702
+ // 点击事件
703
+ onClick: function onClick(e) {
704
+ // 点击菜单将触发这里
705
+
706
+ var editor = this.editor;
707
+ var isSeleEmpty = editor.selection.isSelectionEmpty();
708
+
709
+ if (isSeleEmpty) {
710
+ // 选区是空的,插入并选中一个“空白”
711
+ editor.selection.createEmptyRange();
712
+ }
713
+
714
+ // 执行 bold 命令
715
+ editor.cmd.do('bold');
716
+
717
+ if (isSeleEmpty) {
718
+ // 需要将选取折叠起来
719
+ editor.selection.collapseRange();
720
+ editor.selection.restoreSelection();
721
+ }
722
+ },
723
+
724
+ // 试图改变 active 状态
725
+ tryChangeActive: function tryChangeActive(e) {
726
+ var editor = this.editor;
727
+ var $elem = this.$elem;
728
+ if (editor.cmd.queryCommandState('bold')) {
729
+ this._active = true;
730
+ $elem.addClass('w-e-active');
731
+ } else {
732
+ this._active = false;
733
+ $elem.removeClass('w-e-active');
734
+ }
735
+ }
736
+ };
737
+
738
+ /*
739
+ 替换多语言
740
+ */
741
+
742
+ var replaceLang = function (editor, str) {
743
+ var langArgs = editor.config.langArgs || [];
744
+ var result = str;
745
+
746
+ langArgs.forEach(function (item) {
747
+ var reg = item.reg;
748
+ var val = item.val;
749
+
750
+ if (reg.test(result)) {
751
+ result = result.replace(reg, function () {
752
+ return val;
753
+ });
754
+ }
755
+ });
756
+
757
+ return result;
758
+ };
759
+
760
+ /*
761
+ droplist
762
+ */
763
+ var _emptyFn = function _emptyFn() {};
764
+
765
+ // 构造函数
766
+ function DropList(menu, opt) {
767
+ var _this = this;
768
+
769
+ // droplist 所依附的菜单
770
+ var editor = menu.editor;
771
+ this.menu = menu;
772
+ this.opt = opt;
773
+ // 容器
774
+ var $container = $('<div class="w-e-droplist"></div>');
775
+
776
+ // 标题
777
+ var $title = opt.$title;
778
+ var titleHtml = void 0;
779
+ if ($title) {
780
+ // 替换多语言
781
+ titleHtml = $title.html();
782
+ titleHtml = replaceLang(editor, titleHtml);
783
+ $title.html(titleHtml);
784
+
785
+ $title.addClass('w-e-dp-title');
786
+ $container.append($title);
787
+ }
788
+
789
+ var list = opt.list || [];
790
+ var type = opt.type || 'list'; // 'list' 列表形式(如“标题”菜单) / 'inline-block' 块状形式(如“颜色”菜单)
791
+ var onClick = opt.onClick || _emptyFn;
792
+
793
+ // 加入 DOM 并绑定事件
794
+ var $list = $('<ul class="' + (type === 'list' ? 'w-e-list' : 'w-e-block') + '"></ul>');
795
+ $container.append($list);
796
+ list.forEach(function (item) {
797
+ var $elem = item.$elem;
798
+
799
+ // 替换多语言
800
+ var elemHtml = $elem.html();
801
+ elemHtml = replaceLang(editor, elemHtml);
802
+ $elem.html(elemHtml);
803
+
804
+ var value = item.value;
805
+ var $li = $('<li class="w-e-item"></li>');
806
+ if ($elem) {
807
+ $li.append($elem);
808
+ $list.append($li);
809
+ $elem.on('click', function (e) {
810
+ onClick(value);
811
+
812
+ // 隐藏
813
+ _this.hideTimeoutId = setTimeout(function () {
814
+ _this.hide();
815
+ }, 0);
816
+ });
817
+ }
818
+ });
819
+
820
+ // 绑定隐藏事件
821
+ $container.on('mouseleave', function (e) {
822
+ _this.hideTimeoutId = setTimeout(function () {
823
+ _this.hide();
824
+ }, 0);
825
+ });
826
+
827
+ // 记录属性
828
+ this.$container = $container;
829
+
830
+ // 基本属性
831
+ this._rendered = false;
832
+ this._show = false;
833
+ }
834
+
835
+ // 原型
836
+ DropList.prototype = {
837
+ constructor: DropList,
838
+
839
+ // 显示(插入DOM)
840
+ show: function show() {
841
+ if (this.hideTimeoutId) {
842
+ // 清除之前的定时隐藏
843
+ clearTimeout(this.hideTimeoutId);
844
+ }
845
+
846
+ var menu = this.menu;
847
+ var $menuELem = menu.$elem;
848
+ var $container = this.$container;
849
+ if (this._show) {
850
+ return;
851
+ }
852
+ if (this._rendered) {
853
+ // 显示
854
+ $container.show();
855
+ } else {
856
+ // 加入 DOM 之前先定位位置
857
+ var menuHeight = $menuELem.getSizeData().height || 0;
858
+ var width = this.opt.width || 100; // 默认为 100
859
+ $container.css('margin-top', menuHeight + 'px').css('width', width + 'px');
860
+
861
+ // 加入到 DOM
862
+ $menuELem.append($container);
863
+ this._rendered = true;
864
+ }
865
+
866
+ // 修改属性
867
+ this._show = true;
868
+ },
869
+
870
+ // 隐藏(移除DOM)
871
+ hide: function hide() {
872
+ if (this.showTimeoutId) {
873
+ // 清除之前的定时显示
874
+ clearTimeout(this.showTimeoutId);
875
+ }
876
+
877
+ var $container = this.$container;
878
+ if (!this._show) {
879
+ return;
880
+ }
881
+ // 隐藏并需改属性
882
+ $container.hide();
883
+ this._show = false;
884
+ }
885
+ };
886
+
887
+ /*
888
+ menu - header
889
+ */
890
+ // 构造函数
891
+ function Head(editor) {
892
+ var _this = this;
893
+
894
+ this.editor = editor;
895
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-header"><i/></div>');
896
+ this.type = 'droplist';
897
+
898
+ // 当前是否 active 状态
899
+ this._active = false;
900
+
901
+ // 初始化 droplist
902
+ this.droplist = new DropList(this, {
903
+ width: 100,
904
+ $title: $('<p>设置标题</p>'),
905
+ type: 'list', // droplist 以列表形式展示
906
+ list: [{ $elem: $('<h1>H1</h1>'), value: '<h1>' }, { $elem: $('<h2>H2</h2>'), value: '<h2>' }, { $elem: $('<h3>H3</h3>'), value: '<h3>' }, { $elem: $('<h4>H4</h4>'), value: '<h4>' }, { $elem: $('<h5>H5</h5>'), value: '<h5>' }, { $elem: $('<p>正文</p>'), value: '<p>' }],
907
+ onClick: function onClick(value) {
908
+ // 注意 this 是指向当前的 Head 对象
909
+ _this._command(value);
910
+ }
911
+ });
912
+ }
913
+
914
+ // 原型
915
+ Head.prototype = {
916
+ constructor: Head,
917
+
918
+ // 执行命令
919
+ _command: function _command(value) {
920
+ var editor = this.editor;
921
+
922
+ var $selectionElem = editor.selection.getSelectionContainerElem();
923
+ if (editor.$textElem.equal($selectionElem)) {
924
+ // 不能选中多行来设置标题,否则会出现问题
925
+ // 例如选中的是 <p>xxx</p><p>yyy</p> 来设置标题,设置之后会成为 <h1>xxx<br>yyy</h1> 不符合预期
926
+ return;
927
+ }
928
+
929
+ editor.cmd.do('formatBlock', value);
930
+ },
931
+
932
+ // 试图改变 active 状态
933
+ tryChangeActive: function tryChangeActive(e) {
934
+ var editor = this.editor;
935
+ var $elem = this.$elem;
936
+ var reg = /^h/i;
937
+ var cmdValue = editor.cmd.queryCommandValue('formatBlock');
938
+ if (reg.test(cmdValue)) {
939
+ this._active = true;
940
+ $elem.addClass('w-e-active');
941
+ } else {
942
+ this._active = false;
943
+ $elem.removeClass('w-e-active');
944
+ }
945
+ }
946
+ };
947
+
948
+ /*
949
+ panel
950
+ */
951
+
952
+ var emptyFn = function emptyFn() {};
953
+
954
+ // 记录已经显示 panel 的菜单
955
+ var _isCreatedPanelMenus = [];
956
+
957
+ // 构造函数
958
+ function Panel(menu, opt) {
959
+ this.menu = menu;
960
+ this.opt = opt;
961
+ }
962
+
963
+ // 原型
964
+ Panel.prototype = {
965
+ constructor: Panel,
966
+
967
+ // 显示(插入DOM)
968
+ show: function show() {
969
+ var _this = this;
970
+
971
+ var menu = this.menu;
972
+ if (_isCreatedPanelMenus.indexOf(menu) >= 0) {
973
+ // 该菜单已经创建了 panel 不能再创建
974
+ return;
975
+ }
976
+
977
+ var editor = menu.editor;
978
+ var $body = $('body');
979
+ var $textContainerElem = editor.$textContainerElem;
980
+ var opt = this.opt;
981
+
982
+ // panel 的容器
983
+ var $container = $('<div class="w-e-panel-container"></div>');
984
+ var width = opt.width || 300; // 默认 300px
985
+ $container.css('width', width + 'px').css('margin-left', (0 - width) / 2 + 'px');
986
+
987
+ // 添加关闭按钮
988
+ var $closeBtn = $('<i class="w-e-icon-close w-e-panel-close"></i>');
989
+ $container.append($closeBtn);
990
+ $closeBtn.on('click', function () {
991
+ _this.hide();
992
+ });
993
+
994
+ // 准备 tabs 容器
995
+ var $tabTitleContainer = $('<ul class="w-e-panel-tab-title"></ul>');
996
+ var $tabContentContainer = $('<div class="w-e-panel-tab-content"></div>');
997
+ $container.append($tabTitleContainer).append($tabContentContainer);
998
+
999
+ // 设置高度
1000
+ var height = opt.height;
1001
+ if (height) {
1002
+ $tabContentContainer.css('height', height + 'px').css('overflow-y', 'auto');
1003
+ }
1004
+
1005
+ // tabs
1006
+ var tabs = opt.tabs || [];
1007
+ var tabTitleArr = [];
1008
+ var tabContentArr = [];
1009
+ tabs.forEach(function (tab, tabIndex) {
1010
+ if (!tab) {
1011
+ return;
1012
+ }
1013
+ var title = tab.title || '';
1014
+ var tpl = tab.tpl || '';
1015
+
1016
+ // 替换多语言
1017
+ title = replaceLang(editor, title);
1018
+ tpl = replaceLang(editor, tpl);
1019
+
1020
+ // 添加到 DOM
1021
+ var $title = $('<li class="w-e-item">' + title + '</li>');
1022
+ $tabTitleContainer.append($title);
1023
+ var $content = $(tpl);
1024
+ $tabContentContainer.append($content);
1025
+
1026
+ // 记录到内存
1027
+ $title._index = tabIndex;
1028
+ tabTitleArr.push($title);
1029
+ tabContentArr.push($content);
1030
+
1031
+ // 设置 active 项
1032
+ if (tabIndex === 0) {
1033
+ $title._active = true;
1034
+ $title.addClass('w-e-active');
1035
+ } else {
1036
+ $content.hide();
1037
+ }
1038
+
1039
+ // 绑定 tab 的事件
1040
+ $title.on('click', function (e) {
1041
+ if ($title._active) {
1042
+ return;
1043
+ }
1044
+ // 隐藏所有的 tab
1045
+ tabTitleArr.forEach(function ($title) {
1046
+ $title._active = false;
1047
+ $title.removeClass('w-e-active');
1048
+ });
1049
+ tabContentArr.forEach(function ($content) {
1050
+ $content.hide();
1051
+ });
1052
+
1053
+ // 显示当前的 tab
1054
+ $title._active = true;
1055
+ $title.addClass('w-e-active');
1056
+ $content.show();
1057
+ });
1058
+ });
1059
+
1060
+ // 绑定关闭事件
1061
+ $container.on('click', function (e) {
1062
+ // 点击时阻止冒泡
1063
+ e.stopPropagation();
1064
+ });
1065
+ $body.on('click', function (e) {
1066
+ _this.hide();
1067
+ });
1068
+
1069
+ // 添加到 DOM
1070
+ $textContainerElem.append($container);
1071
+
1072
+ // 绑定 opt 的事件,只有添加到 DOM 之后才能绑定成功
1073
+ tabs.forEach(function (tab, index) {
1074
+ if (!tab) {
1075
+ return;
1076
+ }
1077
+ var events = tab.events || [];
1078
+ events.forEach(function (event) {
1079
+ var selector = event.selector;
1080
+ var type = event.type;
1081
+ var fn = event.fn || emptyFn;
1082
+ var $content = tabContentArr[index];
1083
+ $content.find(selector).on(type, function (e) {
1084
+ e.stopPropagation();
1085
+ var needToHide = fn(e);
1086
+ // 执行完事件之后,是否要关闭 panel
1087
+ if (needToHide) {
1088
+ _this.hide();
1089
+ }
1090
+ });
1091
+ });
1092
+ });
1093
+
1094
+ // focus 第一个 elem
1095
+ var $inputs = $container.find('input[type=text],textarea');
1096
+ if ($inputs.length) {
1097
+ $inputs.get(0).focus();
1098
+ }
1099
+
1100
+ // 添加到属性
1101
+ this.$container = $container;
1102
+
1103
+ // 隐藏其他 panel
1104
+ this._hideOtherPanels();
1105
+ // 记录该 menu 已经创建了 panel
1106
+ _isCreatedPanelMenus.push(menu);
1107
+ },
1108
+
1109
+ // 隐藏(移除DOM)
1110
+ hide: function hide() {
1111
+ var menu = this.menu;
1112
+ var $container = this.$container;
1113
+ if ($container) {
1114
+ $container.remove();
1115
+ }
1116
+
1117
+ // 将该 menu 记录中移除
1118
+ _isCreatedPanelMenus = _isCreatedPanelMenus.filter(function (item) {
1119
+ if (item === menu) {
1120
+ return false;
1121
+ } else {
1122
+ return true;
1123
+ }
1124
+ });
1125
+ },
1126
+
1127
+ // 一个 panel 展示时,隐藏其他 panel
1128
+ _hideOtherPanels: function _hideOtherPanels() {
1129
+ if (!_isCreatedPanelMenus.length) {
1130
+ return;
1131
+ }
1132
+ _isCreatedPanelMenus.forEach(function (menu) {
1133
+ var panel = menu.panel || {};
1134
+ if (panel.hide) {
1135
+ panel.hide();
1136
+ }
1137
+ });
1138
+ }
1139
+ };
1140
+
1141
+ /*
1142
+ menu - link
1143
+ */
1144
+ // 构造函数
1145
+ function Link(editor) {
1146
+ this.editor = editor;
1147
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-link"><i/></div>');
1148
+ this.type = 'panel';
1149
+
1150
+ // 当前是否 active 状态
1151
+ this._active = false;
1152
+ }
1153
+
1154
+ // 原型
1155
+ Link.prototype = {
1156
+ constructor: Link,
1157
+
1158
+ // 点击事件
1159
+ onClick: function onClick(e) {
1160
+ var editor = this.editor;
1161
+ var $linkelem = void 0;
1162
+
1163
+ if (this._active) {
1164
+ // 当前选区在链接里面
1165
+ $linkelem = editor.selection.getSelectionContainerElem();
1166
+ if (!$linkelem) {
1167
+ return;
1168
+ }
1169
+ // 将该元素都包含在选取之内,以便后面整体替换
1170
+ editor.selection.createRangeByElem($linkelem);
1171
+ editor.selection.restoreSelection();
1172
+ // 显示 panel
1173
+ this._createPanel($linkelem.text(), $linkelem.attr('href'));
1174
+ } else {
1175
+ // 当前选区不在链接里面
1176
+ if (editor.selection.isSelectionEmpty()) {
1177
+ // 选区是空的,未选中内容
1178
+ this._createPanel('', '');
1179
+ } else {
1180
+ // 选中内容了
1181
+ this._createPanel(editor.selection.getSelectionText(), '');
1182
+ }
1183
+ }
1184
+ },
1185
+
1186
+ // 创建 panel
1187
+ _createPanel: function _createPanel(text, link) {
1188
+ var _this = this;
1189
+
1190
+ // panel 中需要用到的id
1191
+ var inputLinkId = getRandom('input-link');
1192
+ var inputTextId = getRandom('input-text');
1193
+ var btnOkId = getRandom('btn-ok');
1194
+ var btnDelId = getRandom('btn-del');
1195
+
1196
+ // 是否显示“删除链接”
1197
+ var delBtnDisplay = this._active ? 'inline-block' : 'none';
1198
+
1199
+ // 初始化并显示 panel
1200
+ var panel = new Panel(this, {
1201
+ width: 300,
1202
+ // panel 中可包含多个 tab
1203
+ tabs: [{
1204
+ // tab 的标题
1205
+ title: '链接',
1206
+ // 模板
1207
+ tpl: '<div>\n <input id="' + inputTextId + '" type="text" class="block" value="' + text + '" placeholder="\u94FE\u63A5\u6587\u5B57"/></td>\n <input id="' + inputLinkId + '" type="text" class="block" value="' + link + '" placeholder="http://..."/></td>\n <div class="w-e-button-container">\n <button id="' + btnOkId + '" class="right">\u63D2\u5165</button>\n <button id="' + btnDelId + '" class="gray right" style="display:' + delBtnDisplay + '">\u5220\u9664\u94FE\u63A5</button>\n </div>\n </div>',
1208
+ // 事件绑定
1209
+ events: [
1210
+ // 插入链接
1211
+ {
1212
+ selector: '#' + btnOkId,
1213
+ type: 'click',
1214
+ fn: function fn() {
1215
+ // 执行插入链接
1216
+ var $link = $('#' + inputLinkId);
1217
+ var $text = $('#' + inputTextId);
1218
+ var link = $link.val();
1219
+ var text = $text.val();
1220
+ _this._insertLink(text, link);
1221
+
1222
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
1223
+ return true;
1224
+ }
1225
+ },
1226
+ // 删除链接
1227
+ {
1228
+ selector: '#' + btnDelId,
1229
+ type: 'click',
1230
+ fn: function fn() {
1231
+ // 执行删除链接
1232
+ _this._delLink();
1233
+
1234
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
1235
+ return true;
1236
+ }
1237
+ }]
1238
+ } // tab end
1239
+ ] // tabs end
1240
+ });
1241
+
1242
+ // 显示 panel
1243
+ panel.show();
1244
+
1245
+ // 记录属性
1246
+ this.panel = panel;
1247
+ },
1248
+
1249
+ // 删除当前链接
1250
+ _delLink: function _delLink() {
1251
+ if (!this._active) {
1252
+ return;
1253
+ }
1254
+ var editor = this.editor;
1255
+ var $selectionELem = editor.selection.getSelectionContainerElem();
1256
+ if (!$selectionELem) {
1257
+ return;
1258
+ }
1259
+ var selectionText = editor.selection.getSelectionText();
1260
+ editor.cmd.do('insertHTML', '<span>' + selectionText + '</span>');
1261
+ },
1262
+
1263
+ // 插入链接
1264
+ _insertLink: function _insertLink(text, link) {
1265
+ if (!text || !link) {
1266
+ return;
1267
+ }
1268
+ var editor = this.editor;
1269
+ var config = editor.config;
1270
+ var linkCheck = config.linkCheck;
1271
+ var checkResult = true; // 默认为 true
1272
+ if (linkCheck && typeof linkCheck === 'function') {
1273
+ checkResult = linkCheck(text, link);
1274
+ }
1275
+ if (checkResult === true) {
1276
+ editor.cmd.do('insertHTML', '<a href="' + link + '" target="_blank">' + text + '</a>');
1277
+ } else {
1278
+ alert(checkResult);
1279
+ }
1280
+ },
1281
+
1282
+ // 试图改变 active 状态
1283
+ tryChangeActive: function tryChangeActive(e) {
1284
+ var editor = this.editor;
1285
+ var $elem = this.$elem;
1286
+ var $selectionELem = editor.selection.getSelectionContainerElem();
1287
+ if (!$selectionELem) {
1288
+ return;
1289
+ }
1290
+ if ($selectionELem.getNodeName() === 'A') {
1291
+ this._active = true;
1292
+ $elem.addClass('w-e-active');
1293
+ } else {
1294
+ this._active = false;
1295
+ $elem.removeClass('w-e-active');
1296
+ }
1297
+ }
1298
+ };
1299
+
1300
+ /*
1301
+ italic-menu
1302
+ */
1303
+ // 构造函数
1304
+ function Italic(editor) {
1305
+ this.editor = editor;
1306
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-italic"><i/>\n </div>');
1307
+ this.type = 'click';
1308
+
1309
+ // 当前是否 active 状态
1310
+ this._active = false;
1311
+ }
1312
+
1313
+ // 原型
1314
+ Italic.prototype = {
1315
+ constructor: Italic,
1316
+
1317
+ // 点击事件
1318
+ onClick: function onClick(e) {
1319
+ // 点击菜单将触发这里
1320
+
1321
+ var editor = this.editor;
1322
+ var isSeleEmpty = editor.selection.isSelectionEmpty();
1323
+
1324
+ if (isSeleEmpty) {
1325
+ // 选区是空的,插入并选中一个“空白”
1326
+ editor.selection.createEmptyRange();
1327
+ }
1328
+
1329
+ // 执行 italic 命令
1330
+ editor.cmd.do('italic');
1331
+
1332
+ if (isSeleEmpty) {
1333
+ // 需要将选取折叠起来
1334
+ editor.selection.collapseRange();
1335
+ editor.selection.restoreSelection();
1336
+ }
1337
+ },
1338
+
1339
+ // 试图改变 active 状态
1340
+ tryChangeActive: function tryChangeActive(e) {
1341
+ var editor = this.editor;
1342
+ var $elem = this.$elem;
1343
+ if (editor.cmd.queryCommandState('italic')) {
1344
+ this._active = true;
1345
+ $elem.addClass('w-e-active');
1346
+ } else {
1347
+ this._active = false;
1348
+ $elem.removeClass('w-e-active');
1349
+ }
1350
+ }
1351
+ };
1352
+
1353
+ /*
1354
+ redo-menu
1355
+ */
1356
+ // 构造函数
1357
+ function Redo(editor) {
1358
+ this.editor = editor;
1359
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-redo"><i/>\n </div>');
1360
+ this.type = 'click';
1361
+
1362
+ // 当前是否 active 状态
1363
+ this._active = false;
1364
+ }
1365
+
1366
+ // 原型
1367
+ Redo.prototype = {
1368
+ constructor: Redo,
1369
+
1370
+ // 点击事件
1371
+ onClick: function onClick(e) {
1372
+ // 点击菜单将触发这里
1373
+
1374
+ var editor = this.editor;
1375
+
1376
+ // 执行 redo 命令
1377
+ editor.cmd.do('redo');
1378
+ }
1379
+ };
1380
+
1381
+ /*
1382
+ strikeThrough-menu
1383
+ */
1384
+ // 构造函数
1385
+ function StrikeThrough(editor) {
1386
+ this.editor = editor;
1387
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-strikethrough"><i/>\n </div>');
1388
+ this.type = 'click';
1389
+
1390
+ // 当前是否 active 状态
1391
+ this._active = false;
1392
+ }
1393
+
1394
+ // 原型
1395
+ StrikeThrough.prototype = {
1396
+ constructor: StrikeThrough,
1397
+
1398
+ // 点击事件
1399
+ onClick: function onClick(e) {
1400
+ // 点击菜单将触发这里
1401
+
1402
+ var editor = this.editor;
1403
+ var isSeleEmpty = editor.selection.isSelectionEmpty();
1404
+
1405
+ if (isSeleEmpty) {
1406
+ // 选区是空的,插入并选中一个“空白”
1407
+ editor.selection.createEmptyRange();
1408
+ }
1409
+
1410
+ // 执行 strikeThrough 命令
1411
+ editor.cmd.do('strikeThrough');
1412
+
1413
+ if (isSeleEmpty) {
1414
+ // 需要将选取折叠起来
1415
+ editor.selection.collapseRange();
1416
+ editor.selection.restoreSelection();
1417
+ }
1418
+ },
1419
+
1420
+ // 试图改变 active 状态
1421
+ tryChangeActive: function tryChangeActive(e) {
1422
+ var editor = this.editor;
1423
+ var $elem = this.$elem;
1424
+ if (editor.cmd.queryCommandState('strikeThrough')) {
1425
+ this._active = true;
1426
+ $elem.addClass('w-e-active');
1427
+ } else {
1428
+ this._active = false;
1429
+ $elem.removeClass('w-e-active');
1430
+ }
1431
+ }
1432
+ };
1433
+
1434
+ /*
1435
+ underline-menu
1436
+ */
1437
+ // 构造函数
1438
+ function Underline(editor) {
1439
+ this.editor = editor;
1440
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-underline"><i/>\n </div>');
1441
+ this.type = 'click';
1442
+
1443
+ // 当前是否 active 状态
1444
+ this._active = false;
1445
+ }
1446
+
1447
+ // 原型
1448
+ Underline.prototype = {
1449
+ constructor: Underline,
1450
+
1451
+ // 点击事件
1452
+ onClick: function onClick(e) {
1453
+ // 点击菜单将触发这里
1454
+
1455
+ var editor = this.editor;
1456
+ var isSeleEmpty = editor.selection.isSelectionEmpty();
1457
+
1458
+ if (isSeleEmpty) {
1459
+ // 选区是空的,插入并选中一个“空白”
1460
+ editor.selection.createEmptyRange();
1461
+ }
1462
+
1463
+ // 执行 underline 命令
1464
+ editor.cmd.do('underline');
1465
+
1466
+ if (isSeleEmpty) {
1467
+ // 需要将选取折叠起来
1468
+ editor.selection.collapseRange();
1469
+ editor.selection.restoreSelection();
1470
+ }
1471
+ },
1472
+
1473
+ // 试图改变 active 状态
1474
+ tryChangeActive: function tryChangeActive(e) {
1475
+ var editor = this.editor;
1476
+ var $elem = this.$elem;
1477
+ if (editor.cmd.queryCommandState('underline')) {
1478
+ this._active = true;
1479
+ $elem.addClass('w-e-active');
1480
+ } else {
1481
+ this._active = false;
1482
+ $elem.removeClass('w-e-active');
1483
+ }
1484
+ }
1485
+ };
1486
+
1487
+ /*
1488
+ undo-menu
1489
+ */
1490
+ // 构造函数
1491
+ function Undo(editor) {
1492
+ this.editor = editor;
1493
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-undo"><i/>\n </div>');
1494
+ this.type = 'click';
1495
+
1496
+ // 当前是否 active 状态
1497
+ this._active = false;
1498
+ }
1499
+
1500
+ // 原型
1501
+ Undo.prototype = {
1502
+ constructor: Undo,
1503
+
1504
+ // 点击事件
1505
+ onClick: function onClick(e) {
1506
+ // 点击菜单将触发这里
1507
+
1508
+ var editor = this.editor;
1509
+
1510
+ // 执行 undo 命令
1511
+ editor.cmd.do('undo');
1512
+ }
1513
+ };
1514
+
1515
+ /*
1516
+ menu - list
1517
+ */
1518
+ // 构造函数
1519
+ function List(editor) {
1520
+ var _this = this;
1521
+
1522
+ this.editor = editor;
1523
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-list2"><i/></div>');
1524
+ this.type = 'droplist';
1525
+
1526
+ // 当前是否 active 状态
1527
+ this._active = false;
1528
+
1529
+ // 初始化 droplist
1530
+ this.droplist = new DropList(this, {
1531
+ width: 120,
1532
+ $title: $('<p>设置列表</p>'),
1533
+ type: 'list', // droplist 以列表形式展示
1534
+ list: [{ $elem: $('<span><i class="w-e-icon-list-numbered"></i> 有序列表</span>'), value: 'insertOrderedList' }, { $elem: $('<span><i class="w-e-icon-list2"></i> 无序列表</span>'), value: 'insertUnorderedList' }],
1535
+ onClick: function onClick(value) {
1536
+ // 注意 this 是指向当前的 List 对象
1537
+ _this._command(value);
1538
+ }
1539
+ });
1540
+ }
1541
+
1542
+ // 原型
1543
+ List.prototype = {
1544
+ constructor: List,
1545
+
1546
+ // 执行命令
1547
+ _command: function _command(value) {
1548
+ var editor = this.editor;
1549
+ var $textElem = editor.$textElem;
1550
+ editor.selection.restoreSelection();
1551
+ if (editor.cmd.queryCommandState(value)) {
1552
+ return;
1553
+ }
1554
+ editor.cmd.do(value);
1555
+
1556
+ // 验证列表是否被包裹在 <p> 之内
1557
+ var $selectionElem = editor.selection.getSelectionContainerElem();
1558
+ if ($selectionElem.getNodeName() === 'LI') {
1559
+ $selectionElem = $selectionElem.parent();
1560
+ }
1561
+ if (/^ol|ul$/i.test($selectionElem.getNodeName()) === false) {
1562
+ return;
1563
+ }
1564
+ if ($selectionElem.equal($textElem)) {
1565
+ // 证明是顶级标签,没有被 <p> 包裹
1566
+ return;
1567
+ }
1568
+ var $parent = $selectionElem.parent();
1569
+ if ($parent.equal($textElem)) {
1570
+ // $parent 是顶级标签,不能删除
1571
+ return;
1572
+ }
1573
+
1574
+ $selectionElem.insertAfter($parent);
1575
+ $parent.remove();
1576
+ },
1577
+
1578
+ // 试图改变 active 状态
1579
+ tryChangeActive: function tryChangeActive(e) {
1580
+ var editor = this.editor;
1581
+ var $elem = this.$elem;
1582
+ if (editor.cmd.queryCommandState('insertUnOrderedList') || editor.cmd.queryCommandState('insertOrderedList')) {
1583
+ this._active = true;
1584
+ $elem.addClass('w-e-active');
1585
+ } else {
1586
+ this._active = false;
1587
+ $elem.removeClass('w-e-active');
1588
+ }
1589
+ }
1590
+ };
1591
+
1592
+ /*
1593
+ menu - justify
1594
+ */
1595
+ // 构造函数
1596
+ function Justify(editor) {
1597
+ var _this = this;
1598
+
1599
+ this.editor = editor;
1600
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-paragraph-left"><i/></div>');
1601
+ this.type = 'droplist';
1602
+
1603
+ // 当前是否 active 状态
1604
+ this._active = false;
1605
+
1606
+ // 初始化 droplist
1607
+ this.droplist = new DropList(this, {
1608
+ width: 100,
1609
+ $title: $('<p>对齐方式</p>'),
1610
+ type: 'list', // droplist 以列表形式展示
1611
+ list: [{ $elem: $('<span><i class="w-e-icon-paragraph-left"></i> 靠左</span>'), value: 'justifyLeft' }, { $elem: $('<span><i class="w-e-icon-paragraph-center"></i> 居中</span>'), value: 'justifyCenter' }, { $elem: $('<span><i class="w-e-icon-paragraph-right"></i> 靠右</span>'), value: 'justifyRight' }],
1612
+ onClick: function onClick(value) {
1613
+ // 注意 this 是指向当前的 List 对象
1614
+ _this._command(value);
1615
+ }
1616
+ });
1617
+ }
1618
+
1619
+ // 原型
1620
+ Justify.prototype = {
1621
+ constructor: Justify,
1622
+
1623
+ // 执行命令
1624
+ _command: function _command(value) {
1625
+ var editor = this.editor;
1626
+ editor.cmd.do(value);
1627
+ }
1628
+ };
1629
+
1630
+ /*
1631
+ menu - Forecolor
1632
+ */
1633
+ // 构造函数
1634
+ function ForeColor(editor) {
1635
+ var _this = this;
1636
+
1637
+ this.editor = editor;
1638
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-pencil2"><i/></div>');
1639
+ this.type = 'droplist';
1640
+
1641
+ // 当前是否 active 状态
1642
+ this._active = false;
1643
+
1644
+ // 初始化 droplist
1645
+ this.droplist = new DropList(this, {
1646
+ width: 120,
1647
+ $title: $('<p>文字颜色</p>'),
1648
+ type: 'inline-block', // droplist 内容以 block 形式展示
1649
+ list: [{ $elem: $('<i style="color:#000000;" class="w-e-icon-pencil2"></i>'), value: '#000000' }, { $elem: $('<i style="color:#eeece0;" class="w-e-icon-pencil2"></i>'), value: '#eeece0' }, { $elem: $('<i style="color:#1c487f;" class="w-e-icon-pencil2"></i>'), value: '#1c487f' }, { $elem: $('<i style="color:#4d80bf;" class="w-e-icon-pencil2"></i>'), value: '#4d80bf' }, { $elem: $('<i style="color:#c24f4a;" class="w-e-icon-pencil2"></i>'), value: '#c24f4a' }, { $elem: $('<i style="color:#8baa4a;" class="w-e-icon-pencil2"></i>'), value: '#8baa4a' }, { $elem: $('<i style="color:#7b5ba1;" class="w-e-icon-pencil2"></i>'), value: '#7b5ba1' }, { $elem: $('<i style="color:#46acc8;" class="w-e-icon-pencil2"></i>'), value: '#46acc8' }, { $elem: $('<i style="color:#f9963b;" class="w-e-icon-pencil2"></i>'), value: '#f9963b' }, { $elem: $('<i style="color:#ffffff;" class="w-e-icon-pencil2"></i>'), value: '#ffffff' }],
1650
+ onClick: function onClick(value) {
1651
+ // 注意 this 是指向当前的 ForeColor 对象
1652
+ _this._command(value);
1653
+ }
1654
+ });
1655
+ }
1656
+
1657
+ // 原型
1658
+ ForeColor.prototype = {
1659
+ constructor: ForeColor,
1660
+
1661
+ // 执行命令
1662
+ _command: function _command(value) {
1663
+ var editor = this.editor;
1664
+ editor.cmd.do('foreColor', value);
1665
+ }
1666
+ };
1667
+
1668
+ /*
1669
+ menu - BackColor
1670
+ */
1671
+ // 构造函数
1672
+ function BackColor(editor) {
1673
+ var _this = this;
1674
+
1675
+ this.editor = editor;
1676
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-paint-brush"><i/></div>');
1677
+ this.type = 'droplist';
1678
+
1679
+ // 当前是否 active 状态
1680
+ this._active = false;
1681
+
1682
+ // 初始化 droplist
1683
+ this.droplist = new DropList(this, {
1684
+ width: 120,
1685
+ $title: $('<p>背景色</p>'),
1686
+ type: 'inline-block', // droplist 内容以 block 形式展示
1687
+ list: [{ $elem: $('<i style="color:#000000;" class="w-e-icon-paint-brush"></i>'), value: '#000000' }, { $elem: $('<i style="color:#eeece0;" class="w-e-icon-paint-brush"></i>'), value: '#eeece0' }, { $elem: $('<i style="color:#1c487f;" class="w-e-icon-paint-brush"></i>'), value: '#1c487f' }, { $elem: $('<i style="color:#4d80bf;" class="w-e-icon-paint-brush"></i>'), value: '#4d80bf' }, { $elem: $('<i style="color:#c24f4a;" class="w-e-icon-paint-brush"></i>'), value: '#c24f4a' }, { $elem: $('<i style="color:#8baa4a;" class="w-e-icon-paint-brush"></i>'), value: '#8baa4a' }, { $elem: $('<i style="color:#7b5ba1;" class="w-e-icon-paint-brush"></i>'), value: '#7b5ba1' }, { $elem: $('<i style="color:#46acc8;" class="w-e-icon-paint-brush"></i>'), value: '#46acc8' }, { $elem: $('<i style="color:#f9963b;" class="w-e-icon-paint-brush"></i>'), value: '#f9963b' }, { $elem: $('<i style="color:#ffffff;" class="w-e-icon-paint-brush"></i>'), value: '#ffffff' }],
1688
+ onClick: function onClick(value) {
1689
+ // 注意 this 是指向当前的 BackColor 对象
1690
+ _this._command(value);
1691
+ }
1692
+ });
1693
+ }
1694
+
1695
+ // 原型
1696
+ BackColor.prototype = {
1697
+ constructor: BackColor,
1698
+
1699
+ // 执行命令
1700
+ _command: function _command(value) {
1701
+ var editor = this.editor;
1702
+ editor.cmd.do('backColor', value);
1703
+ }
1704
+ };
1705
+
1706
+ /*
1707
+ menu - quote
1708
+ */
1709
+ // 构造函数
1710
+ function Quote(editor) {
1711
+ this.editor = editor;
1712
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-quotes-left"><i/>\n </div>');
1713
+ this.type = 'click';
1714
+
1715
+ // 当前是否 active 状态
1716
+ this._active = false;
1717
+ }
1718
+
1719
+ // 原型
1720
+ Quote.prototype = {
1721
+ constructor: Quote,
1722
+
1723
+ onClick: function onClick(e) {
1724
+ var editor = this.editor;
1725
+ if (!UA.isIE()) {
1726
+ editor.cmd.do('formatBlock', '<BLOCKQUOTE>');
1727
+ return;
1728
+ }
1729
+
1730
+ // IE 中不支持 formatBlock <BLOCKQUOTE> ,要用其他方式兼容
1731
+
1732
+ var $selectionElem = editor.selection.getSelectionContainerElem();
1733
+ var content = void 0,
1734
+ $targetELem = void 0;
1735
+ if ($selectionElem.getNodeName() === 'P') {
1736
+ // 将 P 转换为 quote
1737
+ content = $selectionElem.text();
1738
+ $targetELem = $('<blockquote>' + content + '</blockquote>');
1739
+ $targetELem.insertAfter($selectionElem);
1740
+ $selectionElem.remove();
1741
+ return;
1742
+ }
1743
+ if ($selectionElem.getNodeName() === 'BLOCKQUOTE') {
1744
+ // 撤销 quote
1745
+ content = $selectionElem.text();
1746
+ $targetELem = $('<p>' + content + '</p>');
1747
+ $targetELem.insertAfter($selectionElem);
1748
+ $selectionElem.remove();
1749
+ }
1750
+ },
1751
+
1752
+ tryChangeActive: function tryChangeActive(e) {
1753
+ var editor = this.editor;
1754
+ var $elem = this.$elem;
1755
+ var reg = /^BLOCKQUOTE$/i;
1756
+ var cmdValue = editor.cmd.queryCommandValue('formatBlock');
1757
+ if (reg.test(cmdValue)) {
1758
+ this._active = true;
1759
+ $elem.addClass('w-e-active');
1760
+ } else {
1761
+ this._active = false;
1762
+ $elem.removeClass('w-e-active');
1763
+ }
1764
+ }
1765
+ };
1766
+
1767
+ /*
1768
+ menu - code
1769
+ */
1770
+ // 构造函数
1771
+ function Code(editor) {
1772
+ this.editor = editor;
1773
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-terminal"><i/>\n </div>');
1774
+ this.type = 'panel';
1775
+
1776
+ // 当前是否 active 状态
1777
+ this._active = false;
1778
+ }
1779
+
1780
+ // 原型
1781
+ Code.prototype = {
1782
+ constructor: Code,
1783
+
1784
+ onClick: function onClick(e) {
1785
+ var editor = this.editor;
1786
+ var $startElem = editor.selection.getSelectionStartElem();
1787
+ var $endElem = editor.selection.getSelectionEndElem();
1788
+ var isSeleEmpty = editor.selection.isSelectionEmpty();
1789
+ var selectionText = editor.selection.getSelectionText();
1790
+ var $code = void 0;
1791
+
1792
+ if (!$startElem.equal($endElem)) {
1793
+ // 跨元素选择,不做处理
1794
+ editor.selection.restoreSelection();
1795
+ return;
1796
+ }
1797
+ if (!isSeleEmpty) {
1798
+ // 选取不是空,用 <code> 包裹即可
1799
+ $code = $('<code>' + selectionText + '</code>');
1800
+ editor.cmd.do('insertElem', $code);
1801
+ editor.selection.createRangeByElem($code, false);
1802
+ editor.selection.restoreSelection();
1803
+ return;
1804
+ }
1805
+
1806
+ // 选取是空,且没有夸元素选择,则插入 <pre><code></code></prev>
1807
+ if (this._active) {
1808
+ // 选中状态,将编辑内容
1809
+ this._createPanel($startElem.html());
1810
+ } else {
1811
+ // 未选中状态,将创建内容
1812
+ this._createPanel();
1813
+ }
1814
+ },
1815
+
1816
+ _createPanel: function _createPanel(value) {
1817
+ var _this = this;
1818
+
1819
+ // value - 要编辑的内容
1820
+ value = value || '';
1821
+ var type = !value ? 'new' : 'edit';
1822
+ var textId = getRandom('texxt');
1823
+ var btnId = getRandom('btn');
1824
+
1825
+ var panel = new Panel(this, {
1826
+ width: 500,
1827
+ // 一个 Panel 包含多个 tab
1828
+ tabs: [{
1829
+ // 标题
1830
+ title: '插入代码',
1831
+ // 模板
1832
+ tpl: '<div>\n <textarea id="' + textId + '" style="height:145px;;">' + value + '</textarea>\n <div class="w-e-button-container">\n <button id="' + btnId + '" class="right">\u63D2\u5165</button>\n </div>\n <div>',
1833
+ // 事件绑定
1834
+ events: [
1835
+ // 插入代码
1836
+ {
1837
+ selector: '#' + btnId,
1838
+ type: 'click',
1839
+ fn: function fn() {
1840
+ var $text = $('#' + textId);
1841
+ var text = $text.val() || $text.html();
1842
+ text = replaceHtmlSymbol(text);
1843
+ if (type === 'new') {
1844
+ // 新插入
1845
+ _this._insertCode(text);
1846
+ } else {
1847
+ // 编辑更新
1848
+ _this._updateCode(text);
1849
+ }
1850
+
1851
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
1852
+ return true;
1853
+ }
1854
+ }]
1855
+ } // first tab end
1856
+ ] // tabs end
1857
+ }); // new Panel end
1858
+
1859
+ // 显示 panel
1860
+ panel.show();
1861
+
1862
+ // 记录属性
1863
+ this.panel = panel;
1864
+ },
1865
+
1866
+ // 插入代码
1867
+ _insertCode: function _insertCode(value) {
1868
+ var editor = this.editor;
1869
+ editor.cmd.do('insertHTML', '<pre><code>' + value + '</code></pre><p><br></p>');
1870
+ },
1871
+
1872
+ // 更新代码
1873
+ _updateCode: function _updateCode(value) {
1874
+ var editor = this.editor;
1875
+ var $selectionELem = editor.selection.getSelectionContainerElem();
1876
+ if (!$selectionELem) {
1877
+ return;
1878
+ }
1879
+ $selectionELem.html(value);
1880
+ editor.selection.restoreSelection();
1881
+ },
1882
+
1883
+ // 试图改变 active 状态
1884
+ tryChangeActive: function tryChangeActive(e) {
1885
+ var editor = this.editor;
1886
+ var $elem = this.$elem;
1887
+ var $selectionELem = editor.selection.getSelectionContainerElem();
1888
+ if (!$selectionELem) {
1889
+ return;
1890
+ }
1891
+ var $parentElem = $selectionELem.parent();
1892
+ if ($selectionELem.getNodeName() === 'CODE' && $parentElem.getNodeName() === 'PRE') {
1893
+ this._active = true;
1894
+ $elem.addClass('w-e-active');
1895
+ } else {
1896
+ this._active = false;
1897
+ $elem.removeClass('w-e-active');
1898
+ }
1899
+ }
1900
+ };
1901
+
1902
+ /*
1903
+ menu - emoticon
1904
+ */
1905
+ // 构造函数
1906
+ function Emoticon(editor) {
1907
+ this.editor = editor;
1908
+ this.$elem = $('<div class="w-e-menu">\n <i class="w-e-icon-happy"><i/>\n </div>');
1909
+ this.type = 'panel';
1910
+
1911
+ // 当前是否 active 状态
1912
+ this._active = false;
1913
+ }
1914
+
1915
+ // 原型
1916
+ Emoticon.prototype = {
1917
+ constructor: Emoticon,
1918
+
1919
+ onClick: function onClick() {
1920
+ this._createPanel();
1921
+ },
1922
+
1923
+ _createPanel: function _createPanel() {
1924
+ var _this = this;
1925
+
1926
+ // 拼接表情字符串
1927
+ var faceHtml = '';
1928
+ var faceStr = '😀 😃 😄 😁 😆 😅 😂 😊 😇 🙂 🙃 😉 😌 😍 😘 😗 😙 😚 😋 😜 😝 😛 🤑 🤗 🤓 😎 😏 😒 😞 😔 😟 😕 🙁 😣 😖 😫 😩 😤 😠 😡 😶 😐 😑 😯 😦 😧 😮 😲 😵 😳 😱 😨 😰 😢 😥 😭 😓 😪 😴 🙄 🤔 😬 🤐';
1929
+ faceStr.split(/\s/).forEach(function (item) {
1930
+ if (item) {
1931
+ faceHtml += '<span class="w-e-item">' + item + '</span>';
1932
+ }
1933
+ });
1934
+
1935
+ var handHtml = '';
1936
+ var handStr = '🙌 👏 👋 👍 👎 👊 ✊ ️👌 ✋ 👐 💪 🙏 ️👆 👇 👈 👉 🖕 🖐 🤘 🖖';
1937
+ handStr.split(/\s/).forEach(function (item) {
1938
+ if (item) {
1939
+ handHtml += '<span class="w-e-item">' + item + '</span>';
1940
+ }
1941
+ });
1942
+
1943
+ var panel = new Panel(this, {
1944
+ width: 300,
1945
+ height: 200,
1946
+ // 一个 Panel 包含多个 tab
1947
+ tabs: [{
1948
+ // 标题
1949
+ title: '表情',
1950
+ // 模板
1951
+ tpl: '<div class="w-e-emoticon-container">' + faceHtml + '</div>',
1952
+ // 事件绑定
1953
+ events: [{
1954
+ selector: 'span.w-e-item',
1955
+ type: 'click',
1956
+ fn: function fn(e) {
1957
+ var target = e.target;
1958
+ _this._insert(target.innerHTML);
1959
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
1960
+ return true;
1961
+ }
1962
+ }]
1963
+ }, // first tab end
1964
+ {
1965
+ // 标题
1966
+ title: '手势',
1967
+ // 模板
1968
+ tpl: '<div class="w-e-emoticon-container">' + handHtml + '</div>',
1969
+ // 事件绑定
1970
+ events: [{
1971
+ selector: 'span.w-e-item',
1972
+ type: 'click',
1973
+ fn: function fn(e) {
1974
+ var target = e.target;
1975
+ _this._insert(target.innerHTML);
1976
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
1977
+ return true;
1978
+ }
1979
+ }]
1980
+ } // second tab end
1981
+ ] // tabs end
1982
+ });
1983
+
1984
+ // 显示 panel
1985
+ panel.show();
1986
+
1987
+ // 记录属性
1988
+ this.panel = panel;
1989
+ },
1990
+
1991
+ // 插入表情
1992
+ _insert: function _insert(emoji) {
1993
+ var editor = this.editor;
1994
+ editor.cmd.do('insertHTML', '<span>' + emoji + '</span>');
1995
+ }
1996
+ };
1997
+
1998
+ /*
1999
+ menu - table
2000
+ */
2001
+ // 构造函数
2002
+ function Table(editor) {
2003
+ this.editor = editor;
2004
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-table2"><i/></div>');
2005
+ this.type = 'panel';
2006
+
2007
+ // 当前是否 active 状态
2008
+ this._active = false;
2009
+ }
2010
+
2011
+ // 原型
2012
+ Table.prototype = {
2013
+ constructor: Table,
2014
+
2015
+ onClick: function onClick() {
2016
+ if (this._active) {
2017
+ // 编辑现有表格
2018
+ this._createEditPanel();
2019
+ } else {
2020
+ // 插入新表格
2021
+ this._createInsertPanel();
2022
+ }
2023
+ },
2024
+
2025
+ // 创建插入新表格的 panel
2026
+ _createInsertPanel: function _createInsertPanel() {
2027
+ var _this = this;
2028
+
2029
+ // 用到的 id
2030
+ var btnInsertId = getRandom('btn');
2031
+ var textRowNum = getRandom('row');
2032
+ var textColNum = getRandom('col');
2033
+
2034
+ var panel = new Panel(this, {
2035
+ width: 250,
2036
+ // panel 包含多个 tab
2037
+ tabs: [{
2038
+ // 标题
2039
+ title: '插入表格',
2040
+ // 模板
2041
+ tpl: '<div>\n <p style="text-align:left; padding:5px 0;">\n \u521B\u5EFA\n <input id="' + textRowNum + '" type="text" value="5" style="width:40px;text-align:center;"/>\n \u884C\n <input id="' + textColNum + '" type="text" value="5" style="width:40px;text-align:center;"/>\n \u5217\u7684\u8868\u683C\n </p>\n <div class="w-e-button-container">\n <button id="' + btnInsertId + '" class="right">\u63D2\u5165</button>\n </div>\n </div>',
2042
+ // 事件绑定
2043
+ events: [{
2044
+ // 点击按钮,插入表格
2045
+ selector: '#' + btnInsertId,
2046
+ type: 'click',
2047
+ fn: function fn() {
2048
+ var rowNum = parseInt($('#' + textRowNum).val());
2049
+ var colNum = parseInt($('#' + textColNum).val());
2050
+
2051
+ if (rowNum && colNum && rowNum > 0 && colNum > 0) {
2052
+ // form 数据有效
2053
+ _this._insert(rowNum, colNum);
2054
+ }
2055
+
2056
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2057
+ return true;
2058
+ }
2059
+ }]
2060
+ } // first tab end
2061
+ ] // tabs end
2062
+ }); // panel end
2063
+
2064
+ // 展示 panel
2065
+ panel.show();
2066
+
2067
+ // 记录属性
2068
+ this.panel = panel;
2069
+ },
2070
+
2071
+ // 插入表格
2072
+ _insert: function _insert(rowNum, colNum) {
2073
+ // 拼接 table 模板
2074
+ var r = void 0,
2075
+ c = void 0;
2076
+ var html = '<table border="0" width="100%" cellpadding="0" cellspacing="0">';
2077
+ for (r = 0; r < rowNum; r++) {
2078
+ html += '<tr>';
2079
+ if (r === 0) {
2080
+ for (c = 0; c < colNum; c++) {
2081
+ html += '<th>&nbsp;</th>';
2082
+ }
2083
+ } else {
2084
+ for (c = 0; c < colNum; c++) {
2085
+ html += '<td>&nbsp;</td>';
2086
+ }
2087
+ }
2088
+ html += '</tr>';
2089
+ }
2090
+ html += '</table><p><br></p>';
2091
+
2092
+ // 执行命令
2093
+ var editor = this.editor;
2094
+ editor.cmd.do('insertHTML', html);
2095
+
2096
+ // 防止 firefox 下出现 resize 的控制点
2097
+ editor.cmd.do('enableObjectResizing', false);
2098
+ editor.cmd.do('enableInlineTableEditing', false);
2099
+ },
2100
+
2101
+ // 创建编辑表格的 panel
2102
+ _createEditPanel: function _createEditPanel() {
2103
+ var _this2 = this;
2104
+
2105
+ // 可用的 id
2106
+ var addRowBtnId = getRandom('add-row');
2107
+ var addColBtnId = getRandom('add-col');
2108
+ var delRowBtnId = getRandom('del-row');
2109
+ var delColBtnId = getRandom('del-col');
2110
+ var delTableBtnId = getRandom('del-table');
2111
+
2112
+ // 创建 panel 对象
2113
+ var panel = new Panel(this, {
2114
+ width: 320,
2115
+ // panel 包含多个 tab
2116
+ tabs: [{
2117
+ // 标题
2118
+ title: '编辑表格',
2119
+ // 模板
2120
+ tpl: '<div>\n <div class="w-e-button-container" style="border-bottom:1px solid #f1f1f1;padding-bottom:5px;margin-bottom:5px;">\n <button id="' + addRowBtnId + '" class="left">\u589E\u52A0\u884C</button>\n <button id="' + delRowBtnId + '" class="red left">\u5220\u9664\u884C</button>\n <button id="' + addColBtnId + '" class="left">\u589E\u52A0\u5217</button>\n <button id="' + delColBtnId + '" class="red left">\u5220\u9664\u5217</button>\n </div>\n <div class="w-e-button-container">\n <button id="' + delTableBtnId + '" class="gray left">\u5220\u9664\u8868\u683C</button>\n </dv>\n </div>',
2121
+ // 事件绑定
2122
+ events: [{
2123
+ // 增加行
2124
+ selector: '#' + addRowBtnId,
2125
+ type: 'click',
2126
+ fn: function fn() {
2127
+ _this2._addRow();
2128
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2129
+ return true;
2130
+ }
2131
+ }, {
2132
+ // 增加列
2133
+ selector: '#' + addColBtnId,
2134
+ type: 'click',
2135
+ fn: function fn() {
2136
+ _this2._addCol();
2137
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2138
+ return true;
2139
+ }
2140
+ }, {
2141
+ // 删除行
2142
+ selector: '#' + delRowBtnId,
2143
+ type: 'click',
2144
+ fn: function fn() {
2145
+ _this2._delRow();
2146
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2147
+ return true;
2148
+ }
2149
+ }, {
2150
+ // 删除列
2151
+ selector: '#' + delColBtnId,
2152
+ type: 'click',
2153
+ fn: function fn() {
2154
+ _this2._delCol();
2155
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2156
+ return true;
2157
+ }
2158
+ }, {
2159
+ // 删除表格
2160
+ selector: '#' + delTableBtnId,
2161
+ type: 'click',
2162
+ fn: function fn() {
2163
+ _this2._delTable();
2164
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2165
+ return true;
2166
+ }
2167
+ }]
2168
+ }]
2169
+ });
2170
+ // 显示 panel
2171
+ panel.show();
2172
+ },
2173
+
2174
+ // 获取选中的单元格的位置信息
2175
+ _getLocationData: function _getLocationData() {
2176
+ var result = {};
2177
+ var editor = this.editor;
2178
+ var $selectionELem = editor.selection.getSelectionContainerElem();
2179
+ if (!$selectionELem) {
2180
+ return;
2181
+ }
2182
+ var nodeName = $selectionELem.getNodeName();
2183
+ if (nodeName !== 'TD' && nodeName !== 'TH') {
2184
+ return;
2185
+ }
2186
+
2187
+ // 获取 td index
2188
+ var $tr = $selectionELem.parent();
2189
+ var $tds = $tr.children();
2190
+ var tdLength = $tds.length;
2191
+ $tds.forEach(function (td, index) {
2192
+ if (td === $selectionELem[0]) {
2193
+ // 记录并跳出循环
2194
+ result.td = {
2195
+ index: index,
2196
+ elem: td,
2197
+ length: tdLength
2198
+ };
2199
+ return false;
2200
+ }
2201
+ });
2202
+
2203
+ // 获取 tr index
2204
+ var $tbody = $tr.parent();
2205
+ var $trs = $tbody.children();
2206
+ var trLength = $trs.length;
2207
+ $trs.forEach(function (tr, index) {
2208
+ if (tr === $tr[0]) {
2209
+ // 记录并跳出循环
2210
+ result.tr = {
2211
+ index: index,
2212
+ elem: tr,
2213
+ length: trLength
2214
+ };
2215
+ return false;
2216
+ }
2217
+ });
2218
+
2219
+ // 返回结果
2220
+ return result;
2221
+ },
2222
+
2223
+ // 增加行
2224
+ _addRow: function _addRow() {
2225
+ // 获取当前单元格的位置信息
2226
+ var locationData = this._getLocationData();
2227
+ if (!locationData) {
2228
+ return;
2229
+ }
2230
+ var trData = locationData.tr;
2231
+ var $currentTr = $(trData.elem);
2232
+ var tdData = locationData.td;
2233
+ var tdLength = tdData.length;
2234
+
2235
+ // 拼接即将插入的字符串
2236
+ var newTr = document.createElement('tr');
2237
+ var tpl = '',
2238
+ i = void 0;
2239
+ for (i = 0; i < tdLength; i++) {
2240
+ tpl += '<td>&nbsp;</td>';
2241
+ }
2242
+ newTr.innerHTML = tpl;
2243
+ // 插入
2244
+ $(newTr).insertAfter($currentTr);
2245
+ },
2246
+
2247
+ // 增加列
2248
+ _addCol: function _addCol() {
2249
+ // 获取当前单元格的位置信息
2250
+ var locationData = this._getLocationData();
2251
+ if (!locationData) {
2252
+ return;
2253
+ }
2254
+ var trData = locationData.tr;
2255
+ var tdData = locationData.td;
2256
+ var tdIndex = tdData.index;
2257
+ var $currentTr = $(trData.elem);
2258
+ var $trParent = $currentTr.parent();
2259
+ var $trs = $trParent.children();
2260
+
2261
+ // 遍历所有行
2262
+ $trs.forEach(function (tr) {
2263
+ var $tr = $(tr);
2264
+ var $tds = $tr.children();
2265
+ var $currentTd = $tds.get(tdIndex);
2266
+ var name = $currentTd.getNodeName().toLowerCase();
2267
+
2268
+ // new 一个 td,并插入
2269
+ var newTd = document.createElement(name);
2270
+ $(newTd).insertAfter($currentTd);
2271
+ });
2272
+ },
2273
+
2274
+ // 删除行
2275
+ _delRow: function _delRow() {
2276
+ // 获取当前单元格的位置信息
2277
+ var locationData = this._getLocationData();
2278
+ if (!locationData) {
2279
+ return;
2280
+ }
2281
+ var trData = locationData.tr;
2282
+ var $currentTr = $(trData.elem);
2283
+ $currentTr.remove();
2284
+ },
2285
+
2286
+ // 删除列
2287
+ _delCol: function _delCol() {
2288
+ // 获取当前单元格的位置信息
2289
+ var locationData = this._getLocationData();
2290
+ if (!locationData) {
2291
+ return;
2292
+ }
2293
+ var trData = locationData.tr;
2294
+ var tdData = locationData.td;
2295
+ var tdIndex = tdData.index;
2296
+ var $currentTr = $(trData.elem);
2297
+ var $trParent = $currentTr.parent();
2298
+ var $trs = $trParent.children();
2299
+
2300
+ // 遍历所有行
2301
+ $trs.forEach(function (tr) {
2302
+ var $tr = $(tr);
2303
+ var $tds = $tr.children();
2304
+ var $currentTd = $tds.get(tdIndex);
2305
+ // 删除
2306
+ $currentTd.remove();
2307
+ });
2308
+ },
2309
+
2310
+ // 删除表格
2311
+ _delTable: function _delTable() {
2312
+ var editor = this.editor;
2313
+ var $selectionELem = editor.selection.getSelectionContainerElem();
2314
+ if (!$selectionELem) {
2315
+ return;
2316
+ }
2317
+ var $table = $selectionELem.parentUntil('table');
2318
+ if (!$table) {
2319
+ return;
2320
+ }
2321
+ $table.remove();
2322
+ },
2323
+
2324
+ // 试图改变 active 状态
2325
+ tryChangeActive: function tryChangeActive(e) {
2326
+ var editor = this.editor;
2327
+ var $elem = this.$elem;
2328
+ var $selectionELem = editor.selection.getSelectionContainerElem();
2329
+ if (!$selectionELem) {
2330
+ return;
2331
+ }
2332
+ var nodeName = $selectionELem.getNodeName();
2333
+ if (nodeName === 'TD' || nodeName === 'TH') {
2334
+ this._active = true;
2335
+ $elem.addClass('w-e-active');
2336
+ } else {
2337
+ this._active = false;
2338
+ $elem.removeClass('w-e-active');
2339
+ }
2340
+ }
2341
+ };
2342
+
2343
+ /*
2344
+ menu - video
2345
+ */
2346
+ // 构造函数
2347
+ function Video(editor) {
2348
+ this.editor = editor;
2349
+ this.$elem = $('<div class="w-e-menu"><i class="w-e-icon-play"><i/></div>');
2350
+ this.type = 'panel';
2351
+
2352
+ // 当前是否 active 状态
2353
+ this._active = false;
2354
+ }
2355
+
2356
+ // 原型
2357
+ Video.prototype = {
2358
+ constructor: Video,
2359
+
2360
+ onClick: function onClick() {
2361
+ this._createPanel();
2362
+ },
2363
+
2364
+ _createPanel: function _createPanel() {
2365
+ var _this = this;
2366
+
2367
+ // 创建 id
2368
+ var textValId = getRandom('text-val');
2369
+ var btnId = getRandom('btn');
2370
+
2371
+ // 创建 panel
2372
+ var panel = new Panel(this, {
2373
+ width: 350,
2374
+ // 一个 panel 多个 tab
2375
+ tabs: [{
2376
+ // 标题
2377
+ title: '插入视频',
2378
+ // 模板
2379
+ tpl: '<div>\n <input id="' + textValId + '" type="text" class="block" placeholder="\u683C\u5F0F\u5982\uFF1A<iframe src=... ></iframe>"/>\n <div class="w-e-button-container">\n <button id="' + btnId + '" class="right">\u63D2\u5165</button>\n </div>\n </div>',
2380
+ // 事件绑定
2381
+ events: [{
2382
+ selector: '#' + btnId,
2383
+ type: 'click',
2384
+ fn: function fn() {
2385
+ var $text = $('#' + textValId);
2386
+ var val = $text.val().trim();
2387
+
2388
+ // 测试用视频地址
2389
+ // <iframe height=498 width=510 src='http://player.youku.com/embed/XMjcwMzc3MzM3Mg==' frameborder=0 'allowfullscreen'></iframe>
2390
+
2391
+ if (val) {
2392
+ // 插入视频
2393
+ _this._insert(val);
2394
+ }
2395
+
2396
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2397
+ return true;
2398
+ }
2399
+ }]
2400
+ } // first tab end
2401
+ ] // tabs end
2402
+ }); // panel end
2403
+
2404
+ // 显示 panel
2405
+ panel.show();
2406
+
2407
+ // 记录属性
2408
+ this.panel = panel;
2409
+ },
2410
+
2411
+ // 插入视频
2412
+ _insert: function _insert(val) {
2413
+ var editor = this.editor;
2414
+ editor.cmd.do('insertHTML', val + '<p><br></p>');
2415
+ }
2416
+ };
2417
+
2418
+ /*
2419
+ menu - img
2420
+ */
2421
+ // 构造函数
2422
+ function Image(editor) {
2423
+ this.editor = editor;
2424
+ var imgMenuId = getRandom('w-e-img');
2425
+ this.$elem = $('<div class="w-e-menu" id="' + imgMenuId + '"><i class="w-e-icon-image"><i/></div>');
2426
+ editor.imgMenuId = imgMenuId;
2427
+ this.type = 'panel';
2428
+
2429
+ // 当前是否 active 状态
2430
+ this._active = false;
2431
+ }
2432
+
2433
+ // 原型
2434
+ Image.prototype = {
2435
+ constructor: Image,
2436
+
2437
+ onClick: function onClick() {
2438
+ var editor = this.editor;
2439
+ var config = editor.config;
2440
+ if (config.qiniu) {
2441
+ return;
2442
+ }
2443
+ if (this._active) {
2444
+ this._createEditPanel();
2445
+ } else {
2446
+ this._createInsertPanel();
2447
+ }
2448
+ },
2449
+
2450
+ _createEditPanel: function _createEditPanel() {
2451
+ var editor = this.editor;
2452
+
2453
+ // id
2454
+ var width30 = getRandom('width-30');
2455
+ var width50 = getRandom('width-50');
2456
+ var width100 = getRandom('width-100');
2457
+ var delBtn = getRandom('del-btn');
2458
+
2459
+ // tab 配置
2460
+ var tabsConfig = [{
2461
+ title: '编辑图片',
2462
+ tpl: '<div>\n <div class="w-e-button-container" style="border-bottom:1px solid #f1f1f1;padding-bottom:5px;margin-bottom:5px;">\n <span style="float:left;font-size:14px;margin:4px 5px 0 5px;color:#333;">\u6700\u5927\u5BBD\u5EA6\uFF1A</span>\n <button id="' + width30 + '" class="left">30%</button>\n <button id="' + width50 + '" class="left">50%</button>\n <button id="' + width100 + '" class="left">100%</button>\n </div>\n <div class="w-e-button-container">\n <button id="' + delBtn + '" class="gray left">\u5220\u9664\u56FE\u7247</button>\n </dv>\n </div>',
2463
+ events: [{
2464
+ selector: '#' + width30,
2465
+ type: 'click',
2466
+ fn: function fn() {
2467
+ var $img = editor._selectedImg;
2468
+ if ($img) {
2469
+ $img.css('max-width', '30%');
2470
+ }
2471
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2472
+ return true;
2473
+ }
2474
+ }, {
2475
+ selector: '#' + width50,
2476
+ type: 'click',
2477
+ fn: function fn() {
2478
+ var $img = editor._selectedImg;
2479
+ if ($img) {
2480
+ $img.css('max-width', '50%');
2481
+ }
2482
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2483
+ return true;
2484
+ }
2485
+ }, {
2486
+ selector: '#' + width100,
2487
+ type: 'click',
2488
+ fn: function fn() {
2489
+ var $img = editor._selectedImg;
2490
+ if ($img) {
2491
+ $img.css('max-width', '100%');
2492
+ }
2493
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2494
+ return true;
2495
+ }
2496
+ }, {
2497
+ selector: '#' + delBtn,
2498
+ type: 'click',
2499
+ fn: function fn() {
2500
+ var $img = editor._selectedImg;
2501
+ if ($img) {
2502
+ $img.remove();
2503
+ }
2504
+ // 返回 true,表示该事件执行完之后,panel 要关闭。否则 panel 不会关闭
2505
+ return true;
2506
+ }
2507
+ }]
2508
+ }];
2509
+
2510
+ // 创建 panel 并显示
2511
+ var panel = new Panel(this, {
2512
+ width: 300,
2513
+ tabs: tabsConfig
2514
+ });
2515
+ panel.show();
2516
+
2517
+ // 记录属性
2518
+ this.panel = panel;
2519
+ },
2520
+
2521
+ _createInsertPanel: function _createInsertPanel() {
2522
+ var editor = this.editor;
2523
+ var uploadImg = editor.uploadImg;
2524
+ var config = editor.config;
2525
+
2526
+ // id
2527
+ var upTriggerId = getRandom('up-trigger');
2528
+ var upFileId = getRandom('up-file');
2529
+ var linkUrlId = getRandom('link-url');
2530
+ var linkBtnId = getRandom('link-btn');
2531
+
2532
+ // tabs 的配置
2533
+ var tabsConfig = [{
2534
+ title: '上传图片',
2535
+ tpl: '<div class="w-e-up-img-container">\n <div id="' + upTriggerId + '" class="w-e-up-btn">\n <i class="w-e-icon-upload2"></i>\n </div>\n <div style="display:none;">\n <input id="' + upFileId + '" type="file" multiple="multiple" accept="image/jpg,image/jpeg,image/png,image/gif,image/bmp"/>\n </div>\n </div>',
2536
+ events: [{
2537
+ // 触发选择图片
2538
+ selector: '#' + upTriggerId,
2539
+ type: 'click',
2540
+ fn: function fn() {
2541
+ var $file = $('#' + upFileId);
2542
+ var fileElem = $file[0];
2543
+ if (fileElem) {
2544
+ fileElem.click();
2545
+ } else {
2546
+ // 返回 true 可关闭 panel
2547
+ return true;
2548
+ }
2549
+ }
2550
+ }, {
2551
+ // 选择图片完毕
2552
+ selector: '#' + upFileId,
2553
+ type: 'change',
2554
+ fn: function fn() {
2555
+ var $file = $('#' + upFileId);
2556
+ var fileElem = $file[0];
2557
+ if (!fileElem) {
2558
+ // 返回 true 可关闭 panel
2559
+ return true;
2560
+ }
2561
+
2562
+ // 获取选中的 file 对象列表
2563
+ var fileList = fileElem.files;
2564
+ if (fileList.length) {
2565
+ uploadImg.uploadImg(fileList);
2566
+ }
2567
+
2568
+ // 返回 true 可关闭 panel
2569
+ return true;
2570
+ }
2571
+ }]
2572
+ }, // first tab end
2573
+ {
2574
+ title: '网络图片',
2575
+ tpl: '<div>\n <input id="' + linkUrlId + '" type="text" class="block" placeholder="\u56FE\u7247\u94FE\u63A5"/></td>\n <div class="w-e-button-container">\n <button id="' + linkBtnId + '" class="right">\u63D2\u5165</button>\n </div>\n </div>',
2576
+ events: [{
2577
+ selector: '#' + linkBtnId,
2578
+ type: 'click',
2579
+ fn: function fn() {
2580
+ var $linkUrl = $('#' + linkUrlId);
2581
+ var url = $linkUrl.val().trim();
2582
+
2583
+ if (url) {
2584
+ uploadImg.insertLinkImg(url);
2585
+ }
2586
+
2587
+ // 返回 true 表示函数执行结束之后关闭 panel
2588
+ return true;
2589
+ }
2590
+ }]
2591
+ } // second tab end
2592
+ ]; // tabs end
2593
+
2594
+ // 判断 tabs 的显示
2595
+ var tabsConfigResult = [];
2596
+ if ((config.uploadImgShowBase64 || config.uploadImgServer || config.customUploadImg) && window.FileReader) {
2597
+ // 显示“上传图片”
2598
+ tabsConfigResult.push(tabsConfig[0]);
2599
+ }
2600
+ if (config.showLinkImg) {
2601
+ // 显示“网络图片”
2602
+ tabsConfigResult.push(tabsConfig[1]);
2603
+ }
2604
+
2605
+ // 创建 panel 并显示
2606
+ var panel = new Panel(this, {
2607
+ width: 300,
2608
+ tabs: tabsConfigResult
2609
+ });
2610
+ panel.show();
2611
+
2612
+ // 记录属性
2613
+ this.panel = panel;
2614
+ },
2615
+
2616
+ // 试图改变 active 状态
2617
+ tryChangeActive: function tryChangeActive(e) {
2618
+ var editor = this.editor;
2619
+ var $elem = this.$elem;
2620
+ if (editor._selectedImg) {
2621
+ this._active = true;
2622
+ $elem.addClass('w-e-active');
2623
+ } else {
2624
+ this._active = false;
2625
+ $elem.removeClass('w-e-active');
2626
+ }
2627
+ }
2628
+ };
2629
+
2630
+ /*
2631
+ 所有菜单的汇总
2632
+ */
2633
+
2634
+ // 存储菜单的构造函数
2635
+ var MenuConstructors = {};
2636
+
2637
+ MenuConstructors.bold = Bold;
2638
+
2639
+ MenuConstructors.head = Head;
2640
+
2641
+ MenuConstructors.link = Link;
2642
+
2643
+ MenuConstructors.italic = Italic;
2644
+
2645
+ MenuConstructors.redo = Redo;
2646
+
2647
+ MenuConstructors.strikeThrough = StrikeThrough;
2648
+
2649
+ MenuConstructors.underline = Underline;
2650
+
2651
+ MenuConstructors.undo = Undo;
2652
+
2653
+ MenuConstructors.list = List;
2654
+
2655
+ MenuConstructors.justify = Justify;
2656
+
2657
+ MenuConstructors.foreColor = ForeColor;
2658
+
2659
+ MenuConstructors.backColor = BackColor;
2660
+
2661
+ MenuConstructors.quote = Quote;
2662
+
2663
+ MenuConstructors.code = Code;
2664
+
2665
+ MenuConstructors.emoticon = Emoticon;
2666
+
2667
+ MenuConstructors.table = Table;
2668
+
2669
+ MenuConstructors.video = Video;
2670
+
2671
+ MenuConstructors.image = Image;
2672
+
2673
+ /*
2674
+ 菜单集合
2675
+ */
2676
+ // 构造函数
2677
+ function Menus(editor) {
2678
+ this.editor = editor;
2679
+ this.menus = {};
2680
+ }
2681
+
2682
+ // 修改原型
2683
+ Menus.prototype = {
2684
+ constructor: Menus,
2685
+
2686
+ // 初始化菜单
2687
+ init: function init() {
2688
+ var _this = this;
2689
+
2690
+ var editor = this.editor;
2691
+ var config = editor.config || {};
2692
+ var configMenus = config.menus || []; // 获取配置中的菜单
2693
+
2694
+ // 根据配置信息,创建菜单
2695
+ configMenus.forEach(function (menuKey) {
2696
+ var MenuConstructor = MenuConstructors[menuKey];
2697
+ if (MenuConstructor && typeof MenuConstructor === 'function') {
2698
+ // 创建单个菜单
2699
+ _this.menus[menuKey] = new MenuConstructor(editor);
2700
+ }
2701
+ });
2702
+
2703
+ // 添加到菜单栏
2704
+ this._addToToolbar();
2705
+
2706
+ // 绑定事件
2707
+ this._bindEvent();
2708
+ },
2709
+
2710
+ // 添加到菜单栏
2711
+ _addToToolbar: function _addToToolbar() {
2712
+ var editor = this.editor;
2713
+ var $toolbarElem = editor.$toolbarElem;
2714
+ var menus = this.menus;
2715
+ var config = editor.config;
2716
+ // config.zIndex 是配置的编辑区域的 z-index,菜单的 z-index 得在其基础上 +1
2717
+ var zIndex = config.zIndex + 1;
2718
+ objForEach(menus, function (key, menu) {
2719
+ var $elem = menu.$elem;
2720
+ if ($elem) {
2721
+ // 设置 z-index
2722
+ $elem.css('z-index', zIndex);
2723
+ $toolbarElem.append($elem);
2724
+ }
2725
+ });
2726
+ },
2727
+
2728
+ // 绑定菜单 click mouseenter 事件
2729
+ _bindEvent: function _bindEvent() {
2730
+ var menus = this.menus;
2731
+ var editor = this.editor;
2732
+ objForEach(menus, function (key, menu) {
2733
+ var type = menu.type;
2734
+ if (!type) {
2735
+ return;
2736
+ }
2737
+ var $elem = menu.$elem;
2738
+ var droplist = menu.droplist;
2739
+ var panel = menu.panel;
2740
+
2741
+ // 点击类型,例如 bold
2742
+ if (type === 'click' && menu.onClick) {
2743
+ $elem.on('click', function (e) {
2744
+ if (editor.selection.getRange() == null) {
2745
+ return;
2746
+ }
2747
+ menu.onClick(e);
2748
+ });
2749
+ }
2750
+
2751
+ // 下拉框,例如 head
2752
+ if (type === 'droplist' && droplist) {
2753
+ $elem.on('mouseenter', function (e) {
2754
+ if (editor.selection.getRange() == null) {
2755
+ return;
2756
+ }
2757
+ // 显示
2758
+ droplist.showTimeoutId = setTimeout(function () {
2759
+ droplist.show();
2760
+ }, 200);
2761
+ }).on('mouseleave', function (e) {
2762
+ // 隐藏
2763
+ droplist.hideTimeoutId = setTimeout(function () {
2764
+ droplist.hide();
2765
+ }, 0);
2766
+ });
2767
+ }
2768
+
2769
+ // 弹框类型,例如 link
2770
+ if (type === 'panel' && menu.onClick) {
2771
+ $elem.on('click', function (e) {
2772
+ e.stopPropagation();
2773
+ if (editor.selection.getRange() == null) {
2774
+ return;
2775
+ }
2776
+ // 在自定义事件中显示 panel
2777
+ menu.onClick(e);
2778
+ });
2779
+ }
2780
+ });
2781
+ },
2782
+
2783
+ // 尝试修改菜单状态
2784
+ changeActive: function changeActive() {
2785
+ var menus = this.menus;
2786
+ objForEach(menus, function (key, menu) {
2787
+ if (menu.tryChangeActive) {
2788
+ setTimeout(function () {
2789
+ menu.tryChangeActive();
2790
+ }, 100);
2791
+ }
2792
+ });
2793
+ }
2794
+ };
2795
+
2796
+ /*
2797
+ 粘贴信息的处理
2798
+ */
2799
+
2800
+ // 获取粘贴的纯文本
2801
+ function getPasteText(e) {
2802
+ var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData;
2803
+ var pasteText = void 0;
2804
+ if (clipboardData == null) {
2805
+ pasteText = window.clipboardData && window.clipboardData.getData('text');
2806
+ } else {
2807
+ pasteText = clipboardData.getData('text/plain');
2808
+ }
2809
+
2810
+ return replaceHtmlSymbol(pasteText);
2811
+ }
2812
+
2813
+ // 获取粘贴的html
2814
+ function getPasteHtml(e, filterStyle) {
2815
+ var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData;
2816
+ var pasteText = void 0,
2817
+ pasteHtml = void 0;
2818
+ if (clipboardData == null) {
2819
+ pasteText = window.clipboardData && window.clipboardData.getData('text');
2820
+ } else {
2821
+ pasteText = clipboardData.getData('text/plain');
2822
+ pasteHtml = clipboardData.getData('text/html');
2823
+ }
2824
+ if (!pasteHtml && pasteText) {
2825
+ pasteHtml = '<p>' + replaceHtmlSymbol(pasteText) + '</p>';
2826
+ }
2827
+ if (!pasteHtml) {
2828
+ return;
2829
+ }
2830
+
2831
+ // 过滤word中状态过来的无用字符
2832
+ var docSplitHtml = pasteHtml.split('</html>');
2833
+ if (docSplitHtml.length === 2) {
2834
+ pasteHtml = docSplitHtml[0];
2835
+ }
2836
+
2837
+ // 过滤无用标签
2838
+ pasteHtml = pasteHtml.replace(/<(meta|script|link).+?>/igm, '');
2839
+ // 去掉注释
2840
+ pasteHtml = pasteHtml.replace(/<!--.*?-->/mg, '');
2841
+
2842
+ if (filterStyle) {
2843
+ // 过滤样式
2844
+ pasteHtml = pasteHtml.replace(/\s?(class|style)=('|").+?('|")/igm, '');
2845
+ } else {
2846
+ // 保留样式
2847
+ pasteHtml = pasteHtml.replace(/\s?class=('|").+?('|")/igm, '');
2848
+ }
2849
+
2850
+ return pasteHtml;
2851
+ }
2852
+
2853
+ // 获取粘贴的图片文件
2854
+ function getPasteImgs(e) {
2855
+ var result = [];
2856
+ var txt = getPasteText(e);
2857
+ if (txt) {
2858
+ // 有文字,就忽略图片
2859
+ return result;
2860
+ }
2861
+
2862
+ var clipboardData = e.clipboardData || e.originalEvent && e.originalEvent.clipboardData || {};
2863
+ var items = clipboardData.items;
2864
+ if (!items) {
2865
+ return result;
2866
+ }
2867
+
2868
+ objForEach(items, function (key, value) {
2869
+ var type = value.type;
2870
+ if (/image/i.test(type)) {
2871
+ result.push(value.getAsFile());
2872
+ }
2873
+ });
2874
+
2875
+ return result;
2876
+ }
2877
+
2878
+ /*
2879
+ 编辑区域
2880
+ */
2881
+
2882
+ // 构造函数
2883
+ function Text(editor) {
2884
+ this.editor = editor;
2885
+ }
2886
+
2887
+ // 修改原型
2888
+ Text.prototype = {
2889
+ constructor: Text,
2890
+
2891
+ // 初始化
2892
+ init: function init() {
2893
+ // 绑定事件
2894
+ this._bindEvent();
2895
+ },
2896
+
2897
+ // 清空内容
2898
+ clear: function clear() {
2899
+ this.html('<p><br></p>');
2900
+ },
2901
+
2902
+ // 获取 设置 html
2903
+ html: function html(val) {
2904
+ var editor = this.editor;
2905
+ var $textElem = editor.$textElem;
2906
+ if (val == null) {
2907
+ return $textElem.html();
2908
+ } else {
2909
+ $textElem.html(val);
2910
+
2911
+ // 初始化选取,将光标定位到内容尾部
2912
+ editor.initSelection();
2913
+ }
2914
+ },
2915
+
2916
+ // 获取 设置 text
2917
+ text: function text(val) {
2918
+ var editor = this.editor;
2919
+ var $textElem = editor.$textElem;
2920
+ if (val == null) {
2921
+ return $textElem.text();
2922
+ } else {
2923
+ $textElem.text('<p>' + val + '</p>');
2924
+
2925
+ // 初始化选取,将光标定位到内容尾部
2926
+ editor.initSelection();
2927
+ }
2928
+ },
2929
+
2930
+ // 追加内容
2931
+ append: function append(html) {
2932
+ var editor = this.editor;
2933
+ var $textElem = editor.$textElem;
2934
+ $textElem.append($(html));
2935
+
2936
+ // 初始化选取,将光标定位到内容尾部
2937
+ editor.initSelection();
2938
+ },
2939
+
2940
+ // 绑定事件
2941
+ _bindEvent: function _bindEvent() {
2942
+ // 实时保存选取
2943
+ this._saveRangeRealTime();
2944
+
2945
+ // 按回车建时的特殊处理
2946
+ this._enterKeyHandle();
2947
+
2948
+ // 清空时保留 <p><br></p>
2949
+ this._clearHandle();
2950
+
2951
+ // 粘贴事件(粘贴文字,粘贴图片)
2952
+ this._pasteHandle();
2953
+
2954
+ // tab 特殊处理
2955
+ this._tabHandle();
2956
+
2957
+ // img 点击
2958
+ this._imgHandle();
2959
+
2960
+ // 拖拽事件
2961
+ this._dragHandle();
2962
+ },
2963
+
2964
+ // 实时保存选取
2965
+ _saveRangeRealTime: function _saveRangeRealTime() {
2966
+ var editor = this.editor;
2967
+ var $textElem = editor.$textElem;
2968
+
2969
+ // 保存当前的选区
2970
+ function saveRange(e) {
2971
+ // 随时保存选区
2972
+ editor.selection.saveRange();
2973
+ // 更新按钮 ative 状态
2974
+ editor.menus.changeActive();
2975
+ }
2976
+ // 按键后保存
2977
+ $textElem.on('keyup', saveRange);
2978
+ $textElem.on('mousedown', function (e) {
2979
+ // mousedown 状态下,鼠标滑动到编辑区域外面,也需要保存选区
2980
+ $textElem.on('mouseleave', saveRange);
2981
+ });
2982
+ $textElem.on('mouseup', function (e) {
2983
+ saveRange();
2984
+ // 在编辑器区域之内完成点击,取消鼠标滑动到编辑区外面的事件
2985
+ $textElem.off('mouseleave', saveRange);
2986
+ });
2987
+ },
2988
+
2989
+ // 按回车键时的特殊处理
2990
+ _enterKeyHandle: function _enterKeyHandle() {
2991
+ var editor = this.editor;
2992
+ var $textElem = editor.$textElem;
2993
+
2994
+ // 将回车之后生成的非 <p> 的顶级标签,改为 <p>
2995
+ function pHandle(e) {
2996
+ var $selectionElem = editor.selection.getSelectionContainerElem();
2997
+ var $parentElem = $selectionElem.parent();
2998
+ if (!$parentElem.equal($textElem)) {
2999
+ // 不是顶级标签
3000
+ return;
3001
+ }
3002
+ var nodeName = $selectionElem.getNodeName();
3003
+ if (nodeName === 'P') {
3004
+ // 当前的标签是 P ,不用做处理
3005
+ return;
3006
+ }
3007
+
3008
+ if ($selectionElem.text()) {
3009
+ // 有内容,不做处理
3010
+ return;
3011
+ }
3012
+
3013
+ // 插入 <p> ,并将选取定位到 <p>,删除当前标签
3014
+ var $p = $('<p><br></p>');
3015
+ $p.insertBefore($selectionElem);
3016
+ editor.selection.createRangeByElem($p, true);
3017
+ editor.selection.restoreSelection();
3018
+ $selectionElem.remove();
3019
+ }
3020
+
3021
+ $textElem.on('keyup', function (e) {
3022
+ if (e.keyCode !== 13) {
3023
+ // 不是回车键
3024
+ return;
3025
+ }
3026
+ // 将回车之后生成的非 <p> 的顶级标签,改为 <p>
3027
+ pHandle(e);
3028
+ });
3029
+
3030
+ // <pre><code></code></pre> 回车时 特殊处理
3031
+ function codeHandle(e) {
3032
+ var $selectionElem = editor.selection.getSelectionContainerElem();
3033
+ if (!$selectionElem) {
3034
+ return;
3035
+ }
3036
+ var $parentElem = $selectionElem.parent();
3037
+ var selectionNodeName = $selectionElem.getNodeName();
3038
+ var parentNodeName = $parentElem.getNodeName();
3039
+
3040
+ if (selectionNodeName !== 'CODE' || parentNodeName !== 'PRE') {
3041
+ // 不符合要求 忽略
3042
+ return;
3043
+ }
3044
+
3045
+ if (!editor.cmd.queryCommandSupported('insertHTML')) {
3046
+ // 必须原生支持 insertHTML 命令
3047
+ return;
3048
+ }
3049
+
3050
+ // 处理:光标定位到代码末尾,联系点击两次回车,即跳出代码块
3051
+ if (editor._willBreakCode === true) {
3052
+ // 此时可以跳出代码块
3053
+ // 插入 <p> ,并将选取定位到 <p>
3054
+ var $p = $('<p><br></p>');
3055
+ $p.insertAfter($parentElem);
3056
+ editor.selection.createRangeByElem($p, true);
3057
+ editor.selection.restoreSelection();
3058
+
3059
+ // 修改状态
3060
+ editor._willBreakCode = false;
3061
+
3062
+ e.preventDefault();
3063
+ return;
3064
+ }
3065
+
3066
+ var _startOffset = editor.selection.getRange().startOffset;
3067
+
3068
+ // 处理:回车时,不能插入 <br> 而是插入 \n ,因为是在 pre 标签里面
3069
+ editor.cmd.do('insertHTML', '\n');
3070
+ editor.selection.saveRange();
3071
+ if (editor.selection.getRange().startOffset === _startOffset) {
3072
+ // 没起作用,再来一遍
3073
+ editor.cmd.do('insertHTML', '\n');
3074
+ }
3075
+
3076
+ var codeLength = $selectionElem.html().length;
3077
+ if (editor.selection.getRange().startOffset + 1 === codeLength) {
3078
+ // 说明光标在代码最后的位置,执行了回车操作
3079
+ // 记录下来,以便下次回车时候跳出 code
3080
+ editor._willBreakCode = true;
3081
+ }
3082
+
3083
+ // 阻止默认行为
3084
+ e.preventDefault();
3085
+ }
3086
+
3087
+ $textElem.on('keydown', function (e) {
3088
+ if (e.keyCode !== 13) {
3089
+ // 不是回车键
3090
+ // 取消即将跳转代码块的记录
3091
+ editor._willBreakCode = false;
3092
+ return;
3093
+ }
3094
+ // <pre><code></code></pre> 回车时 特殊处理
3095
+ codeHandle(e);
3096
+ });
3097
+ },
3098
+
3099
+ // 清空时保留 <p><br></p>
3100
+ _clearHandle: function _clearHandle() {
3101
+ var editor = this.editor;
3102
+ var $textElem = editor.$textElem;
3103
+
3104
+ $textElem.on('keydown', function (e) {
3105
+ if (e.keyCode !== 8) {
3106
+ return;
3107
+ }
3108
+ var txtHtml = $textElem.html().toLowerCase().trim();
3109
+ if (txtHtml === '<p><br></p>') {
3110
+ // 最后剩下一个空行,就不再删除了
3111
+ e.preventDefault();
3112
+ return;
3113
+ }
3114
+ });
3115
+
3116
+ $textElem.on('keyup', function (e) {
3117
+ if (e.keyCode !== 8) {
3118
+ return;
3119
+ }
3120
+ var $p = void 0;
3121
+ var txtHtml = $textElem.html().toLowerCase().trim();
3122
+
3123
+ // firefox 时用 txtHtml === '<br>' 判断,其他用 !txtHtml 判断
3124
+ if (!txtHtml || txtHtml === '<br>') {
3125
+ // 内容空了
3126
+ $p = $('<p><br/></p>');
3127
+ $textElem.html(''); // 一定要先清空,否则在 firefox 下有问题
3128
+ $textElem.append($p);
3129
+ editor.selection.createRangeByElem($p, false, true);
3130
+ editor.selection.restoreSelection();
3131
+ }
3132
+ });
3133
+ },
3134
+
3135
+ // 粘贴事件(粘贴文字 粘贴图片)
3136
+ _pasteHandle: function _pasteHandle() {
3137
+ var editor = this.editor;
3138
+ var pasteFilterStyle = editor.config.pasteFilterStyle;
3139
+ var $textElem = editor.$textElem;
3140
+
3141
+ // 粘贴文字
3142
+ $textElem.on('paste', function (e) {
3143
+ if (UA.isIE()) {
3144
+ return;
3145
+ } else {
3146
+ // 阻止默认行为,使用 execCommand 的粘贴命令
3147
+ e.preventDefault();
3148
+ }
3149
+
3150
+ // 获取粘贴的文字
3151
+ var pasteHtml = getPasteHtml(e, pasteFilterStyle);
3152
+ var pasteText = getPasteText(e);
3153
+ pasteText = pasteText.replace(/\n/gm, '<br>');
3154
+
3155
+ var $selectionElem = editor.selection.getSelectionContainerElem();
3156
+ if (!$selectionElem) {
3157
+ return;
3158
+ }
3159
+ var nodeName = $selectionElem.getNodeName();
3160
+
3161
+ // code 中只能粘贴纯文本
3162
+ if (nodeName === 'CODE' || nodeName === 'PRE') {
3163
+ editor.cmd.do('insertHTML', '<p>' + pasteText + '</p>');
3164
+ return;
3165
+ }
3166
+
3167
+ // 先放开注释,有问题再追查 ————
3168
+ // // 表格中忽略,可能会出现异常问题
3169
+ // if (nodeName === 'TD' || nodeName === 'TH') {
3170
+ // return
3171
+ // }
3172
+
3173
+ if (!pasteHtml) {
3174
+ return;
3175
+ }
3176
+ try {
3177
+ // firefox 中,获取的 pasteHtml 可能是没有 <ul> 包裹的 <li>
3178
+ // 因此执行 insertHTML 会报错
3179
+ editor.cmd.do('insertHTML', pasteHtml);
3180
+ } catch (ex) {
3181
+ // 此时使用 pasteText 来兼容一下
3182
+ editor.cmd.do('insertHTML', '<p>' + pasteText + '</p>');
3183
+ }
3184
+ });
3185
+
3186
+ // 粘贴图片
3187
+ $textElem.on('paste', function (e) {
3188
+ if (UA.isIE()) {
3189
+ return;
3190
+ } else {
3191
+ e.preventDefault();
3192
+ }
3193
+
3194
+ // 获取粘贴的图片
3195
+ var pasteFiles = getPasteImgs(e);
3196
+ if (!pasteFiles || !pasteFiles.length) {
3197
+ return;
3198
+ }
3199
+
3200
+ // 获取当前的元素
3201
+ var $selectionElem = editor.selection.getSelectionContainerElem();
3202
+ if (!$selectionElem) {
3203
+ return;
3204
+ }
3205
+ var nodeName = $selectionElem.getNodeName();
3206
+
3207
+ // code 中粘贴忽略
3208
+ if (nodeName === 'CODE' || nodeName === 'PRE') {
3209
+ return;
3210
+ }
3211
+
3212
+ // 上传图片
3213
+ var uploadImg = editor.uploadImg;
3214
+ uploadImg.uploadImg(pasteFiles);
3215
+ });
3216
+ },
3217
+
3218
+ // tab 特殊处理
3219
+ _tabHandle: function _tabHandle() {
3220
+ var editor = this.editor;
3221
+ var $textElem = editor.$textElem;
3222
+
3223
+ $textElem.on('keydown', function (e) {
3224
+ if (e.keyCode !== 9) {
3225
+ return;
3226
+ }
3227
+ if (!editor.cmd.queryCommandSupported('insertHTML')) {
3228
+ // 必须原生支持 insertHTML 命令
3229
+ return;
3230
+ }
3231
+ var $selectionElem = editor.selection.getSelectionContainerElem();
3232
+ if (!$selectionElem) {
3233
+ return;
3234
+ }
3235
+ var $parentElem = $selectionElem.parent();
3236
+ var selectionNodeName = $selectionElem.getNodeName();
3237
+ var parentNodeName = $parentElem.getNodeName();
3238
+
3239
+ if (selectionNodeName === 'CODE' && parentNodeName === 'PRE') {
3240
+ // <pre><code> 里面
3241
+ editor.cmd.do('insertHTML', ' ');
3242
+ } else {
3243
+ // 普通文字
3244
+ editor.cmd.do('insertHTML', '&nbsp;&nbsp;&nbsp;&nbsp;');
3245
+ }
3246
+
3247
+ e.preventDefault();
3248
+ });
3249
+ },
3250
+
3251
+ // img 点击
3252
+ _imgHandle: function _imgHandle() {
3253
+ var editor = this.editor;
3254
+ var $textElem = editor.$textElem;
3255
+ var selectedClass = 'w-e-selected';
3256
+
3257
+ // 为图片增加 selected 样式
3258
+ $textElem.on('click', 'img', function (e) {
3259
+ var img = this;
3260
+ var $img = $(img);
3261
+
3262
+ // 去掉所有图片的 selected 样式
3263
+ $textElem.find('img').removeClass(selectedClass);
3264
+
3265
+ // 为点击的图片增加样式,并记录当前图片
3266
+ $img.addClass(selectedClass);
3267
+ editor._selectedImg = $img;
3268
+
3269
+ // 修改选取
3270
+ editor.selection.createRangeByElem($img);
3271
+ });
3272
+
3273
+ // 去掉图片的 selected 样式
3274
+ $textElem.on('click keyup', function (e) {
3275
+ if (e.target.matches('img')) {
3276
+ // 点击的是图片,忽略
3277
+ return;
3278
+ }
3279
+ // 取消掉 selected 样式,并删除记录
3280
+ $textElem.find('img').removeClass(selectedClass);
3281
+ editor._selectedImg = null;
3282
+ });
3283
+ },
3284
+
3285
+ // 拖拽事件
3286
+ _dragHandle: function _dragHandle() {
3287
+ var editor = this.editor;
3288
+
3289
+ // 禁用 document 拖拽事件
3290
+ var $document = $(document);
3291
+ $document.on('dragleave drop dragenter dragover', function (e) {
3292
+ e.preventDefault();
3293
+ });
3294
+
3295
+ // 添加编辑区域拖拽事件
3296
+ var $textElem = editor.$textElem;
3297
+ $textElem.on('drop', function (e) {
3298
+ e.preventDefault();
3299
+ var files = e.dataTransfer && e.dataTransfer.files;
3300
+ if (!files || !files.length) {
3301
+ return;
3302
+ }
3303
+
3304
+ // 上传图片
3305
+ var uploadImg = editor.uploadImg;
3306
+ uploadImg.uploadImg(files);
3307
+ });
3308
+ }
3309
+ };
3310
+
3311
+ /*
3312
+ 命令,封装 document.execCommand
3313
+ */
3314
+
3315
+ // 构造函数
3316
+ function Command(editor) {
3317
+ this.editor = editor;
3318
+ }
3319
+
3320
+ // 修改原型
3321
+ Command.prototype = {
3322
+ constructor: Command,
3323
+
3324
+ // 执行命令
3325
+ do: function _do(name, value) {
3326
+ var editor = this.editor;
3327
+
3328
+ // 如果无选区,忽略
3329
+ if (!editor.selection.getRange()) {
3330
+ return;
3331
+ }
3332
+
3333
+ // 恢复选取
3334
+ editor.selection.restoreSelection();
3335
+
3336
+ // 执行
3337
+ var _name = '_' + name;
3338
+ if (this[_name]) {
3339
+ // 有自定义事件
3340
+ this[_name](value);
3341
+ } else {
3342
+ // 默认 command
3343
+ this._execCommand(name, value);
3344
+ }
3345
+
3346
+ // 修改菜单状态
3347
+ editor.menus.changeActive();
3348
+
3349
+ // 最后,恢复选取保证光标在原来的位置闪烁
3350
+ editor.selection.saveRange();
3351
+ editor.selection.restoreSelection();
3352
+
3353
+ // 触发 onchange
3354
+ editor.change && editor.change();
3355
+ },
3356
+
3357
+ // 自定义 insertHTML 事件
3358
+ _insertHTML: function _insertHTML(html) {
3359
+ var editor = this.editor;
3360
+ var range = editor.selection.getRange();
3361
+
3362
+ if (this.queryCommandSupported('insertHTML')) {
3363
+ // W3C
3364
+ this._execCommand('insertHTML', html);
3365
+ } else if (range.insertNode) {
3366
+ // IE
3367
+ range.deleteContents();
3368
+ range.insertNode($(html)[0]);
3369
+ } else if (range.pasteHTML) {
3370
+ // IE <= 10
3371
+ range.pasteHTML(html);
3372
+ }
3373
+ },
3374
+
3375
+ // 插入 elem
3376
+ _insertElem: function _insertElem($elem) {
3377
+ var editor = this.editor;
3378
+ var range = editor.selection.getRange();
3379
+
3380
+ if (range.insertNode) {
3381
+ range.deleteContents();
3382
+ range.insertNode($elem[0]);
3383
+ }
3384
+ },
3385
+
3386
+ // 封装 execCommand
3387
+ _execCommand: function _execCommand(name, value) {
3388
+ document.execCommand(name, false, value);
3389
+ },
3390
+
3391
+ // 封装 document.queryCommandValue
3392
+ queryCommandValue: function queryCommandValue(name) {
3393
+ return document.queryCommandValue(name);
3394
+ },
3395
+
3396
+ // 封装 document.queryCommandState
3397
+ queryCommandState: function queryCommandState(name) {
3398
+ return document.queryCommandState(name);
3399
+ },
3400
+
3401
+ // 封装 document.queryCommandSupported
3402
+ queryCommandSupported: function queryCommandSupported(name) {
3403
+ return document.queryCommandSupported(name);
3404
+ }
3405
+ };
3406
+
3407
+ /*
3408
+ selection range API
3409
+ */
3410
+
3411
+ // 构造函数
3412
+ function API(editor) {
3413
+ this.editor = editor;
3414
+ this._currentRange = null;
3415
+ }
3416
+
3417
+ // 修改原型
3418
+ API.prototype = {
3419
+ constructor: API,
3420
+
3421
+ // 获取 range 对象
3422
+ getRange: function getRange() {
3423
+ return this._currentRange;
3424
+ },
3425
+
3426
+ // 保存选区
3427
+ saveRange: function saveRange(_range) {
3428
+ if (_range) {
3429
+ // 保存已有选区
3430
+ this._currentRange = _range;
3431
+ return;
3432
+ }
3433
+
3434
+ // 获取当前的选区
3435
+ var selection = window.getSelection();
3436
+ if (selection.rangeCount === 0) {
3437
+ return;
3438
+ }
3439
+ var range = selection.getRangeAt(0);
3440
+
3441
+ // 判断选区内容是否在编辑内容之内
3442
+ var $containerElem = this.getSelectionContainerElem(range);
3443
+ if (!$containerElem) {
3444
+ return;
3445
+ }
3446
+ var editor = this.editor;
3447
+ var $textElem = editor.$textElem;
3448
+ if ($textElem.isContain($containerElem)) {
3449
+ // 是编辑内容之内的
3450
+ this._currentRange = range;
3451
+ }
3452
+ },
3453
+
3454
+ // 折叠选区
3455
+ collapseRange: function collapseRange(toStart) {
3456
+ if (toStart == null) {
3457
+ // 默认为 false
3458
+ toStart = false;
3459
+ }
3460
+ var range = this._currentRange;
3461
+ if (range) {
3462
+ range.collapse(toStart);
3463
+ }
3464
+ },
3465
+
3466
+ // 选中区域的文字
3467
+ getSelectionText: function getSelectionText() {
3468
+ var range = this._currentRange;
3469
+ if (range) {
3470
+ return this._currentRange.toString();
3471
+ } else {
3472
+ return '';
3473
+ }
3474
+ },
3475
+
3476
+ // 选区的 $Elem
3477
+ getSelectionContainerElem: function getSelectionContainerElem(range) {
3478
+ range = range || this._currentRange;
3479
+ var elem = void 0;
3480
+ if (range) {
3481
+ elem = range.commonAncestorContainer;
3482
+ return $(elem.nodeType === 1 ? elem : elem.parentNode);
3483
+ }
3484
+ },
3485
+ getSelectionStartElem: function getSelectionStartElem(range) {
3486
+ range = range || this._currentRange;
3487
+ var elem = void 0;
3488
+ if (range) {
3489
+ elem = range.startContainer;
3490
+ return $(elem.nodeType === 1 ? elem : elem.parentNode);
3491
+ }
3492
+ },
3493
+ getSelectionEndElem: function getSelectionEndElem(range) {
3494
+ range = range || this._currentRange;
3495
+ var elem = void 0;
3496
+ if (range) {
3497
+ elem = range.endContainer;
3498
+ return $(elem.nodeType === 1 ? elem : elem.parentNode);
3499
+ }
3500
+ },
3501
+
3502
+ // 选区是否为空
3503
+ isSelectionEmpty: function isSelectionEmpty() {
3504
+ var range = this._currentRange;
3505
+ if (range && range.startContainer) {
3506
+ if (range.startContainer === range.endContainer) {
3507
+ if (range.startOffset === range.endOffset) {
3508
+ return true;
3509
+ }
3510
+ }
3511
+ }
3512
+ return false;
3513
+ },
3514
+
3515
+ // 恢复选区
3516
+ restoreSelection: function restoreSelection() {
3517
+ var selection = window.getSelection();
3518
+ selection.removeAllRanges();
3519
+ selection.addRange(this._currentRange);
3520
+ },
3521
+
3522
+ // 创建一个空白(即 &#8203 字符)选区
3523
+ createEmptyRange: function createEmptyRange() {
3524
+ var editor = this.editor;
3525
+ var range = this.getRange();
3526
+ var $elem = void 0;
3527
+
3528
+ if (!range) {
3529
+ // 当前无 range
3530
+ return;
3531
+ }
3532
+ if (!this.isSelectionEmpty()) {
3533
+ // 当前选区必须没有内容才可以
3534
+ return;
3535
+ }
3536
+
3537
+ try {
3538
+ // 目前只支持 webkit 内核
3539
+ if (UA.isWebkit()) {
3540
+ // 插入 &#8203
3541
+ editor.cmd.do('insertHTML', '&#8203;');
3542
+ // 修改 offset 位置
3543
+ range.setEnd(range.endContainer, range.endOffset + 1);
3544
+ // 存储
3545
+ this.saveRange(range);
3546
+ } else {
3547
+ $elem = $('<strong>&#8203;</strong>');
3548
+ editor.cmd.do('insertElem', $elem);
3549
+ this.createRangeByElem($elem, true);
3550
+ }
3551
+ } catch (ex) {
3552
+ // 部分情况下会报错,兼容一下
3553
+ }
3554
+ },
3555
+
3556
+ // 根据 $Elem 设置选区
3557
+ createRangeByElem: function createRangeByElem($elem, toStart, isContent) {
3558
+ // $elem - 经过封装的 elem
3559
+ // toStart - true 开始位置,false 结束位置
3560
+ // isContent - 是否选中Elem的内容
3561
+ if (!$elem.length) {
3562
+ return;
3563
+ }
3564
+
3565
+ var elem = $elem[0];
3566
+ var range = document.createRange();
3567
+
3568
+ if (isContent) {
3569
+ range.selectNodeContents(elem);
3570
+ } else {
3571
+ range.selectNode(elem);
3572
+ }
3573
+
3574
+ if (typeof toStart === 'boolean') {
3575
+ range.collapse(toStart);
3576
+ }
3577
+
3578
+ // 存储 range
3579
+ this.saveRange(range);
3580
+ }
3581
+ };
3582
+
3583
+ /*
3584
+ 上传进度条
3585
+ */
3586
+
3587
+ function Progress(editor) {
3588
+ this.editor = editor;
3589
+ this._time = 0;
3590
+ this._isShow = false;
3591
+ this._isRender = false;
3592
+ this._timeoutId = 0;
3593
+ this.$textContainer = editor.$textContainerElem;
3594
+ this.$bar = $('<div class="w-e-progress"></div>');
3595
+ }
3596
+
3597
+ Progress.prototype = {
3598
+ constructor: Progress,
3599
+
3600
+ show: function show(progress) {
3601
+ var _this = this;
3602
+
3603
+ // 状态处理
3604
+ if (this._isShow) {
3605
+ return;
3606
+ }
3607
+ this._isShow = true;
3608
+
3609
+ // 渲染
3610
+ var $bar = this.$bar;
3611
+ if (!this._isRender) {
3612
+ var $textContainer = this.$textContainer;
3613
+ $textContainer.append($bar);
3614
+ } else {
3615
+ this._isRender = true;
3616
+ }
3617
+
3618
+ // 改变进度(节流,100ms 渲染一次)
3619
+ if (Date.now() - this._time > 100) {
3620
+ if (progress <= 1) {
3621
+ $bar.css('width', progress * 100 + '%');
3622
+ this._time = Date.now();
3623
+ }
3624
+ }
3625
+
3626
+ // 隐藏
3627
+ var timeoutId = this._timeoutId;
3628
+ if (timeoutId) {
3629
+ clearTimeout(timeoutId);
3630
+ }
3631
+ timeoutId = setTimeout(function () {
3632
+ _this._hide();
3633
+ }, 500);
3634
+ },
3635
+
3636
+ _hide: function _hide() {
3637
+ var $bar = this.$bar;
3638
+ $bar.remove();
3639
+
3640
+ // 修改状态
3641
+ this._time = 0;
3642
+ this._isShow = false;
3643
+ this._isRender = false;
3644
+ }
3645
+ };
3646
+
3647
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
3648
+ return typeof obj;
3649
+ } : function (obj) {
3650
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
3651
+ };
3652
+
3653
+ /*
3654
+ 上传图片
3655
+ */
3656
+
3657
+ // 构造函数
3658
+ function UploadImg(editor) {
3659
+ this.editor = editor;
3660
+ }
3661
+
3662
+ // 原型
3663
+ UploadImg.prototype = {
3664
+ constructor: UploadImg,
3665
+
3666
+ // 根据 debug 弹出不同的信息
3667
+ _alert: function _alert(alertInfo, debugInfo) {
3668
+ var editor = this.editor;
3669
+ var debug = editor.config.debug;
3670
+ var customAlert = editor.config.customAlert;
3671
+
3672
+ if (debug) {
3673
+ throw new Error('wangEditor: ' + (debugInfo || alertInfo));
3674
+ } else {
3675
+ if (customAlert && typeof customAlert === 'function') {
3676
+ customAlert(alertInfo);
3677
+ } else {
3678
+ alert(alertInfo);
3679
+ }
3680
+ }
3681
+ },
3682
+
3683
+ // 根据链接插入图片
3684
+ insertLinkImg: function insertLinkImg(link) {
3685
+ var _this2 = this;
3686
+
3687
+ if (!link) {
3688
+ return;
3689
+ }
3690
+ var editor = this.editor;
3691
+ var config = editor.config;
3692
+ editor.cmd.do('insertHTML', '<img src="' + link + '" style="max-width:100%;"/>');
3693
+
3694
+ // 验证图片 url 是否有效,无效的话给出提示
3695
+ var img = document.createElement('img');
3696
+ img.onload = function () {
3697
+ var callback = config.linkImgCallback;
3698
+ if (callback && typeof callback === 'function') {
3699
+ callback(link);
3700
+ }
3701
+
3702
+ img = null;
3703
+ };
3704
+ img.onerror = function () {
3705
+ img = null;
3706
+ // 无法成功下载图片
3707
+ _this2._alert('插入图片错误', 'wangEditor: \u63D2\u5165\u56FE\u7247\u51FA\u9519\uFF0C\u56FE\u7247\u94FE\u63A5\u662F "' + link + '"\uFF0C\u4E0B\u8F7D\u8BE5\u94FE\u63A5\u5931\u8D25');
3708
+ return;
3709
+ };
3710
+ img.onabort = function () {
3711
+ img = null;
3712
+ };
3713
+ img.src = link;
3714
+ },
3715
+
3716
+ // 上传图片
3717
+ uploadImg: function uploadImg(files) {
3718
+ var _this3 = this;
3719
+
3720
+ if (!files || !files.length) {
3721
+ return;
3722
+ }
3723
+
3724
+ // ------------------------------ 获取配置信息 ------------------------------
3725
+ var editor = this.editor;
3726
+ var config = editor.config;
3727
+ var uploadImgServer = config.uploadImgServer;
3728
+ var uploadImgShowBase64 = config.uploadImgShowBase64;
3729
+ if (!uploadImgServer && !uploadImgShowBase64) {
3730
+ return;
3731
+ }
3732
+
3733
+ var maxSize = config.uploadImgMaxSize;
3734
+ var maxSizeM = maxSize / 1000 / 1000;
3735
+ var maxLength = config.uploadImgMaxLength || 10000;
3736
+ var uploadFileName = config.uploadFileName || '';
3737
+ var uploadImgParams = config.uploadImgParams || {};
3738
+ var uploadImgHeaders = config.uploadImgHeaders || {};
3739
+ var hooks = config.uploadImgHooks || {};
3740
+ var timeout = config.uploadImgTimeout || 3000;
3741
+ var withCredentials = config.withCredentials;
3742
+ if (withCredentials == null) {
3743
+ withCredentials = false;
3744
+ }
3745
+ var customUploadImg = config.customUploadImg;
3746
+
3747
+ // ------------------------------ 验证文件信息 ------------------------------
3748
+ var resultFiles = [];
3749
+ var errInfo = [];
3750
+ arrForEach(files, function (file) {
3751
+ var name = file.name;
3752
+ var size = file.size;
3753
+
3754
+ // chrome 低版本 name === undefined
3755
+ if (!name || !size) {
3756
+ return;
3757
+ }
3758
+
3759
+ if (/\.(jpg|jpeg|png|bmp|gif)$/i.test(name) === false) {
3760
+ // 后缀名不合法,不是图片
3761
+ errInfo.push('\u3010' + name + '\u3011\u4E0D\u662F\u56FE\u7247');
3762
+ return;
3763
+ }
3764
+ if (maxSize < size) {
3765
+ // 上传图片过大
3766
+ errInfo.push('\u3010' + name + '\u3011\u5927\u4E8E ' + maxSizeM + 'M');
3767
+ return;
3768
+ }
3769
+
3770
+ // 验证通过的加入结果列表
3771
+ resultFiles.push(file);
3772
+ });
3773
+ // 抛出验证信息
3774
+ if (errInfo.length) {
3775
+ this._alert('图片验证未通过: \n' + errInfo.join('\n'));
3776
+ return;
3777
+ }
3778
+ if (resultFiles.length > maxLength) {
3779
+ this._alert('一次最多上传' + maxLength + '张图片');
3780
+ return;
3781
+ }
3782
+
3783
+ // ------------------------------ 自定义上传 ------------------------------
3784
+ if (customUploadImg && typeof customUploadImg === 'function') {
3785
+ customUploadImg(resultFiles, this.insertLinkImg.bind(this));
3786
+
3787
+ // 阻止以下代码执行
3788
+ return;
3789
+ }
3790
+
3791
+ // 添加图片数据
3792
+ var formdata = new FormData();
3793
+ arrForEach(resultFiles, function (file) {
3794
+ var name = uploadFileName || file.name;
3795
+ formdata.append(name, file);
3796
+ });
3797
+
3798
+ // ------------------------------ 上传图片 ------------------------------
3799
+ if (uploadImgServer && typeof uploadImgServer === 'string') {
3800
+ // 添加参数
3801
+ var uploadImgServerArr = uploadImgServer.split('#');
3802
+ uploadImgServer = uploadImgServerArr[0];
3803
+ var uploadImgServerHash = uploadImgServerArr[1] || '';
3804
+ objForEach(uploadImgParams, function (key, val) {
3805
+ val = encodeURIComponent(val);
3806
+
3807
+ // 第一,将参数拼接到 url 中
3808
+ if (uploadImgServer.indexOf('?') > 0) {
3809
+ uploadImgServer += '&';
3810
+ } else {
3811
+ uploadImgServer += '?';
3812
+ }
3813
+ uploadImgServer = uploadImgServer + key + '=' + val;
3814
+
3815
+ // 第二,将参数添加到 formdata 中
3816
+ formdata.append(key, val);
3817
+ });
3818
+ if (uploadImgServerHash) {
3819
+ uploadImgServer += '#' + uploadImgServerHash;
3820
+ }
3821
+
3822
+ // 定义 xhr
3823
+ var xhr = new XMLHttpRequest();
3824
+ xhr.open('POST', uploadImgServer);
3825
+
3826
+ // 设置超时
3827
+ xhr.timeout = timeout;
3828
+ xhr.ontimeout = function () {
3829
+ // hook - timeout
3830
+ if (hooks.timeout && typeof hooks.timeout === 'function') {
3831
+ hooks.timeout(xhr, editor);
3832
+ }
3833
+
3834
+ _this3._alert('上传图片超时');
3835
+ };
3836
+
3837
+ // 监控 progress
3838
+ if (xhr.upload) {
3839
+ xhr.upload.onprogress = function (e) {
3840
+ var percent = void 0;
3841
+ // 进度条
3842
+ var progressBar = new Progress(editor);
3843
+ if (e.lengthComputable) {
3844
+ percent = e.loaded / e.total;
3845
+ progressBar.show(percent);
3846
+ }
3847
+ };
3848
+ }
3849
+
3850
+ // 返回数据
3851
+ xhr.onreadystatechange = function () {
3852
+ var result = void 0;
3853
+ if (xhr.readyState === 4) {
3854
+ if (xhr.status < 200 || xhr.status >= 300) {
3855
+ // hook - error
3856
+ if (hooks.error && typeof hooks.error === 'function') {
3857
+ hooks.error(xhr, editor);
3858
+ }
3859
+
3860
+ // xhr 返回状态错误
3861
+ _this3._alert('上传图片发生错误', '\u4E0A\u4F20\u56FE\u7247\u53D1\u751F\u9519\u8BEF\uFF0C\u670D\u52A1\u5668\u8FD4\u56DE\u72B6\u6001\u662F ' + xhr.status);
3862
+ return;
3863
+ }
3864
+
3865
+ result = xhr.responseText;
3866
+ if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) !== 'object') {
3867
+ try {
3868
+ result = JSON.parse(result);
3869
+ } catch (ex) {
3870
+ // hook - fail
3871
+ if (hooks.fail && typeof hooks.fail === 'function') {
3872
+ hooks.fail(xhr, editor, result);
3873
+ }
3874
+
3875
+ _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果是: ' + result);
3876
+ return;
3877
+ }
3878
+ }
3879
+ if (!hooks.customInsert && result.errno != '0') {
3880
+ // hook - fail
3881
+ if (hooks.fail && typeof hooks.fail === 'function') {
3882
+ hooks.fail(xhr, editor, result);
3883
+ }
3884
+
3885
+ // 数据错误
3886
+ _this3._alert('上传图片失败', '上传图片返回结果错误,返回结果 errno=' + result.errno);
3887
+ } else {
3888
+ if (hooks.customInsert && typeof hooks.customInsert === 'function') {
3889
+ // 使用者自定义插入方法
3890
+ hooks.customInsert(_this3.insertLinkImg.bind(_this3), result, editor);
3891
+ } else {
3892
+ // 将图片插入编辑器
3893
+ var data = result.data || [];
3894
+ data.forEach(function (link) {
3895
+ _this3.insertLinkImg(link);
3896
+ });
3897
+ }
3898
+
3899
+ // hook - success
3900
+ if (hooks.success && typeof hooks.success === 'function') {
3901
+ hooks.success(xhr, editor, result);
3902
+ }
3903
+ }
3904
+ }
3905
+ };
3906
+
3907
+ // hook - before
3908
+ if (hooks.before && typeof hooks.before === 'function') {
3909
+ var beforeResult = hooks.before(xhr, editor, resultFiles);
3910
+ if (beforeResult && (typeof beforeResult === 'undefined' ? 'undefined' : _typeof(beforeResult)) === 'object') {
3911
+ if (beforeResult.prevent) {
3912
+ // 如果返回的结果是 {prevent: true, msg: 'xxxx'} 则表示用户放弃上传
3913
+ this._alert(beforeResult.msg);
3914
+ return;
3915
+ }
3916
+ }
3917
+ }
3918
+
3919
+ // 自定义 headers
3920
+ objForEach(uploadImgHeaders, function (key, val) {
3921
+ xhr.setRequestHeader(key, val);
3922
+ });
3923
+
3924
+ // 跨域传 cookie
3925
+ xhr.withCredentials = withCredentials;
3926
+
3927
+ // 发送请求
3928
+ xhr.send(formdata);
3929
+
3930
+ // 注意,要 return 。不去操作接下来的 base64 显示方式
3931
+ return;
3932
+ }
3933
+
3934
+ // ------------------------------ 显示 base64 格式 ------------------------------
3935
+ if (uploadImgShowBase64) {
3936
+ arrForEach(files, function (file) {
3937
+ var _this = _this3;
3938
+ var reader = new FileReader();
3939
+ reader.readAsDataURL(file);
3940
+ reader.onload = function () {
3941
+ _this.insertLinkImg(this.result);
3942
+ };
3943
+ });
3944
+ }
3945
+ }
3946
+ };
3947
+
3948
+ /*
3949
+ 编辑器构造函数
3950
+ */
3951
+
3952
+ // id,累加
3953
+ var editorId = 1;
3954
+
3955
+ // 构造函数
3956
+ function Editor(toolbarSelector, textSelector) {
3957
+ if (toolbarSelector == null) {
3958
+ // 没有传入任何参数,报错
3959
+ throw new Error('错误:初始化编辑器时候未传入任何参数,请查阅文档');
3960
+ }
3961
+ // id,用以区分单个页面不同的编辑器对象
3962
+ this.id = 'wangEditor-' + editorId++;
3963
+
3964
+ this.toolbarSelector = toolbarSelector;
3965
+ this.textSelector = textSelector;
3966
+
3967
+ // 自定义配置
3968
+ this.customConfig = {};
3969
+ }
3970
+
3971
+ // 修改原型
3972
+ Editor.prototype = {
3973
+ constructor: Editor,
3974
+
3975
+ // 初始化配置
3976
+ _initConfig: function _initConfig() {
3977
+ // _config 是默认配置,this.customConfig 是用户自定义配置,将它们 merge 之后再赋值
3978
+ var target = {};
3979
+ this.config = Object.assign(target, config, this.customConfig);
3980
+
3981
+ // 将语言配置,生成正则表达式
3982
+ var langConfig = this.config.lang || {};
3983
+ var langArgs = [];
3984
+ objForEach(langConfig, function (key, val) {
3985
+ // key 即需要生成正则表达式的规则,如“插入链接”
3986
+ // val 即需要被替换成的语言,如“insert link”
3987
+ langArgs.push({
3988
+ reg: new RegExp(key, 'img'),
3989
+ val: val
3990
+
3991
+ });
3992
+ });
3993
+ this.config.langArgs = langArgs;
3994
+ },
3995
+
3996
+ // 初始化 DOM
3997
+ _initDom: function _initDom() {
3998
+ var _this = this;
3999
+
4000
+ var toolbarSelector = this.toolbarSelector;
4001
+ var $toolbarSelector = $(toolbarSelector);
4002
+ var textSelector = this.textSelector;
4003
+
4004
+ var config$$1 = this.config;
4005
+ var zIndex = config$$1.zIndex;
4006
+
4007
+ // 定义变量
4008
+ var $toolbarElem = void 0,
4009
+ $textContainerElem = void 0,
4010
+ $textElem = void 0,
4011
+ $children = void 0;
4012
+
4013
+ if (textSelector == null) {
4014
+ // 只传入一个参数,即是容器的选择器或元素,toolbar 和 text 的元素自行创建
4015
+ $toolbarElem = $('<div></div>');
4016
+ $textContainerElem = $('<div></div>');
4017
+
4018
+ // 将编辑器区域原有的内容,暂存起来
4019
+ $children = $toolbarSelector.children();
4020
+
4021
+ // 添加到 DOM 结构中
4022
+ $toolbarSelector.append($toolbarElem).append($textContainerElem);
4023
+
4024
+ // 自行创建的,需要配置默认的样式
4025
+ $toolbarElem.css('background-color', '#f1f1f1').css('border', '1px solid #ccc');
4026
+ $textContainerElem.css('border', '1px solid #ccc').css('border-top', 'none').css('height', '300px');
4027
+ } else {
4028
+ // toolbar 和 text 的选择器都有值,记录属性
4029
+ $toolbarElem = $toolbarSelector;
4030
+ $textContainerElem = $(textSelector);
4031
+ // 将编辑器区域原有的内容,暂存起来
4032
+ $children = $textContainerElem.children();
4033
+ }
4034
+
4035
+ // 编辑区域
4036
+ $textElem = $('<div></div>');
4037
+ $textElem.attr('contenteditable', 'true').css('width', '100%').css('height', '100%');
4038
+
4039
+ // 初始化编辑区域内容
4040
+ if ($children && $children.length) {
4041
+ $textElem.append($children);
4042
+ } else {
4043
+ $textElem.append($('<p><br></p>'));
4044
+ }
4045
+
4046
+ // 编辑区域加入DOM
4047
+ $textContainerElem.append($textElem);
4048
+
4049
+ // 设置通用的 class
4050
+ $toolbarElem.addClass('w-e-toolbar');
4051
+ $textContainerElem.addClass('w-e-text-container');
4052
+ $textContainerElem.css('z-index', zIndex);
4053
+ $textElem.addClass('w-e-text');
4054
+
4055
+ // 添加 ID
4056
+ var toolbarElemId = getRandom('toolbar-elem');
4057
+ $toolbarElem.attr('id', toolbarElemId);
4058
+ var textElemId = getRandom('text-elem');
4059
+ $textElem.attr('id', textElemId);
4060
+
4061
+ // 记录属性
4062
+ this.$toolbarElem = $toolbarElem;
4063
+ this.$textContainerElem = $textContainerElem;
4064
+ this.$textElem = $textElem;
4065
+ this.toolbarElemId = toolbarElemId;
4066
+ this.textElemId = textElemId;
4067
+
4068
+ // 绑定 onchange
4069
+ $textContainerElem.on('click keyup', function () {
4070
+ _this.change && _this.change();
4071
+ });
4072
+ $toolbarElem.on('click', function () {
4073
+ this.change && this.change();
4074
+ });
4075
+ },
4076
+
4077
+ // 封装 command
4078
+ _initCommand: function _initCommand() {
4079
+ this.cmd = new Command(this);
4080
+ },
4081
+
4082
+ // 封装 selection range API
4083
+ _initSelectionAPI: function _initSelectionAPI() {
4084
+ this.selection = new API(this);
4085
+ },
4086
+
4087
+ // 添加图片上传
4088
+ _initUploadImg: function _initUploadImg() {
4089
+ this.uploadImg = new UploadImg(this);
4090
+ },
4091
+
4092
+ // 初始化菜单
4093
+ _initMenus: function _initMenus() {
4094
+ this.menus = new Menus(this);
4095
+ this.menus.init();
4096
+ },
4097
+
4098
+ // 添加 text 区域
4099
+ _initText: function _initText() {
4100
+ this.txt = new Text(this);
4101
+ this.txt.init();
4102
+ },
4103
+
4104
+ // 初始化选区,将光标定位到内容尾部
4105
+ initSelection: function initSelection(newLine) {
4106
+ var $textElem = this.$textElem;
4107
+ var $children = $textElem.children();
4108
+ if (!$children.length) {
4109
+ // 如果编辑器区域无内容,添加一个空行,重新设置选区
4110
+ $textElem.append($('<p><br></p>'));
4111
+ this.initSelection();
4112
+ return;
4113
+ }
4114
+
4115
+ var $last = $children.last();
4116
+
4117
+ if (newLine) {
4118
+ // 新增一个空行
4119
+ var html = $last.html().toLowerCase();
4120
+ var nodeName = $last.getNodeName();
4121
+ if (html !== '<br>' && html !== '<br\/>' || nodeName !== 'P') {
4122
+ // 最后一个元素不是 <p><br></p>,添加一个空行,重新设置选区
4123
+ $textElem.append($('<p><br></p>'));
4124
+ this.initSelection();
4125
+ return;
4126
+ }
4127
+ }
4128
+
4129
+ this.selection.createRangeByElem($last, false, true);
4130
+ this.selection.restoreSelection();
4131
+ },
4132
+
4133
+ // 绑定事件
4134
+ _bindEvent: function _bindEvent() {
4135
+ // -------- 绑定 onchange 事件 --------
4136
+ var onChangeTimeoutId = 0;
4137
+ var beforeChangeHtml = this.txt.html();
4138
+ var config$$1 = this.config;
4139
+ var onchange = config$$1.onchange;
4140
+ if (onchange && typeof onchange === 'function') {
4141
+ // 触发 change 的有三个场景:
4142
+ // 1. $textContainerElem.on('click keyup')
4143
+ // 2. $toolbarElem.on('click')
4144
+ // 3. editor.cmd.do()
4145
+ this.change = function () {
4146
+ // 判断是否有变化
4147
+ var currentHtml = this.txt.html();
4148
+ if (currentHtml.length === beforeChangeHtml.length) {
4149
+ return;
4150
+ }
4151
+
4152
+ // 执行,使用节流
4153
+ if (onChangeTimeoutId) {
4154
+ clearTimeout(onChangeTimeoutId);
4155
+ }
4156
+ onChangeTimeoutId = setTimeout(function () {
4157
+ // 触发配置的 onchange 函数
4158
+ onchange(currentHtml);
4159
+ beforeChangeHtml = currentHtml;
4160
+ }, 200);
4161
+ };
4162
+ }
4163
+ },
4164
+
4165
+ // 创建编辑器
4166
+ create: function create() {
4167
+ // 初始化配置信息
4168
+ this._initConfig();
4169
+
4170
+ // 初始化 DOM
4171
+ this._initDom();
4172
+
4173
+ // 封装 command API
4174
+ this._initCommand();
4175
+
4176
+ // 封装 selection range API
4177
+ this._initSelectionAPI();
4178
+
4179
+ // 添加 text
4180
+ this._initText();
4181
+
4182
+ // 初始化菜单
4183
+ this._initMenus();
4184
+
4185
+ // 添加 图片上传
4186
+ this._initUploadImg();
4187
+
4188
+ // 初始化选区,将光标定位到内容尾部
4189
+ this.initSelection(true);
4190
+
4191
+ // 绑定事件
4192
+ this._bindEvent();
4193
+ }
4194
+ };
4195
+
4196
+ // 检验是否浏览器环境
4197
+ try {
4198
+ document;
4199
+ } catch (ex) {
4200
+ throw new Error('请在浏览器环境下运行');
4201
+ }
4202
+
4203
+ // polyfill
4204
+ polyfill();
4205
+
4206
+ // 这里的 `inlinecss` 将被替换成 css 代码的内容,详情可去 ./gulpfile.js 中搜索 `inlinecss` 关键字
4207
+ 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-toolbar { display: -webkit-box; display: -ms-flexbox; display: flex; padding: 0 5px; /* 单个菜单 */}.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;}.w-e-text img.w-e-selected { border: 2px solid #1e88e5;}.w-e-text img.w-e-selected:hover { box-shadow: none;}';
4208
+
4209
+ // 将 css 代码添加到 <style> 中
4210
+ var style = document.createElement('style');
4211
+ style.type = 'text/css';
4212
+ style.innerHTML = inlinecss;
4213
+ document.getElementsByTagName('HEAD').item(0).appendChild(style);
4214
+
4215
+ // 返回
4216
+ var index = window.wangEditor || Editor;
4217
+
4218
+ return index;
4219
+
4220
+ })));