├── .gitignore
├── README.md
├── index.js
├── light.js
├── package.json
├── test
├── dist
│ ├── test-light.js
│ └── test.js
├── test-light.js
├── test.html
└── test.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .c9/
3 | *.log
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # html2idom
2 |
3 | Convert a string of HTML into an [Incremental DOM](https://github.com/google/incremental-dom) render.
4 |
5 | HTML parsing done by [htmlparser2](https://github.com/fb55/htmlparser2), which is a very fast and accurate HTML parser. However, it comes at a cost up front as it adds around 40kb (min and gzipped) to your code.
6 |
7 | If that dependency is too large,then there is a light version (`require("html2idom/light")`) using [html-parse-stringy](https://github.com/HenrikJoreteg/html-parse-stringify), which is much smaller in size, but slower when parsing HTML. If you are only dealing with small sets of HTML, then this option might be better.
8 |
9 |
10 | ## Installation
11 |
12 | ```
13 | npm install html2idom
14 | ```
15 |
16 | ## Usage
17 |
18 | ```
19 | var patchHTML = require("html2idom").patchHTML;
20 |
21 | // get you view's el
22 | var el = document.getElementById("view");
23 | var html = "
Hello, World
";
24 |
25 | // apply the HTML to the el via incremental dom
26 | patchHTML(el, HTML);
27 | ```
28 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var Parser = require("htmlparser2").Parser;
2 | var IncrementalDOM = require("incremental-dom");
3 |
4 | var elementOpen = IncrementalDOM.elementOpen;
5 | var elementClose = IncrementalDOM.elementClose;
6 | var text = IncrementalDOM.text;
7 | var patch = IncrementalDOM.patch;
8 |
9 | /** Parser with callbacks that invoke Incremental DOM */
10 | var parser = new Parser({
11 | onopentag: function (name, attrs) {
12 | var argsArray = [name, null, null];
13 |
14 | // convert attribs object into a flat array
15 | for (var attr in attrs) {
16 | argsArray.push(attr, attrs[attr]);
17 | }
18 |
19 | elementOpen.apply(null, argsArray);
20 | },
21 | ontext: text,
22 | onclosetag: elementClose
23 | }, {decodeEntities: true});
24 |
25 | /**
26 | * build IDOM for ast node
27 | * @private
28 | * @param {Object} node - An AST node to render
29 | */
30 | function renderToIDom(html) {
31 | parser.write(html);
32 | parser.end();
33 | };
34 |
35 | /**
36 | * apply the HTML to an element via Incremental DOM's `patch`
37 | * @param {Element} el - The element to apply the patch to
38 | * @param {String} html - A string of HTML
39 | */
40 | function patchHTML(el, html) {
41 | patch(el, function() {
42 | return renderToIDom(html);
43 | });
44 | }
45 |
46 | module.exports = {
47 | renderToIDom: renderToIDom,
48 | patchHTML: patchHTML
49 | }
--------------------------------------------------------------------------------
/light.js:
--------------------------------------------------------------------------------
1 | var parse = require("html-parse-stringify").parse;
2 | var IncrementalDOM = require("incremental-dom");
3 |
4 | var elementOpen = IncrementalDOM.elementOpen;
5 | var elementClose = IncrementalDOM.elementClose;
6 | var elementVoid = IncrementalDOM.elementVoid;
7 | var text = IncrementalDOM.text;
8 | var patch = IncrementalDOM.patch;
9 |
10 | /**
11 | * build IDOM for ast node
12 | * @private
13 | * @param {Object} node - An AST node to render
14 | */
15 | function renderAstNode(node) {
16 | if (node.type == "text") {
17 | text(node.content);
18 | }
19 |
20 | if (node.type == "tag") {
21 | var argsArray = [node.name, null, null];
22 |
23 | // convert attribs object into a flat array
24 | for (var attr in node.attrs) {
25 | argsArray.push(attr);
26 | argsArray.push(node.attrs[attr]);
27 | }
28 |
29 | if (node.voidElement) {
30 | elementVoid.apply(null, argsArray);
31 | } else {
32 | elementOpen.apply(null, argsArray);
33 |
34 | for (var i = 0, len = node.children.length; i < len; i++) {
35 | renderAstNode(node.children[i]);
36 | }
37 |
38 | elementClose(node.name);
39 | }
40 | }
41 | }
42 |
43 | /**
44 | * render function for IDOM that takes a string of HTML
45 | * @param {String} html - The string of HTML to render
46 | */
47 | function renderToIDom(html) {
48 | var ast = parse(html);
49 |
50 | if (Array.isArray(ast)) {
51 | ast.forEach(renderAstNode);
52 | } else {
53 | renderAstNode(ast);
54 | }
55 | };
56 |
57 | /**
58 | * apply the HTML to an element via Incremental DOM's `patch`
59 | * @param {Element} el - The element to apply the patch to
60 | * @param {String} html - A string of HTML
61 | */
62 | function patchHTML(el, html) {
63 | patch(el, function() {
64 | return renderToIDom(html);
65 | });
66 | }
67 |
68 | module.exports = {
69 | renderToIDom: renderToIDom,
70 | patchHTML: patchHTML
71 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html2idom",
3 | "version": "0.1.0",
4 | "description": "parse a string of html into incremental dom",
5 | "main": "index.js",
6 | "scripts": {
7 | "build-tests": "browserify test/test.js -o test/dist/test.js && browserify test/test-light.js -o test/dist/test-light.js"
8 | },
9 | "author": "eric.ponto@gmail.com",
10 | "license": "ISC",
11 | "directories": {
12 | "test": "test"
13 | },
14 | "dependencies": {
15 | "html-parse-stringify": "^1.0.2",
16 | "htmlparser2": "^3.9.2",
17 | "incremental-dom": "^0.5.1"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "https://github.com/ericponto/html2IDOM.git"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/ericponto/html2IDOM/issues"
25 | },
26 | "homepage": "https://github.com/ericponto/html2IDOM"
27 | }
28 |
--------------------------------------------------------------------------------
/test/dist/test-light.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 1) {
47 | for (var i = 1; i < arguments.length; i++) {
48 | args[i - 1] = arguments[i];
49 | }
50 | }
51 | queue.push(new Item(fun, args));
52 | if (queue.length === 1 && !draining) {
53 | setTimeout(drainQueue, 0);
54 | }
55 | };
56 |
57 | // v8 likes predictible objects
58 | function Item(fun, array) {
59 | this.fun = fun;
60 | this.array = array;
61 | }
62 | Item.prototype.run = function () {
63 | this.fun.apply(null, this.array);
64 | };
65 | process.title = 'browser';
66 | process.browser = true;
67 | process.env = {};
68 | process.argv = [];
69 | process.version = ''; // empty string to avoid regexp issues
70 | process.versions = {};
71 |
72 | function noop() {}
73 |
74 | process.on = noop;
75 | process.addListener = noop;
76 | process.once = noop;
77 | process.off = noop;
78 | process.removeListener = noop;
79 | process.removeAllListeners = noop;
80 | process.emit = noop;
81 |
82 | process.binding = function (name) {
83 | throw new Error('process.binding is not supported');
84 | };
85 |
86 | // TODO(shtylman)
87 | process.cwd = function () { return '/' };
88 | process.chdir = function (dir) {
89 | throw new Error('process.chdir is not supported');
90 | };
91 | process.umask = function() { return 0; };
92 |
93 | },{}],2:[function(require,module,exports){
94 | var parse = require("html-parse-stringify").parse;
95 | var IncrementalDOM = require("incremental-dom");
96 |
97 | var elementOpen = IncrementalDOM.elementOpen;
98 | var elementClose = IncrementalDOM.elementClose;
99 | var elementVoid = IncrementalDOM.elementVoid;
100 | var text = IncrementalDOM.text;
101 | var patch = IncrementalDOM.patch;
102 |
103 | /**
104 | * build IDOM for ast node
105 | * @private
106 | * @param {Object} node - An AST node to render
107 | */
108 | function renderAstNode(node) {
109 | if (node.type == "text") {
110 | text(node.content);
111 | }
112 |
113 | if (node.type == "tag") {
114 | var argsArray = [node.name, null, null];
115 |
116 | // convert attribs object into a flat array
117 | for (var attr in node.attrs) {
118 | argsArray.push(attr);
119 | argsArray.push(node.attrs[attr]);
120 | }
121 |
122 | if (node.voidElement) {
123 | elementVoid.apply(null, argsArray);
124 | } else {
125 | elementOpen.apply(null, argsArray);
126 |
127 | for (var i = 0, len = node.children.length; i < len; i++) {
128 | renderAstNode(node.children[i]);
129 | }
130 |
131 | elementClose(node.name);
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * render function for IDOM that takes a string of HTML
138 | * @param {String} html - The string of HTML to render
139 | */
140 | function renderToIDom(html) {
141 | var ast = parse(html);
142 |
143 | if (Array.isArray(ast)) {
144 | ast.forEach(renderAstNode);
145 | } else {
146 | renderAstNode(ast);
147 | }
148 | };
149 |
150 | /**
151 | * apply the HTML to an element via Incremental DOM's `patch`
152 | * @param {Element} el - The element to apply the patch to
153 | * @param {String} html - A string of HTML
154 | */
155 | function patchHTML(el, html) {
156 | patch(el, function() {
157 | return renderToIDom(html);
158 | });
159 | }
160 |
161 | module.exports = {
162 | renderToIDom: renderToIDom,
163 | patchHTML: patchHTML
164 | }
165 | },{"html-parse-stringify":3,"incremental-dom":7}],3:[function(require,module,exports){
166 | module.exports = {
167 | parse: require('./lib/parse'),
168 | stringify: require('./lib/stringify')
169 | };
170 |
171 | },{"./lib/parse":5,"./lib/stringify":6}],4:[function(require,module,exports){
172 | var attrRE = /([\w-]+)|['"]{1}([^'"]*)['"]{1}/g;
173 |
174 | // create optimized lookup object for
175 | // void elements as listed here:
176 | // http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
177 | var lookup = (Object.create) ? Object.create(null) : {};
178 | lookup.area = true;
179 | lookup.base = true;
180 | lookup.br = true;
181 | lookup.col = true;
182 | lookup.embed = true;
183 | lookup.hr = true;
184 | lookup.img = true;
185 | lookup.input = true;
186 | lookup.keygen = true;
187 | lookup.link = true;
188 | lookup.menuitem = true;
189 | lookup.meta = true;
190 | lookup.param = true;
191 | lookup.source = true;
192 | lookup.track = true;
193 | lookup.wbr = true;
194 |
195 | module.exports = function (tag) {
196 | var i = 0;
197 | var key;
198 | var res = {
199 | type: 'tag',
200 | name: '',
201 | voidElement: false,
202 | attrs: {},
203 | children: []
204 | };
205 |
206 | tag.replace(attrRE, function (match) {
207 | if (i % 2) {
208 | key = match;
209 | } else {
210 | if (i === 0) {
211 | if (lookup[match] || tag.charAt(tag.length - 2) === '/') {
212 | res.voidElement = true;
213 | }
214 | res.name = match;
215 | } else {
216 | res.attrs[key] = match.replace(/['"]/g, '');
217 | }
218 | }
219 | i++;
220 | });
221 |
222 | return res;
223 | };
224 |
225 | },{}],5:[function(require,module,exports){
226 | /*jshint -W030 */
227 | var tagRE = /<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g;
228 | var parseTag = require('./parse-tag');
229 | // re-used obj for quick lookups of components
230 | var empty = Object.create ? Object.create(null) : {};
231 |
232 | module.exports = function parse(html, options) {
233 | options || (options = {});
234 | options.components || (options.components = empty);
235 | var result = [];
236 | var current;
237 | var level = -1;
238 | var arr = [];
239 | var byTag = {};
240 | var inComponent = false;
241 |
242 | html.replace(tagRE, function (tag, index) {
243 | if (inComponent) {
244 | if (tag !== ('' + current.name + '>')) {
245 | return;
246 | } else {
247 | inComponent = false;
248 | }
249 | }
250 | var isOpen = tag.charAt(1) !== '/';
251 | var start = index + tag.length;
252 | var nextChar = html.charAt(start);
253 | var parent;
254 |
255 | if (isOpen) {
256 | level++;
257 |
258 | current = parseTag(tag);
259 | if (current.type === 'tag' && options.components[current.name]) {
260 | current.type = 'component';
261 | inComponent = true;
262 | }
263 |
264 | if (!current.voidElement && !inComponent && nextChar && nextChar !== '<') {
265 | current.children.push({
266 | type: 'text',
267 | content: html.slice(start, html.indexOf('<', start))
268 | });
269 | }
270 |
271 | byTag[current.tagName] = current;
272 |
273 | // if we're at root, push new base node
274 | if (level === 0) {
275 | result.push(current);
276 | }
277 |
278 | parent = arr[level - 1];
279 |
280 | if (parent) {
281 | parent.children.push(current);
282 | }
283 |
284 | arr[level] = current;
285 | }
286 |
287 | if (!isOpen || current.voidElement) {
288 | level--;
289 | if (!inComponent && nextChar !== '<' && nextChar) {
290 | // trailing text node
291 | arr[level].children.push({
292 | type: 'text',
293 | content: html.slice(start, html.indexOf('<', start))
294 | });
295 | }
296 | }
297 | });
298 |
299 | return result;
300 | };
301 |
302 | },{"./parse-tag":4}],6:[function(require,module,exports){
303 | function attrString(attrs) {
304 | var buff = [];
305 | for (var key in attrs) {
306 | buff.push(key + '="' + attrs[key] + '"');
307 | }
308 | if (!buff.length) {
309 | return '';
310 | }
311 | return ' ' + buff.join(' ');
312 | }
313 |
314 | function stringify(buff, doc) {
315 | switch (doc.type) {
316 | case 'text':
317 | return buff + doc.content;
318 | case 'tag':
319 | buff += '<' + doc.name + (doc.attrs ? attrString(doc.attrs) : '') + (doc.voidElement ? '/>' : '>');
320 | if (doc.voidElement) {
321 | return buff;
322 | }
323 | return buff + doc.children.reduce(stringify, '') + '' + doc.name + '>';
324 | }
325 | }
326 |
327 | module.exports = function (doc) {
328 | return doc.reduce(function (token, rootEl) {
329 | return token + stringify('', rootEl);
330 | }, '');
331 | };
332 |
333 | },{}],7:[function(require,module,exports){
334 | /**
335 | * @license
336 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
337 | *
338 | * Licensed under the Apache License, Version 2.0 (the "License");
339 | * you may not use this file except in compliance with the License.
340 | * You may obtain a copy of the License at
341 | *
342 | * http://www.apache.org/licenses/LICENSE-2.0
343 | *
344 | * Unless required by applicable law or agreed to in writing, software
345 | * distributed under the License is distributed on an "AS-IS" BASIS,
346 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
347 | * See the License for the specific language governing permissions and
348 | * limitations under the License.
349 | */
350 |
351 | var patch = require('./src/patch').patch;
352 | var elements = require('./src/virtual_elements');
353 |
354 | module.exports = {
355 | patch: patch,
356 | elementVoid: elements.elementVoid,
357 | elementOpenStart: elements.elementOpenStart,
358 | elementOpenEnd: elements.elementOpenEnd,
359 | elementOpen: elements.elementOpen,
360 | elementClose: elements.elementClose,
361 | text: elements.text,
362 | attr: elements.attr
363 | };
364 |
365 |
366 | },{"./src/patch":12,"./src/virtual_elements":15}],8:[function(require,module,exports){
367 | /**
368 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
369 | *
370 | * Licensed under the Apache License, Version 2.0 (the "License");
371 | * you may not use this file except in compliance with the License.
372 | * You may obtain a copy of the License at
373 | *
374 | * http://www.apache.org/licenses/LICENSE-2.0
375 | *
376 | * Unless required by applicable law or agreed to in writing, software
377 | * distributed under the License is distributed on an "AS-IS" BASIS,
378 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
379 | * See the License for the specific language governing permissions and
380 | * limitations under the License.
381 | */
382 |
383 | var nodes = require('./nodes'),
384 | createNode = nodes.createNode,
385 | getKey = nodes.getKey,
386 | getNodeName = nodes.getNodeName,
387 | getChild = nodes.getChild,
388 | registerChild = nodes.registerChild;
389 | var markVisited = require('./traversal').markVisited;
390 | var getWalker = require('./walker').getWalker;
391 |
392 |
393 | /**
394 | * Checks whether or not a given node matches the specified nodeName and key.
395 | *
396 | * @param {?Node} node An HTML node, typically an HTMLElement or Text.
397 | * @param {?string} nodeName The nodeName for this node.
398 | * @param {?string} key An optional key that identifies a node.
399 | * @return {boolean} True if the node matches, false otherwise.
400 | */
401 | var matches = function(node, nodeName, key) {
402 | return node &&
403 | key === getKey(node) &&
404 | nodeName === getNodeName(node);
405 | };
406 |
407 |
408 | /**
409 | * Aligns the virtual Element definition with the actual DOM, moving the
410 | * corresponding DOM node to the correct location or creating it if necessary.
411 | * @param {?string} nodeName For an Element, this should be a valid tag string.
412 | * For a Text, this should be #text.
413 | * @param {?string} key The key used to identify this element.
414 | * @param {?Array<*>|string} statics For an Element, this should be an array of
415 | * name-value pairs. For a Text, this should be the text content of the
416 | * node.
417 | * @return {!Node} The matching node.
418 | */
419 | var alignWithDOM = function(nodeName, key, statics) {
420 | var walker = getWalker();
421 | var currentNode = walker.currentNode;
422 | var parent = walker.getCurrentParent();
423 | var matchingNode;
424 |
425 | // Check to see if we have a node to reuse
426 | if (matches(currentNode, nodeName, key)) {
427 | matchingNode = currentNode;
428 | } else {
429 | var existingNode = key && getChild(parent, key);
430 |
431 | // Check to see if the node has moved within the parent or if a new one
432 | // should be created
433 | if (existingNode) {
434 | matchingNode = existingNode;
435 | } else {
436 | matchingNode = createNode(walker.doc, nodeName, key, statics);
437 | registerChild(parent, key, matchingNode);
438 | }
439 |
440 | parent.insertBefore(matchingNode, currentNode);
441 | walker.currentNode = matchingNode;
442 | }
443 |
444 | markVisited(parent, matchingNode);
445 |
446 | return matchingNode;
447 | };
448 |
449 |
450 | /** */
451 | module.exports = {
452 | alignWithDOM: alignWithDOM
453 | };
454 |
455 |
456 | },{"./nodes":11,"./traversal":13,"./walker":16}],9:[function(require,module,exports){
457 | /**
458 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
459 | *
460 | * Licensed under the Apache License, Version 2.0 (the "License");
461 | * you may not use this file except in compliance with the License.
462 | * You may obtain a copy of the License at
463 | *
464 | * http://www.apache.org/licenses/LICENSE-2.0
465 | *
466 | * Unless required by applicable law or agreed to in writing, software
467 | * distributed under the License is distributed on an "AS-IS" BASIS,
468 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
469 | * See the License for the specific language governing permissions and
470 | * limitations under the License.
471 | */
472 |
473 | var getData = require('./node_data').getData;
474 |
475 |
476 | /**
477 | * Applies an attribute or property to a given Element. If the value is a object
478 | * or a function (which includes null), it is set as a property on the Element.
479 | * Otherwise, the value is set as an attribute.
480 | * @param {!Element} el
481 | * @param {string} name The attribute's name.
482 | * @param {*} value The attribute's value. If the value is a string, it is set
483 | * as an HTML attribute, otherwise, it is set on node.
484 | */
485 | var applyAttr = function(el, name, value) {
486 | var data = getData(el);
487 | var attrs = data.attrs;
488 |
489 | if (attrs[name] === value) {
490 | return;
491 | }
492 |
493 | var type = typeof value;
494 |
495 | if (value === undefined) {
496 | el.removeAttribute(name);
497 | } else if (type === 'object' || type === 'function') {
498 | el[name] = value;
499 | } else {
500 | el.setAttribute(name, value);
501 | }
502 |
503 | attrs[name] = value;
504 | };
505 |
506 |
507 | /**
508 | * Applies a style to an Element. No vendor prefix expansion is done for
509 | * property names/values.
510 | * @param {!Element} el
511 | * @param {string|Object} style The style to set. Either a string
512 | * of css or an object containing property-value pairs.
513 | */
514 | var applyStyle = function(el, style) {
515 | if (typeof style === 'string' || style instanceof String) {
516 | el.style.cssText = style;
517 | } else {
518 | el.style.cssText = '';
519 |
520 | for (var prop in style) {
521 | el.style[prop] = style[prop];
522 | }
523 | }
524 | };
525 |
526 |
527 | /**
528 | * Updates a single attribute on an Element. For some types (e.g. id or class),
529 | * the value is applied directly to the Element using the corresponding accessor
530 | * function.
531 | * @param {!Element} el
532 | * @param {string} name The attribute's name.
533 | * @param {*} value The attribute's value. If the value is a string, it is set
534 | * as an HTML attribute, otherwise, it is set on node.
535 | */
536 | var updateAttribute = function(el, name, value) {
537 | switch (name) {
538 | case 'id':
539 | el.id = value;
540 | break;
541 | case 'class':
542 | el.className = value;
543 | break;
544 | case 'tabindex':
545 | el.tabIndex = value;
546 | break;
547 | case 'style':
548 | applyStyle(el, value);
549 | break;
550 | default:
551 | applyAttr(el, name, value);
552 | break;
553 | }
554 | };
555 |
556 |
557 | /** */
558 | module.exports = {
559 | updateAttribute: updateAttribute
560 | };
561 |
562 |
563 | },{"./node_data":10}],10:[function(require,module,exports){
564 | /**
565 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
566 | *
567 | * Licensed under the Apache License, Version 2.0 (the "License");
568 | * you may not use this file except in compliance with the License.
569 | * You may obtain a copy of the License at
570 | *
571 | * http://www.apache.org/licenses/LICENSE-2.0
572 | *
573 | * Unless required by applicable law or agreed to in writing, software
574 | * distributed under the License is distributed on an "AS-IS" BASIS,
575 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
576 | * See the License for the specific language governing permissions and
577 | * limitations under the License.
578 | */
579 |
580 |
581 | /**
582 | * Keeps track of information needed to perform diffs for a given DOM node.
583 | * @param {?string} nodeName
584 | * @param {?string} key
585 | * @constructor
586 | */
587 | function NodeData(nodeName, key) {
588 | /**
589 | * The attributes and their values.
590 | * @const
591 | */
592 | this.attrs = {};
593 |
594 | /**
595 | * An array of attribute name/value pairs, used for quickly diffing the
596 | * incomming attributes to see if the DOM node's attributes need to be
597 | * updated.
598 | * @const {Array<*>}
599 | */
600 | this.attrsArr = [];
601 |
602 | /**
603 | * The incoming attributes for this Node, before they are updated.
604 | * @const {!Object}
605 | */
606 | this.newAttrs = {};
607 |
608 | /**
609 | * The key used to identify this node, used to preserve DOM nodes when they
610 | * move within their parent.
611 | * @const
612 | */
613 | this.key = key;
614 |
615 | /**
616 | * Keeps track of children within this node by their key.
617 | * {?Object}
618 | */
619 | this.keyMap = null;
620 |
621 | /**
622 | * The last child to have been visited within the current pass.
623 | * {?Node}
624 | */
625 | this.lastVisitedChild = null;
626 |
627 | /**
628 | * The node name for this node.
629 | * @const
630 | */
631 | this.nodeName = nodeName;
632 |
633 | /**
634 | * @const {string}
635 | */
636 | this.text = null;
637 | }
638 |
639 |
640 | /**
641 | * Initializes a NodeData object for a Node.
642 | *
643 | * @param {!Node} node The node to initialze data for.
644 | * @param {string} nodeName The node name of node.
645 | * @param {?string} key The key that identifies the node.
646 | * @return {!NodeData} The newly initialized data object
647 | */
648 | var initData = function(node, nodeName, key) {
649 | var data = new NodeData(nodeName, key);
650 | node['__incrementalDOMData'] = data;
651 | return data;
652 | };
653 |
654 |
655 | /**
656 | * Retrieves the NodeData object for a Node, creating it if necessary.
657 | *
658 | * @param {!Node} node The node to retrieve the data for.
659 | * @return {NodeData} The NodeData for this Node.
660 | */
661 | var getData = function(node) {
662 | var data = node['__incrementalDOMData'];
663 |
664 | if (!data) {
665 | var nodeName = node.nodeName.toLowerCase();
666 | var key = null;
667 |
668 | if (node instanceof Element) {
669 | key = node.getAttribute('key');
670 | }
671 |
672 | data = initData(node, nodeName, key);
673 | }
674 |
675 | return data;
676 | };
677 |
678 |
679 | /** */
680 | module.exports = {
681 | getData: getData,
682 | initData: initData
683 | };
684 |
685 |
686 | },{}],11:[function(require,module,exports){
687 | /**
688 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
689 | *
690 | * Licensed under the Apache License, Version 2.0 (the "License");
691 | * you may not use this file except in compliance with the License.
692 | * You may obtain a copy of the License at
693 | *
694 | * http://www.apache.org/licenses/LICENSE-2.0
695 | *
696 | * Unless required by applicable law or agreed to in writing, software
697 | * distributed under the License is distributed on an "AS-IS" BASIS,
698 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
699 | * See the License for the specific language governing permissions and
700 | * limitations under the License.
701 | */
702 |
703 | var updateAttribute = require('./attributes').updateAttribute;
704 | var nodeData = require('./node_data'),
705 | getData = nodeData.getData,
706 | initData = nodeData.initData;
707 |
708 |
709 | /**
710 | * Creates an Element.
711 | * @param {!Document} doc The document with which to create the Element.
712 | * @param {string} tag The tag for the Element.
713 | * @param {?string} key A key to identify the Element.
714 | * @param {?Array<*>} statics An array of attribute name/value pairs of
715 | * the static attributes for the Element.
716 | * @return {!Element}
717 | */
718 | var createElement = function(doc, tag, key, statics) {
719 | var el = doc.createElement(tag);
720 | initData(el, tag, key);
721 |
722 | if (statics) {
723 | for (var i = 0; i < statics.length; i += 2) {
724 | updateAttribute(el, statics[i], statics[i + 1]);
725 | }
726 | }
727 |
728 | return el;
729 | };
730 |
731 | /**
732 | * Creates a Text.
733 | * @param {!Document} doc The document with which to create the Text.
734 | * @param {string} text The intial content of the Text.
735 | * @return {!Text}
736 | */
737 | var createTextNode = function(doc, text) {
738 | var node = doc.createTextNode(text);
739 | getData(node).text = text;
740 |
741 | return node;
742 | };
743 |
744 |
745 | /**
746 | * Creates a Node, either a Text or an Element depending on the node name
747 | * provided.
748 | * @param {!Document} doc The document with which to create the Node.
749 | * @param {string} nodeName The tag if creating an element or #text to create
750 | * a Text.
751 | * @param {?string} key A key to identify the Element.
752 | * @param {?Array<*>|string} statics The static data to initialize the Node
753 | * with. For an Element, an array of attribute name/value pairs of
754 | * the static attributes for the Element. For a Text, a string with the
755 | * intial content of the Text.
756 | * @return {!Node}
757 | */
758 | var createNode = function(doc, nodeName, key, statics) {
759 | if (nodeName === '#text') {
760 | return createTextNode(doc, statics);
761 | }
762 |
763 | return createElement(doc, nodeName, key, statics);
764 | };
765 |
766 |
767 | /**
768 | * Creates a mapping that can be used to look up children using a key.
769 | * @param {!Element} el
770 | * @return {!Object} A mapping of keys to the children of the
771 | * Element.
772 | */
773 | var createKeyMap = function(el) {
774 | var map = {};
775 | var children = el.children;
776 | var count = children.length;
777 |
778 | for (var i = 0; i < count; i += 1) {
779 | var child = children[i];
780 | var key = getKey(child);
781 |
782 | if (key) {
783 | map[key] = child;
784 | }
785 | }
786 |
787 | return map;
788 | };
789 |
790 |
791 | /**
792 | * @param {?Node} node A node to get the key for.
793 | * @return {?string} The key for the Node, if applicable.
794 | */
795 | var getKey = function(node) {
796 | return getData(node).key;
797 | };
798 |
799 |
800 | /**
801 | * @param {?Node} node A node to get the node name for.
802 | * @return {?string} The node name for the Node, if applicable.
803 | */
804 | var getNodeName = function(node) {
805 | return getData(node).nodeName;
806 | };
807 |
808 |
809 | /**
810 | * Retrieves the mapping of key to child node for a given Element, creating it
811 | * if necessary.
812 | * @param {!Element} el
813 | * @return {!Object} A mapping of keys to child Nodes
814 | */
815 | var getKeyMap = function(el) {
816 | var data = getData(el);
817 |
818 | if (!data.keyMap) {
819 | data.keyMap = createKeyMap(el);
820 | }
821 |
822 | return data.keyMap;
823 | };
824 |
825 |
826 | /**
827 | * Retrieves a child from the parent with the given key.
828 | * @param {!Element} parent
829 | * @param {?string} key
830 | * @return {?Node} The child corresponding to the key.
831 | */
832 | var getChild = function(parent, key) {
833 | return getKeyMap(parent)[key];
834 | };
835 |
836 |
837 | /**
838 | * Registers a node as being a child. If a key is provided, the parent will
839 | * keep track of the child using the key. The child can be retrieved using the
840 | * same key using getKeyMap. The provided key should be unique within the
841 | * parent Element.
842 | * @param {!Element} parent The parent of child.
843 | * @param {?string} key A key to identify the child with.
844 | * @param {!Node} child The child to register.
845 | */
846 | var registerChild = function(parent, key, child) {
847 | if (key) {
848 | getKeyMap(parent)[key] = child;
849 | }
850 | };
851 |
852 |
853 | /** */
854 | module.exports = {
855 | createNode: createNode,
856 | getKey: getKey,
857 | getNodeName: getNodeName,
858 | getChild: getChild,
859 | registerChild: registerChild
860 | };
861 |
862 |
863 | },{"./attributes":9,"./node_data":10}],12:[function(require,module,exports){
864 | /**
865 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
866 | *
867 | * Licensed under the Apache License, Version 2.0 (the "License");
868 | * you may not use this file except in compliance with the License.
869 | * You may obtain a copy of the License at
870 | *
871 | * http://www.apache.org/licenses/LICENSE-2.0
872 | *
873 | * Unless required by applicable law or agreed to in writing, software
874 | * distributed under the License is distributed on an "AS-IS" BASIS,
875 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
876 | * See the License for the specific language governing permissions and
877 | * limitations under the License.
878 | */
879 |
880 | var traversal = require('./traversal'),
881 | firstChild = traversal.firstChild,
882 | parentNode = traversal.parentNode;
883 | var TreeWalker = require('./tree_walker');
884 | var walker = require('./walker'),
885 | getWalker = walker.getWalker,
886 | setWalker = walker.setWalker;
887 |
888 |
889 | /**
890 | * Patches the document starting at el with the provided function. This function
891 | * may be called during an existing patch operation.
892 | * @param {!Element} el the element to patch
893 | * @param {!function} fn A function containing elementOpen/elementClose/etc.
894 | * calls that describe the DOM.
895 | */
896 | var patch = function(el, fn) {
897 | var prevWalker = getWalker();
898 | setWalker(new TreeWalker(el));
899 |
900 | firstChild();
901 | fn();
902 | parentNode();
903 |
904 | setWalker(prevWalker);
905 | };
906 |
907 |
908 | /** */
909 | module.exports = {
910 | patch: patch
911 | };
912 |
913 |
914 | },{"./traversal":13,"./tree_walker":14,"./walker":16}],13:[function(require,module,exports){
915 | /**
916 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
917 | *
918 | * Licensed under the Apache License, Version 2.0 (the "License");
919 | * you may not use this file except in compliance with the License.
920 | * You may obtain a copy of the License at
921 | *
922 | * http://www.apache.org/licenses/LICENSE-2.0
923 | *
924 | * Unless required by applicable law or agreed to in writing, software
925 | * distributed under the License is distributed on an "AS-IS" BASIS,
926 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
927 | * See the License for the specific language governing permissions and
928 | * limitations under the License.
929 | */
930 |
931 | var getWalker = require('./walker').getWalker;
932 | var getData = require('./node_data').getData;
933 |
934 |
935 | /**
936 | * Enters a Element, clearing out the last visited child field.
937 | * @param {!Element} node
938 | */
939 | var enterNode = function(node) {
940 | var data = getData(node);
941 | data.lastVisitedChild = null;
942 | };
943 |
944 |
945 | /**
946 | * Clears out any unvisited Nodes, as the corresponding virtual element
947 | * functions were never called for them.
948 | * @param {!Element} node
949 | */
950 | var exitNode = function(node) {
951 | var data = getData(node);
952 | var lastVisitedChild = data.lastVisitedChild;
953 |
954 | if (node.lastChild === lastVisitedChild) {
955 | return;
956 | }
957 |
958 | while (node.lastChild !== lastVisitedChild) {
959 | node.removeChild(node.lastChild);
960 | }
961 |
962 | // Invalidate the key map since we removed children. It will get recreated
963 | // next time we need it.
964 | data.keyMap = null;
965 | };
966 |
967 |
968 | /**
969 | * Marks a parent as having visited a child.
970 | * @param {!Element} parent
971 | * @param {!Node} child
972 | */
973 | var markVisited = function(parent, child) {
974 | var data = getData(parent);
975 | data.lastVisitedChild = child;
976 | };
977 |
978 |
979 | /**
980 | * Changes to the first child of the current node.
981 | */
982 | var firstChild = function() {
983 | var walker = getWalker();
984 | enterNode(walker.currentNode);
985 | walker.firstChild();
986 | };
987 |
988 |
989 | /**
990 | * Changes to the next sibling of the current node.
991 | */
992 | var nextSibling = function() {
993 | var walker = getWalker();
994 | walker.nextSibling();
995 | };
996 |
997 |
998 | /**
999 | * Changes to the parent of the current node, removing any unvisited children.
1000 | */
1001 | var parentNode = function() {
1002 | var walker = getWalker();
1003 | walker.parentNode();
1004 | exitNode(walker.currentNode);
1005 | };
1006 |
1007 |
1008 | /** */
1009 | module.exports = {
1010 | firstChild: firstChild,
1011 | nextSibling: nextSibling,
1012 | parentNode: parentNode,
1013 | markVisited: markVisited
1014 | };
1015 |
1016 |
1017 | },{"./node_data":10,"./walker":16}],14:[function(require,module,exports){
1018 | /**
1019 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
1020 | *
1021 | * Licensed under the Apache License, Version 2.0 (the "License");
1022 | * you may not use this file except in compliance with the License.
1023 | * You may obtain a copy of the License at
1024 | *
1025 | * http://www.apache.org/licenses/LICENSE-2.0
1026 | *
1027 | * Unless required by applicable law or agreed to in writing, software
1028 | * distributed under the License is distributed on an "AS-IS" BASIS,
1029 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1030 | * See the License for the specific language governing permissions and
1031 | * limitations under the License.
1032 | */
1033 |
1034 | /**
1035 | * Similar to the built-in Treewalker class, but simplified and allows direct
1036 | * access to modify the currentNode property.
1037 | * @param {!Node} node The root Node of the subtree the walker should start
1038 | * traversing.
1039 | * @constructor
1040 | */
1041 | function TreeWalker(node) {
1042 | /**
1043 | * Keeps track of the current parent node. This is necessary as the traversal
1044 | * methods may traverse past the last child and we still need a way to get
1045 | * back to the parent.
1046 | * @const @private {!Array}
1047 | */
1048 | this.stack_ = [];
1049 |
1050 | /** {?Node} */
1051 | this.currentNode = node;
1052 |
1053 | /** {!Document} */
1054 | this.doc = node.ownerDocument;
1055 | }
1056 |
1057 |
1058 | /**
1059 | * @return {!Node} The current parent of the current location in the subtree.
1060 | */
1061 | TreeWalker.prototype.getCurrentParent = function() {
1062 | return this.stack_[this.stack_.length - 1];
1063 | };
1064 |
1065 |
1066 | /**
1067 | * Changes the current location the firstChild of the current location.
1068 | */
1069 | TreeWalker.prototype.firstChild = function() {
1070 | this.stack_.push(this.currentNode);
1071 | this.currentNode = this.currentNode.firstChild;
1072 | };
1073 |
1074 |
1075 | /**
1076 | * Changes the current location the nextSibling of the current location.
1077 | */
1078 | TreeWalker.prototype.nextSibling = function() {
1079 | this.currentNode = this.currentNode.nextSibling;
1080 | };
1081 |
1082 |
1083 | /**
1084 | * Changes the current location the parentNode of the current location.
1085 | */
1086 | TreeWalker.prototype.parentNode = function() {
1087 | this.currentNode = this.stack_.pop();
1088 | };
1089 |
1090 |
1091 | /** */
1092 | module.exports = TreeWalker;
1093 |
1094 |
1095 | },{}],15:[function(require,module,exports){
1096 | (function (process){
1097 | /**
1098 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
1099 | *
1100 | * Licensed under the Apache License, Version 2.0 (the "License");
1101 | * you may not use this file except in compliance with the License.
1102 | * You may obtain a copy of the License at
1103 | *
1104 | * http://www.apache.org/licenses/LICENSE-2.0
1105 | *
1106 | * Unless required by applicable law or agreed to in writing, software
1107 | * distributed under the License is distributed on an "AS-IS" BASIS,
1108 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1109 | * See the License for the specific language governing permissions and
1110 | * limitations under the License.
1111 | */
1112 |
1113 | var alignWithDOM = require('./alignment').alignWithDOM;
1114 | var updateAttribute = require('./attributes').updateAttribute;
1115 | var getData = require('./node_data').getData;
1116 | var getWalker = require('./walker').getWalker;
1117 | var traversal = require('./traversal'),
1118 | firstChild = traversal.firstChild,
1119 | nextSibling = traversal.nextSibling,
1120 | parentNode = traversal.parentNode;
1121 |
1122 |
1123 | /**
1124 | * The offset in the virtual element declaration where the attributes are
1125 | * specified.
1126 | * @const
1127 | */
1128 | var ATTRIBUTES_OFFSET = 3;
1129 |
1130 |
1131 | /**
1132 | * Builds an array of arguments for use with elementOpenStart, attr and
1133 | * elementOpenEnd.
1134 | * @type {Array<*>}
1135 | * @const
1136 | */
1137 | var argsBuilder = [];
1138 |
1139 |
1140 | if (process.env.NODE_ENV !== 'production') {
1141 | /**
1142 | * Keeps track whether or not we are in an attributes declaration (after
1143 | * elementOpenStart, but before elementOpenEnd).
1144 | * @type {boolean}
1145 | */
1146 | var inAttributes = false;
1147 |
1148 |
1149 | /** Makes sure that the caller is not where attributes are expected. */
1150 | var assertNotInAttributes = function() {
1151 | if (inAttributes) {
1152 | throw new Error('Was not expecting a call to attr or elementOpenEnd, ' +
1153 | 'they must follow a call to elementOpenStart.');
1154 | }
1155 | };
1156 |
1157 |
1158 | /** Makes sure that the caller is where attributes are expected. */
1159 | var assertInAttributes = function() {
1160 | if (!inAttributes) {
1161 | throw new Error('Was expecting a call to attr or elementOpenEnd. ' +
1162 | 'elementOpenStart must be followed by zero or more calls to attr, ' +
1163 | 'then one call to elementOpenEnd.');
1164 | }
1165 | };
1166 |
1167 |
1168 | /** Updates the state to being in an attribute declaration. */
1169 | var setInAttributes = function() {
1170 | inAttributes = true;
1171 | };
1172 |
1173 |
1174 | /** Updates the state to not being in an attribute declaration. */
1175 | var setNotInAttributes = function() {
1176 | inAttributes = false;
1177 | };
1178 | }
1179 |
1180 |
1181 | /**
1182 | * Checks to see if one or more attributes have changed for a given
1183 | * Element. When no attributes have changed, this function is much faster than
1184 | * checking each individual argument. When attributes have changed, the overhead
1185 | * of this function is minimal.
1186 | *
1187 | * This function is called in the context of the Element and the arguments from
1188 | * elementOpen-like function so that the arguments are not de-optimized.
1189 | *
1190 | * @this {Element} The Element to check for changed attributes.
1191 | * @param {*} unused1
1192 | * @param {*} unused2
1193 | * @param {*} unused3
1194 | * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
1195 | * for the Element.
1196 | * @return {boolean} True if the Element has one or more changed attributes,
1197 | * false otherwise.
1198 | */
1199 | var hasChangedAttrs = function(unused1, unused2, unused3, var_args) {
1200 | var data = getData(this);
1201 | var attrsArr = data.attrsArr;
1202 | var attrsChanged = false;
1203 | var i;
1204 |
1205 | for (i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
1206 | // Translate the from the arguments index (for values) to the attribute's
1207 | // ordinal. The attribute values are at arguments index 3, 5, 7, etc. To get
1208 | // the ordinal, need to subtract the offset and divide by 2
1209 | if (attrsArr[(i - ATTRIBUTES_OFFSET) >> 1] !== arguments[i + 1]) {
1210 | attrsChanged = true;
1211 | break;
1212 | }
1213 | }
1214 |
1215 | if (attrsChanged) {
1216 | for (i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
1217 | attrsArr[(i - ATTRIBUTES_OFFSET) >> 1] = arguments[i + 1];
1218 | }
1219 | }
1220 |
1221 | return attrsChanged;
1222 | };
1223 |
1224 |
1225 | /**
1226 | * Updates the newAttrs object for an Element.
1227 | *
1228 | * This function is called in the context of the Element and the arguments from
1229 | * elementOpen-like function so that the arguments are not de-optimized.
1230 | *
1231 | * @this {Element} The Element to update newAttrs for.
1232 | * @param {*} unused1
1233 | * @param {*} unused2
1234 | * @param {*} unused3
1235 | * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
1236 | * for the Element.
1237 | * @return {!Object} The updated newAttrs object.
1238 | */
1239 | var updateNewAttrs = function(unused1, unused2, unused3, var_args) {
1240 | var node = this;
1241 | var data = getData(node);
1242 | var newAttrs = data.newAttrs;
1243 |
1244 | for (var attr in newAttrs) {
1245 | newAttrs[attr] = undefined;
1246 | }
1247 |
1248 | for (var i = ATTRIBUTES_OFFSET; i < arguments.length; i += 2) {
1249 | newAttrs[arguments[i]] = arguments[i + 1];
1250 | }
1251 |
1252 | return newAttrs;
1253 | };
1254 |
1255 |
1256 | /**
1257 | * Updates the attributes for a given Element.
1258 | * @param {!Element} node
1259 | * @param {!Object} newAttrs The new attributes for node
1260 | */
1261 | var updateAttributes = function(node, newAttrs) {
1262 | for (var attr in newAttrs) {
1263 | updateAttribute(node, attr, newAttrs[attr]);
1264 | }
1265 | };
1266 |
1267 |
1268 | /**
1269 | * Declares a virtual Element at the current location in the document. This
1270 | * corresponds to an opening tag and a elementClose tag is required.
1271 | * @param {string} tag The element's tag.
1272 | * @param {?string} key The key used to identify this element. This can be an
1273 | * empty string, but performance may be better if a unique value is used
1274 | * when iterating over an array of items.
1275 | * @param {?Array<*>} statics An array of attribute name/value pairs of the
1276 | * static attributes for the Element. These will only be set once when the
1277 | * Element is created.
1278 | * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
1279 | * for the Element.
1280 | */
1281 | var elementOpen = function(tag, key, statics, var_args) {
1282 | if (process.env.NODE_ENV !== 'production') {
1283 | assertNotInAttributes();
1284 | }
1285 |
1286 | var node = alignWithDOM(tag, key, statics);
1287 |
1288 | if (hasChangedAttrs.apply(node, arguments)) {
1289 | var newAttrs = updateNewAttrs.apply(node, arguments);
1290 | updateAttributes(node, newAttrs);
1291 | }
1292 |
1293 | firstChild();
1294 | };
1295 |
1296 |
1297 | /**
1298 | * Declares a virtual Element at the current location in the document. This
1299 | * corresponds to an opening tag and a elementClose tag is required. This is
1300 | * like elementOpen, but the attributes are defined using the attr function
1301 | * rather than being passed as arguments. Must be folllowed by 0 or more calls
1302 | * to attr, then a call to elementOpenEnd.
1303 | * @param {string} tag The element's tag.
1304 | * @param {?string} key The key used to identify this element. This can be an
1305 | * empty string, but performance may be better if a unique value is used
1306 | * when iterating over an array of items.
1307 | * @param {?Array<*>} statics An array of attribute name/value pairs of the
1308 | * static attributes for the Element. These will only be set once when the
1309 | * Element is created.
1310 | */
1311 | var elementOpenStart = function(tag, key, statics) {
1312 | if (process.env.NODE_ENV !== 'production') {
1313 | assertNotInAttributes();
1314 | setInAttributes();
1315 | }
1316 |
1317 | argsBuilder[0] = tag;
1318 | argsBuilder[1] = key;
1319 | argsBuilder[2] = statics;
1320 | argsBuilder.length = ATTRIBUTES_OFFSET;
1321 | };
1322 |
1323 |
1324 | /***
1325 | * Defines a virtual attribute at this point of the DOM. This is only valid
1326 | * when called between elementOpenStart and elementOpenEnd.
1327 | *
1328 | * @param {string} name
1329 | * @param {*} value
1330 | */
1331 | var attr = function(name, value) {
1332 | if (process.env.NODE_ENV !== 'production') {
1333 | assertInAttributes();
1334 | }
1335 |
1336 | argsBuilder.push(name, value);
1337 | };
1338 |
1339 |
1340 | /**
1341 | * Closes an open tag started with elementOpenStart.
1342 | */
1343 | var elementOpenEnd = function() {
1344 | if (process.env.NODE_ENV !== 'production') {
1345 | assertInAttributes();
1346 | setNotInAttributes();
1347 | }
1348 |
1349 | elementOpen.apply(null, argsBuilder);
1350 | };
1351 |
1352 |
1353 | /**
1354 | * Closes an open virtual Element.
1355 | *
1356 | * @param {string} tag The element's tag.
1357 | */
1358 | var elementClose = function(tag) {
1359 | if (process.env.NODE_ENV !== 'production') {
1360 | assertNotInAttributes();
1361 | }
1362 |
1363 | parentNode();
1364 | nextSibling();
1365 | };
1366 |
1367 |
1368 | /**
1369 | * Declares a virtual Element at the current location in the document that has
1370 | * no children.
1371 | * @param {string} tag The element's tag.
1372 | * @param {?string} key The key used to identify this element. This can be an
1373 | * empty string, but performance may be better if a unique value is used
1374 | * when iterating over an array of items.
1375 | * @param {?Array<*>} statics An array of attribute name/value pairs of the
1376 | * static attributes for the Element. These will only be set once when the
1377 | * Element is created.
1378 | * @param {...*} var_args Attribute name/value pairs of the dynamic attributes
1379 | * for the Element.
1380 | */
1381 | var elementVoid = function(tag, key, statics, var_args) {
1382 | if (process.env.NODE_ENV !== 'production') {
1383 | assertNotInAttributes();
1384 | }
1385 |
1386 | elementOpen.apply(null, arguments);
1387 | elementClose.apply(null, arguments);
1388 | };
1389 |
1390 |
1391 | /**
1392 | * Declares a virtual Text at this point in the document.
1393 | *
1394 | * @param {string} value The text of the Text.
1395 | */
1396 | var text = function(value) {
1397 | if (process.env.NODE_ENV !== 'production') {
1398 | assertNotInAttributes();
1399 | }
1400 |
1401 | var node = alignWithDOM('#text', null, value);
1402 | var data = getData(node);
1403 |
1404 | if (data.text !== value) {
1405 | node.data = value;
1406 | data.text = value;
1407 | }
1408 |
1409 | nextSibling();
1410 | };
1411 |
1412 |
1413 | /** */
1414 | module.exports = {
1415 | elementOpenStart: elementOpenStart,
1416 | elementOpenEnd: elementOpenEnd,
1417 | elementOpen: elementOpen,
1418 | elementVoid: elementVoid,
1419 | elementClose: elementClose,
1420 | text: text,
1421 | attr: attr
1422 | };
1423 |
1424 |
1425 | }).call(this,require('_process'))
1426 | },{"./alignment":8,"./attributes":9,"./node_data":10,"./traversal":13,"./walker":16,"_process":1}],16:[function(require,module,exports){
1427 | /**
1428 | * Copyright 2015 The Incremental DOM Authors. All Rights Reserved.
1429 | *
1430 | * Licensed under the Apache License, Version 2.0 (the "License");
1431 | * you may not use this file except in compliance with the License.
1432 | * You may obtain a copy of the License at
1433 | *
1434 | * http://www.apache.org/licenses/LICENSE-2.0
1435 | *
1436 | * Unless required by applicable law or agreed to in writing, software
1437 | * distributed under the License is distributed on an "AS-IS" BASIS,
1438 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1439 | * See the License for the specific language governing permissions and
1440 | * limitations under the License.
1441 | */
1442 |
1443 | /**
1444 | * @type {TreeWalker}
1445 | */
1446 | var walker_;
1447 |
1448 |
1449 | /**
1450 | * @return {TreeWalker} the current TreeWalker
1451 | */
1452 | var getWalker = function() {
1453 | return walker_;
1454 | };
1455 |
1456 |
1457 | /**
1458 | * Sets the current TreeWalker
1459 | * @param {TreeWalker} walker
1460 | */
1461 | var setWalker = function(walker) {
1462 | walker_ = walker;
1463 | };
1464 |
1465 |
1466 | /** */
1467 | module.exports = {
1468 | getWalker: getWalker,
1469 | setWalker: setWalker
1470 | };
1471 |
1472 |
1473 | },{}],17:[function(require,module,exports){
1474 | var html2idom = require("../light");
1475 |
1476 | html2idom.patchHTML(document.querySelector("#test"), "Hello, Incremental DOM
");
1477 | },{"../light":2}]},{},[17]);
1478 |
--------------------------------------------------------------------------------
/test/test-light.js:
--------------------------------------------------------------------------------
1 | var html2idom = require("../light");
2 |
3 | html2idom.patchHTML(document.querySelector("#test"), "Hello, Incremental DOM
");
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Test HTML To Incremental DOM
6 |
11 |
12 |
13 |
14 |
Hello, world
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | var html2idom = require("../index");
2 |
3 | html2idom.patchHTML(document.querySelector("#test"), "Hello, Incremental DOM
");
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | core-util-is@~1.0.0:
6 | version "1.0.2"
7 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
8 |
9 | dom-serializer@0:
10 | version "0.1.0"
11 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
12 | dependencies:
13 | domelementtype "~1.1.1"
14 | entities "~1.1.1"
15 |
16 | domelementtype@1, domelementtype@^1.3.0:
17 | version "1.3.0"
18 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
19 |
20 | domelementtype@~1.1.1:
21 | version "1.1.3"
22 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
23 |
24 | domhandler@^2.3.0:
25 | version "2.4.1"
26 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259"
27 | dependencies:
28 | domelementtype "1"
29 |
30 | domutils@^1.5.1:
31 | version "1.6.2"
32 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff"
33 | dependencies:
34 | dom-serializer "0"
35 | domelementtype "1"
36 |
37 | entities@^1.1.1, entities@~1.1.1:
38 | version "1.1.1"
39 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
40 |
41 | html-parse-stringify@^1.0.2:
42 | version "1.0.2"
43 | resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-1.0.2.tgz#1208e98cd7430ad7efb12e1e7acee9214fd22283"
44 | dependencies:
45 | void-elements "^1.0.0"
46 |
47 | htmlparser2@^3.9.2:
48 | version "3.9.2"
49 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
50 | dependencies:
51 | domelementtype "^1.3.0"
52 | domhandler "^2.3.0"
53 | domutils "^1.5.1"
54 | entities "^1.1.1"
55 | inherits "^2.0.1"
56 | readable-stream "^2.0.2"
57 |
58 | incremental-dom@^0.5.1:
59 | version "0.5.1"
60 | resolved "https://registry.yarnpkg.com/incremental-dom/-/incremental-dom-0.5.1.tgz#52f4539c3e9eee7cd112a6da052fb7162fefb0c3"
61 |
62 | inherits@^2.0.1, inherits@~2.0.3:
63 | version "2.0.3"
64 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
65 |
66 | isarray@~1.0.0:
67 | version "1.0.0"
68 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
69 |
70 | process-nextick-args@~1.0.6:
71 | version "1.0.7"
72 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
73 |
74 | readable-stream@^2.0.2:
75 | version "2.3.3"
76 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
77 | dependencies:
78 | core-util-is "~1.0.0"
79 | inherits "~2.0.3"
80 | isarray "~1.0.0"
81 | process-nextick-args "~1.0.6"
82 | safe-buffer "~5.1.1"
83 | string_decoder "~1.0.3"
84 | util-deprecate "~1.0.1"
85 |
86 | safe-buffer@~5.1.0, safe-buffer@~5.1.1:
87 | version "5.1.1"
88 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
89 |
90 | string_decoder@~1.0.3:
91 | version "1.0.3"
92 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
93 | dependencies:
94 | safe-buffer "~5.1.0"
95 |
96 | util-deprecate@~1.0.1:
97 | version "1.0.2"
98 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
99 |
100 | void-elements@^1.0.0:
101 | version "1.0.0"
102 | resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-1.0.0.tgz#6e5db1e35d591f5ac690ce1a340f793a817b2c2a"
103 |
--------------------------------------------------------------------------------