jodit 3.23.3 → 3.24.2

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 (175) hide show
  1. package/README.md +4 -4
  2. package/build/jodit.css +8 -3
  3. package/build/jodit.es2018.css +7 -2
  4. package/build/jodit.es2018.en.css +7 -2
  5. package/build/jodit.es2018.en.js +8384 -7869
  6. package/build/jodit.es2018.en.min.css +1 -1
  7. package/build/jodit.es2018.en.min.js +1 -1
  8. package/build/jodit.es2018.js +8384 -7869
  9. package/build/jodit.es2018.min.css +1 -1
  10. package/build/jodit.es2018.min.js +1 -1
  11. package/build/jodit.js +2954 -2414
  12. package/build/jodit.min.css +2 -2
  13. package/build/jodit.min.js +1 -1
  14. package/build/plugins/debug/debug.es2018.en.js +8 -8
  15. package/build/plugins/debug/debug.es2018.en.min.js +1 -1
  16. package/build/plugins/debug/debug.es2018.js +8 -8
  17. package/build/plugins/debug/debug.es2018.min.js +1 -1
  18. package/build/plugins/debug/debug.js +8 -8
  19. package/build/plugins/debug/debug.min.js +1 -1
  20. package/build/plugins/speech-recognize/speech-recognize.css +1 -1
  21. package/build/plugins/speech-recognize/speech-recognize.es2018.css +1 -1
  22. package/build/plugins/speech-recognize/speech-recognize.es2018.en.css +1 -1
  23. package/build/plugins/speech-recognize/speech-recognize.es2018.en.js +38 -38
  24. package/build/plugins/speech-recognize/speech-recognize.es2018.en.min.js +1 -1
  25. package/build/plugins/speech-recognize/speech-recognize.es2018.js +38 -38
  26. package/build/plugins/speech-recognize/speech-recognize.es2018.min.js +1 -1
  27. package/build/plugins/speech-recognize/speech-recognize.js +30 -30
  28. package/build/plugins/speech-recognize/speech-recognize.min.js +1 -1
  29. package/build/vdom.css +12 -1
  30. package/build/vdom.js +1445 -41
  31. package/package.json +23 -23
  32. package/src/core/constants.ts +10 -8
  33. package/src/core/dom/dom.test.js +25 -0
  34. package/src/core/dom/dom.ts +90 -41
  35. package/src/core/helpers/checker/has-browser-color-picker.ts +2 -2
  36. package/src/core/helpers/checker/index.ts +1 -0
  37. package/src/core/helpers/checker/is-boolean.ts +1 -1
  38. package/src/core/helpers/checker/is-marker.ts +20 -0
  39. package/src/core/helpers/normalize/normalize-css-value.ts +19 -0
  40. package/src/core/helpers/normalize/normalize-node.ts +2 -2
  41. package/src/core/helpers/size/position.test.js +198 -187
  42. package/src/core/helpers/utils/css.ts +7 -14
  43. package/src/core/plugin/plugin.ts +1 -1
  44. package/src/core/selection/interface.ts +24 -0
  45. package/src/core/selection/select.ts +69 -29
  46. package/src/core/selection/selection.test.js +5 -17
  47. package/src/core/selection/style/api/extract.ts +6 -7
  48. package/src/core/selection/style/api/finite-state-machine.ts +40 -35
  49. package/src/core/selection/style/api/get-suit-child.ts +3 -4
  50. package/src/core/selection/style/api/get-suit-parent.ts +3 -4
  51. package/src/core/selection/style/api/{element-has-same-style.ts → has-same-style.ts} +25 -6
  52. package/src/core/selection/style/api/index.ts +6 -6
  53. package/src/core/selection/style/api/is-inside-invisible-element.ts +1 -1
  54. package/src/core/selection/style/api/is-normal-node.ts +3 -3
  55. package/src/core/selection/style/api/is-same-attributes.ts +56 -0
  56. package/src/core/selection/style/api/is-suit-element.ts +22 -12
  57. package/src/core/selection/style/api/list/toggle-ordered-list.ts +100 -0
  58. package/src/core/selection/style/api/list/wrap-list.ts +71 -0
  59. package/src/core/selection/style/api/toggle-attributes.ts +251 -0
  60. package/src/core/selection/style/api/unwrap-children.ts +10 -8
  61. package/src/core/selection/style/api/wrap-unwrapped-text.ts +7 -8
  62. package/src/core/selection/style/api/{wrap-and-commit-style.ts → wrap.ts} +8 -11
  63. package/src/core/selection/style/apply-style.ts +32 -181
  64. package/src/core/selection/style/commit-style.ts +79 -4
  65. package/src/core/selection/style/style.test.js +457 -128
  66. package/src/core/selection/style/transactions.ts +256 -0
  67. package/src/core/ui/button/tooltip/tooltip.test.js +95 -76
  68. package/src/core/ui/popup/popup.test.js +158 -153
  69. package/src/{plugins/backspace/interface.d.ts → core/vdom/on-demand.ts} +7 -5
  70. package/src/core/vdom/render/index.ts +2 -4
  71. package/src/core/vdom/render/patcher.ts +14 -0
  72. package/src/core/vdom/v-dom-jodit.less +17 -0
  73. package/src/core/vdom/v-dom-jodit.ts +102 -3
  74. package/src/core/view/view.ts +2 -2
  75. package/src/index.ts +2 -0
  76. package/src/jodit.ts +5 -1
  77. package/src/langs/i18n.test.js +221 -216
  78. package/src/modules/dialog/dialog.less +9 -1
  79. package/src/modules/dialog/dialog.ts +25 -16
  80. package/src/modules/file-browser/data-provider.ts +44 -12
  81. package/src/modules/file-browser/file-browser.test.js +1019 -958
  82. package/src/modules/file-browser/file-browser.ts +0 -1
  83. package/src/modules/messages/messages.ts +3 -8
  84. package/src/plugins/backspace/backspace.test.js +2 -9
  85. package/src/plugins/backspace/backspace.ts +5 -0
  86. package/src/plugins/backspace/cases/check-join-neighbors.ts +7 -1
  87. package/src/plugins/backspace/cases/check-join-two-lists.ts +2 -1
  88. package/src/plugins/backspace/helpers.ts +9 -1
  89. package/src/plugins/backspace/interface.ts +31 -0
  90. package/src/plugins/clean-html/helpers/visitor/filters/fill-empty-paragraph.ts +2 -1
  91. package/src/plugins/color/color.test.js +122 -119
  92. package/src/plugins/enter/enter.test.js +18 -5
  93. package/src/plugins/enter/enter.ts +9 -5
  94. package/src/plugins/enter/interface.ts +41 -0
  95. package/src/plugins/font/config.ts +8 -8
  96. package/src/plugins/font/font.test.js +18 -23
  97. package/src/plugins/hotkeys/hotkeys.test.js +35 -47
  98. package/src/plugins/iframe/iframe.test.js +206 -195
  99. package/src/plugins/image/image.ts +1 -1
  100. package/src/plugins/image-properties/config.ts +22 -0
  101. package/src/plugins/image-properties/image-properties.test.js +174 -93
  102. package/src/plugins/image-properties/templates/position-tab.ts +22 -1
  103. package/src/plugins/indent/indent.test.js +2 -8
  104. package/src/plugins/link/link.test.js +19 -0
  105. package/src/plugins/link/link.ts +25 -15
  106. package/src/plugins/placeholder/placeholder.ts +8 -7
  107. package/src/plugins/size/size.test.js +239 -225
  108. package/src/plugins/tab/cases/on-tab-inside-li.ts +131 -22
  109. package/src/plugins/tab/tab.test.js +95 -11
  110. package/src/plugins/tab/tab.ts +22 -2
  111. package/src/plugins/wrap-nodes/config.ts +11 -0
  112. package/src/plugins/wrap-nodes/wrap-nodes.ts +0 -1
  113. package/src/types/events.d.ts +4 -0
  114. package/src/types/file-browser.d.ts +17 -10
  115. package/src/types/select.d.ts +6 -1
  116. package/src/types/style.d.ts +72 -5
  117. package/src/types/uploader.d.ts +14 -0
  118. package/src/types/view.d.ts +2 -2
  119. package/types/core/async/async.d.ts +1 -1
  120. package/types/core/constants.d.ts +3 -3
  121. package/types/core/dom/dom.d.ts +6 -3
  122. package/types/core/helpers/checker/index.d.ts +1 -0
  123. package/types/core/helpers/checker/is-boolean.d.ts +1 -1
  124. package/types/core/helpers/checker/is-marker.d.ts +10 -0
  125. package/types/core/helpers/html/safe-html.d.ts +1 -1
  126. package/types/core/helpers/normalize/normalize-css-value.d.ts +2 -0
  127. package/types/core/helpers/normalize/normalize-node.d.ts +1 -1
  128. package/types/core/helpers/utils/append-script.d.ts +1 -1
  129. package/types/core/selection/interface.d.ts +19 -0
  130. package/types/core/selection/select.d.ts +22 -5
  131. package/types/core/selection/style/api/finite-state-machine.d.ts +10 -9
  132. package/types/core/selection/style/api/get-suit-child.d.ts +2 -3
  133. package/types/core/selection/style/api/get-suit-parent.d.ts +2 -3
  134. package/types/core/selection/style/api/{element-has-same-style.d.ts → has-same-style.d.ts} +2 -2
  135. package/types/core/selection/style/api/index.d.ts +6 -6
  136. package/types/core/selection/style/api/is-same-attributes.d.ts +12 -0
  137. package/types/core/selection/style/api/is-suit-element.d.ts +4 -4
  138. package/types/core/selection/style/api/{toggle → list}/toggle-ordered-list.d.ts +2 -3
  139. package/types/core/selection/style/api/{wrap-ordered-list.d.ts → list/wrap-list.d.ts} +2 -3
  140. package/types/core/selection/style/api/toggle-attributes.d.ts +11 -0
  141. package/types/core/selection/style/api/unwrap-children.d.ts +2 -2
  142. package/types/core/selection/style/api/wrap-unwrapped-text.d.ts +2 -3
  143. package/types/core/selection/style/api/{wrap-and-commit-style.d.ts → wrap.d.ts} +2 -3
  144. package/types/core/selection/style/apply-style.d.ts +3 -3
  145. package/types/core/selection/style/commit-style.d.ts +6 -2
  146. package/types/core/selection/style/transactions.d.ts +29 -0
  147. package/types/core/ui/popup/popup.d.ts +1 -1
  148. package/types/core/view/view.d.ts +1 -1
  149. package/types/index.d.ts +2 -0
  150. package/types/modules/file-browser/builders/elements-map.d.ts +1 -1
  151. package/types/modules/file-browser/data-provider.d.ts +5 -0
  152. package/types/modules/messages/messages.d.ts +2 -1
  153. package/types/plugins/backspace/helpers.d.ts +5 -1
  154. package/types/plugins/backspace/interface.d.ts +21 -0
  155. package/types/plugins/enter/enter.d.ts +2 -0
  156. package/types/plugins/enter/interface.d.ts +32 -0
  157. package/types/plugins/image-properties/config.d.ts +19 -0
  158. package/types/plugins/link/link.d.ts +1 -1
  159. package/types/plugins/paste/interface.d.ts +2 -2
  160. package/types/plugins/tab/cases/on-tab-inside-li.d.ts +1 -1
  161. package/types/plugins/wrap-nodes/config.d.ts +10 -0
  162. package/types/types/events.d.ts +4 -0
  163. package/types/types/file-browser.d.ts +17 -10
  164. package/types/types/select.d.ts +6 -1
  165. package/types/types/storage.d.ts +1 -1
  166. package/types/types/style.d.ts +72 -5
  167. package/types/types/uploader.d.ts +14 -0
  168. package/types/types/view.d.ts +2 -2
  169. package/vdom.html +20 -0
  170. package/src/core/selection/style/api/toggle/toggle-css.ts +0 -136
  171. package/src/core/selection/style/api/toggle/toggle-ordered-list.ts +0 -54
  172. package/src/core/selection/style/api/toggle-commit-styles.ts +0 -35
  173. package/src/core/selection/style/api/wrap-ordered-list.ts +0 -42
  174. package/types/core/selection/style/api/toggle/toggle-css.d.ts +0 -12
  175. package/types/core/selection/style/api/toggle-commit-styles.d.ts +0 -13
