jodit 4.12.35 → 4.12.36

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 (74) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/es2015/jodit.css +1 -1
  3. package/es2015/jodit.fat.min.js +2 -2
  4. package/es2015/jodit.js +105 -49
  5. package/es2015/jodit.min.js +2 -2
  6. package/es2015/plugins/debug/debug.css +1 -1
  7. package/es2015/plugins/debug/debug.js +1 -1
  8. package/es2015/plugins/debug/debug.min.js +1 -1
  9. package/es2015/plugins/speech-recognize/speech-recognize.css +1 -1
  10. package/es2015/plugins/speech-recognize/speech-recognize.js +1 -1
  11. package/es2015/plugins/speech-recognize/speech-recognize.min.js +1 -1
  12. package/es2018/jodit.fat.min.js +2 -2
  13. package/es2018/jodit.min.js +2 -2
  14. package/es2018/plugins/debug/debug.min.js +1 -1
  15. package/es2018/plugins/speech-recognize/speech-recognize.min.js +1 -1
  16. package/es2021/jodit.css +1 -1
  17. package/es2021/jodit.fat.min.js +3 -3
  18. package/es2021/jodit.js +104 -47
  19. package/es2021/jodit.min.js +3 -3
  20. package/es2021/plugins/debug/debug.css +1 -1
  21. package/es2021/plugins/debug/debug.js +1 -1
  22. package/es2021/plugins/debug/debug.min.js +1 -1
  23. package/es2021/plugins/speech-recognize/speech-recognize.css +1 -1
  24. package/es2021/plugins/speech-recognize/speech-recognize.js +1 -1
  25. package/es2021/plugins/speech-recognize/speech-recognize.min.js +1 -1
  26. package/es2021.en/jodit.css +1 -1
  27. package/es2021.en/jodit.fat.min.js +3 -3
  28. package/es2021.en/jodit.js +104 -47
  29. package/es2021.en/jodit.min.js +3 -3
  30. package/es2021.en/plugins/debug/debug.css +1 -1
  31. package/es2021.en/plugins/debug/debug.js +1 -1
  32. package/es2021.en/plugins/debug/debug.min.js +1 -1
  33. package/es2021.en/plugins/speech-recognize/speech-recognize.css +1 -1
  34. package/es2021.en/plugins/speech-recognize/speech-recognize.js +1 -1
  35. package/es2021.en/plugins/speech-recognize/speech-recognize.min.js +1 -1
  36. package/es5/jodit.css +2 -2
  37. package/es5/jodit.fat.min.js +2 -2
  38. package/es5/jodit.js +254 -195
  39. package/es5/jodit.min.css +2 -2
  40. package/es5/jodit.min.js +2 -2
  41. package/es5/plugins/debug/debug.css +1 -1
  42. package/es5/plugins/debug/debug.js +1 -1
  43. package/es5/plugins/debug/debug.min.js +1 -1
  44. package/es5/plugins/speech-recognize/speech-recognize.css +1 -1
  45. package/es5/plugins/speech-recognize/speech-recognize.js +1 -1
  46. package/es5/plugins/speech-recognize/speech-recognize.min.js +1 -1
  47. package/es5/polyfills.fat.min.js +1 -1
  48. package/es5/polyfills.js +1 -1
  49. package/es5/polyfills.min.js +1 -1
  50. package/esm/core/constants.js +1 -1
  51. package/esm/core/dom/dom.d.ts +35 -12
  52. package/esm/core/dom/dom.js +42 -20
  53. package/esm/core/dom/lazy-walker.d.ts +18 -1
  54. package/esm/core/dom/lazy-walker.js +23 -3
  55. package/esm/core/selection/helpers/index.d.ts +3 -1
  56. package/esm/core/selection/helpers/index.js +3 -1
  57. package/esm/core/selection/selection.d.ts +14 -0
  58. package/esm/core/selection/selection.js +19 -8
  59. package/esm/core/selection/style/api/is-same-attributes.d.ts +4 -2
  60. package/esm/core/selection/style/api/is-same-attributes.js +3 -4
  61. package/esm/core/selection/style/api/is-suit-element.d.ts +2 -1
  62. package/esm/core/selection/style/api/is-suit-element.js +2 -1
  63. package/esm/core/selection/style/api/list/toggle-ordered-list.js +7 -0
  64. package/esm/core/selection/style/api/toggle-attributes.js +3 -1
  65. package/esm/core/selection/style/commit-style.d.ts +1 -1
  66. package/esm/core/selection/style/commit-style.js +2 -2
  67. package/package.json +1 -1
  68. package/types/core/dom/dom.d.ts +35 -12
  69. package/types/core/dom/lazy-walker.d.ts +18 -1
  70. package/types/core/selection/helpers/index.d.ts +3 -1
  71. package/types/core/selection/selection.d.ts +14 -0
  72. package/types/core/selection/style/api/is-same-attributes.d.ts +4 -2
  73. package/types/core/selection/style/api/is-suit-element.d.ts +2 -1
  74. package/types/core/selection/style/commit-style.d.ts +1 -1
package/es2021/jodit.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * jodit - Jodit is an awesome and useful wysiwyg editor with filebrowser
3
3
  * Author: Chupurnov <chupurnov@gmail.com> (https://xdsoft.net/jodit/)
4
- * Version: v4.12.35
4
+ * Version: v4.12.36
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1810,7 +1810,7 @@ __webpack_require__.r(__webpack_exports__);
1810
1810
  * ```
1811
1811
  * @packageDocumentation
1812
1812
  * @module constants
1813
- */ const APP_VERSION = "4.12.35";
1813
+ */ const APP_VERSION = "4.12.36";
1814
1814
  // prettier-ignore
1815
1815
  const ES = "es2021";
1816
1816
  const IS_ES_MODERN = true;
@@ -2998,6 +2998,7 @@ function wait(condition) {
2998
2998
 
2999
2999
 
3000
3000
 
3001
+ const TEMP_ELEMENT_REG_EXP = new RegExp(`<([a-z]+)[^>]+${jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.TEMP_ATTR}[^>]*>(.+?)</\\1>`, 'gi');
3001
3002
  /**
3002
3003
  * Module for working with DOM
3003
3004
  */ class Dom {
@@ -3098,7 +3099,11 @@ function wait(condition) {
3098
3099
  }
3099
3100
  }
3100
3101
  /**
3101
- * Call functions for all nodes between `start` and `end`
3102
+ * Call callback for all nodes between `start` and `end` in document order
3103
+ * (`start` and `end` are not included). Iteration stops when the callback returns `true`.
3104
+ *
3105
+ * `end` must be positioned after `start` in the document (e.g. selection markers),
3106
+ * otherwise iteration will stop only at the end of the tree.
3102
3107
  */ static between(start, end, callback) {
3103
3108
  let next = start;
3104
3109
  while(next && next !== end){
@@ -3109,6 +3114,10 @@ function wait(condition) {
3109
3114
  if (!step) {
3110
3115
  while(next && !next.nextSibling){
3111
3116
  next = next.parentNode;
3117
+ // `end` was reached while ascending - all nodes between were visited
3118
+ if (next === end) {
3119
+ return;
3120
+ }
3112
3121
  }
3113
3122
  step = next?.nextSibling;
3114
3123
  }
@@ -3190,7 +3199,7 @@ function wait(condition) {
3190
3199
  return Dom.isNode(elm) && (elm.nodeName === 'TD' || elm.nodeName === 'TH');
3191
3200
  }
3192
3201
  /**
3193
- * Check if element is a list element UL or OL
3202
+ * Check if element is a list element UL or OL
3194
3203
  */ static isList(elm) {
3195
3204
  return Dom.isTag(elm, jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.LIST_TAGS);
3196
3205
  }
@@ -3236,8 +3245,10 @@ function wait(condition) {
3236
3245
  if (!Dom.isNode(node)) {
3237
3246
  return false;
3238
3247
  }
3239
- const win = node.ownerDocument?.defaultView;
3240
- return Boolean(win && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
3248
+ // no `defaultView` requirement — fragments of an inert document
3249
+ // (`template.content`, `DOMParser`, `implementation.createHTMLDocument`)
3250
+ // are still fragments
3251
+ return node.nodeType === Node.DOCUMENT_FRAGMENT_NODE;
3241
3252
  }
3242
3253
  /**
3243
3254
  * Check if element is HTMLElement node
@@ -3345,16 +3356,19 @@ function wait(condition) {
3345
3356
  return null;
3346
3357
  }
3347
3358
  /**
3348
- * Find next/prev node what `condition(next) === true`
3359
+ * Lazily iterate over all nodes what follow after `start` (in document order
3360
+ * for `leftToRight = true`, in reverse order otherwise) inside `root`.
3361
+ * Ancestors of `start` are not yielded.
3349
3362
  */ static *nextGen(start, root, leftToRight = true, withChild = true) {
3350
3363
  const stack = [];
3351
3364
  let currentNode = start;
3352
3365
  do {
3353
3366
  let next = leftToRight ? currentNode.nextSibling : currentNode.previousSibling;
3354
3367
  while(next){
3355
- stack.unshift(next);
3368
+ stack.push(next);
3356
3369
  next = leftToRight ? next.nextSibling : next.previousSibling;
3357
3370
  }
3371
+ stack.reverse();
3358
3372
  yield* this.runInStack(start, stack, leftToRight, withChild);
3359
3373
  currentNode = currentNode.parentNode;
3360
3374
  }while (currentNode && currentNode !== root)
@@ -3369,7 +3383,7 @@ function wait(condition) {
3369
3383
  * ```javascript
3370
3384
  * Jodit.modules.Dom.each(editor.s.current(), function (node) {
3371
3385
  * if (node.nodeType === Node.TEXT_NODE) {
3372
- * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EX, '') // remove all of the text element codes invisible character
3386
+ * node.nodeValue = node.nodeValue.replace(Jodit.INVISIBLE_SPACE_REG_EXP(), '') // remove all of the text element codes invisible character
3373
3387
  * }
3374
3388
  * });
