tinymce-rails 8.9.3 → 8.10.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/source/tinymce/tinymce.js +110 -68
  3. data/lib/tinymce/rails/version.rb +2 -2
  4. data/vendor/assets/javascripts/tinymce/icons/default/icons.js +1 -1
  5. data/vendor/assets/javascripts/tinymce/models/dom/model.js +1 -1
  6. data/vendor/assets/javascripts/tinymce/plugins/advlist/plugin.js +1 -1
  7. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.css +1 -1
  8. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.css +1 -1
  9. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.js +1 -1
  10. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.inline.min.css +1 -1
  11. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.js +1 -1
  12. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/content.min.css +1 -1
  13. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.css +1 -1
  14. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.js +1 -1
  15. data/vendor/assets/javascripts/tinymce/skins/ui/oxide/skin.min.css +1 -1
  16. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.css +1 -1
  17. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.css +1 -1
  18. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.js +1 -1
  19. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.inline.min.css +1 -1
  20. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.js +1 -1
  21. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/content.min.css +1 -1
  22. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.css +1 -1
  23. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.js +1 -1
  24. data/vendor/assets/javascripts/tinymce/skins/ui/oxide-dark/skin.min.css +1 -1
  25. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.css +1 -1
  26. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.css +1 -1
  27. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.js +1 -1
  28. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.inline.min.css +1 -1
  29. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.js +1 -1
  30. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/content.min.css +1 -1
  31. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.css +1 -1
  32. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.js +1 -1
  33. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5/skin.min.css +1 -1
  34. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.css +1 -1
  35. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.css +1 -1
  36. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.js +1 -1
  37. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.inline.min.css +1 -1
  38. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.js +1 -1
  39. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/content.min.css +1 -1
  40. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.css +1 -1
  41. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.js +1 -1
  42. data/vendor/assets/javascripts/tinymce/skins/ui/tinymce-5-dark/skin.min.css +1 -1
  43. data/vendor/assets/javascripts/tinymce/themes/silver/theme.js +1 -1
  44. data/vendor/assets/javascripts/tinymce/tinymce.d.ts +2 -1
  45. data/vendor/assets/javascripts/tinymce/tinymce.js +3 -3
  46. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54b14ed8570aa7ab902e0b2abf90c2a9c2d4198407c22a27d0089bae5b7c2175
4
- data.tar.gz: 2e19733bc1406ca4940884fae634b89e88d17279971391467ff8aa98cc3de092
3
+ metadata.gz: 4a61952e308104873ef07b099e38b1695ee58eef14f81cf862ad3afeadd07d80
4
+ data.tar.gz: 368d783df479b393e05443329c53a047cd565f23f5e137a3b812192f2ab18fdb
5
5
  SHA512:
6
- metadata.gz: eaec7a999a4a5364971ddedbd8e5b637fdf528bae8ba7ed3563f80c9e8f080ad34b756aa5e734cc9a702ed64b28eab05137222db7d0542cc5dd91c44dfe6a2f3
7
- data.tar.gz: a558e0db2b1f6cf62625fec20d4fb49a329a2917ecaf8aa339cdf2263dda3eab9568f411b65c8ad2e23813bae03c6def8cac6fb185f08dbe97202bf6232c4c88
6
+ metadata.gz: 63d959b67aff1a65e5279f146e65c331b0221746acad246fd2a186aad2985455137c17e7a487d332c314c1b41aab14aab167bf5650eecbe706b1dd36e4f0d8a2
7
+ data.tar.gz: a4691ff86f4ba66f24cbf4cb7371a77c435ce51e38f92be0285e6691d7303b44af9f7a6d6c9e555edf04c2bb1b4de42f656ce94bfb5595f9945b9d2f26d2e618
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TinyMCE version 8.9.3 (2026-10-06)
2
+ * TinyMCE version 8.10.0 (2026-10-14)
3
3
  */
4
4
 
5
5
  (function () {
@@ -1155,6 +1155,35 @@
1155
1155
  return isNaN(num) ? Optional.none() : Optional.some(num);
1156
1156
  };
1157
1157
 
1158
+ // Run a function fn after rate ms. If another invocation occurs
1159
+ // during the time it is waiting, update the arguments f will run
1160
+ // with (but keep the current schedule)
1161
+ const adaptable = (fn, rate) => {
1162
+ let timer = null;
1163
+ let args = null;
1164
+ const cancel = () => {
1165
+ if (!isNull(timer)) {
1166
+ clearTimeout(timer);
1167
+ timer = null;
1168
+ args = null;
1169
+ }
1170
+ };
1171
+ const throttle = (...newArgs) => {
1172
+ args = newArgs;
1173
+ if (isNull(timer)) {
1174
+ timer = setTimeout(() => {
1175
+ const tempArgs = args;
1176
+ timer = null;
1177
+ args = null;
1178
+ fn.apply(null, tempArgs);
1179
+ }, rate);
1180
+ }
1181
+ };
1182
+ return {
1183
+ cancel,
1184
+ throttle
1185
+ };
1186
+ };
1158
1187
  // Run a function fn after rate ms. If another invocation occurs
1159
1188
  // during the time it is waiting, ignore it completely.
1160
1189
  const first$1 = (fn, rate) => {
@@ -3374,47 +3403,6 @@
3374
3403
  }
3375
3404
  }
3376
3405
 
