Jelajahi Sumber

Merge pull request #773 from threema-ch/new-compose-area

Integrate new compose-area
Danilo Bargen 6 tahun lalu
induk
melakukan
c553129e03

+ 1 - 2
.gitignore

@@ -6,8 +6,7 @@
 
 
 # Generated files
 # Generated files
 js/
 js/
-dist/*.js
-dist/*.js.map
+dist/generated/
 dist/*.tar.gz
 dist/*.tar.gz
 public/css/*.css
 public/css/*.css
 public/css/*.map
 public/css/*.map

+ 7 - 0
CHANGELOG.md

@@ -4,6 +4,13 @@ This changelog lists the most important changes for each released version. For
 the full log, please refer to the git commit history.
 the full log, please refer to the git commit history.
 
 
 
 
+### Unreleased
+
+**NOTE:** If you are self-hosting threema-web, then you need to make sure to
+set the proper MIME type for `.wasm` (WebAssembly) files. See
+`docs/self_hosting.md` for more details.
+
+
 ### [v2.1.7][v2.1.7] (2019-02-07)
 ### [v2.1.7][v2.1.7] (2019-02-07)
 
 
 Changes:
 Changes:

+ 2 - 2
README.md

@@ -82,8 +82,8 @@ Web on a server, please follow the instructions at
 
 
 To run unit tests:
 To run unit tests:
 
 
-    npm run build:unittests
-    firefox tests/testsuite.html
+    npm run build:unittests && npm run testserver
+    firefox http://localhost:7777/tests/testsuite.html
 
 
 To run UI tests:
 To run UI tests:
 
 

+ 9 - 3
dist/build-package.js

@@ -4,12 +4,18 @@ var process = require('process');
 
 
 var args = process.argv.slice(2);
 var args = process.argv.slice(2);
 
 
+function abortOnError(code) {
+    if (code !== 0) {
+        process.exit(code);
+    }
+}
+
 if (os.type() === 'Linux') {
 if (os.type() === 'Linux') {
-   spawn('bash', ['dist/package.sh'].concat(args), {stdio: 'inherit'});
+   spawn('bash', ['dist/package.sh'].concat(args), {stdio: 'inherit'}).on('exit', abortOnError);
 } else if (os.type() === 'Darwin') {
 } else if (os.type() === 'Darwin') {
-   spawn('bash', ['dist/package.sh'].concat(args), {stdio: 'inherit'});
+   spawn('bash', ['dist/package.sh'].concat(args), {stdio: 'inherit'}).on('exit', abortOnError);
 } else if (os.type() === 'Windows_NT') {
 } else if (os.type() === 'Windows_NT') {
-   spawn('powershell', ['dist/package.sh'].concat(args), {stdio: 'inherit'});
+   spawn('powershell', ['dist/package.sh'].concat(args), {stdio: 'inherit'}).on('exit', abortOnError);
 } else {
 } else {
    throw new Error("Unsupported OS found: " + os.type());
    throw new Error("Unsupported OS found: " + os.type());
 }
 }

+ 7 - 5
dist/package.sh

@@ -36,16 +36,18 @@ if [ -e "release" ]; then
 fi
 fi
 
 
 VERSION=$(grep "\"version\"" package.json  | sed 's/[[:blank:]]*\"version\": \"\([^\"]*\).*/\1/')$SUFFIX
 VERSION=$(grep "\"version\"" package.json  | sed 's/[[:blank:]]*\"version\": \"\([^\"]*\).*/\1/')$SUFFIX
-echo "+ Building version $VERSION"
+echo "+ Packaging version $VERSION"
 
 
 DIR="release/threema-web-$VERSION"
 DIR="release/threema-web-$VERSION"
 
 
 echo "+ Create release directory..."
 echo "+ Create release directory..."
-mkdir -p $DIR/{dist,partials,directives,components,node_modules,partials/messenger.receiver,troubleshoot}
+mkdir -p $DIR/{partials,directives,components,node_modules,partials/messenger.receiver,troubleshoot}
 
 
 echo "+ Copy code..."
 echo "+ Copy code..."
 cp -R index.html $DIR/
 cp -R index.html $DIR/
-cp -R dist/app.bundle.js $DIR/dist/
+cp -R dist/generated/*.bundle.js $DIR/
+cp -R dist/generated/*.bundle.js.map $DIR/
+cp -R dist/generated/*.wasm $DIR/
 cp -R public/* $DIR/
 cp -R public/* $DIR/
 cp -R troubleshoot/* $DIR/troubleshoot/
 cp -R troubleshoot/* $DIR/troubleshoot/
 cp -R src/partials/*.html $DIR/partials/
 cp -R src/partials/*.html $DIR/partials/
@@ -98,8 +100,8 @@ for target in "${targets[@]}"; do
 done
 done
 
 
 echo "+ Update version number..."
 echo "+ Update version number..."
-sed -i.bak -e "s/\[\[VERSION\]\]/${VERSION}/g" $DIR/index.html $DIR/troubleshoot/index.html $DIR/dist/app.bundle.js $DIR/manifest.webmanifest $DIR/browserconfig.xml $DIR/version.txt
-rm $DIR/index.html.bak $DIR/troubleshoot/index.html.bak $DIR/dist/app.bundle.js.bak $DIR/manifest.webmanifest.bak $DIR/browserconfig.xml.bak $DIR/version.txt.bak
+sed -i.bak -e "s/\[\[VERSION\]\]/${VERSION}/g" $DIR/index.html $DIR/troubleshoot/index.html $DIR/*.bundle.js $DIR/manifest.webmanifest $DIR/browserconfig.xml $DIR/version.txt
+rm $DIR/*.bak $DIR/troubleshoot/index.html.bak
 
 
 echo "+ Update permissions..."
 echo "+ Update permissions..."
 find $DIR/ -type f -exec chmod 644 {} \;
 find $DIR/ -type f -exec chmod 644 {} \;

+ 9 - 3
docker/entrypoint.sh

@@ -5,10 +5,16 @@ set -euo pipefail
 echo "Patching config file..."
 echo "Patching config file..."
 cd /usr/share/nginx/html/
 cd /usr/share/nginx/html/
 if [ ! -z "$SALTYRTC_HOST" ]; then
 if [ ! -z "$SALTYRTC_HOST" ]; then
-    sed -i "s/SALTYRTC_HOST: null,/SALTYRTC_HOST: '${SALTYRTC_HOST}',/g" dist/app.js
+    sed -i -E "s/SALTYRTC_HOST:\s*null,/SALTYRTC_HOST:'${SALTYRTC_HOST}',/g" *.bundle.js
+fi
+sed -i -E "s/SALTYRTC_PORT:\s*[^,]*,/SALTYRTC_PORT:${SALTYRTC_PORT},/g" *.bundle.js
+sed -i -E "s/SALTYRTC_SERVER_KEY:\s*\"[^\"]*\",/SALTYRTC_SERVER_KEY:\"${SALTYRTC_SERVER_KEY}\",/g" *.bundle.js
+
+# Add nginx mime type for wasm
+# See https://trac.nginx.org/nginx/ticket/1606
+if ! grep -q application/wasm "/etc/nginx/mime.types"; then
+    sed -i '2aapplication/wasm wasm;' /etc/nginx/mime.types
 fi
 fi
-sed -i "s/SALTYRTC_PORT: [^,]*,/SALTYRTC_PORT: ${SALTYRTC_PORT},/g" dist/app.js
-sed -i "s/SALTYRTC_SERVER_KEY: '[^']*',/SALTYRTC_SERVER_KEY: '${SALTYRTC_SERVER_KEY}',/g" dist/app.js
 
 
 echo "Starting Threema Web..."
 echo "Starting Threema Web..."
 exec nginx -g 'daemon off;'
 exec nginx -g 'daemon off;'

+ 2 - 2
docs/docker.md

@@ -5,11 +5,11 @@ A Docker image with Threema Web is published on Docker Hub at
 
 
 Alternatively you can build the image yourself:
 Alternatively you can build the image yourself:
 
 
-    $ docker build . -t threema/threema-web:v2.1
+    $ docker build . -t threema/threema-web:master
 
 
 To run the Docker image:
 To run the Docker image:
 
 
-    $ docker run --rm -p 8080:80 threema/threema-web
+    $ docker run --rm -p 8080:80 threema/threema-web:master
 
 
 Now you can open `http://localhost:8080/` in your browser to use Threema Web.
 Now you can open `http://localhost:8080/` in your browser to use Threema Web.
 
 

+ 12 - 2
docs/self_hosting.md

@@ -19,8 +19,18 @@ issue](https://github.com/threema-ch/threema-web/issues) on Github.
 
 
 Threema Web is a web application written in TypeScript with AngularJS 1. All
 Threema Web is a web application written in TypeScript with AngularJS 1. All
 that is required to host it is a web server that can deliver static content via
 that is required to host it is a web server that can deliver static content via
-https. We recommend using Nginx. Additionally, to build the release version
-yourself, a recent version of npm is required.
+https. We recommend using Nginx. Additionally, to build the
+release version yourself, a recent version of npm is required.
+
+*Note:* The web server needs to set the proper MIME type for `*.wasm`
+(WebAssembly) files. When using Nginx, add the following line to the types in
+`/etc/nginx/mime.types`:
+
+    application/wasm    wasm;
+
+When using Python,
+[someting like this](https://gist.github.com/dbrgn/6bf88d32b1b44b1b1d4140f92b8a7a0a)
+should work.
 
 
 ### Building from source
 ### Building from source
 
 

+ 2 - 2
index.html

@@ -19,7 +19,7 @@
     along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
     along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
 
 
 -->
 -->
-<html ng-app="3ema" ng-strict-di ng-controller="StatusController as ctrl">
+<html ng-strict-di ng-controller="StatusController as ctrl">
 <head>
 <head>
     <meta charset="utf-8">
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -138,7 +138,7 @@
     <script src="node_modules/croppie/croppie.min.js?v=[[VERSION]]"></script>
     <script src="node_modules/croppie/croppie.min.js?v=[[VERSION]]"></script>
 
 
     <!-- App -->
     <!-- App -->
-    <script src="dist/app.bundle.js?v=[[VERSION]]"></script>
+    <script src="app.bundle.js?v=[[VERSION]]"></script>
 
 
 </body>
 </body>
 </html>
 </html>

+ 26 - 5
karma.conf.js

@@ -2,16 +2,33 @@ module.exports = function(config) {
 
 
     var configuration = {
     var configuration = {
         frameworks: ['jasmine'],
         frameworks: ['jasmine'],
+        mime: {
+            'application/wasm': ['wasm'],
+        },
         files: [
         files: [
+            // Angular core
             'node_modules/angular/angular.js',
             'node_modules/angular/angular.js',
-            'node_modules/angular-mocks/angular-mocks.js',
-            'node_modules/angular-translate/dist/angular-translate.min.js',
             'node_modules/angular-aria/angular-aria.min.js',
             'node_modules/angular-aria/angular-aria.min.js',
             'node_modules/angular-animate/angular-animate.min.js',
             'node_modules/angular-animate/angular-animate.min.js',
+            'node_modules/angular-sanitize/angular-sanitize.min.js',
+            'node_modules/angular-route/angular-route.min.js',
             'node_modules/angular-material/angular-material.min.js',
             'node_modules/angular-material/angular-material.min.js',
+            'node_modules/angular-translate/dist/angular-translate.min.js',
+
+            // Angular mocking
+            'node_modules/angular-mocks/angular-mocks.js',
+
+            // SaltyRTC
             'node_modules/@saltyrtc/chunked-dc/dist/chunked-dc.es5.js',
             'node_modules/@saltyrtc/chunked-dc/dist/chunked-dc.es5.js',
-            'dist/app.bundle.js',
-            'dist/unittests.bundle.js',
+
+            // App bundles
+            'dist/generated/app_noinit.bundle.js',
+            'dist/generated/unittest_karma.bundle.js',
+            {pattern: 'dist/generated/[0-9].*.bundle.js', included: false, serve: true},
+            {pattern: 'dist/generated/*.module.wasm', included: false, serve: true, type: 'wasm'},
+
+            // Tests
+            'tests/init.js',
             'tests/filters.js',
             'tests/filters.js',
             'tests/service/message.js',
             'tests/service/message.js',
             'tests/service/mime.js',
             'tests/service/mime.js',
@@ -23,8 +40,12 @@ module.exports = function(config) {
             'tests/service/keystore.js',
             'tests/service/keystore.js',
             'tests/service/notification.js',
             'tests/service/notification.js',
             'tests/service/receiver.js',
             'tests/service/receiver.js',
-            'tests/ts/helpers.ts',
         ],
         ],
+        proxies: {
+            // Also serve all generated files on the root.
+            // This is required for the .wasm modules.
+            '/dist/generated/': '/base/dist/generated/',
+        },
         customLaunchers: {
         customLaunchers: {
             Chromium_ci_gitlab: {
             Chromium_ci_gitlab: {
                 base: 'Chromium',
                 base: 'Chromium',

File diff ditekan karena terlalu besar
+ 520 - 1121
package-lock.json


+ 8 - 5
package.json

@@ -10,18 +10,18 @@
     "build:tests": "echo -e 'NOTE: Use either \"npm build:unittests\" or \"npm build:uitests\"\n' && exit 1",
     "build:tests": "echo -e 'NOTE: Use either \"npm build:unittests\" or \"npm build:uitests\"\n' && exit 1",
     "build:unittests": "webpack --config webpack.tests.js",
     "build:unittests": "webpack --config webpack.tests.js",
     "build:uitests": "npm run build:css && webpack --config webpack.tests.js",
     "build:uitests": "npm run build:css && webpack --config webpack.tests.js",
-    "dist": "npm run build && echo \"\" && node dist/build-package.js",
+    "dist": "npm run clean && npm run build && echo \"\" && node dist/build-package.js",
     "serve:live": "echo 'NOTE: serve:live command has been renamed to devserver'",
     "serve:live": "echo 'NOTE: serve:live command has been renamed to devserver'",
     "devserver": "npm run build:css && concurrently --kill-others --names \"css,server\" -p name \"npm run build:css:watch\" \"npx webpack-dev-server --config webpack.dev.js\"",
     "devserver": "npm run build:css && concurrently --kill-others --names \"css,server\" -p name \"npm run build:css:watch\" \"npx webpack-dev-server --config webpack.dev.js\"",
     "testserver": "npx webpack-dev-server --config webpack.tests.js",
     "testserver": "npx webpack-dev-server --config webpack.tests.js",
     "test": "echo -e 'NOTE: Use either \"npm run test:unit\" or \"npm run test:ui\"\n' && exit 1",
     "test": "echo -e 'NOTE: Use either \"npm run test:unit\" or \"npm run test:ui\"\n' && exit 1",
     "test:unit": "npm run build:unittests && karma start --single-run --log-level=debug --colors",
     "test:unit": "npm run build:unittests && karma start --single-run --log-level=debug --colors",
-    "test:ui": "npm run build:uitests && bash tests/ui/run.sh",
+    "test:ui": "bash tests/ui/run.sh",
     "lint": "npm run lint:ts && npm run lint:sass",
     "lint": "npm run lint:ts && npm run lint:sass",
     "lint:ts": "tslint -c tslint.json --project tsconfig.json --exclude \"**/src/config.ts\"",
     "lint:ts": "tslint -c tslint.json --project tsconfig.json --exclude \"**/src/config.ts\"",
     "lint:sass": "sass-lint -c .sass-lint.yml -v -q",
     "lint:sass": "sass-lint -c .sass-lint.yml -v -q",
     "lint:sass:fix": "sass-lint-auto-fix -c .sass-lint.yml",
     "lint:sass:fix": "sass-lint-auto-fix -c .sass-lint.yml",
-    "clean": "rm -rf js/ build/ dist/app*"
+    "clean": "rm -rf dist/generated"
   },
   },
   "keywords": [
   "keywords": [
     "threema",
     "threema",
@@ -35,12 +35,14 @@
   "homepage": "https://threema.ch/",
   "homepage": "https://threema.ch/",
   "dependencies": {
   "dependencies": {
     "@babel/core": "^7.4.3",
     "@babel/core": "^7.4.3",
+    "@babel/plugin-syntax-dynamic-import": "^7.2.0",
     "@babel/plugin-transform-runtime": "^7.4.3",
     "@babel/plugin-transform-runtime": "^7.4.3",
     "@babel/preset-env": "^7.4.3",
     "@babel/preset-env": "^7.4.3",
     "@babel/runtime": "^7.4.3",
     "@babel/runtime": "^7.4.3",
     "@saltyrtc/client": "^0.13.2",
     "@saltyrtc/client": "^0.13.2",
     "@saltyrtc/task-relayed-data": "^0.3.1",
     "@saltyrtc/task-relayed-data": "^0.3.1",
     "@saltyrtc/task-webrtc": "^0.13.0",
     "@saltyrtc/task-webrtc": "^0.13.0",
+    "@threema/compose-area": "^0.3.1",
     "@types/angular": "^1.6.53",
     "@types/angular": "^1.6.53",
     "@types/angular-material": "^1.1.62",
     "@types/angular-material": "^1.1.62",
     "@types/angular-sanitize": "^1.3.7",
     "@types/angular-sanitize": "^1.3.7",
@@ -64,6 +66,7 @@
     "babel-loader": "^8.0.5",
     "babel-loader": "^8.0.5",
     "core-js": "^3.0.1",
     "core-js": "^3.0.1",
     "croppie": "^2.6.3",
     "croppie": "^2.6.3",
+    "emoji-regex": "^8.0.0",
     "file-saver": "2.0.0",
     "file-saver": "2.0.0",
     "messageformat": "^2.0.5",
     "messageformat": "^2.0.5",
     "msgpack-lite": "~0.1.26",
     "msgpack-lite": "~0.1.26",
@@ -83,14 +86,14 @@
   "devDependencies": {
   "devDependencies": {
     "@types/chai": "^4.1.7",
     "@types/chai": "^4.1.7",
     "@types/jasmine": "^3.3.5",
     "@types/jasmine": "^3.3.5",
-    "@types/selenium-webdriver": "^3.0.14",
+    "@types/selenium-webdriver": "^4.0.0",
     "angular-mocks": "^1.7.5",
     "angular-mocks": "^1.7.5",
     "chai": "^4.2.0",
     "chai": "^4.2.0",
     "concurrently": "~4.1.0",
     "concurrently": "~4.1.0",
     "geckodriver": "^1.14.1",
     "geckodriver": "^1.14.1",
     "jasmine": "^3.3.1",
     "jasmine": "^3.3.1",
     "jasmine-core": "^3.3.0",
     "jasmine-core": "^3.3.0",
-    "karma": "^3.1.4",
+    "karma": "^4.1.0",
     "karma-chrome-launcher": "^2.2.0",
     "karma-chrome-launcher": "^2.2.0",
     "karma-firefox-launcher": "^1.1.0",
     "karma-firefox-launcher": "^1.1.0",
     "karma-jasmine": "^2.0.1",
     "karma-jasmine": "^2.0.1",

+ 28 - 0
src/bootstrap.ts

@@ -0,0 +1,28 @@
+/**
+ * Copyright © 2016-2019 Threema GmbH (https://threema.ch/).
+ *
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// tslint:disable:no-console
+
+// A dependency graph that contains any wasm must all be imported asynchronously.
+import('./app')
+    .then(() => {
+        console.info('Bundle loaded, bootstrapping application.');
+        angular.bootstrap(document, ['3ema']);
+    })
+    .catch((e) => console.error('Could not bootstrap application', e));

+ 185 - 375
src/directives/compose_area.ts

@@ -15,15 +15,15 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-import * as twemoji from 'twemoji';
+import {ComposeArea} from '@threema/compose-area';
 
 
-import {extractText, hasValue, isActionTrigger, logAdapter, replaceWhitespace} from '../helpers';
-import {emojify, shortnameToUnicode} from '../helpers/emoji';
+import {isActionTrigger} from '../helpers';
+import {emojifyNew, shortnameToUnicode} from '../helpers/emoji';
 import {BrowserService} from '../services/browser';
 import {BrowserService} from '../services/browser';
 import {ReceiverService} from '../services/receiver';
 import {ReceiverService} from '../services/receiver';
 import {StringService} from '../services/string';
 import {StringService} from '../services/string';
 import {TimeoutService} from '../services/timeout';
 import {TimeoutService} from '../services/timeout';
-import {isElementNode, isTextNode} from '../typeguards';
+import {isEmojiInfo} from '../typeguards';
 
 
 /**
 /**
  * The compose area where messages are written.
  * The compose area where messages are written.
@@ -39,6 +39,7 @@ export default [
     '$filter',
     '$filter',
     '$log',
     '$log',
     '$rootScope',
     '$rootScope',
+    'CONFIG',
     function(browserService: BrowserService,
     function(browserService: BrowserService,
              stringService: StringService,
              stringService: StringService,
              timeoutService: TimeoutService,
              timeoutService: TimeoutService,
@@ -48,10 +49,14 @@ export default [
              $mdDialog: ng.material.IDialogService,
              $mdDialog: ng.material.IDialogService,
              $filter: ng.IFilterService,
              $filter: ng.IFilterService,
              $log: ng.ILogService,
              $log: ng.ILogService,
-             $rootScope: ng.IRootScopeService) {
+             $rootScope: ng.IRootScopeService,
+             CONFIG: threema.Config) {
         return {
         return {
             restrict: 'EA',
             restrict: 'EA',
             scope: {
             scope: {
+                // Callback to get a reference to the initialized ComposeArea instance.
+                onInit: '=',
+
                 // Callback to submit text or file data
                 // Callback to submit text or file data
                 submit: '=',
                 submit: '=',
 
 
@@ -70,7 +75,7 @@ export default [
 
 
                 receiver: '<receiver',
                 receiver: '<receiver',
             },
             },
-            link: function(scope: any, element) {
+            link: function(scope: any, wrapper: JQLite) {
                 // Logging
                 // Logging
                 const logTag = '[Directives.ComposeArea]';
                 const logTag = '[Directives.ComposeArea]';
 
 
@@ -79,13 +84,19 @@ export default [
                 const TRIGGER_ACTIVE_CSS_CLASS = 'is-active';
                 const TRIGGER_ACTIVE_CSS_CLASS = 'is-active';
 
 
                 // Elements
                 // Elements
-                const composeArea: any = element;
-                const composeDiv: any = angular.element(element[0].querySelector('div.compose'));
-                const emojiTrigger: any = angular.element(element[0].querySelector('i.emoji-trigger'));
-                const emojiKeyboard: any = angular.element(element[0].querySelector('.emoji-keyboard'));
-                const sendTrigger: any = angular.element(element[0].querySelector('i.send-trigger'));
-                const fileTrigger: any = angular.element(element[0].querySelector('i.file-trigger'));
-                const fileInput: any = angular.element(element[0].querySelector('input.file-input'));
+                const select = (selector) => angular.element(wrapper[0].querySelector(selector));
+                const composeDiv = select('div.compose') as JQuery<HTMLElement>;
+                const emojiTrigger = select('i.emoji-trigger') as JQuery<HTMLElement>;
+                const emojiKeyboard = select('.emoji-keyboard') as JQuery<HTMLElement>;
+                const sendTrigger = select('i.send-trigger') as JQuery<HTMLElement>;
+                const fileTrigger = select('i.file-trigger') as JQuery<HTMLElement>;
+                const fileInput = select('input.file-input') as JQuery<HTMLInputElement>;
+
+                // Initialize compose area lib
+                const composeArea = ComposeArea.bind_to(composeDiv[0], CONFIG.DEBUG ? 'debug' : 'warn');
+                if (scope.onInit) {
+                    scope.onInit(composeArea);
+                }
 
 
                 // Set initial text
                 // Set initial text
                 if (scope.initialData.initialText) {
                 if (scope.initialData.initialText) {
@@ -95,16 +106,6 @@ export default [
                     composeDiv[0].innerText = scope.initialData.draft;
                     composeDiv[0].innerText = scope.initialData.draft;
                 }
                 }
 
 
-                // The current caret position, used when inserting objects
-                let caretPosition: {
-                    // The position in the source HTML
-                    from?: number,
-                    to?: number,
-                    // The position in the visible character list
-                    fromChar?: number,
-                    toChar?: number,
-                } = null;
-
                 let chatBlocked = false;
                 let chatBlocked = false;
 
 
                 // Function to update blocking state
                 // Function to update blocking state
@@ -115,7 +116,7 @@ export default [
                         sendTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
                         sendTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
                         emojiTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
                         emojiTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
                         fileTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
                         fileTrigger.removeClass(TRIGGER_ENABLED_CSS_CLASS);
-                        composeDiv.attr('contenteditable', false);
+                        composeDiv.attr('contenteditable', 'false');
                         if (emojiKeyboard.hasClass('active')) {
                         if (emojiKeyboard.hasClass('active')) {
                             hideEmojiPicker();
                             hideEmojiPicker();
                         }
                         }
@@ -127,7 +128,7 @@ export default [
                         }
                         }
                         emojiTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
                         emojiTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
                         fileTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
                         fileTrigger.addClass(TRIGGER_ENABLED_CSS_CLASS);
-                        composeDiv.attr('contenteditable', true);
+                        composeDiv.attr('contenteditable', 'true');
                     }
                     }
                 }
                 }
 
 
@@ -144,38 +145,6 @@ export default [
                     },
                     },
                 );
                 );
 
 
-                /**
-                 * Stop propagation of click events and hold htmlElement of the emojipicker
-                 */
-                const EmojiPickerContainer = (function() {
-                    let instance;
-
-                    function click(e) {
-                        e.stopPropagation();
-                    }
-
-                    return {
-                        get: function() {
-                            if (instance === undefined) {
-                                instance = {
-                                    htmlElement: composeArea[0].querySelector('div.twemoji-picker'),
-                                };
-                                // append stop propagation
-                                angular.element(instance.htmlElement).on('click', click);
-
-                            }
-                            return instance;
-                        },
-                        destroy: function() {
-                            if (instance !== undefined) {
-                                // remove stop propagation
-                                angular.element(instance.htmlElement).off('click', click);
-                                instance = undefined;
-                            }
-                        },
-                    };
-                })();
-
                 // Typing events
                 // Typing events
                 let stopTypingTimer: ng.IPromise<void> = null;
                 let stopTypingTimer: ng.IPromise<void> = null;
 
 
@@ -208,24 +177,14 @@ export default [
 
 
                 // Determine whether field is empty
                 // Determine whether field is empty
                 function composeAreaIsEmpty() {
                 function composeAreaIsEmpty() {
-                    const text = extractText(composeDiv[0], logAdapter($log.warn, logTag));
-                    return text.length === 0;
+                    return composeArea.get_text().length === 0;
                 }
                 }
 
 
                 // Submit the text from the compose area.
                 // Submit the text from the compose area.
                 //
                 //
                 // Emoji images are converted to their alt text in this process.
                 // Emoji images are converted to their alt text in this process.
                 function submitText(): Promise<any> {
                 function submitText(): Promise<any> {
-                    const rawText = extractText(composeDiv[0], logAdapter($log.warn, logTag));
-
-                    // Due to #731, and the hack introduced in #706, the
-                    // extracted text may contain non-breaking spaces (U+00A0).
-                    // Replace them with actual whitespace to avoid strange
-                    // behavior when submitting the text.
-                    //
-                    // TODO: Remove this once we have a compose area rewrite and can
-                    // fix the actual bug.
-                    const text = rawText.replace(/\u00A0/g, ' ');
+                    const text = composeArea.get_text().replace(/\r/g, '');
 
 
                     return new Promise((resolve, reject) => {
                     return new Promise((resolve, reject) => {
                         const submitTexts = (strings: string[]) => {
                         const submitTexts = (strings: string[]) => {
@@ -241,9 +200,8 @@ export default [
                                 .catch(reject);
                                 .catch(reject);
                         };
                         };
 
 
-                        const fullText = text.trim().replace(/\r/g, '');
-                        if (fullText.length > scope.maxTextLength) {
-                            const pieces: string[] = stringService.byteChunk(fullText, scope.maxTextLength, 50);
+                        if (text.length > scope.maxTextLength) {
+                            const pieces: string[] = stringService.byteChunk(text, scope.maxTextLength, 50);
                             const confirm = $mdDialog.confirm()
                             const confirm = $mdDialog.confirm()
                                 .title($translate.instant('messenger.MESSAGE_TOO_LONG_SPLIT_SUBJECT'))
                                 .title($translate.instant('messenger.MESSAGE_TOO_LONG_SPLIT_SUBJECT'))
                                 .textContent($translate.instant('messenger.MESSAGE_TOO_LONG_SPLIT_BODY', {
                                 .textContent($translate.instant('messenger.MESSAGE_TOO_LONG_SPLIT_BODY', {
@@ -259,7 +217,7 @@ export default [
                                 reject();
                                 reject();
                             });
                             });
                         } else {
                         } else {
-                            submitTexts([fullText]);
+                            submitTexts([text]);
                         }
                         }
                     });
                     });
                 }
                 }
@@ -268,8 +226,8 @@ export default [
                     if (!composeAreaIsEmpty()) {
                     if (!composeAreaIsEmpty()) {
                         submitText().then(() => {
                         submitText().then(() => {
                             // Clear compose div
                             // Clear compose div
-                            composeDiv[0].innerText = '';
-                            composeDiv[0].focus();
+                            composeArea.clear();
+                            composeArea.focus();
 
 
                             // Send stopTyping event
                             // Send stopTyping event
                             stopTyping();
                             stopTyping();
@@ -289,6 +247,17 @@ export default [
                 }
                 }
 
 
                 // Handle typing events
                 // Handle typing events
+
+                let isComposing = false;
+
+                function onCompositionStart(ev: CompositionEvent): void {
+                    isComposing = true;
+                }
+
+                function onCompositionEnd(ev: CompositionEvent): void {
+                    isComposing = false;
+                }
+
                 function onKeyDown(ev: KeyboardEvent): void {
                 function onKeyDown(ev: KeyboardEvent): void {
                     // If enter is pressed, prevent default event from being dispatched
                     // If enter is pressed, prevent default event from being dispatched
                     if (!ev.shiftKey && ev.key === 'Enter') {
                     if (!ev.shiftKey && ev.key === 'Enter') {
@@ -301,6 +270,22 @@ export default [
                         return;
                         return;
                     }
                     }
 
 
+                    // If the enter key is part of a composition (e.g. when
+                    // entering text with an IME), don't submit the text.
+                    // See https://github.com/threema-ch/threema-web/issues/777
+                    if ((ev as any).isComposing || isComposing) {
+                        return;
+                    }
+
+                    // If a : is pressed, possibly insert emoji
+                    if (ev.key === ':') {
+                        const modified = onEmojiShortcodeKeyPressed(ev, ':', false);
+                        if (modified) {
+                            ev.preventDefault();
+                            return;
+                        }
+                    }
+
                     // At link time, the element is not yet evaluated.
                     // At link time, the element is not yet evaluated.
                     // Therefore add following code to end of event loop.
                     // Therefore add following code to end of event loop.
                     $timeout(() => {
                     $timeout(() => {
@@ -316,39 +301,23 @@ export default [
                 }
                 }
 
 
                 function onKeyUp(ev: KeyboardEvent): void {
                 function onKeyUp(ev: KeyboardEvent): void {
-                    // At link time, the element is not yet evaluated.
-                    // Therefore add following code to end of event loop.
-                    $timeout(() => {
-                        // If the compose area contains only a single <br>, make it fully empty.
-                        // See also: https://stackoverflow.com/q/14638887/284318
-                        const text = extractText(composeDiv[0], logAdapter($log.warn, logTag), false);
-                        if (text === '\n') {
-                            composeDiv[0].innerText = '';
-                        } else if ((ev.keyCode === 190 || ev.key === ':') && caretPosition !== null) {
-                            // A ':' is pressed, try to parse
-                            const currentWord = stringService.getWord(text, caretPosition.fromChar, [':']);
-                            if (currentWord.realLength > 2 && currentWord.word.substr(0, 1) === ':') {
-                                const trimmed = currentWord.word.substr(1, currentWord.word.length - 2);
-                                const unicodeEmoji = shortnameToUnicode(trimmed);
-                                if (unicodeEmoji !== null) {
-                                    return insertEmoji(unicodeEmoji,
-                                        caretPosition.from - currentWord.realLength,
-                                        caretPosition.to);
-                                }
-                            }
-                        }
+                    // If the compose area contains only a single <br>, make it fully empty.
+                    // See also: https://stackoverflow.com/q/14638887/284318
+                    const text = composeArea.get_text(true);
+                    if (text === '\n') {
+                        composeDiv[0].innerText = '';
+                    }
 
 
-                        // Update typing information (use text instead method)
-                        if (text.trim().length === 0 || caretPosition === null) {
-                            stopTyping();
-                            scope.onTyping('');
-                        } else {
-                            startTyping();
-                            scope.onTyping(text.trim(), stringService.getWord(text, caretPosition.from));
-                        }
+                    // Update typing information
+                    if (text.trim().length === 0) {
+                        stopTyping();
+                        scope.onTyping('');
+                    } else {
+                        startTyping();
+                        scope.onTyping(text.trim());
+                    }
 
 
-                        updateView();
-                    }, 0);
+                    updateView();
                 }
                 }
 
 
                 // Function to fetch file contents
                 // Function to fetch file contents
@@ -479,23 +448,17 @@ export default [
                     // Handle pasting of text
                     // Handle pasting of text
                     } else if (textIdx !== null) {
                     } else if (textIdx !== null) {
                         const text = ev.clipboardData.getData('text/plain');
                         const text = ev.clipboardData.getData('text/plain');
-
-                        // Look up some filter functions
-                        // tslint:disable-next-line:max-line-length
-                        const escapeHtml = $filter('escapeHtml') as (a: string) => string;
-                        const mentionify = $filter('mentionify') as (a: string) => string;
-                        const nlToBr = $filter('nlToBr') as (a: string, b?: boolean) => string;
-
-                        // Escape HTML markup
-                        const escaped = escapeHtml(text);
-
-                        // Apply filters (emojify, convert newline, etc)
-                        const formatted = emojify(mentionify(replaceWhitespace(nlToBr(escaped, true))));
-
-                        // Insert resulting HTML
-                        document.execCommand('insertHTML', false, formatted);
-
-                        updateView();
+                        if (text) {
+                            const tokens = emojifyNew(text);
+                            for (const token of tokens) {
+                                if (isEmojiInfo(token)) {
+                                    insertEmoji(token);
+                                } else {
+                                    composeArea.insert_text(token);
+                                }
+                            }
+                            updateView();
+                        }
                     }
                     }
                 }
                 }
 
 
@@ -507,46 +470,48 @@ export default [
 
 
                 // Show emoji picker element
                 // Show emoji picker element
                 function showEmojiPicker() {
                 function showEmojiPicker() {
-                    const emojiPicker: HTMLElement = EmojiPickerContainer.get().htmlElement;
+                    // If the emoji picker is triggered too early, it's possible that the picker element
+                    // has not yet been fully loaded (e.g. during UI tests). Therefore enqueue the event
+                    // handler at the end of the event loop.
+                    $timeout(() => {
+                        const emojiPicker = wrapper[0].querySelector('div.twemoji-picker');
 
 
-                    // Show
-                    emojiKeyboard.addClass('active');
-                    emojiTrigger.addClass(TRIGGER_ACTIVE_CSS_CLASS);
+                        // Show
+                        emojiKeyboard.addClass('active');
+                        emojiTrigger.addClass(TRIGGER_ACTIVE_CSS_CLASS);
 
 
-                    // Find some selectors
-                    const allEmoji: any = angular.element(emojiPicker.querySelectorAll('.content .em'));
-                    const allEmojiTabs: any = angular.element(emojiPicker.querySelectorAll('.tab label img'));
+                        // Find some selectors
+                        const allEmoji = angular.element(emojiPicker.querySelectorAll('.content .em'));
+                        const allEmojiTabs = angular.element(emojiPicker.querySelectorAll('.tab label img'));
 
 
-                    // Add event handlers
-                    allEmoji.on('click', onEmojiChosen);
-                    allEmojiTabs.on('keydown', onEmojiTabSelected);
+                        // Add event handlers
+                        allEmoji.on('click', onEmojiChosen as any);
+                        allEmojiTabs.on('keydown', onEmojiTabSelected as any);
 
 
-                    // set focus to fix chat scroll bug
-                    $timeout(() => {
-                        composeDiv[0].focus();
+                        // Focus compose area again
+                        composeArea.focus();
                     });
                     });
                 }
                 }
 
 
                 // Hide emoji picker element
                 // Hide emoji picker element
                 function hideEmojiPicker() {
                 function hideEmojiPicker() {
-                    const emojiPicker: HTMLElement = EmojiPickerContainer.get().htmlElement;
+                    const emojiPicker = wrapper[0].querySelector('div.twemoji-picker');
 
 
                     // Hide
                     // Hide
                     emojiKeyboard.removeClass('active');
                     emojiKeyboard.removeClass('active');
                     emojiTrigger.removeClass(TRIGGER_ACTIVE_CSS_CLASS);
                     emojiTrigger.removeClass(TRIGGER_ACTIVE_CSS_CLASS);
 
 
                     // Find some selectors
                     // Find some selectors
-                    const allEmoji: any = angular.element(emojiPicker.querySelectorAll('.content .em'));
-                    const allEmojiTabs: any = angular.element(emojiPicker.querySelectorAll('.tab label img'));
+                    const allEmoji = angular.element(emojiPicker.querySelectorAll('.content .em'));
+                    const allEmojiTabs = angular.element(emojiPicker.querySelectorAll('.tab label img'));
 
 
                     // Remove event handlers
                     // Remove event handlers
-                    allEmoji.off('click', onEmojiChosen);
-                    allEmojiTabs.off('keydown', onEmojiTabSelected);
-                    EmojiPickerContainer.destroy();
+                    allEmoji.off('click', onEmojiChosen as any);
+                    allEmojiTabs.off('keydown', onEmojiTabSelected as any);
                 }
                 }
 
 
                 // Emoji trigger is clicked
                 // Emoji trigger is clicked
-                function onEmojiTrigger(ev: UIEvent): void {
+                function onEmojiTrigger(ev: KeyboardEvent): void {
                     ev.stopPropagation();
                     ev.stopPropagation();
                     if (chatBlocked) {
                     if (chatBlocked) {
                         hideEmojiPicker();
                         hideEmojiPicker();
@@ -563,124 +528,74 @@ export default [
                 // Emoji is chosen
                 // Emoji is chosen
                 function onEmojiChosen(ev: MouseEvent): void {
                 function onEmojiChosen(ev: MouseEvent): void {
                     ev.stopPropagation();
                     ev.stopPropagation();
-                    insertEmoji(this.textContent);
+                    insertSingleEmojiString((ev.target as Element).textContent);
                 }
                 }
 
 
                 // Emoji tab is selected
                 // Emoji tab is selected
                 function onEmojiTabSelected(ev: KeyboardEvent): void {
                 function onEmojiTabSelected(ev: KeyboardEvent): void {
                     if (ev.key === ' ' || ev.key === 'Enter') {
                     if (ev.key === ' ' || ev.key === 'Enter') {
                         // Warning: Hacky
                         // Warning: Hacky
-                        this.parentElement.previousElementSibling.checked = true;
+                        (ev.target as any).parentElement.previousElementSibling.checked = true;
                     }
                     }
                 }
                 }
 
 
-                function insertEmoji(emoji, posFrom?: number, posTo?: number): void {
-                    const emojiElement = emojify(emoji);
-                    insertHTMLElement(emoji, emojiElement, posFrom, posTo);
+                // Insert a single emoji, passed in as string
+                function insertSingleEmojiString(emojiString: string): void {
+                    const tokens = emojifyNew(emojiString);
+                    if (tokens.length !== 1) {
+                        throw new Error(`Emoji parsing failed: Expected 1 element, found ${tokens.length}`);
+                    }
+                    const emoji = tokens[0];
+                    if (!isEmojiInfo(emoji)) {
+                        throw new Error(`Emoji parsing failed: Returned text, not emoji info`);
+                    }
+                    insertEmoji(emoji);
                 }
                 }
 
 
-                function insertMention(mentionString, posFrom?: number, posTo?: number): void {
-                    const mentionElement = ($filter('mentionify') as any)(mentionString) as string;
-                    insertHTMLElement(mentionString, mentionElement, posFrom, posTo);
+                // Insert a single emoji
+                function insertEmoji(emoji: threema.EmojiInfo): void {
+                    const img: HTMLElement = composeArea.insert_image(emoji.imgPath, emoji.emojiString, 'em');
+                    img.setAttribute('data-c', emoji.codepoint);
+                    img.draggable = false;
+                    img.ondragstart = () => false;
                 }
                 }
 
 
-                function insertHTMLElement(
-                    elementText: string, // The element as the original text representation, not yet converted to HTML
-                    elementHtml: string, // The element converted to HTML
-                    posFrom?: number,
-                    posTo?: number,
-                ): void {
-                    // In Chrome in right-to-left mode, our content editable
-                    // area may contain a DIV element.
-                    const childNodes = composeDiv[0].childNodes;
-                    const nestedDiv = childNodes.length === 1
-                        && childNodes[0].tagName !== undefined
-                        && childNodes[0].tagName.toLowerCase() === 'div';
-                    let contentElement;
-                    if (nestedDiv === true) {
-                        contentElement = composeDiv[0].childNodes[0];
-                    } else {
-                        contentElement = composeDiv[0];
+                // The emoji shortcode trigger (:) was inserted. Return a boolean
+                // indicating whether the compose area contents were modified.
+                //
+                // The `alreadyProcessed` indicates whether the key has already
+                // been processed in the DOM (onKeyUp) or not (onKeyDown).
+                function onEmojiShortcodeKeyPressed(ev, trigger: string, alreadyProcessed: boolean): boolean {
+                    const word = composeArea.get_word_at_caret();
+                    if (word === undefined) {
+                        return false;
                     }
                     }
-
-                    let currentHtml = '';
-                    for (let i = 0; i < contentElement.childNodes.length; i++) {
-                        const node: Node = contentElement.childNodes[i];
-
-                        if (isTextNode(node)) {
-                            currentHtml += node.textContent;
-                        } else if (isElementNode(node)) {
-                            const tag = node.tagName.toLowerCase();
-                            if (tag === 'img' || tag === 'span') {
-                                currentHtml += getOuterHtml(node);
-                            } else if (tag === 'br') {
-                                // Firefox inserts a <br> after editing content editable fields.
-                                // Remove the last <br> to fix this.
-                                if (i < contentElement.childNodes.length - 1) {
-                                    currentHtml += getOuterHtml(node);
-                                }
-                            } else if (tag === 'div') {
-                                // Safari inserts a <div><br></div> after editing content editable fields.
-                                // Remove the last instance to fix this.
-                                if (node.childNodes.length === 1
-                                    && isElementNode(node.lastChild)
-                                    && node.lastChild.tagName.toLowerCase() === 'br') {
-                                    // Ignore
-                                } else {
-                                    currentHtml += getOuterHtml(node);
-                                }
+                    let before = word.before();
+                    const after = word.after();
+                    if (!alreadyProcessed) {
+                        before += trigger;
+                    }
+                    if (after.length === 0 && before.length > 2) {
+                        if (before.startsWith(trigger) && before.endsWith(trigger)) {
+                            const trimmed = before.substr(1, before.length - 2);
+                            const unicodeEmoji = shortnameToUnicode(trimmed);
+                            if (unicodeEmoji !== null) {
+                                composeArea.select_word_at_caret();
+                                composeArea.store_selection_range();
+                                insertSingleEmojiString(unicodeEmoji);
+                                return true;
                             }
                             }
                         }
                         }
                     }
                     }
-
-                    // Because the browser may transform HTML code when
-                    // inserting it into the DOM, we temporarily write it to a
-                    // DOM element to ensure that the current representation
-                    // corresponds to the representation when inserted into the
-                    // DOM. (See #671 for details.)
-                    const tmpDiv = document.createElement('div');
-                    tmpDiv.innerHTML = elementHtml;
-                    const cleanedElementHtml = tmpDiv.innerHTML;
-
-                    // Insert element into currentHtml and determine new caret position
-                    let newPos = posFrom;
-                    if (caretPosition !== null) {
-                        // If the caret position is set, then the user has moved around
-                        // in the contenteditable field and might not be ad the end
-                        // of the line.
-                        posFrom = posFrom === undefined ? caretPosition.from : posFrom;
-                        posTo = posTo === undefined ? caretPosition.to : posTo;
-
-                        currentHtml = currentHtml.substr(0, posFrom)
-                            + cleanedElementHtml
-                            + currentHtml.substr(posTo);
-
-                        // Change caret position
-                        caretPosition.from += cleanedElementHtml.length;
-                        caretPosition.fromChar += elementText.length;
-                        newPos = posFrom + cleanedElementHtml.length;
-                    } else {
-                        // If the caret position is not set, then the user must be at the
-                        // end of the line. Insert element there.
-                        newPos = currentHtml.length;
-                        currentHtml += cleanedElementHtml;
-                        caretPosition = {
-                            from: currentHtml.length,
-                        };
-                    }
-                    caretPosition.to = caretPosition.from;
-                    caretPosition.toChar = caretPosition.fromChar;
-
-                    contentElement.innerHTML = currentHtml;
-                    setCaretPosition(newPos);
-
-                    // Update the draft text
-                    const text = extractText(composeDiv[0], logAdapter($log.warn, logTag));
-                    scope.onTyping(text);
-
-                    updateView();
+                    return false;
                 }
                 }
 
 
+                // TODO
+                // function insertMention(mentionString, posFrom?: number, posTo?: number): void {
+                //     const mentionElement = ($filter('mentionify') as any)(mentionString) as string;
+                //     insertHTMLElement(mentionString, mentionElement, posFrom, posTo);
+                // }
+
                 // File trigger is clicked
                 // File trigger is clicked
                 function onFileTrigger(ev: UIEvent): void {
                 function onFileTrigger(ev: UIEvent): void {
                     ev.preventDefault();
                     ev.preventDefault();
@@ -688,7 +603,7 @@ export default [
                     if (chatBlocked) {
                     if (chatBlocked) {
                         return;
                         return;
                     }
                     }
-                    const input = element[0].querySelector('.file-input') as HTMLInputElement;
+                    const input = wrapper[0].querySelector('.file-input') as HTMLInputElement;
                     input.click();
                     input.click();
                 }
                 }
 
 
@@ -716,137 +631,37 @@ export default [
                     }
                     }
                 }
                 }
 
 
-                // return the outer html of a node element
-                function getOuterHtml(node: Node): string {
-                    const pseudoElement = document.createElement('pseudo');
-                    pseudoElement.appendChild(node.cloneNode(true));
-                    return pseudoElement.innerHTML;
-                }
-
-                // return the html code position of the container element
-                function getPositions(offset: number, container: Node): { html: number, text: number } {
-                    let pos = null;
-                    let textPos = null;
-
-                    if (composeDiv[0].contains(container)) {
-                        let selectedElement;
-                        if (container === composeDiv[0]) {
-                            if (offset === 0) {
-                                return {
-                                    html: 0, text: 0,
-                                };
-                            }
-                            selectedElement = composeDiv[0].childNodes[offset - 1];
-                            pos = 0;
-                            textPos = 0;
-                        } else {
-                            selectedElement = container.previousSibling;
-                            pos = offset;
-                            textPos = offset;
-                        }
-
-                        while (selectedElement !== null) {
-                            if (selectedElement.nodeType === Node.TEXT_NODE) {
-                                pos += selectedElement.textContent.length;
-                                textPos += selectedElement.textContent.length;
-                            } else {
-                                pos += getOuterHtml(selectedElement).length;
-                                textPos += 1;
-                            }
-                            selectedElement = selectedElement.previousSibling;
-                        }
-                    }
-                    return {
-                        html: pos,
-                        text: textPos,
-                    };
-                }
-
-                // Update the current caret position or selection
-                function updateCaretPosition() {
-                    caretPosition = null;
-                    if (window.getSelection && composeDiv[0].innerHTML.length > 0) {
-                        const selection = window.getSelection();
-                        if (selection.rangeCount) {
-                            const range = selection.getRangeAt(0);
-                            const from = getPositions(range.startOffset, range.startContainer);
-                            if (from !== null && from.html >= 0) {
-                                const to = getPositions(range.endOffset, range.endContainer);
-                                caretPosition = {
-                                    from: from.html,
-                                    to: to.html,
-                                    fromChar: from.text,
-                                    toChar: to.text,
-                                };
-                            }
-                        }
-                    }
-                }
-
-                // Set the correct cart position in the content editable div.
-                // Pos is the position in the html content (not in the visible plain text).
-                function setCaretPosition(pos: number) {
-                    const rangeAt = (node: Node, offset?: number) => {
-                        const range = document.createRange();
-                        range.collapse(false);
-                        if (offset !== undefined) {
-                            range.setStart(node, offset);
-                        } else {
-                            range.setStartAfter(node);
-                        }
-                        const sel = window.getSelection();
-                        sel.removeAllRanges();
-                        sel.addRange(range);
-                    };
-
-                    for (let i = 0; i < composeDiv[0].childNodes.length; i++) {
-                        const node = composeDiv[0].childNodes[i];
-                        let size;
-                        let offset;
-                        switch (node.nodeType) {
-                            case Node.TEXT_NODE:
-                                size = node.textContent.length;
-                                offset = pos;
-                                break;
-                            case Node.ELEMENT_NODE:
-                                size = getOuterHtml(node).length;
-                                break;
-                            default:
-                                $log.warn(logTag, 'Unhandled node:', node);
-                        }
-
-                        if (pos < size) {
-                            // use this node
-                            rangeAt(node, offset);
-                        } else if (i === composeDiv[0].childNodes.length - 1) {
-                            rangeAt(node);
-                        }
-                        pos -= size;
-                    }
-                }
-
                 // Handle typing events
                 // Handle typing events
-                composeDiv.on('keydown', onKeyDown);
-                composeDiv.on('keyup', onKeyUp);
-                composeDiv.on('keyup mouseup', updateCaretPosition);
-                composeDiv.on('selectionchange', updateCaretPosition);
+                composeDiv.on('compositionstart', onCompositionStart as any);
+                composeDiv.on('compositionend', onCompositionEnd as any);
+                composeDiv.on('keydown', onKeyDown as any);
+                composeDiv.on('keyup', onKeyUp as any);
+
+                // Handle selection change
+                document.addEventListener('selectionchange', () => {
+                    composeArea.store_selection_range();
+                });
 
 
                 // Handle paste event
                 // Handle paste event
-                composeDiv.on('paste', onPaste);
+                composeDiv.on('paste', onPaste as any);
 
 
                 // Handle click on emoji trigger
                 // Handle click on emoji trigger
-                emojiTrigger.on('click', onEmojiTrigger);
-                emojiTrigger.on('keypress', (ev: KeyboardEvent) => {
+                emojiTrigger.on('click', onEmojiTrigger as any);
+                emojiTrigger[0].addEventListener('keypress', (ev: KeyboardEvent) => {
                     if (isActionTrigger(ev)) {
                     if (isActionTrigger(ev)) {
-                        onEmojiTrigger(ev);
+                        $rootScope.$apply(() => {
+                            onEmojiTrigger(ev);
+                        });
                     }
                     }
                 });
                 });
 
 
                 // Handle click on file trigger
                 // Handle click on file trigger
-                fileTrigger.on('click', onFileTrigger);
-                fileTrigger.on('keypress', (ev: KeyboardEvent) => {
+                fileTrigger.on('click', onFileTrigger as any);
+                fileTrigger[0].addEventListener('keypress', (ev: any) => {
                     if (isActionTrigger(ev)) {
                     if (isActionTrigger(ev)) {
-                        onFileTrigger(ev);
+                        $rootScope.$apply(() => {
+                            onFileTrigger(ev);
+                        });
                     }
                     }
                 });
                 });
 
 
@@ -854,10 +669,12 @@ export default [
                 fileInput.on('change', onFileSelected);
                 fileInput.on('change', onFileSelected);
 
 
                 // Handle click on send trigger
                 // Handle click on send trigger
-                sendTrigger.on('click', onSendTrigger);
-                sendTrigger.on('keypress', (ev: KeyboardEvent) => {
+                sendTrigger.on('click', onSendTrigger as any);
+                sendTrigger[0].addEventListener('keypress', (ev: any) => {
                     if (isActionTrigger(ev)) {
                     if (isActionTrigger(ev)) {
-                        onSendTrigger(ev);
+                        $rootScope.$apply(() => {
+                            onSendTrigger(ev);
+                        });
                     }
                     }
                 });
                 });
 
 
@@ -866,15 +683,7 @@ export default [
                 // Listen to broadcasts
                 // Listen to broadcasts
                 const unsubscribeListeners = [];
                 const unsubscribeListeners = [];
                 unsubscribeListeners.push($rootScope.$on('onQuoted', (event: ng.IAngularEvent, args: any) => {
                 unsubscribeListeners.push($rootScope.$on('onQuoted', (event: ng.IAngularEvent, args: any) => {
-                    composeDiv[0].focus();
-                }));
-
-                unsubscribeListeners.push($rootScope.$on('onMentionSelected', (event: ng.IAngularEvent, args: any) => {
-                    if (args.query && args.mention) {
-                        // Insert resulting HTML
-                        insertMention(args.mention, caretPosition ? caretPosition.to - args.query.length : null,
-                            caretPosition ? caretPosition.to : null);
-                    }
+                    composeArea.focus();
                 }));
                 }));
 
 
                 // When switching chat, send stopTyping message
                 // When switching chat, send stopTyping message
@@ -895,6 +704,7 @@ export default [
                     <div>
                     <div>
                         <div
                         <div
                             class="compose"
                             class="compose"
+                            id="composeDiv"
                             contenteditable
                             contenteditable
                             autofocus
                             autofocus
                             translate
                             translate

+ 0 - 55
src/helpers.ts

@@ -396,61 +396,6 @@ export function copyDeep<T extends object>(obj: T): T {
     return JSON.parse(JSON.stringify(obj));
     return JSON.parse(JSON.stringify(obj));
 }
 }
 
 
-/**
- * Process a DOM node recursively and extract text from compose area.
- */
-export function extractText(targetNode: HTMLElement, logWarning: (msg: string) => void, trim = true) {
-    let text = '';
-    const visitChildNodes = (parentNode: HTMLElement) => {
-        // When pressing shift-enter and typing more text:
-        //
-        // - Firefox and chrome insert a <br> between two text nodes
-        // - Safari creates two <div>s without any line break in between
-        //   (except for the first line, which stays plain text)
-        //
-        // Thus, for Safari, we need to detect <div>s and insert a newline.
-
-        let lastNodeType;
-        // tslint:disable-next-line: prefer-for-of (see #98)
-        for (let i = 0; i < parentNode.childNodes.length; i++) {
-            const node = parentNode.childNodes[i] as HTMLElement;
-            switch (node.nodeType) {
-                case Node.TEXT_NODE:
-                    lastNodeType = 'text';
-                    // Append text, but strip leading and trailing newlines
-                    text += node.nodeValue.replace(/(^[\r\n]*|[\r\n]*$)/g, '');
-                    break;
-                case Node.ELEMENT_NODE:
-                    const tag = node.tagName.toLowerCase();
-                    const _lastNodeType = lastNodeType;
-                    lastNodeType = tag;
-                    if (tag === 'div') {
-                        text += '\n';
-                        visitChildNodes(node);
-                        break;
-                    } else if (tag === 'img') {
-                        if (_lastNodeType === 'div') {
-                            // An image following a div should go on a new line
-                            text += '\n';
-                        }
-                        text += (node as HTMLImageElement).alt;
-                        break;
-                    } else if (tag === 'br') {
-                        text += '\n';
-                        break;
-                    } else if (tag === 'span' && node.hasAttribute('text')) {
-                        text += node.getAttributeNode('text').value;
-                        break;
-                    }
-                default:
-                    logWarning(`Unhandled node: ${node}`);
-            }
-        }
-    };
-    visitChildNodes(targetNode);
-    return trim ? text.trim() : text;
-}
-
 /**
 /**
  * Replace spaces with `&nbsp;` and tabs with `&nbsp;&nbsp;`.
  * Replace spaces with `&nbsp;` and tabs with `&nbsp;&nbsp;`.
  */
  */

File diff ditekan karena terlalu besar
+ 3522 - 3519
src/helpers/emoji.ts


+ 198 - 198
src/partials/emoji-picker.html

@@ -13,7 +13,7 @@
             <span class="em em-people-1f605" data-c="1f605" data-s=":sweat_smile:" title="grinning face with sweat">&#x1f605;</span>
             <span class="em em-people-1f605" data-c="1f605" data-s=":sweat_smile:" title="grinning face with sweat">&#x1f605;</span>
             <span class="em em-people-1f602" data-c="1f602" data-s=":joy:" title="face with tears of joy">&#x1f602;</span>
             <span class="em em-people-1f602" data-c="1f602" data-s=":joy:" title="face with tears of joy">&#x1f602;</span>
             <span class="em em-people-1f923" data-c="1f923" data-s=":rofl:" title="rolling on the floor laughing">&#x1f923;</span>
             <span class="em em-people-1f923" data-c="1f923" data-s=":rofl:" title="rolling on the floor laughing">&#x1f923;</span>
-            <span class="em em-people-263a" data-c="263a" data-s=":relaxed:" title="smiling face">&#x263a;</span>
+            <span class="em em-people-263a" data-c="263a" data-s=":relaxed:" title="smiling face">&#x263a;&#xfe0f;</span>
             <span class="em em-people-1f60a" data-c="1f60a" data-s=":blush:" title="smiling face with smiling eyes">&#x1f60a;</span>
             <span class="em em-people-1f60a" data-c="1f60a" data-s=":blush:" title="smiling face with smiling eyes">&#x1f60a;</span>
             <span class="em em-people-1f607" data-c="1f607" data-s=":innocent:" title="smiling face with halo">&#x1f607;</span>
             <span class="em em-people-1f607" data-c="1f607" data-s=":innocent:" title="smiling face with halo">&#x1f607;</span>
             <span class="em em-people-1f642" data-c="1f642" data-s=":slight_smile:" title="slightly smiling face">&#x1f642;</span>
             <span class="em em-people-1f642" data-c="1f642" data-s=":slight_smile:" title="slightly smiling face">&#x1f642;</span>
@@ -44,7 +44,7 @@
             <span class="em em-people-1f61f" data-c="1f61f" data-s=":worried:" title="worried face">&#x1f61f;</span>
             <span class="em em-people-1f61f" data-c="1f61f" data-s=":worried:" title="worried face">&#x1f61f;</span>
             <span class="em em-people-1f615" data-c="1f615" data-s=":confused:" title="confused face">&#x1f615;</span>
             <span class="em em-people-1f615" data-c="1f615" data-s=":confused:" title="confused face">&#x1f615;</span>
             <span class="em em-people-1f641" data-c="1f641" data-s=":slight_frown:" title="slightly frowning face">&#x1f641;</span>
             <span class="em em-people-1f641" data-c="1f641" data-s=":slight_frown:" title="slightly frowning face">&#x1f641;</span>
-            <span class="em em-people-2639" data-c="2639" data-s=":frowning2:" title="frowning face">&#x2639;</span>
+            <span class="em em-people-2639" data-c="2639" data-s=":frowning2:" title="frowning face">&#x2639;&#xfe0f;</span>
             <span class="em em-people-1f623" data-c="1f623" data-s=":persevere:" title="persevering face">&#x1f623;</span>
             <span class="em em-people-1f623" data-c="1f623" data-s=":persevere:" title="persevering face">&#x1f623;</span>
             <span class="em em-people-1f616" data-c="1f616" data-s=":confounded:" title="confounded face">&#x1f616;</span>
             <span class="em em-people-1f616" data-c="1f616" data-s=":confounded:" title="confounded face">&#x1f616;</span>
             <span class="em em-people-1f62b" data-c="1f62b" data-s=":tired_face:" title="tired face">&#x1f62b;</span>
             <span class="em em-people-1f62b" data-c="1f62b" data-s=":tired_face:" title="tired face">&#x1f62b;</span>
@@ -102,7 +102,7 @@
             <span class="em em-people-1f4a9" data-c="1f4a9" data-s=":poop:" title="pile of poo">&#x1f4a9;</span>
             <span class="em em-people-1f4a9" data-c="1f4a9" data-s=":poop:" title="pile of poo">&#x1f4a9;</span>
             <span class="em em-people-1f47b" data-c="1f47b" data-s=":ghost:" title="ghost">&#x1f47b;</span>
             <span class="em em-people-1f47b" data-c="1f47b" data-s=":ghost:" title="ghost">&#x1f47b;</span>
             <span class="em em-people-1f480" data-c="1f480" data-s=":skull:" title="skull">&#x1f480;</span>
             <span class="em em-people-1f480" data-c="1f480" data-s=":skull:" title="skull">&#x1f480;</span>
-            <span class="em em-people-2620" data-c="2620" data-s=":skull_crossbones:" title="skull and crossbones">&#x2620;</span>
+            <span class="em em-people-2620" data-c="2620" data-s=":skull_crossbones:" title="skull and crossbones">&#x2620;&#xfe0f;</span>
             <span class="em em-people-1f47d" data-c="1f47d" data-s=":alien:" title="alien">&#x1f47d;</span>
             <span class="em em-people-1f47d" data-c="1f47d" data-s=":alien:" title="alien">&#x1f47d;</span>
             <span class="em em-people-1f47e" data-c="1f47e" data-s=":space_invader:" title="alien monster">&#x1f47e;</span>
             <span class="em em-people-1f47e" data-c="1f47e" data-s=":space_invader:" title="alien monster">&#x1f47e;</span>
             <span class="em em-people-1f916" data-c="1f916" data-s=":robot:" title="robot face">&#x1f916;</span>
             <span class="em em-people-1f916" data-c="1f916" data-s=":robot:" title="robot face">&#x1f916;</span>
@@ -183,7 +183,7 @@
             <span class="em em-people-1f91e-1f3fd" data-c="1f91e-1f3fd" data-s=":fingers_crossed_tone3:" title="crossed fingers: medium skin tone">&#x1f91e;&#x1f3fd;</span>
             <span class="em em-people-1f91e-1f3fd" data-c="1f91e-1f3fd" data-s=":fingers_crossed_tone3:" title="crossed fingers: medium skin tone">&#x1f91e;&#x1f3fd;</span>
             <span class="em em-people-1f91e-1f3fe" data-c="1f91e-1f3fe" data-s=":fingers_crossed_tone4:" title="crossed fingers: medium-dark skin tone">&#x1f91e;&#x1f3fe;</span>
             <span class="em em-people-1f91e-1f3fe" data-c="1f91e-1f3fe" data-s=":fingers_crossed_tone4:" title="crossed fingers: medium-dark skin tone">&#x1f91e;&#x1f3fe;</span>
             <span class="em em-people-1f91e-1f3ff" data-c="1f91e-1f3ff" data-s=":fingers_crossed_tone5:" title="crossed fingers: dark skin tone">&#x1f91e;&#x1f3ff;</span>
             <span class="em em-people-1f91e-1f3ff" data-c="1f91e-1f3ff" data-s=":fingers_crossed_tone5:" title="crossed fingers: dark skin tone">&#x1f91e;&#x1f3ff;</span>
-            <span class="em em-people-270c" data-c="270c" data-s=":v:" title="victory hand">&#x270c;</span>
+            <span class="em em-people-270c" data-c="270c" data-s=":v:" title="victory hand">&#x270c;&#xfe0f;</span>
             <span class="em em-people-270c-1f3fb" data-c="270c-1f3fb" data-s=":v_tone1:" title="victory hand: light skin tone">&#x270c;&#x1f3fb;</span>
             <span class="em em-people-270c-1f3fb" data-c="270c-1f3fb" data-s=":v_tone1:" title="victory hand: light skin tone">&#x270c;&#x1f3fb;</span>
             <span class="em em-people-270c-1f3fc" data-c="270c-1f3fc" data-s=":v_tone2:" title="victory hand: medium-light skin tone">&#x270c;&#x1f3fc;</span>
             <span class="em em-people-270c-1f3fc" data-c="270c-1f3fc" data-s=":v_tone2:" title="victory hand: medium-light skin tone">&#x270c;&#x1f3fc;</span>
             <span class="em em-people-270c-1f3fd" data-c="270c-1f3fd" data-s=":v_tone3:" title="victory hand: medium skin tone">&#x270c;&#x1f3fd;</span>
             <span class="em em-people-270c-1f3fd" data-c="270c-1f3fd" data-s=":v_tone3:" title="victory hand: medium skin tone">&#x270c;&#x1f3fd;</span>
@@ -231,7 +231,7 @@
             <span class="em em-people-1f447-1f3fd" data-c="1f447-1f3fd" data-s=":point_down_tone3:" title="backhand index pointing down: medium skin tone">&#x1f447;&#x1f3fd;</span>
             <span class="em em-people-1f447-1f3fd" data-c="1f447-1f3fd" data-s=":point_down_tone3:" title="backhand index pointing down: medium skin tone">&#x1f447;&#x1f3fd;</span>
             <span class="em em-people-1f447-1f3fe" data-c="1f447-1f3fe" data-s=":point_down_tone4:" title="backhand index pointing down: medium-dark skin tone">&#x1f447;&#x1f3fe;</span>
             <span class="em em-people-1f447-1f3fe" data-c="1f447-1f3fe" data-s=":point_down_tone4:" title="backhand index pointing down: medium-dark skin tone">&#x1f447;&#x1f3fe;</span>
             <span class="em em-people-1f447-1f3ff" data-c="1f447-1f3ff" data-s=":point_down_tone5:" title="backhand index pointing down: dark skin tone">&#x1f447;&#x1f3ff;</span>
             <span class="em em-people-1f447-1f3ff" data-c="1f447-1f3ff" data-s=":point_down_tone5:" title="backhand index pointing down: dark skin tone">&#x1f447;&#x1f3ff;</span>
-            <span class="em em-people-261d" data-c="261d" data-s=":point_up:" title="index pointing up">&#x261d;</span>
+            <span class="em em-people-261d" data-c="261d" data-s=":point_up:" title="index pointing up">&#x261d;&#xfe0f;</span>
             <span class="em em-people-261d-1f3fb" data-c="261d-1f3fb" data-s=":point_up_tone1:" title="index pointing up: light skin tone">&#x261d;&#x1f3fb;</span>
             <span class="em em-people-261d-1f3fb" data-c="261d-1f3fb" data-s=":point_up_tone1:" title="index pointing up: light skin tone">&#x261d;&#x1f3fb;</span>
             <span class="em em-people-261d-1f3fc" data-c="261d-1f3fc" data-s=":point_up_tone2:" title="index pointing up: medium-light skin tone">&#x261d;&#x1f3fc;</span>
             <span class="em em-people-261d-1f3fc" data-c="261d-1f3fc" data-s=":point_up_tone2:" title="index pointing up: medium-light skin tone">&#x261d;&#x1f3fc;</span>
             <span class="em em-people-261d-1f3fd" data-c="261d-1f3fd" data-s=":point_up_tone3:" title="index pointing up: medium skin tone">&#x261d;&#x1f3fd;</span>
             <span class="em em-people-261d-1f3fd" data-c="261d-1f3fd" data-s=":point_up_tone3:" title="index pointing up: medium skin tone">&#x261d;&#x1f3fd;</span>
@@ -249,7 +249,7 @@
             <span class="em em-people-1f91a-1f3fd" data-c="1f91a-1f3fd" data-s=":raised_back_of_hand_tone3:" title="raised back of hand: medium skin tone">&#x1f91a;&#x1f3fd;</span>
             <span class="em em-people-1f91a-1f3fd" data-c="1f91a-1f3fd" data-s=":raised_back_of_hand_tone3:" title="raised back of hand: medium skin tone">&#x1f91a;&#x1f3fd;</span>
             <span class="em em-people-1f91a-1f3fe" data-c="1f91a-1f3fe" data-s=":raised_back_of_hand_tone4:" title="raised back of hand: medium-dark skin tone">&#x1f91a;&#x1f3fe;</span>
             <span class="em em-people-1f91a-1f3fe" data-c="1f91a-1f3fe" data-s=":raised_back_of_hand_tone4:" title="raised back of hand: medium-dark skin tone">&#x1f91a;&#x1f3fe;</span>
             <span class="em em-people-1f91a-1f3ff" data-c="1f91a-1f3ff" data-s=":raised_back_of_hand_tone5:" title="raised back of hand: dark skin tone">&#x1f91a;&#x1f3ff;</span>
             <span class="em em-people-1f91a-1f3ff" data-c="1f91a-1f3ff" data-s=":raised_back_of_hand_tone5:" title="raised back of hand: dark skin tone">&#x1f91a;&#x1f3ff;</span>
-            <span class="em em-people-1f590" data-c="1f590" data-s=":hand_splayed:" title="hand with fingers splayed">&#x1f590;</span>
+            <span class="em em-people-1f590" data-c="1f590" data-s=":hand_splayed:" title="hand with fingers splayed">&#x1f590;&#xfe0f;</span>
             <span class="em em-people-1f590-1f3fb" data-c="1f590-1f3fb" data-s=":hand_splayed_tone1:" title="hand with fingers splayed: light skin tone">&#x1f590;&#x1f3fb;</span>
             <span class="em em-people-1f590-1f3fb" data-c="1f590-1f3fb" data-s=":hand_splayed_tone1:" title="hand with fingers splayed: light skin tone">&#x1f590;&#x1f3fb;</span>
             <span class="em em-people-1f590-1f3fc" data-c="1f590-1f3fc" data-s=":hand_splayed_tone2:" title="hand with fingers splayed: medium-light skin tone">&#x1f590;&#x1f3fc;</span>
             <span class="em em-people-1f590-1f3fc" data-c="1f590-1f3fc" data-s=":hand_splayed_tone2:" title="hand with fingers splayed: medium-light skin tone">&#x1f590;&#x1f3fc;</span>
             <span class="em em-people-1f590-1f3fd" data-c="1f590-1f3fd" data-s=":hand_splayed_tone3:" title="hand with fingers splayed: medium skin tone">&#x1f590;&#x1f3fd;</span>
             <span class="em em-people-1f590-1f3fd" data-c="1f590-1f3fd" data-s=":hand_splayed_tone3:" title="hand with fingers splayed: medium skin tone">&#x1f590;&#x1f3fd;</span>
@@ -297,7 +297,7 @@
             <span class="em em-people-1f595-1f3fd" data-c="1f595-1f3fd" data-s=":middle_finger_tone3:" title="middle finger: medium skin tone">&#x1f595;&#x1f3fd;</span>
             <span class="em em-people-1f595-1f3fd" data-c="1f595-1f3fd" data-s=":middle_finger_tone3:" title="middle finger: medium skin tone">&#x1f595;&#x1f3fd;</span>
             <span class="em em-people-1f595-1f3fe" data-c="1f595-1f3fe" data-s=":middle_finger_tone4:" title="middle finger: medium-dark skin tone">&#x1f595;&#x1f3fe;</span>
             <span class="em em-people-1f595-1f3fe" data-c="1f595-1f3fe" data-s=":middle_finger_tone4:" title="middle finger: medium-dark skin tone">&#x1f595;&#x1f3fe;</span>
             <span class="em em-people-1f595-1f3ff" data-c="1f595-1f3ff" data-s=":middle_finger_tone5:" title="middle finger: dark skin tone">&#x1f595;&#x1f3ff;</span>
             <span class="em em-people-1f595-1f3ff" data-c="1f595-1f3ff" data-s=":middle_finger_tone5:" title="middle finger: dark skin tone">&#x1f595;&#x1f3ff;</span>
-            <span class="em em-people-270d" data-c="270d" data-s=":writing_hand:" title="writing hand">&#x270d;</span>
+            <span class="em em-people-270d" data-c="270d" data-s=":writing_hand:" title="writing hand">&#x270d;&#xfe0f;</span>
             <span class="em em-people-270d-1f3fb" data-c="270d-1f3fb" data-s=":writing_hand_tone1:" title="writing hand: light skin tone">&#x270d;&#x1f3fb;</span>
             <span class="em em-people-270d-1f3fb" data-c="270d-1f3fb" data-s=":writing_hand_tone1:" title="writing hand: light skin tone">&#x270d;&#x1f3fb;</span>
             <span class="em em-people-270d-1f3fc" data-c="270d-1f3fc" data-s=":writing_hand_tone2:" title="writing hand: medium-light skin tone">&#x270d;&#x1f3fc;</span>
             <span class="em em-people-270d-1f3fc" data-c="270d-1f3fc" data-s=":writing_hand_tone2:" title="writing hand: medium-light skin tone">&#x270d;&#x1f3fc;</span>
             <span class="em em-people-270d-1f3fd" data-c="270d-1f3fd" data-s=":writing_hand_tone3:" title="writing hand: medium skin tone">&#x270d;&#x1f3fd;</span>
             <span class="em em-people-270d-1f3fd" data-c="270d-1f3fd" data-s=":writing_hand_tone3:" title="writing hand: medium skin tone">&#x270d;&#x1f3fd;</span>
@@ -327,12 +327,12 @@
             <span class="em em-people-1f443-1f3fe" data-c="1f443-1f3fe" data-s=":nose_tone4:" title="nose: medium-dark skin tone">&#x1f443;&#x1f3fe;</span>
             <span class="em em-people-1f443-1f3fe" data-c="1f443-1f3fe" data-s=":nose_tone4:" title="nose: medium-dark skin tone">&#x1f443;&#x1f3fe;</span>
             <span class="em em-people-1f443-1f3ff" data-c="1f443-1f3ff" data-s=":nose_tone5:" title="nose: dark skin tone">&#x1f443;&#x1f3ff;</span>
             <span class="em em-people-1f443-1f3ff" data-c="1f443-1f3ff" data-s=":nose_tone5:" title="nose: dark skin tone">&#x1f443;&#x1f3ff;</span>
             <span class="em em-people-1f463" data-c="1f463" data-s=":footprints:" title="footprints">&#x1f463;</span>
             <span class="em em-people-1f463" data-c="1f463" data-s=":footprints:" title="footprints">&#x1f463;</span>
-            <span class="em em-people-1f441" data-c="1f441" data-s=":eye:" title="eye">&#x1f441;</span>
+            <span class="em em-people-1f441" data-c="1f441" data-s=":eye:" title="eye">&#x1f441;&#xfe0f;</span>
             <span class="em em-people-1f440" data-c="1f440" data-s=":eyes:" title="eyes">&#x1f440;</span>
             <span class="em em-people-1f440" data-c="1f440" data-s=":eyes:" title="eyes">&#x1f440;</span>
             <span class="em em-people-1f9e0" data-c="1f9e0" data-s=":brain:" title="brain">&#x1f9e0;</span>
             <span class="em em-people-1f9e0" data-c="1f9e0" data-s=":brain:" title="brain">&#x1f9e0;</span>
             <span class="em em-people-1f9b4" data-c="1f9b4" data-s=":bone:" title="bone">&#x1f9b4;</span>
             <span class="em em-people-1f9b4" data-c="1f9b4" data-s=":bone:" title="bone">&#x1f9b4;</span>
             <span class="em em-people-1f9b7" data-c="1f9b7" data-s=":tooth:" title="tooth">&#x1f9b7;</span>
             <span class="em em-people-1f9b7" data-c="1f9b7" data-s=":tooth:" title="tooth">&#x1f9b7;</span>
-            <span class="em em-people-1f5e3" data-c="1f5e3" data-s=":speaking_head:" title="speaking head">&#x1f5e3;</span>
+            <span class="em em-people-1f5e3" data-c="1f5e3" data-s=":speaking_head:" title="speaking head">&#x1f5e3;&#xfe0f;</span>
             <span class="em em-people-1f464" data-c="1f464" data-s=":bust_in_silhouette:" title="bust in silhouette">&#x1f464;</span>
             <span class="em em-people-1f464" data-c="1f464" data-s=":bust_in_silhouette:" title="bust in silhouette">&#x1f464;</span>
             <span class="em em-people-1f465" data-c="1f465" data-s=":busts_in_silhouette:" title="busts in silhouette">&#x1f465;</span>
             <span class="em em-people-1f465" data-c="1f465" data-s=":busts_in_silhouette:" title="busts in silhouette">&#x1f465;</span>
             <span class="em em-people-1f476" data-c="1f476" data-s=":baby:" title="baby">&#x1f476;</span>
             <span class="em em-people-1f476" data-c="1f476" data-s=":baby:" title="baby">&#x1f476;</span>
@@ -521,7 +521,7 @@
             <span class="em em-people-1f482-1f3fd-200d-2640-fe0f" data-c="1f482-1f3fd-200d-2640-fe0f" data-s=":woman_guard_tone3:" title="woman guard: medium skin tone">&#x1f482;&#x1f3fd;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-people-1f482-1f3fd-200d-2640-fe0f" data-c="1f482-1f3fd-200d-2640-fe0f" data-s=":woman_guard_tone3:" title="woman guard: medium skin tone">&#x1f482;&#x1f3fd;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-people-1f482-1f3fe-200d-2640-fe0f" data-c="1f482-1f3fe-200d-2640-fe0f" data-s=":woman_guard_tone4:" title="woman guard: medium-dark skin tone">&#x1f482;&#x1f3fe;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-people-1f482-1f3fe-200d-2640-fe0f" data-c="1f482-1f3fe-200d-2640-fe0f" data-s=":woman_guard_tone4:" title="woman guard: medium-dark skin tone">&#x1f482;&#x1f3fe;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-people-1f482-1f3ff-200d-2640-fe0f" data-c="1f482-1f3ff-200d-2640-fe0f" data-s=":woman_guard_tone5:" title="woman guard: dark skin tone">&#x1f482;&#x1f3ff;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-people-1f482-1f3ff-200d-2640-fe0f" data-c="1f482-1f3ff-200d-2640-fe0f" data-s=":woman_guard_tone5:" title="woman guard: dark skin tone">&#x1f482;&#x1f3ff;&#x200d;&#x2640;&#xfe0f;</span>
-            <span class="em em-people-1f575" data-c="1f575" data-s=":detective:" title="detective">&#x1f575;</span>
+            <span class="em em-people-1f575" data-c="1f575" data-s=":detective:" title="detective">&#x1f575;&#xfe0f;</span>
             <span class="em em-people-1f575-1f3fb" data-c="1f575-1f3fb" data-s=":detective_tone1:" title="detective: light skin tone">&#x1f575;&#x1f3fb;</span>
             <span class="em em-people-1f575-1f3fb" data-c="1f575-1f3fb" data-s=":detective_tone1:" title="detective: light skin tone">&#x1f575;&#x1f3fb;</span>
             <span class="em em-people-1f575-1f3fc" data-c="1f575-1f3fc" data-s=":detective_tone2:" title="detective: medium-light skin tone">&#x1f575;&#x1f3fc;</span>
             <span class="em em-people-1f575-1f3fc" data-c="1f575-1f3fc" data-s=":detective_tone2:" title="detective: medium-light skin tone">&#x1f575;&#x1f3fc;</span>
             <span class="em em-people-1f575-1f3fd" data-c="1f575-1f3fd" data-s=":detective_tone3:" title="detective: medium skin tone">&#x1f575;&#x1f3fd;</span>
             <span class="em em-people-1f575-1f3fd" data-c="1f575-1f3fd" data-s=":detective_tone3:" title="detective: medium skin tone">&#x1f575;&#x1f3fd;</span>
@@ -1037,7 +1037,7 @@
             <span class="em em-people-1f57a-1f3fe" data-c="1f57a-1f3fe" data-s=":man_dancing_tone4:" title="man dancing: medium-dark skin tone">&#x1f57a;&#x1f3fe;</span>
             <span class="em em-people-1f57a-1f3fe" data-c="1f57a-1f3fe" data-s=":man_dancing_tone4:" title="man dancing: medium-dark skin tone">&#x1f57a;&#x1f3fe;</span>
             <span class="em em-people-1f46f" data-c="1f46f" data-s=":people_with_bunny_ears_partying:" title="people with bunny ears">&#x1f46f;</span>
             <span class="em em-people-1f46f" data-c="1f46f" data-s=":people_with_bunny_ears_partying:" title="people with bunny ears">&#x1f46f;</span>
             <span class="em em-people-1f46f-200d-2642-fe0f" data-c="1f46f-200d-2642-fe0f" data-s=":men_with_bunny_ears_partying:" title="men with bunny ears">&#x1f46f;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-people-1f46f-200d-2642-fe0f" data-c="1f46f-200d-2642-fe0f" data-s=":men_with_bunny_ears_partying:" title="men with bunny ears">&#x1f46f;&#x200d;&#x2642;&#xfe0f;</span>
-            <span class="em em-people-1f574" data-c="1f574" data-s=":levitate:" title="man in suit levitating">&#x1f574;</span>
+            <span class="em em-people-1f574" data-c="1f574" data-s=":levitate:" title="man in suit levitating">&#x1f574;&#xfe0f;</span>
             <span class="em em-people-1f574-1f3fb" data-c="1f574-1f3fb" data-s=":levitate_tone1:" title="man in suit levitating: light skin tone">&#x1f574;&#x1f3fb;</span>
             <span class="em em-people-1f574-1f3fb" data-c="1f574-1f3fb" data-s=":levitate_tone1:" title="man in suit levitating: light skin tone">&#x1f574;&#x1f3fb;</span>
             <span class="em em-people-1f574-1f3fc" data-c="1f574-1f3fc" data-s=":levitate_tone2:" title="man in suit levitating: medium-light skin tone">&#x1f574;&#x1f3fc;</span>
             <span class="em em-people-1f574-1f3fc" data-c="1f574-1f3fc" data-s=":levitate_tone2:" title="man in suit levitating: medium-light skin tone">&#x1f574;&#x1f3fc;</span>
             <span class="em em-people-1f574-1f3fd" data-c="1f574-1f3fd" data-s=":levitate_tone3:" title="man in suit levitating: medium skin tone">&#x1f574;&#x1f3fd;</span>
             <span class="em em-people-1f574-1f3fd" data-c="1f574-1f3fd" data-s=":levitate_tone3:" title="man in suit levitating: medium skin tone">&#x1f574;&#x1f3fd;</span>
@@ -1124,7 +1124,7 @@
             <span class="em em-people-1f9e2" data-c="1f9e2" data-s=":billed_cap:" title="billed cap">&#x1f9e2;</span>
             <span class="em em-people-1f9e2" data-c="1f9e2" data-s=":billed_cap:" title="billed cap">&#x1f9e2;</span>
             <span class="em em-people-1f452" data-c="1f452" data-s=":womans_hat:" title="woman’s hat">&#x1f452;</span>
             <span class="em em-people-1f452" data-c="1f452" data-s=":womans_hat:" title="woman’s hat">&#x1f452;</span>
             <span class="em em-people-1f393" data-c="1f393" data-s=":mortar_board:" title="graduation cap">&#x1f393;</span>
             <span class="em em-people-1f393" data-c="1f393" data-s=":mortar_board:" title="graduation cap">&#x1f393;</span>
-            <span class="em em-people-26d1" data-c="26d1" data-s=":helmet_with_cross:" title="rescue worker’s helmet">&#x26d1;</span>
+            <span class="em em-people-26d1" data-c="26d1" data-s=":helmet_with_cross:" title="rescue worker’s helmet">&#x26d1;&#xfe0f;</span>
             <span class="em em-people-1f451" data-c="1f451" data-s=":crown:" title="crown">&#x1f451;</span>
             <span class="em em-people-1f451" data-c="1f451" data-s=":crown:" title="crown">&#x1f451;</span>
             <span class="em em-people-1f45d" data-c="1f45d" data-s=":pouch:" title="clutch bag">&#x1f45d;</span>
             <span class="em em-people-1f45d" data-c="1f45d" data-s=":pouch:" title="clutch bag">&#x1f45d;</span>
             <span class="em em-people-1f45b" data-c="1f45b" data-s=":purse:" title="purse">&#x1f45b;</span>
             <span class="em em-people-1f45b" data-c="1f45b" data-s=":purse:" title="purse">&#x1f45b;</span>
@@ -1132,7 +1132,7 @@
             <span class="em em-people-1f4bc" data-c="1f4bc" data-s=":briefcase:" title="briefcase">&#x1f4bc;</span>
             <span class="em em-people-1f4bc" data-c="1f4bc" data-s=":briefcase:" title="briefcase">&#x1f4bc;</span>
             <span class="em em-people-1f392" data-c="1f392" data-s=":school_satchel:" title="school backpack">&#x1f392;</span>
             <span class="em em-people-1f392" data-c="1f392" data-s=":school_satchel:" title="school backpack">&#x1f392;</span>
             <span class="em em-people-1f453" data-c="1f453" data-s=":eyeglasses:" title="glasses">&#x1f453;</span>
             <span class="em em-people-1f453" data-c="1f453" data-s=":eyeglasses:" title="glasses">&#x1f453;</span>
-            <span class="em em-people-1f576" data-c="1f576" data-s=":dark_sunglasses:" title="sunglasses">&#x1f576;</span>
+            <span class="em em-people-1f576" data-c="1f576" data-s=":dark_sunglasses:" title="sunglasses">&#x1f576;&#xfe0f;</span>
             <span class="em em-people-1f97d" data-c="1f97d" data-s=":goggles:" title="goggles">&#x1f97d;</span>
             <span class="em em-people-1f97d" data-c="1f97d" data-s=":goggles:" title="goggles">&#x1f97d;</span>
             <span class="em em-people-1f302" data-c="1f302" data-s=":closed_umbrella:" title="closed umbrella">&#x1f302;</span>
             <span class="em em-people-1f302" data-c="1f302" data-s=":closed_umbrella:" title="closed umbrella">&#x1f302;</span>
         </div>
         </div>
@@ -1191,8 +1191,8 @@
             <span class="em em-nature-1f41e" data-c="1f41e" data-s=":beetle:" title="lady beetle">&#x1f41e;</span>
             <span class="em em-nature-1f41e" data-c="1f41e" data-s=":beetle:" title="lady beetle">&#x1f41e;</span>
             <span class="em em-nature-1f41c" data-c="1f41c" data-s=":ant:" title="ant">&#x1f41c;</span>
             <span class="em em-nature-1f41c" data-c="1f41c" data-s=":ant:" title="ant">&#x1f41c;</span>
             <span class="em em-nature-1f997" data-c="1f997" data-s=":cricket:" title="cricket">&#x1f997;</span>
             <span class="em em-nature-1f997" data-c="1f997" data-s=":cricket:" title="cricket">&#x1f997;</span>
-            <span class="em em-nature-1f577" data-c="1f577" data-s=":spider:" title="spider">&#x1f577;</span>
-            <span class="em em-nature-1f578" data-c="1f578" data-s=":spider_web:" title="spider web">&#x1f578;</span>
+            <span class="em em-nature-1f577" data-c="1f577" data-s=":spider:" title="spider">&#x1f577;&#xfe0f;</span>
+            <span class="em em-nature-1f578" data-c="1f578" data-s=":spider_web:" title="spider web">&#x1f578;&#xfe0f;</span>
             <span class="em em-nature-1f982" data-c="1f982" data-s=":scorpion:" title="scorpion">&#x1f982;</span>
             <span class="em em-nature-1f982" data-c="1f982" data-s=":scorpion:" title="scorpion">&#x1f982;</span>
             <span class="em em-nature-1f99f" data-c="1f99f" data-s=":mosquito:" title="mosquito">&#x1f99f;</span>
             <span class="em em-nature-1f99f" data-c="1f99f" data-s=":mosquito:" title="mosquito">&#x1f99f;</span>
             <span class="em em-nature-1f9a0" data-c="1f9a0" data-s=":microbe:" title="microbe">&#x1f9a0;</span>
             <span class="em em-nature-1f9a0" data-c="1f9a0" data-s=":microbe:" title="microbe">&#x1f9a0;</span>
@@ -1239,11 +1239,11 @@
             <span class="em em-nature-1f408" data-c="1f408" data-s=":cat2:" title="cat">&#x1f408;</span>
             <span class="em em-nature-1f408" data-c="1f408" data-s=":cat2:" title="cat">&#x1f408;</span>
             <span class="em em-nature-1f413" data-c="1f413" data-s=":rooster:" title="rooster">&#x1f413;</span>
             <span class="em em-nature-1f413" data-c="1f413" data-s=":rooster:" title="rooster">&#x1f413;</span>
             <span class="em em-nature-1f983" data-c="1f983" data-s=":turkey:" title="turkey">&#x1f983;</span>
             <span class="em em-nature-1f983" data-c="1f983" data-s=":turkey:" title="turkey">&#x1f983;</span>
-            <span class="em em-nature-1f54a" data-c="1f54a" data-s=":dove:" title="dove">&#x1f54a;</span>
+            <span class="em em-nature-1f54a" data-c="1f54a" data-s=":dove:" title="dove">&#x1f54a;&#xfe0f;</span>
             <span class="em em-nature-1f407" data-c="1f407" data-s=":rabbit2:" title="rabbit">&#x1f407;</span>
             <span class="em em-nature-1f407" data-c="1f407" data-s=":rabbit2:" title="rabbit">&#x1f407;</span>
             <span class="em em-nature-1f401" data-c="1f401" data-s=":mouse2:" title="mouse">&#x1f401;</span>
             <span class="em em-nature-1f401" data-c="1f401" data-s=":mouse2:" title="mouse">&#x1f401;</span>
             <span class="em em-nature-1f400" data-c="1f400" data-s=":rat:" title="rat">&#x1f400;</span>
             <span class="em em-nature-1f400" data-c="1f400" data-s=":rat:" title="rat">&#x1f400;</span>
-            <span class="em em-nature-1f43f" data-c="1f43f" data-s=":chipmunk:" title="chipmunk">&#x1f43f;</span>
+            <span class="em em-nature-1f43f" data-c="1f43f" data-s=":chipmunk:" title="chipmunk">&#x1f43f;&#xfe0f;</span>
             <span class="em em-nature-1f994" data-c="1f994" data-s=":hedgehog:" title="hedgehog">&#x1f994;</span>
             <span class="em em-nature-1f994" data-c="1f994" data-s=":hedgehog:" title="hedgehog">&#x1f994;</span>
             <span class="em em-nature-1f43e" data-c="1f43e" data-s=":feet:" title="paw prints">&#x1f43e;</span>
             <span class="em em-nature-1f43e" data-c="1f43e" data-s=":feet:" title="paw prints">&#x1f43e;</span>
             <span class="em em-nature-1f409" data-c="1f409" data-s=":dragon:" title="dragon">&#x1f409;</span>
             <span class="em em-nature-1f409" data-c="1f409" data-s=":dragon:" title="dragon">&#x1f409;</span>
@@ -1255,7 +1255,7 @@
             <span class="em em-nature-1f334" data-c="1f334" data-s=":palm_tree:" title="palm tree">&#x1f334;</span>
             <span class="em em-nature-1f334" data-c="1f334" data-s=":palm_tree:" title="palm tree">&#x1f334;</span>
             <span class="em em-nature-1f331" data-c="1f331" data-s=":seedling:" title="seedling">&#x1f331;</span>
             <span class="em em-nature-1f331" data-c="1f331" data-s=":seedling:" title="seedling">&#x1f331;</span>
             <span class="em em-nature-1f33f" data-c="1f33f" data-s=":herb:" title="herb">&#x1f33f;</span>
             <span class="em em-nature-1f33f" data-c="1f33f" data-s=":herb:" title="herb">&#x1f33f;</span>
-            <span class="em em-nature-2618" data-c="2618" data-s=":shamrock:" title="shamrock">&#x2618;</span>
+            <span class="em em-nature-2618" data-c="2618" data-s=":shamrock:" title="shamrock">&#x2618;&#xfe0f;</span>
             <span class="em em-nature-1f340" data-c="1f340" data-s=":four_leaf_clover:" title="four leaf clover">&#x1f340;</span>
             <span class="em em-nature-1f340" data-c="1f340" data-s=":four_leaf_clover:" title="four leaf clover">&#x1f340;</span>
             <span class="em em-nature-1f38d" data-c="1f38d" data-s=":bamboo:" title="pine decoration">&#x1f38d;</span>
             <span class="em em-nature-1f38d" data-c="1f38d" data-s=":bamboo:" title="pine decoration">&#x1f38d;</span>
             <span class="em em-nature-1f38b" data-c="1f38b" data-s=":tanabata_tree:" title="tanabata tree">&#x1f38b;</span>
             <span class="em em-nature-1f38b" data-c="1f38b" data-s=":tanabata_tree:" title="tanabata tree">&#x1f38b;</span>
@@ -1294,32 +1294,32 @@
             <span class="em em-nature-1f31f" data-c="1f31f" data-s=":star2:" title="glowing star">&#x1f31f;</span>
             <span class="em em-nature-1f31f" data-c="1f31f" data-s=":star2:" title="glowing star">&#x1f31f;</span>
             <span class="em em-nature-2728" data-c="2728" data-s=":sparkles:" title="sparkles">&#x2728;</span>
             <span class="em em-nature-2728" data-c="2728" data-s=":sparkles:" title="sparkles">&#x2728;</span>
             <span class="em em-nature-26a1" data-c="26a1" data-s=":zap:" title="high voltage">&#x26a1;</span>
             <span class="em em-nature-26a1" data-c="26a1" data-s=":zap:" title="high voltage">&#x26a1;</span>
-            <span class="em em-nature-2604" data-c="2604" data-s=":comet:" title="comet">&#x2604;</span>
+            <span class="em em-nature-2604" data-c="2604" data-s=":comet:" title="comet">&#x2604;&#xfe0f;</span>
             <span class="em em-nature-1f4a5" data-c="1f4a5" data-s=":boom:" title="collision">&#x1f4a5;</span>
             <span class="em em-nature-1f4a5" data-c="1f4a5" data-s=":boom:" title="collision">&#x1f4a5;</span>
             <span class="em em-nature-1f525" data-c="1f525" data-s=":fire:" title="fire">&#x1f525;</span>
             <span class="em em-nature-1f525" data-c="1f525" data-s=":fire:" title="fire">&#x1f525;</span>
-            <span class="em em-nature-1f32a" data-c="1f32a" data-s=":cloud_tornado:" title="tornado">&#x1f32a;</span>
+            <span class="em em-nature-1f32a" data-c="1f32a" data-s=":cloud_tornado:" title="tornado">&#x1f32a;&#xfe0f;</span>
             <span class="em em-nature-1f308" data-c="1f308" data-s=":rainbow:" title="rainbow">&#x1f308;</span>
             <span class="em em-nature-1f308" data-c="1f308" data-s=":rainbow:" title="rainbow">&#x1f308;</span>
-            <span class="em em-nature-2600" data-c="2600" data-s=":sunny:" title="sun">&#x2600;</span>
-            <span class="em em-nature-1f324" data-c="1f324" data-s=":white_sun_small_cloud:" title="sun behind small cloud">&#x1f324;</span>
+            <span class="em em-nature-2600" data-c="2600" data-s=":sunny:" title="sun">&#x2600;&#xfe0f;</span>
+            <span class="em em-nature-1f324" data-c="1f324" data-s=":white_sun_small_cloud:" title="sun behind small cloud">&#x1f324;&#xfe0f;</span>
             <span class="em em-nature-26c5" data-c="26c5" data-s=":partly_sunny:" title="sun behind cloud">&#x26c5;</span>
             <span class="em em-nature-26c5" data-c="26c5" data-s=":partly_sunny:" title="sun behind cloud">&#x26c5;</span>
-            <span class="em em-nature-1f325" data-c="1f325" data-s=":white_sun_cloud:" title="sun behind large cloud">&#x1f325;</span>
-            <span class="em em-nature-2601" data-c="2601" data-s=":cloud:" title="cloud">&#x2601;</span>
-            <span class="em em-nature-1f326" data-c="1f326" data-s=":white_sun_rain_cloud:" title="sun behind rain cloud">&#x1f326;</span>
-            <span class="em em-nature-1f327" data-c="1f327" data-s=":cloud_rain:" title="cloud with rain">&#x1f327;</span>
-            <span class="em em-nature-26c8" data-c="26c8" data-s=":thunder_cloud_rain:" title="cloud with lightning and rain">&#x26c8;</span>
-            <span class="em em-nature-1f329" data-c="1f329" data-s=":cloud_lightning:" title="cloud with lightning">&#x1f329;</span>
-            <span class="em em-nature-1f328" data-c="1f328" data-s=":cloud_snow:" title="cloud with snow">&#x1f328;</span>
-            <span class="em em-nature-2744" data-c="2744" data-s=":snowflake:" title="snowflake">&#x2744;</span>
-            <span class="em em-nature-2603" data-c="2603" data-s=":snowman2:" title="snowman">&#x2603;</span>
+            <span class="em em-nature-1f325" data-c="1f325" data-s=":white_sun_cloud:" title="sun behind large cloud">&#x1f325;&#xfe0f;</span>
+            <span class="em em-nature-2601" data-c="2601" data-s=":cloud:" title="cloud">&#x2601;&#xfe0f;</span>
+            <span class="em em-nature-1f326" data-c="1f326" data-s=":white_sun_rain_cloud:" title="sun behind rain cloud">&#x1f326;&#xfe0f;</span>
+            <span class="em em-nature-1f327" data-c="1f327" data-s=":cloud_rain:" title="cloud with rain">&#x1f327;&#xfe0f;</span>
+            <span class="em em-nature-26c8" data-c="26c8" data-s=":thunder_cloud_rain:" title="cloud with lightning and rain">&#x26c8;&#xfe0f;</span>
+            <span class="em em-nature-1f329" data-c="1f329" data-s=":cloud_lightning:" title="cloud with lightning">&#x1f329;&#xfe0f;</span>
+            <span class="em em-nature-1f328" data-c="1f328" data-s=":cloud_snow:" title="cloud with snow">&#x1f328;&#xfe0f;</span>
+            <span class="em em-nature-2744" data-c="2744" data-s=":snowflake:" title="snowflake">&#x2744;&#xfe0f;</span>
+            <span class="em em-nature-2603" data-c="2603" data-s=":snowman2:" title="snowman">&#x2603;&#xfe0f;</span>
             <span class="em em-nature-26c4" data-c="26c4" data-s=":snowman:" title="snowman without snow">&#x26c4;</span>
             <span class="em em-nature-26c4" data-c="26c4" data-s=":snowman:" title="snowman without snow">&#x26c4;</span>
-            <span class="em em-nature-1f32c" data-c="1f32c" data-s=":wind_blowing_face:" title="wind face">&#x1f32c;</span>
+            <span class="em em-nature-1f32c" data-c="1f32c" data-s=":wind_blowing_face:" title="wind face">&#x1f32c;&#xfe0f;</span>
             <span class="em em-nature-1f4a8" data-c="1f4a8" data-s=":dash:" title="dashing away">&#x1f4a8;</span>
             <span class="em em-nature-1f4a8" data-c="1f4a8" data-s=":dash:" title="dashing away">&#x1f4a8;</span>
             <span class="em em-nature-1f4a7" data-c="1f4a7" data-s=":droplet:" title="droplet">&#x1f4a7;</span>
             <span class="em em-nature-1f4a7" data-c="1f4a7" data-s=":droplet:" title="droplet">&#x1f4a7;</span>
             <span class="em em-nature-1f4a6" data-c="1f4a6" data-s=":sweat_drops:" title="sweat droplets">&#x1f4a6;</span>
             <span class="em em-nature-1f4a6" data-c="1f4a6" data-s=":sweat_drops:" title="sweat droplets">&#x1f4a6;</span>
             <span class="em em-nature-2614" data-c="2614" data-s=":umbrella:" title="umbrella with rain drops">&#x2614;</span>
             <span class="em em-nature-2614" data-c="2614" data-s=":umbrella:" title="umbrella with rain drops">&#x2614;</span>
-            <span class="em em-nature-2602" data-c="2602" data-s=":umbrella2:" title="umbrella">&#x2602;</span>
+            <span class="em em-nature-2602" data-c="2602" data-s=":umbrella2:" title="umbrella">&#x2602;&#xfe0f;</span>
             <span class="em em-nature-1f30a" data-c="1f30a" data-s=":ocean:" title="water wave">&#x1f30a;</span>
             <span class="em em-nature-1f30a" data-c="1f30a" data-s=":ocean:" title="water wave">&#x1f30a;</span>
-            <span class="em em-nature-1f32b" data-c="1f32b" data-s=":fog:" title="fog">&#x1f32b;</span>
+            <span class="em em-nature-1f32b" data-c="1f32b" data-s=":fog:" title="fog">&#x1f32b;&#xfe0f;</span>
         </div>
         </div>
    </div>
    </div>
     <div class="tab">
     <div class="tab">
@@ -1350,7 +1350,7 @@
             <span class="em em-food-1f966" data-c="1f966" data-s=":broccoli:" title="broccoli">&#x1f966;</span>
             <span class="em em-food-1f966" data-c="1f966" data-s=":broccoli:" title="broccoli">&#x1f966;</span>
             <span class="em em-food-1f96c" data-c="1f96c" data-s=":leafy_green:" title="leafy green">&#x1f96c;</span>
             <span class="em em-food-1f96c" data-c="1f96c" data-s=":leafy_green:" title="leafy green">&#x1f96c;</span>
             <span class="em em-food-1f952" data-c="1f952" data-s=":cucumber:" title="cucumber">&#x1f952;</span>
             <span class="em em-food-1f952" data-c="1f952" data-s=":cucumber:" title="cucumber">&#x1f952;</span>
-            <span class="em em-food-1f336" data-c="1f336" data-s=":hot_pepper:" title="hot pepper">&#x1f336;</span>
+            <span class="em em-food-1f336" data-c="1f336" data-s=":hot_pepper:" title="hot pepper">&#x1f336;&#xfe0f;</span>
             <span class="em em-food-1f33d" data-c="1f33d" data-s=":corn:" title="ear of corn">&#x1f33d;</span>
             <span class="em em-food-1f33d" data-c="1f33d" data-s=":corn:" title="ear of corn">&#x1f33d;</span>
             <span class="em em-food-1f955" data-c="1f955" data-s=":carrot:" title="carrot">&#x1f955;</span>
             <span class="em em-food-1f955" data-c="1f955" data-s=":carrot:" title="carrot">&#x1f955;</span>
             <span class="em em-food-1f954" data-c="1f954" data-s=":potato:" title="potato">&#x1f954;</span>
             <span class="em em-food-1f954" data-c="1f954" data-s=":potato:" title="potato">&#x1f954;</span>
@@ -1429,7 +1429,7 @@
             <span class="em em-food-1f37e" data-c="1f37e" data-s=":champagne:" title="bottle with popping cork">&#x1f37e;</span>
             <span class="em em-food-1f37e" data-c="1f37e" data-s=":champagne:" title="bottle with popping cork">&#x1f37e;</span>
             <span class="em em-food-1f944" data-c="1f944" data-s=":spoon:" title="spoon">&#x1f944;</span>
             <span class="em em-food-1f944" data-c="1f944" data-s=":spoon:" title="spoon">&#x1f944;</span>
             <span class="em em-food-1f374" data-c="1f374" data-s=":fork_and_knife:" title="fork and knife">&#x1f374;</span>
             <span class="em em-food-1f374" data-c="1f374" data-s=":fork_and_knife:" title="fork and knife">&#x1f374;</span>
-            <span class="em em-food-1f37d" data-c="1f37d" data-s=":fork_knife_plate:" title="fork and knife with plate">&#x1f37d;</span>
+            <span class="em em-food-1f37d" data-c="1f37d" data-s=":fork_knife_plate:" title="fork and knife with plate">&#x1f37d;&#xfe0f;</span>
             <span class="em em-food-1f963" data-c="1f963" data-s=":bowl_with_spoon:" title="bowl with spoon">&#x1f963;</span>
             <span class="em em-food-1f963" data-c="1f963" data-s=":bowl_with_spoon:" title="bowl with spoon">&#x1f963;</span>
             <span class="em em-food-1f961" data-c="1f961" data-s=":takeout_box:" title="takeout box">&#x1f961;</span>
             <span class="em em-food-1f961" data-c="1f961" data-s=":takeout_box:" title="takeout box">&#x1f961;</span>
             <span class="em em-food-1f962" data-c="1f962" data-s=":chopsticks:" title="chopsticks">&#x1f962;</span>
             <span class="em em-food-1f962" data-c="1f962" data-s=":chopsticks:" title="chopsticks">&#x1f962;</span>
@@ -1465,18 +1465,18 @@
             <span class="em em-activity-1f94b" data-c="1f94b" data-s=":martial_arts_uniform:" title="martial arts uniform">&#x1f94b;</span>
             <span class="em em-activity-1f94b" data-c="1f94b" data-s=":martial_arts_uniform:" title="martial arts uniform">&#x1f94b;</span>
             <span class="em em-activity-1f3bd" data-c="1f3bd" data-s=":running_shirt_with_sash:" title="running shirt">&#x1f3bd;</span>
             <span class="em em-activity-1f3bd" data-c="1f3bd" data-s=":running_shirt_with_sash:" title="running shirt">&#x1f3bd;</span>
             <span class="em em-activity-1f6f9" data-c="1f6f9" data-s=":skateboard:" title="skateboard">&#x1f6f9;</span>
             <span class="em em-activity-1f6f9" data-c="1f6f9" data-s=":skateboard:" title="skateboard">&#x1f6f9;</span>
-            <span class="em em-activity-26f8" data-c="26f8" data-s=":ice_skate:" title="ice skate">&#x26f8;</span>
+            <span class="em em-activity-26f8" data-c="26f8" data-s=":ice_skate:" title="ice skate">&#x26f8;&#xfe0f;</span>
             <span class="em em-activity-1f94c" data-c="1f94c" data-s=":curling_stone:" title="curling stone">&#x1f94c;</span>
             <span class="em em-activity-1f94c" data-c="1f94c" data-s=":curling_stone:" title="curling stone">&#x1f94c;</span>
             <span class="em em-activity-1f6f7" data-c="1f6f7" data-s=":sled:" title="sled">&#x1f6f7;</span>
             <span class="em em-activity-1f6f7" data-c="1f6f7" data-s=":sled:" title="sled">&#x1f6f7;</span>
             <span class="em em-activity-1f3bf" data-c="1f3bf" data-s=":ski:" title="skis">&#x1f3bf;</span>
             <span class="em em-activity-1f3bf" data-c="1f3bf" data-s=":ski:" title="skis">&#x1f3bf;</span>
-            <span class="em em-activity-26f7" data-c="26f7" data-s=":skier:" title="skier">&#x26f7;</span>
+            <span class="em em-activity-26f7" data-c="26f7" data-s=":skier:" title="skier">&#x26f7;&#xfe0f;</span>
             <span class="em em-activity-1f3c2" data-c="1f3c2" data-s=":snowboarder:" title="snowboarder">&#x1f3c2;</span>
             <span class="em em-activity-1f3c2" data-c="1f3c2" data-s=":snowboarder:" title="snowboarder">&#x1f3c2;</span>
             <span class="em em-activity-1f3c2-1f3fb" data-c="1f3c2-1f3fb" data-s=":snowboarder_tone1:" title="snowboarder: light skin tone">&#x1f3c2;&#x1f3fb;</span>
             <span class="em em-activity-1f3c2-1f3fb" data-c="1f3c2-1f3fb" data-s=":snowboarder_tone1:" title="snowboarder: light skin tone">&#x1f3c2;&#x1f3fb;</span>
             <span class="em em-activity-1f3c2-1f3fc" data-c="1f3c2-1f3fc" data-s=":snowboarder_tone2:" title="snowboarder: medium-light skin tone">&#x1f3c2;&#x1f3fc;</span>
             <span class="em em-activity-1f3c2-1f3fc" data-c="1f3c2-1f3fc" data-s=":snowboarder_tone2:" title="snowboarder: medium-light skin tone">&#x1f3c2;&#x1f3fc;</span>
             <span class="em em-activity-1f3c2-1f3fd" data-c="1f3c2-1f3fd" data-s=":snowboarder_tone3:" title="snowboarder: medium skin tone">&#x1f3c2;&#x1f3fd;</span>
             <span class="em em-activity-1f3c2-1f3fd" data-c="1f3c2-1f3fd" data-s=":snowboarder_tone3:" title="snowboarder: medium skin tone">&#x1f3c2;&#x1f3fd;</span>
             <span class="em em-activity-1f3c2-1f3fe" data-c="1f3c2-1f3fe" data-s=":snowboarder_tone4:" title="snowboarder: medium-dark skin tone">&#x1f3c2;&#x1f3fe;</span>
             <span class="em em-activity-1f3c2-1f3fe" data-c="1f3c2-1f3fe" data-s=":snowboarder_tone4:" title="snowboarder: medium-dark skin tone">&#x1f3c2;&#x1f3fe;</span>
             <span class="em em-activity-1f3c2-1f3ff" data-c="1f3c2-1f3ff" data-s=":snowboarder_tone5:" title="snowboarder: dark skin tone">&#x1f3c2;&#x1f3ff;</span>
             <span class="em em-activity-1f3c2-1f3ff" data-c="1f3c2-1f3ff" data-s=":snowboarder_tone5:" title="snowboarder: dark skin tone">&#x1f3c2;&#x1f3ff;</span>
-            <span class="em em-activity-1f3cb" data-c="1f3cb" data-s=":person_lifting_weights:" title="person lifting weights">&#x1f3cb;</span>
+            <span class="em em-activity-1f3cb" data-c="1f3cb" data-s=":person_lifting_weights:" title="person lifting weights">&#x1f3cb;&#xfe0f;</span>
             <span class="em em-activity-1f3cb-1f3fb" data-c="1f3cb-1f3fb" data-s=":person_lifting_weights_tone1:" title="person lifting weights: light skin tone">&#x1f3cb;&#x1f3fb;</span>
             <span class="em em-activity-1f3cb-1f3fb" data-c="1f3cb-1f3fb" data-s=":person_lifting_weights_tone1:" title="person lifting weights: light skin tone">&#x1f3cb;&#x1f3fb;</span>
             <span class="em em-activity-1f3cb-1f3fc" data-c="1f3cb-1f3fc" data-s=":person_lifting_weights_tone2:" title="person lifting weights: medium-light skin tone">&#x1f3cb;&#x1f3fc;</span>
             <span class="em em-activity-1f3cb-1f3fc" data-c="1f3cb-1f3fc" data-s=":person_lifting_weights_tone2:" title="person lifting weights: medium-light skin tone">&#x1f3cb;&#x1f3fc;</span>
             <span class="em em-activity-1f3cb-1f3fd" data-c="1f3cb-1f3fd" data-s=":person_lifting_weights_tone3:" title="person lifting weights: medium skin tone">&#x1f3cb;&#x1f3fd;</span>
             <span class="em em-activity-1f3cb-1f3fd" data-c="1f3cb-1f3fd" data-s=":person_lifting_weights_tone3:" title="person lifting weights: medium skin tone">&#x1f3cb;&#x1f3fd;</span>
@@ -1502,7 +1502,7 @@
             <span class="em em-activity-1f938-1f3fd-200d-2642-fe0f" data-c="1f938-1f3fd-200d-2642-fe0f" data-s=":man_cartwheeling_tone3:" title="man cartwheeling: medium skin tone">&#x1f938;&#x1f3fd;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f938-1f3fd-200d-2642-fe0f" data-c="1f938-1f3fd-200d-2642-fe0f" data-s=":man_cartwheeling_tone3:" title="man cartwheeling: medium skin tone">&#x1f938;&#x1f3fd;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f938-1f3fe-200d-2642-fe0f" data-c="1f938-1f3fe-200d-2642-fe0f" data-s=":man_cartwheeling_tone4:" title="man cartwheeling: medium-dark skin tone">&#x1f938;&#x1f3fe;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f938-1f3fe-200d-2642-fe0f" data-c="1f938-1f3fe-200d-2642-fe0f" data-s=":man_cartwheeling_tone4:" title="man cartwheeling: medium-dark skin tone">&#x1f938;&#x1f3fe;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f938-1f3ff-200d-2642-fe0f" data-c="1f938-1f3ff-200d-2642-fe0f" data-s=":man_cartwheeling_tone5:" title="man cartwheeling: dark skin tone">&#x1f938;&#x1f3ff;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f938-1f3ff-200d-2642-fe0f" data-c="1f938-1f3ff-200d-2642-fe0f" data-s=":man_cartwheeling_tone5:" title="man cartwheeling: dark skin tone">&#x1f938;&#x1f3ff;&#x200d;&#x2642;&#xfe0f;</span>
-            <span class="em em-activity-26f9" data-c="26f9" data-s=":person_bouncing_ball:" title="person bouncing ball">&#x26f9;</span>
+            <span class="em em-activity-26f9" data-c="26f9" data-s=":person_bouncing_ball:" title="person bouncing ball">&#x26f9;&#xfe0f;</span>
             <span class="em em-activity-26f9-1f3fb" data-c="26f9-1f3fb" data-s=":person_bouncing_ball_tone1:" title="person bouncing ball: light skin tone">&#x26f9;&#x1f3fb;</span>
             <span class="em em-activity-26f9-1f3fb" data-c="26f9-1f3fb" data-s=":person_bouncing_ball_tone1:" title="person bouncing ball: light skin tone">&#x26f9;&#x1f3fb;</span>
             <span class="em em-activity-26f9-1f3fc" data-c="26f9-1f3fc" data-s=":person_bouncing_ball_tone2:" title="person bouncing ball: medium-light skin tone">&#x26f9;&#x1f3fc;</span>
             <span class="em em-activity-26f9-1f3fc" data-c="26f9-1f3fc" data-s=":person_bouncing_ball_tone2:" title="person bouncing ball: medium-light skin tone">&#x26f9;&#x1f3fc;</span>
             <span class="em em-activity-26f9-1f3fd" data-c="26f9-1f3fd" data-s=":person_bouncing_ball_tone3:" title="person bouncing ball: medium skin tone">&#x26f9;&#x1f3fd;</span>
             <span class="em em-activity-26f9-1f3fd" data-c="26f9-1f3fd" data-s=":person_bouncing_ball_tone3:" title="person bouncing ball: medium skin tone">&#x26f9;&#x1f3fd;</span>
@@ -1527,7 +1527,7 @@
             <span class="em em-activity-1f93e-1f3fd-200d-2642-fe0f" data-c="1f93e-1f3fd-200d-2642-fe0f" data-s=":man_playing_handball_tone3:" title="man playing handball: medium skin tone">&#x1f93e;&#x1f3fd;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f93e-1f3fd-200d-2642-fe0f" data-c="1f93e-1f3fd-200d-2642-fe0f" data-s=":man_playing_handball_tone3:" title="man playing handball: medium skin tone">&#x1f93e;&#x1f3fd;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f93e-1f3fe-200d-2642-fe0f" data-c="1f93e-1f3fe-200d-2642-fe0f" data-s=":man_playing_handball_tone4:" title="man playing handball: medium-dark skin tone">&#x1f93e;&#x1f3fe;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f93e-1f3fe-200d-2642-fe0f" data-c="1f93e-1f3fe-200d-2642-fe0f" data-s=":man_playing_handball_tone4:" title="man playing handball: medium-dark skin tone">&#x1f93e;&#x1f3fe;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f93e-1f3ff-200d-2642-fe0f" data-c="1f93e-1f3ff-200d-2642-fe0f" data-s=":man_playing_handball_tone5:" title="man playing handball: dark skin tone">&#x1f93e;&#x1f3ff;&#x200d;&#x2642;&#xfe0f;</span>
             <span class="em em-activity-1f93e-1f3ff-200d-2642-fe0f" data-c="1f93e-1f3ff-200d-2642-fe0f" data-s=":man_playing_handball_tone5:" title="man playing handball: dark skin tone">&#x1f93e;&#x1f3ff;&#x200d;&#x2642;&#xfe0f;</span>
-            <span class="em em-activity-1f3cc" data-c="1f3cc" data-s=":person_golfing:" title="person golfing">&#x1f3cc;</span>
+            <span class="em em-activity-1f3cc" data-c="1f3cc" data-s=":person_golfing:" title="person golfing">&#x1f3cc;&#xfe0f;</span>
             <span class="em em-activity-1f3cc-1f3fb" data-c="1f3cc-1f3fb" data-s=":person_golfing_tone1:" title="person golfing: light skin tone">&#x1f3cc;&#x1f3fb;</span>
             <span class="em em-activity-1f3cc-1f3fb" data-c="1f3cc-1f3fb" data-s=":person_golfing_tone1:" title="person golfing: light skin tone">&#x1f3cc;&#x1f3fb;</span>
             <span class="em em-activity-1f3cc-1f3fc" data-c="1f3cc-1f3fc" data-s=":person_golfing_tone2:" title="person golfing: medium-light skin tone">&#x1f3cc;&#x1f3fc;</span>
             <span class="em em-activity-1f3cc-1f3fc" data-c="1f3cc-1f3fc" data-s=":person_golfing_tone2:" title="person golfing: medium-light skin tone">&#x1f3cc;&#x1f3fc;</span>
             <span class="em em-activity-1f3cc-1f3fd" data-c="1f3cc-1f3fd" data-s=":person_golfing_tone3:" title="person golfing: medium skin tone">&#x1f3cc;&#x1f3fd;</span>
             <span class="em em-activity-1f3cc-1f3fd" data-c="1f3cc-1f3fd" data-s=":person_golfing_tone3:" title="person golfing: medium skin tone">&#x1f3cc;&#x1f3fd;</span>
@@ -1646,11 +1646,11 @@
             <span class="em em-activity-1f948" data-c="1f948" data-s=":second_place:" title="2nd place medal">&#x1f948;</span>
             <span class="em em-activity-1f948" data-c="1f948" data-s=":second_place:" title="2nd place medal">&#x1f948;</span>
             <span class="em em-activity-1f949" data-c="1f949" data-s=":third_place:" title="3rd place medal">&#x1f949;</span>
             <span class="em em-activity-1f949" data-c="1f949" data-s=":third_place:" title="3rd place medal">&#x1f949;</span>
             <span class="em em-activity-1f3c5" data-c="1f3c5" data-s=":medal:" title="sports medal">&#x1f3c5;</span>
             <span class="em em-activity-1f3c5" data-c="1f3c5" data-s=":medal:" title="sports medal">&#x1f3c5;</span>
-            <span class="em em-activity-1f396" data-c="1f396" data-s=":military_medal:" title="military medal">&#x1f396;</span>
-            <span class="em em-activity-1f3f5" data-c="1f3f5" data-s=":rosette:" title="rosette">&#x1f3f5;</span>
-            <span class="em em-activity-1f397" data-c="1f397" data-s=":reminder_ribbon:" title="reminder ribbon">&#x1f397;</span>
+            <span class="em em-activity-1f396" data-c="1f396" data-s=":military_medal:" title="military medal">&#x1f396;&#xfe0f;</span>
+            <span class="em em-activity-1f3f5" data-c="1f3f5" data-s=":rosette:" title="rosette">&#x1f3f5;&#xfe0f;</span>
+            <span class="em em-activity-1f397" data-c="1f397" data-s=":reminder_ribbon:" title="reminder ribbon">&#x1f397;&#xfe0f;</span>
             <span class="em em-activity-1f3ab" data-c="1f3ab" data-s=":ticket:" title="ticket">&#x1f3ab;</span>
             <span class="em em-activity-1f3ab" data-c="1f3ab" data-s=":ticket:" title="ticket">&#x1f3ab;</span>
-            <span class="em em-activity-1f39f" data-c="1f39f" data-s=":tickets:" title="admission tickets">&#x1f39f;</span>
+            <span class="em em-activity-1f39f" data-c="1f39f" data-s=":tickets:" title="admission tickets">&#x1f39f;&#xfe0f;</span>
             <span class="em em-activity-1f3aa" data-c="1f3aa" data-s=":circus_tent:" title="circus tent">&#x1f3aa;</span>
             <span class="em em-activity-1f3aa" data-c="1f3aa" data-s=":circus_tent:" title="circus tent">&#x1f3aa;</span>
             <span class="em em-activity-1f939-200d-2640-fe0f" data-c="1f939-200d-2640-fe0f" data-s=":woman_juggling:" title="woman juggling">&#x1f939;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-activity-1f939-200d-2640-fe0f" data-c="1f939-200d-2640-fe0f" data-s=":woman_juggling:" title="woman juggling">&#x1f939;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-activity-1f939-1f3fb-200d-2640-fe0f" data-c="1f939-1f3fb-200d-2640-fe0f" data-s=":woman_juggling_tone1:" title="woman juggling: light skin tone">&#x1f939;&#x1f3fb;&#x200d;&#x2640;&#xfe0f;</span>
             <span class="em em-activity-1f939-1f3fb-200d-2640-fe0f" data-c="1f939-1f3fb-200d-2640-fe0f" data-s=":woman_juggling_tone1:" title="woman juggling: light skin tone">&#x1f939;&#x1f3fb;&#x200d;&#x2640;&#xfe0f;</span>
@@ -1694,7 +1694,7 @@
             <span class="em em-travel-1f699" data-c="1f699" data-s=":blue_car:" title="sport utility vehicle">&#x1f699;</span>
             <span class="em em-travel-1f699" data-c="1f699" data-s=":blue_car:" title="sport utility vehicle">&#x1f699;</span>
             <span class="em em-travel-1f68c" data-c="1f68c" data-s=":bus:" title="bus">&#x1f68c;</span>
             <span class="em em-travel-1f68c" data-c="1f68c" data-s=":bus:" title="bus">&#x1f68c;</span>
             <span class="em em-travel-1f68e" data-c="1f68e" data-s=":trolleybus:" title="trolleybus">&#x1f68e;</span>
             <span class="em em-travel-1f68e" data-c="1f68e" data-s=":trolleybus:" title="trolleybus">&#x1f68e;</span>
-            <span class="em em-travel-1f3ce" data-c="1f3ce" data-s=":race_car:" title="racing car">&#x1f3ce;</span>
+            <span class="em em-travel-1f3ce" data-c="1f3ce" data-s=":race_car:" title="racing car">&#x1f3ce;&#xfe0f;</span>
             <span class="em em-travel-1f693" data-c="1f693" data-s=":police_car:" title="police car">&#x1f693;</span>
             <span class="em em-travel-1f693" data-c="1f693" data-s=":police_car:" title="police car">&#x1f693;</span>
             <span class="em em-travel-1f691" data-c="1f691" data-s=":ambulance:" title="ambulance">&#x1f691;</span>
             <span class="em em-travel-1f691" data-c="1f691" data-s=":ambulance:" title="ambulance">&#x1f691;</span>
             <span class="em em-travel-1f692" data-c="1f692" data-s=":fire_engine:" title="fire engine">&#x1f692;</span>
             <span class="em em-travel-1f692" data-c="1f692" data-s=":fire_engine:" title="fire engine">&#x1f692;</span>
@@ -1705,7 +1705,7 @@
             <span class="em em-travel-1f6f4" data-c="1f6f4" data-s=":scooter:" title="kick scooter">&#x1f6f4;</span>
             <span class="em em-travel-1f6f4" data-c="1f6f4" data-s=":scooter:" title="kick scooter">&#x1f6f4;</span>
             <span class="em em-travel-1f6b2" data-c="1f6b2" data-s=":bike:" title="bicycle">&#x1f6b2;</span>
             <span class="em em-travel-1f6b2" data-c="1f6b2" data-s=":bike:" title="bicycle">&#x1f6b2;</span>
             <span class="em em-travel-1f6f5" data-c="1f6f5" data-s=":motor_scooter:" title="motor scooter">&#x1f6f5;</span>
             <span class="em em-travel-1f6f5" data-c="1f6f5" data-s=":motor_scooter:" title="motor scooter">&#x1f6f5;</span>
-            <span class="em em-travel-1f3cd" data-c="1f3cd" data-s=":motorcycle:" title="motorcycle">&#x1f3cd;</span>
+            <span class="em em-travel-1f3cd" data-c="1f3cd" data-s=":motorcycle:" title="motorcycle">&#x1f3cd;&#xfe0f;</span>
             <span class="em em-travel-1f6a8" data-c="1f6a8" data-s=":rotating_light:" title="police car light">&#x1f6a8;</span>
             <span class="em em-travel-1f6a8" data-c="1f6a8" data-s=":rotating_light:" title="police car light">&#x1f6a8;</span>
             <span class="em em-travel-1f694" data-c="1f694" data-s=":oncoming_police_car:" title="oncoming police car">&#x1f694;</span>
             <span class="em em-travel-1f694" data-c="1f694" data-s=":oncoming_police_car:" title="oncoming police car">&#x1f694;</span>
             <span class="em em-travel-1f68d" data-c="1f68d" data-s=":oncoming_bus:" title="oncoming bus">&#x1f68d;</span>
             <span class="em em-travel-1f68d" data-c="1f68d" data-s=":oncoming_bus:" title="oncoming bus">&#x1f68d;</span>
@@ -1726,22 +1726,22 @@
             <span class="em em-travel-1f687" data-c="1f687" data-s=":metro:" title="metro">&#x1f687;</span>
             <span class="em em-travel-1f687" data-c="1f687" data-s=":metro:" title="metro">&#x1f687;</span>
             <span class="em em-travel-1f68a" data-c="1f68a" data-s=":tram:" title="tram">&#x1f68a;</span>
             <span class="em em-travel-1f68a" data-c="1f68a" data-s=":tram:" title="tram">&#x1f68a;</span>
             <span class="em em-travel-1f689" data-c="1f689" data-s=":station:" title="station">&#x1f689;</span>
             <span class="em em-travel-1f689" data-c="1f689" data-s=":station:" title="station">&#x1f689;</span>
-            <span class="em em-travel-2708" data-c="2708" data-s=":airplane:" title="airplane">&#x2708;</span>
+            <span class="em em-travel-2708" data-c="2708" data-s=":airplane:" title="airplane">&#x2708;&#xfe0f;</span>
             <span class="em em-travel-1f6eb" data-c="1f6eb" data-s=":airplane_departure:" title="airplane departure">&#x1f6eb;</span>
             <span class="em em-travel-1f6eb" data-c="1f6eb" data-s=":airplane_departure:" title="airplane departure">&#x1f6eb;</span>
             <span class="em em-travel-1f6ec" data-c="1f6ec" data-s=":airplane_arriving:" title="airplane arrival">&#x1f6ec;</span>
             <span class="em em-travel-1f6ec" data-c="1f6ec" data-s=":airplane_arriving:" title="airplane arrival">&#x1f6ec;</span>
-            <span class="em em-travel-1f6e9" data-c="1f6e9" data-s=":airplane_small:" title="small airplane">&#x1f6e9;</span>
+            <span class="em em-travel-1f6e9" data-c="1f6e9" data-s=":airplane_small:" title="small airplane">&#x1f6e9;&#xfe0f;</span>
             <span class="em em-travel-1f4ba" data-c="1f4ba" data-s=":seat:" title="seat">&#x1f4ba;</span>
             <span class="em em-travel-1f4ba" data-c="1f4ba" data-s=":seat:" title="seat">&#x1f4ba;</span>
             <span class="em em-travel-1f9f3" data-c="1f9f3" data-s=":luggage:" title="luggage">&#x1f9f3;</span>
             <span class="em em-travel-1f9f3" data-c="1f9f3" data-s=":luggage:" title="luggage">&#x1f9f3;</span>
-            <span class="em em-travel-1f6f0" data-c="1f6f0" data-s=":satellite_orbital:" title="satellite">&#x1f6f0;</span>
+            <span class="em em-travel-1f6f0" data-c="1f6f0" data-s=":satellite_orbital:" title="satellite">&#x1f6f0;&#xfe0f;</span>
             <span class="em em-travel-1f680" data-c="1f680" data-s=":rocket:" title="rocket">&#x1f680;</span>
             <span class="em em-travel-1f680" data-c="1f680" data-s=":rocket:" title="rocket">&#x1f680;</span>
             <span class="em em-travel-1f6f8" data-c="1f6f8" data-s=":flying_saucer:" title="flying saucer">&#x1f6f8;</span>
             <span class="em em-travel-1f6f8" data-c="1f6f8" data-s=":flying_saucer:" title="flying saucer">&#x1f6f8;</span>
             <span class="em em-travel-1f681" data-c="1f681" data-s=":helicopter:" title="helicopter">&#x1f681;</span>
             <span class="em em-travel-1f681" data-c="1f681" data-s=":helicopter:" title="helicopter">&#x1f681;</span>
             <span class="em em-travel-1f6f6" data-c="1f6f6" data-s=":canoe:" title="canoe">&#x1f6f6;</span>
             <span class="em em-travel-1f6f6" data-c="1f6f6" data-s=":canoe:" title="canoe">&#x1f6f6;</span>
             <span class="em em-travel-26f5" data-c="26f5" data-s=":sailboat:" title="sailboat">&#x26f5;</span>
             <span class="em em-travel-26f5" data-c="26f5" data-s=":sailboat:" title="sailboat">&#x26f5;</span>
             <span class="em em-travel-1f6a4" data-c="1f6a4" data-s=":speedboat:" title="speedboat">&#x1f6a4;</span>
             <span class="em em-travel-1f6a4" data-c="1f6a4" data-s=":speedboat:" title="speedboat">&#x1f6a4;</span>
-            <span class="em em-travel-1f6e5" data-c="1f6e5" data-s=":motorboat:" title="motor boat">&#x1f6e5;</span>
-            <span class="em em-travel-1f6f3" data-c="1f6f3" data-s=":cruise_ship:" title="passenger ship">&#x1f6f3;</span>
-            <span class="em em-travel-26f4" data-c="26f4" data-s=":ferry:" title="ferry">&#x26f4;</span>
+            <span class="em em-travel-1f6e5" data-c="1f6e5" data-s=":motorboat:" title="motor boat">&#x1f6e5;&#xfe0f;</span>
+            <span class="em em-travel-1f6f3" data-c="1f6f3" data-s=":cruise_ship:" title="passenger ship">&#x1f6f3;&#xfe0f;</span>
+            <span class="em em-travel-26f4" data-c="26f4" data-s=":ferry:" title="ferry">&#x26f4;&#xfe0f;</span>
             <span class="em em-travel-1f6a2" data-c="1f6a2" data-s=":ship:" title="ship">&#x1f6a2;</span>
             <span class="em em-travel-1f6a2" data-c="1f6a2" data-s=":ship:" title="ship">&#x1f6a2;</span>
             <span class="em em-travel-2693" data-c="2693" data-s=":anchor:" title="anchor">&#x2693;</span>
             <span class="em em-travel-2693" data-c="2693" data-s=":anchor:" title="anchor">&#x2693;</span>
             <span class="em em-travel-26fd" data-c="26fd" data-s=":fuelpump:" title="fuel pump">&#x26fd;</span>
             <span class="em em-travel-26fd" data-c="26fd" data-s=":fuelpump:" title="fuel pump">&#x26fd;</span>
@@ -1749,32 +1749,32 @@
             <span class="em em-travel-1f6a6" data-c="1f6a6" data-s=":vertical_traffic_light:" title="vertical traffic light">&#x1f6a6;</span>
             <span class="em em-travel-1f6a6" data-c="1f6a6" data-s=":vertical_traffic_light:" title="vertical traffic light">&#x1f6a6;</span>
             <span class="em em-travel-1f6a5" data-c="1f6a5" data-s=":traffic_light:" title="horizontal traffic light">&#x1f6a5;</span>
             <span class="em em-travel-1f6a5" data-c="1f6a5" data-s=":traffic_light:" title="horizontal traffic light">&#x1f6a5;</span>
             <span class="em em-travel-1f68f" data-c="1f68f" data-s=":busstop:" title="bus stop">&#x1f68f;</span>
             <span class="em em-travel-1f68f" data-c="1f68f" data-s=":busstop:" title="bus stop">&#x1f68f;</span>
-            <span class="em em-travel-1f5fa" data-c="1f5fa" data-s=":map:" title="world map">&#x1f5fa;</span>
+            <span class="em em-travel-1f5fa" data-c="1f5fa" data-s=":map:" title="world map">&#x1f5fa;&#xfe0f;</span>
             <span class="em em-travel-1f5ff" data-c="1f5ff" data-s=":moyai:" title="moai">&#x1f5ff;</span>
             <span class="em em-travel-1f5ff" data-c="1f5ff" data-s=":moyai:" title="moai">&#x1f5ff;</span>
             <span class="em em-travel-1f5fd" data-c="1f5fd" data-s=":statue_of_liberty:" title="Statue of Liberty">&#x1f5fd;</span>
             <span class="em em-travel-1f5fd" data-c="1f5fd" data-s=":statue_of_liberty:" title="Statue of Liberty">&#x1f5fd;</span>
             <span class="em em-travel-1f5fc" data-c="1f5fc" data-s=":tokyo_tower:" title="Tokyo tower">&#x1f5fc;</span>
             <span class="em em-travel-1f5fc" data-c="1f5fc" data-s=":tokyo_tower:" title="Tokyo tower">&#x1f5fc;</span>
             <span class="em em-travel-1f3f0" data-c="1f3f0" data-s=":european_castle:" title="castle">&#x1f3f0;</span>
             <span class="em em-travel-1f3f0" data-c="1f3f0" data-s=":european_castle:" title="castle">&#x1f3f0;</span>
             <span class="em em-travel-1f3ef" data-c="1f3ef" data-s=":japanese_castle:" title="Japanese castle">&#x1f3ef;</span>
             <span class="em em-travel-1f3ef" data-c="1f3ef" data-s=":japanese_castle:" title="Japanese castle">&#x1f3ef;</span>
-            <span class="em em-travel-1f3df" data-c="1f3df" data-s=":stadium:" title="stadium">&#x1f3df;</span>
+            <span class="em em-travel-1f3df" data-c="1f3df" data-s=":stadium:" title="stadium">&#x1f3df;&#xfe0f;</span>
             <span class="em em-travel-1f3a1" data-c="1f3a1" data-s=":ferris_wheel:" title="ferris wheel">&#x1f3a1;</span>
             <span class="em em-travel-1f3a1" data-c="1f3a1" data-s=":ferris_wheel:" title="ferris wheel">&#x1f3a1;</span>
             <span class="em em-travel-1f3a2" data-c="1f3a2" data-s=":roller_coaster:" title="roller coaster">&#x1f3a2;</span>
             <span class="em em-travel-1f3a2" data-c="1f3a2" data-s=":roller_coaster:" title="roller coaster">&#x1f3a2;</span>
             <span class="em em-travel-1f3a0" data-c="1f3a0" data-s=":carousel_horse:" title="carousel horse">&#x1f3a0;</span>
             <span class="em em-travel-1f3a0" data-c="1f3a0" data-s=":carousel_horse:" title="carousel horse">&#x1f3a0;</span>
             <span class="em em-travel-26f2" data-c="26f2" data-s=":fountain:" title="fountain">&#x26f2;</span>
             <span class="em em-travel-26f2" data-c="26f2" data-s=":fountain:" title="fountain">&#x26f2;</span>
-            <span class="em em-travel-26f1" data-c="26f1" data-s=":beach_umbrella:" title="umbrella on ground">&#x26f1;</span>
-            <span class="em em-travel-1f3d6" data-c="1f3d6" data-s=":beach:" title="beach with umbrella">&#x1f3d6;</span>
-            <span class="em em-travel-1f3dd" data-c="1f3dd" data-s=":island:" title="desert island">&#x1f3dd;</span>
-            <span class="em em-travel-1f3dc" data-c="1f3dc" data-s=":desert:" title="desert">&#x1f3dc;</span>
+            <span class="em em-travel-26f1" data-c="26f1" data-s=":beach_umbrella:" title="umbrella on ground">&#x26f1;&#xfe0f;</span>
+            <span class="em em-travel-1f3d6" data-c="1f3d6" data-s=":beach:" title="beach with umbrella">&#x1f3d6;&#xfe0f;</span>
+            <span class="em em-travel-1f3dd" data-c="1f3dd" data-s=":island:" title="desert island">&#x1f3dd;&#xfe0f;</span>
+            <span class="em em-travel-1f3dc" data-c="1f3dc" data-s=":desert:" title="desert">&#x1f3dc;&#xfe0f;</span>
             <span class="em em-travel-1f30b" data-c="1f30b" data-s=":volcano:" title="volcano">&#x1f30b;</span>
             <span class="em em-travel-1f30b" data-c="1f30b" data-s=":volcano:" title="volcano">&#x1f30b;</span>
-            <span class="em em-travel-26f0" data-c="26f0" data-s=":mountain:" title="mountain">&#x26f0;</span>
-            <span class="em em-travel-1f3d4" data-c="1f3d4" data-s=":mountain_snow:" title="snow-capped mountain">&#x1f3d4;</span>
+            <span class="em em-travel-26f0" data-c="26f0" data-s=":mountain:" title="mountain">&#x26f0;&#xfe0f;</span>
+            <span class="em em-travel-1f3d4" data-c="1f3d4" data-s=":mountain_snow:" title="snow-capped mountain">&#x1f3d4;&#xfe0f;</span>
             <span class="em em-travel-1f5fb" data-c="1f5fb" data-s=":mount_fuji:" title="mount fuji">&#x1f5fb;</span>
             <span class="em em-travel-1f5fb" data-c="1f5fb" data-s=":mount_fuji:" title="mount fuji">&#x1f5fb;</span>
-            <span class="em em-travel-1f3d5" data-c="1f3d5" data-s=":camping:" title="camping">&#x1f3d5;</span>
+            <span class="em em-travel-1f3d5" data-c="1f3d5" data-s=":camping:" title="camping">&#x1f3d5;&#xfe0f;</span>
             <span class="em em-travel-26fa" data-c="26fa" data-s=":tent:" title="tent">&#x26fa;</span>
             <span class="em em-travel-26fa" data-c="26fa" data-s=":tent:" title="tent">&#x26fa;</span>
             <span class="em em-travel-1f3e0" data-c="1f3e0" data-s=":house:" title="house">&#x1f3e0;</span>
             <span class="em em-travel-1f3e0" data-c="1f3e0" data-s=":house:" title="house">&#x1f3e0;</span>
             <span class="em em-travel-1f3e1" data-c="1f3e1" data-s=":house_with_garden:" title="house with garden">&#x1f3e1;</span>
             <span class="em em-travel-1f3e1" data-c="1f3e1" data-s=":house_with_garden:" title="house with garden">&#x1f3e1;</span>
-            <span class="em em-travel-1f3d8" data-c="1f3d8" data-s=":homes:" title="houses">&#x1f3d8;</span>
-            <span class="em em-travel-1f3da" data-c="1f3da" data-s=":house_abandoned:" title="derelict house">&#x1f3da;</span>
-            <span class="em em-travel-1f3d7" data-c="1f3d7" data-s=":construction_site:" title="building construction">&#x1f3d7;</span>
+            <span class="em em-travel-1f3d8" data-c="1f3d8" data-s=":homes:" title="houses">&#x1f3d8;&#xfe0f;</span>
+            <span class="em em-travel-1f3da" data-c="1f3da" data-s=":house_abandoned:" title="derelict house">&#x1f3da;&#xfe0f;</span>
+            <span class="em em-travel-1f3d7" data-c="1f3d7" data-s=":construction_site:" title="building construction">&#x1f3d7;&#xfe0f;</span>
             <span class="em em-travel-1f3ed" data-c="1f3ed" data-s=":factory:" title="factory">&#x1f3ed;</span>
             <span class="em em-travel-1f3ed" data-c="1f3ed" data-s=":factory:" title="factory">&#x1f3ed;</span>
             <span class="em em-travel-1f3e2" data-c="1f3e2" data-s=":office:" title="office building">&#x1f3e2;</span>
             <span class="em em-travel-1f3e2" data-c="1f3e2" data-s=":office:" title="office building">&#x1f3e2;</span>
             <span class="em em-travel-1f3ec" data-c="1f3ec" data-s=":department_store:" title="department store">&#x1f3ec;</span>
             <span class="em em-travel-1f3ec" data-c="1f3ec" data-s=":department_store:" title="department store">&#x1f3ec;</span>
@@ -1787,17 +1787,17 @@
             <span class="em em-travel-1f3eb" data-c="1f3eb" data-s=":school:" title="school">&#x1f3eb;</span>
             <span class="em em-travel-1f3eb" data-c="1f3eb" data-s=":school:" title="school">&#x1f3eb;</span>
             <span class="em em-travel-1f3e9" data-c="1f3e9" data-s=":love_hotel:" title="love hotel">&#x1f3e9;</span>
             <span class="em em-travel-1f3e9" data-c="1f3e9" data-s=":love_hotel:" title="love hotel">&#x1f3e9;</span>
             <span class="em em-travel-1f492" data-c="1f492" data-s=":wedding:" title="wedding">&#x1f492;</span>
             <span class="em em-travel-1f492" data-c="1f492" data-s=":wedding:" title="wedding">&#x1f492;</span>
-            <span class="em em-travel-1f3db" data-c="1f3db" data-s=":classical_building:" title="classical building">&#x1f3db;</span>
+            <span class="em em-travel-1f3db" data-c="1f3db" data-s=":classical_building:" title="classical building">&#x1f3db;&#xfe0f;</span>
             <span class="em em-travel-26ea" data-c="26ea" data-s=":church:" title="church">&#x26ea;</span>
             <span class="em em-travel-26ea" data-c="26ea" data-s=":church:" title="church">&#x26ea;</span>
             <span class="em em-travel-1f54c" data-c="1f54c" data-s=":mosque:" title="mosque">&#x1f54c;</span>
             <span class="em em-travel-1f54c" data-c="1f54c" data-s=":mosque:" title="mosque">&#x1f54c;</span>
             <span class="em em-travel-1f54d" data-c="1f54d" data-s=":synagogue:" title="synagogue">&#x1f54d;</span>
             <span class="em em-travel-1f54d" data-c="1f54d" data-s=":synagogue:" title="synagogue">&#x1f54d;</span>
             <span class="em em-travel-1f54b" data-c="1f54b" data-s=":kaaba:" title="kaaba">&#x1f54b;</span>
             <span class="em em-travel-1f54b" data-c="1f54b" data-s=":kaaba:" title="kaaba">&#x1f54b;</span>
-            <span class="em em-travel-26e9" data-c="26e9" data-s=":shinto_shrine:" title="shinto shrine">&#x26e9;</span>
-            <span class="em em-travel-1f6e4" data-c="1f6e4" data-s=":railway_track:" title="railway track">&#x1f6e4;</span>
-            <span class="em em-travel-1f6e3" data-c="1f6e3" data-s=":motorway:" title="motorway">&#x1f6e3;</span>
+            <span class="em em-travel-26e9" data-c="26e9" data-s=":shinto_shrine:" title="shinto shrine">&#x26e9;&#xfe0f;</span>
+            <span class="em em-travel-1f6e4" data-c="1f6e4" data-s=":railway_track:" title="railway track">&#x1f6e4;&#xfe0f;</span>
+            <span class="em em-travel-1f6e3" data-c="1f6e3" data-s=":motorway:" title="motorway">&#x1f6e3;&#xfe0f;</span>
             <span class="em em-travel-1f5fe" data-c="1f5fe" data-s=":japan:" title="map of Japan">&#x1f5fe;</span>
             <span class="em em-travel-1f5fe" data-c="1f5fe" data-s=":japan:" title="map of Japan">&#x1f5fe;</span>
             <span class="em em-travel-1f391" data-c="1f391" data-s=":rice_scene:" title="moon viewing ceremony">&#x1f391;</span>
             <span class="em em-travel-1f391" data-c="1f391" data-s=":rice_scene:" title="moon viewing ceremony">&#x1f391;</span>
-            <span class="em em-travel-1f3de" data-c="1f3de" data-s=":park:" title="national park">&#x1f3de;</span>
+            <span class="em em-travel-1f3de" data-c="1f3de" data-s=":park:" title="national park">&#x1f3de;&#xfe0f;</span>
             <span class="em em-travel-1f305" data-c="1f305" data-s=":sunrise:" title="sunrise">&#x1f305;</span>
             <span class="em em-travel-1f305" data-c="1f305" data-s=":sunrise:" title="sunrise">&#x1f305;</span>
             <span class="em em-travel-1f304" data-c="1f304" data-s=":sunrise_over_mountains:" title="sunrise over mountains">&#x1f304;</span>
             <span class="em em-travel-1f304" data-c="1f304" data-s=":sunrise_over_mountains:" title="sunrise over mountains">&#x1f304;</span>
             <span class="em em-travel-1f320" data-c="1f320" data-s=":stars:" title="shooting star">&#x1f320;</span>
             <span class="em em-travel-1f320" data-c="1f320" data-s=":stars:" title="shooting star">&#x1f320;</span>
@@ -1806,7 +1806,7 @@
             <span class="em em-travel-1f9e8" data-c="1f9e8" data-s=":firecracker:" title="firecracker">&#x1f9e8;</span>
             <span class="em em-travel-1f9e8" data-c="1f9e8" data-s=":firecracker:" title="firecracker">&#x1f9e8;</span>
             <span class="em em-travel-1f307" data-c="1f307" data-s=":city_sunset:" title="sunset">&#x1f307;</span>
             <span class="em em-travel-1f307" data-c="1f307" data-s=":city_sunset:" title="sunset">&#x1f307;</span>
             <span class="em em-travel-1f306" data-c="1f306" data-s=":city_dusk:" title="cityscape at dusk">&#x1f306;</span>
             <span class="em em-travel-1f306" data-c="1f306" data-s=":city_dusk:" title="cityscape at dusk">&#x1f306;</span>
-            <span class="em em-travel-1f3d9" data-c="1f3d9" data-s=":cityscape:" title="cityscape">&#x1f3d9;</span>
+            <span class="em em-travel-1f3d9" data-c="1f3d9" data-s=":cityscape:" title="cityscape">&#x1f3d9;&#xfe0f;</span>
             <span class="em em-travel-1f303" data-c="1f303" data-s=":night_with_stars:" title="night with stars">&#x1f303;</span>
             <span class="em em-travel-1f303" data-c="1f303" data-s=":night_with_stars:" title="night with stars">&#x1f303;</span>
             <span class="em em-travel-1f30c" data-c="1f30c" data-s=":milky_way:" title="milky way">&#x1f30c;</span>
             <span class="em em-travel-1f30c" data-c="1f30c" data-s=":milky_way:" title="milky way">&#x1f30c;</span>
             <span class="em em-travel-1f309" data-c="1f309" data-s=":bridge_at_night:" title="bridge at night">&#x1f309;</span>
             <span class="em em-travel-1f309" data-c="1f309" data-s=":bridge_at_night:" title="bridge at night">&#x1f309;</span>
@@ -1823,15 +1823,15 @@
             <span class="em em-objects-1f4f1" data-c="1f4f1" data-s=":iphone:" title="mobile phone">&#x1f4f1;</span>
             <span class="em em-objects-1f4f1" data-c="1f4f1" data-s=":iphone:" title="mobile phone">&#x1f4f1;</span>
             <span class="em em-objects-1f4f2" data-c="1f4f2" data-s=":calling:" title="mobile phone with arrow">&#x1f4f2;</span>
             <span class="em em-objects-1f4f2" data-c="1f4f2" data-s=":calling:" title="mobile phone with arrow">&#x1f4f2;</span>
             <span class="em em-objects-1f4bb" data-c="1f4bb" data-s=":computer:" title="laptop computer">&#x1f4bb;</span>
             <span class="em em-objects-1f4bb" data-c="1f4bb" data-s=":computer:" title="laptop computer">&#x1f4bb;</span>
-            <span class="em em-objects-2328" data-c="2328" data-s=":keyboard:" title="keyboard">&#x2328;</span>
-            <span class="em em-objects-1f5a5" data-c="1f5a5" data-s=":desktop:" title="desktop computer">&#x1f5a5;</span>
-            <span class="em em-objects-1f5a8" data-c="1f5a8" data-s=":printer:" title="printer">&#x1f5a8;</span>
-            <span class="em em-objects-1f5b1" data-c="1f5b1" data-s=":mouse_three_button:" title="computer mouse">&#x1f5b1;</span>
-            <span class="em em-objects-1f5b2" data-c="1f5b2" data-s=":trackball:" title="trackball">&#x1f5b2;</span>
-            <span class="em em-objects-1f579" data-c="1f579" data-s=":joystick:" title="joystick">&#x1f579;</span>
+            <span class="em em-objects-2328" data-c="2328" data-s=":keyboard:" title="keyboard">&#x2328;&#xfe0f;</span>
+            <span class="em em-objects-1f5a5" data-c="1f5a5" data-s=":desktop:" title="desktop computer">&#x1f5a5;&#xfe0f;</span>
+            <span class="em em-objects-1f5a8" data-c="1f5a8" data-s=":printer:" title="printer">&#x1f5a8;&#xfe0f;</span>
+            <span class="em em-objects-1f5b1" data-c="1f5b1" data-s=":mouse_three_button:" title="computer mouse">&#x1f5b1;&#xfe0f;</span>
+            <span class="em em-objects-1f5b2" data-c="1f5b2" data-s=":trackball:" title="trackball">&#x1f5b2;&#xfe0f;</span>
+            <span class="em em-objects-1f579" data-c="1f579" data-s=":joystick:" title="joystick">&#x1f579;&#xfe0f;</span>
             <span class="em em-objects-265f" data-c="265f" data-s=":chess_pawn:" title="chess pawn">&#x265f;&#xfe0f;</span>
             <span class="em em-objects-265f" data-c="265f" data-s=":chess_pawn:" title="chess pawn">&#x265f;&#xfe0f;</span>
             <span class="em em-objects-1f9e9" data-c="1f9e9" data-s=":jigsaw:" title="jigsaw">&#x1f9e9;</span>
             <span class="em em-objects-1f9e9" data-c="1f9e9" data-s=":jigsaw:" title="jigsaw">&#x1f9e9;</span>
-            <span class="em em-objects-1f5dc" data-c="1f5dc" data-s=":compression:" title="clamp">&#x1f5dc;</span>
+            <span class="em em-objects-1f5dc" data-c="1f5dc" data-s=":compression:" title="clamp">&#x1f5dc;&#xfe0f;</span>
             <span class="em em-objects-1f4bd" data-c="1f4bd" data-s=":minidisc:" title="computer disk">&#x1f4bd;</span>
             <span class="em em-objects-1f4bd" data-c="1f4bd" data-s=":minidisc:" title="computer disk">&#x1f4bd;</span>
             <span class="em em-objects-1f4be" data-c="1f4be" data-s=":floppy_disk:" title="floppy disk">&#x1f4be;</span>
             <span class="em em-objects-1f4be" data-c="1f4be" data-s=":floppy_disk:" title="floppy disk">&#x1f4be;</span>
             <span class="em em-objects-1f4bf" data-c="1f4bf" data-s=":cd:" title="optical disk">&#x1f4bf;</span>
             <span class="em em-objects-1f4bf" data-c="1f4bf" data-s=":cd:" title="optical disk">&#x1f4bf;</span>
@@ -1841,21 +1841,21 @@
             <span class="em em-objects-1f4f8" data-c="1f4f8" data-s=":camera_with_flash:" title="camera with flash">&#x1f4f8;</span>
             <span class="em em-objects-1f4f8" data-c="1f4f8" data-s=":camera_with_flash:" title="camera with flash">&#x1f4f8;</span>
             <span class="em em-objects-1f4f9" data-c="1f4f9" data-s=":video_camera:" title="video camera">&#x1f4f9;</span>
             <span class="em em-objects-1f4f9" data-c="1f4f9" data-s=":video_camera:" title="video camera">&#x1f4f9;</span>
             <span class="em em-objects-1f3a5" data-c="1f3a5" data-s=":movie_camera:" title="movie camera">&#x1f3a5;</span>
             <span class="em em-objects-1f3a5" data-c="1f3a5" data-s=":movie_camera:" title="movie camera">&#x1f3a5;</span>
-            <span class="em em-objects-1f4fd" data-c="1f4fd" data-s=":projector:" title="film projector">&#x1f4fd;</span>
-            <span class="em em-objects-1f39e" data-c="1f39e" data-s=":film_frames:" title="film frames">&#x1f39e;</span>
+            <span class="em em-objects-1f4fd" data-c="1f4fd" data-s=":projector:" title="film projector">&#x1f4fd;&#xfe0f;</span>
+            <span class="em em-objects-1f39e" data-c="1f39e" data-s=":film_frames:" title="film frames">&#x1f39e;&#xfe0f;</span>
             <span class="em em-objects-1f4de" data-c="1f4de" data-s=":telephone_receiver:" title="telephone receiver">&#x1f4de;</span>
             <span class="em em-objects-1f4de" data-c="1f4de" data-s=":telephone_receiver:" title="telephone receiver">&#x1f4de;</span>
-            <span class="em em-objects-260e" data-c="260e" data-s=":telephone:" title="telephone">&#x260e;</span>
+            <span class="em em-objects-260e" data-c="260e" data-s=":telephone:" title="telephone">&#x260e;&#xfe0f;</span>
             <span class="em em-objects-1f4df" data-c="1f4df" data-s=":pager:" title="pager">&#x1f4df;</span>
             <span class="em em-objects-1f4df" data-c="1f4df" data-s=":pager:" title="pager">&#x1f4df;</span>
             <span class="em em-objects-1f4e0" data-c="1f4e0" data-s=":fax:" title="fax machine">&#x1f4e0;</span>
             <span class="em em-objects-1f4e0" data-c="1f4e0" data-s=":fax:" title="fax machine">&#x1f4e0;</span>
             <span class="em em-objects-1f4fa" data-c="1f4fa" data-s=":tv:" title="television">&#x1f4fa;</span>
             <span class="em em-objects-1f4fa" data-c="1f4fa" data-s=":tv:" title="television">&#x1f4fa;</span>
             <span class="em em-objects-1f4fb" data-c="1f4fb" data-s=":radio:" title="radio">&#x1f4fb;</span>
             <span class="em em-objects-1f4fb" data-c="1f4fb" data-s=":radio:" title="radio">&#x1f4fb;</span>
-            <span class="em em-objects-1f399" data-c="1f399" data-s=":microphone2:" title="studio microphone">&#x1f399;</span>
-            <span class="em em-objects-1f39a" data-c="1f39a" data-s=":level_slider:" title="level slider">&#x1f39a;</span>
-            <span class="em em-objects-1f39b" data-c="1f39b" data-s=":control_knobs:" title="control knobs">&#x1f39b;</span>
-            <span class="em em-objects-23f1" data-c="23f1" data-s=":stopwatch:" title="stopwatch">&#x23f1;</span>
-            <span class="em em-objects-23f2" data-c="23f2" data-s=":timer:" title="timer clock">&#x23f2;</span>
+            <span class="em em-objects-1f399" data-c="1f399" data-s=":microphone2:" title="studio microphone">&#x1f399;&#xfe0f;</span>
+            <span class="em em-objects-1f39a" data-c="1f39a" data-s=":level_slider:" title="level slider">&#x1f39a;&#xfe0f;</span>
+            <span class="em em-objects-1f39b" data-c="1f39b" data-s=":control_knobs:" title="control knobs">&#x1f39b;&#xfe0f;</span>
+            <span class="em em-objects-23f1" data-c="23f1" data-s=":stopwatch:" title="stopwatch">&#x23f1;&#xfe0f;</span>
+            <span class="em em-objects-23f2" data-c="23f2" data-s=":timer:" title="timer clock">&#x23f2;&#xfe0f;</span>
             <span class="em em-objects-23f0" data-c="23f0" data-s=":alarm_clock:" title="alarm clock">&#x23f0;</span>
             <span class="em em-objects-23f0" data-c="23f0" data-s=":alarm_clock:" title="alarm clock">&#x23f0;</span>
-            <span class="em em-objects-1f570" data-c="1f570" data-s=":clock:" title="mantelpiece clock">&#x1f570;</span>
+            <span class="em em-objects-1f570" data-c="1f570" data-s=":clock:" title="mantelpiece clock">&#x1f570;&#xfe0f;</span>
             <span class="em em-objects-231b" data-c="231b" data-s=":hourglass:" title="hourglass done">&#x231b;</span>
             <span class="em em-objects-231b" data-c="231b" data-s=":hourglass:" title="hourglass done">&#x231b;</span>
             <span class="em em-objects-23f3" data-c="23f3" data-s=":hourglass_flowing_sand:" title="hourglass not done">&#x23f3;</span>
             <span class="em em-objects-23f3" data-c="23f3" data-s=":hourglass_flowing_sand:" title="hourglass not done">&#x23f3;</span>
             <span class="em em-objects-1f4e1" data-c="1f4e1" data-s=":satellite:" title="satellite antenna">&#x1f4e1;</span>
             <span class="em em-objects-1f4e1" data-c="1f4e1" data-s=":satellite:" title="satellite antenna">&#x1f4e1;</span>
@@ -1865,10 +1865,10 @@
             <span class="em em-objects-1f9f2" data-c="1f9f2" data-s=":magnet:" title="magnet">&#x1f9f2;</span>
             <span class="em em-objects-1f9f2" data-c="1f9f2" data-s=":magnet:" title="magnet">&#x1f9f2;</span>
             <span class="em em-objects-1f4a1" data-c="1f4a1" data-s=":bulb:" title="light bulb">&#x1f4a1;</span>
             <span class="em em-objects-1f4a1" data-c="1f4a1" data-s=":bulb:" title="light bulb">&#x1f4a1;</span>
             <span class="em em-objects-1f526" data-c="1f526" data-s=":flashlight:" title="flashlight">&#x1f526;</span>
             <span class="em em-objects-1f526" data-c="1f526" data-s=":flashlight:" title="flashlight">&#x1f526;</span>
-            <span class="em em-objects-1f56f" data-c="1f56f" data-s=":candle:" title="candle">&#x1f56f;</span>
+            <span class="em em-objects-1f56f" data-c="1f56f" data-s=":candle:" title="candle">&#x1f56f;&#xfe0f;</span>
             <span class="em em-objects-1f9ef" data-c="1f9ef" data-s=":fire_extinguisher:" title="fire extinguisher">&#x1f9ef;</span>
             <span class="em em-objects-1f9ef" data-c="1f9ef" data-s=":fire_extinguisher:" title="fire extinguisher">&#x1f9ef;</span>
-            <span class="em em-objects-1f5d1" data-c="1f5d1" data-s=":wastebasket:" title="wastebasket">&#x1f5d1;</span>
-            <span class="em em-objects-1f6e2" data-c="1f6e2" data-s=":oil:" title="oil drum">&#x1f6e2;</span>
+            <span class="em em-objects-1f5d1" data-c="1f5d1" data-s=":wastebasket:" title="wastebasket">&#x1f5d1;&#xfe0f;</span>
+            <span class="em em-objects-1f6e2" data-c="1f6e2" data-s=":oil:" title="oil drum">&#x1f6e2;&#xfe0f;</span>
             <span class="em em-objects-1f4b8" data-c="1f4b8" data-s=":money_with_wings:" title="money with wings">&#x1f4b8;</span>
             <span class="em em-objects-1f4b8" data-c="1f4b8" data-s=":money_with_wings:" title="money with wings">&#x1f4b8;</span>
             <span class="em em-objects-1f4b5" data-c="1f4b5" data-s=":dollar:" title="dollar banknote">&#x1f4b5;</span>
             <span class="em em-objects-1f4b5" data-c="1f4b5" data-s=":dollar:" title="dollar banknote">&#x1f4b5;</span>
             <span class="em em-objects-1f4b4" data-c="1f4b4" data-s=":yen:" title="yen banknote">&#x1f4b4;</span>
             <span class="em em-objects-1f4b4" data-c="1f4b4" data-s=":yen:" title="yen banknote">&#x1f4b4;</span>
@@ -1879,40 +1879,40 @@
             <span class="em em-objects-1f48e" data-c="1f48e" data-s=":gem:" title="gem stone">&#x1f48e;</span>
             <span class="em em-objects-1f48e" data-c="1f48e" data-s=":gem:" title="gem stone">&#x1f48e;</span>
             <span class="em em-objects-1f9ff" data-c="1f9ff" data-s=":nazar_amulet:" title="nazar amulet">&#x1f9ff;</span>
             <span class="em em-objects-1f9ff" data-c="1f9ff" data-s=":nazar_amulet:" title="nazar amulet">&#x1f9ff;</span>
             <span class="em em-objects-1f9f1" data-c="1f9f1" data-s=":bricks:" title="bricks">&#x1f9f1;</span>
             <span class="em em-objects-1f9f1" data-c="1f9f1" data-s=":bricks:" title="bricks">&#x1f9f1;</span>
-            <span class="em em-objects-2696" data-c="2696" data-s=":scales:" title="balance scale">&#x2696;</span>
+            <span class="em em-objects-2696" data-c="2696" data-s=":scales:" title="balance scale">&#x2696;&#xfe0f;</span>
             <span class="em em-objects-1f9f0" data-c="1f9f0" data-s=":toolbox:" title="toolbox">&#x1f9f0;</span>
             <span class="em em-objects-1f9f0" data-c="1f9f0" data-s=":toolbox:" title="toolbox">&#x1f9f0;</span>
             <span class="em em-objects-1f527" data-c="1f527" data-s=":wrench:" title="wrench">&#x1f527;</span>
             <span class="em em-objects-1f527" data-c="1f527" data-s=":wrench:" title="wrench">&#x1f527;</span>
             <span class="em em-objects-1f528" data-c="1f528" data-s=":hammer:" title="hammer">&#x1f528;</span>
             <span class="em em-objects-1f528" data-c="1f528" data-s=":hammer:" title="hammer">&#x1f528;</span>
-            <span class="em em-objects-2692" data-c="2692" data-s=":hammer_pick:" title="hammer and pick">&#x2692;</span>
-            <span class="em em-objects-1f6e0" data-c="1f6e0" data-s=":tools:" title="hammer and wrench">&#x1f6e0;</span>
-            <span class="em em-objects-26cf" data-c="26cf" data-s=":pick:" title="pick">&#x26cf;</span>
+            <span class="em em-objects-2692" data-c="2692" data-s=":hammer_pick:" title="hammer and pick">&#x2692;&#xfe0f;</span>
+            <span class="em em-objects-1f6e0" data-c="1f6e0" data-s=":tools:" title="hammer and wrench">&#x1f6e0;&#xfe0f;</span>
+            <span class="em em-objects-26cf" data-c="26cf" data-s=":pick:" title="pick">&#x26cf;&#xfe0f;</span>
             <span class="em em-objects-1f529" data-c="1f529" data-s=":nut_and_bolt:" title="nut and bolt">&#x1f529;</span>
             <span class="em em-objects-1f529" data-c="1f529" data-s=":nut_and_bolt:" title="nut and bolt">&#x1f529;</span>
-            <span class="em em-objects-2699" data-c="2699" data-s=":gear:" title="gear">&#x2699;</span>
-            <span class="em em-objects-26d3" data-c="26d3" data-s=":chains:" title="chains">&#x26d3;</span>
+            <span class="em em-objects-2699" data-c="2699" data-s=":gear:" title="gear">&#x2699;&#xfe0f;</span>
+            <span class="em em-objects-26d3" data-c="26d3" data-s=":chains:" title="chains">&#x26d3;&#xfe0f;</span>
             <span class="em em-objects-1f52b" data-c="1f52b" data-s=":gun:" title="pistol">&#x1f52b;</span>
             <span class="em em-objects-1f52b" data-c="1f52b" data-s=":gun:" title="pistol">&#x1f52b;</span>
             <span class="em em-objects-1f4a3" data-c="1f4a3" data-s=":bomb:" title="bomb">&#x1f4a3;</span>
             <span class="em em-objects-1f4a3" data-c="1f4a3" data-s=":bomb:" title="bomb">&#x1f4a3;</span>
             <span class="em em-objects-1f52a" data-c="1f52a" data-s=":knife:" title="kitchen knife">&#x1f52a;</span>
             <span class="em em-objects-1f52a" data-c="1f52a" data-s=":knife:" title="kitchen knife">&#x1f52a;</span>
-            <span class="em em-objects-1f5e1" data-c="1f5e1" data-s=":dagger:" title="dagger">&#x1f5e1;</span>
-            <span class="em em-objects-2694" data-c="2694" data-s=":crossed_swords:" title="crossed swords">&#x2694;</span>
-            <span class="em em-objects-1f6e1" data-c="1f6e1" data-s=":shield:" title="shield">&#x1f6e1;</span>
+            <span class="em em-objects-1f5e1" data-c="1f5e1" data-s=":dagger:" title="dagger">&#x1f5e1;&#xfe0f;</span>
+            <span class="em em-objects-2694" data-c="2694" data-s=":crossed_swords:" title="crossed swords">&#x2694;&#xfe0f;</span>
+            <span class="em em-objects-1f6e1" data-c="1f6e1" data-s=":shield:" title="shield">&#x1f6e1;&#xfe0f;</span>
             <span class="em em-objects-1f6ac" data-c="1f6ac" data-s=":smoking:" title="cigarette">&#x1f6ac;</span>
             <span class="em em-objects-1f6ac" data-c="1f6ac" data-s=":smoking:" title="cigarette">&#x1f6ac;</span>
-            <span class="em em-objects-26b0" data-c="26b0" data-s=":coffin:" title="coffin">&#x26b0;</span>
-            <span class="em em-objects-26b1" data-c="26b1" data-s=":urn:" title="funeral urn">&#x26b1;</span>
+            <span class="em em-objects-26b0" data-c="26b0" data-s=":coffin:" title="coffin">&#x26b0;&#xfe0f;</span>
+            <span class="em em-objects-26b1" data-c="26b1" data-s=":urn:" title="funeral urn">&#x26b1;&#xfe0f;</span>
             <span class="em em-objects-1f3fa" data-c="1f3fa" data-s=":amphora:" title="amphora">&#x1f3fa;</span>
             <span class="em em-objects-1f3fa" data-c="1f3fa" data-s=":amphora:" title="amphora">&#x1f3fa;</span>
             <span class="em em-objects-1f52e" data-c="1f52e" data-s=":crystal_ball:" title="crystal ball">&#x1f52e;</span>
             <span class="em em-objects-1f52e" data-c="1f52e" data-s=":crystal_ball:" title="crystal ball">&#x1f52e;</span>
             <span class="em em-objects-1f4ff" data-c="1f4ff" data-s=":prayer_beads:" title="prayer beads">&#x1f4ff;</span>
             <span class="em em-objects-1f4ff" data-c="1f4ff" data-s=":prayer_beads:" title="prayer beads">&#x1f4ff;</span>
             <span class="em em-objects-1f488" data-c="1f488" data-s=":barber:" title="barber pole">&#x1f488;</span>
             <span class="em em-objects-1f488" data-c="1f488" data-s=":barber:" title="barber pole">&#x1f488;</span>
-            <span class="em em-objects-2697" data-c="2697" data-s=":alembic:" title="alembic">&#x2697;</span>
+            <span class="em em-objects-2697" data-c="2697" data-s=":alembic:" title="alembic">&#x2697;&#xfe0f;</span>
             <span class="em em-objects-1f9ea" data-c="1f9ea" data-s=":test_tube:" title="test tube">&#x1f9ea;</span>
             <span class="em em-objects-1f9ea" data-c="1f9ea" data-s=":test_tube:" title="test tube">&#x1f9ea;</span>
             <span class="em em-objects-1f9eb" data-c="1f9eb" data-s=":petri_dish:" title="petri dish">&#x1f9eb;</span>
             <span class="em em-objects-1f9eb" data-c="1f9eb" data-s=":petri_dish:" title="petri dish">&#x1f9eb;</span>
             <span class="em em-objects-1f9ec" data-c="1f9ec" data-s=":dna:" title="dna">&#x1f9ec;</span>
             <span class="em em-objects-1f9ec" data-c="1f9ec" data-s=":dna:" title="dna">&#x1f9ec;</span>
             <span class="em em-objects-1f9ee" data-c="1f9ee" data-s=":abacus:" title="abacus">&#x1f9ee;</span>
             <span class="em em-objects-1f9ee" data-c="1f9ee" data-s=":abacus:" title="abacus">&#x1f9ee;</span>
             <span class="em em-objects-1f52d" data-c="1f52d" data-s=":telescope:" title="telescope">&#x1f52d;</span>
             <span class="em em-objects-1f52d" data-c="1f52d" data-s=":telescope:" title="telescope">&#x1f52d;</span>
             <span class="em em-objects-1f52c" data-c="1f52c" data-s=":microscope:" title="microscope">&#x1f52c;</span>
             <span class="em em-objects-1f52c" data-c="1f52c" data-s=":microscope:" title="microscope">&#x1f52c;</span>
-            <span class="em em-objects-1f573" data-c="1f573" data-s=":hole:" title="hole">&#x1f573;</span>
+            <span class="em em-objects-1f573" data-c="1f573" data-s=":hole:" title="hole">&#x1f573;&#xfe0f;</span>
             <span class="em em-objects-1f48a" data-c="1f48a" data-s=":pill:" title="pill">&#x1f48a;</span>
             <span class="em em-objects-1f48a" data-c="1f48a" data-s=":pill:" title="pill">&#x1f48a;</span>
             <span class="em em-objects-1f489" data-c="1f489" data-s=":syringe:" title="syringe">&#x1f489;</span>
             <span class="em em-objects-1f489" data-c="1f489" data-s=":syringe:" title="syringe">&#x1f489;</span>
-            <span class="em em-objects-1f321" data-c="1f321" data-s=":thermometer:" title="thermometer">&#x1f321;</span>
+            <span class="em em-objects-1f321" data-c="1f321" data-s=":thermometer:" title="thermometer">&#x1f321;&#xfe0f;</span>
             <span class="em em-objects-1f6bd" data-c="1f6bd" data-s=":toilet:" title="toilet">&#x1f6bd;</span>
             <span class="em em-objects-1f6bd" data-c="1f6bd" data-s=":toilet:" title="toilet">&#x1f6bd;</span>
             <span class="em em-objects-1f6b0" data-c="1f6b0" data-s=":potable_water:" title="potable water">&#x1f6b0;</span>
             <span class="em em-objects-1f6b0" data-c="1f6b0" data-s=":potable_water:" title="potable water">&#x1f6b0;</span>
             <span class="em em-objects-1f6bf" data-c="1f6bf" data-s=":shower:" title="shower">&#x1f6bf;</span>
             <span class="em em-objects-1f6bf" data-c="1f6bf" data-s=":shower:" title="shower">&#x1f6bf;</span>
@@ -1931,12 +1931,12 @@
             <span class="em em-objects-1f9f4" data-c="1f9f4" data-s=":squeeze_bottle:" title="squeeze bottle">&#x1f9f4;</span>
             <span class="em em-objects-1f9f4" data-c="1f9f4" data-s=":squeeze_bottle:" title="squeeze bottle">&#x1f9f4;</span>
             <span class="em em-objects-1f9f5" data-c="1f9f5" data-s=":thread:" title="thread">&#x1f9f5;</span>
             <span class="em em-objects-1f9f5" data-c="1f9f5" data-s=":thread:" title="thread">&#x1f9f5;</span>
             <span class="em em-objects-1f9f6" data-c="1f9f6" data-s=":yarn:" title="yarn">&#x1f9f6;</span>
             <span class="em em-objects-1f9f6" data-c="1f9f6" data-s=":yarn:" title="yarn">&#x1f9f6;</span>
-            <span class="em em-objects-1f6ce" data-c="1f6ce" data-s=":bellhop:" title="bellhop bell">&#x1f6ce;</span>
+            <span class="em em-objects-1f6ce" data-c="1f6ce" data-s=":bellhop:" title="bellhop bell">&#x1f6ce;&#xfe0f;</span>
             <span class="em em-objects-1f511" data-c="1f511" data-s=":key:" title="key">&#x1f511;</span>
             <span class="em em-objects-1f511" data-c="1f511" data-s=":key:" title="key">&#x1f511;</span>
-            <span class="em em-objects-1f5dd" data-c="1f5dd" data-s=":key2:" title="old key">&#x1f5dd;</span>
+            <span class="em em-objects-1f5dd" data-c="1f5dd" data-s=":key2:" title="old key">&#x1f5dd;&#xfe0f;</span>
             <span class="em em-objects-1f6aa" data-c="1f6aa" data-s=":door:" title="door">&#x1f6aa;</span>
             <span class="em em-objects-1f6aa" data-c="1f6aa" data-s=":door:" title="door">&#x1f6aa;</span>
-            <span class="em em-objects-1f6cb" data-c="1f6cb" data-s=":couch:" title="couch and lamp">&#x1f6cb;</span>
-            <span class="em em-objects-1f6cf" data-c="1f6cf" data-s=":bed:" title="bed">&#x1f6cf;</span>
+            <span class="em em-objects-1f6cb" data-c="1f6cb" data-s=":couch:" title="couch and lamp">&#x1f6cb;&#xfe0f;</span>
+            <span class="em em-objects-1f6cf" data-c="1f6cf" data-s=":bed:" title="bed">&#x1f6cf;&#xfe0f;</span>
             <span class="em em-objects-1f6cc" data-c="1f6cc" data-s=":sleeping_accommodation:" title="person in bed">&#x1f6cc;</span>
             <span class="em em-objects-1f6cc" data-c="1f6cc" data-s=":sleeping_accommodation:" title="person in bed">&#x1f6cc;</span>
             <span class="em em-objects-1f6cc-1f3fb" data-c="1f6cc-1f3fb" data-s=":person_in_bed_tone1:" title="person in bed: light skin tone">&#x1f6cc;&#x1f3fb;</span>
             <span class="em em-objects-1f6cc-1f3fb" data-c="1f6cc-1f3fb" data-s=":person_in_bed_tone1:" title="person in bed: light skin tone">&#x1f6cc;&#x1f3fb;</span>
             <span class="em em-objects-1f6cc-1f3fc" data-c="1f6cc-1f3fc" data-s=":person_in_bed_tone2:" title="person in bed: medium-light skin tone">&#x1f6cc;&#x1f3fc;</span>
             <span class="em em-objects-1f6cc-1f3fc" data-c="1f6cc-1f3fc" data-s=":person_in_bed_tone2:" title="person in bed: medium-light skin tone">&#x1f6cc;&#x1f3fc;</span>
@@ -1944,8 +1944,8 @@
             <span class="em em-objects-1f6cc-1f3fe" data-c="1f6cc-1f3fe" data-s=":person_in_bed_tone4:" title="person in bed: medium-dark skin tone">&#x1f6cc;&#x1f3fe;</span>
             <span class="em em-objects-1f6cc-1f3fe" data-c="1f6cc-1f3fe" data-s=":person_in_bed_tone4:" title="person in bed: medium-dark skin tone">&#x1f6cc;&#x1f3fe;</span>
             <span class="em em-objects-1f6cc-1f3ff" data-c="1f6cc-1f3ff" data-s=":person_in_bed_tone5:" title="person in bed: dark skin tone">&#x1f6cc;&#x1f3ff;</span>
             <span class="em em-objects-1f6cc-1f3ff" data-c="1f6cc-1f3ff" data-s=":person_in_bed_tone5:" title="person in bed: dark skin tone">&#x1f6cc;&#x1f3ff;</span>
             <span class="em em-objects-1f9f8" data-c="1f9f8" data-s=":teddy_bear:" title="teddy bear">&#x1f9f8;</span>
             <span class="em em-objects-1f9f8" data-c="1f9f8" data-s=":teddy_bear:" title="teddy bear">&#x1f9f8;</span>
-            <span class="em em-objects-1f5bc" data-c="1f5bc" data-s=":frame_photo:" title="framed picture">&#x1f5bc;</span>
-            <span class="em em-objects-1f6cd" data-c="1f6cd" data-s=":shopping_bags:" title="shopping bags">&#x1f6cd;</span>
+            <span class="em em-objects-1f5bc" data-c="1f5bc" data-s=":frame_photo:" title="framed picture">&#x1f5bc;&#xfe0f;</span>
+            <span class="em em-objects-1f6cd" data-c="1f6cd" data-s=":shopping_bags:" title="shopping bags">&#x1f6cd;&#xfe0f;</span>
             <span class="em em-objects-1f6d2" data-c="1f6d2" data-s=":shopping_cart:" title="shopping cart">&#x1f6d2;</span>
             <span class="em em-objects-1f6d2" data-c="1f6d2" data-s=":shopping_cart:" title="shopping cart">&#x1f6d2;</span>
             <span class="em em-objects-1f381" data-c="1f381" data-s=":gift:" title="wrapped gift">&#x1f381;</span>
             <span class="em em-objects-1f381" data-c="1f381" data-s=":gift:" title="wrapped gift">&#x1f381;</span>
             <span class="em em-objects-1f388" data-c="1f388" data-s=":balloon:" title="balloon">&#x1f388;</span>
             <span class="em em-objects-1f388" data-c="1f388" data-s=":balloon:" title="balloon">&#x1f388;</span>
@@ -1957,7 +1957,7 @@
             <span class="em em-objects-1f3ee" data-c="1f3ee" data-s=":izakaya_lantern:" title="red paper lantern">&#x1f3ee;</span>
             <span class="em em-objects-1f3ee" data-c="1f3ee" data-s=":izakaya_lantern:" title="red paper lantern">&#x1f3ee;</span>
             <span class="em em-objects-1f390" data-c="1f390" data-s=":wind_chime:" title="wind chime">&#x1f390;</span>
             <span class="em em-objects-1f390" data-c="1f390" data-s=":wind_chime:" title="wind chime">&#x1f390;</span>
             <span class="em em-objects-1f9e7" data-c="1f9e7" data-s=":red_envelope:" title="red envelope">&#x1f9e7;</span>
             <span class="em em-objects-1f9e7" data-c="1f9e7" data-s=":red_envelope:" title="red envelope">&#x1f9e7;</span>
-            <span class="em em-objects-2709" data-c="2709" data-s=":envelope:" title="envelope">&#x2709;</span>
+            <span class="em em-objects-2709" data-c="2709" data-s=":envelope:" title="envelope">&#x2709;&#xfe0f;</span>
             <span class="em em-objects-1f4e9" data-c="1f4e9" data-s=":envelope_with_arrow:" title="envelope with arrow">&#x1f4e9;</span>
             <span class="em em-objects-1f4e9" data-c="1f4e9" data-s=":envelope_with_arrow:" title="envelope with arrow">&#x1f4e9;</span>
             <span class="em em-objects-1f4e8" data-c="1f4e8" data-s=":incoming_envelope:" title="incoming envelope">&#x1f4e8;</span>
             <span class="em em-objects-1f4e8" data-c="1f4e8" data-s=":incoming_envelope:" title="incoming envelope">&#x1f4e8;</span>
             <span class="em em-objects-1f4e7" data-c="1f4e7" data-s=":e-mail:" title="e-mail">&#x1f4e7;</span>
             <span class="em em-objects-1f4e7" data-c="1f4e7" data-s=":e-mail:" title="e-mail">&#x1f4e7;</span>
@@ -1965,7 +1965,7 @@
             <span class="em em-objects-1f4e5" data-c="1f4e5" data-s=":inbox_tray:" title="inbox tray">&#x1f4e5;</span>
             <span class="em em-objects-1f4e5" data-c="1f4e5" data-s=":inbox_tray:" title="inbox tray">&#x1f4e5;</span>
             <span class="em em-objects-1f4e4" data-c="1f4e4" data-s=":outbox_tray:" title="outbox tray">&#x1f4e4;</span>
             <span class="em em-objects-1f4e4" data-c="1f4e4" data-s=":outbox_tray:" title="outbox tray">&#x1f4e4;</span>
             <span class="em em-objects-1f4e6" data-c="1f4e6" data-s=":package:" title="package">&#x1f4e6;</span>
             <span class="em em-objects-1f4e6" data-c="1f4e6" data-s=":package:" title="package">&#x1f4e6;</span>
-            <span class="em em-objects-1f3f7" data-c="1f3f7" data-s=":label:" title="label">&#x1f3f7;</span>
+            <span class="em em-objects-1f3f7" data-c="1f3f7" data-s=":label:" title="label">&#x1f3f7;&#xfe0f;</span>
             <span class="em em-objects-1f4ea" data-c="1f4ea" data-s=":mailbox_closed:" title="closed mailbox with lowered flag">&#x1f4ea;</span>
             <span class="em em-objects-1f4ea" data-c="1f4ea" data-s=":mailbox_closed:" title="closed mailbox with lowered flag">&#x1f4ea;</span>
             <span class="em em-objects-1f4eb" data-c="1f4eb" data-s=":mailbox:" title="closed mailbox with raised flag">&#x1f4eb;</span>
             <span class="em em-objects-1f4eb" data-c="1f4eb" data-s=":mailbox:" title="closed mailbox with raised flag">&#x1f4eb;</span>
             <span class="em em-objects-1f4ec" data-c="1f4ec" data-s=":mailbox_with_mail:" title="open mailbox with raised flag">&#x1f4ec;</span>
             <span class="em em-objects-1f4ec" data-c="1f4ec" data-s=":mailbox_with_mail:" title="open mailbox with raised flag">&#x1f4ec;</span>
@@ -1980,19 +1980,19 @@
             <span class="em em-objects-1f4ca" data-c="1f4ca" data-s=":bar_chart:" title="bar chart">&#x1f4ca;</span>
             <span class="em em-objects-1f4ca" data-c="1f4ca" data-s=":bar_chart:" title="bar chart">&#x1f4ca;</span>
             <span class="em em-objects-1f4c8" data-c="1f4c8" data-s=":chart_with_upwards_trend:" title="chart increasing">&#x1f4c8;</span>
             <span class="em em-objects-1f4c8" data-c="1f4c8" data-s=":chart_with_upwards_trend:" title="chart increasing">&#x1f4c8;</span>
             <span class="em em-objects-1f4c9" data-c="1f4c9" data-s=":chart_with_downwards_trend:" title="chart decreasing">&#x1f4c9;</span>
             <span class="em em-objects-1f4c9" data-c="1f4c9" data-s=":chart_with_downwards_trend:" title="chart decreasing">&#x1f4c9;</span>
-            <span class="em em-objects-1f5d2" data-c="1f5d2" data-s=":notepad_spiral:" title="spiral notepad">&#x1f5d2;</span>
-            <span class="em em-objects-1f5d3" data-c="1f5d3" data-s=":calendar_spiral:" title="spiral calendar">&#x1f5d3;</span>
+            <span class="em em-objects-1f5d2" data-c="1f5d2" data-s=":notepad_spiral:" title="spiral notepad">&#x1f5d2;&#xfe0f;</span>
+            <span class="em em-objects-1f5d3" data-c="1f5d3" data-s=":calendar_spiral:" title="spiral calendar">&#x1f5d3;&#xfe0f;</span>
             <span class="em em-objects-1f4c6" data-c="1f4c6" data-s=":calendar:" title="tear-off calendar">&#x1f4c6;</span>
             <span class="em em-objects-1f4c6" data-c="1f4c6" data-s=":calendar:" title="tear-off calendar">&#x1f4c6;</span>
             <span class="em em-objects-1f4c5" data-c="1f4c5" data-s=":date:" title="calendar">&#x1f4c5;</span>
             <span class="em em-objects-1f4c5" data-c="1f4c5" data-s=":date:" title="calendar">&#x1f4c5;</span>
             <span class="em em-objects-1f4c7" data-c="1f4c7" data-s=":card_index:" title="card index">&#x1f4c7;</span>
             <span class="em em-objects-1f4c7" data-c="1f4c7" data-s=":card_index:" title="card index">&#x1f4c7;</span>
-            <span class="em em-objects-1f5c3" data-c="1f5c3" data-s=":card_box:" title="card file box">&#x1f5c3;</span>
-            <span class="em em-objects-1f5f3" data-c="1f5f3" data-s=":ballot_box:" title="ballot box with ballot">&#x1f5f3;</span>
-            <span class="em em-objects-1f5c4" data-c="1f5c4" data-s=":file_cabinet:" title="file cabinet">&#x1f5c4;</span>
+            <span class="em em-objects-1f5c3" data-c="1f5c3" data-s=":card_box:" title="card file box">&#x1f5c3;&#xfe0f;</span>
+            <span class="em em-objects-1f5f3" data-c="1f5f3" data-s=":ballot_box:" title="ballot box with ballot">&#x1f5f3;&#xfe0f;</span>
+            <span class="em em-objects-1f5c4" data-c="1f5c4" data-s=":file_cabinet:" title="file cabinet">&#x1f5c4;&#xfe0f;</span>
             <span class="em em-objects-1f4cb" data-c="1f4cb" data-s=":clipboard:" title="clipboard">&#x1f4cb;</span>
             <span class="em em-objects-1f4cb" data-c="1f4cb" data-s=":clipboard:" title="clipboard">&#x1f4cb;</span>
             <span class="em em-objects-1f4c1" data-c="1f4c1" data-s=":file_folder:" title="file folder">&#x1f4c1;</span>
             <span class="em em-objects-1f4c1" data-c="1f4c1" data-s=":file_folder:" title="file folder">&#x1f4c1;</span>
             <span class="em em-objects-1f4c2" data-c="1f4c2" data-s=":open_file_folder:" title="open file folder">&#x1f4c2;</span>
             <span class="em em-objects-1f4c2" data-c="1f4c2" data-s=":open_file_folder:" title="open file folder">&#x1f4c2;</span>
-            <span class="em em-objects-1f5c2" data-c="1f5c2" data-s=":dividers:" title="card index dividers">&#x1f5c2;</span>
-            <span class="em em-objects-1f5de" data-c="1f5de" data-s=":newspaper2:" title="rolled-up newspaper">&#x1f5de;</span>
+            <span class="em em-objects-1f5c2" data-c="1f5c2" data-s=":dividers:" title="card index dividers">&#x1f5c2;&#xfe0f;</span>
+            <span class="em em-objects-1f5de" data-c="1f5de" data-s=":newspaper2:" title="rolled-up newspaper">&#x1f5de;&#xfe0f;</span>
             <span class="em em-objects-1f4f0" data-c="1f4f0" data-s=":newspaper:" title="newspaper">&#x1f4f0;</span>
             <span class="em em-objects-1f4f0" data-c="1f4f0" data-s=":newspaper:" title="newspaper">&#x1f4f0;</span>
             <span class="em em-objects-1f4d3" data-c="1f4d3" data-s=":notebook:" title="notebook">&#x1f4d3;</span>
             <span class="em em-objects-1f4d3" data-c="1f4d3" data-s=":notebook:" title="notebook">&#x1f4d3;</span>
             <span class="em em-objects-1f4d4" data-c="1f4d4" data-s=":notebook_with_decorative_cover:" title="notebook with decorative cover">&#x1f4d4;</span>
             <span class="em em-objects-1f4d4" data-c="1f4d4" data-s=":notebook_with_decorative_cover:" title="notebook with decorative cover">&#x1f4d4;</span>
@@ -2006,20 +2006,20 @@
             <span class="em em-objects-1f516" data-c="1f516" data-s=":bookmark:" title="bookmark">&#x1f516;</span>
             <span class="em em-objects-1f516" data-c="1f516" data-s=":bookmark:" title="bookmark">&#x1f516;</span>
             <span class="em em-objects-1f517" data-c="1f517" data-s=":link:" title="link">&#x1f517;</span>
             <span class="em em-objects-1f517" data-c="1f517" data-s=":link:" title="link">&#x1f517;</span>
             <span class="em em-objects-1f4ce" data-c="1f4ce" data-s=":paperclip:" title="paperclip">&#x1f4ce;</span>
             <span class="em em-objects-1f4ce" data-c="1f4ce" data-s=":paperclip:" title="paperclip">&#x1f4ce;</span>
-            <span class="em em-objects-1f587" data-c="1f587" data-s=":paperclips:" title="linked paperclips">&#x1f587;</span>
+            <span class="em em-objects-1f587" data-c="1f587" data-s=":paperclips:" title="linked paperclips">&#x1f587;&#xfe0f;</span>
             <span class="em em-objects-1f4d0" data-c="1f4d0" data-s=":triangular_ruler:" title="triangular ruler">&#x1f4d0;</span>
             <span class="em em-objects-1f4d0" data-c="1f4d0" data-s=":triangular_ruler:" title="triangular ruler">&#x1f4d0;</span>
             <span class="em em-objects-1f4cf" data-c="1f4cf" data-s=":straight_ruler:" title="straight ruler">&#x1f4cf;</span>
             <span class="em em-objects-1f4cf" data-c="1f4cf" data-s=":straight_ruler:" title="straight ruler">&#x1f4cf;</span>
             <span class="em em-objects-1f9f7" data-c="1f9f7" data-s=":safety_pin:" title="safety pin">&#x1f9f7;</span>
             <span class="em em-objects-1f9f7" data-c="1f9f7" data-s=":safety_pin:" title="safety pin">&#x1f9f7;</span>
             <span class="em em-objects-1f4cc" data-c="1f4cc" data-s=":pushpin:" title="pushpin">&#x1f4cc;</span>
             <span class="em em-objects-1f4cc" data-c="1f4cc" data-s=":pushpin:" title="pushpin">&#x1f4cc;</span>
             <span class="em em-objects-1f4cd" data-c="1f4cd" data-s=":round_pushpin:" title="round pushpin">&#x1f4cd;</span>
             <span class="em em-objects-1f4cd" data-c="1f4cd" data-s=":round_pushpin:" title="round pushpin">&#x1f4cd;</span>
-            <span class="em em-objects-2702" data-c="2702" data-s=":scissors:" title="scissors">&#x2702;</span>
-            <span class="em em-objects-1f58a" data-c="1f58a" data-s=":pen_ballpoint:" title="pen">&#x1f58a;</span>
-            <span class="em em-objects-1f58b" data-c="1f58b" data-s=":pen_fountain:" title="fountain pen">&#x1f58b;</span>
-            <span class="em em-objects-2712" data-c="2712" data-s=":black_nib:" title="black nib">&#x2712;</span>
-            <span class="em em-objects-1f58c" data-c="1f58c" data-s=":paintbrush:" title="paintbrush">&#x1f58c;</span>
-            <span class="em em-objects-1f58d" data-c="1f58d" data-s=":crayon:" title="crayon">&#x1f58d;</span>
+            <span class="em em-objects-2702" data-c="2702" data-s=":scissors:" title="scissors">&#x2702;&#xfe0f;</span>
+            <span class="em em-objects-1f58a" data-c="1f58a" data-s=":pen_ballpoint:" title="pen">&#x1f58a;&#xfe0f;</span>
+            <span class="em em-objects-1f58b" data-c="1f58b" data-s=":pen_fountain:" title="fountain pen">&#x1f58b;&#xfe0f;</span>
+            <span class="em em-objects-2712" data-c="2712" data-s=":black_nib:" title="black nib">&#x2712;&#xfe0f;</span>
+            <span class="em em-objects-1f58c" data-c="1f58c" data-s=":paintbrush:" title="paintbrush">&#x1f58c;&#xfe0f;</span>
+            <span class="em em-objects-1f58d" data-c="1f58d" data-s=":crayon:" title="crayon">&#x1f58d;&#xfe0f;</span>
             <span class="em em-objects-1f4dd" data-c="1f4dd" data-s=":pencil:" title="memo">&#x1f4dd;</span>
             <span class="em em-objects-1f4dd" data-c="1f4dd" data-s=":pencil:" title="memo">&#x1f4dd;</span>
-            <span class="em em-objects-270f" data-c="270f" data-s=":pencil2:" title="pencil">&#x270f;</span>
+            <span class="em em-objects-270f" data-c="270f" data-s=":pencil2:" title="pencil">&#x270f;&#xfe0f;</span>
             <span class="em em-objects-1f50d" data-c="1f50d" data-s=":mag:" title="magnifying glass tilted left">&#x1f50d;</span>
             <span class="em em-objects-1f50d" data-c="1f50d" data-s=":mag:" title="magnifying glass tilted left">&#x1f50d;</span>
             <span class="em em-objects-1f50e" data-c="1f50e" data-s=":mag_right:" title="magnifying glass tilted right">&#x1f50e;</span>
             <span class="em em-objects-1f50e" data-c="1f50e" data-s=":mag_right:" title="magnifying glass tilted right">&#x1f50e;</span>
             <span class="em em-objects-1f50f" data-c="1f50f" data-s=":lock_with_ink_pen:" title="locked with pen">&#x1f50f;</span>
             <span class="em em-objects-1f50f" data-c="1f50f" data-s=":lock_with_ink_pen:" title="locked with pen">&#x1f50f;</span>
@@ -2034,7 +2034,7 @@
             <img src="emoji/symbols.svg" class="em-symbols" height="24" width="24" role="button" tabindex="0"></span>
             <img src="emoji/symbols.svg" class="em-symbols" height="24" width="24" role="button" tabindex="0"></span>
         </label>
         </label>
         <div class="content">
         <div class="content">
-            <span class="em em-symbols-2764" data-c="2764" data-s=":heart:" title="red heart">&#x2764;</span>
+            <span class="em em-symbols-2764" data-c="2764" data-s=":heart:" title="red heart">&#x2764;&#xfe0f;</span>
             <span class="em em-symbols-1f9e1" data-c="1f9e1" data-s=":orange_heart:" title="orange heart">&#x1f9e1;</span>
             <span class="em em-symbols-1f9e1" data-c="1f9e1" data-s=":orange_heart:" title="orange heart">&#x1f9e1;</span>
             <span class="em em-symbols-1f49b" data-c="1f49b" data-s=":yellow_heart:" title="yellow heart">&#x1f49b;</span>
             <span class="em em-symbols-1f49b" data-c="1f49b" data-s=":yellow_heart:" title="yellow heart">&#x1f49b;</span>
             <span class="em em-symbols-1f49a" data-c="1f49a" data-s=":green_heart:" title="green heart">&#x1f49a;</span>
             <span class="em em-symbols-1f49a" data-c="1f49a" data-s=":green_heart:" title="green heart">&#x1f49a;</span>
@@ -2042,7 +2042,7 @@
             <span class="em em-symbols-1f49c" data-c="1f49c" data-s=":purple_heart:" title="purple heart">&#x1f49c;</span>
             <span class="em em-symbols-1f49c" data-c="1f49c" data-s=":purple_heart:" title="purple heart">&#x1f49c;</span>
             <span class="em em-symbols-1f5a4" data-c="1f5a4" data-s=":black_heart:" title="black heart">&#x1f5a4;</span>
             <span class="em em-symbols-1f5a4" data-c="1f5a4" data-s=":black_heart:" title="black heart">&#x1f5a4;</span>
             <span class="em em-symbols-1f494" data-c="1f494" data-s=":broken_heart:" title="broken heart">&#x1f494;</span>
             <span class="em em-symbols-1f494" data-c="1f494" data-s=":broken_heart:" title="broken heart">&#x1f494;</span>
-            <span class="em em-symbols-2763" data-c="2763" data-s=":heart_exclamation:" title="heavy heart exclamation">&#x2763;</span>
+            <span class="em em-symbols-2763" data-c="2763" data-s=":heart_exclamation:" title="heavy heart exclamation">&#x2763;&#xfe0f;</span>
             <span class="em em-symbols-1f495" data-c="1f495" data-s=":two_hearts:" title="two hearts">&#x1f495;</span>
             <span class="em em-symbols-1f495" data-c="1f495" data-s=":two_hearts:" title="two hearts">&#x1f495;</span>
             <span class="em em-symbols-1f49e" data-c="1f49e" data-s=":revolving_hearts:" title="revolving hearts">&#x1f49e;</span>
             <span class="em em-symbols-1f49e" data-c="1f49e" data-s=":revolving_hearts:" title="revolving hearts">&#x1f49e;</span>
             <span class="em em-symbols-1f493" data-c="1f493" data-s=":heartbeat:" title="beating heart">&#x1f493;</span>
             <span class="em em-symbols-1f493" data-c="1f493" data-s=":heartbeat:" title="beating heart">&#x1f493;</span>
@@ -2051,16 +2051,16 @@
             <span class="em em-symbols-1f498" data-c="1f498" data-s=":cupid:" title="heart with arrow">&#x1f498;</span>
             <span class="em em-symbols-1f498" data-c="1f498" data-s=":cupid:" title="heart with arrow">&#x1f498;</span>
             <span class="em em-symbols-1f49d" data-c="1f49d" data-s=":gift_heart:" title="heart with ribbon">&#x1f49d;</span>
             <span class="em em-symbols-1f49d" data-c="1f49d" data-s=":gift_heart:" title="heart with ribbon">&#x1f49d;</span>
             <span class="em em-symbols-1f49f" data-c="1f49f" data-s=":heart_decoration:" title="heart decoration">&#x1f49f;</span>
             <span class="em em-symbols-1f49f" data-c="1f49f" data-s=":heart_decoration:" title="heart decoration">&#x1f49f;</span>
-            <span class="em em-symbols-262e" data-c="262e" data-s=":peace:" title="peace symbol">&#x262e;</span>
-            <span class="em em-symbols-271d" data-c="271d" data-s=":cross:" title="latin cross">&#x271d;</span>
-            <span class="em em-symbols-262a" data-c="262a" data-s=":star_and_crescent:" title="star and crescent">&#x262a;</span>
-            <span class="em em-symbols-1f549" data-c="1f549" data-s=":om_symbol:" title="om">&#x1f549;</span>
-            <span class="em em-symbols-2638" data-c="2638" data-s=":wheel_of_dharma:" title="wheel of dharma">&#x2638;</span>
-            <span class="em em-symbols-2721" data-c="2721" data-s=":star_of_david:" title="star of David">&#x2721;</span>
+            <span class="em em-symbols-262e" data-c="262e" data-s=":peace:" title="peace symbol">&#x262e;&#xfe0f;</span>
+            <span class="em em-symbols-271d" data-c="271d" data-s=":cross:" title="latin cross">&#x271d;&#xfe0f;</span>
+            <span class="em em-symbols-262a" data-c="262a" data-s=":star_and_crescent:" title="star and crescent">&#x262a;&#xfe0f;</span>
+            <span class="em em-symbols-1f549" data-c="1f549" data-s=":om_symbol:" title="om">&#x1f549;&#xfe0f;</span>
+            <span class="em em-symbols-2638" data-c="2638" data-s=":wheel_of_dharma:" title="wheel of dharma">&#x2638;&#xfe0f;</span>
+            <span class="em em-symbols-2721" data-c="2721" data-s=":star_of_david:" title="star of David">&#x2721;&#xfe0f;</span>
             <span class="em em-symbols-1f52f" data-c="1f52f" data-s=":six_pointed_star:" title="dotted six-pointed star">&#x1f52f;</span>
             <span class="em em-symbols-1f52f" data-c="1f52f" data-s=":six_pointed_star:" title="dotted six-pointed star">&#x1f52f;</span>
             <span class="em em-symbols-1f54e" data-c="1f54e" data-s=":menorah:" title="menorah">&#x1f54e;</span>
             <span class="em em-symbols-1f54e" data-c="1f54e" data-s=":menorah:" title="menorah">&#x1f54e;</span>
-            <span class="em em-symbols-262f" data-c="262f" data-s=":yin_yang:" title="yin yang">&#x262f;</span>
-            <span class="em em-symbols-2626" data-c="2626" data-s=":orthodox_cross:" title="orthodox cross">&#x2626;</span>
+            <span class="em em-symbols-262f" data-c="262f" data-s=":yin_yang:" title="yin yang">&#x262f;&#xfe0f;</span>
+            <span class="em em-symbols-2626" data-c="2626" data-s=":orthodox_cross:" title="orthodox cross">&#x2626;&#xfe0f;</span>
             <span class="em em-symbols-1f6d0" data-c="1f6d0" data-s=":place_of_worship:" title="place of worship">&#x1f6d0;</span>
             <span class="em em-symbols-1f6d0" data-c="1f6d0" data-s=":place_of_worship:" title="place of worship">&#x1f6d0;</span>
             <span class="em em-symbols-26ce" data-c="26ce" data-s=":ophiuchus:" title="Ophiuchus">&#x26ce;</span>
             <span class="em em-symbols-26ce" data-c="26ce" data-s=":ophiuchus:" title="Ophiuchus">&#x26ce;</span>
             <span class="em em-symbols-2648" data-c="2648" data-s=":aries:" title="Aries">&#x2648;</span>
             <span class="em em-symbols-2648" data-c="2648" data-s=":aries:" title="Aries">&#x2648;</span>
@@ -2076,33 +2076,33 @@
             <span class="em em-symbols-2652" data-c="2652" data-s=":aquarius:" title="Aquarius">&#x2652;</span>
             <span class="em em-symbols-2652" data-c="2652" data-s=":aquarius:" title="Aquarius">&#x2652;</span>
             <span class="em em-symbols-2653" data-c="2653" data-s=":pisces:" title="Pisces">&#x2653;</span>
             <span class="em em-symbols-2653" data-c="2653" data-s=":pisces:" title="Pisces">&#x2653;</span>
             <span class="em em-symbols-1f194" data-c="1f194" data-s=":id:" title="ID button">&#x1f194;</span>
             <span class="em em-symbols-1f194" data-c="1f194" data-s=":id:" title="ID button">&#x1f194;</span>
-            <span class="em em-symbols-269b" data-c="269b" data-s=":atom:" title="atom symbol">&#x269b;</span>
+            <span class="em em-symbols-269b" data-c="269b" data-s=":atom:" title="atom symbol">&#x269b;&#xfe0f;</span>
             <span class="em em-symbols-267e" data-c="267e" data-s=":infinity:" title="infinity">&#x267e;&#xfe0f;</span>
             <span class="em em-symbols-267e" data-c="267e" data-s=":infinity:" title="infinity">&#x267e;&#xfe0f;</span>
             <span class="em em-symbols-1f251" data-c="1f251" data-s=":accept:" title="Japanese “acceptable” button">&#x1f251;</span>
             <span class="em em-symbols-1f251" data-c="1f251" data-s=":accept:" title="Japanese “acceptable” button">&#x1f251;</span>
-            <span class="em em-symbols-2622" data-c="2622" data-s=":radioactive:" title="radioactive">&#x2622;</span>
-            <span class="em em-symbols-2623" data-c="2623" data-s=":biohazard:" title="biohazard">&#x2623;</span>
+            <span class="em em-symbols-2622" data-c="2622" data-s=":radioactive:" title="radioactive">&#x2622;&#xfe0f;</span>
+            <span class="em em-symbols-2623" data-c="2623" data-s=":biohazard:" title="biohazard">&#x2623;&#xfe0f;</span>
             <span class="em em-symbols-1f4f4" data-c="1f4f4" data-s=":mobile_phone_off:" title="mobile phone off">&#x1f4f4;</span>
             <span class="em em-symbols-1f4f4" data-c="1f4f4" data-s=":mobile_phone_off:" title="mobile phone off">&#x1f4f4;</span>
             <span class="em em-symbols-1f4f3" data-c="1f4f3" data-s=":vibration_mode:" title="vibration mode">&#x1f4f3;</span>
             <span class="em em-symbols-1f4f3" data-c="1f4f3" data-s=":vibration_mode:" title="vibration mode">&#x1f4f3;</span>
             <span class="em em-symbols-1f236" data-c="1f236" data-s=":u6709:" title="Japanese “not free of charge” button">&#x1f236;</span>
             <span class="em em-symbols-1f236" data-c="1f236" data-s=":u6709:" title="Japanese “not free of charge” button">&#x1f236;</span>
             <span class="em em-symbols-1f21a" data-c="1f21a" data-s=":u7121:" title="Japanese “free of charge” button">&#x1f21a;</span>
             <span class="em em-symbols-1f21a" data-c="1f21a" data-s=":u7121:" title="Japanese “free of charge” button">&#x1f21a;</span>
             <span class="em em-symbols-1f238" data-c="1f238" data-s=":u7533:" title="Japanese “application” button">&#x1f238;</span>
             <span class="em em-symbols-1f238" data-c="1f238" data-s=":u7533:" title="Japanese “application” button">&#x1f238;</span>
             <span class="em em-symbols-1f23a" data-c="1f23a" data-s=":u55b6:" title="Japanese “open for business” button">&#x1f23a;</span>
             <span class="em em-symbols-1f23a" data-c="1f23a" data-s=":u55b6:" title="Japanese “open for business” button">&#x1f23a;</span>
-            <span class="em em-symbols-1f237" data-c="1f237" data-s=":u6708:" title="Japanese “monthly amount” button">&#x1f237;</span>
-            <span class="em em-symbols-2734" data-c="2734" data-s=":eight_pointed_black_star:" title="eight-pointed star">&#x2734;</span>
+            <span class="em em-symbols-1f237" data-c="1f237" data-s=":u6708:" title="Japanese “monthly amount” button">&#x1f237;&#xfe0f;</span>
+            <span class="em em-symbols-2734" data-c="2734" data-s=":eight_pointed_black_star:" title="eight-pointed star">&#x2734;&#xfe0f;</span>
             <span class="em em-symbols-1f19a" data-c="1f19a" data-s=":vs:" title="VS button">&#x1f19a;</span>
             <span class="em em-symbols-1f19a" data-c="1f19a" data-s=":vs:" title="VS button">&#x1f19a;</span>
             <span class="em em-symbols-1f4ae" data-c="1f4ae" data-s=":white_flower:" title="white flower">&#x1f4ae;</span>
             <span class="em em-symbols-1f4ae" data-c="1f4ae" data-s=":white_flower:" title="white flower">&#x1f4ae;</span>
             <span class="em em-symbols-1f250" data-c="1f250" data-s=":ideograph_advantage:" title="Japanese “bargain” button">&#x1f250;</span>
             <span class="em em-symbols-1f250" data-c="1f250" data-s=":ideograph_advantage:" title="Japanese “bargain” button">&#x1f250;</span>
-            <span class="em em-symbols-3299" data-c="3299" data-s=":secret:" title="Japanese “secret” button">&#x3299;</span>
-            <span class="em em-symbols-3297" data-c="3297" data-s=":congratulations:" title="Japanese “congratulations” button">&#x3297;</span>
+            <span class="em em-symbols-3299" data-c="3299" data-s=":secret:" title="Japanese “secret” button">&#x3299;&#xfe0f;</span>
+            <span class="em em-symbols-3297" data-c="3297" data-s=":congratulations:" title="Japanese “congratulations” button">&#x3297;&#xfe0f;</span>
             <span class="em em-symbols-1f234" data-c="1f234" data-s=":u5408:" title="Japanese “passing grade” button">&#x1f234;</span>
             <span class="em em-symbols-1f234" data-c="1f234" data-s=":u5408:" title="Japanese “passing grade” button">&#x1f234;</span>
             <span class="em em-symbols-1f235" data-c="1f235" data-s=":u6e80:" title="Japanese “no vacancy” button">&#x1f235;</span>
             <span class="em em-symbols-1f235" data-c="1f235" data-s=":u6e80:" title="Japanese “no vacancy” button">&#x1f235;</span>
             <span class="em em-symbols-1f239" data-c="1f239" data-s=":u5272:" title="Japanese “discount” button">&#x1f239;</span>
             <span class="em em-symbols-1f239" data-c="1f239" data-s=":u5272:" title="Japanese “discount” button">&#x1f239;</span>
             <span class="em em-symbols-1f232" data-c="1f232" data-s=":u7981:" title="Japanese “prohibited” button">&#x1f232;</span>
             <span class="em em-symbols-1f232" data-c="1f232" data-s=":u7981:" title="Japanese “prohibited” button">&#x1f232;</span>
-            <span class="em em-symbols-1f170" data-c="1f170" data-s=":a:" title="A button (blood type)">&#x1f170;</span>
-            <span class="em em-symbols-1f171" data-c="1f171" data-s=":b:" title="B button (blood type)">&#x1f171;</span>
+            <span class="em em-symbols-1f170" data-c="1f170" data-s=":a:" title="A button (blood type)">&#x1f170;&#xfe0f;</span>
+            <span class="em em-symbols-1f171" data-c="1f171" data-s=":b:" title="B button (blood type)">&#x1f171;&#xfe0f;</span>
             <span class="em em-symbols-1f18e" data-c="1f18e" data-s=":ab:" title="AB button (blood type)">&#x1f18e;</span>
             <span class="em em-symbols-1f18e" data-c="1f18e" data-s=":ab:" title="AB button (blood type)">&#x1f18e;</span>
             <span class="em em-symbols-1f191" data-c="1f191" data-s=":cl:" title="CL button">&#x1f191;</span>
             <span class="em em-symbols-1f191" data-c="1f191" data-s=":cl:" title="CL button">&#x1f191;</span>
-            <span class="em em-symbols-1f17e" data-c="1f17e" data-s=":o2:" title="O button (blood type)">&#x1f17e;</span>
+            <span class="em em-symbols-1f17e" data-c="1f17e" data-s=":o2:" title="O button (blood type)">&#x1f17e;&#xfe0f;</span>
             <span class="em em-symbols-1f198" data-c="1f198" data-s=":sos:" title="SOS button">&#x1f198;</span>
             <span class="em em-symbols-1f198" data-c="1f198" data-s=":sos:" title="SOS button">&#x1f198;</span>
             <span class="em em-symbols-274c" data-c="274c" data-s=":x:" title="cross mark">&#x274c;</span>
             <span class="em em-symbols-274c" data-c="274c" data-s=":x:" title="cross mark">&#x274c;</span>
             <span class="em em-symbols-2b55" data-c="2b55" data-s=":o:" title="heavy large circle">&#x2b55;</span>
             <span class="em em-symbols-2b55" data-c="2b55" data-s=":o:" title="heavy large circle">&#x2b55;</span>
@@ -2112,7 +2112,7 @@
             <span class="em em-symbols-1f6ab" data-c="1f6ab" data-s=":no_entry_sign:" title="prohibited">&#x1f6ab;</span>
             <span class="em em-symbols-1f6ab" data-c="1f6ab" data-s=":no_entry_sign:" title="prohibited">&#x1f6ab;</span>
             <span class="em em-symbols-1f4af" data-c="1f4af" data-s=":100:" title="hundred points">&#x1f4af;</span>
             <span class="em em-symbols-1f4af" data-c="1f4af" data-s=":100:" title="hundred points">&#x1f4af;</span>
             <span class="em em-symbols-1f4a2" data-c="1f4a2" data-s=":anger:" title="anger symbol">&#x1f4a2;</span>
             <span class="em em-symbols-1f4a2" data-c="1f4a2" data-s=":anger:" title="anger symbol">&#x1f4a2;</span>
-            <span class="em em-symbols-2668" data-c="2668" data-s=":hotsprings:" title="hot springs">&#x2668;</span>
+            <span class="em em-symbols-2668" data-c="2668" data-s=":hotsprings:" title="hot springs">&#x2668;&#xfe0f;</span>
             <span class="em em-symbols-1f6b7" data-c="1f6b7" data-s=":no_pedestrians:" title="no pedestrians">&#x1f6b7;</span>
             <span class="em em-symbols-1f6b7" data-c="1f6b7" data-s=":no_pedestrians:" title="no pedestrians">&#x1f6b7;</span>
             <span class="em em-symbols-1f6af" data-c="1f6af" data-s=":do_not_litter:" title="no littering">&#x1f6af;</span>
             <span class="em em-symbols-1f6af" data-c="1f6af" data-s=":do_not_litter:" title="no littering">&#x1f6af;</span>
             <span class="em em-symbols-1f6b3" data-c="1f6b3" data-s=":no_bicycles:" title="no bicycles">&#x1f6b3;</span>
             <span class="em em-symbols-1f6b3" data-c="1f6b3" data-s=":no_bicycles:" title="no bicycles">&#x1f6b3;</span>
@@ -2124,34 +2124,34 @@
             <span class="em em-symbols-2755" data-c="2755" data-s=":grey_exclamation:" title="white exclamation mark">&#x2755;</span>
             <span class="em em-symbols-2755" data-c="2755" data-s=":grey_exclamation:" title="white exclamation mark">&#x2755;</span>
             <span class="em em-symbols-2753" data-c="2753" data-s=":question:" title="question mark">&#x2753;</span>
             <span class="em em-symbols-2753" data-c="2753" data-s=":question:" title="question mark">&#x2753;</span>
             <span class="em em-symbols-2754" data-c="2754" data-s=":grey_question:" title="white question mark">&#x2754;</span>
             <span class="em em-symbols-2754" data-c="2754" data-s=":grey_question:" title="white question mark">&#x2754;</span>
-            <span class="em em-symbols-203c" data-c="203c" data-s=":bangbang:" title="double exclamation mark">&#x203c;</span>
-            <span class="em em-symbols-2049" data-c="2049" data-s=":interrobang:" title="exclamation question mark">&#x2049;</span>
+            <span class="em em-symbols-203c" data-c="203c" data-s=":bangbang:" title="double exclamation mark">&#x203c;&#xfe0f;</span>
+            <span class="em em-symbols-2049" data-c="2049" data-s=":interrobang:" title="exclamation question mark">&#x2049;&#xfe0f;</span>
             <span class="em em-symbols-1f505" data-c="1f505" data-s=":low_brightness:" title="dim button">&#x1f505;</span>
             <span class="em em-symbols-1f505" data-c="1f505" data-s=":low_brightness:" title="dim button">&#x1f505;</span>
             <span class="em em-symbols-1f506" data-c="1f506" data-s=":high_brightness:" title="bright button">&#x1f506;</span>
             <span class="em em-symbols-1f506" data-c="1f506" data-s=":high_brightness:" title="bright button">&#x1f506;</span>
-            <span class="em em-symbols-303d" data-c="303d" data-s=":part_alternation_mark:" title="part alternation mark">&#x303d;</span>
-            <span class="em em-symbols-26a0" data-c="26a0" data-s=":warning:" title="warning">&#x26a0;</span>
+            <span class="em em-symbols-303d" data-c="303d" data-s=":part_alternation_mark:" title="part alternation mark">&#x303d;&#xfe0f;</span>
+            <span class="em em-symbols-26a0" data-c="26a0" data-s=":warning:" title="warning">&#x26a0;&#xfe0f;</span>
             <span class="em em-symbols-1f6b8" data-c="1f6b8" data-s=":children_crossing:" title="children crossing">&#x1f6b8;</span>
             <span class="em em-symbols-1f6b8" data-c="1f6b8" data-s=":children_crossing:" title="children crossing">&#x1f6b8;</span>
             <span class="em em-symbols-1f531" data-c="1f531" data-s=":trident:" title="trident emblem">&#x1f531;</span>
             <span class="em em-symbols-1f531" data-c="1f531" data-s=":trident:" title="trident emblem">&#x1f531;</span>
-            <span class="em em-symbols-269c" data-c="269c" data-s=":fleur-de-lis:" title="fleur-de-lis">&#x269c;</span>
+            <span class="em em-symbols-269c" data-c="269c" data-s=":fleur-de-lis:" title="fleur-de-lis">&#x269c;&#xfe0f;</span>
             <span class="em em-symbols-1f530" data-c="1f530" data-s=":beginner:" title="Japanese symbol for beginner">&#x1f530;</span>
             <span class="em em-symbols-1f530" data-c="1f530" data-s=":beginner:" title="Japanese symbol for beginner">&#x1f530;</span>
-            <span class="em em-symbols-267b" data-c="267b" data-s=":recycle:" title="recycling symbol">&#x267b;</span>
+            <span class="em em-symbols-267b" data-c="267b" data-s=":recycle:" title="recycling symbol">&#x267b;&#xfe0f;</span>
             <span class="em em-symbols-2705" data-c="2705" data-s=":white_check_mark:" title="white heavy check mark">&#x2705;</span>
             <span class="em em-symbols-2705" data-c="2705" data-s=":white_check_mark:" title="white heavy check mark">&#x2705;</span>
             <span class="em em-symbols-1f22f" data-c="1f22f" data-s=":u6307:" title="Japanese “reserved” button">&#x1f22f;</span>
             <span class="em em-symbols-1f22f" data-c="1f22f" data-s=":u6307:" title="Japanese “reserved” button">&#x1f22f;</span>
             <span class="em em-symbols-1f4b9" data-c="1f4b9" data-s=":chart:" title="chart increasing with yen">&#x1f4b9;</span>
             <span class="em em-symbols-1f4b9" data-c="1f4b9" data-s=":chart:" title="chart increasing with yen">&#x1f4b9;</span>
-            <span class="em em-symbols-2747" data-c="2747" data-s=":sparkle:" title="sparkle">&#x2747;</span>
-            <span class="em em-symbols-2733" data-c="2733" data-s=":eight_spoked_asterisk:" title="eight-spoked asterisk">&#x2733;</span>
+            <span class="em em-symbols-2747" data-c="2747" data-s=":sparkle:" title="sparkle">&#x2747;&#xfe0f;</span>
+            <span class="em em-symbols-2733" data-c="2733" data-s=":eight_spoked_asterisk:" title="eight-spoked asterisk">&#x2733;&#xfe0f;</span>
             <span class="em em-symbols-274e" data-c="274e" data-s=":negative_squared_cross_mark:" title="cross mark button">&#x274e;</span>
             <span class="em em-symbols-274e" data-c="274e" data-s=":negative_squared_cross_mark:" title="cross mark button">&#x274e;</span>
             <span class="em em-symbols-1f310" data-c="1f310" data-s=":globe_with_meridians:" title="globe with meridians">&#x1f310;</span>
             <span class="em em-symbols-1f310" data-c="1f310" data-s=":globe_with_meridians:" title="globe with meridians">&#x1f310;</span>
             <span class="em em-symbols-1f4a0" data-c="1f4a0" data-s=":diamond_shape_with_a_dot_inside:" title="diamond with a dot">&#x1f4a0;</span>
             <span class="em em-symbols-1f4a0" data-c="1f4a0" data-s=":diamond_shape_with_a_dot_inside:" title="diamond with a dot">&#x1f4a0;</span>
-            <span class="em em-symbols-24c2" data-c="24c2" data-s=":m:" title="circled M">&#x24c2;</span>
+            <span class="em em-symbols-24c2" data-c="24c2" data-s=":m:" title="circled M">&#x24c2;&#xfe0f;</span>
             <span class="em em-symbols-1f300" data-c="1f300" data-s=":cyclone:" title="cyclone">&#x1f300;</span>
             <span class="em em-symbols-1f300" data-c="1f300" data-s=":cyclone:" title="cyclone">&#x1f300;</span>
             <span class="em em-symbols-1f4a4" data-c="1f4a4" data-s=":zzz:" title="zzz">&#x1f4a4;</span>
             <span class="em em-symbols-1f4a4" data-c="1f4a4" data-s=":zzz:" title="zzz">&#x1f4a4;</span>
             <span class="em em-symbols-1f3e7" data-c="1f3e7" data-s=":atm:" title="ATM sign">&#x1f3e7;</span>
             <span class="em em-symbols-1f3e7" data-c="1f3e7" data-s=":atm:" title="ATM sign">&#x1f3e7;</span>
             <span class="em em-symbols-1f6be" data-c="1f6be" data-s=":wc:" title="water closet">&#x1f6be;</span>
             <span class="em em-symbols-1f6be" data-c="1f6be" data-s=":wc:" title="water closet">&#x1f6be;</span>
             <span class="em em-symbols-267f" data-c="267f" data-s=":wheelchair:" title="wheelchair symbol">&#x267f;</span>
             <span class="em em-symbols-267f" data-c="267f" data-s=":wheelchair:" title="wheelchair symbol">&#x267f;</span>
-            <span class="em em-symbols-1f17f" data-c="1f17f" data-s=":parking:" title="P button">&#x1f17f;</span>
+            <span class="em em-symbols-1f17f" data-c="1f17f" data-s=":parking:" title="P button">&#x1f17f;&#xfe0f;</span>
             <span class="em em-symbols-1f233" data-c="1f233" data-s=":u7a7a:" title="Japanese “vacancy” button">&#x1f233;</span>
             <span class="em em-symbols-1f233" data-c="1f233" data-s=":u7a7a:" title="Japanese “vacancy” button">&#x1f233;</span>
-            <span class="em em-symbols-1f202" data-c="1f202" data-s=":sa:" title="Japanese “service charge” button">&#x1f202;</span>
+            <span class="em em-symbols-1f202" data-c="1f202" data-s=":sa:" title="Japanese “service charge” button">&#x1f202;&#xfe0f;</span>
             <span class="em em-symbols-1f6c2" data-c="1f6c2" data-s=":passport_control:" title="passport control">&#x1f6c2;</span>
             <span class="em em-symbols-1f6c2" data-c="1f6c2" data-s=":passport_control:" title="passport control">&#x1f6c2;</span>
             <span class="em em-symbols-1f6c3" data-c="1f6c3" data-s=":customs:" title="customs">&#x1f6c3;</span>
             <span class="em em-symbols-1f6c3" data-c="1f6c3" data-s=":customs:" title="customs">&#x1f6c3;</span>
             <span class="em em-symbols-1f6c4" data-c="1f6c4" data-s=":baggage_claim:" title="baggage claim">&#x1f6c4;</span>
             <span class="em em-symbols-1f6c4" data-c="1f6c4" data-s=":baggage_claim:" title="baggage claim">&#x1f6c4;</span>
@@ -2165,7 +2165,7 @@
             <span class="em em-symbols-1f4f6" data-c="1f4f6" data-s=":signal_strength:" title="antenna bars">&#x1f4f6;</span>
             <span class="em em-symbols-1f4f6" data-c="1f4f6" data-s=":signal_strength:" title="antenna bars">&#x1f4f6;</span>
             <span class="em em-symbols-1f201" data-c="1f201" data-s=":koko:" title="Japanese “here” button">&#x1f201;</span>
             <span class="em em-symbols-1f201" data-c="1f201" data-s=":koko:" title="Japanese “here” button">&#x1f201;</span>
             <span class="em em-symbols-1f523" data-c="1f523" data-s=":symbols:" title="input symbols">&#x1f523;</span>
             <span class="em em-symbols-1f523" data-c="1f523" data-s=":symbols:" title="input symbols">&#x1f523;</span>
-            <span class="em em-symbols-2139" data-c="2139" data-s=":information_source:" title="information">&#x2139;</span>
+            <span class="em em-symbols-2139" data-c="2139" data-s=":information_source:" title="information">&#x2139;&#xfe0f;</span>
             <span class="em em-symbols-1f524" data-c="1f524" data-s=":abc:" title="input latin letters">&#x1f524;</span>
             <span class="em em-symbols-1f524" data-c="1f524" data-s=":abc:" title="input latin letters">&#x1f524;</span>
             <span class="em em-symbols-1f521" data-c="1f521" data-s=":abcd:" title="input latin lowercase">&#x1f521;</span>
             <span class="em em-symbols-1f521" data-c="1f521" data-s=":abcd:" title="input latin lowercase">&#x1f521;</span>
             <span class="em em-symbols-1f520" data-c="1f520" data-s=":capital_abcd:" title="input latin uppercase">&#x1f520;</span>
             <span class="em em-symbols-1f520" data-c="1f520" data-s=":capital_abcd:" title="input latin uppercase">&#x1f520;</span>
@@ -2189,35 +2189,35 @@
             <span class="em em-symbols-1f522" data-c="1f522" data-s=":1234:" title="input numbers">&#x1f522;</span>
             <span class="em em-symbols-1f522" data-c="1f522" data-s=":1234:" title="input numbers">&#x1f522;</span>
             <span class="em em-symbols-23-20e3" data-c="23-20e3" data-s=":hash:" title="keycap: #">&#x23;&#xfe0f;&#x20e3;</span>
             <span class="em em-symbols-23-20e3" data-c="23-20e3" data-s=":hash:" title="keycap: #">&#x23;&#xfe0f;&#x20e3;</span>
             <span class="em em-symbols-2a-20e3" data-c="2a-20e3" data-s=":asterisk:" title="keycap: *">&#x2a;&#xfe0f;&#x20e3;</span>
             <span class="em em-symbols-2a-20e3" data-c="2a-20e3" data-s=":asterisk:" title="keycap: *">&#x2a;&#xfe0f;&#x20e3;</span>
-            <span class="em em-symbols-23cf" data-c="23cf" data-s=":eject:" title="eject button">&#x23cf;</span>
-            <span class="em em-symbols-25b6" data-c="25b6" data-s=":arrow_forward:" title="play button">&#x25b6;</span>
-            <span class="em em-symbols-23f8" data-c="23f8" data-s=":pause_button:" title="pause button">&#x23f8;</span>
-            <span class="em em-symbols-23ef" data-c="23ef" data-s=":play_pause:" title="play or pause button">&#x23ef;</span>
-            <span class="em em-symbols-23f9" data-c="23f9" data-s=":stop_button:" title="stop button">&#x23f9;</span>
-            <span class="em em-symbols-23fa" data-c="23fa" data-s=":record_button:" title="record button">&#x23fa;</span>
-            <span class="em em-symbols-23ed" data-c="23ed" data-s=":track_next:" title="next track button">&#x23ed;</span>
-            <span class="em em-symbols-23ee" data-c="23ee" data-s=":track_previous:" title="last track button">&#x23ee;</span>
+            <span class="em em-symbols-23cf" data-c="23cf" data-s=":eject:" title="eject button">&#x23cf;&#xfe0f;</span>
+            <span class="em em-symbols-25b6" data-c="25b6" data-s=":arrow_forward:" title="play button">&#x25b6;&#xfe0f;</span>
+            <span class="em em-symbols-23f8" data-c="23f8" data-s=":pause_button:" title="pause button">&#x23f8;&#xfe0f;</span>
+            <span class="em em-symbols-23ef" data-c="23ef" data-s=":play_pause:" title="play or pause button">&#x23ef;&#xfe0f;</span>
+            <span class="em em-symbols-23f9" data-c="23f9" data-s=":stop_button:" title="stop button">&#x23f9;&#xfe0f;</span>
+            <span class="em em-symbols-23fa" data-c="23fa" data-s=":record_button:" title="record button">&#x23fa;&#xfe0f;</span>
+            <span class="em em-symbols-23ed" data-c="23ed" data-s=":track_next:" title="next track button">&#x23ed;&#xfe0f;</span>
+            <span class="em em-symbols-23ee" data-c="23ee" data-s=":track_previous:" title="last track button">&#x23ee;&#xfe0f;</span>
             <span class="em em-symbols-23e9" data-c="23e9" data-s=":fast_forward:" title="fast-forward button">&#x23e9;</span>
             <span class="em em-symbols-23e9" data-c="23e9" data-s=":fast_forward:" title="fast-forward button">&#x23e9;</span>
             <span class="em em-symbols-23ea" data-c="23ea" data-s=":rewind:" title="fast reverse button">&#x23ea;</span>
             <span class="em em-symbols-23ea" data-c="23ea" data-s=":rewind:" title="fast reverse button">&#x23ea;</span>
             <span class="em em-symbols-23eb" data-c="23eb" data-s=":arrow_double_up:" title="fast up button">&#x23eb;</span>
             <span class="em em-symbols-23eb" data-c="23eb" data-s=":arrow_double_up:" title="fast up button">&#x23eb;</span>
             <span class="em em-symbols-23ec" data-c="23ec" data-s=":arrow_double_down:" title="fast down button">&#x23ec;</span>
             <span class="em em-symbols-23ec" data-c="23ec" data-s=":arrow_double_down:" title="fast down button">&#x23ec;</span>
-            <span class="em em-symbols-25c0" data-c="25c0" data-s=":arrow_backward:" title="reverse button">&#x25c0;</span>
+            <span class="em em-symbols-25c0" data-c="25c0" data-s=":arrow_backward:" title="reverse button">&#x25c0;&#xfe0f;</span>
             <span class="em em-symbols-1f53c" data-c="1f53c" data-s=":arrow_up_small:" title="upwards button">&#x1f53c;</span>
             <span class="em em-symbols-1f53c" data-c="1f53c" data-s=":arrow_up_small:" title="upwards button">&#x1f53c;</span>
             <span class="em em-symbols-1f53d" data-c="1f53d" data-s=":arrow_down_small:" title="downwards button">&#x1f53d;</span>
             <span class="em em-symbols-1f53d" data-c="1f53d" data-s=":arrow_down_small:" title="downwards button">&#x1f53d;</span>
-            <span class="em em-symbols-27a1" data-c="27a1" data-s=":arrow_right:" title="right arrow">&#x27a1;</span>
-            <span class="em em-symbols-2b05" data-c="2b05" data-s=":arrow_left:" title="left arrow">&#x2b05;</span>
-            <span class="em em-symbols-2b06" data-c="2b06" data-s=":arrow_up:" title="up arrow">&#x2b06;</span>
-            <span class="em em-symbols-2b07" data-c="2b07" data-s=":arrow_down:" title="down arrow">&#x2b07;</span>
-            <span class="em em-symbols-2197" data-c="2197" data-s=":arrow_upper_right:" title="up-right arrow">&#x2197;</span>
-            <span class="em em-symbols-2198" data-c="2198" data-s=":arrow_lower_right:" title="down-right arrow">&#x2198;</span>
-            <span class="em em-symbols-2199" data-c="2199" data-s=":arrow_lower_left:" title="down-left arrow">&#x2199;</span>
-            <span class="em em-symbols-2196" data-c="2196" data-s=":arrow_upper_left:" title="up-left arrow">&#x2196;</span>
-            <span class="em em-symbols-2195" data-c="2195" data-s=":arrow_up_down:" title="up-down arrow">&#x2195;</span>
-            <span class="em em-symbols-2194" data-c="2194" data-s=":left_right_arrow:" title="left-right arrow">&#x2194;</span>
-            <span class="em em-symbols-21aa" data-c="21aa" data-s=":arrow_right_hook:" title="left arrow curving right">&#x21aa;</span>
-            <span class="em em-symbols-21a9" data-c="21a9" data-s=":leftwards_arrow_with_hook:" title="right arrow curving left">&#x21a9;</span>
-            <span class="em em-symbols-2934" data-c="2934" data-s=":arrow_heading_up:" title="right arrow curving up">&#x2934;</span>
-            <span class="em em-symbols-2935" data-c="2935" data-s=":arrow_heading_down:" title="right arrow curving down">&#x2935;</span>
+            <span class="em em-symbols-27a1" data-c="27a1" data-s=":arrow_right:" title="right arrow">&#x27a1;&#xfe0f;</span>
+            <span class="em em-symbols-2b05" data-c="2b05" data-s=":arrow_left:" title="left arrow">&#x2b05;&#xfe0f;</span>
+            <span class="em em-symbols-2b06" data-c="2b06" data-s=":arrow_up:" title="up arrow">&#x2b06;&#xfe0f;</span>
+            <span class="em em-symbols-2b07" data-c="2b07" data-s=":arrow_down:" title="down arrow">&#x2b07;&#xfe0f;</span>
+            <span class="em em-symbols-2197" data-c="2197" data-s=":arrow_upper_right:" title="up-right arrow">&#x2197;&#xfe0f;</span>
+            <span class="em em-symbols-2198" data-c="2198" data-s=":arrow_lower_right:" title="down-right arrow">&#x2198;&#xfe0f;</span>
+            <span class="em em-symbols-2199" data-c="2199" data-s=":arrow_lower_left:" title="down-left arrow">&#x2199;&#xfe0f;</span>
+            <span class="em em-symbols-2196" data-c="2196" data-s=":arrow_upper_left:" title="up-left arrow">&#x2196;&#xfe0f;</span>
+            <span class="em em-symbols-2195" data-c="2195" data-s=":arrow_up_down:" title="up-down arrow">&#x2195;&#xfe0f;</span>
+            <span class="em em-symbols-2194" data-c="2194" data-s=":left_right_arrow:" title="left-right arrow">&#x2194;&#xfe0f;</span>
+            <span class="em em-symbols-21aa" data-c="21aa" data-s=":arrow_right_hook:" title="left arrow curving right">&#x21aa;&#xfe0f;</span>
+            <span class="em em-symbols-21a9" data-c="21a9" data-s=":leftwards_arrow_with_hook:" title="right arrow curving left">&#x21a9;&#xfe0f;</span>
+            <span class="em em-symbols-2934" data-c="2934" data-s=":arrow_heading_up:" title="right arrow curving up">&#x2934;&#xfe0f;</span>
+            <span class="em em-symbols-2935" data-c="2935" data-s=":arrow_heading_down:" title="right arrow curving down">&#x2935;&#xfe0f;</span>
             <span class="em em-symbols-1f500" data-c="1f500" data-s=":twisted_rightwards_arrows:" title="shuffle tracks button">&#x1f500;</span>
             <span class="em em-symbols-1f500" data-c="1f500" data-s=":twisted_rightwards_arrows:" title="shuffle tracks button">&#x1f500;</span>
             <span class="em em-symbols-1f501" data-c="1f501" data-s=":repeat:" title="repeat button">&#x1f501;</span>
             <span class="em em-symbols-1f501" data-c="1f501" data-s=":repeat:" title="repeat button">&#x1f501;</span>
             <span class="em em-symbols-1f502" data-c="1f502" data-s=":repeat_one:" title="repeat single button">&#x1f502;</span>
             <span class="em em-symbols-1f502" data-c="1f502" data-s=":repeat_one:" title="repeat single button">&#x1f502;</span>
@@ -2228,13 +2228,13 @@
             <span class="em em-symbols-2795" data-c="2795" data-s=":heavy_plus_sign:" title="heavy plus sign">&#x2795;</span>
             <span class="em em-symbols-2795" data-c="2795" data-s=":heavy_plus_sign:" title="heavy plus sign">&#x2795;</span>
             <span class="em em-symbols-2796" data-c="2796" data-s=":heavy_minus_sign:" title="heavy minus sign">&#x2796;</span>
             <span class="em em-symbols-2796" data-c="2796" data-s=":heavy_minus_sign:" title="heavy minus sign">&#x2796;</span>
             <span class="em em-symbols-2797" data-c="2797" data-s=":heavy_division_sign:" title="heavy division sign">&#x2797;</span>
             <span class="em em-symbols-2797" data-c="2797" data-s=":heavy_division_sign:" title="heavy division sign">&#x2797;</span>
-            <span class="em em-symbols-2716" data-c="2716" data-s=":heavy_multiplication_x:" title="heavy multiplication x">&#x2716;</span>
+            <span class="em em-symbols-2716" data-c="2716" data-s=":heavy_multiplication_x:" title="heavy multiplication x">&#x2716;&#xfe0f;</span>
             <span class="em em-symbols-1f4b2" data-c="1f4b2" data-s=":heavy_dollar_sign:" title="heavy dollar sign">&#x1f4b2;</span>
             <span class="em em-symbols-1f4b2" data-c="1f4b2" data-s=":heavy_dollar_sign:" title="heavy dollar sign">&#x1f4b2;</span>
             <span class="em em-symbols-1f4b1" data-c="1f4b1" data-s=":currency_exchange:" title="currency exchange">&#x1f4b1;</span>
             <span class="em em-symbols-1f4b1" data-c="1f4b1" data-s=":currency_exchange:" title="currency exchange">&#x1f4b1;</span>
             <span class="em em-symbols-2122" data-c="2122" data-s=":tm:" title="trade mark">&#x2122;&#xfe0f;</span>
             <span class="em em-symbols-2122" data-c="2122" data-s=":tm:" title="trade mark">&#x2122;&#xfe0f;</span>
             <span class="em em-symbols-a9" data-c="a9" data-s=":copyright:" title="copyright">&#xa9;&#xfe0f;</span>
             <span class="em em-symbols-a9" data-c="a9" data-s=":copyright:" title="copyright">&#xa9;&#xfe0f;</span>
             <span class="em em-symbols-ae" data-c="ae" data-s=":registered:" title="registered">&#xae;&#xfe0f;</span>
             <span class="em em-symbols-ae" data-c="ae" data-s=":registered:" title="registered">&#xae;&#xfe0f;</span>
-            <span class="em em-symbols-3030" data-c="3030" data-s=":wavy_dash:" title="wavy dash">&#x3030;</span>
+            <span class="em em-symbols-3030" data-c="3030" data-s=":wavy_dash:" title="wavy dash">&#x3030;&#xfe0f;</span>
             <span class="em em-symbols-27b0" data-c="27b0" data-s=":curly_loop:" title="curly loop">&#x27b0;</span>
             <span class="em em-symbols-27b0" data-c="27b0" data-s=":curly_loop:" title="curly loop">&#x27b0;</span>
             <span class="em em-symbols-27bf" data-c="27bf" data-s=":loop:" title="double curly loop">&#x27bf;</span>
             <span class="em em-symbols-27bf" data-c="27bf" data-s=":loop:" title="double curly loop">&#x27bf;</span>
             <span class="em em-symbols-1f51a" data-c="1f51a" data-s=":end:" title="END arrow">&#x1f51a;</span>
             <span class="em em-symbols-1f51a" data-c="1f51a" data-s=":end:" title="END arrow">&#x1f51a;</span>
@@ -2242,8 +2242,8 @@
             <span class="em em-symbols-1f51b" data-c="1f51b" data-s=":on:" title="ON! arrow">&#x1f51b;</span>
             <span class="em em-symbols-1f51b" data-c="1f51b" data-s=":on:" title="ON! arrow">&#x1f51b;</span>
             <span class="em em-symbols-1f51d" data-c="1f51d" data-s=":top:" title="TOP arrow">&#x1f51d;</span>
             <span class="em em-symbols-1f51d" data-c="1f51d" data-s=":top:" title="TOP arrow">&#x1f51d;</span>
             <span class="em em-symbols-1f51c" data-c="1f51c" data-s=":soon:" title="SOON arrow">&#x1f51c;</span>
             <span class="em em-symbols-1f51c" data-c="1f51c" data-s=":soon:" title="SOON arrow">&#x1f51c;</span>
-            <span class="em em-symbols-2714" data-c="2714" data-s=":heavy_check_mark:" title="heavy check mark">&#x2714;</span>
-            <span class="em em-symbols-2611" data-c="2611" data-s=":ballot_box_with_check:" title="ballot box with check">&#x2611;</span>
+            <span class="em em-symbols-2714" data-c="2714" data-s=":heavy_check_mark:" title="heavy check mark">&#x2714;&#xfe0f;</span>
+            <span class="em em-symbols-2611" data-c="2611" data-s=":ballot_box_with_check:" title="ballot box with check">&#x2611;&#xfe0f;</span>
             <span class="em em-symbols-1f518" data-c="1f518" data-s=":radio_button:" title="radio button">&#x1f518;</span>
             <span class="em em-symbols-1f518" data-c="1f518" data-s=":radio_button:" title="radio button">&#x1f518;</span>
             <span class="em em-symbols-26aa" data-c="26aa" data-s=":white_circle:" title="white circle">&#x26aa;</span>
             <span class="em em-symbols-26aa" data-c="26aa" data-s=":white_circle:" title="white circle">&#x26aa;</span>
             <span class="em em-symbols-26ab" data-c="26ab" data-s=":black_circle:" title="black circle">&#x26ab;</span>
             <span class="em em-symbols-26ab" data-c="26ab" data-s=":black_circle:" title="black circle">&#x26ab;</span>
@@ -2257,12 +2257,12 @@
             <span class="em em-symbols-1f537" data-c="1f537" data-s=":large_blue_diamond:" title="large blue diamond">&#x1f537;</span>
             <span class="em em-symbols-1f537" data-c="1f537" data-s=":large_blue_diamond:" title="large blue diamond">&#x1f537;</span>
             <span class="em em-symbols-1f533" data-c="1f533" data-s=":white_square_button:" title="white square button">&#x1f533;</span>
             <span class="em em-symbols-1f533" data-c="1f533" data-s=":white_square_button:" title="white square button">&#x1f533;</span>
             <span class="em em-symbols-1f532" data-c="1f532" data-s=":black_square_button:" title="black square button">&#x1f532;</span>
             <span class="em em-symbols-1f532" data-c="1f532" data-s=":black_square_button:" title="black square button">&#x1f532;</span>
-            <span class="em em-symbols-25aa" data-c="25aa" data-s=":black_small_square:" title="black small square">&#x25aa;</span>
-            <span class="em em-symbols-25ab" data-c="25ab" data-s=":white_small_square:" title="white small square">&#x25ab;</span>
+            <span class="em em-symbols-25aa" data-c="25aa" data-s=":black_small_square:" title="black small square">&#x25aa;&#xfe0f;</span>
+            <span class="em em-symbols-25ab" data-c="25ab" data-s=":white_small_square:" title="white small square">&#x25ab;&#xfe0f;</span>
             <span class="em em-symbols-25fe" data-c="25fe" data-s=":black_medium_small_square:" title="black medium-small square">&#x25fe;</span>
             <span class="em em-symbols-25fe" data-c="25fe" data-s=":black_medium_small_square:" title="black medium-small square">&#x25fe;</span>
             <span class="em em-symbols-25fd" data-c="25fd" data-s=":white_medium_small_square:" title="white medium-small square">&#x25fd;</span>
             <span class="em em-symbols-25fd" data-c="25fd" data-s=":white_medium_small_square:" title="white medium-small square">&#x25fd;</span>
-            <span class="em em-symbols-25fc" data-c="25fc" data-s=":black_medium_square:" title="black medium square">&#x25fc;</span>
-            <span class="em em-symbols-25fb" data-c="25fb" data-s=":white_medium_square:" title="white medium square">&#x25fb;</span>
+            <span class="em em-symbols-25fc" data-c="25fc" data-s=":black_medium_square:" title="black medium square">&#x25fc;&#xfe0f;</span>
+            <span class="em em-symbols-25fb" data-c="25fb" data-s=":white_medium_square:" title="white medium square">&#x25fb;&#xfe0f;</span>
             <span class="em em-symbols-2b1b" data-c="2b1b" data-s=":black_large_square:" title="black large square">&#x2b1b;</span>
             <span class="em em-symbols-2b1b" data-c="2b1b" data-s=":black_large_square:" title="black large square">&#x2b1b;</span>
             <span class="em em-symbols-2b1c" data-c="2b1c" data-s=":white_large_square:" title="white large square">&#x2b1c;</span>
             <span class="em em-symbols-2b1c" data-c="2b1c" data-s=":white_large_square:" title="white large square">&#x2b1c;</span>
             <span class="em em-symbols-1f508" data-c="1f508" data-s=":speaker:" title="speaker low volume">&#x1f508;</span>
             <span class="em em-symbols-1f508" data-c="1f508" data-s=":speaker:" title="speaker low volume">&#x1f508;</span>
@@ -2273,15 +2273,15 @@
             <span class="em em-symbols-1f515" data-c="1f515" data-s=":no_bell:" title="bell with slash">&#x1f515;</span>
             <span class="em em-symbols-1f515" data-c="1f515" data-s=":no_bell:" title="bell with slash">&#x1f515;</span>
             <span class="em em-symbols-1f4e3" data-c="1f4e3" data-s=":mega:" title="megaphone">&#x1f4e3;</span>
             <span class="em em-symbols-1f4e3" data-c="1f4e3" data-s=":mega:" title="megaphone">&#x1f4e3;</span>
             <span class="em em-symbols-1f4e2" data-c="1f4e2" data-s=":loudspeaker:" title="loudspeaker">&#x1f4e2;</span>
             <span class="em em-symbols-1f4e2" data-c="1f4e2" data-s=":loudspeaker:" title="loudspeaker">&#x1f4e2;</span>
-            <span class="em em-symbols-1f5e8" data-c="1f5e8" data-s=":speech_left:" title="left speech bubble">&#x1f5e8;</span>
+            <span class="em em-symbols-1f5e8" data-c="1f5e8" data-s=":speech_left:" title="left speech bubble">&#x1f5e8;&#xfe0f;</span>
             <span class="em em-symbols-1f441-200d-1f5e8" data-c="1f441-200d-1f5e8" data-s=":eye_in_speech_bubble:" title="eye in speech bubble">&#x1f441;&#xfe0f;&#x200d;&#x1f5e8;&#xfe0f;</span>
             <span class="em em-symbols-1f441-200d-1f5e8" data-c="1f441-200d-1f5e8" data-s=":eye_in_speech_bubble:" title="eye in speech bubble">&#x1f441;&#xfe0f;&#x200d;&#x1f5e8;&#xfe0f;</span>
             <span class="em em-symbols-1f4ac" data-c="1f4ac" data-s=":speech_balloon:" title="speech balloon">&#x1f4ac;</span>
             <span class="em em-symbols-1f4ac" data-c="1f4ac" data-s=":speech_balloon:" title="speech balloon">&#x1f4ac;</span>
             <span class="em em-symbols-1f4ad" data-c="1f4ad" data-s=":thought_balloon:" title="thought balloon">&#x1f4ad;</span>
             <span class="em em-symbols-1f4ad" data-c="1f4ad" data-s=":thought_balloon:" title="thought balloon">&#x1f4ad;</span>
-            <span class="em em-symbols-1f5ef" data-c="1f5ef" data-s=":anger_right:" title="right anger bubble">&#x1f5ef;</span>
-            <span class="em em-symbols-2660" data-c="2660" data-s=":spades:" title="spade suit">&#x2660;</span>
-            <span class="em em-symbols-2663" data-c="2663" data-s=":clubs:" title="club suit">&#x2663;</span>
-            <span class="em em-symbols-2665" data-c="2665" data-s=":hearts:" title="heart suit">&#x2665;</span>
-            <span class="em em-symbols-2666" data-c="2666" data-s=":diamonds:" title="diamond suit">&#x2666;</span>
+            <span class="em em-symbols-1f5ef" data-c="1f5ef" data-s=":anger_right:" title="right anger bubble">&#x1f5ef;&#xfe0f;</span>
+            <span class="em em-symbols-2660" data-c="2660" data-s=":spades:" title="spade suit">&#x2660;&#xfe0f;</span>
+            <span class="em em-symbols-2663" data-c="2663" data-s=":clubs:" title="club suit">&#x2663;&#xfe0f;</span>
+            <span class="em em-symbols-2665" data-c="2665" data-s=":hearts:" title="heart suit">&#x2665;&#xfe0f;</span>
+            <span class="em em-symbols-2666" data-c="2666" data-s=":diamonds:" title="diamond suit">&#x2666;&#xfe0f;</span>
             <span class="em em-symbols-1f0cf" data-c="1f0cf" data-s=":black_joker:" title="joker">&#x1f0cf;</span>
             <span class="em em-symbols-1f0cf" data-c="1f0cf" data-s=":black_joker:" title="joker">&#x1f0cf;</span>
             <span class="em em-symbols-1f3b4" data-c="1f3b4" data-s=":flower_playing_cards:" title="flower playing cards">&#x1f3b4;</span>
             <span class="em em-symbols-1f3b4" data-c="1f3b4" data-s=":flower_playing_cards:" title="flower playing cards">&#x1f3b4;</span>
             <span class="em em-symbols-1f004" data-c="1f004" data-s=":mahjong:" title="mahjong red dragon">&#x1f004;</span>
             <span class="em em-symbols-1f004" data-c="1f004" data-s=":mahjong:" title="mahjong red dragon">&#x1f004;</span>
@@ -2317,7 +2317,7 @@
             <img src="emoji/flags.svg" class="em-flags" height="24" width="24" role="button" tabindex="0"></span>
             <img src="emoji/flags.svg" class="em-flags" height="24" width="24" role="button" tabindex="0"></span>
         </label>
         </label>
         <div class="content">
         <div class="content">
-            <span class="em em-flags-1f3f3" data-c="1f3f3" data-s=":flag_white:" title="white flag">&#x1f3f3;</span>
+            <span class="em em-flags-1f3f3" data-c="1f3f3" data-s=":flag_white:" title="white flag">&#x1f3f3;&#xfe0f;</span>
             <span class="em em-flags-1f3f4" data-c="1f3f4" data-s=":flag_black:" title="black flag">&#x1f3f4;</span>
             <span class="em em-flags-1f3f4" data-c="1f3f4" data-s=":flag_black:" title="black flag">&#x1f3f4;</span>
             <span class="em em-flags-1f3c1" data-c="1f3c1" data-s=":checkered_flag:" title="chequered flag">&#x1f3c1;</span>
             <span class="em em-flags-1f3c1" data-c="1f3c1" data-s=":checkered_flag:" title="chequered flag">&#x1f3c1;</span>
             <span class="em em-flags-1f6a9" data-c="1f6a9" data-s=":triangular_flag_on_post:" title="triangular flag">&#x1f6a9;</span>
             <span class="em em-flags-1f6a9" data-c="1f6a9" data-s=":triangular_flag_on_post:" title="triangular flag">&#x1f6a9;</span>

+ 1 - 27
src/partials/messenger.ts

@@ -735,35 +735,9 @@ class ConversationController {
      * In contrast to startTyping, this method is is always called, not just if
      * In contrast to startTyping, this method is is always called, not just if
      * the text field is non-empty.
      * the text field is non-empty.
      */
      */
-    public onTyping = (text: string, currentWord: threema.WordResult = null) => {
+    public onTyping = (text: string) => {
         // Update draft
         // Update draft
         this.webClientService.setDraft(this.receiver, text);
         this.webClientService.setDraft(this.receiver, text);
-
-        /* Make mentions readonly for now
-        if (currentWord && currentWord.substr(0, 1) === '@') {
-            this.currentMentionFilterWord = currentWord.substr(1);
-            const query = this.currentMentionFilterWord.toLowerCase().trim();
-            const selectedMentionObject = this.getSelectedMention();
-            this.currentMentions = this.allMentions.filter((i) => {
-                if (query.length === 0) {
-                    return true;
-                }
-                return i.query.indexOf(query) >= 0;
-            });
-            // If only one mention is filtered, select them
-            if (this.currentMentions.length === 1) {
-                this.selectedMention = 0;
-            } else if (selectedMentionObject !== null) {
-                // Get the new position of the latest selected mention object
-                this.selectedMention = null;
-                this.selectedMention = this.currentMentions.findIndex((m) => {
-                    return m.identity === selectedMentionObject.identity;
-                });
-            }
-        } else {
-            this.currentMentionFilterWord = null;
-        }
-        */
     }
     }
 
 
     public getSelectedMention = (): threema.Mention => {
     public getSelectedMention = (): threema.Mention => {

+ 0 - 55
src/services/string.ts

@@ -58,59 +58,4 @@ export class StringService {
         });
         });
         return chunks;
         return chunks;
     }
     }
-
-    /**
-     * Return the word below the cursor position.
-     *
-     * If the cursor is at the end of a word, the word to the left of it will
-     * be returned.
-     *
-     * If there is whitespace to the left of the cursor, then the returned
-     * `WordResult` object will include the trimmed word to the left of the
-     * cursor. The `realLength` includes the trimmed whitespace though.
-     */
-    public getWord(input: string, pos: number, additionalSeparators: string[] = null): threema.WordResult {
-        const result = {
-            word: null,
-            realLength: 0,
-        };
-        if (input !== null && input.length > 0) {
-            const chars = Array.from(input);
-            let charFound = false;
-            const realPos = Math.min(pos, chars.length) - 1;
-
-            if (realPos > 0) {
-                const wordChars = new Array(realPos);
-                for (let n = realPos; n >= 0; n--) {
-                    const realChar = chars[n].trim();
-                    if (realChar === '') {
-                        // Abort
-                        if (charFound === false) {
-                            result.realLength++;
-                            continue;
-                        } else {
-                            break;
-                        }
-                    } else if (additionalSeparators !== null) {
-                        if (additionalSeparators.indexOf(chars[n]) > -1) {
-                            // append char
-                            result.realLength++;
-                            wordChars[n] = realChar;
-                            if (charFound === false) {
-                                continue;
-                            } else {
-                                break;
-                            }
-                        }
-                    }
-                    result.realLength++;
-                    wordChars[n] = realChar;
-                    charFound = true;
-                }
-                result.word = wordChars.join('');
-            }
-
-        }
-        return result;
-    }
 }
 }

+ 16 - 14
src/threema.d.ts

@@ -27,9 +27,9 @@ declare namespace threema {
     }
     }
 
 
     interface WireMessageAcknowledgement {
     interface WireMessageAcknowledgement {
-        id: string,
-        success: boolean,
-        error?: string,
+        id: string;
+        success: boolean;
+        error?: string;
     }
     }
 
 
     /**
     /**
@@ -740,18 +740,11 @@ declare namespace threema {
         isAll: boolean;
         isAll: boolean;
     }
     }
 
 
-    interface WordResult {
-        // The trimmed word
-        word: string;
-        // The length of the untrimmed word
-        realLength: number;
-    }
-
     interface WebClientServiceStopArguments {
     interface WebClientServiceStopArguments {
-        reason: DisconnectReason,
-        send: boolean,
-        close: boolean | string,
-        connectionBuildupState?: ConnectionBuildupState,
+        reason: DisconnectReason;
+        send: boolean;
+        close: boolean | string;
+        connectionBuildupState?: ConnectionBuildupState;
     }
     }
 
 
     const enum ChosenTask {
     const enum ChosenTask {
@@ -768,6 +761,15 @@ declare namespace threema {
         SessionError = 'error',
         SessionError = 'error',
     }
     }
 
 
+    interface EmojiInfo {
+        // The plain emoji string
+        emojiString: string;
+        // The image path, e.g. emoji/png32/1f9df-200d-2640-fe0f.png
+        imgPath: string;
+        // The codepoint string, e.g. 1f9df-200d-2640-fe0f
+        codepoint: string;
+    }
+
     namespace Container {
     namespace Container {
         interface ReceiverData {
         interface ReceiverData {
             contacts: ContactReceiver[];
             contacts: ContactReceiver[];

+ 10 - 0
src/typeguards.ts

@@ -91,3 +91,13 @@ export function isTextNode(node: Node): node is Text {
 export function isElementNode(node: Node): node is HTMLElement {
 export function isElementNode(node: Node): node is HTMLElement {
     return node.nodeType === node.ELEMENT_NODE;
     return node.nodeType === node.ELEMENT_NODE;
 }
 }
+
+/**
+ * Emoji info type guard.
+ */
+export function isEmojiInfo(val: string | threema.EmojiInfo): val is threema.EmojiInfo {
+    return typeof val === 'object'
+        && val.emojiString !== undefined
+        && val.imgPath !== undefined
+        && val.codepoint !== undefined;
+}

+ 25 - 0
tests/bootstrap.ts

@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2019 Threema GmbH (https://threema.ch/).
+ *
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// tslint:disable:no-console
+
+// A dependency graph that contains any wasm must all be imported asynchronously.
+import('../src/app')
+    .then(() => console.info('Bundle loaded'))
+    .catch((e) => console.error('Could not load bundle', e));

+ 0 - 4
tests/filters.js

@@ -1,7 +1,3 @@
-afterEach(function () {
-    jasmine.clock().uninstall();
-});
-
 describe('Filters', function() {
 describe('Filters', function() {
 
 
     let $filter;
     let $filter;

+ 5 - 0
tests/init.js

@@ -0,0 +1,5 @@
+// Wait for main application to be fully loaded
+beforeAll((done) => setTimeout(done, 1000));
+
+// Uninstall the mock clock after every test
+afterEach(() => jasmine.clock().uninstall());

+ 0 - 87
tests/service/string.js

@@ -16,93 +16,6 @@ describe('StringService', function() {
 
 
     });
     });
 
 
-    describe('getWord', function () {
-
-        it('parse null string', () => {
-            expect($service.getWord(null, 1)).toEqual(jasmine.objectContaining({
-                word: null,
-                realLength: 0
-            }));
-        });
-
-        it('parse empty string', () => {
-            expect($service.getWord('', 1)).toEqual(jasmine.objectContaining({
-                word: null,
-                realLength: 0
-            }));
-        });
-
-        it('parse string (spaces)', () => {
-            expect($service.getWord('When the man comes around.', 12)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 3
-            }));
-            expect($service.getWord('When the man comes around.', 13)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 4
-            }));
-            expect($service.getWord('When the man        comes around.', 16)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 7
-            }));
-        });
-
-        it('parse string (newline)', () => {
-            expect($service.getWord("When\nthe\nman\ncomes\naround.", 12)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 3
-            }));
-            expect($service.getWord("When\nthe\nman\ncomes\naround.", 13)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 4
-            }));
-            expect($service.getWord("When\nthe\nman\n\n\n\n\n\n\n\ncomes\naround.", 16)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 7
-            }));
-        });
-
-        it('parse string (newline/spaces)', () => {
-            expect($service.getWord("When the\nman comes around.", 12)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 3
-            }));
-            expect($service.getWord("When the\nman \ncomes around.", 13)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 4
-            }));
-            expect($service.getWord("When the\nman \n \n \n \ncomes around.", 16)).toEqual(jasmine.objectContaining({
-                word: 'man',
-                realLength: 7
-            }));
-        });
-
-        it('parse string (special character)', () => {
-            expect($service.getWord('When the :man: comes around.', 15)).toEqual(jasmine.objectContaining({
-                word: ':man:',
-                realLength: 6
-            }));
-            expect($service.getWord('When the :man: comes around.', 14)).toEqual(jasmine.objectContaining({
-                word: ':man:',
-                realLength: 5
-            }));
-        });
-
-        it('parse string (with emoji (2 chars))', () => {
-            expect($service.getWord('this 😄 is a :smile: face', 19)).toEqual(jasmine.objectContaining({
-                word: ':smile:',
-                realLength: 7
-            }));
-        });
-
-        it('parse string (additional separators)', () => {
-            expect($service.getWord('When the spider:man: comes around.', 20, [':'])).toEqual(jasmine.objectContaining({
-                word: ':man:',
-                realLength: 5
-            }));
-        });
-    });
-
     describe('byteChunkSplit', function() {
     describe('byteChunkSplit', function() {
         this.testPatterns = (cases, size, offset) => {
         this.testPatterns = (cases, size, offset) => {
             for (let testcase of cases) {
             for (let testcase of cases) {

+ 17 - 7
tests/testsuite.html

@@ -7,22 +7,32 @@
 
 
         <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
         <link rel="stylesheet" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
 
 
+        <!-- Jasmine -->
         <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
         <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
         <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
         <script src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
         <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
         <script src="../node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
 
 
+        <!-- Angular core -->
         <script src="../node_modules/angular/angular.js"></script>
         <script src="../node_modules/angular/angular.js"></script>
-        <script src="../node_modules/angular-mocks/angular-mocks.js"></script>
-        <script src="../node_modules/angular-translate/dist/angular-translate.min.js"></script>
-        <script src="../node_modules/angular-material/angular-material.min.js"></script>
-        <script src="../node_modules/angular-animate/angular-animate.min.js"></script>
         <script src="../node_modules/angular-aria/angular-aria.min.js"></script>
         <script src="../node_modules/angular-aria/angular-aria.min.js"></script>
+        <script src="../node_modules/angular-animate/angular-animate.min.js"></script>
+        <script src="../node_modules/angular-sanitize/angular-sanitize.min.js"></script>
+        <script src="../node_modules/angular-route/angular-route.min.js"></script>
+        <script src="../node_modules/angular-material/angular-material.min.js"></script>
+        <script src="../node_modules/angular-translate/dist/angular-translate.min.js"></script>
+
+        <!-- Angular mocking -->
+        <script src="../node_modules/angular-mocks/angular-mocks.js"></script>
 
 
+        <!-- SaltyRTC -->
         <script src="../node_modules/@saltyrtc/chunked-dc/dist/chunked-dc.es5.js"></script>
         <script src="../node_modules/@saltyrtc/chunked-dc/dist/chunked-dc.es5.js"></script>
 
 
-        <script src="../dist/app.bundle.js"></script>
-        <script src="../dist/unittests.bundle.js"></script>
+        <!-- App bundles -->
+        <script src="../dist/generated/app_noinit.bundle.js"></script>
+        <script src="../dist/generated/unittest.bundle.js"></script>
 
 
+        <!-- Tests -->
+        <script src="init.js"></script>
         <script src="filters.js"></script>
         <script src="filters.js"></script>
         <script src="service/message.js"></script>
         <script src="service/message.js"></script>
         <script src="service/mime.js"></script>
         <script src="service/mime.js"></script>
@@ -33,7 +43,7 @@
         <script src="service/browser.js"></script>
         <script src="service/browser.js"></script>
         <script src="service/keystore.js"></script>
         <script src="service/keystore.js"></script>
         <script src="service/notification.js"></script>
         <script src="service/notification.js"></script>
-        <script src="helpers.js"></script>
+        <script src="service/receiver.js"></script>
     </head>
     </head>
     <body>
     <body>
     <script>
     <script>

+ 25 - 0
tests/ts/bootstrap.ts

@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2019 Threema GmbH (https://threema.ch/).
+ *
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// tslint:disable:no-console
+
+// A dependency graph that contains any wasm must all be imported asynchronously.
+import('./main')
+    .then(() => console.info('Tests bootstrapped'))
+    .catch((e) => console.error('Could not bootstrap tests', e));

+ 82 - 4
tests/ts/emoji_helpers.ts

@@ -17,7 +17,30 @@
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-import {emojify, enlargeSingleEmoji, shortnameToUnicode} from '../../src/helpers/emoji';
+import twemoji from 'twemoji';
+import {emojify, emojifyNew, enlargeSingleEmoji, shortnameToUnicode} from '../../src/helpers/emoji';
+
+
+const textVariantSelector = '\ufe0e';
+const emojiVariantSelector = '\ufe0f';
+
+const beer = '\ud83c\udf7b';
+const bird = '\ud83d\udc26';
+
+function makeEmoji(emojiString: string, codepoint?: string, imgCodepoint?: string): threema.EmojiInfo {
+    if (codepoint === undefined) {
+        codepoint = twemoji.convert.toCodePoint(emojiString);
+    }
+    const imgPath = imgCodepoint === undefined
+        ? `emoji/png32/${codepoint}.png`
+        : `emoji/png32/${imgCodepoint}.png`;
+    return {
+        emojiString: emojiString,
+        imgPath: imgPath,
+        codepoint: codepoint,
+    }
+}
+
 
 
 describe('Emoji Helpers', () => {
 describe('Emoji Helpers', () => {
     describe('emojify', () => {
     describe('emojify', () => {
@@ -34,11 +57,66 @@ describe('Emoji Helpers', () => {
         });
         });
     });
     });
 
 
+    describe('emojifyNew', () => {
+        it('returns text unmodified', function() {
+            expect(emojifyNew('hello world')).toEqual(['hello world']);
+        });
+
+        it('emojifies single emoji', function() {
+            expect(emojifyNew(bird))
+                .toEqual([makeEmoji(bird)]);
+        });
+
+        it('emojifies multiple emoji', function() {
+            expect(emojifyNew(`${beer}${bird}`))
+                .toEqual([makeEmoji(beer), makeEmoji(bird)]);
+        });
+
+        it('emojifies mixed content', function() {
+            expect(emojifyNew(`hi ${bird}`))
+                .toEqual(['hi ', makeEmoji(bird)]);
+            expect(emojifyNew(`${bird} bird`))
+                .toEqual([makeEmoji(bird), ' bird']);
+            expect(emojifyNew(`hi ${bird} bird`))
+                .toEqual(['hi ', makeEmoji(bird), ' bird']);
+            expect(emojifyNew(`hi ${bird}${beer}`))
+                .toEqual(['hi ', makeEmoji(bird), makeEmoji(beer)]);
+        });
+
+        it('ignores certain codepoints', function() {
+            expect(emojifyNew('©')).toEqual(['©']);
+            expect(emojifyNew('®')).toEqual(['®']);
+            expect(emojifyNew('™')).toEqual(['™']);
+        });
+
+        it('properly handles variant selectors (text-default)', function() {
+            // Copyright: Text-default
+            const copy = '©';
+            expect(emojifyNew(copy))
+                .toEqual([copy]);
+            expect(emojifyNew(copy + textVariantSelector))
+                .toEqual([copy + textVariantSelector]);
+            expect(emojifyNew(copy + emojiVariantSelector))
+                .toEqual([makeEmoji(copy + emojiVariantSelector, 'a9-fe0f', 'a9')]);
+        });
+
+        it('properly handles variant selectors (emoji-default)', function() {
+            // Exclamation mark: Emoji-default
+            const exclamation = '\u2757';
+            expect(emojifyNew(exclamation))
+                .toEqual([makeEmoji(exclamation, '2757', '2757')]);
+            expect(emojifyNew(exclamation + textVariantSelector))
+                .toEqual([exclamation + textVariantSelector]);
+            expect(emojifyNew(exclamation + emojiVariantSelector))
+                .toEqual([makeEmoji(exclamation + emojiVariantSelector, '2757', '2757')]);
+        });
+    });
+
     describe('shortnameToUnicode', () => {
     describe('shortnameToUnicode', () => {
         it('converts valid shortnames', function() {
         it('converts valid shortnames', function() {
-            expect(shortnameToUnicode('+1')).toEqual('\ud83d\udc4d');
-            expect(shortnameToUnicode('thumbup')).toEqual('\ud83d\udc4d');
-            expect(shortnameToUnicode('thumbsup')).toEqual('\ud83d\udc4d');
+            expect(shortnameToUnicode('+1')).toEqual('\ud83d\udc4d\ufe0f');
+            expect(shortnameToUnicode('thumbup')).toEqual('\ud83d\udc4d\ufe0f');
+            expect(shortnameToUnicode('thumbsup')).toEqual('\ud83d\udc4d\ufe0f');
         });
         });
 
 
         it('returns null for unknown shortcodes', function() {
         it('returns null for unknown shortcodes', function() {

+ 25 - 0
tests/ui/bootstrap.ts

@@ -0,0 +1,25 @@
+/**
+ * Copyright © 2016-2019 Threema GmbH (https://threema.ch/).
+ *
+ * This file is part of Threema Web.
+ *
+ * Threema Web is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// tslint:disable:no-console
+
+// A dependency graph that contains any wasm must all be imported asynchronously.
+import('./main')
+    .then(() => console.info('Tests bootstrapped'))
+    .catch((e) => console.error('Could not bootstrap tests', e));

+ 13 - 3
tests/ui/compose_area.html

@@ -23,12 +23,22 @@
     <link rel="stylesheet" href="../../public/css/app.css?v=[[VERSION]]">
     <link rel="stylesheet" href="../../public/css/app.css?v=[[VERSION]]">
 
 
     <!-- Scripts -->
     <!-- Scripts -->
-    <script src="../../dist/uitest.bundle.js"></script>
-    <script>window.uiTests.initComposeArea();</script>
+    <script src="../../dist/generated/uitest.bundle.js"></script>
+    <script>
+        function init() {
+            if (window.uiTests === undefined) {
+                window.setTimeout(init, 100);
+            } else {
+                window.uiTests.initComposeArea();
+            }
+        }
+        init();
+    </script>
 </head>
 </head>
-<body ng-app="uitest">
+<body>
     <div ng-controller="ComposeAreaController as ctrl">
     <div ng-controller="ComposeAreaController as ctrl">
         <compose-area
         <compose-area
+            on-init="ctrl.onInit"
             submit="ctrl.submit"
             submit="ctrl.submit"
             initial-data="ctrl.initialData"
             initial-data="ctrl.initialData"
             start-typing="ctrl.startTyping"
             start-typing="ctrl.startTyping"

+ 7 - 0
tests/ui/compose_area.ts

@@ -58,6 +58,8 @@ export function init() {
         }]);
         }]);
     }]);
     }]);
 
 
+    // Bootstrap application
+    angular.bootstrap(document, ['uitest']);
 }
 }
 
 
 class ComposeAreaController {
 class ComposeAreaController {
@@ -72,6 +74,11 @@ class ComposeAreaController {
         };
         };
     }
     }
 
 
+    public onInit(composeArea) {
+        // tslint:disable-next-line:no-string-literal
+        window['composeArea'] = composeArea;
+    }
+
     public startTyping() {
     public startTyping() {
         // ignore
         // ignore
     }
     }

+ 28 - 15
tests/ui/run.ts

@@ -14,8 +14,6 @@ import { expect } from 'chai';
 import { Builder, By, Key, until, WebDriver, WebElement } from 'selenium-webdriver';
 import { Builder, By, Key, until, WebDriver, WebElement } from 'selenium-webdriver';
 import * as TermColor from 'term-color';
 import * as TermColor from 'term-color';
 
 
-import { extractText as extractTextFunc } from '../../src/helpers';
-
 // Script arguments
 // Script arguments
 const browser = process.argv[2];
 const browser = process.argv[2];
 const filterQuery = process.argv[3];
 const filterQuery = process.argv[3];
@@ -24,7 +22,7 @@ const filterQuery = process.argv[3];
 type Testfunc = (driver: WebDriver) => void;
 type Testfunc = (driver: WebDriver) => void;
 
 
 // Shared selectors
 // Shared selectors
-const composeArea = By.css('div.compose');
+const composeArea = By.id('composeDiv');
 const emojiKeyboard = By.css('.emoji-keyboard');
 const emojiKeyboard = By.css('.emoji-keyboard');
 const emojiTrigger = By.css('.emoji-trigger');
 const emojiTrigger = By.css('.emoji-trigger');
 
 
@@ -32,12 +30,22 @@ const emojiTrigger = By.css('.emoji-trigger');
  * Helper function to extract text.
  * Helper function to extract text.
  */
  */
 async function extractText(driver: WebDriver): Promise<string> {
 async function extractText(driver: WebDriver): Promise<string> {
+    const script = `return window.composeArea.get_text();`;
+    return driver.executeScript<string>(script);
+}
+
+/**
+ * Helper function to send a KeyDown event.
+ */
+async function sendKeyDown(driver: WebDriver, key: string): Promise<void> {
     const script = `
     const script = `
-        ${extractTextFunc.toString()}
+        const e = document.createEvent('HTMLEvents');
+        e.initEvent('keydown', false, true);
+        e.key = '${key}';
         const element = document.querySelector("div.compose");
         const element = document.querySelector("div.compose");
-        return extractText(element);
+        element.dispatchEvent(e);
     `;
     `;
-    return driver.executeScript<string>(script);
+    return driver.executeScript<void>(script);
 }
 }
 
 
 /**
 /**
@@ -57,24 +65,24 @@ async function sendKeyUp(driver: WebDriver, key: string): Promise<void> {
 /**
 /**
  * The emoji trigger should toggle the emoji keyboard.
  * The emoji trigger should toggle the emoji keyboard.
  */
  */
-async function showEmojiSelector(driver: WebDriver) {
+async function buttonTogglesEmojiSelector(driver: WebDriver) {
     // Initially not visible
     // Initially not visible
     expect(
     expect(
-        await driver.findElement(emojiKeyboard).isDisplayed()
+        await driver.findElement(emojiKeyboard).isDisplayed(),
     ).to.be.false;
     ).to.be.false;
 
 
     // Show
     // Show
     await driver.findElement(emojiTrigger).click();
     await driver.findElement(emojiTrigger).click();
 
 
     expect(
     expect(
-        await driver.findElement(emojiKeyboard).isDisplayed()
+        await driver.findElement(emojiKeyboard).isDisplayed(),
     ).to.be.true;
     ).to.be.true;
 
 
     // Hide
     // Hide
     await driver.findElement(emojiTrigger).click();
     await driver.findElement(emojiTrigger).click();
 
 
     expect(
     expect(
-        await driver.findElement(emojiKeyboard).isDisplayed()
+        await driver.findElement(emojiKeyboard).isDisplayed(),
     ).to.be.false;
     ).to.be.false;
 }
 }
 
 
@@ -93,7 +101,8 @@ async function insertEmoji(driver: WebDriver) {
 
 
     // Insert beer
     // Insert beer
     await driver.findElement(By.className('em-food')).click();
     await driver.findElement(By.className('em-food')).click();
-    await driver.findElement(By.css('.em[data-s=":beers:"]')).click();
+    const elem = await driver.findElement(By.css('.em[data-s=":beers:"]'));
+    await elem.click();
 
 
     // Validate emoji
     // Validate emoji
     const emoji = await driver.findElement(composeArea).findElements(By.xpath('*'));
     const emoji = await driver.findElement(composeArea).findElements(By.xpath('*'));
@@ -193,16 +202,16 @@ async function regression672(driver: WebDriver) {
 async function insertEmojiWithShortcode(driver: WebDriver) {
 async function insertEmojiWithShortcode(driver: WebDriver) {
     // Insert text
     // Insert text
     await driver.findElement(composeArea).click();
     await driver.findElement(composeArea).click();
-    await driver.findElement(composeArea).sendKeys('hello :+1:');
-    await sendKeyUp(driver, ':');
+    await driver.findElement(composeArea).sendKeys('hello :+1');
+    await sendKeyDown(driver, ':');
 
 
     const text = await extractText(driver);
     const text = await extractText(driver);
-    expect(text).to.equal('hello 👍');
+    expect(text).to.equal('hello 👍');
 }
 }
 
 
 // Register tests here
 // Register tests here
 const TESTS: Array<[string, Testfunc]> = [
 const TESTS: Array<[string, Testfunc]> = [
-    ['Show and hide emoji selector', showEmojiSelector],
+    ['Show and hide emoji selector', buttonTogglesEmojiSelector],
     ['Insert emoji and text', insertEmoji],
     ['Insert emoji and text', insertEmoji],
     ['Insert three lines of text', insertNewline],
     ['Insert three lines of text', insertNewline],
     ['Regression test #574', regression574],
     ['Regression test #574', regression574],
@@ -215,6 +224,7 @@ const TESTS: Array<[string, Testfunc]> = [
 const TEST_URL = 'http://localhost:7777/tests/ui/compose_area.html';
 const TEST_URL = 'http://localhost:7777/tests/ui/compose_area.html';
 (async function() {
 (async function() {
     const driver: WebDriver = await new Builder().forBrowser(browser).build();
     const driver: WebDriver = await new Builder().forBrowser(browser).build();
+    driver.manage().setTimeouts({implicit: 1000, pageLoad: 30000, script: 30000});
     let i = 0;
     let i = 0;
     let success = 0;
     let success = 0;
     let failed = 0;
     let failed = 0;
@@ -224,6 +234,9 @@ const TEST_URL = 'http://localhost:7777/tests/ui/compose_area.html';
         console.info(`Filter query: "${filterQuery}"\n`);
         console.info(`Filter query: "${filterQuery}"\n`);
     }
     }
     try {
     try {
+        // Initial pageload to ensure bundles are generated
+        await driver.get(TEST_URL);
+
         for (const [name, testfunc] of TESTS) {
         for (const [name, testfunc] of TESTS) {
             try {
             try {
                 if (filterQuery === undefined || name.toLowerCase().indexOf(filterQuery.toLowerCase()) !== -1) {
                 if (filterQuery === undefined || name.toLowerCase().indexOf(filterQuery.toLowerCase()) !== -1) {

+ 5 - 1
tools/twemoji/generate-emoji-picker.py

@@ -42,12 +42,16 @@ for i, category in enumerate(category_order):
     print('        </label>')
     print('        </label>')
     print('        <div class="content">')
     print('        <div class="content">')
     for emoji in groups[category['id']]:
     for emoji in groups[category['id']]:
+        if emoji['representation'] == 'emoji-default':
+            hex_codepoint = emoji['codepoint'].lower()
+        else:
+            hex_codepoint = emoji['codepoint_fully_qualified']
         print('            <span class="em em-{3}-{0}" data-c="{0}" data-s="{1}" title="{2}">{4}</span>'.format(
         print('            <span class="em em-{3}-{0}" data-c="{0}" data-s="{1}" title="{2}">{4}</span>'.format(
             emoji['codepoint'].lower(),
             emoji['codepoint'].lower(),
             emoji['shortname'],
             emoji['shortname'],
             emoji['name'],
             emoji['name'],
             category['id'],
             category['id'],
-            make_hexchar(emoji['codepoint_fully_qualified']),
+            make_hexchar(hex_codepoint),
         ))
         ))
     print('        </div>')
     print('        </div>')
     print('   </div>')
     print('   </div>')

+ 7 - 5
tools/twemoji/generate-shortname-mapping.py

@@ -6,12 +6,14 @@ GROUPS_JSON = '../../../twemoji-picker/generated/groups.json'
 with open(GROUPS_JSON, 'r') as f:
 with open(GROUPS_JSON, 'r') as f:
     groups = json.loads(f.read())
     groups = json.loads(f.read())
 
 
-print('const shortnames = {')
+mappings = []
+
 for emoji_list in groups.values():
 for emoji_list in groups.values():
     for emoji in emoji_list:
     for emoji in emoji_list:
         for shortname in emoji['shortnames']:
         for shortname in emoji['shortnames']:
-            print("    '{}': '{}',".format(
-                shortname.strip(':'),
-                emoji['codepoint_fully_qualified']
-            ))
+            mappings.append((shortname.strip(':'), emoji['codepoint_fully_qualified']))
+
+print('const shortnames = {')
+for (k, v) in sorted(mappings):
+    print("    '{}': '{}',".format(k, v))
 print('}')
 print('}')

+ 1 - 1
tsconfig.json

@@ -1,7 +1,7 @@
 {
 {
     "compilerOptions": {
     "compilerOptions": {
         "target": "ES2015",
         "target": "ES2015",
-        "module": "es2015",
+        "module": "esNext",
         "moduleResolution": "node",
         "moduleResolution": "node",
         "removeComments": true
         "removeComments": true
     },
     },

+ 5 - 3
webpack.common.js

@@ -23,12 +23,13 @@ const babelOptions = {
     ['@babel/plugin-transform-runtime', {
     ['@babel/plugin-transform-runtime', {
       regenerator: true,
       regenerator: true,
     }],
     }],
+    ['@babel/plugin-syntax-dynamic-import'],
   ],
   ],
 };
 };
 
 
 module.exports = {
 module.exports = {
   entry: {
   entry: {
-    app: './src/app.ts',
+    app: './src/bootstrap.ts',
   },
   },
   module: {
   module: {
     rules: [
     rules: [
@@ -50,10 +51,11 @@ module.exports = {
     ],
     ],
   },
   },
   resolve: {
   resolve: {
-    extensions: ['.js', '.ts'],
+    extensions: ['.js', '.ts', '.wasm'],
   },
   },
   output: {
   output: {
-    path: path.resolve(__dirname, 'dist'),
+    path: path.resolve(__dirname, 'dist', 'generated'),
     filename: '[name].bundle.js',
     filename: '[name].bundle.js',
+    chunkFilename: '[name].[chunkhash].bundle.js',
   },
   },
 };
 };

+ 1 - 1
webpack.dev.js

@@ -11,8 +11,8 @@ module.exports = merge(common, {
       path.join(__dirname, 'public'),
       path.join(__dirname, 'public'),
       path.join(__dirname, 'src'),
       path.join(__dirname, 'src'),
     ],
     ],
-    publicPath: '/dist/',
     compress: true,
     compress: true,
+    host: '127.0.0.1',
     port: 9966,
     port: 9966,
   },
   },
 });
 });

+ 1 - 10
webpack.prod.js

@@ -24,16 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
 module.exports = merge(common, {
 module.exports = merge(common, {
   mode: 'production',
   mode: 'production',
   devtool: 'source-map',
   devtool: 'source-map',
-  performance: {
-    hints: 'warning'
-  },
-  output: {
-    pathinfo: false
-  },
   plugins: [
   plugins: [
-    new webpack.DefinePlugin({"process.env.NODE_ENV": JSON.stringify("production")}),
-    new webpack.optimize.ModuleConcatenationPlugin(),
-    new webpack.NoEmitOnErrorsPlugin(),
-    new webpack.BannerPlugin({banner: banner}),
+    new webpack.BannerPlugin({banner: banner, entryOnly: true}),
   ],
   ],
 });
 });

+ 12 - 7
webpack.tests.js

@@ -2,11 +2,16 @@ const dev = require('./webpack.dev.js');
 const merge = require('webpack-merge');
 const merge = require('webpack-merge');
 
 
 module.exports = merge(dev, {
 module.exports = merge(dev, {
-    entry: {
-        unittest: './tests/ts/main.ts',
-        uitest: './tests/ui/main.ts',
-    },
-    devServer: {
-        port: 7777,
-    },
+  entry: {
+    unittest: './tests/ts/bootstrap.ts',
+    uitest: './tests/ui/bootstrap.ts',
+    app_noinit: './tests/bootstrap.ts',
+    unittest_karma: './tests/ts/main.ts',
+  },
+  devServer: {
+    port: 7777,
+  },
+  output: {
+    publicPath: '/dist/generated/',
+  },
 });
 });

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini