(function () { var socket, attempts, attemptTimer, pingTimer; window.send = function (msg) { // need deferral or reporting on these lost messages... if (connSM.state != 'synced' && connSM.state != 'syncing') return; if (socket.readyState != SockJS.OPEN) { if (console) console.warn("Attempting to send while socket closed"); return; } msg = JSON.stringify(msg); if (DEBUG) console.log('<', msg); socket.send(msg); }; var pong = '[[0,' + PING + ']]'; function on_message(e) { if (e == pong) return; if (DEBUG) console.log('>', e.data); var msgs = JSON.parse(e.data); with_dom(function () { for (var i = 0; i < msgs.length; i++) { var msg = msgs[i]; var op = msg.shift(); var type = msg.shift(); if (is_pubsub(type) && op in syncs) syncs[op]++; dispatcher[type](msg, op); } }); } function sync_status(msg, hover) { $('#sync').text(msg).attr('class', hover ? 'error' : ''); } connSM.act('load + start -> conn', function () { sync_status('Connecting...', false); attempts = 0; connect(); }); function connect() { if (socket) { socket.onclose = null; socket.onmessage = null; } if (window.location.protocol == 'file:') { console.log("Page downloaded locally; refusing to sync."); return; } socket = window.new_socket(attempts); socket.onopen = connSM.feeder('open'); socket.onclose = connSM.feeder('close'); socket.onmessage = on_message; if (DEBUG) window.socket = socket; } window.new_socket = function (attempt) { var protocols = ['xdr-streaming', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling']; if (config.USE_WEBSOCKETS) protocols.unshift('websocket'); var url = SOCKET_PATH; if (typeof ctoken != 'undefined') { url += '?' + $.param({ctoken: ctoken}); } return new SockJS(url, null, { transports: protocols, }); }; connSM.act('conn, reconn + open -> syncing', function () { sync_status('Syncing...', false); CONN_ID = random_id(); send([SYNCHRONIZE, CONN_ID, BOARD, syncs, BUMP, document.cookie]); if (pingTimer) clearInterval(pingTimer); pingTimer = setInterval(ping, 25000); }); connSM.act('syncing + sync -> synced', function () { sync_status('Synced.', false); attemptTimer = setTimeout(function () { attemptTimer = 0; reset_attempts(); }, 10000); }); function reset_attempts() { if (attemptTimer) { clearTimeout(attemptTimer); attemptTimer = 0; } attempts = 0; } connSM.act('* + close -> dropped', function (e) { if (socket) { socket.onclose = null; socket.onmessage = null; } if (DEBUG) console.error('E:', e); if (attemptTimer) { clearTimeout(attemptTimer); attemptTimer = 0; } if (pingTimer) { clearInterval(pingTimer); pingTimer = 0; } sync_status('Dropped.', true); attempts++; var n = Math.min(Math.floor(attempts/2), 12); var wait = 500 * Math.pow(1.5, n); // wait maxes out at ~1min setTimeout(connSM.feeder('retry'), wait); }); connSM.act('dropped + retry -> reconn', function () { connect(); /* Don't show this immediately so we don't thrash on network loss */ setTimeout(function () { if (connSM.state == 'reconn') sync_status('Reconnecting...', true); }, 100); }); connSM.act('* + invalid, desynced + close -> desynced', function (msg) { msg = (msg && msg[0]) ? 'Out of sync: ' + msg[0] : 'Out of sync.'; sync_status(msg, true); if (attemptTimer) { clearTimeout(attemptTimer); attemptTimer = 0; } socket.onclose = null; socket.onmessage = null; socket.close(); socket = null; if (DEBUG) window.socket = null; }); function window_focused() { var s = connSM.state; if (s == 'desynced') return; // might have just been suspended; // try to get our SM up to date if possible if (s == 'synced' || s == 'syncing' || s == 'conn') { var rs = socket.readyState; if (rs != SockJS.OPEN && rs != SockJS.CONNECTING) { connSM.feed('close'); return; } else if (navigator.onLine === false) { connSM.feed('close'); return; } ping(); } connSM.feed('retry'); } function ping() { if (socket.readyState == SockJS.OPEN) socket.send('['+PING+']'); else if (pingTimer) { clearInterval(pingTimer); pingTimer = 0; } } (function () { _.defer(connSM.feeder('start')); $(window).focus(function () { setTimeout(window_focused, 20); }); window.addEventListener('online', function () { reset_attempts(); connSM.feed('retry'); }); window.addEventListener('offline', connSM.feeder('close')); })(); })();