mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-05 10:38:30 +00:00
Pre dev stream
This commit is contained in:
parent
b184fc74b2
commit
6a62804ec1
26 changed files with 4658 additions and 732 deletions
27
gulpfile.js
27
gulpfile.js
|
@ -10,6 +10,12 @@ var fs = require('fs'),
|
||||||
rename = require('gulp-rename'),
|
rename = require('gulp-rename'),
|
||||||
uglify = require('gulp-uglify');
|
uglify = require('gulp-uglify');
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
var jsEscape = require('gulp-js-escape'),
|
||||||
|
wrap = require('gulp-wrap'),
|
||||||
|
declare = require('gulp-declare');
|
||||||
|
|
||||||
|
|
||||||
// Server Dependencies
|
// Server Dependencies
|
||||||
var http = require("http"),
|
var http = require("http"),
|
||||||
path = require("path"),
|
path = require("path"),
|
||||||
|
@ -31,7 +37,26 @@ gulp.task('prepare', ['clean'], function() {
|
||||||
.pipe(gulp.dest('build/'));
|
.pipe(gulp.dest('build/'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('scripts', ['prepare'], function() {
|
|
||||||
|
gulp.task('templates', ['prepare'], function() {
|
||||||
|
gulp.src(['build/templates/**/*.hbs'])
|
||||||
|
.pipe(jsEscape())
|
||||||
|
.pipe(wrap('Handlebars.compile(<%= contents %>)'))
|
||||||
|
.pipe(declare({
|
||||||
|
root: 'exports',
|
||||||
|
noRedeclare: true,
|
||||||
|
processName: function(filePath) {
|
||||||
|
var match = filePath.match(/build[\\\/]templates[\\\/](.*)\.hbs$/);
|
||||||
|
return declare.processNameByPath((match && match.length > 1) ? match[1] : filePath);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.pipe(concat('templates.js'))
|
||||||
|
.pipe(gulp.dest('build/'))
|
||||||
|
.on('error', util.log);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
gulp.task('scripts', ['prepare', 'templates'], function() {
|
||||||
gulp.src(['build/main.js'])
|
gulp.src(['build/main.js'])
|
||||||
.pipe(browserify())
|
.pipe(browserify())
|
||||||
.pipe(concat('script.js'))
|
.pipe(concat('script.js'))
|
||||||
|
|
2397
script.js
2397
script.js
File diff suppressed because one or more lines are too long
10
script.min.js
vendored
10
script.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -53,7 +53,7 @@ FFZ.ws_commands.set_badge = function(data) {
|
||||||
user = this.users[user_id] = this.users[user_id] || {},
|
user = this.users[user_id] = this.users[user_id] || {},
|
||||||
badges = user.badges = user.badges || {};
|
badges = user.badges = user.badges || {};
|
||||||
|
|
||||||
if ( badge === undefined )
|
if ( badge === undefined || badge === null )
|
||||||
delete badges[slot];
|
delete badges[slot];
|
||||||
else
|
else
|
||||||
badges[slot] = badge;
|
badges[slot] = badge;
|
||||||
|
@ -86,6 +86,9 @@ FFZ.prototype.bttv_badges = function(data) {
|
||||||
if ( ! user || ! user.badges )
|
if ( ! user || ! user.badges )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( ! data.badges )
|
||||||
|
data.badges = [];
|
||||||
|
|
||||||
// Determine where in the list to insert these badges.
|
// Determine where in the list to insert these badges.
|
||||||
for(var i=0; i < data.badges.length; i++) {
|
for(var i=0; i < data.badges.length; i++) {
|
||||||
var badge = data.badges[i];
|
var badge = data.badges[i];
|
||||||
|
@ -236,7 +239,7 @@ FFZ.prototype.render_badge = function(component) {
|
||||||
|
|
||||||
if ( reverse ) {
|
if ( reverse ) {
|
||||||
while(badges_out.length)
|
while(badges_out.length)
|
||||||
badges.before(badges_out.shift()[1]);
|
before.before(badges_out.shift()[1]);
|
||||||
} else {
|
} else {
|
||||||
while(badges_out.length)
|
while(badges_out.length)
|
||||||
badges.append(badges_out.shift()[1]);
|
badges.append(badges_out.shift()[1]);
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,12 @@ var FFZ = window.FrankerFaceZ,
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
FFZ.prototype.setup_channel = function() {
|
FFZ.prototype.setup_channel = function() {
|
||||||
|
// Style Stuff!
|
||||||
|
this.log("Creating channel style element.");
|
||||||
|
var s = this._channel_style = document.createElement("style");
|
||||||
|
s.id = "ffz-channel-css";
|
||||||
|
document.head.appendChild(s);
|
||||||
|
|
||||||
// Settings stuff!
|
// Settings stuff!
|
||||||
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
|
document.body.classList.toggle("ffz-hide-view-count", !this.settings.channel_views);
|
||||||
|
|
||||||
|
@ -72,13 +78,32 @@ FFZ.prototype.setup_channel = function() {
|
||||||
ffzHostTarget: function() {
|
ffzHostTarget: function() {
|
||||||
var target = this.get('content.hostModeTarget'),
|
var target = this.get('content.hostModeTarget'),
|
||||||
name = target && target.get('name'),
|
name = target && target.get('name'),
|
||||||
|
id = target && target.get('id'),
|
||||||
display_name = target && target.get('display_name');
|
display_name = target && target.get('display_name');
|
||||||
|
|
||||||
|
if ( id !== f.__old_host_target ) {
|
||||||
|
if ( f.__old_host_target )
|
||||||
|
f.ws_send("unsub_channel", f.__old_host_target);
|
||||||
|
|
||||||
|
if ( id ) {
|
||||||
|
f.ws_send("sub_channel", id);
|
||||||
|
f.__old_host_target = id;
|
||||||
|
} else
|
||||||
|
delete f.__old_host_target;
|
||||||
|
}
|
||||||
|
|
||||||
if ( display_name )
|
if ( display_name )
|
||||||
FFZ.capitalization[name] = [display_name, Date.now()];
|
FFZ.capitalization[name] = [display_name, Date.now()];
|
||||||
|
|
||||||
if ( f.settings.group_tabs && f._chatv )
|
if ( f.settings.group_tabs && f._chatv )
|
||||||
f._chatv.ffzRebuildTabs();
|
f._chatv.ffzRebuildTabs();
|
||||||
|
|
||||||
|
if ( f.settings.follow_buttons )
|
||||||
|
f.rebuild_following_ui();
|
||||||
|
|
||||||
|
if ( f.settings.srl_races )
|
||||||
|
f.rebuild_race_ui();
|
||||||
|
|
||||||
}.observes("content.hostModeTarget")
|
}.observes("content.hostModeTarget")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,15 +132,28 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzInit: function() {
|
ffzInit: function() {
|
||||||
|
var id = this.get('controller.id'),
|
||||||
|
el = this.get('element');
|
||||||
|
|
||||||
f._cindex = this;
|
f._cindex = this;
|
||||||
this.get('element').setAttribute('data-channel', this.get('controller.id'));
|
f.ws_send("sub_channel", id);
|
||||||
|
|
||||||
|
el.setAttribute('data-channel', id);
|
||||||
|
el.classList.add('ffz-channel');
|
||||||
|
|
||||||
this.ffzFixTitle();
|
this.ffzFixTitle();
|
||||||
this.ffzUpdateUptime();
|
this.ffzUpdateUptime();
|
||||||
this.ffzUpdateChatters();
|
this.ffzUpdateChatters();
|
||||||
|
|
||||||
var el = this.get('element').querySelector('.svg-glyph_views:not(.ffz-svg)')
|
var views = this.get('element').querySelector('.svg-glyph_views:not(.ffz-svg)')
|
||||||
if ( el )
|
if ( views )
|
||||||
el.parentNode.classList.add('twitch-channel-views');
|
views.parentNode.classList.add('twitch-channel-views');
|
||||||
|
|
||||||
|
if ( f.settings.follow_buttons )
|
||||||
|
f.rebuild_following_ui();
|
||||||
|
|
||||||
|
if ( f.settings.srl_races )
|
||||||
|
f.rebuild_race_ui();
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzFixTitle: function() {
|
ffzFixTitle: function() {
|
||||||
|
@ -129,6 +167,9 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
|
|
||||||
this.$(".title span").each(function(i, el) {
|
this.$(".title span").each(function(i, el) {
|
||||||
var scripts = el.querySelectorAll("script");
|
var scripts = el.querySelectorAll("script");
|
||||||
|
if ( ! scripts.length )
|
||||||
|
el.innerHTML = status;
|
||||||
|
else
|
||||||
el.innerHTML = scripts[0].outerHTML + status + scripts[1].outerHTML;
|
el.innerHTML = scripts[0].outerHTML + status + scripts[1].outerHTML;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -148,7 +189,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var chatter_count = Object.keys(room.room.get('ffz_chatters') || {}).length,
|
var chatter_count = Object.keys(room.room.get('ffz_chatters') || {}).length,
|
||||||
ffz_chatters = room.ffz_chatters || 0;
|
ffz_chatters = room.ffz_chatters || 0,
|
||||||
|
ffz_viewers = room.ffz_viewers || 0;
|
||||||
|
|
||||||
var el = this.get('element').querySelector('#ffz-chatter-display span');
|
var el = this.get('element').querySelector('#ffz-chatter-display span');
|
||||||
if ( ! el ) {
|
if ( ! el ) {
|
||||||
|
@ -159,7 +201,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
var stat = document.createElement('span');
|
var stat = document.createElement('span');
|
||||||
stat.className = 'ffz stat';
|
stat.className = 'ffz stat';
|
||||||
stat.id = 'ffz-chatter-display';
|
stat.id = 'ffz-chatter-display';
|
||||||
stat.title = "Current Chatters";
|
stat.title = "Currently in Chat";
|
||||||
|
|
||||||
stat.innerHTML = constants.ROOMS + " ";
|
stat.innerHTML = constants.ROOMS + " ";
|
||||||
el = document.createElement("span");
|
el = document.createElement("span");
|
||||||
|
@ -176,7 +218,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
|
|
||||||
el.innerHTML = utils.number_commas(chatter_count);
|
el.innerHTML = utils.number_commas(chatter_count);
|
||||||
|
|
||||||
if ( ! ffz_chatters ) {
|
if ( ! ffz_chatters && ! ffz_viewers ) {
|
||||||
el = this.get('element').querySelector('#ffz-ffzchatter-display');
|
el = this.get('element').querySelector('#ffz-ffzchatter-display');
|
||||||
el && el.parentNode.removeChild(el);
|
el && el.parentNode.removeChild(el);
|
||||||
return;
|
return;
|
||||||
|
@ -191,7 +233,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
var stat = document.createElement('span');
|
var stat = document.createElement('span');
|
||||||
stat.className = 'ffz stat';
|
stat.className = 'ffz stat';
|
||||||
stat.id = 'ffz-ffzchatter-display';
|
stat.id = 'ffz-ffzchatter-display';
|
||||||
stat.title = "Chatters with FrankerFaceZ";
|
stat.title = "Viewers (In Chat) with FrankerFaceZ";
|
||||||
|
|
||||||
stat.innerHTML = constants.ZREKNARF + " ";
|
stat.innerHTML = constants.ZREKNARF + " ";
|
||||||
el = document.createElement("span");
|
el = document.createElement("span");
|
||||||
|
@ -206,7 +248,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
jQuery(stat).tipsy();
|
jQuery(stat).tipsy();
|
||||||
}
|
}
|
||||||
|
|
||||||
el.innerHTML = utils.number_commas(ffz_chatters);
|
el.innerHTML = utils.number_commas(ffz_viewers) + " (" + utils.number_commas(ffz_chatters) + ")";
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,10 +315,16 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzTeardown: function() {
|
ffzTeardown: function() {
|
||||||
|
var id = this.get('controller.id');
|
||||||
|
if ( id )
|
||||||
|
f.ws_send("unsub_channel", id);
|
||||||
|
|
||||||
this.get('element').setAttribute('data-channel', '');
|
this.get('element').setAttribute('data-channel', '');
|
||||||
f._cindex = undefined;
|
f._cindex = undefined;
|
||||||
if ( this._ffz_update_uptime )
|
if ( this._ffz_update_uptime )
|
||||||
clearTimeout(this._ffz_update_uptime);
|
clearTimeout(this._ffz_update_uptime);
|
||||||
|
|
||||||
|
utils.update_css(f._channel_style, id, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,36 @@ var FFZ = window.FrankerFaceZ,
|
||||||
// Settings
|
// Settings
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.minimal_chat = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
|
||||||
|
//no_bttv: true,
|
||||||
|
|
||||||
|
category: "Chat",
|
||||||
|
name: "Minimalistic Chat",
|
||||||
|
help: "Hide all of the chat user interface, only showing messages and an input box.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
document.body.classList.toggle("ffz-minimal-chat", val);
|
||||||
|
if ( this.settings.group_tabs && this._chatv && this._chatv._ffz_tabs ) {
|
||||||
|
var f = this;
|
||||||
|
setTimeout(function() {
|
||||||
|
f._chatv && f._chatv.$('.chat-room').css('top', f._chatv._ffz_tabs.offsetHeight + "px");
|
||||||
|
},0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.prevent_clear = {
|
FFZ.settings_info.prevent_clear = {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: false,
|
value: false,
|
||||||
|
|
||||||
no_bttv: true,
|
no_bttv: true,
|
||||||
|
|
||||||
category: "Chat",
|
category: "Chat Moderation",
|
||||||
name: "Show Deleted Messages",
|
name: "Show Deleted Messages",
|
||||||
help: "Fade deleted messages instead of replacing them, and prevent chat from being cleared.",
|
help: "Fade deleted messages instead of replacing them, and prevent chat from being cleared.",
|
||||||
|
|
||||||
|
@ -98,6 +121,9 @@ FFZ.settings_info.pinned_rooms = {
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
FFZ.prototype.setup_chatview = function() {
|
FFZ.prototype.setup_chatview = function() {
|
||||||
|
//if ( ! this.has_bttv )
|
||||||
|
document.body.classList.toggle("ffz-minimal-chat", this.settings.minimal_chat);
|
||||||
|
|
||||||
this.log("Hooking the Ember Chat controller.");
|
this.log("Hooking the Ember Chat controller.");
|
||||||
|
|
||||||
var Chat = App.__container__.lookup('controller:chat'),
|
var Chat = App.__container__.lookup('controller:chat'),
|
||||||
|
@ -108,7 +134,26 @@ FFZ.prototype.setup_chatview = function() {
|
||||||
ffzUpdateChannels: function() {
|
ffzUpdateChannels: function() {
|
||||||
if ( f.settings.group_tabs && f._chatv )
|
if ( f.settings.group_tabs && f._chatv )
|
||||||
f._chatv.ffzRebuildTabs();
|
f._chatv.ffzRebuildTabs();
|
||||||
}.observes("currentChannelRoom", "connectedPrivateGroupRooms")
|
}.observes("currentChannelRoom", "connectedPrivateGroupRooms"),
|
||||||
|
|
||||||
|
removeCurrentChannelRoom: function() {
|
||||||
|
if ( ! f.settings.group_tabs || f.has_bttv )
|
||||||
|
return this._super();
|
||||||
|
|
||||||
|
var room = this.get("currentChannelRoom"),
|
||||||
|
room_id = room && room.get('id');
|
||||||
|
|
||||||
|
if ( ! f.settings.pinned_rooms || f.settings.pinned_rooms.indexOf(room_id) === -1 ) {
|
||||||
|
// We can actually destroy it.
|
||||||
|
if ( room === this.get("currentRoom") )
|
||||||
|
this.blurRoom();
|
||||||
|
|
||||||
|
if ( room )
|
||||||
|
room.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set("currentChannelRoom", void 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +371,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
if ( target && Room ) {
|
if ( target && Room ) {
|
||||||
var target_id = target.get('id');
|
var target_id = target.get('id');
|
||||||
if ( this._ffz_host !== target_id ) {
|
if ( this._ffz_host !== target_id ) {
|
||||||
if ( this._ffz_host_room ) {
|
if ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && this._ffz_host_room ) {
|
||||||
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
||||||
this.get('controller').blurRoom();
|
this.get('controller').blurRoom();
|
||||||
this._ffz_host_room.destroy();
|
this._ffz_host_room.destroy();
|
||||||
|
@ -336,7 +381,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
this._ffz_host_room = Room.findOne(target_id);
|
this._ffz_host_room = Room.findOne(target_id);
|
||||||
}
|
}
|
||||||
} else if ( this._ffz_host ) {
|
} else if ( this._ffz_host ) {
|
||||||
if ( this._ffz_host_room ) {
|
if ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && this._ffz_host_room ) {
|
||||||
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
||||||
this.get('controller').blurRoom();
|
this.get('controller').blurRoom();
|
||||||
this._ffz_host_room.destroy();
|
this._ffz_host_room.destroy();
|
||||||
|
@ -409,7 +454,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzBuildTab: function(view, room, current_channel, host_channel) {
|
ffzBuildTab: function(view, room, current_channel, host_channel) {
|
||||||
var tab = document.createElement('span'), name, unread,
|
var tab = document.createElement('span'), name, unread, icon = '',
|
||||||
group = room.get('isGroupRoom'),
|
group = room.get('isGroupRoom'),
|
||||||
current = room === view.get('controller.currentRoom');
|
current = room === view.get('controller.currentRoom');
|
||||||
|
|
||||||
|
@ -421,21 +466,25 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
tab.classList.toggle('group-chat', group);
|
tab.classList.toggle('group-chat', group);
|
||||||
tab.classList.toggle('active', current);
|
tab.classList.toggle('active', current);
|
||||||
|
|
||||||
name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room.get('id')));
|
|
||||||
unread = format_unread(current ? 0 : room.get('unreadCount'));
|
unread = format_unread(current ? 0 : room.get('unreadCount'));
|
||||||
|
|
||||||
|
name = room.get('tmiRoom.displayName') || (group ? room.get('tmiRoom.name') : FFZ.get_capitalization(room.get('id'), function(name) {
|
||||||
|
unread = format_unread(current ? 0 : room.get('unreadCount'));
|
||||||
|
tab.innerHTML = icon + utils.sanitize(name) + '<span>' + unread + '</span>';
|
||||||
|
}));
|
||||||
|
|
||||||
if ( current_channel ) {
|
if ( current_channel ) {
|
||||||
tab.innerHTML = constants.CAMERA;
|
icon = constants.CAMERA;
|
||||||
tab.title = "Current Channel";
|
tab.title = "Current Channel";
|
||||||
} else if ( host_channel ) {
|
} else if ( host_channel ) {
|
||||||
tab.innerHTML = constants.EYE;
|
icon = constants.EYE;
|
||||||
tab.title = "Hosted Channel";
|
tab.title = "Hosted Channel";
|
||||||
} else if ( group )
|
} else if ( group )
|
||||||
tab.title = "Group Chat";
|
tab.title = "Group Chat";
|
||||||
else
|
else
|
||||||
tab.title = "Pinned Channel";
|
tab.title = "Pinned Channel";
|
||||||
|
|
||||||
tab.innerHTML += utils.sanitize(name) + '<span>' + unread + '</span>';
|
tab.innerHTML = icon + utils.sanitize(name) + '<span>' + unread + '</span>';
|
||||||
|
|
||||||
tab.addEventListener('click', function() {
|
tab.addEventListener('click', function() {
|
||||||
view.get('controller').focusRoom(room);
|
view.get('controller').focusRoom(room);
|
||||||
|
@ -452,7 +501,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this._ffz_host ) {
|
if ( this._ffz_host ) {
|
||||||
if ( this._ffz_host_room ) {
|
if ( f.settings.pinned_rooms.indexOf(this._ffz_host) === -1 && this._ffz_host_room ) {
|
||||||
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
if ( this.get('controller.currentRoom') === this._ffz_host_room )
|
||||||
this.get('controller').blurRoom();
|
this.get('controller').blurRoom();
|
||||||
this._ffz_host_room.destroy();
|
this._ffz_host_room.destroy();
|
||||||
|
|
|
@ -15,8 +15,12 @@
|
||||||
|
|
||||||
|
|
||||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||||
|
SRCSETS = {};
|
||||||
build_srcset = function(id) {
|
build_srcset = function(id) {
|
||||||
return TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
if ( SRCSETS[id] )
|
||||||
|
return SRCSETS[id];
|
||||||
|
var out = SRCSETS[id] = TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
||||||
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +35,7 @@
|
||||||
if ( ! set )
|
if ( ! set )
|
||||||
return data.code;
|
return data.code;
|
||||||
|
|
||||||
else if ( set == "00000turbo" || set == "turbo" ) {
|
else if ( set == "--twitch-turbo--" || set == "turbo" ) {
|
||||||
set = "Twitch Turbo";
|
set = "Twitch Turbo";
|
||||||
set_type = null;
|
set_type = null;
|
||||||
}
|
}
|
||||||
|
@ -181,6 +185,69 @@
|
||||||
// Settings
|
// Settings
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
|
FFZ.settings_info.parse_emoji = {
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
|
||||||
|
category: "Chat",
|
||||||
|
|
||||||
|
name: "Replace Emoji with Images",
|
||||||
|
help: "Replace emoji in chat messages with nicer looking images from the open-source Twitter Emoji project."
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.room_status = {
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
|
||||||
|
category: "Chat",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Room Status Indicators",
|
||||||
|
help: "Display the current room state (slow mode, sub mode, and r9k mode) next to the Chat button.",
|
||||||
|
|
||||||
|
on_update: function() {
|
||||||
|
if ( this._roomv )
|
||||||
|
this._roomv.ffzUpdateStatus();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.scrollback_length = {
|
||||||
|
type: "button",
|
||||||
|
value: 150,
|
||||||
|
|
||||||
|
category: "Chat",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Scrollback Length",
|
||||||
|
help: "Set the maximum number of lines to keep in chat.",
|
||||||
|
|
||||||
|
method: function() {
|
||||||
|
var new_val = prompt("Scrollback Length\n\nPlease enter a new maximum length for the chat scrollback. Default: 150\n\nNote: Making this too large will cause your browser to lag.", this.settings.scrollback_length);
|
||||||
|
if ( new_val === null || new_val === undefined )
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_val = parseInt(new_val);
|
||||||
|
if ( new_val === NaN )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( new_val < 10 )
|
||||||
|
new_val = 10;
|
||||||
|
|
||||||
|
this.settings.set("scrollback_length", new_val);
|
||||||
|
|
||||||
|
// Update our everything.
|
||||||
|
var Chat = App.__container__.lookup('controller:chat'),
|
||||||
|
current_id = Chat && Chat.get('currentRoom.id');
|
||||||
|
|
||||||
|
for(var room_id in this.rooms) {
|
||||||
|
var room = this.rooms[room_id];
|
||||||
|
room.room.set('messageBufferSize', new_val + ((this._roomv && !this._roomv.get('stuckToBottom') && current_id === room_id) ? 150 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FFZ.settings_info.banned_words = {
|
FFZ.settings_info.banned_words = {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: [],
|
value: [],
|
||||||
|
@ -360,6 +427,9 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
tokens = f._remove_banned(tokens);
|
tokens = f._remove_banned(tokens);
|
||||||
tokens = f._emoticonize(this, tokens);
|
tokens = f._emoticonize(this, tokens);
|
||||||
|
|
||||||
|
if ( f.settings.parse_emoji )
|
||||||
|
tokens = f.tokenize_emoji(tokens);
|
||||||
|
|
||||||
// Store the capitalization.
|
// Store the capitalization.
|
||||||
var display = this.get("msgObject.tags.display-name");
|
var display = this.get("msgObject.tags.display-name");
|
||||||
if ( display && display.length )
|
if ( display && display.length )
|
||||||
|
@ -395,6 +465,18 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
this.rerender();
|
this.rerender();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
willClearRender: function() {
|
||||||
|
// This is here to prevent tipsy tooltips from hanging around.
|
||||||
|
try {
|
||||||
|
this.$('a.mod-icon').tipsy('disable');
|
||||||
|
jQuery('body > .tipsy:last').remove();
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
f.error("LineView willClearRender: " + err);
|
||||||
|
}
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this._super();
|
this._super();
|
||||||
try {
|
try {
|
||||||
|
@ -416,8 +498,8 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
this.set("msgObject.ffz_alternate", row_type);
|
this.set("msgObject.ffz_alternate", row_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
el.classList.toggle('ffz-alternate', row_type);
|
el.classList.toggle('ffz-alternate', row_type || false);
|
||||||
el.classList.toggle('ffz-deleted', f.settings.prevent_clear && this.get('msgObject.ffz_deleted'));
|
el.classList.toggle('ffz-deleted', f.settings.prevent_clear && this.get('msgObject.ffz_deleted') || false);
|
||||||
|
|
||||||
|
|
||||||
// Basic Data
|
// Basic Data
|
||||||
|
@ -452,50 +534,9 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
|
|
||||||
|
|
||||||
// Banned Links
|
// Banned Links
|
||||||
var bad_links = el.querySelectorAll('a.deleted-link');
|
var bad_links = el.querySelectorAll('span.message a.deleted-link');
|
||||||
for(var i=0; i < bad_links.length; i++) {
|
for(var i=0; i < bad_links.length; i++)
|
||||||
var link = bad_links[i];
|
bad_links[i].addEventListener("click", f._deleted_link_click);
|
||||||
|
|
||||||
link.addEventListener("click", function(e) {
|
|
||||||
if ( ! this.classList.contains("deleted-link") )
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Get the URL
|
|
||||||
var href = this.getAttribute('data-url'),
|
|
||||||
link = href;
|
|
||||||
|
|
||||||
// Delete Old Stuff
|
|
||||||
this.classList.remove('deleted-link');
|
|
||||||
this.removeAttribute("data-url");
|
|
||||||
this.removeAttribute("title");
|
|
||||||
this.removeAttribute("original-title");
|
|
||||||
|
|
||||||
// Process URL
|
|
||||||
if ( href.indexOf("@") > -1 && (-1 === href.indexOf("/") || href.indexOf("@") < href.indexOf("/")) )
|
|
||||||
href = "mailto:" + href;
|
|
||||||
else if ( ! href.match(/^https?:\/\//) )
|
|
||||||
href = "http://" + href;
|
|
||||||
|
|
||||||
// Set up the Link
|
|
||||||
this.href = href;
|
|
||||||
this.target = "_new";
|
|
||||||
this.textContent = link;
|
|
||||||
|
|
||||||
// Now, check for a tooltip.
|
|
||||||
var link_data = f._link_data[link];
|
|
||||||
if ( link_data && typeof link_data != "boolean" ) {
|
|
||||||
this.title = link_data.tooltip;
|
|
||||||
if ( link_data.unsafe )
|
|
||||||
this.classList.add('unsafe-link');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop from Navigating
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Also add a nice tooltip.
|
|
||||||
jQuery(link).tipsy({html:true});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Link Tooltips
|
// Link Tooltips
|
||||||
|
@ -531,12 +572,11 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
|
|
||||||
|
|
||||||
// Enhanced Emotes
|
// Enhanced Emotes
|
||||||
var images = el.querySelectorAll('img.emoticon');
|
var images = el.querySelectorAll('span.message img.emoticon');
|
||||||
for(var i=0; i < images.length; i++) {
|
for(var i=0; i < images.length; i++) {
|
||||||
var img = images[i],
|
var img = images[i],
|
||||||
name = img.alt,
|
name = img.alt,
|
||||||
match = /\/emoticons\/v1\/(\d+)\/1\.0/.exec(img.src),
|
id = FFZ.src_to_id(img.src);
|
||||||
id = match ? parseInt(match[1]) : null;
|
|
||||||
|
|
||||||
if ( id !== null ) {
|
if ( id !== null ) {
|
||||||
// High-DPI Images
|
// High-DPI Images
|
||||||
|
@ -554,6 +594,15 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, img.alt));
|
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, img.alt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if ( img.getAttribute('data-ffz-emoji') ) {
|
||||||
|
var eid = img.getAttribute('data-ffz-emoji'),
|
||||||
|
data = f.emoji_data && f.emoji_data[eid];
|
||||||
|
|
||||||
|
if ( data ) {
|
||||||
|
img.setAttribute('srcset', data.srcSet);
|
||||||
|
img.title = "Emoji: " + img.alt + "\nName: " + data.short_name;
|
||||||
|
}
|
||||||
|
|
||||||
} else if ( img.getAttribute('data-ffz-emote') ) {
|
} else if ( img.getAttribute('data-ffz-emote') ) {
|
||||||
var data = JSON.parse(decodeURIComponent(img.getAttribute('data-ffz-emote'))),
|
var data = JSON.parse(decodeURIComponent(img.getAttribute('data-ffz-emote'))),
|
||||||
id = data && data[0] || null,
|
id = data && data[0] || null,
|
||||||
|
@ -660,10 +709,6 @@ FFZ.capitalization = {};
|
||||||
FFZ._cap_fetching = 0;
|
FFZ._cap_fetching = 0;
|
||||||
|
|
||||||
FFZ.get_capitalization = function(name, callback) {
|
FFZ.get_capitalization = function(name, callback) {
|
||||||
// Use the BTTV code if it's present.
|
|
||||||
if ( window.BetterTTV && BetterTTV.chat && BetterTTV.chat.helpers.lookupDisplayName )
|
|
||||||
return BetterTTV.chat.helpers.lookupDisplayName(name);
|
|
||||||
|
|
||||||
if ( ! name )
|
if ( ! name )
|
||||||
return name;
|
return name;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
utils = require("../utils"),
|
utils = require("../utils"),
|
||||||
|
helpers,
|
||||||
|
|
||||||
keycodes = {
|
keycodes = {
|
||||||
ESC: 27,
|
ESC: 27,
|
||||||
|
@ -9,31 +10,150 @@ var FFZ = window.FrankerFaceZ,
|
||||||
U: 85
|
U: 85
|
||||||
},
|
},
|
||||||
|
|
||||||
btns = [
|
|
||||||
['5m', 300],
|
|
||||||
['10m', 600],
|
|
||||||
['1hr', 3600],
|
|
||||||
['12hr', 43200],
|
|
||||||
['24hr', 86400]],
|
|
||||||
|
|
||||||
MESSAGE = '<svg class="svg-messages" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,15V3h16v12H1z M15.354,5.354l-0.707-0.707L9,10.293L3.354,4.646L2.646,5.354L6.293,9l-3.646,3.646l0.707,0.707L7,9.707l1.646,1.646h0.707L11,9.707l3.646,3.646l0.707-0.707L11.707,9L15.354,5.354z" fill-rule="evenodd"></path></svg>',
|
MESSAGE = '<svg class="svg-messages" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,15V3h16v12H1z M15.354,5.354l-0.707-0.707L9,10.293L3.354,4.646L2.646,5.354L6.293,9l-3.646,3.646l0.707,0.707L7,9.707l1.646,1.646h0.707L11,9.707l3.646,3.646l0.707-0.707L11.707,9L15.354,5.354z" fill-rule="evenodd"></path></svg>',
|
||||||
CHECK = '<svg class="svg-unban" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M6.5,12.75L2,8.25l2-2l2.5,2.5l5.5-5.5l2,2L6.5,12.75z"/></svg>';
|
CHECK = '<svg class="svg-unban" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M6.5,12.75L2,8.25l2-2l2.5,2.5l5.5-5.5l2,2L6.5,12.75z"/></svg>',
|
||||||
|
|
||||||
|
DURATIONS = {},
|
||||||
|
duration_string = function(val) {
|
||||||
|
if ( val === 1 )
|
||||||
|
return 'Purge';
|
||||||
|
|
||||||
|
if ( DURATIONS[val] )
|
||||||
|
return DURATIONS[val];
|
||||||
|
|
||||||
|
var weeks, days, hours, minutes, seconds;
|
||||||
|
|
||||||
|
weeks = Math.floor(val / 604800);
|
||||||
|
seconds = val % 604800;
|
||||||
|
|
||||||
|
days = Math.floor(seconds / 86400);
|
||||||
|
seconds %= 86400;
|
||||||
|
|
||||||
|
hours = Math.floor(seconds / 3600);
|
||||||
|
seconds %= 3600;
|
||||||
|
|
||||||
|
minutes = Math.floor(seconds / 60);
|
||||||
|
seconds %= 60;
|
||||||
|
|
||||||
|
var out = DURATIONS[val] = (weeks ? weeks + 'w' : '') + ((days || (weeks && (hours || minutes || seconds))) ? days + 'd' : '') + ((hours || ((weeks || days) && (minutes || seconds))) ? hours + 'h' : '') + ((minutes || ((weeks || days || hours) && seconds)) ? minutes + 'm' : '') + (seconds ? seconds + 's' : '');
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
|
||||||
|
} catch(err) { }
|
||||||
|
|
||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// Settings
|
// Settings
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
FFZ.settings_info.enhanced_moderation = {
|
FFZ.settings_info.chat_hover_pause = {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: false,
|
value: false,
|
||||||
|
|
||||||
no_bttv: true,
|
no_bttv: true,
|
||||||
//visible: function() { return ! this.has_bttv },
|
|
||||||
category: "Chat",
|
|
||||||
|
|
||||||
name: "Enhanced Moderation",
|
category: "Chat Moderation",
|
||||||
help: "Use /p, /t, /u and /b in chat to moderate chat, or use hotkeys with moderation cards."
|
name: "Pause Chat Scrolling on Mouse Hover",
|
||||||
|
help: "Automatically prevent the chat from scrolling when moving the mouse over it to prevent moderation mistakes and link mis-clicks.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( ! this._roomv )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( val )
|
||||||
|
this._roomv.ffzEnableFreeze();
|
||||||
|
else
|
||||||
|
this._roomv.ffzDisableFreeze();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.short_commands = {
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
|
||||||
|
no_bttv: true,
|
||||||
|
category: "Chat Moderation",
|
||||||
|
|
||||||
|
name: "Short Moderation Commands",
|
||||||
|
help: "Use /t, /b, and /u in chat in place of /timeout, /ban, /unban for quicker moderation, and use /p for 1 second timeouts."
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.mod_card_hotkeys = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
|
||||||
|
no_bttv: true,
|
||||||
|
category: "Chat Moderation",
|
||||||
|
|
||||||
|
name: "Moderation Card Hotkeys",
|
||||||
|
help: "With a moderation card selected, press B to ban the user, T to time them out for 10 minutes, P to time them out for 1 second, or U to unban them. ESC closes the card."
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.mod_card_history = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
|
||||||
|
no_bttv: true,
|
||||||
|
category: "Chat Moderation",
|
||||||
|
|
||||||
|
name: "Moderation Card History",
|
||||||
|
help: "Display a few of the user's previously sent messages on moderation cards.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( val || ! this.rooms )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Delete all history~!
|
||||||
|
for(var room_id in this.rooms) {
|
||||||
|
var room = this.rooms[room_id];
|
||||||
|
if ( room )
|
||||||
|
room.user_history = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.mod_card_durations = {
|
||||||
|
type: "button",
|
||||||
|
value: [300, 600, 3600, 43200, 86400, 604800],
|
||||||
|
|
||||||
|
category: "Chat Moderation",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Moderation Card Timeout Buttons",
|
||||||
|
help: "Add additional timeout buttons to moderation cards with specific durations.",
|
||||||
|
|
||||||
|
method: function() {
|
||||||
|
var old_val = this.settings.mod_card_durations.join(", "),
|
||||||
|
new_val = prompt("Moderation Card Timeout Buttons\n\nPlease enter a comma-separated list of durations that you would like to have timeout buttons for. Durations must be expressed in seconds.\n\nEnter \"reset\" without quotes to return to the default value.", old_val);
|
||||||
|
|
||||||
|
if ( new_val === null || new_val === undefined )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( new_val === "reset" )
|
||||||
|
new_val = FFZ.settings_info.mod_card_durations.value.join(", ");
|
||||||
|
|
||||||
|
// Split them up.
|
||||||
|
new_val = new_val.trim().split(/[ ,]+/);
|
||||||
|
var vals = [];
|
||||||
|
|
||||||
|
for(var i=0; i < new_val.length; i++) {
|
||||||
|
var val = parseInt(new_val[i]);
|
||||||
|
if ( val === 0 )
|
||||||
|
val = 1;
|
||||||
|
|
||||||
|
if ( val !== NaN && val > 0 )
|
||||||
|
vals.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings.set("mod_card_durations", vals);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,22 +162,42 @@ FFZ.settings_info.enhanced_moderation = {
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
FFZ.prototype.setup_mod_card = function() {
|
FFZ.prototype.setup_mod_card = function() {
|
||||||
|
this.log("Modifying Mousetrap stopCallback so we can catch ESC.");
|
||||||
|
var orig_stop = Mousetrap.stopCallback;
|
||||||
|
Mousetrap.stopCallback = function(e, element, combo) {
|
||||||
|
if ( element.classList.contains('no-mousetrap') )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return orig_stop(e, element, combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mousetrap.bind("up up down down left right left right b a enter", function() {
|
||||||
|
var el = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
|
||||||
|
el && el.classList.toggle('ffz-flip');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.log("Hooking the Ember Moderation Card view.");
|
this.log("Hooking the Ember Moderation Card view.");
|
||||||
var Card = App.__container__.resolve('component:moderation-card'),
|
var Card = App.__container__.resolve('component:moderation-card'),
|
||||||
f = this;
|
f = this;
|
||||||
|
|
||||||
Card.reopen({
|
Card.reopen({
|
||||||
|
ffzForceRedraw: function() {
|
||||||
|
this.rerender();
|
||||||
|
}.observes("cardInfo.isModeratorOrHigher", "cardInfo.user"),
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this._super();
|
this._super();
|
||||||
window._card = this;
|
window._card = this;
|
||||||
try {
|
try {
|
||||||
if ( f.has_bttv || ! f.settings.enhanced_moderation )
|
if ( f.has_bttv )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var el = this.get('element'),
|
var el = this.get('element'),
|
||||||
controller = this.get('controller');
|
controller = this.get('controller');
|
||||||
|
|
||||||
// Style it!
|
// Style it!
|
||||||
|
if ( f.settings.mod_card_hotkeys || (f.settings.mod_card_durations && f.settings.mod_card_durations.length) )
|
||||||
el.classList.add('ffz-moderation-card');
|
el.classList.add('ffz-moderation-card');
|
||||||
|
|
||||||
// Only do the big stuff if we're mod.
|
// Only do the big stuff if we're mod.
|
||||||
|
@ -66,6 +206,9 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
el.setAttribute('tabindex', 1);
|
el.setAttribute('tabindex', 1);
|
||||||
|
|
||||||
// Key Handling
|
// Key Handling
|
||||||
|
if ( f.settings.mod_card_hotkeys ) {
|
||||||
|
el.classList.add('no-mousetrap');
|
||||||
|
|
||||||
el.addEventListener('keyup', function(e) {
|
el.addEventListener('keyup', function(e) {
|
||||||
var key = e.keyCode || e.which,
|
var key = e.keyCode || e.which,
|
||||||
user_id = controller.get('cardInfo.user.id'),
|
user_id = controller.get('cardInfo.user.id'),
|
||||||
|
@ -86,13 +229,9 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
else if ( key != keycodes.ESC )
|
else if ( key != keycodes.ESC )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
controller.send('hideModOverlay');
|
controller.send('close');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Extra Moderation
|
|
||||||
var line = document.createElement('div');
|
|
||||||
line.className = 'interface clearfix';
|
|
||||||
|
|
||||||
var btn_click = function(timeout) {
|
var btn_click = function(timeout) {
|
||||||
var user_id = controller.get('cardInfo.user.id'),
|
var user_id = controller.get('cardInfo.user.id'),
|
||||||
|
@ -104,15 +243,15 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
room.send("/timeout " + user_id + " " + timeout);
|
room.send("/timeout " + user_id + " " + timeout);
|
||||||
},
|
},
|
||||||
|
|
||||||
btn_make = function(text, timeout) {
|
btn_make = function(timeout) {
|
||||||
var btn = document.createElement('button');
|
var btn = document.createElement('button')
|
||||||
btn.className = 'button';
|
btn.className = 'button';
|
||||||
btn.innerHTML = text;
|
btn.innerHTML = duration_string(timeout);
|
||||||
btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : "");
|
btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : "");
|
||||||
|
|
||||||
if ( timeout === 600 )
|
if ( f.settings.mod_card_hotkeys && timeout === 600 )
|
||||||
btn.title = "(T)" + btn.title.substr(1);
|
btn.title = "(T)" + btn.title.substr(1);
|
||||||
else if ( timeout === 1 )
|
else if ( f.settings.mod_card_hotkeys && timeout === 1 )
|
||||||
btn.title = "(P)urge - " + btn.title;
|
btn.title = "(P)urge - " + btn.title;
|
||||||
|
|
||||||
jQuery(btn).tipsy();
|
jQuery(btn).tipsy();
|
||||||
|
@ -121,36 +260,40 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
return btn;
|
return btn;
|
||||||
};
|
};
|
||||||
|
|
||||||
line.appendChild(btn_make('Purge', 1));
|
if ( f.settings.mod_card_durations && f.settings.mod_card_durations.length ) {
|
||||||
|
// Extra Moderation
|
||||||
|
var line = document.createElement('div');
|
||||||
|
line.className = 'interface clearfix';
|
||||||
|
|
||||||
|
line.appendChild(btn_make(1));
|
||||||
|
|
||||||
var s = document.createElement('span');
|
var s = document.createElement('span');
|
||||||
s.className = 'right';
|
s.className = 'right';
|
||||||
line.appendChild(s);
|
line.appendChild(s);
|
||||||
|
|
||||||
for(var i=0; i < btns.length; i++)
|
for(var i=0; i < f.settings.mod_card_durations.length; i++)
|
||||||
s.appendChild(btn_make(btns[i][0], btns[i][1]));
|
s.appendChild(btn_make(f.settings.mod_card_durations[i]));
|
||||||
|
|
||||||
el.appendChild(line);
|
el.appendChild(line);
|
||||||
|
|
||||||
|
// Fix Other Buttons
|
||||||
|
this.$("button.timeout").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
var ban_btn = el.querySelector('button.ban');
|
||||||
|
if ( f.settings.mod_card_hotkeys )
|
||||||
|
ban_btn.setAttribute('title', '(B)an User');
|
||||||
|
|
||||||
// Unban Button
|
// Unban Button
|
||||||
|
|
||||||
var unban_btn = document.createElement('button');
|
var unban_btn = document.createElement('button');
|
||||||
unban_btn.className = 'unban button glyph-only light';
|
unban_btn.className = 'unban button glyph-only light';
|
||||||
unban_btn.innerHTML = CHECK;
|
unban_btn.innerHTML = CHECK;
|
||||||
unban_btn.title = "(U)nban User";
|
unban_btn.title = (f.settings.mod_card_hotkeys ? "(U)" : "U") + "nban User";
|
||||||
|
|
||||||
jQuery(unban_btn).tipsy();
|
jQuery(unban_btn).tipsy();
|
||||||
unban_btn.addEventListener("click", btn_click.bind(this, -1));
|
unban_btn.addEventListener("click", btn_click.bind(this, -1));
|
||||||
|
|
||||||
var ban_btn = el.querySelector('button.ban');
|
|
||||||
ban_btn.setAttribute('title', '(B)an User');
|
|
||||||
|
|
||||||
jQuery(ban_btn).after(unban_btn);
|
jQuery(ban_btn).after(unban_btn);
|
||||||
|
|
||||||
|
|
||||||
// Fix Other Buttons
|
|
||||||
this.$("button.timeout").remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -172,11 +315,52 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
msg_btn.classList.add('glyph-only');
|
msg_btn.classList.add('glyph-only');
|
||||||
msg_btn.classList.add('message');
|
msg_btn.classList.add('message');
|
||||||
|
|
||||||
msg_btn.title = "Message User";
|
msg_btn.title = "Whisper User";
|
||||||
jQuery(msg_btn).tipsy();
|
jQuery(msg_btn).tipsy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Message History
|
||||||
|
if ( f.settings.mod_card_history ) {
|
||||||
|
var Chat = App.__container__.lookup('controller:chat'),
|
||||||
|
room = Chat && Chat.get('currentRoom'),
|
||||||
|
ffz_room = room && f.rooms && f.rooms[room.get('id')],
|
||||||
|
user_history = ffz_room && ffz_room.user_history && ffz_room.user_history[controller.get('cardInfo.user.id')];
|
||||||
|
|
||||||
|
if ( user_history && user_history.length ) {
|
||||||
|
var history = document.createElement('ul'),
|
||||||
|
alternate = false;
|
||||||
|
history.className = 'interface clearfix chat-history';
|
||||||
|
|
||||||
|
for(var i=0; i < user_history.length; i++) {
|
||||||
|
var line = user_history[i],
|
||||||
|
l_el = document.createElement('li');
|
||||||
|
|
||||||
|
l_el.className = 'message-line chat-line';
|
||||||
|
l_el.classList.toggle('ffz-alternate', alternate);
|
||||||
|
alternate = !alternate;
|
||||||
|
|
||||||
|
if ( line.style )
|
||||||
|
l_el.classList.add(line.style);
|
||||||
|
|
||||||
|
l_el.innerHTML = (helpers ? '<span class="timestamp float-left">' + helpers.getTime(line.date) + '</span> ' : '') + '<span class="message">' + (line.style === 'action' ? '*' + line.from + ' ' : '') + f.render_tokens(line.cachedTokens) + '</span>';
|
||||||
|
|
||||||
|
// Banned Links
|
||||||
|
var bad_links = l_el.querySelectorAll('a.deleted-link');
|
||||||
|
for(var x=0; x < bad_links.length; x++)
|
||||||
|
bad_links[x].addEventListener("click", f._deleted_link_click);
|
||||||
|
|
||||||
|
jQuery('a', l_el).tipsy({html:true});
|
||||||
|
history.appendChild(l_el);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.appendChild(history);
|
||||||
|
|
||||||
|
// Lazy scroll-to-bottom
|
||||||
|
history.scrollTop = history.scrollHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Focus the Element
|
// Focus the Element
|
||||||
this.$().draggable({
|
this.$().draggable({
|
||||||
start: function() {
|
start: function() {
|
||||||
|
@ -198,7 +382,7 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
// Chat Commands
|
// Chat Commands
|
||||||
// ----------------
|
// ----------------
|
||||||
|
|
||||||
FFZ.chat_commands.purge = FFZ.chat_commands.p = function(room, args) {
|
FFZ.chat_commands.purge = function(room, args) {
|
||||||
if ( ! args || ! args.length )
|
if ( ! args || ! args.length )
|
||||||
return "Purge Usage: /p username [more usernames separated by spaces]";
|
return "Purge Usage: /p username [more usernames separated by spaces]";
|
||||||
|
|
||||||
|
@ -212,7 +396,11 @@ FFZ.chat_commands.purge = FFZ.chat_commands.p = function(room, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFZ.chat_commands.p.enabled = function() { return this.settings.enhanced_moderation; }
|
FFZ.chat_commands.p = function(room, args) {
|
||||||
|
return FFZ.chat_commands.purge.bind(this)(room, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FFZ.chat_commands.p.enabled = function() { return this.settings.short_commands; }
|
||||||
|
|
||||||
|
|
||||||
FFZ.chat_commands.t = function(room, args) {
|
FFZ.chat_commands.t = function(room, args) {
|
||||||
|
@ -221,7 +409,7 @@ FFZ.chat_commands.t = function(room, args) {
|
||||||
room.room.send("/timeout " + args.join(" "));
|
room.room.send("/timeout " + args.join(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
FFZ.chat_commands.t.enabled = function() { return this.settings.enhanced_moderation; }
|
FFZ.chat_commands.t.enabled = function() { return this.settings.short_commands; }
|
||||||
|
|
||||||
|
|
||||||
FFZ.chat_commands.b = function(room, args) {
|
FFZ.chat_commands.b = function(room, args) {
|
||||||
|
@ -238,7 +426,7 @@ FFZ.chat_commands.b = function(room, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFZ.chat_commands.b.enabled = function() { return this.settings.enhanced_moderation; }
|
FFZ.chat_commands.b.enabled = function() { return this.settings.short_commands; }
|
||||||
|
|
||||||
|
|
||||||
FFZ.chat_commands.u = function(room, args) {
|
FFZ.chat_commands.u = function(room, args) {
|
||||||
|
@ -255,4 +443,4 @@ FFZ.chat_commands.u = function(room, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFZ.chat_commands.u.enabled = function() { return this.settings.enhanced_moderation; }
|
FFZ.chat_commands.u.enabled = function() { return this.settings.short_commands; }
|
|
@ -26,18 +26,33 @@ FFZ.prototype.setup_room = function() {
|
||||||
s.id = "ffz-room-css";
|
s.id = "ffz-room-css";
|
||||||
document.head.appendChild(s);
|
document.head.appendChild(s);
|
||||||
|
|
||||||
this.log("Hooking the Ember Room model.");
|
this.log("Hooking the Ember Room controller.");
|
||||||
|
|
||||||
// Responsive ban button.
|
// Responsive ban button.
|
||||||
var RC = App.__container__.lookup('controller:room');
|
var f = this,
|
||||||
|
RC = App.__container__.lookup('controller:room');
|
||||||
if ( RC ) {
|
if ( RC ) {
|
||||||
var orig_action = RC._actions.banUser;
|
var orig_ban = RC._actions.banUser,
|
||||||
|
orig_to = RC._actions.timeoutUser;
|
||||||
|
|
||||||
RC._actions.banUser = function(e) {
|
RC._actions.banUser = function(e) {
|
||||||
orig_action.bind(this)(e);
|
orig_ban.bind(this)(e);
|
||||||
|
this.get("model").clearMessages(e.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
RC._actions.timeoutUser = function(e) {
|
||||||
|
orig_to.bind(this)(e);
|
||||||
|
this.get("model").clearMessages(e.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
RC._actions.purgeUser = function(e) {
|
||||||
|
this.get("model.tmiRoom").sendMessage("/timeout " + e.user + " 1");
|
||||||
this.get("model").clearMessages(e.user);
|
this.get("model").clearMessages(e.user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log("Hooking the Ember Room model.");
|
||||||
|
|
||||||
var Room = App.__container__.resolve('model:room');
|
var Room = App.__container__.resolve('model:room');
|
||||||
this._modify_room(Room);
|
this._modify_room(Room);
|
||||||
|
|
||||||
|
@ -53,6 +68,323 @@ FFZ.prototype.setup_room = function() {
|
||||||
this._modify_room(inst);
|
this._modify_room(inst);
|
||||||
inst.ffzPatchTMI();
|
inst.ffzPatchTMI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.log("Hooking the Ember Room view.");
|
||||||
|
|
||||||
|
var RoomView = App.__container__.resolve('view:room');
|
||||||
|
this._modify_rview(RoomView);
|
||||||
|
|
||||||
|
// For some reason, this doesn't work unless we create an instance of the
|
||||||
|
// room view and then destroy it immediately.
|
||||||
|
try {
|
||||||
|
RoomView.create().destroy();
|
||||||
|
} catch(err) { }
|
||||||
|
|
||||||
|
// Modify all existing Room views.
|
||||||
|
for(var key in Ember.View.views) {
|
||||||
|
if ( ! Ember.View.views.hasOwnProperty(key) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var view = Ember.View.views[key];
|
||||||
|
if ( !(view instanceof RoomView) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.log("Manually updating existing Room view.", view);
|
||||||
|
try {
|
||||||
|
view.ffzInit();
|
||||||
|
} catch(err) {
|
||||||
|
this.error("RoomView setup ffzInit: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// View Customization
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
FFZ.prototype._modify_rview = function(view) {
|
||||||
|
var f = this;
|
||||||
|
view.reopen({
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.ffzInit();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("RoomView didInsertElement: " + err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willClearRender: function() {
|
||||||
|
try {
|
||||||
|
this.ffzTeardown();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("RoomView willClearRender: " + err);
|
||||||
|
}
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzInit: function() {
|
||||||
|
f._roomv = this;
|
||||||
|
|
||||||
|
this.ffz_frozen = false;
|
||||||
|
|
||||||
|
if ( f.settings.chat_hover_pause )
|
||||||
|
this.ffzEnableFreeze();
|
||||||
|
|
||||||
|
if ( f.settings.room_status )
|
||||||
|
this.ffzUpdateStatus();
|
||||||
|
|
||||||
|
var controller = this.get('controller');
|
||||||
|
if ( controller ) {
|
||||||
|
controller.reopen({
|
||||||
|
submitButtonText: function() {
|
||||||
|
if ( this.get("model.isWhisperMessage") && this.get("model.isWhispersEnabled") )
|
||||||
|
return i18n("Whisper");
|
||||||
|
|
||||||
|
var wait = this.get("model.slowWait"),
|
||||||
|
msg = this.get("model.messageToSend") || "";
|
||||||
|
|
||||||
|
if ( (msg.charAt(0) === "/" && msg.substr(0, 4) !== "/me ") || !wait || !f.settings.room_status )
|
||||||
|
return i18n("Chat");
|
||||||
|
|
||||||
|
return utils.time_to_string(wait, false, false, true);
|
||||||
|
}.property("model.isWhisperMessage", "model.isWhispersEnabled", "model.slowWait")
|
||||||
|
});
|
||||||
|
|
||||||
|
Ember.propertyDidChange(controller, 'submitButtonText');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzTeardown: function() {
|
||||||
|
if ( f._roomv === this )
|
||||||
|
f._roomv = undefined;
|
||||||
|
|
||||||
|
this.ffzDisableFreeze();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdateStatus: function() {
|
||||||
|
var room = this.get('controller.model'),
|
||||||
|
|
||||||
|
el = this.get('element'),
|
||||||
|
cont = el && el.querySelector('.chat-buttons-container');
|
||||||
|
|
||||||
|
if ( ! cont )
|
||||||
|
return f.log("no container");
|
||||||
|
|
||||||
|
var r9k_badge = cont.querySelector('#ffz-stat-r9k'),
|
||||||
|
sub_badge = cont.querySelector('#ffz-stat-sub'),
|
||||||
|
slow_badge = cont.querySelector('#ffz-stat-slow'),
|
||||||
|
banned_badge = cont.querySelector('#ffz-stat-banned'),
|
||||||
|
btn = cont.querySelector('button');
|
||||||
|
|
||||||
|
if ( f.has_bttv || ! f.settings.room_status ) {
|
||||||
|
if ( r9k_badge )
|
||||||
|
r9k_badge.parentElement.removeChild(r9k_badge);
|
||||||
|
if ( sub_badge )
|
||||||
|
sub_badge.parentElement.removeChild(sub_badge);
|
||||||
|
if ( slow_badge )
|
||||||
|
slow_badge.parentElement.removeChild(slow_badge);
|
||||||
|
|
||||||
|
if ( btn )
|
||||||
|
btn.classList.remove('ffz-waiting');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! r9k_badge ) {
|
||||||
|
r9k_badge = document.createElement('span');
|
||||||
|
r9k_badge.className = 'ffz room-state stat float-right';
|
||||||
|
r9k_badge.id = 'ffz-stat-r9k';
|
||||||
|
r9k_badge.innerHTML = 'R9K';
|
||||||
|
r9k_badge.title = "This room is in R9K-mode.";
|
||||||
|
cont.appendChild(r9k_badge);
|
||||||
|
jQuery(r9k_badge).tipsy({gravity:"s", offset:15});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! sub_badge ) {
|
||||||
|
sub_badge = document.createElement('span');
|
||||||
|
sub_badge.className = 'ffz room-state stat float-right';
|
||||||
|
sub_badge.id = 'ffz-stat-sub';
|
||||||
|
sub_badge.innerHTML = 'SUB';
|
||||||
|
sub_badge.title = "This room is in subscribers-only mode.";
|
||||||
|
cont.appendChild(sub_badge);
|
||||||
|
jQuery(sub_badge).tipsy({gravity:"s", offset:15});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! slow_badge ) {
|
||||||
|
slow_badge = document.createElement('span');
|
||||||
|
slow_badge.className = 'ffz room-state stat float-right';
|
||||||
|
slow_badge.id = 'ffz-stat-slow';
|
||||||
|
slow_badge.innerHTML = 'SLOW';
|
||||||
|
slow_badge.title = "This room is in slow mode. You may send messages every 120 seconds.";
|
||||||
|
cont.appendChild(slow_badge);
|
||||||
|
jQuery(slow_badge).tipsy({gravity:"s", offset:15});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! banned_badge ) {
|
||||||
|
banned_badge = document.createElement('span');
|
||||||
|
banned_badge.className = 'ffz room-state stat float-right';
|
||||||
|
banned_badge.id = 'ffz-stat-banned';
|
||||||
|
banned_badge.innerHTML = 'BAN';
|
||||||
|
banned_badge.title = "You have been banned from talking in this room.";
|
||||||
|
cont.appendChild(banned_badge);
|
||||||
|
jQuery(banned_badge).tipsy({gravity:"s", offset:15});
|
||||||
|
}
|
||||||
|
|
||||||
|
r9k_badge.classList.toggle('hidden', !(room && room.get('r9kMode')));
|
||||||
|
sub_badge.classList.toggle('hidden', !(room && room.get('subsOnlyMode')));
|
||||||
|
slow_badge.classList.toggle('hidden', !(room && room.get('slowMode')));
|
||||||
|
slow_badge.title = "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slowValue')||120) + " seconds.";
|
||||||
|
banned_badge.classList.toggle('hidden', !(room && room.get('ffz_banned')));
|
||||||
|
|
||||||
|
if ( btn ) {
|
||||||
|
btn.classList.toggle('ffz-waiting', (room && room.get('slowWait') || 0));
|
||||||
|
btn.classList.toggle('ffz-banned', (room && room.get('ffz_banned')));
|
||||||
|
}
|
||||||
|
|
||||||
|
}.observes('controller.model'),
|
||||||
|
|
||||||
|
ffzEnableFreeze: function() {
|
||||||
|
var el = this.get('element'),
|
||||||
|
messages = el.querySelector('.chat-messages');
|
||||||
|
|
||||||
|
if ( ! messages )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
|
||||||
|
this._ffz_messages = messages;
|
||||||
|
this._ffz_mouse_move = this.ffzMouseMove.bind(this);
|
||||||
|
this._ffz_mouse_out = this.ffzMouseOut.bind(this);
|
||||||
|
|
||||||
|
messages.addEventListener('mousemove', this._ffz_mouse_move);
|
||||||
|
messages.addEventListener('mouseout', this._ffz_mouse_out);
|
||||||
|
document.addEventListener('mouseout', this._ffz_mouse_out);
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzDisableFreeze: function() {
|
||||||
|
if ( this._ffz_interval ) {
|
||||||
|
clearInterval(this._ffz_interval);
|
||||||
|
this._ffz_interval = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ffzUnfreeze();
|
||||||
|
|
||||||
|
var messages = this._ffz_messages;
|
||||||
|
if ( ! messages )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_messages = undefined;
|
||||||
|
|
||||||
|
if ( this._ffz_mouse_move ) {
|
||||||
|
messages.removeEventListener('mousemove', this._ffz_mouse_move);
|
||||||
|
this._ffz_mouse_move = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this._ffz_mouse_out ) {
|
||||||
|
messages.removeEventListener('mouseout', this._ffz_mouse_out);
|
||||||
|
this._ffz_mouse_out = undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzPulse: function() {
|
||||||
|
if ( this.ffz_frozen ) {
|
||||||
|
var elapsed = Date.now() - this._ffz_last_move;
|
||||||
|
if ( elapsed > 750 )
|
||||||
|
this.ffzUnfreeze();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUnfreeze: function() {
|
||||||
|
this.ffz_frozen = false;
|
||||||
|
this._ffz_last_move = 0;
|
||||||
|
this.ffzUnwarnPaused();
|
||||||
|
|
||||||
|
if ( this.get('stuckToBottom') )
|
||||||
|
this._scrollToBottom();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzMouseOut: function(event) {
|
||||||
|
this._ffz_outside = true;
|
||||||
|
var e = this;
|
||||||
|
Ember.run.next(function() {
|
||||||
|
if ( e._ffz_outside )
|
||||||
|
e.ffzUnfreeze();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzMouseMove: function(event) {
|
||||||
|
this._ffz_last_move = Date.now();
|
||||||
|
this._ffz_outside = false;
|
||||||
|
|
||||||
|
if ( event.screenX === this._ffz_last_screenx && event.screenY === this._ffz_last_screeny )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_last_screenx = event.screenX;
|
||||||
|
this._ffz_last_screeny = event.screenY;
|
||||||
|
|
||||||
|
if ( this.ffz_frozen )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't do it if we're over the bar itself.
|
||||||
|
if ( event.clientY >= (this._ffz_messages.getBoundingClientRect().bottom - 21) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ffz_frozen = true;
|
||||||
|
if ( this.get('stuckToBottom') ) {
|
||||||
|
this.set('controller.model.messageBufferSize', f.settings.scrollback_length + 150);
|
||||||
|
this.ffzWarnPaused();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_scrollToBottom: _.throttle(function() {
|
||||||
|
var e = this,
|
||||||
|
s = this._$chatMessagesScroller;
|
||||||
|
|
||||||
|
Ember.run.next(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
!e.ffz_frozen && s && s.length && (s.scrollTop(s[0].scrollHeight), e._setStuckToBottom(!0));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, 200),
|
||||||
|
|
||||||
|
_setStuckToBottom: function(val) {
|
||||||
|
this.set("stuckToBottom", val);
|
||||||
|
this.get("controller.model") && this.set("controller.model.messageBufferSize", f.settings.scrollback_length + (val ? 0 : 150));
|
||||||
|
},
|
||||||
|
|
||||||
|
// Warnings~!
|
||||||
|
ffzWarnPaused: function() {
|
||||||
|
var el = this.get('element'),
|
||||||
|
warning = el && el.querySelector('.chat-interface .more-messages-indicator.ffz-freeze-indicator');
|
||||||
|
|
||||||
|
if ( ! el )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( ! warning ) {
|
||||||
|
warning = document.createElement('div');
|
||||||
|
warning.className = 'more-messages-indicator ffz-freeze-indicator';
|
||||||
|
warning.innerHTML = '(Chat Paused Due to Mouse Movement)';
|
||||||
|
|
||||||
|
var cont = el.querySelector('.chat-interface');
|
||||||
|
if ( ! cont )
|
||||||
|
return;
|
||||||
|
cont.insertBefore(warning, cont.childNodes[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
warning.classList.remove('hidden');
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
ffzUnwarnPaused: function() {
|
||||||
|
var el = this.get('element'),
|
||||||
|
warning = el && el.querySelector('.chat-interface .more-messages-indicator.ffz-freeze-indicator');
|
||||||
|
|
||||||
|
if ( warning )
|
||||||
|
warning.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,6 +528,27 @@ FFZ.prototype.add_room = function(id, room) {
|
||||||
// Create a basic data table for this room.
|
// Create a basic data table for this room.
|
||||||
var data = this.rooms[id] = {id: id, room: room, menu_sets: [], sets: [], css: null, needs_history: false};
|
var data = this.rooms[id] = {id: id, room: room, menu_sets: [], sets: [], css: null, needs_history: false};
|
||||||
|
|
||||||
|
if ( this.follow_sets && this.follow_sets[id] ) {
|
||||||
|
data.extra_sets = this.follow_sets[id];
|
||||||
|
delete this.follow_sets[id];
|
||||||
|
|
||||||
|
for(var i=0; i < data.extra_sets.length; i++) {
|
||||||
|
var sid = data.extra_sets[i],
|
||||||
|
set = this.emote_sets && this.emote_sets[sid];
|
||||||
|
|
||||||
|
if ( set ) {
|
||||||
|
if ( set.users.indexOf(id) === -1 )
|
||||||
|
set.users.push(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.load_set(sid, function(success, data) {
|
||||||
|
if ( success )
|
||||||
|
data.users.push(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Let the server know where we are.
|
// Let the server know where we are.
|
||||||
this.ws_send("sub", id);
|
this.ws_send("sub", id);
|
||||||
|
|
||||||
|
@ -205,6 +558,9 @@ FFZ.prototype.add_room = function(id, room) {
|
||||||
data.needs_history = true;
|
data.needs_history = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Why don't we set the scrollback length, too?
|
||||||
|
room.set('messageBufferSize', this.settings.scrollback_length + ((this._roomv && !this._roomv.get('stuckToBottom') && this._roomv.get('controller.model.id') === id) ? 150 : 0));
|
||||||
|
|
||||||
// For now, we use the legacy function to grab the .css file.
|
// For now, we use the legacy function to grab the .css file.
|
||||||
this.load_room(id);
|
this.load_room(id);
|
||||||
}
|
}
|
||||||
|
@ -412,6 +768,12 @@ FFZ.prototype._load_room_json = function(room_id, callback, data) {
|
||||||
if ( this.rooms[room_id] )
|
if ( this.rooms[room_id] )
|
||||||
data.room = this.rooms[room_id].room;
|
data.room = this.rooms[room_id].room;
|
||||||
|
|
||||||
|
// Preserve everything else.
|
||||||
|
for(var key in this.rooms[room_id]) {
|
||||||
|
if ( key !== 'room' && this.rooms[room_id].hasOwnProperty(key) && ! data.hasOwnProperty(key) )
|
||||||
|
data[key] = this.rooms[room_id][key];
|
||||||
|
}
|
||||||
|
|
||||||
data.needs_history = this.rooms[room_id] && this.rooms[room_id].needs_history || false;
|
data.needs_history = this.rooms[room_id] && this.rooms[room_id].needs_history || false;
|
||||||
|
|
||||||
this.rooms[room_id] = data;
|
this.rooms[room_id] = data;
|
||||||
|
@ -420,7 +782,12 @@ FFZ.prototype._load_room_json = function(room_id, callback, data) {
|
||||||
utils.update_css(this._room_style, room_id, moderator_css(data) + (data.css||""));
|
utils.update_css(this._room_style, room_id, moderator_css(data) + (data.css||""));
|
||||||
|
|
||||||
if ( ! this.emote_sets.hasOwnProperty(data.set) )
|
if ( ! this.emote_sets.hasOwnProperty(data.set) )
|
||||||
this.load_set(data.set);
|
this.load_set(data.set, function(success, set) {
|
||||||
|
if ( set.users.indexOf(room_id) === -1 )
|
||||||
|
set.users.push(room_id);
|
||||||
|
});
|
||||||
|
else if ( this.emote_sets[data.set].users.indexOf(room_id) === -1 )
|
||||||
|
this.emote_sets[data.set].users.push(room_id);
|
||||||
|
|
||||||
this.update_ui_link();
|
this.update_ui_link();
|
||||||
|
|
||||||
|
@ -436,6 +803,42 @@ FFZ.prototype._load_room_json = function(room_id, callback, data) {
|
||||||
FFZ.prototype._modify_room = function(room) {
|
FFZ.prototype._modify_room = function(room) {
|
||||||
var f = this;
|
var f = this;
|
||||||
room.reopen({
|
room.reopen({
|
||||||
|
subsOnlyMode: false,
|
||||||
|
r9kMode: false,
|
||||||
|
slowWaiting: false,
|
||||||
|
slowValue: 0,
|
||||||
|
|
||||||
|
updateWait: function(value, was_banned) {
|
||||||
|
var wait = this.get('slowWait') || 0;
|
||||||
|
this.set('slowWait', value);
|
||||||
|
if ( wait < 1 && value > 0 ) {
|
||||||
|
setTimeout(this.ffzUpdateWait.bind(this), 1000);
|
||||||
|
f._roomv && f._roomv.ffzUpdateStatus();
|
||||||
|
} else if ( (wait > 0 && value < 1) || was_banned ) {
|
||||||
|
this.set('ffz_banned', false);
|
||||||
|
f._roomv && f._roomv.ffzUpdateStatus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdateWait: function() {
|
||||||
|
var wait = this.get('slowWait') || 0;
|
||||||
|
if ( wait < 1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.set('slowWait', --wait);
|
||||||
|
if ( wait > 0 )
|
||||||
|
setTimeout(this.ffzUpdateWait.bind(this), 1000);
|
||||||
|
else {
|
||||||
|
this.set('ffz_banned', false);
|
||||||
|
f._roomv && f._roomv.ffzUpdateStatus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzUpdateStatus: function() {
|
||||||
|
if ( f._roomv )
|
||||||
|
f._roomv.ffzUpdateStatus();
|
||||||
|
}.observes('r9kMode', 'subsOnlyMode', 'slowMode', 'slowValue', 'ffz_banned'),
|
||||||
|
|
||||||
// Track which rooms the user is currently in.
|
// Track which rooms the user is currently in.
|
||||||
init: function() {
|
init: function() {
|
||||||
this._super();
|
this._super();
|
||||||
|
@ -466,6 +869,23 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
t.set("messages." + n + ".deleted", true);
|
t.set("messages." + n + ".deleted", true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ( f.settings.mod_card_history ) {
|
||||||
|
var room = f.rooms && f.rooms[t.get('id')],
|
||||||
|
user_history = room && room.user_history && room.user_history[user]
|
||||||
|
|
||||||
|
if ( user_history !== null && user_history !== undefined ) {
|
||||||
|
var has_delete = false,
|
||||||
|
last = user_history.length > 0 ? user_history[user_history.length-1] : null;
|
||||||
|
|
||||||
|
has_delete = last !== null && last.is_delete;
|
||||||
|
if ( ! has_delete ) {
|
||||||
|
user_history.push({from: 'jtv', is_delete: true, style: 'admin', cachedTokens: ['User has been timed out.'], date: new Date()});
|
||||||
|
while ( user_history.length > 20 )
|
||||||
|
user_history.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( f.settings.prevent_clear )
|
if ( f.settings.prevent_clear )
|
||||||
this.addTmiMessage("A moderator's attempt to clear chat was ignored.");
|
this.addTmiMessage("A moderator's attempt to clear chat was ignored.");
|
||||||
|
@ -504,6 +924,39 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
msg.room = this.get('id');
|
msg.room = this.get('id');
|
||||||
|
|
||||||
f.tokenize_chat_line(msg);
|
f.tokenize_chat_line(msg);
|
||||||
|
|
||||||
|
// Keep the history.
|
||||||
|
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_history ) {
|
||||||
|
var room = f.rooms && f.rooms[msg.room];
|
||||||
|
if ( room ) {
|
||||||
|
var chat_history = room.user_history = room.user_history || {},
|
||||||
|
user_history = room.user_history[msg.from] = room.user_history[msg.from] || [];
|
||||||
|
|
||||||
|
user_history.push({
|
||||||
|
from: msg.tags && msg.tags['display-name'] || msg.from,
|
||||||
|
cachedTokens: msg.cachedTokens,
|
||||||
|
style: msg.style,
|
||||||
|
date: msg.date
|
||||||
|
});
|
||||||
|
while ( user_history.length > 20 )
|
||||||
|
user_history.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for message from us.
|
||||||
|
if ( ! is_whisper ) {
|
||||||
|
var user = f.get_user();
|
||||||
|
if ( user && user.login === msg.from ) {
|
||||||
|
var was_banned = this.get('ffz_banned');
|
||||||
|
this.set('ffz_banned', false);
|
||||||
|
|
||||||
|
// Update the wait time.
|
||||||
|
if ( this.get('isModeratorOrHigher') || ! this.get('slowMode') )
|
||||||
|
this.updateWait(0, was_banned)
|
||||||
|
else if ( this.get('slowMode') )
|
||||||
|
this.updateWait(this.get('slowValue'));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
f.error("Room addMessage: " + err);
|
f.error("Room addMessage: " + err);
|
||||||
|
@ -578,6 +1031,7 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
ffzUpdateChatters: function(add, remove) {
|
ffzUpdateChatters: function(add, remove) {
|
||||||
var chatters = this.get("ffz_chatters") || {};
|
var chatters = this.get("ffz_chatters") || {};
|
||||||
if ( add )
|
if ( add )
|
||||||
|
@ -591,8 +1045,10 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( f._cindex )
|
if ( f._cindex )
|
||||||
f._cindex.ffzUpdateChatters();
|
f._cindex.ffzUpdateChatters();
|
||||||
|
|
||||||
|
try {
|
||||||
if ( window.parent && window.parent.postMessage )
|
if ( window.parent && window.parent.postMessage )
|
||||||
window.parent.postMessage({from_ffz: true, command: 'chatter_count', message: Object.keys(this.get('ffz_chatters') || {}).length}, "http://www.twitch.tv/");
|
window.parent.postMessage({from_ffz: true, command: 'chatter_count', message: Object.keys(this.get('ffz_chatters') || {}).length}, "http://www.twitch.tv/");
|
||||||
|
} catch(err) { /* Ignore errors because of security */ }
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
@ -606,24 +1062,8 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
var tmi = this.get('tmiRoom'),
|
var tmi = this.get('tmiRoom'),
|
||||||
room = this;
|
room = this;
|
||||||
|
|
||||||
// This method is stupid and bad and it leaks between rooms.
|
|
||||||
if ( ! tmi.ffz_notice_patched ) {
|
|
||||||
tmi.ffz_notice_patched = true;
|
|
||||||
|
|
||||||
tmi._roomConn.off("notice", tmi._onNotice, tmi);
|
|
||||||
tmi._roomConn.on("notice", function(ircMsg) {
|
|
||||||
var target = ircMsg.target || (ircMsg.params && ircMsg.params[0]) || this.ircChannel;
|
|
||||||
if( target != this.ircChannel )
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._trigger("notice", {
|
|
||||||
msgId: ircMsg.tags['msg-id'],
|
|
||||||
message: ircMsg.message
|
|
||||||
});
|
|
||||||
}, tmi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's get chatter information!
|
// Let's get chatter information!
|
||||||
|
// TODO: Remove this cause it's terrible.
|
||||||
var connection = tmi._roomConn._connection;
|
var connection = tmi._roomConn._connection;
|
||||||
if ( ! connection.ffz_cap_patched ) {
|
if ( ! connection.ffz_cap_patched ) {
|
||||||
connection.ffz_cap_patched = true;
|
connection.ffz_cap_patched = true;
|
||||||
|
@ -632,17 +1072,82 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
connection.on("opened", function() {
|
connection.on("opened", function() {
|
||||||
this._send("CAP REQ :twitch.tv/membership");
|
this._send("CAP REQ :twitch.tv/membership");
|
||||||
}, connection);
|
}, connection);
|
||||||
|
}
|
||||||
|
|
||||||
// Since TMI starts sending SPECIALUSER with this, we need to
|
|
||||||
// ignore that. \ CatBag /
|
// NOTICE for catching slow-mode updates
|
||||||
var orig_handle = connection._handleTmiPrivmsg.bind(connection);
|
tmi.on('notice', function(msg) {
|
||||||
connection._handleTmiPrivmsg = function(msg) {
|
if ( msg.msgId === 'msg_slowmode' ) {
|
||||||
if ( msg.message && msg.message.split(" ",1)[0] === "SPECIALUSER" )
|
var match = /in (\d+) seconds/.exec(msg.message);
|
||||||
|
if ( match ) {
|
||||||
|
room.updateWait(parseInt(match[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( msg.msgId === 'msg_timedout' ) {
|
||||||
|
var match = /for (\d+) more seconds/.exec(msg.message);
|
||||||
|
if ( match ) {
|
||||||
|
room.set('ffz_banned', true);
|
||||||
|
room.updateWait(parseInt(match[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( msg.msgId === 'msg_banned' ) {
|
||||||
|
room.set('ffz_banned', true);
|
||||||
|
f._roomv && f._roomv.ffzUpdateStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// ROOMSTATE~!
|
||||||
|
if ( ! connection.ffz_roomstate_patched ) {
|
||||||
|
connection.ffz_roomstate_patched = true;
|
||||||
|
connection._socket.off('data', connection._onSocketDataReceived, connection);
|
||||||
|
connection._socket.on('data', function(data) {
|
||||||
|
try {
|
||||||
|
var msg = utils.splitIRCMessage(data.data);
|
||||||
|
if ( msg.command === 'ROOMSTATE' ) {
|
||||||
|
// We have ROOMSTATE! Now, let's parse it a bit
|
||||||
|
// more and send it on.
|
||||||
|
msg.tags = utils.parseIRCTags(msg.tags);
|
||||||
|
msg.target = msg.params && msg.params[0];
|
||||||
|
|
||||||
|
this._trigger('roomstate', msg);
|
||||||
return;
|
return;
|
||||||
return orig_handle(msg);
|
|
||||||
}
|
}
|
||||||
|
} catch(err) { f.error("Connection onData: " + err); }
|
||||||
|
|
||||||
|
return this._onSocketDataReceived(data);
|
||||||
|
}, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Glorious ROOMSTATE.
|
||||||
|
if ( ! tmi.ffz_roomstate_patched ) {
|
||||||
|
tmi.ffz_roomstate_patched = true;
|
||||||
|
tmi._roomConn.on("roomstate", function(ircMsg) {
|
||||||
|
if ( ircMsg.target !== this.ircChannel )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._trigger("roomstate", ircMsg.tags);
|
||||||
|
}, tmi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IT IS GLORIOUS!
|
||||||
|
tmi.on('roomstate', function(state) {
|
||||||
|
if ( state.hasOwnProperty('slow') ) {
|
||||||
|
room.set('slowMode', state.slow > 0);
|
||||||
|
room.set('slowValue', state.slow);
|
||||||
|
if ( ! room.get('slowMode') )
|
||||||
|
room.updateWait(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( state.hasOwnProperty('r9k') )
|
||||||
|
room.set('r9kMode', state.r9k);
|
||||||
|
|
||||||
|
if ( state.hasOwnProperty('subs-only') )
|
||||||
|
room.set('subsOnlyMode', state['subs-only']);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
// Check this shit.
|
// Check this shit.
|
||||||
tmi._roomConn._connection.off("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
tmi._roomConn._connection.off("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
||||||
|
@ -675,32 +1180,6 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
tmi._roomConn._connection.on("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
tmi._roomConn._connection.on("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
||||||
|
|
||||||
|
|
||||||
// Okay, we need to patch the *session's* updateUserState
|
|
||||||
if ( ! tmi.session.ffz_patched ) {
|
|
||||||
tmi.session.ffz_patched = true;
|
|
||||||
var uus = tmi.session._updateUserState.bind(tmi.session);
|
|
||||||
|
|
||||||
tmi.session._updateUserState = function(user, tags) {
|
|
||||||
try {
|
|
||||||
if ( tags.color )
|
|
||||||
this._onUserColorChanged(user, tags.color);
|
|
||||||
|
|
||||||
if ( tags['display-name'] )
|
|
||||||
this._onUserDisplayNameChanged(user, tags['display-name']);
|
|
||||||
|
|
||||||
if ( tags.turbo )
|
|
||||||
this._onUserSpecialAdded(user, 'turbo');
|
|
||||||
|
|
||||||
if ( tags['user_type'] === 'staff' || tags['user_type'] === 'admin' || tags['user_type'] === 'global_mod' )
|
|
||||||
this._onUserSpecialAdded(user, tags['user-type']);
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
f.error("SessionManager _updateUserState: " + err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('ffz_is_patched', true);
|
this.set('ffz_is_patched', true);
|
||||||
|
|
||||||
}.observes('tmiRoom')
|
}.observes('tmiRoom')
|
||||||
|
|
|
@ -57,6 +57,7 @@ var FFZ = window.FrankerFaceZ,
|
||||||
FFZ.prototype.setup_emoticons = function() {
|
FFZ.prototype.setup_emoticons = function() {
|
||||||
this.log("Preparing emoticon system.");
|
this.log("Preparing emoticon system.");
|
||||||
|
|
||||||
|
this.emoji_data = {};
|
||||||
this.emote_sets = {};
|
this.emote_sets = {};
|
||||||
this.global_sets = [];
|
this.global_sets = [];
|
||||||
this.default_sets = [];
|
this.default_sets = [];
|
||||||
|
@ -74,6 +75,9 @@ FFZ.prototype.setup_emoticons = function() {
|
||||||
this.log("Loading global emote sets.");
|
this.log("Loading global emote sets.");
|
||||||
this.load_global_sets();
|
this.load_global_sets();
|
||||||
|
|
||||||
|
this.log("Loading emoji data.");
|
||||||
|
this.load_emoji_data();
|
||||||
|
|
||||||
this.log("Watching Twitch emoticon parser to ensure it loads.");
|
this.log("Watching Twitch emoticon parser to ensure it loads.");
|
||||||
this._twitch_emote_check = setTimeout(this.check_twitch_emotes.bind(this), 10000);
|
this._twitch_emote_check = setTimeout(this.check_twitch_emotes.bind(this), 10000);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +159,7 @@ FFZ.prototype.getEmotes = function(user_id, room_id) {
|
||||||
var user = this.users && this.users[user_id],
|
var user = this.users && this.users[user_id],
|
||||||
room = this.rooms && this.rooms[room_id];
|
room = this.rooms && this.rooms[room_id];
|
||||||
|
|
||||||
return _.union(user && user.sets || [], room && room.set && [room.set] || [], this.default_sets);
|
return _.union(user && user.sets || [], room && room.set && [room.set] || [], room && room.extra_sets || [], this.default_sets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,6 +198,49 @@ FFZ.prototype._emote_tooltip = function(emote) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Emoji Loading
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
FFZ.prototype.load_emoji_data = function(callback, tries) {
|
||||||
|
var f = this;
|
||||||
|
jQuery.getJSON(constants.SERVER + "emoji/emoji.json")
|
||||||
|
.done(function(data) {
|
||||||
|
var new_data = {};
|
||||||
|
for(var eid in data) {
|
||||||
|
var emoji = data[eid];
|
||||||
|
eid = eid.toLowerCase();
|
||||||
|
new_data[eid] = emoji;
|
||||||
|
|
||||||
|
emoji.src = constants.SERVER + 'emoji/' + eid + '-1x.png';
|
||||||
|
emoji.srcSet = emoji.src + ' 1x, ' + constants.SERVER + 'emoji/' + eid + '-2x.png 2x, ' + constants.SERVER + 'emoji/' + eid + '-4x.png 4x';
|
||||||
|
|
||||||
|
emoji.token = {
|
||||||
|
srcSet: emoji.srcSet,
|
||||||
|
emoticonSrc: emoji.src + '" data-ffz-emoji="' + eid + '" height="18px',
|
||||||
|
ffzEmoji: eid,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
f.emoji_data = new_data;
|
||||||
|
f.log("Loaded data on " + Object.keys(new_data).length + " emoji.");
|
||||||
|
if ( typeof callback === "function" )
|
||||||
|
callback(true, data);
|
||||||
|
|
||||||
|
}).fail(function(data) {
|
||||||
|
if ( data.status === 404 )
|
||||||
|
return typeof callback === "function" && callback(false);
|
||||||
|
|
||||||
|
tries = (tries || 0) + 1;
|
||||||
|
if ( tries < 50 )
|
||||||
|
return f.load_emoji(callback, tries);
|
||||||
|
|
||||||
|
return typeof callback === "function" && callback(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Set Loading
|
// Set Loading
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -264,9 +311,12 @@ FFZ.prototype._load_set_json = function(set_id, callback, data) {
|
||||||
if ( ! data )
|
if ( ! data )
|
||||||
return typeof callback == "function" && callback(false);
|
return typeof callback == "function" && callback(false);
|
||||||
|
|
||||||
|
// Do we have existing users?
|
||||||
|
var users = this.emote_sets[set_id] && this.emote_sets[set_id].users || [];
|
||||||
|
|
||||||
// Store our set.
|
// Store our set.
|
||||||
this.emote_sets[set_id] = data;
|
this.emote_sets[set_id] = data;
|
||||||
data.users = [];
|
data.users = users;
|
||||||
data.count = 0;
|
data.count = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
|
constants = require('../constants'),
|
||||||
|
utils = require('../utils'),
|
||||||
SENDER_REGEX = /(\sdata-sender="[^"]*"(?=>))/;
|
SENDER_REGEX = /(\sdata-sender="[^"]*"(?=>))/;
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +39,16 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
this._chatv.ffzDisableTabs();
|
this._chatv.ffzDisableTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this._roomv ) {
|
||||||
|
// Disable Chat Pause
|
||||||
|
if ( this.settings.chat_hover_pause )
|
||||||
|
this._roomv.ffzDisableFreeze();
|
||||||
|
|
||||||
|
// And hide the status
|
||||||
|
if ( this.settings.room_status )
|
||||||
|
this._roomv.ffzUpdateStatus();
|
||||||
|
}
|
||||||
|
|
||||||
// Disable other features too.
|
// Disable other features too.
|
||||||
document.body.classList.remove("ffz-chat-colors");
|
document.body.classList.remove("ffz-chat-colors");
|
||||||
document.body.classList.remove("ffz-chat-background");
|
document.body.classList.remove("ffz-chat-background");
|
||||||
|
@ -45,6 +57,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
if ( this.is_dashboard )
|
if ( this.is_dashboard )
|
||||||
this._update_subscribers();
|
this._update_subscribers();
|
||||||
|
|
||||||
|
document.body.classList.add('ffz-bttv');
|
||||||
|
|
||||||
// Send Message Behavior
|
// Send Message Behavior
|
||||||
var original_send = BetterTTV.chat.helpers.sendMessage, f = this;
|
var original_send = BetterTTV.chat.helpers.sendMessage, f = this;
|
||||||
|
@ -91,6 +104,26 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whispers too!
|
||||||
|
var original_whisper = BetterTTV.chat.templates.whisper;
|
||||||
|
BetterTTV.chat.templates.whisper = function(data) {
|
||||||
|
try {
|
||||||
|
// Handle badges.
|
||||||
|
f.bttv_badges(data);
|
||||||
|
|
||||||
|
// Now, do everything else manually because things are hard-coded.
|
||||||
|
return '<div class="chat-line whisper" data-sender="' + data.sender + '">' +
|
||||||
|
BetterTTV.chat.templates.timestamp(data.time) + ' ' +
|
||||||
|
(data.badges && data.badges.length ? BetterTTV.chat.templates.badges(data.badges) : '') +
|
||||||
|
BetterTTV.chat.templates.whisperName(data.sender, data.receiver, data.from, data.to, data.fromColor, data.toColor) +
|
||||||
|
BetterTTV.chat.templates.message(data.sender, data.message, data.emotes, false) +
|
||||||
|
'</div>';
|
||||||
|
} catch(err) {
|
||||||
|
f.log("Error: ", err);
|
||||||
|
return original_whisper(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Message Renderer. I had to completely rewrite this method to get it to
|
// Message Renderer. I had to completely rewrite this method to get it to
|
||||||
// use my replacement emoticonizer.
|
// use my replacement emoticonizer.
|
||||||
var original_message = BetterTTV.chat.templates.message,
|
var original_message = BetterTTV.chat.templates.message,
|
||||||
|
@ -151,9 +184,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't bother proceeding if we have no emotes.
|
// Don't bother proceeding if we have no emotes.
|
||||||
if ( ! emotes.length )
|
if ( emotes.length ) {
|
||||||
return tokens;
|
|
||||||
|
|
||||||
// Why is emote parsing so bad? ;_;
|
// Why is emote parsing so bad? ;_;
|
||||||
_.each(emotes, function(emote) {
|
_.each(emotes, function(emote) {
|
||||||
var tooltip = f._emote_tooltip(emote),
|
var tooltip = f._emote_tooltip(emote),
|
||||||
|
@ -162,9 +193,6 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
|
|
||||||
tokens = [];
|
tokens = [];
|
||||||
|
|
||||||
if ( ! old_tokens || ! old_tokens.length )
|
|
||||||
return tokens;
|
|
||||||
|
|
||||||
for(var i=0; i < old_tokens.length; i++) {
|
for(var i=0; i < old_tokens.length; i++) {
|
||||||
var token = old_tokens[i];
|
var token = old_tokens[i];
|
||||||
if ( typeof token != "string" ) {
|
if ( typeof token != "string" ) {
|
||||||
|
@ -191,6 +219,45 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sneak in Emojicon Processing
|
||||||
|
if ( f.settings.parse_emoji && f.emoji_data ) {
|
||||||
|
var old_tokens = tokens;
|
||||||
|
tokens = [];
|
||||||
|
|
||||||
|
for(var i=0; i < old_tokens.length; i++) {
|
||||||
|
var token = old_tokens[i];
|
||||||
|
if ( typeof token !== "string" ) {
|
||||||
|
tokens.push(token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tbits = token.split(constants.EMOJI_REGEX);
|
||||||
|
while(tbits.length) {
|
||||||
|
var bit = tbits.shift();
|
||||||
|
bit && tokens.push(bit);
|
||||||
|
|
||||||
|
if ( tbits.length ) {
|
||||||
|
var match = tbits.shift(),
|
||||||
|
variant = tbits.shift();
|
||||||
|
|
||||||
|
if ( variant === '\uFE0E' )
|
||||||
|
bits.push(match);
|
||||||
|
else {
|
||||||
|
var eid = utils.emoji_to_codepoint(match, variant),
|
||||||
|
data = f.emoji_data[eid],
|
||||||
|
alt = match + (variant || "");
|
||||||
|
|
||||||
|
if ( data ) {
|
||||||
|
tokens.push(['<img class="emoticon" height="18px" srcset="' + (data.srcSet || "") + '" src="' + data.src + '" alt="' + alt + '" title="Emoji: ' + alt + '\nName: ' + data.short_name + '">']);
|
||||||
|
} else
|
||||||
|
tokens.push(match + (variant || ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
16
src/main.js
16
src/main.js
|
@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
var VER = FFZ.version_info = {
|
var VER = FFZ.version_info = {
|
||||||
major: 3, minor: 4, revision: 2,
|
major: 3, minor: 4, revision: 10,
|
||||||
toString: function() {
|
toString: function() {
|
||||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||||
}
|
}
|
||||||
|
@ -102,8 +102,6 @@ FFZ.prototype.get_user = function() {
|
||||||
// Import Everything!
|
// Import Everything!
|
||||||
// -------------------
|
// -------------------
|
||||||
|
|
||||||
//require('./templates');
|
|
||||||
|
|
||||||
// Import these first to set up data structures
|
// Import these first to set up data structures
|
||||||
require('./ui/menu');
|
require('./ui/menu');
|
||||||
require('./settings');
|
require('./settings');
|
||||||
|
@ -140,6 +138,7 @@ require('./ui/viewer_count');
|
||||||
require('./ui/sub_count');
|
require('./ui/sub_count');
|
||||||
|
|
||||||
require('./ui/menu_button');
|
require('./ui/menu_button');
|
||||||
|
require('./ui/following');
|
||||||
require('./ui/races');
|
require('./ui/races');
|
||||||
require('./ui/my_emotes');
|
require('./ui/my_emotes');
|
||||||
require('./ui/about_page');
|
require('./ui/about_page');
|
||||||
|
@ -190,6 +189,10 @@ FFZ.prototype.setup_normal = function(delay) {
|
||||||
this.log("Found non-Ember Twitch after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
this.log("Found non-Ember Twitch after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||||
|
|
||||||
this.users = {};
|
this.users = {};
|
||||||
|
this.is_dashboard = false;
|
||||||
|
try {
|
||||||
|
this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname);
|
||||||
|
} catch(err) { this.embed_in_dash = false; }
|
||||||
|
|
||||||
// Initialize all the modules.
|
// Initialize all the modules.
|
||||||
this.load_settings();
|
this.load_settings();
|
||||||
|
@ -222,6 +225,7 @@ FFZ.prototype.setup_dashboard = function(delay) {
|
||||||
|
|
||||||
this.users = {};
|
this.users = {};
|
||||||
this.is_dashboard = true;
|
this.is_dashboard = true;
|
||||||
|
this.embed_in_dash = false;
|
||||||
|
|
||||||
// Initialize all the modules.
|
// Initialize all the modules.
|
||||||
this.load_settings();
|
this.load_settings();
|
||||||
|
@ -255,6 +259,10 @@ FFZ.prototype.setup_ember = function(delay) {
|
||||||
this.log("Found Twitch application after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
this.log("Found Twitch application after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||||
|
|
||||||
this.users = {};
|
this.users = {};
|
||||||
|
this.is_dashboard = false;
|
||||||
|
try {
|
||||||
|
this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname);
|
||||||
|
} catch(err) { this.embed_in_dash = false; }
|
||||||
|
|
||||||
// Initialize all the modules.
|
// Initialize all the modules.
|
||||||
this.load_settings();
|
this.load_settings();
|
||||||
|
@ -282,6 +290,7 @@ FFZ.prototype.setup_ember = function(delay) {
|
||||||
this.setup_css();
|
this.setup_css();
|
||||||
this.setup_menu();
|
this.setup_menu();
|
||||||
this.setup_my_emotes();
|
this.setup_my_emotes();
|
||||||
|
this.setup_following();
|
||||||
this.setup_races();
|
this.setup_races();
|
||||||
|
|
||||||
this.connect_extra_chat();
|
this.connect_extra_chat();
|
||||||
|
@ -313,5 +322,4 @@ FFZ.prototype._on_window_message = function(e) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var msg = e.data;
|
var msg = e.data;
|
||||||
this.log("Window Message", msg);
|
|
||||||
}
|
}
|
|
@ -51,8 +51,10 @@ FFZ.prototype.ws_create = function() {
|
||||||
// Join the right channel if we're in the dashboard.
|
// Join the right channel if we're in the dashboard.
|
||||||
if ( f.is_dashboard ) {
|
if ( f.is_dashboard ) {
|
||||||
var match = location.pathname.match(/\/([^\/]+)/);
|
var match = location.pathname.match(/\/([^\/]+)/);
|
||||||
if ( match )
|
if ( match ) {
|
||||||
f.ws_send("sub", match[1]);
|
f.ws_send("sub", match[1]);
|
||||||
|
f.ws_send("sub_channel", match[1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the current rooms.
|
// Send the current rooms.
|
||||||
|
@ -69,6 +71,18 @@ FFZ.prototype.ws_create = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the channel(s).
|
||||||
|
if ( f._cindex ) {
|
||||||
|
var channel_id = f._cindex.get('controller.id'),
|
||||||
|
hosted_id = f._cindex.get('controller.hostModeTarget.id');
|
||||||
|
|
||||||
|
if ( channel_id )
|
||||||
|
f.ws_send("sub_channel", channel_id);
|
||||||
|
|
||||||
|
if ( hosted_id )
|
||||||
|
f.ws_send("sub_channel", hosted_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Send any pending commands.
|
// Send any pending commands.
|
||||||
var pending = f._ws_pending;
|
var pending = f._ws_pending;
|
||||||
f._ws_pending = [];
|
f._ws_pending = [];
|
||||||
|
|
167
src/tokenize.js
167
src/tokenize.js
|
@ -1,5 +1,6 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
utils = require("./utils"),
|
utils = require("./utils"),
|
||||||
|
constants = require("./constants"),
|
||||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||||
helpers,
|
helpers,
|
||||||
|
|
||||||
|
@ -10,11 +11,28 @@ var FFZ = window.FrankerFaceZ,
|
||||||
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
|
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
|
||||||
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*");
|
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*");
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
|
helpers = window.require && window.require("ember-twitch-chat/helpers/chat-line-helpers");
|
||||||
} catch(err) { }
|
} catch(err) { }
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.SRC_IDS = {},
|
||||||
|
FFZ.src_to_id = function(src) {
|
||||||
|
if ( FFZ.SRC_IDS.hasOwnProperty(src) )
|
||||||
|
return FFZ.SRC_IDS[src];
|
||||||
|
|
||||||
|
var match = /\/emoticons\/v1\/(\d+)\/1\.0/.exec(src),
|
||||||
|
id = match ? parseInt(match[1]) : null;
|
||||||
|
|
||||||
|
if ( id === NaN )
|
||||||
|
id = null;
|
||||||
|
|
||||||
|
FFZ.SRC_IDS[src] = id;
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Tokenization
|
// Tokenization
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -41,6 +59,9 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
tokens = this._remove_banned(tokens);
|
tokens = this._remove_banned(tokens);
|
||||||
tokens = this.tokenize_emotes(msgObject.from, room_id, tokens, from_me);
|
tokens = this.tokenize_emotes(msgObject.from, room_id, tokens, from_me);
|
||||||
|
|
||||||
|
if ( this.settings.parse_emoji )
|
||||||
|
tokens = this.tokenize_emoji(tokens);
|
||||||
|
|
||||||
// Capitalization
|
// Capitalization
|
||||||
var display = msgObject.tags && msgObject.tags['display-name'];
|
var display = msgObject.tags && msgObject.tags['display-name'];
|
||||||
if ( display && display.length )
|
if ( display && display.length )
|
||||||
|
@ -53,7 +74,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
|
|
||||||
for(var i=0; i < tokens.length; i++) {
|
for(var i=0; i < tokens.length; i++) {
|
||||||
var token = tokens[i];
|
var token = tokens[i];
|
||||||
if ( _.isString(token) || ! token.mentionedUser || token.own || msgObject.style === 'whisper' )
|
if ( msgObject.style !== 'whisper' && (_.isString(token) || ! token.mentionedUser || token.own) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// We have a mention!
|
// We have a mention!
|
||||||
|
@ -69,7 +90,7 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
// Display notifications if that setting is enabled. Also make sure
|
// Display notifications if that setting is enabled. Also make sure
|
||||||
// that we have a chat view because showing a notification when we
|
// that we have a chat view because showing a notification when we
|
||||||
// can't actually go to it is a bad thing.
|
// can't actually go to it is a bad thing.
|
||||||
if ( this._chatv && this.settings.highlight_notifications && ! document.hasFocus() && ! prevent_notification ) {
|
if ( this._chatv && this.settings.highlight_notifications && ! this.embed_in_dash && ! document.hasFocus() && ! prevent_notification ) {
|
||||||
var room = this.rooms[room_id] && this.rooms[room_id].room,
|
var room = this.rooms[room_id] && this.rooms[room_id].room,
|
||||||
room_name;
|
room_name;
|
||||||
|
|
||||||
|
@ -86,6 +107,17 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
msg = display + ': ' + msg;
|
msg = display + ': ' + msg;
|
||||||
|
|
||||||
var f = this;
|
var f = this;
|
||||||
|
if ( msgObject.style === 'whisper' )
|
||||||
|
this.show_notification(
|
||||||
|
msg,
|
||||||
|
"Twitch Chat Whisper",
|
||||||
|
"ffz_whisper_notice",
|
||||||
|
60000,
|
||||||
|
function() {
|
||||||
|
window.focus();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
else
|
||||||
this.show_notification(
|
this.show_notification(
|
||||||
msg,
|
msg,
|
||||||
"Twitch Chat Mention in " + room_name,
|
"Twitch Chat Mention in " + room_name,
|
||||||
|
@ -129,9 +161,31 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes) {
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.render_tokens = function(tokens, render_links) {
|
FFZ.prototype.render_tokens = function(tokens, render_links) {
|
||||||
|
var f = this;
|
||||||
return _.map(tokens, function(token) {
|
return _.map(tokens, function(token) {
|
||||||
if ( token.emoticonSrc )
|
if ( token.emoticonSrc ) {
|
||||||
return '<img class="emoticon tooltip" src="' + token.emoticonSrc + '" alt="' + token.altText + '" title="' + token.altText + '">';
|
var tooltip;
|
||||||
|
if ( token.ffzEmote ) {
|
||||||
|
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
||||||
|
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
||||||
|
|
||||||
|
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
||||||
|
|
||||||
|
} else if ( token.ffzEmoji ) {
|
||||||
|
var eid = token.ffzEmoji,
|
||||||
|
emoji = f.emoji_data && f.emoji_data[eid];
|
||||||
|
|
||||||
|
tooltip = emoji ? "Emoji: " + token.altText + "\nName: " + emoji.short_name : token.altText;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var id = FFZ.src_to_id(token.emoticonSrc),
|
||||||
|
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
||||||
|
|
||||||
|
tooltip = data && data.tooltip ? data.tooltip : token.altText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<img class="emoticon tooltip" src="' + token.emoticonSrc + '" ' + (token.srcSet ? 'srcset="' + token.srcSet + '" ' : '') + 'alt="' + token.altText + '" title="' + tooltip + '">';
|
||||||
|
}
|
||||||
|
|
||||||
if ( token.isLink ) {
|
if ( token.isLink ) {
|
||||||
if ( ! render_links && render_links !== undefined )
|
if ( ! render_links && render_links !== undefined )
|
||||||
|
@ -142,7 +196,11 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
||||||
return '<a href="mailto:' + s + '">' + s + '</a>';
|
return '<a href="mailto:' + s + '">' + s + '</a>';
|
||||||
|
|
||||||
var n = (s.match(/^https?:\/\//) ? "" : "http://") + s;
|
var n = (s.match(/^https?:\/\//) ? "" : "http://") + s;
|
||||||
return '<a href="' + n + '" target="_blank">' + s + '</a>';
|
|
||||||
|
// Check for link data.
|
||||||
|
var data = f._link_data && f._link_data[n] || {};
|
||||||
|
|
||||||
|
return '<a href="' + n + '" class="' + (data.unsafe ? 'unsafe-link' : '') + '" title="' + utils.sanitize(data.tooltip || '') + '" target="_blank">' + s + '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( token.mentionedUser )
|
if ( token.mentionedUser )
|
||||||
|
@ -257,7 +315,7 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
||||||
return tokens;
|
return tokens;
|
||||||
|
|
||||||
// Now that we have all the matching tokens, do crazy stuff.
|
// Now that we have all the matching tokens, do crazy stuff.
|
||||||
if ( typeof tokens == "string" )
|
if ( typeof tokens === "string" )
|
||||||
tokens = [tokens];
|
tokens = [tokens];
|
||||||
|
|
||||||
// This is weird stuff I basically copied from the old Twitch code.
|
// This is weird stuff I basically copied from the old Twitch code.
|
||||||
|
@ -269,6 +327,8 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
||||||
var eo = {
|
var eo = {
|
||||||
srcSet: emote.srcSet,
|
srcSet: emote.srcSet,
|
||||||
emoticonSrc: emote.urls[1] + '" data-ffz-emote="' + encodeURIComponent(JSON.stringify([emote.id, emote.set_id])),
|
emoticonSrc: emote.urls[1] + '" data-ffz-emote="' + encodeURIComponent(JSON.stringify([emote.id, emote.set_id])),
|
||||||
|
ffzEmote: emote.id,
|
||||||
|
ffzEmoteSet: emote.set_id,
|
||||||
altText: (emote.hidden ? "???" : emote.name)
|
altText: (emote.hidden ? "???" : emote.name)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -301,6 +361,58 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Emoji Processing
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
FFZ.prototype.tokenize_emoji = function(tokens) {
|
||||||
|
if ( typeof tokens === "string" )
|
||||||
|
tokens = [tokens];
|
||||||
|
|
||||||
|
if ( ! this.emoji_data )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
var f = this;
|
||||||
|
|
||||||
|
return _.compact(_.flatten(_.map(tokens, function(token) {
|
||||||
|
if ( _.isObject(token) )
|
||||||
|
return token;
|
||||||
|
|
||||||
|
var tbits = token.split(constants.EMOJI_REGEX), bits = [];
|
||||||
|
while(tbits.length) {
|
||||||
|
// Deal with the unmatched string first.
|
||||||
|
var bit = tbits.shift();
|
||||||
|
bit && bits.push(bit);
|
||||||
|
|
||||||
|
if ( tbits.length ) {
|
||||||
|
// We have an emoji too, so let's handle that.
|
||||||
|
var match = tbits.shift(),
|
||||||
|
variant = tbits.shift();
|
||||||
|
|
||||||
|
if ( variant === '\uFE0E' ) {
|
||||||
|
// Text Variant
|
||||||
|
bits.push(match);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Find the right image~!
|
||||||
|
var eid = utils.emoji_to_codepoint(match, variant),
|
||||||
|
data = f.emoji_data[eid],
|
||||||
|
alt = match + (variant || "");
|
||||||
|
|
||||||
|
if ( data ) {
|
||||||
|
data.token.altText = alt;
|
||||||
|
bits.push(data.token);
|
||||||
|
} else
|
||||||
|
bits.push(alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bits;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// Mention Parsing
|
// Mention Parsing
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -368,3 +480,46 @@ FFZ.prototype.tokenize_mentions = function(tokens) {
|
||||||
|
|
||||||
return new_tokens;
|
return new_tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------
|
||||||
|
// Handling Bad Stuff
|
||||||
|
// ---------------------
|
||||||
|
|
||||||
|
FFZ.prototype._deleted_link_click = function(e) {
|
||||||
|
if ( ! this.classList.contains("deleted-link") )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Get the URL
|
||||||
|
var href = this.getAttribute('data-url'),
|
||||||
|
link = href,
|
||||||
|
f = FrankerFaceZ.get();
|
||||||
|
|
||||||
|
// Delete Old Stuff
|
||||||
|
this.classList.remove('deleted-link');
|
||||||
|
this.removeAttribute("data-url");
|
||||||
|
this.removeAttribute("title");
|
||||||
|
this.removeAttribute("original-title");
|
||||||
|
|
||||||
|
// Process URL
|
||||||
|
if ( href.indexOf("@") > -1 && (-1 === href.indexOf("/") || href.indexOf("@") < href.indexOf("/")) )
|
||||||
|
href = "mailto:" + href;
|
||||||
|
else if ( ! href.match(/^https?:\/\//) )
|
||||||
|
href = "http://" + href;
|
||||||
|
|
||||||
|
// Set up the Link
|
||||||
|
this.href = href;
|
||||||
|
this.target = "_new";
|
||||||
|
this.textContent = link;
|
||||||
|
|
||||||
|
// Now, check for a tooltip.
|
||||||
|
var link_data = f._link_data[link];
|
||||||
|
if ( link_data && typeof link_data != "boolean" ) {
|
||||||
|
this.title = link_data.tooltip;
|
||||||
|
if ( link_data.unsafe )
|
||||||
|
this.classList.add('unsafe-link');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop from Navigating
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
|
@ -68,6 +68,6 @@ FFZ.prototype._load_dark_css = function() {
|
||||||
|
|
||||||
s.id = "ffz-dark-css";
|
s.id = "ffz-dark-css";
|
||||||
s.setAttribute('rel', 'stylesheet');
|
s.setAttribute('rel', 'stylesheet');
|
||||||
s.setAttribute('href', constants.SERVER + "script/dark.css?_=" + Date.now());
|
s.setAttribute('href', constants.SERVER + "script/dark.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||||
document.head.appendChild(s);
|
document.head.appendChild(s);
|
||||||
}
|
}
|
404
src/ui/following.js
Normal file
404
src/ui/following.js
Normal file
|
@ -0,0 +1,404 @@
|
||||||
|
var FFZ = window.FrankerFaceZ,
|
||||||
|
utils = require('../utils'),
|
||||||
|
|
||||||
|
EMOTE_CHANNELS = {
|
||||||
|
sirstendec: true,
|
||||||
|
europeanspeedsterassembly: true,
|
||||||
|
esamarathon2: true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Initialization
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.prototype.setup_following = function() {
|
||||||
|
this.log("Initializing following support.");
|
||||||
|
this.follow_data = {};
|
||||||
|
this.follow_sets = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Settings
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.settings_info.follow_buttons = {
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
|
||||||
|
category: "Channel Metadata",
|
||||||
|
name: "Relevant Follow Buttons",
|
||||||
|
help: 'Display additional Follow buttons for channels relevant to the stream, such as people participating in co-operative gameplay.',
|
||||||
|
on_update: function(val) {
|
||||||
|
this.rebuild_following_ui();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Command
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.ffz_commands.following = function(room, args) {
|
||||||
|
args = args.join(" ").trim().split(/\s*,+\s*/);
|
||||||
|
|
||||||
|
if ( args.length && args[0] === '' )
|
||||||
|
args.shift();
|
||||||
|
|
||||||
|
if ( args.length && args[args.length-1] === '' )
|
||||||
|
args.pop();
|
||||||
|
|
||||||
|
var user = this.get_user(), f = this;
|
||||||
|
if ( ! user || (user.login !== room.id && user.login !== "sirstendec" && user.login !== "dansalvato") )
|
||||||
|
return "You must be logged in as the broadcaster to use this command.";
|
||||||
|
|
||||||
|
if ( ! this.ws_send("update_follow_buttons", [room.id, args], function(success, data) {
|
||||||
|
if ( ! success ) {
|
||||||
|
f.room_message(room, "There was an error updating the following buttons.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data )
|
||||||
|
f.room_message(room, "The following buttons have been updated.");
|
||||||
|
else
|
||||||
|
f.room_message(room, "The following buttons have been disabled.");
|
||||||
|
}) )
|
||||||
|
return "There was an error communicating with the server.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Socket Handler
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.ws_on_close.push(function() {
|
||||||
|
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||||
|
current_id = controller && controller.get('id'),
|
||||||
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
|
need_update = false;
|
||||||
|
|
||||||
|
this.follow_sets = {};
|
||||||
|
|
||||||
|
if ( ! controller )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(var channel_id in this.follow_data) {
|
||||||
|
delete this.follow_data[channel_id];
|
||||||
|
if ( channel_id === current_id || channel_id === current_host )
|
||||||
|
need_update = true;
|
||||||
|
|
||||||
|
if ( this.rooms && this.rooms[channel_id] && this.rooms[channel_id].extra_sets ) {
|
||||||
|
var sets = this.rooms[channel_id].extra_sets;
|
||||||
|
delete this.rooms[channel_id].extra_sets;
|
||||||
|
|
||||||
|
for(var i=0; i < sets.length; i++) {
|
||||||
|
var set = this.emote_sets[sets[i]];
|
||||||
|
if ( set ) {
|
||||||
|
set.users.removeObject(channel_id);
|
||||||
|
if ( ! this.global_sets.contains(sets[i]) && ! set.users.length )
|
||||||
|
this.unload_set(sets[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( need_update )
|
||||||
|
this.rebuild_following_ui();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.ws_commands.follow_buttons = function(data) {
|
||||||
|
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||||
|
current_id = controller && controller.get('id'),
|
||||||
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
|
need_update = false;
|
||||||
|
|
||||||
|
this.follow_data = this.follow_data || {};
|
||||||
|
|
||||||
|
for(var channel_id in data) {
|
||||||
|
this.follow_data[channel_id] = data[channel_id];
|
||||||
|
if ( channel_id === current_id || channel_id === current_host )
|
||||||
|
need_update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( need_update )
|
||||||
|
this.rebuild_following_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.ws_commands.follow_sets = function(data) {
|
||||||
|
var controller = App.__container__.lookup('controller:channel'),
|
||||||
|
current_id = controller && controller.get('id'),
|
||||||
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
|
need_update = false;
|
||||||
|
|
||||||
|
this.follow_sets = this.follow_sets || {};
|
||||||
|
|
||||||
|
for(var room_id in data) {
|
||||||
|
if ( ! this.rooms || ! this.rooms.hasOwnProperty(room_id) ) {
|
||||||
|
this.follow_sets[room_id] = data[room_id];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var old_sets = this.rooms[room_id].extra_sets || [],
|
||||||
|
new_sets = this.rooms[room_id].extra_sets = data[room_id];
|
||||||
|
|
||||||
|
// Unload sets we aren't using anymore.
|
||||||
|
for(var i=0; i < old_sets.length; i++) {
|
||||||
|
var sid = old_sets[i];
|
||||||
|
if ( new_sets.indexOf(sid) !== -1 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var set = this.emote_sets && this.emote_sets[sid];
|
||||||
|
if ( set ) {
|
||||||
|
set.users.removeObject(room_id);
|
||||||
|
if ( ! this.global_sets.contains(sid) && ! set.users.length )
|
||||||
|
this.unload_set(sid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And load the new sets.
|
||||||
|
for(var i=0; i < new_sets.length; i++) {
|
||||||
|
var sid = new_sets[i],
|
||||||
|
set = this.emote_sets && this.emote_sets[sid];
|
||||||
|
|
||||||
|
if ( set ) {
|
||||||
|
if ( set.users.indexOf(room_id) === -1 )
|
||||||
|
set.users.push(room_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.load_set(sid, function(success, data) {
|
||||||
|
if ( success )
|
||||||
|
data.users.push(room_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Following UI
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.prototype.rebuild_following_ui = function() {
|
||||||
|
var controller = App.__container__.lookup('controller:channel'),
|
||||||
|
channel_id = controller && controller.get('id'),
|
||||||
|
hosted_id = controller && controller.get('hostModeTarget.id');
|
||||||
|
|
||||||
|
if ( ! this._cindex )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( channel_id ) {
|
||||||
|
var data = this.follow_data && this.follow_data[channel_id],
|
||||||
|
|
||||||
|
el = this._cindex.get('element'),
|
||||||
|
container = el && el.querySelector('.stats-and-actions .channel-actions'),
|
||||||
|
cont = container && container.querySelector('#ffz-ui-following');
|
||||||
|
|
||||||
|
if ( ! container || ! this.settings.follow_buttons || ! data || ! data.length ) {
|
||||||
|
if ( cont )
|
||||||
|
cont.parentElement.removeChild(cont);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ( ! cont ) {
|
||||||
|
cont = document.createElement('span');
|
||||||
|
cont.id = 'ffz-ui-following';
|
||||||
|
|
||||||
|
var before = container.querySelector(':scope > span');
|
||||||
|
if ( before )
|
||||||
|
container.insertBefore(cont, before);
|
||||||
|
else
|
||||||
|
container.appendChild(cont);
|
||||||
|
} else
|
||||||
|
cont.innerHTML = '';
|
||||||
|
|
||||||
|
for(var i=0; i < data.length; i++) {
|
||||||
|
this._build_following_button(cont, data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( hosted_id ) {
|
||||||
|
var data = this.follow_data && this.follow_data[hosted_id],
|
||||||
|
|
||||||
|
el = this._cindex.get('element'),
|
||||||
|
container = el && el.querySelector('#hostmode .channel-actions'),
|
||||||
|
cont = container && container.querySelector('#ffz-ui-following');
|
||||||
|
|
||||||
|
if ( ! container || ! this.settings.follow_buttons || ! data || ! data.length ) {
|
||||||
|
if ( cont )
|
||||||
|
cont.parentElement.removeChild(cont);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ( ! cont ) {
|
||||||
|
cont = document.createElement('span');
|
||||||
|
cont.id = 'ffz-ui-following';
|
||||||
|
|
||||||
|
var before = container.querySelector(':scope > span');
|
||||||
|
if ( before )
|
||||||
|
container.insertBefore(cont, before);
|
||||||
|
else
|
||||||
|
container.appendChild(cont);
|
||||||
|
} else
|
||||||
|
cont.innerHTML = '';
|
||||||
|
|
||||||
|
for(var i=0; i < data.length; i++) {
|
||||||
|
this._build_following_button(cont, data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// UI Construction
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.prototype._build_following_button = function(container, channel_id) {
|
||||||
|
var btn = document.createElement('a'), f = this,
|
||||||
|
btn_c = document.createElement('div'),
|
||||||
|
noti = document.createElement('a'),
|
||||||
|
noti_c = document.createElement('div'),
|
||||||
|
|
||||||
|
display_name,
|
||||||
|
following = false,
|
||||||
|
notifications = false,
|
||||||
|
|
||||||
|
update = function() {
|
||||||
|
btn_c.classList.toggle('is-following', following);
|
||||||
|
btn.title = (following ? "Unf" : "F") + "ollow " + display_name;
|
||||||
|
btn.innerHTML = (following ? "" : "Follow ") + display_name;
|
||||||
|
noti_c.classList.toggle('hidden', !following);
|
||||||
|
},
|
||||||
|
|
||||||
|
check_following = function() {
|
||||||
|
var user = f.get_user();
|
||||||
|
if ( ! user || ! user.login ) {
|
||||||
|
following = false;
|
||||||
|
notification = false;
|
||||||
|
btn_c.classList.add('is-initialized');
|
||||||
|
return update();
|
||||||
|
}
|
||||||
|
|
||||||
|
Twitch.api.get("users/" + user.login + "/follows/channels/" + channel_id)
|
||||||
|
.done(function(data) {
|
||||||
|
following = true;
|
||||||
|
notifications = data.notifications;
|
||||||
|
btn_c.classList.add('is-initialized');
|
||||||
|
update();
|
||||||
|
}).fail(function(data) {
|
||||||
|
following = false;
|
||||||
|
notifications = false;
|
||||||
|
btn_c.classList.add('is-initialized');
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
do_follow = function(notice) {
|
||||||
|
if ( notice !== false )
|
||||||
|
notice = true;
|
||||||
|
|
||||||
|
var user = f.get_user();
|
||||||
|
if ( ! user || ! user.login )
|
||||||
|
return null;
|
||||||
|
|
||||||
|
notifications = notice;
|
||||||
|
return Twitch.api.put("users/:login/follows/channels/" + channel_id, {notifications: notifications})
|
||||||
|
.fail(check_following);
|
||||||
|
},
|
||||||
|
|
||||||
|
on_name = function(cap_name) {
|
||||||
|
display_name = cap_name || channel_id;
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
btn_c.className = 'ember-follow follow-button';
|
||||||
|
btn_c.appendChild(btn);
|
||||||
|
|
||||||
|
// The drop-down button!
|
||||||
|
noti.className = 'toggle-notification-menu js-toggle-notification-menu';
|
||||||
|
noti.href = '#';
|
||||||
|
|
||||||
|
noti_c.className = 'notification-controls v2 hidden';
|
||||||
|
noti_c.appendChild(noti);
|
||||||
|
|
||||||
|
// Event Listeners!
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
var user = f.get_user();
|
||||||
|
if ( ! user || ! user.login )
|
||||||
|
// Show the login dialog~!
|
||||||
|
return Ember.$.login({mpSourceAction: "follow-button", follow: channel_id});
|
||||||
|
|
||||||
|
// Immediate update for nice UI.
|
||||||
|
following = ! following;
|
||||||
|
update();
|
||||||
|
|
||||||
|
// Report it!
|
||||||
|
f.ws_send("track_follow", [channel_id, following]);
|
||||||
|
|
||||||
|
// Do it, and make sure it happened.
|
||||||
|
if ( following )
|
||||||
|
do_follow()
|
||||||
|
else
|
||||||
|
Twitch.api.del("users/:login/follows/channels/" + channel_id)
|
||||||
|
.done(check_following);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
noti.addEventListener('click', function() {
|
||||||
|
var sw = f._build_following_popup(noti_c, channel_id, notifications);
|
||||||
|
if ( sw )
|
||||||
|
sw.addEventListener('click', function() {
|
||||||
|
var notice = ! notifications;
|
||||||
|
sw.classList.toggle('active', notice);
|
||||||
|
do_follow(notice);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
display_name = FFZ.get_capitalization(channel_id, on_name);
|
||||||
|
update();
|
||||||
|
check_following();
|
||||||
|
|
||||||
|
container.appendChild(btn_c);
|
||||||
|
container.appendChild(noti_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype._build_following_popup = function(container, channel_id, notifications) {
|
||||||
|
var popup = this._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;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup = this._popup = document.createElement('div');
|
||||||
|
popup.id = 'ffz-following-popup';
|
||||||
|
popup.setAttribute('data-channel', channel_id);
|
||||||
|
|
||||||
|
popup.className = (pos >= 300 ? 'right' : 'left') + ' dropmenu notify-menu js-notify';
|
||||||
|
|
||||||
|
out = '<div class="header">You are following ' + FFZ.get_capitalization(channel_id) + '</div>';
|
||||||
|
out += '<p class="clearfix">';
|
||||||
|
out += '<a class="switch' + (notifications ? ' active' : '') + '"><span></span></a>';
|
||||||
|
out += '<span class="switch-label">Notify me when the broadcaster goes live</span>';
|
||||||
|
out += '</p>';
|
||||||
|
|
||||||
|
popup.innerHTML = out;
|
||||||
|
container.appendChild(popup);
|
||||||
|
return popup.querySelector('a.switch');
|
||||||
|
}
|
168
src/ui/menu.js
168
src/ui/menu.js
|
@ -28,6 +28,135 @@ FFZ.prototype.setup_menu = function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.classList.toggle("ffz-menu-replace", this.settings.replace_twitch_menu);
|
document.body.classList.toggle("ffz-menu-replace", this.settings.replace_twitch_menu);
|
||||||
|
|
||||||
|
// Add FFZ to the chat settings menu.
|
||||||
|
|
||||||
|
this.log("Hooking the Ember Chat Settings view.");
|
||||||
|
|
||||||
|
var Settings = App.__container__.resolve('view:settings');
|
||||||
|
|
||||||
|
if ( ! Settings )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Settings.reopen({
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.ffzInit();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("ChatSettings didInsertElement: " + err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willClearRender: function() {
|
||||||
|
try {
|
||||||
|
this.ffzTeardown();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("ChatSettings willClearRender: " + err);
|
||||||
|
}
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzInit: function() {
|
||||||
|
var view = this,
|
||||||
|
el = this.get('element'),
|
||||||
|
menu = el && el.querySelector('.dropmenu');
|
||||||
|
|
||||||
|
if ( ! menu )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var header = document.createElement('div'),
|
||||||
|
content = document.createElement('div'),
|
||||||
|
p, cb, a;
|
||||||
|
|
||||||
|
header.className = 'list-header';
|
||||||
|
header.innerHTML = 'FrankerFaceZ';
|
||||||
|
|
||||||
|
content.className = 'chat-menu-content';
|
||||||
|
|
||||||
|
// Dark Twitch
|
||||||
|
p = document.createElement('p');
|
||||||
|
p.className = 'no-bttv';
|
||||||
|
cb = document.createElement('input');
|
||||||
|
cb.type = "checkbox";
|
||||||
|
cb.className = "ember-checkbox ffz-setting-dark-twitch";
|
||||||
|
cb.checked = f.settings.dark_twitch;
|
||||||
|
p.appendChild(cb);
|
||||||
|
p.appendChild(document.createTextNode("Dark Twitch"));
|
||||||
|
content.appendChild(p);
|
||||||
|
|
||||||
|
cb.addEventListener("change", function(e) {
|
||||||
|
f.settings.set("dark_twitch", this.checked);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Minimal Chat
|
||||||
|
/*
|
||||||
|
p = document.createElement('p');
|
||||||
|
//p.className = 'no-bttv';
|
||||||
|
cb = document.createElement('input');
|
||||||
|
cb.type = "checkbox";
|
||||||
|
cb.className = "ember-checkbox ffz-setting-minimal-twitch";
|
||||||
|
cb.checked = f.settings.minimal_chat;
|
||||||
|
p.appendChild(cb);
|
||||||
|
p.appendChild(document.createTextNode("Minimalistic Chat"));
|
||||||
|
content.appendChild(p);
|
||||||
|
|
||||||
|
cb.addEventListener("change", function(e) {
|
||||||
|
f.settings.set("minimal_chat", this.checked);
|
||||||
|
if ( this.checked )
|
||||||
|
view.set('controller.model.hidden', true);
|
||||||
|
});*/
|
||||||
|
|
||||||
|
|
||||||
|
// More Settings
|
||||||
|
p = document.createElement('p');
|
||||||
|
a = document.createElement('a');
|
||||||
|
a.href = '#';
|
||||||
|
a.innerHTML = 'More Settings';
|
||||||
|
p.appendChild(a);
|
||||||
|
content.appendChild(p);
|
||||||
|
|
||||||
|
a.addEventListener('click', function(e) {
|
||||||
|
view.set('controller.model.hidden', true);
|
||||||
|
f._last_page = 'settings';
|
||||||
|
f.build_ui_popup(f._chatv);
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
menu.appendChild(header);
|
||||||
|
menu.appendChild(content);
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzTeardown: function() {
|
||||||
|
// Nothing~!
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// For some reason, this doesn't work unless we create an instance of the
|
||||||
|
// chat settings view and then destroy it immediately.
|
||||||
|
try {
|
||||||
|
Settings.create().destroy();
|
||||||
|
} catch(err) { }
|
||||||
|
|
||||||
|
// Modify all existing Chat Settings views.
|
||||||
|
for(var key in Ember.View.views) {
|
||||||
|
if ( ! Ember.View.views.hasOwnProperty(key) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var view = Ember.View.views[key];
|
||||||
|
if ( !(view instanceof Settings) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.log("Manually updating existing Chat Settings view.", view);
|
||||||
|
try {
|
||||||
|
view.ffzInit();
|
||||||
|
} catch(err) {
|
||||||
|
this.error("setup: ChatSettings ffzInit: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,6 +305,7 @@ FFZ.menu_pages.channel = {
|
||||||
has_product = true;
|
has_product = true;
|
||||||
var tickets = App.__container__.resolve('model:ticket').find('user', {channel: room_id}),
|
var tickets = App.__container__.resolve('model:ticket').find('user', {channel: room_id}),
|
||||||
is_subscribed = tickets ? tickets.get('content') : false,
|
is_subscribed = tickets ? tickets.get('content') : false,
|
||||||
|
is_loaded = tickets ? tickets.get('isLoaded') : false,
|
||||||
icon = room.room.get("badgeSet.subscriber.image"),
|
icon = room.room.get("badgeSet.subscriber.image"),
|
||||||
|
|
||||||
grid = document.createElement("div"),
|
grid = document.createElement("div"),
|
||||||
|
@ -185,6 +315,25 @@ FFZ.menu_pages.channel = {
|
||||||
// Weird is_subscribed check. Might be more accurate?
|
// Weird is_subscribed check. Might be more accurate?
|
||||||
is_subscribed = is_subscribed && is_subscribed.length > 0;
|
is_subscribed = is_subscribed && is_subscribed.length > 0;
|
||||||
|
|
||||||
|
// See if we've loaded. If we haven't loaded the ticket yet
|
||||||
|
// then try loading it, and then re-render the menu.
|
||||||
|
if ( tickets && ! is_subscribed && ! is_loaded ) {
|
||||||
|
var f = this;
|
||||||
|
tickets.addObserver('isLoaded', function() {
|
||||||
|
setTimeout(function(){
|
||||||
|
if ( inner.getAttribute('data-page') !== 'channel' )
|
||||||
|
return;
|
||||||
|
|
||||||
|
inner.innerHTML = '';
|
||||||
|
FFZ.menu_pages.channel.render.bind(f)(view, inner);
|
||||||
|
},0);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
tickets.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
grid.className = "emoticon-grid";
|
grid.className = "emoticon-grid";
|
||||||
header.className = "heading";
|
header.className = "heading";
|
||||||
if ( icon )
|
if ( icon )
|
||||||
|
@ -193,7 +342,7 @@ FFZ.menu_pages.channel = {
|
||||||
header.innerHTML = '<span class="right">Twitch</span>Subscriber Emoticons';
|
header.innerHTML = '<span class="right">Twitch</span>Subscriber Emoticons';
|
||||||
grid.appendChild(header);
|
grid.appendChild(header);
|
||||||
|
|
||||||
for(var emotes=product.get("emoticons"), i=0; i < emotes.length; i++) {
|
for(var emotes=product.get("emoticons") || [], i=0; i < emotes.length; i++) {
|
||||||
var emote = emotes[i];
|
var emote = emotes[i];
|
||||||
if ( emote.state !== "active" )
|
if ( emote.state !== "active" )
|
||||||
continue;
|
continue;
|
||||||
|
@ -222,7 +371,7 @@ FFZ.menu_pages.channel = {
|
||||||
if ( c > 0 )
|
if ( c > 0 )
|
||||||
inner.appendChild(grid);
|
inner.appendChild(grid);
|
||||||
|
|
||||||
if ( ! is_subscribed ) {
|
if ( c > 0 && ! is_subscribed ) {
|
||||||
var sub_message = document.createElement("div"),
|
var sub_message = document.createElement("div"),
|
||||||
nonsub_message = document.createElement("div"),
|
nonsub_message = document.createElement("div"),
|
||||||
unlock_text = document.createElement("span"),
|
unlock_text = document.createElement("span"),
|
||||||
|
@ -242,7 +391,7 @@ FFZ.menu_pages.channel = {
|
||||||
nonsub_message.appendChild(sub_link);
|
nonsub_message.appendChild(sub_link);
|
||||||
|
|
||||||
inner.appendChild(sub_message);
|
inner.appendChild(sub_message);
|
||||||
} else {
|
} else if ( c > 0 ) {
|
||||||
var last_content = tickets.get("content");
|
var last_content = tickets.get("content");
|
||||||
last_content = last_content.length > 0 ? last_content[last_content.length-1] : undefined;
|
last_content = last_content.length > 0 ? last_content[last_content.length-1] : undefined;
|
||||||
if ( last_content && last_content.purchase_profile && !last_content.purchase_profile.will_renew ) {
|
if ( last_content && last_content.purchase_profile && !last_content.purchase_profile.will_renew ) {
|
||||||
|
@ -265,8 +414,19 @@ FFZ.menu_pages.channel = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do we have extra sets?
|
||||||
|
var extra_sets = room && room.extra_sets || [];
|
||||||
|
|
||||||
// Basic Emote Sets
|
// Basic Emote Sets
|
||||||
this._emotes_for_sets(inner, view, room && room.set && [room.set] || [], (this.feature_friday || has_product) ? "Channel Emoticons" : null, "http://cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ");
|
this._emotes_for_sets(inner, view, room && room.set && [room.set] || [], (this.feature_friday || has_product || extra_sets.length ) ? "Channel Emoticons" : null, "http://cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ");
|
||||||
|
|
||||||
|
for(var i=0; i < extra_sets.length; i++) {
|
||||||
|
// Look up the set name.
|
||||||
|
var set = this.emote_sets[extra_sets[i]],
|
||||||
|
name = set ? "Featured " + set.title : "Featured Channel";
|
||||||
|
|
||||||
|
this._emotes_for_sets(inner, view, [extra_sets[i]], name, "http://cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ");
|
||||||
|
}
|
||||||
|
|
||||||
// Feature Friday!
|
// Feature Friday!
|
||||||
this._feature_friday_ui(room_id, inner, view);
|
this._feature_friday_ui(room_id, inner, view);
|
||||||
|
|
|
@ -55,11 +55,11 @@ FFZ.prototype.setup_my_emotes = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._twitch_set_to_channel[0] = "global";
|
this._twitch_set_to_channel[0] = "global";
|
||||||
this._twitch_set_to_channel[33] = "tfaces";
|
this._twitch_set_to_channel[33] = "turbo_faces";
|
||||||
this._twitch_set_to_channel[42] = "tfaces";
|
this._twitch_set_to_channel[42] = "turbo_faces";
|
||||||
|
|
||||||
this._twitch_badges["global"] = "//cdn.frankerfacez.com/script/twitch_logo.png";
|
this._twitch_badges["global"] = "//cdn.frankerfacez.com/script/twitch_logo.png";
|
||||||
this._twitch_badges["tfaces"] = this._twitch_badges["turbo"] = "//cdn.frankerfacez.com/script/turbo_badge.png";
|
this._twitch_badges["turbo_faces"] = this._twitch_badges["turbo"] = "//cdn.frankerfacez.com/script/turbo_badge.png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ FFZ.menu_pages.my_emotes = {
|
||||||
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, ts);
|
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, ts);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ws_send("twitch_sets", needed_sets, function(success, data) {
|
if ( ! this.ws_send("twitch_sets", needed_sets, function(success, data) {
|
||||||
if ( ! needed_sets.length )
|
if ( ! needed_sets.length )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -123,8 +123,9 @@ FFZ.menu_pages.my_emotes = {
|
||||||
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, twitch_sets);
|
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, twitch_sets);
|
||||||
} else
|
} else
|
||||||
fail();
|
fail();
|
||||||
});
|
}) )
|
||||||
|
fail()
|
||||||
|
else
|
||||||
setTimeout(fail, 2000);
|
setTimeout(fail, 2000);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ FFZ.menu_pages.my_emotes = {
|
||||||
|
|
||||||
if ( channel_id === "global" )
|
if ( channel_id === "global" )
|
||||||
title = "Global Emoticons";
|
title = "Global Emoticons";
|
||||||
else if ( channel_id === "turbo" )
|
else if ( channel_id === "turbo" || channel_id === "turbo_faces" )
|
||||||
title = "Twitch Turbo";
|
title = "Twitch Turbo";
|
||||||
else
|
else
|
||||||
title = FFZ.get_capitalization(channel_id, function(name) {
|
title = FFZ.get_capitalization(channel_id, function(name) {
|
||||||
|
@ -285,12 +286,12 @@ FFZ.menu_pages.my_emotes = {
|
||||||
// Finally, sort and add them all.
|
// Finally, sort and add them all.
|
||||||
sets.sort(function(a,b) {
|
sets.sort(function(a,b) {
|
||||||
var an = a[0], bn = b[0];
|
var an = a[0], bn = b[0];
|
||||||
if ( an === "turbo" || an === "tfaces" )
|
if ( an === "turbo" || an === "turbo_faces" )
|
||||||
an = "zza|" + an;
|
an = "zza|" + an;
|
||||||
else if ( an === "global" || an === "global emoticons" )
|
else if ( an === "global" || an === "global emoticons" )
|
||||||
an = "zzz|" + an;
|
an = "zzz|" + an;
|
||||||
|
|
||||||
if ( bn === "turbo" || bn === "tfaces" )
|
if ( bn === "turbo" || bn === "turbo_faces" )
|
||||||
bn = "zza|" + bn;
|
bn = "zza|" + bn;
|
||||||
else if ( bn === "global" || bn === "global emoticons" )
|
else if ( bn === "global" || bn === "global emoticons" )
|
||||||
bn = "zzz|" + bn;
|
bn = "zzz|" + bn;
|
||||||
|
|
|
@ -24,7 +24,7 @@ FFZ.settings_info.highlight_notifications = {
|
||||||
//visible: function() { return ! this.has_bttv },
|
//visible: function() { return ! this.has_bttv },
|
||||||
|
|
||||||
name: "Highlight Notifications",
|
name: "Highlight Notifications",
|
||||||
help: "Display notifications when a highlighted word appears in chat in an unfocused tab.",
|
help: "Display notifications when a highlighted word appears in chat in an unfocused tab. This is automatically disabled on the dashboard.",
|
||||||
|
|
||||||
on_update: function(val, direct) {
|
on_update: function(val, direct) {
|
||||||
// Check to see if we have notification permission. If this is
|
// Check to see if we have notification permission. If this is
|
||||||
|
|
104
src/ui/races.js
104
src/ui/races.js
|
@ -36,6 +36,7 @@ FFZ.settings_info.srl_races = {
|
||||||
FFZ.ws_on_close.push(function() {
|
FFZ.ws_on_close.push(function() {
|
||||||
var controller = window.App && App.__container__.lookup('controller:channel'),
|
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('id'),
|
current_id = controller && controller.get('id'),
|
||||||
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
if ( ! controller )
|
if ( ! controller )
|
||||||
|
@ -43,7 +44,7 @@ FFZ.ws_on_close.push(function() {
|
||||||
|
|
||||||
for(var chan in this.srl_races) {
|
for(var chan in this.srl_races) {
|
||||||
delete this.srl_races[chan];
|
delete this.srl_races[chan];
|
||||||
if ( chan == current_id )
|
if ( chan === current_id || chan === current_host )
|
||||||
need_update = true;
|
need_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,15 +52,19 @@ FFZ.ws_on_close.push(function() {
|
||||||
this.rebuild_race_ui();
|
this.rebuild_race_ui();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
FFZ.ws_commands.srl_race = function(data) {
|
FFZ.ws_commands.srl_race = function(data) {
|
||||||
var controller = App.__container__.lookup('controller:channel'),
|
var controller = App.__container__.lookup('controller:channel'),
|
||||||
current_id = controller.get('id'),
|
current_id = controller && controller.get('id'),
|
||||||
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false;
|
||||||
|
|
||||||
|
this.srl_races = this.srl_races || {};
|
||||||
|
|
||||||
for(var i=0; i < data[0].length; i++) {
|
for(var i=0; i < data[0].length; i++) {
|
||||||
var channel_id = data[0][i];
|
var channel_id = data[0][i];
|
||||||
this.srl_races[channel_id] = data[1];
|
this.srl_races[channel_id] = data[1];
|
||||||
if ( channel_id == current_id )
|
if ( channel_id === current_id || channel_id === current_host )
|
||||||
need_update = true;
|
need_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,41 +91,75 @@ FFZ.ws_commands.srl_race = function(data) {
|
||||||
|
|
||||||
FFZ.prototype.rebuild_race_ui = function() {
|
FFZ.prototype.rebuild_race_ui = function() {
|
||||||
var controller = App.__container__.lookup('controller:channel'),
|
var controller = App.__container__.lookup('controller:channel'),
|
||||||
channel_id = controller.get('id'),
|
channel_id = controller && controller.get('id'),
|
||||||
race = this.srl_races[channel_id],
|
hosted_id = controller && controller.get('hostModeTarget.id');
|
||||||
enable_ui = this.settings.srl_races,
|
|
||||||
|
|
||||||
actions = document.querySelector('.stats-and-actions .channel-actions'),
|
if ( ! this._cindex )
|
||||||
race_container = actions.querySelector('#ffz-ui-race');
|
return;
|
||||||
|
|
||||||
if ( ! race || ! enable_ui ) {
|
if ( channel_id ) {
|
||||||
|
var race = this.srl_races && this.srl_races[channel_id],
|
||||||
|
|
||||||
|
el = this._cindex.get('element'),
|
||||||
|
container = el && el.querySelector('.stats-and-actions .channel-actions'),
|
||||||
|
race_container = container && container.querySelector('#ffz-ui-race');
|
||||||
|
|
||||||
|
if ( ! container || ! this.settings.srl_races || ! race ) {
|
||||||
if ( race_container )
|
if ( race_container )
|
||||||
race_container.parentElement.removeChild(race_container);
|
race_container.parentElement.removeChild(race_container);
|
||||||
if ( this._popup && this._popup.id == "ffz-race-popup" ) {
|
|
||||||
delete this._popup;
|
|
||||||
this._popup_kill && this._popup_kill();
|
|
||||||
delete this._popup_kill;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( race_container )
|
|
||||||
return this._update_race(true);
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ( ! race_container ) {
|
||||||
race_container = document.createElement('span');
|
race_container = document.createElement('span');
|
||||||
race_container.setAttribute('data-channel', channel_id);
|
|
||||||
race_container.id = 'ffz-ui-race';
|
race_container.id = 'ffz-ui-race';
|
||||||
|
race_container.setAttribute('data-channel', channel_id);
|
||||||
|
|
||||||
var btn = document.createElement('span');
|
var btn = document.createElement('span');
|
||||||
btn.className = 'button drop action';
|
btn.className = 'button drop action';
|
||||||
btn.title = "SpeedRunsLive Race";
|
btn.title = "SpeedRunsLive Race";
|
||||||
btn.innerHTML = '<span class="logo"><span>';
|
btn.innerHTML = '<span class="logo"></span>';
|
||||||
|
|
||||||
btn.addEventListener('click', this.build_race_popup.bind(this));
|
btn.addEventListener('click', this._build_race_popup.bind(this, race_container, channel_id));
|
||||||
|
|
||||||
race_container.appendChild(btn);
|
race_container.appendChild(btn);
|
||||||
actions.appendChild(race_container);
|
container.appendChild(race_container);
|
||||||
this._update_race(true);
|
}
|
||||||
|
|
||||||
|
this._update_race(race_container, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( hosted_id ) {
|
||||||
|
var race = this.srl_races && this.srl_races[hosted_id],
|
||||||
|
|
||||||
|
el = this._cindex.get('element'),
|
||||||
|
container = el && el.querySelector('#hostmode .channel-actions'),
|
||||||
|
race_container = container && container.querySelector('#ffz-ui-race');
|
||||||
|
|
||||||
|
if ( ! container || ! this.settings.srl_races || ! race ) {
|
||||||
|
if ( race_container )
|
||||||
|
race_container.parentElement.removeChild(race_container);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ( ! race_container ) {
|
||||||
|
race_container = document.createElement('span');
|
||||||
|
race_container.id = 'ffz-ui-race';
|
||||||
|
race_container.setAttribute('data-channel', hosted_id);
|
||||||
|
|
||||||
|
var btn = document.createElement('span');
|
||||||
|
btn.className = 'button drop action';
|
||||||
|
btn.title = "SpeedRunsLive Race";
|
||||||
|
btn.innerHTML = '<span class="logo"></span>';
|
||||||
|
|
||||||
|
btn.addEventListener('click', this._build_race_popup.bind(this, race_container, hosted_id));
|
||||||
|
|
||||||
|
race_container.appendChild(btn);
|
||||||
|
container.appendChild(race_container);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._update_race(race_container, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,7 +178,7 @@ FFZ.prototype._race_kill = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.build_race_popup = function() {
|
FFZ.prototype._build_race_popup = function(container, channel_id) {
|
||||||
var popup = this._popup;
|
var popup = this._popup;
|
||||||
if ( popup ) {
|
if ( popup ) {
|
||||||
popup.parentElement.removeChild(popup);
|
popup.parentElement.removeChild(popup);
|
||||||
|
@ -147,22 +186,20 @@ FFZ.prototype.build_race_popup = function() {
|
||||||
this._popup_kill && this._popup_kill();
|
this._popup_kill && this._popup_kill();
|
||||||
delete this._popup_kill;
|
delete this._popup_kill;
|
||||||
|
|
||||||
if ( popup.id == "ffz-race-popup" )
|
if ( popup.id === "ffz-race-popup" && popup.getAttribute('data-channel') === channel_id )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var container = document.querySelector('#ffz-ui-race');
|
|
||||||
if ( ! container )
|
if ( ! container )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var el = container.querySelector('.button'),
|
var el = container.querySelector('.button'),
|
||||||
pos = el.offsetLeft + el.offsetWidth,
|
pos = el.offsetLeft + el.offsetWidth,
|
||||||
|
|
||||||
channel_id = container.getAttribute('data-channel'),
|
|
||||||
race = this.srl_races[channel_id];
|
race = this.srl_races[channel_id];
|
||||||
|
|
||||||
var popup = document.createElement('div'), out = '';
|
var popup = document.createElement('div'), out = '';
|
||||||
popup.id = 'ffz-race-popup';
|
popup.id = 'ffz-race-popup';
|
||||||
|
popup.setAttribute('data-channel', channel_id);
|
||||||
popup.className = (pos >= 300 ? 'right' : 'left') + ' share dropmenu';
|
popup.className = (pos >= 300 ? 'right' : 'left') + ' share dropmenu';
|
||||||
|
|
||||||
this._popup_kill = this._race_kill.bind(this);
|
this._popup_kill = this._race_kill.bind(this);
|
||||||
|
@ -199,17 +236,16 @@ FFZ.prototype.build_race_popup = function() {
|
||||||
popup.innerHTML = out;
|
popup.innerHTML = out;
|
||||||
container.appendChild(popup);
|
container.appendChild(popup);
|
||||||
|
|
||||||
this._update_race(true);
|
this._update_race(container, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype._update_race = function(not_timer) {
|
FFZ.prototype._update_race = function(container, not_timer) {
|
||||||
if ( this._race_timer && not_timer ) {
|
if ( this._race_timer && not_timer ) {
|
||||||
clearTimeout(this._race_timer);
|
clearTimeout(this._race_timer);
|
||||||
delete this._race_timer;
|
delete this._race_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
var container = document.querySelector('#ffz-ui-race');
|
|
||||||
if ( ! container )
|
if ( ! container )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -219,11 +255,13 @@ FFZ.prototype._update_race = function(not_timer) {
|
||||||
if ( ! race ) {
|
if ( ! race ) {
|
||||||
// No race. Abort.
|
// No race. Abort.
|
||||||
container.parentElement.removeChild(container);
|
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();
|
this._popup_kill && this._popup_kill();
|
||||||
if ( this._popup ) {
|
if ( this._popup ) {
|
||||||
delete this._popup;
|
delete this._popup;
|
||||||
delete this._popup_kill;
|
delete this._popup_kill;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +339,7 @@ FFZ.prototype._update_race = function(not_timer) {
|
||||||
timer.innerHTML = "Done";
|
timer.innerHTML = "Done";
|
||||||
else {
|
else {
|
||||||
timer.innerHTML = utils.time_to_string(elapsed);
|
timer.innerHTML = utils.time_to_string(elapsed);
|
||||||
this._race_timer = setTimeout(this._update_race.bind(this), 1000);
|
this._race_timer = setTimeout(this._update_race.bind(this, container), 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ FFZ.prototype.setup_css = function() {
|
||||||
|
|
||||||
s.id = "ffz-ui-css";
|
s.id = "ffz-ui-css";
|
||||||
s.setAttribute('rel', 'stylesheet');
|
s.setAttribute('rel', 'stylesheet');
|
||||||
s.setAttribute('href', constants.SERVER + "script/style.css?_=" + Date.now());
|
s.setAttribute('href', constants.SERVER + "script/style.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
|
||||||
document.head.appendChild(s);
|
document.head.appendChild(s);
|
||||||
|
|
||||||
jQuery.noty.themes.ffzTheme = {
|
jQuery.noty.themes.ffzTheme = {
|
||||||
|
|
|
@ -13,6 +13,9 @@ FFZ.prototype._update_subscribers = function() {
|
||||||
delete this._update_subscribers_timer;
|
delete this._update_subscribers_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schedule an update.
|
||||||
|
this._update_subscribers_timer = setTimeout(this._update_subscribers.bind(this), 60000);
|
||||||
|
|
||||||
var user = this.get_user(), f = this,
|
var user = this.get_user(), f = this,
|
||||||
match = this.is_dashboard ? location.pathname.match(/\/([^\/]+)/) : undefined,
|
match = this.is_dashboard ? location.pathname.match(/\/([^\/]+)/) : undefined,
|
||||||
id = this.is_dashboard && match && match[1];
|
id = this.is_dashboard && match && match[1];
|
||||||
|
@ -24,9 +27,6 @@ FFZ.prototype._update_subscribers = function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule an update.
|
|
||||||
this._update_subscribers_timer = setTimeout(this._update_subscribers.bind(this), 60000);
|
|
||||||
|
|
||||||
// Spend a moment wishing we could just hit the subscribers API from the
|
// Spend a moment wishing we could just hit the subscribers API from the
|
||||||
// context of the web user.
|
// context of the web user.
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ FFZ.prototype._update_subscribers = function() {
|
||||||
jQuery(stat).tipsy(f.is_dashboard ? {"gravity":"s"} : undefined);
|
jQuery(stat).tipsy(f.is_dashboard ? {"gravity":"s"} : undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
el.innerHTML = utils.number_commas(parseInt(sub_count));
|
el.innerHTML = sub_count;
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
f.error("_update_subscribers: " + err);
|
f.error("_update_subscribers: " + err);
|
||||||
|
|
|
@ -7,7 +7,7 @@ var FFZ = window.FrankerFaceZ,
|
||||||
// FFZ Viewers
|
// FFZ Viewers
|
||||||
// ------------
|
// ------------
|
||||||
|
|
||||||
FFZ.ws_commands.viewers = function(data) {
|
FFZ.ws_commands.chatters = function(data) {
|
||||||
var channel = data[0], count = data[1];
|
var channel = data[0], count = data[1];
|
||||||
|
|
||||||
var controller = window.App && App.__container__.lookup('controller:channel'),
|
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||||
|
@ -24,12 +24,33 @@ FFZ.ws_commands.viewers = function(data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._dash_chatters = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFZ.ws_commands.viewers = function(data) {
|
||||||
|
var channel = data[0], count = data[1];
|
||||||
|
|
||||||
|
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||||
|
match = this.is_dashboard ? location.pathname.match(/\/([^\/]+)/) : undefined,
|
||||||
|
id = this.is_dashboard ? match && match[1] : controller && controller.get && controller.get('id');
|
||||||
|
|
||||||
|
if ( ! this.is_dashboard ) {
|
||||||
|
var room = this.rooms && this.rooms[channel];
|
||||||
|
if ( room ) {
|
||||||
|
room.ffz_viewers = count;
|
||||||
|
if ( this._cindex )
|
||||||
|
this._cindex.ffzUpdateChatters();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._dash_viewers = count;
|
||||||
|
|
||||||
if ( ! this.settings.chatter_count || id !== channel )
|
if ( ! this.settings.chatter_count || id !== channel )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var view_count = document.querySelector('#ffz-ffzchatter-display'),
|
var view_count = document.querySelector('#ffz-ffzchatter-display'),
|
||||||
content = constants.ZREKNARF + ' ' + utils.number_commas(count);
|
content = constants.ZREKNARF + ' ' + utils.number_commas(count) + (typeof this._dash_chatters === "number" ? ' (' + utils.number_commas(this._dash_chatters) + ')' : "");
|
||||||
|
|
||||||
if ( view_count )
|
if ( view_count )
|
||||||
view_count.innerHTML = content;
|
view_count.innerHTML = content;
|
||||||
|
@ -41,7 +62,7 @@ FFZ.ws_commands.viewers = function(data) {
|
||||||
view_count = document.createElement('span');
|
view_count = document.createElement('span');
|
||||||
view_count.id = "ffz-ffzchatter-display";
|
view_count.id = "ffz-ffzchatter-display";
|
||||||
view_count.className = 'ffz stat';
|
view_count.className = 'ffz stat';
|
||||||
view_count.title = 'Chatters with FrankerFaceZ';
|
view_count.title = 'Viewers (In Chat) with FrankerFaceZ';
|
||||||
view_count.innerHTML = content;
|
view_count.innerHTML = content;
|
||||||
|
|
||||||
parent.appendChild(view_count);
|
parent.appendChild(view_count);
|
||||||
|
|
132
src/utils.js
132
src/utils.js
|
@ -69,6 +69,128 @@ var sanitize_cache = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Date(unix);
|
return new Date(unix);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// IRC Messages
|
||||||
|
splitIRCMessage = function(msgString) {
|
||||||
|
msgString = $.trim(msgString);
|
||||||
|
var split = {raw: msgString};
|
||||||
|
|
||||||
|
var tagsEnd = -1;
|
||||||
|
if ( msgString.charAt(0) === '@' ) {
|
||||||
|
tagsEnd = msgString.indexOf(' ');
|
||||||
|
split.tags = msgString.substr(1, tagsEnd - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefixStart = tagsEnd + 1,
|
||||||
|
prefixEnd = -1;
|
||||||
|
|
||||||
|
if ( msgString.charAt(prefixStart) === ':' ) {
|
||||||
|
prefixEnd = msgString.indexOf(' ', prefixStart);
|
||||||
|
split.prefix = msgString.substr(prefixStart + 1, prefixEnd - (prefixStart + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
var trailingStart = msgString.indexOf(' :', prefixStart);
|
||||||
|
if ( trailingStart >= 0 ) {
|
||||||
|
split.trailing = msgString.substr(trailingStart + 2);
|
||||||
|
} else {
|
||||||
|
trailingStart = msgString.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandAndParams = msgString.substr(prefixEnd + 1, trailingStart - prefixEnd - 1).split(' ');
|
||||||
|
split.command = commandAndParams[0];
|
||||||
|
if ( commandAndParams.length > 1 )
|
||||||
|
split.params = commandAndParams.slice(1);
|
||||||
|
|
||||||
|
return split;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
ESCAPE_CHARS = {
|
||||||
|
':': ';',
|
||||||
|
s: ' ',
|
||||||
|
r: '\r',
|
||||||
|
n: '\n',
|
||||||
|
'\\': '\\'
|
||||||
|
},
|
||||||
|
|
||||||
|
unescapeTag = function(tag) {
|
||||||
|
var result = '';
|
||||||
|
for(var i=0; i < tag.length; i++) {
|
||||||
|
var c = tag.charAt(i);
|
||||||
|
if ( c === '\\' ) {
|
||||||
|
if ( i === tag.length - 1 )
|
||||||
|
throw 'Improperly escaped tag';
|
||||||
|
|
||||||
|
c = ESCAPE_CHARS[tag.charAt(i+1)];
|
||||||
|
if ( c === undefined )
|
||||||
|
throw 'Improperly escaped tag';
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
result += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
parseTag = function(tag, value) {
|
||||||
|
switch(tag) {
|
||||||
|
case 'slow':
|
||||||
|
try {
|
||||||
|
return parseInt(value);
|
||||||
|
} catch(err) { return 0; }
|
||||||
|
case 'subs-only':
|
||||||
|
case 'r9k':
|
||||||
|
case 'subscriber':
|
||||||
|
case 'turbo':
|
||||||
|
return value === '1';
|
||||||
|
default:
|
||||||
|
try {
|
||||||
|
return unescapeTag(value);
|
||||||
|
} catch(err) { return ''; }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parseIRCTags = function(tagsString) {
|
||||||
|
var tags = {},
|
||||||
|
keyValues = tagsString.split(';');
|
||||||
|
|
||||||
|
for(var i=0; i < keyValues.length; ++i) {
|
||||||
|
var kv = keyValues[i].split('=');
|
||||||
|
if ( kv.length === 2 )
|
||||||
|
tags[kv[0]] = parseTag(kv[0], kv[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
EMOJI_CODEPOINTS = {},
|
||||||
|
emoji_to_codepoint = function(icon, variant) {
|
||||||
|
if ( EMOJI_CODEPOINTS[icon] && EMOJI_CODEPOINTS[icon][variant] )
|
||||||
|
return EMOJI_CODEPOINTS[icon][variant];
|
||||||
|
|
||||||
|
var ico = variant === '\uFE0F' ? icon.slice(0, -1) : (icon.length === 3 && icon.charAt(1) === '\uFE0F' ? icon.charAt(0) + icon.charAt(2) : icon),
|
||||||
|
r = [], c = 0, p = 0, i = 0;
|
||||||
|
|
||||||
|
while ( i < ico.length ) {
|
||||||
|
c = ico.charCodeAt(i++);
|
||||||
|
if ( p ) {
|
||||||
|
r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
|
||||||
|
p = 0;
|
||||||
|
} else if ( 0xD800 <= c && c <= 0xDBFF) {
|
||||||
|
p = c;
|
||||||
|
} else {
|
||||||
|
r.push(c.toString(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var es = EMOJI_CODEPOINTS[icon] = EMOJI_CODEPOINTS[icon] || {},
|
||||||
|
out = es[variant] = r.join("-");
|
||||||
|
|
||||||
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,6 +215,12 @@ module.exports = {
|
||||||
element.innerHTML = all;
|
element.innerHTML = all;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
splitIRCMessage: splitIRCMessage,
|
||||||
|
parseIRCTags: parseIRCTags,
|
||||||
|
|
||||||
|
emoji_to_codepoint: emoji_to_codepoint,
|
||||||
|
|
||||||
get_luminance: get_luminance,
|
get_luminance: get_luminance,
|
||||||
brighten: brighten,
|
brighten: brighten,
|
||||||
darken: darken,
|
darken: darken,
|
||||||
|
@ -157,7 +285,7 @@ module.exports = {
|
||||||
return 'less than a second';
|
return 'less than a second';
|
||||||
},
|
},
|
||||||
|
|
||||||
time_to_string: function(elapsed, separate_days, days_only) {
|
time_to_string: function(elapsed, separate_days, days_only, no_hours) {
|
||||||
var seconds = elapsed % 60,
|
var seconds = elapsed % 60,
|
||||||
minutes = Math.floor(elapsed / 60),
|
minutes = Math.floor(elapsed / 60),
|
||||||
hours = Math.floor(minutes / 60),
|
hours = Math.floor(minutes / 60),
|
||||||
|
@ -174,6 +302,6 @@ module.exports = {
|
||||||
days = ( days > 0 ) ? days + " days, " : "";
|
days = ( days > 0 ) ? days + " days, " : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return days + (hours < 10 ? "0" : "") + hours + ":" + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
|
return days + ((!no_hours || days || hours) ? ((hours < 10 ? "0" : "") + hours + ':') : '') + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
236
style.css
236
style.css
|
@ -13,14 +13,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-hide-view-count .stat.twitch-channel-views,
|
.ffz-hide-view-count .stat.twitch-channel-views,
|
||||||
|
.ffz-minimal-chat .emoticon-selector-toggle,
|
||||||
.ffz-menu-replace .emoticon-selector-toggle {
|
.ffz-menu-replace .emoticon-selector-toggle {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle svg { height: 14px; width: 18px; }
|
body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle svg,
|
||||||
|
body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle svg
|
||||||
|
{
|
||||||
|
height: 14px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle {
|
body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle,
|
||||||
height: 14px; width: 18px;
|
body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle {
|
||||||
|
height: 14px;
|
||||||
|
width: 18px;
|
||||||
top: 28px;
|
top: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,15 +165,13 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
#dash_main #stats .stat.dark#ffz_count svg path { fill: #cacaca; }
|
#dash_main #stats .stat.dark#ffz_count svg path { fill: #cacaca; }
|
||||||
|
|
||||||
.ffz-agdq .follow-button span.follow {
|
#ffz-ui-following .follow-button a {
|
||||||
padding: 0 10px !important;
|
padding: 0 10px;
|
||||||
width: inherit !important;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-agdq span.notify { cursor: pointer; }
|
#ffz-following-popup {
|
||||||
|
background-image: url('//cdn.frankerfacez.com/script/zreknarf-bg.png');
|
||||||
#ffz-agdq-popup {
|
|
||||||
background-image: url('//cdn.frankerfacez.com/channel/602race/zreknarf.png');
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 115% -75%;
|
background-position: 115% -75%;
|
||||||
background-size: 50%;
|
background-size: 50%;
|
||||||
|
@ -255,6 +261,12 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
/* SRL Race Support */
|
/* SRL Race Support */
|
||||||
|
|
||||||
|
#ffz-following-popup.right {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ffz-ui-following .notification-controls,
|
||||||
#ffz-ui-race {
|
#ffz-ui-race {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +285,7 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
#ffz-race-popup {
|
#ffz-race-popup {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 5px;
|
bottom: 0;
|
||||||
background-image: url("//cdn.frankerfacez.com/script/zreknarf-bg.png");
|
background-image: url("//cdn.frankerfacez.com/script/zreknarf-bg.png");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 115% 110%;
|
background-position: 115% 110%;
|
||||||
|
@ -377,12 +389,6 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
margin: 11px 13px;
|
margin: 11px 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 369px) {
|
|
||||||
.ember-chat-container .ember-chat .chat-interface .emoticon-selector {
|
|
||||||
right: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ffz-ui-menu-page { overflow-y: auto; }
|
.ffz-ui-menu-page { overflow-y: auto; }
|
||||||
|
|
||||||
.ffz-ui-menu-page[data-page="about"],
|
.ffz-ui-menu-page[data-page="about"],
|
||||||
|
@ -552,12 +558,14 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
/* Menu Scrollbar */
|
/* Menu Scrollbar */
|
||||||
|
|
||||||
|
.chat-history::-webkit-scrollbar,
|
||||||
#ffz-race-popup .table::-webkit-scrollbar,
|
#ffz-race-popup .table::-webkit-scrollbar,
|
||||||
.emoticon-selector-box .all-emotes::-webkit-scrollbar,
|
.emoticon-selector-box .all-emotes::-webkit-scrollbar,
|
||||||
.ffz-ui-menu-page::-webkit-scrollbar {
|
.ffz-ui-menu-page::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-history::-webkit-scrollbar-thumb,
|
||||||
#ffz-race-popup .table::-webkit-scrollbar-thumb,
|
#ffz-race-popup .table::-webkit-scrollbar-thumb,
|
||||||
.emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
.emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||||
.ffz-ui-menu-page::-webkit-scrollbar-thumb {
|
.ffz-ui-menu-page::-webkit-scrollbar-thumb {
|
||||||
|
@ -566,6 +574,7 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
box-shadow: 0 0 1px 1px rgba(255,255,255,0.25);
|
box-shadow: 0 0 1px 1px rgba(255,255,255,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .chat-history::-webkit-scrollbar-thumb,
|
||||||
.ffz-dark .table::-webkit-scrollbar-thumb,
|
.ffz-dark .table::-webkit-scrollbar-thumb,
|
||||||
.ember-chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
.ember-chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||||
.chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
.chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||||
|
@ -608,6 +617,7 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
.ember-chat .ffz-moderation-card {
|
.ember-chat .ffz-moderation-card {
|
||||||
border: 2px solid #cbcbcb;
|
border: 2px solid #cbcbcb;
|
||||||
|
max-width: 340px;
|
||||||
/*box-shadow: #808080 0 0 5px;*/
|
/*box-shadow: #808080 0 0 5px;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -692,35 +702,50 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This cuts off emotes.
|
||||||
.ffz-chat-background .ember-chat .chat-messages .chat-line {
|
.ffz-chat-background .ember-chat .chat-messages .chat-line {
|
||||||
padding-left: 20px;
|
padding: 3px 20px;
|
||||||
padding-right: 20px;
|
margin: 0px 0px;
|
||||||
}
|
} */
|
||||||
|
|
||||||
|
.ffz-chat-background .chat-history .chat-line.ffz-alternate,
|
||||||
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-alternate {
|
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-alternate {
|
||||||
background-color: rgba(0,0,0, 0.1);
|
background-color: rgba(0,0,0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .chat-history .chat-line.ffz-mentioned,
|
||||||
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-mentioned {
|
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-mentioned {
|
||||||
background-color: rgba(255,127,127, 0.2);
|
background-color: rgba(255,127,127, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .chat-history .chat-line.ffz-mentioned-ffz-alternate,
|
||||||
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate {
|
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate {
|
||||||
background-color: rgba(255,127,127, 0.4);
|
background-color: rgba(255,127,127, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz-chat-background .app-main.theatre .chat-history .chat-line.ffz-alternate,
|
||||||
|
.ffz-chat-background .chat-container.dark .chat-history .chat-line.ffz-alternate,
|
||||||
|
.ffz-chat-background .ember-chat-container.dark .chat-history .chat-line.ffz-alternate,
|
||||||
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-alternate,
|
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-alternate,
|
||||||
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-alternate,
|
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-alternate,
|
||||||
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-alternate {
|
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-alternate {
|
||||||
background-color: rgba(255,255,255, 0.05);
|
background-color: rgba(255,255,255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ffz-chat-background .app-main.theatre .chat-history .chat-line.ffz-mentioned,
|
||||||
|
.ffz-chat-background .chat-container.dark .chat-history .chat-line.ffz-mentioned,
|
||||||
|
.ffz-chat-background .ember-chat-container.dark .chat-history .chat-line.ffz-mentioned,
|
||||||
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-mentioned,
|
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-mentioned,
|
||||||
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned,
|
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned,
|
||||||
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned {
|
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned {
|
||||||
background-color: rgba(255,0,0, 0.2);
|
background-color: rgba(255,0,0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .app-main.theatre .chat-history .chat-line.ffz-mentioned.ffz-alternate,
|
||||||
|
.ffz-chat-background .chat-container.dark .chat-history .chat-line.ffz-mentioned.ffz-alternate,
|
||||||
|
.ffz-chat-background .ember-chat-container.dark .chat-history .chat-line.ffz-mentioned.ffz-alternate,
|
||||||
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate,
|
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate,
|
||||||
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate,
|
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate,
|
||||||
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate {
|
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .chat-line.ffz-mentioned.ffz-alternate {
|
||||||
|
@ -730,11 +755,31 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
/* The New Whispers */
|
/* The New Whispers */
|
||||||
|
|
||||||
|
.ffz-chat-background .ember-chat .chat-messages .whisper-line {
|
||||||
|
padding-left: 16px;
|
||||||
|
border-left-width: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .whisper-line.whisper-incoming,
|
||||||
|
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming,
|
||||||
|
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming {
|
||||||
|
/* 675980 */
|
||||||
|
background-color: rgba(78,51,128, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate,
|
.ffz-chat-background .app-main.theatre .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate,
|
||||||
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate,
|
.ffz-chat-background .chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate,
|
||||||
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate {
|
.ffz-chat-background .ember-chat-container.dark .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate {
|
||||||
background-color: #131317;
|
/* 675980 */
|
||||||
|
background-color: rgba(78,51,128, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .ember-chat .chat-messages .whisper-line.whisper-incoming {
|
||||||
|
background-color: rgba(205,178,255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-chat-background .ember-chat .chat-messages .whisper-line.whisper-incoming.ffz-alternate {
|
||||||
|
background-color: rgba(205,178,255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -827,6 +872,8 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
|
|
||||||
/* Dumb Fixes */
|
/* Dumb Fixes */
|
||||||
|
|
||||||
|
.ffz-bttv .no-bttv { display: none; }
|
||||||
|
|
||||||
.chat-container.dark, .app-main.theatre .chat-container,
|
.chat-container.dark, .app-main.theatre .chat-container,
|
||||||
.chat-container.force-dark, .ember-chat-container.dark,
|
.chat-container.force-dark, .ember-chat-container.dark,
|
||||||
.app-main.theatre .ember-chat-container.chat-container,
|
.app-main.theatre .ember-chat-container.chat-container,
|
||||||
|
@ -834,10 +881,6 @@ body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-controls .notify-menu {
|
|
||||||
bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Unsafe Links */
|
/* Unsafe Links */
|
||||||
|
|
||||||
|
@ -854,9 +897,19 @@ a.unsafe-link {
|
||||||
#ffz-group-tabs {
|
#ffz-group-tabs {
|
||||||
padding: 10px 10px 6px;
|
padding: 10px 10px 6px;
|
||||||
box-shadow: inset 0 -1px 0 0 rgba(0,0,0,0.2);
|
box-shadow: inset 0 -1px 0 0 rgba(0,0,0,0.2);
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-chat-tab {
|
.ffz-chat-tab {
|
||||||
|
flex-grow: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 70px;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
margin: 0 4px 4px 0;
|
margin: 0 4px 4px 0;
|
||||||
|
@ -925,10 +978,14 @@ a.unsafe-link {
|
||||||
.ffz-chat-tab span:empty { display: none; }
|
.ffz-chat-tab span:empty { display: none; }
|
||||||
|
|
||||||
.ffz-chat-tab span {
|
.ffz-chat-tab span {
|
||||||
padding: 2px;
|
padding: 0 4px;
|
||||||
margin-left: 5px;
|
display: inline-block;
|
||||||
background-color: #777;
|
border-radius: 2px;
|
||||||
color: #fff;
|
text-align: center;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
color: #666;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -946,8 +1003,129 @@ a.unsafe-link {
|
||||||
color: #B9A3E3;
|
color: #B9A3E3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-main.theatre .ffz-chat-tab span,
|
||||||
|
.chat-container.dark .ffz-chat-tab span,
|
||||||
|
.ember-chat-container.dark .ffz-chat-tab span {
|
||||||
|
background-color: #19191f;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.app-main.theatre .ffz-chat-tab svg path,
|
.app-main.theatre .ffz-chat-tab svg path,
|
||||||
.chat-container.dark .ffz-chat-tab svg path,
|
.chat-container.dark .ffz-chat-tab svg path,
|
||||||
.ember-chat-container.dark .ffz-chat-tab svg path {
|
.ember-chat-container.dark .ffz-chat-tab svg path {
|
||||||
fill: #B9A3E3;
|
fill: #B9A3E3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Minimalistic Chat */
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-header,
|
||||||
|
body.ffz-minimal-chat .ember-chat #ffz-group-tabs,
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-buttons-container {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-messages,
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
||||||
|
bottom: 33px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector .dropmenu {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-room {
|
||||||
|
top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface {
|
||||||
|
height: 33px !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain {
|
||||||
|
top: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
|
||||||
|
height: 33px !important;
|
||||||
|
border-bottom: 0 !important;
|
||||||
|
border-left: 0;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Pause */
|
||||||
|
|
||||||
|
.ember-chat .chat-interface .more-messages-indicator.ffz-freeze-indicator {
|
||||||
|
opacity: 1;
|
||||||
|
cursor: default;
|
||||||
|
padding: 2px 0;
|
||||||
|
top: -21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat History */
|
||||||
|
|
||||||
|
.ember-chat .moderation-card .chat-history,
|
||||||
|
.chat-history {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: 0;
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-history.interface li:first-child { padding-top: 10px; }
|
||||||
|
.chat-history.interface li:last-child { padding-bottom: 10px; }
|
||||||
|
|
||||||
|
.chat-history .chat-line {
|
||||||
|
line-height: 20px;
|
||||||
|
padding: 4px 10px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
list-style-position: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-history .chat-line.admin .message { color: #666; }
|
||||||
|
|
||||||
|
.chat-history .timestamp {
|
||||||
|
color: #8c8c8c;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Room State */
|
||||||
|
|
||||||
|
.ffz.room-state.stat {
|
||||||
|
line-height: 30px;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.primary.ffz-waiting:not(:hover) {
|
||||||
|
background-color: rgba(0,0,0,0.1);
|
||||||
|
color: #32323e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button.primary.ffz-waiting.ffz-banned:not(:hover) {
|
||||||
|
background-color: rgba(128,0,0,0.1);
|
||||||
|
color: #88323e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-container.dark .button.primary.ffz-waiting:not(:hover),
|
||||||
|
.app-main.theatre .button-primary.ffz-waiting:not(:hover),
|
||||||
|
.chat-container.force-dark .button-primary.ffz-waiting:not(:hover),
|
||||||
|
.ember-chat-container.dark .button-primary.ffz-waiting:not(:hover),
|
||||||
|
.ember-chat-container.force-dark .button-primary.ffz-waiting:not(:hover) {
|
||||||
|
background-color: rgba(255,255,255,0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-container.dark .button.primary.ffz-waiting.ffz-banned:not(:hover),
|
||||||
|
.app-main.theatre .button-primary.ffz-waiting.ffz-banned:not(:hover),
|
||||||
|
.chat-container.force-dark .button-primary.ffz-waiting.ffz-banned:not(:hover),
|
||||||
|
.ember-chat-container.dark .button-primary.ffz-waiting.ffz-banned:not(:hover),
|
||||||
|
.ember-chat-container.force-dark .button-primary.ffz-waiting.ffz-banned:not(:hover) {
|
||||||
|
background-color: rgba(255,128,128,0.1);
|
||||||
|
color: #f66;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue