mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
3.5.521. Emote data stuff. Bugfixes. Rich content in chat. Server socket changes.
This commit is contained in:
parent
aa084d71ec
commit
d97a44326a
14 changed files with 996 additions and 194 deletions
|
@ -1,3 +1,44 @@
|
|||
<div class="list-header">3.5.521 <time datetime="2017-09-15">(2017-09-15)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Changed: Hopefully a more resilient way of grabbing emote data.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.520 <time datetime="2017-09-06">(2017-09-06)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Issue with sub emoticons not being tab-completable without prefixes and showing as having an unknown source.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.519 <time datetime="2017-09-05">(2017-09-05)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>The Emote Data Update</li>
|
||||
<li> </li>
|
||||
<li>Changed: Start querying the socket server for emote set associations rather than pre-loading data into the client.</li>
|
||||
<li>Changed: Use an update badges API for the My Emoticons menu.</li>
|
||||
<li>Changed: Support multiple sub tiers with the Channel emoticons menu.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.518 <time datetime="2017-09-02">(2017-09-02)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Bug with rich content attached to chat messages not getting removed when a message is timed out.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.517 <time datetime="2017-09-02">(2017-09-02)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Changed: Dark theme CSS tweak for the directory.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.516 <time datetime="2017-09-01">(2017-09-01)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Typo in video url regular expression.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.515 <time datetime="2017-09-01">(2017-09-01)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Option to disable rich content in chat.</li>
|
||||
<li>Changed: Add support for rich content in chat. This includes shared purchases, clip information, and video information.</li>
|
||||
<li>API Added: <code>replaces</code> field to emoticons to allow extension emotes to replace Twitch emotes.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.514 <time datetime="2017-09-01">(2017-09-01)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Schedule parsing issue with null values.</li>
|
||||
|
@ -25,46 +66,5 @@
|
|||
<li>Fixed: Re-sub messages not showing in channels without custom sub badges and with <code>Chat Appearance > Old-Style Subscriber Notices</code> enabled.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.509 <time datetime="2017-08-07">(2017-08-07)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>API Changed: Throw the <code>room-recent-highlights</code> event even if the Recent Highlights feature is disabled.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.508 <time datetime="2017-08-07">(2017-08-07)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>API Added: <code>room-recent-highlights</code> event for triggering behaviors when new highlighted messages are displayed.</li>
|
||||
<li>API Changed: Allow extensions to repress chat messages by marking them as removed in the <code>room-message</code> API event.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.507 <time datetime="2017-08-02">(2017-08-02)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Dark theme for the dashboard.</li>
|
||||
<li>Fixed: Moderation cards not rendering correctly when you start by opening your own mod card.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.506 <time datetime="2017-07-26">(2017-07-26)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Setting to hide Trending Emotes in chat.</li>
|
||||
<li>Fixed: Make Minimal Chat input hide the trending emotes stuff too.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.505 <time datetime="2017-07-24">(2017-07-24)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Changed: Minor dark theme tweaks.</li>
|
||||
<li>Fixed: A scrollbar would appear at the side of the page at times due to tooltips.</li>
|
||||
<li>Fixed: Don't escape HTML for legacy sidebar tooltips.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.504 <time datetime="2017-07-13">(2017-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Support for the <code>AUTOMOD_SMALLER</code> experiment. In-Line AutoMod is not yet working when you are part of that experiment.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.503 <time datetime="2017-07-13">(2017-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Emotes parsing in local messages.</li>
|
||||
<li>Fixed: Use the <code>user-emotes</code> service in all places since <code>tmiSession</code>'s emote parser has been removed.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header" id="ffz-old-news-button"><a href="#">View Older</a></div>
|
||||
<div id="ffz-old-news"></div>
|
3
dark.css
3
dark.css
|
@ -661,7 +661,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
|||
.ffz-dark .card .card__info a,
|
||||
.ffz-dark .items-grid .meta .title,
|
||||
.ffz-dark .items-grid .meta p a {
|
||||
color: #9c9c9c !important;
|
||||
color: #9c9c9c;
|
||||
}
|
||||
|
||||
.ffz-dark .event-calendar {
|
||||
|
@ -1862,6 +1862,7 @@ body.ffz-dark:not([data-page="teams#show"]),
|
|||
.ffz-dark .search-result-view__block.isActive { background-color: #222 }
|
||||
|
||||
.ffz-dark .search-result-view__titlesep:hover,
|
||||
.ffz-dark a:hover .card__title,
|
||||
.ffz-dark .card__title a:hover,
|
||||
.ffz-dark .card__info a:hover {
|
||||
color: #ccd;
|
||||
|
|
|
@ -1,3 +1,44 @@
|
|||
<div class="list-header">3.5.509 <time datetime="2017-08-07">(2017-08-07)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>API Changed: Throw the <code>room-recent-highlights</code> event even if the Recent Highlights feature is disabled.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.508 <time datetime="2017-08-07">(2017-08-07)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>API Added: <code>room-recent-highlights</code> event for triggering behaviors when new highlighted messages are displayed.</li>
|
||||
<li>API Changed: Allow extensions to repress chat messages by marking them as removed in the <code>room-message</code> API event.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.507 <time datetime="2017-08-02">(2017-08-02)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Dark theme for the dashboard.</li>
|
||||
<li>Fixed: Moderation cards not rendering correctly when you start by opening your own mod card.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.506 <time datetime="2017-07-26">(2017-07-26)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Setting to hide Trending Emotes in chat.</li>
|
||||
<li>Fixed: Make Minimal Chat input hide the trending emotes stuff too.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.505 <time datetime="2017-07-24">(2017-07-24)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Changed: Minor dark theme tweaks.</li>
|
||||
<li>Fixed: A scrollbar would appear at the side of the page at times due to tooltips.</li>
|
||||
<li>Fixed: Don't escape HTML for legacy sidebar tooltips.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.504 <time datetime="2017-07-13">(2017-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Support for the <code>AUTOMOD_SMALLER</code> experiment. In-Line AutoMod is not yet working when you are part of that experiment.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.503 <time datetime="2017-07-13">(2017-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Emotes parsing in local messages.</li>
|
||||
<li>Fixed: Use the <code>user-emotes</code> service in all places since <code>tmiSession</code>'s emote parser has been removed.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">3.5.502 <time datetime="2017-07-13">(2017-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Dark theme for chat.</li>
|
||||
|
|
|
@ -37,9 +37,10 @@ var commandHandlers = map[Command]CommandHandler{
|
|||
"twitch_emote": C2SHandleBunchedCommand,
|
||||
"get_link": C2SHandleBunchedCommand,
|
||||
"get_display_name": C2SHandleBunchedCommand,
|
||||
"get_emote": C2SHandleBunchedCommand,
|
||||
"get_emote_set": C2SHandleBunchedCommand,
|
||||
"has_logs": C2SHandleBunchedCommand,
|
||||
"update_follow_buttons": C2SHandleRemoteCommand,
|
||||
"chat_history": C2SHandleRemoteCommand,
|
||||
"user_history": C2SHandleRemoteCommand,
|
||||
}
|
||||
|
||||
func setupInterning() {
|
||||
|
@ -56,12 +57,12 @@ func setupInterning() {
|
|||
CommandPool._Intern_Setup("track_follow")
|
||||
CommandPool._Intern_Setup("emoticon_uses")
|
||||
CommandPool._Intern_Setup("twitch_emote")
|
||||
CommandPool._Intern_Setup("get_emote")
|
||||
CommandPool._Intern_Setup("get_emote_set")
|
||||
CommandPool._Intern_Setup("has_logs")
|
||||
CommandPool._Intern_Setup("get_link")
|
||||
CommandPool._Intern_Setup("get_display_name")
|
||||
CommandPool._Intern_Setup("update_follow_buttons")
|
||||
CommandPool._Intern_Setup("chat_history")
|
||||
CommandPool._Intern_Setup("user_history")
|
||||
CommandPool._Intern_Setup("adjacent_history")
|
||||
}
|
||||
|
||||
// DispatchC2SCommand handles a C2S Command in the provided ClientMessage.
|
||||
|
|
|
@ -126,6 +126,7 @@ module.exports = FrankerFaceZ.constants = {
|
|||
//modifier: '262f'
|
||||
},
|
||||
|
||||
COMMERCE_CARET: '<svg version="1.1" x="0" y="0" viewbox="0 0 792 612" enable-background="new 0 0 792 612"><path fill="#D8D8D8" d="M777.1,444.6c19.8,19.8,19.8,49.5,0,69.3c-19.8,19.8-49.5,19.8-69.3,0L361.4,167.4c-19.8-19.8-19.8-49.5,0-69.3c19.8-19.8,49.5-19.8,69.3,0L777.1,444.6z"></path><path fill="#D8D8D8" d="M430.6,98.1c19.8,19.8,19.8,49.5,0,69.3L84.1,513.9c-19.8,19.8-49.5,19.8-69.3,0c-19.8-19.8-19.8-49.5,0-69.3L361.3,98.1C381.1,78.3,410.9,78.3,430.6,98.1z"></path></svg',
|
||||
ZREKNARF: svg('glyph_views svg-zreknarf', 16, 12.5, SVGPATH, '0 0 249 195'),
|
||||
CHAT_BUTTON: svg('emoticons', 24, 18, SVGPATH, '0 0 249 195'),
|
||||
ROOMS: svg('glyph_views svg-roomlist', 16, 16, 'M1,13v-2h14v2H1z M1,5h13v2H1V5z M1,2h10v2H1V2z M12,10H1V8h11V10z'),
|
||||
|
|
|
@ -783,7 +783,7 @@ FFZ.prototype.modify_chat_input = function(component) {
|
|||
room_id = room && room.get('id'),
|
||||
user_emotes = utils.ember_lookup('service:user-emotes'),
|
||||
|
||||
set_name, replacement, url, is_inventory, is_sub_set, fav_list,
|
||||
set_data, set_name, replacement, url, is_inventory, is_sub_set, fav_list,
|
||||
emote_set, emote, emote_id, code, sort_factor, is_fav,
|
||||
prefix_length, per_pref,
|
||||
|
||||
|
@ -804,7 +804,8 @@ FFZ.prototype.modify_chat_input = function(component) {
|
|||
is_inventory = f._twitch_inventory_sets.indexOf(set_id) !== -1;
|
||||
fav_list = f.settings.favorite_emotes['twitch-' + (is_inventory ? 'inventory' : set_id)] || [];
|
||||
is_sub_set = false;
|
||||
set_name = f._twitch_set_to_channel[set_id];
|
||||
set_data = !is_inventory && f.get_twitch_set(set_id);
|
||||
set_name = set_data && set_data.c_name;
|
||||
if ( ! emote_set )
|
||||
continue;
|
||||
|
||||
|
|
|
@ -1,16 +1,50 @@
|
|||
var FFZ = window.FrankerFaceZ,
|
||||
utils = require("../utils"),
|
||||
constants = require("../constants"),
|
||||
bits_service,
|
||||
|
||||
CLIP_ERROR = constants.TWITCH_BASE + '86/1.0',
|
||||
CLIP_FALLBACK = 'https://clips-media-assets.twitch.tv/404-preview-86x45.jpg',
|
||||
|
||||
TB_TOOLTIP = 'This message was flagged by AutoMod. Should it be allowed?',
|
||||
|
||||
BAN_SPLIT = /[\/\.](?:ban ([^ ]+)|timeout ([^ ]+)(?: (\d+))?|timeout_message ([^ ]+) ([^ ]+)(?: (\d+))?)(?: (.*))?$/;
|
||||
|
||||
|
||||
FFZ._fallback_image = function(img) {
|
||||
var src = img.dataset.fallbackUrl;
|
||||
if ( ! src )
|
||||
return;
|
||||
|
||||
img.dataset.fallbackUrl = '';
|
||||
img.src = src;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------
|
||||
// Settings
|
||||
// ---------------------
|
||||
|
||||
FFZ.settings_info.chat_rich_content = {
|
||||
type: "boolean",
|
||||
value: true,
|
||||
|
||||
category: "Chat Appearance",
|
||||
name: "Rich Content in Chat",
|
||||
help: "Display rich content in chat, such as clip embeds and blocks about people's purchases.",
|
||||
|
||||
on_update: function(val) {
|
||||
var CL = utils.ember_resolve('component:chat/chat-line'),
|
||||
views = CL ? utils.ember_views() : [];
|
||||
|
||||
for(var vid in views) {
|
||||
var view = views[vid];
|
||||
if ( view instanceof CL && view.ffzRender )
|
||||
view.ffzRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FFZ.settings_info.automod_inline = {
|
||||
type: "boolean",
|
||||
value: false,
|
||||
|
@ -809,6 +843,10 @@ FFZ.settings_info.chat_ts_size = {
|
|||
// ---------------------
|
||||
|
||||
FFZ.prototype.setup_line = function() {
|
||||
bits_service = utils.ember_lookup('service:bits-emotes');
|
||||
if ( ! bits_service )
|
||||
bits_service = utils.ember_lookup('service:bits-rendering-config');
|
||||
|
||||
// Tipsy Handler
|
||||
jQuery(document.body).on("mouseleave", ".tipsy", function() {
|
||||
this.parentElement.removeChild(this);
|
||||
|
@ -934,8 +972,11 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
|||
this.$(".mod-icons").replaceWith(this.buildModIconsHTML());
|
||||
if ( this.get("msgObject.deleted") ) {
|
||||
this.$(".message").replaceWith(this.buildDeletedMessageHTML());
|
||||
} else
|
||||
this.$(".ffz-rich-content").html("");
|
||||
} else {
|
||||
this.$(".deleted,.message").replaceWith(this.buildMessageHTML());
|
||||
this.$(".ffz-rich-content").html(this.buildRichContentHTML());
|
||||
}
|
||||
}),
|
||||
|
||||
clickedChanged: Ember.observer("hasClickedFlaggedMessage", function() {
|
||||
|
@ -1092,33 +1133,230 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
|||
|
||||
var tags = this.get('msgObject.tags') || {},
|
||||
msg_type = tags['msg-id'],
|
||||
out = '';
|
||||
out;
|
||||
|
||||
if ( msg_type === 'bits-hashtag' )
|
||||
/*if ( msg_type === 'bits-hashtag' )
|
||||
out = f.render_token(true,false,true, {type: "bits", prefix: "Cheer", amount: parseInt(tags['msg-param-total'])}) +
|
||||
utils.sanitize(tags['system-msg'] || '')
|
||||
.replace('{hashtag}', '<strong>#' + utils.sanitize(tags['msg-param-hashtag']) + '</strong>')
|
||||
.replace('{link}', '<a target="_blank" href="' + utils.quote_san(tags['msg-param-link']) + '">' + utils.sanitize(tags['msg-param-linkname']) + '</a>')
|
||||
.replace('{link}', '<a target="_blank" href="' + utils.quote_san(tags['msg-param-link']) + '">' + utils.sanitize(tags['msg-param-linkname']) + '</a>')*/
|
||||
|
||||
|
||||
else if ( msg_type === 'purchase' ) {
|
||||
var Intl = utils.ember_lookup('service:intl');
|
||||
out = Intl && ('<p class="purchase-message-title pd-t-0 float-left">' +
|
||||
Intl.t('gameCommerce.purchaseNotifications.message', {
|
||||
userName: tags['login'],
|
||||
purchaseTitle: tags['msg-param-title']
|
||||
}) +
|
||||
'</p><div><img class="purchase-notif__box-art float-right mg-t-0 mg-1-1" src="' +
|
||||
utils.quote_san(tags['msg-param-imageURL']) +
|
||||
'"></div>');
|
||||
if ( msg_type === 'purchase' ) {
|
||||
var Intl = utils.ember_lookup('service:intl'),
|
||||
commerce = this.get('msgObject.tags.content.commerce.firstObject');
|
||||
|
||||
if ( commerce && Intl )
|
||||
out = '<p class="purchase-message-title">' +
|
||||
Intl.t(
|
||||
commerce.numCrates ?
|
||||
'gameCommerce.purchaseNotifications.plusCrates.systemMessage' :
|
||||
'gameCommerce.purchaseNotifications.systemMessage',
|
||||
|
||||
{
|
||||
userName: tags['login'],
|
||||
purchaseTitle: tags['msg-param-title'],
|
||||
numCrates: commerce.numCrates,
|
||||
htmlSafe: true
|
||||
}) + '</p>';
|
||||
|
||||
}
|
||||
|
||||
else if ( msg_type === 'raid' || msg_type === 'unraid' )
|
||||
// TODO: This.
|
||||
return '';
|
||||
|
||||
else
|
||||
out = utils.sanitize(this.get('systemMsg'));
|
||||
|
||||
return out ? '<div class="system-msg">' + out + '</div>' : '';
|
||||
},
|
||||
|
||||
buildRichContentHTML: function() {
|
||||
if ( ! f.settings.chat_rich_content || this.get('msgObject.deleted') )
|
||||
return '';
|
||||
|
||||
var content = this.get('msgObject.tags.content') || {},
|
||||
out = '';
|
||||
|
||||
for(var pk in content) {
|
||||
if ( pk === 'commerce' )
|
||||
out += this.buildPurchaseContentHTML();
|
||||
else if ( pk in FFZ.rich_content_providers )
|
||||
out += this.buildRichEmbedHTML(this.ffzGetContent(pk));
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
ffzUpdateRichContent: function() {
|
||||
if ( this.get('msgObject.tags.content') )
|
||||
this.$(".ffz-rich-content").html(this.buildRichContentHTML());
|
||||
},
|
||||
|
||||
ffzGetContent: function(provider_key, info) {
|
||||
info = info || this.get('msgObject.tags.content.' + provider_key + '.firstObject')
|
||||
if ( ! info || ! info.data )
|
||||
return {
|
||||
loaded: true,
|
||||
errored: true
|
||||
};
|
||||
|
||||
var t = this,
|
||||
provider = FFZ.rich_content_providers[provider_key],
|
||||
|
||||
content_info = this._ffz_content_info = this._ffz_content_info || {},
|
||||
content = content_info[info.index] = content_info[info.index] || {
|
||||
embed_type: provider.display_name || provider_key,
|
||||
input: info.data
|
||||
};
|
||||
|
||||
if ( ! content._started ) {
|
||||
content._started = true;
|
||||
provider.get_info.call(f, content.input).then(function(data) {
|
||||
content.data = data;
|
||||
content.loaded = true;
|
||||
t.isDestroyed || t.ffzUpdateRichContent();
|
||||
}).catch(function() {
|
||||
content.errored = true;
|
||||
content.loaded = true;
|
||||
t.isDestroyed || t.ffzUpdateRichContent();
|
||||
})
|
||||
}
|
||||
|
||||
return content;
|
||||
},
|
||||
|
||||
buildRichEmbedHTML: function(content) {
|
||||
var data = content.data || {},
|
||||
out = '<div class="chat-chip pd-y-05 mg-t-05">' +
|
||||
'<div class="card card--row card--sm">';
|
||||
|
||||
if ( ! content.loaded || content.errored ) {
|
||||
out += '<div class="card__layout">' +
|
||||
'<figure class="card__img chat-chip-img' + (content.errored ? ' chat-chip-img--error' : '') + '">';
|
||||
|
||||
if ( content.loaded )
|
||||
out += '<img src="' + utils.quote_attr(content.errored ? CLIP_ERROR : data.image) + '" data-fallback-url="' + utils.quote_attr(CLIP_FALLBACK) + '" onerror="FrankerFaceZ._fallback_image(this)">';
|
||||
else
|
||||
out += '<div class="loading-spinner"></div>';
|
||||
|
||||
out += '</figure>' +
|
||||
'<div class="card__body">' +
|
||||
'<h3 class="card__title ellipsis">' +
|
||||
(content.errored ?
|
||||
'Something went wrong' :
|
||||
'Loading ' + content.embed_type + '...') +
|
||||
'</h3>' +
|
||||
'<p class="card__info ellipsis">' +
|
||||
(content.errored ?
|
||||
"We couldn't find that " + content.embed_type + '.' :
|
||||
'...') +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
} else {
|
||||
out += '<a class="card__layout" href="' + utils.quote_attr(data.url) + '" target="_blank" rel="noopener noreferrer" class="card__layout">' +
|
||||
'<figure class="card__img chat-chip-img">' +
|
||||
'<img src="' + utils.quote_attr(data.image) + '" data-fallback-url="' + utils.quote_attr(CLIP_FALLBACK) + '" onerror="FrankerFaceZ._fallback_image(this)">' +
|
||||
'</figure>' +
|
||||
'<div class="card__body">' +
|
||||
'<h3 class="card__title ellipsis">' + data.title + '</h3>';
|
||||
|
||||
for(var i=0; i < data.by_lines.length; i++)
|
||||
out += '<p class="card__info ellipsis">' + data.by_lines[i] + '</p>';
|
||||
|
||||
out += '</div>' +
|
||||
'</a>';
|
||||
}
|
||||
|
||||
return out + '</div></div>';
|
||||
},
|
||||
|
||||
buildPurchaseContentHTML: function() {
|
||||
var commerce = this.get('msgObject.tags.content.commerce.firstObject'),
|
||||
out;
|
||||
if ( ! commerce || ! commerce.purchased || ! commerce.purchased.length )
|
||||
return '';
|
||||
|
||||
var purchased = commerce.purchased[0],
|
||||
crated = commerce.crated || [],
|
||||
show_drawer = crated.length > 2,
|
||||
drawer_open = this._ffz_commerce_drawer_open,
|
||||
image_url = purchased.boxart,
|
||||
title = purchased.title,
|
||||
Intl = utils.ember_lookup('service:intl');
|
||||
|
||||
out = '<div class="chat-commerce-rich-content chat-chip flex flex--column full-width mg-t-05 pd-0">' +
|
||||
'<div class="flex flex--nowrap">' +
|
||||
'<div class="flex__item--noShrink flex__item--noGrow mg-05">' +
|
||||
'<img class="chat-commerce-rich-content__image chat-commerce-rich-content__image--xlarge" src="' + utils.quote_attr(image_url) + '">' +
|
||||
'</div>' +
|
||||
'<div class="flex__item--grow mg-05 mg-r-05">' +
|
||||
'<div class="font-size-4">' + utils.sanitize(title) + '</div>';
|
||||
|
||||
if ( Intl && commerce.numCrates > 0 ) {
|
||||
out += '<div class="chat-commerce-rich-content__subtext mg-t-05">' +
|
||||
Intl.t('gameCommerce.purchaseNotifications.plusCrates.richContentMessage', {
|
||||
numCrates: commerce.numCrates,
|
||||
numRewards: crated.length
|
||||
}) +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
out += '</div>';
|
||||
|
||||
if ( crated.length ) {
|
||||
out += '<div class="border-1 pd-05 flex flex--nowrap justify-content-center align-items-center font-size-4 flex__item--noShrink' + (show_drawer ? ' chat-commerce-right-content__pocket--button' : '') + '">';
|
||||
|
||||
if ( show_drawer ) {
|
||||
if ( drawer_open )
|
||||
out += '<div><figure class="icon chat-commerce-rich-content__caret">' + constants.COMMERCE_CARET + '</figure></div>';
|
||||
else
|
||||
out += '<div class="flex flex--nowrap">' +
|
||||
this.buildPurchaseContentItemHTML(crated[0], 'align-self-center') +
|
||||
'<div class="align-self-center pill pill--red flex__item--noShrink">+' +
|
||||
((crated.length||0) - 1) + '</div>' +
|
||||
'</div>';
|
||||
} else
|
||||
for(var i=0; i < crated.length; i++)
|
||||
out += this.buildPurchaseContentItemHTML(crated[i]);
|
||||
|
||||
out += '</div>';
|
||||
}
|
||||
|
||||
out += '</div></div>';
|
||||
|
||||
if ( show_drawer && drawer_open ) {
|
||||
out += '<div class="chat-commerce-rich-content__drawer flex flex--horizontalEnd align-content-center align-items-center pd-05 pd-t-0">';
|
||||
for(var i=0; i < crated.length; i++)
|
||||
out += this.buildPurchaseContentItemHTML(crated[i], 'mg-r-05 mg-t-05');
|
||||
out += '</div>';
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
buildPurchaseContentItemHTML: function(loot, extra_classes) {
|
||||
var loot_type = loot.type,
|
||||
out;
|
||||
|
||||
if ( loot_type === 'emoticon' )
|
||||
out = '<img src="//static-cdn.jtvnw.net/emoticons/v1/' + utils.quote_san(loot.id) + '/2.0">';
|
||||
|
||||
else if ( loot_type === 'bits' ) {
|
||||
var amount = loot.quantity,
|
||||
tier = bits_service.ffz_get_tier('Cheer', amount) || [null, null];
|
||||
if ( tier[1] )
|
||||
out = '<span class="emoticon js-bits-emote-image ffz-bit bit-prefix-Cheer bit-tier-' + tier[0] + ' inventory-bits__image" data-prefix="Cheer" data-amount="' + utils.number_commas(amount) + '"></span>';
|
||||
|
||||
} else if ( loot.img )
|
||||
out = '<img src="' + utils.quote_san(loot.img) + '">';
|
||||
|
||||
return out ? '<div class="chat-commerce-rich-content__' + utils.quote_san(loot_type) + ' chat-commerce-rich-content__image' + (extra_classes ? ' ' + extra_classes : '') + '">' + out + '</div>' : '';
|
||||
},
|
||||
|
||||
buildBadgesHTML: function() {
|
||||
if ( ! this.get('msgObject.room') && this.get('msgObject.payday_timestamp') )
|
||||
this.set('msgObject.room', Chat.get('currentRoom.id'));
|
||||
|
@ -1225,6 +1463,9 @@ FFZ.prototype._modify_chat_line = function(component, is_vod) {
|
|||
if ( (this.get('isAutoModPromptSmaller') || ! f.settings.automod_inline) && this.get('msgObject.autoModRejected') )
|
||||
output += this.buildAutoModHTML();
|
||||
|
||||
if ( this.get('msgObject.tags.content') )
|
||||
output += '<div class="ffz-rich-content">' + this.buildRichContentHTML() + '</div>';
|
||||
|
||||
el.innerHTML = output;
|
||||
},
|
||||
|
||||
|
@ -1533,6 +1774,8 @@ FFZ.prototype._modify_chat_subline = function(component, is_whisper) {
|
|||
if ( ! target )
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var n = this.get('element'),
|
||||
bounds = n && n.getBoundingClientRect() || document.body.getBoundingClientRect(),
|
||||
x = 0, right;
|
||||
|
@ -1551,6 +1794,11 @@ FFZ.prototype._modify_chat_subline = function(component, is_whisper) {
|
|||
} else if ( cl.contains('undelete') ) {
|
||||
e.preventDefault();
|
||||
this.set("msgObject.deleted", false);
|
||||
|
||||
} else if ( cl.contains('chat-commerce-right-content__pocket--button') ) {
|
||||
e.preventDefault();
|
||||
this._ffz_commerce_drawer_open = !this._ffz_commerce_drawer_open;
|
||||
this.ffzUpdateRichContent();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1697,7 +1945,7 @@ FFZ.get_capitalization = function(name, callback) {
|
|||
try {
|
||||
typeof waiting[i] === "function" && waiting[i](cap_name);
|
||||
} catch(err) { }
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
return old_data ? old_data[0] : name;
|
||||
|
|
|
@ -3,6 +3,11 @@ var FFZ = window.FrankerFaceZ,
|
|||
constants = require('../constants'),
|
||||
utils = require('../utils'),
|
||||
tmimotes,
|
||||
commerce,
|
||||
|
||||
CLIP_URL = /\b(?:https?:\/\/)?clips\.twitch\.tv\/(\w+)(?:\/)?(\w+)?(?:\/edit)?\b/,
|
||||
VIDEO_URL = /\b(?:https?:\/\/)?(?:www\.)?twitch\.tv\/(?:\w+\/v|videos)\/(\w+)(?:\?[\w\.-=]*)?\b/,
|
||||
FFZ_URL = /\b(?:https?:\/\/)?(?:www\.)?frankerfacez\.com\/emoticon\/(\d+)(?:-\w*)?\b/,
|
||||
|
||||
NOTICE_MAPPING = {
|
||||
'slow': 'slow_on',
|
||||
|
@ -58,6 +63,10 @@ FFZ.prototype.setup_room = function() {
|
|||
tmimotes = window.require && window.require("web-client/utilities/tmi-emotes").default;
|
||||
} catch(err) { }
|
||||
|
||||
try {
|
||||
commerce = window.require && window.require("web-client/utils/commerce/chat");
|
||||
} catch(err) { }
|
||||
|
||||
this.log("Creating room style element.");
|
||||
var f = this,
|
||||
s = this._room_style = document.createElement("style");
|
||||
|
@ -2321,6 +2330,55 @@ FFZ.prototype._modify_room = function(room) {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle Rich Content
|
||||
if ( commerce && commerce.addCommerceParamsAsRichContent )
|
||||
commerce.addCommerceParamsAsRichContent(msg);
|
||||
|
||||
/*var first_clip = CLIP_URL.exec(msg.message);
|
||||
if ( first_clip ) {
|
||||
msg.tags.content = msg.tags.content || {};
|
||||
msg.tags.content.clips = msg.tags.content.clips || [];
|
||||
msg.tags.content.clips.push({
|
||||
index: first_clip.index,
|
||||
removeOriginal: true,
|
||||
data: {
|
||||
url: first_clip[0],
|
||||
slug: first_clip[1]
|
||||
}
|
||||
})
|
||||
|
||||
} else {
|
||||
var first_vid = VIDEO_URL.exec(msg.message);
|
||||
if ( first_vid ) {
|
||||
msg.tags.content = msg.tags.content || {};
|
||||
msg.tags.content.clips = msg.tags.content.clips || [];
|
||||
msg.tags.content.clips.push({
|
||||
index: first_vid.index,
|
||||
removeOriginal: true,
|
||||
data: {
|
||||
is_video: true,
|
||||
url: first_vid[0],
|
||||
video: first_vid[1]
|
||||
}
|
||||
})
|
||||
|
||||
} /*else {
|
||||
var first_ffz = FFZ_URL.exec(msg.message);
|
||||
if ( first_ffz ) {
|
||||
msg.tags.content = msg.tags.content || {};
|
||||
msg.tags.content.ffz_emotes = msg.tags.content.ffz_emotes || [];
|
||||
msg.tags.content.ffz_emotes.push({
|
||||
index: first_ffz.index,
|
||||
removeOriginal: true,
|
||||
data: {
|
||||
url: first_ffz[0],
|
||||
id: first_ffz[1]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Tokenization
|
||||
f.tokenize_chat_line(msg, false, this.get('roomProperties.hide_chat_links'));
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ FFZ.channel_metadata = {};
|
|||
|
||||
// Version
|
||||
var VER = FFZ.version_info = {
|
||||
major: 3, minor: 5, revision: 514,
|
||||
major: 3, minor: 5, revision: 521,
|
||||
toString: function() {
|
||||
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
background-color: rgba(0,0,0, 0.1);
|
||||
}
|
||||
|
||||
.theme--dark .special-message .system-msg,
|
||||
.special-message .system-msg { background-color: transparent }
|
||||
|
||||
|
||||
/* Dark: Alternating Background */
|
||||
.ffz-dark .conversation-chat-lines > div:nth-child(2n+0),
|
||||
|
|
342
src/tokenize.js
342
src/tokenize.js
|
@ -21,7 +21,8 @@ var FFZ = window.FrankerFaceZ,
|
|||
LINK = /(?:https?:\/\/)?(?:[-a-zA-Z0-9@:%_\+~#=]+\.)+[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=()]*)/g,
|
||||
|
||||
CLIP_URL = /^(?:https?:\/\/)?clips\.twitch\.tv\/(\w+?\/?\w*?)(?:\/edit)?(?:[\?#]|$)/,
|
||||
VIDEO_URL = /^(?:https?:\/\/)?(?:www\.)twitch\.tv\/(?:\w+\/v|videos)\/(\w+)$/,
|
||||
VIDEO_URL = /^(?:https?:\/\/)?(?:www\.)?twitch\.tv\/(?:\w+\/v|videos)\/(\w+)$/,
|
||||
FFZ_EMOTE_URL = /^(?:https?:\/\/)?(?:www\.)?frankerfacez\.com\/emoticon\/(\d+)(?:-\w*)?$/,
|
||||
|
||||
LINK_SPLIT = /^(?:(https?):\/\/)?(?:(.*?)@)?([^\/:]+)(?::(\d+))?(.*?)(?:\?(.*?))?(?:\#(.*?))?$/,
|
||||
YOUTUBE_CHECK = /^(?:https?:\/\/)?(?:m\.|www\.)?youtu(?:be\.com|\.be)\/(?:v\/|watch\/|.*?(?:embed|watch).*?v=)?([a-zA-Z0-9\-_]+)$/,
|
||||
|
@ -200,7 +201,7 @@ FFZ.prototype.setup_tokenization = function() {
|
|||
this._twitch_set_to_channel = {};
|
||||
this._link_data = {};
|
||||
|
||||
this.load_twitch_emote_data();
|
||||
//this.load_twitch_emote_data();
|
||||
utils.toggle_cls('ffz-clickable-mentions')(this.settings.clickable_mentions);
|
||||
|
||||
try {
|
||||
|
@ -347,7 +348,7 @@ FFZ.prototype.format_display_name = function(display_name, user_id, disable_alia
|
|||
// Twitch Emote Data
|
||||
// ---------------------
|
||||
|
||||
FFZ.prototype.load_twitch_emote_data = function(tries) {
|
||||
/*FFZ.prototype.load_twitch_emote_data = function(tries) {
|
||||
var f = this;
|
||||
f._twitch_set_to_channel[0] = "--global--";
|
||||
f._twitch_set_to_channel[33] = "--turbo-faces--";
|
||||
|
@ -355,7 +356,7 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
f._twitch_set_to_channel[19194] = "--prime--";
|
||||
f._twitch_set_to_channel[19151] = "--curse--";
|
||||
|
||||
this.log("Loading Twitch Emote Data (Try " + (tries || 0) + ")");
|
||||
/*this.log("Loading Twitch Emote Data (Try " + (tries || 0) + ")");
|
||||
|
||||
jQuery.ajax(constants.SERVER + "twitch_emotes.json")
|
||||
.done(function(data) {
|
||||
|
@ -381,7 +382,91 @@ FFZ.prototype.load_twitch_emote_data = function(tries) {
|
|||
tries = (tries || 0) + 1;
|
||||
if ( tries < 10 )
|
||||
setTimeout(f.load_twitch_emote_data.bind(f, tries), 1000);
|
||||
});
|
||||
});*
|
||||
}*/
|
||||
|
||||
var UNSET = {};
|
||||
|
||||
FFZ.prototype.get_twitch_set_for = function(emote_id, callback) {
|
||||
if ( typeof emote_id !== "number" )
|
||||
emote_id = parseInt(emote_id);
|
||||
|
||||
if ( isNaN(emote_id) || ! isFinite(emote_id) )
|
||||
return null;
|
||||
|
||||
if ( this._twitch_emote_to_set.hasOwnProperty(emote_id) && this._twitch_emote_to_set[emote_id] !== UNSET )
|
||||
return this._twitch_emote_to_set[emote_id];
|
||||
|
||||
this._twitch_emote_to_set[emote_id] = null;
|
||||
var f = this,
|
||||
use_ss = this._ws_open && Math.random() > .5,
|
||||
cb = function(success, data) {
|
||||
if ( ! success ) {
|
||||
f._twitch_emote_to_set[emote_id] = UNSET;
|
||||
return;
|
||||
}
|
||||
|
||||
var set_id = null;
|
||||
if ( data ) {
|
||||
set_id = data['s_id'];
|
||||
f._twitch_set_to_channel[set_id] = data;
|
||||
}
|
||||
|
||||
f._twitch_emote_to_set[emote_id] = set_id;
|
||||
if ( callback )
|
||||
callback(set_id);
|
||||
};
|
||||
|
||||
if ( use_ss )
|
||||
this.ws_send("get_emote", emote_id, cb);
|
||||
else
|
||||
fetch(constants.API_SERVER = "ed/emote/" + emote_id)
|
||||
.then(function(resp) {
|
||||
if ( ! resp.ok )
|
||||
return cb(false, null);
|
||||
resp.json().then(function(data) {
|
||||
cb(true, data);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.get_twitch_set = function(set_id, callback) {
|
||||
if ( typeof set_id !== "number" )
|
||||
set_id = parseInt(set_id);
|
||||
|
||||
if ( isNaN(set_id) || ! isFinite(set_id) )
|
||||
return null;
|
||||
|
||||
if ( this._twitch_set_to_channel.hasOwnProperty(set_id) && this._twitch_set_to_channel[set_id] !== UNSET )
|
||||
return this._twitch_set_to_channel[set_id];
|
||||
|
||||
this._twitch_set_to_channel[set_id] = null;
|
||||
|
||||
var f = this,
|
||||
use_ss = this._ws_open && Math.random() > .5,
|
||||
cb = function(success, data) {
|
||||
if ( ! success ) {
|
||||
f._twitch_set_to_channel[set_id] = UNSET;
|
||||
return;
|
||||
}
|
||||
|
||||
f._twitch_set_to_channel[set_id] = data || null;
|
||||
if ( callback )
|
||||
callback(data || null);
|
||||
};
|
||||
|
||||
if ( use_ss )
|
||||
this.ws_send("get_emote_set", set_id, cb);
|
||||
else
|
||||
fetch(constants.API_SERVER + "ed/set/" + set_id)
|
||||
.then(function(resp) {
|
||||
if ( ! resp.ok )
|
||||
return cb(false, null);
|
||||
resp.json().then(function(data) {
|
||||
cb(true, data);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -423,6 +508,7 @@ FFZ.prototype.render_tooltip = function(el) {
|
|||
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'),
|
||||
sellout_text = this.getAttribute('data-sellout'),
|
||||
mod_text = '';
|
||||
|
||||
if ( modifiers ) {
|
||||
|
@ -433,6 +519,9 @@ FFZ.prototype.render_tooltip = function(el) {
|
|||
}).join('<br>');
|
||||
}
|
||||
|
||||
if ( sellout_text )
|
||||
mod_text = '<hr>' + sellout_text + mod_text;
|
||||
|
||||
if ( emote_id ) {
|
||||
if ( emote_id == "93269" )
|
||||
return '';
|
||||
|
@ -480,7 +569,8 @@ FFZ.prototype.render_tooltip = function(el) {
|
|||
emote_id = this.getAttribute('data-emote');
|
||||
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_data = set_id && f.get_twitch_set(set_id);
|
||||
emote_set = set_data && set_data.c_name;
|
||||
|
||||
var set_type = "Channel",
|
||||
favorite_key = 'twitch-' + set_id;
|
||||
|
@ -615,6 +705,11 @@ FFZ.prototype.tokenize_conversation_line = function(message, prevent_notificatio
|
|||
if ( helpers && helpers.emoticonizeMessage && emotes && this.settings.parse_emoticons )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
// Pre-load emote information.
|
||||
if ( emotes )
|
||||
for(var emote_id in emotes)
|
||||
this.get_twitch_set_for(emote_id);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
|
||||
|
@ -667,6 +762,11 @@ FFZ.prototype.tokenize_vod_line = function(msgObject, delete_links) {
|
|||
if ( helpers && helpers.emoticonizeMessage && emotes && this.settings.parse_emoticons )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
// Pre-load emote information.
|
||||
if ( emotes )
|
||||
for(var emote_id in emotes)
|
||||
this.get_twitch_set_for(emote_id);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
|
||||
|
@ -713,6 +813,185 @@ FFZ.prototype._tokenize_bits = function(tokens) {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype.tokenize_rich_content = function(tokens, content) {
|
||||
'use strict';
|
||||
|
||||
// First, we want to get the indices of all the existing rich content elements.
|
||||
// This should only really be grabbing commerce content.
|
||||
var indices = [];
|
||||
for(var content_type in content) {
|
||||
var cont = content[content_type];
|
||||
for(var i=0; i < cont.length; i++) {
|
||||
var c = cont[i];
|
||||
if ( c.removeOriginal && c.index >= 0 )
|
||||
indices.push(c.index);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize tokens.
|
||||
if ( typeof tokens === 'string' )
|
||||
tokens = [{type: 'text', text: tokens}];
|
||||
|
||||
var providers = FFZ.rich_content_providers;
|
||||
|
||||
// Iterate tokens.
|
||||
var idx = 0;
|
||||
for(var i=0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if ( typeof token === 'string' )
|
||||
token = tokens[i] = {type: 'text', text: token};
|
||||
|
||||
// If a token's index matches rich content, then the token is being
|
||||
// expressed as rich content and it should be suppressed when the
|
||||
// message is rendered with rich content.
|
||||
if ( indices.indexOf(idx) !== -1 ) {
|
||||
token.rich_removed = true;
|
||||
|
||||
} else {
|
||||
// However, if it doesn't match existing rich content, then we
|
||||
// should proceed to check it using our rich content providers.
|
||||
for(var pk in providers) {
|
||||
var provider = providers[pk];
|
||||
if ( ! provider.type || token.type === provider.type ) {
|
||||
var cont = provider.extract(token);
|
||||
if ( cont ) {
|
||||
token.rich_removed = provider.remove_token;
|
||||
content[pk] = content[pk] || [];
|
||||
content[pk].push({
|
||||
index: idx,
|
||||
removeOriginal: provider.remove_token,
|
||||
data: cont
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idx += token.length || (token.text && token.text.length) || 1;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
|
||||
FFZ.rich_content_providers = {
|
||||
/*ffz_emote: {
|
||||
token_type: 'link',
|
||||
remove_token: true,
|
||||
display_name: 'FFZ emote',
|
||||
|
||||
extract: function(token) {
|
||||
var href = token.link || token.text,
|
||||
match = FFZ_EMOTE_URL.exec(href);
|
||||
|
||||
if ( match )
|
||||
return {
|
||||
url: href,
|
||||
id: match[1]
|
||||
}
|
||||
},
|
||||
|
||||
get_info: function(info) { return new Promise(function(s,f) {
|
||||
fetch("https://api.frankerfacez.com/v1/emote/" + info.id)
|
||||
.then(utils.json).then(function(data) {
|
||||
if ( ! data || ! data.emote )
|
||||
return f();
|
||||
|
||||
var em = data.emote;
|
||||
|
||||
s({
|
||||
image: em.urls[2] || em.urls[1],
|
||||
url: info.url,
|
||||
title: utils.sanitize(em.name) + ' by <span class="user-token" data-user="' + utils.quote_attr(em.owner.name) + '">' + utils.sanitize(em.owner.display_name) + '</span>',
|
||||
by_lines: [
|
||||
(em.public ? 'Public' : 'Private') + ' FFZ Emote'
|
||||
]
|
||||
})
|
||||
})
|
||||
})}
|
||||
},*/
|
||||
|
||||
video: {
|
||||
token_type: 'link',
|
||||
remove_token: true,
|
||||
|
||||
extract: function(token) {
|
||||
var href = token.link || token.text,
|
||||
match = VIDEO_URL.exec(href);
|
||||
|
||||
if ( match )
|
||||
return {
|
||||
url: href,
|
||||
id: match[1]
|
||||
}
|
||||
},
|
||||
|
||||
get_info: function(info) { return new Promise(function(s,f) {
|
||||
utils.api.get("videos/" + info.id, undefined, {version: 5}).then(function(data) {
|
||||
var published = utils.parse_date(data.recorded_at),
|
||||
now = new Date,
|
||||
raw_age = (now - published) / 1000,
|
||||
age = raw_age >= 86400 ? published.toLocaleDateString() : utils.full_human_time(raw_age);
|
||||
|
||||
s({
|
||||
image: data.preview.small,
|
||||
title: utils.sanitize(data.title || 'Untitled Video'),
|
||||
url: info.url,
|
||||
by_lines: [
|
||||
'<span class="user-token" data-user="' + utils.quote_attr(data.channel.name) + '">' + utils.sanitize(data.channel.display_name) + '</span>' +
|
||||
(data.game === 'Creative' ? ' being Creative' : data.game ? ' playing ' + utils.sanitize(data.game) : ''),
|
||||
utils.time_to_string(data.length || 0) +
|
||||
' — ' + utils.number_commas(data.views) + ' Views' +
|
||||
(published ?
|
||||
' — <span class="html-tooltip" title="Published: <nobr>' +
|
||||
utils.quote_san(published.toLocaleString()) + '</nobr>">' +
|
||||
utils.sanitize(age) +
|
||||
'</span>' : '')
|
||||
]
|
||||
})
|
||||
}).fail(f)
|
||||
})}
|
||||
},
|
||||
|
||||
clip: {
|
||||
token_type: 'link',
|
||||
remove_token: true,
|
||||
|
||||
get_info: function(info) { return new Promise(function(s,f) {
|
||||
var Clips = utils.ember_lookup('service:clips');
|
||||
if ( ! Clips )
|
||||
return f();
|
||||
|
||||
Clips.fetchClipBySlug(info.slug).then(function(data) {
|
||||
s({
|
||||
image: data.thumbnails.tiny,
|
||||
title: utils.sanitize(data.title || 'Untitled Clip'),
|
||||
url: info.url,
|
||||
by_lines: [
|
||||
'<span class="user-token" data-user="' + utils.quote_attr(data.broadcaster_login) + '">' + utils.sanitize(data.broadcaster_display_name) + '</span>' +
|
||||
(data.game === 'Creative' ? ' being Creative' : data.game ? ' playing ' + utils.sanitize(data.game) : ''),
|
||||
'Clipped by <span class="user-token" data-user="' + utils.quote_attr(data.curator_login) + '">' + utils.sanitize(data.curator_display_name) + '</span> — ' +
|
||||
utils.number_commas(data.views) + ' View' + utils.pluralize(data.views)
|
||||
]
|
||||
});
|
||||
}).catch(f)
|
||||
})},
|
||||
|
||||
extract: function(token) {
|
||||
var href = token.link || token.text,
|
||||
match = CLIP_URL.exec(href);
|
||||
|
||||
if ( match )
|
||||
return {
|
||||
url: href,
|
||||
slug: match[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, delete_links, disable_cache) {
|
||||
if ( msgObject.cachedTokens && ! disable_cache )
|
||||
return msgObject.cachedTokens;
|
||||
|
@ -749,6 +1028,11 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
if ( helpers && helpers.emoticonizeMessage && this.settings.parse_emoticons )
|
||||
tokens = helpers.emoticonizeMessage(tokens, emotes);
|
||||
|
||||
// Pre-load emote information.
|
||||
if ( emotes )
|
||||
for(var emote_id in emotes)
|
||||
this.get_twitch_set_for(emote_id);
|
||||
|
||||
// FrankerFaceZ Extras
|
||||
tokens = this._remove_banned(tokens);
|
||||
|
||||
|
@ -898,6 +1182,12 @@ FFZ.prototype.tokenize_chat_line = function(msgObject, prevent_notification, del
|
|||
// Tokenize users last.
|
||||
tokens = this.tokenize_users(tokens);
|
||||
|
||||
// Take care of rich content.
|
||||
if ( ! tags.content )
|
||||
tags.content = {};
|
||||
|
||||
this.tokenize_rich_content(tokens, tags.content);
|
||||
|
||||
if ( ! disable_cache )
|
||||
msgObject.cachedTokens = tokens;
|
||||
|
||||
|
@ -951,6 +1241,11 @@ FFZ.prototype.tokenize_feed_body = function(message, emotes, user_id, room_id) {
|
|||
if ( helpers && helpers.emoticonizeMessage && this.settings.parse_emoticons )
|
||||
message = helpers.emoticonizeMessage(message, emotes);
|
||||
|
||||
// Pre-load emote information.
|
||||
if ( emotes )
|
||||
for(var emote_id in emotes)
|
||||
this.get_twitch_set_for(emote_id);
|
||||
|
||||
// Tokenize Lines
|
||||
var tokens = [], token;
|
||||
|
||||
|
@ -986,7 +1281,7 @@ FFZ.prototype.render_token = function(render_links, warn_links, render_bits, tok
|
|||
if ( ! token )
|
||||
return "";
|
||||
|
||||
if ( token.hidden )
|
||||
if ( token.hidden || (this.settings.chat_rich_content && token.rich_removed) )
|
||||
return "";
|
||||
|
||||
else if ( token.type === "raw" )
|
||||
|
@ -1076,16 +1371,15 @@ FFZ.prototype.render_token = function(render_links, warn_links, render_bits, tok
|
|||
video_info = VIDEO_URL.exec(href);
|
||||
|
||||
if ( clip_info ) {
|
||||
var clips = utils.ember_lookup('service:store');
|
||||
clips && clips.findRecord && clips.findRecord('clip', clip_info[1]).then(function(data) {
|
||||
//clips && clips.getClipInfo(clip_info[1]).then(function(data) {
|
||||
var Clips = utils.ember_lookup('service:clips');
|
||||
Clips && Clips.fetchClipBySlug(clip_info[1]).then(function(data) {
|
||||
data &&
|
||||
success(true, {
|
||||
image: data.get('scaledPreviewUrl'),
|
||||
image: data.thumbnails.medium,
|
||||
image_iframe: false,
|
||||
html: '<span class="ffz-clip-title">' + utils.sanitize(data.get('title')) + '</span>' +
|
||||
'Channel: ' + utils.sanitize(data.get('broadcasterDisplayName')) +
|
||||
'<br>Game: ' + utils.sanitize(data.get('game'))
|
||||
html: '<span class="ffz-clip-title">' + utils.sanitize(data.title) + '</span>' +
|
||||
'Channel: ' + utils.sanitize(data.broadcaster_display_name) +
|
||||
'<br>Game: ' + utils.sanitize(data.game)
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1286,8 +1580,24 @@ FFZ.prototype.tokenize_emotes = function(user, room, tokens, do_report) {
|
|||
if ( token.type === "text" )
|
||||
token = token.text;
|
||||
else {
|
||||
if ( ! token.modifiers && token.type === 'emoticon' )
|
||||
token.modifiers = [];
|
||||
if ( token.type === 'emoticon' ) {
|
||||
emote = emotes[token.altText];
|
||||
if ( emote && emote.replaces ) {
|
||||
token = _.extend({}, emote.token);
|
||||
token.modifiers = [];
|
||||
new_tokens.push(token);
|
||||
last_token = token;
|
||||
|
||||
if ( do_report && room )
|
||||
this.add_usage(room, emote);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ( ! token.modifiers )
|
||||
token.modifiers = [];
|
||||
}
|
||||
|
||||
new_tokens.push(token);
|
||||
last_token = token;
|
||||
|
|
263
src/ui/menu.js
263
src/ui/menu.js
|
@ -453,17 +453,18 @@ FFZ.menu_pages.channel = {
|
|||
has_product = true;
|
||||
var Ticket = utils.ember_resolve('model:ticket'),
|
||||
tickets = Ticket && Ticket.find('user', {channel: room_id}),
|
||||
is_subscribed = tickets ? tickets.get('content') : false,
|
||||
subbed_products = _.pluck(tickets && tickets.get('content') || [], 'product'),
|
||||
subbed_plans = _.pluck(subbed_products, 'short_name'),
|
||||
subbed_emote_sets = _.uniq(_.flatten(_.map(subbed_products, function(x) { return x.features.emoticon_set_ids }))),
|
||||
|
||||
is_subscribed = subbed_plans.length > 0,
|
||||
is_loaded = tickets ? tickets.get('isLoaded') : false,
|
||||
icon = room.room.get("badgeSet.subscriber.image"),
|
||||
icon,
|
||||
|
||||
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;
|
||||
|
||||
// See if we've loaded. If we haven't loaded the ticket yet
|
||||
// then try loading it, and then re-render the menu.
|
||||
if ( tickets && ! is_subscribed && ! is_loaded ) {
|
||||
|
@ -481,6 +482,10 @@ FFZ.menu_pages.channel = {
|
|||
tickets.load();
|
||||
}
|
||||
|
||||
// Null-conditional try/catch
|
||||
try {
|
||||
icon = room.badges.subscriber.versions[0].image_url_1x;
|
||||
} catch(err) { }
|
||||
|
||||
grid.className = "emoticon-grid top-set";
|
||||
header.className = "heading";
|
||||
|
@ -493,108 +498,168 @@ FFZ.menu_pages.channel = {
|
|||
header.innerHTML = '<span class="right">Twitch</span>Subscriber Emoticons';
|
||||
grid.appendChild(header);
|
||||
|
||||
var known_sets = [];
|
||||
var all_emotes = {},
|
||||
plans = product.get("plans") || [],
|
||||
pwe = 0;
|
||||
|
||||
for(var emotes=product.get("emoticons") || [], i=0; i < emotes.length; i++) {
|
||||
var emote = emotes[i];
|
||||
if ( emote.state !== "active" )
|
||||
continue;
|
||||
if ( ! plans.length ) {
|
||||
// If we have a product with no defined plans, fake a plan with the required
|
||||
// information to keep our script happy.
|
||||
plans.push({
|
||||
name: product.name,
|
||||
product_url: product.product_url,
|
||||
price: product.price,
|
||||
|
||||
var s = document.createElement('span'),
|
||||
can_use = is_subscribed || !emote.subscriber_only,
|
||||
img_set = 'image-set(url("' + constants.TWITCH_BASE + emote.id + '/1.0") 1x, url("' + constants.TWITCH_BASE + emote.id + '/2.0") 2x), url("' + constants.TWITCH_BASE + emote.id + '/3.0") 4x)';
|
||||
emoticon_set_ids: _.uniq(_.pluck(product.emoticons, 'emoticon_set')),
|
||||
emoticons: product.emoticons
|
||||
})
|
||||
}
|
||||
|
||||
s.className = 'emoticon ffz-tooltip ffz-tooltip-no-credit' + (!can_use ? " locked" : "");
|
||||
for(var i=0; i < plans.length; i++) {
|
||||
var plan = plans[i],
|
||||
emotes = plan.emoticons || [],
|
||||
jm = emotes.length;
|
||||
|
||||
if ( known_sets.indexOf(emote.emoticon_set) === -1 )
|
||||
known_sets.push(emote.emoticon_set);
|
||||
if ( jm > 0 )
|
||||
pwe++;
|
||||
|
||||
if ( emote.emoticon_set ) {
|
||||
var favs = this.settings.favorite_emotes["twitch-" + emote.emoticon_set];
|
||||
s.classList.add('ffz-can-favorite');
|
||||
s.classList.toggle('ffz-favorite', favs && favs.indexOf(emote.id) !== -1 || false);
|
||||
for(var j = 0; j < jm; j++) {
|
||||
var emote = emotes[j];
|
||||
if ( emote.state !== "active" )
|
||||
continue;
|
||||
|
||||
var ae = all_emotes[emote.regex] = all_emotes[emote.regex] || [];
|
||||
ae.push([emote, i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(var ek in all_emotes) {
|
||||
var ems = all_emotes[ek];
|
||||
for(var i=0, l = ems.length; i < l; i++) {
|
||||
var emote = ems[i][0],
|
||||
plan_idx = ems[i][1],
|
||||
plan = plans[plan_idx],
|
||||
s = utils.createElement('span', 'emoticon ffz-tooltip ffz-tooltip-no-credit'),
|
||||
|
||||
set_id = emote.emoticon_set,
|
||||
can_use = ! emote.subscriber_only || subbed_emote_sets.indexOf(set_id) !== -1,
|
||||
img_set = utils.build_srcset(emote.id);
|
||||
|
||||
if ( ! can_use ) {
|
||||
s.classList.add('locked');
|
||||
s.dataset.sellout = 'Subscribe for ' + utils.sanitize(plan.price) + ' to unlock <nobr>this emote.</nobr>';
|
||||
}
|
||||
|
||||
if ( set_id ) {
|
||||
var faves = this.settings.favorite_emotes["twitch-" + set_id];
|
||||
s.classList.add('ffz-can-favorite');
|
||||
if ( faves && faves.indexOf(emote.id) !== -1 )
|
||||
s.classList.add('ffz-favorite');
|
||||
}
|
||||
|
||||
s.dataset.plan = plan_idx;
|
||||
s.dataset.emote = emote.id;
|
||||
s.dataset.set = set_id;
|
||||
s.alt = emote.regex;
|
||||
|
||||
s.style.backgroundImage = 'url("' + constants.TWITCH_BASE + emote.id + '/1.0")';
|
||||
s.style.backgroundImage = img_set;
|
||||
|
||||
s.style.width = (10 + emote.width) + "px";
|
||||
s.style.height = (10 + emote.height) + "px";
|
||||
|
||||
s.addEventListener('click', function(can_use, emote_id, code, set_id, plan_idx, e) {
|
||||
e.preventDefault();
|
||||
if ( ( e.shiftKey || e.shiftLeft) && this.settings.clickable_emoticons )
|
||||
window.open("https://twitchemotes.com/emote/" + emote_id);
|
||||
else if ( can_use )
|
||||
this._add_emote(view, code, "twitch-" + set_id, emote_id, e);
|
||||
else {
|
||||
var plan = plans[plan_idx],
|
||||
url = plan && plan.product_url;
|
||||
url && window.open(url);
|
||||
}
|
||||
|
||||
}.bind(this, can_use, emote.id, emote.regex, set_id, plan_idx));
|
||||
|
||||
grid.appendChild(s);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( c > 0 ) {
|
||||
inner.appendChild(grid);
|
||||
var msg;
|
||||
|
||||
if ( ! is_loaded ) {
|
||||
msg = 'Loading sub information...';
|
||||
|
||||
} else if ( ! is_subscribed && plans.length ) {
|
||||
var sub_message = utils.createElement('div', null, 'Subscribe to unlock ' + (pwe === 1 ? utils.number_commas(c) : 'some') + ' Sub Emotes'),
|
||||
sub_container = utils.createElement('div', 'mg-l-1 mg-r-1 align-center', sub_message),
|
||||
had_prime = false,
|
||||
|
||||
filter_grid = function(price, sets) {
|
||||
var kids = grid.querySelectorAll('.emoticon'),
|
||||
count = 0;
|
||||
for(var i=0, l = kids.length; i < l; i++) {
|
||||
var emote = kids[i],
|
||||
set_id = parseInt(emote.dataset.set),
|
||||
would_unlock = sets ? sets.indexOf(set_id) !== -1 : false;
|
||||
|
||||
if ( would_unlock )
|
||||
count++;
|
||||
|
||||
emote.classList.toggle('unlocked', would_unlock);
|
||||
}
|
||||
|
||||
sub_message.textContent = price ?
|
||||
'Subscribe for ' + price +
|
||||
' to unlock ' + utils.number_commas(count) + ' Sub Emotes'
|
||||
: sub_message.dataset.original;
|
||||
};
|
||||
|
||||
sub_message.dataset.original = sub_message.textContent;
|
||||
|
||||
for(var i=0; i < plans.length; i++) {
|
||||
var plan = plans[i],
|
||||
btn = utils.createElement('a', 'ffz-sub-button button mg-t-1', utils.sanitize(plan.price));
|
||||
|
||||
sub_container.appendChild(btn);
|
||||
|
||||
btn.href = plan.product_url;
|
||||
btn.target = '_blank';
|
||||
btn.rel = 'noopener noreferrer';
|
||||
|
||||
btn.addEventListener('mouseover', filter_grid.bind(this, plan.price, plan.emoticon_set_ids));
|
||||
btn.addEventListener('mouseout', filter_grid.bind(this, null, null));
|
||||
}
|
||||
|
||||
inner.appendChild(sub_container);
|
||||
|
||||
} else if ( plans.length ) {
|
||||
// We are subscribed. Check to see if the subscription will expire.
|
||||
var content = tickets.get('content.lastObject'),
|
||||
pp = content && content.purchase_profile,
|
||||
ends_at = content && utils.parse_date(content.access_end);
|
||||
|
||||
if ( pp && ends_at ) {
|
||||
var now = Date.now() - (this._ws_server_offset || 0),
|
||||
end_time = ends_at ? Math.floor((ends_at.getTime() - now) / 1000) : null,
|
||||
|
||||
provider = pp.payment_provider,
|
||||
renews = pp.will_renew;
|
||||
|
||||
msg = 'Subscription ' + (renews ? 'renews' : 'expires') +
|
||||
' in ' + utils.time_to_string(end_time, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
s.setAttribute('data-emote', emote.id);
|
||||
s.alt = emote.regex;
|
||||
if ( msg ) {
|
||||
var sub_message = utils.createElement('div', null, msg),
|
||||
sub_container = utils.createElement('div', 'mg-l-1 mg-r-1 align-center', sub_message);
|
||||
|
||||
s.style.backgroundImage = 'url("' + constants.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 = (10+emote.width) + "px";
|
||||
s.style.height = (10+emote.height) + "px";
|
||||
|
||||
s.addEventListener('click', function(can_use, id, code, emote_set, e) {
|
||||
if ( (e.shiftKey || e.shiftLeft) && f.settings.clickable_emoticons )
|
||||
window.open("https://twitchemotes.com/emote/" + id);
|
||||
else if ( can_use )
|
||||
this._add_emote(view, code, "twitch-" + emote_set, id, e);
|
||||
else
|
||||
return;
|
||||
e.preventDefault();
|
||||
}.bind(this, can_use, emote.id, emote.regex, emote.emoticon_set));
|
||||
|
||||
grid.appendChild(s);
|
||||
c++;
|
||||
}
|
||||
|
||||
if ( reported_sets.indexOf(product.get('id')) === -1 && known_sets.length ) {
|
||||
reported_sets.push(product.get('id'));
|
||||
this.log("Sets for " + product.get('id') + " [" + product.get('ticketProductId') + "]: " + JSON.stringify(known_sets));
|
||||
this.ws_send("report_twitch_set", [product.get('id'), product.get('ticketProductId'), known_sets]);
|
||||
}
|
||||
|
||||
|
||||
if ( c > 0 )
|
||||
inner.appendChild(grid);
|
||||
|
||||
if ( c > 0 && ! 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 js-sub-button subscribe-button button button--purchase";
|
||||
sub_link.href = product.get("product_url");
|
||||
sub_link.innerHTML = '<span class="subscribe-text">Subscribe</span><span class="subscribe-price button__num-block">' + product.get("price") + '</span>';
|
||||
nonsub_message.appendChild(sub_link);
|
||||
|
||||
inner.appendChild(sub_message);
|
||||
} else if ( c > 0 ) {
|
||||
var last_content = tickets.get("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 || ""),
|
||||
provider = last_content.purchase_profile && last_content.purchase_profile.payment_provider,
|
||||
sub_message = document.createElement("div"),
|
||||
nonsub_message = document.createElement("div"),
|
||||
unlock_text = document.createElement("span"),
|
||||
now = Date.now() - (this._ws_server_offset || 0),
|
||||
end_time = ends_at ? Math.floor((ends_at.getTime() - 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);
|
||||
|
||||
if ( provider === "samus" )
|
||||
unlock_text.innerHTML += '<br>(Twitch Prime Free Sub)';
|
||||
|
||||
nonsub_message.appendChild(unlock_text);
|
||||
inner.appendChild(sub_message);
|
||||
inner.appendChild(sub_container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,12 @@ FFZ.settings_info.favorite_emotes = {
|
|||
|
||||
|
||||
FFZ.prototype.setup_my_emotes = function() {
|
||||
var UserEmotes = utils.ember_lookup('service:user-emotes');
|
||||
if ( UserEmotes ) {
|
||||
this.modify_user_emotes(UserEmotes);
|
||||
UserEmotes.ffzUpdateData();
|
||||
}
|
||||
|
||||
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";
|
||||
|
@ -89,6 +95,27 @@ FFZ.prototype.setup_my_emotes = function() {
|
|||
}
|
||||
|
||||
|
||||
FFZ.prototype.modify_user_emotes = function(service) {
|
||||
var f = this;
|
||||
service.reopen({
|
||||
ffzUpdateData: function() {
|
||||
var emotes = (this.get('allEmotes') || {})['emoticon_sets'] || {};
|
||||
for(var set_id in emotes) {
|
||||
f.get_twitch_set(set_id);
|
||||
var es = emotes[set_id] || [],
|
||||
esl = es.length;
|
||||
for(var i=0; i < esl; i++)
|
||||
f._twitch_emote_to_set[es[i].id] = set_id;
|
||||
}
|
||||
|
||||
if ( f._inputv )
|
||||
Ember.propertyDidChange(f._inputv, 'ffz_emoticons');
|
||||
|
||||
}.observes('allEmotes')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// -------------------
|
||||
// Menu Page
|
||||
// -------------------
|
||||
|
@ -101,7 +128,7 @@ FFZ.menu_pages.myemotes = {
|
|||
var user = this.get_user(),
|
||||
controller = utils.ember_lookup('controller:chat'),
|
||||
user_emotes = utils.ember_lookup('service:user-emotes'),
|
||||
twitch_sets = (user_emotes && user_emotes.allEmotes || {'emoticon_sets': {}})['emoticon_sets'] || {},
|
||||
twitch_sets = (user_emotes && user_emotes.allEmotes || {})['emoticon_sets'] || {},
|
||||
ffz_sets = user && this.users[user.login] && this.users[user.login].sets || [],
|
||||
|
||||
sk = twitch_sets && Object.keys(twitch_sets);
|
||||
|
@ -229,7 +256,7 @@ FFZ.menu_pages.myemotes = {
|
|||
render_lists: function(view, container, favorites_only) {
|
||||
var controller = utils.ember_lookup('controller:chat'),
|
||||
user_emotes = utils.ember_lookup('service:user-emotes'),
|
||||
twitch_sets = (user_emotes && user_emotes.allEmotes || {'emoticon_sets': {}})['emoticon_sets'] || {},
|
||||
twitch_sets = (user_emotes && user_emotes.allEmotes || {})['emoticon_sets'] || {},
|
||||
|
||||
user = this.get_user(),
|
||||
ffz_sets = this.getEmotes(user && user.login, null),
|
||||
|
@ -261,7 +288,8 @@ FFZ.menu_pages.myemotes = {
|
|||
continue;
|
||||
}
|
||||
|
||||
var raw_id = this._twitch_set_to_channel[set_id],
|
||||
var raw_data = this.get_twitch_set(set_id),
|
||||
raw_id = raw_data && raw_data.c_name,
|
||||
menu_id = raw_id ? raw_id.toLowerCase() : 'unknown',
|
||||
favorites_list = this.settings.favorite_emotes["twitch-" + set_id];
|
||||
|
||||
|
@ -528,7 +556,8 @@ FFZ.menu_pages.myemotes = {
|
|||
collapsed = ! favorites_only && this.settings.emote_menu_collapsed.indexOf('twitch-' + set_id) === -1,
|
||||
f = this,
|
||||
|
||||
channel_id = set_id === 'inventory' ? '--inventory--' : (this._twitch_set_to_channel[set_id] || 'twitch_unknown'), title,
|
||||
set_data = this.get_twitch_set(set_id),
|
||||
channel_id = set_id === 'inventory' ? '--inventory--' : (set_data && set_data.c_name || 'twitch_unknown'), title,
|
||||
favorites = this.settings.favorite_emotes["twitch-" + set_id] || [],
|
||||
c = 0;
|
||||
|
||||
|
@ -561,16 +590,18 @@ FFZ.menu_pages.myemotes = {
|
|||
heading.style.backgroundImage = 'url("' + icon + '")';
|
||||
if ( icon.indexOf('.svg') !== -1 )
|
||||
heading.style.backgroundSize = "18px";
|
||||
} else {
|
||||
} else if ( set_data ) {
|
||||
var f = this;
|
||||
utils.api.get("chat/" + channel_id + "/badges", null, {version: 3})
|
||||
.done(function(data) {
|
||||
if ( data.subscriber && data.subscriber.image ) {
|
||||
f._twitch_badges[channel_id] = data.subscriber.image;
|
||||
localStorage.ffzTwitchBadges = JSON.stringify(f._twitch_badges);
|
||||
heading.style.backgroundImage = 'url("' + data.subscriber.image + '")';
|
||||
}
|
||||
});
|
||||
fetch("https://badges.twitch.tv/v1/badges/channels/" + set_data.c_id + "/display?language=" + (Twitch.receivedLanguage || "en"), {
|
||||
headers: {
|
||||
'Client-ID': constants.CLIENT_ID
|
||||
}
|
||||
}).then(utils.json).then(function(data) {
|
||||
try {
|
||||
var badge = f._twitch_badges[channel_id] = data.badge_sets.subscriber.versions[0].image_url_1x;
|
||||
heading.style.backgroundImage = 'url("' + badge + '")';
|
||||
} catch(err) { /* Lament JS's lack of null coalescing operators */ }
|
||||
});
|
||||
}
|
||||
|
||||
menu.classList.add('collapsable');
|
||||
|
|
44
style.css
44
style.css
|
@ -564,6 +564,32 @@ body.ffz-bttv-dark .ffz-ui-toggle.blue.live:hover svg.svg-emoticons path { fill:
|
|||
|
||||
/* Dark Menu */
|
||||
|
||||
.ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon.locked:before {
|
||||
background-color: rgba(255,255,255,0.5);
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background-position: calc(100% - 2.5px) calc(100% - 2.5px);
|
||||
}
|
||||
|
||||
.ffz-dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon.locked:before,
|
||||
.theatre .ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon.locked:before,
|
||||
.theme--dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon.locked:before,
|
||||
.dark .ffz-ui-popup.emoticon-selector .emoticon-selector-box .emoticon.locked:before {
|
||||
background-color: rgba(16,16,16,0.5);
|
||||
}
|
||||
|
||||
.ffz-sub-button + .ffz-sub-button { border-left: 1px solid #ffffff }
|
||||
|
||||
.ffz-dark .ffz-sub-button + .ffz-sub-button,
|
||||
.theatre .ffz-sub-button + .ffz-sub-button,
|
||||
.theme--dark .ffz-sub-button + .ffz-sub-button,
|
||||
.dark .ffz-sub-button + .ffz-sub-button { border-left-color: #101010 }
|
||||
|
||||
.emoticon.locked.unlocked:before { display: none !important }
|
||||
|
||||
|
||||
#ffz-chat-menu { background-color: transparent !important; }
|
||||
|
||||
.ffz-dark .ember-chat .chat-menu .list-header,
|
||||
|
@ -2660,6 +2686,8 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
background-color: #191919;
|
||||
}
|
||||
|
||||
.ffz-no-blue .theme--dark .chat-chip,
|
||||
|
||||
.ffz-no-blue .app-main.theatre .bits-footer,
|
||||
.ffz-no-blue .theme--dark .bits-footer,
|
||||
.ffz-no-blue .dark .bits-footer,
|
||||
|
@ -2745,10 +2773,23 @@ body:not([data-current-path^="user."]) .ffz-sidebar-swap .ember-chat .chat-inter
|
|||
background-color: #080808;
|
||||
}
|
||||
|
||||
.ffz-no-blue .theme--dark .chat-commerce-rich-content__drawer,
|
||||
.ffz-no-blue .top-nav-drawer {
|
||||
background-color: #101010;
|
||||
}
|
||||
|
||||
.ffz-no-blue .theme--dark .chat-commerce-right-content__pocket--button:hover {
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.theme--dark .chat-chip {
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,.85);
|
||||
}
|
||||
|
||||
.ffz-rich-content p.card__info {
|
||||
line-height: 1.4rem;
|
||||
}
|
||||
|
||||
/* Following Count */
|
||||
|
||||
li[data-name="following"] a {
|
||||
|
@ -2993,6 +3034,7 @@ li[data-name="following"] a {
|
|||
|
||||
/* Directory Logos */
|
||||
|
||||
.chat-commerce-right-content__pocket--button > *,
|
||||
.item .meta .title a a,
|
||||
.item .meta .title a img { pointer-events: none }
|
||||
|
||||
|
@ -3713,7 +3755,7 @@ body.ffz-bttv #ffz-feed-tabs .tabs { margin-bottom: 0 }
|
|||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.ffz-bit:after {
|
||||
.ffz-bit:not(.inventory-bits__image):after {
|
||||
content: attr(data-amount);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue