Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions static/js/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

const tags = ['left', 'center', 'justify', 'right'];

// Match `text-align: <value>` in a CSS style attribute. The leading
// boundary keeps us from accidentally matching `vertical-align` etc.
const STYLE_TEXT_ALIGN_RE = /(?:^|;|\s)text-align\s*:\s*(left|center|right|justify)\b/i;

exports.collectContentPre = (hookName, context, cb) => {
const tname = context.tname;
const state = context.state;
Expand All @@ -13,6 +17,17 @@ exports.collectContentPre = (hookName, context, cb) => {
if (tagIndex >= 0) {
lineAttributes.align = tags[tagIndex];
}
// Pick up `style="text-align:..."` on imported HTML so a round-trip
// through HTML or DOCX preserves alignment. Etherpad core's
// `getLineHTMLForExport` already serializes the alignment to an
// inline style on `<p>` / `<h1..h6>`, but without this the importer
// dropped it on the way back.
if (context.styl) {
const m = STYLE_TEXT_ALIGN_RE.exec(context.styl);
if (m) {
lineAttributes.align = m[1].toLowerCase();
}
}
return cb();
};

Expand Down
27 changes: 27 additions & 0 deletions static/tests/backend/specs/exportHTML.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,33 @@ describe('export alignment to HTML', function () {
});
});

context('when imported HTML uses style="text-align"', function () {
// Regression for the round-trip: getLineHTMLForExport already emits
// <p style="text-align:..."> on export, so importing that HTML (or
// any HTML produced by mammoth from a DOCX) needs collectContentPre
// to read the style attribute. Without the fix, alignment was lost
// on import even though the legacy <left>/<center>/<right>/<justify>
// tags worked.
for (const align of ['left', 'center', 'right', 'justify']) {
it(`preserves text-align:${align} from inline style`, async function () {
// Set the pad HTML using `style="text-align:..."` directly.
const padHtml = `<p style="text-align:${align}">Hello world</p>`;
const newPadID = randomString(5);
await createPad(newPadID);
await setHTML(newPadID, buildHTML(padHtml));
const res = await agent.get(getHTMLEndPointFor(newPadID))
.set('Authorization', await generateJWTToken());
const html = res.body.data.html;
const expected =
new RegExp(`<p +style='text-align:${align}'>Hello world</p>`);
if (html.search(expected) === -1) {
throw new Error(
`Expected ${expected} in re-exported HTML, got: ${html}`);
}
});
}
});

context('when pad text is heading', function () {
before(async function () {
html = () => buildHTML('<h1><left>Hello world</left></h1>');
Expand Down
Loading