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

3.5.217. Well, this commit is a bit late. Look at changelog.html for details on each version's changes.

This commit is contained in:
SirStendec 2016-06-22 14:23:03 -04:00
parent 2f7dc1d8d3
commit 8cfef363f1
22 changed files with 634 additions and 191 deletions

View file

@ -67,7 +67,7 @@
color:rgb(222,222,222)!important;
}
.ffz-dark .moderation-card .button:not(.glyph-only):hover {
.ffz-dark .moderation-card .button:not(.button--icon-only):hover {
color: #fff !important;
}
@ -204,10 +204,16 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box { border: none }
.ffz-dark .balloon--up:after { box-shadow: 1px 1px 0 rgba(255,255,255,0.2) }
.ffz-dark .balloon--right.balloon--down:after,
.ffz-dark .balloon--down:after { box-shadow: -1px -1px 0 rgba(255,255,255,0.2) }
.ffz-dark .balloon--left:after { box-shadow: 1px -1px 0 rgba(255,255,255,0.2) }
.ffz-dark .balloon--right:after { box-shadow: -1px 1px 0 rgba(255,255,255,0.2) }
.ffz-dark .balloon__footer {
background-color: rgb(25,25,25);
border: 1px solid rgba(255,255,255,0.2);
}
.ffz-dark .player-menu__header { color: #c3c3c3 }
.ffz-dark .player-menu__section { border-bottom-color: rgba(255,255,255,0.2) }
@ -349,15 +355,17 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: #25252a;
}
.ffz-dark .button:not(.button--status):not(.primary) {
.ffz-dark .message-button,
.ffz-dark .button--text,
.ffz-dark .button.ffz-no-bg {
color: #a68ed2;
}
.ffz-dark .button.glyph-only svg path {
.ffz-dark .button.button--icon-only svg path {
fill: #a68ed2;
}
.ffz-dark .button.glyph-only:hover svg path {
.ffz-dark .button.button--icon-only:hover svg path {
fill: #fff;
}
@ -565,10 +573,6 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .toggle-darkmode { display: none; }
/* Chat Text Contrast */
.ffz-dark .ember-chat-container.dark .chat-line,
.ffz-dark .chat-container.dark .chat-line {
color: #acacbf;
}
.ffz-dark .ember-chat .chat-settings .chat-colors .chat-colors-swatch:hover,
.ffz-dark .ember-chat .chat-settings .chat-colors .chat-colors-switch.selected {
@ -956,6 +960,7 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark #mantle_skin ul.vtabs li .not_linked,
.ffz-dark #mantle_skin ul.vtabs li.selected a,
.ffz-dark #mantle_skin ul.submenu li a:hover,
.ffz-dark .sort-contain .sort-options li a:hover,
.ffz-dark #mantle_skin ul.submenu .active a {
color: #fff !important;
}
@ -1128,8 +1133,8 @@ body.ffz-dark:not([data-page="teams#show"]),
fill: rgba(255,255,255,0.5);
}
.ffz-dark .conversation-input-bar .emoticon-selector-box .emote-set {
border-color: #323232;
.ffz-dark .emoticon-selector-box .emote-set {
border-color: #323232 !important;
}
.ffz-dark .conversation-input-bar .emoticon-selector-box .emoticon-grid {
@ -1203,28 +1208,26 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .activity-list-end svg { fill: #474747 }
.ffz-dark .activity-meta:before { background-color: #474747 }
.ffz-dark .activity-meta-divider:before { background-color: #474747 }
.ffz-dark .activity-react__item:hover {
.ffz-dark .activity-button:hover {
border-color: #d5d5d5;
background-color: #242424;
}
.ffz-dark .activity-react__item svg.endorse-icon #head__base { fill: #fff }
.ffz-dark .activity-create__actions {
background-color: #191919;
box-shadow: 0 -1px 0 #474747;
}
.ffz-dark .activity-create,
.ffz-dark .activity-react__item {
.ffz-dark .activity-button {
border-color: #474747;
color: #fff !important;
background-color: #191919;
}
.ffz-dark .activity-meta:before,
.ffz-dark .activity-meta-divider:before,
.ffz-dark .activity-card {
border-color: #474747;
}
@ -1233,11 +1236,12 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: #191919;
}
.ffz-dark .activity-meta {
.ffz-dark .activity-meta-divider {
box-shadow: inset 0 -1px 0 #474747;
}
.ffz-dark a.balloon__link:hover { color: #fff !important }
.ffz-dark .activity-meta__name { color: #ccc }
/* Search Panel */

View file

@ -128,8 +128,8 @@ FFZ.settings_info.sub_notice_badges = {
value: false,
category: "Chat Appearance",
name: "Subscriber Notice Badges",
help: "Display a subscriber badge on chat messages about new subscribers.",
name: "Old-Style Subscriber Notice Badges",
help: "Display a subscriber badge on old-style chat messages about new subscribers.",
on_update: function(val) {
this.toggle_style('badges-sub-notice', ! val);
@ -348,8 +348,16 @@ FFZ.prototype.get_line_badges = function(msg) {
globals = badgeCollection && badgeCollection.global || {},
channel = badgeCollection && badgeCollection.channel || {};
// Whisper Chat Lines have a non-associative array for some reason.
if ( Array.isArray(badge_tag) ) {
var val = badge_tag;
badge_tag = {};
for(var i=0; i < val.length; i++)
badge_tag[val[i].id] = val[i].version;
}
// VoD Chat lines don't have the badges pre-parsed for some reason.
if ( typeof badge_tag === 'string' ) {
else if ( typeof badge_tag === 'string' ) {
var val = badge_tag.split(',');
badge_tag = {};
for(var i=0; i < val.length; i++) {
@ -367,20 +375,6 @@ FFZ.prototype.get_line_badges = function(msg) {
var versions = channel[badge] || globals[badge],
binfo = versions && versions.versions && versions.versions[version];
if ( from === 'sirstendec' && badge === 'turbo' && globals.warcraft ) {
badge = 'warcraft';
version = 'protoss';
binfo = {
click_action: 'visit_url',
click_url: 'https://www.youtube.com/watch?v=dpBM2FIHprM',
description: 'My life for Aiur!',
title: 'Protoss',
image_url_1x: 'https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/1.png',
image_url_2x: 'https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/2.png',
image_url_3x: 'https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/4.png'
}
}
if ( hidden_badges.indexOf(badge) !== -1 )
continue;
@ -456,7 +450,7 @@ FFZ.prototype.render_badges = function(badges) {
if ( badge.click_url )
klass += ' click_url';
out.push('<div class="badge float-left tooltip ' + utils.quote_attr(klass) + '"' + (badge.click_url ? ' data-url="' + utils.quote_attr(badge.click_url) + '"' : '') + (css ? ' style="' + utils.quote_attr(css) + '"' : '') + ' title="' + utils.quote_attr(badge.title) + '"></div>');
out.push('<div class="badge float-left html-tooltip ' + utils.quote_attr(klass) + '"' + (badge.click_url ? ' data-url="' + utils.quote_attr(badge.click_url) + '"' : '') + (css ? ' style="' + utils.quote_attr(css) + '"' : '') + ' title="' + utils.quote_attr(badge.title) + '"></div>');
}
return out.join("");

View file

@ -46,12 +46,12 @@ FFZ.settings_info.fix_color = {
},
on_update: function(val) {
this.toggle_style('chat-colors-gray', !this.has_bttv && val === '-1');
this.toggle_style('chat-colors-gray', !this.has_bttv && val === '-1');
if ( ! this.has_bttv && val !== '-1' )
this._rebuild_colors();
}
};
if ( ! this.has_bttv && val !== '-1' )
this._rebuild_colors();
}
};
FFZ.settings_info.luv_contrast = {

View file

@ -3,6 +3,10 @@ var SVGPATH = 'm120.95 1.74c4.08-0.09 8.33-0.84 12.21 0.82 3.61 1.8 7 4.16 11.01
DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'),
SERVER = DEBUG ? "//localhost:8000/" : "https://cdn.frankerfacez.com/",
IS_OSX = navigator.platform ? navigator.platform.indexOf('Mac') !== -1 : /OS X/.test(navigator.userAgent),
IS_WIN = navigator.platform ? navigator.platform.indexOf('Win') !== -1 : /Windows/.test(navigator.userAgent),
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
@ -15,7 +19,9 @@ module.exports = FrankerFaceZ.constants = {
DEBUG: DEBUG,
SERVER: SERVER,
IS_OSX: navigator.platform ? navigator.platform.indexOf('Mac') !== -1 : /OS X/.test(navigator.userAgent),
IS_OSX: IS_OSX,
IS_WIN: IS_WIN,
META_NAME: IS_OSX ? "⌘" : (IS_WIN ? "Win" : "Meta"),
// Twitch Client ID for API Stuff
CLIENT_ID: "a3bc9znoz6vi8ozsoca0inlcr4fcvkl",

View file

@ -111,7 +111,6 @@ FFZ.prototype.setup_channel = function() {
var game = data.stream.game || (data.stream.channel && data.stream.channel.game);
if ( game ) {
t.set('content.game', game);
t.set('content.rollbackData.game', game);
}
if ( data.stream.channel ) {
@ -228,9 +227,9 @@ FFZ.prototype._modify_cindex = function(view) {
f.rebuild_race_ui();
if ( f.settings.auto_theater ) {
var Layout = utils.ember_lookup('service:layout');
if ( Layout )
Layout.set('isTheatreMode', true);
var player = f.players && f.players[id] && f.players[id].get('player');
if ( player )
player.setTheatre(true);
}
this.$().on("click", ".ffz-creative-tag-link", function(e) {
@ -296,7 +295,7 @@ FFZ.prototype._modify_cindex = function(view) {
if ( ! btn ) {
btn = document.createElement('span');
btn.id = 'ffz-ui-host-button';
btn.className = 'button action';
btn.className = 'button button--text';
btn.addEventListener('click', this.ffzClickHost.bind(this, false));
@ -336,7 +335,7 @@ FFZ.prototype._modify_cindex = function(view) {
if ( ! btn ) {
btn = document.createElement('span');
btn.id = 'ffz-ui-host-button';
btn.className = 'button action';
btn.className = 'button button--text';
btn.addEventListener('click', this.ffzClickHost.bind(this, true));

View file

@ -121,6 +121,35 @@ FFZ.settings_info.input_complete_emotes = {
}
FFZ.settings_info.input_complete_aliases = {
type: "select",
options: {
0: "Disabled",
1: "By Name or Alias",
2: "Aliases Only"
},
value: 1,
process_value: function(val) {
if ( typeof val === 'string' )
return parseInt(val) || 0;
return val;
},
category: "Chat Input",
no_bttv: true,
name: "Tab-Complete User Aliases",
help: "Use tab completion to complete aliases you've given to users rather than their username.",
on_update: function(val) {
if ( this._inputv )
Ember.propertyDidChange(this._inputv, 'ffz_name_suggestions');
}
}
FFZ.settings_info.input_complete_name_at = {
type: "boolean",
value: true,
@ -479,8 +508,11 @@ FFZ.prototype._modify_chat_input = function(component) {
ind = this.get('ffz_partial_word_start'),
text = this.get('textareaValue'),
content = ((f.settings.input_complete_name_at && item.type === 'user' && this.get('ffz_partial_word').charAt(0) === '@') ? '@' : '') +
((item.command_content && text.charAt(0) === '/' ?
first_char = text.charAt(0),
is_cmd = first_char === '/' || first_char === '.',
content = ((f.settings.input_complete_name_at && ! is_cmd && item.type === 'user' && this.get('ffz_partial_word').charAt(0) === '@') ? '@' : '') +
((item.command_content && is_cmd ?
item.command_content : item.content) || item.label),
trail = text.substr(ind + this.get('ffz_partial_word').length),
@ -664,26 +696,50 @@ FFZ.prototype._modify_chat_input = function(component) {
// Always include Users
var user_output = {};
var user_output = {},
alias_setting = f.settings.input_complete_aliases;
for(var i=0; i < suggestions.length; i++) {
var suggestion = suggestions[i],
name = suggestion.id;
name = suggestion.id,
alias = f.aliases[name];
if ( user_output[name] ) {
if ( user_output[name] && ! user_output[name].is_alias ) {
var token = user_output[name];
token.whispered |= suggestion.whispered;
if ( suggestion.timestamp > token.timestamp )
token.timestamp = suggestion.timestamp;
} else
output.push(user_output[name] = {
type: "user",
command_content: name,
label: FFZ.get_capitalization(name),
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User'
});
} else {
if ( alias_setting !== 2 )
output.push(user_output[name] = {
type: "user",
command_content: name,
label: FFZ.get_capitalization(name),
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User',
is_alias: false
});
if ( alias && alias_setting ) {
if ( user_output[alias] && user_output[alias].is_alias ) {
var token = user_output[name];
token.whispered |= suggestion.whispered;
token.timestamp = Math.max(token.timestamp, suggestion.timestamp);
} else if ( ! user_output[alias] )
output.push(user_output[alias] = {
type: "user",
command_content: name,
label: alias,
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User Alias',
is_alias: true
});
}
}
}
return output;

View file

@ -974,12 +974,12 @@ FFZ.prototype._modify_cview = function(view) {
if ( current_channel ) {
icon.innerHTML = constants.CAMERA;
row.title = "Current Channel";
row.classList.add('tooltip');
row.classList.add('html-tooltip');
} else if ( host_channel ) {
icon.innerHTML = constants.EYE;
row.title = "Hosted Channel";
row.classList.add('tooltip');
row.classList.add('html-tooltip');
}
name_el.className = 'ffz-room';
@ -1008,7 +1008,7 @@ FFZ.prototype._modify_cview = function(view) {
});
} else {
btn = document.createElement('a');
btn.className = 'leave-chat tooltip';
btn.className = 'leave-chat html-tooltip';
btn.innerHTML = constants.CLOSE;
btn.title = 'Leave Group';
@ -1090,9 +1090,9 @@ FFZ.prototype._modify_cview = function(view) {
// Chat Room Management Button
link.className = 'button glyph-only';
link.className = 'button button--icon-only';
link.title = "Chat Room Management";
link.innerHTML = constants.ROOMS + '<span class="notifications"></span>';
link.innerHTML = '<figure class="icon">' + constants.ROOMS + '</figure><span class="notifications"></span>';
jQuery(link).tipsy({gravity: "n", offset: 5});
@ -1106,9 +1106,9 @@ FFZ.prototype._modify_cview = function(view) {
// Invite Button
link = document.createElement('a'),
link.className = 'button glyph-only tooltip invite';
link.className = 'button button--icon-only html-tooltip invite';
link.title = "Invite a User";
link.innerHTML = constants.INVITE;
link.innerHTML = '<figure class="icon">' + constants.INVITE + '</figure>';
link.addEventListener('click', function() {
var controller = view.get('controller');
@ -1170,7 +1170,7 @@ FFZ.prototype._modify_cview = function(view) {
tab.setAttribute('data-room', room_id);
tab.className = 'ffz-chat-tab tooltip';
tab.className = 'ffz-chat-tab html-tooltip';
tab.classList.toggle('current-channel', current_channel);
tab.classList.toggle('host-channel', host_channel);
tab.classList.toggle('group-chat', group);

View file

@ -224,7 +224,7 @@ FFZ.prototype._modify_conversation_line = function(component) {
colored = style ? ' has-color' : '';
if ( alias )
e.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
e.push('<span class="from ffz-alias html-tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.quote_san(name) + '">' + utils.sanitize(alias) + '</span>');
else
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');

View file

@ -189,13 +189,13 @@ FFZ.prototype.setup_profile_following = function() {
this._hook_followers(followers);
var counted = false;
if ( following.get('isLoaded') || following.get('isLoading') ) {
if ( following && (following.get('isLoaded') || following.get('isLoading')) ) {
refresher(following);
count++;
counted = true;
}
if ( followers.get('isLoaded') || followers.get('isLoading') ) {
if ( followers && (followers.get('isLoaded') || followers.get('isLoading')) ) {
refresher(followers);
if ( ! counted )
count++;
@ -285,8 +285,8 @@ FFZ.prototype._modify_display_followed_item = function(component) {
return;
var actions = createElement('div', 'actions'),
follow = createElement('button', 'button follow'),
notif = createElement('button', 'button notifications'),
follow = createElement('button', 'button ffz-no-bg follow'),
notif = createElement('button', 'button ffz-no-bg notifications'),
update_follow = function() {
data = user_cache[user_id];
@ -358,7 +358,7 @@ FFZ.prototype._modify_display_followed_item = function(component) {
FFZ.prototype._hook_following = function(Following) {
var f = this;
if ( Following.ffz_hooked )
if ( ! Following || Following.ffz_hooked )
return;
Following.reopen({
@ -398,7 +398,7 @@ FFZ.prototype._hook_following = function(Following) {
FFZ.prototype._hook_followers = function(Followers) {
var f = this;
if ( Followers.ffz_hooked )
if ( ! Followers || Followers.ffz_hooked )
return;
Followers.reopen({

View file

@ -299,7 +299,7 @@ FFZ.settings_info.chat_rows = {
on_update: function(val) {
this.toggle_style('chat-background', !this.has_bttv && val);
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_separators));
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_separators || this.settings.highlight_messages_with_mod_card));
}
};
@ -332,7 +332,7 @@ FFZ.settings_info.chat_separators = {
help: "Display thin lines between chat messages for further visual separation.",
on_update: function(val) {
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_rows));
this.toggle_style('chat-setup', !this.has_bttv && (val || this.settings.chat_rows || this.settings.highlight_messages_with_mod_card));
this.toggle_style('chat-separator', !this.has_bttv && val);
this.toggle_style('chat-separator-3d', !this.has_bttv && val === 2);
@ -342,6 +342,18 @@ FFZ.settings_info.chat_separators = {
};
FFZ.settings_info.old_sub_notices = {
type: "boolean",
value: false,
category: "Chat Appearance",
no_bttv: true,
name: "Old-Style Subscriber Notices",
help: "Display the old style subscriber notices, with the message on a separate line."
};
FFZ.settings_info.chat_padding = {
type: "boolean",
value: false,
@ -577,7 +589,7 @@ FFZ.prototype.setup_line = function() {
// Chat Enhancements
document.body.classList.toggle('ffz-alias-italics', this.settings.alias_italics);
this.toggle_style('chat-setup', !this.has_bttv && (this.settings.chat_rows || this.settings.chat_separators));
this.toggle_style('chat-setup', !this.has_bttv && (this.settings.chat_rows || this.settings.chat_separators || this.settings.highlight_messages_with_mod_card));
this.toggle_style('chat-padding', !this.has_bttv && this.settings.chat_padding);
this.toggle_style('chat-background', !this.has_bttv && this.settings.chat_rows);
@ -708,22 +720,22 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
if ( btn === false ) {
if ( deleted )
output += '<a class="mod-icon float-left tooltip unban" title="Unban User" href="#">Unban</a>';
output += '<a class="mod-icon float-left html-tooltip unban" title="Unban User" href="#">Unban</a>';
else
output += '<a class="mod-icon float-left tooltip ban" title="Ban User" href="#">Ban</a>';
output += '<a class="mod-icon float-left html-tooltip ban" title="Ban User" href="#">Ban</a>';
} else if ( btn === 600 )
output += '<a class="mod-icon float-left tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>';
output += '<a class="mod-icon float-left html-tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>';
else {
if ( typeof btn === "string" ) {
cmd = btn.replace(/{user}/g, user).replace(/ *<LINE> */, "\n");
tip = "Custom Command" + (cmd.indexOf("\n") !== -1 ? 's' : '') + '\n' + cmd;
tip = "Custom Command" + (cmd.indexOf("\n") !== -1 ? 's' : '') + '<br>' + utils.quote_san(cmd).replace('\n','<br>');
} else {
cmd = "/timeout " + user + " " + btn;
tip = "Timeout User (" + utils.duration_string(btn) + ")";
}
output += '<a class="mod-icon float-left tooltip' + (cmd.substr(0,9) === '/timeout' ? ' is-timeout' : '') + ' custom" data-cmd="' + utils.quote_attr(cmd) + '" title="' + utils.quote_attr(tip) + '" href="#">' + prefix + '</a>';
output += '<a class="mod-icon float-left html-tooltip' + (cmd.substr(0,9) === '/timeout' ? ' is-timeout' : '') + ' custom" data-cmd="' + utils.quote_attr(cmd) + '" title="' + tip + '" href="#">' + prefix + '</a>';
}
}
@ -750,10 +762,20 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode'))),
system_msg = this.get('systemMsg'),
output = '';
output = '<div class="indicator"></div>';
output = '<div class="indicator"></div><span class="timestamp float-left">' + this.get('timestamp') + '</span> ';
// System Message
if ( system_msg ) {
output += '<div class="system-msg">' + utils.sanitize(system_msg) + '</div>';
if ( this.get('shouldRenderMessageBody') === false )
return output;
}
// Timestamp
output += '<span class="timestamp float-left">' + this.get('timestamp') + '</span> ';
// Moderator Actions
output += this.buildModIconsHTML();
@ -767,10 +789,10 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]) || '',
colored = style ? ' has-color' + (is_replay ? ' replay-color' : '') : '';
output += '<span class="from' + (alias ? ' ffz-alias tooltip' : '') + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '');
output += '<span class="from' + (alias ? ' ffz-alias html-tooltip' : '') + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '');
if ( alias )
output += '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias);
output += '" title="' + utils.quote_san(name) + '">' + utils.sanitize(alias);
else
output += '">' + utils.sanitize(name);
@ -787,10 +809,10 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
to_colored = to_style ? ' has-color' : '';
output += "</span><svg class='svg-whisper-arrow' height='10px' version='1.1' width='16px'><polyline points='6 2, 10 6, 6 10, 6 2' /></svg>";
output += '<span class="to' + (to_alias ? ' ffz-alias tooltip' : '') + to_colored + '" style="' + to_style + (to_colors ? '" data=color="' + to_color : '');
output += '<span class="to' + (to_alias ? ' ffz-alias html-tooltip' : '') + to_colored + '" style="' + to_style + (to_colors ? '" data=color="' + to_color : '');
if ( to_alias )
output += '" title="' + utils.sanitize(to_name) + '">' + utils.sanitize(to_alias);
output += '" title="' + utils.quote_san(to_name) + '">' + utils.sanitize(to_alias);
else
output += '">' + utils.sanitize(to_name);
}
@ -833,10 +855,12 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
var el = this.get('element'),
output = this.buildSenderHTML();
if ( this.get('msgObject.deleted') )
output += this.buildDeletedMessageHTML()
else
output += this.buildMessageHTML();
// If this is a whisper, or if we should render the message body, render it.
if ( this.get('shouldRenderMessageBody') !== false )
if ( this.get('msgObject.deleted') )
output += this.buildDeletedMessageHTML()
else
output += this.buildMessageHTML();
el.innerHTML = output;
},
@ -932,10 +956,18 @@ FFZ.prototype._modify_chat_subline = function(component) {
return;
else if ( e.target.classList.contains('from') ) {
var n = this.$();
var n = this.get('element'),
bounds = n && n.getBoundingClientRect() || document.body.getBoundingClientRect(),
x = 0, right;
if ( bounds.left > 400 )
right = bounds.left - 40;
this.sendAction("showModOverlay", {
left: n.offset().left,
top: n.offset().top + n.height(),
left: bounds.left,
right: right,
top: bounds.top + bounds.height,
real_top: bounds.top,
sender: from
});
@ -995,7 +1027,7 @@ FFZ.prototype._modify_vod_line = function(component) {
return '<span class="mod-icons float-left">' +
(this.get('msgObject.deleted') ?
'<em class="mod-icon float-left unban"></em>' :
'<a class="mod-icon float-left tooltip delete" title="Delete Message" href="#">Delete</a>') + '</span>';
'<a class="mod-icon float-left html-tooltip delete" title="Delete Message" href="#">Delete</a>') + '</span>';
},
buildDeletedMesageHTML: function() {

View file

@ -1,6 +1,7 @@
var FFZ = window.FrankerFaceZ,
utils = require("../utils"),
constants = require("../constants"),
styles = require("../compiled_styles"),
helpers,
TO_REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/,
@ -66,24 +67,112 @@ FFZ.basic_settings.chat_hover_pause = {
};
FFZ.settings_info.chat_hover_pause = {
FFZ.settings_info.highlight_messages_with_mod_card = {
type: "boolean",
value: false,
no_bttv: true,
category: "Chat Moderation",
name: "Highlight Messages with Mod Card Open",
help: "Highlight a user's messages in chat when their moderation card is open.",
on_update: function(val) {
this.toggle_style('chat-setup', !this.has_bttv && (this.settings.chat_rows || this.settings.chat_separators || val));
if ( ! this._mod_card )
return;
if ( val )
utils.update_css(this._chat_style, 'mod-card-highlight', styles['chat-user-bg'].replace(/{user_id}/g, this._mod_card.get('cardInfo.user.id')));
else
utils.update_css(this._chat_style, 'mod-card-highlight');
}
};
FFZ.settings_info.chat_mod_icon_visibility = {
type: "select",
options: {
0: "Disabled",
1: "Enabled",
2: "When Ctrl is Held",
3: "When " + constants.META_NAME + " is Held",
4: "When Alt is Held",
5: "When Shift is Held"
},
value: function() {
var settings = utils.ember_lookup('controller:settings');
return (settings && settings.get('settings.showModIcons')) ? 1 : 0;
},
process_value: function(val) {
if ( typeof val === "string" )
return parseInt(val) || 0;
return val;
},
no_bttv: true,
category: "Chat Moderation",
name: "Pause Chat Scrolling on Mouse Hover",
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link misclicks.",
name: "Display In-Line Mod Icons",
help: "Choose when you should see in-line moderation icons in chat.",
on_update: function(val) {
var settings = utils.ember_lookup('controller:settings');
if ( settings )
settings.set('settings.showModIcons', val === 1);
}
}
FFZ.settings_info.chat_hover_pause = {
type: "select",
options: {
0: "Disabled",
1: "On Hover",
2: "When Ctrl is Held",
3: "When " + constants.META_NAME + " is Held",
4: "When Alt is Held",
5: "When Shift is Held",
6: "Ctrl or Hover",
7: constants.META_NAME + " or Hover",
8: "Alt or Hover",
9: "Shift or Hover"
},
value: 0,
process_value: function(val) {
if ( val === true )
return 1;
else if ( val === false )
return 0;
else if ( typeof val === "string" )
return parseInt(val) || 0;
return val;
},
no_bttv: true,
category: "Chat Moderation",
name: "Pause Chat Scrolling",
help: "Automatically prevent the chat from scrolling when moving the mouse over it or holding Ctrl to prevent moderation mistakes and link misclicks.",
on_update: function(val) {
if ( ! this._roomv )
return;
this._roomv.ffzDisableFreeze();
// Remove the old warning to make sure the label updates.
var el = this._roomv.get('element'),
warning = el && el.querySelector('.chat-interface .more-messages-indicator.ffz-freeze-indicator');
if ( warning )
warning.parentElement.removeChild(warning);
if ( val )
this._roomv.ffzEnableFreeze();
else
this._roomv.ffzDisableFreeze();
}
};
@ -495,6 +584,17 @@ FFZ.prototype.setup_mod_card = function() {
} catch(err) { }
this.log("Listening to the Settings controller to catch mod icon state changes.");
var f = this,
Settings = utils.ember_lookup('controller:settings');
if ( Settings )
Settings.addObserver('settings.showModIcons', function() {
if ( Settings.get('settings.showModIcons') )
f.settings.set('chat_mod_icon_visibility', 1);
});
this.log("Modifying Mousetrap stopCallback so we can catch ESC.");
var orig_stop = Mousetrap.stopCallback;
Mousetrap.stopCallback = function(e, element, combo) {
@ -511,8 +611,7 @@ FFZ.prototype.setup_mod_card = function() {
this.log("Hooking the Ember Moderation Card view.");
var Card = utils.ember_resolve('component:chat/moderation-card'),
f = this;
var Card = utils.ember_resolve('component:chat/moderation-card');
Card.reopen({
ffzForceRedraw: function() {
@ -520,6 +619,10 @@ FFZ.prototype.setup_mod_card = function() {
if ( f.settings.mod_card_history )
this.ffzRenderHistory();
// Highlight this user's chat messages.
if ( f.settings.highlight_messages_with_mod_card )
utils.update_css(f._chat_style, 'mod-card-highlight', styles['chat-user-bg'].replace(/{user_id}/g, this.get('cardInfo.user.id')));
}.observes("cardInfo.isModeratorOrHigher", "cardInfo.user.id"),
ffzRebuildInfo: function() {
@ -528,12 +631,12 @@ FFZ.prototype.setup_mod_card = function() {
if ( ! info )
return;
var out = '<span class="stat tooltip" title="Total Views">' + constants.EYE + ' ' + utils.number_commas(this.get('cardInfo.user.views') || 0) + '</span>',
var out = '<span class="stat html-tooltip" title="Total Views">' + constants.EYE + ' ' + utils.number_commas(this.get('cardInfo.user.views') || 0) + '</span>',
since = utils.parse_date(this.get('cardInfo.user.created_at') || ''),
followers = this.get('cardInfo.user.ffz_followers');
if ( typeof followers === "number" ) {
out += '<span class="stat tooltip" title="Followers">' + constants.HEART + ' ' + utils.number_commas(followers || 0) + '</span>';
out += '<span class="stat html-tooltip" title="Followers">' + constants.HEART + ' ' + utils.number_commas(followers || 0) + '</span>';
} else if ( followers === undefined ) {
var t = this;
@ -550,7 +653,7 @@ FFZ.prototype.setup_mod_card = function() {
var now = Date.now() - (f._ws_server_offset || 0),
age = Math.floor((now - since.getTime()) / 1000);
if ( age > 0 ) {
out += '<span class="stat tooltip" title="Member Since: ' + (age > 86400 ? since.toLocaleDateString() : since.toLocaleString()) + '">' + constants.CLOCK + ' ' + utils.human_time(age, 10) + '</span>';
out += '<span class="stat html-tooltip" title="Member Since: ' + utils.quote_san(age > 86400 ? since.toLocaleDateString() : since.toLocaleString()) + '">' + constants.CLOCK + ' ' + utils.human_time(age, 10) + '</span>';
}
}
@ -567,6 +670,9 @@ FFZ.prototype.setup_mod_card = function() {
willDestroy: function() {
if ( f._mod_card === this )
f._mod_card = undefined;
utils.update_css(f._chat_style, 'mod-card-highlight');
this._super();
},
@ -594,6 +700,8 @@ FFZ.prototype.setup_mod_card = function() {
user_id = controller.get('cardInfo.user.id'),
alias = f.aliases[user_id],
handle_key,
ban_reason = function() {
return ban_reasons && ban_reasons.value ? ' ' + ban_reasons.value : "";
};
@ -601,6 +709,9 @@ FFZ.prototype.setup_mod_card = function() {
this.ffz_room_id = room_id;
// Highlight this user's chat messages.
if ( f.settings.highlight_messages_with_mod_card )
utils.update_css(f._chat_style, 'mod-card-highlight', styles['chat-user-bg'].replace(/{user_id}/g, user_id));
// Action Override
this.set('banAction', function(e) {
@ -677,7 +788,7 @@ FFZ.prototype.setup_mod_card = function() {
},
add_btn_make = function(cmd) {
var btn = document.createElement('button'),
var btn = utils.createElement('button', 'button ffz-no-bg'),
segment = cmd.split(' ', 1)[0],
title = cmds[segment] > 1 ? cmd.split(' ', cmds[segment]) : [segment];
@ -686,7 +797,6 @@ FFZ.prototype.setup_mod_card = function() {
title = _.map(title, function(s){ return s.capitalize() }).join(' ');
btn.className = 'button';
btn.innerHTML = utils.sanitize(title);
btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}'));
@ -718,7 +828,7 @@ FFZ.prototype.setup_mod_card = function() {
if ( f.settings.mod_card_hotkeys ) {
el.classList.add('no-mousetrap');
el.addEventListener('keyup', function(e) {
handle_key = function(e) {
var key = e.keyCode || e.which,
user_id = controller.get('cardInfo.user.id'),
is_mod = controller.get('cardInfo.isModeratorOrHigher'),
@ -753,7 +863,9 @@ FFZ.prototype.setup_mod_card = function() {
return;
t.get('closeAction')();
});
};
el.addEventListener('keyup', handle_key);
}
@ -773,13 +885,13 @@ FFZ.prototype.setup_mod_card = function() {
btn_make = function(timeout) {
var btn = document.createElement('button')
btn.className = 'button';
btn.className = 'button ffz-no-bg';
btn.innerHTML = utils.duration_string(timeout);
btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : "");
if ( f.settings.mod_card_hotkeys && timeout === 600 )
btn.title = "(T)" + btn.title.substr(1);
else if ( f.settings.mod_card_hotkeys && timeout === 1 )
else if ( f.settings.mod_card_hotkeys && timeout === 1 )
btn.title = "(P)urge - " + btn.title;
jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
@ -812,7 +924,7 @@ FFZ.prototype.setup_mod_card = function() {
if ( f.settings.mod_card_reasons && f.settings.mod_card_reasons.length ) {
// Moderation Reasons
line = utils.createElement('div', 'extra-interface interface clearfix');
ban_reasons = utils.createElement('select', 'ffz-ban-reasons', '<option value="">Select a Ban (R)eason</option>');
ban_reasons = utils.createElement('select', 'ffz-ban-reasons', '<option value="">Select a Ban ' + (f.settings.mod_card_hotkeys ? '(R)' : 'R') + 'eason</option>');
line.appendChild(ban_reasons);
for(var i=0; i < f.settings.mod_card_reasons.length; i++) {
@ -832,8 +944,8 @@ FFZ.prototype.setup_mod_card = function() {
// Unban Button
var unban_btn = document.createElement('button');
unban_btn.className = 'unban button glyph-only light';
unban_btn.innerHTML = CHECK;
unban_btn.className = 'unban button button--icon-only light';
unban_btn.innerHTML = '<figure class="icon">' + CHECK + '</figure>';
unban_btn.title = (f.settings.mod_card_hotkeys ? "(U)" : "U") + "nban User";
jQuery(unban_btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
@ -843,6 +955,10 @@ FFZ.prototype.setup_mod_card = function() {
}
// Tooltips for ban and ignore.
jQuery("button.ignore, button.ban").tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
// More Fixing Other Buttons
var op_btn = el.querySelector('button.mod');
if ( op_btn ) {
@ -863,7 +979,7 @@ FFZ.prototype.setup_mod_card = function() {
var msg_btn = el.querySelector(".interface > button.message-button");
if ( msg_btn ) {
msg_btn.innerHTML = 'W';
msg_btn.classList.add('glyph-only');
msg_btn.classList.add('button--icon-only');
msg_btn.classList.add('message');
msg_btn.title = "Whisper User";
@ -871,8 +987,8 @@ FFZ.prototype.setup_mod_card = function() {
var real_msg = document.createElement('button');
real_msg.className = 'message-button button glyph-only message tooltip';
real_msg.innerHTML = MESSAGE;
real_msg.className = 'message-button button button--icon-only message html-tooltip';
real_msg.innerHTML = '<figure class="icon">' + MESSAGE + '</figure>';
real_msg.title = "Message User";
real_msg.addEventListener('click', function() {
@ -885,8 +1001,8 @@ FFZ.prototype.setup_mod_card = function() {
// Alias Button
var alias_btn = document.createElement('button');
alias_btn.className = 'alias button glyph-only tooltip';
alias_btn.innerHTML = constants.EDIT;
alias_btn.className = 'alias button button--icon-only html-tooltip';
alias_btn.innerHTML = '<figure class="icon">' + constants.EDIT + '</figure>';
alias_btn.title = "Set Alias";
alias_btn.addEventListener('click', function() {
@ -932,26 +1048,7 @@ FFZ.prototype.setup_mod_card = function() {
this.ffzRenderHistory();
// Reposition the menu if it's off-screen.
var el_bound = el.getBoundingClientRect(),
body_bound = document.body.getBoundingClientRect(),
renderBottom = this.get('cardInfo.renderBottom'),
renderRight = this.get('cardInfo.renderRight');
if ( renderRight ) {
var offset = (el_bound.left + el_bound.width) - renderRight;
el.style.left = (el_bound.left - offset) + "px";
}
if ( renderBottom ) {
var offset = el_bound.bottom - renderBottom;
el.style.top = (el_bound.top - offset) + "px";
} else if ( el_bound.bottom > body_bound.bottom ) {
var offset = el_bound.bottom - body_bound.bottom;
if ( el_bound.top - offset > body_bound.top )
el.style.top = (el_bound.top - offset) + "px";
}
this.ffzReposition();
// Focus the Element
this.$().draggable({
@ -968,6 +1065,30 @@ FFZ.prototype.setup_mod_card = function() {
}
},
ffzReposition: function() {
var el = this.get('element'),
el_bound = el.getBoundingClientRect(),
body_bound = document.body.getBoundingClientRect(),
renderBottom = this.get('cardInfo.renderBottom'),
renderRight = this.get('cardInfo.renderRight');
if ( renderRight ) {
var offset = (el_bound.left + el_bound.width) - renderRight;
el.style.left = (el_bound.left - offset) + "px";
}
if ( renderBottom ) {
var offset = el_bound.bottom - renderBottom;
el.style.top = (el_bound.top - offset) + "px";
} else if ( el_bound.bottom > body_bound.bottom ) {
var offset = el_bound.bottom - body_bound.bottom;
if ( el_bound.top - offset > body_bound.top )
el.style.top = (el_bound.top - offset) + "px";
}
}.observes('cardInfo.renderTop', 'cardInfo.renderLeft', 'cardInfo.renderRight', 'cardInfo.renderBottom'),
ffzRenderHistory: function() {
var t = this,
Chat = utils.ember_lookup('controller:chat'),
@ -1093,7 +1214,7 @@ FFZ.prototype.setup_mod_card = function() {
logs = document.createElement('ul');
back = document.createElement('button');
back.className = 'button back-button';
back.className = 'button ffz-no-bg back-button';
back.innerHTML = '&laquo; Back';
back.addEventListener('click', function() {
@ -1184,7 +1305,7 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
if ( alias )
out.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
out.push('<span class="from ffz-alias html-tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.quote_san(name) + '">' + utils.sanitize(alias) + '</span>');
else
out.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name ) + '</span>');
@ -1287,6 +1408,11 @@ FFZ.prototype._update_alias = function(user) {
el_from.title = alias ? cap_name : '';
}
// Update tab completion.
if ( this._inputv )
Ember.propertyDidChange(this._inputv, 'ffz_name_suggestions');
// TODO: Update conversations~
}

View file

@ -77,10 +77,10 @@ FFZ.prototype.setup_room = function() {
this.set("showModerationCard", true);
// We pass in renderBottom and renderRight, which we use to reposition the window
// after we know how big it actually is.
// after we know how big it actually is. This doesn't work a lot of the time.
this.set("moderationCardInfo", {
user: chan,
renderTop: e.top,
renderTop: e.real_top || e.top,
renderLeft: e.left,
renderBottom: e.bottom,
renderRight: e.right,
@ -171,6 +171,12 @@ FFZ.prototype._modify_rview = function(view) {
f._roomv = this;
this.ffz_frozen = false;
this.ffz_ctrl = false;
// Monitor the Ctrl key.
this._ffz_keyw = this.ffzOnKey.bind(this);
document.body.addEventListener('keydown', this._ffz_keyw);
document.body.addEventListener('keyup', this._ffz_keyw);
// Fix scrolling.
this._ffz_mouse_down = this.ffzMouseDown.bind(this);
@ -215,9 +221,43 @@ FFZ.prototype._modify_rview = function(view) {
if ( this._ffz_chat_display )
this._ffz_chat_display = undefined;
if ( this._ffz_keyw ) {
document.body.removeEventListener('keydown', this._ffz_keyw);
document.body.removeEventListener('keyup', this._ffz_keyw);
this._ffz_keyw = undefined;
}
this.ffzDisableFreeze();
},
ffzOnKey: function(event) {
this.ffz_ctrl = event.ctrlKey;
this.ffz_alt = event.altKey;
this.ffz_shift = event.shiftKey;
this.ffz_meta = event.metaKey;
var cmi = f.settings.chat_mod_icon_visibility;
if ( ! this._ffz_outside && cmi > 1 )
this.get('element').classList.toggle('show-mod-icons',
cmi === 2 ? this.ffz_ctrl :
cmi === 3 ? this.ffz_meta :
cmi === 4 ? this.ffz_alt :
this.ffz_shift);
if ( this._ffz_outside || f.settings.chat_hover_pause < 2 )
return;
// Okay, so at this point we should change the state of the freeze?
var should_freeze = this.ffzShouldBeFrozen(),
freeze_change = this.ffz_frozen !== should_freeze;
if ( freeze_change )
if ( should_freeze )
this.ffzFreeze();
else
this.ffzUnfreeze();
},
ffzUpdateStatus: function() {
var room = this.get('controller.model'),
el = this.get('element'),
@ -273,8 +313,8 @@ FFZ.prototype._modify_rview = function(view) {
if ( ! messages )
return;
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
this._ffz_messages = messages;
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
this._ffz_mouse_move = this.ffzMouseMove.bind(this);
this._ffz_mouse_out = this.ffzMouseOut.bind(this);
@ -311,11 +351,8 @@ FFZ.prototype._modify_rview = function(view) {
},
ffzPulse: function() {
if ( this.ffz_frozen ) {
var elapsed = Date.now() - this._ffz_last_move;
if ( elapsed > 750 )
this.ffzUnfreeze();
}
if ( this.ffz_frozen && ! this.ffzShouldBeFrozen() )
this.ffzUnfreeze();
},
ffzUnfreeze: function(from_stuck) {
@ -327,11 +364,17 @@ FFZ.prototype._modify_rview = function(view) {
this._scrollToBottom();
},
ffzFreeze: function() {
this.ffz_frozen = true;
if ( this.get('stuckToBottom') ) {
this.set('controller.model.messageBufferSize', f.settings.scrollback_length + 150);
this.ffzWarnPaused();
}
},
ffzMouseDown: function(event) {
var t = this._$chatMessagesScroller;
if ( t && t[0] && ((!this.ffz_frozen && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) {
if ( event.type === "mousedown" )
f.log("Freezing from mouse down!", event);
var r = t[0].scrollHeight - t[0].scrollTop - t[0].offsetHeight;
this._setStuckToBottom(10 >= r);
}
@ -341,29 +384,57 @@ FFZ.prototype._modify_rview = function(view) {
this._ffz_outside = true;
var e = this;
setTimeout(function() {
if ( e._ffz_outside )
if ( e._ffz_outside ) {
if ( f.settings.chat_mod_icon_visibility > 1 )
e.get('element').classList.toggle('show-mod-icons', false);
e.ffzUnfreeze();
}
}, 25);
},
ffzShouldBeFrozen: function(since) {
if ( since === undefined )
since = Date.now() - this._ffz_last_move;
var hp = f.settings.chat_hover_pause;
return (this.ffz_ctrl && (hp === 2 || hp === 6)) || (this.ffz_meta && (hp === 3 || hp === 7)) || (this.ffz_alt && (hp === 4 || hp === 8)) || (this.ffz_shift && (hp === 5 || hp === 9)) || (since < 750 && (hp === 1 || hp > 5));
},
ffzMouseMove: function(event) {
// Store the last move time.
this._ffz_last_move = Date.now();
this._ffz_outside = false;
if ( event.screenX === this._ffz_last_screenx && event.screenY === this._ffz_last_screeny )
// If nothing of interest has happened, stop.
if ( event.altKey === this.ffz_alt && event.shiftKey === this.ffz_shift && event.ctrlKey === this.ffz_ctrl && event.metaKey === this.ffz_meta && event.screenX === this._ffz_last_screenx && event.screenY === this._ffz_last_screeny )
return;
// Grab a bit of state.
this.ffz_ctrl = event.ctrlKey;
this.ffz_alt = event.altKey;
this.ffz_shift = event.shiftKey;
this.ffz_meta = event.metaKey;
this._ffz_last_screenx = event.screenX;
this._ffz_last_screeny = event.screenY;
if ( this.ffz_frozen )
return;
var cmi = f.settings.chat_mod_icon_visibility;
if ( ! this._ffz_outside && cmi > 1 )
this.get('element').classList.toggle('show-mod-icons',
cmi === 2 ? this.ffz_ctrl :
cmi === 3 ? this.ffz_meta :
cmi === 4 ? this.ffz_alt :
this.ffz_shift);
this.ffz_frozen = true;
if ( this.get('stuckToBottom') ) {
this.set('controller.model.messageBufferSize', f.settings.scrollback_length + 150);
this.ffzWarnPaused();
}
// Should the state have changed?
var should_freeze = this.ffzShouldBeFrozen(),
freeze_change = this.ffz_frozen !== should_freeze;
if ( freeze_change )
if ( should_freeze )
this.ffzFreeze();
else
this.ffzUnfreeze();
},
_scrollToBottom: _.throttle(function() {
@ -400,7 +471,19 @@ FFZ.prototype._modify_rview = function(view) {
if ( ! warning ) {
warning = document.createElement('div');
warning.className = 'more-messages-indicator ffz-freeze-indicator';
warning.innerHTML = '(Chat Paused Due to Mouse Movement)';
var hp = f.settings.chat_hover_pause,
label = hp === 2 ? 'Ctrl Key' :
hp === 3 ? (constants.META_NAME + ' Key') :
hp === 4 ? 'Alt Key' :
hp === 5 ? 'Shift Key' :
hp === 6 ? 'Ctrl or Mouse' :
hp === 7 ? (constants.META_NAME + ' or Mouse') :
hp === 8 ? 'Alt or Mouse' :
hp === 9 ? 'Shift or Mouse' :
'Mouse Movement';
warning.innerHTML = '(Chat Paused Due to ' + label + ')';
var cont = el.querySelector('.chat-interface');
if ( ! cont )
@ -923,8 +1006,10 @@ FFZ.prototype._modify_room = function(room) {
user = f.get_user(),
room_id = this.get('id');
if ( (Chat && Chat.get('currentChannelRoom') === this) || (user && user.login === room_id) || (f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1) )
return this.ffzUnsubscribe(true);
/* ???
if ( (Chat && Chat.get('currentChannelRoom') === this) || (user && user.login === room_id) || (f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1) )
f.ws_unsub()
return this.ffzUnsubscribe(true);*/
this.destroy();
},
@ -1284,6 +1369,31 @@ FFZ.prototype._modify_room = function(room) {
addMessage: function(msg) {
if ( msg ) {
var is_resub = msg.tags && msg.tags['msg-id'] === 'resub',
room_id = this.get('id');
// Ignore resubs in other rooms.
if ( is_resub && ! f.settings.hosted_sub_notices && (msg.tags['room-id'] != this.get('roomProperties._id') || HOSTED_SUB.test(msg.tags['system-msg'])) )
return;
// Split this into two messages if requested.
if ( is_resub && f.settings.old_sub_notices ) {
this.addMessage({
style: "notification",
from: "twitchnotify",
date: msg.date || new Date,
room: room_id,
message: msg.tags['system-msg']
});
// If there's no message just quit now.
if ( ! msg.message )
return;
// And delete the system message so it won't render weirdly.
msg.tags['system-msg'] = '';
}
var is_whisper = msg.style === 'whisper';
// Ignore whispers if conversations are enabled.
@ -1291,7 +1401,7 @@ FFZ.prototype._modify_room = function(room) {
return;
if ( ! is_whisper )
msg.room = this.get('id');
msg.room = room_id;
// Look up color and labels.
if ( this.tmiRoom && msg.from ) {
@ -1434,6 +1544,13 @@ FFZ.prototype._modify_room = function(room) {
try {
this.ffz_last_input = Date.now();
var first_char = text.charAt(0),
is_cmd = first_char === '/' || first_char === '.';
// Strip trailing whitespace from commands.
if ( is_cmd )
text = text.replace(/\s+$/, '');
if ( text && ! ignore_history ) {
// Command History
var mru = this.get('mru_list'),

View file

@ -37,7 +37,7 @@ FFZ.msg_commands = {};
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 203,
major: 3, minor: 5, revision: 216,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -228,7 +228,7 @@ FFZ.prototype.initialize = function(increment, delay) {
if ( location.hostname === 'passport.twitch.tv' || /^\/user\/two_factor/.test(location.pathname) )
return this.log("Found authentication sub-page. Not initializing.");
if ( ['im.twitch.tv', 'api.twitch.tv'].indexOf(location.hostname) !== -1 )
if ( ['im.twitch.tv', 'api.twitch.tv'].indexOf(location.hostname) !== -1 || /^\/products\//.test(location.pathname) )
return this.log("Found banned sub-domain. Not initializing.");
// Check for the player

View file

@ -9,6 +9,9 @@
}
.ember-chat .chat-messages .chat-line.brick--marked { padding-left: 8px }
/* Remove Extra Conversation Padding */
.conversation-window .conversation-chat-lines {
padding-top: 0;

View file

@ -16,3 +16,11 @@
.chat-history .chat-line:before {
top: 0; bottom: 0;
}
.chat-line.brick--marked {
box-shadow: none;
}
.chat-line.brick--marked:before {
box-shadow: 3px 0 0 #6441a4 inset;
}

View file

@ -0,0 +1,19 @@
.chat-lines .chat-line[data-sender="{user_id}"]:before {
background-color: rgba(0,127,255, 0.2);
}
.chat-lines .chat-line[data-sender="{user_id}"]:nth-child(2n+0):before {
background-color: rgba(0,127,255, 0.4);
}
.theatre .chat-lines .chat-line[data-sender="{user_id}"]:before,
.dark .chat-lines .chat-line[data-sender="{user_id}"]:before,
.force-dark .chat-lines .chat-line[data-sender="{user_id}"]:before {
background-color: rgba(0,63,127, 0.2);
}
.theatre .chat-lines .chat-line[data-sender="{user_id}"]:nth-child(2n+0):before,
.dark .chat-lines .chat-line[data-sender="{user_id}"]:nth-child(2n+0):before,
.force-dark .chat-lines .chat-line[data-sender="{user_id}"]:nth-child(2n+0):before {
background-color: rgba(0,63,127, 0.4);
}

View file

@ -3,6 +3,7 @@ var FFZ = window.FrankerFaceZ,
constants = require("./constants"),
helpers,
conv_helpers,
emote_helpers,
EXPLANATION_WARN = '<hr>This link has been sent to you via a whisper rather than standard chat, and has not been checked or approved of by any moderators or staff members. Please treat this link with caution and do not visit it if you do not trust the sender.',
@ -160,6 +161,12 @@ FFZ.prototype.setup_tokenization = function() {
this.error("Unable to get conversation helper functions.", err);
}
try {
emote_helpers = window.require && window.require("web-client/utilities/tmi-emotes").default;
} catch(err) {
this.error("Unable to get tmi-emotes helper function.", err);
}
this.log("Hooking Ember chat line helpers.");
var f = this;
@ -395,6 +402,9 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
if ( conv_helpers && conv_helpers.checkActionMessage )
tokens = conv_helpers.checkActionMessage(tokens);
if ( emote_helpers )
emotes = emote_helpers(emotes);
// Standard Tokenization
if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens);
@ -775,8 +785,8 @@ FFZ.prototype.render_token = function(render_links, warn_links, token) {
}
else if ( token.type === "deleted" )
return '<span class="deleted-word tooltip" title="' + utils.quote_attr(token.text) + '" data-text="' + utils.sanitize(token.text) + '">&times;&times;&times;</span>';
//return `<span class="deleted-word tooltip" title="${utils.quote_attr(token.text)}" data-text="${utils.sanitize(token.text)}">&times;&times;&times;</span>`;
return '<span class="deleted-word html-tooltip" title="' + utils.quote_san(token.text) + '" data-text="' + utils.sanitize(token.text) + '">&times;&times;&times;</span>';
//return `<span class="deleted-word html-tooltip" title="${utils.quote_attr(token.text)}" data-text="${utils.sanitize(token.text)}">&times;&times;&times;</span>`;
else if ( token.type === "mention" )
return '<span class="' + (token.isOwnMessage ? 'mentioning' : 'mentioned') + '">' + utils.sanitize(token.user) + '</span>';

View file

@ -69,7 +69,7 @@ FFZ.prototype.setup_following_count = function(has_ember) {
setTimeout(this._install_following_tooltips.bind(this), 2000);
// If we don't have Ember, no point in trying this stuff.
if ( ! has_ember )
if ( this.is_dashboard || ! has_ember )
return this._following_get_me();
this.log("Connecting to Live Streams model.");
@ -163,10 +163,10 @@ FFZ.prototype._update_following_count = function() {
f = this;
if ( HostLive && document.body.getAttribute('data-current-path').indexOf('directory.following') !== -1 )
if ( ! this.is_dashboard && HostLive && document.body.getAttribute('data-current-path').indexOf('directory.following') !== -1 )
HostLive.load();
if ( Live )
if ( ! this.is_dashboard && Live )
Live.load();
else {
var u = this.get_user();

View file

@ -612,9 +612,9 @@ FFZ.menu_pages.channel = {
unlock_text.innerHTML = "Subscribe to unlock Emoticons";
nonsub_message.appendChild(unlock_text);
sub_link.className = "action subscribe-button button primary";
sub_link.className = "action js-sub-button subscribe-button button button--purchase";
sub_link.href = product.get("product_url");
sub_link.innerHTML = '<span class="subscribe-text">Subscribe</span><span class="subscribe-price">' + product.get("price") + '</span>';
sub_link.innerHTML = '<span class="subscribe-text">Subscribe</span><span class="subscribe-price button__num-block">' + product.get("price") + '</span>';
nonsub_message.appendChild(sub_link);
inner.appendChild(sub_message);

View file

@ -116,7 +116,7 @@ FFZ.prototype.rebuild_race_ui = function() {
race_container.setAttribute('data-channel', channel_id);
var btn = document.createElement('span');
btn.className = 'button drop action';
btn.className = 'button button--text button--dropmenu';
btn.title = "SpeedRunsLive Race";
btn.innerHTML = '<span class="logo"></span>';
@ -148,7 +148,7 @@ FFZ.prototype.rebuild_race_ui = function() {
race_container.setAttribute('data-channel', hosted_id);
var btn = document.createElement('span');
btn.className = 'button drop action';
btn.className = 'button button--text button--dropmenu';
btn.title = "SpeedRunsLive Race";
btn.innerHTML = '<span class="logo"></span>';
@ -271,7 +271,7 @@ FFZ.prototype._update_race = function(container, not_timer) {
// Make sure we don't leave any tooltips lying around when we update.
// Of course, we should just rewrite logic to not constantly mutilate
// rows.
jQuery('.tooltip', tbody).trigger('mouseout');
jQuery('.html-tooltip', tbody).trigger('mouseout');
tbody.innerHTML = '';
var entrants = [], done = true;
@ -312,23 +312,23 @@ FFZ.prototype._update_race = function(container, not_timer) {
hitbox_link = ent.hitbox ? '<a target="_new" class="hitbox" href="http://www.hitbox.tv/' + utils.sanitize(ent.hitbox) + '"></a>' : '',
time = elapsed ? utils.time_to_string(ent.time||elapsed) : "",
place = utils.place_string(ent.place),
comment = ent.comment ? utils.sanitize(ent.comment) : "";
comment = ent.comment ? utils.quote_san(ent.comment) : "";
tbody.innerHTML += '<tr' + (comment ? ' title="' + comment + '"' : '') + ' class="' + ent.state + (comment ? ' tooltip' : '') + '"><td>' + place + '</td><td>' + name + '</td><td>' + twitch_link + hitbox_link + '</td><td class="time">' + (ent.state == "forfeit" ? "Forfeit" : time) + '</td></tr>';
tbody.innerHTML += '<tr' + (comment ? ' title="' + comment + '"' : '') + ' class="' + ent.state + (comment ? ' html-tooltip' : '') + '"><td>' + place + '</td><td>' + name + '</td><td>' + twitch_link + hitbox_link + '</td><td class="time">' + (ent.state == "forfeit" ? "Forfeit" : time) + '</td></tr>';
}
if ( this._race_game != race.game || this._race_goal != race.goal ) {
this._race_game = race.game;
this._race_goal = race.goal;
var game = utils.sanitize(race.game),
var game = utils.quote_san(race.game),
goal = utils.unquote_attr(race.goal),
old_goal = popup.getAttribute('data-old-goal');
if ( goal !== old_goal ) {
popup.setAttribute('data-old-goal', goal);
goal = goal ? this.render_tokens(this.tokenize_line("jtv", null, goal, true)) : '';
info.innerHTML = '<h2 class="tooltip" title="' + game + '">' + game + '</h2><span class="goal"><b>Goal: </b>' + goal + '</span>';
info.innerHTML = '<h2 class="html-tooltip" title="' + game + '">' + game + '</h2><span class="goal"><b>Goal: </b>' + goal + '</span>';
}
}

View file

@ -29,6 +29,10 @@ var sanitize_el = document.createElement('span'),
return msg.replace(R_AMP, "&amp;").replace(R_QUOTE, "&quot;").replace(R_SQUOTE, "&apos;").replace(R_LT, "&lt;").replace(R_GT, "&gt;");
},
quote_san = function(msg) {
return sanitize(msg).replace(R_QUOTE, "&quot;").replace(R_SQUOTE, "&apos;");
},
HUMAN_NUMBERS = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
],
@ -333,6 +337,10 @@ module.exports = FFZ.utils = {
closer = show_modal(contents, cb, width);
try {
input.focus();
} catch(err) { }
form.addEventListener('submit', function(e) { e.preventDefault(); cb(true); return false });
okay_btn.addEventListener('click', function(e) { e.preventDefault(); cb(true); return false });
close_btn.addEventListener('click', function(e) { e.preventDefault(); cb(false); return false });
@ -423,6 +431,7 @@ module.exports = FFZ.utils = {
sanitize: sanitize,
unquote_attr: unquote_attr,
quote_attr: quote_attr,
quote_san: quote_san,
date_string: function(date) {
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate();

View file

@ -325,7 +325,7 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
color: #a68ed2;
}
.ffz-theater-stats .app-main.theatre .button.glyph-only svg path {
.ffz-theater-stats .app-main.theatre .button.button--icon-only svg path {
fill: #a68ed2;
}
@ -336,6 +336,8 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
/* SRL Race Support */
#ffz-ui-host-button { vertical-align: middle }
#ffz-following-popup.right {
right: 0;
left: auto;
@ -354,6 +356,7 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
#ffz-ui-race .button span.logo {
padding-left: 44px;
margin-bottom: -10px;
background-image: url("//cdn.frankerfacez.com/script/srl_button.png");
}
@ -1288,8 +1291,11 @@ img.channel_background[src="null"] { display: none; }
.ffz-moderation-card button {
margin: 0;
padding: 0 5px;
color: #6441a4;
}
.ffz-moderation-card .mod-controls button figure { padding: 0 }
.ember-chat .ffz-moderation-card .mod-controls button {
width: auto;
margin-right: 10px;
@ -1306,10 +1312,13 @@ img.channel_background[src="null"] { display: none; }
padding-right: 0 !important;
}
.ffz-moderation-card .button.glyph-only { padding: 0 !important }
.ffz-moderation-card .button.button--icon-only {
padding: 0 !important;
margin-right: 5px;
}
.ffz-moderation-card button:not(.glyph-only):hover,
.ffz-moderation-card button:not(.glyph-only):focus {
.ffz-moderation-card button:not(.button--icon-only):hover,
.ffz-moderation-card button:not(.button--icon-only):focus {
color: #fff;
background-color: rgba(117,80,186, 1);
}
@ -1404,6 +1413,13 @@ img.channel_background[src="null"] { display: none; }
/* Chat Rows */
.theatre .conversation-window .conversation-chat-line,
.dark .chat-line,
.force-dark .chat-line,
.theatre .chat-line {
color: #acacbf;
}
.ffz-alias-italics .ffz-alias { font-style: italic; }
.ember-chat .chat-messages .chat-line.ffz-has-deleted {
@ -1720,7 +1736,7 @@ th.ffz-row-switch {
margin-right: 4px;
}
#ffz-group-tabs .button.glyph-only svg {
#ffz-group-tabs .button.button--icon-only svg {
margin: 6px 0;
}
@ -2216,6 +2232,14 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
background-color: #191919;
}
.ffz-no-blue .theatre .conversation-input-bar textarea,
.ffz-no-blue .theatre input.text,
.ffz-no-blue .theatre input.countries-input,
.ffz-no-blue .theatre .recurly input,
.ffz-no-blue .recurly .theatre input {
background-color: #202020;
}
.ffz-no-blue .warp__anchor,
.ffz-no-blue .warp__item--anchor,
.ffz-no-blue .warp__drawer,
@ -2966,6 +2990,14 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
background-size: 100%
}*/
/* Button Fix */
.ffz-no-bg {
background: transparent;
}
/* Odd Badges */
.badge.click_url { cursor: pointer }
@ -2981,8 +3013,36 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
background-image: image-set(url("https://cdn.frankerfacez.com/badges/twitch/warcraft/horde/1.png") 1x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/horde/2.png") 2x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/horde/4.png") 4x);
}
.badge.warcraft.version-protoss {
background: url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/1.png") #5bc7ff;
background-image: -webkit-image-set(url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/1.png") 1x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/2.png") 2x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/4.png") 4x);
background-image: image-set(url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/1.png") 1x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/2.png") 2x,url("https://cdn.frankerfacez.com/badges/twitch/warcraft/protoss/4.png") 4x);
/* New Resub Banner */
.top-notification--resub {
position: relative;
z-index: 10;
pointer-events: none;
}
.top-notification--resub > * {
pointer-events: auto;
}
.sticky-message {
padding: 5px 10px;
}
.chat-room .show-mod-icons .chat-line:not(.admin) .mod-icons {
display: block !important;
position: absolute;
top: 2px;
bottom: 1px;
padding: 4px 5px 0;
left: 0;
z-index: 99;
background-color: rgba(255,255,255,0.8);
}
.theatre .chat-room .show-mod-icons .chat-line:not(.admin) .mod-icons,
.dark .chat-room .show-mod-icons .chat-line:not(.admin) .mod-icons,
.force-dark .chat-room .show-mod-icons .chat-line:not(.admin) .mod-icons {
background-color: rgba(0,0,0,0.8);
}