3377
- const whiteSpaceRegExp = /^[ \t\r\n]*$/;
3378
- const isWhitespaceText = (text) => whiteSpaceRegExp.test(text);
3379
- const isZwsp$1 = (text) => {
3380
- for (const c of text) {
3381
- if (!isZwsp$2(c)) {
3382
- return false;
3383
- }
3384
- }
3385
- return true;
3386
- };
3387
- // Don't compare other unicode spaces here, as we're only concerned about whitespace the browser would collapse
3388
- const isCollapsibleWhitespace$1 = (c) => ' \f\t\v'.indexOf(c) !== -1;
3389
- const isNewLineChar = (c) => c === '\n' || c === '\r';
3390
- const isNewline = (text, idx) => (idx < text.length && idx >= 0) ? isNewLineChar(text[idx]) : false;
3391
- // Converts duplicate whitespace to alternating space/nbsps and tabs to spaces
3392
- const normalize$4 = (text, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => {
3393
- // Replace tabs with a variable amount of spaces
3394
- // Note: We don't use an actual tab character here, as it only works when in a "whitespace: pre" element,
3395
- // which will cause other issues, such as trying to type the content will also be treated as being in a pre.
3396
- const tabSpace = repeat(' ', tabSpaces);
3397
- const normalizedText = text.replace(/\t/g, tabSpace);
3398
- const result = foldl(normalizedText, (acc, c) => {
3399
- // Are we dealing with a char other than some collapsible whitespace or nbsp? if so then just use it as is
3400
- if (isCollapsibleWhitespace$1(c) || c === nbsp) {
3401
- // If the previous char is a space, we are at the start or end, or if the next char is a new line char, then we need
3402
- // to convert the space to a nbsp
3403
- if (acc.pcIsSpace || (acc.str === '' && isStartOfContent) || (acc.str.length === normalizedText.length - 1 && isEndOfContent) || isNewline(normalizedText, acc.str.length + 1)) {
3404
- return { pcIsSpace: false, str: acc.str + nbsp };
3405
- }
3406
- else {
3407
- return { pcIsSpace: true, str: acc.str + ' ' };
3408
- }
3409
- }
3410
- else {
3411
- // Treat newlines as being a space, since we'll need to convert any leading spaces to nsbps
3412
- return { pcIsSpace: isNewLineChar(c), str: acc.str + c };
3413
- }
3414
- }, { pcIsSpace: false, str: '' });
3415
- return result.str;
3416
- };
3417
-
3418
3406
  const isNodeType = (type) => {
3419
3407
  return (node) => {
3420
3408
  return !!node && node.nodeType === type;
@@ -3502,6 +3490,47 @@
3502
3490
  const ucVideoNodeName = 'uc-video';
3503
3491
  const isUcVideo = matchNodeName$1(ucVideoNodeName);
3504
3492
 
3493
+ const whiteSpaceRegExp = /^[ \t\r\n]*$/;
3494
+ const isWhitespaceText = (text) => whiteSpaceRegExp.test(text);
3495
+ const isZwsp$1 = (text) => {
3496
+ for (const c of text) {
3497
+ if (!isZwsp$2(c)) {
3498
+ return false;
3499
+ }
3500
+ }
3501
+ return true;
3502
+ };
3503
+ // Don't compare other unicode spaces here, as we're only concerned about whitespace the browser would collapse
3504
+ const isCollapsibleWhitespace$1 = (c) => ' \f\t\v'.indexOf(c) !== -1;
3505
+ const isNewLineChar = (c) => c === '\n' || c === '\r';
3506
+ const isNewline = (text, idx) => (idx < text.length && idx >= 0) ? isNewLineChar(text[idx]) : false;
3507
+ // Converts duplicate whitespace to alternating space/nbsps and tabs to spaces
3508
+ const normalize$4 = (text, tabSpaces = 4, isStartOfContent = true, isEndOfContent = true) => {
3509
+ // Replace tabs with a variable amount of spaces
3510
+ // Note: We don't use an actual tab character here, as it only works when in a "whitespace: pre" element,
3511
+ // which will cause other issues, such as trying to type the content will also be treated as being in a pre.
3512
+ const tabSpace = repeat(' ', tabSpaces);
3513
+ const normalizedText = text.replace(/\t/g, tabSpace);
3514
+ const result = foldl(normalizedText, (acc, c) => {
3515
+ // Are we dealing with a char other than some collapsible whitespace or nbsp? if so then just use it as is
3516
+ if (isCollapsibleWhitespace$1(c) || c === nbsp) {
3517
+ // If the previous char is a space, we are at the start or end, or if the next char is a new line char, then we need
3518
+ // to convert the space to a nbsp
3519
+ if (acc.pcIsSpace || (acc.str === '' && isStartOfContent) || (acc.str.length === normalizedText.length - 1 && isEndOfContent) || isNewline(normalizedText, acc.str.length + 1)) {
3520
+ return { pcIsSpace: false, str: acc.str + nbsp };
3521
+ }
3522
+ else {
3523
+ return { pcIsSpace: true, str: acc.str + ' ' };
3524
+ }
3525
+ }
3526
+ else {
3527
+ // Treat newlines as being a space, since we'll need to convert any leading spaces to nsbps
3528
+ return { pcIsSpace: isNewLineChar(c), str: acc.str + c };
3529
+ }
3530
+ }, { pcIsSpace: false, str: '' });
3531
+ return result.str;
3532
+ };
3533
+
3505
3534
  const defaultOptionValues = {
3506
3535
  skipBogus: true,
3507
3536
  includeZwsp: false,
@@ -13765,6 +13794,12 @@
13765
13794
  editor.nodeChanged();
13766
13795
  };
13767
13796
  const showResizeRect = (targetElm) => {
13797
+ // The throttled call can fire after setContent has replaced the target, leaving
13798
+ // handles bound to the zero-sized geometry of a detached element
13799
+ if (!targetElm.isConnected) {
13800
+ hideResizeRect();
13801
+ return;
13802
+ }
13768
13803
  unbindResizeHandleEvents();
13769
13804
  // Get position and size of target
13770
13805
  const position = dom.getPos(targetElm, rootElement);
@@ -13863,7 +13898,9 @@
13863
13898
  hideResizeRect(false);
13864
13899
  }
13865
13900
  };
13866
- const throttledShowResizeRect = first$1(showResizeRect, 0);
13901
+ // Adaptable so a burst of update requests shows the rect for the most recently
13902
+ // requested element rather than the first, which may since have been detached
13903
+ const throttledShowResizeRect = adaptable(showResizeRect, 0);
13867
13904
  const hideResizeRect = (removeSelected = true) => {
13868
13905
  throttledShowResizeRect.cancel();
13869
13906
  unbindResizeHandleEvents();
@@ -16935,14 +16972,18 @@
16935
16972
  selection.setRng(rng);
16936
16973
  }
16937
16974
  };
16975
+ const isAdjacent = (node, sibling) => node.nextSibling === sibling || node.previousSibling === sibling;
16976
+ const isCaretContainerNodeAdjacentToSelection = (node, startContainer, endContainer) => node === startContainer || node === endContainer || isAdjacent(node, startContainer) || isAdjacent(node, endContainer);
16938
16977
  // Removes the caret container for the specified node or all on the current document
16939
16978
  const removeCaretContainer = (editor, node, moveCaret) => {
16940
16979
  const dom = editor.dom, selection = editor.selection;
16941
16980
  if (!node) {
16942
16981
  node = getParentCaretContainer(editor.getBody(), selection.getStart());
16943
16982
  if (!node) {
16983
+ const startContainer = editor.selection.getRng().startContainer;
16984
+ const endContainer = editor.selection.getRng().endContainer;
16944
16985
  while ((node = dom.get(CARET_ID))) {
16945
- removeCaretContainerNode(editor, node, moveCaret);
16986
+ removeCaretContainerNode(editor, node, isCaretContainerNodeAdjacentToSelection(node, startContainer, endContainer));
16946
16987
  }
16947
16988
  }
16948
16989
  }
@@ -17145,9 +17186,9 @@
17145
17186
  }
17146
17187
  }
