@@ -36,6 +36,14 @@ limitations under the License.
3636******************************************************************************/
3737
3838const polyplug = function ( ) {
39+
40+ /**************************************************************************
41+ Internal state for PolyPlug.
42+ **************************************************************************/
43+
44+ // Tracks event handler functions.
45+ const REGISTERED_EVENTS = { } ;
46+
3947 /**************************************************************************
4048 Serialization functions for PolyPlug.
4149 **************************************************************************/
@@ -330,6 +338,14 @@ const polyplug = function() {
330338 Event handling functions for PolyPlug.
331339 **************************************************************************/
332340
341+ function getEventKey ( query , eventType , listener ) {
342+ /*
343+ Return a unique key to identify a query/eventType/listener combination.
344+ */
345+ const sortedQuery = Object . keys ( query ) . sort ( ) . reduce ( ( result , key ) => ( result [ key ] = query [ key ] , result ) , { } ) ;
346+ return JSON . stringify ( [ sortedQuery , eventType , listener ] ) ;
347+ }
348+
333349 function registerEvent ( query , eventType , listener ) {
334350 /*
335351 Register an event listener, given:
@@ -346,19 +362,41 @@ const polyplug = function() {
346362 expected function in the most appropriate way.
347363 */
348364 const elements = getElements ( query ) ;
349- elements . forEach ( function ( element ) {
350- element . addEventListener ( eventType , function ( e ) {
351- const detail = JSON . stringify ( {
352- type : e . type ,
353- target : nodeToJS ( e . target ) ,
354- listener : listener
355- } ) ;
356- const send = new CustomEvent ( "polyplugSend" , { detail : detail } ) ;
357- document . dispatchEvent ( send ) ;
365+ const eventHandler = function ( e ) {
366+ const detail = JSON . stringify ( {
367+ type : e . type ,
368+ target : nodeToJS ( e . target ) ,
369+ listener : listener
358370 } ) ;
371+ const send = new CustomEvent ( "polyplugSend" , { detail : detail } ) ;
372+ document . dispatchEvent ( send ) ;
373+ }
374+ const eventKey = getEventKey ( query , eventType , listener ) ;
375+ REGISTERED_EVENTS [ eventKey ] = eventHandler ;
376+ elements . forEach ( function ( element ) {
377+ element . addEventListener ( eventType , eventHandler ) ;
359378 } ) ;
360379 }
361380
381+ function removeEvent ( query , eventType , listener ) {
382+ /*
383+ Remove an event listener, given:
384+
385+ * target element[s] via a query object (see getElements),
386+ * the event type (e.g. "click"), and,
387+ * the name of the listener to call in the remote interpreter.
388+ */
389+ const elements = getElements ( query ) ;
390+ const eventKey = getEventKey ( query , eventType , listener ) ;
391+ const eventHandler = REGISTERED_EVENTS [ eventKey ] ;
392+ if ( eventHandler ) {
393+ elements . forEach ( function ( element ) {
394+ element . removeEventListener ( eventType , eventHandler ) ;
395+ } ) ;
396+ delete REGISTERED_EVENTS [ eventKey ] ;
397+ }
398+ }
399+
362400 /**************************************************************************
363401 Message handling functions for PolyPlug.
364402 **************************************************************************/
@@ -368,27 +406,34 @@ const polyplug = function() {
368406 Receive a raw message string (containing JSON). Deserialize it and
369407 dispatch the message to the appropriate handler function.
370408 */
371- const message = JSON . parse ( raw ) ;
372- switch ( message . type ) {
373- case "updateDOM" :
374- onUpdateDOM ( message ) ;
375- break ;
376- case "registerEvent" :
377- onRegisterEvent ( message ) ;
378- break ;
379- case "stdout" :
380- onStdout ( message ) ;
381- break ;
382- case "stderr" :
383- onStderr ( message ) ;
384- break ;
385- case "error" :
386- onError ( message ) ;
387- break ;
388- default :
389- console . log ( "Unknown message type." )
390- console . log ( message )
391- break ;
409+ try {
410+ const message = JSON . parse ( raw ) ;
411+ switch ( message . type ) {
412+ case "updateDOM" :
413+ onUpdateDOM ( message ) ;
414+ break ;
415+ case "registerEvent" :
416+ onRegisterEvent ( message ) ;
417+ break ;
418+ case "removeEvent" :
419+ onRemoveEvent ( message ) ;
420+ break
421+ case "stdout" :
422+ onStdout ( message ) ;
423+ break ;
424+ case "stderr" :
425+ onStderr ( message ) ;
426+ break ;
427+ case "error" :
428+ onError ( message ) ;
429+ break ;
430+ default :
431+ console . log ( "Unknown message type." )
432+ console . log ( message )
433+ break ;
434+ }
435+ } catch ( error ) {
436+ onError ( error ) ;
392437 }
393438 }
394439
@@ -442,6 +487,24 @@ const polyplug = function() {
442487 registerEvent ( msg . query , msg . eventType , msg . listener ) ;
443488 }
444489
490+ function onRemoveEvent ( msg ) {
491+ /*
492+ Handle requests to unbind the referenced event type on the elements
493+ matched by the query.
494+
495+ Sample message:
496+
497+ msg = {
498+ type: "removeEvent",
499+ query: {
500+ id: "idOfDomElement"
501+ },
502+ eventType: "click",
503+ }
504+ */
505+ removeEvent ( msg . query , msg . eventType ) ;
506+ }
507+
445508 function onStdout ( msg ) {
446509 /*
447510 Handle "normal" STDOUT output.
@@ -505,7 +568,7 @@ const polyplug = function() {
505568 mutate : mutate ,
506569 getElements : getElements ,
507570 registerEvent : registerEvent ,
571+ removeEvent : removeEvent ,
508572 receiveMessage : receiveMessage
509573 }
510574} ;
511-
0 commit comments