Various emoji improvements:
* Add emoji tooltip * Make categories configurable and add smileys category * Rearrange emoji categories and style & Show all emojis together
This commit is contained in:
parent
4cb9fd88a8
commit
d7ce231c51
@ -37,6 +37,7 @@
|
||||
"lodash/prefer-noop": "off",
|
||||
"lodash/prefer-startswith": "off",
|
||||
"lodash/preferred-alias": "off",
|
||||
"lodash/matches-prop-shorthand": "off",
|
||||
"accessor-pairs": "error",
|
||||
"array-bracket-spacing": "off",
|
||||
"array-callback-return": "error",
|
||||
|
5308
dist/emojis.json
vendored
5308
dist/emojis.json
vendored
File diff suppressed because it is too large
Load Diff
@ -112,15 +112,6 @@
|
||||
text-align: left;
|
||||
margin: 0 var(--chat-gutter);
|
||||
|
||||
img {
|
||||
&.emoji {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
margin: 0 .05em 0 .1em;
|
||||
vertical-align: -0.1em;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-height: $mobile-landscape-height) {
|
||||
margin: 0;
|
||||
width: var(--mobile-chat-width);
|
||||
@ -364,64 +355,6 @@
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
.emoji-picker-container {
|
||||
background: white;
|
||||
}
|
||||
ul {
|
||||
&.emoji-picker {
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
padding: 0.5em;
|
||||
}
|
||||
li {
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
position: relative;
|
||||
&.insert-emoji {
|
||||
padding: 0.2em;
|
||||
&.picked {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
a {
|
||||
font-size: var(--font-size-huge);
|
||||
&:hover {
|
||||
color: #8f2831;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.toggle-smiley {
|
||||
a.toggle-smiley {
|
||||
padding: 0;
|
||||
}
|
||||
.emoji-toolbar {
|
||||
box-shadow: 0 -1px 1px 0 rgba(0, 0, 0, 0.4);
|
||||
.emoji-category-picker {
|
||||
padding-top: 0.5em;
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-category-picker,
|
||||
.emoji-skintone-picker {
|
||||
li {
|
||||
padding: 0.25em;
|
||||
font-size: var(--font-size-huge);
|
||||
&:hover {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.toggle-otr {
|
||||
ul {
|
||||
@ -492,11 +425,6 @@
|
||||
@include make-col(4);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-picker {
|
||||
height: var(--embedded-emoji-picker-height);
|
||||
}
|
||||
|
||||
.chatbox {
|
||||
min-width: var(--overlayed-chat-width) !important;
|
||||
width: var(--overlayed-chat-width);
|
||||
@ -526,33 +454,12 @@
|
||||
.chat-textarea {
|
||||
max-height: var(--overlayed-max-chat-textarea-height);
|
||||
}
|
||||
.emoji-picker {
|
||||
height: var(--overlayed-emoji-picker-height);
|
||||
}
|
||||
.chatbox {
|
||||
.sendXMPPMessage {
|
||||
.chat-toolbar {
|
||||
li {
|
||||
.toolbar-menu {
|
||||
min-width: 235px;
|
||||
ul {
|
||||
&.emoji-toolbar {
|
||||
width: 100%;
|
||||
.emoji-category {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.toggle-smiley {
|
||||
.emoji-toolbar {
|
||||
.emoji-category-picker {
|
||||
ul {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -561,15 +468,14 @@
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
#conversejs.converse-overlayed {
|
||||
> .row {
|
||||
flex-direction: column;
|
||||
|
||||
#conversejs.converse-overlayed {
|
||||
> .row {
|
||||
flex-direction: column;
|
||||
&.no-gutters {
|
||||
margin: -1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -684,9 +590,6 @@
|
||||
.chat-textarea {
|
||||
max-height: var(--fullpage-max-chat-textarea-height);
|
||||
}
|
||||
.emoji-picker {
|
||||
height: var(--fullpage-emoji-picker-height);
|
||||
}
|
||||
.chatbox {
|
||||
.box-flyout {
|
||||
background-color: var(--chat-head-color);
|
||||
@ -714,19 +617,6 @@
|
||||
ul {
|
||||
width: 100%;
|
||||
}
|
||||
.toggle-smiley {
|
||||
ul {
|
||||
&.emoji-toolbar {
|
||||
.emoji-category-picker {
|
||||
margin-right: 5em;
|
||||
}
|
||||
.emoji-category {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -324,7 +324,6 @@
|
||||
}
|
||||
|
||||
.sendXMPPMessage {
|
||||
|
||||
.suggestion-box__results--above {
|
||||
bottom: 4.5em;
|
||||
}
|
||||
@ -338,6 +337,7 @@
|
||||
color: var(--message-input-color);
|
||||
}
|
||||
}
|
||||
|
||||
.chat-textarea {
|
||||
&:active, &:focus{
|
||||
outline-color: var(--chatroom-head-color);
|
||||
|
169
sass/_emoji.scss
Normal file
169
sass/_emoji.scss
Normal file
@ -0,0 +1,169 @@
|
||||
#conversejs {
|
||||
.chatbox {
|
||||
img.emoji {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
margin: 0 .05em 0 .1em;
|
||||
vertical-align: -0.1em;
|
||||
}
|
||||
|
||||
.toggle-smiley {
|
||||
a.toggle-smiley {
|
||||
padding: 0;
|
||||
}
|
||||
.emoji-picker.toolbar-menu {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
background-color: var(--chat-head-color);
|
||||
.emoji-picker__container {
|
||||
overflow-y: hidden;
|
||||
background: white;
|
||||
.emoji-picker__lists {
|
||||
overflow-y: auto;
|
||||
.emoji-category__heading {
|
||||
color: var(--subdued-color);
|
||||
font-size: var(--font-size);
|
||||
padding: 0.5em 0 0 0.5em;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.emoji-skintone-picker {
|
||||
padding: 0.25em 0;
|
||||
background-color: var(--chat-head-color);
|
||||
width: auto;
|
||||
font-size: var(--font-size-huge);
|
||||
&:hover {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.emoji-picker {
|
||||
background-color: white;
|
||||
padding: 0.5em;
|
||||
li {
|
||||
margin-left: 0;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
position: relative;
|
||||
&.insert-emoji {
|
||||
padding: 0.2em;
|
||||
&.picked {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
&:hover {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
a {
|
||||
font-size: var(--font-size-huge);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.emoji-category-picker {
|
||||
padding-top: 0.5em;
|
||||
background-color: var(--chat-head-color);
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
.emoji-category {
|
||||
&.picked {
|
||||
background-color: white;
|
||||
border: 1px var(--chat-head-color) solid;
|
||||
border-bottom: none;
|
||||
}
|
||||
padding: 0.25em;
|
||||
font-size: var(--font-size-huge);
|
||||
&:hover {
|
||||
background-color: var(--highlight-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chatroom {
|
||||
.toggle-smiley {
|
||||
.emoji-picker.toolbar-menu {
|
||||
background-color: var(--chatroom-head-color);
|
||||
.emoji-picker__container {
|
||||
background: white;
|
||||
.emoji-skintone-picker {
|
||||
background-color: var(--chatroom-head-color);
|
||||
}
|
||||
.emoji-category-picker {
|
||||
background-color: var(--chatroom-head-color);
|
||||
.emoji-category {
|
||||
&.picked {
|
||||
border: 1px var(--chatroom-head-color) solid;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#conversejs.converse-embedded,
|
||||
#conversejs.converse-overlayed {
|
||||
.emoji-picker__container {
|
||||
height: var(--embedded-emoji-picker-height);
|
||||
.emoji-picker__lists {
|
||||
height: calc(var(--embedded-emoji-picker-height) - 4em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#conversejs.converse-overlayed {
|
||||
.emoji-picker__container {
|
||||
height: var(--overlayed-emoji-picker-height);
|
||||
}
|
||||
.chatbox {
|
||||
.toggle-smiley {
|
||||
.emoji-picker.toolbar-menu {
|
||||
.emoji-picker__container {
|
||||
.emoji-picker {
|
||||
.insert-emoji {
|
||||
a {
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
}
|
||||
}
|
||||
.emoji-skintone-picker {
|
||||
font-size: var(--font-size-small);
|
||||
}
|
||||
.emoji-category-picker {
|
||||
.emoji-category {
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#conversejs.converse-fullscreen {
|
||||
.emoji-picker__container {
|
||||
height: var(--fullpage-emoji-picker-height);
|
||||
.emoji-picker__lists {
|
||||
height: calc(var(--fullpage-emoji-picker-height) - 4em;
|
||||
}
|
||||
}
|
||||
.chatbox {
|
||||
.sendXMPPMessage {
|
||||
.toggle-smiley {
|
||||
.emoji-category {
|
||||
padding-left: 0.2em;
|
||||
padding-right: 0.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -43,8 +43,8 @@ $mobile_portrait_length: 480px !default;
|
||||
--send-button-height: 27px;
|
||||
--send-button-margin: 3px;
|
||||
|
||||
--controlbox-heading-top-margin: 0.75em;
|
||||
--inline-action-margin: 0.75em;
|
||||
--controlbox-heading-top-margin: 0.75em;
|
||||
--inline-action-margin: 0.75em;
|
||||
|
||||
--roster-height: 194px;
|
||||
|
||||
|
@ -63,3 +63,4 @@
|
||||
@import "minimized_chats";
|
||||
@import "bookmarks";
|
||||
@import "autocomplete";
|
||||
@import "emoji";
|
||||
|
@ -927,7 +927,7 @@
|
||||
|
||||
it("will display larger if it's a single emoji",
|
||||
mock.initConverse(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {},
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched', 'emojisInitialized'], {'use_system_emojis': false},
|
||||
async function (done, _converse) {
|
||||
|
||||
await test_utils.waitForRoster(_converse, 'current');
|
||||
|
@ -119,8 +119,9 @@ converse.plugins.add('converse-chatview', {
|
||||
Object.assign(
|
||||
this.model.toJSON(), {
|
||||
'_': _,
|
||||
'emoji_categories': _converse.emojis.categories,
|
||||
'emojis_by_category': _converse.emojis.by_category,
|
||||
'_converse': _converse,
|
||||
'emoji_categories': _converse.emoji_categories,
|
||||
'emojis_by_category': u.getEmojisByCategory(),
|
||||
'shouldBeHidden': this.shouldBeHidden,
|
||||
'skintones': ['tone1', 'tone2', 'tone3', 'tone4', 'tone5'],
|
||||
'toned_emojis': _converse.emojis.toned,
|
||||
@ -162,10 +163,7 @@ converse.plugins.add('converse-chatview', {
|
||||
},
|
||||
|
||||
chooseCategory (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const target = ev.target.nodeName === 'IMG' ?
|
||||
ev.target.parentElement : ev.target;
|
||||
const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
|
||||
const category = target.getAttribute("data-category").trim();
|
||||
this.model.save({
|
||||
'current_category': category,
|
||||
|
@ -9,9 +9,9 @@
|
||||
import * as twemoji from "twemoji";
|
||||
import _ from "./lodash.noconflict";
|
||||
import converse from "./converse-core";
|
||||
import u from "./utils/core";
|
||||
|
||||
const { Strophe } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
|
||||
const ASCII_LIST = {
|
||||
'*\\0/*':'1f646',
|
||||
@ -137,9 +137,8 @@ const ASCII_REPLACE_REGEX = new RegExp("<object[^>]*>.*?<\/object>|<span[^>]*>.*
|
||||
|
||||
|
||||
function convert (unicode) {
|
||||
/* For converting unicode code points and code pairs
|
||||
* to their respective characters
|
||||
*/
|
||||
// Converts unicode code points and code pairs
|
||||
// to their respective characters
|
||||
if (unicode.indexOf("-") > -1) {
|
||||
const parts = [],
|
||||
s = unicode.split('-');
|
||||
@ -171,15 +170,45 @@ converse.plugins.add('converse-emoji', {
|
||||
|
||||
_converse.api.settings.update({
|
||||
'emoji_image_path': twemoji.default.base,
|
||||
'emoji_json_path': '/dist/emojis.json'
|
||||
'emoji_json_path': '/dist/emojis.json',
|
||||
'emoji_categories': {
|
||||
"smileys": ":grinning:",
|
||||
"people": ":thumbsup:",
|
||||
"activity": ":soccer:",
|
||||
"travel": ":motorcycle:",
|
||||
"objects": ":bomb:",
|
||||
"nature": ":rainbow:",
|
||||
"food": ":hotdog:",
|
||||
"symbols": ":musical_note:",
|
||||
"flags": ":flag_ac:"
|
||||
}
|
||||
});
|
||||
_converse.api.promises.add(['emojisInitialized']);
|
||||
twemoji.default.base = _converse.emoji_image_path;
|
||||
|
||||
_converse.emoji_category_labels = {
|
||||
"smileys": __("Smileys and emotions"),
|
||||
"people": __("People"),
|
||||
"activity": __("Activities"),
|
||||
"travel": __("Travel"),
|
||||
"objects": __("Objects"),
|
||||
"nature": __("Animals and nature"),
|
||||
"food": __("Food and drink"),
|
||||
"symbols": __("Symbols"),
|
||||
"flags": __("Flags")
|
||||
}
|
||||
|
||||
_converse.emojis = {};
|
||||
|
||||
u.getEmojiRenderer = function () {
|
||||
return _converse.use_system_emojis ? u.shortnameToUnicode : _.flow(u.shortnameToUnicode, twemoji.default.parse);
|
||||
const how = {
|
||||
'attributes': (icon, variant) => {
|
||||
const codepoint = twemoji.default.convert.toCodePoint(icon);
|
||||
return {'title': `${u.getEmojisByAtrribute('cp')[codepoint]['sn']} ${icon}`}
|
||||
}
|
||||
};
|
||||
const toUnicode = u.shortnameToUnicode;
|
||||
return _converse.use_system_emojis ? toUnicode: text => twemoji.default.parse(toUnicode(text), how);
|
||||
};
|
||||
|
||||
u.addEmoji = function (text) {
|
||||
@ -189,105 +218,21 @@ converse.plugins.add('converse-emoji', {
|
||||
function getTonedEmojis () {
|
||||
if (!_converse.toned_emojis) {
|
||||
_converse.toned_emojis = _.uniq(
|
||||
_.map(
|
||||
_.filter(
|
||||
_converse.emojis.by_category.people,
|
||||
person => _.includes(person._shortname, '_tone')
|
||||
),
|
||||
person => person._shortname.replace(/_tone[1-5]/, '')
|
||||
)
|
||||
u.getEmojisByCategory().people
|
||||
.filter(person => person.sn.includes('_tone'))
|
||||
.map(person => person.sn.replace(/_tone[1-5]/, ''))
|
||||
);
|
||||
}
|
||||
return _converse.toned_emojis;
|
||||
}
|
||||
|
||||
function getEmojisByCategory () {
|
||||
/* Return a dict of emojis with the categories as keys and
|
||||
* lists of emojis in that category as values.
|
||||
*/
|
||||
const emojis = Object.values(_.mapValues(_converse.emojis.json, function (value, key, o) {
|
||||
value._shortname = key;
|
||||
return value
|
||||
}));
|
||||
const tones = [':tone1:', ':tone2:', ':tone3:', ':tone4:', ':tone5:'];
|
||||
const excluded = [':kiss_ww:', ':kiss_mm:', ':kiss_woman_man:'];
|
||||
const excluded_substrings = [
|
||||
':woman', ':man', ':women_', ':men_', '_man_', '_woman_', '_woman:', '_man:'
|
||||
];
|
||||
const excluded_categories = ['modifier', 'regional'];
|
||||
const categories = _.difference(
|
||||
_.uniq(_.map(emojis, _.partial(_.get, _, 'category'))),
|
||||
excluded_categories
|
||||
);
|
||||
const emojis_by_category = {};
|
||||
_.forEach(categories, (cat) => {
|
||||
let list = _.sortBy(_.filter(emojis, ['category', cat]), ['uc_base']);
|
||||
list = _.filter(
|
||||
list,
|
||||
(item) => !_.includes(_.concat(tones, excluded), item._shortname) &&
|
||||
!_.some(excluded_substrings, _.partial(_.includes, item._shortname))
|
||||
);
|
||||
if (cat === 'people') {
|
||||
const 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));
|
||||
} else if (cat === 'symbols') {
|
||||
list = _.union(_.slice(list, 60-1), _.slice(list, 0, 60));
|
||||
}
|
||||
emojis_by_category[cat] = list;
|
||||
});
|
||||
return emojis_by_category;
|
||||
}
|
||||
|
||||
u.isSingleEmoji = function (str) {
|
||||
str = str.trim();
|
||||
if (!str || (str.length > 2 && !str.startsWith(':'))) {
|
||||
return;
|
||||
}
|
||||
const result = _.flow(u.shortnameToUnicode, twemoji.default.parse)(str)
|
||||
const match = result.match(/<img class="emoji" draggable="false" alt=".*?" src=".*?\.png"\/>/);
|
||||
return match && match.length === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unicode represented by the psased in shortname.
|
||||
* @private
|
||||
* @param {string} str - String containg the shortname(s)
|
||||
*/
|
||||
u.shortnameToUnicode = function (str) {
|
||||
str = str.replace(_converse.emojis.shortnames_regex, shortname => {
|
||||
if( (typeof shortname === 'undefined') || (shortname === '') || (!(shortname in _converse.emojis.json)) ) {
|
||||
// if the shortname doesnt exist just return the entire match
|
||||
return shortname;
|
||||
}
|
||||
const unicode = _converse.emojis.json[shortname].uc_output.toUpperCase();
|
||||
return convert(unicode);
|
||||
});
|
||||
// Also replace ASCII smileys
|
||||
str = str.replace(ASCII_REPLACE_REGEX, (entire, m1, m2, m3) => {
|
||||
if( (typeof m3 === 'undefined') || (m3 === '') || (!(u.unescapeHTML(m3) in ASCII_LIST)) ) {
|
||||
// if the ascii doesnt exist just return the entire match
|
||||
return entire;
|
||||
}
|
||||
m3 = u.unescapeHTML(m3);
|
||||
const unicode = ASCII_LIST[m3].toUpperCase();
|
||||
return m2+convert(unicode);
|
||||
});
|
||||
return str;
|
||||
}
|
||||
|
||||
function getShortNames () {
|
||||
const shortnames = [];
|
||||
for (const emoji in _converse.emojis.json) {
|
||||
if (!Object.prototype.hasOwnProperty.call(_converse.emojis.json, emoji) || (emoji === '')) continue;
|
||||
shortnames.push(emoji.replace(/[+]/g, "\\$&"));
|
||||
for (let i = 0; i < _converse.emojis.json[emoji].shortnames.length; i++) {
|
||||
shortnames.push(_converse.emojis.json[emoji].shortnames[i].replace(/[+]/g, "\\$&"));
|
||||
for (let i = 0; i < _converse.emojis.json[emoji].sns.length; i++) {
|
||||
shortnames.push(_converse.emojis.json[emoji].sns[i].replace(/[+]/g, "\\$&"));
|
||||
}
|
||||
}
|
||||
return shortnames.join('|');
|
||||
@ -323,11 +268,121 @@ converse.plugins.add('converse-emoji', {
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
/************************ BEGIN Utils ************************/
|
||||
// Closured cache
|
||||
const emojis_by_attribute = {};
|
||||
|
||||
Object.assign(u, {
|
||||
/**
|
||||
* @method u.shortnameToUnicode
|
||||
* Returns unicode represented by the passed in shortname.
|
||||
* @param {string} str - String containg the shortname(s)
|
||||
*/
|
||||
shortnameToUnicode (str) {
|
||||
str = str.replace(_converse.emojis.shortnames_regex, shortname => {
|
||||
if( (typeof shortname === 'undefined') || (shortname === '') || (!(shortname in _converse.emojis.json)) ) {
|
||||
// if the shortname doesnt exist just return the entire match
|
||||
return shortname;
|
||||
}
|
||||
const unicode = _converse.emojis.json[shortname].cp.toUpperCase();
|
||||
return convert(unicode);
|
||||
});
|
||||
// Also replace ASCII smileys
|
||||
str = str.replace(ASCII_REPLACE_REGEX, (entire, m1, m2, m3) => {
|
||||
if( (typeof m3 === 'undefined') || (m3 === '') || (!(u.unescapeHTML(m3) in ASCII_LIST)) ) {
|
||||
// if the ascii doesnt exist just return the entire match
|
||||
return entire;
|
||||
}
|
||||
m3 = u.unescapeHTML(m3);
|
||||
const unicode = ASCII_LIST[m3].toUpperCase();
|
||||
return m2+convert(unicode);
|
||||
});
|
||||
return str;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether the passed in string is just a single emoji shortname;
|
||||
* @method u.isSingleEmoji
|
||||
* @param {string} shortname - A string which migh be just an emoji shortname
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSingleEmoji (shortname) {
|
||||
shortname = shortname.trim();
|
||||
if (!shortname || (shortname.length > 2 && !shortname.startsWith(':'))) {
|
||||
return;
|
||||
}
|
||||
const result = twemoji.default.parse(u.shortnameToUnicode(shortname));
|
||||
const match = result.match(/<img class="emoji" draggable="false" alt=".*?" src=".*?\.png"\/>/);
|
||||
return match && match.length === 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* @method u.getEmojisByAtrribute
|
||||
* @param {string} attr - The attribute according to which the
|
||||
* returned map should be keyed.
|
||||
* @returns {object} - Map of emojis with the passed in attribute values
|
||||
* as keys and a list of emojis for a particular category as values.
|
||||
*/
|
||||
getEmojisByAtrribute (attr) {
|
||||
if (emojis_by_attribute[attr]) {
|
||||
return emojis_by_attribute[attr];
|
||||
}
|
||||
if (attr === 'category') {
|
||||
return u.getEmojisByCategory();
|
||||
}
|
||||
emojis_by_attribute[attr] = {};
|
||||
const all_variants = _converse.emojis_list
|
||||
.map(e => e[attr])
|
||||
.filter((c, i, arr) => arr.indexOf(c) == i);
|
||||
|
||||
all_variants.forEach(v => {
|
||||
emojis_by_attribute[attr][v] = _.find(_converse.emojis_list, i => (i[attr] === v));
|
||||
});
|
||||
return emojis_by_attribute[attr];
|
||||
},
|
||||
|
||||
/**
|
||||
* @method u.getEmojisByCategory
|
||||
* @returns {object} - Map of emojis with categories as keys
|
||||
* and a list of emojis for a particular category as values.
|
||||
*/
|
||||
getEmojisByCategory () {
|
||||
if (emojis_by_attribute['category']) {
|
||||
return emojis_by_attribute['category'];
|
||||
}
|
||||
const tones = [':tone1:', ':tone2:', ':tone3:', ':tone4:', ':tone5:'];
|
||||
const excluded = [':kiss_ww:', ':kiss_mm:', ':kiss_woman_man:'];
|
||||
const excluded_substrings = [':woman', ':man', ':women_', ':men_', '_man_', '_woman_', '_woman:', '_man:'];
|
||||
const is_excluded = sn => [...tones, ...excluded].includes(sn);
|
||||
const has_excluded_substring = sn => excluded_substrings.reduce((out, str) => (out || sn.includes(str)), false);
|
||||
emojis_by_attribute['category'] = {};
|
||||
_converse.emojis.all_categories.forEach(cat => {
|
||||
let list = _.sortBy(_converse.emojis_list.filter(e => e.c === cat), ['cp']);
|
||||
list = list.filter(item => (!is_excluded(item.sn) && !has_excluded_substring(item.sn)));
|
||||
if (cat === 'smileys') {
|
||||
const idx = _.findIndex(list, ['cp', '1f600']);
|
||||
list = _.union(_.slice(list, idx), _.slice(list, 0, idx+1));
|
||||
}
|
||||
emojis_by_attribute['category'][cat] = list;
|
||||
});
|
||||
return emojis_by_attribute['category'];
|
||||
}
|
||||
});
|
||||
/************************ END Utils ************************/
|
||||
|
||||
await fetchEmojiJSON();
|
||||
_converse.emojis.shortnames_regex = new RegExp("<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+getShortNames()+")", "gi");
|
||||
_converse.emojis.by_category = getEmojisByCategory();
|
||||
_converse.emojis.categories = ["people", "activity", "travel", "objects", "nature", "food", "symbols", "flags"];
|
||||
_converse.emojis_list = Object.values(_converse.emojis.json);
|
||||
|
||||
const excluded_categories = ['modifier', 'regional'];
|
||||
_converse.emojis.all_categories = _converse.emojis_list
|
||||
.map(e => e.c)
|
||||
.filter((c, i, arr) => arr.indexOf(c) == i)
|
||||
.filter(c => !excluded_categories.includes(c));
|
||||
|
||||
_converse.emojis.toned = getTonedEmojis();
|
||||
|
||||
/**
|
||||
* Triggered once the JSON file representing emoji data has been
|
||||
* fetched and its save to start calling emoji utility methods.
|
||||
|
@ -1,32 +1,31 @@
|
||||
<div class="emoji-picker-container">
|
||||
{[ o.emoji_categories.forEach(function (category) { ]}
|
||||
<ul class="emoji-picker emoji-picker-{{{category}}} {[ if (o.current_category !== category) { ]} hidden {[ } ]}">
|
||||
{[ o._.forEach(o.emojis_by_category[category], function (emoji) { ]}
|
||||
<li class="emoji insert-emoji {[ if (o.shouldBeHidden(emoji._shortname, o.current_skintone, o.toned_emojis)) { ]} hidden {[ }; ]}"
|
||||
data-emoji="{{{emoji._shortname}}}" title="{{{emoji._shortname}}}">
|
||||
<a href="#" data-emoji="{{{emoji._shortname}}}"> {{ o.transform(emoji._shortname) }} </a>
|
||||
</li>
|
||||
<div class="emoji-picker__container">
|
||||
<div class="emoji-category-picker">
|
||||
<ul>
|
||||
{[ Object.keys(o.emoji_categories).forEach(function (category) { ]}
|
||||
<li data-category="{{{category}}}" class="emoji-category {[ if (o.current_category === category) { ]} picked {[ } ]}">
|
||||
<a class="pick-category" href="#emoji-picker-{{{category}}}" data-category="{{{category}}}"> {{ o.transform(o.emoji_categories[category]) }} </a>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="emoji-picker__lists">
|
||||
{[ Object.keys(o.emoji_categories).forEach(function (category) { ]}
|
||||
<a id="emoji-picker-{{{category}}}" class="emoji-category__heading">{{{o._converse.emoji_category_labels[category]}}}</a>
|
||||
<ul class="emoji-picker emoji-picker-{{{category}}}">
|
||||
{[ o._.forEach(o.emojis_by_category[category], function (emoji) { ]}
|
||||
<li class="emoji insert-emoji {[ if (o.shouldBeHidden(emoji.sn, o.current_skintone, o.toned_emojis)) { ]} hidden {[ }; ]}"
|
||||
data-emoji="{{{emoji.sn}}}" title="{{{emoji.sn}}}">
|
||||
<a href="#" data-emoji="{{{emoji.sn}}}"> {{ o.transform(emoji.sn) }} </a>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
{[ }); ]}
|
||||
</div>
|
||||
<ul class="emoji-skintone-picker">
|
||||
{[ o._.forEach(o.skintones, function (skintone) { ]}
|
||||
<li data-skintone="{{{skintone}}}" class="emoji-skintone {[ if (o.current_skintone === skintone) { ]} picked {[ } ]}">
|
||||
<a class="pick-skintone" href="#" data-skintone="{{{skintone}}}"> {{ o.transform(':'+skintone+':') }} </a>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
{[ }); ]}
|
||||
<ul class="emoji-toolbar">
|
||||
<li class="emoji-category-picker">
|
||||
<ul>
|
||||
{[ o.emoji_categories.forEach(function (category) { ]}
|
||||
<li data-category="{{{category}}}" class="emoji-category {[ if (o.current_category === category) { ]} picked {[ } ]}">
|
||||
<a class="pick-category" href="#" data-category="{{{category}}}"> {{ o.transform(o.emojis_by_category[category][0]._shortname) }} </a>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="emoji-skintone-picker">
|
||||
<ul>
|
||||
{[ o._.forEach(o.skintones, function (skintone) { ]}
|
||||
<li data-skintone="{{{skintone}}}" class="emoji-skintone {[ if (o.current_skintone === skintone) { ]} picked {[ } ]}">
|
||||
<a class="pick-skintone" href="#" data-skintone="{{{skintone}}}"> {{ o.transform(':'+skintone+':') }} </a>
|
||||
</li>
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user