17147
17188
  };
17148
- const disableCaretContainer = (editor, keyCode, moveCaret) => {
17189
+ const disableCaretContainer = (editor, keyCode) => {
17149
17190
  const selection = editor.selection, body = editor.getBody();
17150
- removeCaretContainer(editor, null, moveCaret);
17191
+ removeCaretContainer(editor, null, false);
17151
17192
  // Remove caret container if it's empty
17152
17193
  if ((keyCode === 8 || keyCode === 46) && selection.isCollapsed() && selection.getStart().innerHTML === ZWSP) {
17153
17194
  removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()), true);
@@ -17157,10 +17198,9 @@
17157
17198
  removeCaretContainer(editor, getParentCaretContainer(body, selection.getStart()), true);
17158
17199
  }
17159
17200
  };
17160
- const endsWithNbsp = (element) => isText$b(element) && endsWith(element.data, nbsp);
17161
17201
  const setup$B = (editor) => {
17162
17202
  editor.on('mouseup keydown', (e) => {
17163
- disableCaretContainer(editor, e.keyCode, endsWithNbsp(editor.selection.getRng().endContainer));
17203
+ disableCaretContainer(editor, e.keyCode);
17164
17204
  });
17165
17205
  };
17166
17206
  const createCaretFormat = (formatNodes) => {
@@ -28952,6 +28992,16 @@
28952
28992
  }
28953
28993
  };
28954
28994
 
28995
+ const clearShadowingItemStyles = (editor) => {
28996
+ each$e(getSelectedListItems(editor), (item) => {
28997
+ const styleType = editor.dom.getStyle(item, 'list-style-type');
28998
+ // Keep none intact as TinyMCE uses it for nested lists
28999
+ if (isNonNullable(styleType) && styleType !== 'none') {
29000
+ editor.dom.setStyle(item, 'list-style-type', '');
29001
+ }
29002
+ });
29003
+ };
29004
+
28955
29005
  const updateListStyle = (dom, el, detail) => {
28956
29006
  const type = detail['list-style-type'] ? detail['list-style-type'] : null;
28957
29007
  dom.setStyle(el, 'list-style-type', type);
@@ -29286,6 +29336,7 @@
29286
29336
  else {
29287
29337
  toggleSingleList(editor, parentList, listName, detail);
29288
29338
  }
29339
+ clearShadowingItemStyles(editor);
29289
29340
  };
29290
29341
 
29291
29342
  const findNextCaretContainer = (editor, rng, isForward, root) => {
@@ -37597,16 +37648,6 @@
37597
37648
  }
37598
37649
  }, true);
37599
37650
  };
37600
- /**
37601
- * Forces Gecko to render a broken image icon if it fails to load an image.
37602
- */
37603
- const showBrokenImageIcon = () => {
37604
- editor.contentStyles.push('img:-moz-broken {' +
37605
- '-moz-force-broken-image-icon:1;' +
37606
- 'min-width:24px;' +
37607
- 'min-height:24px' +
37608
- '}');
37609
- };
37610
37651
  /**
37611
37652
  * iOS has a bug where it's impossible to type if the document has a touchstart event
37612
37653
  * bound and the user touches the document while having the on screen keyboard visible.
@@ -37783,8 +37824,8 @@
37783
37824
  const clickAfterEl = (clientX, clientY, el) => get$c(getClientRects([el.dom]), 0).exists((rect) => clientX >= rect.right && clientY >= rect.top && clientY <= rect.bottom);
37784
37825
  const selectPos = (editor, e, pos) => {
37785
37826
  e.preventDefault();
37786
- editor.focus();
37787
37827
  editor.selection.setRng(pos.toRange());
37828
+ editor.focus();
37788
37829
  };
37789
37830
  /**
37790
37831
  * In Chrome in a `LI` that contains a block element and where the first child is an inline element
@@ -37792,7 +37833,7 @@
37792
37833
  * issue: https://issues.chromium.org/issues/40767343
37793
37834
  **/
37794
37835
  const fixInLISelection = () => {
37795
- editor.on('mousedown', (e) => {
37836
+ editor.on('click', (e) => {
37796
37837
  const target = SugarElement.fromDom(e.target);
37797
37838
  if (isListItem$2(target)) {
37798
37839
  firstBlockChildOrNewLine(target).fold(() => {
@@ -37836,7 +37877,6 @@
37836
37877
  if (isGecko) {
37837
37878
  focusBody();
37838
37879
  setGeckoEditingOptions();
37839
- showBrokenImageIcon();
37840
37880
  blockCmdArrowNavigation();
37841
37881
  }
37842
37882
  };
@@ -37880,7 +37920,6 @@
37880
37920
  removeStylesWhenDeletingAcrossBlockElements();
37881
37921
  setGeckoEditingOptions();
37882
37922
  addBrAfterLastLinks();
37883
- showBrokenImageIcon();
37884
37923
  blockCmdArrowNavigation();
37885
37924
  disableBackspaceIntoATable();
37886
37925
  }
@@ -38399,7 +38438,7 @@
38399
38438
  id: id + '_ifr',
38400
38439
  frameBorder: '0',
38401
38440
  allowTransparency: 'true',
38402
- ...Env.browser.isFirefox() ? { title } : {}
38441
+ title
38403
38442
  });
38404
38443
  add$2(iframe, 'tox-edit-area__iframe');
38405
38444
  return iframe;
@@ -38415,12 +38454,15 @@
38415
38454
  const bodyId = getBodyId(editor);
38416
38455
  const bodyClass = getBodyClass(editor);
38417
38456
  const translatedAriaText = editor.translate(getIframeAriaText(editor));
38418
- const iframeBodyAriaLabel = Env.browser.isFirefox() ? '' : `aria-label="${translatedAriaText}"`;
38457
+ // TINYMCE-13100: VoiceOver does not read the iframe title, so the body is labelled
38458
+ // for it. VoiceOver only runs on macOS, and elsewhere the title is announced.
38459
+ // The role goes with it, since aria-label on a generic role is a violation.
38460
+ const iframeBodyAriaAttrs = Env.os.isMacOS() || Env.os.isiOS() ? `role="textbox" aria-multiline="true" aria-label="${translatedAriaText}"` : '';
38419
38461
  if (getContentSecurityPolicy(editor)) {
38420
38462
  iframeHTML += '<meta http-equiv="Content-Security-Policy" content="' + getContentSecurityPolicy(editor) + '" />';
38421
38463
  }
38422
38464
  iframeHTML += '</head>' +
38423
- `<body id="${bodyId}" class="mce-content-body ${bodyClass}" data-id="${editor.id}" ${iframeBodyAriaLabel}>` +
38465
+ `<body id="${bodyId}" class="mce-content-body ${bodyClass}" data-id="${editor.id}" ${iframeBodyAriaAttrs}>` +
38424
38466
  '<br>' +
38425
38467
  '</body></html>';
38426
38468
  return iframeHTML;
@@ -39725,7 +39767,7 @@
39725
39767
  return false;
39726
39768
  }
39727
39769
  if (lowerCaseCommand !== 'mcefocus') {
39728
- if (!/^(mceAddUndoLevel|mceEndUndoLevel)$/i.test(lowerCaseCommand) && !skipFocus) {
39770
+ if (!/^(mceAddUndoLevel|mceEndUndoLevel|toggleview)$/i.test(lowerCaseCommand) && !skipFocus) {
39729
39771
  editor.focus();
39730
39772
  }
39731
39773
  else {
@@ -42102,14 +42144,14 @@
42102
42144
  * @property minorVersion
42103
42145
  * @type String
42104
42146
  */
42105
- minorVersion: '9.3',
42147
+ minorVersion: '10.0',
42106
42148
  /**
42107
42149
  * Release date of TinyMCE build.
42108
42150
  *
42109
42151
  * @property releaseDate
42110
42152
  * @type String
42111
42153
  */
42112
- releaseDate: '2026-10-06',
42154
+ releaseDate: '2026-10-14',
42113
42155
  /**
42114
42156
  * Collection of language pack data.
42115
42157
  *
@@ -1,6 +1,6 @@
1
1
  module TinyMCE
2
2
  module Rails
3
- VERSION = "8.9.3"
4
- TINYMCE_VERSION = "8.9.3"
3
+ VERSION = "8.10.0"
4
+ TINYMCE_VERSION = "8.10.0"
5
5
  end
6
6
  end