UNPKG

gaf-mobile

Version:

GAF mobile Web site

355 lines (309 loc) 11.8 kB
/* global SockJS */ (function() { 'use strict'; var build_libnotify = function() { var socket; var is_ready = false; var ready_list = []; var registered_functions = []; var registered_filters = []; var max_seen_packets = 5; var seen_packets = { 'last': undefined, 'first': undefined, 'packets': {} }; var job_channels; var reloadedTimestamp; var add_packet_no_delete = function(id, packet_id) { seen_packets.packets[id] = { 'id': packet_id, 'next': undefined }; seen_packets.packets[seen_packets.first].next = id; seen_packets.first = id; }; var add_packet = function(id) { var last = seen_packets.last, last_packet = seen_packets.packets[last], new_last = last_packet.next; add_packet_no_delete(id, id); seen_packets.last = new_last; delete seen_packets.packets[last]; }; var new_packet = function(id) { // Packet is only considered seen if it exists // and if it's not a placeholder return !(seen_packets.packets[id] && seen_packets.packets[id].id === id); }; (function() { var i; seen_packets.packets[0] = { 'id': false, 'next': undefined }; seen_packets.first = 0; seen_packets.last = 0; for (i = 1; i <= max_seen_packets; i++) { add_packet_no_delete(i, false); } }()); var extend = function ( defaults, options ) { var extended = {}; var prop; for (prop in defaults) { if (Object.prototype.hasOwnProperty.call(defaults, prop)) { extended[prop] = defaults[prop]; } } for (prop in options) { if (Object.prototype.hasOwnProperty.call(options, prop)) { extended[prop] = options[prop]; } } return extended; }; var message_received = function(obj) { var key = obj.parent_type + ':' + obj.type; if (new_packet(obj.id)) { add_packet(obj.id); var allowed = true; // TODO: Register filters when user_jobs is available // registered_filters[key].forEach(function(i, e) { // if (e(obj) === false) { // allowed = false; // return false; // } // }); if (!allowed) { return false; } if (registered_functions[key]) { registered_functions[key].forEach(function(e) { var obj_copy = extend({}, obj); e(obj_copy); }); } } }; var register = function(parent_type, channel, callback) { var key = parent_type + ':' + channel; if (registered_functions[key]) { var callback_exists = registered_functions[key].filter( function(cb) { return cb === callback; }).length !== 0; if (!callback_exists){ registered_functions[key].push(callback); } } else { registered_functions[key] = new Array(callback); } return this; }; var register_filter = function(parent_type, channel, callback) { var key = parent_type + ':' + channel; if (registered_filters[key]) { var callback_exists = registered_functions[key].filter( function(cb) { return cb === callback; }).length !== 0; if (!callback_exists){ registered_filters[key].push(callback); } } else { registered_filters[key] = new Array(callback); } return this; }; var ready = function(f, args, context){ context = context || window; args = args || []; if (is_ready){ f.apply(context, args); } else { ready_list.push({'f': f, 'args': args, 'context': context}); } }; var set_ready = function(){ for (var i = 0; i < ready_list.length; i++){ var obj = ready_list[i]; obj.f.apply(obj.context, obj.args); } ready_list = []; }; var send = function(channel, data){ socket.send(JSON.stringify({ channel: channel, body: data })); }; var close_socket = function() { if (socket) { socket.close(); } }; var set_job_channel = function(channels) { if (typeof channels !== 'undefined') { if (typeof job_channels === 'undefined' || (job_channels.join('') !== channels.join(''))) { job_channels = channels; close_socket(); } } }; var get_job_channel = function() { if (typeof job_channels === 'undefined') { job_channels = [1, 2]; } return job_channels; }; var Backoff = function(name, reset_time, first_timeout) { this.name = name; // Is this the first time its failed this.first_fail = true; // How long is the wait for first failure this.first_timeout = first_timeout; // The current failure wait time this.wait_time = reset_time; // The base failure wait time this.reset_time = reset_time; }; Backoff.prototype.fail = function() { var delay; if (this.first_fail) { this.first_fail = false; delay = this.first_timeout; } else { delay = this.wait_time; this.wait_time *= 2; this.wait_time = Math.min(this.wait_time, 60000); } console.log(this.name + ' Failed retrying in ' + delay); return delay; }; Backoff.prototype.ok = function() { if (!this.first_fail) { console.log(this.name + ' Recovered'); if (!reloadedTimestamp || reloadedTimestamp + 5000 < Date.now()) { setTimeout(function() { var event; var eventString = 'libnotify.connected'; try { event = new Event(eventString); } catch (error) { event = document.createEvent('Event'); event.initEvent(eventString, true, false); } document.dispatchEvent(event); reloadedTimestamp = Date.now(); // Spread the request from 0 to 30 seconds }, Math.floor(Math.random() * 30 * 1000)); } } this.first_fail = true; this.wait_time = this.reset_time; }; function getCookie(c_name) { var c_start, c_end; var cookies = document.cookie; if (cookies.length>0) { c_start = cookies.indexOf(c_name + '='); if (c_start !== -1) { c_start = c_start + c_name.length+1; c_end = cookies.indexOf(';',c_start); if (c_end === -1) { c_end = cookies.length; } return cookies.substring(c_start,c_end); } } return ''; } var isLoggedIn = false; var init = function(auth_fail_callback) { var connect_backoff = new Backoff('sockJS Connect', 1000, 200), reconnect_backoff = new Backoff('sockJS Reconnect', 1000, 200), auth_backoff = new Backoff('sockJS Auth', 4000, 500); var cookies = { 'hash': getCookie('GETAFREE_AUTH_HASH'), 'hash2': getCookie('GETAFREE_AUTH_HASH_V2'), 'user_id': getCookie('GETAFREE_USER_ID'), 'channels': get_job_channel() }; // Dont talk to node if not loged in if (cookies.hash2 === '' || cookies.user_id === '') { return false; } else { isLoggedIn = true; } var new_socket = function() { socket = new SockJS('//notifications.freelancer.com'); // Always get latest channels when socket reopens // TODO: Enable this once user_jobs is defined in mobile web for // posted project notifications // cookies.channels = get_job_channel(); socket.onmessage = function(e) { var message = JSON.parse(e.data); if (message.channel === 'user') { message_received(message.body); } else if (message.channel === 'subscribe') { reconnect_backoff.ok(); if (message.body === 'NO') { auth_fail_callback(); socket.close(4000, 'Auth Failure'); } else { auth_backoff.ok(); is_ready = true; set_ready(); } } }; socket.onopen = function() { connect_backoff.ok(); socket.send(JSON.stringify({ channel: 'auth', body: cookies })); }; socket.onclose = function(e) { socket = undefined; is_ready = false; if (e.code === 4000) { setTimeout(new_socket, auth_backoff.fail()); } else if (!e.wasClean) { setTimeout(new_socket, connect_backoff.fail()); } else { setTimeout(new_socket, reconnect_backoff.fail()); } }; }; new_socket(); }; init(function() { console.log('auth failure'); }); // Handle the event where we come from logged out and log in document.addEventListener('libnotify.loggedin', function() { if (!isLoggedIn) { init(function() { console.log('auth failure'); }); } }); // Register the public interface return { 'log': true, 'register': register, 'register_filter': register_filter, 'close': close_socket, 'packets': seen_packets, 'send': send, 'ready': ready, 'set_job_channel': set_job_channel }; }; window.libnotify = build_libnotify(); }());