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

3.5.457. Commerce stuff. Favorite any emote. Consolidate emotes from your Twitch Inventory. Dark CSS tweaks. Add emoji size settings. Highlight messages with mentions without having alternating rows enabled.

This commit is contained in:
SirStendec 2017-04-05 19:12:11 -04:00
parent 3d6acd46dd
commit 61ac0b97f6
20 changed files with 830 additions and 160 deletions

View file

@ -185,7 +185,7 @@ FFZ.settings_info.hidden_badges = {
utils.prompt(
"Hidden Badges",
"Please enter a comma-separated list of badges that you would like to be hidden in chat.</p><p><b>Possible Values:</b> " + output.join(", "),
"Please enter a comma-separated list of badges that you would like to be hidden in chat. You can use the special value <code>game</code> to hide all the game-specific badges at once.</p><p><b>Possible Values:</b> " + output.join(", "),
old_val,
function(new_val) {
if ( new_val === null || new_val === undefined )
@ -514,9 +514,10 @@ FFZ.prototype.get_twitch_badges = function(badge_tag, room_id) {
continue;
var versions = channel[badge] || globals[badge],
binfo = versions && versions.versions && versions.versions[version];
binfo = versions && versions.versions && versions.versions[version],
is_game = badge.substr(-2) === '_1';
if ( hidden_badges.indexOf(badge) !== -1 )
if ( hidden_badges.indexOf(badge) !== -1 || (is_game && hidden_badges.indexOf('game') !== -1) )
continue;
if ( BADGE_POSITIONS.hasOwnProperty(badge) )
@ -648,7 +649,8 @@ FFZ.prototype.bttv_badges = function(data) {
hidden_key = hidden_key.substr(0, ind);
}
if ( hidden_badges.indexOf(hidden_key) !== -1 ) {
var is_game = hidden_key.substr(-2) === '_1';
if ( hidden_badges.indexOf(hidden_key) !== -1 || (is_game && hidden_badges.indexOf('game') !== -1) ) {
data.badges.splice(i, 1);
i--;
continue;

View file

@ -26,6 +26,11 @@ module.exports = FrankerFaceZ.constants = {
IS_WEBKIT: IS_WEBKIT,
META_NAME: IS_OSX ? "⌘" : (IS_WIN ? "Win" : "Meta"),
// ITAD Stuff
ITAD_SERVER: "https://api.isthereanydeal.com/",
ITAD_CLIENT_ID: "ffdd6ec4951be145",
ITAD_API_KEY: "43667479346a55e9b473f605a6b07ede02c8f3b2",
// Twitch Client ID for API Stuff
CLIENT_ID: "a3bc9znoz6vi8ozsoca0inlcr4fcvkl",

View file

@ -90,21 +90,29 @@ FFZ.settings_info.bits_pinned = {
}
FFZ.settings_info.bits_disable_charity = {
FFZ.settings_info.bits_redesign = {
type: "boolean",
value: false,
category: "Chat Filtering",
name: "Disable Cheering with #Charity Notices",
help: "Stop displaying Twitch's notices about Cheering with #Charity."
}
category: "Chat Appearance",
name: "Bits Redesign",
help: "Use the special cheering animations from April 1st, 2017.",
on_update: function() {
var bits = utils.ember_lookup('service:bits-emotes') ||
utils.ember_lookup('service:bits-rendering-config');
if ( bits && bits.ffz_has_css )
bits.ffz_update_css();
}
}
// --------------------
// Initialization
// --------------------
var redesign = function(x) { return x.replace('/actions/cheer/', '/actions/cheer-redesign/') };
FFZ.prototype.setup_bits = function() {
utils.toggle_cls('ffz-show-bits-tags')(this.settings.bits_tags_container);
@ -115,9 +123,19 @@ FFZ.prototype.setup_bits = function() {
PinnedCheers = utils.ember_lookup('service:bits-pinned-cheers'),
image_css = function(images) {
return 'background-image: url("' + images[1] + '");' +
var im_1 = images[1],
im_2 = images[2],
im_4 = images[4];
if ( f.settings.bits_redesign ) {
im_1 = redesign(im_1);
im_2 = redesign(im_2);
im_4 = redesign(im_4);
}
return 'background-image: url("' + im_1 + '");' +
'background-image: ' + (constants.IS_WEBKIT ? ' -webkit-' : '') + 'image-set(' +
'url("' + images[1] + '") 1x, url("' + images[2] + '") 2x, url("' + images[4] + '") 4x);';
'url("' + im_1 + '") 1x, url("' + im_2 + '") 2x, url("' + im_4 + '") 4x);';
},
tier_css = function(ind, prefix, tier) {
@ -184,7 +202,8 @@ FFZ.prototype.setup_bits = function() {
},
ffz_get_preview: function(prefix, amount) {
return this.getImageSrc(amount, prefix, true, !f.settings.bits_animated, 4);
var src = this.getImageSrc(amount, prefix, true, !f.settings.bits_animated, 4);
return f.settings.bits_redesign ? redesign(src) : src;
},
_ffz_image_css: image_css,
@ -249,12 +268,14 @@ FFZ.prototype.setup_bits = function() {
ffz_get_preview: function(prefix, amount) {
var data = this.ffz_get_tier(prefix, amount),
tier = data && data[1];
return tier ? this._constructImageSrc([4], tier, {
background: 'dark',
scale: 4,
state: f.settings.bits_animated ? 'animated' : 'static'
}).src : '';
tier = data && data[1],
src = tier ? this._constructImageSrc([4], tier, {
background: 'dark',
scale: 4,
state: f.settings.bits_animated ? 'animated' : 'static'
}).src : '';
return f.settings.bits_redesign ? redesign(src) : src;
},
_ffz_image_css: image_css,

316
src/ember/commerce.js Normal file
View file

@ -0,0 +1,316 @@
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
constants = require('../constants');
// --------------------
// Settings
// --------------------
FFZ.settings_info.show_commerce = {
type: "select",
options: {
0: "Never",
1: "When Revenue is Shared",
2: "Always"
},
value: 2,
process_value: utils.process_int(0),
no_mobile: true,
category: "Commerce",
name: "Display Commerce Bar",
help: "Show the commerce bar under channels that allows you to purchase supported games.",
on_update: function(val) {
utils.toggle_cls('ffz-hide-purchase-game')(val === 0);
var views = utils.ember_views(),
ChannelBox = utils.ember_resolve('component:commerce/channel-box');
if ( ! ChannelBox )
return;
for(var key in views)
if ( views[key] instanceof ChannelBox )
try {
views[key].ffzUpdateVisibility();
} catch(err) { }
}
}
FFZ.settings_info.show_itad = {
type: "boolean",
value: true,
no_mobile: true,
category: "Commerce",
name: "Display Competitor Pricing",
help: "Add a button on the commerce bar with pricing from other stores.",
on_update: function(val) {
var views = utils.ember_views(),
BuyGameNow = utils.ember_resolve('component:commerce/buy-game-now');
if ( ! BuyGameNow )
return;
for(var key in views)
if ( views[key] instanceof BuyGameNow )
try {
views[key].ffzRenderPricing();
} catch(err) { }
}
}
// --------------------
// Initialization
// --------------------
FFZ.prototype.setup_commerce = function() {
this._itad_game_to_plain = {};
// Styles
utils.toggle_cls('ffz-hide-purchase-game')(this.settings.show_commerce === 0);
// Ember Modifications
this.update_views('component:commerce/channel-box', this.modify_commerce_box);
this.update_views('component:commerce/buy-game-now', this.modify_buy_game_now);
}
// --------------------
// Modifications
// --------------------
FFZ.prototype.modify_commerce_box = function(view) {
var f = this;
utils.ember_reopen_view(view, {
ffz_init: function() {
this.ffzUpdateVisibility();
},
ffzUpdateVisibility: function() {
var el = this.parentView.get('element'),
real_el = el && el.querySelector('.cmrc-channel-box');
if ( ! real_el )
! this.isDestroyed && setTimeout(this.ffzUpdateVisibility.bind(this), 250);
else
real_el.classList.toggle('hidden', f.settings.show_commerce === 1 && ! this.get('showSupports'));
}.observes('showSupports')
})
}
FFZ.prototype.modify_buy_game_now = function(view) {
var f = this;
utils.ember_reopen_view(view, {
itad_plain: null,
itad_price: null,
twitch_geo: null,
ffz_init: function() {
//f.log("Buy-Game-New Component", this);
this.itad_count = 0;
this.ffzUpdateITADPlain();
var t = this;
Twitch.geo.then(function(data) {
t.set('twitch_geo', data && data.geo);
});
},
ffzTitle: function() {
// Do this because Twitch's ToS say you're not allowed to use data collected from
// the API to show users commercial offers. This extracts the game title from the
// page itself and not from any kind of JS API.
// Granted, they're probably more worried about automated chat spam and people
// sending spam to email addresses recovered from authenticated user profile requests.
var el;
if ( document.body.dataset.currentPath === 'directory.game-details' )
el = document.querySelector('.game-details__page-title');
else
el = document.querySelector('.card__info [data-tt_content="current_game"]');
var output = el ? _.pluck(_.filter(el.childNodes, function(x) { return x.nodeType === document.TEXT_NODE }), 'textContent').join(' ').trim() : null;
if ( ! output && this.itad_count < 50 ) {
var t = this;
setTimeout(function() {
t.itad_count += 1;
Ember.propertyDidChange(this, 'ffzTitle');
}, 250);
}
return output;
}.property(),
didReceiveAttrs: function() {
this._super();
Ember.propertyDidChange(this, 'ffzTitle');
},
ffzUpdateITADPlain: function() {
var title = this.get('ffzTitle'),
old_plain = this.get('itad_plain'),
plain = f._itad_game_to_plain[title] || null;
//f.log("Update ITAD Plain: " + title + " -- [" + plain + "]", this);
// If we already have the value, fetch it now.
if ( ! title || plain ) {
if ( old_plain !== plain )
this.set('itad_plain', plain);
return;
}
if ( old_plain )
this.set('itad_plain', null);
var t = this;
f.ws_send("get_itad_plain", title, function(success, data) {
if ( ! success ) return;
f._itad_game_to_plain[title] = data;
t.ffzUpdateITADPlain();
}, true);
}.observes('ffzTitle'),
ffzUpdateITADPrice: function() {
var t = this,
old_price = this.get('itad_price'),
geo = this.get('twitch_geo'),
plain = this.get('itad_plain');
if ( old_price && old_price[0] === plain )
return;
if ( ! plain || ! geo )
return this.set('itad_price', null);
this.set('itad_price', [plain, null]);
f.ws_send("get_itad_prices", [plain, geo], function(success, data) {
if ( ! success ) return;
t.set('itad_price', [plain, data]);
});
}.observes('itad_plain', 'twitch_geo'),
ffzRenderPricing: function() {
var t = this,
el = this.get('element'),
cont = el && el.querySelector('.ffz-price-info'),
btn_price,
data = this.get('itad_price');
if ( ! f.settings.show_itad || ! data || ! data[1] || ! data[1].list || ! data[1].list.length ) {
if ( cont )
jQuery(cont).remove();
return;
}
if ( ! cont ) {
cont = utils.createElement('div', 'ffz-price-info mg-l-1 balloon-wrapper');
btn_price = utils.createElement('span', 'ffz-price-num button__num-block pd-x-1 mg-1-0');
var btn = utils.createElement('button', 'button itad-button button--dropmenu',
utils.createElement('span', 'ffz-price-label inline-block pd-r-1', 'ITAD'));
btn.appendChild(btn_price);
cont.appendChild(btn);
el.appendChild(cont);
btn.addEventListener('click', function(event) {
t.ffzRenderPopup(event);
});
} else
btn_price = cont.querySelector('.ffz-price-num');
// Determine the cheapest price.
var sales = data[1].list,
cheapest = sales[0].price_new,
currency = data[1].currency,
formatter = new Intl.NumberFormat(undefined, (currency && currency.code) ? {style: 'currency', currency: currency.code, minimumFractionDigits: 2} : {minimumFractionDigits: 2});
btn_price.textContent = formatter.format(cheapest);
}.observes('itad_price'),
ffzRenderPopup: function(e) {
if ( e.button !== 0 || e.altKey || e.ctrlKey || e.shiftKey || e.metaKey )
return;
e.preventDefault();
e.stopPropagation();
var popup = f._popup ? f.close_popup() : f._last_popup,
t = this,
data = t.get('itad_price'),
el = this.get('element'),
cont = el && el.querySelector('.ffz-price-info');
if ( popup && popup.id === 'ffz-price-popup' || ! data || ! data[1] || ! data[1].list || ! data[1].list.length )
return;
var balloon = utils.createElement('div', 'itad-balloon balloon balloon--md show', '<table><thead><tr><th>Store</th><th>Price Cut</th><th>Current</th><th>Regular</th></tr></thead><tbody></tbody></table>'),
tbody = balloon.querySelector('tbody');
balloon.id = 'ffz-price-popup';
// Render the table.
var currency = data[1].currency,
formatter = new Intl.NumberFormat(undefined, (currency && currency.code) ? {style: 'currency', currency: currency.code, minimumFractionDigits: 2} : {minimumFractionDigits: 2});
var sales = data[1].list;
for(var i=0; i < sales.length; i++) {
var entry = sales[i],
row = utils.createElement('tr');
row.innerHTML = '<td><a rel="noreferrer" target="_blank" href="' + utils.quote_san(entry.url) + '">' + utils.sanitize(entry.shop.name) + '</a></td>' +
'<td>' + (entry.price_cut < 0 ? '' : '-') + utils.sanitize(entry.price_cut) + '%</td>' +
'<td>' + formatter.format(entry.price_new) + '</td>' +
'<td>' + formatter.format(entry.price_old) + '</td>';
tbody.appendChild(row);
}
// Add a by-line for IsThereAnyDeal.
var url = data[1].urls && data[1].urls.game || "https://isthereanydeal.com",
by_line = utils.createElement('span', 'ffz-attribution', 'Source: <a rel="noreferrer" target="_blank" href="' + utils.quote_san(url) + '">IsThereAnyDeal.com</a><br><br>Any affiliate links in the provided data are the responsibility of IsThereAnyDeal.');
balloon.appendChild(by_line);
// Now calculate the position and add the balloon to the DOM.
var container = document.querySelector('#main_col'),
outer = container.getBoundingClientRect(),
rect = cont.getBoundingClientRect();
var is_up = (rect.top - outer.top) > (outer.bottom - rect.bottom);
balloon.classList.add('balloon--' + (is_up ? 'up' : 'down'));
balloon.classList.toggle('balloon--right', (rect.left - outer.left) > (outer.right - rect.right));
f._popup_allow_parent = true;
f._popup = balloon;
cont.appendChild(balloon);
}
})
}

View file

@ -346,17 +346,30 @@ FFZ.settings_info.image_hover_all_domains = {
FFZ.settings_info.chat_rows = {
type: "boolean",
value: false,
type: "select",
options: {
0: "Disabled",
1: "Alternating",
2: "Red Highlights",
3: "Both"
},
value: 0,
process_value: utils.process_int(0, 0, 3),
category: "Chat Appearance",
no_bttv: true,
name: "Chat Line Backgrounds",
help: "Display alternating background colors for lines in chat.",
help: "Display alternating background colors for lines in chat or make messages with highlighted words red.",
on_update: function(val) {
this.toggle_style('chat-background', !this.has_bttv && val);
if ( this.has_bttv )
val = 0;
this.toggle_style('chat-background', val === 1 || val === 3);
this.toggle_style('chat-mention-bg', val > 1);
this.toggle_style('chat-mention-bg-alt', val === 3);
}
};
@ -527,6 +540,36 @@ FFZ.settings_info.chat_font_family = {
};
FFZ.settings_info.emoji_scale = {
type: "select",
options: {
0: "Scale with Text (Default)",
1: "Old Default (18px)",
2: "Emote Size (28px)"
},
value: 0,
process_value: utils.process_int(0),
category: "Chat Appearance",
no_bttv: true,
name: "Emoji Size",
help: "Make emoji in chat bigger or smaller.",
on_update: function(val) {
var size = 18;
if ( val === 0 )
size = 1.5 * this.settings.chat_font_size;
else if ( val === 2 )
size = 28;
utils.update_css(this._chat_style, 'emoji_size',
size !== 18 ? '.chat-line .emoji{height:' + size + 'px}' : '');
}
}
FFZ.settings_info.chat_font_size = {
type: "button",
value: 12,
@ -567,6 +610,7 @@ FFZ.settings_info.chat_font_size = {
else {
var lh = Math.max(20, Math.round((20/12)*val)),
pd = Math.floor((lh - 20) / 2);
css = ".pinned-cheers .chat-line,.timestamp-line,.conversation-chat-line,.conversation-system-messages,.chat-history .chat-line,.ember-chat .chat-messages .chat-line { font-size: " + val + "px !important; line-height: " + lh + "px !important; }";
if ( pd )
css += ".pinned-cheers .chat-line,.ember-chat .chat-messages .chat-line .mod-icons, .ember-chat .chat-messages .chat-line .badges { padding-top: " + pd + "px; }";
@ -574,6 +618,7 @@ FFZ.settings_info.chat_font_size = {
utils.update_css(this._chat_style, "chat_font_size", css);
FFZ.settings_info.chat_ts_size.on_update.call(this, this.settings.chat_ts_size);
FFZ.settings_info.emoji_scale.on_update.call(this, this.settings.emoji_scale);
}
};
@ -664,7 +709,7 @@ FFZ.prototype.setup_line = function() {
utils.toggle_cls('ffz-baseline-emoticons')(!this.has_bttv && this.settings.emote_alignment === 2);
this.toggle_style('chat-padding', !this.has_bttv && this.settings.chat_padding);
this.toggle_style('chat-background', !this.has_bttv && this.settings.chat_rows);
FFZ.settings_info.chat_rows.on_update.call(this, this.settings.chat_rows);
this.toggle_style('chat-separator', !this.has_bttv && this.settings.chat_separators);
this.toggle_style('chat-separator-3d', !this.has_bttv && this.settings.chat_separators === 2);

View file

@ -2112,9 +2112,6 @@ FFZ.prototype._modify_room = function(room) {
notice_type = msg.tags && msg.tags['msg-id'],
is_whisper = msg.style === 'whisper';
if ( notice_type === 'charity' && f.settings.bits_disable_charity )
return;
// If this message is already in the room, discard the duplicate.
if ( msg_id && this.ffz_ids && this.ffz_ids[msg_id] )
return;

View file

@ -4,6 +4,8 @@ var FFZ = window.FrankerFaceZ,
constants = require('./constants'),
utils = require('./utils'),
IS_OSX = constants.IS_OSX,
MODIFIERS = {
59847: {
modifier_offset: '0 15px 15px 0',
@ -116,7 +118,68 @@ FFZ.prototype._report_emotes = function() {
// ------------------------
FFZ.prototype._click_emote = function(target, event) {
if ( ! this.settings.clickable_emoticons || (event && !((event.shiftKey || event.shiftLeft) && target && target.classList.contains('emoticon'))) )
if ( ! target || ! target.classList.contains('emoticon') )
return;
if ( event && ((!IS_OSX && event.ctrlKey) || (IS_OSX && event.metaKey)) ) {
var eid, favorite_key;
if ( target.classList.contains('emoji') ) {
var emoji = target.getAttribute('data-ffz-emoji'),
emoji_data = this.emoji_data[emoji];
if ( emoji_data ) {
favorite_key = 'emoji';
eid = emoji_data.raw;
}
} else {
eid = target.getAttribute('data-emote');
if ( eid ) {
eid = parseInt(eid);
var twitch_set = this._twitch_emote_to_set[eid];
if ( twitch_set ) {
var Chat = utils.ember_lookup('controller:chat'),
tmi = Chat && Chat.get('currentRoom.tmiSession'),
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'];
if ( twitch_sets[twitch_set] ) {
var set = twitch_sets[twitch_set];
if ( set.length === 1 && ! this._twitch_set_to_channel[twitch_set] )
favorite_key = 'twitch-inventory';
else
favorite_key = 'twitch-' + twitch_set;
}
}
} else {
eid = parseInt(target.getAttribute('data-ffz-emote'));
var set_id = target.getAttribute('data-ffz-set'),
emote_set = set_id && this.emote_sets[set_id];
if ( emote_set && emote_set.emoticons && emote_set.emoticons[eid] )
favorite_key = 'ffz-' + (emote_set.hasOwnProperty('source_ext') ? 'ext-' + emote_set.source_ext + '-' + set.source_id : set_id);
}
}
if ( favorite_key ) {
var favs = this.settings.favorite_emotes[favorite_key] = this.settings.favorite_emotes[favorite_key] || [],
is_favorited = favs.indexOf(eid) !== -1;
if ( is_favorited )
favs.removeObject(eid);
else
favs.push(eid);
this.settings.set("favorite_emotes", this.settings.favorite_emotes, true);
jQuery(target).trigger('mouseout').trigger('mouseover');
}
return true;
}
if ( ! this.settings.clickable_emoticons || (event && !(event.shiftKey || event.shiftLeft)) )
return;
var eid = target.getAttribute('data-emote');

View file

@ -48,7 +48,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.feature_friday.icon, "FrankerFaceZ");
this._emotes_for_set(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

@ -52,6 +52,27 @@
}
.view-clip__meta-wrapper {
background-color: @nav-bg-color;
}
.view-clip__actions,
.view-clip__bc {
border-color: lighten(@nav-bg-color, 10%);
}
.view-clip__title {
color: @fg-color;
}
.emote-picker {
background-color: @nav-bg-color;
}
.emote-picker__emotes .reaction__emote:hover {
background-color: lighten(@nav-bg-color, 10%);
}
.yin-alert {
background-color: lighten(@nav-bg-color, 10%);
@ -103,6 +124,10 @@
// Buttons
.emote-picker__trigger {
fill: @link-color;
}
.button {
&, &:hover, &:focus {
color: #fff;
@ -214,6 +239,11 @@
}
.form__input {
background-color: fade(white, 5%);
color: lighten(@fg-color, 10%);
border-color: fade(white, 10%);
}
.cf-textarea-input {
background-color: fade(white, 5%);

View file

@ -61,7 +61,7 @@ FFZ.channel_metadata = {};
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 444,
major: 3, minor: 5, revision: 457,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}
@ -253,6 +253,7 @@ require('./ember/following');
require('./ember/feed-card');
require('./ember/sidebar');
require('./ember/dashboard');
require('./ember/commerce');
require('./debug');
@ -563,6 +564,7 @@ FFZ.prototype.init_ember = function(delay) {
this.setup_feed_cards();
this.setup_sidebar();
this.setup_dashboard();
this.setup_commerce();
//this.setup_teams();

View file

@ -89,69 +89,6 @@
}
/* DEPRECIATED: Mention Backgrounds */
.chat-line.ffz-mentioned {
background-color: rgba(255,127,127,0.2);
}
.chat-lines > div:nth-child(2n+0) > .chat-line.ffz-mentioned,
.chat-line.ffz-mentioned:nth-child(2n+0) {
background-color: rgba(255,127,127, 0.4);
}
/* DEPRECIATED: DARK THEME: Mention Backgrounds */
.ffz-dark .chat-history .chat-line.ffz-mentioned,
.theatre .chat-line.ffz-mentioned,
.dark .chat-line.ffz-mentioned {
background-color: rgba(255,0,0, 0.2) !important;
}
.theatre .chat-lines > div:nth-child(2n+0) > .chat-line.ffz-mentioned,
.dark .chat-lines > div:nth-child(2n+0) > .chat-line.ffz-mentioned,
.ffz-dark .chat-history .chat-line.ffz-mentioned:nth-child(2n+0),
.theatre .chat-line.ffz-mentioned:nth-child(2n+0),
.dark .chat-line.ffz-mentioned:nth-child(2n+0) {
background-color: rgba(255,0,0, 0.3) !important;
}
/* Chat Mentions */
/* TODO: Move this by itself */
.mentioned:empty,
.mentioning:empty {
display: none;
}
.chat-line .mentioned,
.chat-line .mentioning {
border-radius: 10px;
padding: 3px 7px;
font-weight: bold;
color: #323232;
background-color: rgba(255,255,255, 0.75);
}
/* DARK THEME: Chat Mentions */
.ffz-dark .chat-history .mentioned,
.ffz-dark .chat-history .mentioning,
.chat-container.dark .chat-line .mentioned,
.ember-chat-container.dark .chat-line .mentioned,
.chat-container.dark .chat-line .mentioning,
.ember-chat-container.dark .chat-line .mentioning {
color: #8c8c8c;
background-color: rgba(16,16,16, 0.75);
}
/* Fix Conversations */
.conversation-window .timestamp-line span,

View file

@ -10,6 +10,8 @@ var FFZ = window.FrankerFaceZ,
HOP = Object.prototype.hasOwnProperty,
FAV_MARKER = '<span class="ffz-favorite"></span>',
EXPLANATION_WARN = '<hr>This link has been sent to you via a whisper rather than standard chat, and has not been checked or approved of by any moderators or staff members. Please treat this link with caution and do not visit it if you do not trust the sender.',
reg_escape = function(str) {
@ -416,7 +418,9 @@ FFZ.prototype.render_tooltip = function(el) {
return image + out;
} else if ( this.classList.contains('emoticon') ) {
var preview_url, width=0, height=0, image, set_id, emote, emote_set,
var can_favorite = this.classList.contains('ffz-can-favorite'),
preview_url, width=0, height=0, image, set_id, emote, emote_set,
emote_id = this.getAttribute('data-ffz-emote'),
modifiers = this.getAttribute('data-modifier-info'),
mod_text = '';
@ -438,6 +442,10 @@ FFZ.prototype.render_tooltip = function(el) {
emote = emote_set && emote_set.emoticons[emote_id];
if ( emote ) {
var favorite_key = 'ffz-' + (emote_set.hasOwnProperty('source_ext') ? 'ext-' + emote_set.source_ext + '-' + emote_set.source_id : emote_set.id),
favorites = f.settings.favorite_emotes[favorite_key] || [],
is_favorite = ! can_favorite && favorites.indexOf(emote && emote.id) !== -1;
var owner = emote.owner,
title = emote_set.title || "Global",
source = emote_set.source || "FFZ",
@ -463,7 +471,7 @@ FFZ.prototype.render_tooltip = function(el) {
//image = preview_url ? `<img style="height:${height}px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
image = preview_url ? '<img style="height:' + height + 'px" class="emoticon ffz-image-hover" src="' + preview_url + '"?_=preview">' : '';
return image + 'Emoticon: ' + (emote.hidden ? '???' : utils.sanitize(emote.name)) + '<br>' + source_line + (owner ? '<br>By: ' + utils.sanitize(owner.display_name) : '') + mod_text;
return (is_favorite ? FAV_MARKER : '') + image + 'Emoticon: ' + (emote.hidden ? '???' : utils.sanitize(emote.name)) + '<br>' + source_line + (owner ? '<br>By: ' + utils.sanitize(owner.display_name) : '') + mod_text;
//return `${image}Emoticon: ${emote.hidden ? '???' : emote.name}<br>${source} ${title}${owner ? '<br>By: ' + owner.display_name : ""}`;
}
@ -473,7 +481,22 @@ FFZ.prototype.render_tooltip = function(el) {
if ( emote_id ) {
set_id = f._twitch_emote_to_set[emote_id];
emote_set = set_id && f._twitch_set_to_channel[set_id];
var set_type = "Channel";
var set_type = "Channel",
favorite_key = 'twitch-' + set_id,
Chat = utils.ember_lookup('controller:chat'),
tmi = Chat && Chat.get('currentRoom.tmiSession'),
twitch_sets = (tmi && tmi.getEmotes() || {'emoticon_sets': {}})['emoticon_sets'];
if ( ! emote_set && twitch_sets[set_id] ) {
var set = twitch_sets[set_id];
if ( set.length === 1 )
favorite_key = 'twitch-inventory';
}
var favorites = set_id && f.settings.favorite_emotes[favorite_key] || [],
is_favorite = ! can_favorite && favorites.indexOf(parseInt(emote_id)) !== -1;
preview_url = f.settings.emote_image_hover && (constants.TWITCH_BASE + emote_id + '/3.0');
//image = preview_url ? `<img style="height:112px" class="emoticon ffz-image-hover" src="${preview_url}?_=preview">` : '';
@ -492,9 +515,9 @@ FFZ.prototype.render_tooltip = function(el) {
}
if ( this.classList.contains('ffz-tooltip-no-credit') )
return image + utils.sanitize(this.alt) + mod_text;
return (is_favorite ? FAV_MARKER : '') + image + utils.sanitize(this.alt) + mod_text;
else
return image + 'Emoticon: ' + utils.sanitize(this.alt) + '<br>' + (set_type ? set_type + ': ' : '') + emote_set + mod_text;
return (is_favorite ? FAV_MARKER : '') + image + 'Emoticon: ' + utils.sanitize(this.alt) + '<br>' + (set_type ? set_type + ': ' : '') + emote_set + mod_text;
//return `${image}Emoticon: ${this.alt}<br>${set_type ? set_type + ": " : ""}${emote_set}`;
}
@ -503,11 +526,14 @@ FFZ.prototype.render_tooltip = function(el) {
emote = f.emoji_data[emote_id];
var src = emote && (f.settings.parse_emoji === 3 ? emote.one_src : (f.settings.parse_emoji === 2 ? emote.noto_src : emote.tw_src));
var favorites = f.settings.favorite_emotes.emoji || [],
is_favorite = ! can_favorite && favorites.indexOf(emote.raw) !== -1;
preview_url = f.settings.emote_image_hover && src;
//image = preview_url ? `<img style="height:72px" class="emoticon ffz-image-hover" src="${preview_url}">` : '';
image = preview_url ? '<img style="height:72px" class="emoticon ffz-image-hover" src="' + preview_url + '"?_=preview">' : '';
return image + "Emoji: " + this.alt + '<br>Name: ' + emote.name + (emote.short_name ? '<br>Short Name :' + emote.short_name + ':' : '') + (emote.cat ? '<br>Category: ' + utils.sanitize(constants.EMOJI_CATEGORIES[emote.cat] || emote.cat) : '') + mod_text;
return (is_favorite ? FAV_MARKER : '') + image + "Emoji: " + this.alt + '<br>Name: ' + emote.name + (emote.short_name ? '<br>Short Name :' + emote.short_name + ':' : '') + (emote.cat ? '<br>Category: ' + utils.sanitize(constants.EMOJI_CATEGORIES[emote.cat] || emote.cat) : '') + mod_text;
//return `${image}Emoji: ${this.alt}<br>Name: ${emote.name}${emote.short_name ? '<br>Short Name: :' + emote.short_name + ':' : ''}`;
}

View file

@ -604,7 +604,7 @@ FFZ.menu_pages.channel = {
var extra_sets = _.union(room && room.extra_sets || [], room && room.ext_sets || [], []);
// Basic Emote Sets
this._emotes_for_sets(inner, view, room && room.set && [room.set] || [], (this.feature_friday || has_product || extra_sets.length ) ? "Channel Emoticons" : null, (room && room.moderator_badge) || "//cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ", ! has_product && extra_sets.length);
this._emotes_for_set(inner, view, room && room.set, (this.feature_friday || has_product || extra_sets.length ) ? "Channel Emoticons" : null, (room && room.moderator_badge) || "//cdn.frankerfacez.com/script/devicon.png", "FrankerFaceZ", ! has_product && extra_sets.length);
for(var i=0; i < extra_sets.length; i++) {
// Look up the set name.
@ -614,7 +614,7 @@ FFZ.menu_pages.channel = {
if ( ! set || ! set.count || set.hidden )
continue;
this._emotes_for_sets(inner, view, [extra_sets[i]], name, set.icon || "//cdn.frankerfacez.com/script/devicon.png", set.source || "FrankerFaceZ");
this._emotes_for_set(inner, view, extra_sets[i], name, set.icon || "//cdn.frankerfacez.com/script/devicon.png", set.source || "FrankerFaceZ");
}
// Feature Friday!
@ -630,8 +630,10 @@ FFZ.menu_pages.channel = {
// Emotes for Sets
// --------------------
FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub_text, top_set) {
var grid = document.createElement('div'), c = 0, f = this;
FFZ.prototype._emotes_for_set = function(parent, view, set_id, header, image, sub_text, top_set) {
var grid = document.createElement('div'), c = 0, f = this,
set = this.emote_sets[set_id];
grid.className = 'emoticon-grid';
if ( top_set )
grid.classList.add('top-set');
@ -659,18 +661,13 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
}
var emotes = [];
for(var i=0; i < sets.length; i++) {
var set = this.emote_sets[sets[i]];
if ( ! set || ! set.emoticons )
continue;
if ( set && set.emoticons )
for(var eid in set.emoticons) {
if ( ! set.emoticons.hasOwnProperty(eid) || set.emoticons[eid].hidden )
continue;
emotes.push(set.emoticons[eid]);
}
}
// Sort the emotes!
emotes.sort(function(a,b) {
@ -682,6 +679,10 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
return 0;
});
// Favoriting Info
var favorite_key = set ? 'ffz-' + (set.hasOwnProperty('source_ext') ? 'ext-' + set.source_ext + '-' + set.source_id : set.id) : undefined,
favorites = this.settings.favorite_emotes[favorite_key] || [];
for(var i=0; i < emotes.length; i++) {
var emote = emotes[i], srcset = null;
@ -695,7 +696,9 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
c++;
var s = document.createElement('span');
s.className = 'emoticon ffz-tooltip';
s.className = 'emoticon ffz-tooltip ffz-can-favorite';
if ( favorites.indexOf(emote.id) !== -1 )
s.classList.add('ffz-favorite');
s.setAttribute('data-ffz-emote', emote.id);
s.setAttribute('data-ffz-set', set.id);
@ -726,7 +729,7 @@ FFZ.prototype._emotes_for_sets = function(parent, view, sets, header, image, sub
if ( url )
window.open(url);
} else
this._add_emote(view, code);
this._add_emote(view, code, favorite_key, id, e);
}.bind(this, emote.id, emote.name));
grid.appendChild(s);

View file

@ -80,6 +80,7 @@ FFZ.settings_info.favorite_emotes = {
FFZ.prototype.setup_my_emotes = function() {
this._twitch_badges = {};
this._twitch_badges["--inventory--"] = "//cdn.frankerfacez.com/script/inventory_icon.svg";
this._twitch_badges["--global--"] = "//cdn.frankerfacez.com/script/twitch_logo.png";
this._twitch_badges["--turbo-faces--"] = this._twitch_badges["turbo"] = "//cdn.frankerfacez.com/script/turbo_badge.png";
this._twitch_badges["--prime-faces--"] = this._twitch_badges["--prime--"] = "//cdn.frankerfacez.com/badges/twitch/premium/1/1.png";
@ -139,7 +140,7 @@ FFZ.menu_pages.myemotes = {
var el = document.createElement("div");
el.className = "emoticon-grid ffz-no-emotes center";
el.innerHTML = "You have no favorite emoticons.<br> <img src=\"//cdn.frankerfacez.com/emoticon/26608/2\"><br>To make an emote a favorite, find it on the <nobr>All Emoticons</nobr> tab and <nobr>" + (constants.IS_OSX ? '⌘' : 'Ctrl') + "-Click</nobr> it.";
el.innerHTML = "You have no favorite emoticons.<br> <img src=\"//cdn.frankerfacez.com/emoticon/26608/2\"><br>To make an emote a favorite, find it and <nobr>" + (constants.IS_OSX ? '⌘' : 'Ctrl') + "-Click</nobr> it.";
container.appendChild(el);
}
},
@ -194,28 +195,38 @@ FFZ.menu_pages.myemotes = {
user = this.get_user(),
ffz_sets = this.getEmotes(user && user.login, null),
sets = [];
sets = [],
gathered_emotes = [];
// Start with Twitch Sets
var gathered_favorites = this.settings.favorite_emotes['twitch-inventory'] || [];
for(var set_id in twitch_sets) {
if ( ! twitch_sets.hasOwnProperty(set_id) || ( ! this.settings.global_emotes_in_menu && set_id === '0' ) )
if ( ! twitch_sets.hasOwnProperty(set_id) || ( ! favorites_only && ! this.settings.global_emotes_in_menu && set_id === '0' ) )
continue;
// Skip the Twitch Turbo set if we have Twitch Prime. They're identical.
if ( set_id == 793 && twitch_sets.hasOwnProperty(19194) )
continue;
var favorites_list = this.settings.favorite_emotes["twitch-" + set_id];
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
continue;
var set = twitch_sets[set_id];
if ( ! set.length )
continue;
var raw_id = this._twitch_set_to_channel[set_id],
menu_id = raw_id ? raw_id.toLowerCase() : 'unknown',
sort_key = 0,
menu_id = raw_id ? raw_id.toLowerCase() : 'unknown';
if ( set.length === 1 && ! raw_id ) {
if ( ! favorites_only || gathered_favorites.indexOf(set[0].id) !== -1 )
gathered_emotes.push(set[0]);
continue;
}
var favorites_list = this.settings.favorite_emotes["twitch-" + set_id];
if ( favorites_only && (! favorites_list || ! favorites_list.length) )
continue;
var sort_key = 0,
menu = FFZ.menu_pages.myemotes.draw_twitch_set.call(this, view, set_id, set, favorites_only);
if ( menu_id.indexOf('global') !== -1 )
@ -227,6 +238,13 @@ FFZ.menu_pages.myemotes = {
sets.push([[sort_key, menu_id], menu]);
}
// Handle the gathered single emotes.
if ( gathered_emotes.length ) {
var menu = FFZ.menu_pages.myemotes.draw_twitch_set.call(this, view, 'inventory', gathered_emotes, favorites_only);
sets.push([[50, 'twitch-inventory'], menu]);
}
// Emoji~!
if ( favorites_only && this.settings.emoji_in_menu ) {
var favorites_list = this.settings.favorite_emotes["emoji"];
@ -238,10 +256,25 @@ FFZ.menu_pages.myemotes = {
}
// Now, FFZ!
if ( favorites_only ) {
// But first, inject all the sets from this specific room.
var ffz_room = controller && this.rooms && this.rooms[controller.get('currentRoom.id')];
if ( ffz_room ) {
if ( ffz_room.set && ffz_sets.indexOf(ffz_room.set) === -1 )
ffz_sets.push(ffz_room.set);
if ( ffz_room.sets && ffz_room.sets.length )
ffz_sets = _.uniq(ffz_sets.concat(ffz_room.sets));
if ( ffz_room.extra_sets && ffz_room.extra_sets.length )
ffz_sets = _.uniq(ffz_sets.concat(ffz_room.extra_sets));
}
}
for(var i=0; i < ffz_sets.length; i++) {
var set_id = ffz_sets[i],
set = this.emote_sets[set_id];
if ( ! set || ! set.count || set.hidden || ( ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
if ( ! set || ! set.count || set.hidden || ( ! favorites_only && ! this.settings.global_emotes_in_menu && this.default_sets.indexOf(set_id) !== -1 ) )
continue;
var menu_id = set.hasOwnProperty('source_ext') ? 'ffz-ext-' + set.source_ext + '-' + set.source_id : 'ffz-' + set.id,
@ -415,7 +448,7 @@ FFZ.menu_pages.myemotes = {
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf('twitch-' + set_id) === -1,
f = this,
channel_id = this._twitch_set_to_channel[set_id] || 'twitch_unknown', title,
channel_id = set_id === 'inventory' ? '--inventory--' : (this._twitch_set_to_channel[set_id] || 'twitch_unknown'), title,
favorites = this.settings.favorite_emotes["twitch-" + set_id] || [],
c = 0;
@ -423,7 +456,9 @@ FFZ.menu_pages.myemotes = {
menu.setAttribute('data-set', 'twitch-' + set_id);
if ( ! favorites_only ) {
if ( channel_id === "twitch_unknown" )
if ( channel_id === '--inventory--' )
title = "Inventory";
else if ( channel_id === "twitch_unknown" )
title = "Unknown Channel";
else if ( channel_id === "--global--" )
title = "Global Emoticons";
@ -441,9 +476,12 @@ FFZ.menu_pages.myemotes = {
heading.className = 'heading';
heading.innerHTML = '<span class="right">Twitch</span>' + utils.sanitize(title);
if ( this._twitch_badges[channel_id] )
heading.style.backgroundImage = 'url("' + this._twitch_badges[channel_id] + '")';
else {
var icon = this._twitch_badges[channel_id];
if ( icon ) {
heading.style.backgroundImage = 'url("' + icon + '")';
if ( icon.indexOf('.svg') !== -1 )
heading.style.backgroundSize = "18px";
} else {
var f = this;
utils.api.get("chat/" + channel_id + "/badges", null, {version: 3})
.done(function(data) {

View file

@ -76,17 +76,20 @@ FFZ.settings_info.notification_timeout = {
help: "Specify how long notifications should be displayed before automatically closing.",
method: function() {
var f = this;
var f = this,
old_val = this.settings.notification_timeout;
utils.prompt(
"Notification Timeout",
"Please enter the time you'd like notifications to be displayed before automatically closing, in seconds.</p><p><b>Default:</b> 60",
this.settings.notification_timeout,
"Please enter the time you'd like notifications to be displayed before automatically closing, in seconds. A value of zero may prevent notifications from disappearing automatically. This value is subject to the limitations of your web browser.</p><p><b>Default:</b> 60",
old_val === false ? 0 : old_val,
function(new_val) {
if ( new_val === null || new_val === undefined )
return;
new_val = parseInt(new_val);
if ( Number.isNaN(new_val) || ! Number.isFinite(new_val) || new_val < 1 )
if ( new_val <= 0 )
new_val = false;
else if ( Number.isNaN(new_val) || ! Number.isFinite(new_val) )
new_val = 60;
f.settings.set("notification_timeout", new_val);
@ -135,7 +138,7 @@ FFZ.prototype.show_notification = function(message, title, tag, timeout, on_clic
if ( perm === "granted" ) {
title = title || "FrankerFaceZ";
timeout = timeout || (this.settings.notification_timeout*1000);
timeout = timeout || (this.settings.notification_timeout === false ? false : this.settings.notification_timeout * 1000);
var options = {
lang: "en-US",