jodit
Version:
Jodit is an awesome and useful wysiwyg editor with filebrowser
123 lines (122 loc) • 4.89 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2026 Valerii Chupurnov. All rights reserved. https://xdsoft.net
*/
/**
* @module helpers/html
*/
import { globalDocument, IS_PROD } from "../../constants.js";
import { Dom } from "../../dom/dom.js";
import { trim } from "../string/trim.js";
import { $$ } from "../utils/selector.js";
function normalizeCSS(s) {
return s
.replace(/mso-[a-z-]+:[\s]*[^;]+;/gi, '')
.replace(/mso-[a-z-]+:[\s]*[^";']+$/gi, '')
.replace(/border[a-z-]*:[\s]*[^;]+;/gi, '')
.replace(/([0-9.]+)(pt|cm)/gi, (match, units, metrics) => {
switch (metrics.toLowerCase()) {
case 'pt':
return (parseFloat(units) * 1.328).toFixed(0) + 'px';
case 'cm':
return (parseFloat(units) * 0.02645833).toFixed(0) + 'px';
}
return match;
});
}
/**
* If the HTML has CSS rules with selectors,
* it applies them to the selectors in the HTML itself
* and then removes the selector styles, leaving only the inline ones.
*/
export function applyStyles(html) {
// Match the opening <html> tag whether or not it carries attributes. MS
// Word emits `<html xmlns:o=…>` (note the trailing space), but Excel/Calc
// wrap the copied table in a bare `<html>`. The old `'<html '` check missed
// the bare tag, so for Excel clipboards the `<style>` rules (class-based
// cell backgrounds/borders, e.g. `.xl31 { background:#FCE4D6 }`) were never
// inlined and all styling was lost once the `<style>` block got stripped.
// See https://github.com/xdan/jodit/issues/1362
const openMatch = /<html(?:\s[^>]*)?>/i.exec(html);
if (!openMatch) {
return html;
}
html = html.substring(openMatch.index);
const closeIndex = html.toLowerCase().lastIndexOf('</html>');
if (closeIndex !== -1) {
html = html.substring(0, closeIndex + '</html>'.length);
}
const iframe = globalDocument.createElement('iframe');
iframe.style.display = 'none';
globalDocument.body.appendChild(iframe);
let convertedString = '', collection = [];
try {
const iframeDoc = iframe.contentDocument ||
(iframe.contentWindow ? iframe.contentWindow.document : null);
if (iframeDoc) {
iframeDoc.open();
iframeDoc.write(html);
iframeDoc.close();
// Word marks its auto-generated list markers (the literal
// bullet/number, e.g. `1.` or `·`) with `mso-list:Ignore`.
// They are display-only and must not be imported, otherwise
// the marker text leaks into the content. Drop them before any
// style normalization strips the `mso-list` hint. See #948
Dom.each(iframeDoc.body, (node) => {
if (Dom.isElement(node) &&
/mso-list:\s*ignore/i.test(node.getAttribute('style') || '')) {
Dom.safeRemove(node);
}
});
try {
for (let i = 0; i < iframeDoc.styleSheets.length; i += 1) {
const rules = iframeDoc.styleSheets[i].cssRules;
for (let idx = 0; idx < rules.length; idx += 1) {
if (rules[idx].selectorText === '') {
continue;
}
collection = $$(rules[idx].selectorText, iframeDoc.body);
collection.forEach((elm) => {
elm.style.cssText = normalizeCSS(rules[idx].style.cssText +
';' +
elm.style.cssText);
});
}
}
}
catch (e) {
if (!IS_PROD) {
throw e;
}
}
Dom.each(iframeDoc.body, node => {
if (Dom.isElement(node)) {
const elm = node;
const css = elm.getAttribute('style');
if (css) {
elm.style.cssText = normalizeCSS(css);
}
if (elm.hasAttribute('style') &&
!elm.getAttribute('style')) {
elm.removeAttribute('style');
}
}
});
convertedString = iframeDoc.firstChild
? trim(iframeDoc.body.innerHTML)
: '';
}
}
catch (_a) {
}
finally {
Dom.safeRemove(iframe);
}
if (convertedString) {
html = convertedString;
}
return trim(html
.replace(/<(\/)?(html|colgroup|col|o:p)[^>]*>/g, '')
.replace(/<!--[^>]*>/g, ''));
}