From 8056463bbe6136a3b3b9dae253081d0d10cf63c8 Mon Sep 17 00:00:00 2001
From: SirStendec
Date: Fri, 6 May 2016 02:23:12 -0400
Subject: [PATCH] 3.5.169. Oops. Haven't commited in a while :< Did... stuff?
And things.
---
dark.css | 65 +++++--
src/constants.js | 6 +-
src/ember/channel.js | 60 ++++--
src/ember/chat-input.js | 2 +-
src/ember/chatview.js | 14 +-
src/ember/conversations.js | 8 +-
src/ember/directory.js | 159 +++++++++++++++-
src/ember/feed-card.js | 17 +-
src/ember/following.js | 356 ++++++++++++++++++++---------------
src/ember/layout.js | 2 +-
src/ember/line.js | 21 ++-
src/ember/moderation-card.js | 11 +-
src/ember/player.js | 4 +-
src/ember/room.js | 195 +++++++------------
src/ember/vod-chat.js | 4 +-
src/ext/api.js | 72 +++++++
src/ext/rechat.js | 4 +-
src/main.js | 5 +-
src/settings.js | 4 +-
src/socket.js | 6 +-
src/tokenize.js | 61 +++++-
src/ui/about_page.js | 77 ++++++--
src/ui/following-count.js | 143 ++++++--------
src/ui/menu.js | 10 +-
src/ui/my_emotes.js | 2 +-
src/ui/tooltips.js | 5 +
src/utils.js | 13 ++
style.css | 90 +++++----
28 files changed, 908 insertions(+), 508 deletions(-)
diff --git a/dark.css b/dark.css
index 89ded21a..3acd3273 100644
--- a/dark.css
+++ b/dark.css
@@ -72,6 +72,8 @@
}
/* stats */
+.ffz-dark .ct-tags--extracted { border-bottom: none }
+
.ffz-dark .stats-and-actions,
.ffz-dark #main_col .content #stats_and_actions {
border-bottom-color: rgba(255,255,255,0.2);
@@ -115,19 +117,23 @@ body.ffz-dark,
}
.ffz-dark div.title > span.real,
-.ffz-dark div.title > span.over{
- color:rgb(222,222,222)!important;
- background-color:rgba(16,16,16,0.3)!important;
+.ffz-dark div.title > span.over,
+.ffz-dark #broadcast-meta .info .title {
+ color: #DEDEDE !important;
+ background-color: rgba(16,16,16,0.3) !important;
}
.ffz-dark div.title > span.over:hover,
-.ffz-dark div.title > span.real:hover {
- background-color:rgb(16,16,16)!important;
- color:rgb(255,255,255)!important;
+.ffz-dark div.title > span.real:hover,
+.ffz-dark #broadcast-meta .info .title:hover {
+ color: #fff !important;
+ background-color: #101010 !important;
}
+
/* Right Column */
+.ffz-dark .ct-banner--off .ct-banner__toggle,
.ffz-dark #right_col {
background-color: rgb(25,25,31);
color: #fff;
@@ -175,6 +181,9 @@ body.ffz-dark,
box-shadow: rgba(255,255,255,0.2) 0 0 0 1px inset;
}
+.ffz-dark .player-menu__header { color: #c3c3c3 }
+.ffz-dark .player-menu__section { border-bottom-color: rgba(255,255,255,0.2) }
+
.ffz-dark .balloon:after { box-shadow: none }
.ffz-dark .st-autocomplete-sidebar .label,
@@ -216,6 +225,7 @@ body.ffz-dark,
}
.ffz-dark .change-banner .banner-preview,
+.ffz-dark .ct-banner--off .ct-banner__inputbox,
.ffz-dark form.js-new_panel_form input,
.ffz-dark form.js-new_panel_form textarea,
.ffz-dark .conversation-input-bar textarea,
@@ -269,10 +279,20 @@ body.ffz-dark,
.ffz-dark .manager .videos-grid .video:hover .meta .actions li a,
.ffz-dark .ember-chat .chat-room-list .room:not(:hover) p.room-title,
.ffz-dark .dropmenu_action:not(:hover) span,
-.ffz-dark a:not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) {
+.ffz-dark a:not(.profile-card__content):not(.filter-item):not(.ui-state-focus):not(.button):not(.switch):not(.follow):not(.fb_button):not(.what) {
color: #a68ed2;
}
+.ffz-dark .balloon--cols .balloon__list~.balloon__list {
+ box-shadow: -1px 0 0 rgba(255,255,255,0.2);
+}
+
+.ffz-dark .warp__item a.js-language-select,
+.ffz-dark .warp__item > a { color: #d5d4d9 !important }
+
+.ffz-dark .warp__item--toggled a.js-language-select,
+.ffz-dark .warm__item--toggled > a { color: #eae9ec !important }
+
.ffz-dark .exit-theatre > a { color: #000 !important; }
.ffz-dark .follow-button a,
@@ -297,7 +317,7 @@ body.ffz-dark,
background-color: #25252a;
}
-.ffz-dark .button:not(.primary) {
+.ffz-dark .button:not(.button--status):not(.primary) {
color: #a68ed2;
}
@@ -1117,6 +1137,29 @@ body.ffz-dark,
}
+/* Creative Tags */
+
+.ffz-dark .ct-tags__tag {
+ background-color: #191919;
+ color: #999 !important;
+}
+
+.ffz-dark .ct-tag--light .ct-tag__link:hover { color: #ccc !important }
+
+.ffz-dark .ct-tags__tag:hover {
+ background-color: #000;
+ color: #ccc !important;
+}
+
+.ffz-dark .ct-tags__tag.ct-tags__tag--current {
+ background-color: #7d5bbc;
+ color: #fff !important;
+}
+
+.ffz-dark .ct-banner--off .ct-banner__header { color: #999 }
+.ffz-dark .ct-banner--off .ct-banner__link { color: #ccc }
+
+
/* Activity Feeds */
.button.alert { color: #fff !important }
@@ -1128,12 +1171,12 @@ body.ffz-dark,
.ffz-dark .activity-meta:before { background-color: #474747 }
-.ffz-dark .activity-react__like:hover {
+.ffz-dark .activity-react__item:hover {
border-color: #d5d5d5;
background-color: #242424;
}
-.ffz-dark .activity-react__like svg.endorse-icon #head__base { fill: #fff }
+.ffz-dark .activity-react__item svg.endorse-icon #head__base { fill: #fff }
.ffz-dark .activity-create__actions {
background-color: #191919;
@@ -1141,7 +1184,7 @@ body.ffz-dark,
}
.ffz-dark .activity-create,
-.ffz-dark .activity-react__like {
+.ffz-dark .activity-react__item {
border-color: #474747;
color: #fff !important;
background-color: #191919;
diff --git a/src/constants.js b/src/constants.js
index ec575f43..c85845ab 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -15,6 +15,8 @@ module.exports = FrankerFaceZ.constants = {
DEBUG: DEBUG,
SERVER: SERVER,
+ IS_OSX: navigator.platform ? navigator.platform.indexOf('Mac') !== -1 : /OS X/.test(navigator.userAgent),
+
// Twitch Client ID for API Stuff
CLIENT_ID: "a3bc9znoz6vi8ozsoca0inlcr4fcvkl",
@@ -22,11 +24,11 @@ module.exports = FrankerFaceZ.constants = {
WS_SERVER_POOLS: {
1: [
- ["wss://catbag.frankerfacez.com/", 0.5],
+ ["wss://catbag.frankerfacez.com/", 0.25],
["wss://andknuckles.frankerfacez.com/", 1],
["wss://tuturu.frankerfacez.com/", 1]],
2: [
- ["ws://localhost:8001/", 1]]
+ ["wss://localhost:8001/", 1]]
},
CHAT_COLORS: ["#FF0000", "#0000FF", "#008000", "#B22222", "#FF7F50", "#9ACD32", "#FF4500", "#2E8B57", "#DAA520", "#D2691E", "#5F9EA0", "#1E90FF", "#FF69B4", "#8A2BE2", "#00FF7F"],
diff --git a/src/ember/channel.js b/src/ember/channel.js
index 8ba72a32..1b4e2d95 100644
--- a/src/ember/channel.js
+++ b/src/ember/channel.js
@@ -40,16 +40,17 @@ FFZ.prototype.setup_channel = function() {
// Update Existing
var views = utils.ember_views();
for(var key in views) {
- if ( ! views.hasOwnProperty(key) )
- continue;
-
var view = views[key];
- if ( !(view instanceof Channel) )
- continue;
-
- this.log("Manually updating Channel Index view.", view);
- this._modify_cindex(view);
- view.ffzInit();
+ if ( view instanceof Channel ) {
+ this.log("Manually updating existing Channel Index view.", view);
+ try {
+ if ( ! view.ffzInit )
+ this._modify_cindex(view);
+ view.ffzInit();
+ } catch(err) {
+ this.error("setup: view:channel/index: " + err);
+ }
+ }
};
@@ -145,7 +146,7 @@ FFZ.prototype.setup_channel = function() {
if ( f._cindex )
f._cindex.ffzFixTitle();
- }.observes("content.status", "content.id"),
+ }.observes("content.status", "content.id", "hostModeTarget.status", "hostModeTarget.id"),
ffzHostTarget: function() {
var target = this.get('content.hostModeTarget'),
@@ -245,24 +246,43 @@ FFZ.prototype._modify_cindex = function(view) {
if ( Layout )
Layout.set('isTheatreMode', true);
}
+
+ this.$().on("click", ".ffz-creative-tag-link", function(e) {
+ if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
+ return;
+
+ utils.ember_lookup("router:main").transitionTo('creative.hashtag.index', this.getAttribute('data-tag'));
+ e.preventDefault();
+ return false;
+ });
},
ffzFixTitle: function() {
if ( f.has_bttv || ! f.settings.stream_title )
return;
- var status = this.get("controller.content.status") || this.get("controller.status"),
- channel = this.get("controller.content.id") || this.get("controller.id");
+ var status = this.get("controller.content.status"),
+ channel = this.get("controller.content.id"),
+ game = this.get("controller.content.game"),
- status = f.render_tokens(f.tokenize_line(channel, channel, status, true));
+ tokens = f.tokenize_line(channel, channel, status, true);
- this.$(".title span").each(function(i, el) {
- var scripts = el.querySelectorAll("script");
- if ( ! scripts.length )
- el.innerHTML = status;
- else
- el.innerHTML = scripts[0].outerHTML + status + scripts[1].outerHTML;
- });
+ if ( game === 'Creative' )
+ tokens = f.tokenize_ctags(tokens);
+
+ this.$("#broadcast-meta .title").html(f.render_tokens(tokens));
+
+ status = this.get('controller.hostModeTarget.status');
+ channel = this.get('controller.hostModeTarget.id');
+ game = this.get('controller.hostModeTarget.game');
+
+ if ( channel ) {
+ tokens = f.tokenize_line(channel, channel, status, true);
+ if ( game === 'Creative' )
+ tokens = f.tokenize_ctags(tokens);
+
+ this.$(".target-meta .target-title").html(f.render_tokens(tokens));
+ }
},
diff --git a/src/ember/chat-input.js b/src/ember/chat-input.js
index 6d71caec..41706494 100644
--- a/src/ember/chat-input.js
+++ b/src/ember/chat-input.js
@@ -871,7 +871,7 @@ FFZ.prototype._modify_chat_input = function(component) {
case KEYCODES.TAB:
// If we do Ctrl-Tab or Alt-Tab. Just don't
// even think of doing suggestions.
- if ( e.ctrlKey || e.altKey )
+ if ( e.ctrlKey || e.altKey || e.metaKey )
break;
e.preventDefault();
diff --git a/src/ember/chatview.js b/src/ember/chatview.js
index c90599b6..aab5feec 100644
--- a/src/ember/chatview.js
+++ b/src/ember/chatview.js
@@ -366,6 +366,16 @@ FFZ.prototype.setup_chatview = function() {
}.observes("currentChannelRoom", "connectedPrivateGroupRooms"),
+ ffzSubOwnChannelRoom: function() {
+ var user = f.get_user(),
+ room = this.get("currentChannelRoom"),
+ room_id = room && room.get("id");
+
+ if ( user && user.login && user.login === room_id )
+ room && room.ffzSubscribe && room.ffzSubscribe();
+
+ }.observes("currentChannelRoom"),
+
ffzUpdateInvites: function() {
if ( ! f._chatv || f.has_bttv )
return;
@@ -520,8 +530,8 @@ FFZ.prototype._modify_cview = function(view) {
el && el.setAttribute('data-room', room_id || "");
this.$('.textarea-contain').append(f.build_ui_link(this));
- this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- this.$('.chat-messages').find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //this.$('.chat-messages').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //this.$('.chat-messages').find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
if ( ! f.has_bttv ) {
if ( f.settings.group_tabs )
diff --git a/src/ember/conversations.js b/src/ember/conversations.js
index aa1d7fc0..9b8a9e98 100644
--- a/src/ember/conversations.js
+++ b/src/ember/conversations.js
@@ -101,8 +101,8 @@ FFZ.prototype.setup_conversations = function() {
this.log("Unable to resolve: component:conversation-line");
// TODO: Make this better later.
- jQuery('.conversations-list').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- jQuery('.conversations-list').find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.conversations-list').find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.conversations-list').find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
}
@@ -175,8 +175,8 @@ FFZ.prototype._modify_conversation_window = function(component) {
}
jQuery('.badge', el).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
- jQuery(el).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- jQuery(el).find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery(el).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery(el).find('.ffz-tooltip').tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
}
});
}
diff --git a/src/ember/directory.js b/src/ember/directory.js
index 31cb3407..032388be 100644
--- a/src/ember/directory.js
+++ b/src/ember/directory.js
@@ -55,7 +55,23 @@ FFZ.settings_info.sidebar_hide_recommended_channels = {
};
-FFZ.settings_info.directory_creative_all_tags = {
+FFZ.settings_info.sidebar_hide_recommended_friends = {
+ type: "boolean",
+ value: true,
+
+ category: "Appearance",
+ no_mobile: true,
+
+ name: "Sidebar Recommended Friends",
+ help: "Display the Recommended Friends section on the sidebar.",
+
+ on_update: function(val) {
+ document.body.classList.toggle('ffz-hide-recommended-friends', !val);
+ }
+ };
+
+
+/*FFZ.settings_info.directory_creative_all_tags = {
type: "boolean",
value: false,
@@ -68,7 +84,7 @@ FFZ.settings_info.directory_creative_all_tags = {
on_update: function(val) {
document.body.classList.toggle('ffz-creative-tags', val);
}
- };
+ };*/
FFZ.settings_info.directory_creative_showcase = {
@@ -123,6 +139,82 @@ FFZ.settings_info.directory_group_hosts = {
};
+FFZ.settings_info.banned_games = {
+ type: "button",
+ value: [],
+
+ category: "Directory",
+ no_mobile: true,
+ name: "Banned Games",
+ help: "A list of games that will not be displayed in the Directory.",
+
+ on_update: function() {
+ var banned = this.settings.banned_games,
+ els = document.querySelectorAll('.ffz-directory-preview');
+
+ for(var i=0; i < els.length; i++) {
+ var el = els[i],
+ game = el.getAttribute('data-game');
+
+ el.classList.toggle('ffz-game-banned', banned.indexOf(game && game.toLowerCase()) !== -1);
+ }
+ },
+
+ method: function() {
+ var f = this,
+ old_val = f.settings.banned_games.join(", ");
+
+ utils.prompt(
+ "Banned Games",
+ "Please enter a comma-separated list of games that you would like to be banned from viewing in the Directory.
This is case insensitive, however you must type the full name.
Example: League of Legends, Dota 2, Smite
",
+ old_val,
+ function(new_val) {
+ if ( new_val === null || new_val === undefined )
+ return;
+ f.settings.set("banned_games", _.unique(new_val.trim().toLowerCase().split(/\s*,\s*/)));
+ }, 600);
+ }
+}
+
+
+FFZ.settings_info.spoiler_games = {
+ type: "button",
+ value: [],
+
+ category: "Directory",
+ no_mobile: true,
+ name: "Spoiler Games",
+ help: "Stream thumbnails will be hidden for games that you add to this list.",
+
+ on_update: function() {
+ var spoiled = this.settings.spoiler_games,
+ els = document.querySelectorAll('.ffz-directory-preview');
+
+ for(var i=0; i < els.length; i++) {
+ var el = els[i],
+ game = el.getAttribute('data-game');
+
+ el.classList.toggle('ffz-game-spoilered', spoiled.indexOf(game && game.toLowerCase()) !== -1);
+ }
+ },
+
+ method: function() {
+ var f = this,
+ old_val = f.settings.spoiler_games.join(", ");
+
+ utils.prompt(
+ "Spoiler Games",
+ "Please enter a comma-separated list of games that you would like to have the thumbnails hidden for in the Directory.
This is case insensitive, however you must type the full name.
Example: Undertale
",
+ old_val,
+ function(new_val) {
+ if ( new_val === null || new_val === undefined )
+ return;
+ f.settings.set("spoiler_games", _.unique(new_val.trim().toLowerCase().split(/\s*,\s*/)));
+ }, 600);
+ }
+}
+
+
FFZ.settings_info.directory_host_menus = {
type: "select",
options: {
@@ -168,6 +260,7 @@ FFZ.prototype.setup_directory = function() {
document.body.classList.toggle('ffz-creative-tags', this.settings.directory_creative_all_tags);
document.body.classList.toggle('ffz-creative-showcase', this.settings.directory_creative_showcase);
document.body.classList.toggle('ffz-hide-recommended-channels', !this.settings.sidebar_hide_recommended_channels);
+ document.body.classList.toggle('ffz-hide-recommended-friends', !this.settings.sidebar_hide_recommended_friends);
var GamesFollowing = utils.ember_lookup('controller:games-following'),
f = this;
@@ -197,7 +290,7 @@ FFZ.prototype.setup_directory = function() {
var ChannelView = utils.ember_resolve('component:stream-preview');
if ( ChannelView ) {
- this._modify_directory_live(ChannelView);
+ this._modify_directory_live(ChannelView, false);
try {
ChannelView.create().destroy();
} catch(err) { }
@@ -205,7 +298,7 @@ FFZ.prototype.setup_directory = function() {
var CreativeChannel = utils.ember_resolve('component:creative-preview');
if ( CreativeChannel ) {
- this._modify_directory_live(CreativeChannel);
+ this._modify_directory_live(CreativeChannel, false);
try {
CreativeChannel.create().destroy();
} catch(err) { }
@@ -222,20 +315,31 @@ FFZ.prototype.setup_directory = function() {
} catch(err) { }
+ var VideoPreview = utils.ember_resolve('component:video-preview');
+ if ( VideoPreview ) {
+ VideoPreview = this._modify_video_preview(VideoPreview);
+ try { VideoPreview.create().destroy();
+ } catch(err) { }
+ }
+
+
// Initialize existing views.
var views = utils.ember_views();
for(var key in views) {
var view = views[key];
if ( (ChannelView && view instanceof ChannelView) || (CreativeChannel && view instanceof CreativeChannel) ) {
if ( ! view.ffzInit )
- this._modify_directory_live(view);
+ this._modify_directory_live(view, false);
} else if ( CSGOChannel && view instanceof CSGOChannel ) {
if ( ! view.ffzInit )
this._modify_directory_live(view, true);
} else if ( view instanceof HostView || view.get('tt_content') === 'live_host' ) {
if ( ! view.ffzInit )
this._modify_directory_host(view);
- } else
+ } else if ( VideoPreview && view instanceof VideoPreview ) {
+ if ( ! view.ffzInit )
+ this._modify_video_preview(view);
+ } else
continue;
try {
@@ -386,9 +490,15 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
meta = el && el.querySelector('.meta'),
thumb = el && el.querySelector('.thumb'),
cap = thumb && thumb.querySelector('.cap'),
- channel_id = this.get(pref + 'channel.name');
+ channel_id = this.get(pref + 'channel.name'),
+ game = this.get(pref + 'game');
+ el.classList.add('ffz-directory-preview');
el.setAttribute('data-channel', channel_id);
+ el.setAttribute('data-game', game);
+
+ el.classList.toggle('ffz-game-banned', f.settings.banned_games.indexOf(game && game.toLowerCase()) !== -1);
+ el.classList.toggle('ffz-game-spoilered', f.settings.spoiler_games.indexOf(game && game.toLowerCase()) !== -1);
// CSGO doesn't provide the actual uptime information...
if ( !is_csgo && f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
@@ -482,6 +592,33 @@ FFZ.prototype._modify_directory_live = function(dir, is_csgo) {
}
+FFZ.prototype._modify_video_preview = function(vp) {
+ var f = this;
+ vp.reopen({
+ didInsertElement: function() {
+ this._super();
+ try {
+ this.ffzInit();
+ } catch(err) {
+ f.error("component:video-preview ffzInit: " + err);
+ }
+ },
+
+ ffzInit: function() {
+ var el = this.get('element'),
+ game = this.get('video.game');
+
+ el.classList.add('ffz-directory-preview');
+ el.setAttribute('data-channel', this.get('video.channel.id'));
+ el.setAttribute('data-game', game);
+
+ el.classList.toggle('ffz-game-banned', f.settings.banned_games.indexOf(game && game.toLowerCase()) !== -1);
+ el.classList.toggle('ffz-game-spoilered', f.settings.spoiler_games.indexOf(game && game.toLowerCase()) !== -1);
+ }
+ });
+}
+
+
FFZ.prototype._modify_directory_host = function(dir) {
var f = this, mutator;
@@ -613,11 +750,17 @@ FFZ.prototype._modify_directory_host = function(dir) {
title = meta && meta.querySelector('.title a'),
target = this.get('stream.target.channel'),
+ game = this.get('stream.target.meta_game'),
hosts = this.get('stream.ffz_hosts'); //,
//boxart = thumb && thumb.querySelector('.boxart');
- el.setAttribute('data-channel', target.name);
+ el.classList.add('ffz-directory-preview');
+ el.setAttribute('data-channel', target.name);
+ el.setAttribute('data-game', game);
+
+ el.classList.toggle('ffz-game-banned', f.settings.banned_games.indexOf(game && game.toLowerCase()) !== -1);
+ el.classList.toggle('ffz-game-spoilered', f.settings.spoiler_games.indexOf(game && game.toLowerCase()) !== -1);
this._ffz_image_timer = setInterval(this.ffzRotateImage.bind(this), 30000);
this.ffzRotateImage();
diff --git a/src/ember/feed-card.js b/src/ember/feed-card.js
index a8bccaaf..11d2a157 100644
--- a/src/ember/feed-card.js
+++ b/src/ember/feed-card.js
@@ -21,10 +21,11 @@ var FFZ = window.FrankerFaceZ,
FFZ.prototype.setup_feed_cards = function() {
- var FeedCard = utils.ember_resolve('component:feed-card');
+ var FeedCard = utils.ember_resolve('component:channel-feed/card');
if ( ! FeedCard )
- return;
+ return this.error("Unable to locate component:channel-feed/card");
+ this.log("Modifying the feed-card component.");
this._modify_feed_card(FeedCard);
try { FeedCard.create().destroy();
@@ -35,7 +36,7 @@ FFZ.prototype.setup_feed_cards = function() {
FFZ.prototype.rerender_feed_cards = function(for_set) {
- var FeedCard = utils.ember_resolve('component:feed-card'),
+ var FeedCard = utils.ember_resolve('component:channel-feed/card'),
views = utils.ember_views();
if ( ! FeedCard )
@@ -49,7 +50,7 @@ FFZ.prototype.rerender_feed_cards = function(for_set) {
this._modify_feed_card(view);
view.ffzInit(for_set);
} catch(err) {
- this.error("setup component:feed-card ffzInit: " + err)
+ this.error("setup component:channel-feed/card ffzInit: " + err)
}
}
}
@@ -64,7 +65,7 @@ FFZ.prototype._modify_feed_card = function(component) {
try {
this.ffzInit();
} catch(err) {
- f.error("component:feed-card ffzInit: " + err);
+ f.error("component:channel-feed/card ffzInit: " + err);
}
},
@@ -73,7 +74,7 @@ FFZ.prototype._modify_feed_card = function(component) {
message = this.get('post.body'),
emotes = parse_emotes(this.get('post.emotes')),
user_id = this.get('post.user.login'),
- room_id = this.get('channelId'),
+ room_id = this.get('channelId') || user_id,
pbody = el && el.querySelector('.activity-body');
if ( ! message || ! el || ! pbody )
@@ -91,8 +92,8 @@ FFZ.prototype._modify_feed_card = function(component) {
pbody.innerHTML = '
' + output + '
';
- jQuery('.ffz-tooltip', pbody).tipsy({html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- jQuery('.html-tooltip', pbody).tipsy({html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.ffz-tooltip', pbody).tipsy({html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.html-tooltip', pbody).tipsy({html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
}
});
}
\ No newline at end of file
diff --git a/src/ember/following.js b/src/ember/following.js
index cbf30e6b..acee365e 100644
--- a/src/ember/following.js
+++ b/src/ember/following.js
@@ -1,6 +1,8 @@
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
- constants = require('../constants');
+ constants = require('../constants'),
+
+ createElement = utils.createElement;
// --------------------
@@ -13,7 +15,7 @@ FFZ.settings_info.enhance_profile_following = {
category: "Directory",
name: "Enhanced Following Control",
- help: "Display additional controls on your own profile's Following tab to make management easier."
+ help: "Display additional controls on your own profile's Following tab to make management easier, as well as telling you how long everyone has been following everyone else in the profile."
}
@@ -29,6 +31,7 @@ FFZ.prototype.setup_profile_following = function() {
// Build our is-following cache.
this._following_cache = {};
+ this._follower_cache = {};
// First, we need to hook the model. This is what we'll use to grab the following notification state,
// rather than making potentially hundreds of API requests.
@@ -36,12 +39,24 @@ FFZ.prototype.setup_profile_following = function() {
if ( Following )
this._hook_following(Following);
+ var Followers = utils.ember_resolve('model:user-followers');
+ if ( Followers )
+ this._hook_followers(Followers);
+
// Also try hooking that other model.
var Notification = utils.ember_resolve('model:notification');
if ( Notification )
this._hook_following(Notification, true);
+ // Find the followed item view
+ var FollowedItem = utils.ember_resolve('component:display-followed-item');
+ if ( ! FollowedItem )
+ return;
+
+ this._modify_display_followed_item(FollowedItem);
+
+
// Now, we need to edit the profile Following view itself.
var ProfileView = utils.ember_resolve('view:channel/following');
if ( ! ProfileView )
@@ -57,155 +72,171 @@ FFZ.prototype.setup_profile_following = function() {
}
},
+ ffzInit: function() {
+ // Only process our own profile following page.
+ if ( ! f.settings.enhance_profile_following )
+ return;
+
+ var el = this.get('element'),
+ user = f.get_user(),
+ user_id = this.get('context.model.id');
+
+ el.classList.add('ffz-enhanced-following');
+ el.classList.toggle('ffz-my-following', user && user.login === user_id);
+ el.setAttribute('data-user', user_id);
+ }
+ });
+
+ // TODO: Add nice Manage Following button to the directory.
+
+ // Now, rebuild any views.
+ try { FollowedItem.create().destroy();
+ } catch(err) { }
+
+ var views = utils.ember_views();
+
+ if ( views ) {
+ for(var key in views) {
+ var view = views[key];
+ if ( view instanceof FollowedItem ) {
+ this.log("Manually updating existing component:display-followed-item.", view);
+ try {
+ if ( ! view.ffzInit )
+ this._modify_display_followed_item(view);
+ view.ffzInit();
+ } catch(err) {
+ this.error("setup: component:display-followed-item ffzInit: " + err);
+ }
+ }
+ }
+ }
+
+
+ // Refresh all existing following data.
+ var count = 0,
+ Channel = utils.ember_resolve('model:channel');
+
+ if ( Channel && Channel._cache )
+ for(var key in Channel._cache) {
+ var chan = Channel._cache[key];
+ if ( chan instanceof Channel ) {
+ var following = chan.get('following'),
+ followers = chan.get('followers'),
+
+ refresher = function(x) {
+ if ( x.get('isLoading') )
+ setTimeout(refresher.bind(this,x), 25);
+
+ x.clear();
+ x.load();
+ };
+
+ // Make sure this channel's Following collection is modified.
+ this._hook_following(following);
+ this._hook_followers(followers);
+
+ var counted = false;
+ if ( following.get('isLoaded') || following.get('isLoading') ) {
+ refresher(following);
+ count++;
+ counted = true;
+ }
+
+ if ( followers.get('isLoaded') || followers.get('isLoading') ) {
+ refresher(followers);
+ if ( ! counted )
+ count++;
+ }
+ }
+ }
+
+ f.log("Refreshing previously loaded user following data for " + count + " channels.");
+}
+
+
+FFZ.prototype._modify_display_followed_item = function(component) {
+ var f = this;
+ component.reopen({
+ didInsertElement: function() {
+ this._super();
+ try {
+ this.ffzInit();
+ } catch(err) {
+ f.error("component:display-followed-item ffzInit: " + err);
+ }
+ },
+
willClearRender: function() {
try {
this.ffzTeardown();
} catch(err) {
- f.error("ProvileView ffzTeardown: " + err);
+ f.error("component:display-followed-item ffzTeardown: " + err);
}
- this._super();
},
ffzInit: function() {
- // Only process our own profile following page.
- var user = f.get_user();
- if ( ! f.settings.enhance_profile_following || ! user || user.login !== this.get('context.model.id') )
+ var el = this.get('element'),
+ channel_id = this.get('parentView.parentView.model.id'),
+ is_following = document.body.getAttribute('data-current-path').indexOf('.following') !== -1,
+
+ user = f.get_user(),
+ mine = user && user.login && user.login === channel_id,
+ big_cache = is_following ? f._following_cache : f._follower_cache;
+ user_cache = big_cache[channel_id] = big_cache[channel_id] || {},
+
+ user_id = this.get('followed.id'),
+ data = user_cache[user_id];
+
+ if ( ! f.settings.enhance_profile_following )
return;
- var el = this.get('element'),
- users = el && el.querySelectorAll('.user.item');
+ el.classList.add('ffz-processed');
- el.classList.add('ffz-enhanced-following');
+ // TODO: REMOVE
+ window._d = this;
- var had_data = true;
-
- if ( users && users.length )
- for(var i=0; i < users.length; i++)
- had_data = this.ffzProcessUser(users[i]) && had_data;
- else
- had_data = false;
-
- if ( ! had_data ) {
- // Force a refresh.
- f.log("Forcing a refresh of user following data.");
- var following = this.get('context.following'),
- refresher = function() {
- if ( following.get('isLoading') )
- setTimeout(refresher, 25);
-
- following.clear();
- following.load();
- }
-
- // Make sure the Following is modified.
- f._hook_following(following);
-
- // We use this weird function to prevent trying to load twice mucking things up.
- setTimeout(refresher);
- }
-
- // Watch for new ones the bad way.
- if ( ! this._ffz_observer ) {
- var t = this;
- var observer = this._ffz_observer = new MutationObserver(function(mutations) {
- for(var i=0; i < mutations.length; i++) {
- var mutation = mutations[i];
- if ( mutation.type !== "childList" )
- continue;
-
- for(var x=0; x < mutation.addedNodes.length; x++) {
- var added = mutation.addedNodes[x];
- if ( added.nodeType !== added.ELEMENT_NODE || added.tagName !== "DIV" )
- continue;
-
- // Is it an ember-view? Check its kids.
- if ( added.classList.contains('user') )
- t.ffzProcessUser(added);
-
- else if ( added.classList.contains('ember-view') ) {
- var users = added.querySelectorAll('.user.item');
- if ( users )
- for(var y=0; y < users.length; y++)
- t.ffzProcessUser(users[y]);
- }
- }
- }
- });
-
- observer.observe(el, {
- childList: true,
- subtree: true
- });
- }
- },
-
- ffzTeardown: function() {
- if ( this._ffz_observer ) {
- this._ffz_observer.disconnect();
- this._ffz_observer = null;
- }
- },
-
- ffzProcessUser: function(user) {
- if ( user.classList.contains('ffz-processed') )
- return true;
-
- var link = user.querySelector('a'),
- link_parts = link && link.href.split("/"),
- user_id = link_parts && link_parts[3],
- data = f._following_cache[user_id],
- t_el = document.createElement('div');
-
- user.classList.add('ffz-processed');
if ( ! data )
return false;
- t_el.className = 'overlay_info length';
- jQuery(t_el).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
-
var now = Date.now() - (f._ws_server_offset || 0),
- age = data[0] ? Math.floor((now - data[0].getTime()) / 1000) : 0;
- if ( age ) {
- t_el.innerHTML = constants.CLOCK + ' ' + utils.human_time(age, 10);
- t_el.setAttribute('original-title', 'Following Since: ' + data[0].toLocaleString() + '');
- } else
- t_el.style.display = 'none';
+ age = data[0] ? Math.floor((now - data[0].getTime()) / 1000) : 0,
+ t_el = createElement('div', 'overlay_info length'),
- user.appendChild(t_el);
+ update_time = function() {
+ var now = Date.now() - (f._ws_server_offset || 0),
+ age = data && data[0] ? Math.floor((now - data[0].getTime()) / 1000) : undefined;
- var actions = document.createElement('div'),
- follow = document.createElement('button'),
- notif = document.createElement('button'),
+ if ( age !== undefined ) {
+ t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
+ t_el.title = 'Following Since: ' + data[0].toLocaleString() + '';
+ t_el.style.display = '';
+ } else
+ t_el.style.display = 'none';
+ };
+
+ update_time();
+ jQuery(t_el).tipsy({html:true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 's')});
+ el.appendChild(t_el);
+
+ if ( ! mine || ! is_following )
+ return;
+
+ var actions = createElement('div', 'actions'),
+ follow = createElement('button', 'button follow'),
+ notif = createElement('button', 'button notifications'),
update_follow = function() {
- data = f._following_cache[user_id];
- user.classList.toggle('followed', data);
+ data = user_cache[user_id];
+ el.classList.toggle('followed', data);
follow.innerHTML = constants.HEART + constants.UNHEART + ' Follow';
-
- if ( t_el ) {
- var now = Date.now() - (f._ws_server_offset || 0),
- age = data && data[0] ? Math.floor((now - data[0].getTime()) / 1000) : undefined;
- if ( age !== undefined ) {
- t_el.innerHTML = constants.CLOCK + ' ' + (age < 60 ? 'now' : utils.human_time(age, 10));
- t_el.setAttribute('original-title', 'Following Since: ' + data[0].toLocaleString() + '');
- t_el.style.display = '';
- } else {
- t_el.style.display = 'none';
- }
- }
},
update_notif = function() {
- data = f._following_cache[user_id];
+ data = user_cache[user_id];
notif.classList.toggle('notifications-on', data && data[1]);
notif.textContent = 'Notification ' + (data && data[1] ? 'On' : 'Off');
};
- actions.className = 'actions';
-
- follow.className = 'button follow';
- notif.className = 'button notifications';
-
update_follow();
update_notif();
@@ -220,11 +251,12 @@ FFZ.prototype.setup_profile_following = function() {
utils.api.del("users/:login/follows/channels/" + user_id) :
utils.api.put("users/:login/follows/channels/" + user_id, {notifications: false}))
.done(function() {
- data = f._following_cache[user_id] = was_following ? null : [new Date(Date.now() - (f._ws_server_offset||0)), false];
+ data = user_cache[user_id] = was_following ? null : [new Date(Date.now() - (f._ws_server_offset||0)), false];
})
.always(function() {
update_follow();
update_notif();
+ update_time();
follow.disabled = false;
notif.disabled = false;
})
@@ -250,32 +282,14 @@ FFZ.prototype.setup_profile_following = function() {
actions.appendChild(follow);
actions.appendChild(notif);
- user.appendChild(actions);
- return true;
+ el.appendChild(actions);
+ },
+
+ ffzTeardown: function() {
+
}
});
-
- // TODO: Add nice Manage Following button to the directory.
-
- // Now, rebuild any views.
- try {
- ProfileView.create().destroy();
- } catch(err) { }
-
- var views = utils.ember_views();
- if ( views )
- for(var key in views) {
- var view = views[key];
- if ( view instanceof ProfileView ) {
- this.log("Manually updating existing Following View.", view);
- try {
- view.ffzInit();
- } catch(err) {
- this.error("setup: view:channel/following: " + err);
- }
- }
- }
}
@@ -287,12 +301,10 @@ FFZ.prototype._hook_following = function(Following) {
Following.reopen({
ffz_hooked: true,
apiLoad: function(e) {
- var user = f.get_user(),
- channel_id = this.get('id'),
+ var channel_id = this.get('id'),
t = this;
- if ( ! user || user.login !== channel_id )
- return this._super(e);
+ f._following_cache[channel_id] = f._following_cache[channel_id] || {};
return new RSVP.Promise(function(success, fail) {
t._super(e).then(function(data) {
@@ -307,7 +319,47 @@ FFZ.prototype._hook_following = function(Following) {
if ( follow.channel.display_name )
FFZ.capitalization[follow.channel.name] = [follow.channel.display_name, now];
- f._following_cache[follow.channel.name] = [follow.created_at ? utils.parse_date(follow.created_at) : null, follow.notifications || false];
+ f._following_cache[channel_id][follow.channel.name] = [follow.created_at ? utils.parse_date(follow.created_at) : null, follow.notifications || false];
+ }
+ }
+
+ success(data);
+
+ }, function(err) {
+ fail(err);
+ })
+ });
+ }
+ });
+}
+
+FFZ.prototype._hook_followers = function(Followers) {
+ var f = this;
+ if ( Followers.ffz_hooked )
+ return;
+
+ Followers.reopen({
+ ffz_hooked: true,
+ apiLoad: function(e) {
+ var channel_id = this.get('id'),
+ t = this;
+
+ f._follower_cache[channel_id] = f._follower_cache[channel_id] || {};
+
+ return new RSVP.Promise(function(success, fail) {
+ t._super(e).then(function(data) {
+ if ( data && data.follows ) {
+ var now = Date.now();
+ for(var i=0; i < data.follows.length; i++) {
+ var follow = data.follows[i];
+ if ( ! follow || ! follow.user || ! follow.user.name ) {
+ continue;
+ }
+
+ if ( follow.user.display_name )
+ FFZ.capitalization[follow.user.name] = [follow.user.display_name, now];
+
+ f._follower_cache[channel_id][follow.user.name] = [follow.created_at ? utils.parse_date(follow.created_at) : null, follow.notifications || false];
}
}
diff --git a/src/ember/layout.js b/src/ember/layout.js
index b6e169ee..8696d20f 100644
--- a/src/ember/layout.js
+++ b/src/ember/layout.js
@@ -145,7 +145,6 @@ FFZ.prototype.setup_layout = function() {
return;
document.body.classList.toggle("ffz-sidebar-swap", this.settings.swap_sidebars);
- document.body.classList.toggle("ffz-portrait", this.settings.portrait_mode);
this.log("Creating layout style element.");
var s = this._layout_style = document.createElement('style');
@@ -328,4 +327,5 @@ FFZ.prototype.setup_layout = function() {
// Force re-calculation of everything.
Ember.propertyDidChange(Layout, 'windowWidth');
Ember.propertyDidChange(Layout, 'windowHeight');
+ Layout.ffzUpdatePortraitCSS();
}
\ No newline at end of file
diff --git a/src/ember/line.js b/src/ember/line.js
index 9d08eaaf..dd006fd9 100644
--- a/src/ember/line.js
+++ b/src/ember/line.js
@@ -1024,6 +1024,7 @@ FFZ.prototype._modify_vod_line = function(component) {
FFZ.capitalization = {};
FFZ._cap_fetching = 0;
+FFZ._cap_waiting = {};
FFZ.get_capitalization = function(name, callback) {
if ( ! name )
@@ -1039,13 +1040,25 @@ FFZ.get_capitalization = function(name, callback) {
return old_data[0];
}
- if ( FFZ._cap_fetching < 25 ) {
+ if ( FFZ._cap_waiting[name] )
+ FFZ._cap_waiting[name].push(callback);
+
+ else if ( FFZ._cap_fetching < 25 ) {
FFZ._cap_fetching++;
- FFZ.get().ws_send("get_display_name", name, function(success, data) {
- var cap_name = success ? data : name;
+ FFZ._cap_waiting[name] = [callback];
+
+ FFZ.get().ws_send("get_display_name", name, function(success, data) {
+ var cap_name = success ? data : name,
+ waiting = FFZ._cap_waiting[name];
+
FFZ.capitalization[name] = [cap_name, Date.now()];
FFZ._cap_fetching--;
- typeof callback === "function" && callback(cap_name);
+ FFZ._cap_waiting[name] = false;
+
+ for(var i=0; i < waiting.length; i++)
+ try {
+ typeof waiting[i] === "function" && waiting[i](cap_name);
+ } catch(err) { }
});
}
diff --git a/src/ember/moderation-card.js b/src/ember/moderation-card.js
index e7f80d7c..d4f8e5a2 100644
--- a/src/ember/moderation-card.js
+++ b/src/ember/moderation-card.js
@@ -704,6 +704,13 @@ FFZ.prototype.setup_mod_card = function() {
}
+ // Follow Button
+ var follow_button = el.querySelector(".interface > .follow-button");
+ if ( follow_button )
+ jQuery(follow_button).tipsy({title: function() { return follow_button.classList.contains('is-following') ? "Unfollow" : "Follow"}});
+
+
+ // Whisper and Message Buttons
var msg_btn = el.querySelector(".interface > button.message-button");
if ( msg_btn ) {
msg_btn.innerHTML = 'W';
@@ -1082,8 +1089,8 @@ FFZ.prototype._build_mod_card_history = function(msg, modcard, show_from) {
jQuery('.deleted-word', l_el).click(function(e) { jQuery(this).trigger('mouseout'); this.outerHTML = this.getAttribute('data-text'); });
jQuery('a.deleted-link', l_el).click(f._deleted_link_click);
jQuery('img.emoticon', l_el).click(function(e) { f._click_emote(this, e) });
- jQuery('.html-tooltip', l_el).tipsy({html:true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
- jQuery('.ffz-tooltip', l_el).tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
+ //jQuery('.html-tooltip', l_el).tipsy({html:true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
+ //jQuery('.ffz-tooltip', l_el).tipsy({live: true, html: true, title: f.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
if ( modcard ) {
modcard.get('cardInfo.user.id') !== msg.from && jQuery('span.from', l_el).click(function(e) {
diff --git a/src/ember/player.js b/src/ember/player.js
index 9351ccea..93f4dd13 100644
--- a/src/ember/player.js
+++ b/src/ember/player.js
@@ -164,9 +164,9 @@ FFZ.prototype._modify_player = function(player) {
var stats = this.$('.player .js-playback-stats');
stats.draggable({cancel: 'li', containment: 'parent'});
- // Give the player time to do stuff before we change this
+ /*// Give the player time to do stuff before we change this
// text. It's a bit weird otherwise.
- setTimeout(function(){stats.children('.js-stats-toggle').html(constants.CLOSE);},500);
+ setTimeout(function(){stats.children('.js-stats-toggle').html(constants.CLOSE);},500);*/
// Only set up the stats hooks if we need stats.
diff --git a/src/ember/room.js b/src/ember/room.js
index 7da44031..7d979f60 100644
--- a/src/ember/room.js
+++ b/src/ember/room.js
@@ -4,6 +4,15 @@ var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
helpers,
+ STATUS_BADGES = [
+ ["r9k", "r9k", "This room is in R9K-mode."],
+ ["sub", "subsOnly", "This room is in subscribers-only mode."],
+ ["slow", "slow", function(room) { return "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow') || 120) + " seconds." }],
+ ["ban", "ffz_banned", "You have been banned from talking in this room."],
+ ["delay", function() { return this.settings.chat_delay !== 0 }, function() { return "You have enabled artificial chat delay. Messages are displayed after " + (this.settings.chat_delay/1000) + " seconds." }],
+ ["batch", function() { return this.settings.chat_batching !== 0 }, function() { return "You have enabled chat message batching. Messages are displayed in " + (this.settings.chat_batching/1000) + " second increments." }]
+ ],
+
// StrimBagZ Support
is_android = navigator.userAgent.indexOf('Android') !== -1,
@@ -210,152 +219,47 @@ FFZ.prototype._modify_rview = function(view) {
ffzUpdateStatus: function() {
var room = this.get('controller.model'),
-
el = this.get('element'),
cont = el && el.querySelector('.chat-buttons-container');
if ( ! cont )
return;
- var r9k_badge = cont.querySelector('#ffz-stat-r9k'),
- sub_badge = cont.querySelector('#ffz-stat-sub'),
- emote_badge = cont.querySelector('#ffz-stat-emote'),
- slow_badge = cont.querySelector('#ffz-stat-slow'),
- banned_badge = cont.querySelector('#ffz-stat-banned'),
- delay_badge = cont.querySelector('#ffz-stat-delay'),
- batch_badge = cont.querySelector('#ffz-stat-batch'),
- btn = cont.querySelector('button');
+ var btn = cont.querySelector('button');
if ( f.has_bttv || ! f.settings.room_status ) {
- if ( r9k_badge )
- r9k_badge.parentElement.removeChild(r9k_badge);
- if ( sub_badge )
- sub_badge.parentElement.removeChild(sub_badge);
- if ( emote_badge )
- emote_badge.parentElement.removeChild(emote_badge);
- if ( slow_badge )
- slow_badge.parentElement.removeChild(slow_badge);
- if ( delay_badge )
- delay_badge.parentElement.removeChild(delay_badge);
- if ( batch_badge )
- batch_badge.parentElement.removeChild(batch_badge);
+ jQuery(".ffz.room-state", cont).remove();
if ( btn )
btn.classList.remove('ffz-waiting');
return;
- }
- if ( ! r9k_badge ) {
- r9k_badge = document.createElement('span');
- r9k_badge.className = 'ffz room-state stat float-right';
- r9k_badge.id = 'ffz-stat-r9k';
- r9k_badge.innerHTML = 'R9K';
- r9k_badge.title = "This room is in R9K-mode.";
- cont.appendChild(r9k_badge);
- jQuery(r9k_badge).tipsy({gravity:"s", offset:15});
- }
-
- if ( ! sub_badge ) {
- sub_badge = document.createElement('span');
- sub_badge.className = 'ffz room-state stat float-right';
- sub_badge.id = 'ffz-stat-sub';
- sub_badge.innerHTML = 'SUB';
- sub_badge.title = "This room is in subscribers-only mode.";
- cont.appendChild(sub_badge);
- jQuery(sub_badge).tipsy({gravity:"s", offset:15});
- }
-
- if ( ! emote_badge ) {
- emote_badge = document.createElement('span');
- emote_badge.className = 'ffz room-state stat float-right';
- emote_badge.id = 'ffz-stat-emote';
- emote_badge.innerHTML = 'EMOTE';
- emote_badge.title = "This room is in Twitch emote-only mode. Emotes added by extensions are not permitted in this mode.";
- cont.appendChild(emote_badge);
- jQuery(emote_badge).tipsy({gravity: "s", offset: 15});
- }
-
- if ( ! slow_badge ) {
- slow_badge = document.createElement('span');
- slow_badge.className = 'ffz room-state stat float-right';
- slow_badge.id = 'ffz-stat-slow';
- slow_badge.innerHTML = 'SLOW';
- cont.appendChild(slow_badge);
- jQuery(slow_badge).tipsy({gravity:"s", offset:15});
- }
-
- if ( ! banned_badge ) {
- banned_badge = document.createElement('span');
- banned_badge.className = 'ffz room-state stat float-right';
- banned_badge.id = 'ffz-stat-banned';
- banned_badge.innerHTML = 'BAN';
- banned_badge.title = "You have been banned from talking in this room.";
- cont.appendChild(banned_badge);
- jQuery(banned_badge).tipsy({gravity:"s", offset:15});
- }
-
- if ( ! delay_badge ) {
- delay_badge = document.createElement('span');
- delay_badge.className = 'ffz room-state stat float-right';
- delay_badge.id = 'ffz-stat-delay';
- delay_badge.innerHTML = 'DELAY';
- cont.appendChild(delay_badge);
- jQuery(delay_badge).tipsy({gravity:"s", offset:15});
- }
-
- if ( ! batch_badge ) {
- batch_badge = document.createElement('span');
- batch_badge.className = 'ffz room-state stat float-right';
- batch_badge.id = 'ffz-stat-batch';
- batch_badge.innerHTML = 'BATCH';
- cont.appendChild(batch_badge);
- jQuery(batch_badge).tipsy({gravity:"s", offset:15});
- }
-
- var vis_count = 0,
- r9k_vis = room && room.get('r9k'),
- sub_vis = room && room.get('subsOnly'),
- emote_vis = room && room.get('emoteOnly') && room.get('emoteOnly') !== '0',
- ban_vis = room && room.get('ffz_banned'),
- slow_vis = room && room.get('slowMode'),
- delay_vis = f.settings.chat_delay !== 0,
- batch_vis = f.settings.chat_batching !== 0;
-
- if ( r9k_vis ) vis_count += 1;
- if ( sub_vis ) vis_count += 1;
- if ( emote_vis ) vis_count += 1;
- if ( ban_vis ) vis_count += 1;
- if ( slow_vis ) vis_count += 1;
- if ( delay_vis ) vis_count += 1;
- if ( batch_vis ) vis_count += 1;
-
- r9k_badge.classList.toggle('truncated', vis_count > 3);
- sub_badge.classList.toggle('truncated', vis_count > 3);
- emote_badge.classList.toggle('truncated', vis_count > 3);
- banned_badge.classList.toggle('truncated', vis_count > 3);
- slow_badge.classList.toggle('truncated', vis_count > 3);
- delay_badge.classList.toggle('truncated', vis_count > 3);
- batch_badge.classList.toggle('truncated', vis_count > 3);
-
- r9k_badge.classList.toggle('hidden', ! r9k_vis);
- sub_badge.classList.toggle('hidden', ! sub_vis);
- emote_badge.classList.toggle('hidden', ! emote_vis);
- banned_badge.classList.toggle('hidden', ! ban_vis);
-
- slow_badge.classList.toggle('hidden', ! slow_vis);
- slow_badge.title = "This room is in slow mode. You may send messages every " + utils.number_commas(room && room.get('slow')||120) + " seconds.";
-
- delay_badge.title = "You have enabled artificial chat delay. Messages are displayed after " + (f.settings.chat_delay/1000) + " seconds.";
- delay_badge.classList.toggle('hidden', ! delay_vis);
-
- batch_badge.title = "You have enabled chat message batching. Messages are displayed in " + (f.settings.chat_batching/1000) + " second increments.";
- batch_badge.classList.toggle('hidden', ! batch_vis);
-
- if ( btn ) {
+ } else if ( btn ) {
btn.classList.toggle('ffz-waiting', (room && room.get('slowWait') || 0));
btn.classList.toggle('ffz-banned', (room && room.get('ffz_banned')));
}
+ var badge, id, info, vis_count = 0;
+ for(var i=0; i < STATUS_BADGES.length; i++) {
+ info = STATUS_BADGES[i];
+ id = 'ffz-stat-' + info[0];
+ badge = cont.querySelector('#' + id);
+ visible = typeof info[1] === "function" ? info[1].call(f, room) : room && room.get(info[1]);
+ if ( ! badge ) {
+ badge = utils.createElement('span', 'ffz room-state stat float-right', info[0].charAt(0).toUpperCase() + '' + info[0].substr(1).toUpperCase() + '');
+ badge.id = id;
+ jQuery(badge).tipsy({gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
+ cont.appendChild(badge);
+ }
+
+ badge.title = typeof info[2] === "function" ? info[2].call(f, room) : info[2];
+ badge.classList.toggle('hidden', ! visible);
+ if ( visible )
+ vis_count++;
+ }
+
+ jQuery(".ffz.room-state", cont).toggleClass("truncated", vis_count > 3);
+
}.observes('controller.model'),
ffzEnableFreeze: function() {
@@ -677,7 +581,8 @@ FFZ.prototype.add_room = function(id, room) {
}
// Let the server know where we are.
- this.ws_send("sub", "room." + id);
+ room && room.ffzSubscribe && room.ffzSubscribe();
+ //this.ws_send("sub", "room." + id);
// See if we need history?
if ( ! this.has_bttv && this.settings.chat_history && room && (room.get('messages.length') || 0) < 10 ) {
@@ -983,7 +888,7 @@ FFZ.prototype._modify_room = function(room) {
room_id = this.get('id');
if ( (Chat && Chat.get('currentChannelRoom') === this) || (user && user.login === room_id) || (f._chatv && f._chatv._ffz_host === room_id) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1) )
- return;
+ return this.ffzUnsubscribe(true);
this.destroy();
},
@@ -993,6 +898,24 @@ FFZ.prototype._modify_room = function(room) {
f._roomv.ffzUpdateStatus();
}.observes('r9k', 'subsOnly', 'emoteOnly', 'slow', 'ffz_banned'),
+
+ ffzShouldSubscribe: function() {
+ var Chat = utils.ember_lookup('controller:chat'),
+ room_id = this.get('id');
+
+ return (Chat && Chat.get('currentChannelRoom') === this) || (f.settings.pinned_rooms && f.settings.pinned_rooms.indexOf(room_id) !== -1);
+ },
+
+ ffzSubscribe: function() {
+ if ( this.ffzShouldSubscribe() )
+ f.ws_send("sub", "room." + this.get('id'));
+ },
+
+ ffzUnsubscribe: function(not_always) {
+ if ( ! not_always || ! this.ffzShouldSubscribe() )
+ f.ws_send("unsub", "room." + this.get('id'));
+ },
+
// User Level
ffzUserLevel: function() {
if ( this.get('isStaff') )
@@ -1300,6 +1223,12 @@ FFZ.prototype._modify_room = function(room) {
if ( msg.color )
f._handle_color(msg.color);
+ // Message Filtering
+ var i = f._chat_filters.length;
+ while(i--)
+ if ( f._chat_filters[i](msg) === false )
+ return;
+
// Report this message to the dashboard.
if ( window !== window.parent && parent.postMessage && msg.from && msg.from !== "jtv" && msg.from !== "twitchnotify" )
parent.postMessage({from_ffz: true, command: 'chat_message', data: {from: msg.from, room: msg.room}}, location.protocol + "//www.twitch.tv/");
@@ -1308,6 +1237,10 @@ FFZ.prototype._modify_room = function(room) {
return this._super(msg);
},
+ ffzChatFilters: function(msg) {
+ var i = f._chat_filters.length;
+ },
+
setHostMode: function(e) {
this.set('ffz_host_target', e && e.hostTarget || null);
var user = f.get_user();
diff --git a/src/ember/vod-chat.js b/src/ember/vod-chat.js
index 2fd2016a..dbdceec1 100644
--- a/src/ember/vod-chat.js
+++ b/src/ember/vod-chat.js
@@ -154,7 +154,7 @@ FFZ.prototype._modify_vod_chat_display = function(component) {
if ( f.settings.chat_hover_pause )
this.ffzEnableFreeze();
- this.$('.chat-messages').find('.html-tooltip').tipsy({
+ /*this.$('.chat-messages').find('.html-tooltip').tipsy({
live: true, html: true,
gravity: utils.tooltip_placement(2 * constants.TOOLTIP_DISTANCE, function() {
return this.classList.contains('right') ? 'e' : 'n'
@@ -165,7 +165,7 @@ FFZ.prototype._modify_vod_chat_display = function(component) {
title: f.render_tooltip(),
gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, function() {
return this.classList.contains('right') ? 'e' : 'n'
- })});
+ })});*/
},
ffzTeardown: function() {
diff --git a/src/ext/api.js b/src/ext/api.js
index a0d38f94..88420426 100644
--- a/src/ext/api.js
+++ b/src/ext/api.js
@@ -50,6 +50,8 @@ var API = FFZ.API = function(instance, name, icon, version) {
this.global_sets = [];
this.default_sets = [];
+ this.users = {};
+ this.chat_filters = [];
this.on_room_callbacks = [];
this.name = name || ("Extension#" + this.id);
@@ -384,6 +386,76 @@ API.prototype.unregister_room_set = function(room_id, id) {
}
+// -----------------------
+// User Modifications
+// -----------------------
+
+API.prototype.user_add_set = function(user_name, set_id) {
+ var user = this.users[user_name] = this.users[user_name] || {},
+ ffz_user = this.ffz.users[user_name] = this.ffz.users[user_name] || {},
+
+ emote_sets = user.sets = user.sets || [],
+ ffz_sets = ffz_user.sets = ffz_user.sets || [],
+
+ exact_id = this.id + '-' + set_id;
+
+ if ( emote_sets.indexOf(exact_id) === -1 )
+ emote_sets.push(exact_id);
+
+ if ( ffz_sets.indexOf(exact_id) === -1 )
+ ffz_sets.push(exact_id);
+
+ // Update tab completion.
+ var user = this.ffz.get_user();
+ if ( this.ffz._inputv && user && user.login === user_name )
+ Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
+}
+
+
+API.prototype.user_remove_set = function(user_name, set_id) {
+ var user = this.users[user_name],
+ ffz_user = this.ffz.users[user_name],
+
+ emote_sets = user && user.sets,
+ ffz_sets = ffz_user && ffz_user.sets,
+
+ exact_id = this.id + '-' + set_id;
+
+ var ind = emote_sets ? emote_sets.indexOf(exact_id) : -1;
+ if ( ind !== -1 )
+ emote_sets.splice(ind, 1);
+
+ ind = ffz_sets ? ffz_sets.indexOf(exact_id) : -1;
+ if ( ind !== -1 )
+ ffz_sets.splice(ind, 1);
+
+ // Update tab completion.
+ var user = this.ffz.get_user();
+ if ( this.ffz._inputv && user && user.login === user_name )
+ Ember.propertyDidChange(this.ffz._inputv, 'ffz_emoticons');
+}
+
+
+// -----------------------
+// Chat Callback
+// -----------------------
+
+API.prototype.register_chat_filter = function(filter) {
+ this.chat_filters.push(filter);
+ this.ffz._chat_filters.push(filter);
+}
+
+API.prototype.unregister_chat_filter = function(filter) {
+ var ind = this.chat_filters.indexOf(filter);
+ if ( ind !== -1 )
+ this.chat_filters.splice(ind, 1);
+
+ ind = this.ffz._chat_filters.indexOf(filter);
+ if ( ind !== -1 )
+ this.ffz._chat_filters.splice(ind, 1);
+}
+
+
// -----------------------
// Channel Callbacks
// -----------------------
diff --git a/src/ext/rechat.js b/src/ext/rechat.js
index b470dee6..a153a56e 100644
--- a/src/ext/rechat.js
+++ b/src/ext/rechat.js
@@ -102,8 +102,8 @@ FFZ.prototype.find_rechat = function() {
// Tooltips
jQuery(container).find('.tooltip').tipsy({live: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'n')});
- jQuery(container).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- jQuery(container).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery(container).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery(container).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
// Load the room data.
var room_id = el.getAttribute('data-room');
diff --git a/src/main.js b/src/main.js
index 323cc7f0..5c2b5103 100644
--- a/src/main.js
+++ b/src/main.js
@@ -11,6 +11,7 @@ var FFZ = window.FrankerFaceZ = function() {
// Logging
this._log_data = [];
this._apis = {};
+ this._chat_filters = [];
// Error Logging
var t = this;
@@ -35,7 +36,7 @@ FFZ.msg_commands = {};
// Version
var VER = FFZ.version_info = {
- major: 3, minor: 5, revision: 159,
+ major: 3, minor: 5, revision: 169,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@@ -91,7 +92,7 @@ FFZ.prototype.paste_logs = function() {
FFZ.prototype._pastebin = function(data, callback) {
- jQuery.ajax({url: "http://putco.de/", type: "PUT", data: data, context: this})
+ jQuery.ajax({url: "https://putco.de/", type: "PUT", data: data, context: this})
.success(function(e) {
callback.call(this, e.trim() + ".log");
}).fail(function(e) {
diff --git a/src/settings.js b/src/settings.js
index 682ce70f..077ff8b1 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -413,8 +413,8 @@ var is_android = navigator.userAgent.indexOf('Android') !== -1,
el.appendChild(label);
el.appendChild(help);
menu.appendChild(el);
- jQuery('.html-tooltip', el).tipsy({html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
- jQuery('.ffz-tooltip', el).tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.html-tooltip', el).tipsy({html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery('.ffz-tooltip', el).tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
}
container.appendChild(menu);
diff --git a/src/socket.js b/src/socket.js
index 3644427a..5d46d04b 100644
--- a/src/socket.js
+++ b/src/socket.js
@@ -153,10 +153,12 @@ FFZ.prototype.ws_create = function() {
// Send the current rooms.
for(var room_id in f.rooms) {
- if ( ! f.rooms.hasOwnProperty(room_id) || ! f.rooms[room_id] )
+ var room = f.rooms[room_id];
+ if ( ! f.rooms.hasOwnProperty(room_id) || ! room )
continue;
- f.ws_send("sub", "room." + room_id);
+ room.room && room.room.ffzSubscribe && room.room.ffzSubscribe();
+ //f.ws_send("sub", "room." + room_id);
if ( f.rooms[room_id].needs_history ) {
f.rooms[room_id].needs_history = false;
diff --git a/src/tokenize.js b/src/tokenize.js
index d247e7be..ce84b58d 100644
--- a/src/tokenize.js
+++ b/src/tokenize.js
@@ -719,6 +719,11 @@ FFZ.prototype.render_token = function(render_links, warn_links, token) {
return '
';
}
+ else if ( token.type === "tag" ) {
+ var link = Twitch.uri.game("Creative") + "/" + token.tag;
+ return '' + utils.sanitize(token.text) + '';
+ }
+
else if ( token.type === "link" ) {
var text = token.title || (token.isLong && '') || (token.isDeleted && '') || (warn_links && '') || token.text;
@@ -757,8 +762,8 @@ FFZ.prototype.render_token = function(render_links, warn_links, token) {
href = '#';
}
- //return `${utils.sanitize(text)}`;
- return '' + utils.sanitize(text) + '';
+ //return `${utils.sanitize(text)}`;
+ return '' + utils.sanitize(text) + '';
}
else if ( token.type === "deleted" )
@@ -785,6 +790,58 @@ FFZ.prototype.render_tokens = function(tokens, render_links, warn_links) {
}
+// ---------------------
+// Creative Tags
+// ---------------------
+
+FFZ.prototype.tokenize_ctags = function(tokens) {
+ "use strict";
+
+ if ( typeof tokens === "string" )
+ tokens = [tokens];
+
+ var banned_tags = window.SiteOptions && SiteOptions.creative_banned_tags && SiteOptions.creative_banned_tags.split(',') || [],
+ new_tokens = [];
+
+ for(var i=0, l = tokens.length; i < l; i++) {
+ var token = tokens[i];
+ if ( ! token )
+ continue;
+
+ if ( typeof token !== "string" )
+ if ( token.type === "text" )
+ token = token.text;
+ else {
+ new_tokens.push(token);
+ continue;
+ }
+
+ var segments = token.split(' '),
+ text = [], segment, tag;
+
+ for(var x=0,y=segments.length; x < y; x++) {
+ segment = segments[x];
+ tag = segment.substr(1).toLowerCase();
+ if ( segment.charAt(0) === '#' && banned_tags.indexOf(tag) === -1 ) {
+ if ( text.length ) {
+ new_tokens.push({type: "text", text: text.join(' ') + ' '});
+ text = [];
+ }
+
+ new_tokens.push({type: "tag", text: segment, tag: tag});
+ text.push('');
+ } else
+ text.push(segment);
+ }
+
+ if ( text.length > 1 || (text.length === 1 && text[0] !== '') )
+ new_tokens.push({type: "text", text: text.join(' ')});
+ }
+
+ return new_tokens;
+}
+
+
// ---------------------
// Emoticon Processing
// ---------------------
diff --git a/src/ui/about_page.js b/src/ui/about_page.js
index b3b298ce..72d00ff7 100644
--- a/src/ui/about_page.js
+++ b/src/ui/about_page.js
@@ -79,6 +79,41 @@ var include_html = function(heading_text, filename) {
render_news = include_html("news", constants.SERVER + "script/news.html");
+var make_line = function(key, container) {
+ var desc = NICE_DESCRIPTION.hasOwnProperty(key) ? NICE_DESCRIPTION[key] : key;
+ if ( ! desc )
+ return;
+
+ line = createElement('li', null, desc + '');
+ line.setAttribute('data-property', key);
+ container.appendChild(line);
+ return line;
+};
+
+var update_mem_stats = function(container) {
+ if ( ! document.querySelector('.ffz-ui-sub-menu-page[data-page="debugging"]') )
+ return;
+
+ setTimeout(update_mem_stats.bind(this, container), 1000);
+
+ var mem = window.performance && performance.memory;
+ if ( ! mem )
+ return;
+
+ var sorted_keys = ['jsHeapSizeLimit', 'totalJSHeapSize', 'usedJSHeapSize'];
+ for(var i=0; i < sorted_keys.length; i++) {
+ var key = sorted_keys[i],
+ data = mem[key],
+ line = container.querySelector('li[data-property="' + key + '"]');
+
+ if ( ! line )
+ line = make_line(key, container);
+
+ if ( line )
+ line.querySelector('span').textContent = utils.format_size(data) + ' (' + data + ')';
+ }
+};
+
var update_player_stats = function(player, container) {
if ( ! document.querySelector('.ffz-ui-sub-menu-page[data-page="debugging"]') || ! player.getVideoInfo )
return;
@@ -100,17 +135,11 @@ var update_player_stats = function(player, container) {
data = player_data[key],
line = container.querySelector('li[data-property="' + key + '"]');
- if ( ! line ) {
- var desc = NICE_DESCRIPTION.hasOwnProperty(key) ? NICE_DESCRIPTION[key] : key;
- if ( ! desc )
- continue;
+ if ( ! line )
+ line = make_line(key, container);
- line = createElement('li', null, desc + '');
- line.setAttribute('data-property', key);
- container.appendChild(line);
- }
-
- line.querySelector('span').textContent = data;
+ if ( line )
+ line.querySelector('span').textContent = data;
}
};
@@ -270,6 +299,10 @@ FFZ.menu_pages.about = {
['Deploy Flavor', SiteOptions.deploy_flavor]
],
+ has_memory = window.performance && performance.memory,
+ mem_head = createElement('div'),
+ mem_list = createElement('ul'),
+
player_head = createElement('div'),
player_list = createElement('ul'),
@@ -279,6 +312,7 @@ FFZ.menu_pages.about = {
vers = createElement('ul'),
version_list = [
['Ember', Ember.VERSION],
+ ['Ember Data', window.DS && DS.VERSION || 'unknown'],
['GIT Version', EmberENV.GIT_VERSION],
null,
['FrankerFaceZ', FFZ.version_info.toString()]
@@ -302,9 +336,8 @@ FFZ.menu_pages.about = {
heading.className = 'chat-menu-content center';
heading.innerHTML = 'FrankerFaceZ
woofs for nerds
';
- info_head.className = twitch_head.className = player_head.className = ver_head.className = log_head.className = 'list-header';
- info.className = twitch.className = player_list.className = vers.className = 'chat-menu-content menu-side-padding version-list';
-
+ info_head.className = mem_head.className = twitch_head.className = player_head.className = ver_head.className = log_head.className = 'list-header';
+ info.className = mem_list.className = twitch.className = player_list.className = vers.className = 'chat-menu-content menu-side-padding version-list';
info_head.innerHTML = 'Client Status';
@@ -351,13 +384,6 @@ FFZ.menu_pages.about = {
twitch.appendChild(line);
}
-
- if ( player_data ) {
- player_head.innerHTML = "Player Statistics";
- update_player_stats(player, player_list);
- }
-
-
ver_head.innerHTML = 'Versions';
if ( this.has_bttv )
@@ -395,7 +421,18 @@ FFZ.menu_pages.about = {
container.appendChild(twitch_head);
container.appendChild(twitch);
+ if ( has_memory ) {
+ mem_head.innerHTML = 'Memory Statistics';
+ setTimeout(update_mem_stats.bind(this,mem_list),0);
+
+ container.appendChild(mem_head);
+ container.appendChild(mem_list);
+ }
+
if ( player_data ) {
+ player_head.innerHTML = "Player Statistics";
+ setTimeout(update_player_stats.bind(this,player,player_list),0);
+
container.appendChild(player_head);
container.appendChild(player_list);
}
diff --git a/src/ui/following-count.js b/src/ui/following-count.js
index 50bb2ef8..75ba6c65 100644
--- a/src/ui/following-count.js
+++ b/src/ui/following-count.js
@@ -2,12 +2,29 @@ var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
constants = require('../constants'),
+ FOLLOWING_CONTAINERS = [
+ ['#small_nav ul.game_filters li[data-name="following"] a', true],
+ ['nav a.warp__tipsy[data-href="following"]', true],
+ ['#large_nav #nav_personal li[data-name="following"] a', false],
+ ['#header_actions #header_following', false]
+ ],
+
FOLLOW_GRAVITY = function(f, el) {
- return (f.settings.following_count && el.parentElement.getAttribute('data-name') === 'following' ? 'n' : '') + (f.settings.swap_sidebars ? 'e' : 'w');
+ return (f.settings.following_count && (
+ el.getAttribute('data-href') === 'following' ||
+ el.parentElement.getAttribute('data-name') === 'following'
+ ) ? 'n' : '') +
+
+ (f.settings.swap_sidebars ? 'e' : 'w');
},
WIDE_TIP = function(f, el) {
- return ( ! f.settings.following_count || (el.id !== 'header_following' && el.parentElement.getAttribute('data-name') !== 'following') ) ? '' : 'ffz-wide-tip';
+ return (f.settings.following_count && (
+ el.id === 'header_following' ||
+ el.getAttribute('data-href') === 'following' ||
+ el.parentElement.getAttribute('data-name') === 'following'
+
+ )) ? 'ffz-wide-tip' : '';
};
@@ -49,6 +66,7 @@ FFZ.prototype.setup_following_count = function(has_ember) {
// Tooltips~!
this._install_following_tooltips();
+ setTimeout(this._install_following_tooltips.bind(this), 2000);
// If we don't have Ember, no point in trying this stuff.
if ( ! has_ember )
@@ -76,13 +94,18 @@ FFZ.prototype.setup_following_count = function(has_ember) {
if ( HostLive )
HostLive.load();*/
- var total = Live.get('total'),
- streams = Live.get('content');
- if ( typeof total === "number" ) {
- this._draw_following_count(total);
- if ( streams && streams.length )
- this._draw_following_channels(streams, total);
+ var init = function() {
+ var total = Live.get('total'),
+ streams = Live.get('content');
+ if ( typeof total === "number" ) {
+ f._draw_following_count(total);
+ if ( streams && streams.length )
+ f._draw_following_channels(streams, total);
+ }
}
+
+ init()
+ setTimeout(init, 2000);
}
@@ -161,7 +184,7 @@ FFZ.prototype._update_following_count = function() {
FFZ.prototype._build_following_tooltip = function(el) {
- if ( el.id !== 'header_following' && el.parentElement.getAttribute('data-name') !== 'following' )
+ if ( el.id !== 'header_following' && el.getAttribute('data-href') !== 'following' && el.parentElement.getAttribute('data-name') !== 'following' )
return el.getAttribute('original-title');
if ( ! this.settings.following_count )
@@ -262,43 +285,24 @@ FFZ.prototype._build_following_tooltip = function(el) {
FFZ.prototype._install_following_tooltips = function() {
var f = this,
+ gravity = function() { return FOLLOW_GRAVITY(f, this) },
data = {
html: true,
className: function() { return WIDE_TIP(f, this); },
title: function() { return f._build_following_tooltip(this); }
};
- // Small
- var small_following = jQuery('#small_nav ul.game_filters li[data-name="following"] a');
- if ( small_following && small_following.length ) {
- var td = small_following.data('tipsy');
- if ( td && td.options ) {
- td.options = _.extend(td.options, data);
- td.options.gravity = function() { return FOLLOW_GRAVITY(f, this); };
- } else
- small_following.tipsy(_.extend({gravity: function() { return FOLLOW_GRAVITY(f, this); }}, data));
- }
-
-
- // Large
- var large_following = jQuery('#large_nav #nav_personal li[data-name="following"] a');
- if ( large_following && large_following.length ) {
- var td = large_following.data('tipsy');
- if ( td && td.options )
- td.options = _.extend(td.options, data);
- else
- large_following.tipsy(data);
- }
-
-
- // Heading
- var head_following = jQuery('#header_actions #header_following');
- if ( head_following && head_following.length ) {
- var td = head_following.data('tipsy');
- if ( td && td.options )
- td.options = _.extend(td.options, data);
- else
- head_following.tipsy(data);
+ for(var i=0; i < FOLLOWING_CONTAINERS.length; i++) {
+ var following = jQuery(FOLLOWING_CONTAINERS[i][0]);
+ if ( following && following.length ) {
+ var td = following.data('tipsy');
+ if ( td && td.options ) {
+ td.options = _.extend(td.options, data);
+ if ( FOLLOWING_CONTAINERS[i][1] )
+ td.options.gravity = gravity;
+ } else
+ following.tipsy(FOLLOWING_CONTAINERS[i][1] ? _.extend({gravity: gravity}, data) : data);
+ }
}
}
@@ -310,55 +314,22 @@ FFZ.prototype._draw_following_channels = function(streams, total) {
FFZ.prototype._draw_following_count = function(count) {
- // Small
- var small_following = document.querySelector('#small_nav ul.game_filters li[data-name="following"] a');
- if ( small_following ) {
- var badge = small_following.querySelector('.ffz-follow-count');
- if ( this.has_bttv || ! this.settings.following_count ) {
- if ( badge )
- badge.parentElement.removeChild(badge);
- } else {
- if ( ! badge ) {
- badge = document.createElement('span');
- badge.className = 'ffz-follow-count';
- small_following.appendChild(badge);
- }
- badge.innerHTML = count ? utils.format_unread(count) : '';
- }
- }
+ count = count ? utils.format_unread(count) : '';
+ for(var i=0; i < FOLLOWING_CONTAINERS.length; i++) {
+ var container = document.querySelector(FOLLOWING_CONTAINERS[i][0]),
+ badge = container && container.querySelector('.ffz-follow-count');
+ if ( ! container )
+ continue;
-
- // Large
- var large_following = document.querySelector('#large_nav #nav_personal li[data-name="following"] a');
- if ( large_following ) {
- var badge = large_following.querySelector('.ffz-follow-count');
if ( this.has_bttv || ! this.settings.following_count ) {
- if ( badge )
- badge.parentElement.removeChild(badge);
- } else {
- if ( ! badge ) {
- badge = document.createElement('span');
- badge.className = 'ffz-follow-count';
- large_following.appendChild(badge);
- }
- badge.innerHTML = count ? utils.format_unread(count) : '';
- }
- }
+ container.removeChild(badge);
+ continue;
- // Heading
- var head_following = document.querySelector('#header_actions #header_following');
- if ( head_following ) {
- var badge = head_following.querySelector('.ffz-follow-count');
- if ( this.has_bttv || ! this.settings.following_count ) {
- if ( badge )
- badge.parentElement.removeChild(badge);
- } else {
- if ( ! badge ) {
- badge = document.createElement('span');
- badge.className = 'ffz-follow-count';
- head_following.appendChild(badge);
- }
- badge.innerHTML = count ? utils.format_unread(count) : '';
+ } else if ( ! badge ) {
+ badge = utils.createElement('span', 'ffz-follow-count');
+ container.appendChild(badge);
}
+
+ badge.innerHTML = count;
}
}
\ No newline at end of file
diff --git a/src/ui/menu.js b/src/ui/menu.js
index 7b563d88..ada92a6d 100644
--- a/src/ui/menu.js
+++ b/src/ui/menu.js
@@ -2,6 +2,8 @@ var FFZ = window.FrankerFaceZ,
constants = require('../constants'),
utils = require('../utils'),
+ IS_OSX = constants.IS_OSX,
+
reported_sets = [],
fix_menu_position = function(container) {
@@ -218,8 +220,8 @@ FFZ.prototype.build_ui_popup = function(view) {
container.classList.toggle('dark', dark);
// Stuff
- jQuery(inner).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
- jQuery(inner).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ //jQuery(inner).find('.html-tooltip').tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 's')});
+ //jQuery(inner).find('.ffz-tooltip').tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
// Menu Container
var sub_container = document.createElement('div');
@@ -761,7 +763,7 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
if ( api && api.emote_url_generator )
url = api.emote_url_generator(set.source_id, id);
} else
- url = "https://www.frankerfacez.com/emoticons/" + id;
+ url = "https://www.frankerfacez.com/emoticon/" + id;
if ( url )
window.open(url);
} else
@@ -781,7 +783,7 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
FFZ.prototype._add_emote = function(view, emote, favorites_set, favorites_key, event) {
- if ( event && event.ctrlKey ) {
+ if ( event && ((!IS_OSX && event.ctrlKey) || (IS_OSX && event.metaKey)) ) {
var el = event.target;
if ( ! el.classList.contains('locked') && el.classList.contains('ffz-can-favorite') && favorites_set && favorites_key ) {
var favs = this.settings.favorite_emotes[favorites_set] = this.settings.favorite_emotes[favorites_set] || [],
diff --git a/src/ui/my_emotes.js b/src/ui/my_emotes.js
index 80cec9c2..b25d8871 100644
--- a/src/ui/my_emotes.js
+++ b/src/ui/my_emotes.js
@@ -129,7 +129,7 @@ FFZ.menu_pages.myemotes = {
var el = document.createElement("div");
el.className = "emoticon-grid ffz-no-emotes center";
- el.innerHTML = "You have no favorite emoticons.

To make an emote a favorite, find it on the All Emoticons tab and Ctrl-Click it.";
+ el.innerHTML = "You have no favorite emoticons.

To make an emote a favorite, find it on the All Emoticons tab and " + (constants.IS_OSX ? '⌘' : 'Ctrl') + "-Click it.";
container.appendChild(el);
}
},
diff --git a/src/ui/tooltips.js b/src/ui/tooltips.js
index 8ea7f616..8028e860 100644
--- a/src/ui/tooltips.js
+++ b/src/ui/tooltips.js
@@ -8,6 +8,11 @@ var FFZ = window.FrankerFaceZ,
// ---------------------
FFZ.prototype.fix_tooltips = function() {
+ // Add handlers to FFZ's tooltip classes.
+ jQuery(".html-tooltip").tipsy({live: true, html: true, gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+ jQuery(".ffz-tooltip").tipsy({live: true, html: true, title: this.render_tooltip(), gravity: utils.tooltip_placement(2*constants.TOOLTIP_DISTANCE, 'n')});
+
+
// First, override the tooltip mixin.
var TipsyTooltip = utils.ember_resolve('component:tipsy-tooltip');
if ( TipsyTooltip ) {
diff --git a/src/utils.js b/src/utils.js
index 0f9af5da..31e0e0e1 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -467,6 +467,19 @@ module.exports = FFZ.utils = {
return "" + count;
},
+ format_size: function(bits) {
+ if(Math.abs(bits) < 1024)
+ return bits + ' b';
+
+ var units = ['Kb','Mb','Gb','Tb','Pb','Eb','Zb','Yb'],
+ u = -1;
+ do {
+ bits /= 1024;
+ ++u;
+ } while(Math.abs(bits) >= 1024 && u < units.length - 1);
+ return bits.toFixed(1) + ' ' + units[u];
+ },
+
escape_regex: escape_regex,
createElement: function(tag, className, content) {
diff --git a/style.css b/style.css
index 49f85790..7ac74f16 100644
--- a/style.css
+++ b/style.css
@@ -19,6 +19,7 @@ body > div.tipsy .tipsy-arrow { opacity: 0.8; }
cursor: pointer;
}
+.ffz-hide-recommended-friends .recommended-friends,
.ffz-hide-recommended-channels .js-recommended-channels,
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
.ffz-hide-view-count .stat.twitch-channel-views,
@@ -1235,6 +1236,24 @@ img.channel_background[src="null"] { display: none; }
padding: 0 5px;
}
+.ember-chat .ffz-moderation-card .mod-controls button {
+ width: auto;
+ margin-right: 10px;
+}
+
+.ffz-moderation-card .follow-button,
+.ffz-moderation-card .friend-button { max-height: 30px }
+
+.ffz-moderation-card .right button:last-of-type,
+.ffz-moderation-card .mod-controls button:last-of-type { margin-right: 0 }
+
+.ffz-moderation-card .follow-button a {
+ text-indent: -9999px;
+ padding-right: 0 !important;
+}
+
+.ffz-moderation-card .button.glyph-only { padding: 0 !important }
+
.ffz-moderation-card button:not(.glyph-only):hover,
.ffz-moderation-card button:not(.glyph-only):focus {
color: #fff;
@@ -1242,7 +1261,7 @@ img.channel_background[src="null"] { display: none; }
}
.ffz-moderation-card button.message {
- height: 30px; width: 28px;
+ height: 30px;
}
.ffz-moderation-card.ffz-is-mod .interface .mod-controls:last-of-type,
@@ -1994,8 +2013,8 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
}
.ffz-sidebar-swap #left_close {
- right: auto;
- left: -25px;
+ right: 5px;
+ left: auto;
}
.ffz-sidebar-swap #main_col {
@@ -2226,11 +2245,13 @@ li[data-name="following"] a {
background-color: rgba(25,25,25,0.5);
}
+#left_col.open .warp__item .ffz-follow-count,
#large_nav .ffz-follow-count,
.ffz-dark #header_following .ffz-follow-count {
background-color: rgba(127,127,127,0.5);
}
+#left_col.open .warp__item .ffz-follow-count,
#large_nav .ffz-follow-count {
position: absolute;
right: 10px;
@@ -2240,6 +2261,12 @@ li[data-name="following"] a {
padding: 2px 5px;
}
+#left_col.open .warp__item .ffz-follow-count {
+ top: 6px;
+ right: 20px;
+}
+
+#left_col.closed .warp__item .ffz-follow-count,
#small_nav .ffz-follow-count {
position: absolute;
bottom: 2px;
@@ -2644,41 +2671,13 @@ body:not(.ffz-top-conversations) .conversations-list-bottom-bar {
/* Creative Directory */
-.ffz-creative-tags .creativetag-list.filter-list,
-.ffz-creative-tags .creativetag-list ul,
-.ffz-creative-tags .creativetag-list-contain {
- width: inherit;
- height: inherit;
- overflow: inherit;
-}
+.ffz-top-conversations .ct-banner { margin-top: -50px }
+.ffz-top-conversations .ct-banner__feature { top: 40px; margin-bottom: 0 }
-body:not(.ffz-creative-showcase) .creative-hero,
-.ffz-creative-tags .creativetag-list-contain .filter-nav { display: none; }
+body:not(.ffz-tags-on-channel) #broadcast-meta .ct-tags,
+body:not(.ffz-creative-showcase) .ct-gallery { display: none; }
-.ffz-creative-tags .creativetag-list ul {
- display: flex;
- flex-wrap: wrap;
- position: inherit;
- margin-bottom: -5px;
-}
-
-.ffz-creative-tags .creativetag-list li {
- flex-grow: 1;
- height: inherit;
- float: none;
- margin: 0 0 5px;
-}
-
-.ffz-creative-tags .creativetag-list a {
- display: block;
- text-align: center;
- padding: 5px 10px;
-}
-
-.ffz-creative-tags .creativetag-list a.active {
- color: #fff !important;
- background-color: #6441a5 !important;
-}
+body:not(.ffz-tags-on-channel) .tw-title--tall { height: 60px }
/* Content-Box~! Down with Twitch randomly changing the box-sizing for everything! */
@@ -2732,8 +2731,21 @@ body:not(.ffz-creative-showcase) .creative-hero,
line-height: 40px;
}
+/* Banned and Spoiler Games */
+
+.ffz-game-spoilered .thumb .cap img,
+.ffz-game-banned { display: none }
+.ffz-game-spoilered .thumb .cap {
+ position: absolute !important; top: 0; left: 0; bottom: 0; right: 0;
+ background: url("https://static-cdn.jtvnw.net/ttv-static/404_preview-320x180.jpg") no-repeat !important;
+ background-size: cover !important;
+}
+
+
/* Dashboard Channel Feed */
+.activity-card .emoticon-selector .tabs { display: none }
+
.ffz-feed-button span { line-height: 30px; }
#ffz-feed-tabs .tabs { margin-bottom: 10px }
#ffz-feed-tabs textarea { resize: vertical }
@@ -2747,4 +2759,8 @@ body:not(.ffz-creative-showcase) .creative-hero,
body.ffz-bttv #ffz-feed-tabs .tabs li:not(.selected):not(:hover) a { color: #6441a5; }
body.ffz-bttv-dark #ffz-feed-tabs .tabs li:not(.selected):not(:hover) a { color: #999; }
-body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
\ No newline at end of file
+body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
+
+/* New Sidebar */
+
+.warp__anchor { height: 5.5rem }
\ No newline at end of file