Coordinator.js is a self-contained JavaScript library. It only exports one variable into the global namespace (namely, coordinator
). The following statement is sufficient to add Coordinator.js to your project, and will be used throughout this demo:
<!-- Imported scripts -->
<script type='text/javascript' src='libraries/coordinator.js'></script>
This demo is supposed to demonstrate coordination of user interactions across multiple objects. The objects in this part of the demo are represented by geometric shapes (rectangles) of different color. These shapes will coordinate both the mode of user interaction ("highlight" event triggered by mouse hover and "filter" event triggered by mouse click) and the identity of the affected shape (namely, its color).
Color blocks used in this example are SVG rectangles built using D3.js. Under the hood, each of the blocks is represented by a JavaScript object. These objects are constructed in bulk using functions that look like the one below:
function buildBlock (...) {
var block = {};
// Block initialization
// ...
return block;
}
This is the example of the most basic use of the coordination mechanism. First, a range of color blocks are built and marked for participating in the coordination framework in the following fashion:
var newBlock = buildBlock(...);
coordinator.signUp(newBlock);
Calling signUp
method of the coordinator
object is the way individual objects let coordinator know they would like to exchange events as part of the coordination process. Each of the objects has to have the following methods and properties built in:
function buildBlock (...) {
var block = {};
// Block initialization
// ...
// Logic for handling an incoming "highlight" event.
block.onHighlight = function (event) {
// Only necessary if "highlight" is listed as one
// of the event types being listened to in the
// coordination metadata property below.
};
// Generic logic for emitting an event.
block.triggerEvent = coordinator.triggerEvent.bind(block);
// Shortcut for emitting a "highlight" event
block.triggerHighlight = function (flag) {
block.triggerEvent("highlight", block.color, flag);
};
// Coordination metadata
block.coordinationMetadata = {
// This section is used by Coordinator.js to
// determine which components should be linked
// together.
listensTo: {
"highlight": block.onHighlight
},
triggers: {
"highlight": {}
}
};
return block;
}
Once all blocks have been built and the last call to signUp
method is done, coordinator
object can be asked to link the color blocks together based on their coordination metadata property. This is done once per application and involves the following command:
coordinator.linkComponents();
As mentioned above, color blocks are visualized using D3. An array of colored blocks called colorBlocks
is passed to D3, and the following code is executed:
svgCanvas.selectAll(".colorBlocks")
.data(colorBlocks)
.enter()
.append("g")
.append("rect")
// Link DOM listeners to event triggers on individual color blocks:
.on("mouseover", function (d, i) { d.triggerHighlight(true) })
.on("mouseout", function (d, i) { d.triggerHighlight(false)})
// Visual attributes of color blocks can be now set:
// ...
D3 code can be ran either before or after the coordination mechanism set up, order is unimportant. Once all of the above is done, the following result can be seen:
Each of the blocks above responds to mouse hover and broadcasts a message outlining that fact to all other objects in its group. They respond by showing a small rectangular marker that has the same color as the affected block.
This example follows the previous one closely, but adds a second type of event (namely, "filter"). Objects can be made to work with richers sets of event types by having more elaborate coordination metadata:
function buildBlock (...) {
var block = {};
// Block initialization
// ...
// Logic for handling an incoming "highlight" event.
block.onHighlight = function (event) {
// Only necessary if "highlight" is listed as one
// of the event types being listened to in the
// coordination metadata property below.
};
// Logic for handling an incoming "filter" event.
block.onFilter = function (event) {
// Only necessary if "filter" is listed as one
// of the event types being listened to in the
// coordination metadata property below.
};
// Generic logic for emitting an event.
block.triggerEvent = coordinator.triggerEvent.bind(block);
// Shortcut for emitting a "highlight" event.
block.triggerHighlight = function (flag) {
block.triggerEvent("highlight", block.color, flag);
};
// Shortcut for emitting a "filter" event.
block.triggerFilter = function (flag) {
block.triggerEvent("filter", block.color, flag)
};
// Coordination metadata
block.coordinationMetadata = {
// This section is used by Coordinator.js to
// determine which components should be linked
// together.
listensTo: {
"highlight": block.onHighlight,
"filter": block.onFilter
},
triggers: {
"highlight": {},
"filter": {}
}
};
return block;
}
Blocks used in this example are built, passed to the signUp
method, linked by calling the linkComponents
method of the coordinator
object, and drawn using D3 in the same way as in the previous example. Once all of it is done, the following result can be seen:
Each of the blocks above responds to mouse hover and broadcasts a message outlining that fact to all other objects in its group, just like in the example above. This time, however, it is possible to click on the block. This event will also be broadcast to all other objects in this group, and they will respond by disappearing, unless they are of the same color as the block that was clicked.
This example demonstrates the use of an eventGate
, a metacomponent that can be built on top of Coordinator.js. Event gates are small, self-contained JavaScript components that encapsulate the messaging logic that is used to communicate between a browser window and a pop-up it has summoned. Although pop-up windows have a way to talk to their parents, they do not have direct access to the JavaScript code that is ran in them, and thus cannot participate in the coordination framework directly. Event gates solve this problem by collecting the Coordinator.js events in their respective window, marshalling them to other windows using browser-specific cross-window communication mechanisms, and re-broadcasting events they receive from other event gates.
Event gates are implemented using a different object-creation pattern than the one used above in order to showcase the flexibility of Coordinator.js. A snippet of code describing how event gates are built is shown below:
function EventGate() {
// Constructor
// ...
// Event coordination
this.onHighlight = this.onHighlight.bind(this);
this.onFilter = this.onFilter.bind(this);
this.coordinationMetadata = {
// This section is used by Coordinator.js to
// determine which components should be linked
// together.
listensTo: {
"highlight": block.onHighlight,
"filter": block.onFilter
},
triggers: {
"highlight": {},
"filter": {}
}
};
// Generic logic for emitting an event.
this.triggerEvent = coordinator.triggerEvent.bind(this);
// Shortcut for emitting a "highlight" event.
this.triggerHighlight = function(objectId, flag) {
this.triggerEvent("highlight", objectId, flag);
};
// Shortcut for emitting a "filter" event.
this.triggerFilter = function(objectId, flag) {
this.triggerEvent("filter", objectId, flag);
};
}
// Logic for handling an incoming "highlight" event.
EventGate.prototype.onHighlight = function (event) {
// Only necessary if "highlight" is listed as one
// of the event types being listened to in the
// coordination metadata property above.
};
// Logic for handling an incoming "filter" event.
EventGate.prototype.onFilter = function (event) {
// Only necessary if "highlight" is listed as one
// of the event types being listened to in the
// coordination metadata property above.
};
For the regular user, however, none of this is necessary. Event gates are added to the user application using the following command:
// Initialize local event gate
coordinator.signUp(new EventGate());
Once all of the above is done, a pop-up can be summoned that will interact with the rest of the application. Click here to spawn a pop-up, play around with the colored blocks it contains, and watch how they affect the colored blocks in the SVG sketch below.
This example demonstrates the use of an networkGate
, another metacomponent built on top of Coordinator.js. Network gates are small, self-contained JavaScript components that encapsulate the messaging logic that is used to communicate between a browser window and a server. This specific example is using WebSockets as the means of communication. Network gates allow the coordination framework to span across networks, platforms, and sandboxed browser instances. Network gates achieve this problem by collecting the Coordinator.js events in their respective windows, marshalling them to an intermediary WebSocket server set up in a cooperating location, and re-broadcasting events they receive from the said server.
A snippet of code describing how event gates are built is shown below:
var networkGate = {};
// Logic for handling an incoming "highlight" event.
networkGate.onHighlight = function (event) {
// Only necessary if "highlight" is listed as one
// of the event types being listened to in the
// coordination metadata property below.
};
// Logic for handling an incoming "filter" event.
networkGate.onFilter = function (event) {
// Only necessary if "filter" is listed as one
// of the event types being listened to in the
// coordination metadata property below.
};
networkGate.coordinationMetadata = {
// This section is used by Coordinator.js to
// determine which components should be linked
// together.
listeners: {
"highlight": networkGate.onHighlight,
"filter": networkGate.onFilter
},
triggers: {
"highlight": {},
"filter": {}
}
};
// Generic logic for emitting an event.
networkGate.triggerEvent = coordinator.triggerEvent.bind(networkGate);
// Networking functionality is added to the networkGate object below
// ...
Similar to the event gate, none of this is necessary for the regular user. Network gates are added to the user application using the following command:
// Initialize network gate
coordinator.signUp(networkGate.init("000.000.000.000", 80));
Using a new browser window, a new tab, or a different computer altogether, open this link. Play around with the colored blocks it contains, and watch how they affect the colored blocks in the SVG sketch below. This example requires the intermediary WebSocket server to be set up.
This concludes the first part of this demo.