1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-02 16:08:31 +00:00

Added nice following tooltips to the sidebar. Shift-clicking emotes in the menu works now. Fixed color code bugs on non-ember. Fixed DDoS prevention nonsense.

This commit is contained in:
SirStendec 2015-08-02 02:41:03 -04:00
parent 08ad23ec02
commit 9ece18ec0f
12 changed files with 737 additions and 421 deletions

279
script.js
View file

@ -549,8 +549,8 @@ FFZ.prototype.setup_colors = function() {
this._update_colors();
// Events for rebuilding colors.
var Layout = App.__container__.lookup('controller:layout'),
Settings = App.__container__.lookup('controller:settings');
var Layout = window.App && App.__container__.lookup('controller:layout'),
Settings = window.App && App.__container__.lookup('controller:settings');
if ( Layout )
Layout.addObserver("isTheatreMode", this._update_colors.bind(this, true));
@ -989,8 +989,8 @@ FFZ.prototype._rebuild_colors = function() {
FFZ.prototype._update_colors = function(darkness_only) {
// Update the lines. ALL of them.
var Layout = App.__container__.lookup('controller:layout'),
Settings = App.__container__.lookup('controller:settings'),
var Layout = window.App && App.__container__.lookup('controller:layout'),
Settings = window.App && App.__container__.lookup('controller:settings'),
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode'));
@ -1242,6 +1242,8 @@ module.exports = {
CAMERA: '<svg class="svg-camera" height="16px" version="1.1" viewBox="0 0 36 36" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" d="M24,20v6H4V10h20v6l8-6v16L24,20z"/></svg>',
INVITE: '<svg class="svg-plus" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,9h-3v3h-2V9H7V7h3V4h2v3h3V9z M9,6H6v4h2h1v3h4l0,0l0,0v1h-3H4H1v-1l3-3h2L4,8V2h6v1H9V6z" fill-rule="evenodd"></path></svg>',
LIVE: '<svg class="svg-glyph_live_small" height="16px" version="1.1" viewbox="0 0 16 16" width="13px" x="0px" y="0px"><path clip-rule="evenodd" d="M11,14H5H2v-1l3-3h2L5,8V2h6v6l-2,2h2l3,3v1H11z" fill-rule="evenodd"></path></svg>',
EYE: '<svg class="svg-glyph_views ffz-svg svg-eye" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M11,13H5L1,9V8V7l4-4h6l4,4v1v1L11,13z M8,5C6.344,5,5,6.343,5,8c0,1.656,1.344,3,3,3c1.657,0,3-1.344,3-3C11,6.343,9.657,5,8,5z M8,9C7.447,9,7,8.552,7,8s0.447-1,1-1s1,0.448,1,1S8.553,9,8,9z" fill-rule="evenodd"></path></svg>',
CLOCK: '<svg class="svg-glyph_views ffz-svg svg-clock" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M8,15c-3.866,0-7-3.134-7-7s3.134-7,7-7s7,3.134,7,7 S11.866,15,8,15z M8,3C5.238,3,3,5.238,3,8s2.238,5,5,5s5-2.238,5-5S10.762,3,8,3z M7.293,8.707L7,8l1-4l0.902,3.607L11,11 L7.293,8.707z"/></svg>',
GEAR: '<svg class="svg-gear" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,7v2h-2.115c-0.125,0.615-0.354,1.215-0.713,1.758l1.484,1.484l-1.414,1.414l-1.484-1.484C10.215,12.531,9.615,12.76,9,12.885V15H7v-2.12c-0.614-0.126-1.21-0.356-1.751-0.714l-1.491,1.49l-1.414-1.414l1.491-1.49C3.477,10.211,3.247,9.613,3.12,9H1V7h2.116C3.24,6.384,3.469,5.785,3.829,5.242L2.343,3.757l1.414-1.414l1.485,1.485C5.785,3.469,6.384,3.24,7,3.115V1h2v2.12c0.613,0.126,1.211,0.356,1.752,0.714l1.49-1.491l1.414,1.414l-1.49,1.492C12.523,5.79,12.754,6.387,12.88,7H15z M8,6C6.896,6,6,6.896,6,8s0.896,2,2,2s2-0.896,2-2S9.104,6,8,6z" fill-rule="evenodd"></path></svg>',
@ -7243,22 +7245,8 @@ FFZ.prototype._emote_menu_enumerator = function() {
if ( emote.hidden )
continue;
// TODO: Stop having to calculate this here.
var title = set.title, badge = set.icon || null;
if ( ! title ) {
if ( set.id === "global" )
title = "FrankerFaceZ Global Emotes";
else if ( set.id == "globalevent" )
title = "FrankerFaceZ Event Emotes";
else if ( this.feature_friday && set.id == this.feature_friday.set )
title = "FrankerFaceZ " + this.feature_friday.title + ": " + this.feature_friday.display_name;
else
title = "FrankerFaceZ Set: " + FFZ.get_capitalization(set.id);
} else
title = "FrankerFaceZ: " + title;
var title = "FrankerFaceZ " + set.title,
badge = set.icon || '//cdn.frankerfacez.com/script/devicon.png';
emotes.push({text: emote.name, url: emote.urls[1],
hidden: false, channel: title, badge: badge});
@ -7291,7 +7279,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 8,
major: 3, minor: 5, revision: 10,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -8147,6 +8135,7 @@ var FFZ = window.FrankerFaceZ;
FFZ.prototype._ws_open = false;
FFZ.prototype._ws_delay = 0;
FFZ.prototype._ws_last_iframe = 0;
FFZ.ws_commands = {};
FFZ.ws_on_close = [];
@ -8156,6 +8145,22 @@ FFZ.ws_on_close = [];
// Socket Creation
// ----------------
FFZ.prototype.ws_iframe = function() {
this._ws_last_iframe = Date.now();
var ifr = document.createElement('iframe'),
f = this;
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);
}
FFZ.prototype.ws_create = function() {
var f = this, ws;
@ -8175,6 +8180,7 @@ 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.");
// Check for incognito. We don't want to do a hello in incognito mode.
@ -8251,6 +8257,12 @@ FFZ.prototype.ws_create = function() {
}
}
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();
}
// We never ever want to not have a socket.
if ( f._ws_delay < 60000 )
f._ws_delay += (Math.floor(Math.random()*10) + 5) * 1000;
@ -9630,7 +9642,8 @@ FFZ.prototype._load_dark_css = function() {
}
},{"../constants":4}],24:[function(require,module,exports){
var FFZ = window.FrankerFaceZ,
utils = require('../utils');
utils = require('../utils'),
constants = require('../constants');
@ -9683,11 +9696,17 @@ FFZ.prototype.setup_following_count = function(has_ember) {
return this.log("Unable to find Live Streams collection.");
Live.addObserver('total', function() { f._draw_following_count(this.get('total')); });
Live.addObserver('content.length', function() { f._draw_following_channels(this.get('content'), this.get('total')); })
Live.load();
var total = Live.get('total');
if ( typeof total === "number" )
var total = Live.get('total'),
streams = Live.get('content');
if ( typeof total === "number" ) {
this._draw_following_count(total);
if ( streams && streams.length )
this._draw_following_channels(streams, total);
}
}
@ -9723,15 +9742,84 @@ FFZ.prototype._update_following_count = function() {
if ( Live )
Live.load();
else
Twitch.api && Twitch.api.get("streams/followed", {limit:1, offset:0}, {version:3})
Twitch.api && Twitch.api.get("streams/followed", {limit:5, offset:0}, {version:3})
.done(function(data) {
f._draw_following_count(data._total);
f._draw_following_channels(data.streams, data._total);
}).fail(function() {
f._draw_following_count();
f._draw_following_channels();
})
}
FFZ.prototype._draw_following_channels = function(streams, total) {
// First, build the data.
var tooltip = 'Following';
if ( streams && streams.length ) {
var c = 0;
for(var i=0, l = streams.length; i < l; i++) {
var stream = streams[i];
if ( ! stream || ! stream.channel )
continue;
c += 1;
if ( c > 5 ) {
var ttl = total || streams.length;
tooltip += '<hr><span>And ' + utils.number_commas(ttl - 5) + ' more...</span>';
break;
}
tooltip += (i > 0 ? '<br>' : '<hr>') + '<span class="viewers">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span><b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br><span class="playing">' + (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing') + '</span>';
}
}
// Small
var small_following = jQuery('#small_nav ul.game_filters li[data-name="following"] a');
if ( small_following && small_following.length ) {
var data = small_following.data('tipsy');
if ( data && data.options ) {
data.options.gravity = function() { return this.parentElement.getAttribute('data-name') === 'following' ? 'nw': 'w'; };
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
small_following.tipsy({html: true, className: 'ffz-wide-tip', gravity: 'nw'});
small_following.attr('title', tooltip);
}
// Large
var large_following = jQuery('#large_nav #nav_personal li[data-name="following"] a');
if ( large_following && large_following.length ) {
var data = large_following.data('tipsy');
if ( data && data.options ) {
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
large_following.tipsy({html:true, className: 'ffz-wide-tip'});
large_following.attr('title', tooltip);
}
// Heading
var head_following = jQuery('#header_actions #header_following');
if ( head_following && head_following.length ) {
var data = head_following.data('tipsy');
if ( data && data.options ) {
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
head_following.tipsy({html: true, className: 'ffz-wide-tip'});
head_following.attr('title', tooltip);
}
}
FFZ.prototype._draw_following_count = function(count) {
// Small
var small_following = document.querySelector('#small_nav ul.game_filters li[data-name="following"] a');
@ -9785,7 +9873,7 @@ FFZ.prototype._draw_following_count = function(count) {
}
}
}
},{"../utils":34}],25:[function(require,module,exports){
},{"../constants":4,"../utils":34}],25:[function(require,module,exports){
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
@ -10231,14 +10319,30 @@ var FFZ = window.FrankerFaceZ,
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
fix_menu_position = function(container) {
var swapped = document.body.classList.contains('ffz-sidebar-swap');
var bounds = container.getBoundingClientRect(),
left = parseInt(container.style.left || '0'),
right = bounds.left + container.scrollWidth;
right = bounds.left + container.scrollWidth,
moved = !!container.style.left;
if ( swapped ) {
if ( bounds.left < 20 ) {
container.style.left = '';
moved = false;
} else if ( right > document.body.clientWidth )
container.style.left = (left - (right - document.body.clientWidth)) + 'px';
} else {
if ( bounds.left < 0 )
container.style.left = (left - bounds.left) + 'px';
else if ( right > document.body.clientWidth )
container.style.left = (left - (right - document.body.clientWidth)) + 'px';
else if ( right > (document.body.clientWidth - 20) ) {
container.style.left = '';
moved = false;
}
}
container.classList.toggle('ui-moved', moved);
};
@ -10447,7 +10551,31 @@ FFZ.prototype.build_ui_popup = function(view) {
var heading = document.createElement('li');
heading.className = 'title';
heading.innerHTML = "<span>" + (constants.DEBUG ? "[DEV] " : "") + "FrankerFaceZ</span>";
heading.innerHTML = '<span class="title">Franker' + (constants.DEBUG ? 'Dev' : 'Face') + 'Z</span>';
// Close Button
var close_btn = document.createElement('span'),
f = this;
close_btn.className = 'ffz-handle ffz-close-button';
heading.insertBefore(close_btn, heading.firstChild);
var can_close = false;
close_btn.addEventListener('mousedown', function() {
var popup = f._popup;
can_close = popup && popup.id === "ffz-chat-menu" && popup.style.left;
});
close_btn.addEventListener('click', function() {
var popup = f._popup;
if ( can_close && popup ) {
popup.parentElement.removeChild(popup);
delete f._popup;
f._popup_kill && f._popup_kill();
delete f._popup_kill;
}
});
menu.appendChild(heading);
// Draggable
@ -10554,7 +10682,8 @@ FFZ.menu_pages.channel = {
// Get the current room.
var room_id = view.get('controller.currentRoom.id'),
room = this.rooms[room_id],
has_product = false;
has_product = false,
f = this;
// Check for a product.
if ( this.settings.replace_twitch_menu ) {
@ -10577,7 +10706,6 @@ FFZ.menu_pages.channel = {
// See if we've loaded. If we haven't loaded the ticket yet
// then try loading it, and then re-render the menu.
if ( tickets && ! is_subscribed && ! is_loaded ) {
var f = this;
tickets.addObserver('isLoaded', function() {
setTimeout(function(){
if ( inner.getAttribute('data-page') !== 'channel' )
@ -10621,8 +10749,17 @@ FFZ.menu_pages.channel = {
s.style.width = emote.width + "px";
s.style.height = emote.height + "px";
s.title = emote.regex;
if ( can_use )
s.addEventListener('click', this._add_emote.bind(this, view, emote.regex));
s.addEventListener('click', function(can_use, id, code, e) {
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://twitchemotes.com/emote/" + id);
else if ( can_use )
this._add_emote(view, code);
else
return;
e.preventDefault();
}.bind(this, can_use, emote.id, emote.regex));
grid.appendChild(s);
c++;
}
@ -10701,7 +10838,7 @@ FFZ.menu_pages.channel = {
// --------------------
FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub_text) {
var grid = document.createElement('div'), c = 0;
var grid = document.createElement('div'), c = 0, f = this;
grid.className = 'emoticon-grid';
if ( header != null ) {
@ -10775,7 +10912,14 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
s.style.height = emote.height + "px";
s.title = this._emote_tooltip(emote);
s.addEventListener('click', this._add_emote.bind(this, view, emote.name));
s.addEventListener('click', function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://www.frankerfacez.com/emoticons/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.name));
grid.appendChild(s);
}
@ -10936,53 +11080,10 @@ FFZ.menu_pages.myemotes = {
render: function(view, container) {
var tmi = view.get('controller.currentRoom.tmiSession'),
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'],
needed_sets = [];
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'];
for(var set_id in twitch_sets)
if ( twitch_sets.hasOwnProperty(set_id) && ! this._twitch_set_to_channel.hasOwnProperty(set_id) )
needed_sets.push(set_id);
if ( ! needed_sets.length )
// We don't have to do async stuff anymore cause we pre-load data~!
return FFZ.menu_pages.myemotes.draw_menu.bind(this)(view, container, twitch_sets);
var f = this,
fail = function() {
if ( ! needed_sets.length )
return;
needed_sets = [];
var ts = {};
for(var set_id in twitch_sets)
if ( f._twitch_set_to_channel[set_id] )
ts[set_id] = twitch_sets[set_id];
else
ts[set_id] = []; //"twitch_unknown";
return FFZ.menu_pages.myemotes.draw_menu.bind(f)(view, container, ts);
};
if ( ! this.ws_send("twitch_sets", needed_sets, function(success, data) {
if ( ! needed_sets.length )
return;
needed_sets = [];
if ( success ) {
for(var set_id in data) {
if ( ! data.hasOwnProperty(set_id) )
continue;
f._twitch_set_to_channel[set_id] = data[set_id];
}
localStorage.ffzTwitchSets = JSON.stringify(f._twitch_set_to_channel);
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, twitch_sets);
} else
fail();
}) )
fail()
else
setTimeout(fail, 2000);
},
toggle_section: function(heading) {
@ -11125,7 +11226,13 @@ FFZ.menu_pages.myemotes = {
}
em.title = code;
em.addEventListener("click", this._add_emote.bind(this, view, code));
em.addEventListener("click", function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://twitchemotes.com/emote/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.code));
menu.appendChild(em);
}
@ -11190,7 +11297,13 @@ FFZ.menu_pages.myemotes = {
em.style.width = emote.width + "px";
em.title = this._emote_tooltip(emote);
em.addEventListener("click", this._add_emote.bind(this, view, emote.name));
em.addEventListener("click", function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://www.frankerfacez.com/emoticons/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.name));
menu.appendChild(em);
}

16
script.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -120,8 +120,8 @@ FFZ.prototype.setup_colors = function() {
this._update_colors();
// Events for rebuilding colors.
var Layout = App.__container__.lookup('controller:layout'),
Settings = App.__container__.lookup('controller:settings');
var Layout = window.App && App.__container__.lookup('controller:layout'),
Settings = window.App && App.__container__.lookup('controller:settings');
if ( Layout )
Layout.addObserver("isTheatreMode", this._update_colors.bind(this, true));
@ -560,8 +560,8 @@ FFZ.prototype._rebuild_colors = function() {
FFZ.prototype._update_colors = function(darkness_only) {
// Update the lines. ALL of them.
var Layout = App.__container__.lookup('controller:layout'),
Settings = App.__container__.lookup('controller:settings'),
var Layout = window.App && App.__container__.lookup('controller:layout'),
Settings = window.App && App.__container__.lookup('controller:settings'),
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode'));

View file

@ -59,6 +59,8 @@ module.exports = {
CAMERA: '<svg class="svg-camera" height="16px" version="1.1" viewBox="0 0 36 36" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" d="M24,20v6H4V10h20v6l8-6v16L24,20z"/></svg>',
INVITE: '<svg class="svg-plus" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,9h-3v3h-2V9H7V7h3V4h2v3h3V9z M9,6H6v4h2h1v3h4l0,0l0,0v1h-3H4H1v-1l3-3h2L4,8V2h6v1H9V6z" fill-rule="evenodd"></path></svg>',
LIVE: '<svg class="svg-glyph_live_small" height="16px" version="1.1" viewbox="0 0 16 16" width="13px" x="0px" y="0px"><path clip-rule="evenodd" d="M11,14H5H2v-1l3-3h2L5,8V2h6v6l-2,2h2l3,3v1H11z" fill-rule="evenodd"></path></svg>',
EYE: '<svg class="svg-glyph_views ffz-svg svg-eye" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M11,13H5L1,9V8V7l4-4h6l4,4v1v1L11,13z M8,5C6.344,5,5,6.343,5,8c0,1.656,1.344,3,3,3c1.657,0,3-1.344,3-3C11,6.343,9.657,5,8,5z M8,9C7.447,9,7,8.552,7,8s0.447-1,1-1s1,0.448,1,1S8.553,9,8,9z" fill-rule="evenodd"></path></svg>',
CLOCK: '<svg class="svg-glyph_views ffz-svg svg-clock" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M8,15c-3.866,0-7-3.134-7-7s3.134-7,7-7s7,3.134,7,7 S11.866,15,8,15z M8,3C5.238,3,3,5.238,3,8s2.238,5,5,5s5-2.238,5-5S10.762,3,8,3z M7.293,8.707L7,8l1-4l0.902,3.607L11,11 L7.293,8.707z"/></svg>',
GEAR: '<svg class="svg-gear" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,7v2h-2.115c-0.125,0.615-0.354,1.215-0.713,1.758l1.484,1.484l-1.414,1.414l-1.484-1.484C10.215,12.531,9.615,12.76,9,12.885V15H7v-2.12c-0.614-0.126-1.21-0.356-1.751-0.714l-1.491,1.49l-1.414-1.414l1.491-1.49C3.477,10.211,3.247,9.613,3.12,9H1V7h2.116C3.24,6.384,3.469,5.785,3.829,5.242L2.343,3.757l1.414-1.414l1.485,1.485C5.785,3.469,6.384,3.24,7,3.115V1h2v2.12c0.613,0.126,1.211,0.356,1.752,0.714l1.49-1.491l1.414,1.414l-1.49,1.492C12.523,5.79,12.754,6.387,12.88,7H15z M8,6C6.896,6,6,6.896,6,8s0.896,2,2,2s2-0.896,2-2S9.104,6,8,6z" fill-rule="evenodd"></path></svg>',

View file

@ -49,22 +49,8 @@ FFZ.prototype._emote_menu_enumerator = function() {
if ( emote.hidden )
continue;
// TODO: Stop having to calculate this here.
var title = set.title, badge = set.icon || null;
if ( ! title ) {
if ( set.id === "global" )
title = "FrankerFaceZ Global Emotes";
else if ( set.id == "globalevent" )
title = "FrankerFaceZ Event Emotes";
else if ( this.feature_friday && set.id == this.feature_friday.set )
title = "FrankerFaceZ " + this.feature_friday.title + ": " + this.feature_friday.display_name;
else
title = "FrankerFaceZ Set: " + FFZ.get_capitalization(set.id);
} else
title = "FrankerFaceZ: " + title;
var title = "FrankerFaceZ " + set.title,
badge = set.icon || '//cdn.frankerfacez.com/script/devicon.png';
emotes.push({text: emote.name, url: emote.urls[1],
hidden: false, channel: title, badge: badge});

View file

@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 8,
major: 3, minor: 5, revision: 10,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}

View file

@ -2,6 +2,7 @@ var FFZ = window.FrankerFaceZ;
FFZ.prototype._ws_open = false;
FFZ.prototype._ws_delay = 0;
FFZ.prototype._ws_last_iframe = 0;
FFZ.ws_commands = {};
FFZ.ws_on_close = [];
@ -11,6 +12,22 @@ FFZ.ws_on_close = [];
// Socket Creation
// ----------------
FFZ.prototype.ws_iframe = function() {
this._ws_last_iframe = Date.now();
var ifr = document.createElement('iframe'),
f = this;
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);
}
FFZ.prototype.ws_create = function() {
var f = this, ws;
@ -30,6 +47,7 @@ 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.");
// Check for incognito. We don't want to do a hello in incognito mode.
@ -106,6 +124,12 @@ FFZ.prototype.ws_create = function() {
}
}
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();
}
// We never ever want to not have a socket.
if ( f._ws_delay < 60000 )
f._ws_delay += (Math.floor(Math.random()*10) + 5) * 1000;

View file

@ -1,5 +1,6 @@
var FFZ = window.FrankerFaceZ,
utils = require('../utils');
utils = require('../utils'),
constants = require('../constants');
@ -52,11 +53,17 @@ FFZ.prototype.setup_following_count = function(has_ember) {
return this.log("Unable to find Live Streams collection.");
Live.addObserver('total', function() { f._draw_following_count(this.get('total')); });
Live.addObserver('content.length', function() { f._draw_following_channels(this.get('content'), this.get('total')); })
Live.load();
var total = Live.get('total');
if ( typeof total === "number" )
var total = Live.get('total'),
streams = Live.get('content');
if ( typeof total === "number" ) {
this._draw_following_count(total);
if ( streams && streams.length )
this._draw_following_channels(streams, total);
}
}
@ -92,15 +99,84 @@ FFZ.prototype._update_following_count = function() {
if ( Live )
Live.load();
else
Twitch.api && Twitch.api.get("streams/followed", {limit:1, offset:0}, {version:3})
Twitch.api && Twitch.api.get("streams/followed", {limit:5, offset:0}, {version:3})
.done(function(data) {
f._draw_following_count(data._total);
f._draw_following_channels(data.streams, data._total);
}).fail(function() {
f._draw_following_count();
f._draw_following_channels();
})
}
FFZ.prototype._draw_following_channels = function(streams, total) {
// First, build the data.
var tooltip = 'Following';
if ( streams && streams.length ) {
var c = 0;
for(var i=0, l = streams.length; i < l; i++) {
var stream = streams[i];
if ( ! stream || ! stream.channel )
continue;
c += 1;
if ( c > 5 ) {
var ttl = total || streams.length;
tooltip += '<hr><span>And ' + utils.number_commas(ttl - 5) + ' more...</span>';
break;
}
tooltip += (i > 0 ? '<br>' : '<hr>') + '<span class="viewers">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span><b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br><span class="playing">' + (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing') + '</span>';
}
}
// Small
var small_following = jQuery('#small_nav ul.game_filters li[data-name="following"] a');
if ( small_following && small_following.length ) {
var data = small_following.data('tipsy');
if ( data && data.options ) {
data.options.gravity = function() { return this.parentElement.getAttribute('data-name') === 'following' ? 'nw': 'w'; };
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
small_following.tipsy({html: true, className: 'ffz-wide-tip', gravity: 'nw'});
small_following.attr('title', tooltip);
}
// Large
var large_following = jQuery('#large_nav #nav_personal li[data-name="following"] a');
if ( large_following && large_following.length ) {
var data = large_following.data('tipsy');
if ( data && data.options ) {
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
large_following.tipsy({html:true, className: 'ffz-wide-tip'});
large_following.attr('title', tooltip);
}
// Heading
var head_following = jQuery('#header_actions #header_following');
if ( head_following && head_following.length ) {
var data = head_following.data('tipsy');
if ( data && data.options ) {
data.options.html = true;
data.options.className = 'ffz-wide-tip';
} else
head_following.tipsy({html: true, className: 'ffz-wide-tip'});
head_following.attr('title', tooltip);
}
}
FFZ.prototype._draw_following_count = function(count) {
// Small
var small_following = document.querySelector('#small_nav ul.game_filters li[data-name="following"] a');

View file

@ -5,14 +5,30 @@ var FFZ = window.FrankerFaceZ,
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
fix_menu_position = function(container) {
var swapped = document.body.classList.contains('ffz-sidebar-swap');
var bounds = container.getBoundingClientRect(),
left = parseInt(container.style.left || '0'),
right = bounds.left + container.scrollWidth;
right = bounds.left + container.scrollWidth,
moved = !!container.style.left;
if ( swapped ) {
if ( bounds.left < 20 ) {
container.style.left = '';
moved = false;
} else if ( right > document.body.clientWidth )
container.style.left = (left - (right - document.body.clientWidth)) + 'px';
} else {
if ( bounds.left < 0 )
container.style.left = (left - bounds.left) + 'px';
else if ( right > document.body.clientWidth )
container.style.left = (left - (right - document.body.clientWidth)) + 'px';
else if ( right > (document.body.clientWidth - 20) ) {
container.style.left = '';
moved = false;
}
}
container.classList.toggle('ui-moved', moved);
};
@ -221,7 +237,31 @@ FFZ.prototype.build_ui_popup = function(view) {
var heading = document.createElement('li');
heading.className = 'title';
heading.innerHTML = "<span>" + (constants.DEBUG ? "[DEV] " : "") + "FrankerFaceZ</span>";
heading.innerHTML = '<span class="title">Franker' + (constants.DEBUG ? 'Dev' : 'Face') + 'Z</span>';
// Close Button
var close_btn = document.createElement('span'),
f = this;
close_btn.className = 'ffz-handle ffz-close-button';
heading.insertBefore(close_btn, heading.firstChild);
var can_close = false;
close_btn.addEventListener('mousedown', function() {
var popup = f._popup;
can_close = popup && popup.id === "ffz-chat-menu" && popup.style.left;
});
close_btn.addEventListener('click', function() {
var popup = f._popup;
if ( can_close && popup ) {
popup.parentElement.removeChild(popup);
delete f._popup;
f._popup_kill && f._popup_kill();
delete f._popup_kill;
}
});
menu.appendChild(heading);
// Draggable
@ -328,7 +368,8 @@ FFZ.menu_pages.channel = {
// Get the current room.
var room_id = view.get('controller.currentRoom.id'),
room = this.rooms[room_id],
has_product = false;
has_product = false,
f = this;
// Check for a product.
if ( this.settings.replace_twitch_menu ) {
@ -351,7 +392,6 @@ FFZ.menu_pages.channel = {
// See if we've loaded. If we haven't loaded the ticket yet
// then try loading it, and then re-render the menu.
if ( tickets && ! is_subscribed && ! is_loaded ) {
var f = this;
tickets.addObserver('isLoaded', function() {
setTimeout(function(){
if ( inner.getAttribute('data-page') !== 'channel' )
@ -395,8 +435,17 @@ FFZ.menu_pages.channel = {
s.style.width = emote.width + "px";
s.style.height = emote.height + "px";
s.title = emote.regex;
if ( can_use )
s.addEventListener('click', this._add_emote.bind(this, view, emote.regex));
s.addEventListener('click', function(can_use, id, code, e) {
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://twitchemotes.com/emote/" + id);
else if ( can_use )
this._add_emote(view, code);
else
return;
e.preventDefault();
}.bind(this, can_use, emote.id, emote.regex));
grid.appendChild(s);
c++;
}
@ -475,7 +524,7 @@ FFZ.menu_pages.channel = {
// --------------------
FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub_text) {
var grid = document.createElement('div'), c = 0;
var grid = document.createElement('div'), c = 0, f = this;
grid.className = 'emoticon-grid';
if ( header != null ) {
@ -549,7 +598,14 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
s.style.height = emote.height + "px";
s.title = this._emote_tooltip(emote);
s.addEventListener('click', this._add_emote.bind(this, view, emote.name));
s.addEventListener('click', function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://www.frankerfacez.com/emoticons/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.name));
grid.appendChild(s);
}

View file

@ -79,53 +79,10 @@ FFZ.menu_pages.myemotes = {
render: function(view, container) {
var tmi = view.get('controller.currentRoom.tmiSession'),
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'],
needed_sets = [];
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'];
for(var set_id in twitch_sets)
if ( twitch_sets.hasOwnProperty(set_id) && ! this._twitch_set_to_channel.hasOwnProperty(set_id) )
needed_sets.push(set_id);
if ( ! needed_sets.length )
// We don't have to do async stuff anymore cause we pre-load data~!
return FFZ.menu_pages.myemotes.draw_menu.bind(this)(view, container, twitch_sets);
var f = this,
fail = function() {
if ( ! needed_sets.length )
return;
needed_sets = [];
var ts = {};
for(var set_id in twitch_sets)
if ( f._twitch_set_to_channel[set_id] )
ts[set_id] = twitch_sets[set_id];
else
ts[set_id] = []; //"twitch_unknown";
return FFZ.menu_pages.myemotes.draw_menu.bind(f)(view, container, ts);
};
if ( ! this.ws_send("twitch_sets", needed_sets, function(success, data) {
if ( ! needed_sets.length )
return;
needed_sets = [];
if ( success ) {
for(var set_id in data) {
if ( ! data.hasOwnProperty(set_id) )
continue;
f._twitch_set_to_channel[set_id] = data[set_id];
}
localStorage.ffzTwitchSets = JSON.stringify(f._twitch_set_to_channel);
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, twitch_sets);
} else
fail();
}) )
fail()
else
setTimeout(fail, 2000);
},
toggle_section: function(heading) {
@ -268,7 +225,13 @@ FFZ.menu_pages.myemotes = {
}
em.title = code;
em.addEventListener("click", this._add_emote.bind(this, view, code));
em.addEventListener("click", function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://twitchemotes.com/emote/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.code));
menu.appendChild(em);
}
@ -333,7 +296,13 @@ FFZ.menu_pages.myemotes = {
em.style.width = emote.width + "px";
em.title = this._emote_tooltip(emote);
em.addEventListener("click", this._add_emote.bind(this, view, emote.name));
em.addEventListener("click", function(id, code, e) {
e.preventDefault();
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
window.open("https://www.frankerfacez.com/emoticons/" + id);
else
this._add_emote(view, code);
}.bind(this, emote.id, emote.name));
menu.appendChild(em);
}

View file

@ -579,12 +579,85 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
float: left;
}
.ffz-ui-popup ul.menu li.title > span {
display: block;
padding: 10px 20px;
line-height: 16px;
span.ffz-handle {
display: inline-block;
position: relative;
height: 26px;
width: 14px;
transition: width 500ms;
}
span.ffz-handle:before,
span.ffz-handle:after {
position: absolute;
left: 4px;
top: 5px;
content: "";
height: 14px;
border: 1px solid #bbb;
border-radius: 4px;
transition: transform 500ms, left 500ms, border-color 500ms, border-width 500ms, height 500ms;
}
span.ffz-handle:after { left: 8px }
.ffz-ui-popup.ui-moved span.ffz-handle { width: 24px; cursor: pointer; }
.ffz-ui-popup.ui-moved span.ffz-handle:before,
.ffz-ui-popup.ui-moved span.ffz-handle:after {
left: 11px;
border-color: #333;
}
.ffz-ui-popup.ui-moved span.ffz-handle:before { transform: rotate(45deg); }
.ffz-ui-popup.ui-moved span.ffz-handle:after { transform: rotate(-45deg); }
.app-main.theatre span.ffz-handle:before,
.chat-container.dark span.ffz-handle:before,
.chat-container.force-dark span.ffz-handle:before,
.ember-chat-container.dark span.ffz-handle:before,
.ember-chat-container.force-dark span.ffz-handle:before,
.ffz-ui-popup.dark span.ffz-handle:before,
.app-main.theatre span.ffz-handle:after,
.chat-container.dark span.ffz-handle:after,
.chat-container.force-dark span.ffz-handle:after,
.ember-chat-container.dark span.ffz-handle:after,
.ember-chat-container.force-dark span.ffz-handle:after,
.ffz-ui-popup.dark span.ffz-handle:after {
border-color: #666;
}
.app-main.theatre .ffz-ui-popup.ui-moved span.ffz-handle:before,
.chat-container.dark .ffz-ui-popup.ui-moved span.ffz-handle:before,
.chat-container.force-dark .ffz-ui-popup.ui-moved span.ffz-handle:before,
.ember-chat-container.dark .ffz-ui-popup.ui-moved span.ffz-handle:before,
.ember-chat-container.force-dark .ffz-ui-popup.ui-moved span.ffz-handle:before,
.ffz-ui-popup.ui-moved.dark span.ffz-handle:before,
.app-main.theatre .ffz-ui-popup.ui-moved span.ffz-handle:after,
.chat-container.dark .ffz-ui-popup.ui-moved span.ffz-handle:after,
.chat-container.force-dark .ffz-ui-popup.ui-moved span.ffz-handle:after,
.ember-chat-container.dark .ffz-ui-popup.ui-moved span.ffz-handle:after,
.ember-chat-container.force-dark .ffz-ui-popup.ui-moved span.ffz-handle:after,
.ffz-ui-popup.ui-moved.dark span.ffz-handle:after {
border-color: #d3d3d3;
}
.ffz-ui-popup ul.menu li.title > span.ffz-handle {
float: left;
margin: 5px;
}
.ffz-ui-popup ul.menu li.title > span.title {
display: block;
margin-left: 24px;
padding: 10px 20px 10px 0;
line-height: 16px;
transition: margin-left 500ms;
}
.ffz-ui-popup.ui-moved ul.menu li.title > span.title { margin-left: 34px; }
.ffz-ui-popup ul.menu a {
display: block;
padding: 10px;
@ -1006,6 +1079,23 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
/* Emoticon Tooltips */
.ffz-wide-tip .tipsy-inner {
max-width: 600px;
text-align: left;
}
.ffz-wide-tip span.viewers {
float: right;
}
.ffz-wide-tip span.viewers svg {
float: left;
margin: 1px;
}
.ffz-wide-tip svg path { fill: #fff; }
.ffz-wide-tip span.playing { opacity: 0.7; }
.tipsy .tipsy-inner {
white-space: pre-wrap;
}