inital commit

This commit is contained in:
2026-01-01 15:25:19 +05:30
commit f0ae49465a
36361 changed files with 4894111 additions and 0 deletions

5
node_modules/jodit/esm/plugins/about/about.d.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/

49
node_modules/jodit/esm/plugins/about/about.js generated vendored Normal file
View File

@@ -0,0 +1,49 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import * as constants from "../../core/constants.js";
import { HOMEPAGE } from "../../core/constants.js";
import { pluginSystem } from "../../core/global.js";
import { css, isLicense, normalizeLicense } from "../../core/helpers/index.js";
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import aboutIcon from "./about.svg.js";
Config.prototype.controls.about = {
exec: (editor) => {
const dialog = editor.dlg({ closeOnClickOverlay: true }), i = editor.i18n.bind(editor);
dialog
.setMod('theme', editor.o.theme)
.setHeader(i('About Jodit'))
.setContent(`<div class="jodit-about">
<div>${i('Jodit Editor')} v.${editor.getVersion()}</div>
<div>${i('License: %s', !isLicense(editor.o.license)
? 'MIT'
: normalizeLicense(editor.o.license))}</div>
<div>
<a href="${HOMEPAGE}" target="_blank">${HOMEPAGE}</a>
</div>
<div>
<a href="https://xdsoft.net/jodit/docs/" target="_blank">${i("Jodit User's Guide")}</a>
${i('contains detailed help for using')}
</div>
<div>${i('Copyright © XDSoft.net - Chupurnov Valeriy. All rights reserved.')}</div>
</div>`);
css(dialog.dialog, {
minHeight: 200,
minWidth: 420
});
dialog.open(true, true);
},
tooltip: 'About Jodit',
mode: constants.MODE_SOURCE + constants.MODE_WYSIWYG
};
function about(editor) {
editor.registerButton({
name: 'about',
group: 'info'
});
}
pluginSystem.add('about', about);
Icon.set('about', aboutIcon);

1
node_modules/jodit/esm/plugins/about/about.svg.js generated vendored Normal file
View File

@@ -0,0 +1 @@
export default "<svg viewBox=\"0 0 1792 1792\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M1088 1256v240q0 16-12 28t-28 12h-240q-16 0-28-12t-12-28v-240q0-16 12-28t28-12h240q16 0 28 12t12 28zm316-600q0 54-15.5 101t-35 76.5-55 59.5-57.5 43.5-61 35.5q-41 23-68.5 65t-27.5 67q0 17-12 32.5t-28 15.5h-240q-15 0-25.5-18.5t-10.5-37.5v-45q0-83 65-156.5t143-108.5q59-27 84-56t25-76q0-42-46.5-74t-107.5-32q-65 0-108 29-35 25-107 115-13 16-31 16-12 0-25-8l-164-125q-13-10-15.5-25t5.5-28q160-266 464-266 80 0 161 31t146 83 106 127.5 41 158.5z\"/> </svg> ";

View File

@@ -0,0 +1,37 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/add-new-line/README.md]]
* @packageDocumentation
* @module plugins/add-new-line
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../modules/index";
import "./config";
/**
* Create helper for adding new paragraph(Jodit.defaultOptions.enter tag) before iframe, table or image
*/
export declare class addNewLine extends Plugin {
private __line;
private __isMatchedTag;
private __timeout;
private __isBeforeContent;
private __current;
private __lineInFocus;
private __isShown;
private __show;
private __hideForce;
protected onLock(isLocked: true): void;
private __hide;
private __canGetFocus;
protected afterInit(editor: IJodit): void;
private __addEventListeners;
private __onClickLine;
protected onDblClickEditor(e: MouseEvent): void;
private __onMouseMove;
/** @override */
protected beforeDestruct(): void;
}

View File

@@ -0,0 +1,229 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { autobind, throttle, watch } from "../../core/decorators/index.js";
import { pluginSystem } from "../../core/global.js";
import { call, offset, position, scrollIntoViewIfNeeded } from "../../core/helpers/index.js";
import { Dom, Icon, Plugin } from "../../modules/index.js";
import "./config.js";
const ns = 'addnewline';
/**
* Create helper for adding new paragraph(Jodit.defaultOptions.enter tag) before iframe, table or image
*/
export class addNewLine extends Plugin {
constructor() {
super(...arguments);
this.__line = this.j.c.fromHTML(`<div role="button" tabindex="-1" title="${this.j.i18n('Break')}" class="jodit-add-new-line"><span>${Icon.get('enter')}</span></div>`);
this.__isMatchedTag = (node) => Boolean(node &&
this.j.o.addNewLineTagsTriggers.includes(node.nodeName.toLowerCase()));
this.__isBeforeContent = false;
this.__lineInFocus = false;
this.__isShown = false;
this.__hideForce = () => {
if (!this.__isShown) {
return;
}
this.__isShown = false;
this.j.async.clearTimeout(this.__timeout);
this.__lineInFocus = false;
Dom.safeRemove(this.__line);
this.__line.style.setProperty('--jd-anl-handle-offset', '0');
};
this.__canGetFocus = (elm) => {
return (elm != null &&
Dom.isBlock(elm) &&
!/^(img|table|iframe|hr)$/i.test(elm.nodeName));
};
this.__onClickLine = (e) => {
const editor = this.j;
const p = editor.createInside.element(editor.o.enter);
if (this.__isBeforeContent &&
this.__current &&
this.__current.parentNode) {
if (this.__current === editor.editor) {
Dom.prepend(editor.editor, p);
}
else {
this.__current.parentNode.insertBefore(p, this.__current);
}
}
else {
editor.editor.appendChild(p);
}
editor.s.setCursorIn(p);
scrollIntoViewIfNeeded(p, editor.editor, editor.ed);
editor.synchronizeValues();
this.__hideForce();
e.preventDefault();
};
}
__show() {
if (this.j.o.readonly || this.j.isLocked) {
return;
}
this.j.async.clearTimeout(this.__timeout);
if (this.__isShown) {
return;
}
this.__isShown = true;
this.j.container.appendChild(this.__line);
this.__line.style.width = this.j.container.clientWidth + 'px';
}
onLock(isLocked) {
if (isLocked && this.__isShown) {
this.__hideForce();
}
}
__hide() {
if (!this.__isShown || this.__lineInFocus) {
return;
}
this.__timeout = this.j.async.setTimeout(this.__hideForce, {
timeout: 500,
label: 'add-new-line-hide'
});
}
afterInit(editor) {
if (!editor.o.addNewLine) {
return;
}
editor.e
.on(this.__line, 'mousemove', (e) => {
e.stopPropagation();
})
.on(this.__line, 'mousedown touchstart', this.__onClickLine)
.on('change', this.__hideForce)
.on(this.__line, 'mouseenter', () => {
this.j.async.clearTimeout(this.__timeout);
this.__lineInFocus = true;
})
.on(this.__line, 'mouseleave', () => {
this.__lineInFocus = false;
})
.on('changePlace', this.__addEventListeners.bind(this));
this.__addEventListeners();
}
__addEventListeners() {
const editor = this.j;
editor.e
.off(editor.editor, '.' + ns)
.off(editor.container, '.' + ns)
.off('.' + ns)
.on([editor.ow, editor.ew, editor.editor], 'scroll' + '.' + ns, this.__hideForce)
.on('finishedCleanHTMLWorker' + '.' + ns, this.__hideForce)
.on(editor.editor, 'click' + '.' + ns, this.__hide)
.on(editor.container, 'mouseleave' + '.' + ns, this.__hide)
.on(editor.editor, 'mousemove' + '.' + ns, this.__onMouseMove);
}
onDblClickEditor(e) {
const editor = this.j;
if (!editor.o.readonly &&
editor.o.addNewLineOnDBLClick &&
e.target === editor.editor &&
editor.s.isCollapsed()) {
const editorBound = offset(editor.editor, editor, editor.ed);
const top = e.pageY - editor.ew.scrollX;
const p = editor.createInside.element(editor.o.enter);
if (Math.abs(top - editorBound.top) <
Math.abs(top - (editorBound.height + editorBound.top)) &&
editor.editor.firstChild) {
editor.editor.insertBefore(p, editor.editor.firstChild);
}
else {
editor.editor.appendChild(p);
}
editor.s.setCursorIn(p);
editor.synchronizeValues();
this.__hideForce();
e.preventDefault();
}
}
__onMouseMove(e) {
const editor = this.j;
let currentElement = editor.ed.elementFromPoint(e.clientX, e.clientY);
if (!Dom.isHTMLElement(currentElement) ||
!Dom.isOrContains(editor.editor, currentElement)) {
return;
}
if (editor.editor !== currentElement &&
!this.__isMatchedTag(currentElement)) {
currentElement = Dom.closest(currentElement, this.__isMatchedTag, editor.editor);
}
if (!currentElement) {
this.__hide();
return;
}
if (this.__isMatchedTag(currentElement)) {
const parentBox = Dom.up(currentElement, Dom.isBlock, editor.editor);
if (parentBox && parentBox !== editor.editor) {
currentElement = parentBox;
}
}
const pos = position(currentElement, this.j);
let top = false;
let { clientY, clientX } = e;
if (this.j.iframe) {
const { top, left } = position(this.j.iframe, this.j, true);
clientY += top;
clientX += left;
}
const delta = this.j.o.addNewLineDeltaShow;
if (Math.abs(clientY - pos.top) <= delta) {
top = pos.top;
this.__isBeforeContent = true;
}
else if (Math.abs(clientY - (pos.top + pos.height)) <= delta) {
top = pos.top + pos.height;
this.__isBeforeContent = false;
}
const isEditor = editor.editor === currentElement;
if (top !== false &&
((isEditor && !this.__isBeforeContent) ||
(!isEditor &&
!call(this.__isBeforeContent ? Dom.prev : Dom.next, currentElement, this.__canGetFocus, editor.editor)))) {
this.__line.style.top = top + 'px';
this.__current = currentElement;
this.__show();
this.__line.style.setProperty('--jd-anl-handle-offset', clientX - pos.left - 10 + 'px');
}
else {
this.__current = false;
this.__hide();
}
}
/** @override */
beforeDestruct() {
this.j.async.clearTimeout(this.__timeout);
this.j.e.off(this.__line).off('changePlace', this.__addEventListeners);
Dom.safeRemove(this.__line);
this.j.e
.off([this.j.ow, this.j.ew, this.j.editor], '.' + ns)
.off(this.j.container, '.' + ns)
.off('.' + ns);
}
}
__decorate([
watch(':lock')
], addNewLine.prototype, "onLock", null);
__decorate([
autobind
], addNewLine.prototype, "__hide", null);
__decorate([
watch(':dblclick')
], addNewLine.prototype, "onDblClickEditor", null);
__decorate([
throttle(ctx => ctx.defaultTimeout)
], addNewLine.prototype, "__onMouseMove", null);
pluginSystem.add('addNewLine', addNewLine);

View File

@@ -0,0 +1,36 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/add-new-line
*/
import type { HTMLTagNames } from "../../types/index";
declare module 'jodit/config' {
interface Config {
/**
* Create helper
*/
addNewLine: boolean;
/**
* What kind of tags it will be impact
*/
addNewLineTagsTriggers: HTMLTagNames[];
/**
* On dbl click on empty space of editor it add new P element
* @example
* ```js
* Jodit.make('#editor', {
* addNewLineOnDBLClick: false // disable
* })
* ```
*/
addNewLineOnDBLClick: boolean;
/**
* Absolute delta between cursor position and edge(top or bottom)
* of element when show line
*/
addNewLineDeltaShow: number;
}
}

20
node_modules/jodit/esm/plugins/add-new-line/config.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import enterIcon from "./enter.svg.js";
Config.prototype.addNewLine = true;
Config.prototype.addNewLineOnDBLClick = true;
Config.prototype.addNewLineTagsTriggers = [
'table',
'iframe',
'img',
'hr',
'pre',
'jodit'
];
Config.prototype.addNewLineDeltaShow = 20;
Icon.set('enter', enterIcon);

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 128 128\" xml:space=\"preserve\"> <polygon points=\"112.4560547,23.3203125 112.4560547,75.8154297 31.4853516,75.8154297 31.4853516,61.953125 16.0131836,72.6357422 0.5410156,83.3164063 16.0131836,93.9990234 31.4853516,104.6796875 31.4853516,90.8183594 112.4560547,90.8183594 112.4560547,90.8339844 127.4589844,90.8339844 127.4589844,23.3203125\"/> </svg> ";

View File

@@ -0,0 +1,29 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/ai-assistant/README.md]]
* @packageDocumentation
* @module plugins/ai-assistant
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../core/plugin/plugin";
import "./config";
/**
* The plugin inserts content generated by AI into the editor.
*/
export declare class aiAssistant extends Plugin {
/** @override */
buttons: Plugin['buttons'];
private get __dialog();
private get __container();
constructor(jodit: IJodit);
/** @override */
afterInit(): void;
protected onGenerateAiAssistantForm(prompt: string): void;
protected onInvokeAiAssistant(prompt: string): void;
/** @override */
protected beforeDestruct(_: IJodit): void;
}

View File

@@ -0,0 +1,120 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { cache, cached } from "../../core/decorators/cache/cache.js";
import { watch } from "../../core/decorators/watch/watch.js";
import { extendLang, pluginSystem } from "../../core/global.js";
import { isAbortError } from "../../core/helpers/checker/is-abort-error.js";
import { Plugin } from "../../core/plugin/plugin.js";
import "./config.js";
import { UiAiAssistant } from "./ui/ui-ai-assistant.js";
import * as lang from "./langs/index.js";
/**
* The plugin inserts content generated by AI into the editor.
*/
export class aiAssistant extends Plugin {
get __dialog() {
return this.jodit.dlg({
buttons: ['fullsize', 'dialog.close'],
closeOnClickOverlay: true,
closeOnEsc: true,
resizable: false,
draggable: true,
minHeight: 160
});
}
get __container() {
const { jodit, __dialog } = this;
return new UiAiAssistant(jodit, {
onInsertAfter(html) {
jodit.s.focus();
jodit.s.setCursorAfter(jodit.s.current());
jodit.s.insertHTML(html);
__dialog.close();
},
onInsert(html) {
jodit.s.focus();
jodit.s.insertHTML(html);
__dialog.close();
}
});
}
constructor(jodit) {
super(jodit);
/** @override */
this.buttons = [
{
name: 'ai-commands',
group: 'insert'
},
{
name: 'ai-assistant',
group: 'insert'
}
];
extendLang(lang);
}
/** @override */
afterInit() { }
onGenerateAiAssistantForm(prompt) {
this.__dialog.open(this.__container, 'AI Assistant');
this.__container.setPrompt(prompt);
}
onInvokeAiAssistant(prompt) {
const { jodit } = this;
jodit.s.focus();
const selectedText = jodit.s.html;
jodit.async
.promise(async (resolve, reject) => {
try {
const result = await jodit.o.aiAssistant
.aiAssistantCallback(prompt, selectedText);
resolve(result);
}
catch (error) {
reject(error);
}
})
.then((htmlFragment) => {
jodit.e.fire('ai-assistant-response', htmlFragment);
})
.catch(error => {
if (isAbortError(error)) {
return;
}
jodit.message.error(error.message);
jodit.e.fire('ai-assistant-error', error.message);
});
}
/** @override */
beforeDestruct(_) {
var _a, _b;
(_a = cached(this, '__container')) === null || _a === void 0 ? void 0 : _a.destruct();
(_b = cached(this, '__dialog')) === null || _b === void 0 ? void 0 : _b.destruct();
}
}
__decorate([
cache
], aiAssistant.prototype, "__dialog", null);
__decorate([
cache
], aiAssistant.prototype, "__container", null);
__decorate([
watch(':generateAiAssistantForm.ai-assistant')
], aiAssistant.prototype, "onGenerateAiAssistantForm", null);
__decorate([
watch(':invokeAiAssistant')
], aiAssistant.prototype, "onInvokeAiAssistant", null);
pluginSystem.add('ai-assistant', aiAssistant);

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 270 270\"> <path d=\"M 255.9537,58.150081 237.69527,40.997278 c -1.49414,-1.375593 -3.43653,-2.077427 -5.37891,-2.077427 -1.94239,0 -3.88478,0.701834 -5.37892,2.077427 L 29.919751,226.1128 c -2.988286,2.77926 -2.988286,7.32714 0,10.13447 L 48.148295,253.372 c 1.46426,1.37559 3.406646,2.1055 5.378915,2.1055 1.972268,0 3.884771,-0.72991 5.378914,-2.1055 L 221.34935,100.73732 255.9537,68.284552 c 2.9584,-2.807333 2.9584,-7.355212 0,-10.134471 z M 251.17244,63.79282 221.34935,91.781927 201.89561,73.506191 231.68882,45.48901 c 0.20918,-0.140367 0.38847,-0.224587 0.62754,-0.224587 0.23906,0 0.44824,0.08422 0.59765,0.224587 l 18.25843,17.152803 c 0,0 0.23906,0.33688 0.23906,0.561467 0,0.224586 -0.0896,0.4211 -0.23906,0.58954 z\" style=\"stroke-width:2.8964;stroke-opacity:1\" /> <path d=\"m 48.626421,116.87948 10.578532,23.10435 c 0.83672,1.82477 3.615826,1.85284 4.452546,0 l 10.937126,-23.52545 c 2.629692,-5.69888 7.470715,-10.24676 13.536935,-12.71722 l 25.07172,-10.274833 c 1.94239,-0.786053 1.94239,-3.396873 0,-4.182926 L 88.13156,79.008563 C 82.06534,76.53811 77.224317,71.990231 74.594625,66.291346 L 63.657499,42.737824 c -0.83672,-1.824766 -3.615826,-1.824766 -4.452546,0 L 48.267826,66.291346 C 45.638135,71.990231 40.797112,76.53811 34.730891,79.008563 L 9.6292894,89.311474 c -1.9423859,0.786054 -1.9423859,3.3688 0,4.182926 l 25.5498446,10.61172 c 6.036338,2.49852 10.847478,7.07448 13.477169,12.77336 z\" style=\"stroke-width:2.8964;fill-opacity:1;stroke-opacity:1\" /> <path d=\"m 111.79878,33.136746 13.56682,5.642739 c 3.19747,1.319446 5.76739,3.761826 7.14201,6.793745 l 5.61797,12.268044 c 0.44825,0.982567 1.91251,0.982567 2.36075,0 l 5.79727,-12.492631 c 1.4045,-3.031919 3.97442,-5.446225 7.20177,-6.765672 l 13.29788,-5.446225 c 1.0459,-0.4211 1.0459,-1.796693 0,-2.217793 l -13.29788,-5.446225 c -3.22735,-1.319447 -5.79727,-3.733753 -7.20177,-6.765672 L 140.48633,6.2144248 c -0.44824,-0.9825664 -1.9125,-0.9825664 -2.36075,0 l -5.79727,12.4926312 c -1.4045,3.031919 -3.97442,5.446225 -7.20177,6.765672 l -13.32776,5.474298 c -1.01601,0.4211 -1.0459,1.796693 0,2.217793 z\" style=\"stroke-width:2.8964;fill-opacity:1\" /> <path d=\"m 233.09331,192.98627 -14.13459,-5.7831 c -3.40665,-1.40367 -6.12599,-3.95834 -7.62013,-7.1587 l -6.15587,-13.27868 c -0.47813,-1.03872 -2.03203,-1.03872 -2.51016,0 l -6.15587,13.27868 c -1.49414,3.20036 -4.21348,5.75503 -7.62013,7.1587 l -14.13459,5.81118 c -1.10567,0.44917 -1.10567,1.90898 0,2.35816 l 14.40354,5.97961 c 3.40664,1.40367 6.12598,3.98642 7.59024,7.21485 l 5.97658,13.02602 c 0.47812,1.03872 2.03203,1.03872 2.51016,0 l 6.15586,-13.25061 c 1.49415,-3.20036 4.21349,-5.75503 7.62013,-7.1587 l 14.1346,-5.7831 c 1.10566,-0.44917 1.10566,-1.90899 0,-2.35816 z\" style=\"stroke-width:2.8964;stroke-opacity:1\" /> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 24 24\"> <g transform=\"scale(1.2 1.2) translate(-2 -0.5)\"> <path d=\"M 22,12.5 A 1.49995,1.49995 0 0 0 20.50006,11 H 20 V 10 A 3,3 0 0 0 17,7 H 13 V 5.7226 a 2,2 0 1 0 -2,0 V 7 H 7 a 3,3 0 0 0 -3,3 v 1 H 3.49994 a 1.5,1.5 0 0 0 0,3 H 4 v 1 A 3.00128,3.00128 0 0 0 6.20251,17.89282 1.03113,1.03113 0 0 1 7,18.86975 v 0.716 a 0.99928,0.99928 0 0 0 1.00726,1.002 0.9792,0.9792 0 0 0 0.69983,-0.29486 l 2,-2 A 1,1 0 0 1 11.41425,18 H 17 a 3,3 0 0 0 3,-3 v -1 h 0.50006 A 1.49995,1.49995 0 0 0 22,12.5 Z M 19,15 a 2.00226,2.00226 0 0 1 -2,2 H 11.41425 A 1.987,1.987 0 0 0 10,17.58575 l -2,2 v -0.716 A 2.02082,2.02082 0 0 0 6.46771,16.92865 2.00439,2.00439 0 0 1 5,15 V 10 A 2.00226,2.00226 0 0 1 7,8 h 10 a 2.00222,2.00222 0 0 1 2,2 z M 10,12.5 A 1.5,1.5 0 1 1 8.5,11 1.5,1.5 0 0 1 10,12.5 Z m 7,0 A 1.5,1.5 0 1 1 15.5,11 1.5,1.5 0 0 1 17,12.5 Z\"/> </g> </svg> ";

View File

@@ -0,0 +1,11 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import type { AiAssistantSettings } from "./interface";
declare module 'jodit/config' {
interface Config {
aiAssistant: AiAssistantSettings;
}
}

91
node_modules/jodit/esm/plugins/ai-assistant/config.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import magicWandIcon from "./ai-assistant.svg.js";
import chatBotIcon from "./chat-bot.svg.js";
const aiAssistantDefaults = {
aiCommonPrefixPrompt: '',
aiCommonSuffixPrompt: '',
aiImproveWritingPrompt: 'It needs to be refined for better clarity, coherence, and overall quality. Please enhance the writing style while keeping the original meaning and language intact.',
aiMakeShorterPrompt: 'Please condense this content to make it more concise, while preserving the key messages, language and information.',
aiMakeLongerPrompt: 'Expand on this content to provide more detail, depth, and richness, without diverging from the original message and language.',
aiSimplifyLanguagePrompt: 'The language used here needs to be simplified for easier understanding, without altering the core information and the original language.',
aiSummarizePrompt: 'Provide a brief summary of this content, capturing the essential points in a concise manner. Preserve the original language and meaning.',
aiContinuePrompt: 'Continue the narrative or discussion from this content seamlessly, maintaining the same language, tone and style.',
aiChangeToneProfessionalPrompt: 'Adjust the tone to be professional, suitable for a formal business or academic setting, while retaining the original message and language.',
aiChangeToneFriendlyPrompt: 'It needs to be rewritten in a friendly tone while maintaining the original message and language. Please modify this content to be warm, approachable, and engaging.',
aiChangeToneFormalPrompt: 'Transform this content to have a formal tone, appropriate for official or serious contexts, without changing the main points and language.',
aiChangeToneCasualPrompt: 'Revise this content to have a casual, relaxed tone, making it feel more personal and less formal, without changing the original meaning and language.',
aiChangeToneDirectPrompt: 'Make the tone more direct, with straightforward language and a clear, assertive approach, without changing the original meaning and language.',
aiChangeToneConfidentPrompt: 'Infuse this content with a confident tone, showcasing assurance and decisiveness, without changing the original meaning and language.',
aiChangeStyleBusinessPrompt: 'Rewrite this content with a business-oriented style, focusing on clarity, efficiency, and professionalism, without changing the original meaning and language.',
aiChangeStyleLegalPrompt: 'Adapt this content to a legal style, incorporating appropriate terminology and formality typical of legal documents, without changing the original meaning and language.',
aiChangeStyleJournalismPrompt: 'Convert this content into a journalistic style, emphasizing factual accuracy, objectivity, and informative reporting, without changing the original meaning and language.',
aiChangeStylePoeticPrompt: 'Recreate this content with a poetic style, using expressive language, rhythm, and imagery to convey the message, without changing the original meaning and language.',
aiTranslateToSpanishPrompt: 'Translate this content into Spanish, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToFrenchPrompt: 'Translate this content into French, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToGermanPrompt: 'Translate this content into German, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToItalianPrompt: 'Translate this content into Italian, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToPortuguesePrompt: 'Translate this content into Portuguese, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToEnglishPrompt: 'Translate this content into English, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToChinesePrompt: 'Translate this content into Chinese, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToJapanesePrompt: 'Translate this content into Japanese, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToKoreanPrompt: 'Translate this content into Korean, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToRussianPrompt: 'Translate this content into Russian, ensuring the translation is accurate and maintains the original meaning.',
aiTranslateToArabicPrompt: 'Translate this content into Arabic, ensuring the translation is accurate and maintains the original meaning.'
};
Config.prototype.aiAssistant = aiAssistantDefaults;
Icon.set('ai-assistant', chatBotIcon);
Icon.set('ai-commands', magicWandIcon);
Config.prototype.controls['ai-commands'] = {
isDisabled(editor) {
return !editor.o.aiAssistant.aiAssistantCallback;
},
tooltip: 'AI Commands',
list: {
aiImproveWritingPrompt: 'Improve writing',
aiMakeShorterPrompt: 'Make shorter',
aiMakeLongerPrompt: 'Make longer',
aiSimplifyLanguagePrompt: 'Simplify language',
aiSummarizePrompt: 'Summarize',
aiContinuePrompt: 'Continue',
aiChangeToneProfessionalPrompt: 'Change tone to professional',
aiChangeToneFriendlyPrompt: 'Change tone to friendly',
aiChangeToneFormalPrompt: 'Change tone to formal',
aiChangeToneCasualPrompt: 'Change tone to casual',
aiChangeToneDirectPrompt: 'Change tone to direct',
aiChangeToneConfidentPrompt: 'Change tone to confident',
aiChangeStyleBusinessPrompt: 'Change style to business',
aiChangeStyleLegalPrompt: 'Change style to legal',
aiChangeStyleJournalismPrompt: 'Change style of journalism',
aiChangeStylePoeticPrompt: 'Change style to poetic',
aiTranslateToSpanishPrompt: 'Translate to Spanish',
aiTranslateToFrenchPrompt: 'Translate to French',
aiTranslateToGermanPrompt: 'Translate to German',
aiTranslateToItalianPrompt: 'Translate to Italian',
aiTranslateToPortuguesePrompt: 'Translate to Portuguese',
aiTranslateToEnglishPrompt: 'Translate to English',
aiTranslateToChinesePrompt: 'Translate to Chinese',
aiTranslateToJapanesePrompt: 'Translate to Japanese',
aiTranslateToKoreanPrompt: 'Translate to Korean',
aiTranslateToRussianPrompt: 'Translate to Russian',
aiTranslateToArabicPrompt: 'Translate to Arabic'
},
exec: (editor, event, { control }) => {
editor.e.fire('generateAiAssistantForm.ai-assistant', control.name);
}
};
Config.prototype.controls['ai-assistant'] = {
isDisabled(editor) {
return !editor.o.aiAssistant.aiAssistantCallback;
},
hotkeys: ['ctrl+a+i', 'cmd+a+i'],
tooltip: 'AI Assistant',
exec: (editor, current, close) => {
editor.e.fire('generateAiAssistantForm.ai-assistant');
}
};

View File

@@ -0,0 +1,70 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/ai-assistant
*/
export interface AiAssistantSettings {
/** Callback function for AI assistant to process and return the result */
aiAssistantCallback?: (prompt: string, htmlFragment: string) => Promise<string>;
/** Prompt for common prefix */
aiCommonPrefixPrompt: string;
/** Prompt for common suffix */
aiCommonSuffixPrompt: string;
/** Prompt for improving writing quality */
aiImproveWritingPrompt: string;
/** Prompt for making text shorter */
aiMakeShorterPrompt: string;
/** Prompt for making text longer */
aiMakeLongerPrompt: string;
/** Prompt for simplifying language */
aiSimplifyLanguagePrompt: string;
/** Prompt for summarizing text */
aiSummarizePrompt: string;
/** Prompt for continuing text */
aiContinuePrompt: string;
/** Prompt for changing tone to professional */
aiChangeToneProfessionalPrompt: string;
/** Prompt for changing tone to friendly */
aiChangeToneFriendlyPrompt: string;
/** Prompt for changing tone to formal */
aiChangeToneFormalPrompt: string;
/** Prompt for changing tone to casual */
aiChangeToneCasualPrompt: string;
/** Prompt for changing tone to direct */
aiChangeToneDirectPrompt: string;
/** Prompt for changing tone to confident */
aiChangeToneConfidentPrompt: string;
/** Prompt for changing style to business */
aiChangeStyleBusinessPrompt: string;
/** Prompt for changing style to legal */
aiChangeStyleLegalPrompt: string;
/** Prompt for changing style to journalism */
aiChangeStyleJournalismPrompt: string;
/** Prompt for changing style to poetic */
aiChangeStylePoeticPrompt: string;
/** Prompt for translating text to Spanish */
aiTranslateToSpanishPrompt: string;
/** Prompt for translating text to French */
aiTranslateToFrenchPrompt: string;
/** Prompt for translating text to German */
aiTranslateToGermanPrompt: string;
/** Prompt for translating text to Italian */
aiTranslateToItalianPrompt: string;
/** Prompt for translating text to Portuguese */
aiTranslateToPortuguesePrompt: string;
/** Prompt for translating text to English */
aiTranslateToEnglishPrompt: string;
/** Prompt for translating text to Chinese */
aiTranslateToChinesePrompt: string;
/** Prompt for translating text to Japanese */
aiTranslateToJapanesePrompt: string;
/** Prompt for translating text to Korean */
aiTranslateToKoreanPrompt: string;
/** Prompt for translating text to Russian */
aiTranslateToRussianPrompt: string;
/** Prompt for translating text to Arabic */
aiTranslateToArabicPrompt: string;
}

View File

@@ -0,0 +1,6 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
export {};

View File

@@ -0,0 +1,9 @@
declare const _exports: {
aiAssistant: string;
aiCommands: string;
'AI Assistant': string;
Prompt: string;
'Ask AI to improve generated text': string;
Submit: string;
};
export = _exports;

View File

@@ -0,0 +1,14 @@
"use strict";
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
export default {
aiAssistant: 'AI Assistant',
aiCommands: 'AI Commands',
'AI Assistant': 'AI Assistant',
Prompt: 'Prompt',
'Ask AI to improve generated text': 'Ask AI to improve generated text',
Submit: 'Submit'
};

View File

@@ -0,0 +1,40 @@
declare const _exports: {
aiAssistant: string;
aiCommands: string;
'AI Assistant': string;
'AI Commands': string;
'Improve writing': string;
'Make shorter': string;
'Make longer': string;
'Simplify language': string;
Summarize: string;
Continue: string;
Prompt: string;
Insert: string;
'Insert After': string;
'Try Again': string;
'Ask AI to improve generated text': string;
'Change tone to professional': string;
'Change tone to friendly': string;
'Change tone to formal': string;
'Change tone to casual': string;
'Change tone to direct': string;
'Change tone to confident': string;
'Change tone to business': string;
'Change style to legal': string;
'Change style to journalism': string;
'Change style to poetic': string;
'Translate to Spanish': string;
'Translate to French': string;
'Translate to German': string;
'Translate to Italian': string;
'Translate to Portuguese': string;
'Translate to English': string;
'Translate to Chinese': string;
'Translate to Japanese': string;
'Translate to Korean': string;
'Translate to Russian': string;
'Translate to Arabic': string;
Submit: string;
};
export = _exports;

View File

@@ -0,0 +1,45 @@
"use strict";
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
export default {
aiAssistant: 'Asistente de IA',
aiCommands: 'Comandos de IA',
'AI Assistant': 'Asistente de IA',
'AI Commands': 'Comandos de IA',
'Improve writing': 'Mejorar escritura',
'Make shorter': 'Hacer más corto',
'Make longer': 'Hacer más largo',
'Simplify language': 'Simplificar lenguaje',
Summarize: 'Resumir',
Continue: 'Continuar',
Prompt: 'Solicitud',
Insert: 'Insertar',
'Insert After': 'Insertar después',
'Try Again': 'Intentar de nuevo',
'Ask AI to improve generated text': 'Pida a la IA que mejore el texto generado',
'Change tone to professional': 'Cambiar tono a profesional',
'Change tone to friendly': 'Cambiar tono a amigable',
'Change tone to formal': 'Cambiar tono a formal',
'Change tone to casual': 'Cambiar tono a casual',
'Change tone to direct': 'Cambiar tono a directo',
'Change tone to confident': 'Cambiar tono a confiado',
'Change tone to business': 'Cambiar tono a empresarial',
'Change style to legal': 'Cambiar estilo a legal',
'Change style to journalism': 'Cambiar estilo a periodístico',
'Change style to poetic': 'Cambiar estilo a poético',
'Translate to Spanish': 'Traducir al español',
'Translate to French': 'Traducir al francés',
'Translate to German': 'Traducir al alemán',
'Translate to Italian': 'Traducir al italiano',
'Translate to Portuguese': 'Traducir al portugués',
'Translate to English': 'Traducir al inglés',
'Translate to Chinese': 'Traducir al chino',
'Translate to Japanese': 'Traducid al japonés',
'Translate to Korean': 'Traducir al coreano',
'Translate to Russian': 'Traducir al ruso',
'Translate to Arabic': 'Traducir al árabe',
Submit: 'Enviar'
};

View File

@@ -0,0 +1,11 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/ai-assistant
*/
import * as en from "./en";
import * as es from "./es";
export { en, es };

View File

@@ -0,0 +1,11 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/ai-assistant
*/
import * as en from "./en.js";
import * as es from "./es.js";
export { en, es };

View File

@@ -0,0 +1,37 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/ai-assistant
*/
import type { IJodit } from "../../../types/index";
import { UITextArea } from "../../../core/ui/index";
import { UIElement } from "../../../core/ui/element";
export declare class UiAiAssistant extends UIElement<IJodit> {
private __body;
private __buttons;
private __results;
private __spinner;
private __error;
promptInput: UITextArea;
private __insertAfterButton;
private __submitButton;
private __tryAgainButton;
private __insertButton;
private __formAiAssistant;
className(): string;
constructor(jodit: IJodit, { onInsert, onInsertAfter }: {
onInsert: (html: string) => void;
onInsertAfter: (html: string) => void;
});
protected render(): string;
setPrompt(prompt: string): void;
private __aiResult;
protected onAiAssistentResponse(result: string): void;
protected onAiAssistentError(error: string): void;
protected onChangePromptValue(): void;
private __toggleSubmitButton;
private __toggleInsertButton;
}

View File

@@ -0,0 +1,152 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { component } from "../../../core/decorators/component/component.js";
import { watch } from "../../../core/decorators/watch/watch.js";
import { Dom } from "../../../core/dom/dom.js";
import { isString } from "../../../core/helpers/checker/is-string.js";
import { Button, UIBlock, UIForm, UITextArea } from "../../../core/ui/index.js";
import { UIElement } from "../../../core/ui/element.js";
let UiAiAssistant = class UiAiAssistant extends UIElement {
className() {
return 'UIAiAssistant';
}
constructor(jodit, { onInsert, onInsertAfter }) {
super(jodit);
this.__aiResult = '';
this.__error = this.getElm('error');
this.__body = this.getElm('body');
this.__buttons = this.getElm('buttons');
this.__results = this.getElm('results');
this.__spinner = this.getElm('spinner');
this.__insertButton = Button(jodit, '', 'Insert', 'primary').onAction(() => onInsert(this.__aiResult));
this.__insertAfterButton = Button(jodit, '', 'Insert After', 'initial').onAction(() => onInsertAfter(this.__aiResult));
const onSubmit = () => {
if (this.__formAiAssistant.validate()) {
this.__formAiAssistant.submit();
this.__toggleInsertButton(true);
this.__toggleSubmitButton(true);
}
};
this.__submitButton = Button(jodit, 'ai-assistant', '').onAction(onSubmit);
this.__tryAgainButton = Button(jodit, 'update', '', 'initial').onAction(onSubmit);
this.promptInput = new UITextArea(jodit, {
name: 'prompt',
required: true,
label: 'Prompt',
placeholder: 'Ask AI to improve generated text',
className: this.getFullElName('prompt-row-input')
});
const buttonsBLock = new UIBlock(jodit, [
this.__insertButton,
this.__insertAfterButton,
this.__tryAgainButton
], {
className: this.getFullElName('prompt-row')
});
this.__formAiAssistant = new UIForm(jodit, [
new UIBlock(jodit, [this.promptInput, this.__submitButton], {
className: this.getFullElName('prompt-row')
})
]).onSubmit((data) => {
this.__error.textContent = '';
this.setMod('loading', true);
jodit.e.fire('invokeAiAssistant', data.prompt);
const hideMod = this.getFullElName('', 'hide', 'true');
this.__results.classList.remove(hideMod);
this.__buttons.classList.remove(hideMod);
Dom.detach(this.__results);
this.__results.appendChild(this.__spinner);
this.__insertButton.focus();
});
this.__buttons.appendChild(buttonsBLock.container);
this.__body.appendChild(this.__formAiAssistant.container);
this.onChangePromptValue();
}
render() {
return `<div>
<div class="&__container">
<div class="&__error"></div>
<div class="&__body"></div>
<div class="&__buttons &_hide_true"></div>
<div class="&__results &_hide_true">
<div class="&__spinner"></div>
</div>
</div>
</div>`;
}
setPrompt(prompt) {
if (prompt) {
const { jodit } = this;
const promptOpt = jodit.o.aiAssistant[prompt];
const { aiCommonPrefixPrompt, aiCommonSuffixPrompt } = jodit.o.aiAssistant;
this.promptInput.value = [
aiCommonPrefixPrompt,
isString(promptOpt) ? promptOpt : '',
aiCommonSuffixPrompt
]
.filter(Boolean)
.join(' ');
this.__toggleInsertButton(true);
if (this.promptInput.value) {
this.__formAiAssistant.submit();
this.__toggleSubmitButton(true);
}
}
this.promptInput.focus();
}
onAiAssistentResponse(result) {
this.setMod('loading', false);
Dom.detach(this.__results);
this.__aiResult = result;
this.__results.appendChild(this.jodit.c.fromHTML(result));
this.__toggleSubmitButton(false);
this.__toggleInsertButton(false);
}
onAiAssistentError(error) {
this.__aiResult = '';
this.setMod('loading', false);
this.__error.textContent = error;
Dom.detach(this.__results);
this.__toggleSubmitButton(false);
const hideMod = this.getFullElName('', 'hide', 'true');
this.__results.classList.add(hideMod);
this.__toggleInsertButton(true);
}
onChangePromptValue() {
this.__toggleSubmitButton(!this.promptInput.value);
}
__toggleSubmitButton(value) {
this.__submitButton.state.disabled = value;
this.__tryAgainButton.state.disabled = value;
}
__toggleInsertButton(value) {
this.__insertButton.state.disabled = value;
this.__insertAfterButton.state.disabled = value;
}
};
__decorate([
watch(':ai-assistant-response')
], UiAiAssistant.prototype, "onAiAssistentResponse", null);
__decorate([
watch(':ai-assistant-error')
], UiAiAssistant.prototype, "onAiAssistentError", null);
__decorate([
watch('promptInput:change')
], UiAiAssistant.prototype, "onChangePromptValue", null);
UiAiAssistant = __decorate([
component
], UiAiAssistant);
export { UiAiAssistant };

72
node_modules/jodit/esm/plugins/all.d.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/README.md]]
* @packageDocumentation
* @module plugins
*/
import "./about/about";
import "./add-new-line/add-new-line";
import "./backspace/backspace";
import "./delete/delete";
import "./bold/bold";
import "./class-span/class-span";
import "./clean-html/clean-html";
import "./clipboard/clipboard";
import "./color/color";
import "./copy-format/copy-format";
import "./drag-and-drop/drag-and-drop";
import "./drag-and-drop-element/drag-and-drop-element";
import "./enter/enter";
import "./file/file";
import "./focus/focus";
import "./font/font";
import "./format-block/format-block";
import "./fullsize/fullsize";
import "./hotkeys/hotkeys";
import "./hr/hr";
import "./iframe/iframe";
import "./image/image";
import "./image-processor/image-processor";
import "./image-properties/image-properties";
import "./indent/indent";
import "./inline-popup/inline-popup";
import "./justify/justify";
import "./key-arrow-outside/key-arrow-outside";
import "./limit/limit";
import "./line-height/line-height";
import "./link/link";
import "./media/media";
import "./mobile/mobile";
import "./ordered-list/ordered-list";
import "./paste/paste";
import "./paste-from-word/paste-from-word";
import "./paste-storage/paste-storage";
import "./placeholder/placeholder";
import "./powered-by-jodit/powered-by-jodit";
import "./preview/preview";
import "./print/print";
import "./redo-undo/redo-undo";
import "./resize-cells/resize-cells";
import "./resize-handler/resize-handler";
import "./resizer/resizer";
import "./search/search";
import "./select/select";
import "./select-cells/select-cells";
import "./size/size";
import "./source/source";
import "./spellcheck/spellcheck";
import "./stat/stat";
import "./sticky/sticky";
import "./symbols/symbols";
import "./ai-assistant/ai-assistant";
import "./tab/tab";
import "./table/table";
import "./table-keyboard-navigation/table-keyboard-navigation";
import "./video/video";
import "./wrap-nodes/wrap-nodes";
import "./dtd/dtd";
import "./xpath/xpath";

76
node_modules/jodit/esm/plugins/all.js generated vendored Normal file
View File

@@ -0,0 +1,76 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/README.md]]
* @packageDocumentation
* @module plugins
*/
import "./about/about.js";
import "./add-new-line/add-new-line.js";
import "./backspace/backspace.js";
// import 'jodit/plugins/debug/debug';
import "./delete/delete.js";
import "./bold/bold.js";
import "./class-span/class-span.js";
import "./clean-html/clean-html.js";
import "./clipboard/clipboard.js";
import "./color/color.js";
import "./copy-format/copy-format.js";
import "./drag-and-drop/drag-and-drop.js";
import "./drag-and-drop-element/drag-and-drop-element.js";
import "./enter/enter.js";
import "./file/file.js";
import "./focus/focus.js";
import "./font/font.js";
import "./format-block/format-block.js";
import "./fullsize/fullsize.js";
import "./hotkeys/hotkeys.js";
import "./hr/hr.js";
import "./iframe/iframe.js";
import "./image/image.js";
import "./image-processor/image-processor.js";
import "./image-properties/image-properties.js";
import "./indent/indent.js";
import "./inline-popup/inline-popup.js";
import "./justify/justify.js";
import "./key-arrow-outside/key-arrow-outside.js";
import "./limit/limit.js";
import "./line-height/line-height.js";
import "./link/link.js";
import "./media/media.js";
import "./mobile/mobile.js";
import "./ordered-list/ordered-list.js";
import "./paste/paste.js";
import "./paste-from-word/paste-from-word.js";
import "./paste-storage/paste-storage.js";
import "./placeholder/placeholder.js";
import "./powered-by-jodit/powered-by-jodit.js";
import "./preview/preview.js";
import "./print/print.js";
import "./redo-undo/redo-undo.js";
import "./resize-cells/resize-cells.js";
import "./resize-handler/resize-handler.js";
import "./resizer/resizer.js";
import "./search/search.js";
import "./select/select.js";
import "./select-cells/select-cells.js";
import "./size/size.js";
import "./source/source.js";
import "./spellcheck/spellcheck.js";
import "./stat/stat.js";
import "./sticky/sticky.js";
import "./symbols/symbols.js";
import "./ai-assistant/ai-assistant.js";
import "./tab/tab.js";
import "./table/table.js";
import "./table-keyboard-navigation/table-keyboard-navigation.js";
import "./video/video.js";
import "./wrap-nodes/wrap-nodes.js";
import "./dtd/dtd.js";
import "./xpath/xpath.js";
// JODIT-START:FAT
import "./speech-recognize/speech-recognize.js";
// JODIT-END:FAT

View File

@@ -0,0 +1,26 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/backspace/README.md]]
* @packageDocumentation
* @module plugins/backspace
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../core/plugin/index";
import "./config";
export declare class backspace extends Plugin {
static requires: string[];
protected afterInit(jodit: IJodit): void;
protected beforeDestruct(jodit: IJodit): void;
/**
* Listener BackSpace or Delete button
*/
private onDelete;
/**
* Remove node and replace cursor position out of it
*/
private safeRemoveEmptyNode;
}

127
node_modules/jodit/esm/plugins/backspace/backspace.js generated vendored Normal file
View File

@@ -0,0 +1,127 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INVISIBLE_SPACE, IS_PROD } from "../../core/constants.js";
import { Dom } from "../../core/dom/index.js";
import { pluginSystem } from "../../core/global.js";
import { isFunction } from "../../core/helpers/checker/is-function.js";
import { Plugin } from "../../core/plugin/index.js";
import { moveNodeInsideStart } from "../../core/selection/helpers/index.js";
import "./config.js";
import { checkNotCollapsed } from "./cases/check-not-collapsed.js";
import { cases } from "./cases/index.js";
export class backspace extends Plugin {
afterInit(jodit) {
jodit
.registerCommand('deleteButton', {
exec: () => this.onDelete(false),
hotkeys: jodit.o.delete.hotkeys.delete
}, {
stopPropagation: false
})
.registerCommand('backspaceButton', {
exec: () => this.onDelete(true),
hotkeys: jodit.o.delete.hotkeys.backspace
}, {
stopPropagation: false
})
.registerCommand('deleteWordButton', {
exec: () => this.onDelete(false, 'word'),
hotkeys: jodit.o.delete.hotkeys.deleteWord
})
.registerCommand('backspaceWordButton', {
exec: () => this.onDelete(true, 'word'),
hotkeys: jodit.o.delete.hotkeys.backspaceWord
})
.registerCommand('deleteSentenceButton', {
exec: () => this.onDelete(false, 'sentence'),
hotkeys: jodit.o.delete.hotkeys.deleteSentence
})
.registerCommand('backspaceSentenceButton', {
exec: () => this.onDelete(true, 'sentence'),
hotkeys: jodit.o.delete.hotkeys.backspaceSentence
});
}
beforeDestruct(jodit) {
jodit.e.off('afterCommand.delete');
}
/**
* Listener BackSpace or Delete button
*/
onDelete(backspace, mode = 'char') {
const jodit = this.j;
const sel = jodit.selection;
if (!sel.isFocused()) {
sel.focus();
}
if (checkNotCollapsed(jodit)) {
return false;
}
const range = sel.range;
const fakeNode = jodit.createInside.text(INVISIBLE_SPACE);
try {
Dom.safeInsertNode(range, fakeNode);
if (!Dom.isOrContains(jodit.editor, fakeNode)) {
return;
}
if (jodit.e.fire('backSpaceBeforeCases', backspace, fakeNode)) {
return false;
}
moveNodeInsideStart(jodit, fakeNode, backspace);
if (cases.some((func) => {
if (isFunction(func) &&
func(jodit, fakeNode, backspace, mode)) {
if (!IS_PROD) {
console.info('Remove case:', func.name);
}
return true;
}
})) {
return false;
}
}
catch (e) {
if (!IS_PROD) {
console.error(e);
}
throw e;
}
finally {
jodit.e.fire('backSpaceAfterDelete', backspace, fakeNode);
this.safeRemoveEmptyNode(fakeNode);
}
return false;
}
/**
* Remove node and replace cursor position out of it
*/
safeRemoveEmptyNode(fakeNode) {
var _a, _b;
const { range } = this.j.s;
if (range.startContainer === fakeNode) {
if (fakeNode.previousSibling) {
if (Dom.isText(fakeNode.previousSibling)) {
range.setStart(fakeNode.previousSibling, (_b = (_a = fakeNode.previousSibling.nodeValue) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0);
}
else {
range.setStartAfter(fakeNode.previousSibling);
}
}
else if (fakeNode.nextSibling) {
if (Dom.isText(fakeNode.nextSibling)) {
range.setStart(fakeNode.nextSibling, 0);
}
else {
range.setStartBefore(fakeNode.nextSibling);
}
}
range.collapse(true);
this.j.s.selectRange(range);
}
Dom.safeRemove(fakeNode);
}
}
backspace.requires = ['hotkeys'];
pluginSystem.add('backspace', backspace);

View File

@@ -0,0 +1,14 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Check if two separate elements can be connected
* @private
*/
export declare function checkJoinNeighbors(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,62 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { LIST_TAGS } from "../../../core/constants.js";
import { Dom } from "../../../core/dom/dom.js";
import { getMoveFilter } from "../helpers.js";
/**
* Check if two separate elements can be connected
* @private
*/
export function checkJoinNeighbors(jodit, fakeNode, backspace) {
let nextBox = fakeNode, mainClosestBox = nextBox;
// Find the main big closest element
while (nextBox &&
!Dom.findNotEmptySibling(nextBox, backspace) &&
nextBox.parentElement !== jodit.editor) {
nextBox = nextBox.parentElement;
mainClosestBox = nextBox;
}
if (Dom.isElement(mainClosestBox) &&
Dom.isContentEditable(mainClosestBox, jodit.editor)) {
const sibling = Dom.findNotEmptySibling(mainClosestBox, backspace);
if (sibling &&
(checkMoveListContent(jodit, mainClosestBox, sibling, backspace) ||
moveContentAndRemoveEmpty(jodit, mainClosestBox, sibling, backspace))) {
jodit.s.setCursorBefore(fakeNode);
return true;
}
}
return false;
}
function checkMoveListContent(jodit, mainClosestBox, sibling, backspace) {
// Process UL/LI/OL cases
const siblingIsList = Dom.isTag(sibling, LIST_TAGS);
const boxIsList = Dom.isTag(mainClosestBox, LIST_TAGS);
const elementChild = (elm, side) => side ? elm.firstElementChild : elm.lastElementChild;
if (boxIsList) {
sibling = jodit.createInside.element(jodit.o.enterBlock);
Dom.before(mainClosestBox, sibling);
return moveContentAndRemoveEmpty(jodit, elementChild(mainClosestBox, backspace), sibling, backspace);
}
if (sibling && siblingIsList && !boxIsList) {
return moveContentAndRemoveEmpty(jodit, mainClosestBox, elementChild(sibling, !backspace), backspace);
}
return false;
}
function moveContentAndRemoveEmpty(jodit, mainClosestBox, sibling, backspace) {
// Move content and remove empty nodes
if (mainClosestBox && Dom.isElement(sibling)) {
Dom.moveContent(mainClosestBox, sibling, !backspace, getMoveFilter(jodit));
let remove = mainClosestBox;
while (remove && remove !== jodit.editor && Dom.isEmpty(remove)) {
const parent = remove.parentElement;
Dom.safeRemove(remove);
remove = parent;
}
return true;
}
return false;
}

View File

@@ -0,0 +1,23 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Try join two UL elements
*
* @example
* ```html
* <ul><li>one</li></ul>|<ol><li>two</li></ol>
* ```
* Result
* ```html
* <ul><li>one|</li><li>two</li></ul>
* ```
* @private
*/
export declare function checkJoinTwoLists(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,39 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../core/dom/dom.js";
import { call } from "../../../core/helpers/utils/utils.js";
import { getMoveFilter } from "../helpers.js";
/**
* Try join two UL elements
*
* @example
* ```html
* <ul><li>one</li></ul>|<ol><li>two</li></ol>
* ```
* Result
* ```html
* <ul><li>one|</li><li>two</li></ul>
* ```
* @private
*/
export function checkJoinTwoLists(jodit, fakeNode, backspace) {
const next = Dom.findSibling(fakeNode, backspace), prev = Dom.findSibling(fakeNode, !backspace);
if (!Dom.closest(fakeNode, Dom.isElement, jodit.editor) &&
Dom.isList(next) &&
Dom.isList(prev) &&
Dom.isTag(next.lastElementChild, 'li') &&
Dom.isTag(prev.firstElementChild, 'li')) {
const { setCursorBefore, setCursorAfter } = jodit.s;
const target = next.lastElementChild, second = prev.firstElementChild;
call(!backspace ? Dom.append : Dom.prepend, second, fakeNode);
Dom.moveContent(prev, next, !backspace, getMoveFilter(jodit));
Dom.safeRemove(prev);
call(backspace ? Dom.append : Dom.prepend, target, fakeNode);
call(backspace ? setCursorBefore : setCursorAfter, fakeNode);
return true;
}
return false;
}

View File

@@ -0,0 +1,23 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* On Not collapsed selection - should only remove whole selected content
*
* @example
* ```html
* <p>first | stop</p><p>second | stop</p>
* ```
* result
* ```html
* <p>first | stop</p>
* ```
* @private
*/
export declare function checkNotCollapsed(jodit: IJodit): boolean;

View File

@@ -0,0 +1,25 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* On Not collapsed selection - should only remove whole selected content
*
* @example
* ```html
* <p>first | stop</p><p>second | stop</p>
* ```
* result
* ```html
* <p>first | stop</p>
* ```
* @private
*/
export function checkNotCollapsed(jodit) {
if (!jodit.s.isCollapsed()) {
jodit.execCommand('Delete');
return true;
}
return false;
}

View File

@@ -0,0 +1,24 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
import type { DeleteMode } from "../interface";
/**
* Check possibility the char can be removed
*
* @example
* ```html
* te|st
* ```
* result
* ```html
* t|st
* ```
* @private
*/
export declare function checkRemoveChar(jodit: IJodit, fakeNode: Node, backspace: boolean, mode: DeleteMode): boolean;

View File

@@ -0,0 +1,160 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INVISIBLE_SPACE, NBSP_SPACE } from "../../../core/constants.js";
import { Dom } from "../../../core/dom/index.js";
import { call, isVoid, toArray, trimInv } from "../../../core/helpers/index.js";
import { findMostNestedNeighbor } from "../helpers.js";
/**
* Check possibility the char can be removed
*
* @example
* ```html
* te|st
* ```
* result
* ```html
* t|st
* ```
* @private
*/
// eslint-disable-next-line complexity
export function checkRemoveChar(jodit, fakeNode, backspace, mode) {
var _a, _b;
const step = backspace ? -1 : 1;
const anotherSibling = Dom.sibling(fakeNode, !backspace);
let sibling = Dom.sibling(fakeNode, backspace);
let removeNeighbor = null;
let charRemoved = false;
let removed;
if (!sibling) {
sibling = getNextInlineSibling(fakeNode, backspace, jodit.editor);
}
while (sibling && (Dom.isText(sibling) || Dom.isInlineBlock(sibling))) {
while (Dom.isInlineBlock(sibling)) {
sibling = (backspace ? sibling === null || sibling === void 0 ? void 0 : sibling.lastChild : sibling === null || sibling === void 0 ? void 0 : sibling.firstChild);
}
if (!sibling) {
break;
}
if ((_a = sibling.nodeValue) === null || _a === void 0 ? void 0 : _a.length) {
removed = tryRemoveChar(sibling, backspace, step, anotherSibling);
if (!sibling.nodeValue.length &&
Dom.isInlineBlock(sibling.parentNode)) {
sibling.nodeValue = INVISIBLE_SPACE;
}
}
if (!((_b = sibling.nodeValue) === null || _b === void 0 ? void 0 : _b.length)) {
removeNeighbor = sibling;
}
if (!isVoid(removed) && removed !== INVISIBLE_SPACE) {
checkRepeatRemoveCharAction(backspace, sibling, fakeNode, mode, removed, jodit);
charRemoved = true;
break;
}
const nextSibling = getNextInlineSibling(sibling, backspace, jodit.editor);
if (removeNeighbor) {
Dom.safeRemove(removeNeighbor);
removeNeighbor = null;
}
sibling = nextSibling;
}
if (removeNeighbor) {
Dom.safeRemove(removeNeighbor);
removeNeighbor = null;
}
if (charRemoved) {
removeEmptyForParent(fakeNode, 'a');
addBRInsideEmptyBlock(jodit, fakeNode);
jodit.s.setCursorBefore(fakeNode);
if (Dom.isTag(fakeNode.previousSibling, 'br') &&
!Dom.findNotEmptySibling(fakeNode, false)) {
Dom.after(fakeNode, jodit.createInside.element('br'));
}
}
return charRemoved;
}
function getNextInlineSibling(sibling, backspace, root) {
let nextSibling = Dom.sibling(sibling, backspace);
if (!nextSibling && sibling.parentNode && sibling.parentNode !== root) {
nextSibling = findMostNestedNeighbor(sibling, !backspace, root, true);
}
return nextSibling;
}
/**
* Helper removes all empty inline parents
*/
function removeEmptyForParent(node, tags) {
let parent = node.parentElement;
while (parent && Dom.isInlineBlock(parent) && Dom.isTag(parent, tags)) {
const p = parent.parentElement;
if (Dom.isEmpty(parent)) {
Dom.after(parent, node);
Dom.safeRemove(parent);
}
parent = p;
}
}
/**
* Helper add BR element inside empty block element
*/
function addBRInsideEmptyBlock(jodit, node) {
if (node.parentElement !== jodit.editor &&
Dom.isBlock(node.parentElement) &&
Dom.each(node.parentElement, Dom.isEmptyTextNode)) {
Dom.after(node, jodit.createInside.element('br'));
}
}
function tryRemoveChar(sibling, backspace, step, anotherSibling) {
// For Unicode escapes
let value = toArray(sibling.nodeValue);
const length = value.length;
let index = backspace ? length - 1 : 0;
if (value[index] === INVISIBLE_SPACE) {
while (value[index] === INVISIBLE_SPACE) {
index += step;
}
}
const removed = value[index];
if (value[index + step] === INVISIBLE_SPACE) {
index += step;
while (value[index] === INVISIBLE_SPACE) {
index += step;
}
index += backspace ? 1 : -1;
}
if (backspace && index < 0) {
value = [];
}
else {
value = value.slice(backspace ? 0 : index + 1, backspace ? index : length);
}
replaceSpaceOnNBSP(anotherSibling, backspace, value);
sibling.nodeValue = value.join('');
return removed;
}
function replaceSpaceOnNBSP(anotherSibling, backspace, value) {
var _a;
if (!anotherSibling ||
!Dom.isText(anotherSibling) ||
(!backspace ? / $/ : /^ /).test((_a = anotherSibling.nodeValue) !== null && _a !== void 0 ? _a : '') ||
!trimInv(anotherSibling.nodeValue || '').length) {
for (let i = backspace ? value.length - 1 : 0; backspace ? i >= 0 : i < value.length; i += backspace ? -1 : 1) {
if (value[i] === ' ') {
value[i] = NBSP_SPACE;
}
else {
break;
}
}
}
}
function checkRepeatRemoveCharAction(backspace, sibling, fakeNode, mode, removed, jodit) {
call(backspace ? Dom.after : Dom.before, sibling, fakeNode);
if (mode === 'sentence' ||
(mode === 'word' && removed !== ' ' && removed !== NBSP_SPACE)) {
checkRemoveChar(jodit, fakeNode, backspace, mode);
}
}

View File

@@ -0,0 +1,14 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Checks if a non-editable element can be deleted
* @private
*/
export declare function checkRemoveContentNotEditable(jodit: IJodit, fakeNode: Text, backspace: boolean): boolean;

View File

@@ -0,0 +1,29 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../core/dom/index.js";
import { call } from "../../../core/helpers/index.js";
import { moveNodeInsideStart } from "../../../core/selection/helpers/index.js";
/**
* Checks if a non-editable element can be deleted
* @private
*/
export function checkRemoveContentNotEditable(jodit, fakeNode, backspace) {
let neighbor = Dom.findSibling(fakeNode, backspace);
if (!neighbor &&
fakeNode.parentElement &&
fakeNode.parentElement !== jodit.editor) {
neighbor = Dom.findSibling(fakeNode.parentElement, backspace);
}
if (Dom.isElement(neighbor) &&
!Dom.isContentEditable(neighbor, jodit.editor)) {
call(backspace ? Dom.before : Dom.after, neighbor, fakeNode);
Dom.safeRemove(neighbor);
moveNodeInsideStart(jodit, fakeNode, backspace);
call(backspace ? jodit.s.setCursorBefore : jodit.s.setCursorAfter, fakeNode);
return true;
}
return false;
}

View File

@@ -0,0 +1,23 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Check if it is possible to remove an empty adjacent element.
*
* @example
* ```html
* <p><br></p><p>|second stop</p>
* ```
* result
* ```html
* <p>|second stop</p>
* ```
* @private
*/
export declare function checkRemoveEmptyNeighbor(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,32 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../core/dom/dom.js";
/**
* Check if it is possible to remove an empty adjacent element.
*
* @example
* ```html
* <p><br></p><p>|second stop</p>
* ```
* result
* ```html
* <p>|second stop</p>
* ```
* @private
*/
export function checkRemoveEmptyNeighbor(jodit, fakeNode, backspace) {
const parent = Dom.closest(fakeNode, Dom.isElement, jodit.editor);
if (!parent) {
return false;
}
const neighbor = Dom.findNotEmptySibling(parent, backspace);
if (neighbor && Dom.isEmpty(neighbor)) {
Dom.safeRemove(neighbor);
jodit.s.setCursorBefore(fakeNode);
return true;
}
return false;
}

View File

@@ -0,0 +1,24 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Check if the current empty item can be removed
*
* @example
* ```html
* <p>first stop</p><p>|<br></p>
* ```
* result
* ```html
* <p>first stop|</p>
* ```
*
* @private
*/
export declare function checkRemoveEmptyParent(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,55 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INSEPARABLE_TAGS } from "../../../core/constants.js";
import { Dom } from "../../../core/dom/index.js";
import { checkJoinTwoLists } from "./check-join-two-lists.js";
/**
* Check if the current empty item can be removed
*
* @example
* ```html
* <p>first stop</p><p>|<br></p>
* ```
* result
* ```html
* <p>first stop|</p>
* ```
*
* @private
*/
export function checkRemoveEmptyParent(jodit, fakeNode, backspace) {
let found = false;
const { setCursorBefore, setCursorIn } = jodit.s;
let prn = Dom.closest(fakeNode, Dom.isElement, jodit.editor);
if (!prn || !Dom.isEmpty(prn)) {
return false;
}
const neighbor = Dom.findNotEmptyNeighbor(fakeNode, backspace, jodit.editor);
do {
if (prn && Dom.isEmpty(prn) && !Dom.isCell(prn)) {
Dom.after(prn, fakeNode);
const tmp = Dom.closest(prn, n => Dom.isElement(n) && n !== prn, jodit.editor);
Dom.safeRemove(prn);
found = true;
prn = tmp;
}
else {
break;
}
} while (prn);
if (found && checkJoinTwoLists(jodit, fakeNode, backspace)) {
return true;
}
if (neighbor &&
!Dom.isText(neighbor) &&
!Dom.isTag(neighbor, INSEPARABLE_TAGS)) {
setCursorIn(neighbor, !backspace);
}
else {
setCursorBefore(fakeNode);
}
return found;
}

View File

@@ -0,0 +1,24 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Check possibility inseparable Element can be removed (img, hr etc.)
*
* @example
* ```html
* <p>first second <img>| stop</p>
* ```
* result
* ```html
* <p>first second | stop</p>
* ```
*
* @private
*/
export declare function checkRemoveUnbreakableElement(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,39 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INSEPARABLE_TAGS } from "../../../core/constants.js";
import { Dom } from "../../../core/dom/dom.js";
import { checkRemoveEmptyParent } from "./check-remove-empty-parent.js";
/**
* Check possibility inseparable Element can be removed (img, hr etc.)
*
* @example
* ```html
* <p>first second <img>| stop</p>
* ```
* result
* ```html
* <p>first second | stop</p>
* ```
*
* @private
*/
export function checkRemoveUnbreakableElement(jodit, fakeNode, backspace) {
const neighbor = Dom.findSibling(fakeNode, backspace);
if (Dom.isElement(neighbor) &&
(Dom.isTag(neighbor, INSEPARABLE_TAGS) || Dom.isEmpty(neighbor))) {
Dom.safeRemove(neighbor);
if (Dom.isTag(neighbor, 'br') &&
!Dom.findNotEmptySibling(fakeNode, false)) {
Dom.after(fakeNode, jodit.createInside.element('br'));
}
jodit.s.setCursorBefore(fakeNode);
if (Dom.isTag(neighbor, 'br')) {
checkRemoveEmptyParent(jodit, fakeNode, backspace);
}
return true;
}
return false;
}

View File

@@ -0,0 +1,24 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* Inside the CELL table - nothing to do
*
* @example
* ```html
* <table><tr><td>|test</td></tr></table>
* ```
* result
* ```html
* <table><tr><td>|test</td></tr></table>
* ```
*
* @private
*/
export declare function checkTableCell(jodit: IJodit, fakeNode: Node): boolean;

View File

@@ -0,0 +1,27 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../core/dom/dom.js";
/**
* Inside the CELL table - nothing to do
*
* @example
* ```html
* <table><tr><td>|test</td></tr></table>
* ```
* result
* ```html
* <table><tr><td>|test</td></tr></table>
* ```
*
* @private
*/
export function checkTableCell(jodit, fakeNode) {
const cell = fakeNode.parentElement;
if (Dom.isCell(cell)) {
return true;
}
return false;
}

View File

@@ -0,0 +1,24 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit } from "../../../types/index";
/**
* For the first item in a list on backspace, try to move his content in new P
*
* @example
* ```html
* <ul><li>|first</li><li>second</li></ul>
* ```
* Result
* ```html
* <p>|first</p><ul><li>second</li></ul>
* ```
*
* @private
*/
export declare function checkUnwrapFirstListItem(jodit: IJodit, fakeNode: Node, backspace: boolean): boolean;

View File

@@ -0,0 +1,41 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../core/dom/dom.js";
import { call } from "../../../core/helpers/utils/index.js";
/**
* For the first item in a list on backspace, try to move his content in new P
*
* @example
* ```html
* <ul><li>|first</li><li>second</li></ul>
* ```
* Result
* ```html
* <p>|first</p><ul><li>second</li></ul>
* ```
*
* @private
*/
export function checkUnwrapFirstListItem(jodit, fakeNode, backspace) {
var _a;
const li = Dom.closest(fakeNode, Dom.isElement, jodit.editor);
const { s } = jodit;
if (Dom.isLeaf(li) &&
((_a = li === null || li === void 0 ? void 0 : li.parentElement) === null || _a === void 0 ? void 0 : _a[backspace ? 'firstElementChild' : 'lastElementChild']) === li &&
s.cursorInTheEdge(backspace, li)) {
const ul = li.parentElement;
const p = jodit.createInside.element(jodit.o.enterBlock);
call(backspace ? Dom.before : Dom.after, ul, p);
Dom.moveContent(li, p);
Dom.safeRemove(li);
if (Dom.isEmpty(ul)) {
Dom.safeRemove(ul);
}
call(backspace ? s.setCursorBefore : s.setCursorAfter, fakeNode);
return true;
}
return false;
}

View File

@@ -0,0 +1,11 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { checkRemoveChar } from "./check-remove-char";
import { checkRemoveContentNotEditable } from "./check-remove-content-not-editable";
/**
* @private
*/
export declare const cases: (typeof checkRemoveContentNotEditable | typeof checkRemoveChar)[];

View File

@@ -0,0 +1,31 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import { checkJoinNeighbors } from "./check-join-neighbors.js";
import { checkJoinTwoLists } from "./check-join-two-lists.js";
import { checkRemoveChar } from "./check-remove-char.js";
import { checkRemoveContentNotEditable } from "./check-remove-content-not-editable.js";
import { checkRemoveEmptyNeighbor } from "./check-remove-empty-neighbor.js";
import { checkRemoveEmptyParent } from "./check-remove-empty-parent.js";
import { checkRemoveUnbreakableElement } from "./check-remove-unbreakable-element.js";
import { checkTableCell } from "./check-table-cell.js";
import { checkUnwrapFirstListItem } from "./check-unwrap-first-list-item.js";
/**
* @private
*/
export const cases = [
checkRemoveUnbreakableElement,
checkRemoveContentNotEditable,
checkRemoveChar,
checkTableCell,
checkRemoveEmptyParent,
checkRemoveEmptyNeighbor,
checkJoinTwoLists,
checkJoinNeighbors,
checkUnwrapFirstListItem
];

20
node_modules/jodit/esm/plugins/backspace/config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,20 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
declare module 'jodit/config' {
interface Config {
delete: {
hotkeys: {
delete: string[];
deleteWord: string[];
deleteSentence: string[];
backspace: string[];
backspaceWord: string[];
backspaceSentence: string[];
};
};
}
}
export {};

19
node_modules/jodit/esm/plugins/backspace/config.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import { Config } from "../../config.js";
Config.prototype.delete = {
hotkeys: {
delete: ['delete', 'cmd+backspace'],
deleteWord: ['ctrl+delete', 'cmd+alt+backspace', 'ctrl+alt+backspace'],
deleteSentence: ['ctrl+shift+delete', 'cmd+shift+delete'],
backspace: ['backspace'],
backspaceWord: ['ctrl+backspace'],
backspaceSentence: ['ctrl+shift+backspace', 'cmd+shift+backspace']
}
};

19
node_modules/jodit/esm/plugins/backspace/helpers.d.ts generated vendored Normal file
View File

@@ -0,0 +1,19 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
import type { IJodit, Nullable } from "../../types/index";
/**
* Finds the nearest neighbor that would be in the maximum nesting depth.
* Ie if neighbor `<DIV><SPAN>Text` then return Text node.
* @private
*/
export declare function findMostNestedNeighbor(node: Node, right: boolean, root: HTMLElement, onlyInlide?: boolean): Nullable<Node>;
/**
* @private
*/
export declare function getMoveFilter(jodit: IJodit): (node: Node) => boolean;

35
node_modules/jodit/esm/plugins/backspace/helpers.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../core/dom/index.js";
/**
* Finds the nearest neighbor that would be in the maximum nesting depth.
* Ie if neighbor `<DIV><SPAN>Text` then return Text node.
* @private
*/
export function findMostNestedNeighbor(node, right, root, onlyInlide = false) {
const nextChild = (node) => right ? node.firstChild : node.lastChild;
let next = Dom.findNotEmptyNeighbor(node, !right, root);
if (onlyInlide && Dom.isElement(next) && !Dom.isInlineBlock(next)) {
return null;
}
if (next) {
do {
if (nextChild(next)) {
next = nextChild(next);
}
else {
return next;
}
} while (next);
}
return null;
}
/**
* @private
*/
export function getMoveFilter(jodit) {
return (node) => jodit.e.fire('backSpaceIsMovedIgnore', node) !== true;
}

View File

@@ -0,0 +1,50 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/backspace
*/
export type DeleteMode = 'char' | 'word' | 'sentence';
declare module 'jodit/types/events' {
interface IEventEmitter {
/**
* Enables content preparation prior to deletion, or allows for a complete
* override of the deletion logic if true is returned.
*/
on(event: 'backSpaceBeforeCases', callback: (backspace: boolean, fakeNode: Node) => void | true): this;
/**
* Triggers after the Backspace or Delete key has been pressed and processed.
*/
on(event: 'backSpaceAfterDelete', callback: (backspace: boolean, fakeNode: Node) => void): this;
}
}
declare module 'jodit/types/jodit' {
interface IJodit {
/**
* Backspace plugin: Deletes the next character or selected text.
*/
execCommand(command: 'deleteButton'): void;
/**
* Backspace plugin: Deletes the previous character or selected text.
*/
execCommand(command: 'backspaceButton'): void;
/**
* Backspace plugin: Deletes the next word or selected text.
*/
execCommand(command: 'deleteWordButton'): void;
/**
* Backspace plugin: Deletes the previous word or selected text.
*/
execCommand(command: 'backspaceWordButton'): void;
/**
* Backspace plugin: Deletes the next sentence or selected text.
*/
execCommand(command: 'deleteSentenceButton'): void;
/**
* Backspace plugin: Deletes the previous sentence or selected text.
*/
execCommand(command: 'backspaceSentenceButton'): void;
}
}

View File

@@ -0,0 +1,6 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
export {};

17
node_modules/jodit/esm/plugins/bold/bold.d.ts generated vendored Normal file
View File

@@ -0,0 +1,17 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/bold/README.md]]
* @packageDocumentation
* @module plugins/bold
*/
import type { IJodit } from "../../types/index";
import "./interface";
import "./config";
/**
* Adds `bold`,` strikethrough`, `underline` and` italic` buttons to Jodit
*/
export declare function bold(editor: IJodit): void;

