diff --git a/scribblings/cs19.scrbl b/scribblings/cs19.scrbl index e7f1ece..799965f 100644 --- a/scribblings/cs19.scrbl +++ b/scribblings/cs19.scrbl @@ -79,6 +79,10 @@ functions. One difference introduced by the web is the web page itself: because the page itself is a source of state, it too will be passed to callbacks. +The world-updating callbacks may optionally take an @tech{event} object, which +provides additional information about the event that triggered the callback. + + This library presents a functional version of the DOM in the form of a @emph{view}. @@ -92,9 +96,30 @@ Provide an initial view for the big-bang.} @defproc[(stop-when [stop? ([w world] [dom view] -> boolean)]) big-bang-handler]{ Tells @racket[big-bang] the predicate for termination. } -@defproc[(on-tick [tick-f ([w world] [v view] -> world)]) big-bang-handler]{ +@defproc[(on-tick [tick-f ([w world] [v view] [e event]? -> world)]) big-bang-handler]{ Tells @racket[big-bang] to update the world during clock ticks. } + + +@defproc[(on-mock-location-change [location-f ([w world] [v view] [e event]? -> world)]) big-bang-handler]{ +Tells @racket[big-bang] to update the world during simulated movement. + +During the extent of a big-bang, a form widget will appear in the +@tt{document.body} to allow you to manually send location-changing +events. + +} + + +@defproc[(on-location-change [location-f ([w world] [v view] [e event]? -> world)]) big-bang-handler]{ +Tells @racket[big-bang] to update when the location changes, as +received by the +@link["http://dev.w3.org/geo/api/spec-source.html"]{Geolocation API}. +} + + + + @defproc[(to-draw [draw-f ([w world] [v view] -> view)]) big-bang-handler]{ Tells @racket[big-bang] how to update the rendering of the world. } @@ -133,7 +158,7 @@ Get the textual content at the focus. @defproc[(update-view-text [v view] [s string]) view]{ Update the textual content at the focus.} -@defproc[(view-bind [v view] [type string] [world-updater ([w world] [v view] -> world)]) view]{ +@defproc[(view-bind [v view] [type string] [world-updater ([w world] [v view] [e event]? -> world)]) view]{ Attach a world-updating event to the focus. Common event types include @racket["click"], @racket["trigger"], @@ -169,6 +194,9 @@ Add the dom node @racket[d] as the last child of the focused node.} +@subsection{Events} +An @deftech{event} is a collection of name-value pairs. + diff --git a/web-world/impl.rkt b/web-world/impl.rkt index ee2634a..27fbe24 100644 --- a/web-world/impl.rkt +++ b/web-world/impl.rkt @@ -20,6 +20,12 @@ ;; clock tick handler on-tick + ;; location changes + on-mock-location-change + + ;; location changes (for real!) + on-location-change + ;; draw and update the view to-draw diff --git a/web-world/js-impl.js b/web-world/js-impl.js index 4cb5796..8fe78be 100644 --- a/web-world/js-impl.js +++ b/web-world/js-impl.js @@ -657,6 +657,91 @@ }; + + + var MockLocationEventSource = function() { + this.elt = undefined; + }; + MockLocationEventSource.prototype = plt.baselib.heir(EventSource.prototype); + MockLocationEventSource.prototype.onStart = function(fireEvent) { + var mockLocationSetter = document.createElement("div"); + + var latInput = document.createElement("input"); + latInput.type = "text"; + + var latOutput = document.createElement("input"); + latOutput.type = "text"; + + var submitButton = document.createElement("input"); + submitButton.type = "button"; + submitButton.value = "send lat/lng"; + submitButton.onclick = function() { + fireEvent(undefined, + { latitude : plt.baselib.numbers.makeFloat(latInput.value), + longitude : plt.baselib.numbers.makeFloat(latOutput.value) }); + return false; + }; + + mockLocationSetter.style.border = "1pt solid black"; + mockLocationSetter.appendChild( + document.createTextNode("mock location setter")); + mockLocationSetter.appendChild(latInput); + mockLocationSetter.appendChild(latOutput); + mockLocationSetter.appendChild(submitButton); + document.body.appendChild(mockLocationSetter); + + this.elt = mockLocationSetter; + }; + + MockLocationEventSource.prototype.onStop = function() { + if (this.elt !== undefined) { + document.body.removeChild(mockLocationSetter); + this.elt = undefined; + }; + }; + + + + + + // This version really does use the geolocation object. + var LocationEventSource = function() { + this.id = undefined; + }; + + LocationEventSource.prototype = plt.baselib.heir(EventSource.prototype); + + LocationEventSource.prototype.onStart = function(fireEvent) { + var success = function(position) { + fireEvent(undefined, + { latitude : plt.baselib.numbers.makeFloat(position.coords.latitude), + longitude: plt.baselib.numbers.makeFloat(position.coords.longitude) }; + }; + var fail = function(err) { + // Quiet failure + } + if (!!navigator.geolocation) { + this.id = navigator.geolocation.watchPosition(success, fail); + } + }; + + LocationEventSource.prototype.onStop = function() { + if (this.id !== undefined) { + navigator.geolocation.clearWatch(this.id); + this.id = undefined; + }; + }; + + + + + + + + + + + // DomElementSource: string (U DOM string) -> EventSource // A DomEventSource allows DOM elements to send events over to // web-world. @@ -1273,5 +1358,28 @@ + EXPORTS['on-location-change'] = makePrimitiveProcedure( + 'on-location-change', + 1, + function(MACHINE) { + var onChange = wrapFunction(checkProcedure(MACHINE, 'on-location-change', 0)); + return new EventHandler('on-location-change', + new LocationEventSource(), + onChange); + }); + + + EXPORTS['on-mock-location-change'] = makePrimitiveProcedure( + 'on-mock-location-change', + 1, + function(MACHINE) { + var onChange = wrapFunction(checkProcedure(MACHINE, 'on-mock-location-change', 0)); + return new EventHandler('on-mock-location-change', + new MockLocationEventSource(), + onChange); + }); + + + ////////////////////////////////////////////////////////////////////// }()); \ No newline at end of file diff --git a/web-world/racket-impl.rkt b/web-world/racket-impl.rkt index 64593c2..c1cb35b 100644 --- a/web-world/racket-impl.rkt +++ b/web-world/racket-impl.rkt @@ -1,6 +1,10 @@ #lang racket/base -(provide big-bang initial-view stop-when on-tick to-draw +(provide big-bang initial-view stop-when + on-tick + on-location-change on-mock-location-change + to-draw + ->view view-focus view-left view-right view-up view-down @@ -33,6 +37,22 @@ [(f delay) (error 'on-tick "Please run in JavaScript context.")])) + +(define on-location-change + (case-lambda [(f) + (error 'on-location-change "Please run in JavaScript context.")] + [(f delay) + (error 'on-location-change "Please run in JavaScript context.")])) + + +(define on-mock-location-change + (case-lambda [(f) + (error 'on-mock-location-change "Please run in JavaScript context.")] + [(f delay) + (error 'on-mock-location-change "Please run in JavaScript context.")])) + + + (define (to-draw w) (error 'to-draw "Please run in JavaScript context.")) diff --git a/web-world/where-am-i/index.html b/web-world/where-am-i/index.html new file mode 100644 index 0000000..d96e4ea --- /dev/null +++ b/web-world/where-am-i/index.html @@ -0,0 +1,9 @@ + +
+ I am at: . + The mock location says: . +
+ + diff --git a/web-world/where-am-i/where-am-i.rkt b/web-world/where-am-i/where-am-i.rkt new file mode 100644 index 0000000..bd0d3ca --- /dev/null +++ b/web-world/where-am-i/where-am-i.rkt @@ -0,0 +1,28 @@ +#lang planet dyoo/whalesong +(require (planet dyoo/whalesong/web-world) + (planet dyoo/whalesong/resource)) + +(define-resource index.html) + +(define-struct coord (lat lng)) +(define-struct world (real mock)) + + + +(define (location-change world dom evt) + world) + + +(define (mock-location-change world dom evt) + world) + + +(define (draw world dom) + dom) + + +(big-bang (make-world 'unknown 'unknown) + (initial-view index.html) + (to-draw draw) + (on-location-change location-change) + (on-mock-location-change mock-location-change)) \ No newline at end of file