Skip to content

Commit

Permalink
refactor virtual hyperscript
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt-Esch committed Dec 21, 2014
1 parent 79b7040 commit 900d332
Show file tree
Hide file tree
Showing 13 changed files with 345 additions and 346 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "virtual-dom",
"version": "0.0.24",
"description": "A batched diff-based DOM rendering strategy",
"keywords": [],
"keywords": ["virtual", "dom", "vdom", "vtree", "diff", "patch", "browser"],
"author": "Matt-Esch <matt@mattesch.info>",
"repository": "git://github.com/Matt-Esch/virtual-dom.git",
"main": "index",
Expand All @@ -18,8 +18,8 @@
},
"dependencies": {
"browser-split": "0.0.1",
"data-set": "^3.1.0",
"error": "^4.3.0",
"ev-store": "^7.0.0",
"global": "^4.3.0",
"is-object": "^1.0.1",
"next-tick": "^0.2.2",
Expand Down
20 changes: 5 additions & 15 deletions virtual-hyperscript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,22 @@ var tree = h('div.foo#some-id', [

See [hyperscript](https://github.com/dominictarr/hyperscript) which has the
same interface.

Except `virtual-hyperscript` returns a virtual DOM tree instead of a DOM
element.

### `h(selector, properties, children)`

`h()` takes a selector, an optional properties object and an
optional array of children or a child that is a string.

If you pass it a selector like `span.foo.bar#some-id` it will
parse the selector and change the `id` and `className`
properties of the `properties` object.

If you pass it an array of `children` it will have child
nodes, normally ou want to create children with `h()`.

If you pass it a string it will create an array containing
a single child node that is a text element.

Expand All @@ -64,17 +64,6 @@ If you call `h` with `h('div', { namespace: "http://www.w3.org/2000/svg" })`
`namespace` is not a normal DOM property, instead it will
cause `vdom` to create a DOM element with a namespace.

#### `data-*`

If you call `h` with `h('div', { data-foo: "bar" })` it will
set `data-foo` to be a `VHook` that set's a `DataSet` property
named `foo` with the value `"bar"` on the actual dom element.

It will not set a property `data-foo` on the dom element.

This means that somewhere else in your code you can use
`DataSet(elem).foo` to read this property.

#### `ev-*`

If you call `h` with `h('div', { ev-click: function (ev) { } })` it
Expand All @@ -92,6 +81,7 @@ This means that `dom-delegator` will recognise the event handler
## Contributors

- Raynos
- Matt Esch

## MIT Licenced

Expand Down
9 changes: 6 additions & 3 deletions virtual-hyperscript/hooks/attribute-hook.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
'use strict';

module.exports = AttributeHook;

function AttributeHook(value) {
function AttributeHook(namespace, value) {
if (!(this instanceof AttributeHook)) {
return new AttributeHook(value);
}

this.namespace = namespace;
this.value = value;
}

Expand All @@ -13,5 +16,5 @@ AttributeHook.prototype.hook = function (node, prop, prev) {
return;
}

node.setAttributeNS(null, prop, this.value)
}
node.setAttributeNS(this.namespace, prop, this.value);
};
18 changes: 0 additions & 18 deletions virtual-hyperscript/hooks/data-set-hook.js

This file was deleted.

28 changes: 15 additions & 13 deletions virtual-hyperscript/hooks/ev-hook.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
var DataSet = require("data-set")
'use strict';

module.exports = DataSetHook;
var EvStore = require('ev-store');

function DataSetHook(value) {
if (!(this instanceof DataSetHook)) {
return new DataSetHook(value);
module.exports = EvHook;

function EvHook(value) {
if (!(this instanceof EvHook)) {
return new EvHook(value);
}

this.value = value;
}

DataSetHook.prototype.hook = function (node, propertyName) {
var ds = DataSet(node)
var propName = propertyName.substr(3)
EvHook.prototype.hook = function (node, propertyName) {
var es = EvStore(node);
var propName = propertyName.substr(3);

ds[propName] = this.value;
es[propName] = this.value;
};

DataSetHook.prototype.unhook = function(node, propertyName) {
var ds = DataSet(node);
EvHook.prototype.unhook = function(node, propertyName) {
var es = EvStore(node);
var propName = propertyName.substr(3);

ds[propName] = undefined;
}
es[propName] = undefined;
};
16 changes: 9 additions & 7 deletions virtual-hyperscript/hooks/focus-hook.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
var document = require("global/document")
var nextTick = require("next-tick")
'use strict';

module.exports = MutableFocusHook
var document = require("global/document");
var nextTick = require("next-tick");

module.exports = MutableFocusHook;

function MutableFocusHook() {
if (!(this instanceof MutableFocusHook)) {
return new MutableFocusHook()
return new MutableFocusHook();
}
}

MutableFocusHook.prototype.hook = function (node, property) {
MutableFocusHook.prototype.hook = function (node) {
nextTick(function () {
if (document.activeElement !== node) {
node.focus();
}
})
}
});
};
2 changes: 2 additions & 0 deletions virtual-hyperscript/hooks/soft-set-hook.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use strict';

module.exports = SoftSetHook;

function SoftSetHook(value) {
Expand Down
133 changes: 64 additions & 69 deletions virtual-hyperscript/index.js
Original file line number Diff line number Diff line change
@@ -1,130 +1,125 @@
var TypedError = require("error/typed")

var VNode = require("../vnode/vnode.js")
var VText = require("../vnode/vtext.js")
var isVNode = require("../vnode/is-vnode")
var isVText = require("../vnode/is-vtext")
var isWidget = require("../vnode/is-widget")
var isHook = require("../vnode/is-vhook")
var isVThunk = require("../vnode/is-thunk")

var parseTag = require("./parse-tag.js")
var softSetHook = require("./hooks/soft-set-hook.js")
var dataSetHook = require("./hooks/data-set-hook.js")
var evHook = require("./hooks/ev-hook.js")

var UnexpectedVirtualElement = TypedError({
type: "virtual-hyperscript.unexpected.virtual-element",
message: "Unexpected virtual child passed to h().\n" +
"Expected a VNode / Vthunk / VWidget / string but:\n" +
"got a {foreignObjectStr}.\n" +
"The parent vnode is {parentVnodeStr}.\n" +
"Suggested fix: change your `h(..., [ ... ])` callsite.",
foreignObjectStr: null,
parentVnodeStr: null,
foreignObject: null,
parentVnode: null
})

module.exports = h
'use strict';

var isArray = require('x-is-array');

var VNode = require('../vnode/vnode.js');
var VText = require('../vnode/vtext.js');
var isVNode = require('../vnode/is-vnode');
var isVText = require('../vnode/is-vtext');
var isWidget = require('../vnode/is-widget');
var isHook = require('../vnode/is-vhook');
var isVThunk = require('../vnode/is-thunk');

var parseTag = require('./parse-tag.js');
var softSetHook = require('./hooks/soft-set-hook.js');
var evHook = require('./hooks/ev-hook.js');

module.exports = h;

function h(tagName, properties, children) {
var childNodes = []
var tag, props, key, namespace
var childNodes = [];
var tag, props, key, namespace;

if (!children && isChildren(properties)) {
children = properties
props = {}
children = properties;
props = {};
}

props = props || properties || {}
tag = parseTag(tagName, props)
props = props || properties || {};
tag = parseTag(tagName, props);

// support keys
if (props.hasOwnProperty("key")) {
key = props.key
props.key = undefined
if (props.hasOwnProperty('key')) {
key = props.key;
props.key = undefined;
}

// support namespace
if (props.hasOwnProperty("namespace")) {
namespace = props.namespace
props.namespace = undefined
if (props.hasOwnProperty('namespace')) {
namespace = props.namespace;
props.namespace = undefined;
}

// fix cursor bug
if (tag === "INPUT" &&
if (tag === 'INPUT' &&
!props.namespace &&
props.hasOwnProperty("value") &&
props.hasOwnProperty('value') &&
props.value !== undefined &&
!isHook(props.value)
) {
props.value = softSetHook(props.value)
props.value = softSetHook(props.value);
}

transformProperties(props)
transformProperties(props);

if (children !== undefined && children !== null) {
addChild(children, childNodes, tag, props)
addChild(children, childNodes, tag, props);
}


var node = new VNode(tag, props, childNodes, key, namespace)
var node = new VNode(tag, props, childNodes, key, namespace);

return node
return node;
}

function addChild(c, childNodes, tag, props) {
if (typeof c === "string") {
childNodes.push(new VText(c))
if (typeof c === 'string') {
childNodes.push(new VText(c));
} else if (isChild(c)) {
childNodes.push(c)
} else if (Array.isArray(c)) {
childNodes.push(c);
} else if (isArray(c)) {
for (var i = 0; i < c.length; i++) {
addChild(c[i], childNodes, tag, props)
addChild(c[i], childNodes, tag, props);
}
} else if (c === null || c === undefined) {
return
return;
} else {
throw UnexpectedVirtualElement({
foreignObjectStr: JSON.stringify(c),
foreignObject: c,
parentVnodeStr: JSON.stringify({
tagName: tag,
properties: props
}),
parentVnode: {
tagName: tag,
properties: props
}
})
});
}
}

function transformProperties(props) {
for (var propName in props) {
if (props.hasOwnProperty(propName)) {
var value = props[propName]
var value = props[propName];

if (isHook(value)) {
continue
continue;
}

if (propName.substr(0, 5) === "data-") {
// add data-foo support
props[propName] = dataSetHook(value)
} else if (propName.substr(0, 3) === "ev-") {
if (propName.substr(0, 3) === 'ev-') {
// add ev-foo support
props[propName] = evHook(value)
props[propName] = evHook(value);
}
}
}
}

function isChild(x) {
return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x)
return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x);
}

function isChildren(x) {
return typeof x === "string" || Array.isArray(x) || isChild(x)
return typeof x === 'string' || isArray(x) || isChild(x);
}

function UnexpectedVirtualElement(data) {
var err = new Error();

err.type = 'virtual-hyperscript.unexpected.virtual-element';
err.message = 'Unexpected virtual child passed to h().\n' +
'Expected a VNode / Vthunk / VWidget / string but:\n' +
'got a {foreignObjectStr}.\n' +
'The parent vnode is {parentVnodeStr}.\n' +
'Suggested fix: change your `h(..., [ ... ])` callsite.';
err.foreignObject = data.foreignObject;
err.parentVnode = data.parentVnode;

return err;
}

0 comments on commit 900d332

Please sign in to comment.