Skip to content

Instantly share code, notes, and snippets.

@pasaran
Last active December 13, 2015 22:48
Show Gist options
  • Save pasaran/4986711 to your computer and use it in GitHub Desktop.
Save pasaran/4986711 to your computer and use it in GitHub Desktop.

Черновой прототип для выбора "мастер"-вкладки. Например, мастер-вкладка будет держать один websocket-коннект к серверу, а остальные вкладки с тем же сайтом, будут его использовать. Что делать, если мастер-вкладка вдруг закрыта? Нужно выбрать новую.

После клонирования репозитория, нужно поднять над этими файлами какой-нибудь веб-сервер, например так:

python -m SimpleHTTPServer 8000

После чего, можно открывать вкладки по адресу http://127.0.0.1:8000. У вкладки отображается ее id, у мастер-вкладки он будет красного цвета. Если мастер-вкладку закрыть, то в течение 5 секунд вкладка с минимальным среди оставшихся вкладок id станет мастером.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Выборы master вкладки</title>
<script src="http://yandex.st/jquery/1.9.1/jquery.js"></script>
<script src="vote.js"></script>
<style>
body {
margin: 40px;
}
.id {
font-size: 10em;
color: #eee;
}
.is-master {
color: #c00;
}
</style>
</head>
<body>
<div class="id"></div>
</body>
</html>
// id текущей вкладки.
var id;
// Является ли текущая вкладка мастером.
var is_master = false;
// Неизвестный пока id мастер-вкладки.
var master_id = null;
var ids;
var handler;
$(function() {
// Максимальный использованный id лежит в localStorage с ключом 'id'.
// Генерим новый id и сохраняем его в localStorage.
id = ( +get('id') || 0 ) + 1;
set('id', id);
$('.id').text(id);
// Эта функция запускается раз в 5 секунд.
function check() {
if (is_master) { return; }
master_id = null;
// Сюда будем собирать id-шники всех откликнувшихся вкладок.
ids = [ id ];
// Кидаем глобальный 'ping'. Все вкладки (кроме этой) должны на него ответить
// событием 'pong' со своим id-шником.
trigger('ping');
// Если через 50 мс мастер-вкладка не объявится, то проводим выборы.
handler = setTimeout(function() {
if (!master_id) {
// Выбираем минимальный id-шник из всех открытых вкладок.
var min_id = ids.reduce( function(x, y) { return Math.min(x, y); } );
// Если это мы и есть, объявляем себя мастером.
if (min_id === id) {
is_master = true;
// Сообщаем всем, что мы и есть мастер.
trigger('master', id);
$('.id').addClass('is-master');
} else {
$('.id').removeClass('is-master');
}
}
}, 50);
}
setInterval(check, 5000);
check();
});
// --------------------------------------------------------------------------------------------------------------- //
var events = {}
// Если пришел 'ping', отвечаем 'pong'-ом со своим id.
events.ping = function() {
// Если мы еще и мастер впридачу, объявляем об этом всем остальным.
if (is_master) {
trigger('master', id);
}
trigger('pong', id);
};
// Коллекционируем все пришедшие id-шники.
events.pong = function(id) {
ids.push(+id);
};
// Объявление о том, что мастер где-то там есть.
events.master = function(id) {
master_id = +id
if (handler) {
clearTimeout(handler);
}
};
// --------------------------------------------------------------------------------------------------------------- //
// Тут дальше магия на тему общения между вкладками.
// Чтобы послать всем событие 'foo', создаем (или изменяем) в localStorage'е
// ключ foo#event. При посылке значения 'bar' дописываем к нему авто-инкрементный счетчик
// (иначе может не случиться событие 'storage'.
// FIXME: Вешать обработчик через jQuery, чтобы работало и в IE.
//
window.addEventListener('storage', function(e) {
// Получаем реальное название события (выкидываем #event).
var name = e.key.replace(/#.*$/, '');
var handler = events[name];
if (!handler) { return; }
// Отрезаем от значения счетчик.
var value = e.newValue.replace(/#.*$/, '');
// Зовем "обработчик" события.
handler(value);
}, false);
function trigger(name, value) {
// Генерим ключ в localStorage для события name.
var key = name + '#event';
// В старом значении (если есть) ищем текущее значение счетчика.
var oldValue = get(key);
var counter = (oldValue) ? +oldValue.replace(/^.*#/, '') : 0;
// "Отправляем" значение с увеличенным счетчиком.
set( key, (value || '') + '#' + (counter + 1) );
}
// --------------------------------------------------------------------------------------------------------------- //
// Шоткаты для доступа к localStorage.
function get(key) {
return localStorage.getItem(key);
}
function set(key, value) {
localStorage.setItem(key, value);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment