1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00

3.5.271 to 3.5.284. As usual, I only remember to commit when someone mentions it.

This commit is contained in:
SirStendec 2016-09-09 17:34:20 -04:00
parent 9592dc1c2c
commit 8db999a8a8
29 changed files with 1388 additions and 317 deletions

View file

@ -1,3 +1,86 @@
<div class="list-header">3.5.284</div>
<ul class="chat-menu-content menu-side-padding">
<li>Changed: Inject FFZ emotes into the BetterTTV tab completion list.</li>
</ul>
<div class="list-header">3.5.283</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Support for moderation logging. When possible, messages are combined to avoid filling chat with nonsense.</li>
</ul>
<div class="list-header">3.5.282</div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Height of classic player theme controls when in theater mode.</li>
</ul>
<div class="list-header">3.5.281</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Option to hide whispers on embedded chat.</li>
</ul>
<div class="list-header">3.5.280</div>
<ul class="chat-menu-content menu-side-padding">
<li>Changed: Use new display name formatting logic in more places.</li>
<li>Fixed: Aliases now appear for moderation cards again.</li>
</ul>
<div class="list-header">3.5.279</div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Dark CSS tweaks for non-Ember pages.</li>
</ul>
<div class="list-header">3.5.278</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Darken Clips.</li>
<li>Fixed: Classic Player's CSS was broken because of a malformed comment.</li>
</ul>
<div class="list-header">3.5.277</div>
<ul class="chat-menu-content menu-side-padding">
<li>Changed: More tab completion tweaks for users with display names that don't match their username.</li>
</ul>
<div class="list-header">3.5.276</div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Highlight a user's display name if it's different than their username. (Well, more of an addition with how it works...)</li>
<li>Changed: Make FFZ's tab completion work nicer for users with non-matching usernames and display names.</li>
<li>Changed: CSS tweaks for mod cards when user information is displayed alongside a user's username.</li>
</ul>
<div class="list-header">3.5.275</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Option to control the appearance of usernames in chat when the user's name and display name do not match.</li>
<li>Changed: Darken the Closed Captioning settings dialog.</li>
<li>Fixed: CSS tweaks for mod cards.</li>
<li>Fixed: User information was not being added to mod cards properly.</li>
</ul>
<div class="list-header">3.5.274</div>
<ul class="chat-menu-content menu-side-padding">
<li>Changed: Use the new badges code for conversation window header badges.</li>
<li>Fixed: Dark CSS tweaks for the modified conversation windows.</li>
</ul>
<div class="list-header">3.5.273</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Support the Discover directory with stream uptime and channel logos.</li>
<li>Changed: Dark theme CSS tweaks for the Discover directory.</li>
<li>Fixed: Chat status labels were breaking stuff on navigation thanks to an undefined variable.</li>
</ul>
<div class="list-header">3.5.272</div>
<ul class="chat-menu-content menu-side-padding">
<li>Added: Ember error handlers to hopefully catch more useful logs in the future when there are issues.</li>
<li>Fixed: Bug in an API wrapper that would cause the Ember app to fail to navigate in certain conditions.</li>
<li>Fixed: CSS tweaks.</li>
</ul>
<div class="list-header">3.5.271</div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: FFZ menu icon would appear at an incorrect position with the Bits dialog open.</li>
<li>Changed: Properly darken the bits dialog and adjust its colors when no-blue is enabled.</li>
</ul>
<div class="list-header">3.5.270</div>
<ul class="chat-menu-content menu-side-padding">
<li>Changed: Remove client-side permissions check from <code>/ffz following</code>.</li>

View file

@ -197,7 +197,7 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: rgb(16,16,16);
color: rgb(195,195,195); /*#acacbf;*/
border-color: #32323e;
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
}
.ffz-dark #flyout .content,
@ -315,6 +315,7 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .st-autocomplete-small .list ul .result:not(.active) p,
.ffz-dark .manager .videos-grid .video:hover .meta .actions li a,
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
.ffz-dark .dropmenu_action:not(:hover),
.ffz-dark .dropmenu_action:not(:hover) span,
.ffz-dark a {
/*.ffz-dark a:not(.profile-card__content):not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) {*/
@ -522,6 +523,7 @@ body.ffz-dark:not([data-page="teams#show"]),
color: #999;
}
.ffz-dark .card__boxpin,
.ffz-dark .streams .stream .content .thumb .boxart,
.ffz-dark .videos .video .content .thumb .boxart {
border-color: rgb(16,16,16);
@ -532,6 +534,8 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: rgb(16,16,16);
}
.ffz-dark .card .card__title a,
.ffz-dark .card .card__info a,
.ffz-dark .items-grid .meta .title,
.ffz-dark .items-grid .meta p a {
color: #9c9c9c !important;
@ -541,18 +545,21 @@ body.ffz-dark:not([data-page="teams#show"]),
color: #6c6c6c;
}
.ffz-dark .mininav li > a,
.ffz-dark ul.tabs li > a,
.ffz-dark .directory_header .nav li > a,
.ffz-dark ul.tabs_fake li > a {
color: #a68ed2;
}
.ffz-dark .mininav,
.ffz-dark ul.tabs:before,
.ffz-dark .direcotry_header .nav:before,
.ffz-dark ul.tabs_fake:before {
border-color: #32323e;
}
.ffz-dark .mininav li > a:hover,
.ffz-dark ul.mininav li.active,
.ffz-dark ul.tabs li.selected a,
.ffz-dark .directory_header .nav li.selected a,
@ -793,13 +800,34 @@ body.ffz-dark:not([data-page="teams#show"]),
/* Dashboard */
.ffz-dark .carousel__button {
background-color: #101010;
}
.ffz-dark .carousel__arrow:before {
border-color: #a68ed2;
}
.ffz-dark .carousel__button:hover .carousel__arrow:before {
border-color: #fff;
}
.ffz-dark .carousel__button:hover {
background-color: #191919;
}
.ffz-dark .brick {
background-color: #121212;
background-color: #121212;
}
.ffz-dark .brick--marked.brick--theme-white,
.ffz-dark .brick--marked.brick--theme-grey {
box-shadow: 3px 0 0 #9a7fcc inset,0 0 0 1px rgba(255,255,255,0.2) inset;
}
.ffz-dark .brick--faint,
.ffz-dark .brick--block {
background-color: rgb(25,25,25);
background-color: rgb(25,25,25);
}
.ffz-dark .brick,
@ -807,7 +835,7 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .brick--block,
.ffz-dark #action_feed .action,
.ffz-dark .revHeader__item {
border-color: rgba(255,255,255,0.2);
border-color: rgba(255,255,255,0.2);
}
.ffz-dark #mantle_skin .what { background-color: #6441a5; }
@ -955,7 +983,23 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: transparent;
}
.ffz-dark #mantle_skin div[style^=" display:background:#ffffff"],
.ffz-dark #mantle_skin .social_links img {
background-color: #fff;
}
.ffz-dark #mantle_skin div[style^=" display:background:#ffffff"] .extraspace { color: #333 }
.ffz-dark #mantle_skin ul.vtabs li .not_linked,
.ffz-dark #mantle_skin ul.vtabs li a {
color: #acacbf;
}
.ffz-dark #partner_application .partner_reqs {
background-color: #191919;
border-color: #404040;
}
.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,
@ -998,6 +1042,15 @@ body.ffz-dark:not([data-page="teams#show"]),
-webkit-filter: invert(100%);
}
.ffz-dark .new-header-search input {
color: #fff;
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
}
.ffz-dark .new-header-search input:focus {
box-shadow: rgba(255,255,255,0.4) 0 0 0 1px inset;
}
/* Playlist */
.ffz-dark .ember-chat .chat-header {
@ -1088,7 +1141,7 @@ body.ffz-dark:not([data-page="teams#show"]),
}
.ffz-dark .conversations-list .search-divider,
.ffz-dark .conversation-header {
.ffz-dark .convoHeader {
background-color: #121217;
box-shadow: none;
}
@ -1099,11 +1152,11 @@ body.ffz-dark:not([data-page="teams#show"]),
background-color: #444;
}
.ffz-dark .conversation-window.has-focus .conversation-header {
.ffz-dark .conversation-window.has-focus .convoHeader {
background-color: #121217;
}
.ffz-dark .conversation-window.has-focus .conversation-header-name {
.ffz-dark .conversation-window.has-focus .convoHeader .username {
color: #fff;
}
@ -1169,7 +1222,7 @@ body.ffz-dark:not([data-page="teams#show"]),
border-top: none;
}
.ffz-dark .conversation-window:not(.collapsed) .conversation-header {
.ffz-dark .conversation-window:not(.collapsed) .convoHeader {
border-bottom: 1px solid rgba(255,255,255,0.2);
}
@ -1329,6 +1382,11 @@ body.ffz-dark:not([data-page="teams#show"]),
color: #C3C3C3;
}
.ffz-dark .searchPanel--fly:before,
.ffz-dark .searchPanel--fly:after {
border-bottom-color: #404040;
}
.ffz-dark .titleBar {
background-color: #090909;
}
@ -1344,6 +1402,15 @@ body.ffz-dark:not([data-page="teams#show"]),
.ffz-dark .titleBar__back:hover,
.ffz-dark .resultView__titlesep.isActive { background-color: #222 }
.ffz-dark .searchPanel .card__body--overlay .card__title {
text-shadow: 1px 1px #000, -1px 1px #000, -1px -1px #000, 1px -1px #000;
}
.ffz-dark .card__title a:hover,
.ffz-dark .card__info a:hover {
color: #ccd;
}
.ffz-dark .resultView__item .card__info span,
.ffz-dark .resultView__item .card__title,
.ffz-dark .resultView__titlesep.isActive .resultView__titleMore { color: #a68ed2 }

View file

@ -14,7 +14,7 @@ var fs = require('fs'),
var jsEscape = require('gulp-js-escape'),
wrap = require('gulp-wrap'),
declare = require('gulp-declare'),
minifyCss = require('gulp-minify-css');
cleanCSS = require('gulp-clean-css');
// LESS
@ -30,7 +30,7 @@ var ftp = require('vinyl-ftp'),
// Server Dependencies
var http = require("http"),
https = require("https"),
net = require('net'),
net = require('net'),
path = require("path"),
request = require("request"),
url = require("url");
@ -69,8 +69,8 @@ gulp.task('prepare', ['clean'], function() {
//});
gulp.task('styles', ['prepare'], function() {
return;
return gulp.src(['build/less/*.less'])
//return;
return gulp.src(['build/less/*.less', '!build/less/style.less'])
.pipe(sourcemaps.init())
.pipe(less())
.pipe(sourcemaps.write())
@ -81,7 +81,7 @@ gulp.task('styles', ['prepare'], function() {
gulp.task('embedded_styles', ['prepare'], function() {
return gulp.src(['build/styles/**/*.css'])
.pipe(minifyCss())
.pipe(cleanCSS())
.pipe(jsEscape())
.pipe(declare({
root: 'exports',
@ -126,8 +126,8 @@ gulp.task('minify_script', ['scripts'], function() {
});
gulp.task('minify_style', function() {
return gulp.src(['style.css', 'dark.css'])
.pipe(minifyCss())
return gulp.src(['style.css', 'style-clips.css', 'dark.css', 'dark-clips.css'])
.pipe(cleanCSS())
.pipe(rename(function(path) {
path.basename += '.min';
}))
@ -258,33 +258,33 @@ gulp.task('server', function() {
};
if ( fs.existsSync("dev_key.pem") ) {
var https_options = {
key: fs.readFileSync("dev_key.pem"),
cert: fs.readFileSync("dev_cert.pem")
};
if ( fs.existsSync("dev_key.pem") ) {
var https_options = {
key: fs.readFileSync("dev_key.pem"),
cert: fs.readFileSync("dev_cert.pem")
};
http.createServer(handle_req).listen(8001, "localhost");
https.createServer(https_options, handle_req).listen(8002, "localhost");
http.createServer(handle_req).listen(8001, "localhost");
https.createServer(https_options, handle_req).listen(8002, "localhost");
net.createServer(function(conn) {
conn.on('error', function(e) {
util.log("[" + util.colors.cyan("HTTP") + "] Connection Error: " + util.colors.magenta('' + e));
});
net.createServer(function(conn) {
conn.on('error', function(e) {
util.log("[" + util.colors.cyan("HTTP") + "] Connection Error: " + util.colors.magenta('' + e));
});
conn.once('data', function(buf) {
var address = (buf[0] === 22) ? 8002 : 8001;
var proxy = net.createConnection(address, function() {
proxy.write(buf);
conn.pipe(proxy).pipe(conn);
});
});
}).listen(8000);
conn.once('data', function(buf) {
var address = (buf[0] === 22) ? 8002 : 8001;
var proxy = net.createConnection(address, function() {
proxy.write(buf);
conn.pipe(proxy).pipe(conn);
});
});
}).listen(8000);
util.log("[" + util.colors.cyan("HTTPS") + "] Listening on Port: " + util.colors.magenta("8000"));
util.log("[" + util.colors.cyan("HTTPS") + "] Listening on Port: " + util.colors.magenta("8000"));
} else {
http.createServer(handle_req).listen(8000, "localhost");
util.log("[" + util.colors.cyan("HTTP") + "] Listening on Port: " + util.colors.magenta("8000"));
}
} else {
http.createServer(handle_req).listen(8000, "localhost");
util.log("[" + util.colors.cyan("HTTP") + "] Listening on Port: " + util.colors.magenta("8000"));
}
});

View file

@ -5,25 +5,25 @@
"description": "FrankerFaceZ gives Twitch users custom emoticons.",
"main": "script.js",
"devDependencies": {
"gulp": "^3.8.10",
"gulp": "^3.9.1",
"gulp-browserify": "^0.5.1",
"gulp-changed": "^1.1.0",
"gulp-clean": "^0.3.1",
"gulp-concat": "^2.4.3",
"gulp-changed": "^1.3.2",
"gulp-clean": "^0.3.2",
"gulp-clean-css": "^2.0.12",
"gulp-concat": "^2.6.0",
"gulp-declare": "^0.3.0",
"gulp-filesize": "0.0.6",
"gulp-footer": "^1.0.5",
"gulp-header": "^1.2.2",
"gulp-header": "^1.8.8",
"gulp-js-escape": "^1.0.1",
"gulp-less": "^3.1.0",
"gulp-minify-css": "^1.2.1",
"gulp-rename": "^1.2.0",
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^1.0.2",
"gulp-util": "^3.0.2",
"gulp-wrap": "^0.11.0",
"request": "^2.65.0",
"vinyl-ftp": "^0.4.5"
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.7",
"gulp-wrap": "^0.13.0",
"request": "^2.74.0",
"vinyl-ftp": "^0.5.0"
},
"repository": {
"type": "git",

View file

@ -340,15 +340,25 @@ FFZ.prototype.get_badges = function(user, room_id, badges, msg) {
FFZ.prototype.get_line_badges = function(msg) {
var room = msg.get && msg.get('room') || msg.room,
from = msg.get && msg.get('from') || msg.from,
tags = msg.get && msg.get('tags') || msg.tags || {},
badge_tag = tags.badges || {};
// Twitch Badges
var badges = this.get_twitch_badges(badge_tag);
// FFZ Badges
return this.get_badges(from, room, badges, msg);
}
FFZ.prototype.get_twitch_badges = function(badge_tag) {
var badges = {},
hidden_badges = this.settings.hidden_badges,
last_id = -1,
had_last = false,
room = msg.get && msg.get('room') || msg.room,
from = msg.get && msg.get('from') || msg.from,
tags = msg.get && msg.get('tags') || msg.tags || {},
badge_tag = tags.badges || {},
service = utils.ember_lookup('service:badges'),
badgeCollection = service && service.badgeCollection,
@ -410,12 +420,11 @@ FFZ.prototype.get_line_badges = function(msg) {
}
}
// FFZ Badges
return this.get_badges(from, room, badges, msg);
return badges;
}
FFZ.prototype.get_other_badges = function(user_id, room_id, user_type, has_sub, has_turbo) {
/*FFZ.prototype.get_other_badges = function(user_id, room_id, user_type, has_sub, has_turbo) {
var badges = {};
if ( room_id && user_id === room_id )
@ -435,7 +444,7 @@ FFZ.prototype.get_other_badges = function(user_id, room_id, user_type, has_sub,
badges[15] = {klass: 'turbo', title: 'Turbo'}
return this.get_badges(user_id, room_id, badges, null);
}
}*/
// --------------------

View file

@ -175,6 +175,46 @@ FFZ.ffz_commands.massmod = function(room, args) {
FFZ.ffz_commands.massmod.help = "Usage: /ffz massmod <list, of, users>\nBroadcaster only. Mod all the users in the provided list.";
// -----------------
// Mass Unbanning
// -----------------
FFZ.prototype.get_banned_users = function() {
var f = this;
return new Promise(function(succeed, fail) {
var user = f.get_user();
if ( ! user )
return fail();
jQuery.get("/settings/channel").done(function(data) {
try {
var dom = new DOMParser().parseFromString(data, 'text/html'),
users = _.pluck(dom.querySelectorAll('.ban .obj'), 'textContent');
succeed(_.map(users, function(x) { return x.trim() }));
} catch(err) {
f.error("Failed to parse banned users", err);
fail();
}
}).fail(function(err) {
f.error("Failed to load banned users", err);
fail();
})
});
}
/*FFZ.ffz_commands.massunban = function(room, args) {
var user = this.get_user();
if ( ! user || (user.login !== room.id && ! user.is_admin && ! user.is_staff) )
return "You must be the broadcaster to use massunban.";
}*/
/*FFZ.ffz_commands.massunban = function(room, args) {
args = args.join(" ").trim();

View file

@ -312,7 +312,7 @@ FFZ.prototype.modify_chat_input = function(component) {
// Let the browser's paste be do as it do if there weren't any emoji.
if ( output.length === text.length )
return f.log("No emoji in paste");
return;
// Can we get the selection in our input box?
var input = this.get('chatTextArea'),
@ -321,7 +321,7 @@ FFZ.prototype.modify_chat_input = function(component) {
s_end = input && input.selectionEnd;
if ( ! input || typeof s_start !== "number" || typeof s_end !== "number" )
return f.log("Can't get input");
return;
// Still here? We're clear to inject this ourselves then.
event.stopPropagation();
@ -717,6 +717,8 @@ FFZ.prototype.modify_chat_input = function(component) {
for(var i=0; i < suggestions.length; i++) {
var suggestion = suggestions[i],
name = suggestion.id,
display_name = suggestion.displayName || (name && name.capitalize()),
username_match = display_name.trim().toLowerCase() === name,
alias = f.aliases[name];
if ( user_output[name] && ! user_output[name].is_alias ) {
@ -724,22 +726,43 @@ FFZ.prototype.modify_chat_input = function(component) {
token.whispered |= suggestion.whispered;
if ( suggestion.timestamp > token.timestamp )
token.timestamp = suggestion.timestamp;
}
if ( user_output[display_name] && ! user_output[display_name].is_alias ) {
var token = user_output[display_name];
token.whispered |= suggestion.whispered;
if ( suggestion.timestamp > token.timestamp )
token.timestamp = suggestion.timestamp;
} else {
if ( alias_setting !== 2 )
output.push(user_output[name] = {
if ( alias_setting !== 2 ) {
output.push(user_output[display_name] = {
type: "user",
command_content: name,
label: FFZ.get_capitalization(name),
label: display_name,
alternate_match: username_match ? null : name,
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User',
info: 'User' + (username_match ? '' : ' (' + name + ')'),
is_display_name: ! username_match,
is_alias: false
});
if ( ! username_match )
output.push(user_output[name] = {
type: "user",
label: name,
alternate_match: display_name,
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User (' + display_name + ')',
is_alias: false
});
}
if ( alias && alias_setting ) {
if ( user_output[alias] && user_output[alias].is_alias ) {
var token = user_output[name];
var token = user_output[alias];
token.whispered |= suggestion.whispered;
token.timestamp = Math.max(token.timestamp, suggestion.timestamp);
@ -750,7 +773,7 @@ FFZ.prototype.modify_chat_input = function(component) {
label: alias,
whispered: suggestion.whispered,
timestamp: suggestion.timestamp || new Date(0),
info: 'User Alias',
info: 'User Alias (' + name + ')',
is_alias: true
});
}
@ -779,7 +802,10 @@ FFZ.prototype.modify_chat_input = function(component) {
// Names are case insensitive, and we have to ignore the leading @ of our
// partial word when matching.
name = name.toLowerCase();
return char === '@' ? name.indexOf(part2.toLowerCase()) === 0 : name.indexOf(partial.toLowerCase()) === 0;
var part = (char === '@' ? part2 : partial).toLowerCase(),
alt_name = item.alternate_match;
return name.indexOf(part) === 0 || (alt_name && alt_name.indexOf(part) === 0);
} else if ( type === 'emoji' || ! f.settings.input_emoticons_case_sensitive ) {
name = name.toLowerCase();
@ -794,7 +820,9 @@ FFZ.prototype.modify_chat_input = function(component) {
ffz_sorted_suggestions: Ember.computed("ffz_filtered_suggestions.[]", function() {
var text = this.get('textareaValue'),
now = Date.now(),
is_whisper = text.substr(0,3) === '/w ';
char = text.charAt(0),
is_command = char === '/' || char === '.',
is_whisper = is_command && text.substr(1, 2) === 'w ';
return this.get('ffz_filtered_suggestions').sort(function(a, b) {
// First off, sort users ahead of everything else.
@ -809,6 +837,11 @@ FFZ.prototype.modify_chat_input = function(component) {
return 1;
}
if ( a.is_display_name && ! b.is_display_name )
return -1;
else if ( ! a.is_display_name && b.is_display_name )
return 1;
if ( a.timestamp > b.timestamp ) return -1;
else if ( a.timestamp < b.timestamp ) return 1;

View file

@ -622,7 +622,7 @@ FFZ.prototype.modify_chat_view = function(view) {
if ( room && room._ffz_tab ) {
room._ffz_tab.classList.remove('tab-mentioned');
room._ffz_tab.classList.add('active');
var sp = room._ffz_tab.querySelector('span');
var sp = room._ffz_tab.querySelector('span:not(.intl-login)');
if ( sp )
sp.innerHTML = '';
}
@ -630,7 +630,7 @@ FFZ.prototype.modify_chat_view = function(view) {
if ( room && room._ffz_row ) {
room._ffz_row.classList.remove('row-mentioned');
room._ffz_row.classList.add('active');
var sp = room._ffz_row.querySelector('span');
var sp = room._ffz_row.querySelector('span:not(.intl-login)');
if ( sp )
sp.innerHTML = '';
}
@ -740,7 +740,7 @@ FFZ.prototype.modify_chat_view = function(view) {
}
if ( row ) {
var sp = row.querySelector('span');
var sp = row.querySelector('span:not(.intl-login)');
if ( sp )
sp.innerHTML = unread;
}
@ -748,7 +748,7 @@ FFZ.prototype.modify_chat_view = function(view) {
if ( tab ) {
var was_hidden = tab.classList.contains('hidden'),
is_hidden = ! this.ffzTabVisible(room_id),
sp = tab.querySelector('span');
sp = tab.querySelector('span:not(.intl-login)');
if ( was_hidden !== is_hidden ) {
tab.classList.toggle('hidden', is_hidden);
@ -909,10 +909,13 @@ FFZ.prototype.modify_chat_view = function(view) {
active_channel = room === this.get('controller.currentRoom'),
unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount')),
name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) {
name = room.get('channel.display_name') || room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) {
var active_channel = room === view.get('controller.currentRoom');
unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount'));
name_el.innerHTML = utils.sanitize(name) + ' <span>' + unread + '</span>';
var results = group ? [name, undefined] : f.format_display_name(name, room_id, true);
name_el.innerHTML = results[0] + ' <span>' + unread + '</span>';
if ( results[1] )
row.title += '<br>' + results[1];
}));
@ -936,7 +939,11 @@ FFZ.prototype.modify_chat_view = function(view) {
}
name_el.className = 'ffz-room';
name_el.innerHTML = utils.sanitize(name) + ' <span>' + unread + '</span>';
var results = group ? [name, undefined] : f.format_display_name(name, room_id, true);
name_el.innerHTML = results[0] + ' <span>' + unread + '</span>';
if ( results[1] )
row.title += '<br>' + results[1];
row.appendChild(icon);
row.appendChild(name_el);
@ -1044,7 +1051,7 @@ FFZ.prototype.modify_chat_view = function(view) {
link.title = "Chat Room Management";
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: 10});
link.addEventListener('click', function() {
var controller = view.get('controller');
@ -1129,10 +1136,13 @@ FFZ.prototype.modify_chat_view = function(view) {
unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount'));
name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) {
name = room.get('channel.display_name') || room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room_id, function(name) {
var active_channel = room === view.get('controller.currentRoom');
unread = utils.format_unread(active_channel ? 0 : room.get('unreadCount'));
tab.innerHTML = icon + utils.sanitize(name) + '<span>' + unread + '</span>';
var results = group ? [name, undefined] : f.format_display_name(name, room_id, true, true);
tab.innerHTML = icon + results[0] + '<span>' + unread + '</span>';
if ( results[1] )
tab.title += '<br>' + results[1];
}));
if ( current_channel ) {
@ -1146,7 +1156,10 @@ FFZ.prototype.modify_chat_view = function(view) {
else
tab.title = "Pinned Channel";
tab.innerHTML = icon + utils.sanitize(name) + '<span>' + unread + '</span>';
var results = group ? [name, undefined] : f.format_display_name(name, room_id, true, true);
tab.innerHTML = icon + results[0] + '<span>' + unread + '</span>';
if ( results[1] )
tab.title += '<br>' + results[1];
tab.addEventListener('click', function() {
var controller = view.get('controller');
@ -1336,6 +1349,8 @@ FFZ.chat_commands.join = function(room, args) {
return "You have already joined " + room_id + ". Please use \"/part " + room_id + "\" to leave it.";
}
FFZ.chat_commands.join.no_bttv = true;
FFZ.chat_commands.part = function(room, args) {
if ( this.has_bttv )
@ -1354,4 +1369,6 @@ FFZ.chat_commands.part = function(room, args) {
return "You do not have " + room_id + " pinned and you cannot leave the current channel or hosted channels via /part.";
else
return "You are not in " + room_id + ".";
}
}
FFZ.chat_commands.part.no_bttv = true;

View file

@ -20,6 +20,7 @@ FFZ.settings_info.conv_focus_on_click = {
help: "Focus on a conversation's input box when you click it."
};
FFZ.settings_info.top_conversations = {
type: "boolean",
value: false,
@ -34,6 +35,37 @@ FFZ.settings_info.top_conversations = {
};
FFZ.settings_info.hide_whispers_in_embedded_chat = {
type: "boolean",
value: false,
no_bttv: true,
category: "Whispers",
name: "Hide Whispers in Embedded Chat",
help: "Do not display whispers on the dashboard, in pop-out chat, or in chat embedded into other websites.",
on_update: function(val) {
if ( ! val || this.has_bttv )
return;
for(var room_id in this.rooms) {
var room = this.rooms[room_id].room;
if ( ! room )
continue;
var messages = room.get('messages'),
length = messages && messages.length || 0,
i = length;
while(--i >= 0) {
if ( messages[i] && messages[i].style === 'whisper' )
messages.removeAt(i);
}
}
}
};
FFZ.settings_info.hide_conversations_in_theatre = {
type: "boolean",
value: false,
@ -117,7 +149,7 @@ FFZ.prototype.modify_conversation_window = function(component) {
ffzHeaderBadges: Ember.computed("thread.participants", "currentUsername", function() {
var e = this.get("otherUser");
return f.get_other_badges(e.get('username'), null, e.get('userType'), false, e.get('hasTurbo'));
return f.get_badges(e.get('username'), null, f.get_twitch_badges(e.get('badges')), null);
}),
ffzReplaceBadges: function() {
@ -125,13 +157,25 @@ FFZ.prototype.modify_conversation_window = function(component) {
badge_el = el && el.querySelector('.badges'),
badges = this.get('ffzHeaderBadges');
if ( ! el )
return;
if ( ! badge_el ) {
badge_el = createElement('span', 'badges');
var header = el && el.querySelector('.convoHeader .username');
if ( ! header )
return;
header.insertBefore(badge_el, header.firstChild);
}
badge_el.innerHTML = f.render_badges(badges);
}.observes('ffzHeaderBadges'),
ffz_init: function() {
var el = this.get('element'),
header = el && el.querySelector('.conversation-header'),
header_name = header && header.querySelector('.conversation-header-name'),
header = el && el.querySelector('.convoHeader'),
header_name = header && header.querySelector('.username'),
raw_color = this.get('otherUser.color'),
colors = raw_color && f._handle_color(raw_color),

View file

@ -225,6 +225,7 @@ FFZ.prototype.setup_directory = function() {
this.update_views('component:stream-preview', function(x) { this.modify_directory_live(x, false) }, true);
this.update_views('component:creative-preview', function(x) { this.modify_directory_live(x, false) }, true);
this.update_views('component:csgo-channel-preview', function(x) { this.modify_directory_live(x, true) }, true);
this.update_views('component:twitch-carousel/stream-item', function(x) { this.modify_directory_live(x, false, true) }, true);
this.update_views('component:host-preview', this.modify_directory_host, true, true);
this.update_views('component:video-preview', this.modify_video_preview, true);
@ -418,16 +419,22 @@ FFZ.prototype.modify_game_follow_button = function(component) {
}
FFZ.prototype.modify_directory_live = function(component, is_csgo) {
FFZ.prototype.modify_directory_live = function(component, is_csgo, is_card) {
var f = this,
pref = is_csgo ? 'channel.' : 'stream.';
pref = is_csgo ? 'channel.' : 'stream.',
meta_selector = is_card ? '.card__body' : '.meta',
thumb_selector = is_card ? '.card__img' : '.thumb',
cap_selector = is_card ? 'a:not(.card__boxpin)' : '.cap';
utils.ember_reopen_view(component, {
ffz_init: function() {
var el = this.get('element'),
meta = el && el.querySelector('.meta'),
thumb = el && el.querySelector('.thumb'),
cap = thumb && thumb.querySelector('.cap'),
meta = el && el.querySelector(meta_selector),
thumb = el && el.querySelector(thumb_selector),
cap = thumb && thumb.querySelector(cap_selector),
uptime_parent = is_card ? thumb : cap,
channel_id = this.get(pref + 'channel.name'),
game = this.get(pref + 'game');
@ -438,13 +445,13 @@ FFZ.prototype.modify_directory_live = function(component, is_csgo) {
el.classList.toggle('ffz-game-banned', f.settings.banned_games.indexOf(game && game.toLowerCase()) !== -1);
el.classList.toggle('ffz-game-spoilered', f.settings.spoiler_games.indexOf(game && game.toLowerCase()) !== -1);
if (f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
if (f.settings.stream_uptime && f.settings.stream_uptime < 3 && uptime_parent ) {
var t_el = this._ffz_uptime = document.createElement('div');
t_el.className = 'overlay_info length live';
jQuery(t_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
cap.appendChild(t_el);
uptime_parent.appendChild(t_el);
this._ffz_uptime_timer = setInterval(this.ffzUpdateUptime.bind(this), 1000);
this.ffzUpdateUptime();
}
@ -463,7 +470,7 @@ FFZ.prototype.modify_directory_live = function(component, is_csgo) {
logo.classList.toggle('is-csgo', is_csgo);
logo.src = this.get(pref + 'channel.logo') || NO_LOGO;
logo.alt = this.get(pref + 'channel.display_name');
logo.alt = f.format_display_name(this.get(pref + 'channel.display_name'), channel_id, true, true)[0];
link.href = '/' + channel_id;
link.addEventListener('click', function(e) {
@ -510,9 +517,16 @@ FFZ.prototype.modify_directory_live = function(component, is_csgo) {
},
ffzUpdateUptime: function() {
var raw_created = this.get(pref + 'created_at'),
up_since = raw_created && utils.parse_date(raw_created),
now = Date.now() - (f._ws_server_offset || 0),
var up_since;
if ( is_card )
up_since = this.get(pref + 'createdAt');
else {
var raw_created = this.get(pref + 'created_at');
up_since = raw_created && utils.parse_date(raw_created);
}
var now = Date.now() - (f._ws_server_offset || 0),
uptime = up_since && Math.floor((now - up_since.getTime()) / 1000) || 0;
if ( uptime > 0 ) {
@ -620,11 +634,13 @@ FFZ.prototype.modify_directory_host = function(component) {
var menu = document.createElement('div'), hdr,
make_link = function(target) {
var link = document.createElement('a');
var link = document.createElement('a'),
results = f.format_display_name(target.display_name, target.name, true);
link.className = 'dropmenu_action';
link.setAttribute('data-channel', target.name);
link.href = '/' + target.name;
link.innerHTML = '<img class="image" src="' + utils.sanitize(target.logo || NO_LOGO) + '"><span class="title">' + utils.sanitize(target.display_name) + '</span>';
link.innerHTML = '<img class="image" src="' + utils.sanitize(target.logo || NO_LOGO) + '"><span class="title' + (results[1] ? ' html-tooltip" title="' + utils.quote_attr(results[1]) : '') + '">' + results[0] + '</span>';
link.addEventListener('click', t.ffzVisitChannel.bind(t, target.name));
menu.appendChild(link);
return link;
@ -709,7 +725,7 @@ FFZ.prototype.modify_directory_host = function(component) {
logo.className = 'profile-photo';
logo.src = this.get('stream.target.channel.logo') || NO_LOGO;
logo.alt = this.get('stream.target.channel.display_name');
logo.alt = f.format_display_name(target.display_name, target.name, true, true)[0];
link.href = '/' + target.name;
link.addEventListener('click', this.ffzVisitChannel.bind(this, target.name));

View file

@ -85,8 +85,9 @@ FFZ.prototype.setup_profile_following = function() {
}
success(result);
}).catch(function(err) {
fail(result);
fail(err);
})
});
}
@ -131,15 +132,14 @@ FFZ.prototype.modify_display_followed_item = function(component) {
el.classList.add('ffz-processed');
// TODO: REMOVE
window._d = this;
jQuery('.aspect', el).tipsy();
if ( ! data )
return false;
var now = Date.now() - (f._ws_server_offset || 0),
age = data[0] ? Math.floor((now - data[0].getTime()) / 1000) : 0,
t_el = createElement('div', 'overlay_info length'),
t_el = createElement('div', 'overlay_info length html-tooltip'),
update_time = function() {
var now = Date.now() - (f._ws_server_offset || 0),
@ -154,7 +154,6 @@ FFZ.prototype.modify_display_followed_item = function(component) {
};
update_time();
jQuery(t_el).tipsy({html:true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
el.appendChild(t_el);
if ( ! mine || ! is_following )
@ -162,7 +161,7 @@ FFZ.prototype.modify_display_followed_item = function(component) {
var actions = createElement('div', 'actions'),
follow = createElement('button', 'button ffz-no-bg follow'),
notif = createElement('button', 'button ffz-no-bg notifications'),
notif = createElement('button', 'button ffz-no-bg notifications html-tooltip'),
update_follow = function() {
data = user_cache[user_id];
@ -173,7 +172,9 @@ FFZ.prototype.modify_display_followed_item = function(component) {
update_notif = function() {
data = user_cache[user_id];
notif.classList.toggle('notifications-on', data && data[1]);
notif.textContent = 'Notification ' + (data && data[1] ? 'On' : 'Off');
notif.textContent = 'Notifications'; // ' + (data && data[1] ? 'On' : 'Off');
notif.setAttribute('original-title', 'Email Notifications: ' + (data && data[1] ? 'En' : 'Dis') + 'abled');
jQuery(notif).trigger('mouseout');
};
update_follow();

View file

@ -22,6 +22,50 @@ FFZ.settings_info.alias_italics = {
}
};
FFZ.settings_info.username_display = {
type: "select",
options: {
0: "Username Only",
1: "Capitalization Only",
2: "Display Name Only",
3: "Username in Parenthesis",
4: "Username in Tooltip"
},
category: "Chat Appearance",
no_bttv: true,
name: "Username Display",
help: "How a user's name should be rendered when their display name differs from the username.",
value: 3,
process_value: function(val) {
if ( typeof val === "string" ) {
val = parseInt(val);
if ( isNaN(val) || ! isFinite(val) )
val = 3;
}
return val;
},
on_update: function(val) {
var CL = utils.ember_resolve('component:chat/chat-line'),
views = CL ? utils.ember_views() : [];
for(var vid in views) {
var view = views[vid];
if ( view instanceof CL && view.buildFromHTML ) {
view.$('.from').replaceWith(view.buildFromHTML());
if ( view.get('msgObject.to') )
view.$('.to').replaceWith(view.buildFromHTML(true));
}
}
}
}
FFZ.settings_info.room_status = {
type: "boolean",
value: true,
@ -768,27 +812,27 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
return output + '</span>';
},
buildSenderHTML: function() {
var user = this.get('msgObject.from'),
room_id = this.get('msgObject.room'),
room = f.rooms && f.rooms[room_id],
buildFromHTML: function(is_recipient) {
var username = this.get(is_recipient ? 'msgObject.to' : 'msgObject.from'),
raw_display = this.get(is_recipient ? 'msgObject.tags.recipient-display-name' : 'msgObject.tags.display-name'),
alias = f.aliases[username],
deleted = this.get('msgObject.deleted'),
r = this,
recipient = this.get('msgObject.to'),
is_whisper = recipient && recipient.length,
is_replay = this.get('ffz_is_replay'),
this_ul = this.get('ffzUserLevel'),
other_ul = room && room.room && room.room.get('ffzUserLevel') || 0,
raw_color = this.get('msgObject.color'),
colors = raw_color && f._handle_color(raw_color),
raw_color = this.get(is_recipient ? 'msgObject.toColor' : 'msgObject.color'),
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode'))),
is_replay = this.get('ffz_is_replay'),
system_msg = this.get('systemMsg'),
colors = raw_color && f._handle_color(raw_color),
style = colors ? 'color:' + (is_dark ? colors[1] : colors[0]) : '',
colored = colors ? ' has-color' + (is_replay ? ' replay-color' : '') : '',
results = f.format_display_name(raw_display, username);
return '<span class="' + (is_recipient ? 'to' : 'from') + (alias ? ' ffz-alias' : '') + (results[1] ? ' html-tooltip' : '') + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + (results[1] ? '" title="' + utils.quote_attr(results[1]) : '') + '">' + results[0] + '</span>';
},
buildSenderHTML: function() {
var system_msg = this.get('systemMsg'),
output = '';
output = '<div class="indicator"></div>';
@ -809,41 +853,15 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
// Badges
output += '<span class="badges">' + f.render_badges(f.get_line_badges(this.get('msgObject'))) + '</span>';
// Alias Support
var alias = f.aliases[user],
name = this.get('msgObject.tags.display-name') || (user && user.capitalize()) || "unknown user",
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]) || '',
colored = style ? ' has-color' + (is_replay ? ' replay-color' : '') : '';
// From!
output += this.buildFromHTML();
output += '<span class="from' + (alias ? ' ffz-alias html-tooltip' : '') + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '');
if ( alias )
output += '" title="' + utils.quote_san(name) + '">' + utils.sanitize(alias);
else
output += '">' + utils.sanitize(name);
// Whisper Legacy Sucks
if ( is_whisper ) {
var to_alias = f.aliases[recipient],
to_name = this.get('msgObject.tags.recipient-display-name') || (recipient && recipient.capitalize()) || "unknown user",
to_color = this.get('msgObject.toColor'),
to_colors = to_color && f._handle_color(to_color),
to_style = to_color ? 'color:' + (is_dark ? to_colors[1] : to_colors[0]) : '',
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 html-tooltip' : '') + to_colored + '" style="' + to_style + (to_colors ? '" data=color="' + to_color : '');
if ( to_alias )
output += '" title="' + utils.quote_san(to_name) + '">' + utils.sanitize(to_alias);
else
output += '">' + utils.sanitize(to_name);
if ( this.get('msgObject.to') ) {
output += "<svg class='svg-whisper-arrow' height='10px' version='1.1' width='16px'><polyline points='6 2, 10 6, 6 10, 6 2' /></svg>";
output += this.buildFromHTML(true);
}
return output + '</span><span class="colon">:</span> ';
return output + '<span class="colon">:</span> ';
},
buildDeletedMessageHTML: function() {
@ -917,7 +935,7 @@ FFZ.prototype._modify_chat_subline = function(component) {
this._modify_chat_line(component);
component.reopen({
classNameBindings: ["msgObject.style", "msgObject.ffz_has_mention:ffz-mentioned", "ffzWasDeleted:ffz-deleted", "ffzHasOldMessages:clearfix", "ffzHasOldMessages:ffz-has-deleted"],
classNameBindings: ["msgObject.style", "msgObject.isModerationMessage:moderation-message", "msgObject.ffz_has_mention:ffz-mentioned", "ffzWasDeleted:ffz-deleted", "ffzHasOldMessages:clearfix", "ffzHasOldMessages:ffz-has-deleted"],
attributeBindings: ["msgObject.tags.id:data-id", "msgObject.room:data-room", "msgObject.from:data-sender", "msgObject.deleted:data-deleted"],
didInsertElement: function() {
@ -989,7 +1007,7 @@ FFZ.prototype._modify_chat_subline = function(component) {
} else if ( f._click_emote(e.target, e) )
return;
else if ( e.target.classList.contains('from') ) {
else if ( e.target.classList.contains('from') || e.target.parentElement.classList.contains('from') ) {
var n = this.get('element'),
bounds = n && n.getBoundingClientRect() || document.body.getBoundingClientRect(),
x = 0, right;
@ -1005,6 +1023,22 @@ FFZ.prototype._modify_chat_subline = function(component) {
sender: from
});
} else if ( e.target.classList.contains('to') || e.target.parentElement.classList.contains('to') ) {
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: bounds.left,
right: right,
top: bounds.top + bounds.height,
real_top: bounds.top,
sender: this.get('msgObject.to')
});
} else if ( e.target.classList.contains('undelete') ) {
e.preventDefault();
this.set("msgObject.deleted", false);

View file

@ -661,13 +661,6 @@ FFZ.prototype.modify_moderation_card = function(component) {
info.innerHTML = out;
}.observes("cardInfo.user.views"),
userName: Ember.computed("cardInfo.user.id", "cardInfo.user.display_name", function() {
var user_id = this.get("cardInfo.user.id"),
alias = f.aliases[user_id];
return alias || this.get("cardInfo.user.display_name") || user_id.capitalize();
}),
ffz_destroy: function() {
if ( f._mod_card === this )
f._mod_card = undefined;
@ -724,15 +717,15 @@ FFZ.prototype.modify_moderation_card = function(component) {
// Alias Display
if ( alias ) {
var name = el.querySelector('h3.name'),
link = name && name.querySelector('a');
if ( link )
name = link;
var name = el.querySelector('h4.name a');
if ( name ) {
name.classList.add('ffz-alias');
name.title = utils.sanitize(controller.get('cardInfo.user.display_name') || user_id.capitalize());
jQuery(name).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
var results = f.format_display_name(controller.get('cardInfo.user.display_name'), user_id);
name.innerHTML = results[0];
name.title = results[1] || '';
if ( results[1] )
jQuery(name).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
}
}
@ -742,7 +735,7 @@ FFZ.prototype.modify_moderation_card = function(component) {
// Info-tize it!
if ( f.settings.mod_card_info ) {
var info = utils.createElement('div', 'info channel-stats'),
after = el.querySelector('h3.name');
after = el.querySelector('h4.name');
if ( after ) {
el.classList.add('ffz-has-info');
after.parentElement.insertBefore(info, after.nextSibling);
@ -995,10 +988,11 @@ FFZ.prototype.modify_moderation_card = function(component) {
alias_btn.addEventListener('click', function() {
var user = controller.get('cardInfo.user.id'),
alias = f.aliases[user];
alias = f.aliases[user],
results = f.format_display_name(controller.get('cardInfo.user.display_name'), user, true);
utils.prompt(
"Alias for <b>" + utils.sanitize(controller.get('cardInfo.user.display_name') || user) + "</b>",
"Alias for <b" + (results[1] ? ' class="html-tooltip" title="' + utils.quote_attr(results[1]) + '">' : '>') + results[0] + "</b>",
"Please enter an alias for the user. Leave it blank to remove the alias.",
alias,
function(new_val) {
@ -1015,10 +1009,16 @@ FFZ.prototype.modify_moderation_card = function(component) {
// Update UI
f._update_alias(user);
Ember.propertyDidChange(controller, 'cardInfo.user.display_name');
var name = el.querySelector('h3.name');
if ( name )
var name = el.querySelector('h4.name');
if ( name ) {
name.classList.toggle('ffz-alias', new_val);
var results = f.format_display_name(controller.get('cardInfo.user.display_name'), user_id);
name.innerHTML = results[0];
name.title = results[1] || '';
if ( results[1] )
jQuery(name).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
}
});
});
@ -1256,11 +1256,10 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
if ( helpers && helpers.getTime )
out.push('<span class="timestamp">' + helpers.getTime(msg.date) + '</span>');
var alias = this.aliases[msg.from],
name = (msg.tags && msg.tags['display-name']) || (msg.from && msg.from.capitalize()) || "unknown user";
if ( show_from ) {
var alias = this.aliases[msg.from],
results = this.format_display_name(msg.tags && msg.tags['display-name'], msg.from);
// Badges
out.push('<span class="badges">');
out.push(this.render_badges(this.get_line_badges(msg, false)));
@ -1277,15 +1276,18 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
// Aliases and Styling
// Styling
var style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
colored = style ? ' has-color' : '';
if ( alias )
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>');
out.push('<span class="from' +
(alias ? ' ffz-alias' : '') +
(results[1] ? ' html-tooltip' : '') +
(style ? ' has-color' : '') +
'" style="' + style + '"' +
(colors ? ' data-color="' + raw_color + '"' : '') +
(results[1] ? ' title="' + utils.quote_attr(results[1]) + '"' : '') + '>'
+ results[0] + '</span>');
out.push('<span class="colon">:</span> ');
}
@ -1367,8 +1369,8 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
FFZ.prototype._update_alias = function(user) {
var alias = this.aliases && this.aliases[user],
cap_name = FFZ.get_capitalization(user),
display_name = alias || cap_name,
results = this.format_display_name(FFZ.get_capitalization(user), user),
el = this._roomv && this._roomv.get('element'),
lines = el && el.querySelectorAll('.chat-line[data-sender="' + user + '"]');
@ -1383,8 +1385,9 @@ FFZ.prototype._update_alias = function(user) {
continue;
el_from.classList.toggle('ffz-alias', alias);
el_from.textContent = display_name;
el_from.title = alias ? cap_name : '';
el_from.classList.toggle('html-tooltip', results[1] || false);
el_from.innerHTML = results[0];
el_from.title = results[1] || '';
}
@ -1402,16 +1405,12 @@ FFZ.prototype._update_alias = function(user) {
FFZ.chat_commands.purge = function(room, args) {
if ( ! args || ! args.length )
return "Purge Usage: /p username [more usernames separated by spaces]";
return "Purge Usage: /p username [ban reason]";
if ( args.length > 10 )
return "Please only purge up to 10 users at once.";
var name = args.shift(),
reason = args.length ? args.join(" ") : "";
for(var i=0; i < args.length; i++) {
var name = args[i];
if ( name )
room.room.send("/timeout " + name + " 1", true);
}
room.room.send("/timeout " + name + " 1 " + reason, true);
}
FFZ.chat_commands.p = function(room, args) {
@ -1423,7 +1422,7 @@ FFZ.chat_commands.p.enabled = function() { return this.settings.short_commands;
FFZ.chat_commands.t = function(room, args) {
if ( ! args || ! args.length )
return "Timeout Usage: /t username [duration]";
return "Timeout Usage: /t username [duration] [ban reason]";
room.room.send("/timeout " + args.join(" "), true);
}
@ -1432,16 +1431,12 @@ FFZ.chat_commands.t.enabled = function() { return this.settings.short_commands;
FFZ.chat_commands.b = function(room, args) {
if ( ! args || ! args.length )
return "Ban Usage: /b username [more usernames separated by spaces]";
return "Ban Usage: /b username [ban reason]";
if ( args.length > 10 )
return "Please only ban up to 10 users at once.";
var name = args.shift(),
reason = args.length ? args.join(" ") : "";
for(var i=0; i < args.length; i++) {
var name = args[i];
if ( name )
room.room.send("/ban " + name, true);
}
room.room.send("/ban " + name + " " + reason, true);
}
FFZ.chat_commands.b.enabled = function() { return this.settings.short_commands; }

View file

@ -165,6 +165,7 @@ FFZ.prototype.modify_twitch_player = function(player) {
this.$('#video-1').html('');
Mousetrap.unbind(['alt+x', 'alt+t', 'esc']);
this.set('player', null);
this.set('ffz_post_player', false);
// Now, let Twitch create a new player as usual.
Ember.run.next(this.insertPlayer.bind(this, true));

View file

@ -4,13 +4,24 @@ var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
helpers,
NOTICE_MAPPING = {
'slow': 'slow_on',
'slowoff': 'slow_off',
'r9kbeta': 'r9k_on',
'r9kbetaoff': 'r9k_off',
'subscribers': 'subs_on',
'subscribersoff': 'subs_off',
'emoteonly': 'emote_only_on',
'emoteonlyoff': 'emote_only_off'
},
STATUS_BADGES = [
["r9k", "r9k", "This room is in R9K-mode."],
["emote", "emoteOnly", "This room is in Twitch emoticons only mode. Emoticons added by extensions are not available in this mode."],
["sub", "subsOnly", "This room is in subscribers-only mode."],
["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow') || 120) + " seconds." }],
["ban", "ffz_banned", "You have been banned from talking in this room."],
["delay", function(room) { return room && room.get('ffz_chat_delay') !== 0 }, function(room) { return "Artificial chat delay is enabled. Messages are displayed after " + (room.get('ffz_chat_delay')/1000) + " seconds." }],
["delay", function(room) { return room && room.get('ffz_chat_delay') !== 0 }, function(room) { return "Artificial chat delay is enabled. Messages are displayed after " + (room ? room.get('ffz_chat_delay')/1000 : 0) + " seconds." }],
["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in " + (this.settings.chat_batching/1000) + " second increments." }]
],
@ -38,15 +49,24 @@ FFZ.prototype.setup_room = function() {
this.rooms = {};
this.log("Creating room style element.");
var s = this._room_style = document.createElement("style");
var f = this,
s = this._room_style = document.createElement("style");
s.id = "ffz-room-css";
document.head.appendChild(s);
this.log("Hooking the Ember Chat PubSub service.");
var PubSub = utils.ember_lookup('service:chat-pubsub');
if ( PubSub )
this._modify_chat_pubsub(PubSub);
else
this.error("Cannot locate the Chat PubSub service.");
this.log("Hooking the Ember Room controller.");
// Responsive ban button.
var f = this,
RC = utils.ember_lookup('controller:room');
var RC = utils.ember_lookup('controller:room');
if ( RC ) {
var orig_ban = RC._actions.banUser,
@ -119,6 +139,120 @@ FFZ.prototype.setup_room = function() {
}
// --------------------
// PubSub is fucking awful
// --------------------
FFZ.prototype._modify_chat_pubsub = function(pubsub) {
var f = this;
pubsub.reopen({
setupService: function(room_id, t) {
var n = this;
this.get("session").withCurrentUser(function(user) {
if ( n.isDestroyed )
return;
var ps = n._pubsub(),
token = user.chat_oauth_token,
new_topics = [
"chat_message_updated." + room_id,
"chat_moderator_actions." + room_id];
for(var i=0; i < new_topics.length; i++)
ps.Listen({
topic: new_topics[i],
auth: token,
success: function() {},
failure: function() {},
message: Ember.run.bind(n, n._onPubsubMessage, new_topics[i])
});
if ( n.chatTopics )
n.chatTopics = n.chatTopics.concat(new_topics);
else
n.chatTopics = new_topics;
ps.on("connected", Ember.run.bind(n, n._onPubsubConnect));
ps.on("disconnected", Ember.run.bind(n, n._onPubsubDisconnect));
t();
});
},
tearDownService: function(room_id) {
if ( ! this.chatTopics )
return;
var ps = this._pubsub(),
old_topics;
if ( ! room_id )
room_id = this.get("ffz_teardown_target");
if ( room_id ) {
// Make sure it's a string.
room_id = '.' + room_id;
old_topics = this.chatTopics.filter(function(x) { return x.substr(-room_id.length) === room_id });
} else
old_topics = this.chatTopics;
for(var i=0; i < old_topics.length; i++) {
ps.Unlisten({
topic: old_topics[i],
success: function() {},
failure: function() {}
});
this.chatTopics.removeObject(old_topics[i]);
}
if ( ! this.chatTopics.length )
this.chatTopics = null;
},
_onPubsubMessage: function(topic, e) {
if ( this.isDestroyed )
return;
var msg = JSON.parse(e),
msg_data = msg.data,
msg_type = msg.type || msg_data.type;
if ( msg_data )
msg_data.topic = topic;
this.trigger(msg_type, msg_data);
}
});
if ( ! pubsub.chatTopics )
return;
// Now that we've modified that, we need to re-listen to everything.
pubsub.get("session").withCurrentUser(function(user) {
if ( pubsub.isDestroyed )
return;
var ps = pubsub._pubsub(),
token = user.chat_oauth_token;
for(var i=0; i < pubsub.chatTopics.length; i++) {
ps.Unlisten({
topic: pubsub.chatTopics[i],
success: function() {},
failure: function() {}
});
ps.Listen({
topic: pubsub.chatTopics[i],
auth: token,
success: function() {},
failure: function() {},
message: Ember.run.bind(pubsub, pubsub._onPubsubMessage, pubsub.chatTopics[i])
});
}
});
}
// --------------------
// View Customization
// --------------------
@ -225,6 +359,7 @@ FFZ.prototype.modify_room_view = function(view) {
this.ffzDisableFreeze();
},
ffzOnKey: function(event) {
this.ffz_ctrl = event.ctrlKey;
this.ffz_alt = event.altKey;
@ -835,10 +970,17 @@ FFZ.prototype._insert_history = function(room_id, data, from_server) {
// Store the message ID for this message, of course.
var msg_id = msg.tags && msg.tags.id,
ids = r.ffz_ids = r.ffz_ids || {};
notice_type = msg.tags && msg.tags['msg-id'],
ids = r.ffz_ids = r.ffz_ids || {},
notices = r.ffz_last_notices = r.ffz_last_notices || {};
if ( msg_id && ! ids[msg_id] )
ids[msg_id] = msg;
if ( notice_type && ! notices[notice_type] )
notices[notice_type] = msg;
messages.unshiftObject(msg);
inserted += 1;
@ -876,9 +1018,15 @@ FFZ.prototype._insert_history = function(room_id, data, from_server) {
messages.insertAt(inserted, msg);
while ( messages.length > buffer_size ) {
// Remove this message from the ID tracker.
var m = messages.get(0);
if ( m.tags && m.tags.id && r.ffz_ids && r.ffz_ids[m.tags.id] )
delete r.ffz_ids[m.tags.id];
var m = messages.get(0),
msg_id = m.tags && m.tags.id,
notice_type = m.tags && m.tags['msg-id'];
if ( msg_id && r.ffz_ids && r.ffz_ids[msg_id] )
delete r.ffz_ids[msg_id];
if ( notice_type && r.ffz_last_notices && r.ffz_last_notices[notice_type] === m )
delete r.ffz_last_notices[notice_type];
messages.removeAt(0);
removed++;
@ -1052,13 +1200,16 @@ FFZ.prototype._modify_room = function(room) {
f.add_room(this.id, this);
this.set("ffz_chatters", {});
this.set("ffz_ids", this.get('ffz_ids') || {});
this.set("ffz_last_notices", this.get('ffz_last_notices') || {});
} catch(err) {
f.error("add_room: " + err);
}
},
willDestroy: function() {
this.get("pubsub").set("ffz_teardown_target", this.get('roomProperties._id'));
this._super();
this.get("pubsub").set("ffz_teardown_target", null);
try {
f.remove_room(this.id);
@ -1067,26 +1218,80 @@ FFZ.prototype._modify_room = function(room) {
}
},
clearMessages: function(user, tags, disable_log) {
addChannelModerationMessage: function(event) {
// Throw out messages that are for other rooms.
var room_id = '.' + this.get("roomProperties._id");
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
return;
var target_notice = NOTICE_MAPPING[event.moderation_action];
if ( target_notice ) {
var last_notice = this.ffz_last_notices && this.ffz_last_notices[target_notice];
if ( last_notice && ! last_notice.has_owner ) {
last_notice.message += ' (By: ' + event.created_by + ')';
last_notice.has_owner = true;
last_notice.cachedTokens = undefined;
if ( last_notice._line )
last_notice._line.ffzRender();
} else {
var waiting = this.ffz_waiting_notices = this.ffz_waiting_notices || {};
waiting[target_notice] = event.created_by;
}
} else if ( f.settings.get_twitch('showModerationActions') )
this._super(event);
},
addLoginModerationMessage: function(event) {
// Throw out messages that are for other rooms.
var room_id = '.' + this.get("roomProperties._id");
if ( event.topic && event.topic.substr(-room_id.length) !== room_id || event.created_by === this.get("session.userData.login") )
return;
// In case we get unexpected input, do the other thing.
if ( ["ban", "unban", "timeout"].indexOf(event.moderation_action) === -1 )
return this._super(event);
var tags = {
'ban-duration': event.moderation_action === 'unban' ? -Infinity : event.args[1],
'ban-reason': event.args[2],
'ban-moderator': event.created_by
};
this.clearMessages(event.args[0], tags, false, event.moderation_action !== 'unban');
},
clearMessages: function(user, tags, disable_log, report_only) {
var t = this;
if ( user ) {
var duration = Infinity,
reason = undefined,
moderator = undefined,
msg_id = undefined,
current_user = f.get_user(),
is_me = current_user && current_user.login === user;
// Read the ban duration and reason from the message tags.
if ( tags && tags['ban-duration'] )
duration = parseInt(tags['ban-duration']);
if ( tags && tags['ban-duration'] ) {
duration = tags['ban-duration'];
if ( typeof duration === 'string' )
duration = parseInt(duration);
if ( isNaN(duration) )
duration = Infinity;
if ( isNaN(duration) )
duration = Infinity;
}
if ( tags && tags['ban-reason'] && (is_me || t.get('isModeratorOrHigher')) )
reason = tags['ban-reason'];
if ( tags && tags['ban-moderator'] && (is_me || t.get('isModeratorOrHigher')) )
moderator = tags['ban-moderator'];
// Does anything really matter?
if ( ! report_only && duration !== -Infinity ) {
// Is there a UUID on the end of the ban reason?
if ( reason ) {
@ -1142,6 +1347,11 @@ FFZ.prototype._modify_room = function(room) {
if ( msg.tags && msg.tags.id === msg_id ) {
msgs.removeAt(i);
delete this.ffz_ids[msg_id];
var notice_type = msg.tags && msg.tags['msg-id'];
if ( notice_type && this.ffz_last_notices && this.ffz_last_notices[notice_type] === msg )
delete this.ffz_last_notices[notice_type];
break;
}
}
@ -1170,8 +1380,14 @@ FFZ.prototype._modify_room = function(room) {
if ( msg.from === user ) {
if ( f.settings.remove_deleted ) {
// Remove this message from the ID tracker.
if ( msg.tags && msg.tags.id && this.ffz_ids && this.ffz_ids[msg.tags.id] )
delete this.ffz_ids[msg.tags.id];
var msg_id = msg.tags && msg.tags.id,
notice_type = msg.tags && msg.tags['msg-id'];
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
delete this.ffz_ids[msg_id];
if ( notice_type && this.ffz_last_notices && this.ffz_last_notices[notice_type] === msg )
delete this.ffz_last_notices[notice_type];
msgs.removeAt(i);
removed++;
@ -1199,6 +1415,9 @@ FFZ.prototype._modify_room = function(room) {
}
}
// End of report_only check.
}
// Now we need to see about displaying a ban notice.
if ( ! disable_log ) {
@ -1220,7 +1439,11 @@ FFZ.prototype._modify_room = function(room) {
}
// Display a notice in chat.
var message = (is_me ? "You have" : FFZ.get_capitalization(user) + " has") + " been " + (isFinite(duration) ? "timed out for " + utils.duration_string(duration, true) : "banned");
var message = (is_me ?
"You have" : ffz.format_display_name(FFZ.get_capitalization(user), user, true, false, true)[0] + " has") +
" been " + (duration === -Infinity ? 'unbanned' :
(duration === 1 ? 'purged' :
(isFinite(duration) ? "timed out for " + utils.duration_string(duration, true) : "banned")));
if ( show_notice ) {
if ( ! last_ban ) {
@ -1229,11 +1452,12 @@ FFZ.prototype._modify_room = function(room) {
date: now,
ffz_ban_target: user,
reasons: reason ? [reason] : [],
moderators: moderator ? [moderator] : [],
msg_ids: msg_id ? [msg_id] : [],
durations: [duration],
end_time: end_time,
timeouts: 1,
message: message + (show_reason && reason ? ' with reason: ' + reason : '.')
timeouts: report_only ? 0 : 1,
message: message + (show_reason && moderator ? ' by ' + moderator : '') + (show_reason && reason ? ' with reason: ' + reason : '.')
};
if ( ban_history )
@ -1248,13 +1472,21 @@ FFZ.prototype._modify_room = function(room) {
if ( reason && last_ban.reasons.indexOf(reason) === -1 )
last_ban.reasons.push(reason);
if ( moderator && last_ban.moderators.indexOf(moderator) === -1 )
last_ban.moderators.push(moderator);
if ( last_ban.durations.indexOf(duration) === -1 )
last_ban.durations.push(duration);
last_ban.end_time = end_time;
last_ban.timeouts++;
last_ban.message = message + ' (' + utils.number_commas(last_ban.timeouts) + ' times)' + (!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '));
if ( ! report_only )
last_ban.timeouts++;
last_ban.message = message +
(last_ban.timeouts > 1 ? ' (' + utils.number_commas(last_ban.timeouts) + ' times)' : '') +
(!show_reason || last_ban.moderators.length === 0 ? '' : ' by ' + last_ban.moderators.join(', ') ) +
(!show_reason || last_ban.reasons.length === 0 ? '.' : ' with reason' + utils.pluralize(last_ban.reasons.length) + ': ' + last_ban.reasons.join(', '));
last_ban.cachedTokens = [{type: "text", text: last_ban.message}];
// Now that we've reset the tokens, if there's a line for this,
@ -1333,9 +1565,15 @@ FFZ.prototype._modify_room = function(room) {
var to_remove = len - limit;
for(var i = 0; i < to_remove; i++) {
// Remove this message from the ID tracker.
var msg = messages.get(i);
if ( msg.tags && msg.tags.id && this.ffz_ids && this.ffz_ids[msg.tags.id] )
delete this.ffz_ids[msg.tags.id];
var msg = messages.get(i),
msg_id = msg.tags && msg.tags.id,
notice_type = msg.tags && msg.tags['msg-id'];
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
delete this.ffz_ids[msg_id];
if ( notice_type && this.ffz_last_notices && this.ffz_last_notices[notice_type] === msg )
delete this.ffz_last_notices[notice_type];
}
messages.removeAt(0, to_remove);
@ -1423,9 +1661,15 @@ FFZ.prototype._modify_room = function(room) {
var msg = this.ffzPending[i];
if ( msg.removed ) {
// Don't keep this message ID around.
var msg_id = msg && msg.tags && msg.tags.id;
var msg_id = msg && msg.tags && msg.tags.id,
notice_type = msg && msg.tags && msg.tags['msg-id'];
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
delete this.ffz_ids[msg_id];
if ( notice_type && this.ffz_last_notices && this.ffz_last_notices[notice_type] === msg )
delete this.ffz_last_notices[notice_type];
continue;
}
@ -1470,7 +1714,24 @@ FFZ.prototype._modify_room = function(room) {
if ( (msg.msgId === 'timeout_success' || msg.msgId === 'ban_success') && this.ffzShouldDisplayNotice() )
return;
return this._super(msg);
f.log("Notification", msg);
if ( ! msg.tags )
msg.tags = {};
if ( ! msg.tags['msg-id'] )
msg.tags['msg-id'] = msg.msgId;
if ( ! msg.style )
msg.style = 'admin';
if ( this.ffz_waiting_notices && this.ffz_waiting_notices[msg.msgId]) {
msg.has_owner = true;
msg.message += ' (By: ' + this.ffz_waiting_notices[msg.msgId] + ')';
delete this.ffz_waiting_notices[msg.msgId];
}
return this.addMessage(msg);
}
},
@ -1481,7 +1742,8 @@ FFZ.prototype._modify_room = function(room) {
addMessage: function(msg) {
if ( msg ) {
var is_resub = msg.tags && msg.tags['msg-id'] === 'resub',
var notice_type = msg.tags && msg.tags['msg-id'],
is_resub = notice_type === 'resub',
room_id = this.get('id'),
msg_id = msg.tags && msg.tags.id;
@ -1510,8 +1772,11 @@ FFZ.prototype._modify_room = function(room) {
var is_whisper = msg.style === 'whisper';
// Ignore whispers if conversations are enabled.
if ( is_whisper && utils.ember_lookup('controller:application').get('isConversationsEnabled') )
return;
if ( is_whisper ) {
var conv_enabled = utils.ember_lookup('controller:application').get('isConversationsEnabled');
if ( conv_enabled || (!conv_enabled && f.settings.hide_whispers_in_embedded_chat) )
return;
}
if ( ! is_whisper )
msg.room = room_id;
@ -1638,6 +1903,12 @@ FFZ.prototype._modify_room = function(room) {
ids[msg_id] = msg;
}
// If this is a notice, store that this is the last of its type.
if ( notice_type ) {
var ids = this.ffz_last_notices = this.ffz_last_notices || {};
ids[notice_type] = msg;
}
// Report this message to the dashboard.
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, "*"); //location.protocol + "//www.twitch.tv/");

View file

@ -185,8 +185,8 @@ FFZ.prototype.setup_sidebar = function() {
}
// Sidebar Followed Games
var GamesFollowing = utils.ember_lookup('controller:games-following'),
f = this;
var f = this,
GamesFollowing = utils.ember_lookup('controller:games-following');
if ( GamesFollowing ) {
this.log("Hooking the Ember games-following controller.");

View file

@ -86,7 +86,11 @@ FFZ.prototype.modify_viewer_list = function(component) {
first_user = false;
}
viewers.push({chatter: FFZ.get_capitalization(data[x])});
var display_name = FFZ.get_capitalization(data[x]);
if ( display_name.trim().toLowerCase() !== data[x] )
display_name = data[x];
viewers.push({chatter: display_name});
}
}

View file

@ -239,6 +239,43 @@ FFZ.prototype.setup_bttv = function(delay) {
}
};
// Tab Completion
var original_emotes = BC.emotes;
BC.emotes = function() {
var output = original_emotes(),
user = f.get_user(),
room_id = BetterTTV.getChannel(),
ffz_sets = f.getEmotes(user && user.login, room_id);
for(var i=0; i < ffz_sets.length; i++) {
var emote_set = f.emote_sets[ffz_sets[i]];
if ( ! emote_set )
continue;
var set_name = (emote_set.source || "FFZ") + " " + (emote_set.title || "Global") + " Emotes",
set_icon = emote_set.icon || (emote_set.hasOwnProperty('source_ext') && f._apis[emote_set.source_ext] && f._apis[emote_set.source_ext].icon) || '//cdn.frankerfacez.com/script/devicon.png';
for(var emote_id in emote_set.emoticons) {
var emote = emote_set.emoticons[emote_id];
if ( ! emote.hidden && emote.name ) {
output.push({
text: emote.name,
channel: set_name,
badge: set_icon,
url: emote.urls[1]
});
}
}
}
f.log("BTTV Emotes", output);
return output;
}
// Emoji!
var parse_emoji = function(token) {
var setting = f.settings.parse_emoji,

240
src/less/dark-clips.less Normal file
View file

@ -0,0 +1,240 @@
/* Colors */
@fg-color: #a49ab5;
@bg-color: #101010;
@link-color: #a68ed2;
@remove-link-color: #fc3636;
@hollow-button-color: darken(@link-color, 5%); //#9d8dba;
@bright-bg: lighten(@bg-color, 15%);
@bright-fg: lighten(@fg-color, 15%);
@nav-bg-color: #191919;
.ffz-dark {
html&, body {
background-color: @bg-color;
color: @fg-color;
}
a {
color: @link-color;
&:hover, &:focus {
color: lighten(@link-color, 10%);
}
}
.sub-text { color: darken(@fg-color, 10%) }
// Navigation
.nav {
background-color: @nav-bg-color;
border-bottom-color: lighten(@nav-bg-color, 10%);
}
.svg-logo_twitch, .clips-nav__logo { fill: white }
// Buttons
.button {
&, &:hover, &:focus {
color: #fff;
}
figure img, figure svg {
fill: @link-color;
}
}
.button--hollow {
box-shadow: inset 0 0 0 1px fade(@link-color, 50%);
color: @hollow-button-color;
&:hover, &:focus {
background-color: fade(@link-color, 10%);
color: @hollow-button-color;
}
}
.button--hollow.button--dropmenu {
&:after {
}
}
// Balloons
.balloon {
background-color: lighten(@bg-color, 10%);
color: #ccc;
box-shadow: 0 0 0 1px fade(white, 20%), 0 1px 1px fade(white, 5%);
&:after {
background-color: lighten(@bg-color, 10%);
}
&--fancy {
color: lighten(@link-color, 5%);
box-shadow: 0 0 0 1px fade(white, 10%);
}
&--left:after { box-shadow: 1px -1px 0 fade(white, 20%) }
&--right:after { box-shadow: -1px 1px 0 fade(white, 20%) }
&--up:after { box-shadow: 1px 1px 0 fade(white, 20%) }
&--down:after { box-shadow: -1px -1px 0 fade(white, 20%) }
&--cols .balloon__list ~ .balloon__list { box-shadow: -1px 0 0 #202021 }
&__stroke { border-bottom-color: fade(white, 10%) }
.balloon__link {
color: @link-color !important;
&:hover {
background-color: @link-color !important;
color: @bg-color !important;
}
&.request-removal-link,
&.clip-remove-link {
color: @remove-link-color !important;
&:hover {
background-color: @remove-link-color !important;
color: white !important;
}
}
}
}
// Modal Content
.modal {
&__content {
background-color: @bg-color;
box-shadow: 0 1px 1px fade(white, 10%);
}
&-title {
color: @bright-fg;
}
&-body-text {
color: @fg-color;
}
}
// Table
.table {
background-color: @bg-color;
box-shadow: 0 2px 4px 0 fade(white, 20%);
&__row {
background-color: @bg-color;
color: @fg-color;
&:nth-child(even) {
background-color: lighten(@bg-color, 5%);
}
&:hover {
background-color: lighten(@bg-color, 10%);
}
&--no-hover:hover { background-color: @bg-color }
}
&__header {
background-color: lighten(@bg-color, 5%)
}
&__cell {
border-top-color: @bright-bg;
border-bottom-color: @bright-bg;
color: @fg-color;
}
&__cell--header {
border-bottom-color: @bright-bg;
color: @bright-fg;
}
&__zero-title {
color: @bright-fg;
}
&__zero-body {
color: @fg-color;
}
}
// Tabs
.tabs {
box-shadow: inset 0 -1px 0 #202021;
&__item {
box-shadow: inset 0 -1px 0 #202021;
& > a { color: @link-color }
&:hover, &--active {
box-shadow: inset 0 -1px 0 @link-color;
& > a {
color: @bright-fg;
}
}
}
}
.loading-spinner {
border-color: fade(white, 18%);
border-left-color: @bright-fg;
}
.expand-button__border,
.ldboard-head {
border-bottom-color: #3e3d40;
}
// Previewer
.ld-previewer__iframe, .previewer__pane {
background-color: @bg-color;
}
.previewer__close {
fill: @link-color;
&:hover, &:focus {
fill: lighten(@link-color, 10%);
}
}
// Labels
.label {
color: @bright-fg;
}
// Input
select,
.input--text {
background-color: @bg-color;
color: @bright-fg;
border-color: @bright-bg;
&:focus {
border-color: lighten(@bright-bg, 15%);
}
}
}

View file

@ -0,0 +1,5 @@
.ffz-darken-clips {
position: fixed;
bottom: 10px;
left: 10px;
}

View file

@ -34,7 +34,7 @@ FFZ.msg_commands = {};
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 270,
major: 3, minor: 5, revision: 283,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -241,22 +241,20 @@ FFZ.prototype.initialize = function(increment, delay) {
return this.log("Found banned sub-domain. Not initializing.");
// Check for the player
if ( location.hostname === 'player.twitch.tv' ) {
this.init_player(delay);
return;
}
if ( location.hostname === 'player.twitch.tv' )
return this.init_player(delay);
// Clips~
if ( location.hostname === 'clips.twitch.tv' )
return this.init_clips(delay);
// Check for special non-ember pages.
if ( /^\/(?:$|search$|team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) ) {
this.init_normal(delay);
return;
}
if ( /^\/(?:$|search$|team\/|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) )
return this.init_normal(delay);
// Check for the dashboard.
if ( /\/[^\/]+\/dashboard/.test(location.pathname) && !/bookmarks$/.test(location.pathname) ) {
this.init_dashboard(delay);
return;
}
if ( /\/[^\/]+\/dashboard/.test(location.pathname) && !/bookmarks$/.test(location.pathname) )
return this.init_dashboard(delay);
var loaded = FFZ.utils.ember_resolve('model:room');
if ( !loaded ) {
@ -273,6 +271,32 @@ FFZ.prototype.initialize = function(increment, delay) {
}
FFZ.prototype.init_clips = function(delay) {
var start = (window.performance && performance.now) ? performance.now() : Date.now();
this.log("Found Twitch Clips after " + (delay||0) + " ms at: " + location);
this.log("Initializing FrankerFaceZ version " + FFZ.version_info);
this.users = {};
this.is_dashboard = false;
this.embed_in_dash = false;
this.is_clips = true;
try {
this.embed_in_clips = window.top !== window && window.top.location.hostname === 'clips.twitch.tv';
} catch(err) { this.embed_in_clips = false; }
this.load_settings();
this.setup_dark();
this.setup_css();
this.add_clips_darken_button();
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
duration = end - start;
this.log("Initialization complete in " + duration + "ms");
}
FFZ.prototype.init_player = function(delay) {
var start = (window.performance && performance.now) ? performance.now() : Date.now();
this.log("Found Twitch Player after " + (delay||0) + " ms at: " + location);
@ -415,8 +439,24 @@ FFZ.prototype.init_ember = function(delay) {
Settings.reopen({settings: Ember.computed.alias('model')});
// Initialize all the modules.
// Settings are important.
this.load_settings();
// Is debug mode enabled? Scratch that, everyone gets error handlers!
if ( true ) { //this.settings.developer_mode ) {
// Set up an error listener for RSVP.
var f = this;
if ( Ember.RSVP && Ember.RSVP.on )
Ember.RSVP.on('error', function(error) {
f.error("There was an error within an Ember RSVP.", error);
});
Ember.onerror = function(error) {
f.error("There was an unknown error within Ember.", error);
}
}
// Set up all the everything.
this.setup_ember_wrapper();
// Start this early, for quick loading.

View file

@ -67,6 +67,9 @@ FFZ.prototype.load_settings = function() {
this.settings.del = this._setting_del.bind(this);
this.settings.load = this._setting_load.bind(this);
this.settings.get_twitch = this._setting_get_twitch.bind(this);
var found_settings = false;
for(var key in FFZ.settings_info) {
@ -811,6 +814,11 @@ FFZ.prototype._setting_get = function(key) {
return this.settings[key];
}
FFZ.prototype._setting_get_twitch = function(key) {
var Settings = utils.ember_lookup('controller:settings');
return Settings && Settings.get('settings.' + key);
}
FFZ.prototype._setting_set = function(key, val, suppress_log) {
var info = FFZ.settings_info[key],

View file

@ -289,6 +289,46 @@ FFZ.prototype.setup_tokenization = function() {
}
// ------------------------
// Display Name Formatting
// ------------------------
FFZ.prototype.format_display_name = function(display_name, user_id, disable_alias, disable_intl, disable_html) {
var setting = this.settings.username_display,
alias = this.aliases[user_id],
name_matches = ! display_name || display_name.trim().toLowerCase() === user_id,
tooltip,
display_name;
if ( setting === 0 )
display_name = user_id;
else if ( setting === 1 )
display_name = name_matches ? (display_name || (user_id && user_id.capitalize())) : user_id;
else {
display_name = utils.sanitize(display_name || (user_id && user_id.capitalize()));
if ( ! disable_intl && setting === 3 && ! name_matches )
display_name += disable_html ? '(' + user_id + ')' : ' <span class="intl-login">(' + user_id + ')</span>';
else if ( ((disable_intl && setting === 3) || setting === 4) && ! name_matches )
tooltip = user_id;
}
if ( ! disable_alias && alias ) {
if ( display_name )
tooltip = display_name + (tooltip ? ' (' + tooltip + ')' : '');
display_name = utils.sanitize(alias);
}
return [display_name, tooltip];
}
// ---------------------
// Twitch Emote Data
// ---------------------
@ -505,9 +545,14 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens);
if ( user && user.login && helpers && helpers.mentionizeMessage )
if ( user && user.login && helpers && helpers.mentionizeMessage ) {
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
// Display names~~
if ( ! from_me && user.name && user.name.trim().toLowerCase() !== user.login )
tokens = helpers.mentionizeMessage(tokens, user.name, from_me);
}
if ( helpers && helpers.emoticonizeMessage && emotes && this.settings.parse_emoticons )
tokens = helpers.emoticonizeMessage(tokens, emotes);
@ -546,14 +591,20 @@ FFZ.prototype.tokenize_vod_line = function(msgObject, delete_links) {
user = this.get_user(),
from_me = user && from_user === user.login,
emotes = msgObject.get('tags.emotes'),
tokens = [msg];
if ( helpers && helpers.linkifyMessage )
tokens = helpers.linkifyMessage(tokens, delete_links);
if ( user && user.login && helpers && helpers.mentionizeMessage )
if ( user && user.login && helpers && helpers.mentionizeMessage ) {
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
// Display names~~
if ( ! from_me && user.name && user.name.trim().toLowerCase() !== user.login )
tokens = helpers.mentionizeMessage(tokens, user.name, from_me);
}
if ( helpers && helpers.emoticonizeMessage && emotes && this.settings.parse_emoticons )
tokens = helpers.emoticonizeMessage(tokens, emotes);
@ -620,9 +671,14 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
}
if ( user && user.login && helpers && helpers.mentionizeMessage )
if ( user && user.login && helpers && helpers.mentionizeMessage ) {
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
// Display names~~
if ( ! from_me && user.name && user.name.trim().toLowerCase() !== user.login )
tokens = helpers.mentionizeMessage(tokens, user.name, from_me);
}
if ( helpers && helpers.emoticonizeMessage && this.settings.parse_emoticons )
tokens = helpers.emoticonizeMessage(tokens, emotes);
@ -754,8 +810,13 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji)
if ( helpers && helpers.mentionizeMessage ) {
var u = this.get_user();
if ( u && u.login )
if ( u && u.login ) {
message = helpers.mentionizeMessage(message, u.login, user === u.login);
// Display names~~
if ( ! user === u.login && u.name && u.name.trim().toLowerCase() !== u.login )
tokens = helpers.mentionizeMessage(tokens, u.name, from_me);
}
}
if ( ! no_emotes && this.settings.parse_emoticons && this.settings.parse_emoticons !== 2 )

View file

@ -135,7 +135,7 @@ FFZ.settings_info.dark_twitch = {
if ( this.has_bttv )
return;
document.body.classList.toggle("ffz-dark", val);
(this.is_clips ? document.querySelector('html') : document.body).classList.toggle("ffz-dark", val);
var Settings = utils.ember_lookup('controller:settings'),
settings = Settings && Settings.get('settings');
@ -148,7 +148,7 @@ FFZ.settings_info.dark_twitch = {
settings && settings.set('darkMode', this.settings.twitch_chat_dark);
// Try coloring chat replay
jQuery('.chatReplay').toggleClass('dark', val || false);
window.jQuery && jQuery('.chatReplay').toggleClass('dark', val || false);
//jQuery('.rechat-chat-line').parents('.chat-container').toggleClass('dark', val || this.settings.twitch_chat_dark);
}
};
@ -198,7 +198,8 @@ FFZ.prototype.setup_dark = function() {
if ( this.has_bttv )
return;
document.body.classList.toggle("ffz-dark", this.settings.dark_twitch);
(this.is_clips ? document.querySelector('html') : document.body).classList.toggle('ffz-dark', this.settings.dark_twitch);
if ( ! this.settings.dark_twitch )
return;
@ -226,6 +227,24 @@ FFZ.prototype._load_dark_css = function() {
s.id = "ffz-dark-css";
s.setAttribute('rel', 'stylesheet');
s.setAttribute('href', constants.SERVER + "script/dark" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
s.setAttribute('href', constants.SERVER + "script/dark" + (this.is_clips ? '-clips' : '') + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
document.head.appendChild(s);
}
FFZ.prototype.add_clips_darken_button = function() {
if ( this.embed_in_clips )
return;
this.log("Adding Clips Darken button.");
var f = this,
btn = utils.createElement('a', 'button button--hollow ffz-darken-clips', f.settings.dark_twitch ? 'Light' : 'Dark');
btn.addEventListener('click', function() {
f.settings.set('dark_twitch', ! f.settings.dark_twitch);
btn.textContent = f.settings.dark_twitch ? 'Light' : 'Dark';
});
document.body.appendChild(btn);
}

View file

@ -299,20 +299,22 @@ FFZ.prototype._build_following_button = function(cont, channel_id) {
return this.log("Ignoring Invalid Channel: " + utils.sanitize(channel_id));
var f = this,
btn = utils.createElement('button', 'follow-button button'),
btn = utils.createElement('button', 'follow-button button html-tooltip'),
noti = utils.createElement('a', 'toggle-notification-menu js-toggle-notification-menu'),
noti_c = utils.createElement('div', 'notification-controls v2 hidden', noti),
display_name,
tooltip,
following = false,
notifications = false,
update = function() {
btn.classList.toggle('is-following', following);
btn.classList.toggle('button--status', following);
btn.title = (following ? "Unf" : "F") + "ollow " + utils.sanitize(display_name);
btn.innerHTML = (following ? "" : "Follow ") + utils.sanitize(display_name);
btn.title = tooltip ? (following ? "Unf" : "F") + "ollow " + tooltip : '';
btn.innerHTML = (following ? "" : "Follow ") + display_name;
noti_c.classList.toggle('hidden', !following);
},
@ -353,7 +355,9 @@ FFZ.prototype._build_following_button = function(cont, channel_id) {
},
on_name = function(cap_name) {
display_name = cap_name || channel_id;
var results = f.format_display_name(cap_name, channel_id, true, true);
display_name = results[0];
tooltip = results[1];
update();
};
@ -404,9 +408,7 @@ FFZ.prototype._build_following_button = function(cont, channel_id) {
return false;
});
display_name = FFZ.get_capitalization(channel_id, on_name);
update();
on_name(FFZ.get_capitalization(channel_id, on_name));
setTimeout(check_following, Math.random()*5000);
@ -429,7 +431,9 @@ FFZ.prototype._build_following_popup = function(container, channel_id, notificat
this._popup_allow_parent = true;
this._popup_parent = container;
out = '<div class="header">You are following ' + FFZ.get_capitalization(channel_id) + '</div>';
var results = this.format_display_name(FFZ.get_capitalization(channel_id), channel_id, true);
out = '<div class="header">You are following <span' + (results[1] ? ' class="html-tooltip" title="' + utils.quote_attr(results[1]) + '"' : '') + '>' + results[0] + '</span></div>';
out += '<p class="clearfix">';
out += '<a class="switch' + (notifications ? ' active' : '') + '"><span></span></a>';
out += '<span class="switch-label">Notify me when the broadcaster goes live</span>';

View file

@ -80,15 +80,10 @@ FFZ.prototype.setup_menu = function() {
if ( ! menu )
return;
var header = document.createElement('div'),
content = document.createElement('div'),
var header = utils.createElement('div', 'list-header', 'FrankerFaceZ'),
content = utils.createElement('div', 'chat-menu-content'),
p, cb, a;
header.className = 'list-header';
header.innerHTML = 'FrankerFaceZ';
content.className = 'chat-menu-content';
// Dark Twitch
p = document.createElement('p');
p.className = 'no-bttv';

View file

@ -504,7 +504,7 @@ FFZ.menu_pages.myemotes = {
menu_id = set.hasOwnProperty('source_ext') ? 'ffz-ext-' + set.source_ext + '-' + set.source_id : 'ffz-' + set.id,
favorites = this.settings.favorite_emotes[menu_id] || [],
c = 0,
icon = set.icon || (set.hasOwnProperty('source_ext') && '//cdn.frankerfacez.com/emoji/tw-1f4ac.svg') || '//cdn.frankerfacez.com/script/devicon.png',
icon = set.icon || (set.hasOwnProperty('source_ext') && this._apis[set.source_ext] && this._apis[set.source_ext].icon) || '//cdn.frankerfacez.com/script/devicon.png',
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf(menu_id) === -1;
menu.className = 'emoticon-grid';

View file

@ -11,7 +11,7 @@ FFZ.prototype.setup_css = function() {
var s = this._main_style = document.createElement('link');
s.id = "ffz-main-css";
s.setAttribute('rel', 'stylesheet');
s.setAttribute('href', constants.SERVER + "script/style" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
s.setAttribute('href', constants.SERVER + "script/style" + (this.is_clips ? '-clips' : '') + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
document.head.appendChild(s);
this.log("Readying toggleable styles.");

127
style.css
View file

@ -39,22 +39,24 @@ body:not(.ffz-show-bits-tags) .ember-chat .chat-messages.bits-tags__offset {
}
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + .ffz-ui-toggle svg,
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + script + .ffz-ui-toggle svg
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + div + .ffz-ui-toggle svg
{
height: 14px;
width: 18px;
}
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + .ffz-ui-toggle,
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + script + .ffz-ui-toggle {
body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emoticon-selector-toggle + div + .ffz-ui-toggle {
height: 14px;
width: 18px;
top: 28px;
right: 6px;
}
.ffz-ui-toggle svg.svg-emoticons path { fill: rgba(0,0,0,0.2); }
.ffz-ui-toggle:hover svg.svg-emoticons path { fill: rgba(0,0,0,0.5); }
.carousel__item .card__img .overlay_info.live svg path,
.streams .stream .content .overlay_info.live svg path,
.videos .video .content .overlay_info.live svg path { fill: #ff2020; }
@ -1257,7 +1259,8 @@ img.channel_background[src="null"] { display: none; }
}
.ember-chat .ffz-moderation-card .close-button {
right: 7px;
right: 7px;
width: 20px;
}
.ember-chat .ffz-moderation-card .extra-interface {
@ -1268,13 +1271,17 @@ img.channel_background[src="null"] { display: none; }
margin-top: -10px;
}
.ffz-moderation-card.ffz-has-info h3.name {
margin-top: -2px;
.ffz-moderation-card.ffz-has-info h4.name {
/*margin-top: -2px;*/
margin-bottom: 0;
padding-top: 0;
white-space: nowrap;
}
.ffz-moderation-card.ffz-has-info .name .intl-login {
margin: -2px 0 -4px;
}
.ffz-moderation-card .info {
float: none;
position: relative;
@ -1359,10 +1366,10 @@ img.channel_background[src="null"] { display: none; }
border-top: none;
}
.ffz-moderation-card h3.name { display: inline-block; }
.ffz-moderation-card h4.name { display: inline-block; }
.ffz-moderation-card .info,
.ffz-moderation-card h3.name {
.ffz-moderation-card h4.name {
text-shadow: black 0 0 5px;
}
@ -1403,7 +1410,7 @@ img.channel_background[src="null"] { display: none; }
background-color: #232323;
}
.moderation-card h3.name a { color: #fff !important; }
.moderation-card h4.name a { color: #fff !important; }
/* purge icon */
@ -1799,7 +1806,7 @@ th.ffz-row-switch {
.ffz-room-row td > span:empty,
.ffz-chat-tab span:empty { display: none; }
.ffz-room-row td > span,
.ffz-room-row td > span:not(.intl-login),
.ffz-chat-tab span {
padding: 0 4px;
display: inline-block;
@ -1815,7 +1822,7 @@ th.ffz-row-switch {
position: relative;
}
.ffz-room-row td > span {
.ffz-room-row td > span:not(.intl-login) {
line-height: 16px;
margin: 5px 0;
}
@ -1860,7 +1867,7 @@ th.ffz-row-switch {
color: #B9A3E3;
}
.ffz-dark .ffz-room-row td > span,
.ffz-dark .ffz-room-row td > span:not(.intl-login),
.app-main.theatre .ffz-chat-tab span,
.chat-container.dark .ffz-chat-tab span,
.ember-chat-container.dark .ffz-chat-tab span {
@ -2186,11 +2193,11 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
.ffz-no-blue .theatre .conversation-system-message,
.ffz-no-blue.ffz-dark .conversation-system-message,
.ffz-no-blue .theatre .bits-card,
.ffz-no-blue .app-main.theatre .bits-card,
.ffz-no-blue .dark .bits-card,
.ffz-no-blue .force-dark .bits-card,
.ffz-no-blue .theatre .bits-card--standard,
.ffz-no-blue .app-main.theatre .bits-card--standard,
.ffz-no-blue .dark .bits-card--standard,
.ffz-no-blue .force-dark .bits-card--standard,
@ -2256,6 +2263,10 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
background-color: #191919;
}
.ffz-no-blue .app-main.theatre .bits-footer,
.ffz-no-blue .dark .bits-footer,
.ffz-no-blue .force-dark .bits-footer,
.ffz-no-blue .theatre .conversation-input-bar textarea,
.ffz-no-blue .theatre input.text,
.ffz-no-blue .theatre input.countries-input,
@ -2320,13 +2331,13 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
border-bottom-color: #323232;
}
.ffz-no-blue .theatre .conversation-header,
.ffz-no-blue.ffz-dark .conversation-header,
.ffz-no-blue .theatre .convoHeader,
.ffz-no-blue.ffz-dark .convoHeader,
.ffz-no-blue .theatre .conversations-list .search-divider,
.ffz-no-blue.ffz-dark .conversations-list .search-divider,
.ffz-no-blue.ffz-dark .conversations-list .conversations-list-header,
.ffz-no-blue .theatre .conversation-window.has-focus .conversation-header,
.ffz-no-blue.ffz-dark .conversation-window.has-focus .conversation-header,
.ffz-no-blue .theatre .conversation-window.has-focus .convoHeader,
.ffz-no-blue.ffz-dark .conversation-window.has-focus .convoHeader,
.ffz-no-blue .theatre .conversations-list .conversations-list-item:hover,
.ffz-no-blue.ffz-dark .conversations-list .conversations-list-item:hover {
background-color: #121212;
@ -2418,7 +2429,7 @@ li[data-name="following"] a {
max-height: 90px;
}
/* Player Captions *\/
/* Player Captions */
.ffz-dark .player-modal__content,
.theatre .player-modal__content {
@ -2457,7 +2468,7 @@ li[data-name="following"] a {
}
.ffz-dark label.cc-edge-palette__square,
.theatre label.cc-edge-palette__square { color: #fff }*/
.theatre label.cc-edge-palette__square { color: #fff }
/* Classic Player */
@ -2484,12 +2495,12 @@ li[data-name="following"] a {
}
.ffz-classic-player .app-main.theatre .player .player-video,
.ffz-classic-player .player[data-fullscreen="true"] .player-video {
.ffz-classic-player .player[data-isfullscreen="true"] .player-video {
bottom: 0;
}
.ffz-classic-player .app-main.theatre .player .player-controls-bottom,
.ffz-classic-player .player[data-fullscreen="true"] .player-controls-bottom {
.ffz-classic-player .player[data-isfullscreen="true"] .player-controls-bottom {
margin-bottom: -32px;
-webkit-transition: margin-bottom .2s ease-out, padding-bottom .2s ease-out;
transition: margin-bottom .2s ease-out, padding-bottom .2s ease-out;
@ -2497,23 +2508,22 @@ li[data-name="following"] a {
.ffz-classic-player:not(.ffz-top-conversations):not(.ffz-theatre-conversations) .app-main.theatre .player .player-controls-bottom,
.ffz-classic-player:not(.ffz-top-conversations):not(.ffz-theatre-conversations) .app-main.theatre .player[data-controls=true] .player-controls-bottom,
.ffz-classic-player .app-main.theatre .player[data-fullscreen="true"] .player-controls-bottom,
.ffz-classic-player .app-main.theatre .player[data-fullscreen="true"][data-controls=true] .player-controls-bottom {
.ffz-classic-player .app-main.theatre .player[data-isfullscreen="true"] .player-controls-bottom,
.ffz-classic-player .app-main.theatre .player[data-isfullscreen="true"][data-controls=true] .player-controls-bottom {
padding-bottom: 0;
}
.ffz-classic-player .app-main.theatre .player.player-isvod .player-controls-bottom,
.ffz-classic-player .player.player-isvod[data-fullscreen="true"] .player-controls-bottom {
.ffz-classic-player .player.player-isvod[data-isfullscreen="true"] .player-controls-bottom {
margin-bottom: -36px;
}
.ffz-classic-player .app-main.theatre .player-column:hover .player .player-controls-bottom,
.ffz-classic-player .app-main.theatre .player-column:focus .player .player-controls-bottom,
.ffz-classic-player .player[data-fullscreen="true"][data-controls="true"] .player-controls-bottom {
.ffz-classic-player .app-main.theatre .player-column .player[data-controls=true] .player-controls-bottom,
.ffz-classic-player .player[data-isfullscreen="true"][data-controls="true"] .player-controls-bottom {
margin-bottom: 0;
}
.ffz-classic-player:not(.ffz-top-conversations):not(.ffz-theatre-conversations) .app-main.theatre .player-column:hover .player[data-fullscreen="false"] .player-controls-bottom {
.ffz-classic-player:not(.ffz-top-conversations):not(.ffz-theatre-conversations) .app-main.theatre .player[data-isfullscreen=false][data-controls=true] .player-controls-bottom {
padding-bottom: 40px;
}
@ -2569,8 +2579,14 @@ li[data-name="following"] a {
.item .meta .title a a,
.item .meta .title a img { pointer-events: none }
.ffz-directory-logo .card__body,
.ffz-directory-logo .meta {
padding-top: 5px;
}
.ffz-directory-logo .card__title,
.ffz-directory-logo .meta p.title {
padding-top: 4px;
padding-top: 4px;
min-height: 24px;
}
@ -2656,11 +2672,9 @@ body:not(.ffz-bttv) .conversation-window .ignore-cta:not(.hidden) + .conversatio
.conversation-window.collapsed .ignore-cta,
.conversation-chat-line.action .colon,
.conversation-input-bar .emoticon-selector .tabs,
.conversation-preview-line .badges,
body.ffz-conv-title-clickable .conversation-header span.conversation-header-name,
body:not(.ffz-conv-title-clickable) .conversation-header a.conversation-header-name { display:none }
.conversation-preview-line .badges { display:none }
.conversation-window.has-unread .conversation-header .conversation-header-name { color: #fff !important; }
.conversation-window.has-unread .convoHeader .username { color: #fff !important; }
/* Top Conversations */
@ -2680,7 +2694,7 @@ body:not(.ffz-top-conversations) .conversations-list-bottom-bar {
top: 0px;
}
.ffz-top-conversations .conversation-window.collapsed .conversation-header {
.ffz-top-conversations .conversation-window.collapsed .convoHeader {
box-shadow: none;
}
@ -2758,13 +2772,15 @@ body:not(.ffz-top-conversations) .conversations-list-bottom-bar {
.user.item { position: relative; }
.card__img .overlay_info.length,
.user.item .overlay_info.length {
position: absolute;
top: 5px;
right: 25px;
right: 15px;
z-index: 1;
display: inline-block;
cursor: pointer;
padding: 0 5px;
font-size: 11px;
@ -2775,18 +2791,20 @@ body:not(.ffz-top-conversations) .conversations-list-bottom-bar {
opacity: 0.75;
}
.card__img .overlay_info.length { right: 5px }
.card__img .overlay_info.length svg,
.user.item .overlay_info.length svg {
float: left;
margin: 3px 5px 3px 0;
}
.card__img .overlay_info.length svg path,
.user.item .actions .follow svg path,
.user.item .overlay_info.length svg path { fill: #fff; }
.user.item .actions {
margin-top: -15px;
margin-bottom: 20px;
margin-right: 20px;
margin-top: 5px;
display: flex;
flex-wrap: nowrap;
}
@ -2901,10 +2919,12 @@ body:not(.ffz-creative-showcase) .ct-spotlight-container { display: none; }
/* Banned and Spoiler Games */
body:not([data-current-path^="directory.csgo"]):not([data-current-path^="directory.game"]):not([data-current-path^="directory.creative"]) .ffz-game-banned { display: none }
body:not([data-current-path^="directory.csgo"]):not([data-current-path^="directory.game"]):not([data-current-path^="directory.creative"]) .ffz-game-banned { display: none !important }
.ffz-game-spoilered .card__img a:not(.card__boxpin) img,
.ffz-game-spoilered .thumb .cap img { visibility: hidden }
.ffz-game-spoilered .card__img a:not(.card__boxpin):after,
.ffz-game-spoilered .thumb .cap:after {
position: absolute;
top: 0; left: 0;
@ -2986,7 +3006,7 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
/* Hide Outlines */
.conversation-header,
.convoHeader,
.conversations-list-icon,
.toggle-notification-menu {
outline: none !important
@ -2997,6 +3017,10 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
.badges .badge { background-size: 18px 18px }
.convoHeader .badges .badge {
background-size: 14px 14px;
}
/*.badges .badge {
height: 18px;
min-width: 18px;
@ -3145,6 +3169,29 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
content: attr(data-amount);
}
.theatre .bits-purchase__header,
.dark .bits-purchase__header,
.force-dark .bits-purchase__header {
color: #acacbf;
}
.theatre .bits-card,
.app-main.theatre .chat-container .bits-card, /* Twitch's CSS is retarded */
.dark .bits-card,
.force-dark .bits-card,
.theatre .bits-purchase__row,
.dark .bits-purchase__row,
.force-dark .bits-purchase__row,
.theatre .bits-footer,
.dark .bits-footer,
.force-dark .bits-footer {
border-color: rgba(255,255,255,0.2);
color: #ccc;
}
/* New Chat Formatting */
.ember-chat .chat-messages .timestamp { margin-right: 0; }