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

3.5.349. Fix emote CSS generation, the view:room going away thing, add stuff to modifier emotes, fix tab completion. Yay. Closes #49

This commit is contained in:
SirStendec 2016-10-25 14:44:22 -04:00
parent 5d3ba8778c
commit 96cfa89351
9 changed files with 476 additions and 57 deletions

View file

@ -1,3 +1,20 @@
<div class="list-header">3.5.349 <time datetime="2016-10-25">(2016-10-25)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Tab completion was not being populated correctly.</li>
</ul>
<div class="list-header">3.5.348 <time datetime="2016-10-25">(2016-10-25)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>Fixed: Twitch replaced <code>view:room</code> with <code>component:chat/chat-room</code> which broke several features. Unbroke them.</li>
<li>Remaining Bugs: Bits eligibility isn't being recalculated when you switch to a different chat room.</li>
<li>Changed: Modifier emotes have more properties to control their appearance.</li>
</ul>
<div class="list-header">3.5.347 <time datetime="2016-10-25">(2016-10-25)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>API Fixed: Emote CSS was not being generated correctly.</li>
</ul>
<div class="list-header">3.5.346 <time datetime="2016-10-25">(2016-10-25)</time></div>
<ul class="chat-menu-content menu-side-padding">
<li>API Fixed: Emoticons' <code>modifier</code> flags were not being accepted.</li>

View file

