mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-07 03:22:21 +03:00
f57268daae
refs https://github.com/TryGhost/Ghost/issues/9742 We've identified some changes we need to make to the HTML output of the [new Koenig editor]( https://forum.ghost.org/t/koenig-editor-beta-release/1284/102) for future proofing and consistency across cards. - the `<div class="kg-post">` wrapper around post content has been removed - for image cards the `.kg-image-wide` and `.kg-image-full` classes have been changed to `.kg-width-wide` and `.kg-width-full` and applied to the `<figure>` element rather than the `<img>` element Before: ```html <div class="kg-post"> <figure class="kg-image-card"> <img class="kg-image kg-image-wide" src="..."> <figcaption>example wide image</figcaption> </figure> </div> ``` After: ```html <figure class="kg-image-card kg-width-wide"> <img class="kg-image" src="..."> <figcaption>example wide image</figcaption> </figure> ```
124 lines
3.5 KiB
JavaScript
124 lines
3.5 KiB
JavaScript
const SimpleDom = require('simple-dom');
|
|
const Renderer = require('mobiledoc-dom-renderer').default;
|
|
const common = require('../../common');
|
|
const atoms = require('../atoms');
|
|
const cards = require('../cards');
|
|
const options = {
|
|
dom: new SimpleDom.Document(),
|
|
cards: cards,
|
|
atoms: atoms,
|
|
unknownCardHandler: function (args) {
|
|
common.logging.error(new common.errors.InternalServerError({
|
|
message: 'Mobiledoc card \'' + args.env.name + '\' not found.'
|
|
}));
|
|
}
|
|
};
|
|
|
|
// used to walk the rendered SimpleDOM output and modify elements before
|
|
// serializing to HTML. Saves having a large HTML parsing dependency such as
|
|
// jsdom that may break on malformed HTML in MD or HTML cards
|
|
class DomModifier {
|
|
constructor() {
|
|
this.usedIds = [];
|
|
}
|
|
|
|
addHeadingId(node) {
|
|
if (!node.firstChild || node.getAttribute('id')) {
|
|
return;
|
|
}
|
|
|
|
let text = this.getTextValue(node);
|
|
let id = text
|
|
.replace(/[<>&"?]/g, '')
|
|
.trim()
|
|
.replace(/[^\w]/g, '-')
|
|
.replace(/-{2,}/g, '-')
|
|
.toLowerCase();
|
|
|
|
if (this.usedIds[id] !== undefined) {
|
|
this.usedIds[id] += 1;
|
|
id += `-${this.usedIds[id]}`;
|
|
} else {
|
|
this.usedIds[id] = 0;
|
|
}
|
|
|
|
node.setAttribute('id', id);
|
|
}
|
|
|
|
// extract to util?
|
|
getTextValue(node) {
|
|
let buffer = '';
|
|
let next = node.firstChild;
|
|
while (next !== null) {
|
|
buffer += this._extractTextValue(next);
|
|
next = next.nextSibling;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
_extractTextValue(node) {
|
|
let buffer = '';
|
|
|
|
if (node.nodeType === 3) {
|
|
buffer += node.nodeValue;
|
|
}
|
|
|
|
buffer += this.getTextValue(node);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
modifyChildren(node) {
|
|
let next = node.firstChild;
|
|
while (next !== null) {
|
|
this.modify(next);
|
|
next = next.nextSibling;
|
|
}
|
|
}
|
|
|
|
modify(node) {
|
|
// add id attributes to H* tags
|
|
if (node.nodeType === 1 && node.nodeName.match(/^h\d$/i)) {
|
|
this.addHeadingId(node);
|
|
}
|
|
|
|
this.modifyChildren(node);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
// version 1 === Ghost 1.0 markdown-only mobiledoc
|
|
// version 2 === Ghost 2.0 full mobiledoc
|
|
render(mobiledoc, version) {
|
|
version = version || 1;
|
|
|
|
// pass the version through to the card renderers.
|
|
// create a new object here to avoid modifying the default options
|
|
// object because the version can change per-render until 2.0 is released
|
|
let versionedOptions = Object.assign({}, options, {
|
|
cardOptions: {version}
|
|
});
|
|
|
|
let renderer = new Renderer(versionedOptions);
|
|
let rendered = renderer.render(mobiledoc);
|
|
let serializer = new SimpleDom.HTMLSerializer(SimpleDom.voidMap);
|
|
|
|
// Koenig keeps a blank paragraph at the end of a doc but we want to
|
|
// make sure it doesn't get rendered
|
|
let lastChild = rendered.result.lastChild;
|
|
if (lastChild && lastChild.tagName === 'P' && !lastChild.firstChild) {
|
|
rendered.result.removeChild(lastChild);
|
|
}
|
|
|
|
// Walk the DOM output and modify nodes as needed
|
|
// eg. to add ID attributes to heading elements
|
|
let modifier = new DomModifier();
|
|
modifier.modifyChildren(rendered.result);
|
|
|
|
let html = serializer.serializeChildren(rendered.result);
|
|
|
|
return html;
|
|
}
|
|
};
|