mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-08-11 00:20:54 +00:00
Re-factored chat rendering. Lots of other stuff. Still don't remember to commit frequently like I should. :(
This commit is contained in:
parent
02b0a95bd0
commit
c5f55bd6b8
22 changed files with 2875 additions and 1433 deletions
55
dark.css
55
dark.css
|
@ -167,21 +167,40 @@
|
||||||
.ffz-dark form.js-new_panel_form,
|
.ffz-dark form.js-new_panel_form,
|
||||||
.ffz-dark .js-new_panel_btn,
|
.ffz-dark .js-new_panel_btn,
|
||||||
.ffz-dark .manager .videos-grid .video .meta,
|
.ffz-dark .manager .videos-grid .video .meta,
|
||||||
.ffz-dark .ember-chat .chat-room-list {
|
.ffz-dark .ember-chat .chat-room-list,
|
||||||
|
.ffz-dark .st-autocomplete-sidebar,
|
||||||
|
.ffz-dark .st-autocomplete-small,
|
||||||
|
.ffz-dark .st-autocomplete,
|
||||||
|
.ffz-dark .st-autocomplete-small,
|
||||||
|
.ffz-dark .player-menu__menu {
|
||||||
background-color: rgb(16,16,16);
|
background-color: rgb(16,16,16);
|
||||||
color: rgb(195,195,195); /*#acacbf;*/
|
color: rgb(195,195,195); /*#acacbf;*/
|
||||||
border-color: #32323e;
|
border-color: #32323e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .st-autocomplete-sidebar .label,
|
||||||
|
.ffz-dark .st-autocomplete-small .label,
|
||||||
|
.ffz-dark .st-autocomplete .label,
|
||||||
|
.ffz-dark .st-autocomplete-small .label {
|
||||||
|
background-color: rgb(24,24,24);
|
||||||
|
}
|
||||||
|
|
||||||
.ffz-dark .js-new_panel_btn:hover {
|
.ffz-dark .js-new_panel_btn:hover {
|
||||||
background-color: rgb(24,24,24);
|
background-color: rgb(24,24,24);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-dark #flyout .point::before {
|
.ffz-dark .player-menu__menu:before,
|
||||||
|
.ffz-dark .player-menu__menu:after {
|
||||||
|
border-top-color: rgb(16,16,16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .st-autocomplete-sidebar:before,
|
||||||
|
.ffz-dark #flyout .point:before {
|
||||||
border-right-color: rgb(16,16,16); /*rgb(25,25,31);*/
|
border-right-color: rgb(16,16,16); /*rgb(25,25,31);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-dark #flyout .point::after {
|
.ffz-dark .st-autocomplete-sidebar:after,
|
||||||
|
.ffz-dark #flyout .point:after {
|
||||||
border-right-color: #32323e;
|
border-right-color: #32323e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,6 +224,7 @@
|
||||||
.ffz-dark .dropmenu input,
|
.ffz-dark .dropmenu input,
|
||||||
.ffz-dark input.text,
|
.ffz-dark input.text,
|
||||||
.ffz-dark input.string,
|
.ffz-dark input.string,
|
||||||
|
.ffz-dark #sidebar_query_small,
|
||||||
.ffz-dark textarea,
|
.ffz-dark textarea,
|
||||||
.ffz-dark select,
|
.ffz-dark select,
|
||||||
.ffz-dark option,
|
.ffz-dark option,
|
||||||
|
@ -237,10 +257,18 @@
|
||||||
color: #8c8c9c;
|
color: #8c8c9c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-dark .st-autocomplete-sidebar .all p:not(.active),
|
||||||
|
.ffz-dark .st-autocomplete-small .all p:not(.active),
|
||||||
|
.ffz-dark .st-autocomplete .all p:not(.active),
|
||||||
|
.ffz-dark .st-autocomplete-small .all p:not(.active),
|
||||||
|
.ffz-dark .st-autocomplete-sidebar .list ul .result:not(.active) p,
|
||||||
|
.ffz-dark .st-autocomplete-small .list ul .result:not(.active) p,
|
||||||
|
.ffz-dark .st-autocomplete .list ul .result:not(.active) p,
|
||||||
|
.ffz-dark .st-autocomplete-small .list ul .result:not(.active) p,
|
||||||
.ffz-dark .manager .videos-grid .video:hover .meta .actions li a,
|
.ffz-dark .manager .videos-grid .video:hover .meta .actions li a,
|
||||||
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
|
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
|
||||||
.ffz-dark .dropmenu_action:not(:hover) span,
|
.ffz-dark .dropmenu_action:not(:hover) span,
|
||||||
.ffz-dark a:not(.button):not(.switch):not(.follow):not(.fb_button) {
|
.ffz-dark a:not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button) {
|
||||||
color: #a68ed2;
|
color: #a68ed2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,7 +695,7 @@
|
||||||
background-color: rgba(255,255,255,0.05);
|
background-color: rgba(255,255,255,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ffz-dark .ui-slider-horizontal {
|
.ffz-dark .ui-slider-horizontal:not(.player-slider) {
|
||||||
background-color: rgba(255,255,255,0.2);
|
background-color: rgba(255,255,255,0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,4 +877,21 @@
|
||||||
|
|
||||||
.ffz-dark .legal_page ol.legal li p {
|
.ffz-dark .legal_page ol.legal li p {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search */
|
||||||
|
|
||||||
|
.ffz-dark ul.subtabs,
|
||||||
|
.ffz-dark .user_list .user,
|
||||||
|
.ffz-dark .mixed_list .user,
|
||||||
|
.ffz-dark .video_list .video,
|
||||||
|
.ffz-dark .mixed_list .video {
|
||||||
|
border-bottom-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ffz-dark .user_list .user .user_stats .followers_count,
|
||||||
|
.ffz-dark .mixed_list .user .user_stats .followers_count,
|
||||||
|
.ffz-dark li.video.vods img.video_type {
|
||||||
|
filter: invert(100%);
|
||||||
|
-webkit-filter: invert(100%);
|
||||||
}
|
}
|
14
script.min.js
vendored
14
script.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -143,7 +143,7 @@ FFZ.prototype.bttv_badges = function(data) {
|
||||||
var replaced = false;
|
var replaced = false;
|
||||||
for(var i=0; i < data.badges.length; i++) {
|
for(var i=0; i < data.badges.length; i++) {
|
||||||
var b = data.badges[i];
|
var b = data.badges[i];
|
||||||
if ( b.type == full_badge.replaces ) {
|
if ( b.type === full_badge.replaces_type ) {
|
||||||
b.type = "ffz-badge-replacement " + b.type;
|
b.type = "ffz-badge-replacement " + b.type;
|
||||||
b.description += ", " + (badge.title || full_badge.title) +
|
b.description += ", " + (badge.title || full_badge.title) +
|
||||||
'" style="background-image: url("' + (badge.image || full_badge.image) + "")";
|
'" style="background-image: url("' + (badge.image || full_badge.image) + "")";
|
||||||
|
@ -183,6 +183,58 @@ FFZ.prototype.bttv_badges = function(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype.render_badges = function(component, badges) {
|
||||||
|
if ( ! this.settings.show_badges )
|
||||||
|
return badges;
|
||||||
|
|
||||||
|
var user = component.get('msgObject.from'),
|
||||||
|
room_id = component.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id');
|
||||||
|
|
||||||
|
var data = this.users[user];
|
||||||
|
if ( ! data || ! data.badges )
|
||||||
|
return badges;
|
||||||
|
|
||||||
|
for(var slot in data.badges) {
|
||||||
|
if ( ! data.badges.hasOwnProperty(slot) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var badge = data.badges[slot],
|
||||||
|
full_badge = this.badges[badge.id] || {},
|
||||||
|
old_badge = badges[slot];
|
||||||
|
|
||||||
|
if ( full_badge.visible !== undefined ) {
|
||||||
|
var visible = full_badge.visible;
|
||||||
|
if ( typeof visible === "function" )
|
||||||
|
visible = visible.bind(this)(room_id, user, component, badges);
|
||||||
|
|
||||||
|
if ( ! visible )
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( old_badge ) {
|
||||||
|
var replaces = badge.hasOwnProperty('replaces') ? badge.replaces : full_badge.replaces;
|
||||||
|
if ( ! replaces )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
old_badge.image = badge.image || full_badge.image;
|
||||||
|
old_badge.klass += ' ffz-badge-replacement';
|
||||||
|
old_badge.title += ', ' + (badge.title || full_badge.title);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
badges[slot] = {
|
||||||
|
klass: 'ffz-badge-' + badge.id,
|
||||||
|
title: badge.title || full_badge.title,
|
||||||
|
image: badge.image,
|
||||||
|
color: badge.color,
|
||||||
|
extra_css: badge.extra_css
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return badges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.render_badge = function(component) {
|
FFZ.prototype.render_badge = function(component) {
|
||||||
if ( ! this.settings.show_badges )
|
if ( ! this.settings.show_badges )
|
||||||
return;
|
return;
|
||||||
|
@ -288,8 +340,9 @@ FFZ.prototype._legacy_add_donors = function() {
|
||||||
|
|
||||||
// Bot Badge
|
// Bot Badge
|
||||||
this.badges[2] = {id: 2, title: "Bot", color: "#595959", image: "//cdn.frankerfacez.com/script/boticon.png",
|
this.badges[2] = {id: 2, title: "Bot", color: "#595959", image: "//cdn.frankerfacez.com/script/boticon.png",
|
||||||
replaces: 'moderator',
|
replaces: true, replaces_type: "moderator",
|
||||||
visible: function(r,user) { return !(this.has_bttv && FFZ.bttv_known_bots.indexOf(user)!==-1); }};
|
visible: function(r,user) { return !(this.has_bttv && FFZ.bttv_known_bots.indexOf(user)!==-1); }};
|
||||||
|
|
||||||
utils.update_css(this._badge_style, 2, badge_css(this.badges[2]));
|
utils.update_css(this._badge_style, 2, badge_css(this.badges[2]));
|
||||||
|
|
||||||
// Load BTTV Bots
|
// Load BTTV Bots
|
||||||
|
|
|
@ -52,7 +52,40 @@ FFZ.settings_info.fix_color = {
|
||||||
this._rebuild_colors();
|
this._rebuild_colors();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.luv_contrast = {
|
||||||
|
type: "button",
|
||||||
|
value: 4.5,
|
||||||
|
|
||||||
|
category: "Chat Appearance",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Username Colors - Luv Minimum 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);
|
||||||
|
|
||||||
|
if ( new_val === null || new_val === undefined )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var parsed = parseFloat(new_val);
|
||||||
|
if ( parsed === NaN || parsed < 1 )
|
||||||
|
parsed = 4.5;
|
||||||
|
|
||||||
|
this.settings.set("luv_contrast", parsed);
|
||||||
|
},
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
this._rebuild_contrast();
|
||||||
|
|
||||||
|
if ( ! this.has_bttv && this.settings.fix_color == '1' )
|
||||||
|
this._rebuild_colors();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.color_blind = {
|
FFZ.settings_info.color_blind = {
|
||||||
type: "select",
|
type: "select",
|
||||||
|
@ -85,6 +118,7 @@ FFZ.prototype.setup_colors = function() {
|
||||||
this.log("Preparing color-alteration style element.");
|
this.log("Preparing color-alteration style element.");
|
||||||
|
|
||||||
this._colors = {};
|
this._colors = {};
|
||||||
|
this._rebuild_contrast();
|
||||||
|
|
||||||
var s = this._color_style = document.createElement('style');
|
var s = this._color_style = document.createElement('style');
|
||||||
s.id = 'ffz-style-username-colors';
|
s.id = 'ffz-style-username-colors';
|
||||||
|
@ -493,18 +527,15 @@ 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); }
|
LUVColor.prototype._v = function(v) { return new LUVColor(this.l, this.u, v); }
|
||||||
|
|
||||||
|
|
||||||
// Required Colors
|
|
||||||
|
|
||||||
var REQUIRED_CONTRAST = 4.5,
|
|
||||||
|
|
||||||
REQUIRED_BRIGHT = new XYZColor(0, (REQUIRED_CONTRAST * (new RGBColor(35,35,35).toXYZ().y + 0.05) - 0.05), 0).toLUV().l,
|
|
||||||
REQUIRED_DARK = new XYZColor(0, ((new RGBColor(217,217,217).toXYZ().y + 0.05) / REQUIRED_CONTRAST - 0.05), 0).toLUV().l;
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------
|
// --------------------
|
||||||
// Rebuild Colors
|
// Rebuild Colors
|
||||||
// --------------------
|
// --------------------
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
FFZ.prototype._rebuild_colors = function() {
|
FFZ.prototype._rebuild_colors = function() {
|
||||||
if ( ! this._color_style || this.has_bttv )
|
if ( ! this._color_style || this.has_bttv )
|
||||||
return;
|
return;
|
||||||
|
@ -602,14 +633,14 @@ FFZ.prototype._handle_color = function(color) {
|
||||||
if ( this.settings.fix_color === '1' ) {
|
if ( this.settings.fix_color === '1' ) {
|
||||||
var luv = rgb.toLUV();
|
var luv = rgb.toLUV();
|
||||||
|
|
||||||
if ( luv.l > REQUIRED_DARK ) {
|
if ( luv.l > this._luv_required_dark ) {
|
||||||
matched = true;
|
matched = true;
|
||||||
light_color = luv._l(REQUIRED_DARK).toRGB().toCSS();
|
light_color = luv._l(this._luv_required_dark).toRGB().toCSS();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( luv.l < REQUIRED_BRIGHT ) {
|
if ( luv.l < this._luv_required_bright ) {
|
||||||
matched = true;
|
matched = true;
|
||||||
dark_color = luv._l(REQUIRED_BRIGHT).toRGB().toCSS();
|
dark_color = luv._l(this._luv_required_bright).toRGB().toCSS();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,5 +65,9 @@ module.exports = {
|
||||||
HEART: '<svg class="svg-heart" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>',
|
HEART: '<svg class="svg-heart" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>',
|
||||||
EMOTE: '<svg class="svg-emote" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M9,18c-4.971,0-9-4.029-9-9s4.029-9,9-9s9,4.029,9,9S13.971,18,9,18z M14,4.111V4h-0.111C12.627,2.766,10.904,2,9,2C7.095,2,5.373,2.766,4.111,4H4v0.111C2.766,5.373,2,7.096,2,9s0.766,3.627,2,4.889V14l0.05-0.051C5.317,15.217,7.067,16,9,16c1.934,0,3.684-0.783,4.949-2.051L14,14v-0.111c1.234-1.262,2-2.984,2-4.889S15.234,5.373,14,4.111zM11,6h2v4h-2V6z M12.535,12.535C11.631,13.44,10.381,14,9,14s-2.631-0.56-3.536-1.465l0.707-0.707C6.896,12.553,7.896,13,9,13s2.104-0.447,2.828-1.172L12.535,12.535z M5,6h2v4H5V6z" fill-rule="evenodd"></path></svg>',
|
EMOTE: '<svg class="svg-emote" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M9,18c-4.971,0-9-4.029-9-9s4.029-9,9-9s9,4.029,9,9S13.971,18,9,18z M14,4.111V4h-0.111C12.627,2.766,10.904,2,9,2C7.095,2,5.373,2.766,4.111,4H4v0.111C2.766,5.373,2,7.096,2,9s0.766,3.627,2,4.889V14l0.05-0.051C5.317,15.217,7.067,16,9,16c1.934,0,3.684-0.783,4.949-2.051L14,14v-0.111c1.234-1.262,2-2.984,2-4.889S15.234,5.373,14,4.111zM11,6h2v4h-2V6z M12.535,12.535C11.631,13.44,10.381,14,9,14s-2.631-0.56-3.536-1.465l0.707-0.707C6.896,12.553,7.896,13,9,13s2.104-0.447,2.828-1.172L12.535,12.535z M5,6h2v4H5V6z" fill-rule="evenodd"></path></svg>',
|
||||||
STAR: '<svg class="svg-star" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,6l-4.041,2.694L13,14l-5-3.333L3,14l2.041-5.306L1,6h5.077L8,1l1.924,5H15z" fill-rule="evenodd"></path></svg>',
|
STAR: '<svg class="svg-star" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,6l-4.041,2.694L13,14l-5-3.333L3,14l2.041-5.306L1,6h5.077L8,1l1.924,5H15z" fill-rule="evenodd"></path></svg>',
|
||||||
CLOSE: '<svg class="svg-close_small" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M12.657,4.757L9.414,8l3.243,3.242l-1.415,1.415L8,9.414l-3.243,3.243l-1.414-1.415L6.586,8L3.343,4.757l1.414-1.414L8,6.586l3.242-3.243L12.657,4.757z" fill-rule="evenodd"></path></svg>'
|
CLOSE: '<svg class="svg-close_small" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M12.657,4.757L9.414,8l3.243,3.242l-1.415,1.415L8,9.414l-3.243,3.243l-1.414-1.415L6.586,8L3.343,4.757l1.414-1.414L8,6.586l3.242-3.243L12.657,4.757z" fill-rule="evenodd"></path></svg>',
|
||||||
|
|
||||||
|
EDIT: '<svg class="svg-edit" height="16px" version="1.1" viewbox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M6.414,12.414L3.586,9.586l8-8l2.828,2.828L6.414,12.414z M4.829,14H2l0,0v-2.828l0.586-0.586l2.828,2.828L4.829,14z" fill-rule="evenodd"></path></svg>',
|
||||||
|
|
||||||
|
GRAPH: '<svg class="svg-graph" height="16px" version="1.1" viewbox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M1,16V2h16v14H1z M5,4H3v1h2V4z M5,7H3v1h2V7z M5,10H3v1h2V10zM5,13H3v1h2V13z M9,7H7v7h2V7z M12,10h-2v4h2V10z M15,4h-2v10h2V4z" fill-rule="evenodd"></path></svg>'
|
||||||
}
|
}
|
|
@ -84,6 +84,56 @@ FFZ.prototype.setup_channel = function() {
|
||||||
|
|
||||||
}.observes("isLive", "content.id"),
|
}.observes("isLive", "content.id"),
|
||||||
|
|
||||||
|
ffzUpdateInfo: function() {
|
||||||
|
if ( this._ffz_update_timer )
|
||||||
|
clearTimeout(this._ffz_update_timer);
|
||||||
|
|
||||||
|
if ( ! this.get('content.id') )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._ffz_update_timer = setTimeout(this.ffzCheckUpdate.bind(this), 60000);
|
||||||
|
}.observes("content.id"),
|
||||||
|
|
||||||
|
ffzCheckUpdate: function() {
|
||||||
|
var t = this,
|
||||||
|
id = t.get('content.id');
|
||||||
|
|
||||||
|
id && Twitch.api && Twitch.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);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.set('stream.created_at', data.stream.created_at || null);
|
||||||
|
t.set('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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( data.stream.channel ) {
|
||||||
|
if ( data.stream.channel.status )
|
||||||
|
t.set('status', data.stream.channel.status);
|
||||||
|
|
||||||
|
if ( data.stream.channel.views )
|
||||||
|
t.set('views', data.stream.channel.views);
|
||||||
|
|
||||||
|
if ( data.stream.channel.followers && t.get('content.followers.isLoaded') )
|
||||||
|
t.set('content.followers.total', data.stream.channel.followers);
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.always(function(data) {
|
||||||
|
t.ffzUpdateInfo();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
ffzUpdateTitle: function() {
|
ffzUpdateTitle: function() {
|
||||||
var name = this.get('content.name'),
|
var name = this.get('content.name'),
|
||||||
display_name = this.get('content.display_name');
|
display_name = this.get('content.display_name');
|
||||||
|
@ -126,6 +176,8 @@ FFZ.prototype.setup_channel = function() {
|
||||||
|
|
||||||
}.observes("content.hostModeTarget")
|
}.observes("content.hostModeTarget")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Channel.ffzUpdateInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,6 +220,7 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
this.ffzUpdateUptime();
|
this.ffzUpdateUptime();
|
||||||
this.ffzUpdateChatters();
|
this.ffzUpdateChatters();
|
||||||
this.ffzUpdateHostButton();
|
this.ffzUpdateHostButton();
|
||||||
|
this.ffzUpdatePlayerStats();
|
||||||
|
|
||||||
var views = this.get('element').querySelector('.svg-glyph_views:not(.ffz-svg)')
|
var views = this.get('element').querySelector('.svg-glyph_views:not(.ffz-svg)')
|
||||||
if ( views )
|
if ( views )
|
||||||
|
@ -379,6 +432,85 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
ffzUpdatePlayerStats: function() {
|
||||||
|
var channel_id = this.get('controller.id'),
|
||||||
|
hosted_id = this.get('controller.hostModeTarget.id'),
|
||||||
|
|
||||||
|
el = this.get('element');
|
||||||
|
|
||||||
|
if ( channel_id ) {
|
||||||
|
var container = el && el.querySelector('.stats-and-actions .channel-stats'),
|
||||||
|
stat_el = container && container.querySelector('#ffz-ui-player-stats'),
|
||||||
|
el = stat_el && stat_el.querySelector('span'),
|
||||||
|
|
||||||
|
player_cont = f.players && f.players[channel_id],
|
||||||
|
player = player_cont && player_cont.player,
|
||||||
|
stats = player && player.stats;
|
||||||
|
|
||||||
|
|
||||||
|
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
|
||||||
|
if ( stat_el )
|
||||||
|
stat_el.parentElement.removeChild(stat_el);
|
||||||
|
} else {
|
||||||
|
if ( ! stat_el ) {
|
||||||
|
stat_el = document.createElement('span');
|
||||||
|
stat_el.id = 'ffz-ui-player-stats';
|
||||||
|
stat_el.className = 'ffz stat tooltip';
|
||||||
|
|
||||||
|
stat_el.innerHTML = constants.GRAPH + " ";
|
||||||
|
el = document.createElement('span');
|
||||||
|
stat_el.appendChild(el);
|
||||||
|
|
||||||
|
var other = container.querySelector('#ffz-uptime-display');
|
||||||
|
if ( other )
|
||||||
|
container.insertBefore(stat_el, other.nextSibling);
|
||||||
|
else
|
||||||
|
container.appendChild(stat_el);
|
||||||
|
}
|
||||||
|
|
||||||
|
stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
|
||||||
|
el.textContent = stats.hlsLatencyBroadcaster + 's';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ( hosted_id ) {
|
||||||
|
var container = el && el.querySelector('#hostmode .channel-stats'),
|
||||||
|
stat_el = container && container.querySelector('#ffz-ui-player-stats'),
|
||||||
|
el = stat_el && stat_el.querySelector('span'),
|
||||||
|
|
||||||
|
player_cont = f.players && f.players[hosted_id],
|
||||||
|
player = player_cont && player_cont.player,
|
||||||
|
stats = player && player.stats;
|
||||||
|
|
||||||
|
|
||||||
|
if ( ! container || ! f.settings.player_stats || ! stats || stats.hlsLatencyBroadcaster === 'NaN' || stats.hlsLatencyBroadcaster === NaN ) {
|
||||||
|
if ( stat_el )
|
||||||
|
stat_el.parentElement.removeChild(stat_el);
|
||||||
|
} else {
|
||||||
|
if ( ! stat_el ) {
|
||||||
|
stat_el = document.createElement('span');
|
||||||
|
stat_el.id = 'ffz-ui-player-stats';
|
||||||
|
stat_el.className = 'ffz stat tooltip';
|
||||||
|
|
||||||
|
stat_el.innerHTML = constants.GRAPH + " ";
|
||||||
|
el = document.createElement('span');
|
||||||
|
stat_el.appendChild(el);
|
||||||
|
|
||||||
|
var other = container.querySelector('#ffz-uptime-display');
|
||||||
|
if ( other )
|
||||||
|
container.insertBefore(stat_el, other.nextSibling);
|
||||||
|
else
|
||||||
|
container.appendChild(stat_el);
|
||||||
|
}
|
||||||
|
|
||||||
|
stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
|
||||||
|
el.textContent = stats.hlsLatencyBroadcaster + 's';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
ffzUpdateUptime: function() {
|
ffzUpdateUptime: function() {
|
||||||
if ( this._ffz_update_uptime ) {
|
if ( this._ffz_update_uptime ) {
|
||||||
clearTimeout(this._ffz_update_uptime);
|
clearTimeout(this._ffz_update_uptime);
|
||||||
|
@ -397,16 +529,15 @@ FFZ.prototype._modify_cindex = function(view) {
|
||||||
|
|
||||||
// Determine when the channel last went live.
|
// Determine when the channel last went live.
|
||||||
var online = this.get("controller.content.stream.created_at");
|
var online = this.get("controller.content.stream.created_at");
|
||||||
if ( ! online )
|
online = online && utils.parse_date(online);
|
||||||
return;
|
|
||||||
|
|
||||||
online = utils.parse_date(online);
|
var uptime = online && Math.floor((Date.now() - online.getTime()) / 1000) || -1;
|
||||||
if ( ! online )
|
if ( uptime < 0 ) {
|
||||||
return;
|
var el = this.get('element').querySelector('#ffz-uptime-display');
|
||||||
|
if ( el )
|
||||||
var uptime = Math.floor((Date.now() - online.getTime()) / 1000);
|
el.parentElement.removeChild(el);
|
||||||
if ( uptime < 0 )
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var el = this.get('element').querySelector('#ffz-uptime-display span');
|
var el = this.get('element').querySelector('#ffz-uptime-display span');
|
||||||
if ( ! el ) {
|
if ( ! el ) {
|
||||||
|
|
|
@ -2,6 +2,8 @@ var FFZ = window.FrankerFaceZ,
|
||||||
utils = require("../utils"),
|
utils = require("../utils"),
|
||||||
constants = require("../constants"),
|
constants = require("../constants"),
|
||||||
|
|
||||||
|
is_android = navigator.userAgent.indexOf('Android') !== -1,
|
||||||
|
|
||||||
KEYCODES = {
|
KEYCODES = {
|
||||||
BACKSPACE: 8,
|
BACKSPACE: 8,
|
||||||
TAB: 9,
|
TAB: 9,
|
||||||
|
@ -133,13 +135,20 @@ FFZ.prototype._modify_chat_input = function(component) {
|
||||||
ffzInit: function() {
|
ffzInit: function() {
|
||||||
f._inputv = this;
|
f._inputv = this;
|
||||||
|
|
||||||
|
var s = this._ffz_minimal_style = document.createElement('style');
|
||||||
|
s.id = 'ffz-minimal-chat-textarea-height';
|
||||||
|
document.head.appendChild(s);
|
||||||
|
|
||||||
// Redo our key bindings.
|
// Redo our key bindings.
|
||||||
var t = this.$("textarea");
|
var t = this.$("textarea");
|
||||||
|
|
||||||
t.off("keydown");
|
t.off("keydown");
|
||||||
//t.off("keyup");
|
|
||||||
|
|
||||||
t.on("keydown", this._ffzKeyDown.bind(this));
|
t.on("keydown", this._ffzKeyDown.bind(this));
|
||||||
//t.on("keyup", this._ffzKeyUp.bind(this));
|
|
||||||
|
t.attr('rows', 1);
|
||||||
|
|
||||||
|
this.ffzResizeInput();
|
||||||
|
setTimeout(this.ffzResizeInput.bind(this), 500);
|
||||||
|
|
||||||
/*var suggestions = this._parentView.get('context.model.chatSuggestions');
|
/*var suggestions = this._parentView.get('context.model.chatSuggestions');
|
||||||
this.set('ffz_chatters', suggestions);*/
|
this.set('ffz_chatters', suggestions);*/
|
||||||
|
@ -149,16 +158,59 @@ FFZ.prototype._modify_chat_input = function(component) {
|
||||||
if ( f._inputv === this )
|
if ( f._inputv === this )
|
||||||
f._inputv = undefined;
|
f._inputv = undefined;
|
||||||
|
|
||||||
|
this.ffzResizeInput();
|
||||||
|
|
||||||
|
if ( this._ffz_minimal_style ) {
|
||||||
|
this._ffz_minimal_style.parentElement.removeChild(this._ffz_minimal_style);
|
||||||
|
this._ffz_minimal_style = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset normal key bindings.
|
// Reset normal key bindings.
|
||||||
var t = this.$("textarea");
|
var t = this.$("textarea");
|
||||||
|
|
||||||
|
t.attr('rows', undefined);
|
||||||
|
|
||||||
t.off("keydown");
|
t.off("keydown");
|
||||||
t.on("keydown", this._onKeyDown.bind(this));
|
t.on("keydown", this._onKeyDown.bind(this));
|
||||||
//t.on("keyup", this._onKeyUp.bind(this));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Input Control
|
// Input Control
|
||||||
|
|
||||||
|
ffzOnInput: function() {
|
||||||
|
if ( ! f._chat_style || ! f.settings.minimal_chat || is_android )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var now = Date.now(),
|
||||||
|
since = now - (this._ffz_last_resize || 0);
|
||||||
|
|
||||||
|
if ( since > 500 )
|
||||||
|
this.ffzResizeInput();
|
||||||
|
|
||||||
|
}.observes('textareaValue'),
|
||||||
|
|
||||||
|
ffzResizeInput: function() {
|
||||||
|
this._ffz_last_resize = Date.now();
|
||||||
|
|
||||||
|
var el = this.get('element'),
|
||||||
|
t = el && el.querySelector('textarea');
|
||||||
|
|
||||||
|
if ( ! t || ! f._chat_style || ! f.settings.minimal_chat )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Unfortunately, we need to change this with CSS.
|
||||||
|
this._ffz_minimal_style.innerHTML = 'body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea { height: auto !important; }';
|
||||||
|
var height = Math.max(32, Math.min(128, t.scrollHeight));
|
||||||
|
this._ffz_minimal_style.innerHTML = 'body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea { height: ' + height + 'px !important; }';
|
||||||
|
|
||||||
|
if ( height !== this._ffz_last_height ) {
|
||||||
|
utils.update_css(f._chat_style, "input_height", 'body.ffz-minimal-chat .ember-chat .chat-interface { height: ' + height + 'px !important; }' +
|
||||||
|
'body.ffz-minimal-chat .ember-chat .chat-messages, body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector { bottom: ' + height + 'px !important; }');
|
||||||
|
f._roomv && f._roomv.get('stuckToBottom') && f._roomv._scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ffz_last_height = height;
|
||||||
|
},
|
||||||
|
|
||||||
_ffzKeyDown: function(event) {
|
_ffzKeyDown: function(event) {
|
||||||
var e = event || window.event,
|
var e = event || window.event,
|
||||||
key = e.charCode || e.keyCode;
|
key = e.charCode || e.keyCode;
|
||||||
|
|
|
@ -47,6 +47,68 @@ FFZ.settings_info.minimal_chat = {
|
||||||
|
|
||||||
if ( this._chatv && this._chatv.get('controller.showList') )
|
if ( this._chatv && this._chatv.get('controller.showList') )
|
||||||
this._chatv.set('controller.showList', false);
|
this._chatv.set('controller.showList', false);
|
||||||
|
|
||||||
|
// Remove the style if we have it.
|
||||||
|
if ( ! val && this._chat_style ) {
|
||||||
|
if ( this._inputv ) {
|
||||||
|
if ( this._inputv._ffz_minimal_style )
|
||||||
|
this._inputv._ffz_minimal_style.innerHTML = '';
|
||||||
|
|
||||||
|
this._inputv._ffz_last_height = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.update_css(this._chat_style, "input_height", '');
|
||||||
|
this._roomv && this._roomv.get('stuckToBottom') && this._roomv._scrollToBottom();
|
||||||
|
|
||||||
|
} else if ( this._inputv )
|
||||||
|
this._inputv.ffzResizeInput();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.remove_deleted = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
category: "Chat Filtering",
|
||||||
|
name: "Remove Deleted Messages",
|
||||||
|
help: "Remove deleted messages from chat entirely rather than leaving behind a clickable <deleted message>.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( this.has_bttv || ! this.rooms || ! val )
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(var room_id in this.rooms) {
|
||||||
|
var ffz_room = this.rooms[room_id],
|
||||||
|
room = ffz_room && ffz_room.room;
|
||||||
|
if ( ! room )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var msgs = room.get('messages'),
|
||||||
|
total = msgs.get('length'),
|
||||||
|
i = total,
|
||||||
|
alternate;
|
||||||
|
|
||||||
|
while(i--) {
|
||||||
|
var msg = msgs.get(i);
|
||||||
|
|
||||||
|
if ( msg.ffz_deleted || msg.deleted ) {
|
||||||
|
if ( alternate === undefined )
|
||||||
|
alternate = msg.ffz_alternate;
|
||||||
|
msgs.removeAt(i--);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( alternate === undefined )
|
||||||
|
alternate = msg.ffz_alternate;
|
||||||
|
else {
|
||||||
|
alternate = ! alternate;
|
||||||
|
room.set('messages.' + i + '.ffz_alternate', alternate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -268,6 +330,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
ffzInit: function() {
|
ffzInit: function() {
|
||||||
f._chatv = this;
|
f._chatv = this;
|
||||||
this.$('.textarea-contain').append(f.build_ui_link(this));
|
this.$('.textarea-contain').append(f.build_ui_link(this));
|
||||||
|
this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true});
|
||||||
|
|
||||||
if ( !f.has_bttv && f.settings.group_tabs )
|
if ( !f.has_bttv && f.settings.group_tabs )
|
||||||
this.ffzEnableTabs();
|
this.ffzEnableTabs();
|
||||||
|
@ -294,43 +357,44 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzChangeRoom: Ember.observer('controller.currentRoom', function() {
|
ffzChangeRoom: Ember.observer('controller.currentRoom', function() {
|
||||||
try {
|
f.update_ui_link();
|
||||||
f.update_ui_link();
|
|
||||||
|
|
||||||
var room = this.get('controller.currentRoom'), rows;
|
var room = this.get('controller.currentRoom'), rows;
|
||||||
room && room.resetUnreadCount();
|
room && room.resetUnreadCount();
|
||||||
|
|
||||||
if ( this._ffz_chan_table ) {
|
if ( this._ffz_chan_table ) {
|
||||||
rows = jQuery(this._ffz_chan_table);
|
rows = jQuery(this._ffz_chan_table);
|
||||||
rows.children('.ffz-room-row').removeClass('active');
|
rows.children('.ffz-room-row').removeClass('active');
|
||||||
if ( room )
|
if ( room )
|
||||||
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this._ffz_group_table ) {
|
if ( this._ffz_group_table ) {
|
||||||
rows = jQuery(this._ffz_group_table);
|
rows = jQuery(this._ffz_group_table);
|
||||||
rows.children('.ffz-room-row').removeClass('active');
|
rows.children('.ffz-room-row').removeClass('active');
|
||||||
if ( room )
|
if ( room )
|
||||||
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
rows.children('.ffz-room-row[data-room="' + room.get('id') + '"]').addClass('active').children('span').text('');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !f.has_bttv && f.settings.group_tabs && this._ffz_tabs ) {
|
||||||
|
var tabs = jQuery(this._ffz_tabs);
|
||||||
|
tabs.children('.ffz-chat-tab').removeClass('active');
|
||||||
|
if ( room && room._ffz_tab ) {
|
||||||
|
room._ffz_tab.classList.remove('tab-mentioned');
|
||||||
|
room._ffz_tab.classList.remove('hidden');
|
||||||
|
room._ffz_tab.classList.add('active');
|
||||||
|
var sp = room._ffz_tab.querySelector('span');
|
||||||
|
if ( sp )
|
||||||
|
sp.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !f.has_bttv && f.settings.group_tabs && this._ffz_tabs ) {
|
// Invite Link
|
||||||
var tabs = jQuery(this._ffz_tabs);
|
var can_invite = room && room.get('canInvite');
|
||||||
tabs.children('.ffz-chat-tab').removeClass('active');
|
this._ffz_invite && this._ffz_invite.classList.toggle('hidden', !can_invite);
|
||||||
if ( room )
|
this.set('controller.showInviteUser', can_invite && this.get('controller.showInviteUser'))
|
||||||
tabs.children('.ffz-chat-tab[data-room="' + room.get('id') + '"]').removeClass('tab-mentioned').removeClass('hidden').addClass('active').children('span').text('');
|
|
||||||
|
|
||||||
// Invite Link
|
// Now, adjust the chat-room.
|
||||||
var can_invite = room && room.get('canInvite');
|
this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px");
|
||||||
this._ffz_invite && this._ffz_invite.classList.toggle('hidden', !can_invite);
|
|
||||||
this.set('controller.showInviteUser', can_invite && this.get('controller.showInviteUser'))
|
|
||||||
|
|
||||||
// Now, adjust the chat-room.
|
|
||||||
this.$('.chat-room').css('top', this._ffz_tabs.offsetHeight + "px");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
f.error("ChatView ffzUpdateLink: " + err);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -664,27 +728,25 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
|
|
||||||
var tabs = this._ffz_tabs || this.get('element').querySelector('#ffz-group-tabs'),
|
var tabs = this._ffz_tabs || this.get('element').querySelector('#ffz-group-tabs'),
|
||||||
current_id = this.get('controller.currentRoom.id');
|
current_id = this.get('controller.currentRoom.id');
|
||||||
|
|
||||||
if ( ! tabs )
|
if ( ! tabs )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if ( room_id ) {
|
if ( room_id ) {
|
||||||
var tab = tabs.querySelector('.ffz-chat-tab[data-room="' + room_id + '"]'),
|
var room = f.rooms && f.rooms[room_id] && f.rooms[room_id].room,
|
||||||
room = f.rooms && f.rooms[room_id];
|
tab = room && room._ffz_tab;
|
||||||
|
|
||||||
if ( tab && room ) {
|
if ( tab ) {
|
||||||
var unread = utils.format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
|
var unread = utils.format_unread(room_id === current_id ? 0 : room.get('unreadCount'));
|
||||||
tab.querySelector('span').innerHTML = unread;
|
tab.querySelector('span').innerHTML = unread;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, adjust the chat-room.
|
|
||||||
return this.$('.chat-room').css('top', tabs.offsetHeight + "px");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var children = tabs.querySelectorAll('.ffz-chat-tab');
|
var children = tabs.querySelectorAll('.ffz-chat-tab');
|
||||||
for(var i=0; i < children.length; i++) {
|
for(var i=0; i < children.length; i++) {
|
||||||
var tab = children[i],
|
var tab = children[i],
|
||||||
room_id = tab.getAttribute('data-room'),
|
room_id = tab.getAttribute('data-room'),
|
||||||
room = f.rooms && f.rooms[room_id];
|
room = f.rooms && f.rooms[room_id] && f.rooms[room_id];
|
||||||
|
|
||||||
if ( ! room )
|
if ( ! room )
|
||||||
continue;
|
continue;
|
||||||
|
@ -692,9 +754,6 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
var unread = utils.format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
|
var unread = utils.format_unread(room_id === current_id ? 0 : room.room.get('unreadCount'));
|
||||||
tab.querySelector('span').innerHTML = unread;
|
tab.querySelector('span').innerHTML = unread;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, adjust the chat-room.
|
|
||||||
this.$('.chat-room').css('top', tabs.offsetHeight + "px");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
ffzBuildTab: function(view, room, current_channel, host_channel) {
|
ffzBuildTab: function(view, room, current_channel, host_channel) {
|
||||||
|
@ -739,6 +798,7 @@ FFZ.prototype._modify_cview = function(view) {
|
||||||
controller.set('showList', false);
|
controller.set('showList', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
room._ffz_tab = tab;
|
||||||
return tab;
|
return tab;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -3,208 +3,7 @@
|
||||||
constants = require("../constants"),
|
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]",
|
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
|
||||||
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
|
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*");
|
||||||
|
|
||||||
|
|
||||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
|
||||||
SRCSETS = {};
|
|
||||||
build_srcset = function(id) {
|
|
||||||
if ( SRCSETS[id] )
|
|
||||||
return SRCSETS[id];
|
|
||||||
var out = SRCSETS[id] = TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
|
||||||
return out;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
|
|
||||||
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
|
|
||||||
IMGUR_PATH = /^\/(?:gallery\/)?[A-Za-z0-9]+(?:\.(?:png|jpg|jpeg|gif|gifv|bmp))?$/,
|
|
||||||
IMAGE_EXT = /\.(?:png|jpg|jpeg|gif|bmp)$/i,
|
|
||||||
IMAGE_DOMAINS = [],
|
|
||||||
|
|
||||||
is_image = function(href, any_domain) {
|
|
||||||
var match = href.match(LINK_SPLIT);
|
|
||||||
if ( ! match )
|
|
||||||
return;
|
|
||||||
|
|
||||||
var domain = match[3].toLowerCase(), port = match[4],
|
|
||||||
path = match[5];
|
|
||||||
|
|
||||||
// Don't allow non-standard ports.
|
|
||||||
if ( port && port !== '80' && port !== '443' )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// imgur-specific checks.
|
|
||||||
if ( domain === 'i.imgur.com' || domain === 'imgur.com' || domain === 'www.imgur.com' || domain === 'm.imgur.com' )
|
|
||||||
return IMGUR_PATH.test(path);
|
|
||||||
|
|
||||||
return any_domain ? IMAGE_EXT.test(path) : IMAGE_DOMAINS.indexOf(domain) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
image_iframe = function(href, extra_class) {
|
|
||||||
return '<iframe class="ffz-image-hover' + (extra_class ? ' ' + extra_class : '') + '" allowtransparency="true" src="' + constants.SERVER + 'script/image-proxy.html?' + utils.quote_attr(href) + '"></iframe>';
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
data_to_tooltip = function(data) {
|
|
||||||
var set = data.set,
|
|
||||||
set_type = data.set_type,
|
|
||||||
owner = data.owner;
|
|
||||||
|
|
||||||
if ( set_type === undefined )
|
|
||||||
set_type = "Channel";
|
|
||||||
|
|
||||||
if ( ! set )
|
|
||||||
return data.code;
|
|
||||||
|
|
||||||
else if ( set == "--twitch-turbo--" || set == "turbo" ) {
|
|
||||||
set = "Twitch Turbo";
|
|
||||||
set_type = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
|
|
||||||
},
|
|
||||||
|
|
||||||
build_tooltip = function(id) {
|
|
||||||
var emote_data = this._twitch_emotes[id],
|
|
||||||
set = emote_data ? emote_data.set : null;
|
|
||||||
|
|
||||||
if ( ! emote_data )
|
|
||||||
return "???";
|
|
||||||
|
|
||||||
if ( typeof emote_data == "string" )
|
|
||||||
return emote_data;
|
|
||||||
|
|
||||||
if ( emote_data.tooltip )
|
|
||||||
return emote_data.tooltip;
|
|
||||||
|
|
||||||
return emote_data.tooltip = data_to_tooltip(emote_data);
|
|
||||||
},
|
|
||||||
|
|
||||||
load_emote_data = function(id, code, success, data) {
|
|
||||||
if ( ! success )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( code )
|
|
||||||
data.code = code;
|
|
||||||
|
|
||||||
this._twitch_emotes[id] = data;
|
|
||||||
var tooltip = build_tooltip.bind(this)(id);
|
|
||||||
|
|
||||||
var images = document.querySelectorAll('img[emote-id="' + id + '"]');
|
|
||||||
for(var x=0; x < images.length; x++)
|
|
||||||
images[x].title = tooltip;
|
|
||||||
},
|
|
||||||
|
|
||||||
build_link_tooltip = function(href) {
|
|
||||||
var link_data = this._link_data[href],
|
|
||||||
tooltip;
|
|
||||||
|
|
||||||
if ( ! link_data )
|
|
||||||
return "";
|
|
||||||
|
|
||||||
if ( link_data.tooltip )
|
|
||||||
return link_data.tooltip;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 ( data.unsafe )
|
|
||||||
links[x].classList.add('unsafe-link');
|
|
||||||
|
|
||||||
if ( ! links[x].classList.contains('deleted-link') )
|
|
||||||
links[x].title = tooltip;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
@ -321,6 +120,19 @@ FFZ.settings_info.scrollback_length = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.settings_info.hosted_sub_notices = {
|
||||||
|
type: "boolean",
|
||||||
|
value: true,
|
||||||
|
|
||||||
|
category: "Chat Filtering",
|
||||||
|
no_bttv: true,
|
||||||
|
|
||||||
|
name: "Show Hosted Channel Subscriber Notices",
|
||||||
|
help: "Display notices in chat when someone subscribes to the hosted channel."
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
FFZ.settings_info.banned_words = {
|
FFZ.settings_info.banned_words = {
|
||||||
type: "button",
|
type: "button",
|
||||||
value: [],
|
value: [],
|
||||||
|
@ -538,7 +350,7 @@ FFZ.settings_info.chat_font_size = {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var css;
|
var css;
|
||||||
if ( val === 12 )
|
if ( val === 12 || ! val )
|
||||||
css = "";
|
css = "";
|
||||||
else {
|
else {
|
||||||
var lh = Math.max(20, Math.round((20/12)*val)),
|
var lh = Math.max(20, Math.round((20/12)*val)),
|
||||||
|
@ -567,7 +379,7 @@ FFZ.settings_info.chat_ts_size = {
|
||||||
method: function() {
|
method: function() {
|
||||||
var old_val = this.settings.chat_ts_size;
|
var old_val = this.settings.chat_ts_size;
|
||||||
|
|
||||||
if ( old_val === null )
|
if ( ! old_val )
|
||||||
old_val = this.settings.chat_font_size;
|
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);
|
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);
|
||||||
|
@ -609,6 +421,14 @@ FFZ.prototype.setup_line = function() {
|
||||||
this.parentElement.removeChild(this);
|
this.parentElement.removeChild(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Aliases
|
||||||
|
try {
|
||||||
|
this.aliases = JSON.parse(localStorage.ffz_aliases || '{}');
|
||||||
|
} catch(err) {
|
||||||
|
this.log("Error Loading Aliases: " + err);
|
||||||
|
this.aliases = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Chat Style
|
// Chat Style
|
||||||
var s = this._chat_style = document.createElement('style');
|
var s = this._chat_style = document.createElement('style');
|
||||||
|
@ -657,6 +477,13 @@ FFZ.prototype.setup_line = function() {
|
||||||
FFZ.capitalization[user.login] = [user.name, Date.now()];
|
FFZ.capitalization[user.login] = [user.name, Date.now()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype.save_aliases = function() {
|
||||||
|
this.log("Saving " + Object.keys(this.aliases).length + " aliases to local storage.");
|
||||||
|
localStorage.ffz_aliases = JSON.stringify(this.aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype._modify_line = function(component) {
|
FFZ.prototype._modify_line = function(component) {
|
||||||
var f = this;
|
var f = this;
|
||||||
|
|
||||||
|
@ -670,43 +497,36 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
|
|
||||||
tokens = this._super();
|
tokens = this._super();
|
||||||
|
|
||||||
try {
|
var start = performance.now(),
|
||||||
var start = performance.now(),
|
user = f.get_user(),
|
||||||
user = f.get_user(),
|
from_me = user && this.get("msgObject.from") === user.login;
|
||||||
from_me = user && this.get("msgObject.from") === user.login;
|
|
||||||
|
|
||||||
tokens = f._remove_banned(tokens);
|
tokens = f._remove_banned(tokens);
|
||||||
tokens = f._emoticonize(this, tokens);
|
tokens = f._emoticonize(this, tokens);
|
||||||
|
|
||||||
if ( f.settings.parse_emoji )
|
if ( f.settings.parse_emoji )
|
||||||
tokens = f.tokenize_emoji(tokens);
|
tokens = f.tokenize_emoji(tokens);
|
||||||
|
|
||||||
// Store the capitalization.
|
// Store the capitalization.
|
||||||
var display = this.get("msgObject.tags.display-name");
|
var display = this.get("msgObject.tags.display-name");
|
||||||
if ( display && display.length )
|
if ( display && display.length )
|
||||||
FFZ.capitalization[this.get("msgObject.from")] = [display.trim(), Date.now()];
|
FFZ.capitalization[this.get("msgObject.from")] = [display.trim(), Date.now()];
|
||||||
|
|
||||||
if ( ! from_me )
|
if ( ! from_me )
|
||||||
tokens = f.tokenize_mentions(tokens);
|
tokens = f.tokenize_mentions(tokens);
|
||||||
|
|
||||||
for(var i = 0; i < tokens.length; i++) {
|
for(var i = 0; i < tokens.length; i++) {
|
||||||
var token = tokens[i];
|
var token = tokens[i];
|
||||||
if ( ! _.isString(token) && token.mentionedUser && ! token.own ) {
|
if ( ! _.isString(token) && token.mentionedUser && ! token.own ) {
|
||||||
this.set('msgObject.ffz_has_mention', true);
|
this.set('msgObject.ffz_has_mention', true);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var end = performance.now();
|
|
||||||
if ( end - start > 5 )
|
|
||||||
f.log("Tokenizing Message Took Too Long - " + (end-start) + "ms", tokens, false, true);
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
try {
|
|
||||||
f.error("LineController tokenizedMessage: " + err);
|
|
||||||
} catch(err) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var end = performance.now();
|
||||||
|
if ( end - start > 5 )
|
||||||
|
f.log("Tokenizing Message Took Too Long - " + (end-start) + "ms", tokens, false, true);
|
||||||
|
|
||||||
this.set("msgObject.cachedTokens", tokens);
|
this.set("msgObject.cachedTokens", tokens);
|
||||||
return tokens;
|
return tokens;
|
||||||
|
|
||||||
|
@ -715,17 +535,14 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
ffzUpdated: Ember.observer("msgObject.ffz_deleted", "msgObject.ffz_old_messages", function() {
|
ffzUpdated: Ember.observer("msgObject.ffz_deleted", "msgObject.ffz_old_messages", function() {
|
||||||
this.rerender();
|
this.rerender();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
willClearRender: function() {
|
|
||||||
try {
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
f.error("LineView willClearRender: " + err);
|
|
||||||
}
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
|
|
||||||
click: function(e) {
|
click: function(e) {
|
||||||
|
if ( e.target && e.target.classList.contains('ffz-old-messages') )
|
||||||
|
return f._show_deleted(this.get('msgObject.room'));
|
||||||
|
|
||||||
|
if ( e.target && e.target.classList.contains('deleted-link') )
|
||||||
|
return f._deleted_link_click.bind(e.target)(e);
|
||||||
|
|
||||||
if ( e.target && e.target.classList.contains('mod-icon') ) {
|
if ( e.target && e.target.classList.contains('mod-icon') ) {
|
||||||
jQuery(e.target).trigger('mouseout');
|
jQuery(e.target).trigger('mouseout');
|
||||||
|
|
||||||
|
@ -744,190 +561,160 @@ FFZ.prototype._modify_line = function(component) {
|
||||||
|
|
||||||
return this._super(e);
|
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('isGlobalModerator') )
|
||||||
|
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,
|
||||||
|
|
||||||
|
badges = {},
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
row_type = this.get('msgObject.ffz_alternate');
|
||||||
|
|
||||||
|
if ( row_type === undefined ) {
|
||||||
|
row_type = f._last_row[room_id] = f._last_row.hasOwnProperty(room_id) ? !f._last_row[room_id] : false;
|
||||||
|
this.set("msgObject.ffz_alternate", row_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.push('<div class="indicator"></div>');
|
||||||
|
e.push('<span class="timestamp float-left">' + this.get("timestamp") + '</span> ');
|
||||||
|
|
||||||
|
if ( ! is_whisper && this_ul < other_ul ) {
|
||||||
|
e.push('<span class="mod-icons float-left">');
|
||||||
|
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>');
|
||||||
|
|
||||||
|
e.push('<a class="mod-icon float-left tooltip timeout" title="Timeout User (10m)" href="#">Timeout</a>');
|
||||||
|
e.push('<a class="mod-icon float-left tooltip purge" title="Purge User (Timeout 1s)" href="#">Purge</a>');
|
||||||
|
e.push('</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stock Badges
|
||||||
|
if ( ! is_whisper && this.get('isBroadcaster') )
|
||||||
|
badges[0] = {klass: 'broadcaster', title: 'Broadcaster'};
|
||||||
|
else if ( this.get('isStaff') )
|
||||||
|
badges[0] = {klass: 'staff', title: 'Staff'};
|
||||||
|
else if ( this.get('isAdmin') )
|
||||||
|
badges[0] = {klass: 'admin', title: 'Admin'};
|
||||||
|
else if ( this.get('isGlobalMod') )
|
||||||
|
badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
|
||||||
|
else if ( ! is_whisper && this.get('isModerator') )
|
||||||
|
badges[0] = {klass: 'moderator', title: 'Moderator'};
|
||||||
|
|
||||||
|
if ( ! is_whisper && this.get('isSubscriber') )
|
||||||
|
badges[10] = {klass: 'subscriber', title: 'Subscriber'};
|
||||||
|
if ( this.get('hasTurbo') )
|
||||||
|
badges[15] = {klass: 'turbo', title: 'Turbo'};
|
||||||
|
|
||||||
|
// FFZ Badges
|
||||||
|
badges = f.render_badges(this, badges);
|
||||||
|
|
||||||
|
// Rendering!
|
||||||
|
e.push('<span class="badges float-left">');
|
||||||
|
|
||||||
|
for(var key in badges) {
|
||||||
|
var badge = badges[key],
|
||||||
|
css = badge.image ? 'background-image:url("' + badge.image + '");' : '';
|
||||||
|
|
||||||
|
if ( badge.color )
|
||||||
|
css += 'background-color:' + badge.color + ';';
|
||||||
|
|
||||||
|
if ( badge.extra_css )
|
||||||
|
css += badge.extra_css;
|
||||||
|
|
||||||
|
e.push('<div class="badge float-left tooltip ' + badge.klass + '"' + (css ? ' style="' + css + '"' : '') + ' title="' + badge.title + '"></div>');
|
||||||
|
}
|
||||||
|
|
||||||
|
e.push('</span>');
|
||||||
|
|
||||||
|
var alias = f.aliases[user],
|
||||||
|
name = this.get('msgObject.tags.display-name') || (user && user.capitalize()) || "unknown user";
|
||||||
|
|
||||||
|
if ( alias )
|
||||||
|
e.push('<span class="from ffz-alias tooltip" style="' + this.get('fromStyle') + '" title="' + utils.sanitize(name) + '">' + utils.sanitize(alias) + '</span>');
|
||||||
|
else
|
||||||
|
e.push('<span class="from" style="' + this.get('fromStyle') + '">' + utils.sanitize(name) + '</span>');
|
||||||
|
|
||||||
|
if ( is_whisper ) {
|
||||||
|
var to_alias = f.aliases[recipient],
|
||||||
|
to_name = this.get('msgObject.tags.recipient-display-name') || (recipient && recipient.capitalize()) || "unknown user";
|
||||||
|
|
||||||
|
this._renderWhisperArrow(e);
|
||||||
|
|
||||||
|
if ( to_alias )
|
||||||
|
e.push('<span class="to ffz-alias tooltip" style="' + this.get('toStyle') + '" title="' + utils.sanitize(to_name) + '">' + utils.sanitize(to_alias) + '</span>');
|
||||||
|
else
|
||||||
|
e.push('<span class="to" style="' + this.get('toStyle') + '">' + utils.sanitize(to_name) + '</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
e.push('<span class="colon">:</span> ');
|
||||||
|
|
||||||
|
if ( deleted )
|
||||||
|
e.push('<span class="deleted"><a class="undelete" href="#"><message deleted></a></span>');
|
||||||
|
else {
|
||||||
|
e.push('<span class="message" style="' + this.get('messageStyle') + '">');
|
||||||
|
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>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
classNameBindings: [
|
||||||
|
'msgObject.ffz_alternate:ffz-alternate',
|
||||||
|
'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'),
|
||||||
|
|
||||||
|
ffzHasOldMessages: function() {
|
||||||
|
var old_messages = this.get('msgObject.ffz_old_messages');
|
||||||
|
return old_messages && old_messages.length;
|
||||||
|
}.property('msgObject.ffz_old_messages'),
|
||||||
|
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this._super();
|
this._super();
|
||||||
try {
|
|
||||||
var start = performance.now();
|
var el = this.get('element');
|
||||||
|
|
||||||
var el = this.get('element'),
|
el.setAttribute('data-room', this.get('msgObject.room'));
|
||||||
user = this.get('msgObject.from'),
|
el.setAttribute('data-sender', this.get('msgObject.from'));
|
||||||
room = this.get('msgObject.room') || App.__container__.lookup('controller:chat').get('currentRoom.id'),
|
el.setAttribute('data-deleted', this.get('msgObject.deleted') || false);
|
||||||
row_type = this.get('msgObject.ffz_alternate');
|
|
||||||
|
|
||||||
// Row Alternation
|
|
||||||
if ( row_type === undefined ) {
|
|
||||||
row_type = f._last_row[room] = f._last_row.hasOwnProperty(room) ? !f._last_row[room] : false;
|
|
||||||
this.set("msgObject.ffz_alternate", row_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
el.classList.toggle('ffz-alternate', row_type || false);
|
|
||||||
el.classList.toggle('ffz-deleted', f.settings.prevent_clear && this.get('msgObject.ffz_deleted') || false);
|
|
||||||
|
|
||||||
|
|
||||||
// Basic Data
|
|
||||||
el.setAttribute('data-room', room);
|
|
||||||
el.setAttribute('data-sender', user);
|
|
||||||
el.setAttribute('data-deleted', this.get('msgObject.deleted')||false);
|
|
||||||
|
|
||||||
|
|
||||||
// Old Messages (for Chat Clear)
|
|
||||||
var old_messages = this.get("msgObject.ffz_old_messages");
|
|
||||||
if ( old_messages && old_messages.length ) {
|
|
||||||
var btn = document.createElement('div');
|
|
||||||
btn.className = 'button primary float-right';
|
|
||||||
btn.innerHTML = 'Show ' + utils.number_commas(old_messages.length) + ' Old';
|
|
||||||
|
|
||||||
btn.addEventListener("click", f._show_deleted.bind(f, room));
|
|
||||||
|
|
||||||
el.classList.add('clearfix');
|
|
||||||
el.classList.add('ffz-has-deleted');
|
|
||||||
|
|
||||||
this.$('.message').append(btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Hide Mod Buttons
|
|
||||||
if ( (this.get('isBroadcaster') && !(this.get('controller.parentController.model.isStaff') || this.get('controller.parentController.model.isAdmin'))) || (this.get('isModeratorOrHigher') && !(this.get('controller.parentController.model.isBroadcaster') || this.get('controller.parentController.model.isStaff') || this.get('controller.parentController.model.isAdmin'))) ) {
|
|
||||||
var mod_icons = el.querySelector('span.mod-icons');
|
|
||||||
mod_icons && mod_icons.classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Purge Button
|
|
||||||
var timeout_btn = el.querySelector('span.mod-icons a.mod-icon.timeout');
|
|
||||||
if ( timeout_btn ) {
|
|
||||||
var purge_btn = document.createElement('a');
|
|
||||||
purge_btn.className = 'mod-icon float-left tooltip purge';
|
|
||||||
purge_btn.innerHTML = 'Purge';
|
|
||||||
purge_btn.title = 'Purge User (Timeout 1s)';
|
|
||||||
purge_btn.href = '#';
|
|
||||||
|
|
||||||
timeout_btn.title = 'Timeout User (10m)';
|
|
||||||
timeout_btn.parentElement.insertBefore(purge_btn, timeout_btn.nextSibling);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Badge
|
|
||||||
f.render_badge(this);
|
|
||||||
|
|
||||||
|
|
||||||
// Mention Highlighting
|
|
||||||
if ( this.get("msgObject.ffz_has_mention") )
|
|
||||||
el.classList.add("ffz-mentioned");
|
|
||||||
|
|
||||||
|
|
||||||
// Banned Links
|
|
||||||
var bad_links = el.querySelectorAll('span.message a.deleted-link');
|
|
||||||
for(var i=0; i < bad_links.length; i++)
|
|
||||||
bad_links[i].addEventListener("click", f._deleted_link_click);
|
|
||||||
|
|
||||||
|
|
||||||
// Link Tooltips
|
|
||||||
if ( f.settings.link_info || f.settings.link_image_hover ) {
|
|
||||||
var links = el.querySelectorAll("span.message a");
|
|
||||||
for(var i=0; i < links.length; i++) {
|
|
||||||
var link = links[i],
|
|
||||||
href = link.href,
|
|
||||||
deleted = false;
|
|
||||||
|
|
||||||
if ( link.classList.contains("deleted-link") ) {
|
|
||||||
href = link.getAttribute("data-url");
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check the cache.
|
|
||||||
if ( f.settings.link_info ) {
|
|
||||||
var link_data = f._link_data[href];
|
|
||||||
if ( link_data ) {
|
|
||||||
if ( !deleted && typeof link_data != "boolean" )
|
|
||||||
link.title = link_data.tooltip;
|
|
||||||
|
|
||||||
if ( link_data.unsafe )
|
|
||||||
link.classList.add('unsafe-link');
|
|
||||||
|
|
||||||
} else if ( ! /^mailto:/.test(href) ) {
|
|
||||||
f._link_data[href] = true;
|
|
||||||
f.ws_send("get_link", href, load_link_data.bind(f, href));
|
|
||||||
if ( ! deleted && f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
|
|
||||||
link.title = image_iframe(href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, Images
|
|
||||||
else if ( ! deleted && f.settings.link_image_hover && is_image(href, f.settings.image_hover_all_domains) )
|
|
||||||
link.title = image_iframe(href);
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(links).tipsy({html:true});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Enhanced Emotes
|
|
||||||
var images = el.querySelectorAll('span.message img.emoticon');
|
|
||||||
for(var i=0; i < images.length; i++) {
|
|
||||||
var img = images[i],
|
|
||||||
name = img.alt,
|
|
||||||
id = FFZ.src_to_id(img.src);
|
|
||||||
|
|
||||||
if ( id !== null ) {
|
|
||||||
// High-DPI Images
|
|
||||||
if ( ! constants.EMOTE_REPLACEMENTS[id] )
|
|
||||||
img.setAttribute('srcset', build_srcset(id));
|
|
||||||
|
|
||||||
img.setAttribute('emote-id', id);
|
|
||||||
|
|
||||||
// Source Lookup
|
|
||||||
var emote_data = f._twitch_emotes[id];
|
|
||||||
if ( emote_data ) {
|
|
||||||
if ( typeof emote_data != "string" )
|
|
||||||
img.title = emote_data.tooltip;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
f._twitch_emotes[id] = img.alt;
|
|
||||||
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, img.alt));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ( img.getAttribute('data-ffz-emoji') ) {
|
|
||||||
var eid = img.getAttribute('data-ffz-emoji'),
|
|
||||||
data = f.emoji_data && f.emoji_data[eid];
|
|
||||||
|
|
||||||
if ( data ) {
|
|
||||||
img.setAttribute('srcset', data.srcSet);
|
|
||||||
img.title = "Emoji: " + img.alt + "\nName: :" + data.short_name + ":";
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ( img.getAttribute('data-ffz-emote') ) {
|
|
||||||
var data = JSON.parse(decodeURIComponent(img.getAttribute('data-ffz-emote'))),
|
|
||||||
id = data && data[0] || null,
|
|
||||||
set_id = data && data[1] || null,
|
|
||||||
|
|
||||||
set = f.emote_sets[set_id],
|
|
||||||
emote = set ? set.emoticons[id] : null;
|
|
||||||
|
|
||||||
// High-DPI!
|
|
||||||
if ( emote && emote.srcSet )
|
|
||||||
img.setAttribute('srcset', emote.srcSet);
|
|
||||||
|
|
||||||
if ( set && f.feature_friday && set.id == f.feature_friday.set )
|
|
||||||
set_name = f.feature_friday.title + " - " + f.feature_friday.display_name;
|
|
||||||
|
|
||||||
img.title = f._emote_tooltip(emote);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jQuery(images).tipsy();
|
|
||||||
|
|
||||||
|
|
||||||
var duration = performance.now() - start;
|
|
||||||
if ( duration > 5 )
|
|
||||||
f.log("Line Took Too Long - " + duration + "ms", el.innerHTML, false, true);
|
|
||||||
|
|
||||||
} catch(err) {
|
|
||||||
try {
|
|
||||||
f.error("LineView didInsertElement: " + err);
|
|
||||||
} catch(err) { }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -990,9 +777,16 @@ FFZ.prototype._remove_banned = function(tokens) {
|
||||||
new_tokens.push(token.altText.replace(regex, "$1***"));
|
new_tokens.push(token.altText.replace(regex, "$1***"));
|
||||||
else if ( token.isLink && regex.test(token.href) )
|
else if ( token.isLink && regex.test(token.href) )
|
||||||
new_tokens.push({
|
new_tokens.push({
|
||||||
|
isLink: true,
|
||||||
|
href: token.href,
|
||||||
|
isDeleted: true,
|
||||||
|
isLong: false,
|
||||||
|
censoredHref: token.href.replace(regex, "$1***")
|
||||||
|
});
|
||||||
|
/*{
|
||||||
mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(token.href.replace(regex, "$1***")) + '" data-url="' + utils.quote_attr(token.href) + '" href="#"><banned link></a><span class="mentioning">',
|
mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(token.href.replace(regex, "$1***")) + '" data-url="' + utils.quote_attr(token.href) + '" href="#"><banned link></a><span class="mentioning">',
|
||||||
own: true
|
own: true
|
||||||
});
|
});*/
|
||||||
else
|
else
|
||||||
new_tokens.push(token);
|
new_tokens.push(token);
|
||||||
|
|
||||||
|
|
|
@ -296,6 +296,13 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
info.innerHTML = out;
|
info.innerHTML = out;
|
||||||
}.observes("cardInfo.user.views"),
|
}.observes("cardInfo.user.views"),
|
||||||
|
|
||||||
|
userName: Ember.computed("cardInfo.user.id", "cardInfo.user.display_name", function() {
|
||||||
|
var user_id = this.get("cardInfo.user.id"),
|
||||||
|
alias = f.aliases[user_id];
|
||||||
|
|
||||||
|
return alias || this.get("cardInfo.user.display_name") || user_id.capitalize();
|
||||||
|
}),
|
||||||
|
|
||||||
didInsertElement: function() {
|
didInsertElement: function() {
|
||||||
this._super();
|
this._super();
|
||||||
window._card = this;
|
window._card = this;
|
||||||
|
@ -305,7 +312,24 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
|
|
||||||
var el = this.get('element'),
|
var el = this.get('element'),
|
||||||
controller = this.get('controller'),
|
controller = this.get('controller'),
|
||||||
line;
|
line,
|
||||||
|
|
||||||
|
user_id = controller.get('cardInfo.user.id'),
|
||||||
|
alias = f.aliases[user_id];
|
||||||
|
|
||||||
|
// Alias Display
|
||||||
|
if ( alias ) {
|
||||||
|
var name = el.querySelector('h3.name'),
|
||||||
|
link = name && name.querySelector('a');
|
||||||
|
|
||||||
|
if ( link )
|
||||||
|
name = link;
|
||||||
|
if ( name ) {
|
||||||
|
name.classList.add('ffz-alias');
|
||||||
|
name.title = utils.sanitize(controller.get('cardInfo.user.display_name') || user_id.capitalize());
|
||||||
|
jQuery(name).tipsy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Style it!
|
// Style it!
|
||||||
el.classList.add('ffz-moderation-card');
|
el.classList.add('ffz-moderation-card');
|
||||||
|
@ -523,10 +547,9 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
|
|
||||||
|
|
||||||
var real_msg = document.createElement('button');
|
var real_msg = document.createElement('button');
|
||||||
real_msg.className = 'message-button button glyph-only message';
|
real_msg.className = 'message-button button glyph-only message tooltip';
|
||||||
real_msg.innerHTML = MESSAGE;
|
real_msg.innerHTML = MESSAGE;
|
||||||
real_msg.title = "Message User";
|
real_msg.title = "Message User";
|
||||||
jQuery(real_msg).tipsy();
|
|
||||||
|
|
||||||
real_msg.addEventListener('click', function() {
|
real_msg.addEventListener('click', function() {
|
||||||
window.open('http://www.twitch.tv/message/compose?to=' + controller.get('cardInfo.user.id'));
|
window.open('http://www.twitch.tv/message/compose?to=' + controller.get('cardInfo.user.id'));
|
||||||
|
@ -534,6 +557,47 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
|
|
||||||
msg_btn.parentElement.insertBefore(real_msg, msg_btn.nextSibling);
|
msg_btn.parentElement.insertBefore(real_msg, msg_btn.nextSibling);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Alias Button
|
||||||
|
var alias_btn = document.createElement('button');
|
||||||
|
alias_btn.className = 'alias button glyph-only tooltip';
|
||||||
|
alias_btn.innerHTML = constants.EDIT;
|
||||||
|
alias_btn.title = "Set Alias";
|
||||||
|
|
||||||
|
alias_btn.addEventListener('click', 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);
|
||||||
|
if ( new_val === null || new_val === undefined )
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_val = new_val.trim();
|
||||||
|
if ( ! new_val )
|
||||||
|
new_val = undefined;
|
||||||
|
|
||||||
|
f.aliases[user] = new_val;
|
||||||
|
f.save_aliases();
|
||||||
|
|
||||||
|
// Update UI
|
||||||
|
Ember.propertyDidChange(controller, 'userName');
|
||||||
|
var name = el.querySelector('h3.name'),
|
||||||
|
link = name && name.querySelector('a');
|
||||||
|
|
||||||
|
if ( link )
|
||||||
|
name = link;
|
||||||
|
if ( name )
|
||||||
|
name.classList.toggle('ffz-alias', new_val);
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( msg_btn )
|
||||||
|
msg_btn.parentElement.insertBefore(alias_btn, msg_btn);
|
||||||
|
else {
|
||||||
|
var follow_btn = el.querySelector(".interface > .follow-button");
|
||||||
|
if ( follow_btn )
|
||||||
|
follow_btn.parentElement.insertBefore(alias_btn, follow_btn.nextSibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Message History
|
// Message History
|
||||||
|
@ -552,7 +616,7 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
var line = user_history[i],
|
var line = user_history[i],
|
||||||
l_el = document.createElement('li');
|
l_el = document.createElement('li');
|
||||||
|
|
||||||
l_el.className = 'message-line chat-line';
|
l_el.className = 'message-line chat-line clearfix';
|
||||||
l_el.classList.toggle('ffz-alternate', alternate);
|
l_el.classList.toggle('ffz-alternate', alternate);
|
||||||
alternate = !alternate;
|
alternate = !alternate;
|
||||||
|
|
||||||
|
@ -566,7 +630,7 @@ FFZ.prototype.setup_mod_card = function() {
|
||||||
for(var x=0; x < bad_links.length; x++)
|
for(var x=0; x < bad_links.length; x++)
|
||||||
bad_links[x].addEventListener("click", f._deleted_link_click);
|
bad_links[x].addEventListener("click", f._deleted_link_click);
|
||||||
|
|
||||||
jQuery('a', l_el).tipsy({html:true});
|
jQuery('.html-tooltip', l_el).tipsy({html:true});
|
||||||
history.appendChild(l_el);
|
history.appendChild(l_el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
147
src/ember/player.js
Normal file
147
src/ember/player.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
var FFZ = window.FrankerFaceZ;
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Settings
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.settings_info.player_stats = {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
no_mobile: true,
|
||||||
|
|
||||||
|
category: "Channel Metadata",
|
||||||
|
|
||||||
|
name: "Stream Latency",
|
||||||
|
help: "<i>New HTML5 Player Only.</i> Display your current stream latency (how far behind the broadcast you are) under the player, with a few useful statistics in a tooltip.",
|
||||||
|
|
||||||
|
on_update: function(val) {
|
||||||
|
if ( ! this._cindex )
|
||||||
|
return;
|
||||||
|
|
||||||
|
this._cindex.ffzUpdatePlayerStats();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Initialization
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.prototype.setup_player = function() {
|
||||||
|
this.players = {};
|
||||||
|
|
||||||
|
var Player2 = App && App.__container__.resolve('component:twitch-player2');
|
||||||
|
if ( ! Player2 )
|
||||||
|
return this.log("Unable to find twitch-player2 component.");
|
||||||
|
|
||||||
|
this.log("Hooking HTML5 Player UI.");
|
||||||
|
this._modify_player(Player2)
|
||||||
|
|
||||||
|
// Modify all existing players.
|
||||||
|
for(var key in Ember.View.views) {
|
||||||
|
if ( ! Ember.View.views.hasOwnProperty(key) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var view = Ember.View.views[key];
|
||||||
|
if ( !(view instanceof Player2) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this.log("Manually updating existing Player instance.", view);
|
||||||
|
try {
|
||||||
|
view.ffzInit();
|
||||||
|
if ( view.get('player') )
|
||||||
|
view.ffzPostPlayer();
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
this.error("Player2 setup ffzInit: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
// Component
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
FFZ.prototype._modify_player = function(player) {
|
||||||
|
var f = this;
|
||||||
|
player.reopen({
|
||||||
|
didInsertElement: function() {
|
||||||
|
this._super();
|
||||||
|
try {
|
||||||
|
this.ffzInit();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("Player2 didInsertElement: " + err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willClearRender: function() {
|
||||||
|
try {
|
||||||
|
this.ffzTeardown();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("Player2 willClearRender: " + err);
|
||||||
|
}
|
||||||
|
this._super();
|
||||||
|
},
|
||||||
|
|
||||||
|
postPlayerSetup: function() {
|
||||||
|
this._super();
|
||||||
|
try {
|
||||||
|
this.ffzPostPlayer();
|
||||||
|
} catch(err) {
|
||||||
|
f.error("Player2 postPlayerSetup: " + err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzInit: function() {
|
||||||
|
var id = this.get('channel.id');
|
||||||
|
f.players[id] = this;
|
||||||
|
|
||||||
|
this._ffz_stat_update = this.ffzStatUpdate.bind(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzTeardown: function() {
|
||||||
|
var id = this.get('channel.id');
|
||||||
|
if ( f.players[id] === this )
|
||||||
|
f.players[id] = undefined;
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzStatUpdate: function() {
|
||||||
|
f._cindex && f._cindex.ffzUpdatePlayerStats();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzPostPlayer: function() {
|
||||||
|
var player = this.get('player');
|
||||||
|
if ( ! player )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Make it so stats can no longer be disabled.
|
||||||
|
player.ffzSetStatsEnabled = player.setStatsEnabled;
|
||||||
|
player.setStatsEnabled = function() {}
|
||||||
|
|
||||||
|
// We can't just request stats straight away...
|
||||||
|
this.ffzWaitForStats();
|
||||||
|
},
|
||||||
|
|
||||||
|
ffzWaitForStats: function() {
|
||||||
|
var player = this.get('player');
|
||||||
|
if ( ! player )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( player.stats ) {
|
||||||
|
// Add the event listener.
|
||||||
|
player.addEventListener('statschange', this._ffz_stat_update);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Keep going until we've got it.
|
||||||
|
player.ffzSetStatsEnabled(false);
|
||||||
|
var t = this;
|
||||||
|
setTimeout(function() {
|
||||||
|
player.ffzSetStatsEnabled(true);
|
||||||
|
setTimeout(t.ffzWaitForStats.bind(t), 1250);
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,13 +2,13 @@ var FFZ = window.FrankerFaceZ,
|
||||||
CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg,
|
CSS = /\.([\w\-_]+)\s*?\{content:\s*?"([^"]+)";\s*?background-image:\s*?url\("([^"]+)"\);\s*?height:\s*?(\d+)px;\s*?width:\s*?(\d+)px;\s*?margin:([^;}]+);?([^}]*)\}/mg,
|
||||||
MOD_CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/,
|
MOD_CSS = /[^\n}]*\.badges\s+\.moderator\s*{\s*background-image:\s*url\(\s*['"]([^'"]+)['"][^}]+(?:}|$)/,
|
||||||
GROUP_CHAT = /^_([^_]+)_\d+$/,
|
GROUP_CHAT = /^_([^_]+)_\d+$/,
|
||||||
|
HOSTED_SUB = / subscribed to /,
|
||||||
constants = require('../constants'),
|
constants = require('../constants'),
|
||||||
utils = require('../utils'),
|
utils = require('../utils'),
|
||||||
|
|
||||||
// StrimBagZ Support
|
// StrimBagZ Support
|
||||||
is_android = navigator.userAgent.indexOf('Android') !== -1,
|
is_android = navigator.userAgent.indexOf('Android') !== -1,
|
||||||
|
|
||||||
|
|
||||||
moderator_css = function(room) {
|
moderator_css = function(room) {
|
||||||
if ( ! room.moderator_badge )
|
if ( ! room.moderator_badge )
|
||||||
return "";
|
return "";
|
||||||
|
@ -244,10 +244,10 @@ FFZ.prototype._modify_rview = function(view) {
|
||||||
jQuery(banned_badge).tipsy({gravity:"s", offset:15});
|
jQuery(banned_badge).tipsy({gravity:"s", offset:15});
|
||||||
}
|
}
|
||||||
|
|
||||||
r9k_badge.classList.toggle('hidden', !(room && room.get('r9kMode')));
|
r9k_badge.classList.toggle('hidden', !(room && room.get('r9k')));
|
||||||
sub_badge.classList.toggle('hidden', !(room && room.get('subsOnlyMode')));
|
sub_badge.classList.toggle('hidden', !(room && room.get('subsOnly')));
|
||||||
slow_badge.classList.toggle('hidden', !(room && room.get('slowMode')));
|
slow_badge.classList.toggle('hidden', !(room && room.get('slowMode')));
|
||||||
slow_badge.title = "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slowValue')||120) + " seconds.";
|
slow_badge.title = "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow')||120) + " seconds.";
|
||||||
banned_badge.classList.toggle('hidden', !(room && room.get('ffz_banned')));
|
banned_badge.classList.toggle('hidden', !(room && room.get('ffz_banned')));
|
||||||
|
|
||||||
if ( btn ) {
|
if ( btn ) {
|
||||||
|
@ -320,7 +320,9 @@ FFZ.prototype._modify_rview = function(view) {
|
||||||
|
|
||||||
ffzMouseDown: function(event) {
|
ffzMouseDown: function(event) {
|
||||||
var t = this._$chatMessagesScroller;
|
var t = this._$chatMessagesScroller;
|
||||||
if ( t && t[0] && (event.which > 0 || (!this.ffz_frozne && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) {
|
if ( t && t[0] && ((!this.ffz_frozen && "mousedown" === event.type) || "mousewheel" === event.type || (is_android && "scroll" === event.type) ) ) {
|
||||||
|
if ( event.type === "mousedown" )
|
||||||
|
f.log("Freezing from mouse down!", event);
|
||||||
var r = t[0].scrollHeight - t[0].scrollTop - t[0].offsetHeight;
|
var r = t[0].scrollHeight - t[0].scrollTop - t[0].offsetHeight;
|
||||||
this._setStuckToBottom(10 >= r);
|
this._setStuckToBottom(10 >= r);
|
||||||
}
|
}
|
||||||
|
@ -360,8 +362,12 @@ FFZ.prototype._modify_rview = function(view) {
|
||||||
s = this._$chatMessagesScroller;
|
s = this._$chatMessagesScroller;
|
||||||
|
|
||||||
Ember.run.next(function() {
|
Ember.run.next(function() {
|
||||||
setTimeout(function() {
|
setTimeout(function(){
|
||||||
!e.ffz_frozen && s && s.length && (s.scrollTop(s[0].scrollHeight), e._setStuckToBottom(!0));
|
if ( e.ffz_frozen || ! s || ! s.length )
|
||||||
|
return;
|
||||||
|
|
||||||
|
s[0].scrollTop = s[0].scrollHeight;
|
||||||
|
e._setStuckToBottom(true);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, 200),
|
}, 200),
|
||||||
|
@ -711,7 +717,7 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
if ( ! msg.cachedTokens || ! msg.cachedTokens.length )
|
||||||
this.tokenize_chat_line(msg, true);
|
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||||
|
|
||||||
if ( r.shouldShowMessage(msg) ) {
|
if ( r.shouldShowMessage(msg) ) {
|
||||||
if ( messages.length < r.get("messageBufferSize") ) {
|
if ( messages.length < r.get("messageBufferSize") ) {
|
||||||
|
@ -740,7 +746,7 @@ FFZ.prototype._insert_history = function(room_id, data) {
|
||||||
room: room_id
|
room: room_id
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tokenize_chat_line(msg);
|
this.tokenize_chat_line(msg, true, r.get('roomProperties.hide_chat_links'));
|
||||||
if ( r.shouldShowMessage(msg) ) {
|
if ( r.shouldShowMessage(msg) ) {
|
||||||
messages.insertAt(inserted, msg);
|
messages.insertAt(inserted, msg);
|
||||||
while( messages.length > r.get('messageBufferSize') )
|
while( messages.length > r.get('messageBufferSize') )
|
||||||
|
@ -823,10 +829,8 @@ FFZ.prototype._load_room_json = function(room_id, callback, data) {
|
||||||
FFZ.prototype._modify_room = function(room) {
|
FFZ.prototype._modify_room = function(room) {
|
||||||
var f = this;
|
var f = this;
|
||||||
room.reopen({
|
room.reopen({
|
||||||
subsOnlyMode: false,
|
|
||||||
r9kMode: false,
|
|
||||||
slowWaiting: false,
|
slowWaiting: false,
|
||||||
slowValue: 0,
|
slow: 0,
|
||||||
|
|
||||||
mru_list: [],
|
mru_list: [],
|
||||||
|
|
||||||
|
@ -862,7 +866,22 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
ffzUpdateStatus: function() {
|
ffzUpdateStatus: function() {
|
||||||
if ( f._roomv )
|
if ( f._roomv )
|
||||||
f._roomv.ffzUpdateStatus();
|
f._roomv.ffzUpdateStatus();
|
||||||
}.observes('r9kMode', 'subsOnlyMode', 'slowMode', 'slowValue', 'ffz_banned'),
|
}.observes('r9k', 'subsOnly', 'slow', 'ffz_banned'),
|
||||||
|
|
||||||
|
// User Level
|
||||||
|
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('isGlobalModerator') )
|
||||||
|
return 2;
|
||||||
|
else if ( this.get('isModerator') )
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}.property('id', 'chatLabels.[]'),
|
||||||
|
|
||||||
// Track which rooms the user is currently in.
|
// Track which rooms the user is currently in.
|
||||||
init: function() {
|
init: function() {
|
||||||
|
@ -887,13 +906,34 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
clearMessages: function(user) {
|
clearMessages: function(user) {
|
||||||
var t = this;
|
var t = this;
|
||||||
if ( user ) {
|
if ( user ) {
|
||||||
this.get("messages").forEach(function(s, n) {
|
var msgs = t.get('messages'),
|
||||||
if ( s.from === user ) {
|
total = msgs.get('length'),
|
||||||
t.set("messages." + n + ".ffz_deleted", true);
|
i = total,
|
||||||
|
alternate;
|
||||||
|
|
||||||
|
while(i--) {
|
||||||
|
var msg = msgs.get(i);
|
||||||
|
|
||||||
|
if ( msg.from === user ) {
|
||||||
|
if ( f.settings.remove_deleted ) {
|
||||||
|
if ( alternate === undefined )
|
||||||
|
alternate = msg.ffz_alternate;
|
||||||
|
msgs.removeAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
t.set('messages.' + i + '.ffz_deleted', true);
|
||||||
if ( ! f.settings.prevent_clear )
|
if ( ! f.settings.prevent_clear )
|
||||||
t.set("messages." + n + ".deleted", true);
|
t.set('messages.' + i + '.deleted', true);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if ( alternate === undefined )
|
||||||
|
alternate = msg.ffz_alternate;
|
||||||
|
else {
|
||||||
|
alternate = ! alternate;
|
||||||
|
t.set('messages.' + i + '.ffz_alternate', alternate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( f.settings.mod_card_history ) {
|
if ( f.settings.mod_card_history ) {
|
||||||
var room = f.rooms && f.rooms[t.get('id')],
|
var room = f.rooms && f.rooms[t.get('id')],
|
||||||
|
@ -904,8 +944,10 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
last = user_history.length > 0 ? user_history[user_history.length-1] : null;
|
last = user_history.length > 0 ? user_history[user_history.length-1] : null;
|
||||||
|
|
||||||
has_delete = last !== null && last.is_delete;
|
has_delete = last !== null && last.is_delete;
|
||||||
if ( ! has_delete ) {
|
if ( has_delete ) {
|
||||||
user_history.push({from: 'jtv', is_delete: true, style: 'admin', cachedTokens: ['User has been timed out.'], date: new Date()});
|
last.cachedTokens = ['User has been timed out ' + utils.number_commas(++last.deleted_times) + ' times.'];
|
||||||
|
} else {
|
||||||
|
user_history.push({from: 'jtv', is_delete: true, style: 'admin', cachedTokens: ['User has been timed out.'], deleted_times: 1, date: new Date()});
|
||||||
while ( user_history.length > 20 )
|
while ( user_history.length > 20 )
|
||||||
user_history.shift();
|
user_history.shift();
|
||||||
}
|
}
|
||||||
|
@ -926,74 +968,86 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
trimMessages: function() {
|
||||||
|
var messages = this.get("messages"),
|
||||||
|
len = messages.get("length"),
|
||||||
|
limit = this.get("messageBufferSize");
|
||||||
|
|
||||||
|
if ( len > limit )
|
||||||
|
messages.removeAt(0, len - limit);
|
||||||
|
},
|
||||||
|
|
||||||
pushMessage: function(msg) {
|
pushMessage: function(msg) {
|
||||||
if ( this.shouldShowMessage(msg) ) {
|
if ( this.shouldShowMessage(msg) ) {
|
||||||
var t, s, n, a = this.get("messageBufferSize");
|
this.get("messages").pushObject(msg);
|
||||||
for (this.get("messages").pushObject(msg), t = this.get("messages.length"), s = t - a, n = 0; s > n; n++)
|
this.trimMessages();
|
||||||
this.get("messages").removeAt(0);
|
|
||||||
|
|
||||||
"admin" === msg.style || ("whisper" === msg.style && ! this.ffz_whisper_room ) || this.incrementProperty("unreadCount", 1);
|
"admin" === msg.style || ("whisper" === msg.style && ! this.ffz_whisper_room ) || this.incrementProperty("unreadCount", 1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addMessage: function(msg) {
|
addMessage: function(msg) {
|
||||||
try {
|
if ( msg ) {
|
||||||
if ( msg ) {
|
if ( ! f.settings.hosted_sub_notices && msg.style === 'notification' && HOSTED_SUB.test(msg.message) )
|
||||||
var is_whisper = msg.style === 'whisper';
|
return;
|
||||||
if ( f.settings.group_tabs && f.settings.whisper_room ) {
|
|
||||||
if ( ( is_whisper && ! this.ffz_whisper_room ) || ( ! is_whisper && this.ffz_whisper_room ) )
|
var is_whisper = msg.style === 'whisper';
|
||||||
return;
|
if ( f.settings.group_tabs && f.settings.whisper_room ) {
|
||||||
}
|
if ( ( is_whisper && ! this.ffz_whisper_room ) || ( ! is_whisper && this.ffz_whisper_room ) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( ! is_whisper )
|
if ( ! is_whisper )
|
||||||
msg.room = this.get('id');
|
msg.room = this.get('id');
|
||||||
|
|
||||||
// Tokenization
|
// Tokenization
|
||||||
f.tokenize_chat_line(msg);
|
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
||||||
|
|
||||||
// Keep the history.
|
// Keep the history.
|
||||||
if ( ! is_whisper && msg.from && msg.from !== 'jtv' && msg.from !== 'twitchnotify' && f.settings.mod_card_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];
|
var room = f.rooms && f.rooms[msg.room];
|
||||||
if ( room ) {
|
if ( room ) {
|
||||||
var chat_history = room.user_history = room.user_history || {},
|
var chat_history = room.user_history = room.user_history || {},
|
||||||
user_history = room.user_history[msg.from] = room.user_history[msg.from] || [];
|
user_history = room.user_history[msg.from] = room.user_history[msg.from] || [];
|
||||||
|
|
||||||
user_history.push({
|
user_history.push({
|
||||||
from: msg.tags && msg.tags['display-name'] || msg.from,
|
from: msg.tags && msg.tags['display-name'] || msg.from,
|
||||||
cachedTokens: msg.cachedTokens,
|
cachedTokens: msg.cachedTokens,
|
||||||
style: msg.style,
|
style: msg.style,
|
||||||
date: msg.date
|
date: msg.date
|
||||||
});
|
});
|
||||||
while ( user_history.length > 20 )
|
|
||||||
user_history.shift();
|
if ( user_history.length > 20 )
|
||||||
}
|
user_history.shift();
|
||||||
}
|
|
||||||
|
|
||||||
// Check for message from us.
|
|
||||||
if ( ! is_whisper ) {
|
|
||||||
var user = f.get_user();
|
|
||||||
if ( user && user.login === msg.from ) {
|
|
||||||
var was_banned = this.get('ffz_banned');
|
|
||||||
this.set('ffz_banned', false);
|
|
||||||
|
|
||||||
// Update the wait time.
|
|
||||||
if ( this.get('isModeratorOrHigher') || ! this.get('slowMode') )
|
|
||||||
this.updateWait(0, was_banned)
|
|
||||||
else if ( this.get('slowMode') )
|
|
||||||
this.updateWait(this.get('slowValue'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(err) { f.error("Room addMessage: " + err); }
|
|
||||||
|
// Check for message from us.
|
||||||
|
if ( ! is_whisper ) {
|
||||||
|
var user = f.get_user();
|
||||||
|
if ( user && user.login === msg.from ) {
|
||||||
|
var was_banned = this.get('ffz_banned');
|
||||||
|
this.set('ffz_banned', false);
|
||||||
|
|
||||||
|
// Update the wait time.
|
||||||
|
if ( this.get('isModeratorOrHigher') || ! this.get('slowMode') )
|
||||||
|
this.updateWait(0, was_banned)
|
||||||
|
else if ( this.get('slowMode') )
|
||||||
|
this.updateWait(this.get('slow'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also update chatters.
|
||||||
|
if ( ! is_whisper && this.chatters && ! this.chatters[msg.from] && msg.from !== 'twitchnotify' && msg.from !== 'jtv' )
|
||||||
|
this.ffzUpdateChatters(msg.from);
|
||||||
|
}
|
||||||
|
|
||||||
var out = this._super(msg);
|
var out = this._super(msg);
|
||||||
|
|
||||||
try {
|
// Color processing.
|
||||||
// Color processing.
|
if ( msg.color )
|
||||||
var color = msg.color;
|
f._handle_color(msg.color);
|
||||||
if ( color )
|
|
||||||
f._handle_color(color);
|
|
||||||
} catch(err) { f.error("Room addMessage2: " + err); }
|
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1061,6 +1115,11 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
if ( ! this.tmiRoom )
|
if ( ! this.tmiRoom )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ( this._ffz_chatter_timer ) {
|
||||||
|
clearTimeout(this._ffz_chatter_timer);
|
||||||
|
this._ffz_chatter_timer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
var room = this;
|
var room = this;
|
||||||
this.tmiRoom.list().done(function(data) {
|
this.tmiRoom.list().done(function(data) {
|
||||||
var chatters = {};
|
var chatters = {};
|
||||||
|
@ -1078,6 +1137,8 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
room.set("ffz_chatters", chatters);
|
room.set("ffz_chatters", chatters);
|
||||||
room.ffzUpdateChatters();
|
room.ffzUpdateChatters();
|
||||||
|
}).always(function() {
|
||||||
|
room._ffz_chatter_timer = setTimeout(room.ffzInitChatterCount.bind(room), 300000);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1156,60 +1217,6 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// ROOMSTATE~!
|
|
||||||
if ( ! connection.ffz_roomstate_patched ) {
|
|
||||||
connection.ffz_roomstate_patched = true;
|
|
||||||
connection._socket.off('data', connection._onSocketDataReceived, connection);
|
|
||||||
connection._socket.on('data', function(data) {
|
|
||||||
try {
|
|
||||||
var msg = utils.splitIRCMessage(data.data);
|
|
||||||
if ( msg.command === 'ROOMSTATE' ) {
|
|
||||||
// We have ROOMSTATE! Now, let's parse it a bit
|
|
||||||
// more and send it on.
|
|
||||||
msg.tags = utils.parseIRCTags(msg.tags);
|
|
||||||
msg.target = msg.params && msg.params[0];
|
|
||||||
|
|
||||||
this._trigger('roomstate', msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch(err) { f.error("Connection onData: " + err); }
|
|
||||||
|
|
||||||
return this._onSocketDataReceived(data);
|
|
||||||
}, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Glorious ROOMSTATE.
|
|
||||||
if ( ! tmi.ffz_roomstate_patched ) {
|
|
||||||
tmi.ffz_roomstate_patched = true;
|
|
||||||
tmi._roomConn.on("roomstate", function(ircMsg) {
|
|
||||||
if ( ircMsg.target !== this.ircChannel )
|
|
||||||
return;
|
|
||||||
|
|
||||||
this._trigger("roomstate", ircMsg.tags);
|
|
||||||
}, tmi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IT IS GLORIOUS!
|
|
||||||
tmi.on('roomstate', function(state) {
|
|
||||||
if ( ! room )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( state.hasOwnProperty('slow') ) {
|
|
||||||
room.set('slowMode', state.slow > 0);
|
|
||||||
room.set('slowValue', state.slow);
|
|
||||||
if ( ! room.get('slowMode') )
|
|
||||||
room.updateWait(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( state.hasOwnProperty('r9k') )
|
|
||||||
room.set('r9kMode', state.r9k);
|
|
||||||
|
|
||||||
if ( state.hasOwnProperty('subs-only') )
|
|
||||||
room.set('subsOnlyMode', state['subs-only']);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Check this shit.
|
// Check this shit.
|
||||||
tmi._roomConn._connection.off("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
tmi._roomConn._connection.off("message", tmi._roomConn._onIrcMessage, tmi._roomConn);
|
||||||
|
|
||||||
|
@ -1243,6 +1250,17 @@ FFZ.prototype._modify_room = function(room) {
|
||||||
|
|
||||||
this.set('ffz_is_patched', true);
|
this.set('ffz_is_patched', true);
|
||||||
|
|
||||||
}.observes('tmiRoom')
|
}.observes('tmiRoom'),
|
||||||
|
|
||||||
|
// Room State Stuff
|
||||||
|
|
||||||
|
slowMode: function() {
|
||||||
|
return this.get('slow') > 0;
|
||||||
|
}.property('slow'),
|
||||||
|
|
||||||
|
onSlowOff: function() {
|
||||||
|
if ( ! this.get('slowMode') )
|
||||||
|
this.updateWait(0);
|
||||||
|
}.observes('slowMode')
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -35,8 +35,8 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this._chat_style ) {
|
if ( this._chat_style ) {
|
||||||
this._chat_style.parentElement.removeChild(this._chat_style);
|
utils.update_css(this._chat_style, 'chat_font_size', '');
|
||||||
this._chat_style = undefined;
|
utils.update_css(this._chat_style, 'chat_ts_font_size', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable Chat Tabs
|
// Disable Chat Tabs
|
||||||
|
@ -175,11 +175,11 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Emoticonize
|
// Emoticonize
|
||||||
var original_emoticonize = BetterTTV.chat.templates.emoticonize;
|
var original_emoticonize = BetterTTV.chat.templates.emoticonize;
|
||||||
BetterTTV.chat.templates.emoticonize = function(message, emotes) {
|
BetterTTV.chat.templates.emoticonize = function(message, emotes) {
|
||||||
var tokens = original_emoticonize(message, emotes),
|
var tokens = original_emoticonize(message, emotes),
|
||||||
|
|
||||||
room = (received_room || BetterTTV.getChannel()),
|
room = (received_room || BetterTTV.getChannel()),
|
||||||
l_room = room && room.toLowerCase(),
|
l_room = room && room.toLowerCase(),
|
||||||
l_sender = received_sender && received_sender.toLowerCase(),
|
l_sender = received_sender && received_sender.toLowerCase(),
|
||||||
|
@ -206,7 +206,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
// Why is emote parsing so bad? ;_;
|
// Why is emote parsing so bad? ;_;
|
||||||
_.each(emotes, function(emote) {
|
_.each(emotes, function(emote) {
|
||||||
var tooltip = f._emote_tooltip(emote),
|
var tooltip = f._emote_tooltip(emote),
|
||||||
eo = ['<img class="emoticon" srcset="' + (emote.srcSet || "") + '" src="' + emote.urls[1] + '" alt="' + tooltip + '" title="' + tooltip + '" />'],
|
eo = ['<img class="emoticon" data-ffz-emote="' + emote.id + '" srcset="' + (emote.srcSet || "") + '" src="' + emote.urls[1] + '" data-regex="' + emote.name + '" title="' + tooltip + '" />'],
|
||||||
old_tokens = tokens;
|
old_tokens = tokens;
|
||||||
|
|
||||||
tokens = [];
|
tokens = [];
|
||||||
|
@ -240,6 +240,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sneak in Emojicon Processing
|
// Sneak in Emojicon Processing
|
||||||
|
/*
|
||||||
if ( f.settings.parse_emoji && f.emoji_data ) {
|
if ( f.settings.parse_emoji && f.emoji_data ) {
|
||||||
var old_tokens = tokens;
|
var old_tokens = tokens;
|
||||||
tokens = [];
|
tokens = [];
|
||||||
|
@ -274,7 +275,7 @@ FFZ.prototype.setup_bttv = function(delay) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
47
src/main.js
47
src/main.js
|
@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
|
||||||
|
|
||||||
// Version
|
// Version
|
||||||
var VER = FFZ.version_info = {
|
var VER = FFZ.version_info = {
|
||||||
major: 3, minor: 4, revision: 25,
|
major: 3, minor: 5, revision: 2,
|
||||||
toString: function() {
|
toString: function() {
|
||||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||||
}
|
}
|
||||||
|
@ -115,6 +115,7 @@ require('./tokenize');
|
||||||
|
|
||||||
// Analytics: require('./ember/router');
|
// Analytics: require('./ember/router');
|
||||||
require('./ember/channel');
|
require('./ember/channel');
|
||||||
|
require('./ember/player');
|
||||||
require('./ember/room');
|
require('./ember/room');
|
||||||
require('./ember/line');
|
require('./ember/line');
|
||||||
require('./ember/chatview');
|
require('./ember/chatview');
|
||||||
|
@ -156,21 +157,27 @@ FFZ.prototype.initialize = function(increment, delay) {
|
||||||
// Make sure that FrankerFaceZ doesn't start setting itself up until the
|
// Make sure that FrankerFaceZ doesn't start setting itself up until the
|
||||||
// Twitch ember application is ready.
|
// Twitch ember application is ready.
|
||||||
|
|
||||||
|
// Check for the player
|
||||||
|
if ( location.hostname === 'player.twitch.tv' ) {
|
||||||
|
this.init_player(delay);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for special non-ember pages.
|
// Check for special non-ember pages.
|
||||||
if ( /^\/(?:$|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) ) {
|
if ( /^\/(?:$|search$|user\/|p\/|settings|m\/|messages?\/)/.test(location.pathname) ) {
|
||||||
this.setup_normal(delay);
|
this.init_normal(delay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( location.hostname === 'passport' && /^\/(?:authorize)/.test(location.pathname) ) {
|
if ( location.hostname === 'passport' && /^\/(?:authorize)/.test(location.pathname) ) {
|
||||||
this.log("Running on passport!");
|
this.log("Running on passport!");
|
||||||
this.setup_normal(delay, true);
|
this.init_normal(delay, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for the dashboard.
|
// Check for the dashboard.
|
||||||
if ( /\/[^\/]+\/dashboard/.test(location.pathname) && !/bookmarks$/.test(location.pathname) ) {
|
if ( /\/[^\/]+\/dashboard/.test(location.pathname) && !/bookmarks$/.test(location.pathname) ) {
|
||||||
this.setup_dashboard(delay);
|
this.init_dashboard(delay);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,11 +195,32 @@ FFZ.prototype.initialize = function(increment, delay) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setup_ember(delay);
|
this.init_ember(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.setup_normal = function(delay, no_socket) {
|
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.users = {};
|
||||||
|
this.is_dashboard = false;
|
||||||
|
try {
|
||||||
|
this.embed_in_dash = window.top !== window && /\/[^\/]+\/dashboard/.test(window.top.location.pathname) && !/bookmarks$/.test(window.top.location.pathname);
|
||||||
|
} catch(err) { this.embed_in_dash = false; }
|
||||||
|
|
||||||
|
// Literally only make it dark.
|
||||||
|
this.load_settings();
|
||||||
|
this.setup_dark();
|
||||||
|
|
||||||
|
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
|
||||||
|
duration = end - start;
|
||||||
|
|
||||||
|
this.log("Initialization complete in " + duration + "ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FFZ.prototype.init_normal = function(delay, no_socket) {
|
||||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
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 in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||||
|
|
||||||
|
@ -231,7 +259,7 @@ FFZ.prototype.setup_normal = function(delay, no_socket) {
|
||||||
|
|
||||||
FFZ.prototype.is_dashboard = false;
|
FFZ.prototype.is_dashboard = false;
|
||||||
|
|
||||||
FFZ.prototype.setup_dashboard = function(delay) {
|
FFZ.prototype.init_dashboard = function(delay) {
|
||||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
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 in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||||
|
|
||||||
|
@ -268,7 +296,7 @@ FFZ.prototype.setup_dashboard = function(delay) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FFZ.prototype.setup_ember = function(delay) {
|
FFZ.prototype.init_ember = function(delay) {
|
||||||
var start = (window.performance && performance.now) ? performance.now() : Date.now();
|
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 in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
|
||||||
|
|
||||||
|
@ -293,6 +321,7 @@ FFZ.prototype.setup_ember = function(delay) {
|
||||||
//this.setup_router();
|
//this.setup_router();
|
||||||
this.setup_colors();
|
this.setup_colors();
|
||||||
this.setup_tokenization();
|
this.setup_tokenization();
|
||||||
|
this.setup_player();
|
||||||
this.setup_channel();
|
this.setup_channel();
|
||||||
this.setup_room();
|
this.setup_room();
|
||||||
this.setup_line();
|
this.setup_line();
|
||||||
|
|
|
@ -151,8 +151,8 @@ FFZ.menu_pages.settings = {
|
||||||
var a = a[1],
|
var a = a[1],
|
||||||
b = b[1],
|
b = b[1],
|
||||||
|
|
||||||
at = a.type === "button" ? 2 : 1,
|
at = a.type === "boolean" ? 1 : 2,
|
||||||
bt = b.type === "button" ? 2 : 1,
|
bt = b.type === "boolean" ? 1 : 2,
|
||||||
|
|
||||||
an = a.name.toLowerCase(),
|
an = a.name.toLowerCase(),
|
||||||
bn = b.name.toLowerCase();
|
bn = b.name.toLowerCase();
|
||||||
|
|
|
@ -152,6 +152,8 @@ FFZ.prototype.ws_create = function() {
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
f.error("Callback for " + request + ": " + err);
|
f.error("Callback for " + request + ": " + err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f._ws_callbacks[request] = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
326
src/tokenize.js
326
src/tokenize.js
|
@ -4,6 +4,66 @@ var FFZ = window.FrankerFaceZ,
|
||||||
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/",
|
||||||
helpers,
|
helpers,
|
||||||
|
|
||||||
|
SRCSETS = {};
|
||||||
|
build_srcset = function(id) {
|
||||||
|
if ( SRCSETS[id] )
|
||||||
|
return SRCSETS[id];
|
||||||
|
var out = SRCSETS[id] = TWITCH_BASE + id + "/1.0 1x, " + TWITCH_BASE + id + "/2.0 2x, " + TWITCH_BASE + id + "/3.0 4x";
|
||||||
|
return out;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
data_to_tooltip = function(data) {
|
||||||
|
var set = data.set,
|
||||||
|
set_type = data.set_type,
|
||||||
|
owner = data.owner;
|
||||||
|
|
||||||
|
if ( set_type === undefined )
|
||||||
|
set_type = "Channel";
|
||||||
|
|
||||||
|
if ( ! set )
|
||||||
|
return data.code;
|
||||||
|
|
||||||
|
else if ( set == "--twitch-turbo--" || set == "turbo" ) {
|
||||||
|
set = "Twitch Turbo";
|
||||||
|
set_type = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Emoticon: " + data.code + "\n" + (set_type ? set_type + ": " : "") + set + (owner ? "\nBy: " + owner.display_name : "");
|
||||||
|
},
|
||||||
|
|
||||||
|
build_tooltip = function(id) {
|
||||||
|
var emote_data = this._twitch_emotes[id],
|
||||||
|
set = emote_data ? emote_data.set : null;
|
||||||
|
|
||||||
|
if ( ! emote_data )
|
||||||
|
return "???";
|
||||||
|
|
||||||
|
if ( typeof emote_data == "string" )
|
||||||
|
return emote_data;
|
||||||
|
|
||||||
|
if ( emote_data.tooltip )
|
||||||
|
return emote_data.tooltip;
|
||||||
|
|
||||||
|
return emote_data.tooltip = data_to_tooltip(emote_data);
|
||||||
|
},
|
||||||
|
|
||||||
|
load_emote_data = function(id, code, success, data) {
|
||||||
|
if ( ! success )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ( code )
|
||||||
|
data.code = code;
|
||||||
|
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
reg_escape = function(str) {
|
reg_escape = function(str) {
|
||||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||||
},
|
},
|
||||||
|
@ -11,7 +71,148 @@ var FFZ = window.FrankerFaceZ,
|
||||||
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#!?&//=]*)/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]",
|
SEPARATORS = "[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]",
|
||||||
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*");
|
SPLITTER = new RegExp(SEPARATORS + "*," + SEPARATORS + "*"),
|
||||||
|
|
||||||
|
|
||||||
|
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
|
||||||
|
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
|
||||||
|
IMGUR_PATH = /^\/(?:gallery\/)?[A-Za-z0-9]+(?:\.(?:png|jpg|jpeg|gif|gifv|bmp))?$/,
|
||||||
|
IMAGE_EXT = /\.(?:png|jpg|jpeg|gif|bmp)$/i,
|
||||||
|
IMAGE_DOMAINS = [],
|
||||||
|
|
||||||
|
is_image = function(href, any_domain) {
|
||||||
|
var match = href.match(LINK_SPLIT);
|
||||||
|
if ( ! match )
|
||||||
|
return;
|
||||||
|
|
||||||
|
var domain = match[3].toLowerCase(), port = match[4],
|
||||||
|
path = match[5];
|
||||||
|
|
||||||
|
// Don't allow non-standard ports.
|
||||||
|
if ( port && port !== '80' && port !== '443' )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// imgur-specific checks.
|
||||||
|
if ( domain === 'i.imgur.com' || domain === 'imgur.com' || domain === 'www.imgur.com' || domain === 'm.imgur.com' )
|
||||||
|
return IMGUR_PATH.test(path);
|
||||||
|
|
||||||
|
return any_domain ? IMAGE_EXT.test(path) : IMAGE_DOMAINS.indexOf(domain) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
image_iframe = function(href, extra_class) {
|
||||||
|
return '<iframe class="ffz-image-hover' + (extra_class ? ' ' + extra_class : '') + '" allowtransparency="true" src="' + constants.SERVER + 'script/image-proxy.html?' + utils.quote_attr(href) + '"></iframe>';
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
build_link_tooltip = function(href) {
|
||||||
|
var link_data = this._link_data[href],
|
||||||
|
tooltip;
|
||||||
|
|
||||||
|
if ( ! link_data )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if ( link_data.tooltip )
|
||||||
|
return link_data.tooltip;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 ( data.unsafe )
|
||||||
|
links[x].classList.add('unsafe-link');
|
||||||
|
|
||||||
|
if ( ! links[x].classList.contains('deleted-link') )
|
||||||
|
links[x].title = tooltip;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
FFZ.SRC_IDS = {},
|
FFZ.SRC_IDS = {},
|
||||||
|
@ -105,8 +306,10 @@ FFZ.prototype.setup_tokenization = function() {
|
||||||
return _.zip(
|
return _.zip(
|
||||||
token.split(LINK),
|
token.split(LINK),
|
||||||
_.map(matches, function(e) {
|
_.map(matches, function(e) {
|
||||||
if ( ! show_deleted && (delete_links || e.length > 255) )
|
var long = e.length > 255;
|
||||||
return {mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(e) + '" data-url="' + utils.quote_attr(e) + '" href="#"><' + (e.length > 255 ? 'long link' : 'deleted link') + '></a><span class="mentioning">', own: true}
|
if ( ! show_deleted && (delete_links || long) )
|
||||||
|
return {isLink: true, isDeleted: true, isLong: long, href: e};
|
||||||
|
//return {mentionedUser: '</span><a class="deleted-link" title="' + utils.quote_attr(e) + '" data-url="' + utils.quote_attr(e) + '" href="#"><' + (e.length > 255 ? 'long link' : 'deleted link') + '></a><span class="mentioning">', own: true}
|
||||||
return {isLink: true, href: e};
|
return {isLink: true, href: e};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -119,12 +322,10 @@ FFZ.prototype.setup_tokenization = function() {
|
||||||
// Tokenization
|
// Tokenization
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, delete_links) {
|
||||||
if ( msgObject.cachedTokens )
|
if ( msgObject.cachedTokens )
|
||||||
return msgObject.cachedTokens;
|
return msgObject.cachedTokens;
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
var msg = msgObject.message,
|
var msg = msgObject.message,
|
||||||
user = this.get_user(),
|
user = this.get_user(),
|
||||||
room_id = msgObject.room,
|
room_id = msgObject.room,
|
||||||
|
@ -134,8 +335,18 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
tokens = [msg];
|
tokens = [msg];
|
||||||
|
|
||||||
// Standard tokenization
|
// Standard tokenization
|
||||||
if ( helpers && helpers.linkifyMessage )
|
if ( helpers && helpers.linkifyMessage ) {
|
||||||
tokens = helpers.linkifyMessage(tokens);
|
var labels = msg.labels || [],
|
||||||
|
mod_or_higher = labels.indexOf("owner") !== -1 ||
|
||||||
|
labels.indexOf("staff") !== -1 ||
|
||||||
|
labels.indexOf("admin") !== -1 ||
|
||||||
|
labels.indexOf("global_mod") !== -1 ||
|
||||||
|
labels.indexOf("mod") !== -1 ||
|
||||||
|
msg.style === 'admin';
|
||||||
|
|
||||||
|
tokens = helpers.linkifyMessage(tokens, delete_links && !mod_or_higher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( user && user.login && helpers && helpers.mentionizeMessage )
|
if ( user && user.login && helpers && helpers.mentionizeMessage )
|
||||||
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
|
tokens = helpers.mentionizeMessage(tokens, user.login, from_me);
|
||||||
|
@ -230,10 +441,6 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification) {
|
||||||
}
|
}
|
||||||
|
|
||||||
msgObject.cachedTokens = tokens;
|
msgObject.cachedTokens = tokens;
|
||||||
} catch(err) {
|
|
||||||
this.error("Tokenization Error: " + err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,47 +475,109 @@ FFZ.prototype.render_tokens = function(tokens, render_links) {
|
||||||
var f = this;
|
var f = this;
|
||||||
return _.map(tokens, function(token) {
|
return _.map(tokens, function(token) {
|
||||||
if ( token.emoticonSrc ) {
|
if ( token.emoticonSrc ) {
|
||||||
var tooltip;
|
var tooltip, srcset, extra;
|
||||||
if ( token.ffzEmote ) {
|
if ( token.ffzEmote ) {
|
||||||
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
var emote_set = f.emote_sets && f.emote_sets[token.ffzEmoteSet],
|
||||||
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
emote = emote_set && emote_set.emoticons && emote_set.emoticons[token.ffzEmote];
|
||||||
|
|
||||||
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
tooltip = emote ? utils.sanitize(f._emote_tooltip(emote)) : token.altText;
|
||||||
|
srcset = emote ? emote.srcSet : token.srcSet;
|
||||||
|
extra = ' data-ffz-emote="' + emote.id + '"';
|
||||||
|
|
||||||
} else if ( token.ffzEmoji ) {
|
} else if ( token.ffzEmoji ) {
|
||||||
var eid = token.ffzEmoji,
|
var eid = token.ffzEmoji,
|
||||||
emoji = f.emoji_data && f.emoji_data[eid];
|
emoji = f.emoji_data && f.emoji_data[eid];
|
||||||
|
|
||||||
tooltip = emoji ? "Emoji: " + token.altText + "\nName: :" + emoji.short_name + ":" : token.altText;
|
tooltip = emoji ? "Emoji: " + token.altText + "\nName: :" + emoji.short_name + ":" : token.altText;
|
||||||
|
srcset = emoji ? emoji.srcSet : token.srcSet;
|
||||||
|
extra = ' data-ffz-emoji="' + eid + '"';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var id = FFZ.src_to_id(token.emoticonSrc),
|
var id = token.replacedId || FFZ.src_to_id(token.emoticonSrc),
|
||||||
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
data = id && f._twitch_emotes && f._twitch_emotes[id];
|
||||||
|
|
||||||
tooltip = data && data.tooltip ? data.tooltip : token.altText;
|
if ( data )
|
||||||
|
tooltip = data.tooltip ? data.tooltip : token.altText;
|
||||||
|
else {
|
||||||
|
tooltip = f._twitch_emotes[id] = token.altText;
|
||||||
|
f.ws_send("twitch_emote", id, load_emote_data.bind(f, id, token.altText));
|
||||||
|
}
|
||||||
|
|
||||||
|
extra = ' data-emote="' + id + '"';
|
||||||
|
|
||||||
|
if ( ! constants.EMOTE_REPLACEMENTS[id] )
|
||||||
|
srcset = build_srcset(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return '<img class="emoticon tooltip" src="' + token.emoticonSrc + '" ' + (token.srcSet ? 'srcset="' + token.srcSet + '" ' : '') + 'alt="' + token.altText + '" title="' + tooltip + '">';
|
return '<img class="emoticon tooltip"' + (extra||"") + ' src="' + utils.quote_attr(token.emoticonSrc) + '" ' + (srcset ? 'srcset="' + utils.quote_attr(srcset) + '" ' : '') + 'alt="' + utils.quote_attr(token.altText) + '" title="' + utils.quote_attr(tooltip) + '">';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( token.isLink ) {
|
if ( token.isLink ) {
|
||||||
|
var text = token.title || (token.isLong && '<long link>') || (token.isDeleted && '<deleted link>') || token.href;
|
||||||
|
|
||||||
if ( ! render_links && render_links !== undefined )
|
if ( ! render_links && render_links !== undefined )
|
||||||
return token.href;
|
return utils.sanitize(text);
|
||||||
|
|
||||||
var s = token.href;
|
var href = token.href,
|
||||||
if ( s.indexOf("@") > -1 && (-1 === s.indexOf("/") || s.indexOf("@") < s.indexOf("/")) )
|
tooltip, cls = '',
|
||||||
return '<a href="mailto:' + s + '">' + s + '</a>';
|
|
||||||
|
ind_at = href.indexOf("@"),
|
||||||
|
ind_sl = href.indexOf("/");
|
||||||
|
|
||||||
|
if ( ind_at !== -1 && (ind_sl === -1 || ind_at < ind_sl) ) {
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var n = (s.match(/^https?:\/\//) ? "" : "http://") + s;
|
// Deleted Links
|
||||||
|
var actual_href = href;
|
||||||
// Check for link data.
|
if ( token.isDeleted ) {
|
||||||
var data = f._link_data && f._link_data[n] || {};
|
cls = 'deleted-link ' + cls;
|
||||||
|
tooltip = utils.sanitize(token.censoredHref || token.href);
|
||||||
return '<a href="' + n + '" class="' + (data.unsafe ? 'unsafe-link' : '') + '" title="' + utils.sanitize(data.tooltip || '') + '" target="_blank">' + s + '</a>';
|
href = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '<a class="' + cls + '" data-url="' + utils.quote_attr(actual_href) + '" href="' + utils.quote_attr(href || '#') + '" title="' + utils.quote_attr(tooltip || '') + '" target="_blank">' + utils.sanitize(text) + '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( token.mentionedUser )
|
if ( token.mentionedUser )
|
||||||
return '<span class="' + (token.own ? "mentioning" : "mentioned") + '">' + token.mentionedUser + "</span>";
|
return '<span class="' + (token.own ? "mentioning" : "mentioned") + '">' + utils.sanitize(token.mentionedUser) + "</span>";
|
||||||
|
|
||||||
if ( token.deletedLink )
|
if ( token.deletedLink )
|
||||||
return utils.sanitize(token.text);
|
return utils.sanitize(token.text);
|
||||||
|
@ -337,7 +606,8 @@ FFZ.prototype.tokenize_replace_emotes = function(tokens) {
|
||||||
// Check for a few specific emoticon IDs.
|
// Check for a few specific emoticon IDs.
|
||||||
var emote_id = FFZ.src_to_id(token.emoticonSrc);
|
var emote_id = FFZ.src_to_id(token.emoticonSrc);
|
||||||
if ( constants.EMOTE_REPLACEMENTS.hasOwnProperty(emote_id) ) {
|
if ( constants.EMOTE_REPLACEMENTS.hasOwnProperty(emote_id) ) {
|
||||||
token.emoticonSrc = constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote_id] + '" data-twitch-emote="' + emote_id;
|
token.replacedId = emote_id;
|
||||||
|
token.emoticonSrc = constants.EMOTE_REPLACEMENT_BASE + constants.EMOTE_REPLACEMENTS[emote_id];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ FFZ.prototype._schedule_following_count = function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! this._following_count_timer )
|
if ( ! this._following_count_timer )
|
||||||
this._following_count_timer = setTimeout(this._update_following_count.bind(this), 60000);
|
this._following_count_timer = setTimeout(this._update_following_count.bind(this), 55000 + (10000*Math.random()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ FFZ.prototype._update_following_count = function() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._following_count_timer = setTimeout(this._update_following_count.bind(this), 60000);
|
this._following_count_timer = setTimeout(this._update_following_count.bind(this), 55000 + (10000*Math.random()));
|
||||||
|
|
||||||
var Stream = window.App && App.__container__.resolve('model:stream'),
|
var Stream = window.App && App.__container__.resolve('model:stream'),
|
||||||
Live = Stream && Stream.find("live"),
|
Live = Stream && Stream.find("live"),
|
||||||
|
@ -92,7 +92,7 @@ FFZ.prototype._update_following_count = function() {
|
||||||
if ( Live )
|
if ( Live )
|
||||||
Live.load();
|
Live.load();
|
||||||
else
|
else
|
||||||
Twitch.api.get("streams/followed", {limit:1, offset:0}, {version:3})
|
Twitch.api && Twitch.api.get("streams/followed", {limit:1, offset:0}, {version:3})
|
||||||
.done(function(data) {
|
.done(function(data) {
|
||||||
f._draw_following_count(data._total);
|
f._draw_following_count(data._total);
|
||||||
}).fail(function() {
|
}).fail(function() {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
var FFZ = window.FrankerFaceZ,
|
var FFZ = window.FrankerFaceZ,
|
||||||
utils = require('../utils');
|
utils = require('../utils'),
|
||||||
|
|
||||||
|
VALID_CHANNEL = /^[A-Za-z0-9_]+$/,
|
||||||
|
TWITCH_URL = /^(?:https?:\/\/)?(?:www\.)?twitch\.tv\/([A-Za-z0-9_]+)/i;
|
||||||
|
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
|
@ -36,19 +39,24 @@ FFZ.settings_info.follow_buttons = {
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
FFZ.ffz_commands.following = function(room, args) {
|
FFZ.ffz_commands.following = function(room, args) {
|
||||||
args = args.join(" ").trim().split(/\s*,+\s*/);
|
args = args.join(" ").trim().toLowerCase().split(/[ ,]+/);
|
||||||
|
|
||||||
if ( args.length && args[0] === '' )
|
var out = [];
|
||||||
args.shift();
|
for(var i=0,l=args.length; i<l; i++) {
|
||||||
|
var arg = args[i],
|
||||||
|
match = arg.match(TWITCH_URL);
|
||||||
|
if ( match )
|
||||||
|
arg = match[1];
|
||||||
|
|
||||||
if ( args.length && args[args.length-1] === '' )
|
if ( arg !== '' && out.indexOf(arg) === -1 )
|
||||||
args.pop();
|
out.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
var user = this.get_user(), f = this;
|
var user = this.get_user(), f = this;
|
||||||
if ( ! user || (user.login !== room.id && user.login !== "sirstendec" && user.login !== "dansalvato") )
|
if ( ! user || (user.login !== room.id && user.login !== "sirstendec" && user.login !== "dansalvato") )
|
||||||
return "You must be logged in as the broadcaster to use this command.";
|
return "You must be logged in as the broadcaster to use this command.";
|
||||||
|
|
||||||
if ( ! this.ws_send("update_follow_buttons", [room.id, args], function(success, data) {
|
if ( ! this.ws_send("update_follow_buttons", [room.id, out], function(success, data) {
|
||||||
if ( ! success ) {
|
if ( ! success ) {
|
||||||
f.room_message(room, "There was an error updating the following buttons.");
|
f.room_message(room, "There was an error updating the following buttons.");
|
||||||
return;
|
return;
|
||||||
|
@ -126,7 +134,8 @@ FFZ.ws_commands.follow_sets = function(data) {
|
||||||
var controller = App.__container__.lookup('controller:channel'),
|
var controller = App.__container__.lookup('controller:channel'),
|
||||||
current_id = controller && controller.get('id'),
|
current_id = controller && controller.get('id'),
|
||||||
current_host = controller && controller.get('hostModeTarget.id'),
|
current_host = controller && controller.get('hostModeTarget.id'),
|
||||||
need_update = false;
|
need_update = false,
|
||||||
|
f = this;
|
||||||
|
|
||||||
this.follow_sets = this.follow_sets || {};
|
this.follow_sets = this.follow_sets || {};
|
||||||
|
|
||||||
|
@ -164,10 +173,11 @@ FFZ.ws_commands.follow_sets = function(data) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.load_set(sid, function(success, data) {
|
setTimeout(
|
||||||
if ( success )
|
this.load_set.bind(this, sid, function(success, data) {
|
||||||
data.users.push(room_id);
|
if ( success )
|
||||||
});
|
data.users.push(room_id);
|
||||||
|
}), Math.random()*2500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,8 +219,13 @@ FFZ.prototype.rebuild_following_ui = function() {
|
||||||
} else
|
} else
|
||||||
cont.innerHTML = '';
|
cont.innerHTML = '';
|
||||||
|
|
||||||
for(var i=0; i < data.length; i++) {
|
var processed = [channel_id];
|
||||||
this._build_following_button(cont, data[i]);
|
for(var i=0; i < data.length && i < 10; i++) {
|
||||||
|
var cid = data[i];
|
||||||
|
if ( processed.indexOf(cid) !== -1 )
|
||||||
|
continue;
|
||||||
|
this._build_following_button(cont, cid);
|
||||||
|
processed.push(cid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,8 +255,13 @@ FFZ.prototype.rebuild_following_ui = function() {
|
||||||
} else
|
} else
|
||||||
cont.innerHTML = '';
|
cont.innerHTML = '';
|
||||||
|
|
||||||
for(var i=0; i < data.length; i++) {
|
var processed = [hosted_id];
|
||||||
this._build_following_button(cont, data[i]);
|
for(var i=0; i < data.length && i < 10; i++) {
|
||||||
|
var cid = data[i];
|
||||||
|
if ( processed.indexOf(cid) !== -1 )
|
||||||
|
continue;
|
||||||
|
this._build_following_button(cont, cid);
|
||||||
|
processed.push(cid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,6 +273,9 @@ FFZ.prototype.rebuild_following_ui = function() {
|
||||||
// ---------------
|
// ---------------
|
||||||
|
|
||||||
FFZ.prototype._build_following_button = function(container, channel_id) {
|
FFZ.prototype._build_following_button = function(container, channel_id) {
|
||||||
|
if ( ! VALID_CHANNEL.test(channel_id) )
|
||||||
|
return this.log("Ignoring Invalid Channel: " + utils.sanitize(channel_id));
|
||||||
|
|
||||||
var btn = document.createElement('a'), f = this,
|
var btn = document.createElement('a'), f = this,
|
||||||
btn_c = document.createElement('div'),
|
btn_c = document.createElement('div'),
|
||||||
noti = document.createElement('a'),
|
noti = document.createElement('a'),
|
||||||
|
@ -264,8 +287,8 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
||||||
|
|
||||||
update = function() {
|
update = function() {
|
||||||
btn_c.classList.toggle('is-following', following);
|
btn_c.classList.toggle('is-following', following);
|
||||||
btn.title = (following ? "Unf" : "F") + "ollow " + display_name;
|
btn.title = (following ? "Unf" : "F") + "ollow " + utils.sanitize(display_name);
|
||||||
btn.innerHTML = (following ? "" : "Follow ") + display_name;
|
btn.innerHTML = (following ? "" : "Follow ") + utils.sanitize(display_name);
|
||||||
noti_c.classList.toggle('hidden', !following);
|
noti_c.classList.toggle('hidden', !following);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -359,7 +382,8 @@ FFZ.prototype._build_following_button = function(container, channel_id) {
|
||||||
|
|
||||||
display_name = FFZ.get_capitalization(channel_id, on_name);
|
display_name = FFZ.get_capitalization(channel_id, on_name);
|
||||||
update();
|
update();
|
||||||
check_following();
|
|
||||||
|
setTimeout(check_following, Math.random()*5000);
|
||||||
|
|
||||||
container.appendChild(btn_c);
|
container.appendChild(btn_c);
|
||||||
container.appendChild(noti_c);
|
container.appendChild(noti_c);
|
||||||
|
|
|
@ -112,6 +112,8 @@ FFZ.menu_pages.my_emotes = {
|
||||||
for(var set_id in twitch_sets)
|
for(var set_id in twitch_sets)
|
||||||
if ( f._twitch_set_to_channel[set_id] )
|
if ( f._twitch_set_to_channel[set_id] )
|
||||||
ts[set_id] = twitch_sets[set_id];
|
ts[set_id] = twitch_sets[set_id];
|
||||||
|
else
|
||||||
|
ts[set_id] = "twitch_unknown";
|
||||||
|
|
||||||
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, ts);
|
return FFZ.menu_pages.my_emotes.draw_menu.bind(f)(view, container, ts);
|
||||||
};
|
};
|
||||||
|
@ -161,6 +163,7 @@ FFZ.menu_pages.my_emotes = {
|
||||||
|
|
||||||
heading.className = 'heading';
|
heading.className = 'heading';
|
||||||
heading.innerHTML = '<span class="right">FrankerFaceZ</span>Emoji';
|
heading.innerHTML = '<span class="right">FrankerFaceZ</span>Emoji';
|
||||||
|
heading.style.backgroundImage = 'url("' + constants.SERVER + '/emoji/1f4af-1x.png")';
|
||||||
|
|
||||||
menu.className = 'emoticon-grid collapsable';
|
menu.className = 'emoticon-grid collapsable';
|
||||||
menu.appendChild(heading);
|
menu.appendChild(heading);
|
||||||
|
@ -212,7 +215,9 @@ FFZ.menu_pages.my_emotes = {
|
||||||
|
|
||||||
channel_id = this._twitch_set_to_channel[set_id], title;
|
channel_id = this._twitch_set_to_channel[set_id], title;
|
||||||
|
|
||||||
if ( channel_id === "global" )
|
if ( channel_id === "twitch_unknown" )
|
||||||
|
title = "Unknown Channel";
|
||||||
|
else if ( channel_id === "global" )
|
||||||
title = "Global Emoticons";
|
title = "Global Emoticons";
|
||||||
else if ( channel_id === "turbo" || channel_id === "turbo_faces" )
|
else if ( channel_id === "turbo" || channel_id === "turbo_faces" )
|
||||||
title = "Twitch Turbo";
|
title = "Twitch Turbo";
|
||||||
|
|
34
style.css
34
style.css
|
@ -188,6 +188,8 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
||||||
|
|
||||||
/* Theater Mode hover bar */
|
/* Theater Mode hover bar */
|
||||||
|
|
||||||
|
.app-main.theatre .player-column:focus #hostmode > div.target-meta,
|
||||||
|
.app-main.theatre .player-column:hover #hostmode > div.target-meta,
|
||||||
.app-main.theatre #channel .player-column:focus #broadcast-meta,
|
.app-main.theatre #channel .player-column:focus #broadcast-meta,
|
||||||
.app-main.theatre #channel .player-column:hover #broadcast-meta {
|
.app-main.theatre #channel .player-column:hover #broadcast-meta {
|
||||||
background-color: #19191f;
|
background-color: #19191f;
|
||||||
|
@ -202,29 +204,45 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-sidebar-swap .app-main.theatre .player-column:focus #hostmode > div.target-meta,
|
||||||
|
.ffz-sidebar-swap .app-main.theatre .player-column:hover #hostmode > div.target-meta,
|
||||||
.ffz-sidebar-swap .app-main.theatre #channel .player-column:focus #broadcast-meta,
|
.ffz-sidebar-swap .app-main.theatre #channel .player-column:focus #broadcast-meta,
|
||||||
.ffz-sidebar-swap .app-main.theatre #channel .player-column:hover #broadcast-meta {
|
.ffz-sidebar-swap .app-main.theatre #channel .player-column:hover #broadcast-meta {
|
||||||
left: 145px;
|
left: 145px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-main.theatre #hostmode > div.target-meta div.target-title {
|
||||||
|
padding: 5px 0 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-main.theatre #hostmode > div.target-meta div.target-title,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info { padding-left: 5px; }
|
.app-main.theatre #channel .player-column #broadcast-meta .info { padding-left: 5px; }
|
||||||
|
|
||||||
|
.app-main.theatre #hostmode > div.target-meta div.target-title,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info .title {
|
.app-main.theatre #channel .player-column #broadcast-meta .info .title {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
color: #dedede;
|
color: #dedede;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-main.theatre #hostmode > div.target-meta div.target-title,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info .title,
|
.app-main.theatre #channel .player-column #broadcast-meta .info .title,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info .title .over {
|
.app-main.theatre #channel .player-column #broadcast-meta .info .title .over {
|
||||||
background-color: rgba(16,16,16,0.3);
|
background-color: rgba(16,16,16,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-main.theatre #hostmode > div.target-meta .target-user-and-game,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info .channel,
|
.app-main.theatre #channel .player-column #broadcast-meta .info .channel,
|
||||||
.app-main.theatre #channel .player-column #broadcast-meta .info .edit-link,
|
.app-main.theatre #channel .player-column #broadcast-meta .info .edit-link,
|
||||||
.app-main.theatre #broadcast-meta .profile-link {
|
.app-main.theatre #broadcast-meta .profile-link {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-main.theatre .player-column:focus #hostmode > div.clearfix, .app-main.theatre .player-column:hover #hostmode > div.clearfix {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-main.theatre .player-column:focus #hostmode > div.clearfix, .app-main.theatre .player-column:hover #hostmode > div.clearfix,
|
||||||
.app-main.theatre .player-column:focus .stats-and-actions, .app-main.theatre .player-column:hover .stats-and-actions {
|
.app-main.theatre .player-column:focus .stats-and-actions, .app-main.theatre .player-column:hover .stats-and-actions {
|
||||||
background-color: #19191f;
|
background-color: #19191f;
|
||||||
|
|
||||||
|
@ -690,6 +708,8 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
|
||||||
|
|
||||||
/* Fix Moderation Cards */
|
/* Fix Moderation Cards */
|
||||||
|
|
||||||
|
img.channel_background[src="null"] { display: none; }
|
||||||
|
|
||||||
.ember-chat .ffz-moderation-card {
|
.ember-chat .ffz-moderation-card {
|
||||||
border: 2px solid #cbcbcb;
|
border: 2px solid #cbcbcb;
|
||||||
max-width: 340px;
|
max-width: 340px;
|
||||||
|
@ -784,6 +804,8 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
|
||||||
|
|
||||||
/* Chat Rows */
|
/* Chat Rows */
|
||||||
|
|
||||||
|
.ffz-alias { font-style: italic; }
|
||||||
|
|
||||||
.ember-chat .chat-messages .chat-line.ffz-has-deleted {
|
.ember-chat .chat-messages .chat-line.ffz-has-deleted {
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
}
|
}
|
||||||
|
@ -1290,10 +1312,10 @@ body.ffz-minimal-chat .ember-chat .chat-buttons-container {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ffz-minimal-chat .ember-chat .chat-messages,
|
/*body.ffz-minimal-chat .ember-chat .chat-messages,
|
||||||
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
||||||
bottom: 33px;
|
bottom: 33px;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
|
||||||
right: 10px;
|
right: 10px;
|
||||||
|
@ -1308,17 +1330,19 @@ body.ffz-minimal-chat .ember-chat .chat-room {
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ffz-minimal-chat .ember-chat .chat-interface {
|
body.ffz-minimal-chat .ember-chat .chat-interface {
|
||||||
height: 33px !important;
|
/*height: 33px !important;*/
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain {
|
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain {
|
||||||
top: 0 !important;
|
top: 0 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
|
body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
|
||||||
height: 33px !important;
|
/*height: 33px !important;*/
|
||||||
|
overflow: hidden;
|
||||||
border-bottom: 0 !important;
|
border-bottom: 0 !important;
|
||||||
border-left: 0;
|
border-left: 0;
|
||||||
border-right: 0;
|
border-right: 0;
|
||||||
|
@ -1581,6 +1605,8 @@ li[data-name="following"] a {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ffz-follow-count:empty { display: none; }
|
||||||
|
|
||||||
.ffz-follow-count {
|
.ffz-follow-count {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue