wormhole.js — общение между табами/окнами браузера
Нужно общаться между вкладками/окнами браузера.
А ещё различать, кто сейчас master.
#!+ // tab.js
#!- var worker = new SharedWorker("shared-resource.js");
#!+ worker.port.addEventListener("message", function (evt) {
console.log(evt.data);
#!- });
#! worker.port.start();
#!+ // shared-resource.js
var ports = [];
window.onconnect = function (evt) {
var port = evt.ports[0];
ports.push(port); // добавляем port
#!+ port.onmessage = function (evt) {
#! broadcast(evt.data); // рассылаем всем табам
#!- };
#!- };
// shared-resource.js
function broadcast(data) {
ports.forEach(function (port) {
port.postMessage(data);
});
}
// tab.js
// ... создаем воркер
// Отправляем сообщение через воркер
worker.port.postMessage("any data");
// теперь все табы получат переданные данные
// tab.js
#!+ // Индекс последнего события
#!- var lastIndex = localStorage.getItem("index")|0;
#!+ function emit(data) {
#! var events = localStorage.getItem("events") || [];
#! var nextIndex = (localStorage.getItem("index")|0) + 1;
#!+ events.push({
index: nextIndex,
data: data
#!- });
#! localStorage.setItem("index", nextIndex);
#! localStorage.setItem("events", JSON.stringify(events));
#!- }
// tab.js
// Подписываемся на изменение хранилища
window.onstorage = function (evt) {
#!+ if (evt.key === "events") {
#! var events = JSON.parse(localStorage.getItem(evt.key));
#!+ events.forEach(function (evt) {
#!+ if (evt.idx > lastIndex) {
// Актуальное для нас событие
#! lastIndex = evt.idx;
#! console.log(evt.data);
#!- }
#!- });
#!- }
};
Куки, как куки...
Стандартное решение решение на основе localStorage.
// X.js
#!+ wormhole().on("message", function (data) {
console.log(data);
#!- });
#!+ wormhole().on("peers", function (peers) {
console.log(peers); // ["X"], а потом ["X", "Y"]
#!- });
// Y.js
wormhole().emit("message", "any data");
#!+ wormhole().on("master", function () {
console.log("Wow! I'm master!");
#!- });
#!+ // регистрируем команду `foo`
wormhole()["foo"] = function (array, next) {
#! next(null, array.reverse());
#!- };
#!+ // Выполняем команду (на мастере)
wormhole().call("foo", [1, 2, 3], function (err, result) {
console.log(result);
#!- });
// `meta` — мета информация
{
#! id: null, // идентификатор мастера
#! ts: 0, // время последнего обновления
#! peers: {..} // список табов и время обновления (id => ts)
}
// `queue` — очередь событий
{
#! idx: 0, // текущий индекс события
#! items: [] // массив событий
}
function emit(type, args) {
var queue = this._store("queue");
#!+ queue.items.push({
#! idx: ++queue.idx, // увеличим индекс
#! type: type,
#! args: args,
#! source: this.id // идентификатор «дырки»
#!- });
#! this._store("queue", queue);
#! this._processingQueue(queue.items);
}
// Обработка изменения в хранилище
#!+ store.on("change", function (key, data) {
#!+ if (key === "queue") {
this._processingQueue(queue.items);
#!- }
#!+ else if (key === "meta") {
this._checkMeta();
#!- }
#!- }.bind(this));
function _processingQueue(queue) {
for (var i = 0, n = queue.length, evt; i < n; i++) {
evt = queue[i];
#!+ if (this._idx < evt.idx) {
#! this._idx = evt.idx;
#! _emitterEmit.call(this, evt.type, evt.args);
#!- }
}
}