jodit 4.12.18 → 4.12.21

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 (65) hide show
  1. package/CHANGELOG.md +21 -1
  2. package/es2015/jodit.css +1 -1
  3. package/es2015/jodit.fat.min.js +6 -6
  4. package/es2015/jodit.js +195 -44
  5. package/es2015/jodit.min.js +6 -6
  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 +6 -6
  13. package/es2018/jodit.min.js +6 -6
  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 +8 -8
  18. package/es2021/jodit.js +194 -44
  19. package/es2021/jodit.min.js +8 -8
  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 +8 -8
  28. package/es2021.en/jodit.js +194 -44
  29. package/es2021.en/jodit.min.js +8 -8
  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 +214 -50
  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/helpers/html/apply-styles.js +11 -0
  52. package/esm/core/helpers/html/clean-from-word.js +9 -0
  53. package/esm/core/helpers/html/safe-html.js +71 -19
  54. package/esm/core/helpers/html/strip-tags.d.ts +1 -1
  55. package/esm/core/helpers/html/strip-tags.js +7 -3
  56. package/esm/core/helpers/utils/convert-media-url-to-video-embed.js +41 -19
  57. package/esm/jodit.js +20 -0
  58. package/esm/modules/uploader/config.js +11 -1
  59. package/esm/plugins/color/config.js +12 -3
  60. package/esm/plugins/hotkeys/config.js +1 -1
  61. package/esm/plugins/indent/config.js +20 -6
  62. package/esm/plugins/paste/paste.js +6 -1
  63. package/esm/plugins/paste-from-word/paste-from-word.js +1 -1
  64. package/package.json +1 -1
  65. package/types/core/helpers/html/strip-tags.d.ts +1 -1
package/es2015/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.18
4
+ * Version: v4.12.21
5
5
  * Url: https://xdsoft.net/jodit/
6
6
  * License(s): MIT
7
7
  */
@@ -1808,7 +1808,7 @@ __webpack_require__.r(__webpack_exports__);
1808
1808
  * ```
1809
1809
  * @packageDocumentation
1810
1810
  * @module constants
1811
- */ const APP_VERSION = "4.12.18";
1811
+ */ const APP_VERSION = "4.12.21";
1812
1812
  // prettier-ignore
1813
1813
  const ES = "es2015";
1814
1814
  const IS_ES_MODERN = true;
@@ -5790,6 +5790,16 @@ function normalizeCSS(s) {
5790
5790
  iframeDoc.open();
5791
5791
  iframeDoc.write(html);
5792
5792
  iframeDoc.close();
5793
+ // Word marks its auto-generated list markers (the literal
5794
+ // bullet/number, e.g. `1.` or `·`) with `mso-list:Ignore`.
5795
+ // They are display-only and must not be imported, otherwise
5796
+ // the marker text leaks into the content. Drop them before any
5797
+ // style normalization strips the `mso-list` hint. See #948
5798
+ jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_1__.Dom.each(iframeDoc.body, (node)=>{
5799
+ if (jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_1__.Dom.isElement(node) && /mso-list:\s*ignore/i.test(node.getAttribute('style') || '')) {
5800
+ jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_1__.Dom.safeRemove(node);
5801
+ }
5802
+ });
5793
5803
  try {
5794
5804
  for(let i = 0; i < iframeDoc.styleSheets.length; i += 1){
5795
5805
  const rules = iframeDoc.styleSheets[i].cssRules;
@@ -5887,6 +5897,15 @@ function normalizeCSS(s) {
5887
5897
  jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_1__.Dom.unwrap(node);
5888
5898
  break;
5889
5899
  default:
5900
+ // Word marks its auto-generated list markers
5901
+ // (the literal bullet/number, e.g. `1.` or `·`)
5902
+ // with `mso-list:Ignore`. They are display-only
5903
+ // and must not be imported, otherwise the marker
5904
+ // text leaks into the content. See #948
5905
+ if (/mso-list:\s*ignore/i.test(node.getAttribute('style') || '')) {
5906
+ marks.push(node);
5907
+ break;
5908
+ }
5890
5909
  (0,jodit_core_helpers_array_to_array__WEBPACK_IMPORTED_MODULE_2__.toArray)(node.attributes).forEach((attr)=>{
5891
5910
  if ([
5892
5911
  'src',
@@ -6028,20 +6047,20 @@ function normalizeCSS(s) {
6028
6047
  return;
6029
6048
  }
6030
6049
  const removeEvents = (_options_removeEventAttributes = options.removeEventAttributes) !== null && _options_removeEventAttributes !== void 0 ? _options_removeEventAttributes : options.removeOnError;
6031
- if (removeEvents) {
6032
- removeAllEventAttributes(box);
6033
- (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.$$)('*', box).forEach((elm)=>removeAllEventAttributes(elm));
6034
- } else if (options.removeOnError) {
6035
- sanitizeHTMLElement(box, options);
6036
- (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.$$)('[onerror]', box).forEach((elm)=>sanitizeHTMLElement(elm, options));
6037
- }
6038
- if (options.safeJavaScriptLink) {
6039
- sanitizeHTMLElement(box, options);
6040
- (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.$$)('a[href^="javascript"]', box).forEach((elm)=>sanitizeHTMLElement(elm, options));
6041
- }
6042
- if (options.safeLinksTarget) {
6043
- (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.$$)('a[target="_blank"]', box).forEach((elm)=>{
6044
- const rel = elm.getAttribute('rel') || '';
6050
+ // Single synchronous traversal of the subtree. Besides removing event
6051
+ // handlers and `javascript:` links, `sanitizeHTMLElement` neutralises
6052
+ // executable `iframe[srcdoc]`, `data:text/html` / SVG `data:` document
6053
+ // sources and dangerous schemes in every URL-bearing attribute.
6054
+ const process = (node)=>{
6055
+ if (!jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isElement(node)) {
6056
+ return;
6057
+ }
6058
+ if (removeEvents) {
6059
+ removeAllEventAttributes(node);
6060
+ }
6061
+ sanitizeHTMLElement(node, options);
6062
+ if (options.safeLinksTarget && node.nodeName === 'A' && node.getAttribute('target') === '_blank') {
6063
+ const rel = node.getAttribute('rel') || '';
6045
6064
  const parts = rel.split(/\s+/).filter(Boolean);
6046
6065
  if (!parts.includes('noopener')) {
6047
6066
  parts.push('noopener');
@@ -6049,9 +6068,11 @@ function normalizeCSS(s) {
6049
6068
  if (!parts.includes('noreferrer')) {
6050
6069
  parts.push('noreferrer');
6051
6070
  }
6052
- (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.attr)(elm, 'rel', parts.join(' '));
6053
- });
6054
- }
6071
+ (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.attr)(node, 'rel', parts.join(' '));
6072
+ }
6073
+ };
6074
+ process(box);
6075
+ jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.each(box, process);
6055
6076
  }
6056
6077
  /**
6057
6078
  * Remove all on* event handler attributes from an element
@@ -6072,6 +6093,41 @@ function normalizeCSS(s) {
6072
6093
  }
6073
6094
  return effected;
6074
6095
  }
6096
+ /**
6097
+ * URL-bearing attributes (besides `href`) that can load or execute content.
6098
+ */ const URL_ATTRIBUTES = [
6099
+ 'src',
6100
+ 'data',
6101
+ 'action',
6102
+ 'formaction',
6103
+ 'poster',
6104
+ 'background',
6105
+ 'xlink:href'
6106
+ ];
6107
+ /**
6108
+ * Tags that load their URL as a *document* (scripts inside run). An SVG data
6109
+ * URL is only an XSS vector here — as an `<img>` source it renders inertly.
6110
+ */ const DOCUMENT_EMBED_TAGS = new Set([
6111
+ 'iframe',
6112
+ 'frame',
6113
+ 'object',
6114
+ 'embed'
6115
+ ]);
6116
+ /**
6117
+ * Detects executable / script-bearing URL schemes. The attribute value is
6118
+ * already HTML-entity-decoded by `getAttribute`, so only whitespace and
6119
+ * control characters (which browsers ignore inside a scheme) need stripping.
6120
+ */ function isDangerousUrl(value, tagName) {
6121
+ // eslint-disable-next-line no-control-regex
6122
+ const normalized = value.replace(/[\u0000-\u0020]+/g, '').toLowerCase();
6123
+ if (/^(?:javascript|vbscript|livescript|mocha):/.test(normalized)) {
6124
+ return true;
6125
+ }
6126
+ if (/^data:(?:text\/html|application\/xhtml)/.test(normalized)) {
6127
+ return true;
6128
+ }
6129
+ return /^data:image\/svg/.test(normalized) && DOCUMENT_EMBED_TAGS.has(tagName);
6130
+ }
6075
6131
  function sanitizeHTMLElement(elm, { safeJavaScriptLink, removeOnError } = {
6076
6132
  safeJavaScriptLink: true,
6077
6133
  removeOnError: true
@@ -6089,6 +6145,22 @@ function sanitizeHTMLElement(elm, { safeJavaScriptLink, removeOnError } = {
6089
6145
  (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.attr)(elm, 'href', location.protocol + '//' + href);
6090
6146
  effected = true;
6091
6147
  }
6148
+ if (safeJavaScriptLink) {
6149
+ // `srcdoc` runs its content as a full HTML document — drop it entirely.
6150
+ if (elm.hasAttribute('srcdoc')) {
6151
+ (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.attr)(elm, 'srcdoc', null);
6152
+ effected = true;
6153
+ }
6154
+ // Strip executable schemes from any other URL-bearing attribute.
6155
+ const tagName = elm.nodeName.toLowerCase();
6156
+ for (const name of URL_ATTRIBUTES){
6157
+ const value = elm.getAttribute(name);
6158
+ if (value && isDangerousUrl(value, tagName)) {
6159
+ (0,jodit_core_helpers_utils__WEBPACK_IMPORTED_MODULE_1__.attr)(elm, name, null);
6160
+ effected = true;
6161
+ }
6162
+ }
6163
+ }
6092
6164
  return effected;
6093
6165
  }
6094
6166
 
@@ -6139,7 +6211,7 @@ const ALONE_TAGS = new Set([
6139
6211
  ]);
6140
6212
  /**
6141
6213
  * Extract plain text from HTML text
6142
- */ function stripTags(html, doc = document, exclude = null) {
6214
+ */ function stripTags(html, doc = document, exclude = null, blockBr = false) {
6143
6215
  const tmp = doc.createElement('div');
6144
6216
  if ((0,jodit_core_helpers_checker_is_string__WEBPACK_IMPORTED_MODULE_1__.isString)(html)) {
6145
6217
  tmp.innerHTML = html;
@@ -6153,7 +6225,7 @@ const ALONE_TAGS = new Set([
6153
6225
  }
6154
6226
  if (exclude && jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isTag(p, exclude)) {
6155
6227
  const tag = p.nodeName.toLowerCase();
6156
- const text = !jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isTag(p, ALONE_TAGS) ? `%%%jodit-${tag}%%%${stripTags(p.innerHTML, doc, exclude)}%%%/jodit-${tag}%%%` : `%%%jodit-single-${tag}%%%`;
6228
+ const text = !jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isTag(p, ALONE_TAGS) ? `%%%jodit-${tag}%%%${stripTags(p.innerHTML, doc, exclude, blockBr)}%%%/jodit-${tag}%%%` : `%%%jodit-single-${tag}%%%`;
6157
6229
  jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.before(p, doc.createTextNode(text));
6158
6230
  jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.safeRemove(p);
6159
6231
  return;
@@ -6170,7 +6242,11 @@ const ALONE_TAGS = new Set([
6170
6242
  return;
6171
6243
  }
6172
6244
  if (nx) {
6173
- pr.insertBefore(doc.createTextNode(' '), nx);
6245
+ // By default blocks are joined with a single space (single-line
6246
+ // plain text). When `blockBr` is set, separate them with a line
6247
+ // break instead, so paragraph structure survives — e.g. the
6248
+ // "Insert only Text" paste option. See #1232
6249
+ pr.insertBefore(doc.createTextNode(blockBr ? '%%%jodit-single-br%%%' : ' '), nx);
6174
6250
  }
6175
6251
  });
6176
6252
  return restoreTags((0,jodit_core_helpers_string_trim__WEBPACK_IMPORTED_MODULE_2__.trim)(tmp.innerText));
@@ -7833,7 +7909,6 @@ function ConfigDeepFlatten(obj) {
7833
7909
  return url;
7834
7910
  }
7835
7911
  const parser = jodit_core_constants__WEBPACK_IMPORTED_MODULE_0__.globalDocument.createElement('a');
7836
- const pattern1 = /(?:http?s?:\/\/)?(?:www\.)?(?:vimeo\.com)\/?(.+)/g;
7837
7912
  parser.href = url;
7838
7913
  if (!width) {
7839
7914
  width = 400;
@@ -7845,19 +7920,41 @@ function ConfigDeepFlatten(obj) {
7845
7920
  switch(parser.hostname){
7846
7921
  case 'www.vimeo.com':
7847
7922
  case 'vimeo.com':
7848
- return pattern1.test(url) ? url.replace(pattern1, '<iframe width="' + width + '" height="' + height + '" src="' + protocol + '//player.vimeo.com/video/$1" frameborder="0" allowfullscreen></iframe>') : url;
7923
+ {
7924
+ // The numeric video id can be preceded by `channels/<name>/` or
7925
+ // `groups/<name>/videos/` and followed by tracking params (e.g.
7926
+ // `?share=copy`). Unlisted videos keep a hash right after the id
7927
+ // (`vimeo.com/<id>/<hash>`). Extract the id (+ hash) from the path
7928
+ // so all of those forms produce a valid embed. See #1209
7929
+ const segments = parser.pathname.split('/').filter(Boolean);
7930
+ const idIndex = segments.findIndex((s)=>/^\d+$/.test(s));
7931
+ if (idIndex === -1) {
7932
+ return url;
7933
+ }
7934
+ let path = segments[idIndex];
7935
+ const hash = segments[idIndex + 1];
7936
+ if (hash && idIndex === 0) {
7937
+ path += '/' + hash;
7938
+ }
7939
+ return '<iframe width="' + width + '" height="' + height + '" src="' + protocol + '//player.vimeo.com/video/' + path + '" frameborder="0" allowfullscreen></iframe>';
7940
+ }
7849
7941
  case 'youtube.com':
7850
7942
  case 'www.youtube.com':
7943
+ case 'm.youtube.com':
7944
+ case 'music.youtube.com':
7851
7945
  case 'youtu.be':
7852
7946
  case 'www.youtu.be':
7853
7947
  {
7854
- const query = parser.search ? (0,_parse_query__WEBPACK_IMPORTED_MODULE_2__.parseQuery)(parser.search) : {
7855
- v: parser.pathname.substring(1)
7856
- };
7857
- if (/^embed\/.*/.test(query.v)) {
7858
- query.v = query.v.substring(6);
7859
- }
7860
- return query.v ? '<iframe width="' + width + '" height="' + height + '" src="' + protocol + '//www.youtube.com/embed/' + query.v + '" frameborder="0" allowfullscreen></iframe>' : url;
7948
+ const query = parser.search ? (0,_parse_query__WEBPACK_IMPORTED_MODULE_2__.parseQuery)(parser.search) : {};
7949
+ // `youtube.com/watch` keeps the video id in the `v` query
7950
+ // parameter, while the short `youtu.be/<id>` links and the
7951
+ // `/embed/`, `/shorts/`, `/live/` paths keep it in the pathname.
7952
+ // Modern share urls add tracking params (e.g. `?si=`, `?t=`), so
7953
+ // the pathname must still be used as a fallback when there is no
7954
+ // `v`. See #1209
7955
+ let v = query.v || parser.pathname.substring(1);
7956
+ v = v.replace(/^(watch|embed|shorts|live|v)\//, '').replace(/\/$/, '');
7957
+ return v ? '<iframe width="' + width + '" height="' + height + '" src="' + protocol + '//www.youtube.com/embed/' + v + '" frameborder="0" allowfullscreen></iframe>' : url;
7861
7958
  }
7862
7959
  }
7863
7960
  return url;
@@ -17770,6 +17867,23 @@ class Jodit extends jodit_modules__WEBPACK_IMPORTED_MODULE_10__.ViewWithToolbar
17770
17867
  }
17771
17868
  this.synchronizeValues();
17772
17869
  }
17870
+ }).on(this.ow, 'mouseup', (event)=>{
17871
+ if (this.o.readonly || this.__isSilentChange) {
17872
+ return;
17873
+ }
17874
+ // When a selection is started inside the editor and the
17875
+ // mouse button is released outside of it, the editable
17876
+ // area never receives the `mouseup` event, so the toolbar
17877
+ // state (active buttons) is not recalculated. Re-fire the
17878
+ // event manually for that case while the selection still
17879
+ // belongs to the editor. See #1251
17880
+ const target = event.target;
17881
+ const insideEditor = Boolean(target && (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.isNumber)(target.nodeType) && editor.contains(target));
17882
+ if (insideEditor || !this.s.isInsideArea) {
17883
+ return;
17884
+ }
17885
+ this.e.fire('changeSelection');
17886
+ this.synchronizeValues();
17773
17887
  });
17774
17888
  }
17775
17889
  fetch(url, options) {
@@ -24239,9 +24353,14 @@ jodit_config__WEBPACK_IMPORTED_MODULE_2__.Config.prototype.uploader = {
24239
24353
  insertImageAsBase64URI: false,
24240
24354
  imagesExtensions: [
24241
24355
  'jpg',
24242
- 'png',
24243
24356
  'jpeg',
24244
- 'gif'
24357
+ 'png',
24358
+ 'gif',
24359
+ 'webp',
24360
+ 'bmp',
24361
+ 'svg',
24362
+ 'tiff',
24363
+ 'avif'
24245
24364
  ],
24246
24365
  headers: null,
24247
24366
  data: null,
@@ -28525,8 +28644,9 @@ jodit_config__WEBPACK_IMPORTED_MODULE_3__.Config.prototype.controls.brush = {
28525
28644
  const update = (key, value)=>{
28526
28645
  if (value && value !== (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.css)(editor.editor, key).toString()) {
28527
28646
  button.state.icon.fill = value;
28528
- return;
28647
+ return true;
28529
28648
  }
28649
+ return false;
28530
28650
  };
28531
28651
  if (color) {
28532
28652
  const mode = (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.dataBind)(button, 'color');
@@ -28536,8 +28656,16 @@ jodit_config__WEBPACK_IMPORTED_MODULE_3__.Config.prototype.controls.brush = {
28536
28656
  const current = editor.s.current();
28537
28657
  if (current && !button.state.disabled) {
28538
28658
  const currentBpx = jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(current, jodit_core_dom_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isElement, editor.editor) || editor.editor;
28539
- update('color', (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.css)(currentBpx, 'color').toString());
28540
- update('background-color', (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.css)(currentBpx, 'background-color').toString());
28659
+ // The icon's fill mirrors the current text/background color so the
28660
+ // button reflects the formatting under the caret. Both calls run so
28661
+ // that a background color (the second call) wins over the text color
28662
+ // when both are set. Keep the computed fill instead of resetting it
28663
+ // below. See #195, #182
28664
+ const hasColor = update('color', (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.css)(currentBpx, 'color').toString());
28665
+ const hasBackground = update('background-color', (0,jodit_core_helpers___WEBPACK_IMPORTED_MODULE_1__.css)(currentBpx, 'background-color').toString());
28666
+ if (hasColor || hasBackground) {
28667
+ return;
28668
+ }
28541
28669
  }
28542
28670
  button.state.icon.fill = '';
28543
28671
  button.state.activated = false;
@@ -31155,7 +31283,8 @@ jodit_config__WEBPACK_IMPORTED_MODULE_0__.Config.prototype.commandToHotkeys = {
31155
31283
  'cmd+shift+7'
31156
31284
  ],
31157
31285
  insertUnorderedList: [
31158
- 'ctrl+shift+8, cmd+shift+8'
31286
+ 'ctrl+shift+8',
31287
+ 'cmd+shift+8'
31159
31288
  ],
31160
31289
  selectall: [
31161
31290
  'ctrl+a',
@@ -33404,14 +33533,31 @@ jodit_config__WEBPACK_IMPORTED_MODULE_2__.Config.prototype.controls.indent = {
33404
33533
  };
33405
33534
  jodit_config__WEBPACK_IMPORTED_MODULE_2__.Config.prototype.controls.outdent = {
33406
33535
  isDisabled: (editor)=>{
33536
+ var _editor_o_tab;
33407
33537
  const current = editor.s.current();
33408
- if (current) {
33409
- const currentBox = jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(current, jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isBlock, editor.editor);
33410
- if (currentBox) {
33411
- const arrow = (0,_helpers__WEBPACK_IMPORTED_MODULE_5__.getKey)(editor.o.direction, currentBox);
33412
- return !currentBox.style[arrow] || parseInt(currentBox.style[arrow], 10) <= 0;
33538
+ if (!current) {
33539
+ return true;
33540
+ }
33541
+ // A list item whose list is nested inside another list item can be
33542
+ // outdented (un-nested) by the `tab` plugin, even without an inline
33543
+ // indent margin. Keep the button enabled in that case. See #1247
33544
+ if ((_editor_o_tab = editor.o.tab) === null || _editor_o_tab === void 0 ? void 0 : _editor_o_tab.tabInsideLiInsertNewList) {
33545
+ const li = jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(current, 'li', editor.editor);
33546
+ if (li) {
33547
+ const list = jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(li, [
33548
+ 'ul',
33549
+ 'ol'
33550
+ ], editor.editor);
33551
+ if (list && jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(list, 'li', editor.editor)) {
33552
+ return false;
33553
+ }
33413
33554
  }
33414
33555
  }
33556
+ const currentBox = jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.closest(current, jodit_core_dom__WEBPACK_IMPORTED_MODULE_0__.Dom.isBlock, editor.editor);
33557
+ if (currentBox) {
33558
+ const arrow = (0,_helpers__WEBPACK_IMPORTED_MODULE_5__.getKey)(editor.o.direction, currentBox);
33559
+ return !currentBox.style[arrow] || parseInt(currentBox.style[arrow], 10) <= 0;
33560
+ }
33415
33561
  return true;
33416
33562
  },
33417
33563
  tooltip: 'Decrease Indent'
@@ -36288,7 +36434,7 @@ class pasteFromWord extends jodit_core_plugin__WEBPACK_IMPORTED_MODULE_6__.Plugi
36288
36434
  }
36289
36435
  case jodit_core_constants__WEBPACK_IMPORTED_MODULE_2__.INSERT_ONLY_TEXT:
36290
36436
  {
36291
- html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_5__.stripTags)((0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_5__.cleanFromWord)(html));
36437
+ html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_5__.stripTags)((0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_5__.cleanFromWord)(html), this.j.ed, new Set(this.j.o.pasteExcludeStripTags), this.j.o.nl2brInPlainText);
36292
36438
  break;
36293
36439
  }
36294
36440
  }
@@ -36847,10 +36993,15 @@ jodit_config__WEBPACK_IMPORTED_MODULE_2__.Config.prototype.controls.paste = {
36847
36993
  html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.cleanFromWord)(html);
36848
36994
  break;
36849
36995
  case jodit_core_constants__WEBPACK_IMPORTED_MODULE_2__.INSERT_ONLY_TEXT:
36850
- html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.stripTags)(html, this.j.ed, new Set(this.j.o.pasteExcludeStripTags));
36996
+ html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.stripTags)(html, this.j.ed, new Set(this.j.o.pasteExcludeStripTags), this.j.o.nl2brInPlainText);
36851
36997
  break;
36852
36998
  case jodit_core_constants__WEBPACK_IMPORTED_MODULE_2__.INSERT_AS_TEXT:
36853
36999
  html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.htmlspecialchars)(html);
37000
+ // Keep the source line breaks instead of letting the raw
37001
+ // newlines collapse into spaces when rendered. See #1093
37002
+ if (this.j.o.nl2brInPlainText) {
37003
+ html = (0,jodit_core_helpers__WEBPACK_IMPORTED_MODULE_6__.nl2br)(html);
37004
+ }
36854
37005
  break;
36855
37006
  default:
36856
37007
  {