/** * Module dependencies. */ var extend = require('extend'); var encode = require('ent/encode'); var CustomEvent = require('custom-event'); var voidElements = require('void-elements'); /** * Module exports. */ exports = module.exports = serialize; exports.serializeElement = serializeElement; exports.serializeAttribute = serializeAttribute; exports.serializeText = serializeText; exports.serializeComment = serializeComment; exports.serializeDocument = serializeDocument; exports.serializeDoctype = serializeDoctype; exports.serializeDocumentFragment = serializeDocumentFragment; exports.serializeNodeList = serializeNodeList; /** * Serializes any DOM node. Returns a string. * * @param {Node} node - DOM Node to serialize * @param {String} [context] - optional arbitrary "context" string to use (useful for event listeners) * @param {Function} [fn] - optional callback function to use in the "serialize" event for this call * @param {EventTarget} [eventTarget] - optional EventTarget instance to emit the "serialize" event on (defaults to `node`) * return {String} * @public */ function serialize (node, context, fn, eventTarget) { if (!node) return ''; if ('function' === typeof context) { fn = context; context = null; } if (!context) context = null; var rtn; var nodeType = node.nodeType; if (!nodeType && 'number' === typeof node.length) { // assume it's a NodeList or Array of Nodes rtn = exports.serializeNodeList(node, context, fn); } else { if ('function' === typeof fn) { // one-time "serialize" event listener node.addEventListener('serialize', fn, false); } // emit a custom "serialize" event on `node`, in case there // are event listeners for custom serialization of this node var e = new CustomEvent('serialize', { bubbles: true, cancelable: true, detail: { serialize: null, context: context } }); e.serializeTarget = node; var target = eventTarget || node; var cancelled = !target.dispatchEvent(e); // `e.detail.serialize` can be set to a: // String - returned directly // Node - goes through serializer logic instead of `node` // Anything else - get Stringified first, and then returned directly var s = e.detail.serialize; if (s != null) { if ('string' === typeof s) { rtn = s; } else if ('number' === typeof s.nodeType) { // make it go through the serialization logic rtn = serialize(s, context, null, target); } else { rtn = String(s); } } else if (!cancelled) { // default serialization logic switch (nodeType) { case 1 /* element */: rtn = exports.serializeElement(node, context, eventTarget); break; case 2 /* attribute */: rtn = exports.serializeAttribute(node); break; case 3 /* text */: rtn = exports.serializeText(node); break; case 8 /* comment */: rtn = exports.serializeComment(node); break; case 9 /* document */: rtn = exports.serializeDocument(node, context, eventTarget); break; case 10 /* doctype */: rtn = exports.serializeDoctype(node); break; case 11 /* document fragment */: rtn = exports.serializeDocumentFragment(node, context, eventTarget); break; } } if ('function' === typeof fn) { node.removeEventListener('serialize', fn, false); } } return rtn || ''; } /** * Serialize an Attribute node. */ function serializeAttribute (node, opts) { return node.name + '="' + encode(node.value, extend({ named: true }, opts)) + '"'; } /** * Serialize a DOM element. */ function serializeElement (node, context, eventTarget) { var c, i, l; var name = node.nodeName.toLowerCase(); // opening tag var r = '<' + name; // attributes for (i = 0, c = node.attributes, l = c.length; i < l; i++) { r += ' ' + exports.serializeAttribute(c[i]); } r += '>'; // child nodes r += exports.serializeNodeList(node.childNodes, context, null, eventTarget); // closing tag, only for non-void elements if (!voidElements[name]) { r += ''; } return r; } /** * Serialize a text node. */ function serializeText (node, opts) { return encode(node.nodeValue, extend({ named: true, special: { '<': true, '>': true, '&': true } }, opts)); } /** * Serialize a comment node. */ function serializeComment (node) { return ''; } /** * Serialize a Document node. */ function serializeDocument (node, context, eventTarget) { return exports.serializeNodeList(node.childNodes, context, null, eventTarget); } /** * Serialize a DOCTYPE node. * See: http://stackoverflow.com/a/10162353 */ function serializeDoctype (node) { var r = ''; return r; } /** * Serialize a DocumentFragment instance. */ function serializeDocumentFragment (node, context, eventTarget) { return exports.serializeNodeList(node.childNodes, context, null, eventTarget); } /** * Serialize a NodeList/Array of nodes. */ function serializeNodeList (list, context, fn, eventTarget) { var r = ''; for (var i = 0, l = list.length; i < l; i++) { r += serialize(list[i], context, fn, eventTarget); } return r; }