From a433a054b801db811da11882ae2641062a3d771b Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Thu, 12 May 2016 15:56:42 -0400 Subject: [PATCH] Syndicate.UI.windowEvent --- js/src/ui.js | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/js/src/ui.js b/js/src/ui.js index 742d3f4..6baef3d 100644 --- a/js/src/ui.js +++ b/js/src/ui.js @@ -34,6 +34,11 @@ var _$ = Dataspace_._$; // do such a scan. var globalEvent = Struct.makeConstructor('globalEvent', ['selector', 'eventType', 'event']); +// Message. As globalEvent, but instead of using a selector to choose +// target DOM nodes, attaches an event handler to the browser "window" +// object itself. +var windowEvent = Struct.makeConstructor('windowEvent', ['eventType', 'event']); + // Message. Like globalEvent, but applies only within the scope of the // UI fragment identified. var uiEvent = Struct.makeConstructor('uiEvent', ['fragmentId', 'selector', 'eventType', 'event']); @@ -69,6 +74,16 @@ function spawnUIDriver() { } })); + var windowEventProj = windowEvent(_$('eventType'), __); + Dataspace.spawn( + new DemandMatcher([Patch.observe(windowEventProj)], + [Patch.advertise(windowEventProj)], + { + onDemandIncrease: function (c) { + Dataspace.spawn(new WindowEventSupply(c.eventType)); + } + })); + Dataspace.spawn( new DemandMatcher([uiFragment(_$('fragmentId'), __, __)], [uiFragmentExists(_$('fragmentId'))], @@ -133,6 +148,47 @@ GlobalEventSupply.prototype.handleEvent = function (e) { /////////////////////////////////////////////////////////////////////////// +function WindowEventSupply(eventType) { + this.eventType = eventType; + this.demandPat = Patch.observe(windowEvent(this.eventType, __)); +} + +WindowEventSupply.prototype.boot = function () { + var self = this; + this.handlerClosure = Dataspace.wrap(function(e) { return self.handleDomEvent(e); }); + this.updateEventListeners(true); + + return Patch.sub(this.demandPat) // track demand + .andThen(Patch.pub(windowEvent(this.eventType, __))) // indicate our presence + ; +}; + +WindowEventSupply.prototype.updateEventListeners = function (install) { + if (install) { + window.addEventListener(cleanEventType(this.eventType), this.handlerClosure); + } else { + window.removeEventListener(cleanEventType(this.eventType), this.handlerClosure); + } +}; + +WindowEventSupply.prototype.trapexit = function () { + console.log('WindowEventSupply trapexit running', this.eventType); + this.updateEventListeners(false); +}; + +WindowEventSupply.prototype.handleDomEvent = function (event) { + Dataspace.send(windowEvent(this.eventType, event)); + return dealWithPreventDefault(this.eventType, event); +}; + +WindowEventSupply.prototype.handleEvent = function (e) { + if (e.type === 'stateChange' && e.patch.project(this.demandPat).hasRemoved()) { + Dataspace.exit(); // trapexit will uninstall event listeners + } +}; + +/////////////////////////////////////////////////////////////////////////// + function UIFragment(fragmentId) { this.fragmentId = fragmentId; this.demandProj = uiFragment(this.fragmentId, _$('selector'), _$('html')); @@ -362,6 +418,7 @@ module.exports.newFragmentId = newFragmentId; module.exports.spawnUIDriver = spawnUIDriver; module.exports.Anchor = Anchor; module.exports.globalEvent = globalEvent; +module.exports.windowEvent = windowEvent; module.exports.uiEvent = uiEvent; module.exports.uiFragment = uiFragment; module.exports.uiFragmentExists = uiFragmentExists;