suneditor 2.44.2 → 2.44.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/LICENSE.txt +20 -20
  2. package/README.md +1585 -1585
  3. package/dist/css/suneditor.min.css +1 -1
  4. package/dist/suneditor.min.js +2 -2
  5. package/package.json +71 -71
  6. package/src/assets/css/suneditor-contents.css +556 -556
  7. package/src/assets/css/suneditor.css +0 -0
  8. package/src/assets/defaultIcons.js +103 -103
  9. package/src/lang/Lang.d.ts +143 -143
  10. package/src/lang/ckb.d.ts +4 -4
  11. package/src/lang/ckb.js +187 -187
  12. package/src/lang/da.d.ts +4 -4
  13. package/src/lang/da.js +191 -191
  14. package/src/lang/de.d.ts +4 -4
  15. package/src/lang/de.js +187 -187
  16. package/src/lang/en.d.ts +4 -4
  17. package/src/lang/en.js +187 -187
  18. package/src/lang/es.d.ts +4 -4
  19. package/src/lang/es.js +187 -187
  20. package/src/lang/fr.d.ts +4 -4
  21. package/src/lang/fr.js +188 -188
  22. package/src/lang/he.d.ts +4 -4
  23. package/src/lang/he.js +188 -188
  24. package/src/lang/index.d.ts +22 -21
  25. package/src/lang/index.js +25 -24
  26. package/src/lang/it.d.ts +4 -4
  27. package/src/lang/it.js +188 -188
  28. package/src/lang/ja.d.ts +4 -4
  29. package/src/lang/ja.js +187 -187
  30. package/src/lang/ko.d.ts +4 -4
  31. package/src/lang/ko.js +187 -187
  32. package/src/lang/lv.d.ts +4 -4
  33. package/src/lang/lv.js +187 -187
  34. package/src/lang/nl.d.ts +4 -4
  35. package/src/lang/nl.js +187 -187
  36. package/src/lang/pl.d.ts +4 -4
  37. package/src/lang/pl.js +187 -187
  38. package/src/lang/pt_br.d.ts +4 -4
  39. package/src/lang/pt_br.js +189 -189
  40. package/src/lang/ro.d.ts +4 -4
  41. package/src/lang/ro.js +187 -187
  42. package/src/lang/ru.d.ts +4 -4
  43. package/src/lang/ru.js +187 -187
  44. package/src/lang/se.d.ts +4 -4
  45. package/src/lang/se.js +191 -191
  46. package/src/lang/ua.d.ts +5 -5
  47. package/src/lang/ua.js +188 -188
  48. package/src/lang/ur.d.ts +5 -0
  49. package/src/lang/ur.js +188 -0
  50. package/src/lang/zh_cn.d.ts +4 -4
  51. package/src/lang/zh_cn.js +187 -187
  52. package/src/lib/constructor.js +1 -2
  53. package/src/lib/context.d.ts +42 -42
  54. package/src/lib/context.js +0 -0
  55. package/src/lib/core.d.ts +1101 -1101
  56. package/src/lib/core.js +220 -99
  57. package/src/lib/history.d.ts +48 -48
  58. package/src/lib/history.js +218 -218
  59. package/src/lib/util.d.ts +677 -677
  60. package/src/lib/util.js +38 -12
  61. package/src/options.d.ts +608 -608
  62. package/src/plugins/CommandPlugin.d.ts +7 -7
  63. package/src/plugins/DialogPlugin.d.ts +19 -19
  64. package/src/plugins/FileBrowserPlugin.d.ts +29 -29
  65. package/src/plugins/Module.d.ts +14 -14
  66. package/src/plugins/Plugin.d.ts +41 -41
  67. package/src/plugins/SubmenuPlugin.d.ts +7 -7
  68. package/src/plugins/command/blockquote.d.ts +4 -4
  69. package/src/plugins/command/blockquote.js +46 -46
  70. package/src/plugins/dialog/audio.d.ts +4 -4
  71. package/src/plugins/dialog/audio.js +556 -554
  72. package/src/plugins/dialog/image.d.ts +4 -4
  73. package/src/plugins/dialog/image.js +1122 -1107
  74. package/src/plugins/dialog/link.d.ts +4 -4
  75. package/src/plugins/dialog/link.js +223 -223
  76. package/src/plugins/dialog/math.d.ts +4 -4
  77. package/src/plugins/dialog/math.js +294 -294
  78. package/src/plugins/dialog/mention.d.ts +5 -5
  79. package/src/plugins/dialog/mention.js +242 -242
  80. package/src/plugins/dialog/video.d.ts +4 -4
  81. package/src/plugins/dialog/video.js +983 -981
  82. package/src/plugins/fileBrowser/imageGallery.d.ts +4 -4
  83. package/src/plugins/fileBrowser/imageGallery.js +63 -63
  84. package/src/plugins/index.d.ts +79 -79
  85. package/src/plugins/index.js +32 -32
  86. package/src/plugins/modules/_anchor.js +461 -462
  87. package/src/plugins/modules/_colorPicker.d.ts +59 -59
  88. package/src/plugins/modules/_colorPicker.js +0 -0
  89. package/src/plugins/modules/_notice.d.ts +20 -20
  90. package/src/plugins/modules/_notice.js +72 -72
  91. package/src/plugins/modules/_selectMenu.js +118 -118
  92. package/src/plugins/modules/component.d.ts +24 -24
  93. package/src/plugins/modules/component.js +82 -82
  94. package/src/plugins/modules/dialog.d.ts +27 -27
  95. package/src/plugins/modules/dialog.js +174 -174
  96. package/src/plugins/modules/fileBrowser.d.ts +41 -41
  97. package/src/plugins/modules/fileBrowser.js +373 -373
  98. package/src/plugins/modules/fileManager.d.ts +66 -66
  99. package/src/plugins/modules/fileManager.js +325 -324
  100. package/src/plugins/modules/index.d.ts +10 -10
  101. package/src/plugins/modules/index.js +8 -8
  102. package/src/plugins/modules/resizing.d.ts +153 -153
  103. package/src/plugins/modules/resizing.js +895 -895
  104. package/src/plugins/submenu/align.d.ts +4 -4
  105. package/src/plugins/submenu/align.js +160 -160
  106. package/src/plugins/submenu/font.d.ts +4 -4
  107. package/src/plugins/submenu/font.js +120 -120
  108. package/src/plugins/submenu/fontColor.d.ts +4 -4
  109. package/src/plugins/submenu/fontColor.js +0 -0
  110. package/src/plugins/submenu/fontSize.d.ts +4 -4
  111. package/src/plugins/submenu/fontSize.js +112 -112
  112. package/src/plugins/submenu/formatBlock.d.ts +4 -4
  113. package/src/plugins/submenu/formatBlock.js +273 -273
  114. package/src/plugins/submenu/hiliteColor.d.ts +4 -4
  115. package/src/plugins/submenu/hiliteColor.js +0 -0
  116. package/src/plugins/submenu/horizontalRule.d.ts +4 -4
  117. package/src/plugins/submenu/horizontalRule.js +98 -98
  118. package/src/plugins/submenu/lineHeight.d.ts +4 -4
  119. package/src/plugins/submenu/lineHeight.js +104 -104
  120. package/src/plugins/submenu/list.d.ts +4 -4
  121. package/src/plugins/submenu/list.js +456 -455
  122. package/src/plugins/submenu/paragraphStyle.d.ts +4 -4
  123. package/src/plugins/submenu/paragraphStyle.js +135 -135
  124. package/src/plugins/submenu/table.d.ts +4 -4
  125. package/src/plugins/submenu/template.d.ts +4 -4
  126. package/src/plugins/submenu/template.js +71 -71
  127. package/src/plugins/submenu/textStyle.d.ts +4 -4
  128. package/src/plugins/submenu/textStyle.js +167 -167
  129. package/src/suneditor.d.ts +9 -9
  130. package/src/suneditor.js +75 -75
  131. package/src/suneditor_build.js +17 -17
@@ -1,462 +1,461 @@
1
- /*
2
- * wysiwyg web editor
3
- *
4
- * suneditor.js
5
- * Copyright 2017 JiHong Lee.
6
- * MIT license.
7
- */
8
- 'use strict';
9
-
10
- import selectMenu from './_selectMenu';
11
-
12
- export default {
13
- name: 'anchor',
14
- add: function (core) {
15
- core.addModule([selectMenu]);
16
-
17
- core.context.anchor = {
18
- caller: {},
19
- forms: this.setDialogForm(core),
20
- host: (core._w.location.origin + core._w.location.pathname).replace(/\/$/, ''),
21
- callerContext: null
22
- };
23
- },
24
-
25
- /** dialog */
26
- setDialogForm: function (core) {
27
- const lang = core.lang;
28
- const relList = core.options.linkRel;
29
- const defaultRel = (core.options.linkRelDefault.default || '').split(' ');
30
- const icons = core.icons;
31
- const forms = core.util.createElement('DIV');
32
-
33
- let html = '<div class="se-dialog-body">' +
34
- '<div class="se-dialog-form">' +
35
- '<label>' + lang.dialogBox.linkBox.url + '</label>' +
36
- '<div class="se-dialog-form-files">' +
37
- '<input class="se-input-form se-input-url" type="text" placeholder="' + (core.options.protocol || '') + '" />' +
38
- '<button type="button" class="se-btn se-dialog-files-edge-button _se_bookmark_button" title="' + lang.dialogBox.linkBox.bookmark + '" aria-label="' + lang.dialogBox.linkBox.bookmark + '">' + icons.bookmark + '</button>' +
39
- core.plugins.selectMenu.setForm() +
40
- '</div>' +
41
- '<div class="se-anchor-preview-form">' +
42
- '<span class="se-svg se-anchor-preview-icon _se_anchor_bookmark_icon">' + icons.bookmark + '</span>' +
43
- '<span class="se-svg se-anchor-preview-icon _se_anchor_download_icon">' + icons.download + '</span>' +
44
- '<pre class="se-link-preview"></pre>' +
45
- '</div>' +
46
- '</div>' +
47
- '<div class="se-dialog-form">' +
48
- '<label>' + lang.dialogBox.linkBox.text + '</label><input class="se-input-form _se_anchor_text" type="text" />' +
49
- '</div>' +
50
- '<div class="se-dialog-form-footer">' +
51
- '<label><input type="checkbox" class="se-dialog-btn-check _se_anchor_check" />&nbsp;' + lang.dialogBox.linkBox.newWindowCheck + '</label>' +
52
- '<label><input type="checkbox" class="se-dialog-btn-check _se_anchor_download" />&nbsp;' + lang.dialogBox.linkBox.downloadLinkCheck + '</label>';
53
- if (relList.length > 0) {
54
- html += '<div class="se-anchor-rel"><button type="button" class="se-btn se-btn-select se-anchor-rel-btn">&lt;rel&gt;</button>' +
55
- '<div class="se-anchor-rel-wrapper"><pre class="se-link-preview se-anchor-rel-preview"></pre></div>' +
56
- '<div class="se-list-layer">' +
57
- '<div class="se-list-inner">' +
58
- '<ul class="se-list-basic se-list-checked">';
59
- for (let i = 0, len = relList.length, rel; i < len; i++) {
60
- rel = relList[i];
61
- html += '<li><button type="button" class="se-btn-list' + (defaultRel.indexOf(rel) > -1 ? ' se-checked' : '') + '" data-command="' + rel + '" title="' + rel + '" aria-label="' + rel + '"><span class="se-svg">' + icons.checked + '</span>' + rel + '</button></li>';
62
- }
63
- html += '</ul></div></div></div>';
64
- }
65
-
66
- html += '</div></div>';
67
-
68
- forms.innerHTML = html;
69
- return forms;
70
- },
71
-
72
- initEvent: function (pluginName, forms) {
73
- const anchorPlugin = this.plugins.anchor;
74
- const context = this.context.anchor.caller[pluginName] = {
75
- modal: forms,
76
- urlInput: null,
77
- linkDefaultRel: this.options.linkRelDefault,
78
- defaultRel: this.options.linkRelDefault.default || '',
79
- currentRel: [],
80
- linkAnchor: null,
81
- linkValue: '',
82
- _change: false,
83
- callerName: pluginName
84
- };
85
-
86
- if (typeof context.linkDefaultRel.default === 'string') context.linkDefaultRel.default = context.linkDefaultRel.default.trim();
87
- if (typeof context.linkDefaultRel.check_new_window === 'string') context.linkDefaultRel.check_new_window = context.linkDefaultRel.check_new_window.trim();
88
- if (typeof context.linkDefaultRel.check_bookmark === 'string') context.linkDefaultRel.check_bookmark = context.linkDefaultRel.check_bookmark.trim();
89
-
90
- context.urlInput = forms.querySelector('.se-input-url');
91
- context.anchorText = forms.querySelector('._se_anchor_text');
92
- context.newWindowCheck = forms.querySelector('._se_anchor_check');
93
- context.downloadCheck = forms.querySelector('._se_anchor_download');
94
- context.download = forms.querySelector('._se_anchor_download_icon');
95
- context.preview = forms.querySelector('.se-link-preview');
96
- context.bookmark = forms.querySelector('._se_anchor_bookmark_icon');
97
- context.bookmarkButton = forms.querySelector('._se_bookmark_button');
98
-
99
- this.plugins.selectMenu.initEvent.call(this, pluginName, forms);
100
- const listContext = this.context.selectMenu.caller[pluginName];
101
-
102
- /** rel */
103
- if (this.options.linkRel.length > 0) {
104
- context.relButton = forms.querySelector('.se-anchor-rel-btn');
105
- context.relList = forms.querySelector('.se-list-layer');
106
- context.relPreview = forms.querySelector('.se-anchor-rel-preview');
107
- context.relButton.addEventListener('click', anchorPlugin.onClick_relButton.bind(this, context));
108
- context.relList.addEventListener('click', anchorPlugin.onClick_relList.bind(this, context));
109
- }
110
-
111
- context.newWindowCheck.addEventListener('change', anchorPlugin.onChange_newWindowCheck.bind(this, context));
112
- context.downloadCheck.addEventListener('change', anchorPlugin.onChange_downloadCheck.bind(this, context));
113
- context.anchorText.addEventListener('input', anchorPlugin.onChangeAnchorText.bind(this, context));
114
- context.urlInput.addEventListener('input', anchorPlugin.onChangeUrlInput.bind(this, context));
115
- context.urlInput.addEventListener('keydown', anchorPlugin.onKeyDownUrlInput.bind(this, listContext));
116
- context.urlInput.addEventListener('focus', anchorPlugin.onFocusUrlInput.bind(this, context, listContext));
117
- context.urlInput.addEventListener('blur', anchorPlugin.onBlurUrlInput.bind(this, listContext));
118
- context.bookmarkButton.addEventListener('click', anchorPlugin.onClick_bookmarkButton.bind(this, context));
119
- },
120
-
121
- on: function (contextAnchor, update) {
122
- const anchorPlugin = this.plugins.anchor;
123
-
124
- if (!update) {
125
- anchorPlugin.init.call(this, contextAnchor);
126
- contextAnchor.anchorText.value = this.getSelection().toString().trim();
127
- contextAnchor.newWindowCheck.checked = this.options.linkTargetNewWindow;
128
- } else if (contextAnchor.linkAnchor) {
129
- this.context.dialog.updateModal = true;
130
- const href = contextAnchor.linkAnchor.getAttribute('href');
131
- contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = anchorPlugin.selfPathBookmark.call(this, href) ? href.substr(href.lastIndexOf('#')) : href;
132
- contextAnchor.anchorText.value = contextAnchor.linkAnchor.textContent || contextAnchor.linkAnchor.getAttribute('alt');
133
- contextAnchor.newWindowCheck.checked = (/_blank/i.test(contextAnchor.linkAnchor.target) ? true : false);
134
- contextAnchor.downloadCheck.checked = contextAnchor.linkAnchor.download;
135
- }
136
-
137
- this.context.anchor.callerContext = contextAnchor;
138
- anchorPlugin.setRel.call(this, contextAnchor, (update && contextAnchor.linkAnchor) ? contextAnchor.linkAnchor.rel : contextAnchor.defaultRel);
139
- anchorPlugin.setLinkPreview.call(this, contextAnchor, contextAnchor.linkValue);
140
- this.plugins.selectMenu.on.call(this, contextAnchor.callerName, this.plugins.anchor.setHeaderBookmark);
141
- },
142
-
143
- selfPathBookmark: function(path) {
144
- const href = this._w.location.href.replace(/\/$/, '');
145
- return path.indexOf('#') === 0 || (path.indexOf(href) === 0 && path.indexOf('#') === (href.indexOf("#") === -1 ? href.length : href.substr(0, href.indexOf("#")).length));
146
- },
147
-
148
- _closeRelMenu: null,
149
- toggleRelList: function (contextAnchor, show) {
150
- if (!show) {
151
- if (this.plugins.anchor._closeRelMenu) this.plugins.anchor._closeRelMenu();
152
- } else {
153
- const target = contextAnchor.relButton;
154
- const relList = contextAnchor.relList;
155
- this.util.addClass(target, 'active');
156
- relList.style.visibility = 'hidden';
157
- relList.style.display = 'block';
158
- if (!this.options.rtl) relList.style.left = (target.offsetLeft + target.offsetWidth + 1) + 'px';
159
- else relList.style.left = (target.offsetLeft - relList.offsetWidth - 1) + 'px';
160
- relList.style.top = (target.offsetTop + (target.offsetHeight / 2) - (relList.offsetHeight / 2)) + 'px';
161
- relList.style.visibility = '';
162
-
163
- this.plugins.anchor._closeRelMenu = function (context, target, e) {
164
- if (e && (context.relButton.contains(e.target) || context.relList.contains(e.target))) return;
165
- this.util.removeClass(target, 'active');
166
- context.relList.style.display = 'none';
167
- this.modalForm.removeEventListener('click', this.plugins.anchor._closeRelMenu);
168
- this.plugins.anchor._closeRelMenu = null;
169
- }.bind(this, contextAnchor, target);
170
-
171
- this.modalForm.addEventListener('click', this.plugins.anchor._closeRelMenu);
172
- }
173
- },
174
-
175
- onClick_relButton: function (contextAnchor, e) {
176
- this.plugins.anchor.toggleRelList.call(this, contextAnchor, !this.util.hasClass(e.target, 'active'));
177
- },
178
-
179
- onClick_relList: function (contextAnchor, e) {
180
- const target = e.target;
181
- const cmd = target.getAttribute('data-command');
182
- if (!cmd) return;
183
-
184
- const current = contextAnchor.currentRel;
185
- const checked = this.util.toggleClass(target, 'se-checked');
186
- const index = current.indexOf(cmd);
187
- if (checked) {
188
- if (index === -1) current.push(cmd);
189
- } else {
190
- if (index > -1) current.splice(index, 1);
191
- }
192
-
193
- contextAnchor.relPreview.title = contextAnchor.relPreview.textContent = current.join(' ');
194
- },
195
-
196
- setRel: function (contextAnchor, relAttr) {
197
- const relListEl = contextAnchor.relList;
198
- const rels = contextAnchor.currentRel = !relAttr ? [] : relAttr.split(' ');
199
- if (!relListEl) return;
200
-
201
- const checkedRel = relListEl.querySelectorAll('button');
202
- for (let i = 0, len = checkedRel.length, cmd; i < len; i++) {
203
- cmd = checkedRel[i].getAttribute('data-command');
204
- if (rels.indexOf(cmd) > -1) {
205
- this.util.addClass(checkedRel[i], 'se-checked');
206
- } else {
207
- this.util.removeClass(checkedRel[i], 'se-checked');
208
- }
209
- }
210
-
211
- contextAnchor.relPreview.title = contextAnchor.relPreview.textContent = rels.join(' ');
212
- },
213
-
214
- createHeaderList: function (contextAnchor, contextList, urlValue) {
215
- const headers = this.util.getListChildren(this.context.element.wysiwyg, function(current) {
216
- return /h[1-6]/i.test(current.nodeName);
217
- });
218
- if (headers.length === 0) return;
219
-
220
- const valueRegExp = new this._w.RegExp('^' + urlValue.replace(/^#/, ''), 'i');
221
- const list = [];
222
- let html = '';
223
- for(let i = 0, len = headers.length, h; i < len; i++) {
224
- h = headers[i];
225
- if (!valueRegExp.test(h.textContent)) continue;
226
- list.push(h);
227
- html += '<li class="se-select-item" data-index="' + i + '">' + h.textContent + '</li>';
228
- }
229
-
230
- if (list.length === 0) {
231
- this.plugins.selectMenu.close.call(this, contextList);
232
- } else {
233
- this.plugins.selectMenu.createList(contextList, list, html);
234
- this.plugins.selectMenu.open.call(this, contextList, this.plugins.anchor._setMenuListPosition.bind(this, contextAnchor));
235
- }
236
- },
237
-
238
- _setMenuListPosition: function (contextAnchor, list) {
239
- list.style.top = (contextAnchor.urlInput.offsetHeight + 1) + 'px';
240
- },
241
-
242
- onKeyDownUrlInput: function (contextList, e) {
243
- const keyCode = e.keyCode;
244
- switch (keyCode) {
245
- case 38: // up
246
- e.preventDefault();
247
- e.stopPropagation();
248
- this.plugins.selectMenu.moveItem.call(this, contextList, -1);
249
- break;
250
- case 40: // down
251
- e.preventDefault();
252
- e.stopPropagation();
253
- this.plugins.selectMenu.moveItem.call(this, contextList, 1);
254
- break;
255
- case 13: // enter
256
- if (contextList.index > -1) {
257
- e.preventDefault();
258
- e.stopPropagation();
259
- this.plugins.anchor.setHeaderBookmark.call(this, this.plugins.selectMenu.getItem(contextList, null));
260
- }
261
- break;
262
- }
263
- },
264
-
265
- setHeaderBookmark: function (header) {
266
- const contextAnchor = this.context.anchor.callerContext;
267
- const id = header.id || 'h_' + this._w.Math.random().toString().replace(/.+\./, '');
268
- header.id = id;
269
- contextAnchor.urlInput.value = '#' + id;
270
-
271
- if (!contextAnchor.anchorText.value.trim() || !contextAnchor._change) {
272
- contextAnchor.anchorText.value = header.textContent;
273
- }
274
-
275
- this.plugins.anchor.setLinkPreview.call(this, contextAnchor, contextAnchor.urlInput.value);
276
- this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
277
- this.context.anchor.callerContext.urlInput.focus();
278
- },
279
-
280
- onChangeAnchorText: function (contextAnchor, e) {
281
- contextAnchor._change = !!e.target.value.trim();
282
- },
283
-
284
- onChangeUrlInput: function (contextAnchor, e) {
285
- const value = e.target.value.trim();
286
- this.plugins.anchor.setLinkPreview.call(this, contextAnchor, value);
287
-
288
- if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, value);
289
- else this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
290
- },
291
-
292
- onFocusUrlInput: function (contextAnchor, contextLink) {
293
- const value = contextAnchor.urlInput.value;
294
- if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, contextLink, value);
295
- },
296
-
297
- onBlurUrlInput: function (contextList) {
298
- this.plugins.selectMenu.close.call(this, contextList);
299
- },
300
-
301
- setLinkPreview: function (context, value) {
302
- const preview = context.preview;
303
- const protocol = this.options.linkProtocol;
304
- const noPrefix = this.options.linkNoPrefix;
305
- const reservedProtocol = /^(mailto\:|tel\:|sms\:|https*\:\/\/|#)/.test(value);
306
- const sameProtocol = !protocol ? false : this._w.RegExp('^' + value.substr(0, protocol.length)).test(protocol);
307
- value = context.linkValue = preview.textContent = !value ? '' : noPrefix ? value : (protocol && !reservedProtocol && !sameProtocol) ? protocol + value : reservedProtocol ? value : /^www\./.test(value) ? 'http://' + value : this.context.anchor.host + (/^\//.test(value) ? '' : '/') + value;
308
-
309
- if (this.plugins.anchor.selfPathBookmark.call(this, value)) {
310
- context.bookmark.style.display = 'block';
311
- this.util.addClass(context.bookmarkButton, 'active');
312
- } else {
313
- context.bookmark.style.display = 'none';
314
- this.util.removeClass(context.bookmarkButton, 'active');
315
- }
316
-
317
- if (!this.plugins.anchor.selfPathBookmark.call(this, value) && context.downloadCheck.checked) {
318
- context.download.style.display = 'block';
319
- } else {
320
- context.download.style.display = 'none';
321
- }
322
- },
323
-
324
- setCtx: function (anchor, contextAnchor) {
325
- if (!anchor) return;
326
- contextAnchor.linkAnchor = anchor;
327
- contextAnchor.linkValue = anchor.href;
328
- contextAnchor.currentRel = anchor.rel.split(" ");
329
- },
330
-
331
- updateAnchor: function (anchor, url, alt, contextAnchor, notText) {
332
- // download
333
- if (!this.plugins.anchor.selfPathBookmark.call(this, url) && contextAnchor.downloadCheck.checked) {
334
- anchor.setAttribute('download', alt || url);
335
- } else {
336
- anchor.removeAttribute('download');
337
- }
338
-
339
- // new window
340
- if (contextAnchor.newWindowCheck.checked) anchor.target = '_blank';
341
- else anchor.removeAttribute('target');
342
-
343
- // rel
344
- const rel = contextAnchor.currentRel.join(' ');
345
- if (!rel) anchor.removeAttribute('rel');
346
- else anchor.rel = rel;
347
-
348
- // est url, alt
349
- anchor.href = url;
350
- anchor.setAttribute('alt', alt);
351
- if (notText) {
352
- if (anchor.children.length === 0) anchor.textContent = '';
353
- } else {
354
- anchor.textContent = alt;
355
- }
356
- },
357
-
358
- createAnchor: function (contextAnchor, notText) {
359
- if (contextAnchor.linkValue.length === 0) return null;
360
-
361
- const url = contextAnchor.linkValue;
362
- const anchor = contextAnchor.anchorText;
363
- const anchorText = anchor.value.length === 0 ? url : anchor.value;
364
-
365
- const oA = contextAnchor.linkAnchor || this.util.createElement('A');
366
- this.plugins.anchor.updateAnchor.call(this, oA, url, anchorText, contextAnchor, notText);
367
-
368
- contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = contextAnchor.anchorText.value = '';
369
-
370
- return oA;
371
- },
372
-
373
- onClick_bookmarkButton: function (contextAnchor) {
374
- let url = contextAnchor.urlInput.value;
375
- if (this.plugins.anchor.selfPathBookmark.call(this, url)) {
376
- url = url.substr(1);
377
- contextAnchor.bookmark.style.display = 'none';
378
- this.util.removeClass(contextAnchor.bookmarkButton, 'active');
379
- this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
380
- } else {
381
- url = '#' + url;
382
- contextAnchor.bookmark.style.display = 'block';
383
- this.util.addClass(contextAnchor.bookmarkButton, 'active');
384
- contextAnchor.downloadCheck.checked = false;
385
- contextAnchor.download.style.display = 'none';
386
- this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, url);
387
- }
388
-
389
- contextAnchor.urlInput.value = url;
390
- this.plugins.anchor.setLinkPreview.call(this, contextAnchor, url);
391
- contextAnchor.urlInput.focus();
392
- },
393
-
394
- onChange_newWindowCheck: function (contextAnchor, e) {
395
- if (typeof contextAnchor.linkDefaultRel.check_new_window !== 'string') return;
396
- if (e.target.checked) {
397
- this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relMerge.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_new_window));
398
- } else {
399
- this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relDelete.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_new_window));
400
- }
401
- },
402
-
403
- onChange_downloadCheck: function (contextAnchor, e) {
404
- if (e.target.checked) {
405
- contextAnchor.download.style.display = 'block';
406
- contextAnchor.bookmark.style.display = 'none';
407
- this.util.removeClass(contextAnchor.bookmarkButton, 'active');
408
- contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = contextAnchor.urlInput.value.replace(/^\#+/, '');
409
- if (typeof contextAnchor.linkDefaultRel.check_bookmark === 'string') {
410
- this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relMerge.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_bookmark));
411
- }
412
- } else {
413
- contextAnchor.download.style.display = 'none';
414
- if (typeof contextAnchor.linkDefaultRel.check_bookmark === 'string') {
415
- this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relDelete.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_bookmark));
416
- }
417
- }
418
- },
419
-
420
- _relMerge: function (contextAnchor, relAttr) {
421
- const current = contextAnchor.currentRel;
422
- if (!relAttr) return current.join(' ');
423
-
424
- if (/^only\:/.test(relAttr)) {
425
- relAttr = relAttr.replace(/^only\:/, '').trim();
426
- contextAnchor.currentRel = relAttr.split(' ');
427
- return relAttr;
428
- }
429
-
430
- const rels = relAttr.split(' ');
431
- for (let i = 0, len = rels.length, index; i < len; i++) {
432
- index = current.indexOf(rels[i]);
433
- if (index === -1) current.push(rels[i]);
434
- }
435
-
436
- return current.join(' ');
437
- },
438
-
439
- _relDelete: function (contextAnchor, relAttr) {
440
- if (!relAttr) return contextAnchor.currentRel.join(' ');
441
- if (/^only\:/.test(relAttr)) relAttr = relAttr.replace(/^only\:/, '').trim();
442
-
443
- const rels = contextAnchor.currentRel.join(' ').replace(this._w.RegExp(relAttr + '\\s*'), '');
444
- contextAnchor.currentRel = rels.split(' ');
445
- return rels;
446
- },
447
-
448
- init: function (contextAnchor) {
449
- contextAnchor.linkAnchor = null;
450
- contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = '';
451
- contextAnchor.anchorText.value = '';
452
- contextAnchor.newWindowCheck.checked = false;
453
- contextAnchor.downloadCheck.checked = false;
454
- contextAnchor._change = false;
455
- this.plugins.anchor.setRel.call(this, contextAnchor, contextAnchor.defaultRel);
456
- if (contextAnchor.relList) {
457
- this.plugins.anchor.toggleRelList.call(this, contextAnchor, false);
458
- }
459
- this.context.anchor.callerContext = null;
460
- this.plugins.selectMenu.init.call(this, this.context.selectMenu.callerContext);
461
- }
462
- };
1
+ /*
2
+ * wysiwyg web editor
3
+ *
4
+ * suneditor.js
5
+ * Copyright 2017 JiHong Lee.
6
+ * MIT license.
7
+ */
8
+ 'use strict';
9
+
10
+ import selectMenu from './_selectMenu';
11
+
12
+ export default {
13
+ name: 'anchor',
14
+ add: function (core) {
15
+ core.addModule([selectMenu]);
16
+
17
+ core.context.anchor = {
18
+ caller: {},
19
+ forms: this.setDialogForm(core),
20
+ host: (core._w.location.origin + core._w.location.pathname).replace(/\/$/, ''),
21
+ callerContext: null
22
+ };
23
+ },
24
+
25
+ /** dialog */
26
+ setDialogForm: function (core) {
27
+ const lang = core.lang;
28
+ const relList = core.options.linkRel;
29
+ const defaultRel = (core.options.linkRelDefault.default || '').split(' ');
30
+ const icons = core.icons;
31
+ const forms = core.util.createElement('DIV');
32
+
33
+ let html = '<div class="se-dialog-body">' +
34
+ '<div class="se-dialog-form">' +
35
+ '<label>' + lang.dialogBox.linkBox.url + '</label>' +
36
+ '<div class="se-dialog-form-files">' +
37
+ '<input class="se-input-form se-input-url" type="text" placeholder="' + (core.options.protocol || '') + '" />' +
38
+ '<button type="button" class="se-btn se-dialog-files-edge-button _se_bookmark_button" title="' + lang.dialogBox.linkBox.bookmark + '" aria-label="' + lang.dialogBox.linkBox.bookmark + '">' + icons.bookmark + '</button>' +
39
+ core.plugins.selectMenu.setForm() +
40
+ '</div>' +
41
+ '<div class="se-anchor-preview-form">' +
42
+ '<span class="se-svg se-anchor-preview-icon _se_anchor_bookmark_icon">' + icons.bookmark + '</span>' +
43
+ '<span class="se-svg se-anchor-preview-icon _se_anchor_download_icon">' + icons.download + '</span>' +
44
+ '<pre class="se-link-preview"></pre>' +
45
+ '</div>' +
46
+ '</div>' +
47
+ '<div class="se-dialog-form">' +
48
+ '<label>' + lang.dialogBox.linkBox.text + '</label><input class="se-input-form _se_anchor_text" type="text" />' +
49
+ '</div>' +
50
+ '<div class="se-dialog-form-footer">' +
51
+ '<label><input type="checkbox" class="se-dialog-btn-check _se_anchor_check" />&nbsp;' + lang.dialogBox.linkBox.newWindowCheck + '</label>' +
52
+ '<label><input type="checkbox" class="se-dialog-btn-check _se_anchor_download" />&nbsp;' + lang.dialogBox.linkBox.downloadLinkCheck + '</label>';
53
+ if (relList.length > 0) {
54
+ html += '<div class="se-anchor-rel"><button type="button" class="se-btn se-btn-select se-anchor-rel-btn">&lt;rel&gt;</button>' +
55
+ '<div class="se-anchor-rel-wrapper"><pre class="se-link-preview se-anchor-rel-preview"></pre></div>' +
56
+ '<div class="se-list-layer">' +
57
+ '<div class="se-list-inner">' +
58
+ '<ul class="se-list-basic se-list-checked">';
59
+ for (let i = 0, len = relList.length, rel; i < len; i++) {
60
+ rel = relList[i];
61
+ html += '<li><button type="button" class="se-btn-list' + (defaultRel.indexOf(rel) > -1 ? ' se-checked' : '') + '" data-command="' + rel + '" title="' + rel + '" aria-label="' + rel + '"><span class="se-svg">' + icons.checked + '</span>' + rel + '</button></li>';
62
+ }
63
+ html += '</ul></div></div></div>';
64
+ }
65
+
66
+ html += '</div></div>';
67
+
68
+ forms.innerHTML = html;
69
+ return forms;
70
+ },
71
+
72
+ initEvent: function (pluginName, forms) {
73
+ const anchorPlugin = this.plugins.anchor;
74
+ const context = this.context.anchor.caller[pluginName] = {
75
+ modal: forms,
76
+ urlInput: null,
77
+ linkDefaultRel: this.options.linkRelDefault,
78
+ defaultRel: this.options.linkRelDefault.default || '',
79
+ currentRel: [],
80
+ linkAnchor: null,
81
+ linkValue: '',
82
+ _change: false,
83
+ callerName: pluginName
84
+ };
85
+
86
+ if (typeof context.linkDefaultRel.default === 'string') context.linkDefaultRel.default = context.linkDefaultRel.default.trim();
87
+ if (typeof context.linkDefaultRel.check_new_window === 'string') context.linkDefaultRel.check_new_window = context.linkDefaultRel.check_new_window.trim();
88
+ if (typeof context.linkDefaultRel.check_bookmark === 'string') context.linkDefaultRel.check_bookmark = context.linkDefaultRel.check_bookmark.trim();
89
+
90
+ context.urlInput = forms.querySelector('.se-input-url');
91
+ context.anchorText = forms.querySelector('._se_anchor_text');
92
+ context.newWindowCheck = forms.querySelector('._se_anchor_check');
93
+ context.downloadCheck = forms.querySelector('._se_anchor_download');
94
+ context.download = forms.querySelector('._se_anchor_download_icon');
95
+ context.preview = forms.querySelector('.se-link-preview');
96
+ context.bookmark = forms.querySelector('._se_anchor_bookmark_icon');
97
+ context.bookmarkButton = forms.querySelector('._se_bookmark_button');
98
+
99
+ this.plugins.selectMenu.initEvent.call(this, pluginName, forms);
100
+ const listContext = this.context.selectMenu.caller[pluginName];
101
+
102
+ /** rel */
103
+ if (this.options.linkRel.length > 0) {
104
+ context.relButton = forms.querySelector('.se-anchor-rel-btn');
105
+ context.relList = forms.querySelector('.se-list-layer');
106
+ context.relPreview = forms.querySelector('.se-anchor-rel-preview');
107
+ context.relButton.addEventListener('click', anchorPlugin.onClick_relButton.bind(this, context));
108
+ context.relList.addEventListener('click', anchorPlugin.onClick_relList.bind(this, context));
109
+ }
110
+
111
+ context.newWindowCheck.addEventListener('change', anchorPlugin.onChange_newWindowCheck.bind(this, context));
112
+ context.downloadCheck.addEventListener('change', anchorPlugin.onChange_downloadCheck.bind(this, context));
113
+ context.anchorText.addEventListener('input', anchorPlugin.onChangeAnchorText.bind(this, context));
114
+ context.urlInput.addEventListener('input', anchorPlugin.onChangeUrlInput.bind(this, context));
115
+ context.urlInput.addEventListener('keydown', anchorPlugin.onKeyDownUrlInput.bind(this, listContext));
116
+ context.urlInput.addEventListener('focus', anchorPlugin.onFocusUrlInput.bind(this, context, listContext));
117
+ context.urlInput.addEventListener('blur', anchorPlugin.onBlurUrlInput.bind(this, listContext));
118
+ context.bookmarkButton.addEventListener('click', anchorPlugin.onClick_bookmarkButton.bind(this, context));
119
+ },
120
+
121
+ on: function (contextAnchor, update) {
122
+ const anchorPlugin = this.plugins.anchor;
123
+
124
+ if (!update) {
125
+ anchorPlugin.init.call(this, contextAnchor);
126
+ contextAnchor.anchorText.value = this.getSelection().toString().trim();
127
+ contextAnchor.newWindowCheck.checked = this.options.linkTargetNewWindow;
128
+ } else if (contextAnchor.linkAnchor) {
129
+ this.context.dialog.updateModal = true;
130
+ const href = contextAnchor.linkAnchor.getAttribute('href');
131
+ contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = anchorPlugin.selfPathBookmark.call(this, href) ? href.substr(href.lastIndexOf('#')) : href;
132
+ contextAnchor.anchorText.value = contextAnchor.linkAnchor.textContent;
133
+ contextAnchor.newWindowCheck.checked = (/_blank/i.test(contextAnchor.linkAnchor.target) ? true : false);
134
+ contextAnchor.downloadCheck.checked = contextAnchor.linkAnchor.download;
135
+ }
136
+
137
+ this.context.anchor.callerContext = contextAnchor;
138
+ anchorPlugin.setRel.call(this, contextAnchor, (update && contextAnchor.linkAnchor) ? contextAnchor.linkAnchor.rel : contextAnchor.defaultRel);
139
+ anchorPlugin.setLinkPreview.call(this, contextAnchor, contextAnchor.linkValue);
140
+ this.plugins.selectMenu.on.call(this, contextAnchor.callerName, this.plugins.anchor.setHeaderBookmark);
141
+ },
142
+
143
+ selfPathBookmark: function(path) {
144
+ const href = this._w.location.href.replace(/\/$/, '');
145
+ return path.indexOf('#') === 0 || (path.indexOf(href) === 0 && path.indexOf('#') === (href.indexOf("#") === -1 ? href.length : href.substr(0, href.indexOf("#")).length));
146
+ },
147
+
148
+ _closeRelMenu: null,
149
+ toggleRelList: function (contextAnchor, show) {
150
+ if (!show) {
151
+ if (this.plugins.anchor._closeRelMenu) this.plugins.anchor._closeRelMenu();
152
+ } else {
153
+ const target = contextAnchor.relButton;
154
+ const relList = contextAnchor.relList;
155
+ this.util.addClass(target, 'active');
156
+ relList.style.visibility = 'hidden';
157
+ relList.style.display = 'block';
158
+ if (!this.options.rtl) relList.style.left = (target.offsetLeft + target.offsetWidth + 1) + 'px';
159
+ else relList.style.left = (target.offsetLeft - relList.offsetWidth - 1) + 'px';
160
+ relList.style.top = (target.offsetTop + (target.offsetHeight / 2) - (relList.offsetHeight / 2)) + 'px';
161
+ relList.style.visibility = '';
162
+
163
+ this.plugins.anchor._closeRelMenu = function (context, target, e) {
164
+ if (e && (context.relButton.contains(e.target) || context.relList.contains(e.target))) return;
165
+ this.util.removeClass(target, 'active');
166
+ context.relList.style.display = 'none';
167
+ this.modalForm.removeEventListener('click', this.plugins.anchor._closeRelMenu);
168
+ this.plugins.anchor._closeRelMenu = null;
169
+ }.bind(this, contextAnchor, target);
170
+
171
+ this.modalForm.addEventListener('click', this.plugins.anchor._closeRelMenu);
172
+ }
173
+ },
174
+
175
+ onClick_relButton: function (contextAnchor, e) {
176
+ this.plugins.anchor.toggleRelList.call(this, contextAnchor, !this.util.hasClass(e.target, 'active'));
177
+ },
178
+
179
+ onClick_relList: function (contextAnchor, e) {
180
+ const target = e.target;
181
+ const cmd = target.getAttribute('data-command');
182
+ if (!cmd) return;
183
+
184
+ const current = contextAnchor.currentRel;
185
+ const checked = this.util.toggleClass(target, 'se-checked');
186
+ const index = current.indexOf(cmd);
187
+ if (checked) {
188
+ if (index === -1) current.push(cmd);
189
+ } else {
190
+ if (index > -1) current.splice(index, 1);
191
+ }
192
+
193
+ contextAnchor.relPreview.title = contextAnchor.relPreview.textContent = current.join(' ');
194
+ },
195
+
196
+ setRel: function (contextAnchor, relAttr) {
197
+ const relListEl = contextAnchor.relList;
198
+ const rels = contextAnchor.currentRel = !relAttr ? [] : relAttr.split(' ');
199
+ if (!relListEl) return;
200
+
201
+ const checkedRel = relListEl.querySelectorAll('button');
202
+ for (let i = 0, len = checkedRel.length, cmd; i < len; i++) {
203
+ cmd = checkedRel[i].getAttribute('data-command');
204
+ if (rels.indexOf(cmd) > -1) {
205
+ this.util.addClass(checkedRel[i], 'se-checked');
206
+ } else {
207
+ this.util.removeClass(checkedRel[i], 'se-checked');
208
+ }
209
+ }
210
+
211
+ contextAnchor.relPreview.title = contextAnchor.relPreview.textContent = rels.join(' ');
212
+ },
213
+
214
+ createHeaderList: function (contextAnchor, contextList, urlValue) {
215
+ const headers = this.util.getListChildren(this.context.element.wysiwyg, function(current) {
216
+ return /h[1-6]/i.test(current.nodeName);
217
+ });
218
+ if (headers.length === 0) return;
219
+
220
+ const valueRegExp = new this._w.RegExp('^' + urlValue.replace(/^#/, ''), 'i');
221
+ const list = [];
222
+ let html = '';
223
+ for(let i = 0, len = headers.length, h; i < len; i++) {
224
+ h = headers[i];
225
+ if (!valueRegExp.test(h.textContent)) continue;
226
+ list.push(h);
227
+ html += '<li class="se-select-item" data-index="' + i + '">' + h.textContent + '</li>';
228
+ }
229
+
230
+ if (list.length === 0) {
231
+ this.plugins.selectMenu.close.call(this, contextList);
232
+ } else {
233
+ this.plugins.selectMenu.createList(contextList, list, html);
234
+ this.plugins.selectMenu.open.call(this, contextList, this.plugins.anchor._setMenuListPosition.bind(this, contextAnchor));
235
+ }
236
+ },
237
+
238
+ _setMenuListPosition: function (contextAnchor, list) {
239
+ list.style.top = (contextAnchor.urlInput.offsetHeight + 1) + 'px';
240
+ },
241
+
242
+ onKeyDownUrlInput: function (contextList, e) {
243
+ const keyCode = e.keyCode;
244
+ switch (keyCode) {
245
+ case 38: // up
246
+ e.preventDefault();
247
+ e.stopPropagation();
248
+ this.plugins.selectMenu.moveItem.call(this, contextList, -1);
249
+ break;
250
+ case 40: // down
251
+ e.preventDefault();
252
+ e.stopPropagation();
253
+ this.plugins.selectMenu.moveItem.call(this, contextList, 1);
254
+ break;
255
+ case 13: // enter
256
+ if (contextList.index > -1) {
257
+ e.preventDefault();
258
+ e.stopPropagation();
259
+ this.plugins.anchor.setHeaderBookmark.call(this, this.plugins.selectMenu.getItem(contextList, null));
260
+ }
261
+ break;
262
+ }
263
+ },
264
+
265
+ setHeaderBookmark: function (header) {
266
+ const contextAnchor = this.context.anchor.callerContext;
267
+ const id = header.id || 'h_' + this._w.Math.random().toString().replace(/.+\./, '');
268
+ header.id = id;
269
+ contextAnchor.urlInput.value = '#' + id;
270
+
271
+ if (!contextAnchor.anchorText.value.trim() || !contextAnchor._change) {
272
+ contextAnchor.anchorText.value = header.textContent;
273
+ }
274
+
275
+ this.plugins.anchor.setLinkPreview.call(this, contextAnchor, contextAnchor.urlInput.value);
276
+ this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
277
+ this.context.anchor.callerContext.urlInput.focus();
278
+ },
279
+
280
+ onChangeAnchorText: function (contextAnchor, e) {
281
+ contextAnchor._change = !!e.target.value.trim();
282
+ },
283
+
284
+ onChangeUrlInput: function (contextAnchor, e) {
285
+ const value = e.target.value.trim();
286
+ this.plugins.anchor.setLinkPreview.call(this, contextAnchor, value);
287
+
288
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, value);
289
+ else this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
290
+ },
291
+
292
+ onFocusUrlInput: function (contextAnchor, contextLink) {
293
+ const value = contextAnchor.urlInput.value;
294
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) this.plugins.anchor.createHeaderList.call(this, contextAnchor, contextLink, value);
295
+ },
296
+
297
+ onBlurUrlInput: function (contextList) {
298
+ this.plugins.selectMenu.close.call(this, contextList);
299
+ },
300
+
301
+ setLinkPreview: function (context, value) {
302
+ const preview = context.preview;
303
+ const protocol = this.options.linkProtocol;
304
+ const noPrefix = this.options.linkNoPrefix;
305
+ const reservedProtocol = /^(mailto\:|tel\:|sms\:|https*\:\/\/|#)/.test(value);
306
+ const sameProtocol = !protocol ? false : this._w.RegExp('^' + value.substr(0, protocol.length)).test(protocol);
307
+ value = context.linkValue = preview.textContent = !value ? '' : noPrefix ? value : (protocol && !reservedProtocol && !sameProtocol) ? protocol + value : reservedProtocol ? value : /^www\./.test(value) ? 'http://' + value : this.context.anchor.host + (/^\//.test(value) ? '' : '/') + value;
308
+
309
+ if (this.plugins.anchor.selfPathBookmark.call(this, value)) {
310
+ context.bookmark.style.display = 'block';
311
+ this.util.addClass(context.bookmarkButton, 'active');
312
+ } else {
313
+ context.bookmark.style.display = 'none';
314
+ this.util.removeClass(context.bookmarkButton, 'active');
315
+ }
316
+
317
+ if (!this.plugins.anchor.selfPathBookmark.call(this, value) && context.downloadCheck.checked) {
318
+ context.download.style.display = 'block';
319
+ } else {
320
+ context.download.style.display = 'none';
321
+ }
322
+ },
323
+
324
+ setCtx: function (anchor, contextAnchor) {
325
+ if (!anchor) return;
326
+ contextAnchor.linkAnchor = anchor;
327
+ contextAnchor.linkValue = anchor.href;
328
+ contextAnchor.currentRel = anchor.rel.split(" ");
329
+ },
330
+
331
+ updateAnchor: function (anchor, url, displayText, contextAnchor, notText) {
332
+ // download
333
+ if (!this.plugins.anchor.selfPathBookmark.call(this, url) && contextAnchor.downloadCheck.checked) {
334
+ anchor.setAttribute('download', displayText || url);
335
+ } else {
336
+ anchor.removeAttribute('download');
337
+ }
338
+
339
+ // new window
340
+ if (contextAnchor.newWindowCheck.checked) anchor.target = '_blank';
341
+ else anchor.removeAttribute('target');
342
+
343
+ // rel
344
+ const rel = contextAnchor.currentRel.join(' ');
345
+ if (!rel) anchor.removeAttribute('rel');
346
+ else anchor.rel = rel;
347
+
348
+ // set url
349
+ anchor.href = url;
350
+ if (notText) {
351
+ if (anchor.children.length === 0) anchor.textContent = '';
352
+ } else {
353
+ anchor.textContent = displayText;
354
+ }
355
+ },
356
+
357
+ createAnchor: function (contextAnchor, notText) {
358
+ if (contextAnchor.linkValue.length === 0) return null;
359
+
360
+ const url = contextAnchor.linkValue;
361
+ const anchor = contextAnchor.anchorText;
362
+ const displayText = anchor.value.length === 0 ? url : anchor.value;
363
+
364
+ const oA = contextAnchor.linkAnchor || this.util.createElement('A');
365
+ this.plugins.anchor.updateAnchor.call(this, oA, url, displayText, contextAnchor, notText);
366
+
367
+ contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = contextAnchor.anchorText.value = '';
368
+
369
+ return oA;
370
+ },
371
+
372
+ onClick_bookmarkButton: function (contextAnchor) {
373
+ let url = contextAnchor.urlInput.value;
374
+ if (this.plugins.anchor.selfPathBookmark.call(this, url)) {
375
+ url = url.substr(1);
376
+ contextAnchor.bookmark.style.display = 'none';
377
+ this.util.removeClass(contextAnchor.bookmarkButton, 'active');
378
+ this.plugins.selectMenu.close.call(this, this.context.selectMenu.callerContext);
379
+ } else {
380
+ url = '#' + url;
381
+ contextAnchor.bookmark.style.display = 'block';
382
+ this.util.addClass(contextAnchor.bookmarkButton, 'active');
383
+ contextAnchor.downloadCheck.checked = false;
384
+ contextAnchor.download.style.display = 'none';
385
+ this.plugins.anchor.createHeaderList.call(this, contextAnchor, this.context.selectMenu.callerContext, url);
386
+ }
387
+
388
+ contextAnchor.urlInput.value = url;
389
+ this.plugins.anchor.setLinkPreview.call(this, contextAnchor, url);
390
+ contextAnchor.urlInput.focus();
391
+ },
392
+
393
+ onChange_newWindowCheck: function (contextAnchor, e) {
394
+ if (typeof contextAnchor.linkDefaultRel.check_new_window !== 'string') return;
395
+ if (e.target.checked) {
396
+ this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relMerge.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_new_window));
397
+ } else {
398
+ this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relDelete.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_new_window));
399
+ }
400
+ },
401
+
402
+ onChange_downloadCheck: function (contextAnchor, e) {
403
+ if (e.target.checked) {
404
+ contextAnchor.download.style.display = 'block';
405
+ contextAnchor.bookmark.style.display = 'none';
406
+ this.util.removeClass(contextAnchor.bookmarkButton, 'active');
407
+ contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = contextAnchor.urlInput.value.replace(/^\#+/, '');
408
+ if (typeof contextAnchor.linkDefaultRel.check_bookmark === 'string') {
409
+ this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relMerge.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_bookmark));
410
+ }
411
+ } else {
412
+ contextAnchor.download.style.display = 'none';
413
+ if (typeof contextAnchor.linkDefaultRel.check_bookmark === 'string') {
414
+ this.plugins.anchor.setRel.call(this, contextAnchor, this.plugins.anchor._relDelete.call(this, contextAnchor, contextAnchor.linkDefaultRel.check_bookmark));
415
+ }
416
+ }
417
+ },
418
+
419
+ _relMerge: function (contextAnchor, relAttr) {
420
+ const current = contextAnchor.currentRel;
421
+ if (!relAttr) return current.join(' ');
422
+
423
+ if (/^only\:/.test(relAttr)) {
424
+ relAttr = relAttr.replace(/^only\:/, '').trim();
425
+ contextAnchor.currentRel = relAttr.split(' ');
426
+ return relAttr;
427
+ }
428
+
429
+ const rels = relAttr.split(' ');
430
+ for (let i = 0, len = rels.length, index; i < len; i++) {
431
+ index = current.indexOf(rels[i]);
432
+ if (index === -1) current.push(rels[i]);
433
+ }
434
+
435
+ return current.join(' ');
436
+ },
437
+
438
+ _relDelete: function (contextAnchor, relAttr) {
439
+ if (!relAttr) return contextAnchor.currentRel.join(' ');
440
+ if (/^only\:/.test(relAttr)) relAttr = relAttr.replace(/^only\:/, '').trim();
441
+
442
+ const rels = contextAnchor.currentRel.join(' ').replace(this._w.RegExp(relAttr + '\\s*'), '');
443
+ contextAnchor.currentRel = rels.split(' ');
444
+ return rels;
445
+ },
446
+
447
+ init: function (contextAnchor) {
448
+ contextAnchor.linkAnchor = null;
449
+ contextAnchor.linkValue = contextAnchor.preview.textContent = contextAnchor.urlInput.value = '';
450
+ contextAnchor.anchorText.value = '';
451
+ contextAnchor.newWindowCheck.checked = false;
452
+ contextAnchor.downloadCheck.checked = false;
453
+ contextAnchor._change = false;
454
+ this.plugins.anchor.setRel.call(this, contextAnchor, contextAnchor.defaultRel);
455
+ if (contextAnchor.relList) {
456
+ this.plugins.anchor.toggleRelList.call(this, contextAnchor, false);
457
+ }
458
+ this.context.anchor.callerContext = null;
459
+ this.plugins.selectMenu.init.call(this, this.context.selectMenu.callerContext);
460
+ }
461
+ };