Skip to content

Releases: xdan/jodit

4.14.3

Choose a tag to compare

@github-actions github-actions released this 08 Sep 07:19

πŸ› Bug Fix

  • Selection: save(), fakes(), remove() and wrapInTag() leave selections crossing the editor boundary untouched. isInsideArea checks both boundaries, and restoreFakes() ignores nodes moved outside the editor.
  • Selection / caret: current() resolves the last text descendant when the caret follows a nested element and no longer returns a <br> outside the editor. Edge checks use the correct selection endpoint, including selections across blocks.
  • Selection / ranges and history: preserve backward selections through save/restore, formatting, expansion and undo/redo, including iframe editors. Restore and format all native disjoint ranges in Firefox; selected HTML and node iteration include all ranges without duplicate callbacks. Existing history snapshots remain supported.
  • Selection / insertion: invalid or unsupported cursor point lookups preserve the selection. Selecting the editor root selects its contents; positioning a cursor outside the root is rejected. Splitting validates the block, caret and optional edge before changing DOM. An empty sanitized fragment no longer deletes selected content, and insertion uses the surviving final child after hooks.
  • Selection / Shadow DOM: save() finds markers inside the editor's shadow tree and keeps the saved selection active.
  • Formatting: splitting a text node preserves live element offsets, so mixed text/element selection boundaries no longer omit part of the selected content. Temporary wrappers are cleaned up when callbacks throw or iteration stops early; ApplyStyle also restores the selection after a hook throws. Removed fragments are skipped when hooks replace or delete DOM, and formatting stops safely when a hook destroys the editor.
  • Formatting / CSS: explicit property resets such as color: null no longer throw. Wrapper cleanup runs after all attributes have been applied, preserving subsequent CSS rules, classes and unrelated attributes. Applying styles across nested elements overrides overlapping properties without discarding other styles. Formatting skips contenteditable="false" blocks and preserves styles inside protected inline content.
  • Formatting / links: extracting selected text no longer removes links from adjacent, unselected images by treating image-only fragments as empty.
  • Formatting / classes and lists: class and className accept multiple class tokens. Adjacent lists are matched correctly with multiple classes and numeric or boolean attributes. Removing a class fires afterToggleAttribute even when other classes remain.
  • TypeScript: corrected the declared argument order of beforeToggleList and the modes/return type of beforeUnwrapList to match existing runtime behavior. Runtime hook signatures are unchanged.
  • Languages: added missing Azerbaijani translations for Line height, Spellcheck, Speech Recognize and newline.

🏠 Internal

  • Simplified style iteration with for…of and guaranteed selection cleanup, removed intermediate array copies from caret edge checks, reused CSS reset rules across nested elements, and reused one scratch DOM Range during containment traversal instead of allocating one per node.
  • Added 92 selection and style regression/edge-case tests, including Shadow DOM, iframe, undo/redo, disjoint and backward selections, hook mutations, protected content, nested lists, CSS/attribute combinations and documents up to 3000 paragraphs. Browser suites verified in Chrome and Firefox; Chromium skips native multi-range cases it does not support.

4.14.2

Choose a tag to compare

@github-actions github-actions released this 02 Sep 02:19

🏠 Internal

  • Dev dependencies: webpack 5.109.2, less-loader 13, webpack-dev-middleware 8, eslint-plugin-simple-import-sort 14 (imports re-sorted), @types/node 26, synchronous-promise 2.0.18, cssnano-preset-advanced 8.0.10 (#1446, #1447, #1448, #1450, #1452, #1454, #1456). eslint 10 / eslint-plugin-mocha 12 stay on hold until eslint-plugin-import allows eslint 10; TypeScript 7 needs a tsconfig migration first.

4.14.1

Choose a tag to compare

@github-actions github-actions released this 02 Sep 01:55

🏠 Internal

  • Release pipeline: the jodit-react and jodit-pro release triggers, paused for the 4.14.0 rollout, are back on. Both wrappers were bumped to a new minor by hand (jodit-react 5.5.0, jodit-pro 4.14.0, jodit-pro-react 5.6.0); this release lets the bots publish their first builds on top of it.

4.14.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 01:39

🏠 Internal

  • DOM access goes through the Dom module only. Groundwork for virtual-DOM editing, no public API changes: parameter types are widened, return types untouched. New src/types/vdom.d.ts describes the structural subset of the browser DOM the editor relies on (VNode, VElement, VHTMLElement, VStyle, VDocument); browser nodes satisfy it, so existing call sites compile unchanged. The Dom mutation/structural core (append, prepend, after, before, appendChildFirst, detach, unwrap, moveContent, sibling, hide/show) is typed against that subset and deduplicated (first/last share one traversal, prevWithClass/nextWithClass share one helper). A new Virtual DOM contract suite in dom.test.js runs Dom against a non-browser VNode implementation, pinning the invariant at runtime. Across core, modules, plugins and jodit.ts: no direct appendChild/insertBefore/removeChild/replaceChild left outside Dom (was ~207 call sites), .style.x = β†’ css()/VStyle, get/setAttribute β†’ attr(), querySelector reduced to the sanctioned boundaries (user-supplied selectors in selector.ts, pasted stylesheet selectors in apply-styles.ts, the BASE_PATH bootstrap in constants.ts, the sanitizers). #1441
  • Dialog: open dialogs are tracked in a static registry; setMaxZIndex/getMaxZIndexDialog read it instead of scanning the whole destination subtree (usually document.body) on every open and mousedown.

πŸš€ New Feature

  • cssInline(element, key): exact inline read of a CSS property (the style attribute) β€” no computed style, no layout access, no number/color normalization. css(element, key, true) still works but is deprecated: its result goes through normalizeCssValue ('0px' β†’ 0, bold β†’ 700), which made it unusable for exact comparisons and save/restore. Internal call sites (popup/dialog z-index, indent, resizer, media, image properties readers, table widths) migrated.
  • attrRaw(element, name, value?): attribute access with the name taken verbatim β€” no camelCase β†’ kebab-case conversion, no data- fallback, no px stripping. Used by the sanitizers (safeHTML, cleanHTML.allowTags, cleanFromWord) and attribute comparison, where attr()'s name normalization must never apply (onLoad, viewBox, xlink:href).
  • VTokenList.length added to the VNode subset.

πŸ› Bug Fix

  • Paste from Word: attribute cleanup on inline SVG (viewBox, preserveAspectRatio, …) silently skipped those attributes because the removal went through attr(), which kebab-cased the names. Uses attrRaw now.
  • Uploader: bind(form) matches <input type="FILE"> again (attribute selectors are case-insensitive, the attr() comparison was not).
  • Dialog / Resize cells: dialog position and the cell resize handle keep fractional pixels; the numeric css() setter truncated them to integers.
  • Resizer: an image with an inline width: 0px is treated as having an inline size again (cssInline keeps the raw '0px' where css(..., true) returned 0).

πŸ’… Polish

  • Dom.temporaryList is no longer deprecated: it is the history snapshot cleanup and is a plain tree walk (no selector engine, works on VNode).
  • alignElement looks the command up in a Map instead of a plain object (no prototype keys).
  • Toolbar/plugin tag sets (Dom.isTag(node, Set)) hoisted to module constants instead of being allocated per visited node.
  • element.innerHTML = '' clears replaced with Dom.detach(element) (table rows, tooltip, paste storage, delete, debug, inline container).

4.13.27

Choose a tag to compare

@github-actions github-actions released this 01 Sep 23:04

πŸš€ New Feature

  • Formatting / Lists: applying a CSS style (text color, background, font size, font family) to a fully selected list item now puts the style on the <li> itself instead of wrapping the content in a <span>, so the bullet or number gets the same color and size as the text. Partially selected items, items with a nested list and element formats such as bold/italic keep wrapping the content as before; applying the same style again toggles it off the item. #1460

4.13.26

Choose a tag to compare

@github-actions github-actions released this 01 Sep 22:19

πŸ› Bug Fix

  • Color picker: pressing Enter in the hex input applied the color and closed the popup, but Chrome then fired change on the removed, still-focused input, so the same color was applied a second time and toggled off again. The widget now applies a typed value only once. #1459
  • Toolbar: closing a button popup by clicking outside while the hex input had focus threw NotFoundError: Failed to execute 'removeChild'. Chrome fires blur/change in the middle of removeChild, the color callback re-entered the button's close routine, and it destructed the popup while the outer removal was still running. ToolbarButton now detaches the popup before closing it so a re-entrant close is a no-op, and Popup.close() blurs a focused control before removing the container so that Chrome and Firefox both run those handlers while the popup is still in the DOM (Firefox never fired them for a removed element, so the typed color was silently dropped there). #1458

4.13.25

Choose a tag to compare

@github-actions github-actions released this 01 Sep 19:18

πŸ› Bug Fix

  • Decorators: @cacheHTML (used by UIButton.createContainer and ToolbarButton.createContainer) kept a single template element per class for the whole page lifetime, keyed only by the constructor. When an editor was created inside an iframe (ownerDocument/ownerWindow options) and that iframe was later removed, every subsequent editor received cloneNode copies of a template that belonged to the destroyed document, and its toolbar buttons rendered but never reacted to clicks. The cache is now keyed by the owner document first and only then by the class, so each document builds its own templates. #1457

4.13.24

Choose a tag to compare

@github-actions github-actions released this 01 Sep 13:20

πŸ› Bug Fix

  • Selection: applying a block format while the selection sat in bare text inside a container block (e.g. a <div> that also holds paragraphs) replaced the whole container with the new element, merging all its blocks into one heading. The nearest-block search now skips container blocks that have block-level children and wraps the selected text island in a new block inside the container instead. Additionally, toggling a block format off inside a container (an <h3> inside a <div>) used to bare-unwrap the element, leaving orphan inline text that the wrapNodes plugin only restores at the editor root; such a block is now replaced with the default paragraph tag. Reported by Kevin Sormann (li-life web+it, Jodit PRO).

4.13.23

Choose a tag to compare

@github-actions github-actions released this 18 Aug 09:29

πŸ› Bug Fix

  • Clean HTML: the plugin's asynchronous walker stole focus back from an element the user had navigated to. Shift+Tab moved focus out of the editor, but when the walker then removed an empty text node left near the old caret position, it restored the selection β€” and selecting re-focuses the editor, yanking focus away from the target element a fraction of a second later. The caret is now only restored while the editor still owns focus; cleaning is unchanged. Reported by Ralf Pichler (Uniquare, Jodit OEM).

4.13.22

Choose a tag to compare

@github-actions github-actions released this 13 Aug 06:05

πŸ› Bug Fix

  • Image processor: an image with a percent-encoded data URI (<img src="data:image/svg+xml,%3Csvg...">) made dataURItoBlob call atob on a payload that is not base64, so the editor threw InvalidCharacterError out of the change handler β€” on every change while such an image was in the content. Percent-encoded payloads are now decoded as well, and a data URI that still cannot be decoded leaves the image untouched instead of breaking the editor. The plugin's private copy of the helper is gone; both paths share modules/uploader/helpers/data-uri-to-blob.