1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-07-25 03:58:30 +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; 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; 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 .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--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--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--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--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__header { color: #c3c3c3 }
.ffz-dark .player-menu__section { border-bottom-color: rgba(255,255,255,0.2) } .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; 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; color: #a68ed2;
} }
.ffz-dark .button.glyph-only svg path { .ffz-dark .button.button--icon-only svg path {
fill: #a68ed2; fill: #a68ed2;
} }
.ffz-dark .button.glyph-only:hover svg path { .ffz-dark .button.button--icon-only:hover svg path {
fill: #fff; fill: #fff;
} }
@ -565,10 +573,6 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .toggle-darkmode { display: none; } .ffz-dark .toggle-darkmode { display: none; }
/* Chat Text Contrast */ /* 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-swatch:hover,
.ffz-dark .ember-chat .chat-settings .chat-colors .chat-colors-switch.selected { .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 .not_linked,
.ffz-dark #mantle_skin ul.vtabs li.selected a, .ffz-dark #mantle_skin ul.vtabs li.selected a,
.ffz-dark #mantle_skin ul.submenu li a:hover, .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 { .ffz-dark #mantle_skin ul.submenu .active a {
color: #fff !important; color: #fff !important;
} }
@ -1128,8 +1133,8 @@ body.ffz-dark:not([data-page="teams#show"]),
fill: rgba(255,255,255,0.5); fill: rgba(255,255,255,0.5);
} }
.ffz-dark .conversation-input-bar .emoticon-selector-box .emote-set { .ffz-dark .emoticon-selector-box .emote-set {
border-color: #323232; border-color: #323232 !important;
} }
.ffz-dark .conversation-input-bar .emoticon-selector-box .emoticon-grid { .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-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; border-color: #d5d5d5;
background-color: #242424; background-color: #242424;
} }
.ffz-dark .activity-react__item svg.endorse-icon #head__base { fill: #fff }
.ffz-dark .activity-create__actions { .ffz-dark .activity-create__actions {
background-color: #191919; background-color: #191919;
box-shadow: 0 -1px 0 #474747; box-shadow: 0 -1px 0 #474747;
} }
.ffz-dark .activity-create, .ffz-dark .activity-create,
.ffz-dark .activity-react__item { .ffz-dark .activity-button {
border-color: #474747; border-color: #474747;
color: #fff !important; color: #fff !important;
background-color: #191919; background-color: #191919;
} }
.ffz-dark .activity-meta:before, .ffz-dark .activity-meta-divider:before,
.ffz-dark .activity-card { .ffz-dark .activity-card {
border-color: #474747; border-color: #474747;
} }
@ -1233,11 +1236,12 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: #191919; background-color: #191919;
} }
.ffz-dark .activity-meta { .ffz-dark .activity-meta-divider {
box-shadow: inset 0 -1px 0 #474747; box-shadow: inset 0 -1px 0 #474747;
} }
.ffz-dark a.balloon__link:hover { color: #fff !important } .ffz-dark a.balloon__link:hover { color: #fff !important }
.ffz-dark .activity-meta__name { color: #ccc }
/* Search Panel */ /* Search Panel */

View file

@ -128,8 +128,8 @@ FFZ.settings_info.sub_notice_badges = {
value: false, value: false,
category: "Chat Appearance", category: "Chat Appearance",
name: "Subscriber Notice Badges", name: "Old-Style Subscriber Notice Badges",
help: "Display a subscriber badge on chat messages about new subscribers.", help: "Display a subscriber badge on old-style chat messages about new subscribers.",
on_update: function(val) { on_update: function(val) {
this.toggle_style('badges-sub-notice', ! val); this.toggle_style('badges-sub-notice', ! val);
@ -348,8 +348,16 @@ FFZ.prototype.get_line_badges = function(msg) {
globals = badgeCollection && badgeCollection.global || {}, globals = badgeCollection && badgeCollection.global || {},
channel = badgeCollection && badgeCollection.channel || {}; 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. // 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(','); var val = badge_tag.split(',');
badge_tag = {}; badge_tag = {};
for(var i=0; i < val.length; i++) { 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], var versions = channel[badge] || globals[badge],
binfo = versions && versions.versions && versions.versions[version]; 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 ) if ( hidden_badges.indexOf(badge) !== -1 )
continue; continue;
@ -456,7 +450,7 @@ FFZ.prototype.render_badges = function(badges) {
if ( badge.click_url ) if ( badge.click_url )
klass += ' 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(""); return out.join("");

View file

@ -46,12 +46,12 @@ FFZ.settings_info.fix_color = {
}, },
on_update: function(val) { 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' ) if ( ! this.has_bttv && val !== '-1' )
this._rebuild_colors(); this._rebuild_colors();
} }
}; };
FFZ.settings_info.luv_contrast = { 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'), DEBUG = localStorage.ffzDebugMode == "true" && document.body.classList.contains('ffz-dev'),
SERVER = DEBUG ? "//localhost:8000/" : "https://cdn.frankerfacez.com/", 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]", 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 + "*"), SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
@ -15,7 +19,9 @@ module.exports = FrankerFaceZ.constants = {
DEBUG: DEBUG, DEBUG: DEBUG,
SERVER: SERVER, 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 // Twitch Client ID for API Stuff
CLIENT_ID: "a3bc9znoz6vi8ozsoca0inlcr4fcvkl", 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); var game = data.stream.game || (data.stream.channel && data.stream.channel.game);
if ( game ) { if ( game ) {
t.set('content.game', game); t.set('content.game', game);
t.set('content.rollbackData.game', game);
} }
if ( data.stream.channel ) { if ( data.stream.channel ) {
@ -228,9 +227,9 @@ FFZ.prototype._modify_cindex = function(view) {
f.rebuild_race_ui(); f.rebuild_race_ui();
if ( f.settings.auto_theater ) { if ( f.settings.auto_theater ) {
var Layout = utils.ember_lookup('service:layout'); var player = f.players && f.players[id] && f.players[id].get('player');
if ( Layout ) if ( player )
Layout.set('isTheatreMode', true); player.setTheatre(true);
} }
this.$().on("click", ".ffz-creative-tag-link", function(e) { this.$().on("click", ".ffz-creative-tag-link", function(e) {
@ -296,7 +295,7 @@ FFZ.prototype._modify_cindex = function(view) {
if ( ! btn ) { if ( ! btn ) {
btn = document.createElement('span'); btn = document.createElement('span');
btn.id = 'ffz-ui-host-button'; btn.id = 'ffz-ui-host-button';
btn.className = 'button action'; btn.className = 'button button--text';
btn.addEventListener('click', this.ffzClickHost.bind(this, false)); btn.addEventListener('click', this.ffzClickHost.bind(this, false));
@ -336,7 +335,7 @@ FFZ.prototype._modify_cindex = function(view) {
if ( ! btn ) { if ( ! btn ) {
btn = document.createElement('span'); btn = document.createElement('span');
btn.id = 'ffz-ui-host-button'; btn.id = 'ffz-ui-host-button';
btn.className = 'button action'; btn.className = 'button button--text';
btn.addEventListener('click', this.ffzClickHost.bind(this, true)); 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 = { FFZ.settings_info.input_complete_name_at = {
type: "boolean", type: "boolean",
value: true, value: true,
@ -479,8 +508,11 @@ FFZ.prototype._modify_chat_input = function(component) {
ind = this.get('ffz_partial_word_start'), ind = this.get('ffz_partial_word_start'),
text = this.get('textareaValue'), text = this.get('textareaValue'),
content = ((f.settings.input_complete_name_at && item.type === 'user' && this.get('ffz_partial_word').charAt(0) === '@') ? '@' : '') + first_char = text.charAt(0),
((item.command_content && 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), item.command_content : item.content) || item.label),
trail = text.substr(ind + this.get('ffz_partial_word').length), trail = text.substr(ind + this.get('ffz_partial_word').length),
@ -664,26 +696,50 @@ FFZ.prototype._modify_chat_input = function(component) {
// Always include Users // Always include Users
var user_output = {}; var user_output = {},
alias_setting = f.settings.input_complete_aliases;
for(var i=0; i < suggestions.length; i++) { for(var i=0; i < suggestions.length; i++) {
var suggestion = suggestions[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]; var token = user_output[name];
token.whispered |= suggestion.whispered; token.whispered |= suggestion.whispered;
if ( suggestion.timestamp > token.timestamp ) if ( suggestion.timestamp > token.timestamp )
token.timestamp = suggestion.timestamp; token.timestamp = suggestion.timestamp;
} else } else {
output.push(user_output[name] = { if ( alias_setting !== 2 )
type: "user", output.push(user_output[name] = {
command_content: name, type: "user",
label: FFZ.get_capitalization(name), command_content: name,
whispered: suggestion.whispered, label: FFZ.get_capitalization(name),
timestamp: suggestion.timestamp || new Date(0), whispered: suggestion.whispered,
info: 'User' 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; return output;

View file

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

View file

@ -224,7 +224,7 @@ FFZ.prototype._modify_conversation_line = function(component) {
colored = style ? ' has-color' : ''; colored = style ? ' has-color' : '';
if ( alias ) 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 else
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>'); 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); this._hook_followers(followers);
var counted = false; var counted = false;
if ( following.get('isLoaded') || following.get('isLoading') ) { if ( following && (following.get('isLoaded') || following.get('isLoading')) ) {
refresher(following); refresher(following);
count++; count++;
counted = true; counted = true;
} }
if ( followers.get('isLoaded') || followers.get('isLoading') ) { if ( followers && (followers.get('isLoaded') || followers.get('isLoading')) ) {
refresher(followers); refresher(followers);
if ( ! counted ) if ( ! counted )
count++; count++;
@ -285,8 +285,8 @@ FFZ.prototype._modify_display_followed_item = function(component) {
return; return;
var actions = createElement('div', 'actions'), var actions = createElement('div', 'actions'),
follow = createElement('button', 'button follow'), follow = createElement('button', 'button ffz-no-bg follow'),
notif = createElement('button', 'button notifications'), notif = createElement('button', 'button ffz-no-bg notifications'),
update_follow = function() { update_follow = function() {
data = user_cache[user_id]; data = user_cache[user_id];
@ -358,7 +358,7 @@ FFZ.prototype._modify_display_followed_item = function(component) {
FFZ.prototype._hook_following = function(Following) { FFZ.prototype._hook_following = function(Following) {
var f = this; var f = this;
if ( Following.ffz_hooked ) if ( ! Following || Following.ffz_hooked )
return; return;
Following.reopen({ Following.reopen({
@ -398,7 +398,7 @@ FFZ.prototype._hook_following = function(Following) {
FFZ.prototype._hook_followers = function(Followers) { FFZ.prototype._hook_followers = function(Followers) {
var f = this; var f = this;
if ( Followers.ffz_hooked ) if ( ! Followers || Followers.ffz_hooked )
return; return;
Followers.reopen({ Followers.reopen({

View file

@ -299,7 +299,7 @@ FFZ.settings_info.chat_rows = {
on_update: function(val) { on_update: function(val) {
this.toggle_style('chat-background', !this.has_bttv && 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.", help: "Display thin lines between chat messages for further visual separation.",
on_update: function(val) { 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', !this.has_bttv && val);
this.toggle_style('chat-separator-3d', !this.has_bttv && val === 2); 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 = { FFZ.settings_info.chat_padding = {
type: "boolean", type: "boolean",
value: false, value: false,
@ -577,7 +589,7 @@ FFZ.prototype.setup_line = function() {
// Chat Enhancements // Chat Enhancements
document.body.classList.toggle('ffz-alias-italics', this.settings.alias_italics); 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-padding', !this.has_bttv && this.settings.chat_padding);
this.toggle_style('chat-background', !this.has_bttv && this.settings.chat_rows); 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 ( btn === false ) {
if ( deleted ) 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 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 ) } 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 { else {
if ( typeof btn === "string" ) { if ( typeof btn === "string" ) {
cmd = btn.replace(/{user}/g, user).replace(/ *<LINE> */, "\n"); 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 { } else {
cmd = "/timeout " + user + " " + btn; cmd = "/timeout " + user + " " + btn;
tip = "Timeout User (" + utils.duration_string(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'))), is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode'))),
system_msg = this.get('systemMsg'),
output = ''; 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 // Moderator Actions
output += this.buildModIconsHTML(); 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]) || '', style = colors && 'color:' + (is_dark ? colors[1] : colors[0]) || '',
colored = style ? ' has-color' + (is_replay ? ' replay-color' : '') : ''; 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 ) if ( alias )
output += '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias); output += '" title="' + utils.quote_san(name) + '">' + utils.sanitize(alias);
else else
output += '">' + utils.sanitize(name); output += '">' + utils.sanitize(name);
@ -787,10 +809,10 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
to_colored = to_style ? ' has-color' : ''; 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><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 ) 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 else
output += '">' + utils.sanitize(to_name); output += '">' + utils.sanitize(to_name);
} }
@ -833,10 +855,12 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
var el = this.get('element'), var el = this.get('element'),
output = this.buildSenderHTML(); output = this.buildSenderHTML();
if ( this.get('msgObject.deleted') ) // If this is a whisper, or if we should render the message body, render it.
output += this.buildDeletedMessageHTML() if ( this.get('shouldRenderMessageBody') !== false )
else if ( this.get('msgObject.deleted') )
output += this.buildMessageHTML(); output += this.buildDeletedMessageHTML()
else
output += this.buildMessageHTML();
el.innerHTML = output; el.innerHTML = output;
}, },
@ -932,10 +956,18 @@ FFZ.prototype._modify_chat_subline = function(component) {
return; return;
else if ( e.target.classList.contains('from') ) { 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", { this.sendAction("showModOverlay", {
left: n.offset().left, left: bounds.left,
top: n.offset().top + n.height(), right: right,
top: bounds.top + bounds.height,
real_top: bounds.top,
sender: from sender: from
}); });
@ -995,7 +1027,7 @@ FFZ.prototype._modify_vod_line = function(component) {
return '<span class="mod-icons float-left">' + return '<span class="mod-icons float-left">' +
(this.get('msgObject.deleted') ? (this.get('msgObject.deleted') ?
'<em class="mod-icon float-left unban"></em>' : '<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() { buildDeletedMesageHTML: function() {

View file

@ -1,6 +1,7 @@
var FFZ = window.FrankerFaceZ, var FFZ = window.FrankerFaceZ,
utils = require("../utils"), utils = require("../utils"),
constants = require("../constants"), constants = require("../constants"),
styles = require("../compiled_styles"),
helpers, helpers,
TO_REG = /^\/t(?:imeout)? +([^ ]+)(?: +(\d+)(?: +(.+))?)?$/, 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", type: "boolean",
value: false, 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, no_bttv: true,
category: "Chat Moderation", category: "Chat Moderation",
name: "Pause Chat Scrolling on Mouse Hover", name: "Display In-Line Mod Icons",
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link misclicks.", 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) { on_update: function(val) {
if ( ! this._roomv ) if ( ! this._roomv )
return; 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 ) if ( val )
this._roomv.ffzEnableFreeze(); this._roomv.ffzEnableFreeze();
else
this._roomv.ffzDisableFreeze();
} }
}; };
@ -495,6 +584,17 @@ FFZ.prototype.setup_mod_card = function() {
} catch(err) { } } 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."); this.log("Modifying Mousetrap stopCallback so we can catch ESC.");
var orig_stop = Mousetrap.stopCallback; var orig_stop = Mousetrap.stopCallback;
Mousetrap.stopCallback = function(e, element, combo) { Mousetrap.stopCallback = function(e, element, combo) {
@ -511,8 +611,7 @@ FFZ.prototype.setup_mod_card = function() {
this.log("Hooking the Ember Moderation Card view."); this.log("Hooking the Ember Moderation Card view.");
var Card = utils.ember_resolve('component:chat/moderation-card'), var Card = utils.ember_resolve('component:chat/moderation-card');
f = this;
Card.reopen({ Card.reopen({
ffzForceRedraw: function() { ffzForceRedraw: function() {
@ -520,6 +619,10 @@ FFZ.prototype.setup_mod_card = function() {
if ( f.settings.mod_card_history ) if ( f.settings.mod_card_history )
this.ffzRenderHistory(); 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"), }.observes("cardInfo.isModeratorOrHigher", "cardInfo.user.id"),
ffzRebuildInfo: function() { ffzRebuildInfo: function() {
@ -528,12 +631,12 @@ FFZ.prototype.setup_mod_card = function() {
if ( ! info ) if ( ! info )
return; 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') || ''), since = utils.parse_date(this.get('cardInfo.user.created_at') || ''),
followers = this.get('cardInfo.user.ffz_followers'); followers = this.get('cardInfo.user.ffz_followers');
if ( typeof followers === "number" ) { 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 ) { } else if ( followers === undefined ) {
var t = this; var t = this;
@ -550,7 +653,7 @@ FFZ.prototype.setup_mod_card = function() {
var now = Date.now() - (f._ws_server_offset || 0), var now = Date.now() - (f._ws_server_offset || 0),
age = Math.floor((now - since.getTime()) / 1000); age = Math.floor((now - since.getTime()) / 1000);
if ( age > 0 ) { 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() { willDestroy: function() {
if ( f._mod_card === this ) if ( f._mod_card === this )
f._mod_card = undefined; f._mod_card = undefined;
utils.update_css(f._chat_style, 'mod-card-highlight');
this._super(); this._super();
}, },
@ -594,6 +700,8 @@ FFZ.prototype.setup_mod_card = function() {
user_id = controller.get('cardInfo.user.id'), user_id = controller.get('cardInfo.user.id'),
alias = f.aliases[user_id], alias = f.aliases[user_id],
handle_key,
ban_reason = function() { ban_reason = function() {
return ban_reasons && ban_reasons.value ? ' ' + ban_reasons.value : ""; 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; 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 // Action Override
this.set('banAction', function(e) { this.set('banAction', function(e) {
@ -677,7 +788,7 @@ FFZ.prototype.setup_mod_card = function() {
}, },
add_btn_make = function(cmd) { add_btn_make = function(cmd) {
var btn = document.createElement('button'), var btn = utils.createElement('button', 'button ffz-no-bg'),
segment = cmd.split(' ', 1)[0], segment = cmd.split(' ', 1)[0],
title = cmds[segment] > 1 ? cmd.split(' ', cmds[segment]) : [segment]; 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(' '); title = _.map(title, function(s){ return s.capitalize() }).join(' ');
btn.className = 'button';
btn.innerHTML = utils.sanitize(title); btn.innerHTML = utils.sanitize(title);
btn.title = utils.sanitize(cmd.replace(/{user}/g, controller.get('cardInfo.user.id') || '{user}')); 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 ) { if ( f.settings.mod_card_hotkeys ) {
el.classList.add('no-mousetrap'); el.classList.add('no-mousetrap');
el.addEventListener('keyup', function(e) { handle_key = function(e) {
var key = e.keyCode || e.which, var key = e.keyCode || e.which,
user_id = controller.get('cardInfo.user.id'), user_id = controller.get('cardInfo.user.id'),
is_mod = controller.get('cardInfo.isModeratorOrHigher'), is_mod = controller.get('cardInfo.isModeratorOrHigher'),
@ -753,7 +863,9 @@ FFZ.prototype.setup_mod_card = function() {
return; return;
t.get('closeAction')(); t.get('closeAction')();
}); };
el.addEventListener('keyup', handle_key);
} }
@ -773,13 +885,13 @@ FFZ.prototype.setup_mod_card = function() {
btn_make = function(timeout) { btn_make = function(timeout) {
var btn = document.createElement('button') var btn = document.createElement('button')
btn.className = 'button'; btn.className = 'button ffz-no-bg';
btn.innerHTML = utils.duration_string(timeout); btn.innerHTML = utils.duration_string(timeout);
btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : ""); btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : "");
if ( f.settings.mod_card_hotkeys && timeout === 600 ) if ( f.settings.mod_card_hotkeys && timeout === 600 )
btn.title = "(T)" + btn.title.substr(1); 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; btn.title = "(P)urge - " + btn.title;
jQuery(btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')}); 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 ) { if ( f.settings.mod_card_reasons && f.settings.mod_card_reasons.length ) {
// Moderation Reasons // Moderation Reasons
line = utils.createElement('div', 'extra-interface interface clearfix'); 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); line.appendChild(ban_reasons);
for(var i=0; i < f.settings.mod_card_reasons.length; i++) { for(var i=0; i < f.settings.mod_card_reasons.length; i++) {
@ -832,8 +944,8 @@ FFZ.prototype.setup_mod_card = function() {
// Unban Button // Unban Button
var unban_btn = document.createElement('button'); var unban_btn = document.createElement('button');
unban_btn.className = 'unban button glyph-only light'; unban_btn.className = 'unban button button--icon-only light';
unban_btn.innerHTML = CHECK; unban_btn.innerHTML = '<figure class="icon">' + CHECK + '</figure>';
unban_btn.title = (f.settings.mod_card_hotkeys ? "(U)" : "U") + "nban User"; unban_btn.title = (f.settings.mod_card_hotkeys ? "(U)" : "U") + "nban User";
jQuery(unban_btn).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')}); 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 // More Fixing Other Buttons
var op_btn = el.querySelector('button.mod'); var op_btn = el.querySelector('button.mod');
if ( op_btn ) { if ( op_btn ) {
@ -863,7 +979,7 @@ FFZ.prototype.setup_mod_card = function() {
var msg_btn = el.querySelector(".interface > button.message-button"); var msg_btn = el.querySelector(".interface > button.message-button");
if ( msg_btn ) { if ( msg_btn ) {
msg_btn.innerHTML = 'W'; msg_btn.innerHTML = 'W';
msg_btn.classList.add('glyph-only'); msg_btn.classList.add('button--icon-only');
msg_btn.classList.add('message'); msg_btn.classList.add('message');
msg_btn.title = "Whisper User"; msg_btn.title = "Whisper User";
@ -871,8 +987,8 @@ FFZ.prototype.setup_mod_card = function() {
var real_msg = document.createElement('button'); var real_msg = document.createElement('button');
real_msg.className = 'message-button button glyph-only message tooltip'; real_msg.className = 'message-button button button--icon-only message html-tooltip';
real_msg.innerHTML = MESSAGE; real_msg.innerHTML = '<figure class="icon">' + MESSAGE + '</figure>';
real_msg.title = "Message User"; real_msg.title = "Message User";
real_msg.addEventListener('click', function() { real_msg.addEventListener('click', function() {
@ -885,8 +1001,8 @@ FFZ.prototype.setup_mod_card = function() {
// Alias Button // Alias Button
var alias_btn = document.createElement('button'); var alias_btn = document.createElement('button');
alias_btn.className = 'alias button glyph-only tooltip'; alias_btn.className = 'alias button button--icon-only html-tooltip';
alias_btn.innerHTML = constants.EDIT; alias_btn.innerHTML = '<figure class="icon">' + constants.EDIT + '</figure>';
alias_btn.title = "Set Alias"; alias_btn.title = "Set Alias";
alias_btn.addEventListener('click', function() { alias_btn.addEventListener('click', function() {
@ -932,26 +1048,7 @@ FFZ.prototype.setup_mod_card = function() {
this.ffzRenderHistory(); this.ffzRenderHistory();
// Reposition the menu if it's off-screen. // Reposition the menu if it's off-screen.
var el_bound = el.getBoundingClientRect(), this.ffzReposition();
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";
}
// Focus the Element // Focus the Element
this.$().draggable({ 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() { ffzRenderHistory: function() {
var t = this, var t = this,
Chat = utils.ember_lookup('controller:chat'), Chat = utils.ember_lookup('controller:chat'),
@ -1093,7 +1214,7 @@ FFZ.prototype.setup_mod_card = function() {
logs = document.createElement('ul'); logs = document.createElement('ul');
back = document.createElement('button'); back = document.createElement('button');
back.className = 'button back-button'; back.className = 'button ffz-no-bg back-button';
back.innerHTML = '&laquo; Back'; back.innerHTML = '&laquo; Back';
back.addEventListener('click', function() { back.addEventListener('click', function() {
@ -1184,7 +1305,7 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
if ( alias ) 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 else
out.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name ) + '</span>'); 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 : ''; el_from.title = alias ? cap_name : '';
} }
// Update tab completion.
if ( this._inputv )
Ember.propertyDidChange(this._inputv, 'ffz_name_suggestions');
// TODO: Update conversations~ // TODO: Update conversations~
} }

View file

@ -77,10 +77,10 @@ FFZ.prototype.setup_room = function() {
this.set("showModerationCard", true); this.set("showModerationCard", true);
// We pass in renderBottom and renderRight, which we use to reposition the window // 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", { this.set("moderationCardInfo", {
user: chan, user: chan,
renderTop: e.top, renderTop: e.real_top || e.top,
renderLeft: e.left, renderLeft: e.left,
renderBottom: e.bottom, renderBottom: e.bottom,
renderRight: e.right, renderRight: e.right,
@ -171,6 +171,12 @@ FFZ.prototype._modify_rview = function(view) {
f._roomv = this; f._roomv = this;
this.ffz_frozen = false; 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. // Fix scrolling.
this._ffz_mouse_down = this.ffzMouseDown.bind(this); this._ffz_mouse_down = this.ffzMouseDown.bind(this);
@ -215,9 +221,43 @@ FFZ.prototype._modify_rview = function(view) {
if ( this._ffz_chat_display ) if ( this._ffz_chat_display )
this._ffz_chat_display = undefined; 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(); 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() { ffzUpdateStatus: function() {
var room = this.get('controller.model'), var room = this.get('controller.model'),
el = this.get('element'), el = this.get('element'),
@ -273,8 +313,8 @@ FFZ.prototype._modify_rview = function(view) {
if ( ! messages ) if ( ! messages )
return; return;
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
this._ffz_messages = messages; this._ffz_messages = messages;
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
this._ffz_mouse_move = this.ffzMouseMove.bind(this); this._ffz_mouse_move = this.ffzMouseMove.bind(this);
this._ffz_mouse_out = this.ffzMouseOut.bind(this); this._ffz_mouse_out = this.ffzMouseOut.bind(this);
@ -311,11 +351,8 @@ FFZ.prototype._modify_rview = function(view) {
}, },
ffzPulse: function() { ffzPulse: function() {
if ( this.ffz_frozen ) { if ( this.ffz_frozen && ! this.ffzShouldBeFrozen() )
var elapsed = Date.now() - this._ffz_last_move; this.ffzUnfreeze();
if ( elapsed > 750 )
this.ffzUnfreeze();
}
}, },
ffzUnfreeze: function(from_stuck) { ffzUnfreeze: function(from_stuck) {
@ -327,11 +364,17 @@ FFZ.prototype._modify_rview = function(view) {
this._scrollToBottom(); 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) { ffzMouseDown: function(event) {
var t = this._$chatMessagesScroller; var t = this._$chatMessagesScroller;
if ( t && t[0] && ((!this.ffz_frozen && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) { 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; var r = t[0].scrollHeight - t[0].scrollTop - t[0].offsetHeight;
this._setStuckToBottom(10 >= r); this._setStuckToBottom(10 >= r);
} }
@ -341,29 +384,57 @@ FFZ.prototype._modify_rview = function(view) {
this._ffz_outside = true; this._ffz_outside = true;
var e = this; var e = this;
setTimeout(function() { 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(); e.ffzUnfreeze();
}
}, 25); }, 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) { ffzMouseMove: function(event) {
// Store the last move time.
this._ffz_last_move = Date.now(); this._ffz_last_move = Date.now();
this._ffz_outside = false; 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; 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_screenx = event.screenX;
this._ffz_last_screeny = event.screenY; this._ffz_last_screeny = event.screenY;
if ( this.ffz_frozen ) var cmi = f.settings.chat_mod_icon_visibility;
return; 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; // Should the state have changed?
if ( this.get('stuckToBottom') ) { var should_freeze = this.ffzShouldBeFrozen(),
this.set('controller.model.messageBufferSize', f.settings.scrollback_length + 150); freeze_change = this.ffz_frozen !== should_freeze;
this.ffzWarnPaused();
} if ( freeze_change )
if ( should_freeze )
this.ffzFreeze();
else
this.ffzUnfreeze();
}, },
_scrollToBottom: _.throttle(function() { _scrollToBottom: _.throttle(function() {
@ -400,7 +471,19 @@ FFZ.prototype._modify_rview = function(view) {
if ( ! warning ) { if ( ! warning ) {
warning = document.createElement('div'); warning = document.createElement('div');
warning.className = 'more-messages-indicator ffz-freeze-indicator'; 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'); var cont = el.querySelector('.chat-interface');
if ( ! cont ) if ( ! cont )
@ -923,8 +1006,10 @@ FFZ.prototype._modify_room = function(room) {
user = f.get_user(), user = f.get_user(),
room_id = this.get('id'); 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(); this.destroy();
}, },
@ -1284,6 +1369,31 @@ FFZ.prototype._modify_room = function(room) {
addMessage: function(msg) { addMessage: function(msg) {
if ( 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'; var is_whisper = msg.style === 'whisper';
// Ignore whispers if conversations are enabled. // Ignore whispers if conversations are enabled.
@ -1291,7 +1401,7 @@ FFZ.prototype._modify_room = function(room) {
return; return;
if ( ! is_whisper ) if ( ! is_whisper )
msg.room = this.get('id'); msg.room = room_id;
// Look up color and labels. // Look up color and labels.
if ( this.tmiRoom && msg.from ) { if ( this.tmiRoom && msg.from ) {
@ -1434,6 +1544,13 @@ FFZ.prototype._modify_room = function(room) {
try { try {
this.ffz_last_input = Date.now(); 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 ) { if ( text && ! ignore_history ) {
// Command History // Command History
var mru = this.get('mru_list'), var mru = this.get('mru_list'),

View file

@ -37,7 +37,7 @@ FFZ.msg_commands = {};
// Version // Version
var VER = FFZ.version_info = { var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 203, major: 3, minor: 5, revision: 216,
toString: function() { toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || ""); 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) ) if ( location.hostname === 'passport.twitch.tv' || /^\/user\/two_factor/.test(location.pathname) )
return this.log("Found authentication sub-page. Not initializing."); 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."); return this.log("Found banned sub-domain. Not initializing.");
// Check for the player // 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 */ /* Remove Extra Conversation Padding */
.conversation-window .conversation-chat-lines { .conversation-window .conversation-chat-lines {
padding-top: 0; padding-top: 0;

View file

@ -15,4 +15,12 @@
.chat-history .chat-line:before { .chat-history .chat-line:before {
top: 0; bottom: 0; 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"), constants = require("./constants"),
helpers, helpers,
conv_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.', 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); 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."); this.log("Hooking Ember chat line helpers.");
var f = this; var f = this;
@ -395,6 +402,9 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
if ( conv_helpers && conv_helpers.checkActionMessage ) if ( conv_helpers && conv_helpers.checkActionMessage )
tokens = conv_helpers.checkActionMessage(tokens); tokens = conv_helpers.checkActionMessage(tokens);
if ( emote_helpers )
emotes = emote_helpers(emotes);
// Standard Tokenization // Standard Tokenization
if ( helpers && helpers.linkifyMessage ) if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens); tokens = helpers.linkifyMessage(tokens);
@ -775,8 +785,8 @@ FFZ.prototype.render_token = function(render_links, warn_links, token) {
} }
else if ( token.type === "deleted" ) 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 html-tooltip" title="' + utils.quote_san(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_attr(token.text)}" data-text="${utils.sanitize(token.text)}">&times;&times;&times;</span>`;
else if ( token.type === "mention" ) else if ( token.type === "mention" )
return '<span class="' + (token.isOwnMessage ? 'mentioning' : 'mentioned') + '">' + utils.sanitize(token.user) + '</span>'; 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); setTimeout(this._install_following_tooltips.bind(this), 2000);
// If we don't have Ember, no point in trying this stuff. // 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(); return this._following_get_me();
this.log("Connecting to Live Streams model."); this.log("Connecting to Live Streams model.");
@ -163,10 +163,10 @@ FFZ.prototype._update_following_count = function() {
f = this; 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(); HostLive.load();
if ( Live ) if ( ! this.is_dashboard && Live )
Live.load(); Live.load();
else { else {
var u = this.get_user(); var u = this.get_user();

View file

@ -612,9 +612,9 @@ FFZ.menu_pages.channel = {
unlock_text.innerHTML = "Subscribe to unlock Emoticons"; unlock_text.innerHTML = "Subscribe to unlock Emoticons";
nonsub_message.appendChild(unlock_text); 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.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); nonsub_message.appendChild(sub_link);
inner.appendChild(sub_message); inner.appendChild(sub_message);

View file

@ -116,7 +116,7 @@ FFZ.prototype.rebuild_race_ui = function() {
race_container.setAttribute('data-channel', channel_id); race_container.setAttribute('data-channel', channel_id);
var btn = document.createElement('span'); var btn = document.createElement('span');
btn.className = 'button drop action'; btn.className = 'button button--text button--dropmenu';
btn.title = "SpeedRunsLive Race"; btn.title = "SpeedRunsLive Race";
btn.innerHTML = '<span class="logo"></span>'; btn.innerHTML = '<span class="logo"></span>';
@ -148,7 +148,7 @@ FFZ.prototype.rebuild_race_ui = function() {
race_container.setAttribute('data-channel', hosted_id); race_container.setAttribute('data-channel', hosted_id);
var btn = document.createElement('span'); var btn = document.createElement('span');
btn.className = 'button drop action'; btn.className = 'button button--text button--dropmenu';
btn.title = "SpeedRunsLive Race"; btn.title = "SpeedRunsLive Race";
btn.innerHTML = '<span class="logo"></span>'; 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. // Make sure we don't leave any tooltips lying around when we update.
// Of course, we should just rewrite logic to not constantly mutilate // Of course, we should just rewrite logic to not constantly mutilate
// rows. // rows.
jQuery('.tooltip', tbody).trigger('mouseout'); jQuery('.html-tooltip', tbody).trigger('mouseout');
tbody.innerHTML = ''; tbody.innerHTML = '';
var entrants = [], done = true; 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>' : '', 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) : "", time = elapsed ? utils.time_to_string(ent.time||elapsed) : "",
place = utils.place_string(ent.place), 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 ) { if ( this._race_game != race.game || this._race_goal != race.goal ) {
this._race_game = race.game; this._race_game = race.game;
this._race_goal = race.goal; this._race_goal = race.goal;
var game = utils.sanitize(race.game), var game = utils.quote_san(race.game),
goal = utils.unquote_attr(race.goal), goal = utils.unquote_attr(race.goal),
old_goal = popup.getAttribute('data-old-goal'); old_goal = popup.getAttribute('data-old-goal');
if ( goal !== old_goal ) { if ( goal !== old_goal ) {
popup.setAttribute('data-old-goal', goal); popup.setAttribute('data-old-goal', goal);
goal = goal ? this.render_tokens(this.tokenize_line("jtv", null, goal, true)) : ''; 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;"); 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 = [ HUMAN_NUMBERS = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" "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); closer = show_modal(contents, cb, width);
try {
input.focus();
} catch(err) { }
form.addEventListener('submit', function(e) { e.preventDefault(); cb(true); return false }); form.addEventListener('submit', function(e) { e.preventDefault(); cb(true); return false });
okay_btn.addEventListener('click', 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 }); close_btn.addEventListener('click', function(e) { e.preventDefault(); cb(false); return false });
@ -423,6 +431,7 @@ module.exports = FFZ.utils = {
sanitize: sanitize, sanitize: sanitize,
unquote_attr: unquote_attr, unquote_attr: unquote_attr,
quote_attr: quote_attr, quote_attr: quote_attr,
quote_san: quote_san,
date_string: function(date) { date_string: function(date) {
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate(); 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; 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; fill: #a68ed2;
} }
@ -336,6 +336,8 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
/* SRL Race Support */ /* SRL Race Support */
#ffz-ui-host-button { vertical-align: middle }
#ffz-following-popup.right { #ffz-following-popup.right {
right: 0; right: 0;
left: auto; 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 { #ffz-ui-race .button span.logo {
padding-left: 44px; padding-left: 44px;
margin-bottom: -10px;
background-image: url("//cdn.frankerfacez.com/script/srl_button.png"); 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 { .ffz-moderation-card button {
margin: 0; margin: 0;
padding: 0 5px; padding: 0 5px;
color: #6441a4;
} }
.ffz-moderation-card .mod-controls button figure { padding: 0 }
.ember-chat .ffz-moderation-card .mod-controls button { .ember-chat .ffz-moderation-card .mod-controls button {
width: auto; width: auto;
margin-right: 10px; margin-right: 10px;
@ -1306,10 +1312,13 @@ img.channel_background[src="null"] { display: none; }
padding-right: 0 !important; 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(.button--icon-only):hover,
.ffz-moderation-card button:not(.glyph-only):focus { .ffz-moderation-card button:not(.button--icon-only):focus {
color: #fff; color: #fff;
background-color: rgba(117,80,186, 1); background-color: rgba(117,80,186, 1);
} }
@ -1404,6 +1413,13 @@ img.channel_background[src="null"] { display: none; }
/* Chat Rows */ /* 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; } .ffz-alias-italics .ffz-alias { font-style: italic; }
.ember-chat .chat-messages .chat-line.ffz-has-deleted { .ember-chat .chat-messages .chat-line.ffz-has-deleted {
@ -1720,7 +1736,7 @@ th.ffz-row-switch {
margin-right: 4px; margin-right: 4px;
} }
#ffz-group-tabs .button.glyph-only svg { #ffz-group-tabs .button.button--icon-only svg {
margin: 6px 0; margin: 6px 0;
} }
@ -2216,6 +2232,14 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
background-color: #191919; 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__anchor,
.ffz-no-blue .warp__item--anchor, .ffz-no-blue .warp__item--anchor,
.ffz-no-blue .warp__drawer, .ffz-no-blue .warp__drawer,
@ -2966,6 +2990,14 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
background-size: 100% background-size: 100%
}*/ }*/
/* Button Fix */
.ffz-no-bg {
background: transparent;
}
/* Odd Badges */ /* Odd Badges */
.badge.click_url { cursor: pointer } .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); 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; /* New Resub Banner */
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); .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);
} }