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

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)));
}

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 visitNodeWalker(jodit: IJodit, nodeElm: Node, allowTags: IDictionary | false, denyTags: IDictionary | false, currentSelectionNode: 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 { IS_PROD } from "../../../../core/constants.js";
import * as filters from "./filters/index.js";
const keys = Object.keys(filters);
/**
* @private
*/
export function visitNodeWalker(jodit, nodeElm, allowTags, denyTags, currentSelectionNode) {
let hadEffect = false;
const dcf = jodit.o.cleanHTML.disableCleanFilter;
for (const key of keys) {
if (dcf && dcf.has(key)) {
continue;
}
const filter = filters[key];
const tmp = hadEffect;
hadEffect = filter(jodit, nodeElm, hadEffect, allowTags, denyTags, currentSelectionNode);
if (!IS_PROD && !tmp && hadEffect) {
console.warn(`CleanHTML: Effect "${key}"`);
}
if (!nodeElm.isConnected) {
return true;
}
}
return hadEffect;
}