/// /** * Module dependencies */ import dataset = require('dataset'); import query = require('component-query'); import matches = require('matches-selector'); import domSerialize = require('dom-serialize'); import DEBUG = require('debug'); /** * Local imports */ import Editor = require('../editor/index') var debug = DEBUG('editor:serializer'); class Serializer { private editor: Editor; // a selector to match temporary objects that should be // removed from the markup during serialization private temporary: string; public constructor(editor: Editor) { this.editor = editor; this.temporary = 'span.join-hint, span.gallery-tmp-placeholder'; } private spaces(s: string, replaceFirst: boolean, replaceLast: boolean): string { if (replaceFirst) { s = s.replace(/^ /g, ' '); } if (replaceLast) { s = s.replace(/ $/g, ' '); } s = s.replace(/ /g, '  '); return s; } /** * Serializes a DOM node. Emits a "node" event, and a node-specific event * (i.e. "text", "element") for plugins to hook in to. * * Plugins may specify a different node to apply the serialization rules to * by setting the `serialize` property on the `Event` object provided. * * Useful for plugins. * * @param {Node} node - DOM node (TextNode, HTMLElement, etc.) * @return {String} serialized string of `node` * @public */ public serialize(node: Node|NodeList|Array, context: string = 'post'): string { return domSerialize(node, context, (e) => { var target: Node = e.serializeTarget; if (target.nodeType === 1 /* element */) { // `data-serialize` is a raw string to use as the serialized content var data; if (data = dataset(target, 'serialize')) { debug('using `data-serialize` attribute for %o: %o', target, data); e.detail.serialize = data; } else if (matches(target, this.temporary)) { // don't render anything for "temporary" nodes // TODO: move this logic to whoever is responsible for these "temporary // elements" e.preventDefault(); } } else if (target.nodeType === 3 /* text node */) { // use our "text node" serializer logic e.detail.serialize = this.serializeTextNode(target); } else { debug('ignoring serialization of Node: %o', node); e.preventDefault(); } }); } public serializeTextNode(text: Text): string { var content: string = domSerialize.serializeText(text, { named: false }); content = this.spaces(content, !text.previousSibling, !text.nextSibling); return content; } /** * Processes the root node of the Editor instance's children, * producing the final HTML string to be saved. * * @return {String} serialized editor contents * @public */ public serializeRoot(): string { // // IMPORTANT: this function shouldn't modify the markup it's operating on // as it's going to potentially be called async via a timer for auto-save // var result = this.serialize(this.editor.el.childNodes); // remove BR nodes result = result.replace(/<\/?br\s?>/g, ''); return result; } } export = Serializer;