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