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); #!- } } }