Skip to content

Commit 21fef4d

Browse files
authored
feat(attributes): expose ccRegisterBlockElements (#14)
Etherpad core's import-side content collector keeps its own `_blockElems` set (`{div, p, pre, li}` by default), separate from the editor's. `aceRegisterBlockElements` registers tags on the editor side only -- so plugins built on `lineAttribute` / `tagAttribute` (`ep_headings2`, `ep_subscript_and_superscript`, etc.) tell the editor that `h1..h4` / `code` / `sub` / `sup` are block elements but DON'T tell the importer. Symptom: round-tripping a pad with `<h1>` / `<h2>` / `<code>` lines through any HTML import path collapses adjacent heading-style blocks into a single pad line. This just bit native DOCX export+ import in ether/etherpad#7568. Fix: have `createLineAttribute` and `createTagAttribute` return a `ccRegisterBlockElements` function with the same tag set as `aceRegisterBlockElements`. Plugins re-export this from their `ep.json` under `"ccRegisterBlockElements"` to register the same tags on the import side. Tests added for both factories. Closes #13
1 parent 5db3a16 commit 21fef4d

2 files changed

Lines changed: 39 additions & 0 deletions

File tree

attributes.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ const createLineAttribute = (config) => {
3232

3333
const aceRegisterBlockElements = () => tags;
3434

35+
// Server-side counterpart of aceRegisterBlockElements. Etherpad core's
36+
// import-side content collector keeps its own `_blockElems` set
37+
// separate from the editor's, so plugins that want their custom block
38+
// tags treated as block elements on import (so adjacent ones don't
39+
// merge into a single pad line) need to register them on the server
40+
// side too. A plugin that uses `lineAttribute({tags: [...]})` should
41+
// re-export this hook from its `ep.json` under
42+
// `"ccRegisterBlockElements"`.
43+
const ccRegisterBlockElements = () => tags;
44+
3545
const collectContentPre = (hookName, context, cb) => {
3646
const tname = context.tname;
3747
const lineAttributes = context.state.lineAttributes;
@@ -60,6 +70,7 @@ const createLineAttribute = (config) => {
6070
aceDomLineProcessLineAttributes,
6171
aceRegisterBlockElements,
6272
aceRegisterLineAttributes,
73+
ccRegisterBlockElements,
6374
collectContentPre,
6475
collectContentPost,
6576
};
@@ -122,6 +133,16 @@ const createTagAttribute = (config) => {
122133

123134
const aceRegisterBlockElements = () => tags;
124135

136+
// Server-side counterpart of aceRegisterBlockElements. Etherpad core's
137+
// import-side content collector keeps its own `_blockElems` set
138+
// separate from the editor's, so plugins that want their custom block
139+
// tags treated as block elements on import (so adjacent ones don't
140+
// merge into a single pad line) need to register them on the server
141+
// side too. A plugin that uses `lineAttribute({tags: [...]})` should
142+
// re-export this hook from its `ep.json` under
143+
// `"ccRegisterBlockElements"`.
144+
const ccRegisterBlockElements = () => tags;
145+
125146
const collectContentPre = (hookName, context, cb) => {
126147
const tname = context.tname;
127148
if (tags.includes(tname)) {
@@ -138,6 +159,7 @@ const createTagAttribute = (config) => {
138159
aceAttribClasses,
139160
aceAttribsToClasses,
140161
aceRegisterBlockElements,
162+
ccRegisterBlockElements,
141163
collectContentPre,
142164
collectContentPost,
143165
};

test/attributes.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ describe('lineAttribute', () => {
5656
});
5757
});
5858

59+
describe('ccRegisterBlockElements', () => {
60+
it('returns the tags array (server-side counterpart)', () => {
61+
assert.deepStrictEqual(headings.ccRegisterBlockElements(), tags);
62+
});
63+
64+
it('is exposed alongside aceRegisterBlockElements', () => {
65+
assert.strictEqual(typeof headings.ccRegisterBlockElements, 'function');
66+
assert.strictEqual(typeof headings.aceRegisterBlockElements, 'function');
67+
});
68+
});
69+
5970
describe('collectContentPre', () => {
6071
it('sets line attribute for matching tag', () => {
6172
const state = {lineAttributes: {}};
@@ -198,6 +209,12 @@ describe('tagAttribute', () => {
198209
});
199210
});
200211

212+
describe('ccRegisterBlockElements', () => {
213+
it('returns the tags array (server-side)', () => {
214+
assert.deepStrictEqual(subSup.ccRegisterBlockElements(), ['sub', 'sup']);
215+
});
216+
});
217+
201218
describe('collectContentPre', () => {
202219
it('calls doAttrib for matching tag', () => {
203220
let called = null;

0 commit comments

Comments
 (0)