@atlaskit/editor-core
Version:
A package contains Atlassian editor core functionality
65 lines (55 loc) • 2.63 kB
text/typescript
export const AC_XMLNS = 'http://example.com/ac';
export const RI_XMLNS = 'http://example.com/ri';
export const FAB_XMLNS = 'http://example.com/fab';
export const XHTML_XMLNS = 'http://www.w3.org/1999/xhtml';
export default function(node: Node): string {
// The naive approach here is to simply call .outerHTML on a node, however this has an undesirable
// behaviour of including the xmlns:* attributes on the node. Confluence Storage Format expects that
// these are *not* included.
//
// To avoid including these, we fall back to hacky plain text slicing. The strategy is to included
// some wrapper elements that will contain the xmlns:* attributes. A document will be structured as:
//
// <wrapper xmlns="http://www.w3.org/1999/xhtml">
// <fab:wrapper xmlns="http://example.com/fab">
// <ac:wrapper xmlns="http://example.com/ac">
// <ri:wrapper xmlns="http://example.com/ri">
// …
// </ri:wrapper>
// </ac:wrapper>
// </fab:wrapper>
// </wrapper>
//
// Before content is added, `.outerHTML` of the `ac:wrapper` is used to determine the number of
// leading/trailing bytes need to be trimmed in the final result (to remove the wrappers).
//
// WARNING: This method will move `element` into a new parent element, but will put it back
// before returning.
const doc = node.ownerDocument;
const marker = doc.createElement('marker');
if (node.parentNode) {
node.parentNode.replaceChild(marker, node);
}
const wrapper = doc.createElementNS(XHTML_XMLNS, 'wrapper');
const acWrapper = doc.createElementNS(AC_XMLNS, 'ac:wrapper');
const riWrapper = doc.createElementNS(RI_XMLNS, 'ri:wrapper');
const fabWrapper = doc.createElementNS(FAB_XMLNS, 'fab:wrapper');
wrapper.appendChild(fabWrapper);
fabWrapper.appendChild(acWrapper);
acWrapper.appendChild(riWrapper);
// Force avoid self-closing tags, as these would invalidate suffix/prefix length calculations.
const wedge = '|';
const wedgeText = doc.createTextNode(wedge);
riWrapper.appendChild(wedgeText);
const template = wrapper.outerHTML;
const prefixLength = template.indexOf(wedge);
const suffixLength = template.length - prefixLength - wedge.length;
riWrapper.removeChild(wedgeText);
riWrapper.appendChild(node);
const wrappedResult = wrapper.outerHTML;
const result = wrappedResult.slice(prefixLength, wrappedResult.length - suffixLength);
if (marker.parentNode) {
marker.parentNode.replaceChild(node, marker);
}
return result.replace(/\s\/>/g, '/>'); // Remove unnecessary white-space added by IE
}