diff --git a/dark.css b/dark.css
index 23f049d9..ecaa814b 100644
--- a/dark.css
+++ b/dark.css
@@ -46,7 +46,7 @@
/* hover icon for chats menu */
.ffz-dark .ember-chat .chat-menu-button-container:hover svg path{
fill:#fff!important;
-}
+}
/* dropdown arrows */
@@ -161,6 +161,7 @@
.ffz-dark .ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
.ffz-dark .card,
.ffz-dark #flyout .content,
+.ffz-dark .whatisthis,
.ffz-dark .ui-menu,
.ffz-dark .dropmenu,
.ffz-dark .top-dropdown,
@@ -782,10 +783,17 @@
}
.ffz-dark .whatisthis {
- background-color: #333;
box-shadow: 0 0 0 1px rgba(255,255,255,0.2);
}
+.ffz-dark .whatisthis:before {
+ border-top-color: #101010;
+}
+
+.ffz-dark .whatisthis:after {
+ border-top-color: #32323e;
+}
+
.ffz-dark #chart_container svg rect[fill="#FFFFFF"] {
fill: rgb(32,32,32) !important;
}
@@ -843,6 +851,7 @@
background-color: transparent;
}
+.ffz-dark .whatisthis .actions .divider,
.ffz-dark .dash-chat-column {
border-color: rgba(255,255,255,0.2);
}
@@ -894,4 +903,20 @@
.ffz-dark li.video.vods img.video_type {
filter: invert(100%);
-webkit-filter: invert(100%);
+}
+
+/* Playlist */
+
+.ffz-dark .ember-chat .chat-header {
+ box-shadow: inset 0 -1px 0 0 rgba(255,255,255,0.2);
+}
+
+.ffz-dark .playlist-controller,
+.ffz-dark .playlist-item {
+ border-color: rgba(255,255,255,0.2);
+}
+
+.ffz-dark .playlist-container:not(.playlist-enabled) .playlist-item:hover,
+.ffz-dark .playlist-container:not(.playlist-enabled) .ui-sortable-helper {
+ background-color: rgba(255,255,255,0.2);
}
\ No newline at end of file
diff --git a/image-proxy.html b/image-proxy.html
deleted file mode 100644
index a0af78c8..00000000
--- a/image-proxy.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/src/badges.js b/src/badges.js
index f418153c..8277fb01 100644
--- a/src/badges.js
+++ b/src/badges.js
@@ -431,7 +431,7 @@ FFZ.prototype._legacy_add_donors = function() {
}
FFZ.prototype._legacy_load_bots = function(tries) {
- jQuery.ajax(constants.SERVER + "script/bots.txt", {cache: false, context: this})
+ jQuery.ajax(constants.SERVER + "script/bots.txt", {context: this})
.done(function(data) {
this._legacy_parse_badges(data, 0, 2, "Bot (By: {})");
@@ -446,7 +446,7 @@ FFZ.prototype._legacy_load_bots = function(tries) {
}
FFZ.prototype._legacy_load_donors = function(tries) {
- jQuery.ajax(constants.SERVER + "script/donors.txt", {cache: false, context: this})
+ jQuery.ajax(constants.SERVER + "script/donors.txt", {context: this})
.done(function(data) {
this._legacy_parse_badges(data, 1, 1);
diff --git a/src/ember/channel.js b/src/ember/channel.js
index f6197b24..ea154da4 100644
--- a/src/ember/channel.js
+++ b/src/ember/channel.js
@@ -361,9 +361,9 @@ FFZ.prototype._modify_cindex = function(view) {
controller.set('ffz_host_updating', true);
if ( now_hosting === target )
- room.send("/unhost");
+ room.send("/unhost", true);
else
- room.send("/host " + target);
+ room.send("/host " + target, true);
},
@@ -480,8 +480,25 @@ FFZ.prototype._modify_cindex = function(view) {
container.appendChild(stat_el);
}
- stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
- el.textContent = stats.hlsLatencyBroadcaster + 's';
+ var delay = parseFloat(stats.hlsLatencyBroadcaster);
+
+ if ( delay > 180 ) {
+ delay = Math.floor(delay);
+ stat_el.setAttribute('original-title', 'Video Information\nBroadcast ' + utils.time_to_string(delay, true) + ' Ago\n\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps')
+ el.textContent = utils.time_to_string(Math.floor(delay), true, delay > 172800) + ' old';
+ } else {
+ stat_el.setAttribute('original-title', 'Stream Latency\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps');
+
+ delay = stats.hlsLatencyBroadcaster;
+ var pos = delay.lastIndexOf('.');
+
+ if ( pos === -1 )
+ delay = delay + '.00';
+ else if ( delay.length - pos < 3 )
+ delay = delay + '0';
+
+ el.textContent = delay + 's';
+ }
}
}
@@ -516,8 +533,25 @@ FFZ.prototype._modify_cindex = function(view) {
container.appendChild(stat_el);
}
- stat_el.title = 'Stream Latency\nFPS: ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps';
- el.textContent = stats.hlsLatencyBroadcaster + 's';
+ var delay = parseFloat(stats.hlsLatencyBroadcaster);
+
+ if ( delay > 180 ) {
+ delay = Math.floor(delay);
+ stat_el.setAttribute('original-title', 'Video Information\nBroadcast ' + utils.time_to_string(delay, true) + ' Ago\n\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps')
+ el.textContent = utils.time_to_string(Math.floor(delay), true, delay > 172800) + ' old';
+ } else {
+ stat_el.setAttribute('original-title', 'Stream Latency\nVideo: ' + stats.videoResolution + 'p @ ' + stats.fps + '\nPlayback Rate: ' + stats.playbackRate + ' Kbps');
+
+ delay = stats.hlsLatencyBroadcaster;
+ var pos = delay.lastIndexOf('.');
+
+ if ( pos === -1 )
+ delay = delay + '.00';
+ else if ( delay.length - pos < 3 )
+ delay = delay + '0';
+
+ el.textContent = delay + 's';
+ }
}
}
},
@@ -581,7 +615,7 @@ FFZ.prototype._modify_cindex = function(view) {
jQuery(stat).tipsy({html: true});
}
- el.innerHTML = utils.time_to_string(uptime);
+ el.innerHTML = utils.time_to_string(uptime, false, false, false, f.settings.stream_uptime === 1 || f.settings.stream_uptime === 3);
},
ffzTeardown: function() {
@@ -698,10 +732,27 @@ FFZ.settings_info.stream_host_button = {
FFZ.settings_info.stream_uptime = {
- type: "boolean",
- value: false,
- no_mobile: true,
+ type: "select",
+ options: {
+ 0: "Disabled",
+ 1: "Enabled",
+ 2: "Enabled (with Seconds)",
+ 3: "Enabled (Channel Only)",
+ 4: "Enabled (Channel Only with Seconds)"
+ },
+ value: 1,
+ process_value: function(val) {
+ if ( val === false )
+ return 0;
+ if ( val === true )
+ return 2;
+ if ( typeof val === "string" )
+ return parseInt(val || "0") || 0;
+ return val;
+ },
+
+ no_mobile: true,
category: "Channel Metadata",
name: "Stream Uptime",
help: 'Display the stream uptime under a channel by the viewer count.',
@@ -725,4 +776,31 @@ FFZ.settings_info.stream_title = {
if ( this._cindex )
this._cindex.ffzFixTitle();
}
- };
\ No newline at end of file
+ };
+
+
+FFZ.basic_settings.channel_info = {
+ type: "select",
+ options: {
+ 0: "Disabled",
+ 1: "Enabled",
+ 2: "Enabled (with Seconds)",
+ 3: "Enabled (Channel Only)",
+ 4: "Enabled (Channel Only with Seconds)"
+ },
+
+ category: "General",
+ name: "Stream Uptime",
+ help: "Display the current stream's uptime under the player.",
+
+ get: function() {
+ return this.settings.stream_uptime;
+ },
+
+ set: function(val) {
+ if ( typeof val === 'string' )
+ val = parseInt(val || "0");
+
+ this.settings.set('stream_uptime', val);
+ }
+}
\ No newline at end of file
diff --git a/src/ember/chat-input.js b/src/ember/chat-input.js
index 3bd5e28f..4eddef44 100644
--- a/src/ember/chat-input.js
+++ b/src/ember/chat-input.js
@@ -67,7 +67,7 @@ FFZ.settings_info.input_mru = {
category: "Chat Input",
no_bttv: true,
-
+
name: "Chat Input History",
help: "Use the Up and Down arrows in chat to select previously sent chat messages."
};
@@ -79,7 +79,7 @@ FFZ.settings_info.input_emoji = {
category: "Chat Input",
//visible: false,
no_bttv: true,
-
+
name: "Enter Emoji By Name",
help: "Replace emoji that you type by name with the character. :+1: becomes 👍."
};
@@ -113,10 +113,10 @@ FFZ.prototype.setup_chat_input = function() {
FFZ.prototype._modify_chat_input = function(component) {
var f = this;
-
+
component.reopen({
ffz_mru_index: -1,
-
+
didInsertElement: function() {
this._super();
@@ -124,7 +124,7 @@ FFZ.prototype._modify_chat_input = function(component) {
this.ffzInit();
} catch(err) { f.error("ChatInput didInsertElement: " + err); }
},
-
+
willClearRender: function() {
try {
this.ffzTeardown();
@@ -141,7 +141,7 @@ FFZ.prototype._modify_chat_input = function(component) {
// Redo our key bindings.
var t = this.$("textarea");
-
+
t.off("keydown");
t.on("keydown", this._ffzKeyDown.bind(this));
@@ -153,10 +153,10 @@ FFZ.prototype._modify_chat_input = function(component) {
/*var suggestions = this._parentView.get('context.model.chatSuggestions');
this.set('ffz_chatters', suggestions);*/
},
-
+
ffzTeardown: function() {
if ( f._inputv === this )
- f._inputv = undefined;
+ f._inputv = undefined;
this.ffzResizeInput();
@@ -167,7 +167,7 @@ FFZ.prototype._modify_chat_input = function(component) {
// Reset normal key bindings.
var t = this.$("textarea");
-
+
t.attr('rows', undefined);
t.off("keydown");
@@ -175,42 +175,42 @@ FFZ.prototype._modify_chat_input = function(component) {
},
// Input Control
-
+
ffzOnInput: function() {
- if ( ! f._chat_style || ! f.settings.minimal_chat || is_android )
+ if ( ! f._chat_style || f.settings.minimal_chat < 2 || 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 )
+
+ if ( ! t || ! f._chat_style || f.settings.minimal_chat < 2 )
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; }';
-
+ this._ffz_minimal_style.innerHTML = 'body.ffz-minimal-chat-input .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-input .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; }');
+ utils.update_css(f._chat_style, "input_height", 'body.ffz-minimal-chat-input .ember-chat .chat-interface { height: ' + height + 'px !important; }' +
+ 'body.ffz-minimal-chat-input .ember-chat .chat-messages, body.ffz-minimal-chat-input .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) {
var e = event || window.event,
key = e.charCode || e.keyCode;
@@ -227,7 +227,7 @@ FFZ.prototype._modify_chat_input = function(component) {
else
return this._onKeyDown(event);
break;
-
+
case KEYCODES.SPACE:
if ( f.settings.input_quick_reply && selection_start(this.get("chatTextArea")) === 2 && this.get("textareaValue").substring(0,2) === "/r" ) {
var t = this;
@@ -237,7 +237,7 @@ FFZ.prototype._modify_chat_input = function(component) {
var text = "/w " + wt + t.get("textareaValue").substr(2);
t.set("_currentWhisperTarget", 0);
t.set("textareaValue", text);
-
+
Ember.run.next(function() {
move_selection(t.get('chatTextArea'), 4 + wt.length);
});
@@ -246,7 +246,7 @@ FFZ.prototype._modify_chat_input = function(component) {
} else
return this._onKeyDown(event);
break;
-
+
case KEYCODES.COLON:
case KEYCODES.FAKE_COLON:
if ( f.settings.input_emoji && (e.shiftKey || e.shiftLeft) ) {
@@ -274,22 +274,22 @@ FFZ.prototype._modify_chat_input = function(component) {
return;
}
return this._onKeyDown(event);
-
+
case KEYCODES.ENTER:
if ( ! e.shiftKey && ! e.shiftLeft )
this.set('ffz_mru_index', -1);
-
+
default:
return this._onKeyDown(event);
}
},
-
+
ffzCycleMRU: function(key, start_ind) {
// We don't want to do this if the keys were just moving the cursor around.
var cur_pos = selection_start(this.get("chatTextArea"));
if ( start_ind !== cur_pos )
return;
-
+
var ind = this.get('ffz_mru_index'),
mru = this._parentView.get('context.model.mru_list') || [];
@@ -369,8 +369,8 @@ FFZ.prototype._modify_chat_input = function(component) {
output.push({id:emote.name});
}
}
-
- return output;
+
+ return output;
}.property(),
ffz_chatters: [],
@@ -379,17 +379,17 @@ FFZ.prototype._modify_chat_input = function(component) {
if ( arguments.length > 1 ) {
this.set('ffz_chatters', value);
}
-
+
var output = [];
-
+
// Chatters
output = output.concat(this.get('ffz_chatters'));
-
+
// Emoticons
if ( this.get('isSuggestionsTriggeredWithTab') ) {
output = output.concat(this.get('ffz_emoticons'));
}
-
+
return output;
}.property("ffz_emoticons", "ffz_chatters", "isSuggestionsTriggeredWithTab")*/
});
diff --git a/src/ember/chatview.js b/src/ember/chatview.js
index 48a3dcfd..f6d6fb02 100644
--- a/src/ember/chatview.js
+++ b/src/ember/chatview.js
@@ -40,16 +40,35 @@ FFZ.basic_settings.delayed_chat = {
FFZ.settings_info.minimal_chat = {
- type: "boolean",
- value: false,
+ type: "select",
+ options: {
+ 0: "Disabled",
+ 1: "No Heading",
+ 2: "Minimalistic Input",
+ 3: "All"
+ },
+
+ value: 0,
category: "Chat Appearance",
name: "Minimalistic Chat",
help: "Hide all of the chat user interface, only showing messages and an input box.",
+ process_value: function(val) {
+ if ( val === false )
+ return 0;
+ else if ( val === true )
+ return 3;
+ else if ( typeof val === "string" )
+ return parseInt(val) || 0;
+ return val;
+ },
+
on_update: function(val) {
- document.body.classList.toggle("ffz-minimal-chat", val);
+ document.body.classList.toggle("ffz-minimal-chat-head", val === 1 || val === 3);
+ document.body.classList.toggle("ffz-minimal-chat-input", val > 1);
+
if ( this.settings.group_tabs && this._chatv && this._chatv._ffz_tabs ) {
var f = this;
setTimeout(function() {
@@ -58,11 +77,11 @@ FFZ.settings_info.minimal_chat = {
},0);
}
- if ( this._chatv && this._chatv.get('controller.showList') )
+ if ( (val === 1 || val === 3) && this._chatv && this._chatv.get('controller.showList') )
this._chatv.set('controller.showList', false);
// Remove the style if we have it.
- if ( ! val && this._chat_style ) {
+ if ( ! (val > 1) && this._chat_style ) {
if ( this._inputv ) {
if ( this._inputv._ffz_minimal_style )
this._inputv._ffz_minimal_style.innerHTML = '';
@@ -73,7 +92,7 @@ FFZ.settings_info.minimal_chat = {
utils.update_css(this._chat_style, "input_height", '');
this._roomv && this._roomv.get('stuckToBottom') && this._roomv._scrollToBottom();
- } else if ( this._inputv )
+ } else if ( val > 1 && this._inputv )
this._inputv.ffzResizeInput();
}
};
@@ -247,7 +266,8 @@ FFZ.settings_info.visible_rooms = {
// --------------------
FFZ.prototype.setup_chatview = function() {
- document.body.classList.toggle("ffz-minimal-chat", this.settings.minimal_chat);
+ document.body.classList.toggle("ffz-minimal-chat-head", this.settings.minimal_chat === 1 || this.settings.minimal_chat === 3);
+ document.body.classList.toggle("ffz-minimal-chat-input", this.settings.minimal_chat === 2 || this.settings.minimal_chat === 3);
this.log("Hooking the Ember Chat controller.");
diff --git a/src/ember/directory.js b/src/ember/directory.js
new file mode 100644
index 00000000..31c218e2
--- /dev/null
+++ b/src/ember/directory.js
@@ -0,0 +1,191 @@
+var FFZ = window.FrankerFaceZ,
+ utils = require('../utils'),
+ constants = require('../constants');
+
+
+// --------------------
+// Settings
+// --------------------
+
+FFZ.settings_info.directory_logos = {
+ type: "boolean",
+ value: false,
+
+ category: "Appearance",
+ no_mobile: true,
+
+ name: "Directory Logos",
+ help: "Display channel logos in the Twitch directory."
+ };
+
+
+// --------------------
+// Initialization
+// --------------------
+
+FFZ.prototype.setup_directory = function() {
+ this.log("Hooking the Ember Directory View.");
+
+ var ChannelView = App.__container__.resolve('view:channel');
+ if ( ChannelView )
+ this._modify_directory_live(ChannelView);
+
+ var HostView = App.__container__.resolve('view:host');
+ if ( HostView )
+ this._modify_directory_host(HostView);
+
+ var VideoView = App.__container__.resolve('view:video');
+ if ( VideoView )
+ this._modify_directory_video(VideoView);
+
+ // TODO: Process existing views.
+}
+
+
+FFZ.prototype._modify_directory_video = function(dir) {
+ var f = this;
+ dir.reopen({
+ didInsertElement: function() {
+ this._super();
+
+ f.log("New Video View", this);
+ window.v = this;
+ }
+ });
+
+ try {
+ dir.create().destroy();
+ } catch(err) { }
+}
+
+
+FFZ.prototype._modify_directory_live = function(dir) {
+ var f = this;
+ dir.reopen({
+ didInsertElement: function() {
+ this._super();
+
+ var el = this.get('element'),
+ meta = el && el.querySelector('.meta'),
+ thumb = el && el.querySelector('.thumb'),
+ cap = thumb && thumb.querySelector('.cap');
+
+
+ if ( f.settings.stream_uptime && f.settings.stream_uptime < 3 && cap ) {
+ var t_el = this._ffz_uptime = document.createElement('div');
+ t_el.className = 'overlay_info length live';
+
+ jQuery(t_el).tipsy({html: true});
+
+ cap.appendChild(t_el);
+ this._ffz_uptime_timer = setInterval(this.ffzUpdateUptime.bind(this), 1000);
+ this.ffzUpdateUptime();
+ }
+
+ if ( f.settings.directory_logos ) {
+ el.classList.add('ffz-directory-logo');
+
+ var link = document.createElement('a'),
+ logo = document.createElement('img'),
+ t = this,
+ target = this.get('context.model.channel.name');
+
+ logo.className = 'profile-photo';
+ logo.src = this.get('context.model.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
+ logo.alt = this.get('context.model.channel.display_name');
+
+ link.href = '/' + target;
+ link.addEventListener('click', function(e) {
+ var Channel = App.__container__.resolve('model:channel');
+ if ( ! Channel )
+ return;
+
+ e.preventDefault();
+ t.get('controller').transitionTo('channel.index', Channel.find({id: target}).load());
+ return false;
+ });
+
+ link.appendChild(logo);
+ meta.insertBefore(link, meta.firstChild);
+ }
+ },
+
+ willClearRender: function() {
+ if ( this._ffz_uptime ) {
+ this._ffz_uptime.parentElement.removeChild(this._ffz_uptime);
+ this._ffz_uptime = null;
+ }
+
+ if ( this._ffz_uptime_timer )
+ clearInterval(this._ffz_uptime_timer);
+
+ this._super();
+ },
+
+
+ ffzUpdateUptime: function() {
+ var raw_created = this.get('context.model.created_at'),
+ up_since = raw_created && utils.parse_date(raw_created),
+ uptime = up_since && Math.floor((Date.now() - up_since.getTime()) / 1000) || 0;
+
+ if ( uptime > 0 ) {
+ this._ffz_uptime.innerHTML = constants.CLOCK + utils.time_to_string(uptime, false, false, false, f.settings.stream_uptime === 1);
+ this._ffz_uptime.setAttribute('original-title', 'Stream Uptime (since ' + up_since.toLocaleString() + ')');;
+ } else {
+ this._ffz_uptime.setAttribute('original-title', '');
+ this._ffz_uptime.innerHTML = '';
+ }
+ }
+ });
+
+ try {
+ dir.create().destroy();
+ } catch(err) { }
+}
+
+
+FFZ.prototype._modify_directory_host = function(dir) {
+ var f = this;
+ dir.reopen({
+ didInsertElement: function() {
+ this._super();
+
+ var el = this.get('element'),
+ meta = el && el.querySelector('.meta'),
+ thumb = el && el.querySelector('.thumb'),
+ cap = thumb && thumb.querySelector('.cap');
+
+
+ if ( f.settings.directory_logos ) {
+ el.classList.add('ffz-directory-logo');
+
+ var link = document.createElement('a'),
+ logo = document.createElement('img'),
+ t = this,
+ target = this.get('context.model.target.channel.name');
+
+ logo.className = 'profile-photo';
+ logo.src = this.get('context.model.target.channel.logo') || "http://static-cdn.jtvnw.net/jtv_user_pictures/xarth/404_user_150x150.png";
+ logo.alt = this.get('context.model.target.channel.display_name');
+
+ link.href = '/' + target;
+ link.addEventListener('click', function(e) {
+ var Channel = App.__container__.resolve('model:channel');
+ if ( ! Channel )
+ return;
+
+ e.preventDefault();
+ t.get('controller').transitionTo('channel.index', Channel.find({id: target}).load());
+ return false;
+ });
+
+ link.appendChild(logo);
+ meta.insertBefore(link, meta.firstChild);
+ }
+ }
+ });
+
+ try {
+ dir.create().destroy();
+ } catch(err) { }
+}
\ No newline at end of file
diff --git a/src/ember/layout.js b/src/ember/layout.js
index c5cfc04c..0d4de51b 100644
--- a/src/ember/layout.js
+++ b/src/ember/layout.js
@@ -12,7 +12,7 @@ FFZ.settings_info.swap_sidebars = {
category: "Appearance",
no_mobile: true,
no_bttv: true,
-
+
name: "Swap Sidebar Positions",
help: "Swap the positions of the left and right sidebars, placing chat on the left.",
@@ -26,6 +26,26 @@ FFZ.settings_info.swap_sidebars = {
};
+FFZ.settings_info.flip_dashboard = {
+ type: "boolean",
+ value: false,
+
+ category: "Appearance",
+ no_mobile: true,
+ no_bttv: true,
+
+ name: "Swap Dashboard Positions",
+ help: "Swap the positions of the left and right columns of the dashboard.",
+
+ on_update: function(val) {
+ if ( this.has_bttv )
+ return;
+
+ document.body.classList.toggle("ffz-flip-dashboard", val);
+ }
+ };
+
+
FFZ.settings_info.right_column_width = {
type: "button",
value: 340,
@@ -33,17 +53,17 @@ FFZ.settings_info.right_column_width = {
category: "Appearance",
no_mobile: true,
no_bttv: true,
-
+
name: "Right Sidebar Width",
help: "Set the width of the right sidebar for chat.",
-
+
method: function() {
var old_val = this.settings.right_column_width || 340,
new_val = prompt("Right Sidebar Width\n\nPlease enter a new width for the right sidebar, in pixels. Minimum: 250, Default: 340", old_val);
-
+
if ( new_val === null || new_val === undefined )
return;
-
+
var width = parseInt(new_val);
if ( ! width || width === NaN )
width = 340;
@@ -54,11 +74,11 @@ FFZ.settings_info.right_column_width = {
on_update: function(val) {
if ( this.has_bttv )
return;
-
+
var Layout = App.__container__.lookup('controller:layout');
if ( ! Layout )
return;
-
+
Layout.set('rightColumnWidth', val);
Ember.propertyDidChange(Layout, 'contentWidth');
}
@@ -89,48 +109,64 @@ FFZ.prototype.setup_layout = function() {
Layout.reopen({
rightColumnWidth: 340,
-
+
isTooSmallForRightColumn: function() {
return this.get("windowWidth") < (1090 - this.get('rightColumnWidth'))
}.property("windowWidth", "rightColumnWidth"),
-
+
contentWidth: function() {
var left_width = this.get("isLeftColumnClosed") ? 50 : 240,
right_width = this.get("isRightColumnClosed") ? 0 : this.get("rightColumnWidth");
return this.get("windowWidth") - left_width - right_width - 60;
-
+
}.property("windowWidth", "isRightColumnClosed", "isLeftColumnClosed", "rightColumnWidth"),
-
+
+ playerStyle: function() {
+ var h = this.get('windowHeight'),
+ c = this.get('PLAYER_CONTROLS_HEIGHT'),
+ r = this.get('contentWidth'),
+
+ i = (9 * r / 16) + c,
+ d = h - 120 - 60,
+ c = h - 94 - 185,
+
+ l = Math.floor(r),
+ o = Math.floor(Math.min(i, d)),
+ s = Math.floor(Math.min(i, c));
+
+ return "";
+ }.property("contentWidth", "windowHeight", "PLAYER_CONTROLS_HEIGHT"),
+
/*ffzUpdateWidth: _.throttle(function() {
var rc = document.querySelector('#right_close');
if ( ! rc )
return;
-
+
var left_width = this.get("isLeftColumnClosed") ? 50 : 240,
right_width;
-
+
if ( f.settings.swap_sidebars )
right_width = rc.offsetLeft; // + this.get('rightColumnWidth') - 5;
else
right_width = document.body.offsetWidth - rc.offsetLeft - left_width - 25;
-
+
if ( right_width < 250 ) {
// Close it!
-
+
}
this.set('rightColumnWidth', right_width);
Ember.propertyDidChange(Layout, 'contentWidth');
}, 200),*/
-
+
ffzUpdateCss: function() {
var width = this.get('rightColumnWidth');
-
+
f._layout_style.innerHTML = '#main_col.expandRight #right_close { left: none !important; } #right_col { width: ' + width + 'px; } body:not(.ffz-sidebar-swap) #main_col:not(.expandRight) { margin-right: ' + width + 'px; } body.ffz-sidebar-swap #main_col:not(.expandRight) { margin-left: ' + width + 'px; }';
}.observes("rightColumnWidth"),
-
+
ffzFixTabs: function() {
if ( f.settings.group_tabs && f._chatv && f._chatv._ffz_tabs ) {
setTimeout(function() {
diff --git a/src/ember/line.js b/src/ember/line.js
index 3c660534..a1955756 100644
--- a/src/ember/line.js
+++ b/src/ember/line.js
@@ -27,25 +27,6 @@ FFZ.settings_info.room_status = {
};
-FFZ.settings_info.line_purge_icon = {
- type: "boolean",
- value: false,
-
- no_bttv: true,
- category: "Chat Moderation",
-
- name: "Purge Icon in Mod Icons",
- help: "Display a Purge Icon in chat line Mod Icons for quickly purging users.",
-
- on_update: function(val) {
- if ( this.has_bttv )
- return;
-
- document.body.classList.toggle("ffz-chat-purge-icon", val);
- }
- };
-
-
FFZ.settings_info.replace_bad_emotes = {
type: "boolean",
value: true,
@@ -297,18 +278,22 @@ FFZ.settings_info.chat_separators = {
options: {
0: "Disabled",
1: "Basic Line (1px solid)",
- 2: "3D Line (2px groove)"
+ 2: "3D Line (2px groove)",
+ 3: "3D Line (2px groove inset)",
+ 4: "Wide Line (2px solid)"
},
- value: '0',
+ value: 0,
category: "Chat Appearance",
no_bttv: true,
process_value: function(val) {
if ( val === false )
- return '0';
+ return 0;
else if ( val === true )
- return '1';
+ return 1;
+ else if ( typeof val === "string" )
+ return parseInt(val) || 0;
return val;
},
@@ -316,8 +301,10 @@ FFZ.settings_info.chat_separators = {
help: "Display thin lines between chat messages for further visual separation.",
on_update: function(val) {
- document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && val !== '0');
- document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && val === '2');
+ document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && val !== 0);
+ document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && val === 2);
+ document.body.classList.toggle("ffz-chat-separator-3d-inset", !this.has_bttv && val === 3);
+ document.body.classList.toggle("ffz-chat-separator-wide", !this.has_bttv && val === 4);
}
};
@@ -543,10 +530,11 @@ FFZ.prototype.setup_line = function() {
document.body.classList.toggle("ffz-chat-colors-gray", !this.has_bttv && this.settings.fix_color === '-1');
document.body.classList.toggle('ffz-chat-background', !this.has_bttv && this.settings.chat_rows);
- document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && this.settings.chat_separators !== '0');
- document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && this.settings.chat_separators === '2');
+ document.body.classList.toggle("ffz-chat-separator", !this.has_bttv && this.settings.chat_separators !== 0);
+ document.body.classList.toggle("ffz-chat-separator-wide", !this.has_bttv && this.settings.chat_separators === 4);
+ document.body.classList.toggle("ffz-chat-separator-3d", !this.has_bttv && this.settings.chat_separators === 2);
+ document.body.classList.toggle("ffz-chat-separator-3d-inset", !this.has_bttv && this.settings.chat_separators === 3);
document.body.classList.toggle("ffz-chat-padding", !this.has_bttv && this.settings.chat_padding);
- document.body.classList.toggle("ffz-chat-purge-icon", !this.has_bttv && this.settings.line_purge_icon);
document.body.classList.toggle("ffz-high-contrast-chat-text", !this.has_bttv && this.settings.high_contrast_chat[2] === '1');
document.body.classList.toggle("ffz-high-contrast-chat-bold", !this.has_bttv && this.settings.high_contrast_chat[1] === '1');
@@ -645,16 +633,30 @@ FFZ.prototype._modify_line = function(component) {
if ( e.target && e.target.classList.contains('mod-icon') ) {
jQuery(e.target).trigger('mouseout');
- if ( e.target.classList.contains('purge') ) {
+ /*if ( e.target.classList.contains('purge') ) {
var i = this.get('msgObject.from'),
room_id = this.get('msgObject.room'),
room = room_id && f.rooms[room_id] && f.rooms[room_id].room;
if ( room ) {
- room.send("/timeout " + i + " 1");
+ room.send("/timeout " + i + " 1", true);
room.clearMessages(i);
}
return;
+ }*/
+
+ if ( e.target.classList.contains('custom') ) {
+ var room_id = this.get('msgObject.room'),
+ room = room_id && f.rooms[room_id] && f.rooms[room_id].room,
+
+ cmd = e.target.getAttribute('data-cmd');
+
+ if ( room ) {
+ room.send(cmd, true);
+ if ( e.target.classList.contains('is-timeout') )
+ room.clearMessages(this.get('msgObject.from'));
+ }
+ return;
}
}
@@ -678,7 +680,7 @@ FFZ.prototype._modify_line = function(component) {
return 4;
else if ( this.get('isBroadcaster') )
return 3;
- else if ( this.get('isGlobalModerator') )
+ else if ( this.get('isGlobalMod') )
return 2;
else if ( this.get('isModerator') )
return 1;
@@ -712,13 +714,34 @@ FFZ.prototype._modify_line = function(component) {
if ( ! is_whisper && this_ul < other_ul ) {
e.push('');
- if ( deleted )
- e.push('Unban');
- else
- e.push('Ban');
+ for(var i=0, l = f.settings.mod_buttons.length; i < l; i++) {
+ var pair = f.settings.mod_buttons[i],
+ prefix = pair[0], btn = pair[1],
+
+ cmd, tip;
+
+ if ( btn === false ) {
+ if ( deleted )
+ e.push('Unban');
+ else
+ e.push('Ban');
+
+ } else if ( btn === 600 )
+ e.push('Timeout');
+
+ else {
+ if ( typeof btn === "string" ) {
+ cmd = btn.replace(/{user}/g, user);
+ tip = 'Custom Command\n' + cmd;
+ } else {
+ cmd = "/timeout " + user + " " + btn;
+ tip = "Timeout User (" + utils.duration_string(btn) + ")";
+ }
+
+ e.push('' + prefix + '');
+ }
+ }
- e.push('Timeout');
- e.push('Purge');
e.push('');
}
@@ -730,7 +753,7 @@ FFZ.prototype._modify_line = function(component) {
else if ( this.get('isAdmin') )
badges[0] = {klass: 'admin', title: 'Admin'};
else if ( this.get('isGlobalMod') )
- badges[0] = {klass: 'global-moderator', title: 'Global Moderator'};
+ badges[0] = {klass: 'global-mod', title: 'Global Moderator'};
else if ( ! is_whisper && this.get('isModerator') )
badges[0] = {klass: 'moderator', title: 'Moderator'};
@@ -881,7 +904,7 @@ FFZ.get_capitalization = function(name, callback) {
FFZ.prototype._remove_banned = function(tokens) {
var banned_words = this.settings.banned_words,
- banned_links = this.settings.filter_bad_shorteners ? ['goo.gl', 'j.mp', 'bit.ly'] : null,
+ banned_links = this.settings.filter_bad_shorteners ? ['apo.af', 'goo.gl', 'j.mp', 'bit.ly'] : null,
has_banned_words = banned_words && banned_words.length;
diff --git a/src/ember/moderation-card.js b/src/ember/moderation-card.js
index c7a1602a..37cf14e9 100644
--- a/src/ember/moderation-card.js
+++ b/src/ember/moderation-card.js
@@ -12,33 +12,7 @@ var FFZ = window.FrankerFaceZ,
},
MESSAGE = '',
- CHECK = '',
-
- DURATIONS = {},
- duration_string = function(val) {
- if ( val === 1 )
- return 'Purge';
-
- if ( DURATIONS[val] )
- return DURATIONS[val];
-
- var weeks, days, hours, minutes, seconds;
-
- weeks = Math.floor(val / 604800);
- seconds = val % 604800;
-
- days = Math.floor(seconds / 86400);
- seconds %= 86400;
-
- hours = Math.floor(seconds / 3600);
- seconds %= 3600;
-
- minutes = Math.floor(seconds / 60);
- seconds %= 60;
-
- var out = DURATIONS[val] = (weeks ? weeks + 'w' : '') + ((days || (weeks && (hours || minutes || seconds))) ? days + 'd' : '') + ((hours || ((weeks || days) && (minutes || seconds))) ? hours + 'h' : '') + ((minutes || ((weeks || days || hours) && seconds)) ? minutes + 'm' : '') + (seconds ? seconds + 's' : '');
- return out;
- };
+ CHECK = '';
try {
@@ -169,6 +143,127 @@ FFZ.settings_info.mod_card_history = {
};
+FFZ.settings_info.mod_buttons = {
+ type: "button",
+
+ // Special Values
+ // false = Ban/Unban
+ // integer = Timeout (that amount of time)
+ value: [['', false, false], ['',600, false]], //, ['', 1, false]],
+
+ category: "Chat Moderation",
+ no_bttv: true,
+
+ name: "Custom In-Line Moderation Icons",
+ help: "Change out the different in-line moderation icons to use any command quickly.",
+
+ method: function() {
+ var old_val = "";
+ for(var i=0; i < this.settings.mod_buttons.length; i++) {
+ var pair = this.settings.mod_buttons[i],
+ prefix = pair[0], cmd = pair[1], had_prefix = pair[2];
+
+ if ( cmd === false )
+ cmd = "";
+ else if ( typeof cmd !== "string" )
+ cmd = '' + cmd;
+
+ if ( ! had_prefix )
+ prefix = '';
+ else
+ prefix += '=';
+
+ if ( cmd.substr(cmd.length - 7) === ' {user}' )
+ cmd = cmd.substr(0, cmd.length - 7);
+
+ if ( cmd.indexOf(' ') !== -1 )
+ old_val += ' ' + prefix + '"' + cmd + '"';
+ else
+ old_val += ' ' + prefix + cmd;
+ }
+
+ var new_val = prompt("Custom In-Line Moderation Icons\n\nPlease enter a list of commands to be made available as mod icons within chat lines. Commands are separated by spaces. To include spaces in a command, surround the command with double quotes (\"). Use \"{user}\" to insert the user's username into the command, otherwise it will be appended to the end.\n\nExample: !permit \"!reg add {user}\"\n\nNumeric values will become timeout buttons for that number of seconds. The text \"\" is a special value that will act like the normal Ban button in chat.\n\nTo assign a specific letter for use as the icon, specify it at the start of the command followed by an equals sign.\n\nExample: A=\"!reg add\"\n\nDefault: 600", old_val);
+
+ if ( new_val === null || new_val === undefined )
+ return;
+
+ var vals = [], prefix = '';
+ new_val = new_val.trim();
+
+ while(new_val) {
+ if ( new_val.charAt(1) === '=' ) {
+ prefix = new_val.charAt(0);
+ new_val = new_val.substr(2);
+ continue;
+ }
+
+ if ( new_val.charAt(0) === '"' ) {
+ var end = new_val.indexOf('"', 1);
+ if ( end === -1 )
+ end = new_val.length;
+
+ var segment = new_val.substr(1, end - 1);
+ if ( segment ) {
+ vals.push([prefix, segment]);
+ prefix = '';
+ }
+
+ new_val = new_val.substr(end + 1);
+
+ } else {
+ var ind = new_val.indexOf(' ');
+ if ( ind === -1 ) {
+ if ( new_val ) {
+ vals.push([prefix, new_val]);
+ prefix = '';
+ }
+
+ new_val = '';
+
+ } else {
+ var segment = new_val.substr(0, ind);
+ if ( segment ) {
+ vals.push([prefix, segment]);
+ prefix = '';
+ }
+
+ new_val = new_val.substr(ind + 1);
+ }
+ }
+ }
+
+ var final = [];
+ for(var i=0; i < vals.length; i++) {
+ var had_prefix = false, prefix = vals[i][0], val = vals[i][1];
+ if ( val === "" )
+ val = false;
+
+ var num = parseInt(val);
+ if ( num > 0 && num !== NaN )
+ val = num;
+
+ if ( ! prefix ) {
+ var tmp;
+ if ( typeof val === "string" )
+ tmp = /\w/.exec(val);
+ else
+ tmp = utils.duration_string(val);
+
+ prefix = tmp && tmp.length ? tmp[0].toUpperCase() : "C";
+ } else
+ had_prefix = true;
+
+ if ( typeof val === "string" && val.indexOf('{user}') === -1 )
+ val += ' {user}';
+
+ final.push([prefix, val, had_prefix]);
+ }
+
+ this.settings.set('mod_buttons', final);
+ }
+ };
+
+
FFZ.settings_info.mod_card_buttons = {
type: "button",
value: [],
@@ -351,6 +446,8 @@ FFZ.prototype.setup_mod_card = function() {
controller = this.get('controller'),
line,
+ is_mod = controller.get('cardInfo.isModeratorOrHigher'),
+
user_id = controller.get('cardInfo.user.id'),
alias = f.aliases[user_id];
@@ -384,7 +481,7 @@ FFZ.prototype.setup_mod_card = function() {
}
// Additional Buttons
- if ( f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
+ if ( is_mod && f.settings.mod_card_buttons && f.settings.mod_card_buttons.length ) {
line = document.createElement('div');
line.className = 'extra-interface interface clearfix';
@@ -394,7 +491,7 @@ FFZ.prototype.setup_mod_card = function() {
cont = App.__container__.lookup('controller:chat'),
room = cont && cont.get('currentRoom');
- room && room.send(cmd.replace(/{user}/g, user_id));
+ room && room.send(cmd.replace(/{user}/g, user_id), true);
},
add_btn_make = function(cmd) {
@@ -446,16 +543,16 @@ FFZ.prototype.setup_mod_card = function() {
room = App.__container__.lookup('controller:chat').get('currentRoom');
if ( is_mod && key == keycodes.P )
- room.send("/timeout " + user_id + " 1");
+ room.send("/timeout " + user_id + " 1", true);
else if ( is_mod && key == keycodes.B )
- room.send("/ban " + user_id);
+ room.send("/ban " + user_id, true);
else if ( is_mod && key == keycodes.T )
- room.send("/timeout " + user_id + " 600");
+ room.send("/timeout " + user_id + " 600", true);
else if ( is_mod && key == keycodes.U )
- room.send("/unban " + user_id);
+ room.send("/unban " + user_id, true);
else if ( key != keycodes.ESC )
return;
@@ -466,51 +563,23 @@ FFZ.prototype.setup_mod_card = function() {
// Only do the big stuff if we're mod.
- if ( controller.get('cardInfo.isModeratorOrHigher') ) {
+ if ( is_mod ) {
el.classList.add('ffz-is-mod');
- // Key Handling
- if ( f.settings.mod_card_hotkeys ) {
- el.classList.add('no-mousetrap');
-
- el.addEventListener('keyup', function(e) {
- var key = e.keyCode || e.which,
- user_id = controller.get('cardInfo.user.id'),
- room = App.__container__.lookup('controller:chat').get('currentRoom');
-
- if ( key == keycodes.P )
- room.send("/timeout " + user_id + " 1");
-
- else if ( key == keycodes.B )
- room.send("/ban " + user_id);
-
- else if ( key == keycodes.T )
- room.send("/timeout " + user_id + " 600");
-
- else if ( key == keycodes.U )
- room.send("/unban " + user_id);
-
- else if ( key != keycodes.ESC )
- return;
-
- controller.send('close');
- });
- }
-
var btn_click = function(timeout) {
var user_id = controller.get('cardInfo.user.id'),
room = App.__container__.lookup('controller:chat').get('currentRoom');
if ( timeout === -1 )
- room.send("/unban " + user_id);
+ room.send("/unban " + user_id, true);
else
- room.send("/timeout " + user_id + " " + timeout);
+ room.send("/timeout " + user_id + " " + timeout, true);
},
btn_make = function(timeout) {
var btn = document.createElement('button')
btn.className = 'button';
- btn.innerHTML = duration_string(timeout);
+ btn.innerHTML = utils.duration_string(timeout);
btn.title = "Timeout User for " + utils.number_commas(timeout) + " Second" + (timeout != 1 ? "s" : "");
if ( f.settings.mod_card_hotkeys && timeout === 600 )
@@ -746,7 +815,7 @@ FFZ.chat_commands.purge = function(room, args) {
for(var i=0; i < args.length; i++) {
var name = args[i];
if ( name )
- room.room.send("/timeout " + name + " 1");
+ room.room.send("/timeout " + name + " 1", true);
}
}
@@ -760,7 +829,7 @@ FFZ.chat_commands.p.enabled = function() { return this.settings.short_commands;
FFZ.chat_commands.t = function(room, args) {
if ( ! args || ! args.length )
return "Timeout Usage: /t username [duration]";
- room.room.send("/timeout " + args.join(" "));
+ room.room.send("/timeout " + args.join(" "), true);
}
FFZ.chat_commands.t.enabled = function() { return this.settings.short_commands; }
@@ -776,7 +845,7 @@ FFZ.chat_commands.b = function(room, args) {
for(var i=0; i < args.length; i++) {
var name = args[i];
if ( name )
- room.room.send("/ban " + name);
+ room.room.send("/ban " + name, true);
}
}
@@ -793,7 +862,7 @@ FFZ.chat_commands.u = function(room, args) {
for(var i=0; i < args.length; i++) {
var name = args[i];
if ( name )
- room.room.send("/unban " + name);
+ room.room.send("/unban " + name, true);
}
}
diff --git a/src/ember/player.js b/src/ember/player.js
index 9e0a9339..bfa09250 100644
--- a/src/ember/player.js
+++ b/src/ember/player.js
@@ -13,42 +13,76 @@ FFZ.settings_info.player_stats = {
category: "Channel Metadata",
name: "Stream Latency",
- help: "New HTML5 Player Only. Display your current stream latency (how far behind the broadcast you are) under the player, with a few useful statistics in a tooltip.",
+ help: "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) {
+ for(var key in this.players) {
+ var player = this.players[key];
+ if ( player && player.player && player.player.ffzSetStatsEnabled )
+ player.player.ffzSetStatsEnabled(val || player.player.ffz_stats);
+ }
+
if ( ! this._cindex )
return;
-
+
this._cindex.ffzUpdatePlayerStats();
}
};
+FFZ.settings_info.classic_player = {
+ type: "boolean",
+ value: false,
+ no_mobile: true,
+
+ category: "Appearance",
+
+ name: "Classic Player",
+ help: "Alter the appearance of the player to resemble the older Twitch player with always visible controls.",
+
+ on_update: function(val) {
+ document.body.classList.toggle('ffz-classic-player', val);
+ var Layout = window.App && App.__container__.lookup('controller:layout');
+ if ( Layout )
+ Layout.set('PLAYER_CONTROLS_HEIGHT', val ? 32 : 0);
+ }
+ };
+
+
// ---------------
// Initialization
// ---------------
FFZ.prototype.setup_player = function() {
+ document.body.classList.toggle('ffz-classic-player', this.settings.classic_player);
+ var Layout = window.App && App.__container__.lookup('controller:layout');
+ if ( Layout )
+ Layout.set('PLAYER_CONTROLS_HEIGHT', this.settings.classic_player ? 32 : 0);
+
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.
+ if ( ! window.Ember )
+ return;
+
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 {
+ this._modify_player(view);
view.ffzInit();
if ( view.get('player') )
view.ffzPostPlayer();
@@ -65,7 +99,12 @@ FFZ.prototype.setup_player = function() {
// ---------------
FFZ.prototype._modify_player = function(player) {
- var f = this;
+ var f = this,
+ update_stats = function() {
+ f._cindex && f._cindex.ffzUpdatePlayerStats();
+ };
+
+
player.reopen({
didInsertElement: function() {
this._super();
@@ -75,7 +114,7 @@ FFZ.prototype._modify_player = function(player) {
f.error("Player2 didInsertElement: " + err);
}
},
-
+
willClearRender: function() {
try {
this.ffzTeardown();
@@ -84,7 +123,7 @@ FFZ.prototype._modify_player = function(player) {
}
this._super();
},
-
+
postPlayerSetup: function() {
this._super();
try {
@@ -93,55 +132,101 @@ FFZ.prototype._modify_player = function(player) {
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;
+
+ if ( this._ffz_stat_interval ) {
+ clearInterval(this._ffz_stat_interval);
+ this._ffz_stat_interval = null;
+ }
},
-
- 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();
+ // Subscribe to the qualities event.
+ //player.addEventListener('qualitieschange', this.ffzQualitiesUpdated.bind(this));
+ //this.ffzQualitiesUpdated();
+
+ // Only set up the stats hooks if we need stats.
+ if ( ! player.getVideo() )
+ this.ffzInitStats();
},
-
- ffzWaitForStats: function() {
+
+ ffzInitStats: function() {
+ if ( this.get('ffzStatsInitialized') )
+ return;
+
var player = this.get('player');
if ( ! player )
return;
- if ( player.stats ) {
- // Add the event listener.
- player.addEventListener('statschange', this._ffz_stat_update);
+ this.set('ffzStatsInitialized', true);
- } 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);
+ // Make it so stats can no longer be disabled if we want them.
+ player.ffzSetStatsEnabled = player.setStatsEnabled;
+ player.ffz_stats = player.getStatsEnabled();
+
+ var t = this;
+
+ player.setStatsEnabled = function(e, s) {
+ if ( s !== false )
+ player.ffz_stats = e;
+
+ var out = player.ffzSetStatsEnabled(e || f.settings.player_stats);
+
+ if ( ! t._ffz_player_stats_initialized ) {
+ t._ffz_player_stats_initialized = true;
+ player.addEventListener('statschange', update_stats);
+ }
+
+ return out;
}
- }
+
+ this._ffz_stat_interval = setInterval(function() {
+ if ( f.settings.player_stats || player.ffz_stats ) {
+ player.ffzSetStatsEnabled(false);
+ player.ffzSetStatsEnabled(true);
+ }
+ }, 5000);
+
+ if ( f.settings.player_stats && ! player.ffz_stats ) {
+ this._ffz_player_stats_initialized = true;
+ player.addEventListener('statschange', update_stats);
+ player.ffzSetStatsEnabled(true);
+ }
+ },
+
+ ffzSetQuality: function(q) {
+ var player = this.get('player');
+ if ( ! player )
+ return;
+
+ this.$(".js-quality-display-contain").attr("data-q", "loading");
+
+ player.setQuality(q);
+
+ var t = this.$(".js-player-alert");
+ t.find(".js-player-alert__message").text();
+ t.attr("data-active", !0);
+ },
+
+ ffzGetQualities: function() {
+ var player = this.get('player');
+ if ( ! player )
+ return [];
+ return player.getQualities();
+ },
+
});
}
\ No newline at end of file
diff --git a/src/ember/room.js b/src/ember/room.js
index 9f06fa6a..a4b04135 100644
--- a/src/ember/room.js
+++ b/src/ember/room.js
@@ -1180,12 +1180,12 @@ FFZ.prototype._modify_room = function(room) {
return this._super(e);
},
- send: function(text) {
+ send: function(text, ignore_history) {
if ( f.settings.group_tabs && f.settings.whisper_room && this.ffz_whisper_room )
return;
try {
- if ( text ) {
+ if ( text && ! ignore_history ) {
// Command History
var mru = this.get('mru_list'),
ind = mru.indexOf(text);
diff --git a/src/ext/betterttv.js b/src/ext/betterttv.js
index b5919466..4a3369de 100644
--- a/src/ext/betterttv.js
+++ b/src/ext/betterttv.js
@@ -66,7 +66,10 @@ FFZ.prototype.setup_bttv = function(delay) {
document.body.classList.remove("ffz-chat-padding");
document.body.classList.remove("ffz-chat-separator");
document.body.classList.remove("ffz-chat-separator-3d");
+ document.body.classList.remove("ffz-chat-separator-wide");
+ document.body.classList.remove("ffz-chat-separator-3d-inset");
document.body.classList.remove("ffz-sidebar-swap");
+ document.body.classList.remove("ffz-flip-dashboard");
document.body.classList.remove("ffz-transparent-badges");
document.body.classList.remove("ffz-high-contrast-chat-text");
document.body.classList.remove("ffz-high-contrast-chat-bg");
diff --git a/src/featurefriday.js b/src/featurefriday.js
index fed32535..e1cbe3ec 100644
--- a/src/featurefriday.js
+++ b/src/featurefriday.js
@@ -17,7 +17,7 @@ FFZ.prototype.check_ff = function(tries) {
if ( ! tries )
this.log("Checking for Feature Friday data...");
- jQuery.ajax(constants.SERVER + "script/event.json", {cache: false, dataType: "json", context: this})
+ jQuery.ajax(constants.SERVER + "script/event.json", {dataType: "json", context: this})
.done(function(data) {
return this._load_ff(data);
}).fail(function(data) {
diff --git a/src/localization.js b/src/localization.js
new file mode 100644
index 00000000..8bcd4948
--- /dev/null
+++ b/src/localization.js
@@ -0,0 +1,6 @@
+var FFZ = window.FrankerFaceZ;
+
+
+FFZ.prototype.tr = function(s) {
+ return s;
+}
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 159cc75a..94f78330 100644
--- a/src/main.js
+++ b/src/main.js
@@ -21,7 +21,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
- major: 3, minor: 5, revision: 21,
+ major: 3, minor: 5, revision: 30,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@@ -103,6 +103,7 @@ FFZ.prototype.get_user = function() {
// -------------------
// Import these first to set up data structures
+require('./localization');
require('./ui/menu');
require('./settings');
require('./socket');
@@ -111,11 +112,12 @@ require('./colors');
require('./emoticons');
require('./badges');
require('./tokenize');
+//require('./filtering');
// Analytics: require('./ember/router');
require('./ember/channel');
-//require('./ember/player');
+require('./ember/player');
require('./ember/room');
require('./ember/layout');
require('./ember/line');
@@ -124,6 +126,7 @@ require('./ember/viewers');
require('./ember/moderation-card');
require('./ember/chat-input');
//require('./ember/teams');
+require('./ember/directory');
require('./debug');
@@ -158,7 +161,7 @@ FFZ.prototype.initialize = function(increment, delay) {
// Check for the player
if ( location.hostname === 'player.twitch.tv' ) {
- //this.init_player(delay);
+ this.init_player(delay);
return;
}
@@ -211,6 +214,8 @@ FFZ.prototype.init_player = function(delay) {
// Literally only make it dark.
this.load_settings();
this.setup_dark();
+ this.setup_css();
+ this.setup_player();
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
duration = end - start;
@@ -279,7 +284,9 @@ FFZ.prototype.init_dashboard = function(delay) {
this.setup_tokenization();
this.setup_notifications();
+ this.setup_following_count(false);
this.setup_css();
+ this.setup_menu();
this._update_subscribers();
@@ -318,7 +325,9 @@ FFZ.prototype.init_ember = function(delay) {
//this.setup_router();
this.setup_colors();
this.setup_tokenization();
- //this.setup_player();
+ //this.setup_filtering();
+
+ this.setup_player();
this.setup_channel();
this.setup_room();
this.setup_line();
@@ -327,6 +336,7 @@ FFZ.prototype.init_ember = function(delay) {
this.setup_viewers();
this.setup_mod_card();
this.setup_chat_input();
+ this.setup_directory();
//this.setup_teams();
diff --git a/src/settings.js b/src/settings.js
index 33f5b2b7..d030b343 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -89,11 +89,37 @@ FFZ.prototype.load_settings = function() {
// Backup and Restore
// --------------------
+FFZ.prototype.reset_settings = function() {
+ if ( ! confirm(this.tr('Are you sure you wish to reset FrankerFaceZ?\n\nThis will force the tab to refresh.')) )
+ return;
+
+
+ // Clear Settings
+ for(var key in FFZ.settings_info) {
+ if ( ! FFZ.settings_info.hasOwnProperty(key) )
+ continue;
+
+ this.settings.del(key);
+ }
+
+ // Clear Aliases
+ this.aliases = {};
+ localStorage.ffz_aliases = '{}';
+
+ // TODO: Filters
+
+
+ // Refresh
+ window.location.reload();
+}
+
+
FFZ.prototype.save_settings_file = function() {
var data = {
version: 1,
script_version: FFZ.version_info + '',
aliases: this.aliases,
+ filters: this.filters,
settings: {}
};
@@ -135,8 +161,8 @@ FFZ.prototype._load_settings_file = function(data) {
this.log("Loading Settings Data", data);
- var skipped = [],
- applied = [];
+ var skipped = [], applied = [],
+ aliases = 0;
if ( data.settings ) {
for(var key in data.settings) {
@@ -158,9 +184,26 @@ FFZ.prototype._load_settings_file = function(data) {
}
}
+ if ( data.aliases ) {
+ for(var key in data.aliases) {
+ if ( this.aliases[key] === data.aliases[key] )
+ continue;
+
+ this.aliases[key] = data.aliases[key];
+ aliases++;
+ }
+
+ if ( aliases )
+ localStorage.ffz_aliases = JSON.stringify(this.aliases);
+ }
+
+ if ( data.filters ) {
+ // TODO: Load filters!
+ }
+
// Do this in a timeout so that any styles have a moment to update.
setTimeout(function(){
- alert('Successfully loaded ' + applied.length + ' settings and skipped ' + skipped.length + ' settings.');
+ alert('Successfully loaded ' + applied.length + ' settings and skipped ' + skipped.length + ' settings. Added ' + aliases + ' user nicknames.');
});
}
@@ -257,8 +300,11 @@ FFZ.menu_pages.settings = {
render_save: function(view, container) {
var backup_head = document.createElement('div'),
restore_head = document.createElement('div'),
+ reset_head = document.createElement('div'),
+
backup_cont = document.createElement('div'),
restore_cont = document.createElement('div'),
+ reset_cont = document.createElement('div'),
backup_para = document.createElement('p'),
backup_link = document.createElement('a'),
@@ -268,6 +314,10 @@ FFZ.menu_pages.settings = {
restore_input = document.createElement('input'),
restore_link = document.createElement('a'),
restore_help = document.createElement('span'),
+
+ reset_para = document.createElement('p'),
+ reset_link = document.createElement('a'),
+ reset_help = document.createElement('span'),
f = this;
@@ -310,8 +360,27 @@ FFZ.menu_pages.settings = {
restore_para.appendChild(restore_help);
restore_cont.appendChild(restore_para);
+ reset_cont.className = 'chat-menu-content';
+ reset_head.className = 'heading';
+ reset_head.innerHTML = this.tr('Reset Settings');
+ reset_cont.appendChild(reset_head);
+
+ reset_para.className = 'clearfix option';
+
+ reset_link.href = '#';
+ reset_link.innerHTML = this.tr('Reset FrankerFaceZ');
+ reset_link.addEventListener('click', this.reset_settings.bind(this));
+
+ reset_help.className = 'help';
+ reset_help.innerHTML = this.tr('This resets all of your FFZ data. That includes chat filters, nicknames for users, and settings.');
+
+ reset_para.appendChild(reset_link);
+ reset_para.appendChild(reset_help);
+ reset_cont.appendChild(reset_para);
+
container.appendChild(backup_cont);
container.appendChild(restore_cont);
+ container.appendChild(reset_cont);
},
render_basic: function(view, container) {
@@ -797,11 +866,11 @@ FFZ.prototype._setting_del = function(key) {
if ( localStorage.hasOwnProperty(ls_key) )
localStorage.removeItem(ls_key);
- delete this.settings[key];
-
if ( info )
val = this.settings[key] = info.hasOwnProperty("value") ? info.value : undefined;
+ this.settings[key] = val;
+
if ( info.on_update )
try {
info.on_update.bind(this)(val, true);
diff --git a/src/tokenize.js b/src/tokenize.js
index 0e5a4ce9..e24c4b46 100644
--- a/src/tokenize.js
+++ b/src/tokenize.js
@@ -108,7 +108,7 @@ var FFZ = window.FrankerFaceZ,
}
image_iframe = function(href, extra_class) {
- return '';
+ return '';
},
@@ -352,7 +352,7 @@ FFZ.prototype.setup_tokenization = function() {
// ---------------------
FFZ.prototype.load_twitch_emote_data = function(tries) {
- jQuery.ajax(constants.SERVER + "script/twitch_emotes.json", {cache: false, context: this})
+ jQuery.ajax(constants.SERVER + "script/twitch_emotes.json", {context: this})
.done(function(data) {
for(var set_id in data) {
var set = data[set_id];
diff --git a/src/ui/dark.js b/src/ui/dark.js
index 50256331..6de8f3bd 100644
--- a/src/ui/dark.js
+++ b/src/ui/dark.js
@@ -50,11 +50,11 @@ FFZ.basic_settings.minimalistic_chat = {
help: "Hide all of chat except messages and the input box and reduce chat margins.",
get: function() {
- return this.settings.minimal_chat && this.settings.chat_padding;
+ return this.settings.minimal_chat === 3 && this.settings.chat_padding;
},
set: function(val) {
- this.settings.set('minimal_chat', val);
+ this.settings.set('minimal_chat', val ? 3 : 0);
this.settings.set('chat_padding', val);
}
};
diff --git a/src/ui/styles.js b/src/ui/styles.js
index 11184095..b21f008d 100644
--- a/src/ui/styles.js
+++ b/src/ui/styles.js
@@ -2,6 +2,8 @@ var FFZ = window.FrankerFaceZ,
constants = require('../constants');
FFZ.prototype.setup_css = function() {
+ document.body.classList.toggle('ffz-flip-dashboard', this.settings.flip_dashboard);
+
this.log("Injecting main FrankerFaceZ CSS.");
var s = this._main_style = document.createElement('link');
@@ -11,14 +13,15 @@ FFZ.prototype.setup_css = function() {
s.setAttribute('href', constants.SERVER + "script/style.css?_=" + (constants.DEBUG ? Date.now() : FFZ.version_info));
document.head.appendChild(s);
- jQuery.noty.themes.ffzTheme = {
- name: "ffzTheme",
- style: function() {
- this.$bar.removeClass().addClass("noty_bar").addClass("ffz-noty").addClass(this.options.type);
- },
- callback: {
- onShow: function() {},
- onClose: function() {}
- }
- };
+ if ( window.jQuery && jQuery.noty )
+ jQuery.noty.themes.ffzTheme = {
+ name: "ffzTheme",
+ style: function() {
+ this.$bar.removeClass().addClass("noty_bar").addClass("ffz-noty").addClass(this.options.type);
+ },
+ callback: {
+ onShow: function() {},
+ onClose: function() {}
+ }
+ };
}
\ No newline at end of file
diff --git a/src/utils.js b/src/utils.js
index b8fd7e05..512e008f 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -8,13 +8,15 @@ var sanitize_el = document.createElement('span'),
sanitize_el.textContent = msg;
return sanitize_el.innerHTML;
},
-
+
R_QUOTE = /"/g,
R_SQUOTE = /'/g,
R_AMP = /&/g,
R_LT = //g,
-
+
+ DURATIONS = {},
+
quote_attr = function(msg) {
return msg.replace(R_AMP, "&").replace(R_QUOTE, """).replace(R_SQUOTE, "'").replace(R_LT, "<").replace(R_GT, ">");
},
@@ -256,7 +258,7 @@ module.exports = {
return 'less than a second';
},
- time_to_string: function(elapsed, separate_days, days_only, no_hours) {
+ time_to_string: function(elapsed, separate_days, days_only, no_hours, no_seconds) {
var seconds = elapsed % 60,
minutes = Math.floor(elapsed / 60),
hours = Math.floor(minutes / 60),
@@ -273,7 +275,32 @@ module.exports = {
days = ( days > 0 ) ? days + " days, " : "";
}
- return days + ((!no_hours || days || hours) ? ((hours < 10 ? "0" : "") + hours + ':') : '') + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
+ return days + ((!no_hours || days || hours) ? ((days && hours < 10 ? "0" : "") + hours + ':') : '') + (minutes < 10 ? "0" : "") + minutes + (no_seconds ? "" : (":" + (seconds < 10 ? "0" : "") + seconds));
+ },
+
+ duration_string: function(val) {
+ if ( val === 1 )
+ return 'Purge';
+
+ if ( DURATIONS[val] )
+ return DURATIONS[val];
+
+ var weeks, days, hours, minutes, seconds;
+
+ weeks = Math.floor(val / 604800);
+ seconds = val % 604800;
+
+ days = Math.floor(seconds / 86400);
+ seconds %= 86400;
+
+ hours = Math.floor(seconds / 3600);
+ seconds %= 3600;
+
+ minutes = Math.floor(seconds / 60);
+ seconds %= 60;
+
+ var out = DURATIONS[val] = (weeks ? weeks + 'w' : '') + ((days || (weeks && (hours || minutes || seconds))) ? days + 'd' : '') + ((hours || ((weeks || days) && (minutes || seconds))) ? hours + 'h' : '') + ((minutes || ((weeks || days || hours) && seconds)) ? minutes + 'm' : '') + (seconds ? seconds + 's' : '');
+ return out;
},
format_unread: function(count) {
diff --git a/style.css b/style.css
index 1cd8d82d..5cd812e0 100644
--- a/style.css
+++ b/style.css
@@ -21,20 +21,20 @@ body > div.tipsy .tipsy-arrow { opacity: 0.8; }
.ffz-hide-recent-past-broadcast .recent-past-broadcast,
.ffz-hide-view-count .stat.twitch-channel-views,
-.ffz-minimal-chat .emoticon-selector-toggle,
+.ffz-minimal-chat-input .emoticon-selector-toggle,
.ffz-menu-replace .emoticon-selector-toggle {
display: none !important;
}
-body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle svg,
-body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle svg
+body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle svg,
+body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle svg
{
height: 14px;
width: 18px;
}
-body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle,
-body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle {
+body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .emoticon-selector-toggle + .ffz-ui-toggle,
+body:not(.ffz-minimal-chat-input):not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle {
height: 14px;
width: 18px;
top: 28px;
@@ -43,6 +43,8 @@ body:not(.ffz-minimal-chat):not(.ffz-menu-replace) .emoticon-selector-toggle + s
.ffz-ui-toggle svg.svg-emoticons path { fill: rgba(0,0,0,0.2); }
.ffz-ui-toggle:hover svg.svg-emoticons path { fill: rgba(0,0,0,0.5); }
+.streams .stream .content .overlay_info.live svg path,
+.videos .video .content .overlay_info.live svg path { fill: #ff2020; }
.ember-chat-container.dark .ffz-ui-toggle svg.svg-emoticons path,
.chat-container.dark .ffz-ui-toggle svg.svg-emoticons path,
@@ -974,6 +976,16 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
background-repeat: no-repeat;
}
+.ember-chat .mod-icons .custom {
+ text-indent: 0;
+ text-align: center;
+ text-decoration: none;
+ font-size: 18px;
+ font-weight: bold;
+ color: #888 !important;
+}
+
+
/* Chat Rows */
.ffz-alias { font-style: italic; }
@@ -1045,10 +1057,19 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
border-bottom: 1px solid #aaa;
}
+.ffz-chat-separator-wide .chat-line:before {
+ border-top: 1px solid #aaa;
+}
+
.ffz-chat-separator-3d .chat-line:before {
border-top: 1px solid rgba(255,255,255,0.5);
}
+.ffz-chat-separator-3d-inset .chat-line:before {
+ border-bottom-color: rgba(255,255,255,0.5);
+ border-top: 1px solid #aaa;
+}
+
.ffz-chat-separator-3d ul.chat-lines div:first-of-type .chat-line:before {
border-top: none;
}
@@ -1066,6 +1087,14 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
border-bottom-color: #000;
}
+.ffz-chat-separator-wide .app-main.theatre .chat-line:before,
+.ffz-chat-separator-wide .chat-container.dark .chat-line:before,
+.ffz-chat-separator-wide .chat-container.force-dark .chat-line:before,
+.ffz-chat-separator-wide .ember-chat-container.dark .chat-line:before,
+.ffz-chat-separator-wide .ember-chat-container.force-dark .chat-line:before {
+ border-top-color: #000;
+}
+
.ffz-chat-separator-3d .app-main.theatre .chat-line:before,
.ffz-chat-separator-3d .chat-container.dark .chat-line:before,
.ffz-chat-separator-3d .chat-container.force-dark .chat-line:before,
@@ -1074,6 +1103,15 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
border-top-color: rgba(255,255,255,0.1);
}
+.ffz-chat-separator-3d-inset .app-main.theatre .chat-line:before,
+.ffz-chat-separator-3d-inset .chat-container.dark .chat-line:before,
+.ffz-chat-separator-3d-inset .chat-container.force-dark .chat-line:before,
+.ffz-chat-separator-3d-inset .ember-chat-container.dark .chat-line:before,
+.ffz-chat-separator-3d-inset .ember-chat-container.force-dark .chat-line:before {
+ border-bottom-color: rgba(255,255,255,0.1);
+ border-top-color: #000;
+}
+
.ffz-chat-background .chat-history .chat-line.ffz-alternate:before,
.ffz-chat-background .ember-chat .chat-messages .chat-line.ffz-alternate:before {
background-color: rgba(0,0,0, 0.1);
@@ -1213,6 +1251,7 @@ body:not(.ffz-chat-purge-icon) .ember-chat .mod-icons .purge { display: none; }
display: block;
width: 80px;
height: 63px;
+
background-image: url("//cdn.frankerfacez.com/script/spinner-dark.png");
margin: 50px auto;
@@ -1514,9 +1553,9 @@ th.ffz-row-switch {
/* Minimalistic Chat */
-body.ffz-minimal-chat .ember-chat .chat-header,
-body.ffz-minimal-chat .ember-chat #ffz-group-tabs,
-body.ffz-minimal-chat .ember-chat .chat-buttons-container {
+body.ffz-minimal-chat-head .ember-chat > .chat-header,
+body.ffz-minimal-chat-head .ember-chat #ffz-group-tabs,
+body.ffz-minimal-chat-input .ember-chat .chat-buttons-container {
display: none !important;
}
@@ -1525,30 +1564,30 @@ body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
bottom: 33px;
}*/
-body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector {
+body.ffz-minimal-chat-input .ember-chat .chat-interface .emoticon-selector {
right: 10px;
}
-body.ffz-minimal-chat .ember-chat .chat-interface .emoticon-selector .dropmenu {
+body.ffz-minimal-chat-input .ember-chat .chat-interface .emoticon-selector .dropmenu {
margin-bottom: 10px;
}
-body.ffz-minimal-chat .ember-chat .chat-room {
+body.ffz-minimal-chat-head .ember-chat .chat-room {
top: 0 !important;
}
-body.ffz-minimal-chat .ember-chat .chat-interface {
+body.ffz-minimal-chat-input .ember-chat .chat-interface {
/*height: 33px !important;*/
padding: 0;
}
-body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain {
+body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain {
top: 0 !important;
margin: 0 !important;
height: auto;
}
-body.ffz-minimal-chat .ember-chat .chat-interface .textarea-contain textarea {
+body.ffz-minimal-chat-input .ember-chat .chat-interface .textarea-contain textarea {
/*height: 33px !important;*/
overflow: hidden;
border-bottom: 0 !important;
@@ -1982,4 +2021,119 @@ li[data-name="following"] a {
.ffz-yt-thumb {
max-height: 90px;
+}
+
+/* Classic Player */
+
+.ffz-classic-player .player .player-video {
+ position: absolute;
+ top: 0; bottom: 32px;
+ left: 0; right: 0;
+}
+
+.ffz-classic-player .player.player-isvod .player-video {
+ bottom: 36px;
+}
+
+.ffz-classic-player .player .player-controls-bottom {
+ opacity: 1;
+
+ padding-top: 0;
+ border-top: 1px solid #000;
+ border-bottom: 1px solid #000;
+
+ background: -webkit-linear-gradient(bottom, #252525, #666);
+ background: linear-gradient(to top, #252525, #666);
+}
+
+.ffz-classic-player .app-main.theatre .player .player-video,
+.ffz-classic-player .player[data-fullscreen="true"] .player-video {
+ bottom: 0;
+}
+
+.ffz-classic-player .app-main.theatre .player .player-controls-bottom,
+.ffz-classic-player .player[data-fullscreen="true"] .player-controls-bottom {
+ margin-bottom: -32px;
+ -webkit-transition: margin-bottom .2s ease-out;
+ transition: margin-bottom .2s ease-out;
+}
+
+.ffz-classic-player .app-main.theatre .player.player-isvod .player-controls-bottom,
+.ffz-classic-player .player.player-isvod[data-fullscreen="true"] .player-controls-bottom {
+ margin-bottom: -36px;
+}
+
+.ffz-classic-player .app-main.theatre .player-column:hover .player .player-controls-bottom,
+.ffz-classic-player .app-main.theatre .player-column:focus .player .player-controls-bottom,
+.ffz-classic-player .player[data-fullscreen="true"][data-controls="true"] .player-controls-bottom {
+ margin-bottom: 0;
+}
+
+.ffz-classic-player .player .player-button {
+ padding-bottom: 0;
+ height: 30px;
+}
+
+
+.ffz-classic-player .player .player-slider:before,
+.ffz-classic-player .player .player-button,
+.ffz-classic-player .player .player-slider .ui-slider-handle,
+.ffz-classic-player .player .player-seek .player-seek__time {
+ -webkit-filter: drop-shadow(0px 0px 1px #000);
+ filter: drop-shadow(0px 0px 1px #000);
+}
+
+
+.ffz-classic-player .player .player-slider .ui-slider-handle { background-color: #aeaeae; }
+.ffz-classic-player .player .player-button svg { fill: #aeaeae; }
+.ffz-classic-player .player .player-seek .player-seek__time { color: #ddd; }
+
+.ffz-classic-player .player .player-volume__slider-container {
+ width: auto;
+}
+
+
+.ffz-classic-player .player .player-seek {
+ padding: 0;
+}
+
+.ffz-classic-player .player .player-seek .player-slider {
+ margin: -1em 0;
+}
+
+.ffz-classic-player .player .player-seek .player-seek__time-container {
+ position: absolute;
+ bottom: -12px;
+ left: 210px;
+}
+
+.ffz-classic-player .player .player-seek .player-seek__time + .player-seek__time:before {
+ content: "/";
+ padding: 0 5px;
+ opacity: 0.8;
+}
+
+/* Directory Logos */
+
+.ffz-directory-logo .meta p { width: auto; }
+
+.ffz-directory-logo .profile-photo {
+ float: left;
+ height: 46px;
+ width: 46px;
+ margin-right: 10px;
+}
+
+/* Flip Dashboard */
+
+.ffz-flip-dashboard #dash_main #controls_column {
+ float: right;
+ margin-left: 20px;
+ margin-right: 0;
+}
+
+.ffz-flip-dashboard #dash_main .dash-chat-column {
+ right: inherit;
+ left: 0;
+ margin-right: 20px;
}
\ No newline at end of file