1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-25 12:08:30 +00:00

3.5.51 to 3.5.57. Changes to how chat history is implemented. Made the Actions and Metadata overlay for theater mode optional. Added support for the new Conversation UI. Increased socket server rollout. Mod cards update history in real time. Fixed global moderator badges. Refactored emote clicking code. Moved CSS to external again.

This commit is contained in:
SirStendec 2015-11-07 22:56:15 -05:00
parent 10da2b4fd5
commit c40b3ba337
20 changed files with 459 additions and 2291 deletions

111
dark.css
View file

@ -164,6 +164,7 @@
/* Popups */
.ffz-dark .conversation-settings-menu,
.ffz-dark .ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
.ffz-dark .card,
.ffz-dark #flyout .content,
@ -926,3 +927,113 @@
.ffz-dark .playlist-container:not(.playlist-enabled) .ui-sortable-helper {
background-color: rgba(255,255,255,0.2);
}
/* Conversations */
.ffz-dark .conversation-settings-menu .options-divider {
border-bottom-color: rgba(255,255,255,0.2);
}
.ffz-dark .conversation-settings-menu:before {
border-color: transparent;
border-bottom-color: #32323e;
}
.ffz-dark .conversation-settings-menu:after {
border-color: transparent;
border-bottom-color: rgb(16,16,16);
}
.ffz-dark .conversations-list-icon {
background: #19191f;
color: #8c8c9c;
border-color: #19191f
}
.ffz-dark .conversations-list-icon:hover {
color: #fff
}
.ffz-dark .conversations-list {
background: #19191f;
border: 1px solid #32323e;
color: #fff
}
.ffz-dark .conversations-list:after {
border-color: rgba(25,25,31,0);
border-top-color: #19191f;
border-width: 10px;
margin-left: -10px
}
.ffz-dark .conversations-list:before {
right: 9px;
border-color: rgba(50,50,62,0);
border-top-color: #32323e
}
.ffz-dark .conversations-list .conversations-list-header {
background: #19191f;
border-bottom: 1px solid #32323e;
color: #fff
}
.ffz-dark .conversations-list .conversation-preview-line {
color: #8c8c9c
}
.ffz-dark .conversations-list .conversations-list-item {
border-bottom: 1px solid #32323e
}
.ffz-dark .conversations-list .conversations-list-item:hover {
background-color: #121217
}
.ffz-dark .conversation-window {
background: #19191f;
box-shadow: none;
color: #8c8c9c;
}
.ffz-dark .conversations-list-icon,
.ffz-dark .conversation-window {
border: 1px solid rgba(255,255,255,0.2);
border-bottom: none;
}
.ffz-dark .conversation-header {
background: #121217;
box-shadow: none;
border-bottom: 1px solid rgba(255,255,255,0.2);
}
.ffz-dark .conversation-input-bar textarea {
border-color: rgba(255,255,255,0.1);
background-color: rgba(255,255,255,0.05);
color: #b6b6b6
}
.ffz-dark .conversation-input-actions .button,
.ffz-dark .conversation-input-actions .follow-button:not(.ember-follow) .follow {
background-color: #444
}
.ffz-dark .conversation-window.has-focus .conversation-header {
background-color: #121217
}
.ffz-dark .conversation-window.has-focus .conversation-header-name {
color: #fff
}
.ffz-dark .conversation-window.has-focus .conversation-input-bar textarea:focus {
border-color: rgba(255,255,255,0.2)
}
.ffz-dark .conversation-window.has-focus .conversation-input-actions .button,
.ffz-dark .conversation-window.has-focus .conversation-input-actions .follow-button:not(.ember-follow) .follow {
background-color: #6441a5
}

View file

@ -254,7 +254,7 @@ FFZ.prototype.render_badges = function(component, badges) {
if ( ! this.settings.show_badges )
return badges;
var user = component.get('msgObject.from'),
var user = component.get('msgObject.from') || component.get('message.from.username'),
room_id = component.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id');
var data = this.users[user];

View file

@ -581,6 +581,18 @@ FFZ.prototype._update_colors = function(darkness_only) {
bit.style.color = is_dark ? colors[1] : colors[0];
}
colored_bits = document.querySelectorAll('.conversation-chat-line .has-color');
for(var i=0, l=colored_bits.length; i < l; i++) {
var bit = colored_bits[i],
color = bit.getAttribute('data-color'),
colors = color && this._handle_color(color);
if ( ! colors )
continue;
bit.style.color = is_dark ? colors[1] : colors[0];
}
}

View file

@ -2,10 +2,13 @@ var SVGPATH = '<path d="m120.95 1.74c4.08-0.09 8.33-0.84 12.21 0.82 3.61 1.8 7 4
DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'),
SERVER = DEBUG ? "//localhost:8000/" : "//cdn.frankerfacez.com/";
DIRECT_SERVER = DEBUG ? "//localhost:8000/" : "//direct-cdn.frankerfacez.com/";
module.exports = {
DEBUG: DEBUG,
SERVER: SERVER,
DIRECT_SERVER: DIRECT_SERVER,
API_SERVER: "//api.frankerfacez.com/",
API_SERVER_2: "//direct-api.frankerfacez.com/",

View file

@ -16,6 +16,7 @@ FFZ.prototype.setup_channel = function() {
// Settings stuff!
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
document.body.classList.toggle('ffz-theater-stats', this.settings.theater_stats);
this.log("Creating channel style element.");
var s = this._channel_style = document.createElement('style');
@ -790,6 +791,20 @@ FFZ.settings_info.stream_title = {
};
FFZ.settings_info.theater_stats = {
type: "boolean",
value: true,
no_mobile: true,
category: "Channel Metadata",
name: "Display on Theater Mode Hover",
help: "Show the channel metadata and actions over the video player in theater mode when you hover it with your mouse.",
on_update: function(val) {
document.body.classList.toggle('ffz-theater-stats', val);
}
};
FFZ.basic_settings.channel_info = {
type: "select",
options: {

View file

@ -280,6 +280,20 @@ FFZ.settings_info.group_tabs = {
};
FFZ.settings_info.top_conversations = {
type: "boolean",
value: false,
no_mobile: true,
category: "Chat Appearance",
name: "Conversations on Top",
help: "Display the new conversation-style whisper UI at the top of the window instead of the bottom.",
on_update: function(val) {
document.body.classList.toggle('ffz-top-conversations', val);
}
};
FFZ.settings_info.pinned_rooms = {
value: [],
visible: false,
@ -298,6 +312,7 @@ FFZ.settings_info.visible_rooms = {
FFZ.prototype.setup_chatview = function() {
document.body.classList.toggle("ffz-minimal-chat-head", this.settings.minimal_chat === 1 || this.settings.minimal_chat === 3);
document.body.classList.toggle("ffz-minimal-chat-input", this.settings.minimal_chat === 2 || this.settings.minimal_chat === 3);
document.body.classList.toggle('ffz-top-conversations', this.settings.top_conversations);
this.log("Hooking the Ember Chat controller.");

View file

@ -30,45 +30,44 @@ FFZ.prototype.setup_directory = function() {
if ( ChannelView )
this._modify_directory_live(ChannelView);
var CreativeChannel = App.__container__.resolve('view:creative-channel');
if ( CreativeChannel )
this._modify_directory_live(CreativeChannel);
var CSGOChannel = App.__container__.resolve('view:cs-go-channel');
if ( CSGOChannel )
this._modify_directory_live(CSGOChannel, true);
var HostView = App.__container__.resolve('view:host');
if ( HostView )
this._modify_directory_host(HostView);
var VideoView = App.__container__.resolve('view:video');
if ( VideoView )
this._modify_directory_video(VideoView);
// TODO: Process existing views.
}
FFZ.prototype._modify_directory_video = function(dir) {
var f = this;
dir.reopen({
didInsertElement: function() {
this._super();
// Initialize existing views.
for(var key in Ember.View.views) {
var view = Ember.View.views[key];
if ( view instanceof ChannelView || view instanceof CreativeChannel || view instanceof CSGOChannel || view instanceof HostView )
view.ffzInit();
}
});
try {
dir.create().destroy();
} catch(err) { }
}
FFZ.prototype._modify_directory_live = function(dir) {
FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
var f = this;
dir.reopen({
didInsertElement: function() {
this._super();
this.ffzInit();
},
ffzInit: function() {
var el = this.get('element'),
meta = el && el.querySelector('.meta'),
thumb = el && el.querySelector('.thumb'),
cap = thumb && thumb.querySelector('.cap');
if ( f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
// CSGO doesn't provide the actual uptime information...
if ( !is_csgo && f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
var t_el = this._ffz_uptime = document.createElement('div');
t_el.className = 'overlay_info length live';
@ -88,6 +87,8 @@ FFZ.prototype._modify_directory_live = function(dir) {
target = this.get('context.model.channel.name');
logo.className = 'profile-photo';
logo.classList.toggle('is-csgo', is_csgo);
logo.src = this.get('context.model.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
logo.alt = this.get('context.model.channel.display_name');

View file

@ -47,6 +47,12 @@ FFZ.settings_info.portrait_mode = {
}
}
FFZ.settings_info.portrait_warning = {
value: false,
visible: false
}
FFZ.settings_info.swap_sidebars = {
type: "boolean",
value: false,
@ -177,7 +183,7 @@ FFZ.prototype.setup_layout = function() {
height = size[1];
// Make sure we have at least a bit of room for the chat.
return this.get("windowHeight") < (height + 120 + 60 + 100);
return this.get("windowHeight") < (height + 120 + 60 + 200);
} else
return this.get("windowWidth") < (1090 - this.get('rightColumnWidth'))
@ -217,27 +223,14 @@ FFZ.prototype.setup_layout = function() {
return "<style>.dynamic-player, .dynamic-player object, .dynamic-player video{width:" + width + "px !important;height:" + height + "px !important} .dynamic-target-player,.dynamic-target-player object, .dynamic-target-player video{width:" + width + "px !important;height:" + host_height + "px !important}</style><style>.dynamic-player .player object{width:100% !important; height:100% !important}</style>";
}.property("playerSize"),
/*ffzUpdateWidth: _.throttle(function() {
var rc = document.querySelector('#right_close');
if ( ! rc )
ffzPortraitWarning: function() {
if ( ! f.settings.portrait_mode || f._portrait_warning || f.settings.portrait_warning || ! this.get('isTooSmallForRightColumn') )
return;
var left_width = this.get("isLeftColumnClosed") ? 50 : 240,
right_width;
f._portrait_warning = true;
f.show_message('Twitch\'s Chat Sidebar has been hidden as a result of FrankerFaceZ\'s Portrait Mode because the window is too wide.<br><br>Please <a href="#" onclick="ffz.settings.set(\'portrait_mode\',0);jQuery(this).parents(\'.ffz-noty\').remove();ffz._portrait_warning = false;return false">disable Portrait Mode</a> or make your window narrower.<br><br><a href="#" onclick="onclick="ffz.settings.set(\'portrait_warning\',true);jQuery(this).parents(\'.ffz-noty\').remove();return false">Do not show this message again</a>');
if ( f.settings.swap_sidebars )
right_width = rc.offsetLeft; // + this.get('rightColumnWidth') - 5;
else
right_width = document.body.offsetWidth - rc.offsetLeft - left_width - 25;
if ( right_width < 250 ) {
// Close it!
}
this.set('rightColumnWidth', right_width);
Ember.propertyDidChange(Layout, 'contentWidth');
}, 200),*/
}.observes("isTooSmallForRightColumn"),
ffzUpdateCss: function() {
// TODO: Fix this mess of duplicate code.
@ -332,6 +325,6 @@ FFZ.prototype.setup_layout = function() {
Layout.set('rawPortraitMode', this.settings.portrait_mode);
// Force re-calculation of everything.
Ember.propertyDidChange(Layout, 'contentWidth');
Ember.propertyDidChange(Layout, 'windowWidth');
Ember.propertyDidChange(Layout, 'windowHeight');
}

View file

@ -555,6 +555,13 @@ FFZ.prototype.setup_line = function() {
if ( Line )
this._modify_line(Line);
this.log("Hooking the Ember Conversation Line component.");
var Conversation = App.__container__.resolve('component:conversation-line');
if ( Conversation )
this._modify_conversation_line(Conversation);
// Store the capitalization of our own name.
var user = this.get_user();
if ( user && user.name )
@ -568,6 +575,67 @@ FFZ.prototype.save_aliases = function() {
}
FFZ.prototype._modify_conversation_line = function(component) {
var f = this,
Layout = App.__container__.lookup('controller:layout'),
Settings = App.__container__.lookup('controller:settings');
component.reopen({
tokenizedMessage: function() {
try {
return f.tokenize_conversation_line(this.get('message'));
} catch(err) {
f.error("convo-line tokenizedMessage: " + err);
return this._super();
}
}.property("message", "currentUsername"),
click: function(e) {
if ( e.target && e.target.classList.contains('deleted-link') )
return f._deleted_link_click.bind(e.target)(e);
if ( f._click_emote(e.target, e) )
return;
return this._super(e);
},
render: function(e) {
var user = this.get('message.from.username'),
raw_color = this.get('message.from.color'),
colors = raw_color && f._handle_color(raw_color),
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
e.push('<div class="indicator"></div>');
var alias = f.aliases[user],
name = this.get('message.from.displayName') || (user && user.capitalize()) || "unknown user",
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
colored = style ? ' has-color' : '';
if ( alias )
e.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-colors="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
else
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');
e.push('<span class="colon">:</span> ');
if ( ! this.get('isActionMessage') ) {
style = '';
colored = '';
}
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
e.push('</span>');
}
});
}
FFZ.prototype._modify_line = function(component) {
var f = this,
@ -577,45 +645,12 @@ FFZ.prototype._modify_line = function(component) {
component.reopen({
tokenizedMessage: function() {
// Add our own step to the tokenization procedure.
var tokens = this.get("msgObject.cachedTokens");
if ( tokens )
return tokens;
tokens = this._super();
var start = performance.now(),
user = f.get_user(),
from_me = user && this.get("msgObject.from") === user.login;
tokens = f._remove_banned(tokens);
tokens = f._emoticonize(this, tokens);
if ( f.settings.parse_emoji )
tokens = f.tokenize_emoji(tokens);
// Store the capitalization.
var display = this.get("msgObject.tags.display-name");
if ( display && display.length )
FFZ.capitalization[this.get("msgObject.from")] = [display.trim(), Date.now()];
if ( ! from_me )
tokens = f.tokenize_mentions(tokens);
for(var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if ( ! _.isString(token) && token.mentionedUser && ! token.own ) {
this.set('msgObject.ffz_has_mention', true);
break;
try {
return f.tokenize_chat_line(this.get('msgObject'));
} catch(err) {
f.error("chat-line tokenizedMessage: " + err);
return this._super();
}
}
var end = performance.now();
if ( end - start > 5 )
f.log("Tokenizing Message Took Too Long - " + (end-start) + "ms", tokens, false, true);
this.set("msgObject.cachedTokens", tokens);
return tokens;
}.property("msgObject.message", "isChannelLinksDisabled", "currentUserNick", "msgObject.from", "msgObject.tags.emotes"),
@ -633,18 +668,6 @@ FFZ.prototype._modify_line = function(component) {
if ( e.target && e.target.classList.contains('mod-icon') ) {
jQuery(e.target).trigger('mouseout');
/*if ( e.target.classList.contains('purge') ) {
var i = this.get('msgObject.from'),
room_id = this.get('msgObject.room'),
room = room_id && f.rooms[room_id] && f.rooms[room_id].room;
if ( room ) {
room.send("/timeout " + i + " 1", true);
room.clearMessages(i);
}
return;
}*/
if ( e.target.classList.contains('custom') ) {
var room_id = this.get('msgObject.room'),
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
@ -660,31 +683,9 @@ FFZ.prototype._modify_line = function(component) {
}
}
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons && e.target && e.target.classList.contains('emoticon') ) {
var eid = e.target.getAttribute('data-emote');
if ( eid )
window.open("https://twitchemotes.com/emote/" + eid);
else {
eid = e.target.getAttribute("data-ffz-emote");
var es = e.target.getAttribute("data-ffz-set"),
set = es && f.emote_sets[es],
url;
if ( ! set )
if ( f._click_emote(e.target, e) )
return;
if ( set.hasOwnProperty('source_ext') ) {
var api = f._apis[set.source_ext];
if ( api && api.emote_url_generator )
url = api.emote_url_generator(set.source_id, eid);
} else
url = "https://www.frankerfacez.com/emoticons/" + eid;
if ( url )
window.open(url);
}
}
return this._super(e);
},
@ -768,7 +769,7 @@ FFZ.prototype._modify_line = function(component) {
else if ( this.get('isAdmin') )
badges[0] = {klass: 'admin', title: 'Admin'};
else if ( this.get('isGlobalMod') )
badges[0] = {klass: 'global-mod', title: 'Global Moderator'};
badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
else if ( ! is_whisper && this.get('isModerator') )
badges[0] = {klass: 'moderator', title: 'Moderator'};
@ -835,7 +836,7 @@ FFZ.prototype._modify_line = function(component) {
if ( deleted )
e.push('<span class="deleted"><a class="undelete" href="#">&lt;message deleted&gt;</a></span>');
else {
e.push('<span class="message' + colored + '" style="' + style + '">');
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
var old_messages = this.get('msgObject.ffz_old_messages');

View file

@ -435,13 +435,20 @@ FFZ.prototype.setup_mod_card = function() {
return alias || this.get("cardInfo.user.display_name") || user_id.capitalize();
}),
willDestroy: function() {
if ( f._mod_card === this )
f._mod_card = undefined;
this._super();
},
didInsertElement: function() {
this._super();
window._card = this;
try {
if ( f.has_bttv )
return;
f._mod_card = this;
var el = this.get('element'),
controller = this.get('controller'),
line,
@ -450,11 +457,14 @@ FFZ.prototype.setup_mod_card = function() {
chat = window.App && App.__container__.lookup('controller:chat'),
user = f.get_user(),
is_broadcaster = user && chat && chat.get('currentRoom.id') === user.login,
room_id = chat && chat.get('currentRoom.id'),
is_broadcaster = user && room_id === user.login,
user_id = controller.get('cardInfo.user.id'),
alias = f.aliases[user_id];
this.ffz_room_id = room_id;
// Alias Display
if ( alias ) {
var name = el.querySelector('h3.name'),
@ -735,17 +745,19 @@ FFZ.prototype.setup_mod_card = function() {
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(line.date) + '</span> ' : '') + '<span class="message">' + (line.style === 'action' ? '*' + line.from + ' ' : '') + f.render_tokens(line.cachedTokens) + '</span>';
// Banned Links
var bad_links = l_el.querySelectorAll('a.deleted-link');
for(var x=0; x < bad_links.length; x++)
bad_links[x].addEventListener("click", f._deleted_link_click);
// Interactivity
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
jQuery('.html-tooltip', l_el).tipsy({html:true});
// Append
history.appendChild(l_el);
}
el.appendChild(history);
this.ffz_alternate = alternate;
// Lazy scroll-to-bottom
history.scrollTop = history.scrollHeight;
}

View file

@ -5,6 +5,7 @@ var FFZ = window.FrankerFaceZ,
HOSTED_SUB = / subscribed to /,
constants = require('../constants'),
utils = require('../utils'),
helpers,
// StrimBagZ Support
is_android = navigator.userAgent.indexOf('Android') !== -1,
@ -14,7 +15,12 @@ var FFZ = window.FrankerFaceZ,
return "";
return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-image:url("' + room.moderator_badge + '") !important; }';
}
};
try {
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
} catch(err) { }
// --------------------
@ -723,10 +729,11 @@ FFZ.prototype._insert_history = function(room_id, data) {
tmiRoom = r.tmiRoom,
inserted = 0,
purged = {},
last_msg = data[data.length - 1],
now = new Date(),
last_date = typeof last_msg.date === "string" ? utils.parse_date(last_msg.date) : last_msg.date,
last_date = (typeof last_msg.date === "string" || typeof last_msg.date === "number") ? (last_msg.date = utils.parse_date(last_msg.date)) : last_msg.date,
age = (now - last_date) / 1000,
is_old = age > 300,
@ -738,9 +745,13 @@ FFZ.prototype._insert_history = function(room_id, data) {
var i = data.length;
while(i--) {
var msg = data[i];
var msg = data[i],
is_deleted = msg.ffz_deleted = purged[msg.from] || false;
if ( typeof msg.date === "string" )
if ( is_deleted && ! this.settings.prevent_clear )
msg.deleted = true;
if ( typeof msg.date === "string" || typeof msg.date === "number" )
msg.date = utils.parse_date(msg.date);
msg.ffz_alternate = alternation = ! alternation;
@ -774,6 +785,14 @@ FFZ.prototype._insert_history = function(room_id, data) {
msg.style = "notification";
}
if ( msg.tags && typeof msg.tags.emotes === "string" )
try {
msg.tags.emotes = JSON.parse(msg.tags.emotes);
} catch(err) {
f.log("Error Parsing JSON Emotes: " + err);
msg.tags.emotes = {};
}
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
@ -791,6 +810,14 @@ FFZ.prototype._insert_history = function(room_id, data) {
} else
break;
}
// If there was a CLEARCHAT, stop processing.
if ( msg.tags && msg.tags.target === '@@' )
break;
// If there was a purge, just track the name.
else if ( msg.tags && msg.tags.target )
purged[msg.tags.target] = true;
}
if ( is_old ) {
@ -1190,6 +1217,38 @@ FFZ.prototype._modify_room = function(room) {
if ( user_history.length > 20 )
user_history.shift();
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
var el = f._mod_card.get('element'),
history = el && el.querySelector('.chat-history'),
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
if ( history ) {
var l_el = document.createElement('li');
l_el.className = 'message-line chat-line clearfix';
l_el.classList.toggle('ffz-alternate', f._mod_card.ffz_alternate);
f._mod_card.ffz_alternate = !f._mod_card.ffz_alternate;
if ( msg.style )
l_el.classList.add(msg.style);
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(msg.date) + '</span> ' : '') + '<span class="message">' + (msg.style === 'action' ? '*' + msg.from + ' ' : '') + f.render_tokens(msg.cachedTokens) + '</span>';
// Interactivity
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
jQuery('.html-tooltip', l_el).tipsy({html:true});
history.appendChild(l_el);
if ( was_at_top )
setTimeout(function() { history.scrollTop = history.scrollHeight; })
// Don't do infinite scrollback.
if ( history.childElementCount > 50 )
history.removeChild(history.firstElementChild);
}
}
}
}

View file

@ -141,6 +141,41 @@ FFZ.prototype._report_emotes = function() {
}
// ------------------------
// Emote Click Handler
// ------------------------
FFZ.prototype._click_emote = function(target, event) {
if ( ! this.settings.clickable_emoticons || (event && !((event.shiftKey || event.shiftLeft) && target && target.classList.contains('emoticon'))) )
return;
var eid = target.getAttribute('data-emote');
if ( eid )
window.open("https://twitchemotes.com/emote/" + eid);
else {
eid = target.getAttribute("data-ffz-emote");
var es = target.getAttribute("data-ffz-set"),
emote_set = es && this.emote_sets[es],
url;
if ( ! emote_set )
return;
if ( emote_set.hasOwnProperty('source_ext') ) {
var api = this._apis[emote_set.source_ext];
if ( api && api.emote_url_generator )
url = api.emote_url_generator(emote_set.source_id, eid);
} else
url = "https://www.frankerfacez.com/emoticons/" + eid;
if ( url ) {
window.open(url);
return true;
}
}
}
// ------------------------
// Twitch Emoticon Checker
// ------------------------

View file

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

View file

@ -35,7 +35,7 @@ FFZ.settings_info.socket_server_pool = {
2: "Development"
},
value: ffz_socket_seed > 0.65 ? 1 : 0,
value: ffz_socket_seed > 0.4 ? 1 : 0,
process_value: function(val) {
if ( typeof val === "string" )

File diff suppressed because it is too large Load diff

View file

@ -401,14 +401,58 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
// Tokenization
// ---------------------
FFZ.prototype.tokenize_conversation_line = function(message, prevent_notification) {
var msg = message.get('body'),
user = this.get_user(),
from_user = message.get('from.username'),
from_me = user && from_user === user.login,
emotes = message.get('tags.emotes'),
tokens = [msg];
// Standard Tokenization
if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens);
if ( user && user.login && helpers && helpers.mentionizeMessage )
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
if ( helpers && helpers.emoticonizeMessage && emotes )
tokens = helpers.emoticonizeMessage(tokens, emotes);
if ( this.settings.replace_bad_emotes )
tokens = this.tokenize_replace_emotes(tokens);
// FrankerFaceZ Extras
tokens = this._remove_banned(tokens);
tokens = this.tokenize_emotes(from_user, undefined, tokens, from_me);
if ( this.settings.parse_emoji )
tokens = this.tokenize_emoji(tokens);
// Capitalization
var display_name = message.get('from.displayName');
if ( display_name && display_name.length )
FFZ.capitalization[from_user] = [display_name.trim(), Date.now()];
// Mentions!
if ( ! from_me )
tokens = this.tokenize_mentions(tokens);
// TODO: Notifications?
return tokens;
}
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, delete_links) {
if ( msgObject.cachedTokens )
return msgObject.cachedTokens;
var msg = msgObject.message,
var msg = msgObject.message || msgObject.get('body'),
user = this.get_user(),
room_id = msgObject.room,
from_me = user && msgObject.from === user.login,
from_user = msgObject.from,
from_me = user && from_user === user.login,
emotes = msgObject.tags && msgObject.tags.emotes,
tokens = [msg];
@ -438,7 +482,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
// FrankerFaceZ Extras
tokens = this._remove_banned(tokens);
tokens = this.tokenize_emotes(msgObject.from, room_id, tokens, from_me);
tokens = this.tokenize_emotes(from_user, room_id, tokens, from_me);
if ( this.settings.parse_emoji )
tokens = this.tokenize_emoji(tokens);
@ -446,7 +490,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
// Capitalization
var display = msgObject.tags && msgObject.tags['display-name'];
if ( display && display.length )
FFZ.capitalization[msgObject.from] = [display.trim(), Date.now()];
FFZ.capitalization[from_user] = [display.trim(), Date.now()];
// Mentions!
@ -482,7 +526,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
else
room_name = FFZ.get_capitalization(room_id);
display = display || Twitch.display.capitalize(msgObject.from);
display = display || Twitch.display.capitalize(from_user);
if ( msgObject.style === 'action' )
msg = '* ' + display + ' ' + msg;
@ -602,7 +646,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
var mirror_url = utils.quote_attr(constants.EMOTE_MIRROR_BASE + id + '.png');
extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"';
extra = ' data-emote="' + id + '"'; // onerror="FrankerFaceZ._emote_mirror_swap(this)"'; // Disable error checking for now.
if ( ! constants.EMOTE_REPLACEMENTS[id] )
srcset = build_srcset(id);

View file

@ -1,6 +1,6 @@
var FFZ = window.FrankerFaceZ,
constants = require("../constants"),
styles = require("../styles");
constants = require("../constants");
//styles = require("../styles");
// ---------------------
@ -211,7 +211,6 @@ FFZ.prototype._load_dark_css = function() {
s.id = "ffz-dark-css";
s.setAttribute('rel', 'stylesheet');
s.setAttribute('href', constants.SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
s.onerror = "this.href = this.href + '_';"
s.setAttribute('href', constants.DIRECT_SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
document.head.appendChild(s);
}

View file

@ -181,6 +181,11 @@ FFZ.prototype.show_notification = function(message, title, tag, timeout, on_clic
// ---------------------
FFZ.prototype.show_message = function(message) {
if ( ! window.jQuery || ! window.jQuery.noty || ! jQuery.noty.themes.ffzTheme ) {
setTimeout(this.show_message.bind(this, message), 50);
return;
}
window.noty({
text: message,
theme: "ffzTheme",

View file

@ -1,18 +1,24 @@
var FFZ = window.FrankerFaceZ,
constants = require('../constants'),
styles = require('../styles');
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('style');
var s = this._main_style = document.createElement('link');
s.id = "ffz-main-css";
s.setAttribute('rel', 'stylesheet');
s.setAttribute('href', constants.DIRECT_SERVER + "script/style.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
document.head.appendChild(s);
/*var s = this._main_style = document.createElement('style');
s.textContent = styles.style;
s.id = "ffz-ui-css";
document.head.appendChild(s);
document.head.appendChild(s);*/
if ( window.jQuery && jQuery.noty )
jQuery.noty.themes.ffzTheme = {

View file

@ -42,6 +42,9 @@ var sanitize_el = document.createElement('span'),
date_regex = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/,
parse_date = function(str) {
if ( typeof str === "number" )
return new Date(str);
var parts = str.match(date_regex);
if ( ! parts )
return null;