suneditor 3.0.0-beta.2 → 3.0.0-beta.20
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.
- package/CONTRIBUTING.md +186 -184
- package/LICENSE +21 -21
- package/README.md +157 -180
- package/dist/suneditor.min.css +1 -1
- package/dist/suneditor.min.js +1 -1
- package/package.json +126 -123
- package/src/assets/design/color.css +131 -121
- package/src/assets/design/index.css +3 -3
- package/src/assets/design/size.css +37 -35
- package/src/assets/design/typography.css +37 -37
- package/src/assets/icons/defaultIcons.js +247 -232
- package/src/assets/suneditor-contents.css +779 -778
- package/src/assets/suneditor.css +43 -35
- package/src/core/base/eventHandlers/handler_toolbar.js +135 -135
- package/src/core/base/eventHandlers/handler_ww_clipboard.js +56 -56
- package/src/core/base/eventHandlers/handler_ww_dragDrop.js +115 -113
- package/src/core/base/eventHandlers/handler_ww_key_input.js +1200 -1200
- package/src/core/base/eventHandlers/handler_ww_mouse.js +194 -194
- package/src/core/base/eventManager.js +1550 -1484
- package/src/core/base/history.js +355 -355
- package/src/core/class/char.js +163 -162
- package/src/core/class/component.js +856 -842
- package/src/core/class/format.js +3433 -3422
- package/src/core/class/html.js +1927 -1890
- package/src/core/class/menu.js +357 -346
- package/src/core/class/nodeTransform.js +424 -424
- package/src/core/class/offset.js +858 -891
- package/src/core/class/selection.js +710 -620
- package/src/core/class/shortcuts.js +98 -98
- package/src/core/class/toolbar.js +438 -430
- package/src/core/class/ui.js +424 -422
- package/src/core/class/viewer.js +750 -750
- package/src/core/editor.js +1810 -1708
- package/src/core/section/actives.js +268 -241
- package/src/core/section/constructor.js +1348 -1661
- package/src/core/section/context.js +102 -102
- package/src/core/section/documentType.js +582 -561
- package/src/core/section/options.js +367 -0
- package/src/core/util/instanceCheck.js +59 -0
- package/src/editorInjector/_classes.js +36 -36
- package/src/editorInjector/_core.js +92 -92
- package/src/editorInjector/index.js +75 -75
- package/src/events.js +634 -622
- package/src/helper/clipboard.js +59 -59
- package/src/helper/converter.js +586 -564
- package/src/helper/dom/domCheck.js +304 -304
- package/src/helper/dom/domQuery.js +677 -669
- package/src/helper/dom/domUtils.js +618 -557
- package/src/helper/dom/index.js +12 -12
- package/src/helper/env.js +249 -240
- package/src/helper/index.js +25 -25
- package/src/helper/keyCodeMap.js +183 -183
- package/src/helper/numbers.js +72 -72
- package/src/helper/unicode.js +47 -47
- package/src/langs/ckb.js +231 -231
- package/src/langs/cs.js +231 -231
- package/src/langs/da.js +231 -231
- package/src/langs/de.js +231 -231
- package/src/langs/en.js +230 -230
- package/src/langs/es.js +231 -231
- package/src/langs/fa.js +231 -231
- package/src/langs/fr.js +231 -231
- package/src/langs/he.js +231 -231
- package/src/langs/hu.js +230 -230
- package/src/langs/index.js +28 -28
- package/src/langs/it.js +231 -231
- package/src/langs/ja.js +230 -230
- package/src/langs/km.js +230 -230
- package/src/langs/ko.js +230 -230
- package/src/langs/lv.js +231 -231
- package/src/langs/nl.js +231 -231
- package/src/langs/pl.js +231 -231
- package/src/langs/pt_br.js +231 -231
- package/src/langs/ro.js +231 -231
- package/src/langs/ru.js +231 -231
- package/src/langs/se.js +231 -231
- package/src/langs/tr.js +231 -231
- package/src/langs/uk.js +231 -231
- package/src/langs/ur.js +231 -231
- package/src/langs/zh_cn.js +231 -231
- package/src/modules/ApiManager.js +191 -191
- package/src/modules/Browser.js +669 -667
- package/src/modules/ColorPicker.js +364 -362
- package/src/modules/Controller.js +474 -454
- package/src/modules/Figure.js +1620 -1617
- package/src/modules/FileManager.js +359 -359
- package/src/modules/HueSlider.js +577 -565
- package/src/modules/Modal.js +346 -346
- package/src/modules/ModalAnchorEditor.js +643 -643
- package/src/modules/SelectMenu.js +549 -549
- package/src/modules/_DragHandle.js +17 -17
- package/src/modules/index.js +14 -14
- package/src/plugins/browser/audioGallery.js +83 -83
- package/src/plugins/browser/fileBrowser.js +103 -103
- package/src/plugins/browser/fileGallery.js +83 -83
- package/src/plugins/browser/imageGallery.js +81 -81
- package/src/plugins/browser/videoGallery.js +103 -103
- package/src/plugins/command/blockquote.js +61 -60
- package/src/plugins/command/exportPDF.js +134 -134
- package/src/plugins/command/fileUpload.js +456 -456
- package/src/plugins/command/list_bulleted.js +149 -148
- package/src/plugins/command/list_numbered.js +152 -151
- package/src/plugins/dropdown/align.js +157 -155
- package/src/plugins/dropdown/backgroundColor.js +108 -104
- package/src/plugins/dropdown/font.js +141 -137
- package/src/plugins/dropdown/fontColor.js +109 -105
- package/src/plugins/dropdown/formatBlock.js +170 -178
- package/src/plugins/dropdown/hr.js +152 -152
- package/src/plugins/dropdown/layout.js +83 -83
- package/src/plugins/dropdown/lineHeight.js +131 -130
- package/src/plugins/dropdown/list.js +123 -122
- package/src/plugins/dropdown/paragraphStyle.js +138 -138
- package/src/plugins/dropdown/table.js +4110 -4000
- package/src/plugins/dropdown/template.js +83 -83
- package/src/plugins/dropdown/textStyle.js +149 -149
- package/src/plugins/field/mention.js +242 -242
- package/src/plugins/index.js +120 -120
- package/src/plugins/input/fontSize.js +414 -410
- package/src/plugins/input/pageNavigator.js +71 -70
- package/src/plugins/modal/audio.js +677 -677
- package/src/plugins/modal/drawing.js +537 -531
- package/src/plugins/modal/embed.js +886 -886
- package/src/plugins/modal/image.js +1377 -1376
- package/src/plugins/modal/link.js +248 -240
- package/src/plugins/modal/math.js +563 -563
- package/src/plugins/modal/video.js +1226 -1226
- package/src/plugins/popup/anchor.js +224 -222
- package/src/suneditor.js +114 -107
- package/src/themes/dark.css +132 -122
- package/src/typedef.js +132 -130
- package/types/assets/icons/defaultIcons.d.ts +8 -0
- package/types/core/base/eventManager.d.ts +29 -4
- package/types/core/class/char.d.ts +2 -1
- package/types/core/class/component.d.ts +1 -2
- package/types/core/class/format.d.ts +8 -1
- package/types/core/class/html.d.ts +8 -0
- package/types/core/class/menu.d.ts +8 -0
- package/types/core/class/offset.d.ts +24 -26
- package/types/core/class/selection.d.ts +2 -0
- package/types/core/class/toolbar.d.ts +6 -0
- package/types/core/class/ui.d.ts +1 -1
- package/types/core/editor.d.ts +34 -12
- package/types/core/section/constructor.d.ts +5 -638
- package/types/core/section/documentType.d.ts +12 -2
- package/types/core/section/options.d.ts +740 -0
- package/types/core/util/instanceCheck.d.ts +50 -0
- package/types/editorInjector/_core.d.ts +5 -5
- package/types/editorInjector/index.d.ts +2 -2
- package/types/events.d.ts +2 -0
- package/types/helper/converter.d.ts +9 -0
- package/types/helper/dom/domQuery.d.ts +5 -5
- package/types/helper/dom/domUtils.d.ts +8 -0
- package/types/helper/env.d.ts +6 -1
- package/types/helper/index.d.ts +4 -1
- package/types/index.d.ts +122 -120
- package/types/langs/_Lang.d.ts +194 -194
- package/types/modules/ColorPicker.d.ts +5 -1
- package/types/modules/Controller.d.ts +8 -4
- package/types/modules/Figure.d.ts +2 -1
- package/types/modules/HueSlider.d.ts +4 -1
- package/types/modules/SelectMenu.d.ts +1 -1
- package/types/plugins/command/blockquote.d.ts +1 -0
- package/types/plugins/command/list_bulleted.d.ts +1 -0
- package/types/plugins/command/list_numbered.d.ts +1 -0
- package/types/plugins/dropdown/align.d.ts +1 -0
- package/types/plugins/dropdown/backgroundColor.d.ts +1 -0
- package/types/plugins/dropdown/font.d.ts +1 -0
- package/types/plugins/dropdown/fontColor.d.ts +1 -0
- package/types/plugins/dropdown/formatBlock.d.ts +3 -2
- package/types/plugins/dropdown/lineHeight.d.ts +1 -0
- package/types/plugins/dropdown/list.d.ts +1 -0
- package/types/plugins/dropdown/table.d.ts +6 -0
- package/types/plugins/input/fontSize.d.ts +1 -0
- package/types/plugins/modal/drawing.d.ts +4 -0
- package/types/plugins/modal/link.d.ts +32 -15
- package/types/suneditor.d.ts +13 -9
- package/types/typedef.d.ts +8 -0
|
@@ -1,842 +1,856 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Component class
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
-
import { dom, env, numbers, unicode, keyCodeMap, converter } from '../../helper';
|
|
7
|
-
import { Figure, _DragHandle } from '../../modules';
|
|
8
|
-
|
|
9
|
-
const { _w, ON_OVER_COMPONENT, isMobile } = env;
|
|
10
|
-
const DIR_KEYCODE = /^(
|
|
11
|
-
const DIR_UP_KEYCODE = /^
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {Omit<Component & Partial<__se__EditorInjector>, 'component'>} ComponentThis
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @constructor
|
|
19
|
-
* @this {ComponentThis}
|
|
20
|
-
* @description Class for managing components such as images and tables that are not in line format
|
|
21
|
-
* @param {__se__EditorCore} editor - The root editor instance
|
|
22
|
-
*/
|
|
23
|
-
function Component(editor) {
|
|
24
|
-
CoreInjector.call(this, editor);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* @description The current component information, used copy, cut, and keydown events
|
|
28
|
-
* @type {__se__ComponentInfo}
|
|
29
|
-
*/
|
|
30
|
-
this.info = null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @description Component is selected
|
|
34
|
-
* @type {boolean}
|
|
35
|
-
*/
|
|
36
|
-
this.isSelected = false;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @description Currently selected component target
|
|
40
|
-
* @type {Node|null}
|
|
41
|
-
*/
|
|
42
|
-
this.currentTarget = null;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @description Currently selected component plugin instance
|
|
46
|
-
* @type {*}
|
|
47
|
-
*/
|
|
48
|
-
this.currentPlugin = null;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* @description Currently selected component plugin name
|
|
52
|
-
* @type {*}
|
|
53
|
-
*/
|
|
54
|
-
this.currentPluginName = '';
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @description Currently selected component information
|
|
58
|
-
* @type {__se__ComponentInfo|null}
|
|
59
|
-
*/
|
|
60
|
-
this.currentInfo = null;
|
|
61
|
-
|
|
62
|
-
/** @type {Object<string, (...args: *) => *>} */
|
|
63
|
-
this.__globalEvents = {
|
|
64
|
-
copy: OnCopy_component.bind(this),
|
|
65
|
-
cut: OnCut_component.bind(this),
|
|
66
|
-
keydown: OnKeyDown_component.bind(this),
|
|
67
|
-
mousedown: CloseListener_mousedown.bind(this)
|
|
68
|
-
};
|
|
69
|
-
/** @type {__se__GlobalEventInfo|void} */
|
|
70
|
-
this._bindClose_copy = null;
|
|
71
|
-
/** @type {__se__GlobalEventInfo|void} */
|
|
72
|
-
this._bindClose_cut = null;
|
|
73
|
-
/** @type {__se__GlobalEventInfo|void} */
|
|
74
|
-
this._bindClose_keydown = null;
|
|
75
|
-
/** @type {__se__GlobalEventInfo|void} */
|
|
76
|
-
this._bindClose_mousedown = null;
|
|
77
|
-
/** @type {
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
82
|
-
this.editor.applyFrameRoots((e) => {
|
|
83
|
-
// drag
|
|
84
|
-
const dragHandle = dom.utils.createElement('DIV', { class: 'se-drag-handle', draggable: 'true' }, this.icons.selection);
|
|
85
|
-
e.get('wrapper').appendChild(dragHandle);
|
|
86
|
-
this.eventManager.addEvent(dragHandle, 'mouseenter', OnDragEnter.bind(this));
|
|
87
|
-
this.eventManager.addEvent(dragHandle, 'mouseleave', OnDragLeave.bind(this));
|
|
88
|
-
this.eventManager.addEvent(dragHandle, 'dragstart', OnDragStart.bind(this));
|
|
89
|
-
this.eventManager.addEvent(dragHandle, 'dragend', OnDragEnd.bind(this));
|
|
90
|
-
this.eventManager.addEvent(dragHandle, 'click', OnDragClick.bind(this));
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
Component.prototype = {
|
|
95
|
-
/**
|
|
96
|
-
* @this {ComponentThis}
|
|
97
|
-
* @description Inserts an element and returns it. (Used for elements: table, hr, image, video)
|
|
98
|
-
* - If "element" is "HR", inserts and returns the new line.
|
|
99
|
-
* @param {Node} element Element to be inserted
|
|
100
|
-
* @param {Object} [options] Options
|
|
101
|
-
* @param {boolean} [options.skipCharCount=false] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
|
|
102
|
-
* @param {boolean} [options.skipSelection=false] If true, do not automatically select the inserted component.
|
|
103
|
-
* @param {boolean} [options.skipHistory=false] If true, do not push to history.
|
|
104
|
-
* @returns {HTMLElement} The inserted element or new line (for HR)
|
|
105
|
-
*/
|
|
106
|
-
insert(element, { skipCharCount, skipSelection, skipHistory } = {}) {
|
|
107
|
-
if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(element))) {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const r = this.html.remove();
|
|
112
|
-
const isInline = this.isInline(element);
|
|
113
|
-
this.selection.getRangeAndAddLine(this.selection.getRange(), r.container);
|
|
114
|
-
const selectionNode = this.selection.getNode();
|
|
115
|
-
let oNode = null;
|
|
116
|
-
let formatEl = this.format.getLine(selectionNode, null);
|
|
117
|
-
|
|
118
|
-
if (dom.check.isListCell(formatEl)) {
|
|
119
|
-
this.html.insertNode(element, { afterNode: isInline ? null : selectionNode === formatEl ? null : r.container.nextSibling, skipCharCount: true });
|
|
120
|
-
if (!isInline && !element.nextSibling) element.parentNode.appendChild(dom.utils.createElement('BR'));
|
|
121
|
-
} else {
|
|
122
|
-
if (!isInline && this.selection.getRange().collapsed && (r.container.nodeType === 3 || dom.check.isBreak(r.container))) {
|
|
123
|
-
const depthFormat = dom.query.getParentElement(r.container, this.format.isBlock.bind(this.format));
|
|
124
|
-
oNode = this.nodeTransform.split(r.container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
|
|
125
|
-
if (oNode) formatEl = /** @type {HTMLElement} */ (oNode.previousSibling);
|
|
126
|
-
}
|
|
127
|
-
this.html.insertNode(element, { afterNode: isInline ? null : this.format.isBlock(formatEl) ? null : formatEl, skipCharCount: true });
|
|
128
|
-
if (!isInline && formatEl && dom.check.isZeroWidth(formatEl)) dom.utils.removeItem(formatEl);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (isInline) {
|
|
132
|
-
const empty = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
133
|
-
element.parentNode.insertBefore(empty, element.nextSibling);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!skipHistory) this.history.push(false);
|
|
137
|
-
|
|
138
|
-
if (!skipSelection) {
|
|
139
|
-
this.selection.setRange(element, 0, element, 0);
|
|
140
|
-
const fileComponentInfo = this.get(element);
|
|
141
|
-
if (fileComponentInfo) {
|
|
142
|
-
this.select(fileComponentInfo.target, fileComponentInfo.pluginName);
|
|
143
|
-
} else if (oNode) {
|
|
144
|
-
oNode = dom.query.getEdgeChildNodes(oNode, null).sc || oNode;
|
|
145
|
-
this.selection.setRange(oNode, 0, oNode, 0);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// document type
|
|
150
|
-
if (this.editor.frameContext.has('documentType-use-header')) {
|
|
151
|
-
this.editor.frameContext.get('documentType').reHeader();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return /** @type {HTMLElement} */ (oNode || element);
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* @this {ComponentThis}
|
|
159
|
-
* @description Gets the file component and that plugin name
|
|
160
|
-
* - return: {target, component, pluginName} | null
|
|
161
|
-
* @param {Node} element Target element (figure tag, component div, file tag)
|
|
162
|
-
* @returns {__se__ComponentInfo|null}
|
|
163
|
-
*/
|
|
164
|
-
get(element) {
|
|
165
|
-
if (!element) return null;
|
|
166
|
-
|
|
167
|
-
let target;
|
|
168
|
-
let pluginName = '';
|
|
169
|
-
let options = {};
|
|
170
|
-
let isFile = false;
|
|
171
|
-
let launcher = null;
|
|
172
|
-
|
|
173
|
-
if (this.is(element)) {
|
|
174
|
-
if (dom.utils.hasClass(element, 'se-component') && !dom.utils.hasClass(element, 'se-inline-component')) element = /** @type {HTMLElement} */ (element).firstElementChild || element;
|
|
175
|
-
if (/^FIGURE$/i.test(element.nodeName)) element = /** @type {HTMLElement} */ (element).firstElementChild;
|
|
176
|
-
if (!element) return null;
|
|
177
|
-
|
|
178
|
-
const comp = this.editor._componentManager.map((f) => f(element)).find((e) => e);
|
|
179
|
-
if (!comp) return null;
|
|
180
|
-
target = comp.target;
|
|
181
|
-
pluginName = comp.pluginName;
|
|
182
|
-
options = comp.options;
|
|
183
|
-
launcher = comp.launcher;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!target && element.nodeName) {
|
|
187
|
-
if (this.__isFiles(element)) {
|
|
188
|
-
isFile = true;
|
|
189
|
-
}
|
|
190
|
-
const comp = this.editor._componentManager.map((f) => f(element)).find((e) => e);
|
|
191
|
-
if (!comp) return null;
|
|
192
|
-
target = comp.target;
|
|
193
|
-
pluginName = comp.pluginName;
|
|
194
|
-
options = comp.options;
|
|
195
|
-
launcher = comp.launcher;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (!target) {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const figureInfo = Figure.GetContainer(target);
|
|
203
|
-
const container = figureInfo.container || figureInfo.cover || target;
|
|
204
|
-
return (this.info = {
|
|
205
|
-
target,
|
|
206
|
-
pluginName,
|
|
207
|
-
options,
|
|
208
|
-
container: container,
|
|
209
|
-
cover: figureInfo.cover,
|
|
210
|
-
inlineCover: figureInfo.inlineCover,
|
|
211
|
-
caption: figureInfo.caption,
|
|
212
|
-
isFile,
|
|
213
|
-
launcher,
|
|
214
|
-
isInputType: dom.utils.hasClass(container, 'se-input-component')
|
|
215
|
-
});
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* @this {ComponentThis}
|
|
220
|
-
* @description The component(media, file component, table, etc) is selected and the resizing module is called.
|
|
221
|
-
* @param {Node} element Target element
|
|
222
|
-
* @param {string} pluginName The plugin name for the selected target.
|
|
223
|
-
* @param {Object} [options] Options
|
|
224
|
-
* @param {boolean} [options.isInput=false] Whether the target is an input component.(table)
|
|
225
|
-
*/
|
|
226
|
-
select(element, pluginName, { isInput = false } = {}) {
|
|
227
|
-
const info = this.get(element);
|
|
228
|
-
if (!info || dom.check.isUneditable(dom.query.getParentElement(element, this.is.bind(this))) || dom.check.isUneditable(element)) return false;
|
|
229
|
-
|
|
230
|
-
const plugin = info.launcher || this.plugins[pluginName];
|
|
231
|
-
if (!plugin) return;
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
this.
|
|
256
|
-
|
|
257
|
-
this.
|
|
258
|
-
this.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
dragHandle.style.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
* @
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* @this {ComponentThis}
|
|
377
|
-
* @description
|
|
378
|
-
* - This function is
|
|
379
|
-
*
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
dom.utils.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
* @
|
|
412
|
-
* @
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
this.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (this.
|
|
434
|
-
this.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
this.
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
this.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
* @
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
* @
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
this.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this._removeDragEvent();
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
* @
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
)
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
dom.
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
if (
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
this.
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Component class
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import CoreInjector from '../../editorInjector/_core';
|
|
6
|
+
import { dom, env, numbers, unicode, keyCodeMap, converter } from '../../helper';
|
|
7
|
+
import { Figure, _DragHandle } from '../../modules';
|
|
8
|
+
|
|
9
|
+
const { _w, ON_OVER_COMPONENT, isMobile } = env;
|
|
10
|
+
const DIR_KEYCODE = /^Arrow(Left|Up|Right|Down)$/;
|
|
11
|
+
const DIR_UP_KEYCODE = /^Arrow(Left|Up)$/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Omit<Component & Partial<__se__EditorInjector>, 'component'>} ComponentThis
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @constructor
|
|
19
|
+
* @this {ComponentThis}
|
|
20
|
+
* @description Class for managing components such as images and tables that are not in line format
|
|
21
|
+
* @param {__se__EditorCore} editor - The root editor instance
|
|
22
|
+
*/
|
|
23
|
+
function Component(editor) {
|
|
24
|
+
CoreInjector.call(this, editor);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @description The current component information, used copy, cut, and keydown events
|
|
28
|
+
* @type {__se__ComponentInfo}
|
|
29
|
+
*/
|
|
30
|
+
this.info = null;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @description Component is selected
|
|
34
|
+
* @type {boolean}
|
|
35
|
+
*/
|
|
36
|
+
this.isSelected = false;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @description Currently selected component target
|
|
40
|
+
* @type {Node|null}
|
|
41
|
+
*/
|
|
42
|
+
this.currentTarget = null;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @description Currently selected component plugin instance
|
|
46
|
+
* @type {*}
|
|
47
|
+
*/
|
|
48
|
+
this.currentPlugin = null;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @description Currently selected component plugin name
|
|
52
|
+
* @type {*}
|
|
53
|
+
*/
|
|
54
|
+
this.currentPluginName = '';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @description Currently selected component information
|
|
58
|
+
* @type {__se__ComponentInfo|null}
|
|
59
|
+
*/
|
|
60
|
+
this.currentInfo = null;
|
|
61
|
+
|
|
62
|
+
/** @type {Object<string, (...args: *) => *>} */
|
|
63
|
+
this.__globalEvents = {
|
|
64
|
+
copy: OnCopy_component.bind(this),
|
|
65
|
+
cut: OnCut_component.bind(this),
|
|
66
|
+
keydown: OnKeyDown_component.bind(this),
|
|
67
|
+
mousedown: CloseListener_mousedown.bind(this)
|
|
68
|
+
};
|
|
69
|
+
/** @type {__se__GlobalEventInfo|void} */
|
|
70
|
+
this._bindClose_copy = null;
|
|
71
|
+
/** @type {__se__GlobalEventInfo|void} */
|
|
72
|
+
this._bindClose_cut = null;
|
|
73
|
+
/** @type {__se__GlobalEventInfo|void} */
|
|
74
|
+
this._bindClose_keydown = null;
|
|
75
|
+
/** @type {__se__GlobalEventInfo|void} */
|
|
76
|
+
this._bindClose_mousedown = null;
|
|
77
|
+
/** @type {boolean} */
|
|
78
|
+
this.__selectionSelected = false;
|
|
79
|
+
|
|
80
|
+
this.__prevent = false;
|
|
81
|
+
|
|
82
|
+
this.editor.applyFrameRoots((e) => {
|
|
83
|
+
// drag
|
|
84
|
+
const dragHandle = dom.utils.createElement('DIV', { class: 'se-drag-handle', draggable: 'true' }, this.icons.selection);
|
|
85
|
+
e.get('wrapper').appendChild(dragHandle);
|
|
86
|
+
this.eventManager.addEvent(dragHandle, 'mouseenter', OnDragEnter.bind(this));
|
|
87
|
+
this.eventManager.addEvent(dragHandle, 'mouseleave', OnDragLeave.bind(this));
|
|
88
|
+
this.eventManager.addEvent(dragHandle, 'dragstart', OnDragStart.bind(this));
|
|
89
|
+
this.eventManager.addEvent(dragHandle, 'dragend', OnDragEnd.bind(this));
|
|
90
|
+
this.eventManager.addEvent(dragHandle, 'click', OnDragClick.bind(this));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
Component.prototype = {
|
|
95
|
+
/**
|
|
96
|
+
* @this {ComponentThis}
|
|
97
|
+
* @description Inserts an element and returns it. (Used for elements: table, hr, image, video)
|
|
98
|
+
* - If "element" is "HR", inserts and returns the new line.
|
|
99
|
+
* @param {Node} element Element to be inserted
|
|
100
|
+
* @param {Object} [options] Options
|
|
101
|
+
* @param {boolean} [options.skipCharCount=false] If true, it will be inserted even if "frameOptions.get('charCounter_max')" is exceeded.
|
|
102
|
+
* @param {boolean} [options.skipSelection=false] If true, do not automatically select the inserted component.
|
|
103
|
+
* @param {boolean} [options.skipHistory=false] If true, do not push to history.
|
|
104
|
+
* @returns {HTMLElement} The inserted element or new line (for HR)
|
|
105
|
+
*/
|
|
106
|
+
insert(element, { skipCharCount, skipSelection, skipHistory } = {}) {
|
|
107
|
+
if (this.editor.frameContext.get('isReadOnly') || (!skipCharCount && !this.char.check(element))) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const r = this.html.remove();
|
|
112
|
+
const isInline = this.isInline(element);
|
|
113
|
+
this.selection.getRangeAndAddLine(this.selection.getRange(), r.container);
|
|
114
|
+
const selectionNode = this.selection.getNode();
|
|
115
|
+
let oNode = null;
|
|
116
|
+
let formatEl = this.format.getLine(selectionNode, null);
|
|
117
|
+
|
|
118
|
+
if (dom.check.isListCell(formatEl)) {
|
|
119
|
+
this.html.insertNode(element, { afterNode: isInline ? null : selectionNode === formatEl ? null : r.container.nextSibling, skipCharCount: true });
|
|
120
|
+
if (!isInline && !element.nextSibling) element.parentNode.appendChild(dom.utils.createElement('BR'));
|
|
121
|
+
} else {
|
|
122
|
+
if (!isInline && this.selection.getRange().collapsed && (r.container.nodeType === 3 || dom.check.isBreak(r.container))) {
|
|
123
|
+
const depthFormat = dom.query.getParentElement(r.container, this.format.isBlock.bind(this.format));
|
|
124
|
+
oNode = this.nodeTransform.split(r.container, r.offset, !depthFormat ? 0 : dom.query.getNodeDepth(depthFormat) + 1);
|
|
125
|
+
if (oNode) formatEl = /** @type {HTMLElement} */ (oNode.previousSibling);
|
|
126
|
+
}
|
|
127
|
+
this.html.insertNode(element, { afterNode: isInline ? null : this.format.isBlock(formatEl) ? null : formatEl, skipCharCount: true });
|
|
128
|
+
if (!isInline && formatEl && dom.check.isZeroWidth(formatEl)) dom.utils.removeItem(formatEl);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (isInline) {
|
|
132
|
+
const empty = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
133
|
+
element.parentNode.insertBefore(empty, element.nextSibling);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!skipHistory) this.history.push(false);
|
|
137
|
+
|
|
138
|
+
if (!skipSelection) {
|
|
139
|
+
this.selection.setRange(element, 0, element, 0);
|
|
140
|
+
const fileComponentInfo = this.get(element);
|
|
141
|
+
if (fileComponentInfo) {
|
|
142
|
+
this.select(fileComponentInfo.target, fileComponentInfo.pluginName);
|
|
143
|
+
} else if (oNode) {
|
|
144
|
+
oNode = dom.query.getEdgeChildNodes(oNode, null).sc || oNode;
|
|
145
|
+
this.selection.setRange(oNode, 0, oNode, 0);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// document type
|
|
150
|
+
if (this.editor.frameContext.has('documentType-use-header')) {
|
|
151
|
+
this.editor.frameContext.get('documentType').reHeader();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return /** @type {HTMLElement} */ (oNode || element);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @this {ComponentThis}
|
|
159
|
+
* @description Gets the file component and that plugin name
|
|
160
|
+
* - return: {target, component, pluginName} | null
|
|
161
|
+
* @param {Node} element Target element (figure tag, component div, file tag)
|
|
162
|
+
* @returns {__se__ComponentInfo|null}
|
|
163
|
+
*/
|
|
164
|
+
get(element) {
|
|
165
|
+
if (!element) return null;
|
|
166
|
+
|
|
167
|
+
let target;
|
|
168
|
+
let pluginName = '';
|
|
169
|
+
let options = {};
|
|
170
|
+
let isFile = false;
|
|
171
|
+
let launcher = null;
|
|
172
|
+
|
|
173
|
+
if (this.is(element)) {
|
|
174
|
+
if (dom.utils.hasClass(element, 'se-component') && !dom.utils.hasClass(element, 'se-inline-component')) element = /** @type {HTMLElement} */ (element).firstElementChild || element;
|
|
175
|
+
if (/^FIGURE$/i.test(element.nodeName)) element = /** @type {HTMLElement} */ (element).firstElementChild;
|
|
176
|
+
if (!element) return null;
|
|
177
|
+
|
|
178
|
+
const comp = this.editor._componentManager.map((f) => f(element)).find((e) => e);
|
|
179
|
+
if (!comp) return null;
|
|
180
|
+
target = comp.target;
|
|
181
|
+
pluginName = comp.pluginName;
|
|
182
|
+
options = comp.options;
|
|
183
|
+
launcher = comp.launcher;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (!target && element.nodeName) {
|
|
187
|
+
if (this.__isFiles(element)) {
|
|
188
|
+
isFile = true;
|
|
189
|
+
}
|
|
190
|
+
const comp = this.editor._componentManager.map((f) => f(element)).find((e) => e);
|
|
191
|
+
if (!comp) return null;
|
|
192
|
+
target = comp.target;
|
|
193
|
+
pluginName = comp.pluginName;
|
|
194
|
+
options = comp.options;
|
|
195
|
+
launcher = comp.launcher;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!target) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const figureInfo = Figure.GetContainer(target);
|
|
203
|
+
const container = figureInfo.container || figureInfo.cover || target;
|
|
204
|
+
return (this.info = {
|
|
205
|
+
target,
|
|
206
|
+
pluginName,
|
|
207
|
+
options,
|
|
208
|
+
container: container,
|
|
209
|
+
cover: figureInfo.cover,
|
|
210
|
+
inlineCover: figureInfo.inlineCover,
|
|
211
|
+
caption: figureInfo.caption,
|
|
212
|
+
isFile,
|
|
213
|
+
launcher,
|
|
214
|
+
isInputType: dom.utils.hasClass(container, 'se-input-component')
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @this {ComponentThis}
|
|
220
|
+
* @description The component(media, file component, table, etc) is selected and the resizing module is called.
|
|
221
|
+
* @param {Node} element Target element
|
|
222
|
+
* @param {string} pluginName The plugin name for the selected target.
|
|
223
|
+
* @param {Object} [options] Options
|
|
224
|
+
* @param {boolean} [options.isInput=false] Whether the target is an input component.(table)
|
|
225
|
+
*/
|
|
226
|
+
select(element, pluginName, { isInput = false } = {}) {
|
|
227
|
+
const info = this.get(element);
|
|
228
|
+
if (!info || dom.check.isUneditable(dom.query.getParentElement(element, this.is.bind(this))) || dom.check.isUneditable(element)) return false;
|
|
229
|
+
|
|
230
|
+
const plugin = info.launcher || this.plugins[pluginName];
|
|
231
|
+
if (!plugin) return;
|
|
232
|
+
|
|
233
|
+
const notOver = _DragHandle.get('__overInfo') !== ON_OVER_COMPONENT;
|
|
234
|
+
if (!isInput && notOver) {
|
|
235
|
+
if (this.editor.status._onMousedown) return;
|
|
236
|
+
|
|
237
|
+
this.editor._preventBlur = true;
|
|
238
|
+
this.__selectionSelected = true;
|
|
239
|
+
if (this.isInline(info.container)) {
|
|
240
|
+
this.selection.setRange(info.container, 0, info.container, 0);
|
|
241
|
+
}
|
|
242
|
+
this.editor.blur();
|
|
243
|
+
_w.setTimeout(() => {
|
|
244
|
+
this.__selectionSelected = false;
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
this.isSelected = true;
|
|
249
|
+
this.__prevent = true;
|
|
250
|
+
|
|
251
|
+
let isNonFigureComponent;
|
|
252
|
+
if (typeof plugin.select === 'function') isNonFigureComponent = plugin.select(element);
|
|
253
|
+
|
|
254
|
+
const isBreakComponent = dom.utils.hasClass(info.target, 'se-component-line-break');
|
|
255
|
+
if (isBreakComponent || (!isNonFigureComponent && !dom.utils.hasClass(info.container, 'se-inline-component'))) this._setComponentLineBreaker(/** @type {HTMLElement} */ (info.container || info.cover || element));
|
|
256
|
+
|
|
257
|
+
this.currentTarget = element;
|
|
258
|
+
this.currentPlugin = plugin;
|
|
259
|
+
this.currentPluginName = pluginName;
|
|
260
|
+
this.currentInfo = info;
|
|
261
|
+
|
|
262
|
+
_DragHandle.set('__dragInst', this);
|
|
263
|
+
|
|
264
|
+
const __overInfo = _DragHandle.get('__overInfo');
|
|
265
|
+
_w.setTimeout(() => {
|
|
266
|
+
_DragHandle.set('__overInfo', __overInfo === ON_OVER_COMPONENT ? undefined : false);
|
|
267
|
+
if (__overInfo !== ON_OVER_COMPONENT) this.__addGlobalEvent();
|
|
268
|
+
if (!info.isFile) this.__addNotFileGlobalEvent();
|
|
269
|
+
}, 0);
|
|
270
|
+
|
|
271
|
+
converter.debounce(() => {
|
|
272
|
+
dom.utils.addClass(info.container, 'se-component-selected');
|
|
273
|
+
}, 0)();
|
|
274
|
+
|
|
275
|
+
if (notOver && !this.status.hasFocus && !this.editor._preventFocus) {
|
|
276
|
+
this.eventManager.__postFocusEvent(this.editor.frameContext, null);
|
|
277
|
+
this.editor._preventFocus = true;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!isBreakComponent && __overInfo !== ON_OVER_COMPONENT) {
|
|
281
|
+
// set zero width space
|
|
282
|
+
if (!this.isInline(info.container)) return;
|
|
283
|
+
|
|
284
|
+
const oNode = info.container;
|
|
285
|
+
let zeroWidth = null;
|
|
286
|
+
if (!oNode.previousSibling || dom.check.isBreak(oNode.previousSibling)) {
|
|
287
|
+
zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
288
|
+
oNode.parentNode.insertBefore(zeroWidth, oNode);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!oNode.nextSibling || dom.check.isBreak(oNode.nextSibling)) {
|
|
292
|
+
zeroWidth = dom.utils.createTextNode(unicode.zeroWidthSpace);
|
|
293
|
+
oNode.parentNode.insertBefore(zeroWidth, oNode.nextSibling);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.editor.status.onSelected = true;
|
|
297
|
+
} else if (isBreakComponent || !dom.utils.hasClass(info.container, 'se-input-component')) {
|
|
298
|
+
const dragHandle = this.editor.frameContext.get('wrapper').querySelector('.se-drag-handle');
|
|
299
|
+
dom.utils.addClass(dragHandle, 'se-drag-handle-full');
|
|
300
|
+
this.ui._visibleControllers(false, false);
|
|
301
|
+
|
|
302
|
+
const sizeTarget = info.caption ? info.target : info.cover || info.container || info.target;
|
|
303
|
+
const w = sizeTarget.offsetWidth;
|
|
304
|
+
const h = sizeTarget.offsetHeight;
|
|
305
|
+
const { top, left } = this.offset.getLocal(sizeTarget);
|
|
306
|
+
|
|
307
|
+
dragHandle.style.opacity = 0;
|
|
308
|
+
dragHandle.style.width = w + 'px';
|
|
309
|
+
dragHandle.style.height = h + 'px';
|
|
310
|
+
dragHandle.style.top = top + 'px';
|
|
311
|
+
dragHandle.style.left = left + 'px';
|
|
312
|
+
|
|
313
|
+
_DragHandle.set('__dragHandler', dragHandle);
|
|
314
|
+
_DragHandle.set('__dragContainer', info.container);
|
|
315
|
+
_DragHandle.set('__dragCover', info.cover);
|
|
316
|
+
|
|
317
|
+
dragHandle.style.display = 'block';
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @this {ComponentThis}
|
|
323
|
+
* @description Deselects the selected component.
|
|
324
|
+
*/
|
|
325
|
+
deselect() {
|
|
326
|
+
_w.setTimeout(() => {
|
|
327
|
+
this.editor.status.onSelected = false;
|
|
328
|
+
}, 0);
|
|
329
|
+
this.__deselect();
|
|
330
|
+
this.ui.setControllerOnDisabledButtons(false);
|
|
331
|
+
|
|
332
|
+
if (this.editor._preventFocus && !this.status.hasFocus && !this.__prevent) {
|
|
333
|
+
this.eventManager.__postBlurEvent(this.editor.frameContext, null);
|
|
334
|
+
this.editor._preventFocus = false;
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @this {ComponentThis}
|
|
340
|
+
* @description Determines if the specified node is a block component (e.g., img, iframe, video, audio, table) with the class "se-component"
|
|
341
|
+
* - or a direct FIGURE node. This function checks if the node itself is a component
|
|
342
|
+
* - or if it belongs to any components identified by the component manager.
|
|
343
|
+
* @param {Node} element The DOM node to check.
|
|
344
|
+
* @returns {boolean} True if the node is a block component or part of it, otherwise false.
|
|
345
|
+
*/
|
|
346
|
+
is(element) {
|
|
347
|
+
if (!element) return false;
|
|
348
|
+
|
|
349
|
+
if (/^FIGURE$/i.test(element.nodeName) || dom.utils.hasClass(element, 'se-component')) return true;
|
|
350
|
+
if (this.editor._componentManager.find((f) => f(element))) return true;
|
|
351
|
+
|
|
352
|
+
return false;
|
|
353
|
+
},
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* @this {ComponentThis}
|
|
357
|
+
* @description Checks if the given node is an inline component (class "se-inline-component").
|
|
358
|
+
* - If the node is a FIGURE, it checks the parent element instead.
|
|
359
|
+
* - It also verifies whether the node is part of an inline component recognized by the component manager.
|
|
360
|
+
* @param {Node} element The DOM node to check.
|
|
361
|
+
* @returns {boolean} True if the node is an inline component or part of it, otherwise false.
|
|
362
|
+
*/
|
|
363
|
+
isInline(element) {
|
|
364
|
+
if (!element) return false;
|
|
365
|
+
|
|
366
|
+
if (/^FIGURE$/i.test(element.nodeName)) element = element.parentElement;
|
|
367
|
+
if (dom.utils.hasClass(element, 'se-inline-component')) return true;
|
|
368
|
+
|
|
369
|
+
const container = this.editor._componentManager.find((f) => f(element));
|
|
370
|
+
if (container && dom.utils.hasClass(element, 'se-inline-component')) return true;
|
|
371
|
+
|
|
372
|
+
return false;
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @this {ComponentThis}
|
|
377
|
+
* @description Checks if the specified node qualifies as a basic component within the editor.
|
|
378
|
+
* - This function verifies whether the node is recognized as a component by the `is` function, while also ensuring that it is not an inline component as determined by the `isInline` function.
|
|
379
|
+
* - This is used to identify block-level elements or standalone components that are not part of the inline component classification.
|
|
380
|
+
* @param {Node} element The DOM node to check.
|
|
381
|
+
* @returns {boolean} True if the node is a basic (non-inline) component, otherwise false.
|
|
382
|
+
*/
|
|
383
|
+
isBasic(element) {
|
|
384
|
+
return this.is(element) && !this.isInline(element);
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @this {ComponentThis}
|
|
389
|
+
* @description Copies the specified component node to the clipboard.
|
|
390
|
+
* - This function is different from the one called when the user presses the "Ctrl + C" key combination.
|
|
391
|
+
* @param {Node} container The DOM node to check.
|
|
392
|
+
*/
|
|
393
|
+
copy(container) {
|
|
394
|
+
const cloneContainer = /** @type {HTMLElement} */ (dom.utils.clone(container, true));
|
|
395
|
+
|
|
396
|
+
// remove selected class
|
|
397
|
+
dom.utils.removeClass(cloneContainer, 'se-component-selected');
|
|
398
|
+
dom.utils.removeClass(cloneContainer.querySelectorAll('.se-figure-selected'), 'se-figure-selected');
|
|
399
|
+
dom.utils.removeClass(cloneContainer.querySelectorAll('.se-selected-table-cell'), 'se-selected-table-cell');
|
|
400
|
+
dom.utils.removeClass(cloneContainer.querySelector('.se-selected-cell-focus'), 'se-selected-cell-focus');
|
|
401
|
+
|
|
402
|
+
// copy to clipboard
|
|
403
|
+
this.html.copy(cloneContainer);
|
|
404
|
+
|
|
405
|
+
// copy effect
|
|
406
|
+
dom.utils.flashClass(container, 'se-copy');
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* @private
|
|
411
|
+
* @this {ComponentThis}
|
|
412
|
+
* @description Checks if the given element is a file component by matching its tag name against the file manager's regular expressions.
|
|
413
|
+
* - It also verifies whether the element has the required attributes based on the tag type.
|
|
414
|
+
* @param {Node} element The element to check.
|
|
415
|
+
* @returns {boolean} Returns true if the element is a file component, otherwise false.
|
|
416
|
+
*/
|
|
417
|
+
__isFiles(element) {
|
|
418
|
+
const nodeName = element.nodeName.toLowerCase();
|
|
419
|
+
return this.editor._fileManager.regExp.test(nodeName) && (!this.editor._fileManager.tagAttrs[nodeName] || this.editor._fileManager.tagAttrs[nodeName]?.every((v) => /** @type {HTMLElement} */ (element).hasAttribute(v)));
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @private
|
|
424
|
+
* @this {ComponentThis}
|
|
425
|
+
* @description Deselects the currently selected component, removing any selection effects and associated event listeners.
|
|
426
|
+
* - This method resets the selection state and hides UI elements related to the component selection.
|
|
427
|
+
*/
|
|
428
|
+
__deselect() {
|
|
429
|
+
this.editor._preventBlur = false;
|
|
430
|
+
_DragHandle.set('__overInfo', null);
|
|
431
|
+
this._removeDragEvent();
|
|
432
|
+
|
|
433
|
+
if (this.currentInfo) {
|
|
434
|
+
const infoContainer = this.currentInfo.container;
|
|
435
|
+
const infoCover = this.currentInfo.cover;
|
|
436
|
+
converter.debounce(() => {
|
|
437
|
+
dom.utils.removeClass(infoContainer, 'se-component-selected');
|
|
438
|
+
dom.utils.removeClass(infoCover, 'se-figure-over-selected');
|
|
439
|
+
}, 0)();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const { frameContext } = this.editor;
|
|
443
|
+
if (frameContext.get('lineBreaker_t')) {
|
|
444
|
+
frameContext.get('lineBreaker_t').style.display = frameContext.get('lineBreaker_b').style.display = 'none';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (this.currentPlugin && typeof this.currentPlugin.deselect === 'function') {
|
|
448
|
+
this.currentPlugin.deselect(this.currentTarget);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
this.isSelected = false;
|
|
452
|
+
this.currentPlugin = null;
|
|
453
|
+
this.currentTarget = null;
|
|
454
|
+
this.currentPluginName = '';
|
|
455
|
+
this.currentInfo = null;
|
|
456
|
+
this.__removeGlobalEvent();
|
|
457
|
+
this.ui.__offControllers();
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* @private
|
|
462
|
+
* @this {ComponentThis}
|
|
463
|
+
* @description Set line breaker of component
|
|
464
|
+
* @param {HTMLElement} element Element tag
|
|
465
|
+
*/
|
|
466
|
+
_setComponentLineBreaker(element) {
|
|
467
|
+
const _overInfo = _DragHandle.get('__overInfo') === ON_OVER_COMPONENT;
|
|
468
|
+
this.eventManager._lineBreakComp = null;
|
|
469
|
+
const info = this.get(element);
|
|
470
|
+
if (!info) return;
|
|
471
|
+
|
|
472
|
+
const fc = this.editor.frameContext;
|
|
473
|
+
const container = info.container;
|
|
474
|
+
const isNonSelected = dom.utils.hasClass(container, 'se-flex-component');
|
|
475
|
+
const lb_t = fc.get('lineBreaker_t');
|
|
476
|
+
const lb_b = fc.get('lineBreaker_b');
|
|
477
|
+
const t_style = lb_t.style;
|
|
478
|
+
const b_style = lb_b.style;
|
|
479
|
+
const offsetTarget = container.offsetWidth < element.offsetWidth ? container : element;
|
|
480
|
+
const target = this.editor._figureContainer?.style.display === 'block' ? this.editor._figureContainer : offsetTarget;
|
|
481
|
+
const isList = dom.check.isListCell(container.parentNode);
|
|
482
|
+
|
|
483
|
+
// top
|
|
484
|
+
let componentTop, w;
|
|
485
|
+
const isRtl = this.options.get('_rtl');
|
|
486
|
+
const dir = isRtl ? ['right', 'left'] : ['left', 'right'];
|
|
487
|
+
const { top, left, right, scrollX, scrollY } = this.offset.getLocal(offsetTarget);
|
|
488
|
+
const sideOffset = isRtl ? right : left;
|
|
489
|
+
|
|
490
|
+
if (isList ? !container.previousSibling : !this.format.isLine(container.previousElementSibling)) {
|
|
491
|
+
const cStyle = _w.getComputedStyle(lb_t);
|
|
492
|
+
const cH = numbers.get(cStyle.height, 1);
|
|
493
|
+
const cW = numbers.get(cStyle.width, 1);
|
|
494
|
+
|
|
495
|
+
this.eventManager._lineBreakComp = container;
|
|
496
|
+
componentTop = top;
|
|
497
|
+
w = target.offsetWidth / 2 / 2;
|
|
498
|
+
|
|
499
|
+
t_style.top = componentTop - cH / 2 + 'px';
|
|
500
|
+
t_style[dir[0]] = (isNonSelected ? sideOffset - cW / 2 : sideOffset + w) + 'px';
|
|
501
|
+
t_style[dir[1]] = '';
|
|
502
|
+
|
|
503
|
+
lb_t.setAttribute('data-offset', scrollY + ',' + scrollX);
|
|
504
|
+
if (_overInfo) dom.utils.removeClass(lb_t, 'se-on-selected');
|
|
505
|
+
else dom.utils.addClass(lb_t, 'se-on-selected');
|
|
506
|
+
|
|
507
|
+
t_style.display = 'block';
|
|
508
|
+
t_style.visibility = '';
|
|
509
|
+
} else {
|
|
510
|
+
t_style.display = 'none';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// bottom
|
|
514
|
+
if (isList ? !container.nextSibling : !this.format.isLine(container.nextElementSibling)) {
|
|
515
|
+
const cStyle = _w.getComputedStyle(lb_b);
|
|
516
|
+
const cH = numbers.get(cStyle.height, 1);
|
|
517
|
+
const cW = numbers.get(cStyle.width, 1);
|
|
518
|
+
|
|
519
|
+
if (!componentTop) {
|
|
520
|
+
this.eventManager._lineBreakComp = container;
|
|
521
|
+
componentTop = top;
|
|
522
|
+
w = target.offsetWidth / 2 / 2;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
b_style.top = componentTop + target.offsetHeight - cH / 2 + 'px';
|
|
526
|
+
b_style[dir[0]] = sideOffset + target.offsetWidth - (isNonSelected ? 0 : w) - (isNonSelected ? cW / 2 : cW) + 'px';
|
|
527
|
+
b_style[dir[1]] = '';
|
|
528
|
+
|
|
529
|
+
lb_b.setAttribute('data-offset', scrollY + ',' + dir[0] + ',' + scrollX);
|
|
530
|
+
if (_overInfo) dom.utils.removeClass(lb_b, 'se-on-selected');
|
|
531
|
+
else dom.utils.addClass(lb_b, 'se-on-selected');
|
|
532
|
+
|
|
533
|
+
b_style.display = 'block';
|
|
534
|
+
b_style.visibility = '';
|
|
535
|
+
} else {
|
|
536
|
+
b_style.display = 'none';
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* @private
|
|
542
|
+
* @this {ComponentThis}
|
|
543
|
+
* @description Adds global event listeners for component interactions such as copy, cut, and keydown events.
|
|
544
|
+
*/
|
|
545
|
+
__addGlobalEvent() {
|
|
546
|
+
this.__removeGlobalEvent();
|
|
547
|
+
this._bindClose_copy = this.eventManager.addGlobalEvent('copy', this.__globalEvents.copy);
|
|
548
|
+
this._bindClose_cut = this.eventManager.addGlobalEvent('cut', this.__globalEvents.cut);
|
|
549
|
+
this._bindClose_keydown = this.eventManager.addGlobalEvent('keydown', this.__globalEvents.keydown);
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* @private
|
|
554
|
+
* @this {ComponentThis}
|
|
555
|
+
* @description Removes global event listeners that were previously added for component interactions.
|
|
556
|
+
*/
|
|
557
|
+
__removeGlobalEvent() {
|
|
558
|
+
this.__removeNotFileGlobalEvent();
|
|
559
|
+
if (this._bindClose_copy) this._bindClose_copy = this.eventManager.removeGlobalEvent(this._bindClose_copy);
|
|
560
|
+
if (this._bindClose_cut) this._bindClose_cut = this.eventManager.removeGlobalEvent(this._bindClose_cut);
|
|
561
|
+
if (this._bindClose_keydown) this._bindClose_keydown = this.eventManager.removeGlobalEvent(this._bindClose_keydown);
|
|
562
|
+
},
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* @private
|
|
566
|
+
* @this {ComponentThis}
|
|
567
|
+
* @description Adds global event listeners for non-file-related interactions such as mouse and touch events.
|
|
568
|
+
*/
|
|
569
|
+
__addNotFileGlobalEvent() {
|
|
570
|
+
this.__removeNotFileGlobalEvent();
|
|
571
|
+
this._bindClose_mousedown = this.eventManager.addGlobalEvent(isMobile ? 'click' : 'mousedown', this.__globalEvents.mousedown, true);
|
|
572
|
+
},
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* @private
|
|
576
|
+
* @this {ComponentThis}
|
|
577
|
+
* @description Removes global event listeners related to non-file interactions.
|
|
578
|
+
*/
|
|
579
|
+
__removeNotFileGlobalEvent() {
|
|
580
|
+
if (this._bindClose_mousedown) this._bindClose_mousedown = this.eventManager.removeGlobalEvent(this._bindClose_mousedown);
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* @private
|
|
585
|
+
* @this {ComponentThis}
|
|
586
|
+
* @description Removes drag-related events and resets drag-related states.
|
|
587
|
+
*/
|
|
588
|
+
_removeDragEvent() {
|
|
589
|
+
/** @type {HTMLElement} */ (this.carrierWrapper.querySelector('.se-drag-cursor')).style.left = '-10000px';
|
|
590
|
+
if (_DragHandle.get('__dragHandler')) _DragHandle.get('__dragHandler').style.display = 'none';
|
|
591
|
+
|
|
592
|
+
dom.utils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
|
|
593
|
+
dom.utils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
|
|
594
|
+
|
|
595
|
+
_DragHandle.set('__figureInst', null);
|
|
596
|
+
_DragHandle.set('__dragInst', null);
|
|
597
|
+
_DragHandle.set('__dragHandler', null);
|
|
598
|
+
_DragHandle.set('__dragContainer', null);
|
|
599
|
+
_DragHandle.set('__dragCover', null);
|
|
600
|
+
_DragHandle.set('__dragMove', null);
|
|
601
|
+
_DragHandle.set('__overInfo', null);
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
constructor: Component
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* @this {ComponentThis}
|
|
609
|
+
*/
|
|
610
|
+
function OnDragEnter() {
|
|
611
|
+
this.editor._preventBlur = true;
|
|
612
|
+
this.ui._visibleControllers(false, dom.utils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full'));
|
|
613
|
+
dom.utils.addClass(_DragHandle.get('__dragCover') || _DragHandle.get('__dragContainer'), 'se-drag-over');
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* @this {ComponentThis}
|
|
618
|
+
*/
|
|
619
|
+
function OnDragLeave() {
|
|
620
|
+
this.editor._preventBlur = false;
|
|
621
|
+
if (!dom.utils.hasClass(_DragHandle.get('__dragHandler'), 'se-drag-handle-full')) this.ui._visibleControllers(true, true);
|
|
622
|
+
dom.utils.removeClass([_DragHandle.get('__dragCover'), _DragHandle.get('__dragContainer')], 'se-drag-over');
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* @this {ComponentThis}
|
|
627
|
+
* @param {DragEvent} e - Drag event
|
|
628
|
+
*/
|
|
629
|
+
function OnDragStart(e) {
|
|
630
|
+
const cover = _DragHandle.get('__dragCover') || _DragHandle.get('__dragContainer');
|
|
631
|
+
|
|
632
|
+
if (!cover) {
|
|
633
|
+
e.preventDefault();
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
this.editor._preventBlur = false;
|
|
638
|
+
dom.utils.addClass(_DragHandle.get('__dragHandler'), 'se-dragging');
|
|
639
|
+
dom.utils.addClass(_DragHandle.get('__dragContainer'), 'se-dragging');
|
|
640
|
+
e.dataTransfer.setDragImage(cover, this.options.get('_rtl') ? cover.offsetWidth : -5, -5);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* @this {ComponentThis}
|
|
645
|
+
*/
|
|
646
|
+
function OnDragEnd() {
|
|
647
|
+
this.editor._preventBlur = false;
|
|
648
|
+
dom.utils.removeClass([_DragHandle.get('__dragHandler'), _DragHandle.get('__dragContainer')], 'se-dragging');
|
|
649
|
+
this._removeDragEvent();
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* @this {ComponentThis}
|
|
654
|
+
* @param {MouseEvent} e - Mouse event
|
|
655
|
+
*/
|
|
656
|
+
function OnDragClick(e) {
|
|
657
|
+
const target = dom.query.getEventTarget(e);
|
|
658
|
+
if (!dom.utils.hasClass(target, 'se-drag-handle-full')) return;
|
|
659
|
+
|
|
660
|
+
const dragInst = _DragHandle.get('__dragInst');
|
|
661
|
+
if (!dragInst) return;
|
|
662
|
+
|
|
663
|
+
this._removeDragEvent();
|
|
664
|
+
this.select(dragInst.currentTarget, dragInst.currentPluginName);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* @this {ComponentThis}
|
|
669
|
+
* @param {MouseEvent} e - Mouse event
|
|
670
|
+
*/
|
|
671
|
+
function CloseListener_mousedown(e) {
|
|
672
|
+
const target = dom.query.getEventTarget(e);
|
|
673
|
+
if (
|
|
674
|
+
this.currentTarget?.contains(target) ||
|
|
675
|
+
dom.query.getParentElement(target, '.se-controller') ||
|
|
676
|
+
dom.utils.hasClass(target, 'se-drag-handle') ||
|
|
677
|
+
(this.currentPluginName === this.editor.currentControllerName && this.editor.opendControllers.some(({ form }) => form.contains(target)))
|
|
678
|
+
) {
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
this.deselect();
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* @this {ComponentThis}
|
|
686
|
+
* @param {ClipboardEvent} e - Event object
|
|
687
|
+
*/
|
|
688
|
+
function OnCopy_component(e) {
|
|
689
|
+
const target = dom.query.getEventTarget(e);
|
|
690
|
+
if (dom.check.isInputElement(target) && dom.query.getParentElement(target, '.se-modal')) return;
|
|
691
|
+
|
|
692
|
+
const info = this.info;
|
|
693
|
+
if (!info) return;
|
|
694
|
+
|
|
695
|
+
const cloneContainer = info.container.cloneNode(true);
|
|
696
|
+
dom.utils.removeClass(cloneContainer, 'se-component-selected');
|
|
697
|
+
|
|
698
|
+
if (typeof this.plugins[info.pluginName]?.onCopyComponent !== 'function' || this.plugins[info.pluginName].onCopyComponent({ event: e, cloneContainer, info }) === false) {
|
|
699
|
+
SetClipboardComponent(e, cloneContainer, e.clipboardData);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// copy effect
|
|
703
|
+
dom.utils.flashClass(info.container, 'se-copy');
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* @this {ComponentThis}
|
|
708
|
+
* @param {ClipboardEvent} e - Event object
|
|
709
|
+
*/
|
|
710
|
+
function OnCut_component(e) {
|
|
711
|
+
const info = this.info;
|
|
712
|
+
if (!info) return;
|
|
713
|
+
|
|
714
|
+
const cloneContainer = info.container.cloneNode(true);
|
|
715
|
+
dom.utils.removeClass(cloneContainer, 'se-component-selected');
|
|
716
|
+
|
|
717
|
+
SetClipboardComponent(e, cloneContainer, e.clipboardData);
|
|
718
|
+
this.deselect();
|
|
719
|
+
dom.utils.removeItem(info.container);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* @this {ComponentThis}
|
|
724
|
+
* @param {KeyboardEvent} e - Event object
|
|
725
|
+
*/
|
|
726
|
+
async function OnKeyDown_component(e) {
|
|
727
|
+
if (this.editor.selectMenuOn) return;
|
|
728
|
+
|
|
729
|
+
const keyCode = e.code;
|
|
730
|
+
const ctrl = keyCodeMap.isCtrl(e);
|
|
731
|
+
|
|
732
|
+
// redo, undo
|
|
733
|
+
if (ctrl) {
|
|
734
|
+
if (keyCode !== 'ControlRight' && keyCode !== 'ControlLeft') {
|
|
735
|
+
const info = this.editor.shortcutsKeyMap.get(keyCode + (e.shiftKey ? '1000' : ''));
|
|
736
|
+
if (/^(redo|undo)$/.test(info?.c)) {
|
|
737
|
+
e.preventDefault();
|
|
738
|
+
e.stopPropagation();
|
|
739
|
+
this.editor.run(info.c, info.t, info.e);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// backspace key, delete key
|
|
746
|
+
if (keyCodeMap.isRemoveKey(keyCode)) {
|
|
747
|
+
e.preventDefault();
|
|
748
|
+
e.stopPropagation();
|
|
749
|
+
if (typeof this.currentPlugin?.destroy === 'function') {
|
|
750
|
+
await this.currentPlugin.destroy(this.currentTarget);
|
|
751
|
+
this.deselect();
|
|
752
|
+
this.editor.focus();
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// enter key
|
|
758
|
+
if (keyCodeMap.isEnter(keyCode)) {
|
|
759
|
+
e.preventDefault();
|
|
760
|
+
const compContext = this.currentInfo || this.get(this.currentTarget);
|
|
761
|
+
const container = compContext.container || compContext.target;
|
|
762
|
+
const sibling = container.previousElementSibling || container.nextElementSibling;
|
|
763
|
+
let newEl = null;
|
|
764
|
+
if (dom.check.isListCell(container.parentNode)) {
|
|
765
|
+
newEl = dom.utils.createElement('BR');
|
|
766
|
+
} else {
|
|
767
|
+
newEl = dom.utils.createElement(this.format.isLine(sibling) ? sibling.nodeName : this.options.get('defaultLine'), null, '<br>');
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const pluginName = this.currentPluginName;
|
|
771
|
+
this.deselect();
|
|
772
|
+
container.parentNode.insertBefore(newEl, container);
|
|
773
|
+
if (this.select(compContext.target, pluginName) === false) this.editor.blur();
|
|
774
|
+
this.history.push(false);
|
|
775
|
+
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// up down, left right
|
|
780
|
+
if (DIR_KEYCODE.test(keyCode)) {
|
|
781
|
+
const { container } = this.get(this.currentTarget);
|
|
782
|
+
const isInline = this.isInline(container || this.currentTarget);
|
|
783
|
+
|
|
784
|
+
let el = null;
|
|
785
|
+
let offset = 1;
|
|
786
|
+
if (isInline) {
|
|
787
|
+
switch (keyCode) {
|
|
788
|
+
case 'ArrowLeft': // left
|
|
789
|
+
el = container.previousSibling;
|
|
790
|
+
offset = el?.nodeType === 3 ? el.textContent.length : 1;
|
|
791
|
+
break;
|
|
792
|
+
case 'ArrowRight': // right
|
|
793
|
+
el = container.nextSibling;
|
|
794
|
+
offset = 0;
|
|
795
|
+
break;
|
|
796
|
+
case 'ArrowUp': {
|
|
797
|
+
// up
|
|
798
|
+
const line = this.format.getLine(container, null);
|
|
799
|
+
el = line?.previousElementSibling;
|
|
800
|
+
offset = 0;
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
case 'ArrowDown': {
|
|
804
|
+
// down
|
|
805
|
+
const line = this.format.getLine(container, null);
|
|
806
|
+
el = line?.nextElementSibling;
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
} else {
|
|
811
|
+
if (DIR_UP_KEYCODE.test(keyCode)) {
|
|
812
|
+
el = container.previousElementSibling;
|
|
813
|
+
} else {
|
|
814
|
+
el = container.nextElementSibling;
|
|
815
|
+
offset = 0;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (!el) return;
|
|
820
|
+
|
|
821
|
+
this.deselect();
|
|
822
|
+
|
|
823
|
+
const elComp = this.get(el);
|
|
824
|
+
if (elComp?.container) {
|
|
825
|
+
e.stopPropagation();
|
|
826
|
+
e.preventDefault();
|
|
827
|
+
this.select(elComp.target, elComp.pluginName);
|
|
828
|
+
} else {
|
|
829
|
+
try {
|
|
830
|
+
this.editor._preventBlur = true;
|
|
831
|
+
e.stopPropagation();
|
|
832
|
+
e.preventDefault();
|
|
833
|
+
this.selection.setRange(el, offset, el, offset);
|
|
834
|
+
} finally {
|
|
835
|
+
this.editor._preventBlur = false;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// ESC
|
|
843
|
+
if (keyCodeMap.isEsc(keyCode)) {
|
|
844
|
+
this.deselect();
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function SetClipboardComponent(e, container, clipboardData) {
|
|
850
|
+
e.preventDefault();
|
|
851
|
+
e.stopPropagation();
|
|
852
|
+
container.querySelectorAll('.se-figure-selected').forEach((el) => dom.utils.removeClass(el, 'se-figure-selected'));
|
|
853
|
+
clipboardData.setData('text/html', container.outerHTML);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export default Component;
|