@@ -17,6 +17,7 @@ import type {
17
17
  IJodit,
18
18
  ISelect,
19
19
  IStyle,
20
+ IStyleOptions,
20
21
  MarkerInfo,
21
22
  Nullable
22
23
  } from 'jodit/types';
@@ -32,10 +33,9 @@ import {
32
33
  import { Dom } from 'jodit/core/dom';
33
34
 
34
35
  import {
36
+ size,
35
37
  attr,
36
38
  error,
37
- isFunction,
38
- isString,
39
39
  $$,
40
40
  css,
41
41
  call,
@@ -45,6 +45,10 @@ import {
45
45
  import { CommitStyle } from './style/commit-style';
46
46
  import { autobind } from 'jodit/core/decorators';
47
47
  import { moveTheNodeAlongTheEdgeOutward } from 'jodit/core/selection/helpers';
48
+ import { assert } from 'jodit/core/helpers/utils/assert';
49
+ import { isMarker, isFunction, isString } from 'jodit/core/helpers/checker';
50
+
51
+ import './interface';
48
52
 
49
53
  export class Select implements ISelect {
50
54
  constructor(readonly jodit: IJodit) {
@@ -214,17 +218,6 @@ export class Select implements ISelect {
214
218
  return false;
215
219
  }
216
220
 
217
- /**
218
- * Define element is selection helper
219
- */
220
- static isMarker(elm: Nullable<Node>): elm is HTMLElement {
221
- return (
222
- Dom.isNode(elm) &&
223
- Dom.isTag(elm, 'span') &&
224
- elm.hasAttribute('data-' + consts.MARKER_CLASS)
225
- );
226
- }
227
-
228
221
  /**
229
222
  * Check if editor has selection markers
230
223
  */
@@ -837,7 +830,7 @@ export class Select implements ISelect {
837
830
  node &&
838
831
  node !== root &&
839
832
  !Dom.isEmptyTextNode(node) &&
840
- !Select.isMarker(node as HTMLElement)
833
+ !isMarker(node as HTMLElement)
841
834
  ) {
842
835
  nodes.push(node);
843
836
  }
@@ -926,8 +919,17 @@ export class Select implements ISelect {
926
919
  const container = start ? range.startContainer : range.endContainer;
927
920
  const offset = start ? range.startOffset : range.endOffset;
928
921
 
929
- const check = (elm: Node | null): boolean =>
930
- Boolean(elm && !Dom.isTag(elm, 'br') && !Dom.isEmptyTextNode(elm));
922
+ const isSignificant = (elm: Node | null): boolean =>
923
+ Boolean(
924
+ elm &&
925
+ !Dom.isTag(elm, 'br') &&
926
+ !Dom.isEmptyTextNode(elm) &&
927
+ !Dom.isTemporary(elm) &&
928
+ !(
929
+ Dom.isElement(elm) &&
930
+ this.j.e.fire('isInvisibleForCursor', elm) === true
931
+ )
932
+ );
931
933
 
932
934
  // check right offset
933
935
  if (Dom.isText(container)) {
@@ -949,17 +951,32 @@ export class Select implements ISelect {
949
951
  const children = toArray(container.childNodes);
950
952
 
951
953
  if (end) {
952
- if (children.slice(offset).some(check)) {
954
+ if (children.slice(offset).some(isSignificant)) {
953
955
  return false;
954
956
  }
955
957
  } else {
956
- if (children.slice(0, offset).some(check)) {
958
+ if (children.slice(0, offset).some(isSignificant)) {
957
959
  return false;
958
960
  }
959
961
  }
960
962
  }
961
963
 
962
- return !call(start ? Dom.prev : Dom.next, current, check, parentBlock);
964
+ let next: Nullable<Node> = current;
965
+
966
+ while (next && next !== parentBlock) {
967
+ const nextOne = Dom.sibling(next, start);
968
+ if (!nextOne) {
969
+ next = next.parentNode;
970
+ continue;
971
+ }
972
+ next = nextOne;
973
+
974
+ if (next && isSignificant(next)) {
975
+ return false;
976
+ }
977
+ }
978
+
979
+ return true;
963
980
  }
964
981
 
965
982
  /**
@@ -1221,17 +1238,17 @@ export class Select implements ISelect {
1221
1238
  if (
1222
1239
  firstChild &&
1223
1240
  firstChild === lastChild &&
1224
- Select.isMarker(firstChild)
1241
+ isMarker(firstChild)
1225
1242
  ) {
1226
1243
  Dom.unwrap(font);
1227
1244
  continue;
1228
1245
  }
1229
1246
 
1230
- if (firstChild && Select.isMarker(firstChild)) {
1247
+ if (firstChild && isMarker(firstChild)) {
1231
1248
  Dom.before(font, firstChild);
1232
1249
  }
1233
1250
 
1234
- if (lastChild && Select.isMarker(lastChild)) {
1251
+ if (lastChild && isMarker(lastChild)) {
1235
1252
  Dom.after(font, lastChild);
1236
1253
  }
1237
1254
 
@@ -1253,7 +1270,7 @@ export class Select implements ISelect {
1253
1270
  if (
1254
1271
  font.firstChild &&
1255
1272
  font.firstChild === font.lastChild &&
1256
- Select.isMarker(font.firstChild)
1273
+ isMarker(font.firstChild)
1257
1274
  ) {
1258
1275
  continue;
1259
1276
  }
@@ -1281,6 +1298,30 @@ export class Select implements ISelect {
1281
1298
  return result;
1282
1299
  }
1283
1300
 
1301
+ /**
1302
+ * Apply some css rules for all selections. It method wraps selections in nodeName tag.
1303
+ * @example
1304
+ * ```js
1305
+ * const editor = Jodit.make('#editor');
1306
+ * editor.value = 'test';
1307
+ * editor.execCommand('selectall');
1308
+ *
1309
+ * editor.s.commitStyle({
1310
+ * style: {color: 'red'}
1311
+ * }) // will wrap `text` in `span` and add style `color:red`
1312
+ * editor.s.commitStyle({
1313
+ * style: {color: 'red'}
1314
+ * }) // will remove `color:red` from `span`
1315
+ * ```
1316
+ */
1317
+ commitStyle(options: IStyleOptions): void {
1318
+ assert(size(options) > 0, 'Need to pass at least one option');
1319
+
1320
+ const styleElm = new CommitStyle(options);
1321
+
1322
+ styleElm.apply(this.j);
1323
+ }
1324
+
1284
1325
  /**
1285
1326
  * Apply some css rules for all selections. It method wraps selections in nodeName tag.
1286
1327
  * @example
@@ -1292,6 +1333,7 @@ export class Select implements ISelect {
1292
1333
  * editor.s.applyStyle({color: 'red'}) // will wrap `text` in `span` and add style `color:red`
1293
1334
  * editor.s.applyStyle({color: 'red'}) // will remove `color:red` from `span`
1294
1335
  * ```
1336
+ * @deprecated
1295
1337
  */
1296
1338
  applyStyle(
1297
1339
  style: CanUndef<IStyle>,
@@ -1300,21 +1342,19 @@ export class Select implements ISelect {
1300
1342
  * equal CSSRule (e.g. strong === font-weight: 700)
1301
1343
  */
1302
1344
  element?: HTMLTagNames;
1345
+ /** @deprecated Instead use attributes.class*/
1303
1346
  className?: string;
1347
+ attributes?: IDictionary<string | number>;
1304
1348
  /**
1305
1349
  * tag for wrapping and apply styles
1306
1350
  */
1307
1351
  defaultTag?: HTMLTagNames;
1308
1352
  } = {}
1309
1353
  ): void {
1310
- const styleElm = new CommitStyle({
1354
+ this.commitStyle({
1311
1355
  style,
1312
- element: options.element,
1313
- className: options.className,
1314
- defaultTag: options.defaultTag
1356
+ ...options
1315
1357
  });
1316
-
1317
- styleElm.apply(this.j);
1318
1358
  }
1319
1359
 
1320
1360
  /**
@@ -109,15 +109,8 @@ describe('Selection Module Tests', function () {
109
109
  describe('Cursor in the end of text node', function () {
110
110
  it('Should work correct', function () {
111
111
  const editor = getJodit();
112
- editor.value = '<ul><li><p>test</p></li></ul>';
113
-
114
- const range = editor.s.createRange();
115
-
116
- range.setStartAfter(
117
- editor.editor.querySelector('p').firstChild
118
- );
119
- range.collapse(true);
120
- editor.s.selectRange(range);
112
+ editor.value = '<ul><li><p>test|</p></li></ul>';
113
+ setCursorToChar(editor);
121
114
 
122
115
  ['li', 'p'].forEach(function (tag) {
123
116
  expect(
@@ -185,13 +178,8 @@ describe('Selection Module Tests', function () {
185
178
  describe('Cursor in the end of text node but after this has image', function () {
186
179
  it('Should return false', function () {
187
180
  const editor = getJodit();
188
- editor.value = '<p>test<img/></p>';
189
-
190
- const range = editor.s.createRange();
191
-
192
- range.setStart(editor.editor.firstChild.firstChild, 4);
193
- range.collapse(true);
194
- editor.s.selectRange(range);
181
+ editor.value = '<p>test|<img/></p>';
182
+ setCursorToChar(editor);
195
183
 
196
184
  expect(editor.s.cursorOnTheRight(editor.editor.firstChild))
197
185
  .is.false;
@@ -538,7 +526,7 @@ describe('Selection Module Tests', function () {
538
526
  });
539
527
  });
540
528
 
541
- describe('Curson in the end of span inside P and check cursorInTheEdge(true)', function () {
529
+ describe('Cursor in the end of span inside P and check cursorInTheEdge(true)', function () {
542
530
  it('Should return false', function () {
543
531
  const editor = getJodit();
544
532
  editor.value = '<p>Some <span>|text|</span></p>';
@@ -5,9 +5,10 @@
5
5
  */
6
6
 
7
7
  import type { IJodit } from 'jodit/types';
8
- import { Select } from '../../select';
9
- import { call, trim } from 'jodit/core/helpers';
10
- import { Dom } from 'jodit/core/dom';
8
+ import { call } from 'jodit/core/helpers/utils/utils';
9
+ import { trim } from 'jodit/core/helpers/string/trim';
10
+ import { Dom } from 'jodit/core/dom/dom';
11
+ import { isMarker } from 'jodit/core/helpers/checker/is-marker';
11
12
 
12
13
  /**
13
14
  * If the selection area is inside an element that matches the commit (suitable relative),
@@ -33,7 +34,7 @@ export function extractSelectedPart(
33
34
  const range = jodit.s.createRange();
34
35
 
35
36
  // Left part
36
- const leftEdge = Select.isMarker(font.previousSibling)
37
+ const leftEdge = isMarker(font.previousSibling)
37
38
  ? font.previousSibling
38
39
  : font;
39
40
 
@@ -43,9 +44,7 @@ export function extractSelectedPart(
43
44
  extractAndMove(wrapper, range, true);
44
45
 
45
46
  // Right part
46
- const rightEdge = Select.isMarker(font.nextSibling)
47
- ? font.nextSibling
48
- : font;
47
+ const rightEdge = isMarker(font.nextSibling) ? font.nextSibling : font;
49
48
 
50
49
  range.setStartAfter(rightEdge);
51
50
  range.setEndAfter(wrapper);
@@ -4,28 +4,32 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
- import type { CanUndef, IDictionary } from 'jodit/types';
7
+ import type { IDictionary } from 'jodit/types';
8
+ import { isString } from 'jodit/core/helpers/checker/is-string';
9
+ import { assert } from 'jodit/src/core/helpers/utils/assert';
8
10
 
9
11
  /**
10
12
  * A state machine implementation for applying styles.
11
13
  */
12
- export class FiniteStateMachine {
13
- setState(state: string, subState?: string): void {
14
- this.state = state;
14
+ export class FiniteStateMachine<
15
+ K extends string,
16
+ V extends object & { next: K },
17
+ T extends IDictionary<IDictionary<(value: V) => V>, K> = IDictionary<
18
+ IDictionary<(...attrs: any[]) => any>,
19
+ K
20
+ >,
21
+ A extends keyof T[K] = keyof T[K]
22
+ > {
23
+ private __state!: K;
24
+ private setState(state: K): void {
25
+ assert(!this.__previewsStates.has(state), 'Circled states');
15
26
 
16
- if (subState != null) {
17
- this.subState = subState;
18
- }
27
+ this.__previewsStates.add(state);
28
+ this.__state = state;
19
29
  }
20
30
 
21
- private subState: string = '';
22
-
23
- getState(): string {
24
- return this.state;
25
- }
26
-
27
- getSubState(): string {
28
- return this.subState;
31
+ getState(): K {
32
+ return this.__state;
29
33
  }
30
34
 
31
35
  private silent: boolean = true;
@@ -33,34 +37,35 @@ export class FiniteStateMachine {
33
37
  this.silent = false;
34
38
  }
35
39
 
36
- constructor(
37
- private state: string,
38
- private readonly transitions: IDictionary<
39
- IDictionary<(this: FiniteStateMachine, ...attrs: any[]) => any>
40
- >
41
- ) {}
40
+ private __previewsStates: Set<K> = new Set();
41
+ constructor(state: K, private readonly transitions: T) {
42
+ this.setState(state);
43
+ }
42
44
 
43
- dispatch<T>(actionName: string, ...attrs: any[]): CanUndef<T> {
44
- const action = this.transitions[this.state][actionName];
45
+ dispatch(actionName: A, value: V): V {
46
+ const action = this.transitions[this.getState()][actionName];
45
47
 
46
48
  if (action) {
47
- if (!this.silent) {
48
- console.log('State: ' + this.state, 'Action: ' + actionName);
49
- }
49
+ const res = action.call(this, value);
50
50
 
51
- const res = action.call(this, ...attrs);
51
+ assert(res && res !== value, 'Action should return new value');
52
+ assert(isString(res.next), 'Value should contains next state');
53
+ assert(
54
+ res.next !== this.getState(),
55
+ 'The new state should not be equal to the old one.'
56
+ );
52
57
 
53
- if (!this.silent) {
54
- console.log('State: ' + this.state);
55
- }
58
+ this.setState(res.next);
56
59
 
57
- return <T>res;
58
- }
60
+ if (!isProd && !this.silent) {
61
+ console.log(`State: ${this.getState()}`);
62
+ }
59
63
 
60
- if (!this.silent) {
61
- throw new Error('invalid action: ' + this.state + '.' + actionName);
64
+ return res;
62
65
  }
63
66
 
64
- return;
67
+ throw new Error(
68
+ `invalid action: ${this.getState()}.${actionName.toString()}`
69
+ );
65
70
  }
66
71
  }
@@ -4,9 +4,8 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
- import type { Nullable } from 'jodit/types';
8
- import type { CommitStyle } from '../commit-style';
9
- import { Dom } from 'jodit/core/dom';
7
+ import type { Nullable, ICommitStyle } from 'jodit/types';
8
+ import { Dom } from 'jodit/core/dom/dom';
10
9
  import { isNormalNode } from './is-normal-node';
11
10
  import { isSuitElement } from './is-suit-element';
12
11
 
@@ -19,7 +18,7 @@ import { isSuitElement } from './is-suit-element';
19
18
  * @private
20
19
  */
21
20
  export function getSuitChild(
22
- style: CommitStyle,
21
+ style: ICommitStyle,
23
22
  font: HTMLElement
24
23
  ): Nullable<HTMLElement> {
25
24
  let { firstChild: child } = font;
@@ -4,9 +4,8 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
- import type { Nullable } from 'jodit/types';
8
- import type { CommitStyle } from '../commit-style';
9
- import { Dom } from 'jodit/core/dom';
7
+ import type { Nullable, ICommitStyle } from 'jodit/types';
8
+ import { Dom } from 'jodit/core/dom/dom';
10
9
  import { isNormalNode } from './is-normal-node';
11
10
  import { isSuitElement } from './is-suit-element';
12
11
 
@@ -19,7 +18,7 @@ import { isSuitElement } from './is-suit-element';
19
18
  * @private
20
19
  */
21
20
  export function getSuitParent(
22
- style: CommitStyle,
21
+ style: ICommitStyle,
23
22
  node: Node,
24
23
  root: Node
25
24
  ): Nullable<HTMLElement> {
@@ -8,19 +8,27 @@ import type { IStyle } from 'jodit/types';
8
8
  import { css } from 'jodit/core/helpers/utils/css';
9
9
  import { isVoid } from 'jodit/core/helpers/checker/is-void';
10
10
  import { normalizeCssValue } from 'jodit/core/helpers/normalize/normalize-css-value';
11
- import { Dom } from 'jodit/core/dom';
11
+ import { Dom } from 'jodit/core/dom/dom';
12
+ import { assert } from 'jodit/core/helpers/utils/assert';
12
13
 
13
14
  /**
14
15
  * Element has the same styles as in the commit
15
16
  * @private
16
17
  */
17
- export function elementHasSameStyle(elm: Node, rules: IStyle): boolean {
18
+ export function hasSameStyle(elm: Node, rules: IStyle): boolean {
18
19
  return Boolean(
19
20
  !Dom.isTag(elm, 'font') &&
20
21
  Dom.isHTMLElement(elm) &&
21
22
  Object.keys(rules).every(property => {
22
23
  const value = css(elm, property, true);
23
24
 
25
+ if (
26
+ value === '' &&
27
+ (rules[property] === '' || rules[property] == null)
28
+ ) {
29
+ return true;
30
+ }
31
+
24
32
  return (
25
33
  !isVoid(value) &&
26
34
  value !== '' &&
@@ -33,15 +41,26 @@ export function elementHasSameStyle(elm: Node, rules: IStyle): boolean {
33
41
  );
34
42
  }
35
43
 
44
+ const elm = document.createElement('div');
45
+ elm.style.color = 'red';
46
+ assert(hasSameStyle(elm, { color: 'red' }), 'Style test');
47
+ assert(hasSameStyle(elm, { fontSize: null }), 'Style test');
48
+ assert(hasSameStyle(elm, { fontSize: '' }), 'Style test');
49
+
36
50
  /**
37
51
  * Element has the similar styles
38
52
  */
39
- export function elementHasSameStyleKeys(elm: Node, rules: IStyle): boolean {
53
+ export function hasSameStyleKeys(elm: Node, rules: IStyle): boolean {
40
54
  return Boolean(
41
55
  !Dom.isTag(elm, 'font') &&
42
56
  Dom.isHTMLElement(elm) &&
43
- Object.keys(rules).every(
44
- property => !isVoid(css(elm, property, true))
45
- )
57
+ Object.keys(rules).every(property => {
58
+ const value = css(elm, property, true);
59
+
60
+ return !isVoid(value);
61
+ })
46
62
  );
47
63
  }
64
+
65
+ assert(hasSameStyleKeys(elm, { color: 'red' }), 'Style test');
66
+ assert(hasSameStyleKeys(elm, { font: 'Arial', color: 'red' }), 'Style test');
@@ -4,18 +4,18 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
- export * from './toggle/toggle-css';
8
- export * from './toggle/toggle-ordered-list';
9
- export * from './element-has-same-style';
7
+ export * from './toggle-attributes';
8
+ export * from './list/toggle-ordered-list';
9
+ export * from './list/wrap-list';
10
+ export * from './has-same-style';
10
11
  export * from './extract';
11
12
  export * from './finite-state-machine';
12
13
  export * from './get-suit-child';
13
14
  export * from './get-suit-parent';
14
15
  export * from './is-inside-invisible-element';
16
+ export * from './is-same-attributes';
15
17
  export * from './is-normal-node';
16
18
  export * from './is-suit-element';
17
- export * from './toggle-commit-styles';
18
19
  export * from './unwrap-children';
19
- export * from './wrap-and-commit-style';
20
- export * from './wrap-ordered-list';
20
+ export * from './wrap';
21
21
  export * from './wrap-unwrapped-text';
@@ -4,7 +4,7 @@
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
6
 
7
- import { Dom } from 'jodit/core/dom';
7
+ import { Dom } from 'jodit/core/dom/dom';
8
8
 
9
9
  /**
10
10
  * Check if FONT inside STYLE or SCRIPT element
@@ -5,8 +5,8 @@
5
5
  */
6
6
 
7
7
  import type { Nullable } from 'jodit/types';
8
- import { Dom } from 'jodit/core/dom';
9
- import { Select } from 'jodit/core/selection';
8
+ import { Dom } from 'jodit/core/dom/dom';
9
+ import { isMarker } from 'jodit/core/helpers/checker/is-marker';
10
10
 
11
11
  /**
12
12
  * Is normal usual element
@@ -17,6 +17,6 @@ export function isNormalNode(elm: Nullable<Node>): boolean {
17
17
  elm &&
18
18
  !Dom.isEmptyTextNode(elm) &&
19
19
  !Dom.isTemporary(elm) &&
20
- !Select.isMarker(elm)
20
+ !isMarker(elm)
21
21
  );
22
22
  }
@@ -0,0 +1,56 @@
1
+ /*!
2
+ * Jodit Editor (https://xdsoft.net/jodit/)
3
+ * Released under MIT see LICENSE.txt in the project root for license information.
4
+ * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
+ */
6
+
7
+ import type { IDictionary, IStyle } from 'jodit/types';
8
+ import { attr } from 'jodit/core/helpers/utils';
9
+ import { size } from 'jodit/core/helpers/size/object-size';
10
+ import { assert } from 'jodit/core/helpers/utils/assert';
11
+ import { hasSameStyle } from './has-same-style';
12
+
13
+ /**
14
+ * Compares whether the given attributes match the element's own attributes
15
+ * @private
16
+ */
17
+ export function isSameAttributes(
18
+ elm: HTMLElement,
19
+ attrs?: IDictionary
20
+ ): elm is HTMLElement {
21
+ if (!elm.attributes.length && !size(attrs)) {
22
+ return true;
23
+ }
24
+
25
+ if (!size(attrs)) {
26
+ return true;
27
+ }
28
+
29
+ assert(attrs, 'Attrs must be a non-empty object');
30
+
31
+ return Object.keys(attrs).every(key => {
32
+ if (key === 'class') {
33
+ return elm.classList.contains(attrs[key]);
34
+ }
35
+
36
+ if (key === 'style') {
37
+ return hasSameStyle(elm, attrs[key] as IStyle);
38
+ }
39
+
40
+ return attr(elm, key) === attrs[key];
41
+ });
42
+ }
43
+
44
+ export function elementsEqualAttributes(
45
+ elm1: HTMLElement,
46
+ elm2: HTMLElement
47
+ ): boolean {
48
+ return (
49
+ elm1.attributes.length === elm2.attributes.length &&
50
+ Array.from(elm1.attributes).every(
51
+ attr =>
52
+ elm2.hasAttribute(attr.name) &&
53
+ elm2.getAttribute(attr.name) === attr.value
54
+ )
55
+ );
56
+ }
@@ -3,14 +3,10 @@
3
3
  * Released under MIT see LICENSE.txt in the project root for license information.
4
4
  * Copyright (c) 2013-2022 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
5
5
  */
6
- import type { Nullable } from 'jodit/types';
7
- import type { CommitStyle } from '../commit-style';
6
+ import type { IStyle, Nullable, ICommitStyle } from 'jodit/types';
7
+ import { Dom } from 'jodit/core/dom/dom';
8
8
  import { isNormalNode } from './is-normal-node';
9
- import {
10
- elementHasSameStyle,
11
- elementHasSameStyleKeys
12
- } from './element-has-same-style';
13
- import { Dom } from 'jodit/core/dom';
9
+ import { hasSameStyle, hasSameStyleKeys } from './has-same-style';
14
10
 
15
11
  /**
16
12
  * Checks if an item is suitable for applying a commit. The element suits us if it
@@ -23,7 +19,7 @@ import { Dom } from 'jodit/core/dom';
23
19
  * @private
24
20
  */
25
21
  export function isSuitElement(
26
- commitStyle: CommitStyle,
22
+ commitStyle: ICommitStyle,
27
23
  elm: Nullable<Node>,
28
24
  strict: boolean
29
25
  ): elm is HTMLElement {
@@ -34,7 +30,8 @@ export function isSuitElement(
34
30
  const { element, elementIsDefault, options } = commitStyle;
35
31
 
36
32
  const elmHasSameStyle = Boolean(
37
- options.style && elementHasSameStyle(elm, options.style)
33
+ options.attributes?.style &&
34
+ hasSameStyle(elm, options.attributes.style as IStyle)
38
35
  );
39
36
 
40
37
  const elmIsSame =
@@ -43,7 +40,7 @@ export function isSuitElement(
43
40
 
44
41
  if (
45
42
  ((!elementIsDefault || !strict) && elmIsSame) ||
46
- (elmHasSameStyle && isNormalNode(elm))
43
+ (elmHasSameStyle && isNormalNode(elm) && !commitStyle.elementIsList)
47
44
  ) {
48
45
  return true;
49
46
  }
@@ -53,6 +50,18 @@ export function isSuitElement(
53
50
  );
54
51
  }
55
52
 
53
+ export function findSuitClosest(
54
+ commitStyle: ICommitStyle,
55
+ element: HTMLElement,
56
+ root: HTMLElement
57
+ ): Nullable<HTMLElement> {
58
+ return Dom.closest(
59
+ element,
60
+ node => isSuitElement(commitStyle, node, true),
61
+ root
62
+ );
63
+ }
64
+
56
65
  /**
57
66
  * Inside the parent element there is a block with the same styles
58
67
  * @example
@@ -63,7 +72,7 @@ export function isSuitElement(
63
72
  * Apply `{element:'strong'}`
64
73
  */
65
74
  export function isSameStyleChild(
66
- commitStyle: CommitStyle,
75
+ commitStyle: ICommitStyle,
67
76
  elm: Nullable<Node>
68
77
  ): elm is HTMLElement {
69
78
  const { element, options } = commitStyle;
@@ -75,7 +84,8 @@ export function isSameStyleChild(
75
84
  const elmIsSame = elm.nodeName.toLowerCase() === element;
76
85
 
77
86
  const elmHasSameStyle = Boolean(
78
- options.style && elementHasSameStyleKeys(elm, options.style)
87
+ options.attributes?.style &&
88
+ hasSameStyleKeys(elm, options.attributes?.style as IStyle)
79
89
  );
80
90
 
81
91
  return elmIsSame && elmHasSameStyle;