Merge of marko-vdom completed

This commit is contained in:
Patrick Steele-Idem 2016-11-02 20:23:42 -06:00
parent 35842ba70d
commit 32bacb7002
139 changed files with 335 additions and 789 deletions

View File

@ -9,4 +9,6 @@
.DS_Store
.vscode
coverage
.nvmrc
.nvmrc
/benchmark
/.cache

20
benchmark/patch-module.js Normal file
View File

@ -0,0 +1,20 @@
var nodePath = require('path');
var Module = require('module').Module;
var oldResolveFilename = Module._resolveFilename;
var rootDir = nodePath.join(__dirname, '../');
Module._resolveFilename = function(request, parent, isMain) {
if (request.charAt(0) !== '.') {
var firstSlash = request.indexOf('/');
var targetPackageName = firstSlash === -1 ? request : request.substring(0, firstSlash);
if (targetPackageName === 'marko') {
request = request.substring('marko'.length);
request = rootDir + request;
}
}
return oldResolveFilename.call(this, request, parent, isMain);
};

View File

@ -1,4 +1,4 @@
window.registerBenchmark('create', function(app) {
module.exports = function(app) {
var Suite = window.Benchmark.Suite;
var names = [
@ -53,4 +53,4 @@ window.registerBenchmark('create', function(app) {
return promiseChain;
};
});
};

View File

@ -1,6 +1,6 @@
window.registerBenchmark('walk', function(app) {
module.exports = function(app) {
var Suite = window.Benchmark.Suite;
var MarkoVDOM = window.MarkoVDOM;
var MarkoVDOM = app.vdom;
var suite = new Suite('walk');
@ -91,4 +91,4 @@ window.registerBenchmark('walk', function(app) {
return function() {
return app.runSuite(suite);
};
});
};

View File

@ -0,0 +1,11 @@
{
"dependencies": [
{
"type": "js",
"url": "https://unpkg.com/react@15.3.1/dist/react.min.js"
},
"lodash/lodash.js",
"benchmark/benchmark.js",
"require-run: ./client.js"
]
}

122
benchmark/vdom/client.js Normal file
View File

@ -0,0 +1,122 @@
var HTMLElement = require('../../runtime/vdom/HTMLElement');
var Text = require('../../runtime/vdom/Text');
var Comment = require('../../runtime/vdom/Comment');
var DocumentFragment = require('../../runtime/vdom/DocumentFragment');
var resultsEl = document.getElementById('results');
var running = false;
var benchmarks = {};
function loadScript(path) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = path;
script.onload = function () {
resolve();
};
script.onerror = function(e) {
reject(e);
};
document.head.appendChild(script); //or something of the likes
});
}
function loadScripts(paths) {
return Promise.all(paths.map(function(path) {
return loadScript(path);
}));
}
function runSuite(suite) {
return new Promise(function(resolve, reject) {
if (running) {
return;
}
running = true;
suite
.on('start', function(event) {
resultsEl.innerHTML += 'Running "' + suite.name + '"...\n';
})
.on('cycle', function(event) {
resultsEl.innerHTML += String(event.target) + '\n';
})
.on('complete', function() {
resultsEl.innerHTML += 'Fastest is ' + this.filter('fastest').map('name') + '\n\n--------------\n\n';
running = false;
suite.off('start cycle complete');
resolve();
})
.on('error', function(e) {
running = false;
suite.off('start cycle complete error');
reject(e.target.error);
})
// run async
.run({ 'async': true });
});
}
var vdom = window.MarkoVDOM = {
virtualize: require('../../runtime/vdom/virtualize'),
createElement: function(tagName, attrs, childCount, constId) {
return new HTMLElement(tagName, attrs, childCount, constId);
},
createText: function(value) {
return new Text(value);
},
createComment: function(value) {
return new Comment(value);
},
createDocumentFragment: function() {
return new DocumentFragment();
}
};
var app = {
loadScript,
loadScripts,
runSuite,
vdom
};
function registerBenchmark(name, func) {
benchmarks[name] = func(app);
}
registerBenchmark('create', require('./benchmark-create'));
registerBenchmark('walk', require('./benchmark-walk'));
document.body.addEventListener('click', function(event) {
if (running) {
return;
}
var target = event.target;
var benchmarkName = target.getAttribute('data-benchmark');
if (benchmarkName) {
var oldButtonLabel = target.innerHTML;
target.innerHTML = oldButtonLabel + ' - running...';
resultsEl.innerHTML = '';
var benchmarkFunc = benchmarks[benchmarkName];
benchmarkFunc()
.then(function() {
target.innerHTML = oldButtonLabel;
resultsEl.innerHTML += '\nDONE!';
})
.catch(function(e) {
target.innerHTML = oldButtonLabel;
console.error(e);
resultsEl.innerHTML = e.toString();
});
}
});

View File

@ -1,8 +1,11 @@
<lasso-page package-path="./browser.json"/>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>marko-vdom benchmarks</title>
<lasso-head/>
</head>
<body>
<button type="button" data-benchmark="walk">
@ -90,12 +93,6 @@
</footer>
</div>
</section>
<script src="https://unpkg.com/react@15.3.1/dist/react.min.js"></script>
<script src="../dist/marko-vdom-umd.js"></script>
<script src="../node_modules/lodash/lodash.js"></script>
<script src="../node_modules/benchmark/benchmark.js"></script>
<script src="./app.js"></script>
<script src="./benchmark-walk.js"></script>
<script src="./benchmark-create.js"></script>
<lasso-body/>
</body>
</html>

View File

@ -0,0 +1,27 @@
function create(__markoHelpers) {
var __browser_json = require.resolve("./browser.json"),
marko_loadTag = __markoHelpers.t,
lasso_page_tag = marko_loadTag(require("lasso/taglib/page-tag")),
lasso_head_tag = marko_loadTag(require("lasso/taglib/head-tag")),
lasso_body_tag = marko_loadTag(require("lasso/taglib/body-tag"));
return function render(data, out) {
lasso_page_tag({
packagePath: __browser_json,
dirname: __dirname,
filename: __filename
}, out);
out.w("<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><title>marko-vdom benchmarks</title>");
lasso_head_tag({}, out);
out.w("</head><body><button type=\"button\" data-benchmark=\"walk\">Run benchmark: walk</button><button type=\"button\" data-benchmark=\"create\">Run benchmark: create</button><pre id=\"results\" style=\"width: 100%; border: 1px solid black;\">\n </pre><section id=\"todoapp\" style=\"display: none\"><div id=\"w0\"><header id=\"header\"><h1>todos</h1><form data-w-onsubmit=\"handleFormSubmit|header\"><input id=\"new-todo\" placeholder=\"What needs to be done?\"></form></header><section id=\"main\"><input id=\"toggle-all\" type=\"checkbox\" data-w-onchange=\"handleToggleAllOnChange|main\"><label for=\"toggle-all\">Mark all as complete</label><ul id=\"todo-list\"><li id=\"main-todo-0\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-0\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-0\">Go to the grocery store</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-0\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-0-titleInput\" data-w-onchange=\"handleInputChange|main-todo-0\" data-w-onkeydown=\"handleInputKeyDown|main-todo-0\"></li><li class=\"completed\" id=\"main-todo-1\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" checked=\"\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-1\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-1\">Ship item</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-1\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-1-titleInput\" data-w-onchange=\"handleInputChange|main-todo-1\" data-w-onkeydown=\"handleInputKeyDown|main-todo-1\"></li><li id=\"main-todo-2\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-2\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-2\">Respond to email</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-2\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-2-titleInput\" data-w-onchange=\"handleInputChange|main-todo-2\" data-w-onkeydown=\"handleInputKeyDown|main-todo-2\"></li><li id=\"main-todo-3\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-3\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-3\">Foo</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-3\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-3-titleInput\" data-w-onchange=\"handleInputChange|main-todo-3\" data-w-onkeydown=\"handleInputKeyDown|main-todo-3\"></li><li id=\"main-todo-4\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-4\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-4\">Bar</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-4\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-4-titleInput\" data-w-onchange=\"handleInputChange|main-todo-4\" data-w-onkeydown=\"handleInputKeyDown|main-todo-4\"></li><li id=\"main-todo-5\"><div class=\"view\"><input class=\"toggle\" type=\"checkbox\" aria-label=\"Toggle todo completed\" data-w-onchange=\"handleCheckboxChange|main-todo-5\"><label data-w-ondblclick=\"handleLabelDblClick|main-todo-5\">Baz</label><button class=\"destroy\" aria-label=\"Delete todo\" data-w-onclick=\"handleDestroyClick|main-todo-5\"></button></div><input title=\"Enter the new todo title\" type=\"text\" class=\"edit\" id=\"main-todo-5-titleInput\" data-w-onchange=\"handleInputChange|main-todo-5\" data-w-onkeydown=\"handleInputKeyDown|main-todo-5\"></li></ul></section><footer id=\"footer\"><span id=\"todo-count\"><strong>5</strong> items left</span><ul id=\"filters\"><li><a href=\"#/\" class=\"selected\" data-w-onclick=\"handleAllFilterClick|footer\">All</a></li><li><a href=\"#/active\" data-w-onclick=\"handleActiveFilterClick|footer\">Active</a></li><li><a href=\"#/completed\" data-w-onclick=\"handleCompletedFilterClick|footer\">Completed</a></li></ul><button id=\"clear-completed\" data-w-onclick=\"handleClearCompletedClick|footer\">Clear completed (1)</button></footer></div></section>");
lasso_body_tag({}, out);
out.w("</body></html>");
};
}
module.exports = require("marko/html").c(__filename, create);

35
benchmark/vdom/run.js Normal file
View File

@ -0,0 +1,35 @@
require('../patch-module');
require('marko/node-require');
require('marko/express');
require('lasso').configure({
outputDir: __dirname + '/static',
bundlingEnabled: false
});
var express = require('express');
var app = express();
var serveStatic = require('serve-static');
require('./codegen-create/run');
var template = require('./index.marko');
app.use('/codegen-create', serveStatic(__dirname + '/codegen-create'));
app.use(require('lasso/middleware').serveStatic());
app.get('/', function(req, res) {
res.marko(template);
});
app.listen(8080, function(err) {
if (err) {
throw err;
}
console.log('Server ready:\nhttp://localhost:8080');
});

13
marko-vdom/.gitignore vendored
View File

@ -1,13 +0,0 @@
/work
/build
/.idea/
/npm-debug.log
/node_modules
/*.sublime-workspace
*.orig
.DS_Store
.vscode
coverage
.nvmrc
~*
/dist

View File

@ -1,40 +0,0 @@
{
"predef": [
],
"globals": {
"define": true,
"require": true
},
"node" : true,
"esnext" : true,
"browser" : false,
"boss" : false,
"curly": false,
"debug": false,
"devel": false,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": true,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": true,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": false,
"white": false,
"eqeqeq": false,
"latedef": false,
"unused": "vars",
"jquery": true,
"strict": false,
"eqnull": true
}

View File

@ -1,13 +0,0 @@
/work
/build
/.idea/
/npm-debug.log
/node_modules
/*.sublime-workspace
*.orig
.DS_Store
.vscode
coverage
.nvmrc
~*
/test

View File

@ -1,8 +0,0 @@
sudo: false
node_js:
- "4"
- "5"
- "6"
language: node_js
script: "npm run test-coverage"
after_success: "npm run coveralls"

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) Patrick Steele-Idem <pnidem@gmail.com> (psteeleidem.com)
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:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
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.

View File

@ -1,320 +0,0 @@
marko-vdom
==========
This module provides an optimized virtual DOM implementation where each virtual DOM node is API compatible with real DOM nodes for the minimal subset that is required to support DOM diffing/patching using [morphdom](https://github.com/patrick-steele-idem/morphdom).
[![Build Status](https://travis-ci.org/marko-js/marko-vdom.svg?branch=master)](https://travis-ci.org/marko-js/marko-vdom)
[![Coverage Status](https://coveralls.io/repos/github/marko-js/marko-vdom/badge.svg?branch=master)](https://coveralls.io/github/marko-js/marko-vdom?branch=master)
[![NPM](https://img.shields.io/npm/v/marko-vdom.svg)](https://www.npmjs.com/package/marko-vdom)
# Overview
Each virtual DOM node supports the following properties and methods required by [morphdom](https://github.com/patrick-steele-idem/morphdom):
- `node.firstChild`
- `node.nextSibling`
- `node.nodeType`
- `node.nodeName`
- `node.namespaceURI`
- `node.nodeValue`
- `node.attributes` <sup><a href="#attributes">[1]</a><sup>
- `node.value`
- `node.selected`
- `node.disabled`
- `node.actualize(document)` <sup><a href="#actualize">[2]</a><sup>
- `node.hasAttributeNS(namespaceURI, name)`
- `node.isSameNode(anotherNode)` <sup><a href="#isSameNode">[3]</a><sup>
- `node.assignAttributes(targetNode)` <sup><a href="#assignAttributes">[4]</a><sup>
NOTES:
1. <a name="attributes"></a>Unlike with real DOM nodes, `node.attributes` can either be an `Array` of [Attr](https://developer.mozilla.org/en-US/docs/Web/API/Attr) objects or an `Object` (where each property represents an attribute. e.g., `{ "class": "foo", "id": "bar" }`)
2. <a name="actualize"></a>In addition to the standard DOM node methods and properties, a virtual DOM node must also provide a `node.actualize(document)` method. The `node.actualize(document)` will be called when the virtual DOM node needs to be upgraded to a real DOM node so that it can be moved into the real DOM.
3. <a name="isSameNode"></a>A virtual DOM node may choose to implement `isSameNode(anotherNode)` to short-circuit diffing/patching a particular DOM subtree by treating two nodes as the "same"
4. <a name="assignAttributes"></a>A virtual DOM node may choose to implement the non-standard `assignAttributes(targetNode)` to optimize copying the attributes from the virtual DOM node to the target DOM node
`marko-vdom` is namespace aware and will work correctly with SVG and MathML elements.
While `marko-vdom` exposes an API that can be used directly, the terse API is designed to be used with a compiler that generates JavaScript code.
# Usage
## Create an element with a fixed number of attributes and a fixed number of children
```javascript
var createElement = require('marko-vdom').createElement;
createElement('div', { class: 'foo', onclick: 'doSomething()' }, 2 /* childCount */)
.e('span', null, 1)
.e('b', null, 1)
.t('Hello World!')
.e('a', { href: 'http://ebay.com' }, 1)
.t('eBay')
```
The above code will generate a virtual DOM tree that mirrors the following:
```html
<div class="foo" onclick="doSomething()">
<span>
<b>Hello World!</b>
</span>
<a href="http://ebay.com">eBay</a>
</div>
```
## Dynamic HTML with a unknown number of children
```javascript
var createElement = require('marko-vdom').createElement;
var el = createElement('div', { class: 'foo' });
el.appendChild(createElement('span', { class: 'bar' }));
```
The above code will generate a virtual DOM tree that mirrors the following:
```html
<div class="foo">
<span class="bar"></span>
</div>
```
## Static subtree
```javascript
var createElement = require('marko-vdom').createElement;
var staticLink = createElement('a', { href: 'http://ebay.com' }, 1 /* childCount */, 'abc123' /* key */)
.t('eBay')
function render() {
createElement('div', null, 1 /* childCount */)
.n(staticLink);
}
```
The above code will generate a virtual DOM tree that, when converted to a real DOM, will be the following:
```html
<div>
<a href="http://ebay.com" data-marko-same-id="abc123">
eBay
</a>
</div>
```
For the static link, both the virtual DOM node and the real DOM node will be marked with an "id" that identifies the two nodes as the "same" node in order to short-circuit DOM/diffing patching. That is:
```javascript
var realStaticLink = staticLink.actualize(document);
console.log(staticLink.isSameNode(realStaticLink)); //Output: true
```
## Document fragments
Document fragments are containers for child nodes that can be appended as children nodes, but the actual `DocumentFragment` node is never directly visited when walking the DOM using `node.firstChild` and `node.nextSibling`. Instead, the children (if any) of a `DocumentFragment` node are treated as direct children of the parent of the `DocumentFragment` node. A `DocumentFragment` node can be modified with new children even after it has been inserted into the DOM.
```javascript
var createElement = require('marko-vdom').createElement;
var div = createElement('div');
documentFragment.appendChild(createElement('div'));
var documentFragment = div.appendDocumentFragment();
documentFragment.appendChild(createElement('span', { class: 'foo' }));
documentFragment.appendChild(createElement('span', { class: 'bar' }));
/*
Output DOM:
<div>
<span class="foo"></span>
<span class="bar"></span>
</div>
*/
```
<a name="benchmarks"></a>
# Benchmarks
This library includes some benchmarks to compare performance with the real DOM (and React). To run the benchmarks:
```
npm run benchmark
```
This will open a web page in your browser that you can use to run a variety of benchmarks.
We are interested in the following performance characteristics:
- <b>Creation time</b> - the time it takes to construct a [virtual] DOM tree
- <b>Walk time</b> - the time it takes to walk a [virtual] DOM tree using `firstChild` and `nextSibilng`
We encourage you to run the benchmarks on your machine and in various browsers. If you see any problems with the benchmarks, or if you would like clarifying information please open a Github issue.
Please see [Benchmark Results](./docs/benchmark-results.md) for more detailed numbers.
# API
## `marko-vdom`
### Methods
#### `createElement(tagName, attrCount, childCount, key)`
Returns a new [HTMLElement](#HTMLElement).
#### `createText(value)`
Returns a new [Text](#Text).
#### `createComment(value)`
Returns a new [Comment](#Comment).
#### `createAttributes(attrCount)`
Returns a new [AttributeCollection](#AttributeCollection).
#### `createDocumentFragment()`
Returns a new [DocumentFragment](#DocumentFragment)
---------------
<a name="AttributeCollection"></a>
## `AttributeCollection`
### Methods
<a name="AttributeCollection-a"></a>
#### `a(name, value)`
<a name="AttributeCollection-as"></a>
#### `as(attributes)`
---------------
<a name="Comment"></a>
## `Comment`
---------------
<a name="DocumentFragment"></a>
## `DocumentFragment`
---------------
<a name="HTMLElement"></a>
## `HTMLElement`
### Constructors
#### `HTMLElement(tagName, attrCount, childCount, key)`
Parameters:
- __tagName__ - The tag name for the new HTML element (`String`)
- __attrCount__ - The number of attributes (if known) (an integer, `null` or `undefined`)
- __childCount__ - The number of child nodes (if known) (an integer, `null` or `undefined`)
- __key__ - A key for static nodes to use for `isSameNode()` checks
#### `HTMLElement(htmlElement)`
Used to do a shallow clone of another `HTMLElement`
### Properties
#### `nodeType`
Always set to `1`
### Methods
#### `a(name, value)` : [`Node`](#Node)
See [AttributeCollection#a](#AttributeCollection-a)
#### `actualize(document)` : `HTMLElement`
Converts the virtual `HTMLElement` tree to a real `HTMLElement` tree using the provided `document`.
#### `as(name, value)` : [`Node`](#Node)
See [AttributeCollection#a](#AttributeCollection-as)
#### `appendDocumentFragment()` : [`DocumentFragment`](#DocumentFragment)
See [Node#appendDocumentFragment](#Node-appendDocumentFragment)
#### `c(value)` : [`Node`](#Node)
Shorthand method for creating a [Comment](#Comment) node and appending it as a child.
#### `cloneNode()` : [`HTMLElement`](#HTMLElement)
Performs a shallow clone of the node (`nextSibling` and `parentNode` will be `undefined` since a cloned node will always start out as detached)
#### `e(tagName, attrCount, childCount, key)` : [`Node`](#Node)
Shorthand method for creating an [HTMLElement](#HTMLElement) node and appending it as a child.
#### `hasAttributeNS(namespaceURI, name)` : `boolean`
#### `isSameNode(otherNode)` : `boolean`
Called by `morphdom` to determine if the target `HTMLElement` (either virtual or real) node is the same as the current node. The `key` passed in to the constructor is used to do determine if the other node is the "same" node. If the other node is a real DOM node then the key is pulled from the `data-markokey` attribute.
#### `n(node)` : [`Node`](#Node)
Shorthand method for appending a node as a child. The provided is automatically cloned (using a shallow clone) since it is assumed that this method will be called to append a static/memoized DOM node and the original DOM node should not be modified.
#### `t(value)` : [`Node`](#Node)
Shorthand method for creating a [Text](#Text) node and appending it as a child.
---------------
<a name="Node"></a>
## `Node`
### Properties
#### `firstChild` : [`Node`](#Node)
Returns the first child node
#### `nextSibling` : [`Node`](#Node)
Returns the next sibling node
### Methods
<a name="Node-appendDocumentFragment"></a>
#### `appendDocumentFragment()` : [`DocumentFragment`](#DocumentFragment)
Creates and appends a new [DocumentFragment](#DocumentFragment) node and appends it as a child and the newly created [DocumentFragment](#DocumentFragment) node is returned.
#### `removeChildren()`
Clears out all child nodes
---------------
<a name="Text"></a>
## `Text`

View File

@ -1,98 +0,0 @@
(function() {
var resultsEl = document.getElementById('results');
var running = false;
var benchmarks = {};
function loadScript(path) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = path;
script.onload = function () {
resolve();
};
script.onerror = function(e) {
reject(e);
};
document.head.appendChild(script); //or something of the likes
});
}
function loadScripts(paths) {
return Promise.all(paths.map(function(path) {
return loadScript(path);
}));
}
function runSuite(suite) {
return new Promise(function(resolve, reject) {
if (running) {
return;
}
running = true;
suite
.on('start', function(event) {
resultsEl.innerHTML += 'Running "' + suite.name + '"...\n';
})
.on('cycle', function(event) {
resultsEl.innerHTML += String(event.target) + '\n';
})
.on('complete', function() {
resultsEl.innerHTML += 'Fastest is ' + this.filter('fastest').map('name') + '\n\n--------------\n\n';
running = false;
suite.off('start cycle complete');
resolve();
})
.on('error', function(e) {
running = false;
suite.off('start cycle complete error');
reject(e.target.error);
})
// run async
.run({ 'async': true });
});
}
var app = {
loadScript,
loadScripts,
runSuite
};
window.registerBenchmark = function(name, func) {
benchmarks[name] = func(app);
};
document.body.addEventListener('click', function(event) {
if (running) {
return;
}
var target = event.target;
var benchmarkName = target.getAttribute('data-benchmark');
if (benchmarkName) {
var oldButtonLabel = target.innerHTML;
target.innerHTML = oldButtonLabel + ' - running...';
resultsEl.innerHTML = '';
var benchmarkFunc = benchmarks[benchmarkName];
benchmarkFunc()
.then(function() {
target.innerHTML = oldButtonLabel;
resultsEl.innerHTML += '\nDONE!';
})
.catch(function(e) {
target.innerHTML = oldButtonLabel;
console.error(e);
resultsEl.innerHTML = e.toString();
});
}
});
}());

View File

@ -1,5 +0,0 @@
var open = require('open');
var path = require('path');
require('./codegen-create/run');
open(path.join(__dirname, 'index.html'));

View File

@ -1,40 +0,0 @@
{
"name": "marko-vdom",
"version": "1.0.1",
"description": "Virtual DOM implementation for use with Marko and/or morphdom",
"main": "src/index.js",
"scripts": {
"test": "npm run mocha && npm run jshint",
"mocha": "mocha --ui bdd --reporter spec ./test/",
"mocha-debug": "node-debug _mocha ./test/",
"jshint": "jshint src/",
"benchmark": "node benchmark/run.js",
"test-coverage": "istanbul cover _mocha -- --ui bdd --reporter spec ./test/ && npm run jshint",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"dist": "mkdir -p dist && browserify src/all.js > dist/marko-vdom-umd.js --standalone MarkoVDOM"
},
"keywords": [
"vdom",
"dom",
"marko",
"morphdom",
"virtual",
"virtual-dom"
],
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
"license": "MIT",
"devDependencies": {
"benchmark": "^2.1.1",
"chai": "^3.5.0",
"coveralls": "^2.11.12",
"istanbul": "^0.4.5",
"jsdom": "^9.4.2",
"jshint": "^2.9.3",
"lodash": "^4.15.0",
"mocha": "^3.0.2",
"open": "0.0.5"
},
"dependencies": {
"raptor-util": "^2.0.0"
}
}

View File

@ -1,2 +0,0 @@
module.exports = require('./index');
module.exports.virtualize = require('./virtualize');

View File

@ -1,27 +0,0 @@
'use strict';
var HTMLElement = require('./HTMLElement');
var Text = require('./Text');
var Comment = require('./Comment');
var DocumentFragment = require('./DocumentFragment');
function createElement(tagName, attrs, childCount, key) {
return new HTMLElement(tagName, attrs, childCount, key);
}
function createText(value) {
return new Text(value);
}
function createComment(value) {
return new Comment(value);
}
function createDocumentFragment() {
return new DocumentFragment();
}
exports.createElement = createElement;
exports.createText = createText;
exports.createComment = createComment;
exports.createDocumentFragment = createDocumentFragment;

View File

@ -1,3 +0,0 @@
*-actual*
actual.*
actualized-expected.html

View File

@ -1,43 +0,0 @@
{
"predef": [
"it",
"xit",
"console",
"describe",
"xdescribe",
"beforeEach",
"before",
"after",
"waits",
"waitsFor",
"runs"
],
"node" : true,
"es5" : false,
"esnext": true,
"browser" : true,
"boss" : false,
"curly": false,
"debug": false,
"devel": false,
"eqeqeq": true,
"evil": true,
"forin": false,
"immed": true,
"laxbreak": false,
"newcap": true,
"noarg": true,
"noempty": false,
"nonew": true,
"nomen": false,
"onevar": false,
"plusplus": false,
"regexp": false,
"undef": true,
"sub": false,
"white": false,
"eqeqeq": false,
"latedef": false,
"unused": "vars",
"eqnull": true
}

View File

@ -1,27 +0,0 @@
var path = require('path');
var markoVDOM = require('../');
var fs = require('fs');
var toHTML = require('./util/toHTML');
var jsdom = require("jsdom").jsdom;
var document = jsdom('<html><body></body></html>');
describe('marko-vdom', () => {
require('./util/autotest').scanDir(
path.join(__dirname, 'autotests-create'),
function(dir, helpers, done) {
helpers.vdom = markoVDOM;
helpers.document = document;
var mainPath = path.join(dir, 'index.js');
if (fs.existsSync(mainPath)) {
var main = require(mainPath);
var rootNode = main(helpers);
var rootNodeHTML = rootNode != null ? toHTML(rootNode) : '(null)';
helpers.compare(rootNodeHTML, '.html');
}
done();
}
);
});

View File

@ -1,75 +0,0 @@
var fs = require('fs');
var enabledTest = process.env.TEST;
var path = require('path');
var assert = require('assert');
function compareHelper(dir, actual, prefix, suffix) {
var actualPath = path.join(dir, prefix + 'actual' + suffix);
var expectedPath = path.join(dir, prefix + 'expected' + suffix);
var isObject = typeof actual === 'string' ? false : true;
var actualString = isObject ? JSON.stringify(actual, null, 4) : actual;
fs.writeFileSync(actualPath, actualString, { encoding: 'utf8' });
var expectedString;
try {
expectedString = fs.readFileSync(expectedPath, { encoding: 'utf8' });
} catch(e) {
expectedString = isObject ? '"TBD"' : 'TBD';
fs.writeFileSync(expectedPath, expectedString, {encoding: 'utf8'});
}
var expected = isObject ? JSON.parse(expectedString) : expectedString;
assert.deepEqual(actual, expected);
}
function autoTest(name, dir, run, options, done) {
options = options || {};
var helpers = {
compare(actual, prefix, suffix) {
if (typeof prefix === 'object') {
var options = prefix;
suffix = options.suffix;
prefix = options.prefix;
} else if (arguments.length === 2) {
suffix = prefix;
prefix = null;
}
compareHelper(dir, actual, prefix || '', suffix || '');
},
readFileSync(relPath) {
var fullPath = path.resolve(dir, relPath);
return fs.readFileSync(fullPath, { encoding: 'utf8' });
}
};
run(dir, helpers, done);
}
exports.scanDir = function(autoTestDir, run, options) {
describe('autotest', function() {
fs.readdirSync(autoTestDir)
.forEach(function(name) {
if (name.charAt(0) === '.') {
return;
}
var itFunc = it;
if (enabledTest && name === enabledTest) {
itFunc = it.only;
}
var dir = path.join(autoTestDir, name);
itFunc(`[${name}] `, function(done) {
autoTest(name, dir, run, options, done);
});
});
});
};

View File

@ -1 +0,0 @@
module.exports = require('./src/virtualize');

View File

@ -76,7 +76,7 @@ function getLoadedTemplate(path) {
return cached && cached.exports.render ? cached.exports : undefined;
}
exports.install = function(options) {
function install(options) {
options = options || {};
var compilerOptions = options.compilerOptions;
@ -120,4 +120,8 @@ exports.install = function(options) {
// source code that is being loaded. This allows stack traces to match up.
module._compile(compiledSrc, targetFile);
};
};
}
install();
exports.install = install;

View File

@ -5,7 +5,15 @@
"templating",
"template",
"async",
"streaming"
"streaming",
"widgets",
"components",
"ui",
"vdom",
"dom",
"morphdom",
"virtual",
"virtual-dom"
],
"repository": {
"type": "git",
@ -37,7 +45,6 @@
"dependencies": {
"argly": "^1.0.0",
"app-module-path": "^1.0.5",
"async-vdom-builder": "^1.0.0",
"browser-refresh-client": "^1.0.0",
"char-props": "~0.1.5",
"deresolve": "^1.0.0",
@ -69,6 +76,7 @@
},
"devDependencies": {
"async": "^0.9.0",
"benchmark": "^2.1.1",
"bluebird": "^2.9.30",
"browser-refresh": "^1.6.0",
"browser-refresh-taglib": "^1.1.0",
@ -90,6 +98,7 @@
"phantomjs-prebuilt": "^2.1.13",
"request": "^2.72.0",
"require-self-ref": "^2.0.1",
"serve-static": "^1.11.1",
"through": "^2.3.4",
"through2": "^2.0.1"
},

View File

@ -1,11 +1,12 @@
var EventEmitter = require('events').EventEmitter;
var dom = require('marko-dom');
var markoVdom = require('marko-vdom');
var createElement = markoVdom.createElement;
var createDocumentFragment = markoVdom.createDocumentFragment;
var createComment = markoVdom.createComment;
var createText = markoVdom.createText;
var virtualize = require('marko-vdom/virtualize');
var HTMLElement = require('./HTMLElement');
var DocumentFragment = require('./DocumentFragment');
var Comment = require('./Comment');
var Text = require('./Text');
var virtualize = require('./virtualize');
var specialHtmlRegexp = /[&<]/;
var defaultDocument = typeof document != 'undefined' && document;
@ -36,7 +37,7 @@ function State(tree) {
function AsyncVDOMBuilder(globalData, parentNode, state) {
if (!parentNode) {
parentNode = createDocumentFragment();
parentNode = new DocumentFragment();
}
if (state) {
@ -59,7 +60,7 @@ var proto = AsyncVDOMBuilder.prototype = {
isAsyncVDOMBuilder: true,
element: function(name, attrs, childCount) {
var element = createElement(name, attrs, childCount);
var element = new HTMLElement(name, attrs, childCount);
var parent = this._parent;
@ -91,14 +92,14 @@ var proto = AsyncVDOMBuilder.prototype = {
if (lastChild && lastChild.nodeType === 3) {
lastChild.nodeValue += text;
} else {
parent.appendChild(createText(text));
parent.appendChild(new Text(text));
}
}
return this;
},
comment: function(comment) {
return this.node(createComment(comment));
return this.node(new Comment(comment));
},
html: function(html) {
@ -125,7 +126,7 @@ var proto = AsyncVDOMBuilder.prototype = {
var curChild = container.firstChild;
if (curChild) {
vdomFragment = createDocumentFragment();
vdomFragment = new DocumentFragment();
while(curChild) {
vdomFragment.appendChild(virtualize(curChild));
curChild = curChild.nextSibling;
@ -141,7 +142,7 @@ var proto = AsyncVDOMBuilder.prototype = {
},
beginElement: function(name, attrs) {
var element = createElement(name, attrs);
var element = new HTMLElement(name, attrs);
var parent = this._parent;
if (parent) {
parent.appendChild(element);

View File

@ -16,15 +16,20 @@
'use strict';
var markoVDOM = require('marko-vdom');
var HTMLElement = require('./HTMLElement');
var Text = require('./Text');
var commonHelpers = require('../helpers');
var extend = require('raptor-util/extend');
var classList = commonHelpers.cl;
module.exports = extend({
e: markoVDOM.createElement,
t: markoVDOM.createText,
e: function(tagName, attrs, childCount, constId) {
return new HTMLElement(tagName, attrs, childCount, constId);
},
t: function(value) {
return new Text(value);
},
const: function(id) {
var i=0;
return function() {

View File

@ -1,9 +1,7 @@
var markoVDOM = require('./');
var createElement = markoVDOM.createElement;
var createText = markoVDOM.createText;
var createComment = markoVDOM.createComment;
var createDocumentFragment = markoVDOM.createDocumentFragment;
var HTMLElement = require('./HTMLElement');
var DocumentFragment = require('./DocumentFragment');
var Comment = require('./Comment');
var Text = require('./Text');
function virtualizeChildNodes(node, vdomParent) {
var curChild = node.firstChild;
@ -40,7 +38,7 @@ function virtualize(node) {
}
}
var vdomEL = createElement(node.nodeName, attrs, childCount);
var vdomEL = new HTMLElement(node.nodeName, attrs, childCount);
if (vdomEL._isTextArea) {
vdomEL.value = node.value;
@ -50,11 +48,11 @@ function virtualize(node) {
return vdomEL;
} else if (node.nodeType === 3) { // Text node
return createText(node.nodeValue);
return new Text(node.nodeValue);
} else if (node.nodeType === 8) { // Text node
return createComment(node.nodeValue);
return new Comment(node.nodeValue);
} else if (node.nodeType === 11) { // DocumentFragment node
var vdomDocFragment = createDocumentFragment();
var vdomDocFragment = new DocumentFragment();
virtualizeChildNodes(node, vdomDocFragment);
}
}

8
test/.gitignore vendored
View File

@ -2,8 +2,10 @@
/scratch.js
*.generated.js
actual.js
actual.html
*.actual.js
*.actual.html
/generated
/generated
*-actual*
actual.*
actualized-expected.html

View File

@ -52,7 +52,11 @@ function autoTest(name, dir, run, options, done) {
var helpers = {
compare(actual, prefix, suffix) {
if (arguments.length === 2) {
if (typeof prefix === 'object') {
var options = prefix;
prefix = options.prefix;
suffix = options.suffix;
} else if (arguments.length === 2) {
suffix = prefix;
prefix = null;
}

Some files were not shown because too many files have changed in this diff Show More