[FIX] web_editor, mass_mailing: support responsive columns in mailings
Since flexbox is not well supported across e-mail clients, columns were until now not responsive. This adds support for responsive columns and a special rendering of them for Outlook so they don't break there. task-2761098 Part-of: odoo/odoo#87962
This commit is contained in:
@@ -60,6 +60,14 @@
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1135px) {
|
||||
td {
|
||||
max-width: inherit !important;
|
||||
}
|
||||
td > img:only-child {
|
||||
min-width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -128,6 +128,8 @@ function bootstrapToTable($editable) {
|
||||
|
||||
// Now convert all containers with rows to tables.
|
||||
for (const container of [...editable.querySelectorAll('.container, .container-fluid, .o_fake_table')].filter(n => [...n.children].some(c => c.classList.contains('row')))) {
|
||||
const containerWidth = _getWidth(container);
|
||||
|
||||
// TABLE
|
||||
const table = _createTable(container.attributes);
|
||||
for (const child of [...container.childNodes]) {
|
||||
@@ -188,7 +190,7 @@ function bootstrapToTable($editable) {
|
||||
const columnSize = _getColumnSize(bootstrapColumn);
|
||||
if (gridIndex + columnSize < 12) {
|
||||
currentCol = grid[gridIndex];
|
||||
_applyColspan(currentCol, columnSize);
|
||||
_applyColspan(currentCol, columnSize, containerWidth);
|
||||
if (columnIndex === bootstrapColumns.length - 1) {
|
||||
// We handled all the columns but there is still space
|
||||
// in the row. Insert the columns and fill the row.
|
||||
@@ -199,7 +201,7 @@ function bootstrapToTable($editable) {
|
||||
} else if (gridIndex + columnSize === 12) {
|
||||
// Finish the row.
|
||||
currentCol = grid[gridIndex];
|
||||
_applyColspan(currentCol, columnSize);
|
||||
_applyColspan(currentCol, columnSize, containerWidth);
|
||||
currentRow.append(...grid.filter(td => td.getAttribute('colspan')));
|
||||
if (columnIndex !== bootstrapColumns.length - 1) {
|
||||
// The row was filled before we handled all of its
|
||||
@@ -213,7 +215,7 @@ function bootstrapToTable($editable) {
|
||||
} else {
|
||||
// Fill the row with what was in the grid before it
|
||||
// overflowed.
|
||||
_applyColspan(grid[gridIndex], 12 - gridIndex);
|
||||
_applyColspan(grid[gridIndex], 12 - gridIndex, containerWidth);
|
||||
currentRow.append(...grid.filter(td => td.getAttribute('colspan')));
|
||||
// Start a new row that starts with the current col.
|
||||
const previousRow = currentRow;
|
||||
@@ -221,7 +223,7 @@ function bootstrapToTable($editable) {
|
||||
previousRow.after(currentRow);
|
||||
grid = _createColumnGrid();
|
||||
currentCol = grid[0];
|
||||
_applyColspan(currentCol, columnSize);
|
||||
_applyColspan(currentCol, columnSize, containerWidth);
|
||||
gridIndex = columnSize;
|
||||
if (columnIndex === bootstrapColumns.length - 1 && gridIndex < 12) {
|
||||
// We handled all the columns but there is still space
|
||||
@@ -229,7 +231,7 @@ function bootstrapToTable($editable) {
|
||||
grid[gridIndex].setAttribute('colspan', 12 - gridIndex);
|
||||
currentRow.append(...grid.filter(td => td.getAttribute('colspan')));
|
||||
// Adapt width to colspan.
|
||||
_applyColspan(grid[gridIndex], 12 - gridIndex);
|
||||
_applyColspan(grid[gridIndex], 12 - gridIndex, containerWidth);
|
||||
}
|
||||
}
|
||||
if (currentCol) {
|
||||
@@ -247,7 +249,7 @@ function bootstrapToTable($editable) {
|
||||
currentCol.append(child);
|
||||
}
|
||||
// Adapt width to colspan.
|
||||
_applyColspan(currentCol, +currentCol.getAttribute('colspan'));
|
||||
_applyColspan(currentCol, +currentCol.getAttribute('colspan'), containerWidth);
|
||||
}
|
||||
columnIndex++;
|
||||
}
|
||||
@@ -391,6 +393,23 @@ function classToStyle($editable, cssRules) {
|
||||
};
|
||||
writes.forEach(fn => fn());
|
||||
}
|
||||
/**
|
||||
* Add styles to all table rows and columns, that are necessary for them to be
|
||||
* responsive. This only works if columns have a max-width so the styles are
|
||||
* only applied to columns where that is the case.
|
||||
*
|
||||
* @param {Element} editable
|
||||
*/
|
||||
function enforceTablesResponsivity(editable) {
|
||||
for (const tr of editable.querySelectorAll('tr')) {
|
||||
tr.style.setProperty('width', '100%');
|
||||
}
|
||||
for (const td of editable.querySelectorAll('td[style*="max-width"]')) {
|
||||
td.setAttribute('width', '100%');
|
||||
td.style.setProperty('width', '100%');
|
||||
td.style.setProperty('display', 'inline-block');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert the contents of an editable area (as a JQuery element) into content
|
||||
* that is widely compatible with email clients. If no CSS Rules are given, they
|
||||
@@ -456,11 +475,13 @@ async function toInline($editable, cssRules, $iframe) {
|
||||
cardToTable($editable);
|
||||
listGroupToTable($editable);
|
||||
addTables($editable);
|
||||
formatTables($editable);
|
||||
normalizeColors($editable);
|
||||
const rootFontSizeProperty = getComputedStyle(editable.ownerDocument.documentElement).fontSize;
|
||||
const rootFontSize = parseFloat(rootFontSizeProperty.replace(/[^\d\.]/g, ''));
|
||||
normalizeRem($editable, rootFontSize);
|
||||
responsiveToStaticForOutlook(editable);
|
||||
enforceTablesResponsivity(editable);
|
||||
formatTables($editable);
|
||||
await flattenBackgroundImages(editable);
|
||||
|
||||
// Styles were applied inline, we don't need a style element anymore.
|
||||
@@ -668,14 +689,14 @@ function formatTables($editable) {
|
||||
}
|
||||
}
|
||||
// Align items doesn't work on table rows.
|
||||
for (const cell of editable.querySelectorAll('tr')) {
|
||||
const alignItems = cell.style.alignItems;
|
||||
for (const row of editable.querySelectorAll('tr')) {
|
||||
const alignItems = row.style.alignItems;
|
||||
if (alignItems === 'flex-start') {
|
||||
cell.style.verticalAlign = 'top';
|
||||
row.style.verticalAlign = 'top';
|
||||
} else if (alignItems === 'center') {
|
||||
cell.style.verticalAlign = 'middle';
|
||||
row.style.verticalAlign = 'middle';
|
||||
} else if (alignItems === 'flex-end' || alignItems === 'baseline') {
|
||||
cell.style.verticalAlign = 'bottom';
|
||||
row.style.verticalAlign = 'bottom';
|
||||
}
|
||||
}
|
||||
// Tables don't properly inherit alignments from their ancestors in Outlook.
|
||||
@@ -690,6 +711,11 @@ function formatTables($editable) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Hide replaced cells on Outlook
|
||||
for (const toHide of editable.querySelectorAll('.mso-hide')) {
|
||||
const style = toHide.getAttribute('style') || '';
|
||||
toHide.setAttribute('style', `${style} mso-hide: all;`.trim());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse through the given document's stylesheets, preprocess(*) them and return
|
||||
@@ -839,6 +865,36 @@ function normalizeRem($editable, rootFontSize=16) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This replaces column html with a dumbed down, Outlook-compliant version of
|
||||
* them just for Outlook so while not responsive, these columns still display OK
|
||||
* on Outlook.
|
||||
*
|
||||
* @param {Element} editable
|
||||
*/
|
||||
function responsiveToStaticForOutlook(editable) {
|
||||
// Replace the responsive tables with static ones for Outlook
|
||||
for (const td of editable.querySelectorAll('td')) {
|
||||
const tdStyle = td.getAttribute('style') || '';
|
||||
const msoAttributes = [...td.attributes].filter(attr => attr.name !== 'style' && attr.name !== 'width');
|
||||
const msoWidth = td.style.getPropertyValue('max-width');
|
||||
const msoStyles = tdStyle.replace(/(^| |max-)width:[^;]*;\s*/g, '');
|
||||
const outlookTd = document.createElement('td');
|
||||
for (const attribute of msoAttributes) {
|
||||
outlookTd.setAttribute(attribute.name, td.getAttribute(attribute.name));
|
||||
}
|
||||
if (msoWidth) {
|
||||
outlookTd.setAttribute('width', ('' + msoWidth).replace('px', '').trim());
|
||||
outlookTd.setAttribute('style', `${msoStyles}width: ${msoWidth};`);
|
||||
} else {
|
||||
outlookTd.setAttribute('style', msoStyles);
|
||||
}
|
||||
td.before(document.createComment(`[if mso]>${outlookTd.outerHTML.replace('</td>', '')}<![endif]`));
|
||||
td.before(document.createComment('[if !mso]><!--'));
|
||||
td.prepend(document.createComment('<![endif]'));
|
||||
td.after(document.createComment(`[if mso]></td><![endif]`));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Convert images of type svg to png.
|
||||
*
|
||||
@@ -885,13 +941,14 @@ async function svgToPng($editable) {
|
||||
*
|
||||
* @param {Element} element
|
||||
* @param {number} colspan
|
||||
* @param {number} tableWidth
|
||||
*/
|
||||
function _applyColspan(element, colspan) {
|
||||
function _applyColspan(element, colspan, tableWidth) {
|
||||
element.setAttribute('colspan', colspan);
|
||||
const widthPercentage = +element.getAttribute('colspan') / 12;
|
||||
// Round to 2 decimal places.
|
||||
const width = (Math.round(+element.getAttribute('colspan') * 10000 / 12) / 100) + '%';
|
||||
element.setAttribute('width', width);
|
||||
element.style.setProperty('width', width);
|
||||
const width = Math.round(tableWidth * widthPercentage * 100) / 100;
|
||||
element.style.setProperty('max-width', width + 'px');
|
||||
}
|
||||
/**
|
||||
* Take a selector and return its specificity according to the w3 specification.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** @odoo-module **/
|
||||
import convertInline from '@web_editor/js/backend/convert_inline';
|
||||
import {getGridHtml, getTableHtml, getRegularGridHtml, getRegularTableHtml} from 'web_editor.test_utils';
|
||||
import {getGridHtml, getTableHtml, getRegularGridHtml, getRegularTableHtml, removeComments} from 'web_editor.test_utils';
|
||||
|
||||
QUnit.module('web_editor', {}, function () {
|
||||
QUnit.module('convert_inline', {}, function () {
|
||||
@@ -13,25 +13,25 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 1x1
|
||||
let $editable = $(`<div>${getRegularGridHtml(1, 1)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(1, 1, 12, 100),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(1, 1, 12, 100),
|
||||
"should have converted a 1x1 grid to an equivalent table");
|
||||
|
||||
// 1x2
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 2)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(1, 2, 6, 50),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(1, 2, 6, 50),
|
||||
"should have converted a 1x2 grid to an equivalent table");
|
||||
|
||||
// 1x3
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 3)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(1, 3, 4, 33.33),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(1, 3, 4, 33.33),
|
||||
"should have converted a 1x3 grid to an equivalent table");
|
||||
|
||||
// 1x12
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 12)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(1, 12, 1, 8.33),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(1, 12, 1, 8.33),
|
||||
"should have converted a 1x12 grid to an equivalent table");
|
||||
});
|
||||
QUnit.test('convert a single-row regular overflowing grid', async function (assert) {
|
||||
@@ -40,39 +40,39 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 1x13
|
||||
let $editable = $(`<div>${getRegularGridHtml(1, 13)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(1, 12, 1, 8.33).slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(0, 12)</td></tr></table>`,
|
||||
`<tr><td colspan="12">(0, 12)</td></tr></table>`,
|
||||
"should have converted a 1x13 grid to an equivalent table (overflowing)");
|
||||
|
||||
// 1x14
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 14)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(1, 12, 1, 8.33).slice(0, -8) +
|
||||
`<tr><td colspan="1" width="8.33%" style="width: 8.33%;">(0, 12)</td>` +
|
||||
`<td colspan="11" width="91.67%" style="width: 91.67%;">(0, 13)</td></tr></table>`,
|
||||
`<tr><td colspan="1">(0, 12)</td>` +
|
||||
`<td colspan="11">(0, 13)</td></tr></table>`,
|
||||
"should have converted a 1x14 grid to an equivalent table (overflowing)");
|
||||
|
||||
// 1x25
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 25)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(1, 12, 1, 8.33).slice(0, -8) +
|
||||
getRegularTableHtml(1, 12, 1, 8.33).replace(/\(0, (\d+)\)/g, (s, c) => `(0, ${+c + 12})`)
|
||||
.replace(/^<table[^<]*>/, '').slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(0, 24)</td></tr></table>`,
|
||||
`<tr><td colspan="12">(0, 24)</td></tr></table>`,
|
||||
"should have converted a 1x25 grid to an equivalent table (overflowing)");
|
||||
|
||||
// 1x26
|
||||
$editable = $(`<div>${getRegularGridHtml(1, 26)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(1, 12, 1, 8.33).slice(0, -8) +
|
||||
getRegularTableHtml(1, 12, 1, 8.33).replace(/\(0, (\d+)\)/g, (s, c) => `(0, ${+c + 12})`)
|
||||
.replace(/^<table[^<]*>/, '').slice(0, -8) +
|
||||
`<tr><td colspan="1" width="8.33%" style="width: 8.33%;">(0, 24)</td>` +
|
||||
`<td colspan="11" width="91.67%" style="width: 91.67%;">(0, 25)</td></tr></table>`,
|
||||
`<tr><td colspan="1">(0, 24)</td>` +
|
||||
`<td colspan="11">(0, 25)</td></tr></table>`,
|
||||
"should have converted a 1x26 grid to an equivalent table (overflowing)");
|
||||
});
|
||||
QUnit.test('convert a multi-row regular grid', async function (assert) {
|
||||
@@ -81,25 +81,25 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 2x1
|
||||
let $editable = $(`<div>${getRegularGridHtml(2, 1)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(2, 1, 12, 100),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(2, 1, 12, 100),
|
||||
"should have converted a 2x1 grid to an equivalent table");
|
||||
|
||||
// 2x[1,2]
|
||||
$editable = $(`<div>${getRegularGridHtml(2, [1, 2])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(2, [1, 2], [12, 6], [100, 50]),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(2, [1, 2], [12, 6], [100, 50], true),
|
||||
"should have converted a 2x[1,2] grid to an equivalent table");
|
||||
|
||||
// 3x3
|
||||
$editable = $(`<div>${getRegularGridHtml(3, 3)}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(3, 3, 4, 33.33),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(3, 3, 4, 33.33),
|
||||
"should have converted a 3x3 grid to an equivalent table");
|
||||
|
||||
// 3x[3,2,1]
|
||||
$editable = $(`<div>${getRegularGridHtml(3, [3,2,1])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getRegularTableHtml(3, [3, 2, 1], [4, 6, 12], [33.33, 50, 100]),
|
||||
assert.strictEqual(removeComments($editable.html()), getRegularTableHtml(3, [3, 2, 1], [4, 6, 12], [33.33, 50, 100], true),
|
||||
"should have converted a 3x[3,2,1] grid to an equivalent table");
|
||||
});
|
||||
QUnit.test('convert a multi-row regular overflowing grid', async function (assert) {
|
||||
@@ -108,35 +108,35 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 2x[13,1]
|
||||
let $editable = $(`<div>${getRegularGridHtml(2, [13, 1])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(1, 12, 1, 8.33).slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(0, 12)</td></tr>` +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(1, 0)</td></tr></table>`,
|
||||
`<tr><td colspan="12">(0, 12)</td></tr>` +
|
||||
`<tr><td colspan="12">(1, 0)</td></tr></table>`,
|
||||
"should have converted a 2x[13,1] grid to an equivalent table (overflowing)");
|
||||
|
||||
// 2x[1,13]
|
||||
$editable = $(`<div>${getRegularGridHtml(2, [1, 13])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
getRegularTableHtml(2, [1, 12], [12, 1], [100, 8.33]).slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(1, 12)</td></tr></table>`,
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(2, [1, 12], [12, 1], [100, 8.33], true).slice(0, -8) +
|
||||
`<tr><td colspan="12">(1, 12)</td></tr></table>`,
|
||||
"should have converted a 2x[1,13] grid to an equivalent table (overflowing)");
|
||||
|
||||
// 3x[1,13,6]
|
||||
$editable = $(`<div>${getRegularGridHtml(3, [1, 13, 6])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
getRegularTableHtml(2, [1, 12], [12, 1], [100, 8.33]).slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(1, 12)</td></tr>` +
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(2, [1, 12], [12, 1], [100, 8.33], true).slice(0, -8) +
|
||||
`<tr><td colspan="12">(1, 12)</td></tr>` +
|
||||
getRegularTableHtml(1, 6, 2, 16.67).replace(/\(0,/g, `(2,`).replace(/^<table[^<]*>/, ''),
|
||||
"should have converted a 3x[1,13,6] grid to an equivalent table (overflowing)");
|
||||
|
||||
// 3x[1,6,13]
|
||||
$editable = $(`<div>${getRegularGridHtml(3, [1, 6, 13])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
getRegularTableHtml(3, [1, 6, 12], [12, 2, 1], [100, 16.67, 8.33]).slice(0, -8) +
|
||||
`<tr><td colspan="12" width="100%" style="width: 100%;">(2, 12)</td></tr></table>`,
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getRegularTableHtml(3, [1, 6, 12], [12, 2, 1], [100, 16.67, 8.33], true).slice(0, -8) +
|
||||
`<tr><td colspan="12">(2, 12)</td></tr></table>`,
|
||||
"should have converted a 3x[1,6,13] grid to an equivalent table (overflowing)");
|
||||
});
|
||||
QUnit.test('convert a single-row irregular grid', async function (assert) {assert.expect(4);
|
||||
@@ -145,13 +145,13 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 1x2
|
||||
let $editable = $(`<div>${getGridHtml([[8, 4]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([[[8, 66.67], [4, 33.33]]]),
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([[[8, 66.67], [4, 33.33]]], true),
|
||||
"should have converted a 1x2 irregular grid to an equivalent table");
|
||||
|
||||
// 1x3
|
||||
$editable = $(`<div>${getGridHtml([[2, 3, 7]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([[[2, 16.67], [3, 25], [7, 58.33]]]),
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([[[2, 16.67], [3, 25], [7, 58.33]]], true),
|
||||
"should have converted a 1x3 grid to an equivalent table");
|
||||
});
|
||||
QUnit.test('convert a single-row irregular overflowing grid', async function (assert) {assert.expect(4);
|
||||
@@ -160,20 +160,20 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 1x2
|
||||
let $editable = $(`<div>${getGridHtml([[8, 5]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([
|
||||
[[8, 66.67], [4, 33.33, '']],
|
||||
[[5, 41.67, '(0, 1)'], [7, 58.33, '']],
|
||||
]),
|
||||
], true),
|
||||
"should have converted a 1x2 irregular overflowing grid to an equivalent table");
|
||||
|
||||
// 1x3
|
||||
$editable = $(`<div>${getGridHtml([[7, 6, 9]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([
|
||||
[[7, 58.33], [5, 41.67, '']],
|
||||
[[6, 50, '(0, 1)'], [6, 50, '']],
|
||||
[[9, 75, '(0, 2)'], [3, 25, '']],
|
||||
]),
|
||||
], true),
|
||||
"should have converted a 1x3 irregular overflowing grid to an equivalent table");
|
||||
});
|
||||
QUnit.test('convert a multi-row irregular grid', async function (assert) {
|
||||
@@ -182,13 +182,13 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 2x2
|
||||
let $editable = $(`<div>${getGridHtml([[1, 11], [2, 10]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([[[1, 8.33], [11, 91.67]], [[2, 16.67], [10, 83.33]]]),
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([[[1, 8.33], [11, 91.67]], [[2, 16.67], [10, 83.33]]], true),
|
||||
"should have converted a 2x2 irregular grid to an equivalent table");
|
||||
|
||||
// 2x[2,3]
|
||||
$editable = $(`<div>${getGridHtml([[3, 9], [4, 6, 2]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(), getTableHtml([[[3, 25], [9, 75]], [[4, 33.33], [6, 50], [2, 16.67]]]),
|
||||
assert.strictEqual(removeComments($editable.html()), getTableHtml([[[3, 25], [9, 75]], [[4, 33.33], [6, 50], [2, 16.67]]], true),
|
||||
"should have converted a 2x[2,3] irregular grid to an equivalent table");
|
||||
});
|
||||
QUnit.test('convert a multi-row irregular overflowing grid', async function (assert) {
|
||||
@@ -197,35 +197,35 @@ QUnit.module('convert_inline', {}, function () {
|
||||
// 2x2 (both rows overflow)
|
||||
let $editable = $(`<div>${getGridHtml([[6, 8], [7, 9]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getTableHtml([
|
||||
[[6, 50], [6, 50, '']],
|
||||
[[8, 66.67, '(0, 1)'], [4, 33.33, '']],
|
||||
[[7, 58.33, '(1, 0)'], [5, 41.67, '']],
|
||||
[[9, 75, '(1, 1)'], [3, 25, '']],
|
||||
]),
|
||||
], true),
|
||||
"should have converted a 2x[1,13] irregular grid to an equivalent table (both rows overflowing)");
|
||||
|
||||
// 2x[2,3] (first row overflows)
|
||||
$editable = $(`<div>${getGridHtml([[5, 8], [4, 2, 6]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getTableHtml([
|
||||
[[5, 41.67], [7, 58.33, '']],
|
||||
[[8, 66.67, '(0, 1)'], [4, 33.33, '']],
|
||||
[[4, 33.33, '(1, 0)'], [2, 16.67, '(1, 1)'], [6, 50, '(1, 2)']],
|
||||
]),
|
||||
], true),
|
||||
"should have converted a 2x[2,3] irregular grid to an equivalent table (first row overflowing)");
|
||||
|
||||
// 2x[3,2] (second row overflows)
|
||||
$editable = $(`<div>${getGridHtml([[4, 2, 6], [5, 8]])}</div>`);
|
||||
convertInline.bootstrapToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
assert.strictEqual(removeComments($editable.html()),
|
||||
getTableHtml([
|
||||
[[4, 33.33], [2, 16.67], [6, 50]],
|
||||
[[5, 41.67], [7, 58.33, '']],
|
||||
[[8, 66.67, '(1, 1)'], [4, 33.33, '']],
|
||||
]),
|
||||
], true),
|
||||
"should have converted a 2x[3,2] irregular grid to an equivalent table (second row overflowing)");
|
||||
});
|
||||
QUnit.test('convert a card to a table', async function (assert) {
|
||||
@@ -247,7 +247,7 @@ QUnit.module('convert_inline', {}, function () {
|
||||
convertInline.cardToTable($editable);
|
||||
assert.strictEqual($editable.html(),
|
||||
getRegularTableHtml(3, 1, 12, 100)
|
||||
.split('style="').join('class="card" style="')
|
||||
.replace('role=\"presentation\"', 'role=\"presentation\" class=\"card\"')
|
||||
.replace(/<td[^>]*>\(0, 0\)<\/td>/,
|
||||
`<td>` +
|
||||
`<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" align=\"center\" ` +
|
||||
|
||||
@@ -801,7 +801,7 @@ function getTableHtml(matrix) {
|
||||
matrix.map((row, iRow) => (
|
||||
`<tr>` +
|
||||
row.map((col, iCol) => (
|
||||
`<td colspan="${col[0]}" width="${col[1]}%" style="width: ${col[1]}%;">` +
|
||||
`<td colspan="${col[0]}">` +
|
||||
(col.length === 3 ? col[2] : `(${iRow}, ${iCol})`) +
|
||||
`</td>`
|
||||
)).join('') +
|
||||
@@ -852,6 +852,23 @@ function getRegularTableHtml(nRows, nCols, colspan, width) {
|
||||
);
|
||||
return getTableHtml(matrix);
|
||||
}
|
||||
/**
|
||||
* Take an HTML string and returns that string stripped from any HTML comments.
|
||||
* By default, also removes the mso-hide class which is only there for outlook
|
||||
* to hide elements when we use mso conditional comments.
|
||||
*
|
||||
* @param {string} html
|
||||
* @param {boolean} [removeMsoHide=true]
|
||||
* @returns {string}
|
||||
*/
|
||||
function removeComments(html, removeMsoHide=true) {
|
||||
const cleanHtml = html.replace(/<!--(.*?)-->/g, '');
|
||||
if (removeMsoHide) {
|
||||
return cleanHtml.replaceAll(' class="mso-hide"', '').replace(/\s*mso-hide/g, '').replace(/mso-hide\s*/g, '');
|
||||
} else {
|
||||
return cleanHtml;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
wysiwygData: wysiwygData,
|
||||
@@ -865,6 +882,7 @@ return {
|
||||
getTableHtml: getTableHtml,
|
||||
getRegularGridHtml: getRegularGridHtml,
|
||||
getRegularTableHtml: getRegularTableHtml,
|
||||
removeComments: removeComments,
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user