There are a few pieces to the puzzle. You can easily find out which window opened your encyclopædia (it's in window.opener) and send a message that way, and you can then intercept it there with a pre-registered event listener and react to it.
On the "encyclopædia" side:
if(window.opener) {
window.opener.postMessage("GET_DATA", "*");
}
On the "game" side (and using the "openOther" function similar to @Chapel's answer):
setup.openOther = function () {
var args = [].slice.call(arguments);
var url = args.shift();
var newWindowTwine = open(url);
// TODO: What to do with the arguments now?
};
function receiveMessage(event)
{
if(event.data === "GET_DATA") {
// TODO: Implement this
}
}
window.addEventListener("message", receiveMessage, false);
Now ... this is one-way communication. You can send data, but you can't receive any, and the receiving side can mutate the data all it wants, it won't be reflected on the sending side. There are also limits to what kind of data you can send, though plain JavaScript values, objects and arrays work fine. Still - with just this you can already implement dynamic loading of passages from external files, for example. Just send the passage's HTML code over as a string.
To get data from the "game" window back to "encyclopædia" window, we need to send a message back. Thankfully, the event already tells us where to do it, via event.source. We also need to register some event handler on the "encyclopædia" side of things, so let's start with this:
function receiveMessage(event)
{
State.variables.params = event.data;
// TODO: How to make the current passage aware of the changed data?
}
window.addEventListener("message", receiveMessage, false);
if(window.opener) {
window.opener.postMessage("GET_DATA", "*");
}
The order is important here; we need to have the event listener registered before we ask for data to be sent to that event listener. Now let's implement the data sending. We need to temporarily save the arguments-to-send, then send them when the loaded page requests them (but only once, in case the user reloads the other page):
setup.openOtherData = new Map();
setup.openOther = function () {
var args = [].slice.call(arguments);
var url = args.shift();
var newWindowTwine = open(url);
setup.openOtherData.set(newWindowTwine, args);
};
function receiveMessage(event)
{
if(event.data === "GET_DATA") {
if(setup.openOtherData.has(event.source)) {
event.source.postMessage(setup.openOtherData.get(event.source), "*");
setup.openOtherData.delete(event.source);
}
}
}
window.addEventListener("message", receiveMessage, false);
Thankfully, the last TODO on our list is easy: Just put in Engine.show() in there to re-render the current passage, or Engine.play(...) if you want to go to some passage based on the data received from the other window.
This works just fine directly from the file system and across the network. It works without problems even when the two stories are on different domains, or when one's a local file and one on a server somewhere else. The amount of data transferred is only really limited by your available RAM; you can easily push gigabytes of data either way.