@akilli/editor-src 5.1.5
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/abbreviation/Abbreviation.js +32 -0
- package/abbreviation/AbbreviationDialog.js +21 -0
- package/audio/Audio.js +47 -0
- package/audio/AudioDialog.js +18 -0
- package/audio/AudioListener.js +50 -0
- package/base/AlignCommand.js +34 -0
- package/base/AlignableListener.js +45 -0
- package/base/Alignment.js +36 -0
- package/base/BarListener.js +95 -0
- package/base/Base.js +127 -0
- package/base/Command.js +139 -0
- package/base/CommandManager.js +60 -0
- package/base/ContentFilter.js +109 -0
- package/base/DeletableListener.js +36 -0
- package/base/DeleteCommand.js +18 -0
- package/base/Dialog.js +153 -0
- package/base/DialogManager.js +44 -0
- package/base/Dispatcher.js +88 -0
- package/base/Dom.js +790 -0
- package/base/EditableListener.js +82 -0
- package/base/Editor.js +448 -0
- package/base/Filter.js +35 -0
- package/base/FilterManager.js +44 -0
- package/base/FocusableListener.js +22 -0
- package/base/FocusbarListener.js +99 -0
- package/base/FormCreator.js +162 -0
- package/base/FormatbarListener.js +32 -0
- package/base/Key.js +258 -0
- package/base/Listener.js +51 -0
- package/base/NavigableListener.js +81 -0
- package/base/Plugin.js +176 -0
- package/base/PluginManager.js +51 -0
- package/base/SlotableListener.js +40 -0
- package/base/SortCommand.js +30 -0
- package/base/SortableListener.js +135 -0
- package/base/Sorting.js +36 -0
- package/base/Tag.js +113 -0
- package/base/TagGroup.js +183 -0
- package/base/TagListener.js +34 -0
- package/base/TagManager.js +61 -0
- package/base/TagName.js +470 -0
- package/base/ToolbarListener.js +11 -0
- package/base/util.js +59 -0
- package/block/Block.js +51 -0
- package/block/BlockDialog.js +11 -0
- package/block/BlockElement.js +21 -0
- package/block/BlockListener.js +60 -0
- package/blockquote/Blockquote.js +43 -0
- package/blockquote/BlockquoteFilter.js +22 -0
- package/blockquote/BlockquoteListener.js +34 -0
- package/bold/Bold.js +30 -0
- package/break/Break.js +33 -0
- package/break/BreakFilter.js +24 -0
- package/build/BuildEditor.js +97 -0
- package/build/editor.css +548 -0
- package/build/editor.woff2 +0 -0
- package/cite/Cite.js +30 -0
- package/code/Code.js +30 -0
- package/data/Data.js +32 -0
- package/data/DataDialog.js +13 -0
- package/definition/Definition.js +32 -0
- package/definition/DefinitionDialog.js +13 -0
- package/deletion/Deletion.js +30 -0
- package/details/Details.js +63 -0
- package/details/DetailsFilter.js +17 -0
- package/details/DetailsListener.js +102 -0
- package/division/Division.js +53 -0
- package/division/DivisionDialog.js +13 -0
- package/emphasis/Emphasis.js +30 -0
- package/figure/Figure.js +58 -0
- package/figure/FigureFilter.js +14 -0
- package/figure/FigureListener.js +23 -0
- package/heading/Heading.js +38 -0
- package/horizontalrule/HorizontalRule.js +37 -0
- package/i18n/I18n.js +26 -0
- package/i18n/de.js +167 -0
- package/iframe/Iframe.js +49 -0
- package/iframe/IframeDialog.js +20 -0
- package/iframe/IframeListener.js +48 -0
- package/image/Image.js +47 -0
- package/image/ImageDialog.js +23 -0
- package/image/ImageListener.js +47 -0
- package/insertion/Insertion.js +30 -0
- package/italic/Italic.js +30 -0
- package/keyboard/Keyboard.js +30 -0
- package/link/Link.js +34 -0
- package/link/LinkDialog.js +14 -0
- package/link/LinkListener.js +45 -0
- package/list/List.js +40 -0
- package/list/ListListener.js +91 -0
- package/mark/Mark.js +30 -0
- package/orderedlist/OrderedList.js +39 -0
- package/package.json +24 -0
- package/paragraph/Paragraph.js +42 -0
- package/paragraph/ParagraphListener.js +40 -0
- package/preformat/Preformat.js +43 -0
- package/preformat/PreformatFilter.js +22 -0
- package/preformat/PreformatListener.js +34 -0
- package/quote/Quote.js +30 -0
- package/sample/Sample.js +30 -0
- package/section/Section.js +55 -0
- package/section/SectionDialog.js +13 -0
- package/small/Small.js +30 -0
- package/strikethrough/Strikethrough.js +30 -0
- package/strong/Strong.js +30 -0
- package/subheading/Subheading.js +38 -0
- package/subscript/Subscript.js +30 -0
- package/superscript/Superscript.js +30 -0
- package/table/Table.js +113 -0
- package/table/TableCellListener.js +125 -0
- package/table/TableColumnAddCommand.js +19 -0
- package/table/TableColumnListener.js +34 -0
- package/table/TableCommand.js +23 -0
- package/table/TableDialog.js +14 -0
- package/table/TableFilter.js +35 -0
- package/table/TableListener.js +39 -0
- package/table/TableRowAddCommand.js +18 -0
- package/table/TableRowListener.js +34 -0
- package/time/Time.js +32 -0
- package/time/TimeDialog.js +13 -0
- package/underline/Underline.js +30 -0
- package/unorderedlist/UnorderedList.js +39 -0
- package/variable/Variable.js +30 -0
- package/video/Video.js +47 -0
- package/video/VideoDialog.js +20 -0
- package/video/VideoListener.js +48 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import AbbreviationDialog from './AbbreviationDialog.js';
|
|
2
|
+
import Base from '../base/Base.js';
|
|
3
|
+
import Key from '../base/Key.js';
|
|
4
|
+
import Plugin from '../base/Plugin.js';
|
|
5
|
+
import TagGroup from '../base/TagGroup.js';
|
|
6
|
+
import TagName from '../base/TagName.js';
|
|
7
|
+
|
|
8
|
+
export default class Abbreviation extends Plugin {
|
|
9
|
+
/**
|
|
10
|
+
* @type {string}
|
|
11
|
+
*/
|
|
12
|
+
static get name() {
|
|
13
|
+
return 'abbreviation';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @type {Plugin[]}
|
|
18
|
+
*/
|
|
19
|
+
static get dependencies() {
|
|
20
|
+
return [Base];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @return {void}
|
|
25
|
+
*/
|
|
26
|
+
init() {
|
|
27
|
+
this._tag({ name: TagName.ABBR, group: TagGroup.FORMAT, attributes: ['title'] });
|
|
28
|
+
this.editor.dialogs.set(new AbbreviationDialog(this.editor));
|
|
29
|
+
this._command(TagName.ABBR);
|
|
30
|
+
this._formatbar('Abbreviation', Key.A);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import Abbreviation from './Abbreviation.js';
|
|
2
|
+
import Dialog from '../base/Dialog.js';
|
|
3
|
+
|
|
4
|
+
export default class AbbreviationDialog extends Dialog {
|
|
5
|
+
/**
|
|
6
|
+
* @param {Editor} editor
|
|
7
|
+
*/
|
|
8
|
+
constructor(editor) {
|
|
9
|
+
super(editor, Abbreviation.name);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @protected
|
|
14
|
+
* @return {void}
|
|
15
|
+
*/
|
|
16
|
+
_prepareForm() {
|
|
17
|
+
this.formCreator.addLegend(this._('Abbreviation')).addTextInput('title', this._('Full term'), {
|
|
18
|
+
placeholder: this._('Insert full term or leave empty to remove it'),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
package/audio/Audio.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import AudioDialog from './AudioDialog.js';
|
|
2
|
+
import AudioListener from './AudioListener.js';
|
|
3
|
+
import Base from '../base/Base.js';
|
|
4
|
+
import Figure from '../figure/Figure.js';
|
|
5
|
+
import Plugin from '../base/Plugin.js';
|
|
6
|
+
import TagGroup from '../base/TagGroup.js';
|
|
7
|
+
import TagName from '../base/TagName.js';
|
|
8
|
+
|
|
9
|
+
export default class Audio extends Plugin {
|
|
10
|
+
/**
|
|
11
|
+
* @type {string}
|
|
12
|
+
*/
|
|
13
|
+
static get name() {
|
|
14
|
+
return 'audio';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @type {Plugin[]}
|
|
19
|
+
*/
|
|
20
|
+
static get dependencies() {
|
|
21
|
+
return [Base, Figure];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @type {Object.<string, any>}
|
|
26
|
+
*/
|
|
27
|
+
static get config() {
|
|
28
|
+
return { browser: undefined };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @return {void}
|
|
33
|
+
*/
|
|
34
|
+
init() {
|
|
35
|
+
this._tag({
|
|
36
|
+
name: TagName.AUDIO,
|
|
37
|
+
group: TagGroup.AUDIO,
|
|
38
|
+
attributes: ['controls', 'id', 'src'],
|
|
39
|
+
empty: true,
|
|
40
|
+
navigable: true,
|
|
41
|
+
});
|
|
42
|
+
new AudioListener(this.editor);
|
|
43
|
+
this.editor.dialogs.set(new AudioDialog(this.editor, this.constructor.name, this.editor.config.audio.browser));
|
|
44
|
+
this._command(TagName.AUDIO);
|
|
45
|
+
this._toolbar('Audio');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import Dialog from '../base/Dialog.js';
|
|
2
|
+
|
|
3
|
+
export default class AudioDialog extends Dialog {
|
|
4
|
+
/**
|
|
5
|
+
* @protected
|
|
6
|
+
* @return {void}
|
|
7
|
+
*/
|
|
8
|
+
_prepareForm() {
|
|
9
|
+
this.formCreator
|
|
10
|
+
.addLegend(this._('Audio'))
|
|
11
|
+
.addTextInput('src', this._('URL'), {
|
|
12
|
+
pattern: '(https?|/).+',
|
|
13
|
+
placeholder: this._('Insert URL to audio'),
|
|
14
|
+
required: 'required',
|
|
15
|
+
})
|
|
16
|
+
.addTextInput('id', this._('ID'));
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import Audio from './Audio.js';
|
|
2
|
+
import Listener from '../base/Listener.js';
|
|
3
|
+
import TagName from '../base/TagName.js';
|
|
4
|
+
|
|
5
|
+
export default class AudioListener extends Listener {
|
|
6
|
+
/**
|
|
7
|
+
* @param {Editor} editor
|
|
8
|
+
*/
|
|
9
|
+
constructor(editor) {
|
|
10
|
+
super(editor);
|
|
11
|
+
this.editor.root.addEventListener('sethtml', this);
|
|
12
|
+
this.editor.root.addEventListener('insertaudio', this);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {CustomEvent} event
|
|
17
|
+
* @param {HTMLElement} event.detail.element
|
|
18
|
+
* @return {void}
|
|
19
|
+
*/
|
|
20
|
+
sethtml({ detail: { element } }) {
|
|
21
|
+
Array.from(element.getElementsByTagName(TagName.AUDIO)).forEach((item) => this.#init(item));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {CustomEvent} event
|
|
26
|
+
* @param {HTMLAudioElement} event.detail.element
|
|
27
|
+
* @return {void}
|
|
28
|
+
*/
|
|
29
|
+
insertaudio({ detail: { element } }) {
|
|
30
|
+
this.#init(element);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Adds controls and wraps in figure if necessary
|
|
35
|
+
*
|
|
36
|
+
* @param {HTMLAudioElement} element
|
|
37
|
+
* @return {void}
|
|
38
|
+
*/
|
|
39
|
+
#init(element) {
|
|
40
|
+
const src = element.getAttribute('src');
|
|
41
|
+
|
|
42
|
+
if (!src) {
|
|
43
|
+
element.parentElement.removeChild(element);
|
|
44
|
+
} else {
|
|
45
|
+
element.setAttribute('src', this.editor.url(src));
|
|
46
|
+
element.controls = true;
|
|
47
|
+
this.editor.dom.wrap(element, TagName.FIGURE, { attributes: { class: Audio.name } });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import Alignment from './Alignment.js';
|
|
2
|
+
import Command from './Command.js';
|
|
3
|
+
|
|
4
|
+
export default class AlignCommand extends Command {
|
|
5
|
+
/**
|
|
6
|
+
* @type {string}
|
|
7
|
+
*/
|
|
8
|
+
#alignment;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {Editor} editor
|
|
12
|
+
* @param {string} alignment
|
|
13
|
+
*/
|
|
14
|
+
constructor(editor, alignment) {
|
|
15
|
+
if (!Alignment.values().includes(alignment)) {
|
|
16
|
+
throw new TypeError('Invalid argument');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
super(editor, 'align-' + alignment);
|
|
20
|
+
this.#alignment = alignment;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @return {void}
|
|
25
|
+
*/
|
|
26
|
+
execute() {
|
|
27
|
+
const element = this.editor.dom.getActiveElement();
|
|
28
|
+
|
|
29
|
+
if (element?.hasAttribute('data-alignable')) {
|
|
30
|
+
this.editor.dom.removeClass(element, ...Alignment.values());
|
|
31
|
+
this.#alignment !== Alignment.NONE && element.classList.add(this.#alignment);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Alignment from './Alignment.js';
|
|
2
|
+
import Key from './Key.js';
|
|
3
|
+
import Listener from './Listener.js';
|
|
4
|
+
|
|
5
|
+
export default class AlignableListener extends Listener {
|
|
6
|
+
/**
|
|
7
|
+
* @param {Editor} editor
|
|
8
|
+
*/
|
|
9
|
+
constructor(editor) {
|
|
10
|
+
super(editor);
|
|
11
|
+
this.editor.root.addEventListener('insert', this);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {CustomEvent} event
|
|
16
|
+
* @param {HTMLElement} event.detail.element
|
|
17
|
+
* @return {void}
|
|
18
|
+
*/
|
|
19
|
+
insert({ detail: { element } }) {
|
|
20
|
+
if (element.hasAttribute('data-alignable')) {
|
|
21
|
+
element.addEventListener('keydown', this);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {KeyboardEvent} event
|
|
27
|
+
* @param {HTMLElement} event.target
|
|
28
|
+
* @return {void}
|
|
29
|
+
*/
|
|
30
|
+
keydown(event) {
|
|
31
|
+
const map = {
|
|
32
|
+
[Key.ARROWUP]: Alignment.NONE,
|
|
33
|
+
[Key.ARROWLEFT]: Alignment.LEFT,
|
|
34
|
+
[Key.ARROWDOWN]: Alignment.CENTER,
|
|
35
|
+
[Key.ARROWRIGHT]: Alignment.RIGHT,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
if (event.target === event.currentTarget && Key.isEventFor(event, Object.keys(map), { shift: true })) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
this.editor.dom.removeClass(event.target, ...Alignment.values());
|
|
42
|
+
map[event.key] !== Alignment.NONE && event.target.classList.add(map[event.key]);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export default class Alignment {
|
|
2
|
+
/**
|
|
3
|
+
* @return {string}
|
|
4
|
+
*/
|
|
5
|
+
static get NONE() {
|
|
6
|
+
return 'none';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @return {string}
|
|
11
|
+
*/
|
|
12
|
+
static get LEFT() {
|
|
13
|
+
return 'left';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @return {string}
|
|
18
|
+
*/
|
|
19
|
+
static get CENTER() {
|
|
20
|
+
return 'center';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @return {string}
|
|
25
|
+
*/
|
|
26
|
+
static get RIGHT() {
|
|
27
|
+
return 'right';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @return {string[]}
|
|
32
|
+
*/
|
|
33
|
+
static values() {
|
|
34
|
+
return [this.NONE, this.LEFT, this.CENTER, this.RIGHT];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import Key from './Key.js';
|
|
2
|
+
import Listener from './Listener.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @abstract
|
|
6
|
+
*/
|
|
7
|
+
export default class BarListener extends Listener {
|
|
8
|
+
/**
|
|
9
|
+
* @param {CustomEvent} event
|
|
10
|
+
* @param {HTMLButtonElement} event.detail.element
|
|
11
|
+
* @return {void}
|
|
12
|
+
*/
|
|
13
|
+
insertbutton({ target, detail: { element } }) {
|
|
14
|
+
if (element.getAttribute('data-command')) {
|
|
15
|
+
element.addEventListener('click', this);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
element.tabIndex = element === target.firstElementChild ? 0 : -1;
|
|
19
|
+
element.addEventListener('keydown', this);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {MouseEvent} event
|
|
24
|
+
* @param {HTMLElement} event.target
|
|
25
|
+
* @return {void}
|
|
26
|
+
*/
|
|
27
|
+
click({ target }) {
|
|
28
|
+
this.editor.commands.execute(target.getAttribute('data-command'));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {KeyboardEvent} event
|
|
33
|
+
* @param {HTMLElement} event.target
|
|
34
|
+
* @return {void}
|
|
35
|
+
*/
|
|
36
|
+
keydown(event) {
|
|
37
|
+
if (Key.isEventFor(event, [Key.ARROWLEFT, Key.ARROWRIGHT, Key.HOME, Key.END])) {
|
|
38
|
+
const prev = event.target.previousElementSibling;
|
|
39
|
+
const next = event.target.nextElementSibling;
|
|
40
|
+
const first = event.target.parentElement.firstElementChild;
|
|
41
|
+
const last = event.target.parentElement.lastElementChild;
|
|
42
|
+
const isFirst = event.target === first;
|
|
43
|
+
const isLast = event.target === last;
|
|
44
|
+
|
|
45
|
+
if (event.key === Key.ARROWLEFT && !isFirst) {
|
|
46
|
+
prev.focus();
|
|
47
|
+
} else if (event.key === Key.ARROWRIGHT && !isLast) {
|
|
48
|
+
next.focus();
|
|
49
|
+
} else if (event.key === Key.HOME || (event.key === Key.ARROWRIGHT && isLast)) {
|
|
50
|
+
first.focus();
|
|
51
|
+
} else if (event.key === Key.END || (event.key === Key.ARROWLEFT && isFirst)) {
|
|
52
|
+
last.focus();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
event.stopPropagation();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @protected
|
|
62
|
+
* @param {HTMLElement} toolbar
|
|
63
|
+
* @param {HTMLElement} element
|
|
64
|
+
* @return {void}
|
|
65
|
+
*/
|
|
66
|
+
_show(toolbar, element) {
|
|
67
|
+
if (!(toolbar instanceof HTMLElement) || !(element instanceof HTMLElement)) {
|
|
68
|
+
throw new TypeError('Invalid argument');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
toolbar.hidden = false;
|
|
72
|
+
const { x, y } = element.getBoundingClientRect();
|
|
73
|
+
const { x: rx, y: ry } = this.editor.root.getBoundingClientRect();
|
|
74
|
+
const diff = x - rx + toolbar.clientWidth - this.editor.root.clientWidth;
|
|
75
|
+
const top = y - ry - element.scrollTop + this.editor.root.offsetTop - toolbar.clientHeight;
|
|
76
|
+
let left = toolbar.clientWidth < this.editor.root.clientWidth ? x - rx : 0;
|
|
77
|
+
|
|
78
|
+
if (left > 0 && diff > 0) {
|
|
79
|
+
left = left > diff ? (left - diff) / 2 : 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
toolbar.style.left = `${left}px`;
|
|
83
|
+
toolbar.style.top = `${top}px`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @protected
|
|
88
|
+
* @param {HTMLElement} toolbar
|
|
89
|
+
* @return {void}
|
|
90
|
+
*/
|
|
91
|
+
_hide(toolbar) {
|
|
92
|
+
toolbar.hidden = true;
|
|
93
|
+
toolbar.removeAttribute('style');
|
|
94
|
+
}
|
|
95
|
+
}
|
package/base/Base.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import AlignCommand from './AlignCommand.js';
|
|
2
|
+
import AlignableListener from './AlignableListener.js';
|
|
3
|
+
import Alignment from './Alignment.js';
|
|
4
|
+
import ContentFilter from './ContentFilter.js';
|
|
5
|
+
import DeleteCommand from './DeleteCommand.js';
|
|
6
|
+
import DeletableListener from './DeletableListener.js';
|
|
7
|
+
import EditableListener from './EditableListener.js';
|
|
8
|
+
import FocusableListener from './FocusableListener.js';
|
|
9
|
+
import FocusbarListener from './FocusbarListener.js';
|
|
10
|
+
import FormatbarListener from './FormatbarListener.js';
|
|
11
|
+
import NavigableListener from './NavigableListener.js';
|
|
12
|
+
import Plugin from './Plugin.js';
|
|
13
|
+
import SlotableListener from './SlotableListener.js';
|
|
14
|
+
import SortCommand from './SortCommand.js';
|
|
15
|
+
import SortableListener from './SortableListener.js';
|
|
16
|
+
import Sorting from './Sorting.js';
|
|
17
|
+
import TagGroup from './TagGroup.js';
|
|
18
|
+
import TagListener from './TagListener.js';
|
|
19
|
+
import ToolbarListener from './ToolbarListener.js';
|
|
20
|
+
import TagName from './TagName.js';
|
|
21
|
+
|
|
22
|
+
export default class Base extends Plugin {
|
|
23
|
+
/**
|
|
24
|
+
* @type {string}
|
|
25
|
+
*/
|
|
26
|
+
static get name() {
|
|
27
|
+
return 'base';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @type {Object.<string, any>}
|
|
32
|
+
*/
|
|
33
|
+
static get config() {
|
|
34
|
+
return { browser: {}, filter: {}, lang: undefined, plugins: [], pluginsDisabled: false };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @return {void}
|
|
39
|
+
*/
|
|
40
|
+
init() {
|
|
41
|
+
this._tag({
|
|
42
|
+
name: TagName.ROOT,
|
|
43
|
+
group: TagGroup.ROOT,
|
|
44
|
+
children: [
|
|
45
|
+
TagGroup.AUDIO,
|
|
46
|
+
TagGroup.BLOCK,
|
|
47
|
+
TagGroup.CONTAINER,
|
|
48
|
+
TagGroup.FIGURE,
|
|
49
|
+
TagGroup.HEADING,
|
|
50
|
+
TagGroup.IFRAME,
|
|
51
|
+
TagGroup.IMAGE,
|
|
52
|
+
TagGroup.LIST,
|
|
53
|
+
TagGroup.PARAGRAPH,
|
|
54
|
+
TagGroup.PREFORMAT,
|
|
55
|
+
TagGroup.QUOTE,
|
|
56
|
+
TagGroup.RULE,
|
|
57
|
+
TagGroup.TABLE,
|
|
58
|
+
TagGroup.VIDEO,
|
|
59
|
+
],
|
|
60
|
+
});
|
|
61
|
+
this._tag({
|
|
62
|
+
name: TagName.SLOT,
|
|
63
|
+
group: TagGroup.SLOT,
|
|
64
|
+
editable: true,
|
|
65
|
+
focusable: true,
|
|
66
|
+
navigable: true,
|
|
67
|
+
sortable: true,
|
|
68
|
+
});
|
|
69
|
+
new TagListener(this.editor);
|
|
70
|
+
new ToolbarListener(this.editor);
|
|
71
|
+
new FormatbarListener(this.editor);
|
|
72
|
+
new FocusbarListener(this.editor);
|
|
73
|
+
new EditableListener(this.editor);
|
|
74
|
+
new DeletableListener(this.editor);
|
|
75
|
+
new NavigableListener(this.editor);
|
|
76
|
+
new SortableListener(this.editor);
|
|
77
|
+
new AlignableListener(this.editor);
|
|
78
|
+
new FocusableListener(this.editor);
|
|
79
|
+
new SlotableListener(this.editor);
|
|
80
|
+
this.editor.filters.add(new ContentFilter(this.editor));
|
|
81
|
+
this.#initAlign();
|
|
82
|
+
this.#initSort();
|
|
83
|
+
this.#initDelete();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @return {void}
|
|
88
|
+
*/
|
|
89
|
+
#initAlign() {
|
|
90
|
+
const alignments = {
|
|
91
|
+
[Alignment.NONE]: 'No alignment',
|
|
92
|
+
[Alignment.LEFT]: 'Align left',
|
|
93
|
+
[Alignment.CENTER]: 'Align center',
|
|
94
|
+
[Alignment.RIGHT]: 'Align right',
|
|
95
|
+
};
|
|
96
|
+
Object.entries(alignments).forEach(([alignment, label]) => {
|
|
97
|
+
const command = new AlignCommand(this.editor, alignment);
|
|
98
|
+
this.editor.commands.set(command);
|
|
99
|
+
this._focusbar(label, command.name);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @return {void}
|
|
105
|
+
*/
|
|
106
|
+
#initSort() {
|
|
107
|
+
const sortings = {
|
|
108
|
+
[Sorting.FIRST]: 'Sort to the beginning',
|
|
109
|
+
[Sorting.PREV]: 'Sort before previous element',
|
|
110
|
+
[Sorting.NEXT]: 'Sort after next element',
|
|
111
|
+
[Sorting.LAST]: 'Sort to the end',
|
|
112
|
+
};
|
|
113
|
+
Object.entries(sortings).forEach(([sorting, label]) => {
|
|
114
|
+
const command = new SortCommand(this.editor, sorting);
|
|
115
|
+
this.editor.commands.set(command);
|
|
116
|
+
this._focusbar(label, command.name);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @return {void}
|
|
122
|
+
*/
|
|
123
|
+
#initDelete() {
|
|
124
|
+
this.editor.commands.set(new DeleteCommand(this.editor));
|
|
125
|
+
this._focusbar('Delete', 'delete');
|
|
126
|
+
}
|
|
127
|
+
}
|
package/base/Command.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import Editor from './Editor.js';
|
|
2
|
+
import TagGroup from './TagGroup.js';
|
|
3
|
+
import { isOptString, isString } from './util.js';
|
|
4
|
+
|
|
5
|
+
export default class Command {
|
|
6
|
+
/**
|
|
7
|
+
* @type {Editor}
|
|
8
|
+
*/
|
|
9
|
+
#editor;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @return {Editor}
|
|
13
|
+
*/
|
|
14
|
+
get editor() {
|
|
15
|
+
return this.#editor;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @type {string}
|
|
20
|
+
*/
|
|
21
|
+
#name;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @return {string}
|
|
25
|
+
*/
|
|
26
|
+
get name() {
|
|
27
|
+
return this.#name;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @type {Tag|undefined}
|
|
32
|
+
*/
|
|
33
|
+
#tag;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @return {Tag|undefined}
|
|
37
|
+
*/
|
|
38
|
+
get tag() {
|
|
39
|
+
return this.#tag;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @type {Dialog|undefined}
|
|
44
|
+
*/
|
|
45
|
+
#dialog;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @return {Dialog|undefined}
|
|
49
|
+
*/
|
|
50
|
+
get dialog() {
|
|
51
|
+
return this.#dialog;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Editor} editor
|
|
56
|
+
* @param {string} name
|
|
57
|
+
* @param {string|undefined} [tagName = undefined]
|
|
58
|
+
*/
|
|
59
|
+
constructor(editor, name, tagName = undefined) {
|
|
60
|
+
if (!(editor instanceof Editor) || !isString(name) || !isOptString(tagName)) {
|
|
61
|
+
throw new TypeError('Invalid argument');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.#editor = editor;
|
|
65
|
+
this.#name = name;
|
|
66
|
+
this.#tag = tagName && this.editor.tags.get(tagName);
|
|
67
|
+
this.#dialog = this.editor.dialogs.get(name);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @return {void}
|
|
72
|
+
*/
|
|
73
|
+
execute() {
|
|
74
|
+
this.dialog ? this._openDialog() : this._insert(this.#selectedAttributes());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @protected
|
|
79
|
+
* @param {Object.<string, string>} [attributes = {}]
|
|
80
|
+
* @return {void}
|
|
81
|
+
*/
|
|
82
|
+
_insert(attributes = {}) {
|
|
83
|
+
if (!this.tag) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.#filterAttributes(attributes);
|
|
88
|
+
const element = this.editor.dom.getSelectedElementByName(this.tag.name);
|
|
89
|
+
|
|
90
|
+
if (this.tag.group !== TagGroup.FORMAT) {
|
|
91
|
+
this.editor.dom.insert(this.editor.dom.createElement(this.tag.name, { attributes }));
|
|
92
|
+
} else if (element && Object.keys(attributes).length > 0) {
|
|
93
|
+
element.parentElement.replaceChild(
|
|
94
|
+
this.editor.dom.createElement(this.tag.name, { attributes, html: element.textContent }),
|
|
95
|
+
element
|
|
96
|
+
);
|
|
97
|
+
} else if (element) {
|
|
98
|
+
element.parentElement.replaceChild(this.editor.dom.createText(element.textContent), element);
|
|
99
|
+
} else {
|
|
100
|
+
this.editor.dom.format(this.editor.dom.createElement(this.tag.name, { attributes }));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @protected
|
|
106
|
+
* @return {void}
|
|
107
|
+
*/
|
|
108
|
+
_openDialog() {
|
|
109
|
+
this.dialog?.open((attributes) => this._insert(attributes), this.#selectedAttributes());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns attributes from selected element if its tag name matches configured tag name
|
|
114
|
+
*
|
|
115
|
+
* @return {Object.<string, string>}
|
|
116
|
+
*/
|
|
117
|
+
#selectedAttributes() {
|
|
118
|
+
if (!this.tag) {
|
|
119
|
+
return {};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const attributes = this.editor.dom.getSelectedAttributesByName(this.tag.name);
|
|
123
|
+
this.#filterAttributes(attributes);
|
|
124
|
+
|
|
125
|
+
return attributes;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Filters allowed attributes
|
|
130
|
+
*
|
|
131
|
+
* @param {Object.<string, string>} attributes
|
|
132
|
+
* @return {void}
|
|
133
|
+
*/
|
|
134
|
+
#filterAttributes(attributes) {
|
|
135
|
+
Object.keys(attributes).forEach(
|
|
136
|
+
(item) => (this.tag.attributes.includes(item) && attributes[item]) || delete attributes[item]
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|