@ -670,7 +670,7 @@ FFZ.prototype.modify_chat_input = function(component) {
ffz_commands: function() {
var commands = _.extend({}, DEFAULT_COMMANDS),
in_conversation = ConvoInput && this.parentView instanceof ConvoInput,
room = this.get('parentView.context.model'),
room = this.get('parentView.room'),
is_moderator = room && room.get('isModeratorOrHigher'),
user = f.get_user(),
is_broadcaster = room && user && user.login === room.get('name');
@ -732,14 +732,14 @@ FFZ.prototype.modify_chat_input = function(component) {
return commands;
}.property('parentView.context.model.isModeratorOrHigher', 'parentView.context.model.name'),
}.property('parentView.room.isModeratorOrHigher', 'parentView.room.name'),
ffz_emoticons: function() {
var emotes = {},
in_conversation = ConvoInput && this.parentView instanceof ConvoInput,
room = ! in_conversation && this.get('parentView.context.model'),
room = ! in_conversation && this.get('parentView.room'),
room_id = room && room.get('id'),
tmi = in_conversation ? window.TMI && TMI._sessions && TMI._sessions[0] : room && room.tmiSession,
@ -1387,7 +1387,7 @@ FFZ.prototype.modify_chat_input = function(component) {
return;
var ind = this.get('ffz_mru_index'),
mru = this.get('parentView.context.model.mru_list') || [];
mru = this.get('parentView.room.mru_list') || [];
if ( key === KEYCODES.UP )
ind = (ind + 1) % (mru.length + 1);

View file

@ -156,8 +156,12 @@ FFZ.prototype.setup_room = function() {
inst.ffzPatchTMI();
}
this.log("Hooking the Ember Room view.");
this.update_views('view:room', this.modify_room_view);
this.update_views('component:chat/chat-room', this.modify_room_component);
this.update_views('component:chat/chat-interface', this.modify_chat_interface);
//this.log("Hooking the Ember Room view.");
//this.update_views('view:room', this.modify_room_view);
}
@ -324,7 +328,390 @@ FFZ.prototype._modify_chat_pubsub = function(pubsub) {
// View Customization
// --------------------
FFZ.prototype.modify_room_view = function(view) {
FFZ.prototype.modify_chat_interface = function(component) {
var f = this;
utils.ember_reopen_view(component, {
ffz_init: function() {
f._chati = this;
},
ffz_destroy: function() {
if ( f._chati === this )
f._chati = undefined;
},
submitButtonText: function() {
if ( this.get('room.isWhisperMessage') )
return i18n('Whisper');
var wait = this.get('room.slowWait'),
msg = this.get('room.messageToSend') || '',
first_char = msg.charAt(0),
is_cmd = first_char === '/' || first_char === '.';
if ( (is_cmd && msg.substr(0,4) !== '/me ') || ! wait || ! f.settings.room_status )
return i18n('Chat');
return utils.time_to_string(wait, false, false, true);
}.property('room.isWhisperMessage', 'room.slowWait')
});
}
FFZ.HoverPause = {
ffz_frozen: false,
ffz_ctrl: false,
ffz_freeze_selector: '.chat-messages',
ffzAddKeyHook: function() {
if ( ! this._ffz_freeze_key ) {
var key = this._ffz_freeze_key = this.ffzFreezeOnKey.bind(this);
document.body.addEventListener('keydown', key);
document.body.addEventListener('keyup', key);
}
},
ffzRemoveKeyHook: function() {
if ( this._ffz_freeze_key ) {
var key = this._ffz_freeze_key;
this._ffz_freeze_key = null;
document.body.removeEventListener('keydown', key);
document.body.removeEventListener('keyup', key);
}
},
ffzEnableFreeze: function() {
var el = this.get('element'),
messages = el && el.querySelector(this.get('ffz_freeze_selector'));
if ( ! messages )
return;
this._ffz_freeze_messages = messages;
if ( ! this._ffz_freeze_mouse_move ) {
var mm = this._ffz_freeze_mouse_move = this.ffzFreezeMouseMove.bind(this);
messages.addEventListener('mousemove', mm);
messages.addEventListener('touchmove', mm);
}
if ( ! this._ffz_freeze_mouse_out ) {
var mo = this._ffz_freeze_mouse_out = this.ffzFreezeMouseOut.bind(this);
messages.addEventListener('mouseout', mo);
}
this.ffzAddKeyHook();
},
ffzDisableFreeze: function() {
if ( this._ffz_freeze_interval ) {
clearInterval(this._ffz_freeze_interval);
this._ffz_freeze_interval = null;
}
if ( this._ffz_freeze_messages ) {
var messages = this._ffz_freeze_messages;
this._ffz_freeze_messages = null;
if ( this._ffz_freeze_mouse_move ) {
var mm = this._ffz_freeze_mouse_move;
this._ffz_freeze_mouse_move = null;
messages.removeEventListener('mousemove', mm);
messages.removeEventListener('touchmove', mm);
}
if ( this._ffz_freeze_mouse_out ) {
messages.removeEventListener('mouseout', this._ffz_freeze_mouse_out);
this._ffz_freeze_mouse_out = null;
}
}
},
ffzFreezePulse: function() {
if ( this.ffz_frozen && ! this.ffzShouldBeFrozen() )
this.ffzUnfreeze();
},
ffzFreeze: function() {
this.set('ffz_frozen', true);
if ( this.get('stuckToBottom') ) {
if ( ! this._ffz_freeze_interval )
this._ffz_freeze_interval = setInterval(this.ffzFreezePulse.bind(this), 200);
this.ffzFreezeWarn();
}
},
ffzUnfreeze: function(from_stuck) {
this.set('ffz_frozen', false);
this._ffz_freeze_last_move = 0;
this.ffzFreezeUnwarn();
if ( this._ffz_freeze_interval ) {
clearInterval(this._ffz_freeze_interval);
this._ffz_freeze_interval = null;
}
if ( ! from_stuck && this.get('stuckToBottom') )
this._scrollToBottom();
},
ffzFreezeWarn: function() {
var el = this.get('element'),
warning = el && el.querySelector('.chat-interface .more-messages-indicator.ffz-freeze-indicator');
if ( ! el )
return;
if ( warning )
return warning.classList.remove('hidden');
warning = utils.createElement('div', 'more-messages-indicator ffz-freeze-indicator');
var hp = FFZ.get().settings.chat_hover_pause,
label = hp === 2 ? 'Ctrl Key' :
hp === 3 ? (constants.META_NAME + ' Key') :
hp === 4 ? 'Alt Key' :
hp === 5 ? 'Shift Key' :
hp === 6 ? 'Ctrl or Mouse' :
hp === 7 ? (constants.META_NAME + ' or Mouse') :
hp === 8 ? 'Alt or Mouse' :
hp === 9 ? 'Shift or Mouse' :
'Mouse Movement';
warning.innerHTML = '(Chat Paused Due to ' + label + ')';
var cont = el.querySelector('.chat-interface');
if ( ! cont )
return;
cont.insertBefore(warning, cont.childNodes[0])
},
ffzFreezeUnwarn: function() {
var el = this.get('element'),
warning = el && el.querySelector('.chat-interface .more-messages-indicator.ffz-freeze-indicator');
if ( warning )
warning.classList.add('hidden');
},
ffzShouldBeFrozen: function(since) {
if ( since === undefined )
since = Date.now() - this._ffz_freeze_last_move;
var hp = FFZ.get().settings.chat_hover_pause;
return (this._ffz_freeze_ctrl && (hp === 2 || hp === 6)) ||
(this._ffz_freeze_meta && (hp === 3 || hp === 7)) ||
(this._ffz_freeze_alt && (hp === 4 || hp === 8)) ||
(this._ffz_freeze_shift && (hp === 5 || hp === 9)) ||
(since < 750 && (hp === 1 || hp > 5));
},
ffzFreezeMouseOut: function(event) {
this._ffz_freeze_outside = true;
var e = this;
setTimeout(function() {
if ( e._ffz_freeze_outside ) {
if ( FFZ.get().settings.chat_mod_icon_visibility > 1 )
e.get('element').classList.toggle('show-mod-icons', false);
e.ffzUnfreeze();
}
}, 25);
},
ffzFreezeOnKey: function(event) {
this._ffz_freeze_alt = event.altKey;
this._ffz_freeze_ctrl = event.ctrlKey;
this._ffz_freeze_meta = event.metaKey;
this._ffz_freeze_shift = event.shiftKey;
var f = FFZ.get(),
cmi = f.settings.chat_mod_icon_visibility;
if ( ! this._ffz_freeze_outside && cmi > 1 )
this.get('element').classList.toggle('show-mod-icons',
cmi === 2 ? this._ffz_freeze_ctrl :
cmd === 3 ? this._ffz_freeze_meta :
cmi === 4 ? this._ffz_freeze_alt :
this._ffz_freeze_shift);
if ( this._ffz_outside || f.settings.chat_hover_pause < 2 )
return;
// Okay, so at this point we should change the state of the freeze?
var should_freeze = this.ffzShouldBeFrozen(),
freeze_change = this.ffz_frozen !== should_freeze;
if ( freeze_change )
if ( should_freeze )
this.ffzFreeze();
else
this.ffzUnfreeze();
},
ffzFreezeMouseMove: function(event) {
this._ffz_freeze_last_move = Date.now();
this._ffz_freeze_outside = false;
// If nothing of interest has happened, stop.
if ( event.altKey === this._ffz_freeze_alt &&
event.shiftKey === this._ffz_freeze_shift &&
event.ctrlKey === this._ffz_freeze_ctrl &&
event.metaKey === this._ffz_freeze_meta &&
event.screenX === this._ffz_freeze_last_screenx &&
event.screenY === this._ffz_freeze_last_screeny )
return;
// Grab state.
this._ffz_freeze_alt = event.altKey;
this._ffz_freeze_ctrl = event.ctrlKey;
this._ffz_freeze_meta = event.metaKey;
this._ffz_freeze_shift = event.shiftKey;
this._ffz_freeze_last_screenx = event.screenX;
this._ffz_freeze_last_screeny = event.screenY;
var cmi = FFZ.get().settings.chat_mod_icon_visibility;
if ( ! this._ffz_freeze_outside && cmi > 1 )
this.get('element').classList.toggle('show-mod-icons',
cmi === 2 ? this._ffz_freeze_ctrl :
cmd === 3 ? this._ffz_freeze_meta :
cmi === 4 ? this._ffz_freeze_alt :
this._ffz_freeze_shift);
var should_freeze = this.ffzShouldBeFrozen(),
freeze_change = this.ffz_frozen !== should_freeze;
if ( freeze_change )
if ( should_freeze )
this.ffzFreeze();
else
this.ffzUnfreeze();
},
_prepareStickyBottom: function() {
var e = this,
t = 10;
this._setStuckToBottom(true);
this._$chatMessagesScroller.on(this._scrollEvents, function(n) {
var a = e._$chatMessagesScroller;
a && a[0] && (!e.ffz_frozen && (n.which > 0 || 'mousedown' === n.type || 'mousewheel' === n.type)) && ! function() {
var n = a[0].scrollHeight = a[0].scrollTop - a[0].offsetHeight;
e._setStuckToBottom(n <= t);
}
});
},
ffzFixStickyBottom: function() {
this._$chatMessagesScroller.off(this._scrollEvents);
this._prepareStickyBottom();
},
_scrollToBottom: function() {
var e = this;
this._scrollToBottomRequested = true;
Ember.run.schedule("afterRender", function() {
if ( ! e.ffz_frozen && ! e.isDestroyed && ! e.isDestroying && e._scrollToBottomRequested ) {
var t = e._$chatMessagesScroller;
if ( t && t.length ) {
t[0].scrollTop = t[0].scrollHeight;
e._setStuckToBottom(true);
}
}
});
}
}
FFZ.prototype.modify_room_component = function(component) {
var f = this;
utils.ember_reopen_view(component, _.extend({
ffz_init: function() {
f._roomv = this;
this.ffzFixStickyBottom();
this.ffzAddKeyHook();
if ( f.settings.chat_hover_pause )
this.ffzEnableFreeze();
if ( f.settings.room_status )
this.ffzUpdateStatus();
// TODO: Fix bits visibility calculation
},
ffz_destroy: function() {
if ( f._roomv === this )
f._roomv = undefined;
this.ffzDisableFreeze();
this.ffzRemoveKeyHook();
},
ffzUpdateStatus: function() {
var room = this.get('room'),
el = this.get('element'),
container = el && el.querySelector('.chat-buttons-container');
if ( ! container )
return;
var btn = container.querySelector('button');
if ( f.has_bttv || ! f.settings.room_status ) {
jQuery(".ffz.room-state", container).remove();
if ( btn ) {
btn.classList.remove('ffz-waiting');
btn.classList.remove('ffz-banned');
}
return;
} else if ( btn ) {
btn.classList.toggle('ffz-waiting', room && room.get('slowWait') || 0);
btn.classList.toggle('ffz-banned', room && room.get('ffz_banned') || false);
}
var badge, id, info, vis_count = 0, label;
for(var i=0; i < STATUS_BADGES.length; i++) {
info = STATUS_BADGES[i];
id = 'ffz-stat-' + info[0];
badge = container.querySelector('#' + id);
visible = typeof info[1] === 'function' ? info[1].call(f, room) : room && room.get(info[1]);
if ( typeof visible === 'string' )
visible = visible === '1';
label = typeof info[3] === "function" ? info[3].call(f, room) : undefined;
if ( ! badge ) {
badge = utils.createElement('span', 'ffz room-state stat float-right', (label || info[0]).charAt(0).toUpperCase() + '<span>' + (label || info[0]).substr(1).toUpperCase() + '</span>');
badge.id = id;
jQuery(badge).tipsy({html: true, gravity: utils.tooltip_placement(constants.TOOLTIP_DISTANCE, 'se')});
container.appendChild(badge);
}
if ( label )
badge.innerHTML = (label || info[0]).charAt(0).toUpperCase() + '<span>' + (label || info[0]).substr(1).toUpperCase() + '</span>';
badge.title = typeof info[2] === "function" ? info[2].call(f, room) : info[2];
badge.classList.toggle('hidden', ! visible);
badge.classList.toggle('faded', info[4] !== undefined ? typeof info[4] === "function" ? info[4].call(f, room) : info[4] : false);
if ( visible )
vis_count++;
}
jQuery(".ffz.room-state", container).toggleClass('truncated', vis_count > 3);
}.observes('room')
}, FFZ.HoverPause));
}
/*FFZ.prototype.modify_room_view = function(view) {
var f = this;
utils.ember_reopen_view(view, {
ffz_init: function() {
@ -608,7 +995,11 @@ FFZ.prototype.modify_room_view = function(view) {
since = Date.now() - this._ffz_last_move;
var hp = f.settings.chat_hover_pause;
return (this.ffz_ctrl && (hp === 2 || hp === 6)) || (this.ffz_meta && (hp === 3 || hp === 7)) || (this.ffz_alt && (hp === 4 || hp === 8)) || (this.ffz_shift && (hp === 5 || hp === 9)) || (since < 750 && (hp === 1 || hp > 5));
return (this.ffz_ctrl && (hp === 2 || hp === 6)) ||
(this.ffz_meta && (hp === 3 || hp === 7)) ||
(this.ffz_alt && (hp === 4 || hp === 8)) ||
(this.ffz_shift && (hp === 5 || hp === 9)) ||
(since < 750 && (hp === 1 || hp > 5));
},
ffzMouseMove: function(event) {
@ -716,7 +1107,7 @@ FFZ.prototype.modify_room_view = function(view) {
warning.classList.add('hidden');
}
});
}
}*/
// --------------------
@ -1093,9 +1484,11 @@ FFZ.prototype._modify_room = function(room) {
if ( this._ffz_wait_timer )
clearTimeout(this._ffz_wait_timer);
this._ffz_wait_timer = setTimeout(this.ffzUpdateWait.bind(this), 1000);
! update && f._chati && Ember.propertyDidChange(f._chati, 'submitButtonText');
! update && f._roomv && f._roomv.ffzUpdateStatus();
} else if ( (wait > 0 && value < 1) || was_banned ) {
this.set('ffz_banned', false);
! update && f._chati && Ember.propertyDidChange(f._chati, 'submitButtonText');
! update && f._roomv && f._roomv.ffzUpdateStatus();
}
},
@ -1107,6 +1500,8 @@ FFZ.prototype._modify_room = function(room) {
return;
this.set('slowWait', --wait);
f._chati && Ember.propertyDidChange(f._chati, 'submitButtonText');
if ( wait > 0 )
this._ffz_wait_timer = setTimeout(this.ffzUpdateWait.bind(this), 1000);
else {

View file

@ -5,39 +5,22 @@ var FFZ = window.FrankerFaceZ,
utils = require('./utils'),
MODIFIERS = {
59847: '0 10px 10px 0',
70852: '0 5px 20px 0',
70854: '30px 0 0',
59847: {
margins: '0 10px 10px 0',
modifier: true
},
build_css = function(emote) {
var output = '';
if ( ! emote.margins && ! emote.css )
return output;
70852: {
modifier: true,
margins: '0 5px 20px 0',
extra_width: 5,
shrink_to_fit: true
},
if ( emote.modifier && emote.margins ) {
var margins = _.map(emote.margins.split(/\s+/), function(n) { return parseInt(n) });
if ( margins.length === 3 )
margins.push(margins[1]);
var l = margins.length,
m_left = margins[3 % l],
m_right = margins[1 % l],
m_top = margins[0 % l],
m_bottom = margins[2 % l];
output += '.modified-emoticon img[data-ffz-emote="' + emote.id + '"] {' +
'padding:' + m_top + 'px ' + m_right + 'px ' + m_bottom + 'px ' + m_left + 'px;' +
'max-width: calc(100% - ' + (200 - (2*m_left) - (2*m_right)) + 'px);' +
'margin: 0 !important' +
'}\n';
70854: {
modifier: true,
margins: '30px 0 0'
}
return output +
'img[data-ffz-emote="' + emote.id + '"] {' +
(emote.margins && ! emote.modifier ? 'margin:' + emote.margins + ' !important;' : '') +
(emote.css || '') +
'}\n';
};
@ -446,12 +429,10 @@ FFZ.prototype._load_set_json = function(set_id, callback, data) {
altText: emote.hidden ? '???' : emote.name
};
if ( MODIFIERS.hasOwnProperty(emote.id) ) {
emote.modifier = true;
emote.margins = MODIFIERS[emote.id];
}
if ( MODIFIERS.hasOwnProperty(emote.id) )
emote = _.extend(emote, MODIFIERS[emote.id]);
output_css += build_css(emote);
output_css += utils.emote_css(emote);
data.count++;
data.emoticons[emote.id] = emote;
}

View file

@ -1,12 +1,5 @@
var FFZ = window.FrankerFaceZ,
utils = require('../utils'),
build_css = function(emote) {
if ( ! emote.margins && ! emote.css )
return "";
return 'img[src="' + emote.urls[1] + '"]{' + (emote.margins ? 'margin:' + emote.margins + ';' : '') + (emote.css || "") + '}'
};
utils = require('../utils');
// ---------------------
@ -209,7 +202,10 @@ API.prototype._load_set = function(real_id, set_id, data) {
new_emote.css = emote.css;
new_emote.margins = emote.margins;
new_emote.modifier = emote.modifier;
new_emote.extra_width = emote.extra_width;
new_emote.shrink_to_fit = emote.shrink_to_fit;
new_emote.srcSet = emote.urls[1] + ' 1x';
new_emote.urls[1] = emote.urls[1];
@ -245,7 +241,7 @@ API.prototype._load_set = function(real_id, set_id, data) {
altText: new_emote.hidden ? '???' : new_emote.name
};
output_css += build_css(new_emote);
output_css += utils.emote_css(new_emote);
emote_set.count++;
emoticons[id] = new_emote;
}

View file

@ -61,7 +61,7 @@ FFZ.channel_metadata = {};
// Version
var VER = FFZ.version_info = {
major: 3, minor: 5, revision: 346,
major: 3, minor: 5, revision: 349,
toString: function() {
return [VER.major, VER.minor, VER.revision].join(".") + (VER.extra || "");
}

View file

@ -400,7 +400,7 @@ FFZ.prototype.render_tooltip = function(el) {
} else if ( this.classList.contains('emoticon') ) {
var preview_url, width=0, height=0, image, set_id, emote, emote_set,
emote_id = this.getAttribute('data-ffz-emote'),
modifiers = this.getAttribute('data-ffz-modifiers'),
modifiers = this.getAttribute('data-modifier-info'),
mod_text = '';
if ( modifiers ) {
@ -942,7 +942,7 @@ FFZ.prototype.render_token = function(render_links, warn_links, render_bits, tok
return '<span>' + f.render_token(render_links, warn_links, render_bits, t) + '</span>';
}).join('') + '</span>';
extra += ' data-ffz-modifiers="' + utils.quote_attr(JSON.stringify(_.map(token.modifiers, function(t) { return [t.ffzEmoteSet, t.ffzEmote] }))) + '"';
extra += ' data-ffz-modifiers="' + utils.quote_attr(_.map(token.modifiers, function(t) { return t.ffzEmote }).join(' ')) + '" data-modifier-info="' + utils.quote_attr(JSON.stringify(_.map(token.modifiers, function(t) { return [t.ffzEmoteSet, t.ffzEmote] }))) + '"';
}
return prefix + '<img class="emoticon ffz-tooltip' + (cls||'') + '"' + (extra||'') + ' src="' + utils.quote_attr(src) + '"' + (srcset ? ' srcset="' + utils.quote_attr(srcset) + '"' : '') + ' alt="' + utils.quote_attr(token.altText) + '">' + suffix;

View file

@ -972,6 +972,36 @@ module.exports = FFZ.utils = {
}
},
emote_css: function(emote) {
var output = '';
if ( ! emote.margins && ! emote.css )
return output;
if ( emote.modifier && emote.margins ) {
var margins = _.map(emote.margins.split(/\s+/), function(n) { return parseInt(n) });
if ( margins.length === 3 )
margins.push(margins[1]);
var l = margins.length,
m_left = margins[3 % l],
m_right = margins[1 % l],
m_top = margins[0 % l],
m_bottom = margins[2 % l];
output += '.modified-emoticon img[data-ffz-emote="' + emote.id + '"] {' +
'padding:' + m_top + 'px ' + m_right + 'px ' + m_bottom + 'px ' + m_left + 'px;' +
(emote.shrink_to_fit ? 'max-width: calc(100% - ' + (40 - m_left - m_right - (emote.extra_width || 0)) + 'px);' : '') +
'margin: 0 !important' +
'}\n';
}
return output +
'img[data-ffz-emote="' + emote.id + '"] {' +
(emote.margins && ! emote.modifier ? 'margin:' + emote.margins + ' !important;' : '') +
(emote.css || '') +
'}\n';
},
badge_css: function(badge, klass) {
klass = klass || ('ffz-badge-' + badge.id);
var out = ".badges ." + klass + (badge.no_color ? ":not(.colored)" : "") + " { background-color: " + badge.color + '; background-image: url("' + badge.image + '"); ' + (badge.css || "") + '}';

View file

@ -3758,7 +3758,7 @@ body:not(.ffz-channel-bar-bottom).ffz-small-player.ffz-minimal-channel-bar #play
.modified-emoticon span {
position: absolute;
top: -100px; bottom: -100px; left: -100px; right: -100px;
top: -20px; bottom: -20px; left: -20px; right: -20px;
margin: auto;
pointer-events: none;
}