├── .gitignore ├── .travis.yml ├── History.md ├── LICENSE ├── Makefile ├── README.md ├── index.js ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | /npm-debug.log 2 | /node_modules 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | env: 5 | global: 6 | - secure: VEtxOvO70QprLOKcRNFaEGoWMFXzUJ6D6/lmS8Iw+wlSuXMZVYb/vxqM3Fgp6WR5NaFoJ9drgTmIIR1jqlzN6vDpVh1NKAxYm6YAajJn8hFxQlbbagoM7E3Zx1A+YKldk8UygD3Tldj231ZLc8We+ZBWdoVWM3Si/bLO8O6rBVU= 7 | - secure: ZBLLYhAqGdP6R5z4ZJb/9OOmqlRasMn2lynzYKn/VO6RNgG0gqDtWOh9M+9LaGNkRGrUOyjkhnh+6FI1vmBpGwmapCq5voagzc34+4vz/CWhHM3P0/axr9kSNtWZ5Oj57PiOuvbBoMUWZcDsv8vomhm/K/c6LIxlC3Z6sJ55l78= 8 | matrix: 9 | - BROWSER_NAME=chrome BROWSER_VERSION=latest 10 | - BROWSER_NAME=chrome BROWSER_VERSION=35 11 | - BROWSER_NAME=chrome BROWSER_VERSION=34 12 | - BROWSER_NAME=firefox BROWSER_VERSION=latest 13 | - BROWSER_NAME=firefox BROWSER_VERSION=30 14 | - BROWSER_NAME=firefox BROWSER_VERSION=29 15 | - BROWSER_NAME=opera BROWSER_VERSION=latest 16 | - BROWSER_NAME=opera BROWSER_VERSION=11 17 | - BROWSER_NAME=safari BROWSER_VERSION=latest 18 | - BROWSER_NAME=safari BROWSER_VERSION=7 19 | - BROWSER_NAME=safari BROWSER_VERSION=6 20 | - BROWSER_NAME=safari BROWSER_VERSION=5 21 | - BROWSER_NAME=ie BROWSER_VERSION=11 22 | - BROWSER_NAME=ie BROWSER_VERSION=10 23 | - BROWSER_NAME=ie BROWSER_VERSION=9 24 | - BROWSER_NAME=iphone BROWSER_VERSION=7.1 25 | - BROWSER_NAME=iphone BROWSER_VERSION=7.0 26 | - BROWSER_NAME=iphone BROWSER_VERSION=6.1 27 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.2.1 / 2015-11-05 3 | ================== 4 | 5 | * bump extend and void-elements (@shinnn, #1) 6 | 7 | 2.2.0 / 2015-02-13 8 | ================== 9 | 10 | * add support for passing `encode()` options in to `serializeAttribute()` and `serializeTextNode()` 11 | 12 | 2.1.0 / 2015-02-10 13 | ================== 14 | 15 | * if `e.detail.serialize` is set and the event is cancelled, still use the `e.detail.serialize` value 16 | * attempting to get 100% test code coverage 17 | * package: allow any "zuul" v1 18 | * test: add HTML5 Doctype test, for 100% test code coverage! 19 | * test: remove `console.log()` call 20 | 21 | 2.0.1 / 2015-02-03 22 | ================== 23 | 24 | * index: support Nodes with children for `e.detail.serialize` 25 | 26 | 2.0.0 / 2015-02-03 27 | ================== 28 | 29 | * README: update for `serializeTarget` 30 | * index: emit the "serialize" event on the node that we know is in the DOM 31 | 32 | 1.2.1 / 2015-02-03 33 | ================== 34 | 35 | * fix one-time callback functions on NodeLists / Arrays 36 | * README: fix weird spacing 37 | * README: add "inspect" example to readme 38 | 39 | 1.2.0 / 2015-02-02 40 | ================== 41 | 42 | * add support for one-time "serialize" callback functions 43 | * add support for a "context" argument 44 | * index: make `serializeDoctype()` more readable 45 | * README: fix typo in example output 46 | * README: better description 47 | 48 | 1.1.0 / 2015-01-16 49 | ================== 50 | 51 | * add support for Comment, Document, Doctype, DocumentFragment and NodeList types to be serialized 52 | * add .travis.yml file 53 | * add Makefile for zuul tests 54 | * add README.md file 55 | * index: run `e.detail.serialize` through all the serialize() logic 56 | * index: use += operator for String concatentation (faster) 57 | * index: use `require('ent/encode')` syntax 58 | * package: update "ent" to v2.2.0 59 | * package: rename to "dom-serialize" 60 | * test: add Array serialize test 61 | 62 | 1.0.0 / 2015-01-15 63 | ================== 64 | 65 | * index: add support for Nodes to be set on `e.data.serialize` 66 | * index: remove redundant `break` statements 67 | * test: add `e.detail.serialize` Node and Number tests 68 | * test: add "serialize" event tests 69 | * test: add initial test cases 70 | * package: add "string" as a keyword 71 | * package: add "zuul" as a dev dependency 72 | * package: use ~ for dep versions 73 | * initial commit 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright 2017 Nathan Rajlich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | # get Makefile directory name: http://stackoverflow.com/a/5982798/376773 3 | THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)) 4 | THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd) 5 | 6 | # BIN directory 7 | BIN := $(THIS_DIR)/node_modules/.bin 8 | 9 | # applications 10 | NODE ?= node 11 | ZUUL ?= $(NODE) $(BIN)/zuul 12 | 13 | test: 14 | @if [ "x$(BROWSER_PLATFORM)" = "x" ]; then \ 15 | $(ZUUL) \ 16 | --ui mocha-bdd \ 17 | --browser-name $(BROWSER_NAME) \ 18 | --browser-version $(BROWSER_VERSION) \ 19 | test/*.js; \ 20 | else \ 21 | $(ZUUL) \ 22 | --ui mocha-bdd \ 23 | --browser-name $(BROWSER_NAME) \ 24 | --browser-version $(BROWSER_VERSION) \ 25 | --browser-platform "$(BROWSER_PLATFORM)" \ 26 | test/*.js; \ 27 | fi 28 | 29 | .PHONY: test 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dom-serialize 2 | ============= 3 | ### Serializes any DOM node into a String 4 | 5 | [![Sauce Test Status](https://saucelabs.com/browser-matrix/dom-serialize.svg)](https://saucelabs.com/u/dom-serialize) 6 | 7 | [![Build Status](https://travis-ci.org/webmodules/dom-serialize.svg?branch=master)](https://travis-ci.org/webmodules/dom-serialize) 8 | 9 | It's like `outerHTML`, but it works with: 10 | 11 | * DOM elements 12 | * Text nodes 13 | * Attributes 14 | * Comment nodes 15 | * Documents 16 | * DocumentFragments 17 | * Doctypes 18 | * NodeLists / Arrays 19 | 20 | For custom serialization logic, a "serialize" event is dispatched on 21 | every `Node` which event listeners can override the default behavior on by 22 | setting the `event.detail.serialize` property to a String or other Node. 23 | 24 | The "serialize" event bubbles, so it could be a good idea to utilize 25 | event delegation on a known root node that will be serialized. 26 | Check the `event.serializeTarget` property to check which `Node` is 27 | currently being serialized. 28 | 29 | 30 | Installation 31 | ------------ 32 | 33 | ``` bash 34 | $ npm install dom-serialize 35 | ``` 36 | 37 | 38 | Example 39 | ------- 40 | 41 | ``` js 42 | var serialize = require('dom-serialize'); 43 | var node; 44 | 45 | // works with Text nodes 46 | node = document.createTextNode('foo & '); 47 | console.log(serialize(node)); 48 | 49 | 50 | // works with DOM elements 51 | node = document.createElement('body'); 52 | node.appendChild(document.createElement('strong')); 53 | node.firstChild.appendChild(document.createTextNode('hello')); 54 | console.log(serialize(node)); 55 | 56 | 57 | // custom "serialize" event 58 | node.firstChild.addEventListener('serialize', function (event) { 59 | event.detail.serialize = 'pwn'; 60 | }, false); 61 | console.log(serialize(node)); 62 | 63 | 64 | // you can also just pass a function in for a one-time serializer 65 | console.log(serialize(node, function (event) { 66 | if (event.serializeTarget === node.firstChild) { 67 | // for the first child, output an ellipsis to summarize "content" 68 | event.detail.serialze = '…'; 69 | } else if (event.serializeTarget !== node) { 70 | // any other child 71 | event.preventDefault(); 72 | } 73 | })); 74 | ``` 75 | 76 | ``` 77 | foo & <bar> 78 | hello 79 | pwn 80 | … 81 | ``` 82 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var extend = require('extend'); 7 | var encode = require('ent/encode'); 8 | var CustomEvent = require('custom-event'); 9 | var voidElements = require('void-elements'); 10 | 11 | /** 12 | * Module exports. 13 | */ 14 | 15 | exports = module.exports = serialize; 16 | exports.serializeElement = serializeElement; 17 | exports.serializeAttribute = serializeAttribute; 18 | exports.serializeText = serializeText; 19 | exports.serializeComment = serializeComment; 20 | exports.serializeDocument = serializeDocument; 21 | exports.serializeDoctype = serializeDoctype; 22 | exports.serializeDocumentFragment = serializeDocumentFragment; 23 | exports.serializeNodeList = serializeNodeList; 24 | 25 | /** 26 | * Serializes any DOM node. Returns a string. 27 | * 28 | * @param {Node} node - DOM Node to serialize 29 | * @param {String} [context] - optional arbitrary "context" string to use (useful for event listeners) 30 | * @param {Function} [fn] - optional callback function to use in the "serialize" event for this call 31 | * @param {EventTarget} [eventTarget] - optional EventTarget instance to emit the "serialize" event on (defaults to `node`) 32 | * return {String} 33 | * @public 34 | */ 35 | 36 | function serialize (node, context, fn, eventTarget) { 37 | if (!node) return ''; 38 | if ('function' === typeof context) { 39 | fn = context; 40 | context = null; 41 | } 42 | if (!context) context = null; 43 | 44 | var rtn; 45 | var nodeType = node.nodeType; 46 | 47 | if (!nodeType && 'number' === typeof node.length) { 48 | // assume it's a NodeList or Array of Nodes 49 | rtn = exports.serializeNodeList(node, context, fn); 50 | } else { 51 | 52 | if ('function' === typeof fn) { 53 | // one-time "serialize" event listener 54 | node.addEventListener('serialize', fn, false); 55 | } 56 | 57 | // emit a custom "serialize" event on `node`, in case there 58 | // are event listeners for custom serialization of this node 59 | var e = new CustomEvent('serialize', { 60 | bubbles: true, 61 | cancelable: true, 62 | detail: { 63 | serialize: null, 64 | context: context 65 | } 66 | }); 67 | 68 | e.serializeTarget = node; 69 | 70 | var target = eventTarget || node; 71 | var cancelled = !target.dispatchEvent(e); 72 | 73 | // `e.detail.serialize` can be set to a: 74 | // String - returned directly 75 | // Node - goes through serializer logic instead of `node` 76 | // Anything else - get Stringified first, and then returned directly 77 | var s = e.detail.serialize; 78 | if (s != null) { 79 | if ('string' === typeof s) { 80 | rtn = s; 81 | } else if ('number' === typeof s.nodeType) { 82 | // make it go through the serialization logic 83 | rtn = serialize(s, context, null, target); 84 | } else { 85 | rtn = String(s); 86 | } 87 | } else if (!cancelled) { 88 | // default serialization logic 89 | switch (nodeType) { 90 | case 1 /* element */: 91 | rtn = exports.serializeElement(node, context, eventTarget); 92 | break; 93 | case 2 /* attribute */: 94 | rtn = exports.serializeAttribute(node); 95 | break; 96 | case 3 /* text */: 97 | rtn = exports.serializeText(node); 98 | break; 99 | case 8 /* comment */: 100 | rtn = exports.serializeComment(node); 101 | break; 102 | case 9 /* document */: 103 | rtn = exports.serializeDocument(node, context, eventTarget); 104 | break; 105 | case 10 /* doctype */: 106 | rtn = exports.serializeDoctype(node); 107 | break; 108 | case 11 /* document fragment */: 109 | rtn = exports.serializeDocumentFragment(node, context, eventTarget); 110 | break; 111 | } 112 | } 113 | 114 | if ('function' === typeof fn) { 115 | node.removeEventListener('serialize', fn, false); 116 | } 117 | } 118 | 119 | return rtn || ''; 120 | } 121 | 122 | /** 123 | * Serialize an Attribute node. 124 | */ 125 | 126 | function serializeAttribute (node, opts) { 127 | return node.name + '="' + encode(node.value, extend({ 128 | named: true 129 | }, opts)) + '"'; 130 | } 131 | 132 | /** 133 | * Serialize a DOM element. 134 | */ 135 | 136 | function serializeElement (node, context, eventTarget) { 137 | var c, i, l; 138 | var name = node.nodeName.toLowerCase(); 139 | 140 | // opening tag 141 | var r = '<' + name; 142 | 143 | // attributes 144 | for (i = 0, c = node.attributes, l = c.length; i < l; i++) { 145 | r += ' ' + exports.serializeAttribute(c[i]); 146 | } 147 | 148 | r += '>'; 149 | 150 | // child nodes 151 | r += exports.serializeNodeList(node.childNodes, context, null, eventTarget); 152 | 153 | // closing tag, only for non-void elements 154 | if (!voidElements[name]) { 155 | r += ''; 156 | } 157 | 158 | return r; 159 | } 160 | 161 | /** 162 | * Serialize a text node. 163 | */ 164 | 165 | function serializeText (node, opts) { 166 | return encode(node.nodeValue, extend({ 167 | named: true, 168 | special: { '<': true, '>': true, '&': true } 169 | }, opts)); 170 | } 171 | 172 | /** 173 | * Serialize a comment node. 174 | */ 175 | 176 | function serializeComment (node) { 177 | return ''; 178 | } 179 | 180 | /** 181 | * Serialize a Document node. 182 | */ 183 | 184 | function serializeDocument (node, context, eventTarget) { 185 | return exports.serializeNodeList(node.childNodes, context, null, eventTarget); 186 | } 187 | 188 | /** 189 | * Serialize a DOCTYPE node. 190 | * See: http://stackoverflow.com/a/10162353 191 | */ 192 | 193 | function serializeDoctype (node) { 194 | var r = ''; 209 | return r; 210 | } 211 | 212 | /** 213 | * Serialize a DocumentFragment instance. 214 | */ 215 | 216 | function serializeDocumentFragment (node, context, eventTarget) { 217 | return exports.serializeNodeList(node.childNodes, context, null, eventTarget); 218 | } 219 | 220 | /** 221 | * Serialize a NodeList/Array of nodes. 222 | */ 223 | 224 | function serializeNodeList (list, context, fn, eventTarget) { 225 | var r = ''; 226 | for (var i = 0, l = list.length; i < l; i++) { 227 | r += serialize(list[i], context, fn, eventTarget); 228 | } 229 | return r; 230 | } 231 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dom-serialize", 3 | "version": "2.2.1", 4 | "description": "Serializes any DOM node into a String", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/webmodules/dom-serialize.git" 12 | }, 13 | "keywords": [ 14 | "browser", 15 | "node", 16 | "dom", 17 | "serialize", 18 | "string" 19 | ], 20 | "author": "Nathan Rajlich (http://n8.io/)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/webmodules/dom-serialize/issues" 24 | }, 25 | "homepage": "https://github.com/webmodules/dom-serialize", 26 | "dependencies": { 27 | "custom-event": "~1.0.0", 28 | "ent": "~2.2.0", 29 | "extend": "^3.0.0", 30 | "void-elements": "^2.0.0" 31 | }, 32 | "devDependencies": { 33 | "zuul": "1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert'); 3 | var serialize = require('../'); 4 | 5 | describe('node-serialize', function () { 6 | var node; 7 | 8 | afterEach(function () { 9 | if (node) { 10 | // clean up... 11 | if (node.parentNode) { 12 | node.parentNode.removeChild(node); 13 | } 14 | node = null; 15 | } 16 | }); 17 | 18 | it('should return an empty string on invalid input', function () { 19 | assert.strictEqual('', serialize(null)); 20 | }); 21 | 22 | it('should serialize a SPAN element', function () { 23 | node = document.createElement('span'); 24 | assert.equal('', serialize(node)); 25 | }); 26 | 27 | it('should serialize a BR element', function () { 28 | node = document.createElement('br'); 29 | assert.equal('
', serialize(node)); 30 | }); 31 | 32 | it('should serialize a text node', function () { 33 | node = document.createTextNode('test'); 34 | assert.equal('test', serialize(node)); 35 | }); 36 | 37 | it('should serialize a text node with special HTML characters', function () { 38 | node = document.createTextNode('<>\'"&'); 39 | assert.equal('<>\'"&', serialize(node)); 40 | }); 41 | 42 | it('should serialize a DIV element with child nodes', function () { 43 | node = document.createElement('div'); 44 | node.appendChild(document.createTextNode('hello ')); 45 | var b = document.createElement('b'); 46 | b.appendChild(document.createTextNode('world')); 47 | node.appendChild(b); 48 | node.appendChild(document.createTextNode('!')); 49 | node.appendChild(document.createElement('br')); 50 | assert.equal('
hello world!
', serialize(node)); 51 | }); 52 | 53 | it('should serialize a DIV element with attributes', function () { 54 | node = document.createElement('div'); 55 | node.setAttribute('foo', 'bar'); 56 | node.setAttribute('escape', '<>&"\''); 57 | assert.equal('
', serialize(node)); 58 | }); 59 | 60 | it('should serialize an Attribute node', function () { 61 | var div = document.createElement('div'); 62 | div.setAttribute('foo', 'bar'); 63 | node = div.attributes[0]; 64 | assert.equal('foo="bar"', serialize(node)); 65 | }); 66 | 67 | it('should serialize a Comment node', function () { 68 | node = document.createComment('test'); 69 | assert.equal('', serialize(node)); 70 | }); 71 | 72 | it('should serialize a Document node', function () { 73 | node = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null); 74 | assert.equal('', serialize(node)); 75 | }); 76 | 77 | it('should serialize a Doctype node', function () { 78 | node = document.implementation.createDocumentType( 79 | 'html', 80 | '-//W3C//DTD XHTML 1.0 Strict//EN', 81 | 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' 82 | ); 83 | 84 | // Some older browsers require the DOCTYPE to be within a Document, 85 | // otherwise the "serialize" custom event is considered "cancelled". 86 | // See: https://travis-ci.org/webmodules/dom-serialize/builds/47307749 87 | var doc = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', node); 88 | 89 | assert.equal('', serialize(node)); 90 | assert.equal('', serialize(doc)); 91 | }); 92 | 93 | it('should serialize a Doctype node with systemId', function () { 94 | node = document.implementation.createDocumentType( 95 | 'root-element', 96 | '', 97 | 'http://www.w3.org/1999/xhtml' 98 | ); 99 | document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'root-element', node); 100 | 101 | assert.equal('', serialize(node)); 102 | }); 103 | 104 | 105 | it('should serialize an HTML5 Doctype node', function () { 106 | node = document.implementation.createDocumentType( 107 | 'html', 108 | '', 109 | '' 110 | ); 111 | document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'node', node); 112 | 113 | assert.equal('', serialize(node)); 114 | }); 115 | 116 | it('should serialize a DocumentFragment node', function () { 117 | node = document.createDocumentFragment(); 118 | node.appendChild(document.createElement('b')); 119 | node.appendChild(document.createElement('i')); 120 | node.lastChild.appendChild(document.createTextNode('foo')); 121 | assert.equal('foo', serialize(node)); 122 | }); 123 | 124 | it('should serialize an Array of nodes', function () { 125 | var array = []; 126 | array.push(document.createTextNode('foo')); 127 | array.push(document.createElement('div')); 128 | array[1].appendChild(document.createTextNode('bar')); 129 | assert.equal('foo
bar
', serialize(array)); 130 | }); 131 | 132 | describe('serializeText()', function () { 133 | 134 | it('should serialize an Attribute node', function () { 135 | var d = document.createElement('div'); 136 | d.setAttribute('foo', '<>"&'); 137 | assert.equal('foo="<>"&"', serialize.serializeAttribute(d.attributes[0])); 138 | }); 139 | 140 | it('should allow an "options" object to be passed in', function () { 141 | var d = document.createElement('div'); 142 | d.setAttribute('foo', '<>"&'); 143 | assert.equal('foo="<>"&"', serialize.serializeAttribute(d.attributes[0], { named: false })); 144 | }); 145 | 146 | }); 147 | 148 | describe('serializeText()', function () { 149 | 150 | it('should serialize a TextNode instance', function () { 151 | node = document.createTextNode('&'); 152 | assert.equal('<b>&', serialize.serializeText(node)); 153 | }); 154 | 155 | it('should allow an "options" object to be passed in', function () { 156 | node = document.createTextNode('&'); 157 | assert.equal('<b>&', serialize.serializeText(node, { named: false })); 158 | }); 159 | 160 | }); 161 | 162 | describe('"serialize" event', function () { 163 | 164 | it('should emit a "serialize" event on a DIV node', function () { 165 | node = document.createElement('div'); 166 | var count = 0; 167 | node.addEventListener('serialize', function (e) { 168 | count++; 169 | e.detail.serialize = 'MEOW'; 170 | }); 171 | assert.equal(0, count); 172 | assert.equal('MEOW', serialize(node)); 173 | assert.equal(1, count); 174 | }); 175 | 176 | it('should emit a "serialize" event on a Text node', function () { 177 | node = document.createTextNode('whaaaaa!!!!!!'); 178 | var count = 0; 179 | node.addEventListener('serialize', function (e) { 180 | count++; 181 | e.detail.serialize = 'MEOW'; 182 | }); 183 | assert.equal(0, count); 184 | assert.equal('MEOW', serialize(node)); 185 | assert.equal(1, count); 186 | }); 187 | 188 | it('should output an empty string when event is cancelled', function () { 189 | node = document.createElement('div'); 190 | node.appendChild(document.createTextNode('!')); 191 | var count = 0; 192 | node.firstChild.addEventListener('serialize', function (e) { 193 | count++; 194 | e.preventDefault(); 195 | }); 196 | assert.equal(0, count); 197 | assert.equal('
', serialize(node)); 198 | assert.equal(1, count); 199 | }); 200 | 201 | it('should render a Number when set as `e.detail.serialize`', function () { 202 | node = document.createTextNode('whaaaaa!!!!!!'); 203 | var count = 0; 204 | node.addEventListener('serialize', function (e) { 205 | count++; 206 | e.detail.serialize = 123; 207 | }); 208 | assert.equal(0, count); 209 | assert.equal('123', serialize(node)); 210 | assert.equal(1, count); 211 | }); 212 | 213 | it('should render a Node when set as `e.detail.serialize`', function () { 214 | node = document.createTextNode('whaaaaa!!!!!!'); 215 | var count = 0; 216 | node.addEventListener('serialize', function (e) { 217 | count++; 218 | if (count === 1) { 219 | e.detail.serialize = document.createTextNode('foo'); 220 | } 221 | }); 222 | assert.equal(0, count); 223 | assert.equal('foo', serialize(node)); 224 | assert.equal(2, count); 225 | }); 226 | 227 | it('should render a Node when set as `e.detail.serialize` and event is cancelled', function () { 228 | node = document.createTextNode('whaaaaa!!!!!!'); 229 | var count = 0; 230 | node.addEventListener('serialize', function (e) { 231 | count++; 232 | if (count === 1) { 233 | e.preventDefault(); 234 | e.detail.serialize = document.createTextNode('foo'); 235 | } 236 | }); 237 | assert.equal(0, count); 238 | assert.equal('foo', serialize(node)); 239 | assert.equal(2, count); 240 | }); 241 | 242 | it('should have `context` set on the event', function () { 243 | node = document.createTextNode(''); 244 | var count = 0; 245 | node.addEventListener('serialize', function (e) { 246 | count++; 247 | e.detail.serialize = e.detail.context; 248 | }); 249 | assert.equal(0, count); 250 | assert.equal('foo', serialize(node, 'foo')); 251 | assert.equal(1, count); 252 | assert.equal('bar', serialize(node, 'bar')); 253 | assert.equal(2, count); 254 | assert.equal('baz', serialize(node, 'baz')); 255 | assert.equal(3, count); 256 | }); 257 | 258 | it('should bubble', function () { 259 | node = document.createElement('div'); 260 | node.appendChild(document.createTextNode('foo')); 261 | node.appendChild(document.createTextNode(' ')); 262 | node.appendChild(document.createTextNode('bar')); 263 | 264 | // `node` must be inside the DOM for the "serialize" event to bubble 265 | document.body.appendChild(node); 266 | 267 | var count = 0; 268 | node.addEventListener('serialize', function (e) { 269 | count++; 270 | assert.equal('foo', e.detail.context); 271 | if (e.serializeTarget === node) 272 | return; 273 | else if (e.serializeTarget.nodeValue === 'foo') 274 | e.detail.serialize = '…'; 275 | else 276 | e.preventDefault(); 277 | }, false); 278 | 279 | assert.equal(0, count); 280 | assert.equal('
', serialize(node, 'foo')); 281 | assert.equal(4, count); 282 | }); 283 | 284 | it('should support one-time callback function on Elements', function () { 285 | node = document.createElement('div'); 286 | var count = 0; 287 | 288 | function callback (e) { 289 | count++; 290 | e.detail.serialize = count; 291 | } 292 | 293 | assert.equal(0, count); 294 | assert.equal('1', serialize(node, callback)); 295 | assert.equal(1, count); 296 | assert.equal('
', serialize(node)); 297 | assert.equal(1, count); 298 | }); 299 | 300 | it('should support one-time callback function on NodeLists', function () { 301 | node = document.createElement('div'); 302 | node.appendChild(document.createElement('strong')); 303 | node.appendChild(document.createTextNode('foo')); 304 | node.appendChild(document.createElement('em')); 305 | node.appendChild(document.createTextNode('bar')); 306 | 307 | var count = 0; 308 | 309 | function callback (e) { 310 | count++; 311 | e.detail.serialize = count; 312 | } 313 | 314 | assert.equal(0, count); 315 | assert.equal('1234', serialize(node.childNodes, callback)); 316 | assert.equal(4, count); 317 | assert.equal('foobar', serialize(node.childNodes)); 318 | assert.equal(4, count); 319 | }); 320 | 321 | it('should support one-time callback function on Nodes set in `e.detail.serialize`', function () { 322 | node = document.createElement('div'); 323 | node.appendChild(document.createTextNode('foo')); 324 | 325 | // `node` must be inside the DOM for the "serialize" event to bubble 326 | document.body.appendChild(node); 327 | 328 | var count = 0; 329 | 330 | function callback (e) { 331 | count++; 332 | 333 | if (2 === count) { 334 | assert.equal('foo', e.serializeTarget.nodeValue); 335 | var text = document.createTextNode('bar'); 336 | e.detail.serialize = text; 337 | } else if (3 === count) { 338 | assert.equal('bar', e.serializeTarget.nodeValue); 339 | var text = document.createTextNode('baz'); 340 | e.detail.serialize = text; 341 | } 342 | } 343 | 344 | assert.equal(0, count); 345 | assert.equal('
baz
', serialize(node, callback)); 346 | assert.equal(4, count); 347 | }); 348 | 349 | it('should support one-time callback function on complex Nodes set in `e.detail.serialize`', function () { 350 | node = document.createElement('div'); 351 | node.appendChild(document.createTextNode('foo')); 352 | 353 | // `node` must be inside the DOM for the "serialize" event to bubble 354 | document.body.appendChild(node); 355 | 356 | var count = 0; 357 | 358 | function callback (e) { 359 | count++; 360 | if (e.serializeTarget.nodeValue === 'foo') { 361 | var el = document.createElement('p'); 362 | el.appendChild(document.createTextNode('x ')); 363 | el.appendChild(document.createElement('i')); 364 | el.lastChild.appendChild(document.createTextNode('bar')); 365 | 366 | e.detail.serialize = el; 367 | } 368 | } 369 | 370 | assert.equal(0, count); 371 | assert.equal('

x bar

', serialize(node, callback)); 372 | assert.equal(6, count); 373 | }); 374 | 375 | }); 376 | 377 | }); 378 | --------------------------------------------------------------------------------