diff --git a/.gitignore b/.gitignore
index a524bdc7..60225f6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,6 @@ Extension Building
*.iml
script.js
script.min.js
+credentials.json
/socketserver/cmd/socketserver/socketserver
diff --git a/gulpfile.js b/gulpfile.js
index baa2333e..411e6b64 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -13,7 +13,13 @@ var fs = require('fs'),
// Templates
var jsEscape = require('gulp-js-escape'),
wrap = require('gulp-wrap'),
- declare = require('gulp-declare');
+ declare = require('gulp-declare'),
+ minifyCss = require('gulp-minify-css');
+
+
+// Deploy Dependencies
+var ftp = require('vinyl-ftp'),
+ request = require('request');
// Server Dependencies
@@ -56,7 +62,25 @@ gulp.task('templates', ['prepare'], function() {
});
-gulp.task('scripts', ['prepare', 'templates'], function() {
+gulp.task('styles', ['prepare'], function() {
+ gulp.src(['build/styles/**/*.css'])
+ .pipe(minifyCss())
+ .pipe(jsEscape())
+ .pipe(declare({
+ root: 'exports',
+ noRedeclare: true,
+ processName: function(filePath) {
+ var match = filePath.match(/build[\\\/]styles[\\\/](.*)\.css$/);
+ return declare.processNameByPath((match && match.length > 1) ? match[1] : filePath);
+ }
+ }))
+ .pipe(concat('styles.js'))
+ .pipe(gulp.dest('build/'))
+ .on('error', util.log)
+});
+
+
+gulp.task('scripts', ['prepare', 'templates', 'styles'], function() {
gulp.src(['build/main.js'])
.pipe(browserify())
.pipe(concat('script.js'))
@@ -76,6 +100,79 @@ gulp.task('watch', ['default', 'server'], function() {
gulp.task('default', ['scripts']);
+// Deploy
+
+gulp.task('upload', ['default'], function() {
+ // Load credentials from an external file.
+ var contents = fs.readFileSync('credentials.json', 'utf8'),
+ cred = JSON.parse(contents);
+
+ cred.log = util.log;
+
+ // Create the connection.
+ var conn = ftp.create(cred);
+
+ // What we're transfering.
+ var ftp_path = cred.remote_path,
+
+ globs = [
+ "script.min.js",
+ "style.css",
+ "dark.css",
+ "changelog.html"
+ ];
+
+ util.log(cred.remote_path);
+
+ return gulp.src(globs, {base: '.', buffer: false})
+ .pipe(conn.newerOrDifferentSize(ftp_path))
+ .pipe(conn.dest(ftp_path))
+ .on('error', util.log);
+});
+
+gulp.task('clear_cache', ['upload'], function() {
+ // Load credentials from an external file.
+ var contents = fs.readFileSync('credentials.json', 'utf8'),
+ cred = JSON.parse(contents);
+
+ // Build the URLs.
+ var base = "://cdn.frankerfacez.com/script/",
+ files = [],
+ globs = [
+ "script.min.js",
+ "style.css",
+ "dark.css",
+ "changelog.html"
+ ];
+
+ for(var i=0; i < globs.length; i++) {
+ files.push("http" + base + globs[i]);
+ files.push("https" + base + globs[i]);
+ }
+
+ request({
+ method: 'DELETE',
+ uri: "https://api.cloudflare.com/client/v4/zones/" + cred.cloudflare_zone + "/purge_cache",
+ headers: {
+ "X-Auth-Email": cred.cloudflare_email,
+ "X-Auth-Key": cred.cloudflare_key
+ },
+ json: {
+ "files": files
+ }
+ }, function(error, request, body) {
+ if ( error )
+ return util.log("[FAIL] Error: " + error);
+ else if ( request.statusCode !== 200 )
+ return util.log("[FAIL] Non-200 Status: " + request.statusCode);
+
+ util.log("[SUCCESS] Cache cleared.");
+ });
+});
+
+gulp.task('deploy', ['upload', 'clear_cache']);
+
+
// Server
gulp.task('server', function() {
diff --git a/package.json b/package.json
index c3fffe8c..1d4b18e7 100755
--- a/package.json
+++ b/package.json
@@ -15,11 +15,13 @@
"gulp-footer": "^1.0.5",
"gulp-header": "^1.2.2",
"gulp-js-escape": "^1.0.1",
+ "gulp-minify-css": "^1.2.1",
"gulp-rename": "^1.2.0",
"gulp-uglify": "^1.0.2",
"gulp-util": "^3.0.2",
"gulp-wrap": "^0.11.0",
- "request": "^2.51.0"
+ "request": "^2.65.0",
+ "vinyl-ftp": "^0.4.5"
},
"repository": {
"type": "git",
diff --git a/src/constants.js b/src/constants.js
index 0a1da39b..32d35656 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -1,15 +1,19 @@
var SVGPATH = '',
+
DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'),
- WS_SERVERS = DEBUG ? ["localhost:8001", "catbag.frankerfacez.com"] : ["catbag.frankerfacez.com"],
SERVER = DEBUG ? "//localhost:8000/" : "//cdn.frankerfacez.com/";
module.exports = {
DEBUG: DEBUG,
SERVER: SERVER,
- WS_SERVERS: WS_SERVERS,
API_SERVER: "//api.frankerfacez.com/",
API_SERVER_2: "//direct-api.frankerfacez.com/",
+ WS_SERVER_POOLS: {
+ 1: ["ws://catbag.frankerfacez.com/", "ws://andknuckles.frankerfacez.com/"],
+ 2: ["ws://localhost:8001/"]
+ },
+
KNOWN_CODES: {
"#-?[\\\\/]": "#-/",
":-?(?:7|L)": ":-7",
diff --git a/src/ember/channel.js b/src/ember/channel.js
index a7adb41b..57cc4c67 100644
--- a/src/ember/channel.js
+++ b/src/ember/channel.js
@@ -456,8 +456,14 @@ FFZ.prototype._modify_cindex = function(view) {
el = stat_el && stat_el.querySelector('span'),
player_cont = f.players && f.players[channel_id],
- player = player_cont && player_cont.ffz_player,
+ player = undefined, stats = undefined;
+
+ try {
+ player = player_cont && player_cont.ffz_player;
stats = player && player.stats;
+ } catch(err) {
+ f.error("Channel ffzUpdatePlayerStats: player.stats: " + err);
+ }
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
if ( stat_el )
@@ -508,8 +514,14 @@ FFZ.prototype._modify_cindex = function(view) {
el = stat_el && stat_el.querySelector('span'),
player_cont = f.players && f.players[hosted_id],
- player = player_cont && player_cont.ffz_player,
+ player = undefined, stats = undefined;
+
+ try {
+ player = player_cont && player_cont.ffz_player;
stats = player && player.stats;
+ } catch(err) {
+ f.error("Channel ffzUpdatePlayerStats: player.stats: " + err);
+ }
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
diff --git a/src/ember/chatview.js b/src/ember/chatview.js
index 06ce3945..0ef7b468 100644
--- a/src/ember/chatview.js
+++ b/src/ember/chatview.js
@@ -98,6 +98,36 @@ FFZ.settings_info.minimal_chat = {
};
+FFZ.settings_info.chat_batching = {
+ type: "select",
+ options: {
+ 0: "No Batching",
+ 250: "Minor (0.25s)",
+ 500: "Normal (0.5s)",
+ 750: "Large (0.75s)",
+ 1000: "Extreme (1s)"
+ },
+ value: 0,
+
+ category: "Chat Appearance",
+ no_bttv: true,
+
+ name: "Chat Message Batching",
+ help: "Display chat messages in batches to improve performance in extremely fast chats.",
+
+ process_value: function(val) {
+ if ( typeof val === "string" )
+ return parseInt(val) || 0;
+ return val;
+ },
+
+ on_update: function(val) {
+ if ( this._roomv )
+ this._roomv.ffzUpdateStatus();
+ }
+};
+
+
FFZ.settings_info.chat_delay = {
type: "select",
options: {
diff --git a/src/ember/player.js b/src/ember/player.js
index 8b714677..67c8e888 100644
--- a/src/ember/player.js
+++ b/src/ember/player.js
@@ -159,14 +159,23 @@ FFZ.prototype._modify_player = function(player) {
return;
player = tp2.getPlayer();
- if ( ! player )
+ if ( ! player || ! player.getVideo )
+ // We can't get a valid player. :-(
return;
}
this.set('ffz_player', player);
// Only set up the stats hooks if we need stats.
- if ( ! player.getVideo() )
+ var has_video;
+
+ try {
+ has_video = player.getVideo();
+ } catch(err) {
+ f.error("Player2 ffzPostPlayer: getVideo: " + err);
+ }
+
+ if ( ! has_video )
this.ffzInitStats();
},
@@ -186,7 +195,7 @@ FFZ.prototype._modify_player = function(player) {
player.ffz_stats = player.getStatsEnabled();
} catch(err) {
// Assume stats are off.
- f.log("player ffzInitStats: getStatsEnabled still doesn't work: " + err);
+ f.error("Player2 ffzInitStats: getStatsEnabled still doesn't work: " + err);
player.ffz_stats = false;
}
diff --git a/src/ember/room.js b/src/ember/room.js
index 0978d6c5..a6f9cbbe 100644
--- a/src/ember/room.js
+++ b/src/ember/room.js
@@ -190,6 +190,7 @@ FFZ.prototype._modify_rview = function(view) {
slow_badge = cont.querySelector('#ffz-stat-slow'),
banned_badge = cont.querySelector('#ffz-stat-banned'),
delay_badge = cont.querySelector('#ffz-stat-delay'),
+ batch_badge = cont.querySelector('#ffz-stat-batch'),
btn = cont.querySelector('button');
if ( f.has_bttv || ! f.settings.room_status ) {
@@ -199,6 +200,10 @@ FFZ.prototype._modify_rview = function(view) {
sub_badge.parentElement.removeChild(sub_badge);
if ( slow_badge )
slow_badge.parentElement.removeChild(slow_badge);
+ if ( delay_badge )
+ delay_badge.parentElement.removeChild(delay_badge);
+ if ( batch_badge )
+ batch_badge.parentElement.removeChild(batch_badge);
if ( btn )
btn.classList.remove('ffz-waiting');
@@ -209,7 +214,7 @@ FFZ.prototype._modify_rview = function(view) {
r9k_badge = document.createElement('span');
r9k_badge.className = 'ffz room-state stat float-right';
r9k_badge.id = 'ffz-stat-r9k';
- r9k_badge.innerHTML = 'R9K';
+ r9k_badge.innerHTML = 'R9K';
r9k_badge.title = "This room is in R9K-mode.";
cont.appendChild(r9k_badge);
jQuery(r9k_badge).tipsy({gravity:"s", offset:15});
@@ -219,7 +224,7 @@ FFZ.prototype._modify_rview = function(view) {
sub_badge = document.createElement('span');
sub_badge.className = 'ffz room-state stat float-right';
sub_badge.id = 'ffz-stat-sub';
- sub_badge.innerHTML = 'SUB';
+ sub_badge.innerHTML = 'SUB';
sub_badge.title = "This room is in subscribers-only mode.";
cont.appendChild(sub_badge);
jQuery(sub_badge).tipsy({gravity:"s", offset:15});
@@ -229,8 +234,7 @@ FFZ.prototype._modify_rview = function(view) {
slow_badge = document.createElement('span');
slow_badge.className = 'ffz room-state stat float-right';
slow_badge.id = 'ffz-stat-slow';
- slow_badge.innerHTML = 'SLOW';
- slow_badge.title = "This room is in slow mode. You may send messages every 120 seconds.";
+ slow_badge.innerHTML = 'SLOW';
cont.appendChild(slow_badge);
jQuery(slow_badge).tipsy({gravity:"s", offset:15});
}
@@ -239,7 +243,7 @@ FFZ.prototype._modify_rview = function(view) {
banned_badge = document.createElement('span');
banned_badge.className = 'ffz room-state stat float-right';
banned_badge.id = 'ffz-stat-banned';
- banned_badge.innerHTML = 'BAN';
+ banned_badge.innerHTML = 'BAN';
banned_badge.title = "You have been banned from talking in this room.";
cont.appendChild(banned_badge);
jQuery(banned_badge).tipsy({gravity:"s", offset:15});
@@ -249,21 +253,54 @@ FFZ.prototype._modify_rview = function(view) {
delay_badge = document.createElement('span');
delay_badge.className = 'ffz room-state stat float-right';
delay_badge.id = 'ffz-stat-delay';
- delay_badge.innerHTML = 'DELAY';
- delay_badge.title = "You have enabled artificial chat delay. Messages are displayed after 0.3 seconds.";
+ delay_badge.innerHTML = 'DELAY';
cont.appendChild(delay_badge);
jQuery(delay_badge).tipsy({gravity:"s", offset:15});
}
- r9k_badge.classList.toggle('hidden', !(room && room.get('r9k')));
- sub_badge.classList.toggle('hidden', !(room && room.get('subsOnly')));
- banned_badge.classList.toggle('hidden', !(room && room.get('ffz_banned')));
+ if ( ! batch_badge ) {
+ batch_badge = document.createElement('span');
+ batch_badge.className = 'ffz room-state stat float-right';
+ batch_badge.id = 'ffz-stat-batch';
+ batch_badge.innerHTML = 'BATCH';
+ cont.appendChild(batch_badge);
+ jQuery(batch_badge).tipsy({gravity:"s", offset:15});
+ }
- slow_badge.classList.toggle('hidden', !(room && room.get('slowMode')));
+ var vis_count = 0,
+ r9k_vis = room && room.get('r9k'),
+ sub_vis = room && room.get('subsOnly'),
+ ban_vis = room && room.get('ffz_banned'),
+ slow_vis = room && room.get('slowMode'),
+ delay_vis = f.settings.chat_delay !== 0,
+ batch_vis = f.settings.chat_batching !== 0;
+
+ if ( r9k_vis ) vis_count += 1;
+ if ( sub_vis ) vis_count += 1;
+ if ( ban_vis ) vis_count += 1;
+ if ( slow_vis ) vis_count += 1;
+ if ( delay_vis ) vis_count += 1;
+ if ( batch_vis ) vis_count += 1;
+
+ r9k_badge.classList.toggle('truncated', vis_count > 3);
+ sub_badge.classList.toggle('truncated', vis_count > 3);
+ banned_badge.classList.toggle('truncated', vis_count > 3);
+ slow_badge.classList.toggle('truncated', vis_count > 3);
+ delay_badge.classList.toggle('truncated', vis_count > 3);
+ batch_badge.classList.toggle('truncated', vis_count > 3);
+
+ r9k_badge.classList.toggle('hidden', ! r9k_vis);
+ sub_badge.classList.toggle('hidden', ! sub_vis);
+ banned_badge.classList.toggle('hidden', ! ban_vis);
+
+ slow_badge.classList.toggle('hidden', ! slow_vis);
slow_badge.title = "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow')||120) + " seconds.";
delay_badge.title = "You have enabled artificial chat delay. Messages are displayed after " + (f.settings.chat_delay/1000) + " seconds.";
- delay_badge.classList.toggle('hidden', f.settings.chat_delay === 0);
+ delay_badge.classList.toggle('hidden', ! delay_vis);
+
+ batch_badge.title = "You have enabled chat message batching. Messages are displayed in " + (f.settings.chat_batching/1000) + " second increments.";
+ batch_badge.classList.toggle('hidden', ! batch_vis);
if ( btn ) {
btn.classList.toggle('ffz-waiting', (room && room.get('slowWait') || 0));
@@ -1022,7 +1059,7 @@ FFZ.prototype._modify_room = function(room) {
// Artificial chat delay
pushMessage: function(msg) {
- if ( f.settings.chat_delay !== 0 || (this.ffzPending && this.ffzPending.length) ) {
+ if ( f.settings.chat_batching !== 0 || f.settings.chat_delay !== 0 || (this.ffzPending && this.ffzPending.length) ) {
if ( ! this.ffzPending )
this.ffzPending = [];
@@ -1056,11 +1093,21 @@ FFZ.prototype._modify_room = function(room) {
// Instead of just blindly looping every x seconds, we want to calculate the time until
// the next message should be displayed, and then set the timeout for that. We'll
// end up looping a bit more frequently, but it'll make chat feel more responsive.
+
+ // If we have a pending flush, don't reschedule. It wouldn't change.
if ( this._ffz_pending_flush )
- clearTimeout(this._ffz_pending_flush);
+ return;
+
+ /*if ( this._ffz_pending_flush )
+ clearTimeout(this._ffz_pending_flush);*/
if ( this.ffzPending && this.ffzPending.length ) {
- var delay = 50 + Math.max(0, (f.settings.chat_delay + (this.ffzPending[0].time||0)) - (now || Date.now()));
+ // We need either the amount of chat delay past the first message, if chat_delay is on, or the
+ // amount of time from the last batch.
+ var delay = Math.max(
+ (f.settings.chat_delay !== 0 ? 50 + Math.max(0, (f.settings.chat_delay + (this.ffzPending[0].time||0)) - (now || Date.now())) : 0),
+ (f.settings.chat_batching !== 0 ? Math.max(0, f.settings.chat_batching - ((now || Date.now()) - (this._ffz_last_batch||0))) : 0));
+
this._ffz_pending_flush = setTimeout(this.ffzPendingFlush.bind(this), delay);
}
},
@@ -1068,13 +1115,14 @@ FFZ.prototype._modify_room = function(room) {
ffzPendingFlush: function() {
this._ffz_pending_flush = null;
- var now = Date.now();
+ var now = this._ffz_last_batch = Date.now();
+
for (var i = 0, l = this.ffzPending.length; i < l; i++) {
var msg = this.ffzPending[i];
if ( msg.removed )
continue;
- if ( f.settings.chat_delay + msg.time > now )
+ if ( f.settings.chat_delay !== 0 && (f.settings.chat_delay + msg.time > now) )
break;
this.ffzActualPushMessage(msg);
diff --git a/src/main.js b/src/main.js
index 655e8d6f..3eb6c92c 100644
--- a/src/main.js
+++ b/src/main.js
@@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
- major: 3, minor: 5, revision: 42,
+ major: 3, minor: 5, revision: 50,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
diff --git a/src/socket.js b/src/socket.js
index 99847bfc..0ebceab2 100644
--- a/src/socket.js
+++ b/src/socket.js
@@ -3,50 +3,106 @@ var FFZ = window.FrankerFaceZ,
FFZ.prototype._ws_open = false;
FFZ.prototype._ws_delay = 0;
-FFZ.prototype._ws_last_iframe = 0;
-FFZ.prototype._ws_host_idx = Math.floor(Math.random() * constants.WS_SERVERS.length) + 1;
-if (constants.DEBUG) {
- FFZ.prototype._ws_host_idx = 0;
-}
+FFZ.prototype._ws_host_idx = -1;
+FFZ.prototype._ws_current_pool = -1;
+
FFZ.ws_commands = {};
FFZ.ws_on_close = [];
// ----------------
-// Socket Creation
+// Settings
// ----------------
-// Attempt to authenticate to the socket server as a real browser by loading the root page.
-// e.g. cloudflare ddos check
-FFZ.prototype.ws_iframe = function() {
- this._ws_last_iframe = Date.now();
- var ifr = document.createElement('iframe'),
- f = this;
+var ffz_socket_seed;
- ifr.src = 'http://catbag.frankerfacez.com';
- ifr.style.visibility = 'hidden';
- document.body.appendChild(ifr);
- setTimeout(function() {
- document.body.removeChild(ifr);
- if ( ! f._ws_open )
- f.ws_create();
- }, 2000);
+try {
+ ffz_socket_seed = JSON.parse(localStorage.ffz_socket_seed);
+} catch(err) { }
+
+if ( ! ffz_socket_seed ) {
+ ffz_socket_seed = Math.random();
+ localStorage.ffz_socket_seed = JSON.stringify(ffz_socket_seed);
}
-FFZ.prototype.ws_create = function() {
- // Disable sockets for now.
- return;
+FFZ.settings_info.socket_server_pool = {
+ type: "select",
+ options: {
+ 0: "Disabled",
+ 1: "Production",
+ 2: "Development"
+ },
+ value: ffz_socket_seed > 0.65 ? 1 : 0,
+
+ process_value: function(val) {
+ if ( typeof val === "string" )
+ return parseInt(val) || 0;
+ return val;
+ },
+
+ visible: function() { return (localStorage.hasOwnProperty('ffz_socket_server_pool') && this.settings.socket_server_pool !== 1) || this.settings.developer_mode || (Date.now() - parseInt(localStorage.ffzLastDevMode || "0")) < 604800000; },
+
+ category: "Debugging",
+ name: "Socket Server Cluster",
+ help: "Select which cluster of socket servers to connect to.",
+
+ on_update: function(val) {
+ if ( val === this._ws_current_pool )
+ return;
+
+ try {
+ this._ws_sock.close();
+ } catch(err) { }
+
+ this._ws_open = false;
+ this._ws_delay = 0;
+ this._ws_host_idx = -1;
+
+ if ( this._ws_recreate_timer ) {
+ clearTimeout(this._ws_recreate_timer);
+ this._ws_recreate_timer = null;
+ }
+
+ if ( val === 0 )
+ return;
+
+ this.ws_create();
+ }
+};
+
+
+// ----------------
+// Socket Creation
+// ----------------
+
+FFZ.prototype.ws_create = function() {
var f = this, ws;
- this._ws_last_req = 0;
- this._ws_callbacks = {};
+ this._ws_last_req = 1;
+ this._ws_callbacks = {1: f._ws_on_hello.bind(f)};
this._ws_pending = this._ws_pending || [];
+ this._ws_recreate_timer = null;
+
+ var pool_id = this.settings.socket_server_pool,
+ pool = constants.WS_SERVER_POOLS[pool_id];
+
+ this._ws_current_pool = pool_id;
+
+ if ( ! pool )
+ return;
+
+ if ( this._ws_host_idx < 0 )
+ this._ws_host_idx = Math.floor(Math.random() * pool.length);
+
+ var server = pool[this._ws_host_idx];
+
+ this.log("Using Socket Server: " + server + " [" + pool_id + ":" + this._ws_host_idx + "]");
try {
- ws = this._ws_sock = new WebSocket("ws://" + constants.WS_SERVERS[this._ws_host_idx] + "/");
+ ws = this._ws_sock = new WebSocket(pool[this._ws_host_idx]);
} catch(err) {
this._ws_exists = false;
return this.log("Error Creating WebSocket: " + err);
@@ -57,10 +113,10 @@ FFZ.prototype.ws_create = function() {
ws.onopen = function(e) {
f._ws_open = true;
f._ws_delay = 0;
- f._ws_last_iframe = Date.now();
- f.log("Socket connected.");
+ f.log("Socket Connected.");
- f.ws_send("hello", ["ffz_" + FFZ.version_info, localStorage.ffzClientId], f._ws_on_hello.bind(f));
+ // Hard-code the first command.
+ ws.send("1 hello " + JSON.stringify(["ffz_" + FFZ.version_info, localStorage.ffzClientId]));
var user = f.get_user();
if ( user )
@@ -70,7 +126,6 @@ FFZ.prototype.ws_create = function() {
if ( f.is_dashboard ) {
var match = location.pathname.match(/\/([^\/]+)/);
if ( match ) {
- f.ws_send("sub", "room." + match[1]);
f.ws_send("sub", "channel." + match[1]);
}
}
@@ -124,13 +179,16 @@ FFZ.prototype.ws_create = function() {
if ( ! f._ws_offline_time ) {
f._ws_offline_time = new Date().getTime();
}
-
- // Cycle selected server
- f._ws_host_idx = (f._ws_host_idx + 1) % constants.WS_SERVERS.length;
}
ws.onclose = function(e) {
+ var was_open = f._ws_open;
f.log("Socket closed. (Code: " + e.code + ", Reason: " + e.reason + ")");
+
+ // If a recreate is already scheduled, this is expected.
+ if ( f._ws_recreate_timer )
+ return;
+
f._ws_open = false;
if ( ! f._ws_offline_time ) {
f._ws_offline_time = new Date().getTime();
@@ -145,14 +203,11 @@ FFZ.prototype.ws_create = function() {
}
}
- // Cycle selected server
- f._ws_host_idx = (f._ws_host_idx + 1) % constants.WS_SERVERS.length;
-
- if ( f._ws_delay > 10000 ) {
- var ua = navigator.userAgent.toLowerCase();
- if ( Date.now() - f._ws_last_iframe > 1800000 && !(ua.indexOf('chrome') === -1 && ua.indexOf('safari') !== -1) )
- return f.ws_iframe();
- }
+ // Cycle selected server if our last attempt to connect didn't
+ // actually connect.
+ if ( ! was_open )
+ // Actually, let's get a random new server instead.
+ f._ws_host_idx = -1; //(f._ws_host_idx + 1) % pool.length;
// We never ever want to not have a socket.
if ( f._ws_delay < 60000 )
@@ -161,7 +216,7 @@ FFZ.prototype.ws_create = function() {
// Randomize delay.
f._ws_delay = (Math.floor(Math.random()*60)+30)*1000;
- setTimeout(f.ws_create.bind(f), f._ws_delay);
+ f._ws_recreate_timer = setTimeout(f.ws_create.bind(f), f._ws_delay);
}
ws.onmessage = function(e) {
@@ -193,7 +248,7 @@ FFZ.prototype.ws_create = function() {
delete f._ws_callbacks[request];
} else {
- var success = cmd === 'True',
+ var success = cmd === 'ok',
has_callback = typeof f._ws_callbacks[request] === "function";
if ( ! has_callback )
@@ -251,7 +306,7 @@ FFZ.prototype._ws_on_hello = function(success, data) {
localStorage.ffzClientId = data;
this.log("Client ID: " + data);
- var survey = {},
+ /*var survey = {},
set = survey['settings'] = {};
for(var key in FFZ.settings_info)
@@ -271,10 +326,35 @@ FFZ.prototype._ws_on_hello = function(success, data) {
survey['language'] = navigator.language;
survey['platform'] = navigator.platform;
- this.ws_send("survey", [survey]);
+ this.ws_send("survey", [survey]);*/
}
+// ----------------
+// Reconnect Logic
+// ----------------
+
+FFZ.ws_commands.reconnect = function() {
+ this.log("Socket Reconnect Command Received");
+
+ // Set the socket as closed and close it.
+ this._ws_open = false;
+ this._ws_sock.close();
+
+ // Socket Close Callbacks
+ for(var i=0; i < FFZ.ws_on_close.length; i++) {
+ try {
+ FFZ.ws_on_close[i].bind(this)();
+ } catch(err) {
+ this.log("Error on Socket Close Callback: " + err);
+ }
+ }
+
+ // Randomize the reconnect delay to avoid a complete hammering.
+ this._ws_delay = Math.floor(Math.random() * 5) * 1000;
+ this._ws_recreate_timer = setTimeout(this.ws_create.bind(this), this._ws_delay);
+}
+
// ----------------
// Authorization
diff --git a/style.css b/src/styles/style.css
similarity index 99%
rename from style.css
rename to src/styles/style.css
index 651dd885..766e3640 100644
--- a/style.css
+++ b/src/styles/style.css
@@ -850,6 +850,7 @@ span.ffz-handle:after { left: 8px }
/* Chat Mentions */
+
.ember-chat .mentioned:empty,
.ember-chat .mentioning:empty {
display: none;
@@ -1155,7 +1156,7 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate:before {
background-color: rgba(255,0,0, 0.3);
}
-
+*/
/* The New Whispers */
@@ -1643,6 +1644,8 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
margin-right: 15px;
}
+.ffz.room-state.truncated span { font-size: 8px; }
+
.button.primary.ffz-waiting:not(:hover) {
background-color: rgba(0,0,0,0.1);
color: #32323e;
@@ -1932,28 +1935,28 @@ li[data-name="following"] a {
.ffz-legacy-mod-badges .ember-chat .badges .moderator,
.ffz-legacy-badges .ember-chat .badges .moderator {
background-color: #068c10;
- background-image: url('legacy-mod.png');
+ background-image: url('//cdn.frankerfacez.com/script/legacy-mod.png');
}
.ffz-legacy-badges .ember-chat .badges .staff {
background-color: #6441a5;
- background-image: url('legacy-staff.png');
+ background-image: url('//cdn.frankerfacez.com/script/legacy-staff.png');
}
.ffz-legacy-badges .ember-chat .badges .broadcaster {
background-color: #000;
- background-image: url('legacy-broadcaster.png');
+ background-image: url('//cdn.frankerfacez.com/script/legacy-broadcaster.png');
}
.ffz-legacy-badges .ember-chat .badges .admin {
background-color: #ff0303;
- background-image: url('legacy-admin.png');
+ background-image: url('//cdn.frankerfacez.com/script/legacy-admin.png');
}
.ffz-legacy-turbo-badges .ember-chat .badges .turbo,
.ffz-legacy-badges .ember-chat .badges .turbo {
background-color: #6441a3;
- background-image: url('legacy-turbo.png');
+ background-image: url('//cdn.frankerfacez.com/script/legacy-turbo.png');
}
/* High Contrast Chat */
diff --git a/src/ui/dark.js b/src/ui/dark.js
index a625892e..65d98a80 100644
--- a/src/ui/dark.js
+++ b/src/ui/dark.js
@@ -1,5 +1,6 @@
var FFZ = window.FrankerFaceZ,
- constants = require("../constants");
+ constants = require("../constants"),
+ styles = require("../styles");
// ---------------------
diff --git a/src/ui/styles.js b/src/ui/styles.js
index d8312d29..a10b1684 100644
--- a/src/ui/styles.js
+++ b/src/ui/styles.js
@@ -1,17 +1,16 @@
var FFZ = window.FrankerFaceZ,
- constants = require('../constants');
+ constants = require('../constants'),
+ styles = require('../styles');
FFZ.prototype.setup_css = function() {
document.body.classList.toggle('ffz-flip-dashboard', this.settings.flip_dashboard);
this.log("Injecting main FrankerFaceZ CSS.");
- var s = this._main_style = document.createElement('link');
+ var s = this._main_style = document.createElement('style');
+ s.textContent = styles.style;
s.id = "ffz-ui-css";
- s.setAttribute('rel', 'stylesheet');
- s.setAttribute('href', constants.SERVER + "script/style.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
- s.onerror = "this.href = this.href + '_';"
document.head.appendChild(s);