mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 05:15:54 +00:00
I really need to remember to commit more frequently.
This commit is contained in:
parent
800553c602
commit
d55af32b4e
45 changed files with 4777 additions and 2913 deletions
184
dark.css
184
dark.css
|
@ -89,6 +89,7 @@
|
|||
|
||||
|
||||
/* main column */
|
||||
body.ffz-dark,
|
||||
.ffz-dark div#mantle_skin,
|
||||
.ffz-dark div#main_col {
|
||||
background:rgb(16,16,16);
|
||||
|
@ -143,9 +144,14 @@
|
|||
|
||||
/* Popups */
|
||||
|
||||
.ffz-dark #commission_modal {
|
||||
background-color: #101010 !important;
|
||||
border-color: #32323e !important;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-settings-menu,
|
||||
.ffz-dark .ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
|
||||
.ffz-dark .card,
|
||||
.ffz-dark .card:not(#passport_modal),
|
||||
.ffz-dark #flyout .content,
|
||||
.ffz-dark .whatisthis,
|
||||
.ffz-dark .ui-menu,
|
||||
|
@ -164,6 +170,7 @@
|
|||
background-color: rgb(16,16,16);
|
||||
color: rgb(195,195,195); /*#acacbf;*/
|
||||
border-color: #32323e;
|
||||
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
|
||||
}
|
||||
|
||||
.ffz-dark .st-autocomplete-sidebar .label,
|
||||
|
@ -207,6 +214,7 @@
|
|||
.ffz-dark .change-banner .banner-preview,
|
||||
.ffz-dark form.js-new_panel_form input,
|
||||
.ffz-dark form.js-new_panel_form textarea,
|
||||
.ffz-dark .conversation-input-bar textarea,
|
||||
.ffz-dark .card input,
|
||||
.ffz-dark .card textarea,
|
||||
.ffz-dark .dropmenu input,
|
||||
|
@ -216,6 +224,7 @@
|
|||
.ffz-dark textarea,
|
||||
.ffz-dark select,
|
||||
.ffz-dark option,
|
||||
.ffz-dark #mantle_skin .dropdown,
|
||||
.ffz-dark .directory_header #custom_filter input {
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
border-color: rgba(255,255,255,0.1);
|
||||
|
@ -379,6 +388,17 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.ffz-dark .tabs.tabs--fullwidth li:not(.selected) a:hover {
|
||||
color: #fff !important;
|
||||
background: #24242a;
|
||||
border-color: #8c8c9c;
|
||||
}
|
||||
|
||||
.ffz-dark .tabs li.selected a {
|
||||
color: #fff !important;
|
||||
border-color: #a68cd4 !important;
|
||||
}
|
||||
|
||||
|
||||
/* Subscriptions Page */
|
||||
|
||||
|
@ -715,6 +735,25 @@
|
|||
|
||||
/* Dashboard */
|
||||
|
||||
.ffz-dark .brick {
|
||||
background-color: #121212;
|
||||
}
|
||||
|
||||
.ffz-dark .brick--faint,
|
||||
.ffz-dark .brick--block {
|
||||
background-color: rgb(25,25,25);
|
||||
}
|
||||
|
||||
.ffz-dark .brick--faint,
|
||||
.ffz-dark .brick--block,
|
||||
.ffz-dark #action_feed .action,
|
||||
.ffz-dark .revHeader__item {
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark #mantle_skin .what { background-color: #6441a5; }
|
||||
.ffz-dark #action_feed .action { padding-bottom: 20px }
|
||||
|
||||
.ffz-dark .js-stream-key-button-container .button {
|
||||
margin: 15px auto;
|
||||
}
|
||||
|
@ -824,6 +863,13 @@
|
|||
border-right-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark #dash_main .dash-player-contain.collapsed #player_overlay,
|
||||
.ffz-dark .dash-hostmode-contain {
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.ffz-dark #dash_main #delay_controls,
|
||||
.ffz-dark #dash_main #commercial_buttons,
|
||||
.ffz-dark .dash-hostmode-list-contain {
|
||||
border-top-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
@ -909,15 +955,111 @@
|
|||
}
|
||||
|
||||
|
||||
/* Conversations */
|
||||
/* VoDs */
|
||||
|
||||
.ffz-dark .ignore-cta {
|
||||
background-color: #333;
|
||||
box-shadow: 0 3px 0 #000;
|
||||
.ffz-dark .app-main .chatReplay .notice-wrapper .svg-logo_glitch {
|
||||
fill: #242424 !important;
|
||||
}
|
||||
|
||||
.ffz-dark .ignore-cta .conversation-system-message {
|
||||
color: #ccc;
|
||||
.ffz-dark .app-main .chatReplay .notice-wrapper {
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
.ffz-dark .chatReplay .loading-spinner-container {
|
||||
background: rgba(25,25,25,0.65);
|
||||
}
|
||||
|
||||
|
||||
/* Conversations */
|
||||
|
||||
.ffz-dark .conversations-list-bottom-bar {
|
||||
background-color: #19191f;
|
||||
color: #8c8c9c;
|
||||
border-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-list-bottom-bar:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list {
|
||||
background-color: #19191f;
|
||||
border: 1px solid #32323e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list:before {
|
||||
right: 9px;
|
||||
border-color: rgba(50,50,62,0);
|
||||
border-top-color: #32323e;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list:after {
|
||||
border-color: rgba(25,25,31,0);
|
||||
border-top-color: #19191f;
|
||||
border-width: 10px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversations-list-header {
|
||||
background-color: #121217;
|
||||
border-bottom: 1px solid #32323e;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversation-preview-line {
|
||||
color: #8c8c9c;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .search-divider,
|
||||
.ffz-dark .conversations-list .conversations-list-item {
|
||||
border-bottom: 1px solid #32323e;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .conversations-list-item:hover {
|
||||
background-color: #121217;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window {
|
||||
background-color: #19191f;
|
||||
box-shadow: none;
|
||||
color: #8c8c9c;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .search-divider,
|
||||
.ffz-dark .conversation-header {
|
||||
background-color: #121217;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-actions .button,
|
||||
.ffz-dark .conversation-input-actions .follow-button:not(.ember-follow) .follow,
|
||||
.ffz-dark .follow-button:not(.ember-follow) .conversation-input-actions .follow {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-header {
|
||||
background-color: #121217;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-header-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-bar textarea:focus {
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .button,
|
||||
.ffz-dark .conversation-window.has-focus .conversation-input-actions .follow-button:not(.ember-follow) .follow,
|
||||
.ffz-dark .follow-button:not(.ember-follow) .conversation-window.has-focus .conversation-input-actions .follow {
|
||||
background-color: #6441a5;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-system-message {
|
||||
background-color: #19191f;
|
||||
color: #8c8c9c;
|
||||
border-bottom: 1px solid #32323e;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-toggle svg path {
|
||||
|
@ -954,6 +1096,32 @@
|
|||
border-bottom-color: rgb(16,16,16);
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window {
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
.ffz-dark:not(.ffz-top-conversations) .conversation-window {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ffz-dark.ffz-top-conversations .conversation-window {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ffz-dark .conversation-window:not(.collapsed) .conversation-header {
|
||||
border-bottom: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
|
||||
|
||||
/*.ffz-dark .ignore-cta {
|
||||
background-color: #333;
|
||||
box-shadow: 0 3px 0 #000;
|
||||
}
|
||||
|
||||
.ffz-dark .ignore-cta .conversation-system-message {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list-icon {
|
||||
background: #19191f;
|
||||
color: #8c8c9c;
|
||||
|
@ -1054,4 +1222,4 @@
|
|||
.ffz-dark .conversation-window .new-message-divider span { background: transparent; }
|
||||
|
||||
.ffz-dark .conversation-window .timestamp-line:after,
|
||||
.ffz-dark .conversation-window .new-message-divider:after { display: none; }
|
||||
.ffz-dark .conversation-window .new-message-divider:after { display: none; }*/
|
50
gulpfile.js
50
gulpfile.js
|
@ -24,7 +24,8 @@ var ftp = require('vinyl-ftp'),
|
|||
|
||||
// Server Dependencies
|
||||
var http = require("http"),
|
||||
//https = require("https"),
|
||||
https = require("https"),
|
||||
net = require('net'),
|
||||
path = require("path"),
|
||||
request = require("request"),
|
||||
url = require("url");
|
||||
|
@ -220,7 +221,21 @@ gulp.task('server', function() {
|
|||
fs.exists(file, function(exists) {
|
||||
if ( ! exists ) {
|
||||
util.log("[" + util.colors.cyan("HTTP") + "] " + util.colors.bold.blue("CDN") + " GET " + util.colors.magenta(uri));
|
||||
return request.get("http://cdn.frankerfacez.com/" + uri).pipe(res);
|
||||
/*https.request({
|
||||
hostname: 'cdn.frankerfacez.com',
|
||||
port: 443,
|
||||
path: uri,
|
||||
method: 'GET'
|
||||
}, function(cli_res) {
|
||||
res.writeHead(cli_res.statusCode, cli_res.headers);
|
||||
cli_res.on('data', function(chunk) { res.write(chunk); });
|
||||
cli_res.on('end', function() { res.end() });
|
||||
}).on('error', function(e) {
|
||||
res.writeHead(502, {"Access-Control-Allow-Origin": "*"});
|
||||
res.write('502 Bad Gateway');
|
||||
res.end();
|
||||
});*/
|
||||
return request.get("http://cdn.frankerfacez.com/" + uri).on('error', function(err) { res.end() }).pipe(res);
|
||||
}
|
||||
|
||||
var headers = {"Access-Control-Allow-Origin": "*"};
|
||||
|
@ -242,8 +257,33 @@ gulp.task('server', function() {
|
|||
|
||||
};
|
||||
|
||||
http.createServer(handle_req).listen(8000, "localhost");
|
||||
//https.createServer(handle_req).listen(8000, "localhost");
|
||||
if ( fs.existsSync("dev_key.pem") ) {
|
||||
var https_options = {
|
||||
key: fs.readFileSync("dev_key.pem"),
|
||||
cert: fs.readFileSync("dev_cert.pem")
|
||||
};
|
||||
|
||||
util.log("[" + util.colors.cyan("HTTP") + "] Listening on Port: " + util.colors.magenta("8000"));
|
||||
http.createServer(handle_req).listen(8001, "localhost");
|
||||
https.createServer(https_options, handle_req).listen(8002, "localhost");
|
||||
|
||||
net.createServer(function(conn) {
|
||||
conn.on('error', function(e) {
|
||||
util.log("[" + util.colors.cyan("HTTP") + "] Connection Error: " + util.colors.magenta('' + e));
|
||||
});
|
||||
|
||||
conn.once('data', function(buf) {
|
||||
var address = (buf[0] === 22) ? 8002 : 8001;
|
||||
var proxy = net.createConnection(address, function() {
|
||||
proxy.write(buf);
|
||||
conn.pipe(proxy).pipe(conn);
|
||||
});
|
||||
});
|
||||
}).listen(8000);
|
||||
|
||||
util.log("[" + util.colors.cyan("HTTPS") + "] Listening on Port: " + util.colors.magenta("8000"));
|
||||
|
||||
} else {
|
||||
http.createServer(handle_req).listen(8000, "localhost");
|
||||
util.log("[" + util.colors.cyan("HTTP") + "] Listening on Port: " + util.colors.magenta("8000"));
|
||||
}
|
||||
});
|
|
@ -2,11 +2,10 @@ var FFZ = window.FrankerFaceZ,
|
|||
constants = require('./constants'),
|
||||
utils = require('./utils'),
|
||||
|
||||
MOD_BADGES = [
|
||||
SPECIAL_BADGES = [
|
||||
['staff', 'staff', 'Staff'],
|
||||
['admin', 'admin', 'Admin'],
|
||||
['global_mod', 'global-moderator', 'Global Moderator'],
|
||||
['mod', 'moderator', 'Moderator']
|
||||
['global_mod', 'global-moderator', 'Global Moderator']
|
||||
],
|
||||
|
||||
badge_css = function(badge) {
|
||||
|
@ -31,6 +30,21 @@ FFZ.settings_info.show_badges = {
|
|||
};
|
||||
|
||||
|
||||
FFZ.settings_info.sub_notice_badges = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
||||
category: "Chat Appearance",
|
||||
name: "Subscriber Notice Badges",
|
||||
help: "Display a subscriber badge on chat messages about new subscribers.",
|
||||
|
||||
on_update: function(val) {
|
||||
this.toggle_style('badges-sub-notice', ! val);
|
||||
this.toggle_style('badges-sub-notice-on', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.legacy_badges = {
|
||||
type: "select",
|
||||
options: {
|
||||
|
@ -120,6 +134,9 @@ FFZ.prototype.setup_badges = function() {
|
|||
this.toggle_style('badges-circular-small', val === 4);
|
||||
this.toggle_style('badges-transparent', val === 5);
|
||||
document.body.classList.toggle('ffz-transparent-badges', val === 5);
|
||||
|
||||
this.toggle_style('badges-sub-notice', ! this.settings.sub_notice_badges);
|
||||
this.toggle_style('badges-sub-notice-on', this.settings.sub_notice_badges);
|
||||
}
|
||||
|
||||
this.toggle_style('badges-legacy', this.settings.legacy_badges === 3);
|
||||
|
@ -184,7 +201,7 @@ FFZ.prototype.get_badges = function(user, room_id, badges, msg) {
|
|||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible === "function" )
|
||||
visible = visible.bind(this)(room_id, user, msg, badges);
|
||||
visible = visible.call(this, room_id, user, msg, badges);
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
|
@ -215,26 +232,34 @@ FFZ.prototype.get_badges = function(user, room_id, badges, msg) {
|
|||
|
||||
|
||||
FFZ.prototype.get_line_badges = function(msg) {
|
||||
var badges = {};
|
||||
var badges = {},
|
||||
room = msg.get && msg.get('room') || msg.room,
|
||||
from = msg.get && msg.get('from') || msg.from,
|
||||
tags = msg.get && msg.get('tags') || msg.tags || {},
|
||||
labels = msg.labels || [];
|
||||
|
||||
if ( msg.room && msg.from === msg.room )
|
||||
if ( room && from === room )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else if ( msg.labels )
|
||||
for(var i=0, l = MOD_BADGES.length; i < l; i++) {
|
||||
var mb = MOD_BADGES[i];
|
||||
if ( msg.labels.indexOf(mb[0]) !== -1 ) {
|
||||
else {
|
||||
for(var i=0, l = SPECIAL_BADGES.length; i < l; i++) {
|
||||
var mb = SPECIAL_BADGES[i];
|
||||
if ( tags['user-type'] === mb[0] || labels.indexOf(mb[0]) !== -1 ) {
|
||||
badges[0] = {klass: mb[1], title: mb[2]}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( msg.labels && msg.labels.indexOf('subscriber') !== -1 )
|
||||
if ( tags.mod || labels.indexOf('mod') !== -1 )
|
||||
badges[1] = {klass: 'moderator', title: 'Moderator'};
|
||||
}
|
||||
|
||||
if ( tags.subscriber || labels.indexOf('subscriber') !== -1 )
|
||||
badges[10] = {klass: 'subscriber', title: 'Subscriber'}
|
||||
if ( msg.labels && msg.labels.indexOf('turbo') !== -1 )
|
||||
if ( tags.turbo || labels.indexOf('turbo') !== -1 )
|
||||
badges[15] = {klass: 'turbo', title: 'Turbo'};
|
||||
|
||||
// FFZ Badges
|
||||
return this.get_badges(msg.from, msg.room, badges, msg);
|
||||
return this.get_badges(from, room, badges, msg);
|
||||
}
|
||||
|
||||
|
||||
|
@ -244,8 +269,8 @@ FFZ.prototype.get_other_badges = function(user_id, room_id, user_type, has_sub,
|
|||
if ( room_id && user_id === room_id )
|
||||
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||
else
|
||||
for(var i=0, l = MOD_BADGES.length; i < l; i++) {
|
||||
var mb = MOD_BADGES[i];
|
||||
for(var i=0, l = SPECIAL_BADGES.length; i < l; i++) {
|
||||
var mb = SPECIAL_BADGES[i];
|
||||
if ( user_type === mb[0] ) {
|
||||
badges[0] = {klass: mb[1], title: mb[2]};
|
||||
break;
|
||||
|
@ -325,7 +350,7 @@ FFZ.prototype.bttv_badges = function(data) {
|
|||
if ( full_badge.visible !== undefined ) {
|
||||
var visible = full_badge.visible;
|
||||
if ( typeof visible == "function" )
|
||||
visible = visible.bind(this)(null, user_id);
|
||||
visible = visible.call(this, null, user_id);
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
|
@ -407,12 +432,12 @@ FFZ.prototype._legacy_add_donors = function() {
|
|||
badges = user.badges = user.badges || {};
|
||||
|
||||
if ( ! badges[0] )
|
||||
badges[0] = {id:2};
|
||||
badges[1] = {id:2};
|
||||
}
|
||||
|
||||
// Special Badges
|
||||
this.users.sirstendec = {badges: {1: {id:0}}, sets: [4330]};
|
||||
this.users.zenwan = {badges: {0: {id:2, image: "//cdn.frankerfacez.com/script/momiglee_badge.png", title: "WAN"}}};
|
||||
this.users.sirstendec = {badges: {5: {id:0}}, sets: [4330]};
|
||||
this.users.zenwan = {badges: {1: {id:2, image: "//cdn.frankerfacez.com/script/momiglee_badge.png", title: "WAN"}}};
|
||||
|
||||
this._legacy_load_bots();
|
||||
this._legacy_load_donors();
|
||||
|
@ -421,7 +446,7 @@ FFZ.prototype._legacy_add_donors = function() {
|
|||
FFZ.prototype._legacy_load_bots = function(callback, tries) {
|
||||
jQuery.ajax(constants.SERVER + "script/bots.txt", {context: this})
|
||||
.done(function(data) {
|
||||
this._legacy_parse_badges(callback, data, 0, 2, "Bot (By: {})");
|
||||
this._legacy_parse_badges(callback, data, 1, 2, "Bot (By: {})");
|
||||
|
||||
}).fail(function(data) {
|
||||
if ( data.status == 404 )
|
||||
|
@ -436,7 +461,7 @@ FFZ.prototype._legacy_load_bots = function(callback, tries) {
|
|||
FFZ.prototype._legacy_load_donors = function(callback, tries) {
|
||||
jQuery.ajax(constants.SERVER + "script/donors.txt", {context: this})
|
||||
.done(function(data) {
|
||||
this._legacy_parse_badges(callback, data, 1, 1);
|
||||
this._legacy_parse_badges(callback, data, 5, 1);
|
||||
|
||||
}).fail(function(data) {
|
||||
if ( data.status == 404 )
|
||||
|
|
371
src/colors.js
371
src/colors.js
|
@ -1,4 +1,5 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('./utils'),
|
||||
|
||||
hue2rgb = function(p, q, t) {
|
||||
if ( t < 0 ) t += 1;
|
||||
|
@ -29,6 +30,8 @@ FFZ.settings_info.fix_color = {
|
|||
},
|
||||
value: '1',
|
||||
|
||||
visible: function() { return localStorage.hasOwnProperty("ffz_setting_fix_color") },
|
||||
|
||||
category: "Chat Appearance",
|
||||
no_bttv: true,
|
||||
|
||||
|
@ -64,21 +67,28 @@ FFZ.settings_info.luv_contrast = {
|
|||
help: "Set the minimum contrast ratio used by Luv Adjustment to ensure colors are readable.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.luv_contrast,
|
||||
new_val = prompt("Luv Adjustment Minimum Contrast Ratio\n\nPlease enter a new value for the minimum contrast ratio required between username colors and the background. The default is: 4.5", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.luv_contrast;
|
||||
|
||||
utils.prompt(
|
||||
"Luv Adjustment Minimum Contrast Ratio",
|
||||
"Please enter a new value for the minimum contrast ratio required between username colors and the background.</p><p><b>Default:</b> 4.5",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
var parsed = parseFloat(new_val);
|
||||
if ( parsed === NaN || parsed < 1 )
|
||||
if ( Number.isNaN(parsed) || ! Number.isFinite(parsed) )
|
||||
parsed = 4.5;
|
||||
|
||||
this.settings.set("luv_contrast", parsed);
|
||||
f.settings.set("luv_contrast", parsed);
|
||||
});
|
||||
},
|
||||
|
||||
on_update: function(val) {
|
||||
this._rebuild_contrast();
|
||||
this._rebuild_filter_styles();
|
||||
|
||||
if ( ! this.has_bttv && this.settings.fix_color == '1' )
|
||||
this._rebuild_colors();
|
||||
|
@ -116,7 +126,7 @@ FFZ.settings_info.color_blind = {
|
|||
FFZ.prototype.setup_colors = function() {
|
||||
this.toggle_style('chat-colors-gray', !this.has_bttv && this.settings.fix_color === '-1');
|
||||
|
||||
this._colors = {};
|
||||
this._hex_colors = {};
|
||||
this._rebuild_contrast();
|
||||
|
||||
this._update_colors();
|
||||
|
@ -141,6 +151,9 @@ FFZ.prototype.setup_colors = function() {
|
|||
|
||||
FFZ.Color = {};
|
||||
|
||||
FFZ.Color._canvas = null;
|
||||
FFZ.Color._context = null;
|
||||
|
||||
FFZ.Color.CVDMatrix = {
|
||||
protanope: [ // reds are greatly reduced (1% men)
|
||||
0.0, 2.02344, -2.52581,
|
||||
|
@ -160,44 +173,66 @@ FFZ.Color.CVDMatrix = {
|
|||
}
|
||||
|
||||
|
||||
var RGBColor = FFZ.Color.RGB = function(r, g, b) {
|
||||
this.r = r||0; this.g = g||0; this.b = b||0;
|
||||
var RGBAColor = FFZ.Color.RGBA = function(r, g, b, a) {
|
||||
this.r = r||0; this.g = g||0; this.b = b||0; this.a = a||0;
|
||||
};
|
||||
|
||||
var HSVColor = FFZ.Color.HSV = function(h, s, v) {
|
||||
this.h = h||0; this.s = s||0; this.v = v||0;
|
||||
var HSVAColor = FFZ.Color.HSVA = function(h, s, v, a) {
|
||||
this.h = h||0; this.s = s||0; this.v = v||0; this.a = a||0;
|
||||
};
|
||||
|
||||
var HSLColor = FFZ.Color.HSL = function(h, s, l) {
|
||||
this.h = h||0; this.s = s||0; this.l = l||0;
|
||||
var HSLAColor = FFZ.Color.HSLA = function(h, s, l, a) {
|
||||
this.h = h||0; this.s = s||0; this.l = l||0; this.a = a||0;
|
||||
};
|
||||
|
||||
var XYZColor = FFZ.Color.XYZ = function(x, y, z) {
|
||||
this.x = x||0; this.y = y||0; this.z = z||0;
|
||||
var XYZAColor = FFZ.Color.XYZA = function(x, y, z, a) {
|
||||
this.x = x||0; this.y = y||0; this.z = z||0; this.a = a||0;
|
||||
};
|
||||
|
||||
var LUVColor = FFZ.Color.LUV = function(l, u, v) {
|
||||
this.l = l||0; this.u = u||0; this.v = v||0;
|
||||
var LUVAColor = FFZ.Color.LUVA = function(l, u, v, a) {
|
||||
this.l = l||0; this.u = u||0; this.v = v||0; this.a = a||0;
|
||||
};
|
||||
|
||||
|
||||
// RGB Colors
|
||||
// RGBA Colors
|
||||
|
||||
RGBColor.prototype.eq = function(rgb) {
|
||||
return rgb.r === this.r && rgb.g === this.g && rgb.b === this.b;
|
||||
RGBAColor.prototype.eq = function(rgb) {
|
||||
return rgb.r === this.r && rgb.g === this.g && rgb.b === this.b && rgb.a === this.a;
|
||||
}
|
||||
|
||||
RGBColor.fromCSS = function(rgb) {
|
||||
RGBAColor.fromName = function(name) {
|
||||
var context = FFZ.Color._context;
|
||||
if ( ! context ) {
|
||||
var canvas = FFZ.Color._canvas = document.createElement('canvas');
|
||||
context = FFZ.Color._context = canvas.getContext("2d");
|
||||
}
|
||||
|
||||
context.clearRect(0,0,1,1);
|
||||
context.fillStyle = name;
|
||||
context.fillRect(0,0,1,1);
|
||||
var data = context.getImageData(0,0,1,1);
|
||||
|
||||
if ( ! data || ! data.data || data.data.length !== 4 )
|
||||
return null;
|
||||
|
||||
return new RGBAColor(data.data[0], data.data[1], data.data[2], data.data[3] / 255);
|
||||
}
|
||||
|
||||
RGBAColor.fromCSS = function(rgb) {
|
||||
if ( ! rgb )
|
||||
return null;
|
||||
|
||||
rgb = rgb.trim();
|
||||
|
||||
if ( rgb.charAt(0) === '#' )
|
||||
return RGBColor.fromHex(rgb);
|
||||
return RGBAColor.fromHex(rgb);
|
||||
|
||||
var match = /rgba?\( *(\d+%?) *, *(\d+%?) *, *(\d+%?) *(?:,[^\)]+)?\)/.exec(rgb);
|
||||
var match = /rgba?\( *(\d+%?) *, *(\d+%?) *, *(\d+%?) *(?:, *([\d\.]+))?\)/i.exec(rgb);
|
||||
if ( match ) {
|
||||
var r = match[1],
|
||||
g = match[2],
|
||||
b = match[3];
|
||||
b = match[3],
|
||||
a = match[4];
|
||||
|
||||
if ( r.charAt(r.length-1) === '%' )
|
||||
r = 255 * (parseInt(r) / 100);
|
||||
|
@ -214,26 +249,36 @@ RGBColor.fromCSS = function(rgb) {
|
|||
else
|
||||
b = parseInt(b);
|
||||
|
||||
return new RGBColor(
|
||||
if ( a )
|
||||
if ( a.charAt(a.length-1) === '%' )
|
||||
a = parseInt(a) / 100;
|
||||
else
|
||||
a = parseFloat(a);
|
||||
else
|
||||
a = 1;
|
||||
|
||||
return new RGBAColor(
|
||||
Math.min(Math.max(0, r), 255),
|
||||
Math.min(Math.max(0, g), 255),
|
||||
Math.min(Math.max(0, b), 255)
|
||||
Math.min(Math.max(0, b), 255),
|
||||
Math.min(Math.max(0, a), 1)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
return RGBAColor.fromName(rgb);
|
||||
}
|
||||
|
||||
RGBColor.fromHex = function(code) {
|
||||
RGBAColor.fromHex = function(code) {
|
||||
var raw = parseInt(code.charAt(0) === '#' ? code.substr(1) : code, 16);
|
||||
return new RGBColor(
|
||||
return new RGBAColor(
|
||||
(raw >> 16), // Red
|
||||
(raw >> 8 & 0x00FF), // Green
|
||||
(raw & 0x0000FF) // Blue
|
||||
(raw & 0x0000FF), // Blue,
|
||||
1 // Alpha
|
||||
)
|
||||
}
|
||||
|
||||
RGBColor.fromHSV = function(h, s, v) {
|
||||
RGBAColor.fromHSVA = function(h, s, v, a) {
|
||||
var r, g, b,
|
||||
|
||||
i = Math.floor(h * 6),
|
||||
|
@ -251,55 +296,58 @@ RGBColor.fromHSV = function(h, s, v) {
|
|||
case 5: r = v, g = p, b = q;
|
||||
}
|
||||
|
||||
return new RGBColor(
|
||||
return new RGBAColor(
|
||||
Math.round(Math.min(Math.max(0, r*255), 255)),
|
||||
Math.round(Math.min(Math.max(0, g*255), 255)),
|
||||
Math.round(Math.min(Math.max(0, b*255), 255))
|
||||
Math.round(Math.min(Math.max(0, b*255), 255)),
|
||||
a === undefined ? 1 : a
|
||||
);
|
||||
}
|
||||
|
||||
RGBColor.fromXYZ = function(x, y, z) {
|
||||
RGBAColor.fromXYZA = function(x, y, z, a) {
|
||||
var R = 3.240479 * x - 1.537150 * y - 0.498535 * z,
|
||||
G = -0.969256 * x + 1.875992 * y + 0.041556 * z,
|
||||
B = 0.055648 * x - 0.204043 * y + 1.057311 * z;
|
||||
|
||||
// Make sure we end up in a real color space
|
||||
return new RGBColor(
|
||||
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(R))),
|
||||
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(G))),
|
||||
Math.max(0, Math.min(255, 255 * XYZColor.channelConverter(B)))
|
||||
return new RGBAColor(
|
||||
Math.max(0, Math.min(255, 255 * XYZAColor.channelConverter(R))),
|
||||
Math.max(0, Math.min(255, 255 * XYZAColor.channelConverter(G))),
|
||||
Math.max(0, Math.min(255, 255 * XYZAColor.channelConverter(B))),
|
||||
a === undefined ? 1 : a
|
||||
);
|
||||
}
|
||||
|
||||
RGBColor.fromHSL = function(h, s, l) {
|
||||
RGBAColor.fromHSLA = function(h, s, l, a) {
|
||||
if ( s === 0 ) {
|
||||
var v = Math.round(Math.min(Math.max(0, 255*l), 255));
|
||||
return new RGBColor(v, v, v);
|
||||
return new RGBAColor(v, v, v, a === undefined ? 1 : a);
|
||||
}
|
||||
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s,
|
||||
p = 2 * l - q;
|
||||
|
||||
return new RGBColor(
|
||||
return new RGBAColor(
|
||||
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h + 1/3)), 255)),
|
||||
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h)), 255)),
|
||||
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h - 1/3)), 255))
|
||||
Math.round(Math.min(Math.max(0, 255 * hue2rgb(p, q, h - 1/3)), 255)),
|
||||
a === undefined ? 1 : a
|
||||
);
|
||||
}
|
||||
|
||||
RGBColor.prototype.toHSV = function() { return HSVColor.fromRGB(this.r, this.g, this.b); }
|
||||
RGBColor.prototype.toHSL = function() { return HSLColor.fromRGB(this.r, this.g, this.b); }
|
||||
RGBColor.prototype.toCSS = function() { return "rgb(" + Math.round(this.r) + "," + Math.round(this.g) + "," + Math.round(this.b) + ")"; }
|
||||
RGBColor.prototype.toXYZ = function() { return XYZColor.fromRGB(this.r, this.g, this.b); }
|
||||
RGBColor.prototype.toLUV = function() { return this.toXYZ().toLUV(); }
|
||||
RGBAColor.prototype.toHSVA = function() { return HSVAColor.fromRGBA(this.r, this.g, this.b, this.a); }
|
||||
RGBAColor.prototype.toHSLA = function() { return HSLAColor.fromRGBA(this.r, this.g, this.b, this.a); }
|
||||
RGBAColor.prototype.toCSS = function() { return "rgb" + (this.a !== 1 ? "a" : "") + "(" + Math.round(this.r) + "," + Math.round(this.g) + "," + Math.round(this.b) + (this.a !== 1 ? "," + this.a : "") + ")"; }
|
||||
RGBAColor.prototype.toXYZA = function() { return XYZAColor.fromRGBA(this.r, this.g, this.b, this.a); }
|
||||
RGBAColor.prototype.toLUVA = function() { return this.toXYZA().toLUVA(); }
|
||||
|
||||
RGBColor.prototype.toHex = function() {
|
||||
RGBAColor.prototype.toHex = function() {
|
||||
var rgb = this.b | (this.g << 8) | (this.r << 16);
|
||||
return '#' + (0x1000000 + rgb).toString(16).slice(1);
|
||||
}
|
||||
|
||||
|
||||
RGBColor.prototype.luminance = function() {
|
||||
RGBAColor.prototype.luminance = function() {
|
||||
var rgb = [this.r / 255, this.g / 255, this.b / 255];
|
||||
for (var i =0, l = rgb.length; i < l; i++) {
|
||||
if (rgb[i] <= 0.03928) {
|
||||
|
@ -312,19 +360,20 @@ RGBColor.prototype.luminance = function() {
|
|||
}
|
||||
|
||||
|
||||
RGBColor.prototype.brighten = function(amount) {
|
||||
RGBAColor.prototype.brighten = function(amount) {
|
||||
amount = typeof amount === "number" ? amount : 1;
|
||||
amount = Math.round(255 * (amount / 100));
|
||||
|
||||
return new RGBColor(
|
||||
return new RGBAColor(
|
||||
Math.max(0, Math.min(255, this.r + amount)),
|
||||
Math.max(0, Math.min(255, this.g + amount)),
|
||||
Math.max(0, Math.min(255, this.b + amount))
|
||||
Math.max(0, Math.min(255, this.b + amount)),
|
||||
this.a
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
RGBColor.prototype.daltonize = function(type, amount) {
|
||||
RGBAColor.prototype.daltonize = function(type, amount) {
|
||||
amount = typeof amount === "number" ? amount : 1.0;
|
||||
var cvd;
|
||||
if ( typeof type === "string" ) {
|
||||
|
@ -366,21 +415,22 @@ RGBColor.prototype.daltonize = function(type, amount) {
|
|||
G = Math.min(Math.max(0, GG + this.g), 255);
|
||||
B = Math.min(Math.max(0, BB + this.b), 255);
|
||||
|
||||
return new RGBColor(R, G, B);
|
||||
return new RGBAColor(R, G, B, this.a);
|
||||
}
|
||||
|
||||
RGBColor.prototype._r = function(r) { return new RGBColor(r, this.g, this.b); }
|
||||
RGBColor.prototype._g = function(g) { return new RGBColor(this.r, g, this.b); }
|
||||
RGBColor.prototype._b = function(b) { return new RGBColor(this.r, this.g, b); }
|
||||
RGBAColor.prototype._r = function(r) { return new RGBAColor(r, this.g, this.b, this.a); }
|
||||
RGBAColor.prototype._g = function(g) { return new RGBAColor(this.r, g, this.b, this.a); }
|
||||
RGBAColor.prototype._b = function(b) { return new RGBAColor(this.r, this.g, b, this.a); }
|
||||
RGBAColor.prototype._a = function(a) { return new RGBAColor(this.r, this.g, this.b, a); }
|
||||
|
||||
|
||||
// HSL Colors
|
||||
|
||||
HSLColor.prototype.eq = function(hsl) {
|
||||
return hsl.h === this.h && hsl.s === this.s && hsl.l === this.l;
|
||||
HSLAColor.prototype.eq = function(hsl) {
|
||||
return hsl.h === this.h && hsl.s === this.s && hsl.l === this.l && hsl.a === this.a;
|
||||
}
|
||||
|
||||
HSLColor.fromRGB = function(r, g, b) {
|
||||
HSLAColor.fromRGBA = function(r, g, b, a) {
|
||||
r /= 255; g /= 255; b /= 255;
|
||||
|
||||
var max = Math.max(r,g,b),
|
||||
|
@ -406,27 +456,27 @@ HSLColor.fromRGB = function(r, g, b) {
|
|||
h /= 6;
|
||||
}
|
||||
|
||||
return new HSLColor(h, s, l);
|
||||
return new HSLAColor(h, s, l, a === undefined ? 1 : a);
|
||||
}
|
||||
|
||||
HSLColor.prototype.toRGB = function() { return RGBColor.fromHSL(this.h, this.s, this.l); }
|
||||
HSLColor.prototype.toCSS = function() { return "hsl(" + Math.round(this.h*360) + "," + Math.round(this.s*100) + "%," + Math.round(this.l*100) + "%)"; }
|
||||
HSLColor.prototype.toHex = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toHex(); }
|
||||
HSLColor.prototype.toHSV = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toHSV(); }
|
||||
HSLColor.prototype.toXYZ = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toXYZ(); }
|
||||
HSLColor.prototype.toLUV = function() { return RGBColor.fromHSL(this.h, this.s, this.l).toLUV(); }
|
||||
HSLAColor.prototype.toRGBA = function() { return RGBAColor.fromHSLA(this.h, this.s, this.l, this.a); }
|
||||
HSLAColor.prototype.toCSS = function() { return "hsl" + (this.a !== 1 ? "a" : "") + "(" + Math.round(this.h*360) + "," + Math.round(this.s*100) + "%," + Math.round(this.l*100) + "%" + (this.a !== 1 ? "," + this.a : "") + ")"; }
|
||||
HSLAColor.prototype.toHSVA = function() { return this.toRGBA().toHSVA(); }
|
||||
HSLAColor.prototype.toXYZA = function() { return this.toRGBA().toXYZA(); }
|
||||
HSLAColor.prototype.toLUVA = function() { return this.toRGBA().toLUVA(); }
|
||||
|
||||
|
||||
HSLColor.prototype._h = function(h) { return new HSLColor(h, this.s, this.l); }
|
||||
HSLColor.prototype._s = function(s) { return new HSLColor(this.h, s, this.l); }
|
||||
HSLColor.prototype._l = function(l) { return new HSLColor(this.h, this.s, l); }
|
||||
HSLAColor.prototype._h = function(h) { return new HSLAColor(h, this.s, this.l, this.a); }
|
||||
HSLAColor.prototype._s = function(s) { return new HSLAColor(this.h, s, this.l, this.a); }
|
||||
HSLAColor.prototype._l = function(l) { return new HSLAColor(this.h, this.s, l, this.a); }
|
||||
HSLAColor.prototype._a = function(a) { return new HSLAColor(this.h, this.s, this.l, a); }
|
||||
|
||||
|
||||
// HSV Colors
|
||||
|
||||
HSVColor.prototype.eq = function(hsv) { return hsv.h === this.h && hsv.s === this.s && hsv.v === this.v; }
|
||||
HSVAColor.prototype.eq = function(hsv) { return hsv.h === this.h && hsv.s === this.s && hsv.v === this.v && hsv.a === this.a; }
|
||||
|
||||
HSVColor.fromRGB = function(r, g, b) {
|
||||
HSVAColor.fromRGBA = function(r, g, b, a) {
|
||||
r /= 255; g /= 255; b /= 255;
|
||||
|
||||
var max = Math.max(r, g, b),
|
||||
|
@ -453,24 +503,25 @@ HSVColor.fromRGB = function(r, g, b) {
|
|||
h /= 6;
|
||||
}
|
||||
|
||||
return new HSVColor(h, s, v);
|
||||
return new HSVAColor(h, s, v, a === undefined ? 1 : a);
|
||||
}
|
||||
|
||||
|
||||
HSVColor.prototype.toRGB = function() { return RGBColor.fromHSV(this.h, this.s, this.v); }
|
||||
HSVColor.prototype.toHSL = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toHSL(); }
|
||||
HSVColor.prototype.toXYZ = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toXYZ(); }
|
||||
HSVColor.prototype.toLUV = function() { return RGBColor.fromHSV(this.h, this.s, this.v).toLUV(); }
|
||||
HSVAColor.prototype.toRGBA = function() { return RGBAColor.fromHSVA(this.h, this.s, this.v, this.a); }
|
||||
HSVAColor.prototype.toHSLA = function() { return this.toRGBA().toHSLA(); }
|
||||
HSVAColor.prototype.toXYZA = function() { return this.toRGBA().toXYZA(); }
|
||||
HSVAColor.prototype.toLUVA = function() { return this.toRGBA().toLUVA(); }
|
||||
|
||||
|
||||
HSVColor.prototype._h = function(h) { return new HSVColor(h, this.s, this.v); }
|
||||
HSVColor.prototype._s = function(s) { return new HSVColor(this.h, s, this.v); }
|
||||
HSVColor.prototype._v = function(v) { return new HSVColor(this.h, this.s, v); }
|
||||
HSVAColor.prototype._h = function(h) { return new HSVAColor(h, this.s, this.v, this.a); }
|
||||
HSVAColor.prototype._s = function(s) { return new HSVAColor(this.h, s, this.v, this.a); }
|
||||
HSVAColor.prototype._v = function(v) { return new HSVAColor(this.h, this.s, v, this.a); }
|
||||
HSVAColor.prototype._a = function(a) { return new HSVAColor(this.h, this.s, this.v, a); }
|
||||
|
||||
|
||||
// XYZ Colors
|
||||
|
||||
RGBColor.channelConverter = function (channel) {
|
||||
RGBAColor.channelConverter = function (channel) {
|
||||
// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html
|
||||
// This converts rgb 8bit to rgb linear, lazy because the other algorithm is really really dumb
|
||||
return Math.pow(channel, 2.2);
|
||||
|
@ -479,7 +530,7 @@ RGBColor.channelConverter = function (channel) {
|
|||
return (channel <= 0.04045) ? channel / 12.92 : Math.pow((channel + 0.055) / 1.055, 2.4);
|
||||
};
|
||||
|
||||
XYZColor.channelConverter = function (channel) {
|
||||
XYZAColor.channelConverter = function (channel) {
|
||||
// Using lazy conversion in the other direction as well
|
||||
return Math.pow(channel, 1/2.2);
|
||||
|
||||
|
@ -488,27 +539,28 @@ XYZColor.channelConverter = function (channel) {
|
|||
};
|
||||
|
||||
|
||||
XYZColor.prototype.eq = function(xyz) { return xyz.x === this.x && xyz.y === this.y && xyz.z === this.z; }
|
||||
XYZAColor.prototype.eq = function(xyz) { return xyz.x === this.x && xyz.y === this.y && xyz.z === this.z; }
|
||||
|
||||
XYZColor.fromRGB = function(r, g, b) {
|
||||
var R = RGBColor.channelConverter(r / 255),
|
||||
G = RGBColor.channelConverter(g / 255),
|
||||
B = RGBColor.channelConverter(b / 255);
|
||||
XYZAColor.fromRGBA = function(r, g, b, a) {
|
||||
var R = RGBAColor.channelConverter(r / 255),
|
||||
G = RGBAColor.channelConverter(g / 255),
|
||||
B = RGBAColor.channelConverter(b / 255);
|
||||
|
||||
return new XYZColor(
|
||||
return new XYZAColor(
|
||||
0.412453 * R + 0.357580 * G + 0.180423 * B,
|
||||
0.212671 * R + 0.715160 * G + 0.072169 * B,
|
||||
0.019334 * R + 0.119193 * G + 0.950227 * B
|
||||
0.019334 * R + 0.119193 * G + 0.950227 * B,
|
||||
a === undefined ? 1 : a
|
||||
);
|
||||
}
|
||||
|
||||
XYZColor.fromLUV = function(l, u, v) {
|
||||
var deltaGammaFactor = 1 / (XYZColor.WHITE.x + 15 * XYZColor.WHITE.y + 3 * XYZColor.WHITE.z);
|
||||
var uDeltaGamma = 4 * XYZColor.WHITE.x * deltaGammaFactor;
|
||||
var vDeltagamma = 9 * XYZColor.WHITE.y * deltaGammaFactor;
|
||||
XYZAColor.fromLUVA = function(l, u, v, alpha) {
|
||||
var deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z);
|
||||
var uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor;
|
||||
var vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor;
|
||||
|
||||
// XYZColor.EPSILON * XYZColor.KAPPA = 8
|
||||
var Y = (l > 8) ? Math.pow((l + 16) / 116, 3) : l / XYZColor.KAPPA;
|
||||
// XYZAColor.EPSILON * XYZAColor.KAPPA = 8
|
||||
var Y = (l > 8) ? Math.pow((l + 16) / 116, 3) : l / XYZAColor.KAPPA;
|
||||
|
||||
var a = 1/3 * (((52 * l) / (u + 13 * l * uDeltaGamma)) - 1);
|
||||
var b = -5 * Y;
|
||||
|
@ -518,36 +570,37 @@ XYZColor.fromLUV = function(l, u, v) {
|
|||
var X = (d - b) / (a - c);
|
||||
var Z = X * a + b;
|
||||
|
||||
return new XYZColor(X, Y, Z);
|
||||
return new XYZAColor(X, Y, Z, alpha === undefined ? 1 : alpha);
|
||||
}
|
||||
|
||||
|
||||
XYZColor.prototype.toRGB = function() { return RGBColor.fromXYZ(this.x, this.y, this.z); }
|
||||
XYZColor.prototype.toLUV = function() { return LUVColor.fromXYZ(this.x, this.y, this.z); }
|
||||
XYZColor.prototype.toHSL = function() { return RGBColor.fromXYZ(this.x, this.y, this.z).toHSL(); }
|
||||
XYZColor.prototype.toHSV = function() { return RGBColor.fromXYZ(this.x, this.y, this.z).toHSV(); }
|
||||
XYZAColor.prototype.toRGBA = function() { return RGBAColor.fromXYZA(this.x, this.y, this.z, this.a); }
|
||||
XYZAColor.prototype.toLUVA = function() { return LUVAColor.fromXYZA(this.x, this.y, this.z, this.a); }
|
||||
XYZAColor.prototype.toHSLA = function() { return this.toRGBA().toHSLA(); }
|
||||
XYZAColor.prototype.toHSVA = function() { return this.toRGBA().toHSVA(); }
|
||||
|
||||
|
||||
XYZColor.prototype._x = function(x) { return new XYZColor(x, this.y, this.z); }
|
||||
XYZColor.prototype._y = function(y) { return new XYZColor(this.x, y, this.z); }
|
||||
XYZColor.prototype._z = function(z) { return new XYZColor(this.x, this.y, z); }
|
||||
XYZAColor.prototype._x = function(x) { return new XYZAColor(x, this.y, this.z, this.a); }
|
||||
XYZAColor.prototype._y = function(y) { return new XYZAColor(this.x, y, this.z, this.a); }
|
||||
XYZAColor.prototype._z = function(z) { return new XYZAColor(this.x, this.y, z, this.a); }
|
||||
XYZAColor.prototype._a = function(a) { return new XYZAColor(this.x, this.y, this.z, a); }
|
||||
|
||||
|
||||
// LUV Colors
|
||||
|
||||
XYZColor.EPSILON = Math.pow(6 / 29, 3);
|
||||
XYZColor.KAPPA = Math.pow(29 / 3, 3);
|
||||
XYZColor.WHITE = (new RGBColor(255, 255, 255)).toXYZ();
|
||||
XYZAColor.EPSILON = Math.pow(6 / 29, 3);
|
||||
XYZAColor.KAPPA = Math.pow(29 / 3, 3);
|
||||
XYZAColor.WHITE = (new RGBAColor(255, 255, 255, 1)).toXYZA();
|
||||
|
||||
|
||||
LUVColor.prototype.eq = function(luv) { return luv.l === this.l && luv.u === this.u && luv.v === this.v; }
|
||||
LUVAColor.prototype.eq = function(luv) { return luv.l === this.l && luv.u === this.u && luv.v === this.v; }
|
||||
|
||||
LUVColor.fromXYZ = function(X, Y, Z) {
|
||||
var deltaGammaFactor = 1 / (XYZColor.WHITE.x + 15 * XYZColor.WHITE.y + 3 * XYZColor.WHITE.z);
|
||||
var uDeltaGamma = 4 * XYZColor.WHITE.x * deltaGammaFactor;
|
||||
var vDeltagamma = 9 * XYZColor.WHITE.y * deltaGammaFactor;
|
||||
LUVAColor.fromXYZA = function(X, Y, Z, a) {
|
||||
var deltaGammaFactor = 1 / (XYZAColor.WHITE.x + 15 * XYZAColor.WHITE.y + 3 * XYZAColor.WHITE.z);
|
||||
var uDeltaGamma = 4 * XYZAColor.WHITE.x * deltaGammaFactor;
|
||||
var vDeltagamma = 9 * XYZAColor.WHITE.y * deltaGammaFactor;
|
||||
|
||||
var yGamma = Y / XYZColor.WHITE.y;
|
||||
var yGamma = Y / XYZAColor.WHITE.y;
|
||||
var deltaDivider = (X + 15 * Y + 3 * Z);
|
||||
|
||||
if (deltaDivider === 0) {
|
||||
|
@ -559,23 +612,24 @@ LUVColor.fromXYZ = function(X, Y, Z) {
|
|||
var uDelta = 4 * X * deltaFactor;
|
||||
var vDelta = 9 * Y * deltaFactor;
|
||||
|
||||
var L = (yGamma > XYZColor.EPSILON) ? 116 * Math.pow(yGamma, 1/3) - 16 : XYZColor.KAPPA * yGamma;
|
||||
var L = (yGamma > XYZAColor.EPSILON) ? 116 * Math.pow(yGamma, 1/3) - 16 : XYZAColor.KAPPA * yGamma;
|
||||
var u = 13 * L * (uDelta - uDeltaGamma);
|
||||
var v = 13 * L * (vDelta - vDeltagamma);
|
||||
|
||||
return new LUVColor(L, u, v);
|
||||
return new LUVAColor(L, u, v, a === undefined ? 1 : a);
|
||||
}
|
||||
|
||||
|
||||
LUVColor.prototype.toXYZ = function() { return XYZColor.fromLUV(this.l, this.u, this.v); }
|
||||
LUVColor.prototype.toRGB = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toRGB(); }
|
||||
LUVColor.prototype.toHSL = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toHSL(); }
|
||||
LUVColor.prototype.toHSV = function() { return XYZColor.fromLUV(this.l, this.u, this.v).toHSV(); }
|
||||
LUVAColor.prototype.toXYZA = function() { return XYZAColor.fromLUVA(this.l, this.u, this.v, this.a); }
|
||||
LUVAColor.prototype.toRGBA = function() { return this.toXYZA().toRGBA(); }
|
||||
LUVAColor.prototype.toHSLA = function() { return this.toXYZA().toHSLA(); }
|
||||
LUVAColor.prototype.toHSVA = function() { return this.toXYZA().toHSVA(); }
|
||||
|
||||
|
||||
LUVColor.prototype._l = function(l) { return new LUVColor(l, this.u, this.v); }
|
||||
LUVColor.prototype._u = function(u) { return new LUVColor(this.l, u, this.v); }
|
||||
LUVColor.prototype._v = function(v) { return new LUVColor(this.l, this.u, v); }
|
||||
LUVAColor.prototype._l = function(l) { return new LUVAColor(l, this.u, this.v, this.a); }
|
||||
LUVAColor.prototype._u = function(u) { return new LUVAColor(this.l, u, this.v, this.a); }
|
||||
LUVAColor.prototype._v = function(v) { return new LUVAColor(this.l, this.u, v, this.a); }
|
||||
LUVAColor.prototype._a = function(a) { return new LUVAColor(this.l, this.u, this.v, a); }
|
||||
|
||||
|
||||
// --------------------
|
||||
|
@ -583,8 +637,11 @@ LUVColor.prototype._v = function(v) { return new LUVColor(this.l, this.u, v); }
|
|||
// --------------------
|
||||
|
||||
FFZ.prototype._rebuild_contrast = function() {
|
||||
this._luv_required_bright = new XYZColor(0, (this.settings.luv_contrast * (new RGBColor(35,35,35).toXYZ().y + 0.05) - 0.05), 0).toLUV().l;
|
||||
this._luv_required_dark = new XYZColor(0, ((new RGBColor(217,217,217).toXYZ().y + 0.05) / this.settings.luv_contrast - 0.05), 0).toLUV().l;
|
||||
this._luv_required_bright = new XYZAColor(0, (this.settings.luv_contrast * (new RGBAColor(35,35,35,1).toXYZA().y + 0.05) - 0.05), 0, 1).toLUVA().l;
|
||||
this._luv_required_dark = new XYZAColor(0, ((new RGBAColor(217,217,217,1).toXYZA().y + 0.05) / this.settings.luv_contrast - 0.05), 0, 1).toLUVA().l;
|
||||
|
||||
this._luv_background_bright = new XYZAColor(0, (this.settings.luv_contrast * (RGBAColor.fromCSS("#3c3a41").toXYZA().y + 0.05) - 0.05), 0, 1).toLUVA().l;
|
||||
this._luv_background_dark = new XYZAColor(0, ((RGBAColor.fromCSS("#acacbf").toXYZA().y + 0.05) / this.settings.luv_contrast - 0.05), 0, 1).toLUVA().l;
|
||||
}
|
||||
|
||||
FFZ.prototype._rebuild_colors = function() {
|
||||
|
@ -592,7 +649,7 @@ FFZ.prototype._rebuild_colors = function() {
|
|||
return;
|
||||
|
||||
// With update colors, we'll automatically process the colors we care about.
|
||||
this._colors = {};
|
||||
this._hex_colors = {};
|
||||
this._update_colors();
|
||||
}
|
||||
|
||||
|
@ -602,7 +659,8 @@ FFZ.prototype._update_colors = function(darkness_only) {
|
|||
var Layout = window.App && App.__container__.lookup('controller:layout'),
|
||||
Settings = window.App && App.__container__.lookup('controller:settings'),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode')),
|
||||
cr_dark = this.settings.dark_twitch || (Layout && Layout.get('isTheatreMode'));
|
||||
|
||||
if ( darkness_only && this._color_old_darkness === is_dark )
|
||||
return;
|
||||
|
@ -618,29 +676,50 @@ FFZ.prototype._update_colors = function(darkness_only) {
|
|||
if ( ! colors )
|
||||
continue;
|
||||
|
||||
bit.style.color = is_dark ? colors[1] : colors[0];
|
||||
bit.style.color = (bit.classList.contains('replay-color') ? cr_dark : is_dark) ? colors[1] : colors[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._handle_filter_color = function(color) {
|
||||
if (!( color instanceof RGBAColor ))
|
||||
color = RGBAColor.fromCSS(color);
|
||||
|
||||
var light_color = color,
|
||||
dark_color = color,
|
||||
luv = color.toLUVA();
|
||||
|
||||
if ( luv.l < this._luv_background_bright )
|
||||
light_color = luv._l(this._luv_background_bright).toRGBA();
|
||||
|
||||
if ( luv.l > this._luv_background_dark )
|
||||
dark_color = luv._l(this._luv_background_dark).toRGBA();
|
||||
|
||||
return [light_color, dark_color];
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._handle_color = function(color) {
|
||||
if ( color instanceof RGBColor )
|
||||
color = color.toHex();
|
||||
if ( color instanceof RGBAColor )
|
||||
color = color.toCSS();
|
||||
|
||||
if ( ! color || this._colors.hasOwnProperty(color) )
|
||||
return this._colors[color];
|
||||
if ( ! color )
|
||||
return null;
|
||||
|
||||
var rgb = RGBColor.fromHex(color),
|
||||
if ( this._hex_colors.hasOwnProperty(color) )
|
||||
return this._hex_colors[color];
|
||||
|
||||
light_color = color,
|
||||
dark_color = color;
|
||||
var rgb = RGBAColor.fromCSS(color),
|
||||
|
||||
light_color = rgb,
|
||||
dark_color = rgb;
|
||||
|
||||
// Color Blindness Handling
|
||||
if ( this.settings.color_blind !== '0' ) {
|
||||
var new_color = rgb.daltonize(this.settings.color_blind);
|
||||
if ( ! rgb.eq(new_color) ) {
|
||||
rgb = new_color;
|
||||
light_color = dark_color = rgb.toHex();
|
||||
light_color = dark_color = rgb;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,7 +736,7 @@ FFZ.prototype._handle_color = function(color) {
|
|||
break;
|
||||
}
|
||||
|
||||
light_color = nc.toHex();
|
||||
light_color = nc;
|
||||
}
|
||||
|
||||
if ( lum < 0.15 ) {
|
||||
|
@ -668,46 +747,46 @@ FFZ.prototype._handle_color = function(color) {
|
|||
break;
|
||||
}
|
||||
|
||||
dark_color = nc.toHex();
|
||||
dark_color = nc;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Color Processing - HSL
|
||||
if ( this.settings.fix_color === '2' ) {
|
||||
var hsl = rgb.toHSL();
|
||||
var hsl = rgb.toHSLA();
|
||||
|
||||
light_color = hsl._l(Math.min(Math.max(0, 0.7 * hsl.l), 1)).toHex();
|
||||
dark_color = hsl._l(Math.min(Math.max(0, 0.3 + (0.7 * hsl.l)), 1)).toHex();
|
||||
light_color = hsl._l(Math.min(Math.max(0, 0.7 * hsl.l), 1)).toRGBA();
|
||||
dark_color = hsl._l(Math.min(Math.max(0, 0.3 + (0.7 * hsl.l)), 1)).toRGBA();
|
||||
}
|
||||
|
||||
|
||||
// Color Processing - HSV
|
||||
if ( this.settings.fix_color === '3' ) {
|
||||
var hsv = rgb.toHSV();
|
||||
var hsv = rgb.toHSVA();
|
||||
|
||||
if ( hsv.s === 0 ) {
|
||||
// Black and White
|
||||
light_color = hsv._v(Math.min(Math.max(0.5, 0.5 * hsv.v), 1)).toRGB().toHex();
|
||||
dark_color = hsv._v(Math.min(Math.max(0.5, 0.5 + (0.5 * hsv.v)), 1)).toRGB().toHex();
|
||||
light_color = hsv._v(Math.min(Math.max(0.5, 0.5 * hsv.v), 1)).toRGBA();
|
||||
dark_color = hsv._v(Math.min(Math.max(0.5, 0.5 + (0.5 * hsv.v)), 1)).toRGBA();
|
||||
|
||||
} else {
|
||||
light_color = RGBColor.fromHSV(hsv.h, Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.s)), 1), Math.min(0.7, hsv.v)).toHex();
|
||||
dark_color = RGBColor.fromHSV(hsv.h, Math.min(0.7, hsv.s), Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.v)), 1)).toHex();
|
||||
light_color = RGBAColor.fromHSVA(hsv.h, Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.s)), 1), Math.min(0.7, hsv.v), hsv.a);
|
||||
dark_color = RGBAColor.fromHSVA(hsv.h, Math.min(0.7, hsv.s), Math.min(Math.max(0.7, 0.7 + (0.3 * hsv.v)), 1), hsv.a);
|
||||
}
|
||||
}
|
||||
|
||||
// Color Processing - LUV
|
||||
if ( this.settings.fix_color === '1' ) {
|
||||
var luv = rgb.toLUV();
|
||||
var luv = rgb.toLUVA();
|
||||
|
||||
if ( luv.l > this._luv_required_dark )
|
||||
light_color = luv._l(this._luv_required_dark).toRGB().toHex();
|
||||
light_color = luv._l(this._luv_required_dark).toRGBA();
|
||||
|
||||
if ( luv.l < this._luv_required_bright )
|
||||
dark_color = luv._l(this._luv_required_bright).toRGB().toHex();
|
||||
dark_color = luv._l(this._luv_required_bright).toRGBA();
|
||||
}
|
||||
|
||||
var out = this._colors[color] = [light_color, dark_color];
|
||||
var out = this._hex_colors[color] = [light_color.toHex(), dark_color.toHex()];
|
||||
return out;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -100,30 +100,30 @@ FFZ.prototype.setup_channel = function() {
|
|||
var t = this,
|
||||
id = t.get('content.id');
|
||||
|
||||
id && Twitch.api && Twitch.api.get("streams/" + id, {}, {version:3})
|
||||
id && utils.api.get("streams/" + id, {}, {version:3})
|
||||
.done(function(data) {
|
||||
if ( ! data || ! data.stream ) {
|
||||
// If the stream is offline, clear its created_at time and set it to zero viewers.
|
||||
t.set('stream.created_at', null);
|
||||
t.set('stream.viewers', 0);
|
||||
t.set('content.stream.created_at', null);
|
||||
t.set('content.stream.viewers', 0);
|
||||
return;
|
||||
}
|
||||
|
||||
t.set('stream.created_at', data.stream.created_at || null);
|
||||
t.set('stream.viewers', data.stream.viewers || 0);
|
||||
t.set('content.stream.created_at', data.stream.created_at || null);
|
||||
t.set('content.stream.viewers', data.stream.viewers || 0);
|
||||
|
||||
var game = data.stream.game || (data.stream.channel && data.stream.channel.game);
|
||||
if ( game ) {
|
||||
t.set('game', game);
|
||||
t.set('rollbackData.game', game);
|
||||
t.set('content.game', game);
|
||||
t.set('content.rollbackData.game', game);
|
||||
}
|
||||
|
||||
if ( data.stream.channel ) {
|
||||
if ( data.stream.channel.status )
|
||||
t.set('status', data.stream.channel.status);
|
||||
t.set('content.status', data.stream.channel.status);
|
||||
|
||||
if ( data.stream.channel.views )
|
||||
t.set('views', data.stream.channel.views);
|
||||
t.set('content.views', data.stream.channel.views);
|
||||
|
||||
if ( data.stream.channel.followers && t.get('content.followers.isLoaded') )
|
||||
t.set('content.followers.total', data.stream.channel.followers);
|
||||
|
@ -206,7 +206,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
},
|
||||
|
||||
ffzInit: function() {
|
||||
var id = this.get('controller.id'),
|
||||
var id = this.get('controller.content.id') || this.get('controller.id'),
|
||||
el = this.get('element');
|
||||
|
||||
f._cindex = this;
|
||||
|
@ -251,8 +251,8 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
if ( f.has_bttv || ! f.settings.stream_title )
|
||||
return;
|
||||
|
||||
var status = this.get("controller.status"),
|
||||
channel = this.get("controller.id");
|
||||
var status = this.get("controller.content.status") || this.get("controller.status"),
|
||||
channel = this.get("controller.content.id") || this.get("controller.id");
|
||||
|
||||
status = f.render_tokens(f.tokenize_line(channel, channel, status, true));
|
||||
|
||||
|
@ -267,7 +267,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
|
||||
|
||||
ffzUpdateHostButton: function() {
|
||||
var channel_id = this.get('controller.id'),
|
||||
var channel_id = this.get('controller.content.id') || this.get('controller.id'),
|
||||
hosted_id = this.get('controller.hostModeTarget.id'),
|
||||
|
||||
user = f.get_user(),
|
||||
|
@ -360,7 +360,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
},
|
||||
|
||||
ffzClickHost: function(controller, is_host) {
|
||||
var target = controller.get(is_host ? 'controller.hostModeTarget.id' : 'controller.id'),
|
||||
var target = is_host ? controller.get('controller.hostModeTarget.id') : (controller.get('controller.content.id') || controller.get('controller.id')),
|
||||
user = f.get_user(),
|
||||
room = user && f.rooms && f.rooms[user.login] && f.rooms[user.login].room,
|
||||
now_hosting = room && room.ffz_host_target;
|
||||
|
@ -381,7 +381,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
|
||||
ffzUpdateChatters: function() {
|
||||
// Get the counts.
|
||||
var room_id = this.get('controller.id'),
|
||||
var room_id = this.get('controller.content.id') || this.get('controller.id'),
|
||||
room = f.rooms && f.rooms[room_id];
|
||||
|
||||
if ( ! room || ! f.settings.chatter_count ) {
|
||||
|
@ -457,7 +457,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
|
||||
|
||||
ffzUpdatePlayerStats: function() {
|
||||
var channel_id = this.get('controller.id'),
|
||||
var channel_id = this.get('controller.content.id') || this.get('controller.id'),
|
||||
hosted_id = this.get('controller.hostModeTarget.id'),
|
||||
|
||||
el = this.get('element');
|
||||
|
@ -471,13 +471,13 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
player = undefined, stats = undefined;
|
||||
|
||||
try {
|
||||
player = player_cont && player_cont.ffz_player;
|
||||
player = player_cont && player_cont.get && player_cont.get('player');
|
||||
stats = player && player.stats;
|
||||
} catch(err) {
|
||||
f.error("Channel ffzUpdatePlayerStats: player.stats: " + err);
|
||||
}
|
||||
|
||||
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
|
||||
if ( ! container || ! f.settings.player_stats || ! stats || ! stats.hlsLatencyBroadcaster || stats.hlsLatencyBroadcaster === 'NaN' || Number.isNaN(stats.hlsLatencyBroadcaster) ) {
|
||||
if ( stat_el )
|
||||
stat_el.parentElement.removeChild(stat_el);
|
||||
} else {
|
||||
|
@ -538,7 +538,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
}
|
||||
|
||||
|
||||
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
|
||||
if ( ! container || ! f.settings.player_stats || ! stats || ! stats.hlsLatencyBroadcaster || stats.hlsLatencyBroadcaster === 'NaN' || Number.isNaN(stats.hlsLatencyBroadcaster) ) {
|
||||
if ( stat_el )
|
||||
stat_el.parentElement.removeChild(stat_el);
|
||||
} else {
|
||||
|
@ -648,7 +648,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
|||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
var id = this.get('controller.id');
|
||||
var id = this.get('controller.content.id') || this.get('controller.id');
|
||||
if ( id )
|
||||
f.ws_send("unsub", "channel." + id);
|
||||
|
||||
|
|
|
@ -13,7 +13,12 @@ FFZ.basic_settings.delayed_chat = {
|
|||
0: "No Delay",
|
||||
300: "Minor (Bot Moderation; 0.3s)",
|
||||
1200: "Normal (Human Moderation; 1.2s)",
|
||||
5000: "Large (Spoiler Removal / Really Slow Mods; 5s)"
|
||||
5000: "Large (Spoiler Removal / Really Slow Mods; 5s)",
|
||||
10000: "Extra Large (10s)",
|
||||
15000: "Extremely Large (15s)",
|
||||
20000: "Mods Asleep; Delay Chat (20s)",
|
||||
30000: "Half a Minute (30s)",
|
||||
60000: "Why??? (1m)"
|
||||
},
|
||||
|
||||
category: "Chat",
|
||||
|
@ -134,7 +139,12 @@ FFZ.settings_info.chat_delay = {
|
|||
0: "No Delay",
|
||||
300: "Minor (Bot Moderation; 0.3s)",
|
||||
1200: "Normal (Human Moderation; 1.2s)",
|
||||
5000: "Large (Spoiler Removal / Really Slow Mods; 5s)"
|
||||
5000: "Large (Spoiler Removal / Really Slow Mods; 5s)",
|
||||
10000: "Extra Large (10s)",
|
||||
15000: "Extremely Large (15s)",
|
||||
20000: "Mods Asleep; Delay Chat (20s)",
|
||||
30000: "Half a Minute (30s)",
|
||||
60000: "Why??? (1m)"
|
||||
},
|
||||
value: 0,
|
||||
|
||||
|
@ -316,6 +326,25 @@ FFZ.settings_info.visible_rooms = {
|
|||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype.refresh_chat = function() {
|
||||
var parents, lines = jQuery('ul.chat-lines');
|
||||
if ( this.has_bttv || ! lines || ! lines.length )
|
||||
return;
|
||||
|
||||
parents = lines.parents('.chatReplay');
|
||||
if ( parents && parents.length )
|
||||
return;
|
||||
|
||||
// There are chat-lines in the DOM and they aren't chat replay.
|
||||
var controller = App.__container__.lookup('controller:chat');
|
||||
if ( ! controller )
|
||||
return;
|
||||
|
||||
var current_room = controller.get("currentRoom");
|
||||
controller.blurRoom();
|
||||
controller.focusRoom(current_room);
|
||||
}
|
||||
|
||||
FFZ.prototype.setup_chatview = function() {
|
||||
document.body.classList.toggle("ffz-minimal-chat-head", this.settings.minimal_chat === 1 || this.settings.minimal_chat === 3);
|
||||
document.body.classList.toggle("ffz-minimal-chat-input", this.settings.minimal_chat === 2 || this.settings.minimal_chat === 3);
|
||||
|
@ -423,7 +452,7 @@ FFZ.prototype.setup_chatview = function() {
|
|||
} catch(err) { }
|
||||
|
||||
// Modify all existing Chat views.
|
||||
var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views;
|
||||
var views = window.App && App.__container__.lookup('-view-registry:main');
|
||||
for(var key in views) {
|
||||
if ( ! views.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
@ -434,14 +463,18 @@ FFZ.prototype.setup_chatview = function() {
|
|||
|
||||
this.log("Manually updating existing Chat view.", view);
|
||||
try {
|
||||
if ( ! view.ffzInit )
|
||||
this._modify_cview(view);
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("setup: build_ui_link: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
/*this.log("Hooking the Ember chat-right-column Component.");
|
||||
var Column = App.__container__.resolve('component:')
|
||||
|
||||
this.log("Hooking the Ember 'Right Column' controller. Seriously...");
|
||||
/*this.log("Hooking the Ember 'Right Column' controller. Seriously...");
|
||||
var Column = App.__container__.lookup('controller:right-column');
|
||||
if ( ! Column )
|
||||
return;
|
||||
|
@ -454,7 +487,7 @@ FFZ.prototype.setup_chatview = function() {
|
|||
},0);
|
||||
}
|
||||
}.observes("firstTabSelected")
|
||||
});
|
||||
});*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -487,10 +520,11 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
|
||||
ffzInit: function() {
|
||||
f._chatv = this;
|
||||
this.ffz_unread = {};
|
||||
|
||||
this.$('.textarea-contain').append(f.build_ui_link(this));
|
||||
this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
this.ffz_unread = {};
|
||||
this.$('.chat-messages').find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
if ( ! f.has_bttv ) {
|
||||
if ( f.settings.group_tabs )
|
||||
|
@ -556,13 +590,19 @@ FFZ.prototype._modify_cview = function(view) {
|
|||
ffzChangeRoom: Ember.observer('controller.currentRoom', function() {
|
||||
f.update_ui_link();
|
||||
|
||||
if ( ! this.ffz_unread )
|
||||
f.log("!! Chat View Not Initialized !!", this);
|
||||
|
||||
// Temporary fix
|
||||
this.ffz_unread = this.ffz_unread || {};
|
||||
|
||||
// Close mod cards when changing to a new room.
|
||||
if ( f._mod_card )
|
||||
f._mod_card.send('close');
|
||||
|
||||
var room = this.get('controller.currentRoom'),
|
||||
room_id = room && room.get('id'),
|
||||
was_unread = room_id && this.ffz_unread[room_id],
|
||||
was_unread = room_id && this.ffz_unread && this.ffz_unread[room_id],
|
||||
update_height = false;
|
||||
|
||||
if ( room ) {
|
||||
|
@ -1209,7 +1249,7 @@ FFZ.prototype.connect_extra_chat = function() {
|
|||
|
||||
|
||||
FFZ.prototype.disconnect_extra_chat = function() {
|
||||
var Chat = App.__container__.lookup('controller:chat'),
|
||||
var Chat = window.App && App.__container__.lookup('controller:chat'),
|
||||
current_channel_id = Chat && Chat.get('currentChannelRoom.id'),
|
||||
current_id = Chat && Chat.get('currentRoom.id'),
|
||||
user = this.get_user();
|
||||
|
|
|
@ -13,7 +13,7 @@ FFZ.settings_info.conv_focus_on_click = {
|
|||
no_mobile: true,
|
||||
visible: false,
|
||||
|
||||
category: "Conversations",
|
||||
category: "Whispers",
|
||||
name: "Focus Input on Click",
|
||||
help: "Focus on a conversation's input box when you click it."
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ FFZ.settings_info.top_conversations = {
|
|||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Conversations",
|
||||
category: "Whispers",
|
||||
name: "Position on Top",
|
||||
help: "Display the new conversation-style whisper UI at the top of the window instead of the bottom.",
|
||||
on_update: function(val) {
|
||||
|
@ -32,12 +32,27 @@ FFZ.settings_info.top_conversations = {
|
|||
};
|
||||
|
||||
|
||||
FFZ.settings_info.minimize_conversations = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
no_mobile: true,
|
||||
|
||||
category: "Whispers",
|
||||
name: "Minimize Whisper UI",
|
||||
help: "Slide the Whisper UI mostly out of view when it's not being used and you have no unread messages.",
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-minimize-conversations', val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ---------------
|
||||
// Initialization
|
||||
// ---------------
|
||||
|
||||
FFZ.prototype.setup_conversations = function() {
|
||||
document.body.classList.toggle('ffz-top-conversations', this.settings.top_conversations);
|
||||
document.body.classList.toggle('ffz-minimize-conversations', this.settings.minimize_conversations);
|
||||
|
||||
this.log("Hooking the Ember Conversation Window component.");
|
||||
var ConvWindow = App.__container__.resolve('component:conversation-window');
|
||||
|
@ -52,6 +67,7 @@ FFZ.prototype.setup_conversations = function() {
|
|||
|
||||
// TODO: Make this better later.
|
||||
jQuery('.conversations-list').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery('.conversations-list').find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
|
||||
|
@ -62,8 +78,7 @@ FFZ.prototype._modify_conversation_window = function(component) {
|
|||
|
||||
component.reopen({
|
||||
headerBadges: Ember.computed("thread.participants", "currentUsername", function() {
|
||||
var e = this.get("thread.participants").rejectBy("username", this.get("currentUsername")).objectAt(0),
|
||||
|
||||
var e = this.get("otherUser"),
|
||||
badges = f.get_other_badges(e.get('username'), null, e.get('userType'), false, e.get('hasTurbo')),
|
||||
out = [];
|
||||
|
||||
|
@ -97,6 +112,7 @@ FFZ.prototype._modify_conversation_window = function(component) {
|
|||
|
||||
jQuery('.badge', el).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(el).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(el).find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -120,7 +136,7 @@ FFZ.prototype._modify_conversation_line = function(component) {
|
|||
|
||||
click: function(e) {
|
||||
if ( e.target && e.target.classList.contains('deleted-link') )
|
||||
return f._deleted_link_click.bind(e.target)(e);
|
||||
return f._deleted_link_click.call(e.target, e);
|
||||
|
||||
if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
@ -133,7 +149,9 @@ FFZ.prototype._modify_conversation_line = function(component) {
|
|||
raw_color = this.get('message.from.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch;
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || f.settings.dark_twitch,
|
||||
myself = f.get_user(),
|
||||
from_me = myself && myself.login === user;
|
||||
|
||||
e.push('<div class="indicator"></div>');
|
||||
|
||||
|
@ -155,7 +173,7 @@ FFZ.prototype._modify_conversation_line = function(component) {
|
|||
}
|
||||
|
||||
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true, f.settings.filter_whispered_links && !from_me));
|
||||
e.push('</span>');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
utils = require('../utils'),
|
||||
constants = require('../constants'),
|
||||
|
||||
NO_LOGO = "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
NO_LOGO = "//static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
|
||||
|
||||
|
||||
// --------------------
|
||||
|
@ -38,6 +38,23 @@ FFZ.settings_info.sidebar_followed_games = {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.settings_info.sidebar_hide_recommended_channels = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Appearance",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Sidebar Recommended Channels",
|
||||
help: "Display the Recommended Channels section on the sidebar.",
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-hide-recommended-channels', !val);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.directory_creative_all_tags = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -145,9 +162,12 @@ FFZ.settings_info.directory_host_menus = {
|
|||
// Initialization
|
||||
// --------------------
|
||||
|
||||
FFZ._image_cache = {};
|
||||
|
||||
FFZ.prototype.setup_directory = function() {
|
||||
document.body.classList.toggle('ffz-creative-tags', this.settings.directory_creative_all_tags);
|
||||
document.body.classList.toggle('ffz-creative-showcase', this.settings.directory_creative_showcase);
|
||||
document.body.classList.toggle('ffz-hide-recommended-channels', !this.settings.sidebar_hide_recommended_channels);
|
||||
|
||||
var GamesFollowing = App.__container__.lookup('controller:games-following'),
|
||||
f = this;
|
||||
|
@ -235,6 +255,8 @@ FFZ.prototype._modify_following = function() {
|
|||
offset: this.get('content.length') + this.get('ffz_skipped')
|
||||
};
|
||||
|
||||
// Don't use FFZ's Client ID because loading hosts is a normal part
|
||||
// of the dashboard. We're just manipulating the logic a bit.
|
||||
return Twitch.api.get("/api/users/:login/followed/hosting", t);
|
||||
},
|
||||
|
||||
|
@ -304,8 +326,10 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
var el = this.get('element'),
|
||||
meta = el && el.querySelector('.meta'),
|
||||
thumb = el && el.querySelector('.thumb'),
|
||||
cap = thumb && thumb.querySelector('.cap');
|
||||
cap = thumb && thumb.querySelector('.cap'),
|
||||
channel_id = this.get('context.model.channel.name');
|
||||
|
||||
el.setAttribute('data-channel', channel_id);
|
||||
|
||||
// CSGO doesn't provide the actual uptime information...
|
||||
if ( !is_csgo && f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
|
||||
|
@ -319,13 +343,15 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
this.ffzUpdateUptime();
|
||||
}
|
||||
|
||||
this._ffz_image_timer = setInterval(this.ffzRotateImage.bind(this), 30000);
|
||||
this.ffzRotateImage();
|
||||
|
||||
if ( f.settings.directory_logos ) {
|
||||
el.classList.add('ffz-directory-logo');
|
||||
|
||||
var link = document.createElement('a'),
|
||||
logo = document.createElement('img'),
|
||||
t = this,
|
||||
target = this.get('context.model.channel.name');
|
||||
t = this;
|
||||
|
||||
logo.className = 'profile-photo';
|
||||
logo.classList.toggle('is-csgo', is_csgo);
|
||||
|
@ -333,14 +359,17 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
logo.src = this.get('context.model.channel.logo') || NO_LOGO;
|
||||
logo.alt = this.get('context.model.channel.display_name');
|
||||
|
||||
link.href = '/' + target;
|
||||
link.href = '/' + channel_id;
|
||||
link.addEventListener('click', function(e) {
|
||||
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
|
||||
return;
|
||||
|
||||
var Channel = App.__container__.resolve('model:channel');
|
||||
if ( ! Channel )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
t.get('controller').transitionTo('channel.index', Channel.find({id: target}).load());
|
||||
t.get('controller').transitionTo('channel.index', Channel.find({id: channel_id}).load());
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -358,9 +387,23 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
|
|||
if ( this._ffz_uptime_timer )
|
||||
clearInterval(this._ffz_uptime_timer);
|
||||
|
||||
if ( this._ffz_image_timer )
|
||||
clearInterval(this._ffz_image_timer);
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
ffzRotateImage: function() {
|
||||
var url = this.get('context.model.preview.medium'),
|
||||
now = Math.round((new Date).getTime() / 150000);
|
||||
|
||||
if ( FFZ._image_cache[url] && FFZ._image_cache[url] !== now )
|
||||
url += '?_=' + now;
|
||||
else
|
||||
FFZ._image_cache[url] = now;
|
||||
|
||||
this.$('.thumb .cap img').attr('src', url);
|
||||
},
|
||||
|
||||
ffzUpdateUptime: function() {
|
||||
var raw_created = this.get('context.model.created_at'),
|
||||
|
@ -411,7 +454,7 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
return;
|
||||
|
||||
if ( e ) {
|
||||
if ( e.button !== 0 )
|
||||
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
|
@ -424,7 +467,7 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
},
|
||||
|
||||
ffzShowHostMenu: function(e) {
|
||||
if ( e.button !== 0 )
|
||||
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
|
@ -485,10 +528,25 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
f.show_popup(menu, [x, y], document.querySelector('#main_col > .tse-scroll-content > .tse-content'));
|
||||
},
|
||||
|
||||
ffzRotateImage: function() {
|
||||
var url = this.get('context.model.target.preview'),
|
||||
now = Math.round((new Date).getTime() / 150000);
|
||||
|
||||
if ( FFZ._image_cache[url] && FFZ._image_cache[url] !== now )
|
||||
url += '?_=' + now;
|
||||
else
|
||||
FFZ._image_cache[url] = now;
|
||||
|
||||
this.$('.thumb .cap img').attr('src', url);
|
||||
},
|
||||
|
||||
ffzCleanup: function() {
|
||||
var target = this.get('context.model.target.channel');
|
||||
if ( f._popup && f._popup.classList.contains('ffz-channel-selector') && f._popup.getAttribute('data-channel') === target )
|
||||
f.close_popup();
|
||||
|
||||
if ( this._ffz_image_timer )
|
||||
clearInterval(this._ffz_image_timer);
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
|
@ -503,6 +561,10 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
|
||||
boxart = thumb && thumb.querySelector('.boxart');
|
||||
|
||||
el.setAttribute('data-channel', target.name);
|
||||
|
||||
this._ffz_image_timer = setInterval(this.ffzRotateImage.bind(this), 30000);
|
||||
this.ffzRotateImage();
|
||||
|
||||
// Fix the game not showing
|
||||
if ( ! boxart && thumb && this.get('context.model.game') ) {
|
||||
|
@ -516,6 +578,9 @@ FFZ.prototype._modify_directory_host = function(dir) {
|
|||
boxart.setAttribute('original-title', game);
|
||||
|
||||
boxart.addEventListener('click', function(e) {
|
||||
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
jQuery('.tipsy').remove();
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ FFZ.settings_info.enhance_profile_following = {
|
|||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Appearance",
|
||||
category: "Directory",
|
||||
name: "Enhanced Following Control",
|
||||
help: "Display additional controls on your own profile's Following tab to make management easier."
|
||||
}
|
||||
|
@ -33,9 +33,7 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
// First, we need to hook the model. This is what we'll use to grab the following notification state,
|
||||
// rather than making potentially hundreds of API requests.
|
||||
var Following = App.__container__.resolve('model:kraken-channel-following');
|
||||
if ( ! Following )
|
||||
return;
|
||||
|
||||
if ( Following )
|
||||
this._hook_following(Following);
|
||||
|
||||
// Also try hooking that other model.
|
||||
|
@ -71,7 +69,7 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
ffzInit: function() {
|
||||
// Only process our own profile following page.
|
||||
var user = f.get_user();
|
||||
if ( ! f.settings.enhance_profile_following || ! user || user.login !== this.get('context.id') )
|
||||
if ( ! f.settings.enhance_profile_following || ! user || user.login !== this.get('context.model.id') )
|
||||
return;
|
||||
|
||||
var el = this.get('element'),
|
||||
|
@ -99,6 +97,9 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
following.load();
|
||||
}
|
||||
|
||||
// Make sure the Following is modified.
|
||||
f._hook_following(following);
|
||||
|
||||
// We use this weird function to prevent trying to load twice mucking things up.
|
||||
setTimeout(refresher);
|
||||
}
|
||||
|
@ -118,14 +119,15 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
continue;
|
||||
|
||||
// Is it an ember-view? Check its kids.
|
||||
if ( added.classList.contains('ember-view') ) {
|
||||
if ( added.classList.contains('user') )
|
||||
t.ffzProcessUser(added);
|
||||
|
||||
else if ( added.classList.contains('ember-view') ) {
|
||||
var users = added.querySelectorAll('.user.item');
|
||||
if ( users )
|
||||
for(var y=0; y < users.length; y++)
|
||||
t.ffzProcessUser(users[y]);
|
||||
|
||||
} else if ( added.classList.contains('user') )
|
||||
t.ffzProcessUser(added);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -215,8 +217,8 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
follow.textContent = 'Updating';
|
||||
|
||||
(was_following ?
|
||||
Twitch.api.del("users/:login/follows/channels/" + user_id) :
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
|
||||
utils.api.del("users/:login/follows/channels/" + user_id) :
|
||||
utils.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
|
||||
.done(function() {
|
||||
data = f._following_cache[user_id] = was_following ? null : [new Date(Date.now() - (f._ws_server_offset||0)), false];
|
||||
})
|
||||
|
@ -235,7 +237,7 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
notif.disabled = true;
|
||||
notif.textContent = 'Updating';
|
||||
|
||||
Twitch.api.put("users/:login/follows/channels/" + user_id, {notifications: !was_following})
|
||||
utils.api.put("users/:login/follows/channels/" + user_id, {notifications: !was_following})
|
||||
.done(function() {
|
||||
data[1] = ! was_following;
|
||||
})
|
||||
|
@ -254,25 +256,89 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
}
|
||||
});
|
||||
|
||||
/*// Try adding a nice Manage button to the Following page of the directory
|
||||
var DirectoryFollowing = App.__container__.resolve('view:directory');
|
||||
if ( DirectoryFollowing ) {
|
||||
DirectoryFollowing.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("view:following ffzInit: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
willClearRender: function() {
|
||||
try {
|
||||
this.ffzTeardown();
|
||||
} catch(err) {
|
||||
f.error("view:following ffzTeardown: " + err);
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
f.log("view:directory ffzInit called!", this);
|
||||
|
||||
var el = this.get('element'),
|
||||
nav_bar = el && el.querySelector('.directory_header ul.nav'),
|
||||
user = f.get_user();
|
||||
|
||||
if ( ! nav_bar || ! user || ! user.login || ! f.settings.enhance_profile_following )
|
||||
return;
|
||||
|
||||
var li = document.createElement('li'),
|
||||
btn = document.createElement('a'),
|
||||
t = this;
|
||||
|
||||
li.className = 'ffz-manage-following';
|
||||
btn.innerHTML = 'Manage Following';
|
||||
btn.href = '/' + user.login + '/profile/following';
|
||||
|
||||
btn.addEventListener('click', function(e) {
|
||||
var Channel = App.__container__.resolve('model:channel');
|
||||
if ( ! Channel )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
t.get('controller').transitionTo('profile.following', Channel.find({id: user.login}).load());
|
||||
return false;
|
||||
});
|
||||
|
||||
li.appendChild(btn);
|
||||
nav_bar.appendChild(li);
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
this.$('.ffz-manage-following').remove();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
DirectoryFollowing.create().destroy();
|
||||
} catch(err) { }
|
||||
} else
|
||||
f.log("Could not find Directory Following View.");*/
|
||||
|
||||
// Now, rebuild any views.
|
||||
try {
|
||||
ProfileView.create().destroy();
|
||||
} catch(err) { }
|
||||
|
||||
var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views;
|
||||
var views = window.App && App.__container__.lookup('-view-registry:main');
|
||||
if ( views )
|
||||
for(var key in views) {
|
||||
var view = views[key];
|
||||
if ( ! view || !(view instanceof ProfileView) )
|
||||
continue;
|
||||
|
||||
this.log("Manually updating existing Following View.", view);
|
||||
/*if ( DirectoryFollowing && view instanceof DirectoryFollowing ) {
|
||||
try {
|
||||
var following = view.get('context.following');
|
||||
this._hook_following(following);
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("setup: view:channel/following: model hook: " + err);
|
||||
this.error("setup: view:following: ffzInit: " + err);
|
||||
}
|
||||
|
||||
} else*/ if ( view instanceof ProfileView ) {
|
||||
this.log("Manually updating existing Following View.", view);
|
||||
try {
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
|
@ -280,11 +346,16 @@ FFZ.prototype.setup_profile_following = function() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._hook_following = function(Following) {
|
||||
var f = this;
|
||||
if ( Following.ffz_hooked )
|
||||
return;
|
||||
|
||||
Following.reopen({
|
||||
ffz_hooked: true,
|
||||
apiLoad: function(e) {
|
||||
var user = f.get_user(),
|
||||
channel_id = this.get('id'),
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var FFZ = window.FrankerFaceZ;
|
||||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils');
|
||||
|
||||
|
||||
// --------------------
|
||||
|
@ -78,11 +79,11 @@ FFZ.settings_info.flip_dashboard = {
|
|||
type: "boolean",
|
||||
value: false,
|
||||
|
||||
category: "Appearance",
|
||||
category: "Dashboard",
|
||||
no_mobile: true,
|
||||
no_bttv: true,
|
||||
|
||||
name: "Swap Dashboard Positions",
|
||||
name: "Swap Column Positions",
|
||||
help: "Swap the positions of the left and right columns of the dashboard.",
|
||||
|
||||
on_update: function(val) {
|
||||
|
@ -106,17 +107,19 @@ FFZ.settings_info.right_column_width = {
|
|||
help: "Set the width of the right sidebar for chat.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.right_column_width || 340,
|
||||
new_val = prompt("Right Sidebar Width\n\nPlease enter a new width for the right sidebar, in pixels. Minimum: 250, Default: 340", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.right_column_width || 340;
|
||||
|
||||
utils.prompt("Right Sidebar Width", "Please enter a new width for the right sidebar, in pixels.</p><p><b>Minimum:</b> 250<br><b>Default:</b> 340", old_val, function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
var width = parseInt(new_val);
|
||||
if ( ! width || width === NaN )
|
||||
if ( ! width || Number.isNaN(width) || ! Number.isFinite(width) )
|
||||
width = 340;
|
||||
|
||||
this.settings.set('right_column_width', Math.max(250, width));
|
||||
f.settings.set('right_column_width', Math.max(250, width));
|
||||
});
|
||||
},
|
||||
|
||||
on_update: function(val) {
|
||||
|
@ -220,7 +223,7 @@ FFZ.prototype.setup_layout = function() {
|
|||
height = size[1],
|
||||
host_height = size[2];
|
||||
|
||||
return "<style>.dynamic-player, .dynamic-player object, .dynamic-player video{width:" + width + "px !important;height:" + height + "px !important} .dynamic-target-player,.dynamic-target-player object, .dynamic-target-player video{width:" + width + "px !important;height:" + host_height + "px !important}</style><style>.dynamic-player .player object{width:100% !important; height:100% !important}</style>";
|
||||
return "<style>.dynamic-player, .dynamic-player object, .dynamic-player video{width:" + width + "px !important;height:" + height + "px !important} .dynamic-target-player,.dynamic-target-player object, .dynamic-target-player video{width:" + width + "px !important;height:" + host_height + "px !important}</style><style>.dynamic-player .player object, .dynamic-player .player video{width:100% !important; height:100% !important}</style>";
|
||||
}.property("playerSize"),
|
||||
|
||||
ffzPortraitWarning: function() {
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require("../utils"),
|
||||
constants = require("../constants"),
|
||||
|
||||
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 + "*");
|
||||
constants = require("../constants");
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Settings
|
||||
// ---------------------
|
||||
|
||||
FFZ.settings_info.alias_italics = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Chat Appearance",
|
||||
no_bttv: true,
|
||||
|
||||
name: "Display Aliases in Italics",
|
||||
help: "Format the names of users that have aliases with italics to make it obvious at a glance that they have been renamed.",
|
||||
|
||||
on_update: function(val) {
|
||||
document.body.classList.toggle('ffz-alias-italics', val);
|
||||
}
|
||||
};
|
||||
|
||||
FFZ.settings_info.room_status = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
@ -32,7 +44,7 @@ FFZ.settings_info.replace_bad_emotes = {
|
|||
value: true,
|
||||
|
||||
category: "Chat Appearance",
|
||||
no_bttv: true,
|
||||
warn_bttv: "Only affects Whispers when BetterTTV is enabled.",
|
||||
|
||||
name: "Fix Low Quality Twitch Global Emoticons",
|
||||
help: "Replace emoticons such as DansGame and RedCoat with cleaned up versions that don't have pixels around the edges or white backgrounds for nicer display on dark chat."
|
||||
|
@ -44,7 +56,8 @@ FFZ.settings_info.parse_emoji = {
|
|||
options: {
|
||||
0: "No Images / Font Only",
|
||||
1: "Twitter Emoji Images",
|
||||
2: "Google Noto Images"
|
||||
2: "Google Noto Images",
|
||||
3: "EmojiOne Images"
|
||||
},
|
||||
|
||||
value: 1,
|
||||
|
@ -66,23 +79,6 @@ FFZ.settings_info.parse_emoji = {
|
|||
};
|
||||
|
||||
|
||||
FFZ.settings_info.room_status = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Chat Appearance",
|
||||
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,
|
||||
|
@ -94,27 +90,32 @@ FFZ.settings_info.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);
|
||||
var f = this;
|
||||
utils.prompt(
|
||||
"Scrollback Length",
|
||||
"Please enter a new maximum length for the chat scrollback. Please note that setting this too high may cause your computer to begin lagging as chat messages accumulate.</p><p><b>Default:</b> 150",
|
||||
this.settings.scrollback_length,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
new_val = parseInt(new_val);
|
||||
if ( new_val === NaN )
|
||||
return;
|
||||
if ( Number.isNaN(new_val) || ! Number.isFinite(new_val) )
|
||||
new_val = 150;
|
||||
|
||||
if ( new_val < 10 )
|
||||
new_val = 10;
|
||||
new_val = Math.max(10, new_val);
|
||||
|
||||
this.settings.set("scrollback_length", new_val);
|
||||
f.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 && room.room.set('messageBufferSize', new_val + ((this._roomv && !this._roomv.get('stuckToBottom') && current_id === room_id) ? 150 : 0));
|
||||
for(var room_id in f.rooms) {
|
||||
var room = f.rooms[room_id];
|
||||
room.room && room.room.set('messageBufferSize', new_val + ((f._roomv && !f._roomv.get('stuckToBottom') && current_id === room_id) ? 150 : 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -127,19 +128,19 @@ FFZ.settings_info.hosted_sub_notices = {
|
|||
no_bttv: true,
|
||||
|
||||
name: "Show Hosted Channel Subscriber Notices",
|
||||
help: "Display notices in chat when someone subscribes to the hosted channel."
|
||||
help: "Display (or more specifically <i>hides</i> when disabled) notices in chat when someone subscribes to the hosted channel."
|
||||
};
|
||||
|
||||
|
||||
FFZ.settings_info.filter_bad_shorteners = {
|
||||
FFZ.settings_info.filter_whispered_links = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Chat Filtering",
|
||||
no_bttv: true,
|
||||
warn_bttv: "Only affects Whispers when BetterTTV is enabled.",
|
||||
|
||||
name: "Auto-Hide Potentially Dangerous Shortened Links",
|
||||
help: "Replace potentially dangerous shortened links. Links are still accessible, but require an extra click to access."
|
||||
name: "Auto-Hide Potentially Dangerous Whispered Links",
|
||||
help: "Removes whispered links and displays a placeholder, with a warning that the link has not been approved by moderation or staff. Links remain accessible with an additional click."
|
||||
};
|
||||
|
||||
|
||||
|
@ -148,19 +149,25 @@ FFZ.settings_info.banned_words = {
|
|||
value: [],
|
||||
|
||||
category: "Chat Filtering",
|
||||
no_bttv: true,
|
||||
|
||||
warn_bttv: "Only affects Whispers when BetterTTV is enabled.",
|
||||
|
||||
name: "Banned Words",
|
||||
help: "Set a list of words that will be locally removed from chat messages.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.banned_words.join(", "),
|
||||
new_val = prompt("Banned Words\n\nPlease enter a comma-separated list of words that you would like to be removed from chat messages.", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.banned_words.join(", ");
|
||||
|
||||
utils.prompt(
|
||||
"Banned Words",
|
||||
"Please enter a comma-separated list of words that you would like to have removed from chat messages.",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
new_val = new_val.trim().split(SPLITTER);
|
||||
new_val = new_val.trim().split(constants.SPLITTER);
|
||||
var vals = [];
|
||||
|
||||
for(var i=0; i < new_val.length; i++)
|
||||
|
@ -169,7 +176,8 @@ FFZ.settings_info.banned_words = {
|
|||
if ( vals.length == 1 && vals[0] == "disable" )
|
||||
vals = [];
|
||||
|
||||
this.settings.set("banned_words", vals);
|
||||
f.settings.set("banned_words", vals);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -179,20 +187,26 @@ FFZ.settings_info.keywords = {
|
|||
value: [],
|
||||
|
||||
category: "Chat Filtering",
|
||||
no_bttv: true,
|
||||
|
||||
warn_bttv: "Only affects Whispers when BetterTTV is enabled.",
|
||||
|
||||
name: "Highlight Keywords",
|
||||
help: "Set additional keywords that will be highlighted in chat.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.keywords.join(", "),
|
||||
new_val = prompt("Highlight Keywords\n\nPlease enter a comma-separated list of words that you would like to be highlighted in chat.", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.keywords.join(", ");
|
||||
|
||||
utils.prompt(
|
||||
"Highlight Keywords",
|
||||
"Please enter a comma-separated list of words that you would like to be highlighted in chat.",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
// Split them up.
|
||||
new_val = new_val.trim().split(SPLITTER);
|
||||
new_val = new_val.trim().split(constants.SPLITTER);
|
||||
var vals = [];
|
||||
|
||||
for(var i=0; i < new_val.length; i++)
|
||||
|
@ -201,7 +215,8 @@ FFZ.settings_info.keywords = {
|
|||
if ( vals.length == 1 && vals[0] == "disable" )
|
||||
vals = [];
|
||||
|
||||
this.settings.set("keywords", vals);
|
||||
f.settings.set("keywords", vals);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -211,7 +226,7 @@ FFZ.settings_info.clickable_emoticons = {
|
|||
value: false,
|
||||
|
||||
category: "Chat Tooltips",
|
||||
no_bttv: true,
|
||||
warn_bttv: "Only affects Whispers when BetterTTV is enabled.",
|
||||
no_mobile: true,
|
||||
|
||||
name: "Emoticon Information Pages",
|
||||
|
@ -388,9 +403,14 @@ FFZ.settings_info.chat_font_family = {
|
|||
help: "Change the font used for rendering chat messages.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.chat_font_family || "",
|
||||
new_val = prompt("Chat Font Family\n\nPlease enter a font family to use rendering chat. Leave this blank to use the default.", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.chat_font_family || "";
|
||||
|
||||
utils.prompt(
|
||||
"Chat Font Family",
|
||||
"Please enter a font family to use rendering chat. Leave this blank to use the default.",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
|
@ -398,7 +418,8 @@ FFZ.settings_info.chat_font_family = {
|
|||
if ( ! new_val )
|
||||
new_val = null;
|
||||
|
||||
this.settings.set("chat_font_family", new_val);
|
||||
f.settings.set("chat_font_family", new_val);
|
||||
});
|
||||
},
|
||||
|
||||
on_update: function(val) {
|
||||
|
@ -434,17 +455,23 @@ FFZ.settings_info.chat_font_size = {
|
|||
help: "Make the chat font bigger or smaller.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.chat_font_size,
|
||||
new_val = prompt("Chat Font Size\n\nPlease enter a new size for the chat font. The default is 12.", old_val);
|
||||
var f = this,
|
||||
old_val = this.settings.chat_font_size;
|
||||
|
||||
utils.prompt(
|
||||
"Chat Font Size",
|
||||
"Please enter a new size for the chat font.</p><p><b>Default:</b> 12",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
var parsed = parseInt(new_val);
|
||||
if ( ! parsed || parsed === NaN || parsed < 1 )
|
||||
if ( ! parsed || Number.isNaN(parsed) || parsed < 1 )
|
||||
parsed = 12;
|
||||
|
||||
this.settings.set("chat_font_size", parsed);
|
||||
f.settings.set("chat_font_size", parsed);
|
||||
});
|
||||
},
|
||||
|
||||
on_update: function(val) {
|
||||
|
@ -463,7 +490,7 @@ FFZ.settings_info.chat_font_size = {
|
|||
}
|
||||
|
||||
utils.update_css(this._chat_style, "chat_font_size", css);
|
||||
FFZ.settings_info.chat_ts_size.on_update.bind(this)(this.settings.chat_ts_size);
|
||||
FFZ.settings_info.chat_ts_size.on_update.call(this, this.settings.chat_ts_size);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -479,21 +506,26 @@ FFZ.settings_info.chat_ts_size = {
|
|||
help: "Make the chat timestamp font bigger or smaller.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.chat_ts_size;
|
||||
var f = this,
|
||||
old_val = this.settings.chat_ts_size;
|
||||
|
||||
if ( ! old_val )
|
||||
old_val = this.settings.chat_font_size;
|
||||
|
||||
var new_val = prompt("Chat Timestamp Font Size\n\nPlease enter a new size for the chat timestamp font. The default is to match the regular chat font size.", old_val);
|
||||
|
||||
utils.prompt(
|
||||
"Chat Timestamp Font Size",
|
||||
"Please enter a new size for the chat timestamp font. The default is to match the regular chat font size.",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
var parsed = parseInt(new_val);
|
||||
if ( ! parsed || parsed === NaN || parsed < 1 )
|
||||
if ( parsed < 1 || Number.isNaN(parsed) || ! Number.isFinite(parsed) )
|
||||
parsed = null;
|
||||
|
||||
this.settings.set("chat_ts_size", parsed);
|
||||
f.settings.set("chat_ts_size", parsed);
|
||||
});
|
||||
},
|
||||
|
||||
on_update: function(val) {
|
||||
|
@ -539,11 +571,13 @@ FFZ.prototype.setup_line = function() {
|
|||
document.head.appendChild(s);
|
||||
|
||||
// Initial calculation.
|
||||
FFZ.settings_info.chat_font_size.on_update.bind(this)(this.settings.chat_font_size);
|
||||
FFZ.settings_info.chat_font_family.on_update.bind(this)(this.settings.chat_font_family);
|
||||
FFZ.settings_info.chat_font_size.on_update.call(this, this.settings.chat_font_size);
|
||||
FFZ.settings_info.chat_font_family.on_update.call(this, this.settings.chat_font_family);
|
||||
|
||||
|
||||
// Chat Enhancements
|
||||
document.body.classList.toggle('ffz-alias-italics', this.settings.alias_italics);
|
||||
|
||||
this.toggle_style('chat-setup', !this.has_bttv && (this.settings.chat_rows || this.settings.chat_separators));
|
||||
this.toggle_style('chat-padding', !this.has_bttv && this.settings.chat_padding);
|
||||
|
||||
|
@ -560,17 +594,30 @@ FFZ.prototype.setup_line = function() {
|
|||
|
||||
this._last_row = {};
|
||||
|
||||
this.log("Hooking the Ember Whisper Line component.");
|
||||
this.log("Hooking the Ember Chat Line component.");
|
||||
var Line = App.__container__.resolve('component:chat-line');
|
||||
|
||||
if ( Line )
|
||||
this._modify_chat_line(Line);
|
||||
|
||||
/*this.log("Hooking the Ember Whisper Line component.");
|
||||
var Whisper = App.__container__.resolve('component:whisper-line');
|
||||
|
||||
if ( Whisper )
|
||||
this._modify_line(Whisper);
|
||||
|
||||
this.log("Hooking the Ember Message Line component.");
|
||||
var Line = App.__container__.resolve('component:message-line');
|
||||
var Message = App.__container__.resolve('component:message-line');
|
||||
|
||||
if ( Line )
|
||||
this._modify_line(Line);
|
||||
if ( Message )
|
||||
this._modify_line(Message);*/
|
||||
|
||||
this.log("Hooking the Ember VOD Chat Line component.");
|
||||
var VOD = App.__container__.resolve('component:vod-chat-line');
|
||||
if ( VOD )
|
||||
this._modify_vod_line(VOD);
|
||||
else
|
||||
this.log("Couldn't find VOD Chat Line component.");
|
||||
|
||||
// Store the capitalization of our own name.
|
||||
var user = this.get_user();
|
||||
|
@ -585,13 +632,11 @@ FFZ.prototype.save_aliases = function() {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_line = function(component) {
|
||||
FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
|
||||
component.reopen({
|
||||
tokenizedMessage: function() {
|
||||
try {
|
||||
|
@ -600,13 +645,336 @@ FFZ.prototype._modify_line = function(component) {
|
|||
f.error("chat-line tokenizedMessage: " + err);
|
||||
return this._super();
|
||||
}
|
||||
|
||||
}.property("msgObject.message", "isChannelLinksDisabled", "currentUserNick", "msgObject.from", "msgObject.tags.emotes"),
|
||||
|
||||
ffzUpdated: Ember.observer("msgObject.ffz_deleted", "msgObject.ffz_old_messages", function() {
|
||||
this.rerender();
|
||||
lineChanged: Ember.observer("msgObject.deleted", "isModeratorOrHigher", "msgObject.ffz_old_messages", function() {
|
||||
this.$(".mod-icons").replaceWith(this.buildModIconsHTML());
|
||||
if ( this.get("msgObject.deleted") ) {
|
||||
this.$(".message").replaceWith(this.buildDeletedMessageHTML());
|
||||
} else
|
||||
this.$(".deleted,.message").replaceWith(this.buildMessageHTML());
|
||||
}),
|
||||
|
||||
ffzUserLevel: function() {
|
||||
if ( this.get('isStaff') )
|
||||
return 5;
|
||||
else if ( this.get('isAdmin') )
|
||||
return 4;
|
||||
else if ( this.get('isBroadcaster') )
|
||||
return 3;
|
||||
else if ( this.get('isGlobalMod') )
|
||||
return 2;
|
||||
else if ( this.get('isModerator') )
|
||||
return 1;
|
||||
return 0;
|
||||
}.property('msgObject.labels.[]'),
|
||||
|
||||
buildModIconsHTML: function() {
|
||||
var user = this.get('msgObject.from'),
|
||||
room_id = this.get('msgObject.room'),
|
||||
room = f.rooms && f.rooms[room_id],
|
||||
|
||||
deleted = this.get('msgObject.deleted'),
|
||||
|
||||
recipient = this.get('msgObject.to'),
|
||||
is_whisper = recipient && recipient.length,
|
||||
|
||||
this_ul = this.get('ffzUserLevel'),
|
||||
other_ul = room && room.room && room.room.get('ffzUserLevel') || 0,
|
||||
|
||||
output;
|
||||
|
||||
if ( is_whisper || this_ul >= other_ul || f.settings.mod_buttons.length === 0 )
|
||||
return '';
|
||||
|
||||
output = '<span class="mod-icons float-left">';
|
||||
|
||||
for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
|
||||
var pair = f.settings.mod_buttons[i],
|
||||
prefix = pair[0], btn = pair[1],
|
||||
|
||||
cmd, tip;
|
||||
|
||||
if ( btn === false ) {
|
||||
if ( deleted )
|
||||
output += '<a class="mod-icon float-left tooltip unban" title="Unban User" href="#">Unban</a>';
|
||||
else
|
||||
output += '<a class="mod-icon float-left tooltip ban" title="Ban User" href="#">Ban</a>';
|
||||
|
||||
} else if ( btn === 600 )
|
||||
output += '<a class="mod-icon float-left tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>';
|
||||
|
||||
else {
|
||||
if ( typeof btn === "string" ) {
|
||||
cmd = btn.replace(/{user}/g, user).replace(/ *<LINE> */, "\n");
|
||||
tip = "Custom Command" + (cmd.indexOf("\n") !== -1 ? 's' : '') + '\n' + cmd;
|
||||
} else {
|
||||
cmd = "/timeout " + user + " " + btn;
|
||||
tip = "Timeout User (" + utils.duration_string(btn) + ")";
|
||||
}
|
||||
output += '<a class="mod-icon float-left tooltip' + (cmd.substr(0,9) === '/timeout' ? ' is-timeout' : '') + ' custom" data-cmd="' + utils.quote_attr(cmd) + '" title="' + utils.quote_attr(tip) + '" href="#">' + prefix + '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
return output + '</span>';
|
||||
},
|
||||
|
||||
buildSenderHTML: function() {
|
||||
var user = this.get('msgObject.from'),
|
||||
room_id = this.get('msgObject.room'),
|
||||
room = f.rooms && f.rooms[room_id],
|
||||
|
||||
deleted = this.get('msgObject.deleted'),
|
||||
r = this,
|
||||
|
||||
recipient = this.get('msgObject.to'),
|
||||
is_whisper = recipient && recipient.length,
|
||||
is_replay = this.get('ffz_is_replay'),
|
||||
|
||||
this_ul = this.get('ffzUserLevel'),
|
||||
other_ul = room && room.room && room.room.get('ffzUserLevel') || 0,
|
||||
|
||||
raw_color = this.get('msgObject.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode'))),
|
||||
|
||||
output = '';
|
||||
|
||||
|
||||
output = '<div class="indicator"></div><span class="timestamp float-left">' + this.get('timestamp') + '</span> ';
|
||||
|
||||
// Moderator Actions
|
||||
output += this.buildModIconsHTML();
|
||||
|
||||
// Badges
|
||||
output += '<span class="badges float-left">' + f.render_badges(f.get_line_badges(this.get('msgObject'), is_whisper)) + '</span>';
|
||||
|
||||
// Alias Support
|
||||
var alias = f.aliases[user],
|
||||
name = this.get('msgObject.tags.display-name') || (user && user.capitalize()) || "unknown user",
|
||||
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]) || '',
|
||||
colored = style ? ' has-color' + (is_replay ? ' replay-color' : '') : '';
|
||||
|
||||
output += '<span class="from' + (alias ? ' ffz-alias tooltip' : '') + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '');
|
||||
|
||||
if ( alias )
|
||||
output += '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias);
|
||||
else
|
||||
output += '">' + utils.sanitize(name);
|
||||
|
||||
|
||||
// Whisper Legacy Sucks
|
||||
if ( is_whisper ) {
|
||||
var to_alias = f.aliases[recipient],
|
||||
to_name = this.get('msgObject.tags.recipient-display-name') || (recipient && recipient.capitalize()) || "unknown user",
|
||||
|
||||
to_color = this.get('msgObject.toColor'),
|
||||
to_colors = to_color && f._handle_color(to_color),
|
||||
|
||||
to_style = to_color ? 'color:' + (is_dark ? to_colors[1] : to_colors[0]) : '',
|
||||
to_colored = to_style ? ' has-color' : '';
|
||||
|
||||
output += "</span><svg class='svg-whisper-arrow' height='10px' version='1.1' width='16px'><polyline points='6 2, 10 6, 6 10, 6 2' /></svg>";
|
||||
output += '<span class="to' + (to_alias ? ' ffz-alias tooltip' : '') + to_colored + '" style="' + to_style + (to_colors ? '" data=color="' + to_color : '');
|
||||
|
||||
if ( to_alias )
|
||||
output += '" title="' + utils.sanitize(to_name) + '">' + utils.sanitize(to_alias);
|
||||
else
|
||||
output += '">' + utils.sanitize(to_name);
|
||||
}
|
||||
|
||||
return output + '</span><span class="colon">:</span> ';
|
||||
},
|
||||
|
||||
buildDeletedMessageHTML: function() {
|
||||
return '<span class="deleted"><a class="undelete" href=#"><message deleted></a></span>';
|
||||
},
|
||||
|
||||
buildMessageHTML: function() {
|
||||
var output,
|
||||
recipient = this.get('msgObject.to'),
|
||||
is_whisper = recipient && recipient.length;
|
||||
|
||||
if ( this.get('msgObject.style') === 'action' ) {
|
||||
var raw_color = this.get('msgObject.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
is_replay = this.get('ffz_is_replay'),
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (is_replay ? f.settings.dark_twitch : (Settings && Settings.get('settings.darkMode')));
|
||||
|
||||
if ( raw_color )
|
||||
output = '<span class="message has-color' + (is_replay ? ' replay-color' : '') + '" style="color:' + (is_dark ? colors[1] : colors[0]) + '" data-color="' + raw_color + '">';
|
||||
else
|
||||
output = '<span class="message">';
|
||||
} else
|
||||
output = '<span class="message">';
|
||||
|
||||
output += f.render_tokens(this.get('tokenizedMessage'), true, is_whisper && f.settings.filter_whispered_links && this.get("ffzUserLevel") < 4);
|
||||
|
||||
var old_messages = this.get('msgObject.ffz_old_messages');
|
||||
if ( old_messages && old_messages.length )
|
||||
output += '<div class="button primary float-right ffz-old-messages">Show ' + utils.number_commas(old_messages.length) + ' Old</div>';
|
||||
|
||||
return output + '</span>';
|
||||
},
|
||||
|
||||
tagName: "li",
|
||||
classNameBindings: is_vod ? ["msgObject.ffz_has_mention:ffz-mentioned"] : [":message-line", ":chat-line", "msgObject.style", "msgObject.ffz_has_mention:ffz-mentioned", "ffzWasDeleted:ffz-deleted", "ffzHasOldMessages:clearfix", "ffzHasOldMessages:ffz-has-deleted"],
|
||||
attributeBindings: ["msgObject.room:data-room", "msgObject.from:data-sender", "msgObject.deleted:data-deleted"],
|
||||
|
||||
render: function(e) {
|
||||
e.push(this.buildSenderHTML());
|
||||
if ( this.get("msgObject.deleted") )
|
||||
e.push(this.buildDeletedMessageHTML())
|
||||
else
|
||||
e.push(this.buildMessageHTML());
|
||||
},
|
||||
|
||||
ffzWasDeleted: function() {
|
||||
return f.settings.prevent_clear && this.get("msgObject.ffz_deleted")
|
||||
}.property("msgObject.ffz_deleted"),
|
||||
|
||||
ffzHasOldMessages: function() {
|
||||
var old_messages = this.get("msgObject.ffz_old_messages");
|
||||
return old_messages && old_messages.length;
|
||||
}.property("msgObject.ffz_old_messages"),
|
||||
|
||||
click: function(e) {
|
||||
if ( ! e.target )
|
||||
return;
|
||||
|
||||
var cl = e.target.classList,
|
||||
from = this.get("msgObject.from");
|
||||
|
||||
if ( cl.contains('ffz-old-messages') )
|
||||
return f._show_deleted(this.get('msgObject.room'));
|
||||
|
||||
else if ( cl.contains('deleted-word') ) {
|
||||
jQuery(e.target).trigger('mouseout');
|
||||
e.target.outerHTML = e.target.getAttribute('data-text');
|
||||
|
||||
} else if ( cl.contains('deleted-link') )
|
||||
return f._deleted_link_click.call(e.target, e);
|
||||
|
||||
else if ( cl.contains('mod-icon') ) {
|
||||
jQuery(e.target).trigger('mouseout');
|
||||
e.preventDefault();
|
||||
|
||||
if ( cl.contains('custom') ) {
|
||||
var room_id = this.get('msgObject.room'),
|
||||
room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
|
||||
cmd = e.target.getAttribute('data-cmd');
|
||||
|
||||
if ( room ) {
|
||||
var lines = cmd.split("\n");
|
||||
for(var i=0; i < lines.length; i++)
|
||||
room.send(lines[i], true);
|
||||
|
||||
if ( cl.contains('is-timeout') )
|
||||
room.clearMessages(from);
|
||||
}
|
||||
return;
|
||||
|
||||
} else if ( cl.contains('ban') )
|
||||
this.sendAction("banUser", {user:from});
|
||||
|
||||
else if ( cl.contains('unban') )
|
||||
this.sendAction("unbanUser", {user:from});
|
||||
|
||||
else if ( cl.contains('timeout') )
|
||||
this.sendAction("timeoutUser", {user:from});
|
||||
|
||||
} else if ( cl.contains('badge') ) {
|
||||
if ( cl.contains('turbo') )
|
||||
window.open("/products/turbo?ref=chat_badge", "_blank");
|
||||
|
||||
else if ( cl.contains('subscriber') )
|
||||
this.sendAction("clickSubscriber");
|
||||
|
||||
} else if ( f._click_emote(e.target, e) )
|
||||
return;
|
||||
|
||||
else if ( e.target.classList.contains('from') ) {
|
||||
var n = this.$();
|
||||
this.sendAction("showModOverlay", {
|
||||
left: n.offset().left,
|
||||
top: n.offset().top + n.height(),
|
||||
sender: from
|
||||
});
|
||||
|
||||
} else if ( e.target.classList.contains('undelete') )
|
||||
this.set("msgObject.deleted", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_vod_line = function(component) {
|
||||
var f = this;
|
||||
// We need to override a few things.
|
||||
this._modify_chat_line(component, true);
|
||||
|
||||
component.reopen({
|
||||
ffz_is_replay: true,
|
||||
|
||||
/*lineChanged: Ember.observer("msgObject.deleted", "isModeratorOrHigher", function() {
|
||||
this.$(".mod-icons").replaceWith(this.buildModIconsHTML());
|
||||
if ( this.get("msgObject.deleted") )
|
||||
this.$(".message").replaceWith(this.buildMessageHTML());
|
||||
else
|
||||
this.$(".deleted").replaceWith(this.buildMessageHTML());
|
||||
}),*/
|
||||
|
||||
buildModIconsHTML: function() {
|
||||
if ( ! this.get("isViewerModeratorOrHigher") || this.get("isModeratorOrHigher") )
|
||||
return "";
|
||||
|
||||
return '<span class="mod-icons float-left">' +
|
||||
(this.get('msgObject.deleted') ?
|
||||
'<em class="mod-icon float-left unban"></em>' :
|
||||
'<a class="mod-icon float-left tooltip delete" title="Delete Message" href="#">Delete</a>') + '</span>';
|
||||
},
|
||||
|
||||
buildDeletedMesageHTML: function() {
|
||||
return '<span clas="deleted"><message deleted></span>';
|
||||
},
|
||||
|
||||
render: function(e) {
|
||||
if ( this.get('msgObject.isHorizontalLine') )
|
||||
e.push(this.buildHorizontalLineHTML());
|
||||
else {
|
||||
e.push(this.buildSenderHTML());
|
||||
if ( this.get("msgObject.deleted") )
|
||||
e.push(this.buildDeletedMessageHTML())
|
||||
else
|
||||
e.push(this.buildMessageHTML());
|
||||
}
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
if ( e.target.classList.contains('delete') ) {
|
||||
e.preventDefault();
|
||||
this.sendAction("timeoutUser", this.get("msgObject.id"));
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
if ( this.get("msgObject.ffz_has_mention") )
|
||||
this.get("element").classList.add("ffz-mentioned");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*FFZ.prototype._modify_line = function(component) {
|
||||
var f = this,
|
||||
|
||||
Layout = App.__container__.lookup('controller:layout'),
|
||||
Settings = App.__container__.lookup('controller:settings');
|
||||
|
||||
|
||||
component.reopen({
|
||||
click: function(e) {
|
||||
if ( e.target && e.target.classList.contains('ffz-old-messages') )
|
||||
return f._show_deleted(this.get('msgObject.room'));
|
||||
|
@ -640,146 +1008,11 @@ FFZ.prototype._modify_line = function(component) {
|
|||
return this._super(e);
|
||||
},
|
||||
|
||||
ffzUserLevel: function() {
|
||||
if ( this.get('isStaff') )
|
||||
return 5;
|
||||
else if ( this.get('isAdmin') )
|
||||
return 4;
|
||||
else if ( this.get('isBroadcaster') )
|
||||
return 3;
|
||||
else if ( this.get('isGlobalMod') )
|
||||
return 2;
|
||||
else if ( this.get('isModerator') )
|
||||
return 1;
|
||||
return 0;
|
||||
}.property('msgObject.labels.[]'),
|
||||
|
||||
render: function(e) {
|
||||
var deleted = this.get('msgObject.deleted'),
|
||||
r = this,
|
||||
|
||||
user = this.get('msgObject.from'),
|
||||
room_id = this.get('msgObject.room'),
|
||||
room = f.rooms && f.rooms[room_id],
|
||||
|
||||
recipient = this.get('msgObject.to'),
|
||||
is_whisper = recipient && recipient.length,
|
||||
|
||||
this_ul = this.get('ffzUserLevel'),
|
||||
other_ul = room && room.room && room.room.get('ffzUserLevel') || 0,
|
||||
|
||||
raw_color = this.get('msgObject.color'),
|
||||
colors = raw_color && f._handle_color(raw_color),
|
||||
|
||||
is_dark = (Layout && Layout.get('isTheatreMode')) || (Settings && Settings.get('settings.darkMode'));
|
||||
|
||||
|
||||
e.push('<div class="indicator"></div>');
|
||||
e.push('<span class="timestamp float-left">' + this.get("timestamp") + '</span> ');
|
||||
|
||||
|
||||
// Moderation actions
|
||||
if ( ! is_whisper && this_ul < other_ul ) {
|
||||
e.push('<span class="mod-icons float-left">');
|
||||
for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
|
||||
var pair = f.settings.mod_buttons[i],
|
||||
prefix = pair[0], btn = pair[1],
|
||||
|
||||
cmd, tip;
|
||||
|
||||
if ( btn === false ) {
|
||||
if ( deleted )
|
||||
e.push('<a class="mod-icon float-left tooltip unban" title="Unban User" href="#">Unban</a>');
|
||||
else
|
||||
e.push('<a class="mod-icon float-left tooltip ban" title="Ban User" href="#">Ban</a>');
|
||||
|
||||
} else if ( btn === 600 )
|
||||
e.push('<a class="mod-icon float-left tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>');
|
||||
|
||||
else {
|
||||
if ( typeof btn === "string" ) {
|
||||
cmd = btn.replace(/{user}/g, user).replace(/ *<LINE> */, "\n");
|
||||
tip = 'Custom Command' + (cmd.indexOf('\n') !== -1 ? 's' : '') + '\n' + cmd;
|
||||
} else {
|
||||
cmd = "/timeout " + user + " " + btn;
|
||||
tip = "Timeout User (" + utils.duration_string(btn) + ")";
|
||||
}
|
||||
|
||||
e.push('<a class="mod-icon float-left tooltip' + (cmd.substr(0, 9) === '/timeout' ? ' is-timeout' : '') + ' custom" data-cmd="' + utils.quote_attr(cmd) + '" title="' + utils.quote_attr(tip) + '" href="#">' + prefix + '</a>');
|
||||
}
|
||||
}
|
||||
|
||||
e.push('</span>');
|
||||
}
|
||||
|
||||
|
||||
// Badges
|
||||
e.push('<span class="badges float-left">');
|
||||
e.push(f.render_badges(f.get_line_badges(this.get('msgObject'), is_whisper)));
|
||||
e.push('</span>');
|
||||
|
||||
|
||||
// Handle aliases
|
||||
var alias = f.aliases[user],
|
||||
name = this.get('msgObject.tags.display-name') || (user && user.capitalize()) || "unknown user",
|
||||
style = colors && 'color:' + (is_dark ? colors[1] : colors[0]),
|
||||
colored = style ? ' has-color' : '';
|
||||
|
||||
if ( alias )
|
||||
e.push('<span class="from ffz-alias tooltip' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
|
||||
else
|
||||
e.push('<span class="from' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">' + utils.sanitize(name) + '</span>');
|
||||
|
||||
|
||||
// If it's a whisper, we need to get that user's color, alias, and draw the whisper arrow thing.
|
||||
if ( is_whisper ) {
|
||||
var to_alias = f.aliases[recipient],
|
||||
to_name = this.get('msgObject.tags.recipient-display-name') || (recipient && recipient.capitalize()) || "unknown user",
|
||||
|
||||
to_color = this.get('msgObject.toColor'),
|
||||
to_colors = to_color && f._handle_color(to_color),
|
||||
to_style = to_color && 'color:' + (is_dark ? to_colors[1] : to_colors[0]),
|
||||
to_colored = to_style ? ' has-color' : '';
|
||||
|
||||
this._renderWhisperArrow(e);
|
||||
|
||||
if ( to_alias )
|
||||
e.push('<span class="to ffz-alias tooltip' + to_colored + '" style="' + to_style + (to_color ? '" data-color="' + to_color : '') + '" title="' + utils.sanitize(to_name) + '">' + utils.sanitize(to_alias) + '</span>');
|
||||
else
|
||||
e.push('<span class="to' + to_colored + '" style="' + to_style + (to_colors ? '" data-color="' + to_color : '') + '">' + utils.sanitize(to_name) + '</span>');
|
||||
}
|
||||
|
||||
|
||||
// Finally, onto the message proper.
|
||||
e.push('<span class="colon">:</span> ');
|
||||
|
||||
if ( this.get('msgObject.style') !== 'action' ) {
|
||||
style = '';
|
||||
colored = '';
|
||||
}
|
||||
|
||||
if ( deleted )
|
||||
e.push('<span class="deleted"><a class="undelete" href="#"><message deleted></a></span>');
|
||||
else {
|
||||
e.push('<span class="message' + colored + '" style="' + style + (colors ? '" data-color="' + raw_color : '') + '">');
|
||||
e.push(f.render_tokens(this.get('tokenizedMessage'), true));
|
||||
|
||||
var old_messages = this.get('msgObject.ffz_old_messages');
|
||||
if ( old_messages && old_messages.length )
|
||||
e.push('<div class="button primary float-right ffz-old-messages">Show ' + utils.number_commas(old_messages.length) + ' Old</div>');
|
||||
|
||||
e.push('</span>');
|
||||
}
|
||||
e.push(this.buildSenderHTML());
|
||||
e.push(this.buildMessageHTML())
|
||||
},
|
||||
|
||||
classNameBindings: [
|
||||
'msgObject.ffz_has_mention:ffz-mentioned',
|
||||
'ffzWasDeleted:ffz-deleted',
|
||||
'ffzHasOldMessages:clearfix',
|
||||
'ffzHasOldMessages:ffz-has-deleted'
|
||||
],
|
||||
|
||||
|
||||
ffzWasDeleted: function() {
|
||||
return f.settings.prevent_clear && this.get('msgObject.ffz_deleted');
|
||||
}.property('msgObject.ffz_deleted'),
|
||||
|
@ -789,6 +1022,12 @@ FFZ.prototype._modify_line = function(component) {
|
|||
return old_messages && old_messages.length;
|
||||
}.property('msgObject.ffz_old_messages'),
|
||||
|
||||
classNameBindings: [
|
||||
'msgObject.ffz_has_mention:ffz-mentioned',
|
||||
'ffzWasDeleted:ffz-deleted',
|
||||
'ffzHasOldMessages:clearfix',
|
||||
'ffzHasOldMessages:ffz-has-deleted'
|
||||
],
|
||||
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
|
@ -800,7 +1039,7 @@ FFZ.prototype._modify_line = function(component) {
|
|||
el.setAttribute('data-deleted', this.get('msgObject.deleted') || false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// ---------------------
|
||||
|
@ -843,47 +1082,65 @@ FFZ.get_capitalization = function(name, callback) {
|
|||
// ---------------------
|
||||
|
||||
FFZ.prototype._remove_banned = function(tokens) {
|
||||
var banned_words = this.settings.banned_words,
|
||||
banned_links = this.settings.filter_bad_shorteners ? ['apo.af', 'goo.gl', 'j.mp', 'bit.ly'] : null,
|
||||
|
||||
has_banned_words = banned_words && banned_words.length;
|
||||
|
||||
if ( !has_banned_words && (! banned_links || ! banned_links.length) )
|
||||
var banned_words = this.settings.banned_words;
|
||||
if ( ! banned_words || ! banned_words.length )
|
||||
return tokens;
|
||||
|
||||
if ( typeof tokens == "string" )
|
||||
tokens = [tokens];
|
||||
|
||||
var regex = FFZ._words_to_regex(banned_words),
|
||||
link_regex = this.settings.filter_bad_shorteners && FFZ._words_to_regex(banned_links),
|
||||
new_tokens = [];
|
||||
|
||||
for(var i=0, l = tokens.length; i < l; i++) {
|
||||
var token = tokens[i];
|
||||
if ( ! _.isString(token ) ) {
|
||||
if ( token.emoticonSrc && has_banned_words && regex.test(token.altText) )
|
||||
new_tokens.push(token.altText.replace(regex, "$1***"));
|
||||
else if ( token.isLink && has_banned_words && regex.test(token.href) )
|
||||
new_tokens.push({
|
||||
isLink: true,
|
||||
href: token.href,
|
||||
isDeleted: true,
|
||||
isLong: false,
|
||||
censoredHref: token.href.replace(regex, "$1***")
|
||||
});
|
||||
else if ( token.isLink && this.settings.filter_bad_shorteners && link_regex.test(token.href) )
|
||||
new_tokens.push({
|
||||
isLink: true,
|
||||
href: token.href,
|
||||
isDeleted: true,
|
||||
isLong: false,
|
||||
isShortened: true
|
||||
});
|
||||
else
|
||||
new_tokens.push(token);
|
||||
if ( typeof token === "string" )
|
||||
token = {type: "text", text: token};
|
||||
|
||||
if ( token.type === "text" && regex.test(token.text) ) {
|
||||
token = token.text.replace(regex, function(all, prefix, match) {
|
||||
if ( prefix.length )
|
||||
new_tokens.push({type: "text", text: prefix});
|
||||
new_tokens.push({
|
||||
type: "deleted",
|
||||
length: match.length,
|
||||
text: match
|
||||
});
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
if ( token )
|
||||
new_tokens.push({type: "text", text: token});
|
||||
|
||||
} else if ( token.type === "emoticon" && regex.test(token.altText) ) {
|
||||
token = token.altText.replace(regex, function(all, prefix, match) {
|
||||
if ( prefix.length )
|
||||
new_tokens.push({type: "text", text: prefix});
|
||||
new_tokens.push({
|
||||
type: "deleted",
|
||||
length: match.length,
|
||||
text: match
|
||||
});
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
if ( token )
|
||||
new_tokens.push({type: "text", text: token});
|
||||
|
||||
} else if ( token.type === "link" && regex.test(token.text) )
|
||||
new_tokens.push({
|
||||
type: "link",
|
||||
isDeleted: true,
|
||||
isMailTo: token.isMailTo,
|
||||
isLong: false,
|
||||
length: token.text.length,
|
||||
censoredLink: token.text.replace(regex, "$1***"),
|
||||
link: token.link,
|
||||
text: token.text
|
||||
});
|
||||
|
||||
} else if ( has_banned_words )
|
||||
new_tokens.push(token.replace(regex, "$1***"));
|
||||
else
|
||||
new_tokens.push(token);
|
||||
}
|
||||
|
|
|
@ -158,7 +158,9 @@ FFZ.settings_info.mod_buttons = {
|
|||
help: "Change out the different in-line moderation icons to use any command quickly.",
|
||||
|
||||
method: function() {
|
||||
var old_val = "";
|
||||
var f = this,
|
||||
old_val = "";
|
||||
|
||||
for(var i=0; i < this.settings.mod_buttons.length; i++) {
|
||||
var pair = this.settings.mod_buttons[i],
|
||||
prefix = pair[0], cmd = pair[1], had_prefix = pair[2];
|
||||
|
@ -182,8 +184,18 @@ FFZ.settings_info.mod_buttons = {
|
|||
old_val += ' ' + prefix + cmd;
|
||||
}
|
||||
|
||||
var new_val = prompt("Custom In-Line Moderation Icons\n\nPlease enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. To include spaces in a command, surround the command with double quotes (\"). Use \"{user}\" to insert the user's username into the command, otherwise it will be appended to the end.\n\nExample: !permit \"!reg add {user}\"\n\nTo send multiple commands, separate them with \"<LINE>\".\n\nNumeric values will become timeout buttons for that number of seconds. The text \"<BAN>\" is a special value that will act like the normal Ban button in chat.\n\nTo assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.\n\nExample: A=\"!reg add\"\n\nDefault: <BAN> 600", old_val);
|
||||
|
||||
utils.prompt(
|
||||
"Custom In-Line Moderation Icons",
|
||||
"Please enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. " +
|
||||
"To include spaces in a command, surround the command with double quotes (\"). Use <code>{user}</code> to insert the user's name " +
|
||||
"into the command, otherwise it will be appended to the end.</p><p><b>Example:</b> <code>!permit \"!reg add {user}\"</code></p><p>To " +
|
||||
"send multiple commands, separate them with <code><LINE></code>.</p><p>Numeric values will become timeout buttons for " +
|
||||
"that number of seconds. The text <code><BAN></code> is a special value that will act like the normal Ban button in chat.</p><p>" +
|
||||
"To assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.</p><p>" +
|
||||
"<b>Example:</b> <code>A=\"!reg add\"</code></p><p><b>Default:</b> <code><BAN> 600</code>",
|
||||
old_val.substr(1),
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
|
@ -239,7 +251,7 @@ FFZ.settings_info.mod_buttons = {
|
|||
val = false;
|
||||
|
||||
var num = parseInt(val);
|
||||
if ( num > 0 && num !== NaN )
|
||||
if ( num > 0 && ! Number.isNaN(num) )
|
||||
val = num;
|
||||
|
||||
if ( ! prefix ) {
|
||||
|
@ -266,7 +278,8 @@ FFZ.settings_info.mod_buttons = {
|
|||
final.push([prefix, val, had_prefix]);
|
||||
}
|
||||
|
||||
this.settings.set('mod_buttons', final);
|
||||
f.settings.set('mod_buttons', final);
|
||||
}, 600);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -282,7 +295,8 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
help: "Add additional buttons to moderation cards for running chat commands on those users.",
|
||||
|
||||
method: function() {
|
||||
var old_val = "";
|
||||
var f = this,
|
||||
old_val = "";
|
||||
for(var i=0; i < this.settings.mod_card_buttons.length; i++) {
|
||||
var cmd = this.settings.mod_card_buttons[i];
|
||||
if ( cmd.indexOf(' ') !== -1 )
|
||||
|
@ -291,8 +305,13 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
old_val += ' ' + cmd;
|
||||
}
|
||||
|
||||
var new_val = prompt("Moderation Card Additional Buttons\n\nPlease enter a list of additional commands to display buttons for on moderation cards. Commands are separated by spaces. To include spaces in a command, surround the command with double quotes (\"). Use \"{user}\" to insert the user's username into the command, otherwise it will be appended to the end.\n\nExample: !permit \"!reg add {user}\"", old_val);
|
||||
|
||||
utils.prompt(
|
||||
"Moderation Card Additional Buttons",
|
||||
"Please enter a list of additional commands to display buttons for on moderation cards. Commands are separated by spaces. " +
|
||||
"To include spaces in a command, surround the command with double quotes (\"). Use <code>{user}</code> to insert the " +
|
||||
"user's name into the command, otherwise it will be appended to the end.</p><p><b>Example:</b> !permit \"!reg add {user}\"",
|
||||
old_val.substr(1),
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
|
@ -329,7 +348,8 @@ FFZ.settings_info.mod_card_buttons = {
|
|||
}
|
||||
}
|
||||
|
||||
this.settings.set("mod_card_buttons", vals);
|
||||
f.settings.set("mod_card_buttons", vals);
|
||||
}, 600);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -345,9 +365,15 @@ FFZ.settings_info.mod_card_durations = {
|
|||
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);
|
||||
var f = this,
|
||||
old_val = this.settings.mod_card_durations.join(", ");
|
||||
|
||||
utils.prompt(
|
||||
"Moderation Card Timeout Buttons",
|
||||
"Please enter a comma-separated list of durations that you would like to have timeout buttons for. " +
|
||||
"Durations must be expressed in seconds.</p><p><b>Default:</b> 300, 600, 3600, 43200, 86400, 604800",
|
||||
old_val,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
|
@ -363,11 +389,12 @@ FFZ.settings_info.mod_card_durations = {
|
|||
if ( val === 0 )
|
||||
val = 1;
|
||||
|
||||
if ( val !== NaN && val > 0 )
|
||||
if ( ! Number.isNaN(val) && val > 0 )
|
||||
vals.push(val);
|
||||
}
|
||||
|
||||
this.settings.set("mod_card_durations", vals);
|
||||
f.settings.set("mod_card_durations", vals);
|
||||
}, 600);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -420,7 +447,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
} else if ( followers === undefined ) {
|
||||
var t = this;
|
||||
this.set('cardInfo.user.ffz_followers', false);
|
||||
Twitch.api.get("channels/" + this.get('cardInfo.user.id') + '/follows', {limit:1}).done(function(data) {
|
||||
utils.api.get("channels/" + this.get('cardInfo.user.id') + '/follows', {limit:1}).done(function(data) {
|
||||
t.set('cardInfo.user.ffz_followers', data._total);
|
||||
t.ffzRebuildInfo();
|
||||
}).fail(function(data) {
|
||||
|
@ -682,7 +709,7 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
real_msg.title = "Message User";
|
||||
|
||||
real_msg.addEventListener('click', function() {
|
||||
window.open('http://www.twitch.tv/message/compose?to=' + controller.get('cardInfo.user.id'));
|
||||
window.open('//www.twitch.tv/message/compose?to=' + controller.get('cardInfo.user.id'));
|
||||
})
|
||||
|
||||
msg_btn.parentElement.insertBefore(real_msg, msg_btn.nextSibling);
|
||||
|
@ -699,7 +726,11 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
var user = controller.get('cardInfo.user.id'),
|
||||
alias = f.aliases[user];
|
||||
|
||||
var new_val = prompt("Alias for User: " + user + "\n\nPlease enter an alias for the user. Leave it blank to remove the alias.", alias);
|
||||
utils.prompt(
|
||||
"Alias for <b>" + utils.sanitize(controller.get('cardInfo.user.display_name') || user) + "</b>",
|
||||
"Please enter an alias for the user. Leave it blank to remove the alias.",
|
||||
alias,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
|
@ -713,14 +744,11 @@ FFZ.prototype.setup_mod_card = function() {
|
|||
// Update UI
|
||||
f._update_alias(user);
|
||||
|
||||
Ember.propertyDidChange(controller, 'userName');
|
||||
var name = el.querySelector('h3.name'),
|
||||
link = name && name.querySelector('a');
|
||||
|
||||
if ( link )
|
||||
name = link;
|
||||
Ember.propertyDidChange(controller, 'cardInfo.user.display_name');
|
||||
var name = el.querySelector('h3.name');
|
||||
if ( name )
|
||||
name.classList.toggle('ffz-alias', new_val);
|
||||
});
|
||||
});
|
||||
|
||||
if ( msg_btn )
|
||||
|
@ -1040,9 +1068,11 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
|
|||
|
||||
// Interactivity
|
||||
jQuery('a.undelete', l_el).click(function(e) { this.parentElement.outerHTML = this.getAttribute('data-message'); });
|
||||
jQuery('.deleted-word', l_el).click(function(e) { jQuery(this).trigger('mouseout'); this.outerHTML = this.getAttribute('data-text'); });
|
||||
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
|
||||
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
|
||||
jQuery('.html-tooltip', l_el).tipsy({html:true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
jQuery('.ffz-tooltip', l_el).tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
if ( modcard ) {
|
||||
modcard.get('cardInfo.user.id') !== msg.from && jQuery('span.from', l_el).click(function(e) {
|
||||
|
@ -1082,6 +1112,9 @@ FFZ.prototype._update_alias = function(user) {
|
|||
var line = lines[i],
|
||||
el_from = line.querySelector('.from');
|
||||
|
||||
if ( ! el_from )
|
||||
continue;
|
||||
|
||||
el_from.classList.toggle('ffz-alias', alias);
|
||||
el_from.textContent = display_name;
|
||||
el_from.title = alias ? cap_name : '';
|
||||
|
@ -1110,7 +1143,7 @@ FFZ.chat_commands.purge = function(room, args) {
|
|||
}
|
||||
|
||||
FFZ.chat_commands.p = function(room, args) {
|
||||
return FFZ.chat_commands.purge.bind(this)(room, args);
|
||||
return FFZ.chat_commands.purge.call(this, room, args);
|
||||
}
|
||||
|
||||
FFZ.chat_commands.p.enabled = function() { return this.settings.short_commands; }
|
||||
|
|
|
@ -107,7 +107,6 @@ FFZ.prototype._modify_player = function(player) {
|
|||
f._cindex && f._cindex.ffzUpdatePlayerStats();
|
||||
};
|
||||
|
||||
|
||||
player.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
|
@ -139,6 +138,10 @@ FFZ.prototype._modify_player = function(player) {
|
|||
ffzInit: function() {
|
||||
var id = this.get('channel.id');
|
||||
f.players[id] = this;
|
||||
|
||||
var player = this.get('player');
|
||||
if ( player )
|
||||
this.ffzPostPlayer();
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
|
@ -153,22 +156,12 @@ FFZ.prototype._modify_player = function(player) {
|
|||
},
|
||||
|
||||
ffzPostPlayer: function() {
|
||||
var player = this.get('ffz_player') || this.get('player');
|
||||
if ( ! player ) {
|
||||
var tp2 = window.require("web-client/components/twitch-player2");
|
||||
if ( ! tp2 || ! tp2.getPlayer )
|
||||
var player = this.get('player');
|
||||
if ( ! player )
|
||||
return;
|
||||
|
||||
player = tp2.getPlayer();
|
||||
if ( ! player || ! player.getVideo )
|
||||
// We can't get a valid player. :-(
|
||||
return;
|
||||
}
|
||||
|
||||
this.set('ffz_player', player);
|
||||
|
||||
// Only set up the stats hooks if we need stats.
|
||||
var has_video;
|
||||
var has_video = false;
|
||||
|
||||
try {
|
||||
has_video = player.getVideo();
|
||||
|
@ -184,24 +177,25 @@ FFZ.prototype._modify_player = function(player) {
|
|||
if ( this.get('ffzStatsInitialized') )
|
||||
return;
|
||||
|
||||
var player = this.get('ffz_player');
|
||||
var t = this,
|
||||
player = this.get('player');
|
||||
|
||||
if ( ! player )
|
||||
return;
|
||||
|
||||
this.set('ffzStatsInitialized', true);
|
||||
|
||||
// Make it so stats can no longer be disabled if we want them.
|
||||
if ( player.setStatsEnabled ) {
|
||||
player.ffzSetStatsEnabled = player.setStatsEnabled;
|
||||
try {
|
||||
player.ffz_stats = player.getStatsEnabled();
|
||||
} catch(err) {
|
||||
// Assume stats are off.
|
||||
f.error("Player2 ffzInitStats: getStatsEnabled still doesn't work: " + err);
|
||||
f.log("Player2 ffzInitStats: getStatsEnabled still doesn't work.");
|
||||
player.ffz_stats = false;
|
||||
}
|
||||
|
||||
var t = this;
|
||||
|
||||
player.setStatsEnabled = function(e, s) {
|
||||
if ( s !== false )
|
||||
player.ffz_stats = e;
|
||||
|
@ -222,34 +216,13 @@ FFZ.prototype._modify_player = function(player) {
|
|||
player.ffzSetStatsEnabled(true);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if ( f.settings.player_stats && ! player.ffz_stats ) {
|
||||
if ( f.settings.player_stats && ( ! player.setStatsEnabled || ! player.ffz_stats ) ) {
|
||||
this._ffz_player_stats_initialized = true;
|
||||
player.addEventListener('statschange', update_stats);
|
||||
player.ffzSetStatsEnabled(true);
|
||||
}
|
||||
},
|
||||
|
||||
ffzSetQuality: function(q) {
|
||||
var player = this.get('ffz_player');
|
||||
if ( ! player )
|
||||
return;
|
||||
|
||||
this.$(".js-quality-display-contain").attr("data-q", "loading");
|
||||
|
||||
player.setQuality(q);
|
||||
|
||||
var t = this.$(".js-player-alert");
|
||||
t.find(".js-player-alert__message").text();
|
||||
t.attr("data-active", !0);
|
||||
},
|
||||
|
||||
ffzGetQualities: function() {
|
||||
var player = this.get('ffz_player');
|
||||
if ( ! player )
|
||||
return [];
|
||||
return player.getQualities();
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
}
|
|
@ -11,7 +11,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
if ( ! room.moderator_badge )
|
||||
return "";
|
||||
|
||||
return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-image:url("' + room.moderator_badge + '") !important; }';
|
||||
return '.chat-line[data-room="' + room.id + '"] .badges .moderator:not(.ffz-badge-replacement) { background-repeat: no-repeat; background-size: initial; background-position: center; background-image:url("' + room.moderator_badge + '") !important; }';
|
||||
};
|
||||
|
||||
|
||||
|
@ -37,22 +37,18 @@ FFZ.prototype.setup_room = function() {
|
|||
// Responsive ban button.
|
||||
var f = this,
|
||||
RC = App.__container__.lookup('controller:room');
|
||||
|
||||
if ( RC ) {
|
||||
var orig_ban = RC._actions.banUser,
|
||||
orig_to = RC._actions.timeoutUser;
|
||||
|
||||
RC._actions.banUser = function(e) {
|
||||
orig_ban.bind(this)(e);
|
||||
orig_ban.call(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");
|
||||
orig_to.call(this, e);
|
||||
this.get("model").clearMessages(e.user);
|
||||
}
|
||||
|
||||
|
@ -79,7 +75,7 @@ FFZ.prototype.setup_room = function() {
|
|||
renderBottom: e.bottom,
|
||||
renderRight: e.right,
|
||||
isIgnored: this.get("tmiSession").isIgnored(e.sender),
|
||||
isChannelOwner: this.get("controllers.login.userData.login") === e.sender,
|
||||
isChannelOwner: this.get("login.userData.login") === e.sender,
|
||||
profileHref: Twitch.uri.profile(e.sender),
|
||||
isModeratorOrHigher: this.get("model.isModeratorOrHigher")
|
||||
});
|
||||
|
@ -161,15 +157,6 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
this._super();
|
||||
},
|
||||
|
||||
ffzAlternate: function() {
|
||||
/*if ( ! this._ffz_chat_display ) {
|
||||
var el = this.get('element');
|
||||
this._ffz_chat_display = el && el.querySelector('ul.chat-lines');
|
||||
}
|
||||
|
||||
this._ffz_chat_display && this._ffz_chat_display.classList.toggle('ffz-should-alternate');*/
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
f._roomv = this;
|
||||
|
||||
|
@ -232,6 +219,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
|
||||
var r9k_badge = cont.querySelector('#ffz-stat-r9k'),
|
||||
sub_badge = cont.querySelector('#ffz-stat-sub'),
|
||||
emote_badge = cont.querySelector('#ffz-stat-emote'),
|
||||
slow_badge = cont.querySelector('#ffz-stat-slow'),
|
||||
banned_badge = cont.querySelector('#ffz-stat-banned'),
|
||||
delay_badge = cont.querySelector('#ffz-stat-delay'),
|
||||
|
@ -243,6 +231,8 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
r9k_badge.parentElement.removeChild(r9k_badge);
|
||||
if ( sub_badge )
|
||||
sub_badge.parentElement.removeChild(sub_badge);
|
||||
if ( emote_badge )
|
||||
emote_badge.parentElement.removeChild(emote_badge);
|
||||
if ( slow_badge )
|
||||
slow_badge.parentElement.removeChild(slow_badge);
|
||||
if ( delay_badge )
|
||||
|
@ -275,6 +265,16 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
jQuery(sub_badge).tipsy({gravity:"s", offset:15});
|
||||
}
|
||||
|
||||
if ( ! emote_badge ) {
|
||||
emote_badge = document.createElement('span');
|
||||
emote_badge.className = 'ffz room-state stat float-right';
|
||||
emote_badge.id = 'ffz-stat-emote';
|
||||
emote_badge.innerHTML = 'E<span>MOTE</span>';
|
||||
emote_badge.title = "This room is in Twitch emote-only mode. Emotes added by extensions are not permitted in this mode.";
|
||||
cont.appendChild(emote_badge);
|
||||
jQuery(emote_badge).tipsy({gravity: "s", offset: 15});
|
||||
}
|
||||
|
||||
if ( ! slow_badge ) {
|
||||
slow_badge = document.createElement('span');
|
||||
slow_badge.className = 'ffz room-state stat float-right';
|
||||
|
@ -315,6 +315,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
var vis_count = 0,
|
||||
r9k_vis = room && room.get('r9k'),
|
||||
sub_vis = room && room.get('subsOnly'),
|
||||
emote_vis = room && room.get('emoteOnly') && room.get('emoteOnly') !== '0',
|
||||
ban_vis = room && room.get('ffz_banned'),
|
||||
slow_vis = room && room.get('slowMode'),
|
||||
delay_vis = f.settings.chat_delay !== 0,
|
||||
|
@ -322,6 +323,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
|
||||
if ( r9k_vis ) vis_count += 1;
|
||||
if ( sub_vis ) vis_count += 1;
|
||||
if ( emote_vis ) vis_count += 1;
|
||||
if ( ban_vis ) vis_count += 1;
|
||||
if ( slow_vis ) vis_count += 1;
|
||||
if ( delay_vis ) vis_count += 1;
|
||||
|
@ -329,6 +331,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
|
||||
r9k_badge.classList.toggle('truncated', vis_count > 3);
|
||||
sub_badge.classList.toggle('truncated', vis_count > 3);
|
||||
emote_badge.classList.toggle('truncated', vis_count > 3);
|
||||
banned_badge.classList.toggle('truncated', vis_count > 3);
|
||||
slow_badge.classList.toggle('truncated', vis_count > 3);
|
||||
delay_badge.classList.toggle('truncated', vis_count > 3);
|
||||
|
@ -336,6 +339,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
|
||||
r9k_badge.classList.toggle('hidden', ! r9k_vis);
|
||||
sub_badge.classList.toggle('hidden', ! sub_vis);
|
||||
emote_badge.classList.toggle('hidden', ! emote_vis);
|
||||
banned_badge.classList.toggle('hidden', ! ban_vis);
|
||||
|
||||
slow_badge.classList.toggle('hidden', ! slow_vis);
|
||||
|
@ -370,7 +374,6 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
messages.addEventListener('mousemove', this._ffz_mouse_move);
|
||||
messages.addEventListener('touchmove', this._ffz_mouse_move);
|
||||
messages.addEventListener('mouseout', this._ffz_mouse_out);
|
||||
document.addEventListener('mouseout', this._ffz_mouse_out);
|
||||
},
|
||||
|
||||
ffzDisableFreeze: function() {
|
||||
|
@ -389,6 +392,7 @@ FFZ.prototype._modify_rview = function(view) {
|
|||
|
||||
if ( this._ffz_mouse_move ) {
|
||||
messages.removeEventListener('mousemove', this._ffz_mouse_move);
|
||||
messages.removeEventListener('touchmove', this._ffz_mouse_move);
|
||||
this._ffz_mouse_move = undefined;
|
||||
}
|
||||
|
||||
|
@ -553,7 +557,7 @@ FFZ.prototype.run_command = function(text, room_id) {
|
|||
var val = command.enabled;
|
||||
if ( typeof val == "function" ) {
|
||||
try {
|
||||
val = command.enabled.bind(this)(room, args);
|
||||
val = command.enabled.call(this, room, args);
|
||||
} catch(err) {
|
||||
this.error('command "' + cmd + '" enabled: ' + err);
|
||||
val = false;
|
||||
|
@ -567,7 +571,7 @@ FFZ.prototype.run_command = function(text, room_id) {
|
|||
this.log("Received Command: " + cmd, args, true);
|
||||
|
||||
try {
|
||||
output = command.bind(this)(room, args);
|
||||
output = command.call(this, room, args);
|
||||
} catch(err) {
|
||||
this.error('command "' + cmd + '" runner: ' + err);
|
||||
output = "There was an error running the command.";
|
||||
|
@ -602,7 +606,7 @@ FFZ.prototype.run_ffz_command = function(text, room_id) {
|
|||
var command = FFZ.ffz_commands[cmd], output;
|
||||
if ( command ) {
|
||||
try {
|
||||
output = command.bind(this)(room, args);
|
||||
output = command.call(this, room, args);
|
||||
} catch(err) {
|
||||
this.log("Error Running Command - " + cmd + ": " + err, room);
|
||||
output = "There was an error running the command.";
|
||||
|
@ -849,9 +853,6 @@ FFZ.prototype._insert_history = function(room_id, data, from_server) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (removed % 2) && this._roomv && this._roomv.get('context.model.id') === room_id )
|
||||
this._roomv.ffzAlternate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -987,7 +988,7 @@ FFZ.prototype._modify_room = function(room) {
|
|||
ffzUpdateStatus: function() {
|
||||
if ( f._roomv )
|
||||
f._roomv.ffzUpdateStatus();
|
||||
}.observes('r9k', 'subsOnly', 'slow', 'ffz_banned'),
|
||||
}.observes('r9k', 'subsOnly', 'emoteOnly', 'slow', 'ffz_banned'),
|
||||
|
||||
// User Level
|
||||
ffzUserLevel: function() {
|
||||
|
@ -1057,9 +1058,6 @@ FFZ.prototype._modify_room = function(room) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( (removed % 2) && f._roomv && f._roomv.get('context.model.id') === this.get('id') )
|
||||
f._roomv.ffzAlternate();
|
||||
|
||||
// Delete pending messages
|
||||
if (t.ffzPending) {
|
||||
msgs = t.ffzPending;
|
||||
|
@ -1111,11 +1109,8 @@ FFZ.prototype._modify_room = function(room) {
|
|||
len = messages.get("length"),
|
||||
limit = this.get("messageBufferSize");
|
||||
|
||||
if ( len > limit ) {
|
||||
if ( len > limit )
|
||||
messages.removeAt(0, len - limit);
|
||||
if ( ((len - limit) % 2) && f._roomv && f._roomv.get('context.model.id') === this.get('id') )
|
||||
f._roomv.ffzAlternate();
|
||||
}
|
||||
},
|
||||
|
||||
// Artificial chat delay
|
||||
|
@ -1232,6 +1227,15 @@ FFZ.prototype._modify_room = function(room) {
|
|||
// Tokenization
|
||||
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
||||
|
||||
// If it's from Twitch notify, and it's directly related to
|
||||
if ( msg.from === 'twitchnotify' && msg.message.indexOf('subscribed to') === -1 && msg.message.indexOf('subscribed') !== -1 ) {
|
||||
if ( ! msg.tags )
|
||||
msg.tags = {};
|
||||
msg.tags.subscriber = true;
|
||||
if ( msg.labels && msg.labels.indexOf("subscriber") === -1 )
|
||||
msg.labels.push("subscriber");
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
@ -1293,6 +1297,10 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( msg.color )
|
||||
f._handle_color(msg.color);
|
||||
|
||||
// Report this message to the dashboard.
|
||||
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
|
||||
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, location.protocol + "//www.twitch.tv/");
|
||||
|
||||
// Add the message.
|
||||
return this._super(msg);
|
||||
},
|
||||
|
@ -1403,10 +1411,8 @@ FFZ.prototype._modify_room = function(room) {
|
|||
if ( f._cindex )
|
||||
f._cindex.ffzUpdateChatters();
|
||||
|
||||
try {
|
||||
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/");
|
||||
} catch(err) { /* Ignore errors because of security */ }
|
||||
if ( window !== window.parent && parent.postMessage )
|
||||
parent.postMessage({from_ffz: true, command: 'chatter_count', data: Object.keys(this.get('ffz_chatters') || {}).length}, location.protocol + "//www.twitch.tv/");
|
||||
},
|
||||
|
||||
|
||||
|
|
326
src/ember/vod-chat.js
Normal file
326
src/ember/vod-chat.js
Normal file
|
@ -0,0 +1,326 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require("../utils"),
|
||||
constants = require("../constants");
|
||||
|
||||
// ---------------------
|
||||
// Settings
|
||||
// ---------------------
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Initialization
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.setup_vod_chat = function() {
|
||||
var f = this,
|
||||
VRC = App.__container__.resolve('component:vod-right-column');
|
||||
|
||||
if ( VRC )
|
||||
this._modify_vod_right_column(VRC);
|
||||
else
|
||||
f.error("Unable to locate VOD Right Column component.");
|
||||
|
||||
// Get the VOD Chat Service
|
||||
var VODService = App.__container__.lookup('service:vod-chat-service');
|
||||
if ( VODService )
|
||||
VODService.reopen({
|
||||
messageBufferSize: f.settings.scrollback_length,
|
||||
|
||||
pushMessage: function(msg) {
|
||||
if ( msg.get("color") === null ) {
|
||||
var colors = this.get("colorSettings"),
|
||||
from = msg.get("from");
|
||||
|
||||
if ( ! colors.get(from) )
|
||||
colors.set(from, constants.CHAT_COLORS[Math.floor(Math.random() * constants.CHAT_COLORS.length)]);
|
||||
|
||||
msg.set("color", colors.get(from));
|
||||
}
|
||||
|
||||
this.get("messages").pushObject(msg);
|
||||
|
||||
var messages = this.get("messages"),
|
||||
len = this.get("messages.length"),
|
||||
limit = this.get("messageBufferSize");
|
||||
|
||||
if ( len > limit )
|
||||
messages.removeAt(0, len - limit);
|
||||
}
|
||||
});
|
||||
else
|
||||
f.error("Unable to locate VOD Chat Service.");
|
||||
|
||||
// Get the VOD Chat Display
|
||||
var VODChat = App.__container__.resolve('component:vod-chat-display');
|
||||
|
||||
if ( VODChat )
|
||||
this._modify_vod_chat_display(VODChat);
|
||||
else
|
||||
f.error("Unable to locate VOD Chat Display component.");
|
||||
|
||||
// Modify all existing VOD Chat views.
|
||||
var views = window.App && App.__container__.lookup('-view-registry:main') || Ember.View.views;
|
||||
for(var key in views) {
|
||||
var view = views[key];
|
||||
|
||||
if ( VRC && view instanceof VRC ) {
|
||||
this.log("Manually updating existing VOD Right Column.");
|
||||
try {
|
||||
this._modify_vod_right_column(view);
|
||||
view.ffzInit();
|
||||
//Ember.propertyDidChange(view, 'canSeeDarkLaunch');
|
||||
} catch(err) {
|
||||
this.error("setup: setup_vod_chat: " + err);
|
||||
}
|
||||
|
||||
} else if ( VODChat && view instanceof VODChat ) {
|
||||
this.log("Manually updating existing VOD Chat view.", view);
|
||||
try {
|
||||
this._modify_vod_chat_display(view);
|
||||
view.ffzInit();
|
||||
} catch(err) {
|
||||
this.error("setup: setup_vod_chat: " + err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_vod_right_column = function(component) {
|
||||
var f = this;
|
||||
|
||||
component.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("VODRightColumn didInsertElement: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
if ( f.settings.dark_twitch ) {
|
||||
var el = this.get('element'),
|
||||
cont = el && el.querySelector('.chat-container');
|
||||
|
||||
if ( cont )
|
||||
cont.classList.add('dark');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._modify_vod_chat_display = function(component) {
|
||||
var f = this,
|
||||
VODService = App.__container__.lookup('service:vod-chat-service');
|
||||
|
||||
component.reopen({
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
try {
|
||||
this.ffzInit();
|
||||
} catch(err) {
|
||||
f.error("VODChat didInsertElement: " + err);
|
||||
}
|
||||
},
|
||||
|
||||
willClearRender: function() {
|
||||
try {
|
||||
this.ffzTeardown();
|
||||
} catch(err) {
|
||||
f.error("VODChat willClearRender: " + err);
|
||||
}
|
||||
this._super();
|
||||
},
|
||||
|
||||
_prepareToolTips: function() {
|
||||
this.$(".tooltip").tipsy({
|
||||
live: true,
|
||||
gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')
|
||||
})
|
||||
},
|
||||
|
||||
ffzInit: function() {
|
||||
f._vodc = this;
|
||||
|
||||
// Load the room, if necessary
|
||||
var room_id = this.get('video.channel.name');
|
||||
if ( room_id && ! f.rooms[room_id] )
|
||||
f.load_room(room_id); // TODO: Function to reprocess existing messages.
|
||||
|
||||
this.ffz_frozen = false;
|
||||
if ( f.settings.chat_hover_pause )
|
||||
this.ffzEnableFreeze();
|
||||
|
||||
this.$('.chat-messages').find('.html-tooltip').tipsy({
|
||||
live: true, html: true,
|
||||
gravity: utils.tooltip_placement(2 * constants.TOOLTIP_DISTANCE, function() {
|
||||
return this.classList.contains('right') ? 'e' : 'n'
|
||||
})});
|
||||
|
||||
this.$('.chat-messages').find('.ffz-tooltip').tipsy({
|
||||
live: true, html: true,
|
||||
title: f.render_tooltip(),
|
||||
gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, function() {
|
||||
return this.classList.contains('right') ? 'e' : 'n'
|
||||
})});
|
||||
},
|
||||
|
||||
ffzTeardown: function() {
|
||||
if ( f._vodc === this )
|
||||
f._vodc = undefined;
|
||||
|
||||
// TODO: Function to unload the old room?
|
||||
|
||||
this.ffzDisableFreeze();
|
||||
},
|
||||
|
||||
ffzEnableFreeze: function() {
|
||||
var scroller = this.get('chatMessagesScroller');
|
||||
if ( ! scroller )
|
||||
return;
|
||||
|
||||
this._ffz_interval = setInterval(this.ffzPulse.bind(this), 200);
|
||||
|
||||
this._ffz_mouse_move = this.ffzMouseMove.bind(this);
|
||||
this._ffz_mouse_out = this.ffzMouseOut.bind(this);
|
||||
|
||||
scroller.on('mousemove', this._ffz_mouse_move);
|
||||
scroller.on('touchmove', this._ffz_mouse_move);
|
||||
scroller.on('mouseout', this._ffz_mouse_out);
|
||||
},
|
||||
|
||||
ffzDisableFreeze: function() {
|
||||
if ( this._ffz_interval ) {
|
||||
clearInterval(this._ffz_interval);
|
||||
this._ffz_interval = undefined;
|
||||
}
|
||||
|
||||
this.ffzUnfreeze();
|
||||
var scroller = this.get('chatMessagesScroller');
|
||||
if ( ! scroller )
|
||||
return;
|
||||
|
||||
if ( this._ffz_mouse_move ) {
|
||||
scroller.off('mousemove', this._ffz_mouse_move);
|
||||
scroller.off('touchmove', this._ffz_mouse_move);
|
||||
this._ffz_mouse_move = undefined;
|
||||
}
|
||||
|
||||
if ( this._ffz_mouse_out ) {
|
||||
scroller.off('mouseout', this._ffz_mouse_out);
|
||||
this._ffz_mouse_out = undefined;
|
||||
}
|
||||
},
|
||||
|
||||
ffzUnfreeze: function(from_stuck) {
|
||||
this.ffz_frozen = false;
|
||||
this._ffz_last_move = 0;
|
||||
this.ffzUnwarnPaused();
|
||||
|
||||
if ( ! from_stuck && this.get('stuckToBottom') )
|
||||
this._scrollToBottom();
|
||||
},
|
||||
|
||||
ffzPulse: function() {
|
||||
if ( this.ffz_frozen ) {
|
||||
var elapsed = Date.now() - this._ffz_last_move;
|
||||
if ( elapsed > 750 )
|
||||
this.ffzUnfreeze();
|
||||
}
|
||||
},
|
||||
|
||||
/*ffzMouseDown: function(event) {
|
||||
var scroller = this.get('chatMessagesScroller');
|
||||
if ( scroller && scroller[0] && ((!this.ffz_frozen && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) {
|
||||
var r = scroller[0].scrollHeight - scroller[0].scrollTop - scroller[0].offsetHeight;
|
||||
this._setStuckToBottom(10 >= r);
|
||||
}
|
||||
},*/
|
||||
|
||||
ffzMouseOut: function(event) {
|
||||
this._ffz_outside = true;
|
||||
var e = this;
|
||||
setTimeout(function() {
|
||||
if ( e._ffz_outside )
|
||||
e.ffzUnfreeze();
|
||||
}, 25);
|
||||
},
|
||||
|
||||
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;
|
||||
|
||||
this.ffz_frozen = true;
|
||||
if ( this.get('stuckToBottom') ) {
|
||||
VODService && VODService.set("messageBufferSize", f.settings.scrollback_length + 150);
|
||||
this.ffzWarnPaused();
|
||||
}
|
||||
},
|
||||
|
||||
_scrollToBottom: _.throttle(function() {
|
||||
var e = this,
|
||||
scroller = e.get('chatMessagesScroller');
|
||||
|
||||
if ( ! scroller || ! scroller.length )
|
||||
return;
|
||||
|
||||
Ember.run.next(function() {
|
||||
setTimeout(function() {
|
||||
if ( e.ffz_frozen )
|
||||
return;
|
||||
|
||||
scroller[0].scrollTop = scroller[0].scrollHeight;
|
||||
e._setStuckToBottom(true);
|
||||
})
|
||||
})
|
||||
}, 300),
|
||||
|
||||
_setStuckToBottom: function(val) {
|
||||
this.set("stuckToBottom", val);
|
||||
VODService && VODService.set("messageBufferSize", f.settings.scrollback_length + (val ? 0 : 150));
|
||||
if ( ! val )
|
||||
this.ffUnfreeze(true);
|
||||
},
|
||||
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
127
src/emoticons.js
127
src/emoticons.js
|
@ -9,19 +9,6 @@ var FFZ = window.FrankerFaceZ,
|
|||
return "";
|
||||
|
||||
return 'img[src="' + emote.urls[1] + '"] { ' + (emote.margins ? "margin: " + emote.margins + ";" : "") + (emote.css || "") + " }\n";
|
||||
},
|
||||
|
||||
|
||||
from_code_point = function(cp) {
|
||||
var code = typeof cp === "string" ? parseInt(cp, 16) : cp;
|
||||
if ( code < 0x10000)
|
||||
return String.fromCharCode(code);
|
||||
|
||||
code -= 0x10000;
|
||||
return String.fromCharCode(
|
||||
0xD800 + (code >> 10),
|
||||
0xDC00 + (code & 0x3FF)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
@ -218,97 +205,6 @@ FFZ.ws_commands.load_set = function(set_id) {
|
|||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Tooltip Powah!
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype._emote_tooltip = function(emote) {
|
||||
if ( ! emote )
|
||||
return null;
|
||||
|
||||
if ( emote._tooltip )
|
||||
return emote._tooltip;
|
||||
|
||||
var set = this.emote_sets[emote.set_id],
|
||||
owner = emote.owner,
|
||||
title = set && set.title || "Global",
|
||||
source = set && set.source || "FFZ",
|
||||
|
||||
preview_url = this.settings.emote_image_hover ? (emote.urls[4] || emote.urls[2]) : null,
|
||||
image = preview_url ? '<img class="emoticon ffz-image-hover" src="' + preview_url + '?_=preview">' : '';
|
||||
|
||||
emote._tooltip = image + "Emoticon: " + (emote.hidden ? "???" : emote.name) + "<br>" + source + " " + title + (owner ? "<br>By: " + owner.display_name : "");
|
||||
return emote._tooltip;
|
||||
}
|
||||
|
||||
FFZ.prototype._reset_tooltips = function(twitch_only) {
|
||||
for(var emote_id in this._twitch_emotes) {
|
||||
var data = this._twitch_emotes[emote_id];
|
||||
if ( data && data.tooltip )
|
||||
data.tooltip = null;
|
||||
}
|
||||
|
||||
if ( ! twitch_only ) {
|
||||
for(var set_id in this.emote_sets) {
|
||||
var emote_set = this.emote_sets[set_id];
|
||||
for(var emote_id in emote_set.emoticons) {
|
||||
var emote = emote_set.emoticons[emote_id];
|
||||
if ( emote._tooltip )
|
||||
emote._tooltip = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var emotes = document.querySelectorAll('img.emoticon');
|
||||
for(var i=0; i < emotes.length; i++) {
|
||||
var emote = emotes[i];
|
||||
if ( emote.classList.contains('ffz-image-hover') )
|
||||
continue;
|
||||
|
||||
var set_id,
|
||||
emote_id = emote.getAttribute('data-emote');
|
||||
|
||||
if ( emote_id ) {
|
||||
// Twitch Emotes
|
||||
if ( this.has_bttv )
|
||||
continue;
|
||||
|
||||
emote.setAttribute('original-title', utils.build_tooltip.bind(this)(emote_id, false, emote.alt));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( twitch_only )
|
||||
continue;
|
||||
|
||||
// FFZ Emoji
|
||||
emote_id = emote.getAttribute('data-ffz-emoji');
|
||||
if ( emote_id ) {
|
||||
var emoji = this.emoji_data && this.emoji_data[emote_id],
|
||||
setting = this.settings.parse_emoji,
|
||||
|
||||
src = emoji ? (setting === 2 ? emoji.noto_src : emoji.tw_src) : null,
|
||||
image = '';
|
||||
|
||||
if ( src && this.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover emoji" src="' + src + '">';
|
||||
|
||||
emote.setAttribute('original-title', emoji ? (image + 'Emoji: ' + emote.alt + '<br>Name: ' + emoji.name + (emoji.short_name ? "<br>Short Name: :" + emoji.short_name + ":" : "")) : emote.alt);
|
||||
continue;
|
||||
}
|
||||
|
||||
// FFZ Emotes
|
||||
emote_id = emote.getAttribute('data-ffz-emote');
|
||||
set_id = emote.getAttribute('data-ffz-set');
|
||||
|
||||
var emote_set = this.emote_sets[set_id];
|
||||
if ( ! emote_set || ! emote_set.emoticons || ! emote_set.emoticons[emote_id] )
|
||||
continue;
|
||||
|
||||
emote.setAttribute('original-title', this._emote_tooltip(emote_set.emoticons[emote_id]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Emoji Loading
|
||||
// ---------------------
|
||||
|
@ -325,21 +221,29 @@ FFZ.prototype.load_emoji_data = function(callback, tries) {
|
|||
emoji.code = eid;
|
||||
|
||||
new_data[eid] = emoji;
|
||||
if ( emoji.short_name )
|
||||
by_name[emoji.short_name] = eid;
|
||||
if ( emoji.names && emoji.names.length )
|
||||
for(var x=0,y=emoji.names.length; x < y; x++)
|
||||
by_name[emoji.names[x]] = eid;
|
||||
|
||||
emoji.raw = _.map(emoji.code.split("-"), from_code_point).join("");
|
||||
emoji.raw = _.map(emoji.code.split("-"), utils.codepoint_to_emoji).join("");
|
||||
|
||||
emoji.tw_src = constants.SERVER + 'emoji/tw-' + eid + '.svg';
|
||||
emoji.tw_src = constants.SERVER + 'emoji/tw/' + eid + '.svg';
|
||||
emoji.noto_src = constants.SERVER + 'emoji/noto-' + eid + '.svg';
|
||||
emoji.one_src = constants.SERVER + 'emoji/one/' + eid + '.svg';
|
||||
|
||||
emoji.token = {
|
||||
emoticonSrc: true,
|
||||
type: "emoticon",
|
||||
imgSrc: true,
|
||||
|
||||
tw_src: emoji.tw_src,
|
||||
noto_src: emoji.noto_src,
|
||||
one_src: emoji.one_src,
|
||||
|
||||
tw: emoji.tw,
|
||||
noto: emoji.noto,
|
||||
one: emoji.one,
|
||||
|
||||
ffzEmoji: eid,
|
||||
altText: emoji.raw
|
||||
|
@ -481,6 +385,15 @@ FFZ.prototype._load_set_json = function(set_id, callback, data) {
|
|||
else
|
||||
emote.regex = new RegExp("(^|\\W|\\b)(" + utils.escape_regex(emote.name) + ")\\b", "g");
|
||||
|
||||
emote.token = {
|
||||
type: "emoticon",
|
||||
srcSet: emote.srcSet,
|
||||
imgSrc: emote.urls[1],
|
||||
ffzEmote: emote.id,
|
||||
ffzEmoteSet: set_id,
|
||||
altText: emote.hidden ? '???' : emote.name
|
||||
};
|
||||
|
||||
output_css += build_css(emote);
|
||||
data.count++;
|
||||
data.emoticons[emote.id] = emote;
|
||||
|
|
|
@ -14,7 +14,7 @@ var FFZ = window.FrankerFaceZ,
|
|||
// API Constructor
|
||||
// ---------------------
|
||||
|
||||
var API = FFZ.API = function(instance, name, icon) {
|
||||
var API = FFZ.API = function(instance, name, icon, version) {
|
||||
this.ffz = instance || FFZ.get();
|
||||
|
||||
// Check for a known API!
|
||||
|
@ -54,12 +54,13 @@ var API = FFZ.API = function(instance, name, icon) {
|
|||
|
||||
this.name = name || ("Extension#" + this.id);
|
||||
this.icon = icon || null;
|
||||
this.version = version || null;
|
||||
|
||||
this.ffz.log('Registered New Extension #' + this.id + ': ' + this.name);
|
||||
};
|
||||
|
||||
|
||||
FFZ.prototype.api = function(name, icon) {
|
||||
FFZ.prototype.api = function(name, icon, version) {
|
||||
// Load the known APIs list.
|
||||
if ( ! this._known_apis ) {
|
||||
this._known_apis = {};
|
||||
|
@ -71,7 +72,7 @@ FFZ.prototype.api = function(name, icon) {
|
|||
}
|
||||
}
|
||||
|
||||
return new API(this, name, icon);
|
||||
return new API(this, name, icon, version);
|
||||
}
|
||||
|
||||
|
||||
|
@ -163,6 +164,15 @@ API.prototype._load_set = function(real_id, set_id, data) {
|
|||
else
|
||||
new_emote.regex = new RegExp("(^|\\W|\\b)(" + utils.escape_regex(emote.name) + ")(?=\\W|$)", "g");
|
||||
|
||||
new_emote.token = {
|
||||
type: "emoticon",
|
||||
srcSet: new_emote.srcSet,
|
||||
imgSrc: new_emote.urls[1],
|
||||
ffzEmote: id,
|
||||
ffzEmoteSet: real_id,
|
||||
altText: new_emote.hidden ? '???' : new_emote.name
|
||||
};
|
||||
|
||||
output_css += build_css(new_emote);
|
||||
emote_set.count++;
|
||||
emoticons[id] = new_emote;
|
||||
|
|
|
@ -42,6 +42,14 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
utils.update_css(this._chat_style, 'chat_ts_font_size', '');
|
||||
}
|
||||
|
||||
// Remove Sub Count and the Chart
|
||||
if ( this.is_dashboard ) {
|
||||
this._update_subscribers();
|
||||
this._remove_dash_chart();
|
||||
}
|
||||
|
||||
document.body.classList.add('ffz-bttv');
|
||||
|
||||
// Disable Chat Tabs
|
||||
if ( this._chatv ) {
|
||||
if ( this.settings.group_tabs )
|
||||
|
@ -79,6 +87,8 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
|
||||
this.toggle_style('chat-colors-gray');
|
||||
this.toggle_style('badges-transparent');
|
||||
this.toggle_style('badges-sub-notice');
|
||||
this.toggle_style('badges-sub-notice-on');
|
||||
|
||||
// Disable other features too.
|
||||
document.body.classList.remove('ffz-transparent-badges');
|
||||
|
@ -93,12 +103,6 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
this._draw_following_channels();
|
||||
}
|
||||
|
||||
// Remove Sub Count
|
||||
if ( this.is_dashboard )
|
||||
this._update_subscribers();
|
||||
|
||||
document.body.classList.add('ffz-bttv');
|
||||
|
||||
// Send Message Behavior
|
||||
var original_send = BetterTTV.chat.helpers.sendMessage, f = this;
|
||||
BetterTTV.chat.helpers.sendMessage = function(message) {
|
||||
|
@ -197,6 +201,41 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
}
|
||||
};
|
||||
|
||||
// Emoji!
|
||||
var parse_emoji = function(token) {
|
||||
var setting = f.settings.parse_emoji,
|
||||
output = [],
|
||||
segments = token.split(constants.EMOJI_REGEX),
|
||||
text = null;
|
||||
|
||||
if ( setting === 0 )
|
||||
return [token];
|
||||
|
||||
while(segments.length) {
|
||||
text = (text || '') + segments.shift();
|
||||
if ( segments.length ) {
|
||||
var match = segments.shift(),
|
||||
eid = utils.emoji_to_codepoint(match),
|
||||
data = f.emoji_data[eid],
|
||||
src = data && (setting === 3 ? data.one_src : (setting === 2 ? data.noto_src : data.tw_src));
|
||||
|
||||
if ( src ) {
|
||||
if ( text && text.length )
|
||||
output.push(text);
|
||||
var code = utils.quote_attr(data.raw);
|
||||
output.push(['<img class="emoticon emoji ffz-tooltip" height="18px" data-ffz-emoji="' + eid + '" src="' + utils.quote_attr(src) + '" data-regex="' + code + '" alt="' + code + '">']);
|
||||
text = null;
|
||||
} else
|
||||
text = (text || '') + match;
|
||||
}
|
||||
}
|
||||
|
||||
if ( text && text.length )
|
||||
output.push(text);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Emoticonize
|
||||
var original_emoticonize = BetterTTV.chat.templates.emoticonize;
|
||||
BetterTTV.chat.templates.emoticonize = function(message, emotes) {
|
||||
|
@ -206,106 +245,64 @@ FFZ.prototype.setup_bttv = function(delay) {
|
|||
l_room = room && room.toLowerCase(),
|
||||
l_sender = received_sender && received_sender.toLowerCase(),
|
||||
sets = f.getEmotes(l_sender, l_room),
|
||||
emotes = [],
|
||||
emotes = {}, emote,
|
||||
user = f.get_user(),
|
||||
new_tokens = [],
|
||||
mine = user && user.login === l_sender;
|
||||
|
||||
// Build a list of emotes that match.
|
||||
_.each(sets, function(set_id) {
|
||||
var set = f.emote_sets[set_id];
|
||||
if ( ! set )
|
||||
return;
|
||||
// Build an object with all of our emotes.
|
||||
for(var i=0; i < sets.length; i++) {
|
||||
var emote_set = f.emote_sets[sets[i]];
|
||||
if ( emote_set && emote_set.emoticons )
|
||||
for(var emote_id in emote_set.emoticons) {
|
||||
emote = emote_set.emoticons[emote_id];
|
||||
if ( ! emotes[emote.name] )
|
||||
emotes[emote.name] = emote;
|
||||
}
|
||||
}
|
||||
|
||||
_.each(set.emoticons, function(emote) {
|
||||
_.any(tokens, function(token) {
|
||||
return _.isString(token) && token.match(emote.regex);
|
||||
}) && emotes.push(emote);
|
||||
});
|
||||
});
|
||||
|
||||
// Don't bother proceeding if we have no emotes.
|
||||
if ( emotes.length ) {
|
||||
// Why is emote parsing so bad? ;_;
|
||||
_.each(emotes, function(emote) {
|
||||
var tooltip = f._emote_tooltip(emote),
|
||||
eo = ['<img class="emoticon html-tooltip" data-ffz-emote="' + emote.id + '" srcset="' + utils.quote_attr(emote.srcSet || "") + '" src="' + utils.quote_attr(emote.urls[1]) + '" data-regex="' + utils.quote_attr(emote.name) + '" title="' + utils.quote_attr(tooltip) + '">'],
|
||||
old_tokens = tokens;
|
||||
|
||||
tokens = [];
|
||||
|
||||
for(var i=0; i < old_tokens.length; i++) {
|
||||
var token = old_tokens[i];
|
||||
for(var i=0, l=tokens.length; i < l; i++) {
|
||||
var token = tokens[i];
|
||||
if ( typeof token !== "string" ) {
|
||||
tokens.push(token);
|
||||
new_tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
var tbits = token.split(emote.regex);
|
||||
while(tbits.length) {
|
||||
var bit = tbits.shift();
|
||||
if ( tbits.length ) {
|
||||
bit += tbits.shift();
|
||||
if ( bit )
|
||||
tokens.push(bit);
|
||||
// Split the token!
|
||||
var segments = token.split(' '),
|
||||
text = [], segment;
|
||||
|
||||
tbits.shift();
|
||||
tokens.push(eo);
|
||||
for(var x=0,y=segments.length; x < y; x++) {
|
||||
segment = segments[x];
|
||||
emote = emotes[segment];
|
||||
|
||||
if ( emote ) {
|
||||
if ( text.length ) {
|
||||
var toks = parse_emoji(text.join(' ') + ' ');
|
||||
for(var q=0; q < toks.length; q++)
|
||||
new_tokens.push(toks[q]);
|
||||
|
||||
text = [];
|
||||
}
|
||||
|
||||
new_tokens.push(['<img class="emoticon ffz-tooltip" data-ffz-set="' + emote.set_id + '" data-ffz-emote="' + emote.id + '" srcset="' + utils.quote_attr(emote.srcSet || "") + '" src="' + utils.quote_attr(emote.urls[1]) + '" data-regex="' + utils.quote_attr(emote.name) + '">']);
|
||||
|
||||
if ( mine && l_room )
|
||||
f.add_usage(l_room, emote);
|
||||
|
||||
text.push('');
|
||||
} else
|
||||
tokens.push(bit);
|
||||
}
|
||||
}
|
||||
});
|
||||
text.push(segment);
|
||||
}
|
||||
|
||||
// Sneak in Emojicon Processing
|
||||
if ( f.settings.parse_emoji && f.emoji_data ) {
|
||||
var old_tokens = tokens,
|
||||
setting = f.settings.parse_emoji;
|
||||
|
||||
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' )
|
||||
tokens.push(match);
|
||||
else {
|
||||
var eid = utils.emoji_to_codepoint(match, variant),
|
||||
data = f.emoji_data[eid],
|
||||
src = data && (setting === 2 ? data.noto_src : data.tw_src);
|
||||
|
||||
if ( data && src ) {
|
||||
var image = src && f.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + src + '">' : '',
|
||||
tooltip = image + "Emoji: " + data.raw + "<br>Name: " + data.name + (data.short_name ? "<br>Short Name: :" + data.short_name + ":" : ""),
|
||||
code = utils.quote_attr(data.raw);
|
||||
|
||||
tokens.push(['<img class="emoticon emoji html-tooltip" height="18px" data-ffz-emoji="' + eid + '" src="' + utils.quote_attr(src) + '" data-regex="' + code + '" alt="' + code + '" title="' + utils.quote_attr(tooltip) + '">']);
|
||||
} else
|
||||
tokens.push(match + (variant || ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( text.length > 1 || (text.length === 1 && text[0] !== '') ) {
|
||||
var toks = parse_emoji(text.join(' ') + ' ');
|
||||
for(var q=0; q < toks.length; q++)
|
||||
new_tokens.push(toks[q]);
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
return new_tokens;
|
||||
}
|
||||
|
||||
this.update_ui_link();
|
||||
|
|
|
@ -99,6 +99,7 @@ FFZ.prototype.find_rechat = function() {
|
|||
// Tooltips
|
||||
jQuery(container).find('.tooltip').tipsy({live: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(container).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery(container).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
// Load the room data.
|
||||
var room_id = el.getAttribute('data-room');
|
||||
|
@ -234,20 +235,26 @@ FFZ.prototype.process_rechat_line = function(line, reprocess) {
|
|||
else if ( node.nodeType === node.ELEMENT_NODE ) {
|
||||
if ( node.tagName === 'IMG' )
|
||||
tokens.push({
|
||||
type: "emoticon",
|
||||
altText: node.alt,
|
||||
emoticonSrc: node.src
|
||||
imgSrc: node.src
|
||||
});
|
||||
|
||||
else if ( node.tagName === 'A' )
|
||||
tokens.push({
|
||||
isLink: true,
|
||||
href: node.textContent
|
||||
type: "link",
|
||||
isDeleted: false,
|
||||
isLong: false,
|
||||
length: node.textContent.length,
|
||||
link: node.href,
|
||||
text: node.textContent
|
||||
});
|
||||
|
||||
else if ( node.tagName === 'SPAN' )
|
||||
tokens.push({
|
||||
mentionedUser: node.textContent,
|
||||
own: node.classList.contains('mentioning')
|
||||
type: "mention",
|
||||
user: node.textContent,
|
||||
isOwnMessage: node.classList.contains('mentioning')
|
||||
});
|
||||
|
||||
else {
|
||||
|
|
|
@ -69,7 +69,7 @@ FFZ.prototype._feature_friday_ui = function(room_id, parent, view) {
|
|||
btn.classList.toggle('live', ff.live);
|
||||
btn.classList.toggle('blue', this.has_bttv && BetterTTV.settings.get('showBlueButtons'));
|
||||
|
||||
btn.href = "http://www.twitch.tv/" + ff.channel;
|
||||
btn.href = "//www.twitch.tv/" + ff.channel;
|
||||
btn.title = message;
|
||||
btn.target = "_new";
|
||||
btn.innerHTML = "<span>" + message + "</span>";
|
||||
|
@ -122,7 +122,7 @@ FFZ.prototype._update_ff_live = function() {
|
|||
return;
|
||||
|
||||
var f = this;
|
||||
Twitch.api.get("streams/" + this.feature_friday.channel)
|
||||
utils.api.get("streams/" + this.feature_friday.channel)
|
||||
.done(function(data) {
|
||||
f.feature_friday.live = data.stream != null;
|
||||
f.update_ui_link();
|
||||
|
|
97
src/main.js
97
src/main.js
|
@ -12,6 +12,16 @@ var FFZ = window.FrankerFaceZ = function() {
|
|||
this._log_data = [];
|
||||
this._apis = {};
|
||||
|
||||
// Error Logging
|
||||
var t = this;
|
||||
window.addEventListener('error', function(event) {
|
||||
if ( ! event.error )
|
||||
return;
|
||||
|
||||
var has_stack = event.error && event.error.stack;
|
||||
t.log("JavaScript Error: " + event.message + " [" + event.filename + ":" + event.lineno + ":" + event.colno + "]", has_stack ? event.error.stack : undefined, false, has_stack);
|
||||
});
|
||||
|
||||
// Get things started.
|
||||
this.initialize();
|
||||
}
|
||||
|
@ -19,10 +29,13 @@ var FFZ = window.FrankerFaceZ = function() {
|
|||
|
||||
FFZ.get = function() { return FFZ.instance; }
|
||||
|
||||
// TODO: This should be in a module.
|
||||
FFZ.msg_commands = {};
|
||||
|
||||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 100,
|
||||
major: 3, minor: 5, revision: 133,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
@ -32,36 +45,38 @@ var VER = FFZ.version_info = {
|
|||
// Logging
|
||||
|
||||
FFZ.prototype.log = function(msg, data, to_json, log_json) {
|
||||
msg = "FFZ: " + msg + (to_json ? " -- " + JSON.stringify(data) : "");
|
||||
if ( to_json )
|
||||
msg = msg + ' -- ' + JSON.stringify(data);
|
||||
|
||||
this._log_data.push(msg + ((!to_json && log_json) ? " -- " + JSON.stringify(data) : ""));
|
||||
|
||||
if ( data !== undefined && console.groupCollapsed && console.dir ) {
|
||||
console.groupCollapsed(msg);
|
||||
console.groupCollapsed("FFZ: " + msg);
|
||||
if ( navigator.userAgent.indexOf("Firefox/") !== -1 )
|
||||
console.log(data);
|
||||
else
|
||||
console.dir(data);
|
||||
|
||||
console.groupEnd(msg);
|
||||
console.groupEnd("FFZ: " + msg);
|
||||
} else
|
||||
console.log(msg);
|
||||
console.log("FFZ: " + msg);
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.error = function(msg, data, to_json) {
|
||||
msg = "FFZ Error: " + msg + (to_json ? " -- " + JSON.stringify(data) : "");
|
||||
this._log_data.push(msg);
|
||||
FFZ.prototype.error = function(msg, data, to_json, log_json) {
|
||||
msg = "Error: " + msg + (to_json ? " -- " + JSON.stringify(data) : "");
|
||||
this._log_data.push(msg + ((!to_json && log_json) ? " -- " + JSON.stringify(data) : ""));
|
||||
|
||||
if ( data !== undefined && console.groupCollapsed && console.dir ) {
|
||||
console.groupCollapsed(msg);
|
||||
console.groupCollapsed("FFZ " + msg);
|
||||
if ( navigator.userAgent.indexOf("Firefox/") !== -1 )
|
||||
console.log(data);
|
||||
else
|
||||
console.dir(data);
|
||||
|
||||
console.groupEnd(msg);
|
||||
console.groupEnd("FFZ " + msg);
|
||||
} else
|
||||
console.assert(false, msg);
|
||||
console.assert(false, "FFZ " + msg);
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,9 +93,9 @@ FFZ.prototype.paste_logs = function() {
|
|||
FFZ.prototype._pastebin = function(data, callback) {
|
||||
jQuery.ajax({url: "http://putco.de/", type: "PUT", data: data, context: this})
|
||||
.success(function(e) {
|
||||
callback.bind(this)(e.trim() + ".log");
|
||||
callback.call(this, e.trim() + ".log");
|
||||
}).fail(function(e) {
|
||||
callback.bind(this)(null);
|
||||
callback.call(this, null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -130,6 +145,7 @@ require('./ember/router');
|
|||
require('./ember/channel');
|
||||
require('./ember/player');
|
||||
require('./ember/room');
|
||||
require('./ember/vod-chat');
|
||||
require('./ember/layout');
|
||||
require('./ember/line');
|
||||
require('./ember/chatview');
|
||||
|
@ -143,7 +159,7 @@ require('./ember/following');
|
|||
|
||||
require('./debug');
|
||||
|
||||
require('./ext/rechat');
|
||||
//require('./ext/rechat');
|
||||
require('./ext/betterttv');
|
||||
require('./ext/emote_menu');
|
||||
|
||||
|
@ -156,6 +172,7 @@ require('./ui/tooltips');
|
|||
require('./ui/notifications');
|
||||
require('./ui/viewer_count');
|
||||
require('./ui/sub_count');
|
||||
require('./ui/dash_stats');
|
||||
|
||||
require('./ui/menu_button');
|
||||
require('./ui/following');
|
||||
|
@ -182,6 +199,14 @@ FFZ.prototype.initialize = function(increment, delay) {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check for the transfer page.
|
||||
if ( location.pathname === "/crossdomain/transfer" ) {
|
||||
if ( location.hash.indexOf("ffz-settings-transfer") !== -1 )
|
||||
this.init_settings_transfer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for special non-ember pages.
|
||||
if ( /^\/(?:$|search$|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) ) {
|
||||
this.init_normal(delay);
|
||||
|
@ -218,9 +243,20 @@ FFZ.prototype.initialize = function(increment, delay) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype.init_settings_transfer = function() {
|
||||
this.log("This is the HTTP Transfer URL. Building a settings backup and posting it to our parent.");
|
||||
this.load_settings();
|
||||
try { this.setup_line(); } catch(err) { }
|
||||
var msg = {from_ffz: true, command: "http_settings", data: this._get_settings_object()};
|
||||
window.opener.postMessage(msg, "https://www.twitch.tv");
|
||||
window.close();
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.init_player = function(delay) {
|
||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
this.log("Found Twitch Player after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
this.log("Found Twitch Player after " + (delay||0) + " ms at: " + location);
|
||||
this.log("Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
|
||||
this.users = {};
|
||||
this.is_dashboard = false;
|
||||
|
@ -243,7 +279,8 @@ FFZ.prototype.init_player = function(delay) {
|
|||
|
||||
FFZ.prototype.init_normal = function(delay, no_socket) {
|
||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
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 at: " + location);
|
||||
this.log("Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
|
||||
this.users = {};
|
||||
this.is_dashboard = false;
|
||||
|
@ -272,6 +309,7 @@ FFZ.prototype.init_normal = function(delay, no_socket) {
|
|||
this.setup_following_count(false);
|
||||
this.setup_menu();
|
||||
|
||||
this.setup_message_event();
|
||||
this.fix_tooltips();
|
||||
this.find_bttv(10);
|
||||
|
||||
|
@ -286,7 +324,11 @@ FFZ.prototype.is_dashboard = false;
|
|||
|
||||
FFZ.prototype.init_dashboard = function(delay) {
|
||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
this.log("Found Twitch Dashboard after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
this.log("Found Twitch Dashboard after " + (delay||0) + " ms at: " + location);
|
||||
this.log("Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
|
||||
var match = location.pathname.match(/\/([^\/]+)/);
|
||||
this.dashboard_channel = match && match[1] || undefined;
|
||||
|
||||
this.users = {};
|
||||
this.is_dashboard = true;
|
||||
|
@ -311,6 +353,7 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
this.setup_notifications();
|
||||
this.setup_following_count(false);
|
||||
this.setup_menu();
|
||||
this.setup_dash_stats();
|
||||
|
||||
this._update_subscribers();
|
||||
|
||||
|
@ -329,7 +372,8 @@ FFZ.prototype.init_dashboard = function(delay) {
|
|||
|
||||
FFZ.prototype.init_ember = function(delay) {
|
||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
||||
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 at: " + location);
|
||||
this.log("Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||
|
||||
this.users = {};
|
||||
this.is_dashboard = false;
|
||||
|
@ -367,6 +411,7 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
this.setup_player();
|
||||
this.setup_channel();
|
||||
this.setup_room();
|
||||
this.setup_vod_chat();
|
||||
this.setup_line();
|
||||
this.setup_layout();
|
||||
this.setup_chatview();
|
||||
|
@ -389,12 +434,14 @@ FFZ.prototype.init_ember = function(delay) {
|
|||
this.fix_tooltips();
|
||||
this.connect_extra_chat();
|
||||
|
||||
this.setup_rechat();
|
||||
//this.setup_rechat();
|
||||
this.setup_message_event();
|
||||
this.find_bttv(10);
|
||||
this.find_emote_menu(10);
|
||||
|
||||
//this.check_news();
|
||||
this.check_ff();
|
||||
this.refresh_chat();
|
||||
|
||||
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
|
||||
duration = end - start;
|
||||
|
@ -414,8 +461,16 @@ FFZ.prototype.setup_message_event = function() {
|
|||
|
||||
|
||||
FFZ.prototype._on_window_message = function(e) {
|
||||
if ( ! e.data || ! e.data.from_ffz )
|
||||
var msg = e.data;
|
||||
if ( typeof msg === "string" )
|
||||
msg = JSON.parse(msg);
|
||||
|
||||
if ( ! msg || ! msg.from_ffz )
|
||||
return;
|
||||
|
||||
var msg = e.data;
|
||||
var handler = FFZ.msg_commands[msg.command];
|
||||
if ( handler )
|
||||
handler.call(this, msg.data);
|
||||
else
|
||||
this.log("Invalid Message: " + msg.command, msg.data, false, true);
|
||||
}
|
805
src/settings.js
805
src/settings.js
|
@ -1,39 +1,30 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require("./constants"),
|
||||
FileSaver = require("./FileSaver");
|
||||
utils = require("./utils"),
|
||||
FileSaver = require("./FileSaver"),
|
||||
|
||||
createElement = document.createElement.bind(document),
|
||||
|
||||
make_ls = function(key) {
|
||||
return "ffz_setting_" + key;
|
||||
},
|
||||
|
||||
toggle_setting = function(swit, key) {
|
||||
var val = ! this.settings.get(key);
|
||||
this.settings.set(key, val);
|
||||
swit.classList.toggle('active', val);
|
||||
},
|
||||
|
||||
option_setting = function(select, key) {
|
||||
this.settings.set(key, JSON.parse(select.options[select.selectedIndex].value));
|
||||
},
|
||||
|
||||
|
||||
toggle_basic_setting = function(swit, key) {
|
||||
var getter = FFZ.basic_settings[key].get,
|
||||
val = !(typeof getter === 'function' ? getter.bind(this)() : this.settings.get(getter)),
|
||||
|
||||
setter = FFZ.basic_settings[key].set;
|
||||
|
||||
if ( typeof setter === 'function' )
|
||||
setter.bind(this)(val);
|
||||
toggle_setting = function(swit, key, info) {
|
||||
var val = !(info.get ? (typeof info.get === 'function' ? info.get.call(this) : this.settings.get(info.get)) : this.settings.get(key));
|
||||
if ( typeof info.set === "function" )
|
||||
info.set.call(this, val);
|
||||
else
|
||||
this.settings.set(setter, val);
|
||||
this.settings.set(info.set || key, val);
|
||||
|
||||
swit.classList.toggle('active', val);
|
||||
},
|
||||
|
||||
option_basic_setting = function(select, key) {
|
||||
FFZ.basic_settings[key].set.bind(this)(JSON.parse(select.options[select.selectedIndex].value));
|
||||
option_setting = function(select, key, info) {
|
||||
var val = JSON.parse(select.options[select.selectedIndex].value);
|
||||
if ( typeof info.set === "function" )
|
||||
info.set.call(this, val);
|
||||
else
|
||||
this.settings.set(info.set || key, val);
|
||||
};
|
||||
|
||||
|
||||
|
@ -59,11 +50,17 @@ FFZ.prototype.load_settings = function() {
|
|||
this.settings.del = this._setting_del.bind(this);
|
||||
this.settings.load = this._setting_load.bind(this);
|
||||
|
||||
var found_settings = false;
|
||||
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
this._setting_load(key);
|
||||
var info = FFZ.settings_info[key],
|
||||
ls_key = info && info.storage_key || make_ls(key);
|
||||
|
||||
found_settings = found_settings || localStorage.hasOwnProperty(key);
|
||||
this._setting_load(key) || found_settings;
|
||||
}
|
||||
|
||||
// Listen for Changes
|
||||
|
@ -75,11 +72,19 @@ FFZ.prototype.load_settings = function() {
|
|||
// Backup and Restore
|
||||
// --------------------
|
||||
|
||||
FFZ.prototype._settings_open_http_window = function() {
|
||||
window.open("http://www.twitch.tv/crossdomain/transfer#ffz-settings-transfer", "_ffz_settings");
|
||||
}
|
||||
|
||||
FFZ.msg_commands.http_settings = function(data) {
|
||||
this._load_settings_file(data);
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.reset_settings = function() {
|
||||
if ( ! confirm(this.tr('Are you sure you wish to reset FrankerFaceZ?\n\nThis will force the tab to refresh.')) )
|
||||
return;
|
||||
|
||||
|
||||
// Clear Settings
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
|
@ -94,13 +99,12 @@ FFZ.prototype.reset_settings = function() {
|
|||
|
||||
// TODO: Filters
|
||||
|
||||
|
||||
// Refresh
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.save_settings_file = function() {
|
||||
FFZ.prototype._get_settings_object = function() {
|
||||
var data = {
|
||||
version: 1,
|
||||
script_version: FFZ.version_info + '',
|
||||
|
@ -120,7 +124,15 @@ FFZ.prototype.save_settings_file = function() {
|
|||
data.settings[key] = this.settings[key];
|
||||
}
|
||||
|
||||
var blob = new Blob([JSON.stringify(data, null, 4)], {type: "application/json;charset=utf-8"});
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.save_settings_file = function() {
|
||||
var data = this._get_settings_object(),
|
||||
blob = new Blob(
|
||||
[JSON.stringify(data, null, 4)], {type: "application/json;charset=utf-8"});
|
||||
|
||||
FileSaver.saveAs(blob, "ffz-settings.json");
|
||||
}
|
||||
|
||||
|
@ -137,12 +149,15 @@ FFZ.prototype.load_settings_file = function(file) {
|
|||
}
|
||||
}
|
||||
|
||||
FFZ.prototype._load_settings_file = function(data) {
|
||||
FFZ.prototype._load_settings_file = function(data, hide_alert) {
|
||||
if ( typeof data === "string" )
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(err) {
|
||||
this.error("Error Loading Settings: " + err);
|
||||
return alert("There was an error attempting to read the provided settings data.");
|
||||
if ( ! hide_alert )
|
||||
alert("There was an error attempting to read the provided settings data.");
|
||||
return [-1,-1,-1];
|
||||
}
|
||||
|
||||
this.log("Loading Settings Data", data);
|
||||
|
@ -161,7 +176,7 @@ FFZ.prototype._load_settings_file = function(data) {
|
|||
val = data.settings[key];
|
||||
|
||||
if ( info.process_value )
|
||||
val = info.process_value.bind(this)(val);
|
||||
val = info.process_value.call(this, val);
|
||||
|
||||
if ( val !== this.settings.get(key) )
|
||||
this.settings.set(key, val);
|
||||
|
@ -188,9 +203,12 @@ FFZ.prototype._load_settings_file = function(data) {
|
|||
}
|
||||
|
||||
// Do this in a timeout so that any styles have a moment to update.
|
||||
if ( ! hide_alert )
|
||||
setTimeout(function(){
|
||||
alert('Successfully loaded ' + applied.length + ' settings and skipped ' + skipped.length + ' settings. Added ' + aliases + ' user nicknames.');
|
||||
});
|
||||
|
||||
return [applied.length, skipped.length, aliases];
|
||||
}
|
||||
|
||||
|
||||
|
@ -198,112 +216,274 @@ FFZ.prototype._load_settings_file = function(data) {
|
|||
// Menu Page
|
||||
// --------------------
|
||||
|
||||
FFZ.menu_pages.settings = {
|
||||
render: function(view, container) {
|
||||
// Bottom Bar
|
||||
var menu = document.createElement('ul'),
|
||||
page = document.createElement('div'),
|
||||
var is_android = navigator.userAgent.indexOf('Android') !== -1,
|
||||
settings_renderer = function(settings_data, collapsable, collapsed_key) {
|
||||
return function(view, container) {
|
||||
var f = this,
|
||||
settings = {},
|
||||
categories = [];
|
||||
|
||||
tab_basic = document.createElement('li'),
|
||||
link_basic = document.createElement('a'),
|
||||
for(var key in settings_data) {
|
||||
var info = settings_data[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cat_store = settings[cat];
|
||||
|
||||
tab_adv = document.createElement('li'),
|
||||
link_adv = document.createElement('a'),
|
||||
if ( info.hasOwnProperty('visible') ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof visible === "function" )
|
||||
visible = visible.call(this);
|
||||
|
||||
tab_save = document.createElement('li'),
|
||||
link_save = document.createElement('a'),
|
||||
|
||||
height = parseInt(container.style.maxHeight || '0');
|
||||
|
||||
|
||||
// Height Calculation
|
||||
if ( ! height )
|
||||
height = Math.max(200, view.$().height() - 172);
|
||||
|
||||
if ( height && height !== NaN ) {
|
||||
height -= 37;
|
||||
page.style.maxHeight = height + 'px';
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
// Menu Building
|
||||
page.className = 'ffz-ui-sub-menu-page';
|
||||
menu.className = 'menu sub-menu clearfix';
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
tab_basic.className = 'item';
|
||||
tab_basic.id = 'ffz-settings-page-basic';
|
||||
link_basic.innerHTML = 'Basic';
|
||||
tab_basic.appendChild(link_basic);
|
||||
if ( ! cat_store ) {
|
||||
categories.push(cat);
|
||||
cat_store = settings[cat] = [];
|
||||
}
|
||||
|
||||
tab_adv.className = 'item';
|
||||
tab_adv.id = 'ffz-settings-page-advanced';
|
||||
link_adv.innerHTML = 'Advanced';
|
||||
tab_adv.appendChild(link_adv);
|
||||
cat_store.push([key, info]);
|
||||
}
|
||||
|
||||
tab_save.className = 'item';
|
||||
tab_save.id = 'ffz-settings-page-save';
|
||||
link_save.textContent = 'Backup & Restore';
|
||||
tab_save.appendChild(link_save);
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
|
||||
menu.appendChild(tab_basic);
|
||||
menu.appendChild(tab_adv);
|
||||
menu.appendChild(tab_save);
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
|
||||
var cp = FFZ.menu_pages.settings.change_page;
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
|
||||
link_basic.addEventListener('click', cp.bind(this, view, container, menu, page, 'basic'));
|
||||
link_adv.addEventListener('click', cp.bind(this, view, container, menu, page, 'advanced'));
|
||||
link_save.addEventListener('click', cp.bind(this, view, container, menu, page, 'save'));
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
if ( this.settings.advanced_settings )
|
||||
link_adv.click();
|
||||
else
|
||||
link_basic.click();
|
||||
var current_category = (collapsed_key ? this[collapsed_key] : null) || categories[0];
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
|
||||
bttv_skipped = [],
|
||||
added = 0,
|
||||
|
||||
menu = createElement('div'),
|
||||
heading = createElement('div');
|
||||
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content';
|
||||
menu.setAttribute('data-category', category);
|
||||
|
||||
if ( collapsable ) {
|
||||
menu.classList.add('collapsable');
|
||||
menu.classList.toggle('collapsed', current_category !== category);
|
||||
menu.addEventListener('click', function() {
|
||||
var t = this;
|
||||
if ( ! t.classList.contains('collapsed') )
|
||||
return;
|
||||
|
||||
jQuery(".chat-menu-content:not(.collapsed)", container).addClass("collapsed");
|
||||
t.classList.remove('collapsed');
|
||||
if ( collapsed_key )
|
||||
f[collapsed_key] = t.getAttribute('data-category');
|
||||
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});
|
||||
}
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = 2, //a.type === "boolean" ? 1 : 2,
|
||||
bt = 2, //b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = createElement('p'),
|
||||
val = info.get ? (typeof info.get === 'function' ? info.get.call(this) : this.settings.get(info.get)) : this.settings.get(key);
|
||||
|
||||
el.className = 'clearfix';
|
||||
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
bttv_skipped.push([info.name, info.help]);
|
||||
continue;
|
||||
} else {
|
||||
if ( info.type === "boolean" ) {
|
||||
var swit = createElement('a'),
|
||||
label = createElement('span');
|
||||
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.appendChild(createElement('span'))
|
||||
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
|
||||
swit.addEventListener('click', toggle_setting.bind(this, swit, key, info))
|
||||
|
||||
} else if ( info.type === "select" ) {
|
||||
var select = createElement('select'),
|
||||
label = createElement('span');
|
||||
|
||||
label.className = 'option-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
for(var ok in info.options) {
|
||||
var op = createElement('option');
|
||||
op.value = JSON.stringify(ok);
|
||||
if ( val == ok )
|
||||
op.setAttribute('selected', true);
|
||||
op.innerHTML = info.options[ok];
|
||||
select.appendChild(op);
|
||||
}
|
||||
|
||||
select.addEventListener('change', option_setting.bind(this, select, key, info));
|
||||
|
||||
el.appendChild(label);
|
||||
el.appendChild(select);
|
||||
|
||||
} else if ( typeof info.method === "function" ) {
|
||||
el.classList.add("option");
|
||||
var link = createElement('a');
|
||||
link.innerHTML = info.name;
|
||||
link.href = '#';
|
||||
el.appendChild(link);
|
||||
|
||||
link.addEventListener('click', info.method.bind(this));
|
||||
|
||||
} else
|
||||
continue;
|
||||
|
||||
if ( info.help || (this.has_bttv && info.warn_bttv) ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = (this.has_bttv && info.warn_bttv ? '<i>' + info.warn_bttv + (info.help ? '</i><br>' : '</i>') : '') + (info.help || "");
|
||||
el.appendChild(help);
|
||||
}
|
||||
}
|
||||
|
||||
added++;
|
||||
menu.appendChild(el);
|
||||
}
|
||||
|
||||
if ( ! added )
|
||||
continue;
|
||||
|
||||
if ( bttv_skipped.length ) {
|
||||
var el = createElement('p'),
|
||||
label = createElement('span'),
|
||||
help = createElement('span');
|
||||
|
||||
el.className = 'bttv-incompatibility clearfix disabled';
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = "Features Incompatible with BetterTTV";
|
||||
|
||||
help.className = 'help';
|
||||
for(var i=0; i < bttv_skipped.length; i++) {
|
||||
var skipped = bttv_skipped[i];
|
||||
help.innerHTML += (i > 0 ? ', ' : '') + '<b' + (skipped[1] ? ' class="html-tooltip" title="' + utils.quote_attr(skipped[1]) + '"' : '') + '>' + skipped[0] + '</b>';
|
||||
}
|
||||
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
menu.appendChild(el);
|
||||
jQuery('.html-tooltip', el).tipsy({html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
jQuery('.ffz-tooltip', el).tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
container.appendChild(page);
|
||||
container.appendChild(menu);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
change_page: function(view, container, menu, page, key) {
|
||||
page.innerHTML = '';
|
||||
page.setAttribute('data-page', key);
|
||||
render_basic = settings_renderer(FFZ.basic_settings, false, '_ffz_basic_settings_page'),
|
||||
render_advanced = settings_renderer(FFZ.settings_info, true, '_ffz_settings_page');
|
||||
|
||||
var els = menu.querySelectorAll('li.active');
|
||||
for(var i=0, l = els.length; i < l; i++)
|
||||
els[i].classList.remove('active');
|
||||
|
||||
var el = menu.querySelector('#ffz-settings-page-' + key);
|
||||
if ( el )
|
||||
el.classList.add('active');
|
||||
FFZ.menu_pages.settings = {
|
||||
name: "Settings",
|
||||
icon: constants.GEAR,
|
||||
sort_order: 99999,
|
||||
wide: true,
|
||||
|
||||
FFZ.menu_pages.settings['render_' + key].bind(this)(view, page);
|
||||
default_page: function() { return this.settings.advanced_settings ? 'advanced' : 'basic' },
|
||||
|
||||
if ( key === 'advanced' )
|
||||
this.settings.set('advanced_settings', true);
|
||||
else if ( key === 'basic' )
|
||||
this.settings.set('advanced_settings', false);
|
||||
pages: {
|
||||
basic: {
|
||||
name: "Basic",
|
||||
sort_order: 1,
|
||||
|
||||
render: function(view, container) {
|
||||
this.settings.set("advanced_settings", false);
|
||||
return render_basic.call(this, view, container);
|
||||
}
|
||||
},
|
||||
|
||||
render_save: function(view, container) {
|
||||
var backup_head = document.createElement('div'),
|
||||
restore_head = document.createElement('div'),
|
||||
reset_head = document.createElement('div'),
|
||||
advanced: {
|
||||
name: "Advanced",
|
||||
sort_order: 2,
|
||||
|
||||
backup_cont = document.createElement('div'),
|
||||
restore_cont = document.createElement('div'),
|
||||
reset_cont = document.createElement('div'),
|
||||
render: function(view, container) {
|
||||
this.settings.set("advanced_settings", true);
|
||||
return render_advanced.call(this, view, container);
|
||||
}
|
||||
},
|
||||
|
||||
backup_para = document.createElement('p'),
|
||||
backup_link = document.createElement('a'),
|
||||
backup_help = document.createElement('span'),
|
||||
backup: {
|
||||
name: "Backup & Restore",
|
||||
sort_order: 3,
|
||||
|
||||
restore_para = document.createElement('p'),
|
||||
restore_input = document.createElement('input'),
|
||||
restore_link = document.createElement('a'),
|
||||
restore_help = document.createElement('span'),
|
||||
render: function(view, container) {
|
||||
var backup_head = createElement('div'),
|
||||
restore_head = createElement('div'),
|
||||
reset_head = createElement('div'),
|
||||
|
||||
reset_para = document.createElement('p'),
|
||||
reset_link = document.createElement('a'),
|
||||
reset_help = document.createElement('span'),
|
||||
backup_cont = createElement('div'),
|
||||
restore_cont = createElement('div'),
|
||||
reset_cont = createElement('div'),
|
||||
|
||||
backup_para = createElement('p'),
|
||||
backup_link = createElement('a'),
|
||||
backup_help = createElement('span'),
|
||||
|
||||
http_para = createElement('p'),
|
||||
http_link = createElement('a'),
|
||||
http_help = createElement('span'),
|
||||
|
||||
restore_para = createElement('p'),
|
||||
restore_input = createElement('input'),
|
||||
restore_link = createElement('a'),
|
||||
restore_help = createElement('span'),
|
||||
|
||||
reset_para = createElement('p'),
|
||||
reset_link = createElement('a'),
|
||||
reset_help = createElement('span'),
|
||||
f = this;
|
||||
|
||||
|
||||
|
@ -346,6 +526,20 @@ FFZ.menu_pages.settings = {
|
|||
restore_para.appendChild(restore_help);
|
||||
restore_cont.appendChild(restore_para);
|
||||
|
||||
http_para.className = 'clearfix option';
|
||||
http_link.href = '#';
|
||||
http_link.innerHTML = 'Import from HTTP';
|
||||
http_link.addEventListener('click', this._settings_open_http_window.bind(this));
|
||||
|
||||
http_help.className = 'help';
|
||||
http_help.innerHTML = 'Load your settings from HTTP into HTTPS. (This briefly opens a new window.)';
|
||||
|
||||
http_para.appendChild(http_link);
|
||||
http_para.appendChild(http_help);
|
||||
|
||||
if ( location.protocol === "https:" )
|
||||
restore_cont.appendChild(http_para);
|
||||
|
||||
reset_cont.className = 'chat-menu-content';
|
||||
reset_head.className = 'heading';
|
||||
reset_head.innerHTML = this.tr('Reset Settings');
|
||||
|
@ -367,383 +561,9 @@ FFZ.menu_pages.settings = {
|
|||
container.appendChild(backup_cont);
|
||||
container.appendChild(restore_cont);
|
||||
container.appendChild(reset_cont);
|
||||
},
|
||||
|
||||
render_basic: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
|
||||
for(var key in FFZ.basic_settings) {
|
||||
if ( ! FFZ.basic_settings.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
var info = FFZ.basic_settings[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cs = settings[cat];
|
||||
|
||||
if ( info.visible !== undefined && info.visible !== null ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof info.visible == "function" )
|
||||
visible = info.visible.bind(this)();
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
if ( ! cs ) {
|
||||
categories.push(cat);
|
||||
cs = settings[cat] = [];
|
||||
}
|
||||
|
||||
cs.push([key, info]);
|
||||
}
|
||||
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
var f = this,
|
||||
current_page = this._ffz_basic_settings_page || categories[0];
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
|
||||
menu = document.createElement('div'),
|
||||
heading = document.createElement('div');
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content'; // collapsable';
|
||||
|
||||
menu.setAttribute('data-category', category);
|
||||
//menu.classList.toggle('collapsed', current_page !== category);
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
|
||||
/*menu.addEventListener('click', function() {
|
||||
if ( ! this.classList.contains('collapsed') )
|
||||
return;
|
||||
|
||||
var t = this,
|
||||
old_selection = container.querySelectorAll('.chat-menu-content:not(.collapsed)');
|
||||
for(var i=0; i < old_selection.length; i++)
|
||||
old_selection[i].classList.add('collapsed');
|
||||
|
||||
f._ffz_basic_settings_page = t.getAttribute('data-category');
|
||||
t.classList.remove('collapsed');
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});*/
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = a.type === "boolean" ? 1 : 2,
|
||||
bt = b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = document.createElement('p'),
|
||||
val = info.type !== "button" && typeof info.get === 'function' ? info.get.bind(this)() : this.settings.get(info.get);
|
||||
|
||||
el.className = 'clearfix';
|
||||
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
var label = document.createElement('span'),
|
||||
help = document.createElement('span');
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
|
||||
|
||||
el.classList.add('disabled');
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
|
||||
} else {
|
||||
if ( info.type == "boolean" ) {
|
||||
var swit = document.createElement('a'),
|
||||
label = document.createElement('span');
|
||||
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.innerHTML = "<span></span>";
|
||||
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
|
||||
swit.addEventListener("click", toggle_basic_setting.bind(this, swit, key));
|
||||
|
||||
} else if ( info.type === "select" ) {
|
||||
var select = document.createElement('select'),
|
||||
label = document.createElement('span');
|
||||
|
||||
label.className = 'option-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
for(var ok in info.options) {
|
||||
var op = document.createElement('option');
|
||||
op.value = JSON.stringify(ok);
|
||||
if ( val == ok )
|
||||
op.setAttribute('selected', true);
|
||||
op.innerHTML = info.options[ok];
|
||||
select.appendChild(op);
|
||||
}
|
||||
|
||||
select.addEventListener('change', option_basic_setting.bind(this, select, key));
|
||||
|
||||
el.appendChild(label);
|
||||
el.appendChild(select);
|
||||
|
||||
} else {
|
||||
el.classList.add("option");
|
||||
var link = document.createElement('a');
|
||||
link.innerHTML = info.name;
|
||||
link.href = "#";
|
||||
el.appendChild(link);
|
||||
|
||||
link.addEventListener("click", info.method.bind(this));
|
||||
}
|
||||
|
||||
if ( info.help ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = info.help;
|
||||
el.appendChild(help);
|
||||
}
|
||||
}
|
||||
|
||||
menu.appendChild(el);
|
||||
}
|
||||
|
||||
container.appendChild(menu);
|
||||
}
|
||||
},
|
||||
|
||||
render_advanced: function(view, container) {
|
||||
var settings = {},
|
||||
categories = [],
|
||||
is_android = navigator.userAgent.indexOf('Android') !== -1;
|
||||
|
||||
for(var key in FFZ.settings_info) {
|
||||
if ( ! FFZ.settings_info.hasOwnProperty(key) )
|
||||
continue;
|
||||
|
||||
var info = FFZ.settings_info[key],
|
||||
cat = info.category || "Miscellaneous",
|
||||
cs = settings[cat];
|
||||
|
||||
if ( info.visible !== undefined && info.visible !== null ) {
|
||||
var visible = info.visible;
|
||||
if ( typeof info.visible == "function" )
|
||||
visible = info.visible.bind(this)();
|
||||
|
||||
if ( ! visible )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_android && info.no_mobile )
|
||||
continue;
|
||||
|
||||
if ( ! cs ) {
|
||||
categories.push(cat);
|
||||
cs = settings[cat] = [];
|
||||
}
|
||||
|
||||
cs.push([key, info]);
|
||||
}
|
||||
|
||||
categories.sort(function(a,b) {
|
||||
var a = a.toLowerCase(),
|
||||
b = b.toLowerCase();
|
||||
|
||||
if ( a === "debugging" )
|
||||
a = "zzz" + a;
|
||||
|
||||
if ( b === "debugging" )
|
||||
b = "zzz" + b;
|
||||
|
||||
if ( a < b ) return -1;
|
||||
else if ( a > b ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
var f = this,
|
||||
current_page = this._ffz_settings_page || categories[0];
|
||||
|
||||
for(var ci=0; ci < categories.length; ci++) {
|
||||
var category = categories[ci],
|
||||
cset = settings[category],
|
||||
|
||||
menu = document.createElement('div'),
|
||||
heading = document.createElement('div');
|
||||
|
||||
heading.className = 'heading';
|
||||
menu.className = 'chat-menu-content collapsable';
|
||||
|
||||
menu.setAttribute('data-category', category);
|
||||
menu.classList.toggle('collapsed', current_page !== category);
|
||||
|
||||
heading.innerHTML = category;
|
||||
menu.appendChild(heading);
|
||||
|
||||
menu.addEventListener('click', function() {
|
||||
if ( ! this.classList.contains('collapsed') )
|
||||
return;
|
||||
|
||||
var t = this,
|
||||
old_selection = container.querySelectorAll('.chat-menu-content:not(.collapsed)');
|
||||
for(var i=0; i < old_selection.length; i++)
|
||||
old_selection[i].classList.add('collapsed');
|
||||
|
||||
f._ffz_settings_page = t.getAttribute('data-category');
|
||||
t.classList.remove('collapsed');
|
||||
setTimeout(function(){t.scrollIntoViewIfNeeded()});
|
||||
});
|
||||
|
||||
cset.sort(function(a,b) {
|
||||
var a = a[1],
|
||||
b = b[1],
|
||||
|
||||
at = a.type === "boolean" ? 1 : 2,
|
||||
bt = b.type === "boolean" ? 1 : 2,
|
||||
|
||||
an = a.name.toLowerCase(),
|
||||
bn = b.name.toLowerCase();
|
||||
|
||||
if ( at < bt ) return -1;
|
||||
else if ( at > bt ) return 1;
|
||||
|
||||
else if ( an < bn ) return -1;
|
||||
else if ( an > bn ) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < cset.length; i++) {
|
||||
var key = cset[i][0],
|
||||
info = cset[i][1],
|
||||
el = document.createElement('p'),
|
||||
val = this.settings.get(key);
|
||||
|
||||
el.className = 'clearfix';
|
||||
|
||||
if ( this.has_bttv && info.no_bttv ) {
|
||||
var label = document.createElement('span'),
|
||||
help = document.createElement('span');
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
|
||||
|
||||
el.classList.add('disabled');
|
||||
el.appendChild(label);
|
||||
el.appendChild(help);
|
||||
|
||||
} else {
|
||||
if ( info.type == "boolean" ) {
|
||||
var swit = document.createElement('a'),
|
||||
label = document.createElement('span');
|
||||
|
||||
swit.className = 'switch';
|
||||
swit.classList.toggle('active', val);
|
||||
swit.innerHTML = "<span></span>";
|
||||
|
||||
label.className = 'switch-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
el.appendChild(swit);
|
||||
el.appendChild(label);
|
||||
|
||||
swit.addEventListener("click", toggle_setting.bind(this, swit, key));
|
||||
|
||||
} else if ( info.type === "select" ) {
|
||||
var select = document.createElement('select'),
|
||||
label = document.createElement('span');
|
||||
|
||||
label.className = 'option-label';
|
||||
label.innerHTML = info.name;
|
||||
|
||||
for(var ok in info.options) {
|
||||
var op = document.createElement('option');
|
||||
op.value = JSON.stringify(ok);
|
||||
if ( val == ok )
|
||||
op.setAttribute('selected', true);
|
||||
op.innerHTML = info.options[ok];
|
||||
select.appendChild(op);
|
||||
}
|
||||
|
||||
select.addEventListener('change', option_setting.bind(this, select, key));
|
||||
|
||||
el.appendChild(label);
|
||||
el.appendChild(select);
|
||||
|
||||
} else {
|
||||
el.classList.add("option");
|
||||
var link = document.createElement('a');
|
||||
link.innerHTML = info.name;
|
||||
link.href = "#";
|
||||
el.appendChild(link);
|
||||
|
||||
link.addEventListener("click", info.method.bind(this));
|
||||
}
|
||||
|
||||
if ( info.help ) {
|
||||
var help = document.createElement('span');
|
||||
help.className = 'help';
|
||||
help.innerHTML = info.help;
|
||||
el.appendChild(help);
|
||||
}
|
||||
}
|
||||
|
||||
menu.appendChild(el);
|
||||
}
|
||||
|
||||
container.appendChild(menu);
|
||||
}
|
||||
},
|
||||
|
||||
name: "Settings",
|
||||
icon: constants.GEAR,
|
||||
sort_order: 99999,
|
||||
wide: true,
|
||||
sub_menu: true
|
||||
};
|
||||
|
||||
|
||||
|
@ -790,7 +610,7 @@ FFZ.prototype._setting_update = function(e) {
|
|||
|
||||
if ( info.process_value )
|
||||
try {
|
||||
val = info.process_value.bind(this)(val);
|
||||
val = info.process_value.call(this, val);
|
||||
} catch(err) {
|
||||
this.log('Error processing value for setting "' + key + '": ' + err);
|
||||
return;
|
||||
|
@ -799,7 +619,7 @@ FFZ.prototype._setting_update = function(e) {
|
|||
this.settings[key] = val;
|
||||
if ( info.on_update )
|
||||
try {
|
||||
info.on_update.bind(this)(val, false);
|
||||
info.on_update.call(this, val, false);
|
||||
} catch(err) {
|
||||
this.log('Error running updater for setting "' + key + '": ' + err);
|
||||
}
|
||||
|
@ -825,7 +645,7 @@ FFZ.prototype._setting_load = function(key, default_value) {
|
|||
}
|
||||
|
||||
if ( info && info.process_value )
|
||||
val = info.process_value.bind(this)(val);
|
||||
val = info.process_value.call(this, val);
|
||||
|
||||
this.settings[key] = val;
|
||||
return val;
|
||||
|
@ -840,13 +660,13 @@ FFZ.prototype._setting_get = function(key) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._setting_set = function(key, val) {
|
||||
FFZ.prototype._setting_set = function(key, val, suppress_log) {
|
||||
var info = FFZ.settings_info[key],
|
||||
ls_key = info.storage_key || make_ls(key);
|
||||
|
||||
if ( info.process_value )
|
||||
try {
|
||||
val = info.process_value.bind(this)(val)
|
||||
val = info.process_value.call(this, val)
|
||||
} catch(err) {
|
||||
this.log('Error processing value for setting "' + key + '": ' + err);
|
||||
return false;
|
||||
|
@ -857,11 +677,12 @@ FFZ.prototype._setting_set = function(key, val) {
|
|||
var jval = JSON.stringify(val);
|
||||
localStorage.setItem(ls_key, jval);
|
||||
|
||||
if ( ! suppress_log )
|
||||
this.log('Changed Setting "' + key + '" to: ' + jval);
|
||||
|
||||
if ( info.on_update )
|
||||
try {
|
||||
info.on_update.bind(this)(val, true);
|
||||
info.on_update.call(this, val, true);
|
||||
} catch(err) {
|
||||
this.log('Error running updater for setting "' + key + '": ' + err);
|
||||
}
|
||||
|
@ -883,7 +704,7 @@ FFZ.prototype._setting_del = function(key) {
|
|||
|
||||
if ( info.on_update )
|
||||
try {
|
||||
info.on_update.bind(this)(val, true);
|
||||
info.on_update.call(this, val, true);
|
||||
} catch(err) {
|
||||
this.log('Error running updater for setting "' + key + '": ' + err);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ FFZ.prototype._ws_open = false;
|
|||
FFZ.prototype._ws_delay = 0;
|
||||
FFZ.prototype._ws_host_idx = -1;
|
||||
FFZ.prototype._ws_current_pool = -1;
|
||||
FFZ.prototype._ws_last_ping = null;
|
||||
|
||||
FFZ.prototype._ws_server_offset = null;
|
||||
|
||||
|
@ -166,7 +167,7 @@ FFZ.prototype.ws_create = function() {
|
|||
|
||||
// Send the channel(s).
|
||||
if ( f._cindex ) {
|
||||
var channel_id = f._cindex.get('controller.id'),
|
||||
var channel_id = f._cindex.get('controller.model.id'),
|
||||
hosted_id = f._cindex.get('controller.hostModeTarget.id');
|
||||
|
||||
if ( channel_id )
|
||||
|
@ -343,7 +344,7 @@ FFZ.prototype.setup_time = function() {
|
|||
difference = (new_time - last_time) - 5000;
|
||||
|
||||
last_time = new_time;
|
||||
if ( Math.abs(difference) > 250 ) {
|
||||
if ( Math.abs(difference) > 1000 ) {
|
||||
f.log("WARNING! Time drift of " + difference + "ms across 5 seconds. Did the local time change?");
|
||||
f._ws_server_offset = null;
|
||||
f.ws_ping();
|
||||
|
@ -373,7 +374,7 @@ FFZ.prototype._ws_on_pong = function(success, server_time) {
|
|||
|
||||
if ( this._ws_ping_time ) {
|
||||
var rtt = now - this._ws_ping_time,
|
||||
ping = rtt / 2;
|
||||
ping = this._ws_last_ping = rtt / 2;
|
||||
|
||||
this._ws_ping_time = null;
|
||||
this._ws_server_offset = (d_now - (server_time + ping));
|
||||
|
@ -404,7 +405,7 @@ FFZ.ws_commands.reconnect = function() {
|
|||
// Socket Close Callbacks
|
||||
for(var i=0; i < FFZ.ws_on_close.length; i++) {
|
||||
try {
|
||||
FFZ.ws_on_close[i].bind(this)();
|
||||
FFZ.ws_on_close[i].call(this);
|
||||
} catch(err) {
|
||||
this.log("Error on Socket Close Callback: " + err);
|
||||
}
|
||||
|
|
1
src/styles/badges-sub-notice-on.css
Normal file
1
src/styles/badges-sub-notice-on.css
Normal file
|
@ -0,0 +1 @@
|
|||
.chat-line.notification .indicator { display: none !important }
|
1
src/styles/badges-sub-notice.css
Normal file
1
src/styles/badges-sub-notice.css
Normal file
|
@ -0,0 +1 @@
|
|||
.chat-line.notification .badge.subscriber { display: none }
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
/* Invert Some Badges */
|
||||
body:not(.ffz-dark) .app-main:not(.theatre) .conversation-window .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
body:not(.ffz-dark) > .chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
body:not(.ffz-dark) > .ember-chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
.app-main:not(.theatre) .chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0),
|
||||
.app-main:not(.theatre) .ember-chat-container:not(.dark):not(.force-dark) .badges .badge:not(.subscriber):not(.ffz-badge-0) {
|
||||
filter: invert(100%);
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
/* Regular Alternating Background */
|
||||
.conversation-chat-lines > div:nth-child(2n+0):before,
|
||||
.chat-history .chat-line:nth-child(2n+0):before,
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
|
||||
/* ReChat */
|
||||
.ember-chat.chat-messages > .rechat-chat-line:nth-child(2n+0):before {
|
||||
.chat-line:nth-child(2n+0):before {
|
||||
background-color: rgba(0,0,0, 0.1);
|
||||
}
|
||||
|
||||
|
@ -14,40 +10,32 @@
|
|||
.ffz-dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
|
||||
.theatre .conversation-chat-lines > div:nth-child(2n+0):before,
|
||||
.theatre .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.theatre .ember-chat .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
.theatre .chat-line:nth-child(2n+0):before,
|
||||
|
||||
.dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line:nth-child(2n+0):before,
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line:before,
|
||||
|
||||
/* ReChat */
|
||||
.theatre .chat-lines > .rechat-chat-line:nth-child(2n+0):before,
|
||||
.dark .chat-lines > .rechat-chat-line:nth-child(2n+0):before,
|
||||
.force-dark .chat-lines > .rechat-chat-line:nth-child(2n+0):before {
|
||||
.dark .chat-line:nth-child(2n+0):before,
|
||||
.force-dark .chat-line:nth-child(2n+0):before {
|
||||
background-color: rgba(255,255,255, 0.05);
|
||||
}
|
||||
|
||||
|
||||
/* DEPRECIATED: Whisper Backgrounds */
|
||||
.ember-chat .chat-line.whisper-line:before {
|
||||
.chat-line.whisper:before {
|
||||
background-color: rgba(185, 163, 227, 0.2);
|
||||
}
|
||||
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before {
|
||||
.chat-line.whisper:nth-child(2n+0):before {
|
||||
background-color: rgba(185, 163, 227, 0.4);
|
||||
}
|
||||
|
||||
.theatre .chat-line.whisper-line:before,
|
||||
.dark .chat-line.whisper-line:before,
|
||||
.force-dark .chat-line.whisper-line:before {
|
||||
.theatre .chat-line.whisper:before,
|
||||
.dark .chat-line.whisper:before,
|
||||
.force-dark .chat-line.whisper:before {
|
||||
background-color: rgba(100, 65, 165, 0.2);
|
||||
}
|
||||
|
||||
.theatre .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before,
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line.whisper-line:before {
|
||||
.theatre .chat-line.whisper:nth-child(2n+0):before,
|
||||
.dark .chat-line.whisper:nth-child(2n+0):before,
|
||||
.force-dark .chat-line.whisper:nth-child(2n+0):before {
|
||||
background-color: rgba(100, 65, 165, 0.4);
|
||||
}
|
||||
|
||||
|
@ -111,13 +99,11 @@
|
|||
|
||||
|
||||
/* DEPRECIATED: Mention Backgrounds */
|
||||
.chat-history .chat-line.ffz-mentioned:before,
|
||||
.ember-chat .chat-line.ffz-mentioned:before {
|
||||
.chat-line.ffz-mentioned:before {
|
||||
background-color: rgba(255,127,127,0.2);
|
||||
}
|
||||
|
||||
.chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.ember-chat .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before {
|
||||
.chat-line.ffz-mentioned:nth-child(2n+0):before {
|
||||
background-color: rgba(255,127,127, 0.4);
|
||||
}
|
||||
|
||||
|
@ -131,14 +117,10 @@
|
|||
background-color: rgba(255,0,0, 0.2) !important;
|
||||
}
|
||||
|
||||
|
||||
.theatre .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before,
|
||||
|
||||
.dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.force-dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
|
||||
.dark .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before,
|
||||
.force-dark .chat-lines > div:nth-child(2n+0) .chat-line.ffz-mentioned:before {
|
||||
.ffz-dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.theatre .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.dark .chat-line.ffz-mentioned:nth-child(2n+0):before,
|
||||
.force-dark .chat-line.ffz-mentioned:nth-child(2n+0):before {
|
||||
background-color: rgba(255,0,0, 0.3) !important;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,14 @@
|
|||
}
|
||||
|
||||
|
||||
|
||||
/* Hide First Line */
|
||||
.conversation-chat-lines > div:first-child:before,
|
||||
.chat-lines > div:first-child .chat-line:before {
|
||||
border-top-color: transparent;
|
||||
.chat-line:first-of-type:before {
|
||||
border-top-color: transparent !important;
|
||||
}
|
||||
|
||||
/* Hide Last Line */
|
||||
.conversation-chat-lines > div:last-child:nth-child(odd):before,
|
||||
.chat-lines > div:last-child:nth-child(odd) .chat-line:before {
|
||||
border-bottom-color: transparent;
|
||||
.chat-line:last-of-type:nth-child(odd):before {
|
||||
border-bottom-color: transparent !important;
|
||||
}
|
763
src/tokenize.js
763
src/tokenize.js
|
@ -4,18 +4,13 @@ var FFZ = window.FrankerFaceZ,
|
|||
helpers,
|
||||
conv_helpers,
|
||||
|
||||
EXPLANATION_TRAIL = '<hr>FFZ is hiding this link because this url shortener is known to be used by Twitch spam bots posting malicious links. Please use caution when visiting shortened links.',
|
||||
|
||||
EXPLANATION_WARN = '<hr>This link has been sent to you via a whisper rather than standard chat, and has not been checked or approved of by any moderators or staff members. Please treat this link with caution and do not visit it if you do not trust the sender.',
|
||||
|
||||
reg_escape = function(str) {
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
},
|
||||
|
||||
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#!?&//=]*)/g,
|
||||
|
||||
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 + "*"),
|
||||
|
||||
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/g,
|
||||
|
||||
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
|
||||
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
|
||||
|
@ -46,91 +41,6 @@ var FFZ = window.FrankerFaceZ,
|
|||
return '<iframe class="ffz-image-hover' + (extra_class ? ' ' + extra_class : '') + '" allowtransparency="true" src="' + constants.SERVER + 'script/img-proxy.html#' + utils.quote_attr(href) + '"></iframe>';
|
||||
},
|
||||
|
||||
|
||||
build_link_tooltip = function(href) {
|
||||
var link_data = this._link_data[href],
|
||||
|
||||
tooltip;
|
||||
|
||||
if ( link_data && link_data.tooltip )
|
||||
return link_data.tooltip;
|
||||
|
||||
if ( ! link_data )
|
||||
return "";
|
||||
|
||||
if ( link_data.type == "youtube" ) {
|
||||
tooltip = this.settings.link_image_hover ? image_iframe(link_data.full || href, 'ffz-yt-thumb') : '';
|
||||
tooltip += "<b>YouTube: " + utils.sanitize(link_data.title) + "</b><hr>";
|
||||
tooltip += "Channel: " + utils.sanitize(link_data.channel) + " | " + utils.time_to_string(link_data.duration) + "<br>";
|
||||
tooltip += utils.number_commas(link_data.views||0) + " Views | 👍 " + utils.number_commas(link_data.likes||0) + " 👎 " + utils.number_commas(link_data.dislikes||0);
|
||||
|
||||
} else if ( link_data.type == "strawpoll" ) {
|
||||
tooltip = "<b>Strawpoll: " + utils.sanitize(link_data.title) + "</b><hr><table><tbody>";
|
||||
for(var key in link_data.items) {
|
||||
var votes = link_data.items[key],
|
||||
percentage = Math.floor((votes / link_data.total) * 100);
|
||||
tooltip += '<tr><td style="text-align:left">' + utils.sanitize(key) + '</td><td style="text-align:right">' + utils.number_commas(votes) + "</td></tr>";
|
||||
}
|
||||
tooltip += "</tbody></table><hr>Total: " + utils.number_commas(link_data.total);
|
||||
var fetched = utils.parse_date(link_data.fetched);
|
||||
if ( fetched ) {
|
||||
var age = Math.floor((fetched.getTime() - Date.now()) / 1000);
|
||||
if ( age > 60 )
|
||||
tooltip += "<br><small>Data was cached " + utils.time_to_string(age) + " ago.</small>";
|
||||
}
|
||||
|
||||
|
||||
} else if ( link_data.type == "twitch" ) {
|
||||
tooltip = "<b>Twitch: " + utils.sanitize(link_data.display_name) + "</b><hr>";
|
||||
var since = utils.parse_date(link_data.since);
|
||||
if ( since )
|
||||
tooltip += "Member Since: " + utils.date_string(since) + "<br>";
|
||||
tooltip += "<nobr>Views: " + utils.number_commas(link_data.views) + "</nobr> | <nobr>Followers: " + utils.number_commas(link_data.followers) + "</nobr>";
|
||||
|
||||
|
||||
} else if ( link_data.type == "twitch_vod" ) {
|
||||
tooltip = "<b>Twitch " + (link_data.broadcast_type == "highlight" ? "Highlight" : "Broadcast") + ": " + utils.sanitize(link_data.title) + "</b><hr>";
|
||||
tooltip += "By: " + utils.sanitize(link_data.display_name) + (link_data.game ? " | Playing: " + utils.sanitize(link_data.game) : " | Not Playing") + "<br>";
|
||||
tooltip += "Views: " + utils.number_commas(link_data.views) + " | " + utils.time_to_string(link_data.length);
|
||||
|
||||
|
||||
} else if ( link_data.type == "twitter" ) {
|
||||
tooltip = "<b>Tweet By: " + utils.sanitize(link_data.user) + "</b><hr>";
|
||||
tooltip += utils.sanitize(link_data.tweet);
|
||||
|
||||
|
||||
} else if ( link_data.type == "reputation" ) {
|
||||
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
|
||||
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
|
||||
if ( link_data.trust < 50 || link_data.safety < 50 || (link_data.tags && link_data.tags.length > 0) ) {
|
||||
tooltip += "<hr>";
|
||||
var had_extra = false;
|
||||
if ( link_data.trust < 50 || link_data.safety < 50 ) {
|
||||
link_data.unsafe = true;
|
||||
tooltip += "<b>Potentially Unsafe Link</b><br>";
|
||||
tooltip += "Trust: " + link_data.trust + "% | Child Safety: " + link_data.safety + "%";
|
||||
had_extra = true;
|
||||
}
|
||||
|
||||
if ( link_data.tags && link_data.tags.length > 0 )
|
||||
tooltip += (had_extra ? "<br>" : "") + "Tags: " + link_data.tags.join(", ");
|
||||
|
||||
tooltip += "<br>Data Source: WOT";
|
||||
}
|
||||
|
||||
|
||||
} else if ( link_data.full ) {
|
||||
tooltip = (this.settings.link_image_hover && is_image(link_data.full || href, this.settings.image_hover_all_domains)) ? image_iframe(link_data.full || href) : '';
|
||||
tooltip += '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
|
||||
}
|
||||
|
||||
if ( ! tooltip )
|
||||
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(href.toLowerCase()) + '</span>';
|
||||
|
||||
link_data.tooltip = tooltip;
|
||||
return tooltip;
|
||||
},
|
||||
|
||||
load_link_data = function(href, success, data) {
|
||||
if ( ! success )
|
||||
return;
|
||||
|
@ -138,24 +48,12 @@ var FFZ = window.FrankerFaceZ,
|
|||
this._link_data[href] = data;
|
||||
//data.unsafe = false;
|
||||
|
||||
var tooltip = build_link_tooltip.bind(this)(href), links,
|
||||
no_trail = href.charAt(href.length-1) == "/" ? href.substr(0, href.length-1) : null;
|
||||
|
||||
if ( no_trail )
|
||||
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[href="' + no_trail + '"], span.message a[data-url="' + href + '"], span.message a[data-url="' + no_trail + '"]');
|
||||
else
|
||||
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[data-url="' + href + '"]');
|
||||
|
||||
if ( ! this.settings.link_info )
|
||||
return;
|
||||
|
||||
for(var x=0; x < links.length; x++) {
|
||||
// If this link is unsafe, add the unsafe-link class to all instances of the link.
|
||||
if ( data.unsafe )
|
||||
links[x].classList.add('unsafe-link');
|
||||
|
||||
if ( ! links[x].classList.contains('deleted-link') )
|
||||
links[x].title = tooltip;
|
||||
}
|
||||
jQuery('a.chat-link[data-url="' + href + '"]').addClass('unsafe-link');
|
||||
};
|
||||
|
||||
|
||||
|
@ -167,7 +65,7 @@ FFZ.src_to_id = function(src) {
|
|||
var match = /\/emoticons\/v1\/(\d+)\/1\.0/.exec(src),
|
||||
id = match ? parseInt(match[1]) : null;
|
||||
|
||||
if ( id === NaN )
|
||||
if ( Number.isNaN(id) )
|
||||
id = null;
|
||||
|
||||
FFZ.SRC_IDS[src] = id;
|
||||
|
@ -181,9 +79,10 @@ FFZ._emote_mirror_swap = function(img) {
|
|||
return;
|
||||
|
||||
img.setAttribute('data-alt-attempts', attempts + 1);
|
||||
var id = img.getAttribute('data-emote');
|
||||
var id = img.getAttribute('data-emote'),
|
||||
src = '//' + img.src.split('//')[1];
|
||||
|
||||
if ( img.src.substr(0, constants.TWITCH_BASE.length) === constants.TWITCH_BASE ) {
|
||||
if ( src.substr(0, constants.TWITCH_BASE.length) === constants.TWITCH_BASE ) {
|
||||
img.src = constants.EMOTE_MIRROR_BASE + id + ".png";
|
||||
img.srcset = "";
|
||||
} else {
|
||||
|
@ -283,6 +182,9 @@ FFZ.prototype.setup_tokenization = function() {
|
|||
var show_deleted = f.settings.show_deleted_links;
|
||||
|
||||
return _.chain(tokens).map(function(token) {
|
||||
if ( token.type === "text" )
|
||||
token = token.text;
|
||||
|
||||
if ( ! _.isString(token) )
|
||||
return token;
|
||||
|
||||
|
@ -293,10 +195,21 @@ FFZ.prototype.setup_tokenization = function() {
|
|||
return _.zip(
|
||||
token.split(LINK),
|
||||
_.map(matches, function(e) {
|
||||
var long = e.length > 255;
|
||||
if ( ! show_deleted && (delete_links || long) )
|
||||
return {isLink: true, isDeleted: true, isLong: long, href: e};
|
||||
return {isLink: true, href: e};
|
||||
var long = e.length > 255,
|
||||
out = {
|
||||
type: "link",
|
||||
length: e.length,
|
||||
isDeleted: ! show_deleted && (delete_links || long),
|
||||
isLong: long,
|
||||
isMailTo: e.indexOf("@") > -1 && (-1 === e.indexOf("/") || e.indexOf("@") < e.indexOf("/")),
|
||||
text: e,
|
||||
link: e
|
||||
};
|
||||
|
||||
if ( ! out.isMailTo && ! e.match(/^(?:https?:\/\/)/) )
|
||||
out.link = "http://" + e;
|
||||
|
||||
return out;
|
||||
})
|
||||
);
|
||||
}).flatten().compact().value();
|
||||
|
@ -325,8 +238,6 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
this._twitch_set_to_channel[33] = "--turbo-faces--";
|
||||
this._twitch_set_to_channel[42] = "--turbo-faces--";
|
||||
|
||||
this._reset_tooltips(true);
|
||||
|
||||
}).fail(function(data) {
|
||||
if ( data.status === 404 )
|
||||
return;
|
||||
|
@ -338,6 +249,127 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Tooltip Rendering
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.render_tooltip = function(el) {
|
||||
var f = this,
|
||||
func = function() {
|
||||
if ( this.classList.contains('emoticon') ) {
|
||||
var preview_url, width=0, height=0, image, set_id, emote, emote_set,
|
||||
emote_id = this.getAttribute('data-ffz-emote');
|
||||
if ( emote_id ) {
|
||||
set_id = this.getAttribute('data-ffz-set');
|
||||
emote_set = f.emote_sets[set_id];
|
||||
emote = emote_set && emote_set.emoticons[emote_id];
|
||||
|
||||
if ( emote ) {
|
||||
var owner = emote.owner,
|
||||
title = emote_set.title || "Global",
|
||||
source = emote_set.source || "FFZ";
|
||||
|
||||
if ( f.settings.emote_image_hover ) {
|
||||
if ( emote.urls[4] ) {
|
||||
height = emote.height * 4;
|
||||
width = emote.width * 4;
|
||||
preview_url = emote.urls[4];
|
||||
|
||||
} else if ( emote.urls[2] ) {
|
||||
height = emote.height * 2;
|
||||
width = emote.width * 2;
|
||||
}
|
||||
|
||||
if ( width > 186 )
|
||||
height *= 186 / width;
|
||||
height = Math.min(186, height);
|
||||
|
||||
} else
|
||||
preview_url = null;
|
||||
|
||||
//image = preview_url ? `<img style="height:${height}px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
|
||||
image = preview_url ? '<img style="height:' + height + 'px" class="emoticon ffz-image-hover" src="' + preview_url + '"?_=preview">' : '';
|
||||
return image + 'Emoticon: ' + (emote.hidden ? '???' : emote.name) + '<br>' + source + ' ' + title + (owner ? '<br>By: ' + owner.display_name : '');
|
||||
|
||||
//return `${image}Emoticon: ${emote.hidden ? '???' : emote.name}<br>${source} ${title}${owner ? '<br>By: ' + owner.display_name : ""}`;
|
||||
}
|
||||
}
|
||||
|
||||
emote_id = this.getAttribute('data-emote');
|
||||
if ( emote_id ) {
|
||||
set_id = f._twitch_emote_to_set[emote_id];
|
||||
emote_set = set_id && f._twitch_set_to_channel[set_id];
|
||||
var set_type = "Channel";
|
||||
|
||||
preview_url = f.settings.emote_image_hover && (constants.TWITCH_BASE + emote_id + '/3.0');
|
||||
//image = preview_url ? `<img style="height:112px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
|
||||
image = preview_url ? '<img style="height:112px" class="emoticon ffz-image-hover" src="' + preview_url + '"?_=preview">' : '';
|
||||
|
||||
if ( emote_set === "--global--" ) {
|
||||
emote_set = "Twitch Global";
|
||||
set_type = null;
|
||||
} else if ( emote_set === "--twitch-turbo--" || emote_set === "turbo" || emote_set === "--turbo-faces--" ) {
|
||||
emote_set = "Twitch Turbo";
|
||||
set_type = null;
|
||||
}
|
||||
|
||||
if ( this.classList.contains('ffz-tooltip-no-credit') )
|
||||
return image + this.alt;
|
||||
else
|
||||
return image + 'Emoticon: ' + this.alt + '<br>' + (set_type ? set_type + ': ' : '') + emote_set;
|
||||
//return `${image}Emoticon: ${this.alt}<br>${set_type ? set_type + ": " : ""}${emote_set}`;
|
||||
}
|
||||
|
||||
emote_id = this.getAttribute('data-ffz-emoji');
|
||||
if ( emote_id ) {
|
||||
emote = f.emoji_data[emote_id];
|
||||
var src = emote && (f.settings.parse_emoji === 3 ? emote.one_src : (f.settings.parse_emoji === 2 ? emote.noto_src : emote.tw_src));
|
||||
|
||||
preview_url = f.settings.emote_image_hover && src;
|
||||
//image = preview_url ? `<img style="height:72px" class="emoticon ffz-image-hover" src="${preview_url}">` : '';
|
||||
image = preview_url ? '<img style="height:72px" class="emoticon ffz-image-hover" src="' + preview_url + '"?_=preview">' : '';
|
||||
|
||||
return image + "Emoji: " + this.alt + '<br>Name: ' + emote.name + (emote.short_name ? '<br>Short Name :' + emote.short_name + ':' : '') + (emote.cat ? '<br>Category: ' + utils.sanitize(constants.EMOJI_CATEGORIES[emote.cat] || emote.cat) : '');
|
||||
//return `${image}Emoji: ${this.alt}<br>Name: ${emote.name}${emote.short_name ? '<br>Short Name: :' + emote.short_name + ':' : ''}`;
|
||||
}
|
||||
|
||||
} else if ( this.classList.contains('email-link') ) {
|
||||
var url = this.getAttribute("data-url");
|
||||
return url ? "E-Mail " + url.substr(7) : '';
|
||||
|
||||
} else if ( this.classList.contains('chat-link') ) {
|
||||
// TODO: A lot of shit. Lookup data.
|
||||
var url = this.getAttribute("data-url"),
|
||||
text = '';
|
||||
|
||||
if ( ! url )
|
||||
return;
|
||||
|
||||
if ( f.settings.link_image_hover && is_image(url, f.settings.image_hover_all_domains) )
|
||||
preview_url = url;
|
||||
else
|
||||
preview_url = null;
|
||||
|
||||
image = preview_url ? image_iframe(url) : '';
|
||||
|
||||
// If it's not a deleted link, don't waste time showing the URL in the tooltip.
|
||||
if ( this.classList.contains('deleted-link') )
|
||||
text = url;
|
||||
|
||||
if ( this.classList.contains('warn-link') )
|
||||
text += EXPLANATION_WARN;
|
||||
|
||||
return image + text; //`${image}${text}`;
|
||||
}
|
||||
|
||||
f.log("Unable to Build Tooltip For: " + this.className, this);
|
||||
return "";
|
||||
};
|
||||
|
||||
return el ? func(el) : func;
|
||||
};
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Tokenization
|
||||
// ---------------------
|
||||
|
@ -364,9 +396,6 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
|
|||
if ( helpers && helpers.emoticonizeMessage && emotes )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
if ( this.settings.replace_bad_emotes )
|
||||
tokens = this.tokenize_replace_emotes(tokens);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
tokens = this.tokenize_emotes(from_user, undefined, tokens, from_me);
|
||||
|
@ -392,13 +421,12 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
if ( msgObject.cachedTokens )
|
||||
return msgObject.cachedTokens;
|
||||
|
||||
var msg = msgObject.message || msgObject.get('body'),
|
||||
var msg = msgObject.get && (msgObject.get('message') || msgObject.get('body')) || msgObject.message,
|
||||
room_id = msgObject.get && msgObject.get('room') || msgObject.room,
|
||||
from_user = msgObject.get && msgObject.get('from') || msgObject.from,
|
||||
user = this.get_user(),
|
||||
room_id = msgObject.room,
|
||||
from_user = msgObject.from,
|
||||
from_me = user && from_user === user.login,
|
||||
emotes = msgObject.tags && msgObject.tags.emotes,
|
||||
|
||||
emotes = msgObject.get && msgObject.get('tags.emotes') || msgObject.tags && msgObject.tags.emotes,
|
||||
tokens = [msg];
|
||||
|
||||
// Standard tokenization
|
||||
|
@ -421,9 +449,6 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
if ( helpers && helpers.emoticonizeMessage )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
if ( this.settings.replace_bad_emotes )
|
||||
tokens = this.tokenize_replace_emotes(tokens);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
tokens = this.tokenize_emotes(from_user, room_id, tokens, from_me);
|
||||
|
@ -442,8 +467,10 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
tokens = this.tokenize_mentions(tokens);
|
||||
|
||||
for(var i=0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if ( _.isString(token) || ! token.mentionedUser || token.own )
|
||||
var token = tokens[i],
|
||||
is_mention = token.type === "mention";
|
||||
|
||||
if ( ! is_mention || token.isOwnMessage )
|
||||
continue;
|
||||
|
||||
// We have a mention!
|
||||
|
@ -535,11 +562,8 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji)
|
|||
message = helpers.mentionizeMessage(message, u.login, user === u.login);
|
||||
}
|
||||
|
||||
if ( ! no_emotes ) {
|
||||
if ( ! no_emotes )
|
||||
message = this.tokenize_emotes(user, room, message);
|
||||
if ( this.settings.replace_bad_emotes )
|
||||
message = this.tokenize_replace_emotes(message);
|
||||
}
|
||||
|
||||
if ( this.settings.parse_emoji && ! no_emoji )
|
||||
message = this.tokenize_emoji(message);
|
||||
|
@ -548,153 +572,113 @@ FFZ.prototype.tokenize_line = function(user, room, message, no_emotes, no_emoji)
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype.render_tokens = function(tokens, render_links) {
|
||||
FFZ.prototype.render_tokens = function(tokens, render_links, warn_links) {
|
||||
var f = this;
|
||||
return _.map(tokens, function(token) {
|
||||
if ( ! token )
|
||||
return "";
|
||||
|
||||
if ( token.hidden )
|
||||
return "";
|
||||
|
||||
else if ( token.isRaw )
|
||||
else if ( token.type === "raw" )
|
||||
return token.html;
|
||||
|
||||
else if ( token.emoticonSrc ) {
|
||||
var tooltip, src = token.emoticonSrc, srcset, cls, extra;
|
||||
else if ( token.type === "emoticon" ) {
|
||||
var src = token.imgSrc, srcset, cls, extra;
|
||||
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 ? f._emote_tooltip(emote) : token.altText;
|
||||
srcset = emote ? emote.srcSet : token.srcSet;
|
||||
extra = (emote ? ' data-ffz-emote="' + emote.id + '"' : '') + (emote_set ? ' data-ffz-set="' + emote_set.id + '"' : '');
|
||||
//extra = (emote ? ` data-ffz-emote="${emote.id}"` : '') + (emote_set ? ` data-ffz-set="${emote_set.id}"` : '');
|
||||
extra = (emote ? ' data-ffz-emote="' + emote.id + '"' : '') + (emote_set ? ' data-ffz-set="' + emote_set.id + '"' : '')
|
||||
|
||||
} else if ( token.ffzEmoji ) {
|
||||
var eid = token.ffzEmoji,
|
||||
emoji = f.emoji_data && f.emoji_data[eid],
|
||||
setting = f.settings.parse_emoji,
|
||||
image = '';
|
||||
|
||||
if ( setting === 0 || (setting === 1 && ! emoji.tw) || (setting === 2 && ! emoji.noto) )
|
||||
var setting = f.settings.parse_emoji;
|
||||
if ( setting === 0 || (setting === 1 && ! token.tw) || (setting === 2 && ! token.noto) || (setting === 3 && ! token.one) )
|
||||
return token.altText;
|
||||
|
||||
src = setting === 2 ? token.noto_src : token.tw_src;
|
||||
|
||||
if ( emoji && f.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover" src="' + src + '">';
|
||||
|
||||
tooltip = emoji ? image + "Emoji: " + token.altText + "<br>Name: " + emoji.name + (emoji.short_name ? "<br>Short Name: :" + emoji.short_name + ":" : "") : token.altText;
|
||||
extra = ' data-ffz-emoji="' + eid + '" height="18px"';
|
||||
src = setting === 3 ? token.one_src : (setting === 2 ? token.noto_src : token.tw_src);
|
||||
//extra = ` data-ffz-emoji="${token.ffzEmoji}" height="18px"`;
|
||||
extra = ' data-ffz-emoji="' + token.ffzEmoji + '" height="18px"';
|
||||
cls = ' emoji';
|
||||
|
||||
} else {
|
||||
var id = token.replacedId || FFZ.src_to_id(token.emoticonSrc),
|
||||
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
||||
var id = FFZ.src_to_id(src),
|
||||
replacement = f.settings.replace_bad_emotes && constants.EMOTE_REPLACEMENTS[id];
|
||||
|
||||
if ( data )
|
||||
tooltip = data.tooltip ? data.tooltip : utils.build_tooltip.bind(f)(id, false, token.altText);
|
||||
else {
|
||||
try {
|
||||
var set_id = f._twitch_emote_to_set[id];
|
||||
if ( set_id ) {
|
||||
tooltip = utils.load_emote_data.bind(f)(id, token.altText, true, {
|
||||
code: token.altText,
|
||||
id: id,
|
||||
set: f._twitch_set_to_channel[set_id],
|
||||
set_id: set_id
|
||||
});
|
||||
} else {
|
||||
tooltip = f._twitch_emotes[id] = token.altText;
|
||||
f.ws_send("twitch_emote", id, utils.load_emote_data.bind(f, id, token.altText));
|
||||
}
|
||||
} catch(err) {
|
||||
f.error("Error Generating Emote Tooltip: " + err);
|
||||
}
|
||||
}
|
||||
//extra = ` data-emote="${id}" onerror="FrankerFaceZ._emote_mirror_swap(this)"`;
|
||||
extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"';
|
||||
|
||||
//var mirror_url = utils.quote_attr(constants.EMOTE_MIRROR_BASE + id + '.png');
|
||||
extra = ' data-emote="' + id + '" onerror="FrankerFaceZ._emote_mirror_swap(this)"'; // Disable error checking for now.
|
||||
|
||||
if ( ! constants.EMOTE_REPLACEMENTS[id] )
|
||||
if ( replacement ) {
|
||||
src = constants.EMOTE_REPLACEMENT_BASE + replacement;
|
||||
srcset = '';
|
||||
} else
|
||||
srcset = utils.build_srcset(id);
|
||||
}
|
||||
|
||||
return '<img class="emoticon html-tooltip' + (cls||"") + '"' + (extra||"") + ' src="' + utils.quote_attr(src) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||
//return `<img class="emoticon ffz-tooltip${cls||''}"${extra||''} src="${utils.quote_attr(src)}"${srcset ? ' srcset="' + utils.quote_attr(srcset) + '"' : ''} alt="${utils.quote_attr(token.altText)}">`;
|
||||
return '<img class="emoticon ffz-tooltip' + (cls||'') + '"' + (extra||'') + ' src="' + utils.quote_attr(src) + '"' + (srcset ? ' srcset="' + utils.quote_attr(srcset) + '"' : '') + ' alt="' + utils.quote_attr(token.altText) + '">';
|
||||
}
|
||||
|
||||
else if ( token.isLink ) {
|
||||
var text = token.title || (token.isLong && '<long link>') || (token.isShortened && '<shortened link>') || (token.isDeleted && '<deleted link>') || token.href;
|
||||
else if ( token.type === "link" ) {
|
||||
var text = token.title || (token.isLong && '<long link>') || (token.isDeleted && '<deleted link>') || (warn_links && '<whispered link>') || token.text;
|
||||
|
||||
if ( ! render_links && render_links !== undefined )
|
||||
return utils.sanitize(text);
|
||||
|
||||
var href = token.href,
|
||||
tooltip, cls = '',
|
||||
var href = token.link || token.text,
|
||||
cls = '';
|
||||
|
||||
ind_at = href.indexOf("@"),
|
||||
ind_sl = href.indexOf("/");
|
||||
|
||||
if ( ind_at !== -1 && (ind_sl === -1 || ind_at < ind_sl) ) {
|
||||
if ( token.isMailTo ) {
|
||||
// E-Mail Link
|
||||
cls = 'email-link';
|
||||
|
||||
if ( f.settings.link_info ) {
|
||||
cls += ' tooltip';
|
||||
tooltip = 'E-Mail ' + href;
|
||||
}
|
||||
|
||||
href = 'mailto:' + href;
|
||||
|
||||
} else {
|
||||
// Web Link
|
||||
if ( ! href.match(/^https?:\/\//) )
|
||||
href = 'http://' + href;
|
||||
cls = 'chat-link';
|
||||
|
||||
if ( f.settings.link_info ) {
|
||||
cls = 'html-tooltip';
|
||||
|
||||
var data = f._link_data && f._link_data[href];
|
||||
if ( data ) {
|
||||
tooltip = data.tooltip;
|
||||
if ( data.unsafe )
|
||||
cls += ' unsafe-link';
|
||||
|
||||
} else {
|
||||
if (!( f._link_data && f._link_data[href] )) {
|
||||
f._link_data = f._link_data || {};
|
||||
f._link_data[href] = true;
|
||||
f.ws_send("get_link", href, load_link_data.bind(f, href));
|
||||
if ( f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
|
||||
tooltip = image_iframe(href);
|
||||
}
|
||||
|
||||
} else if ( f.settings.link_image_hover ) {
|
||||
cls = 'html-tooltip';
|
||||
if ( is_image(href, f.settings.image_hover_all_domains) )
|
||||
tooltip = image_iframe(href);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Deleted Links
|
||||
var actual_href = href;
|
||||
if ( token.isShortened ) {
|
||||
cls = 'shortened-link deleted-link ' + cls;
|
||||
tooltip = utils.sanitize(token.href) + EXPLANATION_TRAIL;
|
||||
href = '#';
|
||||
|
||||
} else if ( token.isDeleted ) {
|
||||
if ( token.isDeleted ) {
|
||||
cls = 'deleted-link ' + cls;
|
||||
tooltip = utils.sanitize(token.censoredHref || token.href);
|
||||
href = '#';
|
||||
|
||||
} else if ( warn_links ) {
|
||||
cls = 'warn-link deleted-link ' + cls;
|
||||
href = '#';
|
||||
}
|
||||
|
||||
return '<a class="' + cls + '" data-original-url="' + utils.quote_attr(token.href) + '" data-url="' + utils.quote_attr(actual_href) + '" href="' + utils.quote_attr(href || '#') + '" title="' + utils.quote_attr(tooltip || '') + '" target="_blank">' + utils.sanitize(text) + '</a>';
|
||||
//return `<a class="ffz-tooltip ${cls}" data-text="${utils.quote_attr(token.text)}" data-url="${utils.quote_attr(actual_href)}" href="${utils.quote_attr(href||'#')}" target="_blank">${utils.sanitize(text)}</a>`;
|
||||
return '<a class="ffz-tooltip' + (cls ? ' ' + cls : '') + '" data-text="' + utils.quote_attr(token.text) + '" data-url="' + utils.quote_attr(actual_href) + '" href="' + utils.quote_attr(href||'#') + '" target="_blank">' + utils.sanitize(text) + '</a>';
|
||||
}
|
||||
|
||||
else if ( token.mentionedUser )
|
||||
return '<span class="' + (token.own ? "mentioning" : "mentioned") + '">' + utils.sanitize(token.mentionedUser) + "</span>";
|
||||
else if ( token.type === "deleted" )
|
||||
return '<span class="deleted-word tooltip" title="' + utils.quote_attr(token.text) + '" data-text="' + utils.sanitize(token.text) + '">×××</span>';
|
||||
//return `<span class="deleted-word tooltip" title="${utils.quote_attr(token.text)}" data-text="${utils.sanitize(token.text)}">×××</span>`;
|
||||
|
||||
else if ( token.deletedLink )
|
||||
else if ( token.type === "mention" )
|
||||
return '<span class="' + (token.isOwnMessage ? 'mentioning' : 'mentioned') + '">' + utils.sanitize(token.user) + '</span>';
|
||||
//return `<span class="${token.isOwnMessage ? 'mentioning' : 'mentioned'}">${utils.sanitize(token.user)}</span>`;
|
||||
|
||||
else if ( token.deletedLink || token.text )
|
||||
return utils.sanitize(token.text);
|
||||
|
||||
else if ( typeof token !== "string" )
|
||||
return '<b class="html-tooltip" title="<div style="text-align:left">' + utils.quote_attr(JSON.stringify(token,null,2)) + '</div>">[invalid token]</b>';
|
||||
//return `<b class="html-tooltip" title="<div style="text-align:left">${utils.quote_attr(JSON.stringify(token,null,2))}</div>">[invalid token]</b>`;
|
||||
|
||||
return utils.sanitize(token);
|
||||
}).join("");
|
||||
}
|
||||
|
@ -704,170 +688,80 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
|||
// Emoticon Processing
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.tokenize_replace_emotes = function(tokens) {
|
||||
// Replace bad Twitch emoticons with custom emoticons.
|
||||
var f = this;
|
||||
FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
||||
"use strict";
|
||||
|
||||
if ( _.isString(tokens) )
|
||||
var sets = this.getEmotes(user, room),
|
||||
emotes = {},
|
||||
emote,
|
||||
|
||||
new_tokens = [];
|
||||
|
||||
if ( ! tokens || ! tokens.length || ! sets || ! sets.length )
|
||||
return tokens;
|
||||
|
||||
// Build an object with all of our emotes.
|
||||
for(var i=0; i < sets.length; i++) {
|
||||
var emote_set = this.emote_sets[sets[i]];
|
||||
if ( emote_set && emote_set.emoticons )
|
||||
for(var emote_id in emote_set.emoticons) {
|
||||
emote = emote_set.emoticons[emote_id];
|
||||
if ( ! emotes[emote.name] )
|
||||
emotes[emote.name] = emote;
|
||||
}
|
||||
}
|
||||
|
||||
if ( typeof tokens === "string" )
|
||||
tokens = [tokens];
|
||||
|
||||
for(var i=0; i < tokens.length; i++) {
|
||||
for(var i=0, l=tokens.length; i < l; i++) {
|
||||
var token = tokens[i];
|
||||
if ( ! token || ! token.emoticonSrc || token.ffzEmote )
|
||||
if ( ! token )
|
||||
continue;
|
||||
|
||||
// Check for a few specific emoticon IDs.
|
||||
var emote_id = FFZ.src_to_id(token.emoticonSrc);
|
||||
if ( constants.EMOTE_REPLACEMENTS.hasOwnProperty(emote_id) ) {
|
||||
token.replacedId = emote_id;
|
||||
token.emoticonSrc = constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote_id];
|
||||
}
|
||||
if ( typeof token !== "string" )
|
||||
if ( token.type === "text" )
|
||||
token = token.text;
|
||||
else {
|
||||
new_tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
// Split the token!
|
||||
var segments = token.split(' '),
|
||||
text = [], segment;
|
||||
|
||||
for(var x=0,y=segments.length; x < y; x++) {
|
||||
segment = segments[x];
|
||||
emote = emotes[segment];
|
||||
|
||||
if ( emote ) {
|
||||
if ( text.length ) {
|
||||
// We have pending text. Join it together, with an extra space
|
||||
// on the end for good measure.
|
||||
new_tokens.push({type: "text", text: text.join(' ') + ' '});
|
||||
text = []
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.tokenize_title_emotes = function(tokens) {
|
||||
var f = this,
|
||||
Channel = App.__container__.lookup('controller:channel'),
|
||||
possible = Channel && Channel.get('product.emoticons'),
|
||||
emotes = [];
|
||||
|
||||
if ( _.isString(tokens) )
|
||||
tokens = [tokens];
|
||||
|
||||
// Build a list of emotes that match.
|
||||
_.each(_.union(f.__twitch_global_emotes||[], possible), function(emote) {
|
||||
if ( ! emote || emote.state === "inactive" )
|
||||
return;
|
||||
|
||||
var r = new RegExp("\\b" + emote.regex + "\\b");
|
||||
|
||||
_.any(tokens, function(token) {
|
||||
return _.isString(token) && token.match(r);
|
||||
}) && emotes.push(emote);
|
||||
});
|
||||
|
||||
// Include Global Emotes~!
|
||||
if ( f.__twitch_global_emotes === undefined || f.__twitch_global_emotes === null ) {
|
||||
f.__twitch_global_emotes = false;
|
||||
Twitch.api.get("chat/emoticon_images", {emotesets:"0,42"}).done(function(data) {
|
||||
if ( ! data || ! data.emoticon_sets || ! data.emoticon_sets[0] ) {
|
||||
f.__twitch_global_emotes = [];
|
||||
return;
|
||||
}
|
||||
|
||||
var emotes = f.__twitch_global_emotes = [];
|
||||
data = data.emoticon_sets[0];
|
||||
for(var i=0; i < data.length; i++) {
|
||||
var em = data[i];
|
||||
emotes.push({regex: em.code, url: utils.TWITCH_BASE + em.id + "/1.0"});
|
||||
}
|
||||
|
||||
if ( f._cindex )
|
||||
f._cindex.ffzFixTitle();
|
||||
}).fail(function() {
|
||||
setTimeout(function(){f.__twitch_global_emotes = null;},5000);
|
||||
});;
|
||||
}
|
||||
|
||||
if ( ! emotes.length )
|
||||
return tokens;
|
||||
|
||||
if ( typeof tokens === "string" )
|
||||
tokens = [tokens];
|
||||
|
||||
_.each(emotes, function(emote) {
|
||||
var eo = {isEmoticon:true, srcSet: emote.url + ' 1x', emoticonSrc: emote.url, altText: emote.regex};
|
||||
var r = new RegExp("\\b" + emote.regex + "\\b");
|
||||
|
||||
tokens = _.compact(_.flatten(_.map(tokens, function(token) {
|
||||
if ( _.isObject(token) )
|
||||
return token;
|
||||
|
||||
var tbits = token.split(r), bits = [];
|
||||
tbits.forEach(function(val, ind) {
|
||||
bits.push(val);
|
||||
if ( ind !== tbits.length - 1 )
|
||||
bits.push(eo);
|
||||
});
|
||||
return bits;
|
||||
})));
|
||||
});
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
||||
var f = this;
|
||||
|
||||
// Get our sets.
|
||||
var sets = this.getEmotes(user, room),
|
||||
emotes = [];
|
||||
|
||||
// Build a list of emotes that match.
|
||||
_.each(sets, function(set_id) {
|
||||
var set = f.emote_sets[set_id];
|
||||
if ( ! set )
|
||||
return;
|
||||
|
||||
_.each(set.emoticons, function(emote) {
|
||||
_.any(tokens, function(token) {
|
||||
return _.isString(token) && token.match(emote.regex);
|
||||
}) && emotes.push(emote);
|
||||
});
|
||||
});
|
||||
|
||||
// Don't bother proceeding if we have no emotes.
|
||||
if ( ! emotes.length )
|
||||
return tokens;
|
||||
|
||||
// Now that we have all the matching tokens, do crazy stuff.
|
||||
if ( typeof tokens === "string" )
|
||||
tokens = [tokens];
|
||||
|
||||
// This is weird stuff I basically copied from the old Twitch code.
|
||||
// Here, for each emote, we split apart every text token and we
|
||||
// put it back together with the matching bits of text replaced
|
||||
// with an object telling Twitch's line template how to render the
|
||||
// emoticon.
|
||||
_.each(emotes, function(emote) {
|
||||
var eo = {
|
||||
srcSet: emote.srcSet,
|
||||
emoticonSrc: emote.urls[1],
|
||||
ffzEmote: emote.id,
|
||||
ffzEmoteSet: emote.set_id,
|
||||
altText: (emote.hidden ? "???" : emote.name)
|
||||
};
|
||||
|
||||
tokens = _.compact(_.flatten(_.map(tokens, function(token) {
|
||||
if ( _.isObject(token) )
|
||||
return token;
|
||||
|
||||
var tbits = token.split(emote.regex), bits = [];
|
||||
while(tbits.length) {
|
||||
var bit = tbits.shift();
|
||||
if ( tbits.length ) {
|
||||
bit += tbits.shift();
|
||||
if ( bit )
|
||||
bits.push(bit);
|
||||
|
||||
tbits.shift();
|
||||
bits.push(eo);
|
||||
// Push this emote to the tokens.
|
||||
new_tokens.push(emote.token);
|
||||
|
||||
if ( do_report && room )
|
||||
f.add_usage(room, emote);
|
||||
this.add_usage(room, emote);
|
||||
|
||||
// Finally, push an empty string to text so that this emote gets spaced.
|
||||
text.push('');
|
||||
|
||||
} else
|
||||
bits.push(bit);
|
||||
text.push(segment);
|
||||
}
|
||||
return bits;
|
||||
})));
|
||||
});
|
||||
|
||||
return tokens;
|
||||
// Add any left over text from this segment.
|
||||
if ( text.length > 1 || (text.length === 1 && text[0] !== '') )
|
||||
new_tokens.push({type: "text", text: text.join(' ')});
|
||||
}
|
||||
|
||||
return new_tokens;
|
||||
}
|
||||
|
||||
|
||||
|
@ -876,48 +770,54 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
|||
// ---------------------
|
||||
|
||||
FFZ.prototype.tokenize_emoji = function(tokens) {
|
||||
"use strict";
|
||||
if ( ! tokens || ! tokens.length || ! this.emoji_data )
|
||||
return tokens;
|
||||
|
||||
if ( typeof tokens === "string" )
|
||||
tokens = [tokens];
|
||||
|
||||
if ( ! this.emoji_data )
|
||||
return tokens;
|
||||
var new_tokens = [];
|
||||
|
||||
var f = this;
|
||||
for(var i=0, l=tokens.length; i < l; i++) {
|
||||
var token = tokens[i];
|
||||
if ( ! token )
|
||||
continue;
|
||||
|
||||
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];
|
||||
|
||||
if ( data )
|
||||
bits.push(data.token);
|
||||
else
|
||||
bits.push(match + (variant || ""));
|
||||
}
|
||||
}
|
||||
if ( typeof token !== "string" )
|
||||
if ( token.type === "text" )
|
||||
token = token.text;
|
||||
else {
|
||||
new_tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
return bits;
|
||||
})));
|
||||
var segments = token.split(constants.EMOJI_REGEX),
|
||||
text = null;
|
||||
|
||||
while(segments.length) {
|
||||
text = (text || '') + segments.shift();
|
||||
|
||||
if ( segments.length ) {
|
||||
var match = segments.shift(),
|
||||
eid = utils.emoji_to_codepoint(match),
|
||||
data = this.emoji_data[eid];
|
||||
|
||||
if ( data ) {
|
||||
if ( text && text.length )
|
||||
new_tokens.push({type: "text", text: text});
|
||||
new_tokens.push(data.token);
|
||||
text = null;
|
||||
} else
|
||||
text = (text || '') + match;
|
||||
}
|
||||
}
|
||||
|
||||
if ( text && text.length )
|
||||
new_tokens.push({type: "text", text: text});
|
||||
}
|
||||
|
||||
return new_tokens;
|
||||
}
|
||||
|
||||
|
||||
|
@ -942,7 +842,7 @@ FFZ._words_to_regex = function(list) {
|
|||
reg += (reg ? "|" : "") + reg_escape(list[i]);
|
||||
}
|
||||
|
||||
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + SEPARATORS + ")(" + reg + ")(?=$|" + SEPARATORS + ")", "ig");
|
||||
regex = FFZ._regex_cache[list] = new RegExp("(^|.*?" + constants.SEPARATORS + ")(" + reg + ")(?=$|" + constants.SEPARATORS + ")", "ig");
|
||||
}
|
||||
|
||||
return regex;
|
||||
|
@ -962,12 +862,10 @@ FFZ.prototype.tokenize_mentions = function(tokens) {
|
|||
|
||||
for(var i=0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if ( ! _.isString(token) ) {
|
||||
new_tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
if ( token.type === "text" )
|
||||
token = token.text;
|
||||
|
||||
if ( ! token.match(regex) ) {
|
||||
if ( ! _.isString(token) || ! token.match(regex) ) {
|
||||
new_tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
@ -975,8 +873,10 @@ FFZ.prototype.tokenize_mentions = function(tokens) {
|
|||
token = token.replace(regex, function(all, prefix, match) {
|
||||
new_tokens.push(prefix);
|
||||
new_tokens.push({
|
||||
mentionedUser: match,
|
||||
own: false
|
||||
type: "mention",
|
||||
length: match.length,
|
||||
user: match,
|
||||
isOwnMessage: false,
|
||||
});
|
||||
|
||||
return "";
|
||||
|
@ -998,38 +898,25 @@ FFZ.prototype._deleted_link_click = function(e) {
|
|||
if ( ! this.classList.contains("deleted-link") )
|
||||
return true;
|
||||
|
||||
// Stop from Navigating
|
||||
e.preventDefault();
|
||||
|
||||
// Get the URL
|
||||
var href = this.getAttribute('data-url'),
|
||||
link = this.getAttribute('data-original-url') || href,
|
||||
var link = this.getAttribute('data-url'),
|
||||
text = this.getAttribute('data-text') || link,
|
||||
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;
|
||||
this.classList.remove('warn-link');
|
||||
|
||||
// Set up the Link
|
||||
this.href = href;
|
||||
this.target = "_new";
|
||||
this.textContent = link;
|
||||
this.href = link;
|
||||
this.target = "_blank";
|
||||
this.textContent = text;
|
||||
|
||||
// Now, check for a tooltip.
|
||||
var link_data = f._link_data[href];
|
||||
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();
|
||||
// Refresh tipsy.
|
||||
jQuery(this).trigger('mouseout').trigger('mouseover');
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,18 +1,26 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require("../constants");
|
||||
constants = require("../constants"),
|
||||
utils = require("../utils"),
|
||||
createElement = document.createElement.bind(document),
|
||||
|
||||
NICE_DESCRIPTION = {
|
||||
"cluster": null,
|
||||
"manifest_cluster": null,
|
||||
"user_ip": null
|
||||
};
|
||||
|
||||
|
||||
// -------------------
|
||||
// Initialization
|
||||
// -------------------
|
||||
|
||||
FFZ.prototype._has_news = false;
|
||||
/*FFZ.prototype._has_news = false;
|
||||
FFZ.prototype._news_id = 0;
|
||||
|
||||
FFZ.prototype.check_news = function(tries) {
|
||||
jQuery.ajax(constants.SERVER + "script/news.json", {cache: false, dataType: "json", context: this})
|
||||
.done(function(data) {
|
||||
FFZ.ws_commands.update_news.bind(this)(data.id);
|
||||
FFZ.ws_commands.update_news.call(this, data.id);
|
||||
}).fail(function(data) {
|
||||
if ( data.status === 404 )
|
||||
return;
|
||||
|
@ -26,7 +34,7 @@ FFZ.prototype.check_news = function(tries) {
|
|||
|
||||
FFZ.ws_commands.update_news = function(version) {
|
||||
var old_version = parseInt(localStorage.ffzLastNewsId || "0") || 0;
|
||||
if ( ! old_version || old_version === NaN || old_version < 0 )
|
||||
if ( ! old_version || Number.isNaN(old_version) || old_version < 0 )
|
||||
old_version = 0;
|
||||
|
||||
if ( version <= old_version ) {
|
||||
|
@ -37,73 +45,75 @@ FFZ.ws_commands.update_news = function(version) {
|
|||
this._has_news = true;
|
||||
this._news_id = version;
|
||||
this.update_ui_link();
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// -------------------
|
||||
// About Page
|
||||
// -------------------
|
||||
|
||||
FFZ.menu_pages.about_changelog = {
|
||||
name: "Changelog",
|
||||
visible: false,
|
||||
wide: true,
|
||||
|
||||
render: function(view, container) {
|
||||
var heading = document.createElement('div');
|
||||
|
||||
var include_html = function(heading_text, filename) {
|
||||
return function(view, container) {
|
||||
var heading = createElement('div');
|
||||
heading.className = 'chat-menu-content center';
|
||||
heading.innerHTML = '<h1>FrankerFaceZ</h1><div class="ffz-about-subheading">change log</div>';
|
||||
heading.innerHTML = '<h1>FrankerFaceZ</h1>' + (heading_text ? '<div class="ffz-about-subheading">' + heading_text + '</div>' : '');
|
||||
|
||||
jQuery.ajax(constants.SERVER + "script/changelog.html", {cache: false, context: this})
|
||||
jQuery.ajax(filename, {cache: false, context: this})
|
||||
.done(function(data) {
|
||||
container.appendChild(heading);
|
||||
container.innerHTML += data;
|
||||
|
||||
jQuery('#ffz-old-news-button', container).on('click', function() {
|
||||
jQuery(this).remove();
|
||||
jQuery('#ffz-old-news', container).css('display', 'block');
|
||||
});
|
||||
|
||||
}).fail(function(data) {
|
||||
var content = document.createElement('div');
|
||||
var content = createElement('div');
|
||||
content.className = 'chat-menu-content menu-side-padding';
|
||||
content.textContent = 'There was an error loading the change log from the server.';
|
||||
content.textContent = 'There was an error loading this page from the server.';
|
||||
|
||||
container.appendChild(heading);
|
||||
container.appendChild(content);
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
render_news = include_html("news", constants.SERVER + "script/news.html");
|
||||
|
||||
|
||||
FFZ.menu_pages.about_news = {
|
||||
name: "News",
|
||||
visible: false,
|
||||
wide: true,
|
||||
var update_player_stats = function(player, container) {
|
||||
if ( ! document.querySelector('.ffz-ui-sub-menu-page[data-page="debugging"]') || ! player.getVideoInfo )
|
||||
return;
|
||||
|
||||
render: function(view, container) {
|
||||
// Handle the news state.
|
||||
if ( this._has_news ) {
|
||||
this._has_news = false;
|
||||
localStorage.ffzLastNewsId = this._news_id;
|
||||
this.update_ui_link();
|
||||
setTimeout(update_player_stats.bind(this, player, container), 1000);
|
||||
|
||||
var player_data;
|
||||
|
||||
try {
|
||||
player_data = player.getVideoInfo();
|
||||
} catch(err) { }
|
||||
|
||||
if ( ! player_data )
|
||||
return;
|
||||
|
||||
var sorted_keys = Object.keys(player_data).sort();
|
||||
for(var i=0; i < sorted_keys.length; i++) {
|
||||
var key = sorted_keys[i],
|
||||
data = player_data[key],
|
||||
line = container.querySelector('li[data-property="' + key + '"]');
|
||||
|
||||
if ( ! line ) {
|
||||
var desc = NICE_DESCRIPTION.hasOwnProperty(key) ? NICE_DESCRIPTION[key] : key;
|
||||
if ( ! desc )
|
||||
continue;
|
||||
|
||||
line = createElement('li');
|
||||
line.setAttribute('data-property', key);
|
||||
line.innerHTML = desc + '<span></span>';
|
||||
container.appendChild(line);
|
||||
}
|
||||
|
||||
|
||||
var heading = document.createElement('div');
|
||||
|
||||
heading.className = 'chat-menu-content center';
|
||||
heading.innerHTML = '<h1>FrankerFaceZ</h1><div class="ffz-about-subheading">announcements and news</div>';
|
||||
|
||||
jQuery.ajax(constants.SERVER + "script/news.html", {cache: false, context: this})
|
||||
.done(function(data) {
|
||||
container.appendChild(heading);
|
||||
container.innerHTML += data;
|
||||
|
||||
}).fail(function(data) {
|
||||
var content = document.createElement('div');
|
||||
content.className = 'chat-menu-content menu-side-padding';
|
||||
content.textContent = 'There was an error loading the announcements from the server.';
|
||||
|
||||
container.appendChild(heading);
|
||||
container.appendChild(content);
|
||||
});
|
||||
line.querySelector('span').textContent = data;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -113,11 +123,13 @@ FFZ.menu_pages.about = {
|
|||
icon: constants.HEART,
|
||||
sort_order: 100000,
|
||||
|
||||
pages: {
|
||||
about: {
|
||||
name: "About",
|
||||
render: function(view, container, inner, menu) {
|
||||
var room = this.rooms[view.get("context.currentRoom.id")],
|
||||
has_emotes = false, f = this;
|
||||
|
||||
// Check for emoticons.
|
||||
if ( room && room.set ) {
|
||||
var set = this.emote_sets[room.set];
|
||||
if ( set && set.count > 0 )
|
||||
|
@ -125,7 +137,7 @@ FFZ.menu_pages.about = {
|
|||
}
|
||||
|
||||
// Heading
|
||||
var heading = document.createElement('div'),
|
||||
var heading = createElement('div'),
|
||||
content = '';
|
||||
|
||||
content += "<h1>FrankerFaceZ</h1>";
|
||||
|
@ -149,24 +161,11 @@ FFZ.menu_pages.about = {
|
|||
|
||||
|
||||
// Button Stuff
|
||||
var btn_container = document.createElement('div'),
|
||||
ad_button = document.createElement('a'),
|
||||
news_button = document.createElement('a'),
|
||||
donate_button = document.createElement('a'),
|
||||
message = "To use custom emoticons in " + (has_emotes ? "this channel" : "tons of channels") + ", get FrankerFaceZ from http://www.frankerfacez.com";
|
||||
|
||||
|
||||
// News
|
||||
/*news_button.className = 'button ffz-news';
|
||||
news_button.innerHTML = 'Announcements and News';
|
||||
news_button.addEventListener('click', function() {
|
||||
f._ui_change_page(view, inner, menu, container, 'about_news');
|
||||
});
|
||||
|
||||
btn_container.appendChild(news_button);
|
||||
btn_container.className = 'chat-menu-content center';
|
||||
container.appendChild(btn_container);
|
||||
btn_container = document.createElement('div');*/
|
||||
var btn_container = createElement('div'),
|
||||
ad_button = createElement('a'),
|
||||
news_button = createElement('a'),
|
||||
donate_button = createElement('a'),
|
||||
message = "To use custom emoticons in " + (has_emotes ? "this channel" : "tons of channels") + ", get FrankerFaceZ from https://www.frankerfacez.com";
|
||||
|
||||
|
||||
// Advertising
|
||||
|
@ -190,22 +189,21 @@ FFZ.menu_pages.about = {
|
|||
|
||||
|
||||
// Credits
|
||||
var credits = document.createElement('div');
|
||||
var credits = createElement('div');
|
||||
|
||||
content = '<table class="ffz-about-table">';
|
||||
content += '<tr><th colspan="4">Developers</th></tr>';
|
||||
content += '<tr><td>Dan Salvato</td><td><a class="twitch" href="http://www.twitch.tv/dansalvato" title="Twitch" target="_new"> </a></td><td><a class="twitter" href="https://twitter.com/dansalvato1" title="Twitter" target="_new"> </a></td><td><a class="youtube" href="https://www.youtube.com/user/dansalvato1" title="YouTube" target="_new"> </a></td></tr>';
|
||||
content += '<tr><td>Stendec</td><td><a class="twitch" href="http://www.twitch.tv/sirstendec" title="Twitch" target="_new"> </a></td><td><a class="twitter" href="https://twitter.com/SirStendec" title="Twitter" target="_new"> </a></td><td><a class="youtube" href="https://www.youtube.com/channel/UCnxuvmK1DCPCXSJ-mXIh4KQ" title="YouTube" target="_new"> </a></td></tr>';
|
||||
content += '<tr><td>Dan Salvato</td><td><a class="twitch" href="//www.twitch.tv/dansalvato" title="Twitch" target="_new"> </a></td><td><a class="twitter" href="https://twitter.com/dansalvato1" title="Twitter" target="_new"> </a></td><td><a class="youtube" href="https://www.youtube.com/user/dansalvato1" title="YouTube" target="_new"> </a></td></tr>';
|
||||
content += '<tr><td>Stendec</td><td><a class="twitch" href="//www.twitch.tv/sirstendec" title="Twitch" target="_new"> </a></td><td><a class="twitter" href="https://twitter.com/SirStendec" title="Twitter" target="_new"> </a></td><td><a class="youtube" href="https://www.youtube.com/channel/UCnxuvmK1DCPCXSJ-mXIh4KQ" title="YouTube" target="_new"> </a></td></tr>';
|
||||
|
||||
content += '<tr class="debug"><td><a href="#" id="ffz-changelog">Version ' + FFZ.version_info + '</a></td><td colspan="3"><a href="#" id="ffz-debug-logs">Logs</a></td></tr>';
|
||||
|
||||
credits.className = 'chat-menu-content center';
|
||||
credits.innerHTML = content;
|
||||
|
||||
// Functional Changelog
|
||||
credits.querySelector('#ffz-changelog').addEventListener('click', function() {
|
||||
f._ui_change_page(view, inner, menu, container, 'about_changelog');
|
||||
});
|
||||
// Make the Version clickable.
|
||||
credits.querySelector('#ffz-changelog').addEventListener('click',
|
||||
f._ui_change_subpage.bind(f, view, inner, menu, container, 'changelog'));
|
||||
|
||||
// Make the Logs button functional.
|
||||
var getting_logs = false;
|
||||
|
@ -225,4 +223,185 @@ FFZ.menu_pages.about = {
|
|||
|
||||
container.appendChild(credits);
|
||||
}
|
||||
},
|
||||
|
||||
changelog: {
|
||||
name: "Changelog",
|
||||
wide: true,
|
||||
render: include_html("change log", constants.SERVER + "script/changelog.html")
|
||||
},
|
||||
|
||||
/*news: {
|
||||
name: "News",
|
||||
wide: true,
|
||||
render: function(view, container) {
|
||||
if ( this._has_news ) {
|
||||
this._has_news = false;
|
||||
localStorage.ffzLastNewsId = this._news_id;
|
||||
this.update_ui_link();
|
||||
}
|
||||
|
||||
return render_news.call(this, view, container);
|
||||
}
|
||||
},*/
|
||||
|
||||
credits: {
|
||||
name: "Credits",
|
||||
wide: true,
|
||||
render: include_html("credits", constants.SERVER + "script/credits.html")
|
||||
},
|
||||
|
||||
debugging: {
|
||||
name: "Debug",
|
||||
wide: true,
|
||||
render: function(view, container) {
|
||||
// Heading
|
||||
var heading = createElement('div'),
|
||||
|
||||
info_head = createElement('div'),
|
||||
info = createElement('ul'),
|
||||
info_list = [
|
||||
['Client ID', localStorage.ffzClientId || '<i>not set</i>'],
|
||||
['Socket Server', this._ws_sock && this._ws_sock.url || '<i>disconnected</i>' ],
|
||||
['Server Ping', this._ws_last_ping || '<i>unknown</i>'],
|
||||
['Time Offset', this._ws_sock && this._ws_server_offset && (this._ws_server_offset < 0 ? "-" : "") + utils.time_to_string(Math.abs(this._ws_server_offset) / 1000) || '<i>unknown</i>']
|
||||
],
|
||||
|
||||
twitch_head = createElement('div'),
|
||||
twitch = createElement('ul'),
|
||||
twitch_list = [
|
||||
['Deploy Flavor', SiteOptions.deploy_flavor]
|
||||
],
|
||||
|
||||
player_head = createElement('div'),
|
||||
player_list = createElement('ul'),
|
||||
|
||||
pkeys = Object.keys(this.players),
|
||||
player = pkeys.length && this.players[pkeys[0]] && this.players[pkeys[0]].ffz_player,
|
||||
player_data,
|
||||
|
||||
ver_head = createElement('div'),
|
||||
vers = createElement('ul'),
|
||||
version_list = [
|
||||
['Ember', Ember.VERSION],
|
||||
['GIT Version', EmberENV.GIT_VERSION],
|
||||
null,
|
||||
['FrankerFaceZ', FFZ.version_info.toString()]
|
||||
],
|
||||
|
||||
log_head = createElement('div'),
|
||||
logs = createElement('pre');
|
||||
|
||||
if ( player ) {
|
||||
try {
|
||||
player_data = player.getVideoInfo();
|
||||
} catch(err) { }
|
||||
}
|
||||
|
||||
heading.className = 'chat-menu-content center';
|
||||
heading.innerHTML = '<h1>FrankerFaceZ</h1><div class="ffz-about-subheading">woofs for nerds</div>';
|
||||
|
||||
info_head.className = twitch_head.className = player_head.className = ver_head.className = log_head.className = 'list-header';
|
||||
info.className = twitch.className = player_list.className = vers.className = 'chat-menu-content menu-side-padding version-list';
|
||||
|
||||
|
||||
info_head.innerHTML = 'Client Status';
|
||||
|
||||
for(var i=0; i < info_list.length; i++) {
|
||||
var data = info_list[i],
|
||||
line = createElement('li');
|
||||
line.innerHTML = data === null ? '<br>' : data[0] + '<span>' + data[1] + '</span>';
|
||||
info.appendChild(line);
|
||||
}
|
||||
|
||||
|
||||
twitch_head.innerHTML = 'Twitch Configuration';
|
||||
|
||||
// Check for Twitch geo-location
|
||||
var user = this.get_user();
|
||||
if ( user && user.login ) {
|
||||
twitch_list.push(["Current User", user.login + " [" + user.id + "]"]);
|
||||
var us = [];
|
||||
|
||||
user.is_staff && us.push("staff");
|
||||
user.is_admin && us.push("admin");
|
||||
user.is_partner && us.push("partner");
|
||||
user.is_broadcaster && us.push("broadcaster");
|
||||
user.has_turbo && us.push("turbo");
|
||||
|
||||
twitch_list.push(["User State", us.join(", ") || "<i>none</i>"]);
|
||||
|
||||
} else
|
||||
twitch_list.push(["Current User", "<i>not logged in</i>"]);
|
||||
|
||||
if ( window.Twitch && Twitch.geo && Twitch.geo._result ) {
|
||||
var data = Twitch.geo._result;
|
||||
if ( data.geo )
|
||||
twitch_list.push(["Region", data.geo + (data.eu ? " [EU]" : "")]);
|
||||
|
||||
if ( data.received_language )
|
||||
twitch_list.push(["Received Language", data.received_language])
|
||||
}
|
||||
|
||||
for(var i=0; i < twitch_list.length; i++) {
|
||||
var data = twitch_list[i],
|
||||
line = createElement('li');
|
||||
line.innerHTML = data === null ? '<br>' : data[0] + '<span>' + data[1] + '</span>';
|
||||
twitch.appendChild(line);
|
||||
}
|
||||
|
||||
|
||||
if ( player_data ) {
|
||||
player_head.innerHTML = "Player Statistics";
|
||||
update_player_stats(player, player_list);
|
||||
}
|
||||
|
||||
|
||||
ver_head.innerHTML = 'Versions';
|
||||
|
||||
if ( this.has_bttv )
|
||||
version_list.push(["BetterTTV", BetterTTV.info.version + 'r' + BetterTTV.info.release]);
|
||||
|
||||
if ( Object.keys(this._apis).length ) {
|
||||
version_list.push(null);
|
||||
for(var key in this._apis) {
|
||||
var api = this._apis[key];
|
||||
version_list.push(['<b>Ext #' + api.id + '.</b> ' + api.name, api.version || '<i>unknown</i>']);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i=0; i < version_list.length; i++) {
|
||||
var data = version_list[i],
|
||||
line = createElement('li');
|
||||
line.innerHTML = data === null ? '<br>' : data[0] + '<span>' + data[1] + '</span>';
|
||||
vers.appendChild(line);
|
||||
}
|
||||
|
||||
log_head.className = 'list-header';
|
||||
log_head.innerHTML = 'Logs';
|
||||
|
||||
logs.className = 'chat-menu-content menu-side-padding';
|
||||
logs.textContent = this._log_data.join("\n");
|
||||
|
||||
container.appendChild(heading);
|
||||
|
||||
container.appendChild(ver_head);
|
||||
container.appendChild(vers);
|
||||
|
||||
container.appendChild(info_head);
|
||||
container.appendChild(info);
|
||||
|
||||
container.appendChild(twitch_head);
|
||||
container.appendChild(twitch);
|
||||
|
||||
if ( player_data ) {
|
||||
container.appendChild(player_head);
|
||||
container.appendChild(player_list);
|
||||
}
|
||||
|
||||
container.appendChild(log_head);
|
||||
container.appendChild(logs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
src/ui/channel_stats.js
Normal file
2
src/ui/channel_stats.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
var FFZ = window.FrankerFaceZ;
|
||||
|
|
@ -88,7 +88,7 @@ FFZ.basic_settings.keywords = {
|
|||
help: "Set additional keywords that will be highlighted in chat.",
|
||||
|
||||
method: function() {
|
||||
FFZ.settings_info.keywords.method.bind(this)();
|
||||
FFZ.settings_info.keywords.method.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -102,7 +102,7 @@ FFZ.basic_settings.banned_words = {
|
|||
help: "Set a list of words that will be removed from chat messages, locally.",
|
||||
|
||||
method: function() {
|
||||
FFZ.settings_info.banned_words.method.bind(this)();
|
||||
FFZ.settings_info.banned_words.method.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -137,7 +137,7 @@ FFZ.settings_info.dark_twitch = {
|
|||
document.body.classList.toggle("ffz-dark", val);
|
||||
|
||||
var Settings = window.App && App.__container__.lookup('controller:settings'),
|
||||
settings = Settings.get('settings');
|
||||
settings = Settings && Settings.get('settings');
|
||||
|
||||
if ( val ) {
|
||||
this._load_dark_css();
|
||||
|
@ -146,8 +146,9 @@ FFZ.settings_info.dark_twitch = {
|
|||
} else
|
||||
settings && settings.set('darkMode', this.settings.twitch_chat_dark);
|
||||
|
||||
// Try coloring ReChat
|
||||
jQuery('.rechat-chat-line').parents('.chat-container').toggleClass('dark', val || this.settings.twitch_chat_dark);
|
||||
// Try coloring chat replay
|
||||
jQuery('.chatReplay').toggleClass('dark', val || false);
|
||||
//jQuery('.rechat-chat-line').parents('.chat-container').toggleClass('dark', val || this.settings.twitch_chat_dark);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
271
src/ui/dash_stats.js
Normal file
271
src/ui/dash_stats.js
Normal file
|
@ -0,0 +1,271 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require('../utils');
|
||||
|
||||
|
||||
// -------------------
|
||||
// Settings
|
||||
// -------------------
|
||||
|
||||
FFZ.settings_info.dashboard_graph = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
no_mobile: true,
|
||||
no_bttv: true,
|
||||
|
||||
category: "Dashboard",
|
||||
name: "Statistics Graph <small>(Requires Refresh)</small>",
|
||||
help: "Display a graph of your viewers, followers, and chat activity over time."
|
||||
}
|
||||
|
||||
|
||||
// -------------------
|
||||
// Collecting Chat
|
||||
// -------------------
|
||||
|
||||
FFZ.msg_commands.chat_message = function(data) {
|
||||
if ( ! this.dashboard_channel || data.room !== this.dashboard_channel )
|
||||
return;
|
||||
|
||||
this._stat_chat_lines++;
|
||||
if ( this._stat_chatters.indexOf(data.from) === -1 )
|
||||
this._stat_chatters.push(data.from);
|
||||
}
|
||||
|
||||
|
||||
// -------------------
|
||||
// Initialization
|
||||
// -------------------
|
||||
|
||||
FFZ.prototype.setup_dash_stats = function() {
|
||||
this._stat_chat_lines = 0;
|
||||
this._stat_chatters = [];
|
||||
this._stat_last_game = null;
|
||||
this._stat_last_status = null;
|
||||
|
||||
this._update_dash_stats_timer = setTimeout(this.update_dash_stats.bind(this), 0);
|
||||
|
||||
var f = this,
|
||||
stats = document.querySelector('#stats');
|
||||
|
||||
if ( this.has_bttv || ! stats || ! window.Highcharts || ! this.settings.dashboard_graph )
|
||||
return;
|
||||
|
||||
f.log("Adding dashboard statistics chart.");
|
||||
|
||||
// Build a chart, under stats.
|
||||
var container = document.createElement('div');
|
||||
container.id = "chart_container";
|
||||
container.className = 'ffz-stat-chart';
|
||||
stats.parentElement.insertBefore(container, stats.nextSibling);
|
||||
|
||||
Highcharts.setOptions({global: {useUTC: false}});
|
||||
|
||||
// Load chart visibility
|
||||
var vis = {};
|
||||
if ( localStorage.ffz_dash_chart_visibility )
|
||||
vis = JSON.parse(localStorage.ffz_dash_chart_visibility);
|
||||
|
||||
var date_format = this.settings.twenty_four_timestamps ? "%H:%M" : "%l:%M",
|
||||
chart = this._dash_chart = new Highcharts.Chart({
|
||||
chart: {
|
||||
type: 'line',
|
||||
zoomType: "x",
|
||||
animation: false,
|
||||
renderTo: container,
|
||||
height: 200
|
||||
},
|
||||
|
||||
title: { text: null },
|
||||
credits: { enabled: false },
|
||||
exporting: { enabled: false },
|
||||
legend: {
|
||||
backgroundColor: "#fff"
|
||||
},
|
||||
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
tickPixelInterval: 150,
|
||||
dateTimeLabelFormats: {
|
||||
millisecond: date_format,
|
||||
second: date_format,
|
||||
minute: date_format
|
||||
}
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
formatter: function() {
|
||||
if ( this.point )
|
||||
this.points = [this.point];
|
||||
|
||||
var s = [],
|
||||
key = this.points[0].key || this.points[0].x;
|
||||
|
||||
if ( key ) {
|
||||
if ( typeof key === "number" )
|
||||
key = Highcharts.dateFormat((f.settings.twenty_four_timestamps ? "%H:%M" : "%l:%M %P"), key);
|
||||
|
||||
s.push('<span style="font-size:10px">' + key + '</span>');
|
||||
}
|
||||
|
||||
for(var i=0; i < this.points.length; i++) {
|
||||
var point = this.points[i],
|
||||
series = point.series,
|
||||
to = series.tooltipOptions,
|
||||
y = point.text || point.y;
|
||||
|
||||
if ( ! to || ! to.enabled || y === undefined || y === null )
|
||||
continue;
|
||||
|
||||
if ( typeof y === "number" )
|
||||
y = utils.number_commas(y);
|
||||
|
||||
s.push('<span style="color:' + series.color + '">' + series.name + '</span>: <b>' + y + '</b>');
|
||||
}
|
||||
|
||||
return s.join("<br>");
|
||||
},
|
||||
crosshairs: true,
|
||||
shared: true
|
||||
},
|
||||
|
||||
yAxis: [
|
||||
{title: { text: null }, min: 0},
|
||||
{title: { text: null }, min: 0},
|
||||
{title: { text: null }, min: 0, opposite: true},
|
||||
{title: { text: null }, min: 0, opposite: true}
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
type: 'flags',
|
||||
name: 'Status',
|
||||
showInLegend: false,
|
||||
shape: 'squarepin',
|
||||
data: [],
|
||||
zIndex: 5
|
||||
},
|
||||
{name: "Viewers", data: [], zIndex: 4, visible: vis.hasOwnProperty('viewers')?vis.viewers:true},
|
||||
{name: "Followers", data: [], yAxis: 1, zIndex: 3, visible: vis.hasOwnProperty('followers')?vis.followers:true},
|
||||
{name: "Subscribers", data: [], zIndex: 3, showInLegend: false, visible: vis.hasOwnProperty('subscribers')?vis.subscribers:true},
|
||||
{name: "Chat Lines", type: 'area', data: [], yAxis: 2, visible: vis.hasOwnProperty('chat_lines')?vis.chat_lines:false, zIndex: 1},
|
||||
{name: "Chatters", data: [], yAxis: 3, visible: vis.hasOwnProperty('chatters')?vis.chatters:false, zIndex: 2}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._dash_chart_chatters = function(force) {
|
||||
var now = utils.last_minute(),
|
||||
series = this._dash_chart.series[4],
|
||||
len = series.data.length,
|
||||
last_point = len > 0 && series.data[len-1],
|
||||
rendered = false;
|
||||
|
||||
if ( ! force && ! this._stat_chat_lines && len > 0 && last_point.y === 0 ) {
|
||||
series.addPoint({x: now, y: null}, false);
|
||||
this._dash_chart.series[5].addPoint({x: now, y: null}, false);
|
||||
rendered = true;
|
||||
|
||||
} else if ( force || this._stat_chat_lines || len > 0 && last_point && last_point.y !== null ) {
|
||||
if ( this._stat_chat_lines !== 0 && last_point && last_point.y === null ) {
|
||||
series.addPoint({x:now-60000, y: 0}, false);
|
||||
this._dash_chart.series[5].addPoint({x:now-60000, y: 0}, false);
|
||||
}
|
||||
|
||||
series.addPoint({x: now, y: this._stat_chat_lines || 0}, false);
|
||||
this._dash_chart.series[5].addPoint({x: now, y: this._stat_chatters.length || 0}, false);
|
||||
rendered = true;
|
||||
}
|
||||
|
||||
this._stat_chat_lines = 0;
|
||||
this._stat_chatters = [];
|
||||
return rendered;
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._remove_dash_chart = function() {
|
||||
if ( ! this._dash_chart )
|
||||
return;
|
||||
|
||||
this.log("Removing dashboard statistics chart.");
|
||||
|
||||
this._dash_chart.destroy();
|
||||
this._dash_chart = null;
|
||||
jQuery("#chart_container.ffz-stat-chart").remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
FFZ.prototype.update_dash_stats = function() {
|
||||
var f = this,
|
||||
id = this.dashboard_channel;
|
||||
|
||||
if ( this.has_bttv || ! id )
|
||||
return this._remove_dash_chart();
|
||||
|
||||
this._update_dash_stats_timer = setTimeout(this.update_dash_stats.bind(this), 60000);
|
||||
|
||||
if ( f._dash_chart ) {
|
||||
var series = f._dash_chart.series;
|
||||
localStorage.ffz_dash_chart_visibility = JSON.stringify({
|
||||
viewers: series[1].visble,
|
||||
followers: series[2].visible,
|
||||
subscribers: series[3].visible,
|
||||
chat_lines: series[4].visible,
|
||||
chatters: series[5].visible
|
||||
});
|
||||
}
|
||||
|
||||
utils.api.get("streams/" + id , {}, {version: 3})
|
||||
.done(function(data) {
|
||||
var viewers = null,
|
||||
followers = null,
|
||||
game = null,
|
||||
status = null;
|
||||
|
||||
if ( ! data || ! data.stream )
|
||||
!f.has_bttv && jQuery("#channel_viewer_count").text("Offline");
|
||||
|
||||
else {
|
||||
!f.has_bttv && jQuery("#channel_viewer_count").text(utils.number_commas(data.stream.viewers));
|
||||
viewers = data.stream.viewers;
|
||||
|
||||
var chan = data.stream.channel;
|
||||
if ( chan ) {
|
||||
followers = chan.hasOwnProperty('followers') ? chan.followers || 0 : null;
|
||||
game = chan.game || "Not Playing";
|
||||
status = chan.status || "Untitled Broadcast";
|
||||
|
||||
if ( chan.views )
|
||||
jQuery("#views_count span").text(utils.number_commas(chan.views));
|
||||
if ( followers )
|
||||
jQuery("#followers_count span").text(utils.number_commas(followers));
|
||||
}
|
||||
}
|
||||
|
||||
if ( f._dash_chart ) {
|
||||
var now = utils.last_minute(),
|
||||
force_draw;
|
||||
|
||||
// If the game or status changed, we need to insert a pin.
|
||||
if ( f._stat_last_game !== game || f._stat_last_status !== status ) {
|
||||
f._dash_chart.series[0].addPoint({x: now, title: game, text: status}, false);
|
||||
f._stat_last_game = game;
|
||||
f._stat_last_status = status;
|
||||
}
|
||||
|
||||
force_draw = utils.maybe_chart(f._dash_chart.series[1], {x: now, y: viewers}, false);
|
||||
force_draw = f._dash_chart_chatters(force_draw) || force_draw;
|
||||
|
||||
utils.maybe_chart(f._dash_chart.series[2], {x: now, y: followers}, false, force_draw);
|
||||
|
||||
f._dash_chart.redraw();
|
||||
}
|
||||
}).fail(function() {
|
||||
if ( f._dash_chart ) {
|
||||
f._dash_chart_chatters();
|
||||
f._dash_chart.redraw();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -70,6 +70,12 @@ FFZ.prototype.setup_following_count = function(has_ember) {
|
|||
|
||||
Live.load();
|
||||
|
||||
/*var Host = window.App && App.__container__.resolve('model:host'),
|
||||
HostLive = Host && Host.find("following");
|
||||
|
||||
if ( HostLive )
|
||||
HostLive.load();*/
|
||||
|
||||
var total = Live.get('total'),
|
||||
streams = Live.get('content');
|
||||
if ( typeof total === "number" ) {
|
||||
|
@ -87,7 +93,7 @@ FFZ.prototype._following_get_me = function(tries) {
|
|||
return setTimeout(this._following_get_me.bind(this, tries), Math.floor(2000*Math.random()) + 500);
|
||||
|
||||
var f = this;
|
||||
Twitch.api.get("/api/me").done(function(data) {
|
||||
utils.api.get("/api/me").done(function(data) {
|
||||
f.log("Fetched User Data -- " + (data.name || data.login));
|
||||
f.__user = data;
|
||||
f._update_following_count();
|
||||
|
@ -128,17 +134,21 @@ FFZ.prototype._update_following_count = function() {
|
|||
|
||||
var Stream = window.App && App.__container__.resolve('model:stream'),
|
||||
Live = Stream && Stream.find("live"),
|
||||
|
||||
Host = window.App && App.__container__.resolve('model:host'),
|
||||
HostLive = Host && Host.find("following"),
|
||||
|
||||
f = this;
|
||||
|
||||
if ( HostLive && document.body.getAttribute('data-current-path').indexOf('directory.following') !== -1 )
|
||||
HostLive.load();
|
||||
|
||||
if ( Live )
|
||||
Live.load();
|
||||
else {
|
||||
var a = {},
|
||||
u = this.get_user();
|
||||
var u = this.get_user();
|
||||
|
||||
a.Authorization = "OAuth " + u.chat_oauth_token;
|
||||
|
||||
Twitch.api && Twitch.api.get("streams/followed", {limit:20, offset:0}, {version:3, headers: a})
|
||||
utils.api.get("streams/followed", {limit:20, offset:0}, {version:3}, u && u.chat_oauth_token)
|
||||
.done(function(data) {
|
||||
f._draw_following_count(data._total);
|
||||
f._draw_following_channels(data.streams, data._total);
|
||||
|
@ -159,15 +169,17 @@ FFZ.prototype._build_following_tooltip = function(el) {
|
|||
|
||||
var tooltip = (this.has_bttv ? '<span class="stat playing">FrankerFaceZ</span>' : '') + 'Following',
|
||||
bb = el.getBoundingClientRect(),
|
||||
height = document.body.clientHeight - (bb.bottom + 54),
|
||||
max_lines = Math.max(Math.floor(height / 36) - 1, 2),
|
||||
height = document.body.clientHeight - (bb.bottom + 50),
|
||||
max_lines = Math.max(Math.floor(height / 40) - 1, 2),
|
||||
|
||||
/*Host = window.App && App.__container__.resolve('model:host'),
|
||||
HostLive = Host && Host.find("following"),*/
|
||||
|
||||
streams = this._tooltip_streams,
|
||||
total = this._tooltip_total || (streams && streams.length) || 0;
|
||||
|
||||
total = this._tooltip_total || (streams && streams.length) || 0,
|
||||
c = 0;
|
||||
|
||||
if ( streams && streams.length ) {
|
||||
var c = 0;
|
||||
for(var i=0, l = streams.length; i < l; i++) {
|
||||
var stream = streams[i];
|
||||
if ( ! stream || ! stream.channel )
|
||||
|
@ -189,11 +201,43 @@ FFZ.prototype._build_following_tooltip = function(el) {
|
|||
(uptime > 0 ? '<span class="stat">' + constants.CLOCK + ' ' + (hours > 0 ? hours + 'h' : '') + minutes + 'm</span>' : '') +
|
||||
'<span class="stat">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span>' +
|
||||
'<b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br>' +
|
||||
'<span class="playing">' + (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing') + '</span>';
|
||||
'<span class="playing">' + (stream.channel.game === 'Creative' ? 'Being Creative' : (stream.channel.game ? 'Playing ' + utils.sanitize(stream.channel.game) : 'Not Playing')) + '</span>';
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
c++; // is a terrible programming language
|
||||
tooltip += "<hr>No one you're following is online.";
|
||||
}
|
||||
|
||||
// If we have hosts, and room, try displaying some hosts.
|
||||
/*if ( HostLive && (c + 1) < max_lines && HostLive.get('content.length') > 0 ) {
|
||||
var t = HostLive.get('content.length');
|
||||
c++;
|
||||
tooltip += '<hr>Live Hosts';
|
||||
for(var i=0; i < t; i++) {
|
||||
var host = HostLive.get('content.' + i),
|
||||
stream = host && host.target;
|
||||
if ( ! stream )
|
||||
continue;
|
||||
|
||||
c += 1;
|
||||
if ( c > max_lines ) {
|
||||
var sc = 1 + (streams && streams.length || 0);
|
||||
tooltip += '<hr><span>And ' + utils.number_commas(t - (max_lines - sc)) + ' more...</span>';
|
||||
break;
|
||||
}
|
||||
|
||||
var hosting;
|
||||
if ( ! host.ffz_hosts || host.ffz_hosts.length === 1 )
|
||||
hosting = host.display_name;
|
||||
else
|
||||
hosting = utils.number_commas(host.ffz_hosts.length);
|
||||
|
||||
tooltip += (i === 0 ? '<hr>' : '') +
|
||||
'<span class="stat">' + constants.LIVE + ' ' + utils.number_commas(stream.viewers) + '</span>' +
|
||||
hosting + ' hosting <b>' + utils.sanitize(stream.channel.display_name || stream.channel.name) + '</b><br>' +
|
||||
'<span class="playing">' + (stream.meta_game ? 'Playing ' + utils.sanitize(stream.meta_game) : 'Not Playing') + '</span>';
|
||||
}
|
||||
}*/
|
||||
|
||||
// Reposition the tooltip.
|
||||
setTimeout(function() {
|
||||
|
|
|
@ -113,7 +113,7 @@ FFZ.ws_on_close.push(function() {
|
|||
|
||||
FFZ.ws_commands.follow_buttons = function(data) {
|
||||
var controller = window.App && App.__container__.lookup('controller:channel'),
|
||||
current_id = controller && controller.get('id'),
|
||||
current_id = controller && controller.get('content.id'),
|
||||
current_host = controller && controller.get('hostModeTarget.id'),
|
||||
need_update = false;
|
||||
|
||||
|
@ -132,7 +132,7 @@ FFZ.ws_commands.follow_buttons = function(data) {
|
|||
|
||||
FFZ.ws_commands.follow_sets = function(data) {
|
||||
var controller = App.__container__.lookup('controller:channel'),
|
||||
current_id = controller && controller.get('id'),
|
||||
current_id = controller && controller.get('content.id'),
|
||||
current_host = controller && controller.get('hostModeTarget.id'),
|
||||
need_update = false,
|
||||
f = this;
|
||||
|
@ -189,7 +189,7 @@ FFZ.ws_commands.follow_sets = function(data) {
|
|||
|
||||
FFZ.prototype.rebuild_following_ui = function() {
|
||||
var controller = App.__container__.lookup('controller:channel'),
|
||||
channel_id = controller && controller.get('id'),
|
||||
channel_id = controller && controller.get('content.id'),
|
||||
hosted_id = controller && controller.get('hostModeTarget.id');
|
||||
|
||||
if ( ! this._cindex )
|
||||
|
@ -307,7 +307,7 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
return update();
|
||||
}
|
||||
|
||||
Twitch.api.get("users/" + user.login + "/follows/channels/" + channel_id)
|
||||
utils.api.get("users/" + user.login + "/follows/channels/" + channel_id)
|
||||
.done(function(data) {
|
||||
following = true;
|
||||
notifications = data.notifications;
|
||||
|
@ -330,7 +330,7 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
return null;
|
||||
|
||||
notifications = notice;
|
||||
return Twitch.api.put("users/:login/follows/channels/" + channel_id, {notifications: notifications})
|
||||
return utils.api.put("users/:login/follows/channels/" + channel_id, {notifications: notifications})
|
||||
.fail(check_following);
|
||||
},
|
||||
|
||||
|
@ -367,7 +367,7 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
|||
if ( following )
|
||||
do_follow()
|
||||
else
|
||||
Twitch.api.del("users/:login/follows/channels/" + channel_id)
|
||||
utils.api.del("users/:login/follows/channels/" + channel_id)
|
||||
.done(check_following);
|
||||
|
||||
return false;
|
||||
|
|
215
src/ui/menu.js
215
src/ui/menu.js
|
@ -5,9 +5,9 @@ var FFZ = window.FrankerFaceZ,
|
|||
fix_menu_position = function(container) {
|
||||
var swapped = document.body.classList.contains('ffz-sidebar-swap') && ! document.body.classList.contains('ffz-portrait');
|
||||
|
||||
var bounds = container.getBoundingClientRect(),
|
||||
var bounds = container.children[0].getBoundingClientRect(),
|
||||
left = parseInt(container.style.left || '0'),
|
||||
right = bounds.left + container.scrollWidth,
|
||||
right = bounds.left + bounds.width,
|
||||
moved = !!container.style.left;
|
||||
|
||||
if ( swapped ) {
|
||||
|
@ -217,7 +217,7 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
// Stuff
|
||||
jQuery(inner).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
|
||||
|
||||
jQuery(inner).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
|
||||
|
||||
// Menu Container
|
||||
var sub_container = document.createElement('div');
|
||||
|
@ -270,7 +270,7 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
|
||||
var page = FFZ.menu_pages[key];
|
||||
try {
|
||||
if ( !page || (page.hasOwnProperty("visible") && (!page.visible || (typeof page.visible == "function" && !page.visible.bind(this)(view)))) )
|
||||
if ( !page || (page.hasOwnProperty("visible") && (!page.visible || (typeof page.visible == "function" && !page.visible.call(this, view)))) )
|
||||
continue;
|
||||
} catch(err) {
|
||||
this.error("menu_pages " + key + " visible: " + err);
|
||||
|
@ -298,7 +298,7 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
el = document.createElement('li'),
|
||||
link = document.createElement('a');
|
||||
|
||||
el.className = 'item' + (page.sub_menu ? ' has-sub-menu' : '');
|
||||
el.className = 'item' + (page.sub_menu || page.pages ? ' has-sub-menu' : '');
|
||||
el.id = "ffz-menu-page-" + key;
|
||||
link.title = page.name;
|
||||
link.innerHTML = page.icon;
|
||||
|
@ -315,12 +315,12 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
var page = (this._last_page || "channel").split("_", 1)[0];
|
||||
|
||||
// Do we have news?
|
||||
if ( this._has_news ) {
|
||||
/*if ( this._has_news ) {
|
||||
// Render news, then set the page back so our default doesn't change.
|
||||
this._ui_change_page(view, inner, menu, sub_container, 'about_news');
|
||||
this._last_page = page;
|
||||
|
||||
} else
|
||||
} else*/
|
||||
// Render Current Page
|
||||
this._ui_change_page(view, inner, menu, sub_container, page);
|
||||
|
||||
|
@ -335,26 +335,144 @@ FFZ.prototype.build_ui_popup = function(view) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._ui_change_subpage = function(view, inner, menu, container, subpage) {
|
||||
var page = this._last_page,
|
||||
last_subpages = this._last_subpage = this._last_subpage || {};
|
||||
|
||||
last_subpages[page] = subpage;
|
||||
|
||||
container.innerHTML = "";
|
||||
container.setAttribute('data-page', subpage);
|
||||
|
||||
// Get the data structure for this page.
|
||||
var page_data = FFZ.menu_pages[page],
|
||||
data = page_data.pages[subpage];
|
||||
|
||||
// Render the page first. If there's an error, it won't update the other UI stuff.
|
||||
data.render.call(this, view, container, inner, menu);
|
||||
|
||||
// Make sure the correct menu tab is selected
|
||||
jQuery('li.active', menu).removeClass('active');
|
||||
jQuery('#ffz-menu-page-' + page + '-subpage-' + subpage, menu).addClass('active');
|
||||
|
||||
// Apply wideness - TODO: Revamp wide menus entirely for thin containers
|
||||
var is_wide = false,
|
||||
app = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
|
||||
|
||||
if ( data.hasOwnProperty('wide') )
|
||||
is_wide = data.wide || (typeof data.wide === "function" && data.wide.call(this));
|
||||
else if ( page_data.hasOwnProperty('wide') )
|
||||
is_wide = page_data.wide || (typeof page_data.wide === "function" && page_data.wide.call(this));
|
||||
|
||||
inner.style.maxWidth = is_wide ? (app.offsetWidth < 640 ? (app.offsetWidth-40) : 600) + "px" : "";
|
||||
|
||||
// Re-position if necessary.
|
||||
var f = this;
|
||||
setTimeout(function(){f._fix_menu_position();});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype._ui_change_page = function(view, inner, menu, container, page) {
|
||||
this._last_page = page;
|
||||
container.innerHTML = "";
|
||||
container.setAttribute('data-page', page);
|
||||
|
||||
// Allow settings to be wide. We need to know if chat is stand-alone.
|
||||
var app = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
|
||||
inner.style.maxWidth = (!FFZ.menu_pages[page].wide || (typeof FFZ.menu_pages[page].wide == "function" && !FFZ.menu_pages[page].wide.bind(this)())) ? "" : (app.offsetWidth < 640 ? (app.offsetWidth-40) : 600) + "px";
|
||||
// Get the data structure for the new page.
|
||||
var data = FFZ.menu_pages[page];
|
||||
|
||||
var els = menu.querySelectorAll('li.active');
|
||||
for(var i=0; i < els.length; i++)
|
||||
els[i].classList.remove('active');
|
||||
// See if we're dealing with a sub-menu situation.
|
||||
if ( data.pages ) {
|
||||
// We need to render the sub-menu, and then call the _ui_change_sub_page method
|
||||
// to render the sub-page.
|
||||
var submenu = document.createElement('ul'),
|
||||
subcontainer = document.createElement('div'),
|
||||
|
||||
var el = menu.querySelector('#ffz-menu-page-' + page);
|
||||
if ( el )
|
||||
el.classList.add('active');
|
||||
else
|
||||
this.log("No matching page: " + page);
|
||||
height = parseInt(container.style.maxHeight || '0');
|
||||
|
||||
FFZ.menu_pages[page].render.bind(this)(view, container, inner, menu);
|
||||
if ( ! height )
|
||||
height = Math.max(200, view.$().height() - 172);
|
||||
|
||||
if ( height && ! Number.isNaN(height) ) {
|
||||
height -= 37;
|
||||
subcontainer.style.maxHeight = height + 'px';
|
||||
}
|
||||
|
||||
submenu.className = 'menu sub-menu clearfix';
|
||||
subcontainer.className = 'ffz-ui-sub-menu-page';
|
||||
|
||||
// Building Tabs
|
||||
var subpages = [];
|
||||
for(var key in data.pages) {
|
||||
var subpage = data.pages[key];
|
||||
try {
|
||||
if ( ! subpage || (subpage.hasOwnProperty("visible") && (!subpage.visible || (typeof subpage.visible === "function" && !subpage.visible.call(this, view)))) )
|
||||
continue;
|
||||
} catch(err) {
|
||||
this.error("menu_pages " + page + " subpage " + key + " visible: " + err);
|
||||
continue;
|
||||
}
|
||||
|
||||
subpages.push([subpage.sort_order || 0, key, subpage]);
|
||||
}
|
||||
|
||||
subpages.sort(function(a,b) {
|
||||
if ( a[0] < b[0] ) return -1;
|
||||
else if ( a[0] > b[0] ) return 1;
|
||||
|
||||
var al = a[1].toLowerCase(),
|
||||
bl = b[1].toLowerCase();
|
||||
|
||||
if ( al < bl ) return -1;
|
||||
if ( al > bl ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < subpages.length; i++) {
|
||||
var key = subpages[i][1],
|
||||
subpage = subpages[i][2],
|
||||
tab = document.createElement('li'),
|
||||
link = document.createElement('a');
|
||||
|
||||
tab.className = 'item';
|
||||
tab.id = 'ffz-menu-page-' + page + '-subpage-' + key;
|
||||
link.innerHTML = subpage.name;
|
||||
link.addEventListener('click', this._ui_change_subpage.bind(this, view, inner, submenu, subcontainer, key));
|
||||
|
||||
tab.appendChild(link);
|
||||
submenu.appendChild(tab);
|
||||
}
|
||||
|
||||
// Activate a Tab
|
||||
var last_subpages = this._last_subpage = this._last_subpage || {},
|
||||
last_subpage = last_subpages[page] = last_subpages[page] || data.default_page || subpages[0][1];
|
||||
|
||||
if ( typeof last_subpage === "function" )
|
||||
last_subpage = last_subpage.call(this);
|
||||
|
||||
this._ui_change_subpage(view, inner, submenu, subcontainer, last_subpage);
|
||||
|
||||
// Make sure the correct menu tab is selected
|
||||
jQuery('li.active', menu).removeClass('active');
|
||||
jQuery('#ffz-menu-page-' + page, menu).addClass('active');
|
||||
|
||||
// Add this to the container.
|
||||
container.appendChild(subcontainer);
|
||||
container.appendChild(submenu);
|
||||
return;
|
||||
}
|
||||
|
||||
// Render the page first. If there's an error, it won't update the other UI stuff.
|
||||
data.render.call(this, view, container, inner, menu);
|
||||
|
||||
// Make sure the correct menu tab is selected
|
||||
jQuery('li.active', menu).removeClass('active');
|
||||
jQuery('#ffz-menu-page-' + page, menu).addClass('active');
|
||||
|
||||
// Apply wideness - TODO: Revamp wide menus entirely for thin containers
|
||||
var is_wide = data.wide || (typeof data.wide === "function" && data.wide.call(this)),
|
||||
app = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
|
||||
|
||||
inner.style.maxWidth = is_wide ? (app.offsetWidth < 640 ? (app.offsetWidth-40) : 600) + "px" : "";
|
||||
|
||||
// Re-position if necessary.
|
||||
var f = this;
|
||||
|
@ -430,7 +548,16 @@ FFZ.menu_pages.channel = {
|
|||
can_use = is_subscribed || !emote.subscriber_only,
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x), url("' + constants.TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
|
||||
s.className = 'emoticon html-tooltip' + (!can_use ? " locked" : "");
|
||||
s.className = 'emoticon ffz-tooltip ffz-tooltip-no-credit' + (!can_use ? " locked" : "");
|
||||
|
||||
if ( emote.emoticon_set ) {
|
||||
var favs = this.settings.favorite_emotes["twitch-" + emote.emoticon_set];
|
||||
s.classList.add('ffz-can-favorite');
|
||||
s.classList.toggle('ffz-favorite', favs && favs.indexOf(emote.id) !== -1);
|
||||
}
|
||||
|
||||
s.setAttribute('data-emote', emote.id);
|
||||
s.alt = emote.regex;
|
||||
|
||||
s.style.backgroundImage = 'url("' + constants.TWITCH_BASE + emote.id + '/1.0")';
|
||||
s.style.backgroundImage = '-webkit-' + img_set;
|
||||
|
@ -438,19 +565,18 @@ FFZ.menu_pages.channel = {
|
|||
s.style.backgroundImage = '-ms-' + img_set;
|
||||
s.style.backgroundImage = img_set;
|
||||
|
||||
s.style.width = emote.width + "px";
|
||||
s.style.height = emote.height + "px";
|
||||
s.title = (this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + emote.id + '/3.0?_=preview">' : '') + emote.regex;
|
||||
s.style.width = (10+emote.width) + "px";
|
||||
s.style.height = (10+emote.height) + "px";
|
||||
|
||||
s.addEventListener('click', function(can_use, id, code, e) {
|
||||
s.addEventListener('click', function(can_use, id, code, emote_set, e) {
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
|
||||
window.open("https://twitchemotes.com/emote/" + id);
|
||||
else if ( can_use )
|
||||
this._add_emote(view, code);
|
||||
this._add_emote(view, code, "twitch-" + emote_set, id, e);
|
||||
else
|
||||
return;
|
||||
e.preventDefault();
|
||||
}.bind(this, can_use, emote.id, emote.regex));
|
||||
}.bind(this, can_use, emote.id, emote.regex, emote.emoticon_set));
|
||||
|
||||
grid.appendChild(s);
|
||||
c++;
|
||||
|
@ -507,7 +633,7 @@ FFZ.menu_pages.channel = {
|
|||
var extra_sets = _.union(room && room.extra_sets || [], room && room.ext_sets || [], []);
|
||||
|
||||
// Basic Emote Sets
|
||||
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");
|
||||
this._emotes_for_sets(inner, view, room && room.set && [room.set] || [], (this.feature_friday || has_product || extra_sets.length ) ? "Channel Emoticons" : null, (room && room.moderator_badge) || "//cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ");
|
||||
|
||||
for(var i=0; i < extra_sets.length; i++) {
|
||||
// Look up the set name.
|
||||
|
@ -593,7 +719,11 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
|
|||
|
||||
c++;
|
||||
var s = document.createElement('span');
|
||||
s.className = 'emoticon html-tooltip';
|
||||
s.className = 'emoticon ffz-tooltip';
|
||||
|
||||
s.setAttribute('data-ffz-emote', emote.id);
|
||||
s.setAttribute('data-ffz-set', set.id);
|
||||
|
||||
s.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
|
||||
if ( srcset ) {
|
||||
|
@ -604,9 +734,8 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
|
|||
s.style.backgroundImage = img_set;
|
||||
}
|
||||
|
||||
s.style.width = emote.width + "px";
|
||||
s.style.height = emote.height + "px";
|
||||
s.title = this._emote_tooltip(emote);
|
||||
s.style.width = (10+emote.width) + "px";
|
||||
s.style.height = (10+emote.height) + "px";
|
||||
|
||||
s.addEventListener('click', function(id, code, e) {
|
||||
e.preventDefault();
|
||||
|
@ -636,7 +765,29 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype._add_emote = function(view, emote) {
|
||||
FFZ.prototype._add_emote = function(view, emote, favorites_set, favorites_key, event) {
|
||||
if ( event && event.ctrlKey ) {
|
||||
var el = event.target;
|
||||
if ( ! el.classList.contains('locked') && el.classList.contains('ffz-can-favorite') && favorites_set && favorites_key ) {
|
||||
var favs = this.settings.favorite_emotes[favorites_set] = this.settings.favorite_emotes[favorites_set] || [],
|
||||
is_favorited = favs.indexOf(favorites_key) !== -1;
|
||||
|
||||
if ( is_favorited )
|
||||
favs.removeObject(favorites_key);
|
||||
else
|
||||
favs.push(favorites_key);
|
||||
|
||||
this.settings.set("favorite_emotes", this.settings.favorite_emotes, true);
|
||||
|
||||
if ( el.classList.contains('ffz-is-favorite') && is_favorited ) {
|
||||
jQuery(el).trigger('mouseout');
|
||||
el.parentElement.removeChild(el);
|
||||
} else
|
||||
el.classList.toggle('ffz-favorite', ! is_favorited);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var input_el, text, room;
|
||||
|
||||
if ( this.has_bttv ) {
|
||||
|
|
|
@ -43,5 +43,5 @@ FFZ.prototype.update_ui_link = function(link) {
|
|||
link.classList.toggle('live', live);
|
||||
link.classList.toggle('dark', dark);
|
||||
link.classList.toggle('blue', blue);
|
||||
link.classList.toggle('news', this._has_news);
|
||||
//link.classList.toggle('news', this._has_news);
|
||||
}
|
|
@ -56,7 +56,7 @@ FFZ.settings_info.global_emotes_in_menu = {
|
|||
|
||||
FFZ.settings_info.emoji_in_menu = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
value: true,
|
||||
|
||||
category: "Chat Input",
|
||||
|
||||
|
@ -66,11 +66,18 @@ FFZ.settings_info.emoji_in_menu = {
|
|||
|
||||
|
||||
FFZ.settings_info.emote_menu_collapsed = {
|
||||
storage_key: "ffz_setting_my_emoticons_collapsed_sections",
|
||||
value: [],
|
||||
visible: false
|
||||
}
|
||||
|
||||
|
||||
FFZ.settings_info.favorite_emotes = {
|
||||
value: {},
|
||||
visible: false
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.setup_my_emotes = function() {
|
||||
this._twitch_badges = {};
|
||||
this._twitch_badges["--global--"] = "//cdn.frankerfacez.com/script/twitch_logo.png";
|
||||
|
@ -100,47 +107,208 @@ FFZ.menu_pages.myemotes = {
|
|||
return ffz_sets.length || (sk && sk.length) || this.settings.emoji_in_menu;
|
||||
},
|
||||
|
||||
render: function(view, container) {
|
||||
var tmi = view.get('controller.currentRoom.tmiSession'),
|
||||
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'];
|
||||
default_page: function() {
|
||||
for(var key in this.settings.favorite_emotes)
|
||||
if ( this.settings.favorite_emotes[key] && this.settings.favorite_emotes[key].length )
|
||||
return 'favorites';
|
||||
|
||||
// We don't have to do async stuff anymore cause we pre-load data~!
|
||||
return FFZ.menu_pages.myemotes.draw_menu.bind(this)(view, container, twitch_sets);
|
||||
return 'all';
|
||||
},
|
||||
|
||||
toggle_section: function(heading) {
|
||||
pages: {
|
||||
favorites: {
|
||||
name: "Favorites",
|
||||
sort_order: 1,
|
||||
|
||||
render: function(view, container) {
|
||||
FFZ.menu_pages.myemotes.render_lists.call(this, view, container, true);
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.className = "emoticon-grid ffz-no-emotes center";
|
||||
el.innerHTML = "You have no favorite emoticons.<br><img src=\"//cdn.frankerfacez.com/emoticon/26608/2\"><br>To make an emote a favorite, find it on the <nobr>All Emoticons</nobr> tab and <nobr>Ctrl-Click</nobr> it.";
|
||||
container.appendChild(el);
|
||||
}
|
||||
},
|
||||
|
||||
all: {
|
||||
name: "All Emoticons",
|
||||
sort_order: 2,
|
||||
|
||||
render: function(view, container) {
|
||||
FFZ.menu_pages.myemotes.render_lists.call(this, view, container, false);
|
||||
}
|
||||
},
|
||||
|
||||
emoji: {
|
||||
name: "Emoji",
|
||||
sort_order: 3,
|
||||
visible: function() { return this.settings.emoji_in_menu },
|
||||
|
||||
render: function(view, container) {
|
||||
var sets = [];
|
||||
|
||||
for(var cat in constants.EMOJI_CATEGORIES) {
|
||||
var menu = FFZ.menu_pages.myemotes.draw_emoji.call(this, view, cat, false);
|
||||
if ( menu )
|
||||
sets.push([cat, menu]);
|
||||
}
|
||||
|
||||
sets.sort(function(a,b) {
|
||||
var an = a[0], bn = b[0];
|
||||
if ( an < bn ) return -1;
|
||||
if ( an > bn ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < sets.length; i++)
|
||||
container.appendChild(sets[i][1]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render_lists: function(view, container, favorites_only) {
|
||||
var tmi = view.get('controller.currentRoom.tmiSession'),
|
||||
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'],
|
||||
|
||||
user = this.get_user(),
|
||||
ffz_sets = this.getEmotes(user && user.login, null),
|
||||
sets = [];
|
||||
|
||||
// Start with Twitch Sets
|
||||
for(var set_id in twitch_sets) {
|
||||
if ( ! twitch_sets.hasOwnProperty(set_id) || ( ! this.settings.global_emotes_in_menu && set_id === '0' ) )
|
||||
continue;
|
||||
|
||||
var favorites_list = this.settings.favorite_emotes["twitch-" + set_id];
|
||||
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
|
||||
continue;
|
||||
|
||||
var set = twitch_sets[set_id];
|
||||
if ( ! set.length )
|
||||
continue;
|
||||
|
||||
var menu = FFZ.menu_pages.myemotes.draw_twitch_set.call(this, view, set_id, set, favorites_only);
|
||||
if ( menu )
|
||||
sets.push([this._twitch_set_to_channel[set_id], menu]);
|
||||
}
|
||||
|
||||
// Emoji~!
|
||||
if ( favorites_only && this.settings.emoji_in_menu ) {
|
||||
var favorites_list = this.settings.favorite_emotes["emoji"];
|
||||
if ( favorites_list && favorites_list.length ) {
|
||||
var menu = FFZ.menu_pages.myemotes.draw_emoji.call(this, view, null, favorites_only);
|
||||
if ( menu )
|
||||
sets.push(["emoji", menu]);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, FFZ!
|
||||
for(var i=0; i < ffz_sets.length; i++) {
|
||||
var set_id = ffz_sets[i],
|
||||
set = this.emote_sets[set_id],
|
||||
|
||||
menu_id = set.hasOwnProperty('source_ext') ? 'ffz-ext-' + set.source_ext + '-' + set.source_id : 'ffz-' + set.id,
|
||||
favorites_list = this.settings.favorite_emotes[menu_id];
|
||||
|
||||
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
|
||||
continue;
|
||||
|
||||
if ( ! set || ! set.count || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
|
||||
continue;
|
||||
|
||||
var menu = FFZ.menu_pages.myemotes.draw_ffz_set.call(this, view, set, favorites_only);
|
||||
if ( menu )
|
||||
sets.push([set.title.toLowerCase(), menu]);
|
||||
}
|
||||
|
||||
|
||||
if ( ! sets.length )
|
||||
return false;
|
||||
|
||||
|
||||
// Finally, sort and add them all.
|
||||
sets.sort(function(a,b) {
|
||||
var an = a[0], bn = b[0];
|
||||
if ( an === "turbo" || an === "--turbo-faces--" )
|
||||
an = "zza|" + an;
|
||||
else if ( an === "global" || (an && an.substr(0,16) === "global emoticons") || an === "--global--" )
|
||||
an = "zzy|" + an;
|
||||
else if ( an.substr(0,5) === "emoji" )
|
||||
an = "zzz|" + an;
|
||||
|
||||
if ( bn === "turbo" || bn === "--turbo-faces--" )
|
||||
bn = "zza|" + bn;
|
||||
else if ( bn === "global" || (bn && bn.substr(0,16) === "global emoticons") || bn === "--global--" )
|
||||
bn = "zzy|" + bn;
|
||||
else if ( bn.substr(0,5) === "emoji" )
|
||||
bn = "zzz|" + bn;
|
||||
|
||||
if ( an < bn ) return -1;
|
||||
if ( an > bn ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
if ( favorites_only ) {
|
||||
var grid = document.createElement('div');
|
||||
grid.className = 'emoticon-grid favorites-grid';
|
||||
for(var i=0; i < sets.length; i++)
|
||||
grid.appendChild(sets[i][1]);
|
||||
|
||||
container.appendChild(grid);
|
||||
|
||||
} else
|
||||
for(var i=0; i < sets.length; i++)
|
||||
container.appendChild(sets[i][1]);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
toggle_section: function(heading, container) {
|
||||
var menu = heading.parentElement,
|
||||
set_id = menu.getAttribute('data-set'),
|
||||
collapsed_list = this.settings.emote_menu_collapsed,
|
||||
is_collapsed = collapsed_list.indexOf(set_id) !== -1;
|
||||
is_collapsed = collapsed_list.indexOf(set_id) === -1;
|
||||
|
||||
if ( is_collapsed )
|
||||
if ( ! is_collapsed )
|
||||
collapsed_list.removeObject(set_id);
|
||||
else
|
||||
collapsed_list.push(set_id);
|
||||
|
||||
this.settings.set('emote_menu_collapsed', collapsed_list);
|
||||
this.settings.set('emote_menu_collapsed', collapsed_list, true);
|
||||
menu.classList.toggle('collapsed', !is_collapsed);
|
||||
|
||||
if ( is_collapsed )
|
||||
menu.appendChild(container);
|
||||
else
|
||||
menu.removeChild(container);
|
||||
},
|
||||
|
||||
draw_emoji: function(view) {
|
||||
draw_emoji: function(view, cat, favorites_only) {
|
||||
var heading = document.createElement('div'),
|
||||
menu = document.createElement('div'),
|
||||
menu_id = 'emoji' + (cat ? '-' + cat : ''),
|
||||
emotes = favorites_only ? document.createDocumentFragment() : document.createElement('div'),
|
||||
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf(menu_id) === -1,
|
||||
f = this,
|
||||
settings = this.settings.parse_emoji || 1;
|
||||
settings = this.settings.parse_emoji || 1,
|
||||
|
||||
favorites = this.settings.favorite_emotes["emoji"] || [],
|
||||
c = 0;
|
||||
|
||||
menu.className = 'emoticon-grid';
|
||||
menu.setAttribute('data-set', menu_id);
|
||||
|
||||
if ( ! favorites_only ) {
|
||||
heading.className = 'heading';
|
||||
heading.innerHTML = '<span class="right">Unicode</span>Emoji';
|
||||
heading.style.backgroundImage = 'url("' + constants.SERVER + 'emoji/' + (settings === 2 ? 'noto-' : 'tw-') + '1f4af.svg")';
|
||||
heading.innerHTML = '<span class="right">Unicode</span>' + (cat ? utils.sanitize(constants.EMOJI_CATEGORIES[cat]) : 'Emoji');
|
||||
heading.style.backgroundImage = 'url("' + constants.SERVER + 'emoji/' + (settings === 3 ? 'one/' : (settings === 2 ? 'noto-' : 'tw/')) + (constants.EMOJI_LOGOS[cat] || '1f4af') + '.svg")';
|
||||
heading.style.backgroundSize = "18px";
|
||||
|
||||
menu.className = 'emoticon-grid collapsable';
|
||||
menu.classList.add('collapsable');
|
||||
menu.appendChild(heading);
|
||||
|
||||
menu.setAttribute('data-set', 'emoji');
|
||||
menu.classList.toggle('collapsed', this.settings.emote_menu_collapsed.indexOf('emoji') !== -1);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this); });
|
||||
menu.classList.toggle('collapsed', collapsed);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this, emotes); });
|
||||
}
|
||||
|
||||
var set = [];
|
||||
|
||||
|
@ -162,32 +330,59 @@ FFZ.menu_pages.myemotes = {
|
|||
var emoji = set[i],
|
||||
em = document.createElement('span');
|
||||
|
||||
if ( (settings === 1 && ! emoji.tw) || (settings === 2 && ! emoji.noto) )
|
||||
if ( (cat && cat !== emoji.cat) || (settings === 1 && ! emoji.tw) || (settings === 2 && ! emoji.noto) || (settings === 3 && ! emoji.one) )
|
||||
continue;
|
||||
|
||||
var src = settings === 2 ? emoji.noto_src : emoji.tw_src,
|
||||
var is_favorite = favorites.indexOf(emoji.raw) !== -1,
|
||||
src = settings === 3 ? emoji.one_src : (settings === 2 ? emoji.noto_src : emoji.tw_src),
|
||||
image = this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + src + '">' : '';
|
||||
|
||||
em.className = 'emoticon emoji html-tooltip';
|
||||
em.title = image + 'Emoji: ' + emoji.raw + '<br>Name: ' + emoji.name + (emoji.short_name ? '<br>Short Name: :' + emoji.short_name + ':' : '');
|
||||
em.addEventListener('click', this._add_emote.bind(this, view, emoji.raw));
|
||||
if ( favorites_only && ! is_favorite )
|
||||
continue;
|
||||
|
||||
em.className = 'emoticon emoji ffz-tooltip ffz-can-favorite';
|
||||
em.classList.toggle('ffz-favorite', is_favorite);
|
||||
em.classList.toggle('ffz-is-favorite', favorites_only);
|
||||
|
||||
em.setAttribute('data-ffz-emoji', emoji.code);
|
||||
em.alt = emoji.raw;
|
||||
|
||||
em.addEventListener('click', this._add_emote.bind(this, view, emoji.raw, "emoji", emoji.raw));
|
||||
|
||||
em.style.backgroundImage = 'url("' + src + '")';
|
||||
em.style.backgroundSize = "18px";
|
||||
|
||||
menu.appendChild(em);
|
||||
c++;
|
||||
emotes.appendChild(em);
|
||||
}
|
||||
|
||||
if ( ! c )
|
||||
return;
|
||||
|
||||
if ( favorites_only )
|
||||
return emotes;
|
||||
|
||||
if ( ! collapsed )
|
||||
menu.appendChild(emotes);
|
||||
|
||||
return menu;
|
||||
},
|
||||
|
||||
draw_twitch_set: function(view, set_id, set) {
|
||||
draw_twitch_set: function(view, set_id, set, favorites_only) {
|
||||
var heading = document.createElement('div'),
|
||||
menu = document.createElement('div'),
|
||||
emotes = favorites_only ? document.createDocumentFragment() : document.createElement('div'),
|
||||
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf('twitch-' + set_id) === -1,
|
||||
f = this,
|
||||
|
||||
channel_id = this._twitch_set_to_channel[set_id], title;
|
||||
channel_id = this._twitch_set_to_channel[set_id], title,
|
||||
favorites = this.settings.favorite_emotes["twitch-" + set_id] || [],
|
||||
c = 0;
|
||||
|
||||
menu.className = 'emoticon-grid';
|
||||
menu.setAttribute('data-set', 'twitch-' + set_id);
|
||||
|
||||
if ( ! favorites_only ) {
|
||||
if ( channel_id === "twitch_unknown" )
|
||||
title = "Unknown Channel";
|
||||
else if ( channel_id === "--global--" )
|
||||
|
@ -206,7 +401,7 @@ FFZ.menu_pages.myemotes = {
|
|||
heading.style.backgroundImage = 'url("' + this._twitch_badges[channel_id] + '")';
|
||||
else {
|
||||
var f = this;
|
||||
Twitch.api.get("chat/" + channel_id + "/badges", null, {version: 3})
|
||||
utils.api.get("chat/" + channel_id + "/badges", null, {version: 3})
|
||||
.done(function(data) {
|
||||
if ( data.subscriber && data.subscriber.image ) {
|
||||
f._twitch_badges[channel_id] = data.subscriber.image;
|
||||
|
@ -216,12 +411,11 @@ FFZ.menu_pages.myemotes = {
|
|||
});
|
||||
}
|
||||
|
||||
menu.className = 'emoticon-grid collapsable';
|
||||
menu.classList.add('collapsable');
|
||||
menu.appendChild(heading);
|
||||
|
||||
menu.setAttribute('data-set', 'twitch-' + set_id);
|
||||
menu.classList.toggle('collapsed', this.settings.emote_menu_collapsed.indexOf('twitch-' + set_id) !== -1);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this); });
|
||||
menu.classList.toggle('collapsed', collapsed);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this, emotes); });
|
||||
}
|
||||
|
||||
set.sort(function(a,b) {
|
||||
var an = a.code.toLowerCase(),
|
||||
|
@ -237,11 +431,21 @@ FFZ.menu_pages.myemotes = {
|
|||
for(var i=0; i < set.length; i++) {
|
||||
var emote = set[i],
|
||||
code = constants.KNOWN_CODES[emote.code] || emote.code,
|
||||
is_favorite = favorites.indexOf(emote.id) !== -1;
|
||||
|
||||
em = document.createElement('span'),
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x, url("' + constants.TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
if ( favorites_only && ! is_favorite )
|
||||
continue;
|
||||
|
||||
em.className = 'emoticon html-tooltip';
|
||||
var em = document.createElement('span'),
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x)';
|
||||
|
||||
em.className = 'emoticon ffz-tooltip ffz-can-favorite';
|
||||
em.setAttribute('data-emote', emote.id);
|
||||
em.alt = code;
|
||||
|
||||
em.classList.toggle('ffz-favorite', is_favorite);
|
||||
em.classList.toggle('ffz-is-favorite', favorites_only);
|
||||
em.classList.toggle('ffz-tooltip-no-credit', ! favorites_only);
|
||||
|
||||
if ( this.settings.replace_bad_emotes && constants.EMOTE_REPLACEMENTS[emote.id] ) {
|
||||
em.style.backgroundImage = 'url("' + constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote.id] + '")';
|
||||
|
@ -250,31 +454,51 @@ FFZ.menu_pages.myemotes = {
|
|||
em.style.backgroundImage = '-webkit-' + img_set;
|
||||
em.style.backgroundImage = '-moz-' + img_set;
|
||||
em.style.backgroundImage = '-ms-' + img_set;
|
||||
em.style.backgroudnImage = img_set;
|
||||
em.style.backgroundImage = img_set;
|
||||
}
|
||||
|
||||
em.title = (this.settings.emote_image_hover ? '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + emote.id + '/3.0?_=preview">' : '') + code;
|
||||
em.addEventListener("click", function(id, c, e) {
|
||||
e.preventDefault();
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
|
||||
window.open("https://twitchemotes.com/emote/" + id);
|
||||
else
|
||||
this._add_emote(view, c);
|
||||
this._add_emote(view, c, "twitch-" + set_id, id, e);
|
||||
}.bind(this, emote.id, code));
|
||||
menu.appendChild(em);
|
||||
|
||||
c++;
|
||||
emotes.appendChild(em);
|
||||
}
|
||||
|
||||
if ( ! c )
|
||||
return;
|
||||
|
||||
if ( favorites_only )
|
||||
return emotes;
|
||||
|
||||
if ( ! collapsed )
|
||||
menu.appendChild(emotes);
|
||||
|
||||
return menu;
|
||||
},
|
||||
|
||||
draw_ffz_set: function(view, set) {
|
||||
draw_ffz_set: function(view, set, favorites_only) {
|
||||
var heading = document.createElement('div'),
|
||||
menu = document.createElement('div'),
|
||||
emote_container = favorites_only ? document.createDocumentFragment() : document.createElement('div'),
|
||||
f = this,
|
||||
emotes = [],
|
||||
|
||||
menu_id = set.hasOwnProperty('source_ext') ? 'ffz-ext-' + set.source_ext + '-' + set.source_id : 'ffz-' + set.id,
|
||||
icon = set.icon || (set.hasOwnProperty('source_ext') && '//cdn.frankerfacez.com/emoji/tw-1f4ac.svg') || '//cdn.frankerfacez.com/script/devicon.png';
|
||||
favorites = this.settings.favorite_emotes[menu_id] || [],
|
||||
c = 0,
|
||||
icon = set.icon || (set.hasOwnProperty('source_ext') && '//cdn.frankerfacez.com/emoji/tw-1f4ac.svg') || '//cdn.frankerfacez.com/script/devicon.png',
|
||||
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf(menu_id) === -1;
|
||||
|
||||
menu.className = 'emoticon-grid';
|
||||
menu.setAttribute('data-set', menu_id);
|
||||
|
||||
if ( ! favorites_only ) {
|
||||
menu.classList.add('collapsable');
|
||||
|
||||
heading.className = 'heading';
|
||||
heading.innerHTML = '<span class="right">' + (utils.sanitize(set.source) || 'FrankerFaceZ') + '</span>' + set.title;
|
||||
|
@ -283,12 +507,10 @@ FFZ.menu_pages.myemotes = {
|
|||
if ( icon.indexOf('.svg') !== -1 )
|
||||
heading.style.backgroundSize = "18px";
|
||||
|
||||
menu.className = 'emoticon-grid collapsable';
|
||||
menu.appendChild(heading);
|
||||
|
||||
menu.setAttribute('data-set', menu_id);
|
||||
menu.classList.toggle('collapsed', this.settings.emote_menu_collapsed.indexOf(menu_id) !== -1);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this); });
|
||||
menu.classList.toggle('collapsed', collapsed);
|
||||
heading.addEventListener('click', function() { FFZ.menu_pages.myemotes.toggle_section.bind(f)(this, emote_container); });
|
||||
}
|
||||
|
||||
for(var emote_id in set.emoticons)
|
||||
set.emoticons.hasOwnProperty(emote_id) && ! set.emoticons[emote_id].hidden && emotes.push(set.emoticons[emote_id]);
|
||||
|
@ -306,8 +528,12 @@ FFZ.menu_pages.myemotes = {
|
|||
|
||||
for(var i=0; i < emotes.length; i++) {
|
||||
var emote = emotes[i],
|
||||
is_favorite = favorites.indexOf(emote.id) !== -1;
|
||||
|
||||
em = document.createElement('span'),
|
||||
if ( favorites_only && ! is_favorite )
|
||||
continue;
|
||||
|
||||
var em = document.createElement('span'),
|
||||
img_set = 'image-set(url("' + emote.urls[1] + '") 1x';
|
||||
|
||||
if ( emote.urls[2] )
|
||||
|
@ -318,19 +544,24 @@ FFZ.menu_pages.myemotes = {
|
|||
|
||||
img_set += ')';
|
||||
|
||||
em.className = 'emoticon html-tooltip';
|
||||
em.className = 'emoticon ffz-tooltip ffz-can-favorite';
|
||||
em.classList.toggle('ffz-favorite', is_favorite);
|
||||
em.classList.toggle('ffz-is-favorite', favorites_only);
|
||||
|
||||
em.setAttribute('data-ffz-emote', emote.id);
|
||||
em.setAttribute('data-ffz-set', set.id);
|
||||
|
||||
em.style.backgroundImage = 'url("' + emote.urls[1] + '")';
|
||||
em.style.backgroundImage = '-webkit-' + img_set;
|
||||
em.style.backgroundImage = '-moz-' + img_set;
|
||||
em.style.backgroundImage = '-ms-' + img_set;
|
||||
em.style.backgroudnImage = img_set;
|
||||
em.style.backgroundImage = img_set;
|
||||
|
||||
if ( emote.height )
|
||||
em.style.height = emote.height + "px";
|
||||
em.style.height = (10+emote.height) + "px";
|
||||
if ( emote.width )
|
||||
em.style.width = emote.width + "px";
|
||||
em.style.width = (10+emote.width) + "px";
|
||||
|
||||
em.title = this._emote_tooltip(emote);
|
||||
em.addEventListener("click", function(id, code, e) {
|
||||
e.preventDefault();
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons ) {
|
||||
|
@ -345,97 +576,22 @@ FFZ.menu_pages.myemotes = {
|
|||
if ( url )
|
||||
window.open(url);
|
||||
} else
|
||||
this._add_emote(view, code);
|
||||
this._add_emote(view, code, menu_id, id, e);
|
||||
}.bind(this, emote.id, emote.name));
|
||||
menu.appendChild(em);
|
||||
|
||||
c++;
|
||||
emote_container.appendChild(em);
|
||||
}
|
||||
|
||||
return menu;
|
||||
},
|
||||
|
||||
draw_menu: function(view, container, twitch_sets) {
|
||||
// Make sure we're still on the My Emoticons page. Since this is
|
||||
// asynchronous, the user could've tabbed away.
|
||||
if ( container.getAttribute('data-page') !== 'myemotes' )
|
||||
if ( ! c )
|
||||
return;
|
||||
|
||||
container.innerHTML = "";
|
||||
try {
|
||||
var user = this.get_user(),
|
||||
ffz_sets = this.getEmotes(user && user.login, null),
|
||||
sets = [];
|
||||
if ( favorites_only )
|
||||
return emote_container;
|
||||
|
||||
// Start with Twitch Sets
|
||||
for(var set_id in twitch_sets) {
|
||||
if ( ! twitch_sets.hasOwnProperty(set_id) || ( ! this.settings.global_emotes_in_menu && set_id === '0' ) )
|
||||
continue;
|
||||
if ( ! collapsed )
|
||||
menu.appendChild(emote_container);
|
||||
|
||||
var set = twitch_sets[set_id];
|
||||
if ( ! set.length )
|
||||
continue;
|
||||
|
||||
sets.push([this._twitch_set_to_channel[set_id], FFZ.menu_pages.myemotes.draw_twitch_set.bind(this)(view, set_id, set)]);
|
||||
}
|
||||
|
||||
// Emoji~!
|
||||
if ( this.settings.emoji_in_menu )
|
||||
sets.push(["emoji", FFZ.menu_pages.myemotes.draw_emoji.bind(this)(view)]);
|
||||
|
||||
// Now, FFZ!
|
||||
for(var i=0; i < ffz_sets.length; i++) {
|
||||
var set_id = ffz_sets[i],
|
||||
set = this.emote_sets[set_id];
|
||||
|
||||
if ( ! set || ! set.count || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
|
||||
continue;
|
||||
|
||||
sets.push([set.title.toLowerCase(), FFZ.menu_pages.myemotes.draw_ffz_set.bind(this)(view, set)]);
|
||||
}
|
||||
|
||||
|
||||
// Finally, sort and add them all.
|
||||
sets.sort(function(a,b) {
|
||||
var an = a[0], bn = b[0];
|
||||
if ( an === "turbo" || an === "--turbo-faces--" )
|
||||
an = "zza|" + an;
|
||||
else if ( an === "global" || (an && an.substr(0,16) === "global emoticons") || an === "--global--" )
|
||||
an = "zzy|" + an;
|
||||
else if ( an === "emoji" )
|
||||
an = "zzz|" + an;
|
||||
|
||||
if ( bn === "turbo" || bn === "--turbo-faces--" )
|
||||
bn = "zza|" + bn;
|
||||
else if ( bn === "global" || (bn && bn.substr(0,16) === "global emoticons") || bn === "--global--" )
|
||||
bn = "zzy|" + bn;
|
||||
else if ( bn === "emoji" )
|
||||
bn = "zzz|" + bn;
|
||||
|
||||
if ( an < bn ) return -1;
|
||||
if ( an > bn ) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for(var i=0; i < sets.length; i++)
|
||||
container.appendChild(sets[i][1]);
|
||||
|
||||
} catch(err) {
|
||||
this.error("myemotes draw_menu: " + err);
|
||||
container.innerHTML = "";
|
||||
|
||||
var menu = document.createElement('div'),
|
||||
heading = document.createElement('div'),
|
||||
p = document.createElement('p');
|
||||
|
||||
heading.className = 'heading';
|
||||
heading.innerHTML = 'Error Loading Menu';
|
||||
menu.appendChild(heading);
|
||||
|
||||
p.className = 'clearfix';
|
||||
p.textContent = err;
|
||||
menu.appendChild(p);
|
||||
|
||||
menu.className = 'chat-menu-content';
|
||||
container.appendChild(menu);
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
var FFZ = window.FrankerFaceZ;
|
||||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require("../utils");
|
||||
|
||||
|
||||
// ---------------------
|
||||
|
@ -74,17 +75,21 @@ FFZ.settings_info.notification_timeout = {
|
|||
help: "Specify how long notifications should be displayed before automatically closing.",
|
||||
|
||||
method: function() {
|
||||
var old_val = this.settings.notification_timeout,
|
||||
new_val = prompt("Notification Timeout\n\nPlease enter the time you'd like notifications to be displayed before automatically closing, in seconds.\n\nDefault is: 60", old_val);
|
||||
|
||||
var f = this;
|
||||
utils.prompt(
|
||||
"Notification Timeout",
|
||||
"Please enter the time you'd like notifications to be displayed before automatically closing, in seconds.</p><p><b>Default:</b> 60",
|
||||
this.settings.notification_timeout,
|
||||
function(new_val) {
|
||||
if ( new_val === null || new_val === undefined )
|
||||
return;
|
||||
|
||||
var parsed = parseInt(new_val);
|
||||
if ( parsed === NaN || parsed < 1 )
|
||||
parsed = 60;
|
||||
new_val = parseInt(new_val);
|
||||
if ( Number.isNaN(new_val) || ! Number.isFinite(new_val) || new_val < 1 )
|
||||
new_val = 60;
|
||||
|
||||
this.settings.set("notification_timeout", parsed);
|
||||
f.settings.set("notification_timeout", new_val);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -136,7 +141,7 @@ FFZ.prototype.show_notification = function(message, title, tag, timeout, on_clic
|
|||
dir: "ltr",
|
||||
body: message,
|
||||
tag: tag || "FrankerFaceZ",
|
||||
icon: "http://cdn.frankerfacez.com/icon32.png"
|
||||
icon: "//cdn.frankerfacez.com/icon32.png"
|
||||
};
|
||||
|
||||
var f = this,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
var FFZ = window.FrankerFaceZ;
|
||||
var FFZ = window.FrankerFaceZ,
|
||||
constants = require('../constants');
|
||||
|
||||
|
||||
// ---------------
|
||||
|
@ -13,6 +14,11 @@ FFZ.prototype.setup_popups = function() {
|
|||
if ( e.button && e.button !== 0 )
|
||||
return;
|
||||
|
||||
// Check for modal clicks
|
||||
var modal = document.getElementById('ffz-modal-container');
|
||||
if ( modal && (modal === e.target || modal.contains(e.target)) )
|
||||
return;
|
||||
|
||||
var popup = f._popup,
|
||||
parent = f._popup_parent;
|
||||
|
||||
|
|
|
@ -304,7 +304,7 @@ FFZ.prototype._update_race = function(container, not_timer) {
|
|||
for(var i=0; i < entrants.length; i++) {
|
||||
var ent = entrants[i],
|
||||
name = '<a target="_new" href="http://www.speedrunslive.com/profiles/#!/' + utils.sanitize(ent.name) + '">' + ent.display_name + '</a>',
|
||||
twitch_link = ent.channel ? '<a target="_new" class="twitch" href="http://www.twitch.tv/' + utils.sanitize(ent.channel) + '"></a>' : '',
|
||||
twitch_link = ent.channel ? '<a target="_new" class="twitch" href="//www.twitch.tv/' + utils.sanitize(ent.channel) + '"></a>' : '',
|
||||
hitbox_link = ent.hitbox ? '<a target="_new" class="hitbox" href="http://www.hitbox.tv/' + utils.sanitize(ent.hitbox) + '"></a>' : '',
|
||||
time = elapsed ? utils.time_to_string(ent.time||elapsed) : "",
|
||||
place = utils.place_string(ent.place),
|
||||
|
|
|
@ -31,22 +31,19 @@ FFZ.prototype._update_subscribers = function() {
|
|||
// context of the web user.
|
||||
|
||||
// Get the count!
|
||||
jQuery.ajax({url: "/broadcast/dashboard/partnership"}).done(function(data) {
|
||||
try {
|
||||
var html = document.createElement('span'), dash;
|
||||
jQuery.getJSON("/" + id + "/dashboard/revenue/summary_data").done(function(data) {
|
||||
var el, sub_count = data && data.data && data.data.total_subscriptions;
|
||||
if ( typeof sub_count === "string" )
|
||||
sub_count = parseInt(sub_count.replace(/[,\.]/g, ""));
|
||||
|
||||
html.innerHTML = data;
|
||||
dash = html.querySelector("#dash_main");
|
||||
|
||||
var match = dash && dash.textContent.match(/([\d,\.]+) total active subscribers/),
|
||||
sub_count = match && match[1];
|
||||
|
||||
if ( ! sub_count ) {
|
||||
var el = document.querySelector("#ffz-sub-display");
|
||||
if ( typeof sub_count !== "number" || sub_count === 0 || sub_count === NaN || sub_count === Infinity ) {
|
||||
el = document.querySelector("#ffz-sub-display");
|
||||
if ( el )
|
||||
el.parentElement.removeChild(el);
|
||||
|
||||
if ( f._update_subscribers_timer ) {
|
||||
var failed = f._failed_sub_checks = (f._failed_sub_checks || 0) + 1;
|
||||
if ( f._update_subscribers_timer && failed >= 5 ) {
|
||||
f.log("Subscriber count failed 5 times. Giving up.");
|
||||
clearTimeout(f._update_subscribers_timer);
|
||||
delete f._update_subscribers_timer;
|
||||
}
|
||||
|
@ -54,7 +51,17 @@ FFZ.prototype._update_subscribers = function() {
|
|||
return;
|
||||
}
|
||||
|
||||
var el = document.querySelector('#ffz-sub-display span');
|
||||
// Graph this glorious data point
|
||||
if ( f._dash_chart ) {
|
||||
if ( ! f._dash_chart.series[3].options.showInLegend ) {
|
||||
f._dash_chart.series[3].options.showInLegend = true;
|
||||
f._dash_chart.legend.renderLegend();
|
||||
}
|
||||
|
||||
f._dash_chart.series[3].addPoint({x: utils.last_minute(), y: sub_count});
|
||||
}
|
||||
|
||||
el = document.querySelector('#ffz-sub-display span');
|
||||
if ( ! el ) {
|
||||
var cont = f.is_dashboard ? document.querySelector("#stats") : document.querySelector("#channel .stats-and-actions .channel-stats");
|
||||
if ( ! cont )
|
||||
|
@ -63,14 +70,14 @@ FFZ.prototype._update_subscribers = function() {
|
|||
var stat = document.createElement('span');
|
||||
stat.className = 'ffz stat';
|
||||
stat.id = 'ffz-sub-display';
|
||||
stat.title = 'Active Channel Subscribers';
|
||||
stat.title = 'Subscribers';
|
||||
|
||||
stat.innerHTML = constants.STAR + ' ';
|
||||
|
||||
el = document.createElement('span');
|
||||
stat.appendChild(el);
|
||||
|
||||
Twitch.api.get("chat/" + id + "/badges", null, {version: 3})
|
||||
utils.api.get("chat/" + id + "/badges", null, {version: 3})
|
||||
.done(function(data) {
|
||||
if ( data.subscriber && data.subscriber.image ) {
|
||||
stat.innerHTML = '';
|
||||
|
@ -87,15 +94,12 @@ FFZ.prototype._update_subscribers = function() {
|
|||
jQuery(stat).tipsy({gravity: f.is_dashboard ? "s" : utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
|
||||
}
|
||||
|
||||
el.innerHTML = sub_count;
|
||||
el.innerHTML = utils.number_commas(sub_count);
|
||||
|
||||
} catch(err) {
|
||||
f.error("_update_subscribers: " + err);
|
||||
}
|
||||
}).fail(function(){
|
||||
var el = document.querySelector("#ffz-sub-display");
|
||||
if ( el )
|
||||
el.parentElement.removeChild(el);
|
||||
return;
|
||||
});;
|
||||
});
|
||||
}
|
358
src/utils.js
358
src/utils.js
|
@ -73,100 +73,6 @@ var sanitize_el = document.createElement('span'),
|
|||
},
|
||||
|
||||
|
||||
// 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;
|
||||
},
|
||||
|
||||
uncompressEmotes = function(value) {
|
||||
var output = {},
|
||||
emotes = value.split("/"),
|
||||
|
@ -199,121 +105,211 @@ var sanitize_el = document.createElement('span'),
|
|||
},
|
||||
|
||||
|
||||
// This code borrowed from the twemoji project, with tweaks.
|
||||
UFE0Fg = /\uFE0F/g,
|
||||
U200D = String.fromCharCode(0x200D),
|
||||
|
||||
EMOJI_CODEPOINTS = {},
|
||||
emoji_to_codepoint = function(icon, variant) {
|
||||
if ( EMOJI_CODEPOINTS[icon] && EMOJI_CODEPOINTS[icon][variant] )
|
||||
return EMOJI_CODEPOINTS[icon][variant];
|
||||
emoji_to_codepoint = function(surrogates, sep) {
|
||||
if ( EMOJI_CODEPOINTS[surrogates] && EMOJI_CODEPOINTS[surrogates][sep] )
|
||||
return EMOJI_CODEPOINTS[surrogates][sep];
|
||||
|
||||
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;
|
||||
var input = surrogates.indexOf(U200D) === -1 ? surrogates.replace(UFE0Fg, '') : surrogates,
|
||||
out = [],
|
||||
c = 0, p = 0, i = 0;
|
||||
|
||||
while ( i < ico.length ) {
|
||||
c = ico.charCodeAt(i++);
|
||||
while (i < input.length) {
|
||||
c = input.charCodeAt(i++);
|
||||
if ( p ) {
|
||||
r.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
|
||||
out.push((0x10000 + ((p - 0xD800) << 10) + (c - 0xDC00)).toString(16));
|
||||
p = 0;
|
||||
} else if ( 0xD800 <= c && c <= 0xDBFF) {
|
||||
} else if ( 0xD800 <= c && c <= 0xDBFF )
|
||||
p = c;
|
||||
} else {
|
||||
r.push(c.toString(16));
|
||||
}
|
||||
else
|
||||
out.push(c.toString(16));
|
||||
}
|
||||
|
||||
var es = EMOJI_CODEPOINTS[icon] = EMOJI_CODEPOINTS[icon] || {},
|
||||
out = es[variant] = r.join("-");
|
||||
|
||||
return out;
|
||||
var retval = EMOJI_CODEPOINTS[surrogates] = out.join('-');
|
||||
return retval;
|
||||
},
|
||||
|
||||
// Twitch Emote Tooltips
|
||||
codepoint_to_emoji = function(codepoint) {
|
||||
var code = typeof codepoint === 'string' ? parseInt(codepoint, 16) : codepoint;
|
||||
if ( code < 0x10000 )
|
||||
return String.fromCharCode(code);
|
||||
code -= 0x10000;
|
||||
return String.fromCharCode(
|
||||
0xD800 + (code >> 10),
|
||||
0xDC00 + (code & 0x3FF)
|
||||
)
|
||||
},
|
||||
|
||||
|
||||
// Twitch Emote Helpers
|
||||
|
||||
SRCSETS = {},
|
||||
build_srcset = function(id) {
|
||||
if ( SRCSETS[id] )
|
||||
return SRCSETS[id];
|
||||
var out = SRCSETS[id] = constants.TWITCH_BASE + id + "/1.0 1x, " + constants.TWITCH_BASE + id + "/2.0 2x, " + constants.TWITCH_BASE + id + "/3.0 4x";
|
||||
var out = SRCSETS[id] = constants.TWITCH_BASE + id + "/1.0 1x, " + constants.TWITCH_BASE + id + "/2.0 2x";
|
||||
return out;
|
||||
},
|
||||
|
||||
|
||||
data_to_tooltip = function(data) {
|
||||
var emote_set = data.set,
|
||||
set_type = data.set_type,
|
||||
// Twitch API
|
||||
|
||||
f = FFZ.get(),
|
||||
image = '';
|
||||
|
||||
if ( data.id && f.settings.emote_image_hover )
|
||||
image = '<img class="emoticon ffz-image-hover" src="' + constants.TWITCH_BASE + data.id + '/3.0?_=preview">';
|
||||
|
||||
if ( set_type === undefined )
|
||||
set_type = "Channel";
|
||||
|
||||
if ( ! emote_set )
|
||||
return image + data.code;
|
||||
|
||||
else if ( emote_set === "--global--" ) {
|
||||
emote_set = "Twitch Global";
|
||||
set_type = null;
|
||||
|
||||
} else if ( emote_set == "--twitch-turbo--" || emote_set == "turbo" || emote_set == "--turbo-faces--" ) {
|
||||
emote_set = "Twitch Turbo";
|
||||
set_type = null;
|
||||
}
|
||||
|
||||
return image + "Emoticon: " + data.code + "<br>" + (set_type ? set_type + ": " : "") + emote_set;
|
||||
api_call = function(method, url, data, options, token) {
|
||||
options = options || {};
|
||||
var headers = options.headers = options.headers || {};
|
||||
headers['Client-ID'] = constants.CLIENT_ID;
|
||||
if ( token )
|
||||
headers.Authorization = 'OAuth ' + token;
|
||||
return Twitch.api[method].call(this, url, data, options);
|
||||
},
|
||||
|
||||
build_tooltip = function(id, force_update, code) {
|
||||
var emote_data = this._twitch_emotes[id];
|
||||
|
||||
if ( ! emote_data && code ) {
|
||||
var set_id = this._twitch_emote_to_set[id];
|
||||
if ( set_id ) {
|
||||
emote_data = this._twitch_emotes[id] = {
|
||||
code: code,
|
||||
id: id,
|
||||
set: this._twitch_set_to_channel[set_id],
|
||||
set_id: set_id
|
||||
}
|
||||
}
|
||||
}
|
||||
// Dialogs
|
||||
show_modal = function(contents, on_close, width) {
|
||||
var container = document.createElement('div'),
|
||||
subwindow = document.createElement('div'),
|
||||
card = document.createElement('div'),
|
||||
close_button = document.createElement('div'),
|
||||
|
||||
if ( ! emote_data )
|
||||
return "???";
|
||||
closer = function() { container.parentElement.removeChild(container) };
|
||||
|
||||
if ( typeof emote_data == "string" )
|
||||
return emote_data;
|
||||
container.className = 'twitch_subwindow_container';
|
||||
container.id = 'ffz-modal-container';
|
||||
|
||||
if ( ! force_update && emote_data.tooltip )
|
||||
return emote_data.tooltip;
|
||||
subwindow.className = 'twitch_subwindow ffz-subwindow';
|
||||
subwindow.style.width = '100%';
|
||||
subwindow.style.maxWidth = (width||420) + 'px';
|
||||
|
||||
return emote_data.tooltip = data_to_tooltip(emote_data);
|
||||
card.className = 'card';
|
||||
|
||||
close_button.className = 'modal-close-button';
|
||||
close_button.innerHTML = constants.CLOSE;
|
||||
|
||||
close_button.addEventListener('click', function() {
|
||||
closer();
|
||||
if ( on_close )
|
||||
on_close(false);
|
||||
});
|
||||
|
||||
container.appendChild(subwindow);
|
||||
subwindow.appendChild(card);
|
||||
subwindow.appendChild(close_button);
|
||||
|
||||
card.appendChild(contents);
|
||||
|
||||
var el = document.querySelector('app-main');
|
||||
|
||||
if ( el )
|
||||
el.parentElement.insertBefore(container, el.nextSibling);
|
||||
else
|
||||
document.body.appendChild(container);
|
||||
|
||||
return closer;
|
||||
},
|
||||
|
||||
load_emote_data = function(id, code, success, data) {
|
||||
if ( ! success )
|
||||
return code;
|
||||
|
||||
if ( code )
|
||||
data.code = code;
|
||||
ember_lookup = function(thing) {
|
||||
if ( ! window.App )
|
||||
return;
|
||||
|
||||
this._twitch_emotes[id] = data;
|
||||
var tooltip = build_tooltip.bind(this)(id);
|
||||
|
||||
var images = document.querySelectorAll('img[data-emote="' + id + '"]');
|
||||
for(var x=0; x < images.length; x++)
|
||||
images[x].title = tooltip;
|
||||
|
||||
return tooltip;
|
||||
if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance_.registry.lookup )
|
||||
return App.__deprecatedInstance__.registry.lookup(thing);
|
||||
if ( App.__container__ && App.__container__.lookup )
|
||||
return App.__container__.lookup(thing);
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
build_srcset: build_srcset,
|
||||
build_tooltip: build_tooltip,
|
||||
load_emote_data: load_emote_data,
|
||||
module.exports = FFZ.utils = {
|
||||
ember_views: function() {
|
||||
return ember_lookup('-view-registry:main') || {};
|
||||
},
|
||||
|
||||
ember_lookup: ember_lookup,
|
||||
|
||||
ember_resolve: function(thing) {
|
||||
if ( ! window.App )
|
||||
return;
|
||||
|
||||
if ( App.__deprecatedInstance__ && App.__deprecatedInstance__.registry && App.__deprecatedInstance_.registry.resolve )
|
||||
return App.__deprecatedInstance__.registry.resolve(thing);
|
||||
if ( App.__container__ && App.__container__.resolve )
|
||||
return App.__container__.resolve(thing);
|
||||
},
|
||||
|
||||
build_srcset: build_srcset,
|
||||
/*build_tooltip: build_tooltip,
|
||||
load_emote_data: load_emote_data,*/
|
||||
|
||||
api: {
|
||||
del: function(u,d,o,t) { return api_call('del', u,d,o,t); },
|
||||
get: function(u,d,o,t) { return api_call('get', u,d,o,t); },
|
||||
post: function(u,d,o,t) { return api_call('post', u,d,o,t); },
|
||||
put: function(u,d,o,t) { return api_call('put', u,d,o,t); }
|
||||
},
|
||||
|
||||
|
||||
show_modal: show_modal,
|
||||
prompt: function(title, description, old_value, callback, width) {
|
||||
var contents = document.createElement('div'),
|
||||
heading = document.createElement('div'),
|
||||
form = document.createElement('form'),
|
||||
input, close_btn, okay_btn;
|
||||
|
||||
contents.className = 'text-content';
|
||||
heading.className = 'content-header';
|
||||
heading.innerHTML = '<h4>' + title + '</h4>';
|
||||
|
||||
form.innerHTML = '<div class="item">' + (description ? '<p>' + description + '</p>' : '') + '<input type="text"></div><div class="buttons"><a class="js-subwindow-close button"><span>Cancel</span></a><button class="button primary" type="submit"><span>OK</span></button></div>';
|
||||
|
||||
contents.appendChild(heading);
|
||||
contents.appendChild(form);
|
||||
|
||||
input = form.querySelector('input');
|
||||
close_btn = form.querySelector('.js-subwindow-close');
|
||||
okay_btn = form.querySelector('.button.primary');
|
||||
|
||||
if ( old_value !== undefined )
|
||||
input.value = old_value;
|
||||
|
||||
var closer,
|
||||
cb = function(success) {
|
||||
closer();
|
||||
if ( ! callback )
|
||||
return;
|
||||
|
||||
callback(success ? input.value : null);
|
||||
};
|
||||
|
||||
closer = show_modal(contents, cb, width);
|
||||
|
||||
form.addEventListener('submit', function(e) { e.preventDefault(); cb(true); return false });
|
||||
okay_btn.addEventListener('click', function(e) { e.preventDefault(); cb(true); return false });
|
||||
close_btn.addEventListener('click', function(e) { e.preventDefault(); cb(false); return false });
|
||||
},
|
||||
|
||||
|
||||
last_minute: function() {
|
||||
var now = new Date();
|
||||
if ( now.getSeconds() >= 30 )
|
||||
now.setMinutes(now.getMinutes()+1);
|
||||
|
||||
now.setSeconds(0);
|
||||
now.setMilliseconds(0);
|
||||
return now.getTime();
|
||||
},
|
||||
|
||||
maybe_chart: function(series, point, render, force) {
|
||||
var len = series.data.length;
|
||||
if ( force || point.y !== null || (len > 0 && series.data[len-1].y !== null) ) {
|
||||
series.addPoint(point, render);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
update_css: function(element, id, css) {
|
||||
var all = element.innerHTML,
|
||||
|
@ -338,7 +334,11 @@ module.exports = {
|
|||
|
||||
tooltip_placement: function(margin, prefer) {
|
||||
return function() {
|
||||
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
||||
var pref = prefer;
|
||||
if ( typeof pref === "function" )
|
||||
pref = pref.call(this);
|
||||
|
||||
var dir = {ns: pref[0], ew: (pref.length > 1 ? pref[1] : false)},
|
||||
$this = $(this),
|
||||
half_width = $this.width() / 2,
|
||||
half_height = $this.height() / 2,
|
||||
|
@ -354,12 +354,10 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
|
||||
|
||||
splitIRCMessage: splitIRCMessage,
|
||||
parseIRCTags: parseIRCTags,
|
||||
uncompressEmotes: uncompressEmotes,
|
||||
|
||||
emoji_to_codepoint: emoji_to_codepoint,
|
||||
codepoint_to_emoji: codepoint_to_emoji,
|
||||
|
||||
parse_date: parse_date,
|
||||
|
||||
|
@ -418,7 +416,7 @@ module.exports = {
|
|||
var seconds = elapsed % 60,
|
||||
minutes = Math.floor(elapsed / 60),
|
||||
hours = Math.floor(minutes / 60),
|
||||
days = "";
|
||||
days = null;
|
||||
|
||||
minutes = minutes % 60;
|
||||
|
||||
|
@ -431,7 +429,7 @@ module.exports = {
|
|||
days = ( days > 0 ) ? days + " days, " : "";
|
||||
}
|
||||
|
||||
return days + ((!no_hours || days || hours) ? ((days && hours < 10 ? "0" : "") + hours + ':') : '') + (minutes < 10 ? "0" : "") + minutes + (no_seconds ? "" : (":" + (seconds < 10 ? "0" : "") + seconds));
|
||||
return (days||'') + ((!no_hours || days || hours) ? ((days && hours < 10 ? "0" : "") + hours + ':') : '') + (minutes < 10 ? "0" : "") + minutes + (no_seconds ? "" : (":" + (seconds < 10 ? "0" : "") + seconds));
|
||||
},
|
||||
|
||||
duration_string: function(val) {
|
||||
|
|
388
style.css
388
style.css
|
@ -19,6 +19,7 @@ body > div.tipsy .tipsy-arrow { opacity: 0.8; }
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ffz-hide-recommended-channels .js-recommended-channels,
|
||||
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
|
||||
.ffz-hide-view-count .stat.twitch-channel-views,
|
||||
.ffz-minimal-chat-input .chat-interface .emoticon-selector-toggle,
|
||||
|
@ -125,7 +126,7 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
}
|
||||
|
||||
|
||||
.ember-chat .chat-menu.ffz-ui-popup { padding: 0; }
|
||||
.ember-chat .chat-menu.ffz-ui-popup { padding: 0 }
|
||||
|
||||
.ffz-button {
|
||||
float: right;
|
||||
|
@ -216,8 +217,8 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
color: #aaa;
|
||||
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 120px;
|
||||
top: -20px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
z-index: 7;
|
||||
opacity: 0.95;
|
||||
|
@ -525,6 +526,31 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.emoticon-selector .emoticon-grid.ffz-no-emotes img {
|
||||
padding: 5px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
.emoticon-selector .emoticon-grid.ffz-no-emotes {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.emoticon.ffz-favorite { position: relative; }
|
||||
|
||||
.favorites-grid:not(:empty) + .ffz-no-emotes,
|
||||
.favorites-grid .emoticon.ffz-favorite:before { display: none }
|
||||
|
||||
.emoticon.ffz-favorite:before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 14px; width: 14px;
|
||||
position: absolute;
|
||||
right: 0; bottom: 0;
|
||||
background: url("//cdn.frankerfacez.com/script/emoticon_favorite.png") no-repeat;
|
||||
}
|
||||
|
||||
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content.menu-side-padding { padding-left: 20px; padding-right: 20px; }
|
||||
|
||||
.emoticon-grid.collapsed span,
|
||||
|
@ -584,9 +610,31 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
.ffz-ui-sub-menu-page,
|
||||
.ffz-ui-menu-page { overflow-y: auto; }
|
||||
|
||||
.ffz-ui-menu-page[data-page="about"],
|
||||
.ffz-ui-menu-page[data-page="about"] .ffz-ui-sub-menu-page .chat-menu-content:first-child {
|
||||
padding: 20px 0 10px;
|
||||
}
|
||||
|
||||
.ffz-ui-sub-menu-page[data-page="about"],
|
||||
.ffz-ui-menu-page .chat-menu-content p { padding: 0 20px; }
|
||||
|
||||
.ffz-ui-menu-page .version-list span { float: right }
|
||||
.ffz-ui-menu-page .version-list li:not(:last-of-type) {
|
||||
border-bottom: 1px dotted rgba(127,127,127,0.5);
|
||||
}
|
||||
|
||||
.ffz-ui-menu-page pre {
|
||||
box-shadow: none !important;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#ffz-old-news-button {
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
#ffz-old-news { display: none }
|
||||
|
||||
|
||||
.chat-container.dark .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
|
||||
.app-main.theatre .chat-container .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
|
||||
|
@ -618,7 +666,7 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid:first-of-type .heading {
|
||||
border-top: none;
|
||||
padding-top: 0;
|
||||
background-position-y: 0;
|
||||
background-position: 20px 0;
|
||||
}
|
||||
|
||||
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content {
|
||||
|
@ -661,6 +709,7 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
list-style-type: none;
|
||||
border-top: 1px solid rgba(0,0,0,0.2);
|
||||
background-color: #eee;
|
||||
margin: 0 1px 1px;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.menu:not(.sub-menu) {
|
||||
|
@ -695,7 +744,10 @@ body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .chat-interface .emotic
|
|||
float: left;
|
||||
}
|
||||
|
||||
.ffz-ui-popup ul.sub-menu { background-color: #dfdfdf; }
|
||||
.ffz-ui-popup ul.sub-menu {
|
||||
background-color: #dfdfdf;
|
||||
margin: 0 1px;
|
||||
}
|
||||
|
||||
.app-main.theatre .ffz-ui-popup ul.sub-menu,
|
||||
.chat-container.dark .ffz-ui-popup ul.sub-menu,
|
||||
|
@ -814,6 +866,10 @@ span.ffz-handle:after { left: 8px }
|
|||
border-right: 1px solid rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.ffz-ui-popup.dark ul.menu { border-top-color: rgba(255,255,255,0.1) }
|
||||
.ffz-ui-popup.dark ul.menu a { border-left-color: rgba(255,255,255,0.1) }
|
||||
.ffz-ui-popup.dark ul.sub-menu a { border-right-color: rgba(255,255,255,0.1) }
|
||||
|
||||
.ffz-ui-popup ul.menu li.active {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
@ -898,12 +954,43 @@ span.ffz-handle:after { left: 8px }
|
|||
|
||||
/* BTTV Menu Fixes */
|
||||
|
||||
.bttv-incompatibility {
|
||||
padding-top: 8px !important;
|
||||
}
|
||||
|
||||
.ffz-ui-popup.dark .emoticon-grid .heading,
|
||||
.ffz-ui-popup.dark li.title { color: #fff; }
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page { background-color: #1e1e1e; }
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page { background-color: #101010; }
|
||||
.ffz-bttv .ffz-ui-popup .chat-menu-content p a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ffz-bttv .ffz-ui-popup.dark ul.menu,
|
||||
.ffz-bttv .ffz-ui-popup.dark .ffz-ui-menu-page { margin: 0 }
|
||||
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page .chat-menu-content .heading,
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page .emoticon-grid .heading {
|
||||
border-color: rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.ffz-ui-popup.dark ul.menu:not(.sub-menu),
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page { border: 1px solid rgba(255,255,255,0.1) }
|
||||
.ffz-ui-popup.dark .ffz-ui-menu-page { border-bottom: none }
|
||||
|
||||
.ffz-bttv .emoticon.emoji { padding-top: 0 }
|
||||
|
||||
/* Host Menu */
|
||||
|
||||
.ffz-subwindow input {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.ffz-subwindow p {
|
||||
margin-bottom: 0;
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
|
||||
.ffz-subwindow .card,
|
||||
.ffz-channel-selector {
|
||||
background-image: url("//cdn.frankerfacez.com/script/zreknarf-bg.png");
|
||||
background-repeat: no-repeat;
|
||||
|
@ -946,6 +1033,29 @@ span.ffz-handle:after { left: 8px }
|
|||
}
|
||||
|
||||
|
||||
/* Oh my god this is awful why does Chat Replay move chat lines? */
|
||||
|
||||
.app-main.theatre .chatReplay .chat-messages .chat-line .mod-icons,
|
||||
.ffz-dark .chatReplay .chat-messages .chat-line .mod-icons {
|
||||
background-color: rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.chatReplay .chat-messages .chat-line .mod-icons {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
left: 0; top: 2px; bottom: 2px;
|
||||
padding: 4px 5px 4px 4px;
|
||||
background: rgba(255,255,255,0.8);
|
||||
transition: margin-left .08s linear;
|
||||
}
|
||||
|
||||
.chatReplay .chat-messages .chat-line:hover .mod-icons {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.chatReplay .chat-messages .horizontal-line { margin: 4px 0 }
|
||||
|
||||
|
||||
/* Positioning Fixes */
|
||||
|
||||
body:not(.ffz-bttv) .ember-chat .chat-settings { bottom: 40px; }
|
||||
|
@ -953,8 +1063,36 @@ body:not(.ffz-bttv) .notification-controls .notify-menu { bottom: 25px; }
|
|||
body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; }
|
||||
|
||||
|
||||
/* Dashboard Changes */
|
||||
|
||||
#dash_main #stream-config-status, #chart_container.ffz-stat-chart { margin: 20px auto 0 }
|
||||
|
||||
/*#dash_main #video_player { height: 303.75px }
|
||||
|
||||
#stream-config-status { display: none }
|
||||
|
||||
#delay_controls { float: left }
|
||||
#delay_controls + #commercial_buttons { float: right }
|
||||
#delay_controls + #commercial_buttons + div { clear: both }
|
||||
|
||||
#delay_controls, #delay_controls + #commercial_buttons {
|
||||
width: 50%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#dash_main #delay_controls, #dash_main #commercial_buttons {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
#dash_main #delay_controls .section_header, #commercial_buttons .section_header { padding-top: 0 }
|
||||
#dash_main #delay_controls .ui-slider { margin-top: 18px }*/
|
||||
|
||||
|
||||
/* Menu Scrollbar */
|
||||
|
||||
.conversations-list .scroll-container::-webkit-scrollbar,
|
||||
.chatters-container::-webkit-scrollbar,
|
||||
.ffz-scrollbar::-webkit-scrollbar,
|
||||
.ember-chat .chat-settings::-webkit-scrollbar,
|
||||
|
@ -965,9 +1103,11 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; }
|
|||
.emoticon-selector-box .all-emotes::-webkit-scrollbar,
|
||||
.ffz-ui-sub-menu-page::-webkit-scrollbar,
|
||||
.ffz-ui-menu-page::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.conversations-list .scroll-container::-webkit-scrollbar-thumb,
|
||||
.chatters-container::-webkit-scrollbar-thumb,
|
||||
.ffz-scrollbar::-webkit-scrollbar-thumb,
|
||||
.ember-chat .chat-settings::-webkit-scrollbar-thumb,
|
||||
|
@ -983,12 +1123,14 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; }
|
|||
box-shadow: 0 0 1px 1px rgba(255,255,255,0.25);
|
||||
}
|
||||
|
||||
.ffz-dark .conversations-list .scroll-container::-webkit-scrollbar-thumb,
|
||||
.ffz-dark .ffz-scrollbar::-webkit-scrollbar-thumb,
|
||||
.ffz-dark .table::-webkit-scrollbar-thumb,
|
||||
.ffz-dark .conversation-window .conversation-content::-webkit-scrollbar-thumb,
|
||||
.ffz-dark .conversations-list .conversations-list-inner::-webkit-scrollbar-thumb,
|
||||
.ffz-dark .conversation-input-bar .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
|
||||
|
||||
.theatre .conversations-list .scroll-container::-webkit-scrollbar-thumb,
|
||||
.theatre .chatters-container::-webkit-scrollbar-thumb,
|
||||
.theatre .ffz-scrollbar::-webkit-scrollbar-thumb,
|
||||
.theatre .ember-chat .chat-settings::-webkit-scrollbar-thumb,
|
||||
|
@ -1027,6 +1169,7 @@ body:not(.ffz-bttv) .dropmenu.share { margin-bottom: 0; }
|
|||
|
||||
/* Fix Moderation Cards */
|
||||
|
||||
img.channel_background[src=""],
|
||||
img.channel_background[src="null"] { display: none; }
|
||||
|
||||
.ffz-moderation-card {
|
||||
|
@ -1034,7 +1177,11 @@ img.channel_background[src="null"] { display: none; }
|
|||
max-width: 340px;
|
||||
}
|
||||
|
||||
.ffz-moderation-card .extra-interface {
|
||||
.ember-chat .ffz-moderation-card .close-button {
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
.ember-chat .ffz-moderation-card .extra-interface {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
|
@ -1043,7 +1190,9 @@ img.channel_background[src="null"] { display: none; }
|
|||
}
|
||||
|
||||
.ffz-moderation-card.ffz-has-info h3.name {
|
||||
margin-top: 0;
|
||||
margin-top: -2px;
|
||||
margin-bottom: 0;
|
||||
padding-top: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
@ -1054,6 +1203,11 @@ img.channel_background[src="null"] { display: none; }
|
|||
margin-left: 50px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ffz-moderation-card .info .stat {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.ffz-moderation-card .info.channel-stats .stat {
|
||||
|
@ -1108,7 +1262,7 @@ img.channel_background[src="null"] { display: none; }
|
|||
text-shadow: black 0 0 5px;
|
||||
}
|
||||
|
||||
.ffz-moderation-card .channel_background {
|
||||
.ember-chat .ffz-moderation-card .channel_background {
|
||||
width: 100%;
|
||||
top: 0;
|
||||
}
|
||||
|
@ -1156,13 +1310,14 @@ img.channel_background[src="null"] { display: none; }
|
|||
text-decoration: none;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-top: -1px !important;
|
||||
color: #888 !important;
|
||||
}
|
||||
|
||||
|
||||
/* Chat Rows */
|
||||
|
||||
.ffz-alias { font-style: italic; }
|
||||
.ffz-alias-italics .ffz-alias { font-style: italic; }
|
||||
|
||||
.ember-chat .chat-messages .chat-line.ffz-has-deleted {
|
||||
line-height: 30px;
|
||||
|
@ -1184,7 +1339,8 @@ img.channel_background[src="null"] { display: none; }
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
body:not(.ffz-bttv) .more-messages-indicator {
|
||||
body:not(.ffz-bttv) .ember-chat-container:not(.chatReplay) .more-messages-indicator,
|
||||
body:not(.ffz-bttv) .chat-container:not(.chatReplay) .more-messages-indicator {
|
||||
/* This looks better when it's full width. */
|
||||
margin: 0 -20px;
|
||||
}
|
||||
|
@ -1199,6 +1355,10 @@ body:not(.ffz-bttv) .more-messages-indicator {
|
|||
|
||||
/* Emoticon Tooltips */
|
||||
|
||||
.ffz-wide-tip hr {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.ffz-wide-tip .tipsy-inner {
|
||||
min-width: 300px;
|
||||
max-width: 600px;
|
||||
|
@ -1310,6 +1470,8 @@ body:not(.ffz-bttv) .more-messages-indicator {
|
|||
|
||||
/* Dumb Fixes */
|
||||
|
||||
.channel-stats .stat { vertical-align: top; }
|
||||
|
||||
.ffz-bttv .no-bttv { display: none; }
|
||||
|
||||
.chat-container.dark, .app-main.theatre .chat-container,
|
||||
|
@ -1320,6 +1482,15 @@ body:not(.ffz-bttv) .more-messages-indicator {
|
|||
}
|
||||
|
||||
|
||||
/* Banned Words */
|
||||
|
||||
.deleted-word {
|
||||
font-weight: bold;
|
||||
opacity: 0.35;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Unsafe Links */
|
||||
|
||||
a.unsafe-link {
|
||||
|
@ -1641,6 +1812,10 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
|
|||
.ember-chat .chat-interface .more-messages-indicator.ffz-freeze-indicator {
|
||||
opacity: 1;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.chat-container:not(.chatReplay) .chat-interface .more-messages-indicator.ffz-freeze-indicator {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
|
@ -1669,6 +1844,9 @@ body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textar
|
|||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.chat-history .chat-line.admin .from,
|
||||
.chat-history .chat-line.admin .colon { display: none }
|
||||
|
||||
.chat-history .chat-line.admin .message { color: #666; }
|
||||
|
||||
.chat-history .timestamp {
|
||||
|
@ -1878,13 +2056,21 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
|
||||
/* No Blue */
|
||||
|
||||
.ffz-no-blue .theatre .conversations-list-icon,
|
||||
.ffz-no-blue.ffz-dark .conversations-list-icon,
|
||||
.ffz-no-blue #carousel .nav {
|
||||
background-color: rgba(25,25,25,.5);
|
||||
}
|
||||
|
||||
.ffz-no-blue .theatre .conversations-list-bottom-bar,
|
||||
.ffz-no-blue.ffz-dark .conversations-list-bottom-bar,
|
||||
.ffz-no-blue .theatre .conversations-list,
|
||||
.ffz-no-blue.ffz-dark .conversations-list,
|
||||
.ffz-no-blue .theatre .conversations-list-header,
|
||||
.ffz-no-blue .theatre .conversation-window,
|
||||
.ffz-no-blue.ffz-dark .conversation-window,
|
||||
.ffz-no-blue .theatre .conversation-system-message,
|
||||
.ffz-no-blue.ffz-dark .conversation-system-message,
|
||||
|
||||
.ffz-no-blue .warp,
|
||||
.ffz-no-blue #large_nav .content,
|
||||
.ffz-no-blue #small_nav .content,
|
||||
.ffz-no-blue .chat-container.dark,
|
||||
|
@ -1940,10 +2126,18 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
.ffz-no-blue .takeover #carousel,
|
||||
.ffz-no-blue #carousel_and_background,
|
||||
.ffz-no-blue #carousel .items .pic img,
|
||||
.ffz-no-blue .app-main.theatre .archives-contain,
|
||||
.ffz-no-blue.ffz-dark .archives-contain,
|
||||
.ffz-no-blue #content .turbo_landing {
|
||||
background-color: #191919;
|
||||
}
|
||||
|
||||
.ffz-no-blue .warp__anchor,
|
||||
.ffz-no-blue .warp__item--anchor,
|
||||
.ffz-no-blue .warp__drawer,
|
||||
.ffz-no-blue .leaf,
|
||||
.ffz-no-blue .app-main.theatre .archives-contain .list-video:hover,
|
||||
.ffz-no-blue.ffz-dark .archives-contain .list-video:hover,
|
||||
.ffz-no-blue .theatre .moderation-card .back-button,
|
||||
.ffz-no-blue .dark .moderation-card .back-button,
|
||||
.ffz-no-blue .force-dark .moderation-card .back-button
|
||||
|
@ -1956,8 +2150,8 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
background-color: #232323;
|
||||
}
|
||||
|
||||
.ffz-no-blue .theatre .conversations-list-icon,
|
||||
.ffz-no-blue.ffz-dark .conversations-list-icon,
|
||||
.ffz-no-blue .theatre .conversations-list-bottom-bar,
|
||||
.ffz-no-blue.ffz-dark .conversations-list-bottom-bar,
|
||||
.ffz-no-blue .theatre .conversations-list .conversation-preview-line,
|
||||
.ffz-no-blue.ffz-dark .conversations-list .conversation-preview-line,
|
||||
.ffz-no-blue .theatre .conversation-window,
|
||||
|
@ -1991,7 +2185,8 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
|
||||
.ffz-no-blue .theatre .conversation-header,
|
||||
.ffz-no-blue.ffz-dark .conversation-header,
|
||||
.ffz-no-blue .theatre .conversations-list .conversations-list-header,
|
||||
.ffz-no-blue .theatre .conversations-list .search-divider,
|
||||
.ffz-no-blue.ffz-dark .conversations-list .search-divider,
|
||||
.ffz-no-blue.ffz-dark .conversations-list .conversations-list-header,
|
||||
.ffz-no-blue .theatre .conversation-window.has-focus .conversation-header,
|
||||
.ffz-no-blue.ffz-dark .conversation-window.has-focus .conversation-header,
|
||||
|
@ -2036,14 +2231,13 @@ li[data-name="following"] a {
|
|||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
#large_nav .game_filter.selected .ffz-follow-count { right: 13px; }
|
||||
|
||||
#small_nav .ffz-follow-count {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
padding: 0 2px;
|
||||
right: 5px;
|
||||
padding: 2px;
|
||||
font-size: 10px;
|
||||
line-height: 10px;
|
||||
background-color: #191919;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
@ -2053,8 +2247,6 @@ li[data-name="following"] a {
|
|||
background-color: #101014;
|
||||
}
|
||||
|
||||
#small_nav .game_filter.selected .ffz-follow-count { right: 5px; }
|
||||
|
||||
/* Image Tooltips */
|
||||
|
||||
.ffz-image-hover {
|
||||
|
@ -2109,7 +2301,9 @@ li[data-name="following"] a {
|
|||
}
|
||||
|
||||
.ffz-classic-player:not(.ffz-top-conversations) .app-main.theatre .player .player-controls-bottom,
|
||||
.ffz-classic-player .app-main.theatre .player[data-fullscreen="true"] .player-controls-bottom {
|
||||
.ffz-classic-player:not(.ffz-top-conversations) .app-main.theatre .player[data-controls=true] .player-controls-bottom,
|
||||
.ffz-classic-player .app-main.theatre .player[data-fullscreen="true"] .player-controls-bottom,
|
||||
.ffz-classic-player .app-main.theatre .player[data-fullscreen="true"][data-controls=true] .player-controls-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
|
@ -2174,6 +2368,13 @@ li[data-name="following"] a {
|
|||
|
||||
/* Directory Logos */
|
||||
|
||||
.item .meta .title a a,
|
||||
.item .meta .title a img { pointer-events: none }
|
||||
|
||||
.ffz-directory-logo .meta p.title {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.ffz-directory-logo .meta p { width: auto; }
|
||||
|
||||
.ffz-directory-logo .profile-photo {
|
||||
|
@ -2240,11 +2441,16 @@ body:not(.ffz-bttv) .conversation-window .new-message-divider + .timestamp-line
|
|||
margin-top: -3px;
|
||||
}
|
||||
|
||||
/* Fix the ignore-cta covering up messages with no way to dismiss it. */
|
||||
body:not(.ffz-bttv) .conversation-window .ignore-cta + .conversation-content {
|
||||
/* ignore-cta is a bit silly right now */
|
||||
body:not(.ffz-bttv) .conversation-window .ignore-cta:not(.hidden) + .conversation-content {
|
||||
padding-top: 76px;
|
||||
}
|
||||
|
||||
.conversation-window .conversation-system-message.ignore-cta-container {
|
||||
padding-right: 26px;
|
||||
}
|
||||
|
||||
|
||||
/* Hide that which should be hidden. */
|
||||
.conversation-window.collapsed .ignore-cta,
|
||||
.conversation-chat-line.action .colon,
|
||||
|
@ -2255,25 +2461,11 @@ body:not(.ffz-conv-title-clickable) .conversation-header a.conversation-header-n
|
|||
|
||||
.conversation-window.has-unread .conversation-header .conversation-header-name { color: #fff !important; }
|
||||
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversation-window.collapsed .conversation-header {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.conversations-list:not(.ffz-bttv) .conversations-list-item:last-of-type {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
/* Top Conversations */
|
||||
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversations-content .conversations-list-icon,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversation-window {
|
||||
border: 1px solid #dedede;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.ffz-top-conversations.ffz-dark:not(.ffz-bttv) .conversations-content .conversations-list-icon,
|
||||
.ffz-top-conversations.ffz-dark:not(.ffz-bttv) .conversation-window,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .theatre .conversations-content .conversations-list-icon,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .theatre .conversation-window {
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
body:not(.ffz-top-conversations) .conversations-list-bottom-bar {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.ffz-top-conversations .conversations-content {
|
||||
|
@ -2287,47 +2479,25 @@ body:not(.ffz-conv-title-clickable) .conversation-header a.conversation-header-n
|
|||
top: 0px;
|
||||
}
|
||||
|
||||
.ffz-top-conversations .conversations-list {
|
||||
bottom: inherit;
|
||||
top: 54px;
|
||||
.ffz-top-conversations .conversation-window.collapsed .conversation-header {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ffz-top-conversations.ffz-bttv .conversations-list { top: 46px; }
|
||||
|
||||
.ffz-top-conversations.ffz-bttv .conversations-list:before,
|
||||
.ffz-top-conversations.ffz-bttv .conversations-list:after { display: none; }
|
||||
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversations-list:before,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversations-list:after {
|
||||
bottom: 100%;
|
||||
top: auto;
|
||||
.ffz-top-conversations .conversations-list-container.list-displayed {
|
||||
top: 243px;
|
||||
}
|
||||
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversations-list:before {
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: #dedede;
|
||||
.ffz-top-conversations .conversations-list-bottom-bar {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.ffz-top-conversations:not(.ffz-bttv) .conversations-list:after {
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: #f2f2f2;
|
||||
.ffz-top-conversations .conversations-content .total-unread {
|
||||
top: inherit;
|
||||
bottom: -10px;
|
||||
}
|
||||
|
||||
|
||||
.ffz-dark.ffz-top-conversations:not(.ffz-bttv) .conversations-list:before,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .theatre .conversations-list:before {
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: #32323e;
|
||||
}
|
||||
|
||||
.ffz-dark.ffz-top-conversations:not(.ffz-bttv) .conversations-list:after,
|
||||
.ffz-top-conversations:not(.ffz-bttv) .theatre .conversations-list:after {
|
||||
border-top-color: transparent;
|
||||
border-bottom-color: #121212;
|
||||
}
|
||||
|
||||
|
||||
.ffz-top-conversations .theatre .player-controls-bottom {
|
||||
.ffz-top-conversations .theatre .player-controls-bottom,
|
||||
.ffz-top-conversations .theatre .player[data-controls=true] .player-controls-bottom {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
|
@ -2337,8 +2507,52 @@ body:not(.ffz-conv-title-clickable) .conversation-header a.conversation-header-n
|
|||
|
||||
.ffz-top-conversations #directory-list { padding-top: 50px; }
|
||||
|
||||
|
||||
/* Minimize Conversations */
|
||||
|
||||
/*.ffz-minimize-conversations .conversation-window,*/
|
||||
.ffz-minimize-conversations .conversations-list-container {
|
||||
transition: bottom ease-in-out 75ms, top ease-in-out 75ms, padding-bottom ease-in-out 75ms, padding-top ease-in-out 75ms;
|
||||
}
|
||||
|
||||
.ffz-minimize-conversations .conversations-list-container,
|
||||
.ffz-minimize-conversations .conversations-content:hover {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
|
||||
.ffz-minimize-conversations:not(.ffz-top-conversations) .conversations-content:hover .conversation-window.collapsed:not(.has-unread),
|
||||
.ffz-minimize-conversations:not(.ffz-top-conversations) .conversations-content:hover .conversations-list-container:not(.list-displayed) {
|
||||
bottom: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ffz-minimize-conversations:not(.ffz-top-conversations) .conversation-window.collapsed:not(.has-unread),
|
||||
.ffz-minimize-conversations:not(.ffz-top-conversations) .conversations-list-container:not(.list-displayed) {
|
||||
bottom: -30px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.ffz-minimize-conversations.ffz-top-conversations .conversations-content:hover .conversation-window.collapsed:not(.has-unread),
|
||||
.ffz-minimize-conversations.ffz-top-conversations .conversations-content:hover .conversations-list-container:not(.list-displayed) {
|
||||
top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ffz-minimize-conversations.ffz-top-conversations .conversation-window.collapsed:not(.has-unread),
|
||||
.ffz-minimize-conversations.ffz-top-conversations .conversations-list-container:not(.list-displayed) {
|
||||
top: -30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
|
||||
/* Following Page */
|
||||
|
||||
.directory_header .nav li.ffz-manage-following {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.user.item { position: relative; }
|
||||
|
||||
.user.item .overlay_info.length {
|
||||
|
@ -2452,3 +2666,25 @@ body:not(.ffz-creative-showcase) .creative-hero,
|
|||
color: #fff !important;
|
||||
background-color: #6441a5 !important;
|
||||
}
|
||||
|
||||
/* Content-Box~! Down with Twitch randomly changing the box-sizing for everything! */
|
||||
|
||||
#ffz-channel-table *,
|
||||
#ffz-group-table *,
|
||||
.ffz-ui-popup * {
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
|
||||
/* Dank * /
|
||||
|
||||
.streams .ember-view[data-channel="memeathon"] .stream .thumb:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
left: 0; right: 0;
|
||||
background: url("//cdn.frankerfacez.com/script/wow.gif") no-repeat;
|
||||
background-size: contain;
|
||||
background-position: bottom right;
|
||||
pointer-events: none;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue