mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Improved cleanHtml and the handling of removeColors for dark mode
This commit is contained in:
parent
2c5322129c
commit
9188a8bcd2
2 changed files with 151 additions and 151 deletions
|
|
@ -13,6 +13,8 @@ const
|
||||||
"'": '''
|
"'": '''
|
||||||
},
|
},
|
||||||
|
|
||||||
|
replaceWithChildren = node => node.replaceWith(...[...node.childNodes]),
|
||||||
|
|
||||||
// Strip utm_* tracking
|
// Strip utm_* tracking
|
||||||
stripTracking = text => text.replace(/(\?|&|&)utm_[a-z]+=[^&?#]*/si, '$1');
|
stripTracking = text => text.replace(/(\?|&|&)utm_[a-z]+=[^&?#]*/si, '$1');
|
||||||
|
|
||||||
|
|
@ -29,8 +31,9 @@ export const
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
clearHtml = (html, contentLocationUrls) => {
|
cleanHtml = (html, contentLocationUrls, removeColors) => {
|
||||||
const debug = false, // Config()->Get('debug', 'enable', false);
|
const
|
||||||
|
debug = false, // Config()->Get('debug', 'enable', false);
|
||||||
useProxy = !!SettingsGet('UseLocalProxyForExternalImages'),
|
useProxy = !!SettingsGet('UseLocalProxyForExternalImages'),
|
||||||
detectHiddenImages = true, // !!SettingsGet('try_to_detect_hidden_images'),
|
detectHiddenImages = true, // !!SettingsGet('try_to_detect_hidden_images'),
|
||||||
|
|
||||||
|
|
@ -38,7 +41,72 @@ export const
|
||||||
hasExternals: false,
|
hasExternals: false,
|
||||||
foundCIDs: [],
|
foundCIDs: [],
|
||||||
foundContentLocationUrls: []
|
foundContentLocationUrls: []
|
||||||
|
},
|
||||||
|
|
||||||
|
// convert body attributes to CSS
|
||||||
|
tasks = {
|
||||||
|
link: value => {
|
||||||
|
if (/^#[a-fA-Z0-9]{3,6}$/.test(value)) {
|
||||||
|
tpl.content.querySelectorAll('a').forEach(node => node.style.color = value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: (value, node) => node.style.color = value,
|
||||||
|
topmargin: (value, node) => node.style.marginTop = pInt(value) + 'px',
|
||||||
|
leftmargin: (value, node) => node.style.marginLeft = pInt(value) + 'px',
|
||||||
|
bottommargin: (value, node) => node.style.marginBottom = pInt(value) + 'px',
|
||||||
|
rightmargin: (value, node) => node.style.marginRight = pInt(value) + 'px'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if (static::Config()->Get('labs', 'strict_html_parser', true))
|
||||||
|
let
|
||||||
|
value,
|
||||||
|
allowedAttributes = [
|
||||||
|
// defaults
|
||||||
|
'name',
|
||||||
|
'dir', 'lang', 'style', 'title',
|
||||||
|
'background', 'bgcolor', 'alt', 'height', 'width', 'src', 'href',
|
||||||
|
'border', 'bordercolor', 'charset', 'direction', 'language',
|
||||||
|
// a
|
||||||
|
'coords', 'download', 'hreflang', 'shape',
|
||||||
|
// body
|
||||||
|
'alink', 'bgproperties', 'bottommargin', 'leftmargin', 'link', 'rightmargin', 'text', 'topmargin', 'vlink',
|
||||||
|
'marginwidth', 'marginheight', 'offset',
|
||||||
|
// button,
|
||||||
|
'disabled', 'type', 'value',
|
||||||
|
// col
|
||||||
|
'align', 'valign',
|
||||||
|
// font
|
||||||
|
'color', 'face', 'size',
|
||||||
|
// form
|
||||||
|
'novalidate',
|
||||||
|
// hr
|
||||||
|
'noshade',
|
||||||
|
// img
|
||||||
|
'hspace', 'sizes', 'srcset', 'vspace', 'usemap',
|
||||||
|
// input, textarea
|
||||||
|
'checked', 'max', 'min', 'maxlength', 'multiple', 'pattern', 'placeholder', 'readonly',
|
||||||
|
'required', 'step', 'wrap',
|
||||||
|
// label
|
||||||
|
'for',
|
||||||
|
// meter
|
||||||
|
'low', 'high', 'optimum',
|
||||||
|
// ol
|
||||||
|
'reversed', 'start',
|
||||||
|
// option
|
||||||
|
'selected', 'label',
|
||||||
|
// table
|
||||||
|
'cols', 'rows', 'frame', 'rules', 'summary', 'cellpadding', 'cellspacing',
|
||||||
|
// th
|
||||||
|
'abbr', 'scope',
|
||||||
|
// td
|
||||||
|
'axis', 'colspan', 'rowspan', 'headers', 'nowrap'
|
||||||
|
],
|
||||||
|
disallowedAttributes = [
|
||||||
|
'id', 'class', 'contenteditable', 'designmode', 'formaction', 'manifest', 'action',
|
||||||
|
'data-bind', 'data-reactid', 'xmlns', 'srcset',
|
||||||
|
'fscommand', 'seeksegmenttime'
|
||||||
|
];
|
||||||
|
|
||||||
tpl.innerHTML = html
|
tpl.innerHTML = html
|
||||||
.replace(/(<pre[^>]*>)([\s\S]*?)(<\/pre>)/gi, aMatches => {
|
.replace(/(<pre[^>]*>)([\s\S]*?)(<\/pre>)/gi, aMatches => {
|
||||||
return (aMatches[1] + aMatches[2].trim() + aMatches[3].trim()).replace(/\r?\n/g, '<br>');
|
return (aMatches[1] + aMatches[2].trim() + aMatches[3].trim()).replace(/\r?\n/g, '<br>');
|
||||||
|
|
@ -47,13 +115,13 @@ export const
|
||||||
.replace(/<!--[\s\S]*?-->/g, '')
|
.replace(/<!--[\s\S]*?-->/g, '')
|
||||||
// \MailSo\Base\HtmlUtils::ClearTags()
|
// \MailSo\Base\HtmlUtils::ClearTags()
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
.replace(/<\/?(link|form|center|base|meta|bgsound|keygen|source|object|embed|applet|mocha|i?frame|frameset|video|audio|area|map)(\s[\s\S]*?)?>/gi, '')
|
.replace(/<\/?(link|form|input|output|select|button|textarea|center|base|meta|bgsound|keygen|source|object|embed|applet|mocha|i?frame|frameset|video|audio|area|map)(\s[\s\S]*?)?>/gi, '')
|
||||||
// GetDomFromText
|
// GetDomFromText
|
||||||
.replace('<o:p></o:p>', '')
|
.replace('<o:p></o:p>', '')
|
||||||
.replace('<o:p>', '<span>')
|
.replace('<o:p>', '<span>')
|
||||||
.replace('</o:p>', '</span>')
|
.replace('</o:p>', '</span>')
|
||||||
// https://github.com/the-djmaze/snappymail/issues/187
|
// https://github.com/the-djmaze/snappymail/issues/187
|
||||||
.replace(/<a(?:\s[^>]*)?>((?![\s\S]*<\/a)[\s\S]*?<a(\s[^>]*)?>)/gi, '$1')
|
// .replace(/<a(?:\s[^>]*)?>((?![\s\S]*<\/a)[\s\S]*?<a(\s[^>]*)?>)/gi, '$1')
|
||||||
// \MailSo\Base\HtmlUtils::ClearFastTags
|
// \MailSo\Base\HtmlUtils::ClearFastTags
|
||||||
.replace(/<p[^>]*><\/p>/i, '')
|
.replace(/<p[^>]*><\/p>/i, '')
|
||||||
.replace(/<!doctype[^>]*>/i, '')
|
.replace(/<!doctype[^>]*>/i, '')
|
||||||
|
|
@ -61,73 +129,13 @@ export const
|
||||||
.trim();
|
.trim();
|
||||||
html = '';
|
html = '';
|
||||||
|
|
||||||
// convert body attributes to CSS
|
|
||||||
const tasks = {
|
|
||||||
link: value => {
|
|
||||||
if (/^#[a-fA-Z0-9]{3,6}$/.test(value)) {
|
|
||||||
tpl.content.querySelectorAll('a').forEach(node => node.style.color = value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
text: (value, node) => node.style.color = value,
|
|
||||||
topmargin: (value, node) => node.style.marginTop = pInt(value) + 'px',
|
|
||||||
leftmargin: (value, node) => node.style.marginLeft = pInt(value) + 'px',
|
|
||||||
bottommargin: (value, node) => node.style.marginBottom = pInt(value) + 'px',
|
|
||||||
rightmargin: (value, node) => node.style.marginRight = pInt(value) + 'px'
|
|
||||||
};
|
|
||||||
|
|
||||||
// if (static::Config()->Get('labs', 'strict_html_parser', true))
|
|
||||||
let allowedAttributes = [
|
|
||||||
// defaults
|
|
||||||
'name',
|
|
||||||
'dir', 'lang', 'style', 'title',
|
|
||||||
'background', 'bgcolor', 'alt', 'height', 'width', 'src', 'href',
|
|
||||||
'border', 'bordercolor', 'charset', 'direction', 'language',
|
|
||||||
// a
|
|
||||||
'coords', 'download', 'hreflang', 'shape',
|
|
||||||
// body
|
|
||||||
'alink', 'bgproperties', 'bottommargin', 'leftmargin', 'link', 'rightmargin', 'text', 'topmargin', 'vlink',
|
|
||||||
'marginwidth', 'marginheight', 'offset',
|
|
||||||
// button,
|
|
||||||
'disabled', 'type', 'value',
|
|
||||||
// col
|
|
||||||
'align', 'valign',
|
|
||||||
// font
|
|
||||||
'color', 'face', 'size',
|
|
||||||
// form
|
|
||||||
'novalidate',
|
|
||||||
// hr
|
|
||||||
'noshade',
|
|
||||||
// img
|
|
||||||
'hspace', 'sizes', 'srcset', 'vspace', 'usemap',
|
|
||||||
// input, textarea
|
|
||||||
'checked', 'max', 'min', 'maxlength', 'multiple', 'pattern', 'placeholder', 'readonly',
|
|
||||||
'required', 'step', 'wrap',
|
|
||||||
// label
|
|
||||||
'for',
|
|
||||||
// meter
|
|
||||||
'low', 'high', 'optimum',
|
|
||||||
// ol
|
|
||||||
'reversed', 'start',
|
|
||||||
// option
|
|
||||||
'selected', 'label',
|
|
||||||
// table
|
|
||||||
'cols', 'rows', 'frame', 'rules', 'summary', 'cellpadding', 'cellspacing',
|
|
||||||
// th
|
|
||||||
'abbr', 'scope',
|
|
||||||
// td
|
|
||||||
'axis', 'colspan', 'rowspan', 'headers', 'nowrap'
|
|
||||||
];
|
|
||||||
|
|
||||||
let disallowedAttributes = [
|
|
||||||
'id', 'class', 'contenteditable', 'designmode', 'formaction', 'manifest', 'action',
|
|
||||||
'data-bind', 'data-reactid', 'xmlns', 'srcset',
|
|
||||||
'fscommand', 'seeksegmenttime'
|
|
||||||
];
|
|
||||||
|
|
||||||
tpl.content.querySelectorAll('*').forEach(oElement => {
|
tpl.content.querySelectorAll('*').forEach(oElement => {
|
||||||
const name = oElement.tagName.toUpperCase(),
|
const name = oElement.tagName,
|
||||||
oStyle = oElement.style,
|
oStyle = oElement.style,
|
||||||
getAttribute = name => oElement.hasAttribute(name) ? oElement.getAttribute(name).trim() : '';
|
hasAttribute = name => oElement.hasAttribute(name),
|
||||||
|
getAttribute = name => hasAttribute(name) ? oElement.getAttribute(name).trim() : '',
|
||||||
|
setAttribute = (name, value) => oElement.setAttribute(name, value),
|
||||||
|
delAttribute = name => oElement.removeAttribute(name);
|
||||||
|
|
||||||
if (['HEAD','STYLE','SVG','SCRIPT','TITLE','INPUT','BUTTON','TEXTAREA','SELECT'].includes(name)
|
if (['HEAD','STYLE','SVG','SCRIPT','TITLE','INPUT','BUTTON','TEXTAREA','SELECT'].includes(name)
|
||||||
|| 'none' == oStyle.display
|
|| 'none' == oStyle.display
|
||||||
|
|
@ -143,19 +151,36 @@ export const
|
||||||
|
|
||||||
if ('BODY' === name) {
|
if ('BODY' === name) {
|
||||||
forEachObjectEntry(tasks, (name, cb) => {
|
forEachObjectEntry(tasks, (name, cb) => {
|
||||||
if (oElement.hasAttribute(name)) {
|
if (hasAttribute(name)) {
|
||||||
cb(getAttribute(name), oElement);
|
cb(getAttribute(name), oElement);
|
||||||
oElement.removeAttribute(name);
|
delAttribute(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('TABLE' === name && oElement.hasAttribute('width')) {
|
else if ('TABLE' === name && hasAttribute('width')) {
|
||||||
let value = getAttribute('width');
|
value = getAttribute('width');
|
||||||
oElement.removeAttribute('width');
|
delAttribute('width');
|
||||||
oStyle.maxWidth = value + (/^[0-9]+$/.test(value) ? 'px' : '');
|
oStyle.maxWidth = value + (/^[0-9]+$/.test(value) ? 'px' : '');
|
||||||
oStyle.removeProperty('width');
|
oStyle.removeProperty('width');
|
||||||
oStyle.removeProperty('min-width');
|
}
|
||||||
|
|
||||||
|
else if ('A' === name) {
|
||||||
|
value = oElement.href;
|
||||||
|
// https://github.com/the-djmaze/snappymail/issues/187 <a> can't have block element inside
|
||||||
|
if (!value || oElement.querySelector('td,div,p,li')) {
|
||||||
|
replaceWithChildren(oElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value = stripTracking(value);
|
||||||
|
if (!/^([a-z]+):/i.test(value) && '//' !== value.slice(0, 2)) {
|
||||||
|
setAttribute('data-x-broken-href', value);
|
||||||
|
delAttribute('href');
|
||||||
|
} else {
|
||||||
|
setAttribute('target', '_blank');
|
||||||
|
setAttribute('rel', 'external nofollow noopener noreferrer');
|
||||||
|
}
|
||||||
|
setAttribute('tabindex', '-1');
|
||||||
}
|
}
|
||||||
|
|
||||||
const aAttrsForRemove = [];
|
const aAttrsForRemove = [];
|
||||||
|
|
@ -171,39 +196,23 @@ export const
|
||||||
// || sAttrName.includes(':')
|
// || sAttrName.includes(':')
|
||||||
|| disallowedAttributes.includes(sAttrName))
|
|| disallowedAttributes.includes(sAttrName))
|
||||||
{
|
{
|
||||||
oElement.removeAttribute(sAttrName);
|
delAttribute(sAttrName);
|
||||||
aAttrsForRemove.push(sAttrName);
|
aAttrsForRemove.push(sAttrName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oElement.hasAttribute('href')) {
|
|
||||||
let sHref = stripTracking(getAttribute('href'));
|
|
||||||
if (!/^([a-z]+):/i.test(sHref) && '//' !== sHref.slice(0, 2)) {
|
|
||||||
oElement.setAttribute('data-x-broken-href', sHref);
|
|
||||||
oElement.removeAttribute('href');
|
|
||||||
}
|
|
||||||
if ('A' === name) {
|
|
||||||
oElement.setAttribute('rel', 'external nofollow noopener noreferrer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SVG xlink:href
|
// SVG xlink:href
|
||||||
/*
|
/*
|
||||||
if (oElement.hasAttribute('xlink:href')) {
|
if (hasAttribute('xlink:href')) {
|
||||||
oElement.removeAttribute('xlink:href');
|
delAttribute('xlink:href');
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if ('A' === name) {
|
|
||||||
oElement.setAttribute('tabindex', '-1');
|
|
||||||
oElement.setAttribute('target', '_blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
let skipStyle = false;
|
let skipStyle = false;
|
||||||
if (oElement.hasAttribute('src')) {
|
if (hasAttribute('src')) {
|
||||||
let sSrc = getAttribute('src');
|
value = getAttribute('src');
|
||||||
oElement.removeAttribute('src');
|
delAttribute('src');
|
||||||
|
|
||||||
if (detectHiddenImages
|
if (detectHiddenImages
|
||||||
&& 'IMG' === name
|
&& 'IMG' === name
|
||||||
|
|
@ -214,51 +223,50 @@ export const
|
||||||
'github.com/notifications/beacon/',
|
'github.com/notifications/beacon/',
|
||||||
'mandrillapp.com/track/open',
|
'mandrillapp.com/track/open',
|
||||||
'list-manage.com/track/open'
|
'list-manage.com/track/open'
|
||||||
].filter(uri => sSrc.toLowerCase().includes(uri)).length
|
].filter(uri => value.toLowerCase().includes(uri)).length
|
||||||
)) {
|
)) {
|
||||||
skipStyle = true;
|
skipStyle = true;
|
||||||
oElement.setAttribute('style', 'display:none');
|
setAttribute('style', 'display:none');
|
||||||
oElement.setAttribute('data-x-hidden-src', sSrc);
|
setAttribute('data-x-hidden-src', value);
|
||||||
}
|
}
|
||||||
else if (contentLocationUrls[sSrc])
|
else if (contentLocationUrls[value])
|
||||||
{
|
{
|
||||||
oElement.setAttribute('data-x-src-location', sSrc);
|
setAttribute('data-x-src-location', value);
|
||||||
result.foundContentLocationUrls.push(sSrc);
|
result.foundContentLocationUrls.push(value);
|
||||||
}
|
}
|
||||||
else if ('cid:' === sSrc.slice(0, 4))
|
else if ('cid:' === value.slice(0, 4))
|
||||||
{
|
{
|
||||||
oElement.setAttribute('data-x-src-cid', sSrc.slice(4));
|
setAttribute('data-x-src-cid', value.slice(4));
|
||||||
result.foundCIDs.push(sSrc.slice(4));
|
result.foundCIDs.push(value.slice(4));
|
||||||
}
|
}
|
||||||
else if (/^https?:\/\//i.test(sSrc) || '//' === sSrc.slice(0, 2))
|
else if (/^https?:\/\//i.test(value) || '//' === value.slice(0, 2))
|
||||||
{
|
{
|
||||||
oElement.setAttribute('data-x-src', useProxy ? proxy(sSrc) : sSrc);
|
setAttribute('data-x-src', useProxy ? proxy(value) : value);
|
||||||
result.hasExternals = true;
|
result.hasExternals = true;
|
||||||
}
|
}
|
||||||
else if ('data:image/' === sSrc.slice(0, 11))
|
else if ('data:image/' === value.slice(0, 11))
|
||||||
{
|
{
|
||||||
oElement.setAttribute('src', sSrc);
|
setAttribute('src', value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
oElement.setAttribute('data-x-broken-src', sSrc);
|
setAttribute('data-x-broken-src', value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oElement.hasAttribute('background')) {
|
if (hasAttribute('background')) {
|
||||||
let sBackground = getAttribute('background');
|
oStyle.backgroundImage = 'url("' + getAttribute('background') + '")';
|
||||||
if (sBackground) {
|
delAttribute('background');
|
||||||
oStyle.backgroundImage = 'url("' + sBackground + '")';
|
|
||||||
}
|
|
||||||
oElement.removeAttribute('background');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oElement.hasAttribute('bgcolor')) {
|
if (hasAttribute('bgcolor')) {
|
||||||
let sBackgroundColor = getAttribute('bgcolor');
|
oStyle.backgroundColor = getAttribute('bgcolor');
|
||||||
if (sBackgroundColor) {
|
delAttribute('bgcolor');
|
||||||
oStyle.backgroundColor = sBackgroundColor;
|
}
|
||||||
}
|
|
||||||
oElement.removeAttribute('bgcolor');
|
if (hasAttribute('color')) {
|
||||||
|
oStyle.color = getAttribute('color');
|
||||||
|
delAttribute('color');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipStyle) {
|
if (!skipStyle) {
|
||||||
|
|
@ -269,6 +277,7 @@ export const
|
||||||
*/
|
*/
|
||||||
oStyle.removeProperty('behavior');
|
oStyle.removeProperty('behavior');
|
||||||
oStyle.removeProperty('cursor');
|
oStyle.removeProperty('cursor');
|
||||||
|
oStyle.removeProperty('min-width');
|
||||||
|
|
||||||
const urls = {
|
const urls = {
|
||||||
cid: [], // 'data-x-style-cid'
|
cid: [], // 'data-x-style-cid'
|
||||||
|
|
@ -302,22 +311,31 @@ export const
|
||||||
// oStyle.removeProperty('list-style-image');
|
// oStyle.removeProperty('list-style-image');
|
||||||
|
|
||||||
if (urls.cid.length) {
|
if (urls.cid.length) {
|
||||||
oElement.setAttribute('data-x-style-cid', JSON.stringify(urls.cid));
|
setAttribute('data-x-style-cid', JSON.stringify(urls.cid));
|
||||||
}
|
}
|
||||||
if (urls.remote.length) {
|
if (urls.remote.length) {
|
||||||
oElement.setAttribute('data-x-style-url', JSON.stringify(urls.remote));
|
setAttribute('data-x-style-url', JSON.stringify(urls.remote));
|
||||||
}
|
}
|
||||||
if (urls.broken.length) {
|
if (urls.broken.length) {
|
||||||
oElement.setAttribute('data-x-style-broken-urls', JSON.stringify(urls.broken));
|
setAttribute('data-x-style-broken-urls', JSON.stringify(urls.broken));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (11 < pInt(oStyle.fontSize)) {
|
if (11 < pInt(oStyle.fontSize)) {
|
||||||
oStyle.removeProperty('font-size');
|
oStyle.removeProperty('font-size');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Removes background and color
|
||||||
|
// Many e-mails incorrectly only define one, not both
|
||||||
|
// And in dark theme mode this kills the readability
|
||||||
|
if (removeColors) {
|
||||||
|
oStyle.removeProperty('background-color');
|
||||||
|
oStyle.removeProperty('background-image');
|
||||||
|
oStyle.removeProperty('color');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug && aAttrsForRemove) {
|
if (debug && aAttrsForRemove.length) {
|
||||||
oElement.setAttribute('data-removed-attrs', aAttrsForRemove.join(', '));
|
setAttribute('data-removed-attrs', aAttrsForRemove.join(', '));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -326,21 +344,6 @@ export const
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Removes background and color
|
|
||||||
// Many e-mails incorrectly only define one, not both
|
|
||||||
// And in dark theme mode this kills the readability
|
|
||||||
removeColors = html => {
|
|
||||||
let l;
|
|
||||||
do {
|
|
||||||
l = html.length;
|
|
||||||
html = html
|
|
||||||
.replace(/(<[^>]+[;"'])\s*background(-[a-z]+)?\s*:[^;"']+/gi, '$1')
|
|
||||||
.replace(/(<[^>]+[;"'])\s*color\s*:[^;"']+/gi, '$1')
|
|
||||||
.replace(/(<[^>]+)\s(bg)?color=("[^"]+"|'[^']+')/gi, '$1');
|
|
||||||
} while (l != html.length)
|
|
||||||
return html;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} html
|
* @param {string} html
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
|
|
@ -356,7 +359,7 @@ export const
|
||||||
blockquotes(bq);
|
blockquotes(bq);
|
||||||
// Convert blockquote
|
// Convert blockquote
|
||||||
bq.innerHTML = '\n' + ('\n' + bq.innerHTML.replace(/\n{3,}/gm, '\n\n').trim() + '\n').replace(/^/gm, '> ');
|
bq.innerHTML = '\n' + ('\n' + bq.innerHTML.replace(/\n{3,}/gm, '\n\n').trim() + '\n').replace(/^/gm, '> ');
|
||||||
bq.replaceWith(...[...bq.childNodes]);
|
replaceWithChildren(bq);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { MessagePriority } from 'Common/EnumsUser';
|
||||||
import { i18n } from 'Common/Translator';
|
import { i18n } from 'Common/Translator';
|
||||||
|
|
||||||
import { doc } from 'Common/Globals';
|
import { doc } from 'Common/Globals';
|
||||||
import { encodeHtml, removeColors, plainToHtml, clearHtml } from 'Common/Html';
|
import { encodeHtml, plainToHtml, cleanHtml } from 'Common/Html';
|
||||||
import { isArray, arrayLength, forEachObjectEntry } from 'Common/Utils';
|
import { isArray, arrayLength, forEachObjectEntry } from 'Common/Utils';
|
||||||
import { serverRequestRaw } from 'Common/Links';
|
import { serverRequestRaw } from 'Common/Links';
|
||||||
|
|
||||||
|
|
@ -380,20 +380,17 @@ export class MessageModel extends AbstractModel {
|
||||||
viewHtml() {
|
viewHtml() {
|
||||||
const body = this.body;
|
const body = this.body;
|
||||||
if (body && this.html()) {
|
if (body && this.html()) {
|
||||||
let html = this.html();
|
|
||||||
if (SettingsUserStore.removeColors()) {
|
|
||||||
html = removeColors(html);
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentLocationUrls = {},
|
const contentLocationUrls = {},
|
||||||
oAttachments = this.attachments();
|
oAttachments = this.attachments();
|
||||||
|
|
||||||
|
// Get contentLocationUrls
|
||||||
oAttachments.forEach(oAttachment => {
|
oAttachments.forEach(oAttachment => {
|
||||||
if (oAttachment.cid && oAttachment.contentLocation) {
|
if (oAttachment.cid && oAttachment.contentLocation) {
|
||||||
contentLocationUrls[oAttachment.contentId()] = oAttachment.contentLocation;
|
contentLocationUrls[oAttachment.contentId()] = oAttachment.contentLocation;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let result = clearHtml(html, contentLocationUrls);
|
let result = cleanHtml(this.html(), contentLocationUrls, SettingsUserStore.removeColors());
|
||||||
this.hasExternals(result.hasExternals);
|
this.hasExternals(result.hasExternals);
|
||||||
// this.hasInternals = result.foundCIDs.length || result.foundContentLocationUrls.length;
|
// this.hasInternals = result.foundCIDs.length || result.foundContentLocationUrls.length;
|
||||||
this.hasImages(body.rlHasImages = !!result.hasExternals);
|
this.hasImages(body.rlHasImages = !!result.hasExternals);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue