summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2016-07-05 11:58:18 -0400
committerJoram Wilander <jwawilander@gmail.com>2016-07-05 11:58:18 -0400
commitdc2f2a800105b77e665ec2a00c6290f35b1a2ba3 (patch)
tree82f23c2e72a7c785f55c2d6c1c35c10c16994918
parenta65f1fc266f15eaa8f79541d4d11440c3d356bb6 (diff)
downloadchat-dc2f2a800105b77e665ec2a00c6290f35b1a2ba3.tar.gz
chat-dc2f2a800105b77e665ec2a00c6290f35b1a2ba3.tar.bz2
chat-dc2f2a800105b77e665ec2a00c6290f35b1a2ba3.zip
PLT-3145 Custom Emojis (#3381)
* Reorganized Backstage code to use a view controller and separated it from integrations code * Renamed InstalledIntegrations component to BackstageList * Added EmojiList page * Added AddEmoji page * Added custom emoji to autocomplete and text formatter * Moved system emoji to EmojiStore * Stopped trying to get emoji before logging in * Rerender posts when emojis change * Fixed submit handler on backstage pages to properly support enter * Removed debugging code * Updated javascript driver * Fixed unit tests * Fixed backstage routes * Added clientside validation to prevent users from creating an emoji with the same name as a system one * Fixed AddEmoji page to properly redirect when an emoji is created successfully * Fixed updating emoji list when an emoji is deleted * Added type prop to BackstageList to properly support using a table for the list * Added help text to EmojiList * Fixed backstage on smaller screen sizes * Disable custom emoji by default * Improved restrictions on creating emojis * Fixed non-admin users seeing the option to delete each other's emojis * Fixing gofmt * Fixed emoji unit tests * Fixed trying to get emoji from the server when it's disabled
-rw-r--r--Makefile5
-rw-r--r--api/emoji.go6
-rw-r--r--api/emoji_test.go31
-rw-r--r--config/config.json8
-rw-r--r--einterfaces/emoji.go22
-rw-r--r--i18n/en.json10
-rw-r--r--model/config.go5
-rw-r--r--webapp/components/admin_console/custom_emoji_settings.jsx51
-rw-r--r--webapp/components/backstage/backstage_controller.jsx71
-rw-r--r--webapp/components/backstage/components/backstage_category.jsx (renamed from webapp/components/backstage/backstage_category.jsx)1
-rw-r--r--webapp/components/backstage/components/backstage_header.jsx (renamed from webapp/components/backstage/backstage_header.jsx)0
-rw-r--r--webapp/components/backstage/components/backstage_list.jsx108
-rw-r--r--webapp/components/backstage/components/backstage_navbar.jsx (renamed from webapp/components/backstage/backstage_navbar.jsx)36
-rw-r--r--webapp/components/backstage/components/backstage_section.jsx (renamed from webapp/components/backstage/backstage_section.jsx)0
-rw-r--r--webapp/components/backstage/components/backstage_sidebar.jsx (renamed from webapp/components/backstage/backstage_sidebar.jsx)79
-rw-r--r--webapp/components/backstage/installed_integrations.jsx101
-rw-r--r--webapp/components/emoji/components/add_emoji.jsx307
-rw-r--r--webapp/components/emoji/components/emoji_list.jsx218
-rw-r--r--webapp/components/emoji/components/emoji_list_item.jsx118
-rw-r--r--webapp/components/integrations/components/add_command.jsx (renamed from webapp/components/backstage/add_command.jsx)19
-rw-r--r--webapp/components/integrations/components/add_incoming_webhook.jsx (renamed from webapp/components/backstage/add_incoming_webhook.jsx)20
-rw-r--r--webapp/components/integrations/components/add_outgoing_webhook.jsx (renamed from webapp/components/backstage/add_outgoing_webhook.jsx)20
-rw-r--r--webapp/components/integrations/components/installed_command.jsx (renamed from webapp/components/backstage/installed_command.jsx)5
-rw-r--r--webapp/components/integrations/components/installed_commands.jsx (renamed from webapp/components/backstage/installed_commands.jsx)15
-rw-r--r--webapp/components/integrations/components/installed_incoming_webhook.jsx (renamed from webapp/components/backstage/installed_incoming_webhook.jsx)3
-rw-r--r--webapp/components/integrations/components/installed_incoming_webhooks.jsx (renamed from webapp/components/backstage/installed_incoming_webhooks.jsx)15
-rw-r--r--webapp/components/integrations/components/installed_outgoing_webhook.jsx (renamed from webapp/components/backstage/installed_outgoing_webhook.jsx)3
-rw-r--r--webapp/components/integrations/components/installed_outgoing_webhooks.jsx (renamed from webapp/components/backstage/installed_outgoing_webhooks.jsx)15
-rw-r--r--webapp/components/integrations/components/integration_option.jsx (renamed from webapp/components/backstage/integration_option.jsx)0
-rw-r--r--webapp/components/integrations/components/integrations.jsx (renamed from webapp/components/backstage/integrations.jsx)13
-rw-r--r--webapp/components/logged_in.jsx7
-rw-r--r--webapp/components/navbar_dropdown.jsx20
-rw-r--r--webapp/components/post_view/components/post.jsx8
-rw-r--r--webapp/components/post_view/components/post_body.jsx9
-rw-r--r--webapp/components/post_view/components/post_body_additional_content.jsx3
-rw-r--r--webapp/components/post_view/components/post_list.jsx4
-rw-r--r--webapp/components/post_view/post_focus_view_controller.jsx14
-rw-r--r--webapp/components/post_view/post_view_controller.jsx14
-rw-r--r--webapp/components/root.jsx3
-rw-r--r--webapp/components/suggestion/emoticon_provider.jsx21
-rw-r--r--webapp/i18n/en.json35
-rw-r--r--webapp/package.json2
-rw-r--r--webapp/routes/route_emoji.jsx24
-rw-r--r--webapp/routes/route_integrations.jsx140
-rw-r--r--webapp/routes/route_team.jsx85
-rw-r--r--webapp/sass/responsive/_mobile.scss1
-rw-r--r--webapp/sass/routes/_backstage.scss196
-rw-r--r--webapp/stores/emoji_store.jsx135
-rw-r--r--webapp/stores/team_store.jsx7
-rw-r--r--webapp/tests/emoticons.test.jsx15
-rw-r--r--webapp/utils/async_client.jsx85
-rw-r--r--webapp/utils/constants.jsx5
-rw-r--r--webapp/utils/emoji.json8334
-rw-r--r--webapp/utils/emoticons.jsx121
-rw-r--r--webapp/utils/text_formatting.jsx11
-rw-r--r--webapp/utils/utils.jsx5
56 files changed, 1693 insertions, 8916 deletions
diff --git a/Makefile b/Makefile
index 368b3234b..131cef571 100644
--- a/Makefile
+++ b/Makefile
@@ -180,12 +180,15 @@ ifeq ($(BUILD_ENTERPRISE_READY),true)
$(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/ldap && ./ldap.test -test.v -test.timeout=120s -test.coverprofile=cldap.out || exit 1
$(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/compliance && ./compliance.test -test.v -test.timeout=120s -test.coverprofile=ccompliance.out || exit 1
+ $(GO) test $(GOFLAGS) -run=$(TESTS) -covermode=count -c ./enterprise/emoji && ./emoji.test -test.v -test.timeout=120s -test.coverprofile=cemoji.out || exit 1
tail -n +2 cldap.out >> ecover.out
tail -n +2 ccompliance.out >> ecover.out
- rm -f cldap.out ccompliance.out
+ tail -n +2 cemoji.out >> ecover.out
+ rm -f cldap.out ccompliance.out cemoji.out
rm -r ldap.test
rm -r compliance.test
+ rm -r emoji.test
endif
internal-test-web-client: start-docker prepare-enterprise
diff --git a/api/emoji.go b/api/emoji.go
index 24989924a..d84996230 100644
--- a/api/emoji.go
+++ b/api/emoji.go
@@ -16,6 +16,7 @@ import (
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
+ "github.com/mattermost/platform/einterfaces"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
)
@@ -32,7 +33,7 @@ func InitEmoji() {
BaseRoutes.Emoji.Handle("/list", ApiUserRequired(getEmoji)).Methods("GET")
BaseRoutes.Emoji.Handle("/create", ApiUserRequired(createEmoji)).Methods("POST")
BaseRoutes.Emoji.Handle("/delete", ApiUserRequired(deleteEmoji)).Methods("POST")
- BaseRoutes.Emoji.Handle("/{id:[A-Za-z0-9_]+}", ApiUserRequired(getEmojiImage)).Methods("GET")
+ BaseRoutes.Emoji.Handle("/{id:[A-Za-z0-9_]+}", ApiUserRequiredTrustRequester(getEmojiImage)).Methods("GET")
}
func getEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
@@ -58,7 +59,8 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if !(*utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation == model.RESTRICT_EMOJI_CREATION_ALL || c.IsSystemAdmin()) {
+ if emojiInterface := einterfaces.GetEmojiInterface(); emojiInterface != nil &&
+ !emojiInterface.CanUserCreateEmoji(c.Session.Roles, c.Session.TeamMembers) {
c.Err = model.NewLocAppError("createEmoji", "api.emoji.create.permissions.app_error", nil, "user_id="+c.Session.UserId)
c.Err.StatusCode = http.StatusUnauthorized
return
diff --git a/api/emoji_test.go b/api/emoji_test.go
index 26dbe9323..fb23cc439 100644
--- a/api/emoji_test.go
+++ b/api/emoji_test.go
@@ -22,6 +22,12 @@ func TestGetEmoji(t *testing.T) {
th := Setup().InitBasic()
Client := th.BasicClient
+ EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
+ defer func() {
+ *utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
+ }()
+ *utils.Cfg.ServiceSettings.EnableCustomEmoji = true
+
emojis := []*model.Emoji{
{
CreatorId: model.NewId(),
@@ -95,13 +101,10 @@ func TestCreateEmoji(t *testing.T) {
Client := th.BasicClient
EnableCustomEmoji := *utils.Cfg.ServiceSettings.EnableCustomEmoji
- RestrictCustomEmojiCreation := *utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation
defer func() {
*utils.Cfg.ServiceSettings.EnableCustomEmoji = EnableCustomEmoji
- *utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation = RestrictCustomEmojiCreation
}()
*utils.Cfg.ServiceSettings.EnableCustomEmoji = false
- *utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ALL
emoji := &model.Emoji{
CreatorId: th.BasicUser.Id,
@@ -213,28 +216,6 @@ func TestCreateEmoji(t *testing.T) {
if _, err := Client.CreateEmoji(emoji, createTestGif(t, 10, 10), "image.gif"); err == nil {
t.Fatal("shouldn't be able to create an emoji as another user")
}
-
- *utils.Cfg.ServiceSettings.RestrictCustomEmojiCreation = model.RESTRICT_EMOJI_CREATION_ADMIN
-
- // try to create an emoji when only system admins are allowed to create them
- emoji = &model.Emoji{
- CreatorId: th.BasicUser.Id,
- Name: model.NewId(),
- }
- if _, err := Client.CreateEmoji(emoji, createTestGif(t, 10, 10), "image.gif"); err == nil {
- t.Fatal("shouldn't be able to create an emoji when not a system admin")
- }
-
- emoji = &model.Emoji{
- CreatorId: th.SystemAdminUser.Id,
- Name: model.NewId(),
- }
- if emojiResult, err := th.SystemAdminClient.CreateEmoji(emoji, createTestPng(t, 10, 10), "image.png"); err != nil {
- t.Fatal(err)
- } else {
- emoji = emojiResult
- }
- th.SystemAdminClient.MustGeneric(th.SystemAdminClient.DeleteEmoji(emoji.Id))
}
func TestDeleteEmoji(t *testing.T) {
diff --git a/config/config.json b/config/config.json
index eeb75d0c1..fb325248d 100644
--- a/config/config.json
+++ b/config/config.json
@@ -5,9 +5,9 @@
"SegmentDeveloperKey": "",
"GoogleDeveloperKey": "",
"EnableOAuthServiceProvider": false,
- "EnableIncomingWebhooks": false,
- "EnableOutgoingWebhooks": false,
- "EnableCommands": false,
+ "EnableIncomingWebhooks": true,
+ "EnableOutgoingWebhooks": true,
+ "EnableCommands": true,
"EnableOnlyAdminIntegrations": true,
"EnablePostUsernameOverride": false,
"EnablePostIconOverride": false,
@@ -24,7 +24,7 @@
"WebsocketSecurePort": 443,
"WebsocketPort": 80,
"WebserverMode": "regular",
- "EnableCustomEmoji": true,
+ "EnableCustomEmoji": false,
"RestrictCustomEmojiCreation": "all"
},
"TeamSettings": {
diff --git a/einterfaces/emoji.go b/einterfaces/emoji.go
new file mode 100644
index 000000000..f276f6a32
--- /dev/null
+++ b/einterfaces/emoji.go
@@ -0,0 +1,22 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package einterfaces
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type EmojiInterface interface {
+ CanUserCreateEmoji(string, []*model.TeamMember) bool
+}
+
+var theEmojiInterface EmojiInterface
+
+func RegisterEmojiInterface(newInterface EmojiInterface) {
+ theEmojiInterface = newInterface
+}
+
+func GetEmojiInterface() EmojiInterface {
+ return theEmojiInterface
+}
diff --git a/i18n/en.json b/i18n/en.json
index 3d433dae9..f76c7f637 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -581,7 +581,7 @@
},
{
"id": "api.emoji.create.parse.app_error",
- "translation": "Unable to create emoji. Image exceeds maximum file size."
+ "translation": "Unable to create emoji. Could not understand request."
},
{
"id": "api.emoji.create.permissions.app_error",
@@ -589,7 +589,7 @@
},
{
"id": "api.emoji.create.too_large.app_error",
- "translation": "Unable to create emoji. Could not understand request."
+ "translation": "Unable to create emoji. Image must be less than 64 KB in size."
},
{
"id": "api.emoji.delete.permissions.app_error",
@@ -621,7 +621,7 @@
},
{
"id": "api.emoji.upload.large_image.app_error",
- "translation": "Unable to create emoji. Image exceeds maximum dimensions."
+ "translation": "Unable to create emoji. Image must be at most 128 by 128 pixels."
},
{
"id": "api.export.json.app_error",
@@ -2080,6 +2080,10 @@
"translation": "Compliance export started for job '{{.JobName}}' at '{{.FilePath}}'"
},
{
+ "id": "ent.emoji.licence_disable.app_error",
+ "translation": "Custom emoji restrictions disabled by current license. Please contact your system administrator about upgrading your enterprise license."
+ },
+ {
"id": "ent.ldap.do_login.bind_admin_user.app_error",
"translation": "Unable to bind to LDAP server. Check BindUsername and BindPassword."
},
diff --git a/model/config.go b/model/config.go
index e71a58a21..a8c63b1eb 100644
--- a/model/config.go
+++ b/model/config.go
@@ -38,8 +38,9 @@ const (
FAKE_SETTING = "********************************"
- RESTRICT_EMOJI_CREATION_ALL = "all"
- RESTRICT_EMOJI_CREATION_ADMIN = "system_admin"
+ RESTRICT_EMOJI_CREATION_ALL = "all"
+ RESTRICT_EMOJI_CREATION_ADMIN = "admin"
+ RESTRICT_EMOJI_CREATION_SYSTEM_ADMIN = "system_admin"
)
type ServiceSettings struct {
diff --git a/webapp/components/admin_console/custom_emoji_settings.jsx b/webapp/components/admin_console/custom_emoji_settings.jsx
index 332c7b216..738afa3cd 100644
--- a/webapp/components/admin_console/custom_emoji_settings.jsx
+++ b/webapp/components/admin_console/custom_emoji_settings.jsx
@@ -27,7 +27,10 @@ export default class CustomEmojiSettings extends AdminSettings {
getConfigFromState(config) {
config.ServiceSettings.EnableCustomEmoji = this.state.enableCustomEmoji;
- config.ServiceSettings.RestrictCustomEmojiCreation = this.state.restrictCustomEmojiCreation;
+
+ if (global.window.mm_license.IsLicensed === 'true') {
+ config.ServiceSettings.RestrictCustomEmojiCreation = this.state.restrictCustomEmojiCreation;
+ }
return config;
}
@@ -44,29 +47,14 @@ export default class CustomEmojiSettings extends AdminSettings {
}
renderSettings() {
- return (
- <SettingsGroup>
- <BooleanSetting
- id='enableCustomEmoji'
- label={
- <FormattedMessage
- id='admin.customization.enableCustomEmojiTitle'
- defaultMessage='Enable Custom Emoji:'
- />
- }
- helpText={
- <FormattedMessage
- id='admin.customization.enableCustomEmojiDesc'
- defaultMessage='Enable users to create custom emoji for use in chat messages.'
- />
- }
- value={this.state.enableCustomEmoji}
- onChange={this.handleChange}
- />
+ let restrictSetting = null;
+ if (global.window.mm_license.IsLicensed === 'true') {
+ restrictSetting = (
<DropdownSetting
id='restrictCustomEmojiCreation'
values={[
{value: 'all', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationAll', 'Allow everyone to create custom emoji')},
+ {value: 'admin', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationAdmin', 'Allow system and team admins to create custom emoji')},
{value: 'system_admin', text: Utils.localizeMessage('admin.customization.restrictCustomEmojiCreationSystemAdmin', 'Only allow system admins to create custom emoji')}
]}
label={
@@ -85,6 +73,29 @@ export default class CustomEmojiSettings extends AdminSettings {
onChange={this.handleChange}
disabled={!this.state.enableCustomEmoji}
/>
+ );
+ }
+
+ return (
+ <SettingsGroup>
+ <BooleanSetting
+ id='enableCustomEmoji'
+ label={
+ <FormattedMessage
+ id='admin.customization.enableCustomEmojiTitle'
+ defaultMessage='Enable Custom Emoji:'
+ />
+ }
+ helpText={
+ <FormattedMessage
+ id='admin.customization.enableCustomEmojiDesc'
+ defaultMessage='Enable users to create custom emoji for use in chat messages.'
+ />
+ }
+ value={this.state.enableCustomEmoji}
+ onChange={this.handleChange}
+ />
+ {restrictSetting}
</SettingsGroup>
);
}
diff --git a/webapp/components/backstage/backstage_controller.jsx b/webapp/components/backstage/backstage_controller.jsx
new file mode 100644
index 000000000..690880071
--- /dev/null
+++ b/webapp/components/backstage/backstage_controller.jsx
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import TeamStore from 'stores/team_store.jsx';
+
+import BackstageSidebar from './components/backstage_sidebar.jsx';
+import BackstageNavbar from './components/backstage_navbar.jsx';
+import ErrorBar from 'components/error_bar.jsx';
+
+export default class BackstageController extends React.Component {
+ static get propTypes() {
+ return {
+ children: React.PropTypes.node.isRequired,
+ params: React.PropTypes.object.isRequired,
+ user: React.PropTypes.user.isRequired
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onTeamChange = this.onTeamChange.bind(this);
+
+ this.state = {
+ team: props.params.team ? TeamStore.getByName(props.params.team) : TeamStore.getCurrent()
+ };
+ }
+
+ componentDidMount() {
+ TeamStore.addChangeListener(this.onTeamChange);
+ }
+
+ componentWillUnmount() {
+ TeamStore.removeChangeListener(this.onTeamChange);
+ }
+
+ onTeamChange() {
+ this.state = {
+ team: this.props.params.team ? TeamStore.getByName(this.props.params.team) : TeamStore.getCurrent()
+ };
+ }
+
+ render() {
+ return (
+ <div className='backstage'>
+ <ErrorBar/>
+ <BackstageNavbar team={this.state.team}/>
+ <div className='backstage-body'>
+ <BackstageSidebar
+ team={this.state.team}
+ user={this.props.user}
+ />
+ {
+ React.Children.map(this.props.children, (child) => {
+ if (!child) {
+ return child;
+ }
+
+ return React.cloneElement(child, {
+ team: this.state.team,
+ user: this.props.user
+ });
+ })
+ }
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/webapp/components/backstage/backstage_category.jsx b/webapp/components/backstage/components/backstage_category.jsx
index 1d4b11ca3..74dcf3476 100644
--- a/webapp/components/backstage/backstage_category.jsx
+++ b/webapp/components/backstage/components/backstage_category.jsx
@@ -59,6 +59,7 @@ export default class BackstageCategory extends React.Component {
to={link}
className='category-title'
activeClassName='category-title--active'
+ onlyActiveOnIndex={true}
>
<i className={'fa ' + icon}/>
<span className='category-title__text'>
diff --git a/webapp/components/backstage/backstage_header.jsx b/webapp/components/backstage/components/backstage_header.jsx
index 37b4be349..37b4be349 100644
--- a/webapp/components/backstage/backstage_header.jsx
+++ b/webapp/components/backstage/components/backstage_header.jsx
diff --git a/webapp/components/backstage/components/backstage_list.jsx b/webapp/components/backstage/components/backstage_list.jsx
new file mode 100644
index 000000000..81b8ec4d9
--- /dev/null
+++ b/webapp/components/backstage/components/backstage_list.jsx
@@ -0,0 +1,108 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import * as Utils from 'utils/utils.jsx';
+
+import {Link} from 'react-router';
+import LoadingScreen from 'components/loading_screen.jsx';
+
+export default class BackstageList extends React.Component {
+ static propTypes = {
+ children: React.PropTypes.node,
+ header: React.PropTypes.node.isRequired,
+ addLink: React.PropTypes.string,
+ addText: React.PropTypes.node,
+ emptyText: React.PropTypes.node,
+ loading: React.PropTypes.bool.isRequired,
+ searchPlaceholder: React.PropTypes.string
+ }
+
+ static defaultProps = {
+ searchPlaceholder: Utils.localizeMessage('backstage.search', 'Search')
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.updateFilter = this.updateFilter.bind(this);
+
+ this.state = {
+ filter: ''
+ };
+ }
+
+ updateFilter(e) {
+ this.setState({
+ filter: e.target.value
+ });
+ }
+
+ render() {
+ const filter = this.state.filter.toLowerCase();
+
+ let children;
+ if (this.props.loading) {
+ children = <LoadingScreen/>;
+ } else {
+ children = React.Children.map(this.props.children, (child) => {
+ return React.cloneElement(child, {filter});
+ });
+
+ if (children.length === 0 && this.props.emptyText) {
+ children = (
+ <span className='backstage-list__item backstage-list__empty'>
+ {this.props.emptyText}
+ </span>
+ );
+ }
+ }
+
+ let addLink = null;
+ if (this.props.addLink && this.props.addText) {
+ addLink = (
+ <Link
+ className='add-link'
+ to={this.props.addLink}
+ >
+ <button
+ type='button'
+ className='btn btn-primary'
+ >
+ <span>
+ {this.props.addText}
+ </span>
+ </button>
+ </Link>
+ );
+ }
+
+ return (
+ <div className='backstage-content'>
+ <div className='backstage-header'>
+ <h1>
+ {this.props.header}
+ </h1>
+ {addLink}
+ </div>
+ <div className='backstage-filters'>
+ <div className='backstage-filter__search'>
+ <i className='fa fa-search'></i>
+ <input
+ type='search'
+ className='form-control'
+ placeholder={this.props.searchPlaceholder}
+ value={this.state.filter}
+ onChange={this.updateFilter}
+ style={{flexGrow: 0, flexShrink: 0}}
+ />
+ </div>
+ </div>
+ <div className='backstage-list'>
+ {children}
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/backstage/backstage_navbar.jsx b/webapp/components/backstage/components/backstage_navbar.jsx
index 26ab44c87..7bccfc9f7 100644
--- a/webapp/components/backstage/backstage_navbar.jsx
+++ b/webapp/components/backstage/components/backstage_navbar.jsx
@@ -1,52 +1,28 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import $ from 'jquery';
-
import React from 'react';
-import TeamStore from 'stores/team_store.jsx';
-
import {FormattedMessage} from 'react-intl';
import {Link} from 'react-router/es6';
export default class BackstageNavbar extends React.Component {
- constructor(props) {
- super(props);
-
- this.handleChange = this.handleChange.bind(this);
-
- this.state = {
- team: TeamStore.getCurrent()
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
};
}
- componentDidMount() {
- TeamStore.addChangeListener(this.handleChange);
- $('body').addClass('backstage');
- }
-
- componentWillUnmount() {
- TeamStore.removeChangeListener(this.handleChange);
- $('body').removeClass('backstage');
- }
-
- handleChange() {
- this.setState({
- team: TeamStore.getCurrent()
- });
- }
-
render() {
- if (!this.state.team) {
+ if (!this.props.team) {
return null;
}
return (
- <div className='backstage-navbar row'>
+ <div className='backstage-navbar'>
<Link
className='backstage-navbar__back'
- to={`/${this.state.team.name}/channels/town-square`}
+ to={`/${this.props.team.name}/channels/town-square`}
>
<i className='fa fa-angle-left'/>
<span>
diff --git a/webapp/components/backstage/backstage_section.jsx b/webapp/components/backstage/components/backstage_section.jsx
index c8b63af18..c8b63af18 100644
--- a/webapp/components/backstage/backstage_section.jsx
+++ b/webapp/components/backstage/components/backstage_section.jsx
diff --git a/webapp/components/backstage/backstage_sidebar.jsx b/webapp/components/backstage/components/backstage_sidebar.jsx
index 4d8d8337d..a17d830b0 100644
--- a/webapp/components/backstage/backstage_sidebar.jsx
+++ b/webapp/components/backstage/components/backstage_sidebar.jsx
@@ -3,13 +3,51 @@
import React from 'react';
-import * as Utils from 'utils/utils.jsx';
+import TeamStore from 'stores/team_store.jsx';
+
import BackstageCategory from './backstage_category.jsx';
import BackstageSection from './backstage_section.jsx';
import {FormattedMessage} from 'react-intl';
export default class BackstageSidebar extends React.Component {
- render() {
+ static get propTypes() {
+ return {
+ team: React.PropTypes.object.isRequired,
+ user: React.PropTypes.object.isRequired
+ };
+ }
+
+ renderCustomEmoji() {
+ if (window.mm_config.EnableCustomEmoji !== 'true') {
+ return null;
+ }
+
+ return (
+ <BackstageCategory
+ name='emoji'
+ parentLink={'/' + this.props.team.name}
+ icon='fa-smile-o'
+ title={
+ <FormattedMessage
+ id='backstage_sidebar.emoji'
+ defaultMessage='Custom Emoji'
+ />
+ }
+ />
+ );
+ }
+
+ renderIntegrations() {
+ if (window.mm_config.EnableIncomingWebhooks !== 'true' &&
+ window.mm_config.EnableOutgoingWebhooks !== 'true' &&
+ window.mm_config.EnableCommands !== 'true') {
+ return null;
+ }
+
+ if (window.mm_config.RestrictCustomEmojiCreation !== 'all' && !TeamStore.isTeamAdmin(this.props.user.id, this.props.team.id)) {
+ return null;
+ }
+
let incomingWebhooks = null;
if (window.mm_config.EnableIncomingWebhooks === 'true') {
incomingWebhooks = (
@@ -56,23 +94,30 @@ export default class BackstageSidebar extends React.Component {
}
return (
+ <BackstageCategory
+ name='integrations'
+ parentLink={'/' + this.props.team.name}
+ icon='fa-link'
+ title={
+ <FormattedMessage
+ id='backstage_sidebar.integrations'
+ defaultMessage='Integrations'
+ />
+ }
+ >
+ {incomingWebhooks}
+ {outgoingWebhooks}
+ {commands}
+ </BackstageCategory>
+ );
+ }
+
+ render() {
+ return (
<div className='backstage-sidebar'>
<ul>
- <BackstageCategory
- name='integrations'
- parentLink={'/' + Utils.getTeamNameFromUrl() + '/settings'}
- icon='fa-link'
- title={
- <FormattedMessage
- id='backstage_sidebar.integrations'
- defaultMessage='Integrations'
- />
- }
- >
- {incomingWebhooks}
- {outgoingWebhooks}
- {commands}
- </BackstageCategory>
+ {this.renderCustomEmoji()}
+ {this.renderIntegrations()}
</ul>
</div>
);
diff --git a/webapp/components/backstage/installed_integrations.jsx b/webapp/components/backstage/installed_integrations.jsx
deleted file mode 100644
index f6de8bc11..000000000
--- a/webapp/components/backstage/installed_integrations.jsx
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React from 'react';
-
-import * as Utils from 'utils/utils.jsx';
-
-import {Link} from 'react-router/es6';
-import LoadingScreen from 'components/loading_screen.jsx';
-
-export default class InstalledIntegrations extends React.Component {
- static get propTypes() {
- return {
- children: React.PropTypes.node,
- header: React.PropTypes.node.isRequired,
- addLink: React.PropTypes.string.isRequired,
- addText: React.PropTypes.node.isRequired,
- emptyText: React.PropTypes.node.isRequired,
- loading: React.PropTypes.bool.isRequired
- };
- }
-
- constructor(props) {
- super(props);
-
- this.updateFilter = this.updateFilter.bind(this);
-
- this.state = {
- filter: ''
- };
- }
-
- updateFilter(e) {
- this.setState({
- filter: e.target.value
- });
- }
-
- render() {
- const filter = this.state.filter.toLowerCase();
-
- let children;
-
- if (this.props.loading) {
- children = <LoadingScreen/>;
- } else {
- children = React.Children.map(this.props.children, (child) => {
- return React.cloneElement(child, {filter});
- });
-
- if (children.length === 0) {
- children = (
- <span className='backstage-list__item backstage-list_empty'>
- {this.props.emptyText}
- </span>
- );
- }
- }
-
- return (
- <div className='backstage-content'>
- <div className='installed-integrations'>
- <div className='backstage-header'>
- <h1>
- {this.props.header}
- </h1>
- <Link
- className='add-integrations-link'
- to={this.props.addLink}
- >
- <button
- type='button'
- className='btn btn-primary'
- >
- <span>
- {this.props.addText}
- </span>
- </button>
- </Link>
- </div>
- <div className='backstage-filters'>
- <div className='backstage-filter__search'>
- <i className='fa fa-search'></i>
- <input
- type='search'
- className='form-control'
- placeholder={Utils.localizeMessage('installed_integrations.search', 'Search Integrations')}
- value={this.state.filter}
- onChange={this.updateFilter}
- style={{flexGrow: 0, flexShrink: 0}}
- />
- </div>
- </div>
- <div className='backstage-list'>
- {children}
- </div>
- </div>
- </div>
- );
- }
-}
diff --git a/webapp/components/emoji/components/add_emoji.jsx b/webapp/components/emoji/components/add_emoji.jsx
new file mode 100644
index 000000000..46f345476
--- /dev/null
+++ b/webapp/components/emoji/components/add_emoji.jsx
@@ -0,0 +1,307 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import * as AsyncClient from 'utils/async_client.jsx';
+import EmojiStore from 'stores/emoji_store.jsx';
+
+import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
+import {FormattedMessage} from 'react-intl';
+import FormError from 'components/form_error.jsx';
+import {Link} from 'react-router';
+import SpinnerButton from 'components/spinner_button.jsx';
+
+export default class AddEmoji extends React.Component {
+ static propTypes = {
+ team: React.PropTypes.object.isRequired,
+ user: React.PropTypes.object.isRequired
+ }
+
+ static contextTypes = {
+ router: React.PropTypes.object.isRequired
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.handleSubmit = this.handleSubmit.bind(this);
+
+ this.updateName = this.updateName.bind(this);
+ this.updateImage = this.updateImage.bind(this);
+
+ this.state = {
+ name: '',
+ image: null,
+ imageUrl: '',
+ saving: false,
+ error: null
+ };
+ }
+
+ handleSubmit(e) {
+ e.preventDefault();
+
+ if (this.state.saving) {
+ return;
+ }
+
+ this.setState({
+ saving: true,
+ error: null
+ });
+
+ const emoji = {
+ creator_id: this.props.user.id,
+ name: this.state.name.trim().toLowerCase()
+ };
+
+ if (!emoji.name) {
+ this.setState({
+ saving: false,
+ error: (
+ <FormattedMessage
+ id='add_emoji.nameRequired'
+ defaultMessage='A name is required for the emoji'
+ />
+ )
+ });
+
+ return;
+ } else if (/[^a-z0-9_-]/.test(emoji.name)) {
+ this.setState({
+ saving: false,
+ error: (
+ <FormattedMessage
+ id='add_emoji.nameInvalid'
+ defaultMessage="An emoji's name can only contain lowercase letters, numbers, and the symbols '-' and '_'."
+ />
+ )
+ });
+
+ return;
+ } else if (EmojiStore.getSystemEmojis().has(emoji.name)) {
+ this.setState({
+ saving: false,
+ error: (
+ <FormattedMessage
+ id='add_emoji.nameTaken'
+ defaultMessage='This name is already in use by a system emoji. Please choose another name.'
+ />
+ )
+ });
+
+ return;
+ }
+
+ if (!this.state.image) {
+ this.setState({
+ saving: false,
+ error: (
+ <FormattedMessage
+ id='add_emoji.imageRequired'
+ defaultMessage='An image is required for the emoji'
+ />
+ )
+ });
+
+ return;
+ }
+
+ AsyncClient.addEmoji(
+ emoji,
+ this.state.image,
+ () => {
+ // for some reason, browserHistory.push doesn't trigger a state change even though the url changes
+ this.context.router.push('/' + this.props.team.name + '/emoji');
+ },
+ (err) => {
+ this.setState({
+ saving: false,
+ error: err.message
+ });
+ }
+ );
+ }
+
+ updateName(e) {
+ this.setState({
+ name: e.target.value
+ });
+ }
+
+ updateImage(e) {
+ if (e.target.files.length === 0) {
+ this.setState({
+ image: null,
+ imageUrl: ''
+ });
+
+ return;
+ }
+
+ const image = e.target.files[0];
+
+ const reader = new FileReader();
+ reader.onload = () => {
+ this.setState({
+ image,
+ imageUrl: reader.result
+ });
+ };
+ reader.readAsDataURL(image);
+ }
+
+ render() {
+ let filename = null;
+ if (this.state.image) {
+ filename = (
+ <span className='add-emoji__filename'>
+ {this.state.image.name}
+ </span>
+ );
+ }
+
+ let preview = null;
+ if (this.state.imageUrl) {
+ preview = (
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='preview'
+ >
+ <FormattedMessage
+ id='add_emoji.preview'
+ defaultMessage='Preview'
+ />
+ </label>
+ <div className='col-md-5 col-sm-8 add-emoji__preview'>
+ <FormattedMessage
+ id='add_emoji.preview.sentence'
+ defaultMessage='This is a sentence with {image} in it.'
+ values={{
+ image: (
+ <img
+ className='emoticon'
+ src={this.state.imageUrl}
+ />
+ )
+ }}
+ />
+ </div>
+ </div>
+ );
+ }
+
+ return (
+ <div className='backstage-content row'>
+ <BackstageHeader>
+ <Link to={'/' + this.props.team.name + '/emoji'}>
+ <FormattedMessage
+ id='emoji_list.header'
+ defaultMessage='Custom Emoji'
+ />
+ </Link>
+ <FormattedMessage
+ id='add_emoji.header'
+ defaultMessage='Add'
+ />
+ </BackstageHeader>
+ <div className='backstage-form'>
+ <form
+ className='form-horizontal'
+ onSubmit={this.handleSubmit}
+ >
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='name'
+ >
+ <FormattedMessage
+ id='add_emoji.name'
+ defaultMessage='Name'
+ />
+ </label>
+ <div className='col-md-5 col-sm-8'>
+ <input
+ id='name'
+ type='text'
+ maxLength='64'
+ className='form-control'
+ value={this.state.name}
+ onChange={this.updateName}
+ />
+ <div className='form__help'>
+ <FormattedMessage
+ id='add_emoji.name.help'
+ defaultMessage="Choose a name for your emoji made of up to 64 characters consisting of lowercase letters, numbers, and the symbols '-' and '_'."
+ />
+ </div>
+ </div>
+ </div>
+ <div className='form-group'>
+ <label
+ className='control-label col-sm-4'
+ htmlFor='image'
+ >
+ <FormattedMessage
+ id='add_emoji.image'
+ defaultMessage='Image'
+ />
+ </label>
+ <div className='col-md-5 col-sm-8'>
+ <div>
+ <div className='add-emoji__upload'>
+ <button className='btn btn-primary'>
+ <FormattedMessage
+ id='add_emoji.image.button'
+ defaultMessage='Select'
+ />
+ </button>
+ <input
+ type='file'
+ accept='.jpg,.png,.gif'
+ multiple={false}
+ onChange={this.updateImage}
+ />
+ </div>
+ {filename}
+ <div className='form__help'>
+ <FormattedMessage
+ id='add_emoji.image.help'
+ defaultMessage='Choose the image for your emoji. The image can be a gif, png, or jpeg file with a max size of 64 KB and dimensions up to 128 by 128 pixels.'
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ {preview}
+ <div className='backstage-form__footer'>
+ <FormError error={this.state.error}/>
+ <Link
+ className='btn btn-sm'
+ to={'/' + this.props.team.name + '/emoji'}
+ >
+ <FormattedMessage
+ id='add_emoji.cancel'
+ defaultMessage='Cancel'
+ />
+ </Link>
+ <SpinnerButton
+ className='btn btn-primary'
+ type='submit'
+ spinning={this.state.saving}
+ onClick={this.handleSubmit}
+ >
+ <FormattedMessage
+ id='add_emoji.save'
+ defaultMessage='Save'
+ />
+ </SpinnerButton>
+ </div>
+ </form>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/emoji/components/emoji_list.jsx b/webapp/components/emoji/components/emoji_list.jsx
new file mode 100644
index 000000000..5795a57b2
--- /dev/null
+++ b/webapp/components/emoji/components/emoji_list.jsx
@@ -0,0 +1,218 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import * as AsyncClient from 'utils/async_client.jsx';
+import EmojiStore from 'stores/emoji_store.jsx';
+import TeamStore from 'stores/team_store.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+import {FormattedMessage} from 'react-intl';
+import EmojiListItem from './emoji_list_item.jsx';
+import {Link} from 'react-router';
+import LoadingScreen from 'components/loading_screen.jsx';
+
+export default class EmojiList extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired,
+ user: React.propTypes.object.isRequired
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.canCreateEmojis = this.canCreateEmojis.bind(this);
+
+ this.handleEmojiChange = this.handleEmojiChange.bind(this);
+
+ this.deleteEmoji = this.deleteEmoji.bind(this);
+
+ this.updateFilter = this.updateFilter.bind(this);
+
+ this.state = {
+ emojis: EmojiStore.getCustomEmojiMap(),
+ loading: !EmojiStore.hasReceivedCustomEmojis(),
+ filter: ''
+ };
+ }
+
+ componentDidMount() {
+ EmojiStore.addChangeListener(this.handleEmojiChange);
+
+ if (window.mm_config.EnableCustomEmoji === 'true') {
+ AsyncClient.listEmoji();
+ }
+ }
+
+ componentWillUnmount() {
+ EmojiStore.removeChangeListener(this.handleEmojiChange);
+ }
+
+ handleEmojiChange() {
+ this.setState({
+ emojis: EmojiStore.getCustomEmojiMap(),
+ loading: !EmojiStore.hasReceivedCustomEmojis()
+ });
+ }
+
+ updateFilter(e) {
+ this.setState({
+ filter: e.target.value
+ });
+ }
+
+ deleteEmoji(emoji) {
+ AsyncClient.deleteEmoji(emoji.id);
+ }
+
+ canCreateEmojis() {
+ if (global.window.mm_license.IsLicensed !== 'true') {
+ return true;
+ }
+
+ if (Utils.isSystemAdmin(this.props.user.roles)) {
+ return true;
+ }
+
+ if (window.mm_config.RestrictCustomEmojiCreation === 'all') {
+ return true;
+ }
+
+ if (window.mm_config.RestrictCustomEmojiCreation === 'admin') {
+ // check whether the user is an admin on any of their teams
+ for (const member of TeamStore.getTeamMembers()) {
+ if (Utils.isAdmin(member.roles)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ render() {
+ const filter = this.state.filter.toLowerCase();
+ const isSystemAdmin = Utils.isSystemAdmin(this.props.user.roles);
+
+ let emojis = [];
+ if (this.state.loading) {
+ emojis.push(
+ <LoadingScreen key='loading'/>
+ );
+ } else if (this.state.emojis.length === 0) {
+ emojis.push(
+ <tr className='backstage-list__item backstage-list__empty'>
+ <td colSpan='4'>
+ <FormattedMessage
+ id='emoji_list.empty'
+ defaultMessage='No custom emoji found'
+ />
+ </td>
+ </tr>
+ );
+ } else {
+ for (const [, emoji] of this.state.emojis) {
+ let onDelete = null;
+ if (isSystemAdmin || this.props.user.id === emoji.creator_id) {
+ onDelete = this.deleteEmoji;
+ }
+
+ emojis.push(
+ <EmojiListItem
+ key={emoji.id}
+ emoji={emoji}
+ onDelete={onDelete}
+ filter={filter}
+ />
+ );
+ }
+ }
+
+ let addLink = null;
+ if (this.canCreateEmojis()) {
+ addLink = (
+ <Link
+ className='add-link'
+ to={'/' + this.props.team.name + '/emoji/add'}
+ >
+ <button
+ type='button'
+ className='btn btn-primary'
+ >
+ <FormattedMessage
+ id='emoji_list.add'
+ defaultMessage='Add Custom Emoji'
+ />
+ </button>
+ </Link>
+ );
+ }
+
+ return (
+ <div className='backstage-content emoji-list'>
+ <div className='backstage-header'>
+ <h1>
+ <FormattedMessage
+ id='emoji_list.header'
+ defaultMessage='Custom Emoji'
+ />
+ </h1>
+ {addLink}
+ </div>
+ <div className='backstage-filters'>
+ <div className='backstage-filter__search'>
+ <i className='fa fa-search'></i>
+ <input
+ type='search'
+ className='form-control'
+ placeholder={Utils.localizeMessage('emoji_list.search', 'Search Custom Emoji')}
+ value={this.state.filter}
+ onChange={this.updateFilter}
+ style={{flexGrow: 0, flexShrink: 0}}
+ />
+ </div>
+ </div>
+ <span className='emoji-list__help'>
+ <FormattedMessage
+ id='emoji_list.help'
+ defaultMessage='Custom emoji are available to everyone on your server and will show up in the emoji autocomplete menu.'
+ />
+ </span>
+ <div className='backstage-list'>
+ <table className='emoji-list__table'>
+ <tr className='backstage-list__item emoji-list__table-header'>
+ <th className='emoji-list__name'>
+ <FormattedMessage
+ id='emoji_list.name'
+ defaultMessage='Name'
+ />
+ </th>
+ <th className='emoji-list__image'>
+ <FormattedMessage
+ id='emoji_list.image'
+ defaultMessage='Image'
+ />
+ </th>
+ <th className='emoji-list__creator'>
+ <FormattedMessage
+ id='emoji_list.creator'
+ defaultMessage='Creator'
+ />
+ </th>
+ <th className='emoji-list_actions'>
+ <FormattedMessage
+ id='emoji_list.actions'
+ defaultMessage='Actions'
+ />
+ </th>
+ </tr>
+ {emojis}
+ </table>
+ </div>
+ </div>
+ );
+ }
+}
diff --git a/webapp/components/emoji/components/emoji_list_item.jsx b/webapp/components/emoji/components/emoji_list_item.jsx
new file mode 100644
index 000000000..50a4bacb1
--- /dev/null
+++ b/webapp/components/emoji/components/emoji_list_item.jsx
@@ -0,0 +1,118 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import EmojiStore from 'stores/emoji_store.jsx';
+import UserStore from 'stores/user_store.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+import {FormattedMessage} from 'react-intl';
+
+export default class EmojiListItem extends React.Component {
+ static get propTypes() {
+ return {
+ emoji: React.PropTypes.object.isRequired,
+ onDelete: React.PropTypes.func.isRequired,
+ filter: React.PropTypes.string
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.handleDelete = this.handleDelete.bind(this);
+
+ this.state = {
+ creator: UserStore.getProfile(this.props.emoji.creator_id)
+ };
+ }
+
+ handleDelete(e) {
+ e.preventDefault();
+
+ this.props.onDelete(this.props.emoji);
+ }
+
+ matchesFilter(emoji, creator, filter) {
+ if (!filter) {
+ return true;
+ }
+
+ if (emoji.name.toLowerCase().indexOf(filter) !== -1) {
+ return true;
+ }
+
+ if (creator) {
+ if (creator.username.toLowerCase().indexOf(filter) !== -1 ||
+ (creator.first_name && creator.first_name.toLowerCase().indexOf(filter)) ||
+ (creator.last_name && creator.last_name.toLowerCase().indexOf(filter)) ||
+ (creator.nickname && creator.nickname.toLowerCase().indexOf(filter))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ render() {
+ const emoji = this.props.emoji;
+ const creator = this.state.creator;
+ const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
+
+ if (!this.matchesFilter(emoji, creator, filter)) {
+ return null;
+ }
+
+ let creatorName;
+ if (creator) {
+ creatorName = Utils.displayUsernameForUser(creator);
+
+ if (creatorName !== creator.username) {
+ creatorName += ' (@' + creator.username + ')';
+ }
+ } else {
+ creatorName = (
+ <FormattedMessage
+ id='emoji_list.somebody'
+ defaultMessage='Somebody on another team'
+ />
+ );
+ }
+
+ let deleteButton = null;
+ if (this.props.onDelete) {
+ deleteButton = (
+ <a
+ href='#'
+ onClick={this.handleDelete}
+ >
+ <FormattedMessage
+ id='emoji_list.delete'
+ defaultMessage='Delete'
+ />
+ </a>
+ );
+ }
+
+ return (
+ <tr className='backstage-list__item'>
+ <td className='emoji-list__name'>
+ {':' + emoji.name + ':'}
+ </td>
+ <td className='emoji-list__image'>
+ <img
+ className='emoticon'
+ src={EmojiStore.getEmojiImageUrl(emoji)}
+ />
+ </td>
+ <td className='emoji-list__creator'>
+ {creatorName}
+ </td>
+ <td className='emoji-list-item_actions'>
+ {deleteButton}
+ </td>
+ </tr>
+ );
+ }
+}
diff --git a/webapp/components/backstage/add_command.jsx b/webapp/components/integrations/components/add_command.jsx
index 91af0416b..e72670e47 100644
--- a/webapp/components/backstage/add_command.jsx
+++ b/webapp/components/integrations/components/add_command.jsx
@@ -6,7 +6,7 @@ import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
import * as Utils from 'utils/utils.jsx';
-import BackstageHeader from './backstage_header.jsx';
+import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
import {FormattedMessage} from 'react-intl';
import FormError from 'components/form_error.jsx';
import {browserHistory, Link} from 'react-router/es6';
@@ -17,6 +17,12 @@ const REQUEST_POST = 'P';
const REQUEST_GET = 'G';
export default class AddCommand extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -155,7 +161,7 @@ export default class AddCommand extends React.Component {
AsyncClient.addCommand(
command,
() => {
- browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands');
+ browserHistory.push('/' + this.props.team.name + '/integrations/commands');
},
(err) => {
this.setState({
@@ -300,7 +306,7 @@ export default class AddCommand extends React.Component {
return (
<div className='backstage-content row'>
<BackstageHeader>
- <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}>
+ <Link to={'/' + this.props.team.name + '/integrations/commands'}>
<FormattedMessage
id='installed_command.header'
defaultMessage='Slash Commands'
@@ -312,7 +318,10 @@ export default class AddCommand extends React.Component {
/>
</BackstageHeader>
<div className='backstage-form'>
- <form className='form-horizontal'>
+ <form
+ className='form-horizontal'
+ onSubmit={this.handleSubmit}
+ >
<div className='form-group'>
<label
className='control-label col-sm-4'
@@ -531,7 +540,7 @@ export default class AddCommand extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
- to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
+ to={'/' + this.props.team.name + '/integrations/commands'}
>
<FormattedMessage
id='add_command.cancel'
diff --git a/webapp/components/backstage/add_incoming_webhook.jsx b/webapp/components/integrations/components/add_incoming_webhook.jsx
index 528f03377..122600c90 100644
--- a/webapp/components/backstage/add_incoming_webhook.jsx
+++ b/webapp/components/integrations/components/add_incoming_webhook.jsx
@@ -4,9 +4,8 @@
import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Utils from 'utils/utils.jsx';
-import BackstageHeader from './backstage_header.jsx';
+import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
import {FormattedMessage} from 'react-intl';
import FormError from 'components/form_error.jsx';
@@ -14,6 +13,12 @@ import {browserHistory, Link} from 'react-router/es6';
import SpinnerButton from 'components/spinner_button.jsx';
export default class AddIncomingWebhook extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -69,7 +74,7 @@ export default class AddIncomingWebhook extends React.Component {
AsyncClient.addIncomingHook(
hook,
() => {
- browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks');
+ browserHistory.push('/' + this.props.team.name + '/integrations/incoming_webhooks');
},
(err) => {
this.setState({
@@ -102,7 +107,7 @@ export default class AddIncomingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
- <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}>
+ <Link to={'/' + this.props.team.name + '/integrations/incoming_webhooks'}>
<FormattedMessage
id='installed_incoming_webhooks.header'
defaultMessage='Incoming Webhooks'
@@ -114,7 +119,10 @@ export default class AddIncomingWebhook extends React.Component {
/>
</BackstageHeader>
<div className='backstage-form'>
- <form className='form-horizontal'>
+ <form
+ className='form-horizontal'
+ onSubmit={this.handleSubmit}
+ >
<div className='form-group'>
<label
className='control-label col-sm-4'
@@ -181,7 +189,7 @@ export default class AddIncomingWebhook extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
- to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}
+ to={'/' + this.props.team.name + '/integrations/incoming_webhooks'}
>
<FormattedMessage
id='add_incoming_webhook.cancel'
diff --git a/webapp/components/backstage/add_outgoing_webhook.jsx b/webapp/components/integrations/components/add_outgoing_webhook.jsx
index 5f9d96249..bd49fedc9 100644
--- a/webapp/components/backstage/add_outgoing_webhook.jsx
+++ b/webapp/components/integrations/components/add_outgoing_webhook.jsx
@@ -4,9 +4,8 @@
import React from 'react';
import * as AsyncClient from 'utils/async_client.jsx';
-import * as Utils from 'utils/utils.jsx';
-import BackstageHeader from './backstage_header.jsx';
+import BackstageHeader from 'components/backstage/components/backstage_header.jsx';
import ChannelSelect from 'components/channel_select.jsx';
import {FormattedMessage} from 'react-intl';
import FormError from 'components/form_error.jsx';
@@ -14,6 +13,12 @@ import {browserHistory, Link} from 'react-router/es6';
import SpinnerButton from 'components/spinner_button.jsx';
export default class AddOutgoingWebhook extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -112,7 +117,7 @@ export default class AddOutgoingWebhook extends React.Component {
AsyncClient.addOutgoingHook(
hook,
() => {
- browserHistory.push('/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks');
+ browserHistory.push('/' + this.props.team.name + '/integrations/outgoing_webhooks');
},
(err) => {
this.setState({
@@ -165,7 +170,7 @@ export default class AddOutgoingWebhook extends React.Component {
return (
<div className='backstage-content'>
<BackstageHeader>
- <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}>
+ <Link to={'/' + this.props.team.name + '/integrations/outgoing_webhooks'}>
<FormattedMessage
id='installed_outgoing_webhooks.header'
defaultMessage='Outgoing Webhooks'
@@ -177,7 +182,10 @@ export default class AddOutgoingWebhook extends React.Component {
/>
</BackstageHeader>
<div className='backstage-form'>
- <form className='form-horizontal'>
+ <form
+ className='form-horizontal'
+ onSubmit={this.handleSubmit}
+ >
<div className='form-group'>
<label
className='control-label col-sm-4'
@@ -314,7 +322,7 @@ export default class AddOutgoingWebhook extends React.Component {
<FormError errors={[this.state.serverError, this.state.clientError]}/>
<Link
className='btn btn-sm'
- to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
+ to={'/' + this.props.team.name + '/integrations/outgoing_webhooks'}
>
<FormattedMessage
id='add_outgoing_webhook.cancel'
diff --git a/webapp/components/backstage/installed_command.jsx b/webapp/components/integrations/components/installed_command.jsx
index 88f43f674..658126f19 100644
--- a/webapp/components/backstage/installed_command.jsx
+++ b/webapp/components/integrations/components/installed_command.jsx
@@ -50,8 +50,9 @@ export default class InstalledCommand extends React.Component {
render() {
const command = this.props.command;
+ const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
- if (!this.matchesFilter(command, this.props.filter)) {
+ if (!this.matchesFilter(command, filter)) {
return null;
}
@@ -61,7 +62,7 @@ export default class InstalledCommand extends React.Component {
} else {
name = (
<FormattedMessage
- id='installed_integraions.unnamed_command'
+ id='installed_commands.unnamed_command'
defaultMessage='Unnamed Slash Command'
/>
);
diff --git a/webapp/components/backstage/installed_commands.jsx b/webapp/components/integrations/components/installed_commands.jsx
index df1f56687..597ba7005 100644
--- a/webapp/components/backstage/installed_commands.jsx
+++ b/webapp/components/integrations/components/installed_commands.jsx
@@ -8,11 +8,17 @@ import IntegrationStore from 'stores/integration_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import BackstageList from 'components/backstage/components/backstage_list.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledCommand from './installed_command.jsx';
-import InstalledIntegrations from './installed_integrations.jsx';
export default class InstalledCommands extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -71,7 +77,7 @@ export default class InstalledCommands extends React.Component {
});
return (
- <InstalledIntegrations
+ <BackstageList
header={
<FormattedMessage
id='installed_commands.header'
@@ -84,17 +90,18 @@ export default class InstalledCommands extends React.Component {
defaultMessage='Add Slash Command'
/>
}
- addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands/add'}
+ addLink={'/' + this.props.team.name + '/integrations/commands/add'}
emptyText={
<FormattedMessage
id='installed_commands.empty'
defaultMessage='No slash commands found'
/>
}
+ searchPlaceholder={Utils.localizeMessage('installed_commands.search', 'Search Slash Commands')}
loading={this.state.loading}
>
{commands}
- </InstalledIntegrations>
+ </BackstageList>
);
}
}
diff --git a/webapp/components/backstage/installed_incoming_webhook.jsx b/webapp/components/integrations/components/installed_incoming_webhook.jsx
index afa6e9958..2cf3f24b8 100644
--- a/webapp/components/backstage/installed_incoming_webhook.jsx
+++ b/webapp/components/integrations/components/installed_incoming_webhook.jsx
@@ -51,8 +51,9 @@ export default class InstalledIncomingWebhook extends React.Component {
render() {
const incomingWebhook = this.props.incomingWebhook;
const channel = ChannelStore.get(incomingWebhook.channel_id);
+ const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
- if (!this.matchesFilter(incomingWebhook, channel, this.props.filter)) {
+ if (!this.matchesFilter(incomingWebhook, channel, filter)) {
return null;
}
diff --git a/webapp/components/backstage/installed_incoming_webhooks.jsx b/webapp/components/integrations/components/installed_incoming_webhooks.jsx
index 0a38a6ab5..a3bcf904e 100644
--- a/webapp/components/backstage/installed_incoming_webhooks.jsx
+++ b/webapp/components/integrations/components/installed_incoming_webhooks.jsx
@@ -8,11 +8,17 @@ import IntegrationStore from 'stores/integration_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import BackstageList from 'components/backstage/components/backstage_list.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledIncomingWebhook from './installed_incoming_webhook.jsx';
-import InstalledIntegrations from './installed_integrations.jsx';
export default class InstalledIncomingWebhooks extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -65,7 +71,7 @@ export default class InstalledIncomingWebhooks extends React.Component {
});
return (
- <InstalledIntegrations
+ <BackstageList
header={
<FormattedMessage
id='installed_incoming_webhooks.header'
@@ -78,17 +84,18 @@ export default class InstalledIncomingWebhooks extends React.Component {
defaultMessage='Add Incoming Webhook'
/>
}
- addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks/add'}
+ addLink={'/' + this.props.team.name + '/integrations/incoming_webhooks/add'}
emptyText={
<FormattedMessage
id='installed_incoming_webhooks.empty'
defaultMessage='No incoming webhooks found'
/>
}
+ searchPlaceholder={Utils.localizeMessage('installed_incoming_webhooks.search', 'Search Incoming Webhooks')}
loading={this.state.loading}
>
{incomingWebhooks}
- </InstalledIntegrations>
+ </BackstageList>
);
}
}
diff --git a/webapp/components/backstage/installed_outgoing_webhook.jsx b/webapp/components/integrations/components/installed_outgoing_webhook.jsx
index 99f2439ec..852231823 100644
--- a/webapp/components/backstage/installed_outgoing_webhook.jsx
+++ b/webapp/components/integrations/components/installed_outgoing_webhook.jsx
@@ -65,8 +65,9 @@ export default class InstalledOutgoingWebhook extends React.Component {
render() {
const outgoingWebhook = this.props.outgoingWebhook;
const channel = ChannelStore.get(outgoingWebhook.channel_id);
+ const filter = this.props.filter ? this.props.filter.toLowerCase() : '';
- if (!this.matchesFilter(outgoingWebhook, channel, this.props.filter)) {
+ if (!this.matchesFilter(outgoingWebhook, channel, filter)) {
return null;
}
diff --git a/webapp/components/backstage/installed_outgoing_webhooks.jsx b/webapp/components/integrations/components/installed_outgoing_webhooks.jsx
index b79bc3530..ebc9a6fc1 100644
--- a/webapp/components/backstage/installed_outgoing_webhooks.jsx
+++ b/webapp/components/integrations/components/installed_outgoing_webhooks.jsx
@@ -8,11 +8,17 @@ import IntegrationStore from 'stores/integration_store.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
+import BackstageList from 'components/backstage/components/backstage_list.jsx';
import {FormattedMessage} from 'react-intl';
import InstalledOutgoingWebhook from './installed_outgoing_webhook.jsx';
-import InstalledIntegrations from './installed_integrations.jsx';
export default class InstalledOutgoingWebhooks extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
constructor(props) {
super(props);
@@ -71,7 +77,7 @@ export default class InstalledOutgoingWebhooks extends React.Component {
});
return (
- <InstalledIntegrations
+ <BackstageList
header={
<FormattedMessage
id='installed_outgoing_webhooks.header'
@@ -84,17 +90,18 @@ export default class InstalledOutgoingWebhooks extends React.Component {
defaultMessage='Add Outgoing Webhook'
/>
}
- addLink={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks/add'}
+ addLink={'/' + this.props.team.name + '/integrations/outgoing_webhooks/add'}
emptyText={
<FormattedMessage
id='installed_outgoing_webhooks.empty'
defaultMessage='No outgoing webhooks found'
/>
}
+ searchPlaceholder={Utils.localizeMessage('installed_outgoing_webhooks.search', 'Search Outgoing Webhooks')}
loading={this.state.loading}
>
{outgoingWebhooks}
- </InstalledIntegrations>
+ </BackstageList>
);
}
}
diff --git a/webapp/components/backstage/integration_option.jsx b/webapp/components/integrations/components/integration_option.jsx
index 483e6a888..483e6a888 100644
--- a/webapp/components/backstage/integration_option.jsx
+++ b/webapp/components/integrations/components/integration_option.jsx
diff --git a/webapp/components/backstage/integrations.jsx b/webapp/components/integrations/components/integrations.jsx
index fdd75026a..7894ced5d 100644
--- a/webapp/components/backstage/integrations.jsx
+++ b/webapp/components/integrations/components/integrations.jsx
@@ -5,11 +5,16 @@ import React from 'react';
import {FormattedMessage} from 'react-intl';
import IntegrationOption from './integration_option.jsx';
-import * as Utils from 'utils/utils.jsx';
import WebhookIcon from 'images/webhook_icon.jpg';
export default class Integrations extends React.Component {
+ static get propTypes() {
+ return {
+ team: React.propTypes.object.isRequired
+ };
+ }
+
render() {
const options = [];
@@ -30,7 +35,7 @@ export default class Integrations extends React.Component {
defaultMessage='Incoming webhooks allow external integrations to send messages'
/>
}
- link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/incoming_webhooks'}
+ link={'/' + this.props.team.name + '/integrations/incoming_webhooks'}
/>
);
}
@@ -52,7 +57,7 @@ export default class Integrations extends React.Component {
defaultMessage='Outgoing webhooks allow external integrations to receive and respond to messages'
/>
}
- link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/outgoing_webhooks'}
+ link={'/' + this.props.team.name + '/integrations/outgoing_webhooks'}
/>
);
}
@@ -74,7 +79,7 @@ export default class Integrations extends React.Component {
defaultMessage='Slash commands send events to an external integration'
/>
}
- link={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations/commands'}
+ link={'/' + this.props.team.name + '/integrations/commands'}
/>
);
}
diff --git a/webapp/components/logged_in.jsx b/webapp/components/logged_in.jsx
index 6752b56cd..484164e56 100644
--- a/webapp/components/logged_in.jsx
+++ b/webapp/components/logged_in.jsx
@@ -156,6 +156,11 @@ export default class LoggedIn extends React.Component {
e.preventDefault();
}
});
+
+ // Get custom emoji from the server
+ if (window.mm_config.EnableCustomEmoji === 'true') {
+ AsyncClient.listEmoji();
+ }
}
componentWillUnmount() {
@@ -187,4 +192,4 @@ export default class LoggedIn extends React.Component {
LoggedIn.propTypes = {
children: React.PropTypes.object
-}; \ No newline at end of file
+};
diff --git a/webapp/components/navbar_dropdown.jsx b/webapp/components/navbar_dropdown.jsx
index c3b646e52..4f137979e 100644
--- a/webapp/components/navbar_dropdown.jsx
+++ b/webapp/components/navbar_dropdown.jsx
@@ -85,6 +85,7 @@ export default class NavbarDropdown extends React.Component {
var isSystemAdmin = false;
var teamSettings = null;
let integrationsLink = null;
+ let customEmojiLink = null;
if (currentUser != null) {
isAdmin = TeamStore.isTeamAdminForCurrentTeam() || UserStore.isSystemAdminForCurrentUser();
@@ -166,7 +167,7 @@ export default class NavbarDropdown extends React.Component {
if (integrationsEnabled && (isAdmin || window.mm_config.EnableOnlyAdminIntegrations !== 'true')) {
integrationsLink = (
<li>
- <Link to={'/' + Utils.getTeamNameFromUrl() + '/settings/integrations'}>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/integrations'}>
<FormattedMessage
id='navbar_dropdown.integrations'
defaultMessage='Integrations'
@@ -176,6 +177,19 @@ export default class NavbarDropdown extends React.Component {
);
}
+ if (window.mm_config.EnableCustomEmoji === 'true') {
+ customEmojiLink = (
+ <li>
+ <Link to={'/' + Utils.getTeamNameFromUrl() + '/emoji'}>
+ <FormattedMessage
+ id='navbar_dropdown.emoji'
+ defaultMessage='Custom Emoji'
+ />
+ </Link>
+ </li>
+ );
+ }
+
if (isSystemAdmin) {
sysAdminLink = (
<li>
@@ -327,8 +341,10 @@ export default class NavbarDropdown extends React.Component {
</a>
</li>
<li className='divider'></li>
- {teamSettings}
{integrationsLink}
+ {customEmojiLink}
+ <li className='divider'></li>
+ {teamSettings}
{manageLink}
{sysAdminLink}
{teams}
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index c445ad9f3..6633bd9b9 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -84,6 +84,10 @@ export default class Post extends React.Component {
return true;
}
+ if (nextProps.emojis !== this.props.emojis) {
+ return true;
+ }
+
return false;
}
render() {
@@ -200,6 +204,7 @@ export default class Post extends React.Component {
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
previewCollapsed={this.props.previewCollapsed}
+ emojis={this.props.emojis}
/>
</div>
</div>
@@ -225,5 +230,6 @@ Post.propTypes = {
compactDisplay: React.PropTypes.bool,
previewCollapsed: React.PropTypes.string,
commentCount: React.PropTypes.number,
- useMilitaryTime: React.PropTypes.bool.isRequired
+ useMilitaryTime: React.PropTypes.bool.isRequired,
+ emojis: React.PropTypes.object.isRequired
};
diff --git a/webapp/components/post_view/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index 2a2be75a9..561860b65 100644
--- a/webapp/components/post_view/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -37,6 +37,10 @@ export default class PostBody extends React.Component {
return true;
}
+ if (nextProps.emojis !== this.props.emojis) {
+ return true;
+ }
+
return false;
}
@@ -151,7 +155,7 @@ export default class PostBody extends React.Component {
message = (
<span
onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message)}}
+ dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, {emojis: this.props.emojis})}}
/>
);
}
@@ -199,5 +203,6 @@ PostBody.propTypes = {
retryPost: React.PropTypes.func.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
compactDisplay: React.PropTypes.bool,
- previewCollapsed: React.PropTypes.string
+ previewCollapsed: React.PropTypes.string,
+ emojis: React.PropTypes.object.isRequired
};
diff --git a/webapp/components/post_view/components/post_body_additional_content.jsx b/webapp/components/post_view/components/post_body_additional_content.jsx
index 0ab015ced..a51a557b9 100644
--- a/webapp/components/post_view/components/post_body_additional_content.jsx
+++ b/webapp/components/post_view/components/post_body_additional_content.jsx
@@ -35,6 +35,9 @@ export default class PostBodyAdditionalContent extends React.Component {
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
+ if (!Utils.areObjectsEqual(nextProps.message, this.props.message)) {
+ return true;
+ }
if (nextState.embedVisible !== this.state.embedVisible) {
return true;
}
diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx
index 288a2d5e0..bcd763d58 100644
--- a/webapp/components/post_view/components/post_list.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -265,6 +265,7 @@ export default class PostList extends React.Component {
compactDisplay={this.props.compactDisplay}
previewCollapsed={this.props.previewsCollapsed}
useMilitaryTime={this.props.useMilitaryTime}
+ emojis={this.props.emojis}
/>
);
@@ -527,5 +528,6 @@ PostList.propTypes = {
compactDisplay: React.PropTypes.bool,
previewsCollapsed: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired,
- isFocusPost: React.PropTypes.bool
+ isFocusPost: React.PropTypes.bool,
+ emojis: React.PropTypes.object.isRequired
};
diff --git a/webapp/components/post_view/post_focus_view_controller.jsx b/webapp/components/post_view/post_focus_view_controller.jsx
index c70ebb0f5..f8738e056 100644
--- a/webapp/components/post_view/post_focus_view_controller.jsx
+++ b/webapp/components/post_view/post_focus_view_controller.jsx
@@ -4,6 +4,7 @@
import PostList from './components/post_list.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
+import EmojiStore from 'stores/emoji_store.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
@@ -20,6 +21,7 @@ export default class PostFocusView extends React.Component {
this.onChannelChange = this.onChannelChange.bind(this);
this.onPostsChange = this.onPostsChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
+ this.onEmojiChange = this.onEmojiChange.bind(this);
this.onPostListScroll = this.onPostListScroll.bind(this);
const focusedPostId = PostStore.getFocusedPostId();
@@ -38,7 +40,8 @@ export default class PostFocusView extends React.Component {
currentChannel: ChannelStore.getCurrentId().slice(),
scrollPostId: focusedPostId,
atTop: PostStore.getVisibilityAtTop(focusedPostId),
- atBottom: PostStore.getVisibilityAtBottom(focusedPostId)
+ atBottom: PostStore.getVisibilityAtBottom(focusedPostId),
+ emojis: EmojiStore.getEmojis()
};
}
@@ -46,12 +49,14 @@ export default class PostFocusView extends React.Component {
ChannelStore.addChangeListener(this.onChannelChange);
PostStore.addChangeListener(this.onPostsChange);
UserStore.addChangeListener(this.onUserChange);
+ EmojiStore.addChangeListener(this.onEmojiChange);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChannelChange);
PostStore.removeChangeListener(this.onPostsChange);
UserStore.removeChangeListener(this.onUserChange);
+ EmojiStore.removeChangeListener(this.onEmojiChange);
}
onChannelChange() {
@@ -87,6 +92,12 @@ export default class PostFocusView extends React.Component {
this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))});
}
+ onEmojiChange() {
+ this.setState({
+ emojis: EmojiStore.getEmojis()
+ });
+ }
+
onPostListScroll() {
this.setState({scrollType: ScrollTypes.FREE});
}
@@ -116,6 +127,7 @@ export default class PostFocusView extends React.Component {
showMoreMessagesBottom={!this.state.atBottom}
postsToHighlight={postsToHighlight}
isFocusPost={true}
+ emojis={this.state.emojis}
/>
);
}
diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx
index 6d724f659..17c3e94ae 100644
--- a/webapp/components/post_view/post_view_controller.jsx
+++ b/webapp/components/post_view/post_view_controller.jsx
@@ -4,6 +4,7 @@
import PostList from './components/post_list.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
+import EmojiStore from 'stores/emoji_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PostStore from 'stores/post_store.jsx';
@@ -24,6 +25,7 @@ export default class PostViewController extends React.Component {
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
this.onPostsChange = this.onPostsChange.bind(this);
+ this.onEmojisChange = this.onEmojisChange.bind(this);
this.onPostsViewJumpRequest = this.onPostsViewJumpRequest.bind(this);
this.onPostListScroll = this.onPostListScroll.bind(this);
this.onActivate = this.onActivate.bind(this);
@@ -53,7 +55,8 @@ export default class PostViewController extends React.Component {
displayPostsInCenter: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED,
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
previewsCollapsed: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false'),
- useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false)
+ useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false),
+ emojis: EmojiStore.getEmojis()
};
}
@@ -102,11 +105,18 @@ export default class PostViewController extends React.Component {
});
}
+ onEmojisChange() {
+ this.setState({
+ emojis: EmojiStore.getEmojis()
+ });
+ }
+
onActivate() {
PreferenceStore.addChangeListener(this.onPreferenceChange);
UserStore.addChangeListener(this.onUserChange);
PostStore.addChangeListener(this.onPostsChange);
PostStore.addPostsViewJumpListener(this.onPostsViewJumpRequest);
+ EmojiStore.addChangeListener(this.onEmojisChange);
}
onDeactivate() {
@@ -114,6 +124,7 @@ export default class PostViewController extends React.Component {
UserStore.removeChangeListener(this.onUserChange);
PostStore.removeChangeListener(this.onPostsChange);
PostStore.removePostsViewJumpListener(this.onPostsViewJumpRequest);
+ EmojiStore.removeChangeListener(this.onEmojisChange);
}
componentWillReceiveProps(nextProps) {
@@ -265,6 +276,7 @@ export default class PostViewController extends React.Component {
previewsCollapsed={this.state.previewsCollapsed}
useMilitaryTime={this.state.useMilitaryTime}
lastViewed={this.state.lastViewed}
+ emojis={this.state.emojis}
/>
);
}
diff --git a/webapp/components/root.jsx b/webapp/components/root.jsx
index 977652e99..f98ed4c3d 100644
--- a/webapp/components/root.jsx
+++ b/webapp/components/root.jsx
@@ -1,9 +1,6 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-//import $ from 'jquery';
-//import Client from 'utils/web_client.jsx';
-
import * as GlobalActions from 'actions/global_actions.jsx';
import LocalizationStore from 'stores/localization_store.jsx';
import Client from 'utils/web_client.jsx';
diff --git a/webapp/components/suggestion/emoticon_provider.jsx b/webapp/components/suggestion/emoticon_provider.jsx
index 7a45c5b4e..af8cac070 100644
--- a/webapp/components/suggestion/emoticon_provider.jsx
+++ b/webapp/components/suggestion/emoticon_provider.jsx
@@ -3,6 +3,7 @@
import React from 'react';
+import EmojiStore from 'stores/emoji_store.jsx';
import * as Emoticons from 'utils/emoticons.jsx';
import SuggestionStore from 'stores/suggestion_store.jsx';
@@ -29,7 +30,7 @@ class EmoticonSuggestion extends Suggestion {
<img
alt={text}
className='emoticon-suggestion__image'
- src={emoticon.path}
+ src={EmojiStore.getEmojiImageUrl(emoticon)}
title={text}
/>
</div>
@@ -53,21 +54,19 @@ export default class EmoticonProvider {
const matched = [];
- const emoticons = Emoticons.getEmoticonsByName();
-
// check for text emoticons
for (const emoticon of Object.keys(Emoticons.emoticonPatterns)) {
if (Emoticons.emoticonPatterns[emoticon].test(text)) {
- SuggestionStore.addSuggestion(suggestionId, text, emoticons.get(emoticon), EmoticonSuggestion, text);
+ SuggestionStore.addSuggestion(suggestionId, text, EmojiStore.get(emoticon), EmoticonSuggestion, text);
hasSuggestions = true;
}
}
- // checked for named emoji
- for (const [name, emoticon] of emoticons) {
+ // check for named emoji
+ for (const [name, emoji] of EmojiStore.getEmojis()) {
if (name.indexOf(partialName) !== -1) {
- matched.push(emoticon);
+ matched.push(emoji);
if (matched.length >= MAX_EMOTICON_SUGGESTIONS) {
break;
@@ -77,11 +76,11 @@ export default class EmoticonProvider {
// sort the emoticons so that emoticons starting with the entered text come first
matched.sort((a, b) => {
- const aPrefix = a.alias.startsWith(partialName);
- const bPrefix = b.alias.startsWith(partialName);
+ const aPrefix = a.name.startsWith(partialName);
+ const bPrefix = b.name.startsWith(partialName);
if (aPrefix === bPrefix) {
- return a.alias.localeCompare(b.alias);
+ return a.name.localeCompare(b.name);
} else if (aPrefix) {
return -1;
}
@@ -89,7 +88,7 @@ export default class EmoticonProvider {
return 1;
});
- const terms = matched.map((emoticon) => ':' + emoticon.alias + ':');
+ const terms = matched.map((emoticon) => ':' + emoticon.name + ':');
if (terms.length > 0) {
SuggestionStore.addSuggestions(suggestionId, terms, matched, EmoticonSuggestion, text);
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 574ea6ecc..afe32ae0e 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -64,6 +64,20 @@
"add_command.username": "Response Username",
"add_command.username.help": "Choose a username override for responses for this slash command. Usernames can consist of up to 22 characters consisting of lowercase letters, numbers and they symbols \"-\", \"_\", and \".\" .",
"add_command.username.placeholder": "Username",
+ "add_emoji.cancel": "Cancel",
+ "add_emoji.header": "Add",
+ "add_emoji.image": "Image",
+ "add_emoji.image.button": "Select",
+ "add_emoji.image.help": "Choose the image for your emoji. The image can be a gif, png, or jpeg file with a max size of 64 KB and dimensions up to 128 by 128 pixels.",
+ "add_emoji.imageRequired": "An image is required for the emoji",
+ "add_emoji.name": "Name",
+ "add_emoji.name.help": "Choose a name for your emoji made of up to 64 characters consisting of lowercase letters, numbers, and the symbols '-' and '_'.",
+ "add_emoji.nameRequired": "A name is required for the emoji",
+ "add_emoji.nameInvalid": "An emoji's name can only contain numbers, letters, and the symbols '-' and '_'.",
+ "add_emoji.nameTaken": "This name is already in use by a system emoji. Please choose another name.",
+ "add_emoji.preview": "Preview",
+ "add_emoji.preview.sentence": "This is a sentence with {image} in it.",
+ "add_emoji.save": "Save",
"add_incoming_webhook.cancel": "Cancel",
"add_incoming_webhook.channel": "Channel",
"add_incoming_webhook.channelRequired": "A valid channel is required",
@@ -137,6 +151,7 @@
"admin.customization.enableCustomEmojiTitle": "Enable Custom Emoji:",
"admin.customization.restrictCustomEmojiCreationAll": "Allow everyone to create custom emoji",
"admin.customization.restrictCustomEmojiCreationDesc": "Restrict the creation of custom emoji to certain users.",
+ "admin.customization.restrictCustomEmojiCreationAdmin": "Allow system and team admins to create custom emoji",
"admin.customization.restrictCustomEmojiCreationSystemAdmin": "Only allow system admins to create custom emoji",
"admin.customization.restrictCustomEmojiCreationTitle": "Restrict Custom Emoji Creation:",
"admin.customization.support": "Legal and Support",
@@ -701,6 +716,7 @@
"authorize.app": "The app <strong>{appName}</strong> would like the ability to access and modify your basic information.",
"authorize.deny": "Deny",
"authorize.title": "An application would like to connect to your {teamName} account",
+ "backstage_list.search": "Search",
"backstage_navbar.backToMattermost": "Back to {siteName}",
"backstage_sidebar.integrations": "Integrations",
"backstage_sidebar.integrations.commands": "Slash Commands",
@@ -858,6 +874,9 @@
"create_team.team_url.teamUrl": "Team URL",
"create_team.team_url.unavailable": "This URL is unavailable. Please try another.",
"create_team.team_url.webAddress": "Choose the web address of your new team:",
+ "custom_emoji.empty": "No custom emoji found",
+ "custom_emoji.header": "Custom Emoji",
+ "custom_emoji.search": "Search Custom Emoji",
"delete_channel.cancel": "Cancel",
"delete_channel.channel": "channel",
"delete_channel.confirm": "Confirm DELETE Channel",
@@ -901,6 +920,15 @@
"email_verify.verified": "{siteName} Email Verified",
"email_verify.verifiedBody": "<p>Your email has been verified! Click <a href={url}>here</a> to log in.</p>",
"email_verify.verifyFailed": "Failed to verify your email.",
+ "emoji_list.actions": "Actions",
+ "emoji_list.add": "Add Custom Emoji",
+ "emoji_list.creator": "Creator",
+ "emoji_list.delete": "Delete",
+ "emoji_list.empty": "No Custom Emoji Found",
+ "emoji_list.image": "Image",
+ "emoji_list.name": "Name",
+ "emoji_list.search": "Search Custom Emoji",
+ "emoji_list.somebody": "Somebody on another team",
"error.not_found.link_message": "Back to Mattermost",
"error.not_found.message": "The page you were trying to reach does not exist",
"error.not_found.title": "Page not found",
@@ -960,23 +988,25 @@
"installed_commands.add": "Add Slash Command",
"installed_commands.empty": "No commands found",
"installed_commands.header": "Slash Commands",
+ "installed_commands.search": "Search Slash Commands",
+ "installed_commands.unnamed_command": "Unnamed Slash Command",
"installed_incoming_webhooks.add": "Add Incoming Webhook",
"installed_incoming_webhooks.empty": "No incoming webhooks found",
"installed_incoming_webhooks.header": "Incoming Webhooks",
+ "installed_incoming_webhooks.search": "Search Incoming Webhooks",
"installed_incoming_webhooks.unknown_channel": "A Private Webhook",
- "installed_integraions.unnamed_command": "Unnamed Slash Command",
"installed_integrations.callback_urls": "Callback URLs: {urls}",
"installed_integrations.content_type": "Content-Type: {contentType}",
"installed_integrations.creation": "Created by {creator} on {createAt, date, full}",
"installed_integrations.delete": "Delete",
"installed_integrations.regenToken": "Regenerate Token",
- "installed_integrations.search": "Search Integrations",
"installed_integrations.token": "Token: {token}",
"installed_integrations.triggerWords": "Trigger Words: {triggerWords}",
"installed_integrations.url": "URL: {url}",
"installed_outgoing_webhooks.add": "Add Outgoing Webhook",
"installed_outgoing_webhooks.empty": "No outgoing webhooks found",
"installed_outgoing_webhooks.header": "Outgoing Webhooks",
+ "installed_outgoing_webhooks.search": "Search Outgoing Webhooks",
"installed_outgoing_webhooks.unknown_channel": "A Private Webhook",
"integrations.command.description": "Slash commands send events to external integrations",
"integrations.command.title": "Slash Command",
@@ -1091,6 +1121,7 @@
"navbar_dropdown.accountSettings": "Account Settings",
"navbar_dropdown.console": "System Console",
"navbar_dropdown.create": "Create a New Team",
+ "navbar_dropdown.emoji": "Custom Emoji",
"navbar_dropdown.help": "Help",
"navbar_dropdown.integrations": "Integrations",
"navbar_dropdown.inviteMember": "Invite New Member",
diff --git a/webapp/package.json b/webapp/package.json
index c3bca0e2e..69d91e345 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -18,7 +18,7 @@
"keymirror": "0.1.1",
"marked": "mattermost/marked#12d2be4cdf54d4ec95fead934e18840b6a2c1a7b",
"match-at": "0.1.0",
- "mattermost": "mattermost/mattermost-javascript#release-3.1",
+ "mattermost": "mattermost/mattermost-javascript#8e4c320d5af653eacb248455d77057a76ec28830",
"object-assign": "4.1.0",
"perfect-scrollbar": "0.6.11",
"react": "15.0.2",
diff --git a/webapp/routes/route_emoji.jsx b/webapp/routes/route_emoji.jsx
new file mode 100644
index 000000000..207c81172
--- /dev/null
+++ b/webapp/routes/route_emoji.jsx
@@ -0,0 +1,24 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as RouteUtils from 'routes/route_utils.jsx';
+
+export default {
+ path: 'emoji',
+ getComponents: (location, callback) => {
+ System.import('components/backstage/backstage_controller.jsx').then(RouteUtils.importComponentSuccess(callback));
+ },
+ indexRoute: {
+ getComponents: (location, callback) => {
+ System.import('components/emoji/components/emoji_list.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ },
+ childRoutes: [
+ {
+ path: 'add',
+ getComponents: (location, callback) => {
+ System.import('components/emoji/components/add_emoji.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ }
+ ]
+};
diff --git a/webapp/routes/route_integrations.jsx b/webapp/routes/route_integrations.jsx
index 6ebd09a72..fdfb5d947 100644
--- a/webapp/routes/route_integrations.jsx
+++ b/webapp/routes/route_integrations.jsx
@@ -2,83 +2,65 @@
// See License.txt for license information.
import * as RouteUtils from 'routes/route_utils.jsx';
-import {Route, IndexRoute, Redirect} from 'react-router/es6';
-import React from 'react';
-import BackstageNavbar from 'components/backstage/backstage_navbar.jsx';
-import BackstageSidebar from 'components/backstage/backstage_sidebar.jsx';
-import Integrations from 'components/backstage/integrations.jsx';
-import InstalledIncomingWebhooks from 'components/backstage/installed_incoming_webhooks.jsx';
-import InstalledOutgoingWebhooks from 'components/backstage/installed_outgoing_webhooks.jsx';
-import InstalledCommands from 'components/backstage/installed_commands.jsx';
-import AddIncomingWebhook from 'components/backstage/add_incoming_webhook.jsx';
-import AddOutgoingWebhook from 'components/backstage/add_outgoing_webhook.jsx';
-import AddCommand from 'components/backstage/add_command.jsx';
-
-export default (
- <Route path='integrations'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: Integrations
- }}
- />
- <Route path='incoming_webhooks'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledIncomingWebhooks
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddIncomingWebhook
- }}
- />
- </Route>
- <Route path='outgoing_webhooks'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledOutgoingWebhooks
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddOutgoingWebhook
- }}
- />
- </Route>
- <Route path='commands'>
- <IndexRoute
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: InstalledCommands
- }}
- />
- <Route
- path='add'
- components={{
- navbar: BackstageNavbar,
- sidebar: BackstageSidebar,
- center: AddCommand
- }}
- />
- </Route>
- <Redirect
- from='*'
- to='/error'
- query={RouteUtils.notFoundParams}
- />
- </Route>
-);
+export default {
+ path: 'integrations',
+ getComponents: (location, callback) => {
+ System.import('components/backstage/backstage_controller.jsx').then(RouteUtils.importComponentSuccess(callback));
+ },
+ indexRoute: {
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/integrations.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ },
+ childRoutes: [
+ {
+ path: 'incoming_webhooks',
+ indexRoute: {
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/installed_incoming_webhooks.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ },
+ childRoutes: [
+ {
+ path: 'add',
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/add_incoming_webhook.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ }
+ ]
+ },
+ {
+ path: 'outgoing_webhooks',
+ indexRoute: {
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/installed_outgoing_webhooks.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ },
+ childRoutes: [
+ {
+ path: 'add',
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/add_outgoing_webhook.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ }
+ ]
+ },
+ {
+ path: 'commands',
+ indexRoute: {
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/installed_commands.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ },
+ childRoutes: [
+ {
+ path: 'add',
+ getComponents: (location, callback) => {
+ System.import('components/integrations/components/add_command.jsx').then(RouteUtils.importComponentSuccess(callback));
+ }
+ }
+ ]
+ }
+ ]
+};
diff --git a/webapp/routes/route_team.jsx b/webapp/routes/route_team.jsx
index 7025ecb99..cb4f09ae4 100644
--- a/webapp/routes/route_team.jsx
+++ b/webapp/routes/route_team.jsx
@@ -15,6 +15,9 @@ import Client from 'utils/web_client.jsx';
import * as Utils from 'utils/utils.jsx';
import ChannelStore from 'stores/channel_store.jsx';
+import emojiRoute from 'routes/route_emoji.jsx';
+import integrationsRoute from 'routes/route_integrations.jsx';
+
function onChannelEnter(nextState, replace, callback) {
doChannelChange(nextState, replace, callback);
}
@@ -120,52 +123,52 @@ function onPermalinkEnter(nextState) {
export default {
path: ':team',
- getComponents: (location, callback) => {
- System.import('components/needs_team.jsx').then(RouteUtils.importComponentSuccess(callback));
- },
onEnter: preNeedsTeam,
indexRoute: {onEnter: (nextState, replace) => replace('/' + nextState.params.team + '/channels/town-square')},
childRoutes: [
+ integrationsRoute,
+ emojiRoute,
{
- path: 'channels/:channel',
- onEnter: onChannelEnter,
- getComponents: (location, callback) => {
- Promise.all([
- System.import('components/sidebar.jsx'),
- System.import('components/channel_view.jsx')
- ]).then(
- (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
- );
- }
- },
- {
- path: 'pl/:postid',
- onEnter: onPermalinkEnter,
getComponents: (location, callback) => {
- Promise.all([
- System.import('components/sidebar.jsx'),
- System.import('components/permalink_view.jsx')
- ]).then(
- (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
- );
- }
- },
- {
- path: 'tutorial',
- getComponents: (location, callback) => {
- Promise.all([
- System.import('components/sidebar.jsx'),
- System.import('components/tutorial/tutorial_view.jsx')
- ]).then(
- (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
- );
- }
- },
- {
- path: 'settings',
- getChildRoutes: (location, callback) => {
- System.import('routes/route_integrations.jsx').then((comp) => callback(null, [comp.default]));
- }
+ System.import('components/needs_team.jsx').then(RouteUtils.importComponentSuccess(callback));
+ },
+ childRoutes: [
+ {
+ path: 'channels/:channel',
+ onEnter: onChannelEnter,
+ getComponents: (location, callback) => {
+ Promise.all([
+ System.import('components/sidebar.jsx'),
+ System.import('components/channel_view.jsx')
+ ]).then(
+ (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
+ );
+ }
+ },
+ {
+ path: 'pl/:postid',
+ onEnter: onPermalinkEnter,
+ getComponents: (location, callback) => {
+ Promise.all([
+ System.import('components/sidebar.jsx'),
+ System.import('components/permalink_view.jsx')
+ ]).then(
+ (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
+ );
+ }
+ },
+ {
+ path: 'tutorial',
+ getComponents: (location, callback) => {
+ Promise.all([
+ System.import('components/sidebar.jsx'),
+ System.import('components/tutorial/tutorial_view.jsx')
+ ]).then(
+ (comarr) => callback(null, {sidebar: comarr[0].default, center: comarr[1].default})
+ );
+ }
+ }
+ ]
}
]
};
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index 884c69d74..ba72a8119 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -19,7 +19,6 @@
display: block;
.backstage-filter__search {
- border-bottom: 1px solid $light-gray;
margin: 10px 0;
width: 100%;
}
diff --git a/webapp/sass/routes/_backstage.scss b/webapp/sass/routes/_backstage.scss
index 244aad043..5d7942184 100644
--- a/webapp/sass/routes/_backstage.scss
+++ b/webapp/sass/routes/_backstage.scss
@@ -1,35 +1,11 @@
-body {
- &.backstage {
- height: auto;
- overflow: auto;
-
- .inner-wrap {
- @include translateX(0);
- margin-right: 0 !important;
-
- &:before {
- display: none;
- }
- }
-
- .sidebar--right,
- .sidebar--menu,
- .navbar {
- display: none;
- }
- }
-}
-
-.backstage-content {
- background-color: $bg--gray;
- height: 100%;
- margin: 46px auto;
- max-width: 960px;
- padding-left: 135px;
+.backstage {
+ height: 100vh;
+ width: 100vw;
}
.backstage-navbar {
background: $white;
+ height: 41px;
border-bottom: 1px solid $light-gray;
padding: 10px 20px;
z-index: 10;
@@ -51,13 +27,29 @@ body {
}
}
+.backstage-body {
+ background-color: $bg--gray;
+ bottom: 0;
+ display: inline-block;
+ height: calc(100vh - 41px);
+ overflow: auto;
+ top: 0;
+ width: 100%;
+}
+
+.backstage-content {
+ background-color: $bg--gray;
+ margin: 46px auto;
+ max-width: 960px;
+ padding-left: 135px;
+ vertical-align: top;
+}
+
.backstage-sidebar {
- height: 100%;
- left: 0;
- padding: 50px 20px;
+ padding: 46px 20px;
position: absolute;
+ vertical-align: top;
width: 260px;
- z-index: 5;
ul {
list-style: none;
@@ -69,6 +61,7 @@ body {
border: 1px solid $light-gray;
.category-title {
+ color: $black;
display: block;
line-height: 36px;
padding: 0 10px;
@@ -76,7 +69,8 @@ body {
}
.category-title--active {
- color: $black;
+ background-color: $primary-color;
+ color: $white;
}
.category-title__text {
@@ -110,8 +104,8 @@ body {
}
}
-.backstage__sidebar__category + .backstage__sidebar__category {
- border-top-width: 0;
+.backstage-sidebar__category + .backstage-sidebar__category {
+ margin-top: 1em;
}
.backstage-header__divider {
@@ -130,7 +124,7 @@ body {
margin: 5px 0;
}
- .add-integrations-link {
+ .add-link {
float: right;
}
}
@@ -139,45 +133,45 @@ body {
display: flex;
flex-direction: row;
width: 100%;
+}
- .backstage-filters__sort {
- flex-grow: 1;
- flex-shrink: 0;
- line-height: 30px;
+.backstage-filters__sort {
+ flex-grow: 1;
+ flex-shrink: 0;
+ line-height: 30px;
- .filter-sort {
- text-decoration: none;
+ .filter-sort {
+ text-decoration: none;
- &.filter-sort--active {
- color: inherit;
- cursor: default;
- }
+ &.filter-sort--active {
+ color: inherit;
+ cursor: default;
}
+ }
- .divider {
- margin-left: 8px;
- margin-right: 8px;
- }
+ .divider {
+ margin-left: 8px;
+ margin-right: 8px;
}
+}
- .backstage-filter__search {
- flex-grow: 0;
- flex-shrink: 0;
- position: relative;
- width: 270px;
+.backstage-filter__search {
+ flex-grow: 0;
+ flex-shrink: 0;
+ position: relative;
+ width: 270px;
- .fa {
- @include opacity(.4);
- left: 11px;
- position: absolute;
- top: 11px;
- }
+ .fa {
+ @include opacity(.4);
+ left: 11px;
+ position: absolute;
+ top: 11px;
+ }
- input {
- background: $white;
- border-bottom: none;
- padding-left: 30px;
- }
+ input {
+ background: $white;
+ border-bottom-width: 0;
+ padding-left: 30px;
}
}
@@ -332,3 +326,73 @@ body {
.integration-option__description {
color: $dark-gray;
}
+
+.emoji-list .backstage-filter__search input {
+ border-bottom-width: 1px;
+}
+
+.emoji-list__help {
+ display: block;
+ padding: 1em 0;
+}
+
+.emoji-list__table {
+ width: 100%;
+
+ .backstage-list__item {
+ display: table-row;
+ }
+
+ .backstage-list__empty td {
+ padding: 15px 20px;
+ }
+}
+
+.emoji-list__table-header {
+ font-weight: bold;
+}
+
+.emoji-list__name {
+ padding: 20px 0px 20px 15px;
+ width: 30%;
+}
+
+.emoji-list__image {
+ padding: 15px 0px;
+ width: 15%;
+}
+
+&.emoji-list__creator {
+ padding: 15px 0px;
+ width: 40%;
+}
+
+&.emoji-list__actions {
+ padding: 20px 15px 20px 0px;
+ width: 15%;
+}
+
+.add-emoji__upload {
+ display: inline-block;
+ margin: 0 10px 10px 0;
+ position: relative;
+
+ input {
+ @include opacity(0);
+ height: 100%;
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: 100%;
+ z-index: 5;
+ }
+}
+
+.add-emoji__filename,
+.add-emoji__preview {
+ padding-top: 7px;
+
+ .emoticon {
+ margin-bottom: 0;
+ }
+} \ No newline at end of file
diff --git a/webapp/stores/emoji_store.jsx b/webapp/stores/emoji_store.jsx
new file mode 100644
index 000000000..5e1d81dd3
--- /dev/null
+++ b/webapp/stores/emoji_store.jsx
@@ -0,0 +1,135 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import AppDispatcher from '../dispatcher/app_dispatcher.jsx';
+import Constants from 'utils/constants.jsx';
+import EventEmitter from 'events';
+
+import EmojiJson from 'utils/emoji.json';
+
+const ActionTypes = Constants.ActionTypes;
+
+const CHANGE_EVENT = 'changed';
+
+class EmojiStore extends EventEmitter {
+ constructor() {
+ super();
+
+ this.dispatchToken = AppDispatcher.register(this.handleEventPayload.bind(this));
+
+ this.emojis = new Map(EmojiJson);
+ this.systemEmojis = new Map(EmojiJson);
+
+ this.unicodeEmojis = new Map();
+ for (const [, emoji] of this.systemEmojis) {
+ if (emoji.unicode) {
+ this.unicodeEmojis.set(emoji.unicode, emoji);
+ }
+ }
+
+ this.receivedCustomEmojis = false;
+ this.customEmojis = new Map();
+ }
+
+ addChangeListener(callback) {
+ this.on(CHANGE_EVENT, callback);
+ }
+
+ removeChangeListener(callback) {
+ this.removeListener(CHANGE_EVENT, callback);
+ }
+
+ emitChange() {
+ this.emit(CHANGE_EVENT);
+ }
+
+ hasReceivedCustomEmojis() {
+ return this.receivedCustomEmojis;
+ }
+
+ setCustomEmojis(customEmojis) {
+ this.customEmojis = new Map();
+
+ for (const emoji of customEmojis) {
+ this.addCustomEmoji(emoji);
+ }
+
+ // add custom emojis to the map first so that they can't override system ones
+ this.emojis = new Map([...this.customEmojis, ...this.systemEmojis]);
+ }
+
+ addCustomEmoji(emoji) {
+ this.customEmojis.set(emoji.name, emoji);
+ }
+
+ removeCustomEmoji(id) {
+ for (const [name, emoji] of this.customEmojis) {
+ if (emoji.id === id) {
+ this.customEmojis.delete(name);
+ break;
+ }
+ }
+ }
+
+ getSystemEmojis() {
+ return this.systemEmojis;
+ }
+
+ getCustomEmojiMap() {
+ return this.customEmojis;
+ }
+
+ getEmojis() {
+ return this.emojis;
+ }
+
+ has(name) {
+ return this.emojis.has(name);
+ }
+
+ get(name) {
+ // prioritize system emojis so that custom ones can't override them
+ return this.emojis.get(name);
+ }
+
+ hasUnicode(codepoint) {
+ return this.unicodeEmojis.has(codepoint);
+ }
+
+ getUnicode(codepoint) {
+ return this.unicodeEmojis.get(codepoint);
+ }
+
+ getEmojiImageUrl(emoji) {
+ if (emoji.id) {
+ // must match Client.getCustomEmojiImageUrl
+ return `/api/v3/emoji/${emoji.id}`;
+ }
+
+ const filename = emoji.unicode || emoji.filename || emoji.name;
+
+ return Constants.EMOJI_PATH + '/' + filename + '.png';
+ }
+
+ handleEventPayload(payload) {
+ const action = payload.action;
+
+ switch (action.type) {
+ case ActionTypes.RECEIVED_CUSTOM_EMOJIS:
+ this.setCustomEmojis(action.emojis);
+ this.receivedCustomEmojis = true;
+ this.emitChange();
+ break;
+ case ActionTypes.RECEIVED_CUSTOM_EMOJI:
+ this.addCustomEmoji(action.emoji);
+ this.emitChange();
+ break;
+ case ActionTypes.REMOVED_CUSTOM_EMOJI:
+ this.removeCustomEmoji(action.id);
+ this.emitChange();
+ break;
+ }
+ }
+}
+
+export default new EmojiStore();
diff --git a/webapp/stores/team_store.jsx b/webapp/stores/team_store.jsx
index f4d60ba74..d81863aba 100644
--- a/webapp/stores/team_store.jsx
+++ b/webapp/stores/team_store.jsx
@@ -160,13 +160,16 @@ class TeamStoreClass extends EventEmitter {
}
isTeamAdminForCurrentTeam() {
+ return this.isTeamAdmin(UserStore.getCurrentId(), this.getCurrentId());
+ }
+
+ isTeamAdmin(userId, teamId) {
if (!Utils) {
Utils = require('utils/utils.jsx'); //eslint-disable-line global-require
}
- const userId = UserStore.getCurrentId();
var teamMembers = this.getTeamMembers();
- const teamMember = teamMembers.find((m) => m.user_id === userId && m.team_id === this.getCurrentId());
+ const teamMember = teamMembers.find((m) => m.user_id === userId && m.team_id === teamId);
if (teamMember) {
return Utils.isAdmin(teamMember.roles);
diff --git a/webapp/tests/emoticons.test.jsx b/webapp/tests/emoticons.test.jsx
index bb0421651..bf025e6b2 100644
--- a/webapp/tests/emoticons.test.jsx
+++ b/webapp/tests/emoticons.test.jsx
@@ -3,42 +3,45 @@
import assert from 'assert';
+import EmojiStore from 'stores/emoji_store.jsx';
import * as Emoticons from 'utils/emoticons.jsx';
describe('Emoticons', function() {
this.timeout(100000);
it('handleEmoticons', function(done) {
+ const emojis = EmojiStore.getEmojis();
+
assert.equal(
- Emoticons.handleEmoticons(':goat: :dash:', new Map()),
+ Emoticons.handleEmoticons(':goat: :dash:', new Map(), emojis),
'MM_EMOTICON0 MM_EMOTICON1',
'should replace emoticons with tokens'
);
assert.equal(
- Emoticons.handleEmoticons(':goat::dash:', new Map()),
+ Emoticons.handleEmoticons(':goat::dash:', new Map(), emojis),
'MM_EMOTICON0MM_EMOTICON1',
'should replace emoticons not separated by whitespace'
);
assert.equal(
- Emoticons.handleEmoticons('/:goat:..:dash:)', new Map()),
+ Emoticons.handleEmoticons('/:goat:..:dash:)', new Map(), emojis),
'/MM_EMOTICON0..MM_EMOTICON1)',
'should replace emoticons separated by punctuation'
);
assert.equal(
- Emoticons.handleEmoticons('asdf:goat:asdf:dash:asdf', new Map()),
+ Emoticons.handleEmoticons('asdf:goat:asdf:dash:asdf', new Map(), emojis),
'asdfMM_EMOTICON0asdfMM_EMOTICON1asdf',
'should replace emoticons separated by text'
);
assert.equal(
- Emoticons.handleEmoticons(':asdf: :goat : : dash:', new Map()),
+ Emoticons.handleEmoticons(':asdf: :goat : : dash:', new Map(), emojis),
':asdf: :goat : : dash:',
'shouldn\'t replace invalid emoticons'
);
done();
});
-}); \ No newline at end of file
+});
diff --git a/webapp/utils/async_client.jsx b/webapp/utils/async_client.jsx
index 65cb2d258..04e101aa9 100644
--- a/webapp/utils/async_client.jsx
+++ b/webapp/utils/async_client.jsx
@@ -1384,3 +1384,88 @@ export function getPublicLink(filename, success, error) {
}
);
}
+
+export function listEmoji() {
+ if (isCallInProgress('listEmoji')) {
+ return;
+ }
+
+ callTracker.listEmoji = utils.getTimestamp();
+
+ Client.listEmoji(
+ (data) => {
+ callTracker.listEmoji = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CUSTOM_EMOJIS,
+ emojis: data
+ });
+ },
+ (err) => {
+ callTracker.listEmoji = 0;
+ dispatchError(err, 'listEmoji');
+ }
+ );
+}
+
+export function addEmoji(emoji, image, success, error) {
+ const callName = 'addEmoji' + emoji.name;
+
+ if (isCallInProgress(callName)) {
+ return;
+ }
+
+ callTracker[callName] = utils.getTimestamp();
+
+ Client.addEmoji(
+ emoji,
+ image,
+ (data) => {
+ callTracker[callName] = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.RECEIVED_CUSTOM_EMOJI,
+ emoji: data
+ });
+
+ if (success) {
+ success();
+ }
+ },
+ (err) => {
+ callTracker[callName] = 0;
+
+ if (error) {
+ error(err);
+ } else {
+ dispatchError(err, 'addEmoji');
+ }
+ }
+ );
+}
+
+export function deleteEmoji(id) {
+ const callName = 'deleteEmoji' + id;
+
+ if (isCallInProgress(callName)) {
+ return;
+ }
+
+ callTracker[callName] = utils.getTimestamp();
+
+ Client.deleteEmoji(
+ id,
+ () => {
+ callTracker[callName] = 0;
+
+ AppDispatcher.handleServerAction({
+ type: ActionTypes.REMOVED_CUSTOM_EMOJI,
+ id
+ });
+ },
+ (err) => {
+ callTracker[callName] = 0;
+ dispatchError(err, 'deleteEmoji');
+ }
+ );
+} \ No newline at end of file
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index f6e929270..ac00262c3 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -86,6 +86,11 @@ export default {
UPDATED_COMMAND: null,
REMOVED_COMMAND: null,
+ RECEIVED_CUSTOM_EMOJIS: null,
+ RECEIVED_CUSTOM_EMOJI: null,
+ UPDATED_CUSTOM_EMOJI: null,
+ REMOVED_CUSTOM_EMOJI: null,
+
RECEIVED_MSG: null,
RECEIVED_MY_TEAM: null,
diff --git a/webapp/utils/emoji.json b/webapp/utils/emoji.json
index 8d6d7c7a6..03b56a18a 100644
--- a/webapp/utils/emoji.json
+++ b/webapp/utils/emoji.json
@@ -1,8333 +1 @@
-[
- {
- "emoji": "😄"
- , "description": "smiling face with open mouth and smiling eyes"
- , "aliases": [
- "smile"
- ]
- , "tags": [
- "happy"
- , "joy"
- , "pleased"
- ]
- }
-, {
- "emoji": "😃"
- , "description": "smiling face with open mouth"
- , "aliases": [
- "smiley"
- ]
- , "tags": [
- "happy"
- , "joy"
- , "haha"
- ]
- }
-, {
- "emoji": "😀"
- , "description": "grinning face"
- , "aliases": [
- "grinning"
- ]
- , "tags": [
- "smile"
- , "happy"
- ]
- }
-, {
- "emoji": "😊"
- , "description": "smiling face with smiling eyes"
- , "aliases": [
- "blush"
- ]
- , "tags": [
- "proud"
- ]
- }
-, {
- "emoji": "☺️"
- , "description": "white smiling face"
- , "aliases": [
- "relaxed"
- ]
- , "tags": [
- "blush"
- , "pleased"
- ]
- }
-, {
- "emoji": "😉"
- , "description": "winking face"
- , "aliases": [
- "wink"
- ]
- , "tags": [
- "flirt"
- ]
- }
-, {
- "emoji": "😍"
- , "description": "smiling face with heart-shaped eyes"
- , "aliases": [
- "heart_eyes"
- ]
- , "tags": [
- "love"
- , "crush"
- ]
- }
-, {
- "emoji": "😘"
- , "description": "face throwing a kiss"
- , "aliases": [
- "kissing_heart"
- ]
- , "tags": [
- "flirt"
- ]
- }
-, {
- "emoji": "😚"
- , "description": "kissing face with closed eyes"
- , "aliases": [
- "kissing_closed_eyes"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😗"
- , "description": "kissing face"
- , "aliases": [
- "kissing"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😙"
- , "description": "kissing face with smiling eyes"
- , "aliases": [
- "kissing_smiling_eyes"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😜"
- , "description": "face with stuck-out tongue and winking eye"
- , "aliases": [
- "stuck_out_tongue_winking_eye"
- ]
- , "tags": [
- "prank"
- , "silly"
- ]
- }
-, {
- "emoji": "😝"
- , "description": "face with stuck-out tongue and tightly-closed eyes"
- , "aliases": [
- "stuck_out_tongue_closed_eyes"
- ]
- , "tags": [
- "prank"
- ]
- }
-, {
- "emoji": "😛"
- , "description": "face with stuck-out tongue"
- , "aliases": [
- "stuck_out_tongue"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😳"
- , "description": "flushed face"
- , "aliases": [
- "flushed"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😁"
- , "description": "grinning face with smiling eyes"
- , "aliases": [
- "grin"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😔"
- , "description": "pensive face"
- , "aliases": [
- "pensive"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😌"
- , "description": "relieved face"
- , "aliases": [
- "relieved"
- ]
- , "tags": [
- "whew"
- ]
- }
-, {
- "emoji": "😒"
- , "description": "unamused face"
- , "aliases": [
- "unamused"
- ]
- , "tags": [
- "meh"
- ]
- }
-, {
- "emoji": "😞"
- , "description": "disappointed face"
- , "aliases": [
- "disappointed"
- ]
- , "tags": [
- "sad"
- ]
- }
-, {
- "emoji": "😣"
- , "description": "persevering face"
- , "aliases": [
- "persevere"
- ]
- , "tags": [
- "struggling"
- ]
- }
-, {
- "emoji": "😢"
- , "description": "crying face"
- , "aliases": [
- "cry"
- ]
- , "tags": [
- "sad"
- , "tear"
- ]
- }
-, {
- "emoji": "😂"
- , "description": "face with tears of joy"
- , "aliases": [
- "joy"
- ]
- , "tags": [
- "tears"
- ]
- }
-, {
- "emoji": "😭"
- , "description": "loudly crying face"
- , "aliases": [
- "sob"
- ]
- , "tags": [
- "sad"
- , "cry"
- , "bawling"
- ]
- }
-, {
- "emoji": "😪"
- , "description": "sleepy face"
- , "aliases": [
- "sleepy"
- ]
- , "tags": [
- "tired"
- ]
- }
-, {
- "emoji": "😥"
- , "description": "disappointed but relieved face"
- , "aliases": [
- "disappointed_relieved"
- ]
- , "tags": [
- "phew"
- , "sweat"
- , "nervous"
- ]
- }
-, {
- "emoji": "😰"
- , "description": "face with open mouth and cold sweat"
- , "aliases": [
- "cold_sweat"
- ]
- , "tags": [
- "nervous"
- ]
- }
-, {
- "emoji": "😅"
- , "description": "smiling face with open mouth and cold sweat"
- , "aliases": [
- "sweat_smile"
- ]
- , "tags": [
- "hot"
- ]
- }
-, {
- "emoji": "😓"
- , "description": "face with cold sweat"
- , "aliases": [
- "sweat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😩"
- , "description": "weary face"
- , "aliases": [
- "weary"
- ]
- , "tags": [
- "tired"
- ]
- }
-, {
- "emoji": "😫"
- , "description": "tired face"
- , "aliases": [
- "tired_face"
- ]
- , "tags": [
- "upset"
- , "whine"
- ]
- }
-, {
- "emoji": "😨"
- , "description": "fearful face"
- , "aliases": [
- "fearful"
- ]
- , "tags": [
- "scared"
- , "shocked"
- , "oops"
- ]
- }
-, {
- "emoji": "😱"
- , "description": "face screaming in fear"
- , "aliases": [
- "scream"
- ]
- , "tags": [
- "horror"
- , "shocked"
- ]
- }
-, {
- "emoji": "😠"
- , "description": "angry face"
- , "aliases": [
- "angry"
- ]
- , "tags": [
- "mad"
- , "annoyed"
- ]
- }
-, {
- "emoji": "😡"
- , "description": "pouting face"
- , "aliases": [
- "rage"
- , "pout"
- ]
- , "tags": [
- "angry"
- ]
- }
-, {
- "emoji": "😤"
- , "description": "face with look of triumph"
- , "aliases": [
- "triumph"
- ]
- , "tags": [
- "smug"
- ]
- }
-, {
- "emoji": "😖"
- , "description": "confounded face"
- , "aliases": [
- "confounded"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😆"
- , "description": "smiling face with open mouth and tightly-closed eyes"
- , "aliases": [
- "laughing"
- , "satisfied"
- ]
- , "tags": [
- "happy"
- , "haha"
- ]
- }
-, {
- "emoji": "😋"
- , "description": "face savouring delicious food"
- , "aliases": [
- "yum"
- ]
- , "tags": [
- "tongue"
- , "lick"
- ]
- }
-, {
- "emoji": "😷"
- , "description": "face with medical mask"
- , "aliases": [
- "mask"
- ]
- , "tags": [
- "sick"
- , "ill"
- ]
- }
-, {
- "emoji": "😎"
- , "description": "smiling face with sunglasses"
- , "aliases": [
- "sunglasses"
- ]
- , "tags": [
- "cool"
- ]
- }
-, {
- "emoji": "😴"
- , "description": "sleeping face"
- , "aliases": [
- "sleeping"
- ]
- , "tags": [
- "zzz"
- ]
- }
-, {
- "emoji": "😵"
- , "description": "dizzy face"
- , "aliases": [
- "dizzy_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😲"
- , "description": "astonished face"
- , "aliases": [
- "astonished"
- ]
- , "tags": [
- "amazed"
- , "gasp"
- ]
- }
-, {
- "emoji": "😟"
- , "description": "worried face"
- , "aliases": [
- "worried"
- ]
- , "tags": [
- "nervous"
- ]
- }
-, {
- "emoji": "😦"
- , "description": "frowning face with open mouth"
- , "aliases": [
- "frowning"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😧"
- , "description": "anguished face"
- , "aliases": [
- "anguished"
- ]
- , "tags": [
- "stunned"
- ]
- }
-, {
- "emoji": "😈"
- , "description": "smiling face with horns"
- , "aliases": [
- "smiling_imp"
- ]
- , "tags": [
- "devil"
- , "evil"
- , "horns"
- ]
- }
-, {
- "emoji": "👿"
- , "description": "imp"
- , "aliases": [
- "imp"
- ]
- , "tags": [
- "angry"
- , "devil"
- , "evil"
- , "horns"
- ]
- }
-, {
- "emoji": "😮"
- , "description": "face with open mouth"
- , "aliases": [
- "open_mouth"
- ]
- , "tags": [
- "surprise"
- , "impressed"
- , "wow"
- ]
- }
-, {
- "emoji": "😬"
- , "description": "grimacing face"
- , "aliases": [
- "grimacing"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😐"
- , "description": "neutral face"
- , "aliases": [
- "neutral_face"
- ]
- , "tags": [
- "meh"
- ]
- }
-, {
- "emoji": "😕"
- , "description": "confused face"
- , "aliases": [
- "confused"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😯"
- , "description": "hushed face"
- , "aliases": [
- "hushed"
- ]
- , "tags": [
- "silence"
- , "speechless"
- ]
- }
-, {
- "emoji": "😶"
- , "description": "face without mouth"
- , "aliases": [
- "no_mouth"
- ]
- , "tags": [
- "mute"
- , "silence"
- ]
- }
-, {
- "emoji": "😇"
- , "description": "smiling face with halo"
- , "aliases": [
- "innocent"
- ]
- , "tags": [
- "angel"
- ]
- }
-, {
- "emoji": "😏"
- , "description": "smirking face"
- , "aliases": [
- "smirk"
- ]
- , "tags": [
- "smug"
- ]
- }
-, {
- "emoji": "😑"
- , "description": "expressionless face"
- , "aliases": [
- "expressionless"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👲"
- , "description": "man with gua pi mao"
- , "aliases": [
- "man_with_gua_pi_mao"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👳"
- , "description": "man with turban"
- , "aliases": [
- "man_with_turban"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👮"
- , "description": "police officer"
- , "aliases": [
- "cop"
- ]
- , "tags": [
- "police"
- , "law"
- ]
- }
-, {
- "emoji": "👷"
- , "description": "construction worker"
- , "aliases": [
- "construction_worker"
- ]
- , "tags": [
- "helmet"
- ]
- }
-, {
- "emoji": "💂"
- , "description": "guardsman"
- , "aliases": [
- "guardsman"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👶"
- , "description": "baby"
- , "aliases": [
- "baby"
- ]
- , "tags": [
- "child"
- , "newborn"
- ]
- }
-, {
- "emoji": "👦"
- , "description": "boy"
- , "aliases": [
- "boy"
- ]
- , "tags": [
- "child"
- ]
- }
-, {
- "emoji": "👧"
- , "description": "girl"
- , "aliases": [
- "girl"
- ]
- , "tags": [
- "child"
- ]
- }
-, {
- "emoji": "👨"
- , "description": "man"
- , "aliases": [
- "man"
- ]
- , "tags": [
- "mustache"
- , "father"
- , "dad"
- ]
- }
-, {
- "emoji": "👩"
- , "description": "woman"
- , "aliases": [
- "woman"
- ]
- , "tags": [
- "girls"
- ]
- }
-, {
- "emoji": "👴"
- , "description": "older man"
- , "aliases": [
- "older_man"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👵"
- , "description": "older woman"
- , "aliases": [
- "older_woman"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👱"
- , "description": "person with blond hair"
- , "aliases": [
- "person_with_blond_hair"
- ]
- , "tags": [
- "boy"
- ]
- }
-, {
- "emoji": "👼"
- , "description": "baby angel"
- , "aliases": [
- "angel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👸"
- , "description": "princess"
- , "aliases": [
- "princess"
- ]
- , "tags": [
- "blonde"
- , "crown"
- , "royal"
- ]
- }
-, {
- "emoji": "😺"
- , "description": "smiling cat face with open mouth"
- , "aliases": [
- "smiley_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😸"
- , "description": "grinning cat face with smiling eyes"
- , "aliases": [
- "smile_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😻"
- , "description": "smiling cat face with heart-shaped eyes"
- , "aliases": [
- "heart_eyes_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😽"
- , "description": "kissing cat face with closed eyes"
- , "aliases": [
- "kissing_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😼"
- , "description": "cat face with wry smile"
- , "aliases": [
- "smirk_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙀"
- , "description": "weary cat face"
- , "aliases": [
- "scream_cat"
- ]
- , "tags": [
- "horror"
- ]
- }
-, {
- "emoji": "😿"
- , "description": "crying cat face"
- , "aliases": [
- "crying_cat_face"
- ]
- , "tags": [
- "sad"
- , "tear"
- ]
- }
-, {
- "emoji": "😹"
- , "description": "cat face with tears of joy"
- , "aliases": [
- "joy_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "😾"
- , "description": "pouting cat face"
- , "aliases": [
- "pouting_cat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👹"
- , "description": "japanese ogre"
- , "aliases": [
- "japanese_ogre"
- ]
- , "tags": [
- "monster"
- ]
- }
-, {
- "emoji": "👺"
- , "description": "japanese goblin"
- , "aliases": [
- "japanese_goblin"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙈"
- , "description": "see-no-evil monkey"
- , "aliases": [
- "see_no_evil"
- ]
- , "tags": [
- "monkey"
- , "blind"
- , "ignore"
- ]
- }
-, {
- "emoji": "🙉"
- , "description": "hear-no-evil monkey"
- , "aliases": [
- "hear_no_evil"
- ]
- , "tags": [
- "monkey"
- , "deaf"
- ]
- }
-, {
- "emoji": "🙊"
- , "description": "speak-no-evil monkey"
- , "aliases": [
- "speak_no_evil"
- ]
- , "tags": [
- "monkey"
- , "mute"
- , "hush"
- ]
- }
-, {
- "emoji": "💀"
- , "description": "skull"
- , "aliases": [
- "skull"
- ]
- , "tags": [
- "dead"
- , "danger"
- , "poison"
- ]
- }
-, {
- "emoji": "👽"
- , "description": "extraterrestrial alien"
- , "aliases": [
- "alien"
- ]
- , "tags": [
- "ufo"
- ]
- }
-, {
- "emoji": "💩"
- , "description": "pile of poo"
- , "aliases": [
- "hankey"
- , "poop"
- , "shit"
- ]
- , "tags": [
- "crap"
- ]
- }
-, {
- "emoji": "🔥"
- , "description": "fire"
- , "aliases": [
- "fire"
- ]
- , "tags": [
- "burn"
- ]
- }
-, {
- "emoji": "✨"
- , "description": "sparkles"
- , "aliases": [
- "sparkles"
- ]
- , "tags": [
- "shiny"
- ]
- }
-, {
- "emoji": "🌟"
- , "description": "glowing star"
- , "aliases": [
- "star2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💫"
- , "description": "dizzy symbol"
- , "aliases": [
- "dizzy"
- ]
- , "tags": [
- "star"
- ]
- }
-, {
- "emoji": "💥"
- , "description": "collision symbol"
- , "aliases": [
- "boom"
- , "collision"
- ]
- , "tags": [
- "explode"
- ]
- }
-, {
- "emoji": "💢"
- , "description": "anger symbol"
- , "aliases": [
- "anger"
- ]
- , "tags": [
- "angry"
- ]
- }
-, {
- "emoji": "💦"
- , "description": "splashing sweat symbol"
- , "aliases": [
- "sweat_drops"
- ]
- , "tags": [
- "water"
- , "workout"
- ]
- }
-, {
- "emoji": "💧"
- , "description": "droplet"
- , "aliases": [
- "droplet"
- ]
- , "tags": [
- "water"
- ]
- }
-, {
- "emoji": "💤"
- , "description": "sleeping symbol"
- , "aliases": [
- "zzz"
- ]
- , "tags": [
- "sleeping"
- ]
- }
-, {
- "emoji": "💨"
- , "description": "dash symbol"
- , "aliases": [
- "dash"
- ]
- , "tags": [
- "wind"
- , "blow"
- , "fast"
- ]
- }
-, {
- "emoji": "👂"
- , "description": "ear"
- , "aliases": [
- "ear"
- ]
- , "tags": [
- "hear"
- , "sound"
- , "listen"
- ]
- }
-, {
- "emoji": "👀"
- , "description": "eyes"
- , "aliases": [
- "eyes"
- ]
- , "tags": [
- "look"
- , "see"
- , "watch"
- ]
- }
-, {
- "emoji": "👃"
- , "description": "nose"
- , "aliases": [
- "nose"
- ]
- , "tags": [
- "smell"
- ]
- }
-, {
- "emoji": "👅"
- , "description": "tongue"
- , "aliases": [
- "tongue"
- ]
- , "tags": [
- "taste"
- ]
- }
-, {
- "emoji": "👄"
- , "description": "mouth"
- , "aliases": [
- "lips"
- ]
- , "tags": [
- "kiss"
- ]
- }
-, {
- "emoji": "👍"
- , "description": "thumbs up sign"
- , "aliases": [
- "+1"
- , "thumbsup"
- ]
- , "tags": [
- "approve"
- , "ok"
- ]
- }
-, {
- "emoji": "👎"
- , "description": "thumbs down sign"
- , "aliases": [
- "-1"
- , "thumbsdown"
- ]
- , "tags": [
- "disapprove"
- , "bury"
- ]
- }
-, {
- "emoji": "👌"
- , "description": "ok hand sign"
- , "aliases": [
- "ok_hand"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👊"
- , "description": "fisted hand sign"
- , "aliases": [
- "facepunch"
- , "punch"
- ]
- , "tags": [
- "attack"
- ]
- }
-, {
- "emoji": "✊"
- , "description": "raised fist"
- , "aliases": [
- "fist"
- ]
- , "tags": [
- "power"
- ]
- }
-, {
- "emoji": "✌️"
- , "description": "victory hand"
- , "aliases": [
- "v"
- ]
- , "tags": [
- "victory"
- , "peace"
- ]
- }
-, {
- "emoji": "👋"
- , "description": "waving hand sign"
- , "aliases": [
- "wave"
- ]
- , "tags": [
- "goodbye"
- ]
- }
-, {
- "emoji": "✋"
- , "description": "raised hand"
- , "aliases": [
- "hand"
- , "raised_hand"
- ]
- , "tags": [
- "highfive"
- , "stop"
- ]
- }
-, {
- "emoji": "👐"
- , "description": "open hands sign"
- , "aliases": [
- "open_hands"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👆"
- , "description": "white up pointing backhand index"
- , "aliases": [
- "point_up_2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👇"
- , "description": "white down pointing backhand index"
- , "aliases": [
- "point_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👉"
- , "description": "white right pointing backhand index"
- , "aliases": [
- "point_right"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👈"
- , "description": "white left pointing backhand index"
- , "aliases": [
- "point_left"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙌"
- , "description": "person raising both hands in celebration"
- , "aliases": [
- "raised_hands"
- ]
- , "tags": [
- "hooray"
- ]
- }
-, {
- "emoji": "🙏"
- , "description": "person with folded hands"
- , "aliases": [
- "pray"
- ]
- , "tags": [
- "please"
- , "hope"
- , "wish"
- ]
- }
-, {
- "emoji": "☝️"
- , "description": "white up pointing index"
- , "aliases": [
- "point_up"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👏"
- , "description": "clapping hands sign"
- , "aliases": [
- "clap"
- ]
- , "tags": [
- "praise"
- , "applause"
- ]
- }
-, {
- "emoji": "💪"
- , "description": "flexed biceps"
- , "aliases": [
- "muscle"
- ]
- , "tags": [
- "flex"
- , "bicep"
- , "strong"
- , "workout"
- ]
- }
-, {
- "emoji": "🚶"
- , "description": "pedestrian"
- , "aliases": [
- "walking"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏃"
- , "description": "runner"
- , "aliases": [
- "runner"
- , "running"
- ]
- , "tags": [
- "exercise"
- , "workout"
- , "marathon"
- ]
- }
-, {
- "emoji": "💃"
- , "description": "dancer"
- , "aliases": [
- "dancer"
- ]
- , "tags": [
- "dress"
- ]
- }
-, {
- "emoji": "👫"
- , "description": "man and woman holding hands"
- , "aliases": [
- "couple"
- ]
- , "tags": [
- "date"
- ]
- }
-, {
- "emoji": "👪"
- , "description": "family"
- , "aliases": [
- "family"
- ]
- , "tags": [
- "home"
- , "parents"
- , "child"
- ]
- }
-, {
- "emoji": "👬"
- , "description": "two men holding hands"
- , "aliases": [
- "two_men_holding_hands"
- ]
- , "tags": [
- "couple"
- , "date"
- ]
- }
-, {
- "emoji": "👭"
- , "description": "two women holding hands"
- , "aliases": [
- "two_women_holding_hands"
- ]
- , "tags": [
- "couple"
- , "date"
- ]
- }
-, {
- "emoji": "💏"
- , "description": "kiss"
- , "aliases": [
- "couplekiss"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💑"
- , "description": "couple with heart"
- , "aliases": [
- "couple_with_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👯"
- , "description": "woman with bunny ears"
- , "aliases": [
- "dancers"
- ]
- , "tags": [
- "bunny"
- ]
- }
-, {
- "emoji": "🙆"
- , "description": "face with ok gesture"
- , "aliases": [
- "ok_woman"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙅"
- , "description": "face with no good gesture"
- , "aliases": [
- "no_good"
- , "ng_woman"
- ]
- , "tags": [
- "stop"
- , "halt"
- ]
- }
-, {
- "emoji": "💁"
- , "description": "information desk person"
- , "aliases": [
- "information_desk_person"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙋"
- , "description": "happy person raising one hand"
- , "aliases": [
- "raising_hand"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💆"
- , "description": "face massage"
- , "aliases": [
- "massage"
- ]
- , "tags": [
- "spa"
- ]
- }
-, {
- "emoji": "💇"
- , "description": "haircut"
- , "aliases": [
- "haircut"
- ]
- , "tags": [
- "beauty"
- ]
- }
-, {
- "emoji": "💅"
- , "description": "nail polish"
- , "aliases": [
- "nail_care"
- ]
- , "tags": [
- "beauty"
- , "manicure"
- ]
- }
-, {
- "emoji": "👰"
- , "description": "bride with veil"
- , "aliases": [
- "bride_with_veil"
- ]
- , "tags": [
- "marriage"
- , "wedding"
- ]
- }
-, {
- "emoji": "🙎"
- , "description": "person with pouting face"
- , "aliases": [
- "person_with_pouting_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙍"
- , "description": "person frowning"
- , "aliases": [
- "person_frowning"
- ]
- , "tags": [
- "sad"
- ]
- }
-, {
- "emoji": "🙇"
- , "description": "person bowing deeply"
- , "aliases": [
- "bow"
- ]
- , "tags": [
- "respect"
- , "thanks"
- ]
- }
-, {
- "emoji": "🎩"
- , "description": "top hat"
- , "aliases": [
- "tophat"
- ]
- , "tags": [
- "hat"
- , "classy"
- ]
- }
-, {
- "emoji": "👑"
- , "description": "crown"
- , "aliases": [
- "crown"
- ]
- , "tags": [
- "king"
- , "queen"
- , "royal"
- ]
- }
-, {
- "emoji": "👒"
- , "description": "womans hat"
- , "aliases": [
- "womans_hat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👟"
- , "description": "athletic shoe"
- , "aliases": [
- "athletic_shoe"
- ]
- , "tags": [
- "sneaker"
- , "sport"
- , "running"
- ]
- }
-, {
- "emoji": "👞"
- , "description": "mans shoe"
- , "aliases": [
- "mans_shoe"
- , "shoe"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👡"
- , "description": "womans sandal"
- , "aliases": [
- "sandal"
- ]
- , "tags": [
- "shoe"
- ]
- }
-, {
- "emoji": "👠"
- , "description": "high-heeled shoe"
- , "aliases": [
- "high_heel"
- ]
- , "tags": [
- "shoe"
- ]
- }
-, {
- "emoji": "👢"
- , "description": "womans boots"
- , "aliases": [
- "boot"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👕"
- , "description": "t-shirt"
- , "aliases": [
- "shirt"
- , "tshirt"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👔"
- , "description": "necktie"
- , "aliases": [
- "necktie"
- ]
- , "tags": [
- "shirt"
- , "formal"
- ]
- }
-, {
- "emoji": "👚"
- , "description": "womans clothes"
- , "aliases": [
- "womans_clothes"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👗"
- , "description": "dress"
- , "aliases": [
- "dress"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎽"
- , "description": "running shirt with sash"
- , "aliases": [
- "running_shirt_with_sash"
- ]
- , "tags": [
- "marathon"
- ]
- }
-, {
- "emoji": "👖"
- , "description": "jeans"
- , "aliases": [
- "jeans"
- ]
- , "tags": [
- "pants"
- ]
- }
-, {
- "emoji": "👘"
- , "description": "kimono"
- , "aliases": [
- "kimono"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👙"
- , "description": "bikini"
- , "aliases": [
- "bikini"
- ]
- , "tags": [
- "beach"
- ]
- }
-, {
- "emoji": "💼"
- , "description": "briefcase"
- , "aliases": [
- "briefcase"
- ]
- , "tags": [
- "business"
- ]
- }
-, {
- "emoji": "👜"
- , "description": "handbag"
- , "aliases": [
- "handbag"
- ]
- , "tags": [
- "bag"
- ]
- }
-, {
- "emoji": "👝"
- , "description": "pouch"
- , "aliases": [
- "pouch"
- ]
- , "tags": [
- "bag"
- ]
- }
-, {
- "emoji": "👛"
- , "description": "purse"
- , "aliases": [
- "purse"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "👓"
- , "description": "eyeglasses"
- , "aliases": [
- "eyeglasses"
- ]
- , "tags": [
- "glasses"
- ]
- }
-, {
- "emoji": "🎀"
- , "description": "ribbon"
- , "aliases": [
- "ribbon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌂"
- , "description": "closed umbrella"
- , "aliases": [
- "closed_umbrella"
- ]
- , "tags": [
- "weather"
- , "rain"
- ]
- }
-, {
- "emoji": "💄"
- , "description": "lipstick"
- , "aliases": [
- "lipstick"
- ]
- , "tags": [
- "makeup"
- ]
- }
-, {
- "emoji": "💛"
- , "description": "yellow heart"
- , "aliases": [
- "yellow_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💙"
- , "description": "blue heart"
- , "aliases": [
- "blue_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💜"
- , "description": "purple heart"
- , "aliases": [
- "purple_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💚"
- , "description": "green heart"
- , "aliases": [
- "green_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "❤️"
- , "description": "heavy black heart"
- , "aliases": [
- "heart"
- ]
- , "tags": [
- "love"
- ]
- }
-, {
- "emoji": "💔"
- , "description": "broken heart"
- , "aliases": [
- "broken_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💗"
- , "description": "growing heart"
- , "aliases": [
- "heartpulse"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💓"
- , "description": "beating heart"
- , "aliases": [
- "heartbeat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💕"
- , "description": "two hearts"
- , "aliases": [
- "two_hearts"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💖"
- , "description": "sparkling heart"
- , "aliases": [
- "sparkling_heart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💞"
- , "description": "revolving hearts"
- , "aliases": [
- "revolving_hearts"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💘"
- , "description": "heart with arrow"
- , "aliases": [
- "cupid"
- ]
- , "tags": [
- "love"
- , "heart"
- ]
- }
-, {
- "emoji": "💌"
- , "description": "love letter"
- , "aliases": [
- "love_letter"
- ]
- , "tags": [
- "email"
- , "envelope"
- ]
- }
-, {
- "emoji": "💋"
- , "description": "kiss mark"
- , "aliases": [
- "kiss"
- ]
- , "tags": [
- "lipstick"
- ]
- }
-, {
- "emoji": "💍"
- , "description": "ring"
- , "aliases": [
- "ring"
- ]
- , "tags": [
- "wedding"
- , "marriage"
- , "engaged"
- ]
- }
-, {
- "emoji": "💎"
- , "description": "gem stone"
- , "aliases": [
- "gem"
- ]
- , "tags": [
- "diamond"
- ]
- }
-, {
- "emoji": "👤"
- , "description": "bust in silhouette"
- , "aliases": [
- "bust_in_silhouette"
- ]
- , "tags": [
- "user"
- ]
- }
-, {
- "emoji": "👥"
- , "description": "busts in silhouette"
- , "aliases": [
- "busts_in_silhouette"
- ]
- , "tags": [
- "users"
- , "group"
- , "team"
- ]
- }
-, {
- "emoji": "💬"
- , "description": "speech balloon"
- , "aliases": [
- "speech_balloon"
- ]
- , "tags": [
- "comment"
- ]
- }
-, {
- "emoji": "👣"
- , "description": "footprints"
- , "aliases": [
- "footprints"
- ]
- , "tags": [
- "feet"
- , "tracks"
- ]
- }
-, {
- "emoji": "💭"
- , "description": "thought balloon"
- , "aliases": [
- "thought_balloon"
- ]
- , "tags": [
- "thinking"
- ]
- }
-, {
- "emoji": "🐶"
- , "description": "dog face"
- , "aliases": [
- "dog"
- ]
- , "tags": [
- "pet"
- ]
- }
-, {
- "emoji": "🐺"
- , "description": "wolf face"
- , "aliases": [
- "wolf"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐱"
- , "description": "cat face"
- , "aliases": [
- "cat"
- ]
- , "tags": [
- "pet"
- ]
- }
-, {
- "emoji": "🐭"
- , "description": "mouse face"
- , "aliases": [
- "mouse"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐹"
- , "description": "hamster face"
- , "aliases": [
- "hamster"
- ]
- , "tags": [
- "pet"
- ]
- }
-, {
- "emoji": "🐰"
- , "description": "rabbit face"
- , "aliases": [
- "rabbit"
- ]
- , "tags": [
- "bunny"
- ]
- }
-, {
- "emoji": "🐸"
- , "description": "frog face"
- , "aliases": [
- "frog"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐯"
- , "description": "tiger face"
- , "aliases": [
- "tiger"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐨"
- , "description": "koala"
- , "aliases": [
- "koala"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐻"
- , "description": "bear face"
- , "aliases": [
- "bear"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐷"
- , "description": "pig face"
- , "aliases": [
- "pig"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐽"
- , "description": "pig nose"
- , "aliases": [
- "pig_nose"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐮"
- , "description": "cow face"
- , "aliases": [
- "cow"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐗"
- , "description": "boar"
- , "aliases": [
- "boar"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐵"
- , "description": "monkey face"
- , "aliases": [
- "monkey_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐒"
- , "description": "monkey"
- , "aliases": [
- "monkey"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐴"
- , "description": "horse face"
- , "aliases": [
- "horse"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐑"
- , "description": "sheep"
- , "aliases": [
- "sheep"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐘"
- , "description": "elephant"
- , "aliases": [
- "elephant"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐼"
- , "description": "panda face"
- , "aliases": [
- "panda_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐧"
- , "description": "penguin"
- , "aliases": [
- "penguin"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐦"
- , "description": "bird"
- , "aliases": [
- "bird"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐤"
- , "description": "baby chick"
- , "aliases": [
- "baby_chick"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐥"
- , "description": "front-facing baby chick"
- , "aliases": [
- "hatched_chick"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐣"
- , "description": "hatching chick"
- , "aliases": [
- "hatching_chick"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐔"
- , "description": "chicken"
- , "aliases": [
- "chicken"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐍"
- , "description": "snake"
- , "aliases": [
- "snake"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐢"
- , "description": "turtle"
- , "aliases": [
- "turtle"
- ]
- , "tags": [
- "slow"
- ]
- }
-, {
- "emoji": "🐛"
- , "description": "bug"
- , "aliases": [
- "bug"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐝"
- , "description": "honeybee"
- , "aliases": [
- "bee"
- , "honeybee"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐜"
- , "description": "ant"
- , "aliases": [
- "ant"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐞"
- , "description": "lady beetle"
- , "aliases": [
- "beetle"
- ]
- , "tags": [
- "bug"
- ]
- }
-, {
- "emoji": "🐌"
- , "description": "snail"
- , "aliases": [
- "snail"
- ]
- , "tags": [
- "slow"
- ]
- }
-, {
- "emoji": "🐙"
- , "description": "octopus"
- , "aliases": [
- "octopus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐚"
- , "description": "spiral shell"
- , "aliases": [
- "shell"
- ]
- , "tags": [
- "sea"
- , "beach"
- ]
- }
-, {
- "emoji": "🐠"
- , "description": "tropical fish"
- , "aliases": [
- "tropical_fish"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐟"
- , "description": "fish"
- , "aliases": [
- "fish"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐬"
- , "description": "dolphin"
- , "aliases": [
- "dolphin"
- , "flipper"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐳"
- , "description": "spouting whale"
- , "aliases": [
- "whale"
- ]
- , "tags": [
- "sea"
- ]
- }
-, {
- "emoji": "🐋"
- , "description": "whale"
- , "aliases": [
- "whale2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐄"
- , "description": "cow"
- , "aliases": [
- "cow2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐏"
- , "description": "ram"
- , "aliases": [
- "ram"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐀"
- , "description": "rat"
- , "aliases": [
- "rat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐃"
- , "description": "water buffalo"
- , "aliases": [
- "water_buffalo"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐅"
- , "description": "tiger"
- , "aliases": [
- "tiger2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐇"
- , "description": "rabbit"
- , "aliases": [
- "rabbit2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐉"
- , "description": "dragon"
- , "aliases": [
- "dragon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐎"
- , "description": "horse"
- , "aliases": [
- "racehorse"
- ]
- , "tags": [
- "speed"
- ]
- }
-, {
- "emoji": "🐐"
- , "description": "goat"
- , "aliases": [
- "goat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐓"
- , "description": "rooster"
- , "aliases": [
- "rooster"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐕"
- , "description": "dog"
- , "aliases": [
- "dog2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐖"
- , "description": "pig"
- , "aliases": [
- "pig2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐁"
- , "description": "mouse"
- , "aliases": [
- "mouse2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐂"
- , "description": "ox"
- , "aliases": [
- "ox"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐲"
- , "description": "dragon face"
- , "aliases": [
- "dragon_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐡"
- , "description": "blowfish"
- , "aliases": [
- "blowfish"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐊"
- , "description": "crocodile"
- , "aliases": [
- "crocodile"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐫"
- , "description": "bactrian camel"
- , "aliases": [
- "camel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐪"
- , "description": "dromedary camel"
- , "aliases": [
- "dromedary_camel"
- ]
- , "tags": [
- "desert"
- ]
- }
-, {
- "emoji": "🐆"
- , "description": "leopard"
- , "aliases": [
- "leopard"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐈"
- , "description": "cat"
- , "aliases": [
- "cat2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🐩"
- , "description": "poodle"
- , "aliases": [
- "poodle"
- ]
- , "tags": [
- "dog"
- ]
- }
-, {
- "emoji": "🐾"
- , "description": "paw prints"
- , "aliases": [
- "feet"
- , "paw_prints"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💐"
- , "description": "bouquet"
- , "aliases": [
- "bouquet"
- ]
- , "tags": [
- "flowers"
- ]
- }
-, {
- "emoji": "🌸"
- , "description": "cherry blossom"
- , "aliases": [
- "cherry_blossom"
- ]
- , "tags": [
- "flower"
- , "spring"
- ]
- }
-, {
- "emoji": "🌷"
- , "description": "tulip"
- , "aliases": [
- "tulip"
- ]
- , "tags": [
- "flower"
- ]
- }
-, {
- "emoji": "🍀"
- , "description": "four leaf clover"
- , "aliases": [
- "four_leaf_clover"
- ]
- , "tags": [
- "luck"
- ]
- }
-, {
- "emoji": "🌹"
- , "description": "rose"
- , "aliases": [
- "rose"
- ]
- , "tags": [
- "flower"
- ]
- }
-, {
- "emoji": "🌻"
- , "description": "sunflower"
- , "aliases": [
- "sunflower"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌺"
- , "description": "hibiscus"
- , "aliases": [
- "hibiscus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍁"
- , "description": "maple leaf"
- , "aliases": [
- "maple_leaf"
- ]
- , "tags": [
- "canada"
- ]
- }
-, {
- "emoji": "🍃"
- , "description": "leaf fluttering in wind"
- , "aliases": [
- "leaves"
- ]
- , "tags": [
- "leaf"
- ]
- }
-, {
- "emoji": "🍂"
- , "description": "fallen leaf"
- , "aliases": [
- "fallen_leaf"
- ]
- , "tags": [
- "autumn"
- ]
- }
-, {
- "emoji": "🌿"
- , "description": "herb"
- , "aliases": [
- "herb"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌾"
- , "description": "ear of rice"
- , "aliases": [
- "ear_of_rice"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍄"
- , "description": "mushroom"
- , "aliases": [
- "mushroom"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌵"
- , "description": "cactus"
- , "aliases": [
- "cactus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌴"
- , "description": "palm tree"
- , "aliases": [
- "palm_tree"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌲"
- , "description": "evergreen tree"
- , "aliases": [
- "evergreen_tree"
- ]
- , "tags": [
- "wood"
- ]
- }
-, {
- "emoji": "🌳"
- , "description": "deciduous tree"
- , "aliases": [
- "deciduous_tree"
- ]
- , "tags": [
- "wood"
- ]
- }
-, {
- "emoji": "🌰"
- , "description": "chestnut"
- , "aliases": [
- "chestnut"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌱"
- , "description": "seedling"
- , "aliases": [
- "seedling"
- ]
- , "tags": [
- "plant"
- ]
- }
-, {
- "emoji": "🌼"
- , "description": "blossom"
- , "aliases": [
- "blossom"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌐"
- , "description": "globe with meridians"
- , "aliases": [
- "globe_with_meridians"
- ]
- , "tags": [
- "world"
- , "global"
- , "international"
- ]
- }
-, {
- "emoji": "🌞"
- , "description": "sun with face"
- , "aliases": [
- "sun_with_face"
- ]
- , "tags": [
- "summer"
- ]
- }
-, {
- "emoji": "🌝"
- , "description": "full moon with face"
- , "aliases": [
- "full_moon_with_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌚"
- , "description": "new moon with face"
- , "aliases": [
- "new_moon_with_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌑"
- , "description": "new moon symbol"
- , "aliases": [
- "new_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌒"
- , "description": "waxing crescent moon symbol"
- , "aliases": [
- "waxing_crescent_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌓"
- , "description": "first quarter moon symbol"
- , "aliases": [
- "first_quarter_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌔"
- , "description": "waxing gibbous moon symbol"
- , "aliases": [
- "moon"
- , "waxing_gibbous_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌕"
- , "description": "full moon symbol"
- , "aliases": [
- "full_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌖"
- , "description": "waning gibbous moon symbol"
- , "aliases": [
- "waning_gibbous_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌗"
- , "description": "last quarter moon symbol"
- , "aliases": [
- "last_quarter_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌘"
- , "description": "waning crescent moon symbol"
- , "aliases": [
- "waning_crescent_moon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌜"
- , "description": "last quarter moon with face"
- , "aliases": [
- "last_quarter_moon_with_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌛"
- , "description": "first quarter moon with face"
- , "aliases": [
- "first_quarter_moon_with_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌙"
- , "description": "crescent moon"
- , "aliases": [
- "crescent_moon"
- ]
- , "tags": [
- "night"
- ]
- }
-, {
- "emoji": "🌍"
- , "description": "earth globe europe-africa"
- , "aliases": [
- "earth_africa"
- ]
- , "tags": [
- "globe"
- , "world"
- , "international"
- ]
- }
-, {
- "emoji": "🌎"
- , "description": "earth globe americas"
- , "aliases": [
- "earth_americas"
- ]
- , "tags": [
- "globe"
- , "world"
- , "international"
- ]
- }
-, {
- "emoji": "🌏"
- , "description": "earth globe asia-australia"
- , "aliases": [
- "earth_asia"
- ]
- , "tags": [
- "globe"
- , "world"
- , "international"
- ]
- }
-, {
- "emoji": "🌋"
- , "description": "volcano"
- , "aliases": [
- "volcano"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌌"
- , "description": "milky way"
- , "aliases": [
- "milky_way"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌠"
- , "description": "shooting star"
- , "aliases": [
- "stars"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⭐"
- , "description": "white medium star"
- , "aliases": [
- "star"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "☀️"
- , "description": "black sun with rays"
- , "aliases": [
- "sunny"
- ]
- , "tags": [
- "weather"
- ]
- }
-, {
- "emoji": "⛅"
- , "description": "sun behind cloud"
- , "aliases": [
- "partly_sunny"
- ]
- , "tags": [
- "weather"
- , "cloud"
- ]
- }
-, {
- "emoji": "☁️"
- , "description": "cloud"
- , "aliases": [
- "cloud"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⚡"
- , "description": "high voltage sign"
- , "aliases": [
- "zap"
- ]
- , "tags": [
- "lightning"
- , "thunder"
- ]
- }
-, {
- "emoji": "☔"
- , "description": "umbrella with rain drops"
- , "aliases": [
- "umbrella"
- ]
- , "tags": [
- "rain"
- , "weather"
- ]
- }
-, {
- "emoji": "❄️"
- , "description": "snowflake"
- , "aliases": [
- "snowflake"
- ]
- , "tags": [
- "winter"
- , "cold"
- , "weather"
- ]
- }
-, {
- "emoji": "⛄"
- , "description": "snowman without snow"
- , "aliases": [
- "snowman"
- ]
- , "tags": [
- "winter"
- , "christmas"
- ]
- }
-, {
- "emoji": "🌀"
- , "description": "cyclone"
- , "aliases": [
- "cyclone"
- ]
- , "tags": [
- "swirl"
- ]
- }
-, {
- "emoji": "🌁"
- , "description": "foggy"
- , "aliases": [
- "foggy"
- ]
- , "tags": [
- "karl"
- ]
- }
-, {
- "emoji": "🌈"
- , "description": "rainbow"
- , "aliases": [
- "rainbow"
- ]
- , "tags": [
- "pride"
- ]
- }
-, {
- "emoji": "🌊"
- , "description": "water wave"
- , "aliases": [
- "ocean"
- ]
- , "tags": [
- "sea"
- ]
- }
-, {
- "emoji": "🎍"
- , "description": "pine decoration"
- , "aliases": [
- "bamboo"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💝"
- , "description": "heart with ribbon"
- , "aliases": [
- "gift_heart"
- ]
- , "tags": [
- "chocolates"
- ]
- }
-, {
- "emoji": "🎎"
- , "description": "japanese dolls"
- , "aliases": [
- "dolls"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎒"
- , "description": "school satchel"
- , "aliases": [
- "school_satchel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎓"
- , "description": "graduation cap"
- , "aliases": [
- "mortar_board"
- ]
- , "tags": [
- "education"
- , "college"
- , "university"
- , "graduation"
- ]
- }
-, {
- "emoji": "🎏"
- , "description": "carp streamer"
- , "aliases": [
- "flags"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎆"
- , "description": "fireworks"
- , "aliases": [
- "fireworks"
- ]
- , "tags": [
- "festival"
- , "celebration"
- ]
- }
-, {
- "emoji": "🎇"
- , "description": "firework sparkler"
- , "aliases": [
- "sparkler"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎐"
- , "description": "wind chime"
- , "aliases": [
- "wind_chime"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎑"
- , "description": "moon viewing ceremony"
- , "aliases": [
- "rice_scene"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎃"
- , "description": "jack-o-lantern"
- , "aliases": [
- "jack_o_lantern"
- ]
- , "tags": [
- "halloween"
- ]
- }
-, {
- "emoji": "👻"
- , "description": "ghost"
- , "aliases": [
- "ghost"
- ]
- , "tags": [
- "halloween"
- ]
- }
-, {
- "emoji": "🎅"
- , "description": "father christmas"
- , "aliases": [
- "santa"
- ]
- , "tags": [
- "christmas"
- ]
- }
-, {
- "emoji": "🎄"
- , "description": "christmas tree"
- , "aliases": [
- "christmas_tree"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎁"
- , "description": "wrapped present"
- , "aliases": [
- "gift"
- ]
- , "tags": [
- "present"
- , "birthday"
- , "christmas"
- ]
- }
-, {
- "emoji": "🎋"
- , "description": "tanabata tree"
- , "aliases": [
- "tanabata_tree"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎉"
- , "description": "party popper"
- , "aliases": [
- "tada"
- ]
- , "tags": [
- "party"
- ]
- }
-, {
- "emoji": "🎊"
- , "description": "confetti ball"
- , "aliases": [
- "confetti_ball"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎈"
- , "description": "balloon"
- , "aliases": [
- "balloon"
- ]
- , "tags": [
- "party"
- , "birthday"
- ]
- }
-, {
- "emoji": "🎌"
- , "description": "crossed flags"
- , "aliases": [
- "crossed_flags"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔮"
- , "description": "crystal ball"
- , "aliases": [
- "crystal_ball"
- ]
- , "tags": [
- "fortune"
- ]
- }
-, {
- "emoji": "🎥"
- , "description": "movie camera"
- , "aliases": [
- "movie_camera"
- ]
- , "tags": [
- "film"
- , "video"
- ]
- }
-, {
- "emoji": "📷"
- , "description": "camera"
- , "aliases": [
- "camera"
- ]
- , "tags": [
- "photo"
- ]
- }
-, {
- "emoji": "📹"
- , "description": "video camera"
- , "aliases": [
- "video_camera"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📼"
- , "description": "videocassette"
- , "aliases": [
- "vhs"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💿"
- , "description": "optical disc"
- , "aliases": [
- "cd"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📀"
- , "description": "dvd"
- , "aliases": [
- "dvd"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💽"
- , "description": "minidisc"
- , "aliases": [
- "minidisc"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💾"
- , "description": "floppy disk"
- , "aliases": [
- "floppy_disk"
- ]
- , "tags": [
- "save"
- ]
- }
-, {
- "emoji": "💻"
- , "description": "personal computer"
- , "aliases": [
- "computer"
- ]
- , "tags": [
- "desktop"
- , "screen"
- ]
- }
-, {
- "emoji": "📱"
- , "description": "mobile phone"
- , "aliases": [
- "iphone"
- ]
- , "tags": [
- "smartphone"
- , "mobile"
- ]
- }
-, {
- "emoji": "☎️"
- , "description": "black telephone"
- , "aliases": [
- "phone"
- , "telephone"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📞"
- , "description": "telephone receiver"
- , "aliases": [
- "telephone_receiver"
- ]
- , "tags": [
- "phone"
- , "call"
- ]
- }
-, {
- "emoji": "📟"
- , "description": "pager"
- , "aliases": [
- "pager"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📠"
- , "description": "fax machine"
- , "aliases": [
- "fax"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📡"
- , "description": "satellite antenna"
- , "aliases": [
- "satellite"
- ]
- , "tags": [
- "signal"
- ]
- }
-, {
- "emoji": "📺"
- , "description": "television"
- , "aliases": [
- "tv"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📻"
- , "description": "radio"
- , "aliases": [
- "radio"
- ]
- , "tags": [
- "podcast"
- ]
- }
-, {
- "emoji": "🔊"
- , "description": "speaker with three sound waves"
- , "aliases": [
- "loud_sound"
- ]
- , "tags": [
- "volume"
- ]
- }
-, {
- "emoji": "🔉"
- , "description": "speaker with one sound wave"
- , "aliases": [
- "sound"
- ]
- , "tags": [
- "volume"
- ]
- }
-, {
- "emoji": "🔈"
- , "description": "speaker"
- , "aliases": [
- "speaker"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔇"
- , "description": "speaker with cancellation stroke"
- , "aliases": [
- "mute"
- ]
- , "tags": [
- "sound"
- , "volume"
- ]
- }
-, {
- "emoji": "🔔"
- , "description": "bell"
- , "aliases": [
- "bell"
- ]
- , "tags": [
- "sound"
- , "notification"
- ]
- }
-, {
- "emoji": "🔕"
- , "description": "bell with cancellation stroke"
- , "aliases": [
- "no_bell"
- ]
- , "tags": [
- "volume"
- , "off"
- ]
- }
-, {
- "emoji": "📢"
- , "description": "public address loudspeaker"
- , "aliases": [
- "loudspeaker"
- ]
- , "tags": [
- "announcement"
- ]
- }
-, {
- "emoji": "📣"
- , "description": "cheering megaphone"
- , "aliases": [
- "mega"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⏳"
- , "description": "hourglass with flowing sand"
- , "aliases": [
- "hourglass_flowing_sand"
- ]
- , "tags": [
- "time"
- ]
- }
-, {
- "emoji": "⌛"
- , "description": "hourglass"
- , "aliases": [
- "hourglass"
- ]
- , "tags": [
- "time"
- ]
- }
-, {
- "emoji": "⏰"
- , "description": "alarm clock"
- , "aliases": [
- "alarm_clock"
- ]
- , "tags": [
- "morning"
- ]
- }
-, {
- "emoji": "⌚"
- , "description": "watch"
- , "aliases": [
- "watch"
- ]
- , "tags": [
- "time"
- ]
- }
-, {
- "emoji": "🔓"
- , "description": "open lock"
- , "aliases": [
- "unlock"
- ]
- , "tags": [
- "security"
- ]
- }
-, {
- "emoji": "🔒"
- , "description": "lock"
- , "aliases": [
- "lock"
- ]
- , "tags": [
- "security"
- , "private"
- ]
- }
-, {
- "emoji": "🔏"
- , "description": "lock with ink pen"
- , "aliases": [
- "lock_with_ink_pen"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔐"
- , "description": "closed lock with key"
- , "aliases": [
- "closed_lock_with_key"
- ]
- , "tags": [
- "security"
- ]
- }
-, {
- "emoji": "🔑"
- , "description": "key"
- , "aliases": [
- "key"
- ]
- , "tags": [
- "lock"
- , "password"
- ]
- }
-, {
- "emoji": "🔎"
- , "description": "right-pointing magnifying glass"
- , "aliases": [
- "mag_right"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💡"
- , "description": "electric light bulb"
- , "aliases": [
- "bulb"
- ]
- , "tags": [
- "idea"
- , "light"
- ]
- }
-, {
- "emoji": "🔦"
- , "description": "electric torch"
- , "aliases": [
- "flashlight"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔆"
- , "description": "high brightness symbol"
- , "aliases": [
- "high_brightness"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔅"
- , "description": "low brightness symbol"
- , "aliases": [
- "low_brightness"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔌"
- , "description": "electric plug"
- , "aliases": [
- "electric_plug"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔋"
- , "description": "battery"
- , "aliases": [
- "battery"
- ]
- , "tags": [
- "power"
- ]
- }
-, {
- "emoji": "🔍"
- , "description": "left-pointing magnifying glass"
- , "aliases": [
- "mag"
- ]
- , "tags": [
- "search"
- , "zoom"
- ]
- }
-, {
- "emoji": "🛁"
- , "description": "bathtub"
- , "aliases": [
- "bathtub"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🛀"
- , "description": "bath"
- , "aliases": [
- "bath"
- ]
- , "tags": [
- "shower"
- ]
- }
-, {
- "emoji": "🚿"
- , "description": "shower"
- , "aliases": [
- "shower"
- ]
- , "tags": [
- "bath"
- ]
- }
-, {
- "emoji": "🚽"
- , "description": "toilet"
- , "aliases": [
- "toilet"
- ]
- , "tags": [
- "wc"
- ]
- }
-, {
- "emoji": "🔧"
- , "description": "wrench"
- , "aliases": [
- "wrench"
- ]
- , "tags": [
- "tool"
- ]
- }
-, {
- "emoji": "🔩"
- , "description": "nut and bolt"
- , "aliases": [
- "nut_and_bolt"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔨"
- , "description": "hammer"
- , "aliases": [
- "hammer"
- ]
- , "tags": [
- "tool"
- ]
- }
-, {
- "emoji": "🚪"
- , "description": "door"
- , "aliases": [
- "door"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚬"
- , "description": "smoking symbol"
- , "aliases": [
- "smoking"
- ]
- , "tags": [
- "cigarette"
- ]
- }
-, {
- "emoji": "💣"
- , "description": "bomb"
- , "aliases": [
- "bomb"
- ]
- , "tags": [
- "boom"
- ]
- }
-, {
- "emoji": "🔫"
- , "description": "pistol"
- , "aliases": [
- "gun"
- ]
- , "tags": [
- "shoot"
- , "weapon"
- ]
- }
-, {
- "emoji": "🔪"
- , "description": "hocho"
- , "aliases": [
- "hocho"
- , "knife"
- ]
- , "tags": [
- "cut"
- , "chop"
- ]
- }
-, {
- "emoji": "💊"
- , "description": "pill"
- , "aliases": [
- "pill"
- ]
- , "tags": [
- "health"
- , "medicine"
- ]
- }
-, {
- "emoji": "💉"
- , "description": "syringe"
- , "aliases": [
- "syringe"
- ]
- , "tags": [
- "health"
- , "hospital"
- , "needle"
- ]
- }
-, {
- "emoji": "💰"
- , "description": "money bag"
- , "aliases": [
- "moneybag"
- ]
- , "tags": [
- "dollar"
- , "cream"
- ]
- }
-, {
- "emoji": "💴"
- , "description": "banknote with yen sign"
- , "aliases": [
- "yen"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💵"
- , "description": "banknote with dollar sign"
- , "aliases": [
- "dollar"
- ]
- , "tags": [
- "money"
- ]
- }
-, {
- "emoji": "💷"
- , "description": "banknote with pound sign"
- , "aliases": [
- "pound"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💶"
- , "description": "banknote with euro sign"
- , "aliases": [
- "euro"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💳"
- , "description": "credit card"
- , "aliases": [
- "credit_card"
- ]
- , "tags": [
- "subscription"
- ]
- }
-, {
- "emoji": "💸"
- , "description": "money with wings"
- , "aliases": [
- "money_with_wings"
- ]
- , "tags": [
- "dollar"
- ]
- }
-, {
- "emoji": "📲"
- , "description": "mobile phone with rightwards arrow at left"
- , "aliases": [
- "calling"
- ]
- , "tags": [
- "call"
- , "incoming"
- ]
- }
-, {
- "emoji": "📧"
- , "description": "e-mail symbol"
- , "aliases": [
- "e-mail"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📥"
- , "description": "inbox tray"
- , "aliases": [
- "inbox_tray"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📤"
- , "description": "outbox tray"
- , "aliases": [
- "outbox_tray"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✉️"
- , "description": "envelope"
- , "aliases": [
- "email"
- , "envelope"
- ]
- , "tags": [
- "letter"
- ]
- }
-, {
- "emoji": "📩"
- , "description": "envelope with downwards arrow above"
- , "aliases": [
- "envelope_with_arrow"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📨"
- , "description": "incoming envelope"
- , "aliases": [
- "incoming_envelope"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📯"
- , "description": "postal horn"
- , "aliases": [
- "postal_horn"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📫"
- , "description": "closed mailbox with raised flag"
- , "aliases": [
- "mailbox"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📪"
- , "description": "closed mailbox with lowered flag"
- , "aliases": [
- "mailbox_closed"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📬"
- , "description": "open mailbox with raised flag"
- , "aliases": [
- "mailbox_with_mail"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📭"
- , "description": "open mailbox with lowered flag"
- , "aliases": [
- "mailbox_with_no_mail"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📮"
- , "description": "postbox"
- , "aliases": [
- "postbox"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📦"
- , "description": "package"
- , "aliases": [
- "package"
- ]
- , "tags": [
- "shipping"
- ]
- }
-, {
- "emoji": "📝"
- , "description": "memo"
- , "aliases": [
- "memo"
- , "pencil"
- ]
- , "tags": [
- "document"
- , "note"
- ]
- }
-, {
- "emoji": "📄"
- , "description": "page facing up"
- , "aliases": [
- "page_facing_up"
- ]
- , "tags": [
- "document"
- ]
- }
-, {
- "emoji": "📃"
- , "description": "page with curl"
- , "aliases": [
- "page_with_curl"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📑"
- , "description": "bookmark tabs"
- , "aliases": [
- "bookmark_tabs"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📊"
- , "description": "bar chart"
- , "aliases": [
- "bar_chart"
- ]
- , "tags": [
- "stats"
- , "metrics"
- ]
- }
-, {
- "emoji": "📈"
- , "description": "chart with upwards trend"
- , "aliases": [
- "chart_with_upwards_trend"
- ]
- , "tags": [
- "graph"
- , "metrics"
- ]
- }
-, {
- "emoji": "📉"
- , "description": "chart with downwards trend"
- , "aliases": [
- "chart_with_downwards_trend"
- ]
- , "tags": [
- "graph"
- , "metrics"
- ]
- }
-, {
- "emoji": "📜"
- , "description": "scroll"
- , "aliases": [
- "scroll"
- ]
- , "tags": [
- "document"
- ]
- }
-, {
- "emoji": "📋"
- , "description": "clipboard"
- , "aliases": [
- "clipboard"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📅"
- , "description": "calendar"
- , "aliases": [
- "date"
- ]
- , "tags": [
- "calendar"
- , "schedule"
- ]
- }
-, {
- "emoji": "📆"
- , "description": "tear-off calendar"
- , "aliases": [
- "calendar"
- ]
- , "tags": [
- "schedule"
- ]
- }
-, {
- "emoji": "📇"
- , "description": "card index"
- , "aliases": [
- "card_index"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📁"
- , "description": "file folder"
- , "aliases": [
- "file_folder"
- ]
- , "tags": [
- "directory"
- ]
- }
-, {
- "emoji": "📂"
- , "description": "open file folder"
- , "aliases": [
- "open_file_folder"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✂️"
- , "description": "black scissors"
- , "aliases": [
- "scissors"
- ]
- , "tags": [
- "cut"
- ]
- }
-, {
- "emoji": "📌"
- , "description": "pushpin"
- , "aliases": [
- "pushpin"
- ]
- , "tags": [
- "location"
- ]
- }
-, {
- "emoji": "📎"
- , "description": "paperclip"
- , "aliases": [
- "paperclip"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✒️"
- , "description": "black nib"
- , "aliases": [
- "black_nib"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✏️"
- , "description": "pencil"
- , "aliases": [
- "pencil2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📏"
- , "description": "straight ruler"
- , "aliases": [
- "straight_ruler"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📐"
- , "description": "triangular ruler"
- , "aliases": [
- "triangular_ruler"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📕"
- , "description": "closed book"
- , "aliases": [
- "closed_book"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📗"
- , "description": "green book"
- , "aliases": [
- "green_book"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📘"
- , "description": "blue book"
- , "aliases": [
- "blue_book"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📙"
- , "description": "orange book"
- , "aliases": [
- "orange_book"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📓"
- , "description": "notebook"
- , "aliases": [
- "notebook"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📔"
- , "description": "notebook with decorative cover"
- , "aliases": [
- "notebook_with_decorative_cover"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📒"
- , "description": "ledger"
- , "aliases": [
- "ledger"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📚"
- , "description": "books"
- , "aliases": [
- "books"
- ]
- , "tags": [
- "library"
- ]
- }
-, {
- "emoji": "📖"
- , "description": "open book"
- , "aliases": [
- "book"
- , "open_book"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔖"
- , "description": "bookmark"
- , "aliases": [
- "bookmark"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📛"
- , "description": "name badge"
- , "aliases": [
- "name_badge"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔬"
- , "description": "microscope"
- , "aliases": [
- "microscope"
- ]
- , "tags": [
- "science"
- , "laboratory"
- , "investigate"
- ]
- }
-, {
- "emoji": "🔭"
- , "description": "telescope"
- , "aliases": [
- "telescope"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📰"
- , "description": "newspaper"
- , "aliases": [
- "newspaper"
- ]
- , "tags": [
- "press"
- ]
- }
-, {
- "emoji": "🎨"
- , "description": "artist palette"
- , "aliases": [
- "art"
- ]
- , "tags": [
- "design"
- , "paint"
- ]
- }
-, {
- "emoji": "🎬"
- , "description": "clapper board"
- , "aliases": [
- "clapper"
- ]
- , "tags": [
- "film"
- ]
- }
-, {
- "emoji": "🎤"
- , "description": "microphone"
- , "aliases": [
- "microphone"
- ]
- , "tags": [
- "sing"
- ]
- }
-, {
- "emoji": "🎧"
- , "description": "headphone"
- , "aliases": [
- "headphones"
- ]
- , "tags": [
- "music"
- , "earphones"
- ]
- }
-, {
- "emoji": "🎼"
- , "description": "musical score"
- , "aliases": [
- "musical_score"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎵"
- , "description": "musical note"
- , "aliases": [
- "musical_note"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎶"
- , "description": "multiple musical notes"
- , "aliases": [
- "notes"
- ]
- , "tags": [
- "music"
- ]
- }
-, {
- "emoji": "🎹"
- , "description": "musical keyboard"
- , "aliases": [
- "musical_keyboard"
- ]
- , "tags": [
- "piano"
- ]
- }
-, {
- "emoji": "🎻"
- , "description": "violin"
- , "aliases": [
- "violin"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎺"
- , "description": "trumpet"
- , "aliases": [
- "trumpet"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎷"
- , "description": "saxophone"
- , "aliases": [
- "saxophone"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎸"
- , "description": "guitar"
- , "aliases": [
- "guitar"
- ]
- , "tags": [
- "rock"
- ]
- }
-, {
- "emoji": "👾"
- , "description": "alien monster"
- , "aliases": [
- "space_invader"
- ]
- , "tags": [
- "game"
- , "retro"
- ]
- }
-, {
- "emoji": "🎮"
- , "description": "video game"
- , "aliases": [
- "video_game"
- ]
- , "tags": [
- "play"
- , "controller"
- , "console"
- ]
- }
-, {
- "emoji": "🃏"
- , "description": "playing card black joker"
- , "aliases": [
- "black_joker"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎴"
- , "description": "flower playing cards"
- , "aliases": [
- "flower_playing_cards"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🀄"
- , "description": "mahjong tile red dragon"
- , "aliases": [
- "mahjong"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎲"
- , "description": "game die"
- , "aliases": [
- "game_die"
- ]
- , "tags": [
- "dice"
- , "gambling"
- ]
- }
-, {
- "emoji": "🎯"
- , "description": "direct hit"
- , "aliases": [
- "dart"
- ]
- , "tags": [
- "target"
- ]
- }
-, {
- "emoji": "🏈"
- , "description": "american football"
- , "aliases": [
- "football"
- ]
- , "tags": [
- "sports"
- ]
- }
-, {
- "emoji": "🏀"
- , "description": "basketball and hoop"
- , "aliases": [
- "basketball"
- ]
- , "tags": [
- "sports"
- ]
- }
-, {
- "emoji": "⚽"
- , "description": "soccer ball"
- , "aliases": [
- "soccer"
- ]
- , "tags": [
- "sports"
- ]
- }
-, {
- "emoji": "⚾️"
- , "description": "baseball"
- , "aliases": [
- "baseball"
- ]
- , "tags": [
- "sports"
- ]
- }
-, {
- "emoji": "🎾"
- , "description": "tennis racquet and ball"
- , "aliases": [
- "tennis"
- ]
- , "tags": [
- "sports"
- ]
- }
-, {
- "emoji": "🎱"
- , "description": "billiards"
- , "aliases": [
- "8ball"
- ]
- , "tags": [
- "pool"
- , "billiards"
- ]
- }
-, {
- "emoji": "🏉"
- , "description": "rugby football"
- , "aliases": [
- "rugby_football"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎳"
- , "description": "bowling"
- , "aliases": [
- "bowling"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛳"
- , "description": "flag in hole"
- , "aliases": [
- "golf"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚵"
- , "description": "mountain bicyclist"
- , "aliases": [
- "mountain_bicyclist"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚴"
- , "description": "bicyclist"
- , "aliases": [
- "bicyclist"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏁"
- , "description": "chequered flag"
- , "aliases": [
- "checkered_flag"
- ]
- , "tags": [
- "milestone"
- , "finish"
- ]
- }
-, {
- "emoji": "🏇"
- , "description": "horse racing"
- , "aliases": [
- "horse_racing"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏆"
- , "description": "trophy"
- , "aliases": [
- "trophy"
- ]
- , "tags": [
- "award"
- , "contest"
- , "winner"
- ]
- }
-, {
- "emoji": "🎿"
- , "description": "ski and ski boot"
- , "aliases": [
- "ski"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏂"
- , "description": "snowboarder"
- , "aliases": [
- "snowboarder"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏊"
- , "description": "swimmer"
- , "aliases": [
- "swimmer"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏄"
- , "description": "surfer"
- , "aliases": [
- "surfer"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎣"
- , "description": "fishing pole and fish"
- , "aliases": [
- "fishing_pole_and_fish"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "☕"
- , "description": "hot beverage"
- , "aliases": [
- "coffee"
- ]
- , "tags": [
- "cafe"
- , "espresso"
- ]
- }
-, {
- "emoji": "🍵"
- , "description": "teacup without handle"
- , "aliases": [
- "tea"
- ]
- , "tags": [
- "green"
- , "breakfast"
- ]
- }
-, {
- "emoji": "🍶"
- , "description": "sake bottle and cup"
- , "aliases": [
- "sake"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍼"
- , "description": "baby bottle"
- , "aliases": [
- "baby_bottle"
- ]
- , "tags": [
- "milk"
- ]
- }
-, {
- "emoji": "🍺"
- , "description": "beer mug"
- , "aliases": [
- "beer"
- ]
- , "tags": [
- "drink"
- ]
- }
-, {
- "emoji": "🍻"
- , "description": "clinking beer mugs"
- , "aliases": [
- "beers"
- ]
- , "tags": [
- "drinks"
- ]
- }
-, {
- "emoji": "🍸"
- , "description": "cocktail glass"
- , "aliases": [
- "cocktail"
- ]
- , "tags": [
- "drink"
- ]
- }
-, {
- "emoji": "🍹"
- , "description": "tropical drink"
- , "aliases": [
- "tropical_drink"
- ]
- , "tags": [
- "summer"
- , "vacation"
- ]
- }
-, {
- "emoji": "🍷"
- , "description": "wine glass"
- , "aliases": [
- "wine_glass"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍴"
- , "description": "fork and knife"
- , "aliases": [
- "fork_and_knife"
- ]
- , "tags": [
- "cutlery"
- ]
- }
-, {
- "emoji": "🍕"
- , "description": "slice of pizza"
- , "aliases": [
- "pizza"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍔"
- , "description": "hamburger"
- , "aliases": [
- "hamburger"
- ]
- , "tags": [
- "burger"
- ]
- }
-, {
- "emoji": "🍟"
- , "description": "french fries"
- , "aliases": [
- "fries"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍗"
- , "description": "poultry leg"
- , "aliases": [
- "poultry_leg"
- ]
- , "tags": [
- "meat"
- , "chicken"
- ]
- }
-, {
- "emoji": "🍖"
- , "description": "meat on bone"
- , "aliases": [
- "meat_on_bone"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍝"
- , "description": "spaghetti"
- , "aliases": [
- "spaghetti"
- ]
- , "tags": [
- "pasta"
- ]
- }
-, {
- "emoji": "🍛"
- , "description": "curry and rice"
- , "aliases": [
- "curry"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍤"
- , "description": "fried shrimp"
- , "aliases": [
- "fried_shrimp"
- ]
- , "tags": [
- "tempura"
- ]
- }
-, {
- "emoji": "🍱"
- , "description": "bento box"
- , "aliases": [
- "bento"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍣"
- , "description": "sushi"
- , "aliases": [
- "sushi"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍥"
- , "description": "fish cake with swirl design"
- , "aliases": [
- "fish_cake"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍙"
- , "description": "rice ball"
- , "aliases": [
- "rice_ball"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍘"
- , "description": "rice cracker"
- , "aliases": [
- "rice_cracker"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍚"
- , "description": "cooked rice"
- , "aliases": [
- "rice"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍜"
- , "description": "steaming bowl"
- , "aliases": [
- "ramen"
- ]
- , "tags": [
- "noodle"
- ]
- }
-, {
- "emoji": "🍲"
- , "description": "pot of food"
- , "aliases": [
- "stew"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍢"
- , "description": "oden"
- , "aliases": [
- "oden"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍡"
- , "description": "dango"
- , "aliases": [
- "dango"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍳"
- , "description": "cooking"
- , "aliases": [
- "egg"
- ]
- , "tags": [
- "breakfast"
- ]
- }
-, {
- "emoji": "🍞"
- , "description": "bread"
- , "aliases": [
- "bread"
- ]
- , "tags": [
- "toast"
- ]
- }
-, {
- "emoji": "🍩"
- , "description": "doughnut"
- , "aliases": [
- "doughnut"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍮"
- , "description": "custard"
- , "aliases": [
- "custard"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍦"
- , "description": "soft ice cream"
- , "aliases": [
- "icecream"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍨"
- , "description": "ice cream"
- , "aliases": [
- "ice_cream"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍧"
- , "description": "shaved ice"
- , "aliases": [
- "shaved_ice"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎂"
- , "description": "birthday cake"
- , "aliases": [
- "birthday"
- ]
- , "tags": [
- "party"
- ]
- }
-, {
- "emoji": "🍰"
- , "description": "shortcake"
- , "aliases": [
- "cake"
- ]
- , "tags": [
- "dessert"
- ]
- }
-, {
- "emoji": "🍪"
- , "description": "cookie"
- , "aliases": [
- "cookie"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍫"
- , "description": "chocolate bar"
- , "aliases": [
- "chocolate_bar"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍬"
- , "description": "candy"
- , "aliases": [
- "candy"
- ]
- , "tags": [
- "sweet"
- ]
- }
-, {
- "emoji": "🍭"
- , "description": "lollipop"
- , "aliases": [
- "lollipop"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍯"
- , "description": "honey pot"
- , "aliases": [
- "honey_pot"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍎"
- , "description": "red apple"
- , "aliases": [
- "apple"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍏"
- , "description": "green apple"
- , "aliases": [
- "green_apple"
- ]
- , "tags": [
- "fruit"
- ]
- }
-, {
- "emoji": "🍊"
- , "description": "tangerine"
- , "aliases": [
- "tangerine"
- , "orange"
- , "mandarin"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍋"
- , "description": "lemon"
- , "aliases": [
- "lemon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍒"
- , "description": "cherries"
- , "aliases": [
- "cherries"
- ]
- , "tags": [
- "fruit"
- ]
- }
-, {
- "emoji": "🍇"
- , "description": "grapes"
- , "aliases": [
- "grapes"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍉"
- , "description": "watermelon"
- , "aliases": [
- "watermelon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍓"
- , "description": "strawberry"
- , "aliases": [
- "strawberry"
- ]
- , "tags": [
- "fruit"
- ]
- }
-, {
- "emoji": "🍑"
- , "description": "peach"
- , "aliases": [
- "peach"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍈"
- , "description": "melon"
- , "aliases": [
- "melon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍌"
- , "description": "banana"
- , "aliases": [
- "banana"
- ]
- , "tags": [
- "fruit"
- ]
- }
-, {
- "emoji": "🍐"
- , "description": "pear"
- , "aliases": [
- "pear"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍍"
- , "description": "pineapple"
- , "aliases": [
- "pineapple"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍠"
- , "description": "roasted sweet potato"
- , "aliases": [
- "sweet_potato"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🍆"
- , "description": "aubergine"
- , "aliases": [
- "eggplant"
- ]
- , "tags": [
- "aubergine"
- ]
- }
-, {
- "emoji": "🍅"
- , "description": "tomato"
- , "aliases": [
- "tomato"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌽"
- , "description": "ear of maize"
- , "aliases": [
- "corn"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏠"
- , "description": "house building"
- , "aliases": [
- "house"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏡"
- , "description": "house with garden"
- , "aliases": [
- "house_with_garden"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏫"
- , "description": "school"
- , "aliases": [
- "school"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏢"
- , "description": "office building"
- , "aliases": [
- "office"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏣"
- , "description": "japanese post office"
- , "aliases": [
- "post_office"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏥"
- , "description": "hospital"
- , "aliases": [
- "hospital"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏦"
- , "description": "bank"
- , "aliases": [
- "bank"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏪"
- , "description": "convenience store"
- , "aliases": [
- "convenience_store"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏩"
- , "description": "love hotel"
- , "aliases": [
- "love_hotel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏨"
- , "description": "hotel"
- , "aliases": [
- "hotel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💒"
- , "description": "wedding"
- , "aliases": [
- "wedding"
- ]
- , "tags": [
- "marriage"
- ]
- }
-, {
- "emoji": "⛪"
- , "description": "church"
- , "aliases": [
- "church"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏬"
- , "description": "department store"
- , "aliases": [
- "department_store"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏤"
- , "description": "european post office"
- , "aliases": [
- "european_post_office"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌇"
- , "description": "sunset over buildings"
- , "aliases": [
- "city_sunrise"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌆"
- , "description": "cityscape at dusk"
- , "aliases": [
- "city_sunset"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏯"
- , "description": "japanese castle"
- , "aliases": [
- "japanese_castle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏰"
- , "description": "european castle"
- , "aliases": [
- "european_castle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛺"
- , "description": "tent"
- , "aliases": [
- "tent"
- ]
- , "tags": [
- "camping"
- ]
- }
-, {
- "emoji": "🏭"
- , "description": "factory"
- , "aliases": [
- "factory"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🗼"
- , "description": "tokyo tower"
- , "aliases": [
- "tokyo_tower"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🗾"
- , "description": "silhouette of japan"
- , "aliases": [
- "japan"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🗻"
- , "description": "mount fuji"
- , "aliases": [
- "mount_fuji"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌄"
- , "description": "sunrise over mountains"
- , "aliases": [
- "sunrise_over_mountains"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌅"
- , "description": "sunrise"
- , "aliases": [
- "sunrise"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌃"
- , "description": "night with stars"
- , "aliases": [
- "night_with_stars"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🗽"
- , "description": "statue of liberty"
- , "aliases": [
- "statue_of_liberty"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🌉"
- , "description": "bridge at night"
- , "aliases": [
- "bridge_at_night"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎠"
- , "description": "carousel horse"
- , "aliases": [
- "carousel_horse"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎡"
- , "description": "ferris wheel"
- , "aliases": [
- "ferris_wheel"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛲"
- , "description": "fountain"
- , "aliases": [
- "fountain"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎢"
- , "description": "roller coaster"
- , "aliases": [
- "roller_coaster"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚢"
- , "description": "ship"
- , "aliases": [
- "ship"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛵"
- , "description": "sailboat"
- , "aliases": [
- "boat"
- , "sailboat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚤"
- , "description": "speedboat"
- , "aliases": [
- "speedboat"
- ]
- , "tags": [
- "ship"
- ]
- }
-, {
- "emoji": "🚣"
- , "description": "rowboat"
- , "aliases": [
- "rowboat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⚓"
- , "description": "anchor"
- , "aliases": [
- "anchor"
- ]
- , "tags": [
- "ship"
- ]
- }
-, {
- "emoji": "🚀"
- , "description": "rocket"
- , "aliases": [
- "rocket"
- ]
- , "tags": [
- "ship"
- , "launch"
- ]
- }
-, {
- "emoji": "✈️"
- , "description": "airplane"
- , "aliases": [
- "airplane"
- ]
- , "tags": [
- "flight"
- ]
- }
-, {
- "emoji": "💺"
- , "description": "seat"
- , "aliases": [
- "seat"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚁"
- , "description": "helicopter"
- , "aliases": [
- "helicopter"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚂"
- , "description": "steam locomotive"
- , "aliases": [
- "steam_locomotive"
- ]
- , "tags": [
- "train"
- ]
- }
-, {
- "emoji": "🚊"
- , "description": "tram"
- , "aliases": [
- "tram"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚉"
- , "description": "station"
- , "aliases": [
- "station"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚞"
- , "description": "mountain railway"
- , "aliases": [
- "mountain_railway"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚆"
- , "description": "train"
- , "aliases": [
- "train2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚄"
- , "description": "high-speed train"
- , "aliases": [
- "bullettrain_side"
- ]
- , "tags": [
- "train"
- ]
- }
-, {
- "emoji": "🚅"
- , "description": "high-speed train with bullet nose"
- , "aliases": [
- "bullettrain_front"
- ]
- , "tags": [
- "train"
- ]
- }
-, {
- "emoji": "🚈"
- , "description": "light rail"
- , "aliases": [
- "light_rail"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚇"
- , "description": "metro"
- , "aliases": [
- "metro"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚝"
- , "description": "monorail"
- , "aliases": [
- "monorail"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚋"
- , "description": "tram car"
- , "aliases": [
- "train"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚃"
- , "description": "railway car"
- , "aliases": [
- "railway_car"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚎"
- , "description": "trolleybus"
- , "aliases": [
- "trolleybus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚌"
- , "description": "bus"
- , "aliases": [
- "bus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚍"
- , "description": "oncoming bus"
- , "aliases": [
- "oncoming_bus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚙"
- , "description": "recreational vehicle"
- , "aliases": [
- "blue_car"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚘"
- , "description": "oncoming automobile"
- , "aliases": [
- "oncoming_automobile"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚗"
- , "description": "automobile"
- , "aliases": [
- "car"
- , "red_car"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚕"
- , "description": "taxi"
- , "aliases": [
- "taxi"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚖"
- , "description": "oncoming taxi"
- , "aliases": [
- "oncoming_taxi"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚛"
- , "description": "articulated lorry"
- , "aliases": [
- "articulated_lorry"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚚"
- , "description": "delivery truck"
- , "aliases": [
- "truck"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚨"
- , "description": "police cars revolving light"
- , "aliases": [
- "rotating_light"
- ]
- , "tags": [
- "911"
- , "emergency"
- ]
- }
-, {
- "emoji": "🚓"
- , "description": "police car"
- , "aliases": [
- "police_car"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚔"
- , "description": "oncoming police car"
- , "aliases": [
- "oncoming_police_car"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚒"
- , "description": "fire engine"
- , "aliases": [
- "fire_engine"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚑"
- , "description": "ambulance"
- , "aliases": [
- "ambulance"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚐"
- , "description": "minibus"
- , "aliases": [
- "minibus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚲"
- , "description": "bicycle"
- , "aliases": [
- "bike"
- ]
- , "tags": [
- "bicycle"
- ]
- }
-, {
- "emoji": "🚡"
- , "description": "aerial tramway"
- , "aliases": [
- "aerial_tramway"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚟"
- , "description": "suspension railway"
- , "aliases": [
- "suspension_railway"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚠"
- , "description": "mountain cableway"
- , "aliases": [
- "mountain_cableway"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚜"
- , "description": "tractor"
- , "aliases": [
- "tractor"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💈"
- , "description": "barber pole"
- , "aliases": [
- "barber"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚏"
- , "description": "bus stop"
- , "aliases": [
- "busstop"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎫"
- , "description": "ticket"
- , "aliases": [
- "ticket"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚦"
- , "description": "vertical traffic light"
- , "aliases": [
- "vertical_traffic_light"
- ]
- , "tags": [
- "semaphore"
- ]
- }
-, {
- "emoji": "🚥"
- , "description": "horizontal traffic light"
- , "aliases": [
- "traffic_light"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⚠️"
- , "description": "warning sign"
- , "aliases": [
- "warning"
- ]
- , "tags": [
- "wip"
- ]
- }
-, {
- "emoji": "🚧"
- , "description": "construction sign"
- , "aliases": [
- "construction"
- ]
- , "tags": [
- "wip"
- ]
- }
-, {
- "emoji": "🔰"
- , "description": "japanese symbol for beginner"
- , "aliases": [
- "beginner"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛽"
- , "description": "fuel pump"
- , "aliases": [
- "fuelpump"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏮"
- , "description": "izakaya lantern"
- , "aliases": [
- "izakaya_lantern"
- , "lantern"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎰"
- , "description": "slot machine"
- , "aliases": [
- "slot_machine"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♨️"
- , "description": "hot springs"
- , "aliases": [
- "hotsprings"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🗿"
- , "description": "moyai"
- , "aliases": [
- "moyai"
- ]
- , "tags": [
- "stone"
- ]
- }
-, {
- "emoji": "🎪"
- , "description": "circus tent"
- , "aliases": [
- "circus_tent"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🎭"
- , "description": "performing arts"
- , "aliases": [
- "performing_arts"
- ]
- , "tags": [
- "theater"
- , "drama"
- ]
- }
-, {
- "emoji": "📍"
- , "description": "round pushpin"
- , "aliases": [
- "round_pushpin"
- ]
- , "tags": [
- "location"
- ]
- }
-, {
- "emoji": "🚩"
- , "description": "triangular flag on post"
- , "aliases": [
- "triangular_flag_on_post"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🇯🇵"
- , "description": "regional indicator symbol letter j + regional indicator symbol letter p"
- , "aliases": [
- "jp"
- ]
- , "tags": [
- "japan"
- ]
- }
-, {
- "emoji": "🇰🇷"
- , "description": "regional indicator symbol letter k + regional indicator symbol letter r"
- , "aliases": [
- "kr"
- ]
- , "tags": [
- "korea"
- ]
- }
-, {
- "emoji": "🇩🇪"
- , "description": "regional indicator symbol letter d + regional indicator symbol letter e"
- , "aliases": [
- "de"
- ]
- , "tags": [
- "flag"
- , "germany"
- ]
- }
-, {
- "emoji": "🇨🇳"
- , "description": "regional indicator symbol letter c + regional indicator symbol letter n"
- , "aliases": [
- "cn"
- ]
- , "tags": [
- "china"
- ]
- }
-, {
- "emoji": "🇺🇸"
- , "description": "regional indicator symbol letter u + regional indicator symbol letter s"
- , "aliases": [
- "us"
- ]
- , "tags": [
- "flag"
- , "united"
- , "america"
- ]
- }
-, {
- "emoji": "🇫🇷"
- , "description": "regional indicator symbol letter f + regional indicator symbol letter r"
- , "aliases": [
- "fr"
- ]
- , "tags": [
- "france"
- , "french"
- ]
- }
-, {
- "emoji": "🇪🇸"
- , "description": "regional indicator symbol letter e + regional indicator symbol letter s"
- , "aliases": [
- "es"
- ]
- , "tags": [
- "spain"
- ]
- }
-, {
- "emoji": "🇮🇹"
- , "description": "regional indicator symbol letter i + regional indicator symbol letter t"
- , "aliases": [
- "it"
- ]
- , "tags": [
- "italy"
- ]
- }
-, {
- "emoji": "🇷🇺"
- , "description": "regional indicator symbol letter r + regional indicator symbol letter u"
- , "aliases": [
- "ru"
- ]
- , "tags": [
- "russia"
- ]
- }
-, {
- "emoji": "🇬🇧"
- , "description": "regional indicator symbol letter g + regional indicator symbol letter b"
- , "aliases": [
- "gb"
- , "uk"
- ]
- , "tags": [
- "flag"
- , "british"
- ]
- }
-, {
- "emoji": "1️⃣"
- , "description": "digit one + combining enclosing keycap"
- , "aliases": [
- "one"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "2️⃣"
- , "description": "digit two + combining enclosing keycap"
- , "aliases": [
- "two"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "3️⃣"
- , "description": "digit three + combining enclosing keycap"
- , "aliases": [
- "three"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "4️⃣"
- , "description": "digit four + combining enclosing keycap"
- , "aliases": [
- "four"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "5️⃣"
- , "description": "digit five + combining enclosing keycap"
- , "aliases": [
- "five"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "6️⃣"
- , "description": "digit six + combining enclosing keycap"
- , "aliases": [
- "six"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "7️⃣"
- , "description": "digit seven + combining enclosing keycap"
- , "aliases": [
- "seven"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "8️⃣"
- , "description": "digit eight + combining enclosing keycap"
- , "aliases": [
- "eight"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "9️⃣"
- , "description": "digit nine + combining enclosing keycap"
- , "aliases": [
- "nine"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "0️⃣"
- , "description": "digit zero + combining enclosing keycap"
- , "aliases": [
- "zero"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔟"
- , "description": "keycap ten"
- , "aliases": [
- "keycap_ten"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔢"
- , "description": "input symbol for numbers"
- , "aliases": [
- "1234"
- ]
- , "tags": [
- "numbers"
- ]
- }
-, {
- "emoji": "#️⃣"
- , "description": "number sign + combining enclosing keycap"
- , "aliases": [
- "hash"
- ]
- , "tags": [
- "number"
- ]
- }
-, {
- "emoji": "🔣"
- , "description": "input symbol for symbols"
- , "aliases": [
- "symbols"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⬆️"
- , "description": "upwards black arrow"
- , "aliases": [
- "arrow_up"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⬇️"
- , "description": "downwards black arrow"
- , "aliases": [
- "arrow_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⬅️"
- , "description": "leftwards black arrow"
- , "aliases": [
- "arrow_left"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➡️"
- , "description": "black rightwards arrow"
- , "aliases": [
- "arrow_right"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔠"
- , "description": "input symbol for latin capital letters"
- , "aliases": [
- "capital_abcd"
- ]
- , "tags": [
- "letters"
- ]
- }
-, {
- "emoji": "🔡"
- , "description": "input symbol for latin small letters"
- , "aliases": [
- "abcd"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔤"
- , "description": "input symbol for latin letters"
- , "aliases": [
- "abc"
- ]
- , "tags": [
- "alphabet"
- ]
- }
-, {
- "emoji": "↗️"
- , "description": "north east arrow"
- , "aliases": [
- "arrow_upper_right"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↖️"
- , "description": "north west arrow"
- , "aliases": [
- "arrow_upper_left"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↘️"
- , "description": "south east arrow"
- , "aliases": [
- "arrow_lower_right"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↙️"
- , "description": "south west arrow"
- , "aliases": [
- "arrow_lower_left"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↔️"
- , "description": "left right arrow"
- , "aliases": [
- "left_right_arrow"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↕️"
- , "description": "up down arrow"
- , "aliases": [
- "arrow_up_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔄"
- , "description": "anticlockwise downwards and upwards open circle arrows"
- , "aliases": [
- "arrows_counterclockwise"
- ]
- , "tags": [
- "sync"
- ]
- }
-, {
- "emoji": "◀️"
- , "description": "black left-pointing triangle"
- , "aliases": [
- "arrow_backward"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "▶️"
- , "description": "black right-pointing triangle"
- , "aliases": [
- "arrow_forward"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔼"
- , "description": "up-pointing small red triangle"
- , "aliases": [
- "arrow_up_small"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔽"
- , "description": "down-pointing small red triangle"
- , "aliases": [
- "arrow_down_small"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "↩️"
- , "description": "leftwards arrow with hook"
- , "aliases": [
- "leftwards_arrow_with_hook"
- ]
- , "tags": [
- "return"
- ]
- }
-, {
- "emoji": "↪️"
- , "description": "rightwards arrow with hook"
- , "aliases": [
- "arrow_right_hook"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "ℹ️"
- , "description": "information source"
- , "aliases": [
- "information_source"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⏪"
- , "description": "black left-pointing double triangle"
- , "aliases": [
- "rewind"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⏩"
- , "description": "black right-pointing double triangle"
- , "aliases": [
- "fast_forward"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⏫"
- , "description": "black up-pointing double triangle"
- , "aliases": [
- "arrow_double_up"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⏬"
- , "description": "black down-pointing double triangle"
- , "aliases": [
- "arrow_double_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⤵️"
- , "description": "arrow pointing rightwards then curving downwards"
- , "aliases": [
- "arrow_heading_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⤴️"
- , "description": "arrow pointing rightwards then curving upwards"
- , "aliases": [
- "arrow_heading_up"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆗"
- , "description": "squared ok"
- , "aliases": [
- "ok"
- ]
- , "tags": [
- "yes"
- ]
- }
-, {
- "emoji": "🔀"
- , "description": "twisted rightwards arrows"
- , "aliases": [
- "twisted_rightwards_arrows"
- ]
- , "tags": [
- "shuffle"
- ]
- }
-, {
- "emoji": "🔁"
- , "description": "clockwise rightwards and leftwards open circle arrows"
- , "aliases": [
- "repeat"
- ]
- , "tags": [
- "loop"
- ]
- }
-, {
- "emoji": "🔂"
- , "description": "clockwise rightwards and leftwards open circle arrows with circled one overlay"
- , "aliases": [
- "repeat_one"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆕"
- , "description": "squared new"
- , "aliases": [
- "new"
- ]
- , "tags": [
- "fresh"
- ]
- }
-, {
- "emoji": "🆙"
- , "description": "squared up with exclamation mark"
- , "aliases": [
- "up"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆒"
- , "description": "squared cool"
- , "aliases": [
- "cool"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆓"
- , "description": "squared free"
- , "aliases": [
- "free"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆖"
- , "description": "squared ng"
- , "aliases": [
- "ng"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📶"
- , "description": "antenna with bars"
- , "aliases": [
- "signal_strength"
- ]
- , "tags": [
- "wifi"
- ]
- }
-, {
- "emoji": "🎦"
- , "description": "cinema"
- , "aliases": [
- "cinema"
- ]
- , "tags": [
- "film"
- , "movie"
- ]
- }
-, {
- "emoji": "🈁"
- , "description": "squared katakana koko"
- , "aliases": [
- "koko"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈯"
- , "description": "squared cjk unified ideograph-6307"
- , "aliases": [
- "u6307"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈳"
- , "description": "squared cjk unified ideograph-7a7a"
- , "aliases": [
- "u7a7a"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈵"
- , "description": "squared cjk unified ideograph-6e80"
- , "aliases": [
- "u6e80"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈴"
- , "description": "squared cjk unified ideograph-5408"
- , "aliases": [
- "u5408"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈲"
- , "description": "squared cjk unified ideograph-7981"
- , "aliases": [
- "u7981"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🉐"
- , "description": "circled ideograph advantage"
- , "aliases": [
- "ideograph_advantage"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈹"
- , "description": "squared cjk unified ideograph-5272"
- , "aliases": [
- "u5272"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈺"
- , "description": "squared cjk unified ideograph-55b6"
- , "aliases": [
- "u55b6"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈶"
- , "description": "squared cjk unified ideograph-6709"
- , "aliases": [
- "u6709"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈚"
- , "description": "squared cjk unified ideograph-7121"
- , "aliases": [
- "u7121"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚻"
- , "description": "restroom"
- , "aliases": [
- "restroom"
- ]
- , "tags": [
- "toilet"
- ]
- }
-, {
- "emoji": "🚹"
- , "description": "mens symbol"
- , "aliases": [
- "mens"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚺"
- , "description": "womens symbol"
- , "aliases": [
- "womens"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚼"
- , "description": "baby symbol"
- , "aliases": [
- "baby_symbol"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚾"
- , "description": "water closet"
- , "aliases": [
- "wc"
- ]
- , "tags": [
- "toilet"
- , "restroom"
- ]
- }
-, {
- "emoji": "🚰"
- , "description": "potable water symbol"
- , "aliases": [
- "potable_water"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚮"
- , "description": "put litter in its place symbol"
- , "aliases": [
- "put_litter_in_its_place"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🅿️"
- , "description": "negative squared latin capital letter p"
- , "aliases": [
- "parking"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♿"
- , "description": "wheelchair symbol"
- , "aliases": [
- "wheelchair"
- ]
- , "tags": [
- "accessibility"
- ]
- }
-, {
- "emoji": "🚭"
- , "description": "no smoking symbol"
- , "aliases": [
- "no_smoking"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈷️"
- , "description": "squared cjk unified ideograph-6708"
- , "aliases": [
- "u6708"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈸"
- , "description": "squared cjk unified ideograph-7533"
- , "aliases": [
- "u7533"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🈂️"
- , "description": "squared katakana sa"
- , "aliases": [
- "sa"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "Ⓜ️"
- , "description": "circled latin capital letter m"
- , "aliases": [
- "m"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🛂"
- , "description": "passport control"
- , "aliases": [
- "passport_control"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🛄"
- , "description": "baggage claim"
- , "aliases": [
- "baggage_claim"
- ]
- , "tags": [
- "airport"
- ]
- }
-, {
- "emoji": "🛅"
- , "description": "left luggage"
- , "aliases": [
- "left_luggage"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🛃"
- , "description": "customs"
- , "aliases": [
- "customs"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🉑"
- , "description": "circled ideograph accept"
- , "aliases": [
- "accept"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "㊙️"
- , "description": "circled ideograph secret"
- , "aliases": [
- "secret"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "㊗️"
- , "description": "circled ideograph congratulation"
- , "aliases": [
- "congratulations"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆑"
- , "description": "squared cl"
- , "aliases": [
- "cl"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆘"
- , "description": "squared sos"
- , "aliases": [
- "sos"
- ]
- , "tags": [
- "help"
- , "emergency"
- ]
- }
-, {
- "emoji": "🆔"
- , "description": "squared id"
- , "aliases": [
- "id"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚫"
- , "description": "no entry sign"
- , "aliases": [
- "no_entry_sign"
- ]
- , "tags": [
- "block"
- , "forbidden"
- ]
- }
-, {
- "emoji": "🔞"
- , "description": "no one under eighteen symbol"
- , "aliases": [
- "underage"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📵"
- , "description": "no mobile phones"
- , "aliases": [
- "no_mobile_phones"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚯"
- , "description": "do not litter symbol"
- , "aliases": [
- "do_not_litter"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚱"
- , "description": "non-potable water symbol"
- , "aliases": [
- "non-potable_water"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚳"
- , "description": "no bicycles"
- , "aliases": [
- "no_bicycles"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚷"
- , "description": "no pedestrians"
- , "aliases": [
- "no_pedestrians"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🚸"
- , "description": "children crossing"
- , "aliases": [
- "children_crossing"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛔"
- , "description": "no entry"
- , "aliases": [
- "no_entry"
- ]
- , "tags": [
- "limit"
- ]
- }
-, {
- "emoji": "✳️"
- , "description": "eight spoked asterisk"
- , "aliases": [
- "eight_spoked_asterisk"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "❇️"
- , "description": "sparkle"
- , "aliases": [
- "sparkle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "❎"
- , "description": "negative squared cross mark"
- , "aliases": [
- "negative_squared_cross_mark"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✅"
- , "description": "white heavy check mark"
- , "aliases": [
- "white_check_mark"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✴️"
- , "description": "eight pointed black star"
- , "aliases": [
- "eight_pointed_black_star"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💟"
- , "description": "heart decoration"
- , "aliases": [
- "heart_decoration"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆚"
- , "description": "squared vs"
- , "aliases": [
- "vs"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📳"
- , "description": "vibration mode"
- , "aliases": [
- "vibration_mode"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "📴"
- , "description": "mobile phone off"
- , "aliases": [
- "mobile_phone_off"
- ]
- , "tags": [
- "mute"
- , "off"
- ]
- }
-, {
- "emoji": "🅰️"
- , "description": "negative squared latin capital letter a"
- , "aliases": [
- "a"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🅱️"
- , "description": "negative squared latin capital letter b"
- , "aliases": [
- "b"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🆎"
- , "description": "negative squared ab"
- , "aliases": [
- "ab"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🅾️"
- , "description": "negative squared latin capital letter o"
- , "aliases": [
- "o2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💠"
- , "description": "diamond shape with a dot inside"
- , "aliases": [
- "diamond_shape_with_a_dot_inside"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➿"
- , "description": "double curly loop"
- , "aliases": [
- "loop"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♻️"
- , "description": "black universal recycling symbol"
- , "aliases": [
- "recycle"
- ]
- , "tags": [
- "environment"
- , "green"
- ]
- }
-, {
- "emoji": "♈"
- , "description": "aries"
- , "aliases": [
- "aries"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♉"
- , "description": "taurus"
- , "aliases": [
- "taurus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♊"
- , "description": "gemini"
- , "aliases": [
- "gemini"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♋"
- , "description": "cancer"
- , "aliases": [
- "cancer"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♌"
- , "description": "leo"
- , "aliases": [
- "leo"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♍"
- , "description": "virgo"
- , "aliases": [
- "virgo"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♎"
- , "description": "libra"
- , "aliases": [
- "libra"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♏"
- , "description": "scorpius"
- , "aliases": [
- "scorpius"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♐"
- , "description": "sagittarius"
- , "aliases": [
- "sagittarius"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♑"
- , "description": "capricorn"
- , "aliases": [
- "capricorn"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♒"
- , "description": "aquarius"
- , "aliases": [
- "aquarius"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♓"
- , "description": "pisces"
- , "aliases": [
- "pisces"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⛎"
- , "description": "ophiuchus"
- , "aliases": [
- "ophiuchus"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔯"
- , "description": "six pointed star with middle dot"
- , "aliases": [
- "six_pointed_star"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🏧"
- , "description": "automated teller machine"
- , "aliases": [
- "atm"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💹"
- , "description": "chart with upwards trend and yen sign"
- , "aliases": [
- "chart"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💲"
- , "description": "heavy dollar sign"
- , "aliases": [
- "heavy_dollar_sign"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💱"
- , "description": "currency exchange"
- , "aliases": [
- "currency_exchange"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "©️"
- , "description": "copyright sign"
- , "aliases": [
- "copyright"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "®️"
- , "description": "registered sign"
- , "aliases": [
- "registered"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "™️"
- , "description": "trade mark sign"
- , "aliases": [
- "tm"
- ]
- , "tags": [
- "trademark"
- ]
- }
-, {
- "emoji": "❌"
- , "description": "cross mark"
- , "aliases": [
- "x"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "‼️"
- , "description": "double exclamation mark"
- , "aliases": [
- "bangbang"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⁉️"
- , "description": "exclamation question mark"
- , "aliases": [
- "interrobang"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "❗"
- , "description": "heavy exclamation mark symbol"
- , "aliases": [
- "exclamation"
- , "heavy_exclamation_mark"
- ]
- , "tags": [
- "bang"
- ]
- }
-, {
- "emoji": "❓"
- , "description": "black question mark ornament"
- , "aliases": [
- "question"
- ]
- , "tags": [
- "confused"
- ]
- }
-, {
- "emoji": "❕"
- , "description": "white exclamation mark ornament"
- , "aliases": [
- "grey_exclamation"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "❔"
- , "description": "white question mark ornament"
- , "aliases": [
- "grey_question"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⭕"
- , "description": "heavy large circle"
- , "aliases": [
- "o"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔝"
- , "description": "top with upwards arrow above"
- , "aliases": [
- "top"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔚"
- , "description": "end with leftwards arrow above"
- , "aliases": [
- "end"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔙"
- , "description": "back with leftwards arrow above"
- , "aliases": [
- "back"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔛"
- , "description": "on with exclamation mark with left right arrow above"
- , "aliases": [
- "on"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔜"
- , "description": "soon with rightwards arrow above"
- , "aliases": [
- "soon"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔃"
- , "description": "clockwise downwards and upwards open circle arrows"
- , "aliases": [
- "arrows_clockwise"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕛"
- , "description": "clock face twelve oclock"
- , "aliases": [
- "clock12"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕧"
- , "description": "clock face twelve-thirty"
- , "aliases": [
- "clock1230"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕐"
- , "description": "clock face one oclock"
- , "aliases": [
- "clock1"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕜"
- , "description": "clock face one-thirty"
- , "aliases": [
- "clock130"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕑"
- , "description": "clock face two oclock"
- , "aliases": [
- "clock2"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕝"
- , "description": "clock face two-thirty"
- , "aliases": [
- "clock230"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕒"
- , "description": "clock face three oclock"
- , "aliases": [
- "clock3"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕞"
- , "description": "clock face three-thirty"
- , "aliases": [
- "clock330"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕓"
- , "description": "clock face four oclock"
- , "aliases": [
- "clock4"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕟"
- , "description": "clock face four-thirty"
- , "aliases": [
- "clock430"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕔"
- , "description": "clock face five oclock"
- , "aliases": [
- "clock5"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕠"
- , "description": "clock face five-thirty"
- , "aliases": [
- "clock530"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕕"
- , "description": "clock face six oclock"
- , "aliases": [
- "clock6"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕖"
- , "description": "clock face seven oclock"
- , "aliases": [
- "clock7"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕗"
- , "description": "clock face eight oclock"
- , "aliases": [
- "clock8"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕘"
- , "description": "clock face nine oclock"
- , "aliases": [
- "clock9"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕙"
- , "description": "clock face ten oclock"
- , "aliases": [
- "clock10"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕚"
- , "description": "clock face eleven oclock"
- , "aliases": [
- "clock11"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕡"
- , "description": "clock face six-thirty"
- , "aliases": [
- "clock630"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕢"
- , "description": "clock face seven-thirty"
- , "aliases": [
- "clock730"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕣"
- , "description": "clock face eight-thirty"
- , "aliases": [
- "clock830"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕤"
- , "description": "clock face nine-thirty"
- , "aliases": [
- "clock930"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕥"
- , "description": "clock face ten-thirty"
- , "aliases": [
- "clock1030"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🕦"
- , "description": "clock face eleven-thirty"
- , "aliases": [
- "clock1130"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "✖️"
- , "description": "heavy multiplication x"
- , "aliases": [
- "heavy_multiplication_x"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➕"
- , "description": "heavy plus sign"
- , "aliases": [
- "heavy_plus_sign"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➖"
- , "description": "heavy minus sign"
- , "aliases": [
- "heavy_minus_sign"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➗"
- , "description": "heavy division sign"
- , "aliases": [
- "heavy_division_sign"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♠️"
- , "description": "black spade suit"
- , "aliases": [
- "spades"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♥️"
- , "description": "black heart suit"
- , "aliases": [
- "hearts"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♣️"
- , "description": "black club suit"
- , "aliases": [
- "clubs"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "♦️"
- , "description": "black diamond suit"
- , "aliases": [
- "diamonds"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💮"
- , "description": "white flower"
- , "aliases": [
- "white_flower"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "💯"
- , "description": "hundred points symbol"
- , "aliases": [
- "100"
- ]
- , "tags": [
- "score"
- , "perfect"
- ]
- }
-, {
- "emoji": "✔️"
- , "description": "heavy check mark"
- , "aliases": [
- "heavy_check_mark"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "☑️"
- , "description": "ballot box with check"
- , "aliases": [
- "ballot_box_with_check"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔘"
- , "description": "radio button"
- , "aliases": [
- "radio_button"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔗"
- , "description": "link symbol"
- , "aliases": [
- "link"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "➰"
- , "description": "curly loop"
- , "aliases": [
- "curly_loop"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "〰️"
- , "description": "wavy dash"
- , "aliases": [
- "wavy_dash"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "〽️"
- , "description": "part alternation mark"
- , "aliases": [
- "part_alternation_mark"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔱"
- , "description": "trident emblem"
- , "aliases": [
- "trident"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "◼️"
- , "description": "black medium square"
- , "aliases": [
- "black_medium_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "◻️"
- , "description": "white medium square"
- , "aliases": [
- "white_medium_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "◾"
- , "description": "black medium small square"
- , "aliases": [
- "black_medium_small_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "◽"
- , "description": "white medium small square"
- , "aliases": [
- "white_medium_small_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "▪️"
- , "description": "black small square"
- , "aliases": [
- "black_small_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "▫️"
- , "description": "white small square"
- , "aliases": [
- "white_small_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔺"
- , "description": "up-pointing red triangle"
- , "aliases": [
- "small_red_triangle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔲"
- , "description": "black square button"
- , "aliases": [
- "black_square_button"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔳"
- , "description": "white square button"
- , "aliases": [
- "white_square_button"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⚫"
- , "description": "medium black circle"
- , "aliases": [
- "black_circle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⚪"
- , "description": "medium white circle"
- , "aliases": [
- "white_circle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔴"
- , "description": "large red circle"
- , "aliases": [
- "red_circle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔵"
- , "description": "large blue circle"
- , "aliases": [
- "large_blue_circle"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔻"
- , "description": "down-pointing red triangle"
- , "aliases": [
- "small_red_triangle_down"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⬜"
- , "description": "white large square"
- , "aliases": [
- "white_large_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "⬛"
- , "description": "black large square"
- , "aliases": [
- "black_large_square"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔶"
- , "description": "large orange diamond"
- , "aliases": [
- "large_orange_diamond"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔷"
- , "description": "large blue diamond"
- , "aliases": [
- "large_blue_diamond"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔸"
- , "description": "small orange diamond"
- , "aliases": [
- "small_orange_diamond"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🔹"
- , "description": "small blue diamond"
- , "aliases": [
- "small_blue_diamond"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🇨🇦"
- , "description": "regional indicator symbol letter c + regional indicator symbol letter a"
- , "aliases": [
- "ca",
- "eh"
- ]
- , "tags": [
- "canada"
- ]
- }
-, {
- "emoji": "🇵🇰"
- , "description": "regional indicator symbol letter p + regional indicator symbol letter k"
- , "aliases": [
- "pk"
- ]
- , "tags": [
- "pakistan"
- ]
- }
-, {
- "emoji": "🇿🇦"
- , "description": "regional indicator symbol letter z + regional indicator symbol letter a"
- , "aliases": [
- "za"
- ]
- , "tags": [
- "south_africa"
- ]
- }
-, {
- "emoji": "🙂"
- , "description": "slightly smiling face"
- , "aliases": [
- "slightly_smiling_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙁"
- , "description": "slightly frowning face"
- , "aliases": [
- "slightly_frowning_face"
- ]
- , "tags": [
- ]
- }
-, {
- "emoji": "🙃"
- , "description": "upside-down face"
- , "aliases": [
- "upside_down_face"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "mm",
- "mattermost"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "basecamp"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "basecampy"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "bowtie"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "feelsgood"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "finnadie"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "fu"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "goberserk"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "godmode"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "hurtrealbad"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "metal"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "neckbeard"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "octocat"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "rage1"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "rage2"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "rage3"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "rage4"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "shipit"
- , "squirrel"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "suspect"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "taco"
- ]
- , "tags": [
- ]
- }
-, {
- "aliases": [
- "trollface"
- ]
- , "tags": [
- ]
- }
-]
+[["smile",{"name":"smile","unicode":"1f604"}],["smiley",{"name":"smiley","unicode":"1f603"}],["grinning",{"name":"grinning","unicode":"1f600"}],["blush",{"name":"blush","unicode":"1f60a"}],["relaxed",{"name":"relaxed","unicode":"263a"}],["wink",{"name":"wink","unicode":"1f609"}],["heart_eyes",{"name":"heart_eyes","unicode":"1f60d"}],["kissing_heart",{"name":"kissing_heart","unicode":"1f618"}],["kissing_closed_eyes",{"name":"kissing_closed_eyes","unicode":"1f61a"}],["kissing",{"name":"kissing","unicode":"1f617"}],["kissing_smiling_eyes",{"name":"kissing_smiling_eyes","unicode":"1f619"}],["stuck_out_tongue_winking_eye",{"name":"stuck_out_tongue_winking_eye","unicode":"1f61c"}],["stuck_out_tongue_closed_eyes",{"name":"stuck_out_tongue_closed_eyes","unicode":"1f61d"}],["stuck_out_tongue",{"name":"stuck_out_tongue","unicode":"1f61b"}],["flushed",{"name":"flushed","unicode":"1f633"}],["grin",{"name":"grin","unicode":"1f601"}],["pensive",{"name":"pensive","unicode":"1f614"}],["relieved",{"name":"relieved","unicode":"1f60c"}],["unamused",{"name":"unamused","unicode":"1f612"}],["disappointed",{"name":"disappointed","unicode":"1f61e"}],["persevere",{"name":"persevere","unicode":"1f623"}],["cry",{"name":"cry","unicode":"1f622"}],["joy",{"name":"joy","unicode":"1f602"}],["sob",{"name":"sob","unicode":"1f62d"}],["sleepy",{"name":"sleepy","unicode":"1f62a"}],["disappointed_relieved",{"name":"disappointed_relieved","unicode":"1f625"}],["cold_sweat",{"name":"cold_sweat","unicode":"1f630"}],["sweat_smile",{"name":"sweat_smile","unicode":"1f605"}],["sweat",{"name":"sweat","unicode":"1f613"}],["weary",{"name":"weary","unicode":"1f629"}],["tired_face",{"name":"tired_face","unicode":"1f62b"}],["fearful",{"name":"fearful","unicode":"1f628"}],["scream",{"name":"scream","unicode":"1f631"}],["angry",{"name":"angry","unicode":"1f620"}],["rage",{"name":"rage","unicode":"1f621"}],["pout",{"name":"pout","unicode":"1f621"}],["triumph",{"name":"triumph","unicode":"1f624"}],["confounded",{"name":"confounded","unicode":"1f616"}],["laughing",{"name":"laughing","unicode":"1f606"}],["satisfied",{"name":"satisfied","unicode":"1f606"}],["yum",{"name":"yum","unicode":"1f60b"}],["mask",{"name":"mask","unicode":"1f637"}],["sunglasses",{"name":"sunglasses","unicode":"1f60e"}],["sleeping",{"name":"sleeping","unicode":"1f634"}],["dizzy_face",{"name":"dizzy_face","unicode":"1f635"}],["astonished",{"name":"astonished","unicode":"1f632"}],["worried",{"name":"worried","unicode":"1f61f"}],["frowning",{"name":"frowning","unicode":"1f626"}],["anguished",{"name":"anguished","unicode":"1f627"}],["smiling_imp",{"name":"smiling_imp","unicode":"1f608"}],["imp",{"name":"imp","unicode":"1f47f"}],["open_mouth",{"name":"open_mouth","unicode":"1f62e"}],["grimacing",{"name":"grimacing","unicode":"1f62c"}],["neutral_face",{"name":"neutral_face","unicode":"1f610"}],["confused",{"name":"confused","unicode":"1f615"}],["hushed",{"name":"hushed","unicode":"1f62f"}],["no_mouth",{"name":"no_mouth","unicode":"1f636"}],["innocent",{"name":"innocent","unicode":"1f607"}],["smirk",{"name":"smirk","unicode":"1f60f"}],["expressionless",{"name":"expressionless","unicode":"1f611"}],["man_with_gua_pi_mao",{"name":"man_with_gua_pi_mao","unicode":"1f472"}],["man_with_turban",{"name":"man_with_turban","unicode":"1f473"}],["cop",{"name":"cop","unicode":"1f46e"}],["construction_worker",{"name":"construction_worker","unicode":"1f477"}],["guardsman",{"name":"guardsman","unicode":"1f482"}],["baby",{"name":"baby","unicode":"1f476"}],["boy",{"name":"boy","unicode":"1f466"}],["girl",{"name":"girl","unicode":"1f467"}],["man",{"name":"man","unicode":"1f468"}],["woman",{"name":"woman","unicode":"1f469"}],["older_man",{"name":"older_man","unicode":"1f474"}],["older_woman",{"name":"older_woman","unicode":"1f475"}],["person_with_blond_hair",{"name":"person_with_blond_hair","unicode":"1f471"}],["angel",{"name":"angel","unicode":"1f47c"}],["princess",{"name":"princess","unicode":"1f478"}],["smiley_cat",{"name":"smiley_cat","unicode":"1f63a"}],["smile_cat",{"name":"smile_cat","unicode":"1f638"}],["heart_eyes_cat",{"name":"heart_eyes_cat","unicode":"1f63b"}],["kissing_cat",{"name":"kissing_cat","unicode":"1f63d"}],["smirk_cat",{"name":"smirk_cat","unicode":"1f63c"}],["scream_cat",{"name":"scream_cat","unicode":"1f640"}],["crying_cat_face",{"name":"crying_cat_face","unicode":"1f63f"}],["joy_cat",{"name":"joy_cat","unicode":"1f639"}],["pouting_cat",{"name":"pouting_cat","unicode":"1f63e"}],["japanese_ogre",{"name":"japanese_ogre","unicode":"1f479"}],["japanese_goblin",{"name":"japanese_goblin","unicode":"1f47a"}],["see_no_evil",{"name":"see_no_evil","unicode":"1f648"}],["hear_no_evil",{"name":"hear_no_evil","unicode":"1f649"}],["speak_no_evil",{"name":"speak_no_evil","unicode":"1f64a"}],["skull",{"name":"skull","unicode":"1f480"}],["alien",{"name":"alien","unicode":"1f47d"}],["hankey",{"name":"hankey","unicode":"1f4a9"}],["poop",{"name":"poop","unicode":"1f4a9"}],["shit",{"name":"shit","unicode":"1f4a9"}],["fire",{"name":"fire","unicode":"1f525"}],["sparkles",{"name":"sparkles","unicode":"2728"}],["star2",{"name":"star2","unicode":"1f31f"}],["dizzy",{"name":"dizzy","unicode":"1f4ab"}],["boom",{"name":"boom","unicode":"1f4a5"}],["collision",{"name":"collision","unicode":"1f4a5"}],["anger",{"name":"anger","unicode":"1f4a2"}],["sweat_drops",{"name":"sweat_drops","unicode":"1f4a6"}],["droplet",{"name":"droplet","unicode":"1f4a7"}],["zzz",{"name":"zzz","unicode":"1f4a4"}],["dash",{"name":"dash","unicode":"1f4a8"}],["ear",{"name":"ear","unicode":"1f442"}],["eyes",{"name":"eyes","unicode":"1f440"}],["nose",{"name":"nose","unicode":"1f443"}],["tongue",{"name":"tongue","unicode":"1f445"}],["lips",{"name":"lips","unicode":"1f444"}],["+1",{"name":"+1","unicode":"1f44d"}],["thumbsup",{"name":"thumbsup","unicode":"1f44d"}],["-1",{"name":"-1","unicode":"1f44e"}],["thumbsdown",{"name":"thumbsdown","unicode":"1f44e"}],["ok_hand",{"name":"ok_hand","unicode":"1f44c"}],["facepunch",{"name":"facepunch","unicode":"1f44a"}],["punch",{"name":"punch","unicode":"1f44a"}],["fist",{"name":"fist","unicode":"270a"}],["v",{"name":"v","unicode":"270c"}],["wave",{"name":"wave","unicode":"1f44b"}],["hand",{"name":"hand","unicode":"270b"}],["raised_hand",{"name":"raised_hand","unicode":"270b"}],["open_hands",{"name":"open_hands","unicode":"1f450"}],["point_up_2",{"name":"point_up_2","unicode":"1f446"}],["point_down",{"name":"point_down","unicode":"1f447"}],["point_right",{"name":"point_right","unicode":"1f449"}],["point_left",{"name":"point_left","unicode":"1f448"}],["raised_hands",{"name":"raised_hands","unicode":"1f64c"}],["pray",{"name":"pray","unicode":"1f64f"}],["point_up",{"name":"point_up","unicode":"261d"}],["clap",{"name":"clap","unicode":"1f44f"}],["muscle",{"name":"muscle","unicode":"1f4aa"}],["walking",{"name":"walking","unicode":"1f6b6"}],["runner",{"name":"runner","unicode":"1f3c3"}],["running",{"name":"running","unicode":"1f3c3"}],["dancer",{"name":"dancer","unicode":"1f483"}],["couple",{"name":"couple","unicode":"1f46b"}],["family",{"name":"family","unicode":"1f46a"}],["two_men_holding_hands",{"name":"two_men_holding_hands","unicode":"1f46c"}],["two_women_holding_hands",{"name":"two_women_holding_hands","unicode":"1f46d"}],["couplekiss",{"name":"couplekiss","unicode":"1f48f"}],["couple_with_heart",{"name":"couple_with_heart","unicode":"1f491"}],["dancers",{"name":"dancers","unicode":"1f46f"}],["ok_woman",{"name":"ok_woman","unicode":"1f646"}],["no_good",{"name":"no_good","unicode":"1f645"}],["ng_woman",{"name":"ng_woman","unicode":"1f645"}],["information_desk_person",{"name":"information_desk_person","unicode":"1f481"}],["raising_hand",{"name":"raising_hand","unicode":"1f64b"}],["massage",{"name":"massage","unicode":"1f486"}],["haircut",{"name":"haircut","unicode":"1f487"}],["nail_care",{"name":"nail_care","unicode":"1f485"}],["bride_with_veil",{"name":"bride_with_veil","unicode":"1f470"}],["person_with_pouting_face",{"name":"person_with_pouting_face","unicode":"1f64e"}],["person_frowning",{"name":"person_frowning","unicode":"1f64d"}],["bow",{"name":"bow","unicode":"1f647"}],["tophat",{"name":"tophat","unicode":"1f3a9"}],["crown",{"name":"crown","unicode":"1f451"}],["womans_hat",{"name":"womans_hat","unicode":"1f452"}],["athletic_shoe",{"name":"athletic_shoe","unicode":"1f45f"}],["mans_shoe",{"name":"mans_shoe","unicode":"1f45e"}],["shoe",{"name":"shoe","unicode":"1f45e"}],["sandal",{"name":"sandal","unicode":"1f461"}],["high_heel",{"name":"high_heel","unicode":"1f460"}],["boot",{"name":"boot","unicode":"1f462"}],["shirt",{"name":"shirt","unicode":"1f455"}],["tshirt",{"name":"tshirt","unicode":"1f455"}],["necktie",{"name":"necktie","unicode":"1f454"}],["womans_clothes",{"name":"womans_clothes","unicode":"1f45a"}],["dress",{"name":"dress","unicode":"1f457"}],["running_shirt_with_sash",{"name":"running_shirt_with_sash","unicode":"1f3bd"}],["jeans",{"name":"jeans","unicode":"1f456"}],["kimono",{"name":"kimono","unicode":"1f458"}],["bikini",{"name":"bikini","unicode":"1f459"}],["briefcase",{"name":"briefcase","unicode":"1f4bc"}],["handbag",{"name":"handbag","unicode":"1f45c"}],["pouch",{"name":"pouch","unicode":"1f45d"}],["purse",{"name":"purse","unicode":"1f45b"}],["eyeglasses",{"name":"eyeglasses","unicode":"1f453"}],["ribbon",{"name":"ribbon","unicode":"1f380"}],["closed_umbrella",{"name":"closed_umbrella","unicode":"1f302"}],["lipstick",{"name":"lipstick","unicode":"1f484"}],["yellow_heart",{"name":"yellow_heart","unicode":"1f49b"}],["blue_heart",{"name":"blue_heart","unicode":"1f499"}],["purple_heart",{"name":"purple_heart","unicode":"1f49c"}],["green_heart",{"name":"green_heart","unicode":"1f49a"}],["heart",{"name":"heart","unicode":"2764"}],["broken_heart",{"name":"broken_heart","unicode":"1f494"}],["heartpulse",{"name":"heartpulse","unicode":"1f497"}],["heartbeat",{"name":"heartbeat","unicode":"1f493"}],["two_hearts",{"name":"two_hearts","unicode":"1f495"}],["sparkling_heart",{"name":"sparkling_heart","unicode":"1f496"}],["revolving_hearts",{"name":"revolving_hearts","unicode":"1f49e"}],["cupid",{"name":"cupid","unicode":"1f498"}],["love_letter",{"name":"love_letter","unicode":"1f48c"}],["kiss",{"name":"kiss","unicode":"1f48b"}],["ring",{"name":"ring","unicode":"1f48d"}],["gem",{"name":"gem","unicode":"1f48e"}],["bust_in_silhouette",{"name":"bust_in_silhouette","unicode":"1f464"}],["busts_in_silhouette",{"name":"busts_in_silhouette","unicode":"1f465"}],["speech_balloon",{"name":"speech_balloon","unicode":"1f4ac"}],["footprints",{"name":"footprints","unicode":"1f463"}],["thought_balloon",{"name":"thought_balloon","unicode":"1f4ad"}],["dog",{"name":"dog","unicode":"1f436"}],["wolf",{"name":"wolf","unicode":"1f43a"}],["cat",{"name":"cat","unicode":"1f431"}],["mouse",{"name":"mouse","unicode":"1f42d"}],["hamster",{"name":"hamster","unicode":"1f439"}],["rabbit",{"name":"rabbit","unicode":"1f430"}],["frog",{"name":"frog","unicode":"1f438"}],["tiger",{"name":"tiger","unicode":"1f42f"}],["koala",{"name":"koala","unicode":"1f428"}],["bear",{"name":"bear","unicode":"1f43b"}],["pig",{"name":"pig","unicode":"1f437"}],["pig_nose",{"name":"pig_nose","unicode":"1f43d"}],["cow",{"name":"cow","unicode":"1f42e"}],["boar",{"name":"boar","unicode":"1f417"}],["monkey_face",{"name":"monkey_face","unicode":"1f435"}],["monkey",{"name":"monkey","unicode":"1f412"}],["horse",{"name":"horse","unicode":"1f434"}],["sheep",{"name":"sheep","unicode":"1f411"}],["elephant",{"name":"elephant","unicode":"1f418"}],["panda_face",{"name":"panda_face","unicode":"1f43c"}],["penguin",{"name":"penguin","unicode":"1f427"}],["bird",{"name":"bird","unicode":"1f426"}],["baby_chick",{"name":"baby_chick","unicode":"1f424"}],["hatched_chick",{"name":"hatched_chick","unicode":"1f425"}],["hatching_chick",{"name":"hatching_chick","unicode":"1f423"}],["chicken",{"name":"chicken","unicode":"1f414"}],["snake",{"name":"snake","unicode":"1f40d"}],["turtle",{"name":"turtle","unicode":"1f422"}],["bug",{"name":"bug","unicode":"1f41b"}],["bee",{"name":"bee","unicode":"1f41d"}],["honeybee",{"name":"honeybee","unicode":"1f41d"}],["ant",{"name":"ant","unicode":"1f41c"}],["beetle",{"name":"beetle","unicode":"1f41e"}],["snail",{"name":"snail","unicode":"1f40c"}],["octopus",{"name":"octopus","unicode":"1f419"}],["shell",{"name":"shell","unicode":"1f41a"}],["tropical_fish",{"name":"tropical_fish","unicode":"1f420"}],["fish",{"name":"fish","unicode":"1f41f"}],["dolphin",{"name":"dolphin","unicode":"1f42c"}],["flipper",{"name":"flipper","unicode":"1f42c"}],["whale",{"name":"whale","unicode":"1f433"}],["whale2",{"name":"whale2","unicode":"1f40b"}],["cow2",{"name":"cow2","unicode":"1f404"}],["ram",{"name":"ram","unicode":"1f40f"}],["rat",{"name":"rat","unicode":"1f400"}],["water_buffalo",{"name":"water_buffalo","unicode":"1f403"}],["tiger2",{"name":"tiger2","unicode":"1f405"}],["rabbit2",{"name":"rabbit2","unicode":"1f407"}],["dragon",{"name":"dragon","unicode":"1f409"}],["racehorse",{"name":"racehorse","unicode":"1f40e"}],["goat",{"name":"goat","unicode":"1f410"}],["rooster",{"name":"rooster","unicode":"1f413"}],["dog2",{"name":"dog2","unicode":"1f415"}],["pig2",{"name":"pig2","unicode":"1f416"}],["mouse2",{"name":"mouse2","unicode":"1f401"}],["ox",{"name":"ox","unicode":"1f402"}],["dragon_face",{"name":"dragon_face","unicode":"1f432"}],["blowfish",{"name":"blowfish","unicode":"1f421"}],["crocodile",{"name":"crocodile","unicode":"1f40a"}],["camel",{"name":"camel","unicode":"1f42b"}],["dromedary_camel",{"name":"dromedary_camel","unicode":"1f42a"}],["leopard",{"name":"leopard","unicode":"1f406"}],["cat2",{"name":"cat2","unicode":"1f408"}],["poodle",{"name":"poodle","unicode":"1f429"}],["feet",{"name":"feet","unicode":"1f43e"}],["paw_prints",{"name":"paw_prints","unicode":"1f43e"}],["bouquet",{"name":"bouquet","unicode":"1f490"}],["cherry_blossom",{"name":"cherry_blossom","unicode":"1f338"}],["tulip",{"name":"tulip","unicode":"1f337"}],["four_leaf_clover",{"name":"four_leaf_clover","unicode":"1f340"}],["rose",{"name":"rose","unicode":"1f339"}],["sunflower",{"name":"sunflower","unicode":"1f33b"}],["hibiscus",{"name":"hibiscus","unicode":"1f33a"}],["maple_leaf",{"name":"maple_leaf","unicode":"1f341"}],["leaves",{"name":"leaves","unicode":"1f343"}],["fallen_leaf",{"name":"fallen_leaf","unicode":"1f342"}],["herb",{"name":"herb","unicode":"1f33f"}],["ear_of_rice",{"name":"ear_of_rice","unicode":"1f33e"}],["mushroom",{"name":"mushroom","unicode":"1f344"}],["cactus",{"name":"cactus","unicode":"1f335"}],["palm_tree",{"name":"palm_tree","unicode":"1f334"}],["evergreen_tree",{"name":"evergreen_tree","unicode":"1f332"}],["deciduous_tree",{"name":"deciduous_tree","unicode":"1f333"}],["chestnut",{"name":"chestnut","unicode":"1f330"}],["seedling",{"name":"seedling","unicode":"1f331"}],["blossom",{"name":"blossom","unicode":"1f33c"}],["globe_with_meridians",{"name":"globe_with_meridians","unicode":"1f310"}],["sun_with_face",{"name":"sun_with_face","unicode":"1f31e"}],["full_moon_with_face",{"name":"full_moon_with_face","unicode":"1f31d"}],["new_moon_with_face",{"name":"new_moon_with_face","unicode":"1f31a"}],["new_moon",{"name":"new_moon","unicode":"1f311"}],["waxing_crescent_moon",{"name":"waxing_crescent_moon","unicode":"1f312"}],["first_quarter_moon",{"name":"first_quarter_moon","unicode":"1f313"}],["moon",{"name":"moon","unicode":"1f314"}],["waxing_gibbous_moon",{"name":"waxing_gibbous_moon","unicode":"1f314"}],["full_moon",{"name":"full_moon","unicode":"1f315"}],["waning_gibbous_moon",{"name":"waning_gibbous_moon","unicode":"1f316"}],["last_quarter_moon",{"name":"last_quarter_moon","unicode":"1f317"}],["waning_crescent_moon",{"name":"waning_crescent_moon","unicode":"1f318"}],["last_quarter_moon_with_face",{"name":"last_quarter_moon_with_face","unicode":"1f31c"}],["first_quarter_moon_with_face",{"name":"first_quarter_moon_with_face","unicode":"1f31b"}],["crescent_moon",{"name":"crescent_moon","unicode":"1f319"}],["earth_africa",{"name":"earth_africa","unicode":"1f30d"}],["earth_americas",{"name":"earth_americas","unicode":"1f30e"}],["earth_asia",{"name":"earth_asia","unicode":"1f30f"}],["volcano",{"name":"volcano","unicode":"1f30b"}],["milky_way",{"name":"milky_way","unicode":"1f30c"}],["stars",{"name":"stars","unicode":"1f320"}],["star",{"name":"star","unicode":"2b50"}],["sunny",{"name":"sunny","unicode":"2600"}],["partly_sunny",{"name":"partly_sunny","unicode":"26c5"}],["cloud",{"name":"cloud","unicode":"2601"}],["zap",{"name":"zap","unicode":"26a1"}],["umbrella",{"name":"umbrella","unicode":"2614"}],["snowflake",{"name":"snowflake","unicode":"2744"}],["snowman",{"name":"snowman","unicode":"26c4"}],["cyclone",{"name":"cyclone","unicode":"1f300"}],["foggy",{"name":"foggy","unicode":"1f301"}],["rainbow",{"name":"rainbow","unicode":"1f308"}],["ocean",{"name":"ocean","unicode":"1f30a"}],["bamboo",{"name":"bamboo","unicode":"1f38d"}],["gift_heart",{"name":"gift_heart","unicode":"1f49d"}],["dolls",{"name":"dolls","unicode":"1f38e"}],["school_satchel",{"name":"school_satchel","unicode":"1f392"}],["mortar_board",{"name":"mortar_board","unicode":"1f393"}],["flags",{"name":"flags","unicode":"1f38f"}],["fireworks",{"name":"fireworks","unicode":"1f386"}],["sparkler",{"name":"sparkler","unicode":"1f387"}],["wind_chime",{"name":"wind_chime","unicode":"1f390"}],["rice_scene",{"name":"rice_scene","unicode":"1f391"}],["jack_o_lantern",{"name":"jack_o_lantern","unicode":"1f383"}],["ghost",{"name":"ghost","unicode":"1f47b"}],["santa",{"name":"santa","unicode":"1f385"}],["christmas_tree",{"name":"christmas_tree","unicode":"1f384"}],["gift",{"name":"gift","unicode":"1f381"}],["tanabata_tree",{"name":"tanabata_tree","unicode":"1f38b"}],["tada",{"name":"tada","unicode":"1f389"}],["confetti_ball",{"name":"confetti_ball","unicode":"1f38a"}],["balloon",{"name":"balloon","unicode":"1f388"}],["crossed_flags",{"name":"crossed_flags","unicode":"1f38c"}],["crystal_ball",{"name":"crystal_ball","unicode":"1f52e"}],["movie_camera",{"name":"movie_camera","unicode":"1f3a5"}],["camera",{"name":"camera","unicode":"1f4f7"}],["video_camera",{"name":"video_camera","unicode":"1f4f9"}],["vhs",{"name":"vhs","unicode":"1f4fc"}],["cd",{"name":"cd","unicode":"1f4bf"}],["dvd",{"name":"dvd","unicode":"1f4c0"}],["minidisc",{"name":"minidisc","unicode":"1f4bd"}],["floppy_disk",{"name":"floppy_disk","unicode":"1f4be"}],["computer",{"name":"computer","unicode":"1f4bb"}],["iphone",{"name":"iphone","unicode":"1f4f1"}],["phone",{"name":"phone","unicode":"260e"}],["telephone",{"name":"telephone","unicode":"260e"}],["telephone_receiver",{"name":"telephone_receiver","unicode":"1f4de"}],["pager",{"name":"pager","unicode":"1f4df"}],["fax",{"name":"fax","unicode":"1f4e0"}],["satellite",{"name":"satellite","unicode":"1f4e1"}],["tv",{"name":"tv","unicode":"1f4fa"}],["radio",{"name":"radio","unicode":"1f4fb"}],["loud_sound",{"name":"loud_sound","unicode":"1f50a"}],["sound",{"name":"sound","unicode":"1f509"}],["speaker",{"name":"speaker","unicode":"1f508"}],["mute",{"name":"mute","unicode":"1f507"}],["bell",{"name":"bell","unicode":"1f514"}],["no_bell",{"name":"no_bell","unicode":"1f515"}],["loudspeaker",{"name":"loudspeaker","unicode":"1f4e2"}],["mega",{"name":"mega","unicode":"1f4e3"}],["hourglass_flowing_sand",{"name":"hourglass_flowing_sand","unicode":"23f3"}],["hourglass",{"name":"hourglass","unicode":"231b"}],["alarm_clock",{"name":"alarm_clock","unicode":"23f0"}],["watch",{"name":"watch","unicode":"231a"}],["unlock",{"name":"unlock","unicode":"1f513"}],["lock",{"name":"lock","unicode":"1f512"}],["lock_with_ink_pen",{"name":"lock_with_ink_pen","unicode":"1f50f"}],["closed_lock_with_key",{"name":"closed_lock_with_key","unicode":"1f510"}],["key",{"name":"key","unicode":"1f511"}],["mag_right",{"name":"mag_right","unicode":"1f50e"}],["bulb",{"name":"bulb","unicode":"1f4a1"}],["flashlight",{"name":"flashlight","unicode":"1f526"}],["high_brightness",{"name":"high_brightness","unicode":"1f506"}],["low_brightness",{"name":"low_brightness","unicode":"1f505"}],["electric_plug",{"name":"electric_plug","unicode":"1f50c"}],["battery",{"name":"battery","unicode":"1f50b"}],["mag",{"name":"mag","unicode":"1f50d"}],["bathtub",{"name":"bathtub","unicode":"1f6c1"}],["bath",{"name":"bath","unicode":"1f6c0"}],["shower",{"name":"shower","unicode":"1f6bf"}],["toilet",{"name":"toilet","unicode":"1f6bd"}],["wrench",{"name":"wrench","unicode":"1f527"}],["nut_and_bolt",{"name":"nut_and_bolt","unicode":"1f529"}],["hammer",{"name":"hammer","unicode":"1f528"}],["door",{"name":"door","unicode":"1f6aa"}],["smoking",{"name":"smoking","unicode":"1f6ac"}],["bomb",{"name":"bomb","unicode":"1f4a3"}],["gun",{"name":"gun","unicode":"1f52b"}],["hocho",{"name":"hocho","unicode":"1f52a"}],["knife",{"name":"knife","unicode":"1f52a"}],["pill",{"name":"pill","unicode":"1f48a"}],["syringe",{"name":"syringe","unicode":"1f489"}],["moneybag",{"name":"moneybag","unicode":"1f4b0"}],["yen",{"name":"yen","unicode":"1f4b4"}],["dollar",{"name":"dollar","unicode":"1f4b5"}],["pound",{"name":"pound","unicode":"1f4b7"}],["euro",{"name":"euro","unicode":"1f4b6"}],["credit_card",{"name":"credit_card","unicode":"1f4b3"}],["money_with_wings",{"name":"money_with_wings","unicode":"1f4b8"}],["calling",{"name":"calling","unicode":"1f4f2"}],["e-mail",{"name":"e-mail","unicode":"1f4e7"}],["inbox_tray",{"name":"inbox_tray","unicode":"1f4e5"}],["outbox_tray",{"name":"outbox_tray","unicode":"1f4e4"}],["email",{"name":"email","unicode":"2709"}],["envelope",{"name":"envelope","unicode":"2709"}],["envelope_with_arrow",{"name":"envelope_with_arrow","unicode":"1f4e9"}],["incoming_envelope",{"name":"incoming_envelope","unicode":"1f4e8"}],["postal_horn",{"name":"postal_horn","unicode":"1f4ef"}],["mailbox",{"name":"mailbox","unicode":"1f4eb"}],["mailbox_closed",{"name":"mailbox_closed","unicode":"1f4ea"}],["mailbox_with_mail",{"name":"mailbox_with_mail","unicode":"1f4ec"}],["mailbox_with_no_mail",{"name":"mailbox_with_no_mail","unicode":"1f4ed"}],["postbox",{"name":"postbox","unicode":"1f4ee"}],["package",{"name":"package","unicode":"1f4e6"}],["memo",{"name":"memo","unicode":"1f4dd"}],["pencil",{"name":"pencil","unicode":"1f4dd"}],["page_facing_up",{"name":"page_facing_up","unicode":"1f4c4"}],["page_with_curl",{"name":"page_with_curl","unicode":"1f4c3"}],["bookmark_tabs",{"name":"bookmark_tabs","unicode":"1f4d1"}],["bar_chart",{"name":"bar_chart","unicode":"1f4ca"}],["chart_with_upwards_trend",{"name":"chart_with_upwards_trend","unicode":"1f4c8"}],["chart_with_downwards_trend",{"name":"chart_with_downwards_trend","unicode":"1f4c9"}],["scroll",{"name":"scroll","unicode":"1f4dc"}],["clipboard",{"name":"clipboard","unicode":"1f4cb"}],["date",{"name":"date","unicode":"1f4c5"}],["calendar",{"name":"calendar","unicode":"1f4c6"}],["card_index",{"name":"card_index","unicode":"1f4c7"}],["file_folder",{"name":"file_folder","unicode":"1f4c1"}],["open_file_folder",{"name":"open_file_folder","unicode":"1f4c2"}],["scissors",{"name":"scissors","unicode":"2702"}],["pushpin",{"name":"pushpin","unicode":"1f4cc"}],["paperclip",{"name":"paperclip","unicode":"1f4ce"}],["black_nib",{"name":"black_nib","unicode":"2712"}],["pencil2",{"name":"pencil2","unicode":"270f"}],["straight_ruler",{"name":"straight_ruler","unicode":"1f4cf"}],["triangular_ruler",{"name":"triangular_ruler","unicode":"1f4d0"}],["closed_book",{"name":"closed_book","unicode":"1f4d5"}],["green_book",{"name":"green_book","unicode":"1f4d7"}],["blue_book",{"name":"blue_book","unicode":"1f4d8"}],["orange_book",{"name":"orange_book","unicode":"1f4d9"}],["notebook",{"name":"notebook","unicode":"1f4d3"}],["notebook_with_decorative_cover",{"name":"notebook_with_decorative_cover","unicode":"1f4d4"}],["ledger",{"name":"ledger","unicode":"1f4d2"}],["books",{"name":"books","unicode":"1f4da"}],["book",{"name":"book","unicode":"1f4d6"}],["open_book",{"name":"open_book","unicode":"1f4d6"}],["bookmark",{"name":"bookmark","unicode":"1f516"}],["name_badge",{"name":"name_badge","unicode":"1f4db"}],["microscope",{"name":"microscope","unicode":"1f52c"}],["telescope",{"name":"telescope","unicode":"1f52d"}],["newspaper",{"name":"newspaper","unicode":"1f4f0"}],["art",{"name":"art","unicode":"1f3a8"}],["clapper",{"name":"clapper","unicode":"1f3ac"}],["microphone",{"name":"microphone","unicode":"1f3a4"}],["headphones",{"name":"headphones","unicode":"1f3a7"}],["musical_score",{"name":"musical_score","unicode":"1f3bc"}],["musical_note",{"name":"musical_note","unicode":"1f3b5"}],["notes",{"name":"notes","unicode":"1f3b6"}],["musical_keyboard",{"name":"musical_keyboard","unicode":"1f3b9"}],["violin",{"name":"violin","unicode":"1f3bb"}],["trumpet",{"name":"trumpet","unicode":"1f3ba"}],["saxophone",{"name":"saxophone","unicode":"1f3b7"}],["guitar",{"name":"guitar","unicode":"1f3b8"}],["space_invader",{"name":"space_invader","unicode":"1f47e"}],["video_game",{"name":"video_game","unicode":"1f3ae"}],["black_joker",{"name":"black_joker","unicode":"1f0cf"}],["flower_playing_cards",{"name":"flower_playing_cards","unicode":"1f3b4"}],["mahjong",{"name":"mahjong","unicode":"1f004"}],["game_die",{"name":"game_die","unicode":"1f3b2"}],["dart",{"name":"dart","unicode":"1f3af"}],["football",{"name":"football","unicode":"1f3c8"}],["basketball",{"name":"basketball","unicode":"1f3c0"}],["soccer",{"name":"soccer","unicode":"26bd"}],["baseball",{"name":"baseball","unicode":"26be"}],["tennis",{"name":"tennis","unicode":"1f3be"}],["8ball",{"name":"8ball","unicode":"1f3b1"}],["rugby_football",{"name":"rugby_football","unicode":"1f3c9"}],["bowling",{"name":"bowling","unicode":"1f3b3"}],["golf",{"name":"golf","unicode":"26f3"}],["mountain_bicyclist",{"name":"mountain_bicyclist","unicode":"1f6b5"}],["bicyclist",{"name":"bicyclist","unicode":"1f6b4"}],["checkered_flag",{"name":"checkered_flag","unicode":"1f3c1"}],["horse_racing",{"name":"horse_racing","unicode":"1f3c7"}],["trophy",{"name":"trophy","unicode":"1f3c6"}],["ski",{"name":"ski","unicode":"1f3bf"}],["snowboarder",{"name":"snowboarder","unicode":"1f3c2"}],["swimmer",{"name":"swimmer","unicode":"1f3ca"}],["surfer",{"name":"surfer","unicode":"1f3c4"}],["fishing_pole_and_fish",{"name":"fishing_pole_and_fish","unicode":"1f3a3"}],["coffee",{"name":"coffee","unicode":"2615"}],["tea",{"name":"tea","unicode":"1f375"}],["sake",{"name":"sake","unicode":"1f376"}],["baby_bottle",{"name":"baby_bottle","unicode":"1f37c"}],["beer",{"name":"beer","unicode":"1f37a"}],["beers",{"name":"beers","unicode":"1f37b"}],["cocktail",{"name":"cocktail","unicode":"1f378"}],["tropical_drink",{"name":"tropical_drink","unicode":"1f379"}],["wine_glass",{"name":"wine_glass","unicode":"1f377"}],["fork_and_knife",{"name":"fork_and_knife","unicode":"1f374"}],["pizza",{"name":"pizza","unicode":"1f355"}],["hamburger",{"name":"hamburger","unicode":"1f354"}],["fries",{"name":"fries","unicode":"1f35f"}],["poultry_leg",{"name":"poultry_leg","unicode":"1f357"}],["meat_on_bone",{"name":"meat_on_bone","unicode":"1f356"}],["spaghetti",{"name":"spaghetti","unicode":"1f35d"}],["curry",{"name":"curry","unicode":"1f35b"}],["fried_shrimp",{"name":"fried_shrimp","unicode":"1f364"}],["bento",{"name":"bento","unicode":"1f371"}],["sushi",{"name":"sushi","unicode":"1f363"}],["fish_cake",{"name":"fish_cake","unicode":"1f365"}],["rice_ball",{"name":"rice_ball","unicode":"1f359"}],["rice_cracker",{"name":"rice_cracker","unicode":"1f358"}],["rice",{"name":"rice","unicode":"1f35a"}],["ramen",{"name":"ramen","unicode":"1f35c"}],["stew",{"name":"stew","unicode":"1f372"}],["oden",{"name":"oden","unicode":"1f362"}],["dango",{"name":"dango","unicode":"1f361"}],["egg",{"name":"egg","unicode":"1f373"}],["bread",{"name":"bread","unicode":"1f35e"}],["doughnut",{"name":"doughnut","unicode":"1f369"}],["custard",{"name":"custard","unicode":"1f36e"}],["icecream",{"name":"icecream","unicode":"1f366"}],["ice_cream",{"name":"ice_cream","unicode":"1f368"}],["shaved_ice",{"name":"shaved_ice","unicode":"1f367"}],["birthday",{"name":"birthday","unicode":"1f382"}],["cake",{"name":"cake","unicode":"1f370"}],["cookie",{"name":"cookie","unicode":"1f36a"}],["chocolate_bar",{"name":"chocolate_bar","unicode":"1f36b"}],["candy",{"name":"candy","unicode":"1f36c"}],["lollipop",{"name":"lollipop","unicode":"1f36d"}],["honey_pot",{"name":"honey_pot","unicode":"1f36f"}],["apple",{"name":"apple","unicode":"1f34e"}],["green_apple",{"name":"green_apple","unicode":"1f34f"}],["tangerine",{"name":"tangerine","unicode":"1f34a"}],["orange",{"name":"orange","unicode":"1f34a"}],["mandarin",{"name":"mandarin","unicode":"1f34a"}],["lemon",{"name":"lemon","unicode":"1f34b"}],["cherries",{"name":"cherries","unicode":"1f352"}],["grapes",{"name":"grapes","unicode":"1f347"}],["watermelon",{"name":"watermelon","unicode":"1f349"}],["strawberry",{"name":"strawberry","unicode":"1f353"}],["peach",{"name":"peach","unicode":"1f351"}],["melon",{"name":"melon","unicode":"1f348"}],["banana",{"name":"banana","unicode":"1f34c"}],["pear",{"name":"pear","unicode":"1f350"}],["pineapple",{"name":"pineapple","unicode":"1f34d"}],["sweet_potato",{"name":"sweet_potato","unicode":"1f360"}],["eggplant",{"name":"eggplant","unicode":"1f346"}],["tomato",{"name":"tomato","unicode":"1f345"}],["corn",{"name":"corn","unicode":"1f33d"}],["house",{"name":"house","unicode":"1f3e0"}],["house_with_garden",{"name":"house_with_garden","unicode":"1f3e1"}],["school",{"name":"school","unicode":"1f3eb"}],["office",{"name":"office","unicode":"1f3e2"}],["post_office",{"name":"post_office","unicode":"1f3e3"}],["hospital",{"name":"hospital","unicode":"1f3e5"}],["bank",{"name":"bank","unicode":"1f3e6"}],["convenience_store",{"name":"convenience_store","unicode":"1f3ea"}],["love_hotel",{"name":"love_hotel","unicode":"1f3e9"}],["hotel",{"name":"hotel","unicode":"1f3e8"}],["wedding",{"name":"wedding","unicode":"1f492"}],["church",{"name":"church","unicode":"26ea"}],["department_store",{"name":"department_store","unicode":"1f3ec"}],["european_post_office",{"name":"european_post_office","unicode":"1f3e4"}],["city_sunrise",{"name":"city_sunrise","unicode":"1f307"}],["city_sunset",{"name":"city_sunset","unicode":"1f306"}],["japanese_castle",{"name":"japanese_castle","unicode":"1f3ef"}],["european_castle",{"name":"european_castle","unicode":"1f3f0"}],["tent",{"name":"tent","unicode":"26fa"}],["factory",{"name":"factory","unicode":"1f3ed"}],["tokyo_tower",{"name":"tokyo_tower","unicode":"1f5fc"}],["japan",{"name":"japan","unicode":"1f5fe"}],["mount_fuji",{"name":"mount_fuji","unicode":"1f5fb"}],["sunrise_over_mountains",{"name":"sunrise_over_mountains","unicode":"1f304"}],["sunrise",{"name":"sunrise","unicode":"1f305"}],["night_with_stars",{"name":"night_with_stars","unicode":"1f303"}],["statue_of_liberty",{"name":"statue_of_liberty","unicode":"1f5fd"}],["bridge_at_night",{"name":"bridge_at_night","unicode":"1f309"}],["carousel_horse",{"name":"carousel_horse","unicode":"1f3a0"}],["ferris_wheel",{"name":"ferris_wheel","unicode":"1f3a1"}],["fountain",{"name":"fountain","unicode":"26f2"}],["roller_coaster",{"name":"roller_coaster","unicode":"1f3a2"}],["ship",{"name":"ship","unicode":"1f6a2"}],["boat",{"name":"boat","unicode":"26f5"}],["sailboat",{"name":"sailboat","unicode":"26f5"}],["speedboat",{"name":"speedboat","unicode":"1f6a4"}],["rowboat",{"name":"rowboat","unicode":"1f6a3"}],["anchor",{"name":"anchor","unicode":"2693"}],["rocket",{"name":"rocket","unicode":"1f680"}],["airplane",{"name":"airplane","unicode":"2708"}],["seat",{"name":"seat","unicode":"1f4ba"}],["helicopter",{"name":"helicopter","unicode":"1f681"}],["steam_locomotive",{"name":"steam_locomotive","unicode":"1f682"}],["tram",{"name":"tram","unicode":"1f68a"}],["station",{"name":"station","unicode":"1f689"}],["mountain_railway",{"name":"mountain_railway","unicode":"1f69e"}],["train2",{"name":"train2","unicode":"1f686"}],["bullettrain_side",{"name":"bullettrain_side","unicode":"1f684"}],["bullettrain_front",{"name":"bullettrain_front","unicode":"1f685"}],["light_rail",{"name":"light_rail","unicode":"1f688"}],["metro",{"name":"metro","unicode":"1f687"}],["monorail",{"name":"monorail","unicode":"1f69d"}],["train",{"name":"train","unicode":"1f68b"}],["railway_car",{"name":"railway_car","unicode":"1f683"}],["trolleybus",{"name":"trolleybus","unicode":"1f68e"}],["bus",{"name":"bus","unicode":"1f68c"}],["oncoming_bus",{"name":"oncoming_bus","unicode":"1f68d"}],["blue_car",{"name":"blue_car","unicode":"1f699"}],["oncoming_automobile",{"name":"oncoming_automobile","unicode":"1f698"}],["car",{"name":"car","unicode":"1f697"}],["red_car",{"name":"red_car","unicode":"1f697"}],["taxi",{"name":"taxi","unicode":"1f695"}],["oncoming_taxi",{"name":"oncoming_taxi","unicode":"1f696"}],["articulated_lorry",{"name":"articulated_lorry","unicode":"1f69b"}],["truck",{"name":"truck","unicode":"1f69a"}],["rotating_light",{"name":"rotating_light","unicode":"1f6a8"}],["police_car",{"name":"police_car","unicode":"1f693"}],["oncoming_police_car",{"name":"oncoming_police_car","unicode":"1f694"}],["fire_engine",{"name":"fire_engine","unicode":"1f692"}],["ambulance",{"name":"ambulance","unicode":"1f691"}],["minibus",{"name":"minibus","unicode":"1f690"}],["bike",{"name":"bike","unicode":"1f6b2"}],["aerial_tramway",{"name":"aerial_tramway","unicode":"1f6a1"}],["suspension_railway",{"name":"suspension_railway","unicode":"1f69f"}],["mountain_cableway",{"name":"mountain_cableway","unicode":"1f6a0"}],["tractor",{"name":"tractor","unicode":"1f69c"}],["barber",{"name":"barber","unicode":"1f488"}],["busstop",{"name":"busstop","unicode":"1f68f"}],["ticket",{"name":"ticket","unicode":"1f3ab"}],["vertical_traffic_light",{"name":"vertical_traffic_light","unicode":"1f6a6"}],["traffic_light",{"name":"traffic_light","unicode":"1f6a5"}],["warning",{"name":"warning","unicode":"26a0"}],["construction",{"name":"construction","unicode":"1f6a7"}],["beginner",{"name":"beginner","unicode":"1f530"}],["fuelpump",{"name":"fuelpump","unicode":"26fd"}],["izakaya_lantern",{"name":"izakaya_lantern","unicode":"1f3ee"}],["lantern",{"name":"lantern","unicode":"1f3ee"}],["slot_machine",{"name":"slot_machine","unicode":"1f3b0"}],["hotsprings",{"name":"hotsprings","unicode":"2668"}],["moyai",{"name":"moyai","unicode":"1f5ff"}],["circus_tent",{"name":"circus_tent","unicode":"1f3aa"}],["performing_arts",{"name":"performing_arts","unicode":"1f3ad"}],["round_pushpin",{"name":"round_pushpin","unicode":"1f4cd"}],["triangular_flag_on_post",{"name":"triangular_flag_on_post","unicode":"1f6a9"}],["jp",{"name":"jp","unicode":"1f1ef-1f1f5"}],["kr",{"name":"kr","unicode":"1f1f0-1f1f7"}],["de",{"name":"de","unicode":"1f1e9-1f1ea"}],["cn",{"name":"cn","unicode":"1f1e8-1f1f3"}],["us",{"name":"us","unicode":"1f1fa-1f1f8"}],["fr",{"name":"fr","unicode":"1f1eb-1f1f7"}],["es",{"name":"es","unicode":"1f1ea-1f1f8"}],["it",{"name":"it","unicode":"1f1ee-1f1f9"}],["ru",{"name":"ru","unicode":"1f1f7-1f1fa"}],["gb",{"name":"gb","unicode":"1f1ec-1f1e7"}],["uk",{"name":"uk","unicode":"1f1ec-1f1e7"}],["one",{"name":"one","unicode":"0031-20e3"}],["two",{"name":"two","unicode":"0032-20e3"}],["three",{"name":"three","unicode":"0033-20e3"}],["four",{"name":"four","unicode":"0034-20e3"}],["five",{"name":"five","unicode":"0035-20e3"}],["six",{"name":"six","unicode":"0036-20e3"}],["seven",{"name":"seven","unicode":"0037-20e3"}],["eight",{"name":"eight","unicode":"0038-20e3"}],["nine",{"name":"nine","unicode":"0039-20e3"}],["zero",{"name":"zero","unicode":"0030-20e3"}],["keycap_ten",{"name":"keycap_ten","unicode":"1f51f"}],["1234",{"name":"1234","unicode":"1f522"}],["hash",{"name":"hash","unicode":"0023-20e3"}],["symbols",{"name":"symbols","unicode":"1f523"}],["arrow_up",{"name":"arrow_up","unicode":"2b06"}],["arrow_down",{"name":"arrow_down","unicode":"2b07"}],["arrow_left",{"name":"arrow_left","unicode":"2b05"}],["arrow_right",{"name":"arrow_right","unicode":"27a1"}],["capital_abcd",{"name":"capital_abcd","unicode":"1f520"}],["abcd",{"name":"abcd","unicode":"1f521"}],["abc",{"name":"abc","unicode":"1f524"}],["arrow_upper_right",{"name":"arrow_upper_right","unicode":"2197"}],["arrow_upper_left",{"name":"arrow_upper_left","unicode":"2196"}],["arrow_lower_right",{"name":"arrow_lower_right","unicode":"2198"}],["arrow_lower_left",{"name":"arrow_lower_left","unicode":"2199"}],["left_right_arrow",{"name":"left_right_arrow","unicode":"2194"}],["arrow_up_down",{"name":"arrow_up_down","unicode":"2195"}],["arrows_counterclockwise",{"name":"arrows_counterclockwise","unicode":"1f504"}],["arrow_backward",{"name":"arrow_backward","unicode":"25c0"}],["arrow_forward",{"name":"arrow_forward","unicode":"25b6"}],["arrow_up_small",{"name":"arrow_up_small","unicode":"1f53c"}],["arrow_down_small",{"name":"arrow_down_small","unicode":"1f53d"}],["leftwards_arrow_with_hook",{"name":"leftwards_arrow_with_hook","unicode":"21a9"}],["arrow_right_hook",{"name":"arrow_right_hook","unicode":"21aa"}],["information_source",{"name":"information_source","unicode":"2139"}],["rewind",{"name":"rewind","unicode":"23ea"}],["fast_forward",{"name":"fast_forward","unicode":"23e9"}],["arrow_double_up",{"name":"arrow_double_up","unicode":"23eb"}],["arrow_double_down",{"name":"arrow_double_down","unicode":"23ec"}],["arrow_heading_down",{"name":"arrow_heading_down","unicode":"2935"}],["arrow_heading_up",{"name":"arrow_heading_up","unicode":"2934"}],["ok",{"name":"ok","unicode":"1f197"}],["twisted_rightwards_arrows",{"name":"twisted_rightwards_arrows","unicode":"1f500"}],["repeat",{"name":"repeat","unicode":"1f501"}],["repeat_one",{"name":"repeat_one","unicode":"1f502"}],["new",{"name":"new","unicode":"1f195"}],["up",{"name":"up","unicode":"1f199"}],["cool",{"name":"cool","unicode":"1f192"}],["free",{"name":"free","unicode":"1f193"}],["ng",{"name":"ng","unicode":"1f196"}],["signal_strength",{"name":"signal_strength","unicode":"1f4f6"}],["cinema",{"name":"cinema","unicode":"1f3a6"}],["koko",{"name":"koko","unicode":"1f201"}],["u6307",{"name":"u6307","unicode":"1f22f"}],["u7a7a",{"name":"u7a7a","unicode":"1f233"}],["u6e80",{"name":"u6e80","unicode":"1f235"}],["u5408",{"name":"u5408","unicode":"1f234"}],["u7981",{"name":"u7981","unicode":"1f232"}],["ideograph_advantage",{"name":"ideograph_advantage","unicode":"1f250"}],["u5272",{"name":"u5272","unicode":"1f239"}],["u55b6",{"name":"u55b6","unicode":"1f23a"}],["u6709",{"name":"u6709","unicode":"1f236"}],["u7121",{"name":"u7121","unicode":"1f21a"}],["restroom",{"name":"restroom","unicode":"1f6bb"}],["mens",{"name":"mens","unicode":"1f6b9"}],["womens",{"name":"womens","unicode":"1f6ba"}],["baby_symbol",{"name":"baby_symbol","unicode":"1f6bc"}],["wc",{"name":"wc","unicode":"1f6be"}],["potable_water",{"name":"potable_water","unicode":"1f6b0"}],["put_litter_in_its_place",{"name":"put_litter_in_its_place","unicode":"1f6ae"}],["parking",{"name":"parking","unicode":"1f17f"}],["wheelchair",{"name":"wheelchair","unicode":"267f"}],["no_smoking",{"name":"no_smoking","unicode":"1f6ad"}],["u6708",{"name":"u6708","unicode":"1f237"}],["u7533",{"name":"u7533","unicode":"1f238"}],["sa",{"name":"sa","unicode":"1f202"}],["m",{"name":"m","unicode":"24c2"}],["passport_control",{"name":"passport_control","unicode":"1f6c2"}],["baggage_claim",{"name":"baggage_claim","unicode":"1f6c4"}],["left_luggage",{"name":"left_luggage","unicode":"1f6c5"}],["customs",{"name":"customs","unicode":"1f6c3"}],["accept",{"name":"accept","unicode":"1f251"}],["secret",{"name":"secret","unicode":"3299"}],["congratulations",{"name":"congratulations","unicode":"3297"}],["cl",{"name":"cl","unicode":"1f191"}],["sos",{"name":"sos","unicode":"1f198"}],["id",{"name":"id","unicode":"1f194"}],["no_entry_sign",{"name":"no_entry_sign","unicode":"1f6ab"}],["underage",{"name":"underage","unicode":"1f51e"}],["no_mobile_phones",{"name":"no_mobile_phones","unicode":"1f4f5"}],["do_not_litter",{"name":"do_not_litter","unicode":"1f6af"}],["non-potable_water",{"name":"non-potable_water","unicode":"1f6b1"}],["no_bicycles",{"name":"no_bicycles","unicode":"1f6b3"}],["no_pedestrians",{"name":"no_pedestrians","unicode":"1f6b7"}],["children_crossing",{"name":"children_crossing","unicode":"1f6b8"}],["no_entry",{"name":"no_entry","unicode":"26d4"}],["eight_spoked_asterisk",{"name":"eight_spoked_asterisk","unicode":"2733"}],["sparkle",{"name":"sparkle","unicode":"2747"}],["negative_squared_cross_mark",{"name":"negative_squared_cross_mark","unicode":"274e"}],["white_check_mark",{"name":"white_check_mark","unicode":"2705"}],["eight_pointed_black_star",{"name":"eight_pointed_black_star","unicode":"2734"}],["heart_decoration",{"name":"heart_decoration","unicode":"1f49f"}],["vs",{"name":"vs","unicode":"1f19a"}],["vibration_mode",{"name":"vibration_mode","unicode":"1f4f3"}],["mobile_phone_off",{"name":"mobile_phone_off","unicode":"1f4f4"}],["a",{"name":"a","unicode":"1f170"}],["b",{"name":"b","unicode":"1f171"}],["ab",{"name":"ab","unicode":"1f18e"}],["o2",{"name":"o2","unicode":"1f17e"}],["diamond_shape_with_a_dot_inside",{"name":"diamond_shape_with_a_dot_inside","unicode":"1f4a0"}],["loop",{"name":"loop","unicode":"27bf"}],["recycle",{"name":"recycle","unicode":"267b"}],["aries",{"name":"aries","unicode":"2648"}],["taurus",{"name":"taurus","unicode":"2649"}],["gemini",{"name":"gemini","unicode":"264a"}],["cancer",{"name":"cancer","unicode":"264b"}],["leo",{"name":"leo","unicode":"264c"}],["virgo",{"name":"virgo","unicode":"264d"}],["libra",{"name":"libra","unicode":"264e"}],["scorpius",{"name":"scorpius","unicode":"264f"}],["sagittarius",{"name":"sagittarius","unicode":"2650"}],["capricorn",{"name":"capricorn","unicode":"2651"}],["aquarius",{"name":"aquarius","unicode":"2652"}],["pisces",{"name":"pisces","unicode":"2653"}],["ophiuchus",{"name":"ophiuchus","unicode":"26ce"}],["six_pointed_star",{"name":"six_pointed_star","unicode":"1f52f"}],["atm",{"name":"atm","unicode":"1f3e7"}],["chart",{"name":"chart","unicode":"1f4b9"}],["heavy_dollar_sign",{"name":"heavy_dollar_sign","unicode":"1f4b2"}],["currency_exchange",{"name":"currency_exchange","unicode":"1f4b1"}],["copyright",{"name":"copyright","unicode":"00a9"}],["registered",{"name":"registered","unicode":"00ae"}],["tm",{"name":"tm","unicode":"2122"}],["x",{"name":"x","unicode":"274c"}],["bangbang",{"name":"bangbang","unicode":"203c"}],["interrobang",{"name":"interrobang","unicode":"2049"}],["exclamation",{"name":"exclamation","unicode":"2757"}],["heavy_exclamation_mark",{"name":"heavy_exclamation_mark","unicode":"2757"}],["question",{"name":"question","unicode":"2753"}],["grey_exclamation",{"name":"grey_exclamation","unicode":"2755"}],["grey_question",{"name":"grey_question","unicode":"2754"}],["o",{"name":"o","unicode":"2b55"}],["top",{"name":"top","unicode":"1f51d"}],["end",{"name":"end","unicode":"1f51a"}],["back",{"name":"back","unicode":"1f519"}],["on",{"name":"on","unicode":"1f51b"}],["soon",{"name":"soon","unicode":"1f51c"}],["arrows_clockwise",{"name":"arrows_clockwise","unicode":"1f503"}],["clock12",{"name":"clock12","unicode":"1f55b"}],["clock1230",{"name":"clock1230","unicode":"1f567"}],["clock1",{"name":"clock1","unicode":"1f550"}],["clock130",{"name":"clock130","unicode":"1f55c"}],["clock2",{"name":"clock2","unicode":"1f551"}],["clock230",{"name":"clock230","unicode":"1f55d"}],["clock3",{"name":"clock3","unicode":"1f552"}],["clock330",{"name":"clock330","unicode":"1f55e"}],["clock4",{"name":"clock4","unicode":"1f553"}],["clock430",{"name":"clock430","unicode":"1f55f"}],["clock5",{"name":"clock5","unicode":"1f554"}],["clock530",{"name":"clock530","unicode":"1f560"}],["clock6",{"name":"clock6","unicode":"1f555"}],["clock7",{"name":"clock7","unicode":"1f556"}],["clock8",{"name":"clock8","unicode":"1f557"}],["clock9",{"name":"clock9","unicode":"1f558"}],["clock10",{"name":"clock10","unicode":"1f559"}],["clock11",{"name":"clock11","unicode":"1f55a"}],["clock630",{"name":"clock630","unicode":"1f561"}],["clock730",{"name":"clock730","unicode":"1f562"}],["clock830",{"name":"clock830","unicode":"1f563"}],["clock930",{"name":"clock930","unicode":"1f564"}],["clock1030",{"name":"clock1030","unicode":"1f565"}],["clock1130",{"name":"clock1130","unicode":"1f566"}],["heavy_multiplication_x",{"name":"heavy_multiplication_x","unicode":"2716"}],["heavy_plus_sign",{"name":"heavy_plus_sign","unicode":"2795"}],["heavy_minus_sign",{"name":"heavy_minus_sign","unicode":"2796"}],["heavy_division_sign",{"name":"heavy_division_sign","unicode":"2797"}],["spades",{"name":"spades","unicode":"2660"}],["hearts",{"name":"hearts","unicode":"2665"}],["clubs",{"name":"clubs","unicode":"2663"}],["diamonds",{"name":"diamonds","unicode":"2666"}],["white_flower",{"name":"white_flower","unicode":"1f4ae"}],["100",{"name":"100","unicode":"1f4af"}],["heavy_check_mark",{"name":"heavy_check_mark","unicode":"2714"}],["ballot_box_with_check",{"name":"ballot_box_with_check","unicode":"2611"}],["radio_button",{"name":"radio_button","unicode":"1f518"}],["link",{"name":"link","unicode":"1f517"}],["curly_loop",{"name":"curly_loop","unicode":"27b0"}],["wavy_dash",{"name":"wavy_dash","unicode":"3030"}],["part_alternation_mark",{"name":"part_alternation_mark","unicode":"303d"}],["trident",{"name":"trident","unicode":"1f531"}],["black_medium_square",{"name":"black_medium_square","unicode":"25fc"}],["white_medium_square",{"name":"white_medium_square","unicode":"25fb"}],["black_medium_small_square",{"name":"black_medium_small_square","unicode":"25fe"}],["white_medium_small_square",{"name":"white_medium_small_square","unicode":"25fd"}],["black_small_square",{"name":"black_small_square","unicode":"25aa"}],["white_small_square",{"name":"white_small_square","unicode":"25ab"}],["small_red_triangle",{"name":"small_red_triangle","unicode":"1f53a"}],["black_square_button",{"name":"black_square_button","unicode":"1f532"}],["white_square_button",{"name":"white_square_button","unicode":"1f533"}],["black_circle",{"name":"black_circle","unicode":"26ab"}],["white_circle",{"name":"white_circle","unicode":"26aa"}],["red_circle",{"name":"red_circle","unicode":"1f534"}],["large_blue_circle",{"name":"large_blue_circle","unicode":"1f535"}],["small_red_triangle_down",{"name":"small_red_triangle_down","unicode":"1f53b"}],["white_large_square",{"name":"white_large_square","unicode":"2b1c"}],["black_large_square",{"name":"black_large_square","unicode":"2b1b"}],["large_orange_diamond",{"name":"large_orange_diamond","unicode":"1f536"}],["large_blue_diamond",{"name":"large_blue_diamond","unicode":"1f537"}],["small_orange_diamond",{"name":"small_orange_diamond","unicode":"1f538"}],["small_blue_diamond",{"name":"small_blue_diamond","unicode":"1f539"}],["ca",{"name":"ca","unicode":"1f1e8-1f1e6"}],["eh",{"name":"eh","unicode":"1f1e8-1f1e6"}],["pk",{"name":"pk","unicode":"1f1f5-1f1f0"}],["za",{"name":"za","unicode":"1f1ff-1f1e6"}],["slightly_smiling_face",{"name":"slightly_smiling_face","unicode":"1f642"}],["slightly_frowning_face",{"name":"slightly_frowning_face","unicode":"1f641"}],["upside_down_face",{"name":"upside_down_face","unicode":"1f643"}],["mm",{"name":"mm"}],["mattermost",{"name":"mattermost","filename":"mm"}],["basecamp",{"name":"basecamp"}],["basecampy",{"name":"basecampy"}],["bowtie",{"name":"bowtie"}],["feelsgood",{"name":"feelsgood"}],["finnadie",{"name":"finnadie"}],["fu",{"name":"fu"}],["goberserk",{"name":"goberserk"}],["godmode",{"name":"godmode"}],["hurtrealbad",{"name":"hurtrealbad"}],["metal",{"name":"metal"}],["neckbeard",{"name":"neckbeard"}],["octocat",{"name":"octocat"}],["rage1",{"name":"rage1"}],["rage2",{"name":"rage2"}],["rage3",{"name":"rage3"}],["rage4",{"name":"rage4"}],["shipit",{"name":"shipit"}],["squirrel",{"name":"squirrel","filename":"shipit"}],["suspect",{"name":"suspect"}],["taco",{"name":"taco"}],["trollface",{"name":"trollface"}]] \ No newline at end of file
diff --git a/webapp/utils/emoticons.jsx b/webapp/utils/emoticons.jsx
index 1f3fdff8e..a7d6d84b1 100644
--- a/webapp/utils/emoticons.jsx
+++ b/webapp/utils/emoticons.jsx
@@ -1,8 +1,7 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import Constants from './constants.jsx';
-import emojis from './emoji.json';
+import EmojiStore from 'stores/emoji_store.jsx';
export const emoticonPatterns = {
slightly_smiling_face: /(^|\s)(:-?\))(?=$|\s)/g, // :)
@@ -27,117 +26,17 @@ export const emoticonPatterns = {
thumbsdown: /(^|\s)(:\-1:)(?=$|\s)/g // :-1:
};
-let emoticonsByName;
-let emoticonsByCodePoint;
-
-function initializeEmoticons() {
- emoticonsByName = new Map();
- emoticonsByCodePoint = new Set();
-
- for (const emoji of emojis) {
- const unicode = emoji.emoji;
-
- let filename = '';
- if (unicode) {
- // this is a unicode emoji so the character code determines the file name
- let codepoint = '';
-
- for (let i = 0; i < unicode.length; i += 2) {
- const code = fixedCharCodeAt(unicode, i);
-
- // ignore variation selector characters
- if (code >= 0xfe00 && code <= 0xfe0f) {
- continue;
- }
-
- // some emoji (such as country flags) span multiple unicode characters
- if (i !== 0) {
- codepoint += '-';
- }
-
- codepoint += pad(code.toString(16));
- }
-
- filename = codepoint;
- emoticonsByCodePoint.add(codepoint);
- } else {
- // this isn't a unicode emoji so the first alias determines the file name
- filename = emoji.aliases[0];
- }
-
- for (const alias of emoji.aliases) {
- emoticonsByName.set(alias, {
- alias,
- path: getImagePathForEmoticon(filename)
- });
- }
- }
-}
-
-// Pads a hexadecimal number with zeroes to be at least 4 digits long
-function pad(n) {
- if (n.length >= 4) {
- return n;
- }
-
- // http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
- return ('0000' + n).slice(-4);
-}
-
-// Gets the unicode character code of a character starting at the given index in the string
-// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
-function fixedCharCodeAt(str, idx = 0) {
- // ex. fixedCharCodeAt('\uD800\uDC00', 0); // 65536
- // ex. fixedCharCodeAt('\uD800\uDC00', 1); // false
- const code = str.charCodeAt(idx);
-
- // High surrogate (could change last hex to 0xDB7F to treat high
- // private surrogates as single characters)
- if (code >= 0xD800 && code <= 0xDBFF) {
- const hi = code;
- const low = str.charCodeAt(idx + 1);
-
- if (isNaN(low)) {
- console.log('High surrogate not followed by low surrogate in fixedCharCodeAt()'); // eslint-disable-line
- }
-
- return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
- }
-
- if (code >= 0xDC00 && code <= 0xDFFF) { // Low surrogate
- // We return false to allow loops to skip this iteration since should have
- // already handled high surrogate above in the previous iteration
- return false;
- }
-
- return code;
-}
-
-export function getEmoticonsByName() {
- if (!emoticonsByName) {
- initializeEmoticons();
- }
-
- return emoticonsByName;
-}
-
-export function getEmoticonsByCodePoint() {
- if (!emoticonsByCodePoint) {
- initializeEmoticons();
- }
-
- return emoticonsByCodePoint;
-}
-
-export function handleEmoticons(text, tokens) {
+export function handleEmoticons(text, tokens, emojis) {
let output = text;
function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) {
- if (getEmoticonsByName().has(name)) {
- const index = tokens.size;
- const alias = `MM_EMOTICON${index}`;
- const path = getEmoticonsByName().get(name).path;
+ const index = tokens.size;
+ const alias = `MM_EMOTICON${index}`;
+
+ if (emojis.has(name)) {
+ const path = EmojiStore.getEmojiImageUrl(emojis.get(name));
+ // we have an image path so we found a matching emoticon
tokens.set(alias, {
value: `<img align="absmiddle" alt="${matchText}" class="emoticon" src="${path}" title="${matchText}" />`,
originalText: fullMatch
@@ -163,7 +62,3 @@ export function handleEmoticons(text, tokens) {
return output;
}
-
-export function getImagePathForEmoticon(name) {
- return Constants.EMOJI_PATH + '/' + name + '.png';
-}
diff --git a/webapp/utils/text_formatting.jsx b/webapp/utils/text_formatting.jsx
index 91be443fc..0b46edaeb 100644
--- a/webapp/utils/text_formatting.jsx
+++ b/webapp/utils/text_formatting.jsx
@@ -4,6 +4,7 @@
import Autolinker from 'autolinker';
import {browserHistory} from 'react-router/es6';
import Constants from './constants.jsx';
+import EmojiStore from 'stores/emoji_store.jsx';
import * as Emoticons from './emoticons.jsx';
import * as Markdown from './markdown.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
@@ -61,7 +62,7 @@ export function doFormatText(text, options) {
output = autolinkHashtags(output, tokens);
if (!('emoticons' in options) || options.emoticon) {
- output = Emoticons.handleEmoticons(output, tokens);
+ output = Emoticons.handleEmoticons(output, tokens, options.emojis || EmojiStore.getEmojis());
}
if (options.searchTerm) {
@@ -75,15 +76,13 @@ export function doFormatText(text, options) {
if (!('emoticons' in options) || options.emoticon) {
output = twemoji.parse(output, {
className: 'emoticon',
- base: '',
- folder: Constants.EMOJI_PATH,
- callback: (icon, twemojiOptions) => {
- if (!Emoticons.getEmoticonsByCodePoint().has(icon)) {
+ callback: (icon) => {
+ if (!EmojiStore.hasUnicode(icon)) {
// just leave the unicode characters and hope the browser can handle it
return null;
}
- return ''.concat(twemojiOptions.base, twemojiOptions.size, '/', icon, twemojiOptions.ext);
+ return EmojiStore.getEmojiImageUrl(EmojiStore.getUnicode(icon));
}
});
}
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 2792b144c..9fc342cb1 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -982,7 +982,10 @@ export function getDisplayName(user) {
}
export function displayUsername(userId) {
- const user = UserStore.getProfile(userId);
+ return displayUsernameForUser(UserStore.getProfile(userId));
+}
+
+export function displayUsernameForUser(user) {
const nameFormat = PreferenceStore.get(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false');
let username = '';