components, angular dbmon
This commit is contained in:
parent
ba378d3652
commit
3282ef3f77
30 changed files with 1270 additions and 248 deletions
|
|
@ -1,3 +1,5 @@
|
|||
# Contributing
|
||||
|
||||
# FAQ
|
||||
|
||||
## How do I go about contributing ideas or new features?
|
||||
|
|
@ -16,11 +18,15 @@ Ideally, provide code to reproduce the issue (via jsfiddle, a gist, etc). Even b
|
|||
|
||||
Assuming you have forked this repo, you can open the `index.html` file in a module's `tests` folder and look at console output to see only tests for that module, or you can run `ospec/bin/ospec` from the command line to run all tests under a Node.js environment. Additionally, you can modify a test to use `o.only(description, test)` instead of `o(description, test)` if you wish to run only a specific test.
|
||||
|
||||
There is no need to `npm install` anything in order to run the test suite, however NodeJS is required to run the test suite from the command line.
|
||||
|
||||
|
||||
|
||||
## How do I build Mithril?
|
||||
|
||||
Run `node bundler/bundler.js` from the command line to generate the bundled file.
|
||||
If all you're trying to do is run examples in the codebase, you don't need to build Mithril, you can just open the various html files and things should just work.
|
||||
|
||||
To generate the bundled file, run `node bundler/bundler.js` from the command line. There is no need to `npm install` anything, but NodeJS is required to run the build script.
|
||||
|
||||
|
||||
|
||||
|
|
@ -56,7 +62,7 @@ In addition, ES6 features are usually less performant than equivalent ES5 code,
|
|||
|
||||
## Why doesn't the Mithril codebase use trailing semi-colons? Would a PR to add them be welcome?
|
||||
|
||||
I don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent. If you're not comfortable with ASI rules, bear in mind that this codebase is heavily optimized and large modifications will require an advanced level of javascript mastery.
|
||||
I don't use them. Adding them means the semi-colon usage in the codebase will eventually become inconsistent.
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script>
|
||||
|
|
|
|||
55
examples/dbmonster/angular/app.js
vendored
Normal file
55
examples/dbmonster/angular/app.js
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
var renderStage = 0
|
||||
perfMonitor.startFPSMonitor()
|
||||
perfMonitor.startMemMonitor()
|
||||
perfMonitor.initProfiler("render")
|
||||
|
||||
var AppComponent = ng.core.Component({selector: "my-app"})
|
||||
.View({
|
||||
directives: [ng.common.CORE_DIRECTIVES],
|
||||
template: "<div>" +
|
||||
"<table class='table table-striped latest-data'>" +
|
||||
"<tbody>" +
|
||||
"<tr *ngFor='let db of databases'>" +
|
||||
"<td class='dbname'>{{db.dbname}}</td>" +
|
||||
"<td class='query-count'>" +
|
||||
"<span [class]='db.lastSample.countClassName'>{{db.lastSample.nbQueries}}</span>" +
|
||||
"</td>" +
|
||||
"<td *ngFor='let q of db.lastSample.topFiveQueries' [class]='q.elapsedClassName'>" +
|
||||
"{{q.formatElapsed}}" +
|
||||
"<div class='popover left'>" +
|
||||
"<div class='popover-content'>{{q.query}}</div>" +
|
||||
"<div class='arrow'></div>" +
|
||||
"</div>" +
|
||||
"</td>" +
|
||||
"</tr>" +
|
||||
"</tbody>" +
|
||||
"</table>" +
|
||||
"</div>"
|
||||
})
|
||||
.Class({
|
||||
constructor: function() {
|
||||
this.databases = []
|
||||
this.update()
|
||||
},
|
||||
update: function() {
|
||||
var self = this
|
||||
self.databases = ENV.generateData(true).toArray()
|
||||
setTimeout(function() {self.update()}, ENV.timeout)
|
||||
|
||||
if (renderStage === 0) {
|
||||
renderStage = 1
|
||||
perfMonitor.startProfile("render")
|
||||
}
|
||||
},
|
||||
ngAfterViewChecked: function() {
|
||||
if (renderStage === 1) {
|
||||
perfMonitor.endProfile("render")
|
||||
renderStage = 0
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
ng.core.enableProdMode()
|
||||
ng.platform.browser.bootstrap(AppComponent)
|
||||
})
|
||||
18
examples/dbmonster/angular/index.html
Normal file
18
examples/dbmonster/angular/index.html
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="../lib/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="../styles.css" rel="stylesheet" type="text/css" />
|
||||
<meta charset="utf-8">
|
||||
<title>dbmon (Angular2)</title>
|
||||
</head>
|
||||
<body>
|
||||
<my-app></my-app>
|
||||
<script src="https://code.angularjs.org/2.0.0-beta.17/angular2-polyfills.js"></script>
|
||||
<script src="https://code.angularjs.org/2.0.0-beta.17/Rx.umd.js"></script>
|
||||
<script src="https://code.angularjs.org/2.0.0-beta.17/angular2-all.umd.js"></script>
|
||||
<script src="../ENV.js"></script>
|
||||
<script src="http://localvoid.github.io/perf-monitor/0.1/perf-monitor.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
7
examples/dbmonster/bootstrap.min.css
vendored
7
examples/dbmonster/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
|
|
@ -3,6 +3,10 @@
|
|||
var m = require("../../../render/hyperscript")
|
||||
var render = require("../../../render/render")(window).render
|
||||
|
||||
perfMonitor.startFPSMonitor()
|
||||
perfMonitor.startMemMonitor()
|
||||
perfMonitor.initProfiler("render")
|
||||
|
||||
var data = []
|
||||
|
||||
var root = document.getElementById("app")
|
||||
|
|
@ -11,29 +15,29 @@ update()
|
|||
function update() {
|
||||
data = ENV.generateData().toArray()
|
||||
|
||||
Monitoring.renderRate.ping()
|
||||
|
||||
render(root, [view()])
|
||||
perfMonitor.startProfile("render")
|
||||
render(root, view())
|
||||
perfMonitor.endProfile("render")
|
||||
|
||||
setTimeout(update, ENV.timeout)
|
||||
}
|
||||
|
||||
function view() {
|
||||
return m("div", [
|
||||
m("table", { className: "table table-striped latest-data" }, [
|
||||
m("table", {className: "table table-striped latest-data"}, [
|
||||
m("tbody",
|
||||
data.map(function(db) {
|
||||
return m("tr", {key: db.dbname}, [
|
||||
m("td", { className: "dbname" }, db.dbname),
|
||||
m("td", { className: "query-count" }, [
|
||||
m("span", { className: db.lastSample.countClassName }, db.lastSample.nbQueries)
|
||||
m("td", {className: "dbname"}, db.dbname),
|
||||
m("td", {className: "query-count"}, [
|
||||
m("span", {className: db.lastSample.countClassName}, db.lastSample.nbQueries)
|
||||
]),
|
||||
db.lastSample.topFiveQueries.map(function(query) {
|
||||
return m("td", { className: query.elapsedClassName }, [
|
||||
m("span", query.formatElapsed),
|
||||
m("div", { className: "popover left" }, [
|
||||
m("div", { className: "popover-content" }, query.query),
|
||||
m("div", { className: "arrow" })
|
||||
return m("td", {className: query.elapsedClassName}, [
|
||||
query.formatElapsed,
|
||||
m("div", {className: "popover left"}, [
|
||||
m("div", {className: "popover-content"}, query.query),
|
||||
m("div", {className: "arrow"})
|
||||
])
|
||||
])
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@
|
|||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="../../../module/module.js"></script>
|
||||
<script src="../../../render/normalizeChildren.js"></script>
|
||||
<script src="../../../render/node.js"></script>
|
||||
<script src="../../../render/hyperscript.js"></script>
|
||||
<script src="../../../render/render.js"></script>
|
||||
<script src="../ENV.js"></script>
|
||||
<script src="../memory-stats.js"></script>
|
||||
<script src="../monitor.js"></script>
|
||||
<script src="http://localvoid.github.io/perf-monitor/0.1/perf-monitor.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,36 +1,40 @@
|
|||
"use strict";
|
||||
"use strict"
|
||||
|
||||
var h = React.createElement
|
||||
|
||||
perfMonitor.startFPSMonitor()
|
||||
perfMonitor.startMemMonitor()
|
||||
perfMonitor.initProfiler("render")
|
||||
|
||||
var Query = React.createClass({
|
||||
shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) {
|
||||
if (nextProps.elapsedClassName !== this.props.elapsedClassName) return true;
|
||||
if (nextProps.formatElapsed !== this.props.formatElapsed) return true;
|
||||
if (nextProps.query !== this.props.query) return true;
|
||||
return false;
|
||||
if (nextProps.elapsedClassName !== this.props.elapsedClassName) return true
|
||||
if (nextProps.formatElapsed !== this.props.formatElapsed) return true
|
||||
if (nextProps.query !== this.props.query) return true
|
||||
return false
|
||||
},
|
||||
render: function render() {
|
||||
return h("td", { className: "Query " + this.props.elapsedClassName },
|
||||
return h("td", {className: "Query " + this.props.elapsedClassName},
|
||||
this.props.formatElapsed,
|
||||
h("div", { className: "popover left" },
|
||||
h("div", { className: "popover-content" }, this.props.query),
|
||||
h("div", { className: "arrow" })
|
||||
h("div", {className: "popover left"},
|
||||
h("div", {className: "popover-content"}, this.props.query),
|
||||
h("div", {className: "arrow"})
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
var Database = React.createClass({
|
||||
shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) {
|
||||
if (nextProps.lastMutationId === this.props.lastMutationId) return false;
|
||||
return true;
|
||||
if (nextProps.lastMutationId === this.props.lastMutationId) return false
|
||||
return true
|
||||
},
|
||||
render: function render() {
|
||||
var lastSample = this.props.lastSample;
|
||||
return h("tr", { key: this.props.dbname },
|
||||
h("td", { className: "dbname" }, this.props.dbname),
|
||||
h("td", { className: "query-count" },
|
||||
h("span", { className: this.props.lastSample.countClassName }, this.props.lastSample.nbQueries)
|
||||
var lastSample = this.props.lastSample
|
||||
return h("tr", {key: this.props.dbname},
|
||||
h("td", {className: "dbname"}, this.props.dbname),
|
||||
h("td", {className: "query-count"},
|
||||
h("span", {className: this.props.lastSample.countClassName}, this.props.lastSample.nbQueries)
|
||||
),
|
||||
this.props.lastSample.topFiveQueries.map(function (query, index) {
|
||||
return h(Query, {
|
||||
|
|
@ -39,34 +43,40 @@ var Database = React.createClass({
|
|||
elapsed: query.elapsed,
|
||||
formatElapsed: query.formatElapsed,
|
||||
elapsedClassName: query.elapsedClassName
|
||||
});
|
||||
})
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
var DBMon = React.createClass({
|
||||
getInitialState: function getInitialState() {
|
||||
return {
|
||||
databases: []
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
loadSamples: function loadSamples() {
|
||||
var data = ENV.generateData(true).toArray()
|
||||
|
||||
perfMonitor.startProfile("render")
|
||||
|
||||
this.setState({
|
||||
databases: ENV.generateData(true).toArray()
|
||||
});
|
||||
Monitoring.renderRate.ping();
|
||||
setTimeout(this.loadSamples, ENV.timeout);
|
||||
databases: data
|
||||
})
|
||||
|
||||
perfMonitor.endProfile("render")
|
||||
|
||||
setTimeout(this.loadSamples, ENV.timeout)
|
||||
},
|
||||
|
||||
componentDidMount: function componentDidMount() {
|
||||
this.loadSamples();
|
||||
this.loadSamples()
|
||||
},
|
||||
|
||||
render: function render() {
|
||||
return h("div", null,
|
||||
h("table", { className: "table table-striped latest-data" },
|
||||
h("table", {className: "table table-striped latest-data"},
|
||||
h("tbody", null, this.state.databases.map(function (database) {
|
||||
return h(Database, {
|
||||
key: database.dbname,
|
||||
|
|
@ -74,11 +84,11 @@ var DBMon = React.createClass({
|
|||
dbname: database.dbname,
|
||||
samples: database.samples,
|
||||
lastSample: database.lastSample
|
||||
});
|
||||
})
|
||||
}))
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
ReactDOM.render(h(DBMon, null), document.getElementById('app'));
|
||||
ReactDOM.render(h(DBMon, null), document.getElementById('app'))
|
||||
|
|
@ -8,11 +8,10 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="https://fb.me/react-15.0.1.js"></script>
|
||||
<script src="https://fb.me/react-dom-15.0.1.js"></script>
|
||||
<script src="https://fb.me/react-15.0.2.js"></script>
|
||||
<script src="https://fb.me/react-dom-15.0.2.js"></script>
|
||||
<script src="../ENV.js"></script>
|
||||
<script src="../memory-stats.js"></script>
|
||||
<script src="../monitor.js"></script>
|
||||
<script src="http://localvoid.github.io/perf-monitor/0.1/perf-monitor.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -10,34 +10,17 @@ var router = require("../../router/router")(window, "#")
|
|||
var api = {
|
||||
home : function() {
|
||||
T.timeEnd("Setup")
|
||||
return request({
|
||||
method: "GET",
|
||||
url: T.apiUrl + "/threads/",
|
||||
})
|
||||
return request({method: "GET", url: T.apiUrl + "/threads/"})
|
||||
},
|
||||
thread : function(id) {
|
||||
T.timeEnd("Setup")
|
||||
return request({
|
||||
method: "GET",
|
||||
url: T.apiUrl + "/comments/" + id,
|
||||
}).then(T.transformResponse)
|
||||
return request({method: "GET", url: T.apiUrl + "/comments/" + id}).then(T.transformResponse)
|
||||
},
|
||||
newThread : function(text) {
|
||||
return request({
|
||||
method: "POST",
|
||||
url: T.apiUrl + "/threads/create",
|
||||
data: {text: text},
|
||||
});
|
||||
return request({method: "POST", url: T.apiUrl + "/threads/create",data: {text: text}})
|
||||
},
|
||||
newComment : function(text, id) {
|
||||
return request({
|
||||
url: T.apiUrl + "/comments/create",
|
||||
method: "POST",
|
||||
data: {
|
||||
text: text,
|
||||
parent: id,
|
||||
}
|
||||
});
|
||||
return request({method: "POST", url: T.apiUrl + "/comments/create", data: {text: text, parent: id}});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -82,9 +65,9 @@ function createThread() {
|
|||
return false
|
||||
}
|
||||
|
||||
function showReplying(node) {
|
||||
node.replying = true
|
||||
node.newComment = ""
|
||||
function showReplying(vnode) {
|
||||
vnode.state.replying = true
|
||||
vnode.state.newComment = ""
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -99,39 +82,53 @@ function submitComment(node) {
|
|||
}
|
||||
|
||||
//shared
|
||||
function header() {
|
||||
return [
|
||||
m("p.head_links", [
|
||||
m("a[href='https://github.com/koglerjs/threaditjs/tree/master/examples/mithril']", "Source"),
|
||||
" | ",
|
||||
m("a[href='http://threaditjs.com']", "ThreaditJS Home"),
|
||||
]),
|
||||
m("h2", [
|
||||
m("a[href='#/']", "ThreaditJS: Mithril"),
|
||||
]),
|
||||
]
|
||||
var Header = {
|
||||
view: function() {
|
||||
return [
|
||||
m("p.head_links", [
|
||||
m("a[href='https://github.com/koglerjs/threaditjs/tree/master/examples/mithril']", "Source"),
|
||||
" | ",
|
||||
m("a[href='http://threaditjs.com']", "ThreaditJS Home"),
|
||||
]),
|
||||
m("h2", [
|
||||
m("a[href='#/']", "ThreaditJS: Mithril"),
|
||||
]),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
//home
|
||||
function home() {
|
||||
return {tag: "[", key: "home", attrs: {oncreate: loadThreads}, children: [
|
||||
header(),
|
||||
m(".main", [
|
||||
loaded === false ? m("h2", "Loading") :
|
||||
error ? m("h2", "Error! Try refreshing.") :
|
||||
notFound ? m("h2", "Not found! Don't try refreshing!") :
|
||||
[
|
||||
threads.map(threadListItem),
|
||||
newThread(),
|
||||
]
|
||||
])
|
||||
]}
|
||||
var Home = {
|
||||
oninit: loadThreads,
|
||||
view: function() {
|
||||
return [
|
||||
m(Header),
|
||||
m(".main", [
|
||||
loaded === false ? m("h2", "Loading") :
|
||||
error ? m("h2", "Error! Try refreshing.") :
|
||||
notFound ? m("h2", "Not found! Don't try refreshing!") : [
|
||||
threads.map(function(thread) {
|
||||
return [
|
||||
m("p", [
|
||||
m("a", {href: "#/thread/" + thread.id}, trust(T.trimTitle(thread.text))),
|
||||
]),
|
||||
m("p.comment_count", thread.comment_count + " comment(s)"),
|
||||
m("hr"),
|
||||
]
|
||||
}),
|
||||
m(NewThread),
|
||||
]
|
||||
])
|
||||
]
|
||||
}
|
||||
}
|
||||
function newThread() {
|
||||
return m("form", {onsubmit: createThread}, [
|
||||
m("textarea#threadText"),
|
||||
m("input", {type:"submit", value: "Post!"}),
|
||||
])
|
||||
var NewThread = {
|
||||
view: function() {
|
||||
return m("form", {onsubmit: createThread}, [
|
||||
m("textarea#threadText"),
|
||||
m("input", {type:"submit", value: "Post!"}),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
function threadListItem(thread) {
|
||||
|
|
@ -145,39 +142,49 @@ function threadListItem(thread) {
|
|||
}
|
||||
|
||||
//thread
|
||||
function thread(args) {
|
||||
if (current) T.time("Thread render")
|
||||
return {tag: "[", key: args.id, attrs: {oncreate: function() {loadThread(args.id)}, onremove: unloadThread}, children: [
|
||||
header(),
|
||||
current ? m(".main", {oncreate: function() {T.timeEnd("Thread render")}}, [
|
||||
threadNode({node: current.root})
|
||||
]) : null
|
||||
]}
|
||||
var Thread = {
|
||||
oninit: function(vnode) {
|
||||
loadThread(vnode.attrs.id)
|
||||
},
|
||||
onremove: unloadThread,
|
||||
view: function() {
|
||||
if (current) T.time("Thread render")
|
||||
return [
|
||||
m(Header),
|
||||
current ? m(".main", {oncreate: function() {T.timeEnd("Thread render")}}, [
|
||||
m(ThreadNode, {node: current.root})
|
||||
]) : null
|
||||
]
|
||||
}
|
||||
}
|
||||
function threadNode(args) {
|
||||
return m(".comment", [
|
||||
m("p", trust(args.node.text)),
|
||||
m(".reply", reply(args)),
|
||||
m(".children", [
|
||||
args.node.children.map(function(child) {
|
||||
return threadNode({node: child})
|
||||
})
|
||||
var ThreadNode = {
|
||||
view: function(vnode) {
|
||||
return m(".comment", [
|
||||
m("p", trust(vnode.attrs.node.text)),
|
||||
m(".reply", m(Reply, vnode.attrs)),
|
||||
m(".children", [
|
||||
vnode.attrs.node.children.map(function(child) {
|
||||
return m(ThreadNode, {node: child})
|
||||
})
|
||||
])
|
||||
])
|
||||
])
|
||||
}
|
||||
}
|
||||
function reply(args) {
|
||||
return args.node.replying
|
||||
? m("form", {onsubmit: function() {return submitComment(args.node)}}, [
|
||||
m("textarea", {
|
||||
value: args.node.newComment, //FIXME decouple UI state from data
|
||||
oninput: function(e) {
|
||||
args.node.newComment = e.target.value
|
||||
},
|
||||
}),
|
||||
m("input", {type:"submit", value: "Reply!"}),
|
||||
m(".preview", trust(T.previewComment(args.node.newComment))),
|
||||
])
|
||||
: m("a", {onclick: function() {return showReplying(args.node)}}, "Reply!")
|
||||
var Reply = {
|
||||
view: function(vnode) {
|
||||
return vnode.state.replying
|
||||
? m("form", {onsubmit: function() {return submitComment(vnode.attrs.node)}}, [
|
||||
m("textarea", {
|
||||
value: vnode.state.newComment,
|
||||
oninput: function(e) {
|
||||
vnode.state.newComment = e.target.value
|
||||
},
|
||||
}),
|
||||
m("input", {type:"submit", value: "Reply!"}),
|
||||
m(".preview", trust(T.previewComment(vnode.state.newComment))),
|
||||
])
|
||||
: m("a", {onclick: function() {return showReplying(vnode)}}, "Reply!")
|
||||
}
|
||||
}
|
||||
|
||||
//router
|
||||
|
|
@ -186,10 +193,10 @@ function run() {
|
|||
}
|
||||
|
||||
var replayRoute = router.defineRoutes({
|
||||
"/thread/:id" : thread,
|
||||
"/" : home
|
||||
"/thread/:id" : Thread,
|
||||
"/" : Home
|
||||
}, function(view, args) {
|
||||
render(document.body, [view(args)])
|
||||
render(document.body, [m(view, args)])
|
||||
}, function() {
|
||||
router.setPath("/")
|
||||
})
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<body>
|
||||
<script src="http://threaditjs.com/shared.js"></script>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/trust.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
|
||||
</footer>
|
||||
<script src="../../module/module.js"></script>
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script src="../../querystring/build.js"></script>
|
||||
|
|
|
|||
72
index.html
Normal file
72
index.html
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<style>* {font-size:40px;}</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="./module/module.js"></script>
|
||||
<script src="./render/node.js"></script>
|
||||
<script src="./render/hyperscript.js"></script>
|
||||
<script src="./render/render.js"></script>
|
||||
<script>
|
||||
var m = require("./render/hyperscript")
|
||||
var render = require("./render/render")(window, run).render
|
||||
|
||||
var count = 10
|
||||
function inc() {count++}
|
||||
|
||||
var Counter1 = {
|
||||
oninit: function(vnode) {console.log("init", vnode)},
|
||||
oncreate: function(vnode) {console.log("create", vnode)},
|
||||
onupdate: function(vnode) {console.log("update", vnode)},
|
||||
onremove: function(vnode) {console.log("remove", vnode)},
|
||||
onbeforeremove: function(vnode, done) {console.log("before remove", vnode);done()},
|
||||
view: function({attrs, state}) {
|
||||
return m("a", {
|
||||
onclick: () => {
|
||||
count++
|
||||
state.visible = !state.visible
|
||||
}
|
||||
}, attrs.count + (state.visible ? "visible" : ""))
|
||||
}
|
||||
}
|
||||
/*
|
||||
class Counter2 {
|
||||
oninit(vnode) {
|
||||
request().then(function(foo) {
|
||||
vnode.state.foo = foo
|
||||
})
|
||||
}
|
||||
onupdate(vnode) {
|
||||
vnode.dom.style.borderRight = vnode.attrs.count + "px solid red"
|
||||
}
|
||||
view(vnode) {
|
||||
return m("a", {onclick: inc}, vnode.attrs.count)
|
||||
}
|
||||
}
|
||||
function Counter3() {
|
||||
return m("a", {
|
||||
onupdate: function(vnode) {
|
||||
vnode.dom.style.borderRight = vnode.attrs.count + "px solid red"
|
||||
},
|
||||
onclick: inc
|
||||
}, vnode.attrs.count)
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
function view() {
|
||||
return m("div", [
|
||||
m("a", {onclick: inc}, count),
|
||||
m("br"),
|
||||
count % 7 !== 0 ? {tag: Counter1, attrs: {count: count}, text: "test", state: {}} : null,
|
||||
])
|
||||
}
|
||||
|
||||
function run() {
|
||||
render(document.body, view())
|
||||
}
|
||||
run()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -70,16 +70,22 @@ module.exports = new function init() {
|
|||
var body = fn.toString()
|
||||
var arg = (body.match(/\(([\w_$]+)/) || body.match(/([\w_$]+)\s*=>/) || []).pop()
|
||||
if (body.indexOf(arg) === body.lastIndexOf(arg)) throw new Error("`" + arg + "()` should be called at least once")
|
||||
fn(function done() {
|
||||
if (timeout !== undefined) {
|
||||
timeout = clearTimeout(timeout)
|
||||
if (delay !== Infinity) record(null)
|
||||
if (!isDone) next()
|
||||
else throw new Error("`" + arg + "()` should only be called once")
|
||||
isDone = true
|
||||
}
|
||||
else console.log("# elapsed: " + Math.round(new Date - s) + "ms, expected under " + delay + "ms")
|
||||
}, function(t) {delay = t})
|
||||
try {
|
||||
fn(function done() {
|
||||
if (timeout !== undefined) {
|
||||
timeout = clearTimeout(timeout)
|
||||
if (delay !== Infinity) record(null)
|
||||
if (!isDone) next()
|
||||
else throw new Error("`" + arg + "()` should only be called once")
|
||||
isDone = true
|
||||
}
|
||||
else console.log("# elapsed: " + Math.round(new Date - s) + "ms, expected under " + delay + "ms")
|
||||
}, function(t) {delay = t})
|
||||
}
|
||||
catch (e) {
|
||||
record(e.message, e)
|
||||
next()
|
||||
}
|
||||
if (timeout === 0) {
|
||||
timeout = setTimeout(function() {
|
||||
timeout = undefined
|
||||
|
|
@ -157,11 +163,13 @@ module.exports = new function init() {
|
|||
}
|
||||
}
|
||||
}
|
||||
function record(message) {
|
||||
function record(message, error) {
|
||||
var result = {pass: message === null}
|
||||
if (result.pass === false) {
|
||||
var error = new Error
|
||||
if (error.stack === undefined) new function() {try {throw error} catch (e) {error = e}}
|
||||
if (error == null) {
|
||||
error = new Error
|
||||
if (error.stack === undefined) new function() {try {throw error} catch (e) {error = e}}
|
||||
}
|
||||
result.context = subjects.join(" > ")
|
||||
result.message = message
|
||||
result.error = error.stack
|
||||
|
|
@ -170,6 +178,7 @@ module.exports = new function init() {
|
|||
}
|
||||
function serialize(value) {
|
||||
if (value === null || typeof value === "object") return String(value)
|
||||
else if (typeof value === "function") return value.name || "<anonymous function>"
|
||||
try {return JSON.stringify(value)} catch (e) {return String(value)}
|
||||
}
|
||||
function highlight(message) {
|
||||
|
|
@ -178,7 +187,7 @@ module.exports = new function init() {
|
|||
|
||||
function report() {
|
||||
for (var i = 0, r; r = results[i]; i++) {
|
||||
if (!r.pass) console.info(r.context + ": " + highlight(r.message) + "\n\n" + r.error.match(/^(?:(?!^Error|[\/\\]ospec[\/\\]ospec\.js).)*$/m) + "\n\n", hasProcess ? "" : "color:red", hasProcess ? "" : "color:black")
|
||||
if (!r.pass) console.error(r.context + ": " + highlight(r.message) + "\n\n" + r.error.match(/^(?:(?!Error|[\/\\]ospec[\/\\]ospec\.js).)*$/m) + "\n\n", hasProcess ? "" : "color:red", hasProcess ? "" : "color:black")
|
||||
}
|
||||
console.log(results.length + " tests completed in " + Math.round(new Date - start) + "ms")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"description": "A framework for building brilliant applications",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "node bundler/bundler",
|
||||
"test": "node ospec/bin/ospec"
|
||||
},
|
||||
"author": "Leo Horie",
|
||||
|
|
|
|||
|
|
@ -1,43 +1,51 @@
|
|||
"use strict"
|
||||
|
||||
var normalizeChildren = require("../render/normalizeChildren")
|
||||
var Node = require("../render/node")
|
||||
|
||||
var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[.+?\])/g, attrParser = /\[(.+?)(?:\s*=\s*("|'|)(.*?)\2)?\]/
|
||||
var selectorCache = {}
|
||||
function hyperscript(selector) {
|
||||
if (selectorCache[selector] === undefined) {
|
||||
var match, tag, id, classes = [], attributes = {}
|
||||
while (match = selectorParser.exec(selector)) {
|
||||
var type = match[1], value = match[2]
|
||||
if (type === "" && value !== "") tag = value
|
||||
else if (type === "#") attributes.id = value
|
||||
else if (type === ".") classes.push(value)
|
||||
else if (match[3][0] === "[") {
|
||||
var pair = attrParser.exec(match[3])
|
||||
attributes[pair[1]] = pair[3] || true
|
||||
}
|
||||
}
|
||||
if (classes.length > 0) attributes.className = classes.join(" ")
|
||||
selectorCache[selector] = function(attrs, children) {
|
||||
var hasAttrs = false, childList, text
|
||||
var className = attrs.className || attrs.class
|
||||
for (var key in attributes) attrs[key] = attributes[key]
|
||||
if (className !== undefined) {
|
||||
if (attrs.class !== undefined) {
|
||||
attrs.class = undefined
|
||||
attrs.className = className
|
||||
}
|
||||
if (attributes.className !== undefined) attrs.className = attributes.className + " " + className
|
||||
}
|
||||
for (var key in attrs) {
|
||||
if (key !== "key") {
|
||||
hasAttrs = true
|
||||
break
|
||||
if (typeof selector === "string") {
|
||||
if (selectorCache[selector] === undefined) {
|
||||
var match, tag, id, classes = [], attributes = {}
|
||||
while (match = selectorParser.exec(selector)) {
|
||||
var type = match[1], value = match[2]
|
||||
if (type === "" && value !== "") tag = value
|
||||
else if (type === "#") attributes.id = value
|
||||
else if (type === ".") classes.push(value)
|
||||
else if (match[3][0] === "[") {
|
||||
var pair = attrParser.exec(match[3])
|
||||
attributes[pair[1]] = pair[3] || true
|
||||
}
|
||||
}
|
||||
if (children instanceof Array && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children
|
||||
else childList = children
|
||||
return namespace({tag: tag || "div", key: attrs.key, attrs: hasAttrs ? attrs : undefined, children: childList, text: text})
|
||||
if (classes.length > 0) attributes.className = classes.join(" ")
|
||||
selectorCache[selector] = function(attrs, children) {
|
||||
var hasAttrs = false, childList, text
|
||||
var className = attrs.className || attrs.class
|
||||
for (var key in attributes) attrs[key] = attributes[key]
|
||||
if (className !== undefined) {
|
||||
if (attrs.class !== undefined) {
|
||||
attrs.class = undefined
|
||||
attrs.className = className
|
||||
}
|
||||
if (attributes.className !== undefined) attrs.className = attributes.className + " " + className
|
||||
}
|
||||
for (var key in attrs) {
|
||||
if (key !== "key") {
|
||||
hasAttrs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (children instanceof Array && children.length == 1 && children[0] != null && children[0].tag === "#") text = children[0].children
|
||||
else childList = children
|
||||
|
||||
var vnode = Node(tag || "div", attrs.key, hasAttrs ? attrs : undefined, childList, text, undefined)
|
||||
switch (vnode.tag) {
|
||||
case "svg": changeNS("http://www.w3.org/2000/svg", vnode); break
|
||||
case "math": changeNS("http://www.w3.org/1998/Math/MathML", vnode); break
|
||||
}
|
||||
return vnode
|
||||
}
|
||||
}
|
||||
}
|
||||
var attrs, children, childrenIndex
|
||||
|
|
@ -54,15 +62,8 @@ function hyperscript(selector) {
|
|||
for (var i = childrenIndex; i < arguments.length; i++) children.push(arguments[i])
|
||||
}
|
||||
|
||||
return selectorCache[selector](attrs || {}, normalizeChildren(children))
|
||||
}
|
||||
|
||||
function namespace(vnode) {
|
||||
switch (vnode.tag) {
|
||||
case "svg": changeNS("http://www.w3.org/2000/svg", vnode); break
|
||||
case "math": changeNS("http://www.w3.org/1998/Math/MathML", vnode); break
|
||||
}
|
||||
return vnode
|
||||
if (typeof selector === "string") return selectorCache[selector](attrs || {}, Node.normalizeChildren(children))
|
||||
return Node(selector, attrs && attrs.key, attrs, Node.normalizeChildren(children), undefined, undefined)
|
||||
}
|
||||
|
||||
function changeNS(ns, vnode) {
|
||||
|
|
|
|||
16
render/node.js
Normal file
16
render/node.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
function Node(tag, key, attrs, children, text, dom) {
|
||||
return {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, domSize: undefined, state: {}}
|
||||
}
|
||||
Node.normalize = function(node) {
|
||||
if (node instanceof Array) return Node("[", undefined, undefined, Node.normalizeChildren(node), undefined, undefined)
|
||||
else if (node != null && typeof node !== "object") return Node("#", undefined, undefined, node, undefined, undefined)
|
||||
return node
|
||||
}
|
||||
Node.normalizeChildren = function normalizeChildren(children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i] = Node.normalize(children[i])
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
module.exports = Node
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = function normalizeChildren(children) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (children[i] instanceof Array) children[i] = {tag: "[", key: undefined, attrs: undefined, children: normalizeChildren(children[i]), text: undefined}
|
||||
else if (children[i] != null && typeof children[i] !== "object") children[i] = {tag: "#", key: undefined, attrs: undefined, children: children[i], text: undefined}
|
||||
}
|
||||
return children
|
||||
}
|
||||
104
render/render.js
104
render/render.js
|
|
@ -1,6 +1,6 @@
|
|||
"use strict"
|
||||
|
||||
var normalizeChildren = require("../render/normalizeChildren")
|
||||
var Node = require("../render/node")
|
||||
|
||||
module.exports = function($window, onevent) {
|
||||
var $doc = $window.document
|
||||
|
|
@ -16,15 +16,19 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
function createNode(vnode, hooks) {
|
||||
var tag = vnode.tag
|
||||
if (vnode.attrs && vnode.attrs.oncreate) {
|
||||
hooks.push(vnode.attrs.oncreate.bind(vnode, vnode))
|
||||
if (vnode.attrs) {
|
||||
if (vnode.attrs.oninit) vnode.attrs.oninit.call(vnode, vnode)
|
||||
if (vnode.attrs.oncreate) hooks.push(vnode.attrs.oncreate.bind(vnode, vnode))
|
||||
}
|
||||
switch (tag) {
|
||||
case "#": return createText(vnode)
|
||||
case "<": return createHTML(vnode)
|
||||
case "[": return createFragment(vnode, hooks)
|
||||
default: return createElement(vnode, hooks)
|
||||
if (typeof tag === "string") {
|
||||
switch (tag) {
|
||||
case "#": return createText(vnode)
|
||||
case "<": return createHTML(vnode)
|
||||
case "[": return createFragment(vnode, hooks)
|
||||
default: return createElement(vnode, hooks)
|
||||
}
|
||||
}
|
||||
else return createComponent(vnode, hooks)
|
||||
}
|
||||
function createText(vnode) {
|
||||
return vnode.dom = $doc.createTextNode(vnode.children)
|
||||
|
|
@ -72,7 +76,7 @@ module.exports = function($window, onevent) {
|
|||
|
||||
if (vnode.text != null) {
|
||||
if (vnode.text !== "") element.textContent = vnode.text
|
||||
else vnode.children = [{tag: "#", children: vnode.text}]
|
||||
else vnode.children = [Node("#", undefined, undefined, vnode.text, undefined, undefined)]
|
||||
}
|
||||
|
||||
if (vnode.children != null) {
|
||||
|
|
@ -81,6 +85,14 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
return element
|
||||
}
|
||||
function createComponent(vnode, hooks) {
|
||||
vnode.instance = Node.normalize(vnode.tag.view(vnode))
|
||||
initLifecycle(vnode.tag, vnode, hooks)
|
||||
var element = createNode(vnode.instance, hooks)
|
||||
vnode.dom = vnode.instance.dom
|
||||
vnode.domSize = vnode.instance.domSize
|
||||
return element
|
||||
}
|
||||
|
||||
//update
|
||||
function updateNodes(parent, old, vnodes, hooks, nextSibling) {
|
||||
|
|
@ -98,7 +110,7 @@ module.exports = function($window, onevent) {
|
|||
else if (o != null && v != null && o.key === v.key) {
|
||||
oldStart++, start++
|
||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldStart, nextSibling), recycling)
|
||||
if (recycling) insertNode(parent, toFragment(v), nextSibling)
|
||||
if (recycling) insertNode(parent, toFragment(o), nextSibling)
|
||||
}
|
||||
else {
|
||||
var o = old[oldEnd]
|
||||
|
|
@ -116,7 +128,7 @@ module.exports = function($window, onevent) {
|
|||
if (o === v) oldEnd--, end--
|
||||
else if (o != null && v != null && o.key === v.key) {
|
||||
updateNode(parent, o, v, hooks, getNextSibling(old, oldEnd + 1, nextSibling), recycling)
|
||||
if (recycling) insertNode(parent, toFragment(v), nextSibling)
|
||||
if (recycling) insertNode(parent, toFragment(o), nextSibling)
|
||||
nextSibling = o.dom
|
||||
oldEnd--, end--
|
||||
}
|
||||
|
|
@ -148,16 +160,17 @@ module.exports = function($window, onevent) {
|
|||
function updateNode(parent, old, vnode, hooks, nextSibling, recycling) {
|
||||
var oldTag = old.tag, tag = vnode.tag
|
||||
if (oldTag === tag) {
|
||||
if (recycling) {
|
||||
if (vnode.attrs && vnode.attrs.oncreate) hooks.push(vnode.attrs.oncreate.bind(vnode, vnode))
|
||||
}
|
||||
else if (vnode.attrs && vnode.attrs.onupdate) hooks.push(vnode.attrs.onupdate.bind(vnode, vnode))
|
||||
switch (oldTag) {
|
||||
case "#": updateText(old, vnode); break
|
||||
case "<": updateHTML(parent, old, vnode, nextSibling); break
|
||||
case "[": updateFragment(parent, old, vnode, hooks, nextSibling); break
|
||||
default: updateElement(old, vnode, hooks)
|
||||
vnode.state = old.state
|
||||
if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks, recycling)
|
||||
if (typeof oldTag === "string") {
|
||||
switch (oldTag) {
|
||||
case "#": updateText(old, vnode); break
|
||||
case "<": updateHTML(parent, old, vnode, nextSibling); break
|
||||
case "[": updateFragment(parent, old, vnode, hooks, nextSibling); break
|
||||
default: updateElement(old, vnode, hooks)
|
||||
}
|
||||
}
|
||||
else updateComponent(parent, old, vnode, hooks, nextSibling, recycling)
|
||||
}
|
||||
else {
|
||||
removeNode(parent, old, null, false)
|
||||
|
|
@ -189,7 +202,7 @@ module.exports = function($window, onevent) {
|
|||
domSize += child.domSize || 1
|
||||
}
|
||||
}
|
||||
if (domSize != 1) vnode.domSize = domSize
|
||||
if (domSize !== 1) vnode.domSize = domSize
|
||||
}
|
||||
}
|
||||
function updateElement(old, vnode, hooks) {
|
||||
|
|
@ -199,11 +212,18 @@ module.exports = function($window, onevent) {
|
|||
if (old.text.toString() !== vnode.text.toString()) old.dom.firstChild.nodeValue = vnode.text
|
||||
}
|
||||
else {
|
||||
if (old.text != null) old.children = [{tag: "#", children: old.text, dom: old.dom.firstChild}]
|
||||
if (vnode.text != null) vnode.children = [{tag: "#", children: vnode.text}]
|
||||
if (old.text != null) old.children = [Node("#", undefined, undefined, old.text, undefined, old.dom.firstChild)]
|
||||
if (vnode.text != null) vnode.children = [Node("#", undefined, undefined, vnode.text, undefined, undefined)]
|
||||
updateNodes(element, old.children, vnode.children, hooks, null)
|
||||
}
|
||||
}
|
||||
function updateComponent(parent, old, vnode, hooks, nextSibling, recycling) {
|
||||
vnode.instance = Node.normalize(vnode.tag.view(vnode))
|
||||
updateLifecycle(vnode.tag, vnode, hooks, recycling)
|
||||
updateNode(parent, old.instance, vnode.instance, hooks, nextSibling, recycling)
|
||||
vnode.dom = vnode.instance.dom
|
||||
vnode.domSize = vnode.instance.domSize
|
||||
}
|
||||
function isRecyclable(old, vnodes) {
|
||||
if (old.pool != null && Math.abs(old.pool.length - vnodes.length) <= Math.abs(old.length - vnodes.length)) {
|
||||
var oldChildrenLength = old[0] && old[0].children && old[0].children.length || 0
|
||||
|
|
@ -262,9 +282,20 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
}
|
||||
function removeNode(parent, vnode, context, deferred) {
|
||||
if (vnode.attrs && vnode.attrs.onbeforeremove && deferred === false) {
|
||||
vnode.attrs.onbeforeremove.call(vnode, vnode, function() {removeNode(parent, vnode, context, true)})
|
||||
return
|
||||
if (deferred === false) {
|
||||
var expected = 0, called = 0
|
||||
var callback = function() {
|
||||
if (++called === expected) removeNode(parent, vnode, context, true)
|
||||
}
|
||||
if (vnode.attrs && vnode.attrs.onbeforeremove) {
|
||||
expected++
|
||||
vnode.attrs.onbeforeremove.call(vnode, vnode, callback)
|
||||
}
|
||||
if (typeof vnode.tag !== "string" && vnode.tag.onbeforeremove) {
|
||||
expected++
|
||||
vnode.tag.onbeforeremove.call(vnode, vnode, callback)
|
||||
}
|
||||
if (expected > 0) return
|
||||
}
|
||||
|
||||
onremove(vnode)
|
||||
|
|
@ -285,9 +316,10 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
function onremove(vnode) {
|
||||
if (vnode.attrs && vnode.attrs.onremove) vnode.attrs.onremove.call(vnode, vnode)
|
||||
if (typeof vnode.tag !== "string" && vnode.tag.onremove) vnode.tag.onremove.call(vnode, vnode)
|
||||
|
||||
var children = vnode.children
|
||||
if (children) {
|
||||
if (children instanceof Array) {
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i]
|
||||
if (child != null) onremove(child)
|
||||
|
|
@ -341,7 +373,7 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
}
|
||||
function isLifecycleMethod(attr) {
|
||||
return attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove"
|
||||
return attr === "oninit" || attr === "oncreate" || attr === "onupdate" || attr === "onremove" || attr === "onbeforeremove"
|
||||
}
|
||||
function isAttribute(attr) {
|
||||
return attr === "href" || attr === "list" || attr === "form"// || attr === "type" || attr === "width" || attr === "height"
|
||||
|
|
@ -364,15 +396,23 @@ module.exports = function($window, onevent) {
|
|||
}
|
||||
}
|
||||
|
||||
//lifecycle
|
||||
function initLifecycle(source, vnode, hooks) {
|
||||
if (source.oninit != null) source.oninit.call(vnode, vnode)
|
||||
if (source.oncreate != null) hooks.push(source.oncreate.bind(vnode, vnode))
|
||||
}
|
||||
function updateLifecycle(source, vnode, hooks, recycling) {
|
||||
if (recycling) initLifecycle(source, vnode, hooks)
|
||||
else if (source.onupdate != null) hooks.push(source.onupdate.bind(vnode, vnode))
|
||||
}
|
||||
|
||||
function render(dom, vnodes) {
|
||||
//if (dom.lastRedraw + 16 > performance.now() && vnodes.length > 0) return
|
||||
//dom.lastRedraw = performance.now()
|
||||
var hooks = []
|
||||
var active = $doc.activeElement
|
||||
if (!dom.vnodes) dom.vnodes = []
|
||||
if (dom.vnodes == null) dom.vnodes = []
|
||||
|
||||
if (!(vnodes instanceof Array)) vnodes = [vnodes]
|
||||
updateNodes(dom, dom.vnodes, normalizeChildren(vnodes), hooks, null)
|
||||
updateNodes(dom, dom.vnodes, Node.normalizeChildren(vnodes), hooks, null)
|
||||
for (var i = 0; i < hooks.length; i++) hooks[i]()
|
||||
dom.vnodes = vnodes
|
||||
if ($doc.activeElement !== active) active.focus()
|
||||
|
|
|
|||
|
|
@ -9,10 +9,14 @@
|
|||
<script src="../../test-utils/callAsync.js"></script>
|
||||
<script src="../../test-utils/domMock.js"></script>
|
||||
|
||||
<script src="../../render/normalizeChildren.js"></script>
|
||||
<script src="../../render/node.js"></script>
|
||||
<script src="../../render/trust.js"></script>
|
||||
<script src="../../render/hyperscript.js"></script>
|
||||
<script src="../../render/render.js"></script>
|
||||
<script src="test-hyperscript.js"></script>
|
||||
<script src="test-trust.js"></script>
|
||||
<script src="test-normalize.js"></script>
|
||||
<script src="test-normalizeChildren.js"></script>
|
||||
<script src="test-createText.js"></script>
|
||||
<script src="test-createHTML.js"></script>
|
||||
<script src="test-createFragment.js"></script>
|
||||
|
|
@ -23,6 +27,7 @@
|
|||
<script src="test-updateFragment.js"></script>
|
||||
<script src="test-updateElement.js"></script>
|
||||
<script src="test-updateNodes.js"></script>
|
||||
<script src="test-oninit.js"></script>
|
||||
<script src="test-oncreate.js"></script>
|
||||
<script src="test-onupdate.js"></script>
|
||||
<script src="test-onremove.js"></script>
|
||||
|
|
@ -31,6 +36,7 @@
|
|||
<script src="test-event.js"></script>
|
||||
<script src="test-input.js"></script>
|
||||
<script src="test-textContent.js"></script>
|
||||
<script src="test-component.js"></script>
|
||||
|
||||
<script>require("../../ospec/ospec").run()</script>
|
||||
</body>
|
||||
|
|
|
|||
459
render/tests/test-component.js
Normal file
459
render/tests/test-component.js
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var domMock = require("../../test-utils/domMock")
|
||||
var vdom = require("../../render/render")
|
||||
|
||||
o.spec("component", function() {
|
||||
var $window, root, render
|
||||
o.beforeEach(function() {
|
||||
$window = domMock()
|
||||
root = $window.document.createElement("div")
|
||||
render = vdom($window).render
|
||||
})
|
||||
|
||||
o.spec("basics", function() {
|
||||
o("works", function() {
|
||||
var component = {
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
var node = {tag: component}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("receives arguments", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return {tag: "div", attrs: vnode.attrs, text: vnode.text}
|
||||
}
|
||||
}
|
||||
var node = {tag: component, attrs: {id: "a"}, text: "b"}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("updates", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return {tag: "div", attrs: vnode.attrs, text: vnode.text}
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component, attrs: {id: "a"}, text: "b"}])
|
||||
render(root, [{tag: component, attrs: {id: "c"}, text: "d"}])
|
||||
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("c")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("d")
|
||||
})
|
||||
o("removes", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return {tag: "div"}
|
||||
}
|
||||
}
|
||||
var div = {tag: "div", key: 2}
|
||||
render(root, [{tag: component, key: 1}, div])
|
||||
render(root, [{tag: "div", key: 2}])
|
||||
|
||||
o(root.childNodes.length).equals(1)
|
||||
o(root.firstChild).equals(div.dom)
|
||||
})
|
||||
})
|
||||
o.spec("return value", function() {
|
||||
o("can return fragments", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return [
|
||||
{tag: "label"},
|
||||
{tag: "input"},
|
||||
]
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.childNodes.length).equals(2)
|
||||
o(root.childNodes[0].nodeName).equals("LABEL")
|
||||
o(root.childNodes[1].nodeName).equals("INPUT")
|
||||
})
|
||||
o("can return string", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return "a"
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("a")
|
||||
})
|
||||
o("can return falsy string", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("")
|
||||
})
|
||||
o("can return number", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("1")
|
||||
})
|
||||
o("can return falsy number", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("0")
|
||||
})
|
||||
o("can return boolean", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("true")
|
||||
})
|
||||
o("can return falsy boolean", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("false")
|
||||
})
|
||||
o("can update when returning fragments", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return [
|
||||
{tag: "label"},
|
||||
{tag: "input"},
|
||||
]
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.childNodes.length).equals(2)
|
||||
o(root.childNodes[0].nodeName).equals("LABEL")
|
||||
o(root.childNodes[1].nodeName).equals("INPUT")
|
||||
})
|
||||
o("can update when returning primitive", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return "a"
|
||||
}
|
||||
}
|
||||
render(root, [{tag: component}])
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(root.firstChild.nodeType).equals(3)
|
||||
o(root.firstChild.nodeValue).equals("a")
|
||||
})
|
||||
o("can remove when returning fragments", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return [
|
||||
{tag: "label"},
|
||||
{tag: "input"},
|
||||
]
|
||||
}
|
||||
}
|
||||
var div = {tag: "div", key: 2}
|
||||
render(root, [{tag: component, key: 1}, div])
|
||||
|
||||
render(root, [{tag: "div", key: 2}])
|
||||
|
||||
o(root.childNodes.length).equals(1)
|
||||
o(root.firstChild).equals(div.dom)
|
||||
})
|
||||
o("can remove when returning primitive", function() {
|
||||
var component = {
|
||||
view: function(vnode) {
|
||||
return "a"
|
||||
}
|
||||
}
|
||||
var div = {tag: "div", key: 2}
|
||||
render(root, [{tag: component, key: 1}, div])
|
||||
|
||||
render(root, [{tag: "div", key: 2}])
|
||||
|
||||
o(root.childNodes.length).equals(1)
|
||||
o(root.firstChild).equals(div.dom)
|
||||
})
|
||||
})
|
||||
o.spec("lifecycle", function() {
|
||||
o("calls oninit", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
oninit: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.tag).equals(component)
|
||||
o(vnode.dom).equals(undefined)
|
||||
o(root.childNodes.length).equals(0)
|
||||
},
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
var node = {tag: component}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls oninit when returning fragment", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
oninit: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.tag).equals(component)
|
||||
o(vnode.dom).equals(undefined)
|
||||
o(root.childNodes.length).equals(0)
|
||||
},
|
||||
view: function() {
|
||||
return [{tag: "div", attrs: {id: "a"}, text: "b"}]
|
||||
}
|
||||
}
|
||||
var node = {tag: component}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls oncreate", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
oncreate: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
var node = {tag: component}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls oncreate when returning fragment", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
oncreate: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return [{tag: "div", attrs: {id: "a"}, text: "b"}]
|
||||
}
|
||||
}
|
||||
var node = {tag: component}
|
||||
|
||||
render(root, [node])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls onupdate", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onupdate: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls onupdate when returning fragment", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onupdate: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return [{tag: "div", attrs: {id: "a"}, text: "b"}]
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.firstChild.nodeName).equals("DIV")
|
||||
o(root.firstChild.attributes["id"].nodeValue).equals("a")
|
||||
o(root.firstChild.firstChild.nodeValue).equals("b")
|
||||
})
|
||||
o("calls onremove", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onremove: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.childNodes.length).equals(0)
|
||||
})
|
||||
o("calls onremove when returning fragment", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onremove: function(vnode) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
},
|
||||
view: function() {
|
||||
return [{tag: "div", attrs: {id: "a"}, text: "b"}]
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.childNodes.length).equals(0)
|
||||
})
|
||||
o("calls onbeforeremove", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onbeforeremove: function(vnode, done) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
|
||||
done()
|
||||
},
|
||||
view: function() {
|
||||
return {tag: "div", attrs: {id: "a"}, text: "b"}
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.childNodes.length).equals(0)
|
||||
})
|
||||
o("calls onbeforeremove when returning fragment", function() {
|
||||
var called = 0
|
||||
var component = {
|
||||
onbeforeremove: function(vnode, done) {
|
||||
called++
|
||||
|
||||
o(vnode.dom).notEquals(undefined)
|
||||
o(vnode.dom).equals(root.firstChild)
|
||||
o(root.childNodes.length).equals(1)
|
||||
|
||||
done()
|
||||
},
|
||||
view: function() {
|
||||
return [{tag: "div", attrs: {id: "a"}, text: "b"}]
|
||||
}
|
||||
}
|
||||
|
||||
render(root, [{tag: component}])
|
||||
|
||||
o(called).equals(0)
|
||||
|
||||
render(root, [])
|
||||
|
||||
o(called).equals(1)
|
||||
o(root.childNodes.length).equals(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -376,4 +376,20 @@ o.spec("hyperscript", function() {
|
|||
o(vnode.children[0].ns).equals("http://www.w3.org/1998/Math/MathML")
|
||||
})
|
||||
})
|
||||
o.spec("components", function() {
|
||||
o("works", function() {
|
||||
var component = {
|
||||
view: function() {
|
||||
return m("div")
|
||||
}
|
||||
}
|
||||
var vnode = m(component, {id: "a"}, "b")
|
||||
|
||||
o(vnode.tag).equals(component)
|
||||
o(vnode.attrs.id).equals("a")
|
||||
o(vnode.children.length).equals(1)
|
||||
o(vnode.children[0].tag).equals("#")
|
||||
o(vnode.children[0].children).equals("b")
|
||||
})
|
||||
})
|
||||
})
|
||||
58
render/tests/test-normalize.js
Normal file
58
render/tests/test-normalize.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var Node = require("../../render/node")
|
||||
|
||||
o.spec("normalize", function() {
|
||||
o("normalizes array into fragment", function() {
|
||||
var node = Node.normalize([])
|
||||
|
||||
o(node.tag).equals("[")
|
||||
o(node.children.length).equals(0)
|
||||
})
|
||||
o("normalizes nested array into fragment", function() {
|
||||
var node = Node.normalize([[]])
|
||||
|
||||
o(node.tag).equals("[")
|
||||
o(node.children.length).equals(1)
|
||||
o(node.children[0].tag).equals("[")
|
||||
o(node.children[0].children.length).equals(0)
|
||||
})
|
||||
o("normalizes string into text node", function() {
|
||||
var node = Node.normalize("a")
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals("a")
|
||||
})
|
||||
o("normalizes falsy string into text node", function() {
|
||||
var node = Node.normalize("")
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals("")
|
||||
})
|
||||
o("normalizes number into text node", function() {
|
||||
var node = Node.normalize(1)
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals(1)
|
||||
})
|
||||
o("normalizes falsy number into text node", function() {
|
||||
var node = Node.normalize(0)
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals(0)
|
||||
})
|
||||
o("normalizes boolean into text node", function() {
|
||||
var node = Node.normalize(true)
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals(true)
|
||||
})
|
||||
o("normalizes falsy boolean into text node", function() {
|
||||
var node = Node.normalize(false)
|
||||
|
||||
o(node.tag).equals("#")
|
||||
o(node.children).equals(false)
|
||||
})
|
||||
})
|
||||
|
||||
20
render/tests/test-normalizeChildren.js
Normal file
20
render/tests/test-normalizeChildren.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var Node = require("../../render/node")
|
||||
|
||||
o.spec("normalizeChildren", function() {
|
||||
o("normalizes arrays into fragments", function() {
|
||||
var children = Node.normalizeChildren([[]])
|
||||
|
||||
o(children[0].tag).equals("[")
|
||||
o(children[0].children.length).equals(0)
|
||||
})
|
||||
o("normalizes strings into text nodes", function() {
|
||||
var children = Node.normalizeChildren(["a"])
|
||||
|
||||
o(children[0].tag).equals("#")
|
||||
o(children[0].children).equals("a")
|
||||
})
|
||||
})
|
||||
|
||||
216
render/tests/test-oninit.js
Normal file
216
render/tests/test-oninit.js
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var domMock = require("../../test-utils/domMock")
|
||||
var vdom = require("../../render/render")
|
||||
|
||||
o.spec("oninit", function() {
|
||||
var $window, root, render
|
||||
o.beforeEach(function() {
|
||||
$window = domMock()
|
||||
root = $window.document.createElement("div")
|
||||
render = vdom($window).render
|
||||
})
|
||||
|
||||
o("calls oninit when creating element", function() {
|
||||
var callback = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: callback}}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
o(callback.callCount).equals(1)
|
||||
o(callback.this).equals(vnode)
|
||||
o(callback.args[0]).equals(vnode)
|
||||
})
|
||||
o("calls oninit when creating text", function() {
|
||||
var callback = o.spy()
|
||||
var vnode = {tag: "#", attrs: {oninit: callback}, children: "a"}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
o(callback.callCount).equals(1)
|
||||
o(callback.this).equals(vnode)
|
||||
o(callback.args[0]).equals(vnode)
|
||||
})
|
||||
o("calls oninit when creating fragment", function() {
|
||||
var callback = o.spy()
|
||||
var vnode = {tag: "[", attrs: {oninit: callback}, children: []}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
o(callback.callCount).equals(1)
|
||||
o(callback.this).equals(vnode)
|
||||
o(callback.args[0]).equals(vnode)
|
||||
})
|
||||
o("calls oninit when creating html", function() {
|
||||
var callback = o.spy()
|
||||
var vnode = {tag: "<", attrs: {oninit: callback}, children: "a"}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
o(callback.callCount).equals(1)
|
||||
o(callback.this).equals(vnode)
|
||||
o(callback.args[0]).equals(vnode)
|
||||
})
|
||||
o("calls oninit when replacing keyed", function() {
|
||||
var createDiv = o.spy()
|
||||
var createA = o.spy()
|
||||
var vnode = {tag: "div", key: 1, attrs: {oninit: createDiv}}
|
||||
var updated = {tag: "a", key: 1, attrs: {oninit: createA}}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [updated])
|
||||
|
||||
o(createDiv.callCount).equals(1)
|
||||
o(createDiv.this).equals(vnode)
|
||||
o(createDiv.args[0]).equals(vnode)
|
||||
o(createA.callCount).equals(1)
|
||||
o(createA.this).equals(updated)
|
||||
o(createA.args[0]).equals(updated)
|
||||
})
|
||||
o("does not call oninit when noop", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: create}}
|
||||
var updated = {tag: "div", attrs: {oninit: update}}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [updated])
|
||||
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
o(update.callCount).equals(0)
|
||||
})
|
||||
o("does not call oninit when updating attr", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: create}}
|
||||
var updated = {tag: "div", attrs: {oninit: update, id: "a"}}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [updated])
|
||||
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
o(update.callCount).equals(0)
|
||||
})
|
||||
o("does not call oninit when updating children", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: create}, children: [{tag: "a"}]}
|
||||
var updated = {tag: "div", attrs: {oninit: update}, children: [{tag: "b"}]}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [updated])
|
||||
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
o(update.callCount).equals(0)
|
||||
})
|
||||
o("does not call oninit when updating keyed", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", key: 1, attrs: {oninit: create}}
|
||||
var otherVnode = {tag: "a", key: 2}
|
||||
var updated = {tag: "div", key: 1, attrs: {oninit: update}}
|
||||
var otherUpdated = {tag: "a", key: 2}
|
||||
|
||||
render(root, [vnode, otherVnode])
|
||||
render(root, [otherUpdated, updated])
|
||||
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
o(update.callCount).equals(0)
|
||||
})
|
||||
o("does not call oninit when removing", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: create}}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [])
|
||||
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
})
|
||||
o("calls oninit when recycling", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var vnode = {tag: "div", key: 1, attrs: {oninit: create}}
|
||||
var updated = {tag: "div", key: 1, attrs: {oninit: update}}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [])
|
||||
render(root, [updated])
|
||||
|
||||
o(vnode.dom).equals(updated.dom)
|
||||
o(create.callCount).equals(1)
|
||||
o(create.this).equals(vnode)
|
||||
o(create.args[0]).equals(vnode)
|
||||
o(update.callCount).equals(1)
|
||||
o(update.this).equals(updated)
|
||||
o(update.args[0]).equals(updated)
|
||||
})
|
||||
o("calls oninit at the same step as onupdate", function() {
|
||||
var create = o.spy()
|
||||
var update = o.spy()
|
||||
var callback = o.spy()
|
||||
var vnode = {tag: "div", attrs: {onupdate: create}, children: []}
|
||||
var updated = {tag: "div", attrs: {onupdate: update}, children: [{tag: "a", attrs: {oninit: callback}}]}
|
||||
|
||||
render(root, [vnode])
|
||||
render(root, [updated])
|
||||
|
||||
o(create.callCount).equals(0)
|
||||
o(update.callCount).equals(1)
|
||||
o(update.this).equals(updated)
|
||||
o(update.args[0]).equals(updated)
|
||||
o(callback.callCount).equals(1)
|
||||
o(callback.this).equals(updated.children[0])
|
||||
o(callback.args[0]).equals(updated.children[0])
|
||||
})
|
||||
o("calls oninit before full DOM creation", function() {
|
||||
var called = false
|
||||
var vnode = {tag: "div", children: [
|
||||
{tag: "a", attrs: {oninit: create}, children: [
|
||||
{tag: "b"}
|
||||
]}
|
||||
]}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
function create(vnode) {
|
||||
called = true
|
||||
|
||||
o(vnode.dom).equals(undefined)
|
||||
o(root.childNodes.length).equals(0)
|
||||
}
|
||||
o(called).equals(true)
|
||||
})
|
||||
o("does not set oninit as an event handler", function() {
|
||||
var create = o.spy()
|
||||
var vnode = {tag: "div", attrs: {oninit: create}, children: []}
|
||||
|
||||
render(root, [vnode])
|
||||
|
||||
o(vnode.dom.oninit).equals(undefined)
|
||||
o(vnode.dom.attributes["oninit"]).equals(undefined)
|
||||
})
|
||||
o("calls oninit on recycle", function() {
|
||||
var create = o.spy()
|
||||
var vnodes = [{tag: "div", key: 1, attrs: {oninit: create}}]
|
||||
var temp = []
|
||||
var updated = [{tag: "div", key: 1, attrs: {oninit: create}}]
|
||||
|
||||
render(root, vnodes)
|
||||
render(root, temp)
|
||||
render(root, updated)
|
||||
|
||||
o(create.callCount).equals(2)
|
||||
})
|
||||
})
|
||||
20
render/tests/test-trust.js
Normal file
20
render/tests/test-trust.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
"use strict"
|
||||
|
||||
var o = require("../../ospec/ospec")
|
||||
var domMock = require("../../test-utils/domMock")
|
||||
var trust = require("../../render/trust")
|
||||
|
||||
o.spec("trust", function() {
|
||||
o("works with html", function() {
|
||||
var vnode = trust("<a></a>")
|
||||
|
||||
o(vnode.tag).equals("<")
|
||||
o(vnode.children).equals("<a></a>")
|
||||
})
|
||||
o("works with text", function() {
|
||||
var vnode = trust("abc")
|
||||
|
||||
o(vnode.tag).equals("<")
|
||||
o(vnode.children).equals("abc")
|
||||
})
|
||||
})
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
"use strict"
|
||||
|
||||
var Node = require("../render/node")
|
||||
|
||||
module.exports = function(html) {
|
||||
return {tag: "<", key: undefined, attrs: undefined, children: html, text: undefined}
|
||||
return Node("<", undefined, undefined, html, undefined, undefined)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue