123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- /**
- * 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 += '</' + name + '>';
- }
- 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 '<!--' + node.nodeValue + '-->';
- }
- /**
- * 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 = '<!DOCTYPE ' + node.name;
- if (node.publicId) {
- r += ' PUBLIC "' + node.publicId + '"';
- }
- if (!node.publicId && node.systemId) {
- r += ' SYSTEM';
- }
- if (node.systemId) {
- r += ' "' + node.systemId + '"';
- }
- 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;
- }
|