diff --git a/README.md b/README.md
index e1be8c2b..71c22280 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,6 @@ There are over 4000 assertions in the test suite, and tests cover even difficult
## Modularity
-Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.42 KB min+gzip
+Despite the huge improvements in performance and modularity, the new codebase is smaller than v0.2.x, currently clocking at 7.41 KB min+gzip
In addition, Mithril is now completely modular: you can import only the modules that you need and easily integrate 3rd party modules if you wish to use a different library for routing, ajax, and even rendering
diff --git a/api/autoredraw.js b/api/autoredraw.js
deleted file mode 100644
index f3b88c03..00000000
--- a/api/autoredraw.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict"
-
-var throttle = require("../api/throttle")
-
-module.exports = function(root, renderer, pubsub, callback) {
- var run = throttle(callback)
- if (renderer != null) {
- renderer.setEventCallback(function(e) {
- if (e.redraw !== false) pubsub.publish()
- })
- }
-
- if (pubsub != null) {
- if (root.redraw) pubsub.unsubscribe(root.redraw)
- pubsub.subscribe(run)
- }
-
- return root.redraw = run
-}
diff --git a/api/mount.js b/api/mount.js
index 9ff6c81b..2afadc57 100644
--- a/api/mount.js
+++ b/api/mount.js
@@ -1,23 +1,42 @@
"use strict"
var Vnode = require("../render/vnode")
-var autoredraw = require("../api/autoredraw")
-module.exports = function(renderer, pubsub) {
+module.exports = function(redrawService) {
+ function throttle(callback) {
+ //60fps translates to 16.6ms, round it down since setTimeout requires int
+ var time = 16
+ var last = 0, pending = null
+ var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
+ return function() {
+ var now = Date.now()
+ if (last === 0 || now - last >= time) {
+ last = now
+ callback()
+ }
+ else if (pending === null) {
+ pending = timeout(function() {
+ pending = null
+ callback()
+ last = Date.now()
+ }, time - (now - last))
+ }
+ }
+ }
+
return function(root, component) {
if (component === null) {
- renderer.render(root, [])
- pubsub.unsubscribe(root.redraw)
- delete root.redraw
+ redrawService.render(root, [])
+ redrawService.unsubscribe(root)
return
}
if (component.view == null) throw new Error("m.mount(element, component) expects a component, not a vnode")
-
- var run = autoredraw(root, renderer, pubsub, function() {
- renderer.render(root, Vnode(component, undefined, undefined, undefined, undefined, undefined))
+
+ var run = throttle(function() {
+ redrawService.render(root, Vnode(component))
})
-
+ redrawService.subscribe(root, run)
run()
}
}
diff --git a/api/pubsub.js b/api/pubsub.js
deleted file mode 100644
index 7d9fbb57..00000000
--- a/api/pubsub.js
+++ /dev/null
@@ -1,15 +0,0 @@
-"use strict"
-
-module.exports = function() {
- var callbacks = []
- function unsubscribe(callback) {
- var index = callbacks.indexOf(callback)
- if (index > -1) callbacks.splice(index, 1)
- }
- function publish() {
- for (var i = 0; i < callbacks.length; i++) {
- callbacks[i].apply(this, arguments)
- }
- }
- return {subscribe: callbacks.push.bind(callbacks), unsubscribe: unsubscribe, publish: publish}
-}
diff --git a/api/redraw.js b/api/redraw.js
new file mode 100644
index 00000000..d2d20c52
--- /dev/null
+++ b/api/redraw.js
@@ -0,0 +1,26 @@
+"use strict"
+
+var coreRenderer = require("../render/render")
+
+module.exports = function($window) {
+ var renderService = coreRenderer($window)
+ renderService.setEventCallback(function(e) {
+ if (e.redraw !== false) redraw()
+ })
+
+ var callbacks = []
+ function subscribe(key, callback) {
+ unsubscribe(key)
+ callbacks.push(key, callback)
+ }
+ function unsubscribe(key) {
+ var index = callbacks.indexOf(key)
+ if (index > -1) callbacks.splice(index, 2)
+ }
+ function redraw() {
+ for (var i = 1; i < callbacks.length; i += 2) {
+ callbacks[i]()
+ }
+ }
+ return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render}
+}
diff --git a/api/router.js b/api/router.js
index 459f68c8..553a6751 100644
--- a/api/router.js
+++ b/api/router.js
@@ -3,55 +3,44 @@
var Vnode = require("../render/vnode")
var coreRouter = require("../router/router")
-module.exports = function($window, mount) {
- var router = coreRouter($window)
- var currentResolve, currentComponent, currentRender, currentArgs, currentPath
-
- var RouteComponent = {view: function() {
- return [currentRender(Vnode(currentComponent, null, currentArgs, undefined, undefined, undefined))]
- }}
- function defaultRender(vnode) {
- return vnode
- }
+module.exports = function($window, redrawService) {
+ var routeService = coreRouter($window)
+
+ var identity = function(v) {return v}
+ var current = {render: identity, component: null, path: null, resolve: null}
var route = function(root, defaultRoute, routes) {
- currentComponent = "div"
- currentRender = defaultRender
- currentArgs = null
-
- mount(root, RouteComponent)
-
- router.defineRoutes(routes, function(payload, args, path) {
- var isResolver = typeof payload.view !== "function"
- var render = defaultRender
-
- var resolve = currentResolve = function (component) {
- if (resolve !== currentResolve) return
- currentResolve = null
-
- currentComponent = component != null ? component : isResolver ? "div" : payload
- currentRender = render
- currentArgs = args
- currentPath = path
-
- root.redraw(true)
+ if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
+ var render = function(resolver, component, params, path) {
+ current.render = resolver.render || identity
+ current.component = component
+ current.path = path
+ current.resolve = null
+ redrawService.render(root, current.render(Vnode(component, undefined, params)))
+ }
+ var run = routeService.defineRoutes(routes, function(component, params, path, route, isRouteChange) {
+ if (component.view) render({}, component, params, path)
+ else {
+ if (component.onmatch) {
+ if (isRouteChange === false && current.path === path || current.resolve != null) render(current, current.component, params)
+ else {
+ current.resolve = function(resolved) {
+ render(component, resolved, params, path)
+ }
+ component.onmatch(function(resolved) {
+ if (current.path !== path && current.resolve != null) current.resolve(resolved)
+ }, params, path)
+ }
+ }
+ else render(component, "div", params, path)
}
- var onmatch = function() {
- resolve()
- }
- if (isResolver) {
- if (typeof payload.render === "function") render = payload.render.bind(payload)
- if (typeof payload.onmatch === "function") onmatch = payload.onmatch
- }
-
- onmatch.call(payload, resolve, args, path)
}, function() {
- router.setPath(defaultRoute, null, {replace: true})
+ routeService.setPath(defaultRoute)
})
+ redrawService.subscribe(root, run)
}
- route.link = router.link
- route.prefix = router.setPrefix
- route.set = router.setPath
- route.get = function() {return currentPath}
-
+ route.set = routeService.setPath
+ route.get = function() {return current.path}
+ route.prefix = routeService.setPrefix
+ route.link = routeService.link
return route
}
diff --git a/api/tests/index.html b/api/tests/index.html
index 75d15bea..fd2557d5 100644
--- a/api/tests/index.html
+++ b/api/tests/index.html
@@ -14,7 +14,7 @@
-
+
@@ -24,15 +24,11 @@
-
-
-
+
-
-
-
+
diff --git a/api/tests/test-autoredraw.js b/api/tests/test-autoredraw.js
deleted file mode 100644
index 191d2815..00000000
--- a/api/tests/test-autoredraw.js
+++ /dev/null
@@ -1,95 +0,0 @@
-"use strict"
-
-var o = require("../../ospec/ospec")
-var domMock = require("../../test-utils/domMock")
-
-var coreRenderer = require("../../render/render")
-var apiPubSub = require("../../api/pubsub")
-var autoredraw = require("../../api/autoredraw")
-
-o.spec("autoredraw", function() {
- var FRAME_BUDGET = Math.floor(1000 / 60)
- var $window, root, renderer, pubsub, spy
- o.beforeEach(function() {
- $window = domMock()
- root = $window.document.body
- renderer = coreRenderer($window)
- pubsub = apiPubSub()
- spy = o.spy()
- })
-
- o("returns self-trigger", function() {
- var run = autoredraw(root, renderer, pubsub, spy)
-
- run()
-
- o(spy.callCount).equals(1)
- })
-
- o("null renderer doesn't throw", function(done) {
- autoredraw(root, null, pubsub, spy)
- done()
- })
-
- o("null pubsub doesn't throw", function(done) {
- autoredraw(root, renderer, null, spy)
- done()
- })
-
- o("registers onevent", function() {
- autoredraw(root, renderer, pubsub, spy)
-
- renderer.render(root, {tag: "div", attrs: {onclick: function() {}}})
-
- var e = $window.document.createEvent("MouseEvents")
- e.initEvent("click", true, true)
- root.firstChild.dispatchEvent(e)
-
- o(spy.callCount).equals(1)
- })
-
- o("registers pubsub", function() {
- autoredraw(root, renderer, pubsub, spy)
-
- pubsub.publish()
-
- o(spy.callCount).equals(1)
- })
-
- o("re-registering pubsub works", function() {
- autoredraw(root, renderer, pubsub, spy)
- autoredraw(root, renderer, pubsub, spy)
-
- pubsub.publish()
-
- o(spy.callCount).equals(1)
- })
-
- o("throttles", function(done) {
- var run = autoredraw(root, renderer, pubsub, spy)
-
- run()
- run()
-
- o(spy.callCount).equals(1)
-
- setTimeout(function() {
- o(spy.callCount).equals(2)
-
- done()
- }, FRAME_BUDGET)
- })
-
- o("does not redraw if e.redraw is false", function() {
- autoredraw(root, renderer, pubsub, spy)
-
- renderer.render(root, {tag: "div", attrs: {onclick: function(e) {e.redraw = false}}})
-
- var e = $window.document.createEvent("MouseEvents")
- e.initEvent("click", true, true)
- root.firstChild.dispatchEvent(e)
-
- o(spy.callCount).equals(0)
- })
-
-})
diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js
index b65daf4a..f3b1b431 100644
--- a/api/tests/test-mount.js
+++ b/api/tests/test-mount.js
@@ -5,20 +5,20 @@ var domMock = require("../../test-utils/domMock")
var m = require("../../render/hyperscript")
var coreRenderer = require("../../render/render")
-var apiPubSub = require("../../api/pubsub")
+var apiRedraw = require("../../api/redraw")
var apiMounter = require("../../api/mount")
o.spec("mount", function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
- var $window, root, redraw, mount, render
+ var $window, root, redrawService, mount, render
o.beforeEach(function() {
$window = domMock()
root = $window.document.body
- redraw = apiPubSub()
- mount = apiMounter(coreRenderer($window), redraw)
+ redrawService = apiRedraw($window)
+ mount = apiMounter(redrawService)
render = coreRenderer($window).render
})
@@ -42,18 +42,16 @@ o.spec("mount", function() {
o(root.firstChild.nodeName).equals("DIV")
})
- o("mounting null deletes `redraw` from `root`", function() {
+ o("mounting null unmounts", function() {
mount(root, {
view : function() {
return m("div")
}
})
- o(typeof root.redraw).equals('function')
-
mount(root, null)
- o(typeof root.redraw).equals('undefined')
+ o(root.childNodes.length).equals(0)
})
o("redraws on events", function(done) {
@@ -206,7 +204,7 @@ o.spec("mount", function() {
o(oninit.callCount).equals(1)
o(onupdate.callCount).equals(0)
- redraw.publish()
+ redrawService.redraw()
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
diff --git a/api/tests/test-pubsub.js b/api/tests/test-pubsub.js
deleted file mode 100644
index 270b7c0e..00000000
--- a/api/tests/test-pubsub.js
+++ /dev/null
@@ -1,75 +0,0 @@
-"use strict"
-
-var o = require("../../ospec/ospec")
-var apiPubSub = require("../../api/pubsub")
-
-o.spec("pubsub", function() {
- var pubsub
- o.beforeEach(function() {
- pubsub = apiPubSub()
- })
-
- o("shouldn't error if there are no renderers", function() {
- pubsub.publish()
- })
-
- o("should run a single renderer entry", function() {
- var spy = o.spy()
-
- pubsub.subscribe(spy)
-
- pubsub.publish()
-
- o(spy.callCount).equals(1)
-
- pubsub.publish()
- pubsub.publish()
- pubsub.publish()
-
- o(spy.callCount).equals(4)
- })
-
- o("should run all renderer entries", function() {
- var spy1 = o.spy()
- var spy2 = o.spy()
- var spy3 = o.spy()
-
- pubsub.subscribe(spy1)
- pubsub.subscribe(spy2)
- pubsub.subscribe(spy3)
-
- pubsub.publish()
-
- o(spy1.callCount).equals(1)
- o(spy2.callCount).equals(1)
- o(spy3.callCount).equals(1)
-
- pubsub.publish()
-
- o(spy1.callCount).equals(2)
- o(spy2.callCount).equals(2)
- o(spy3.callCount).equals(2)
- })
-
- o("should stop running after unsubscribe", function() {
- var spy = o.spy()
-
- pubsub.subscribe(spy)
- pubsub.unsubscribe(spy)
-
- pubsub.publish()
-
- o(spy.callCount).equals(0)
- })
-
- o("does nothing on invalid unsubscribe", function() {
- var spy = o.spy()
-
- pubsub.subscribe(spy)
- pubsub.unsubscribe(null)
-
- pubsub.publish()
-
- o(spy.callCount).equals(1)
- })
-})
diff --git a/api/tests/test-redraw.js b/api/tests/test-redraw.js
new file mode 100644
index 00000000..5f002e5f
--- /dev/null
+++ b/api/tests/test-redraw.js
@@ -0,0 +1,82 @@
+"use strict"
+
+var o = require("../../ospec/ospec")
+var domMock = require("../../test-utils/domMock")
+var apiRedraw = require("../../api/redraw")
+
+o.spec("redrawService", function() {
+ var root, redrawService, $document
+ o.beforeEach(function() {
+ var $window = domMock()
+ root = $window.document.body
+ redrawService = apiRedraw($window)
+ $document = $window.document
+ })
+
+ o("shouldn't error if there are no renderers", function() {
+ redrawService.redraw()
+ })
+
+ o("should run a single renderer entry", function() {
+ var spy = o.spy()
+
+ redrawService.subscribe(root, spy)
+
+ redrawService.redraw()
+
+ o(spy.callCount).equals(1)
+
+ redrawService.redraw()
+ redrawService.redraw()
+ redrawService.redraw()
+
+ o(spy.callCount).equals(4)
+ })
+
+ o("should run all renderer entries", function() {
+ var el1 = $document.createElement("div")
+ var el2 = $document.createElement("div")
+ var el3 = $document.createElement("div")
+ var spy1 = o.spy()
+ var spy2 = o.spy()
+ var spy3 = o.spy()
+
+ redrawService.subscribe(el1, spy1)
+ redrawService.subscribe(el2, spy2)
+ redrawService.subscribe(el3, spy3)
+
+ redrawService.redraw()
+
+ o(spy1.callCount).equals(1)
+ o(spy2.callCount).equals(1)
+ o(spy3.callCount).equals(1)
+
+ redrawService.redraw()
+
+ o(spy1.callCount).equals(2)
+ o(spy2.callCount).equals(2)
+ o(spy3.callCount).equals(2)
+ })
+
+ o("should stop running after unsubscribe", function() {
+ var spy = o.spy()
+
+ redrawService.subscribe(root, spy)
+ redrawService.unsubscribe(root, spy)
+
+ redrawService.redraw()
+
+ o(spy.callCount).equals(0)
+ })
+
+ o("does nothing on invalid unsubscribe", function() {
+ var spy = o.spy()
+
+ redrawService.subscribe(root, spy)
+ redrawService.unsubscribe(null)
+
+ redrawService.redraw()
+
+ o(spy.callCount).equals(1)
+ })
+})
diff --git a/api/tests/test-router.js b/api/tests/test-router.js
index 721d6b28..0bd394dc 100644
--- a/api/tests/test-router.js
+++ b/api/tests/test-router.js
@@ -6,25 +6,23 @@ var browserMock = require("../../test-utils/browserMock")
var m = require("../../render/hyperscript")
var coreRenderer = require("../../render/render")
-var apiPubSub = require("../../api/pubsub")
+var apiRedraw = require("../../api/redraw")
var apiRouter = require("../../api/router")
-var apiMounter = require("../../api/mount")
o.spec("route", function() {
void [{protocol: "http:", hostname: "localhost"}, {protocol: "file:", hostname: "/"}].forEach(function(env) {
void ["#", "?", "", "#!", "?!", "/foo"].forEach(function(prefix) {
o.spec("using prefix `" + prefix + "` starting on " + env.protocol + "//" + env.hostname, function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
- var $window, root, redraw, mount, route
+ var $window, root, redrawService, route
o.beforeEach(function() {
$window = browserMock(env)
root = $window.document.body
- redraw = apiPubSub()
- mount = apiMounter(coreRenderer($window), redraw)
- route = apiRouter($window, mount)
+ redrawService = apiRedraw($window)
+ route = apiRouter($window, redrawService)
route.prefix(prefix)
})
@@ -59,7 +57,7 @@ o.spec("route", function() {
o(view.callCount).equals(1)
- redraw.publish(true)
+ redrawService.redraw()
o(view.callCount).equals(2)
@@ -122,15 +120,15 @@ o.spec("route", function() {
o(oninit.callCount).equals(1)
- redraw.publish(true)
+ redrawService.redraw()
o(onupdate.callCount).equals(1)
})
o("redraws on events", function(done) {
var onupdate = o.spy()
- var oninit = o.spy()
- var onclick = o.spy()
+ var oninit = o.spy()
+ var onclick = o.spy()
var e = $window.document.createEvent("MouseEvents")
e.initEvent("click", true, true)
@@ -403,7 +401,7 @@ o.spec("route", function() {
o(matchCount).equals(1)
o(renderCount).equals(1)
- redraw.publish(true)
+ redrawService.redraw()
o(matchCount).equals(1)
o(renderCount).equals(2)
@@ -505,7 +503,7 @@ o.spec("route", function() {
o(view.callCount).equals(1)
o(onmatch.callCount).equals(1)
- redraw.publish(true)
+ redrawService.redraw()
o(view.callCount).equals(2)
o(onmatch.callCount).equals(1)
@@ -515,7 +513,7 @@ o.spec("route", function() {
})
o("m.route.set(m.route.get()) re-runs the resolution logic (#1180)", function(done){
- var onmatch = o.spy(function(resolve){resolve()})
+ var onmatch = o.spy(function(resolve) {resolve()})
$window.location.href = prefix + "/"
route(root, '/', {
diff --git a/api/tests/test-throttle.js b/api/tests/test-throttle.js
deleted file mode 100644
index 4f7c7ee5..00000000
--- a/api/tests/test-throttle.js
+++ /dev/null
@@ -1,90 +0,0 @@
-"use strict"
-
-var o = require("../../ospec/ospec")
-var callAsync = require("../../test-utils/callAsync")
-var throttle = require("../../api/throttle")
-
-o.spec("throttle", function() {
- var FRAME_BUDGET = Math.floor(1000 / 60)
- var spy, throttled
- o.beforeEach(function() {
- spy = o.spy()
- throttled = throttle(spy)
- })
-
- o("runs first call synchronously", function() {
- throttled()
-
- o(spy.callCount).equals(1)
- })
-
- o("throttles subsequent synchronous calls", function(done) {
- throttled()
- throttled()
-
- o(spy.callCount).equals(1)
-
- setTimeout(function() {
- o(spy.callCount).equals(2)
-
- done()
- }, FRAME_BUDGET) //this delay is much higher than 16.6ms due to setTimeout clamp and other runtime costs
- })
-
- o("calls after threshold", function(done) {
- throttled()
-
- o(spy.callCount).equals(1)
-
- setTimeout(function(t) {
- throttled()
-
- o(spy.callCount).equals(2)
-
- done()
- }, FRAME_BUDGET)
-
- })
-
- o("throttles before threshold", function(done) {
- throttled()
-
- o(spy.callCount).equals(1)
-
- callAsync(function(t) {
- throttled()
-
- o(spy.callCount).equals(1)
-
- done()
- })
- })
-
- o("it only runs once per tick", function(done) {
- throttled()
- throttled()
- throttled()
-
- o(spy.callCount).equals(1)
-
- setTimeout(function() {
- o(spy.callCount).equals(2)
-
- done()
- }, FRAME_BUDGET)
- })
-
- o("it supports forcing a synchronous redraw", function(done) {
- throttled()
- throttled()
- throttled(true)
-
- o(spy.callCount).equals(2)
-
- setTimeout(function() {
- o(spy.callCount).equals(3)
-
- done()
- }, FRAME_BUDGET)
- })
-})
diff --git a/api/throttle.js b/api/throttle.js
deleted file mode 100644
index 6655b07c..00000000
--- a/api/throttle.js
+++ /dev/null
@@ -1,22 +0,0 @@
-"use strict"
-
-module.exports = function(callback) {
- //60fps translates to 16.6ms, round it down since setTimeout requires int
- var time = 16
- var last = 0, pending = null
- var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
- return function(synchronous) {
- var now = Date.now()
- if (synchronous === true || last === 0 || now - last >= time) {
- last = now
- callback()
- }
- else if (pending === null) {
- pending = timeout(function() {
- pending = null
- callback()
- last = Date.now()
- }, time - (now - last))
- }
- }
-}
diff --git a/bundler/bundle.js b/bundler/bundle.js
index dff20bfe..4ac98300 100644
--- a/bundler/bundle.js
+++ b/bundler/bundle.js
@@ -32,8 +32,9 @@ function run(input, output) {
def = def || "", variable = variable || "", eq = eq || "", rest = rest || ""
if (def[0] === ",") def = "\nvar ", pre = "\n"
var dependency = resolve(filepath, filename)
- var code = process(dependency, pre + (modules[dependency] == null ? exportCode(filename, dependency, def, variable, eq, rest, uuid) : def + variable + eq + modules[dependency]))
- modules[dependency] = rest ? "_" + uuid : variable
+ var localUUID = uuid // global uuid can update from nested `process` call, ensure same id is used on declaration and consumption
+ var code = process(dependency, pre + (modules[dependency] == null ? exportCode(filename, dependency, def, variable, eq, rest, localUUID) : def + variable + eq + modules[dependency]))
+ modules[dependency] = rest ? "_" + localUUID : variable
uuid++
return code + rest
})
@@ -116,7 +117,7 @@ function run(input, output) {
code = "new function() {\n" + code + "\n}"
if (!isFile(output) || code !== read(output)) {
- try {new Function(code); console.log("build completed at " + new Date())} catch (e) {}
+ //try {new Function(code); console.log("build completed at " + new Date())} catch (e) {}
error = null
fs.writeFileSync(output, code, "utf8")
}
@@ -129,7 +130,7 @@ function run(input, output) {
module.exports = function(input, output, options) {
run(input, output)
if (options && options.watch) {
- fs.watch(process.cwd(), {recursive: true}, function(file) {
+ fs.watch(process.cwd(), {recursive: true}, function(file, type) {
if (typeof file === "string" && path.resolve(output) !== path.resolve(file)) run(input, output)
})
}
diff --git a/bundler/tests/test-bundler.js b/bundler/tests/test-bundler.js
index 80763c00..60859a11 100644
--- a/bundler/tests/test-bundler.js
+++ b/bundler/tests/test-bundler.js
@@ -273,6 +273,30 @@ o.spec("bundler", function() {
remove("d.js")
remove("out.js")
})
+ o("works if included multiple times", function() {
+ write("a.js", `module.exports = 123`)
+ write("b.js", `var a = require("./a").toString()\nmodule.exports = a`)
+ write("c.js", `var a = require("./a").toString()\nvar b = require("./b")`)
+ bundle(ns + "c.js", ns + "out.js")
+
+ o(read("out.js")).equals(`new function() {\nvar _0 = 123\nvar a = _0.toString()\nvar a0 = _0.toString()\nvar b = a0\n}`)
+
+ remove("a.js")
+ remove("b.js")
+ remove("c.js")
+ })
+ o("works if included multiple times reverse", function() {
+ write("a.js", `module.exports = 123`)
+ write("b.js", `var a = require("./a").toString()\nmodule.exports = a`)
+ write("c.js", `var b = require("./b")\nvar a = require("./a").toString()`)
+ bundle(ns + "c.js", ns + "out.js")
+
+ o(read("out.js")).equals(`new function() {\nvar _0 = 123\nvar a0 = _0.toString()\nvar b = a0\nvar a = _0.toString()\n}`)
+
+ remove("a.js")
+ remove("b.js")
+ remove("c.js")
+ })
o("reuses binding if possible", function() {
write("a.js", `var b = require("./b")\nvar c = require("./c")`)
write("b.js", `var d = require("./d")\nmodule.exports = function() {return d + 1}`)
diff --git a/index.js b/index.js
index 8ae9df25..f50049c4 100644
--- a/index.js
+++ b/index.js
@@ -4,13 +4,13 @@ var m = require("./hyperscript")
var requestService = require("./request")
var redrawService = require("./redraw")
-requestService.setCompletionCallback(redrawService.publish)
+requestService.setCompletionCallback(redrawService.redraw)
m.mount = require("./mount")
m.route = require("./route")
m.withAttr = require("./util/withAttr")
-m.render = require("./render").render
-m.redraw = redrawService.publish
+m.render = redrawService.render
+m.redraw = redrawService.redraw
m.request = requestService.request
m.jsonp = requestService.jsonp
m.parseQueryString = require("./querystring/parse")
diff --git a/mithril.js b/mithril.js
index 7fdbec3a..4c29abab 100644
--- a/mithril.js
+++ b/mithril.js
@@ -328,22 +328,7 @@ var _8 = function($window, Promise) {
return {request: request, jsonp: jsonp, setCompletionCallback: setCompletionCallback}
}
var requestService = _8(window, PromisePolyfill)
-var _11 = function() {
- var callbacks = []
- function unsubscribe(callback) {
- var index = callbacks.indexOf(callback)
- if (index > -1) callbacks.splice(index, 1)
- }
- function publish() {
- for (var i = 0; i < callbacks.length; i++) {
- callbacks[i].apply(this, arguments)
- }
- }
- return {subscribe: callbacks.push.bind(callbacks), unsubscribe: unsubscribe, publish: publish}
-}
-var redrawService = _11()
-requestService.setCompletionCallback(redrawService.publish)
-var _13 = function($window) {
+var coreRenderer = function($window) {
var $doc = $window.document
var $emptyFragment = $doc.createDocumentFragment()
var onevent
@@ -642,8 +627,8 @@ var _13 = function($window) {
for (var i = 0; i < end; i++) {
var vnode = vnodes[i]
if (vnode != null) {
- var key1 = vnode.key
- if (key1 != null) map[key1] = i
+ var key2 = vnode.key
+ if (key2 != null) map[key2] = i
}
}
return map
@@ -749,34 +734,34 @@ var _13 = function($window) {
}
//attrs2
function setAttrs(vnode, attrs2, ns) {
- for (var key1 in attrs2) {
- setAttr(vnode, key1, null, attrs2[key1], ns)
+ for (var key2 in attrs2) {
+ setAttr(vnode, key2, null, attrs2[key2], ns)
}
}
- function setAttr(vnode, key1, old, value, ns) {
+ function setAttr(vnode, key2, old, value, ns) {
var element = vnode.dom
- if (key1 === "key" || (old === value && !isFormAttribute(vnode, key1)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key1)) return
- var nsLastIndex = key1.indexOf(":")
- if (nsLastIndex > -1 && key1.substr(0, nsLastIndex) === "xlink") {
- element.setAttributeNS("http://www.w3.org/1999/xlink", key1.slice(nsLastIndex + 1), value)
+ if (key2 === "key" || (old === value && !isFormAttribute(vnode, key2)) && typeof value !== "object" || typeof value === "undefined" || isLifecycleMethod(key2)) return
+ var nsLastIndex = key2.indexOf(":")
+ if (nsLastIndex > -1 && key2.substr(0, nsLastIndex) === "xlink") {
+ element.setAttributeNS("http://www.w3.org/1999/xlink", key2.slice(nsLastIndex + 1), value)
}
- else if (key1[0] === "o" && key1[1] === "n" && typeof value === "function") updateEvent(vnode, key1, value)
- else if (key1 === "style") updateStyle(element, old, value)
- else if (key1 in element && !isAttribute(key1) && ns === undefined) {
+ else if (key2[0] === "o" && key2[1] === "n" && typeof value === "function") updateEvent(vnode, key2, value)
+ else if (key2 === "style") updateStyle(element, old, value)
+ else if (key2 in element && !isAttribute(key2) && ns === undefined) {
//setting input[value] to same value by typing on focused element moves cursor to end in Chrome
- if (vnode.tag === "input" && key1 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
+ if (vnode.tag === "input" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
//setting select[value] to same value while having select open blinks select dropdown in Chrome
- if (vnode.tag === "select" && key1 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
+ if (vnode.tag === "select" && key2 === "value" && vnode.dom.value === value && vnode.dom === $doc.activeElement) return
//setting option[value] to same value while having select open blinks select dropdown in Chrome
- if (vnode.tag === "option" && key1 === "value" && vnode.dom.value === value) return
- element[key1] = value
+ if (vnode.tag === "option" && key2 === "value" && vnode.dom.value === value) return
+ element[key2] = value
}
else {
if (typeof value === "boolean") {
- if (value) element.setAttribute(key1, "")
- else element.removeAttribute(key1)
+ if (value) element.setAttribute(key2, "")
+ else element.removeAttribute(key2)
}
- else element.setAttribute(key1 === "className" ? "class" : key1, value)
+ else element.setAttribute(key2 === "className" ? "class" : key2, value)
}
}
function setLateAttrs(vnode) {
@@ -788,16 +773,16 @@ var _13 = function($window) {
}
function updateAttrs(vnode, old, attrs2, ns) {
if (attrs2 != null) {
- for (var key1 in attrs2) {
- setAttr(vnode, key1, old && old[key1], attrs2[key1], ns)
+ for (var key2 in attrs2) {
+ setAttr(vnode, key2, old && old[key2], attrs2[key2], ns)
}
}
if (old != null) {
- for (var key1 in old) {
- if (attrs2 == null || !(key1 in attrs2)) {
- if (key1 === "className") key1 = "class"
- if (key1[0] === "o" && key1[1] === "n" && !isLifecycleMethod(key1)) updateEvent(vnode, key1, undefined)
- else if (key1 !== "key") vnode.dom.removeAttribute(key1)
+ for (var key2 in old) {
+ if (attrs2 == null || !(key2 in attrs2)) {
+ if (key2 === "className") key2 = "class"
+ if (key2[0] === "o" && key2[1] === "n" && !isLifecycleMethod(key2)) updateEvent(vnode, key2, undefined)
+ else if (key2 !== "key") vnode.dom.removeAttribute(key2)
}
}
}
@@ -821,32 +806,32 @@ var _13 = function($window) {
else if (typeof style === "string") element.style.cssText = style
else {
if (typeof old === "string") element.style.cssText = ""
- for (var key1 in style) {
- element.style[key1] = style[key1]
+ for (var key2 in style) {
+ element.style[key2] = style[key2]
}
if (old != null && typeof old !== "string") {
- for (var key1 in old) {
- if (!(key1 in style)) element.style[key1] = ""
+ for (var key2 in old) {
+ if (!(key2 in style)) element.style[key2] = ""
}
}
}
}
//event
- function updateEvent(vnode, key1, value) {
+ function updateEvent(vnode, key2, value) {
var element = vnode.dom
var callback = function(e) {
var result = value.call(element, e)
if (typeof onevent === "function") onevent.call(element, e)
return result
}
- if (key1 in element) element[key1] = typeof value === "function" ? callback : null
+ if (key2 in element) element[key2] = typeof value === "function" ? callback : null
else {
- var eventName = key1.slice(2)
+ var eventName = key2.slice(2)
if (vnode.events === undefined) vnode.events = {}
- if (vnode.events[key1] != null) element.removeEventListener(eventName, vnode.events[key1], false)
+ if (vnode.events[key2] != null) element.removeEventListener(eventName, vnode.events[key2], false)
if (typeof value === "function") {
- vnode.events[key1] = callback
- element.addEventListener(eventName, vnode.events[key1], false)
+ vnode.events[key2] = callback
+ element.addEventListener(eventName, vnode.events[key2], false)
}
}
}
@@ -888,79 +873,90 @@ var _13 = function($window) {
}
return {render: render, setEventCallback: setEventCallback}
}
-var renderService = _13(window)
-var throttle = function(callback1) {
- //60fps translates to 16.6ms, round it down since setTimeout requires int
- var time = 16
- var last = 0, pending = null
- var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
- return function(synchronous) {
- var now = Date.now()
- if (synchronous === true || last === 0 || now - last >= time) {
- last = now
- callback1()
- }
- else if (pending === null) {
- pending = timeout(function() {
- pending = null
- callback1()
- last = Date.now()
- }, time - (now - last))
+var _11 = function($window) {
+ var renderService = coreRenderer($window)
+ renderService.setEventCallback(function(e) {
+ if (e.redraw !== false) redraw()
+ })
+
+ var callbacks = []
+ function subscribe(key1, callback) {
+ unsubscribe(key1)
+ callbacks.push(key1, callback)
+ }
+ function unsubscribe(key1) {
+ var index = callbacks.indexOf(key1)
+ if (index > -1) callbacks.splice(index, 2)
+ }
+ function redraw() {
+ for (var i = 1; i < callbacks.length; i += 2) {
+ callbacks[i]()
+ }
+ }
+ return {subscribe: subscribe, unsubscribe: unsubscribe, redraw: redraw, render: renderService.render}
+}
+var redrawService = _11(window)
+requestService.setCompletionCallback(redrawService.redraw)
+var _16 = function(redrawService0) {
+ function throttle(callback0) {
+ //60fps translates to 16.6ms, round it down since setTimeout requires int
+ var time = 16
+ var last = 0, pending = null
+ var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
+ return function() {
+ var now = Date.now()
+ if (last === 0 || now - last >= time) {
+ last = now
+ callback0()
+ }
+ else if (pending === null) {
+ pending = timeout(function() {
+ pending = null
+ callback0()
+ last = Date.now()
+ }, time - (now - last))
+ }
}
}
-}
-var autoredraw = function(root, renderer, pubsub, callback0) {
- var run1 = throttle(callback0)
- if (renderer != null) {
- renderer.setEventCallback(function(e) {
- if (e.redraw !== false) pubsub.publish()
- })
- }
- if (pubsub != null) {
- if (root.redraw) pubsub.unsubscribe(root.redraw)
- pubsub.subscribe(run1)
- }
- return root.redraw = run1
-}
-var _17 = function(renderer, pubsub) {
+
return function(root, component) {
if (component === null) {
- renderer.render(root, [])
- pubsub.unsubscribe(root.redraw)
- delete root.redraw
+ redrawService0.render(root, [])
+ redrawService0.unsubscribe(root)
return
}
if (component.view == null) throw new Error("m.mount(element, component) expects a component, not a vnode")
- var run0 = autoredraw(root, renderer, pubsub, function() {
- renderer.render(root, Vnode(component, undefined, undefined, undefined, undefined, undefined))
+
+ var run0 = throttle(function() {
+ redrawService0.render(root, Vnode(component))
})
+ redrawService0.subscribe(root, run0)
run0()
}
}
-m.mount = _17(renderService, redrawService)
-var mount = m.mount
+m.mount = _16(redrawService)
var parseQueryString = function(string) {
if (string === "" || string == null) return {}
if (string.charAt(0) === "?") string = string.slice(1)
var entries = string.split("&"), data0 = {}, counters = {}
for (var i = 0; i < entries.length; i++) {
var entry = entries[i].split("=")
- var key3 = decodeURIComponent(entry[0])
+ var key4 = decodeURIComponent(entry[0])
var value = entry.length === 2 ? decodeURIComponent(entry[1]) : ""
if (value === "true") value = true
else if (value === "false") value = false
- var levels = key3.split(/\]\[?|\[/)
+ var levels = key4.split(/\]\[?|\[/)
var cursor = data0
- if (key3.indexOf("[") > -1) levels.pop()
+ if (key4.indexOf("[") > -1) levels.pop()
for (var j = 0; j < levels.length; j++) {
var level = levels[j], nextLevel = levels[j + 1]
var isNumber = nextLevel == "" || !isNaN(parseInt(nextLevel, 10))
var isValue = j === levels.length - 1
if (level === "") {
- var key3 = levels.slice(0, j).join()
- if (counters[key3] == null) counters[key3] = 0
- level = counters[key3]++
+ var key4 = levels.slice(0, j).join()
+ if (counters[key4] == null) counters[key4] = 0
+ level = counters[key4]++
}
if (cursor[level] == null) {
cursor[level] = isValue ? value : isNumber ? [] : {}
@@ -982,11 +978,11 @@ var coreRouter = function($window) {
}
var asyncId
function debounceAsync(f) {
- return function() {
+ return function(e) {
if (asyncId != null) return
asyncId = callAsync0(function() {
asyncId = null
- f()
+ f(e)
})
}
}
@@ -997,11 +993,11 @@ var coreRouter = function($window) {
if (queryIndex > -1) {
var queryEnd = hashIndex > -1 ? hashIndex : path.length
var queryParams = parseQueryString(path.slice(queryIndex + 1, queryEnd))
- for (var key2 in queryParams) queryData[key2] = queryParams[key2]
+ for (var key3 in queryParams) queryData[key3] = queryParams[key3]
}
if (hashIndex > -1) {
var hashParams = parseQueryString(path.slice(hashIndex + 1))
- for (var key2 in hashParams) hashData[key2] = hashParams[key2]
+ for (var key3 in hashParams) hashData[key3] = hashParams[key3]
}
return path.slice(0, pathEnd)
}
@@ -1017,7 +1013,7 @@ var coreRouter = function($window) {
var queryData = {}, hashData = {}
path = parsePath(path, queryData, hashData)
if (data != null) {
- for (var key2 in data) queryData[key2] = data[key2]
+ for (var key3 in data) queryData[key3] = data[key3]
path = path.replace(/:([^\/]+)/g, function(match2, token) {
delete queryData[token]
return data[token]
@@ -1030,16 +1026,16 @@ var coreRouter = function($window) {
if (supportsPushState) {
if (options && options.replace) $window.history.replaceState(null, null, prefix1 + path)
else $window.history.pushState(null, null, prefix1 + path)
- $window.onpopstate()
+ $window.onpopstate(true)
}
else $window.location.href = prefix1 + path
}
- function defineRoutes(routes, resolve0, reject) {
+ function defineRoutes(routes, resolve, reject) {
if (supportsPushState) $window.onpopstate = debounceAsync(resolveRoute)
else if (prefix1.charAt(0) === "#") $window.onhashchange = resolveRoute
- resolveRoute()
+ resolveRoute(true)
- function resolveRoute() {
+ function resolveRoute(isRouteChange) {
var path = getPath()
var params = {}
var pathname = parsePath(path, params, params)
@@ -1053,14 +1049,14 @@ var coreRouter = function($window) {
for (var i = 0; i < keys.length; i++) {
params[keys[i].replace(/:|\./g, "")] = decodeURIComponent(values[i])
}
- resolve0(routes[route0], params, path, route0)
+ resolve(routes[route0], params, path, route0, !!isRouteChange)
})
return
}
}
reject(path, params)
}
- return resolveRoute
+ return function() {resolveRoute(false)}
}
function link(vnode2) {
vnode2.dom.setAttribute("href", prefix1 + vnode2.attrs.href)
@@ -1075,64 +1071,60 @@ var coreRouter = function($window) {
}
return {setPrefix: setPrefix, getPath: getPath, setPath: setPath, defineRoutes: defineRoutes, link: link}
}
-var _23 = function($window, mount0) {
- var router = coreRouter($window)
- var currentResolve, currentComponent, currentRender, currentArgs, currentPath
- var RouteComponent = {view: function() {
- return [currentRender(Vnode(currentComponent, null, currentArgs, undefined, undefined, undefined))]
- }}
- function defaultRender(vnode1) {
- return vnode1
- }
+var _20 = function($window, redrawService0) {
+ var routeService = coreRouter($window)
+
+ var identity = function(v) {return v}
+ var current = {render: identity, component: null, path: null, resolve: null}
var route = function(root, defaultRoute, routes) {
- currentComponent = "div"
- currentRender = defaultRender
- currentArgs = null
- mount0(root, RouteComponent)
- router.defineRoutes(routes, function(payload, args0, path) {
- var isResolver = typeof payload.view !== "function"
- var render1 = defaultRender
- var resolve = currentResolve = function (component) {
- if (resolve !== currentResolve) return
- currentResolve = null
- currentComponent = component != null ? component : isResolver ? "div" : payload
- currentRender = render1
- currentArgs = args0
- currentPath = path
- root.redraw(true)
+ if (root == null) throw new Error("Ensure the DOM element that was passed to `m.route` is not undefined")
+ var render1 = function(resolver, component, params, path) {
+ current.render = resolver.render || identity
+ current.component = component
+ current.path = path
+ current.resolve = null
+ redrawService0.render(root, current.render(Vnode(component, undefined, params)))
+ }
+ var run1 = routeService.defineRoutes(routes, function(component, params, path, route, isRouteChange) {
+ if (component.view) render1({}, component, params, path)
+ else {
+ if (component.onmatch) {
+ if (isRouteChange === false && current.path === path || current.resolve != null) render1(current, current.component, params)
+ else {
+ current.resolve = function(resolved) {
+ render1(component, resolved, params, path)
+ }
+ component.onmatch(function(resolved) {
+ if (current.path !== path && current.resolve != null) current.resolve(resolved)
+ }, params, path)
+ }
+ }
+ else render1(component, "div", params, path)
}
- var onmatch = function() {
- resolve()
- }
- if (isResolver) {
- if (typeof payload.render === "function") render1 = payload.render.bind(payload)
- if (typeof payload.onmatch === "function") onmatch = payload.onmatch
- }
-
- onmatch.call(payload, resolve, args0, path)
}, function() {
- router.setPath(defaultRoute, null, {replace: true})
+ routeService.setPath(defaultRoute)
})
+ redrawService0.subscribe(root, run1)
}
- route.link = router.link
- route.prefix = router.setPrefix
- route.set = router.setPath
- route.get = function() {return currentPath}
+ route.set = routeService.setPath
+ route.get = function() {return current.path}
+ route.prefix = routeService.setPrefix
+ route.link = routeService.link
return route
}
-m.route = _23(window, mount)
-m.withAttr = function(attrName, callback2, context) {
+m.route = _20(window, redrawService)
+m.withAttr = function(attrName, callback1, context) {
return function(e) {
- return callback2.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
+ return callback1.call(context || this, attrName in e.currentTarget ? e.currentTarget[attrName] : e.currentTarget.getAttribute(attrName))
}
}
-m.render = renderService.render
-m.redraw = redrawService.publish
+m.render = redrawService.render
+m.redraw = redrawService.redraw
m.request = requestService.request
m.jsonp = requestService.jsonp
m.parseQueryString = parseQueryString
m.buildQueryString = buildQueryString
-m.version = "1.0.0-rc.5"
+m.version = "1.0.0-rc.6"
if (typeof module !== "undefined") module["exports"] = m
else window.m = m
}
\ No newline at end of file
diff --git a/mithril.min.js b/mithril.min.js
index 41590c4a..3e9ac712 100644
--- a/mithril.min.js
+++ b/mithril.min.js
@@ -1,40 +1,40 @@
-new function(){function m(a,b,k,e,l,h){return{tag:a,key:b,attrs:k,children:e,text:l,dom:h,domSize:void 0,state:{},events:void 0,instance:void 0,skip:!1}}function t(a){if(null==a||"string"!==typeof a&&null==a.view)throw Error("The selector must be either a string or a component.");if("string"===typeof a&&void 0===H[a]){for(var b,k,e=[],l={};b=O.exec(a);){var h=b[1],v=b[2];""===h&&""!==v?k=v:"#"===h?l.id=v:"."===h?e.push(v):"["===b[3][0]&&((h=b[6])&&(h=h.replace(/\\(["'])/g,"$1").replace(/\\\\/g,"\\")),
-"class"===b[4]?e.push(h):l[b[4]]=h||!0)}0a.indexOf("?")?"?":"&";a+=e+f}return a}function v(a){try{return""!==
-a?JSON.parse(a):null}catch(w){throw Error(a);}}function p(a){return a.responseText}function r(a,b){if("function"===typeof a)if(b instanceof Array)for(var e=0;en.status||304===n.status)b(r(f.type,a));else{var h=Error(n.responseText),k;for(k in a)h[k]=a[k];e(h)}}catch(G){e(G)}};
-u&&null!=f.data?n.send(f.data):n.send()}))},jsonp:function(f){return e(new b(function(b,e){var n=f.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+m++,k=a.document.createElement("script");a[n]=function(e){k.parentNode.removeChild(k);b(r(f.type,e));delete a[n]};k.onerror=function(){k.parentNode.removeChild(k);e(Error("JSONP request failed"));delete a[n]};null==f.data&&(f.data={});f.url=l(f.url,f.data);f.data[f.callbackKey||"callback"]=n;k.src=h(f.url,f.data);a.document.documentElement.appendChild(k)}))},
-setCompletionCallback:function(a){q=a}}}(window,"undefined"!==typeof Promise?Promise:y),J=function(){var a=[];return{subscribe:a.push.bind(a),unsubscribe:function(b){b=a.indexOf(b);-1=A&&B>=l;){var x=a[A],m=d[l];if(x!==m||g)if(null==x)A++;else if(null==m)l++;else if(x.key===m.key)A++,l++,h(c,x,m,f,p(a,A,e),g,n),g&&x.tag===m.tag&&r(c,v(x),e);
-else if(x=a[q],x!==m||g)if(null==x)q--;else if(null==m)l++;else if(x.key===m.key)h(c,x,m,f,p(a,q+1,e),g,n),(g||l=A&&B>=l;){x=a[q];m=d[B];if(x!==m||g)if(null==x)q--;else{if(null!=m)if(x.key===m.key)h(c,x,m,f,p(a,q+1,e),g,n),g&&x.tag===m.tag&&r(c,v(x),e),null!=x.dom&&(e=x.dom),q--;else{if(!w){w=a;var x=q,D={},z;for(z=0;za.indexOf("?")?"?":"&";a+=f+d}return a}function v(a){try{return""!==
+a?JSON.parse(a):null}catch(w){throw Error(a);}}function p(a){return a.responseText}function q(a,b){if("function"===typeof a)if(b instanceof Array)for(var d=0;dm.status||304===m.status)b(q(d.type,a));else{var f=Error(m.responseText),k;for(k in a)f[k]=a[k];h(f)}}catch(G){h(G)}};
+r&&null!=d.data?m.send(d.data):m.send()}))},jsonp:function(d){return b(new h(function(b,h){var m=d.callbackName||"_mithril_"+Math.round(1E16*Math.random())+"_"+n++,r=a.document.createElement("script");a[m]=function(f){r.parentNode.removeChild(r);b(q(d.type,f));delete a[m]};r.onerror=function(){r.parentNode.removeChild(r);h(Error("JSONP request failed"));delete a[m]};null==d.data&&(d.data={});d.url=f(d.url,d.data);d.data[d.callbackKey||"callback"]=m;r.src=k(d.url,d.data);a.document.documentElement.appendChild(r)}))},
+setCompletionCallback:function(a){m=a}}}(window,"undefined"!==typeof Promise?Promise:t),N=function(a){function h(g,c,a,b,d,f,h){for(;a=m&&u>=y;){var x=c[m],n=a[y];if(x!==n||e)if(null==x)m++;else if(null==n)y++;else if(x.key===n.key)m++,y++,k(g,x,n,d,p(c,m,b),e,f),e&&x.tag===n.tag&&q(g,v(x),b);else if(x=c[z],x!==n||e)if(null==x)z--;else if(null==n)y++;else if(x.key===n.key)k(g,x,n,d,p(c,z+1,b),e,f),(e||y=
+m&&u>=y;){x=c[z];n=a[u];if(x!==n||e)if(null==x)z--;else{if(null!=n)if(x.key===n.key)k(g,x,n,d,p(c,z+1,b),e,f),e&&x.tag===n.tag&&q(g,v(x),b),null!=x.dom&&(b=x.dom),z--;else{if(!w){w=c;var x=z,D={},t;for(t=0;t