Add support for Emojis

This commit is contained in:
JC Brand 2017-06-16 11:31:57 +02:00
parent fb69106352
commit ca53a8d8ef
18 changed files with 235 additions and 148 deletions

View File

@ -2,6 +2,7 @@
## 3.1.0 (Unreleased)
- Add support for Emojis (uses <a href="https://www.emojione.com/">Emojione</a>).
- New non-core plugin `converse-singleton` which ensures that no more than
one chat is visible at any given time. Used in the mobile build:
`converse-mobile.js` and makes the unread messages counter possible there.

View File

@ -1260,6 +1260,9 @@
-ms-transform: rotate(359deg);
-o-transform: rotate(359deg);
transform: rotate(359deg); } }
#converse-embedded-chat .emojione,
#conversejs .emojione {
height: 24px; }
#converse-embedded-chat .spinner,
#conversejs .spinner {
-webkit-animation: spin 2s infinite, linear;
@ -1739,7 +1742,6 @@
background: #fff;
bottom: 100%;
box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
display: none;
font-size: 12px;
margin: 0;
position: absolute;
@ -1762,6 +1764,16 @@
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul {
left: 0; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker {
z-index: 100; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker .picked,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker .picked {
background-color: #DCF9F6; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-picker,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-picker {
height: 250px;
overflow: scroll; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li {
font-size: 14px;
@ -1956,7 +1968,7 @@
#conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a:hover {
color: #206485; }
#conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom.unread-msgs .open-room {
max-width: 50%;
max-width: 55%;
width: auto;
font-weight: bold; }
#conversejs #controlbox #chatrooms .rooms-list-container dl.rooms-list dd.available-chatroom a.room-info:before {
@ -2286,16 +2298,22 @@
#conversejs #converse-roster .roster-contacts dd .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .msgs-indicator {
background-color: #E7A151;
opacity: 1;
border-radius: 10%;
padding: 0 0.2em;
font-size: 12px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: 60px; }
#conversejs #converse-roster .roster-contacts dd .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts dd .open-chat .avatar {
float: left;
display: inline-block;

View File

@ -1260,6 +1260,9 @@
-ms-transform: rotate(359deg);
-o-transform: rotate(359deg);
transform: rotate(359deg); } }
#converse-embedded-chat .emojione,
#conversejs .emojione {
height: 24px; }
#converse-embedded-chat .spinner,
#conversejs .spinner {
-webkit-animation: spin 2s infinite, linear;
@ -1785,7 +1788,6 @@ body {
background: #fff;
bottom: 100%;
box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
display: none;
font-size: 12px;
margin: 0;
position: absolute;
@ -1808,6 +1810,16 @@ body {
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul {
left: 0; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker {
z-index: 100; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker .picked,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-category-picker .picked {
background-color: #DCF9F6; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-picker,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul.emoji-picker {
height: 250px;
overflow: scroll; }
#converse-embedded-chat .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li,
#conversejs .chatbox form.sendXMPPMessage .chat-toolbar .toggle-smiley ul li {
font-size: 16px;

View File

@ -993,6 +993,14 @@ Notification will be shown in the following cases:
Requires the `src/converse-notification.js` plugin.
show_emojione
-------------
* Default: ``false``
Determines whether `Emojione <https://www.emojione.com/>`_ should be used to
render emojis. The default is not to do this, but to simply let the operating
system or browser render emoji (if it has support for them).
show_only_online_users
----------------------

View File

@ -40,6 +40,7 @@
"bootstrap": "^3.3.7",
"bourbon": "^4.3.2",
"clean-css-cli": "^4.0.10",
"emojione": "^3.0.3",
"es6-promise": "^4.1.0",
"eslint": "^3.19.0",
"eslint-plugin-lodash": "^2.3.3",

View File

@ -309,7 +309,6 @@
background: #fff;
bottom: 100%;
box-shadow: -1px -1px 2px 0 rgba(0, 0, 0, 0.4);
display: none;
font-size: 12px;
margin: 0;
position: absolute;
@ -330,6 +329,16 @@
color: $link-color;
padding-left: 5px;
ul {
&.emoji-category-picker {
z-index: 100;
.picked {
background-color: $highlight-color;
}
}
&.emoji-picker {
height: 250px;
overflow: scroll;
}
left: 0;
li {
font-size: $font-size;

View File

@ -91,6 +91,10 @@
}
}
.emojione {
height: $emoji_height;
}
.spinner {
@include animation(spin 2s infinite, linear);
display: block;

View File

@ -47,8 +47,11 @@ $save-button-color: $green !default;
$chat-textarea-height: 70px !default;
$send-button-height: 27px !default;
$send-button-margin: 3px !default;
$message-them-color: $green !default;
$emoji_height : 24px !default;
$roster-height: 194px !default;
$roster-item-height: 60px !default;

View File

@ -47,8 +47,11 @@ $save-button-color: $green !default;
$chat-textarea-height: 70px !default;
$send-button-height: 27px !default;
$send-button-margin: 3px !default;
$message-them-color: $green !default;
$emoji_height : 24px !default;
$roster-height: 194px !default;
$roster-item-height: 30px !default;

View File

@ -351,42 +351,22 @@
expect($toolbar.children('li.toggle-smiley').length).toBe(1);
// Register spies
spyOn(view, 'toggleEmoticonMenu').and.callThrough();
spyOn(view, 'insertEmoticon').and.callThrough();
spyOn(view, 'insertEmoticon');
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
$toolbar.children('li.toggle-smiley').click();
expect(view.toggleEmoticonMenu).toHaveBeenCalled();
var $menu = view.$el.find('.toggle-smiley ul');
var $items = $menu.children('li');
expect($menu.is(':visible')).toBeTruthy();
expect($items.length).toBe(13);
expect($($items[0]).children('a').data('emoticon')).toBe(':)');
expect($($items[1]).children('a').data('emoticon')).toBe(';)');
expect($($items[2]).children('a').data('emoticon')).toBe(':D');
expect($($items[3]).children('a').data('emoticon')).toBe(':P');
expect($($items[4]).children('a').data('emoticon')).toBe('8)');
expect($($items[5]).children('a').data('emoticon')).toBe('>:)');
expect($($items[6]).children('a').data('emoticon')).toBe(':S');
expect($($items[7]).children('a').data('emoticon')).toBe(':\\');
expect($($items[8]).children('a').data('emoticon')).toBe('>:(');
expect($($items[9]).children('a').data('emoticon')).toBe(':(');
expect($($items[10]).children('a').data('emoticon')).toBe(':O');
expect($($items[11]).children('a').data('emoticon')).toBe('(^.^)b');
expect($($items[12]).children('a').data('emoticon')).toBe('<3');
var $picker = view.$el.find('.toggle-smiley .emoji-picker-container');
// expect($picker.is(':visible')).toBeTruthy();
// expect(view.toggleEmoticonMenu).toHaveBeenCalled();
var $items = $picker.find('.emoji-picker li');
$items.first().click();
expect(view.insertEmoticon).toHaveBeenCalled();
expect($textarea.val()).toBe(':) ');
expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeFalsy();
$toolbar.children('li.toggle-smiley').click();
expect(view.toggleEmoticonMenu).toHaveBeenCalled();
expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeTruthy();
view.$el.find('.toggle-smiley ul').children('li').last().click();
expect(view.insertEmoticon).toHaveBeenCalled();
expect(view.$el.find('.toggle-smiley ul').is(':visible')).toBeFalsy();
expect($textarea.val()).toBe(':) <3 ');
done();
});
}));
@ -411,11 +391,7 @@
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
$toolbar.children('li.toggle-otr').click();
expect(view.toggleOTRMenu).toHaveBeenCalled();
var $menu = view.$el.find('.toggle-otr ul');
expect($menu.is(':visible')).toBeTruthy();
expect($menu.children('li').length).toBe(2);
done();
});
}));
@ -1139,34 +1115,6 @@
expect(msg.html()).toEqual('&lt;p&gt;This message contains &lt;em&gt;some&lt;/em&gt; &lt;b&gt;markup&lt;/b&gt;&lt;/p&gt;');
}));
it("should display emoticons correctly", mock.initConverse(function (_converse) {
test_utils.createContacts(_converse, 'current');
test_utils.openControlBox();
test_utils.openContactsPanel(_converse);
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
var view = _converse.chatboxviews.get(contact_jid);
var messages = [':)', ';)', ':D', ':P', '8)', '>:)', ':S', ':\\', '>:(', ':(', ':O', '(^.^)b', '<3'];
var emoticons = [
'<span class="emoticon icon-smiley"></span>', '<span class="emoticon icon-wink"></span>',
'<span class="emoticon icon-grin"></span>', '<span class="emoticon icon-tongue"></span>',
'<span class="emoticon icon-cool"></span>', '<span class="emoticon icon-evil"></span>',
'<span class="emoticon icon-confused"></span>', '<span class="emoticon icon-wondering"></span>',
'<span class="emoticon icon-angry"></span>', '<span class="emoticon icon-sad"></span>',
'<span class="emoticon icon-shocked"></span>', '<span class="emoticon icon-thumbs-up"></span>',
'<span class="emoticon icon-heart"></span>'
];
spyOn(view, 'sendMessage').and.callThrough();
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-msg-content');
expect(msg.html()).toEqual(emoticons[i]);
}
}));
it("can contain hyperlinks, which will be clickable", mock.initConverse(function (_converse) {
test_utils.createContacts(_converse, 'current');
test_utils.openControlBox();

View File

@ -21,6 +21,7 @@ require.config({
"backbone.noconflict": "src/backbone.noconflict",
"backbone.browserStorage": "node_modules/backbone.browserStorage/backbone.browserStorage",
"backbone.overview": "node_modules/backbone.overview/backbone.overview",
"emojione": "node_modules/emojione/lib/js/emojione",
"eventemitter": "node_modules/otr/build/dep/eventemitter",
"es6-promise": "node_modules/es6-promise/dist/es6-promise",
"jquery": "node_modules/jquery/dist/jquery",
@ -136,6 +137,7 @@ require.config({
// define module dependencies for modules not using define
shim: {
'awesomplete': { exports: 'Awesomplete' }
'awesomplete': { exports: 'Awesomplete'},
'emojione': { exports: 'emojione'},
}
});

View File

@ -9,9 +9,11 @@
(function (root, factory) {
define([
"converse-core",
"emojione",
"tpl!chatbox",
"tpl!new_day",
"tpl!action",
"tpl!emojis",
"tpl!message",
"tpl!help_message",
"tpl!toolbar",
@ -20,9 +22,11 @@
], factory);
}(this, function (
converse,
emojione,
tpl_chatbox,
tpl_new_day,
tpl_action,
tpl_emojis,
tpl_message,
tpl_help_message,
tpl_toolbar,
@ -43,7 +47,6 @@
FORWARD_SLASH: 47
};
converse.plugins.add('converse-chatview', {
overrides: {
@ -52,6 +55,18 @@
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
//
registerGlobalEventHandlers: function () {
this.__super__.registerGlobalEventHandlers();
document.addEventListener('click', function () {
if ($('.toggle-smiley ul').is(':visible')) {
_.each(
document.querySelectorAll('.toggle-smiley .emoji-picker-container'),
utils.hideElement
);
}
});
},
ChatBoxViews: {
onChatBoxAdded: function (item) {
@ -68,7 +83,6 @@
}
},
initialize: function () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
@ -78,7 +92,9 @@
this.updateSettings({
chatview_avatar_height: 32,
chatview_avatar_width: 32,
chatview_avatartrue: 32,
show_emojione: false, // By default, use native emojis.
emojione_path: 'https://cdn.jsdelivr.net/emojione/assets/' + emojione.emojiVersion + '/png/' + emojione.emojiSize + '/',
show_toolbar: true,
time_format: 'HH:mm',
visible_toolbar_buttons: {
@ -88,15 +104,65 @@
},
});
if (_converse.show_emojione) {
// If using Emojione, we also convert ascii smileys into emoji.
emojione.ascii = true;
emojione.imagePathPNG = _converse.emojione_path
}
var onWindowStateChanged = function (data) {
var state = data.state;
_converse.chatboxviews.each(function (chatboxview) {
chatboxview.onWindowStateChanged(state);
})
};
_converse.api.listen.on('windowStateChanged', onWindowStateChanged);
_converse.EmojiPicker = Backbone.Model.extend({
defaults: {
'current_category': 'people'
}
});
_converse.EmojiPickerView = Backbone.View.extend({
className: 'emoji-picker-container hidden',
events: {
'click .emoji-category-picker li a': 'chooseCategory',
},
initialize: function () {
this.model.on('change', this.render, this);
},
render: function () {
var emojis_by_category = utils.marshallEmojis(emojione);
var converter;
if (_converse.show_emojione) {
converter = emojione.toImage
} else {
converter = emojione.shortnameToUnicode
}
var emojis_html = tpl_emojis(
_.extend(
this.model.toJSON(), {
'emojis_by_category': emojis_by_category,
'emojione': emojione,
'converter': converter
}
));
this.el.innerHTML = emojis_html;
return this;
},
chooseCategory: function (ev) {
ev.preventDefault();
ev.stopPropagation();
var category = ev.target.parentElement.getAttribute("data-category").trim();
this.model.set({'current_category': category});
}
});
_converse.ChatBoxView = Backbone.View.extend({
length: 200,
tagName: 'div',
@ -108,13 +174,15 @@
'keypress .chat-textarea': 'keyPressed',
'click .send-button': 'onSendButtonClicked',
'click .toggle-smiley': 'toggleEmoticonMenu',
'click .toggle-smiley ul li': 'insertEmoticon',
'click .toggle-smiley ul.emoji-picker li': 'insertEmoticon',
'click .toggle-clear': 'clearMessages',
'click .toggle-call': 'toggleCall',
'click .new-msgs-indicator': 'viewUnreadMessages'
},
initialize: function () {
this.emoji_picker_view = new _converse.EmojiPickerView({model: new _converse.EmojiPicker() });
this.model.messages.on('add', this.onMessageAdded, this);
this.model.on('show', this.show, this);
this.model.on('destroy', this.hide, this);
@ -374,7 +442,10 @@
$msg.find('.chat-msg-content').first()
.text(text)
.addHyperlinks()
.addEmoticons(_converse.visible_toolbar_buttons.emoticons);
.addEmoticons(
_converse,
emojione,
_converse.visible_toolbar_buttons.emoticons);
return $msg;
},
@ -642,15 +713,24 @@
insertEmoticon: function (ev) {
ev.stopPropagation();
this.$el.find('.toggle-smiley ul').slideToggle(200);
var $target = $(ev.target);
$target = $target.is('a') ? $target : $target.children('a');
this.insertIntoTextArea($target.data('emoticon'));
this.toggleEmoticonMenu();
var target;
if (ev.target.nodeName === 'IMG') {
target = ev.target.parentElement;
} else {
target = ev.target;
}
this.insertIntoTextArea(
emojione.shortnameToUnicode(
target.getAttribute('data-emoticon')
));
},
toggleEmoticonMenu: function (ev) {
ev.stopPropagation();
this.$el.find('.toggle-smiley ul').slideToggle(200);
if (!_.isUndefined(ev)) {
ev.stopPropagation();
}
utils.toggleElement(this.emoji_picker_view.el);
},
toggleCall: function (ev) {
@ -726,11 +806,15 @@
renderToolbar: function (toolbar, options) {
if (!_converse.show_toolbar) { return; }
toolbar = toolbar || tpl_toolbar;
options = _.extend(
options = _.assign(
this.model.toJSON(),
this.getToolbarOptions(options || {})
);
this.$el.find('.chat-toolbar').html(toolbar(options));
this.el.querySelector('.chat-toolbar').innerHTML = toolbar(options);
var toggle = this.el.querySelector('.toggle-smiley');
toggle.innerHTML = '';
toggle.appendChild(this.emoji_picker_view.render().el);
return this;
},

View File

@ -449,6 +449,8 @@
this.model.on('change:description', this.renderHeading, this);
this.model.on('change:name', this.renderHeading, this);
this.emoji_picker_view = new _converse.EmojiPickerView({model: new _converse.EmojiPicker() });
this.createOccupantsView();
this.render().insertIntoDOM();
this.registerHandlers();

View File

@ -58,12 +58,9 @@
registerGlobalEventHandlers: function () {
this.__super__.registerGlobalEventHandlers();
$(document).click(function () {
document.addEventListener('click', function () {
if ($('.toggle-otr ul').is(':visible')) {
$('.toggle-otr ul', this).slideUp();
}
if ($('.toggle-smiley ul').is(':visible')) {
$('.toggle-smiley ul', this).slideUp();
_.each($('.toggle-otr ul', this), utils.hideElement);
}
});
},
@ -400,7 +397,7 @@
toggleOTRMenu: function (ev) {
ev.stopPropagation();
this.$el.find('.toggle-otr ul').slideToggle(200);
utils.toggleElement(this.el.querySelector('.toggle-otr ul'));
},
getOTRTooltip: function () {
@ -444,9 +441,9 @@
});
this.__super__.renderToolbar.apply(this, arguments);
this.$el.find('.chat-toolbar').append(
tpl_toolbar_otr(
_.extend(this.model.toJSON(), options || {})
));
tpl_toolbar_otr(
_.extend(this.model.toJSON(), options || {})
));
return this;
}
}

View File

@ -1,20 +1,6 @@
{[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
<li><a class="icon-grin" href="#" data-emoticon=":D"></a></li>
<li><a class="icon-tongue" href="#" data-emoticon=":P"></a></li>
<li><a class="icon-cool" href="#" data-emoticon="8)"></a></li>
<li><a class="icon-evil" href="#" data-emoticon=">:)"></a></li>
<li><a class="icon-confused" href="#" data-emoticon=":S"></a></li>
<li><a class="icon-wondering" href="#" data-emoticon=":\"></a></li>
<li><a class="icon-angry" href="#" data-emoticon=">:("></a></li>
<li><a class="icon-sad" href="#" data-emoticon=":("></a></li>
<li><a class="icon-shocked" href="#" data-emoticon=":O"></a></li>
<li><a class="icon-thumbs-up" href="#" data-emoticon="(^.^)b"></a></li>
<li><a class="icon-heart" href="#" data-emoticon="<3"></a></li>
</ul>
<ul class="emoji-picker"></ul>
</li>
{[ } ]}
{[ if (show_call_button) { ]}

View File

@ -1,21 +1,5 @@
{[ if (show_emoticons) { ]}
<li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
<li><a class="icon-grin" href="#" data-emoticon=":D"></a></li>
<li><a class="icon-tongue" href="#" data-emoticon=":P"></a></li>
<li><a class="icon-cool" href="#" data-emoticon="8)"></a></li>
<li><a class="icon-evil" href="#" data-emoticon=">:)"></a></li>
<li><a class="icon-confused" href="#" data-emoticon=":S"></a></li>
<li><a class="icon-wondering" href="#" data-emoticon=":\"></a></li>
<li><a class="icon-angry" href="#" data-emoticon=">:("></a></li>
<li><a class="icon-sad" href="#" data-emoticon=":("></a></li>
<li><a class="icon-shocked" href="#" data-emoticon=":O"></a></li>
<li><a class="icon-thumbs-up" href="#" data-emoticon="(^.^)b"></a></li>
<li><a class="icon-heart" href="#" data-emoticon="<3"></a></li>
</ul>
</li>
<li class="toggle-smiley icon-happy" title="{{{label_insert_smiley}}}"></li>
{[ } ]}
{[ if (show_call_button) { ]}
<li class="toggle-call"><a class="icon-phone" title="{{{label_start_call}}}"></a></li>

View File

@ -13,7 +13,7 @@
{[ if (otr_status == FINISHED) { ]}
<span class="icon-unlocked"></span>
{[ } ]}
<ul>
<ul class="hidden">
{[ if (otr_status == UNENCRYPTED) { ]}
<li><a class="start-otr" href="#">{{{label_start_encrypted_conversation}}}</a></li>
{[ } ]}

View File

@ -115,7 +115,10 @@
_.forEach(list, function (url) {
isImage(unescapeHTML(url)).then(function (img) {
img.className = 'chat-image';
throttledHTML(obj.querySelector('a'), img.outerHTML);
var a = obj.querySelector('a');
if (!_.isNull(a)) {
throttledHTML(a, img.outerHTML);
}
});
});
});
@ -123,36 +126,12 @@
return this;
};
$.fn.addEmoticons = function (allowed) {
$.fn.addEmoticons = function (_converse, emojione, allowed) {
if (allowed) {
if (this.length > 0) {
this.each(function (i, obj) {
var text = $(obj).html();
text = text.replace(/&gt;:\)/g, '<span class="emoticon icon-evil"></span>');
text = text.replace(/:\)/g, '<span class="emoticon icon-smiley"></span>');
text = text.replace(/:\-\)/g, '<span class="emoticon icon-smiley"></span>');
text = text.replace(/;\)/g, '<span class="emoticon icon-wink"></span>');
text = text.replace(/;\-\)/g, '<span class="emoticon icon-wink"></span>');
text = text.replace(/:D/g, '<span class="emoticon icon-grin"></span>');
text = text.replace(/:\-D/g, '<span class="emoticon icon-grin"></span>');
text = text.replace(/:P/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:\-P/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:p/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:\-p/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/8\)/g, '<span class="emoticon icon-cool"></span>');
text = text.replace(/:S/g, '<span class="emoticon icon-confused"></span>');
text = text.replace(/:\\/g, '<span class="emoticon icon-wondering"></span>');
text = text.replace(/:\/ /g, '<span class="emoticon icon-wondering"></span>');
text = text.replace(/&gt;:\(/g, '<span class="emoticon icon-angry"></span>');
text = text.replace(/:\(/g, '<span class="emoticon icon-sad"></span>');
text = text.replace(/:\-\(/g, '<span class="emoticon icon-sad"></span>');
text = text.replace(/:O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/:\-O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/\=\-O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/\(\^.\^\)b/g, '<span class="emoticon icon-thumbs-up"></span>');
text = text.replace(/&lt;3/g, '<span class="emoticon icon-heart"></span>');
$(obj).html(text);
});
if (_converse.show_emojione) {
this.html(emojione.toImage(this.text()));
} else {
this.html(emojione.shortnameToUnicode(this.text()));
}
}
return this;
@ -203,6 +182,19 @@
}
},
hideElement: function (el) {
el.classList.add('hidden');
},
toggleElement: function (el) {
if (_.includes(el.classList, 'hidden')) {
// XXX: use fadeIn?
el.classList.remove('hidden');
} else {
this.hideElement (el);
}
},
fadeIn: function (el, callback) {
if ($.fx.off) {
el.classList.remove('hidden');
@ -522,5 +514,38 @@
frag = tmp = null;
}
utils.marshallEmojis = function (emojione) {
/* Return a dict of emojis with the categories as keys and
* lists of emojis in that category as values.
*/
if (_.isUndefined(this.emojis_by_category)) {
var emojis = _.values(_.mapValues(emojione.emojioneList, function (value, key, o) {
value._shortname = key;
return value
}));
var tones = [':tone1:', ':tone2:', ':tone3:', ':tone4:', ':tone5:'];
var categories = _.uniq(_.map(emojis, _.partial(_.get, _, 'category')));
var emojis_by_category = {};
_.forEach(categories, function (cat) {
var list = _.sortBy(_.filter(emojis, ['category', cat]), ['uc_base']);
list = _.filter(list, function (item) {
return !_.includes(tones, item._shortname);
});
if (cat === 'people') {
var idx = _.findIndex(list, ['uc_base', '1f600']);
list = _.union(_.slice(list, idx), _.slice(list, 0, idx+1));
} else if (cat === 'activity') {
list = _.union(_.slice(list, 27-1), _.slice(list, 0, 27));
} else if (cat === 'objects') {
list = _.union(_.slice(list, 24-1), _.slice(list, 0, 24));
} else if (cat === 'travel') {
list = _.union(_.slice(list, 17-1), _.slice(list, 0, 17));
}
emojis_by_category[cat] = list;
});
this.emojis_by_category = emojis_by_category;
}
return this.emojis_by_category;
}
return utils;
}));