1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-08-02 16:08:31 +00:00

I'm literally a terrible person. Just LOOK at this commit. I did some stuff. A lot of stuff. Too much stuff.

This commit is contained in:
SirStendec 2015-05-17 19:02:57 -04:00
parent e1cfb17081
commit 576c9569b2
22 changed files with 1977 additions and 454 deletions

362
dark.css
View file

@ -37,6 +37,7 @@
}
/* vod icons */
.ffz-dark #stats .stat svg:not(.svg-glyph_live) path,
.ffz-dark #main_col .content #stats_and_actions #channel_stats .stat svg path{
fill:rgba(255,255,255,0.5)!important;
}
@ -50,7 +51,7 @@
/* dropdown arrows */
.ffz-dark .button.drop:after{
border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
}
/* hovering buttons */
@ -62,6 +63,29 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
color: #fff !important;
}
/* moderation card */
.ember-chat-container.dark .ember-chat .ffz-moderation-card,
.chat-container.dark .ember-chat .ffz-moderation-card,
.app-main.theatre .ember-chat .ffz-moderation-card {
border-color: #1b1b20;
}
.ember-chat-container.dark .ember-chat .moderation-card:focus,
.chat-container.dark .ember-chat .moderation-card:focus,
.app-main.theatre .ember-chat .moderation-card:focus {
border-color: #cbcbcb;
}
.ember-chat-container.dark .ember-chat .moderation-card .interface,
.chat-container.dark .ember-chat .moderation-card .interface,
.app-main.theatre .ember-chat .moderation-card .interface {
background-color: #232329;
}
.moderation-card h3.name a { color: #fff !important; }
/* stats */
.ffz-dark #channel .player-column .stats-and-actions .channel-stats .stat svg:not(.svg-glyph_live) path{
fill:rgba(255,255,255,0.35)!important;
@ -75,7 +99,7 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
/* main column */
.ffz-dark div#main_col.column{
.ffz-dark div#main_col {
background:rgb(16,16,16)!important;
color:rgb(195,195,195)!important;
border-right:1px solid rgb(0,0,0)!important;
@ -114,8 +138,10 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
/* Popups */
.ffz-dark .ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box,
.ffz-dark .card,
.ffz-dark #flyout .content,
.ffz-dark .ui-menu,
.ffz-dark .dropmenu,
.ffz-dark .top-dropdown,
.ffz-dark form.js-new_panel_form,
@ -127,6 +153,10 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
border-color: #32323e;
}
.ffz-dark .js-new_panel_btn:hover {
background-color: rgb(24,24,24);
}
.ffz-dark #flyout .point::before {
border-right-color: rgb(16,16,16); /*rgb(25,25,31);*/
}
@ -153,6 +183,18 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
/* Other stuff */
.ffz-dark #channel .player-column #broadcast-meta .info .edit-link span {
color: inherit;
}
.ffz-dark .no-login-contain li {
background-color: rgb(25,25,31);
}
.ffz-dark .no-login-contain h3 {
color: #ccc;
}
.ffz-dark .panel-formatting .panel {
color: #8c8c9c;
}
@ -160,10 +202,14 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
.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(.switch):not(.follow) {
.ffz-dark a:not(.button):not(.switch):not(.follow):not(.fb_button) {
color: #a68ed2;
}
.ffz-dark a.dropmenu_action:hover {
color: #fff !important;
}
.ffz-dark .panel-formatting .panel h3 {
color: inherit;
}
@ -201,6 +247,15 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
border-color: rgb(16,16,16);
}
/* Upsell banner */
.ffz-dark .upsell-banner {
background-color: rgb(25,25,31);
}
.ffz-dark .upsell-banner .message .title {
color: #ccc;
}
/* Video Manager */
.ffz-dark .manager .videos-grid .video .meta .actions {
@ -257,6 +312,16 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
/* Profile page fixes */
.ffz-dark pre {
background-color: rgb(8,8,8);
box-shadow: inset 0 0 0 1px #333;
border-left-color: #6441a5;
}
.ffz-dark code {
color: #999;
}
.ffz-dark .streams .stream .content .thumb .boxart,
.ffz-dark .videos .video .content .thumb .boxart {
border-color: rgb(16,16,16);
@ -269,7 +334,7 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
.ffz-dark .items-grid .meta .title,
.ffz-dark .items-grid .meta p a {
color: #9c9c9c;
color: #9c9c9c !important;
}
.ffz-dark .items-grid .meta .info {
@ -316,6 +381,15 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
box-shadow: inset 0 0 0 1px #32323e;
}
/* Autocomplete Suggestions */
.ffz-dark .ember-chat .chat-interface .suggestions {
background-color: rgb(16,16,16);
border-color: rgba(255,255,255,0.2);
}
/* FrankerFaceZ Menu */
.ffz-dark .ember-chat .chat-menu .list-header {
@ -351,3 +425,283 @@ border-color: rgba(255, 255, 255, 0.35) transparent transparent!important;;
.ffz-dark #new-user-prompt .message .title {
color: #fff;
}
/* Messages Table */
.ffz-dark .messages #send_message_form #bottom_buttons,
.messages #reply_message_form #bottom_buttons {
border-color: rgba(255,255,255,0.2);
box-shadow: none;
}
.ffz-dark .messages #send_message_form .button_group,
.ffz-dark .messages #reply_message_form .button_group,
.ffz-dark .messages #send_message_form,
.ffz-dark .messages #reply_message_form {
background: transparent;
}
.ffz-dark .messages #send_message_form,
.ffz-dark .messages #reply_message_form
{ padding: 10px 0 0 0; }
.ffz-dark .messages #message_actions {
background-color: rgb(48,48,48);
border-bottom-color: rgba(255,255,255,0.2);
}
.ffz-dark .messages #send_message_form,
.ffz-dark .messages .divider {
border-color: rgba(255,255,255,0.2);
}
.ffz-dark .messages .message_action.delete {
display: block;
width: 16px;
height: 16px;
background: url(trash_button.png) no-repeat;
}
.ffz-dark .messages .message_action.delete img { display: none; }
.ffz-dark .messages .message_action.block {
background-image: url(block_button.png);
color: #c1c1c1 !important;
}
.ffz-dark .messages div.message {
border-bottom-color: rgba(255,255,255,0.2);
}
.ffz-dark .messages div.message:last-of-type { border-bottom-color: transparent; }
.ffz-dark .messages div.preview {
background-color: rgb(16,16,16);
border-bottom-color: rgba(255,255,255,0.2);
border-left-color: transparent;
}
.ffz-dark .messages div.preview.unread {
background-color: rgb(32,32,32);
border-left-color: #6441a5;
}
.ffz-dark .messages div.preview:hover {
background-color: rgb(8,8,8);
}
.ffz-dark .messages #messages_column {
background-color: transparent;
}
.ffz-dark .page_links em {
background-color: #EEE;
color: #000;
}
.ffz-dark .page_links a.disabled.previous_page b,
.ffz-dark .page_links span.disabled.previous_page b { border-right-color: transparent; }
.ffz-dark .page_links a.disabled.next_page b,
.ffz-dark .page_links span.disabled.next_page b { border-left-color: transparent; }
/* Tshirts are weird */
.ffz-dark .teespring-panel-progress {
background-color: #393939;
}
.ffz-dark .teespring-panel-image a {
cursor: pointer;
}
.ffz-dark .teespring-panel-image img {
filter: drop-shadow(0px 0px 10px white);
-moz-filter: drop-shadow(0px 0px 10px white);
-webkit-filter: drop-shadow(0px 0px 10px white);
}
.ffz-dark .teespring-panel {
cursor: inherit;
background-color: rgb(8,8,8);
border-color: rgba(255,255,255,0.2);
}
/* Settings */
.ffz-dark .app_title { color: #ccc; }
.ffz-dark .connect_items .connect-item-info .details-toggle,
.ffz-dark .connect_items .connect-item-info,
.ffz-dark ul.manage_simple span.obj {
background-color: rgb(8,8,8);
}
.ffz-dark .connect_items .connect-item-info .details-toggle {
border-color: rgba(255,255,255,0.2);
}
.ffz-dark .connect_items .connect-item-info .details-toggle:hover {
border-color: rgba(255,255,255,0.4);
}
.ffz-dark .connect_items .connect-item-info .status,
.ffz-dark #settings #followers_content .hdr,
.ffz-dark table.simple_table th {
background-color: rgb(32,32,32);
border-color: transparent;
}
.ffz-dark .multi_select li form:hover,
.ffz-dark .multi_select li .ms-int:hover,
.ffz-dark .multi_select li label:hover {
background-color: rgb(8,8,8);
}
.ffz-dark #turbo_chat_color,
.ffz-dark #turbo_emote_set {
background-color: rgba(255,255,255,0.05);
}
.ffz-dark .ui-slider-horizontal {
background-color: rgba(255,255,255,0.2);
}
.ffz-dark .cl-container .section-header,
.ffz-dark .cl-container .cl-section .cl-subheader {
background-color: rgb(32,32,32);
}
.ffz-dark .cl-container .cl-section {
background-color: rgb(16,16,16);
}
.ffz-dark table.simple_table td,
.ffz-dark table.simple_table th,
.ffz-dark .cl-container .section-header,
.ffz-dark .cl-container fieldset {
border-bottom-color: rgba(255,255,255,0.2);
}
.ffz-dark .cl-container fieldset .label-wrapper label {
color: #ccc;
}
.ffz-dark #emoticons #emote_switch .set-button,
.ffz-dark #example .line {
background-color: #19191f;
color: #acacbf;
}
/* Dashboard */
.ffz-dark .js-show-stream-key,
.ffz-dark .js-reset-stream-key {
display: block;
margin: 15px auto;
padding: 0;
text-align: center;
width: 100px;
color: #fff !important;
}
.ffz-dark .js-show-stream-key { background-color: #804400; }
.ffz-dark .js-reset-stream-key { r-color: rgb(128,0,0); }
.ffz-dark .js-show-stream-key:hover,
.ffz-dark .js-show-stream-key:focus,
.ffz-dark .js-reset-stream-key:hover,
.ffz-dark .js-reset-stream-key:focus {
padding: 5px;
margin: 10px auto;
}
.ffz-dark .js-show-stream-key:hover,
.ffz-dark .js-show-stream-key:focus { background-color: rgb(192,102,0); }
.ffz-dark .js-reset-stream-key:hover,
.ffz-dark .js-reset-stream-key:focus { background-color: rgb(192,0,0); }
.ffz-dark .statchart_aggregator .statchart_first_column {
background-color: rgb(32,32,32);
color: #ccc;
text-shadow: none;
}
.ffz-dark .statchart_aggregator {
margin-top: 10px;
}
.ffz-dark .statchart_aggregator td {
border-color: #3f3f42;
}
.ffz-dark .statchart_aggregator .statchart_first_column[style="color: #6441A5"] {
color: #a68ed2 !important;
}
.ffz-dark .whatisthis {
background-color: #333;
box-shadow: 0 0 0 1px rgba(255,255,255,0.2);
}
.ffz-dark #chart_container svg rect[fill="#FFFFFF"] {
fill: rgb(32,32,32) !important;
}
.ffz-dark #chart_container svg rect[fill="rgb(255,255,255)"] {
fill: rgb(8,8,8) !important;
}
.ffz-dark #chart_container svg text[x="5"] {
color: #fff !important;
fill: #fff !important;
}
.ffz-dark #chart_container svg text.highcharts-title {
fill: #a68ed2 !important;
}
.ffz-dark #chart_container svg text {
fill: #fff;
}
.ffz-dark ul.subtabs li.selected {
background-color: #333;
border-radius: 4px;
}
.ffz-dark .dropmenu .menu-divider {
border-bottom-color: rgba(255,255,255,0.2);
}
.ffz-dark #header_logo svg path {
fill: #fff;
}
.ffz-dark #dash_main .action .content .data,
.ffz-dark #site_header {
background-color: rgb(32,32,32);
}
.ffz-dark .header_divider {
border-right-color: rgba(255,255,255,0.2);
}
.ffz-dark .dash-hostmode-list-contain {
border-top-color: rgba(255,255,255,0.2);
}
.ffz-dark #dash_main #controls_column .section_header {
color: #ccc;
}
.ffz-dark .main,
.ffz-dark .fullwidth_main {
color: #ccc;
background-color: transparent;
}
.ffz-dark .dash-chat-column {
border-color: rgba(255,255,255,0.2);
}

888
script.js

File diff suppressed because it is too large Load diff

6
script.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -9,6 +9,7 @@ module.exports = {
ZREKNARF: '<svg style="padding:1.75px 0" class="svg-glyph_views" width="16px" viewBox="0 0 249 195" version="1.1" height="12.5px">' + SVGPATH + '</svg>',
CHAT_BUTTON: '<svg class="svg-emoticons ffz-svg" height="18px" width="24px" viewBox="0 0 249 195" version="1.1">' + SVGPATH + '</svg>',
CLOCK: '<svg class="svg-glyph_views svg-clock" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path fill-rule="evenodd" clip-rule="evenodd" fill="#888888" d="M8,15c-3.866,0-7-3.134-7-7s3.134-7,7-7s7,3.134,7,7 S11.866,15,8,15z M8,3C5.238,3,3,5.238,3,8s2.238,5,5,5s5-2.238,5-5S10.762,3,8,3z M7.293,8.707L7,8l1-4l0.902,3.607L11,11 L7.293,8.707z"/></svg>',
GEAR: '<svg class="svg-gear" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M15,7v2h-2.115c-0.125,0.615-0.354,1.215-0.713,1.758l1.484,1.484l-1.414,1.414l-1.484-1.484C10.215,12.531,9.615,12.76,9,12.885V15H7v-2.12c-0.614-0.126-1.21-0.356-1.751-0.714l-1.491,1.49l-1.414-1.414l1.491-1.49C3.477,10.211,3.247,9.613,3.12,9H1V7h2.116C3.24,6.384,3.469,5.785,3.829,5.242L2.343,3.757l1.414-1.414l1.485,1.485C5.785,3.469,6.384,3.24,7,3.115V1h2v2.12c0.613,0.126,1.211,0.356,1.752,0.714l1.49-1.491l1.414,1.414l-1.49,1.492C12.523,5.79,12.754,6.387,12.88,7H15z M8,6C6.896,6,6,6.896,6,8s0.896,2,2,2s2-0.896,2-2S9.104,6,8,6z" fill-rule="evenodd"></path></svg>',
HEART: '<svg class="svg-heart" height="16px" version="1.1" viewBox="0 0 16 16" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M8,13.5L1.5,7V4l2-2h3L8,3.5L9.5,2h3l2,2v3L8,13.5z" fill-rule="evenodd"></path></svg>',
EMOTE: '<svg class="svg-emote" height="16px" version="1.1" viewBox="0 0 18 18" width="16px" x="0px" y="0px"><path clip-rule="evenodd" d="M9,18c-4.971,0-9-4.029-9-9s4.029-9,9-9s9,4.029,9,9S13.971,18,9,18z M14,4.111V4h-0.111C12.627,2.766,10.904,2,9,2C7.095,2,5.373,2.766,4.111,4H4v0.111C2.766,5.373,2,7.096,2,9s0.766,3.627,2,4.889V14l0.05-0.051C5.317,15.217,7.067,16,9,16c1.934,0,3.684-0.783,4.949-2.051L14,14v-0.111c1.234-1.262,2-2.984,2-4.889S15.234,5.373,14,4.111zM11,6h2v4h-2V6z M12.535,12.535C11.631,13.44,10.381,14,9,14s-2.631-0.56-3.536-1.465l0.707-0.707C6.896,12.553,7.896,13,9,13s2.104-0.447,2.828-1.172L12.535,12.535z M5,6h2v4H5V6z" fill-rule="evenodd"></path></svg>'

171
src/ember/channel.js Normal file
View file

@ -0,0 +1,171 @@
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
constants = require('../constants');
// --------------------
// Initialization
// --------------------
FFZ.prototype.setup_channel = function() {
this.channels = {};
this.log("Creating channel style element.");
var s = this._channel_style = document.createElement('style');
s.id = "ffz-channel-css";
document.head.appendChild(s);
this.log("Hooking the Ember Channel view.");
var Channel = App.__container__.lookup('controller:channel'),
f = this;
if ( ! Channel )
return;
Channel.reopen({
ffzUpdateUptime: function() {
f.update_uptime();
}.observes("isLive", "content.id").on("init")
/*ffzUpdateInfo: function() {
f.log("Updated! ID: " + this.get("content.id"));
f.update_stream_info(true);
}.observes("content.id").on("init")*/
});
// Do uptime the first time.
this.update_uptime();
//this.update_stream_info(true);
}
// ---------------
// Settings
// ---------------
FFZ.settings_info.stream_uptime = {
type: "boolean",
value: false,
category: "Channel Metadata",
name: "Stream Uptime",
help: 'Display the stream uptime under a channel by the viewer count.',
on_update: function(val) {
this.update_uptime();
}
};
// --------------------
// Stream Data Update
// --------------------
/*FFZ.prototype.update_stream_info = function(just_schedule) {
if ( this._stream_info_update ) {
clearTimeout(this._stream_info_update);
delete this._stream_info_update;
}
this._stream_info_update = setTimeout(this.update_stream_info.bind(this), 90000);
if ( just_schedule )
return;
var Channel = App.__container__.lookup('controller:channel'),
channel_id = Channel ? Channel.get('content.id') : undefined,
f = this;
if ( ! channel_id )
return;
Twitch.api.get("streams/" + channel_id, {}, {version: 3})
.done(function(data) {
var channel_id = Channel.get('content.id'), d = data.stream;
if ( ! data.stream || d.channel.name != channel_id )
return;
// Override the data in Twitch. We can't just .load() the stream
// because that resets the whole channel layout, resetting the
// video player. Twitch pls fix
var old_created = Channel.get('content.stream.created_at');
Channel.set('content.stream.created_at', d.created_at);
Channel.set('content.stream.average_fps', d.average_fps);
Channel.set('content.stream.viewers', d.viewers);
Channel.set('content.stream.video_height', d.video_height);
Channel.set('content.stream.csGoSkill', Twitch.uri.csGoSkillImg(("0" + d.skill).slice(-2)));
Channel.set('content.stream.game', d.game);
Channel.set('content.stream.gameUrl', Twitch.uri.game(d.game));
Channel.set('content.stream.gameBoxart', Twitch.uri.gameBoxArtJpg(d.game));
// Update the uptime display.
if ( f.settings.stream_uptime && old_created != d.created_at )
f.update_uptime(true) && f.update_uptime();
});
}*/
// --------------------
// Uptime Display
// --------------------
FFZ.prototype.update_uptime = function(destroy) {
if ( this._uptime_update ) {
clearTimeout(this._uptime_update);
delete this._uptime_update;
}
var Channel = App.__container__.lookup('controller:channel');
if ( destroy || ! this.settings.stream_uptime || ! Channel || ! Channel.get('isLiveAccordingToKraken') ) {
var el = document.querySelector("#ffz-uptime-display");
if ( el )
el.parentElement.removeChild(el);
return;
}
// Schedule an update.
this._update_uptime = setTimeout(this.update_uptime.bind(this), 1000);
// Determine when the channel last went live.
var online = Channel.get('content.stream.created_at');
if ( ! online ) return;
online = utils.parse_date(online);
if ( ! online ) return;
var uptime = Math.floor((Date.now() - online.getTime()) / 1000);
if ( uptime < 0 ) return;
var el = document.querySelector("#ffz-uptime-display span");
if ( ! el ) {
var cont = document.querySelector("#channel .stats-and-actions .channel-stats");
if ( ! cont ) return;
var stat = document.createElement("span");
stat.className = "ffz stat";
stat.id = "ffz-uptime-display";
stat.title = "Stream Uptime <nobr>(since " + online.toLocaleString() + ")</nobr>";
stat.innerHTML = constants.CLOCK + " ";
el = document.createElement("span");
stat.appendChild(el);
var viewers = cont.querySelector(".live-count");
if ( viewers )
cont.insertBefore(stat, viewers.nextSibling);
else {
try {
viewers = cont.querySelector("script:nth-child(0n+2)");
cont.insertBefore(stat, viewers.nextSibling);
} catch(err) {
cont.insertBefore(stat, cont.childNodes[0]);
}
}
jQuery(stat).tipsy({html:true});
}
el.innerHTML = utils.time_to_string(uptime);
}

View file

@ -1,4 +1,4 @@
var FFZ = window.FrankerFaceZ,
var FFZ = window.FrankerFaceZ,
utils = require("../utils"),
reg_escape = function(str) {
@ -71,6 +71,106 @@ var FFZ = window.FrankerFaceZ,
var images = document.querySelectorAll('img[emote-id="' + id + '"]');
for(var x=0; x < images.length; x++)
images[x].title = tooltip;
},
build_link_tooltip = function(href) {
var link_data = this._link_data[href],
tooltip;
if ( ! link_data )
return "";
if ( link_data.tooltip )
return link_data.tooltip;
if ( link_data.type == "youtube" ) {
tooltip = "<b>YouTube: " + utils.sanitize(link_data.title) + "</b><hr>";
tooltip += "Channel: " + utils.sanitize(link_data.channel) + " | " + utils.time_to_string(link_data.duration) + "<br>";
tooltip += utils.number_commas(link_data.views||0) + " Views | &#128077; " + utils.number_commas(link_data.likes||0) + " &#128078; " + utils.number_commas(link_data.dislikes||0);
} else if ( link_data.type == "strawpoll" ) {
tooltip = "<b>Strawpoll: " + utils.sanitize(link_data.title) + "</b><hr><table><tbody>";
for(var key in link_data.items) {
var votes = link_data.items[key],
percentage = Math.floor((votes / link_data.total) * 100);
tooltip += '<tr><td style="text-align:left">' + utils.sanitize(key) + '</td><td style="text-align:right">' + utils.number_commas(votes) + "</td></tr>";
}
tooltip += "</tbody></table><hr>Total: " + utils.number_commas(link_data.total);
var fetched = utils.parse_date(link_data.fetched);
if ( fetched ) {
var age = Math.floor((fetched.getTime() - Date.now()) / 1000);
if ( age > 60 )
tooltip += "<br><small>Data was cached " + utils.time_to_string(age) + " ago.</small>";
}
} else if ( link_data.type == "twitch" ) {
tooltip = "<b>Twitch: " + utils.sanitize(link_data.display_name) + "</b><hr>";
var since = utils.parse_date(link_data.since);
if ( since )
tooltip += "Member Since: " + utils.date_string(since) + "<br>";
tooltip += "<nobr>Views: " + utils.number_commas(link_data.views) + "</nobr> | <nobr>Followers: " + utils.number_commas(link_data.followers) + "</nobr>";
} else if ( link_data.type == "twitter" ) {
tooltip = "<b>Tweet By: " + utils.sanitize(link_data.user) + "</b><hr>";
tooltip += utils.sanitize(link_data.tweet);
} else if ( link_data.type == "reputation" ) {
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
if ( link_data.trust < 50 || link_data.safety < 50 || (link_data.tags && link_data.tags.length > 0) ) {
tooltip += "<hr>";
var had_extra = false;
if ( link_data.trust < 50 || link_data.safety < 50 ) {
link_data.unsafe = true;
tooltip += "<b>Potentially Unsafe Link</b><br>";
tooltip += "Trust: " + link_data.trust + "% | Child Safety: " + link_data.safety + "%";
had_extra = true;
}
if ( link_data.tags && link_data.tags.length > 0 )
tooltip += (had_extra ? "<br>" : "") + "Tags: " + link_data.tags.join(", ");
tooltip += "<br>Data Source: WOT";
}
} else if ( link_data.full )
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(link_data.full.toLowerCase()) + '</span>';
if ( ! tooltip )
tooltip = '<span style="word-wrap: break-word">' + utils.sanitize(href.toLowerCase()) + '</span>';
link_data.tooltip = tooltip;
return tooltip;
},
load_link_data = function(href, success, data) {
if ( ! success )
return;
this._link_data[href] = data;
data.unsafe = false;
var tooltip = build_link_tooltip.bind(this)(href), links,
no_trail = href.charAt(href.length-1) == "/" ? href.substr(0, href.length-1) : null;
if ( no_trail )
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[href="' + no_trail + '"], span.message a[data-url="' + href + '"], span.message a[data-url="' + no_trail + '"]');
else
links = document.querySelectorAll('span.message a[href="' + href + '"], span.message a[data-url="' + href + '"]');
if ( ! this.settings.link_info )
return;
for(var x=0; x < links.length; x++) {
if ( data.unsafe )
links[x].classList.add('unsafe-link');
if ( ! links[x].classList.contains('deleted-link') )
links[x].title = tooltip;
}
};
@ -78,24 +178,13 @@ var FFZ = window.FrankerFaceZ,
// Settings
// ---------------------
FFZ.settings_info.capitalize = {
type: "boolean",
value: true,
category: "Chat",
visible: function() { return ! this.has_bttv },
name: "Username Capitalization",
help: "Display names in chat with proper capitalization."
};
FFZ.settings_info.banned_words = {
type: "button",
value: [],
category: "Chat",
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Banned Words",
help: "Set a list of words that will be locally removed from chat messages.",
@ -126,7 +215,8 @@ FFZ.settings_info.keywords = {
value: [],
category: "Chat",
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Highlight Keywords",
help: "Set additional keywords that will be highlighted in chat.",
@ -158,7 +248,8 @@ FFZ.settings_info.fix_color = {
value: true,
category: "Chat",
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Adjust Username Colors",
help: "Ensure that username colors contrast with the background enough to be readable.",
@ -172,12 +263,26 @@ FFZ.settings_info.fix_color = {
};
FFZ.settings_info.link_info = {
type: "boolean",
value: true,
category: "Chat",
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Link Tooltips <span>Beta</span>",
help: "Check links against known bad websites, unshorten URLs, and show YouTube info."
};
FFZ.settings_info.chat_rows = {
type: "boolean",
value: false,
category: "Chat",
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Chat Line Backgrounds",
help: "Display alternating background colors for lines in chat.",
@ -211,6 +316,7 @@ FFZ.prototype.setup_line = function() {
// Emoticon Data
this._twitch_emotes = {};
this._link_data = {};
this.log("Hooking the Ember Line controller.");
@ -232,6 +338,11 @@ FFZ.prototype.setup_line = function() {
if ( ! user || this.get("model.from") != user.login )
tokens = f._mentionize(this, tokens);
// Store the capitalization.
var display = this.get("model.tags.display-name");
if ( display && display.length )
FFZ.capitalization[this.get("model.from")] = [display.trim(), Date.now()];
var end = performance.now();
if ( end - start > 5 )
f.log("Tokenizing Message Took Too Long - " + (end-start) + "ms", tokens, false, true);
@ -265,7 +376,6 @@ FFZ.prototype.setup_line = function() {
row_type = controller.get('model.ffz_alternate');
// Color Processing
if ( color )
f._handle_color(color);
@ -290,11 +400,6 @@ FFZ.prototype.setup_line = function() {
f.render_badge(this);
// Capitalization
if ( f.settings.capitalize )
f.capitalize(this, user);
// Mention Highlighting
var mentioned = el.querySelector('span.mentioned');
if ( mentioned ) {
@ -357,12 +462,52 @@ FFZ.prototype.setup_line = function() {
this.target = "_new";
this.textContent = link;
// Now, check for a tooltip.
var link_data = f._link_data[link];
if ( link_data && typeof link_data != "boolean" ) {
this.title = link_data.tooltip;
if ( link_data.unsafe )
this.classList.add('unsafe-link');
}
// Stop from Navigating
e.preventDefault();
});
// Also add a nice tooltip.
jQuery(link).tipsy();
jQuery(link).tipsy({html:true});
}
// Link Tooltips
if ( f.settings.link_info ) {
var links = el.querySelectorAll("span.message a");
for(var i=0; i < links.length; i++) {
var link = links[i],
href = link.href,
deleted = false;
if ( link.classList.contains("deleted-link") ) {
href = link.getAttribute("data-url");
deleted = true;
}
// Check the cache.
var link_data = f._link_data[href];
if ( link_data ) {
if ( !deleted && typeof link_data != "boolean" )
link.title = link_data.tooltip;
if ( link_data.unsafe )
link.classList.add('unsafe-link');
} else if ( ! /^mailto:/.test(href) ) {
f._link_data[href] = true;
f.ws_send("get_link", href, load_link_data.bind(f, href));
}
}
jQuery(links).tipsy({html:true});
}
@ -536,16 +681,6 @@ FFZ.get_capitalization = function(name, callback) {
}
FFZ.prototype.capitalize = function(view, user) {
var name = FFZ.get_capitalization(user, this.capitalize.bind(this, view));
if ( !name || !view )
return;
var from = view.$('.from');
from && from.text(name);
}
// ---------------------
// Extra Mentions
// ---------------------

View file

@ -5,7 +5,8 @@ var FFZ = window.FrankerFaceZ,
ESC: 27,
P: 80,
B: 66,
T: 84
T: 84,
U: 85
},
btns = [
@ -27,7 +28,8 @@ FFZ.settings_info.enhanced_moderation = {
type: "boolean",
value: false,
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
category: "Chat",
name: "Enhanced Moderation",
@ -41,32 +43,33 @@ FFZ.settings_info.enhanced_moderation = {
FFZ.prototype.setup_mod_card = function() {
this.log("Hooking the Ember Moderation Card view.");
var Card = App.__container__.resolve('view:moderation-card'),
var Card = App.__container__.resolve('component:moderation-card'),
f = this;
Card.reopen({
didInsertElement: function() {
this._super();
window._card = this;
try {
if ( f.has_bttv || ! f.settings.enhanced_moderation )
return;
var el = this.get('element'),
controller = this.get('context');
controller = this.get('controller');
// Style it!
el.classList.add('ffz-moderation-card');
// Only do the big stuff if we're mod.
if ( controller.get('parentController.model.isModeratorOrHigher') ) {
if ( controller.get('cardInfo.isModeratorOrHigher') ) {
el.classList.add('ffz-is-mod');
el.setAttribute('tabindex', 1);
// Key Handling
el.addEventListener('keyup', function(e) {
var key = e.keyCode || e.which,
user_id = controller.get('model.user.id'),
room = controller.get('parentController.model');
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");
@ -77,6 +80,9 @@ FFZ.prototype.setup_mod_card = function() {
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;
@ -89,8 +95,8 @@ FFZ.prototype.setup_mod_card = function() {
line.className = 'interface clearfix';
var btn_click = function(timeout) {
var user_id = controller.get('model.user.id'),
room = controller.get('parentController.model');
var user_id = controller.get('cardInfo.user.id'),
room = App.__container__.lookup('controller:chat').get('currentRoom');
if ( timeout === -1 )
room.send("/unban " + user_id);
@ -151,8 +157,9 @@ FFZ.prototype.setup_mod_card = function() {
// More Fixing Other Buttons
var op_btn = el.querySelector('button.mod');
if ( op_btn ) {
var model = controller.get('parentController.model'),
can_op = model.get('isBroadcaster') || model.get('isStaff') || model.get('isAdmin');
var is_owner = controller.get('cardInfo.isChannelOwner'),
user = ffz.get_user();
can_op = is_owner || (user && user.is_admin) || (user && user.is_staff);
if ( ! can_op )
op_btn.parentElement.removeChild(op_btn);
@ -160,7 +167,7 @@ FFZ.prototype.setup_mod_card = function() {
var msg_btn = el.querySelector(".interface > button");
if ( msg_btn && msg_btn.className == "button" ) {
if ( msg_btn && msg_btn.classList.contains("message-button") ) {
msg_btn.innerHTML = MESSAGE;
msg_btn.classList.add('glyph-only');
msg_btn.classList.add('message');

View file

@ -28,6 +28,16 @@ FFZ.prototype.setup_room = function() {
this.log("Hooking the Ember Room model.");
// Responsive ban button.
var RC = App.__container__.lookup('controller:room');
if ( RC ) {
var orig_action = RC._actions.banUser;
RC._actions.banUser = function(e) {
orig_action.bind(this)(e);
this.get("model").clearMessages(e.user);
}
}
var Room = App.__container__.resolve('model:room');
this._modify_room(Room);
@ -287,7 +297,6 @@ FFZ.prototype._modify_room = function(room) {
var suggestions = this._super();
try {
if ( f.settings.capitalize )
suggestions = _.map(suggestions, FFZ.get_capitalization);
} catch(err) {
f.error("get_suggestions: " + err);

View file

@ -47,7 +47,7 @@ FFZ.prototype._feature_friday_ui = function(room_id, parent, view) {
if ( ! this.feature_friday || this.feature_friday.channel == room_id )
return;
this._emotes_for_sets(parent, view, [this.feature_friday.set], this.feature_friday.title);
this._emotes_for_sets(parent, view, [this.feature_friday.set], this.feature_friday.title, this.feature_friday.icon, "FrankerFaceZ");
// Before we add the button, make sure the channel isn't the
// current channel.

View file

@ -22,7 +22,7 @@ FFZ.get = function() { return FFZ.instance; }
// Version
var VER = FFZ.version_info = {
major: 3, minor: 2, revision: 5,
major: 3, minor: 3, revision: 1,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -93,7 +93,7 @@ FFZ.prototype.get_user = function() {
if ( window.PP && PP.login ) {
return PP;
} else if ( window.App ) {
var nc = App.__container__.lookup("controller:navigation");
var nc = App.__container__.lookup("controller:login");
return nc ? nc.get("userData") : undefined;
}
}
@ -115,6 +115,7 @@ require('./emoticons');
require('./badges');
// Analytics: require('./ember/router');
require('./ember/channel');
require('./ember/room');
require('./ember/line');
require('./ember/chatview');
@ -154,7 +155,17 @@ FFZ.prototype.initialize = function(increment, delay) {
// Make sure that FrankerFaceZ doesn't start setting itself up until the
// Twitch ember application is ready.
// TODO: Special Dashboard check.
// Check for special non-ember pages.
if ( /\/(?:settings|messages?\/)/.test(location.pathname) ) {
this.setup_normal(delay);
return;
}
// Check for the dashboard.
if ( /\/[A-Za-z_-]+\/dashboard/.test(location.pathname) && !/bookmarks$/.test(location.pathname) ) {
this.setup_dashboard(delay);
return;
}
var loaded = window.App != undefined &&
App.__container__ != undefined &&
@ -174,6 +185,65 @@ FFZ.prototype.initialize = function(increment, delay) {
}
FFZ.prototype.setup_normal = function(delay) {
var start = (window.performance && performance.now) ? performance.now() : Date.now();
this.log("Found non-Ember Twitch after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
this.users = {};
// Initialize all the modules.
this.load_settings();
// Start this early, for quick loading.
this.setup_dark();
this.ws_create();
this.setup_emoticons();
this.setup_badges();
this.setup_notifications();
this.setup_css();
this.find_bttv(10);
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
duration = end - start;
this.log("Initialization complete in " + duration + "ms");
}
FFZ.prototype.is_dashboard = false;
FFZ.prototype.setup_dashboard = function(delay) {
var start = (window.performance && performance.now) ? performance.now() : Date.now();
this.log("Found Twitch Dashboard after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
this.users = {};
this.is_dashboard = true;
// Initialize all the modules.
this.load_settings();
// Start this early, for quick loading.
this.setup_dark();
this.ws_create();
this.setup_emoticons();
this.setup_badges();
this.setup_notifications();
this.setup_css();
this.find_bttv(10);
var end = (window.performance && performance.now) ? performance.now() : Date.now(),
duration = end - start;
this.log("Initialization complete in " + duration + "ms");
}
FFZ.prototype.setup_ember = function(delay) {
var start = (window.performance && performance.now) ? performance.now() : Date.now();
this.log("Found Twitch application after " + (delay||0) + " ms in \"" + location + "\". Initializing FrankerFaceZ version " + FFZ.version_info);
@ -193,6 +263,7 @@ FFZ.prototype.setup_ember = function(delay) {
//this.setup_piwik();
//this.setup_router();
this.setup_channel();
this.setup_room();
this.setup_line();
this.setup_chatview();

View file

@ -58,6 +58,19 @@ FFZ.prototype.load_settings = function() {
// Menu Page
// --------------------
FFZ.settings_info.replace_twitch_menu = {
type: "boolean",
value: false,
name: "Replace Twitch Emoticon Menu <span>Beta</span>",
help: "Completely replace the default Twitch emoticon menu.",
on_update: function(val) {
document.body.classList.toggle("ffz-menu-replace", val);
}
};
FFZ.menu_pages.settings = {
render: function(view, container) {
var settings = {},
@ -141,6 +154,21 @@ FFZ.menu_pages.settings = {
el.className = 'clearfix';
if ( this.has_bttv && info.no_bttv ) {
var label = document.createElement('span'),
help = document.createElement('span');
label.className = 'switch-label';
label.innerHTML = info.name;
help = document.createElement('span');
help.className = 'help';
help.innerHTML = 'Disabled due to incompatibility with BetterTTV.';
el.classList.add('disabled');
el.appendChild(label);
el.appendChild(help);
} else {
if ( info.type == "boolean" ) {
var swit = document.createElement('a'),
label = document.createElement('span');
@ -173,6 +201,7 @@ FFZ.menu_pages.settings = {
help.innerHTML = info.help;
el.appendChild(help);
}
}
menu.appendChild(el);
}
@ -183,7 +212,8 @@ FFZ.menu_pages.settings = {
name: "Settings",
icon: constants.GEAR,
sort_order: 99999
sort_order: 99999,
wide: true
};
@ -195,8 +225,6 @@ FFZ.prototype._setting_update = function(e) {
if ( ! e )
e = window.event;
this.log("Storage Event", e);
if ( ! e.key || e.key.substr(0, 12) !== "ffz_setting_" )
return;

View file

@ -36,6 +36,13 @@ FFZ.prototype.ws_create = function() {
if ( user )
f.ws_send("setuser", user.login);
// Join the right channel if we're in the dashboard.
if ( f.is_dashboard ) {
var match = location.pathname.match(/\/([^\/]+)/);
if ( match )
f.ws_send("sub", match[1]);
}
// Send the current rooms.
for(var room_id in f.rooms)
f.rooms.hasOwnProperty(room_id) && f.ws_send("sub", room_id);

View file

@ -37,6 +37,18 @@ FFZ.menu_pages.about = {
heading.innerHTML = content;
container.appendChild(heading);
var clicks = 0, head = heading.querySelector("h1");
head && head.addEventListener("click", function() {
head.style.cursor = "pointer";
clicks++;
if ( clicks >= 3 ) {
clicks = 0;
var el = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
el && el.classList.toggle('ffz-flip');
}
setTimeout(function(){clicks=0;head.style.cursor=""},2000);
});
// Advertising
var btn_container = document.createElement('div'),

View file

@ -17,9 +17,10 @@ FFZ.settings_info.dark_twitch = {
type: "boolean",
value: false,
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Dark Twitch <span>Beta</span>",
name: "Dark Twitch",
help: "Apply a dark background to channels and other related pages for easier viewing.",
on_update: function(val) {
@ -28,14 +29,14 @@ FFZ.settings_info.dark_twitch = {
document.body.classList.toggle("ffz-dark", val);
var model = App.__container__.lookup('controller:settings').get('model');
var model = window.App ? App.__container__.lookup('controller:settings').get('model') : undefined;
if ( val ) {
this._load_dark_css();
this.settings.set('twitch_chat_dark', model.get('darkMode'));
model.set('darkMode', true);
model && this.settings.set('twitch_chat_dark', model.get('darkMode'));
model && model.set('darkMode', true);
} else
model.set('darkMode', this.settings.twitch_chat_dark);
model && model.set('darkMode', this.settings.twitch_chat_dark);
}
};
@ -50,7 +51,7 @@ FFZ.prototype.setup_dark = function() {
document.body.classList.toggle("ffz-dark", this.settings.dark_twitch);
if ( this.settings.dark_twitch )
App.__container__.lookup('controller:settings').set('model.darkMode', true);
window.App && App.__container__.lookup('controller:settings').set('model.darkMode', true);
if ( this.settings.dark_twitch )
this._load_dark_css();

View file

@ -1,5 +1,8 @@
var FFZ = window.FrankerFaceZ,
constants = require('../constants');
constants = require('../constants'),
utils = require('../utils'),
TWITCH_BASE = "http://static-cdn.jtvnw.net/emoticons/v1/";
// --------------------
@ -23,6 +26,8 @@ FFZ.prototype.setup_menu = function() {
delete f._popup_kill;
}
});
document.body.classList.toggle("ffz-menu-replace", this.settings.replace_twitch_menu);
}
@ -108,27 +113,31 @@ FFZ.prototype.build_ui_popup = function(view) {
jQuery(link).tipsy();
link.addEventListener("click", this._ui_change_page.bind(this, view, menu, sub_container, key));
link.addEventListener("click", this._ui_change_page.bind(this, view, inner, menu, sub_container, key));
el.appendChild(link);
menu.appendChild(el);
}
// Render Current Page
this._ui_change_page(view, menu, sub_container, this._last_page || "channel");
this._ui_change_page(view, inner, menu, sub_container, this._last_page || "channel");
// Add the menu to the DOM.
this._popup = container;
sub_container.style.maxHeight = Math.max(100, view.$().height() - 162) + "px";
sub_container.style.maxHeight = Math.max(200, view.$().height() - 172) + "px";
view.$('.chat-interface').append(container);
}
FFZ.prototype._ui_change_page = function(view, menu, container, page) {
FFZ.prototype._ui_change_page = function(view, inner, menu, container, page) {
this._last_page = page;
container.innerHTML = "";
container.setAttribute('data-page', page);
// Allow settings to be wide. We need to know if chat is stand-alone.
var app = document.querySelector(".app-main") || document.querySelector(".ember-chat-container");
inner.style.maxWidth = (!FFZ.menu_pages[page].wide || (typeof FFZ.menu_pages[page].wide == "function" && !FFZ.menu_pages[page].wide.bind(this)())) ? "" : (app.offsetWidth < 640 ? (app.offsetWidth-40) : 600) + "px";
var els = menu.querySelectorAll('li.active');
for(var i=0; i < els.length; i++)
els[i].classList.remove('active');
@ -143,55 +152,6 @@ FFZ.prototype._ui_change_page = function(view, menu, container, page) {
}
// --------------------
// Favorites Page
// --------------------
FFZ.prototype._tokenize_message = function(message, room_id) {
var lc = App.__container__.lookup('controller:line'),
rc = App.__container__.lookup('controller:room'),
room = this.rooms[room_id],
user = this.get_user();
if ( ! lc || ! rc || ! room )
return [message];
rc.set('model', room.room);
lc.set('parentController', rc);
var model = {
from: user && user.login || "FrankerFaceZ",
message: message,
tags: {
emotes: room.room.tmiSession._emotesParser.parseEmotesTag(message)
}
};
lc.set('model', model);
var tokens = lc.get('tokenizedMessage');
lc.set('model', null);
rc.set('model', null);
lc.set('parentController', null);
return tokens;
}
/*FFZ.menu_pages.favorites = {
render: function(view, container) {
// Get the current room.
var room_id = view.get('controller.currentRoom.id');
},
name: "Favorites",
icon: constants.HEART
};*/
// --------------------
// Channel Page
// --------------------
@ -200,10 +160,107 @@ FFZ.menu_pages.channel = {
render: function(view, inner) {
// Get the current room.
var room_id = view.get('controller.currentRoom.id'),
room = this.rooms[room_id];
room = this.rooms[room_id],
has_product = false;
// Check for a product.
if ( this.settings.replace_twitch_menu ) {
var product = room.room.get("product");
if ( product && !product.get("error") ) {
// We have a product, and no error~!
has_product = true;
var is_subscribed = room.room.get("channel.isSubscribed.content"),
icon = room.room.get("badgeSet.subscriber.image"),
grid = document.createElement("div"),
header = document.createElement("div"),
c = 0;
// Weird is_subscribed check. Might be more accurate?
is_subscribed = is_subscribed && is_subscribed.length > 0;
grid.className = "emoticon-grid";
header.className = "heading";
if ( icon )
header.style.backgroundImage = 'url("' + icon + '")';
header.innerHTML = '<span class="right">Twitch</span>Subscriber Emoticons';
grid.appendChild(header);
for(var emotes=product.get("emoticons"), i=0; i < emotes.length; i++) {
var emote = emotes[i];
if ( emote.state !== "active" )
continue;
var s = document.createElement('span'),
can_use = is_subscribed || !emote.subscriber_only,
img_set = 'image-set(url("' + TWITCH_BASE + emote.id + '/1.0") 1x, url("' + TWITCH_BASE + emote.id + '/2.0") 2x, url("' + TWITCH_BASE + emote.id + '/3.0") 4x)';
s.className = 'emoticon tooltip' + (!can_use ? " locked" : "");
s.style.backgroundImage = 'url("' + TWITCH_BASE + emote.id + '/1.0")';
s.style.backgroundImage = '-webkit-' + img_set;
s.style.backgroundImage = '-moz-' + img_set;
s.style.backgroundImage = '-ms-' + img_set;
s.style.backgroundImage = img_set;
s.style.width = emote.width + "px";
s.style.height = emote.height + "px";
s.title = emote.regex;
if ( can_use )
s.addEventListener('click', this._add_emote.bind(this, view, emote.regex));
grid.appendChild(s);
c++;
}
if ( c > 0 )
inner.appendChild(grid);
if ( ! is_subscribed ) {
var sub_message = document.createElement("div"),
nonsub_message = document.createElement("div"),
unlock_text = document.createElement("span"),
sub_link = document.createElement("a");
sub_message.className = "subscribe-message";
nonsub_message.className = "non-subscriber-message";
sub_message.appendChild(nonsub_message);
unlock_text.className = "unlock-text";
unlock_text.innerHTML = "Subscribe to unlock Emoticons";
nonsub_message.appendChild(unlock_text);
sub_link.className = "action subscribe-button button primary";
sub_link.href = product.get("product_url");
sub_link.innerHTML = '<span class="subscribe-text">Subscribe</span><span class="subscribe-price">' + product.get("price") + '</span>';
nonsub_message.appendChild(sub_link);
inner.appendChild(sub_message);
} else {
var last_content = room.room.get("channel.isSubscribed.content");
last_content = last_content.length > 0 ? last_content[last_content.length-1] : undefined;
if ( last_content && last_content.purchase_profile && !last_content.purchase_profile.will_renew ) {
var ends_at = utils.parse_date(last_content.access_end || "");
sub_message = document.createElement("div"),
nonsub_message = document.createElement("div"),
unlock_text = document.createElement("span"),
end_time = ends_at ? Math.floor((ends_at.getTime() - Date.now()) / 1000) : null;
sub_message.className = "subscribe-message";
nonsub_message.className = "non-subscriber-message";
sub_message.appendChild(nonsub_message);
unlock_text.className = "unlock-text";
unlock_text.innerHTML = "Subscription expires in " + utils.time_to_string(end_time, true, true);
nonsub_message.appendChild(unlock_text);
inner.appendChild(sub_message);
}
}
}
}
// Basic Emote Sets
this._emotes_for_sets(inner, view, room && room.menu_sets || []);
this._emotes_for_sets(inner, view, room && room.menu_sets || [], (this.feature_friday || has_product) ? "Channel Emoticons" : null, "http://cdn.frankerfacez.com/channel/global/devicon.png", "FrankerFaceZ");
// Feature Friday!
this._feature_friday_ui(room_id, inner, view);
@ -218,21 +275,29 @@ FFZ.menu_pages.channel = {
// Emotes for Sets
// --------------------
FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, btn) {
if ( header != null ) {
var el_header = document.createElement('div');
el_header.className = 'list-header';
el_header.appendChild(document.createTextNode(header));
if ( btn )
el_header.appendChild(btn);
parent.appendChild(el_header);
}
FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub_text) {
var grid = document.createElement('div'), c = 0;
grid.className = 'emoticon-grid';
if ( header != null ) {
var el_header = document.createElement('div');
el_header.className = 'heading';
if ( sub_text ) {
var s = document.createElement("span");
s.className = "right";
s.appendChild(document.createTextNode(sub_text));
el_header.appendChild(s);
}
el_header.appendChild(document.createTextNode(header));
if ( image )
el_header.style.backgroundImage = 'url("' + image + '")';
grid.appendChild(el_header);
}
for(var i=0; i < sets.length; i++) {
var set = this.emote_sets[sets[i]];
if ( ! set || ! set.emotes )
@ -259,8 +324,8 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, btn) {
}
if ( !c ) {
grid.innerHTML = "This channel has no emoticons.";
grid.className = "chat-menu-content ffz-no-emotes center";
grid.innerHTML += "This channel has no emoticons.";
grid.className = "emoticon-grid ffz-no-emotes center";
}
parent.appendChild(grid);

View file

@ -18,7 +18,7 @@ FFZ.prototype.build_ui_link = function(view) {
FFZ.prototype.update_ui_link = function(link) {
var controller = App.__container__.lookup('controller:chat');
var controller = window.App && App.__container__.lookup('controller:chat');
link = link || document.querySelector('a.ffz-ui-toggle');
if ( !link || !controller )
return;

View file

@ -5,6 +5,12 @@ var FFZ = window.FrankerFaceZ,
BANNED_SETS = {"00000turbo":true},
KNOWN_CODES = {
"#-?[\\\\/]": "#-/",
":-?(?:7|L)": ":-7",
"\\&lt\\;\\]": "<]",
"\\:-?(S|s)": ":-S",
"\\:-?\\\\": ":-\\",
"\\:\\&gt\\;": ":>",
"B-?\\)": "B-)",
"\\:-?[z|Z|\\|]": ":-Z",
"\\:-?\\)": ":-)",
@ -12,7 +18,7 @@ var FFZ = window.FrankerFaceZ,
"\\:-?(p|P)": ":-P",
"\\;-?(p|P)": ";-P",
"\\&lt\\;3": "<3",
"\\:-?(?:\\/|\\\\)(?!\\/)": ":-/",
"\\:-?[\\\\/]": ":-/",
"\\;-?\\)": ";-)",
"R-?\\)": "R-)",
"[o|O](_|\\.)[o|O]": "O.o",
@ -33,7 +39,7 @@ var FFZ = window.FrankerFaceZ,
user_sets = user && ffz.users[user.login] && ffz.users[user.login].sets || [];
// Remove the 'default' set.
set_ids = set_ids.split(",").removeObject("0")
set_ids = set_ids.split(",").removeObject("0");
if ( ffz.settings.global_emotes_in_menu ) {
set_ids.push("0");
@ -68,6 +74,8 @@ FFZ.prototype.setup_my_emotes = function() {
}
this._twitch_set_to_channel[0] = "twitch_global";
this._twitch_set_to_channel[33] = "twitch_tfaces";
this._twitch_set_to_channel[42] = "twitch_tfaces";
}
@ -151,7 +159,7 @@ FFZ.menu_pages.my_emotes = {
return;
}
if ( name == "turbo" ) {
if ( name == "turbo" || name == "twitch_tfaces" ) {
set.channel = "Twitch Turbo";
set.badge = "//cdn.frankerfacez.com/script/turbo_badge.png";
return;
@ -191,7 +199,7 @@ FFZ.menu_pages.my_emotes = {
if ( ! set.channel )
set.channel = name;
dn();
}.bind(this,set,name,dn), 2000);
}.bind(this,set,name,dn), 500);
}.bind(this, set, lname, name)));
},
handle_promises = function() {
@ -286,11 +294,16 @@ FFZ.menu_pages.my_emotes = {
var an = a[1].toLowerCase(),
bn = b[1].toLowerCase();
if ( an === "twitch turbo" || an === "global emoticons" )
an = "zzz" + an;
if ( an === "twitch turbo" || an === "twitch_tfaces" )
an = "zza|" + an;
if ( bn === "twitch turbo" || bn === "global emoticons" )
bn = "zzz" + bn;
else if ( an === "global emoticons" )
an = "zzz|" + an;
if ( bn === "twitch turbo" || bn === "twitch_tfaces" )
bn = "zza|" + bn;
else if ( bn === "global emoticons" )
bn = "zzz|" + bn;
if ( an < bn ) return -1;
else if ( an > bn ) return 1;
@ -325,7 +338,7 @@ FFZ.menu_pages.my_emotes = {
} else {
ems = set.emotes;
title = FFZ.get_capitalization(set.channel);
title = set.channel == "Twitch Turbo" ? set.channel : FFZ.get_capitalization(set.channel);
badge = set.badge;
}

View file

@ -20,7 +20,8 @@ FFZ.settings_info.highlight_notifications = {
value: false,
category: "Chat",
visible: function() { return ! this.has_bttv },
no_bttv: true,
//visible: function() { return ! this.has_bttv },
name: "Highlight Notifications",
help: "Display notifications when a highlighted word appears in chat in an unfocused tab.",

View file

@ -188,7 +188,7 @@ FFZ.prototype.build_race_popup = function() {
out += '<tbody></tbody></table></div>';
out += '<div class="divider"></div>';
out += '<iframe class="twitter_share_button" style="width:110px; height:20px" src="https://platform.twitter.com/widgets/tweet_button.html?text=' + tweet + '%20Watch%20at&via=Twitch&url=http://www.twitch.tv/' + channel_id + '"></iframe>';
out += '<iframe class="twitter_share_button" style="width:130px; height:25px" src="https://platform.twitter.com/widgets/tweet_button.html?text=' + tweet + '%20Watch%20at&via=Twitch&url=http://www.twitch.tv/' + channel_id + '"></iframe>';
out += '<p class="right"><a target="_new" href="http://www.speedrunslive.com/race/?id=' + race.id + '">SRL</a>';

View file

@ -9,28 +9,30 @@ var FFZ = window.FrankerFaceZ,
FFZ.ws_commands.viewers = function(data) {
var channel = data[0], count = data[1];
var controller = App.__container__.lookup('controller:channel'),
id = controller && controller.get && controller.get('id');
var controller = window.App && App.__container__.lookup('controller:channel'),
match = this.is_dashboard ? location.pathname.match(/\/([^\/]+)/) : undefined,
id = this.is_dashboard ? match && match[1] : controller && controller.get && controller.get('id');
if ( id !== channel )
return;
var view_count = document.querySelector('.channel-stats .ffz.stat'),
var view_count = document.querySelector('#ffz-viewer-display'),
content = constants.ZREKNARF + ' ' + utils.number_commas(count);
if ( view_count )
view_count.innerHTML = content;
else {
var parent = document.querySelector('.channel-stats');
var parent = document.querySelector(this.is_dashboard ? "#stats" : '.stats-and-actions .channel-stats');
if ( ! parent )
return;
view_count = document.createElement('span');
view_count.id = "ffz-viewer-display";
view_count.className = 'ffz stat';
view_count.title = 'Viewers with FrankerFaceZ';
view_count.title = 'Chatters with FrankerFaceZ';
view_count.innerHTML = content;
parent.appendChild(view_count);
jQuery(view_count).tipsy();
jQuery(view_count).tipsy(this.is_dashboard ? {"gravity":"s"} : undefined);
}
}

View file

@ -117,13 +117,27 @@ module.exports = {
return m;
},
time_to_string: function(elapsed) {
date_string: function(date) {
return date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate();
},
time_to_string: function(elapsed, separate_days, days_only) {
var seconds = elapsed % 60,
minutes = Math.floor(elapsed / 60),
hours = Math.floor(minutes / 60);
hours = Math.floor(minutes / 60),
days = "";
minutes = minutes % 60;
return (hours < 10 ? "0" : "") + hours + ":" + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
if ( separate_days ) {
days = Math.floor(hours / 24);
hours = hours % 24;
if ( days_only && days > 0 )
return days + " days";
days = ( days > 0 ) ? days + " days, " : "";
}
return days + (hours < 10 ? "0" : "") + hours + ":" + (minutes < 10 ? "0" : "") + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
}
}

146
style.css
View file

@ -1,3 +1,9 @@
.ffz-flip {
-ms-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
}
.ffz-ui-toggle {
display: block;
position: absolute;
@ -6,9 +12,13 @@
cursor: pointer;
}
.emoticon-selector-toggle + script + .ffz-ui-toggle svg { height: 14px; width: 18px; }
.ffz-menu-replace .emoticon-selector-toggle {
display: none !important;
}
.emoticon-selector-toggle + script + .ffz-ui-toggle {
body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle svg { height: 14px; width: 18px; }
body:not(.ffz-menu-replace) .emoticon-selector-toggle + script + .ffz-ui-toggle {
height: 14px; width: 18px;
top: 28px;
}
@ -170,6 +180,32 @@
/* Theater Mode hover bar */
.app-main.theatre #channel .player-column:focus #broadcast-meta,
.app-main.theatre #channel .player-column:hover #broadcast-meta {
background-color: #19191f;
color: #aaa;
position: absolute;
top: -25px;
left: 120px;
right: 10px;
z-index: 7;
opacity: 0.95;
height: 20px;
}
.app-main.theatre #channel .player-column #broadcast-meta .info { padding-left: 5px; }
.app-main.theatre #channel .player-column #broadcast-meta .info .title {
font-size: 12px;
line-height: 20px;
}
.app-main.theatre #channel .player-column #broadcast-meta .info .channel,
.app-main.theatre #channel .player-column #broadcast-meta .info .edit-link,
.app-main.theatre #broadcast-meta .profile-link {
display: none;
}
.app-main.theatre .player-column:focus .stats-and-actions, .app-main.theatre .player-column:hover .stats-and-actions {
background-color: #19191f;
@ -342,23 +378,31 @@
}
}
.ffz-ui-menu-page { overflow-y: auto; }
.ffz-ui-menu-page {
overflow-y: auto;
padding: 0 20px;
margin: -20px -20px 0;
}
.ffz-ui-menu-page[data-page="about"],
.ffz-ui-menu-page .chat-menu-content p { padding: 0 20px; }
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content .heading,
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid .heading {
margin-bottom: 5px;
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 10px 20px;
border-top: 1px solid rgba(0,0,0,0.2);
text-align: left;
}
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid .heading {
padding-left: 23px;
padding-left: 43px;
background-repeat: no-repeat;
background-position: 20px 10px;
}
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid + .emoticon-grid { padding-top: 0; }
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content:first-of-type .heading,
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .emoticon-grid:first-of-type .heading {
border-top: none;
padding-top: 0;
background-position-y: 0;
}
.chat-menu.ffz-ui-popup .ffz-ui-menu-page .chat-menu-content {
@ -375,17 +419,27 @@
opacity: 0.75;
}
.ffz-ui-menu-page span.help, .ffz-ui-menu-page p.option a {
.ffz-ui-menu-page p.disabled span.switch-label,
.ffz-ui-menu-page span.help,
.ffz-ui-menu-page p.option a {
margin-left: 50px;
}
.ffz-ui-popup ul.menu {
list-style-type: none;
margin: 0 -20px;
border-top: 1px solid rgba(0,0,0,0.2);
background-color: #eee;
}
.ffz-ui-popup .emoticon-selector-box {
width: 10000px !important; /* Max-width has our back */
max-width: 300px;
}
.ember-chat .chat-interface .ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon-grid { background-color: transparent; }
.app-main.theatre .ffz-ui-popup ul.menu,
.chat-container.dark .ffz-ui-popup ul.menu,
.ffz-ui-popup.dark ul.menu {
background-color: #282828;
}
@ -448,6 +502,40 @@
vertical-align: top;
}
/* BTTV Menu Fixes */
.ffz-ui-popup.dark .emoticon-grid .heading,
.ffz-ui-popup.dark li.title { color: #fff; }
.ffz-ui-popup.dark .ffz-ui-menu-page { background-color: #1e1e1e; }
/* Menu Scrollbar */
#ffz-race-popup .table::-webkit-scrollbar,
.emoticon-selector-box .all-emotes::-webkit-scrollbar,
.ffz-ui-menu-page::-webkit-scrollbar {
width: 6px;
}
#ffz-race-popup .table::-webkit-scrollbar-thumb,
.emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
.ffz-ui-menu-page::-webkit-scrollbar-thumb {
border-radius: 7px;
background: rgba(0,0,0,0.7);
box-shadow: 0 0 1px 1px rgba(255,255,255,0.25);
}
.ffz-dark .table::-webkit-scrollbar-thumb,
.ember-chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
.chat-container.dark .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
.app-main.theatre .emoticon-selector-box .all-emotes::-webkit-scrollbar-thumb,
.ember-chat-container.dark .ffz-ui-menu-page::-webkit-scrollbar-thumb,
.chat-container.dark .ffz-ui-menu-page::-webkit-scrollbar-thumb,
.app-main.theatre .ffz-ui-menu-page::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.6);
box-shadow: 0 0 1px 1px rgba(0,0,0,0.25);
}
/* Chat Mentions */
.ember-chat .mentioned:empty,
@ -478,7 +566,8 @@
/* Fix Moderation Cards */
.ember-chat .ffz-moderation-card {
box-shadow: #808080 0 0 5px;
border: 2px solid #cbcbcb;
/*box-shadow: #808080 0 0 5px;*/
}
.ember-chat .ffz-moderation-card button {
@ -503,19 +592,8 @@
.ember-chat .ffz-moderation-card:focus {
outline: none;
box-shadow: #000 0 0 5px;
}
.ember-chat-container.dark .ember-chat .ffz-moderation-card:focus,
.chat-container.dark .ember-chat .ffz-moderation-card:focus,
.app-main.theatre .ember-chat .ffz-moderation-card:focus {
box-shadow: #fff 0 0 5px;
}
.ember-chat-container.dark .ember-chat .ffz-moderation-card .interface,
.chat-container.dark .ember-chat .ffz-moderation-card .interface,
.app-main.theatre .ember-chat .ffz-moderation-card .interface {
background-color: #232329;
border-color: #444;
/*box-shadow: #000 0 0 5px;*/
}
.ember-chat .ffz-moderation-card .interface:not(:last-of-type) {
@ -660,3 +738,19 @@
.button.ffz-donate:not(.disabled):hover {
background: #08c43d;
}
/* Dumb Fixes */
.dropmenu, .ui-menu, .ui-multiselect-menu {
margin-bottom: 0;
}
/* Unsafe Links */
a.unsafe-link {
color: #a64141 !important;
}
.chat-container.dark .chat-line a.unsafe-link {
color: #d28e8e !important;
}