@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.
Files changed (126) hide show
  1. package/abbreviation/Abbreviation.js +32 -0
  2. package/abbreviation/AbbreviationDialog.js +21 -0
  3. package/audio/Audio.js +47 -0
  4. package/audio/AudioDialog.js +18 -0
  5. package/audio/AudioListener.js +50 -0
  6. package/base/AlignCommand.js +34 -0
  7. package/base/AlignableListener.js +45 -0
  8. package/base/Alignment.js +36 -0
  9. package/base/BarListener.js +95 -0
  10. package/base/Base.js +127 -0
  11. package/base/Command.js +139 -0
  12. package/base/CommandManager.js +60 -0
  13. package/base/ContentFilter.js +109 -0
  14. package/base/DeletableListener.js +36 -0
  15. package/base/DeleteCommand.js +18 -0
  16. package/base/Dialog.js +153 -0
  17. package/base/DialogManager.js +44 -0
  18. package/base/Dispatcher.js +88 -0
  19. package/base/Dom.js +790 -0
  20. package/base/EditableListener.js +82 -0
  21. package/base/Editor.js +448 -0
  22. package/base/Filter.js +35 -0
  23. package/base/FilterManager.js +44 -0
  24. package/base/FocusableListener.js +22 -0
  25. package/base/FocusbarListener.js +99 -0
  26. package/base/FormCreator.js +162 -0
  27. package/base/FormatbarListener.js +32 -0
  28. package/base/Key.js +258 -0
  29. package/base/Listener.js +51 -0
  30. package/base/NavigableListener.js +81 -0
  31. package/base/Plugin.js +176 -0
  32. package/base/PluginManager.js +51 -0
  33. package/base/SlotableListener.js +40 -0
  34. package/base/SortCommand.js +30 -0
  35. package/base/SortableListener.js +135 -0
  36. package/base/Sorting.js +36 -0
  37. package/base/Tag.js +113 -0
  38. package/base/TagGroup.js +183 -0
  39. package/base/TagListener.js +34 -0
  40. package/base/TagManager.js +61 -0
  41. package/base/TagName.js +470 -0
  42. package/base/ToolbarListener.js +11 -0
  43. package/base/util.js +59 -0
  44. package/block/Block.js +51 -0
  45. package/block/BlockDialog.js +11 -0
  46. package/block/BlockElement.js +21 -0
  47. package/block/BlockListener.js +60 -0
  48. package/blockquote/Blockquote.js +43 -0
  49. package/blockquote/BlockquoteFilter.js +22 -0
  50. package/blockquote/BlockquoteListener.js +34 -0
  51. package/bold/Bold.js +30 -0
  52. package/break/Break.js +33 -0
  53. package/break/BreakFilter.js +24 -0
  54. package/build/BuildEditor.js +97 -0
  55. package/build/editor.css +548 -0
  56. package/build/editor.woff2 +0 -0
  57. package/cite/Cite.js +30 -0
  58. package/code/Code.js +30 -0
  59. package/data/Data.js +32 -0
  60. package/data/DataDialog.js +13 -0
  61. package/definition/Definition.js +32 -0
  62. package/definition/DefinitionDialog.js +13 -0
  63. package/deletion/Deletion.js +30 -0
  64. package/details/Details.js +63 -0
  65. package/details/DetailsFilter.js +17 -0
  66. package/details/DetailsListener.js +102 -0
  67. package/division/Division.js +53 -0
  68. package/division/DivisionDialog.js +13 -0
  69. package/emphasis/Emphasis.js +30 -0
  70. package/figure/Figure.js +58 -0
  71. package/figure/FigureFilter.js +14 -0
  72. package/figure/FigureListener.js +23 -0
  73. package/heading/Heading.js +38 -0
  74. package/horizontalrule/HorizontalRule.js +37 -0
  75. package/i18n/I18n.js +26 -0
  76. package/i18n/de.js +167 -0
  77. package/iframe/Iframe.js +49 -0
  78. package/iframe/IframeDialog.js +20 -0
  79. package/iframe/IframeListener.js +48 -0
  80. package/image/Image.js +47 -0
  81. package/image/ImageDialog.js +23 -0
  82. package/image/ImageListener.js +47 -0
  83. package/insertion/Insertion.js +30 -0
  84. package/italic/Italic.js +30 -0
  85. package/keyboard/Keyboard.js +30 -0
  86. package/link/Link.js +34 -0
  87. package/link/LinkDialog.js +14 -0
  88. package/link/LinkListener.js +45 -0
  89. package/list/List.js +40 -0
  90. package/list/ListListener.js +91 -0
  91. package/mark/Mark.js +30 -0
  92. package/orderedlist/OrderedList.js +39 -0
  93. package/package.json +24 -0
  94. package/paragraph/Paragraph.js +42 -0
  95. package/paragraph/ParagraphListener.js +40 -0
  96. package/preformat/Preformat.js +43 -0
  97. package/preformat/PreformatFilter.js +22 -0
  98. package/preformat/PreformatListener.js +34 -0
  99. package/quote/Quote.js +30 -0
  100. package/sample/Sample.js +30 -0
  101. package/section/Section.js +55 -0
  102. package/section/SectionDialog.js +13 -0
  103. package/small/Small.js +30 -0
  104. package/strikethrough/Strikethrough.js +30 -0
  105. package/strong/Strong.js +30 -0
  106. package/subheading/Subheading.js +38 -0
  107. package/subscript/Subscript.js +30 -0
  108. package/superscript/Superscript.js +30 -0
  109. package/table/Table.js +113 -0
  110. package/table/TableCellListener.js +125 -0
  111. package/table/TableColumnAddCommand.js +19 -0
  112. package/table/TableColumnListener.js +34 -0
  113. package/table/TableCommand.js +23 -0
  114. package/table/TableDialog.js +14 -0
  115. package/table/TableFilter.js +35 -0
  116. package/table/TableListener.js +39 -0
  117. package/table/TableRowAddCommand.js +18 -0
  118. package/table/TableRowListener.js +34 -0
  119. package/time/Time.js +32 -0
  120. package/time/TimeDialog.js +13 -0
  121. package/underline/Underline.js +30 -0
  122. package/unorderedlist/UnorderedList.js +39 -0
  123. package/variable/Variable.js +30 -0
  124. package/video/Video.js +47 -0
  125. package/video/VideoDialog.js +20 -0
  126. package/video/VideoListener.js +48 -0
@@ -0,0 +1,60 @@
1
+ import Command from './Command.js';
2
+
3
+ export default class CommandManager {
4
+ /**
5
+ * @type {Map<string, Command>}
6
+ */
7
+ #items = new Map();
8
+
9
+ /**
10
+ * @param {Command[]} [commands = []]
11
+ */
12
+ constructor(commands = []) {
13
+ commands.forEach((command) => this.set(command));
14
+ }
15
+
16
+ /**
17
+ * @param {string} name
18
+ * @return {Command|undefined}
19
+ */
20
+ get(name) {
21
+ return this.#items.get(name);
22
+ }
23
+
24
+ /**
25
+ * @param {Command} command
26
+ * @return {void}
27
+ */
28
+ set(command) {
29
+ if (!(command instanceof Command)) {
30
+ throw new TypeError('Invalid argument');
31
+ }
32
+
33
+ this.#items.set(command.name, command);
34
+ }
35
+
36
+ /**
37
+ * @param {string} tagName
38
+ * @return {Command|undefined}
39
+ */
40
+ findByTagName(tagName) {
41
+ return Array.from(this.#items.values()).find((command) => command.tag?.name === tagName);
42
+ }
43
+
44
+ /**
45
+ * @param {string} name
46
+ * @return {void}
47
+ */
48
+ execute(name) {
49
+ this.get(name).execute();
50
+ }
51
+
52
+ /**
53
+ * @return {void}
54
+ */
55
+ freeze() {
56
+ this.#items.forEach((command) => Object.freeze(command));
57
+ Object.freeze(this.#items);
58
+ Object.freeze(this);
59
+ }
60
+ }
@@ -0,0 +1,109 @@
1
+ import Filter from './Filter.js';
2
+ import TagGroup from './TagGroup.js';
3
+ import TagName from './TagName.js';
4
+
5
+ export default class ContentFilter extends Filter {
6
+ /**
7
+ * @param {HTMLElement} element
8
+ * @return {void}
9
+ */
10
+ filter(element) {
11
+ const tag = this.editor.tags.get(element);
12
+ const allowedParagraph = this.editor.tags.allowed(element, TagName.P);
13
+ const allowedText = tag.editable || tag.group === TagGroup.FORMAT;
14
+ let p = [];
15
+ const wrap = (ref = null) => {
16
+ if (allowedParagraph && p.length > 0) {
17
+ const newP = this.editor.dom.createElement(TagName.P, { html: p.join(' ') });
18
+
19
+ if (ref) {
20
+ this.editor.dom.insertBefore(newP, ref);
21
+ } else {
22
+ this.editor.dom.insertLastChild(newP, element);
23
+ }
24
+
25
+ p = [];
26
+ }
27
+ };
28
+
29
+ Array.from(element.childNodes).forEach((child) => {
30
+ const text = child.textContent.trim();
31
+
32
+ if (child instanceof HTMLElement) {
33
+ const realChild = this.#convert(child);
34
+ const childTag = this.editor.tags.get(realChild);
35
+
36
+ if (childTag && this.editor.tags.allowed(element, realChild)) {
37
+ wrap(realChild);
38
+ this.#filter(realChild, childTag);
39
+ } else if (childTag && childTag.group === TagGroup.FORMAT && allowedParagraph) {
40
+ const filteredChild = this.#filter(realChild, childTag);
41
+
42
+ if (filteredChild) {
43
+ p.push(filteredChild.outerHTML);
44
+ element.removeChild(filteredChild);
45
+ }
46
+ } else if (!allowedText && text && allowedParagraph) {
47
+ p.push(text);
48
+ element.removeChild(realChild);
49
+ } else if (allowedText && text) {
50
+ element.replaceChild(this.editor.dom.createText(text), realChild);
51
+ } else {
52
+ element.removeChild(realChild);
53
+ }
54
+ } else if (child instanceof Text) {
55
+ if (!allowedText && text && allowedParagraph) {
56
+ p.push(text);
57
+ }
58
+
59
+ if (!allowedText || !text) {
60
+ element.removeChild(child);
61
+ }
62
+ } else if (child instanceof Node) {
63
+ element.removeChild(child);
64
+ }
65
+ });
66
+
67
+ wrap();
68
+ }
69
+
70
+ /**
71
+ * @param {HTMLElement} element
72
+ * @return {HTMLElement}
73
+ */
74
+ #convert(element) {
75
+ const name = this.editor.config.base.filter[element.localName];
76
+
77
+ if (!name) {
78
+ return element;
79
+ }
80
+
81
+ const convert = this.editor.dom.createElement(name, { html: element.innerHTML });
82
+ element.parentElement.replaceChild(convert, element);
83
+
84
+ return convert;
85
+ }
86
+
87
+ /**
88
+ * @param {HTMLElement} element
89
+ * @param {Tag} tag
90
+ * @return {HTMLElement|undefined}
91
+ */
92
+ #filter(element, tag) {
93
+ Array.from(element.attributes).forEach(
94
+ (item) => !tag.attributes.includes(item.name) && element.removeAttribute(item.name)
95
+ );
96
+
97
+ if (element.hasChildNodes()) {
98
+ this.editor.filters.filter(element);
99
+ }
100
+
101
+ if (element.hasChildNodes() || tag.empty) {
102
+ return element;
103
+ }
104
+
105
+ element.parentElement?.removeChild(element);
106
+
107
+ return undefined;
108
+ }
109
+ }
@@ -0,0 +1,36 @@
1
+ import Key from './Key.js';
2
+ import Listener from './Listener.js';
3
+
4
+ export default class DeletableListener extends Listener {
5
+ /**
6
+ * @param {Editor} editor
7
+ */
8
+ constructor(editor) {
9
+ super(editor);
10
+ this.editor.root.addEventListener('insert', this);
11
+ }
12
+
13
+ /**
14
+ * @param {CustomEvent} event
15
+ * @param {HTMLElement} event.detail.element
16
+ * @return {void}
17
+ */
18
+ insert({ detail: { element } }) {
19
+ if (element.hasAttribute('data-deletable')) {
20
+ element.addEventListener('keydown', this);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * @param {KeyboardEvent} event
26
+ * @param {HTMLElement} event.target
27
+ * @return {void}
28
+ */
29
+ keydown(event) {
30
+ if (event.target === event.currentTarget && Key.isEventFor(event, Key.DEL, { ctrl: true })) {
31
+ this.editor.dom.delete(event.target);
32
+ event.preventDefault();
33
+ event.stopPropagation();
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,18 @@
1
+ import Command from './Command.js';
2
+
3
+ export default class DeleteCommand extends Command {
4
+ /**
5
+ * @param {Editor} editor
6
+ */
7
+ constructor(editor) {
8
+ super(editor, 'delete');
9
+ }
10
+
11
+ /**
12
+ * @return {void}
13
+ */
14
+ execute() {
15
+ const element = this.editor.dom.getActiveElement();
16
+ element?.hasAttribute('data-deletable') && this.editor.dom.delete(element);
17
+ }
18
+ }
package/base/Dialog.js ADDED
@@ -0,0 +1,153 @@
1
+ import Editor from './Editor.js';
2
+ import FormCreator from './FormCreator.js';
3
+ import TagName from './TagName.js';
4
+ import { is, isOptString, isString } from './util.js';
5
+
6
+ export default class Dialog {
7
+ /**
8
+ * @type {Editor}
9
+ */
10
+ #editor;
11
+
12
+ /**
13
+ * @return {Editor}
14
+ */
15
+ get editor() {
16
+ return this.#editor;
17
+ }
18
+
19
+ /**
20
+ * @type {string}
21
+ */
22
+ #name;
23
+
24
+ /**
25
+ * @return {string}
26
+ */
27
+ get name() {
28
+ return this.#name;
29
+ }
30
+
31
+ /**
32
+ * @type {string|undefined}
33
+ */
34
+ #browserUrl;
35
+
36
+ /**
37
+ * @return {string|undefined}
38
+ */
39
+ get browserUrl() {
40
+ return this.#browserUrl;
41
+ }
42
+
43
+ /**
44
+ * @type {FormCreator|undefined}
45
+ */
46
+ #formCreator;
47
+
48
+ /**
49
+ * @type {FormCreator|undefined}
50
+ */
51
+ get formCreator() {
52
+ return this.#formCreator;
53
+ }
54
+
55
+ /**
56
+ * @param {Editor} editor
57
+ * @param {string} name
58
+ * @param {string|undefined} browserUrl
59
+ */
60
+ constructor(editor, name, browserUrl = undefined) {
61
+ if (!(editor instanceof Editor) || !isString(name) || !isOptString(browserUrl)) {
62
+ throw new TypeError('Invalid argument');
63
+ }
64
+
65
+ this.#editor = editor;
66
+ this.#name = name;
67
+ this.#browserUrl = browserUrl;
68
+ }
69
+
70
+ /**
71
+ * Opens a dialog or a browser window if browser URL is set
72
+ *
73
+ * @param {function} save
74
+ * @param {Object} [attributes = {}]
75
+ * @return {void}
76
+ */
77
+ open(save, attributes = {}) {
78
+ this.browserUrl ? this.#openBrowser(save, attributes) : this.#openDialog(save, attributes);
79
+ }
80
+
81
+ /**
82
+ * Translates given string
83
+ *
84
+ * @protected
85
+ * @param {string} key
86
+ * @return {string}
87
+ */
88
+ _(key) {
89
+ return this.editor.translate(key);
90
+ }
91
+
92
+ /**
93
+ * @protected
94
+ * @return {void}
95
+ */
96
+ _prepareForm() {
97
+ throw new Error('Not implemented');
98
+ }
99
+
100
+ /**
101
+ * Opens a browser window and registers a listener for communication between editor and browser windows
102
+ *
103
+ * @param {function} save
104
+ * @param {Object} [attributes = {}]
105
+ * @return {void}
106
+ */
107
+ #openBrowser(save, attributes = {}) {
108
+ this.editor.dom.open({ url: this.browserUrl, name: this.name, call: save, params: attributes });
109
+ }
110
+
111
+ /**
112
+ * Opens a dialog and executes given callback on save
113
+ *
114
+ * @param {function} save
115
+ * @param {Object} [attributes = {}]
116
+ * @return {void}
117
+ */
118
+ #openDialog(save, attributes = {}) {
119
+ const range = this.editor.dom.getRange();
120
+ this.#cleanup();
121
+
122
+ /** @type {HTMLDialogElement} */
123
+ const dialog = this.editor.dom.createElement(TagName.DIALOG);
124
+ dialog.addEventListener('click', (event) => event.target === dialog && dialog.close());
125
+ dialog.addEventListener('close', () => {
126
+ range && this.editor.dom.setRange(range);
127
+ dialog.returnValue && save(JSON.parse(dialog.returnValue));
128
+ this.#cleanup();
129
+ });
130
+ this.editor.dom.insertLastChild(dialog, this.editor.element);
131
+
132
+ this.#formCreator = new FormCreator(this.editor);
133
+ this._prepareForm();
134
+ const form = this.formCreator.form;
135
+ form.addEventListener('submit', () => dialog.close(JSON.stringify(Object.fromEntries(new FormData(form)))));
136
+ form.addEventListener('reset', () => dialog.close());
137
+ new FormData(form).forEach((val, key) => is(attributes[key]) && (form.elements[key].value = attributes[key]));
138
+ this.editor.dom.insertLastChild(form, dialog);
139
+
140
+ dialog.showModal();
141
+ }
142
+
143
+ /**
144
+ * Removes all existing editor dialogs
145
+ *
146
+ * @return {void}
147
+ */
148
+ #cleanup() {
149
+ Array.from(this.editor.element.getElementsByTagName(TagName.DIALOG)).forEach((item) => {
150
+ item.parentElement.removeChild(item);
151
+ });
152
+ }
153
+ }
@@ -0,0 +1,44 @@
1
+ import Dialog from './Dialog.js';
2
+
3
+ export default class DialogManager {
4
+ /**
5
+ * @type {Map<string, Dialog>}
6
+ */
7
+ #items = new Map();
8
+
9
+ /**
10
+ * @param {Dialog[]} [dialogs = []]
11
+ */
12
+ constructor(dialogs = []) {
13
+ dialogs.forEach((dialog) => this.set(dialog));
14
+ }
15
+
16
+ /**
17
+ * @param {string} name
18
+ * @return {Dialog|undefined}
19
+ */
20
+ get(name) {
21
+ return this.#items.get(name);
22
+ }
23
+
24
+ /**
25
+ * @param {Dialog} dialog
26
+ * @return {void}
27
+ */
28
+ set(dialog) {
29
+ if (!(dialog instanceof Dialog)) {
30
+ throw new TypeError('Invalid argument');
31
+ }
32
+
33
+ this.#items.set(dialog.name, dialog);
34
+ }
35
+
36
+ /**
37
+ * @return {void}
38
+ */
39
+ freeze() {
40
+ this.#items.forEach((dialog) => Object.freeze(dialog));
41
+ Object.freeze(this.#items);
42
+ Object.freeze(this);
43
+ }
44
+ }
@@ -0,0 +1,88 @@
1
+ import TagName from './TagName.js';
2
+ import { isFunction, isOptHtml, isString } from './util.js';
3
+
4
+ export default class Dispatcher {
5
+ /**
6
+ * @type {HTMLElement}
7
+ */
8
+ #element;
9
+
10
+ /**
11
+ * @param {HTMLElement} element
12
+ */
13
+ constructor(element) {
14
+ if (!(element instanceof HTMLElement)) {
15
+ throw new TypeError('Invalid argument');
16
+ }
17
+
18
+ this.#element = element;
19
+ this.register(this.#observe.bind(this));
20
+ }
21
+
22
+ /**
23
+ * @param {string} type
24
+ * @param {HTMLElement|undefined} [element = undefined]
25
+ * @param {HTMLElement|undefined} [target = undefined]
26
+ * @return {void}
27
+ */
28
+ dispatch(type, element = undefined, target = undefined) {
29
+ if (!isString(type) || !isOptHtml(element) || !isOptHtml(target)) {
30
+ throw new TypeError('Invalid argument');
31
+ }
32
+
33
+ this.#element.dispatchEvent(new CustomEvent(type, { detail: { element, target } }));
34
+ }
35
+
36
+ /**
37
+ * Registers a mutation observer on managed element
38
+ *
39
+ * @param {function} call
40
+ * @param {MutationObserverInit} [opts = {childList: true, subtree: true}]
41
+ * @return {void}
42
+ */
43
+ register(call, opts = { childList: true, subtree: true }) {
44
+ if (!isFunction(call)) {
45
+ throw new TypeError('Invalid argument');
46
+ }
47
+
48
+ const observer = new MutationObserver(call);
49
+ observer.observe(this.#element, opts);
50
+ }
51
+
52
+ /**
53
+ * Dispatches a mutation event on managed element
54
+ *
55
+ * @param {string} type
56
+ * @param {Element} element
57
+ * @param {Node} target
58
+ * @return {void}
59
+ */
60
+ #dispatch(type, element, target) {
61
+ if (element instanceof HTMLElement && target instanceof HTMLElement) {
62
+ this.dispatch(type, element, target);
63
+ this.dispatch(`${type}${element.localName.replace('-', '')}`, element, target);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Observes mutations on managed element
69
+ *
70
+ * @param {MutationRecord[]} records
71
+ * @return {void}
72
+ */
73
+ #observe(records) {
74
+ records.forEach((record) => {
75
+ record.addedNodes.forEach((element) => {
76
+ if (element instanceof HTMLElement) {
77
+ this.#dispatch('insert', element, record.target);
78
+ Array.from(element.getElementsByTagName(TagName.ALL)).forEach((item) =>
79
+ this.#dispatch('insert', item, record.target)
80
+ );
81
+ }
82
+ });
83
+ record.removedNodes.forEach(
84
+ (element) => element instanceof HTMLElement && this.#dispatch('delete', element, record.target)
85
+ );
86
+ });
87
+ }
88
+ }