82
node_modules/jodit/esm/plugins/bold/bold.js generated vendored Normal file
View File

@@ -0,0 +1,82 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { pluginSystem } from "../../core/global.js";
import { isArray } from "../../core/helpers/index.js";
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import "./interface.js";
import "./config.js";
import boldIcon from "./icons/bold.svg.js";
import italicIcon from "./icons/italic.svg.js";
import strikethroughIcon from "./icons/strikethrough.svg.js";
import subscriptIcon from "./icons/subscript.svg.js";
import superscriptIcon from "./icons/superscript.svg.js";
import underlineIcon from "./icons/underline.svg.js";
/**
* Adds `bold`,` strikethrough`, `underline` and` italic` buttons to Jodit
*/
export function bold(editor) {
const callBack = (command) => {
const control = Config.defaultOptions.controls[command], cssOptions = {
...control.css
};
let cssRules;
Object.keys(cssOptions).forEach((key) => {
if (!cssRules) {
cssRules = {};
}
cssRules[key] = isArray(cssOptions[key])
? cssOptions[key][0]
: cssOptions[key];
});
editor.s.commitStyle({
element: control.tags ? control.tags[0] : undefined
});
editor.synchronizeValues();
return false;
};
['bold', 'italic', 'underline', 'strikethrough'].forEach(name => {
editor.registerButton({
name,
group: 'font-style'
});
});
['superscript', 'subscript'].forEach(name => {
editor.registerButton({
name,
group: 'script'
});
});
editor
.registerCommand('bold', {
exec: callBack,
hotkeys: ['ctrl+b', 'cmd+b']
})
.registerCommand('italic', {
exec: callBack,
hotkeys: ['ctrl+i', 'cmd+i']
})
.registerCommand('underline', {
exec: callBack,
hotkeys: ['ctrl+u', 'cmd+u']
})
.registerCommand('strikethrough', {
exec: callBack
})
.registerCommand('subscript', {
exec: callBack
})
.registerCommand('superscript', {
exec: callBack
});
}
pluginSystem.add('bold', bold);
Icon.set('bold', boldIcon)
.set('italic', italicIcon)
.set('strikethrough', strikethroughIcon)
.set('subscript', subscriptIcon)
.set('superscript', superscriptIcon)
.set('underline', underlineIcon);

6
node_modules/jodit/esm/plugins/bold/config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,6 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
export {};

46
node_modules/jodit/esm/plugins/bold/config.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Config } from "../../config.js";
Config.prototype.controls.subscript = {
tags: ['sub'],
tooltip: 'subscript'
};
Config.prototype.controls.superscript = {
tags: ['sup'],
tooltip: 'superscript'
};
Config.prototype.controls.bold = {
tagRegExp: /^(strong|b)$/i,
tags: ['strong', 'b'],
css: {
'font-weight': ['bold', '700']
},
tooltip: 'Bold'
};
Config.prototype.controls.italic = {
tagRegExp: /^(em|i)$/i,
tags: ['em', 'i'],
css: {
'font-style': 'italic'
},
tooltip: 'Italic'
};
Config.prototype.controls.underline = {
tagRegExp: /^(u)$/i,
tags: ['u'],
css: {
'text-decoration-line': 'underline'
},
tooltip: 'Underline'
};
Config.prototype.controls.strikethrough = {
tagRegExp: /^(s)$/i,
tags: ['s'],
css: {
'text-decoration-line': 'line-through'
},
tooltip: 'Strike through'
};

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M747 1521q74 32 140 32 376 0 376-335 0-114-41-180-27-44-61.5-74t-67.5-46.5-80.5-25-84-10.5-94.5-2q-73 0-101 10 0 53-.5 159t-.5 158q0 8-1 67.5t-.5 96.5 4.5 83.5 12 66.5zm-14-746q42 7 109 7 82 0 143-13t110-44.5 74.5-89.5 25.5-142q0-70-29-122.5t-79-82-108-43.5-124-14q-50 0-130 13 0 50 4 151t4 152q0 27-.5 80t-.5 79q0 46 1 69zm-541 889l2-94q15-4 85-16t106-27q7-12 12.5-27t8.5-33.5 5.5-32.5 3-37.5.5-34v-65.5q0-982-22-1025-4-8-22-14.5t-44.5-11-49.5-7-48.5-4.5-30.5-3l-4-83q98-2 340-11.5t373-9.5q23 0 68.5.5t67.5.5q70 0 136.5 13t128.5 42 108 71 74 104.5 28 137.5q0 52-16.5 95.5t-39 72-64.5 57.5-73 45-84 40q154 35 256.5 134t102.5 248q0 100-35 179.5t-93.5 130.5-138 85.5-163.5 48.5-176 14q-44 0-132-3t-132-3q-106 0-307 11t-231 12z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M384 1662l17-85q6-2 81.5-21.5t111.5-37.5q28-35 41-101 1-7 62-289t114-543.5 52-296.5v-25q-24-13-54.5-18.5t-69.5-8-58-5.5l19-103q33 2 120 6.5t149.5 7 120.5 2.5q48 0 98.5-2.5t121-7 98.5-6.5q-5 39-19 89-30 10-101.5 28.5t-108.5 33.5q-8 19-14 42.5t-9 40-7.5 45.5-6.5 42q-27 148-87.5 419.5t-77.5 355.5q-2 9-13 58t-20 90-16 83.5-6 57.5l1 18q17 4 185 31-3 44-16 99-11 0-32.5 1.5t-32.5 1.5q-29 0-87-10t-86-10q-138-2-206-2-51 0-143 9t-121 11z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M1760 896q14 0 23 9t9 23v64q0 14-9 23t-23 9h-1728q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h1728zm-1277-64q-28-35-51-80-48-97-48-188 0-181 134-309 133-127 393-127 50 0 167 19 66 12 177 48 10 38 21 118 14 123 14 183 0 18-5 45l-12 3-84-6-14-2q-50-149-103-205-88-91-210-91-114 0-182 59-67 58-67 146 0 73 66 140t279 129q69 20 173 66 58 28 95 52h-743zm507 256h411q7 39 7 92 0 111-41 212-23 55-71 104-37 35-109 81-80 48-153 66-80 21-203 21-114 0-195-23l-140-40q-57-16-72-28-8-8-8-22v-13q0-108-2-156-1-30 0-68l2-37v-44l102-2q15 34 30 71t22.5 56 12.5 27q35 57 80 94 43 36 105 57 59 22 132 22 64 0 139-27 77-26 122-86 47-61 47-129 0-84-81-157-34-29-137-71z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M1025 1369v167h-248l-159-252-24-42q-8-9-11-21h-3l-9 21q-10 20-25 44l-155 250h-258v-167h128l197-291-185-272h-137v-168h276l139 228q2 4 23 42 8 9 11 21h3q3-9 11-21l25-42 140-228h257v168h-125l-184 267 204 296h109zm639 217v206h-514l-4-27q-3-45-3-46 0-64 26-117t65-86.5 84-65 84-54.5 65-54 26-64q0-38-29.5-62.5t-70.5-24.5q-51 0-97 39-14 11-36 38l-105-92q26-37 63-66 80-65 188-65 110 0 178 59.5t68 158.5q0 66-34.5 118.5t-84 86-99.5 62.5-87 63-41 73h232v-80h126z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M1025 1369v167h-248l-159-252-24-42q-8-9-11-21h-3l-9 21q-10 20-25 44l-155 250h-258v-167h128l197-291-185-272h-137v-168h276l139 228q2 4 23 42 8 9 11 21h3q3-9 11-21l25-42 140-228h257v168h-125l-184 267 204 296h109zm637-679v206h-514l-3-27q-4-28-4-46 0-64 26-117t65-86.5 84-65 84-54.5 65-54 26-64q0-38-29.5-62.5t-70.5-24.5q-51 0-97 39-14 11-36 38l-105-92q26-37 63-66 83-65 188-65 110 0 178 59.5t68 158.5q0 56-24.5 103t-62 76.5-81.5 58.5-82 50.5-65.5 51.5-30.5 63h232v-80h126z\"/> </svg> ";

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M176 223q-37-2-45-4l-3-88q13-1 40-1 60 0 112 4 132 7 166 7 86 0 168-3 116-4 146-5 56 0 86-2l-1 14 2 64v9q-60 9-124 9-60 0-79 25-13 14-13 132 0 13 .5 32.5t.5 25.5l1 229 14 280q6 124 51 202 35 59 96 92 88 47 177 47 104 0 191-28 56-18 99-51 48-36 65-64 36-56 53-114 21-73 21-229 0-79-3.5-128t-11-122.5-13.5-159.5l-4-59q-5-67-24-88-34-35-77-34l-100 2-14-3 2-86h84l205 10q76 3 196-10l18 2q6 38 6 51 0 7-4 31-45 12-84 13-73 11-79 17-15 15-15 41 0 7 1.5 27t1.5 31q8 19 22 396 6 195-15 304-15 76-41 122-38 65-112 123-75 57-182 89-109 33-255 33-167 0-284-46-119-47-179-122-61-76-83-195-16-80-16-237v-333q0-188-17-213-25-36-147-39zm1488 1409v-64q0-14-9-23t-23-9h-1472q-14 0-23 9t-9 23v64q0 14 9 23t23 9h1472q14 0 23-9t9-23z\"/> </svg> ";

36
node_modules/jodit/esm/plugins/bold/interface.d.ts generated vendored Normal file
View File

@@ -0,0 +1,36 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/bold
*/
declare module 'jodit/types/jodit' {
interface IJodit {
/**
* Bold plugin: Make selected text bold
*/
execCommand(command: 'bold'): void;
/**
* Bold plugin: Make selected text style italic
*/
execCommand(command: 'italic'): void;
/**
* Bold plugin: Make selected text style underline
*/
execCommand(command: 'underline'): void;
/**
* Bold plugin: Make selected text style strikethrough
*/
execCommand(command: 'strikethrough'): void;
/**
* Bold plugin: Wrap selected text in SUB tag
*/
execCommand(command: 'subscript'): void;
/**
* Bold plugin: Wrap selected text in SUP tag
*/
execCommand(command: 'superscript'): void;
}
}

1
node_modules/jodit/esm/plugins/bold/interface.js generated vendored Normal file
View File

@@ -0,0 +1 @@
"use strict";

View File

@@ -0,0 +1,39 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/class-span/README.md]]
* @packageDocumentation
* @module plugins/class-span
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../core/plugin/index";
/**
* Applying some className to selected text.
* @example
* ```js
* const editor = Jodit.make('#editor', {
* controls: {
* classSpan: {
* list: {
* class1: 'Classe 1',
* class2: 'Classe 2',
* class3: 'Classe 3',
* class4: 'Classe 4',
* class5: 'Classe 5'
* }
* }
* }
* });
* ```
*/
export declare class classSpan extends Plugin {
/** @override */
buttons: Plugin['buttons'];
/** @override */
protected afterInit(jodit: IJodit): void;
/** @override */
protected beforeDestruct(): void;
}

101
node_modules/jodit/esm/plugins/class-span/class-span.js generated vendored Normal file
View File

@@ -0,0 +1,101 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../core/dom/dom.js";
import { pluginSystem } from "../../core/global.js";
import { memorizeExec } from "../../core/helpers/utils/utils.js";
import { Plugin } from "../../core/plugin/index.js";
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import classSpanIcon from "./class-span.svg.js";
Config.prototype.controls.classSpan = {
command: 'applyClassName',
exec: memorizeExec,
list: [
'enabled',
'disabled',
'activated',
'text-left',
'text-center',
'text-right',
'warning',
'error'
],
isChildActive: (editor, button) => {
const current = editor.s.current();
if (current) {
const currentBpx = Dom.closest(current, Dom.isElement, editor.editor) || editor.editor;
return Boolean(button.control.args &&
currentBpx.classList.contains(button.control.args[0].toString()));
}
return false;
},
isActive: (editor, btn) => {
const current = editor.s.current();
if (current) {
const currentBpx = Dom.closest(current, Dom.isElement, editor.editor) || editor.editor;
let present = false;
btn.control.list &&
Object.keys(btn.control.list).forEach((className) => {
if (currentBpx.classList.contains(className)) {
present = true;
}
});
return Boolean(currentBpx &&
currentBpx !== editor.editor &&
btn.control.list !== undefined &&
present);
}
return false;
},
childTemplate: (e, key, value) => `<span class="${key}">${e.i18n(value)}</span>`,
tooltip: 'Insert className'
};
/**
* Applying some className to selected text.
* @example
* ```js
* const editor = Jodit.make('#editor', {
* controls: {
* classSpan: {
* list: {
* class1: 'Classe 1',
* class2: 'Classe 2',
* class3: 'Classe 3',
* class4: 'Classe 4',
* class5: 'Classe 5'
* }
* }
* }
* });
* ```
*/
export class classSpan extends Plugin {
constructor() {
super(...arguments);
/** @override */
this.buttons = [
{
name: 'classSpan',
group: 'script'
}
];
}
/** @override */
afterInit(jodit) {
jodit.registerCommand('applyClassName', (command, second, third) => {
jodit.s.commitStyle({
attributes: {
['class']: third
}
});
return false;
});
}
/** @override */
beforeDestruct() { }
}
pluginSystem.add('classSpan', classSpan);
Icon.set('class-span', classSpanIcon);

View File

@@ -0,0 +1 @@
export default "<svg viewBox=\"0 0 48 48\" xmlns=\"http://www.w3.org/2000/svg\"> <path d=\"M36 4h-24c-2.21 0-4 1.79-4 4v32c0 2.21 1.79 4 4 4h24c2.21 0 4-1.79 4-4v-32c0-2.21-1.79-4-4-4zm-24 4h10v16l-5-3-5 3v-16z\"/> </svg> ";

View File

@@ -0,0 +1,40 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* [[include:plugins/clean-html/README.md]]
* @packageDocumentation
* @module plugins/clean-html
*/
import type { IJodit } from "../../types/index";
import { Plugin } from "../../core/plugin/plugin";
import "./config";
/**
* Clean HTML after removeFormat and insertHorizontalRule command
*/
export declare class cleanHtml extends Plugin {
/** @override */
buttons: Plugin['buttons'];
/** @override */
protected afterInit(jodit: IJodit): void;
private get isEditMode();
/**
* Clean HTML code on every change
*/
protected onChangeCleanHTML(): void;
private currentSelectionNode;
private walker;
protected startWalker(): void;
protected beforeCommand(command: string): void | false;
/**
* Event handler when manually assigning a value to the HTML editor.
*/
protected onBeforeSetNativeEditorValue(data: {
value: string;
}): boolean;
protected onSafeHTML(sandBox: HTMLElement): void;
/** @override */
protected beforeDestruct(): void;
}

120
node_modules/jodit/esm/plugins/clean-html/clean-html.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { hook, watch } from "../../core/decorators/index.js";
import { Dom } from "../../core/dom/dom.js";
import { LazyWalker } from "../../core/dom/lazy-walker.js";
import { pluginSystem } from "../../core/global.js";
import { safeHTML } from "../../core/helpers/html/safe-html.js";
import { Plugin } from "../../core/plugin/plugin.js";
import "./config.js";
import { getHash, removeFormatForCollapsedSelection, removeFormatForSelection, visitNodeWalker } from "./helpers/index.js";
/**
* Clean HTML after removeFormat and insertHorizontalRule command
*/
export class cleanHtml extends Plugin {
constructor() {
super(...arguments);
/** @override */
this.buttons = [
{
name: 'eraser',
group: 'font-style'
}
];
this.currentSelectionNode = null;
this.walker = new LazyWalker(this.j.async, {
timeout: this.j.o.cleanHTML.timeout
});
}
/** @override */
afterInit(jodit) { }
get isEditMode() {
return !(this.j.isInDestruct ||
!this.j.isEditorMode() ||
this.j.getReadOnly());
}
/**
* Clean HTML code on every change
*/
onChangeCleanHTML() {
if (!this.isEditMode) {
return;
}
const editor = this.j;
this.walker.setWork(editor.editor);
this.currentSelectionNode = editor.s.current();
}
startWalker() {
const { jodit } = this;
const allow = getHash(this.j.o.cleanHTML.allowTags);
const deny = getHash(this.j.o.cleanHTML.denyTags);
this.walker
.on('visit', (node) => visitNodeWalker(jodit, node, allow, deny, this.currentSelectionNode))
.on('end', (affected) => {
this.j.e.fire(affected
? 'internalChange finishedCleanHTMLWorker'
: 'finishedCleanHTMLWorker');
});
}
beforeCommand(command) {
if (command.toLowerCase() === 'removeformat') {
if (this.j.s.isCollapsed()) {
removeFormatForCollapsedSelection(this.j);
}
else {
removeFormatForSelection(this.j);
}
return false;
}
}
/**
* Event handler when manually assigning a value to the HTML editor.
*/
onBeforeSetNativeEditorValue(data) {
const [sandBox, iframe] = this.j.o.cleanHTML.useIframeSandbox
? this.j.createInside.sandbox()
: [this.j.createInside.div()];
sandBox.innerHTML = data.value;
this.onSafeHTML(sandBox);
data.value = sandBox.innerHTML;
safeHTML(sandBox, { safeJavaScriptLink: true, removeOnError: true });
Dom.safeRemove(iframe);
return false;
}
onSafeHTML(sandBox) {
safeHTML(sandBox, this.j.o.cleanHTML);
}
/** @override */
beforeDestruct() {
this.walker.destruct();
}
}
__decorate([
watch([':change', ':afterSetMode', ':afterInit', ':mousedown', ':keydown'])
], cleanHtml.prototype, "onChangeCleanHTML", null);
__decorate([
hook('ready')
], cleanHtml.prototype, "startWalker", null);
__decorate([
watch(':beforeCommand')
], cleanHtml.prototype, "beforeCommand", null);
__decorate([
watch(':beforeSetNativeEditorValue')
], cleanHtml.prototype, "onBeforeSetNativeEditorValue", null);
__decorate([
watch(':safeHTML')
], cleanHtml.prototype, "onSafeHTML", null);
pluginSystem.add('cleanHtml', cleanHtml);

105
node_modules/jodit/esm/plugins/clean-html/config.d.ts generated vendored Normal file
View File

@@ -0,0 +1,105 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { HTMLTagNames, IDictionary, Nullable } from "../../types/index";
declare module 'jodit/config' {
interface Config {
cleanHTML: {
timeout: number;
/**
* Replace &amp;nbsp; to plain space
*/
replaceNBSP: boolean;
/**
* Remove empty P tags, if they are not in the beginning of the text
*/
fillEmptyParagraph: boolean;
/**
* Remove empty elements
*/
removeEmptyElements: boolean;
/**
* Replace old tags to new eg. <i> to <em>, <b> to <strong>
*/
replaceOldTags: IDictionary<HTMLTagNames> | false;
/**
* You can use an iframe with the sandbox attribute to safely paste and test HTML code.
* It prevents scripts and handlers from running, but it does slow things down.
*
* ```javascript
* Jodit.make('#editor', {
* cleanHTML: {
* useIframeSandbox: true
* }
* });
* ```
*/
useIframeSandbox: boolean;
/**
* Remove onError attributes
*/
removeOnError: boolean;
/**
* Safe href="javascript:" links
*/
safeJavaScriptLink: boolean;
/**
* The allowTags option defines which elements will remain in the
* edited text when the editor saves. You can use this limit the returned HTML.
* @example
* ```javascript
* const jodit = new Jodit.make('#editor', {
* cleanHTML: {
* cleanOnPaste: false
* }
* });
* ```
* @example
* ```javascript
* const editor = Jodit.make('#editor', {
* cleanHTML: {
* allowTags: 'p,a[href],table,tr,td, img[src=1.png]' // allow only <p>,<a>,<table>,<tr>,<td>,<img> tags and
* for <a> allow only `href` attribute and <img> allow only `src` attribute == '1.png'
* }
* });
* editor.value = 'Sorry! <strong>Goodby</strong>\
* <span>mr.</span> <a style="color:red" href="https://xdsoft.net">Freeman</a>';
* console.log(editor.value); //Sorry! <a href="https://xdsoft.net">Freeman</a>
* ```
*
* @example
* ```javascript
* const editor = Jodit.make('#editor', {
* cleanHTML: {
* allowTags: {
* p: true,
* a: {
* href: true
* },
* table: true,
* tr: true,
* td: true,
* img: {
* src: '1.png'
* }
* }
* }
* });
* ```
*/
allowTags: false | string | IDictionary<string>;
denyTags: false | string | IDictionary<string>;
/**
* Node filtering rules that do not need to be applied to content
* The full list of rules is generated dynamically from the folder
* https://github.com/xdan/jodit/tree/main/src/plugins/clean-html/helpers/visitor/filters
*/
disableCleanFilter: Nullable<Set<string>>;
};
}
}

29
node_modules/jodit/esm/plugins/clean-html/config.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Icon } from "../../core/ui/icon.js";
import { Config } from "../../config.js";
import eraserIcon from "./eraser.svg.js";
Config.prototype.cleanHTML = {
timeout: 300,
removeEmptyElements: true,
fillEmptyParagraph: true,
replaceNBSP: true,
replaceOldTags: {
i: 'em',
b: 'strong'
},
allowTags: false,
denyTags: 'script',
useIframeSandbox: false,
removeOnError: true,
safeJavaScriptLink: true,
disableCleanFilter: null
};
Config.prototype.controls.eraser = {
command: 'removeFormat',
tooltip: 'Clear Formatting'
};
Icon.set('eraser', eraserIcon);

View File

@@ -0,0 +1 @@
export default "<svg xmlns='http://www.w3.org/2000/svg' viewBox=\"0 0 1792 1792\"> <path d=\"M832 1408l336-384h-768l-336 384h768zm1013-1077q15 34 9.5 71.5t-30.5 65.5l-896 1024q-38 44-96 44h-768q-38 0-69.5-20.5t-47.5-54.5q-15-34-9.5-71.5t30.5-65.5l896-1024q38-44 96-44h768q38 0 69.5 20.5t47.5 54.5z\"/> </svg> ";

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IDictionary } from "../../../types/index";
/**
* @private
*/
export declare function getHash(tags: false | string | IDictionary<string>): IDictionary | false;

View File

@@ -0,0 +1,48 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { isString } from "../../../core/helpers/checker/is-string.js";
import { trim } from "../../../core/helpers/string/trim.js";
/**
* @private
*/
export function getHash(tags) {
const attributesReg = /([^[]*)\[([^\]]+)]/;
const separator = /[\s]*,[\s]*/, attrReg = /^(.*)[\s]*=[\s]*(.*)$/;
const tagsHash = {};
if (isString(tags)) {
tags.split(separator).map((elm) => {
elm = trim(elm);
const attr = attributesReg.exec(elm), allowAttributes = {}, attributeMap = (attrName) => {
attrName = trim(attrName);
const val = attrReg.exec(attrName);
if (val) {
allowAttributes[val[1]] = val[2];
}
else {
allowAttributes[attrName] = true;
}
};
if (attr) {
const attr2 = attr[2].split(separator);
if (attr[1]) {
attr2.forEach(attributeMap);
tagsHash[attr[1].toUpperCase()] = allowAttributes;
}
}
else {
tagsHash[elm.toUpperCase()] = true;
}
});
return tagsHash;
}
if (tags) {
Object.keys(tags).forEach(tagName => {
tagsHash[tagName.toUpperCase()] = tags[tagName];
});
return tagsHash;
}
return false;
}

View File

@@ -0,0 +1,12 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
export * from "./get-hash";
export * from "./remove-format/remove-format-for-collapsed-selection";
export * from "./remove-format/remove-format-for-selection";
export * from "./visitor/visit-node-walker";

View File

@@ -0,0 +1,12 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
export * from "./get-hash.js";
export * from "./remove-format/remove-format-for-collapsed-selection.js";
export * from "./remove-format/remove-format-for-selection.js";
export * from "./visitor/visit-node-walker.js";

View File

@@ -0,0 +1,19 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit, Nullable } from "../../../../types/index";
/**
* For collapsed selection move cursor outside or split inline block
* @private
*/
export declare function removeFormatForCollapsedSelection(jodit: IJodit, fake?: Node): Nullable<Text> | void;
/**
* Element has inline display mode
* @private
*/
export declare function isInlineBlock(node: Nullable<Node>): node is Node;

View File

@@ -0,0 +1,45 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INSEPARABLE_TAGS } from "../../../../core/constants.js";
import { Dom } from "../../../../core/dom/dom.js";
/**
* For collapsed selection move cursor outside or split inline block
* @private
*/
export function removeFormatForCollapsedSelection(jodit, fake) {
const { s } = jodit;
let fakeNode = fake;
if (!fakeNode) {
fakeNode = jodit.createInside.fake();
const { range } = s;
Dom.safeInsertNode(range, fakeNode);
range.collapse();
}
const mainInline = Dom.furthest(fakeNode, isInlineBlock, jodit.editor);
if (mainInline) {
if (s.cursorOnTheLeft(mainInline)) {
Dom.before(mainInline, fakeNode);
}
else if (s.cursorOnTheRight(mainInline)) {
Dom.after(mainInline, fakeNode);
}
else {
const leftHand = s.splitSelection(mainInline);
leftHand && Dom.after(leftHand, fakeNode);
}
}
if (!fake) {
s.setCursorBefore(fakeNode);
Dom.safeRemove(fakeNode);
}
}
/**
* Element has inline display mode
* @private
*/
export function isInlineBlock(node) {
return Dom.isInlineBlock(node) && !Dom.isTag(node, INSEPARABLE_TAGS);
}

View File

@@ -0,0 +1,14 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit } from "../../../../types/index";
/**
* Remove formatting for all selected elements
* @private
*/
export declare function removeFormatForSelection(jodit: IJodit): void;

View File

@@ -0,0 +1,53 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../../core/dom/dom.js";
import { attr } from "../../../../core/helpers/utils/index.js";
import { isInlineBlock, removeFormatForCollapsedSelection } from "./remove-format-for-collapsed-selection.js";
/**
* Remove formatting for all selected elements
* @private
*/
export function removeFormatForSelection(jodit) {
const { s, editor, createInside } = jodit, { range } = s, left = range.cloneRange(), right = range.cloneRange(), fakeLeft = createInside.fake(), fakeRight = createInside.fake();
left.collapse(true);
right.collapse(false);
Dom.safeInsertNode(left, fakeLeft);
Dom.safeInsertNode(right, fakeRight);
range.setStartBefore(fakeLeft);
range.collapse(true);
s.selectRange(range);
removeFormatForCollapsedSelection(jodit, fakeLeft);
range.setEndAfter(fakeRight);
range.collapse(false);
s.selectRange(range);
removeFormatForCollapsedSelection(jodit, fakeRight);
const shouldUnwrap = [];
Dom.between(fakeLeft, fakeRight, node => {
if (isInlineBlock(node) && !Dom.isTag(node, 'a')) {
shouldUnwrap.push(node);
}
if (Dom.isElement(node) && attr(node, 'style')) {
attr(node, 'style', null);
}
});
shouldUnwrap.forEach(node => Dom.unwrap(node));
const clearParent = (node, left) => {
if (!Dom.findNotEmptySibling(node, left)) {
const pn = node.parentNode;
if (pn && pn !== editor && attr(pn, 'style')) {
attr(pn, 'style', null);
clearParent(pn, left);
return true;
}
}
};
clearParent(fakeLeft, true) && clearParent(fakeRight, false);
range.setStartAfter(fakeLeft);
range.setEndBefore(fakeRight);
s.selectRange(range);
Dom.safeRemove(fakeLeft);
Dom.safeRemove(fakeRight);
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IDictionary, IJodit } from "../../../../../types/index";
/**
* @private
*/
export declare function allowAttributes(jodit: IJodit, nodeElm: Node, hadEffect: boolean, allow: IDictionary | false): boolean;

View File

@@ -0,0 +1,30 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../../../core/dom/dom.js";
/**
* @private
*/
export function allowAttributes(jodit, nodeElm, hadEffect, allow) {
if (allow && Dom.isElement(nodeElm) && allow[nodeElm.nodeName] !== true) {
const attrs = nodeElm.attributes;
if (attrs && attrs.length) {
const removeAttrs = [];
for (let i = 0; i < attrs.length; i += 1) {
const attr = allow[nodeElm.nodeName][attrs[i].name];
if (!attr || (attr !== true && attr !== attrs[i].value)) {
removeAttrs.push(attrs[i].name);
}
}
if (removeAttrs.length) {
hadEffect = true;
}
removeAttrs.forEach(attr => {
nodeElm.removeAttribute(attr);
});
}
}
return hadEffect;
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit } from "../../../../../types/index";
/**
* @private
*/
export declare function fillEmptyParagraph(jodit: IJodit, nodeElm: Node, hadEffect: boolean): boolean;

View File

@@ -0,0 +1,28 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INSEPARABLE_TAGS } from "../../../../../core/constants.js";
import { Dom } from "../../../../../core/dom/dom.js";
const TABLE_CONTAINER_TAGS = new Set([
'table',
'tbody',
'thead',
'tfoot',
'tr'
]);
/**
* @private
*/
export function fillEmptyParagraph(jodit, nodeElm, hadEffect) {
if (jodit.o.cleanHTML.fillEmptyParagraph &&
Dom.isBlock(nodeElm) &&
Dom.isEmpty(nodeElm, INSEPARABLE_TAGS) &&
!Dom.isTag(nodeElm, TABLE_CONTAINER_TAGS)) {
const br = jodit.createInside.element('br');
nodeElm.appendChild(br);
return true;
}
return hadEffect;
}

View File

@@ -0,0 +1,18 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
/**
* @private
*/
export * from "./allow-attributes";
export * from "./fill-empty-paragraph";
export * from "./remove-empty-text-node";
export * from "./remove-inv-text-nodes";
export * from "./replace-old-tags";
export * from "./sanitize-attributes";
export * from "./try-remove-node";

View File

@@ -0,0 +1,18 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
/**
* @private
*/
export * from "./allow-attributes.js";
export * from "./fill-empty-paragraph.js";
export * from "./remove-empty-text-node.js";
export * from "./remove-inv-text-nodes.js";
export * from "./replace-old-tags.js";
export * from "./sanitize-attributes.js";
export * from "./try-remove-node.js";

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit, Nullable } from "../../../../../types/index";
/**
* @private
*/
export declare function removeEmptyTextNode(jodit: IJodit, node: Node, hadEffect: boolean, arg: unknown, argi: unknown, currentNode: Nullable<Node>): boolean;

View File

@@ -0,0 +1,19 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../../../core/dom/dom.js";
/**
* @private
*/
export function removeEmptyTextNode(jodit, node, hadEffect, arg, argi, currentNode) {
if (Dom.isText(node) && !node.nodeValue) {
if (node === currentNode && jodit.s.isCollapsed()) {
jodit.s.setCursorAfter(node);
}
Dom.safeRemove(node);
return true;
}
return hadEffect;
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit, Nullable } from "../../../../../types/index";
/**
* @private
*/
export declare function removeInvTextNodes(jodit: IJodit, node: Node, hadEffect: boolean, arg: unknown, argi: unknown, currentNode: Nullable<Node>): boolean;

View File

@@ -0,0 +1,30 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { INVISIBLE_SPACE_REG_EXP as INV_REG } from "../../../../../core/constants.js";
import { Dom } from "../../../../../core/dom/dom.js";
/**
* @private
*/
export function removeInvTextNodes(jodit, node, hadEffect, arg, argi, currentNode) {
if (currentNode === node || !Dom.isText(node) || node.nodeValue == null) {
return hadEffect;
}
if (!INV_REG().test(node.nodeValue)) {
return hadEffect;
}
const focusBox = Dom.furthest(currentNode, Dom.isBlock, jodit.editor);
if (!focusBox || Dom.isOrContains(focusBox, node)) {
return hadEffect;
}
node.nodeValue = node.nodeValue.replace(INV_REG(), '');
if (node === currentNode && jodit.s.isCollapsed()) {
jodit.s.setCursorAfter(node);
}
if (!node.nodeValue) {
Dom.safeRemove(node);
}
return true;
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit } from "../../../../../types/index";
/**
* @private
*/
export declare function replaceOldTags(jodit: IJodit, nodeElm: Node, hadEffect: boolean): boolean;

View File

@@ -0,0 +1,31 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../../../core/dom/dom.js";
/**
* @private
*/
export function replaceOldTags(jodit, nodeElm, hadEffect) {
const newNodeElm = replaceIfMatched(jodit, nodeElm, jodit.o.cleanHTML.replaceOldTags);
if (nodeElm !== newNodeElm) {
nodeElm = newNodeElm;
return true;
}
return hadEffect;
}
/**
* Replaces an element with a newer one if specified in the configuration match
* @private
*/
function replaceIfMatched(jodit, oldParent, list) {
if (!list || !Dom.isHTMLElement(oldParent)) {
return oldParent;
}
const tagName = list[oldParent.nodeName.toLowerCase()] || list[oldParent.nodeName];
if (tagName) {
return Dom.replace(oldParent, tagName, jodit.createInside, true, false);
}
return oldParent;
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IJodit } from "../../../../../types/index";
/**
* @private
*/
export declare function sanitizeAttributes(jodit: IJodit, nodeElm: Node, hadEffect: boolean): boolean;

View File

@@ -0,0 +1,20 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { Dom } from "../../../../../core/dom/dom.js";
import { sanitizeHTMLElement } from "../../../../../core/helpers/index.js";
/**
* @private
*/
export function sanitizeAttributes(jodit, nodeElm, hadEffect) {
if (Dom.isElement(nodeElm) &&
sanitizeHTMLElement(nodeElm, {
safeJavaScriptLink: jodit.options.cleanHTML.safeJavaScriptLink,
removeOnError: jodit.options.cleanHTML.removeOnError
})) {
return true;
}
return hadEffect;
}

View File

@@ -0,0 +1,13 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module plugins/clean-html
*/
import type { IDictionary, IJodit, Nullable } from "../../../../../types/index";
/**
* @private
*/
export declare function tryRemoveNode(jodit: IJodit, nodeElm: Node, hadEffect: boolean, allowTags: IDictionary | false, denyTags: IDictionary | false, currentSelectionNode: Nullable<Node>): boolean;

View File

@@ -0,0 +1,39 @@
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
import { IS_INLINE } from "../../../../../core/constants.js";
import { Dom } from "../../../../../core/dom/dom.js";
import { trimInv } from "../../../../../core/helpers/string/trim.js";
/**
* @private
*/
export function tryRemoveNode(jodit, nodeElm, hadEffect, allowTags, denyTags, currentSelectionNode) {
if (isRemovableNode(jodit, nodeElm, currentSelectionNode, allowTags, denyTags)) {
Dom.safeRemove(nodeElm);
return true;
}
return hadEffect;
}
/**
* @private
*/
function isRemovableNode(jodit, node, current, allow, deny) {
if (!Dom.isText(node)) {
if (allow && !allow[node.nodeName]) {
return true;
}
if (!allow && deny && deny[node.nodeName]) {
return true;
}
}
if (!jodit.o.cleanHTML.removeEmptyElements) {
return false;
}
return (Dom.isElement(node) &&
node.nodeName.match(IS_INLINE) != null &&
!Dom.isTemporary(node) &&
trimInv(node.innerHTML).length === 0 &&
(current == null || !Dom.isOrContains(node, current)));
}

Some files were not shown because too many files have changed in this diff Show More