Commit ca53a8d8 authored by JC Brand's avatar JC Brand

Add support for Emojis

parent fb691063
......@@ -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.
......
......@@ -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;
......
......@@ -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;
......
......@@ -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
----------------------
......
......@@ -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;
......
......@@ -91,6 +91,10 @@
}
}
.emojione {
height: $emoji_height;
}
.spinner {
@include animation(spin 2s infinite, linear);
display: block;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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();
......
......@@ -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'},
}
});
......@@ -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;
},
......
......@@ -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();
......
......@@ -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;
}
}
......
{[ 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) { ]}
......
{[ 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>
......
......@@ -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>
{[ } ]}
......
......@@ -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;
}));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment