Skip to content

Instantly share code, notes, and snippets.

@corbanbrook
Last active March 28, 2016 18:03
Show Gist options
  • Save corbanbrook/03b641420c786251be94 to your computer and use it in GitHub Desktop.
Save corbanbrook/03b641420c786251be94 to your computer and use it in GitHub Desktop.
convert raw content state into html
/**
*
* Draft-js: convertToHtml
* author: [email protected]
*
*/
var blockNodes = {
'header-one': { type: 'h1' },
'header-two': { type: 'h2' },
'blockquote': { type: 'blockquote' },
'unstyled': { type: 'p' },
'media': { type: 'div', attrs: {class: 'media'} },
'unordered-list-item': { type: 'li' },
'ordered-list-item': { type: 'li' }
};
var styleNodes = {
'UNSTYLED': { type: null },
'BOLD': { type: 'b' },
'ITALIC': { type: 'i' },
'UNDERLINE': { type: 'u' },
'CODE': { type: 'span', attrs: {class: 'code', style: 'font-family: monospace;'} }
};
var entityNodes = {
'LINK': { type: 'a', attrs: {href: data => data.url} }
};
var mediaTemplates = {
photo: (data) => {
return `<div style="background-image: url(${data.url}); background-size: cover; background-position: 50% 50%; height: 360px;" />`;
},
video: (data) => {
return (`
<div className="px-asset-oembed" />
${data.oembed.html}
<div className="px-asset-footer">
<h1>${data.title}</h1>
<div className="px-asset-byline">
<span className="px-asset-byline-provider">${data.source.network}</span>
<span className="px-asset-byline-author"> - ${data.author.name}</span>
</div>
</div>
`);
}
};
const buildMediaTemplate = (entity) => {
if (typeof mediaTemplates[data.type] === 'function') {
return mediaTemplates[data.type](data);
}
};
const wrap = (innerHtml, node, data) => {
if (node.type == null) return innerHtml;
var nodeAttrs = '';
if (node.attrs) {
nodeAttrs = ' ' + Object.keys(node.attrs).map((attr) => {
var value = typeof node.attrs[attr] == 'function' ? node.attrs[attr](data) : node.attrs[attr];
return `${attr}="${value}"`;
}).join(' ');
}
var nodeMarkup = `<${node.type}${nodeAttrs}>`;
return `${nodeMarkup}${innerHtml}</${node.type}>`;
};
class Fragment {
constructor(range, entity) {
this.parent = null;
this.children = [];
this.texts = [];
if (entity) {
this.type = 'entity';
this.entity = entity;
} else {
this.type = 'style';
this.style = range.style || 'UNSTYLED'; // default
}
this.startOffset = range.offset;
this.endOffset = range.offset + range.length;
this.length = range.length;
}
};
const getBlockMarkup = (block, entityMap) => {
var innerHtml;
if (block.type == 'media') {
let entity = entityMap[block.entityRanges[0].key];
innerHtml = buildMediaTemplate(entity);
} else {
// Create fragments from sorted ranges array
var ranges = [].concat(block.inlineStyleRanges, block.entityRanges).sort((a, b) => { return a.length - b.length; });
var fragments = [];
ranges.forEach((range) => {
fragments.push(new Fragment(range, entityMap[range.key]))
});
// Fragments array is sorted from smallest to largest.
// Push the root onto the very end.
var root = new Fragment({offset: 0, length: block.text.length});
fragments.push(root);
fragments.forEach((fragment) => {
fragments.some((other) => {
if (fragment === other || fragment === other.parent) { return; }
if (fragment.startOffset >= other.startOffset && fragment.endOffset <= other.endOffset) {
fragment.parent = other;
other.children.push(fragment);
return true;
}
});
});
function cutText(fragment) {
var currentOffset = fragment.startOffset;
if (fragment.children.length) {
fragment.children.sort((a, b) => { return a.startOffset - b.startOffset; });
fragment.children.forEach((child) => {
fragment.texts.push(block.text.substring(currentOffset, child.startOffset));
currentOffset = child.endOffset;
cutText(child);
});
if (currentOffset < fragment.endOffset) {
fragment.texts.push(block.text.substr(currentOffset, fragment.endOffset - currentOffset));
}
} else {
fragment.texts.push(block.text.substring(fragment.startOffset, fragment.endOffset));
}
}
cutText(root);
function render(fragment) {
var result = '';
fragment.texts.forEach((text, idx) => {
result += text;
let child = fragment.children[idx];
if (child) {
result += render(fragment.children[idx]);
}
});
if (fragment.type == 'entity') {
return wrap(result, entityNodes[fragment.entity.type], fragment.entity.data);
} else {
return wrap(result, styleNodes[fragment.style]);
}
}
innerHtml = render(root);
}
return wrap(innerHtml, blockNodes[block.type]);
}
export default (rawContentState) => {
const {blocks, entityMap} = rawContentState;
return blocks.map(block => getBlockMarkup(block, entityMap)).join('');
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment