Skip to content
This repository was archived by the owner on Oct 1, 2024. It is now read-only.

Commit 2d701c4

Browse files
committed
Python DOM library shaping up.
1 parent b9aa7ed commit 2d701c4

9 files changed

+682
-33
lines changed

Diff for: .gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
*.swp
22
*.min.js
3+
.coverage
4+
__pycache__

Diff for: Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ all:
66
@echo "make test - while serving the app, run the test suite in browser."
77
@echo "make minify - minify the project."
88

9-
test:
9+
testjs:
1010
python -m webbrowser "SpecRunner.html"
1111

12+
testpy:
13+
pytest -v --random-order --cov-report term-missing --cov=polyplug tests/
14+
1215
minify:
1316
uglifyjs polyplug.js --compress --mangle -o polyplug.min.js

Diff for: README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ This is the way:
2424
This project was created for research purposes as part of the efforts to build
2525
[PyScript](https://pyscript.net).
2626

27+
There are two sides to PolyPlug:
28+
29+
* The `polyplug.js` code to be run in the main thread of the browser.
30+
* Code, to be run in the interpreter of the scripting language, to communicate
31+
with PolyPlug. Currently only `polyplug.py` exists as a reference
32+
implementation.
33+
2734
That is all.
2835

2936
## Developer setup
@@ -35,7 +42,8 @@ $ make
3542
There's no default Makefile target right now. Try:
3643
3744
make minify - minify the source.
38-
make test - run the test suite.
45+
make testjs - run the JavaScript test suite.
46+
make testpy - run the Python test suite.
3947
```
4048

4149
## Running the tests
@@ -44,6 +52,9 @@ For the sake of simplicity (and familiarity) we use the
4452
[Jasmine test framework](https://jasmine.github.io/index.html) to exercise the
4553
JavaScript aspects of our code.
4654

55+
For similar reasons, we use [PyTest](https://pytest.org/) to exercise the
56+
Pythonic aspects of our code.
57+
4758
## How it works
4859

4960
Methods of the object returned by the `polyplug` function facilitate all

Diff for: polyplug.js

+94-31
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ limitations under the License.
3636
******************************************************************************/
3737

3838
const 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

Comments
 (0)