3375
3389
  * ```
@@ -3448,11 +3462,17 @@ function wait(condition) {
3448
3462
  */ static findNotEmptyNeighbor(node, left, root) {
3449
3463
  return (0,jodit_core_helpers_utils_utils__WEBPACK_IMPORTED_MODULE_14__.call)(left ? Dom.prev : Dom.next, node, (n)=>Boolean(n && (!(Dom.isText(n) || Dom.isComment(n)) || (0,jodit_core_helpers_string_trim__WEBPACK_IMPORTED_MODULE_8__.trim)(n?.nodeValue || '').length)), root);
3450
3464
  }
3451
- static sibling(node, left) {
3465
+ /**
3466
+ * Returns the previous (`left = true`) or next sibling of the node
3467
+ */ static sibling(node, left) {
3452
3468
  return left ? node.previousSibling : node.nextSibling;
3453
3469
  }
3454
3470
  /**
3455
3471
  * It goes through all the elements in ascending order, and checks to see if they meet the predetermined condition
3472
+ *
3473
+ * The condition is checked for the `node` itself too. The `root` reached
3474
+ * while ascending is checked only when `checkRoot = true`
3475
+ * (but if `node === root` it is checked in any case).
3456
3476
  */ static up(node, condition, root, checkRoot = false) {
3457
3477
  let start = node;
3458
3478
  if (!start) {
@@ -3577,7 +3597,12 @@ function wait(condition) {
3577
3597
  */ static safeRemove(...nodes) {
3578
3598
  nodes.forEach((node)=>Dom.isNode(node) && node.parentNode && node.parentNode.removeChild(node));
3579
3599
  }
3580
- static safeInsertNode(range, node) {
3600
+ /**
3601
+ * Insert a node into the range and collapse the range to the start of
3602
+ * the inserted content. Unlike the native `Range.insertNode` it does not
3603
+ * split inseparable elements (BR, HR, IMG etc.) and removes empty text
3604
+ * nodes produced by the split of a text container.
3605
+ */ static safeInsertNode(range, node) {
3581
3606
  range.collapsed || range.deleteContents();
3582
3607
  const child = Dom.isFragment(node) ? node.lastChild : node;
3583
3608
  if (range.startContainer === range.endContainer && range.collapsed && Dom.isTag(range.startContainer, jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.INSEPARABLE_TAGS)) {
@@ -3616,22 +3641,17 @@ function wait(condition) {
3616
3641
  }
3617
3642
  }
3618
3643
  static isTag(node, tagNames) {
3644
+ if (Array.isArray(tagNames)) {
3645
+ throw new TypeError('Dom.isTag does not support array');
3646
+ }
3619
3647
  if (!this.isElement(node)) {
3620
3648
  return false;
3621
3649
  }
3622
3650
  const nameL = node.tagName.toLowerCase();
3623
- const nameU = node.tagName.toUpperCase();
3624
3651
  if (tagNames instanceof Set) {
3625
- return tagNames.has(nameL) || tagNames.has(nameU);
3626
- }
3627
- if (Array.isArray(tagNames)) {
3628
- throw new TypeError('Dom.isTag does not support array');
3652
+ return tagNames.has(nameL) || tagNames.has(node.tagName.toUpperCase());
3629
3653
  }
3630
- const tags = tagNames;
3631
- if (nameL === tags || nameU === tags) {
3632
- return true;
3633
- }
3634
- return false;
3654
+ return nameL === tagNames || node.tagName.toUpperCase() === tagNames;
3635
3655
  }
3636
3656
  /**
3637
3657
  * Marks an item as temporary
@@ -3654,9 +3674,9 @@ function wait(condition) {
3654
3674
  return Dom.isNode(elm) && Dom.isTag(elm, 'span') && elm.hasAttribute('data-' + jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.MARKER_CLASS);
3655
3675
  }
3656
3676
  /**
3657
- * Replace temporary elements from string
3677
+ * Unwrap temporary elements inside a HTML string (keeps their content)
3658
3678
  */ static replaceTemporaryFromString(value) {
3659
- return value.replace(/<([a-z]+)[^>]+data-jodit-temp[^>]+>(.+?)<\/\1>/gi, '$2');
3679
+ return value.replace(TEMP_ELEMENT_REG_EXP, '$2');
3660
3680
  }
3661
3681
  /**
3662
3682
  * Get temporary list
@@ -3717,13 +3737,29 @@ function wait(condition) {
3717
3737
 
3718
3738
 
3719
3739
 
3720
- class LazyWalker extends jodit_core_event_emitter_eventify__WEBPACK_IMPORTED_MODULE_4__.Eventify {
3721
- setWork(root) {
3740
+ /**
3741
+ * Walks the DOM tree in small chunks between browser tasks so that even huge
3742
+ * documents do not block the main thread.
3743
+ *
3744
+ * Events:
3745
+ * - `visit` - is called for every node; return `true` to mark the pass as
3746
+ * "had effect" (it will be passed into the `end` event)
3747
+ * - `break` - the pass was interrupted via [[LazyWalker.break]]
3748
+ * - `end` - the pass is finished, receives `true` if some `visit` handler returned `true`
3749
+ */ class LazyWalker extends jodit_core_event_emitter_eventify__WEBPACK_IMPORTED_MODULE_4__.Eventify {
3750
+ /**
3751
+ * Starts a new pass over the `root` tree.
3752
+ * If a previous pass is still running it will be stopped first.
3753
+ */ setWork(root) {
3722
3754
  if (this.isWorked) {
3723
3755
  this.break();
3756
+ } else {
3757
+ // cancel a not yet started pass scheduled by the previous `setWork` call
3758
+ this.stop();
3724
3759
  }
3725
3760
  this.workNodes = jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_3__.Dom.eachGen(root, !this.options.reverse);
3726
3761
  this.isFinished = false;
3762
+ this.hadAffect = false;
3727
3763
  this._requestStarting();
3728
3764
  return this;
3729
3765
  }
@@ -3751,7 +3787,8 @@ class LazyWalker extends jodit_core_event_emitter_eventify__WEBPACK_IMPORTED_MOD
3751
3787
  this.isWorked = false;
3752
3788
  this.isFinished = true;
3753
3789
  this.workNodes = null;
3754
- this.async.cancelIdleCallback(this.idleId);
3790
+ this.__schedulerController?.abort();
3791
+ this.__schedulerController = null;
3755
3792
  }
3756
3793
  destruct() {
3757
3794
  super.destruct();
@@ -3762,7 +3799,7 @@ class LazyWalker extends jodit_core_event_emitter_eventify__WEBPACK_IMPORTED_MOD
3762
3799
  this.isWorked = true;
3763
3800
  let count = 0;
3764
3801
  const chunkSize = this.options.timeoutChunkSize ?? 50;
3765
- while(!this.isFinished && count <= chunkSize){
3802
+ while(!this.isFinished && count < chunkSize){
3766
3803
  const item = this.workNodes.next();
3767
3804
  count += 1;
3768
3805
  if (this.visitNode(item.value)) {
@@ -3787,7 +3824,7 @@ class LazyWalker extends jodit_core_event_emitter_eventify__WEBPACK_IMPORTED_MOD
3787
3824
  return this.emit('visit', nodeElm) ?? false;
3788
3825
  }
3789
3826
  constructor(async, options = {}){
3790
- super(), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "async", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "options", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "workNodes", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "hadAffect", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "isWorked", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "isFinished", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "idleId", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "__schedulerController", void 0), this.async = async, this.options = options, this.workNodes = null, this.hadAffect = false, this.isWorked = false, this.isFinished = false, this.idleId = 0, this.__schedulerController = null;
3827
+ super(), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "async", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "options", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "workNodes", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "hadAffect", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "isWorked", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "isFinished", void 0), (0,_swc_helpers_define_property__WEBPACK_IMPORTED_MODULE_0__._)(this, "__schedulerController", void 0), this.async = async, this.options = options, this.workNodes = null, this.hadAffect = false, this.isWorked = false, this.isFinished = false, this.__schedulerController = null;
3791
3828
  }
3792
3829
  }
3793
3830
  (0,_swc_helpers_ts_decorate__WEBPACK_IMPORTED_MODULE_1__.__decorate)([
@@ -10259,7 +10296,9 @@ class Response {
10259
10296
 
10260
10297
 
10261
10298
  /**
10262
- * Check if the cursor is at the edge of the string
10299
+ * Despite the name, returns `true` when the cursor is NOT at the checked edge
10300
+ * of the text node: there is significant (non invisible-space) text between
10301
+ * the offset and the start (`start = true`) or the end (`end = true`) of the string.
10263
10302
  * @private
10264
10303
  */ function cursorInTheEdgeOfString(container, offset, start, end) {
10265
10304
  const text = container.nodeValue?.length ? container.nodeValue : '';
@@ -10577,7 +10616,7 @@ class Selection {
10577
10616
  /**
10578
10617
  * Remove all markers
10579
10618
  */ removeMarkers() {
10580
- jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_4__.Dom.safeRemove.apply(null, this.markers);
10619
+ jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_4__.Dom.safeRemove(...this.markers);
10581
10620
  }
10582
10621
  /**
10583
10622
  * Create marker element
@@ -10634,7 +10673,11 @@ class Selection {
10634
10673
  this.selectRange(range);
10635
10674
  }
10636
10675
  }
10637
- fakes() {
10676
+ /**
10677
+ * Inserts invisible fake nodes on the boundaries of the current selection
10678
+ * and returns them. Unlike [[Select.save]] the selection stays valid while
10679
+ * the DOM around it is being modified. Restore it later with [[Select.restoreFakes]].
10680
+ */ fakes() {
10638
10681
  const sel = this.sel;
10639
10682
  if (!sel || !sel.rangeCount) {
10640
10683
  return [];
@@ -10660,7 +10703,10 @@ class Selection {
10660
10703
  this.selectRange(range);
10661
10704
  return result;
10662
10705
  }
10663
- restoreFakes(fakes) {
10706
+ /**
10707
+ * Restores the selection previously saved with [[Select.fakes]]
10708
+ * and removes the fake nodes (disconnected fakes are ignored).
10709
+ */ restoreFakes(fakes) {
10664
10710
  const nodes = fakes.filter((n)=>n.isConnected);
10665
10711
  if (!nodes.length) {
10666
10712
  return;
@@ -10897,7 +10943,6 @@ class Selection {
10897
10943
  }
10898
10944
  const node = this.j.createInside.div();
10899
10945
  const fragment = this.j.createInside.fragment();
10900
- let lastChild;
10901
10946
  if (!this.isFocused() && this.j.isEditorMode()) {
10902
10947
  this.focus();
10903
10948
  this.restore();
@@ -10910,12 +10955,10 @@ class Selection {
10910
10955
  if (!this.j.isEditorMode() && this.j.e.fire('insertHTML', node.innerHTML) === false) {
10911
10956
  return;
10912
10957
  }
10913
- lastChild = node.lastChild;
10914
- if (!lastChild) {
10958
+ if (!node.lastChild) {
10915
10959
  return;
10916
10960
  }
10917
10961
  while(node.firstChild){
10918
- lastChild = node.firstChild;
10919
10962
  fragment.appendChild(node.firstChild);
10920
10963
  }
10921
10964
  this.insertNode(fragment, insertCursorAfter, false);
@@ -11027,7 +11070,8 @@ class Selection {
11027
11070
  if (jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_4__.Dom.isEmptyTextNode(start)) {
11028
11071
  nodes.push(start);
11029
11072
  }
11030
- if (start.firstChild) {
11073
+ // `start` can be undefined for an empty root
11074
+ if (start?.firstChild) {
11031
11075
  nodes.push(start.firstChild);
11032
11076
  }
11033
11077
  }
@@ -11042,7 +11086,7 @@ class Selection {
11042
11086
  *
11043
11087
  * @returns true - the cursor is at the end(start) block, null - cursor somewhere outside
11044
11088
  */ cursorInTheEdge(start, parentBlock, fake = null) {
11045
- const end = !start, range = this.sel?.getRangeAt(0);
11089
+ const end = !start, sel = this.sel, range = sel?.rangeCount ? sel.getRangeAt(0) : null;
11046
11090
  fake ??= this.current(false);
11047
11091
  if (!range || !fake || !jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_4__.Dom.isOrContains(parentBlock, fake, true)) {
11048
11092
  return null;
@@ -11381,7 +11425,11 @@ class Selection {
11381
11425
  }
11382
11426
  return currentBox.previousElementSibling;
11383
11427
  }
11384
- expandSelection() {
11428
+ /**
11429
+ * Expands the non-collapsed selection outward: boundaries positioned on the
11430
+ * edge of their parents are moved out of them (e.g. `<p><b>|test|</b></p>`
11431
+ * becomes `<p>|<b>test</b>|</p>`)
11432
+ */ expandSelection() {
11385
11433
  if (this.isCollapsed()) {
11386
11434
  return this;
11387
11435
  }
@@ -11877,12 +11925,11 @@ if (jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.globalDocument) {
11877
11925
 
11878
11926
 
11879
11927
  /**
11880
- * Compares whether the given attributes match the element's own attributes
11928
+ * Checks that every attribute from `attrs` is present on the element with
11929
+ * the same value (one-directional match: extra own attributes of the element
11930
+ * are ignored, empty `attrs` matches any element)
11881
11931
  * @private
11882
11932
  */ function isSameAttributes(elm, attrs) {
11883
- if (!elm.attributes.length && !(0,jodit_core_helpers_size_object_size__WEBPACK_IMPORTED_MODULE_0__.size)(attrs)) {
11884
- return true;
11885
- }
11886
11933
  if (!(0,jodit_core_helpers_size_object_size__WEBPACK_IMPORTED_MODULE_0__.size)(attrs)) {
11887
11934
  return true;
11888
11935
  }
@@ -11931,7 +11978,8 @@ function elementsEqualAttributes(elm1, elm2) {
11931
11978
  * @param commitStyle - style commit
11932
11979
  * @param elm - checked item
11933
11980
  * @param strict - strict mode - false - the default tag is suitable for us if it is also in the commit
11934
- * @param strictStyle - strict style mode - true - the element has the same style keys as in the commit, but not their values
11981
+ * @param strictStyle - strict style mode - true - the element must have the same style keys AND values
11982
+ * as in the commit, false - matching style keys are enough
11935
11983
  * @private
11936
11984
  */ function isSuitElement(commitStyle, elm, strict, strictStyle = true) {
11937
11985
  if (!elm || !(0,_is_normal_node__WEBPACK_IMPORTED_MODULE_2__.isNormalNode)(elm)) {
@@ -12041,6 +12089,13 @@ function unwrapList(mode, list, li, jodit, cs) {
12041
12089
  (0,jodit_core_selection_style_api_extract__WEBPACK_IMPORTED_MODULE_2__.extractSelectedPart)(list, li, jodit);
12042
12090
  (0,jodit_core_helpers_utils_assert__WEBPACK_IMPORTED_MODULE_1__.assert)(jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isHTMLElement(li.parentElement), 'Element should be inside the list');
12043
12091
  jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.unwrap(li.parentElement);
12092
+ // In the REPLACE mode the content will be immediately wrapped in a `li`
12093
+ // of the new list inside `wrapList`, so it should stay in the document.
12094
+ // Replacing it with a fragment here would dissolve the content
12095
+ // (with `enter: 'br'`) and the new list would never be inserted.
12096
+ if (mode === jodit_core_selection_style_constants__WEBPACK_IMPORTED_MODULE_4__.REPLACE) {
12097
+ return li;
12098
+ }
12044
12099
  return jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.replace(li, jodit.o.enter.toLowerCase() !== 'br' ? jodit.o.enter : jodit.createInside.fragment(), jodit.createInside);
12045
12100
  }
12046
12101
 
@@ -12210,7 +12265,9 @@ function toggleClass(jodit, value, elm, mode, dry) {
12210
12265
  function toggleAttribute(jodit, value, elm, key, dry, mode) {
12211
12266
  (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_7__.assert)((0,jodit_core_helpers_checker__WEBPACK_IMPORTED_MODULE_3__.isString)(value) || (0,jodit_core_helpers_checker__WEBPACK_IMPORTED_MODULE_3__.isNumber)(value) || (0,jodit_core_helpers_checker__WEBPACK_IMPORTED_MODULE_3__.isBoolean)(value) || value == null, 'Attribute value must be a string or number or boolean or null');
12212
12267
  const hook = jodit.e.fire.bind(jodit.e, `${jodit_core_selection_style_constants__WEBPACK_IMPORTED_MODULE_10__._PREFIX}AfterToggleAttribute`);
12213
- if ((0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_7__.attr)(elm, key) === value) {
12268
+ // `attr()` getter always returns a string, so a number/boolean value
12269
+ // must be normalized, otherwise the toggle-off branch is unreachable
12270
+ if ((0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_7__.attr)(elm, key) === (value == null ? value : String(value))) {
12214
12271
  !dry && (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_7__.attr)(elm, key, null);
12215
12272
  mode = jodit_core_selection_style_constants__WEBPACK_IMPORTED_MODULE_10__.UNSET;
12216
12273
  !dry && hook(mode, elm, key, value);
@@ -12560,7 +12617,7 @@ class CommitStyle {
12560
12617
  if (!data) {
12561
12618
  return false;
12562
12619
  }
12563
- return data[key];
12620
+ return Boolean(data[key]);
12564
12621
  }
12565
12622
  setApplied(elm, key) {
12566
12623
  const data = this.__applyMap.get(elm) ?? {};
@@ -12574,7 +12631,7 @@ class CommitStyle {
12574
12631
  return this.options.element || this.defaultTag;
12575
12632
  }
12576
12633
  /**
12577
- * New element is blocked
12634
+ * New element is a block element
12578
12635
  */ get elementIsBlock() {
12579
12636
  return Boolean(this.options.element && jodit_core_constants__WEBPACK_IMPORTED_MODULE_1__.IS_BLOCK.test(this.options.element));
12580
12637
  }