diff --git a/api/autoredraw.js b/api/autoredraw.js
new file mode 100644
index 00000000..180bf57b
--- /dev/null
+++ b/api/autoredraw.js
@@ -0,0 +1,17 @@
+"use strict"
+
+var throttle = require("../api/throttle")
+
+module.exports = function(root, renderer, pubsub, callback) {
+ var run = throttle(callback)
+ renderer.setEventCallback(function(e) {
+ if (e.redraw !== false) run()
+ })
+
+ if (pubsub != null) {
+ if (root.redraw) pubsub.unsubscribe(root.redraw)
+ pubsub.subscribe(run)
+ }
+
+ return root.redraw = run
+}
\ No newline at end of file
diff --git a/api/mount.js b/api/mount.js
index c17ca6bf..d6430c5f 100644
--- a/api/mount.js
+++ b/api/mount.js
@@ -1,18 +1,15 @@
"use strict"
var coreRenderer = require("../render/render")
-var throttle = require("../api/throttle")
+var autoredraw = require("../api/autoredraw")
-module.exports = function($window, renderers) {
+module.exports = function($window, pubsub) {
var renderer = coreRenderer($window)
return function(root, component) {
- var run = throttle(function() {
+ var run = autoredraw(root, renderer, pubsub, function() {
renderer.render(root, {tag: component})
})
- renderer.setEventCallback(run)
-
- renderers.push(run)
run()
}
}
diff --git a/api/pubsub.js b/api/pubsub.js
new file mode 100644
index 00000000..7d9fbb57
--- /dev/null
+++ b/api/pubsub.js
@@ -0,0 +1,15 @@
+"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
deleted file mode 100644
index 3a87b3df..00000000
--- a/api/redraw.js
+++ /dev/null
@@ -1,12 +0,0 @@
-"use strict"
-
-module.exports = function(renderers) {
- return function() {
- if (renderers.length === 0) return
- if (renderers.length === 1) return renderers[0]()
-
- for (var i = 0; i < renderers.length; i++) {
- renderers[i]()
- }
- }
-}
diff --git a/api/router.js b/api/router.js
index 63278b14..5c637997 100644
--- a/api/router.js
+++ b/api/router.js
@@ -2,9 +2,9 @@
var coreRenderer = require("../render/render")
var coreRouter = require("../router/router")
-var throttle = require("../api/throttle")
+var autoredraw = require("../api/autoredraw")
-module.exports = function($window, renderers) {
+module.exports = function($window, pubsub) {
var renderer = coreRenderer($window)
var router = coreRouter($window)
var route = function(root, defaultRoute, routes) {
@@ -13,10 +13,7 @@ module.exports = function($window, renderers) {
}, function() {
router.setPath(defaultRoute)
})
- var run = throttle(replay)
-
- renderer.setEventCallback(run)
- renderers.push(run)
+ autoredraw(root, renderer, pubsub, replay)
}
route.link = router.link
route.prefix = router.setPrefix
diff --git a/api/tests/index.html b/api/tests/index.html
index 7d211be2..9531cb64 100644
--- a/api/tests/index.html
+++ b/api/tests/index.html
@@ -19,13 +19,15 @@
-
-
-
-
+
+
+
+
+
-
+
+
diff --git a/api/tests/test-autoredraw.js b/api/tests/test-autoredraw.js
new file mode 100644
index 00000000..6f685bc7
--- /dev/null
+++ b/api/tests/test-autoredraw.js
@@ -0,0 +1,63 @@
+"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("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("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)
+ })
+
+})
\ No newline at end of file
diff --git a/api/tests/test-mount.js b/api/tests/test-mount.js
index 5a376de1..5e72badb 100644
--- a/api/tests/test-mount.js
+++ b/api/tests/test-mount.js
@@ -4,35 +4,23 @@ var o = require("../../ospec/ospec")
var domMock = require("../../test-utils/domMock")
var m = require("../../render/hyperscript")
+var apiPubSub = require("../../api/pubsub")
var apiMounter = require("../../api/mount")
-o.spec("m.mount", function() {
+o.spec("mount", function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
- var $window, root, mount, renderers
+ var $window, root, redraw, mount
o.beforeEach(function() {
$window = domMock()
root = $window.document.body
- renderers = []
- mount = apiMounter($window, renderers)
- })
-
- o("pushes a render function", function() {
- mount(root, {
- view : function() {
- return m("div")
- }
- })
-
- o(renderers.length).equals(1)
- o(typeof renderers[0]).equals("function")
+ redraw = apiPubSub()
+ mount = apiMounter($window, redraw)
})
o("renders into `root`", function() {
- var mount = apiMounter($window, [])
-
mount(root, {
view : function() {
return m("div")
@@ -106,7 +94,7 @@ o.spec("m.mount", function() {
o(onupdate.callCount).equals(0)
done()
- }, 20)
+ }, FRAME_BUDGET)
})
o("redraws when the render function is run", function(done) {
@@ -125,7 +113,7 @@ o.spec("m.mount", function() {
o(oninit.callCount).equals(1)
o(onupdate.callCount).equals(0)
- renderers[0]()
+ redraw.publish()
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
diff --git a/api/tests/test-redraw.js b/api/tests/test-pubsub.js
similarity index 56%
rename from api/tests/test-redraw.js
rename to api/tests/test-pubsub.js
index f126117a..1d5a37d5 100644
--- a/api/tests/test-redraw.js
+++ b/api/tests/test-pubsub.js
@@ -1,32 +1,30 @@
"use strict"
var o = require("../../ospec/ospec")
-var createRedraw = require("../../api/redraw")
+var apiPubSub = require("../../api/pubsub")
-o.spec("m.redraw", function() {
- var redraw, renderers
-
+o.spec("pubsub", function() {
+ var pubsub
o.beforeEach(function() {
- renderers = []
- redraw = createRedraw(renderers)
+ pubsub = apiPubSub()
})
o("it shouldn't error if there are no renderers", function() {
- redraw()
+ pubsub.publish()
})
o("it should run a single renderer entry", function() {
var spy = o.spy()
- renderers.push(spy)
+ pubsub.subscribe(spy)
- redraw()
+ pubsub.publish()
o(spy.callCount).equals(1)
- redraw()
- redraw()
- redraw()
+ pubsub.publish()
+ pubsub.publish()
+ pubsub.publish()
o(spy.callCount).equals(4)
})
@@ -36,20 +34,20 @@ o.spec("m.redraw", function() {
var spy2 = o.spy()
var spy3 = o.spy()
- renderers.push(spy1, spy2, spy3)
+ pubsub.subscribe(spy1)
+ pubsub.subscribe(spy2)
+ pubsub.subscribe(spy3)
- redraw()
+ pubsub.publish()
o(spy1.callCount).equals(1)
o(spy2.callCount).equals(1)
o(spy3.callCount).equals(1)
- redraw()
- redraw()
- redraw()
+ pubsub.publish()
- o(spy1.callCount).equals(4)
- o(spy2.callCount).equals(4)
- o(spy3.callCount).equals(4)
+ o(spy1.callCount).equals(2)
+ o(spy2.callCount).equals(2)
+ o(spy3.callCount).equals(2)
})
})
diff --git a/api/tests/test-router.js b/api/tests/test-router.js
index 6f10a136..b4cf6c10 100644
--- a/api/tests/test-router.js
+++ b/api/tests/test-router.js
@@ -5,11 +5,12 @@ var pushStateMock = require("../../test-utils/pushStateMock")
var domMock = require("../../test-utils/domMock")
var m = require("../../render/hyperscript")
+var apiPubSub = require("../../api/pubsub")
var apiRouter = require("../../api/router")
-o.spec("m.route", function() {
+o.spec("route", function() {
var FRAME_BUDGET = Math.floor(1000 / 60)
- var $window, root, route, renderers
+ var $window, root, redraw, route
o.beforeEach(function() {
$window = {}
@@ -22,21 +23,8 @@ o.spec("m.route", function() {
root = $window.document.body
- renderers = []
- route = apiRouter($window, renderers)
- })
-
- o("pushes a render function", function() {
- route(root, "/", {
- "/" : {
- view: function() {
- return m("div")
- }
- }
- })
-
- o(renderers.length).equals(1)
- o(typeof renderers[0]).equals("function")
+ redraw = apiPubSub()
+ route = apiRouter($window, redraw)
})
o("renders into `root`", function() {
@@ -68,7 +56,7 @@ o.spec("m.route", function() {
o(oninit.callCount).equals(1)
- renderers[0]()
+ redraw.publish()
// Wrapped to give time for the rate-limited redraw to fire
setTimeout(function() {
@@ -146,7 +134,7 @@ o.spec("m.route", function() {
o(onupdate.callCount).equals(0)
done()
- }, 20)
+ }, FRAME_BUDGET)
})
o("changes location on route.link", function() {
diff --git a/api/tests/test-throttle.js b/api/tests/test-throttle.js
index 924bc1c0..6c42d058 100644
--- a/api/tests/test-throttle.js
+++ b/api/tests/test-throttle.js
@@ -81,12 +81,4 @@ o.spec("throttle", function() {
o(spy.callCount).equals(2)
})
-
- o("it supports aborting when redraw is falsey", function() {
- throttled({ redraw : false })
- throttled({ redraw : 0 })
- throttled({ redraw : "" })
-
- o(spy.callCount).equals(0)
- })
})
diff --git a/api/throttle.js b/api/throttle.js
index 3d6ffa6b..edc2e4bc 100644
--- a/api/throttle.js
+++ b/api/throttle.js
@@ -7,7 +7,6 @@ module.exports = function(callback) {
var timeout = typeof requestAnimationFrame === "function" ? requestAnimationFrame : setTimeout
return function(synchronous) {
var now = new Date().getTime()
- if (typeof synchronous === "object" && "redraw" in synchronous && !synchronous.redraw) return
if (synchronous === true || last === 0 || now - last >= time) {
last = now
callback()
diff --git a/index.js b/index.js
index a09cc0db..83455088 100644
--- a/index.js
+++ b/index.js
@@ -2,18 +2,18 @@
var m = require("./render/hyperscript")
var trust = require("./render/trust")
-var coreRenderer = require("./render/render")
-var apiRedraw = require("./api/redraw")
-var apiMounter = require("./api/mount")
-var apiRouter = require("./api/router")
var coreRequester = require("./request/request")
-var renderers = []
+var coreRenderer = require("./render/render")
+var apiPubSub = require("./api/pubsub")
+var apiMount = require("./api/mount")
+var apiRouter = require("./api/router")
+var redraw = apiPubSub()
-m.redraw = apiRedraw(renderers)
m.trust = trust
-m.render = coreRenderer(window).render
-m.mount = apiMounter(window, renderers)
-m.route = apiRouter(window, renderers)
m.request = coreRequester(window, Promise).ajax
+m.render = coreRenderer(window).render
+m.mount = apiMount(window, redraw)
+m.route = apiRouter(window, redraw)
+m.redraw = redraw.publish
module.exports = m