mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
3.5.78 to 3.5.83. It's been so long since I've commited. I'm a bad person. Added grouping for hosted channels, and a menu to select which channel you visit. Added weights for selecting which socket server to use. Added server time offset calculations. Refactored a LOT of how chat badges and chat line rendering and history processing works behind the scenes. Removed a ton of duplicate code. Added basic adjacent chat message lookups. Need better server support.
This commit is contained in:
parent
a050063c81
commit
c167a8b626
28 changed files with 1501 additions and 538 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,6 +6,7 @@ Extension Building
|
|||
*.iml
|
||||
script.js
|
||||
script.min.js
|
||||
*.min.css
|
||||
credentials.json
|
||||
|
||||
/socketserver/cmd/socketserver/socketserver
|
||||
|
|
10
dark.css
10
dark.css
|
@ -142,6 +142,7 @@
|
|||
.ffz-dark .whatisthis,
|
||||
.ffz-dark .ui-menu,
|
||||
.ffz-dark .dropmenu,
|
||||
.ffz-dark .sort-contain .sort-options,
|
||||
.ffz-dark .top-dropdown,
|
||||
.ffz-dark form.js-new_panel_form,
|
||||
.ffz-dark .js-new_panel_btn,
|
||||
|
@ -902,6 +903,15 @@
|
|||
|
||||
/* Conversations */
|
||||
|
||||
.ffz-dark .ignore-cta {
|
||||
background-color: #333;
|
||||
box-shadow: 0 3px 0 #000;
|
||||
}
|
||||
|
||||
.ffz-dark .ignore-cta .conversation-system-message {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-toggle svg path {
|
||||
fill: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
|
44
gulpfile.js
44
gulpfile.js
|
@ -24,6 +24,7 @@ var ftp = require('vinyl-ftp'),
|
|||
|
||||
// Server Dependencies
|
||||
var http = require("http"),
|
||||
//https = require("https"),
|
||||
path = require("path"),
|
||||
request = require("request"),
|
||||
url = require("url");
|
||||
|
@ -87,12 +88,10 @@ gulp.task('scripts', ['styles'], function() {
|
|||
.pipe(header('(function(window) {'))
|
||||
.pipe(footer(';window.ffz = new FrankerFaceZ()}(window));'))
|
||||
.pipe(gulp.dest(__dirname))
|
||||
.pipe(uglify())
|
||||
.pipe(rename('script.min.js'))
|
||||
.pipe(gulp.dest(__dirname))
|
||||
.on('error', util.log);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('watch', ['default', 'server'], function() {
|
||||
return gulp.watch('src/**/*', ['default']);
|
||||
});
|
||||
|
@ -102,7 +101,28 @@ gulp.task('default', ['scripts']);
|
|||
|
||||
// Deploy
|
||||
|
||||
gulp.task('upload', ['default'], function() {
|
||||
gulp.task('minify_script', ['scripts'], function() {
|
||||
return gulp.src(['script.js'])
|
||||
.pipe(uglify())
|
||||
.pipe(rename('script.min.js'))
|
||||
.pipe(gulp.dest(__dirname))
|
||||
.on('error', util.log);
|
||||
});
|
||||
|
||||
gulp.task('minify_style', function() {
|
||||
return gulp.src(['style.css', 'dark.css'])
|
||||
.pipe(minifyCss())
|
||||
.pipe(rename(function(path) {
|
||||
path.basename += '.min';
|
||||
}))
|
||||
.pipe(gulp.dest(__dirname))
|
||||
.on('error', util.log);
|
||||
});
|
||||
|
||||
gulp.task('minify', ['minify_script', 'minify_style']);
|
||||
|
||||
|
||||
gulp.task('upload', ['minify'], function() {
|
||||
// Load credentials from an external file.
|
||||
var contents = fs.readFileSync('credentials.json', 'utf8'),
|
||||
cred = JSON.parse(contents);
|
||||
|
@ -117,8 +137,8 @@ gulp.task('upload', ['default'], function() {
|
|||
|
||||
globs = [
|
||||
"script.min.js",
|
||||
"style.css",
|
||||
"dark.css",
|
||||
"style.min.css",
|
||||
"dark.min.css",
|
||||
"changelog.html"
|
||||
];
|
||||
|
||||
|
@ -140,8 +160,8 @@ gulp.task('clear_cache', ['upload'], function(cb) {
|
|||
files = [],
|
||||
globs = [
|
||||
"script.min.js",
|
||||
"style.css",
|
||||
"dark.css",
|
||||
"style.min.css",
|
||||
"dark.min.css",
|
||||
"changelog.html"
|
||||
];
|
||||
|
||||
|
@ -177,7 +197,7 @@ gulp.task('deploy', ['upload', 'clear_cache']);
|
|||
// Server
|
||||
|
||||
gulp.task('server', function() {
|
||||
http.createServer(function(req, res) {
|
||||
var handle_req = function(req, res) {
|
||||
var uri = url.parse(req.url).pathname,
|
||||
lpath = path.join(uri).split(path.sep);
|
||||
|
||||
|
@ -220,6 +240,10 @@ gulp.task('server', function() {
|
|||
fs.createReadStream(file).pipe(res);
|
||||
});
|
||||
|
||||
}).listen(8000, "localhost");
|
||||
};
|
||||
|
||||
http.createServer(handle_req).listen(8000, "localhost");
|
||||
//https.createServer(handle_req).listen(8000, "localhost");
|
||||
|
||||
util.log("[" + util.colors.cyan("HTTP") + "] Listening on Port: " + util.colors.magenta("8000"));
|
||||
});
|
291
src/badges.js
291
src/badges.js
|
@ -1,6 +1,20 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require('./constants'),
|
||||
utils = require('./utils');
|
||||
utils = require('./utils'),
|
||||
|
||||
MOD_BADGES = [
|
||||
['staff', 'staff', 'Staff'],
|
||||
['admin', 'admin', 'Admin'],
|
||||
['global_mod', 'global-moderator', 'Global Moderator'],
|
||||
['mod', 'moderator', 'Moderator']
|
||||
],
|
||||
|
||||
badge_css = function(badge) {
|
||||
var out = ".badges .ffz-badge-" + badge.id + " { background-color: " + badge.color + '; background-image: url("' + badge.image + '"); ' + (badge.extra_css || "") + '}';
|
||||
if ( badge.transparent_image )
|
||||
out += ".badges .badge.alpha.ffz-badge-" + badge.id + ",.ffz-transparent-badges .badges .ffz-badge-" + badge.id + ' { background-image: url("' + badge.transparent_image + '"); }';
|
||||
return out;
|
||||
};
|
||||
|
||||
|
||||
// --------------------
|
||||
|
@ -151,14 +165,99 @@ FFZ.ws_commands.set_badge = function(data) {
|
|||
|
||||
|
||||
// --------------------
|
||||
// Badge CSS
|
||||
// Badge Selection
|
||||
// --------------------
|
||||
|
||||
var badge_css = function(badge) {
|
||||
var out = ".badges .ffz-badge-" + badge.id + " { background-color: " + badge.color + '; background-image: url("' + badge.image + '"); ' + (badge.extra_css || "") + '}';
|
||||
if ( badge.transparent_image )
|
||||
out += ".ffz-transparent-badges .badges .ffz-badge-" + badge.id + ' { background-image: url("' + badge.transparent_image + '"); }';
|
||||
return out;
|
||||
FFZ.prototype.get_badges = function(user, room_id, badges, msg) {
|
||||
var data = this.users[user];
|
||||
if ( ! data || ! data.badges || ! this.settings.show_badges )
|
||||
return badges;
|
||||
|
||||
for(var slot in data.badges) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = this.badges[badge.id] || {},
|
||||
old_badge = badges[slot];
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
visible = visible.bind(this)(room_id, user, msg, badges);
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( old_badge ) {
|
||||
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||
if ( ! replaces )
|
||||
continue;
|
||||
|
||||
old_badge.image = badge.image || full_badge.image;
|
||||
old_badge.klass += ' ffz-badge-replacement';
|
||||
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
|
||||
badges[slot] = {
|
||||
klass: 'ffz-badge-' + badge.id,
|
||||
title: badge.title || full_badge.title,
|
||||
image: badge.image,
|
||||
color: badge.color,
|
||||
extra_css: badge.extra_css
|
||||
};
|
||||
}
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.get_line_badges = function(msg) {
|
||||
var badges = {};
|
||||
|
||||
if ( msg.room && msg.from === msg.room )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else if ( msg.labels )
|
||||
for(var i=0, l = MOD_BADGES.length; i < l; i++) {
|
||||
var mb = MOD_BADGES[i];
|
||||
if ( msg.labels.indexOf(mb[0]) !== -1 ) {
|
||||
badges[0] = {klass: mb[1], title: mb[2]}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( msg.labels.indexOf('subscriber') !== -1 )
|
||||
badges[10] = {klass: 'subscriber', title: 'Subscriber'}
|
||||
if ( msg.labels.indexOf('turbo') !== -1 )
|
||||
badges[15] = {klass: 'turbo', title: 'Turbo'};
|
||||
|
||||
// FFZ Badges
|
||||
return this.get_badges(msg.from, msg.room, badges, msg);
|
||||
}
|
||||
|
||||
|
||||
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 )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else
|
||||
for(var i=0, l = MOD_BADGES.length; i < l; i++) {
|
||||
var mb = MOD_BADGES[i];
|
||||
if ( user_type === mb[0] ) {
|
||||
badges[0] = {klass: mb[1], title: mb[2]};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( has_sub )
|
||||
badges[10] = {klass: 'subscriber', title: 'Subscriber'}
|
||||
if ( has_turbo )
|
||||
badges[15] = {klass: 'turbo', title: 'Turbo'}
|
||||
|
||||
return this.get_badges(user_id, room_id, badges, null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,6 +265,29 @@ var badge_css = function(badge) {
|
|||
// Render Badge
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.render_badges = function(badges) {
|
||||
var out = [];
|
||||
for(var key in badges) {
|
||||
var badge = badges[key],
|
||||
css = badge.image ? 'background-image:url("' + utils.quote_attr(badge.image) + '");' : '';
|
||||
|
||||
if ( badge.color )
|
||||
css += 'background-color:' + badge.color + ';'
|
||||
|
||||
if ( badge.extra_css )
|
||||
css += badge.extra_css;
|
||||
|
||||
out.push('<div class="badge float-left tooltip ' + utils.quote_attr(badge.klass) + '"' + (css ? ' style="' + utils.quote_attr(css) + '"' : '') + ' title="' + utils.quote_attr(badge.title) + '"></div>');
|
||||
}
|
||||
|
||||
return out.join("");
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Extension Support
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.bttv_badges = function(data) {
|
||||
if ( ! this.settings.show_badges )
|
||||
return;
|
||||
|
@ -216,7 +338,7 @@ FFZ.prototype.bttv_badges = function(data) {
|
|||
if ( b.type === full_badge.replaces_type ) {
|
||||
b.type = "ffz-badge-replacement " + b.type;
|
||||
b.description += ", " + (badge.title || full_badge.title) +
|
||||
'" style="background-image: url("' + (badge.image || full_badge.image) + "")";
|
||||
'" style="background-image: url("' + utils.quote_attr(badge.image || full_badge.image) + '")';
|
||||
replaced = true;
|
||||
break;
|
||||
}
|
||||
|
@ -226,14 +348,16 @@ FFZ.prototype.bttv_badges = function(data) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if ( badge.image )
|
||||
style += 'background-image: url("' + badge.image + '"); ';
|
||||
if ( alpha && badge.transparent_image )
|
||||
style += 'background-image: url("' + utils.quote_attr(badge.transparent_image) + '");';
|
||||
else if ( badge.image )
|
||||
style += 'background-image: url("' + utils.quote_attr(badge.image) + '");';
|
||||
|
||||
if ( badge.color && ! alpha )
|
||||
style += 'background-color: ' + badge.color + '; ';
|
||||
style += 'background-color: ' + utils.quote_attr(badge.color) + '; ';
|
||||
|
||||
if ( badge.extra_css )
|
||||
style += badge.extra_css;
|
||||
style += utils.quote_attr(badge.extra_css);
|
||||
|
||||
if ( style )
|
||||
desc += '" style="' + style;
|
||||
|
@ -253,149 +377,6 @@ FFZ.prototype.bttv_badges = function(data) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype.render_badges = function(component, badges) {
|
||||
if ( ! this.settings.show_badges )
|
||||
return badges;
|
||||
|
||||
var user = component.get('msgObject.from') || component.get('message.from.username'),
|
||||
room_id = component.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id');
|
||||
|
||||
return this._render_badges(user, room_id, badges, component);
|
||||
}
|
||||
|
||||
FFZ.prototype._render_badges = function(user, room_id, badges, component) {
|
||||
var data = this.users[user];
|
||||
if ( ! data || ! data.badges )
|
||||
return badges;
|
||||
|
||||
for(var slot in data.badges) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = this.badges[badge.id] || {},
|
||||
old_badge = badges[slot];
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
visible = visible.bind(this)(room_id, user, component, badges);
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( old_badge ) {
|
||||
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||
if ( ! replaces )
|
||||
continue;
|
||||
|
||||
old_badge.image = badge.image || full_badge.image;
|
||||
old_badge.klass += ' ffz-badge-replacement';
|
||||
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
|
||||
badges[slot] = {
|
||||
klass: 'ffz-badge-' + badge.id,
|
||||
title: badge.title || full_badge.title,
|
||||
image: badge.image,
|
||||
color: badge.color,
|
||||
extra_css: badge.extra_css
|
||||
};
|
||||
}
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.render_badge = function(component) {
|
||||
if ( ! this.settings.show_badges )
|
||||
return;
|
||||
|
||||
var user = component.get('msgObject.from'),
|
||||
room_id = App.__container__.lookup('controller:chat').get('currentRoom.id'),
|
||||
badges = component.$('.badges');
|
||||
|
||||
var data = this.users[user];
|
||||
if ( ! data || ! data.badges )
|
||||
return;
|
||||
|
||||
// If we don't have badges, add them.
|
||||
if ( ! badges.length ) {
|
||||
var b_cont = document.createElement('span'),
|
||||
from = component.$('.from');
|
||||
|
||||
b_cont.className = 'badges float-left';
|
||||
|
||||
if ( ! from )
|
||||
return;
|
||||
|
||||
from.before(b_cont);
|
||||
badges = $(b_cont);
|
||||
}
|
||||
|
||||
// Figure out where to place our badge(s).
|
||||
var before = badges.find('.badge').filter(function(i) {
|
||||
var t = this.title.toLowerCase();
|
||||
return t == "subscriber" || t == "turbo";
|
||||
}).first();
|
||||
|
||||
var badges_out = [], reverse = !(!before.length);
|
||||
for ( var slot in data.badges ) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = this.badges[badge.id] || {};
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible == "function" )
|
||||
visible = visible.bind(this)(room_id, user);
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( full_badge.replaces ) {
|
||||
var el = badges[0].querySelector('.badge.' + full_badge.replaces);
|
||||
if ( el ) {
|
||||
el.style.backgroundImage = 'url("' + (badge.image || full_badge.image) + '")';
|
||||
el.classList.add("ffz-badge-replacement");
|
||||
el.title += ", " + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.className = 'badge float-left tooltip ffz-badge-' + badge.id;
|
||||
el.setAttribute('title', badge.title || full_badge.title);
|
||||
|
||||
if ( badge.image )
|
||||
el.style.backgroundImage = 'url("' + badge.image + '")';
|
||||
|
||||
if ( badge.color )
|
||||
el.style.backgroundColor = badge.color;
|
||||
|
||||
if ( badge.extra_css )
|
||||
el.style.cssText += badge.extra_css;
|
||||
|
||||
badges_out.push([((reverse ? 1 : -1) * slot), el]);
|
||||
}
|
||||
|
||||
badges_out.sort(function(a,b){return a[0] - b[0]});
|
||||
|
||||
if ( reverse ) {
|
||||
while(badges_out.length)
|
||||
before.before(badges_out.shift()[1]);
|
||||
} else {
|
||||
while(badges_out.length)
|
||||
badges.append(badges_out.shift()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------
|
||||
// Legacy Support
|
||||
// --------------------
|
||||
|
|
|
@ -13,8 +13,12 @@ module.exports = {
|
|||
API_SERVER_2: "//direct-api.frankerfacez.com/",
|
||||
|
||||
WS_SERVER_POOLS: {
|
||||
1: ["ws://catbag.frankerfacez.com/", "ws://andknuckles.frankerfacez.com/"],
|
||||
2: ["ws://localhost:8001/"]
|
||||
1: [
|
||||
["ws://catbag.frankerfacez.com/", 0.5],
|
||||
["ws://andknuckles.frankerfacez.com/", 1],
|
||||
["ws://tuturu.frankerfacez.com/", 1]],
|
||||
2: [
|
||||
["ws://localhost:8001/", 1]]
|
||||
},
|
||||
|
||||
TOOLTIP_DISTANCE: 50,
|
||||
|
|
|
@ -600,10 +600,12 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
this._ffz_update_uptime = setTimeout(this.ffzUpdateUptime.bind(this), 1000);
|
||||
|
||||
// Determine when the channel last went live.
|
||||
var online = this.get("controller.content.stream.created_at");
|
||||
var online = this.get("controller.content.stream.created_at"),
|
||||
now = Date.now() - (f._ws_server_offset || 0);
|
||||
|
||||
online = online && utils.parse_date(online);
|
||||
|
||||
var uptime = online && Math.floor((Date.now() - online.getTime()) / 1000) || -1;
|
||||
var uptime = online && Math.floor((now - online.getTime()) / 1000) || -1;
|
||||
if ( uptime < 0 ) {
|
||||
var el = this.get('element').querySelector('#ffz-uptime-display');
|
||||
if ( el )
|
||||
|
|
|
@ -426,7 +426,7 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
if ( f.settings.group_tabs && f._chatv && f._chatv._ffz_tabs )
|
||||
f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
|
||||
|
||||
var controller = f._chatv.get('controller');
|
||||
var controller = f._chatv && f._chatv.get('controller');
|
||||
controller && controller.set('showList', false);
|
||||
}, 1000);
|
||||
},
|
||||
|
@ -444,6 +444,10 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
ffzChangeRoom: Ember.observer('controller.currentRoom', function() {
|
||||
f.update_ui_link();
|
||||
|
||||
// Close mod cards when changing to a new room.
|
||||
if ( f._mod_card )
|
||||
f._mod_card.send('close');
|
||||
|
||||
var room = this.get('controller.currentRoom'), rows;
|
||||
room && room.resetUnreadCount();
|
||||
|
||||
|
|
|
@ -64,66 +64,19 @@ FFZ.prototype._modify_conversation_window = function(component) {
|
|||
component.reopen({
|
||||
headerBadges: Ember.computed("thread.participants", "currentUsername", function() {
|
||||
var e = this.get("thread.participants").rejectBy("username", this.get("currentUsername")).objectAt(0),
|
||||
badges = {},
|
||||
|
||||
ut = e.get("userType");
|
||||
badges = f.get_other_badges(e.get('username'), null, e.get('userType'), false, e.get('hasTurbo')),
|
||||
out = [];
|
||||
|
||||
if ( ut === "staff" )
|
||||
badges[0] = {classes: 'badge staff', title: 'Staff'};
|
||||
else if ( ut === 'admin' )
|
||||
badges[0] = {classes: 'badge admin', title: 'Admin'};
|
||||
else if ( ut === 'global_mod' )
|
||||
badges[0] = {classes: 'badge global-moderator', title: 'Global Moderator'};
|
||||
|
||||
if ( e.get('hasTurbo') )
|
||||
badges[15] = {classes: 'badge turbo', title: 'Turbo'}
|
||||
|
||||
// FFZ Badges
|
||||
var data = f.users[e.get('username')];
|
||||
if ( data && data.badges ) {
|
||||
for(var slot in data.badges) {
|
||||
if ( ! data.badges.hasOwnProperty(slot) )
|
||||
continue;
|
||||
|
||||
var badge = data.badges[slot],
|
||||
full_badge = f.badges[badge.id] || {},
|
||||
old_badge = badges[slot];
|
||||
|
||||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
try {
|
||||
visible = visible.bind(f)(null, e.get('username'), null, badges);
|
||||
} catch(err) {
|
||||
f.error("badge " + badge.id + " visible: " + err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( old_badge ) {
|
||||
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||
if ( ! replaces )
|
||||
continue;
|
||||
|
||||
old_badge.klass = 'badge ffz-badge-' + badge.id;
|
||||
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||
continue;
|
||||
}
|
||||
|
||||
badges[slot] = {
|
||||
classes: 'badge ffz-badge-' + badge.id,
|
||||
title: badge.title || full_badge.title
|
||||
}
|
||||
}
|
||||
// It wants slightly different output from us.
|
||||
for(var slot in badges) {
|
||||
var badge = badges[slot];
|
||||
out.push({
|
||||
classes: 'badge ' + badge.klass,
|
||||
title: badge.title
|
||||
});
|
||||
}
|
||||
|
||||
var out = [];
|
||||
for(var slot in badges)
|
||||
out.push(badges[slot]);
|
||||
|
||||
return out;
|
||||
}),
|
||||
|
||||
|
@ -143,6 +96,7 @@ FFZ.prototype._modify_conversation_window = function(component) {
|
|||
header_name.setAttribute('data-color', raw_color);
|
||||
}
|
||||
|
||||
jQuery('.badge', el).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(el).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,30 +1,176 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils'),
|
||||
constants = require('../constants');
|
||||
constants = require('../constants'),
|
||||
|
||||
NO_LOGO = "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
|
||||
|
||||
// --------------------
|
||||
// Settings
|
||||
// --------------------
|
||||
|
||||
FFZ.settings_info.directory_logos = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
FFZ.settings_info.sidebar_followed_games = {
|
||||
type: "select",
|
||||
options: {
|
||||
0: "Disabled",
|
||||
5: "Normal (5)",
|
||||
10: "Large (10)",
|
||||
999: "No Limit"
|
||||
},
|
||||
|
||||
value: 5,
|
||||
process_value: function(val) {
|
||||
if ( typeof val === "string" )
|
||||
return parseInt(val) || 0;
|
||||
return val;
|
||||
},
|
||||
|
||||
category: "Appearance",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Directory Logos",
|
||||
name: "Sidebar Followed Games",
|
||||
help: "Display this number of followed games on the sidebar.",
|
||||
|
||||
on_update: function(val) {
|
||||
var controller = App.__container__.lookup('controller:games-following');
|
||||
if ( controller )
|
||||
controller.set('ffz_sidebar_games', val);
|
||||
}
|
||||
}
|
||||
|
||||
FFZ.settings_info.directory_creative_all_tags = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
||||
category: "Directory",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Display All Creative Tags",
|
||||
help: "Alter the creative tags display to list them all in a cloud rather than having to scroll.",
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-creative-tags', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.directory_creative_showcase = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Directory",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Creative Showcase",
|
||||
help: "Display the showcase on the Creative directory page.",
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-creative-showcase', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.directory_logos = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
||||
category: "Directory",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Channel Logos",
|
||||
help: "Display channel logos in the Twitch directory."
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.directory_group_hosts = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category:"Directory",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Group Hosts",
|
||||
help: "Only show a given hosted channel once in the directory.",
|
||||
|
||||
on_update: function() {
|
||||
var f = this,
|
||||
HostModel = App.__container__.resolve('model:host'),
|
||||
Following = HostModel && HostModel.collections[HostModel.collectionId("following")];
|
||||
|
||||
if ( ! Following )
|
||||
return;
|
||||
|
||||
Following.clear();
|
||||
Following.load();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.directory_host_menus = {
|
||||
type: "select",
|
||||
options: {
|
||||
0: "Disabled",
|
||||
1: "When Multiple are Hosting",
|
||||
2: "Always"
|
||||
},
|
||||
|
||||
value: 1,
|
||||
process_value: function(val) {
|
||||
if ( typeof val === "string" )
|
||||
return parseInt(val) || 0;
|
||||
return val;
|
||||
},
|
||||
|
||||
category: "Directory",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Hosted Channel Menus",
|
||||
help: "Display a menu to select which channel to visit when clicking a hosted channel in the directory.",
|
||||
|
||||
on_update: function() {
|
||||
var f = this,
|
||||
HostModel = App.__container__.resolve('model:host'),
|
||||
Following = HostModel && HostModel.collections[HostModel.collectionId("following")];
|
||||
|
||||
if ( ! Following )
|
||||
return;
|
||||
|
||||
Following.clear();
|
||||
Following.load();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// --------------------
|
||||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_directory = function() {
|
||||
this.log("Hooking the Ember Directory View.");
|
||||
document.body.classList.toggle('ffz-creative-tags', this.settings.directory_creative_all_tags);
|
||||
document.body.classList.toggle('ffz-creative-showcase', this.settings.directory_creative_showcase);
|
||||
|
||||
this.log("Hooking the Ember games-following controller.");
|
||||
var GamesFollowing = App.__container__.lookup('controller:games-following');
|
||||
if ( GamesFollowing ) {
|
||||
GamesFollowing.reopen({
|
||||
ffz_sidebar_games: this.settings.sidebar_followed_games,
|
||||
|
||||
sidePanelFollowing: function() {
|
||||
var content = this.get('liveFollowing.sortedContent'),
|
||||
limit = this.get('ffz_sidebar_games');
|
||||
|
||||
return limit === 999 ? content : _.first(content, limit);
|
||||
}.property("liveFollowing.@each", "ffz_sidebar_games")
|
||||
});
|
||||
|
||||
Ember.propertyDidChange(GamesFollowing, 'sidePanelFollowing');
|
||||
}
|
||||
|
||||
|
||||
this.log("Attempting to modify the Following collection.");
|
||||
this._modify_following();
|
||||
|
||||
this.log("Hooking the Ember Directory views.");
|
||||
|
||||
var ChannelView = App.__container__.resolve('view:channel');
|
||||
if ( ChannelView )
|
||||
|
@ -55,6 +201,93 @@ FFZ.prototype.setup_directory = function() {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_following = function() {
|
||||
var HostModel = App.__container__.resolve('model:host'),
|
||||
f = this;
|
||||
|
||||
if ( HostModel ) {
|
||||
var Following = HostModel.collections[HostModel.collectionId("following")];
|
||||
if ( Following ) {
|
||||
this.log("Found Following model.");
|
||||
Following.reopen({
|
||||
ffz_streams: {},
|
||||
ffz_skipped: 0,
|
||||
|
||||
empty: function() {
|
||||
this._super();
|
||||
this.set("ffz_streams", {});
|
||||
this.set("ffz_skipped", 0);
|
||||
},
|
||||
|
||||
request: function(e) {
|
||||
// We have to override request with nearly the same logic
|
||||
// to prevent infinitely trying to load more streams.
|
||||
if (!Twitch.user.isLoggedIn() || window.App.get("disableFollowingDirectory")) return RSVP.resolve({
|
||||
hosts: [], _total: 0
|
||||
});
|
||||
|
||||
var t = {
|
||||
limit: this.limit,
|
||||
offset: this.get('content.length') + this.get('ffz_skipped')
|
||||
};
|
||||
|
||||
return Twitch.api.get("/api/users/:login/followed/hosting", t);
|
||||
},
|
||||
|
||||
afterSuccess: function(e) {
|
||||
var valid_hosts = [],
|
||||
streams = this.get('ffz_streams'),
|
||||
skipped = this.get('ffz_skipped'),
|
||||
t = this;
|
||||
|
||||
for(var i=0; i < e.hosts.length; i++) {
|
||||
var host = e.hosts[i],
|
||||
target = host && host.target && host.target.id;
|
||||
|
||||
if ( f.settings.directory_group_hosts && streams[target] ) {
|
||||
skipped++;
|
||||
streams[target].ffz_hosts && streams[target].ffz_hosts.push({logo: host.logo, name: host.name, display_name: host.display_name});
|
||||
continue;
|
||||
}
|
||||
|
||||
streams[target] = host;
|
||||
host.ffz_hosts = [{logo: host.logo, name: host.name, display_name: host.display_name}];
|
||||
|
||||
valid_hosts.push(host);
|
||||
}
|
||||
|
||||
this.set('ffz_skipped', skipped);
|
||||
this.setContent(valid_hosts);
|
||||
|
||||
// We could get non-empty results even with no new hosts.
|
||||
this.set('gotNonEmptyResults', e.hosts && e.hosts.length);
|
||||
this.set('total', e._total - skipped);
|
||||
}
|
||||
});
|
||||
|
||||
// Filter the streams immediately.
|
||||
if ( true && ! Following.get('isLoading') ) {
|
||||
var content = Following.get('content'),
|
||||
total = Following.get('total'),
|
||||
host_copy = [];
|
||||
|
||||
// TODO: Something less stupid.
|
||||
for(var i=0; i < content.length; i++)
|
||||
host_copy.push(content[i]);
|
||||
|
||||
Following.clear();
|
||||
Following.afterSuccess({hosts: host_copy, _total: total});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find it. Reschedule.
|
||||
setTimeout(this._modify_following.bind(this), 250);
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
||||
var f = this;
|
||||
dir.reopen({
|
||||
|
@ -93,7 +326,7 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
logo.className = 'profile-photo';
|
||||
logo.classList.toggle('is-csgo', is_csgo);
|
||||
|
||||
logo.src = this.get('context.model.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
logo.src = this.get('context.model.channel.logo') || NO_LOGO;
|
||||
logo.alt = this.get('context.model.channel.display_name');
|
||||
|
||||
link.href = '/' + target;
|
||||
|
@ -128,7 +361,8 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
ffzUpdateUptime: function() {
|
||||
var raw_created = this.get('context.model.created_at'),
|
||||
up_since = raw_created && utils.parse_date(raw_created),
|
||||
uptime = up_since && Math.floor((Date.now() - up_since.getTime()) / 1000) || 0;
|
||||
now = Date.now() - (f._ws_server_offset || 0),
|
||||
uptime = up_since && Math.floor((now - up_since.getTime()) / 1000) || 0;
|
||||
|
||||
if ( uptime > 0 ) {
|
||||
this._ffz_uptime.innerHTML = constants.CLOCK + utils.time_to_string(uptime, false, false, false, f.settings.stream_uptime === 1);
|
||||
|
@ -151,39 +385,143 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
dir.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("directory/host ffzInit: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
willClearRender: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzCleanup();
|
||||
} catch(err) {
|
||||
f.error("directory/host ffzCleanup: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
ffzVisitChannel: function(target, e) {
|
||||
var Channel = App.__container__.resolve('model:channel');
|
||||
if ( ! Channel )
|
||||
return;
|
||||
|
||||
if ( e ) {
|
||||
if ( e.button !== 0 )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
f.close_popup();
|
||||
this.get('controller').transitionTo('channel.index', Channel.find({id: target}).load());
|
||||
return false;
|
||||
},
|
||||
|
||||
ffzShowHostMenu: function(e) {
|
||||
if ( e.button !== 0 )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var hosts = this.get('context.model.ffz_hosts'),
|
||||
target = this.get('context.model.target.channel.name');
|
||||
|
||||
if ( f.settings.directory_host_menus === 0 || ! hosts || (f.settings.directory_host_menus === 1 && hosts.length < 2) )
|
||||
return this.ffzVisitChannel((hosts && hosts.length < 2) ? hosts[0].name : target);
|
||||
|
||||
var popup = f._popup ? f.close_popup() : f._last_popup,
|
||||
t = this;
|
||||
|
||||
// Don't re-show the popup if we were clicking to show it.
|
||||
if ( popup && popup.classList.contains('ffz-channel-selector') && popup.getAttribute('data-channel') === target )
|
||||
return;
|
||||
|
||||
var menu = document.createElement('div'), hdr,
|
||||
make_link = function(target) {
|
||||
var link = document.createElement('a');
|
||||
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.addEventListener('click', t.ffzVisitChannel.bind(t, target.name));
|
||||
menu.appendChild(link);
|
||||
return link;
|
||||
};
|
||||
|
||||
menu.className = 'ffz-channel-selector dropmenu menu-like';
|
||||
menu.setAttribute('data-channel', target);
|
||||
|
||||
hdr = document.createElement('div');
|
||||
hdr.className = 'header';
|
||||
hdr.textContent = 'Hosted Channel';
|
||||
menu.appendChild(hdr);
|
||||
|
||||
make_link(this.get('context.model.target.channel'));
|
||||
|
||||
hdr = document.createElement('div');
|
||||
hdr.className = 'header';
|
||||
hdr.textContent = 'Hosting Channels';
|
||||
menu.appendChild(hdr);
|
||||
|
||||
for(var i=0; i < hosts.length; i++)
|
||||
make_link(hosts[i]);
|
||||
|
||||
f.show_popup(menu, [e.clientX - 60, e.clientY - 60], document.querySelector('#main_col > .tse-scroll-content > .tse-content'));
|
||||
},
|
||||
|
||||
ffzCleanup: function() {
|
||||
if ( f._popup && f._popup.classList.contains('ffz-channel-selector') && f._popup.getAttribute('data-channel') === target )
|
||||
f.close_popup();
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
var el = this.get('element'),
|
||||
meta = el && el.querySelector('.meta'),
|
||||
thumb = el && el.querySelector('.thumb'),
|
||||
cap = thumb && thumb.querySelector('.cap');
|
||||
cap = thumb && thumb.querySelector('.cap'),
|
||||
title = meta && meta.querySelector('.title a'),
|
||||
|
||||
target = this.get('context.model.target.channel'),
|
||||
hosts = this.get('context.model.ffz_hosts');
|
||||
|
||||
if ( f.settings.directory_logos ) {
|
||||
el.classList.add('ffz-directory-logo');
|
||||
|
||||
var link = document.createElement('a'),
|
||||
logo = document.createElement('img'),
|
||||
t = this,
|
||||
target = this.get('context.model.target.channel.name');
|
||||
var logo = document.createElement('img'),
|
||||
link = document.createElement('a');
|
||||
|
||||
logo.className = 'profile-photo';
|
||||
logo.src = this.get('context.model.target.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
logo.src = this.get('context.model.target.channel.logo') || NO_LOGO;
|
||||
logo.alt = this.get('context.model.target.channel.display_name');
|
||||
|
||||
link.href = '/' + target;
|
||||
link.addEventListener('click', function(e) {
|
||||
var Channel = App.__container__.resolve('model:channel');
|
||||
if ( ! Channel )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
t.get('controller').transitionTo('channel.index', Channel.find({id: target}).load());
|
||||
return false;
|
||||
});
|
||||
link.href = '/' + target.name;
|
||||
link.addEventListener('click', this.ffzVisitChannel.bind(this, target.name));
|
||||
|
||||
link.appendChild(logo);
|
||||
meta.insertBefore(link, meta.firstChild);
|
||||
}
|
||||
|
||||
var update_links = f.settings.directory_host_menus === 2 || (hosts && hosts.length > 1);
|
||||
|
||||
if ( title ) {
|
||||
if ( update_links ) {
|
||||
title.href = '/' + target.name;
|
||||
title.addEventListener('click', this.ffzShowHostMenu.bind(this));
|
||||
}
|
||||
|
||||
if ( hosts && hosts.length > 1 ) {
|
||||
title.textContent = utils.number_commas(hosts.length) + ' hosting ' + utils.sanitize(target.display_name);
|
||||
title.title = _.sortBy(hosts, "name").mapProperty("display_name").join(", ");
|
||||
jQuery(title).tipsy({gravity: 's'});
|
||||
}
|
||||
}
|
||||
|
||||
if ( cap && update_links ) {
|
||||
cap.href = '/' + target.name;
|
||||
cap.addEventListener('click', this.ffzShowHostMenu.bind(this));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -161,7 +161,8 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
t_el.className = 'overlay_info length';
|
||||
jQuery(t_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
var age = data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : 0;
|
||||
var now = Date.now() - (f._ws_server_offset || 0),
|
||||
age = data[0] ? Math.floor((now - data[0].getTime()) / 1000) : 0;
|
||||
if ( age ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + utils.human_time(age, 10);
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
|
@ -180,7 +181,8 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
follow.innerHTML = constants.HEART + constants.UNHEART + '<span> Follow</span>';
|
||||
|
||||
if ( t_el ) {
|
||||
var age = data && data[0] ? Math.floor((Date.now() - data[0].getTime()) / 1000) : undefined;
|
||||
var now = Date.now() - (f._ws_server_offset || 0),
|
||||
age = data && data[0] ? Math.floor((now - data[0].getTime()) / 1000) : undefined;
|
||||
if ( age !== undefined ) {
|
||||
t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
|
||||
t_el.setAttribute('original-title', 'Following Since: <nobr>' + data[0].toLocaleString() + '</nobr>');
|
||||
|
@ -216,7 +218,7 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
Twitch.api.del("users/:login/follows/channels/" + user_id) :
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
|
||||
.done(function() {
|
||||
data = f._following_cache[user_id] = was_following ? null : [new Date(), false];
|
||||
data = f._following_cache[user_id] = was_following ? null : [new Date(Date.now() - (f._ws_server_offset||0)), false];
|
||||
})
|
||||
.always(function() {
|
||||
update_follow();
|
||||
|
|
|
@ -505,7 +505,7 @@ FFZ.settings_info.chat_ts_size = {
|
|||
css = "";
|
||||
else {
|
||||
var lh = Math.max(20, Math.round((20/12)*val), Math.round((20/12)*this.settings.chat_font_size));
|
||||
css = ".chat-history .timestamp,.ember-chat .chat-messages .timestamp { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
css = ".ember-chat .chat-messages .timestamp { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
|
||||
}
|
||||
|
||||
utils.update_css(this._chat_style, "chat_ts_font_size", css);
|
||||
|
@ -658,8 +658,6 @@ FFZ.prototype._modify_line = function(component) {
|
|||
var deleted = this.get('msgObject.deleted'),
|
||||
r = this,
|
||||
|
||||
badges = {},
|
||||
|
||||
user = this.get('msgObject.from'),
|
||||
room_id = this.get('msgObject.room'),
|
||||
room = f.rooms && f.rooms[room_id],
|
||||
|
@ -679,6 +677,8 @@ FFZ.prototype._modify_line = function(component) {
|
|||
e.push('<div class="indicator"></div>');
|
||||
e.push('<span class="timestamp float-left">' + this.get("timestamp") + '</span> ');
|
||||
|
||||
|
||||
// Moderation actions
|
||||
if ( ! is_whisper && this_ul < other_ul ) {
|
||||
e.push('<span class="mod-icons float-left">');
|
||||
for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
|
||||
|
@ -712,44 +712,14 @@ FFZ.prototype._modify_line = function(component) {
|
|||
e.push('</span>');
|
||||
}
|
||||
|
||||
// Stock Badges
|
||||
if ( ! is_whisper && this.get('isBroadcaster') )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else if ( this.get('isStaff') )
|
||||
badges[0] = {klass: 'staff', title: 'Staff'};
|
||||
else if ( this.get('isAdmin') )
|
||||
badges[0] = {klass: 'admin', title: 'Admin'};
|
||||
else if ( this.get('isGlobalMod') )
|
||||
badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
|
||||
else if ( ! is_whisper && this.get('isModerator') )
|
||||
badges[0] = {klass: 'moderator', title: 'Moderator'};
|
||||
|
||||
if ( ! is_whisper && this.get('isSubscriber') )
|
||||
badges[10] = {klass: 'subscriber', title: 'Subscriber'};
|
||||
if ( this.get('hasTurbo') )
|
||||
badges[15] = {klass: 'turbo', title: 'Turbo'};
|
||||
|
||||
// FFZ Badges
|
||||
badges = f.render_badges(this, badges);
|
||||
|
||||
// Rendering!
|
||||
// Badges
|
||||
e.push('<span class="badges float-left">');
|
||||
|
||||
for(var key in badges) {
|
||||
var badge = badges[key],
|
||||
css = badge.image ? 'background-image:url("' + badge.image + '");' : '';
|
||||
|
||||
if ( badge.color )
|
||||
css += 'background-color:' + badge.color + ';';
|
||||
|
||||
if ( badge.extra_css )
|
||||
css += badge.extra_css;
|
||||
|
||||
e.push('<div class="badge float-left tooltip ' + badge.klass + '"' + (css ? ' style="' + css + '"' : '') + ' title="' + badge.title + '"></div>');
|
||||
}
|
||||
|
||||
e.push(f.render_badges(f.get_line_badges(this.get('msgObject'), is_whisper)));
|
||||
e.push('</span>');
|
||||
|
||||
|
||||
// Handle aliases
|
||||
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]),
|
||||
|
@ -760,6 +730,8 @@ FFZ.prototype._modify_line = function(component) {
|
|||
else
|
||||
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');
|
||||
|
||||
|
||||
// If it's a whisper, we need to get that user's color, alias, and draw the whisper arrow thing.
|
||||
if ( is_whisper ) {
|
||||
var to_alias = f.aliases[recipient],
|
||||
to_name = this.get('msgObject.tags.recipient-display-name') || (recipient && recipient.capitalize()) || "unknown user",
|
||||
|
@ -777,6 +749,8 @@ FFZ.prototype._modify_line = function(component) {
|
|||
e.push('<span class="to' + to_colored + '" style="' + to_style + (to_colors ? '" data-color="' + to_color : '') + '">' + utils.sanitize(to_name) + '</span>');
|
||||
}
|
||||
|
||||
|
||||
// Finally, onto the message proper.
|
||||
e.push('<span class="colon">:</span> ');
|
||||
|
||||
if ( this.get('msgObject.style') !== 'action' ) {
|
||||
|
|
|
@ -426,7 +426,8 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
}
|
||||
|
||||
if ( since ) {
|
||||
var age = Math.floor((Date.now() - since.getTime()) / 1000);
|
||||
var now = Date.now() - (f._ws_server_offset || 0),
|
||||
age = Math.floor((now - since.getTime()) / 1000);
|
||||
if ( age > 0 ) {
|
||||
out += '<span class="stat tooltip" title="Member Since: ' + (age > 86400 ? since.toLocaleDateString() : since.toLocaleString()) + '">' + constants.CLOCK + ' ' + utils.human_time(age, 10) + '</span>';
|
||||
}
|
||||
|
@ -458,6 +459,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
var el = this.get('element'),
|
||||
controller = this.get('controller'),
|
||||
t = this,
|
||||
line,
|
||||
|
||||
is_mod = controller.get('cardInfo.isModeratorOrHigher'),
|
||||
|
@ -731,6 +733,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
if ( f.settings.mod_card_history ) {
|
||||
var Chat = App.__container__.lookup('controller:chat'),
|
||||
room = Chat && Chat.get('currentRoom'),
|
||||
delete_links = room && room.get('roomProperties.hide_chat_links'),
|
||||
tmiSession = room.tmiSession || (window.TMI && TMI._sessions && TMI._sessions[0]),
|
||||
room_id = room.get('id'),
|
||||
user_id = controller.get('cardInfo.user.id'),
|
||||
|
@ -741,12 +744,14 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
|
||||
history.className = 'interface clearfix chat-history';
|
||||
|
||||
if ( user_history.length < 20 ) {
|
||||
var before = user_history.length > 0 ? user_history[0].date.getTime() : Date.now();
|
||||
if ( user_history.length < 50 ) {
|
||||
var before = (user_history.length > 0 ? user_history[0].date.getTime() : Date.now()) - (f._ws_server_offset || 0);
|
||||
f.ws_send("user_history", [room_id, user_id, 50 - user_history.length], function(success, data) {
|
||||
if ( ! success )
|
||||
return;
|
||||
|
||||
f.parse_history(data, null, room_id, delete_links, tmiSession);
|
||||
|
||||
var i = data.length,
|
||||
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight),
|
||||
first = true;
|
||||
|
@ -756,8 +761,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
if ( ! msg )
|
||||
continue;
|
||||
|
||||
if ( typeof msg.date === "string" || typeof msg.date === "number" )
|
||||
msg.date = utils.parse_date(msg.date);
|
||||
msg.from_server = true;
|
||||
|
||||
if ( ! msg.date || msg.date.getTime() >= before )
|
||||
continue;
|
||||
|
@ -772,27 +776,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
}), history.firstElementChild);
|
||||
}
|
||||
|
||||
if ( ! msg.style ) {
|
||||
if ( msg.from === "jtv" )
|
||||
msg.style = "admin";
|
||||
else if ( msg.from === "twitchnotify" )
|
||||
msg.style = "notification";
|
||||
}
|
||||
|
||||
if ( msg.tags && typeof msg.tags.emotes === "string" )
|
||||
try {
|
||||
msg.tags.emotes = JSON.parse(msg.tags.emotes);
|
||||
} catch(err) {
|
||||
f.log("Error Parsing JSON Emotes: " + err);
|
||||
msg.tags.emotes = {};
|
||||
}
|
||||
|
||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||
f.tokenize_chat_line(msg, true, room.get('roomProperties.hide_chat_links'));
|
||||
|
||||
history.insertBefore(f._build_mod_card_history(msg), history.firstElementChild);
|
||||
if ( history.childElementCount >= 50 )
|
||||
break;
|
||||
history.insertBefore(f._build_mod_card_history(msg, t), history.firstElementChild);
|
||||
}
|
||||
|
||||
if ( was_at_top )
|
||||
|
@ -801,7 +785,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
}
|
||||
|
||||
for(var i=0; i < user_history.length; i++)
|
||||
history.appendChild(f._build_mod_card_history(user_history[i]));
|
||||
history.appendChild(f._build_mod_card_history(user_history[i], t));
|
||||
|
||||
el.appendChild(history);
|
||||
|
||||
|
@ -832,29 +816,214 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
f.error("ModerationCardView didInsertElement: " + err);
|
||||
} catch(err) { }
|
||||
}
|
||||
}});
|
||||
},
|
||||
|
||||
ffzAdjacentHistory: function(line) {
|
||||
var Chat = App.__container__.lookup('controller:chat'),
|
||||
controller = this.get('controller'),
|
||||
t = this,
|
||||
|
||||
user_id = this.get('cardInfo.user.id'),
|
||||
|
||||
room = Chat && Chat.get('currentRoom'),
|
||||
room_id = room.get('id'),
|
||||
delete_links = room && room.get('roomProperties.hide_chat_links'),
|
||||
|
||||
tmiSession = room.tmiSession || (window.TMI && TMI._sessions && TMI._sessions[0]),
|
||||
|
||||
el = this.get('element'),
|
||||
history = el && el.querySelector('.chat-history'),
|
||||
logs = el && el.querySelector('.chat-history.adjacent-history'),
|
||||
|
||||
when = line.date.getTime();
|
||||
|
||||
if ( ! history )
|
||||
return;
|
||||
|
||||
if ( logs )
|
||||
logs.classList.add('loading');
|
||||
else
|
||||
history.classList.add('loading');
|
||||
|
||||
if ( ! f.ws_send("adjacent_history", [room_id, when, 2], function(success, data) {
|
||||
if ( logs )
|
||||
logs.classList.remove('loading');
|
||||
else
|
||||
history.classList.remove('loading');
|
||||
|
||||
if ( ! success || ! data || ! data.length )
|
||||
return;
|
||||
|
||||
var had_logs = false,
|
||||
found_original = false,
|
||||
back;
|
||||
|
||||
if ( logs ) {
|
||||
had_logs = true;
|
||||
logs.innerHTML = '';
|
||||
|
||||
} else {
|
||||
logs = document.createElement('ul');
|
||||
back = document.createElement('button');
|
||||
|
||||
back.className = 'button back-button';
|
||||
back.innerHTML = '« Back';
|
||||
|
||||
back.addEventListener('click', function() {
|
||||
logs.parentElement.removeChild(logs);
|
||||
back.parentElement.removeChild(back);
|
||||
history.classList.remove('hidden');
|
||||
});
|
||||
|
||||
logs.className = 'interface clearfix chat-history adjacent-history';
|
||||
}
|
||||
|
||||
|
||||
f.parse_history(data, null, room_id, delete_links, tmiSession, function(msg) {
|
||||
msg.from_server = true;
|
||||
|
||||
var line_time = line.date.getTime() - (line.from_server ? 0 : (f._ws_server_offset || 0)),
|
||||
is_original = ! found_original && Math.abs(line_time - msg.date.getTime()) < (line.from_server ? 50 : 1000) && line.from === msg.from && line.message === msg.message;
|
||||
|
||||
msg.original_sender = user_id === msg.from;
|
||||
msg.is_original = is_original;
|
||||
found_original = found_original || is_original;
|
||||
|
||||
logs.insertBefore(f._build_mod_card_history(msg, t, true), logs.firstElementChild);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
if ( ! had_logs ) {
|
||||
history.classList.add('hidden');
|
||||
history.parentElement.insertBefore(logs, history);
|
||||
history.parentElement.insertBefore(back, logs);
|
||||
}
|
||||
|
||||
if ( found_original )
|
||||
setTimeout(function(){
|
||||
el = logs.querySelector('.original-msg');
|
||||
if ( el )
|
||||
logs.scrollTop = (el.offsetTop - logs.offsetTop) - (logs.clientHeight - el.clientHeight) / 2;
|
||||
});
|
||||
|
||||
}) )
|
||||
if ( logs )
|
||||
logs.classList.remove('loading');
|
||||
else
|
||||
history.classList.remove('loading');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._build_mod_card_history = function(line) {
|
||||
FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
|
||||
var l_el = document.createElement('li'),
|
||||
out = [],
|
||||
f = this;
|
||||
|
||||
style = '', colored = '';
|
||||
|
||||
if ( helpers && helpers.getTime )
|
||||
out.push('<span class="timestamp float-left">' + helpers.getTime(msg.date) + '</span>');
|
||||
|
||||
|
||||
if ( show_from ) {
|
||||
// Badges
|
||||
out.push('<span class="badges float-left">');
|
||||
out.push(this.render_badges(this.get_line_badges(msg, false)));
|
||||
out.push('</span>');
|
||||
|
||||
|
||||
// Colors
|
||||
var raw_color = msg.color,
|
||||
colors = raw_color && this._handle_color(raw_color),
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings'),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('model.darkMode'));
|
||||
|
||||
|
||||
// Aliases and Styling
|
||||
var alias = this.aliases[msg.from],
|
||||
name = (msg.tags && msg.tags['display-name']) || (msg.from && msg.from.capitalize()) || "unknown user",
|
||||
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
|
||||
colored = style ? ' has-color' : '';
|
||||
|
||||
|
||||
if ( alias )
|
||||
out.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
|
||||
else
|
||||
out.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name ) + '</span>');
|
||||
|
||||
out.push('<span class="colon">:</span> ');
|
||||
}
|
||||
|
||||
|
||||
// The message itself.
|
||||
if ( msg.style !== 'action' ) {
|
||||
style = '';
|
||||
colored = '';
|
||||
}
|
||||
|
||||
|
||||
var message = '<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' +
|
||||
(msg.style === 'action' && ! show_from ? '*' + name + ' ' : '') + this.render_tokens(msg.cachedTokens) + '</span>';
|
||||
|
||||
if ( msg.deleted )
|
||||
out.push('<span class="deleted"><a class="undelete" href="#" data-message="' + utils.quote_attr(message) + '"><message deleted></a></span>');
|
||||
else
|
||||
out.push(message);
|
||||
|
||||
|
||||
// Line attributes and classes.
|
||||
l_el.className = 'message-line chat-line clearfix';
|
||||
|
||||
if ( line.ffz_has_mention )
|
||||
if ( msg.style )
|
||||
l_el.classList.add(msg.style);
|
||||
|
||||
if ( msg.original_sender )
|
||||
l_el.classList.add('original-sender');
|
||||
|
||||
if ( msg.is_original )
|
||||
l_el.classList.add('original-msg');
|
||||
|
||||
if ( msg.ffz_has_mention )
|
||||
l_el.classList.add('ffz-mentioned');
|
||||
|
||||
if ( line.style )
|
||||
l_el.classList.add(line.style);
|
||||
if ( this.settings.prevent_clear && msg.ffz_deleted )
|
||||
l_el.classList.add('ffz-deleted');
|
||||
|
||||
l_el.setAttribute('data-room', msg.room);
|
||||
l_el.setAttribute('data-sender', msg.from);
|
||||
l_el.setAttribute('data-deleted', msg.deleted || false);
|
||||
|
||||
l_el.innerHTML = out.join("");
|
||||
|
||||
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(line.date) + '</span> ' : '') + '<span class="message">' + (line.style === 'action' ? '*' + line.from + ' ' : '') + f.render_tokens(line.cachedTokens) + '</span>';
|
||||
|
||||
// Interactivity
|
||||
jQuery('a.undelete', l_el).click(function(e) { this.parentElement.outerHTML = this.getAttribute('data-message'); });
|
||||
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
|
||||
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
|
||||
jQuery('.html-tooltip', l_el).tipsy({html:true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
if ( modcard ) {
|
||||
modcard.get('cardInfo.user.id') !== msg.from && jQuery('span.from', l_el).click(function(e) {
|
||||
var el = modcard.get('element');
|
||||
el && f._roomv && f._roomv.get('context.model.id') === msg.room && f._roomv.get('controller').send('showModOverlay', {
|
||||
sender: msg.from,
|
||||
top: parseInt(el.style.top),
|
||||
left: parseInt(el.style.left)
|
||||
});
|
||||
});
|
||||
|
||||
l_el.querySelector('.timestamp').addEventListener('click', function(e) {
|
||||
if ( e.button === 0 )
|
||||
modcard.ffzAdjacentHistory(msg);
|
||||
});
|
||||
}
|
||||
|
||||
return l_el;
|
||||
}
|
||||
|
||||
|
@ -881,6 +1050,8 @@ FFZ.prototype._update_alias = function(user) {
|
|||
el_from.textContent = display_name;
|
||||
el_from.title = alias ? cap_name : '';
|
||||
}
|
||||
|
||||
// TODO: Update conversations~
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -713,7 +713,7 @@ FFZ.prototype._load_history = function(room_id, success, data) {
|
|||
if ( ! data.length )
|
||||
return;
|
||||
|
||||
return this._insert_history(room_id, data);
|
||||
return this._insert_history(room_id, data, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -730,118 +730,93 @@ FFZ.prototype._show_deleted = function(room_id) {
|
|||
this._insert_history(room_id, old_messages);
|
||||
}
|
||||
|
||||
FFZ.prototype._insert_history = function(room_id, data) {
|
||||
var room = this.rooms[room_id];
|
||||
FFZ.prototype._insert_history = function(room_id, data, from_server) {
|
||||
var room = this.rooms[room_id], f = this;
|
||||
if ( ! room || ! room.room )
|
||||
return;
|
||||
|
||||
var r = room.room,
|
||||
messages = r.get('messages'),
|
||||
buffer_size = r.get('messageBufferSize'),
|
||||
|
||||
tmiSession = r.tmiSession || (TMI._sessions && TMI._sessions[0]),
|
||||
tmiRoom = r.tmiRoom,
|
||||
delete_links = r.get('roomProperties.hide_chat_links'),
|
||||
|
||||
removed = 0,
|
||||
inserted = 0,
|
||||
purged = {},
|
||||
|
||||
last_msg = data[data.length - 1],
|
||||
now = new Date(),
|
||||
last_date = (typeof last_msg.date === "string" || typeof last_msg.date === "number") ? (last_msg.date = utils.parse_date(last_msg.date)) : last_msg.date,
|
||||
age = (now - last_date) / 1000,
|
||||
is_old = age > 300,
|
||||
first_inserted,
|
||||
first_existing,
|
||||
before;
|
||||
|
||||
i = data.length;
|
||||
first_existing = messages.length ? messages[0] : null;
|
||||
if ( first_existing && first_existing.from === 'jtv' && first_existing.message === 'Welcome to the chat room!' )
|
||||
first_existing = messages.length > 1 ? messages[1] : null;
|
||||
|
||||
var i = data.length;
|
||||
while(i--) {
|
||||
var msg = data[i],
|
||||
is_deleted = msg.ffz_deleted = purged[msg.from] || false;
|
||||
if ( first_existing )
|
||||
before = first_existing.date && first_existing.date.getTime();
|
||||
|
||||
if ( is_deleted && ! this.settings.prevent_clear )
|
||||
msg.deleted = true;
|
||||
|
||||
if ( typeof msg.date === "string" || typeof msg.date === "number" )
|
||||
msg.date = utils.parse_date(msg.date);
|
||||
this.parse_history(data, null, room_id, delete_links, tmiSession, function(msg) {
|
||||
if ( from_server )
|
||||
msg.from_server = true;
|
||||
|
||||
if ( ! msg.room )
|
||||
msg.room = room_id;
|
||||
// Skip messages that are from the future.
|
||||
if ( ! msg.date || (before && (before - (msg.from_server && ! first_existing.from_server ? f._ws_server_offset || 0 : 0)) < msg.date.getTime()) )
|
||||
return true;
|
||||
|
||||
if ( ! msg.color )
|
||||
msg.color = msg.tags && msg.tags.color ? msg.tags.color : tmiSession && msg.from ? tmiSession.getColor(msg.from.toLowerCase()) : "#755000";
|
||||
if ( f.settings.remove_deleted && msg.deleted )
|
||||
return true;
|
||||
|
||||
if ( ! msg.labels || ! msg.labels.length ) {
|
||||
var labels = msg.labels = [];
|
||||
if ( msg.tags ) {
|
||||
if ( msg.tags.turbo )
|
||||
labels.push("turbo");
|
||||
if ( msg.tags.subscriber )
|
||||
labels.push("subscriber");
|
||||
if ( msg.from === room_id )
|
||||
labels.push("owner")
|
||||
else {
|
||||
var ut = msg.tags['user-type'];
|
||||
if ( ut === 'mod' || ut === 'staff' || ut === 'admin' || ut === 'global_mod' )
|
||||
labels.push(ut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! msg.style ) {
|
||||
if ( msg.from === "jtv" )
|
||||
msg.style = "admin";
|
||||
else if ( msg.from === "twitchnotify" )
|
||||
msg.style = "notification";
|
||||
}
|
||||
|
||||
if ( msg.tags && typeof msg.tags.emotes === "string" )
|
||||
try {
|
||||
msg.tags.emotes = JSON.parse(msg.tags.emotes);
|
||||
} catch(err) {
|
||||
f.log("Error Parsing JSON Emotes: " + err);
|
||||
msg.tags.emotes = {};
|
||||
}
|
||||
|
||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||
|
||||
if ( r.shouldShowMessage(msg) ) {
|
||||
if ( messages.length < r.get("messageBufferSize") ) {
|
||||
// One last thing! Make sure we don't have too many messages.
|
||||
if ( r.shouldShowMessage(msg) && r.ffzShouldShowMessage(msg) ) {
|
||||
if ( messages.length < buffer_size ) {
|
||||
if ( msg.ffz_old_messages ) {
|
||||
var max_msgs = r.get("messageBufferSize") - (messages.length + 1);
|
||||
if ( msg.ffz_old_messages.length > max_msgs )
|
||||
msg.ffz_old_messages = msg.ffz_old_messages.slice(msg.ffz_old_messages.length - max_msgs);
|
||||
var max_messages = buffer_size - (messages.length + 1);
|
||||
if ( max_messages <= 0 )
|
||||
msg.ffz_old_messages = null;
|
||||
else if ( msg.ffz_old_messages.length > max_messages )
|
||||
msg.ffz_old_messages = msg.ffz_old_messages.slice(msg.ffz_old_messages.length - max_messages);
|
||||
}
|
||||
|
||||
if ( ! first_inserted )
|
||||
first_inserted = msg;
|
||||
|
||||
messages.unshiftObject(msg);
|
||||
inserted += 1;
|
||||
|
||||
} else
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there was a CLEARCHAT, stop processing.
|
||||
// If there's a CLEARCHAT, stop processing.
|
||||
if ( msg.tags && msg.tags.target === '@@' )
|
||||
break;
|
||||
return false;
|
||||
|
||||
// If there was a purge, just track the name.
|
||||
else if ( msg.tags && msg.tags.target )
|
||||
purged[msg.tags.target] = true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if ( is_old ) {
|
||||
|
||||
if ( ! first_inserted )
|
||||
return;
|
||||
|
||||
var now = Date.now() - (first_inserted.from_server ? this._ws_server_offset || 0 : 0),
|
||||
age = now - first_inserted.date.getTime();
|
||||
|
||||
if ( age > 300000 ) {
|
||||
var msg = {
|
||||
color: "#755000",
|
||||
date: new Date(),
|
||||
date: first_inserted.date,
|
||||
from: "frankerfacez_admin",
|
||||
style: "admin",
|
||||
message: "(Last message is " + utils.human_time(age) + " old.)",
|
||||
room: room_id
|
||||
message: "(Last message is " + utils.human_time(age/1000) + " old.)",
|
||||
room: room_id,
|
||||
from_server: from_server
|
||||
};
|
||||
|
||||
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||
this.tokenize_chat_line(msg, false, delete_links);
|
||||
if ( r.shouldShowMessage(msg) ) {
|
||||
messages.insertAt(inserted, msg);
|
||||
while( messages.length > r.get('messageBufferSize') ) {
|
||||
while ( messages.length > buffer_size ) {
|
||||
messages.removeAt(0);
|
||||
removed++;
|
||||
}
|
||||
|
@ -1100,8 +1075,7 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( ! this.ffzPending )
|
||||
this.ffzPending = [];
|
||||
|
||||
var now = Date.now();
|
||||
msg.time = now;
|
||||
var now = msg.time = Date.now();
|
||||
this.ffzPending.push(msg);
|
||||
this.ffzSchedulePendingFlush(now);
|
||||
|
||||
|
@ -1134,9 +1108,10 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( this.ffzPending && this.ffzPending.length ) {
|
||||
// We need either the amount of chat delay past the first message, if chat_delay is on, or the
|
||||
// amount of time from the last batch.
|
||||
now = now || Date.now();
|
||||
var delay = Math.max(
|
||||
(f.settings.chat_delay !== 0 ? 50 + Math.max(0, (f.settings.chat_delay + (this.ffzPending[0].time||0)) - (now || Date.now())) : 0),
|
||||
(f.settings.chat_batching !== 0 ? Math.max(0, f.settings.chat_batching - ((now || Date.now()) - (this._ffz_last_batch||0))) : 0));
|
||||
(f.settings.chat_delay !== 0 ? 50 + Math.max(0, (f.settings.chat_delay + (this.ffzPending[0].time||0)) - now) : 0),
|
||||
(f.settings.chat_batching !== 0 ? Math.max(0, f.settings.chat_batching - (now - (this._ffz_last_batch||0))) : 0));
|
||||
|
||||
this._ffz_pending_flush = setTimeout(this.ffzPendingFlush.bind(this), delay);
|
||||
}
|
||||
|
@ -1163,6 +1138,9 @@ FFZ.prototype._modify_room = function(room) {
|
|||
},
|
||||
|
||||
ffzShouldShowMessage: function (msg) {
|
||||
if ( ! f.settings.hosted_sub_notices && msg.style === 'notification' && HOSTED_SUB.test(msg.message) )
|
||||
return false;
|
||||
|
||||
if (f.settings.remove_bot_ban_notices && this.ffzRecentlyBanned) {
|
||||
var banned = '(' + this.ffzRecentlyBanned.join('|') + ')';
|
||||
var bots = {
|
||||
|
@ -1181,9 +1159,6 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
addMessage: function(msg) {
|
||||
if ( msg ) {
|
||||
if ( ! f.settings.hosted_sub_notices && msg.style === 'notification' && HOSTED_SUB.test(msg.message) )
|
||||
return;
|
||||
|
||||
var is_whisper = msg.style === 'whisper';
|
||||
|
||||
// Ignore whispers if conversations are enabled.
|
||||
|
@ -1212,7 +1187,9 @@ FFZ.prototype._modify_room = function(room) {
|
|||
user_history = room.user_history[msg.from] = room.user_history[msg.from] || [];
|
||||
|
||||
user_history.push({
|
||||
from: msg.tags && msg.tags['display-name'] || msg.from,
|
||||
from: msg.from,
|
||||
tags: {'display-name': msg.tags && msg.tags['display-name']},
|
||||
message: msg.message,
|
||||
cachedTokens: msg.cachedTokens,
|
||||
style: msg.style,
|
||||
date: msg.date
|
||||
|
@ -1223,16 +1200,16 @@ FFZ.prototype._modify_room = function(room) {
|
|||
|
||||
if ( f._mod_card && f._mod_card.ffz_room_id === msg.room && f._mod_card.get('cardInfo.user.id') === msg.from ) {
|
||||
var el = f._mod_card.get('element'),
|
||||
history = el && el.querySelector('.chat-history'),
|
||||
history = el && el.querySelector('.chat-history:not(.adjacent-history)'),
|
||||
was_at_top = history && history.scrollTop >= (history.scrollHeight - history.clientHeight);
|
||||
|
||||
if ( history ) {
|
||||
history.appendChild(f._build_mod_card_history(msg));
|
||||
history.appendChild(f._build_mod_card_history(msg, f._mod_card));
|
||||
if ( was_at_top )
|
||||
setTimeout(function() { history.scrollTop = history.scrollHeight; })
|
||||
|
||||
// Don't do infinite scrollback.
|
||||
if ( history.childElementCount > 50 )
|
||||
if ( history.childElementCount > 100 )
|
||||
history.removeChild(history.firstElementChild);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,12 @@ FFZ.prototype.setup_router = function() {
|
|||
if ( Router )
|
||||
Router.reopen({
|
||||
ffzTransition: function() {
|
||||
// TODO: Do this before the transition happens.
|
||||
if ( f._force_refresh ) {
|
||||
location.href = this.get('url');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
document.body.setAttribute('data-current-path', App.get('currentPath'));
|
||||
} catch(err) {
|
||||
|
@ -25,4 +31,11 @@ FFZ.prototype.setup_router = function() {
|
|||
});
|
||||
|
||||
document.body.setAttribute('data-current-path', App.get('currentPath'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
FFZ.ws_commands.please_refresh = function() {
|
||||
this.log("Refreshing the page upon the next transition.");
|
||||
this._force_refresh = true;
|
||||
}
|
|
@ -122,7 +122,8 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
|||
|
||||
line.classList.add('ffz-processed');
|
||||
|
||||
var user_id = line.getAttribute('data-sender'),
|
||||
var f = this,
|
||||
user_id = line.getAttribute('data-sender'),
|
||||
room_id = line.getAttribute('data-room'),
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
|
@ -173,23 +174,9 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
|||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
|
||||
if ( user_id )
|
||||
badges = this._render_badges(user_id, room_id, badges);
|
||||
badges = this.get_badges(user_id, room_id, badges, null);
|
||||
|
||||
var output = '';
|
||||
for(var key in badges) {
|
||||
var badge = badges[key],
|
||||
css = badge.iamge ? 'background-image:url("' + badge.image + '");' : '';
|
||||
|
||||
if ( badge.color )
|
||||
css += 'background-color:' + badge.color + ';';
|
||||
|
||||
if ( badge.extra_css )
|
||||
css += badge.extra_css;
|
||||
|
||||
output += '<div class="badge float-left tooltip ' + badge.klass + '"' + (css ? ' style="' + css + '"' : '') + ' title="' + badge.title + '"></div>';
|
||||
}
|
||||
|
||||
badges_el.innerHTML = output;
|
||||
badges_el.innerHTML = this.render_badges(badges);
|
||||
}
|
||||
|
||||
if ( ! reprocess && from_el ) {
|
||||
|
@ -243,8 +230,14 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
|||
own: node.classList.contains('mentioning')
|
||||
});
|
||||
|
||||
else
|
||||
else {
|
||||
this.log("Unknown Tag Type: " + node.tagName);
|
||||
tokens.push({
|
||||
isRaw: true,
|
||||
html: node.outerHTML
|
||||
});
|
||||
}
|
||||
|
||||
} else
|
||||
this.log("Unknown Node Type Tokenizing Message: " + node.nodeType);
|
||||
}
|
||||
|
@ -274,4 +267,8 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
|||
|
||||
// Now, put the content back into the element.
|
||||
message_el.innerHTML = this.render_tokens(tokens);
|
||||
|
||||
// Interactions
|
||||
jQuery('a.deleted-link', message_el).click(f._deleted_link_click);
|
||||
jQuery('img.emoticon', message_el).click(function(e) { f._click_emote(e.target, e); });
|
||||
}
|
14
src/main.js
14
src/main.js
|
@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; }
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 77,
|
||||
major: 3, minor: 5, revision: 83,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ require('./ext/emote_menu');
|
|||
|
||||
require('./featurefriday');
|
||||
|
||||
require('./ui/popups');
|
||||
require('./ui/styles');
|
||||
require('./ui/dark');
|
||||
require('./ui/tooltips');
|
||||
|
@ -256,9 +257,12 @@ FFZ.prototype.init_normal = function(delay, no_socket) {
|
|||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
this.setup_popups();
|
||||
|
||||
if ( ! no_socket )
|
||||
if ( ! no_socket ) {
|
||||
this.setup_time();
|
||||
this.ws_create();
|
||||
}
|
||||
|
||||
this.setup_colors();
|
||||
this.setup_emoticons();
|
||||
|
@ -294,8 +298,11 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
this.setup_popups();
|
||||
|
||||
this.setup_time();
|
||||
this.ws_create();
|
||||
|
||||
this.setup_colors();
|
||||
this.setup_emoticons();
|
||||
this.setup_badges();
|
||||
|
@ -336,8 +343,11 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
// Start this early, for quick loading.
|
||||
this.setup_dark();
|
||||
this.setup_css();
|
||||
this.setup_popups();
|
||||
|
||||
this.setup_time();
|
||||
this.ws_create();
|
||||
|
||||
this.setup_emoticons();
|
||||
this.setup_badges();
|
||||
|
||||
|
|
110
src/socket.js
110
src/socket.js
|
@ -1,11 +1,30 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require('./constants');
|
||||
constants = require('./constants'),
|
||||
utils = require('./utils'),
|
||||
|
||||
pick_server = function(pool) {
|
||||
var total = 0, i = pool.length, val;
|
||||
while(i--)
|
||||
total += pool[i][1];
|
||||
|
||||
val = Math.random() * total;
|
||||
for(i = 0; i < pool.length; i++) {
|
||||
val -= pool[i][1];
|
||||
if ( val <= 0 )
|
||||
return i;
|
||||
}
|
||||
|
||||
return pool.length - 1;
|
||||
};
|
||||
|
||||
|
||||
FFZ.prototype._ws_open = false;
|
||||
FFZ.prototype._ws_delay = 0;
|
||||
FFZ.prototype._ws_host_idx = -1;
|
||||
FFZ.prototype._ws_current_pool = -1;
|
||||
|
||||
FFZ.prototype._ws_server_offset = null;
|
||||
|
||||
|
||||
FFZ.ws_commands = {};
|
||||
FFZ.ws_on_close = [];
|
||||
|
@ -15,7 +34,7 @@ FFZ.ws_on_close = [];
|
|||
// Settings
|
||||
// ----------------
|
||||
|
||||
var ffz_socket_seed;
|
||||
/*var ffz_socket_seed;
|
||||
|
||||
try {
|
||||
ffz_socket_seed = JSON.parse(localStorage.ffz_socket_seed);
|
||||
|
@ -24,7 +43,7 @@ try {
|
|||
if ( ! ffz_socket_seed ) {
|
||||
ffz_socket_seed = Math.random();
|
||||
localStorage.ffz_socket_seed = JSON.stringify(ffz_socket_seed);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
FFZ.settings_info.socket_server_pool = {
|
||||
|
@ -35,7 +54,7 @@ FFZ.settings_info.socket_server_pool = {
|
|||
2: "Development"
|
||||
},
|
||||
|
||||
value: ffz_socket_seed > 0.4 ? 1 : 0,
|
||||
value: 1,
|
||||
|
||||
process_value: function(val) {
|
||||
if ( typeof val === "string" )
|
||||
|
@ -95,14 +114,14 @@ FFZ.prototype.ws_create = function() {
|
|||
return;
|
||||
|
||||
if ( this._ws_host_idx < 0 )
|
||||
this._ws_host_idx = Math.floor(Math.random() * pool.length);
|
||||
this._ws_host_idx = pick_server(pool);
|
||||
|
||||
var server = pool[this._ws_host_idx];
|
||||
var server = pool[this._ws_host_idx][0];
|
||||
|
||||
this.log("Using Socket Server: " + server + " [" + pool_id + ":" + this._ws_host_idx + "]");
|
||||
|
||||
try {
|
||||
ws = this._ws_sock = new WebSocket(pool[this._ws_host_idx]);
|
||||
ws = this._ws_sock = new WebSocket(server);
|
||||
} catch(err) {
|
||||
this._ws_exists = false;
|
||||
return this.log("Error Creating WebSocket: " + err);
|
||||
|
@ -116,6 +135,7 @@ FFZ.prototype.ws_create = function() {
|
|||
f.log("Socket Connected.");
|
||||
|
||||
// Hard-code the first command.
|
||||
f._ws_ping_time = window.performance ? performance.now() : Date.now();
|
||||
ws.send("1 hello " + JSON.stringify(["ffz_" + FFZ.version_info, localStorage.ffzClientId]));
|
||||
|
||||
var user = f.get_user();
|
||||
|
@ -303,30 +323,70 @@ FFZ.prototype._ws_on_hello = function(success, data) {
|
|||
if ( ! success )
|
||||
return this.log("Error Saying Hello: " + data);
|
||||
|
||||
localStorage.ffzClientId = data;
|
||||
this.log("Client ID: " + data);
|
||||
this._ws_on_pong(success, data[1]);
|
||||
|
||||
/*var survey = {},
|
||||
set = survey['settings'] = {};
|
||||
|
||||
for(var key in FFZ.settings_info)
|
||||
set[key] = this.settings[key];
|
||||
|
||||
set["keywords"] = this.settings.keywords.length;
|
||||
set["banned_words"] = this.settings.banned_words.length;
|
||||
localStorage.ffzClientId = data[0];
|
||||
this.log("Client ID: " + localStorage.ffzClientId);
|
||||
}
|
||||
|
||||
|
||||
// Detect BTTV.
|
||||
survey['bttv'] = this.has_bttv || !!document.head.querySelector('script[src*="betterttv"]');
|
||||
// -----------------
|
||||
// Time Calculation
|
||||
// -----------------
|
||||
|
||||
FFZ.prototype.setup_time = function() {
|
||||
var last_time = Date.now(),
|
||||
f = this;
|
||||
|
||||
// Client Info
|
||||
survey['user-agent'] = navigator.userAgent;
|
||||
survey['screen'] = [screen.width, screen.height];
|
||||
survey['language'] = navigator.language;
|
||||
survey['platform'] = navigator.platform;
|
||||
setInterval(function() {
|
||||
var new_time = Date.now(),
|
||||
difference = (new_time - last_time) - 5000;
|
||||
|
||||
this.ws_send("survey", [survey]);*/
|
||||
last_time = new_time;
|
||||
if ( Math.abs(difference) > 250 ) {
|
||||
f.log("WARNING! Time drift of " + difference + "ms across 5 seconds. Did the local time change?");
|
||||
f._ws_server_offset = null;
|
||||
f.ws_ping();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
FFZ.prototype.ws_ping = function() {
|
||||
// Only 1 ping at a time.
|
||||
if ( this._ws_ping_time )
|
||||
return;
|
||||
|
||||
this._ws_ping_time = window.performance ? performance.now() : Date.now();
|
||||
if ( ! this.ws_send("ping", null, this._ws_on_pong.bind(this)) )
|
||||
this._ws_ping_time = null;
|
||||
}
|
||||
|
||||
FFZ.prototype._ws_on_pong = function(success, server_time) {
|
||||
var d_now = Date.now(),
|
||||
now = window.performance ? performance.now() : d_now;
|
||||
|
||||
if ( ! success ) {
|
||||
this._ws_ping_time = null;
|
||||
this.log("Error Pinging Server: " + server_time);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this._ws_ping_time ) {
|
||||
var rtt = now - this._ws_ping_time,
|
||||
ping = rtt / 2;
|
||||
|
||||
this._ws_ping_time = null;
|
||||
this._ws_server_offset = (d_now - (server_time + ping));
|
||||
|
||||
this.log("Server Time: " + new Date(server_time).toISOString());
|
||||
this.log("Local Time: " + new Date(d_now).toISOString());
|
||||
this.log("Estimated Ping: " + ping + "ms");
|
||||
this.log("Time Offset: " + (this._ws_server_offset < 0 ? "-" : "") + utils.time_to_string(Math.abs(this._ws_server_offset) / 1000));
|
||||
|
||||
if ( Math.abs(this._ws_server_offset) > 300000 ) {
|
||||
this.log("WARNING! The time offset with the server is greater than 5 minutes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,6 +30,85 @@
|
|||
}
|
||||
|
||||
|
||||
/* DEPRECIATED: Whisper Backgrounds */
|
||||
.ember-chat .chat-line.whisper-line:before {
|
||||
background-color: rgba(185, 163, 227, 0.2);
|
||||
}
|
||||
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before {
|
||||
background-color: rgba(185, 163, 227, 0.4);
|
||||
}
|
||||
|
||||
.theatre .chat-line.whisper-line:before,
|
||||
.dark .chat-line.whisper-line:before,
|
||||
.force-dark .chat-line.whisper-line:before {
|
||||
background-color: rgba(100, 65, 165, 0.2);
|
||||
}
|
||||
|
||||
.theatre .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before,
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before {
|
||||
background-color: rgba(100, 65, 165, 0.4);
|
||||
}
|
||||
|
||||
|
||||
/* Chat History: Original Sender */
|
||||
.chat-history .chat-line.original-sender:before {
|
||||
background-color: rgba(0,127,255, 0.2);
|
||||
}
|
||||
|
||||
.chat-history .chat-line.original-sender:nth-child(2n+0):before {
|
||||
background-color: rgba(0,127,255, 0.4);
|
||||
}
|
||||
|
||||
.theatre .chat-history .chat-line.original-sender:before,
|
||||
.dark .chat-history .chat-line.original-sender:before,
|
||||
.force-dark .chat-history .chat-line.original-sender:before {
|
||||
background-color: rgba(0,63,127, 0.2);
|
||||
}
|
||||
|
||||
.theatre .chat-history .chat-line.original-sender:nth-child(2n+0):before,
|
||||
.dark .chat-history .chat-line.original-sender:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line.original-sender:nth-child(2n+0):before {
|
||||
background-color: rgba(0,63,127, 0.4);
|
||||
}
|
||||
|
||||
|
||||
/* Chat History: Original Message */
|
||||
.chat-history .chat-line.original-msg:before {
|
||||
background-color: rgba(0,255,127, 0.4);
|
||||
}
|
||||
|
||||
.chat-history .chat-line.original-msg:nth-child(2n+0):before {
|
||||
background-color: rgba(0,255,127, 0.8);
|
||||
}
|
||||
|
||||
.theatre .chat-history .chat-line.original-msg:before,
|
||||
.dark .chat-history .chat-line.original-msg:before,
|
||||
.force-dark .chat-history .chat-line.original-msg:before {
|
||||
background-color: rgba(0,127,63, 0.4);
|
||||
}
|
||||
|
||||
.theatre .chat-history .chat-line.original-msg:nth-child(2n+0):before,
|
||||
.dark .chat-history .chat-line.original-msg:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line.original-msg:nth-child(2n+0):before {
|
||||
background-color: rgba(0,127,63, 0.8);
|
||||
}
|
||||
|
||||
|
||||
/* Reading contrast */
|
||||
.chat-history .chat-line.original-msg span.has-color {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.theatre .chat-history .chat-line.original-msg span.has-color,
|
||||
.dark .chat-history .chat-line.original-msg span.has-color,
|
||||
.force-dark .chat-history .chat-line.original-msg span.has-color {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* DEPRECIATED: Mention Backgrounds */
|
||||
.chat-history .chat-line.ffz-mentioned:before,
|
||||
|
@ -98,4 +177,17 @@
|
|||
.ember-chat-container.force-dark .chat-line .mentioning {
|
||||
color: #8c8c8c;
|
||||
background-color: rgba(16,16,16, 0.75);
|
||||
}
|
||||
|
||||
|
||||
/* Fix Conversations */
|
||||
|
||||
.conversation-window .timestamp-line span,
|
||||
.conversation-window .new-message-divider span {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.conversation-window .new-message-divider:after,
|
||||
.conversation-window .timestamp-line:after {
|
||||
display: none;
|
||||
}
|
|
@ -443,7 +443,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
|
||||
for(var i=0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if ( msgObject.style !== 'whisper' && (_.isString(token) || ! token.mentionedUser || token.own) )
|
||||
if ( _.isString(token) || ! token.mentionedUser || token.own )
|
||||
continue;
|
||||
|
||||
// We have a mention!
|
||||
|
@ -544,7 +544,10 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
if ( token.hidden )
|
||||
return "";
|
||||
|
||||
if ( token.emoticonSrc ) {
|
||||
else if ( token.isRaw )
|
||||
return token.html;
|
||||
|
||||
else if ( token.emoticonSrc ) {
|
||||
var tooltip, src = token.emoticonSrc, srcset, cls, extra;
|
||||
if ( token.ffzEmote ) {
|
||||
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
||||
|
@ -607,7 +610,7 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
return '<img class="emoticon html-tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
}
|
||||
|
||||
if ( token.isLink ) {
|
||||
else if ( token.isLink ) {
|
||||
var text = token.title || (token.isLong && '<long link>') || (token.isShortened && '<shortened link>') || (token.isDeleted && '<deleted link>') || token.href;
|
||||
|
||||
if ( ! render_links && render_links !== undefined )
|
||||
|
@ -676,10 +679,10 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
return '<a class="' + cls + '" data-original-url="' + utils.quote_attr(token.href) + '" data-url="' + utils.quote_attr(actual_href) + '" href="' + utils.quote_attr(href || '#') + '" title="' + utils.quote_attr(tooltip || '') + '" target="_blank">' + utils.sanitize(text) + '</a>';
|
||||
}
|
||||
|
||||
if ( token.mentionedUser )
|
||||
else if ( token.mentionedUser )
|
||||
return '<span class="' + (token.own ? "mentioning" : "mentioned") + '">' + utils.sanitize(token.mentionedUser) + "</span>";
|
||||
|
||||
if ( token.deletedLink )
|
||||
else if ( token.deletedLink )
|
||||
return utils.sanitize(token.text);
|
||||
|
||||
return utils.sanitize(token);
|
||||
|
@ -1017,4 +1020,76 @@ FFZ.prototype._deleted_link_click = function(e) {
|
|||
|
||||
// Stop from Navigating
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// History Loading
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.parse_history = function(history, purged, room_id, delete_links, tmiSession, per_line) {
|
||||
var i = history.length, was_cleared = false;
|
||||
purged = purged || {};
|
||||
|
||||
while(i--) {
|
||||
var msg = history[i],
|
||||
is_deleted = msg.ffz_deleted = purged[msg.from] || false;
|
||||
|
||||
if ( is_deleted && ! this.settings.prevent_clear )
|
||||
msg.deleted = true;
|
||||
|
||||
if ( ! msg.room && room_id )
|
||||
msg.room = room_id;
|
||||
|
||||
if ( typeof msg.date === "string" || typeof msg.date === "number" )
|
||||
msg.date = utils.parse_date(msg.date);
|
||||
|
||||
if ( ! msg.color )
|
||||
msg.color = msg.tags && msg.tags.color ? msg.tags.color : tmiSession && msg.from ? tmiSession.getColor(msg.from) : "#755000";
|
||||
|
||||
if ( ! msg.labels || ! msg.labels.length ) {
|
||||
var labels = msg.labels = [];
|
||||
|
||||
if ( msg.room && msg.room === msg.from )
|
||||
labels.push("owner");
|
||||
else if ( msg.tags ) {
|
||||
var ut = msg.tags['user-type'];
|
||||
if ( ut === 'mod' || ut === 'staff' || ut === 'admin' || ut === 'global_mod' )
|
||||
labels.push(ut);
|
||||
}
|
||||
|
||||
if ( msg.tags ) {
|
||||
if ( msg.tags.turbo )
|
||||
labels.push("turbo");
|
||||
if ( msg.tags.subscriber )
|
||||
labels.push("subscriber");
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! msg.style ) {
|
||||
if ( msg.from === "jtv" )
|
||||
msg.style = "admin";
|
||||
else if ( msg.from === "twitchnotify" )
|
||||
msg.style = "notification";
|
||||
}
|
||||
|
||||
if ( msg.tags && typeof msg.tags.emotes === "string" )
|
||||
msg.tags.emotes = utils.uncompressEmotes(msg.tags.emotes);
|
||||
|
||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||
this.tokenize_chat_line(msg, true, delete_links);
|
||||
|
||||
// CLEARCHAT
|
||||
if ( msg.tags && msg.tags.target === '@@' )
|
||||
was_cleared = true;
|
||||
|
||||
else if ( msg.tags && msg.tags.target )
|
||||
purged[msg.tags.target] = true;
|
||||
|
||||
// Per-line
|
||||
if ( per_line && ! per_line(msg) )
|
||||
break;
|
||||
}
|
||||
|
||||
return [history, purged, was_cleared];
|
||||
}
|
|
@ -214,6 +214,6 @@ FFZ.prototype._load_dark_css = function() {
|
|||
|
||||
s.id = "ffz-dark-css";
|
||||
s.setAttribute('rel', 'stylesheet');
|
||||
s.setAttribute('href', constants.DIRECT_SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
s.setAttribute('href', constants.DIRECT_SERVER + "script/dark" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
document.head.appendChild(s);
|
||||
}
|
|
@ -180,7 +180,8 @@ FFZ.prototype._build_following_tooltip = function(el) {
|
|||
}
|
||||
|
||||
var up_since = this.settings.stream_uptime && stream.created_at && utils.parse_date(stream.created_at),
|
||||
uptime = up_since && Math.floor((Date.now() - up_since.getTime()) / 1000) || 0,
|
||||
now = Date.now() - (this._ws_server_offset || 0),
|
||||
uptime = up_since && Math.floor((now - up_since.getTime()) / 1000) || 0,
|
||||
minutes = Math.floor(uptime / 60) % 60,
|
||||
hours = Math.floor(uptime / 3600);
|
||||
|
||||
|
|
|
@ -281,7 +281,7 @@ FFZ.prototype.rebuild_following_ui = function() {
|
|||
FFZ.prototype._build_following_button = function(container, channel_id) {
|
||||
if ( ! VALID_CHANNEL.test(channel_id) )
|
||||
return this.log("Ignoring Invalid Channel: " + utils.sanitize(channel_id));
|
||||
|
||||
|
||||
var btn = document.createElement('a'), f = this,
|
||||
btn_c = document.createElement('div'),
|
||||
noti = document.createElement('a'),
|
||||
|
@ -376,7 +376,7 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
btn.addEventListener('mousedown', function(e) {
|
||||
if ( e.button !== 1 )
|
||||
return;
|
||||
|
||||
|
||||
e.preventDefault();
|
||||
window.open(Twitch.uri.profile(channel_id));
|
||||
});
|
||||
|
@ -396,7 +396,7 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
|
||||
display_name = FFZ.get_capitalization(channel_id, on_name);
|
||||
update();
|
||||
|
||||
|
||||
setTimeout(check_following, Math.random()*5000);
|
||||
|
||||
container.appendChild(btn_c);
|
||||
|
@ -405,19 +405,11 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
|
||||
|
||||
FFZ.prototype._build_following_popup = function(container, channel_id, notifications) {
|
||||
var popup = this._popup, out = '',
|
||||
var popup = this.close_popup(), out = '',
|
||||
pos = container.offsetLeft + container.offsetWidth;
|
||||
|
||||
|
||||
if ( popup ) {
|
||||
popup.parentElement.removeChild(popup);
|
||||
delete this._popup;
|
||||
this._popup_kill && this._popup_kill();
|
||||
delete this._popup_kill;
|
||||
|
||||
if ( popup.id == "ffz-following-popup" && popup.getAttribute('data-channel') === channel_id )
|
||||
return null;
|
||||
}
|
||||
if ( popup && popup.id == "ffz-following-popup" && popup.getAttribute('data-channel') === channel_id )
|
||||
return null;
|
||||
|
||||
popup = this._popup = document.createElement('div');
|
||||
popup.id = 'ffz-following-popup';
|
||||
|
|
|
@ -35,26 +35,6 @@ var FFZ = window.FrankerFaceZ,
|
|||
// --------------------
|
||||
|
||||
FFZ.prototype.setup_menu = function() {
|
||||
this.log("Installing mouse-up event to auto-close menus.");
|
||||
var f = this;
|
||||
|
||||
jQuery(document).mouseup(function(e) {
|
||||
var popup = f._popup, parent;
|
||||
if ( ! popup ) return;
|
||||
if ( popup.id === 'ffz-chat-menu' && popup.style && popup.style.left )
|
||||
return;
|
||||
|
||||
popup = jQuery(popup);
|
||||
parent = popup.parent();
|
||||
|
||||
if ( ! parent.is(e.target) && parent.has(e.target).length === 0 ) {
|
||||
popup.remove();
|
||||
delete f._popup;
|
||||
f._popup_kill && f._popup_kill();
|
||||
delete f._popup_kill;
|
||||
}
|
||||
});
|
||||
|
||||
document.body.classList.toggle("ffz-menu-replace", this.settings.replace_twitch_menu);
|
||||
|
||||
// Add FFZ to the chat settings menu.
|
||||
|
@ -62,7 +42,8 @@ FFZ.prototype.setup_menu = function() {
|
|||
this.log("Hooking the Ember Chat Settings view.");
|
||||
|
||||
var Settings = window.App && App.__container__.resolve('view:settings'),
|
||||
Layout = App.__container__.lookup('controller:layout');
|
||||
Layout = window.App && App.__container__.lookup('controller:layout'),
|
||||
f = this;
|
||||
|
||||
if ( ! Settings )
|
||||
return;
|
||||
|
@ -215,14 +196,9 @@ FFZ.prototype._fix_menu_position = function() {
|
|||
}
|
||||
|
||||
FFZ.prototype.build_ui_popup = function(view) {
|
||||
var popup = this._popup;
|
||||
if ( popup ) {
|
||||
popup.parentElement.removeChild(popup);
|
||||
delete this._popup;
|
||||
this._popup_kill && this._popup_kill();
|
||||
delete this._popup_kill;
|
||||
var popup = this._popup ? this.close_popup() : this._last_popup;
|
||||
if ( popup && popup.id === 'ffz-chat-menu' )
|
||||
return;
|
||||
}
|
||||
|
||||
// Start building the DOM.
|
||||
var container = document.createElement('div'),
|
||||
|
@ -271,12 +247,8 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
close_btn.addEventListener('click', function() {
|
||||
var popup = f._popup;
|
||||
if ( can_close && popup ) {
|
||||
popup.parentElement.removeChild(popup);
|
||||
delete f._popup;
|
||||
f._popup_kill && f._popup_kill();
|
||||
delete f._popup_kill;
|
||||
}
|
||||
if ( can_close && popup )
|
||||
f.close_popup();
|
||||
});
|
||||
|
||||
menu.appendChild(heading);
|
||||
|
@ -353,9 +325,12 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
|
||||
// Add the menu to the DOM.
|
||||
this._popup = container;
|
||||
sub_container.style.maxHeight = Math.max(200, view.$().height() - 172) + "px";
|
||||
view.$('.chat-interface').append(container);
|
||||
|
||||
// Keep track of the pop-up.
|
||||
this._popup = container;
|
||||
this._popup_allow_parent = true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -511,7 +486,8 @@ FFZ.menu_pages.channel = {
|
|||
sub_message = document.createElement("div"),
|
||||
nonsub_message = document.createElement("div"),
|
||||
unlock_text = document.createElement("span"),
|
||||
end_time = ends_at ? Math.floor((ends_at.getTime() - Date.now()) / 1000) : null;
|
||||
now = Date.now() - (this._ws_server_offset || 0),
|
||||
end_time = ends_at ? Math.floor((ends_at.getTime() - now) / 1000) : null;
|
||||
|
||||
sub_message.className = "subscribe-message";
|
||||
nonsub_message.className = "non-subscriber-message";
|
||||
|
|
84
src/ui/popups.js
Normal file
84
src/ui/popups.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
var FFZ = window.FrankerFaceZ;
|
||||
|
||||
|
||||
// ---------------
|
||||
// Initialization
|
||||
// ---------------
|
||||
|
||||
FFZ.prototype.setup_popups = function() {
|
||||
this.log("Installing mouse-up event to auto-close pop-ups.");
|
||||
var f = this;
|
||||
|
||||
jQuery(document).mouseup(function(e) {
|
||||
if ( e.button && e.button !== 0 )
|
||||
return;
|
||||
|
||||
var popup = f._popup,
|
||||
parent = f._popup_parent;
|
||||
|
||||
if ( ! popup )
|
||||
f._last_popup = undefined;
|
||||
|
||||
if ( ! popup || popup === e.target || popup.contains(e.target) )
|
||||
return;
|
||||
|
||||
if ( popup.id === 'ffz-chat-menu' && popup.style && popup.style.left )
|
||||
return;
|
||||
|
||||
if ( f._popup_allow_parent ) {
|
||||
var parent = f._popup_parent || popup.parentElement;
|
||||
if ( parent && ( parent === e.target || parent.contains(e.target) ) )
|
||||
return;
|
||||
}
|
||||
|
||||
f.close_popup();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ---------------
|
||||
// Management
|
||||
// ---------------
|
||||
|
||||
FFZ.prototype.close_popup = function() {
|
||||
var popup = this._popup;
|
||||
this._last_popup = popup;
|
||||
if ( ! popup )
|
||||
return;
|
||||
|
||||
popup.parentElement.removeChild(popup);
|
||||
|
||||
if ( this._popup_kill )
|
||||
try {
|
||||
this._popup_kill();
|
||||
} catch(err) {
|
||||
this.error("_popup_kill: " + err);
|
||||
}
|
||||
|
||||
this._popup = undefined;
|
||||
this._popup_parent = undefined;
|
||||
this._popup_kill = undefined;
|
||||
this._popup_allow_parent = undefined;
|
||||
return popup;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.show_popup = function(el, position, container, cleanup, allow_parent, dont_insert_handler) {
|
||||
if ( this._popup )
|
||||
this.close_popup();
|
||||
|
||||
this._popup = el;
|
||||
this._popup_allow_parent = allow_parent || false;
|
||||
this._popup_kill = cleanup;
|
||||
|
||||
container = container || document.querySelector('.app-main') || document.body;
|
||||
|
||||
var bounds = container.getBoundingClientRect();
|
||||
|
||||
el.style.display = 'block';
|
||||
el.style.position = 'absolute';
|
||||
el.style.left = (position[0] - bounds.left) + 'px';
|
||||
el.style.top = (position[1] - bounds.top) + 'px';
|
||||
|
||||
container.appendChild(el);
|
||||
}
|
|
@ -180,16 +180,9 @@ FFZ.prototype._race_kill = function() {
|
|||
|
||||
|
||||
FFZ.prototype._build_race_popup = function(container, channel_id) {
|
||||
var popup = this._popup;
|
||||
if ( popup ) {
|
||||
popup.parentElement.removeChild(popup);
|
||||
delete this._popup;
|
||||
this._popup_kill && this._popup_kill();
|
||||
delete this._popup_kill;
|
||||
|
||||
if ( popup.id === "ffz-race-popup" && popup.getAttribute('data-channel') === channel_id )
|
||||
return;
|
||||
}
|
||||
var popup = this.close_popup();
|
||||
if ( popup && popup.id === "ffz-race-popup" && popup.getAttribute('data-channel') === channel_id )
|
||||
return;
|
||||
|
||||
if ( ! container )
|
||||
return;
|
||||
|
@ -204,6 +197,7 @@ FFZ.prototype._build_race_popup = function(container, channel_id) {
|
|||
popup.className = (pos >= 300 ? 'right' : 'left') + ' share dropmenu';
|
||||
|
||||
this._popup_kill = this._race_kill.bind(this);
|
||||
this._popup_allow_parent = true;
|
||||
this._popup = popup;
|
||||
|
||||
var link = 'http://kadgar.net/live',
|
||||
|
@ -256,13 +250,8 @@ FFZ.prototype._update_race = function(container, not_timer) {
|
|||
if ( ! race ) {
|
||||
// No race. Abort.
|
||||
container.parentElement.removeChild(container);
|
||||
if ( this._popup && this._popup.id === 'ffz-race-popup' && this._popup.getAttribute('data-channel') === channel_id ) {
|
||||
this._popup_kill && this._popup_kill();
|
||||
if ( this._popup ) {
|
||||
delete this._popup;
|
||||
delete this._popup_kill;
|
||||
}
|
||||
}
|
||||
if ( this._popup && this._popup.id === 'ffz-race-popup' && this._popup.getAttribute('data-channel') === channel_id )
|
||||
this.close_popup();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -270,7 +259,7 @@ FFZ.prototype._update_race = function(container, not_timer) {
|
|||
entrant = race.entrants[entrant_id],
|
||||
|
||||
popup = container.querySelector('#ffz-race-popup'),
|
||||
now = Date.now() / 1000,
|
||||
now = (Date.now() - (this._ws_server_offset || 0)) / 1000,
|
||||
elapsed = Math.floor(now - race.time);
|
||||
|
||||
container.querySelector('.logo').innerHTML = utils.placement(entrant);
|
||||
|
|
|
@ -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.DIRECT_SERVER + "script/style.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
s.setAttribute('href', constants.DIRECT_SERVER + "script/style" + (constants.DEBUG ? "" : ".min") + ".css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||
document.head.appendChild(s);
|
||||
|
||||
this.log("Readying toggleable styles.");
|
||||
|
|
52
src/utils.js
52
src/utils.js
|
@ -25,6 +25,16 @@ var sanitize_el = document.createElement('span'),
|
|||
return msg.replace(R_AMP, "&").replace(R_QUOTE, """).replace(R_SQUOTE, "'").replace(R_LT, "<").replace(R_GT, ">");
|
||||
},
|
||||
|
||||
HUMAN_NUMBERS = [
|
||||
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
|
||||
],
|
||||
|
||||
number_commas = function(x) {
|
||||
var parts = x.toString().split(".");
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
return parts.join(".");
|
||||
},
|
||||
|
||||
pluralize = function(value, singular, plural) {
|
||||
plural = plural || 's';
|
||||
singular = singular || '';
|
||||
|
@ -157,6 +167,37 @@ var sanitize_el = document.createElement('span'),
|
|||
return tags;
|
||||
},
|
||||
|
||||
uncompressEmotes = function(value) {
|
||||
var output = {},
|
||||
emotes = value.split("/"),
|
||||
i = emotes.length;
|
||||
|
||||
while(i--) {
|
||||
var parts = emotes[i].split(":");
|
||||
if ( parts.length !== 3 )
|
||||
return {};
|
||||
|
||||
var emote_id = parts[0],
|
||||
length = parseInt(parts[1]),
|
||||
positions = parts[2].split(","),
|
||||
indices = output[emote_id] = output[emote_id] || [];
|
||||
|
||||
for(var j=0, jl = positions.length; j < jl; j++) {
|
||||
var start = parseInt(positions[j]),
|
||||
end = start + length;
|
||||
|
||||
for(var x=0, xl = indices.length; x < xl; x++) {
|
||||
if ( start < indices[x][0] )
|
||||
break;
|
||||
}
|
||||
|
||||
indices.splice(x, 0, [start, end]);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
},
|
||||
|
||||
|
||||
EMOJI_CODEPOINTS = {},
|
||||
emoji_to_codepoint = function(icon, variant) {
|
||||
|
@ -316,16 +357,13 @@ module.exports = {
|
|||
|
||||
splitIRCMessage: splitIRCMessage,
|
||||
parseIRCTags: parseIRCTags,
|
||||
uncompressEmotes: uncompressEmotes,
|
||||
|
||||
emoji_to_codepoint: emoji_to_codepoint,
|
||||
|
||||
parse_date: parse_date,
|
||||
|
||||
number_commas: function(x) {
|
||||
var parts = x.toString().split(".");
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
return parts.join(".");
|
||||
},
|
||||
number_commas: number_commas,
|
||||
|
||||
place_string: place_string,
|
||||
|
||||
|
@ -345,6 +383,10 @@ module.exports = {
|
|||
|
||||
pluralize: pluralize,
|
||||
|
||||
human_number: function(value) {
|
||||
return HUMAN_NUMBERS[value] || number_commas(value);
|
||||
},
|
||||
|
||||
human_time: function(elapsed, factor) {
|
||||
factor = factor || 1;
|
||||
elapsed = Math.floor(elapsed);
|
||||
|
|
190
style.css
190
style.css
|
@ -902,6 +902,49 @@ span.ffz-handle:after { left: 8px }
|
|||
.ffz-ui-popup.dark li.title { color: #fff; }
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page { background-color: #1e1e1e; }
|
||||
|
||||
/* Host Menu */
|
||||
|
||||
.ffz-channel-selector {
|
||||
background-image: url("//cdn.frankerfacez.com/script/zreknarf-bg.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 50%;
|
||||
background-position: 110% 115%;
|
||||
}
|
||||
|
||||
.ffz-channel-selector .dropmenu_action {
|
||||
display: block;
|
||||
position: relative;
|
||||
line-height: 34px;
|
||||
overflow: hidden;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.ffz-channel-selector .image {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
float: left;
|
||||
margin: 6px 15px 8px 20px;
|
||||
}
|
||||
|
||||
.ffz-channel-selector .title {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.ffz-channel-selector .header {
|
||||
padding: 5px 20px;
|
||||
margin-bottom: 0;
|
||||
border-bottom: none;
|
||||
line-height: 22px;
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
/* Positioning Fixes */
|
||||
|
||||
|
@ -1187,6 +1230,7 @@ body:not(.ffz-bttv) .more-messages-indicator {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-history.loading:after,
|
||||
.ffz-ui-sub-menu-page:empty::after,
|
||||
.ffz-ui-menu-page:empty::after {
|
||||
content: " ";
|
||||
|
@ -1608,6 +1652,67 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
|
|||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chat-history .chat-line:not(.original-sender) span.from:hover,
|
||||
.chat-history .timestamp:hover {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.chat-history.loading {
|
||||
position: relative;
|
||||
overflow-y: hidden !important;
|
||||
}
|
||||
.chat-history.loading li { pointer-events: none; }
|
||||
|
||||
.chat-history .from { font-weight: 700; }
|
||||
|
||||
.chat-history.loading:before {
|
||||
content: " ";
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
top: 0; bottom: 0;
|
||||
left: 0; right: 0;
|
||||
background-color: rgba(128,128,128,0.8);
|
||||
}
|
||||
|
||||
.theatre .chat-history.loading:before,
|
||||
.dark .chat-history.loading:before,
|
||||
.force-dark .chat-history.loading:before {
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.chat-history.loading:after {
|
||||
visibility: visible;
|
||||
clear: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -40px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.ember-chat .moderation-card .back-button {
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
border-top: none;
|
||||
float: none;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.ember-chat .moderation-card .back-button:hover {
|
||||
background-color: #6441A5 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.theatre .moderation-card .back-button,
|
||||
.dark .moderation-card .back-button,
|
||||
.force-dark .moderation-card .back-button {
|
||||
background-color: #232329;
|
||||
}
|
||||
|
||||
|
||||
/* Room State */
|
||||
|
||||
.ffz.room-state.stat {
|
||||
|
@ -1815,6 +1920,9 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
background-color: #191919;
|
||||
}
|
||||
|
||||
.ffz-no-blue .theatre .moderation-card .back-button,
|
||||
.ffz-no-blue .dark .moderation-card .back-button,
|
||||
.ffz-no-blue .force-dark .moderation-card .back-button
|
||||
.ffz-no-blue .chat-container.dark .chat-interface .emoticon-selector .tabs,
|
||||
.ffz-no-blue .app-main.theatre .chat-container .chat-interface .emoticon-selector .tabs,
|
||||
.ffz-no-blue .chat-container.force-dark .chat-interface .emoticon-selector .tabs,
|
||||
|
@ -2069,8 +2177,52 @@ li[data-name="following"] a {
|
|||
margin-right: 20px;
|
||||
}
|
||||
|
||||
/* Chat Interactions Fixes */
|
||||
|
||||
body:not(.ffz-bttv) .ember-chat .chat-commands-dropdown {
|
||||
z-index: 998; /* 1 less than the header */
|
||||
padding: 0;
|
||||
margin-top: -1px;
|
||||
background-color: #9265d5 !important;
|
||||
}
|
||||
|
||||
body:not(.ffz-bttv) .ember-chat .chat-commands-dropdown li {
|
||||
border: none;
|
||||
}
|
||||
|
||||
body:not(.ffz-bttv) .ember-chat .chat-commands-dropdown li + li {
|
||||
border-top: 1px solid rgba(0,0,0,0.25);
|
||||
}
|
||||
|
||||
body:not(.ffz-bttv) .app-main.theatre .chat-container .chat-commands-dropdown li,
|
||||
body:not(.ffz-bttv) .dark .ember-chat .chat-commands-dropdown li,
|
||||
body:not(.ffz-bttv) .force-dark .ember-chat .chat-commands-dropdown li,
|
||||
body:not(.ffz-bttv) .ember-chat .chat-commands-dropdown li:hover {
|
||||
background-color: rgba(0,0,0,0.25) !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
body:not(.ffz-bttv) .app-main.theatre .chat-container .chat-commands-dropdown li:hover,
|
||||
body:not(.ffz-bttv) .dark .ember-chat .chat-commands-dropdown li:hover,
|
||||
body:not(.ffz-bttv) .force-dark .ember-chat .chat-commands-dropdown li:hover {
|
||||
background-color: rgba(0,0,0,0.75) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
/* Conversations */
|
||||
|
||||
body:not(.ffz-bttv) .conversation-window .new-message-divider + .timestamp-line {
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
/* Fix the ignore-cta covering up messages with no way to dismiss it. */
|
||||
body:not(.ffz-bttv) .conversation-window .ignore-cta + .conversation-content {
|
||||
padding-top: 76px;
|
||||
}
|
||||
|
||||
/* Hide that which should be hidden. */
|
||||
.conversation-window.collapsed .ignore-cta,
|
||||
.conversation-chat-line.action .colon,
|
||||
.conversation-input-bar .emoticon-selector .tabs,
|
||||
.conversation-preview-line .badges,
|
||||
|
@ -2237,4 +2389,42 @@ body:not(.ffz-conv-title-clickable) .conversation-header a.conversation-header-n
|
|||
|
||||
.user.item .actions .follow svg {
|
||||
margin: 4.5px 0 -4.5px -1px;
|
||||
}
|
||||
|
||||
/* Creative Directory */
|
||||
|
||||
.ffz-creative-tags .creativetag-list.filter-list,
|
||||
.ffz-creative-tags .creativetag-list ul,
|
||||
.ffz-creative-tags .creativetag-list-contain {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
overflow: inherit;
|
||||
}
|
||||
|
||||
body:not(.ffz-creative-showcase) .creative-hero,
|
||||
.ffz-creative-tags .creativetag-list-contain .filter-nav { display: none; }
|
||||
|
||||
.ffz-creative-tags .creativetag-list ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: inherit;
|
||||
margin-bottom: -5px;
|
||||
}
|
||||
|
||||
.ffz-creative-tags .creativetag-list li {
|
||||
flex-grow: 1;
|
||||
height: inherit;
|
||||
float: none;
|
||||
margin: 0 0 5px;
|
||||
}
|
||||
|
||||
.ffz-creative-tags .creativetag-list a {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.ffz-creative-tags .creativetag-list a.active {
|
||||
color: #fff !important;
|
||||
background-color: #6441a5 !important;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue