1
0
B Thibault 8 жил өмнө
parent
commit
2f226c234b
58 өөрчлөгдсөн 3668 нэмэгдсэн , 1410 устгасан
  1. 2 1
      .gitignore
  2. 3 0
      .gitmodules
  3. 79 25
      Makefile
  4. BIN
      cli/closure-compiler-v20170521.jar
  5. 32 0
      cli/commands/core.js
  6. 37 0
      cli/commands/sherlock.js
  7. 7 7
      cli/confirmDialog.js
  8. 86 74
      cli/contextBackground.js
  9. 92 31
      cli/data.js
  10. 64 79
      cli/dom.js
  11. 33 0
      cli/emoji/emojione_v2.3/_makeemojiData.js
  12. 0 0
      cli/emoji/emojione_v2.3/emoji.json
  13. 49 0
      cli/emoji/emojione_v2.3/emojione.js
  14. 37 0
      cli/emoji/emojione_v3/_makeemojiData.js
  15. 0 0
      cli/emoji/emojione_v3/emoji.json
  16. 48 0
      cli/emoji/emojione_v3/emojione.js
  17. 108 84
      cli/emojiBar.js
  18. 41 0
      cli/externs/hljs.js
  19. 10 4
      cli/lang/core.js
  20. 30 24
      cli/lang/en.js
  21. 29 24
      cli/lang/fr.js
  22. 1 0
      cli/msgFormatter
  23. 0 163
      cli/msgFormatter.js
  24. 127 0
      cli/osmTile.js
  25. 155 147
      cli/resources.js
  26. 216 206
      cli/ui.js
  27. 434 0
      cli/uiMessage.js
  28. 83 58
      cli/workflow.js
  29. 0 0
      srv/public/emojione.sprites.css
  30. 0 4
      srv/public/emojione.sprites.js
  31. 0 0
      srv/public/emojione_v2.3.sprites.css
  32. 267 0
      srv/public/emojione_v2.3.sprites.js
  33. 0 0
      srv/public/emojione_v2.3.sprites.png
  34. BIN
      srv/public/emojione_v3-sprite-64-activity.png
  35. BIN
      srv/public/emojione_v3-sprite-64-flags.png
  36. BIN
      srv/public/emojione_v3-sprite-64-food.png
  37. BIN
      srv/public/emojione_v3-sprite-64-nature.png
  38. BIN
      srv/public/emojione_v3-sprite-64-objects.png
  39. BIN
      srv/public/emojione_v3-sprite-64-people.png
  40. BIN
      srv/public/emojione_v3-sprite-64-symbols.png
  41. BIN
      srv/public/emojione_v3-sprite-64-travel.png
  42. 0 0
      srv/public/emojione_v3.sprites.css
  43. 560 0
      srv/public/emojione_v3.sprites.js
  44. 1 0
      srv/public/highlight.pack.js
  45. 1 0
      srv/public/hljs-androidstudio.css
  46. 103 78
      srv/public/slack.min.js
  47. 19 8
      srv/public/style.css
  48. 3 1
      srv/src/accounts.js
  49. 9 10
      srv/src/chatter.js
  50. 67 33
      srv/src/context.js
  51. 122 71
      srv/src/httpServ.js
  52. 12 5
      srv/src/message.js
  53. 293 0
      srv/src/multichatManager.js
  54. 7 6
      srv/src/room.js
  55. 243 126
      srv/src/slack.js
  56. 115 62
      srv/src/slackData.js
  57. 36 79
      srv/src/slackHistory.js
  58. 7 0
      srv/src/slackManager.js

+ 2 - 1
.gitignore

@@ -1,6 +1,7 @@
 *.swp
 *.bak
-cli/closure-compiler-v*.jar
+cli/emoji/emojione_v2.3/data.js
+cli/emoji/emojione_v3/data.js
 /srv/config.js
 /srv/database.sqlite
 /srv/sessions.json

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "cli/msgFormatter"]
+	path = cli/msgFormatter
+	url = git.knacki.info:isundil/msgFormatter

+ 79 - 25
Makefile

@@ -1,44 +1,98 @@
 
-SRC=		srv/src/context.js		\
-			srv/src/room.js			\
-			srv/src/message.js		\
-			srv/src/chatter.js		\
+SRC=		srv/src/context.js			\
+			srv/src/room.js				\
+			srv/src/message.js			\
+			srv/src/chatter.js			\
+			srv/src/multichatManager.js	\
 			\
-			cli/lang/core.js		\
-			cli/lang/fr.js			\
-			cli/lang/en.js			\
+			cli/lang/core.js			\
+			cli/lang/fr.js				\
+			cli/lang/en.js				\
 			\
-			cli/confirmDialog.js	\
-			cli/resources.js		\
-			cli/msgFormatter.js		\
-			cli/ui.js				\
-			cli/dom.js				\
-			cli/emojiBar.js			\
-			cli/data.js				\
-			cli/contextBackground.js\
-			cli/workflow.js			\
-			cli/utils.js
+			cli/msgFormatter/msgFormatter.js	\
+			\
+			cli/confirmDialog.js		\
+			cli/resources.js			\
+			cli/ui.js					\
+			cli/osmTile.js				\
+			cli/dom.js					\
+			cli/emojiBar.js				\
+			cli/data.js					\
+			cli/contextBackground.js	\
+			cli/workflow.js				\
+			cli/uiMessage.js			\
+			cli/utils.js				\
+			\
+			cli/commands/core.js		\
+			cli/commands/sherlock.js
+
+EXTERNS=	--externs cli/externs/hljs.js
 
 OUTPUT=		srv/public/slack.min.js
 
-CLOSURE=	cli/closure-compiler-v20170218.jar
+SRC_EMOJIONE_2.3=	cli/emoji/emojione_v2.3/data.js		\
+					cli/emoji/emojione_v2.3/emojione.js
+
+OUTPUT_EMOJIONE_2.3= srv/public/emojione_v2.3.sprites.js
+
+SRC_EMOJIONE_3=	cli/emoji/emojione_v3/data.js		\
+					cli/emoji/emojione_v3/emojione.js
+
+OUTPUT_EMOJIONE_3= srv/public/emojione_v3.sprites.js
 
-all:	core
+CLOSURE=	cli/closure-compiler-v20170521.jar
+
+JSHINT=		jshint
+
+all:	core emojione2.3 emojione3
 
 core:
-	java -jar ${CLOSURE} --compilation_level ADVANCED --language_in=ECMASCRIPT5_STRICT --warning_level=VERBOSE --js_output_file ${OUTPUT} ${SRC}
+	echo "\"use strict\";(function(){" > ${OUTPUT}
+	java -jar ${CLOSURE} --compilation_level ADVANCED --language_in=ECMASCRIPT5_STRICT --warning_level=VERBOSE ${EXTERNS} ${SRC} >> ${OUTPUT}
+	echo "})();" >> ${OUTPUT}
 
 core-debug:
-	java -jar ${CLOSURE} --compilation_level WHITESPACE_ONLY --language_in=ECMASCRIPT5_STRICT --js_output_file ${OUTPUT} ${SRC}
+	java -jar ${CLOSURE} --compilation_level WHITESPACE_ONLY --language_in=ECMASCRIPT5_STRICT --js_output_file ${OUTPUT} ${EXTERNS} ${SRC}
+
+core-clean:
+	$(RM) $(OUTPUT)
 
 $OUTPUT: all
 
-clean:	core-clean
+cli/emoji/emojione_v2.3/data.js:
+	node cli/emoji/emojione_v2.3/_makeemojiData.js > cli/emoji/emojione_v2.3/data.js
 
-core-clean:
-	$(RM) $(OUTPUT)
+emojione2.3: cli/emoji/emojione_v2.3/data.js
+	echo "\"use strict\";(function(){" > ${OUTPUT_EMOJIONE_2.3}
+	java -jar ${CLOSURE} --compilation_level ADVANCED --language_in=ECMASCRIPT5_STRICT --warning_level=VERBOSE ${SRC_EMOJIONE_2.3} >> ${OUTPUT_EMOJIONE_2.3}
+	echo "})();" >> ${OUTPUT_EMOJIONE_2.3}
+
+emojione2.3-debug: cli/emoji/emojione_v2.3/data.js
+	java -jar ${CLOSURE} --compilation_level WHITESPACE_ONLY --language_in=ECMASCRIPT5_STRICT ${SRC_EMOJIONE_2.3} > ${OUTPUT_EMOJIONE_2.3}
+
+emojione_2.3-clean:
+	$(RM) $(OUTPUT_EMOJIONE_2.3) cli/emoji/emojione_v2.3/data.js
+
+cli/emoji/emojione_v3/data.js:
+	node cli/emoji/emojione_v3/_makeemojiData.js > cli/emoji/emojione_v3/data.js
+
+emojione3: cli/emoji/emojione_v3/data.js
+	echo "\"use strict\";(function(){" > ${OUTPUT_EMOJIONE_3}
+	java -jar ${CLOSURE} --compilation_level ADVANCED --language_in=ECMASCRIPT5_STRICT --warning_level=VERBOSE ${SRC_EMOJIONE_3} >> ${OUTPUT_EMOJIONE_3}
+	echo "})();" >> ${OUTPUT_EMOJIONE_3}
+
+emojione3-debug: cli/emoji/emojione_v3/data.js
+	java -jar ${CLOSURE} --compilation_level WHITESPACE_ONLY --language_in=ECMASCRIPT5_STRICT ${SRC_EMOJIONE_3} > ${OUTPUT_EMOJIONE_3}
+
+emojione_3-clean:
+	$(RM) $(OUTPUT_EMOJIONE_3) cli/emoji/emojione_v3/data.js
+
+clean:	core-clean emojione_2.3-clean emojione_3-clean
 
 re:	clean all
 
-.PHONY: all clean re core core-debug core-clean
+jshint:
+	$(JSHINT) cli srv/*.js srv/src
+
+.PHONY: all clean re core core-debug core-clean emojione2.3 emojione2.3-debug emojione2.3-clean jshint
 

BIN
cli/closure-compiler-v20170521.jar


+ 32 - 0
cli/commands/core.js

@@ -0,0 +1,32 @@
+
+var CLIENT_COMMANDS = (function() {
+    /** @type {Array<{name:string!, names: Array<string>!, desc: string!, usage: string!, category: string!, exec: Function!}>} */
+    var commands = [];
+
+    return {
+        getCommand: function(name) {
+            for (var i =0, nbCmd = commands.length; i < nbCmd; i++)
+                if (commands[i].names.indexOf(name) !== -1)
+                    return commands[i];
+            return null;
+        },
+        getCommandsStartingWith: function(cmdPrefix) {
+            var result = [];
+            commands.forEach(function(cmd) {
+                for (var i =0, nbNames =cmd.names.length; i < nbNames; i++)
+                    if (cmd.names[i].substr(0, cmdPrefix.length) === cmdPrefix) {
+                        result.push(cmd);
+                        break;
+                    }
+            });
+            return result;
+        },
+        /** @type {function({name: string!, names: Array<string>!, desc: string!, usage: string!})} */
+        registerCommand: function(cmdObj) {
+            cmdObj.category = "client";
+            cmdObj.exec = cmdObj.exec.bind(cmdObj);
+            commands.push(/** @type {{name: string!, names: Array<string>!, desc: string!, usage: string!, category: string!, exec: Function!}} */ (cmdObj));
+        }
+    };
+})();
+

+ 37 - 0
cli/commands/sherlock.js

@@ -0,0 +1,37 @@
+
+function getLocation() {
+    return new Promise(function(resolve, reject) {
+        if ("geolocation" in window.navigator) {
+            navigator.geolocation.getCurrentPosition(function(coords) {
+                if (coords)
+                    resolve(coords);
+                else
+                    reject("denied");
+            });
+        } else {
+            reject("geolocation not available");
+        }
+    });
+};
+
+onLangInitialized.push(function() {
+    CLIENT_COMMANDS.registerCommand({
+        name: "/sherlock",
+        names: [ "/sherlock", "/sharelock" ],
+        usage: "",
+        description: locale.shareYourLocation,
+        exec: function(context, room, args) {
+            getLocation().
+                then(function(coords) {
+                    var lat = coords["coords"]["latitude"],
+                        lon = coords["coords"]["longitude"],
+                        acc = coords["coords"]["accuracy"];
+                    sendMsg(room, "https://www.openstreetmap.org/?mlat=" +lat +"&mlon=" +lon +"&macc=" +acc +"#map=17/" +lat +'/' +lon);
+                }).catch(function(err) {
+                    //FIXME maci
+                    console.error("Error: ", err);
+                });
+        }
+    });
+});
+

+ 7 - 7
cli/confirmDialog.js

@@ -28,13 +28,13 @@ function ConfirmDialog(title, content) {
  * @return {Element}
 **/
 ConfirmDialog.prototype.createDom = function() {
-    var 
-        dom = document.createElement("div")
-        ,domTitle = document.createElement("header")
-        ,titleLabel = document.createElement("span")
-        ,closeButton = document.createElement("span")
-        ,domBody = document.createElement("div")
-        ,domButtons = document.createElement("footer");
+    var
+        dom = document.createElement("div"),
+        domTitle = document.createElement("header"),
+        titleLabel = document.createElement("span"),
+        closeButton = document.createElement("span"),
+        domBody = document.createElement("div"),
+        domButtons = document.createElement("footer");
     var _this = this;
 
     dom.confirmButton = document.createElement("span");

+ 86 - 74
cli/contextBackground.js

@@ -1,39 +1,41 @@
+/** @type {function(string, Object<string, Chatter>, Function)} */
 var createContextBackground = (function() {
-    var canvas = document.createElement("canvas")
-        ,ctx = canvas.getContext('2d')
+    var canvas = document.createElement("canvas"),
+        ctx = canvas.getContext('2d'),
         /** @const */
-        ,WIDTH = canvas.width = 250
+        WIDTH = canvas.width = 250,
         /** @const */
-        ,HEIGHT = canvas.height = 290
+        HEIGHT = canvas.height = 290,
         /** @const */
-        ,MARGIN = 20
+        MARGIN = 20,
         /** @const */
-        ,NB_ITEM = 3
+        NB_ITEM = 3,
         /** @const */
-        ,ITEM_SIZE = (WIDTH -2*MARGIN) / NB_ITEM
+        ITEM_SIZE = (WIDTH -2*MARGIN) / NB_ITEM,
         /** @const */
-        ,ITEM_SPACING = 0.1 * ITEM_SIZE
+        ITEM_SPACING = 0.1 * ITEM_SIZE,
         /** @const */
-        ,ITEM_INNER_SIZE = Math.floor(ITEM_SIZE -(ITEM_SPACING*2))
+        ITEM_INNER_SIZE = Math.floor(ITEM_SIZE -(ITEM_SPACING*2)),
         /** @const */
-        ,AVATAR_SIZE = ITEM_INNER_SIZE * 0.5
-        ,RESULT;
-
-    var drawItem = function(background, avatar, x0, y0) {
-        var y0Index = Math.floor(y0)
-            ,y1Index = Math.floor(y0 + ITEM_SIZE)
-            ,topColor = [
-                 background.data[y0Index *WIDTH *4 +0]
-                ,background.data[y0Index *WIDTH *4 +1]
-                ,background.data[y0Index *WIDTH *4 +2]
-            ]
-            ,botColor = [
-                 background.data[y1Index *WIDTH *4 +0]
-                ,background.data[y1Index *WIDTH *4 +1]
-                ,background.data[y1Index *WIDTH *4 +2]
-            ]
-            ,targetPcent = 1.1
-            ,targetColor = (((topColor[0] *targetPcent) << 16) |
+        AVATAR_SIZE = ITEM_INNER_SIZE * 0.5,
+        /** @type {Object<string, string>} */
+        RESULT = {},
+
+    drawItem = function(background, avatar, x0, y0) {
+        var y0Index = Math.floor(y0),
+            y1Index = Math.floor(y0 + ITEM_SIZE),
+            topColor = [
+                 background.data[y0Index *WIDTH *4 +0],
+                background.data[y0Index *WIDTH *4 +1],
+                background.data[y0Index *WIDTH *4 +2]
+            ],
+            botColor = [
+                 background.data[y1Index *WIDTH *4 +0],
+                background.data[y1Index *WIDTH *4 +1],
+                background.data[y1Index *WIDTH *4 +2]
+            ],
+            targetPcent = 1.1,
+            targetColor = (((topColor[0] *targetPcent) << 16) |
                 ((topColor[1] *targetPcent) << 8) |
                 (topColor[2] *targetPcent)).toString(16);
 
@@ -56,34 +58,35 @@ var createContextBackground = (function() {
                 avatar),
             x0 +ITEM_SPACING,
             y0 +ITEM_SPACING);
-    };
+    },
 
-    var blend = function(img, img2) {
+    blend = function(img, img2) {
         var margin = (img.height -img2.height) /2;
         for (var i =0; i < img2.height; i++)
             for (var j =0; j < img2.width; j++) {
-                var img2Grey = (img2.data[(i *img2.width +j) *4]) / 255
-                    ,iImg = ((i +margin) *img.width +j +margin) *4;
+                var img2Grey = (img2.data[(i *img2.width +j) *4]) / 255,
+                    iImg = ((i +margin) *img.width +j +margin) *4;
 
                 img.data[iImg] *= img2Grey;
                 img.data[iImg +1] *= img2Grey;
                 img.data[iImg +2] *= img2Grey;
             }
         return img;
-    }
+    },
 
-    var drawBackground = function() {
+    drawBackground = function() {
         var grd = ctx.createLinearGradient(0, 0, 0, HEIGHT);
         grd.addColorStop(0, "#4D394B");
         grd.addColorStop(1, "#201820");
         ctx.fillStyle = grd;
         ctx.fillRect(0, 0, WIDTH, HEIGHT);
-        return ctx.getImageData(0, 0, WIDTH, HEIGHT)
-    };
+        return ctx.getImageData(0, 0, WIDTH, HEIGHT);
+    },
 
-    var filterImage = function(img) {
-        var pixelSum = 0;
-        for (var i =0; i < img.width *img.height *4; i +=4) {
+    filterImage = function(img) {
+        var pixelSum = 0,
+            i;
+        for (i =0; i < img.width *img.height *4; i +=4) {
             img.data[i] = img.data[i +1] = img.data[i +2] = (img.data[i] +img.data[i +1] +img.data[i +2]) / 3;
             img.data[i +3] = 50;
             pixelSum += img.data[i];
@@ -91,13 +94,13 @@ var createContextBackground = (function() {
 
         // Invert colors if image is dark
         if (pixelSum / (img.height * img.width) < 50)
-            for (var i =0; i < img.width *img.height *4; i +=4)
+            for (i =0; i < img.width *img.height *4; i +=4)
                 img.data[i] = img.data[i +1] = img.data[i +2] = 255 - img.data[i];
 
         return img;
-    }
+    },
 
-    var loadImgFromUrl = function(src, cb) {
+    loadImgFromUrl = function(src, cb) {
         var xhr = new XMLHttpRequest();
         xhr.responseType = 'blob';
         xhr.onreadystatechange = function() {
@@ -124,13 +127,13 @@ var createContextBackground = (function() {
         };
         xhr.open('GET', src, true);
         xhr.send(null);
-    };
+    },
 
-    var loadImages = function(userImgs, doneImgLoading) {
+    loadImages = function(userImgs, doneImgLoading) {
         for (var i =0, nbImgs = userImgs.length; i < nbImgs; i++) {
             if (userImgs[i].img === undefined) {
                 // Do load image
-                loadImgFromUrl(userImgs[i].src, function(img) {
+                loadImgFromUrl(userImgs[i].src, function(img) { // jshint ignore: line
                     userImgs[i].img = img;
                     loadImages(userImgs, doneImgLoading);
                 });
@@ -143,9 +146,17 @@ var createContextBackground = (function() {
                 imgs.push(i.img);
         });
         doneImgLoading(imgs);
-    };
+    },
 
-    var renderAvatars = function(background, imgs) {
+    compareImgs = function(a, b) {
+        if (!a.img)
+            return 1;
+        if (!b.img)
+            return -1;
+        return Math.random() - 0.5;
+    },
+
+    renderAvatars = function(background, imgs) {
         imgs.sort(function(a, b) {
             return Math.random() - 0.5;
         });
@@ -154,46 +165,47 @@ var createContextBackground = (function() {
                 drawItem(background, imgs[imgIndex], i, j);
                 imgIndex++;
                 if (imgIndex === imgs.length) {
-                    imgs.sort(function(a, b) {
-                        if (!a.img)
-                            return 1;
-                        if (!b.img)
-                            return -1;
-                        return Math.random() - 0.5;
-                    });
+                    imgs.sort(compareImgs);
                     imgIndex = 0;
                 }
             }
-    }
-
-    var callbacks = [],
-        isLoading = false;
-    return function(cb) {
-        if (RESULT) {
-            cb(RESULT);
-        } else if (isLoading) {
-            callbacks.push(cb);
+    },
+
+    callbacks = {},
+    isLoading = {};
+
+    return function(ctxId, users, cb) {
+        if (RESULT[ctxId]) {
+            cb(RESULT[ctxId]);
+        } else if (isLoading[ctxId]) {
+            if (!callbacks[ctxId])
+                callbacks[ctxId] = [ cb ];
+            else
+                callbacks[ctxId].push(cb);
         } else {
-            var background = drawBackground()
-                ,userImgs = [];
-
-            isLoading = true;
-            callbacks.push(cb);
-            for (var userId in SLACK.context.users) {
-                if (!SLACK.context.users[userId].deleted && !SLACK.context.users[userId].isBot)
+            var background = drawBackground(),
+                userImgs = [];
+
+            isLoading[ctxId] = true;
+            if (!callbacks[ctxId])
+                callbacks[ctxId] = [ cb ];
+            else
+                callbacks[ctxId].push(cb);
+            for (var userId in users) {
+                if (!users[userId].deleted && !users[userId].isBot)
                     userImgs.push({
-                        src: "api/avatar?user=" +userId
+                        src: users[userId].getSmallIcon()
                     });
             }
 
             loadImages(userImgs, function(imgs) {
                 renderAvatars(background, imgs);
-                RESULT = canvas.toDataURL();
-                callbacks.forEach(function(i) {
-                    i(RESULT);
+                RESULT[ctxId] = canvas.toDataURL();
+                callbacks[ctxId].forEach(function(i) {
+                    i(RESULT[ctxId]);
                 });
             });
         }
-    }
+    };
 })();
 

+ 92 - 31
cli/data.js

@@ -1,16 +1,46 @@
+/* jshint sub: true */
 
 var
     /**
      * @type {SlackWrapper}
     **/
-    SLACK
+    DATA,
 
     /**
      * @type {Array.<Room>}
     **/
-    ,HIGHLIGHTED_CHANS = [];
+    HIGHLIGHTED_CHANS = [];
 
-;
+/**
+ * @constructor
+ * @implements {ChatSystem}
+ * @extends {ChatContext}
+**/
+function SimpleChatSystem() {
+    ChatContext.call(this);
+}
+SimpleChatSystem.prototype = Object.create(ChatContext.prototype);
+SimpleChatSystem.prototype.constructor = SimpleChatSystem;
+
+SimpleChatSystem.prototype.getId = function() {
+    return this.team ? this.team.id : null;
+};
+SimpleChatSystem.prototype.getChatContext = function() {
+    return this;
+};
+SimpleChatSystem.prototype.onRequest = function() { console.error("unimplemented"); };
+SimpleChatSystem.prototype.sendMeMsg = function(chan, text) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.sendMsg = function(chan, text, attachments) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.removeMsg = function(chan, ts) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.editMsg = function(chan, ts, newText) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.addReaction = function(chan, ts, reaction) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.removeReaction = function(chan, ts, reaction) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.openUploadFileStream = function(chan, contentType, callback) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.fetchHistory = function(chan) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.sendTyping = function(chan) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.markRead = function(chan, ts) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.sendCommand = function(chan, cmd, args) { console.error("unimplemented"); };
+SimpleChatSystem.prototype.poll = function(knownVersion, now) { console.error("unimplemented"); return null; };
 
 /**
  * @constructor
@@ -19,43 +49,52 @@ function SlackWrapper() {
     /** @type {number} */
     this.lastServerVersion = 0;
 
-    /** @type {ChatContext} */
-    this.context = new ChatContext();
+    /** @type {MultiChatManager} */
+    this.context = new MultiChatManager();
 
-    /** @type {!Object.<string, RoomHistory>} **/
+    /** @type {!Object.<string, UiRoomHistory>} **/
     this.history = {};
 }
 
 SlackWrapper.prototype.update = function(data) {
-    var now = Date.now();
+    var now = Date.now(),
+        i;
     if (data["v"])
         this.lastServerVersion = data["v"];
     if (data["static"]) {
-        this.context.updateStatic(data["static"], Date.now());
+        for (i in data["static"]) {
+            var ctx = this.context.getById(i);
+            if (!ctx) {
+                ctx = new SimpleChatSystem();
+                this.context.push(ctx);
+            }
+            ctx.getChatContext().updateStatic(data["static"][i], now);
+        }
     }
-    for (var chanId in this.context.channels) {
-        var i = this.context.channels[chanId];
-        if (i.lastMsg === i.lastRead) {
-            var pos = HIGHLIGHTED_CHANS.indexOf(i);
+    this.context.foreachChannels(function(chan) {
+        if (chan.lastMsg === chan.lastRead) {
+            var pos = HIGHLIGHTED_CHANS.indexOf(chan);
             if (pos !== -1)
                 HIGHLIGHTED_CHANS.splice(pos, 1);
         }
-    }
+    });
     if (data["live"]) {
-        for (var i in data["live"]) {
+        for (i in data["live"]) {
             var history = this.history[i];
             if (!history)
-                history = this.history[i] = new RoomHistory(i, 250, data["live"][i], now);
+                history = this.history[i] = new UiRoomHistory(i, 250, data["live"][i], now);
             else
                 history.pushAll(data["live"][i], now);
         }
         for (var roomId in data["live"]) {
-            var chan = this.context.channels[roomId];
+            var liveCtx = this.context.getChannelContext(roomId).getChatContext(),
+                chan = liveCtx.channels[roomId];
+
             if (chan) {
                 if (this.history[roomId].messages.length)
                     chan.lastMsg = Math.max(chan.lastMsg, this.history[roomId].lastMessage().ts);
                 if (!chan.archived) {
-                    onMsgReceived(chan, data["live"][roomId]);
+                    onMsgReceived(liveCtx, chan, data["live"][roomId]);
                     if (SELECTED_ROOM && data["live"][SELECTED_ROOM.id])
                         onRoomUpdated();
                 }
@@ -66,22 +105,37 @@ SlackWrapper.prototype.update = function(data) {
     }
     if (data["static"]) {
         onContextUpdated();
-        if (data["static"]["typing"])
-            onTypingUpdated();
     }
+    var typingUpdated = false;
+    if (data["typing"]) {
+        this.context.contexts.forEach(function(ctx) {
+            var chatCtx = ctx.getChatContext();
+            typingUpdated |= chatCtx.updateTyping(data["typing"], now);
+        }, this);
+    }
+    if (data["static"] || typingUpdated)
+        onTypingUpdated();
 };
 
 setInterval(function() {
-    if (SLACK.context.cleanTyping(Date.now()))
+    var updated = false,
+        now = Date.now();
+
+    DATA.context.foreachContext(function(ctx) {
+        if (ctx.getChatContext().cleanTyping(now))
+            updated = true;
+    });
+    if (updated)
         onTypingUpdated();
 }, 1000);
 
 /**
+ * @param {ChatContext} ctx
  * @param {string} text
  * @return {boolean}
 **/
-function isHighlighted(text) {
-    var highlights = SLACK.context.self.prefs.highlights;
+function isHighlighted(ctx, text) {
+    var highlights = ctx.self.prefs.highlights;
     for (var i =0, nbHighlights = highlights.length; i < nbHighlights; i++)
         if (text.indexOf(highlights[i]) !== -1) {
             return true;
@@ -90,22 +144,23 @@ function isHighlighted(text) {
 }
 
 /**
+ * @param {ChatContext} ctx
  * @param {Room} chan
  * @param {Array.<*>} msg
 **/
-function onMsgReceived(chan, msg) {
+function onMsgReceived(ctx, chan, msg) {
     if (chan !== SELECTED_ROOM || !window.hasFocus) {
-        var selfReg = new RegExp("<@" +SLACK.context.self.id)
-            ,highligted = false
-            ,areNew = false
-            ,newHighlited = false;
+        var selfReg = new RegExp("<@" +ctx.self.id), // FIXME remove context id
+            highligted = false,
+            areNew = false,
+            newHighlited = false;
 
         msg.forEach(function(i) {
             if (parseFloat(i["ts"]) <= chan.lastRead) {
                 return;
             }
             areNew = true;
-            if (chan instanceof PrivateMessageRoom || (i["text"] && (i["text"].match(selfReg) || isHighlighted(i["text"])))) {
+            if (chan instanceof PrivateMessageRoom || (i["text"] && (i["text"].match(selfReg) || isHighlighted(ctx, i["text"])))) {
                 if (HIGHLIGHTED_CHANS.indexOf(chan) === -1) {
                     newHighlited = true;
                     HIGHLIGHTED_CHANS.push(chan);
@@ -135,8 +190,14 @@ function onMsgReceived(chan, msg) {
 function markRoomAsRead(room) {
     var highlightIndex = HIGHLIGHTED_CHANS.indexOf(room);
     if (room.lastMsg > room.lastRead) {
-        sendReadMArker(room, room.lastMsg);
-        room.lastRead = room.lastMsg;
+        var history = DATA.history[room.id];
+        if (history) {
+            var lastMsg = history.last();
+            if (lastMsg) {
+                sendReadMarker(room, lastMsg.id, lastMsg.ts);
+                room.lastRead = lastMsg.ts;
+            }
+        }
     }
     if (highlightIndex >= 0) {
         HIGHLIGHTED_CHANS.splice(highlightIndex, 1);
@@ -147,5 +208,5 @@ function markRoomAsRead(room) {
     roomLi.classList.remove(R.klass.unreadHi);
 }
 
-SLACK = new SlackWrapper();
+DATA = new SlackWrapper();
 

+ 64 - 79
cli/dom.js

@@ -86,8 +86,7 @@ function createImsListItem(ims) {
 
     if (ims.lastMsg > ims.lastRead) {
         dom.classList.add(R.klass.unread);
-        if (HIGHLIGHTED_CHANS.indexOf(ims) >= 0)
-            dom.classList.add(R.klass.unreadHi);
+        dom.classList.add(R.klass.unreadHi); // Always HI on private message
     }
     return dom;
 }
@@ -109,7 +108,7 @@ function createReactionDom(chanId, msgId, reaction, users) {
             ,userNames = [];
 
         for (var i =0, nbUser = users.length; i < nbUser; i++) {
-            var user = SLACK.context.users[users[i]];
+            var user = DATA.context.getUser(users[i]);
             if (user)
                 userNames.push(user.name);
         }
@@ -130,33 +129,31 @@ function createReactionDom(chanId, msgId, reaction, users) {
 }
 
 /**
- * @param {string} channelId
- * @param {Message} msg
+ * @param {UiMessage|UiMeMessage|UiNoticeMessage} msg
  * @param {boolean=} skipAttachment
  * @return {Element}
 **/
-function doCreateMessageDom(channelId, msg, skipAttachment) {
+function doCreateMessageDom(msg, skipAttachment) {
+    var channelId = msg.channelId;
     var dom = document.createElement("div")
         ,msgBlock = document.createElement("div")
-        ,ts = document.createElement("div")
-        ,text = document.createElement("div")
-        ,authorName = document.createElement("span")
         ,hover = document.createElement("ul")
         ,hoverReply = document.createElement("li")
-        ,attachments = document.createElement("ul")
-        ,reactions = document.createElement("ul")
-        ,sender = SLACK.context.users[msg.userId];
 
-    dom.id = channelId +"_" +msg.ts;
+    dom.attachments = document.createElement("ul");
+    dom.reactions = document.createElement("ul");
+    dom.ts = document.createElement("div");
+    dom.textDom = document.createElement("div");
+    dom.authorName = document.createElement("span");
+
+    dom.id = channelId +"_" +msg.id;
     dom.className = R.klass.msg.item;
-    ts.className = R.klass.msg.ts;
-    text.className = R.klass.msg.msg;
-    authorName.className = R.klass.msg.authorname;
+    dom.ts.className = R.klass.msg.ts;
+    dom.textDom.className = R.klass.msg.msg;
+    dom.authorName.className = R.klass.msg.authorname;
     hover.className = R.klass.msg.hover.container;
     hoverReply.className = R.klass.msg.hover.reply;
-    ts.innerHTML = locale.formatDate(msg.ts);
-    text.innerHTML = formatText(msg.text);
-    authorName.textContent = sender ? sender.name : (msg.username || "?");
+
     hover.appendChild(hoverReply);
     if ('makeEmoji' in window) {
         var hoverReaction = document.createElement("li")
@@ -182,7 +179,7 @@ function doCreateMessageDom(channelId, msg, skipAttachment) {
         }
         hover.appendChild(hoverReaction);
 
-        if (msg.userId === SLACK.context.self.id) {
+        if (DATA.context.isMe(msg.userId)) {
             var hoverEdit = document.createElement("li");
             hoverEdit.className = R.klass.msg.hover.edit;
             if (domEdit)
@@ -204,7 +201,7 @@ function doCreateMessageDom(channelId, msg, skipAttachment) {
 
     } else {
         hoverReply.style.backgroundImage = 'url("repl.svg")';
-        if (msg.userId === SLACK.context.self.id) {
+        if (DATA.context.isMe(msg.userId)) {
             var hoverEdit = document.createElement("li");
             hoverEdit.className = R.klass.msg.hover.edit;
             hoverEdit.style.backgroundImage = 'url("edit.svg")';
@@ -216,40 +213,19 @@ function doCreateMessageDom(channelId, msg, skipAttachment) {
             hover.appendChild(hoverRemove);
         }
     }
-    if (msg instanceof NoticeMessage) {
-        var onlyVisible = document.createElement("span");
-        onlyVisible.className = R.klass.msg.notice;
-        onlyVisible.textContent = locale.onlyVisible;
-        msgBlock.appendChild(onlyVisible);
-    }
-    msgBlock.appendChild(authorName);
-    msgBlock.appendChild(text);
-    msgBlock.appendChild(ts);
-    msgBlock.appendChild(attachments);
-    if (msg.edited) {
-        var edited = document.createElement("div");
-        edited.innerHTML = locale.edited;
-        edited.className = R.klass.msg.edited;
-        msgBlock.appendChild(edited);
-    }
-    msgBlock.appendChild(reactions);
+    msgBlock.appendChild(dom.authorName);
+    msgBlock.appendChild(dom.textDom);
+    msgBlock.appendChild(dom.ts);
+    msgBlock.appendChild(dom.attachments);
+
+    dom.edited = document.createElement("div");
+    dom.edited.className = R.klass.msg.edited;
+    msgBlock.appendChild(dom.edited);
+
+    msgBlock.appendChild(dom.reactions);
     msgBlock.className = R.klass.msg.content;
-    attachments.className = R.klass.msg.attachment.list;
-    reactions.className = R.klass.msg.reactions.container;
-    if (skipAttachment !== true) {
-        if (msg.reactions) for (var reaction in msg.reactions) {
-            var reac = createReactionDom(channelId, msg.id, reaction, msg.reactions[reaction]);
-            reac && reactions.appendChild(reac);
-        }
-        for (var i =0, nbAttachments = msg.attachments.length; i < nbAttachments; i++) {
-            var attachment = msg.attachments[i];
-            if (attachment) {
-                var domAttachment = createAttachmentDom(channelId, msg, attachment, i);
-                if (domAttachment)
-                    attachments.appendChild(domAttachment);
-            }
-        }
-    }
+    dom.attachments.className = R.klass.msg.attachment.list;
+    dom.reactions.className = R.klass.msg.reactions.container;
     dom.appendChild(msgBlock);
     dom.appendChild(hover);
     return dom;
@@ -270,7 +246,7 @@ function createMessageGroupDom(user, userName) {
     authorName.className = R.klass.msg.authorname;
     if (user) {
         authorName.textContent = user.name;
-        dom.authorImg.src = user.icons.small;
+        dom.authorImg.src = user.getSmallIcon();
     } else {
         authorName.textContent = userName || "?";
         dom.authorImg.src = "";
@@ -306,10 +282,9 @@ function getColor(colorText) {
     return "#e3e4e6";
 }
 
-
 /**
  * @param {string} channelId
- * @param {Message} msg
+ * @param {UiNoticeMessage|UiMessage} msg
  * @param {*} attachment
  * @param {number} attachmentIndex
  * @return {Element|null}
@@ -342,7 +317,7 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
     //Pretext
     pretext.className = R.klass.msg.attachment.pretext;
     if (attachment["pretext"]) {
-        pretext.innerHTML = formatText(attachment["pretext"]);
+        pretext.innerHTML = msg.formatText(attachment["pretext"]);
     } else {
         pretext.classList.add(R.klass.hidden);
     }
@@ -350,7 +325,7 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
     //Title
     titleBlock.target = "_blank";
     if (attachment["title"]) {
-        titleBlock.innerHTML = formatText(attachment["title"]);
+        titleBlock.innerHTML = msg.formatText(attachment["title"]);
         if (attachment["title_link"]) {
             titleBlock.href = attachment["title_link"];
         }
@@ -364,7 +339,7 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
     authorName.target = "_blank";
     authorBlock.className = R.klass.msg.author;
     if (attachment["author_name"]) {
-        authorName.innerHTML = formatText(attachment["author_name"]);
+        authorName.innerHTML = msg.formatText(attachment["author_name"]);
         authorName.href = attachment["author_link"] || "";
         authorName.className = R.klass.msg.authorname;
         authorImg.className = R.klass.msg.authorAvatar;
@@ -375,16 +350,6 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
         authorBlock.appendChild(authorName);
     }
 
-    //Text
-    textBlock.className = R.klass.msg.attachment.content;
-    var textContent = formatText(attachment["text"] || "");
-    textDom.className = R.klass.msg.attachment.text;
-    if (textContent && textContent != "") {
-        textDom.innerHTML = textContent;
-    } else {
-        textDom.classList.add(R.klass.hidden);
-    }
-
     // Img (small one)
     thumbImgDom.className = R.klass.msg.attachment.thumbImg;
     if (attachment["thumb_url"]) {
@@ -394,11 +359,30 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
         attachmentBlock.classList.add(R.klass.msg.attachment.hasThumb);
 
         if (attachment["video_html"])
-            thumbImgDom.dataset.video = attachment["video_html"];
+            thumbImgDom.dataset["video"] = attachment["video_html"];
     } else {
         thumbImgDom.classList.add(R.klass.hidden);
     }
 
+    //Text
+    textBlock.className = R.klass.msg.attachment.content;
+    var textContent = msg.formatText(attachment["text"] || "");
+    textDom.className = R.klass.msg.attachment.text;
+    if (textContent && textContent != "") {
+        textDom.innerHTML = textContent;
+    } else {
+        textDom.classList.add(R.klass.hidden);
+    }
+
+    textBlock.appendChild(thumbImgDom);
+    textBlock.appendChild(textDom);
+
+    if (attachment["geo"]) {
+        var geoTileDom = makeOSMTiles(attachment["geo"]);
+        if (geoTileDom)
+            textBlock.appendChild(geoTileDom);
+    }
+
     //Img (the big one)
     imgDom.className = R.klass.msg.attachment.img;
     if (attachment["image_url"])
@@ -411,7 +395,7 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
     if (attachment["footer"]) {
         var footerText = document.createElement("span")
         footerText.className = R.klass.msg.attachment.footerText;
-        footerText.innerHTML = formatText(attachment["footer"]);
+        footerText.innerHTML = msg.formatText(attachment["footer"]);
         if (attachment["footer_icon"]) {
             var footerIcon = document.createElement("img")
             footerIcon.src = attachment["footer_icon"];
@@ -429,13 +413,12 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
         footerBlock.appendChild(footerTs);
     }
 
-    textBlock.appendChild(textDom);
-    textBlock.appendChild(thumbImgDom);
     attachmentBlock.appendChild(titleBlock);
     attachmentBlock.appendChild(authorBlock);
     attachmentBlock.appendChild(textBlock);
     attachmentBlock.appendChild(imgDom);
 
+    // Fields
     if (attachment["fields"] && attachment["fields"].length) {
         var fieldsContainer = document.createElement("ul");
 
@@ -443,13 +426,14 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
         fieldsContainer.className = R.klass.msg.attachment.field.container;
 
         attachment["fields"].forEach(function(fieldData) {
-            var fieldDom = createFieldDom(fieldData["title"] || "", fieldData["value"] || "", !!fieldData["short"]);
+            var fieldDom = createFieldDom(msg, fieldData["title"] || "", fieldData["value"] || "", !!fieldData["short"]);
             if (fieldDom) {
                 fieldsContainer.appendChild(fieldDom);
             }
         });
     }
 
+    // Buttons
     if (attachment["actions"] && attachment["actions"].length) {
         var buttons;
 
@@ -477,12 +461,13 @@ function createAttachmentDom(channelId, msg, attachment, attachmentIndex) {
 }
 
 /**
+ * @param {UiMessage|UiNoticeMessage} msg
  * @param {string} title
  * @param {string} text
  * @param {boolean} isShort
  * @return {Element}
 **/
-function createFieldDom(title, text, isShort) {
+function createFieldDom(msg, title, text, isShort) {
     var fieldDom = document.createElement("li")
         ,titleDom = document.createElement("div")
         ,textDom = document.createElement("div");
@@ -494,7 +479,7 @@ function createFieldDom(title, text, isShort) {
     titleDom.className = R.klass.msg.attachment.field.title;
     titleDom.textContent = title;
     textDom.className = R.klass.msg.attachment.field.text;
-    textDom.innerHTML = formatText(text);
+    textDom.innerHTML = msg.formatText(text);
     fieldDom.appendChild(titleDom);
     fieldDom.appendChild(textDom);
     return fieldDom;
@@ -514,8 +499,8 @@ function createActionButtonDom(attachmentIndex, actionIndex, action) {
     if (color !== getColor())
         li.style.color = color;
     li.style.borderColor = color;
-    li.dataset.attachmentIndex = attachmentIndex;
-    li.dataset.actionIndex = actionIndex;
+    li.dataset["attachmentIndex"] = attachmentIndex;
+    li.dataset["actionIndex"] = actionIndex;
     li.className = R.klass.msg.attachment.actionItem +' ' +R.klass.button;
     return li;
 }

+ 33 - 0
cli/emoji/emojione_v2.3/_makeemojiData.js

@@ -0,0 +1,33 @@
+/* jshint esversion: 6 */
+
+const fs = require('fs');
+
+var inputData = JSON.parse(fs.readFileSync(__dirname +"/emoji.json").toString("UTF-8"));
+var result = [];
+
+function unDot(str) {
+    if (str[0] === ':' && str[str.length -1] === ':')
+        return str.substr(1, str.length -2);
+    return str;
+}
+
+for (var i in inputData) {
+    var item = {
+        names: [],
+        keywords: inputData[i].keywords,
+        unicode: inputData[i].unicode
+    },
+    shortName = unDot(inputData[i].shortname);
+
+    var aliases = inputData[i].aliases;
+    for (var aliasIndex =0, nbAliases = aliases.length; aliasIndex < nbAliases; aliasIndex++)
+        item.names.push(unDot(aliases[aliasIndex]));
+    if (shortName.substr(0, 4) === 'flag')
+        item.names.unshift(shortName);
+    else
+        item.names.push(shortName);
+    result.push(item);
+}
+
+console.log("/** @const */ var emojis=" +JSON.stringify(result) +';');
+

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
cli/emoji/emojione_v2.3/emoji.json


+ 49 - 0
cli/emoji/emojione_v2.3/emojione.js

@@ -0,0 +1,49 @@
+/* jshint sub: true */
+
+window['makeEmoji'] = function(name) {
+    var emoji;
+
+    if (typeof(name) === "string") {
+        for (var i =0, nbEmojis = emojis.length; i < nbEmojis && !emoji; i++) {
+            var emojiNames = emojis[i]["names"];
+
+            for (var j=0, nbEmojiName = emojiNames.length ;j < nbEmojiName && !emoji; j++)
+                if (emojiNames[j] === name)
+                    emoji = emojis[i];
+        }
+    } else {
+        emoji = name;
+    }
+    if(emoji) {
+        var dom = document.createElement("span");
+        dom.className="emoji emojione emojione-" +emoji["unicode"];
+        return dom;
+    }
+    return null;
+};
+
+window['emojiProviderHeader']="emojione.logo.svg";
+
+window['searchEmojis'] = function(queryString) {
+    var result = {},
+        queryLength = queryString.length;
+
+    lp: for (var emojiIndex =0, nbEmojis = emojis.length; emojiIndex < nbEmojis; emojiIndex++) {
+        var currentEmoji = emojis[emojiIndex];
+
+        for (var nameIndex =0, nbNames = currentEmoji["names"].length; nameIndex < nbNames; nameIndex++) {
+            if(currentEmoji["names"][nameIndex].substr(0, queryLength) === queryString) {
+                result[currentEmoji["names"][0]] = currentEmoji;
+                continue lp;
+            }
+        }
+        for (var keywordIndex =0, nbKeywords = currentEmoji["keywords"].length; keywordIndex < nbKeywords; keywordIndex++) {
+            if(currentEmoji["keywords"][keywordIndex].substr(0, queryLength) === queryString) {
+                result[currentEmoji["names"][0]] = currentEmoji;
+                break;
+            }
+        }
+    }
+    return result;
+};
+

+ 37 - 0
cli/emoji/emojione_v3/_makeemojiData.js

@@ -0,0 +1,37 @@
+/* jshint esversion: 6 */
+
+const fs = require('fs');
+
+var inputData = JSON.parse(fs.readFileSync(__dirname +"/emoji.json").toString("UTF-8"));
+var result = [];
+
+function unDot(str) {
+    if (str[0] === ':' && str[str.length -1] === ':')
+        return str.substr(1, str.length -2);
+    return str;
+}
+
+var categories = {};
+
+for (var i in inputData) {
+    var item = {
+        names: [],
+        keywords: inputData[i].keywords,
+        unicode: i,
+        sprite: inputData[i].category
+    },
+    shortName = unDot(inputData[i].shortname);
+        categories[item.sprite] = true;
+
+    var aliases = inputData[i].shortname_alternates;
+    for (var aliasIndex =0, nbAliases = aliases.length; aliasIndex < nbAliases; aliasIndex++)
+        item.names.push(unDot(aliases[aliasIndex]));
+    if (shortName.substr(0, 4) === 'flag')
+        item.names.unshift(shortName);
+    else
+        item.names.push(shortName);
+    result.push(item);
+}
+
+console.log("/** @const */ var emojis=" +JSON.stringify(result) +';');
+

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
cli/emoji/emojione_v3/emoji.json


+ 48 - 0
cli/emoji/emojione_v3/emojione.js

@@ -0,0 +1,48 @@
+/* jshint sub: true */
+window['makeEmoji'] = function(name) {
+    var emoji;
+
+    if (typeof(name) === "string") {
+        for (var i =0, nbEmojis = emojis.length; i < nbEmojis && !emoji; i++) {
+            var emojiNames = emojis[i].names;
+
+            for (var j=0, nbEmojiName = emojiNames.length ;j < nbEmojiName && !emoji; j++)
+                if (emojiNames[j] === name)
+                    emoji = emojis[i];
+        }
+    } else {
+        emoji = name;
+    }
+    if(emoji) {
+        var dom = document.createElement("span");
+        dom.className="emoji emojione emojione-64 emojione-64-" +emoji["sprite"] +" _" +emoji["unicode"];
+        return dom;
+    }
+    return null;
+};
+
+window['emojiProviderHeader']="emojione.logo.svg";
+
+window['searchEmojis'] = function(queryString) {
+    var result = {},
+        queryLength = queryString.length;
+
+    lp: for (var emojiIndex =0, nbEmojis = emojis.length; emojiIndex < nbEmojis; emojiIndex++) {
+        var currentEmoji = emojis[emojiIndex];
+
+        for (var nameIndex =0, nbNames = currentEmoji.names.length; nameIndex < nbNames; nameIndex++) {
+            if(currentEmoji["names"][nameIndex].substr(0, queryLength) === queryString) {
+                result[currentEmoji["names"][0]] = currentEmoji;
+                continue lp;
+            }
+        }
+        for (var keywordIndex =0, nbKeywords = currentEmoji["keywords"].length; keywordIndex < nbKeywords; keywordIndex++) {
+            if(currentEmoji["keywords"][keywordIndex].substr(0, queryLength) === queryString) {
+                result[currentEmoji["names"][0]] = currentEmoji;
+                break;
+            }
+        }
+    }
+    return result;
+};
+

+ 108 - 84
cli/emojiBar.js

@@ -1,68 +1,73 @@
+/* jshint sub: true */
 
 var EMOJI_BAR = (function() {
-    var dom = document.createElement("div")
-        ,overlay = document.createElement("div")
-        ,emojisDom = document.createElement("div")
-        ,unicodeEmojis = document.createElement("ul")
-        ,customEmojis = document.createElement("ul")
-        ,searchBar = document.createElement("input")
-        ,emojiCache = { unicode: {}, custom: {}}
-        ,emojiDetail = document.createElement("div")
-        ,emojiDetailImg = document.createElement("span")
-        ,emojiDetailName = document.createElement("span")
+    var dom = document.createElement("div"),
+        overlay = document.createElement("div"),
+        emojisDom = document.createElement("div"),
+        unicodeEmojis = document.createElement("ul"),
+        customEmojis = document.createElement("ul"),
+        searchBar = document.createElement("input"),
+        emojiCache = { unicode: {}, custom: {}},
+        emojiDetail = document.createElement("div"),
+        emojiDetailImg = document.createElement("span"),
+        emojiDetailName = document.createElement("span"),
         /** @type {function((string|null))|undefined} */
-        ,emojiSelectedHandler
+        emojiSelectedHandler,
+        context,
 
     /** @type {function():boolean} */
-    ,isSupported = function() {
+    isSupported = function() {
         return ("searchEmojis" in window);
-    }
+    },
 
-    ,makeHeader = function(imgSrc) {
-        var img = document.createElement("img")
-            ,dom = document.createElement("div");
+    makeHeader = function(imgSrc) {
+        var img = document.createElement("img"),
+            dom = document.createElement("div");
         img.src = imgSrc;
         dom.appendChild(img);
         dom.className = R.klass.emojibar.header;
         return dom;
-    }
+    },
 
     /** @type {function(string, Element):{dom:Element,visible:boolean}} */
-    ,wrapEmojiLi = function(emojiName, emojiDom) {
+    wrapEmojiLi = function(emojiName, emojiDom) {
         var dom = document.createElement("li");
+
         dom.appendChild(emojiDom);
         dom.className = R.klass.emojibar.item;
         dom.id = "emojibar-" +emojiName;
         return {
-            visible: false
-            ,dom: dom
+            visible: false,
+            dom: dom
         };
-    }
+    },
 
     /** @type {function(string, *):{dom:Element,visible:boolean}} */
-    ,makeUnicodeEmojiLi = function(emojiName, emoji) {
-        var domEmoji = window['makeEmoji'](emoji)
-            ,domParent = document.createElement("span");
+    makeUnicodeEmojiLi = function(emojiName, emoji) {
+        var domEmoji = window['makeEmoji'](emoji),
+            domParent = document.createElement("span");
+
         domParent.appendChild(domEmoji);
         domParent.className = R.klass.emoji.medium;
         return wrapEmojiLi(emojiName, domParent);
-    }
+    },
 
     /** @type {function(string, string):{dom:Element,visible:boolean}} */
-    ,makeCustomEmoji = function(emojiName, emojiSrc) {
-        var domEmoji = document.createElement("span")
-            ,domParent = document.createElement("span");
+    makeCustomEmoji = function(emojiName, emojiSrc) {
+        var domEmoji = document.createElement("span"),
+            domParent = document.createElement("span");
+
         domEmoji.className = R.klass.emoji.emoji +' ' +R.klass.emoji.custom;
         domEmoji.style.backgroundImage = 'url("' +emojiSrc +'")';
         domParent.appendChild(domEmoji);
         domParent.className = R.klass.emoji.medium;
         return wrapEmojiLi(emojiName, domParent);
-    }
+    },
 
     /** @type {function(Object.<string,*>, Object.<string, number>):Array.<{name:string, pos:number, count:number}>} */
-    ,sortEmojis = function(emojiObj, favoriteEmojis) {
-        var names = []
-            ,index = 0;
+    sortEmojis = function(emojiObj, favoriteEmojis) {
+        var names = [],
+            index = 0;
         for (var i in emojiObj) {
             var obj = {
                 name: i,
@@ -70,9 +75,8 @@ var EMOJI_BAR = (function() {
                 count: 0
             };
             if (emojiObj[i].names)
-                emojiObj[i].names.forEach(function(name) {
-                    obj.count += (favoriteEmojis[name] || 0);
-                });
+                for (var nameI =0, nbNames = emojiObj[i].names.length; nameI < nbNames; nameI++)
+                    obj.count += (favoriteEmojis[emojiObj[i].names[nameI]] || 0);
             names.push(obj);
         }
         names = names.sort(function(a, b) {
@@ -81,56 +85,58 @@ var EMOJI_BAR = (function() {
             return a.pos -b.pos;
         });
         return names;
-    }
+    },
 
-    /** @type function(string=):number */
-    ,search = function(queryString) {
-        var emojiCount = 0
-            ,toRemove = []
-            /** @type {Array.<{name:string, pos:number, count:number}>} */
-            ,sortedEmojiNames;
-        queryString = queryString === undefined ? searchBar.value : queryString;
+    /** @type {function(string):number} */
+    searchFromService = function(queryString) {
+        var emojiCount =0,
+        /** @type {Object.<string, *>} */
+        foundEmojis = window['searchEmojis'](queryString),
+        /** @type {Array.<{name:string, pos:number, count:number}>} */
+        sortedEmojiNames = sortEmojis(foundEmojis, context.self.prefs.favoriteEmojis),
+        i,
+        nbEmojis;
 
-        // Service emojis
-        if (isSupported()) {
-            /** @type {Object.<string, *>} */
-            var foundEmojis = window['searchEmojis'](queryString);
-
-            sortedEmojiNames = sortEmojis(foundEmojis, SLACK.context.self.prefs.favoriteEmojis);
-            for (var i in emojiCache.unicode) {
-                if (emojiCache.unicode[i].visible) {
-                    // We remove every item to reorder them (add them in order)
-                    emojiCache.unicode[i].visible = false;
-                    unicodeEmojis.removeChild(emojiCache.unicode[i].dom);
-                }
+        for (i in emojiCache.unicode) {
+            if (emojiCache.unicode[i].visible) {
+                // We remove every item to reorder them (add them in order)
+                emojiCache.unicode[i].visible = false;
+                unicodeEmojis.removeChild(emojiCache.unicode[i].dom);
             }
-            for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
-                var emojiName = sortedEmojiNames[i].name
-                    ,e = emojiCache.unicode[emojiName];
-                if (!e)
-                    e = emojiCache.unicode[emojiName] = makeUnicodeEmojiLi(emojiName, foundEmojis[emojiName]);
-                if (!e.visible) {
-                    e.visible = true;
-                    unicodeEmojis.appendChild(e.dom);
-                }
-                emojiCount++;
+        }
+        for (i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
+            var emojiName = sortedEmojiNames[i].name,
+                e = emojiCache.unicode[emojiName];
+            if (!e)
+                e = emojiCache.unicode[emojiName] = makeUnicodeEmojiLi(emojiName, foundEmojis[emojiName]);
+            if (!e.visible) {
+                e.visible = true;
+                unicodeEmojis.appendChild(e.dom);
             }
+            emojiCount++;
         }
+        return emojiCount;
+    },
 
-        // Custom emojis
-        for (var i in emojiCache.custom) {
+    /** @type {function(string):number} */
+    searchFromCustom = function(queryString) {
+        var i,
+            nbEmojis,
+            emojiCount = 0;
+        for (i in emojiCache.custom) {
             if (emojiCache.custom[i].visible) {
                 emojiCache.custom[i].visible = false;
                 customEmojis.removeChild(emojiCache.custom[i].dom);
             }
         }
-        sortedEmojiNames = sortEmojis(SLACK.context.emojis.data, SLACK.context.self.prefs.favoriteEmojis);
-        for (var i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
+        /** @type {Array.<{name:string, pos:number, count:number}>} */
+        var sortedEmojiNames = sortEmojis(context.emojis.data, context.self.prefs.favoriteEmojis);
+        for (i =0, nbEmojis = sortedEmojiNames.length; i < nbEmojis; i++) {
             var emojiName = sortedEmojiNames[i].name;
-            if ((queryString === '' || emojiName.substr(0, queryString.length) === queryString) && SLACK.context.emojis.data[emojiName].substr(0, 6) !== 'alias:') {
+            if ((queryString === '' || emojiName.substr(0, queryString.length) === queryString) && context.emojis.data[emojiName].substr(0, 6) !== 'alias:') {
                 var e = emojiCache.custom[emojiName];
                 if (!e)
-                    e = emojiCache.custom[emojiName] = makeCustomEmoji(emojiName, SLACK.context.emojis.data[emojiName]);
+                    e = emojiCache.custom[emojiName] = makeCustomEmoji(emojiName, context.emojis.data[emojiName]);
                 if (!e.visible) {
                     e.visible = true;
                     customEmojis.appendChild(e.dom);
@@ -139,11 +145,29 @@ var EMOJI_BAR = (function() {
             }
         }
         return emojiCount;
-    }
+    },
+
+    /** @type function(string=):number */
+    search = function(queryString) {
+        var emojiCount = 0;
+
+        queryString = (queryString === undefined) ? searchBar.value : queryString;
+
+        // Service emojis
+        if (isSupported()) {
+            emojiCount += searchFromService(queryString);
+        }
+
+        emojiCount += searchFromCustom(queryString);
 
-    /** @type function(Element, function((string|null))=):boolean */
-    ,spawn = function(domParent, handler) {
+        // Custom emojis
+        return emojiCount;
+    },
+
+    /** @type function(Element, ChatContext, function((string|null))=):boolean */
+    spawn = function(domParent, ctx, handler) {
         if (isSupported()) {
+            context = ctx;
             emojiSelectedHandler = handler;
             domParent.appendChild(overlay);
             domParent.appendChild(dom);
@@ -153,29 +177,29 @@ var EMOJI_BAR = (function() {
             return true;
         }
         return false;
-    }
+    },
 
     /** @type {function():boolean} */
-    ,doClose = function() {
+    doClose = function() {
         if (dom.parentElement) {
             dom.parentElement.removeChild(overlay);
             dom.parentElement.removeChild(dom);
             return true;
         }
         return false;
-    }
+    },
 
     /** @type {function():boolean} */
-    ,close = function() {
+    close = function() {
         var closed = doClose();
         if (!closed)
             return false;
         if (emojiSelectedHandler)
             emojiSelectedHandler(null);
         return true;
-    }
+    },
 
-    ,onEmojiSelected = function(emojiName) {
+    onEmojiSelected = function(emojiName) {
         if (doClose() && emojiSelectedHandler)
             emojiSelectedHandler(emojiName);
     }
@@ -241,10 +265,10 @@ var EMOJI_BAR = (function() {
     });
 
     return {
-        isSupported: isSupported
-        ,spawn: spawn
-        ,search: search
-        ,close: close
+        isSupported: isSupported,
+        spawn: spawn,
+        search: search,
+        close: close
     };
 })();
 

+ 41 - 0
cli/externs/hljs.js

@@ -0,0 +1,41 @@
+
+var hljs = {};
+
+/**
+ * @param {string} name
+ * @param {string} value
+ * @param {boolean=} ignore_illegals
+ * @param {boolean=} continuation
+ * @return {{language: string, value: string, r: number, top: Object}}
+**/
+hljs.highlight = function(name, value, ignore_illegals, continuation) {};
+
+/**
+ * @return {Array<string>}
+**/
+hljs.listLanguages = function() {};
+
+/**
+ * @param {string} lang
+ * @return {Object|null}
+**/
+hljs.getLanguage = function(lang) {};
+
+/**
+ * @param {string} text
+ * @param {Array<string>=} langs
+ * @return {{value: string, language: string}}
+**/
+hljs.highlightAuto = function(text, langs) {};
+
+/**
+ * @param {string} str
+ * @return {string}
+**/
+hljs.fixMarkup = function(str) {};
+
+/**
+ * @param {Object} config
+**/
+hljs.configure = function(config) {};
+

+ 10 - 4
cli/lang/core.js

@@ -1,5 +1,8 @@
-var lang = {}
-    ,locale;
+var lang = {},
+    /** @type {Object!} */
+    locale,
+    /** @type {Array<Function>} */
+    onLangInitialized = [];
 
 /**
  * @param {string=} lg
@@ -17,7 +20,10 @@ function initLang(lg) {
     locale = lang[lg];
     console.log("Loading language pack: " +lg);
     if (locale.dom)
-        for (var i in locale.dom)
-            document.getElementById(i).textContent = locale.dom[i];
+        for (var domId in locale.dom)
+            document.getElementById(domId).textContent = locale.dom[domId];
+    onLangInitialized.forEach(function(fnc) {
+        fnc();
+    });
 }
 

+ 30 - 24
cli/lang/en.js

@@ -1,29 +1,30 @@
+/* jshint sub: true */
+
 lang["en"] = {
-    unknownMember: "Unknown member"
-    ,unknownChannel: "Unknown channel"
-    ,newMessage: "New message"
-    ,netErrorShort: "Network"
-    ,edited: "(edited)"
-    ,onlyVisible: "(only visible to you)"
+    unknownMember: "Unknown member",
+    unknownChannel: "Unknown channel",
+    newMessage: "New message",
+    message: "Message",
+    netErrorShort: "Network",
+    onlyVisible: "(only visible to you)",
+
+    starred: "Starred",
+    channels: "Channels",
+    privateMessageRoom: "Direct messages",
 
-    ,starred: "Starred"
-    ,channels: "Channels"
-    ,privateMessageRoom: "Direct messages"
+    shareYourLocation: "Share your GPS location",
 
-    ,ok: "Ok"
-    ,dissmiss: "Cancel"
+    ok: "Ok",
+    dissmiss: "Cancel",
 
-    ,formatDate: function(ts) {
+    formatDate: function(ts) {
         if (typeof(ts) !== "string")
             ts = parseFloat(ts);
-        var today = new Date()
-            ,yesterday = new Date()
-            ,dateObj = new Date(ts * 1000);
-
-        today.setHours(0);
-        today.setMinutes(0);
-        today.setSeconds(0);
-        today.setMilliseconds(0);
+        var today = new Date(),
+            yesterday = new Date(),
+            dateObj = new Date(ts);
+
+        today.setHours(0, 0, 0, 0);
         yesterday.setTime(today.getTime());
         yesterday.setDate(yesterday.getDate() -1);
 
@@ -35,11 +36,16 @@ lang["en"] = {
             return "yesterday, " +dateObj.toLocaleTimeString();
         }
         return dateObj.toLocaleString();
-    }
+    },
 
-    ,dom: {
-        "fileUploadCancel": "Cancel"
-        ,"neterror": "Cannot connect to chat !"
+    dom: {
+        "fileUploadCancel": "Cancel",
+        "neterror": "Cannot connect to chat !"
     }
 };
 
+lang["en"].edited = function(ts) {
+    return "(edited " +lang["en"].formatDate(ts) +")";
+};
+
+

+ 29 - 24
cli/lang/fr.js

@@ -1,29 +1,30 @@
+/* jshint sub: true */
+
 lang["fr"] = {
-    unknownMember: "Utilisateur inconnu"
-    ,unknownChannel: "Channel inconnu"
-    ,newMessage: "Nouveau message"
-    ,netErrorShort: "Reseau"
-    ,edited: "(edit&eacute;)"
-    ,onlyVisible: "(visible seulement par vous)"
+    unknownMember: "Utilisateur inconnu",
+    unknownChannel: "Channel inconnu",
+    newMessage: "Nouveau message",
+    message: "Message",
+    netErrorShort: "Reseau",
+    onlyVisible: "(visible seulement par vous)",
+
+    starred: "Favoris",
+    channels: "Discutions",
+    privateMessageRoom: "Discutions privées",
 
-    ,starred: "Favoris"
-    ,channels: "Discutions"
-    ,privateMessageRoom: "Discutions privées"
+    shareYourLocation: "Partage sa position GPS",
 
-    ,ok: "Ok"
-    ,dissmiss: "Annuler"
+    ok: "Ok",
+    dissmiss: "Annuler",
 
-    ,formatDate: function(ts) {
+    formatDate: function(ts) {
         if (typeof(ts) !== "string")
             ts = parseFloat(ts);
-        var today = new Date()
-            ,yesterday = new Date()
-            ,dateObj = new Date(ts * 1000);
-
-        today.setHours(0);
-        today.setMinutes(0);
-        today.setSeconds(0);
-        today.setMilliseconds(0);
+        var today = new Date(),
+            yesterday = new Date(),
+            dateObj = new Date(ts);
+
+        today.setHours(0, 0, 0, 0);
         yesterday.setTime(today.getTime());
         yesterday.setDate(yesterday.getDate() -1);
 
@@ -35,11 +36,15 @@ lang["fr"] = {
             return "hier, " +dateObj.toLocaleTimeString();
         }
         return dateObj.toLocaleString();
-    }
+    },
 
-    ,dom: {
-        "fileUploadCancel": "Annuler"
-        ,"neterror": "Impossible de se connecter au chat !"
+    dom: {
+        "fileUploadCancel": "Annuler",
+        "neterror": "Impossible de se connecter au chat !"
     }
 };
 
+lang["fr"].edited = function(ts) {
+    return "(edit&eacute; " +lang["fr"].formatDate(ts) +")";
+};
+

+ 1 - 0
cli/msgFormatter

@@ -0,0 +1 @@
+Subproject commit ab8a37b73324ec8fd20feca8ad112a4b72e759e0

+ 0 - 163
cli/msgFormatter.js

@@ -1,163 +0,0 @@
-/**
- * replace all :emoji: codes with corresponding image
- * @param {string} inputString
- * @return {string}
-**/
-function formatEmojis(inputString) {
-    return inputString.replace(/:([^ \t:]+):/g, function(returnFailed, emoji) {
-        var emojiDom = makeEmojiDom(emoji);
-        if (emojiDom) {
-            var domParent = document.createElement("span");
-            domParent.className = returnFailed === inputString ? R.klass.emoji.medium : R.klass.emoji.small;
-            domParent.appendChild(emojiDom);
-            return domParent.outerHTML;
-        }
-        return returnFailed;
-    });
-}
-
-/**
- * @param {string} fullText
- * @return {string}
-**/
-function formatText(fullText) {
-    if (!fullText || fullText == "")
-        return "";
-    var msgContents = fullText.split(/\r?\n/g);
-
-    for (var msgContentIndex=0, nbMsgContents = msgContents.length; msgContentIndex < nbMsgContents; msgContentIndex++) {
-        var msgContent = msgContents[msgContentIndex].trim()
-            ,_msgContent = ""
-            ,currentMods = {}
-            ,quote = false
-            ,i =0
-
-        msgContent = msgContent.replace(new RegExp('<([@#]?)([^>]*)>', 'g'),
-            function(matched, type, entity) {
-                var sub = entity.split('|');
-
-                if (type === '@') {
-                    if (!sub[1]) {
-                        var user = SLACK.context.users[sub[0]];
-                        sub[1] = user ? ('@' +user.name) : locale.unknownMember;
-                    } else if ('@' !== sub[1][0]) {
-                        sub[1] = '@' +sub[1];
-                    }
-                    sub[0] = '#' +sub[0];
-                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkuser;
-                } else if (type === '#') {
-                    if (!sub[1]) {
-                        var chan = SLACK.context.channels[sub[0]];
-                        sub[1] = chan ? ('#' +chan.name) : locale.unknownChannel;
-                    } else if ('#' !== sub[1][0]) {
-                        sub[1] = '#' +sub[1];
-                    }
-                    sub[0] = '#' +sub[0];
-                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkchan;
-                } else if (sub[0].indexOf("://") !== -1) {
-                    if (!sub[1])
-                        sub[1] = sub[0];
-                    sub[2] = R.klass.msg.link;
-                } else {
-                    return matched;
-                }
-                return '<a href="' +sub[0] +'" class="' +sub[2] +'"' +(!type ? ' target="_blank"' : '') +'>' +sub[1] +'</a>';
-            });
-        msgContent = formatEmojis(msgContent);
-        var msgLength = msgContent.length;
-        var isAlphadec = function(c) {
-            return ((c >= 'A' && c <= 'Z') ||
-                (c >= 'a' && c <= 'z') ||
-                (c >= '0' && c <= '9') ||
-                "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ".indexOf(c) !== -1);
-        }
-        ,checkEnd = function(str, pos, c) {
-            while (str[pos]) {
-                if (isAlphadec(str[pos]) && str[pos] != c && str[pos +1] == c) {
-                    return true;
-                }
-                pos++;
-            }
-            return false;
-        } ,appendMod = function(mods) {
-            if (!Object.keys(currentMods).length)
-                return "";
-            return '<span class="' +Object.keys(mods).join(' ') +'">';
-        };
-
-        // Skip trailing
-        while (i < msgLength && (msgContent[i] === ' ' || msgContent[i] === '\t'))
-            i++;
-        if (msgContent.substr(i, 4) === '&gt;') {
-            quote = true;
-            i += 4;
-        }
-        for (; i < msgLength; i++) {
-            var c = msgContent[i];
-
-            if (c === '<') {
-                do {
-                    _msgContent += msgContent[i++];
-                } while (msgContent[i -1] !== '>' && msgContent[i]);
-                i--;
-                continue;
-            }
-            if (!(currentMods[R.klass.msg.style.bold]) && c === '*' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.bold] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.strike]) && c === '~' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.strike] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.code]) && c === '`' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.code] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.italic]) && c === '_' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.italic] = true;
-                _msgContent += appendMod(currentMods);
-            } else {
-                var finalFound = false;
-                _msgContent += c;
-
-                do {
-                    if ((currentMods[R.klass.msg.style.bold]) && c !== '*' && msgContent[i +1] === '*') {
-                        delete currentMods[R.klass.msg.style.bold];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.strike]) && c !== '~' && msgContent[i +1] === '~') {
-                        delete currentMods[R.klass.msg.style.strike];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.code]) && c !== '`' && msgContent[i +1] === '`') {
-                        delete currentMods[R.klass.msg.style.code];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.italic]) && c !== '_' && msgContent[i +1] === '_') {
-                        delete currentMods[R.klass.msg.style.italic];
-                        finalFound = true;
-                    } else {
-                        break;
-                    }
-                    c = msgContent[++i];
-                } while (i < msgLength);
-                if (finalFound)
-                    _msgContent += '</span>' +appendMod(currentMods);
-            }
-        }
-        if (!isObjectEmpty(currentMods)) {
-            // Should not append
-            console.warn("formatter warning");
-            _msgContent += '</span>';
-        }
-        if (quote)
-            msgContents[msgContentIndex] = '<span class="' +R.klass.msg.style.quote +'">' +_msgContent +'</span>';
-        else
-            msgContents[msgContentIndex] = _msgContent;
-    }
-    return msgContents.length === 0 ? "" : msgContents.join('<br/>');
-}
-

+ 127 - 0
cli/osmTile.js

@@ -0,0 +1,127 @@
+
+function makeOSMTiles(geo) {
+    if (geo["latitude"] !== undefined && geo["longitude"] !== undefined &&
+        geo["latitude"] >= -90 && geo["latitude"] <= 90 &&
+        geo["longitude"] >= -180 && geo["longitude"] <= 180) {
+
+        var tileServer = "https://c.tile.openstreetmap.org",
+            currentVersion = 0,
+            getTile = function(zoom, x, y, result) {
+                return new Promise(function(resolve, reject) {
+                    var img = new Image();
+                    img.addEventListener("load", function() {
+                        result.img = img;
+                        resolve(result);
+                    });
+                    img.addEventListener("error", function() {
+                        console.warn("Error loading tile ", {
+                            "zoom":zoom,
+                            "x": x,
+                            "y": y
+                        });
+                        reject(img);
+                    });
+                    img.crossOrigin = "anonymous";
+                    img.src = tileServer + '/' +zoom +'/' +x +'/' +y +'.png';
+                });
+            },
+            canvas = document.createElement("canvas"),
+            mapCanvas = document.createElement("canvas");
+        var imgSize = 300,
+            tileSize = imgSize /3;
+        canvas.height = canvas.width = mapCanvas.height = mapCanvas.width = imgSize;
+        var ctx = canvas.getContext("2d"),
+            mapCtx = mapCanvas.getContext("2d");
+
+        var drawPlot = function(centerX, centerY, radius) {
+            centerX = tileSize *centerX +tileSize;
+            centerY = tileSize *centerY +tileSize;
+
+            ctx.putImageData(mapCtx.getImageData(0, 0, imgSize, imgSize), 0, 0);
+            if (radius !== undefined) {
+                ctx.beginPath();
+                ctx.arc(centerX, centerY, Math.max(radius, 10), 0, 2 * Math.PI, false);
+                ctx.lineWidth = 2;
+                ctx.fillStyle = 'rgba(244, 146, 66, 0.4)';
+                ctx.strokeStyle = 'rgba(244, 146, 66, 0.8)';
+                ctx.stroke();
+                ctx.fill();
+            }
+            if (radius === undefined || radius > 25) {
+                ctx.strokeStyle = 'rgba(244, 146, 66, 1)';
+                ctx.beginPath();
+                ctx.moveTo(centerX -5, centerY -5);
+                ctx.lineTo(centerX +5, centerY +5);
+                ctx.stroke();
+                ctx.moveTo(centerX +5, centerY -5);
+                ctx.lineTo(centerX -5, centerY +5);
+                ctx.stroke();
+            }
+        },
+        distanceLatlonlon = function(lat, lon0, lon1) {
+            lat = lat * Math.PI / 180;
+            lon0 = lon0 *Math.PI / 180;
+            lon1 = lon1 *Math.PI / 180;
+            return Math.abs(6371e3 * Math.acos(Math.pow(Math.sin(lat), 2) +Math.pow(Math.cos(lat), 2) *Math.cos(lon1 -lon0)));
+        },
+        drawTiles = function(zoom, lat, lon, acc) {
+            mapCtx.fillStyle = "#808080";
+            mapCtx.fillRect(0, 0, imgSize, imgSize);
+            ctx.fillStyle = "#808080";
+            ctx.fillRect(0, 0, imgSize, imgSize);
+
+            var nbTiles = Math.pow(2, zoom),
+                px = ((lon +180) / 360) * nbTiles,
+                py = (1 -Math.log(Math.tan(lat *Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180))/Math.PI) / 2 *nbTiles,
+                absoluteX = Math.floor(px),
+                absoluteY = Math.floor(py),
+                accRadiusPx = acc ? acc * 100 /distanceLatlonlon(180/Math.PI*Math.atan(0.5*(Math.exp(Math.PI-2*Math.PI*absoluteY/nbTiles)-Math.exp(-(Math.PI-2*Math.PI*absoluteY/nbTiles)))), absoluteX / nbTiles * 360.0 - 180.0, (absoluteX +1) / nbTiles * 360.0 - 180.0) : 0,
+                v = currentVersion;
+
+            for (var i =0; i < 3; i++)
+                for (var j =0; j < 3; j++)
+                    getTile(zoom, absoluteX +i -1, absoluteY +j -1, { i: i, j: j, v: v }).then(function(img) {
+                        if (img.v === currentVersion) {
+                            mapCtx.drawImage(img.img, tileSize *img.i, tileSize *img.j, tileSize, tileSize);
+                            drawPlot(px -absoluteX, py -absoluteY, accRadiusPx);
+                        }
+                    });
+        },
+        currentZoom,
+        setZoom = function(val) {
+            var newZoom = Math.max(4, Math.min(19, val));
+            if (currentZoom !== newZoom) {
+                currentVersion++;
+                currentZoom = newZoom;
+                drawTiles(currentZoom, Number(geo["latitude"]), Number(geo["longitude"]), Number(geo["accuracy"]));
+            }
+        };
+
+        setZoom(12);
+
+        var canvasWrapper = document.createElement("div"),
+            buttonContainer = document.createElement("div"),
+            zoomP = document.createElement("button"),
+            zoomM = document.createElement("button");
+
+        canvasWrapper.className = R.klass.map.container;
+        canvas.className = R.klass.map.canvas;
+        buttonContainer.className = R.klass.map.buttonContainer;
+        zoomM.className = R.klass.map.buttonM;
+        zoomP.className = R.klass.map.buttonP;
+
+        zoomM.addEventListener("click", function() {
+            setZoom(currentZoom -1);
+        });
+        zoomP.addEventListener("click", function() {
+            setZoom(currentZoom +1);
+        });
+
+        buttonContainer.appendChild(zoomM);
+        buttonContainer.appendChild(zoomP);
+        canvasWrapper.appendChild(canvas);
+        canvasWrapper.appendChild(buttonContainer);
+        return canvasWrapper;
+    }
+}
+

+ 155 - 147
cli/resources.js

@@ -1,162 +1,170 @@
 var R = {
     id: {
-        chanList: "chanList"
-        ,context: "slackCtx"
-        ,chatList: "chatList"
-        ,currentRoom: {
-            title: "currentRoomTitle"
-            ,content: "chatWindow"
-        }
-        ,typing: "whoistyping"
-        ,message: {
-            form: "msgForm"
-            ,slashComplete: "slashList"
-            ,input: "msgInput"
-            ,replyTo: "replyToContainer"
-            ,file: {
-                bt: "attachFile"
-                ,formContainer: "fileUploadContainer"
-                ,fileInput: "fileUploadInput"
-                ,form: "fileUploadForm"
-                ,error: "fileUploadError"
-                ,cancel: "fileUploadCancel"
+        chanList: "chanList",
+        context: "slackCtx",
+        chatList: "chatList",
+        currentRoom: {
+            title: "currentRoomTitle",
+            content: "chatWindow"
+        },
+        typing: "whoistyping",
+        message: {
+            form: "msgForm",
+            slashComplete: "slashList",
+            input: "msgInput",
+            replyTo: "replyToContainer",
+            file: {
+                bt: "attachFile",
+                formContainer: "fileUploadContainer",
+                fileInput: "fileUploadInput",
+                form: "fileUploadForm",
+                error: "fileUploadError",
+                cancel: "fileUploadCancel"
+            },
+            emoji: "emojiButton"
+        },
+        favicon: "linkFavicon"
+    },
+    klass: {
+        selected: "selected",
+        hidden: "hidden",
+        button: "button",
+        buttonContainer: "button-container",
+        noRoomSelected: "no-room-selected",
+        noNetwork: "no-network",
+        unread: "unread",
+        unreadHi: "unreadHi",
+        replyingTo: "replyingTo",
+        presenceAway: "away",
+        typing: {
+            container: "typing-container",
+            dot1: "typing-dot1",
+            dot2: "typing-dot2",
+            dot3: "typing-dot3"
+        },
+        emoji: {
+            emoji: "emoji",
+            small: "emoji-small",
+            medium: "emoji-medium",
+            custom: "emoji-custom"
+        },
+        commands: {
+            item: "slack-command-item",
+            header: "slack-command-header",
+            name: "slack-command-name",
+            usage: "slack-command-usage",
+            desc: "slack-command-desc"
+        },
+        emojibar: {
+            container: "emojibar",
+            emojis: "emojibar-emojis",
+            close: "emojibar-close",
+            header: "emojibar-header",
+            list: "emojibar-list",
+            item: "emojibar-list-item",
+            search: "emojibar-search",
+            overlay: "emojibar-overlay",
+            detail: {
+                container: "emojibar-detail",
+                img: "emojibar-detail-img",
+                name: "emojibar-detail-name"
             }
-            ,emoji: "emojiButton"
+        },
+        chatList: {
+            entry: "slack-context-room",
+            typeChannel: "slack-channel",
+            typePrivate: "slack-group",
+            typeDirect: "slack-ims",
+            typing: "slack-context-typing"
+        },
+        msg: {
+            item: "slackmsg-item",
+            notice: "slackmsg-notice",
+            firstUnread: "slackmsg-first-unread",
+            content: "slackmsg-content",
+            meMessage: "slackmsg-me_message",
+            ts: "slackmsg-ts",
+            author: "slackmsg-author",
+            authorname: "slackmsg-author-name",
+            authorAvatar: "slackmsg-author-img",
+            authorMessages: "slackmsg-author-messages",
+            msg: "slackmsg-msg",
+            editedStatus: "edited",
+            edited: "slackmsg-edited",
 
-        }
-        ,favicon: "linkFavicon"
-    }
-    ,klass: {
-        selected: "selected"
-        ,hidden: "hidden"
-        ,button: "button"
-        ,buttonContainer: "button-container"
-        ,noRoomSelected: "no-room-selected"
-        ,noNetwork: "no-network"
-        ,unread: "unread"
-        ,unreadHi: "unreadHi"
-        ,replyingTo: "replyingTo"
-        ,presenceAway: "away"
-        ,typing: {
-            container: "typing-container"
-            ,dot1: "typing-dot1"
-            ,dot2: "typing-dot2"
-            ,dot3: "typing-dot3"
-        }
-        ,emoji: {
-            emoji: "emoji"
-            ,small: "emoji-small"
-            ,medium: "emoji-medium"
-            ,custom: "emoji-custom"
-        }
-        ,commands: {
-            item: "slack-command-item"
-            ,header: "slack-command-header"
-            ,name: "slack-command-name"
-            ,usage: "slack-command-usage"
-            ,desc: "slack-command-desc"
-        }
-        ,emojibar: {
-            container: "emojibar"
-            ,emojis: "emojibar-emojis"
-            ,close: "emojibar-close"
-            ,header: "emojibar-header"
-            ,list: "emojibar-list"
-            ,item: "emojibar-list-item"
-            ,search: "emojibar-search"
-            ,overlay: "emojibar-overlay"
-            ,detail: {
-                container: "emojibar-detail"
-                ,img: "emojibar-detail-img"
-                ,name: "emojibar-detail-name"
-            }
-        }
-        ,chatList: {
-            entry: "slack-context-room"
-            ,typeChannel: "slack-channel"
-            ,typePrivate: "slack-group"
-            ,typeDirect: "slack-ims"
-            ,typing: "slack-context-typing"
-        }
-        ,msg: {
-            item: "slackmsg-item"
-            ,notice: "slackmsg-notice"
-            ,firstUnread: "slackmsg-first-unread"
-            ,content: "slackmsg-content"
-            ,meMessage: "slackmsg-me_message"
-            ,ts: "slackmsg-ts"
-            ,author: "slackmsg-author"
-            ,authorname: "slackmsg-author-name"
-            ,authorAvatar: "slackmsg-author-img"
-            ,authorMessages: "slackmsg-author-messages"
-            ,msg: "slackmsg-msg"
-            ,edited: "slackmsg-edited"
-
-            ,authorGroup: "slackmsg-authorGroup"
-            ,sameTs: "slackmsg-same-ts"
-            ,hover: {
-                container: "slackmsg-hover"
-                ,reply: "slackmsg-hover-reply"
-                ,reaction: "slackmsg-hover-reaction"
-                ,edit: "slackmsg-hover-edit"
-                ,remove: "slackmsg-hover-remove"
-            }
+            authorGroup: "slackmsg-authorGroup",
+            sameTs: "slackmsg-same-ts",
+            hover: {
+                container: "slackmsg-hover",
+                reply: "slackmsg-hover-reply",
+                reaction: "slackmsg-hover-reaction",
+                edit: "slackmsg-hover-edit",
+                remove: "slackmsg-hover-remove"
+            },
 
-            ,replyTo: {
+            replyTo: {
                 close: "replyto-close"
-            }
+            },
 
-            ,link: "slackmsg-link"
-            ,linkuser: "slackmsg-link-user"
-            ,linkchan: "slackmsg-link-chan"
+            link: "slackmsg-link",
+            linkuser: "slackmsg-link-user",
+            linkchan: "slackmsg-link-chan",
 
-            ,attachment: {
-                container: "slackmsg-attachment"
-                ,list: "slackmsg-attachments"
+            attachment: {
+                container: "slackmsg-attachment",
+                list: "slackmsg-attachments",
 
-                ,hasThumb: "has-thumb"
-                ,pretext: "slackmsg-attachment-pretext"
-                ,block: "slackmsg-attachment-block"
-                ,title: "slackmsg-attachment-title"
-                ,content: "slackmsg-attachment-content"
-                ,text: "slackmsg-attachment-text"
-                ,thumbImg: "slackmsg-attachment-thumb"
-                ,img: "slackmsg-attachment-img"
-                ,actions: "slackmsg-attachment-actions"
-                ,actionItem: "slackmsg-attachment-actions-item"
-                ,field: {
-                    container: "slackmsg-attachment-fields"
-                    ,item: "field"
-                    ,title: "field-title"
-                    ,text: "field-text"
-                    ,longField: "field-long"
-                }
-                ,footer: "slackmsg-attachment-footer"
-                ,footerText: "slackmsg-attachment-footer-text"
-                ,footerIcon: "slackmsg-attachment-footer-icon"
-            }
+                hasThumb: "has-thumb",
+                pretext: "slackmsg-attachment-pretext",
+                block: "slackmsg-attachment-block",
+                title: "slackmsg-attachment-title",
+                content: "slackmsg-attachment-content",
+                text: "slackmsg-attachment-text",
+                thumbImg: "slackmsg-attachment-thumb",
+                img: "slackmsg-attachment-img",
+                actions: "slackmsg-attachment-actions",
+                actionItem: "slackmsg-attachment-actions-item",
+                field: {
+                    container: "slackmsg-attachment-fields",
+                    item: "field",
+                    title: "field-title",
+                    text: "field-text",
+                    longField: "field-long"
+                },
+                footer: "slackmsg-attachment-footer",
+                footerText: "slackmsg-attachment-footer-text",
+                footerIcon: "slackmsg-attachment-footer-icon"
+            },
 
-            ,reactions: {
-                container: "slackmsg-reactions"
-                ,item: "slackmsg-reaction-item"
-            }
+            reactions: {
+                container: "slackmsg-reactions",
+                item: "slackmsg-reaction-item"
+            },
 
-            ,style: {
-                bold: "slackmsg-style-bold"
-                ,code: "slackmsg-style-code"
-                ,italic: "slackmsg-style-italic"
-                ,strike: "slackmsg-style-strike"
-                ,quote: "slackmsg-style-quote"
+            style: {
+                bold: "slackmsg-style-bold",
+                code: "slackmsg-style-code",
+                longCode: "slackmsg-style-longcode",
+                italic: "slackmsg-style-italic",
+                strike: "slackmsg-style-strike",
+                quote: "slackmsg-style-quote"
             }
-        }
-        ,dialog: {
-            container: "dialog"
-            ,overlay: "dialog-overlay"
-            ,title: "dialog-title"
-            ,titleLabel: "dialog-title-label"
-            ,closeButton: "dialog-title-close"
-            ,body: "dialog-body"
-            ,buttonBar: "dialog-footer"
+        },
+        dialog: {
+            container: "dialog",
+            overlay: "dialog-overlay",
+            title: "dialog-title",
+            titleLabel: "dialog-title-label",
+            closeButton: "dialog-title-close",
+            body: "dialog-body",
+            buttonBar: "dialog-footer"
+        },
+        map: {
+            container: "OSM-wrapper",
+            canvas: "OSM-canvas",
+            buttonContainer: "OSM-controls",
+            buttonM: "OSM-controls-zoomMin",
+            buttonP: "OSM-controls-zoomPlus"
         }
     }
 };

+ 216 - 206
cli/ui.js

@@ -4,60 +4,58 @@ var
      * @const
      * @type {number}
     **/
-    NOTIFICATION_COOLDOWN = 30 * 1000 //30 sec
+    NOTIFICATION_COOLDOWN = 30 * 1000, //30 sec
 
     /**
      * Maximum time the notification will stay visible (ms)
      * @const
      * @type {number}
     **/
-    ,NOTIFICATION_DELAY = 5 * 1000 // 5 sec
+    NOTIFICATION_DELAY = 5 * 1000, // 5 sec
 
-    ,MSG_GROUPS = []
+    MSG_GROUPS = [],
 
     /** @type {number} */
-    ,lastNotificationSpawn = 0
-
-;
+    lastNotificationSpawn = 0;
 
 function onContextUpdated() {
-    var chanListFram = document.createDocumentFragment()
-        ,sortedChans = Object.keys(SLACK.context.channels || {})
-        ,starred = []
-        ,channels = []
-        ,privs = []
-        ,priv = [];
+    var chanListFram = document.createDocumentFragment(),
+        sortedChans = DATA.context.getChannelIds(function(chan) {
+            return !chan.archived && chan.isMember !== false;
+        }),
+        starred = [],
+        channels = [],
+        privs = [],
+        priv = [];
 
     sortedChans.sort(function(a, b) {
         if (a[0] !== b[0]) {
             return a[0] - b[0];
         }
-        return SLACK.context.channels[a].name.localeCompare(SLACK.context.channels[b].name);
+        return DATA.context.getChannel(a).name.localeCompare(DATA.context.getChannel(b).name);
     });
     sortedChans.forEach(function(chanId) {
-        var chan = SLACK.context.channels[chanId];
-        if (!chan.archived && chan.isMember !== false) {
-            if (chan instanceof PrivateMessageRoom) {
-                if (!chan.user.deleted) {
-                    var chanListItem = createImsListItem(chan);
-                    if (chanListItem) {
-                        if (chan.starred)
-                            starred.push(chanListItem);
-                        else
-                            priv.push(chanListItem);
-                    }
-                }
-            } else {
-                var chanListItem = createChanListItem(chan);
-                if (chanListItem) {
+        var chan = DATA.context.getChannel(chanId),
+            chanListItem;
+        if (chan instanceof PrivateMessageRoom) {
+            if (!chan.user.deleted) {
+                if ((chanListItem = createImsListItem(chan))) {
                     if (chan.starred)
                         starred.push(chanListItem);
-                    else if (chan.isPrivate)
-                        privs.push(chanListItem);
                     else
-                        channels.push(chanListItem);
+                        priv.push(chanListItem);
                 }
             }
+            //FIXME else remove
+        } else {
+            if ((chanListItem = createChanListItem(chan))) {
+                if (chan.starred)
+                    starred.push(chanListItem);
+                else if (chan.isPrivate)
+                    privs.push(chanListItem);
+                else
+                    channels.push(chanListItem);
+            }
         }
     });
     if (starred.length)
@@ -82,45 +80,50 @@ function onContextUpdated() {
     document.getElementById(R.id.chanList).appendChild(chanListFram);
     setRoomFromHashBang();
     updateTitle();
-    createContextBackground(function(imgData) {
-        document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
-    });
+    if (SELECTED_CONTEXT) {
+        createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) {
+            document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
+        });
+    }
 }
 
 function onTypingUpdated() {
-    var typing = SLACK.context.typing;
-    for (var chanId in SLACK.context.self.channels) {
-        if (!SLACK.context.self.channels[chanId].archived) {
-            var dom = document.getElementById("room_" +chanId);
-            if (typing[chanId])
-                dom.classList.add(R.klass.chatList.typing);
-            else
-                dom.classList.remove(R.klass.chatList.typing);
-        }
-    }
-    for (var userId in SLACK.context.users) {
-        var ims = SLACK.context.users[userId].privateRoom;
-        if (ims && !ims.archived) {
-            var dom = document.getElementById("room_" +ims.id);
-            if (dom) {
-                if (typing[ims.id])
-                    dom.classList.add(R.klass.chatList.typing);
+    DATA.context.foreachContext(function(ctx) {
+        var typing = ctx.typing;
+        for (var chanId in ctx.self.channels) {
+            if (!ctx.self.channels[chanId].archived) {
+                var chanDom = document.getElementById("room_" +chanId);
+                if (typing[chanId])
+                    chanDom.classList.add(R.klass.chatList.typing);
                 else
-                    dom.classList.remove(R.klass.chatList.typing);
+                    chanDom.classList.remove(R.klass.chatList.typing);
             }
         }
-    }
+        for (var userId in ctx.users) {
+            var ims = ctx.users[userId].privateRoom;
+            if (ims && !ims.archived) {
+                var userDom = document.getElementById("room_" +ims.id);
+                if (userDom) {
+                    if (typing[ims.id])
+                        userDom.classList.add(R.klass.chatList.typing);
+                    else
+                        userDom.classList.remove(R.klass.chatList.typing);
+                }
+            }
+        }
+    });
     updateTypingChat();
 }
 
 function updateTypingChat() {
-    var typing = SLACK.context.typing;
+    var typing;
+
     document.getElementById(R.id.typing).textContent = "";
-    if (SELECTED_ROOM && typing[SELECTED_ROOM.id]) {
-        var areTyping = document.createDocumentFragment()
-            ,isOutOfSync = false;
-        for (var i in typing[SELECTED_ROOM.id]) {
-            var member = SLACK.context.users[i];
+    if (SELECTED_CONTEXT && SELECTED_ROOM && (typing = SELECTED_CONTEXT.getChatContext().typing[SELECTED_ROOM.id])) {
+        var areTyping = document.createDocumentFragment(),
+            isOutOfSync = false;
+        for (var i in typing) {
+            var member = DATA.context.getUser(i);
             if (member)
                 areTyping.appendChild(makeUserIsTypingDom(member));
             else
@@ -133,7 +136,10 @@ function updateTypingChat() {
 }
 
 function onNetworkStateUpdated(isNetworkWorking) {
-    isNetworkWorking ? document.body.classList.remove(R.klass.noNetwork) : document.body.classList.add(R.klass.noNetwork);
+    if (isNetworkWorking)
+        document.body.classList.remove(R.klass.noNetwork);
+    else
+        document.body.classList.add(R.klass.noNetwork);
     updateTitle();
 }
 
@@ -168,8 +174,8 @@ function onRoomSelected() {
 function onReplyingToUpdated() {
     if (REPLYING_TO) {
         document.body.classList.add(R.klass.replyingTo);
-        var domParent = document.getElementById(R.id.message.replyTo)
-            ,closeLink = document.createElement("a");
+        var domParent = document.getElementById(R.id.message.replyTo),
+            closeLink = document.createElement("a");
         closeLink.addEventListener("click", function() {
             REPLYING_TO = null;
             onReplyingToUpdated();
@@ -178,7 +184,7 @@ function onReplyingToUpdated() {
         closeLink.textContent = 'x';
         domParent.textContent = "";
         domParent.appendChild(closeLink);
-        domParent.appendChild(createMessageDom("reply_" +SELECTED_ROOM.id, REPLYING_TO, true));
+        domParent.appendChild(REPLYING_TO.duplicateDom());
         focusInput();
     } else {
         document.body.classList.remove(R.klass.replyingTo);
@@ -190,8 +196,8 @@ function onReplyingToUpdated() {
 function onEditingUpdated() {
     if (EDITING) {
         document.body.classList.add(R.klass.replyingTo);
-        var domParent = document.getElementById(R.id.message.replyTo)
-            ,closeLink = document.createElement("a");
+        var domParent = document.getElementById(R.id.message.replyTo),
+            closeLink = document.createElement("a");
         closeLink.addEventListener("click", function() {
             EDITING = null;
             onEditingUpdated();
@@ -200,7 +206,7 @@ function onEditingUpdated() {
         closeLink.textContent = 'x';
         domParent.textContent = "";
         domParent.appendChild(closeLink);
-        domParent.appendChild(createMessageDom("edit_" +SELECTED_ROOM.id, EDITING, true));
+        domParent.appendChild(EDITING.duplicateDom());
         document.getElementById(R.id.message.input).value = EDITING.text;
         focusInput();
     } else {
@@ -216,12 +222,11 @@ function onEditingUpdated() {
  * @param {string} reaction
 **/
 window['toggleReaction'] = function(chanId, msgId, reaction) {
-    var hist = SLACK.history[chanId];
-    if (!hist)
-        return;
-    var msg = hist.getMessageById(msgId);
-    if (msg) {
-        if (msg.hasReactionForUser(reaction, SLACK.context.self.id)) {
+    var hist = DATA.history[chanId],
+        msg,
+        ctx;
+    if ((hist = DATA.history[chanId]) && (msg = hist.getMessageById(msgId)) && (ctx = DATA.context.getChannelContext(chanId))) {
+        if (msg.hasReactionForUser(reaction, ctx.getChatContext().self.id)) {
             removeReaction(chanId, msgId, reaction);
         } else {
             addReaction(chanId, msgId, reaction);
@@ -237,20 +242,24 @@ window['toggleReaction'] = function(chanId, msgId, reaction) {
 function tryGetCustomEmoji(emoji) {
     var loop = {};
 
-    while (!loop[emoji]) {
-        var emojisrc= SLACK.context.emojis.data[emoji];
-        if (emojisrc) {
-            if (emojisrc.substr(0, 6) == "alias:") {
-                loop[emoji] = true;
-                emoji = emojisrc.substr(6);
-            } else {
-                var dom = document.createElement("span");
-                dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji;
-                dom.style.backgroundImage = "url('" +emojisrc +"')";
-                return dom;
+    if (SELECTED_CONTEXT) {
+        var ctx = SELECTED_CONTEXT.getChatContext();
+
+        while (!loop[emoji]) {
+            var emojisrc= ctx.emojis.data[emoji];
+            if (emojisrc) {
+                if (emojisrc.substr(0, 6) == "alias:") {
+                    loop[emoji] = true;
+                    emoji = emojisrc.substr(6);
+                } else {
+                    var dom = document.createElement("span");
+                    dom.className = R.klass.emoji.custom +' ' +R.klass.emoji.emoji;
+                    dom.style.backgroundImage = "url('" +emojisrc +"')";
+                    return dom;
+                }
             }
+            return emoji; // Emoji not found, fallback to std emoji
         }
-        return emoji; // Emoji not found, fallback to std emoji
     }
     return emoji; //loop detected, return first emoji
 }
@@ -263,35 +272,6 @@ function makeEmojiDom(emojiCode) {
     return typeof emoji === "string" ? null : emoji;
 }
 
-/**
- * @param {string} channelId
- * @param {Message} msg
- * @param {boolean=} skipAttachment
- * @return {Element}
-**/
-function doCreateMeMessageDom(channelId, msg, skipAttachment) {
-    var dom = doCreateMessageDom(channelId, msg, skipAttachment);
-    dom.classList.add(R.klass.msg.meMessage);
-    return dom;
-}
-
-/**
- * @param {string} channelId
- * @param {Message} msg
- * @param {boolean=} skipAttachment
- * @return {Element}
-**/
-function createMessageDom(channelId, msg, skipAttachment) {
-    var dom = (msg instanceof MeMessage) ?
-        doCreateMeMessageDom(channelId, msg, skipAttachment):
-        doCreateMessageDom(channelId, msg, skipAttachment);
-    if (msg.edited)
-        dom.classList.add(R.klass.msg.edited);
-    if (msg instanceof NoticeMessage)
-        dom.classList.add(R.klass.msg.notice);
-    return dom;
-}
-
 /**
  * @param {number} unreadhi
  * @param {number} unread
@@ -308,8 +288,8 @@ function setNetErrorFavicon() {
 }
 
 function updateTitle() {
-    var hasHl = HIGHLIGHTED_CHANS.length
-        ,title = "";
+    var hasHl = HIGHLIGHTED_CHANS.length,
+        title = "";
 
     if (NEXT_RETRY) {
         title = '!' +locale.netErrorShort +' - ';
@@ -320,17 +300,16 @@ function updateTitle() {
         setFavicon(hasHl, hasHl);
     } else {
         var hasUnread = 0;
-        for (var chanId in SLACK.context.channels) {
-            var i = SLACK.context.channels[chanId];
+        DATA.context.foreachChannels(function(i) {
             if (i.lastMsg > i.lastRead)
                 hasUnread++;
-        }
+        });
         if (hasUnread)
             title = "(" +hasUnread +") - ";
         setFavicon(0, hasUnread);
     }
-    if (SLACK.context.team)
-        title += SLACK.context.team.name;
+    if (DATA.context.team)
+        title += DATA.context.team.name;
     document.title = title;
 }
 
@@ -353,19 +332,19 @@ function spawnNotification() {
 }
 
 function onRoomUpdated() {
-    var chatFrag = document.createDocumentFragment()
-        ,currentRoomId = SELECTED_ROOM.id
-        ,prevMsg = null
-        ,firstTsCombo = 0
-        ,prevMsgDom = null
-        ,currentMsgGroupDom;
+    var chatFrag = document.createDocumentFragment(),
+        currentRoomId = SELECTED_ROOM.id,
+        prevMsg = null,
+        firstTsCombo = 0,
+        prevMsgDom = null,
+        currentMsgGroupDom;
 
     MSG_GROUPS = [];
-    if (SLACK.history[currentRoomId])
-        SLACK.history[currentRoomId].messages.forEach(function(msg) {
+    if (DATA.history[currentRoomId])
+        DATA.history[currentRoomId].messages.forEach(function(msg) {
             if (!msg.removed) {
-                var dom = createMessageDom(currentRoomId, msg)
-                    ,newGroupDom = false;
+                var dom = msg.getDom(),
+                    newGroupDom = false;
                 if (prevMsg && prevMsg.userId === msg.userId && msg.userId) {
                     if (Math.abs(firstTsCombo -msg.ts) < 30 && !(msg instanceof MeMessage))
                         prevMsgDom.classList.add(R.klass.msg.sameTs);
@@ -377,6 +356,8 @@ function onRoomUpdated() {
                 }
                 if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead)
                     dom.classList.add(R.klass.msg.firstUnread);
+                else
+                    dom.classList.remove(R.klass.msg.firstUnread);
                 if (msg instanceof MeMessage) {
                     prevMsg = null;
                     prevMsgDom = null;
@@ -386,7 +367,7 @@ function onRoomUpdated() {
                     currentMsgGroupDom = null;
                 } else {
                     if (newGroupDom || !currentMsgGroupDom) {
-                        currentMsgGroupDom = createMessageGroupDom(SLACK.context.users[msg.userId], msg.username);
+                        currentMsgGroupDom = createMessageGroupDom(DATA.context.getUser(msg.userId), msg.username);
                         MSG_GROUPS.push(currentMsgGroupDom);
                         chatFrag.appendChild(currentMsgGroupDom);
                     }
@@ -394,6 +375,8 @@ function onRoomUpdated() {
                     prevMsgDom = dom;
                     currentMsgGroupDom.content.appendChild(dom);
                 }
+            } else {
+                msg.removeDom();
             }
         });
     var content = document.getElementById(R.id.currentRoom.content);
@@ -406,12 +389,53 @@ function onRoomUpdated() {
         markRoomAsRead(SELECTED_ROOM);
 }
 
+function onMsgClicked(target, msg) {
+    if (target.classList.contains(R.klass.msg.hover.reply)) {
+        if (EDITING) {
+            EDITING = null;
+            onEditingUpdated();
+        }
+        if (REPLYING_TO !== msg) {
+            REPLYING_TO = msg;
+            onReplyingToUpdated();
+        }
+    } else if (target.classList.contains(R.klass.msg.hover.reaction)) {
+        EMOJI_BAR.spawn(document.body, {
+                selectedRoomId: SELECTED_ROOM.id,
+                msgId: msg.id
+            }, function(emoji) {
+                if (emoji)
+                    addReaction(this.selectedRoomId, this.msgId, emoji);
+        });
+    } else if (target.classList.contains(R.klass.msg.hover.edit)) {
+        if (REPLYING_TO) {
+            REPLYING_TO = null;
+            onReplyingToUpdated();
+        }
+        if (EDITING !== msg) {
+            EDITING = msg;
+            onEditingUpdated();
+        }
+    } else if (target.classList.contains(R.klass.msg.hover.remove)) {
+        //TODO prompt confirm
+        if (REPLYING_TO) {
+            REPLYING_TO = null;
+            onReplyingToUpdated();
+        }
+        if (EDITING) {
+            EDITING = null;
+            onEditingUpdated();
+        }
+        removeMsg(SELECTED_ROOM, msg);
+    }
+}
+
 function chatClickDelegate(e) {
-    var target = e.target
-        ,getMessageId = function(e, target) {
+    var target = e.target,
+        getMessageId = function(e, target) {
             target = target || e.target;
             while (target !== e.currentTarget && target) {
-                if (target.classList.contains(R.klass.msg.item)) {
+                if (target.id && target.classList.contains(R.klass.msg.item)) {
                     return target.id;
                 }
                 target = target.parentElement;
@@ -420,66 +444,38 @@ function chatClickDelegate(e) {
     while (target !== e.currentTarget && target) {
         if (target.classList.contains(R.klass.msg.hover.container)) {
             return;
-        } else if (target.parentElement && target.classList.contains(R.klass.msg.attachment.actionItem)) {
-            var messageId = getMessageId(e, target)
-                ,attachmentIndex = target.dataset.attachmentIndex
-                ,actionIndex = target.dataset.actionIndex;
+        }
+        var messageId,
+            msg;
+        if (target.parentElement && target.classList.contains(R.klass.msg.attachment.actionItem)) {
+            var attachmentIndex = target.dataset["attachmentIndex"],
+                actionIndex = target.dataset["actionIndex"];
 
+            messageId = getMessageId(e, target);
             if (messageId && attachmentIndex !== undefined && actionIndex !== undefined) {
-                messageId = parseFloat(messageId.split("_")[1]);
-                var msg = SLACK.history[SELECTED_ROOM.id].getMessage(messageId);
+                messageId = messageId.substr(messageId.lastIndexOf("_") +1);
+                msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);
 
                 if (msg && msg.attachments[attachmentIndex] && msg.attachments[attachmentIndex].actions && msg.attachments[attachmentIndex].actions[actionIndex]) {
                     confirmAction(SELECTED_ROOM.id, msg, msg.attachments[attachmentIndex], msg.attachments[attachmentIndex].actions[actionIndex]);
                 }
+                return;
             }
-        } else if (target.parentElement && target.parentElement.classList.contains(R.klass.msg.hover.container)) {
-            var messageId = getMessageId(e, target);
-            if (messageId) {
-                messageId = parseFloat(messageId.split("_")[1]);
-                var msg = SLACK.history[SELECTED_ROOM.id].getMessage(messageId);
-
-                if (msg && target.classList.contains(R.klass.msg.hover.reply)) {
-                    if (EDITING) {
-                        EDITING = null;
-                        onEditingUpdated();
-                    }
-                    if (REPLYING_TO !== msg) {
-                        REPLYING_TO = msg;
-                        onReplyingToUpdated();
-                    }
-                } else if (msg && target.classList.contains(R.klass.msg.hover.reaction)) {
-                    EMOJI_BAR.spawn(document.body, function(emoji) {
-                        if (emoji)
-                            addReaction(SELECTED_ROOM.id, msg.id, emoji);
-                    });
-                } else if (msg && target.classList.contains(R.klass.msg.hover.edit)) {
-                    if (REPLYING_TO) {
-                        REPLYING_TO = null;
-                        onReplyingToUpdated();
-                    }
-                    if (EDITING !== msg) {
-                        EDITING = msg;
-                        onEditingUpdated();
-                    }
-                } else if (msg && target.classList.contains(R.klass.msg.hover.remove)) {
-                    //TODO promt confirm
-                    if (REPLYING_TO) {
-                        REPLYING_TO = null;
-                        onReplyingToUpdated();
-                    }
-                    if (EDITING) {
-                        EDITING = null;
-                        onEditingUpdated();
-                    }
-                    removeMsg(SELECTED_ROOM, msg);
-                }
+        }
+        if (target.parentElement && target.parentElement.classList.contains(R.klass.msg.hover.container)) {
+            if ((messageId = getMessageId(e, target))) {
+                messageId = messageId.substr(messageId.lastIndexOf("_") +1);
+                msg = DATA.history[SELECTED_ROOM.id].getMessageById(messageId);
+
+                if (msg)
+                    onMsgClicked(target, msg);
             }
             return;
         }
         target = target.parentElement;
     }
 }
+
 function confirmAction(roomId, msg, attachmentObject, actionObject) {
     var confirmed = function() {
         var payload = getActionPayload(roomId, msg, attachmentObject, actionObject);
@@ -501,27 +497,27 @@ function focusInput() {
 }
 
 function setRoomFromHashBang() {
-    var hashId = document.location.hash.substr(1)
-        ,room = SLACK.context.channels[hashId];
+    var hashId = document.location.hash.substr(1),
+        room = DATA.context.getChannel(hashId);
 
     if (room && room !== SELECTED_ROOM)
         selectRoom(room);
     else {
-        var user = SLACK.context.users[hashId];
+        var user = DATA.context.getUser(hashId);
         if (user && user.ims)
             selectRoom(user.ims);
     }
 }
 
 function updateAuthorAvatarImsOffset() {
-    var chatDom = document.getElementById(R.id.currentRoom.content)
-        ,chatTop = chatDom.getBoundingClientRect().top;
+    var chatDom = document.getElementById(R.id.currentRoom.content),
+        chatTop = chatDom.getBoundingClientRect().top;
 
     MSG_GROUPS.forEach(function(group) {
-        var imgDom = group.authorImg
-            ,imgSize = imgDom.clientHeight
-            ,domRect = group.getBoundingClientRect()
-            ,_top = 0;
+        var imgDom = group.authorImg,
+            imgSize = imgDom.clientHeight,
+            domRect = group.getBoundingClientRect(),
+            _top = 0;
 
         imgDom.style.top = Math.max(0, Math.min(chatTop -domRect.top, domRect.height -imgSize -(imgSize /2))) +"px";
     });
@@ -529,6 +525,10 @@ function updateAuthorAvatarImsOffset() {
 
 document.addEventListener('DOMContentLoaded', function() {
     initLang();
+
+    // FIXME load config
+    initHljs();
+
     document.getElementById(R.id.currentRoom.content).addEventListener("click", chatClickDelegate);
     window.addEventListener("hashchange", function(e) {
         if (document.location.hash && document.location.hash[0] === '#') {
@@ -544,8 +544,8 @@ document.addEventListener('DOMContentLoaded', function() {
     });
     document.getElementById(R.id.message.file.form).addEventListener("submit", function(e) {
         e.preventDefault();
-        var fileInput = document.getElementById(R.id.message.file.fileInput)
-            ,filename = fileInput.value;
+        var fileInput = document.getElementById(R.id.message.file.fileInput),
+            filename = fileInput.value;
 
         if (filename) {
             filename = filename.substr(filename.lastIndexOf('\\') +1);
@@ -606,20 +606,29 @@ document.addEventListener('DOMContentLoaded', function() {
     document.getElementById(R.id.message.input).addEventListener('input', function() {
         if (SELECTED_ROOM) {
             var now = Date.now();
-            if (lastKeyDown + 3000 < now && (SLACK.context.self.presence || (SELECTED_ROOM instanceof PrivateMessageRoom))) {
+            if (lastKeyDown + 3000 < now && (SELECTED_CONTEXT.getChatContext().self.presence || (SELECTED_ROOM instanceof PrivateMessageRoom))) {
                 sendTyping(SELECTED_ROOM);
                 lastKeyDown = now;
             }
-            var commands = []
-                ,input = this.value;
+            var /** @type {Array<Command|{names: Array<string>!, desc: string!, usage: string!, category: string!, exec: Function!}>} */
+                commands = [],
+                input = this.value;
             if (this.value[0] === '/') {
-                var endCmd = input.indexOf(' ')
-                    ,inputFinished = endCmd !== -1;
+                var endCmd = input.indexOf(' '),
+                    inputFinished = endCmd !== -1;
                 endCmd = endCmd === -1 ? input.length : endCmd;
                 var inputCmd = input.substr(0, endCmd);
 
-                for (var i in SLACK.context.commands.data) {
-                    var currentCmd = SLACK.context.commands.data[i]
+                if (inputFinished) {
+                    var currentClientCmd = CLIENT_COMMANDS.getCommand(inputCmd);
+                    if (currentClientCmd)
+                        commands.push(currentClientCmd);
+                } else {
+                    commands = CLIENT_COMMANDS.getCommandsStartingWith(inputCmd);
+                }
+                var availableCommands = (SELECTED_CONTEXT ? SELECTED_CONTEXT.getChatContext().commands.data : {});
+                for (var currentCmdId in availableCommands) {
+                    var currentCmd = availableCommands[currentCmdId];
                     if ((!inputFinished && currentCmd.name.substr(0, endCmd) === inputCmd) ||
                         (inputFinished && currentCmd.name === inputCmd))
                         commands.push(currentCmd);
@@ -628,9 +637,9 @@ document.addEventListener('DOMContentLoaded', function() {
             commands.sort(function(a, b) {
                 return a.category.localeCompare(b.category) || a.name.localeCompare(b.name);
             });
-            var slashDom = document.getElementById(R.id.message.slashComplete)
-                ,slashFrag = document.createDocumentFragment()
-                ,prevService;
+            var slashDom = document.getElementById(R.id.message.slashComplete),
+                slashFrag = document.createDocumentFragment(),
+                prevService;
             slashDom.textContent = '';
             for (var i =0, nbCmd = commands.length; i < nbCmd; i++) {
                 var command = commands[i];
@@ -666,10 +675,11 @@ document.addEventListener('DOMContentLoaded', function() {
             }
 
             emojiButton.addEventListener("click", function() {
-                EMOJI_BAR.spawn(document.body, function(e) {
-                    if (e) document.getElementById(R.id.message.input).value += ":"+e+":";
-                    focusInput();
-                });
+                if (SELECTED_CONTEXT)
+                    EMOJI_BAR.spawn(document.body, SELECTED_CONTEXT.getChatContext(), function(e) {
+                        if (e) document.getElementById(R.id.message.input).value += ":"+e+":";
+                        focusInput();
+                    });
             });
         } else {
             emojiButton.classList.add(R.klass.hidden);

+ 434 - 0
cli/uiMessage.js

@@ -0,0 +1,434 @@
+/* jshint sub: true */
+
+/**
+ * @constructor
+ * @extends {RoomHistory}
+ * @param {Room|string} room or roomId
+ * @param {number} keepMessages number of messages to keep in memory
+ * @param {Array|undefined} evts
+ * @param {number|undefined} now
+**/
+function UiRoomHistory(room, keepMessages, evts, now) {
+    RoomHistory.call(this, room, keepMessages, evts, now);
+}
+UiRoomHistory.prototype = Object.create(RoomHistory.prototype);
+UiRoomHistory.prototype.constructor = UiRoomHistory;
+
+UiRoomHistory.prototype.messageFactory = function(ev, ts) {
+    if (ev["isMeMessage"] === true)
+        return new UiMeMessage(this.id, ev, ts);
+    if (ev["isNotice"] === true)
+        return new UiNoticeMessage(this.id, ev, ts);
+    return new UiMessage(this.id, ev, ts);
+};
+
+/** @interface */
+function IUiMessage() {}
+
+/**
+ * @return {Element}
+**/
+IUiMessage.prototype.getDom = function() {};
+
+/**
+ * @return {IUiMessage}
+**/
+IUiMessage.prototype.removeDom = function() {};
+
+/**
+ * @return {IUiMessage}
+**/
+IUiMessage.prototype.invalidate = function() {};
+
+/**
+ * @return {IUiMessage}
+**/
+IUiMessage.prototype.createDom = function() {};
+
+/**
+ * @return {IUiMessage}
+**/
+IUiMessage.prototype.updateDom = function() {};
+
+/**
+ * @return {Element}
+**/
+IUiMessage.prototype.duplicateDom = function() {};
+
+/** @const */
+var AbstractUiMessage = (function() {
+    var updateReactions = function(_this, channelId) {
+        var reactionFrag = document.createDocumentFragment();
+
+        if (_this.reactions) {
+            for (var reaction in _this.reactions) {
+                var reac = createReactionDom(channelId, _this.id, reaction, _this.reactions[reaction]);
+                if (reac)
+                    reactionFrag.appendChild(reac);
+            }
+        }
+        _this.dom.reactions.textContent = '';
+        _this.dom.reactions.appendChild(reactionFrag);
+    },
+
+    _linkFilter = function(msgContext, str) {
+        var sep = str.indexOf('|'),
+            link,
+            text,
+            isInternal = false;
+
+        if (sep === -1) {
+            link = str;
+        } else {
+            link = str.substr(0, sep);
+            text = str.substr(sep +1);
+        }
+        var newLink;
+        if (link[0] === '@') {
+            newLink = msgContext.context.getId() +'|' +link.substr(1);
+            var user = DATA.context.getUser(newLink);
+
+            if (user) {
+                isInternal = true;
+                link = '#' +user.privateRoom.id;
+                text = '@' +user.name;
+            } else {
+                return null;
+            }
+        } else if (link[0] === '#') {
+            newLink = msgContext.context.getId() +'|' +link.substr(1);
+            var chan = DATA.context.getChannel(newLink);
+
+            if (chan) {
+                isInternal = true;
+                link = '#' +newLink;
+                text = '#' +chan.name;
+            } else {
+                return null;
+            }
+        } else {
+            if (!link.match(/^(https?|mailto):\/\//i))
+                return null;
+            isInternal = false;
+        }
+        return {
+            link: link,
+            text: text || link,
+            isInternal: isInternal
+        };
+    },
+
+    _formatText = function(_this, text) {
+        return formatText(text, {
+            highlights: _this.context.self.prefs.highlights,
+            emojiFormatFunction: function(emoji) {
+                if (emoji[0] === ':' && emoji[emoji.length -1] === ':')
+                    emoji = emoji.substr(1, emoji.length -2);
+                var emojiDom = makeEmojiDom(emoji);
+                if (emojiDom) {
+                    var domParent = document.createElement("span");
+                    domParent.className = R.klass.emoji.small;
+                    domParent.appendChild(emojiDom);
+                    return domParent.outerHTML;
+                }
+                return null;
+            },
+            linkFilter: function(link) {
+                return _linkFilter(_this, link);
+            }
+        });
+    },
+
+    updateAttachments = function(_this, channelId) {
+        var attachmentFrag = document.createDocumentFragment();
+
+        for (var i =0, nbAttachments = _this.attachments.length; i < nbAttachments; i++) {
+            var attachment = _this.attachments[i];
+            if (attachment) {
+                var domAttachment = createAttachmentDom(channelId, _this, attachment, i);
+                if (domAttachment)
+                    attachmentFrag.appendChild(domAttachment);
+            }
+        }
+        _this.dom.attachments.textContent = '';
+        _this.dom.attachments.appendChild(attachmentFrag);
+    },
+
+    updateCommon = function(_this, sender) {
+        _this.dom.ts.innerHTML = locale.formatDate(_this.ts);
+        _this.dom.textDom.innerHTML = _formatText(_this, _this.text);
+        _this.dom.authorName.textContent = sender ? sender.name : (_this.username || "?");
+    };
+
+    return {
+        /** @type {Element|null} *
+        dom: null,
+
+        /** @type {boolean} *
+        uiNeedRefresh: true,
+        */
+
+        /** @param {IUiMessage} _this */
+        invalidate: function(_this) {
+            _this.uiNeedRefresh = true;
+            return _this;
+        },
+
+        /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
+        removeDom: function(_this) {
+            if (_this.dom && _this.dom.parentElement) {
+                _this.dom.remove();
+                delete(_this.dom);
+            }
+            return _this;
+        },
+
+        /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
+        getDom: function(_this) {
+            if (!_this.dom) {
+                _this.createDom().updateDom();
+
+            } else if (_this.uiNeedRefresh) {
+                _this.uiNeedRefresh = false;
+                _this.updateDom();
+            }
+            return _this.dom;
+        },
+
+        /** @param {UiMessage|UiMeMessage|UiNoticeMessage} _this */
+        updateDom: function(_this) {
+            var sender = DATA.context.getUser(_this.userId);
+
+            updateCommon(_this, sender);
+            updateAttachments(_this, _this.channelId);
+            updateReactions(_this, _this.channelId);
+            if (_this.edited) {
+                _this.dom.edited.innerHTML = locale.edited(_this.edited);
+                _this.dom.classList.add(R.klass.msg.editedStatus);
+            }
+            return _this;
+        },
+
+        duplicateDom: function(_this) {
+            return _this.dom.cloneNode(true);
+        },
+
+        formatText: function(_this, text) {
+            return _formatText(_this, text);
+        }
+    };
+})();
+
+/**
+ * @constructor
+ * @implements {IUiMessage}
+ * @extends {MeMessage}
+ * @param {*} ev
+ * @param {number} ts
+**/
+function UiMeMessage(channelId, ev, ts) {
+    // Extends AbstractUiMessage and MeMessage
+    MeMessage.call(this, ev, ts);
+
+    /** @const @type {ChatContext} */
+    this.context = DATA.context.getChannelContext(channelId).getChatContext();
+
+    /** @type {string} @const */
+    this.channelId = channelId;
+    this.dom = AbstractUiMessage.dom;
+    this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh;
+}
+UiMeMessage.prototype = Object.create(MeMessage.prototype);
+UiMeMessage.prototype.constructor = UiMeMessage;
+
+UiMeMessage.prototype.invalidate = function() {
+    return AbstractUiMessage.invalidate(this);
+};
+
+UiMeMessage.prototype.formatText = function(text) {
+    return AbstractUiMessage.formatText(this, text);
+};
+
+UiMeMessage.prototype.removeDom = function() {
+    return AbstractUiMessage.removeDom(this);
+};
+
+UiMeMessage.prototype.getDom = function() {
+    return AbstractUiMessage.getDom(this);
+};
+
+UiMeMessage.prototype.createDom = function() {
+    this.dom = doCreateMessageDom(this, false);
+    this.dom.classList.add(R.klass.msg.meMessage);
+    return this;
+};
+
+UiMeMessage.prototype.duplicateDom = function() {
+    return AbstractUiMessage.duplicateDom(this);
+};
+
+UiMeMessage.prototype.updateDom = function() {
+    AbstractUiMessage.updateDom(this);
+    return this;
+};
+
+UiMeMessage.prototype.update = function(ev, ts) {
+    MeMessage.prototype.update.call(this, ev, ts);
+    this.invalidate();
+};
+
+/**
+ * @constructor
+ * @implements {IUiMessage}
+ * @extends {Message}
+ * @param {*} ev
+ * @param {number} ts
+**/
+function UiMessage(channelId, ev, ts) {
+    // Extends AbstractUiMessage and Message
+    Message.call(this, ev, ts);
+
+    /** @const @type {ChatContext} */
+    this.context = DATA.context.getChannelContext(channelId).getChatContext();
+
+    /** @type {string} @const */
+    this.channelId = channelId;
+    /** @type {Element} */
+    this.dom = AbstractUiMessage.dom;
+    /** @type {boolean} */
+    this.uiNeedRefresh = AbstractUiMessage.uiNeedRefresh;
+}
+UiMessage.prototype = Object.create(Message.prototype);
+UiMessage.prototype.constructor = UiMessage;
+
+UiMessage.prototype.invalidate = function() {
+    return AbstractUiMessage.invalidate(this);
+};
+
+UiMessage.prototype.formatText = function(text) {
+    return AbstractUiMessage.formatText(this, text);
+};
+
+UiMessage.prototype.removeDom = function() {
+    return AbstractUiMessage.removeDom(this);
+};
+
+UiMessage.prototype.getDom = function() {
+    return AbstractUiMessage.getDom(this);
+};
+
+UiMessage.prototype.createDom = function() {
+    this.dom = doCreateMessageDom(this, false);
+    return this;
+};
+
+UiMessage.prototype.duplicateDom = function() {
+    return AbstractUiMessage.duplicateDom(this);
+};
+
+UiMessage.prototype.updateDom = function() {
+    AbstractUiMessage.updateDom(this);
+    return this;
+};
+
+UiMessage.prototype.update = function(ev, ts) {
+    Message.prototype.update.call(this, ev, ts);
+    this.invalidate();
+
+    var match = this.text.match(/^<?https:\/\/www\.openstreetmap\.org\/\?mlat=(-?[0-9\.]+)(&amp;|&)mlon=(-?[0-9\.]+)(&amp;|&)macc=([0-9\.]+)[^\s]*/);
+    if (match) {
+        var lat = match[1],
+            lon = match[3],
+            acc = match[5];
+        this.text = this.text.substr(0, match.index) +this.text.substr(match.index +match[0].length).trim();
+        this.attachments.unshift({
+            "color": "#008000",
+            "text": match[0],
+            "footer": "Open Street Map",
+            "footer_icon": "https://www.openstreetmap.org/assets/favicon-32x32-36d06d8a01933075bc7093c9631cffd02d49b03b659f767340f256bb6839d990.png",
+            "geo": {
+                "latitude": match[1],
+                "longitude": match[3],
+                "accuracy": match[5]
+            }
+        });
+    }
+
+};
+
+/**
+ * @constructor
+ * @implements {IUiMessage}
+ * @extends {NoticeMessage}
+ * @param {*} ev
+ * @param {number} ts
+**/
+function UiNoticeMessage(channelId, ev, ts) {
+    // Extends AbstractUiMessage and NoticeMessage
+    NoticeMessage.call(this, ev, ts);
+
+    /** @const @type {ChatContext} */
+    this.context = DATA.context.getChannelContext(channelId).getChatContext();
+
+    /** @type {string} @const */
+    this.channelId = channelId;
+
+    /** @type {Element} */
+    this.dom; // jshint ignore:line
+
+    /** @type {Element} */
+    this.domWrapper = null;
+
+    /** @type {boolean} */
+    this.uiNeedRefresh = true;
+}
+UiNoticeMessage.prototype = Object.create(NoticeMessage.prototype);
+UiNoticeMessage.prototype.constructor = UiNoticeMessage;
+
+UiNoticeMessage.prototype.invalidate = function() {
+    return AbstractUiMessage.invalidate(this);
+};
+
+UiNoticeMessage.prototype.formatText = function(text) {
+    return AbstractUiMessage.formatText(this, text);
+};
+
+UiNoticeMessage.prototype.removeDom = function() {
+    if (this.domWrapper && this.domWrapper.parentElement) {
+        this.domWrapper.remove();
+        delete(this.domWrapper);
+    }
+    if (this.dom)
+        delete(this.dom);
+    return this;
+};
+
+UiNoticeMessage.prototype.getDom = function() {
+    AbstractUiMessage.getDom(this);
+    return this.domWrapper;
+};
+
+UiNoticeMessage.prototype.duplicateDom = function() {
+    return this.domWrapper.cloneNode(true);
+};
+
+UiNoticeMessage.prototype.createDom = function() {
+    this.dom = doCreateMessageDom(this, false);
+    this.domWrapper = document.createElement("span");
+    this.dom.classList.add(R.klass.msg.notice);
+    this.domWrapper.className = R.klass.msg.notice;
+    this.domWrapper.textContent = locale.onlyVisible;
+    this.domWrapper.appendChild(this.dom);
+    return this;
+};
+
+UiNoticeMessage.prototype.updateDom = function() {
+    AbstractUiMessage.updateDom(this);
+    return this;
+};
+
+UiNoticeMessage.prototype.update = function(ev, ts) {
+    NoticeMessage.prototype.update.call(this, ev, ts);
+    this.invalidate();
+};
+

+ 83 - 58
cli/workflow.js

@@ -1,22 +1,45 @@
+/* jshint sub: true */
 
 var
     /**
      * @type {number} next period to wait before next retry in case of failure, in seconds
     **/
-    NEXT_RETRY = 0
+    NEXT_RETRY = 0,
 
     /**
-     * @type {Room}
+     * @type {Room|null}
     **/
-    ,SELECTED_ROOM = null
+    SELECTED_ROOM = null,
+
+    /**
+     * @type {SimpleChatSystem|null}
+    **/
+    SELECTED_CONTEXT = null,
 
     /** @type {Message|null} */
-    ,REPLYING_TO = null
+    REPLYING_TO = null,
 
     /** @type {Message|null} */
-    ,EDITING = null
+    EDITING = null
 ;
 
+function initHljs() {
+    var xhr = new XMLHttpRequest();
+    xhr.timeout = 1000 * 60 * 1; // 3 min timeout
+    xhr.onreadystatechange = function(e) {
+        if (xhr.readyState === 4) {
+            var script = document.createElement("script");
+
+            script.innerHTML = xhr.response;
+            script.language = "text/javascript";
+            document.head.innerHTML += '<link href="hljs-androidstudio.css" rel="stylesheet"/>';
+            document.body.appendChild(script);
+        }
+    };
+    xhr.open('GET', 'highlight.pack.js', true);
+    xhr.send(null);
+}
+
 /**
  * @param {Room} room
  * @param {function(boolean)} cb
@@ -40,8 +63,8 @@ function poll(callback) {
                 poll(callback); // retry on timeout
                 return;
             }
-            var resp = null
-                ,success = Math.floor(xhr.status / 100) === 2;
+            var resp = null,
+                success = Math.floor(xhr.status / 100) === 2;
 
             if (success) {
                 if (NEXT_RETRY) {
@@ -51,7 +74,7 @@ function poll(callback) {
                 resp = xhr.response;
                 try {
                     resp = JSON.parse(/** @type {string} */ (resp));
-                } catch (e) {
+                } catch (exp) {
                     resp = null;
                 }
             } else {
@@ -66,20 +89,20 @@ function poll(callback) {
             callback(success, resp);
         }
     };
-    xhr.open('GET', 'api?v=' +SLACK.lastServerVersion, true);
+    xhr.open('GET', 'api?v=' +DATA.lastServerVersion, true);
     xhr.send(null);
 }
 
 function outOfSync() {
-    SLACK.lastServerVersion = 0;
+    DATA.lastServerVersion = 0;
 }
 
 /**
  * @param {Room} room
 **/
 function sendTyping(room) {
-    var xhr = new XMLHttpRequest()
-        ,url = 'api/typing?room=' +room.id;
+    var xhr = new XMLHttpRequest(),
+        url = 'api/typing?room=' +room.id;
     xhr.open('POST', url, true);
     xhr.send(null);
 }
@@ -91,7 +114,7 @@ function sendTyping(room) {
 function onPollResponse(success, response) {
     if (success) {
         if (response) {
-            SLACK.update(response);
+            DATA.update(response);
         }
         startPolling();
     } else {
@@ -112,8 +135,12 @@ function selectRoom(room) {
     document.getElementById("room_" +room.id).classList.add(R.klass.selected);
     document.body.classList.remove(R.klass.noRoomSelected);
     SELECTED_ROOM = room;
+    SELECTED_CONTEXT = /** @type {SimpleChatSystem} */ (DATA.context.getChannelContext(room.id));
     onRoomSelected();
-    if (SELECTED_ROOM.lastMsg && !SLACK.history[SELECTED_ROOM.id])
+    createContextBackground(SELECTED_CONTEXT.getChatContext().team.id, SELECTED_CONTEXT.getChatContext().users, function(imgData) {
+        document.getElementById(R.id.context).style.backgroundImage = 'url(' +imgData +')';
+    });
+    if (SELECTED_ROOM.lastMsg !== 0 && !DATA.history[SELECTED_ROOM.id])
         fetchHistory(SELECTED_ROOM, function(success) {});
 }
 
@@ -128,9 +155,9 @@ function unselectRoom() {
  * @param {function(string?)} callback
 **/
 function uploadFile(chan, filename, file, callback) {
-    var fileReader = new FileReader()
-        ,formData = new FormData()
-        ,xhr = new XMLHttpRequest();
+    var fileReader = new FileReader(),
+        formData = new FormData(),
+        xhr = new XMLHttpRequest();
 
     formData.append("file", file);
     formData.append("filename", filename);
@@ -142,7 +169,7 @@ function uploadFile(chan, filename, file, callback) {
                 callback(xhr.statusText);
             }
         }
-    }
+    };
     xhr.open('POST', 'api/file?room=' +chan.id);
     xhr.send(formData);
 }
@@ -153,11 +180,8 @@ function uploadFile(chan, filename, file, callback) {
  * @param {(function((string|null)))=} callback
 **/
 function sendCommand(payload, serviceId, callback) {
-    var formData = new FormData()
-        ,xhr = new XMLHttpRequest();
+    var xhr = new XMLHttpRequest();
 
-    formData.append("payload", payload);
-    formData.append("service_id", serviceId);
     if (callback) {
         xhr.onreadystatechange = function() {
             if (xhr.readyState === 4) {
@@ -167,22 +191,22 @@ function sendCommand(payload, serviceId, callback) {
                     callback(xhr.statusText);
                 }
             }
-        }
+        };
     }
-    xhr.open('POST', "api/attachmentAction");
-    xhr.send(formData);
+    xhr.open('POST', "api/attachmentAction?serviceId=" +serviceId);
+    xhr.send(JSON.stringify(payload));
 }
 
 function getActionPayload(channelId, msg, attachment, action) {
     var payload = {
-        "actions": [ action ]
-        ,"attachment_id": attachment["id"]
-        ,"callback_id": attachment["callback_id"]
-        ,"channel_id": channelId
-        ,"is_ephemeral": msg instanceof NoticeMessage
-        ,"message_ts": msg["id"]
+        "actions": [ action ],
+        "attachment_id": attachment["id"],
+        "callback_id": attachment["callback_id"],
+        "channel_id": channelId,
+        "is_ephemeral": msg instanceof NoticeMessage,
+        "message_ts": msg["id"]
     };
-    return JSON.stringify(payload);
+    return payload;
 }
 
 /**
@@ -191,8 +215,8 @@ function getActionPayload(channelId, msg, attachment, action) {
  * @param {string} args
 **/
 function doCommand(chan, cmd, args) {
-    var xhr = new XMLHttpRequest()
-        ,url = 'api/cmd?room=' +chan.id +"&cmd=" +encodeURIComponent(cmd.name.substr(1)) +"&args=" +encodeURIComponent(args);
+    var xhr = new XMLHttpRequest(),
+        url = 'api/cmd?room=' +chan.id +"&cmd=" +encodeURIComponent(cmd.name.substr(1)) +"&args=" +encodeURIComponent(args);
     xhr.open('POST', url, true);
     xhr.send(null);
 }
@@ -206,23 +230,15 @@ function sendMsg(chan, msg, replyTo) {
     var xhr = new XMLHttpRequest();
     var url = 'api/msg?room=' +chan.id +"&text=" +encodeURIComponent(msg);
     if (replyTo) {
-        var sender = SLACK.context.users[replyTo.userId]
-            ,footer = "Message";
-
-        if (chan.id[0] === 'C') {
-            footer = "Channel message";
-        } else if (chan.id[0] === 'D') {
-            footer = "Direct message";
-        } else if (chan.id[0] === 'G') {
-            footer = "Group message";
-        }
+        var sender = DATA.context.getUser(replyTo.userId),
+            footer = chan.isPrivate ? locale.message : chan.name;
+
         var attachment = {
-            "fallback": replyTo.text
-            ,"author_name": "<@" +sender.id +"|" +sender.name +">"
-            ,"author_icon": sender.icons.small
-            ,"text": replyTo.text
-            ,"footer": footer
-            ,"ts": replyTo.ts
+            "fallback": replyTo.text,
+            "author_name": sender.name,
+            "text": replyTo.text,
+            "footer": footer,
+            "ts": replyTo.ts
         };
         url += "&attachments=" +encodeURIComponent(JSON.stringify([attachment]));
     }
@@ -242,14 +258,22 @@ function onTextEntered(input, skipCommand) {
         return true;
     }
     if (input[0] === '/' && skipCommand !== true) {
-        var endCmd = input.indexOf(' ')
-            ,cmd = input.substr(0, endCmd === -1 ? undefined : endCmd)
-            ,args = endCmd === -1 ? "" : input.substr(endCmd)
-            ,cmdObject = SLACK.context.commands.data[cmd];
+        var endCmd = input.indexOf(' '),
+            cmd = input.substr(0, endCmd === -1 ? undefined : endCmd),
+            args = endCmd === -1 ? "" : input.substr(endCmd),
+            ctx = SELECTED_CONTEXT,
+            cliCmdObject = CLIENT_COMMANDS.getCommand(cmd);
 
-        if (cmdObject) {
-            doCommand(SELECTED_ROOM, cmdObject, args.trim());
+        if (cliCmdObject) {
+            cliCmdObject.exec(ctx, SELECTED_ROOM, args.trim());
             return true;
+        } else if (ctx) {
+            var cmdObject = ctx.getChatContext().commands.data[cmd];
+
+            if (cmdObject) {
+                doCommand(SELECTED_ROOM, cmdObject, args.trim());
+                return true;
+            }
         }
         return false;
     }
@@ -282,11 +306,12 @@ function removeMsg(chan, msg) {
 
 /**
  * @param {Room} chan
+ * @param {string} id
  * @param {number} ts
 **/
-function sendReadMArker(chan, ts) {
+function sendReadMarker(chan, id, ts) {
     var xhr = new XMLHttpRequest();
-    var url = 'api/markread?room=' +chan.id +"&ts=" +ts;
+    var url = 'api/markread?room=' +chan.id +"&id=" +id +"&ts=" +ts;
     xhr.open('POST', url, true);
     xhr.send(null);
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
srv/public/emojione.sprites.css


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 4
srv/public/emojione.sprites.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
srv/public/emojione_v2.3.sprites.css


+ 267 - 0
srv/public/emojione_v2.3.sprites.js

@@ -0,0 +1,267 @@
+"use strict";(function(){
+var f=[{names:["100"],keywords:["symbol","wow","win","perfect","parties"],unicode:"1f4af"},{names:["1234"],keywords:["symbol"],unicode:"1f522"},{names:["grinning"],keywords:["happy","smiley","emotion"],unicode:"1f600"},{names:["grin"],keywords:"happy silly smiley emotion good selfie".split(" "),unicode:"1f601"},{names:["joy"],keywords:"happy silly smiley cry laugh emotion sarcastic".split(" "),unicode:"1f602"},{names:["rolling_on_the_floor_laughing","rofl"],keywords:[],unicode:"1f923"},{names:["smiley"],
+keywords:["happy","smiley","emotion","good"],unicode:"1f603"},{names:["smile"],keywords:["happy","smiley","emotion"],unicode:"1f604"},{names:["sweat_smile"],keywords:["smiley","workout","sweat","emotion"],unicode:"1f605"},{names:["satisfied","laughing"],keywords:["happy","smiley","laugh","emotion"],unicode:"1f606"},{names:["wink"],keywords:["silly","smiley","emotion"],unicode:"1f609"},{names:["blush"],keywords:["happy","smiley","emotion","good","beautiful"],unicode:"1f60a"},{names:["yum"],keywords:"happy silly smiley emotion sarcastic good".split(" "),
+unicode:"1f60b"},{names:["sunglasses"],keywords:["silly","smiley","emojione","glasses","boys night"],unicode:"1f60e"},{names:["heart_eyes"],keywords:"happy;smiley;love;sex;heart eyes;emotion;beautiful".split(";"),unicode:"1f60d"},{names:["kissing_heart"],keywords:["smiley","love","sexy"],unicode:"1f618"},{names:["kissing"],keywords:["smiley","sexy"],unicode:"1f617"},{names:["kissing_smiling_eyes"],keywords:["smiley","sexy"],unicode:"1f619"},{names:["kissing_closed_eyes"],keywords:["smiley","sexy"],
+unicode:"1f61a"},{names:["relaxed"],keywords:["happy","smiley"],unicode:"263a"},{names:["slightly_smiling_face","slight_smile"],keywords:["happy","smiley"],unicode:"1f642"},{names:["hugging_face","hugging"],keywords:["smiley","hug","thank you"],unicode:"1f917"},{names:["thinking_face","thinking"],keywords:["smiley","thinking","boys night"],unicode:"1f914"},{names:["neutral_face"],keywords:["mad","smiley","shrug","neutral","emotion"],unicode:"1f610"},{names:["expressionless"],keywords:["mad","smiley",
+"neutral","emotion"],unicode:"1f611"},{names:["no_mouth"],keywords:["mad","smiley","neutral","emotion"],unicode:"1f636"},{names:["face_with_rolling_eyes","rolling_eyes"],keywords:["mad","smiley","rolling eyes","emotion","sarcastic"],unicode:"1f644"},{names:["smirk"],keywords:["silly","smiley","sexy","sarcastic"],unicode:"1f60f"},{names:["persevere"],keywords:["sad","smiley","angry","emotion"],unicode:"1f623"},{names:["disappointed_relieved"],keywords:"sad smiley stressed sweat cry emotion".split(" "),
+unicode:"1f625"},{names:["open_mouth"],keywords:["smiley","surprised","wow","emotion"],unicode:"1f62e"},{names:["zipper_mouth_face","zipper_mouth"],keywords:["mad","smiley"],unicode:"1f910"},{names:["hushed"],keywords:["smiley","surprised","wow"],unicode:"1f62f"},{names:["sleepy"],keywords:["smiley","sick","emotion"],unicode:"1f62a"},{names:["tired_face"],keywords:["sad","smiley","tired","emotion"],unicode:"1f62b"},{names:["sleeping"],keywords:["smiley","tired","emotion","goodnight"],unicode:"1f634"},
+{names:["relieved"],keywords:["smiley","emotion"],unicode:"1f60c"},{names:["nerd_face","nerd"],keywords:["smiley","glasses"],unicode:"1f913"},{names:["stuck_out_tongue"],keywords:["smiley","sex","emotion"],unicode:"1f61b"},{names:["stuck_out_tongue_winking_eye"],keywords:["happy","smiley","emotion","parties"],unicode:"1f61c"},{names:["stuck_out_tongue_closed_eyes"],keywords:["happy","smiley","emotion"],unicode:"1f61d"},{names:["drool","drooling_face"],keywords:[],unicode:"1f924"},{names:["unamused"],
+keywords:["sad","mad","smiley","tired","emotion"],unicode:"1f612"},{names:["sweat"],keywords:["sad","smiley","stressed","sweat","emotion"],unicode:"1f613"},{names:["pensive"],keywords:["sad","smiley","emotion","rip"],unicode:"1f614"},{names:["confused"],keywords:["smiley","surprised","emotion"],unicode:"1f615"},{names:["upside_down_face","upside_down"],keywords:["silly","smiley","sarcastic"],unicode:"1f643"},{names:["money_mouth_face","money_mouth"],keywords:["smiley","win","money","emotion","boys night"],
+unicode:"1f911"},{names:["astonished"],keywords:["smiley","surprised","wow","emotion","omg"],unicode:"1f632"},{names:["white_frowning_face","frowning2"],keywords:["sad","smiley","emotion"],unicode:"2639"},{names:["slightly_frowning_face","slight_frown"],keywords:["sad","smiley","emotion"],unicode:"1f641"},{names:["confounded"],keywords:["sad","smiley","angry","emotion"],unicode:"1f616"},{names:["disappointed"],keywords:["sad","smiley","tired","emotion"],unicode:"1f61e"},{names:["worried"],keywords:["sad",
+"smiley","emotion"],unicode:"1f61f"},{names:["triumph"],keywords:["mad","smiley","angry","emotion","steam"],unicode:"1f624"},{names:["cry"],keywords:"sad smiley cry emotion rip heartbreak".split(" "),unicode:"1f622"},{names:["sob"],keywords:["sad","smiley","cry","emotion","heartbreak"],unicode:"1f62d"},{names:["frowning"],keywords:["sad","smiley","surprised","emotion"],unicode:"1f626"},{names:["anguished"],keywords:["sad","smiley","surprised","emotion"],unicode:"1f627"},{names:["fearful"],keywords:["smiley",
+"surprised","emotion"],unicode:"1f628"},{names:["weary"],keywords:["sad","smiley","tired","stressed","emotion"],unicode:"1f629"},{names:["grimacing"],keywords:["silly","smiley","emotion","selfie"],unicode:"1f62c"},{names:["cold_sweat"],keywords:["smiley","sweat","emotion"],unicode:"1f630"},{names:["scream"],keywords:["smiley","surprised","wow","emotion","omg"],unicode:"1f631"},{names:["flushed"],keywords:["smiley","emotion","omg"],unicode:"1f633"},{names:["dizzy_face"],keywords:"smiley surprised dead wow emotion omg".split(" "),
+unicode:"1f635"},{names:["rage"],keywords:["mad","smiley","angry","emotion"],unicode:"1f621"},{names:["angry"],keywords:["mad","smiley","emotion"],unicode:"1f620"},{names:["innocent"],keywords:["smiley","emotion"],unicode:"1f607"},{names:["face_with_cowboy_hat","cowboy"],keywords:[],unicode:"1f920"},{names:["clown_face","clown"],keywords:[],unicode:"1f921"},{names:["liar","lying_face"],keywords:[],unicode:"1f925"},{names:["mask"],keywords:["smiley","dead","health","sick"],unicode:"1f637"},{names:["face_with_thermometer",
+"thermometer_face"],keywords:["smiley","health","sick","emotion"],unicode:"1f912"},{names:["face_with_head_bandage","head_bandage"],keywords:["smiley","health","sick","emotion"],unicode:"1f915"},{names:["sick","nauseated_face"],keywords:[],unicode:"1f922"},{names:["sneeze","sneezing_face"],keywords:[],unicode:"1f927"},{names:["smiling_imp"],keywords:"silly;smiley;angry;monster;devil;boys night".split(";"),unicode:"1f608"},{names:["imp"],keywords:["smiley","monster","devil","wth"],unicode:"1f47f"},
+{names:["japanese_ogre"],keywords:["monster"],unicode:"1f479"},{names:["japanese_goblin"],keywords:["angry","monster"],unicode:"1f47a"},{names:["skeleton","skull"],keywords:["dead","halloween","skull"],unicode:"1f480"},{names:["skull_and_crossbones","skull_crossbones"],keywords:["symbol","dead","skull"],unicode:"2620"},{names:["ghost"],keywords:["holidays","halloween","monster"],unicode:"1f47b"},{names:["alien"],keywords:["space","monster","alien","scientology"],unicode:"1f47d"},{names:["space_invader"],
+keywords:["monster","alien"],unicode:"1f47e"},{names:["robot_face","robot"],keywords:["monster","robot"],unicode:"1f916"},{names:["shit","hankey","poo","poop"],keywords:["bathroom","shit","sol","diarrhea"],unicode:"1f4a9"},{names:["smiley_cat"],keywords:["happy","cat","animal"],unicode:"1f63a"},{names:["smile_cat"],keywords:["happy","cat","animal"],unicode:"1f638"},{names:["joy_cat"],keywords:"happy silly cry laugh cat animal sarcastic".split(" "),unicode:"1f639"},{names:["heart_eyes_cat"],keywords:["heart eyes",
+"cat","animal","beautiful"],unicode:"1f63b"},{names:["smirk_cat"],keywords:["cat","animal"],unicode:"1f63c"},{names:["kissing_cat"],keywords:["cat","animal"],unicode:"1f63d"},{names:["scream_cat"],keywords:["cat","animal"],unicode:"1f640"},{names:["crying_cat_face"],keywords:["cry","cat","animal"],unicode:"1f63f"},{names:["pouting_cat"],keywords:["cat","animal"],unicode:"1f63e"},{names:["see_no_evil"],keywords:["animal"],unicode:"1f648"},{names:["hear_no_evil"],keywords:["animal"],unicode:"1f649"},
+{names:["speak_no_evil"],keywords:["animal"],unicode:"1f64a"},{names:["boy"],keywords:["people","baby","diversity"],unicode:"1f466"},{names:["boy_tone1"],keywords:[],unicode:"1f466-1f3fb"},{names:["boy_tone2"],keywords:[],unicode:"1f466-1f3fc"},{names:["boy_tone3"],keywords:[],unicode:"1f466-1f3fd"},{names:["boy_tone4"],keywords:[],unicode:"1f466-1f3fe"},{names:["boy_tone5"],keywords:[],unicode:"1f466-1f3ff"},{names:["girl"],keywords:["people","women","baby","diversity"],unicode:"1f467"},{names:["girl_tone1"],
+keywords:[],unicode:"1f467-1f3fb"},{names:["girl_tone2"],keywords:[],unicode:"1f467-1f3fc"},{names:["girl_tone3"],keywords:[],unicode:"1f467-1f3fd"},{names:["girl_tone4"],keywords:[],unicode:"1f467-1f3fe"},{names:["girl_tone5"],keywords:[],unicode:"1f467-1f3ff"},{names:["man"],keywords:"people;men;sex;diversity;selfie;boys night".split(";"),unicode:"1f468"},{names:["man_tone1"],keywords:[],unicode:"1f468-1f3fb"},{names:["man_tone2"],keywords:[],unicode:"1f468-1f3fc"},{names:["man_tone3"],keywords:[],
+unicode:"1f468-1f3fd"},{names:["man_tone4"],keywords:[],unicode:"1f468-1f3fe"},{names:["man_tone5"],keywords:[],unicode:"1f468-1f3ff"},{names:["woman"],keywords:"people;women;sex;diversity;feminist;selfie;girls night".split(";"),unicode:"1f469"},{names:["woman_tone1"],keywords:[],unicode:"1f469-1f3fb"},{names:["woman_tone2"],keywords:[],unicode:"1f469-1f3fc"},{names:["woman_tone3"],keywords:[],unicode:"1f469-1f3fd"},{names:["woman_tone4"],keywords:[],unicode:"1f469-1f3fe"},{names:["woman_tone5"],
+keywords:[],unicode:"1f469-1f3ff"},{names:["older_man"],keywords:["people","men","old people","diversity"],unicode:"1f474"},{names:["older_man_tone1"],keywords:[],unicode:"1f474-1f3fb"},{names:["older_man_tone2"],keywords:[],unicode:"1f474-1f3fc"},{names:["older_man_tone3"],keywords:[],unicode:"1f474-1f3fd"},{names:["older_man_tone4"],keywords:[],unicode:"1f474-1f3fe"},{names:["older_man_tone5"],keywords:[],unicode:"1f474-1f3ff"},{names:["grandma","older_woman"],keywords:["people","old people","diversity"],
+unicode:"1f475"},{names:["grandma_tone1","older_woman_tone1"],keywords:[],unicode:"1f475-1f3fb"},{names:["grandma_tone2","older_woman_tone2"],keywords:[],unicode:"1f475-1f3fc"},{names:["grandma_tone3","older_woman_tone3"],keywords:[],unicode:"1f475-1f3fd"},{names:["grandma_tone4","older_woman_tone4"],keywords:[],unicode:"1f475-1f3fe"},{names:["grandma_tone5","older_woman_tone5"],keywords:[],unicode:"1f475-1f3ff"},{names:["baby"],keywords:["people","baby","diversity"],unicode:"1f476"},{names:["baby_tone1"],
+keywords:[],unicode:"1f476-1f3fb"},{names:["baby_tone2"],keywords:[],unicode:"1f476-1f3fc"},{names:["baby_tone3"],keywords:[],unicode:"1f476-1f3fd"},{names:["baby_tone4"],keywords:[],unicode:"1f476-1f3fe"},{names:["baby_tone5"],keywords:[],unicode:"1f476-1f3ff"},{names:["angel"],keywords:["people","diversity","omg"],unicode:"1f47c"},{names:["angel_tone1"],keywords:[],unicode:"1f47c-1f3fb"},{names:["angel_tone2"],keywords:[],unicode:"1f47c-1f3fc"},{names:["angel_tone3"],keywords:[],unicode:"1f47c-1f3fd"},
+{names:["angel_tone4"],keywords:[],unicode:"1f47c-1f3fe"},{names:["angel_tone5"],keywords:[],unicode:"1f47c-1f3ff"},{names:["cop"],keywords:"people hat men diversity job police 911".split(" "),unicode:"1f46e"},{names:["cop_tone1"],keywords:[],unicode:"1f46e-1f3fb"},{names:["cop_tone2"],keywords:[],unicode:"1f46e-1f3fc"},{names:["cop_tone3"],keywords:[],unicode:"1f46e-1f3fd"},{names:["cop_tone4"],keywords:[],unicode:"1f46e-1f3fe"},{names:["cop_tone5"],keywords:[],unicode:"1f46e-1f3ff"},{names:["sleuth_or_spy",
+"spy"],keywords:"people hat men glasses diversity job".split(" "),unicode:"1f575"},{names:["sleuth_or_spy_tone1","spy_tone1"],keywords:[],unicode:"1f575-1f3fb"},{names:["sleuth_or_spy_tone2","spy_tone2"],keywords:[],unicode:"1f575-1f3fc"},{names:["sleuth_or_spy_tone3","spy_tone3"],keywords:[],unicode:"1f575-1f3fd"},{names:["sleuth_or_spy_tone4","spy_tone4"],keywords:[],unicode:"1f575-1f3fe"},{names:["sleuth_or_spy_tone5","spy_tone5"],keywords:[],unicode:"1f575-1f3ff"},{names:["guardsman"],keywords:["people",
+"hat","men","diversity","job"],unicode:"1f482"},{names:["guardsman_tone1"],keywords:[],unicode:"1f482-1f3fb"},{names:["guardsman_tone2"],keywords:[],unicode:"1f482-1f3fc"},{names:["guardsman_tone3"],keywords:[],unicode:"1f482-1f3fd"},{names:["guardsman_tone4"],keywords:[],unicode:"1f482-1f3fe"},{names:["guardsman_tone5"],keywords:[],unicode:"1f482-1f3ff"},{names:["construction_worker"],keywords:["people","hat","men","diversity","job"],unicode:"1f477"},{names:["construction_worker_tone1"],keywords:[],
+unicode:"1f477-1f3fb"},{names:["construction_worker_tone2"],keywords:[],unicode:"1f477-1f3fc"},{names:["construction_worker_tone3"],keywords:[],unicode:"1f477-1f3fd"},{names:["construction_worker_tone4"],keywords:[],unicode:"1f477-1f3fe"},{names:["construction_worker_tone5"],keywords:[],unicode:"1f477-1f3ff"},{names:["man_with_turban"],keywords:["people","hat","diversity"],unicode:"1f473"},{names:["man_with_turban_tone1"],keywords:[],unicode:"1f473-1f3fb"},{names:["man_with_turban_tone2"],keywords:[],
+unicode:"1f473-1f3fc"},{names:["man_with_turban_tone3"],keywords:[],unicode:"1f473-1f3fd"},{names:["man_with_turban_tone4"],keywords:[],unicode:"1f473-1f3fe"},{names:["man_with_turban_tone5"],keywords:[],unicode:"1f473-1f3ff"},{names:["person_with_blond_hair"],keywords:["people","men","diversity"],unicode:"1f471"},{names:["person_with_blond_hair_tone1"],keywords:[],unicode:"1f471-1f3fb"},{names:["person_with_blond_hair_tone2"],keywords:[],unicode:"1f471-1f3fc"},{names:["person_with_blond_hair_tone3"],
+keywords:[],unicode:"1f471-1f3fd"},{names:["person_with_blond_hair_tone4"],keywords:[],unicode:"1f471-1f3fe"},{names:["person_with_blond_hair_tone5"],keywords:[],unicode:"1f471-1f3ff"},{names:["santa"],keywords:"people hat winter holidays christmas diversity santa".split(" "),unicode:"1f385"},{names:["santa_tone1"],keywords:[],unicode:"1f385-1f3fb"},{names:["santa_tone2"],keywords:[],unicode:"1f385-1f3fc"},{names:["santa_tone3"],keywords:[],unicode:"1f385-1f3fd"},{names:["santa_tone4"],keywords:[],
+unicode:"1f385-1f3fe"},{names:["santa_tone5"],keywords:[],unicode:"1f385-1f3ff"},{names:["mother_christmas","mrs_claus"],keywords:[],unicode:"1f936"},{names:["mother_christmas_tone1","mrs_claus_tone1"],keywords:[],unicode:"1f936-1f3fb"},{names:["mother_christmas_tone2","mrs_claus_tone2"],keywords:[],unicode:"1f936-1f3fc"},{names:["mother_christmas_tone3","mrs_claus_tone3"],keywords:[],unicode:"1f936-1f3fd"},{names:["mother_christmas_tone4","mrs_claus_tone4"],keywords:[],unicode:"1f936-1f3fe"},{names:["mother_christmas_tone5",
+"mrs_claus_tone5"],keywords:[],unicode:"1f936-1f3ff"},{names:["princess"],keywords:["people","women","diversity","beautiful","girls night"],unicode:"1f478"},{names:["princess_tone1"],keywords:[],unicode:"1f478-1f3fb"},{names:["princess_tone2"],keywords:[],unicode:"1f478-1f3fc"},{names:["princess_tone3"],keywords:[],unicode:"1f478-1f3fd"},{names:["princess_tone4"],keywords:[],unicode:"1f478-1f3fe"},{names:["princess_tone5"],keywords:[],unicode:"1f478-1f3ff"},{names:["prince"],keywords:[],unicode:"1f934"},
+{names:["prince_tone1"],keywords:[],unicode:"1f934-1f3fb"},{names:["prince_tone2"],keywords:[],unicode:"1f934-1f3fc"},{names:["prince_tone3"],keywords:[],unicode:"1f934-1f3fd"},{names:["prince_tone4"],keywords:[],unicode:"1f934-1f3fe"},{names:["prince_tone5"],keywords:[],unicode:"1f934-1f3ff"},{names:["bride_with_veil"],keywords:["people","wedding","women","diversity"],unicode:"1f470"},{names:["bride_with_veil_tone1"],keywords:[],unicode:"1f470-1f3fb"},{names:["bride_with_veil_tone2"],keywords:[],
+unicode:"1f470-1f3fc"},{names:["bride_with_veil_tone3"],keywords:[],unicode:"1f470-1f3fd"},{names:["bride_with_veil_tone4"],keywords:[],unicode:"1f470-1f3fe"},{names:["bride_with_veil_tone5"],keywords:[],unicode:"1f470-1f3ff"},{names:["man_in_tuxedo"],keywords:[],unicode:"1f935"},{names:["tuxedo_tone1","man_in_tuxedo_tone1"],keywords:[],unicode:"1f935-1f3fb"},{names:["tuxedo_tone2","man_in_tuxedo_tone2"],keywords:[],unicode:"1f935-1f3fc"},{names:["tuxedo_tone3","man_in_tuxedo_tone3"],keywords:[],
+unicode:"1f935-1f3fd"},{names:["tuxedo_tone4","man_in_tuxedo_tone4"],keywords:[],unicode:"1f935-1f3fe"},{names:["tuxedo_tone5","man_in_tuxedo_tone5"],keywords:[],unicode:"1f935-1f3ff"},{names:["expecting_woman","pregnant_woman"],keywords:[],unicode:"1f930"},{names:["expecting_woman_tone1","pregnant_woman_tone1"],keywords:[],unicode:"1f930-1f3fb"},{names:["expecting_woman_tone2","pregnant_woman_tone2"],keywords:[],unicode:"1f930-1f3fc"},{names:["expecting_woman_tone3","pregnant_woman_tone3"],keywords:[],
+unicode:"1f930-1f3fd"},{names:["expecting_woman_tone4","pregnant_woman_tone4"],keywords:[],unicode:"1f930-1f3fe"},{names:["expecting_woman_tone5","pregnant_woman_tone5"],keywords:[],unicode:"1f930-1f3ff"},{names:["man_with_gua_pi_mao"],keywords:["people","hat","men","diversity"],unicode:"1f472"},{names:["man_with_gua_pi_mao_tone1"],keywords:[],unicode:"1f472-1f3fb"},{names:["man_with_gua_pi_mao_tone2"],keywords:[],unicode:"1f472-1f3fc"},{names:["man_with_gua_pi_mao_tone3"],keywords:[],unicode:"1f472-1f3fd"},
+{names:["man_with_gua_pi_mao_tone4"],keywords:[],unicode:"1f472-1f3fe"},{names:["man_with_gua_pi_mao_tone5"],keywords:[],unicode:"1f472-1f3ff"},{names:["person_frowning"],keywords:["people","women","diversity"],unicode:"1f64d"},{names:["person_frowning_tone1"],keywords:[],unicode:"1f64d-1f3fb"},{names:["person_frowning_tone2"],keywords:[],unicode:"1f64d-1f3fc"},{names:["person_frowning_tone3"],keywords:[],unicode:"1f64d-1f3fd"},{names:["person_frowning_tone4"],keywords:[],unicode:"1f64d-1f3fe"},{names:["person_frowning_tone5"],
+keywords:[],unicode:"1f64d-1f3ff"},{names:["person_with_pouting_face"],keywords:["people","women","diversity"],unicode:"1f64e"},{names:["person_with_pouting_face_tone1"],keywords:[],unicode:"1f64e-1f3fb"},{names:["person_with_pouting_face_tone2"],keywords:[],unicode:"1f64e-1f3fc"},{names:["person_with_pouting_face_tone3"],keywords:[],unicode:"1f64e-1f3fd"},{names:["person_with_pouting_face_tone4"],keywords:[],unicode:"1f64e-1f3fe"},{names:["person_with_pouting_face_tone5"],keywords:[],unicode:"1f64e-1f3ff"},
+{names:["no_good"],keywords:["people","women","diversity","girls night"],unicode:"1f645"},{names:["no_good_tone1"],keywords:[],unicode:"1f645-1f3fb"},{names:["no_good_tone2"],keywords:[],unicode:"1f645-1f3fc"},{names:["no_good_tone3"],keywords:[],unicode:"1f645-1f3fd"},{names:["no_good_tone4"],keywords:[],unicode:"1f645-1f3fe"},{names:["no_good_tone5"],keywords:[],unicode:"1f645-1f3ff"},{names:["ok_woman"],keywords:["people","women","diversity"],unicode:"1f646"},{names:["ok_woman_tone1"],keywords:[],
+unicode:"1f646-1f3fb"},{names:["ok_woman_tone2"],keywords:[],unicode:"1f646-1f3fc"},{names:["ok_woman_tone3"],keywords:[],unicode:"1f646-1f3fd"},{names:["ok_woman_tone4"],keywords:[],unicode:"1f646-1f3fe"},{names:["ok_woman_tone5"],keywords:[],unicode:"1f646-1f3ff"},{names:["information_desk_person"],keywords:["people","women","diversity"],unicode:"1f481"},{names:["information_desk_person_tone1"],keywords:[],unicode:"1f481-1f3fb"},{names:["information_desk_person_tone2"],keywords:[],unicode:"1f481-1f3fc"},
+{names:["information_desk_person_tone3"],keywords:[],unicode:"1f481-1f3fd"},{names:["information_desk_person_tone4"],keywords:[],unicode:"1f481-1f3fe"},{names:["information_desk_person_tone5"],keywords:[],unicode:"1f481-1f3ff"},{names:["raising_hand"],keywords:["people","women","diversity"],unicode:"1f64b"},{names:["raising_hand_tone1"],keywords:[],unicode:"1f64b-1f3fb"},{names:["raising_hand_tone2"],keywords:[],unicode:"1f64b-1f3fc"},{names:["raising_hand_tone3"],keywords:[],unicode:"1f64b-1f3fd"},
+{names:["raising_hand_tone4"],keywords:[],unicode:"1f64b-1f3fe"},{names:["raising_hand_tone5"],keywords:[],unicode:"1f64b-1f3ff"},{names:["bow"],keywords:["people","pray","diversity"],unicode:"1f647"},{names:["bow_tone1"],keywords:[],unicode:"1f647-1f3fb"},{names:["bow_tone2"],keywords:[],unicode:"1f647-1f3fc"},{names:["bow_tone3"],keywords:[],unicode:"1f647-1f3fd"},{names:["bow_tone4"],keywords:[],unicode:"1f647-1f3fe"},{names:["bow_tone5"],keywords:[],unicode:"1f647-1f3ff"},{names:["facepalm","face_palm"],
+keywords:[],unicode:"1f926"},{names:["facepalm_tone1","face_palm_tone1"],keywords:[],unicode:"1f926-1f3fb"},{names:["facepalm_tone2","face_palm_tone2"],keywords:[],unicode:"1f926-1f3fc"},{names:["facepalm_tone3","face_palm_tone3"],keywords:[],unicode:"1f926-1f3fd"},{names:["facepalm_tone4","face_palm_tone4"],keywords:[],unicode:"1f926-1f3fe"},{names:["facepalm_tone5","face_palm_tone5"],keywords:[],unicode:"1f926-1f3ff"},{names:["shrug"],keywords:[],unicode:"1f937"},{names:["shrug_tone1"],keywords:[],
+unicode:"1f937-1f3fb"},{names:["shrug_tone2"],keywords:[],unicode:"1f937-1f3fc"},{names:["shrug_tone3"],keywords:[],unicode:"1f937-1f3fd"},{names:["shrug_tone4"],keywords:[],unicode:"1f937-1f3fe"},{names:["shrug_tone5"],keywords:[],unicode:"1f937-1f3ff"},{names:["massage"],keywords:["people","women","diversity"],unicode:"1f486"},{names:["massage_tone1"],keywords:[],unicode:"1f486-1f3fb"},{names:["massage_tone2"],keywords:[],unicode:"1f486-1f3fc"},{names:["massage_tone3"],keywords:[],unicode:"1f486-1f3fd"},
+{names:["massage_tone4"],keywords:[],unicode:"1f486-1f3fe"},{names:["massage_tone5"],keywords:[],unicode:"1f486-1f3ff"},{names:["haircut"],keywords:["people","women","diversity"],unicode:"1f487"},{names:["haircut_tone1"],keywords:[],unicode:"1f487-1f3fb"},{names:["haircut_tone2"],keywords:[],unicode:"1f487-1f3fc"},{names:["haircut_tone3"],keywords:[],unicode:"1f487-1f3fd"},{names:["haircut_tone4"],keywords:[],unicode:"1f487-1f3fe"},{names:["haircut_tone5"],keywords:[],unicode:"1f487-1f3ff"},{names:["walking"],
+keywords:["people","men","diversity"],unicode:"1f6b6"},{names:["walking_tone1"],keywords:[],unicode:"1f6b6-1f3fb"},{names:["walking_tone2"],keywords:[],unicode:"1f6b6-1f3fc"},{names:["walking_tone3"],keywords:[],unicode:"1f6b6-1f3fd"},{names:["walking_tone4"],keywords:[],unicode:"1f6b6-1f3fe"},{names:["walking_tone5"],keywords:[],unicode:"1f6b6-1f3ff"},{names:["runner"],keywords:["people","men","diversity","boys night","run"],unicode:"1f3c3"},{names:["runner_tone1"],keywords:[],unicode:"1f3c3-1f3fb"},
+{names:["runner_tone2"],keywords:[],unicode:"1f3c3-1f3fc"},{names:["runner_tone3"],keywords:[],unicode:"1f3c3-1f3fd"},{names:["runner_tone4"],keywords:[],unicode:"1f3c3-1f3fe"},{names:["runner_tone5"],keywords:[],unicode:"1f3c3-1f3ff"},{names:["dancer"],keywords:"people;women;sexy;diversity;girls night;dance".split(";"),unicode:"1f483"},{names:["dancer_tone1"],keywords:[],unicode:"1f483-1f3fb"},{names:["dancer_tone2"],keywords:[],unicode:"1f483-1f3fc"},{names:["dancer_tone3"],keywords:[],unicode:"1f483-1f3fd"},
+{names:["dancer_tone4"],keywords:[],unicode:"1f483-1f3fe"},{names:["dancer_tone5"],keywords:[],unicode:"1f483-1f3ff"},{names:["male_dancer","man_dancing"],keywords:[],unicode:"1f57a"},{names:["male_dancer_tone1","man_dancing_tone1"],keywords:[],unicode:"1f57a-1f3fb"},{names:["male_dancer_tone2","man_dancing_tone2"],keywords:[],unicode:"1f57a-1f3fc"},{names:["male_dancer_tone3","man_dancing_tone3"],keywords:[],unicode:"1f57a-1f3fd"},{names:["male_dancer_tone4","man_dancing_tone4"],keywords:[],unicode:"1f57a-1f3fe"},
+{names:["male_dancer_tone5","man_dancing_tone5"],keywords:[],unicode:"1f57a-1f3ff"},{names:["dancers"],keywords:"people;women;sexy;girls night;boys night;parties;dance".split(";"),unicode:"1f46f"},{names:["man_in_business_suit_levitating","levitate"],keywords:["men","job"],unicode:"1f574"},{names:["speaking_head_in_silhouette","speaking_head"],keywords:["people","talk"],unicode:"1f5e3"},{names:["bust_in_silhouette"],keywords:["people"],unicode:"1f464"},{names:["busts_in_silhouette"],keywords:["people"],
+unicode:"1f465"},{names:["fencing","fencer"],keywords:[],unicode:"1f93a"},{names:["horse_racing"],keywords:["men","sport","horse racing"],unicode:"1f3c7"},{names:["horse_racing_tone1"],keywords:[],unicode:"1f3c7-1f3fb"},{names:["horse_racing_tone2"],keywords:[],unicode:"1f3c7-1f3fc"},{names:["horse_racing_tone3"],keywords:[],unicode:"1f3c7-1f3fd"},{names:["horse_racing_tone4"],keywords:[],unicode:"1f3c7-1f3fe"},{names:["horse_racing_tone5"],keywords:[],unicode:"1f3c7-1f3ff"},{names:["skier"],keywords:["hat",
+"vacation","cold","sport","skiing"],unicode:"26f7"},{names:["snowboarder"],keywords:["hat","vacation","cold","sport","snowboarding"],unicode:"1f3c2"},{names:["golfer"],keywords:"men game ball vacation sport golf".split(" "),unicode:"1f3cc"},{names:["surfer"],keywords:["men","vacation","tropical","sport","diversity"],unicode:"1f3c4"},{names:["surfer_tone1"],keywords:[],unicode:"1f3c4-1f3fb"},{names:["surfer_tone2"],keywords:[],unicode:"1f3c4-1f3fc"},{names:["surfer_tone3"],keywords:[],unicode:"1f3c4-1f3fd"},
+{names:["surfer_tone4"],keywords:[],unicode:"1f3c4-1f3fe"},{names:["surfer_tone5"],keywords:[],unicode:"1f3c4-1f3ff"},{names:["rowboat"],keywords:["men","workout","sport","rowing","diversity"],unicode:"1f6a3"},{names:["rowboat_tone1"],keywords:[],unicode:"1f6a3-1f3fb"},{names:["rowboat_tone2"],keywords:[],unicode:"1f6a3-1f3fc"},{names:["rowboat_tone3"],keywords:[],unicode:"1f6a3-1f3fd"},{names:["rowboat_tone4"],keywords:[],unicode:"1f6a3-1f3fe"},{names:["rowboat_tone5"],keywords:[],unicode:"1f6a3-1f3ff"},
+{names:["swimmer"],keywords:["workout","sport","swim","diversity"],unicode:"1f3ca"},{names:["swimmer_tone1"],keywords:[],unicode:"1f3ca-1f3fb"},{names:["swimmer_tone2"],keywords:[],unicode:"1f3ca-1f3fc"},{names:["swimmer_tone3"],keywords:[],unicode:"1f3ca-1f3fd"},{names:["swimmer_tone4"],keywords:[],unicode:"1f3ca-1f3fe"},{names:["swimmer_tone5"],keywords:[],unicode:"1f3ca-1f3ff"},{names:["person_with_ball","basketball_player"],keywords:"men game ball sport basketball diversity".split(" "),unicode:"26f9"},
+{names:["person_with_ball_tone1","basketball_player_tone1"],keywords:[],unicode:"26f9-1f3fb"},{names:["person_with_ball_tone2","basketball_player_tone2"],keywords:[],unicode:"26f9-1f3fc"},{names:["person_with_ball_tone3","basketball_player_tone3"],keywords:[],unicode:"26f9-1f3fd"},{names:["person_with_ball_tone4","basketball_player_tone4"],keywords:[],unicode:"26f9-1f3fe"},{names:["person_with_ball_tone5","basketball_player_tone5"],keywords:[],unicode:"26f9-1f3ff"},{names:["weight_lifter","lifter"],
+keywords:"men;workout;flex;sport;weight lifting;win;diversity".split(";"),unicode:"1f3cb"},{names:["weight_lifter_tone1","lifter_tone1"],keywords:[],unicode:"1f3cb-1f3fb"},{names:["weight_lifter_tone2","lifter_tone2"],keywords:[],unicode:"1f3cb-1f3fc"},{names:["weight_lifter_tone3","lifter_tone3"],keywords:[],unicode:"1f3cb-1f3fd"},{names:["weight_lifter_tone4","lifter_tone4"],keywords:[],unicode:"1f3cb-1f3fe"},{names:["weight_lifter_tone5","lifter_tone5"],keywords:[],unicode:"1f3cb-1f3ff"},{names:["bicyclist"],
+keywords:["men","workout","sport","bike","diversity"],unicode:"1f6b4"},{names:["bicyclist_tone1"],keywords:[],unicode:"1f6b4-1f3fb"},{names:["bicyclist_tone2"],keywords:[],unicode:"1f6b4-1f3fc"},{names:["bicyclist_tone3"],keywords:[],unicode:"1f6b4-1f3fd"},{names:["bicyclist_tone4"],keywords:[],unicode:"1f6b4-1f3fe"},{names:["bicyclist_tone5"],keywords:[],unicode:"1f6b4-1f3ff"},{names:["mountain_bicyclist"],keywords:["men","sport","bike","diversity"],unicode:"1f6b5"},{names:["mountain_bicyclist_tone1"],
+keywords:[],unicode:"1f6b5-1f3fb"},{names:["mountain_bicyclist_tone2"],keywords:[],unicode:"1f6b5-1f3fc"},{names:["mountain_bicyclist_tone3"],keywords:[],unicode:"1f6b5-1f3fd"},{names:["mountain_bicyclist_tone4"],keywords:[],unicode:"1f6b5-1f3fe"},{names:["mountain_bicyclist_tone5"],keywords:[],unicode:"1f6b5-1f3ff"},{names:["racing_car","race_car"],keywords:["transportation","car"],unicode:"1f3ce"},{names:["racing_motorcycle","motorcycle"],keywords:["transportation","travel","bike"],unicode:"1f3cd"},
+{names:["person_doing_cartwheel","cartwheel"],keywords:[],unicode:"1f938"},{names:["person_doing_cartwheel_tone1","cartwheel_tone1"],keywords:[],unicode:"1f938-1f3fb"},{names:["person_doing_cartwheel_tone2","cartwheel_tone2"],keywords:[],unicode:"1f938-1f3fc"},{names:["person_doing_cartwheel_tone3","cartwheel_tone3"],keywords:[],unicode:"1f938-1f3fd"},{names:["person_doing_cartwheel_tone4","cartwheel_tone4"],keywords:[],unicode:"1f938-1f3fe"},{names:["person_doing_cartwheel_tone5","cartwheel_tone5"],
+keywords:[],unicode:"1f938-1f3ff"},{names:["wrestling","wrestlers"],keywords:[],unicode:"1f93c"},{names:["wrestling_tone1","wrestlers_tone1"],keywords:[],unicode:"1f93c-1f3fb"},{names:["wrestling_tone2","wrestlers_tone2"],keywords:[],unicode:"1f93c-1f3fc"},{names:["wrestling_tone3","wrestlers_tone3"],keywords:[],unicode:"1f93c-1f3fd"},{names:["wrestling_tone4","wrestlers_tone4"],keywords:[],unicode:"1f93c-1f3fe"},{names:["wrestling_tone5","wrestlers_tone5"],keywords:[],unicode:"1f93c-1f3ff"},{names:["water_polo"],
+keywords:[],unicode:"1f93d"},{names:["water_polo_tone1"],keywords:[],unicode:"1f93d-1f3fb"},{names:["water_polo_tone2"],keywords:[],unicode:"1f93d-1f3fc"},{names:["water_polo_tone3"],keywords:[],unicode:"1f93d-1f3fd"},{names:["water_polo_tone4"],keywords:[],unicode:"1f93d-1f3fe"},{names:["water_polo_tone5"],keywords:[],unicode:"1f93d-1f3ff"},{names:["handball"],keywords:[],unicode:"1f93e"},{names:["handball_tone1"],keywords:[],unicode:"1f93e-1f3fb"},{names:["handball_tone2"],keywords:[],unicode:"1f93e-1f3fc"},
+{names:["handball_tone3"],keywords:[],unicode:"1f93e-1f3fd"},{names:["handball_tone4"],keywords:[],unicode:"1f93e-1f3fe"},{names:["handball_tone5"],keywords:[],unicode:"1f93e-1f3ff"},{names:["juggler","juggling"],keywords:[],unicode:"1f939"},{names:["juggler_tone1","juggling_tone1"],keywords:[],unicode:"1f939-1f3fb"},{names:["juggler_tone2","juggling_tone2"],keywords:[],unicode:"1f939-1f3fc"},{names:["juggler_tone3","juggling_tone3"],keywords:[],unicode:"1f939-1f3fd"},{names:["juggler_tone4","juggling_tone4"],
+keywords:[],unicode:"1f939-1f3fe"},{names:["juggler_tone5","juggling_tone5"],keywords:[],unicode:"1f939-1f3ff"},{names:["couple"],keywords:["people","sex","creationism"],unicode:"1f46b"},{names:["two_men_holding_hands"],keywords:["people","gay","men","sex","lgbt"],unicode:"1f46c"},{names:["two_women_holding_hands"],keywords:"people;women;sex;lgbt;lesbian;girls night".split(";"),unicode:"1f46d"},{names:["couplekiss"],keywords:["people","love","sex"],unicode:"1f48f"},{names:["couplekiss_mm","kiss_mm"],
+keywords:"people gay men love sex lgbt".split(" "),unicode:"1f468-2764-1f48b-1f468"},{names:["couplekiss_ww","kiss_ww"],keywords:"people women love sex lgbt lesbian".split(" "),unicode:"1f469-2764-1f48b-1f469"},{names:["couple_with_heart"],keywords:["people","love","sex"],unicode:"1f491"},{names:["couple_with_heart_mm","couple_mm"],keywords:"people gay men love sex lgbt".split(" "),unicode:"1f468-2764-1f468"},{names:["couple_with_heart_ww","couple_ww"],keywords:["people","women","love","sex","lgbt"],
+unicode:"1f469-2764-1f469"},{names:["family"],keywords:["people","family","baby"],unicode:"1f46a"},{names:["family_mwg"],keywords:["people","family","baby"],unicode:"1f468-1f469-1f467"},{names:["family_mwgb"],keywords:["people","family","baby"],unicode:"1f468-1f469-1f467-1f466"},{names:["family_mwbb"],keywords:["people","family","baby"],unicode:"1f468-1f469-1f466-1f466"},{names:["family_mwgg"],keywords:["people","family","baby"],unicode:"1f468-1f469-1f467-1f467"},{names:["family_mmb"],keywords:"people gay family men baby lgbt".split(" "),
+unicode:"1f468-1f468-1f466"},{names:["family_mmg"],keywords:"people gay family men baby lgbt".split(" "),unicode:"1f468-1f468-1f467"},{names:["family_mmgb"],keywords:"people gay family men baby lgbt".split(" "),unicode:"1f468-1f468-1f467-1f466"},{names:["family_mmbb"],keywords:"people gay family men baby lgbt".split(" "),unicode:"1f468-1f468-1f466-1f466"},{names:["family_mmgg"],keywords:"people gay family men baby lgbt".split(" "),unicode:"1f468-1f468-1f467-1f467"},{names:["family_wwb"],keywords:"people family women baby lgbt lesbian".split(" "),
+unicode:"1f469-1f469-1f466"},{names:["family_wwg"],keywords:"people family women baby lgbt lesbian".split(" "),unicode:"1f469-1f469-1f467"},{names:["family_wwgb"],keywords:"people family women baby lgbt lesbian".split(" "),unicode:"1f469-1f469-1f467-1f466"},{names:["family_wwbb"],keywords:"people family women baby lgbt lesbian".split(" "),unicode:"1f469-1f469-1f466-1f466"},{names:["family_wwgg"],keywords:"people family women baby lgbt lesbian".split(" "),unicode:"1f469-1f469-1f467-1f467"},{names:["tone1"],
+keywords:[],unicode:"1f3fb"},{names:["tone2"],keywords:[],unicode:"1f3fc"},{names:["tone3"],keywords:[],unicode:"1f3fd"},{names:["tone4"],keywords:[],unicode:"1f3fe"},{names:["tone5"],keywords:[],unicode:"1f3ff"},{names:["muscle"],keywords:"body;hands;workout;flex;win;diversity;feminist;boys night".split(";"),unicode:"1f4aa"},{names:["muscle_tone1"],keywords:[],unicode:"1f4aa-1f3fb"},{names:["muscle_tone2"],keywords:[],unicode:"1f4aa-1f3fc"},{names:["muscle_tone3"],keywords:[],unicode:"1f4aa-1f3fd"},
+{names:["muscle_tone4"],keywords:[],unicode:"1f4aa-1f3fe"},{names:["muscle_tone5"],keywords:[],unicode:"1f4aa-1f3ff"},{names:["selfie"],keywords:[],unicode:"1f933"},{names:["selfie_tone1"],keywords:[],unicode:"1f933-1f3fb"},{names:["selfie_tone2"],keywords:[],unicode:"1f933-1f3fc"},{names:["selfie_tone3"],keywords:[],unicode:"1f933-1f3fd"},{names:["selfie_tone4"],keywords:[],unicode:"1f933-1f3fe"},{names:["selfie_tone5"],keywords:[],unicode:"1f933-1f3ff"},{names:["point_left"],keywords:["body","hands",
+"hi","diversity"],unicode:"1f448"},{names:["point_left_tone1"],keywords:[],unicode:"1f448-1f3fb"},{names:["point_left_tone2"],keywords:[],unicode:"1f448-1f3fc"},{names:["point_left_tone3"],keywords:[],unicode:"1f448-1f3fd"},{names:["point_left_tone4"],keywords:[],unicode:"1f448-1f3fe"},{names:["point_left_tone5"],keywords:[],unicode:"1f448-1f3ff"},{names:["point_right"],keywords:["body","hands","hi","diversity"],unicode:"1f449"},{names:["point_right_tone1"],keywords:[],unicode:"1f449-1f3fb"},{names:["point_right_tone2"],
+keywords:[],unicode:"1f449-1f3fc"},{names:["point_right_tone3"],keywords:[],unicode:"1f449-1f3fd"},{names:["point_right_tone4"],keywords:[],unicode:"1f449-1f3fe"},{names:["point_right_tone5"],keywords:[],unicode:"1f449-1f3ff"},{names:["point_up"],keywords:["body","hands","emojione","diversity"],unicode:"261d"},{names:["point_up_tone1"],keywords:[],unicode:"261d-1f3fb"},{names:["point_up_tone2"],keywords:[],unicode:"261d-1f3fc"},{names:["point_up_tone3"],keywords:[],unicode:"261d-1f3fd"},{names:["point_up_tone4"],
+keywords:[],unicode:"261d-1f3fe"},{names:["point_up_tone5"],keywords:[],unicode:"261d-1f3ff"},{names:["point_up_2"],keywords:["body","hands","diversity"],unicode:"1f446"},{names:["point_up_2_tone1"],keywords:[],unicode:"1f446-1f3fb"},{names:["point_up_2_tone2"],keywords:[],unicode:"1f446-1f3fc"},{names:["point_up_2_tone3"],keywords:[],unicode:"1f446-1f3fd"},{names:["point_up_2_tone4"],keywords:[],unicode:"1f446-1f3fe"},{names:["point_up_2_tone5"],keywords:[],unicode:"1f446-1f3ff"},{names:["reversed_hand_with_middle_finger_extended",
+"middle_finger"],keywords:["body","hands","middle finger","diversity"],unicode:"1f595"},{names:["reversed_hand_with_middle_finger_extended_tone1","middle_finger_tone1"],keywords:[],unicode:"1f595-1f3fb"},{names:["reversed_hand_with_middle_finger_extended_tone2","middle_finger_tone2"],keywords:[],unicode:"1f595-1f3fc"},{names:["reversed_hand_with_middle_finger_extended_tone3","middle_finger_tone3"],keywords:[],unicode:"1f595-1f3fd"},{names:["reversed_hand_with_middle_finger_extended_tone4","middle_finger_tone4"],
+keywords:[],unicode:"1f595-1f3fe"},{names:["reversed_hand_with_middle_finger_extended_tone5","middle_finger_tone5"],keywords:[],unicode:"1f595-1f3ff"},{names:["point_down"],keywords:["body","hands","diversity"],unicode:"1f447"},{names:["point_down_tone1"],keywords:[],unicode:"1f447-1f3fb"},{names:["point_down_tone2"],keywords:[],unicode:"1f447-1f3fc"},{names:["point_down_tone3"],keywords:[],unicode:"1f447-1f3fd"},{names:["point_down_tone4"],keywords:[],unicode:"1f447-1f3fe"},{names:["point_down_tone5"],
+keywords:[],unicode:"1f447-1f3ff"},{names:["v"],keywords:"body;hands;hi;thank you;peace;diversity;girls night".split(";"),unicode:"270c"},{names:["v_tone1"],keywords:[],unicode:"270c-1f3fb"},{names:["v_tone2"],keywords:[],unicode:"270c-1f3fc"},{names:["v_tone3"],keywords:[],unicode:"270c-1f3fd"},{names:["v_tone4"],keywords:[],unicode:"270c-1f3fe"},{names:["v_tone5"],keywords:[],unicode:"270c-1f3ff"},{names:["hand_with_index_and_middle_finger_crossed","fingers_crossed"],keywords:[],unicode:"1f91e"},
+{names:["hand_with_index_and_middle_fingers_crossed_tone1","fingers_crossed_tone1"],keywords:[],unicode:"1f91e-1f3fb"},{names:["hand_with_index_and_middle_fingers_crossed_tone2","fingers_crossed_tone2"],keywords:[],unicode:"1f91e-1f3fc"},{names:["hand_with_index_and_middle_fingers_crossed_tone3","fingers_crossed_tone3"],keywords:[],unicode:"1f91e-1f3fd"},{names:["hand_with_index_and_middle_fingers_crossed_tone4","fingers_crossed_tone4"],keywords:[],unicode:"1f91e-1f3fe"},{names:["hand_with_index_and_middle_fingers_crossed_tone5",
+"fingers_crossed_tone5"],keywords:[],unicode:"1f91e-1f3ff"},{names:["raised_hand_with_part_between_middle_and_ring_fingers","vulcan"],keywords:["body","hands","hi","diversity"],unicode:"1f596"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone1","vulcan_tone1"],keywords:[],unicode:"1f596-1f3fb"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone2","vulcan_tone2"],keywords:[],unicode:"1f596-1f3fc"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone3",
+"vulcan_tone3"],keywords:[],unicode:"1f596-1f3fd"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone4","vulcan_tone4"],keywords:[],unicode:"1f596-1f3fe"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone5","vulcan_tone5"],keywords:[],unicode:"1f596-1f3ff"},{names:["sign_of_the_horns","metal"],keywords:"body;hands;hi;diversity;boys night;parties".split(";"),unicode:"1f918"},{names:["sign_of_the_horns_tone1","metal_tone1"],keywords:[],unicode:"1f918-1f3fb"},{names:["sign_of_the_horns_tone2",
+"metal_tone2"],keywords:[],unicode:"1f918-1f3fc"},{names:["sign_of_the_horns_tone3","metal_tone3"],keywords:[],unicode:"1f918-1f3fd"},{names:["sign_of_the_horns_tone4","metal_tone4"],keywords:[],unicode:"1f918-1f3fe"},{names:["sign_of_the_horns_tone5","metal_tone5"],keywords:[],unicode:"1f918-1f3ff"},{names:["call_me_hand","call_me"],keywords:[],unicode:"1f919"},{names:["call_me_hand_tone1","call_me_tone1"],keywords:[],unicode:"1f919-1f3fb"},{names:["call_me_hand_tone2","call_me_tone2"],keywords:[],
+unicode:"1f919-1f3fc"},{names:["call_me_hand_tone3","call_me_tone3"],keywords:[],unicode:"1f919-1f3fd"},{names:["call_me_hand_tone4","call_me_tone4"],keywords:[],unicode:"1f919-1f3fe"},{names:["call_me_hand_tone5","call_me_tone5"],keywords:[],unicode:"1f919-1f3ff"},{names:["raised_hand_with_fingers_splayed","hand_splayed"],keywords:["body","hands","hi","diversity"],unicode:"1f590"},{names:["raised_hand_with_fingers_splayed_tone1","hand_splayed_tone1"],keywords:[],unicode:"1f590-1f3fb"},{names:["raised_hand_with_fingers_splayed_tone2",
+"hand_splayed_tone2"],keywords:[],unicode:"1f590-1f3fc"},{names:["raised_hand_with_fingers_splayed_tone3","hand_splayed_tone3"],keywords:[],unicode:"1f590-1f3fd"},{names:["raised_hand_with_fingers_splayed_tone4","hand_splayed_tone4"],keywords:[],unicode:"1f590-1f3fe"},{names:["raised_hand_with_fingers_splayed_tone5","hand_splayed_tone5"],keywords:[],unicode:"1f590-1f3ff"},{names:["raised_hand"],keywords:["body","hands","hi","diversity","girls night"],unicode:"270b"},{names:["raised_hand_tone1"],keywords:[],
+unicode:"270b-1f3fb"},{names:["raised_hand_tone2"],keywords:[],unicode:"270b-1f3fc"},{names:["raised_hand_tone3"],keywords:[],unicode:"270b-1f3fd"},{names:["raised_hand_tone4"],keywords:[],unicode:"270b-1f3fe"},{names:["raised_hand_tone5"],keywords:[],unicode:"270b-1f3ff"},{names:["ok_hand"],keywords:"body hands hi diversity perfect good beautiful".split(" "),unicode:"1f44c"},{names:["ok_hand_tone1"],keywords:[],unicode:"1f44c-1f3fb"},{names:["ok_hand_tone2"],keywords:[],unicode:"1f44c-1f3fc"},{names:["ok_hand_tone3"],
+keywords:[],unicode:"1f44c-1f3fd"},{names:["ok_hand_tone4"],keywords:[],unicode:"1f44c-1f3fe"},{names:["ok_hand_tone5"],keywords:[],unicode:"1f44c-1f3ff"},{names:["+1","thumbup","thumbsup"],keywords:"body;hands;hi;luck;thank you;diversity;perfect;good;beautiful".split(";"),unicode:"1f44d"},{names:["+1_tone1","thumbup_tone1","thumbsup_tone1"],keywords:[],unicode:"1f44d-1f3fb"},{names:["+1_tone2","thumbup_tone2","thumbsup_tone2"],keywords:[],unicode:"1f44d-1f3fc"},{names:["+1_tone3","thumbup_tone3",
+"thumbsup_tone3"],keywords:[],unicode:"1f44d-1f3fd"},{names:["+1_tone4","thumbup_tone4","thumbsup_tone4"],keywords:[],unicode:"1f44d-1f3fe"},{names:["+1_tone5","thumbup_tone5","thumbsup_tone5"],keywords:[],unicode:"1f44d-1f3ff"},{names:["-1","thumbdown","thumbsdown"],keywords:["body","hands","diversity"],unicode:"1f44e"},{names:["-1_tone1","thumbdown_tone1","thumbsdown_tone1"],keywords:[],unicode:"1f44e-1f3fb"},{names:["-1_tone2","thumbdown_tone2","thumbsdown_tone2"],keywords:[],unicode:"1f44e-1f3fc"},
+{names:["-1_tone3","thumbdown_tone3","thumbsdown_tone3"],keywords:[],unicode:"1f44e-1f3fd"},{names:["-1_tone4","thumbdown_tone4","thumbsdown_tone4"],keywords:[],unicode:"1f44e-1f3fe"},{names:["-1_tone5","thumbdown_tone5","thumbsdown_tone5"],keywords:[],unicode:"1f44e-1f3ff"},{names:["fist"],keywords:"body;hands;hi;fist bump;diversity;condolence".split(";"),unicode:"270a"},{names:["fist_tone1"],keywords:[],unicode:"270a-1f3fb"},{names:["fist_tone2"],keywords:[],unicode:"270a-1f3fc"},{names:["fist_tone3"],
+keywords:[],unicode:"270a-1f3fd"},{names:["fist_tone4"],keywords:[],unicode:"270a-1f3fe"},{names:["fist_tone5"],keywords:[],unicode:"270a-1f3ff"},{names:["punch"],keywords:"body;hands;hi;fist bump;diversity;boys night".split(";"),unicode:"1f44a"},{names:["punch_tone1"],keywords:[],unicode:"1f44a-1f3fb"},{names:["punch_tone2"],keywords:[],unicode:"1f44a-1f3fc"},{names:["punch_tone3"],keywords:[],unicode:"1f44a-1f3fd"},{names:["punch_tone4"],keywords:[],unicode:"1f44a-1f3fe"},{names:["punch_tone5"],
+keywords:[],unicode:"1f44a-1f3ff"},{names:["left_fist","left_facing_fist"],keywords:[],unicode:"1f91b"},{names:["left_fist_tone1","left_facing_fist_tone1"],keywords:[],unicode:"1f91b-1f3fb"},{names:["left_fist_tone2","left_facing_fist_tone2"],keywords:[],unicode:"1f91b-1f3fc"},{names:["left_fist_tone3","left_facing_fist_tone3"],keywords:[],unicode:"1f91b-1f3fd"},{names:["left_fist_tone4","left_facing_fist_tone4"],keywords:[],unicode:"1f91b-1f3fe"},{names:["left_fist_tone5","left_facing_fist_tone5"],
+keywords:[],unicode:"1f91b-1f3ff"},{names:["right_fist","right_facing_fist"],keywords:[],unicode:"1f91c"},{names:["right_fist_tone1","right_facing_fist_tone1"],keywords:[],unicode:"1f91c-1f3fb"},{names:["right_fist_tone2","right_facing_fist_tone2"],keywords:[],unicode:"1f91c-1f3fc"},{names:["right_fist_tone3","right_facing_fist_tone3"],keywords:[],unicode:"1f91c-1f3fd"},{names:["right_fist_tone4","right_facing_fist_tone4"],keywords:[],unicode:"1f91c-1f3fe"},{names:["right_fist_tone5","right_facing_fist_tone5"],
+keywords:[],unicode:"1f91c-1f3ff"},{names:["back_of_hand","raised_back_of_hand"],keywords:[],unicode:"1f91a"},{names:["back_of_hand_tone1","raised_back_of_hand_tone1"],keywords:[],unicode:"1f91a-1f3fb"},{names:["back_of_hand_tone2","raised_back_of_hand_tone2"],keywords:[],unicode:"1f91a-1f3fc"},{names:["back_of_hand_tone3","raised_back_of_hand_tone3"],keywords:[],unicode:"1f91a-1f3fd"},{names:["back_of_hand_tone4","raised_back_of_hand_tone4"],keywords:[],unicode:"1f91a-1f3fe"},{names:["back_of_hand_tone5",
+"raised_back_of_hand_tone5"],keywords:[],unicode:"1f91a-1f3ff"},{names:["wave"],keywords:["body","hands","hi","diversity"],unicode:"1f44b"},{names:["wave_tone1"],keywords:[],unicode:"1f44b-1f3fb"},{names:["wave_tone2"],keywords:[],unicode:"1f44b-1f3fc"},{names:["wave_tone3"],keywords:[],unicode:"1f44b-1f3fd"},{names:["wave_tone4"],keywords:[],unicode:"1f44b-1f3fe"},{names:["wave_tone5"],keywords:[],unicode:"1f44b-1f3ff"},{names:["clap"],keywords:"body hands win diversity good beautiful".split(" "),
+unicode:"1f44f"},{names:["clap_tone1"],keywords:[],unicode:"1f44f-1f3fb"},{names:["clap_tone2"],keywords:[],unicode:"1f44f-1f3fc"},{names:["clap_tone3"],keywords:[],unicode:"1f44f-1f3fd"},{names:["clap_tone4"],keywords:[],unicode:"1f44f-1f3fe"},{names:["clap_tone5"],keywords:[],unicode:"1f44f-1f3ff"},{names:["writing_hand"],keywords:["body","hands","write","diversity"],unicode:"270d"},{names:["writing_hand_tone1"],keywords:[],unicode:"270d-1f3fb"},{names:["writing_hand_tone2"],keywords:[],unicode:"270d-1f3fc"},
+{names:["writing_hand_tone3"],keywords:[],unicode:"270d-1f3fd"},{names:["writing_hand_tone4"],keywords:[],unicode:"270d-1f3fe"},{names:["writing_hand_tone5"],keywords:[],unicode:"270d-1f3ff"},{names:["open_hands"],keywords:["body","hands","diversity","condolence"],unicode:"1f450"},{names:["open_hands_tone1"],keywords:[],unicode:"1f450-1f3fb"},{names:["open_hands_tone2"],keywords:[],unicode:"1f450-1f3fc"},{names:["open_hands_tone3"],keywords:[],unicode:"1f450-1f3fd"},{names:["open_hands_tone4"],keywords:[],
+unicode:"1f450-1f3fe"},{names:["open_hands_tone5"],keywords:[],unicode:"1f450-1f3ff"},{names:["raised_hands"],keywords:"body hands diversity perfect good parties".split(" "),unicode:"1f64c"},{names:["raised_hands_tone1"],keywords:[],unicode:"1f64c-1f3fb"},{names:["raised_hands_tone2"],keywords:[],unicode:"1f64c-1f3fc"},{names:["raised_hands_tone3"],keywords:[],unicode:"1f64c-1f3fd"},{names:["raised_hands_tone4"],keywords:[],unicode:"1f64c-1f3fe"},{names:["raised_hands_tone5"],keywords:[],unicode:"1f64c-1f3ff"},
+{names:["pray"],keywords:"body;hands;hi;luck;thank you;pray;diversity;scientology".split(";"),unicode:"1f64f"},{names:["pray_tone1"],keywords:[],unicode:"1f64f-1f3fb"},{names:["pray_tone2"],keywords:[],unicode:"1f64f-1f3fc"},{names:["pray_tone3"],keywords:[],unicode:"1f64f-1f3fd"},{names:["pray_tone4"],keywords:[],unicode:"1f64f-1f3fe"},{names:["pray_tone5"],keywords:[],unicode:"1f64f-1f3ff"},{names:["shaking_hands","handshake"],keywords:[],unicode:"1f91d"},{names:["shaking_hands_tone1","handshake_tone1"],
+keywords:[],unicode:"1f91d-1f3fb"},{names:["shaking_hands_tone2","handshake_tone2"],keywords:[],unicode:"1f91d-1f3fc"},{names:["shaking_hands_tone3","handshake_tone3"],keywords:[],unicode:"1f91d-1f3fd"},{names:["shaking_hands_tone4","handshake_tone4"],keywords:[],unicode:"1f91d-1f3fe"},{names:["shaking_hands_tone5","handshake_tone5"],keywords:[],unicode:"1f91d-1f3ff"},{names:["nail_care"],keywords:"women;body;hands;nailpolish;diversity;girls night".split(";"),unicode:"1f485"},{names:["nail_care_tone1"],
+keywords:[],unicode:"1f485-1f3fb"},{names:["nail_care_tone2"],keywords:[],unicode:"1f485-1f3fc"},{names:["nail_care_tone3"],keywords:[],unicode:"1f485-1f3fd"},{names:["nail_care_tone4"],keywords:[],unicode:"1f485-1f3fe"},{names:["nail_care_tone5"],keywords:[],unicode:"1f485-1f3ff"},{names:["ear"],keywords:["body","diversity"],unicode:"1f442"},{names:["ear_tone1"],keywords:[],unicode:"1f442-1f3fb"},{names:["ear_tone2"],keywords:[],unicode:"1f442-1f3fc"},{names:["ear_tone3"],keywords:[],unicode:"1f442-1f3fd"},
+{names:["ear_tone4"],keywords:[],unicode:"1f442-1f3fe"},{names:["ear_tone5"],keywords:[],unicode:"1f442-1f3ff"},{names:["nose"],keywords:["body","diversity"],unicode:"1f443"},{names:["nose_tone1"],keywords:[],unicode:"1f443-1f3fb"},{names:["nose_tone2"],keywords:[],unicode:"1f443-1f3fc"},{names:["nose_tone3"],keywords:[],unicode:"1f443-1f3fd"},{names:["nose_tone4"],keywords:[],unicode:"1f443-1f3fe"},{names:["nose_tone5"],keywords:[],unicode:"1f443-1f3ff"},{names:["footprints"],keywords:[],unicode:"1f463"},
+{names:["eyes"],keywords:["body","eyes"],unicode:"1f440"},{names:["eye"],keywords:["body","eyes"],unicode:"1f441"},{names:["eye_in_speech_bubble"],keywords:["object","symbol","eyes","talk"],unicode:"1f441-1f5e8"},{names:["tongue"],keywords:["body","sexy","lip"],unicode:"1f445"},{names:["lips"],keywords:["women","body","sexy","lip"],unicode:"1f444"},{names:["kiss"],keywords:"women;love;sexy;lip;beautiful;girls night".split(";"),unicode:"1f48b"},{names:["cupid"],keywords:["love","symbol"],unicode:"1f498"},
+{names:["heart"],keywords:["love","symbol","parties"],unicode:"2764"},{names:["heartbeat"],keywords:["love","symbol"],unicode:"1f493"},{names:["broken_heart"],keywords:["love","symbol","heartbreak"],unicode:"1f494"},{names:["two_hearts"],keywords:["love","symbol"],unicode:"1f495"},{names:["sparkling_heart"],keywords:["love","symbol","girls night"],unicode:"1f496"},{names:["heartpulse"],keywords:["love","symbol"],unicode:"1f497"},{names:["blue_heart"],keywords:["love","symbol"],unicode:"1f499"},{names:["green_heart"],
+keywords:["love","symbol"],unicode:"1f49a"},{names:["yellow_heart"],keywords:["love","symbol"],unicode:"1f49b"},{names:["purple_heart"],keywords:["love","symbol"],unicode:"1f49c"},{names:["black_heart"],keywords:[],unicode:"1f5a4"},{names:["gift_heart"],keywords:["love","symbol","condolence"],unicode:"1f49d"},{names:["revolving_hearts"],keywords:["love","symbol"],unicode:"1f49e"},{names:["heart_decoration"],keywords:["love","symbol"],unicode:"1f49f"},{names:["heavy_heart_exclamation_mark_ornament",
+"heart_exclamation"],keywords:["love","symbol"],unicode:"2763"},{names:["love_letter"],keywords:["object"],unicode:"1f48c"},{names:["zzz"],keywords:["tired","goodnight"],unicode:"1f4a4"},{names:["anger"],keywords:["symbol"],unicode:"1f4a2"},{names:["bomb"],keywords:["object","weapon","dead","blast"],unicode:"1f4a3"},{names:["boom"],keywords:["symbol","blast"],unicode:"1f4a5"},{names:["sweat_drops"],keywords:["rain","stressed","sweat"],unicode:"1f4a6"},{names:["dash"],keywords:["cloud","cold","smoking"],
+unicode:"1f4a8"},{names:["dizzy"],keywords:["star","symbol"],unicode:"1f4ab"},{names:["speech_balloon"],keywords:["symbol","free speech"],unicode:"1f4ac"},{names:["left_speech_bubble","speech_left"],keywords:[],unicode:"1f5e8"},{names:["right_anger_bubble","anger_right"],keywords:["symbol"],unicode:"1f5ef"},{names:["thought_balloon"],keywords:["symbol"],unicode:"1f4ad"},{names:["hole"],keywords:["object"],unicode:"1f573"},{names:["eyeglasses"],keywords:["fashion","glasses","accessories"],unicode:"1f453"},
+{names:["dark_sunglasses"],keywords:["fashion","glasses","accessories"],unicode:"1f576"},{names:["necktie"],keywords:["fashion"],unicode:"1f454"},{names:["shirt"],keywords:["fashion"],unicode:"1f455"},{names:["jeans"],keywords:["fashion"],unicode:"1f456"},{names:["dress"],keywords:["women","fashion","sexy","girls night"],unicode:"1f457"},{names:["kimono"],keywords:["fashion"],unicode:"1f458"},{names:["bikini"],keywords:"women fashion sexy vacation tropical swim".split(" "),unicode:"1f459"},{names:["womans_clothes"],
+keywords:["women","fashion"],unicode:"1f45a"},{names:["purse"],keywords:["bag","women","fashion","accessories","money"],unicode:"1f45b"},{names:["handbag"],keywords:["bag","women","fashion","vacation","accessories"],unicode:"1f45c"},{names:["pouch"],keywords:["bag","women","fashion","accessories"],unicode:"1f45d"},{names:["shopping_bags"],keywords:["object","birthday","parties"],unicode:"1f6cd"},{names:["school_satchel"],keywords:["bag","fashion","office","vacation","accessories"],unicode:"1f392"},
+{names:["mans_shoe"],keywords:["fashion","shoe","accessories"],unicode:"1f45e"},{names:["athletic_shoe"],keywords:["fashion","shoe","accessories","boys night"],unicode:"1f45f"},{names:["high_heel"],keywords:"women;fashion;shoe;sexy;accessories;girls night".split(";"),unicode:"1f460"},{names:["sandal"],keywords:["fashion","shoe","accessories"],unicode:"1f461"},{names:["boot"],keywords:["women","fashion","shoe","sexy","accessories"],unicode:"1f462"},{names:["crown"],keywords:["object","gem","accessories"],
+unicode:"1f451"},{names:["womans_hat"],keywords:["women","fashion","accessories"],unicode:"1f452"},{names:["tophat"],keywords:["hat","fashion","accessories"],unicode:"1f3a9"},{names:["mortar_board"],keywords:["hat","office","accessories"],unicode:"1f393"},{names:["helmet_with_white_cross","helmet_with_cross"],keywords:["object","hat","accessories","job"],unicode:"26d1"},{names:["prayer_beads"],keywords:["object","rosary"],unicode:"1f4ff"},{names:["lipstick"],keywords:["object","women","fashion","sexy",
+"lip"],unicode:"1f484"},{names:["ring"],keywords:["wedding","object","fashion","gem","accessories"],unicode:"1f48d"},{names:["gem"],keywords:["object","gem"],unicode:"1f48e"},{names:["monkey_face"],keywords:["animal"],unicode:"1f435"},{names:["monkey"],keywords:["wildlife","animal"],unicode:"1f412"},{names:["gorilla"],keywords:[],unicode:"1f98d"},{names:["dog"],keywords:["dog","pug","animal"],unicode:"1f436"},{names:["dog2"],keywords:["dog","pug","animal"],unicode:"1f415"},{names:["poodle"],keywords:["dog",
+"animal"],unicode:"1f429"},{names:["wolf"],keywords:["wildlife","roar","animal"],unicode:"1f43a"},{names:["fox_face","fox"],keywords:[],unicode:"1f98a"},{names:["cat"],keywords:["halloween","vagina","cat","animal"],unicode:"1f431"},{names:["cat2"],keywords:["halloween","cat","animal"],unicode:"1f408"},{names:["lion","lion_face"],keywords:["wildlife","roar","cat","animal"],unicode:"1f981"},{names:["tiger"],keywords:["wildlife","roar","cat","animal"],unicode:"1f42f"},{names:["tiger2"],keywords:["wildlife",
+"roar","animal"],unicode:"1f405"},{names:["leopard"],keywords:["wildlife","roar","animal"],unicode:"1f406"},{names:["horse"],keywords:["wildlife","animal"],unicode:"1f434"},{names:["racehorse"],keywords:["wildlife","animal"],unicode:"1f40e"},{names:["deer"],keywords:[],unicode:"1f98c"},{names:["unicorn_face","unicorn"],keywords:["animal"],unicode:"1f984"},{names:["cow"],keywords:["animal"],unicode:"1f42e"},{names:["ox"],keywords:["animal"],unicode:"1f402"},{names:["water_buffalo"],keywords:["wildlife",
+"animal"],unicode:"1f403"},{names:["cow2"],keywords:["animal"],unicode:"1f404"},{names:["pig"],keywords:["animal"],unicode:"1f437"},{names:["pig2"],keywords:["animal"],unicode:"1f416"},{names:["boar"],keywords:["wildlife","animal"],unicode:"1f417"},{names:["pig_nose"],keywords:["animal"],unicode:"1f43d"},{names:["ram"],keywords:["wildlife","animal"],unicode:"1f40f"},{names:["sheep"],keywords:["animal"],unicode:"1f411"},{names:["goat"],keywords:["animal"],unicode:"1f410"},{names:["dromedary_camel"],
+keywords:["wildlife","animal"],unicode:"1f42a"},{names:["camel"],keywords:["wildlife","animal","hump day"],unicode:"1f42b"},{names:["elephant"],keywords:["wildlife","animal"],unicode:"1f418"},{names:["rhinoceros","rhino"],keywords:[],unicode:"1f98f"},{names:["mouse"],keywords:["animal"],unicode:"1f42d"},{names:["mouse2"],keywords:["animal"],unicode:"1f401"},{names:["rat"],keywords:["animal"],unicode:"1f400"},{names:["hamster"],keywords:["animal"],unicode:"1f439"},{names:["rabbit"],keywords:["wildlife",
+"animal"],unicode:"1f430"},{names:["rabbit2"],keywords:["wildlife","animal"],unicode:"1f407"},{names:["chipmunk"],keywords:["wildlife","animal"],unicode:"1f43f"},{names:["bat"],keywords:[],unicode:"1f987"},{names:["bear"],keywords:["wildlife","roar","animal"],unicode:"1f43b"},{names:["koala"],keywords:["wildlife","animal"],unicode:"1f428"},{names:["panda_face"],keywords:["wildlife","roar","animal"],unicode:"1f43c"},{names:["paw_prints","feet"],keywords:["animal"],unicode:"1f43e"},{names:["turkey"],
+keywords:["wildlife","animal"],unicode:"1f983"},{names:["chicken"],keywords:["animal","chicken"],unicode:"1f414"},{names:["rooster"],keywords:["animal"],unicode:"1f413"},{names:["hatching_chick"],keywords:["animal","chicken"],unicode:"1f423"},{names:["baby_chick"],keywords:["animal","chicken"],unicode:"1f424"},{names:["hatched_chick"],keywords:["animal","chicken"],unicode:"1f425"},{names:["bird"],keywords:["wildlife","animal"],unicode:"1f426"},{names:["penguin"],keywords:["wildlife","animal"],unicode:"1f427"},
+{names:["dove_of_peace","dove"],keywords:["animal"],unicode:"1f54a"},{names:["eagle"],keywords:[],unicode:"1f985"},{names:["duck"],keywords:[],unicode:"1f986"},{names:["owl"],keywords:[],unicode:"1f989"},{names:["frog"],keywords:["wildlife","animal"],unicode:"1f438"},{names:["crocodile"],keywords:["wildlife","reptile","animal"],unicode:"1f40a"},{names:["turtle"],keywords:["wildlife","reptile","animal"],unicode:"1f422"},{names:["lizard"],keywords:[],unicode:"1f98e"},{names:["snake"],keywords:["wildlife",
+"reptile","animal","creationism"],unicode:"1f40d"},{names:["dragon_face"],keywords:["roar","monster","reptile","animal"],unicode:"1f432"},{names:["dragon"],keywords:["roar","reptile","animal"],unicode:"1f409"},{names:["whale"],keywords:["wildlife","tropical","whales","animal"],unicode:"1f433"},{names:["whale2"],keywords:["wildlife","tropical","whales","animal"],unicode:"1f40b"},{names:["dolphin"],keywords:["wildlife","tropical","animal"],unicode:"1f42c"},{names:["fish"],keywords:["wildlife","animal"],
+unicode:"1f41f"},{names:["tropical_fish"],keywords:["wildlife","animal"],unicode:"1f420"},{names:["blowfish"],keywords:["wildlife","animal"],unicode:"1f421"},{names:["shark"],keywords:[],unicode:"1f988"},{names:["octopus"],keywords:["wildlife","animal"],unicode:"1f419"},{names:["shell"],keywords:[],unicode:"1f41a"},{names:["crab"],keywords:["tropical","animal"],unicode:"1f980"},{names:["shrimp"],keywords:[],unicode:"1f990"},{names:["squid"],keywords:[],unicode:"1f991"},{names:["butterfly"],keywords:[],
+unicode:"1f98b"},{names:["snail"],keywords:["insects","animal"],unicode:"1f40c"},{names:["bug"],keywords:["insects","animal"],unicode:"1f41b"},{names:["ant"],keywords:["insects","animal"],unicode:"1f41c"},{names:["bee"],keywords:["insects","animal"],unicode:"1f41d"},{names:["beetle"],keywords:["insects","animal"],unicode:"1f41e"},{names:["spider"],keywords:["insects","halloween","animal"],unicode:"1f577"},{names:["spider_web"],keywords:["halloween"],unicode:"1f578"},{names:["scorpion"],keywords:["insects",
+"reptile","animal"],unicode:"1f982"},{names:["bouquet"],keywords:["nature","flower","plant","rip","condolence"],unicode:"1f490"},{names:["cherry_blossom"],keywords:["nature","flower","plant","tropical"],unicode:"1f338"},{names:["white_flower"],keywords:["flower","symbol"],unicode:"1f4ae"},{names:["rosette"],keywords:["tropical"],unicode:"1f3f5"},{names:["rose"],keywords:"nature flower plant rip condolence beautiful".split(" "),unicode:"1f339"},{names:["wilted_flower","wilted_rose"],keywords:[],unicode:"1f940"},
+{names:["hibiscus"],keywords:["nature","flower","plant","tropical"],unicode:"1f33a"},{names:["sunflower"],keywords:["nature","flower","plant"],unicode:"1f33b"},{names:["blossom"],keywords:["nature","flower","plant"],unicode:"1f33c"},{names:["tulip"],keywords:["nature","flower","plant","vagina","girls night"],unicode:"1f337"},{names:["seedling"],keywords:["nature","plant","leaf"],unicode:"1f331"},{names:["evergreen_tree"],keywords:"nature plant holidays christmas camp trees".split(" "),unicode:"1f332"},
+{names:["deciduous_tree"],keywords:["nature","plant","camp","trees"],unicode:"1f333"},{names:["palm_tree"],keywords:["nature","plant","tropical","trees"],unicode:"1f334"},{names:["cactus"],keywords:["nature","plant","trees"],unicode:"1f335"},{names:["ear_of_rice"],keywords:["nature","plant","leaf"],unicode:"1f33e"},{names:["herb"],keywords:["nature","plant","leaf"],unicode:"1f33f"},{names:["shamrock"],keywords:["nature","plant","luck","leaf"],unicode:"2618"},{names:["four_leaf_clover"],keywords:["nature",
+"plant","luck","leaf","sol"],unicode:"1f340"},{names:["maple_leaf"],keywords:["nature","plant","leaf"],unicode:"1f341"},{names:["fallen_leaf"],keywords:["nature","plant","leaf"],unicode:"1f342"},{names:["leaves"],keywords:["nature","plant","leaf"],unicode:"1f343"},{names:["grapes"],keywords:["fruit","food"],unicode:"1f347"},{names:["melon"],keywords:["fruit","boobs","food"],unicode:"1f348"},{names:["watermelon"],keywords:["fruit","food"],unicode:"1f349"},{names:["tangerine"],keywords:["fruit","food"],
+unicode:"1f34a"},{names:["lemon"],keywords:["fruit","food"],unicode:"1f34b"},{names:["banana"],keywords:["fruit","penis","food"],unicode:"1f34c"},{names:["pineapple"],keywords:["fruit","food","tropical"],unicode:"1f34d"},{names:["apple"],keywords:["fruit","food","creationism"],unicode:"1f34e"},{names:["green_apple"],keywords:["fruit","food"],unicode:"1f34f"},{names:["pear"],keywords:["fruit","food"],unicode:"1f350"},{names:["peach"],keywords:["fruit","butt","food"],unicode:"1f351"},{names:["cherries"],
+keywords:["fruit","food"],unicode:"1f352"},{names:["strawberry"],keywords:["fruit","food"],unicode:"1f353"},{names:["kiwifruit","kiwi"],keywords:[],unicode:"1f95d"},{names:["tomato"],keywords:["fruit","vegetables","food"],unicode:"1f345"},{names:["avocado"],keywords:[],unicode:"1f951"},{names:["eggplant"],keywords:["vegetables","penis","food"],unicode:"1f346"},{names:["potato"],keywords:[],unicode:"1f954"},{names:["carrot"],keywords:[],unicode:"1f955"},{names:["corn"],keywords:["vegetables","food"],
+unicode:"1f33d"},{names:["hot_pepper"],keywords:["vegetables","food"],unicode:"1f336"},{names:["cucumber"],keywords:[],unicode:"1f952"},{names:["mushroom"],keywords:["nature","plant","drugs"],unicode:"1f344"},{names:["shelled_peanut","peanuts"],keywords:[],unicode:"1f95c"},{names:["chestnut"],keywords:["nature","plant"],unicode:"1f330"},{names:["bread"],keywords:["food"],unicode:"1f35e"},{names:["croissant"],keywords:[],unicode:"1f950"},{names:["baguette_bread","french_bread"],keywords:[],unicode:"1f956"},
+{names:["pancakes"],keywords:[],unicode:"1f95e"},{names:["cheese_wedge","cheese"],keywords:["food"],unicode:"1f9c0"},{names:["meat_on_bone"],keywords:["food"],unicode:"1f356"},{names:["poultry_leg"],keywords:["food","holidays"],unicode:"1f357"},{names:["bacon"],keywords:["pig"],unicode:"1f953"},{names:["hamburger"],keywords:["america","food"],unicode:"1f354"},{names:["fries"],keywords:["america","food"],unicode:"1f35f"},{names:["pizza"],keywords:["italian","food","boys night"],unicode:"1f355"},{names:["hot_dog",
+"hotdog"],keywords:["america","food"],unicode:"1f32d"},{names:["taco"],keywords:["food","mexican","vagina"],unicode:"1f32e"},{names:["burrito"],keywords:["food","mexican"],unicode:"1f32f"},{names:["stuffed_pita","stuffed_flatbread"],keywords:[],unicode:"1f959"},{names:["egg"],keywords:[],unicode:"1f95a"},{names:["cooking"],keywords:["food"],unicode:"1f373"},{names:["paella","shallow_pan_of_food"],keywords:["pan of food"],unicode:"1f958"},{names:["stew"],keywords:["food","steam"],unicode:"1f372"},
+{names:["green_salad","salad"],keywords:[],unicode:"1f957"},{names:["popcorn"],keywords:["food","parties"],unicode:"1f37f"},{names:["bento"],keywords:["object","sushi","japan","food"],unicode:"1f371"},{names:["rice_cracker"],keywords:["sushi","food"],unicode:"1f358"},{names:["rice_ball"],keywords:["sushi","japan","food"],unicode:"1f359"},{names:["rice"],keywords:["sushi","japan","food"],unicode:"1f35a"},{names:["curry"],keywords:["food"],unicode:"1f35b"},{names:["ramen"],keywords:["noodles","ramen",
+"japan","food"],unicode:"1f35c"},{names:["spaghetti"],keywords:["noodles","pasta","italian","food"],unicode:"1f35d"},{names:["sweet_potato"],keywords:["vegetables","food"],unicode:"1f360"},{names:["oden"],keywords:["food"],unicode:"1f362"},{names:["sushi"],keywords:["sushi","japan","food"],unicode:"1f363"},{names:["fried_shrimp"],keywords:["food"],unicode:"1f364"},{names:["fish_cake"],keywords:["sushi","food"],unicode:"1f365"},{names:["dango"],keywords:["food"],unicode:"1f361"},{names:["icecream"],
+keywords:["food"],unicode:"1f366"},{names:["shaved_ice"],keywords:["food"],unicode:"1f367"},{names:["ice_cream"],keywords:["food"],unicode:"1f368"},{names:["doughnut"],keywords:["food"],unicode:"1f369"},{names:["cookie"],keywords:["food","vagina"],unicode:"1f36a"},{names:["birthday"],keywords:["birthday","food","parties"],unicode:"1f382"},{names:["cake"],keywords:["food"],unicode:"1f370"},{names:["chocolate_bar"],keywords:["food","halloween"],unicode:"1f36b"},{names:["candy"],keywords:["food","halloween"],
+unicode:"1f36c"},{names:["lollipop"],keywords:["food","halloween"],unicode:"1f36d"},{names:["pudding","flan","custard"],keywords:["food"],unicode:"1f36e"},{names:["honey_pot"],keywords:["food","vagina"],unicode:"1f36f"},{names:["baby_bottle"],keywords:["drink","object","food","baby"],unicode:"1f37c"},{names:["glass_of_milk","milk"],keywords:[],unicode:"1f95b"},{names:["coffee"],keywords:["drink","caffeine","steam","morning"],unicode:"2615"},{names:["tea"],keywords:["drink","japan","caffeine","steam",
+"morning"],unicode:"1f375"},{names:["sake"],keywords:["drink","japan","sake","alcohol","girls night"],unicode:"1f376"},{names:["bottle_with_popping_cork","champagne"],keywords:["drink","cheers","alcohol","parties"],unicode:"1f37e"},{names:["wine_glass"],keywords:["drink","italian","alcohol","girls night","parties"],unicode:"1f377"},{names:["cocktail"],keywords:["drink","cocktail","alcohol","girls night","parties"],unicode:"1f378"},{names:["tropical_drink"],keywords:["drink","cocktail","tropical",
+"alcohol"],unicode:"1f379"},{names:["beer"],keywords:["drink","beer","alcohol","parties"],unicode:"1f37a"},{names:["beers"],keywords:"drink;cheers;beer;alcohol;thank you;boys night;parties".split(";"),unicode:"1f37b"},{names:["clinking_glass","champagne_glass"],keywords:[],unicode:"1f942"},{names:["whisky","tumbler_glass"],keywords:["booze"],unicode:"1f943"},{names:["fork_and_knife_with_plate","fork_knife_plate"],keywords:["object","food"],unicode:"1f37d"},{names:["fork_and_knife"],keywords:["object",
+"weapon","food"],unicode:"1f374"},{names:["spoon"],keywords:[],unicode:"1f944"},{names:["knife"],keywords:["object","weapon"],unicode:"1f52a"},{names:["amphora"],keywords:["object"],unicode:"1f3fa"},{names:["earth_africa"],keywords:["map","vacation","globe"],unicode:"1f30d"},{names:["earth_americas"],keywords:["map","vacation","globe"],unicode:"1f30e"},{names:["earth_asia"],keywords:["map","vacation","globe"],unicode:"1f30f"},{names:["globe_with_meridians"],keywords:["symbol","globe"],unicode:"1f310"},
+{names:["world_map","map"],keywords:["travel","map","vacation"],unicode:"1f5fa"},{names:["japan"],keywords:["places","travel","map","vacation","tropical"],unicode:"1f5fe"},{names:["snow_capped_mountain","mountain_snow"],keywords:["places","travel","vacation","cold","camp"],unicode:"1f3d4"},{names:["mountain"],keywords:["places","travel","vacation","camp"],unicode:"26f0"},{names:["volcano"],keywords:["places","tropical"],unicode:"1f30b"},{names:["mount_fuji"],keywords:["places","travel","vacation",
+"cold","camp"],unicode:"1f5fb"},{names:["camping"],keywords:["places","travel","vacation","camp"],unicode:"1f3d5"},{names:["beach_with_umbrella","beach"],keywords:"places travel vacation tropical beach swim".split(" "),unicode:"1f3d6"},{names:["desert"],keywords:["places","travel","vacation","hot"],unicode:"1f3dc"},{names:["desert_island","island"],keywords:"places travel vacation tropical beach swim".split(" "),unicode:"1f3dd"},{names:["national_park","park"],keywords:["travel","vacation","park",
+"camp"],unicode:"1f3de"},{names:["stadium"],keywords:["places","building","travel","vacation","boys night"],unicode:"1f3df"},{names:["classical_building"],keywords:["places","building","travel","vacation"],unicode:"1f3db"},{names:["building_construction","construction_site"],keywords:["building","crane"],unicode:"1f3d7"},{names:["house_buildings","homes"],keywords:["places","building","house"],unicode:"1f3d8"},{names:["cityscape"],keywords:["places","building","vacation"],unicode:"1f3d9"},{names:["derelict_house_building",
+"house_abandoned"],keywords:["places","building","house"],unicode:"1f3da"},{names:["house"],keywords:["places","building","house"],unicode:"1f3e0"},{names:["house_with_garden"],keywords:["places","building","house"],unicode:"1f3e1"},{names:["office"],keywords:["places","building","work"],unicode:"1f3e2"},{names:["post_office"],keywords:["places","building","post office"],unicode:"1f3e3"},{names:["european_post_office"],keywords:["places","building","post office"],unicode:"1f3e4"},{names:["hospital"],
+keywords:["places","building","health","911"],unicode:"1f3e5"},{names:["bank"],keywords:["places","building"],unicode:"1f3e6"},{names:["hotel"],keywords:["places","building","vacation"],unicode:"1f3e8"},{names:["love_hotel"],keywords:["places","building","love"],unicode:"1f3e9"},{names:["convenience_store"],keywords:["places","building"],unicode:"1f3ea"},{names:["school"],keywords:["places","building"],unicode:"1f3eb"},{names:["department_store"],keywords:["places","building"],unicode:"1f3ec"},{names:["factory"],
+keywords:["places","building","travel","steam"],unicode:"1f3ed"},{names:["japanese_castle"],keywords:["places","building","travel","vacation"],unicode:"1f3ef"},{names:["european_castle"],keywords:["places","building","travel","vacation"],unicode:"1f3f0"},{names:["wedding"],keywords:["places","wedding","building","love","parties"],unicode:"1f492"},{names:["tokyo_tower"],keywords:["places","travel","vacation","eiffel tower"],unicode:"1f5fc"},{names:["statue_of_liberty"],keywords:"places;america;travel;vacation;statue of liberty;free speech".split(";"),
+unicode:"1f5fd"},{names:["church"],keywords:["places","wedding","religion","building","condolence"],unicode:"26ea"},{names:["mosque"],keywords:["places","religion","building","vacation","condolence"],unicode:"1f54c"},{names:["synagogue"],keywords:"places religion building travel vacation condolence".split(" "),unicode:"1f54d"},{names:["shinto_shrine"],keywords:["places","building","travel","vacation"],unicode:"26e9"},{names:["kaaba"],keywords:["places","religion","building","condolence"],unicode:"1f54b"},
+{names:["fountain"],keywords:["travel","vacation"],unicode:"26f2"},{names:["tent"],keywords:["places","travel","vacation","camp"],unicode:"26fa"},{names:["foggy"],keywords:["places","building","sky","travel","vacation"],unicode:"1f301"},{names:["night_with_stars"],keywords:["places","building","sky","vacation","goodnight"],unicode:"1f303"},{names:["sunrise_over_mountains"],keywords:"places sky travel vacation day sun camp morning".split(" "),unicode:"1f304"},{names:["sunrise"],keywords:"places;sky;travel;vacation;tropical;day;sun;hump day;morning".split(";"),
+unicode:"1f305"},{names:["city_dusk"],keywords:["places","building"],unicode:"1f306"},{names:["city_sunrise","city_sunset"],keywords:["places","building","sky","vacation"],unicode:"1f307"},{names:["bridge_at_night"],keywords:["places","travel","vacation","goodnight"],unicode:"1f309"},{names:["hotsprings"],keywords:["symbol"],unicode:"2668"},{names:["milky_way"],keywords:["places","space","sky","travel","vacation"],unicode:"1f30c"},{names:["carousel_horse"],keywords:["places","object","vacation","roller coaster",
+"carousel"],unicode:"1f3a0"},{names:["ferris_wheel"],keywords:["places","vacation","ferris wheel"],unicode:"1f3a1"},{names:["roller_coaster"],keywords:["places","vacation","roller coaster"],unicode:"1f3a2"},{names:["barber"],keywords:["object"],unicode:"1f488"},{names:["circus_tent"],keywords:["circus tent"],unicode:"1f3aa"},{names:["performing_arts"],keywords:["theatre","movie"],unicode:"1f3ad"},{names:["frame_with_picture","frame_photo"],keywords:["travel","vacation"],unicode:"1f5bc"},{names:["art"],
+keywords:[],unicode:"1f3a8"},{names:["slot_machine"],keywords:["game","boys night"],unicode:"1f3b0"},{names:["steam_locomotive"],keywords:["transportation","travel","train","steam"],unicode:"1f682"},{names:["railway_car"],keywords:["transportation","travel","train"],unicode:"1f683"},{names:["bullettrain_side"],keywords:["transportation","travel","train"],unicode:"1f684"},{names:["bullettrain_front"],keywords:["transportation","travel","train"],unicode:"1f685"},{names:["train2"],keywords:["transportation",
+"travel","train"],unicode:"1f686"},{names:["metro"],keywords:["transportation","travel","train"],unicode:"1f687"},{names:["light_rail"],keywords:["transportation","travel","train"],unicode:"1f688"},{names:["station"],keywords:["transportation","travel","train"],unicode:"1f689"},{names:["tram"],keywords:["transportation","travel","train"],unicode:"1f68a"},{names:["monorail"],keywords:["transportation","travel","train","vacation"],unicode:"1f69d"},{names:["mountain_railway"],keywords:["transportation",
+"travel","train"],unicode:"1f69e"},{names:["train"],keywords:["transportation","travel","train"],unicode:"1f68b"},{names:["bus"],keywords:["transportation","bus","office"],unicode:"1f68c"},{names:["oncoming_bus"],keywords:["transportation","bus","travel"],unicode:"1f68d"},{names:["trolleybus"],keywords:["transportation","bus","travel"],unicode:"1f68e"},{names:["minibus"],keywords:["transportation","bus"],unicode:"1f690"},{names:["ambulance"],keywords:["transportation","911"],unicode:"1f691"},{names:["fire_engine"],
+keywords:["transportation","truck","911"],unicode:"1f692"},{names:["police_car"],keywords:["transportation","car","police","911"],unicode:"1f693"},{names:["oncoming_police_car"],keywords:["transportation","car","police","911"],unicode:"1f694"},{names:["taxi"],keywords:["transportation","car","travel"],unicode:"1f695"},{names:["oncoming_taxi"],keywords:["transportation","car","travel"],unicode:"1f696"},{names:["red_car"],keywords:["transportation","car","travel"],unicode:"1f697"},{names:["oncoming_automobile"],
+keywords:["transportation","car","travel"],unicode:"1f698"},{names:["blue_car"],keywords:["transportation","car","travel"],unicode:"1f699"},{names:["truck"],keywords:["transportation","truck"],unicode:"1f69a"},{names:["articulated_lorry"],keywords:["transportation","truck"],unicode:"1f69b"},{names:["tractor"],keywords:["transportation"],unicode:"1f69c"},{names:["bike"],keywords:["transportation","travel","bike"],unicode:"1f6b2"},{names:["scooter"],keywords:[],unicode:"1f6f4"},{names:["motorbike",
+"motor_scooter"],keywords:["moped"],unicode:"1f6f5"},{names:["busstop"],keywords:["object"],unicode:"1f68f"},{names:["motorway"],keywords:["travel","vacation","camp"],unicode:"1f6e3"},{names:["railroad_track","railway_track"],keywords:["travel","train","vacation"],unicode:"1f6e4"},{names:["fuelpump"],keywords:["object","gas pump"],unicode:"26fd"},{names:["rotating_light"],keywords:["transportation","object","police","911"],unicode:"1f6a8"},{names:["traffic_light"],keywords:["object","stop light"],
+unicode:"1f6a5"},{names:["vertical_traffic_light"],keywords:["object","stop light"],unicode:"1f6a6"},{names:["construction"],keywords:["object"],unicode:"1f6a7"},{names:["stop_sign","octagonal_sign"],keywords:[],unicode:"1f6d1"},{names:["anchor"],keywords:["object","travel","boat","vacation"],unicode:"2693"},{names:["sailboat"],keywords:["transportation","travel","boat","vacation"],unicode:"26f5"},{names:["kayak","canoe"],keywords:[],unicode:"1f6f6"},{names:["speedboat"],keywords:["transportation",
+"travel","boat","vacation","tropical"],unicode:"1f6a4"},{names:["passenger_ship","cruise_ship"],keywords:["transportation","travel","boat","vacation"],unicode:"1f6f3"},{names:["ferry"],keywords:["transportation","travel","boat","vacation"],unicode:"26f4"},{names:["motorboat"],keywords:["transportation","travel","boat"],unicode:"1f6e5"},{names:["ship"],keywords:["transportation","travel","boat","vacation"],unicode:"1f6a2"},{names:["airplane"],keywords:["transportation","plane","travel","vacation",
+"fly"],unicode:"2708"},{names:["small_airplane","airplane_small"],keywords:["transportation","plane","travel","vacation","fly"],unicode:"1f6e9"},{names:["airplane_departure"],keywords:["transportation","plane","travel","vacation","fly"],unicode:"1f6eb"},{names:["airplane_arriving"],keywords:["transportation","plane","travel","vacation","fly"],unicode:"1f6ec"},{names:["seat"],keywords:["transportation","object","travel","vacation"],unicode:"1f4ba"},{names:["helicopter"],keywords:["transportation",
+"plane","travel","fly"],unicode:"1f681"},{names:["suspension_railway"],keywords:["transportation","travel","train"],unicode:"1f69f"},{names:["mountain_cableway"],keywords:["transportation","travel","train"],unicode:"1f6a0"},{names:["aerial_tramway"],keywords:["transportation","travel","train"],unicode:"1f6a1"},{names:["rocket"],keywords:["transportation","object","space","fly","blast"],unicode:"1f680"},{names:["satellite_orbital"],keywords:["object"],unicode:"1f6f0"},{names:["bellhop_bell","bellhop"],
+keywords:["object"],unicode:"1f6ce"},{names:["door"],keywords:["object"],unicode:"1f6aa"},{names:["sleeping_accommodation"],keywords:["tired"],unicode:"1f6cc"},{names:["bed"],keywords:["object","tired"],unicode:"1f6cf"},{names:["couch_and_lamp","couch"],keywords:["object"],unicode:"1f6cb"},{names:["toilet"],keywords:["object","bathroom"],unicode:"1f6bd"},{names:["shower"],keywords:["object","bathroom"],unicode:"1f6bf"},{names:["bath"],keywords:["bathroom","tired","diversity","steam"],unicode:"1f6c0"},
+{names:["bath_tone1"],keywords:[],unicode:"1f6c0-1f3fb"},{names:["bath_tone2"],keywords:[],unicode:"1f6c0-1f3fc"},{names:["bath_tone3"],keywords:[],unicode:"1f6c0-1f3fd"},{names:["bath_tone4"],keywords:[],unicode:"1f6c0-1f3fe"},{names:["bath_tone5"],keywords:[],unicode:"1f6c0-1f3ff"},{names:["bathtub"],keywords:["object","bathroom","tired","steam"],unicode:"1f6c1"},{names:["hourglass"],keywords:["object","time"],unicode:"231b"},{names:["hourglass_flowing_sand"],keywords:["object","time"],unicode:"23f3"},
+{names:["watch"],keywords:["electronics","time"],unicode:"231a"},{names:["alarm_clock"],keywords:["object","time"],unicode:"23f0"},{names:["stopwatch"],keywords:["electronics","time"],unicode:"23f1"},{names:["timer_clock","timer"],keywords:["object","time"],unicode:"23f2"},{names:["mantlepiece_clock","clock"],keywords:["object","time"],unicode:"1f570"},{names:["clock12"],keywords:["symbol","time"],unicode:"1f55b"},{names:["clock1230"],keywords:["symbol","time"],unicode:"1f567"},{names:["clock1"],
+keywords:["symbol","time"],unicode:"1f550"},{names:["clock130"],keywords:["symbol","time"],unicode:"1f55c"},{names:["clock2"],keywords:["symbol","time"],unicode:"1f551"},{names:["clock230"],keywords:["symbol","time"],unicode:"1f55d"},{names:["clock3"],keywords:["symbol","time"],unicode:"1f552"},{names:["clock330"],keywords:["symbol","time"],unicode:"1f55e"},{names:["clock4"],keywords:["symbol","time"],unicode:"1f553"},{names:["clock430"],keywords:["symbol","time"],unicode:"1f55f"},{names:["clock5"],
+keywords:["symbol","time"],unicode:"1f554"},{names:["clock530"],keywords:["symbol","time"],unicode:"1f560"},{names:["clock6"],keywords:["symbol","time"],unicode:"1f555"},{names:["clock630"],keywords:["symbol","time"],unicode:"1f561"},{names:["clock7"],keywords:["symbol","time"],unicode:"1f556"},{names:["clock730"],keywords:["symbol","time"],unicode:"1f562"},{names:["clock8"],keywords:["symbol","time"],unicode:"1f557"},{names:["clock830"],keywords:["symbol","time"],unicode:"1f563"},{names:["clock9"],
+keywords:["symbol","time"],unicode:"1f558"},{names:["clock930"],keywords:["symbol","time"],unicode:"1f564"},{names:["clock10"],keywords:["symbol","time"],unicode:"1f559"},{names:["clock1030"],keywords:["symbol","time"],unicode:"1f565"},{names:["clock11"],keywords:["symbol","time"],unicode:"1f55a"},{names:["clock1130"],keywords:["symbol","time"],unicode:"1f566"},{names:["new_moon"],keywords:["space","sky","moon"],unicode:"1f311"},{names:["waxing_crescent_moon"],keywords:["space","sky","moon"],unicode:"1f312"},
+{names:["first_quarter_moon"],keywords:["space","sky","moon"],unicode:"1f313"},{names:["waxing_gibbous_moon"],keywords:["space","sky","moon"],unicode:"1f314"},{names:["full_moon"],keywords:["space","sky","moon"],unicode:"1f315"},{names:["waning_gibbous_moon"],keywords:["space","sky","moon"],unicode:"1f316"},{names:["last_quarter_moon"],keywords:["space","sky","moon"],unicode:"1f317"},{names:["waning_crescent_moon"],keywords:["space","sky","moon"],unicode:"1f318"},{names:["crescent_moon"],keywords:["space",
+"sky","goodnight","moon"],unicode:"1f319"},{names:["new_moon_with_face"],keywords:["space","sky","goodnight","moon"],unicode:"1f31a"},{names:["first_quarter_moon_with_face"],keywords:["space","sky","moon"],unicode:"1f31b"},{names:["last_quarter_moon_with_face"],keywords:["space","sky","moon"],unicode:"1f31c"},{names:["thermometer"],keywords:["object","science","health","hot"],unicode:"1f321"},{names:["sunny"],keywords:"weather sky day sun hot morning".split(" "),unicode:"2600"},{names:["full_moon_with_face"],
+keywords:["space","sky","goodnight","moon"],unicode:"1f31d"},{names:["sun_with_face"],keywords:["sky","day","sun","hump day","morning"],unicode:"1f31e"},{names:["star"],keywords:["space","sky","star"],unicode:"2b50"},{names:["star2"],keywords:["space","sky","star"],unicode:"1f31f"},{names:["stars"],keywords:["space"],unicode:"1f320"},{names:["cloud"],keywords:["weather","sky","cloud","cold","rain"],unicode:"2601"},{names:["partly_sunny"],keywords:["weather","sky","cloud","sun"],unicode:"26c5"},{names:["thunder_cloud_and_rain",
+"thunder_cloud_rain"],keywords:["weather","sky","cloud","cold","rain"],unicode:"26c8"},{names:["white_sun_with_small_cloud","white_sun_small_cloud"],keywords:["weather","sky","cloud","sun"],unicode:"1f324"},{names:["white_sun_behind_cloud","white_sun_cloud"],keywords:["weather","sky","cloud","cold","sun"],unicode:"1f325"},{names:["white_sun_behind_cloud_with_rain","white_sun_rain_cloud"],keywords:"weather sky cloud cold rain sun".split(" "),unicode:"1f326"},{names:["cloud_with_rain","cloud_rain"],
+keywords:"weather winter sky cloud cold rain".split(" "),unicode:"1f327"},{names:["cloud_with_snow","cloud_snow"],keywords:"weather winter sky cloud cold snow".split(" "),unicode:"1f328"},{names:["cloud_with_lightning","cloud_lightning"],keywords:["weather","sky","cloud","cold","rain"],unicode:"1f329"},{names:["cloud_with_tornado","cloud_tornado"],keywords:["weather","sky","cold"],unicode:"1f32a"},{names:["fog"],keywords:["weather","sky","cold"],unicode:"1f32b"},{names:["wind_blowing_face"],keywords:["weather",
+"cold"],unicode:"1f32c"},{names:["cyclone"],keywords:["symbol","drugs"],unicode:"1f300"},{names:["rainbow"],keywords:["weather","gay","sky","rain"],unicode:"1f308"},{names:["closed_umbrella"],keywords:["object","sky","rain","accessories"],unicode:"1f302"},{names:["umbrella2"],keywords:["weather","object","sky","cold"],unicode:"2602"},{names:["umbrella"],keywords:["weather","sky","cold","rain"],unicode:"2614"},{names:["umbrella_on_ground","beach_umbrella"],keywords:["travel","vacation","tropical"],
+unicode:"26f1"},{names:["zap"],keywords:["weather","sky","diarrhea"],unicode:"26a1"},{names:["snowflake"],keywords:"weather winter sky holidays cold snow".split(" "),unicode:"2744"},{names:["snowman2"],keywords:"weather winter holidays christmas cold snow".split(" "),unicode:"2603"},{names:["snowman"],keywords:["weather","winter","holidays","cold","snow"],unicode:"26c4"},{names:["comet"],keywords:["space","sky"],unicode:"2604"},{names:["flame","fire"],keywords:["wth","hot"],unicode:"1f525"},{names:["droplet"],
+keywords:["weather","sky","rain"],unicode:"1f4a7"},{names:["ocean"],keywords:["weather","boat","tropical","swim"],unicode:"1f30a"},{names:["jack_o_lantern"],keywords:["holidays","halloween"],unicode:"1f383"},{names:["christmas_tree"],keywords:["plant","holidays","christmas","trees"],unicode:"1f384"},{names:["fireworks"],keywords:["parties"],unicode:"1f386"},{names:["sparkler"],keywords:["parties"],unicode:"1f387"},{names:["sparkles"],keywords:["star","girls night"],unicode:"2728"},{names:["balloon"],
+keywords:["object","birthday","good","parties"],unicode:"1f388"},{names:["tada"],keywords:"object;birthday;holidays;cheers;good;girls night;boys night;parties".split(";"),unicode:"1f389"},{names:["confetti_ball"],keywords:"object;birthday;holidays;cheers;girls night;boys night;parties".split(";"),unicode:"1f38a"},{names:["tanabata_tree"],keywords:["nature","plant","trees"],unicode:"1f38b"},{names:["bamboo"],keywords:["nature","plant"],unicode:"1f38d"},{names:["dolls"],keywords:["people","japan"],
+unicode:"1f38e"},{names:["flags"],keywords:["object","japan"],unicode:"1f38f"},{names:["wind_chime"],keywords:["object","japan"],unicode:"1f390"},{names:["rice_scene"],keywords:["places","space","sky","travel"],unicode:"1f391"},{names:["ribbon"],keywords:["object","gift","birthday"],unicode:"1f380"},{names:["gift"],keywords:"object gift birthday holidays christmas parties".split(" "),unicode:"1f381"},{names:["reminder_ribbon"],keywords:["award"],unicode:"1f397"},{names:["admission_tickets","tickets"],
+keywords:["theatre","movie","parties"],unicode:"1f39f"},{names:["ticket"],keywords:["theatre","movie","parties"],unicode:"1f3ab"},{names:["military_medal"],keywords:["object","award","win"],unicode:"1f396"},{names:["trophy"],keywords:"object game award win perfect parties".split(" "),unicode:"1f3c6"},{names:["sports_medal","medal"],keywords:["object","award","sport","win","perfect"],unicode:"1f3c5"},{names:["first_place_medal","first_place"],keywords:[],unicode:"1f947"},{names:["second_place_medal",
+"second_place"],keywords:[],unicode:"1f948"},{names:["third_place_medal","third_place"],keywords:[],unicode:"1f949"},{names:["soccer"],keywords:["game","ball","sport","soccer","football"],unicode:"26bd"},{names:["baseball"],keywords:["game","ball","sport","baseball"],unicode:"26be"},{names:["basketball"],keywords:["game","ball","sport","basketball"],unicode:"1f3c0"},{names:["volleyball"],keywords:["game","ball","sport","volleyball"],unicode:"1f3d0"},{names:["football"],keywords:["america","game",
+"ball","sport","football"],unicode:"1f3c8"},{names:["rugby_football"],keywords:["game","sport","football"],unicode:"1f3c9"},{names:["tennis"],keywords:["game","ball","sport","tennis"],unicode:"1f3be"},{names:["8ball"],keywords:"game;ball;sport;billiards;luck;boys night".split(";"),unicode:"1f3b1"},{names:["bowling"],keywords:["game","ball","sport","boys night"],unicode:"1f3b3"},{names:["cricket_bat_ball","cricket"],keywords:["ball","sport","cricket"],unicode:"1f3cf"},{names:["field_hockey"],keywords:["ball",
+"sport","hockey"],unicode:"1f3d1"},{names:["hockey"],keywords:["game","sport","hockey"],unicode:"1f3d2"},{names:["table_tennis","ping_pong"],keywords:["game","ball","sport","ping pong"],unicode:"1f3d3"},{names:["badminton"],keywords:["game","sport","badminton"],unicode:"1f3f8"},{names:["boxing_gloves","boxing_glove"],keywords:[],unicode:"1f94a"},{names:["karate_uniform","martial_arts_uniform"],keywords:[],unicode:"1f94b"},{names:["goal_net","goal"],keywords:[],unicode:"1f945"},{names:["dart"],keywords:["game",
+"sport","boys night"],unicode:"1f3af"},{names:["golf"],keywords:["game","ball","vacation","sport","golf"],unicode:"26f3"},{names:["ice_skate"],keywords:["cold","sport","ice skating"],unicode:"26f8"},{names:["fishing_pole_and_fish"],keywords:["vacation","sport","fishing"],unicode:"1f3a3"},{names:["running_shirt_with_sash"],keywords:["award"],unicode:"1f3bd"},{names:["ski"],keywords:["cold","sport","skiing"],unicode:"1f3bf"},{names:["video_game"],keywords:["electronics","game","boys night"],unicode:"1f3ae"},
+{names:["joystick"],keywords:["electronics","game","boys night"],unicode:"1f579"},{names:["game_die"],keywords:["object","game","boys night"],unicode:"1f3b2"},{names:["spades"],keywords:["symbol","game"],unicode:"2660"},{names:["hearts"],keywords:["love","symbol","game"],unicode:"2665"},{names:["diamonds"],keywords:["shapes","symbol","game"],unicode:"2666"},{names:["clubs"],keywords:["symbol","game"],unicode:"2663"},{names:["black_joker"],keywords:["object","symbol","game"],unicode:"1f0cf"},{names:["mahjong"],
+keywords:["object","symbol","game"],unicode:"1f004"},{names:["flower_playing_cards"],keywords:["object","symbol"],unicode:"1f3b4"},{names:["mute"],keywords:["alarm","symbol"],unicode:"1f507"},{names:["speaker"],keywords:["alarm","symbol"],unicode:"1f508"},{names:["sound"],keywords:["alarm","symbol"],unicode:"1f509"},{names:["loud_sound"],keywords:["alarm","symbol"],unicode:"1f50a"},{names:["loudspeaker"],keywords:["object","alarm","symbol"],unicode:"1f4e2"},{names:["mega"],keywords:["object","sport"],
+unicode:"1f4e3"},{names:["postal_horn"],keywords:["object"],unicode:"1f4ef"},{names:["bell"],keywords:["object","alarm","symbol"],unicode:"1f514"},{names:["no_bell"],keywords:["alarm","symbol"],unicode:"1f515"},{names:["musical_score"],keywords:["instruments"],unicode:"1f3bc"},{names:["musical_note"],keywords:["instruments","symbol"],unicode:"1f3b5"},{names:["notes"],keywords:["instruments","symbol"],unicode:"1f3b6"},{names:["studio_microphone","microphone2"],keywords:["electronics","object"],unicode:"1f399"},
+{names:["level_slider"],keywords:[],unicode:"1f39a"},{names:["control_knobs"],keywords:["time"],unicode:"1f39b"},{names:["microphone"],keywords:["instruments"],unicode:"1f3a4"},{names:["headphones"],keywords:["instruments"],unicode:"1f3a7"},{names:["radio"],keywords:["electronics"],unicode:"1f4fb"},{names:["saxophone"],keywords:["instruments"],unicode:"1f3b7"},{names:["guitar"],keywords:["instruments"],unicode:"1f3b8"},{names:["musical_keyboard"],keywords:["instruments"],unicode:"1f3b9"},{names:["trumpet"],
+keywords:["instruments"],unicode:"1f3ba"},{names:["violin"],keywords:["instruments","sarcastic"],unicode:"1f3bb"},{names:["drum_with_drumsticks","drum"],keywords:[],unicode:"1f941"},{names:["iphone"],keywords:["electronics","phone","selfie"],unicode:"1f4f1"},{names:["calling"],keywords:["electronics","phone","selfie"],unicode:"1f4f2"},{names:["telephone"],keywords:["electronics","phone"],unicode:"260e"},{names:["telephone_receiver"],keywords:["electronics","phone"],unicode:"1f4de"},{names:["pager"],
+keywords:["electronics","work"],unicode:"1f4df"},{names:["fax"],keywords:["electronics","work","office"],unicode:"1f4e0"},{names:["battery"],keywords:["object"],unicode:"1f50b"},{names:["electric_plug"],keywords:["electronics"],unicode:"1f50c"},{names:["computer"],keywords:["electronics","work","office"],unicode:"1f4bb"},{names:["desktop_computer","desktop"],keywords:["electronics","work"],unicode:"1f5a5"},{names:["printer"],keywords:["electronics","work","office"],unicode:"1f5a8"},{names:["keyboard"],
+keywords:["electronics","work","office"],unicode:"2328"},{names:["three_button_mouse","mouse_three_button"],keywords:["electronics","work","game","office"],unicode:"1f5b1"},{names:["trackball"],keywords:["electronics","work","game","office"],unicode:"1f5b2"},{names:["minidisc"],keywords:["electronics"],unicode:"1f4bd"},{names:["floppy_disk"],keywords:["electronics","office"],unicode:"1f4be"},{names:["cd"],keywords:["electronics"],unicode:"1f4bf"},{names:["dvd"],keywords:["electronics"],unicode:"1f4c0"},
+{names:["movie_camera"],keywords:["object","camera","movie"],unicode:"1f3a5"},{names:["film_frames"],keywords:["object","camera","movie"],unicode:"1f39e"},{names:["film_projector","projector"],keywords:["object","camera","movie"],unicode:"1f4fd"},{names:["clapper"],keywords:["movie"],unicode:"1f3ac"},{names:["tv"],keywords:["electronics"],unicode:"1f4fa"},{names:["camera"],keywords:["electronics","camera","selfie"],unicode:"1f4f7"},{names:["camera_with_flash"],keywords:["electronics","camera"],unicode:"1f4f8"},
+{names:["video_camera"],keywords:["electronics","camera","movie"],unicode:"1f4f9"},{names:["vhs"],keywords:["electronics"],unicode:"1f4fc"},{names:["mag"],keywords:["object"],unicode:"1f50d"},{names:["mag_right"],keywords:["object"],unicode:"1f50e"},{names:["microscope"],keywords:["object","science"],unicode:"1f52c"},{names:["telescope"],keywords:["object","space","science"],unicode:"1f52d"},{names:["satellite"],keywords:["object"],unicode:"1f4e1"},{names:["candle"],keywords:["object"],unicode:"1f56f"},
+{names:["bulb"],keywords:["object","science"],unicode:"1f4a1"},{names:["flashlight"],keywords:["electronics","object"],unicode:"1f526"},{names:["izakaya_lantern"],keywords:["object","japan"],unicode:"1f3ee"},{names:["notebook_with_decorative_cover"],keywords:["object","office","write"],unicode:"1f4d4"},{names:["closed_book"],keywords:["object","office","write","book"],unicode:"1f4d5"},{names:["book"],keywords:["object","office","write","book"],unicode:"1f4d6"},{names:["green_book"],keywords:["object",
+"office","book"],unicode:"1f4d7"},{names:["blue_book"],keywords:["object","office","write","book"],unicode:"1f4d8"},{names:["orange_book"],keywords:["object","office","write","book"],unicode:"1f4d9"},{names:["books"],keywords:["object","office","write","book"],unicode:"1f4da"},{names:["notebook"],keywords:["object","office","write"],unicode:"1f4d3"},{names:["ledger"],keywords:["object","office","write"],unicode:"1f4d2"},{names:["page_with_curl"],keywords:["office","write"],unicode:"1f4c3"},{names:["scroll"],
+keywords:["object","office"],unicode:"1f4dc"},{names:["page_facing_up"],keywords:["work","office","write"],unicode:"1f4c4"},{names:["newspaper"],keywords:["office","write"],unicode:"1f4f0"},{names:["rolled_up_newspaper","newspaper2"],keywords:["office","write"],unicode:"1f5de"},{names:["bookmark_tabs"],keywords:["office","write"],unicode:"1f4d1"},{names:["bookmark"],keywords:["object","book"],unicode:"1f516"},{names:["label"],keywords:["object"],unicode:"1f3f7"},{names:["moneybag"],keywords:["bag",
+"award","money"],unicode:"1f4b0"},{names:["yen"],keywords:["money"],unicode:"1f4b4"},{names:["dollar"],keywords:["money"],unicode:"1f4b5"},{names:["euro"],keywords:["money"],unicode:"1f4b6"},{names:["pound"],keywords:["money"],unicode:"1f4b7"},{names:["money_with_wings"],keywords:["money","boys night"],unicode:"1f4b8"},{names:["credit_card"],keywords:["object","money","boys night"],unicode:"1f4b3"},{names:["chart"],keywords:["symbol","money"],unicode:"1f4b9"},{names:["currency_exchange"],keywords:["symbol",
+"money"],unicode:"1f4b1"},{names:["heavy_dollar_sign"],keywords:["math","symbol","money"],unicode:"1f4b2"},{names:["envelope"],keywords:["object","office","write"],unicode:"2709"},{names:["email","e-mail"],keywords:["office"],unicode:"1f4e7"},{names:["incoming_envelope"],keywords:["object"],unicode:"1f4e8"},{names:["envelope_with_arrow"],keywords:["object","office"],unicode:"1f4e9"},{names:["outbox_tray"],keywords:["work","office"],unicode:"1f4e4"},{names:["inbox_tray"],keywords:["work","office"],
+unicode:"1f4e5"},{names:["package"],keywords:["object","gift","office"],unicode:"1f4e6"},{names:["mailbox"],keywords:["object"],unicode:"1f4eb"},{names:["mailbox_closed"],keywords:["object","office"],unicode:"1f4ea"},{names:["mailbox_with_mail"],keywords:["object"],unicode:"1f4ec"},{names:["mailbox_with_no_mail"],keywords:["object"],unicode:"1f4ed"},{names:["postbox"],keywords:["object"],unicode:"1f4ee"},{names:["ballot_box_with_ballot","ballot_box"],keywords:["object","office"],unicode:"1f5f3"},
+{names:["pencil2"],keywords:["object","office","write"],unicode:"270f"},{names:["black_nib"],keywords:["object","office","write"],unicode:"2712"},{names:["lower_left_fountain_pen","pen_fountain"],keywords:["object","office","write"],unicode:"1f58b"},{names:["lower_left_ballpoint_pen","pen_ballpoint"],keywords:["object","office","write"],unicode:"1f58a"},{names:["lower_left_paintbrush","paintbrush"],keywords:["object","office","write"],unicode:"1f58c"},{names:["lower_left_crayon","crayon"],keywords:["object",
+"office","write"],unicode:"1f58d"},{names:["pencil"],keywords:["work","office","write"],unicode:"1f4dd"},{names:["briefcase"],keywords:["bag","work","accessories","nutcase","job"],unicode:"1f4bc"},{names:["file_folder"],keywords:["work","office"],unicode:"1f4c1"},{names:["open_file_folder"],keywords:["work","office"],unicode:"1f4c2"},{names:["card_index_dividers","dividers"],keywords:["work","office"],unicode:"1f5c2"},{names:["date"],keywords:["object","office"],unicode:"1f4c5"},{names:["calendar"],
+keywords:["object","office"],unicode:"1f4c6"},{names:["spiral_note_pad","notepad_spiral"],keywords:["work","office","write"],unicode:"1f5d2"},{names:["spiral_calendar_pad","calendar_spiral"],keywords:["object","office"],unicode:"1f5d3"},{names:["card_index"],keywords:["object","work","office"],unicode:"1f4c7"},{names:["chart_with_upwards_trend"],keywords:["work","office"],unicode:"1f4c8"},{names:["chart_with_downwards_trend"],keywords:["work","office"],unicode:"1f4c9"},{names:["bar_chart"],keywords:["work",
+"office"],unicode:"1f4ca"},{names:["clipboard"],keywords:["object","work","office","write"],unicode:"1f4cb"},{names:["pushpin"],keywords:["object","office"],unicode:"1f4cc"},{names:["round_pushpin"],keywords:["object","office"],unicode:"1f4cd"},{names:["paperclip"],keywords:["object","work","office"],unicode:"1f4ce"},{names:["linked_paperclips","paperclips"],keywords:["object","work","office"],unicode:"1f587"},{names:["straight_ruler"],keywords:["object","tool","office"],unicode:"1f4cf"},{names:["triangular_ruler"],
+keywords:["object","tool","office"],unicode:"1f4d0"},{names:["scissors"],keywords:["object","tool","weapon","office"],unicode:"2702"},{names:["card_file_box","card_box"],keywords:["object","work","office"],unicode:"1f5c3"},{names:["file_cabinet"],keywords:["object","work","office"],unicode:"1f5c4"},{names:["wastebasket"],keywords:["object","work"],unicode:"1f5d1"},{names:["lock"],keywords:["object","lock"],unicode:"1f512"},{names:["unlock"],keywords:["object","lock"],unicode:"1f513"},{names:["lock_with_ink_pen"],
+keywords:["object","lock"],unicode:"1f50f"},{names:["closed_lock_with_key"],keywords:["object","lock"],unicode:"1f510"},{names:["key"],keywords:["object","lock"],unicode:"1f511"},{names:["old_key","key2"],keywords:["object","lock"],unicode:"1f5dd"},{names:["hammer"],keywords:["object","tool","weapon"],unicode:"1f528"},{names:["pick"],keywords:["object","tool","weapon"],unicode:"26cf"},{names:["hammer_and_pick","hammer_pick"],keywords:["object","tool","weapon"],unicode:"2692"},{names:["hammer_and_wrench",
+"tools"],keywords:["object","tool"],unicode:"1f6e0"},{names:["dagger_knife","dagger"],keywords:["object","weapon"],unicode:"1f5e1"},{names:["crossed_swords"],keywords:["object","weapon"],unicode:"2694"},{names:["gun"],keywords:["object","weapon","dead","gun","sarcastic"],unicode:"1f52b"},{names:["archery","bow_and_arrow"],keywords:["weapon","sport"],unicode:"1f3f9"},{names:["shield"],keywords:["object"],unicode:"1f6e1"},{names:["wrench"],keywords:["object","tool"],unicode:"1f527"},{names:["nut_and_bolt"],
+keywords:["object","tool","nutcase"],unicode:"1f529"},{names:["gear"],keywords:["object","tool"],unicode:"2699"},{names:["compression"],keywords:[],unicode:"1f5dc"},{names:["alembic"],keywords:["object","science"],unicode:"2697"},{names:["scales"],keywords:["object"],unicode:"2696"},{names:["link"],keywords:["symbol","office"],unicode:"1f517"},{names:["chains"],keywords:["object","tool"],unicode:"26d3"},{names:["syringe"],keywords:["object","weapon","health","drugs"],unicode:"1f489"},{names:["pill"],
+keywords:["object","health","drugs"],unicode:"1f48a"},{names:["smoking"],keywords:["symbol","drugs","smoking"],unicode:"1f6ac"},{names:["coffin"],keywords:["object","dead","rip"],unicode:"26b0"},{names:["funeral_urn","urn"],keywords:["object","dead","rip"],unicode:"26b1"},{names:["moyai"],keywords:["travel","vacation"],unicode:"1f5ff"},{names:["oil_drum","oil"],keywords:["object"],unicode:"1f6e2"},{names:["crystal_ball"],keywords:["object","ball"],unicode:"1f52e"},{names:["shopping_trolley","shopping_cart"],
+keywords:[],unicode:"1f6d2"},{names:["atm"],keywords:["electronics","symbol","money"],unicode:"1f3e7"},{names:["put_litter_in_its_place"],keywords:["symbol"],unicode:"1f6ae"},{names:["potable_water"],keywords:["symbol"],unicode:"1f6b0"},{names:["wheelchair"],keywords:["symbol"],unicode:"267f"},{names:["mens"],keywords:["symbol"],unicode:"1f6b9"},{names:["womens"],keywords:["symbol"],unicode:"1f6ba"},{names:["restroom"],keywords:["symbol"],unicode:"1f6bb"},{names:["baby_symbol"],keywords:["symbol"],
+unicode:"1f6bc"},{names:["wc"],keywords:["symbol"],unicode:"1f6be"},{names:["passport_control"],keywords:["symbol"],unicode:"1f6c2"},{names:["customs"],keywords:["symbol"],unicode:"1f6c3"},{names:["baggage_claim"],keywords:["symbol"],unicode:"1f6c4"},{names:["left_luggage"],keywords:["symbol"],unicode:"1f6c5"},{names:["warning"],keywords:["symbol","punctuation"],unicode:"26a0"},{names:["children_crossing"],keywords:["symbol"],unicode:"1f6b8"},{names:["no_entry"],keywords:["symbol","circle"],unicode:"26d4"},
+{names:["no_entry_sign"],keywords:["symbol","circle"],unicode:"1f6ab"},{names:["no_bicycles"],keywords:["symbol"],unicode:"1f6b3"},{names:["no_smoking"],keywords:["symbol","smoking"],unicode:"1f6ad"},{names:["do_not_litter"],keywords:["symbol"],unicode:"1f6af"},{names:["non-potable_water"],keywords:["symbol"],unicode:"1f6b1"},{names:["no_pedestrians"],keywords:["symbol"],unicode:"1f6b7"},{names:["no_mobile_phones"],keywords:["symbol","phone"],unicode:"1f4f5"},{names:["underage"],keywords:["symbol"],
+unicode:"1f51e"},{names:["radioactive_sign","radioactive"],keywords:["symbol","science"],unicode:"2622"},{names:["biohazard_sign","biohazard"],keywords:["symbol","science"],unicode:"2623"},{names:["arrow_up"],keywords:["arrow","symbol"],unicode:"2b06"},{names:["arrow_upper_right"],keywords:["arrow","symbol"],unicode:"2197"},{names:["arrow_right"],keywords:["arrow","symbol"],unicode:"27a1"},{names:["arrow_lower_right"],keywords:["arrow","symbol"],unicode:"2198"},{names:["arrow_down"],keywords:["arrow",
+"symbol"],unicode:"2b07"},{names:["arrow_lower_left"],keywords:["arrow","symbol"],unicode:"2199"},{names:["arrow_left"],keywords:["arrow","symbol"],unicode:"2b05"},{names:["arrow_upper_left"],keywords:["arrow","symbol"],unicode:"2196"},{names:["arrow_up_down"],keywords:["arrow","symbol"],unicode:"2195"},{names:["left_right_arrow"],keywords:["arrow","symbol"],unicode:"2194"},{names:["leftwards_arrow_with_hook"],keywords:["arrow","symbol"],unicode:"21a9"},{names:["arrow_right_hook"],keywords:["arrow",
+"symbol"],unicode:"21aa"},{names:["arrow_heading_up"],keywords:["arrow","symbol"],unicode:"2934"},{names:["arrow_heading_down"],keywords:["arrow","symbol"],unicode:"2935"},{names:["arrows_clockwise"],keywords:["arrow","symbol"],unicode:"1f503"},{names:["arrows_counterclockwise"],keywords:["arrow","symbol"],unicode:"1f504"},{names:["back"],keywords:["arrow","symbol"],unicode:"1f519"},{names:["end"],keywords:["arrow","symbol"],unicode:"1f51a"},{names:["on"],keywords:["arrow","symbol"],unicode:"1f51b"},
+{names:["soon"],keywords:["arrow","symbol"],unicode:"1f51c"},{names:["top"],keywords:["arrow","symbol"],unicode:"1f51d"},{names:["worship_symbol","place_of_worship"],keywords:["religion","symbol","pray"],unicode:"1f6d0"},{names:["atom_symbol","atom"],keywords:["symbol","science"],unicode:"269b"},{names:["om_symbol"],keywords:["religion","symbol"],unicode:"1f549"},{names:["star_of_david"],keywords:["religion","jew","star","symbol"],unicode:"2721"},{names:["wheel_of_dharma"],keywords:["religion","symbol"],
+unicode:"2638"},{names:["yin_yang"],keywords:["symbol"],unicode:"262f"},{names:["latin_cross","cross"],keywords:["religion","symbol"],unicode:"271d"},{names:["orthodox_cross"],keywords:["religion","symbol"],unicode:"2626"},{names:["star_and_crescent"],keywords:["religion","symbol"],unicode:"262a"},{names:["peace_symbol","peace"],keywords:["symbol","peace","drugs"],unicode:"262e"},{names:["menorah"],keywords:["religion","object","jew","symbol","holidays"],unicode:"1f54e"},{names:["six_pointed_star"],
+keywords:["religion","jew","star","symbol"],unicode:"1f52f"},{names:["aries"],keywords:["zodiac","symbol"],unicode:"2648"},{names:["taurus"],keywords:["zodiac","symbol"],unicode:"2649"},{names:["gemini"],keywords:["zodiac","symbol"],unicode:"264a"},{names:["cancer"],keywords:["zodiac","symbol"],unicode:"264b"},{names:["leo"],keywords:["zodiac","symbol"],unicode:"264c"},{names:["virgo"],keywords:["zodiac","symbol"],unicode:"264d"},{names:["libra"],keywords:["zodiac","symbol"],unicode:"264e"},{names:["scorpius"],
+keywords:["zodiac","symbol"],unicode:"264f"},{names:["sagittarius"],keywords:["zodiac","symbol"],unicode:"2650"},{names:["capricorn"],keywords:["zodiac","symbol"],unicode:"2651"},{names:["aquarius"],keywords:["zodiac","symbol"],unicode:"2652"},{names:["pisces"],keywords:["zodiac","symbol"],unicode:"2653"},{names:["ophiuchus"],keywords:["symbol"],unicode:"26ce"},{names:["twisted_rightwards_arrows"],keywords:["arrow","symbol"],unicode:"1f500"},{names:["repeat"],keywords:["arrow","symbol"],unicode:"1f501"},
+{names:["repeat_one"],keywords:["arrow","symbol"],unicode:"1f502"},{names:["arrow_forward"],keywords:["arrow","symbol","triangle"],unicode:"25b6"},{names:["fast_forward"],keywords:["arrow","symbol"],unicode:"23e9"},{names:["next_track","track_next"],keywords:["arrow","symbol"],unicode:"23ed"},{names:["play_pause"],keywords:["arrow","symbol"],unicode:"23ef"},{names:["arrow_backward"],keywords:["arrow","symbol","triangle"],unicode:"25c0"},{names:["rewind"],keywords:["arrow","symbol"],unicode:"23ea"},
+{names:["previous_track","track_previous"],keywords:["arrow","symbol"],unicode:"23ee"},{names:["arrow_up_small"],keywords:["arrow","symbol","triangle"],unicode:"1f53c"},{names:["arrow_double_up"],keywords:["arrow","symbol"],unicode:"23eb"},{names:["arrow_down_small"],keywords:["arrow","symbol","triangle"],unicode:"1f53d"},{names:["arrow_double_down"],keywords:["arrow","symbol"],unicode:"23ec"},{names:["double_vertical_bar","pause_button"],keywords:["symbol"],unicode:"23f8"},{names:["stop_button"],
+keywords:["symbol","square"],unicode:"23f9"},{names:["record_button"],keywords:["symbol","circle"],unicode:"23fa"},{names:["eject_symbol","eject"],keywords:[],unicode:"23cf"},{names:["cinema"],keywords:["symbol","camera","movie"],unicode:"1f3a6"},{names:["low_brightness"],keywords:["symbol","sun"],unicode:"1f505"},{names:["high_brightness"],keywords:["symbol","sun"],unicode:"1f506"},{names:["signal_strength"],keywords:["symbol"],unicode:"1f4f6"},{names:["vibration_mode"],keywords:["symbol"],unicode:"1f4f3"},
+{names:["mobile_phone_off"],keywords:["symbol"],unicode:"1f4f4"},{names:["recycle"],keywords:["symbol"],unicode:"267b"},{names:["name_badge"],keywords:["work"],unicode:"1f4db"},{names:["fleur-de-lis"],keywords:["object","symbol"],unicode:"269c"},{names:["beginner"],keywords:["symbol"],unicode:"1f530"},{names:["trident"],keywords:["object","symbol"],unicode:"1f531"},{names:["o"],keywords:["symbol","circle"],unicode:"2b55"},{names:["white_check_mark"],keywords:["symbol"],unicode:"2705"},{names:["ballot_box_with_check"],
+keywords:["symbol"],unicode:"2611"},{names:["heavy_check_mark"],keywords:["symbol"],unicode:"2714"},{names:["heavy_multiplication_x"],keywords:["math","symbol"],unicode:"2716"},{names:["x"],keywords:["symbol","sol"],unicode:"274c"},{names:["negative_squared_cross_mark"],keywords:["symbol"],unicode:"274e"},{names:["heavy_plus_sign"],keywords:["math","symbol"],unicode:"2795"},{names:["heavy_minus_sign"],keywords:["math","symbol"],unicode:"2796"},{names:["heavy_division_sign"],keywords:["math","symbol"],
+unicode:"2797"},{names:["curly_loop"],keywords:["symbol"],unicode:"27b0"},{names:["loop"],keywords:["symbol"],unicode:"27bf"},{names:["part_alternation_mark"],keywords:["symbol"],unicode:"303d"},{names:["eight_spoked_asterisk"],keywords:["symbol"],unicode:"2733"},{names:["eight_pointed_black_star"],keywords:["symbol"],unicode:"2734"},{names:["sparkle"],keywords:["symbol"],unicode:"2747"},{names:["bangbang"],keywords:["symbol","punctuation"],unicode:"203c"},{names:["interrobang"],keywords:["symbol",
+"punctuation"],unicode:"2049"},{names:["question"],keywords:["symbol","punctuation","wth"],unicode:"2753"},{names:["grey_question"],keywords:["symbol","punctuation"],unicode:"2754"},{names:["grey_exclamation"],keywords:["symbol","punctuation"],unicode:"2755"},{names:["exclamation"],keywords:["symbol","punctuation"],unicode:"2757"},{names:["wavy_dash"],keywords:["symbol"],unicode:"3030"},{names:["copyright"],keywords:["symbol"],unicode:"00a9"},{names:["registered"],keywords:["symbol"],unicode:"00ae"},
+{names:["tm"],keywords:["symbol"],unicode:"2122"},{names:["hash"],keywords:["number","symbol"],unicode:"0023-20e3"},{names:["keycap_asterisk","asterisk"],keywords:["symbol"],unicode:"002a-20e3"},{names:["zero"],keywords:["number","math","symbol"],unicode:"0030-20e3"},{names:["one"],keywords:["number","math","symbol"],unicode:"0031-20e3"},{names:["two"],keywords:["number","math","symbol"],unicode:"0032-20e3"},{names:["three"],keywords:["number","math","symbol"],unicode:"0033-20e3"},{names:["four"],
+keywords:["number","math","symbol"],unicode:"0034-20e3"},{names:["five"],keywords:["number","math","symbol"],unicode:"0035-20e3"},{names:["six"],keywords:["number","math","symbol"],unicode:"0036-20e3"},{names:["seven"],keywords:["number","math","symbol"],unicode:"0037-20e3"},{names:["eight"],keywords:["number","math","symbol"],unicode:"0038-20e3"},{names:["nine"],keywords:["number","math","symbol"],unicode:"0039-20e3"},{names:["keycap_ten"],keywords:["number","math","symbol"],unicode:"1f51f"},{names:["capital_abcd"],
+keywords:["symbol"],unicode:"1f520"},{names:["abcd"],keywords:["symbol"],unicode:"1f521"},{names:["symbols"],keywords:["symbol"],unicode:"1f523"},{names:["abc"],keywords:["symbol"],unicode:"1f524"},{names:["a"],keywords:["symbol"],unicode:"1f170"},{names:["ab"],keywords:["symbol"],unicode:"1f18e"},{names:["b"],keywords:["symbol"],unicode:"1f171"},{names:["cl"],keywords:["symbol"],unicode:"1f191"},{names:["cool"],keywords:["symbol"],unicode:"1f192"},{names:["free"],keywords:["symbol"],unicode:"1f193"},
+{names:["information_source"],keywords:["symbol"],unicode:"2139"},{names:["id"],keywords:["symbol"],unicode:"1f194"},{names:["m"],keywords:["symbol"],unicode:"24c2"},{names:["new"],keywords:["symbol"],unicode:"1f195"},{names:["ng"],keywords:["symbol"],unicode:"1f196"},{names:["o2"],keywords:["symbol"],unicode:"1f17e"},{names:["ok"],keywords:["symbol"],unicode:"1f197"},{names:["parking"],keywords:["symbol"],unicode:"1f17f"},{names:["sos"],keywords:["symbol"],unicode:"1f198"},{names:["up"],keywords:["symbol"],
+unicode:"1f199"},{names:["vs"],keywords:["symbol"],unicode:"1f19a"},{names:["koko"],keywords:["symbol"],unicode:"1f201"},{names:["sa"],keywords:["symbol"],unicode:"1f202"},{names:["u6708"],keywords:["symbol"],unicode:"1f237"},{names:["u6709"],keywords:["symbol"],unicode:"1f236"},{names:["u6307"],keywords:["symbol"],unicode:"1f22f"},{names:["ideograph_advantage"],keywords:["japan","symbol"],unicode:"1f250"},{names:["u5272"],keywords:["symbol"],unicode:"1f239"},{names:["u7121"],keywords:["symbol"],
+unicode:"1f21a"},{names:["u7981"],keywords:["japan","symbol"],unicode:"1f232"},{names:["accept"],keywords:["symbol"],unicode:"1f251"},{names:["u7533"],keywords:["symbol"],unicode:"1f238"},{names:["u5408"],keywords:["japan","symbol"],unicode:"1f234"},{names:["u7a7a"],keywords:["symbol"],unicode:"1f233"},{names:["congratulations"],keywords:["japan","symbol"],unicode:"3297"},{names:["secret"],keywords:["japan","symbol"],unicode:"3299"},{names:["u55b6"],keywords:["symbol"],unicode:"1f23a"},{names:["u6e80"],
+keywords:["japan","symbol"],unicode:"1f235"},{names:["black_small_square"],keywords:["shapes","symbol","square"],unicode:"25aa"},{names:["white_small_square"],keywords:["shapes","symbol","square"],unicode:"25ab"},{names:["white_medium_square"],keywords:["shapes","symbol","square"],unicode:"25fb"},{names:["black_medium_square"],keywords:["shapes","symbol","square"],unicode:"25fc"},{names:["white_medium_small_square"],keywords:["shapes","symbol","square"],unicode:"25fd"},{names:["black_medium_small_square"],
+keywords:["shapes","symbol","square"],unicode:"25fe"},{names:["black_large_square"],keywords:["shapes","symbol","square"],unicode:"2b1b"},{names:["white_large_square"],keywords:["shapes","symbol","square"],unicode:"2b1c"},{names:["large_orange_diamond"],keywords:["shapes","symbol"],unicode:"1f536"},{names:["large_blue_diamond"],keywords:["shapes","symbol"],unicode:"1f537"},{names:["small_orange_diamond"],keywords:["shapes","symbol"],unicode:"1f538"},{names:["small_blue_diamond"],keywords:["shapes",
+"symbol"],unicode:"1f539"},{names:["small_red_triangle"],keywords:["shapes","symbol","triangle"],unicode:"1f53a"},{names:["small_red_triangle_down"],keywords:["shapes","symbol","triangle"],unicode:"1f53b"},{names:["diamond_shape_with_a_dot_inside"],keywords:["symbol"],unicode:"1f4a0"},{names:["radio_button"],keywords:["symbol","circle"],unicode:"1f518"},{names:["black_square_button"],keywords:["shapes","symbol","square"],unicode:"1f532"},{names:["white_square_button"],keywords:["shapes","symbol",
+"square"],unicode:"1f533"},{names:["white_circle"],keywords:["shapes","symbol","circle"],unicode:"26aa"},{names:["black_circle"],keywords:["shapes","symbol","circle"],unicode:"26ab"},{names:["red_circle"],keywords:["shapes","symbol","circle"],unicode:"1f534"},{names:["blue_circle"],keywords:["shapes","symbol","circle"],unicode:"1f535"},{names:["checkered_flag"],keywords:["object"],unicode:"1f3c1"},{names:["triangular_flag_on_post"],keywords:["object"],unicode:"1f6a9"},{names:["crossed_flags"],keywords:["object",
+"japan"],unicode:"1f38c"},{names:["flag_black","waving_black_flag"],keywords:["object"],unicode:"1f3f4"},{names:["flag_white","waving_white_flag"],keywords:["object"],unicode:"1f3f3"},{names:["gay_pride_flag","rainbow_flag"],keywords:[],unicode:"1f3f3-1f308"},{names:["flag_ac","ac"],keywords:["country","flag"],unicode:"1f1e6-1f1e8"},{names:["flag_ad","ad"],keywords:["country","flag"],unicode:"1f1e6-1f1e9"},{names:["flag_ae","ae"],keywords:["country","flag"],unicode:"1f1e6-1f1ea"},{names:["flag_af",
+"af"],keywords:["country","flag"],unicode:"1f1e6-1f1eb"},{names:["flag_ag","ag"],keywords:["country","flag"],unicode:"1f1e6-1f1ec"},{names:["flag_ai","ai"],keywords:["country","flag"],unicode:"1f1e6-1f1ee"},{names:["flag_al","al"],keywords:["country","flag"],unicode:"1f1e6-1f1f1"},{names:["flag_am","am"],keywords:["country","flag"],unicode:"1f1e6-1f1f2"},{names:["flag_ao","ao"],keywords:["country","flag"],unicode:"1f1e6-1f1f4"},{names:["flag_aq","aq"],keywords:["country","flag"],unicode:"1f1e6-1f1f6"},
+{names:["flag_ar","ar"],keywords:["country","flag"],unicode:"1f1e6-1f1f7"},{names:["flag_as","as"],keywords:["country","flag"],unicode:"1f1e6-1f1f8"},{names:["flag_at","at"],keywords:["country","flag"],unicode:"1f1e6-1f1f9"},{names:["flag_au","au"],keywords:["country","flag"],unicode:"1f1e6-1f1fa"},{names:["flag_aw","aw"],keywords:["country","flag"],unicode:"1f1e6-1f1fc"},{names:["flag_ax","ax"],keywords:["country","flag"],unicode:"1f1e6-1f1fd"},{names:["flag_az","az"],keywords:["country","flag"],
+unicode:"1f1e6-1f1ff"},{names:["flag_ba","ba"],keywords:["country","flag"],unicode:"1f1e7-1f1e6"},{names:["flag_bb","bb"],keywords:["country","flag"],unicode:"1f1e7-1f1e7"},{names:["flag_bd","bd"],keywords:["country","flag"],unicode:"1f1e7-1f1e9"},{names:["flag_be","be"],keywords:["country","flag"],unicode:"1f1e7-1f1ea"},{names:["flag_bf","bf"],keywords:["country","flag"],unicode:"1f1e7-1f1eb"},{names:["flag_bg","bg"],keywords:["country","flag"],unicode:"1f1e7-1f1ec"},{names:["flag_bh","bh"],keywords:["country",
+"flag"],unicode:"1f1e7-1f1ed"},{names:["flag_bi","bi"],keywords:["country","flag"],unicode:"1f1e7-1f1ee"},{names:["flag_bj","bj"],keywords:["country","flag"],unicode:"1f1e7-1f1ef"},{names:["flag_bl","bl"],keywords:["country","flag"],unicode:"1f1e7-1f1f1"},{names:["flag_bm","bm"],keywords:["country","flag"],unicode:"1f1e7-1f1f2"},{names:["flag_bn","bn"],keywords:["country","flag"],unicode:"1f1e7-1f1f3"},{names:["flag_bo","bo"],keywords:["country","flag"],unicode:"1f1e7-1f1f4"},{names:["flag_bq","bq"],
+keywords:["country","flag"],unicode:"1f1e7-1f1f6"},{names:["flag_br","br"],keywords:["country","flag"],unicode:"1f1e7-1f1f7"},{names:["flag_bs","bs"],keywords:["country","flag"],unicode:"1f1e7-1f1f8"},{names:["flag_bt","bt"],keywords:["country","flag"],unicode:"1f1e7-1f1f9"},{names:["flag_bv","bv"],keywords:["country","flag"],unicode:"1f1e7-1f1fb"},{names:["flag_bw","bw"],keywords:["country","flag"],unicode:"1f1e7-1f1fc"},{names:["flag_by","by"],keywords:["country","flag"],unicode:"1f1e7-1f1fe"},
+{names:["flag_bz","bz"],keywords:["country","flag"],unicode:"1f1e7-1f1ff"},{names:["flag_ca","ca"],keywords:["country","flag"],unicode:"1f1e8-1f1e6"},{names:["flag_cc","cc"],keywords:["country","flag"],unicode:"1f1e8-1f1e8"},{names:["flag_cd","congo"],keywords:["country","flag"],unicode:"1f1e8-1f1e9"},{names:["flag_cf","cf"],keywords:["country","flag"],unicode:"1f1e8-1f1eb"},{names:["flag_cg","cg"],keywords:["country","flag"],unicode:"1f1e8-1f1ec"},{names:["flag_ch","ch"],keywords:["country","neutral",
+"flag"],unicode:"1f1e8-1f1ed"},{names:["flag_ci","ci"],keywords:["country","flag"],unicode:"1f1e8-1f1ee"},{names:["flag_ck","ck"],keywords:["country","flag"],unicode:"1f1e8-1f1f0"},{names:["flag_cl","chile"],keywords:["country","flag"],unicode:"1f1e8-1f1f1"},{names:["flag_cm","cm"],keywords:["country","flag"],unicode:"1f1e8-1f1f2"},{names:["flag_cn","cn"],keywords:["country","flag"],unicode:"1f1e8-1f1f3"},{names:["flag_co","co"],keywords:["country","flag"],unicode:"1f1e8-1f1f4"},{names:["flag_cp",
+"cp"],keywords:["country","flag"],unicode:"1f1e8-1f1f5"},{names:["flag_cr","cr"],keywords:["country","flag"],unicode:"1f1e8-1f1f7"},{names:["flag_cu","cu"],keywords:["country","flag"],unicode:"1f1e8-1f1fa"},{names:["flag_cv","cv"],keywords:["country","flag"],unicode:"1f1e8-1f1fb"},{names:["flag_cw","cw"],keywords:["country","flag"],unicode:"1f1e8-1f1fc"},{names:["flag_cx","cx"],keywords:["country","flag"],unicode:"1f1e8-1f1fd"},{names:["flag_cy","cy"],keywords:["country","flag"],unicode:"1f1e8-1f1fe"},
+{names:["flag_cz","cz"],keywords:["country","flag"],unicode:"1f1e8-1f1ff"},{names:["flag_de","de"],keywords:["country","flag"],unicode:"1f1e9-1f1ea"},{names:["flag_dg","dg"],keywords:["country","flag"],unicode:"1f1e9-1f1ec"},{names:["flag_dj","dj"],keywords:["country","flag"],unicode:"1f1e9-1f1ef"},{names:["flag_dk","dk"],keywords:["country","flag"],unicode:"1f1e9-1f1f0"},{names:["flag_dm","dm"],keywords:["country","flag"],unicode:"1f1e9-1f1f2"},{names:["flag_do","do"],keywords:["country","flag"],
+unicode:"1f1e9-1f1f4"},{names:["flag_dz","dz"],keywords:["country","flag"],unicode:"1f1e9-1f1ff"},{names:["flag_ea","ea"],keywords:["country","flag"],unicode:"1f1ea-1f1e6"},{names:["flag_ec","ec"],keywords:["country","flag"],unicode:"1f1ea-1f1e8"},{names:["flag_ee","ee"],keywords:["country","flag"],unicode:"1f1ea-1f1ea"},{names:["flag_eg","eg"],keywords:["country","flag"],unicode:"1f1ea-1f1ec"},{names:["flag_eh","eh"],keywords:["country","flag"],unicode:"1f1ea-1f1ed"},{names:["flag_er","er"],keywords:["country",
+"flag"],unicode:"1f1ea-1f1f7"},{names:["flag_es","es"],keywords:["country","flag"],unicode:"1f1ea-1f1f8"},{names:["flag_et","et"],keywords:["country","flag"],unicode:"1f1ea-1f1f9"},{names:["flag_eu","eu"],keywords:["country","flag"],unicode:"1f1ea-1f1fa"},{names:["flag_fi","fi"],keywords:["country","flag"],unicode:"1f1eb-1f1ee"},{names:["flag_fj","fj"],keywords:["country","flag"],unicode:"1f1eb-1f1ef"},{names:["flag_fk","fk"],keywords:["country","flag"],unicode:"1f1eb-1f1f0"},{names:["flag_fm","fm"],
+keywords:["country","flag"],unicode:"1f1eb-1f1f2"},{names:["flag_fo","fo"],keywords:["country","flag"],unicode:"1f1eb-1f1f4"},{names:["flag_fr","fr"],keywords:["country","flag"],unicode:"1f1eb-1f1f7"},{names:["flag_ga","ga"],keywords:["country","flag"],unicode:"1f1ec-1f1e6"},{names:["flag_gb","gb"],keywords:["country","flag"],unicode:"1f1ec-1f1e7"},{names:["flag_gd","gd"],keywords:["country","flag"],unicode:"1f1ec-1f1e9"},{names:["flag_ge","ge"],keywords:["country","flag"],unicode:"1f1ec-1f1ea"},
+{names:["flag_gf","gf"],keywords:["country","flag"],unicode:"1f1ec-1f1eb"},{names:["flag_gg","gg"],keywords:["country","flag"],unicode:"1f1ec-1f1ec"},{names:["flag_gh","gh"],keywords:["country","flag"],unicode:"1f1ec-1f1ed"},{names:["flag_gi","gi"],keywords:["country","flag"],unicode:"1f1ec-1f1ee"},{names:["flag_gl","gl"],keywords:["country","flag"],unicode:"1f1ec-1f1f1"},{names:["flag_gm","gm"],keywords:["country","flag"],unicode:"1f1ec-1f1f2"},{names:["flag_gn","gn"],keywords:["country","flag"],
+unicode:"1f1ec-1f1f3"},{names:["flag_gp","gp"],keywords:["country","flag"],unicode:"1f1ec-1f1f5"},{names:["flag_gq","gq"],keywords:["country","flag"],unicode:"1f1ec-1f1f6"},{names:["flag_gr","gr"],keywords:["country","flag"],unicode:"1f1ec-1f1f7"},{names:["flag_gs","gs"],keywords:["country","flag"],unicode:"1f1ec-1f1f8"},{names:["flag_gt","gt"],keywords:["country","flag"],unicode:"1f1ec-1f1f9"},{names:["flag_gu","gu"],keywords:["country","flag"],unicode:"1f1ec-1f1fa"},{names:["flag_gw","gw"],keywords:["country",
+"flag"],unicode:"1f1ec-1f1fc"},{names:["flag_gy","gy"],keywords:["country","flag"],unicode:"1f1ec-1f1fe"},{names:["flag_hk","hk"],keywords:["country","flag"],unicode:"1f1ed-1f1f0"},{names:["flag_hm","hm"],keywords:["country","flag"],unicode:"1f1ed-1f1f2"},{names:["flag_hn","hn"],keywords:["country","flag"],unicode:"1f1ed-1f1f3"},{names:["flag_hr","hr"],keywords:["country","flag"],unicode:"1f1ed-1f1f7"},{names:["flag_ht","ht"],keywords:["country","flag"],unicode:"1f1ed-1f1f9"},{names:["flag_hu","hu"],
+keywords:["country","flag"],unicode:"1f1ed-1f1fa"},{names:["flag_ic","ic"],keywords:["country","flag"],unicode:"1f1ee-1f1e8"},{names:["flag_id","indonesia"],keywords:["country","flag"],unicode:"1f1ee-1f1e9"},{names:["flag_ie","ie"],keywords:["country","flag"],unicode:"1f1ee-1f1ea"},{names:["flag_il","il"],keywords:["jew","country","flag"],unicode:"1f1ee-1f1f1"},{names:["flag_im","im"],keywords:["country","flag"],unicode:"1f1ee-1f1f2"},{names:["flag_in","in"],keywords:["country","flag"],unicode:"1f1ee-1f1f3"},
+{names:["flag_io","io"],keywords:["country","flag"],unicode:"1f1ee-1f1f4"},{names:["flag_iq","iq"],keywords:["country","flag"],unicode:"1f1ee-1f1f6"},{names:["flag_ir","ir"],keywords:["country","flag"],unicode:"1f1ee-1f1f7"},{names:["flag_is","is"],keywords:["country","flag"],unicode:"1f1ee-1f1f8"},{names:["flag_it","it"],keywords:["italian","country","flag"],unicode:"1f1ee-1f1f9"},{names:["flag_je","je"],keywords:["country","flag"],unicode:"1f1ef-1f1ea"},{names:["flag_jm","jm"],keywords:["country",
+"flag"],unicode:"1f1ef-1f1f2"},{names:["flag_jo","jo"],keywords:["country","flag"],unicode:"1f1ef-1f1f4"},{names:["flag_jp","jp"],keywords:["japan","country","flag"],unicode:"1f1ef-1f1f5"},{names:["flag_ke","ke"],keywords:["country","flag"],unicode:"1f1f0-1f1ea"},{names:["flag_kg","kg"],keywords:["country","flag"],unicode:"1f1f0-1f1ec"},{names:["flag_kh","kh"],keywords:["country","flag"],unicode:"1f1f0-1f1ed"},{names:["flag_ki","ki"],keywords:["country","flag"],unicode:"1f1f0-1f1ee"},{names:["flag_km",
+"km"],keywords:["country","flag"],unicode:"1f1f0-1f1f2"},{names:["flag_kn","kn"],keywords:["country","flag"],unicode:"1f1f0-1f1f3"},{names:["flag_kp","kp"],keywords:["country","flag"],unicode:"1f1f0-1f1f5"},{names:["flag_kr","kr"],keywords:["country","flag"],unicode:"1f1f0-1f1f7"},{names:["flag_kw","kw"],keywords:["country","flag"],unicode:"1f1f0-1f1fc"},{names:["flag_ky","ky"],keywords:["country","flag"],unicode:"1f1f0-1f1fe"},{names:["flag_kz","kz"],keywords:["country","flag"],unicode:"1f1f0-1f1ff"},
+{names:["flag_la","la"],keywords:["country","flag"],unicode:"1f1f1-1f1e6"},{names:["flag_lb","lb"],keywords:["country","flag"],unicode:"1f1f1-1f1e7"},{names:["flag_lc","lc"],keywords:["country","flag"],unicode:"1f1f1-1f1e8"},{names:["flag_li","li"],keywords:["country","flag"],unicode:"1f1f1-1f1ee"},{names:["flag_lk","lk"],keywords:["country","flag"],unicode:"1f1f1-1f1f0"},{names:["flag_lr","lr"],keywords:["country","flag"],unicode:"1f1f1-1f1f7"},{names:["flag_ls","ls"],keywords:["country","flag"],
+unicode:"1f1f1-1f1f8"},{names:["flag_lt","lt"],keywords:["country","flag"],unicode:"1f1f1-1f1f9"},{names:["flag_lu","lu"],keywords:["country","flag"],unicode:"1f1f1-1f1fa"},{names:["flag_lv","lv"],keywords:["country","flag"],unicode:"1f1f1-1f1fb"},{names:["flag_ly","ly"],keywords:["country","flag"],unicode:"1f1f1-1f1fe"},{names:["flag_ma","ma"],keywords:["country","flag"],unicode:"1f1f2-1f1e6"},{names:["flag_mc","mc"],keywords:["country","flag"],unicode:"1f1f2-1f1e8"},{names:["flag_md","md"],keywords:["country",
+"flag"],unicode:"1f1f2-1f1e9"},{names:["flag_me","me"],keywords:["country","flag"],unicode:"1f1f2-1f1ea"},{names:["flag_mf","mf"],keywords:["country","flag"],unicode:"1f1f2-1f1eb"},{names:["flag_mg","mg"],keywords:["country","flag"],unicode:"1f1f2-1f1ec"},{names:["flag_mh","mh"],keywords:["country","flag"],unicode:"1f1f2-1f1ed"},{names:["flag_mk","mk"],keywords:["country","flag"],unicode:"1f1f2-1f1f0"},{names:["flag_ml","ml"],keywords:["country","flag"],unicode:"1f1f2-1f1f1"},{names:["flag_mm","mm"],
+keywords:["country","flag"],unicode:"1f1f2-1f1f2"},{names:["flag_mn","mn"],keywords:["country","flag"],unicode:"1f1f2-1f1f3"},{names:["flag_mo","mo"],keywords:["country","flag"],unicode:"1f1f2-1f1f4"},{names:["flag_mp","mp"],keywords:["country","flag"],unicode:"1f1f2-1f1f5"},{names:["flag_mq","mq"],keywords:["country","flag"],unicode:"1f1f2-1f1f6"},{names:["flag_mr","mr"],keywords:["country","flag"],unicode:"1f1f2-1f1f7"},{names:["flag_ms","ms"],keywords:["country","flag"],unicode:"1f1f2-1f1f8"},
+{names:["flag_mt","mt"],keywords:["country","flag"],unicode:"1f1f2-1f1f9"},{names:["flag_mu","mu"],keywords:["country","flag"],unicode:"1f1f2-1f1fa"},{names:["flag_mv","mv"],keywords:["country","flag"],unicode:"1f1f2-1f1fb"},{names:["flag_mw","mw"],keywords:["country","flag"],unicode:"1f1f2-1f1fc"},{names:["flag_mx","mx"],keywords:["country","mexican","flag"],unicode:"1f1f2-1f1fd"},{names:["flag_my","my"],keywords:["country","flag"],unicode:"1f1f2-1f1fe"},{names:["flag_mz","mz"],keywords:["country",
+"flag"],unicode:"1f1f2-1f1ff"},{names:["flag_na","na"],keywords:["country","flag"],unicode:"1f1f3-1f1e6"},{names:["flag_nc","nc"],keywords:["country","flag"],unicode:"1f1f3-1f1e8"},{names:["flag_ne","ne"],keywords:["country","flag"],unicode:"1f1f3-1f1ea"},{names:["flag_nf","nf"],keywords:["country","flag"],unicode:"1f1f3-1f1eb"},{names:["flag_ng","nigeria"],keywords:["country","flag"],unicode:"1f1f3-1f1ec"},{names:["flag_ni","ni"],keywords:["country","flag"],unicode:"1f1f3-1f1ee"},{names:["flag_nl",
+"nl"],keywords:["country","flag"],unicode:"1f1f3-1f1f1"},{names:["flag_no","no"],keywords:["country","flag"],unicode:"1f1f3-1f1f4"},{names:["flag_np","np"],keywords:["country","flag"],unicode:"1f1f3-1f1f5"},{names:["flag_nr","nr"],keywords:["country","flag"],unicode:"1f1f3-1f1f7"},{names:["flag_nu","nu"],keywords:["country","flag"],unicode:"1f1f3-1f1fa"},{names:["flag_nz","nz"],keywords:["country","flag"],unicode:"1f1f3-1f1ff"},{names:["flag_om","om"],keywords:["country","flag"],unicode:"1f1f4-1f1f2"},
+{names:["flag_pa","pa"],keywords:["country","flag"],unicode:"1f1f5-1f1e6"},{names:["flag_pe","pe"],keywords:["country","flag"],unicode:"1f1f5-1f1ea"},{names:["flag_pf","pf"],keywords:["country","flag"],unicode:"1f1f5-1f1eb"},{names:["flag_pg","pg"],keywords:["country","flag"],unicode:"1f1f5-1f1ec"},{names:["flag_ph","ph"],keywords:["country","flag"],unicode:"1f1f5-1f1ed"},{names:["flag_pk","pk"],keywords:["country","flag"],unicode:"1f1f5-1f1f0"},{names:["flag_pl","pl"],keywords:["country","flag"],
+unicode:"1f1f5-1f1f1"},{names:["flag_pm","pm"],keywords:["country","flag"],unicode:"1f1f5-1f1f2"},{names:["flag_pn","pn"],keywords:["country","flag"],unicode:"1f1f5-1f1f3"},{names:["flag_pr","pr"],keywords:["country","flag"],unicode:"1f1f5-1f1f7"},{names:["flag_ps","ps"],keywords:["country","flag"],unicode:"1f1f5-1f1f8"},{names:["flag_pt","pt"],keywords:["country","flag"],unicode:"1f1f5-1f1f9"},{names:["flag_pw","pw"],keywords:["country","flag"],unicode:"1f1f5-1f1fc"},{names:["flag_py","py"],keywords:["country",
+"flag"],unicode:"1f1f5-1f1fe"},{names:["flag_qa","qa"],keywords:["country","flag"],unicode:"1f1f6-1f1e6"},{names:["flag_re","re"],keywords:["country","flag"],unicode:"1f1f7-1f1ea"},{names:["flag_ro","ro"],keywords:["country","flag"],unicode:"1f1f7-1f1f4"},{names:["flag_rs","rs"],keywords:["country","flag"],unicode:"1f1f7-1f1f8"},{names:["flag_ru","ru"],keywords:["country","flag"],unicode:"1f1f7-1f1fa"},{names:["flag_rw","rw"],keywords:["country","flag"],unicode:"1f1f7-1f1fc"},{names:["flag_sa","saudiarabia",
+"saudi"],keywords:["country","flag"],unicode:"1f1f8-1f1e6"},{names:["flag_sb","sb"],keywords:["country","flag"],unicode:"1f1f8-1f1e7"},{names:["flag_sc","sc"],keywords:["country","flag"],unicode:"1f1f8-1f1e8"},{names:["flag_sd","sd"],keywords:["country","flag"],unicode:"1f1f8-1f1e9"},{names:["flag_se","se"],keywords:["country","flag"],unicode:"1f1f8-1f1ea"},{names:["flag_sg","sg"],keywords:["country","flag"],unicode:"1f1f8-1f1ec"},{names:["flag_sh","sh"],keywords:["country","flag"],unicode:"1f1f8-1f1ed"},
+{names:["flag_si","si"],keywords:["country","flag"],unicode:"1f1f8-1f1ee"},{names:["flag_sj","sj"],keywords:["country","flag"],unicode:"1f1f8-1f1ef"},{names:["flag_sk","sk"],keywords:["country","flag"],unicode:"1f1f8-1f1f0"},{names:["flag_sl","sl"],keywords:["country","flag"],unicode:"1f1f8-1f1f1"},{names:["flag_sm","sm"],keywords:["country","flag"],unicode:"1f1f8-1f1f2"},{names:["flag_sn","sn"],keywords:["country","flag"],unicode:"1f1f8-1f1f3"},{names:["flag_so","so"],keywords:["country","flag"],
+unicode:"1f1f8-1f1f4"},{names:["flag_sr","sr"],keywords:["country","flag"],unicode:"1f1f8-1f1f7"},{names:["flag_ss","ss"],keywords:["country","flag"],unicode:"1f1f8-1f1f8"},{names:["flag_st","st"],keywords:["country","flag"],unicode:"1f1f8-1f1f9"},{names:["flag_sv","sv"],keywords:["country","flag"],unicode:"1f1f8-1f1fb"},{names:["flag_sx","sx"],keywords:["country","flag"],unicode:"1f1f8-1f1fd"},{names:["flag_sy","sy"],keywords:["country","flag"],unicode:"1f1f8-1f1fe"},{names:["flag_sz","sz"],keywords:["country",
+"flag"],unicode:"1f1f8-1f1ff"},{names:["flag_ta","ta"],keywords:["country","flag"],unicode:"1f1f9-1f1e6"},{names:["flag_tc","tc"],keywords:["country","flag"],unicode:"1f1f9-1f1e8"},{names:["flag_td","td"],keywords:["country","flag"],unicode:"1f1f9-1f1e9"},{names:["flag_tf","tf"],keywords:["country","flag"],unicode:"1f1f9-1f1eb"},{names:["flag_tg","tg"],keywords:["country","flag"],unicode:"1f1f9-1f1ec"},{names:["flag_th","th"],keywords:["country","flag"],unicode:"1f1f9-1f1ed"},{names:["flag_tj","tj"],
+keywords:["country","flag"],unicode:"1f1f9-1f1ef"},{names:["flag_tk","tk"],keywords:["country","flag"],unicode:"1f1f9-1f1f0"},{names:["flag_tl","tl"],keywords:["country","flag"],unicode:"1f1f9-1f1f1"},{names:["flag_tm","turkmenistan"],keywords:["country","flag"],unicode:"1f1f9-1f1f2"},{names:["flag_tn","tn"],keywords:["country","flag"],unicode:"1f1f9-1f1f3"},{names:["flag_to","to"],keywords:["country","flag"],unicode:"1f1f9-1f1f4"},{names:["flag_tr","tr"],keywords:["country","flag"],unicode:"1f1f9-1f1f7"},
+{names:["flag_tt","tt"],keywords:["country","flag"],unicode:"1f1f9-1f1f9"},{names:["flag_tv","tuvalu"],keywords:["country","flag"],unicode:"1f1f9-1f1fb"},{names:["flag_tw","tw"],keywords:["country","flag"],unicode:"1f1f9-1f1fc"},{names:["flag_tz","tz"],keywords:["country","flag"],unicode:"1f1f9-1f1ff"},{names:["flag_ua","ua"],keywords:["country","flag"],unicode:"1f1fa-1f1e6"},{names:["flag_ug","ug"],keywords:["country","flag"],unicode:"1f1fa-1f1ec"},{names:["flag_um","um"],keywords:["country","flag"],
+unicode:"1f1fa-1f1f2"},{names:["flag_us","us"],keywords:["america","country","flag"],unicode:"1f1fa-1f1f8"},{names:["flag_uy","uy"],keywords:["country","flag"],unicode:"1f1fa-1f1fe"},{names:["flag_uz","uz"],keywords:["country","flag"],unicode:"1f1fa-1f1ff"},{names:["flag_va","va"],keywords:["country","flag"],unicode:"1f1fb-1f1e6"},{names:["flag_vc","vc"],keywords:["country","flag"],unicode:"1f1fb-1f1e8"},{names:["flag_ve","ve"],keywords:["country","flag"],unicode:"1f1fb-1f1ea"},{names:["flag_vg",
+"vg"],keywords:["country","flag"],unicode:"1f1fb-1f1ec"},{names:["flag_vi","vi"],keywords:["country","flag"],unicode:"1f1fb-1f1ee"},{names:["flag_vn","vn"],keywords:["country","flag"],unicode:"1f1fb-1f1f3"},{names:["flag_vu","vu"],keywords:["country","flag"],unicode:"1f1fb-1f1fa"},{names:["flag_wf","wf"],keywords:["country","flag"],unicode:"1f1fc-1f1eb"},{names:["flag_ws","ws"],keywords:["country","flag"],unicode:"1f1fc-1f1f8"},{names:["flag_xk","xk"],keywords:["country","flag"],unicode:"1f1fd-1f1f0"},
+{names:["flag_ye","ye"],keywords:["country","flag"],unicode:"1f1fe-1f1ea"},{names:["flag_yt","yt"],keywords:["country","flag"],unicode:"1f1fe-1f1f9"},{names:["flag_za","za"],keywords:["country","flag"],unicode:"1f1ff-1f1e6"},{names:["flag_zm","zm"],keywords:["country","flag"],unicode:"1f1ff-1f1f2"},{names:["flag_zw","zw"],keywords:["country","flag"],unicode:"1f1ff-1f1fc"},{names:["regional_indicator_z"],keywords:[],unicode:"1f1ff"},{names:["regional_indicator_y"],keywords:[],unicode:"1f1fe"},{names:["regional_indicator_x"],
+keywords:[],unicode:"1f1fd"},{names:["regional_indicator_w"],keywords:[],unicode:"1f1fc"},{names:["regional_indicator_v"],keywords:[],unicode:"1f1fb"},{names:["regional_indicator_u"],keywords:[],unicode:"1f1fa"},{names:["regional_indicator_t"],keywords:[],unicode:"1f1f9"},{names:["regional_indicator_s"],keywords:[],unicode:"1f1f8"},{names:["regional_indicator_r"],keywords:[],unicode:"1f1f7"},{names:["regional_indicator_q"],keywords:[],unicode:"1f1f6"},{names:["regional_indicator_p"],keywords:[],unicode:"1f1f5"},
+{names:["regional_indicator_o"],keywords:[],unicode:"1f1f4"},{names:["regional_indicator_n"],keywords:[],unicode:"1f1f3"},{names:["regional_indicator_m"],keywords:[],unicode:"1f1f2"},{names:["regional_indicator_l"],keywords:[],unicode:"1f1f1"},{names:["regional_indicator_k"],keywords:[],unicode:"1f1f0"},{names:["regional_indicator_j"],keywords:[],unicode:"1f1ef"},{names:["regional_indicator_i"],keywords:[],unicode:"1f1ee"},{names:["regional_indicator_h"],keywords:[],unicode:"1f1ed"},{names:["regional_indicator_g"],
+keywords:[],unicode:"1f1ec"},{names:["regional_indicator_f"],keywords:[],unicode:"1f1eb"},{names:["regional_indicator_e"],keywords:[],unicode:"1f1ea"},{names:["regional_indicator_d"],keywords:[],unicode:"1f1e9"},{names:["regional_indicator_c"],keywords:[],unicode:"1f1e8"},{names:["regional_indicator_b"],keywords:[],unicode:"1f1e7"},{names:["regional_indicator_a"],keywords:[],unicode:"1f1e6"}];window.makeEmoji=function(b){var c;if("string"===typeof b)for(var e=0,g=f.length;e<g&&!c;e++)for(var h=f[e].names,a=0,d=h.length;a<d&&!c;a++)h[a]===b&&(c=f[e]);else c=b;return c?(b=document.createElement("span"),b.className="emoji emojione emojione-"+c.unicode,b):null};window.emojiProviderHeader="emojione.logo.svg";
+window.searchEmojis=function(b){var c={},e=b.length,g=0,h=f.length;a:for(;g<h;g++){for(var a=f[g],d=0,k=a.names.length;d<k;d++)if(a.names[d].substr(0,e)===b){c[a.names[0]]=a;continue a}d=0;for(k=a.keywords.length;d<k;d++)if(a.keywords[d].substr(0,e)===b){c[a.names[0]]=a;break}}return c};
+})();

+ 0 - 0
srv/public/emojione.sprites.png → srv/public/emojione_v2.3.sprites.png


BIN
srv/public/emojione_v3-sprite-64-activity.png


BIN
srv/public/emojione_v3-sprite-64-flags.png


BIN
srv/public/emojione_v3-sprite-64-food.png


BIN
srv/public/emojione_v3-sprite-64-nature.png


BIN
srv/public/emojione_v3-sprite-64-objects.png


BIN
srv/public/emojione_v3-sprite-64-people.png


BIN
srv/public/emojione_v3-sprite-64-symbols.png


BIN
srv/public/emojione_v3-sprite-64-travel.png


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
srv/public/emojione_v3.sprites.css


+ 560 - 0
srv/public/emojione_v3.sprites.js

@@ -0,0 +1,560 @@
+"use strict";(function(){
+var f=[{names:["interrobang"],keywords:["exclamation","interrobang","mark","punctuation","question"],unicode:"2049",sprite:"symbols"},{names:["tm"],keywords:["mark","tm","trademark"],unicode:"2122",sprite:"symbols"},{names:["information_source"],keywords:["i","information"],unicode:"2139",sprite:"symbols"},{names:["left_right_arrow"],keywords:["arrow"],unicode:"2194",sprite:"symbols"},{names:["arrow_up_down"],keywords:["arrow"],unicode:"2195",sprite:"symbols"},{names:["arrow_upper_left"],keywords:["arrow",
+"direction","intercardinal","northwest"],unicode:"2196",sprite:"symbols"},{names:["arrow_upper_right"],keywords:["arrow","direction","intercardinal","northeast"],unicode:"2197",sprite:"symbols"},{names:["arrow_lower_right"],keywords:["arrow","direction","intercardinal","southeast"],unicode:"2198",sprite:"symbols"},{names:["arrow_lower_left"],keywords:["arrow","direction","intercardinal","southwest"],unicode:"2199",sprite:"symbols"},{names:["keyboard"],keywords:["computer"],unicode:"2328",sprite:"objects"},
+{names:["sunny"],keywords:["bright","rays","sunny"],unicode:"2600",sprite:"nature"},{names:["cloud"],keywords:["weather"],unicode:"2601",sprite:"nature"},{names:["umbrella2"],keywords:["clothing","rain"],unicode:"2602",sprite:"people"},{names:["snowman2"],keywords:["cold","snow"],unicode:"2603",sprite:"nature"},{names:["comet"],keywords:["space"],unicode:"2604",sprite:"nature"},{names:["ballot_box_with_check"],keywords:["ballot","box","check"],unicode:"2611",sprite:"symbols"},{names:["umbrella"],
+keywords:["clothing","drop","rain","umbrella"],unicode:"2614",sprite:"nature"},{names:["coffee"],keywords:"beverage coffee drink hot steaming tea".split(" "),unicode:"2615",sprite:"food"},{names:["shamrock"],keywords:["plant"],unicode:"2618",sprite:"nature"},{names:["skull_and_crossbones","skull_crossbones"],keywords:["crossbones","death","face","monster","skull"],unicode:"2620",sprite:"people"},{names:["radioactive_sign","radioactive"],keywords:["radioactive","sign"],unicode:"2622",sprite:"symbols"},
+{names:["biohazard_sign","biohazard"],keywords:["biohazard","sign"],unicode:"2623",sprite:"symbols"},{names:["orthodox_cross"],keywords:["Christian","cross","religion"],unicode:"2626",sprite:"symbols"},{names:["wheel_of_dharma"],keywords:["Buddhist","dharma","religion","wheel"],unicode:"2638",sprite:"symbols"},{names:["white_frowning_face","frowning2"],keywords:["face","frown"],unicode:"2639",sprite:"people"},{names:["female_sign"],keywords:["woman"],unicode:"2640",sprite:"people"},{names:["male_sign"],
+keywords:["man"],unicode:"2642",sprite:"people"},{names:["aries"],keywords:["ram","zodiac"],unicode:"2648",sprite:"symbols"},{names:["taurus"],keywords:["bull","ox","zodiac"],unicode:"2649",sprite:"symbols"},{names:["sagittarius"],keywords:["archer","zodiac"],unicode:"2650",sprite:"symbols"},{names:["capricorn"],keywords:["goat","zodiac"],unicode:"2651",sprite:"symbols"},{names:["aquarius"],keywords:["bearer","water","zodiac"],unicode:"2652",sprite:"symbols"},{names:["pisces"],keywords:["fish","zodiac"],
+unicode:"2653",sprite:"symbols"},{names:["spades"],keywords:["card","game"],unicode:"2660",sprite:"symbols"},{names:["clubs"],keywords:["card","game"],unicode:"2663",sprite:"symbols"},{names:["hearts"],keywords:["card","game"],unicode:"2665",sprite:"symbols"},{names:["diamonds"],keywords:["card","game"],unicode:"2666",sprite:"symbols"},{names:["hotsprings"],keywords:["hot","hotsprings","springs","steaming"],unicode:"2668",sprite:"symbols"},{names:["hammer_and_pick","hammer_pick"],keywords:["hammer",
+"pick","tool"],unicode:"2692",sprite:"objects"},{names:["anchor"],keywords:["ship","tool"],unicode:"2693",sprite:"travel"},{names:["crossed_swords"],keywords:["crossed","swords","weapon"],unicode:"2694",sprite:"objects"},{names:["medical_symbol"],keywords:["aesculapius","medicine","staff"],unicode:"2695",sprite:"people"},{names:["scales"],keywords:"Libra balance justice scales tool weight zodiac".split(" "),unicode:"2696",sprite:"objects"},{names:["alembic"],keywords:["chemistry","tool"],unicode:"2697",
+sprite:"objects"},{names:["gear"],keywords:["tool"],unicode:"2699",sprite:"objects"},{names:["scissors"],keywords:["cutting","tool"],unicode:"2702",sprite:"objects"},{names:["white_check_mark"],keywords:["check","mark"],unicode:"2705",sprite:"symbols"},{names:["airplane"],keywords:["aeroplane","airplane"],unicode:"2708",sprite:"travel"},{names:["envelope"],keywords:["email","letter"],unicode:"2709",sprite:"objects"},{names:["black_nib"],keywords:["nib","pen"],unicode:"2712",sprite:"objects"},{names:["heavy_check_mark"],
+keywords:["check","mark"],unicode:"2714",sprite:"symbols"},{names:["heavy_multiplication_x"],keywords:["cancel","multiplication","multiply","x"],unicode:"2716",sprite:"symbols"},{names:["star_of_david"],keywords:["David","Jew","Jewish","religion","star"],unicode:"2721",sprite:"symbols"},{names:["sparkles"],keywords:["sparkle","star"],unicode:"2728",sprite:"nature"},{names:["eight_spoked_asterisk"],keywords:["asterisk"],unicode:"2733",sprite:"symbols"},{names:["eight_pointed_black_star"],keywords:["star"],
+unicode:"2734",sprite:"symbols"},{names:["snowflake"],keywords:["cold","snow"],unicode:"2744",sprite:"nature"},{names:["sparkle"],keywords:["sparkle"],unicode:"2747",sprite:"symbols"},{names:["question"],keywords:["mark","punctuation","question"],unicode:"2753",sprite:"symbols"},{names:["grey_question"],keywords:["mark","outlined","punctuation","question"],unicode:"2754",sprite:"symbols"},{names:["grey_exclamation"],keywords:["exclamation","mark","outlined","punctuation"],unicode:"2755",sprite:"symbols"},
+{names:["exclamation"],keywords:["exclamation","mark","punctuation"],unicode:"2757",sprite:"symbols"},{names:["heavy_heart_exclamation_mark_ornament","heart_exclamation"],keywords:["exclamation","mark","punctuation"],unicode:"2763",sprite:"symbols"},{names:["heart"],keywords:["heart"],unicode:"2764",sprite:"symbols"},{names:["heavy_plus_sign"],keywords:["math","plus"],unicode:"2795",sprite:"symbols"},{names:["heavy_minus_sign"],keywords:["math","minus"],unicode:"2796",sprite:"symbols"},{names:["heavy_division_sign"],
+keywords:["division","math"],unicode:"2797",sprite:"symbols"},{names:["arrow_heading_up"],keywords:["arrow"],unicode:"2934",sprite:"symbols"},{names:["arrow_heading_down"],keywords:["arrow","down"],unicode:"2935",sprite:"symbols"},{names:["wavy_dash"],keywords:["dash","punctuation","wavy"],unicode:"3030",sprite:"symbols"},{names:["congratulations"],keywords:["Japanese","ideograph","\u201ccongratulations\u201d","\u795d"],unicode:"3297",sprite:"symbols"},{names:["secret"],keywords:["Japanese","ideograph",
+"\u201csecret\u201d","\u79d8"],unicode:"3299",sprite:"symbols"},{names:["grinning"],keywords:["face","grin"],unicode:"1f600",sprite:"people"},{names:["smiley"],keywords:["face","mouth","open","smile"],unicode:"1f603",sprite:"people"},{names:["smile"],keywords:["eye","face","mouth","open","smile"],unicode:"1f604",sprite:"people"},{names:["grin"],keywords:["eye","face","grin","smile"],unicode:"1f601",sprite:"people"},{names:["satisfied","laughing"],keywords:"face laugh mouth open satisfied smile".split(" "),
+unicode:"1f606",sprite:"people"},{names:["sweat_smile"],keywords:["cold","face","open","smile","sweat"],unicode:"1f605",sprite:"people"},{names:["joy"],keywords:["face","joy","laugh","tear"],unicode:"1f602",sprite:"people"},{names:["rolling_on_the_floor_laughing","rofl"],keywords:["face","floor","laugh","rolling"],unicode:"1f923",sprite:"people"},{names:["relaxed"],keywords:["face","outlined","relaxed","smile"],unicode:"263a",sprite:"people"},{names:["blush"],keywords:["blush","eye","face","smile"],
+unicode:"1f60a",sprite:"people"},{names:["innocent"],keywords:"angel;face;fairy tale;fantasy;halo;innocent;smile".split(";"),unicode:"1f607",sprite:"people"},{names:["slightly_smiling_face","slight_smile"],keywords:["face","smile"],unicode:"1f642",sprite:"people"},{names:["upside_down_face","upside_down"],keywords:["face","upside-down"],unicode:"1f643",sprite:"people"},{names:["wink"],keywords:["face","wink"],unicode:"1f609",sprite:"people"},{names:["relieved"],keywords:["face","relieved"],unicode:"1f60c",
+sprite:"people"},{names:["heart_eyes"],keywords:["eye","face","love","smile"],unicode:"1f60d",sprite:"people"},{names:["kissing_heart"],keywords:["face","kiss"],unicode:"1f618",sprite:"people"},{names:["kissing"],keywords:["face","kiss"],unicode:"1f617",sprite:"people"},{names:["kissing_smiling_eyes"],keywords:["eye","face","kiss","smile"],unicode:"1f619",sprite:"people"},{names:["kissing_closed_eyes"],keywords:["closed","eye","face","kiss"],unicode:"1f61a",sprite:"people"},{names:["yum"],keywords:"delicious face savouring smile um yum".split(" "),
+unicode:"1f60b",sprite:"people"},{names:["stuck_out_tongue_winking_eye"],keywords:["eye","face","joke","tongue","wink"],unicode:"1f61c",sprite:"people"},{names:["stuck_out_tongue_closed_eyes"],keywords:["eye","face","horrible","taste","tongue"],unicode:"1f61d",sprite:"people"},{names:["stuck_out_tongue"],keywords:["face","tongue"],unicode:"1f61b",sprite:"people"},{names:["money_mouth_face","money_mouth"],keywords:["face","money","mouth"],unicode:"1f911",sprite:"people"},{names:["hugging_face","hugging"],
+keywords:["face","hug","hugging"],unicode:"1f917",sprite:"people"},{names:["nerd_face","nerd"],keywords:["face","geek","nerd"],unicode:"1f913",sprite:"people"},{names:["sunglasses"],keywords:"bright cool eye eyewear face glasses smile sun sunglasses".split(" "),unicode:"1f60e",sprite:"people"},{names:["clown_face","clown"],keywords:["clown","face"],unicode:"1f921",sprite:"people"},{names:["face_with_cowboy_hat","cowboy"],keywords:["cowboy","cowgirl","face","hat"],unicode:"1f920",sprite:"people"},
+{names:["smirk"],keywords:["face","smirk"],unicode:"1f60f",sprite:"people"},{names:["unamused"],keywords:["face","unamused","unhappy"],unicode:"1f612",sprite:"people"},{names:["disappointed"],keywords:["disappointed","face"],unicode:"1f61e",sprite:"people"},{names:["pensive"],keywords:["dejected","face","pensive"],unicode:"1f614",sprite:"people"},{names:["worried"],keywords:["face","worried"],unicode:"1f61f",sprite:"people"},{names:["confused"],keywords:["confused","face"],unicode:"1f615",sprite:"people"},
+{names:["slightly_frowning_face","slight_frown"],keywords:["face","frown"],unicode:"1f641",sprite:"people"},{names:["persevere"],keywords:["face","persevere"],unicode:"1f623",sprite:"people"},{names:["confounded"],keywords:["confounded","face"],unicode:"1f616",sprite:"people"},{names:["tired_face"],keywords:["face","tired"],unicode:"1f62b",sprite:"people"},{names:["weary"],keywords:["face","tired","weary"],unicode:"1f629",sprite:"people"},{names:["triumph"],keywords:["face","triumph","won"],unicode:"1f624",
+sprite:"people"},{names:["angry"],keywords:["angry","face","mad"],unicode:"1f620",sprite:"people"},{names:["rage"],keywords:"angry face mad pouting rage red".split(" "),unicode:"1f621",sprite:"people"},{names:["no_mouth"],keywords:["face","mouth","quiet","silent"],unicode:"1f636",sprite:"people"},{names:["neutral_face"],keywords:["deadpan","face","neutral"],unicode:"1f610",sprite:"people"},{names:["expressionless"],keywords:["expressionless","face","inexpressive","unexpressive"],unicode:"1f611",sprite:"people"},
+{names:["hushed"],keywords:["face","hushed","stunned","surprised"],unicode:"1f62f",sprite:"people"},{names:["frowning"],keywords:["face","frown","mouth","open"],unicode:"1f626",sprite:"people"},{names:["anguished"],keywords:["anguished","face"],unicode:"1f627",sprite:"people"},{names:["open_mouth"],keywords:["face","mouth","open","sympathy"],unicode:"1f62e",sprite:"people"},{names:["astonished"],keywords:["astonished","face","shocked","totally"],unicode:"1f632",sprite:"people"},{names:["dizzy_face"],
+keywords:["dizzy","face"],unicode:"1f635",sprite:"people"},{names:["flushed"],keywords:["dazed","face","flushed"],unicode:"1f633",sprite:"people"},{names:["scream"],keywords:"face fear fearful munch scared scream".split(" "),unicode:"1f631",sprite:"people"},{names:["fearful"],keywords:["face","fear","fearful","scared"],unicode:"1f628",sprite:"people"},{names:["cold_sweat"],keywords:"blue cold face mouth open rushed sweat".split(" "),unicode:"1f630",sprite:"people"},{names:["cry"],keywords:["cry",
+"face","sad","tear"],unicode:"1f622",sprite:"people"},{names:["disappointed_relieved"],keywords:["disappointed","face","relieved","whew"],unicode:"1f625",sprite:"people"},{names:["drool","drooling_face"],keywords:["drooling","face"],unicode:"1f924",sprite:"people"},{names:["sob"],keywords:["cry","face","sad","sob","tear"],unicode:"1f62d",sprite:"people"},{names:["sweat"],keywords:["cold","face","sweat"],unicode:"1f613",sprite:"people"},{names:["sleepy"],keywords:["face","sleep"],unicode:"1f62a",sprite:"people"},
+{names:["sleeping"],keywords:["face","sleep","zzz"],unicode:"1f634",sprite:"people"},{names:["face_with_rolling_eyes","rolling_eyes"],keywords:["eyes","face","rolling"],unicode:"1f644",sprite:"people"},{names:["thinking_face","thinking"],keywords:["face","thinking"],unicode:"1f914",sprite:"people"},{names:["liar","lying_face"],keywords:["face","lie","pinocchio"],unicode:"1f925",sprite:"people"},{names:["grimacing"],keywords:["face","grimace"],unicode:"1f62c",sprite:"people"},{names:["zipper_mouth_face",
+"zipper_mouth"],keywords:["face","mouth","zipper"],unicode:"1f910",sprite:"people"},{names:["sick","nauseated_face"],keywords:["face","nauseated","vomit"],unicode:"1f922",sprite:"people"},{names:["sneeze","sneezing_face"],keywords:["face","gesundheit","sneeze"],unicode:"1f927",sprite:"people"},{names:["mask"],keywords:"cold doctor face mask medicine sick".split(" "),unicode:"1f637",sprite:"people"},{names:["face_with_thermometer","thermometer_face"],keywords:["face","ill","sick","thermometer"],unicode:"1f912",
+sprite:"people"},{names:["face_with_head_bandage","head_bandage"],keywords:["bandage","face","hurt","injury"],unicode:"1f915",sprite:"people"},{names:["smiling_imp"],keywords:["face","fairy tale","fantasy","horns","smile"],unicode:"1f608",sprite:"people"},{names:["imp"],keywords:"demon;devil;face;fairy tale;fantasy;imp".split(";"),unicode:"1f47f",sprite:"people"},{names:["japanese_ogre"],keywords:[""],unicode:"1f479",sprite:"people"},{names:["japanese_goblin"],keywords:["creature","face","fairy tale",
+"fantasy","monster"],unicode:"1f47a",sprite:"people"},{names:["shit","hankey","poo","poop"],keywords:"comic dung face monster poo poop".split(" "),unicode:"1f4a9",sprite:"people"},{names:["ghost"],keywords:["creature","face","fairy tale","fantasy","monster"],unicode:"1f47b",sprite:"people"},{names:["skeleton","skull"],keywords:["death","face","fairy tale","monster"],unicode:"1f480",sprite:"people"},{names:["alien"],keywords:"creature;extraterrestrial;face;fairy tale;fantasy;monster;ufo".split(";"),
+unicode:"1f47d",sprite:"people"},{names:["space_invader"],keywords:"alien;creature;extraterrestrial;face;fairy tale;fantasy;monster;ufo".split(";"),unicode:"1f47e",sprite:"people"},{names:["robot_face","robot"],keywords:["face","monster","robot"],unicode:"1f916",sprite:"people"},{names:["jack_o_lantern"],keywords:["celebration","halloween","jack","lantern"],unicode:"1f383",sprite:"people"},{names:["smiley_cat"],keywords:["cat","face","mouth","open","smile"],unicode:"1f63a",sprite:"people"},{names:["smile_cat"],
+keywords:["cat","eye","face","grin","smile"],unicode:"1f638",sprite:"people"},{names:["joy_cat"],keywords:["cat","face","joy","tear"],unicode:"1f639",sprite:"people"},{names:["heart_eyes_cat"],keywords:["cat","eye","face","love","smile"],unicode:"1f63b",sprite:"people"},{names:["smirk_cat"],keywords:["cat","face","ironic","smile","wry"],unicode:"1f63c",sprite:"people"},{names:["kissing_cat"],keywords:["cat","eye","face","kiss"],unicode:"1f63d",sprite:"people"},{names:["scream_cat"],keywords:["cat",
+"face","oh","surprised","weary"],unicode:"1f640",sprite:"people"},{names:["crying_cat_face"],keywords:["cat","cry","face","sad","tear"],unicode:"1f63f",sprite:"people"},{names:["pouting_cat"],keywords:["cat","face","pouting"],unicode:"1f63e",sprite:"people"},{names:["open_hands"],keywords:["hand","open"],unicode:"1f450",sprite:"people"},{names:["open_hands_tone1"],keywords:["hand","light skin tone","open"],unicode:"1f450-1f3fb",sprite:"people"},{names:["open_hands_tone2"],keywords:["hand","medium-light skin tone",
+"open"],unicode:"1f450-1f3fc",sprite:"people"},{names:["open_hands_tone3"],keywords:["hand","medium skin tone","open"],unicode:"1f450-1f3fd",sprite:"people"},{names:["open_hands_tone4"],keywords:["hand","medium-dark skin tone","open"],unicode:"1f450-1f3fe",sprite:"people"},{names:["open_hands_tone5"],keywords:["dark skin tone","hand","open"],unicode:"1f450-1f3ff",sprite:"people"},{names:["raised_hands"],keywords:["celebration","gesture","hand","hooray","raised"],unicode:"1f64c",sprite:"people"},{names:["raised_hands_tone1"],
+keywords:"celebration;gesture;hand;hooray;light skin tone;raised".split(";"),unicode:"1f64c-1f3fb",sprite:"people"},{names:["raised_hands_tone2"],keywords:"celebration;gesture;hand;hooray;medium-light skin tone;raised".split(";"),unicode:"1f64c-1f3fc",sprite:"people"},{names:["raised_hands_tone3"],keywords:"celebration;gesture;hand;hooray;medium skin tone;raised".split(";"),unicode:"1f64c-1f3fd",sprite:"people"},{names:["raised_hands_tone4"],keywords:"celebration;gesture;hand;hooray;medium-dark skin tone;raised".split(";"),
+unicode:"1f64c-1f3fe",sprite:"people"},{names:["raised_hands_tone5"],keywords:"celebration;dark skin tone;gesture;hand;hooray;raised".split(";"),unicode:"1f64c-1f3ff",sprite:"people"},{names:["clap"],keywords:["clap","hand"],unicode:"1f44f",sprite:"people"},{names:["clap_tone1"],keywords:["clap","hand","light skin tone"],unicode:"1f44f-1f3fb",sprite:"people"},{names:["clap_tone2"],keywords:["clap","hand","medium-light skin tone"],unicode:"1f44f-1f3fc",sprite:"people"},{names:["clap_tone3"],keywords:["clap",
+"hand","medium skin tone"],unicode:"1f44f-1f3fd",sprite:"people"},{names:["clap_tone4"],keywords:["clap","hand","medium-dark skin tone"],unicode:"1f44f-1f3fe",sprite:"people"},{names:["clap_tone5"],keywords:["clap","dark skin tone","hand"],unicode:"1f44f-1f3ff",sprite:"people"},{names:["pray"],keywords:"ask bow folded gesture hand please pray thanks".split(" "),unicode:"1f64f",sprite:"people"},{names:["pray_tone1"],keywords:"ask;bow;folded;gesture;hand;light skin tone;please;pray;thanks".split(";"),
+unicode:"1f64f-1f3fb",sprite:"people"},{names:["pray_tone2"],keywords:"ask;bow;folded;gesture;hand;medium-light skin tone;please;pray;thanks".split(";"),unicode:"1f64f-1f3fc",sprite:"people"},{names:["pray_tone3"],keywords:"ask;bow;folded;gesture;hand;medium skin tone;please;pray;thanks".split(";"),unicode:"1f64f-1f3fd",sprite:"people"},{names:["pray_tone4"],keywords:"ask;bow;folded;gesture;hand;medium-dark skin tone;please;pray;thanks".split(";"),unicode:"1f64f-1f3fe",sprite:"people"},{names:["pray_tone5"],
+keywords:"ask;bow;dark skin tone;folded;gesture;hand;please;pray;thanks".split(";"),unicode:"1f64f-1f3ff",sprite:"people"},{names:["shaking_hands","handshake"],keywords:["agreement","hand","handshake","meeting","shake"],unicode:"1f91d",sprite:"people"},{names:["+1","thumbup","thumbsup"],keywords:["+1","hand","thumb","up"],unicode:"1f44d",sprite:"people"},{names:["+1_tone1","thumbup_tone1","thumbsup_tone1"],keywords:["+1","hand","light skin tone","thumb","up"],unicode:"1f44d-1f3fb",sprite:"people"},
+{names:["+1_tone2","thumbup_tone2","thumbsup_tone2"],keywords:["+1","hand","medium-light skin tone","thumb","up"],unicode:"1f44d-1f3fc",sprite:"people"},{names:["+1_tone3","thumbup_tone3","thumbsup_tone3"],keywords:["+1","hand","medium skin tone","thumb","up"],unicode:"1f44d-1f3fd",sprite:"people"},{names:["+1_tone4","thumbup_tone4","thumbsup_tone4"],keywords:["+1","hand","medium-dark skin tone","thumb","up"],unicode:"1f44d-1f3fe",sprite:"people"},{names:["+1_tone5","thumbup_tone5","thumbsup_tone5"],
+keywords:["+1","dark skin tone","hand","thumb","up"],unicode:"1f44d-1f3ff",sprite:"people"},{names:["-1","thumbdown","thumbsdown"],keywords:["-1","down","hand","thumb"],unicode:"1f44e",sprite:"people"},{names:["-1_tone1","thumbdown_tone1","thumbsdown_tone1"],keywords:["-1","down","hand","light skin tone","thumb"],unicode:"1f44e-1f3fb",sprite:"people"},{names:["-1_tone2","thumbdown_tone2","thumbsdown_tone2"],keywords:["-1","down","hand","medium-light skin tone","thumb"],unicode:"1f44e-1f3fc",sprite:"people"},
+{names:["-1_tone3","thumbdown_tone3","thumbsdown_tone3"],keywords:["-1","down","hand","medium skin tone","thumb"],unicode:"1f44e-1f3fd",sprite:"people"},{names:["-1_tone4","thumbdown_tone4","thumbsdown_tone4"],keywords:["-1","down","hand","medium-dark skin tone","thumb"],unicode:"1f44e-1f3fe",sprite:"people"},{names:["-1_tone5","thumbdown_tone5","thumbsdown_tone5"],keywords:["-1","dark skin tone","down","hand","thumb"],unicode:"1f44e-1f3ff",sprite:"people"},{names:["punch"],keywords:["clenched","fist",
+"hand","punch"],unicode:"1f44a",sprite:"people"},{names:["punch_tone1"],keywords:["clenched","fist","hand","light skin tone","punch"],unicode:"1f44a-1f3fb",sprite:"people"},{names:["punch_tone2"],keywords:["clenched","fist","hand","medium-light skin tone","punch"],unicode:"1f44a-1f3fc",sprite:"people"},{names:["punch_tone3"],keywords:["clenched","fist","hand","medium skin tone","punch"],unicode:"1f44a-1f3fd",sprite:"people"},{names:["punch_tone4"],keywords:["clenched","fist","hand","medium-dark skin tone",
+"punch"],unicode:"1f44a-1f3fe",sprite:"people"},{names:["punch_tone5"],keywords:["clenched","dark skin tone","fist","hand","punch"],unicode:"1f44a-1f3ff",sprite:"people"},{names:["fist"],keywords:["clenched","fist","hand","punch"],unicode:"270a",sprite:"people"},{names:["fist_tone1"],keywords:["clenched","fist","hand","light skin tone","punch"],unicode:"270a-1f3fb",sprite:"people"},{names:["fist_tone2"],keywords:["clenched","fist","hand","medium-light skin tone","punch"],unicode:"270a-1f3fc",sprite:"people"},
+{names:["fist_tone3"],keywords:["clenched","fist","hand","medium skin tone","punch"],unicode:"270a-1f3fd",sprite:"people"},{names:["fist_tone4"],keywords:["clenched","fist","hand","medium-dark skin tone","punch"],unicode:"270a-1f3fe",sprite:"people"},{names:["fist_tone5"],keywords:["clenched","dark skin tone","fist","hand","punch"],unicode:"270a-1f3ff",sprite:"people"},{names:["left_fist","left_facing_fist"],keywords:["fist","leftwards"],unicode:"1f91b",sprite:"people"},{names:["left_fist_tone1",
+"left_facing_fist_tone1"],keywords:["fist","leftwards","light skin tone"],unicode:"1f91b-1f3fb",sprite:"people"},{names:["left_fist_tone2","left_facing_fist_tone2"],keywords:["fist","leftwards","medium-light skin tone"],unicode:"1f91b-1f3fc",sprite:"people"},{names:["left_fist_tone3","left_facing_fist_tone3"],keywords:["fist","leftwards","medium skin tone"],unicode:"1f91b-1f3fd",sprite:"people"},{names:["left_fist_tone4","left_facing_fist_tone4"],keywords:["fist","leftwards","medium-dark skin tone"],
+unicode:"1f91b-1f3fe",sprite:"people"},{names:["left_fist_tone5","left_facing_fist_tone5"],keywords:["dark skin tone","fist","leftwards"],unicode:"1f91b-1f3ff",sprite:"people"},{names:["right_fist","right_facing_fist"],keywords:["fist","rightwards"],unicode:"1f91c",sprite:"people"},{names:["right_fist_tone1","right_facing_fist_tone1"],keywords:["fist","light skin tone","rightwards"],unicode:"1f91c-1f3fb",sprite:"people"},{names:["right_fist_tone2","right_facing_fist_tone2"],keywords:["fist","medium-light skin tone",
+"rightwards"],unicode:"1f91c-1f3fc",sprite:"people"},{names:["right_fist_tone3","right_facing_fist_tone3"],keywords:["fist","medium skin tone","rightwards"],unicode:"1f91c-1f3fd",sprite:"people"},{names:["right_fist_tone4","right_facing_fist_tone4"],keywords:["fist","medium-dark skin tone","rightwards"],unicode:"1f91c-1f3fe",sprite:"people"},{names:["right_fist_tone5","right_facing_fist_tone5"],keywords:["dark skin tone","fist","rightwards"],unicode:"1f91c-1f3ff",sprite:"people"},{names:["hand_with_index_and_middle_finger_crossed",
+"fingers_crossed"],keywords:["cross","finger","hand","luck"],unicode:"1f91e",sprite:"people"},{names:["hand_with_index_and_middle_fingers_crossed_tone1","fingers_crossed_tone1"],keywords:["cross","finger","hand","light skin tone","luck"],unicode:"1f91e-1f3fb",sprite:"people"},{names:["hand_with_index_and_middle_fingers_crossed_tone2","fingers_crossed_tone2"],keywords:["cross","finger","hand","luck","medium-light skin tone"],unicode:"1f91e-1f3fc",sprite:"people"},{names:["hand_with_index_and_middle_fingers_crossed_tone3",
+"fingers_crossed_tone3"],keywords:["cross","finger","hand","luck","medium skin tone"],unicode:"1f91e-1f3fd",sprite:"people"},{names:["hand_with_index_and_middle_fingers_crossed_tone4","fingers_crossed_tone4"],keywords:["cross","finger","hand","luck","medium-dark skin tone"],unicode:"1f91e-1f3fe",sprite:"people"},{names:["hand_with_index_and_middle_fingers_crossed_tone5","fingers_crossed_tone5"],keywords:["cross","dark skin tone","finger","hand","luck"],unicode:"1f91e-1f3ff",sprite:"people"},{names:["v"],
+keywords:["hand","v","victory"],unicode:"270c",sprite:"people"},{names:["v_tone1"],keywords:["hand","light skin tone","v","victory"],unicode:"270c-1f3fb",sprite:"people"},{names:["v_tone2"],keywords:["hand","medium-light skin tone","v","victory"],unicode:"270c-1f3fc",sprite:"people"},{names:["v_tone3"],keywords:["hand","medium skin tone","v","victory"],unicode:"270c-1f3fd",sprite:"people"},{names:["v_tone4"],keywords:["hand","medium-dark skin tone","v","victory"],unicode:"270c-1f3fe",sprite:"people"},
+{names:["v_tone5"],keywords:["dark skin tone","hand","v","victory"],unicode:"270c-1f3ff",sprite:"people"},{names:["sign_of_the_horns","metal"],keywords:["finger","hand","horns","rock-on"],unicode:"1f918",sprite:"people"},{names:["sign_of_the_horns_tone1","metal_tone1"],keywords:["finger","hand","horns","light skin tone","rock-on"],unicode:"1f918-1f3fb",sprite:"people"},{names:["sign_of_the_horns_tone2","metal_tone2"],keywords:["finger","hand","horns","medium-light skin tone","rock-on"],unicode:"1f918-1f3fc",
+sprite:"people"},{names:["sign_of_the_horns_tone3","metal_tone3"],keywords:["finger","hand","horns","medium skin tone","rock-on"],unicode:"1f918-1f3fd",sprite:"people"},{names:["sign_of_the_horns_tone4","metal_tone4"],keywords:["finger","hand","horns","medium-dark skin tone","rock-on"],unicode:"1f918-1f3fe",sprite:"people"},{names:["sign_of_the_horns_tone5","metal_tone5"],keywords:["dark skin tone","finger","hand","horns","rock-on"],unicode:"1f918-1f3ff",sprite:"people"},{names:["ok_hand"],keywords:["OK",
+"hand"],unicode:"1f44c",sprite:"people"},{names:["ok_hand_tone1"],keywords:["OK","hand","light skin tone"],unicode:"1f44c-1f3fb",sprite:"people"},{names:["ok_hand_tone2"],keywords:["OK","hand","medium-light skin tone"],unicode:"1f44c-1f3fc",sprite:"people"},{names:["ok_hand_tone3"],keywords:["OK","hand","medium skin tone"],unicode:"1f44c-1f3fd",sprite:"people"},{names:["ok_hand_tone4"],keywords:["OK","hand","medium-dark skin tone"],unicode:"1f44c-1f3fe",sprite:"people"},{names:["ok_hand_tone5"],keywords:["OK",
+"dark skin tone","hand"],unicode:"1f44c-1f3ff",sprite:"people"},{names:["point_left"],keywords:["backhand","finger","hand","index","point"],unicode:"1f448",sprite:"people"},{names:["point_left_tone1"],keywords:"backhand;finger;hand;index;light skin tone;point".split(";"),unicode:"1f448-1f3fb",sprite:"people"},{names:["point_left_tone2"],keywords:"backhand;finger;hand;index;medium-light skin tone;point".split(";"),unicode:"1f448-1f3fc",sprite:"people"},{names:["point_left_tone3"],keywords:"backhand;finger;hand;index;medium skin tone;point".split(";"),
+unicode:"1f448-1f3fd",sprite:"people"},{names:["point_left_tone4"],keywords:"backhand;finger;hand;index;medium-dark skin tone;point".split(";"),unicode:"1f448-1f3fe",sprite:"people"},{names:["point_left_tone5"],keywords:"backhand;dark skin tone;finger;hand;index;point".split(";"),unicode:"1f448-1f3ff",sprite:"people"},{names:["point_right"],keywords:["backhand","finger","hand","index","point"],unicode:"1f449",sprite:"people"},{names:["point_right_tone1"],keywords:"backhand;finger;hand;index;light skin tone;point".split(";"),
+unicode:"1f449-1f3fb",sprite:"people"},{names:["point_right_tone2"],keywords:"backhand;finger;hand;index;medium-light skin tone;point".split(";"),unicode:"1f449-1f3fc",sprite:"people"},{names:["point_right_tone3"],keywords:"backhand;finger;hand;index;medium skin tone;point".split(";"),unicode:"1f449-1f3fd",sprite:"people"},{names:["point_right_tone4"],keywords:"backhand;finger;hand;index;medium-dark skin tone;point".split(";"),unicode:"1f449-1f3fe",sprite:"people"},{names:["point_right_tone5"],keywords:"backhand;dark skin tone;finger;hand;index;point".split(";"),
+unicode:"1f449-1f3ff",sprite:"people"},{names:["point_up_2"],keywords:"backhand finger hand index point up".split(" "),unicode:"1f446",sprite:"people"},{names:["point_up_2_tone1"],keywords:"backhand;finger;hand;index;light skin tone;point;up".split(";"),unicode:"1f446-1f3fb",sprite:"people"},{names:["point_up_2_tone2"],keywords:"backhand;finger;hand;index;medium-light skin tone;point;up".split(";"),unicode:"1f446-1f3fc",sprite:"people"},{names:["point_up_2_tone3"],keywords:"backhand;finger;hand;index;medium skin tone;point;up".split(";"),
+unicode:"1f446-1f3fd",sprite:"people"},{names:["point_up_2_tone4"],keywords:"backhand;finger;hand;index;medium-dark skin tone;point;up".split(";"),unicode:"1f446-1f3fe",sprite:"people"},{names:["point_up_2_tone5"],keywords:"backhand;dark skin tone;finger;hand;index;point;up".split(";"),unicode:"1f446-1f3ff",sprite:"people"},{names:["point_down"],keywords:"backhand down finger hand index point".split(" "),unicode:"1f447",sprite:"people"},{names:["point_down_tone1"],keywords:"backhand;down;finger;hand;index;light skin tone;point".split(";"),
+unicode:"1f447-1f3fb",sprite:"people"},{names:["point_down_tone2"],keywords:"backhand;down;finger;hand;index;medium-light skin tone;point".split(";"),unicode:"1f447-1f3fc",sprite:"people"},{names:["point_down_tone3"],keywords:"backhand;down;finger;hand;index;medium skin tone;point".split(";"),unicode:"1f447-1f3fd",sprite:"people"},{names:["point_down_tone4"],keywords:"backhand;down;finger;hand;index;medium-dark skin tone;point".split(";"),unicode:"1f447-1f3fe",sprite:"people"},{names:["point_down_tone5"],
+keywords:"backhand;dark skin tone;down;finger;hand;index;point".split(";"),unicode:"1f447-1f3ff",sprite:"people"},{names:["point_up"],keywords:["finger","hand","index","point","up"],unicode:"261d",sprite:"people"},{names:["point_up_tone1"],keywords:"finger;hand;index;light skin tone;point;up".split(";"),unicode:"261d-1f3fb",sprite:"people"},{names:["point_up_tone2"],keywords:"finger;hand;index;medium-light skin tone;point;up".split(";"),unicode:"261d-1f3fc",sprite:"people"},{names:["point_up_tone3"],
+keywords:"finger;hand;index;medium skin tone;point;up".split(";"),unicode:"261d-1f3fd",sprite:"people"},{names:["point_up_tone4"],keywords:"finger;hand;index;medium-dark skin tone;point;up".split(";"),unicode:"261d-1f3fe",sprite:"people"},{names:["point_up_tone5"],keywords:"dark skin tone;finger;hand;index;point;up".split(";"),unicode:"261d-1f3ff",sprite:"people"},{names:["raised_hand"],keywords:["hand"],unicode:"270b",sprite:"people"},{names:["raised_hand_tone1"],keywords:["hand","light skin tone"],
+unicode:"270b-1f3fb",sprite:"people"},{names:["raised_hand_tone2"],keywords:["hand","medium-light skin tone"],unicode:"270b-1f3fc",sprite:"people"},{names:["raised_hand_tone3"],keywords:["hand","medium skin tone"],unicode:"270b-1f3fd",sprite:"people"},{names:["raised_hand_tone4"],keywords:["hand","medium-dark skin tone"],unicode:"270b-1f3fe",sprite:"people"},{names:["raised_hand_tone5"],keywords:["dark skin tone","hand"],unicode:"270b-1f3ff",sprite:"people"},{names:["back_of_hand","raised_back_of_hand"],
+keywords:["backhand","raised"],unicode:"1f91a",sprite:"people"},{names:["back_of_hand_tone1","raised_back_of_hand_tone1"],keywords:["backhand","light skin tone","raised"],unicode:"1f91a-1f3fb",sprite:"people"},{names:["back_of_hand_tone2","raised_back_of_hand_tone2"],keywords:["backhand","medium-light skin tone","raised"],unicode:"1f91a-1f3fc",sprite:"people"},{names:["back_of_hand_tone3","raised_back_of_hand_tone3"],keywords:["backhand","medium skin tone","raised"],unicode:"1f91a-1f3fd",sprite:"people"},
+{names:["back_of_hand_tone4","raised_back_of_hand_tone4"],keywords:["backhand","medium-dark skin tone","raised"],unicode:"1f91a-1f3fe",sprite:"people"},{names:["back_of_hand_tone5","raised_back_of_hand_tone5"],keywords:["backhand","dark skin tone","raised"],unicode:"1f91a-1f3ff",sprite:"people"},{names:["raised_hand_with_fingers_splayed","hand_splayed"],keywords:["finger","hand","splayed"],unicode:"1f590",sprite:"people"},{names:["raised_hand_with_fingers_splayed_tone1","hand_splayed_tone1"],keywords:["finger",
+"hand","light skin tone","splayed"],unicode:"1f590-1f3fb",sprite:"people"},{names:["raised_hand_with_fingers_splayed_tone2","hand_splayed_tone2"],keywords:["finger","hand","medium-light skin tone","splayed"],unicode:"1f590-1f3fc",sprite:"people"},{names:["raised_hand_with_fingers_splayed_tone3","hand_splayed_tone3"],keywords:["finger","hand","medium skin tone","splayed"],unicode:"1f590-1f3fd",sprite:"people"},{names:["raised_hand_with_fingers_splayed_tone4","hand_splayed_tone4"],keywords:["finger",
+"hand","medium-dark skin tone","splayed"],unicode:"1f590-1f3fe",sprite:"people"},{names:["raised_hand_with_fingers_splayed_tone5","hand_splayed_tone5"],keywords:["dark skin tone","finger","hand","splayed"],unicode:"1f590-1f3ff",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers","vulcan"],keywords:["finger","hand","spock","vulcan"],unicode:"1f596",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone1","vulcan_tone1"],keywords:["finger",
+"hand","light skin tone","spock","vulcan"],unicode:"1f596-1f3fb",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone2","vulcan_tone2"],keywords:["finger","hand","medium-light skin tone","spock","vulcan"],unicode:"1f596-1f3fc",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone3","vulcan_tone3"],keywords:["finger","hand","medium skin tone","spock","vulcan"],unicode:"1f596-1f3fd",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone4",
+"vulcan_tone4"],keywords:["finger","hand","medium-dark skin tone","spock","vulcan"],unicode:"1f596-1f3fe",sprite:"people"},{names:["raised_hand_with_part_between_middle_and_ring_fingers_tone5","vulcan_tone5"],keywords:["dark skin tone","finger","hand","spock","vulcan"],unicode:"1f596-1f3ff",sprite:"people"},{names:["wave"],keywords:["hand","wave","waving"],unicode:"1f44b",sprite:"people"},{names:["wave_tone1"],keywords:["hand","light skin tone","wave","waving"],unicode:"1f44b-1f3fb",sprite:"people"},
+{names:["wave_tone2"],keywords:["hand","medium-light skin tone","wave","waving"],unicode:"1f44b-1f3fc",sprite:"people"},{names:["wave_tone3"],keywords:["hand","medium skin tone","wave","waving"],unicode:"1f44b-1f3fd",sprite:"people"},{names:["wave_tone4"],keywords:["hand","medium-dark skin tone","wave","waving"],unicode:"1f44b-1f3fe",sprite:"people"},{names:["wave_tone5"],keywords:["dark skin tone","hand","wave","waving"],unicode:"1f44b-1f3ff",sprite:"people"},{names:["call_me_hand","call_me"],keywords:["call",
+"hand"],unicode:"1f919",sprite:"people"},{names:["call_me_hand_tone1","call_me_tone1"],keywords:["call","hand","light skin tone"],unicode:"1f919-1f3fb",sprite:"people"},{names:["call_me_hand_tone2","call_me_tone2"],keywords:["call","hand","medium-light skin tone"],unicode:"1f919-1f3fc",sprite:"people"},{names:["call_me_hand_tone3","call_me_tone3"],keywords:["call","hand","medium skin tone"],unicode:"1f919-1f3fd",sprite:"people"},{names:["call_me_hand_tone4","call_me_tone4"],keywords:["call","hand",
+"medium-dark skin tone"],unicode:"1f919-1f3fe",sprite:"people"},{names:["call_me_hand_tone5","call_me_tone5"],keywords:["call","dark skin tone","hand"],unicode:"1f919-1f3ff",sprite:"people"},{names:["muscle"],keywords:["biceps","comic","flex","muscle"],unicode:"1f4aa",sprite:"people"},{names:["muscle_tone1"],keywords:["biceps","comic","flex","light skin tone","muscle"],unicode:"1f4aa-1f3fb",sprite:"people"},{names:["muscle_tone2"],keywords:["biceps","comic","flex","medium-light skin tone","muscle"],
+unicode:"1f4aa-1f3fc",sprite:"people"},{names:["muscle_tone3"],keywords:["biceps","comic","flex","medium skin tone","muscle"],unicode:"1f4aa-1f3fd",sprite:"people"},{names:["muscle_tone4"],keywords:["biceps","comic","flex","medium-dark skin tone","muscle"],unicode:"1f4aa-1f3fe",sprite:"people"},{names:["muscle_tone5"],keywords:["biceps","comic","dark skin tone","flex","muscle"],unicode:"1f4aa-1f3ff",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended","middle_finger"],keywords:["finger",
+"hand"],unicode:"1f595",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended_tone1","middle_finger_tone1"],keywords:["finger","hand","light skin tone"],unicode:"1f595-1f3fb",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended_tone2","middle_finger_tone2"],keywords:["finger","hand","medium-light skin tone"],unicode:"1f595-1f3fc",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended_tone3","middle_finger_tone3"],keywords:["finger","hand","medium skin tone"],
+unicode:"1f595-1f3fd",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended_tone4","middle_finger_tone4"],keywords:["finger","hand","medium-dark skin tone"],unicode:"1f595-1f3fe",sprite:"people"},{names:["reversed_hand_with_middle_finger_extended_tone5","middle_finger_tone5"],keywords:["dark skin tone","finger","hand"],unicode:"1f595-1f3ff",sprite:"people"},{names:["writing_hand"],keywords:["hand","write"],unicode:"270d",sprite:"people"},{names:["writing_hand_tone1"],keywords:["hand",
+"light skin tone","write"],unicode:"270d-1f3fb",sprite:"people"},{names:["writing_hand_tone2"],keywords:["hand","medium-light skin tone","write"],unicode:"270d-1f3fc",sprite:"people"},{names:["writing_hand_tone3"],keywords:["hand","medium skin tone","write"],unicode:"270d-1f3fd",sprite:"people"},{names:["writing_hand_tone4"],keywords:["hand","medium-dark skin tone","write"],unicode:"270d-1f3fe",sprite:"people"},{names:["writing_hand_tone5"],keywords:["dark skin tone","hand","write"],unicode:"270d-1f3ff",
+sprite:"people"},{names:["selfie"],keywords:["camera","phone","selfie"],unicode:"1f933",sprite:"people"},{names:["selfie_tone1"],keywords:["camera","light skin tone","phone","selfie"],unicode:"1f933-1f3fb",sprite:"people"},{names:["selfie_tone2"],keywords:["camera","medium-light skin tone","phone","selfie"],unicode:"1f933-1f3fc",sprite:"people"},{names:["selfie_tone3"],keywords:["camera","medium skin tone","phone","selfie"],unicode:"1f933-1f3fd",sprite:"people"},{names:["selfie_tone4"],keywords:["camera",
+"medium-dark skin tone","phone","selfie"],unicode:"1f933-1f3fe",sprite:"people"},{names:["selfie_tone5"],keywords:["camera","dark skin tone","phone","selfie"],unicode:"1f933-1f3ff",sprite:"people"},{names:["nail_care"],keywords:["care","cosmetics","manicure","nail","polish"],unicode:"1f485",sprite:"people"},{names:["nail_care_tone1"],keywords:"care;cosmetics;light skin tone;manicure;nail;polish".split(";"),unicode:"1f485-1f3fb",sprite:"people"},{names:["nail_care_tone2"],keywords:"care;cosmetics;manicure;medium-light skin tone;nail;polish".split(";"),
+unicode:"1f485-1f3fc",sprite:"people"},{names:["nail_care_tone3"],keywords:"care;cosmetics;manicure;medium skin tone;nail;polish".split(";"),unicode:"1f485-1f3fd",sprite:"people"},{names:["nail_care_tone4"],keywords:"care;cosmetics;manicure;medium-dark skin tone;nail;polish".split(";"),unicode:"1f485-1f3fe",sprite:"people"},{names:["nail_care_tone5"],keywords:"care;cosmetics;dark skin tone;manicure;nail;polish".split(";"),unicode:"1f485-1f3ff",sprite:"people"},{names:["ring"],keywords:["diamond"],
+unicode:"1f48d",sprite:"people"},{names:["lipstick"],keywords:["cosmetics","makeup"],unicode:"1f484",sprite:"people"},{names:["kiss"],keywords:["kiss","lips"],unicode:"1f48b",sprite:"people"},{names:["lips"],keywords:["lips"],unicode:"1f444",sprite:"people"},{names:["tongue"],keywords:["body"],unicode:"1f445",sprite:"people"},{names:["ear"],keywords:["body"],unicode:"1f442",sprite:"people"},{names:["ear_tone1"],keywords:["body","light skin tone"],unicode:"1f442-1f3fb",sprite:"people"},{names:["ear_tone2"],
+keywords:["body","medium-light skin tone"],unicode:"1f442-1f3fc",sprite:"people"},{names:["ear_tone3"],keywords:["body","medium skin tone"],unicode:"1f442-1f3fd",sprite:"people"},{names:["ear_tone4"],keywords:["body","medium-dark skin tone"],unicode:"1f442-1f3fe",sprite:"people"},{names:["ear_tone5"],keywords:["body","dark skin tone"],unicode:"1f442-1f3ff",sprite:"people"},{names:["nose"],keywords:["body"],unicode:"1f443",sprite:"people"},{names:["nose_tone1"],keywords:["body","light skin tone"],
+unicode:"1f443-1f3fb",sprite:"people"},{names:["nose_tone2"],keywords:["body","medium-light skin tone"],unicode:"1f443-1f3fc",sprite:"people"},{names:["nose_tone3"],keywords:["body","medium skin tone"],unicode:"1f443-1f3fd",sprite:"people"},{names:["nose_tone4"],keywords:["body","medium-dark skin tone"],unicode:"1f443-1f3fe",sprite:"people"},{names:["nose_tone5"],keywords:["body","dark skin tone"],unicode:"1f443-1f3ff",sprite:"people"},{names:["footprints"],keywords:["clothing","footprint","print"],
+unicode:"1f463",sprite:"people"},{names:["eye"],keywords:["body"],unicode:"1f441",sprite:"people"},{names:["eyes"],keywords:["eye","face"],unicode:"1f440",sprite:"people"},{names:["speaking_head_in_silhouette","speaking_head"],keywords:["face","head","silhouette","speak","speaking"],unicode:"1f5e3",sprite:"people"},{names:["bust_in_silhouette"],keywords:["bust","silhouette"],unicode:"1f464",sprite:"people"},{names:["busts_in_silhouette"],keywords:["bust","silhouette"],unicode:"1f465",sprite:"people"},
+{names:["baby"],keywords:["baby","young"],unicode:"1f476",sprite:"people"},{names:["baby_tone1"],keywords:["baby","light skin tone","young"],unicode:"1f476-1f3fb",sprite:"people"},{names:["baby_tone2"],keywords:["baby","medium-light skin tone","young"],unicode:"1f476-1f3fc",sprite:"people"},{names:["baby_tone3"],keywords:["baby","medium skin tone","young"],unicode:"1f476-1f3fd",sprite:"people"},{names:["baby_tone4"],keywords:["baby","medium-dark skin tone","young"],unicode:"1f476-1f3fe",sprite:"people"},
+{names:["baby_tone5"],keywords:["baby","dark skin tone","young"],unicode:"1f476-1f3ff",sprite:"people"},{names:["boy"],keywords:["boy","young"],unicode:"1f466",sprite:"people"},{names:["boy_tone1"],keywords:["boy","light skin tone","young"],unicode:"1f466-1f3fb",sprite:"people"},{names:["boy_tone2"],keywords:["boy","medium-light skin tone","young"],unicode:"1f466-1f3fc",sprite:"people"},{names:["boy_tone3"],keywords:["boy","medium skin tone","young"],unicode:"1f466-1f3fd",sprite:"people"},{names:["boy_tone4"],
+keywords:["boy","medium-dark skin tone","young"],unicode:"1f466-1f3fe",sprite:"people"},{names:["boy_tone5"],keywords:["boy","dark skin tone","young"],unicode:"1f466-1f3ff",sprite:"people"},{names:["girl"],keywords:["Virgo","young","zodiac"],unicode:"1f467",sprite:"people"},{names:["girl_tone1"],keywords:["Virgo","light skin tone","young","zodiac"],unicode:"1f467-1f3fb",sprite:"people"},{names:["girl_tone2"],keywords:["Virgo","medium-light skin tone","young","zodiac"],unicode:"1f467-1f3fc",sprite:"people"},
+{names:["girl_tone3"],keywords:["Virgo","medium skin tone","young","zodiac"],unicode:"1f467-1f3fd",sprite:"people"},{names:["girl_tone4"],keywords:["Virgo","medium-dark skin tone","young","zodiac"],unicode:"1f467-1f3fe",sprite:"people"},{names:["girl_tone5"],keywords:["Virgo","dark skin tone","young","zodiac"],unicode:"1f467-1f3ff",sprite:"people"},{names:["man"],keywords:["man"],unicode:"1f468",sprite:"people"},{names:["man_tone1"],keywords:["light skin tone","man"],unicode:"1f468-1f3fb",sprite:"people"},
+{names:["man_tone2"],keywords:["man","medium-light skin tone"],unicode:"1f468-1f3fc",sprite:"people"},{names:["man_tone3"],keywords:["man","medium skin tone"],unicode:"1f468-1f3fd",sprite:"people"},{names:["man_tone4"],keywords:["man","medium-dark skin tone"],unicode:"1f468-1f3fe",sprite:"people"},{names:["man_tone5"],keywords:["dark skin tone","man"],unicode:"1f468-1f3ff",sprite:"people"},{names:["woman"],keywords:["woman"],unicode:"1f469",sprite:"people"},{names:["woman_tone1"],keywords:["light skin tone",
+"woman"],unicode:"1f469-1f3fb",sprite:"people"},{names:["woman_tone2"],keywords:["medium-light skin tone","woman"],unicode:"1f469-1f3fc",sprite:"people"},{names:["woman_tone3"],keywords:["medium skin tone","woman"],unicode:"1f469-1f3fd",sprite:"people"},{names:["woman_tone4"],keywords:["medium-dark skin tone","woman"],unicode:"1f469-1f3fe",sprite:"people"},{names:["woman_tone5"],keywords:["dark skin tone","woman"],unicode:"1f469-1f3ff",sprite:"people"},{names:["blond-haired_woman"],keywords:["blonde",
+"woman"],unicode:"1f471-2640",sprite:"people"},{names:["blond-haired_woman_light_skin_tone","blond-haired_woman_tone1"],keywords:["blonde","light skin tone","woman"],unicode:"1f471-1f3fb-2640",sprite:"people"},{names:["blond-haired_woman_medium_light_skin_tone","blond-haired_woman_tone2"],keywords:["blonde","medium-light skin tone","woman"],unicode:"1f471-1f3fc-2640",sprite:"people"},{names:["blond-haired_woman_medium_skin_tone","blond-haired_woman_tone3"],keywords:["blonde","medium skin tone","woman"],
+unicode:"1f471-1f3fd-2640",sprite:"people"},{names:["blond-haired_woman_medium_dark_skin_tone","blond-haired_woman_tone4"],keywords:["blonde","medium-dark skin tone","woman"],unicode:"1f471-1f3fe-2640",sprite:"people"},{names:["blond-haired_woman_dark_skin_tone","blond-haired_woman_tone5"],keywords:["blonde","dark skin tone","woman"],unicode:"1f471-1f3ff-2640",sprite:"people"},{names:["person_with_blond_hair","blond_haired_person"],keywords:["blond"],unicode:"1f471",sprite:"people"},{names:["person_with_blond_hair_tone1",
+"blond_haired_person_tone1"],keywords:["blond","light skin tone"],unicode:"1f471-1f3fb",sprite:"people"},{names:["person_with_blond_hair_tone2","blond_haired_person_tone2"],keywords:["blond","medium-light skin tone"],unicode:"1f471-1f3fc",sprite:"people"},{names:["person_with_blond_hair_tone3","blond_haired_person_tone3"],keywords:["blond","medium skin tone"],unicode:"1f471-1f3fd",sprite:"people"},{names:["person_with_blond_hair_tone4","blond_haired_person_tone4"],keywords:["blond","medium-dark skin tone"],
+unicode:"1f471-1f3fe",sprite:"people"},{names:["person_with_blond_hair_tone5","blond_haired_person_tone5"],keywords:["blond","dark skin tone"],unicode:"1f471-1f3ff",sprite:"people"},{names:["blond-haired_man"],keywords:["blond","man"],unicode:"1f471-2642",sprite:"people"},{names:["blond-haired_man_dark_skin_tone","blond-haired_man_tone5"],keywords:["blond","dark skin tone","man"],unicode:"1f471-1f3ff-2642",sprite:"people"},{names:["blond-haired_man_medium_dark_skin_tone","blond-haired_man_tone4"],
+keywords:["blond","man","medium-dark skin tone"],unicode:"1f471-1f3fe-2642",sprite:"people"},{names:["blond-haired_man_medium_skin_tone","blond-haired_man_tone3"],keywords:["blond","man","medium skin tone"],unicode:"1f471-1f3fd-2642",sprite:"people"},{names:["blond-haired_man_medium_light_skin_tone","blond-haired_man_tone2"],keywords:["blond","man","medium-light skin tone"],unicode:"1f471-1f3fc-2642",sprite:"people"},{names:["blond-haired_man_light_skin_tone","blond-haired_man_tone1"],keywords:["blond",
+"light skin tone","man"],unicode:"1f471-1f3fb-2642",sprite:"people"},{names:["older_man"],keywords:["man","old"],unicode:"1f474",sprite:"people"},{names:["older_man_tone1"],keywords:["light skin tone","man","old"],unicode:"1f474-1f3fb",sprite:"people"},{names:["older_man_tone2"],keywords:["man","medium-light skin tone","old"],unicode:"1f474-1f3fc",sprite:"people"},{names:["older_man_tone3"],keywords:["man","medium skin tone","old"],unicode:"1f474-1f3fd",sprite:"people"},{names:["older_man_tone4"],
+keywords:["man","medium-dark skin tone","old"],unicode:"1f474-1f3fe",sprite:"people"},{names:["older_man_tone5"],keywords:["dark skin tone","man","old"],unicode:"1f474-1f3ff",sprite:"people"},{names:["grandma","older_woman"],keywords:["old","woman"],unicode:"1f475",sprite:"people"},{names:["grandma_tone1","older_woman_tone1"],keywords:["light skin tone","old","woman"],unicode:"1f475-1f3fb",sprite:"people"},{names:["grandma_tone2","older_woman_tone2"],keywords:["medium-light skin tone","old","woman"],
+unicode:"1f475-1f3fc",sprite:"people"},{names:["grandma_tone3","older_woman_tone3"],keywords:["medium skin tone","old","woman"],unicode:"1f475-1f3fd",sprite:"people"},{names:["grandma_tone4","older_woman_tone4"],keywords:["medium-dark skin tone","old","woman"],unicode:"1f475-1f3fe",sprite:"people"},{names:["grandma_tone5","older_woman_tone5"],keywords:["dark skin tone","old","woman"],unicode:"1f475-1f3ff",sprite:"people"},{names:["man_with_gua_pi_mao","man_with_chinese_cap"],keywords:["gua pi mao",
+"hat","man"],unicode:"1f472",sprite:"people"},{names:["man_with_gua_pi_mao_tone1","man_with_chinese_cap_tone1"],keywords:["gua pi mao","hat","light skin tone","man"],unicode:"1f472-1f3fb",sprite:"people"},{names:["man_with_gua_pi_mao_tone2","man_with_chinese_cap_tone2"],keywords:["gua pi mao","hat","man","medium-light skin tone"],unicode:"1f472-1f3fc",sprite:"people"},{names:["man_with_gua_pi_mao_tone3","man_with_chinese_cap_tone3"],keywords:["gua pi mao","hat","man","medium skin tone"],unicode:"1f472-1f3fd",
+sprite:"people"},{names:["man_with_gua_pi_mao_tone4","man_with_chinese_cap_tone4"],keywords:["gua pi mao","hat","man","medium-dark skin tone"],unicode:"1f472-1f3fe",sprite:"people"},{names:["man_with_gua_pi_mao_tone5","man_with_chinese_cap_tone5"],keywords:["dark skin tone","gua pi mao","hat","man"],unicode:"1f472-1f3ff",sprite:"people"},{names:["woman_wearing_turban"],keywords:["turban","woman"],unicode:"1f473-2640",sprite:"people"},{names:["woman_wearing_turban_light_skin_tone","woman_wearing_turban_tone1"],
+keywords:["light skin tone","turban","woman"],unicode:"1f473-1f3fb-2640",sprite:"people"},{names:["woman_wearing_turban_medium_light_skin_tone","woman_wearing_turban_tone2"],keywords:["medium-light skin tone","turban","woman"],unicode:"1f473-1f3fc-2640",sprite:"people"},{names:["woman_wearing_turban_medium_skin_tone","woman_wearing_turban_tone3"],keywords:["medium skin tone","turban","woman"],unicode:"1f473-1f3fd-2640",sprite:"people"},{names:["woman_wearing_turban_medium_dark_skin_tone","woman_wearing_turban_tone4"],
+keywords:["medium-dark skin tone","turban","woman"],unicode:"1f473-1f3fe-2640",sprite:"people"},{names:["woman_wearing_turban_dark_skin_tone","woman_wearing_turban_tone5"],keywords:["dark skin tone","turban","woman"],unicode:"1f473-1f3ff-2640",sprite:"people"},{names:["man_with_turban","person_wearing_turban"],keywords:["turban"],unicode:"1f473",sprite:"people"},{names:["man_with_turban_tone1","person_wearing_turban_tone1"],keywords:["light skin tone","turban"],unicode:"1f473-1f3fb",sprite:"people"},
+{names:["man_with_turban_tone2","person_wearing_turban_tone2"],keywords:["medium-light skin tone","turban"],unicode:"1f473-1f3fc",sprite:"people"},{names:["man_with_turban_tone3","person_wearing_turban_tone3"],keywords:["medium skin tone","turban"],unicode:"1f473-1f3fd",sprite:"people"},{names:["man_with_turban_tone4","person_wearing_turban_tone4"],keywords:["medium-dark skin tone","turban"],unicode:"1f473-1f3fe",sprite:"people"},{names:["man_with_turban_tone5","person_wearing_turban_tone5"],keywords:["dark skin tone",
+"turban"],unicode:"1f473-1f3ff",sprite:"people"},{names:["man_wearing_turban"],keywords:["man","turban"],unicode:"1f473-2642",sprite:"people"},{names:["man_wearing_turban_dark_skin_tone","man_wearing_turban_tone5"],keywords:["dark skin tone","man","turban"],unicode:"1f473-1f3ff-2642",sprite:"people"},{names:["man_wearing_turban_medium_dark_skin_tone","man_wearing_turban_tone4"],keywords:["man","medium-dark skin tone","turban"],unicode:"1f473-1f3fe-2642",sprite:"people"},{names:["man_wearing_turban_medium_skin_tone",
+"man_wearing_turban_tone3"],keywords:["man","medium skin tone","turban"],unicode:"1f473-1f3fd-2642",sprite:"people"},{names:["man_wearing_turban_medium_light_skin_tone","man_wearing_turban_tone2"],keywords:["man","medium-light skin tone","turban"],unicode:"1f473-1f3fc-2642",sprite:"people"},{names:["man_wearing_turban_light_skin_tone","man_wearing_turban_tone1"],keywords:["light skin tone","man","turban"],unicode:"1f473-1f3fb-2642",sprite:"people"},{names:["woman_police_officer"],keywords:["cop",
+"officer","police","woman"],unicode:"1f46e-2640",sprite:"people"},{names:["woman_police_officer_light_skin_tone","woman_police_officer_tone1"],keywords:["cop","light skin tone","officer","police","woman"],unicode:"1f46e-1f3fb-2640",sprite:"people"},{names:["woman_police_officer_medium_light_skin_tone","woman_police_officer_tone2"],keywords:["cop","medium-light skin tone","officer","police","woman"],unicode:"1f46e-1f3fc-2640",sprite:"people"},{names:["woman_police_officer_medium_skin_tone","woman_police_officer_tone3"],
+keywords:["cop","medium skin tone","officer","police","woman"],unicode:"1f46e-1f3fd-2640",sprite:"people"},{names:["woman_police_officer_medium_dark_skin_tone","woman_police_officer_tone4"],keywords:["cop","medium-dark skin tone","officer","police","woman"],unicode:"1f46e-1f3fe-2640",sprite:"people"},{names:["woman_police_officer_dark_skin_tone","woman_police_officer_tone5"],keywords:["cop","dark skin tone","officer","police","woman"],unicode:"1f46e-1f3ff-2640",sprite:"people"},{names:["cop","police_officer"],
+keywords:["cop","officer","police"],unicode:"1f46e",sprite:"people"},{names:["cop_tone1","police_officer_tone1"],keywords:["cop","light skin tone","officer","police"],unicode:"1f46e-1f3fb",sprite:"people"},{names:["cop_tone2","police_officer_tone2"],keywords:["cop","medium-light skin tone","officer","police"],unicode:"1f46e-1f3fc",sprite:"people"},{names:["cop_tone3","police_officer_tone3"],keywords:["cop","medium skin tone","officer","police"],unicode:"1f46e-1f3fd",sprite:"people"},{names:["cop_tone4",
+"police_officer_tone4"],keywords:["cop","medium-dark skin tone","officer","police"],unicode:"1f46e-1f3fe",sprite:"people"},{names:["cop_tone5","police_officer_tone5"],keywords:["cop","dark skin tone","officer","police"],unicode:"1f46e-1f3ff",sprite:"people"},{names:["man_police_officer"],keywords:["cop","man","officer","police"],unicode:"1f46e-2642",sprite:"people"},{names:["man_police_officer_dark_skin_tone","man_police_officer_tone5"],keywords:["cop","dark skin tone","man","officer","police"],unicode:"1f46e-1f3ff-2642",
+sprite:"people"},{names:["man_police_officer_medium_dark_skin_tone","man_police_officer_tone4"],keywords:["cop","man","medium-dark skin tone","officer","police"],unicode:"1f46e-1f3fe-2642",sprite:"people"},{names:["man_police_officer_medium_skin_tone","man_police_officer_tone3"],keywords:["cop","man","medium skin tone","officer","police"],unicode:"1f46e-1f3fd-2642",sprite:"people"},{names:["man_police_officer_medium_light_skin_tone","man_police_officer_tone2"],keywords:["cop","man","medium-light skin tone",
+"officer","police"],unicode:"1f46e-1f3fc-2642",sprite:"people"},{names:["man_police_officer_light_skin_tone","man_police_officer_tone1"],keywords:["cop","light skin tone","man","officer","police"],unicode:"1f46e-1f3fb-2642",sprite:"people"},{names:["woman_construction_worker"],keywords:["construction","woman","worker"],unicode:"1f477-2640",sprite:"people"},{names:["woman_construction_worker_light_skin_tone","woman_construction_worker_tone1"],keywords:["construction","light skin tone","woman","worker"],
+unicode:"1f477-1f3fb-2640",sprite:"people"},{names:["woman_construction_worker_medium_light_skin_tone","woman_construction_worker_tone2"],keywords:["construction","medium-light skin tone","woman","worker"],unicode:"1f477-1f3fc-2640",sprite:"people"},{names:["woman_construction_worker_medium_skin_tone","woman_construction_worker_tone3"],keywords:["construction","medium skin tone","woman","worker"],unicode:"1f477-1f3fd-2640",sprite:"people"},{names:["woman_construction_worker_medium_dark_skin_tone",
+"woman_construction_worker_tone4"],keywords:["construction","medium-dark skin tone","woman","worker"],unicode:"1f477-1f3fe-2640",sprite:"people"},{names:["woman_construction_worker_dark_skin_tone","woman_construction_worker_tone5"],keywords:["construction","dark skin tone","woman","worker"],unicode:"1f477-1f3ff-2640",sprite:"people"},{names:["construction_worker"],keywords:["construction","hat","worker"],unicode:"1f477",sprite:"people"},{names:["construction_worker_tone1"],keywords:["construction",
+"hat","light skin tone","worker"],unicode:"1f477-1f3fb",sprite:"people"},{names:["construction_worker_tone2"],keywords:["construction","hat","medium-light skin tone","worker"],unicode:"1f477-1f3fc",sprite:"people"},{names:["construction_worker_tone3"],keywords:["construction","hat","medium skin tone","worker"],unicode:"1f477-1f3fd",sprite:"people"},{names:["construction_worker_tone4"],keywords:["construction","hat","medium-dark skin tone","worker"],unicode:"1f477-1f3fe",sprite:"people"},{names:["construction_worker_tone5"],
+keywords:["construction","dark skin tone","hat","worker"],unicode:"1f477-1f3ff",sprite:"people"},{names:["man_construction_worker"],keywords:["construction","man","worker"],unicode:"1f477-2642",sprite:"people"},{names:["man_construction_worker_dark_skin_tone","man_construction_worker_tone5"],keywords:["construction","dark skin tone","man","worker"],unicode:"1f477-1f3ff-2642",sprite:"people"},{names:["man_construction_worker_medium_dark_skin_tone","man_construction_worker_tone4"],keywords:["construction",
+"man","medium-dark skin tone","worker"],unicode:"1f477-1f3fe-2642",sprite:"people"},{names:["man_construction_worker_medium_skin_tone","man_construction_worker_tone3"],keywords:["construction","man","medium skin tone","worker"],unicode:"1f477-1f3fd-2642",sprite:"people"},{names:["man_construction_worker_medium_light_skin_tone","man_construction_worker_tone2"],keywords:["construction","man","medium-light skin tone","worker"],unicode:"1f477-1f3fc-2642",sprite:"people"},{names:["man_construction_worker_light_skin_tone",
+"man_construction_worker_tone1"],keywords:["construction","light skin tone","man","worker"],unicode:"1f477-1f3fb-2642",sprite:"people"},{names:["woman_guard"],keywords:["guard","woman"],unicode:"1f482-2640",sprite:"people"},{names:["woman_guard_light_skin_tone","woman_guard_tone1"],keywords:["guard","light skin tone","woman"],unicode:"1f482-1f3fb-2640",sprite:"people"},{names:["woman_guard_medium_light_skin_tone","woman_guard_tone2"],keywords:["guard","medium-light skin tone","woman"],unicode:"1f482-1f3fc-2640",
+sprite:"people"},{names:["woman_guard_medium_skin_tone","woman_guard_tone3"],keywords:["guard","medium skin tone","woman"],unicode:"1f482-1f3fd-2640",sprite:"people"},{names:["woman_guard_medium_dark_skin_tone","woman_guard_tone4"],keywords:["guard","medium-dark skin tone","woman"],unicode:"1f482-1f3fe-2640",sprite:"people"},{names:["woman_guard_dark_skin_tone","woman_guard_tone5"],keywords:["dark skin tone","guard","woman"],unicode:"1f482-1f3ff-2640",sprite:"people"},{names:["guardsman","guard"],
+keywords:["guard"],unicode:"1f482",sprite:"people"},{names:["guardsman_tone1","guard_tone1"],keywords:["guard","light skin tone"],unicode:"1f482-1f3fb",sprite:"people"},{names:["guardsman_tone2","guard_tone2"],keywords:["guard","medium-light skin tone"],unicode:"1f482-1f3fc",sprite:"people"},{names:["guardsman_tone3","guard_tone3"],keywords:["guard","medium skin tone"],unicode:"1f482-1f3fd",sprite:"people"},{names:["guardsman_tone4","guard_tone4"],keywords:["guard","medium-dark skin tone"],unicode:"1f482-1f3fe",
+sprite:"people"},{names:["guardsman_tone5","guard_tone5"],keywords:["dark skin tone","guard"],unicode:"1f482-1f3ff",sprite:"people"},{names:["man_guard"],keywords:["guard","man"],unicode:"1f482-2642",sprite:"people"},{names:["man_guard_dark_skin_tone","man_guard_tone5"],keywords:["dark skin tone","guard","man"],unicode:"1f482-1f3ff-2642",sprite:"people"},{names:["man_guard_medium_dark_skin_tone","man_guard_tone4"],keywords:["guard","man","medium-dark skin tone"],unicode:"1f482-1f3fe-2642",sprite:"people"},
+{names:["man_guard_medium_skin_tone","man_guard_tone3"],keywords:["guard","man","medium skin tone"],unicode:"1f482-1f3fd-2642",sprite:"people"},{names:["man_guard_medium_light_skin_tone","man_guard_tone2"],keywords:["guard","man","medium-light skin tone"],unicode:"1f482-1f3fc-2642",sprite:"people"},{names:["man_guard_light_skin_tone","man_guard_tone1"],keywords:["guard","light skin tone","man"],unicode:"1f482-1f3fb-2642",sprite:"people"},{names:["woman_detective"],keywords:["detective","sleuth","spy",
+"woman"],unicode:"1f575-2640",sprite:"people"},{names:["woman_detective_light_skin_tone","woman_detective_tone1"],keywords:["detective","light skin tone","sleuth","spy","woman"],unicode:"1f575-1f3fb-2640",sprite:"people"},{names:["woman_detective_medium_light_skin_tone","woman_detective_tone2"],keywords:["detective","medium-light skin tone","sleuth","spy","woman"],unicode:"1f575-1f3fc-2640",sprite:"people"},{names:["woman_detective_medium_skin_tone","woman_detective_tone3"],keywords:["detective",
+"medium skin tone","sleuth","spy","woman"],unicode:"1f575-1f3fd-2640",sprite:"people"},{names:["woman_detective_medium_dark_skin_tone","woman_detective_tone4"],keywords:["detective","medium-dark skin tone","sleuth","spy","woman"],unicode:"1f575-1f3fe-2640",sprite:"people"},{names:["woman_detective_dark_skin_tone","woman_detective_tone5"],keywords:["dark skin tone","detective","sleuth","spy","woman"],unicode:"1f575-1f3ff-2640",sprite:"people"},{names:["spy","sleuth_or_spy","detective"],keywords:["detective",
+"sleuth","spy"],unicode:"1f575",sprite:"people"},{names:["spy_tone1","sleuth_or_spy_tone1","detective_tone1"],keywords:["detective","light skin tone","sleuth","spy"],unicode:"1f575-1f3fb",sprite:"people"},{names:["spy_tone2","sleuth_or_spy_tone2","detective_tone2"],keywords:["detective","medium-light skin tone","sleuth","spy"],unicode:"1f575-1f3fc",sprite:"people"},{names:["spy_tone3","sleuth_or_spy_tone3","detective_tone3"],keywords:["detective","medium skin tone","sleuth","spy"],unicode:"1f575-1f3fd",
+sprite:"people"},{names:["spy_tone4","sleuth_or_spy_tone4","detective_tone4"],keywords:["detective","medium-dark skin tone","sleuth","spy"],unicode:"1f575-1f3fe",sprite:"people"},{names:["spy_tone5","sleuth_or_spy_tone5","detective_tone5"],keywords:["dark skin tone","detective","sleuth","spy"],unicode:"1f575-1f3ff",sprite:"people"},{names:["man_detective"],keywords:["detective","man","sleuth","spy"],unicode:"1f575-2642",sprite:"people"},{names:["man_detective_dark_skin_tone","man_detective_tone5"],
+keywords:["dark skin tone","detective","man","sleuth","spy"],unicode:"1f575-1f3ff-2642",sprite:"people"},{names:["man_detective_medium_dark_skin_tone","man_detective_tone4"],keywords:["detective","man","medium-dark skin tone","sleuth","spy"],unicode:"1f575-1f3fe-2642",sprite:"people"},{names:["man_detective_medium_skin_tone","man_detective_tone3"],keywords:["detective","man","medium skin tone","sleuth","spy"],unicode:"1f575-1f3fd-2642",sprite:"people"},{names:["man_detective_medium_light_skin_tone",
+"man_detective_tone2"],keywords:["detective","man","medium-light skin tone","sleuth","spy"],unicode:"1f575-1f3fc-2642",sprite:"people"},{names:["man_detective_light_skin_tone","man_detective_tone1"],keywords:["detective","light skin tone","man","sleuth","spy"],unicode:"1f575-1f3fb-2642",sprite:"people"},{names:["woman_health_worker"],keywords:["doctor","healthcare","nurse","therapist","woman"],unicode:"1f469-2695",sprite:"people"},{names:["woman_health_worker_light_skin_tone","woman_health_worker_tone1"],
+keywords:"doctor;healthcare;light skin tone;nurse;therapist;woman".split(";"),unicode:"1f469-1f3fb-2695",sprite:"people"},{names:["woman_health_worker_medium_light_skin_tone","woman_health_worker_tone2"],keywords:"doctor;healthcare;medium-light skin tone;nurse;therapist;woman".split(";"),unicode:"1f469-1f3fc-2695",sprite:"people"},{names:["woman_health_worker_medium_skin_tone","woman_health_worker_tone3"],keywords:"doctor;healthcare;medium skin tone;nurse;therapist;woman".split(";"),unicode:"1f469-1f3fd-2695",
+sprite:"people"},{names:["woman_health_worker_medium_dark_skin_tone","woman_health_worker_tone4"],keywords:"doctor;healthcare;medium-dark skin tone;nurse;therapist;woman".split(";"),unicode:"1f469-1f3fe-2695",sprite:"people"},{names:["woman_health_worker_dark_skin_tone","woman_health_worker_tone5"],keywords:"dark skin tone;doctor;healthcare;nurse;therapist;woman".split(";"),unicode:"1f469-1f3ff-2695",sprite:"people"},{names:["man_health_worker"],keywords:["doctor","healthcare","man","nurse","therapist"],
+unicode:"1f468-2695",sprite:"people"},{names:["man_health_worker_light_skin_tone","man_health_worker_tone1"],keywords:"doctor;healthcare;light skin tone;man;nurse;therapist".split(";"),unicode:"1f468-1f3fb-2695",sprite:"people"},{names:["man_health_worker_medium_light_skin_tone","man_health_worker_tone2"],keywords:"doctor;healthcare;man;medium-light skin tone;nurse;therapist".split(";"),unicode:"1f468-1f3fc-2695",sprite:"people"},{names:["man_health_worker_medium_skin_tone","man_health_worker_tone3"],
+keywords:"doctor;healthcare;man;medium skin tone;nurse;therapist".split(";"),unicode:"1f468-1f3fd-2695",sprite:"people"},{names:["man_health_worker_medium_dark_skin_tone","man_health_worker_tone4"],keywords:"doctor;healthcare;man;medium-dark skin tone;nurse;therapist".split(";"),unicode:"1f468-1f3fe-2695",sprite:"people"},{names:["man_health_worker_dark_skin_tone","man_health_worker_tone5"],keywords:"dark skin tone;doctor;healthcare;man;nurse;therapist".split(";"),unicode:"1f468-1f3ff-2695",sprite:"people"},
+{names:["woman_farmer"],keywords:["farmer","gardener","rancher","woman"],unicode:"1f469-1f33e",sprite:"people"},{names:["woman_farmer_light_skin_tone","woman_farmer_tone1"],keywords:["farmer","gardener","light skin tone","rancher","woman"],unicode:"1f469-1f3fb-1f33e",sprite:"people"},{names:["woman_farmer_medium_light_skin_tone","woman_farmer_tone2"],keywords:["farmer","gardener","medium-light skin tone","rancher","woman"],unicode:"1f469-1f3fc-1f33e",sprite:"people"},{names:["woman_farmer_medium_skin_tone",
+"woman_farmer_tone3"],keywords:["farmer","gardener","medium skin tone","rancher","woman"],unicode:"1f469-1f3fd-1f33e",sprite:"people"},{names:["woman_farmer_medium_dark_skin_tone","woman_farmer_tone4"],keywords:["farmer","gardener","medium-dark skin tone","rancher","woman"],unicode:"1f469-1f3fe-1f33e",sprite:"people"},{names:["woman_farmer_dark_skin_tone","woman_farmer_tone5"],keywords:["dark skin tone","farmer","gardener","rancher","woman"],unicode:"1f469-1f3ff-1f33e",sprite:"people"},{names:["man_farmer"],
+keywords:["farmer","gardener","man","rancher"],unicode:"1f468-1f33e",sprite:"people"},{names:["man_farmer_light_skin_tone","man_farmer_tone1"],keywords:["farmer","gardener","light skin tone","man","rancher"],unicode:"1f468-1f3fb-1f33e",sprite:"people"},{names:["man_farmer_medium_light_skin_tone","man_farmer_tone2"],keywords:["farmer","gardener","man","medium-light skin tone","rancher"],unicode:"1f468-1f3fc-1f33e",sprite:"people"},{names:["man_farmer_medium_skin_tone","man_farmer_tone3"],keywords:["farmer",
+"gardener","man","medium skin tone","rancher"],unicode:"1f468-1f3fd-1f33e",sprite:"people"},{names:["man_farmer_medium_dark_skin_tone","man_farmer_tone4"],keywords:["farmer","gardener","man","medium-dark skin tone","rancher"],unicode:"1f468-1f3fe-1f33e",sprite:"people"},{names:["man_farmer_dark_skin_tone","man_farmer_tone5"],keywords:["dark skin tone","farmer","gardener","man","rancher"],unicode:"1f468-1f3ff-1f33e",sprite:"people"},{names:["woman_cook"],keywords:["chef","cook","woman"],unicode:"1f469-1f373",
+sprite:"people"},{names:["woman_cook_light_skin_tone","woman_cook_tone1"],keywords:["chef","cook","light skin tone","woman"],unicode:"1f469-1f3fb-1f373",sprite:"people"},{names:["woman_cook_medium_light_skin_tone","woman_cook_tone2"],keywords:["chef","cook","medium-light skin tone","woman"],unicode:"1f469-1f3fc-1f373",sprite:"people"},{names:["woman_cook_medium_skin_tone","woman_cook_tone3"],keywords:["chef","cook","medium skin tone","woman"],unicode:"1f469-1f3fd-1f373",sprite:"people"},{names:["woman_cook_medium_dark_skin_tone",
+"woman_cook_tone4"],keywords:["chef","cook","medium-dark skin tone","woman"],unicode:"1f469-1f3fe-1f373",sprite:"people"},{names:["woman_cook_dark_skin_tone","woman_cook_tone5"],keywords:["chef","cook","dark skin tone","woman"],unicode:"1f469-1f3ff-1f373",sprite:"people"},{names:["man_cook"],keywords:["chef","cook","man"],unicode:"1f468-1f373",sprite:"people"},{names:["man_cook_light_skin_tone","man_cook_tone1"],keywords:["chef","cook","light skin tone","man"],unicode:"1f468-1f3fb-1f373",sprite:"people"},
+{names:["man_cook_medium_light_skin_tone","man_cook_tone2"],keywords:["chef","cook","man","medium-light skin tone"],unicode:"1f468-1f3fc-1f373",sprite:"people"},{names:["man_cook_medium_skin_tone","man_cook_tone3"],keywords:["chef","cook","man","medium skin tone"],unicode:"1f468-1f3fd-1f373",sprite:"people"},{names:["man_cook_medium_dark_skin_tone","man_cook_tone4"],keywords:["chef","cook","man","medium-dark skin tone"],unicode:"1f468-1f3fe-1f373",sprite:"people"},{names:["man_cook_dark_skin_tone",
+"man_cook_tone5"],keywords:["chef","cook","dark skin tone","man"],unicode:"1f468-1f3ff-1f373",sprite:"people"},{names:["woman_student"],keywords:["graduate","student","woman"],unicode:"1f469-1f393",sprite:"people"},{names:["woman_student_light_skin_tone","woman_student_tone1"],keywords:["graduate","light skin tone","student","woman"],unicode:"1f469-1f3fb-1f393",sprite:"people"},{names:["woman_student_medium_light_skin_tone","woman_student_tone2"],keywords:["graduate","medium-light skin tone","student",
+"woman"],unicode:"1f469-1f3fc-1f393",sprite:"people"},{names:["woman_student_medium_skin_tone","woman_student_tone3"],keywords:["graduate","medium skin tone","student","woman"],unicode:"1f469-1f3fd-1f393",sprite:"people"},{names:["woman_student_medium_dark_skin_tone","woman_student_tone4"],keywords:["graduate","medium-dark skin tone","student","woman"],unicode:"1f469-1f3fe-1f393",sprite:"people"},{names:["woman_student_dark_skin_tone","woman_student_tone5"],keywords:["dark skin tone","graduate","student",
+"woman"],unicode:"1f469-1f3ff-1f393",sprite:"people"},{names:["man_student"],keywords:["graduate","man","student"],unicode:"1f468-1f393",sprite:"people"},{names:["man_student_light_skin_tone","man_student_tone1"],keywords:["graduate","light skin tone","man","student"],unicode:"1f468-1f3fb-1f393",sprite:"people"},{names:["man_student_medium_light_skin_tone","man_student_tone2"],keywords:["graduate","man","medium-light skin tone","student"],unicode:"1f468-1f3fc-1f393",sprite:"people"},{names:["man_student_medium_skin_tone",
+"man_student_tone3"],keywords:["graduate","man","medium skin tone","student"],unicode:"1f468-1f3fd-1f393",sprite:"people"},{names:["man_student_medium_dark_skin_tone","man_student_tone4"],keywords:["graduate","man","medium-dark skin tone","student"],unicode:"1f468-1f3fe-1f393",sprite:"people"},{names:["man_student_dark_skin_tone","man_student_tone5"],keywords:["dark skin tone","graduate","man","student"],unicode:"1f468-1f3ff-1f393",sprite:"people"},{names:["woman_singer"],keywords:"actor entertainer rock singer star woman".split(" "),
+unicode:"1f469-1f3a4",sprite:"people"},{names:["woman_singer_light_skin_tone","woman_singer_tone1"],keywords:"actor;entertainer;light skin tone;rock;singer;star;woman".split(";"),unicode:"1f469-1f3fb-1f3a4",sprite:"people"},{names:["woman_singer_medium_light_skin_tone","woman_singer_tone2"],keywords:"actor;entertainer;medium-light skin tone;rock;singer;star;woman".split(";"),unicode:"1f469-1f3fc-1f3a4",sprite:"people"},{names:["woman_singer_medium_skin_tone","woman_singer_tone3"],keywords:"actor;entertainer;medium skin tone;rock;singer;star;woman".split(";"),
+unicode:"1f469-1f3fd-1f3a4",sprite:"people"},{names:["woman_singer_medium_dark_skin_tone","woman_singer_tone4"],keywords:"actor;entertainer;medium-dark skin tone;rock;singer;star;woman".split(";"),unicode:"1f469-1f3fe-1f3a4",sprite:"people"},{names:["woman_singer_dark_skin_tone","woman_singer_tone5"],keywords:"actor;dark skin tone;entertainer;rock;singer;star;woman".split(";"),unicode:"1f469-1f3ff-1f3a4",sprite:"people"},{names:["man_singer"],keywords:"actor entertainer man rock singer star".split(" "),
+unicode:"1f468-1f3a4",sprite:"people"},{names:["man_singer_light_skin_tone","man_singer_tone1"],keywords:"actor;entertainer;light skin tone;man;rock;singer;star".split(";"),unicode:"1f468-1f3fb-1f3a4",sprite:"people"},{names:["man_singer_medium_light_skin_tone","man_singer_tone2"],keywords:"actor;entertainer;man;medium-light skin tone;rock;singer;star".split(";"),unicode:"1f468-1f3fc-1f3a4",sprite:"people"},{names:["man_singer_medium_skin_tone","man_singer_tone3"],keywords:"actor;entertainer;man;medium skin tone;rock;singer;star".split(";"),
+unicode:"1f468-1f3fd-1f3a4",sprite:"people"},{names:["man_singer_medium_dark_skin_tone","man_singer_tone4"],keywords:"actor;entertainer;man;medium-dark skin tone;rock;singer;star".split(";"),unicode:"1f468-1f3fe-1f3a4",sprite:"people"},{names:["man_singer_dark_skin_tone","man_singer_tone5"],keywords:"actor;dark skin tone;entertainer;man;rock;singer;star".split(";"),unicode:"1f468-1f3ff-1f3a4",sprite:"people"},{names:["woman_teacher"],keywords:["instructor","professor","teacher","woman"],unicode:"1f469-1f3eb",
+sprite:"people"},{names:["woman_teacher_light_skin_tone","woman_teacher_tone1"],keywords:["instructor","light skin tone","professor","teacher","woman"],unicode:"1f469-1f3fb-1f3eb",sprite:"people"},{names:["woman_teacher_medium_light_skin_tone","woman_teacher_tone2"],keywords:["instructor","medium-light skin tone","professor","teacher","woman"],unicode:"1f469-1f3fc-1f3eb",sprite:"people"},{names:["woman_teacher_medium_skin_tone","woman_teacher_tone3"],keywords:["instructor","medium skin tone","professor",
+"teacher","woman"],unicode:"1f469-1f3fd-1f3eb",sprite:"people"},{names:["woman_teacher_medium_dark_skin_tone","woman_teacher_tone4"],keywords:["instructor","medium-dark skin tone","professor","teacher","woman"],unicode:"1f469-1f3fe-1f3eb",sprite:"people"},{names:["woman_teacher_dark_skin_tone","woman_teacher_tone5"],keywords:["dark skin tone","instructor","professor","teacher","woman"],unicode:"1f469-1f3ff-1f3eb",sprite:"people"},{names:["man_teacher"],keywords:["instructor","man","professor","teacher"],
+unicode:"1f468-1f3eb",sprite:"people"},{names:["man_teacher_light_skin_tone","man_teacher_tone1"],keywords:["instructor","light skin tone","man","professor","teacher"],unicode:"1f468-1f3fb-1f3eb",sprite:"people"},{names:["man_teacher_medium_light_skin_tone","man_teacher_tone2"],keywords:["instructor","man","medium-light skin tone","professor","teacher"],unicode:"1f468-1f3fc-1f3eb",sprite:"people"},{names:["man_teacher_medium_skin_tone","man_teacher_tone3"],keywords:["instructor","man","medium skin tone",
+"professor","teacher"],unicode:"1f468-1f3fd-1f3eb",sprite:"people"},{names:["man_teacher_medium_dark_skin_tone","man_teacher_tone4"],keywords:["instructor","man","medium-dark skin tone","professor","teacher"],unicode:"1f468-1f3fe-1f3eb",sprite:"people"},{names:["man_teacher_dark_skin_tone","man_teacher_tone5"],keywords:["dark skin tone","instructor","man","professor","teacher"],unicode:"1f468-1f3ff-1f3eb",sprite:"people"},{names:["woman_factory_worker"],keywords:["assembly","factory","industrial",
+"woman","worker"],unicode:"1f469-1f3ed",sprite:"people"},{names:["woman_factory_worker_light_skin_tone","woman_factory_worker_tone1"],keywords:"assembly;factory;industrial;light skin tone;woman;worker".split(";"),unicode:"1f469-1f3fb-1f3ed",sprite:"people"},{names:["woman_factory_worker_medium_light_skin_tone","woman_factory_worker_tone2"],keywords:"assembly;factory;industrial;medium-light skin tone;woman;worker".split(";"),unicode:"1f469-1f3fc-1f3ed",sprite:"people"},{names:["woman_factory_worker_medium_skin_tone",
+"woman_factory_worker_tone3"],keywords:"assembly;factory;industrial;medium skin tone;woman;worker".split(";"),unicode:"1f469-1f3fd-1f3ed",sprite:"people"},{names:["woman_factory_worker_medium_dark_skin_tone","woman_factory_worker_tone4"],keywords:"assembly;factory;industrial;medium-dark skin tone;woman;worker".split(";"),unicode:"1f469-1f3fe-1f3ed",sprite:"people"},{names:["woman_factory_worker_dark_skin_tone","woman_factory_worker_tone5"],keywords:"assembly;dark skin tone;factory;industrial;woman;worker".split(";"),
+unicode:"1f469-1f3ff-1f3ed",sprite:"people"},{names:["man_factory_worker"],keywords:["assembly","factory","industrial","man","worker"],unicode:"1f468-1f3ed",sprite:"people"},{names:["man_factory_worker_light_skin_tone","man_factory_worker_tone1"],keywords:"assembly;factory;industrial;light skin tone;man;worker".split(";"),unicode:"1f468-1f3fb-1f3ed",sprite:"people"},{names:["man_factory_worker_medium_light_skin_tone","man_factory_worker_tone2"],keywords:"assembly;factory;industrial;man;medium-light skin tone;worker".split(";"),
+unicode:"1f468-1f3fc-1f3ed",sprite:"people"},{names:["man_factory_worker_medium_skin_tone","man_factory_worker_tone3"],keywords:"assembly;factory;industrial;man;medium skin tone;worker".split(";"),unicode:"1f468-1f3fd-1f3ed",sprite:"people"},{names:["man_factory_worker_medium_dark_skin_tone","man_factory_worker_tone4"],keywords:"assembly;factory;industrial;man;medium-dark skin tone;worker".split(";"),unicode:"1f468-1f3fe-1f3ed",sprite:"people"},{names:["man_factory_worker_dark_skin_tone","man_factory_worker_tone5"],
+keywords:"assembly;dark skin tone;factory;industrial;man;worker".split(";"),unicode:"1f468-1f3ff-1f3ed",sprite:"people"},{names:["woman_technologist"],keywords:"coder developer inventor software technologist woman".split(" "),unicode:"1f469-1f4bb",sprite:"people"},{names:["woman_technologist_light_skin_tone","woman_technologist_tone1"],keywords:"coder;developer;inventor;light skin tone;software;technologist;woman".split(";"),unicode:"1f469-1f3fb-1f4bb",sprite:"people"},{names:["woman_technologist_medium_light_skin_tone",
+"woman_technologist_tone2"],keywords:"coder;developer;inventor;medium-light skin tone;software;technologist;woman".split(";"),unicode:"1f469-1f3fc-1f4bb",sprite:"people"},{names:["woman_technologist_medium_skin_tone","woman_technologist_tone3"],keywords:"coder;developer;inventor;medium skin tone;software;technologist;woman".split(";"),unicode:"1f469-1f3fd-1f4bb",sprite:"people"},{names:["woman_technologist_medium_dark_skin_tone","woman_technologist_tone4"],keywords:"coder;developer;inventor;medium-dark skin tone;software;technologist;woman".split(";"),
+unicode:"1f469-1f3fe-1f4bb",sprite:"people"},{names:["woman_technologist_dark_skin_tone","woman_technologist_tone5"],keywords:"coder;dark skin tone;developer;inventor;software;technologist;woman".split(";"),unicode:"1f469-1f3ff-1f4bb",sprite:"people"},{names:["man_technologist"],keywords:"coder developer inventor man software technologist".split(" "),unicode:"1f468-1f4bb",sprite:"people"},{names:["man_technologist_light_skin_tone","man_technologist_tone1"],keywords:"coder;developer;inventor;light skin tone;man;software;technologist".split(";"),
+unicode:"1f468-1f3fb-1f4bb",sprite:"people"},{names:["man_technologist_medium_light_skin_tone","man_technologist_tone2"],keywords:"coder;developer;inventor;man;medium-light skin tone;software;technologist".split(";"),unicode:"1f468-1f3fc-1f4bb",sprite:"people"},{names:["man_technologist_medium_skin_tone","man_technologist_tone3"],keywords:"coder;developer;inventor;man;medium skin tone;software;technologist".split(";"),unicode:"1f468-1f3fd-1f4bb",sprite:"people"},{names:["man_technologist_medium_dark_skin_tone",
+"man_technologist_tone4"],keywords:"coder;developer;inventor;man;medium-dark skin tone;software;technologist".split(";"),unicode:"1f468-1f3fe-1f4bb",sprite:"people"},{names:["man_technologist_dark_skin_tone","man_technologist_tone5"],keywords:"coder;dark skin tone;developer;inventor;man;software;technologist".split(";"),unicode:"1f468-1f3ff-1f4bb",sprite:"people"},{names:["woman_office_worker"],keywords:"architect business manager office white-collar woman".split(" "),unicode:"1f469-1f4bc",sprite:"people"},
+{names:["woman_office_worker_light_skin_tone","woman_office_worker_tone1"],keywords:"architect;business;light skin tone;manager;office;white-collar;woman".split(";"),unicode:"1f469-1f3fb-1f4bc",sprite:"people"},{names:["woman_office_worker_medium_light_skin_tone","woman_office_worker_tone2"],keywords:"architect;business;manager;medium-light skin tone;office;white-collar;woman".split(";"),unicode:"1f469-1f3fc-1f4bc",sprite:"people"},{names:["woman_office_worker_medium_skin_tone","woman_office_worker_tone3"],
+keywords:"architect;business;manager;medium skin tone;office;white-collar;woman".split(";"),unicode:"1f469-1f3fd-1f4bc",sprite:"people"},{names:["woman_office_worker_medium_dark_skin_tone","woman_office_worker_tone4"],keywords:"architect;business;manager;medium-dark skin tone;office;white-collar;woman".split(";"),unicode:"1f469-1f3fe-1f4bc",sprite:"people"},{names:["woman_office_worker_dark_skin_tone","woman_office_worker_tone5"],keywords:"architect;business;dark skin tone;manager;office;white-collar;woman".split(";"),
+unicode:"1f469-1f3ff-1f4bc",sprite:"people"},{names:["man_office_worker"],keywords:"architect business man manager office white-collar".split(" "),unicode:"1f468-1f4bc",sprite:"people"},{names:["man_office_worker_light_skin_tone","man_office_worker_tone1"],keywords:"architect;business;light skin tone;man;manager;office;white-collar".split(";"),unicode:"1f468-1f3fb-1f4bc",sprite:"people"},{names:["man_office_worker_medium_light_skin_tone","man_office_worker_tone2"],keywords:"architect;business;man;manager;medium-light skin tone;office;white-collar".split(";"),
+unicode:"1f468-1f3fc-1f4bc",sprite:"people"},{names:["man_office_worker_medium_skin_tone","man_office_worker_tone3"],keywords:"architect;business;man;manager;medium skin tone;office;white-collar".split(";"),unicode:"1f468-1f3fd-1f4bc",sprite:"people"},{names:["man_office_worker_medium_dark_skin_tone","man_office_worker_tone4"],keywords:"architect;business;man;manager;medium-dark skin tone;office;white-collar".split(";"),unicode:"1f468-1f3fe-1f4bc",sprite:"people"},{names:["man_office_worker_dark_skin_tone",
+"man_office_worker_tone5"],keywords:"architect;business;dark skin tone;man;manager;office;white-collar".split(";"),unicode:"1f468-1f3ff-1f4bc",sprite:"people"},{names:["woman_mechanic"],keywords:["electrician","mechanic","plumber","tradesperson","woman"],unicode:"1f469-1f527",sprite:"people"},{names:["woman_mechanic_light_skin_tone","woman_mechanic_tone1"],keywords:"electrician;light skin tone;mechanic;plumber;tradesperson;woman".split(";"),unicode:"1f469-1f3fb-1f527",sprite:"people"},{names:["woman_mechanic_medium_light_skin_tone",
+"woman_mechanic_tone2"],keywords:"electrician;mechanic;medium-light skin tone;plumber;tradesperson;woman".split(";"),unicode:"1f469-1f3fc-1f527",sprite:"people"},{names:["woman_mechanic_medium_skin_tone","woman_mechanic_tone3"],keywords:"electrician;mechanic;medium skin tone;plumber;tradesperson;woman".split(";"),unicode:"1f469-1f3fd-1f527",sprite:"people"},{names:["woman_mechanic_medium_dark_skin_tone","woman_mechanic_tone4"],keywords:"electrician;mechanic;medium-dark skin tone;plumber;tradesperson;woman".split(";"),
+unicode:"1f469-1f3fe-1f527",sprite:"people"},{names:["woman_mechanic_dark_skin_tone","woman_mechanic_tone5"],keywords:"dark skin tone;electrician;mechanic;plumber;tradesperson;woman".split(";"),unicode:"1f469-1f3ff-1f527",sprite:"people"},{names:["man_mechanic"],keywords:["electrician","man","mechanic","plumber","tradesperson"],unicode:"1f468-1f527",sprite:"people"},{names:["man_mechanic_light_skin_tone","man_mechanic_tone1"],keywords:"electrician;light skin tone;man;mechanic;plumber;tradesperson".split(";"),
+unicode:"1f468-1f3fb-1f527",sprite:"people"},{names:["man_mechanic_medium_light_skin_tone","man_mechanic_tone2"],keywords:"electrician;man;mechanic;medium-light skin tone;plumber;tradesperson".split(";"),unicode:"1f468-1f3fc-1f527",sprite:"people"},{names:["man_mechanic_medium_skin_tone","man_mechanic_tone3"],keywords:"electrician;man;mechanic;medium skin tone;plumber;tradesperson".split(";"),unicode:"1f468-1f3fd-1f527",sprite:"people"},{names:["man_mechanic_medium_dark_skin_tone","man_mechanic_tone4"],
+keywords:"electrician;man;mechanic;medium-dark skin tone;plumber;tradesperson".split(";"),unicode:"1f468-1f3fe-1f527",sprite:"people"},{names:["man_mechanic_dark_skin_tone","man_mechanic_tone5"],keywords:"dark skin tone;electrician;man;mechanic;plumber;tradesperson".split(";"),unicode:"1f468-1f3ff-1f527",sprite:"people"},{names:["woman_scientist"],keywords:"biologist chemist engineer mathematician physicist scientist woman".split(" "),unicode:"1f469-1f52c",sprite:"people"},{names:["woman_scientist_light_skin_tone",
+"woman_scientist_tone1"],keywords:"biologist;chemist;engineer;light skin tone;mathematician;physicist;scientist;woman".split(";"),unicode:"1f469-1f3fb-1f52c",sprite:"people"},{names:["woman_scientist_medium_light_skin_tone","woman_scientist_tone2"],keywords:"biologist;chemist;engineer;mathematician;medium-light skin tone;physicist;scientist;woman".split(";"),unicode:"1f469-1f3fc-1f52c",sprite:"people"},{names:["woman_scientist_medium_skin_tone","woman_scientist_tone3"],keywords:"biologist;chemist;engineer;mathematician;medium skin tone;physicist;scientist;woman".split(";"),
+unicode:"1f469-1f3fd-1f52c",sprite:"people"},{names:["woman_scientist_medium_dark_skin_tone","woman_scientist_tone4"],keywords:"biologist;chemist;engineer;mathematician;medium-dark skin tone;physicist;scientist;woman".split(";"),unicode:"1f469-1f3fe-1f52c",sprite:"people"},{names:["woman_scientist_dark_skin_tone","woman_scientist_tone5"],keywords:"biologist;chemist;dark skin tone;engineer;mathematician;physicist;scientist;woman".split(";"),unicode:"1f469-1f3ff-1f52c",sprite:"people"},{names:["man_scientist"],
+keywords:"biologist chemist engineer man mathematician physicist scientist".split(" "),unicode:"1f468-1f52c",sprite:"people"},{names:["man_scientist_light_skin_tone","man_scientist_tone1"],keywords:"biologist;chemist;engineer;light skin tone;man;mathematician;physicist;scientist".split(";"),unicode:"1f468-1f3fb-1f52c",sprite:"people"},{names:["man_scientist_medium_light_skin_tone","man_scientist_tone2"],keywords:"biologist;chemist;engineer;man;mathematician;medium-light skin tone;physicist;scientist".split(";"),
+unicode:"1f468-1f3fc-1f52c",sprite:"people"},{names:["man_scientist_medium_skin_tone","man_scientist_tone3"],keywords:"biologist;chemist;engineer;man;mathematician;medium skin tone;physicist;scientist".split(";"),unicode:"1f468-1f3fd-1f52c",sprite:"people"},{names:["man_scientist_medium_dark_skin_tone","man_scientist_tone4"],keywords:"biologist;chemist;engineer;man;mathematician;medium-dark skin tone;physicist;scientist".split(";"),unicode:"1f468-1f3fe-1f52c",sprite:"people"},{names:["man_scientist_dark_skin_tone",
+"man_scientist_tone5"],keywords:"biologist;chemist;dark skin tone;engineer;man;mathematician;physicist;scientist".split(";"),unicode:"1f468-1f3ff-1f52c",sprite:"people"},{names:["woman_artist"],keywords:["artist","palette","woman"],unicode:"1f469-1f3a8",sprite:"people"},{names:["woman_artist_light_skin_tone","woman_artist_tone1"],keywords:["artist","light skin tone","palette","woman"],unicode:"1f469-1f3fb-1f3a8",sprite:"people"},{names:["woman_artist_medium_light_skin_tone","woman_artist_tone2"],
+keywords:["artist","medium-light skin tone","palette","woman"],unicode:"1f469-1f3fc-1f3a8",sprite:"people"},{names:["woman_artist_medium_skin_tone","woman_artist_tone3"],keywords:["artist","medium skin tone","palette","woman"],unicode:"1f469-1f3fd-1f3a8",sprite:"people"},{names:["woman_artist_medium_dark_skin_tone","woman_artist_tone4"],keywords:["artist","medium-dark skin tone","palette","woman"],unicode:"1f469-1f3fe-1f3a8",sprite:"people"},{names:["woman_artist_dark_skin_tone","woman_artist_tone5"],
+keywords:["artist","dark skin tone","palette","woman"],unicode:"1f469-1f3ff-1f3a8",sprite:"people"},{names:["man_artist"],keywords:["artist","man","palette"],unicode:"1f468-1f3a8",sprite:"people"},{names:["man_artist_light_skin_tone","man_artist_tone1"],keywords:["artist","light skin tone","man","palette"],unicode:"1f468-1f3fb-1f3a8",sprite:"people"},{names:["man_artist_medium_light_skin_tone","man_artist_tone2"],keywords:["artist","man","medium-light skin tone","palette"],unicode:"1f468-1f3fc-1f3a8",
+sprite:"people"},{names:["man_artist_medium_skin_tone","man_artist_tone3"],keywords:["artist","man","medium skin tone","palette"],unicode:"1f468-1f3fd-1f3a8",sprite:"people"},{names:["man_artist_medium_dark_skin_tone","man_artist_tone4"],keywords:["artist","man","medium-dark skin tone","palette"],unicode:"1f468-1f3fe-1f3a8",sprite:"people"},{names:["man_artist_dark_skin_tone","man_artist_tone5"],keywords:["artist","dark skin tone","man","palette"],unicode:"1f468-1f3ff-1f3a8",sprite:"people"},{names:["woman_firefighter"],
+keywords:["firefighter","firetruck","woman"],unicode:"1f469-1f692",sprite:"people"},{names:["woman_firefighter_light_skin_tone","woman_firefighter_tone1"],keywords:["firefighter","firetruck","light skin tone","woman"],unicode:"1f469-1f3fb-1f692",sprite:"people"},{names:["woman_firefighter_medium_light_skin_tone","woman_firefighter_tone2"],keywords:["firefighter","firetruck","medium-light skin tone","woman"],unicode:"1f469-1f3fc-1f692",sprite:"people"},{names:["woman_firefighter_medium_skin_tone",
+"woman_firefighter_tone3"],keywords:["firefighter","firetruck","medium skin tone","woman"],unicode:"1f469-1f3fd-1f692",sprite:"people"},{names:["woman_firefighter_medium_dark_skin_tone","woman_firefighter_tone4"],keywords:["firefighter","firetruck","medium-dark skin tone","woman"],unicode:"1f469-1f3fe-1f692",sprite:"people"},{names:["woman_firefighter_dark_skin_tone","woman_firefighter_tone5"],keywords:["dark skin tone","firefighter","firetruck","woman"],unicode:"1f469-1f3ff-1f692",sprite:"people"},
+{names:["man_firefighter"],keywords:["firefighter","firetruck","man"],unicode:"1f468-1f692",sprite:"people"},{names:["man_firefighter_light_skin_tone","man_firefighter_tone1"],keywords:["firefighter","firetruck","light skin tone","man"],unicode:"1f468-1f3fb-1f692",sprite:"people"},{names:["man_firefighter_medium_light_skin_tone","man_firefighter_tone2"],keywords:["firefighter","firetruck","man","medium-light skin tone"],unicode:"1f468-1f3fc-1f692",sprite:"people"},{names:["man_firefighter_medium_skin_tone",
+"man_firefighter_tone3"],keywords:["firefighter","firetruck","man","medium skin tone"],unicode:"1f468-1f3fd-1f692",sprite:"people"},{names:["man_firefighter_medium_dark_skin_tone","man_firefighter_tone4"],keywords:["firefighter","firetruck","man","medium-dark skin tone"],unicode:"1f468-1f3fe-1f692",sprite:"people"},{names:["man_firefighter_dark_skin_tone","man_firefighter_tone5"],keywords:["dark skin tone","firefighter","firetruck","man"],unicode:"1f468-1f3ff-1f692",sprite:"people"},{names:["woman_pilot"],
+keywords:["pilot","plane","woman"],unicode:"1f469-2708",sprite:"people"},{names:["woman_pilot_light_skin_tone","woman_pilot_tone1"],keywords:["light skin tone","pilot","plane","woman"],unicode:"1f469-1f3fb-2708",sprite:"people"},{names:["woman_pilot_medium_light_skin_tone","woman_pilot_tone2"],keywords:["medium-light skin tone","pilot","plane","woman"],unicode:"1f469-1f3fc-2708",sprite:"people"},{names:["woman_pilot_medium_skin_tone","woman_pilot_tone3"],keywords:["medium skin tone","pilot","plane",
+"woman"],unicode:"1f469-1f3fd-2708",sprite:"people"},{names:["woman_pilot_medium_dark_skin_tone","woman_pilot_tone4"],keywords:["medium-dark skin tone","pilot","plane","woman"],unicode:"1f469-1f3fe-2708",sprite:"people"},{names:["woman_pilot_dark_skin_tone","woman_pilot_tone5"],keywords:["dark skin tone","pilot","plane","woman"],unicode:"1f469-1f3ff-2708",sprite:"people"},{names:["man_pilot"],keywords:["man","pilot","plane"],unicode:"1f468-2708",sprite:"people"},{names:["man_pilot_light_skin_tone",
+"man_pilot_tone1"],keywords:["light skin tone","man","pilot","plane"],unicode:"1f468-1f3fb-2708",sprite:"people"},{names:["man_pilot_medium_light_skin_tone","man_pilot_tone2"],keywords:["man","medium-light skin tone","pilot","plane"],unicode:"1f468-1f3fc-2708",sprite:"people"},{names:["man_pilot_medium_skin_tone","man_pilot_tone3"],keywords:["man","medium skin tone","pilot","plane"],unicode:"1f468-1f3fd-2708",sprite:"people"},{names:["man_pilot_medium_dark_skin_tone","man_pilot_tone4"],keywords:["man",
+"medium-dark skin tone","pilot","plane"],unicode:"1f468-1f3fe-2708",sprite:"people"},{names:["man_pilot_dark_skin_tone","man_pilot_tone5"],keywords:["dark skin tone","man","pilot","plane"],unicode:"1f468-1f3ff-2708",sprite:"people"},{names:["woman_astronaut"],keywords:["astronaut","rocket","woman"],unicode:"1f469-1f680",sprite:"people"},{names:["woman_astronaut_light_skin_tone","woman_astronaut_tone1"],keywords:["astronaut","light skin tone","rocket","woman"],unicode:"1f469-1f3fb-1f680",sprite:"people"},
+{names:["woman_astronaut_medium_light_skin_tone","woman_astronaut_tone2"],keywords:["astronaut","medium-light skin tone","rocket","woman"],unicode:"1f469-1f3fc-1f680",sprite:"people"},{names:["woman_astronaut_medium_skin_tone","woman_astronaut_tone3"],keywords:["astronaut","medium skin tone","rocket","woman"],unicode:"1f469-1f3fd-1f680",sprite:"people"},{names:["woman_astronaut_medium_dark_skin_tone","woman_astronaut_tone4"],keywords:["astronaut","medium-dark skin tone","rocket","woman"],unicode:"1f469-1f3fe-1f680",
+sprite:"people"},{names:["woman_astronaut_dark_skin_tone","woman_astronaut_tone5"],keywords:["astronaut","dark skin tone","rocket","woman"],unicode:"1f469-1f3ff-1f680",sprite:"people"},{names:["man_astronaut"],keywords:["astronaut","man","rocket"],unicode:"1f468-1f680",sprite:"people"},{names:["man_astronaut_light_skin_tone","man_astronaut_tone1"],keywords:["astronaut","light skin tone","man","rocket"],unicode:"1f468-1f3fb-1f680",sprite:"people"},{names:["man_astronaut_medium_light_skin_tone","man_astronaut_tone2"],
+keywords:["astronaut","man","medium-light skin tone","rocket"],unicode:"1f468-1f3fc-1f680",sprite:"people"},{names:["man_astronaut_medium_skin_tone","man_astronaut_tone3"],keywords:["astronaut","man","medium skin tone","rocket"],unicode:"1f468-1f3fd-1f680",sprite:"people"},{names:["man_astronaut_medium_dark_skin_tone","man_astronaut_tone4"],keywords:["astronaut","man","medium-dark skin tone","rocket"],unicode:"1f468-1f3fe-1f680",sprite:"people"},{names:["man_astronaut_dark_skin_tone","man_astronaut_tone5"],
+keywords:["astronaut","dark skin tone","man","rocket"],unicode:"1f468-1f3ff-1f680",sprite:"people"},{names:["woman_judge"],keywords:["judge","scales","woman"],unicode:"1f469-2696",sprite:"people"},{names:["woman_judge_light_skin_tone","woman_judge_tone1"],keywords:["judge","light skin tone","scales","woman"],unicode:"1f469-1f3fb-2696",sprite:"people"},{names:["woman_judge_medium_light_skin_tone","woman_judge_tone2"],keywords:["judge","medium-light skin tone","scales","woman"],unicode:"1f469-1f3fc-2696",
+sprite:"people"},{names:["woman_judge_medium_skin_tone","woman_judge_tone3"],keywords:["judge","medium skin tone","scales","woman"],unicode:"1f469-1f3fd-2696",sprite:"people"},{names:["woman_judge_medium_dark_skin_tone","woman_judge_tone4"],keywords:["judge","medium-dark skin tone","scales","woman"],unicode:"1f469-1f3fe-2696",sprite:"people"},{names:["woman_judge_dark_skin_tone","woman_judge_tone5"],keywords:["dark skin tone","judge","scales","woman"],unicode:"1f469-1f3ff-2696",sprite:"people"},{names:["man_judge"],
+keywords:["justice","man","scales"],unicode:"1f468-2696",sprite:"people"},{names:["man_judge_light_skin_tone","man_judge_tone1"],keywords:["justice","light skin tone","man","scales"],unicode:"1f468-1f3fb-2696",sprite:"people"},{names:["man_judge_medium_light_skin_tone","man_judge_tone2"],keywords:["justice","man","medium-light skin tone","scales"],unicode:"1f468-1f3fc-2696",sprite:"people"},{names:["man_judge_medium_skin_tone","man_judge_tone3"],keywords:["justice","man","medium skin tone","scales"],
+unicode:"1f468-1f3fd-2696",sprite:"people"},{names:["man_judge_medium_dark_skin_tone","man_judge_tone4"],keywords:["justice","man","medium-dark skin tone","scales"],unicode:"1f468-1f3fe-2696",sprite:"people"},{names:["man_judge_dark_skin_tone","man_judge_tone5"],keywords:["dark skin tone","justice","man","scales"],unicode:"1f468-1f3ff-2696",sprite:"people"},{names:["mother_christmas","mrs_claus"],keywords:["Christmas","Mrs.","celebration","claus","mother"],unicode:"1f936",sprite:"people"},{names:["mother_christmas_tone1",
+"mrs_claus_tone1"],keywords:"Christmas;Mrs.;celebration;claus;light skin tone;mother".split(";"),unicode:"1f936-1f3fb",sprite:"people"},{names:["mother_christmas_tone2","mrs_claus_tone2"],keywords:"Christmas;Mrs.;celebration;claus;medium-light skin tone;mother".split(";"),unicode:"1f936-1f3fc",sprite:"people"},{names:["mother_christmas_tone3","mrs_claus_tone3"],keywords:"Christmas;Mrs.;celebration;claus;medium skin tone;mother".split(";"),unicode:"1f936-1f3fd",sprite:"people"},{names:["mother_christmas_tone4",
+"mrs_claus_tone4"],keywords:"Christmas;Mrs.;celebration;claus;medium-dark skin tone;mother".split(";"),unicode:"1f936-1f3fe",sprite:"people"},{names:["mother_christmas_tone5","mrs_claus_tone5"],keywords:"Christmas;Mrs.;celebration;claus;dark skin tone;mother".split(";"),unicode:"1f936-1f3ff",sprite:"people"},{names:["santa"],keywords:["Christmas","celebration","claus","father","santa"],unicode:"1f385",sprite:"people"},{names:["santa_tone1"],keywords:"Christmas;celebration;claus;father;light skin tone;santa".split(";"),
+unicode:"1f385-1f3fb",sprite:"people"},{names:["santa_tone2"],keywords:"Christmas;celebration;claus;father;medium-light skin tone;santa".split(";"),unicode:"1f385-1f3fc",sprite:"people"},{names:["santa_tone3"],keywords:"Christmas;celebration;claus;father;medium skin tone;santa".split(";"),unicode:"1f385-1f3fd",sprite:"people"},{names:["santa_tone4"],keywords:"Christmas;celebration;claus;father;medium-dark skin tone;santa".split(";"),unicode:"1f385-1f3fe",sprite:"people"},{names:["santa_tone5"],keywords:"Christmas;celebration;claus;dark skin tone;father;santa".split(";"),
+unicode:"1f385-1f3ff",sprite:"people"},{names:["princess"],keywords:["fairy tale","fantasy"],unicode:"1f478",sprite:"people"},{names:["princess_tone1"],keywords:["fairy tale","fantasy","light skin tone"],unicode:"1f478-1f3fb",sprite:"people"},{names:["princess_tone2"],keywords:["fairy tale","fantasy","medium-light skin tone"],unicode:"1f478-1f3fc",sprite:"people"},{names:["princess_tone3"],keywords:["fairy tale","fantasy","medium skin tone"],unicode:"1f478-1f3fd",sprite:"people"},{names:["princess_tone4"],
+keywords:["fairy tale","fantasy","medium-dark skin tone"],unicode:"1f478-1f3fe",sprite:"people"},{names:["princess_tone5"],keywords:["dark skin tone","fairy tale","fantasy"],unicode:"1f478-1f3ff",sprite:"people"},{names:["prince"],keywords:["prince"],unicode:"1f934",sprite:"people"},{names:["prince_tone1"],keywords:["light skin tone","prince"],unicode:"1f934-1f3fb",sprite:"people"},{names:["prince_tone2"],keywords:["medium-light skin tone","prince"],unicode:"1f934-1f3fc",sprite:"people"},{names:["prince_tone3"],
+keywords:["medium skin tone","prince"],unicode:"1f934-1f3fd",sprite:"people"},{names:["prince_tone4"],keywords:["medium-dark skin tone","prince"],unicode:"1f934-1f3fe",sprite:"people"},{names:["prince_tone5"],keywords:["dark skin tone","prince"],unicode:"1f934-1f3ff",sprite:"people"},{names:["bride_with_veil"],keywords:["bride","veil","wedding"],unicode:"1f470",sprite:"people"},{names:["bride_with_veil_tone1"],keywords:["bride","light skin tone","veil","wedding"],unicode:"1f470-1f3fb",sprite:"people"},
+{names:["bride_with_veil_tone2"],keywords:["bride","medium-light skin tone","veil","wedding"],unicode:"1f470-1f3fc",sprite:"people"},{names:["bride_with_veil_tone3"],keywords:["bride","medium skin tone","veil","wedding"],unicode:"1f470-1f3fd",sprite:"people"},{names:["bride_with_veil_tone4"],keywords:["bride","medium-dark skin tone","veil","wedding"],unicode:"1f470-1f3fe",sprite:"people"},{names:["bride_with_veil_tone5"],keywords:["bride","dark skin tone","veil","wedding"],unicode:"1f470-1f3ff",sprite:"people"},
+{names:["man_in_tuxedo"],keywords:["groom","man","tuxedo"],unicode:"1f935",sprite:"people"},{names:["tuxedo_tone1","man_in_tuxedo_tone1"],keywords:["groom","light skin tone","man","tuxedo"],unicode:"1f935-1f3fb",sprite:"people"},{names:["tuxedo_tone2","man_in_tuxedo_tone2"],keywords:["groom","man","medium-light skin tone","tuxedo"],unicode:"1f935-1f3fc",sprite:"people"},{names:["tuxedo_tone3","man_in_tuxedo_tone3"],keywords:["groom","man","medium skin tone","tuxedo"],unicode:"1f935-1f3fd",sprite:"people"},
+{names:["tuxedo_tone4","man_in_tuxedo_tone4"],keywords:["groom","man","medium-dark skin tone","tuxedo"],unicode:"1f935-1f3fe",sprite:"people"},{names:["tuxedo_tone5","man_in_tuxedo_tone5"],keywords:["dark skin tone","groom","man","tuxedo"],unicode:"1f935-1f3ff",sprite:"people"},{names:["angel"],keywords:["angel","baby","face","fairy tale","fantasy"],unicode:"1f47c",sprite:"people"},{names:["angel_tone1"],keywords:"angel;baby;face;fairy tale;fantasy;light skin tone".split(";"),unicode:"1f47c-1f3fb",
+sprite:"people"},{names:["angel_tone2"],keywords:"angel;baby;face;fairy tale;fantasy;medium-light skin tone".split(";"),unicode:"1f47c-1f3fc",sprite:"people"},{names:["angel_tone3"],keywords:"angel;baby;face;fairy tale;fantasy;medium skin tone".split(";"),unicode:"1f47c-1f3fd",sprite:"people"},{names:["angel_tone4"],keywords:"angel;baby;face;fairy tale;fantasy;medium-dark skin tone".split(";"),unicode:"1f47c-1f3fe",sprite:"people"},{names:["angel_tone5"],keywords:"angel;baby;dark skin tone;face;fairy tale;fantasy".split(";"),
+unicode:"1f47c-1f3ff",sprite:"people"},{names:["expecting_woman","pregnant_woman"],keywords:["pregnant","woman"],unicode:"1f930",sprite:"people"},{names:["expecting_woman_tone1","pregnant_woman_tone1"],keywords:["light skin tone","pregnant","woman"],unicode:"1f930-1f3fb",sprite:"people"},{names:["expecting_woman_tone2","pregnant_woman_tone2"],keywords:["medium-light skin tone","pregnant","woman"],unicode:"1f930-1f3fc",sprite:"people"},{names:["expecting_woman_tone3","pregnant_woman_tone3"],keywords:["medium skin tone",
+"pregnant","woman"],unicode:"1f930-1f3fd",sprite:"people"},{names:["expecting_woman_tone4","pregnant_woman_tone4"],keywords:["medium-dark skin tone","pregnant","woman"],unicode:"1f930-1f3fe",sprite:"people"},{names:["expecting_woman_tone5","pregnant_woman_tone5"],keywords:["dark skin tone","pregnant","woman"],unicode:"1f930-1f3ff",sprite:"people"},{names:["woman_bowing"],keywords:"apology bowing favor gesture sorry woman".split(" "),unicode:"1f647-2640",sprite:"people"},{names:["woman_bowing_light_skin_tone",
+"woman_bowing_tone1"],keywords:"apology;bowing;favor;gesture;light skin tone;sorry;woman".split(";"),unicode:"1f647-1f3fb-2640",sprite:"people"},{names:["woman_bowing_medium_light_skin_tone","woman_bowing_tone2"],keywords:"apology;bowing;favor;gesture;medium-light skin tone;sorry;woman".split(";"),unicode:"1f647-1f3fc-2640",sprite:"people"},{names:["woman_bowing_medium_skin_tone","woman_bowing_tone3"],keywords:"apology;bowing;favor;gesture;medium skin tone;sorry;woman".split(";"),unicode:"1f647-1f3fd-2640",
+sprite:"people"},{names:["woman_bowing_medium_dark_skin_tone","woman_bowing_tone4"],keywords:"apology;bowing;favor;gesture;medium-dark skin tone;sorry;woman".split(";"),unicode:"1f647-1f3fe-2640",sprite:"people"},{names:["woman_bowing_dark_skin_tone","woman_bowing_tone5"],keywords:"apology;bowing;dark skin tone;favor;gesture;sorry;woman".split(";"),unicode:"1f647-1f3ff-2640",sprite:"people"},{names:["bow","person_bowing"],keywords:["apology","bow","gesture","sorry"],unicode:"1f647",sprite:"people"},
+{names:["bow_tone1","person_bowing_tone1"],keywords:["apology","bow","gesture","light skin tone","sorry"],unicode:"1f647-1f3fb",sprite:"people"},{names:["bow_tone2","person_bowing_tone2"],keywords:["apology","bow","gesture","medium-light skin tone","sorry"],unicode:"1f647-1f3fc",sprite:"people"},{names:["bow_tone3","person_bowing_tone3"],keywords:["apology","bow","gesture","medium skin tone","sorry"],unicode:"1f647-1f3fd",sprite:"people"},{names:["bow_tone4","person_bowing_tone4"],keywords:["apology",
+"bow","gesture","medium-dark skin tone","sorry"],unicode:"1f647-1f3fe",sprite:"people"},{names:["bow_tone5","person_bowing_tone5"],keywords:["apology","bow","dark skin tone","gesture","sorry"],unicode:"1f647-1f3ff",sprite:"people"},{names:["man_bowing"],keywords:"apology bowing favor gesture man sorry".split(" "),unicode:"1f647-2642",sprite:"people"},{names:["man_bowing_dark_skin_tone","man_bowing_tone5"],keywords:"apology;bowing;dark skin tone;favor;gesture;man;sorry".split(";"),unicode:"1f647-1f3ff-2642",
+sprite:"people"},{names:["man_bowing_medium_dark_skin_tone","man_bowing_tone4"],keywords:"apology;bowing;favor;gesture;man;medium-dark skin tone;sorry".split(";"),unicode:"1f647-1f3fe-2642",sprite:"people"},{names:["man_bowing_medium_skin_tone","man_bowing_tone3"],keywords:"apology;bowing;favor;gesture;man;medium skin tone;sorry".split(";"),unicode:"1f647-1f3fd-2642",sprite:"people"},{names:["man_bowing_medium_light_skin_tone","man_bowing_tone2"],keywords:"apology;bowing;favor;gesture;man;medium-light skin tone;sorry".split(";"),
+unicode:"1f647-1f3fc-2642",sprite:"people"},{names:["man_bowing_light_skin_tone","man_bowing_tone1"],keywords:"apology;bowing;favor;gesture;light skin tone;man;sorry".split(";"),unicode:"1f647-1f3fb-2642",sprite:"people"},{names:["information_desk_person","person_tipping_hand"],keywords:["hand","help","information","sassy","tipping"],unicode:"1f481",sprite:"people"},{names:["information_desk_person_tone1","person_tipping_hand_tone1"],keywords:"hand;help;information;light skin tone;sassy;tipping".split(";"),
+unicode:"1f481-1f3fb",sprite:"people"},{names:["information_desk_person_tone2","person_tipping_hand_tone2"],keywords:"hand;help;information;medium-light skin tone;sassy;tipping".split(";"),unicode:"1f481-1f3fc",sprite:"people"},{names:["information_desk_person_tone3","person_tipping_hand_tone3"],keywords:"hand;help;information;medium skin tone;sassy;tipping".split(";"),unicode:"1f481-1f3fd",sprite:"people"},{names:["information_desk_person_tone4","person_tipping_hand_tone4"],keywords:"hand;help;information;medium-dark skin tone;sassy;tipping".split(";"),
+unicode:"1f481-1f3fe",sprite:"people"},{names:["information_desk_person_tone5","person_tipping_hand_tone5"],keywords:"dark skin tone;hand;help;information;sassy;tipping".split(";"),unicode:"1f481-1f3ff",sprite:"people"},{names:["man_tipping_hand"],keywords:["man","sassy","tipping hand"],unicode:"1f481-2642",sprite:"people"},{names:["man_tipping_hand_light_skin_tone","man_tipping_hand_tone1"],keywords:["light skin tone","man","sassy","tipping hand"],unicode:"1f481-1f3fb-2642",sprite:"people"},{names:["man_tipping_hand_medium_light_skin_tone",
+"man_tipping_hand_tone2"],keywords:["man","medium-light skin tone","sassy","tipping hand"],unicode:"1f481-1f3fc-2642",sprite:"people"},{names:["man_tipping_hand_medium_skin_tone","man_tipping_hand_tone3"],keywords:["man","medium skin tone","sassy","tipping hand"],unicode:"1f481-1f3fd-2642",sprite:"people"},{names:["man_tipping_hand_medium_dark_skin_tone","man_tipping_hand_tone4"],keywords:["man","medium-dark skin tone","sassy","tipping hand"],unicode:"1f481-1f3fe-2642",sprite:"people"},{names:["man_tipping_hand_dark_skin_tone",
+"man_tipping_hand_tone5"],keywords:["dark skin tone","man","sassy","tipping hand"],unicode:"1f481-1f3ff-2642",sprite:"people"},{names:["woman_tipping_hand"],keywords:["sassy","tipping hand","woman"],unicode:"1f481-2640",sprite:"people"},{names:["woman_tipping_hand_dark_skin_tone","woman_tipping_hand_tone5"],keywords:["dark skin tone","sassy","tipping hand","woman"],unicode:"1f481-1f3ff-2640",sprite:"people"},{names:["woman_tipping_hand_medium_dark_skin_tone","woman_tipping_hand_tone4"],keywords:["medium-dark skin tone",
+"sassy","tipping hand","woman"],unicode:"1f481-1f3fe-2640",sprite:"people"},{names:["woman_tipping_hand_medium_skin_tone","woman_tipping_hand_tone3"],keywords:["medium skin tone","sassy","tipping hand","woman"],unicode:"1f481-1f3fd-2640",sprite:"people"},{names:["woman_tipping_hand_medium_light_skin_tone","woman_tipping_hand_tone2"],keywords:["medium-light skin tone","sassy","tipping hand","woman"],unicode:"1f481-1f3fc-2640",sprite:"people"},{names:["woman_tipping_hand_light_skin_tone","woman_tipping_hand_tone1"],
+keywords:["light skin tone","sassy","tipping hand","woman"],unicode:"1f481-1f3fb-2640",sprite:"people"},{names:["no_good","person_gesturing_no"],keywords:"forbidden gesture hand no not prohibited".split(" "),unicode:"1f645",sprite:"people"},{names:["no_good_tone1","person_gesturing_no_tone1"],keywords:"forbidden;gesture;hand;light skin tone;no;not;prohibited".split(";"),unicode:"1f645-1f3fb",sprite:"people"},{names:["no_good_tone2","person_gesturing_no_tone2"],keywords:"forbidden;gesture;hand;medium-light skin tone;no;not;prohibited".split(";"),
+unicode:"1f645-1f3fc",sprite:"people"},{names:["no_good_tone3","person_gesturing_no_tone3"],keywords:"forbidden;gesture;hand;medium skin tone;no;not;prohibited".split(";"),unicode:"1f645-1f3fd",sprite:"people"},{names:["no_good_tone4","person_gesturing_no_tone4"],keywords:"forbidden;gesture;hand;medium-dark skin tone;no;not;prohibited".split(";"),unicode:"1f645-1f3fe",sprite:"people"},{names:["no_good_tone5","person_gesturing_no_tone5"],keywords:"dark skin tone;forbidden;gesture;hand;no;not;prohibited".split(";"),
+unicode:"1f645-1f3ff",sprite:"people"},{names:["man_gesturing_no"],keywords:"forbidden gesture hand man no prohibited".split(" "),unicode:"1f645-2642",sprite:"people"},{names:["man_gesturing_no_light_skin_tone","man_gesturing_no_tone1"],keywords:"forbidden;gesture;hand;light skin tone;man;no;prohibited".split(";"),unicode:"1f645-1f3fb-2642",sprite:"people"},{names:["man_gesturing_no_medium_light_skin_tone","man_gesturing_no_tone2"],keywords:"forbidden;gesture;hand;man;medium-light skin tone;no;prohibited".split(";"),
+unicode:"1f645-1f3fc-2642",sprite:"people"},{names:["man_gesturing_no_medium_skin_tone","man_gesturing_no_tone3"],keywords:"forbidden;gesture;hand;man;medium skin tone;no;prohibited".split(";"),unicode:"1f645-1f3fd-2642",sprite:"people"},{names:["man_gesturing_no_medium_dark_skin_tone","man_gesturing_no_tone4"],keywords:"forbidden;gesture;hand;man;medium-dark skin tone;no;prohibited".split(";"),unicode:"1f645-1f3fe-2642",sprite:"people"},{names:["man_gesturing_no_dark_skin_tone","man_gesturing_no_tone5"],
+keywords:"dark skin tone;forbidden;gesture;hand;man;no;prohibited".split(";"),unicode:"1f645-1f3ff-2642",sprite:"people"},{names:["woman_gesturing_no"],keywords:"forbidden gesture hand no prohibited woman".split(" "),unicode:"1f645-2640",sprite:"people"},{names:["woman_gesturing_no_dark_skin_tone","woman_gesturing_no_tone5"],keywords:"dark skin tone;forbidden;gesture;hand;no;prohibited;woman".split(";"),unicode:"1f645-1f3ff-2640",sprite:"people"},{names:["woman_gesturing_no_medium_dark_skin_tone",
+"woman_gesturing_no_tone4"],keywords:"forbidden;gesture;hand;medium-dark skin tone;no;prohibited;woman".split(";"),unicode:"1f645-1f3fe-2640",sprite:"people"},{names:["woman_gesturing_no_medium_skin_tone","woman_gesturing_no_tone3"],keywords:"forbidden;gesture;hand;medium skin tone;no;prohibited;woman".split(";"),unicode:"1f645-1f3fd-2640",sprite:"people"},{names:["woman_gesturing_no_medium_light_skin_tone","woman_gesturing_no_tone2"],keywords:"forbidden;gesture;hand;medium-light skin tone;no;prohibited;woman".split(";"),
+unicode:"1f645-1f3fc-2640",sprite:"people"},{names:["woman_gesturing_no_light_skin_tone","woman_gesturing_no_tone1"],keywords:"forbidden;gesture;hand;light skin tone;no;prohibited;woman".split(";"),unicode:"1f645-1f3fb-2640",sprite:"people"},{names:["ok_woman","person_gesturing_ok"],keywords:["OK","gesture","hand"],unicode:"1f646",sprite:"people"},{names:["ok_woman_tone1","person_gesturing_ok_tone1"],keywords:["OK","gesture","hand","light skin tone"],unicode:"1f646-1f3fb",sprite:"people"},{names:["ok_woman_tone2",
+"person_gesturing_ok_tone2"],keywords:["OK","gesture","hand","medium-light skin tone"],unicode:"1f646-1f3fc",sprite:"people"},{names:["ok_woman_tone3","person_gesturing_ok_tone3"],keywords:["OK","gesture","hand","medium skin tone"],unicode:"1f646-1f3fd",sprite:"people"},{names:["ok_woman_tone4","person_gesturing_ok_tone4"],keywords:["OK","gesture","hand","medium-dark skin tone"],unicode:"1f646-1f3fe",sprite:"people"},{names:["ok_woman_tone5","person_gesturing_ok_tone5"],keywords:["OK","dark skin tone",
+"gesture","hand"],unicode:"1f646-1f3ff",sprite:"people"},{names:["man_gesturing_ok"],keywords:["OK","gesture","hand","man"],unicode:"1f646-2642",sprite:"people"},{names:["man_gesturing_ok_light_skin_tone","man_gesturing_ok_tone1"],keywords:["OK","gesture","hand","light skin tone","man"],unicode:"1f646-1f3fb-2642",sprite:"people"},{names:["man_gesturing_ok_medium_light_skin_tone","man_gesturing_ok_tone2"],keywords:["OK","gesture","hand","man","medium-light skin tone"],unicode:"1f646-1f3fc-2642",sprite:"people"},
+{names:["man_gesturing_ok_medium_skin_tone","man_gesturing_ok_tone3"],keywords:["OK","gesture","hand","man","medium skin tone"],unicode:"1f646-1f3fd-2642",sprite:"people"},{names:["man_gesturing_ok_medium_dark_skin_tone","man_gesturing_ok_tone4"],keywords:["OK","gesture","hand","man","medium-dark skin tone"],unicode:"1f646-1f3fe-2642",sprite:"people"},{names:["man_gesturing_ok_dark_skin_tone","man_gesturing_ok_tone5"],keywords:["OK","dark skin tone","gesture","hand","man"],unicode:"1f646-1f3ff-2642",
+sprite:"people"},{names:["woman_gesturing_ok"],keywords:["OK","gesture","hand","woman"],unicode:"1f646-2640",sprite:"people"},{names:["woman_gesturing_ok_dark_skin_tone","woman_gesturing_ok_tone5"],keywords:["OK","dark skin tone","gesture","hand","woman"],unicode:"1f646-1f3ff-2640",sprite:"people"},{names:["woman_gesturing_ok_medium_dark_skin_tone","woman_gesturing_ok_tone4"],keywords:["OK","gesture","hand","medium-dark skin tone","woman"],unicode:"1f646-1f3fe-2640",sprite:"people"},{names:["woman_gesturing_ok_medium_skin_tone",
+"woman_gesturing_ok_tone3"],keywords:["OK","gesture","hand","medium skin tone","woman"],unicode:"1f646-1f3fd-2640",sprite:"people"},{names:["woman_gesturing_ok_medium_light_skin_tone","woman_gesturing_ok_tone2"],keywords:["OK","gesture","hand","medium-light skin tone","woman"],unicode:"1f646-1f3fc-2640",sprite:"people"},{names:["woman_gesturing_ok_light_skin_tone","woman_gesturing_ok_tone1"],keywords:["OK","gesture","hand","light skin tone","woman"],unicode:"1f646-1f3fb-2640",sprite:"people"},{names:["raising_hand",
+"person_raising_hand"],keywords:["gesture","hand","happy","raised"],unicode:"1f64b",sprite:"people"},{names:["raising_hand_tone1","person_raising_hand_tone1"],keywords:["gesture","hand","happy","light skin tone","raised"],unicode:"1f64b-1f3fb",sprite:"people"},{names:["raising_hand_tone2","person_raising_hand_tone2"],keywords:["gesture","hand","happy","medium-light skin tone","raised"],unicode:"1f64b-1f3fc",sprite:"people"},{names:["raising_hand_tone3","person_raising_hand_tone3"],keywords:["gesture",
+"hand","happy","medium skin tone","raised"],unicode:"1f64b-1f3fd",sprite:"people"},{names:["raising_hand_tone4","person_raising_hand_tone4"],keywords:["gesture","hand","happy","medium-dark skin tone","raised"],unicode:"1f64b-1f3fe",sprite:"people"},{names:["raising_hand_tone5","person_raising_hand_tone5"],keywords:["dark skin tone","gesture","hand","happy","raised"],unicode:"1f64b-1f3ff",sprite:"people"},{names:["man_raising_hand"],keywords:["gesture","man","raising hand"],unicode:"1f64b-2642",sprite:"people"},
+{names:["man_raising_hand_light_skin_tone","man_raising_hand_tone1"],keywords:["gesture","light skin tone","man","raising hand"],unicode:"1f64b-1f3fb-2642",sprite:"people"},{names:["man_raising_hand_medium_light_skin_tone","man_raising_hand_tone2"],keywords:["gesture","man","medium-light skin tone","raising hand"],unicode:"1f64b-1f3fc-2642",sprite:"people"},{names:["man_raising_hand_medium_skin_tone","man_raising_hand_tone3"],keywords:["gesture","man","medium skin tone","raising hand"],unicode:"1f64b-1f3fd-2642",
+sprite:"people"},{names:["man_raising_hand_medium_dark_skin_tone","man_raising_hand_tone4"],keywords:["gesture","man","medium-dark skin tone","raising hand"],unicode:"1f64b-1f3fe-2642",sprite:"people"},{names:["man_raising_hand_dark_skin_tone","man_raising_hand_tone5"],keywords:["dark skin tone","gesture","man","raising hand"],unicode:"1f64b-1f3ff-2642",sprite:"people"},{names:["woman_raising_hand"],keywords:["gesture","raising hand","woman"],unicode:"1f64b-2640",sprite:"people"},{names:["woman_raising_hand_dark_skin_tone",
+"woman_raising_hand_tone5"],keywords:["dark skin tone","gesture","raising hand","woman"],unicode:"1f64b-1f3ff-2640",sprite:"people"},{names:["woman_raising_hand_medium_dark_skin_tone","woman_raising_hand_tone4"],keywords:["gesture","medium-dark skin tone","raising hand","woman"],unicode:"1f64b-1f3fe-2640",sprite:"people"},{names:["woman_raising_hand_medium_skin_tone","woman_raising_hand_tone3"],keywords:["gesture","medium skin tone","raising hand","woman"],unicode:"1f64b-1f3fd-2640",sprite:"people"},
+{names:["woman_raising_hand_medium_light_skin_tone","woman_raising_hand_tone2"],keywords:["gesture","medium-light skin tone","raising hand","woman"],unicode:"1f64b-1f3fc-2640",sprite:"people"},{names:["woman_raising_hand_light_skin_tone","woman_raising_hand_tone1"],keywords:["gesture","light skin tone","raising hand","woman"],unicode:"1f64b-1f3fb-2640",sprite:"people"},{names:["woman_facepalming"],keywords:["disbelief","exasperation","facepalm","woman"],unicode:"1f926-2640",sprite:"people"},{names:["woman_facepalming_light_skin_tone",
+"woman_facepalming_tone1"],keywords:["disbelief","exasperation","facepalm","light skin tone","woman"],unicode:"1f926-1f3fb-2640",sprite:"people"},{names:["woman_facepalming_medium_light_skin_tone","woman_facepalming_tone2"],keywords:["disbelief","exasperation","facepalm","medium-light skin tone","woman"],unicode:"1f926-1f3fc-2640",sprite:"people"},{names:["woman_facepalming_medium_skin_tone","woman_facepalming_tone3"],keywords:["disbelief","exasperation","facepalm","medium skin tone","woman"],unicode:"1f926-1f3fd-2640",
+sprite:"people"},{names:["woman_facepalming_medium_dark_skin_tone","woman_facepalming_tone4"],keywords:["disbelief","exasperation","facepalm","medium-dark skin tone","woman"],unicode:"1f926-1f3fe-2640",sprite:"people"},{names:["woman_facepalming_dark_skin_tone","woman_facepalming_tone5"],keywords:["dark skin tone","disbelief","exasperation","facepalm","woman"],unicode:"1f926-1f3ff-2640",sprite:"people"},{names:["man_facepalming"],keywords:["disbelief","exasperation","facepalm","man"],unicode:"1f926-2642",
+sprite:"people"},{names:["man_facepalming_light_skin_tone","man_facepalming_tone1"],keywords:["disbelief","exasperation","facepalm","light skin tone","man"],unicode:"1f926-1f3fb-2642",sprite:"people"},{names:["man_facepalming_medium_light_skin_tone","man_facepalming_tone2"],keywords:["disbelief","exasperation","facepalm","man","medium-light skin tone"],unicode:"1f926-1f3fc-2642",sprite:"people"},{names:["man_facepalming_medium_skin_tone","man_facepalming_tone3"],keywords:["disbelief","exasperation",
+"facepalm","man","medium skin tone"],unicode:"1f926-1f3fd-2642",sprite:"people"},{names:["man_facepalming_medium_dark_skin_tone","man_facepalming_tone4"],keywords:["disbelief","exasperation","facepalm","man","medium-dark skin tone"],unicode:"1f926-1f3fe-2642",sprite:"people"},{names:["man_facepalming_dark_skin_tone","man_facepalming_tone5"],keywords:["dark skin tone","disbelief","exasperation","facepalm","man"],unicode:"1f926-1f3ff-2642",sprite:"people"},{names:["face_palm","facepalm","person_facepalming"],
+keywords:["disbelief","exasperation","face","palm"],unicode:"1f926",sprite:"people"},{names:["face_palm_tone1","facepalm_tone1","person_facepalming_tone1"],keywords:["disbelief","exasperation","face","light skin tone","palm"],unicode:"1f926-1f3fb",sprite:"people"},{names:["face_palm_tone2","facepalm_tone2","person_facepalming_tone2"],keywords:["disbelief","exasperation","face","medium-light skin tone","palm"],unicode:"1f926-1f3fc",sprite:"people"},{names:["face_palm_tone3","facepalm_tone3","person_facepalming_tone3"],
+keywords:["disbelief","exasperation","face","medium skin tone","palm"],unicode:"1f926-1f3fd",sprite:"people"},{names:["face_palm_tone4","facepalm_tone4","person_facepalming_tone4"],keywords:["disbelief","exasperation","face","medium-dark skin tone","palm"],unicode:"1f926-1f3fe",sprite:"people"},{names:["face_palm_tone5","facepalm_tone5","person_facepalming_tone5"],keywords:["dark skin tone","disbelief","exasperation","face","palm"],unicode:"1f926-1f3ff",sprite:"people"},{names:["woman_shrugging"],
+keywords:["doubt","ignorance","indifference","shrug","woman"],unicode:"1f937-2640",sprite:"people"},{names:["woman_shrugging_light_skin_tone","woman_shrugging_tone1"],keywords:"doubt;ignorance;indifference;light skin tone;shrug;woman".split(";"),unicode:"1f937-1f3fb-2640",sprite:"people"},{names:["woman_shrugging_medium_light_skin_tone","woman_shrugging_tone2"],keywords:"doubt;ignorance;indifference;medium-light skin tone;shrug;woman".split(";"),unicode:"1f937-1f3fc-2640",sprite:"people"},{names:["woman_shrugging_medium_skin_tone",
+"woman_shrugging_tone3"],keywords:"doubt;ignorance;indifference;medium skin tone;shrug;woman".split(";"),unicode:"1f937-1f3fd-2640",sprite:"people"},{names:["woman_shrugging_medium_dark_skin_tone","woman_shrugging_tone4"],keywords:"doubt;ignorance;indifference;medium-dark skin tone;shrug;woman".split(";"),unicode:"1f937-1f3fe-2640",sprite:"people"},{names:["woman_shrugging_dark_skin_tone","woman_shrugging_tone5"],keywords:"dark skin tone;doubt;ignorance;indifference;shrug;woman".split(";"),unicode:"1f937-1f3ff-2640",
+sprite:"people"},{names:["man_shrugging"],keywords:["doubt","ignorance","indifference","man","shrug"],unicode:"1f937-2642",sprite:"people"},{names:["man_shrugging_light_skin_tone","man_shrugging_tone1"],keywords:"doubt;ignorance;indifference;light skin tone;man;shrug".split(";"),unicode:"1f937-1f3fb-2642",sprite:"people"},{names:["man_shrugging_medium_light_skin_tone","man_shrugging_tone2"],keywords:"doubt;ignorance;indifference;man;medium-light skin tone;shrug".split(";"),unicode:"1f937-1f3fc-2642",
+sprite:"people"},{names:["man_shrugging_medium_skin_tone","man_shrugging_tone3"],keywords:"doubt;ignorance;indifference;man;medium skin tone;shrug".split(";"),unicode:"1f937-1f3fd-2642",sprite:"people"},{names:["man_shrugging_medium_dark_skin_tone","man_shrugging_tone4"],keywords:"doubt;ignorance;indifference;man;medium-dark skin tone;shrug".split(";"),unicode:"1f937-1f3fe-2642",sprite:"people"},{names:["man_shrugging_dark_skin_tone","man_shrugging_tone5"],keywords:"dark skin tone;doubt;ignorance;indifference;man;shrug".split(";"),
+unicode:"1f937-1f3ff-2642",sprite:"people"},{names:["shrug","person_shrugging"],keywords:["doubt","ignorance","indifference","shrug"],unicode:"1f937",sprite:"people"},{names:["shrug_tone1","person_shrugging_tone1"],keywords:["doubt","ignorance","indifference","light skin tone","shrug"],unicode:"1f937-1f3fb",sprite:"people"},{names:["shrug_tone2","person_shrugging_tone2"],keywords:["doubt","ignorance","indifference","medium-light skin tone","shrug"],unicode:"1f937-1f3fc",sprite:"people"},{names:["shrug_tone3",
+"person_shrugging_tone3"],keywords:["doubt","ignorance","indifference","medium skin tone","shrug"],unicode:"1f937-1f3fd",sprite:"people"},{names:["shrug_tone4","person_shrugging_tone4"],keywords:["doubt","ignorance","indifference","medium-dark skin tone","shrug"],unicode:"1f937-1f3fe",sprite:"people"},{names:["shrug_tone5","person_shrugging_tone5"],keywords:["dark skin tone","doubt","ignorance","indifference","shrug"],unicode:"1f937-1f3ff",sprite:"people"},{names:["person_with_pouting_face","person_pouting"],
+keywords:["gesture","pouting"],unicode:"1f64e",sprite:"people"},{names:["person_with_pouting_face_tone1","person_pouting_tone1"],keywords:["gesture","light skin tone","pouting"],unicode:"1f64e-1f3fb",sprite:"people"},{names:["person_with_pouting_face_tone2","person_pouting_tone2"],keywords:["gesture","medium-light skin tone","pouting"],unicode:"1f64e-1f3fc",sprite:"people"},{names:["person_with_pouting_face_tone3","person_pouting_tone3"],keywords:["gesture","medium skin tone","pouting"],unicode:"1f64e-1f3fd",
+sprite:"people"},{names:["person_with_pouting_face_tone4","person_pouting_tone4"],keywords:["gesture","medium-dark skin tone","pouting"],unicode:"1f64e-1f3fe",sprite:"people"},{names:["person_with_pouting_face_tone5","person_pouting_tone5"],keywords:["dark skin tone","gesture","pouting"],unicode:"1f64e-1f3ff",sprite:"people"},{names:["man_pouting"],keywords:["gesture","man","pouting"],unicode:"1f64e-2642",sprite:"people"},{names:["man_pouting_light_skin_tone","man_pouting_tone1"],keywords:["gesture",
+"light skin tone","man","pouting"],unicode:"1f64e-1f3fb-2642",sprite:"people"},{names:["man_pouting_medium_light_skin_tone","man_pouting_tone2"],keywords:["gesture","man","medium-light skin tone","pouting"],unicode:"1f64e-1f3fc-2642",sprite:"people"},{names:["man_pouting_medium_skin_tone","man_pouting_tone3"],keywords:["gesture","man","medium skin tone","pouting"],unicode:"1f64e-1f3fd-2642",sprite:"people"},{names:["man_pouting_medium_dark_skin_tone","man_pouting_tone4"],keywords:["gesture","man",
+"medium-dark skin tone","pouting"],unicode:"1f64e-1f3fe-2642",sprite:"people"},{names:["man_pouting_dark_skin_tone","man_pouting_tone5"],keywords:["dark skin tone","gesture","man","pouting"],unicode:"1f64e-1f3ff-2642",sprite:"people"},{names:["woman_pouting"],keywords:["gesture","pouting","woman"],unicode:"1f64e-2640",sprite:"people"},{names:["woman_pouting_dark_skin_tone","woman_pouting_tone5"],keywords:["dark skin tone","gesture","pouting","woman"],unicode:"1f64e-1f3ff-2640",sprite:"people"},{names:["woman_pouting_medium_dark_skin_tone",
+"woman_pouting_tone4"],keywords:["gesture","medium-dark skin tone","pouting","woman"],unicode:"1f64e-1f3fe-2640",sprite:"people"},{names:["woman_pouting_medium_skin_tone","woman_pouting_tone3"],keywords:["gesture","medium skin tone","pouting","woman"],unicode:"1f64e-1f3fd-2640",sprite:"people"},{names:["woman_pouting_medium_light_skin_tone","woman_pouting_tone2"],keywords:["gesture","medium-light skin tone","pouting","woman"],unicode:"1f64e-1f3fc-2640",sprite:"people"},{names:["woman_pouting_light_skin_tone",
+"woman_pouting_tone1"],keywords:["gesture","light skin tone","pouting","woman"],unicode:"1f64e-1f3fb-2640",sprite:"people"},{names:["person_frowning"],keywords:["frown","gesture"],unicode:"1f64d",sprite:"people"},{names:["person_frowning_tone1"],keywords:["frown","gesture","light skin tone"],unicode:"1f64d-1f3fb",sprite:"people"},{names:["person_frowning_tone2"],keywords:["frown","gesture","medium-light skin tone"],unicode:"1f64d-1f3fc",sprite:"people"},{names:["person_frowning_tone3"],keywords:["frown",
+"gesture","medium skin tone"],unicode:"1f64d-1f3fd",sprite:"people"},{names:["person_frowning_tone4"],keywords:["frown","gesture","medium-dark skin tone"],unicode:"1f64d-1f3fe",sprite:"people"},{names:["person_frowning_tone5"],keywords:["dark skin tone","frown","gesture"],unicode:"1f64d-1f3ff",sprite:"people"},{names:["man_frowning"],keywords:["frowning","gesture","man"],unicode:"1f64d-2642",sprite:"people"},{names:["man_frowning_light_skin_tone","man_frowning_tone1"],keywords:["frowning","gesture",
+"light skin tone","man"],unicode:"1f64d-1f3fb-2642",sprite:"people"},{names:["man_frowning_medium_light_skin_tone","man_frowning_tone2"],keywords:["frowning","gesture","man","medium-light skin tone"],unicode:"1f64d-1f3fc-2642",sprite:"people"},{names:["man_frowning_medium_skin_tone","man_frowning_tone3"],keywords:["frowning","gesture","man","medium skin tone"],unicode:"1f64d-1f3fd-2642",sprite:"people"},{names:["man_frowning_medium_dark_skin_tone","man_frowning_tone4"],keywords:["frowning","gesture",
+"man","medium-dark skin tone"],unicode:"1f64d-1f3fe-2642",sprite:"people"},{names:["man_frowning_dark_skin_tone","man_frowning_tone5"],keywords:["dark skin tone","frowning","gesture","man"],unicode:"1f64d-1f3ff-2642",sprite:"people"},{names:["woman_frowning"],keywords:["frowning","gesture","woman"],unicode:"1f64d-2640",sprite:"people"},{names:["woman_frowning_dark_skin_tone","woman_frowning_tone5"],keywords:["dark skin tone","frowning","gesture","woman"],unicode:"1f64d-1f3ff-2640",sprite:"people"},
+{names:["woman_frowning_medium_dark_skin_tone","woman_frowning_tone4"],keywords:["frowning","gesture","medium-dark skin tone","woman"],unicode:"1f64d-1f3fe-2640",sprite:"people"},{names:["woman_frowning_medium_skin_tone","woman_frowning_tone3"],keywords:["frowning","gesture","medium skin tone","woman"],unicode:"1f64d-1f3fd-2640",sprite:"people"},{names:["woman_frowning_medium_light_skin_tone","woman_frowning_tone2"],keywords:["frowning","gesture","medium-light skin tone","woman"],unicode:"1f64d-1f3fc-2640",
+sprite:"people"},{names:["woman_frowning_light_skin_tone","woman_frowning_tone1"],keywords:["frowning","gesture","light skin tone","woman"],unicode:"1f64d-1f3fb-2640",sprite:"people"},{names:["haircut","person_getting_haircut"],keywords:["barber","beauty","haircut","parlor"],unicode:"1f487",sprite:"people"},{names:["haircut_tone1","person_getting_haircut_tone1"],keywords:["barber","beauty","haircut","light skin tone","parlor"],unicode:"1f487-1f3fb",sprite:"people"},{names:["haircut_tone2","person_getting_haircut_tone2"],
+keywords:["barber","beauty","haircut","medium-light skin tone","parlor"],unicode:"1f487-1f3fc",sprite:"people"},{names:["haircut_tone3","person_getting_haircut_tone3"],keywords:["barber","beauty","haircut","medium skin tone","parlor"],unicode:"1f487-1f3fd",sprite:"people"},{names:["haircut_tone4","person_getting_haircut_tone4"],keywords:["barber","beauty","haircut","medium-dark skin tone","parlor"],unicode:"1f487-1f3fe",sprite:"people"},{names:["haircut_tone5","person_getting_haircut_tone5"],keywords:["barber",
+"beauty","dark skin tone","haircut","parlor"],unicode:"1f487-1f3ff",sprite:"people"},{names:["man_getting_haircut"],keywords:["haircut","man"],unicode:"1f487-2642",sprite:"people"},{names:["man_getting_haircut_light_skin_tone","man_getting_haircut_tone1"],keywords:["haircut","light skin tone","man"],unicode:"1f487-1f3fb-2642",sprite:"people"},{names:["man_getting_haircut_medium_light_skin_tone","man_getting_haircut_tone2"],keywords:["haircut","man","medium-light skin tone"],unicode:"1f487-1f3fc-2642",
+sprite:"people"},{names:["man_getting_haircut_medium_skin_tone","man_getting_haircut_tone3"],keywords:["haircut","man","medium skin tone"],unicode:"1f487-1f3fd-2642",sprite:"people"},{names:["man_getting_haircut_medium_dark_skin_tone","man_getting_haircut_tone4"],keywords:["haircut","man","medium-dark skin tone"],unicode:"1f487-1f3fe-2642",sprite:"people"},{names:["man_getting_haircut_dark_skin_tone","man_getting_haircut_tone5"],keywords:["dark skin tone","haircut","man"],unicode:"1f487-1f3ff-2642",
+sprite:"people"},{names:["woman_getting_haircut"],keywords:["haircut","woman"],unicode:"1f487-2640",sprite:"people"},{names:["woman_getting_haircut_dark_skin_tone","woman_getting_haircut_tone5"],keywords:["dark skin tone","haircut","woman"],unicode:"1f487-1f3ff-2640",sprite:"people"},{names:["woman_getting_haircut_medium_dark_skin_tone","woman_getting_haircut_tone4"],keywords:["haircut","medium-dark skin tone","woman"],unicode:"1f487-1f3fe-2640",sprite:"people"},{names:["woman_getting_haircut_medium_skin_tone",
+"woman_getting_haircut_tone3"],keywords:["haircut","medium skin tone","woman"],unicode:"1f487-1f3fd-2640",sprite:"people"},{names:["woman_getting_haircut_medium_light_skin_tone","woman_getting_haircut_tone2"],keywords:["haircut","medium-light skin tone","woman"],unicode:"1f487-1f3fc-2640",sprite:"people"},{names:["woman_getting_haircut_light_skin_tone","woman_getting_haircut_tone1"],keywords:["haircut","light skin tone","woman"],unicode:"1f487-1f3fb-2640",sprite:"people"},{names:["massage","person_getting_massage"],
+keywords:["face","massage","salon"],unicode:"1f486",sprite:"people"},{names:["massage_tone1","person_getting_massage_tone1"],keywords:["face","light skin tone","massage","salon"],unicode:"1f486-1f3fb",sprite:"people"},{names:["massage_tone2","person_getting_massage_tone2"],keywords:["face","massage","medium-light skin tone","salon"],unicode:"1f486-1f3fc",sprite:"people"},{names:["massage_tone3","person_getting_massage_tone3"],keywords:["face","massage","medium skin tone","salon"],unicode:"1f486-1f3fd",
+sprite:"people"},{names:["massage_tone4","person_getting_massage_tone4"],keywords:["face","massage","medium-dark skin tone","salon"],unicode:"1f486-1f3fe",sprite:"people"},{names:["massage_tone5","person_getting_massage_tone5"],keywords:["dark skin tone","face","massage","salon"],unicode:"1f486-1f3ff",sprite:"people"},{names:["man_getting_face_massage"],keywords:["face","man","massage"],unicode:"1f486-2642",sprite:"people"},{names:["man_getting_face_massage_light_skin_tone","man_getting_face_massage_tone1"],
+keywords:["face","light skin tone","man","massage"],unicode:"1f486-1f3fb-2642",sprite:"people"},{names:["man_getting_face_massage_medium_light_skin_tone","man_getting_face_massage_tone2"],keywords:["face","man","massage","medium-light skin tone"],unicode:"1f486-1f3fc-2642",sprite:"people"},{names:["man_getting_face_massage_medium_skin_tone","man_getting_face_massage_tone3"],keywords:["face","man","massage","medium skin tone"],unicode:"1f486-1f3fd-2642",sprite:"people"},{names:["man_getting_face_massage_medium_dark_skin_tone",
+"man_getting_face_massage_tone4"],keywords:["face","man","massage","medium-dark skin tone"],unicode:"1f486-1f3fe-2642",sprite:"people"},{names:["man_getting_face_massage_dark_skin_tone","man_getting_face_massage_tone5"],keywords:["dark skin tone","face","man","massage"],unicode:"1f486-1f3ff-2642",sprite:"people"},{names:["woman_getting_face_massage"],keywords:["face","massage","woman"],unicode:"1f486-2640",sprite:"people"},{names:["woman_getting_face_massage_dark_skin_tone","woman_getting_face_massage_tone5"],
+keywords:["dark skin tone","face","massage","woman"],unicode:"1f486-1f3ff-2640",sprite:"people"},{names:["woman_getting_face_massage_medium_dark_skin_tone","woman_getting_face_massage_tone4"],keywords:["face","massage","medium-dark skin tone","woman"],unicode:"1f486-1f3fe-2640",sprite:"people"},{names:["woman_getting_face_massage_medium_skin_tone","woman_getting_face_massage_tone3"],keywords:["face","massage","medium skin tone","woman"],unicode:"1f486-1f3fd-2640",sprite:"people"},{names:["woman_getting_face_massage_medium_light_skin_tone",
+"woman_getting_face_massage_tone2"],keywords:["face","massage","medium-light skin tone","woman"],unicode:"1f486-1f3fc-2640",sprite:"people"},{names:["woman_getting_face_massage_light_skin_tone","woman_getting_face_massage_tone1"],keywords:["face","light skin tone","massage","woman"],unicode:"1f486-1f3fb-2640",sprite:"people"},{names:["levitate"],keywords:["business","man","suit"],unicode:"1f574",sprite:"people"},{names:["man_in_business_suit_levitating_light_skin_tone","man_in_business_suit_levitating_tone1"],
+keywords:["business","light skin tone","man","suit"],unicode:"1f574-1f3fb",sprite:"people"},{names:["man_in_business_suit_levitating_medium_light_skin_tone","man_in_business_suit_levitating_tone2"],keywords:["business","man","medium-light skin tone","suit"],unicode:"1f574-1f3fc",sprite:"people"},{names:["man_in_business_suit_levitating_medium_skin_tone","man_in_business_suit_levitating_tone3"],keywords:["business","man","medium skin tone","suit"],unicode:"1f574-1f3fd",sprite:"people"},{names:["man_in_business_suit_levitating_medium_dark_skin_tone",
+"man_in_business_suit_levitating_tone4"],keywords:["business","man","medium-dark skin tone","suit"],unicode:"1f574-1f3fe",sprite:"people"},{names:["man_in_business_suit_levitating_dark_skin_tone","man_in_business_suit_levitating_tone5"],keywords:["business","dark skin tone","man","suit"],unicode:"1f574-1f3ff",sprite:"people"},{names:["dancer"],keywords:["dancing","woman"],unicode:"1f483",sprite:"people"},{names:["dancer_tone1"],keywords:["dancing","light skin tone","woman"],unicode:"1f483-1f3fb",
+sprite:"people"},{names:["dancer_tone2"],keywords:["dancing","medium-light skin tone","woman"],unicode:"1f483-1f3fc",sprite:"people"},{names:["dancer_tone3"],keywords:["dancing","medium skin tone","woman"],unicode:"1f483-1f3fd",sprite:"people"},{names:["dancer_tone4"],keywords:["dancing","medium-dark skin tone","woman"],unicode:"1f483-1f3fe",sprite:"people"},{names:["dancer_tone5"],keywords:["dancing","dark skin tone","woman"],unicode:"1f483-1f3ff",sprite:"people"},{names:["male_dancer","man_dancing"],
+keywords:["dance","man"],unicode:"1f57a",sprite:"people"},{names:["male_dancer_tone1","man_dancing_tone1"],keywords:["dance","light skin tone","man"],unicode:"1f57a-1f3fb",sprite:"people"},{names:["male_dancer_tone2","man_dancing_tone2"],keywords:["dance","man","medium-light skin tone"],unicode:"1f57a-1f3fc",sprite:"people"},{names:["male_dancer_tone3","man_dancing_tone3"],keywords:["dance","man","medium skin tone"],unicode:"1f57a-1f3fd",sprite:"people"},{names:["male_dancer_tone4","man_dancing_tone4"],
+keywords:["dance","man","medium-dark skin tone"],unicode:"1f57a-1f3fe",sprite:"people"},{names:["male_dancer_tone5","man_dancing_tone5"],keywords:["dance","dark skin tone","man"],unicode:"1f57a-1f3ff",sprite:"people"},{names:["dancers","people_with_bunny_ears_partying"],keywords:["bunny ear","dancer","partying"],unicode:"1f46f",sprite:"people"},{names:["men_with_bunny_ears_partying"],keywords:["bunny ear","dancer","men","partying"],unicode:"1f46f-2642",sprite:"people"},{names:["women_with_bunny_ears_partying"],
+keywords:["bunny ear","dancer","partying","women"],unicode:"1f46f-2640",sprite:"people"},{names:["woman_walking"],keywords:["hike","walk","woman"],unicode:"1f6b6-2640",sprite:"people"},{names:["woman_walking_light_skin_tone","woman_walking_tone1"],keywords:["hike","light skin tone","walk","woman"],unicode:"1f6b6-1f3fb-2640",sprite:"people"},{names:["woman_walking_medium_light_skin_tone","woman_walking_tone2"],keywords:["hike","medium-light skin tone","walk","woman"],unicode:"1f6b6-1f3fc-2640",sprite:"people"},
+{names:["woman_walking_medium_skin_tone","woman_walking_tone3"],keywords:["hike","medium skin tone","walk","woman"],unicode:"1f6b6-1f3fd-2640",sprite:"people"},{names:["woman_walking_medium_dark_skin_tone","woman_walking_tone4"],keywords:["hike","medium-dark skin tone","walk","woman"],unicode:"1f6b6-1f3fe-2640",sprite:"people"},{names:["woman_walking_dark_skin_tone","woman_walking_tone5"],keywords:["dark skin tone","hike","walk","woman"],unicode:"1f6b6-1f3ff-2640",sprite:"people"},{names:["walking",
+"person_walking"],keywords:["hike","walk","walking"],unicode:"1f6b6",sprite:"people"},{names:["walking_tone1","person_walking_tone1"],keywords:["hike","light skin tone","walk","walking"],unicode:"1f6b6-1f3fb",sprite:"people"},{names:["walking_tone2","person_walking_tone2"],keywords:["hike","medium-light skin tone","walk","walking"],unicode:"1f6b6-1f3fc",sprite:"people"},{names:["walking_tone3","person_walking_tone3"],keywords:["hike","medium skin tone","walk","walking"],unicode:"1f6b6-1f3fd",sprite:"people"},
+{names:["walking_tone4","person_walking_tone4"],keywords:["hike","medium-dark skin tone","walk","walking"],unicode:"1f6b6-1f3fe",sprite:"people"},{names:["walking_tone5","person_walking_tone5"],keywords:["dark skin tone","hike","walk","walking"],unicode:"1f6b6-1f3ff",sprite:"people"},{names:["man_walking"],keywords:["hike","man","walk"],unicode:"1f6b6-2642",sprite:"people"},{names:["man_walking_dark_skin_tone","man_walking_tone5"],keywords:["dark skin tone","hike","man","walk"],unicode:"1f6b6-1f3ff-2642",
+sprite:"people"},{names:["man_walking_medium_dark_skin_tone","man_walking_tone4"],keywords:["hike","man","medium-dark skin tone","walk"],unicode:"1f6b6-1f3fe-2642",sprite:"people"},{names:["man_walking_medium_skin_tone","man_walking_tone3"],keywords:["hike","man","medium skin tone","walk"],unicode:"1f6b6-1f3fd-2642",sprite:"people"},{names:["man_walking_medium_light_skin_tone","man_walking_tone2"],keywords:["hike","man","medium-light skin tone","walk"],unicode:"1f6b6-1f3fc-2642",sprite:"people"},
+{names:["man_walking_light_skin_tone","man_walking_tone1"],keywords:["hike","light skin tone","man","walk"],unicode:"1f6b6-1f3fb-2642",sprite:"people"},{names:["woman_running"],keywords:["marathon","racing","running","woman"],unicode:"1f3c3-2640",sprite:"people"},{names:["woman_running_light_skin_tone","woman_running_tone1"],keywords:["light skin tone","marathon","racing","running","woman"],unicode:"1f3c3-1f3fb-2640",sprite:"people"},{names:["woman_running_medium_light_skin_tone","woman_running_tone2"],
+keywords:["marathon","medium-light skin tone","racing","running","woman"],unicode:"1f3c3-1f3fc-2640",sprite:"people"},{names:["woman_running_medium_skin_tone","woman_running_tone3"],keywords:["marathon","medium skin tone","racing","running","woman"],unicode:"1f3c3-1f3fd-2640",sprite:"people"},{names:["woman_running_medium_dark_skin_tone","woman_running_tone4"],keywords:["marathon","medium-dark skin tone","racing","running","woman"],unicode:"1f3c3-1f3fe-2640",sprite:"people"},{names:["woman_running_dark_skin_tone",
+"woman_running_tone5"],keywords:["dark skin tone","marathon","racing","running","woman"],unicode:"1f3c3-1f3ff-2640",sprite:"people"},{names:["runner","person_running"],keywords:["marathon","running"],unicode:"1f3c3",sprite:"people"},{names:["runner_tone1","person_running_tone1"],keywords:["light skin tone","marathon","running"],unicode:"1f3c3-1f3fb",sprite:"people"},{names:["runner_tone2","person_running_tone2"],keywords:["marathon","medium-light skin tone","running"],unicode:"1f3c3-1f3fc",sprite:"people"},
+{names:["runner_tone3","person_running_tone3"],keywords:["marathon","medium skin tone","running"],unicode:"1f3c3-1f3fd",sprite:"people"},{names:["runner_tone4","person_running_tone4"],keywords:["marathon","medium-dark skin tone","running"],unicode:"1f3c3-1f3fe",sprite:"people"},{names:["runner_tone5","person_running_tone5"],keywords:["dark skin tone","marathon","running"],unicode:"1f3c3-1f3ff",sprite:"people"},{names:["man_running"],keywords:["man","marathon","racing","running"],unicode:"1f3c3-2642",
+sprite:"people"},{names:["man_running_dark_skin_tone","man_running_tone5"],keywords:["dark skin tone","man","marathon","racing","running"],unicode:"1f3c3-1f3ff-2642",sprite:"people"},{names:["man_running_medium_dark_skin_tone","man_running_tone4"],keywords:["man","marathon","medium-dark skin tone","racing","running"],unicode:"1f3c3-1f3fe-2642",sprite:"people"},{names:["man_running_medium_skin_tone","man_running_tone3"],keywords:["man","marathon","medium skin tone","racing","running"],unicode:"1f3c3-1f3fd-2642",
+sprite:"people"},{names:["man_running_medium_light_skin_tone","man_running_tone2"],keywords:["man","marathon","medium-light skin tone","racing","running"],unicode:"1f3c3-1f3fc-2642",sprite:"people"},{names:["man_running_light_skin_tone","man_running_tone1"],keywords:["light skin tone","man","marathon","racing","running"],unicode:"1f3c3-1f3fb-2642",sprite:"people"},{names:["couple"],keywords:["couple","hand","hold","man","woman"],unicode:"1f46b",sprite:"people"},{names:["two_women_holding_hands"],
+keywords:["couple","hand","hold","woman"],unicode:"1f46d",sprite:"people"},{names:["two_men_holding_hands"],keywords:"Gemini couple hand hold man twins zodiac".split(" "),unicode:"1f46c",sprite:"people"},{names:["couple_with_heart"],keywords:["couple","love"],unicode:"1f491",sprite:"people"},{names:["couple_with_heart_ww","couple_ww"],keywords:["couple","love","woman"],unicode:"1f469-2764-1f469",sprite:"people"},{names:["couple_with_heart_mm","couple_mm"],keywords:["couple","love","man"],unicode:"1f468-2764-1f468",
+sprite:"people"},{names:["couplekiss"],keywords:["couple"],unicode:"1f48f",sprite:"people"},{names:["couplekiss_ww","kiss_ww"],keywords:["couple","woman"],unicode:"1f469-2764-1f48b-1f469",sprite:"people"},{names:["couplekiss_mm","kiss_mm"],keywords:["couple","man"],unicode:"1f468-2764-1f48b-1f468",sprite:"people"},{names:["family"],keywords:["family"],unicode:"1f46a",sprite:"people"},{names:["family_mwg"],keywords:["family","girl","man","woman"],unicode:"1f468-1f469-1f467",sprite:"people"},{names:["family_mwgb"],
+keywords:["boy","family","girl","man","woman"],unicode:"1f468-1f469-1f467-1f466",sprite:"people"},{names:["family_mwbb"],keywords:["boy","family","man","woman"],unicode:"1f468-1f469-1f466-1f466",sprite:"people"},{names:["family_mwgg"],keywords:["family","girl","man","woman"],unicode:"1f468-1f469-1f467-1f467",sprite:"people"},{names:["family_wwb"],keywords:["boy","family","woman"],unicode:"1f469-1f469-1f466",sprite:"people"},{names:["family_wwg"],keywords:["family","girl","woman"],unicode:"1f469-1f469-1f467",
+sprite:"people"},{names:["family_wwgb"],keywords:["boy","family","girl","woman"],unicode:"1f469-1f469-1f467-1f466",sprite:"people"},{names:["family_wwbb"],keywords:["boy","family","woman"],unicode:"1f469-1f469-1f466-1f466",sprite:"people"},{names:["family_wwgg"],keywords:["family","girl","woman"],unicode:"1f469-1f469-1f467-1f467",sprite:"people"},{names:["family_mmb"],keywords:["boy","family","man"],unicode:"1f468-1f468-1f466",sprite:"people"},{names:["family_mmg"],keywords:["family","girl","man"],
+unicode:"1f468-1f468-1f467",sprite:"people"},{names:["family_mmgb"],keywords:["boy","family","girl","man"],unicode:"1f468-1f468-1f467-1f466",sprite:"people"},{names:["family_mmbb"],keywords:["boy","family","man"],unicode:"1f468-1f468-1f466-1f466",sprite:"people"},{names:["family_mmgg"],keywords:["family","girl","man"],unicode:"1f468-1f468-1f467-1f467",sprite:"people"},{names:["family_woman_boy"],keywords:["boy","family","woman"],unicode:"1f469-1f466",sprite:"people"},{names:["family_woman_girl"],
+keywords:["family","girl","woman"],unicode:"1f469-1f467",sprite:"people"},{names:["family_woman_girl_boy"],keywords:["boy","family","girl","woman"],unicode:"1f469-1f467-1f466",sprite:"people"},{names:["family_woman_boy_boy"],keywords:["boy","family","woman"],unicode:"1f469-1f466-1f466",sprite:"people"},{names:["family_woman_girl_girl"],keywords:["family","girl","woman"],unicode:"1f469-1f467-1f467",sprite:"people"},{names:["family_man_boy"],keywords:["boy","family","man"],unicode:"1f468-1f466",sprite:"people"},
+{names:["family_man_girl"],keywords:["family","girl","man"],unicode:"1f468-1f467",sprite:"people"},{names:["family_man_girl_boy"],keywords:["boy","family","girl","man"],unicode:"1f468-1f467-1f466",sprite:"people"},{names:["family_man_boy_boy"],keywords:["boy","family","man"],unicode:"1f468-1f466-1f466",sprite:"people"},{names:["family_man_girl_girl"],keywords:["family","girl","man"],unicode:"1f468-1f467-1f467",sprite:"people"},{names:["womans_clothes"],keywords:["clothing","woman"],unicode:"1f45a",
+sprite:"people"},{names:["shirt"],keywords:["clothing","shirt","tshirt"],unicode:"1f455",sprite:"people"},{names:["jeans"],keywords:["clothing","pants","trousers"],unicode:"1f456",sprite:"people"},{names:["necktie"],keywords:["clothing"],unicode:"1f454",sprite:"people"},{names:["dress"],keywords:["clothing"],unicode:"1f457",sprite:"people"},{names:["bikini"],keywords:["clothing","swim"],unicode:"1f459",sprite:"people"},{names:["kimono"],keywords:["clothing"],unicode:"1f458",sprite:"people"},{names:["high_heel"],
+keywords:["clothing","heel","shoe","woman"],unicode:"1f460",sprite:"people"},{names:["sandal"],keywords:["clothing","sandal","shoe","woman"],unicode:"1f461",sprite:"people"},{names:["boot"],keywords:["boot","clothing","shoe","woman"],unicode:"1f462",sprite:"people"},{names:["mans_shoe"],keywords:["clothing","man","shoe"],unicode:"1f45e",sprite:"people"},{names:["athletic_shoe"],keywords:["athletic","clothing","shoe","sneaker"],unicode:"1f45f",sprite:"people"},{names:["womans_hat"],keywords:["clothing",
+"hat","woman"],unicode:"1f452",sprite:"people"},{names:["tophat"],keywords:["clothing","hat","top","tophat"],unicode:"1f3a9",sprite:"people"},{names:["mortar_board"],keywords:["cap","celebration","clothing","graduation","hat"],unicode:"1f393",sprite:"people"},{names:["crown"],keywords:["clothing","king","queen"],unicode:"1f451",sprite:"people"},{names:["helmet_with_white_cross","helmet_with_cross"],keywords:["aid","cross","face","hat","helmet"],unicode:"26d1",sprite:"people"},{names:["school_satchel"],
+keywords:["bag","satchel","school"],unicode:"1f392",sprite:"people"},{names:["pouch"],keywords:["bag","clothing","pouch"],unicode:"1f45d",sprite:"people"},{names:["purse"],keywords:["clothing","coin"],unicode:"1f45b",sprite:"people"},{names:["handbag"],keywords:["bag","clothing","purse"],unicode:"1f45c",sprite:"people"},{names:["briefcase"],keywords:["briefcase"],unicode:"1f4bc",sprite:"people"},{names:["eyeglasses"],keywords:["clothing","eye","eyeglasses","eyewear"],unicode:"1f453",sprite:"people"},
+{names:["dark_sunglasses"],keywords:["dark","eye","eyewear","glasses"],unicode:"1f576",sprite:"people"},{names:["closed_umbrella"],keywords:["clothing","rain","umbrella"],unicode:"1f302",sprite:"people"},{names:["dog"],keywords:["dog","face","pet"],unicode:"1f436",sprite:"nature"},{names:["cat"],keywords:["cat","face","pet"],unicode:"1f431",sprite:"nature"},{names:["mouse"],keywords:["face","mouse"],unicode:"1f42d",sprite:"nature"},{names:["hamster"],keywords:["face","hamster","pet"],unicode:"1f439",
+sprite:"nature"},{names:["rabbit"],keywords:["bunny","face","pet","rabbit"],unicode:"1f430",sprite:"nature"},{names:["fox_face","fox"],keywords:["face","fox"],unicode:"1f98a",sprite:"nature"},{names:["bear"],keywords:["bear","face"],unicode:"1f43b",sprite:"nature"},{names:["panda_face"],keywords:["face","panda"],unicode:"1f43c",sprite:"nature"},{names:["koala"],keywords:["bear"],unicode:"1f428",sprite:"nature"},{names:["tiger"],keywords:["face","tiger"],unicode:"1f42f",sprite:"nature"},{names:["lion",
+"lion_face"],keywords:["Leo","face","lion","zodiac"],unicode:"1f981",sprite:"nature"},{names:["cow"],keywords:["cow","face"],unicode:"1f42e",sprite:"nature"},{names:["pig"],keywords:["face","pig"],unicode:"1f437",sprite:"nature"},{names:["pig_nose"],keywords:["face","nose","pig"],unicode:"1f43d",sprite:"nature"},{names:["frog"],keywords:["face","frog"],unicode:"1f438",sprite:"nature"},{names:["monkey_face"],keywords:["face","monkey"],unicode:"1f435",sprite:"nature"},{names:["see_no_evil"],keywords:"evil face forbidden gesture monkey no not prohibited see".split(" "),
+unicode:"1f648",sprite:"nature"},{names:["hear_no_evil"],keywords:"evil face forbidden gesture hear monkey no not prohibited".split(" "),unicode:"1f649",sprite:"nature"},{names:["speak_no_evil"],keywords:"evil face forbidden gesture monkey no not prohibited speak".split(" "),unicode:"1f64a",sprite:"nature"},{names:["monkey"],keywords:["monkey"],unicode:"1f412",sprite:"nature"},{names:["chicken"],keywords:["bird","chicken"],unicode:"1f414",sprite:"nature"},{names:["penguin"],keywords:["bird","penguin"],
+unicode:"1f427",sprite:"nature"},{names:["bird"],keywords:["bird"],unicode:"1f426",sprite:"nature"},{names:["baby_chick"],keywords:["baby","bird","chick"],unicode:"1f424",sprite:"nature"},{names:["hatching_chick"],keywords:["baby","bird","chick","hatching"],unicode:"1f423",sprite:"nature"},{names:["hatched_chick"],keywords:["baby","bird","chick"],unicode:"1f425",sprite:"nature"},{names:["duck"],keywords:["bird","duck"],unicode:"1f986",sprite:"nature"},{names:["eagle"],keywords:["bird","eagle"],unicode:"1f985",
+sprite:"nature"},{names:["owl"],keywords:["bird","owl","wise"],unicode:"1f989",sprite:"nature"},{names:["bat"],keywords:["bat","vampire"],unicode:"1f987",sprite:"nature"},{names:["wolf"],keywords:["face","wolf"],unicode:"1f43a",sprite:"nature"},{names:["boar"],keywords:["pig"],unicode:"1f417",sprite:"nature"},{names:["horse"],keywords:["face","horse"],unicode:"1f434",sprite:"nature"},{names:["unicorn_face","unicorn"],keywords:["face","unicorn"],unicode:"1f984",sprite:"nature"},{names:["bee"],keywords:["bee",
+"insect"],unicode:"1f41d",sprite:"nature"},{names:["bug"],keywords:["insect"],unicode:"1f41b",sprite:"nature"},{names:["butterfly"],keywords:["butterfly","insect","pretty"],unicode:"1f98b",sprite:"nature"},{names:["snail"],keywords:["snail"],unicode:"1f40c",sprite:"nature"},{names:["shell"],keywords:["shell","spiral"],unicode:"1f41a",sprite:"nature"},{names:["beetle"],keywords:["beetle","insect","ladybird","ladybug"],unicode:"1f41e",sprite:"nature"},{names:["ant"],keywords:["insect"],unicode:"1f41c",
+sprite:"nature"},{names:["spider"],keywords:["insect"],unicode:"1f577",sprite:"nature"},{names:["spider_web"],keywords:["spider","web"],unicode:"1f578",sprite:"nature"},{names:["turtle"],keywords:["terrapin","tortoise","turtle"],unicode:"1f422",sprite:"nature"},{names:["snake"],keywords:["Ophiuchus","bearer","serpent","zodiac"],unicode:"1f40d",sprite:"nature"},{names:["lizard"],keywords:["lizard","reptile"],unicode:"1f98e",sprite:"nature"},{names:["scorpion"],keywords:["Scorpius","scorpio","zodiac"],
+unicode:"1f982",sprite:"nature"},{names:["crab"],keywords:["Cancer","zodiac"],unicode:"1f980",sprite:"nature"},{names:["squid"],keywords:["food","molusc","squid"],unicode:"1f991",sprite:"nature"},{names:["octopus"],keywords:["octopus"],unicode:"1f419",sprite:"nature"},{names:["shrimp"],keywords:["food","shellfish","shrimp","small"],unicode:"1f990",sprite:"nature"},{names:["tropical_fish"],keywords:["fish","tropical"],unicode:"1f420",sprite:"nature"},{names:["fish"],keywords:["Pisces","zodiac"],unicode:"1f41f",
+sprite:"nature"},{names:["blowfish"],keywords:["fish"],unicode:"1f421",sprite:"nature"},{names:["dolphin"],keywords:["flipper"],unicode:"1f42c",sprite:"nature"},{names:["shark"],keywords:["fish","shark"],unicode:"1f988",sprite:"nature"},{names:["whale"],keywords:["face","spouting","whale"],unicode:"1f433",sprite:"nature"},{names:["whale2"],keywords:["whale"],unicode:"1f40b",sprite:"nature"},{names:["crocodile"],keywords:["crocodile"],unicode:"1f40a",sprite:"nature"},{names:["leopard"],keywords:["leopard"],
+unicode:"1f406",sprite:"nature"},{names:["tiger2"],keywords:["tiger"],unicode:"1f405",sprite:"nature"},{names:["water_buffalo"],keywords:["buffalo","water"],unicode:"1f403",sprite:"nature"},{names:["ox"],keywords:["Taurus","bull","zodiac"],unicode:"1f402",sprite:"nature"},{names:["cow2"],keywords:["cow"],unicode:"1f404",sprite:"nature"},{names:["deer"],keywords:["deer"],unicode:"1f98c",sprite:"nature"},{names:["dromedary_camel"],keywords:["dromedary","hump"],unicode:"1f42a",sprite:"nature"},{names:["camel"],
+keywords:["bactrian","camel","hump"],unicode:"1f42b",sprite:"nature"},{names:["elephant"],keywords:["elephant"],unicode:"1f418",sprite:"nature"},{names:["rhinoceros","rhino"],keywords:["rhinoceros"],unicode:"1f98f",sprite:"nature"},{names:["gorilla"],keywords:["gorilla"],unicode:"1f98d",sprite:"nature"},{names:["racehorse"],keywords:["equestrian","racehorse","racing"],unicode:"1f40e",sprite:"nature"},{names:["pig2"],keywords:["sow"],unicode:"1f416",sprite:"nature"},{names:["goat"],keywords:["Capricorn",
+"zodiac"],unicode:"1f410",sprite:"nature"},{names:["ram"],keywords:["Aries","male","sheep","zodiac"],unicode:"1f40f",sprite:"nature"},{names:["sheep"],keywords:["female","sheep"],unicode:"1f411",sprite:"nature"},{names:["dog2"],keywords:["pet"],unicode:"1f415",sprite:"nature"},{names:["poodle"],keywords:["dog"],unicode:"1f429",sprite:"nature"},{names:["cat2"],keywords:["pet"],unicode:"1f408",sprite:"nature"},{names:["rooster"],keywords:["bird","rooster"],unicode:"1f413",sprite:"nature"},{names:["turkey"],
+keywords:["bird","turkey"],unicode:"1f983",sprite:"nature"},{names:["dove_of_peace","dove"],keywords:["bird","fly","peace"],unicode:"1f54a",sprite:"nature"},{names:["rabbit2"],keywords:["bunny","pet"],unicode:"1f407",sprite:"nature"},{names:["mouse2"],keywords:["mouse"],unicode:"1f401",sprite:"nature"},{names:["rat"],keywords:["rat"],unicode:"1f400",sprite:"nature"},{names:["chipmunk"],keywords:["chipmunk"],unicode:"1f43f",sprite:"nature"},{names:["paw_prints","feet"],keywords:["feet","paw","print"],
+unicode:"1f43e",sprite:"nature"},{names:["dragon"],keywords:["fairy tale"],unicode:"1f409",sprite:"nature"},{names:["dragon_face"],keywords:["dragon","face","fairy tale"],unicode:"1f432",sprite:"nature"},{names:["cactus"],keywords:["plant"],unicode:"1f335",sprite:"nature"},{names:["christmas_tree"],keywords:["Christmas","celebration","tree"],unicode:"1f384",sprite:"nature"},{names:["evergreen_tree"],keywords:["tree"],unicode:"1f332",sprite:"nature"},{names:["deciduous_tree"],keywords:["deciduous",
+"shedding","tree"],unicode:"1f333",sprite:"nature"},{names:["palm_tree"],keywords:["palm","tree"],unicode:"1f334",sprite:"nature"},{names:["seedling"],keywords:["young"],unicode:"1f331",sprite:"nature"},{names:["herb"],keywords:["leaf"],unicode:"1f33f",sprite:"nature"},{names:["four_leaf_clover"],keywords:["4","clover","four","leaf"],unicode:"1f340",sprite:"nature"},{names:["bamboo"],keywords:["Japanese","bamboo","celebration","pine"],unicode:"1f38d",sprite:"nature"},{names:["tanabata_tree"],keywords:["Japanese",
+"banner","celebration","tree"],unicode:"1f38b",sprite:"nature"},{names:["leaves"],keywords:["blow","flutter","leaf","wind"],unicode:"1f343",sprite:"nature"},{names:["fallen_leaf"],keywords:["falling","leaf"],unicode:"1f342",sprite:"nature"},{names:["maple_leaf"],keywords:["falling","leaf","maple"],unicode:"1f341",sprite:"nature"},{names:["mushroom"],keywords:["toadstool"],unicode:"1f344",sprite:"nature"},{names:["ear_of_rice"],keywords:["ear","grain","rice"],unicode:"1f33e",sprite:"nature"},{names:["bouquet"],
+keywords:["flower"],unicode:"1f490",sprite:"nature"},{names:["tulip"],keywords:["flower"],unicode:"1f337",sprite:"nature"},{names:["rose"],keywords:["flower"],unicode:"1f339",sprite:"nature"},{names:["wilted_flower","wilted_rose"],keywords:["flower","wilted"],unicode:"1f940",sprite:"nature"},{names:["sunflower"],keywords:["flower","sun"],unicode:"1f33b",sprite:"nature"},{names:["blossom"],keywords:["flower"],unicode:"1f33c",sprite:"nature"},{names:["cherry_blossom"],keywords:["blossom","cherry","flower"],
+unicode:"1f338",sprite:"nature"},{names:["hibiscus"],keywords:["flower"],unicode:"1f33a",sprite:"nature"},{names:["earth_americas"],keywords:["Americas","earth","globe","world"],unicode:"1f30e",sprite:"nature"},{names:["earth_africa"],keywords:["Africa","Europe","earth","globe","world"],unicode:"1f30d",sprite:"nature"},{names:["earth_asia"],keywords:["Asia","Australia","earth","globe","world"],unicode:"1f30f",sprite:"nature"},{names:["full_moon"],keywords:["full","moon"],unicode:"1f315",sprite:"nature"},
+{names:["waning_gibbous_moon"],keywords:["gibbous","moon","waning"],unicode:"1f316",sprite:"nature"},{names:["last_quarter_moon"],keywords:["moon","quarter"],unicode:"1f317",sprite:"nature"},{names:["waning_crescent_moon"],keywords:["crescent","moon","waning"],unicode:"1f318",sprite:"nature"},{names:["new_moon"],keywords:["dark","moon"],unicode:"1f311",sprite:"nature"},{names:["waxing_crescent_moon"],keywords:["crescent","moon","waxing"],unicode:"1f312",sprite:"nature"},{names:["first_quarter_moon"],
+keywords:["moon","quarter"],unicode:"1f313",sprite:"nature"},{names:["waxing_gibbous_moon"],keywords:["gibbous","moon","waxing"],unicode:"1f314",sprite:"nature"},{names:["new_moon_with_face"],keywords:["face","moon"],unicode:"1f31a",sprite:"nature"},{names:["full_moon_with_face"],keywords:["bright","face","full","moon"],unicode:"1f31d",sprite:"nature"},{names:["sun_with_face"],keywords:["bright","face","sun"],unicode:"1f31e",sprite:"nature"},{names:["first_quarter_moon_with_face"],keywords:["face",
+"moon","quarter"],unicode:"1f31b",sprite:"nature"},{names:["last_quarter_moon_with_face"],keywords:["face","moon","quarter"],unicode:"1f31c",sprite:"nature"},{names:["crescent_moon"],keywords:["crescent","moon"],unicode:"1f319",sprite:"nature"},{names:["dizzy"],keywords:["comic","star"],unicode:"1f4ab",sprite:"nature"},{names:["star"],keywords:["star"],unicode:"2b50",sprite:"nature"},{names:["star2"],keywords:["glittery","glow","shining","sparkle","star"],unicode:"1f31f",sprite:"nature"},{names:["zap"],
+keywords:"danger electric electricity lightning voltage zap".split(" "),unicode:"26a1",sprite:"nature"},{names:["flame","fire"],keywords:["flame","tool"],unicode:"1f525",sprite:"nature"},{names:["boom"],keywords:["boom","comic"],unicode:"1f4a5",sprite:"nature"},{names:["white_sun_with_small_cloud","white_sun_small_cloud"],keywords:["cloud","sun"],unicode:"1f324",sprite:"nature"},{names:["partly_sunny"],keywords:["cloud","sun"],unicode:"26c5",sprite:"nature"},{names:["white_sun_behind_cloud","white_sun_cloud"],
+keywords:["cloud","sun"],unicode:"1f325",sprite:"nature"},{names:["white_sun_behind_cloud_with_rain","white_sun_rain_cloud"],keywords:["cloud","rain","sun"],unicode:"1f326",sprite:"nature"},{names:["rainbow"],keywords:["rain"],unicode:"1f308",sprite:"nature"},{names:["cloud_with_rain","cloud_rain"],keywords:["cloud","rain"],unicode:"1f327",sprite:"nature"},{names:["thunder_cloud_and_rain","thunder_cloud_rain"],keywords:["cloud","rain","thunder"],unicode:"26c8",sprite:"nature"},{names:["cloud_with_lightning",
+"cloud_lightning"],keywords:["cloud","lightning"],unicode:"1f329",sprite:"nature"},{names:["cloud_with_snow","cloud_snow"],keywords:["cloud","cold","snow"],unicode:"1f328",sprite:"nature"},{names:["snowman"],keywords:["cold","snow","snowman"],unicode:"26c4",sprite:"nature"},{names:["wind_blowing_face"],keywords:["blow","cloud","face","wind"],unicode:"1f32c",sprite:"nature"},{names:["dash"],keywords:["comic","dash","running"],unicode:"1f4a8",sprite:"nature"},{names:["cloud_with_tornado","cloud_tornado"],
+keywords:["cloud","whirlwind"],unicode:"1f32a",sprite:"nature"},{names:["fog"],keywords:["cloud"],unicode:"1f32b",sprite:"nature"},{names:["ocean"],keywords:["ocean","water","wave"],unicode:"1f30a",sprite:"nature"},{names:["droplet"],keywords:["cold","comic","drop","sweat"],unicode:"1f4a7",sprite:"nature"},{names:["sweat_drops"],keywords:["comic","splashing","sweat"],unicode:"1f4a6",sprite:"nature"},{names:["green_apple"],keywords:["apple","fruit","green"],unicode:"1f34f",sprite:"food"},{names:["apple"],
+keywords:["apple","fruit","red"],unicode:"1f34e",sprite:"food"},{names:["pear"],keywords:["fruit"],unicode:"1f350",sprite:"food"},{names:["tangerine"],keywords:["fruit","orange"],unicode:"1f34a",sprite:"food"},{names:["lemon"],keywords:["citrus","fruit"],unicode:"1f34b",sprite:"food"},{names:["banana"],keywords:["fruit"],unicode:"1f34c",sprite:"food"},{names:["watermelon"],keywords:["fruit"],unicode:"1f349",sprite:"food"},{names:["grapes"],keywords:["fruit","grape"],unicode:"1f347",sprite:"food"},
+{names:["strawberry"],keywords:["berry","fruit"],unicode:"1f353",sprite:"food"},{names:["melon"],keywords:["fruit"],unicode:"1f348",sprite:"food"},{names:["cherries"],keywords:["cherry","fruit"],unicode:"1f352",sprite:"food"},{names:["peach"],keywords:["fruit"],unicode:"1f351",sprite:"food"},{names:["pineapple"],keywords:["fruit"],unicode:"1f34d",sprite:"food"},{names:["kiwifruit","kiwi"],keywords:["food","fruit","kiwi"],unicode:"1f95d",sprite:"food"},{names:["avocado"],keywords:["avocado","food",
+"fruit"],unicode:"1f951",sprite:"food"},{names:["tomato"],keywords:["fruit","vegetable"],unicode:"1f345",sprite:"food"},{names:["eggplant"],keywords:["aubergine","vegetable"],unicode:"1f346",sprite:"food"},{names:["cucumber"],keywords:["cucumber","food","pickle","vegetable"],unicode:"1f952",sprite:"food"},{names:["carrot"],keywords:["carrot","food","vegetable"],unicode:"1f955",sprite:"food"},{names:["corn"],keywords:["corn","ear","maize","maze"],unicode:"1f33d",sprite:"food"},{names:["hot_pepper"],
+keywords:["hot","pepper"],unicode:"1f336",sprite:"food"},{names:["potato"],keywords:["food","potato","vegetable"],unicode:"1f954",sprite:"food"},{names:["sweet_potato"],keywords:["potato","roasted","sweet"],unicode:"1f360",sprite:"food"},{names:["chestnut"],keywords:["plant"],unicode:"1f330",sprite:"food"},{names:["shelled_peanut","peanuts"],keywords:["food","nut","peanut","vegetable"],unicode:"1f95c",sprite:"food"},{names:["honey_pot"],keywords:["honey","honeypot","pot","sweet"],unicode:"1f36f",
+sprite:"food"},{names:["croissant"],keywords:["bread","crescent roll","croissant","food","french"],unicode:"1f950",sprite:"food"},{names:["bread"],keywords:["loaf"],unicode:"1f35e",sprite:"food"},{names:["baguette_bread","french_bread"],keywords:["baguette","bread","food","french"],unicode:"1f956",sprite:"food"},{names:["cheese_wedge","cheese"],keywords:["cheese"],unicode:"1f9c0",sprite:"food"},{names:["egg"],keywords:["egg","food"],unicode:"1f95a",sprite:"food"},{names:["cooking"],keywords:["egg",
+"frying","pan"],unicode:"1f373",sprite:"food"},{names:["bacon"],keywords:["bacon","food","meat"],unicode:"1f953",sprite:"food"},{names:["pancakes"],keywords:["cr\u00eape","food","hotcake","pancake"],unicode:"1f95e",sprite:"food"},{names:["fried_shrimp"],keywords:["fried","prawn","shrimp","tempura"],unicode:"1f364",sprite:"food"},{names:["poultry_leg"],keywords:["bone","chicken","leg","poultry"],unicode:"1f357",sprite:"food"},{names:["meat_on_bone"],keywords:["bone","meat"],unicode:"1f356",sprite:"food"},
+{names:["pizza"],keywords:["cheese","slice"],unicode:"1f355",sprite:"food"},{names:["hot_dog","hotdog"],keywords:["frankfurter","hotdog","sausage"],unicode:"1f32d",sprite:"food"},{names:["hamburger"],keywords:["burger"],unicode:"1f354",sprite:"food"},{names:["fries"],keywords:["french","fries"],unicode:"1f35f",sprite:"food"},{names:["stuffed_pita","stuffed_flatbread"],keywords:"falafel flatbread food gyro kebab stuffed".split(" "),unicode:"1f959",sprite:"food"},{names:["taco"],keywords:["mexican"],
+unicode:"1f32e",sprite:"food"},{names:["burrito"],keywords:["mexican","wrap"],unicode:"1f32f",sprite:"food"},{names:["green_salad","salad"],keywords:["food","green","salad"],unicode:"1f957",sprite:"food"},{names:["paella","shallow_pan_of_food"],keywords:["casserole","food","paella","pan","shallow"],unicode:"1f958",sprite:"food"},{names:["spaghetti"],keywords:["pasta"],unicode:"1f35d",sprite:"food"},{names:["ramen"],keywords:["bowl","noodle","ramen","steaming"],unicode:"1f35c",sprite:"food"},{names:["stew"],
+keywords:["pot","stew"],unicode:"1f372",sprite:"food"},{names:["fish_cake"],keywords:["cake","fish","pastry","swirl"],unicode:"1f365",sprite:"food"},{names:["sushi"],keywords:["sushi"],unicode:"1f363",sprite:"food"},{names:["bento"],keywords:["bento","box"],unicode:"1f371",sprite:"food"},{names:["curry"],keywords:["curry","rice"],unicode:"1f35b",sprite:"food"},{names:["rice_ball"],keywords:["Japanese","ball","rice"],unicode:"1f359",sprite:"food"},{names:["rice"],keywords:["cooked","rice"],unicode:"1f35a",
+sprite:"food"},{names:["rice_cracker"],keywords:["cracker","rice"],unicode:"1f358",sprite:"food"},{names:["oden"],keywords:["kebab","seafood","skewer","stick"],unicode:"1f362",sprite:"food"},{names:["dango"],keywords:["Japanese","dessert","skewer","stick","sweet"],unicode:"1f361",sprite:"food"},{names:["shaved_ice"],keywords:["dessert","ice","shaved","sweet"],unicode:"1f367",sprite:"food"},{names:["ice_cream"],keywords:["cream","dessert","ice","sweet"],unicode:"1f368",sprite:"food"},{names:["icecream"],
+keywords:"cream dessert ice icecream soft sweet".split(" "),unicode:"1f366",sprite:"food"},{names:["cake"],keywords:["cake","dessert","pastry","slice","sweet"],unicode:"1f370",sprite:"food"},{names:["birthday"],keywords:"birthday cake celebration dessert pastry sweet".split(" "),unicode:"1f382",sprite:"food"},{names:["pudding","flan","custard"],keywords:["dessert","pudding","sweet"],unicode:"1f36e",sprite:"food"},{names:["lollipop"],keywords:["candy","dessert","sweet"],unicode:"1f36d",sprite:"food"},
+{names:["candy"],keywords:["dessert","sweet"],unicode:"1f36c",sprite:"food"},{names:["chocolate_bar"],keywords:["bar","chocolate","dessert","sweet"],unicode:"1f36b",sprite:"food"},{names:["popcorn"],keywords:["popcorn"],unicode:"1f37f",sprite:"food"},{names:["doughnut"],keywords:["dessert","donut","sweet"],unicode:"1f369",sprite:"food"},{names:["cookie"],keywords:["dessert","sweet"],unicode:"1f36a",sprite:"food"},{names:["glass_of_milk","milk"],keywords:["drink","glass","milk"],unicode:"1f95b",sprite:"food"},
+{names:["baby_bottle"],keywords:["baby","bottle","drink","milk"],unicode:"1f37c",sprite:"food"},{names:["tea"],keywords:["beverage","cup","drink","tea","teacup"],unicode:"1f375",sprite:"food"},{names:["sake"],keywords:["bar","beverage","bottle","cup","drink"],unicode:"1f376",sprite:"food"},{names:["beer"],keywords:["bar","beer","drink","mug"],unicode:"1f37a",sprite:"food"},{names:["beers"],keywords:["bar","beer","clink","drink","mug"],unicode:"1f37b",sprite:"food"},{names:["clinking_glass","champagne_glass"],
+keywords:["celebrate","clink","drink","glass"],unicode:"1f942",sprite:"food"},{names:["wine_glass"],keywords:["bar","beverage","drink","glass","wine"],unicode:"1f377",sprite:"food"},{names:["whisky","tumbler_glass"],keywords:["glass","liquor","shot","tumbler","whisky"],unicode:"1f943",sprite:"food"},{names:["cocktail"],keywords:["bar","cocktail","drink","glass"],unicode:"1f378",sprite:"food"},{names:["tropical_drink"],keywords:["bar","drink","tropical"],unicode:"1f379",sprite:"food"},{names:["bottle_with_popping_cork",
+"champagne"],keywords:["bar","bottle","cork","drink","popping"],unicode:"1f37e",sprite:"food"},{names:["spoon"],keywords:["spoon","tableware"],unicode:"1f944",sprite:"food"},{names:["fork_and_knife"],keywords:["cooking","fork","knife"],unicode:"1f374",sprite:"food"},{names:["fork_and_knife_with_plate","fork_knife_plate"],keywords:["cooking","fork","knife","plate"],unicode:"1f37d",sprite:"food"},{names:["soccer"],keywords:["ball","football","soccer"],unicode:"26bd",sprite:"activity"},{names:["basketball"],
+keywords:["ball","hoop"],unicode:"1f3c0",sprite:"activity"},{names:["football"],keywords:["american","ball","football"],unicode:"1f3c8",sprite:"activity"},{names:["baseball"],keywords:["ball"],unicode:"26be",sprite:"activity"},{names:["tennis"],keywords:["ball","racquet"],unicode:"1f3be",sprite:"activity"},{names:["volleyball"],keywords:["ball","game"],unicode:"1f3d0",sprite:"activity"},{names:["rugby_football"],keywords:["ball","football","rugby"],unicode:"1f3c9",sprite:"activity"},{names:["8ball"],
+keywords:"8;8 ball;ball;billiard;eight;game".split(";"),unicode:"1f3b1",sprite:"activity"},{names:["table_tennis","ping_pong"],keywords:"ball;bat;game;paddle;ping pong;table tennis".split(";"),unicode:"1f3d3",sprite:"activity"},{names:["badminton"],keywords:["birdie","game","racquet","shuttlecock"],unicode:"1f3f8",sprite:"activity"},{names:["goal_net","goal"],keywords:["goal","net"],unicode:"1f945",sprite:"activity"},{names:["hockey"],keywords:["game","hockey","ice","puck","stick"],unicode:"1f3d2",
+sprite:"activity"},{names:["field_hockey"],keywords:["ball","field","game","hockey","stick"],unicode:"1f3d1",sprite:"activity"},{names:["cricket_bat_ball","cricket"],keywords:["ball","bat","game"],unicode:"1f3cf",sprite:"activity"},{names:["golf"],keywords:["golf","hole"],unicode:"26f3",sprite:"activity"},{names:["archery","bow_and_arrow"],keywords:"Sagittarius archer archery arrow bow tool weapon zodiac".split(" "),unicode:"1f3f9",sprite:"activity"},{names:["fishing_pole_and_fish"],keywords:["fish",
+"pole"],unicode:"1f3a3",sprite:"activity"},{names:["boxing_gloves","boxing_glove"],keywords:["boxing","glove"],unicode:"1f94a",sprite:"activity"},{names:["karate_uniform","martial_arts_uniform"],keywords:["judo","karate","martial arts","taekwondo","uniform"],unicode:"1f94b",sprite:"activity"},{names:["ice_skate"],keywords:["ice","skate"],unicode:"26f8",sprite:"activity"},{names:["ski"],keywords:["ski","snow"],unicode:"1f3bf",sprite:"activity"},{names:["skier"],keywords:["ski","snow"],unicode:"26f7",
+sprite:"activity"},{names:["snowboarder"],keywords:["ski","snow","snowboard"],unicode:"1f3c2",sprite:"activity"},{names:["snowboarder_light_skin_tone","snowboarder_tone1"],keywords:["light skin tone","ski","snow","snowboard"],unicode:"1f3c2-1f3fb",sprite:"activity"},{names:["snowboarder_medium_light_skin_tone","snowboarder_tone2"],keywords:["medium-light skin tone","ski","snow","snowboard"],unicode:"1f3c2-1f3fc",sprite:"activity"},{names:["snowboarder_medium_skin_tone","snowboarder_tone3"],keywords:["medium skin tone",
+"ski","snow","snowboard"],unicode:"1f3c2-1f3fd",sprite:"activity"},{names:["snowboarder_medium_dark_skin_tone","snowboarder_tone4"],keywords:["medium-dark skin tone","ski","snow","snowboard"],unicode:"1f3c2-1f3fe",sprite:"activity"},{names:["snowboarder_dark_skin_tone","snowboarder_tone5"],keywords:["dark skin tone","ski","snow","snowboard"],unicode:"1f3c2-1f3ff",sprite:"activity"},{names:["woman_lifting_weights"],keywords:["weight lifter","woman"],unicode:"1f3cb-2640",sprite:"activity"},{names:["woman_lifting_weights_light_skin_tone",
+"woman_lifting_weights_tone1"],keywords:["light skin tone","weight lifter","woman"],unicode:"1f3cb-1f3fb-2640",sprite:"activity"},{names:["woman_lifting_weights_medium_light_skin_tone","woman_lifting_weights_tone2"],keywords:["medium-light skin tone","weight lifter","woman"],unicode:"1f3cb-1f3fc-2640",sprite:"activity"},{names:["woman_lifting_weights_medium_skin_tone","woman_lifting_weights_tone3"],keywords:["medium skin tone","weight lifter","woman"],unicode:"1f3cb-1f3fd-2640",sprite:"activity"},
+{names:["woman_lifting_weights_medium_dark_skin_tone","woman_lifting_weights_tone4"],keywords:["medium-dark skin tone","weight lifter","woman"],unicode:"1f3cb-1f3fe-2640",sprite:"activity"},{names:["woman_lifting_weights_dark_skin_tone","woman_lifting_weights_tone5"],keywords:["dark skin tone","weight lifter","woman"],unicode:"1f3cb-1f3ff-2640",sprite:"activity"},{names:["lifter","weight_lifter","person_lifting_weights"],keywords:["lifter","weight"],unicode:"1f3cb",sprite:"activity"},{names:["lifter_tone1",
+"weight_lifter_tone1","person_lifting_weights_tone1"],keywords:["lifter","light skin tone","weight"],unicode:"1f3cb-1f3fb",sprite:"activity"},{names:["lifter_tone2","weight_lifter_tone2","person_lifting_weights_tone2"],keywords:["lifter","medium-light skin tone","weight"],unicode:"1f3cb-1f3fc",sprite:"activity"},{names:["lifter_tone3","weight_lifter_tone3","person_lifting_weights_tone3"],keywords:["lifter","medium skin tone","weight"],unicode:"1f3cb-1f3fd",sprite:"activity"},{names:["lifter_tone4",
+"weight_lifter_tone4","person_lifting_weights_tone4"],keywords:["lifter","medium-dark skin tone","weight"],unicode:"1f3cb-1f3fe",sprite:"activity"},{names:["lifter_tone5","weight_lifter_tone5","person_lifting_weights_tone5"],keywords:["dark skin tone","lifter","weight"],unicode:"1f3cb-1f3ff",sprite:"activity"},{names:["man_lifting_weights"],keywords:["man","weight lifter"],unicode:"1f3cb-2642",sprite:"activity"},{names:["man_lifting_weights_dark_skin_tone","man_lifting_weights_tone5"],keywords:["dark skin tone",
+"man","weight lifter"],unicode:"1f3cb-1f3ff-2642",sprite:"activity"},{names:["man_lifting_weights_medium_dark_skin_tone","man_lifting_weights_tone4"],keywords:["man","medium-dark skin tone","weight lifter"],unicode:"1f3cb-1f3fe-2642",sprite:"activity"},{names:["man_lifting_weights_medium_skin_tone","man_lifting_weights_tone3"],keywords:["man","medium skin tone","weight lifter"],unicode:"1f3cb-1f3fd-2642",sprite:"activity"},{names:["man_lifting_weights_medium_light_skin_tone","man_lifting_weights_tone2"],
+keywords:["man","medium-light skin tone","weight lifter"],unicode:"1f3cb-1f3fc-2642",sprite:"activity"},{names:["man_lifting_weights_light_skin_tone","man_lifting_weights_tone1"],keywords:["light skin tone","man","weight lifter"],unicode:"1f3cb-1f3fb-2642",sprite:"activity"},{names:["fencer","fencing","person_fencing"],keywords:["fencer","fencing","sword"],unicode:"1f93a",sprite:"activity"},{names:["women_wrestling"],keywords:["women","wrestle"],unicode:"1f93c-2640",sprite:"activity"},{names:["men_wrestling"],
+keywords:["men","wrestle"],unicode:"1f93c-2642",sprite:"activity"},{names:["wrestlers","wrestling","people_wrestling"],keywords:["wrestle","wrestler"],unicode:"1f93c",sprite:"activity"},{names:["woman_cartwheeling"],keywords:["cartwheel","gymnastics","woman"],unicode:"1f938-2640",sprite:"activity"},{names:["woman_cartwheeling_light_skin_tone","woman_cartwheeling_tone1"],keywords:["cartwheel","gymnastics","light skin tone","woman"],unicode:"1f938-1f3fb-2640",sprite:"activity"},{names:["woman_cartwheeling_medium_light_skin_tone",
+"woman_cartwheeling_tone2"],keywords:["cartwheel","gymnastics","medium-light skin tone","woman"],unicode:"1f938-1f3fc-2640",sprite:"activity"},{names:["woman_cartwheeling_medium_skin_tone","woman_cartwheeling_tone3"],keywords:["cartwheel","gymnastics","medium skin tone","woman"],unicode:"1f938-1f3fd-2640",sprite:"activity"},{names:["woman_cartwheeling_medium_dark_skin_tone","woman_cartwheeling_tone4"],keywords:["cartwheel","gymnastics","medium-dark skin tone","woman"],unicode:"1f938-1f3fe-2640",sprite:"activity"},
+{names:["woman_cartwheeling_dark_skin_tone","woman_cartwheeling_tone5"],keywords:["cartwheel","dark skin tone","gymnastics","woman"],unicode:"1f938-1f3ff-2640",sprite:"activity"},{names:["man_cartwheeling"],keywords:["cartwheel","gymnastics","man"],unicode:"1f938-2642",sprite:"activity"},{names:["man_cartwheeling_light_skin_tone","man_cartwheeling_tone1"],keywords:["cartwheel","gymnastics","light skin tone","man"],unicode:"1f938-1f3fb-2642",sprite:"activity"},{names:["man_cartwheeling_medium_light_skin_tone",
+"man_cartwheeling_tone2"],keywords:["cartwheel","gymnastics","man","medium-light skin tone"],unicode:"1f938-1f3fc-2642",sprite:"activity"},{names:["man_cartwheeling_medium_skin_tone","man_cartwheeling_tone3"],keywords:["cartwheel","gymnastics","man","medium skin tone"],unicode:"1f938-1f3fd-2642",sprite:"activity"},{names:["man_cartwheeling_medium_dark_skin_tone","man_cartwheeling_tone4"],keywords:["cartwheel","gymnastics","man","medium-dark skin tone"],unicode:"1f938-1f3fe-2642",sprite:"activity"},
+{names:["man_cartwheeling_dark_skin_tone","man_cartwheeling_tone5"],keywords:["cartwheel","dark skin tone","gymnastics","man"],unicode:"1f938-1f3ff-2642",sprite:"activity"},{names:["cartwheel","person_doing_cartwheel"],keywords:["cartwheel","gymnastics"],unicode:"1f938",sprite:"activity"},{names:["cartwheel_tone1","person_doing_cartwheel_tone1"],keywords:["cartwheel","gymnastics","light skin tone"],unicode:"1f938-1f3fb",sprite:"activity"},{names:["cartwheel_tone2","person_doing_cartwheel_tone2"],
+keywords:["cartwheel","gymnastics","medium-light skin tone"],unicode:"1f938-1f3fc",sprite:"activity"},{names:["cartwheel_tone3","person_doing_cartwheel_tone3"],keywords:["cartwheel","gymnastics","medium skin tone"],unicode:"1f938-1f3fd",sprite:"activity"},{names:["cartwheel_tone4","person_doing_cartwheel_tone4"],keywords:["cartwheel","gymnastics","medium-dark skin tone"],unicode:"1f938-1f3fe",sprite:"activity"},{names:["cartwheel_tone5","person_doing_cartwheel_tone5"],keywords:["cartwheel","dark skin tone",
+"gymnastics"],unicode:"1f938-1f3ff",sprite:"activity"},{names:["woman_bouncing_ball"],keywords:["ball","woman"],unicode:"26f9-2640",sprite:"activity"},{names:["woman_bouncing_ball_light_skin_tone","woman_bouncing_ball_tone1"],keywords:["ball","light skin tone","woman"],unicode:"26f9-1f3fb-2640",sprite:"activity"},{names:["woman_bouncing_ball_medium_light_skin_tone","woman_bouncing_ball_tone2"],keywords:["ball","medium-light skin tone","woman"],unicode:"26f9-1f3fc-2640",sprite:"activity"},{names:["woman_bouncing_ball_medium_skin_tone",
+"woman_bouncing_ball_tone3"],keywords:["ball","medium skin tone","woman"],unicode:"26f9-1f3fd-2640",sprite:"activity"},{names:["woman_bouncing_ball_medium_dark_skin_tone","woman_bouncing_ball_tone4"],keywords:["ball","medium-dark skin tone","woman"],unicode:"26f9-1f3fe-2640",sprite:"activity"},{names:["woman_bouncing_ball_dark_skin_tone","woman_bouncing_ball_tone5"],keywords:["ball","dark skin tone","woman"],unicode:"26f9-1f3ff-2640",sprite:"activity"},{names:["basketball_player","person_with_ball",
+"person_bouncing_ball"],keywords:["ball"],unicode:"26f9",sprite:"activity"},{names:["basketball_player_tone1","person_with_ball_tone1","person_bouncing_ball_tone1"],keywords:["ball","light skin tone"],unicode:"26f9-1f3fb",sprite:"activity"},{names:["basketball_player_tone2","person_with_ball_tone2","person_bouncing_ball_tone2"],keywords:["ball","medium-light skin tone"],unicode:"26f9-1f3fc",sprite:"activity"},{names:["basketball_player_tone3","person_with_ball_tone3","person_bouncing_ball_tone3"],
+keywords:["ball","medium skin tone"],unicode:"26f9-1f3fd",sprite:"activity"},{names:["basketball_player_tone4","person_with_ball_tone4","person_bouncing_ball_tone4"],keywords:["ball","medium-dark skin tone"],unicode:"26f9-1f3fe",sprite:"activity"},{names:["basketball_player_tone5","person_with_ball_tone5","person_bouncing_ball_tone5"],keywords:["ball","dark skin tone"],unicode:"26f9-1f3ff",sprite:"activity"},{names:["man_bouncing_ball"],keywords:["ball","man"],unicode:"26f9-2642",sprite:"activity"},
+{names:["man_bouncing_ball_dark_skin_tone","man_bouncing_ball_tone5"],keywords:["ball","dark skin tone","man"],unicode:"26f9-1f3ff-2642",sprite:"activity"},{names:["man_bouncing_ball_medium_dark_skin_tone","man_bouncing_ball_tone4"],keywords:["ball","man","medium-dark skin tone"],unicode:"26f9-1f3fe-2642",sprite:"activity"},{names:["man_bouncing_ball_medium_skin_tone","man_bouncing_ball_tone3"],keywords:["ball","man","medium skin tone"],unicode:"26f9-1f3fd-2642",sprite:"activity"},{names:["man_bouncing_ball_medium_light_skin_tone",
+"man_bouncing_ball_tone2"],keywords:["ball","man","medium-light skin tone"],unicode:"26f9-1f3fc-2642",sprite:"activity"},{names:["man_bouncing_ball_light_skin_tone","man_bouncing_ball_tone1"],keywords:["ball","light skin tone","man"],unicode:"26f9-1f3fb-2642",sprite:"activity"},{names:["woman_playing_handball"],keywords:["handball","woman"],unicode:"1f93e-2640",sprite:"activity"},{names:["woman_playing_handball_light_skin_tone","woman_playing_handball_tone1"],keywords:["handball","light skin tone",
+"woman"],unicode:"1f93e-1f3fb-2640",sprite:"activity"},{names:["woman_playing_handball_medium_light_skin_tone","woman_playing_handball_tone2"],keywords:["handball","medium-light skin tone","woman"],unicode:"1f93e-1f3fc-2640",sprite:"activity"},{names:["woman_playing_handball_medium_skin_tone","woman_playing_handball_tone3"],keywords:["handball","medium skin tone","woman"],unicode:"1f93e-1f3fd-2640",sprite:"activity"},{names:["woman_playing_handball_medium_dark_skin_tone","woman_playing_handball_tone4"],
+keywords:["handball","medium-dark skin tone","woman"],unicode:"1f93e-1f3fe-2640",sprite:"activity"},{names:["woman_playing_handball_dark_skin_tone","woman_playing_handball_tone5"],keywords:["dark skin tone","handball","woman"],unicode:"1f93e-1f3ff-2640",sprite:"activity"},{names:["man_playing_handball"],keywords:["handball","man"],unicode:"1f93e-2642",sprite:"activity"},{names:["man_playing_handball_light_skin_tone","man_playing_handball_tone1"],keywords:["handball","light skin tone","man"],unicode:"1f93e-1f3fb-2642",
+sprite:"activity"},{names:["man_playing_handball_medium_light_skin_tone","man_playing_handball_tone2"],keywords:["handball","man","medium-light skin tone"],unicode:"1f93e-1f3fc-2642",sprite:"activity"},{names:["man_playing_handball_medium_skin_tone","man_playing_handball_tone3"],keywords:["handball","man","medium skin tone"],unicode:"1f93e-1f3fd-2642",sprite:"activity"},{names:["man_playing_handball_medium_dark_skin_tone","man_playing_handball_tone4"],keywords:["handball","man","medium-dark skin tone"],
+unicode:"1f93e-1f3fe-2642",sprite:"activity"},{names:["man_playing_handball_dark_skin_tone","man_playing_handball_tone5"],keywords:["dark skin tone","handball","man"],unicode:"1f93e-1f3ff-2642",sprite:"activity"},{names:["handball","person_playing_handball"],keywords:["ball","handball"],unicode:"1f93e",sprite:"activity"},{names:["handball_tone1","person_playing_handball_tone1"],keywords:["ball","handball","light skin tone"],unicode:"1f93e-1f3fb",sprite:"activity"},{names:["handball_tone2","person_playing_handball_tone2"],
+keywords:["ball","handball","medium-light skin tone"],unicode:"1f93e-1f3fc",sprite:"activity"},{names:["handball_tone3","person_playing_handball_tone3"],keywords:["ball","handball","medium skin tone"],unicode:"1f93e-1f3fd",sprite:"activity"},{names:["handball_tone4","person_playing_handball_tone4"],keywords:["ball","handball","medium-dark skin tone"],unicode:"1f93e-1f3fe",sprite:"activity"},{names:["handball_tone5","person_playing_handball_tone5"],keywords:["ball","dark skin tone","handball"],unicode:"1f93e-1f3ff",
+sprite:"activity"},{names:["woman_golfing"],keywords:["golf","woman"],unicode:"1f3cc-2640",sprite:"activity"},{names:["woman_golfing_light_skin_tone","woman_golfing_tone1"],keywords:["golf","light skin tone","woman"],unicode:"1f3cc-1f3fb-2640",sprite:"activity"},{names:["woman_golfing_medium_light_skin_tone","woman_golfing_tone2"],keywords:["golf","medium-light skin tone","woman"],unicode:"1f3cc-1f3fc-2640",sprite:"activity"},{names:["woman_golfing_medium_skin_tone","woman_golfing_tone3"],keywords:["golf",
+"medium skin tone","woman"],unicode:"1f3cc-1f3fd-2640",sprite:"activity"},{names:["woman_golfing_medium_dark_skin_tone","woman_golfing_tone4"],keywords:["golf","medium-dark skin tone","woman"],unicode:"1f3cc-1f3fe-2640",sprite:"activity"},{names:["woman_golfing_dark_skin_tone","woman_golfing_tone5"],keywords:["dark skin tone","golf","woman"],unicode:"1f3cc-1f3ff-2640",sprite:"activity"},{names:["golfer","person_golfing"],keywords:["ball","golf"],unicode:"1f3cc",sprite:"activity"},{names:["person_golfing_light_skin_tone",
+"person_golfing_tone1"],keywords:["ball","golf","light skin tone"],unicode:"1f3cc-1f3fb",sprite:"activity"},{names:["person_golfing_medium_light_skin_tone","person_golfing_tone2"],keywords:["ball","golf","medium-light skin tone"],unicode:"1f3cc-1f3fc",sprite:"activity"},{names:["person_golfing_medium_skin_tone","person_golfing_tone3"],keywords:["ball","golf","medium skin tone"],unicode:"1f3cc-1f3fd",sprite:"activity"},{names:["person_golfing_medium_dark_skin_tone","person_golfing_tone4"],keywords:["ball",
+"golf","medium-dark skin tone"],unicode:"1f3cc-1f3fe",sprite:"activity"},{names:["person_golfing_dark_skin_tone","person_golfing_tone5"],keywords:["ball","dark skin tone","golf"],unicode:"1f3cc-1f3ff",sprite:"activity"},{names:["man_golfing"],keywords:["golf","man"],unicode:"1f3cc-2642",sprite:"activity"},{names:["man_golfing_light_skin_tone","man_golfing_tone1"],keywords:["golf","light skin tone","man"],unicode:"1f3cc-1f3fb-2642",sprite:"activity"},{names:["man_golfing_medium_light_skin_tone","man_golfing_tone2"],
+keywords:["golf","man","medium-light skin tone"],unicode:"1f3cc-1f3fc-2642",sprite:"activity"},{names:["man_golfing_medium_skin_tone","man_golfing_tone3"],keywords:["golf","man","medium skin tone"],unicode:"1f3cc-1f3fd-2642",sprite:"activity"},{names:["man_golfing_medium_dark_skin_tone","man_golfing_tone4"],keywords:["golf","man","medium-dark skin tone"],unicode:"1f3cc-1f3fe-2642",sprite:"activity"},{names:["man_golfing_dark_skin_tone","man_golfing_tone5"],keywords:["dark skin tone","golf","man"],
+unicode:"1f3cc-1f3ff-2642",sprite:"activity"},{names:["woman_surfing"],keywords:["surfing","woman"],unicode:"1f3c4-2640",sprite:"activity"},{names:["woman_surfing_light_skin_tone","woman_surfing_tone1"],keywords:["light skin tone","surfing","woman"],unicode:"1f3c4-1f3fb-2640",sprite:"activity"},{names:["woman_surfing_medium_light_skin_tone","woman_surfing_tone2"],keywords:["medium-light skin tone","surfing","woman"],unicode:"1f3c4-1f3fc-2640",sprite:"activity"},{names:["woman_surfing_medium_skin_tone",
+"woman_surfing_tone3"],keywords:["medium skin tone","surfing","woman"],unicode:"1f3c4-1f3fd-2640",sprite:"activity"},{names:["woman_surfing_medium_dark_skin_tone","woman_surfing_tone4"],keywords:["medium-dark skin tone","surfing","woman"],unicode:"1f3c4-1f3fe-2640",sprite:"activity"},{names:["woman_surfing_dark_skin_tone","woman_surfing_tone5"],keywords:["dark skin tone","surfing","woman"],unicode:"1f3c4-1f3ff-2640",sprite:"activity"},{names:["surfer","person_surfing"],keywords:["surfing"],unicode:"1f3c4",
+sprite:"activity"},{names:["surfer_tone1","person_surfing_tone1"],keywords:["light skin tone","surfing"],unicode:"1f3c4-1f3fb",sprite:"activity"},{names:["surfer_tone2","person_surfing_tone2"],keywords:["medium-light skin tone","surfing"],unicode:"1f3c4-1f3fc",sprite:"activity"},{names:["surfer_tone3","person_surfing_tone3"],keywords:["medium skin tone","surfing"],unicode:"1f3c4-1f3fd",sprite:"activity"},{names:["surfer_tone4","person_surfing_tone4"],keywords:["medium-dark skin tone","surfing"],unicode:"1f3c4-1f3fe",
+sprite:"activity"},{names:["surfer_tone5","person_surfing_tone5"],keywords:["dark skin tone","surfing"],unicode:"1f3c4-1f3ff",sprite:"activity"},{names:["man_surfing"],keywords:["man","surfing"],unicode:"1f3c4-2642",sprite:"activity"},{names:["man_surfing_dark_skin_tone","man_surfing_tone5"],keywords:["dark skin tone","man","surfing"],unicode:"1f3c4-1f3ff-2642",sprite:"activity"},{names:["man_surfing_medium_dark_skin_tone","man_surfing_tone4"],keywords:["man","medium-dark skin tone","surfing"],unicode:"1f3c4-1f3fe-2642",
+sprite:"activity"},{names:["man_surfing_medium_skin_tone","man_surfing_tone3"],keywords:["man","medium skin tone","surfing"],unicode:"1f3c4-1f3fd-2642",sprite:"activity"},{names:["man_surfing_medium_light_skin_tone","man_surfing_tone2"],keywords:["man","medium-light skin tone","surfing"],unicode:"1f3c4-1f3fc-2642",sprite:"activity"},{names:["man_surfing_light_skin_tone","man_surfing_tone1"],keywords:["light skin tone","man","surfing"],unicode:"1f3c4-1f3fb-2642",sprite:"activity"},{names:["woman_swimming"],
+keywords:["swim","woman"],unicode:"1f3ca-2640",sprite:"activity"},{names:["woman_swimming_light_skin_tone","woman_swimming_tone1"],keywords:["light skin tone","swim","woman"],unicode:"1f3ca-1f3fb-2640",sprite:"activity"},{names:["woman_swimming_medium_light_skin_tone","woman_swimming_tone2"],keywords:["medium-light skin tone","swim","woman"],unicode:"1f3ca-1f3fc-2640",sprite:"activity"},{names:["woman_swimming_medium_skin_tone","woman_swimming_tone3"],keywords:["medium skin tone","swim","woman"],
+unicode:"1f3ca-1f3fd-2640",sprite:"activity"},{names:["woman_swimming_medium_dark_skin_tone","woman_swimming_tone4"],keywords:["medium-dark skin tone","swim","woman"],unicode:"1f3ca-1f3fe-2640",sprite:"activity"},{names:["woman_swimming_dark_skin_tone","woman_swimming_tone5"],keywords:["dark skin tone","swim","woman"],unicode:"1f3ca-1f3ff-2640",sprite:"activity"},{names:["swimmer","person_swimming"],keywords:["swim"],unicode:"1f3ca",sprite:"activity"},{names:["swimmer_tone1","person_swimming_tone1"],
+keywords:["light skin tone","swim"],unicode:"1f3ca-1f3fb",sprite:"activity"},{names:["swimmer_tone2","person_swimming_tone2"],keywords:["medium-light skin tone","swim"],unicode:"1f3ca-1f3fc",sprite:"activity"},{names:["swimmer_tone3","person_swimming_tone3"],keywords:["medium skin tone","swim"],unicode:"1f3ca-1f3fd",sprite:"activity"},{names:["swimmer_tone4","person_swimming_tone4"],keywords:["medium-dark skin tone","swim"],unicode:"1f3ca-1f3fe",sprite:"activity"},{names:["swimmer_tone5","person_swimming_tone5"],
+keywords:["dark skin tone","swim"],unicode:"1f3ca-1f3ff",sprite:"activity"},{names:["man_swimming"],keywords:["man","swim"],unicode:"1f3ca-2642",sprite:"activity"},{names:["man_swimming_dark_skin_tone","man_swimming_tone5"],keywords:["dark skin tone","man","swim"],unicode:"1f3ca-1f3ff-2642",sprite:"activity"},{names:["man_swimming_medium_dark_skin_tone","man_swimming_tone4"],keywords:["man","medium-dark skin tone","swim"],unicode:"1f3ca-1f3fe-2642",sprite:"activity"},{names:["man_swimming_medium_skin_tone",
+"man_swimming_tone3"],keywords:["man","medium skin tone","swim"],unicode:"1f3ca-1f3fd-2642",sprite:"activity"},{names:["man_swimming_medium_light_skin_tone","man_swimming_tone2"],keywords:["man","medium-light skin tone","swim"],unicode:"1f3ca-1f3fc-2642",sprite:"activity"},{names:["man_swimming_light_skin_tone","man_swimming_tone1"],keywords:["light skin tone","man","swim"],unicode:"1f3ca-1f3fb-2642",sprite:"activity"},{names:["woman_playing_water_polo"],keywords:["water polo","woman"],unicode:"1f93d-2640",
+sprite:"activity"},{names:["woman_playing_water_polo_light_skin_tone","woman_playing_water_polo_tone1"],keywords:["light skin tone","water polo","woman"],unicode:"1f93d-1f3fb-2640",sprite:"activity"},{names:["woman_playing_water_polo_medium_light_skin_tone","woman_playing_water_polo_tone2"],keywords:["medium-light skin tone","water polo","woman"],unicode:"1f93d-1f3fc-2640",sprite:"activity"},{names:["woman_playing_water_polo_medium_skin_tone","woman_playing_water_polo_tone3"],keywords:["medium skin tone",
+"water polo","woman"],unicode:"1f93d-1f3fd-2640",sprite:"activity"},{names:["woman_playing_water_polo_medium_dark_skin_tone","woman_playing_water_polo_tone4"],keywords:["medium-dark skin tone","water polo","woman"],unicode:"1f93d-1f3fe-2640",sprite:"activity"},{names:["woman_playing_water_polo_dark_skin_tone","woman_playing_water_polo_tone5"],keywords:["dark skin tone","water polo","woman"],unicode:"1f93d-1f3ff-2640",sprite:"activity"},{names:["man_playing_water_polo"],keywords:["man","water polo"],
+unicode:"1f93d-2642",sprite:"activity"},{names:["man_playing_water_polo_light_skin_tone","man_playing_water_polo_tone1"],keywords:["light skin tone","man","water polo"],unicode:"1f93d-1f3fb-2642",sprite:"activity"},{names:["man_playing_water_polo_medium_light_skin_tone","man_playing_water_polo_tone2"],keywords:["man","medium-light skin tone","water polo"],unicode:"1f93d-1f3fc-2642",sprite:"activity"},{names:["man_playing_water_polo_medium_skin_tone","man_playing_water_polo_tone3"],keywords:["man",
+"medium skin tone","water polo"],unicode:"1f93d-1f3fd-2642",sprite:"activity"},{names:["man_playing_water_polo_medium_dark_skin_tone","man_playing_water_polo_tone4"],keywords:["man","medium-dark skin tone","water polo"],unicode:"1f93d-1f3fe-2642",sprite:"activity"},{names:["man_playing_water_polo_dark_skin_tone","man_playing_water_polo_tone5"],keywords:["dark skin tone","man","water polo"],unicode:"1f93d-1f3ff-2642",sprite:"activity"},{names:["water_polo","person_playing_water_polo"],keywords:["polo",
+"water"],unicode:"1f93d",sprite:"activity"},{names:["water_polo_tone1","person_playing_water_polo_tone1"],keywords:["light skin tone","polo","water"],unicode:"1f93d-1f3fb",sprite:"activity"},{names:["water_polo_tone2","person_playing_water_polo_tone2"],keywords:["medium-light skin tone","polo","water"],unicode:"1f93d-1f3fc",sprite:"activity"},{names:["water_polo_tone3","person_playing_water_polo_tone3"],keywords:["medium skin tone","polo","water"],unicode:"1f93d-1f3fd",sprite:"activity"},{names:["water_polo_tone4",
+"person_playing_water_polo_tone4"],keywords:["medium-dark skin tone","polo","water"],unicode:"1f93d-1f3fe",sprite:"activity"},{names:["water_polo_tone5","person_playing_water_polo_tone5"],keywords:["dark skin tone","polo","water"],unicode:"1f93d-1f3ff",sprite:"activity"},{names:["woman_rowing_boat"],keywords:["boat","rowboat","woman"],unicode:"1f6a3-2640",sprite:"activity"},{names:["woman_rowing_boat_light_skin_tone","woman_rowing_boat_tone1"],keywords:["boat","light skin tone","rowboat","woman"],
+unicode:"1f6a3-1f3fb-2640",sprite:"activity"},{names:["woman_rowing_boat_medium_light_skin_tone","woman_rowing_boat_tone2"],keywords:["boat","medium-light skin tone","rowboat","woman"],unicode:"1f6a3-1f3fc-2640",sprite:"activity"},{names:["woman_rowing_boat_medium_skin_tone","woman_rowing_boat_tone3"],keywords:["boat","medium skin tone","rowboat","woman"],unicode:"1f6a3-1f3fd-2640",sprite:"activity"},{names:["woman_rowing_boat_medium_dark_skin_tone","woman_rowing_boat_tone4"],keywords:["boat","medium-dark skin tone",
+"rowboat","woman"],unicode:"1f6a3-1f3fe-2640",sprite:"activity"},{names:["woman_rowing_boat_dark_skin_tone","woman_rowing_boat_tone5"],keywords:["boat","dark skin tone","rowboat","woman"],unicode:"1f6a3-1f3ff-2640",sprite:"activity"},{names:["rowboat","person_rowing_boat"],keywords:["boat","rowboat"],unicode:"1f6a3",sprite:"activity"},{names:["rowboat_tone1","person_rowing_boat_tone1"],keywords:["boat","light skin tone","rowboat"],unicode:"1f6a3-1f3fb",sprite:"activity"},{names:["rowboat_tone2","person_rowing_boat_tone2"],
+keywords:["boat","medium-light skin tone","rowboat"],unicode:"1f6a3-1f3fc",sprite:"activity"},{names:["rowboat_tone3","person_rowing_boat_tone3"],keywords:["boat","medium skin tone","rowboat"],unicode:"1f6a3-1f3fd",sprite:"activity"},{names:["rowboat_tone4","person_rowing_boat_tone4"],keywords:["boat","medium-dark skin tone","rowboat"],unicode:"1f6a3-1f3fe",sprite:"activity"},{names:["rowboat_tone5","person_rowing_boat_tone5"],keywords:["boat","dark skin tone","rowboat"],unicode:"1f6a3-1f3ff",sprite:"activity"},
+{names:["man_rowing_boat"],keywords:["boat","man","rowboat"],unicode:"1f6a3-2642",sprite:"activity"},{names:["man_rowing_boat_dark_skin_tone","man_rowing_boat_tone5"],keywords:["boat","dark skin tone","man","rowboat"],unicode:"1f6a3-1f3ff-2642",sprite:"activity"},{names:["man_rowing_boat_medium_dark_skin_tone","man_rowing_boat_tone4"],keywords:["boat","man","medium-dark skin tone","rowboat"],unicode:"1f6a3-1f3fe-2642",sprite:"activity"},{names:["man_rowing_boat_medium_skin_tone","man_rowing_boat_tone3"],
+keywords:["boat","man","medium skin tone","rowboat"],unicode:"1f6a3-1f3fd-2642",sprite:"activity"},{names:["man_rowing_boat_medium_light_skin_tone","man_rowing_boat_tone2"],keywords:["boat","man","medium-light skin tone","rowboat"],unicode:"1f6a3-1f3fc-2642",sprite:"activity"},{names:["man_rowing_boat_light_skin_tone","man_rowing_boat_tone1"],keywords:["boat","light skin tone","man","rowboat"],unicode:"1f6a3-1f3fb-2642",sprite:"activity"},{names:["horse_racing"],keywords:["horse","jockey","racehorse",
+"racing"],unicode:"1f3c7",sprite:"activity"},{names:["horse_racing_tone1"],keywords:["horse","jockey","light skin tone","racehorse","racing"],unicode:"1f3c7-1f3fb",sprite:"activity"},{names:["horse_racing_tone2"],keywords:["horse","jockey","medium-light skin tone","racehorse","racing"],unicode:"1f3c7-1f3fc",sprite:"activity"},{names:["horse_racing_tone3"],keywords:["horse","jockey","medium skin tone","racehorse","racing"],unicode:"1f3c7-1f3fd",sprite:"activity"},{names:["horse_racing_tone4"],keywords:["horse",
+"jockey","medium-dark skin tone","racehorse","racing"],unicode:"1f3c7-1f3fe",sprite:"activity"},{names:["horse_racing_tone5"],keywords:["dark skin tone","horse","jockey","racehorse","racing"],unicode:"1f3c7-1f3ff",sprite:"activity"},{names:["woman_biking"],keywords:["bicycle","biking","cyclist","woman"],unicode:"1f6b4-2640",sprite:"activity"},{names:["woman_biking_light_skin_tone","woman_biking_tone1"],keywords:["bicycle","biking","cyclist","light skin tone","woman"],unicode:"1f6b4-1f3fb-2640",sprite:"activity"},
+{names:["woman_biking_medium_light_skin_tone","woman_biking_tone2"],keywords:["bicycle","biking","cyclist","medium-light skin tone","woman"],unicode:"1f6b4-1f3fc-2640",sprite:"activity"},{names:["woman_biking_medium_skin_tone","woman_biking_tone3"],keywords:["bicycle","biking","cyclist","medium skin tone","woman"],unicode:"1f6b4-1f3fd-2640",sprite:"activity"},{names:["woman_biking_medium_dark_skin_tone","woman_biking_tone4"],keywords:["bicycle","biking","cyclist","medium-dark skin tone","woman"],
+unicode:"1f6b4-1f3fe-2640",sprite:"activity"},{names:["woman_biking_dark_skin_tone","woman_biking_tone5"],keywords:["bicycle","biking","cyclist","dark skin tone","woman"],unicode:"1f6b4-1f3ff-2640",sprite:"activity"},{names:["bicyclist","person_biking"],keywords:["bicycle","biking","cyclist"],unicode:"1f6b4",sprite:"activity"},{names:["bicyclist_tone1","person_biking_tone1"],keywords:["bicycle","biking","cyclist","light skin tone"],unicode:"1f6b4-1f3fb",sprite:"activity"},{names:["bicyclist_tone2",
+"person_biking_tone2"],keywords:["bicycle","biking","cyclist","medium-light skin tone"],unicode:"1f6b4-1f3fc",sprite:"activity"},{names:["bicyclist_tone3","person_biking_tone3"],keywords:["bicycle","biking","cyclist","medium skin tone"],unicode:"1f6b4-1f3fd",sprite:"activity"},{names:["bicyclist_tone4","person_biking_tone4"],keywords:["bicycle","biking","cyclist","medium-dark skin tone"],unicode:"1f6b4-1f3fe",sprite:"activity"},{names:["bicyclist_tone5","person_biking_tone5"],keywords:["bicycle",
+"biking","cyclist","dark skin tone"],unicode:"1f6b4-1f3ff",sprite:"activity"},{names:["man_biking"],keywords:["bicycle","biking","cyclist","man"],unicode:"1f6b4-2642",sprite:"activity"},{names:["man_biking_dark_skin_tone","man_biking_tone5"],keywords:["bicycle","biking","cyclist","dark skin tone","man"],unicode:"1f6b4-1f3ff-2642",sprite:"activity"},{names:["man_biking_medium_dark_skin_tone","man_biking_tone4"],keywords:["bicycle","biking","cyclist","man","medium-dark skin tone"],unicode:"1f6b4-1f3fe-2642",
+sprite:"activity"},{names:["man_biking_medium_skin_tone","man_biking_tone3"],keywords:["bicycle","biking","cyclist","man","medium skin tone"],unicode:"1f6b4-1f3fd-2642",sprite:"activity"},{names:["man_biking_medium_light_skin_tone","man_biking_tone2"],keywords:["bicycle","biking","cyclist","man","medium-light skin tone"],unicode:"1f6b4-1f3fc-2642",sprite:"activity"},{names:["man_biking_light_skin_tone","man_biking_tone1"],keywords:["bicycle","biking","cyclist","light skin tone","man"],unicode:"1f6b4-1f3fb-2642",
+sprite:"activity"},{names:["woman_mountain_biking"],keywords:"bicycle bike biking cyclist mountain woman".split(" "),unicode:"1f6b5-2640",sprite:"activity"},{names:["woman_mountain_biking_light_skin_tone","woman_mountain_biking_tone1"],keywords:"bicycle;bike;biking;cyclist;light skin tone;mountain;woman".split(";"),unicode:"1f6b5-1f3fb-2640",sprite:"activity"},{names:["woman_mountain_biking_medium_light_skin_tone","woman_mountain_biking_tone2"],keywords:"bicycle;bike;biking;cyclist;medium-light skin tone;mountain;woman".split(";"),
+unicode:"1f6b5-1f3fc-2640",sprite:"activity"},{names:["woman_mountain_biking_medium_skin_tone","woman_mountain_biking_tone3"],keywords:"bicycle;bike;biking;cyclist;medium skin tone;mountain;woman".split(";"),unicode:"1f6b5-1f3fd-2640",sprite:"activity"},{names:["woman_mountain_biking_medium_dark_skin_tone","woman_mountain_biking_tone4"],keywords:"bicycle;bike;biking;cyclist;medium-dark skin tone;mountain;woman".split(";"),unicode:"1f6b5-1f3fe-2640",sprite:"activity"},{names:["woman_mountain_biking_dark_skin_tone",
+"woman_mountain_biking_tone5"],keywords:"bicycle;bike;biking;cyclist;dark skin tone;mountain;woman".split(";"),unicode:"1f6b5-1f3ff-2640",sprite:"activity"},{names:["mountain_bicyclist","person_mountain_biking"],keywords:["bicycle","bicyclist","bike","cyclist","mountain"],unicode:"1f6b5",sprite:"activity"},{names:["mountain_bicyclist_tone1","person_mountain_biking_tone1"],keywords:"bicycle;bicyclist;bike;cyclist;light skin tone;mountain".split(";"),unicode:"1f6b5-1f3fb",sprite:"activity"},{names:["mountain_bicyclist_tone2",
+"person_mountain_biking_tone2"],keywords:"bicycle;bicyclist;bike;cyclist;medium-light skin tone;mountain".split(";"),unicode:"1f6b5-1f3fc",sprite:"activity"},{names:["mountain_bicyclist_tone3","person_mountain_biking_tone3"],keywords:"bicycle;bicyclist;bike;cyclist;medium skin tone;mountain".split(";"),unicode:"1f6b5-1f3fd",sprite:"activity"},{names:["mountain_bicyclist_tone4","person_mountain_biking_tone4"],keywords:"bicycle;bicyclist;bike;cyclist;medium-dark skin tone;mountain".split(";"),unicode:"1f6b5-1f3fe",
+sprite:"activity"},{names:["mountain_bicyclist_tone5","person_mountain_biking_tone5"],keywords:"bicycle;bicyclist;bike;cyclist;dark skin tone;mountain".split(";"),unicode:"1f6b5-1f3ff",sprite:"activity"},{names:["man_mountain_biking"],keywords:["bicycle","bike","cyclist","man","mountain"],unicode:"1f6b5-2642",sprite:"activity"},{names:["man_mountain_biking_dark_skin_tone","man_mountain_biking_tone5"],keywords:"bicycle;bike;cyclist;dark skin tone;man;mountain".split(";"),unicode:"1f6b5-1f3ff-2642",
+sprite:"activity"},{names:["man_mountain_biking_medium_dark_skin_tone","man_mountain_biking_tone4"],keywords:"bicycle;bike;cyclist;man;medium-dark skin tone;mountain".split(";"),unicode:"1f6b5-1f3fe-2642",sprite:"activity"},{names:["man_mountain_biking_medium_skin_tone","man_mountain_biking_tone3"],keywords:"bicycle;bike;cyclist;man;medium skin tone;mountain".split(";"),unicode:"1f6b5-1f3fd-2642",sprite:"activity"},{names:["man_mountain_biking_medium_light_skin_tone","man_mountain_biking_tone2"],
+keywords:"bicycle;bike;cyclist;man;medium-light skin tone;mountain".split(";"),unicode:"1f6b5-1f3fc-2642",sprite:"activity"},{names:["man_mountain_biking_light_skin_tone","man_mountain_biking_tone1"],keywords:"bicycle;bike;cyclist;light skin tone;man;mountain".split(";"),unicode:"1f6b5-1f3fb-2642",sprite:"activity"},{names:["running_shirt_with_sash"],keywords:["athletics","running","sash","shirt"],unicode:"1f3bd",sprite:"activity"},{names:["sports_medal","medal"],keywords:["medal"],unicode:"1f3c5",
+sprite:"activity"},{names:["military_medal"],keywords:["celebration","medal","military"],unicode:"1f396",sprite:"activity"},{names:["first_place_medal","first_place"],keywords:["first","gold","medal"],unicode:"1f947",sprite:"activity"},{names:["second_place_medal","second_place"],keywords:["medal","second","silver"],unicode:"1f948",sprite:"activity"},{names:["third_place_medal","third_place"],keywords:["bronze","medal","third"],unicode:"1f949",sprite:"activity"},{names:["trophy"],keywords:["prize"],
+unicode:"1f3c6",sprite:"activity"},{names:["rosette"],keywords:["plant"],unicode:"1f3f5",sprite:"activity"},{names:["reminder_ribbon"],keywords:["celebration","reminder","ribbon"],unicode:"1f397",sprite:"activity"},{names:["ticket"],keywords:["admission"],unicode:"1f3ab",sprite:"activity"},{names:["admission_tickets","tickets"],keywords:["admission","ticket"],unicode:"1f39f",sprite:"activity"},{names:["circus_tent"],keywords:["circus","tent"],unicode:"1f3aa",sprite:"activity"},{names:["woman_juggling"],
+keywords:["juggling","multitask","woman"],unicode:"1f939-2640",sprite:"activity"},{names:["woman_juggling_light_skin_tone","woman_juggling_tone1"],keywords:["juggling","light skin tone","multitask","woman"],unicode:"1f939-1f3fb-2640",sprite:"activity"},{names:["woman_juggling_medium_light_skin_tone","woman_juggling_tone2"],keywords:["juggling","medium-light skin tone","multitask","woman"],unicode:"1f939-1f3fc-2640",sprite:"activity"},{names:["woman_juggling_medium_skin_tone","woman_juggling_tone3"],
+keywords:["juggling","medium skin tone","multitask","woman"],unicode:"1f939-1f3fd-2640",sprite:"activity"},{names:["woman_juggling_medium_dark_skin_tone","woman_juggling_tone4"],keywords:["juggling","medium-dark skin tone","multitask","woman"],unicode:"1f939-1f3fe-2640",sprite:"activity"},{names:["woman_juggling_dark_skin_tone","woman_juggling_tone5"],keywords:["dark skin tone","juggling","multitask","woman"],unicode:"1f939-1f3ff-2640",sprite:"activity"},{names:["man_juggling"],keywords:["juggling",
+"man","multitask"],unicode:"1f939-2642",sprite:"activity"},{names:["man_juggling_light_skin_tone","man_juggling_tone1"],keywords:["juggling","light skin tone","man","multitask"],unicode:"1f939-1f3fb-2642",sprite:"activity"},{names:["man_juggling_medium_light_skin_tone","man_juggling_tone2"],keywords:["juggling","man","medium-light skin tone","multitask"],unicode:"1f939-1f3fc-2642",sprite:"activity"},{names:["man_juggling_medium_skin_tone","man_juggling_tone3"],keywords:["juggling","man","medium skin tone",
+"multitask"],unicode:"1f939-1f3fd-2642",sprite:"activity"},{names:["man_juggling_medium_dark_skin_tone","man_juggling_tone4"],keywords:["juggling","man","medium-dark skin tone","multitask"],unicode:"1f939-1f3fe-2642",sprite:"activity"},{names:["man_juggling_dark_skin_tone","man_juggling_tone5"],keywords:["dark skin tone","juggling","man","multitask"],unicode:"1f939-1f3ff-2642",sprite:"activity"},{names:["juggling","juggler","person_juggling"],keywords:["balance","juggle","multitask","skill"],unicode:"1f939",
+sprite:"activity"},{names:["juggling_tone1","juggler_tone1","person_juggling_tone1"],keywords:["balance","juggle","light skin tone","multitask","skill"],unicode:"1f939-1f3fb",sprite:"activity"},{names:["juggling_tone2","juggler_tone2","person_juggling_tone2"],keywords:["balance","juggle","medium-light skin tone","multitask","skill"],unicode:"1f939-1f3fc",sprite:"activity"},{names:["juggling_tone3","juggler_tone3","person_juggling_tone3"],keywords:["balance","juggle","medium skin tone","multitask",
+"skill"],unicode:"1f939-1f3fd",sprite:"activity"},{names:["juggling_tone4","juggler_tone4","person_juggling_tone4"],keywords:["balance","juggle","medium-dark skin tone","multitask","skill"],unicode:"1f939-1f3fe",sprite:"activity"},{names:["juggling_tone5","juggler_tone5","person_juggling_tone5"],keywords:["balance","dark skin tone","juggle","multitask","skill"],unicode:"1f939-1f3ff",sprite:"activity"},{names:["performing_arts"],keywords:["art","mask","performing","theater","theatre"],unicode:"1f3ad",
+sprite:"activity"},{names:["art"],keywords:["art","museum","painting","palette"],unicode:"1f3a8",sprite:"activity"},{names:["clapper"],keywords:["clapper","movie"],unicode:"1f3ac",sprite:"activity"},{names:["microphone"],keywords:["karaoke","mic"],unicode:"1f3a4",sprite:"activity"},{names:["headphones"],keywords:["earbud"],unicode:"1f3a7",sprite:"activity"},{names:["musical_score"],keywords:["music","score"],unicode:"1f3bc",sprite:"activity"},{names:["musical_keyboard"],keywords:["instrument","keyboard",
+"music","piano"],unicode:"1f3b9",sprite:"activity"},{names:["drum_with_drumsticks","drum"],keywords:["drum","drumsticks","music"],unicode:"1f941",sprite:"activity"},{names:["saxophone"],keywords:["instrument","music","sax"],unicode:"1f3b7",sprite:"activity"},{names:["trumpet"],keywords:["instrument","music"],unicode:"1f3ba",sprite:"activity"},{names:["guitar"],keywords:["instrument","music"],unicode:"1f3b8",sprite:"activity"},{names:["violin"],keywords:["instrument","music"],unicode:"1f3bb",sprite:"activity"},
+{names:["game_die"],keywords:["dice","die","game"],unicode:"1f3b2",sprite:"activity"},{names:["dart"],keywords:"bull bullseye dart eye game hit target".split(" "),unicode:"1f3af",sprite:"activity"},{names:["bowling"],keywords:["ball","game"],unicode:"1f3b3",sprite:"activity"},{names:["video_game"],keywords:["controller","game"],unicode:"1f3ae",sprite:"activity"},{names:["slot_machine"],keywords:["game","slot"],unicode:"1f3b0",sprite:"activity"},{names:["red_car"],keywords:["car"],unicode:"1f697",
+sprite:"travel"},{names:["taxi"],keywords:["vehicle"],unicode:"1f695",sprite:"travel"},{names:["blue_car"],keywords:["recreational","sport utility"],unicode:"1f699",sprite:"travel"},{names:["bus"],keywords:["vehicle"],unicode:"1f68c",sprite:"travel"},{names:["trolleybus"],keywords:["bus","tram","trolley"],unicode:"1f68e",sprite:"travel"},{names:["racing_car","race_car"],keywords:["car","racing"],unicode:"1f3ce",sprite:"travel"},{names:["police_car"],keywords:["car","patrol","police"],unicode:"1f693",
+sprite:"travel"},{names:["ambulance"],keywords:["vehicle"],unicode:"1f691",sprite:"travel"},{names:["fire_engine"],keywords:["engine","fire","truck"],unicode:"1f692",sprite:"travel"},{names:["minibus"],keywords:["bus"],unicode:"1f690",sprite:"travel"},{names:["truck"],keywords:["delivery","truck"],unicode:"1f69a",sprite:"travel"},{names:["articulated_lorry"],keywords:["lorry","semi","truck"],unicode:"1f69b",sprite:"travel"},{names:["tractor"],keywords:["vehicle"],unicode:"1f69c",sprite:"travel"},
+{names:["scooter"],keywords:["kick","scooter"],unicode:"1f6f4",sprite:"travel"},{names:["bike"],keywords:["bike"],unicode:"1f6b2",sprite:"travel"},{names:["motorbike","motor_scooter"],keywords:["motor","scooter"],unicode:"1f6f5",sprite:"travel"},{names:["racing_motorcycle","motorcycle"],keywords:["racing"],unicode:"1f3cd",sprite:"travel"},{names:["rotating_light"],keywords:["beacon","car","light","police","revolving"],unicode:"1f6a8",sprite:"travel"},{names:["oncoming_police_car"],keywords:["car",
+"oncoming","police"],unicode:"1f694",sprite:"travel"},{names:["oncoming_bus"],keywords:["bus","oncoming"],unicode:"1f68d",sprite:"travel"},{names:["oncoming_automobile"],keywords:["automobile","car","oncoming"],unicode:"1f698",sprite:"travel"},{names:["oncoming_taxi"],keywords:["oncoming","taxi"],unicode:"1f696",sprite:"travel"},{names:["aerial_tramway"],keywords:["aerial","cable","car","gondola","tramway"],unicode:"1f6a1",sprite:"travel"},{names:["mountain_cableway"],keywords:["cable","gondola",
+"mountain"],unicode:"1f6a0",sprite:"travel"},{names:["suspension_railway"],keywords:["railway","suspension"],unicode:"1f69f",sprite:"travel"},{names:["railway_car"],keywords:"car electric railway train tram trolleybus".split(" "),unicode:"1f683",sprite:"travel"},{names:["train"],keywords:["car","tram","trolleybus"],unicode:"1f68b",sprite:"travel"},{names:["mountain_railway"],keywords:["car","mountain","railway"],unicode:"1f69e",sprite:"travel"},{names:["monorail"],keywords:["vehicle"],unicode:"1f69d",
+sprite:"travel"},{names:["bullettrain_side"],keywords:["railway","shinkansen","speed","train"],unicode:"1f684",sprite:"travel"},{names:["bullettrain_front"],keywords:["bullet","railway","shinkansen","speed","train"],unicode:"1f685",sprite:"travel"},{names:["light_rail"],keywords:["railway"],unicode:"1f688",sprite:"travel"},{names:["steam_locomotive"],keywords:["engine","railway","steam","train"],unicode:"1f682",sprite:"travel"},{names:["train2"],keywords:["railway"],unicode:"1f686",sprite:"travel"},
+{names:["metro"],keywords:["subway"],unicode:"1f687",sprite:"travel"},{names:["tram"],keywords:["trolleybus"],unicode:"1f68a",sprite:"travel"},{names:["station"],keywords:["railway","train"],unicode:"1f689",sprite:"travel"},{names:["helicopter"],keywords:["vehicle"],unicode:"1f681",sprite:"travel"},{names:["small_airplane","airplane_small"],keywords:["aeroplane","airplane"],unicode:"1f6e9",sprite:"travel"},{names:["airplane_departure"],keywords:["aeroplane","airplane","check-in","departure","departures"],
+unicode:"1f6eb",sprite:"travel"},{names:["airplane_arriving"],keywords:["aeroplane","airplane","arrivals","arriving","landing"],unicode:"1f6ec",sprite:"travel"},{names:["rocket"],keywords:["space"],unicode:"1f680",sprite:"travel"},{names:["satellite_orbital"],keywords:["space"],unicode:"1f6f0",sprite:"travel"},{names:["seat"],keywords:["chair"],unicode:"1f4ba",sprite:"travel"},{names:["kayak","canoe"],keywords:["boat","canoe"],unicode:"1f6f6",sprite:"travel"},{names:["sailboat"],keywords:["boat",
+"resort","sea","yacht"],unicode:"26f5",sprite:"travel"},{names:["motorboat"],keywords:["boat","motorboat"],unicode:"1f6e5",sprite:"travel"},{names:["speedboat"],keywords:["boat"],unicode:"1f6a4",sprite:"travel"},{names:["passenger_ship","cruise_ship"],keywords:["passenger","ship"],unicode:"1f6f3",sprite:"travel"},{names:["ferry"],keywords:["boat","passenger"],unicode:"26f4",sprite:"travel"},{names:["ship"],keywords:["boat","passenger"],unicode:"1f6a2",sprite:"travel"},{names:["construction"],keywords:["barrier"],
+unicode:"1f6a7",sprite:"travel"},{names:["fuelpump"],keywords:["fuel","fuelpump","gas","pump","station"],unicode:"26fd",sprite:"travel"},{names:["busstop"],keywords:["bus","busstop","stop"],unicode:"1f68f",sprite:"travel"},{names:["vertical_traffic_light"],keywords:["light","signal","traffic"],unicode:"1f6a6",sprite:"travel"},{names:["traffic_light"],keywords:["light","signal","traffic"],unicode:"1f6a5",sprite:"travel"},{names:["world_map","map"],keywords:["map","world"],unicode:"1f5fa",sprite:"travel"},
+{names:["moyai"],keywords:["face","moyai","statue"],unicode:"1f5ff",sprite:"travel"},{names:["statue_of_liberty"],keywords:["liberty","statue"],unicode:"1f5fd",sprite:"travel"},{names:["fountain"],keywords:["fountain"],unicode:"26f2",sprite:"travel"},{names:["tokyo_tower"],keywords:["Tokyo","tower"],unicode:"1f5fc",sprite:"travel"},{names:["european_castle"],keywords:["European"],unicode:"1f3f0",sprite:"travel"},{names:["japanese_castle"],keywords:["Japanese","castle"],unicode:"1f3ef",sprite:"travel"},
+{names:["stadium"],keywords:["stadium"],unicode:"1f3df",sprite:"travel"},{names:["ferris_wheel"],keywords:["amusement park","ferris","wheel"],unicode:"1f3a1",sprite:"travel"},{names:["roller_coaster"],keywords:["amusement park","coaster","roller"],unicode:"1f3a2",sprite:"travel"},{names:["carousel_horse"],keywords:["carousel","horse"],unicode:"1f3a0",sprite:"travel"},{names:["umbrella_on_ground","beach_umbrella"],keywords:["rain","sun","umbrella"],unicode:"26f1",sprite:"travel"},{names:["beach_with_umbrella",
+"beach"],keywords:["beach","umbrella"],unicode:"1f3d6",sprite:"travel"},{names:["desert_island","island"],keywords:["desert","island"],unicode:"1f3dd",sprite:"travel"},{names:["mountain"],keywords:["mountain"],unicode:"26f0",sprite:"travel"},{names:["snow_capped_mountain","mountain_snow"],keywords:["cold","mountain","snow"],unicode:"1f3d4",sprite:"travel"},{names:["mount_fuji"],keywords:["fuji","mountain"],unicode:"1f5fb",sprite:"travel"},{names:["volcano"],keywords:["eruption","mountain"],unicode:"1f30b",
+sprite:"travel"},{names:["desert"],keywords:["desert"],unicode:"1f3dc",sprite:"travel"},{names:["camping"],keywords:["camping"],unicode:"1f3d5",sprite:"travel"},{names:["tent"],keywords:["camping"],unicode:"26fa",sprite:"travel"},{names:["railroad_track","railway_track"],keywords:["railway","train"],unicode:"1f6e4",sprite:"travel"},{names:["motorway"],keywords:["highway","road"],unicode:"1f6e3",sprite:"travel"},{names:["building_construction","construction_site"],keywords:["construction"],unicode:"1f3d7",
+sprite:"travel"},{names:["factory"],keywords:["building"],unicode:"1f3ed",sprite:"travel"},{names:["house"],keywords:["home","house"],unicode:"1f3e0",sprite:"travel"},{names:["house_with_garden"],keywords:["garden","home","house"],unicode:"1f3e1",sprite:"travel"},{names:["house_buildings","homes"],keywords:["houses"],unicode:"1f3d8",sprite:"travel"},{names:["derelict_house_building","house_abandoned"],keywords:["derelict","house"],unicode:"1f3da",sprite:"travel"},{names:["office"],keywords:["building"],
+unicode:"1f3e2",sprite:"travel"},{names:["department_store"],keywords:["department","store"],unicode:"1f3ec",sprite:"travel"},{names:["post_office"],keywords:["Japanese","post"],unicode:"1f3e3",sprite:"travel"},{names:["european_post_office"],keywords:["European","post"],unicode:"1f3e4",sprite:"travel"},{names:["hospital"],keywords:["doctor","medicine"],unicode:"1f3e5",sprite:"travel"},{names:["bank"],keywords:["building"],unicode:"1f3e6",sprite:"travel"},{names:["hotel"],keywords:["building"],unicode:"1f3e8",
+sprite:"travel"},{names:["convenience_store"],keywords:["convenience","store"],unicode:"1f3ea",sprite:"travel"},{names:["school"],keywords:["building"],unicode:"1f3eb",sprite:"travel"},{names:["love_hotel"],keywords:["hotel","love"],unicode:"1f3e9",sprite:"travel"},{names:["wedding"],keywords:["chapel","romance"],unicode:"1f492",sprite:"travel"},{names:["classical_building"],keywords:["classical"],unicode:"1f3db",sprite:"travel"},{names:["church"],keywords:["Christian","cross","religion"],unicode:"26ea",
+sprite:"travel"},{names:["mosque"],keywords:["Muslim","islam","religion"],unicode:"1f54c",sprite:"travel"},{names:["synagogue"],keywords:["Jew","Jewish","religion","temple"],unicode:"1f54d",sprite:"travel"},{names:["kaaba"],keywords:["Muslim","islam","religion"],unicode:"1f54b",sprite:"travel"},{names:["shinto_shrine"],keywords:["religion","shinto","shrine"],unicode:"26e9",sprite:"travel"},{names:["japan"],keywords:["Japan","map"],unicode:"1f5fe",sprite:"travel"},{names:["rice_scene"],keywords:["celebration",
+"ceremony","moon"],unicode:"1f391",sprite:"travel"},{names:["national_park","park"],keywords:["park"],unicode:"1f3de",sprite:"travel"},{names:["sunrise"],keywords:["morning","sun"],unicode:"1f305",sprite:"travel"},{names:["sunrise_over_mountains"],keywords:["morning","mountain","sun","sunrise"],unicode:"1f304",sprite:"travel"},{names:["stars"],keywords:["falling","shooting","star"],unicode:"1f320",sprite:"travel"},{names:["sparkler"],keywords:["celebration","fireworks","sparkle"],unicode:"1f387",
+sprite:"travel"},{names:["fireworks"],keywords:["celebration"],unicode:"1f386",sprite:"travel"},{names:["city_sunrise","city_sunset"],keywords:["dusk","sun"],unicode:"1f307",sprite:"travel"},{names:["city_dusk"],keywords:"city dusk evening landscape sun sunset".split(" "),unicode:"1f306",sprite:"travel"},{names:["cityscape"],keywords:["city"],unicode:"1f3d9",sprite:"travel"},{names:["night_with_stars"],keywords:["night","star"],unicode:"1f303",sprite:"travel"},{names:["milky_way"],keywords:["space"],
+unicode:"1f30c",sprite:"travel"},{names:["bridge_at_night"],keywords:["bridge","night"],unicode:"1f309",sprite:"travel"},{names:["foggy"],keywords:["fog"],unicode:"1f301",sprite:"travel"},{names:["watch"],keywords:["clock"],unicode:"231a",sprite:"objects"},{names:["iphone"],keywords:["cell","mobile","phone","telephone"],unicode:"1f4f1",sprite:"objects"},{names:["calling"],keywords:"arrow call cell mobile phone receive telephone".split(" "),unicode:"1f4f2",sprite:"objects"},{names:["computer"],keywords:["computer",
+"pc","personal"],unicode:"1f4bb",sprite:"objects"},{names:["desktop_computer","desktop"],keywords:["computer","desktop"],unicode:"1f5a5",sprite:"objects"},{names:["printer"],keywords:["computer"],unicode:"1f5a8",sprite:"objects"},{names:["three_button_mouse","mouse_three_button"],keywords:["computer"],unicode:"1f5b1",sprite:"objects"},{names:["trackball"],keywords:["computer"],unicode:"1f5b2",sprite:"objects"},{names:["joystick"],keywords:["game","video game"],unicode:"1f579",sprite:"objects"},{names:["compression"],
+keywords:["compress","tool","vice"],unicode:"1f5dc",sprite:"objects"},{names:["minidisc"],keywords:["computer","disk","minidisk","optical"],unicode:"1f4bd",sprite:"objects"},{names:["floppy_disk"],keywords:["computer","disk","floppy"],unicode:"1f4be",sprite:"objects"},{names:["cd"],keywords:["cd","computer","disk","optical"],unicode:"1f4bf",sprite:"objects"},{names:["dvd"],keywords:["blu-ray","computer","disk","dvd","optical"],unicode:"1f4c0",sprite:"objects"},{names:["vhs"],keywords:["tape","vhs",
+"video"],unicode:"1f4fc",sprite:"objects"},{names:["camera"],keywords:["video"],unicode:"1f4f7",sprite:"objects"},{names:["camera_with_flash"],keywords:["camera","flash","video"],unicode:"1f4f8",sprite:"objects"},{names:["video_camera"],keywords:["camera","video"],unicode:"1f4f9",sprite:"objects"},{names:["movie_camera"],keywords:["camera","cinema","movie"],unicode:"1f3a5",sprite:"objects"},{names:["film_projector","projector"],keywords:["cinema","film","movie","projector","video"],unicode:"1f4fd",
+sprite:"objects"},{names:["film_frames"],keywords:["cinema","film","frames","movie"],unicode:"1f39e",sprite:"objects"},{names:["telephone_receiver"],keywords:["phone","receiver","telephone"],unicode:"1f4de",sprite:"objects"},{names:["telephone"],keywords:["phone"],unicode:"260e",sprite:"objects"},{names:["pager"],keywords:["pager"],unicode:"1f4df",sprite:"objects"},{names:["fax"],keywords:["fax"],unicode:"1f4e0",sprite:"objects"},{names:["tv"],keywords:["tv","video"],unicode:"1f4fa",sprite:"objects"},
+{names:["radio"],keywords:["video"],unicode:"1f4fb",sprite:"objects"},{names:["studio_microphone","microphone2"],keywords:["mic","microphone","music","studio"],unicode:"1f399",sprite:"objects"},{names:["level_slider"],keywords:["level","music","slider"],unicode:"1f39a",sprite:"objects"},{names:["control_knobs"],keywords:["control","knobs","music"],unicode:"1f39b",sprite:"objects"},{names:["stopwatch"],keywords:["clock"],unicode:"23f1",sprite:"objects"},{names:["timer_clock","timer"],keywords:["clock",
+"timer"],unicode:"23f2",sprite:"objects"},{names:["alarm_clock"],keywords:["alarm","clock"],unicode:"23f0",sprite:"objects"},{names:["mantlepiece_clock","clock"],keywords:["clock"],unicode:"1f570",sprite:"objects"},{names:["hourglass"],keywords:["sand","timer"],unicode:"231b",sprite:"objects"},{names:["hourglass_flowing_sand"],keywords:["hourglass","sand","timer"],unicode:"23f3",sprite:"objects"},{names:["satellite"],keywords:["antenna","dish","satellite"],unicode:"1f4e1",sprite:"objects"},{names:["battery"],
+keywords:["battery"],unicode:"1f50b",sprite:"objects"},{names:["electric_plug"],keywords:["electric","electricity","plug"],unicode:"1f50c",sprite:"objects"},{names:["bulb"],keywords:["bulb","comic","electric","idea","light"],unicode:"1f4a1",sprite:"objects"},{names:["flashlight"],keywords:["electric","light","tool","torch"],unicode:"1f526",sprite:"objects"},{names:["candle"],keywords:["light"],unicode:"1f56f",sprite:"objects"},{names:["wastebasket"],keywords:["wastebasket"],unicode:"1f5d1",sprite:"objects"},
+{names:["oil_drum","oil"],keywords:["drum","oil"],unicode:"1f6e2",sprite:"objects"},{names:["money_with_wings"],keywords:"bank banknote bill dollar fly money note wings".split(" "),unicode:"1f4b8",sprite:"objects"},{names:["dollar"],keywords:"bank banknote bill currency dollar money note".split(" "),unicode:"1f4b5",sprite:"objects"},{names:["yen"],keywords:"bank banknote bill currency money note yen".split(" "),unicode:"1f4b4",sprite:"objects"},{names:["euro"],keywords:"bank banknote bill currency euro money note".split(" "),
+unicode:"1f4b6",sprite:"objects"},{names:["pound"],keywords:"bank banknote bill currency money note pound".split(" "),unicode:"1f4b7",sprite:"objects"},{names:["moneybag"],keywords:["bag","dollar","money","moneybag"],unicode:"1f4b0",sprite:"objects"},{names:["credit_card"],keywords:["bank","card","credit","money"],unicode:"1f4b3",sprite:"objects"},{names:["gem"],keywords:["diamond","gem","jewel"],unicode:"1f48e",sprite:"objects"},{names:["wrench"],keywords:["spanner","tool","wrench"],unicode:"1f527",
+sprite:"objects"},{names:["hammer"],keywords:["tool"],unicode:"1f528",sprite:"objects"},{names:["hammer_and_wrench","tools"],keywords:["hammer","spanner","tool","wrench"],unicode:"1f6e0",sprite:"objects"},{names:["pick"],keywords:["mining","tool"],unicode:"26cf",sprite:"objects"},{names:["nut_and_bolt"],keywords:["bolt","nut","tool"],unicode:"1f529",sprite:"objects"},{names:["chains"],keywords:["chain"],unicode:"26d3",sprite:"objects"},{names:["gun"],keywords:["gun","handgun","revolver","tool","weapon"],
+unicode:"1f52b",sprite:"objects"},{names:["bomb"],keywords:["comic"],unicode:"1f4a3",sprite:"objects"},{names:["knife"],keywords:["cooking","hocho","knife","tool","weapon"],unicode:"1f52a",sprite:"objects"},{names:["dagger_knife","dagger"],keywords:["knife","weapon"],unicode:"1f5e1",sprite:"objects"},{names:["shield"],keywords:["weapon"],unicode:"1f6e1",sprite:"objects"},{names:["smoking"],keywords:["smoking"],unicode:"1f6ac",sprite:"objects"},{names:["coffin"],keywords:["death"],unicode:"26b0",sprite:"objects"},
+{names:["funeral_urn","urn"],keywords:["ashes","death","funeral","urn"],unicode:"26b1",sprite:"objects"},{names:["amphora"],keywords:"Aquarius cooking drink jug tool weapon zodiac".split(" "),unicode:"1f3fa",sprite:"objects"},{names:["crystal_ball"],keywords:"ball;crystal;fairy tale;fantasy;fortune;tool".split(";"),unicode:"1f52e",sprite:"objects"},{names:["prayer_beads"],keywords:["beads","clothing","necklace","prayer","religion"],unicode:"1f4ff",sprite:"objects"},{names:["barber"],keywords:["barber",
+"haircut","pole"],unicode:"1f488",sprite:"objects"},{names:["telescope"],keywords:["science","tool"],unicode:"1f52d",sprite:"objects"},{names:["microscope"],keywords:["science","tool"],unicode:"1f52c",sprite:"objects"},{names:["hole"],keywords:["hole"],unicode:"1f573",sprite:"objects"},{names:["pill"],keywords:["doctor","medicine","sick"],unicode:"1f48a",sprite:"objects"},{names:["syringe"],keywords:"doctor medicine needle shot sick tool".split(" "),unicode:"1f489",sprite:"objects"},{names:["thermometer"],
+keywords:["weather"],unicode:"1f321",sprite:"objects"},{names:["toilet"],keywords:["toilet"],unicode:"1f6bd",sprite:"objects"},{names:["potable_water"],keywords:["drinking","potable","water"],unicode:"1f6b0",sprite:"objects"},{names:["shower"],keywords:["water"],unicode:"1f6bf",sprite:"objects"},{names:["bathtub"],keywords:["bath"],unicode:"1f6c1",sprite:"objects"},{names:["bath"],keywords:["bath","bathtub"],unicode:"1f6c0",sprite:"objects"},{names:["bath_tone1"],keywords:["bath","bathtub","light skin tone"],
+unicode:"1f6c0-1f3fb",sprite:"objects"},{names:["bath_tone2"],keywords:["bath","bathtub","medium-light skin tone"],unicode:"1f6c0-1f3fc",sprite:"objects"},{names:["bath_tone3"],keywords:["bath","bathtub","medium skin tone"],unicode:"1f6c0-1f3fd",sprite:"objects"},{names:["bath_tone4"],keywords:["bath","bathtub","medium-dark skin tone"],unicode:"1f6c0-1f3fe",sprite:"objects"},{names:["bath_tone5"],keywords:["bath","bathtub","dark skin tone"],unicode:"1f6c0-1f3ff",sprite:"objects"},{names:["bellhop_bell",
+"bellhop"],keywords:["bell","bellhop","hotel"],unicode:"1f6ce",sprite:"objects"},{names:["key"],keywords:["lock","password"],unicode:"1f511",sprite:"objects"},{names:["old_key","key2"],keywords:["clue","key","lock","old"],unicode:"1f5dd",sprite:"objects"},{names:["door"],keywords:["door"],unicode:"1f6aa",sprite:"objects"},{names:["couch_and_lamp","couch"],keywords:["couch","hotel","lamp"],unicode:"1f6cb",sprite:"objects"},{names:["bed"],keywords:["hotel","sleep"],unicode:"1f6cf",sprite:"objects"},
+{names:["sleeping_accommodation"],keywords:["hotel","sleep"],unicode:"1f6cc",sprite:"objects"},{names:["person_in_bed_light_skin_tone","person_in_bed_tone1"],keywords:["hotel","light skin tone","sleep"],unicode:"1f6cc-1f3fb",sprite:"objects"},{names:["person_in_bed_medium_light_skin_tone","person_in_bed_tone2"],keywords:["hotel","medium-light skin tone","sleep"],unicode:"1f6cc-1f3fc",sprite:"objects"},{names:["person_in_bed_medium_skin_tone","person_in_bed_tone3"],keywords:["hotel","medium skin tone",
+"sleep"],unicode:"1f6cc-1f3fd",sprite:"objects"},{names:["person_in_bed_medium_dark_skin_tone","person_in_bed_tone4"],keywords:["hotel","medium-dark skin tone","sleep"],unicode:"1f6cc-1f3fe",sprite:"objects"},{names:["person_in_bed_dark_skin_tone","person_in_bed_tone5"],keywords:["dark skin tone","hotel","sleep"],unicode:"1f6cc-1f3ff",sprite:"objects"},{names:["frame_with_picture","frame_photo"],keywords:["art","frame","museum","painting","picture"],unicode:"1f5bc",sprite:"objects"},{names:["shopping_bags"],
+keywords:["bag","hotel","shopping"],unicode:"1f6cd",sprite:"objects"},{names:["shopping_trolley","shopping_cart"],keywords:["cart","shopping","trolley"],unicode:"1f6d2",sprite:"objects"},{names:["gift"],keywords:["box","celebration","gift","present","wrapped"],unicode:"1f381",sprite:"objects"},{names:["balloon"],keywords:["celebration"],unicode:"1f388",sprite:"objects"},{names:["flags"],keywords:["carp","celebration","streamer"],unicode:"1f38f",sprite:"objects"},{names:["ribbon"],keywords:["celebration"],
+unicode:"1f380",sprite:"objects"},{names:["confetti_ball"],keywords:["ball","celebration","confetti"],unicode:"1f38a",sprite:"objects"},{names:["tada"],keywords:["celebration","party","popper","tada"],unicode:"1f389",sprite:"objects"},{names:["dolls"],keywords:["Japanese","celebration","doll","festival"],unicode:"1f38e",sprite:"objects"},{names:["izakaya_lantern"],keywords:["bar","lantern","light","red"],unicode:"1f3ee",sprite:"objects"},{names:["wind_chime"],keywords:["bell","celebration","chime",
+"wind"],unicode:"1f390",sprite:"objects"},{names:["envelope_with_arrow"],keywords:"arrow down e-mail email envelope letter mail outgoing sent".split(" "),unicode:"1f4e9",sprite:"objects"},{names:["incoming_envelope"],keywords:"e-mail email envelope incoming letter mail receive".split(" "),unicode:"1f4e8",sprite:"objects"},{names:["email","e-mail"],keywords:["email","letter","mail"],unicode:"1f4e7",sprite:"objects"},{names:["love_letter"],keywords:["heart","letter","love","mail"],unicode:"1f48c",sprite:"objects"},
+{names:["inbox_tray"],keywords:"box inbox letter mail receive tray".split(" "),unicode:"1f4e5",sprite:"objects"},{names:["outbox_tray"],keywords:"box letter mail outbox sent tray".split(" "),unicode:"1f4e4",sprite:"objects"},{names:["package"],keywords:["box","parcel"],unicode:"1f4e6",sprite:"objects"},{names:["label"],keywords:["label"],unicode:"1f3f7",sprite:"objects"},{names:["mailbox_closed"],keywords:["closed","lowered","mail","mailbox","postbox"],unicode:"1f4ea",sprite:"objects"},{names:["mailbox"],
+keywords:["closed","mail","mailbox","postbox"],unicode:"1f4eb",sprite:"objects"},{names:["mailbox_with_mail"],keywords:["mail","mailbox","open","postbox"],unicode:"1f4ec",sprite:"objects"},{names:["mailbox_with_no_mail"],keywords:["lowered","mail","mailbox","open","postbox"],unicode:"1f4ed",sprite:"objects"},{names:["postbox"],keywords:["mail","mailbox"],unicode:"1f4ee",sprite:"objects"},{names:["postal_horn"],keywords:["horn","post","postal"],unicode:"1f4ef",sprite:"objects"},{names:["scroll"],keywords:["paper"],
+unicode:"1f4dc",sprite:"objects"},{names:["page_with_curl"],keywords:["curl","document","page"],unicode:"1f4c3",sprite:"objects"},{names:["page_facing_up"],keywords:["document","page"],unicode:"1f4c4",sprite:"objects"},{names:["bookmark_tabs"],keywords:["bookmark","mark","marker","tabs"],unicode:"1f4d1",sprite:"objects"},{names:["bar_chart"],keywords:["bar","chart","graph"],unicode:"1f4ca",sprite:"objects"},{names:["chart_with_upwards_trend"],keywords:["chart","graph","growth","trend","upward"],unicode:"1f4c8",
+sprite:"objects"},{names:["chart_with_downwards_trend"],keywords:["chart","down","graph","trend"],unicode:"1f4c9",sprite:"objects"},{names:["spiral_note_pad","notepad_spiral"],keywords:["note","pad","spiral"],unicode:"1f5d2",sprite:"objects"},{names:["spiral_calendar_pad","calendar_spiral"],keywords:["calendar","pad","spiral"],unicode:"1f5d3",sprite:"objects"},{names:["calendar"],keywords:["calendar"],unicode:"1f4c6",sprite:"objects"},{names:["date"],keywords:["date"],unicode:"1f4c5",sprite:"objects"},
+{names:["card_index"],keywords:["card","index","rolodex"],unicode:"1f4c7",sprite:"objects"},{names:["card_file_box","card_box"],keywords:["box","card","file"],unicode:"1f5c3",sprite:"objects"},{names:["ballot_box_with_ballot","ballot_box"],keywords:["ballot","box"],unicode:"1f5f3",sprite:"objects"},{names:["file_cabinet"],keywords:["cabinet","file","filing"],unicode:"1f5c4",sprite:"objects"},{names:["clipboard"],keywords:["clipboard"],unicode:"1f4cb",sprite:"objects"},{names:["file_folder"],keywords:["file",
+"folder"],unicode:"1f4c1",sprite:"objects"},{names:["open_file_folder"],keywords:["file","folder","open"],unicode:"1f4c2",sprite:"objects"},{names:["card_index_dividers","dividers"],keywords:["card","dividers","index"],unicode:"1f5c2",sprite:"objects"},{names:["rolled_up_newspaper","newspaper2"],keywords:["news","newspaper","paper","rolled"],unicode:"1f5de",sprite:"objects"},{names:["newspaper"],keywords:["news","paper"],unicode:"1f4f0",sprite:"objects"},{names:["notebook"],keywords:["notebook"],
+unicode:"1f4d3",sprite:"objects"},{names:["notebook_with_decorative_cover"],keywords:["book","cover","decorated","notebook"],unicode:"1f4d4",sprite:"objects"},{names:["ledger"],keywords:["notebook"],unicode:"1f4d2",sprite:"objects"},{names:["closed_book"],keywords:["book","closed"],unicode:"1f4d5",sprite:"objects"},{names:["green_book"],keywords:["book","green"],unicode:"1f4d7",sprite:"objects"},{names:["blue_book"],keywords:["blue","book"],unicode:"1f4d8",sprite:"objects"},{names:["orange_book"],
+keywords:["book","orange"],unicode:"1f4d9",sprite:"objects"},{names:["books"],keywords:["book"],unicode:"1f4da",sprite:"objects"},{names:["book"],keywords:["book","open"],unicode:"1f4d6",sprite:"objects"},{names:["bookmark"],keywords:["mark"],unicode:"1f516",sprite:"objects"},{names:["link"],keywords:["link"],unicode:"1f517",sprite:"objects"},{names:["paperclip"],keywords:["paperclip"],unicode:"1f4ce",sprite:"objects"},{names:["linked_paperclips","paperclips"],keywords:["link","paperclip"],unicode:"1f587",
+sprite:"objects"},{names:["triangular_ruler"],keywords:["ruler","set","triangle"],unicode:"1f4d0",sprite:"objects"},{names:["straight_ruler"],keywords:["ruler","straight edge"],unicode:"1f4cf",sprite:"objects"},{names:["pushpin"],keywords:["pin"],unicode:"1f4cc",sprite:"objects"},{names:["round_pushpin"],keywords:["pin","pushpin"],unicode:"1f4cd",sprite:"objects"},{names:["lower_left_ballpoint_pen","pen_ballpoint"],keywords:["ballpoint"],unicode:"1f58a",sprite:"objects"},{names:["lower_left_fountain_pen",
+"pen_fountain"],keywords:["fountain","pen"],unicode:"1f58b",sprite:"objects"},{names:["lower_left_paintbrush","paintbrush"],keywords:["painting"],unicode:"1f58c",sprite:"objects"},{names:["lower_left_crayon","crayon"],keywords:["crayon"],unicode:"1f58d",sprite:"objects"},{names:["pencil"],keywords:["pencil"],unicode:"1f4dd",sprite:"objects"},{names:["pencil2"],keywords:["pencil"],unicode:"270f",sprite:"objects"},{names:["mag"],keywords:["glass","magnifying","search","tool"],unicode:"1f50d",sprite:"objects"},
+{names:["mag_right"],keywords:["glass","magnifying","search","tool"],unicode:"1f50e",sprite:"objects"},{names:["lock_with_ink_pen"],keywords:["ink","lock","nib","pen","privacy"],unicode:"1f50f",sprite:"objects"},{names:["closed_lock_with_key"],keywords:["closed","key","lock","secure"],unicode:"1f510",sprite:"objects"},{names:["lock"],keywords:["closed"],unicode:"1f512",sprite:"objects"},{names:["unlock"],keywords:["lock","open","unlock"],unicode:"1f513",sprite:"objects"},{names:["yellow_heart"],keywords:["yellow"],
+unicode:"1f49b",sprite:"symbols"},{names:["green_heart"],keywords:["green"],unicode:"1f49a",sprite:"symbols"},{names:["blue_heart"],keywords:["blue"],unicode:"1f499",sprite:"symbols"},{names:["purple_heart"],keywords:["purple"],unicode:"1f49c",sprite:"symbols"},{names:["black_heart"],keywords:["black","evil","wicked"],unicode:"1f5a4",sprite:"symbols"},{names:["broken_heart"],keywords:["break","broken"],unicode:"1f494",sprite:"symbols"},{names:["two_hearts"],keywords:["love"],unicode:"1f495",sprite:"symbols"},
+{names:["revolving_hearts"],keywords:["revolving"],unicode:"1f49e",sprite:"symbols"},{names:["heartbeat"],keywords:["beating","heartbeat","pulsating"],unicode:"1f493",sprite:"symbols"},{names:["heartpulse"],keywords:["excited","growing","nervous","pulse"],unicode:"1f497",sprite:"symbols"},{names:["sparkling_heart"],keywords:["excited","sparkle"],unicode:"1f496",sprite:"symbols"},{names:["cupid"],keywords:["arrow","cupid"],unicode:"1f498",sprite:"symbols"},{names:["gift_heart"],keywords:["ribbon",
+"valentine"],unicode:"1f49d",sprite:"symbols"},{names:["heart_decoration"],keywords:["heart"],unicode:"1f49f",sprite:"symbols"},{names:["peace_symbol","peace"],keywords:["peace"],unicode:"262e",sprite:"symbols"},{names:["latin_cross","cross"],keywords:["Christian","cross","religion"],unicode:"271d",sprite:"symbols"},{names:["star_and_crescent"],keywords:["Muslim","islam","religion"],unicode:"262a",sprite:"symbols"},{names:["om_symbol"],keywords:["Hindu","religion"],unicode:"1f549",sprite:"symbols"},
+{names:["six_pointed_star"],keywords:["fortune","star"],unicode:"1f52f",sprite:"symbols"},{names:["menorah"],keywords:["candelabrum","candlestick","religion"],unicode:"1f54e",sprite:"symbols"},{names:["yin_yang"],keywords:["religion","tao","taoist","yang","yin"],unicode:"262f",sprite:"symbols"},{names:["worship_symbol","place_of_worship"],keywords:["religion","worship"],unicode:"1f6d0",sprite:"symbols"},{names:["ophiuchus"],keywords:["bearer","serpent","snake","zodiac"],unicode:"26ce",sprite:"symbols"},
+{names:["gemini"],keywords:["twins","zodiac"],unicode:"264a",sprite:"symbols"},{names:["cancer"],keywords:["crab","zodiac"],unicode:"264b",sprite:"symbols"},{names:["leo"],keywords:["lion","zodiac"],unicode:"264c",sprite:"symbols"},{names:["virgo"],keywords:["zodiac"],unicode:"264d",sprite:"symbols"},{names:["libra"],keywords:["balance","justice","scales","zodiac"],unicode:"264e",sprite:"symbols"},{names:["scorpius"],keywords:["scorpio","scorpion","zodiac"],unicode:"264f",sprite:"symbols"},{names:["id"],
+keywords:["id","identity"],unicode:"1f194",sprite:"symbols"},{names:["atom_symbol","atom"],keywords:["atheist","atom"],unicode:"269b",sprite:"symbols"},{names:["accept"],keywords:["Japanese","ideograph","\u201cacceptable\u201d","\u53ef"],unicode:"1f251",sprite:"symbols"},{names:["mobile_phone_off"],keywords:["cell","mobile","off","phone","telephone"],unicode:"1f4f4",sprite:"symbols"},{names:["vibration_mode"],keywords:"cell mobile mode phone telephone vibration".split(" "),unicode:"1f4f3",sprite:"symbols"},
+{names:["u6709"],keywords:["Japanese","ideograph","\u201cnot free of charge\u201d","\u6709"],unicode:"1f236",sprite:"symbols"},{names:["u7121"],keywords:["Japanese","ideograph","\u201cfree of charge\u201d","\u7121"],unicode:"1f21a",sprite:"symbols"},{names:["u7533"],keywords:["Japanese","ideograph","\u201capplication\u201d","\u7533"],unicode:"1f238",sprite:"symbols"},{names:["u55b6"],keywords:["Japanese","ideograph","\u201copen for business\u201d","\u55b6"],unicode:"1f23a",sprite:"symbols"},{names:["u6708"],
+keywords:["Japanese","ideograph","\u201cmonthly amount\u201d","\u6708"],unicode:"1f237",sprite:"symbols"},{names:["vs"],keywords:["versus","vs"],unicode:"1f19a",sprite:"symbols"},{names:["white_flower"],keywords:["flower"],unicode:"1f4ae",sprite:"symbols"},{names:["ideograph_advantage"],keywords:["Japanese","ideograph","\u201cbargain\u201d","\u5f97"],unicode:"1f250",sprite:"symbols"},{names:["u5408"],keywords:["Japanese","ideograph","\u201cpassing grade\u201d","\u5408"],unicode:"1f234",sprite:"symbols"},
+{names:["u6e80"],keywords:["Japanese","ideograph","\u201cno vacancy\u201d","\u6e80"],unicode:"1f235",sprite:"symbols"},{names:["u5272"],keywords:["Japanese","ideograph","\u201cdiscount\u201d","\u5272"],unicode:"1f239",sprite:"symbols"},{names:["u7981"],keywords:["Japanese","ideograph","\u201cprohibited\u201d","\u7981"],unicode:"1f232",sprite:"symbols"},{names:["a"],keywords:["a","blood type"],unicode:"1f170",sprite:"symbols"},{names:["b"],keywords:["b","blood type"],unicode:"1f171",sprite:"symbols"},
+{names:["ab"],keywords:["ab","blood type"],unicode:"1f18e",sprite:"symbols"},{names:["cl"],keywords:["cl"],unicode:"1f191",sprite:"symbols"},{names:["o2"],keywords:["blood type","o"],unicode:"1f17e",sprite:"symbols"},{names:["sos"],keywords:["help","sos"],unicode:"1f198",sprite:"symbols"},{names:["x"],keywords:["cancel","mark","multiplication","multiply","x"],unicode:"274c",sprite:"symbols"},{names:["o"],keywords:["circle","o"],unicode:"2b55",sprite:"symbols"},{names:["stop_sign","octagonal_sign"],
+keywords:["octagonal","sign","stop"],unicode:"1f6d1",sprite:"symbols"},{names:["no_entry"],keywords:"entry forbidden no not prohibited traffic".split(" "),unicode:"26d4",sprite:"symbols"},{names:["name_badge"],keywords:["badge","name"],unicode:"1f4db",sprite:"symbols"},{names:["no_entry_sign"],keywords:["entry","forbidden","no","not"],unicode:"1f6ab",sprite:"symbols"},{names:["100"],keywords:["100","full","hundred","score"],unicode:"1f4af",sprite:"symbols"},{names:["anger"],keywords:["angry","comic",
+"mad"],unicode:"1f4a2",sprite:"symbols"},{names:["no_pedestrians"],keywords:["forbidden","no","not","pedestrian","prohibited"],unicode:"1f6b7",sprite:"symbols"},{names:["do_not_litter"],keywords:["forbidden","litter","no","not","prohibited"],unicode:"1f6af",sprite:"symbols"},{names:["no_bicycles"],keywords:"bicycle bike forbidden no not prohibited".split(" "),unicode:"1f6b3",sprite:"symbols"},{names:["non-potable_water"],keywords:["non-drinking","non-potable","water"],unicode:"1f6b1",sprite:"symbols"},
+{names:["underage"],keywords:"18;age restriction;eighteen;forbidden;no;not;prohibited;underage".split(";"),unicode:"1f51e",sprite:"symbols"},{names:["no_mobile_phones"],keywords:"cell forbidden mobile no not phone prohibited telephone".split(" "),unicode:"1f4f5",sprite:"symbols"},{names:["no_smoking"],keywords:["forbidden","no","not","prohibited","smoking"],unicode:"1f6ad",sprite:"symbols"},{names:["bangbang"],keywords:["bangbang","exclamation","mark","punctuation"],unicode:"203c",sprite:"symbols"},
+{names:["low_brightness"],keywords:["brightness","dim","low"],unicode:"1f505",sprite:"symbols"},{names:["high_brightness"],keywords:["bright","brightness"],unicode:"1f506",sprite:"symbols"},{names:["part_alternation_mark"],keywords:["mark","part"],unicode:"303d",sprite:"symbols"},{names:["warning"],keywords:["warning"],unicode:"26a0",sprite:"symbols"},{names:["children_crossing"],keywords:["child","crossing","pedestrian","traffic"],unicode:"1f6b8",sprite:"symbols"},{names:["trident"],keywords:["anchor",
+"emblem","ship","tool","trident"],unicode:"1f531",sprite:"symbols"},{names:["fleur-de-lis"],keywords:["fleur-de-lis"],unicode:"269c",sprite:"symbols"},{names:["beginner"],keywords:"Japanese beginner chevron green leaf tool yellow".split(" "),unicode:"1f530",sprite:"symbols"},{names:["recycle"],keywords:["recycle"],unicode:"267b",sprite:"symbols"},{names:["u6307"],keywords:["Japanese","ideograph","\u201creserved\u201d","\u6307"],unicode:"1f22f",sprite:"symbols"},{names:["chart"],keywords:"bank chart currency graph growth market money rise trend upward yen".split(" "),
+unicode:"1f4b9",sprite:"symbols"},{names:["negative_squared_cross_mark"],keywords:["mark","square"],unicode:"274e",sprite:"symbols"},{names:["globe_with_meridians"],keywords:["earth","globe","meridians","world"],unicode:"1f310",sprite:"symbols"},{names:["diamond_shape_with_a_dot_inside"],keywords:["comic","diamond","geometric","inside"],unicode:"1f4a0",sprite:"symbols"},{names:["m"],keywords:["circle","m"],unicode:"24c2",sprite:"symbols"},{names:["cyclone"],keywords:["dizzy","twister","typhoon"],
+unicode:"1f300",sprite:"symbols"},{names:["zzz"],keywords:["comic","sleep"],unicode:"1f4a4",sprite:"symbols"},{names:["atm"],keywords:["atm","automated","bank","teller"],unicode:"1f3e7",sprite:"symbols"},{names:["wc"],keywords:["closet","lavatory","restroom","water","wc"],unicode:"1f6be",sprite:"symbols"},{names:["wheelchair"],keywords:["access"],unicode:"267f",sprite:"symbols"},{names:["parking"],keywords:["parking"],unicode:"1f17f",sprite:"symbols"},{names:["u7a7a"],keywords:["Japanese","ideograph",
+"\u201cvacancy\u201d","\u7a7a"],unicode:"1f233",sprite:"symbols"},{names:["sa"],keywords:["Japanese","katakana","\u201cservice charge\u201d","\u30b5"],unicode:"1f202",sprite:"symbols"},{names:["passport_control"],keywords:["control","passport"],unicode:"1f6c2",sprite:"symbols"},{names:["customs"],keywords:["customs"],unicode:"1f6c3",sprite:"symbols"},{names:["baggage_claim"],keywords:["baggage","claim"],unicode:"1f6c4",sprite:"symbols"},{names:["left_luggage"],keywords:["baggage","locker","luggage"],
+unicode:"1f6c5",sprite:"symbols"},{names:["mens"],keywords:["lavatory","man","restroom","wc"],unicode:"1f6b9",sprite:"symbols"},{names:["womens"],keywords:["lavatory","restroom","wc","woman"],unicode:"1f6ba",sprite:"symbols"},{names:["baby_symbol"],keywords:["baby","changing"],unicode:"1f6bc",sprite:"symbols"},{names:["restroom"],keywords:["WC","lavatory","restroom"],unicode:"1f6bb",sprite:"symbols"},{names:["put_litter_in_its_place"],keywords:["litter","litter bin"],unicode:"1f6ae",sprite:"symbols"},
+{names:["cinema"],keywords:["camera","film","movie"],unicode:"1f3a6",sprite:"symbols"},{names:["signal_strength"],keywords:"antenna bar cell mobile phone signal telephone".split(" "),unicode:"1f4f6",sprite:"symbols"},{names:["koko"],keywords:["Japanese","katakana","\u201chere\u201d","\u30b3\u30b3"],unicode:"1f201",sprite:"symbols"},{names:["symbols"],keywords:["input","\u3012\u266a&amp;%"],unicode:"1f523",sprite:"symbols"},{names:["abc"],keywords:["abc","alphabet","input","latin","letters"],unicode:"1f524",
+sprite:"symbols"},{names:["abcd"],keywords:["abcd","input","latin","letters","lowercase"],unicode:"1f521",sprite:"symbols"},{names:["capital_abcd"],keywords:["ABCD","input","latin","letters","uppercase"],unicode:"1f520",sprite:"symbols"},{names:["ng"],keywords:["ng"],unicode:"1f196",sprite:"symbols"},{names:["ok"],keywords:["OK"],unicode:"1f197",sprite:"symbols"},{names:["up"],keywords:["mark","up"],unicode:"1f199",sprite:"symbols"},{names:["cool"],keywords:["cool"],unicode:"1f192",sprite:"symbols"},
+{names:["new"],keywords:["new"],unicode:"1f195",sprite:"symbols"},{names:["free"],keywords:["free"],unicode:"1f193",sprite:"symbols"},{names:["zero"],keywords:["keycap"],unicode:"0030-20e3",sprite:"symbols"},{names:["one"],keywords:["keycap"],unicode:"0031-20e3",sprite:"symbols"},{names:["two"],keywords:["keycap"],unicode:"0032-20e3",sprite:"symbols"},{names:["three"],keywords:["keycap"],unicode:"0033-20e3",sprite:"symbols"},{names:["four"],keywords:["keycap"],unicode:"0034-20e3",sprite:"symbols"},
+{names:["five"],keywords:["keycap"],unicode:"0035-20e3",sprite:"symbols"},{names:["six"],keywords:["keycap"],unicode:"0036-20e3",sprite:"symbols"},{names:["seven"],keywords:["keycap"],unicode:"0037-20e3",sprite:"symbols"},{names:["eight"],keywords:["keycap"],unicode:"0038-20e3",sprite:"symbols"},{names:["nine"],keywords:["keycap"],unicode:"0039-20e3",sprite:"symbols"},{names:["keycap_ten"],keywords:["keycap 10"],unicode:"1f51f",sprite:"symbols"},{names:["1234"],keywords:["1234","input","numbers"],
+unicode:"1f522",sprite:"symbols"},{names:["hash"],keywords:["keycap"],unicode:"0023-20e3",sprite:"symbols"},{names:["keycap_asterisk","asterisk"],keywords:["keycap"],unicode:"002a-20e3",sprite:"symbols"},{names:["arrow_forward"],keywords:["arrow","play","right","triangle"],unicode:"25b6",sprite:"symbols"},{names:["double_vertical_bar","pause_button"],keywords:["bar","double","pause","vertical"],unicode:"23f8",sprite:"symbols"},{names:["play_pause"],keywords:["arrow","pause","play","right","triangle"],
+unicode:"23ef",sprite:"symbols"},{names:["stop_button"],keywords:["square","stop"],unicode:"23f9",sprite:"symbols"},{names:["record_button"],keywords:["circle","record"],unicode:"23fa",sprite:"symbols"},{names:["eject_symbol","eject"],keywords:["eject"],unicode:"23cf",sprite:"symbols"},{names:["next_track","track_next"],keywords:["arrow","next scene","next track","triangle"],unicode:"23ed",sprite:"symbols"},{names:["previous_track","track_previous"],keywords:["arrow","previous scene","previous track",
+"triangle"],unicode:"23ee",sprite:"symbols"},{names:["fast_forward"],keywords:["arrow","double","fast","forward"],unicode:"23e9",sprite:"symbols"},{names:["rewind"],keywords:["arrow","double","rewind"],unicode:"23ea",sprite:"symbols"},{names:["arrow_double_up"],keywords:["arrow","double"],unicode:"23eb",sprite:"symbols"},{names:["arrow_double_down"],keywords:["arrow","double","down"],unicode:"23ec",sprite:"symbols"},{names:["arrow_backward"],keywords:["arrow","left","reverse","triangle"],unicode:"25c0",
+sprite:"symbols"},{names:["arrow_up_small"],keywords:["arrow","button","red"],unicode:"1f53c",sprite:"symbols"},{names:["arrow_down_small"],keywords:["arrow","button","down","red"],unicode:"1f53d",sprite:"symbols"},{names:["arrow_right"],keywords:["arrow","cardinal","direction","east"],unicode:"27a1",sprite:"symbols"},{names:["arrow_left"],keywords:["arrow","cardinal","direction","west"],unicode:"2b05",sprite:"symbols"},{names:["arrow_up"],keywords:["arrow","cardinal","direction","north"],unicode:"2b06",
+sprite:"symbols"},{names:["arrow_down"],keywords:["arrow","cardinal","direction","down","south"],unicode:"2b07",sprite:"symbols"},{names:["arrow_right_hook"],keywords:["arrow"],unicode:"21aa",sprite:"symbols"},{names:["leftwards_arrow_with_hook"],keywords:["arrow"],unicode:"21a9",sprite:"symbols"},{names:["twisted_rightwards_arrows"],keywords:["arrow","crossed"],unicode:"1f500",sprite:"symbols"},{names:["repeat"],keywords:["arrow","clockwise","repeat"],unicode:"1f501",sprite:"symbols"},{names:["repeat_one"],
+keywords:["arrow","clockwise","once"],unicode:"1f502",sprite:"symbols"},{names:["arrows_counterclockwise"],keywords:["anticlockwise","arrow","counterclockwise","withershins"],unicode:"1f504",sprite:"symbols"},{names:["arrows_clockwise"],keywords:["arrow","clockwise","reload"],unicode:"1f503",sprite:"symbols"},{names:["musical_note"],keywords:["music","note"],unicode:"1f3b5",sprite:"symbols"},{names:["notes"],keywords:["music","note","notes"],unicode:"1f3b6",sprite:"symbols"},{names:["heavy_dollar_sign"],
+keywords:["currency","dollar","money"],unicode:"1f4b2",sprite:"symbols"},{names:["currency_exchange"],keywords:["bank","currency","exchange","money"],unicode:"1f4b1",sprite:"symbols"},{names:["copyright"],keywords:["copyright"],unicode:"00a9",sprite:"symbols"},{names:["registered"],keywords:["registered"],unicode:"00ae",sprite:"symbols"},{names:["curly_loop"],keywords:["curl","loop"],unicode:"27b0",sprite:"symbols"},{names:["loop"],keywords:["curl","double","loop"],unicode:"27bf",sprite:"symbols"},
+{names:["end"],keywords:["arrow","end"],unicode:"1f51a",sprite:"symbols"},{names:["back"],keywords:["arrow","back"],unicode:"1f519",sprite:"symbols"},{names:["on"],keywords:["arrow","mark","on"],unicode:"1f51b",sprite:"symbols"},{names:["top"],keywords:["arrow","top","up"],unicode:"1f51d",sprite:"symbols"},{names:["soon"],keywords:["arrow","soon"],unicode:"1f51c",sprite:"symbols"},{names:["radio_button"],keywords:["button","geometric","radio"],unicode:"1f518",sprite:"symbols"},{names:["white_circle"],
+keywords:["circle","geometric"],unicode:"26aa",sprite:"symbols"},{names:["black_circle"],keywords:["circle","geometric"],unicode:"26ab",sprite:"symbols"},{names:["red_circle"],keywords:["circle","geometric","red"],unicode:"1f534",sprite:"symbols"},{names:["blue_circle"],keywords:["blue","circle","geometric"],unicode:"1f535",sprite:"symbols"},{names:["small_red_triangle"],keywords:["geometric","red"],unicode:"1f53a",sprite:"symbols"},{names:["small_red_triangle_down"],keywords:["down","geometric",
+"red"],unicode:"1f53b",sprite:"symbols"},{names:["small_orange_diamond"],keywords:["diamond","geometric","orange"],unicode:"1f538",sprite:"symbols"},{names:["small_blue_diamond"],keywords:["blue","diamond","geometric"],unicode:"1f539",sprite:"symbols"},{names:["large_orange_diamond"],keywords:["diamond","geometric","orange"],unicode:"1f536",sprite:"symbols"},{names:["large_blue_diamond"],keywords:["blue","diamond","geometric"],unicode:"1f537",sprite:"symbols"},{names:["white_square_button"],keywords:["button",
+"geometric","outlined","square"],unicode:"1f533",sprite:"symbols"},{names:["black_square_button"],keywords:["button","geometric","square"],unicode:"1f532",sprite:"symbols"},{names:["black_small_square"],keywords:["geometric","square"],unicode:"25aa",sprite:"symbols"},{names:["white_small_square"],keywords:["geometric","square"],unicode:"25ab",sprite:"symbols"},{names:["black_medium_small_square"],keywords:["geometric","square"],unicode:"25fe",sprite:"symbols"},{names:["white_medium_small_square"],
+keywords:["geometric","square"],unicode:"25fd",sprite:"symbols"},{names:["black_medium_square"],keywords:["geometric","square"],unicode:"25fc",sprite:"symbols"},{names:["white_medium_square"],keywords:["geometric","square"],unicode:"25fb",sprite:"symbols"},{names:["black_large_square"],keywords:["geometric","square"],unicode:"2b1b",sprite:"symbols"},{names:["white_large_square"],keywords:["geometric","square"],unicode:"2b1c",sprite:"symbols"},{names:["speaker"],keywords:["soft"],unicode:"1f508",sprite:"symbols"},
+{names:["mute"],keywords:["mute","quiet","silent","speaker"],unicode:"1f507",sprite:"symbols"},{names:["sound"],keywords:["medium"],unicode:"1f509",sprite:"symbols"},{names:["loud_sound"],keywords:["loud"],unicode:"1f50a",sprite:"symbols"},{names:["bell"],keywords:["bell"],unicode:"1f514",sprite:"symbols"},{names:["no_bell"],keywords:"bell forbidden mute no not prohibited quiet silent".split(" "),unicode:"1f515",sprite:"symbols"},{names:["mega"],keywords:["cheering"],unicode:"1f4e3",sprite:"symbols"},
+{names:["loudspeaker"],keywords:["loud","public address"],unicode:"1f4e2",sprite:"symbols"},{names:["left_speech_bubble","speech_left"],keywords:["dialog","speech"],unicode:"1f5e8",sprite:"symbols"},{names:["eye_in_speech_bubble"],keywords:["eye","speech bubble","witness"],unicode:"1f441-1f5e8",sprite:"symbols"},{names:["speech_balloon"],keywords:["balloon","bubble","comic","dialog","speech"],unicode:"1f4ac",sprite:"symbols"},{names:["thought_balloon"],keywords:["balloon","bubble","comic","thought"],
+unicode:"1f4ad",sprite:"symbols"},{names:["right_anger_bubble","anger_right"],keywords:["angry","balloon","bubble","mad"],unicode:"1f5ef",sprite:"symbols"},{names:["black_joker"],keywords:["card","game","wildcard"],unicode:"1f0cf",sprite:"symbols"},{names:["flower_playing_cards"],keywords:["Japanese","card","flower","game","playing"],unicode:"1f3b4",sprite:"symbols"},{names:["mahjong"],keywords:["game","mahjong","red"],unicode:"1f004",sprite:"symbols"},{names:["clock1"],keywords:"00 1 1:00 clock one o\u2019clock".split(" "),
+unicode:"1f550",sprite:"symbols"},{names:["clock2"],keywords:"00 2 2:00 clock o\u2019clock two".split(" "),unicode:"1f551",sprite:"symbols"},{names:["clock3"],keywords:"00 3 3:00 clock o\u2019clock three".split(" "),unicode:"1f552",sprite:"symbols"},{names:["clock4"],keywords:"00 4 4:00 clock four o\u2019clock".split(" "),unicode:"1f553",sprite:"symbols"},{names:["clock5"],keywords:"00 5 5:00 clock five o\u2019clock".split(" "),unicode:"1f554",sprite:"symbols"},{names:["clock6"],keywords:"00 6 6:00 clock o\u2019clock six".split(" "),
+unicode:"1f555",sprite:"symbols"},{names:["clock7"],keywords:"00 7 7:00 clock o\u2019clock seven".split(" "),unicode:"1f556",sprite:"symbols"},{names:["clock8"],keywords:"00 8 8:00 clock eight o\u2019clock".split(" "),unicode:"1f557",sprite:"symbols"},{names:["clock9"],keywords:"00 9 9:00 clock nine o\u2019clock".split(" "),unicode:"1f558",sprite:"symbols"},{names:["clock10"],keywords:"00 10 10:00 clock o\u2019clock ten".split(" "),unicode:"1f559",sprite:"symbols"},{names:["clock11"],keywords:"00 11 11:00 clock eleven o\u2019clock".split(" "),
+unicode:"1f55a",sprite:"symbols"},{names:["clock12"],keywords:"00 12 12:00 clock o\u2019clock twelve".split(" "),unicode:"1f55b",sprite:"symbols"},{names:["clock130"],keywords:"1 1:30 30 clock one thirty".split(" "),unicode:"1f55c",sprite:"symbols"},{names:["clock230"],keywords:"2 2:30 30 clock thirty two".split(" "),unicode:"1f55d",sprite:"symbols"},{names:["clock330"],keywords:"3 30 3:30 clock thirty three".split(" "),unicode:"1f55e",sprite:"symbols"},{names:["clock430"],keywords:"4 30 4:30 clock four thirty".split(" "),
+unicode:"1f55f",sprite:"symbols"},{names:["clock530"],keywords:"5 30 5:30 clock five thirty".split(" "),unicode:"1f560",sprite:"symbols"},{names:["clock630"],keywords:"6 30 6:30 clock six thirty".split(" "),unicode:"1f561",sprite:"symbols"},{names:["clock730"],keywords:"7 30 7:30 clock seven thirty".split(" "),unicode:"1f562",sprite:"symbols"},{names:["clock830"],keywords:"8 30 8:30 clock eight thirty".split(" "),unicode:"1f563",sprite:"symbols"},{names:["clock930"],keywords:"9 30 9:30 clock nine thirty".split(" "),
+unicode:"1f564",sprite:"symbols"},{names:["clock1030"],keywords:"10 10:30 30 clock ten thirty".split(" "),unicode:"1f565",sprite:"symbols"},{names:["clock1130"],keywords:"11 11:30 30 clock eleven thirty".split(" "),unicode:"1f566",sprite:"symbols"},{names:["clock1230"],keywords:"12 12:30 30 clock thirty twelve".split(" "),unicode:"1f567",sprite:"symbols"},{names:["flag_white","waving_white_flag"],keywords:["waving"],unicode:"1f3f3",sprite:"flags"},{names:["flag_black","waving_black_flag"],keywords:["waving"],
+unicode:"1f3f4",sprite:"flags"},{names:["checkered_flag"],keywords:["checkered","chequered","racing"],unicode:"1f3c1",sprite:"flags"},{names:["triangular_flag_on_post"],keywords:["post"],unicode:"1f6a9",sprite:"flags"},{names:["gay_pride_flag","rainbow_flag"],keywords:["rainbow"],unicode:"1f3f3-1f308",sprite:"flags"},{names:["flag_af","af"],keywords:["flag"],unicode:"1f1e6-1f1eb",sprite:"flags"},{names:["flag_ax","ax"],keywords:["flag"],unicode:"1f1e6-1f1fd",sprite:"flags"},{names:["flag_al","al"],
+keywords:["flag"],unicode:"1f1e6-1f1f1",sprite:"flags"},{names:["flag_dz","dz"],keywords:["flag"],unicode:"1f1e9-1f1ff",sprite:"flags"},{names:["flag_as","as"],keywords:["flag"],unicode:"1f1e6-1f1f8",sprite:"flags"},{names:["flag_ad","ad"],keywords:["flag"],unicode:"1f1e6-1f1e9",sprite:"flags"},{names:["flag_ao","ao"],keywords:["flag"],unicode:"1f1e6-1f1f4",sprite:"flags"},{names:["flag_ai","ai"],keywords:["flag"],unicode:"1f1e6-1f1ee",sprite:"flags"},{names:["flag_aq","aq"],keywords:["flag"],unicode:"1f1e6-1f1f6",
+sprite:"flags"},{names:["flag_ag","ag"],keywords:["flag"],unicode:"1f1e6-1f1ec",sprite:"flags"},{names:["flag_ar","ar"],keywords:["flag"],unicode:"1f1e6-1f1f7",sprite:"flags"},{names:["flag_am","am"],keywords:["flag"],unicode:"1f1e6-1f1f2",sprite:"flags"},{names:["flag_aw","aw"],keywords:["flag"],unicode:"1f1e6-1f1fc",sprite:"flags"},{names:["flag_au","au"],keywords:["flag"],unicode:"1f1e6-1f1fa",sprite:"flags"},{names:["flag_at","at"],keywords:["flag"],unicode:"1f1e6-1f1f9",sprite:"flags"},{names:["flag_az",
+"az"],keywords:["flag"],unicode:"1f1e6-1f1ff",sprite:"flags"},{names:["flag_bs","bs"],keywords:["flag"],unicode:"1f1e7-1f1f8",sprite:"flags"},{names:["flag_bh","bh"],keywords:["flag"],unicode:"1f1e7-1f1ed",sprite:"flags"},{names:["flag_bd","bd"],keywords:["flag"],unicode:"1f1e7-1f1e9",sprite:"flags"},{names:["flag_bb","bb"],keywords:["flag"],unicode:"1f1e7-1f1e7",sprite:"flags"},{names:["flag_by","by"],keywords:["flag"],unicode:"1f1e7-1f1fe",sprite:"flags"},{names:["flag_be","be"],keywords:["flag"],
+unicode:"1f1e7-1f1ea",sprite:"flags"},{names:["flag_bz","bz"],keywords:["flag"],unicode:"1f1e7-1f1ff",sprite:"flags"},{names:["flag_bj","bj"],keywords:["flag"],unicode:"1f1e7-1f1ef",sprite:"flags"},{names:["flag_bm","bm"],keywords:["flag"],unicode:"1f1e7-1f1f2",sprite:"flags"},{names:["flag_bt","bt"],keywords:["flag"],unicode:"1f1e7-1f1f9",sprite:"flags"},{names:["flag_bo","bo"],keywords:["flag"],unicode:"1f1e7-1f1f4",sprite:"flags"},{names:["flag_ba","ba"],keywords:["flag"],unicode:"1f1e7-1f1e6",
+sprite:"flags"},{names:["flag_bw","bw"],keywords:["flag"],unicode:"1f1e7-1f1fc",sprite:"flags"},{names:["flag_br","br"],keywords:["flag"],unicode:"1f1e7-1f1f7",sprite:"flags"},{names:["flag_io","io"],keywords:["flag"],unicode:"1f1ee-1f1f4",sprite:"flags"},{names:["flag_vg","vg"],keywords:["flag"],unicode:"1f1fb-1f1ec",sprite:"flags"},{names:["flag_bn","bn"],keywords:["flag"],unicode:"1f1e7-1f1f3",sprite:"flags"},{names:["flag_bg","bg"],keywords:["flag"],unicode:"1f1e7-1f1ec",sprite:"flags"},{names:["flag_bf",
+"bf"],keywords:["flag"],unicode:"1f1e7-1f1eb",sprite:"flags"},{names:["flag_bi","bi"],keywords:["flag"],unicode:"1f1e7-1f1ee",sprite:"flags"},{names:["flag_kh","kh"],keywords:["flag"],unicode:"1f1f0-1f1ed",sprite:"flags"},{names:["flag_cm","cm"],keywords:["flag"],unicode:"1f1e8-1f1f2",sprite:"flags"},{names:["flag_ca","ca"],keywords:["flag"],unicode:"1f1e8-1f1e6",sprite:"flags"},{names:["flag_ic","ic"],keywords:["flag"],unicode:"1f1ee-1f1e8",sprite:"flags"},{names:["flag_cv","cv"],keywords:["flag"],
+unicode:"1f1e8-1f1fb",sprite:"flags"},{names:["flag_bq","bq"],keywords:["flag"],unicode:"1f1e7-1f1f6",sprite:"flags"},{names:["flag_ky","ky"],keywords:["flag"],unicode:"1f1f0-1f1fe",sprite:"flags"},{names:["flag_cf","cf"],keywords:["flag"],unicode:"1f1e8-1f1eb",sprite:"flags"},{names:["flag_td","td"],keywords:["flag"],unicode:"1f1f9-1f1e9",sprite:"flags"},{names:["flag_cl","chile"],keywords:["flag"],unicode:"1f1e8-1f1f1",sprite:"flags"},{names:["flag_cn","cn"],keywords:["flag"],unicode:"1f1e8-1f1f3",
+sprite:"flags"},{names:["flag_cx","cx"],keywords:["flag"],unicode:"1f1e8-1f1fd",sprite:"flags"},{names:["flag_cc","cc"],keywords:["flag"],unicode:"1f1e8-1f1e8",sprite:"flags"},{names:["flag_co","co"],keywords:["flag"],unicode:"1f1e8-1f1f4",sprite:"flags"},{names:["flag_km","km"],keywords:["flag"],unicode:"1f1f0-1f1f2",sprite:"flags"},{names:["flag_cg","cg"],keywords:["flag"],unicode:"1f1e8-1f1ec",sprite:"flags"},{names:["flag_cd","congo"],keywords:["flag"],unicode:"1f1e8-1f1e9",sprite:"flags"},{names:["flag_ck",
+"ck"],keywords:["flag"],unicode:"1f1e8-1f1f0",sprite:"flags"},{names:["flag_cr","cr"],keywords:["flag"],unicode:"1f1e8-1f1f7",sprite:"flags"},{names:["flag_ci","ci"],keywords:["flag"],unicode:"1f1e8-1f1ee",sprite:"flags"},{names:["flag_hr","hr"],keywords:["flag"],unicode:"1f1ed-1f1f7",sprite:"flags"},{names:["flag_cu","cu"],keywords:["flag"],unicode:"1f1e8-1f1fa",sprite:"flags"},{names:["flag_cw","cw"],keywords:["flag"],unicode:"1f1e8-1f1fc",sprite:"flags"},{names:["flag_cy","cy"],keywords:["flag"],
+unicode:"1f1e8-1f1fe",sprite:"flags"},{names:["flag_cz","cz"],keywords:["flag"],unicode:"1f1e8-1f1ff",sprite:"flags"},{names:["flag_dk","dk"],keywords:["flag"],unicode:"1f1e9-1f1f0",sprite:"flags"},{names:["flag_dj","dj"],keywords:["flag"],unicode:"1f1e9-1f1ef",sprite:"flags"},{names:["flag_dm","dm"],keywords:["flag"],unicode:"1f1e9-1f1f2",sprite:"flags"},{names:["flag_do","do"],keywords:["flag"],unicode:"1f1e9-1f1f4",sprite:"flags"},{names:["flag_ec","ec"],keywords:["flag"],unicode:"1f1ea-1f1e8",
+sprite:"flags"},{names:["flag_eg","eg"],keywords:["flag"],unicode:"1f1ea-1f1ec",sprite:"flags"},{names:["flag_sv","sv"],keywords:["flag"],unicode:"1f1f8-1f1fb",sprite:"flags"},{names:["flag_gq","gq"],keywords:["flag"],unicode:"1f1ec-1f1f6",sprite:"flags"},{names:["flag_er","er"],keywords:["flag"],unicode:"1f1ea-1f1f7",sprite:"flags"},{names:["flag_ee","ee"],keywords:["flag"],unicode:"1f1ea-1f1ea",sprite:"flags"},{names:["flag_et","et"],keywords:["flag"],unicode:"1f1ea-1f1f9",sprite:"flags"},{names:["flag_eu",
+"eu"],keywords:["flag"],unicode:"1f1ea-1f1fa",sprite:"flags"},{names:["flag_fk","fk"],keywords:["flag"],unicode:"1f1eb-1f1f0",sprite:"flags"},{names:["flag_fo","fo"],keywords:["flag"],unicode:"1f1eb-1f1f4",sprite:"flags"},{names:["flag_fj","fj"],keywords:["flag"],unicode:"1f1eb-1f1ef",sprite:"flags"},{names:["flag_fi","fi"],keywords:["flag"],unicode:"1f1eb-1f1ee",sprite:"flags"},{names:["flag_fr","fr"],keywords:["flag"],unicode:"1f1eb-1f1f7",sprite:"flags"},{names:["flag_gf","gf"],keywords:["flag"],
+unicode:"1f1ec-1f1eb",sprite:"flags"},{names:["flag_pf","pf"],keywords:["flag"],unicode:"1f1f5-1f1eb",sprite:"flags"},{names:["flag_tf","tf"],keywords:["flag"],unicode:"1f1f9-1f1eb",sprite:"flags"},{names:["flag_ga","ga"],keywords:["flag"],unicode:"1f1ec-1f1e6",sprite:"flags"},{names:["flag_gm","gm"],keywords:["flag"],unicode:"1f1ec-1f1f2",sprite:"flags"},{names:["flag_ge","ge"],keywords:["flag"],unicode:"1f1ec-1f1ea",sprite:"flags"},{names:["flag_de","de"],keywords:["flag"],unicode:"1f1e9-1f1ea",
+sprite:"flags"},{names:["flag_gh","gh"],keywords:["flag"],unicode:"1f1ec-1f1ed",sprite:"flags"},{names:["flag_gi","gi"],keywords:["flag"],unicode:"1f1ec-1f1ee",sprite:"flags"},{names:["flag_gr","gr"],keywords:["flag"],unicode:"1f1ec-1f1f7",sprite:"flags"},{names:["flag_gl","gl"],keywords:["flag"],unicode:"1f1ec-1f1f1",sprite:"flags"},{names:["flag_gd","gd"],keywords:["flag"],unicode:"1f1ec-1f1e9",sprite:"flags"},{names:["flag_gp","gp"],keywords:["flag"],unicode:"1f1ec-1f1f5",sprite:"flags"},{names:["flag_gu",
+"gu"],keywords:["flag"],unicode:"1f1ec-1f1fa",sprite:"flags"},{names:["flag_gt","gt"],keywords:["flag"],unicode:"1f1ec-1f1f9",sprite:"flags"},{names:["flag_gg","gg"],keywords:["flag"],unicode:"1f1ec-1f1ec",sprite:"flags"},{names:["flag_gn","gn"],keywords:["flag"],unicode:"1f1ec-1f1f3",sprite:"flags"},{names:["flag_gw","gw"],keywords:["flag"],unicode:"1f1ec-1f1fc",sprite:"flags"},{names:["flag_gy","gy"],keywords:["flag"],unicode:"1f1ec-1f1fe",sprite:"flags"},{names:["flag_ht","ht"],keywords:["flag"],
+unicode:"1f1ed-1f1f9",sprite:"flags"},{names:["flag_hn","hn"],keywords:["flag"],unicode:"1f1ed-1f1f3",sprite:"flags"},{names:["flag_hk","hk"],keywords:["flag"],unicode:"1f1ed-1f1f0",sprite:"flags"},{names:["flag_hu","hu"],keywords:["flag"],unicode:"1f1ed-1f1fa",sprite:"flags"},{names:["flag_is","is"],keywords:["flag"],unicode:"1f1ee-1f1f8",sprite:"flags"},{names:["flag_in","in"],keywords:["flag"],unicode:"1f1ee-1f1f3",sprite:"flags"},{names:["flag_id","indonesia"],keywords:["flag"],unicode:"1f1ee-1f1e9",
+sprite:"flags"},{names:["flag_ir","ir"],keywords:["flag"],unicode:"1f1ee-1f1f7",sprite:"flags"},{names:["flag_iq","iq"],keywords:["flag"],unicode:"1f1ee-1f1f6",sprite:"flags"},{names:["flag_ie","ie"],keywords:["flag"],unicode:"1f1ee-1f1ea",sprite:"flags"},{names:["flag_im","im"],keywords:["flag"],unicode:"1f1ee-1f1f2",sprite:"flags"},{names:["flag_il","il"],keywords:["flag"],unicode:"1f1ee-1f1f1",sprite:"flags"},{names:["flag_it","it"],keywords:["flag"],unicode:"1f1ee-1f1f9",sprite:"flags"},{names:["flag_jm",
+"jm"],keywords:["flag"],unicode:"1f1ef-1f1f2",sprite:"flags"},{names:["flag_jp","jp"],keywords:["flag"],unicode:"1f1ef-1f1f5",sprite:"flags"},{names:["crossed_flags"],keywords:["Japanese","celebration","cross","crossed"],unicode:"1f38c",sprite:"flags"},{names:["flag_je","je"],keywords:["flag"],unicode:"1f1ef-1f1ea",sprite:"flags"},{names:["flag_jo","jo"],keywords:["flag"],unicode:"1f1ef-1f1f4",sprite:"flags"},{names:["flag_kz","kz"],keywords:["flag"],unicode:"1f1f0-1f1ff",sprite:"flags"},{names:["flag_ke",
+"ke"],keywords:["flag"],unicode:"1f1f0-1f1ea",sprite:"flags"},{names:["flag_ki","ki"],keywords:["flag"],unicode:"1f1f0-1f1ee",sprite:"flags"},{names:["flag_xk","xk"],keywords:["flag"],unicode:"1f1fd-1f1f0",sprite:"flags"},{names:["flag_kw","kw"],keywords:["flag"],unicode:"1f1f0-1f1fc",sprite:"flags"},{names:["flag_kg","kg"],keywords:["flag"],unicode:"1f1f0-1f1ec",sprite:"flags"},{names:["flag_la","la"],keywords:["flag"],unicode:"1f1f1-1f1e6",sprite:"flags"},{names:["flag_lv","lv"],keywords:["flag"],
+unicode:"1f1f1-1f1fb",sprite:"flags"},{names:["flag_lb","lb"],keywords:["flag"],unicode:"1f1f1-1f1e7",sprite:"flags"},{names:["flag_ls","ls"],keywords:["flag"],unicode:"1f1f1-1f1f8",sprite:"flags"},{names:["flag_lr","lr"],keywords:["flag"],unicode:"1f1f1-1f1f7",sprite:"flags"},{names:["flag_ly","ly"],keywords:["flag"],unicode:"1f1f1-1f1fe",sprite:"flags"},{names:["flag_li","li"],keywords:["flag"],unicode:"1f1f1-1f1ee",sprite:"flags"},{names:["flag_lt","lt"],keywords:["flag"],unicode:"1f1f1-1f1f9",
+sprite:"flags"},{names:["flag_lu","lu"],keywords:["flag"],unicode:"1f1f1-1f1fa",sprite:"flags"},{names:["flag_mo","mo"],keywords:["flag"],unicode:"1f1f2-1f1f4",sprite:"flags"},{names:["flag_mk","mk"],keywords:["flag"],unicode:"1f1f2-1f1f0",sprite:"flags"},{names:["flag_mg","mg"],keywords:["flag"],unicode:"1f1f2-1f1ec",sprite:"flags"},{names:["flag_mw","mw"],keywords:["flag"],unicode:"1f1f2-1f1fc",sprite:"flags"},{names:["flag_my","my"],keywords:["flag"],unicode:"1f1f2-1f1fe",sprite:"flags"},{names:["flag_mv",
+"mv"],keywords:["flag"],unicode:"1f1f2-1f1fb",sprite:"flags"},{names:["flag_ml","ml"],keywords:["flag"],unicode:"1f1f2-1f1f1",sprite:"flags"},{names:["flag_mt","mt"],keywords:["flag"],unicode:"1f1f2-1f1f9",sprite:"flags"},{names:["flag_mh","mh"],keywords:["flag"],unicode:"1f1f2-1f1ed",sprite:"flags"},{names:["flag_mq","mq"],keywords:["flag"],unicode:"1f1f2-1f1f6",sprite:"flags"},{names:["flag_mr","mr"],keywords:["flag"],unicode:"1f1f2-1f1f7",sprite:"flags"},{names:["flag_mu","mu"],keywords:["flag"],
+unicode:"1f1f2-1f1fa",sprite:"flags"},{names:["flag_yt","yt"],keywords:["flag"],unicode:"1f1fe-1f1f9",sprite:"flags"},{names:["flag_mx","mx"],keywords:["flag"],unicode:"1f1f2-1f1fd",sprite:"flags"},{names:["flag_fm","fm"],keywords:["flag"],unicode:"1f1eb-1f1f2",sprite:"flags"},{names:["flag_md","md"],keywords:["flag"],unicode:"1f1f2-1f1e9",sprite:"flags"},{names:["flag_mc","mc"],keywords:["flag"],unicode:"1f1f2-1f1e8",sprite:"flags"},{names:["flag_mn","mn"],keywords:["flag"],unicode:"1f1f2-1f1f3",
+sprite:"flags"},{names:["flag_me","me"],keywords:["flag"],unicode:"1f1f2-1f1ea",sprite:"flags"},{names:["flag_ms","ms"],keywords:["flag"],unicode:"1f1f2-1f1f8",sprite:"flags"},{names:["flag_ma","ma"],keywords:["flag"],unicode:"1f1f2-1f1e6",sprite:"flags"},{names:["flag_mz","mz"],keywords:["flag"],unicode:"1f1f2-1f1ff",sprite:"flags"},{names:["flag_mm","mm"],keywords:["flag"],unicode:"1f1f2-1f1f2",sprite:"flags"},{names:["flag_na","na"],keywords:["flag"],unicode:"1f1f3-1f1e6",sprite:"flags"},{names:["flag_nr",
+"nr"],keywords:["flag"],unicode:"1f1f3-1f1f7",sprite:"flags"},{names:["flag_np","np"],keywords:["flag"],unicode:"1f1f3-1f1f5",sprite:"flags"},{names:["flag_nl","nl"],keywords:["flag"],unicode:"1f1f3-1f1f1",sprite:"flags"},{names:["flag_nc","nc"],keywords:["flag"],unicode:"1f1f3-1f1e8",sprite:"flags"},{names:["flag_nz","nz"],keywords:["flag"],unicode:"1f1f3-1f1ff",sprite:"flags"},{names:["flag_ni","ni"],keywords:["flag"],unicode:"1f1f3-1f1ee",sprite:"flags"},{names:["flag_ne","ne"],keywords:["flag"],
+unicode:"1f1f3-1f1ea",sprite:"flags"},{names:["flag_ng","nigeria"],keywords:["flag"],unicode:"1f1f3-1f1ec",sprite:"flags"},{names:["flag_nu","nu"],keywords:["flag"],unicode:"1f1f3-1f1fa",sprite:"flags"},{names:["flag_nf","nf"],keywords:["flag"],unicode:"1f1f3-1f1eb",sprite:"flags"},{names:["flag_kp","kp"],keywords:["flag"],unicode:"1f1f0-1f1f5",sprite:"flags"},{names:["flag_mp","mp"],keywords:["flag"],unicode:"1f1f2-1f1f5",sprite:"flags"},{names:["flag_no","no"],keywords:["flag"],unicode:"1f1f3-1f1f4",
+sprite:"flags"},{names:["flag_om","om"],keywords:["flag"],unicode:"1f1f4-1f1f2",sprite:"flags"},{names:["flag_pk","pk"],keywords:["flag"],unicode:"1f1f5-1f1f0",sprite:"flags"},{names:["flag_pw","pw"],keywords:["flag"],unicode:"1f1f5-1f1fc",sprite:"flags"},{names:["flag_ps","ps"],keywords:["flag"],unicode:"1f1f5-1f1f8",sprite:"flags"},{names:["flag_pa","pa"],keywords:["flag"],unicode:"1f1f5-1f1e6",sprite:"flags"},{names:["flag_pg","pg"],keywords:["flag"],unicode:"1f1f5-1f1ec",sprite:"flags"},{names:["flag_py",
+"py"],keywords:["flag"],unicode:"1f1f5-1f1fe",sprite:"flags"},{names:["flag_pe","pe"],keywords:["flag"],unicode:"1f1f5-1f1ea",sprite:"flags"},{names:["flag_ph","ph"],keywords:["flag"],unicode:"1f1f5-1f1ed",sprite:"flags"},{names:["flag_pn","pn"],keywords:["flag"],unicode:"1f1f5-1f1f3",sprite:"flags"},{names:["flag_pl","pl"],keywords:["flag"],unicode:"1f1f5-1f1f1",sprite:"flags"},{names:["flag_pt","pt"],keywords:["flag"],unicode:"1f1f5-1f1f9",sprite:"flags"},{names:["flag_pr","pr"],keywords:["flag"],
+unicode:"1f1f5-1f1f7",sprite:"flags"},{names:["flag_qa","qa"],keywords:["flag"],unicode:"1f1f6-1f1e6",sprite:"flags"},{names:["flag_re","re"],keywords:["flag"],unicode:"1f1f7-1f1ea",sprite:"flags"},{names:["flag_ro","ro"],keywords:["flag"],unicode:"1f1f7-1f1f4",sprite:"flags"},{names:["flag_ru","ru"],keywords:["flag"],unicode:"1f1f7-1f1fa",sprite:"flags"},{names:["flag_rw","rw"],keywords:["flag"],unicode:"1f1f7-1f1fc",sprite:"flags"},{names:["flag_ws","ws"],keywords:["flag"],unicode:"1f1fc-1f1f8",
+sprite:"flags"},{names:["flag_sm","sm"],keywords:["flag"],unicode:"1f1f8-1f1f2",sprite:"flags"},{names:["flag_st","st"],keywords:["flag"],unicode:"1f1f8-1f1f9",sprite:"flags"},{names:["flag_sa","saudiarabia","saudi"],keywords:["flag"],unicode:"1f1f8-1f1e6",sprite:"flags"},{names:["flag_sn","sn"],keywords:["flag"],unicode:"1f1f8-1f1f3",sprite:"flags"},{names:["flag_rs","rs"],keywords:["flag"],unicode:"1f1f7-1f1f8",sprite:"flags"},{names:["flag_sc","sc"],keywords:["flag"],unicode:"1f1f8-1f1e8",sprite:"flags"},
+{names:["flag_sl","sl"],keywords:["flag"],unicode:"1f1f8-1f1f1",sprite:"flags"},{names:["flag_sg","sg"],keywords:["flag"],unicode:"1f1f8-1f1ec",sprite:"flags"},{names:["flag_sx","sx"],keywords:["flag"],unicode:"1f1f8-1f1fd",sprite:"flags"},{names:["flag_sk","sk"],keywords:["flag"],unicode:"1f1f8-1f1f0",sprite:"flags"},{names:["flag_si","si"],keywords:["flag"],unicode:"1f1f8-1f1ee",sprite:"flags"},{names:["flag_gs","gs"],keywords:["flag"],unicode:"1f1ec-1f1f8",sprite:"flags"},{names:["flag_sb","sb"],
+keywords:["flag"],unicode:"1f1f8-1f1e7",sprite:"flags"},{names:["flag_so","so"],keywords:["flag"],unicode:"1f1f8-1f1f4",sprite:"flags"},{names:["flag_za","za"],keywords:["flag"],unicode:"1f1ff-1f1e6",sprite:"flags"},{names:["flag_kr","kr"],keywords:["flag"],unicode:"1f1f0-1f1f7",sprite:"flags"},{names:["flag_ss","ss"],keywords:["flag"],unicode:"1f1f8-1f1f8",sprite:"flags"},{names:["flag_es","es"],keywords:["flag"],unicode:"1f1ea-1f1f8",sprite:"flags"},{names:["flag_lk","lk"],keywords:["flag"],unicode:"1f1f1-1f1f0",
+sprite:"flags"},{names:["flag_bl","bl"],keywords:["flag"],unicode:"1f1e7-1f1f1",sprite:"flags"},{names:["flag_sh","sh"],keywords:["flag"],unicode:"1f1f8-1f1ed",sprite:"flags"},{names:["flag_kn","kn"],keywords:["flag"],unicode:"1f1f0-1f1f3",sprite:"flags"},{names:["flag_lc","lc"],keywords:["flag"],unicode:"1f1f1-1f1e8",sprite:"flags"},{names:["flag_pm","pm"],keywords:["flag"],unicode:"1f1f5-1f1f2",sprite:"flags"},{names:["flag_vc","vc"],keywords:["flag"],unicode:"1f1fb-1f1e8",sprite:"flags"},{names:["flag_sd",
+"sd"],keywords:["flag"],unicode:"1f1f8-1f1e9",sprite:"flags"},{names:["flag_sr","sr"],keywords:["flag"],unicode:"1f1f8-1f1f7",sprite:"flags"},{names:["flag_sz","sz"],keywords:["flag"],unicode:"1f1f8-1f1ff",sprite:"flags"},{names:["flag_se","se"],keywords:["flag"],unicode:"1f1f8-1f1ea",sprite:"flags"},{names:["flag_ch","ch"],keywords:["flag"],unicode:"1f1e8-1f1ed",sprite:"flags"},{names:["flag_sy","sy"],keywords:["flag"],unicode:"1f1f8-1f1fe",sprite:"flags"},{names:["flag_tw","tw"],keywords:["flag"],
+unicode:"1f1f9-1f1fc",sprite:"flags"},{names:["flag_tj","tj"],keywords:["flag"],unicode:"1f1f9-1f1ef",sprite:"flags"},{names:["flag_tz","tz"],keywords:["flag"],unicode:"1f1f9-1f1ff",sprite:"flags"},{names:["flag_th","th"],keywords:["flag"],unicode:"1f1f9-1f1ed",sprite:"flags"},{names:["flag_tl","tl"],keywords:["flag"],unicode:"1f1f9-1f1f1",sprite:"flags"},{names:["flag_tg","tg"],keywords:["flag"],unicode:"1f1f9-1f1ec",sprite:"flags"},{names:["flag_tk","tk"],keywords:["flag"],unicode:"1f1f9-1f1f0",
+sprite:"flags"},{names:["flag_to","to"],keywords:["flag"],unicode:"1f1f9-1f1f4",sprite:"flags"},{names:["flag_tt","tt"],keywords:["flag"],unicode:"1f1f9-1f1f9",sprite:"flags"},{names:["flag_tn","tn"],keywords:["flag"],unicode:"1f1f9-1f1f3",sprite:"flags"},{names:["flag_tr","tr"],keywords:["flag"],unicode:"1f1f9-1f1f7",sprite:"flags"},{names:["flag_tm","turkmenistan"],keywords:["flag"],unicode:"1f1f9-1f1f2",sprite:"flags"},{names:["flag_tc","tc"],keywords:["flag"],unicode:"1f1f9-1f1e8",sprite:"flags"},
+{names:["flag_tv","tuvalu"],keywords:["flag"],unicode:"1f1f9-1f1fb",sprite:"flags"},{names:["flag_vi","vi"],keywords:["flag"],unicode:"1f1fb-1f1ee",sprite:"flags"},{names:["flag_ug","ug"],keywords:["flag"],unicode:"1f1fa-1f1ec",sprite:"flags"},{names:["flag_ua","ua"],keywords:["flag"],unicode:"1f1fa-1f1e6",sprite:"flags"},{names:["flag_ae","ae"],keywords:["flag"],unicode:"1f1e6-1f1ea",sprite:"flags"},{names:["flag_gb","gb"],keywords:["flag"],unicode:"1f1ec-1f1e7",sprite:"flags"},{names:["flag_us",
+"us"],keywords:["flag"],unicode:"1f1fa-1f1f8",sprite:"flags"},{names:["flag_uy","uy"],keywords:["flag"],unicode:"1f1fa-1f1fe",sprite:"flags"},{names:["flag_uz","uz"],keywords:["flag"],unicode:"1f1fa-1f1ff",sprite:"flags"},{names:["flag_vu","vu"],keywords:["flag"],unicode:"1f1fb-1f1fa",sprite:"flags"},{names:["flag_va","va"],keywords:["flag"],unicode:"1f1fb-1f1e6",sprite:"flags"},{names:["flag_ve","ve"],keywords:["flag"],unicode:"1f1fb-1f1ea",sprite:"flags"},{names:["flag_vn","vn"],keywords:["flag"],
+unicode:"1f1fb-1f1f3",sprite:"flags"},{names:["flag_wf","wf"],keywords:["flag"],unicode:"1f1fc-1f1eb",sprite:"flags"},{names:["flag_eh","eh"],keywords:["flag"],unicode:"1f1ea-1f1ed",sprite:"flags"},{names:["flag_ye","ye"],keywords:["flag"],unicode:"1f1fe-1f1ea",sprite:"flags"},{names:["flag_zm","zm"],keywords:["flag"],unicode:"1f1ff-1f1f2",sprite:"flags"},{names:["flag_zw","zw"],keywords:["flag"],unicode:"1f1ff-1f1fc",sprite:"flags"},{names:["flag_ac","ac"],keywords:["flag"],unicode:"1f1e6-1f1e8",
+sprite:"flags"},{names:["flag_ta","ta"],keywords:["flag"],unicode:"1f1f9-1f1e6",sprite:"flags"},{names:["flag_bv","bv"],keywords:["flag"],unicode:"1f1e7-1f1fb",sprite:"flags"},{names:["flag_hm","hm"],keywords:["flag"],unicode:"1f1ed-1f1f2",sprite:"flags"},{names:["flag_sj","sj"],keywords:["flag"],unicode:"1f1f8-1f1ef",sprite:"flags"},{names:["flag_um","um"],keywords:["flag"],unicode:"1f1fa-1f1f2",sprite:"flags"},{names:["flag_ea","ea"],keywords:["flag"],unicode:"1f1ea-1f1e6",sprite:"flags"},{names:["flag_cp",
+"cp"],keywords:["flag"],unicode:"1f1e8-1f1f5",sprite:"flags"},{names:["flag_dg","dg"],keywords:["flag"],unicode:"1f1e9-1f1ec",sprite:"flags"},{names:["flag_mf","mf"],keywords:["flag"],unicode:"1f1f2-1f1eb",sprite:"flags"},{names:["united_nations"],keywords:["flag"],unicode:"1f1fa-1f1f3",sprite:"flags"},{names:["asterisk_symbol"],keywords:[""],unicode:"002a",sprite:"symbols"},{names:["regional_indicator_z"],keywords:[""],unicode:"1f1ff",sprite:"regional"},{names:["regional_indicator_y"],keywords:[""],
+unicode:"1f1fe",sprite:"regional"},{names:["regional_indicator_x"],keywords:[""],unicode:"1f1fd",sprite:"regional"},{names:["pound_symbol"],keywords:[""],unicode:"0023",sprite:"symbols"},{names:["tone1"],keywords:[""],unicode:"1f3fb",sprite:"modifier"},{names:["tone2"],keywords:[""],unicode:"1f3fc",sprite:"modifier"},{names:["tone3"],keywords:[""],unicode:"1f3fd",sprite:"modifier"},{names:["tone4"],keywords:[""],unicode:"1f3fe",sprite:"modifier"},{names:["tone5"],keywords:[""],unicode:"1f3ff",sprite:"modifier"},
+{names:["regional_indicator_w"],keywords:[""],unicode:"1f1fc",sprite:"regional"},{names:["regional_indicator_v"],keywords:[""],unicode:"1f1fb",sprite:"regional"},{names:["regional_indicator_u"],keywords:[""],unicode:"1f1fa",sprite:"regional"},{names:["regional_indicator_t"],keywords:[""],unicode:"1f1f9",sprite:"regional"},{names:["regional_indicator_s"],keywords:[""],unicode:"1f1f8",sprite:"regional"},{names:["regional_indicator_r"],keywords:[""],unicode:"1f1f7",sprite:"regional"},{names:["regional_indicator_q"],
+keywords:[""],unicode:"1f1f6",sprite:"regional"},{names:["regional_indicator_p"],keywords:[""],unicode:"1f1f5",sprite:"regional"},{names:["regional_indicator_o"],keywords:[""],unicode:"1f1f4",sprite:"regional"},{names:["regional_indicator_n"],keywords:[""],unicode:"1f1f3",sprite:"regional"},{names:["regional_indicator_m"],keywords:[""],unicode:"1f1f2",sprite:"regional"},{names:["regional_indicator_l"],keywords:[""],unicode:"1f1f1",sprite:"regional"},{names:["regional_indicator_k"],keywords:[""],unicode:"1f1f0",
+sprite:"regional"},{names:["regional_indicator_j"],keywords:[""],unicode:"1f1ef",sprite:"regional"},{names:["regional_indicator_i"],keywords:[""],unicode:"1f1ee",sprite:"regional"},{names:["regional_indicator_h"],keywords:[""],unicode:"1f1ed",sprite:"regional"},{names:["regional_indicator_g"],keywords:[""],unicode:"1f1ec",sprite:"regional"},{names:["regional_indicator_f"],keywords:[""],unicode:"1f1eb",sprite:"regional"},{names:["regional_indicator_e"],keywords:[""],unicode:"1f1ea",sprite:"regional"},
+{names:["regional_indicator_d"],keywords:[""],unicode:"1f1e9",sprite:"regional"},{names:["regional_indicator_c"],keywords:[""],unicode:"1f1e8",sprite:"regional"},{names:["regional_indicator_b"],keywords:[""],unicode:"1f1e7",sprite:"regional"},{names:["regional_indicator_a"],keywords:[""],unicode:"1f1e6",sprite:"regional"},{names:["digit_nine"],keywords:[""],unicode:"0039",sprite:"symbols"},{names:["digit_eight"],keywords:[""],unicode:"0038",sprite:"symbols"},{names:["digit_seven"],keywords:[""],unicode:"0037",
+sprite:"symbols"},{names:["digit_six"],keywords:[""],unicode:"0036",sprite:"symbols"},{names:["digit_five"],keywords:[""],unicode:"0035",sprite:"symbols"},{names:["digit_four"],keywords:[""],unicode:"0034",sprite:"symbols"},{names:["digit_three"],keywords:[""],unicode:"0033",sprite:"symbols"},{names:["digit_two"],keywords:[""],unicode:"0032",sprite:"symbols"},{names:["digit_one"],keywords:[""],unicode:"0031",sprite:"symbols"},{names:["digit_zero"],keywords:[""],unicode:"0030",sprite:"symbols"},{names:["family_man_woman_boy"],
+keywords:["boy","family","man","woman"],unicode:"1f468-1f469-1f466",sprite:"people"},{names:["couple_with_heart_woman_man"],keywords:["couple","love","man","woman"],unicode:"1f469-2764-1f468",sprite:"people"},{names:["kiss_woman_man"],keywords:["couple","man","woman"],unicode:"1f469-2764-1f48b-1f468",sprite:"people"}];window.makeEmoji=function(c){var b;if("string"===typeof c)for(var e=0,g=f.length;e<g&&!b;e++)for(var h=f[e].names,a=0,d=h.length;a<d&&!b;a++)h[a]===c&&(b=f[e]);else b=c;return b?(c=document.createElement("span"),c.className="emoji emojione emojione-64 emojione-64-"+b.sprite+" _"+b.unicode,c):null};window.emojiProviderHeader="emojione.logo.svg";
+window.searchEmojis=function(c){var b={},e=c.length,g=0,h=f.length;a:for(;g<h;g++){for(var a=f[g],d=0,k=a.names.length;d<k;d++)if(a.names[d].substr(0,e)===c){b[a.names[0]]=a;continue a}d=0;for(k=a.keywords.length;d<k;d++)if(a.keywords[d].substr(0,e)===c){b[a.names[0]]=a;break}}return b};
+})();

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 1 - 0
srv/public/highlight.pack.js


+ 1 - 0
srv/public/hljs-androidstudio.css

@@ -0,0 +1 @@
+.hljs{color:#a9b7c6;background:#282b2e;display:block;overflow-x:auto;padding:0.5em}.hljs-number,.hljs-literal,.hljs-symbol,.hljs-bullet{color:#6897BB}.hljs-keyword,.hljs-selector-tag,.hljs-deletion{color:#cc7832}.hljs-variable,.hljs-template-variable,.hljs-link{color:#629755}.hljs-comment,.hljs-quote{color:#808080}.hljs-meta{color:#bbb529}.hljs-string,.hljs-attribute,.hljs-addition{color:#6A8759}.hljs-section,.hljs-title,.hljs-type{color:#ffc66d}.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#e8bf6a}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}

+ 103 - 78
srv/public/slack.min.js

@@ -1,78 +1,103 @@
-function n(a){this.id=a;this.version=0}n.prototype.update=function(a,b){void 0!==a.name&&(this.name=a.name);this.version=Math.max(this.version,b)};function aa(a){this.a=a.desc;this.name=a.name;this.type=a.type;this.usage=a.usage;this.B=a.category}function ba(){this.b={};this.a=[]}
-ba.prototype.update=function(a,b){this.b=JSON.parse(a.emoji_use);a.highlight_words?this.a=(a.highlight_words||"").split(",").filter(function(a){return""!==a.trim()}):a.highlights&&(this.a=a.highlights);this.version=Math.max(this.version,b)};function ca(){this.l=null;this.f={};this.a={};this.b=null;this.h={version:0,data:{}};this.g={version:0,data:{}};this.c={};this.s=0}"undefined"!==typeof module&&(module.w.S=ca,module.w.T=n,module.w.V=aa);function r(a){this.id=a;this.o=!1;this.a=this.b=0;this.g={};this.version=0}
-r.prototype.update=function(a,b,c){void 0!==a.name&&(this.name=a.name);void 0!==a.is_archived&&(this.h=a.is_archived);void 0!==a.is_member&&(this.s=a.is_member);void 0!==a.last_read&&(this.b=Math.max(parseFloat(a.last_read),this.b));void 0!==a.last_msg&&(this.a=parseFloat(a.last_msg));void 0!==a.is_private&&(this.l=a.is_private);a.latest&&(this.a=parseFloat(a.latest.ts));void 0!==a.is_starred&&(this.o=a.is_starred);if(a.members&&(this.g={},a.members))for(var d=0,g=a.members.length;d<g;d++){var e=
-b.a[a.members[d]];this.g[e.id]=e;e.f[this.id]=this}this.version=Math.max(this.version,c)};function x(a,b){r.call(this,a);this.c=b;this.name=this.c.name;this.l=!0;b.h=this}x.prototype=Object.create(r.prototype);x.prototype.constructor=x;"undefined"!==typeof module&&(module.w.$=r,module.w.Z=x);function y(a,b){this.m=a.user;this.h=a.username;this.id=a.id||a.ts;this.j=parseFloat(a.ts);this.c="";this.a=[];this.g=this.u=this.o=!1;this.b={};this.version=b;this.update(a,b)}function C(a,b){y.call(this,a,b)}function D(a,b){y.call(this,a,b)}
-y.prototype.update=function(a,b){if(a){if(this.c=a.text||"",a.attachments&&(this.a=a.attachments),this.o=!!a.is_starred,this.u=!!a.edited,this.g=!!a.removed,a.reactions){var c={};a.reactions.forEach(function(a){c[a.name]=[];a.users.forEach(function(b){c[a.name].push(b)})});this.b=c}}else this.g=!0;this.version=b};function E(a,b,c,d){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;c&&da(this,c,d)}function da(a,b,c){var d=0;b.forEach(function(a){d=Math.max(this.push(a,c),d)}.bind(a));ea(a)}
-E.prototype.push=function(a,b){for(var c=!1,d,g=0,e=this.a.length;g<e;g++){var f=this.a[g];if(f.id===a.id){d=f.update(a,b);c=!0;break}}c||(f=!0===a.isMeMessage?new C(a,b):!0===a.isNotice?new D(a,b):new y(a,b),this.a.push(f),d=f.j);for(;this.a.length>this.b;)this.a.shift();return d||0};function fa(a){for(var b=F.b[G.id],c=0,d=b.a.length;c<d&&a>=b.a[c].j;c++)if(b.a[c].j===a)return b.a[c];return null}function ea(a){a.a.sort(function(a,c){return a.j-c.j})}C.prototype=Object.create(y.prototype);
-C.prototype.constructor=C;D.prototype=Object.create(y.prototype);D.prototype.constructor=D;"undefined"!==typeof module&&(module.w={X:y,W:C,Y:D,aa:E});function H(a){this.id=a;this.b={small:"",R:""};this.f={};this.h=this.a=null;this.version=0}H.prototype.update=function(a,b){void 0!==a.name&&(this.name=a.name);void 0!==a.deleted&&(this.g=a.deleted);void 0!==a.status&&(this.status=a.status);void 0!==a.presence&&(this.c="away"!==a.presence);void 0!==a.isPresent&&(this.c=a.isPresent);a.isBot&&(this.s=a.isBot);a.profile&&(this.b.small=a.profile.icon_small,this.b.R=a.profile.icon_large);this.version=Math.max(this.version,b)};
-"undefined"!==typeof module&&(module.w.U=H);var I={},K;function ga(){var a;if(!a){for(var b=0,c=navigator.languages.length;b<c;b++)if(I.hasOwnProperty(navigator.languages[b])){a=navigator.languages[b];break}a||(a="en")}K=I[a];console.log("Loading language pack: "+a);if(K.i)for(b in K.i)document.getElementById(b).textContent=K.i[b]};I.fr={N:"Utilisateur inconnu",M:"Channel inconnu",I:"Nouveau message",H:"Reseau",u:"(edit&eacute;)",J:"(visible seulement par vous)",o:"Favoris",f:"Discutions",L:"Discutions priv\u00e9es",ok:"Ok",G:"Annuler",F:function(a){"string"!==typeof a&&(a=parseFloat(a));var b=new Date,c=new Date;a=new Date(1E3*a);b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0);c.setTime(b.getTime());c.setDate(c.getDate()-1);return a.getTime()>b.getTime()?a.toLocaleTimeString():a.getTime()>c.getTime()?"hier, "+
-a.toLocaleTimeString():a.toLocaleString()},i:{fileUploadCancel:"Annuler",neterror:"Impossible de se connecter au chat !"}};I.en={N:"Unknown member",M:"Unknown channel",I:"New message",H:"Network",u:"(edited)",J:"(only visible to you)",o:"Starred",f:"Channels",L:"Direct messages",ok:"Ok",G:"Cancel",F:function(a){"string"!==typeof a&&(a=parseFloat(a));var b=new Date,c=new Date;a=new Date(1E3*a);b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0);c.setTime(b.getTime());c.setDate(c.getDate()-1);return a.getTime()>b.getTime()?a.toLocaleTimeString():a.getTime()>c.getTime()?"yesterday, "+a.toLocaleTimeString():
-a.toLocaleString()},i:{fileUploadCancel:"Cancel",neterror:"Cannot connect to chat !"}};function L(a,b){this.c=a;this.content=b;this.i=ha(this);this.b=ia(this);this.a=[];this.g=[]}
-function ha(a){var b=document.createElement("div"),c=document.createElement("header"),d=document.createElement("span"),g=document.createElement("span"),e=document.createElement("div"),f=document.createElement("footer");b.a=document.createElement("span");b.b=document.createElement("span");d.textContent=a.c;"string"==typeof a.content?e.innerHTML=a.content:e.appendChild(a.content);c.className=ja;d.className=ka;g.className=la;g.textContent="x";c.appendChild(d);c.appendChild(g);b.appendChild(c);e.className=
-ma;b.appendChild(e);b.b.className=M;b.b.textContent=K.G;b.b.addEventListener("click",function(){N(a,!1)});g.addEventListener("click",function(){N(a,!1)});b.a.addEventListener("click",function(){N(a,!0)});f.appendChild(b.b);b.a.className=M;b.a.textContent=K.ok;f.appendChild(b.a);f.className=na+" "+oa;b.appendChild(f);b.className=pa;return b}function N(a,b){(b?a.a:a.g).forEach(function(a){a()});a.close()}
-function ia(a){var b=document.createElement("div");b.className=qa;b.addEventListener("click",function(){N(this,!1)}.bind(a));return b}function ra(a,b,c){a.i.a.textContent=b;a.i.b.textContent=c;return a}L.prototype.D=function(a){a=a||document.body;a.appendChild(this.b);a.appendChild(this.i);return this};L.prototype.close=function(){this.i.remove();this.b.remove();return this};function sa(a,b){a.a.push(b);return a};var M="button",na="button-container",pa="dialog",qa="dialog-overlay",ja="dialog-title",ka="dialog-title-label",la="dialog-title-close",ma="dialog-body",oa="dialog-footer";function ta(a){return a.replace(/:([^ \t:]+):/g,function(b,c){var d=ua(c);if(d){var g=document.createElement("span");g.className=b===a?"emoji-medium":"emoji-small";g.appendChild(d);return g.outerHTML}return b})}
-function O(a){if(!a||""==a)return"";a=a.split(/\r?\n/g);for(var b=0,c=a.length;b<c;b++){for(var d=a[b].trim(),g="",e={},f=!1,h=0,d=d.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){c=c.split("|");if("@"===b)c[1]?"@"!==c[1][0]&&(c[1]="@"+c[1]):(a=F.a.a[c[0]],c[1]=a?"@"+a.name:K.N),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-user";else if("#"===b)c[1]?"#"!==c[1][0]&&(c[1]="#"+c[1]):(a=F.a.f[c[0]],c[1]=a?"#"+a.name:K.M),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-chan";else if(-1!==c[0].indexOf("://"))c[1]||
-(c[1]=c[0]),c[2]="slackmsg-link";else return a;return'<a href="'+c[0]+'" class="'+c[2]+'"'+(b?"":' target="_blank"')+">"+c[1]+"</a>"}),d=ta(d),l=d.length,k=function(a,b,c){for(;a[b];){var d=a[b];if(("A"<=d&&"Z">=d||"a"<=d&&"z">=d||"0"<=d&&"9">=d||-1!=="\u00e0\u00e8\u00ec\u00f2\u00f9\u00c0\u00c8\u00cc\u00d2\u00d9\u00e1\u00e9\u00ed\u00f3\u00fa\u00fd\u00c1\u00c9\u00cd\u00d3\u00da\u00dd\u00e2\u00ea\u00ee\u00f4\u00fb\u00c2\u00ca\u00ce\u00d4\u00db\u00e3\u00f1\u00f5\u00c3\u00d1\u00d5\u00e4\u00eb\u00ef\u00f6\u00fc\u00ff\u00c4\u00cb\u00cf\u00d6\u00dc\u0178\u00e7\u00c7\u00df\u00d8\u00f8\u00c5\u00e5\u00c6\u00e6\u0153".indexOf(d))&&
-a[b]!=c&&a[b+1]==c)return!0;b++}return!1},p=function(a){return Object.keys(e).length?'<span class="'+Object.keys(a).join(" ")+'">':""};h<l&&(" "===d[h]||"\t"===d[h]);)h++;"&gt;"===d.substr(h,4)&&(f=!0,h+=4);for(;h<l;h++){var m=d[h];if("<"===m){do g+=d[h++];while(">"!==d[h-1]&&d[h]);h--}else if(!e["slackmsg-style-bold"]&&"*"===m&&d[h+1]&&k(d,h,m))Object.keys(e).length&&(g+="</span>"),e["slackmsg-style-bold"]=!0,g+=p(e);else if(!e["slackmsg-style-strike"]&&"~"===m&&d[h+1]&&k(d,h,m))Object.keys(e).length&&
-(g+="</span>"),e["slackmsg-style-strike"]=!0,g+=p(e);else if(!e["slackmsg-style-code"]&&"`"===m&&d[h+1]&&k(d,h,m))Object.keys(e).length&&(g+="</span>"),e["slackmsg-style-code"]=!0,g+=p(e);else if(!e["slackmsg-style-italic"]&&"_"===m&&d[h+1]&&k(d,h,m))Object.keys(e).length&&(g+="</span>"),e["slackmsg-style-italic"]=!0,g+=p(e);else{var q=!1,g=g+m;do{if(e["slackmsg-style-bold"]&&"*"!==m&&"*"===d[h+1])delete e["slackmsg-style-bold"],q=!0;else if(e["slackmsg-style-strike"]&&"~"!==m&&"~"===d[h+1])delete e["slackmsg-style-strike"],
-q=!0;else if(e["slackmsg-style-code"]&&"`"!==m&&"`"===d[h+1])delete e["slackmsg-style-code"],q=!0;else if(e["slackmsg-style-italic"]&&"_"!==m&&"_"===d[h+1])delete e["slackmsg-style-italic"],q=!0;else break;m=d[++h]}while(h<l);q&&(g+="</span>"+p(e))}}va(e)||(console.warn("formatter warning"),g+="</span>");a[b]=f?'<span class="slackmsg-style-quote">'+g+"</span>":g}return a.length?a.join("<br/>"):""};var P=[],Q=0;
-function wa(){var a=document.createDocumentFragment(),b=Object.keys(F.a.f||{}),c=[],d=[],g=[],e=[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:F.a.f[a].name.localeCompare(F.a.f[b].name)});b.forEach(function(a){a=F.a.f[a];if(!a.h&&!1!==a.s)if(a instanceof x){if(!a.c.g){var b;b=document.createElement("li");var f=document.createElement("a");b.id="room_"+a.id;f.href="#"+a.id;b.className="slack-context-room slack-ims";f.textContent=a.c.name;b.appendChild(R());b.appendChild(f);a.c.c||b.classList.add("away");
-G===a&&b.classList.add("selected");a.a>a.b&&(b.classList.add("unread"),0<=S.indexOf(a)&&b.classList.add("unreadHi"));b&&(a.o?c.push(b):e.push(b))}}else b=document.createElement("li"),f=document.createElement("a"),b.id="room_"+a.id,f.href="#"+a.id,a.l?(b.className="slack-context-room slack-group",b.dataset.count=Object.keys(a.g||{}).length):b.className="slack-context-room slack-channel",G===a&&b.classList.add("selected"),f.textContent=a.name,b.appendChild(R()),b.appendChild(f),a.a>a.b&&(b.classList.add("unread"),
-0<=S.indexOf(a)&&b.classList.add("unreadHi")),b&&(a.o?c.push(b):a.l?g.push(b):d.push(b))});c.length&&a.appendChild(xa(K.o));c.forEach(function(b){a.appendChild(b)});d.length&&a.appendChild(xa(K.f));d.forEach(function(b){a.appendChild(b)});g.forEach(function(b){a.appendChild(b)});e.length&&a.appendChild(xa(K.L));e.forEach(function(b){a.appendChild(b)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a);ya();T();za(function(a){document.getElementById("slackCtx").style.backgroundImage=
-"url("+a+")"})}function Aa(){var a=F.a.c,b;for(b in F.a.b.f)if(!F.a.b.f[b].h){var c=document.getElementById("room_"+b);a[b]?c.classList.add("slack-context-typing"):c.classList.remove("slack-context-typing")}for(var d in F.a.a)(b=F.a.a[d].h)&&!b.h&&(c=document.getElementById("room_"+b.id))&&(a[b.id]?c.classList.add("slack-context-typing"):c.classList.remove("slack-context-typing"));Ba()}
-function Ba(){var a=F.a.c;document.getElementById("whoistyping").textContent="";if(G&&a[G.id]){var b=document.createDocumentFragment(),c=!1,d;for(d in a[G.id])(a=F.a.a[d])?b.appendChild(Ca(a)):c=!0;c&&(F.c=0);document.getElementById("whoistyping").appendChild(b)}}function Da(a){a?document.body.classList.remove("no-network"):document.body.classList.add("no-network");T()}
-function Ea(){var a=G.name||(G.c?G.c.name:void 0);if(!a){var b=[];G.g.forEach(function(a){b.push(a.name)});a=b.join(", ")}document.getElementById("currentRoomTitle").textContent=a;Fa();U();document.getElementById("fileUploadContainer").classList.add("hidden");Ga();V&&(V=null,W());X&&(X=null,W());Ba()}
-function W(){if(V){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){V=null;W()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(Ha("reply_"+G.id,V,!0))}else document.body.classList.remove("replyingTo"),document.getElementById("replyToContainer").textContent="";U()}
-function Y(){if(X){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){X=null;Y()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(Ha("edit_"+G.id,X,!0));document.getElementById("msgInput").value=X.c}else document.body.classList.remove("replyingTo"),document.getElementById("replyToContainer").textContent="";U()}
-window.toggleReaction=function(a,b,c){var d=F.b[a];if(d){a:{for(var g=0,e=d.a.length;g<e;g++)if(d.a[g].id==b){d=d.a[g];break a}d=null}d&&(g=F.a.b.id,d.b[c]&&-1!==d.b[c].indexOf(g)?(d=new XMLHttpRequest,d.open("DELETE","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(c),!0),d.send(null)):Ia(a,b,c))}};
-function ua(a){a:{for(var b=a,c={};!c[b];){if(a=F.a.h.data[b])if("alias:"==a.substr(0,6))c[b]=!0,b=a.substr(6);else{b=document.createElement("span");b.className="emoji-custom emoji";b.style.backgroundImage="url('"+a+"')";a=b;break a}break}a=b}"string"===typeof a&&"makeEmoji"in window&&(a=window.makeEmoji(a));return"string"===typeof a?null:a}
-function Ha(a,b,c){b instanceof C?(a=Ja(a,b,c),a.classList.add("slackmsg-me_message")):a=Ja(a,b,c);b.u&&a.classList.add("slackmsg-edited");b instanceof D&&a.classList.add("slackmsg-notice");return a}function Ka(a,b){document.getElementById("linkFavicon").href=a||b?"favicon.png?h="+a+"&m="+b:"favicon_ok.png"}
-function T(){var a=S.length,b="";if(Z)b="!"+K.H+" - ",document.getElementById("linkFavicon").href="favicon_err.png";else if(a)b="(!"+a+") - ",Ka(a,a);else{var a=0,c;for(c in F.a.f){var d=F.a.f[c];d.a>d.b&&a++}a&&(b="("+a+") - ");Ka(0,a)}F.a.l&&(b+=F.a.l.name);document.title=b}
-function La(){if("Notification"in window)if("granted"===Notification.permission){var a=Date.now();if(Q+3E4<a){var b=new Notification(K.I);Q=a;setTimeout(function(){b.close()},5E3)}}else"denied"!==Notification.permission&&Notification.requestPermission()}
-function Fa(){var a=document.createDocumentFragment(),b=G.id,c=null,d=0,g=null,e;P=[];F.b[b]&&F.b[b].a.forEach(function(h){if(!h.g){var f=Ha(b,h),k=!1;c&&c.m===h.m&&h.m?30>Math.abs(d-h.j)&&!(h instanceof C)?g.classList.add("slackmsg-same-ts"):d=h.j:(d=h.j,k=!0);(!c||c.j<=G.b)&&h.j>G.b&&f.classList.add("slackmsg-first-unread");if(h instanceof C)g=c=null,d=0,a.appendChild(f),e=null;else{if(k||!e){var k=F.a.a[h.m],p=h.h,m=document.createElement("div"),q=document.createElement("div"),t=document.createElement("span");
-m.A=document.createElement("img");m.A.className="slackmsg-author-img";t.className="slackmsg-author-name";k?(t.textContent=k.name,m.A.src=k.b.small):(t.textContent=p||"?",m.A.src="");q.appendChild(m.A);q.appendChild(t);q.className="slackmsg-author";m.className="slackmsg-authorGroup";m.appendChild(q);m.content=document.createElement("div");m.content.className="slackmsg-author-messages";m.appendChild(m.content);e=m;P.push(e);a.appendChild(e)}c=h;g=f;e.content.appendChild(f)}}});var f=document.getElementById("chatWindow");
-f.textContent="";f.appendChild(a);f.scrollTop=f.scrollHeight-f.clientHeight;window.hasFocus&&Ga()}
-function Ma(a){function b(a,b){for(b=b||a.target;b!==a.currentTarget&&b;){if(b.classList.contains("slackmsg-item"))return b.id;b=b.parentElement}}for(var c=a.target;c!==a.currentTarget&&c&&!c.classList.contains("slackmsg-hover");){if(c.parentElement&&c.classList.contains("slackmsg-attachment-actions-item")){var d=b(a,c),g=c.dataset.P,e=c.dataset.O;if(d&&void 0!==g&&void 0!==e){var d=parseFloat(d.split("_")[1]),f=fa(d);f&&f.a[g]&&f.a[g].actions&&f.a[g].actions[e]&&Na(f,f.a[g],f.a[g].actions[e])}}else if(c.parentElement&&
-c.parentElement.classList.contains("slackmsg-hover")){if(d=b(a,c))d=parseFloat(d.split("_")[1]),(f=fa(d))&&c.classList.contains("slackmsg-hover-reply")?(X&&(X=null,Y()),V!==f&&(V=f,W())):f&&c.classList.contains("slackmsg-hover-reaction")?Oa.D(document.body,function(a){a&&Ia(G.id,f.id,a)}):f&&c.classList.contains("slackmsg-hover-edit")?(V&&(V=null,W()),X!==f&&(X=f,Y())):f&&c.classList.contains("slackmsg-hover-remove")&&(V&&(V=null,W()),X&&(X=null,Y()),Pa(f));break}c=c.parentElement}}
-function Na(a,b,c){function d(){var d=JSON.stringify({actions:[c],attachment_id:b.id,callback_id:b.callback_id,channel_id:g,is_ephemeral:a instanceof D,message_ts:a.id}),f=a.m,h=new FormData,l=new XMLHttpRequest;h.append("payload",d);h.append("service_id",f);l.open("POST","api/attachmentAction");l.send(h)}var g=G.id;c.confirm?sa(ra(new L(c.confirm.title,c.confirm.text),c.confirm.ok_text,c.confirm.dismiss_text),d).D():d()}function U(){document.getElementById("msgInput").focus()}
-function ya(){var a=document.location.hash.substr(1),b=F.a.f[a];b&&b!==G?Qa(b):(a=F.a.a[a])&&a.l&&Qa(a.l)}function Ra(){var a=document.getElementById("chatWindow").getBoundingClientRect().top;P.forEach(function(b){var c=b.A,d=c.clientHeight;b=b.getBoundingClientRect();c.style.top=Math.max(0,Math.min(a-b.top,b.height-d-d/2))+"px"})}
-document.addEventListener("DOMContentLoaded",function(){ga();document.getElementById("chatWindow").addEventListener("click",Ma);window.addEventListener("hashchange",function(){document.location.hash&&"#"===document.location.hash[0]&&ya()});document.getElementById("fileUploadCancel").addEventListener("click",function(a){a.preventDefault();document.getElementById("fileUploadError").classList.add("hidden");document.getElementById("fileUploadContainer").classList.add("hidden");document.getElementById("fileUploadInput").value=
-"";return!1});document.getElementById("fileUploadForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("fileUploadInput");var b=a.value;b&&(b=b.substr(b.lastIndexOf("\\")+1),Sa(b,a.files[0],function(a){var b=document.getElementById("fileUploadError");a?(b.textContent=a,b.classList.remove("hidden")):(b.classList.add("hidden"),document.getElementById("fileUploadInput").value="",document.getElementById("fileUploadContainer").classList.add("hidden"))}));return!1});
-document.getElementById("attachFile").addEventListener("click",function(a){a.preventDefault();G&&document.getElementById("fileUploadContainer").classList.remove("hidden");return!1});document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");G&&a.value&&Ta(a.value)&&(a.value="",V&&(V=null,W()),X&&(X=null,W()),document.getElementById("slashList").textContent="");U();return!1});window.addEventListener("blur",function(){window.hasFocus=
-!1});window.addEventListener("focus",function(){window.hasFocus=!0;Q=0;G&&Ga();U()});document.getElementById("chatWindow").addEventListener("scroll",Ra);var a=0;document.getElementById("msgInput").addEventListener("input",function(){if(G){var b=Date.now();a+3E3<b&&(F.a.b.c||G instanceof x)&&(Ua(),a=b);var b=[],c=this.value;if("/"===this.value[0]){var d=c.indexOf(" "),g=-1!==d,d=-1===d?c.length:d,c=c.substr(0,d),e;for(e in F.a.g.data){var f=F.a.g.data[e];(!g&&f.name.substr(0,d)===c||g&&f.name===c)&&
-b.push(f)}}b.sort(function(a,b){return a.B.localeCompare(b.B)||a.name.localeCompare(b.name)});var d=document.getElementById("slashList"),g=document.createDocumentFragment(),h;d.textContent="";e=0;for(c=b.length;e<c;e++)f=b[e],h!==f.B&&(h=f.B,g.appendChild(Va(f.B))),g.appendChild(Wa(f));d.appendChild(g)}});window.hasFocus=!0;(function(){var a=document.getElementById("emojiButton");if("makeEmoji"in window){var c=window.makeEmoji("smile");c?a.innerHTML="<span class='emoji-small'>"+c.outerHTML+"</span>":
-a.style.backgroundImage='url("smile.svg")';(c=window.makeEmoji("paperclip"))?document.getElementById("attachFile").innerHTML="<span class='emoji-small'>"+c.outerHTML+"</span>":document.getElementById("attachFile").style.backgroundImage='url("public/paperclip.svg")';a.addEventListener("click",function(){Oa.D(document.body,function(a){a&&(document.getElementById("msgInput").value+=":"+a+":");U()})})}else a.classList.add("hidden")})();Xa()});function R(){var a=document.createElement("span"),b=document.createElement("span"),c=document.createElement("span"),d=document.createElement("span");a.className="typing-container";b.className="typing-dot1";c.className="typing-dot2";d.className="typing-dot3";b.textContent=c.textContent=d.textContent=".";a.appendChild(b);a.appendChild(c);a.appendChild(d);return a}var xa=function(){var a={};return function(b){var c=a[b];c||(c=a[b]=document.createElement("header"),c.textContent=b);return c}}();
-function Ja(a,b,c){var d=document.createElement("div"),g=document.createElement("div"),e=document.createElement("div"),f=document.createElement("div"),h=document.createElement("span"),l=document.createElement("ul"),k=document.createElement("li"),p=document.createElement("ul"),m=document.createElement("ul"),q=F.a.a[b.m];d.id=a+"_"+b.j;d.className="slackmsg-item";e.className="slackmsg-ts";f.className="slackmsg-msg";h.className="slackmsg-author-name";l.className="slackmsg-hover";k.className="slackmsg-hover-reply";
-e.innerHTML=K.F(b.j);f.innerHTML=O(b.c);h.textContent=q?q.name:b.h||"?";l.appendChild(k);if("makeEmoji"in window){var t=document.createElement("li"),u=window.makeEmoji("arrow_heading_down"),v=window.makeEmoji("smile"),z=window.makeEmoji("pencil2"),q=window.makeEmoji("x");t.className="slackmsg-hover-reaction";v?(t.classList.add("emoji-small"),t.appendChild(v)):t.style.backgroundImage='url("smile.svg")';u?(k.classList.add("emoji-small"),k.appendChild(u)):k.style.backgroundImage='url("repl.svg")';l.appendChild(t);
-b.m===F.a.b.id&&(k=document.createElement("li"),k.className="slackmsg-hover-edit",z?k.classList.add("emoji-small"):k.style.backgroundImage='url("edit.svg")',k.appendChild(z),l.appendChild(k),k=document.createElement("li"),k.className="slackmsg-hover-remove",q?k.classList.add("emoji-small"):k.style.backgroundImage='url("remove.svg")',k.appendChild(q),l.appendChild(k))}else k.style.backgroundImage='url("repl.svg")',b.m===F.a.b.id&&(k=document.createElement("li"),k.className="slackmsg-hover-edit",k.style.backgroundImage=
-'url("edit.svg")',l.appendChild(k),k=document.createElement("li"),k.className="slackmsg-hover-remove",k.style.backgroundImage='url("remove.svg")',l.appendChild(k));b instanceof D&&(q=document.createElement("span"),q.className="slackmsg-notice",q.textContent=K.J,g.appendChild(q));g.appendChild(h);g.appendChild(f);g.appendChild(e);g.appendChild(p);b.u&&(e=document.createElement("div"),e.innerHTML=K.u,e.className="slackmsg-edited",g.appendChild(e));g.appendChild(m);g.className="slackmsg-content";p.className=
-"slackmsg-attachments";m.className="slackmsg-reactions";if(!0!==c){if(b.b)for(var A in b.b){c=a;e=b.id;f=A;h=b.b[A];if(q=ua(f)){for(var k=document.createElement("li"),z=document.createElement("a"),t=document.createElement("span"),u=document.createElement("span"),v=[],J=0,B=h.length;J<B;J++){var w=F.a.a[h[J]];w&&v.push(w.name)}v.sort();u.textContent=v.join(", ");t.appendChild(q);t.className="emoji-small";z.href="javascript:toggleReaction('"+c+"', '"+e+"', '"+f+"')";z.appendChild(t);z.appendChild(u);
-k.className="slackmsg-reaction-item";k.appendChild(z);c=k}else console.warn("Reaction id not found: "+f),c=null;c&&m.appendChild(c)}a=0;for(m=b.a.length;a<m;a++)(A=b.a[a])&&(A=Ya(A,a))&&p.appendChild(A)}d.appendChild(g);d.appendChild(l);return d}function Za(a){var b={good:"#2fa44f",warning:"#de9e31",danger:"#d50200"};if(a){if("#"===a[0])return a;if(b[a])return b[a]}return"#e3e4e6"}
-function Ya(a,b){var c=document.createElement("li"),d=document.createElement("div"),g=document.createElement("div"),e=document.createElement("a"),f=document.createElement("div"),h=document.createElement("img"),l=document.createElement("a"),k=document.createElement("div"),p=document.createElement("div"),m=document.createElement("div"),q=document.createElement("img"),t=document.createElement("div");c.className="slackmsg-attachment";d.style.borderColor=Za(a.color||"");d.className="slackmsg-attachment-block";
-g.className="slackmsg-attachment-pretext";a.pretext?g.innerHTML=O(a.pretext):g.classList.add("hidden");e.target="_blank";a.title?(e.innerHTML=O(a.title),a.title_link&&(e.href=a.title_link),e.className="slackmsg-attachment-title"):e.className="hidden slackmsg-attachment-title";l.target="_blank";f.className="slackmsg-author";a.author_name&&(l.innerHTML=O(a.author_name),l.href=a.author_link||"",l.className="slackmsg-author-name",h.className="slackmsg-author-img",a.author_icon&&(h.src=a.author_icon,f.appendChild(h)),
-f.appendChild(l));k.className="slackmsg-attachment-content";h=O(a.text||"");p.className="slackmsg-attachment-text";h&&""!=h?p.innerHTML=h:p.classList.add("hidden");m.className="slackmsg-attachment-thumb";a.thumb_url?(h=document.createElement("img"),h.src=a.thumb_url,m.appendChild(h),d.classList.add("has-thumb"),a.video_html&&(m.dataset.video=a.video_html)):m.classList.add("hidden");q.className="slackmsg-attachment-img";a.image_url?q.src=a.image_url:q.classList.add("hidden");t.className="slackmsg-attachment-footer";
-a.footer&&(h=document.createElement("span"),h.className="slackmsg-attachment-footer-text",h.innerHTML=O(a.footer),a.footer_icon&&(l=document.createElement("img"),l.src=a.footer_icon,l.className="slackmsg-attachment-footer-icon",t.appendChild(l)),t.appendChild(h));a.ts&&(h=document.createElement("span"),h.className="slackmsg-ts",h.innerHTML=K.F(a.ts),t.appendChild(h));k.appendChild(p);k.appendChild(m);d.appendChild(e);d.appendChild(f);d.appendChild(k);d.appendChild(q);if(a.fields&&a.fields.length){var u=
-document.createElement("ul");d.appendChild(u);u.className="slackmsg-attachment-fields";a.fields.forEach(function(a){var b=a.title||"",c=a.value||"";a=!!a["short"];var d=document.createElement("li"),e=document.createElement("div"),g=document.createElement("div");d.className="field";a||d.classList.add("field-long");e.className="field-title";e.textContent=b;g.className="field-text";g.innerHTML=O(c);d.appendChild(e);d.appendChild(g);d&&u.appendChild(d)})}if(a.actions&&a.actions.length)for(e=document.createElement("ul"),
-e.className="slackmsg-attachment-actions "+na,d.appendChild(e),f=0,k=a.actions.length;f<k;f++)(p=a.actions[f])&&(p=$a(b,f,p))&&e.appendChild(p);d.appendChild(t);c.appendChild(g);c.appendChild(d);return c}function $a(a,b,c){var d=document.createElement("li"),g=Za(c.style);d.textContent=c.text;g!==Za()&&(d.style.color=g);d.style.borderColor=g;d.dataset.P=a;d.dataset.O=b;d.className="slackmsg-attachment-actions-item "+M;return d}
-function Ca(a){var b=document.createElement("li"),c=document.createElement("span");c.textContent=a.name;b.appendChild(R());b.appendChild(c);return b}function Va(a){var b=document.createElement("lh");b.textContent=a;b.className="slack-command-header";return b}
-function Wa(a){var b=document.createElement("li"),c=document.createElement("span"),d=document.createElement("span"),g=document.createElement("span");c.textContent=a.name;d.textContent=a.usage;g.textContent=a.a;b.appendChild(c);b.appendChild(d);b.appendChild(g);b.className="slack-command-item";c.className="slack-command-name";d.className="slack-command-usage";g.className="slack-command-desc";return b};var Oa=function(){function a(a,b){for(var c=a.target;c!==l&&c&&"LI"!==c.nodeName;)c=c.parentElement;c&&"LI"===c.nodeName&&c.id&&"emojibar-"===c.id.substr(0,9)?b(c.id.substr(9)):b(null)}function b(){if(!c())return!1;B&&B(null);return!0}function c(){return l.parentElement?(l.parentElement.removeChild(k),l.parentElement.removeChild(l),!0):!1}function d(a){var b=0,c;a=void 0===a?t.value:a;if(h()){var d=window.searchEmojis(a);c=g(d);for(var f in u)u[f].visible&&(u[f].visible=!1,m.removeChild(u[f].i));
-f=0;for(var k=c.length;f<k;f++){var l=c[f].name,w=u[l];if(!w){var w=u,A=l,p=l,l=window.makeEmoji(d[l]),B=document.createElement("span");B.appendChild(l);B.className="emoji-medium";l=e(p,B);w=w[A]=l}w.visible||(w.visible=!0,m.appendChild(w.i));b++}}for(f in v)v[f].visible&&(v[f].visible=!1,q.removeChild(v[f].i));c=g(F.a.h.data);f=0;for(k=c.length;f<k;f++)l=c[f].name,""!==a&&l.substr(0,a.length)!==a||"alias:"===F.a.h.data[l].substr(0,6)||(w=v[l],w||(d=v,A=w=l,l=F.a.h.data[l],p=document.createElement("span"),
-B=document.createElement("span"),p.className="emoji emoji-custom",p.style.backgroundImage='url("'+l+'")',B.appendChild(p),B.className="emoji-medium",l=e(A,B),w=d[w]=l),w.visible||(w.visible=!0,q.appendChild(w.i)),b++);return b}function g(a){var b=F.a.b.a.b,c=[],d;for(d in a){var e={name:d,K:0,count:0};a[d].names&&a[d].names.forEach(function(a){e.count+=b[a]||0});c.push(e)}return c=c.sort(function(a,b){var c=b.count-a.count;return c?c:a.K-b.K})}function e(a,b){var c=document.createElement("li");c.appendChild(b);
-c.className="emojibar-list-item";c.id="emojibar-"+a;return{visible:!1,i:c}}function f(a){var b=document.createElement("img"),c=document.createElement("div");b.src=a;c.appendChild(b);c.className="emojibar-header";return c}function h(){return"searchEmojis"in window}var l=document.createElement("div"),k=document.createElement("div"),p=document.createElement("div"),m=document.createElement("ul"),q=document.createElement("ul"),t=document.createElement("input"),u={},v={},z=document.createElement("div"),
-A=document.createElement("span"),J=document.createElement("span"),B;k.addEventListener("click",function(a){var c=l.getBoundingClientRect();(a.screenY<c.top||a.screenY>c.bottom||a.screenX<c.left||a.screenX>c.right)&&b()});k.className="emojibar-overlay";l.className="emojibar";p.className="emojibar-emojis";z.className="emojibar-detail";A.className="emojibar-detail-img";J.className="emojibar-detail-name";m.className=q.className="emojibar-list";t.className="emojibar-search";z.appendChild(A);z.appendChild(J);
-p.appendChild(f(window.emojiProviderHeader));p.appendChild(m);p.appendChild(f("emojicustom.png"));p.appendChild(q);l.appendChild(p);l.appendChild(z);l.appendChild(t);t.addEventListener("keyup",function(){d()});l.addEventListener("mousemove",function(b){a(b,function(a){var b=a?u[a]||v[a]:null;b?(A.innerHTML=b.i.outerHTML,J.textContent=":"+a+":"):(A.textContent="",J.textContent="")})});l.addEventListener("click",function(b){a(b,function(a){a&&c()&&B&&B(a)})});return{isSupported:h,D:function(a,b){return h()?
-(B=b,a.appendChild(k),a.appendChild(l),t.value="",d(),t.focus(),!0):!1},search:d,close:b}}();var F,S=[];function ab(){this.c=0;this.a=new ca;this.b={}}
-ab.prototype.update=function(a){var b=Date.now();a.v&&(this.c=a.v);if(a["static"]){var c=this.a,d=a["static"],g=Date.now();if(d.users)for(var e=0,f=d.users.length;e<f;e++){var h=c.a[d.users[e].id];h||(h=c.a[d.users[e].id]=new H(d.users[e].id));h.update(d.users[e],g)}if(d.channels)for(e=0,f=d.channels.length;e<f;e++){h=c.f[d.channels[e].id];if(!h){var h=c.f,l=d.channels[e].id,k;k=d.channels[e];k=k.pv?new x(k.id,c.a[k.user]):new r(k.id);h=h[l]=k}h.update(d.channels[e],c,g)}d.emojis&&(c.h.data=d.emojis,
-c.h.version=g);if(void 0!==d.commands){c.g.data={};for(e in d.commands)c.g.data[e]=new aa(d.commands[e]);c.g.version=g}d.team&&(c.l||(c.l=new n(d.team.id)),c.l.update(d.team,g));c.s=Math.max(c.s,g);d.self&&(c.b=c.a[d.self.id]||null,c.b?(c.b.a||(c.b.a=new ba),c.b.a.update(d.self.prefs,g)):c.s=0);if(void 0!==d.typing)for(e in c.c={},d.typing){c.c[e]={};for(var p in d.typing[e])c.c[e][p]=g}}for(var m in this.a.f){var q=this.a.f[m];q.a===q.b&&(c=S.indexOf(q),-1!==c&&S.splice(c,1))}if(a.live){for(q in a.live)(m=
-this.b[q])?da(m,a.live[q],b):this.b[q]=new E(q,250,a.live[q],b);for(var t in a.live)(b=this.a.f[t])?(this.b[t].a.length&&(q=this.b[t],b.a=Math.max(b.a,q.a[q.a.length-1].j)),b.h||(bb(b,a.live[t]),G&&a.live[G.id]&&Fa())):F.c=0}a["static"]&&(wa(),a["static"].typing&&Aa())};setInterval(function(){var a=F.a,b=Date.now(),c=!1,d;for(d in a.c){var g=!0,e;for(e in a.c[d])a.c[d][e]+3E3<b?(delete a.c[d][e],c=!0):g=!1;g&&(delete a.c[d],c=!0)}c&&Aa()},1E3);
-function bb(a,b){if(a!==G||!window.hasFocus){var c=new RegExp("<@"+F.a.b.id),d=!1,g=!1,e=!1;b.forEach(function(b){if(!(parseFloat(b.ts)<=a.b)){g=!0;var f;if(!(f=a instanceof x)&&(f=b.text)&&!(f=b.text.match(c)))a:{f=F.a.b.a.a;for(var h=0,p=f.length;h<p;h++)if(-1!==b.text.indexOf(f[h])){f=!0;break a}f=!1}f&&(-1===S.indexOf(a)&&(e=!0,S.push(a)),d=!0)}});if(g){T();var f=document.getElementById("room_"+a.id);f&&(f.classList.add("unread"),d&&f.classList.add("unreadHi"));e&&!window.hasFocus&&La()}}}
-function Ga(){var a=G,b=S.indexOf(a);if(a.a>a.b){var c=new XMLHttpRequest;c.open("POST","api/markread?room="+a.id+"&ts="+a.a,!0);c.send(null);a.b=a.a}0<=b&&(S.splice(b,1),T());a=document.getElementById("room_"+a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}F=new ab;var za=function(){function a(a,b){b.sort(function(){return Math.random()-.5});for(var c=0,d=20;d<l-40;d+=p)for(var f=0;f+p<=k;f+=p)e(a,b[c],d,f),c++,c===b.length&&(b.sort(function(a,b){return a.C?b.C?Math.random()-.5:-1:1}),c=0)}function b(a,d){for(var e=0,f=a.length;e<f;e++)if(void 0===a[e].C){c(a[e].src,function(c){a[e].C=c;b(a,d)});return}var g=[];a.forEach(function(a){a.C&&g.push(a.C)});d(g)}function c(a,b){var c=new XMLHttpRequest;c.responseType="blob";c.onreadystatechange=function(){if(4===
-c.readyState)if(c.response){var a=new Image;a.onload=function(){var c=document.createElement("canvas");c.height=c.width=t;c=c.getContext("2d");c.drawImage(a,0,0,t,t);for(var c=c.getImageData(0,0,t,t),d=0,e=0;e<c.width*c.height*4;e+=4)c.data[e]=c.data[e+1]=c.data[e+2]=(c.data[e]+c.data[e+1]+c.data[e+2])/3,c.data[e+3]=50,d+=c.data[e];if(50>d/(c.height*c.width))for(e=0;e<c.width*c.height*4;e+=4)c.data[e]=c.data[e+1]=c.data[e+2]=255-c.data[e];b(c)};a.onerror=function(){b(null)};a.src=window.URL.createObjectURL(c.response)}else b(null)};
-c.open("GET",a,!0);c.send(null)}function d(){var a=h.createLinearGradient(0,0,0,k);a.addColorStop(0,"#4D394B");a.addColorStop(1,"#201820");h.fillStyle=a;h.fillRect(0,0,l,k);return h.getImageData(0,0,l,k)}function g(a,b){for(var c=(a.height-b.height)/2,d=0;d<b.height;d++)for(var e=0;e<b.width;e++){var f=b.data[4*(d*b.width+e)]/255,g=4*((d+c)*a.width+e+c);a.data[g]*=f;a.data[g+1]*=f;a.data[g+2]*=f}return a}function e(a,b,c,d){var e=Math.floor(d);a=[a.data[e*l*4+0],a.data[e*l*4+1],a.data[e*l*4+2]];h.fillStyle=
-"#"+(1.1*a[0]<<16|1.1*a[1]<<8|1.1*a[2]).toString(16);h.beginPath();h.moveTo(c+p/2,d+m);h.lineTo(c-m+p,d+p/2);h.lineTo(c+p/2,d-m+p);h.lineTo(c+m,d+p/2);h.closePath();h.fill();h.putImageData(g(h.getImageData(c+m,d+m,q,q),b),c+m,d+m)}var f=document.createElement("canvas"),h=f.getContext("2d"),l=f.width=250,k=f.height=290,p=(l-40)/3,m=.1*p,q=Math.floor(p-2*m),t=.5*q,u,v=[],z=!1;return function(c){if(u)c(u);else if(z)v.push(c);else{var e=d(),g=[];z=!0;v.push(c);for(var h in F.a.a)F.a.a[h].g||F.a.a[h].s||
-g.push({src:"api/avatar?user="+h});b(g,function(b){a(e,b);u=f.toDataURL();v.forEach(function(a){a(u)})})}}}();var Z=0,G=null,V=null,X=null;function cb(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var c=null,d=2===Math.floor(b.status/100);if(d){Z&&(Z=0,Da(!0));c=b.response;try{c=JSON.parse(c)}catch(g){c=null}}else Z?(Z+=Math.floor((Z||5)/2),Z=Math.min(60,Z)):(Z=5,Da(!1));a(d,c)}else Z&&(Z=0,Da(!0)),cb(a)};b.open("GET","api?v="+F.c,!0);b.send(null)}function Ua(){var a=new XMLHttpRequest;a.open("POST","api/typing?room="+G.id,!0);a.send(null)}
-function db(a,b){a?(b&&F.update(b),Xa()):setTimeout(Xa,1E3*Z)}function Xa(){cb(db)}function Qa(a){G&&document.getElementById("room_"+G.id).classList.remove("selected");document.getElementById("room_"+a.id).classList.add("selected");document.body.classList.remove("no-room-selected");G=a;Ea();G.a&&!F.b[G.id]&&(a=new XMLHttpRequest,a.open("GET","api/hist?room="+G.id,!0),a.send(null))}
-function Sa(a,b,c){var d=G;new FileReader;var g=new FormData,e=new XMLHttpRequest;g.append("file",b);g.append("filename",a);e.onreadystatechange=function(){4===e.readyState&&(204===e.status?c(null):c(e.statusText))};e.open("POST","api/file?room="+d.id);e.send(g)}
-function Ta(a){if(X){var b=new XMLHttpRequest;b.open("PUT","api/msg?room="+G.id+"&ts="+X.id+"&text="+encodeURIComponent(a),!0);b.send(null);return!0}if("/"===a[0]){var c=a.indexOf(" "),b=-1===c?"":a.substr(c);return(a=F.a.g.data[a.substr(0,-1===c?void 0:c)])?(c=new XMLHttpRequest,c.open("POST","api/cmd?room="+G.id+"&cmd="+encodeURIComponent(a.name.substr(1))+"&args="+encodeURIComponent(b.trim()),!0),c.send(null),!0):!1}var b=G,c=V,d=new XMLHttpRequest;a="api/msg?room="+b.id+"&text="+encodeURIComponent(a);
-if(c){var g=F.a.a[c.m],e="Message";"C"===b.id[0]?e="Channel message":"D"===b.id[0]?e="Direct message":"G"===b.id[0]&&(e="Group message");a+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:c.c,author_name:"<@"+g.id+"|"+g.name+">",author_icon:g.b.small,text:c.c,footer:e,ts:c.j}]))}d.open("POST",a,!0);d.send(null);return!0}function Pa(a){var b=new XMLHttpRequest;b.open("DELETE","api/msg?room="+G.id+"&ts="+a.id,!0);b.send(null)}
-function Ia(a,b,c){var d=new XMLHttpRequest;d.open("POST","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(c),!0);d.send(null)};function va(a){for(var b in a)if(a.hasOwnProperty(b))return!1;return!0};
+"use strict";(function(){
+var q;function aa(a){this.id=a;this.version=0}aa.prototype.update=function(a,b){void 0!==a.name&&(this.name=a.name);this.version=Math.max(this.version,b)};function da(a){this.a=a.desc;this.name=a.name;this.type=a.type;this.usage=a.usage;this.R=a.category}function ea(){this.ia={};this.w=[];this.version=0}
+ea.prototype.update=function(a,b){a.emoji_use&&(this.ia=JSON.parse(a.emoji_use));a.highlight_words?this.w=(a.highlight_words||"").split(",").filter(function(a){return""!==a.trim()}):a.highlights&&(this.w=a.highlights);this.version=Math.max(this.version,b)};function fa(){this.b=null;this.l={};this.m={};this.self=null;this.a={version:0,data:{}};this.i={version:0,data:{}};this.s={};this.D=0}
+function ga(a,b,c){var d=d||"";b.team&&(a.b||(a.b=new aa(b.team.id)),a.b.update(b.team,c));if(b.users)for(var e=0,g=b.users.length;e<g;e++){var f=a.m[d+b.users[e].id];f||(f=a.m[d+b.users[e].id]=new ha(b.users[e].id));f.update(b.users[e],c)}if(b.channels)for(e=0,g=b.channels.length;e<g;e++){f=a.l[d+b.channels[e].id];if(!f){var f=a.l,n=d+b.channels[e].id;var h=b.channels[e];h=h.pv?new r(h.id,a.m[h.user]):new u(h.id);f=f[n]=h}f.update(b.channels[e],a,c,d)}b.emojis&&(a.a.data=b.emojis,a.a.version=c);
+if(void 0!==b.commands){a.i.data={};for(e in b.commands)a.i.data[e]=new da(b.commands[e]);a.i.version=c}b.self&&(a.self=a.m[d+b.self.id]||null,b.self.prefs&&(a.self.O||(a.self.O=new ea),a.self.O.update(b.self.prefs,c)));a.D=Math.max(a.D,c)}"undefined"!==typeof module&&(module.H.$a=fa,module.H.ab=aa,module.H.cb=da);function u(a){this.id=a;this.J=!1;this.A=0;this.m={};this.version=0}
+u.prototype.update=function(a,b,c,d){d=d||"";void 0!==a.name&&(this.name=a.name);void 0!==a.is_archived&&(this.Y=a.is_archived);void 0!==a.is_member&&(this.i=a.is_member);void 0!==a.last_read&&(this.A=Math.max(parseFloat(a.last_read),this.A));void 0!==a.last_msg&&(this.I=parseFloat(a.last_msg));void 0!==a.is_private&&(this.b=a.is_private);void 0!==a.is_starred&&(this.J=a.is_starred);if(a.members&&(this.m={},a.members))for(var e=0,g=a.members.length;e<g;e++){var f=b.m[d+a.members[e]];this.m[f.id]=
+f;f.l[this.id]=this}this.version=Math.max(this.version,c)};function r(a,b){u.call(this,a);this.a=b;this.name=this.a.name;this.b=!0;b.na=this}r.prototype=Object.create(u.prototype);r.prototype.constructor=r;"undefined"!==typeof module&&(module.H.jb=u,module.H.ib=r);function y(a,b){this.G=a.user;this.username=a.username;this.id=a.id||a.ts;this.j=parseFloat(a.ts);this.text="";this.o=[];this.i=this.C=this.J=!1;this.B={};this.version=b;this.update(a,b)}function A(a,b){y.call(this,a,b)}function C(a,b){y.call(this,a,b)}
+y.prototype.update=function(a,b){if(a){if(this.text=a.text||"",a.attachments&&(this.o=a.attachments),this.J=!!a.is_starred,this.C=void 0===a.edited?!1:a.edited,this.i=!!a.removed,a.reactions){var c={};a.reactions.forEach(function(a){c[a.name]=[];a.users.forEach(function(b){c[a.name].push(b)})});this.B=c}}else this.i=!0;this.version=b};function D(a,b,c,d){this.id="string"===typeof a?a:a.id;this.a=[];this.Na=0;this.i=b;c&&ia(this,c,d)}
+function ia(a,b,c){var d=0;b.forEach(function(a){d=Math.max(this.push(a,c),d)}.bind(a));ja(a)}D.prototype.b=function(a,b){return!0===a.isMeMessage?new A(a,b):!0===a.isNotice?new C(a,b):new y(a,b)};D.prototype.push=function(a,b){for(var c,d=!1,e,g=0,f=this.a.length;g<f;g++)if(c=this.a[g],c.id===a.id){e=c.update(a,b);d=!0;break}d||(c=this.b(a,b),this.a.push(c),e=c.j);for(;this.a.length>this.i;)this.a.shift();return e||0};function ka(a){return a.a[a.a.length-1]}
+function la(a,b){for(var c=0,d=a.a.length;c<d;c++)if(a.a[c].id==b)return a.a[c];return null}function ja(a){a.a.sort(function(a,c){return a.j-c.j})}A.prototype=Object.create(y.prototype);A.prototype.constructor=A;C.prototype=Object.create(y.prototype);C.prototype.constructor=C;"undefined"!==typeof module&&(module.H={fb:y,eb:A,hb:C,kb:D});function ha(a){this.id=a;this.l={};this.na=this.O=null;this.version=0}ha.prototype.update=function(a,b){void 0!==a.name&&(this.name=a.name);void 0!==a.deleted&&(this.Da=a.deleted);void 0!==a.status&&(this.status=a.status);void 0!==a.presence&&(this.a="away"!==a.presence);void 0!==a.isPresent&&(this.a=a.isPresent);a.isBot&&(this.Ta=a.isBot);this.version=Math.max(this.version,b)};"undefined"!==typeof module&&(module.H.bb=ha);function ma(){this.a=[]}ma.prototype.push=function(a){this.a.push(a)};function na(a,b){for(var c=0,d=a.a.length;c<d;c++)if(b===oa(a.a[c]))return a.a[c];return null}function pa(a,b){for(var c=0,d=a.a.length;c<d;c++){var e=a.a[c],g;for(g in e.l)if(!0===b(e.l[g],g))return}}function qa(a){for(var b=E.context,c=0,d=b.a.length;c<d&&!0!==a(b.a[c]);c++);}function H(a,b){for(var c=0,d=a.a.length;c<d;c++)if(a.a[c].l[b])return a.a[c];return null}
+function I(a){for(var b=E.context,c=0,d=b.a.length;c<d;c++){var e=b.a[c].l[a];if(e)return e}return null}function ra(a){for(var b=E.context,c=[],d=0,e=b.a.length;d<e;d++){var g=b.a[d].l,f;for(f in g)a&&!a(g[f],b.a[d],f)||c.push(f)}return c}function J(a){for(var b=E.context,c=0,d=b.a.length;c<d;c++){var e=b.a[c].m[a];if(e)return e}return null}function sa(a){for(var b=E.context,c=0,d=b.a.length;c<d;c++)if(b.a[c].self.id===a)return!0;return!1}"undefined"!==typeof module&&(module.H.gb=ma);var K={},L,ta=[];function ua(){if(!c){for(var a=0,b=navigator.languages.length;a<b;a++)if(K.hasOwnProperty(navigator.languages[a])){var c=navigator.languages[a];break}c||(c="en")}L=K[c];console.log("Loading language pack: "+c);if(L.c)for(var d in L.c)document.getElementById(d).textContent=L.c[d];ta.forEach(function(a){a()})};K.fr={Za:"Utilisateur inconnu",Ya:"Channel inconnu",Ia:"Nouveau message",message:"Message",Ha:"Reseau",Ja:"(visible seulement par vous)",J:"Favoris",l:"Discutions",La:"Discutions priv\u00e9es",Ma:"Partage sa position GPS",ok:"Ok",Ea:"Annuler",W:function(a){"string"!==typeof a&&(a=parseFloat(a));var b=new Date,c=new Date;a=new Date(a);b.setHours(0,0,0,0);c.setTime(b.getTime());c.setDate(c.getDate()-1);return a.getTime()>b.getTime()?a.toLocaleTimeString():a.getTime()>c.getTime()?"hier, "+a.toLocaleTimeString():
+a.toLocaleString()},c:{fileUploadCancel:"Annuler",neterror:"Impossible de se connecter au chat !"}};K.fr.C=function(a){return"(edit&eacute; "+K.fr.W(a)+")"};K.en={Za:"Unknown member",Ya:"Unknown channel",Ia:"New message",message:"Message",Ha:"Network",Ja:"(only visible to you)",J:"Starred",l:"Channels",La:"Direct messages",Ma:"Share your GPS location",ok:"Ok",Ea:"Cancel",W:function(a){"string"!==typeof a&&(a=parseFloat(a));var b=new Date,c=new Date;a=new Date(a);b.setHours(0,0,0,0);c.setTime(b.getTime());c.setDate(c.getDate()-1);return a.getTime()>b.getTime()?a.toLocaleTimeString():a.getTime()>c.getTime()?"yesterday, "+a.toLocaleTimeString():a.toLocaleString()},
+c:{fileUploadCancel:"Cancel",neterror:"Cannot connect to chat !"}};K.en.C=function(a){return"(edited "+K.en.W(a)+")"};var va=function(){function a(a){this.text="";this.g=a}function b(b,c,d){this.T=c;this.f=null;this.h=[];this.a=d||"";this.da="<"===this.a;this.oa="*"===this.a;this.ca="_"===this.a;this.ea="~"===this.a||"-"===this.a;this.i=">"===this.a||"&gt;"===this.a;this.X=":"===this.a;this.qa="`"===this.a;this.Ba="```"===this.a;this.ra="\n"===this.a;this.ba=void 0!==d&&-1!==m.w.indexOf(d);this.g=b;this.fa=null;this.b=this.ra||this.ba?c+d.length-1:!1;this.ba&&(this.f=new a(this),this.h.push(this.f),this.f.text=d)}
+function c(a){return"A"<=a&&"Z">=a||"a"<=a&&"z">=a||"0"<=a&&"9">=a||-1!=="\u00e0\u00e8\u00ec\u00f2\u00f9\u00c0\u00c8\u00cc\u00d2\u00d9\u00e1\u00e9\u00ed\u00f3\u00fa\u00fd\u00c1\u00c9\u00cd\u00d3\u00da\u00dd\u00e2\u00ea\u00ee\u00f4\u00fb\u00c2\u00ca\u00ce\u00d4\u00db\u00e3\u00f1\u00f5\u00c3\u00d1\u00d5\u00e4\u00eb\u00ef\u00f6\u00fc\u00ff\u00c4\u00cb\u00cf\u00d6\u00dc\u0178\u00e7\u00c7\u00df\u00d8\u00f8\u00c5\u00e5\u00c6\u00e6\u0153".indexOf(a)}function d(a){a=a||h;for(var c=0,e=a.h.length;c<e;c++){var l=
+a.h[c];if(l instanceof b)if(l.b){if(l=d(l))return l}else return l}return null}function e(a,c){a.g instanceof b&&(a.g.h.splice(a.g.h.indexOf(a)+(c?1:0)),a.g.f=a.g.h[a.g.h.length-1],e(a.g,!0))}function g(a){return a}function f(a){return{link:a,text:a,Ga:!1}}var n,h,m={w:[],V:g,aa:g,Z:f};b.prototype.ta=function(){return this.oa&&!!this.b||this.g instanceof b&&this.g.ta()};b.prototype.wa=function(){return this.ca&&!!this.b||this.g instanceof b&&this.g.wa()};b.prototype.xa=function(){return this.ea&&!!this.b||
+this.g instanceof b&&this.g.xa()};b.prototype.la=function(){return this.X&&!!this.b||this.g instanceof b&&this.g.la()};b.prototype.va=function(){return this.ba&&!!this.b||this.g instanceof b&&this.g.va()};b.prototype.ua=function(){return this.qa&&!!this.b||this.g instanceof b&&this.g.ua()};b.prototype.ka=function(){return this.Ba&&!!this.b||this.g instanceof b&&this.g.ka()};b.prototype.ya=function(){for(var a=0,c=this.h.length;a<c;a++)if(this.h[a]instanceof b&&(!this.h[a].b||this.h[a].ya()))return!0;
+return!1};b.prototype.za=function(a){if("<"===this.a&&">"===n[a])return!0;var b=c(n[a-1]);if(!this.i&&n.substr(a,this.a.length)===this.a){if(!b&&(this.oa||this.ca||this.ea))return!1;if(this.f&&this.ya())return this.f.Ca();if(this.Oa())return!0}return"\n"===n[a]&&this.i?!0:!1};b.prototype.Oa=function(){for(var a=this;a;){for(var c=0,d=a.h.length;c<d;c++)if(a.h[c]instanceof b||a.h[c].text.length)return!0;a=a.fa}return!1};b.prototype.Ca=function(){var a=new b(this.g,this.T,this.a);a.fa=this;this.f&&
+this.f instanceof b&&(a.f=this.f.Ca(),a.h=[a.f]);return a};b.prototype.Pa=function(a){return this.X&&(" "===n[a]||"\t"===n[a])||(this.X||this.da||this.oa||this.ca||this.ea||this.qa)&&"\n"===n[a]?!1:!0};b.prototype.Qa=function(b){if(this.qa||this.X||this.Ba||this.da)return null;if(!this.f||this.f.b||this.f instanceof a){var d=c(n[b-1]),e=c(n[b+1]);if("```"===n.substr(b,3))return"```";var f=h.ma();if(void 0===f||f){if("&gt;"===n.substr(b,4))return"&gt;";if(">"===n[b])return n[b]}if("`"===n[b]&&!d||
+"\n"===n[b]||!(-1===["*","~","-","_"].indexOf(n[b])||!e&&void 0!==n[b+1]&&-1==="*~-_<&".split("").indexOf(n[b+1])||d&&void 0!==n[b-1]&&-1==="*~-_<&".split("").indexOf(n[b-1]))||-1!==[":"].indexOf(n[b])&&e||-1!==["<"].indexOf(n[b]))return n[b];d=0;for(e=m.w.length;d<e;d++)if(f=m.w[d],n.substr(b,f.length)===f)return f}return null};a.prototype.ma=function(){if(""!==this.text.trim())return!1};b.prototype.ma=function(){for(var a=this.h.length-1;0<=a;a--){var b=this.h[a].ma();if(void 0!==b)return b}if(this.ra||
+this.i)return!0};a.prototype.D=function(a){this.text+=n[a];return 1};b.prototype.D=function(c){var d=this.f&&!this.f.b&&this.f.za?this.f.za(c):null;if(d){var e=this.f.a.length;this.f.sa(c);d instanceof b&&(this.f=d,this.h.push(d));return e}if(!this.f||this.f.b||this.f instanceof a||this.f.Pa(c)){if(d=this.Qa(c))return this.f=new b(this,c,d),this.h.push(this.f),this.f.a.length;if(!this.f||this.f.b)this.f=new a(this),this.h.push(this.f);return this.f.D(c)}d=this.f.T+1;h.ja(this.f.T);this.f=new a(this);
+this.f.D(d-1);this.h.pop();this.h.push(this.f);return d-c};b.prototype.sa=function(a){for(var b=this;b;)b.b=a,b=b.fa};b.prototype.ja=function(a){this.b&&this.b>=a&&(this.b=!1);this.h.forEach(function(c){c instanceof b&&c.ja(a)})};a.prototype.innerHTML=function(){if(this.g.la()){for(var a=this.g;a&&!a.X;)a=a.g;if(a){var a=a.a+this.text+a.a,b=m.V(a);return b?b:a}return(a=m.V(this.text))?a:this.text}if(this.g.ka()){if("undefined"!==typeof hljs)try{return a=this.text.match(/^\w+/),hljs.configure({useBR:!0,
+tabReplace:"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"}),a&&hljs.getLanguage(a[0])?hljs.fixMarkup(hljs.highlight(a[0],this.text.substr(a[0].length)).value):hljs.fixMarkup(hljs.highlightAuto(this.text).value)}catch(p){console.error(p)}return this.text.replace(/\n/g,"<br/>")}return m.aa(this.text)};a.prototype.outerHTML=function(){var a="span",b=[],c="";if(this.g.ka()){a="pre";b.push("codeblock");var d=this.innerHTML()}else this.g.ua()?(b.push("code"),d=this.innerHTML()):(this.g.da&&(d=m.Z(this.text))?(a=
+"a",c=' href="'+d.link+'"',d.Ga||(c+=' target="_blank"'),d=m.aa(d.text)):d=this.innerHTML(),this.g.ta()&&b.push("bold"),this.g.wa()&&b.push("italic"),this.g.xa()&&b.push("strike"),this.g.la()&&b.push("emoji"),this.g.va()&&b.push("highlight"));return"<"+a+c+(b.length?' class="'+b.join(" ")+'"':"")+">"+d+"</"+a+">"};b.prototype.outerHTML=function(){var a="";this.i&&(a+='<span class="quote">');this.ra&&(a+="<br/>");this.h.forEach(function(b){a+=b.outerHTML()});this.i&&(a+="</span>");return a};b.prototype.Aa=
+function(a){this.i&&!this.b&&this.sa(a);this.h.forEach(function(c){c instanceof b&&c.Aa(a)})};return function(c,k){k||(k={});m.w=k.w||[];m.V=k.V||g;m.aa=k.aa||g;m.Z=k.Z||f;n=c;h=new b(this,0);k=0;c=n.length;do{for(;k<c;)k+=h.D(k);h.Aa(n.length);if(k=d()){e(k,!1);h.ja(k.T);var l=new a(k.g);l.D(k.T);k.g.h.push(l);k.g.f=l;k=k.T+1}else k=void 0}while(void 0!==k);return h.outerHTML()}}();"undefined"!==typeof module&&(module.H.u=va);function wa(a,b){this.i=a;this.content=b;this.c=xa(this);this.b=ya(this);this.a=[];this.D=[]}
+function xa(a){var b=document.createElement("div"),c=document.createElement("header"),d=document.createElement("span"),e=document.createElement("span"),g=document.createElement("div"),f=document.createElement("footer");b.a=document.createElement("span");b.b=document.createElement("span");d.textContent=a.i;"string"==typeof a.content?g.innerHTML=a.content:g.appendChild(a.content);c.className=za;d.className=Aa;e.className=Ba;e.textContent="x";c.appendChild(d);c.appendChild(e);b.appendChild(c);g.className=
+Ca;b.appendChild(g);b.b.className=Da;b.b.textContent=L.Ea;b.b.addEventListener("click",function(){M(a,!1)});e.addEventListener("click",function(){M(a,!1)});b.a.addEventListener("click",function(){M(a,!0)});f.appendChild(b.b);b.a.className=Da;b.a.textContent=L.ok;f.appendChild(b.a);f.className=Ea+" "+Fa;b.appendChild(f);b.className=Ga;return b}function M(a,b){(b?a.a:a.D).forEach(function(a){a()});a.close()}
+function ya(a){var b=document.createElement("div");b.className=Ha;b.addEventListener("click",function(){M(this,!1)}.bind(a));return b}function Ia(a,b,c){a.c.a.textContent=b;a.c.b.textContent=c;return a}wa.prototype.$=function(a){a=a||document.body;a.appendChild(this.b);a.appendChild(this.c);return this};wa.prototype.close=function(){this.c.remove();this.b.remove();return this};function Ja(a,b){a.a.push(b);return a};var Da="button",Ea="button-container",Ga="dialog",Ha="dialog-overlay",za="dialog-title",Aa="dialog-title-label",Ba="dialog-title-close",Ca="dialog-body",Fa="dialog-footer";var Ka=[],La=0;
+function Ma(){var a=document.createDocumentFragment(),b=ra(function(a){return!a.Y&&!1!==a.i}),c=[],d=[],e=[],g=[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:I(a).name.localeCompare(I(b).name)});b.forEach(function(a){a=I(a);if(a instanceof r){var b;if(b=!a.a.Da){var f=document.createElement("li");b=document.createElement("a");f.id="room_"+a.id;b.href="#"+a.id;f.className="slack-context-room slack-ims";b.textContent=a.a.name;f.appendChild(Na());f.appendChild(b);a.a.a||f.classList.add("away");N===
+a&&f.classList.add("selected");a.I>a.A&&(f.classList.add("unread"),f.classList.add("unreadHi"));b=f}b&&(a.J?c.push(f):g.push(f))}else f=document.createElement("li"),b=document.createElement("a"),f.id="room_"+a.id,b.href="#"+a.id,a.b?(f.className="slack-context-room slack-group",f.dataset.count=Object.keys(a.m||{}).length):f.className="slack-context-room slack-channel",N===a&&f.classList.add("selected"),b.textContent=a.name,f.appendChild(Na()),f.appendChild(b),a.I>a.A&&(f.classList.add("unread"),0<=
+O.indexOf(a)&&f.classList.add("unreadHi")),f&&(a.J?c.push(f):a.b?e.push(f):d.push(f))});c.length&&a.appendChild(Oa(L.J));c.forEach(function(b){a.appendChild(b)});d.length&&a.appendChild(Oa(L.l));d.forEach(function(b){a.appendChild(b)});e.forEach(function(b){a.appendChild(b)});g.length&&a.appendChild(Oa(L.La));g.forEach(function(b){a.appendChild(b)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a);Pa();P();Q&&Qa(Q.b.id,Q.m,function(a){document.getElementById("slackCtx").style.backgroundImage=
+"url("+a+")"})}function Ra(){qa(function(a){var b=a.s,c;for(c in a.self.l)if(!a.self.l[c].Y){var d=document.getElementById("room_"+c);b[c]?d.classList.add("slack-context-typing"):d.classList.remove("slack-context-typing")}for(var e in a.m)(c=a.m[e].na)&&!c.Y&&(d=document.getElementById("room_"+c.id))&&(b[c.id]?d.classList.add("slack-context-typing"):d.classList.remove("slack-context-typing"))});Sa()}
+function Sa(){var a;document.getElementById("whoistyping").textContent="";if(Q&&N&&(a=Q.s[N.id])){var b=document.createDocumentFragment(),c=!1,d;for(d in a)(a=J(d))?b.appendChild(Ta(a)):c=!0;c&&(E.b=0);document.getElementById("whoistyping").appendChild(b)}}function Ua(a){a?document.body.classList.remove("no-network"):document.body.classList.add("no-network");P()}
+function Va(){var a=N.name||(N.a?N.a.name:void 0);if(!a){var b=[];N.m.forEach(function(a){b.push(a.name)});a=b.join(", ")}document.getElementById("currentRoomTitle").textContent=a;Wa();R();document.getElementById("fileUploadContainer").classList.add("hidden");Xa();T&&(T=null,U());V&&(V=null,U());Sa()}
+function U(){if(T){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){T=null;U()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(T.L())}else document.body.classList.remove("replyingTo"),document.getElementById("replyToContainer").textContent="";R()}
+function W(){if(V){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){V=null;W()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(V.L());document.getElementById("msgInput").value=V.text}else document.body.classList.remove("replyingTo"),document.getElementById("replyToContainer").textContent="";R()}
+window.toggleReaction=function(a,b,c){var d=E.a[a],e,g;(d=E.a[a])&&(e=la(d,b))&&(g=H(E.context,a))&&(e.B[c]&&-1!==e.B[c].indexOf(g.self.id)?(d=new XMLHttpRequest,d.open("DELETE","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(c),!0),d.send(null)):Ya(a,b,c))};
+function Za(a){a:{var b={};if(Q){var c;for(c=Q;!b[a];){if(c=c.a.data[a])if("alias:"==c.substr(0,6))b[a]=!0,a=c.substr(6);else{a=document.createElement("span");a.className="emoji-custom emoji";a.style.backgroundImage="url('"+c+"')";break a}break}}}"string"===typeof a&&"makeEmoji"in window&&(a=window.makeEmoji(a));return"string"===typeof a?null:a}function $a(a,b){document.getElementById("linkFavicon").href=a||b?"favicon.png?h="+a+"&m="+b:"favicon_ok.png"}
+function P(){var a=O.length,b="";if(X)b="!"+L.Ha+" - ",document.getElementById("linkFavicon").href="favicon_err.png";else if(a)b="(!"+a+") - ",$a(a,a);else{var c=0;pa(E.context,function(a){a.I>a.A&&c++});c&&(b="("+c+") - ");$a(0,c)}E.context.b&&(b+=E.context.b.name);document.title=b}
+function ab(){if("Notification"in window)if("granted"===Notification.permission){var a=Date.now();if(La+3E4<a){var b=new Notification(L.Ia);La=a;setTimeout(function(){b.close()},5E3)}}else"denied"!==Notification.permission&&Notification.requestPermission()}
+function Wa(){var a=document.createDocumentFragment(),b=N.id,c=null,d=0,e=null,g;Ka=[];E.a[b]&&E.a[b].a.forEach(function(b){if(b.i)b.S();else{var f=b.M(),h=!1;c&&c.G===b.G&&b.G?30>Math.abs(d-b.j)&&!(b instanceof A)?e.classList.add("slackmsg-same-ts"):d=b.j:(d=b.j,h=!0);(!c||c.j<=N.A)&&b.j>N.A?f.classList.add("slackmsg-first-unread"):f.classList.remove("slackmsg-first-unread");if(b instanceof A)e=c=null,d=0,a.appendChild(f),g=null;else{if(h||!g){var h=J(b.G),m=b.username,l=document.createElement("div"),
+k=document.createElement("div"),p=document.createElement("span");l.U=document.createElement("img");l.U.className="slackmsg-author-img";p.className="slackmsg-author-name";h?(p.textContent=h.name,l.U.src="api/avatar?user="+h.id):(p.textContent=m||"?",l.U.src="");k.appendChild(l.U);k.appendChild(p);k.className="slackmsg-author";l.className="slackmsg-authorGroup";l.appendChild(k);l.content=document.createElement("div");l.content.className="slackmsg-author-messages";l.appendChild(l.content);g=l;Ka.push(g);
+a.appendChild(g)}c=b;e=f;g.content.appendChild(f)}}});b=document.getElementById("chatWindow");b.textContent="";b.appendChild(a);b.scrollTop=b.scrollHeight-b.clientHeight;window.hasFocus&&Xa()}
+function bb(a,b){a.classList.contains("slackmsg-hover-reply")?(V&&(V=null,W()),T!==b&&(T=b,U())):a.classList.contains("slackmsg-hover-reaction")?cb.$(document.body,{Xa:N.id,Va:b.id},function(a){a&&Ya(this.Xa,this.Va,a)}):a.classList.contains("slackmsg-hover-edit")?(T&&(T=null,U()),V!==b&&(V=b,W())):a.classList.contains("slackmsg-hover-remove")&&(T&&(T=null,U()),V&&(V=null,W()),db(b))}
+function eb(a){function b(a,b){for(b=b||a.target;b!==a.currentTarget&&b;){if(b.id&&b.classList.contains("slackmsg-item"))return b.id;b=b.parentElement}}for(var c=a.target;c!==a.currentTarget&&c&&!c.classList.contains("slackmsg-hover");){var d;if(c.parentElement&&c.classList.contains("slackmsg-attachment-actions-item")){var e=c.dataset.attachmentIndex,g=c.dataset.actionIndex;if((d=b(a,c))&&void 0!==e&&void 0!==g){d=d.substr(d.lastIndexOf("_")+1);(a=la(E.a[N.id],d))&&a.o[e]&&a.o[e].actions&&a.o[e].actions[g]&&
+fb(a,a.o[e],a.o[e].actions[g]);break}}if(c.parentElement&&c.parentElement.classList.contains("slackmsg-hover")){if(d=b(a,c))d=d.substr(d.lastIndexOf("_")+1),(a=la(E.a[N.id],d))&&bb(c,a);break}c=c.parentElement}}
+function fb(a,b,c){function d(){var d={actions:[c],attachment_id:b.id,callback_id:b.callback_id,channel_id:e,is_ephemeral:a instanceof C,message_ts:a.id},f=new XMLHttpRequest;f.open("POST","api/attachmentAction?serviceId="+a.G);f.send(JSON.stringify(d))}var e=N.id;c.confirm?Ja(Ia(new wa(c.confirm.title,c.confirm.text),c.confirm.ok_text,c.confirm.dismiss_text),d).$():d()}function R(){document.getElementById("msgInput").focus()}
+function Pa(){var a=document.location.hash.substr(1),b=I(a);b&&b!==N?gb(b):(a=J(a))&&a.b&&gb(a.b)}function hb(){var a=document.getElementById("chatWindow").getBoundingClientRect().top;Ka.forEach(function(b){var c=b.U,d=c.clientHeight;b=b.getBoundingClientRect();c.style.top=Math.max(0,Math.min(a-b.top,b.height-d-d/2))+"px"})}
+document.addEventListener("DOMContentLoaded",function(){ua();ib();document.getElementById("chatWindow").addEventListener("click",eb);window.addEventListener("hashchange",function(){document.location.hash&&"#"===document.location.hash[0]&&Pa()});document.getElementById("fileUploadCancel").addEventListener("click",function(a){a.preventDefault();document.getElementById("fileUploadError").classList.add("hidden");document.getElementById("fileUploadContainer").classList.add("hidden");document.getElementById("fileUploadInput").value=
+"";return!1});document.getElementById("fileUploadForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("fileUploadInput");var b=a.value;b&&(b=b.substr(b.lastIndexOf("\\")+1),jb(b,a.files[0],function(a){var b=document.getElementById("fileUploadError");a?(b.textContent=a,b.classList.remove("hidden")):(b.classList.add("hidden"),document.getElementById("fileUploadInput").value="",document.getElementById("fileUploadContainer").classList.add("hidden"))}));return!1});
+document.getElementById("attachFile").addEventListener("click",function(a){a.preventDefault();N&&document.getElementById("fileUploadContainer").classList.remove("hidden");return!1});document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");N&&a.value&&kb(a.value)&&(a.value="",T&&(T=null,U()),V&&(V=null,U()),document.getElementById("slashList").textContent="");R();return!1});window.addEventListener("blur",function(){window.hasFocus=
+!1});window.addEventListener("focus",function(){window.hasFocus=!0;La=0;N&&Xa();R()});document.getElementById("chatWindow").addEventListener("scroll",hb);var a=0;document.getElementById("msgInput").addEventListener("input",function(){if(N){var b=Date.now();a+3E3<b&&(Q.self.a||N instanceof r)&&(lb(),a=b);var b=[],c=this.value;if("/"===this.value[0]){var d=c.indexOf(" "),e=-1!==d,d=-1===d?c.length:d,c=c.substr(0,d);if(e){var g=mb.Fa(c);g&&b.push(g)}else b=mb.Ra(c);var g=Q?Q.i.data:{};for(n in g){var f=
+g[n];(!e&&f.name.substr(0,d)===c||e&&f.name===c)&&b.push(f)}}b.sort(function(a,b){return a.R.localeCompare(b.R)||a.name.localeCompare(b.name)});var n=document.getElementById("slashList");var d=document.createDocumentFragment();n.textContent="";e=0;for(c=b.length;e<c;e++){g=b[e];if(h!==g.R){var h=g.R;d.appendChild(nb(g.R))}d.appendChild(ob(g))}n.appendChild(d)}});window.hasFocus=!0;(function(){var a=document.getElementById("emojiButton");if("makeEmoji"in window){var c=window.makeEmoji("smile");c?a.innerHTML=
+"<span class='emoji-small'>"+c.outerHTML+"</span>":a.style.backgroundImage='url("smile.svg")';(c=window.makeEmoji("paperclip"))?document.getElementById("attachFile").innerHTML="<span class='emoji-small'>"+c.outerHTML+"</span>":document.getElementById("attachFile").style.backgroundImage='url("public/paperclip.svg")';a.addEventListener("click",function(){Q&&cb.$(document.body,Q,function(a){a&&(document.getElementById("msgInput").value+=":"+a+":");R()})})}else a.classList.add("hidden")})();pb()});function qb(a){if(void 0!==a.latitude&&void 0!==a.longitude&&-90<=a.latitude&&90>=a.latitude&&-180<=a.longitude&&180>=a.longitude){var b=0,c=function(a,b,c,d){return new Promise(function(e,f){var g=new Image;g.addEventListener("load",function(){d.N=g;e(d)});g.addEventListener("error",function(){console.warn("Error loading tile ",{zoom:a,x:b,y:c});f(g)});g.crossOrigin="anonymous";g.src="https://c.tile.openstreetmap.org/"+a+"/"+b+"/"+c+".png"})},d=document.createElement("canvas"),e=document.createElement("canvas");
+d.height=d.width=e.height=e.width=300;var g=d.getContext("2d"),f=e.getContext("2d"),n=function(a,b,c){a=a*Math.PI/180;b=b*Math.PI/180;c=c*Math.PI/180;return Math.abs(6371E3*Math.acos(Math.pow(Math.sin(a),2)+Math.pow(Math.cos(a),2)*Math.cos(c-b)))},h=function(a,d,e,h){f.fillStyle="#808080";f.fillRect(0,0,300,300);g.fillStyle="#808080";g.fillRect(0,0,300,300);var k=Math.pow(2,a),l=(e+180)/360*k,m=(1-Math.log(Math.tan(d*Math.PI/180)+1/Math.cos(d*Math.PI/180))/Math.PI)/2*k,F=Math.floor(l),ba=Math.floor(m),
+ca=h?100*h/n(180/Math.PI*Math.atan(.5*(Math.exp(Math.PI-2*Math.PI*ba/k)-Math.exp(-(Math.PI-2*Math.PI*ba/k)))),F/k*360-180,(F+1)/k*360-180):0;d=b;for(e=0;3>e;e++)for(h=0;3>h;h++)c(a,F+e-1,ba+h-1,{Sa:e,Ua:h,Na:d}).then(function(a){if(a.Na===b){f.drawImage(a.N,100*a.Sa,100*a.Ua,100,100);a=l-F;var c=m-ba;a=100*a+100;c=100*c+100;g.putImageData(f.getImageData(0,0,300,300),0,0);void 0!==ca&&(g.beginPath(),g.arc(a,c,Math.max(ca,10),0,2*Math.PI,!1),g.lineWidth=2,g.fillStyle="rgba(244, 146, 66, 0.4)",g.strokeStyle=
+"rgba(244, 146, 66, 0.8)",g.stroke(),g.fill());if(void 0===ca||25<ca)g.strokeStyle="rgba(244, 146, 66, 1)",g.beginPath(),g.moveTo(a-5,c-5),g.lineTo(a+5,c+5),g.stroke(),g.moveTo(a+5,c-5),g.lineTo(a-5,c+5),g.stroke()}})},m,l=function(c){c=Math.max(4,Math.min(19,c));m!==c&&(b++,m=c,h(m,Number(a.latitude),Number(a.longitude),Number(a.accuracy)))};l(12);var e=document.createElement("div"),k=document.createElement("div"),p=document.createElement("button"),t=document.createElement("button");e.className=
+"OSM-wrapper";d.className="OSM-canvas";k.className="OSM-controls";t.className="OSM-controls-zoomMin";p.className="OSM-controls-zoomPlus";t.addEventListener("click",function(){l(m-1)});p.addEventListener("click",function(){l(m+1)});k.appendChild(t);k.appendChild(p);e.appendChild(d);e.appendChild(k);return e}};function Na(){var a=document.createElement("span"),b=document.createElement("span"),c=document.createElement("span"),d=document.createElement("span");a.className="typing-container";b.className="typing-dot1";c.className="typing-dot2";d.className="typing-dot3";b.textContent=c.textContent=d.textContent=".";a.appendChild(b);a.appendChild(c);a.appendChild(d);return a}var Oa=function(){var a={};return function(b){var c=a[b];c||(c=a[b]=document.createElement("header"),c.textContent=b);return c}}();
+function rb(a){var b=a.b,c=document.createElement("div"),d=document.createElement("div"),e=document.createElement("ul"),g=document.createElement("li");c.o=document.createElement("ul");c.B=document.createElement("ul");c.j=document.createElement("div");c.pa=document.createElement("div");c.ha=document.createElement("span");c.id=b+"_"+a.id;c.className="slackmsg-item";c.j.className="slackmsg-ts";c.pa.className="slackmsg-msg";c.ha.className="slackmsg-author-name";e.className="slackmsg-hover";g.className=
+"slackmsg-hover-reply";e.appendChild(g);if("makeEmoji"in window){var f=document.createElement("li"),n=window.makeEmoji("arrow_heading_down"),h=window.makeEmoji("smile"),m=window.makeEmoji("pencil2"),b=window.makeEmoji("x");f.className="slackmsg-hover-reaction";h?(f.classList.add("emoji-small"),f.appendChild(h)):f.style.backgroundImage='url("smile.svg")';n?(g.classList.add("emoji-small"),g.appendChild(n)):g.style.backgroundImage='url("repl.svg")';e.appendChild(f);sa(a.G)&&(a=document.createElement("li"),
+a.className="slackmsg-hover-edit",m?a.classList.add("emoji-small"):a.style.backgroundImage='url("edit.svg")',a.appendChild(m),e.appendChild(a),a=document.createElement("li"),a.className="slackmsg-hover-remove",b?a.classList.add("emoji-small"):a.style.backgroundImage='url("remove.svg")',a.appendChild(b),e.appendChild(a))}else g.style.backgroundImage='url("repl.svg")',sa(a.G)&&(a=document.createElement("li"),a.className="slackmsg-hover-edit",a.style.backgroundImage='url("edit.svg")',e.appendChild(a),
+a=document.createElement("li"),a.className="slackmsg-hover-remove",a.style.backgroundImage='url("remove.svg")',e.appendChild(a));d.appendChild(c.ha);d.appendChild(c.pa);d.appendChild(c.j);d.appendChild(c.o);c.C=document.createElement("div");c.C.className="slackmsg-edited";d.appendChild(c.C);d.appendChild(c.B);d.className="slackmsg-content";c.o.className="slackmsg-attachments";c.B.className="slackmsg-reactions";c.appendChild(d);c.appendChild(e);return c}
+function sb(a){var b={good:"#2fa44f",warning:"#de9e31",danger:"#d50200"};if(a){if("#"===a[0])return a;if(b[a])return b[a]}return"#e3e4e6"}
+function tb(a,b,c){var d=document.createElement("li"),e=document.createElement("div"),g=document.createElement("div"),f=document.createElement("a"),n=document.createElement("div"),h=document.createElement("img"),m=document.createElement("a"),l=document.createElement("div"),k=document.createElement("div"),p=document.createElement("div"),t=document.createElement("img"),v=document.createElement("div");d.className="slackmsg-attachment";e.style.borderColor=sb(b.color||"");e.className="slackmsg-attachment-block";
+g.className="slackmsg-attachment-pretext";b.pretext?g.innerHTML=a.u(b.pretext):g.classList.add("hidden");f.target="_blank";b.title?(f.innerHTML=a.u(b.title),b.title_link&&(f.href=b.title_link),f.className="slackmsg-attachment-title"):f.className="hidden slackmsg-attachment-title";m.target="_blank";n.className="slackmsg-author";b.author_name&&(m.innerHTML=a.u(b.author_name),m.href=b.author_link||"",m.className="slackmsg-author-name",h.className="slackmsg-author-img",b.author_icon&&(h.src=b.author_icon,
+n.appendChild(h)),n.appendChild(m));p.className="slackmsg-attachment-thumb";b.thumb_url?(h=document.createElement("img"),h.src=b.thumb_url,p.appendChild(h),e.classList.add("has-thumb"),b.video_html&&(p.dataset.video=b.video_html)):p.classList.add("hidden");l.className="slackmsg-attachment-content";h=a.u(b.text||"");k.className="slackmsg-attachment-text";h&&""!=h?k.innerHTML=h:k.classList.add("hidden");l.appendChild(p);l.appendChild(k);b.geo&&(k=qb(b.geo))&&l.appendChild(k);t.className="slackmsg-attachment-img";
+b.image_url?t.src=b.image_url:t.classList.add("hidden");v.className="slackmsg-attachment-footer";b.footer&&(k=document.createElement("span"),k.className="slackmsg-attachment-footer-text",k.innerHTML=a.u(b.footer),b.footer_icon&&(p=document.createElement("img"),p.src=b.footer_icon,p.className="slackmsg-attachment-footer-icon",v.appendChild(p)),v.appendChild(k));b.ts&&(k=document.createElement("span"),k.className="slackmsg-ts",k.innerHTML=L.W(b.ts),v.appendChild(k));e.appendChild(f);e.appendChild(n);
+e.appendChild(l);e.appendChild(t);if(b.fields&&b.fields.length){var w=document.createElement("ul");e.appendChild(w);w.className="slackmsg-attachment-fields";b.fields.forEach(function(b){var c=b.title||"",d=b.value||"";b=!!b["short"];var e=document.createElement("li"),g=document.createElement("div"),f=document.createElement("div");e.className="field";b||e.classList.add("field-long");g.className="field-title";g.textContent=c;f.className="field-text";f.innerHTML=a.u(d);e.appendChild(g);e.appendChild(f);
+e&&w.appendChild(e)})}if(b.actions&&b.actions.length)for(f=document.createElement("ul"),f.className="slackmsg-attachment-actions "+Ea,e.appendChild(f),n=0,l=b.actions.length;n<l;n++)(t=b.actions[n])&&(t=ub(c,n,t))&&f.appendChild(t);e.appendChild(v);d.appendChild(g);d.appendChild(e);return d}
+function ub(a,b,c){var d=document.createElement("li"),e=sb(c.style);d.textContent=c.text;e!==sb()&&(d.style.color=e);d.style.borderColor=e;d.dataset.attachmentIndex=a;d.dataset.actionIndex=b;d.className="slackmsg-attachment-actions-item "+Da;return d}function Ta(a){var b=document.createElement("li"),c=document.createElement("span");c.textContent=a.name;b.appendChild(Na());b.appendChild(c);return b}
+function nb(a){var b=document.createElement("lh");b.textContent=a;b.className="slack-command-header";return b}
+function ob(a){var b=document.createElement("li"),c=document.createElement("span"),d=document.createElement("span"),e=document.createElement("span");c.textContent=a.name;d.textContent=a.usage;e.textContent=a.a;b.appendChild(c);b.appendChild(d);b.appendChild(e);b.className="slack-command-item";c.className="slack-command-name";d.className="slack-command-usage";e.className="slack-command-desc";return b};var cb=function(){function a(a,b){for(a=a.target;a!==h&&a&&"LI"!==a.nodeName;)a=a.parentElement;a&&"LI"===a.nodeName&&a.id&&"emojibar-"===a.id.substr(0,9)?b(a.id.substr(9)):b(null)}function b(){if(!c())return!1;G&&G(null);return!0}function c(){return h.parentElement?(h.parentElement.removeChild(m),h.parentElement.removeChild(h),!0):!1}function d(a){var b=0;a=void 0===a?t.value:a;if(n()){var c=0,d=window.searchEmojis(a),f=e(d,S.self.O.ia),h;for(l in v)v[l].visible&&(v[l].visible=!1,k.removeChild(v[l].c));
+var l=0;for(h=f.length;l<h;l++){var m=f[l].name,z=v[m];if(!z){var z=v,F=m;var G=m;var m=window.makeEmoji(d[m]),x=document.createElement("span");x.appendChild(m);x.className="emoji-medium";G=g(G,x);z=z[F]=G}z.visible||(z.visible=!0,k.appendChild(z.c));c++}b+=c}l=b;var c=0;for(B in w)w[B].visible&&(w[B].visible=!1,p.removeChild(w[B].c));d=e(S.a.data,S.self.O.ia);var B=0;for(b=d.length;B<b;B++)F=d[B].name,""!==a&&F.substr(0,a.length)!==a||"alias:"===S.a.data[F].substr(0,6)||(f=w[F],f||(f=w,z=h=F,F=S.a.data[F],
+G=document.createElement("span"),m=document.createElement("span"),G.className="emoji emoji-custom",G.style.backgroundImage='url("'+F+'")',m.appendChild(G),m.className="emoji-medium",z=g(z,m),f=f[h]=z),f.visible||(f.visible=!0,p.appendChild(f.c)),c++);return l+c}function e(a,b){var c=[],d;for(d in a){var e={name:d,Ka:0,count:0};if(a[d].names)for(var f=0,g=a[d].names.length;f<g;f++)e.count+=b[a[d].names[f]]||0;c.push(e)}return c=c.sort(function(a,b){var c=b.count-a.count;return c?c:a.Ka-b.Ka})}function g(a,
+b){var c=document.createElement("li");c.appendChild(b);c.className="emojibar-list-item";c.id="emojibar-"+a;return{visible:!1,c:c}}function f(a){var b=document.createElement("img"),c=document.createElement("div");b.src=a;c.appendChild(b);c.className="emojibar-header";return c}function n(){return"searchEmojis"in window}var h=document.createElement("div"),m=document.createElement("div"),l=document.createElement("div"),k=document.createElement("ul"),p=document.createElement("ul"),t=document.createElement("input"),
+v={},w={},x=document.createElement("div"),B=document.createElement("span"),z=document.createElement("span"),G,S;m.addEventListener("click",function(a){var c=h.getBoundingClientRect();(a.screenY<c.top||a.screenY>c.bottom||a.screenX<c.left||a.screenX>c.right)&&b()});m.className="emojibar-overlay";h.className="emojibar";l.className="emojibar-emojis";x.className="emojibar-detail";B.className="emojibar-detail-img";z.className="emojibar-detail-name";k.className=p.className="emojibar-list";t.className="emojibar-search";
+x.appendChild(B);x.appendChild(z);l.appendChild(f(window.emojiProviderHeader));l.appendChild(k);l.appendChild(f("emojicustom.png"));l.appendChild(p);h.appendChild(l);h.appendChild(x);h.appendChild(t);t.addEventListener("keyup",function(){d()});h.addEventListener("mousemove",function(b){a(b,function(a){var b=a?v[a]||w[a]:null;b?(B.innerHTML=b.c.outerHTML,z.textContent=":"+a+":"):(B.textContent="",z.textContent="")})});h.addEventListener("click",function(b){a(b,function(a){a&&c()&&G&&G(a)})});return{isSupported:n,
+$:function(a,b,c){return n()?(S=b,G=c,a.appendChild(m),a.appendChild(h),t.value="",d(),t.focus(),!0):!1},search:d,close:b}}();var E,O=[];function vb(){fa.call(this)}vb.prototype=Object.create(fa.prototype);vb.prototype.constructor=vb;function oa(a){return a.b?a.b.id:null}function wb(){this.b=0;this.context=new ma;this.a={}}
+wb.prototype.update=function(a){var b=Date.now();a.v&&(this.b=a.v);if(a["static"])for(e in a["static"]){var c=na(this.context,e);c||(c=new vb,this.context.push(c));ga(c,a["static"][e],b)}pa(this.context,function(a){a.I===a.A&&(a=O.indexOf(a),-1!==a&&O.splice(a,1))});if(a.live){for(e in a.live)(c=this.a[e])?ia(c,a.live[e],b):c=this.a[e]=new Y(e,250,a.live[e],b);for(var d in a.live){var e=H(this.context,d);(c=e.l[d])?(this.a[d].a.length&&(c.I=Math.max(c.I,ka(this.a[d]).j)),c.Y||(xb(e,c,a.live[d]),N&&
+a.live[N.id]&&Wa())):E.b=0}}a["static"]&&Ma();var g=!1;a.typing&&this.context.a.forEach(function(c){var d=g,e=a.typing,f=!1;if(c.s)for(var l in c.s)e[l]||(delete c.s[l],f=!0);if(e)for(l in e)if(c.l[l]){c.s[l]||(c.s[l]={});for(var k in e[l])c.s[l][k]||(f=!0),c.s[l][k]=b}g=d|f},this);(a["static"]||g)&&Ra()};
+setInterval(function(){var a=!1,b=Date.now();qa(function(c){var d=!1,e;for(e in c.s){var g=!0,f;for(f in c.s[e])c.s[e][f]+3E3<b?(delete c.s[e][f],d=!0):g=!1;g&&(delete c.s[e],d=!0)}d&&(a=!0)});a&&Ra()},1E3);
+function xb(a,b,c){if(b!==N||!window.hasFocus){var d=new RegExp("<@"+a.self.id),e=!1,g=!1,f=!1;c.forEach(function(c){if(!(parseFloat(c.ts)<=b.A)){g=!0;var h;if(!(h=b instanceof r)&&(h=c.text)&&!(h=c.text.match(d)))a:{h=a.self.O.w;for(var m=0,l=h.length;m<l;m++)if(-1!==c.text.indexOf(h[m])){h=!0;break a}h=!1}h&&(-1===O.indexOf(b)&&(f=!0,O.push(b)),e=!0)}});if(g){P();if(c=document.getElementById("room_"+b.id))c.classList.add("unread"),e&&c.classList.add("unreadHi");f&&!window.hasFocus&&ab()}}}
+function Xa(){var a=N,b=O.indexOf(a);if(a.I>a.A){var c=E.a[a.id];if(c&&(c=c.a[c.a.length-1])){var d=new XMLHttpRequest;d.open("POST","api/markread?room="+a.id+"&id="+c.id+"&ts="+c.j,!0);d.send(null);a.A=c.j}}0<=b&&(O.splice(b,1),P());a=document.getElementById("room_"+a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}E=new wb;var Qa=function(){function a(a,c){c.sort(function(){return Math.random()-.5});for(var d=0,e=20;e<m-40;e+=k)for(var g=0;g+k<=l;g+=k)f(a,c[d],e,g),d++,d===c.length&&(c.sort(b),d=0)}function b(a,b){return a.N?b.N?Math.random()-.5:-1:1}function c(a,b){for(var e=0,g=a.length;e<g;e++)if(void 0===a[e].N){d(a[e].src,function(d){a[e].N=d;c(a,b)});return}var f=[];a.forEach(function(a){a.N&&f.push(a.N)});b(f)}function d(a,b){var c=new XMLHttpRequest;c.responseType="blob";c.onreadystatechange=function(){if(4===
+c.readyState)if(c.response){var a=new Image;a.onload=function(){var c=document.createElement("canvas");c.height=c.width=v;c=c.getContext("2d");c.drawImage(a,0,0,v,v);var c=c.getImageData(0,0,v,v),d=0,e;for(e=0;e<c.width*c.height*4;e+=4)c.data[e]=c.data[e+1]=c.data[e+2]=(c.data[e]+c.data[e+1]+c.data[e+2])/3,c.data[e+3]=50,d+=c.data[e];if(50>d/(c.height*c.width))for(e=0;e<c.width*c.height*4;e+=4)c.data[e]=c.data[e+1]=c.data[e+2]=255-c.data[e];b(c)};a.onerror=function(){b(null)};a.src=window.URL.createObjectURL(c.response)}else b(null)};
+c.open("GET",a,!0);c.send(null)}function e(){var a=h.createLinearGradient(0,0,0,l);a.addColorStop(0,"#4D394B");a.addColorStop(1,"#201820");h.fillStyle=a;h.fillRect(0,0,m,l);return h.getImageData(0,0,m,l)}function g(a,b){for(var c=(a.height-b.height)/2,d=0;d<b.height;d++)for(var e=0;e<b.width;e++){var g=b.data[4*(d*b.width+e)]/255,f=4*((d+c)*a.width+e+c);a.data[f]*=g;a.data[f+1]*=g;a.data[f+2]*=g}return a}function f(a,b,c,d){var e=Math.floor(d);a=[a.data[e*m*4+0],a.data[e*m*4+1],a.data[e*m*4+2]];h.fillStyle=
+"#"+(1.1*a[0]<<16|1.1*a[1]<<8|1.1*a[2]).toString(16);h.beginPath();h.moveTo(c+k/2,d+p);h.lineTo(c-p+k,d+k/2);h.lineTo(c+k/2,d-p+k);h.lineTo(c+p,d+k/2);h.closePath();h.fill();h.putImageData(g(h.getImageData(c+p,d+p,t,t),b),c+p,d+p)}var n=document.createElement("canvas"),h=n.getContext("2d"),m=n.width=250,l=n.height=290,k=(m-40)/3,p=.1*k,t=Math.floor(k-2*p),v=.5*t,w={},x={},B={};return function(b,d,g){if(w[b])g(w[b]);else if(B[b])x[b]?x[b].push(g):x[b]=[g];else{var f=e(),h=[];B[b]=!0;x[b]?x[b].push(g):
+x[b]=[g];for(var k in d)d[k].Da||d[k].Ta||h.push({src:"api/avatar?user="+d[k].id});c(h,function(c){a(f,c);w[b]=n.toDataURL();x[b].forEach(function(a){a(w[b])})})}}}();var X=0,N=null,Q=null,T=null,V=null;function ib(){var a=new XMLHttpRequest;a.timeout=6E4;a.onreadystatechange=function(){if(4===a.readyState){var b=document.createElement("script");b.innerHTML=a.response;b.language="text/javascript";document.head.innerHTML+='<link href="hljs-androidstudio.css" rel="stylesheet"/>';document.body.appendChild(b)}};a.open("GET","highlight.pack.js",!0);a.send(null)}function yb(){var a=new XMLHttpRequest;a.open("GET","api/hist?room="+N.id,!0);a.send(null)}
+function zb(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var c=null,d=2===Math.floor(b.status/100);if(d){X&&(X=0,Ua(!0));c=b.response;try{c=JSON.parse(c)}catch(e){c=null}}else X?(X+=Math.floor((X||5)/2),X=Math.min(60,X)):(X=5,Ua(!1));a(d,c)}else X&&(X=0,Ua(!0)),zb(a)};b.open("GET","api?v="+E.b,!0);b.send(null)}function lb(){var a=new XMLHttpRequest;a.open("POST","api/typing?room="+N.id,!0);a.send(null)}
+function Ab(a,b){a?(b&&E.update(b),pb()):setTimeout(pb,1E3*X)}function pb(){zb(Ab)}function gb(a){N&&document.getElementById("room_"+N.id).classList.remove("selected");document.getElementById("room_"+a.id).classList.add("selected");document.body.classList.remove("no-room-selected");N=a;Q=H(E.context,a.id);Va();Qa(Q.b.id,Q.m,function(a){document.getElementById("slackCtx").style.backgroundImage="url("+a+")"});N.I&&!E.a[N.id]&&yb()}
+function jb(a,b,c){var d=N;new FileReader;var e=new FormData,g=new XMLHttpRequest;e.append("file",b);e.append("filename",a);g.onreadystatechange=function(){4===g.readyState&&(204===g.status?c(null):c(g.statusText))};g.open("POST","api/file?room="+d.id);g.send(e)}
+function Bb(a,b,c){var d=new XMLHttpRequest;b="api/msg?room="+a.id+"&text="+encodeURIComponent(b);c&&(b+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:c.text,author_name:J(c.G).name,text:c.text,footer:a.b?L.message:a.name,ts:c.j}])));d.open("POST",b,!0);d.send(null)}
+function kb(a){if(V){var b=new XMLHttpRequest;b.open("PUT","api/msg?room="+N.id+"&ts="+V.id+"&text="+encodeURIComponent(a),!0);b.send(null);return!0}if("/"===a[0]){var c=a.indexOf(" "),b=a.substr(0,-1===c?void 0:c);a=-1===c?"":a.substr(c);var c=Q,d=mb.Fa(b);return d?(d.exec(c,N,a.trim()),!0):c&&(b=c.i.data[b])?(c=new XMLHttpRequest,c.open("POST","api/cmd?room="+N.id+"&cmd="+encodeURIComponent(b.name.substr(1))+"&args="+encodeURIComponent(a.trim()),!0),c.send(null),!0):!1}Bb(N,a,T);return!0}
+function db(a){var b=new XMLHttpRequest;b.open("DELETE","api/msg?room="+N.id+"&ts="+a.id,!0);b.send(null)}function Ya(a,b,c){var d=new XMLHttpRequest;d.open("POST","api/reaction?room="+a+"&msg="+b+"&reaction="+encodeURIComponent(c),!0);d.send(null)};function Y(a,b,c,d){D.call(this,a,b,c,d)}Y.prototype=Object.create(D.prototype);Y.prototype.constructor=Y;Y.prototype.b=function(a,b){return!0===a.isMeMessage?new Cb(this.id,a,b):!0===a.isNotice?new Db(this.id,a,b):new Eb(this.id,a,b)};
+var Z=function(){function a(a,d){return va(d,{w:a.context.self.O.w,V:function(a){":"===a[0]&&":"===a[a.length-1]&&(a=a.substr(1,a.length-2));if(a=Za(a)){var b=document.createElement("span");b.className="emoji-small";b.appendChild(a);return b.outerHTML}return null},Z:function(c){return b(a,c)}})}function b(a,b){var c=b.indexOf("|");if(-1===c)var d=b;else{d=b.substr(0,c);var f=b.substr(c+1)}if("@"===d[0])if(d=oa(a.context)+"|"+d.substr(1),f=J(d))a=!0,d="#"+f.na.id,f="@"+f.name;else return null;else if("#"===
+d[0])if(d=oa(a.context)+"|"+d.substr(1),f=I(d))a=!0,d="#"+d,f="#"+f.name;else return null;else{if(!d.match(/^(https?|mailto):\/\//i))return null;a=!1}return{link:d,text:f||d,Ga:a}}return{F:function(a){a.P=!0;return a},S:function(a){a.c&&a.c.parentElement&&(a.c.remove(),delete a.c);return a},M:function(a){a.c?a.P&&(a.P=!1,a.K()):a.ga().K();return a.c},K:function(b){var c=J(b.G);b.c.j.innerHTML=L.W(b.j);b.c.pa.innerHTML=a(b,b.text);b.c.ha.textContent=c?c.name:b.username||"?";for(var c=document.createDocumentFragment(),
+e=0,g=b.o.length;e<g;e++){var f=b.o[e];f&&(f=tb(b,f,e))&&c.appendChild(f)}b.c.o.textContent="";b.c.o.appendChild(c);c=b.b;e=document.createDocumentFragment();if(b.B)for(var n in b.B){var g=c,f=b.id,h=n,m=b.B[n],l=Za(h);if(l){for(var k=document.createElement("li"),p=document.createElement("a"),t=document.createElement("span"),v=document.createElement("span"),w=[],x=0,B=m.length;x<B;x++){var z=J(m[x]);z&&w.push(z.name)}w.sort();v.textContent=w.join(", ");t.appendChild(l);t.className="emoji-small";p.href=
+"javascript:toggleReaction('"+g+"', '"+f+"', '"+h+"')";p.appendChild(t);p.appendChild(v);k.className="slackmsg-reaction-item";k.appendChild(p);g=k}else console.warn("Reaction id not found: "+h),g=null;g&&e.appendChild(g)}b.c.B.textContent="";b.c.B.appendChild(e);b.C&&(b.c.C.innerHTML=L.C(b.C),b.c.classList.add("edited"));return b},L:function(a){return a.c.cloneNode(!0)},u:function(b,d){return a(b,d)}}}();
+function Cb(a,b,c){y.call(this,b,c);this.context=H(E.context,a);this.b=a;this.c=Z.c;this.P=Z.P}Cb.prototype=Object.create(A.prototype);q=Cb.prototype;q.constructor=Cb;q.F=function(){return Z.F(this)};q.u=function(a){return Z.u(this,a)};q.S=function(){return Z.S(this)};q.M=function(){return Z.M(this)};q.ga=function(){this.c=rb(this);this.c.classList.add("slackmsg-me_message");return this};q.L=function(){return Z.L(this)};q.K=function(){Z.K(this);return this};
+q.update=function(a,b){A.prototype.update.call(this,a,b);this.F()};function Eb(a,b,c){y.call(this,b,c);this.context=H(E.context,a);this.b=a;this.c=Z.c;this.P=Z.P}Eb.prototype=Object.create(y.prototype);q=Eb.prototype;q.constructor=Eb;q.F=function(){return Z.F(this)};q.u=function(a){return Z.u(this,a)};q.S=function(){return Z.S(this)};q.M=function(){return Z.M(this)};q.ga=function(){this.c=rb(this);return this};q.L=function(){return Z.L(this)};q.K=function(){Z.K(this);return this};
+q.update=function(a,b){y.prototype.update.call(this,a,b);this.F();if(a=this.text.match(/^<?https:\/\/www\.openstreetmap\.org\/\?mlat=(-?[0-9\.]+)(&amp;|&)mlon=(-?[0-9\.]+)(&amp;|&)macc=([0-9\.]+)[^\s]*/))this.text=this.text.substr(0,a.index)+this.text.substr(a.index+a[0].length).trim(),this.o.unshift({color:"#008000",text:a[0],footer:"Open Street Map",footer_icon:"https://www.openstreetmap.org/assets/favicon-32x32-36d06d8a01933075bc7093c9631cffd02d49b03b659f767340f256bb6839d990.png",geo:{latitude:a[1],
+longitude:a[3],accuracy:a[5]}})};function Db(a,b,c){y.call(this,b,c);this.context=H(E.context,a);this.b=a;this.a=null;this.P=!0}Db.prototype=Object.create(C.prototype);q=Db.prototype;q.constructor=Db;q.F=function(){return Z.F(this)};q.u=function(a){return Z.u(this,a)};q.S=function(){this.a&&this.a.parentElement&&(this.a.remove(),delete this.a);this.c&&delete this.c;return this};q.M=function(){Z.M(this);return this.a};q.L=function(){return this.a.cloneNode(!0)};
+q.ga=function(){this.c=rb(this);this.a=document.createElement("span");this.c.classList.add("slackmsg-notice");this.a.className="slackmsg-notice";this.a.textContent=L.Ja;this.a.appendChild(this.c);return this};q.K=function(){Z.K(this);return this};q.update=function(a,b){C.prototype.update.call(this,a,b);this.F()};var mb=function(){var a=[];return{Fa:function(b){for(var c=0,d=a.length;c<d;c++)if(-1!==a[c].names.indexOf(b))return a[c];return null},Ra:function(b){var c=[];a.forEach(function(a){for(var d=0,g=a.names.length;d<g;d++)if(a.names[d].substr(0,b.length)===b){c.push(a);break}});return c},Wa:function(b){b.R="client";b.exec=b.exec.bind(b);a.push(b)}}}();function Fb(){return new Promise(function(a,b){"geolocation"in window.navigator?navigator.geolocation.getCurrentPosition(function(c){c?a(c):b("denied")}):b("geolocation not available")})}
+ta.push(function(){mb.Wa({name:"/sherlock",names:["/sherlock","/sharelock"],usage:"",description:L.Ma,exec:function(a,b){Fb().then(function(a){var c=a.coords.latitude,e=a.coords.longitude;Bb(b,"https://www.openstreetmap.org/?mlat="+c+"&mlon="+e+"&macc="+a.coords.accuracy+"#map=17/"+c+"/"+e)}).catch(function(a){console.error("Error: ",a)})}})});
+})();

+ 19 - 8
srv/public/style.css

@@ -71,9 +71,12 @@ body { display: flex; margin: 0; padding: 0; font-family: Lato, sans-serif; heig
 .slackmsg-author-name { display: inline; font-weight: bold; }
 .slackmsg-msg { display: block; vertical-align: top; }
 .slackmsg-reactions:empty,.slackmsg-attachments:empty { display: none; }
-.slackmsg-reactions { padding: 0 0 0 40px; margin: 5px 0; }
+.slackmsg-reactions { padding: 0 0 0 24px; margin: 5px 0; list-style: none; }
 .slackmsg-attachments { padding: 0; }
 .slackmsg-content > .slackmsg-edited, .slackmsg-ts, .slackmsg-attachment-footer * { font-size: 0.75em; font-style: italic; }
+.slackmsg-item .slackmsg-edited { display: none; }
+.slackmsg-item.edited .slackmsg-edited { display: block; }
+.slackmsg-attachment-footer-icon { max-height: 1em; max-width: 1em; vertical-align: middle; }
 
 .slackmsg-notice { background: rgba(255, 255, 0, 0.2); }
 .slackmsg-content .slackmsg-notice { display: inline; font-style: italic; margin-right: 15px; }
@@ -102,11 +105,14 @@ body { display: flex; margin: 0; padding: 0; font-family: Lato, sans-serif; heig
 .slackmsg-item.slackmsg-me_message .slackmsg-author-img { display: none; }
 .slackmsg-item.slackmsg-me_message .slackmsg-ts { display: block; }
 
-.slackmsg-style-bold { font-weight: bold; }
-.slackmsg-style-italic { font-style: italic; }
-.slackmsg-style-strike { text-decoration: line-through; }
-.slackmsg-style-code { background: #FFF6B4; color: red; font-weight: normal; font-style: normal; text-decoration: none; }
-.slackmsg-style-quote { border-left: 3px solid #e3e4e6; padding-left: 10px; }
+.slackmsg-item .bold { font-weight: bold; }
+.slackmsg-item .italic { font-style: italic; }
+.slackmsg-item .strike { text-decoration: line-through; }
+.slackmsg-item .quote { border-left: 3px solid #e3e4e6; padding-left: 10px; }
+.slackmsg-item .code, .slackmsg-item .codeblock { background: #005; color: white; border-radius: 3px; font-weight: normal; font-style: normal; font-family: 'Space mono', 'monospace'; text-decoration: none; padding: 0 5px; }
+.slackmsg-item .code { padding: 0 5px; }
+.slackmsg-item .codeblock { display: block; padding: 5px; }
+.slackmsg-item .highlight { background: #005; color: white; border-radius: 3px; }
 
 .slackmsg-attachments { list-style: none; }
 .slackmsg-attachment .slackmsg-attachment-block .slackmsg-author * { display: inline-block; }
@@ -115,8 +121,7 @@ body { display: flex; margin: 0; padding: 0; font-family: Lato, sans-serif; heig
 .slackmsg-attachment-block { border-left: 3px solid; padding-left: 10px; }
 .slackmsg-attachment-block.has-thumb .slackmsg-attachment-content { display: flex; }
 .slackmsg-attachment-block.has-thumb .slackmsg-attachment-text { flex: 1; margin-left: 14px; }
-.slackmsg-attachment-block.has-thumb .slackmsg-attachment-thumb { display: inline-block; width: 142px; height: 100px; }
-.slackmsg-attachment-block.has-thumb .slackmsg-attachment-thumb img { max-width: 100%; max-height: 100%; }
+.slackmsg-attachment-block.has-thumb .slackmsg-attachment-thumb img { max-width: 150px; max-height: 150px; }
 .slackmsg-attachment-footer > * + *::before { padding: 0 2px 0 5px; font-style: normal; content: "|"; }
 
 .slackmsg-attachment-fields { max-width: 100%; padding: 0; margin: 0; list-style: none; }
@@ -176,3 +181,9 @@ body { display: flex; margin: 0; padding: 0; font-family: Lato, sans-serif; heig
 .typing-container .typing-dot2 { animation-delay: 0.5s; }
 .typing-container .typing-dot3 { animation-delay: 1s; }
 
+.OSM-wrapper { position: relative; }
+.OSM-wrapper .OSM-controls { position: absolute; top: 10px; left: 5px; }
+.OSM-wrapper .OSM-controls button { display: inline-block; height: 25px; width: 25px; padding: 0; margin: 0 5px; }
+.OSM-wrapper .OSM-controls-zoomMin { }
+.OSM-wrapper .OSM-controls-zoomPlus { }
+

+ 3 - 1
srv/src/accounts.js

@@ -43,7 +43,7 @@ function Account(dbResult) {
     this.cguReadAndAccepted;
     this.dirty;
 
-
+    //TODO permanent login token array
     if (dbResult) {
         this.id = dbResult.id;
 
@@ -74,6 +74,7 @@ Account.prototype.toDb = function() {
         ,authSlackUserEmailAndTeam: this.authSlackUserEmailAndTeam
         ,certificates: this.certificates
         ,cguReadAndAccepted: this.cguReadAndAccepted ? 1 : 0
+        //TODO permanent login token array
     };
 };
 
@@ -119,5 +120,6 @@ module.exports.createTable = function(dbObject, cb) {
         +"`certificates` STRING,"
         +"`cguReadAndAccepted` BOOLEAN NOT NULL DEFAULT FALSE"
         +')', cb);
+    //TODO permanent login token array
 };
 

+ 9 - 10
srv/src/chatter.js

@@ -14,11 +14,6 @@ function Chatter(id) {
     this.realName;
     /** @type {boolean} */
     this.presence;
-    /** @type {Object.<string, string>} */
-    this.icons = {
-        small: ""
-        ,large: ""
-    };
     /** @type {string} */
     this.email;
     /** @type {string} */
@@ -43,15 +38,13 @@ Chatter.prototype.toStatic = function(t) {
         ,"name": this.name
         ,"deleted": this.deleted
         ,"status": this.status
-        ,"real_name": this.realName
+        ,"real_name": this.realName // FIXME move to profile (or remove profile)
         ,"isPresent": this.presence
         ,"isBot": this.isBot
         ,"profile": {
             "email": this.email
             ,"first_name": this.firstName
             ,"last_name": this.lastName
-            ,"icon_small": this.icons.small
-            ,"icon_large": this.icons.large
         }
     };
 };
@@ -74,8 +67,6 @@ Chatter.prototype.update = function(userData, t) {
         this.presence = userData["isPresent"];
     if (userData["isBot"]) this.isBot = userData["isBot"];
     if (userData["profile"]) {
-        this.icons.small = userData["profile"]["icon_small"];
-        this.icons.large = userData["profile"]["icon_large"];
         this.email = userData["profile"]["email"];
         this.firstName = userData["profile"]["first_name"];
         this.lastName = userData["profile"]["last_name"];
@@ -83,6 +74,14 @@ Chatter.prototype.update = function(userData, t) {
     this.version = Math.max(this.version, t);
 };
 
+Chatter.prototype.getSmallIcon = function() {
+    return "api/avatar?user=" +this.id;
+};
+
+Chatter.prototype.getLargeIcon = function() {
+    return "api/avatar?size=l&user=" +this.id;
+};
+
 /** @suppress {undefinedVars,checkTypes} */
 (function() {
     if (typeof module !== "undefined") {

+ 67 - 33
srv/src/context.js

@@ -63,13 +63,17 @@ function SelfPreferences() {
 
     /** @type {Array.<string>} */
     this.highlights = [];
+
+    /** @type {number} */
+    this.version = 0;
 };
 
 /**
  * @param {*} prefs
 **/
 SelfPreferences.prototype.update = function(prefs, t) {
-    this.favoriteEmojis = /** @type {Object<string,number>} */ (JSON.parse(prefs["emoji_use"]));
+    if (prefs["emoji_use"])
+        this.favoriteEmojis = /** @type {Object<string,number>} */ (JSON.parse(prefs["emoji_use"]));
     if (prefs["highlight_words"])
         this.highlights = (prefs["highlight_words"]||"").split(',').filter(function(i) {
             return i.trim() !== '';
@@ -80,7 +84,7 @@ SelfPreferences.prototype.update = function(prefs, t) {
 };
 
 SelfPreferences.prototype.toStatic = function(t) {
-    return this.version > t ? null : {
+    return this.version <= t ? undefined : {
         "emoji_use": JSON.stringify(this.favoriteEmojis)
         ,"highlights": this.highlights
     };
@@ -92,20 +96,28 @@ SelfPreferences.prototype.toStatic = function(t) {
 function ChatContext() {
     /** @type {ChatInfo} */
     this.team = null;
+
     /** @type {Object.<string, Room>} */
     this.channels = {};
+
     /** @type {Object.<string, Chatter>} */
     this.users = {};
+
     /** @type {Chatter} */
     this.self = null;
+
     /** @type {{version: number, data: Object.<string, string>}} */
     this.emojis = { version: 0, data: {} };
+
     /** @type {{version: number, data: Object.<string, Command>}} */
     this.commands = {version: 0, data: {} };
+
     /** @type {Object.<string, Object.<string, number>>} */
     this.typing = {};
+
     /** @type {number} */
     this.staticV = 0;
+
     /** @type {number} */
     this.liveV = 0;
 };
@@ -131,19 +143,25 @@ ChatContext.prototype.commandFactory = function(data) {
 /**
  * @param {*} data
  * @param {number} t
+ * @param {string=} idPrefix
 **/
-ChatContext.prototype.updateStatic = function(data, t) {
+ChatContext.prototype.updateStatic = function(data, t, idPrefix) {
+    idPrefix = idPrefix || "";
+    if (data["team"]) {
+        if (!this.team) this.team = this.teamFactory(data["team"]["id"]);
+        this.team.update(data["team"], t);
+    }
     if (data["users"]) for (var i =0, nbUsers = data["users"].length; i < nbUsers; i++) {
-        var userObj = this.users[data["users"][i]["id"]];
+        var userObj = this.users[idPrefix +data["users"][i]["id"]];
         if (!userObj)
-            userObj = this.users[data["users"][i]["id"]] = this.userFactory(data["users"][i]);
+            userObj = this.users[idPrefix +data["users"][i]["id"]] = this.userFactory(data["users"][i]);
         userObj.update(data["users"][i], t);
     }
     if (data["channels"]) for (var i =0, nbChan = data["channels"].length; i < nbChan; i++) {
-        var chanObj = this.channels[data["channels"][i]["id"]];
+        var chanObj = this.channels[idPrefix +data["channels"][i]["id"]];
         if (!chanObj)
-            chanObj = this.channels[data["channels"][i]["id"]] = this.roomFactory(data["channels"][i]);
-        chanObj.update(data["channels"][i], this, t);
+            chanObj = this.channels[idPrefix +data["channels"][i]["id"]] = this.roomFactory(data["channels"][i]);
+        chanObj.update(data["channels"][i], this, t, idPrefix);
     }
     if (data["emojis"]) {
         this.emojis.data = data["emojis"];
@@ -156,43 +174,53 @@ ChatContext.prototype.updateStatic = function(data, t) {
         }
         this.commands.version = t;
     }
-    if (data["team"]) {
-        if (!this.team) this.team = this.teamFactory(data["team"]["id"]);
-        this.team.update(data["team"], t);
-    }
-    this.staticV = Math.max(this.staticV, t);
     if (data["self"]) {
-        this.self = this.users[data["self"]["id"]] || null;
-        if (this.self) {
+        this.self = this.users[idPrefix +data["self"]["id"]] || null;
+        if (data["self"]["prefs"]) {
             if (!this.self.prefs)
                 this.self.prefs = new SelfPreferences();
             this.self.prefs.update(data["self"]["prefs"], t);
-        } else {
-            this.staticV = 0;
         }
     }
-    if (data["typing"] !== undefined) {
-        this.typing = {};
-        for (var i in data["typing"]) {
-            this.typing[i] = {};
-            for (var j in data["typing"][i])
-                this.typing[i][j] = t;
+    this.staticV = Math.max(this.staticV, t);
+};
+
+ChatContext.prototype.updateTyping = function(typing, now) {
+    var updated = false;
+
+    if (this.typing)
+        for (var i in this.typing)
+            if (!typing[i]) {
+                delete (this.typing[i]);
+                updated = true;
+            }
+    if (typing) {
+        for (var i in typing) {
+            if (this.channels[i]) {
+                if (!this.typing[i])
+                    this.typing[i] = {};
+                for (var j in typing[i]) {
+                    if (!this.typing[i][j])
+                        updated = true;
+                    this.typing[i][j] = now;
+                }
+            }
         }
     }
+    return updated;
 };
 
 /**
  * @param {number} t
- * @param {number} now
 **/
-ChatContext.prototype.toStatic = function(t, now) {
+ChatContext.prototype.toStatic = function(t) {
     var channels = []
         ,users = [];
     var res = {
         "team": this.team.toStatic(t)
         ,"self": {
             "id": this.self.id
-            ,"prefs": this.self.prefs.toStatic(t)
+            ,"prefs": this.self.prefs ? this.self.prefs.toStatic(t) : undefined
         }
         ,"emojis": this.emojis.version > t ? this.emojis.data : undefined
     };
@@ -216,23 +244,29 @@ ChatContext.prototype.toStatic = function(t, now) {
     }
     if (users.length)
         res["users"] = users;
+    return res;
+};
+
+ChatContext.prototype.getWhoIsTyping = function(now) {
+    var res;
     for (var typingChan in this.typing) {
         var tChan = null;
         for (var typingUser in this.typing[typingChan]) {
-            if (this.typing[typingChan][typingUser] +3000 >= now) {
-                if (!tChan) tChan = {};
+            if (this.typing[typingChan][typingUser] >= now) {
+                if (!tChan)
+                    tChan = {};
                 tChan[typingUser] = 1;
             } else {
                 delete this.typing[typingChan][typingUser];
             }
         }
         if (tChan) {
-            if (res["typing"] === undefined)
-                res["typing"] = {};
-            res["typing"][typingChan] = tChan;
-        }
-        else
+            if (!res)
+                res = {};
+            res[typingChan] = tChan;
+        } else {
             delete this.typing[typingChan];
+        }
     }
     return res;
 };

+ 122 - 71
srv/src/httpServ.js

@@ -1,10 +1,12 @@
-var http = require("http")
+const http = require("http")
     ,https = require("https")
     ,clientSession = require("client-sessions")
 
     ,Url = require("./url.js").Url
     ,config = require("../config.js")
     ,sessionManager = require("./session.js").SessionManager
+    ,MultiChatManager = require("./multichatManager.js").MultiChatManager
+    ,Slack = require("./slack.js").Slack
     ,slackManager = require("./slackManager.js").SlackManager
     ,FaviconWriter = require("./faviconWriter.js").FaviconWriter;
 
@@ -71,6 +73,26 @@ Server.prototype.execTemplate = function(template, req, res) {
     // else something is running asynchronously, let template close request later..
 };
 
+function recursiveGet(url, cb, redirectLoop) {
+    var getFnc = http.get;
+
+    if (url.substr(0, 8) === "https://")
+        getFnc = https.get;
+    getFnc(url, (d) => {
+        if (d.statusCode >= 300 && d.statusCode < 400 && d.headers["location"]) {
+            if (!redirectLoop)
+                redirectLoop = [];
+
+            if (redirectLoop.indexOf(d.headers["location"]) === -1 && redirectLoop.length < 5) {
+                redirectLoop.push(d.headers["location"]);
+                recursiveGet(d.headers["location"], cb, redirectLoop);
+                return;
+            }
+        }
+        cb(d);
+    });
+};
+
 Server.prototype.onRequest = function(req, res) {
     req.reqT = Date.now();
     req.cookies = Server.parseCookies(req);
@@ -108,22 +130,20 @@ Server.prototype.onRequest = function(req, res) {
         // Api / dynamic content
         var apiSuccess = false;
 
-        res.slack = slackManager.lazyGet(req.session, req.reqT);
+        res.chatContext = new MultiChatManager();
+        res.chatContext.push(slackManager.lazyGet(req.session, req.reqT));
+
         if (req.urlObj.match(["api", "hist"])) {
             if (!req.urlObj.queryTokens.room) {
                 res.writeHeader("400", "Bad request");
             } else {
-                var allFound = true;
-                req.urlObj.queryTokens.room.forEach(function(targetId) {
-                    if (!res.slack.data.channels[targetId]) {
-                        allFound = false;
-                    }
-                    res.slack.fetchHistory(targetId);
-                });
-                if (allFound)
+                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
+                if (ctx) {
+                    ctx.fetchHistory(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
                     res.writeHeader("204", "No Content");
-                else
+                } else {
                     res.writeHeader("404", "Channel not found");
+                }
             }
             sessionManager.saveSession(req.session);
             res.end();
@@ -132,12 +152,12 @@ Server.prototype.onRequest = function(req, res) {
                 res.writeHeader("400", "Bad request");
                 res.end();
             } else {
-                var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]];
+                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
 
-                if (!chan) {
+                if (!ctx) {
                     res.writeHeader("404", "Chan not found");
                 } else {
-                    res.slack.sendTyping(chan);
+                    ctx.sendTyping(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
                     res.writeHeader("204", "No Content");
                 }
                 res.end();
@@ -148,10 +168,10 @@ Server.prototype.onRequest = function(req, res) {
                 res.writeHeader("400", "Bad request");
                 res.end();
             } else {
-                var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]]
-                    ,cmd = res.slack.data.commands.data['/' +req.urlObj.queryTokens.cmd[0]];
+                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0])
+                    ,cmd = ctx.getChatContext().commands.data['/' +req.urlObj.queryTokens.cmd[0]];
 
-                if (!chan) {
+                if (!ctx) {
                     res.writeHeader("404", "Chan not found");
                 } else if (!cmd) {
                     res.writeHeader("404", "No such command");
@@ -159,24 +179,26 @@ Server.prototype.onRequest = function(req, res) {
                     var args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : "";
                     if (args === true)
                         args = "";
-                    res.slack.sendCommand(chan, cmd, args);
+                    ctx.sendCommand(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], cmd, args);
                     res.writeHeader("204", "No Content");
                 }
                 res.end();
             }
         } else if (req.urlObj.match(["api", "markread"])) {
-            if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
+            if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.id || !req.urlObj.queryTokens.ts) {
                 res.writeHeader("400", "Bad request");
                 res.end();
             } else {
-                var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]]
-                    ,ts = parseFloat(req.urlObj.queryTokens.ts[0]);
-                if (!chan)
+                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0])
+                    ,id = req.urlObj.queryTokens.id[0]
+                    ,ts = req.urlObj.queryTokens.ts[0];
+
+                if (!ctx) {
                     res.writeHeader("404", "Chan Not Found");
-                else if (isNaN(ts))
-                    res.writeHeader("400", "Invalid date");
-                else
-                    res.slack.markRead(chan, ts);
+                } else {
+                    ctx.markRead(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], id, ts);
+                    res.writeHeader("204", "No Content");
+                }
                 res.end();
             }
             sessionManager.saveSession(req.session);
@@ -185,20 +207,19 @@ Server.prototype.onRequest = function(req, res) {
                 res.writeHeader("400", "Bad request");
                 res.end();
             } else {
-                var user = res.slack.data.users[req.urlObj.queryTokens.user[0]];
+                var user = res.chatContext.getUser(req.urlObj.queryTokens.user[0]);
                 if (!user) {
                     res.writeHeader("404", "User Not Found");
                     res.end();
                 } else {
-                    var url = user.icons.small;
-                    if (url.substr(0, 7) === "http://")
-                        http.get(url, (d) => {
-                            d.pipe(res, { end: true });
-                        });
-                    else if (url.substr(0, 8) === "https://")
-                        https.get(url, (d) => {
-                            d.pipe(res, { end: true });
-                        });
+                    var url = req.urlObj.queryTokens.size && req.urlObj.queryTokens.size[0] === 'l'
+                            ? user.getLargeIcon()
+                            : user.getSmallIcon();
+                    if (!config.isDebug)
+                        res.setHeader('Cache-Control', 'private, max-age=' +60 * 60); // 1 hour cache for avatars
+                    recursiveGet(url, (d) => {
+                        d.pipe(res, { end: true });
+                    });
                 }
             }
             sessionManager.saveSession(req.session);
@@ -207,17 +228,18 @@ Server.prototype.onRequest = function(req, res) {
                 if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.text) {
                     res.writeHeader("400", "Bad request");
                 } else {
-                    var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]];
-                    if (chan) {
+                    var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
+
+                    if (ctx) {
                         var attachments = null;
                         if (req.urlObj.queryTokens.attachments) {
                             try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); }
                             catch (e) {}
                         }
                         if (req.urlObj.queryTokens.me)
-                            res.slack.sendMeMsg(chan, req.urlObj.queryTokens.text);
+                            ctx.sendMeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text);
                         else
-                            res.slack.sendMsg(chan, req.urlObj.queryTokens.text, attachments);
+                            ctx.sendMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text, attachments);
                         res.writeHeader("204", "No Content");
                     } else {
                         res.writeHeader("404", "Channel not found");
@@ -227,9 +249,9 @@ Server.prototype.onRequest = function(req, res) {
                 if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
                     res.writeHeader("400", "Bad request");
                 } else {
-                    var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]];
-                    if (chan) {
-                        res.slack.removeMsg(chan, req.urlObj.queryTokens.ts[0]);
+                    var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
+                    if (ctx) {
+                        ctx.removeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0]);
                         res.writeHeader("204", "No Content");
                     } else {
                         res.writeHeader("404", "Channel not found");
@@ -239,9 +261,9 @@ Server.prototype.onRequest = function(req, res) {
                 if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts || !req.urlObj.queryTokens.text) {
                     res.writeHeader("400", "Bad request");
                 } else {
-                    var chan = res.slack.data.channels[req.urlObj.queryTokens.room[0]];
-                    if (chan) {
-                        res.slack.editMsg(chan, req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text);
+                    var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
+                    if (ctx) {
+                        ctx.editMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text);
                         res.writeHeader("204", "No Content");
                     } else {
                         res.writeHeader("404", "Channel not found");
@@ -258,15 +280,15 @@ Server.prototype.onRequest = function(req, res) {
                 ,reaction = req.urlObj.queryTokens["reaction"] ? req.urlObj.queryTokens["reaction"][0] : undefined;
 
             if (chanId && msgId && reaction) {
-                var chan = res.slack.data.channels[chanId];
-                if (!chan) {
+                var ctx = res.chatContext.getChannelContext(chanId);
+                if (!ctx) {
                     res.writeHeader("404", "Channel Not Found");
                 } else if (req.method === 'POST') {
                     res.writeHeader("204", "No Content");
-                    res.slack.addReaction(chan, msgId, reaction);
+                    ctx.addReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
                 } else if (req.method === 'DELETE') {
                     res.writeHeader("204", "No Content");
-                    res.slack.removeReaction(chan, msgId, reaction);
+                    ctx.removeReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
                 } else {
                     res.writeHeader("405", "Method not allowed");
                 }
@@ -277,10 +299,10 @@ Server.prototype.onRequest = function(req, res) {
             res.end();
         } else if (req.urlObj.match(["api", "file"])) {
             sessionManager.saveSession(req.session);
-            if (req.urlObj.queryTokens["room"]) {
-                var chan = res.slack.data.channels[req.urlObj.queryTokens["room"][0]];
-                if (chan) {
-                    var uploadRequest = res.slack.openUploadFileStream(chan, req.headers["content-type"], (errorMsg) => {
+            if (req.urlObj.queryTokens.room) {
+                var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
+                if (ctx) {
+                    var uploadRequest = ctx.openUploadFileStream(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.headers["content-type"], (errorMsg) => {
                         if (!errorMsg)
                             res.writeHeader("204", "No Content");
                         else
@@ -300,27 +322,56 @@ Server.prototype.onRequest = function(req, res) {
                 res.end();
             }
         } else if (req.urlObj.match(["api", "attachmentAction"])) {
+            if (!req.urlObj.queryTokens.serviceId) {
+                res.writeHeader("400", "Bad Request");
+                res.end();
+            } else {
+                var serviceId = req.urlObj.queryTokens.serviceId[0]
+                    ,ctx = res.chatContext.getUserContext(serviceId);
+
+                if (ctx) {
+                    var body = [];
+                    req.on('data', (chunk) => { body.push(chunk); });
+                    req.once('end', ()=> {
+                        var payload;
+
+                        try {
+                            payload = JSON.parse(Buffer.concat(body).toString());
+                        } catch(e) {
+                            res.writeHeader("400", "Bad Request");
+                            res.end();
+                            return;
+                        }
+                        if (!ctx.sendAction(serviceId, payload, (result) => {
+                                if (result !== false) {
+                                    res.writeHeader(200);
+                                    res.end(JSON.stringify(result));
+                                } else {
+                                    res.writeHeader(503, "Service unavailable");
+                                    res.end();
+                                }
+                            })) {
+                            // Error re-interpreting payload
+                            res.writeHeader("400", "Bad Request");
+                            res.end();
+                        }
+                    });
+                } else {
+                    res.writeHeader("404", "Service not found");
+                    res.end();
+                }
+            }
             sessionManager.saveSession(req.session);
-            res.slack.sendAction(req.headers["content-type"], req, (result) => {
-                res.end(result);
-            });
         } else if (req.urlObj.match(["api"])) {
-            res.slack.onRequest(
-                (req.urlObj.queryTokens.v ? req.urlObj.queryTokens.v[0] : 0) || 0
-                , (slack, newData) => {
+            res.chatContext.poll(
+                (req.urlObj.queryTokens.v ? parseInt(req.urlObj.queryTokens.v[0], 10) : 0) || 0
+                , (newData) => {
                 if (!res.ended) {
                     try {
-                        if (!slack.connected) {
-                            res.writeHeader("403", {
-                                "Content-Type": "application/json"
-                            });
-                            res.end(slack.error);
-                        } else {
-                            res.writeHeader("200", {
-                                "Content-Type": "application/json"
-                            });
-                            res.end(JSON.stringify(newData));
-                        }
+                        res.writeHeader("200", {
+                            "Content-Type": "application/json"
+                        });
+                        res.end(JSON.stringify(newData));
                     } catch (e) {}
                 }
                 sessionManager.saveSession(req.session);

+ 12 - 5
srv/src/message.js

@@ -29,7 +29,7 @@ function Message(e, ts) {
     /** @type {boolean} */
     this.pinned = false;
 
-    /** @type {boolean} */
+    /** @type {number|boolean} */
     this.edited = false;
 
     /** @type {boolean} */
@@ -63,13 +63,17 @@ function NoticeMessage(e, ts) {
     Message.call(this, e, ts);
 };
 
+/**
+ * @param {*} e
+ * @param {number} ts
+**/
 Message.prototype.update = function(e, ts) {
     if (e) {
         this.text = e["text"] || "";
         if (e["attachments"]) this.attachments = e["attachments"];
         this.starred = !!e["is_starred"];
         this.pinned = false; // TODO
-        this.edited = !!e["edited"];
+        this.edited = e["edited"] === undefined ? false : e["edited"];
         this.removed = !!e["removed"];
 
         if (e["reactions"]) {
@@ -105,7 +109,7 @@ Message.prototype.toStatic = function() {
         ,"text": this.text
         ,"attachments": this.attachments.length ? this.attachments : undefined
         ,"is_starred": this.is_starred || undefined
-        ,"edited": this.edited || undefined
+        ,"edited": this.edited === false ? undefined : this.edited
         ,"removed": this.removed || undefined
         ,"reactions": reactions
         ,"isMeMessage": this instanceof MeMessage || undefined
@@ -290,16 +294,19 @@ RoomHistory.prototype.getMessageById = function(id) {
     return null;
 };
 
+RoomHistory.prototype.last = function() {
+    return this.messages[this.messages.length -1];
+};
+
 /**
  * @return {Array.<*>}
 **/
 RoomHistory.prototype.toStatic = function(knownVersion) {
     var result = [];
 
-    for (var i = this.messages.length -1; i >= 0; i--) {
+    for (var i = this.messages.length -1; i >= 0; i--)
         if (this.messages[i].version > knownVersion)
             result.push(this.messages[i].toStatic());
-    }
     return result;
 };
 

+ 293 - 0
srv/src/multichatManager.js

@@ -0,0 +1,293 @@
+
+/** @interface */
+function ChatSystem() {
+};
+
+/** */
+ChatSystem.prototype.onRequest = function() {};
+
+/**
+ * @return {string|null}
+**/
+ChatSystem.prototype.getId = function() {};
+
+/**
+ * @return {ChatContext}
+**/
+ChatSystem.prototype.getChatContext = function() {};
+
+/**
+ * Async fetch history
+ * @param {Room} chan
+ * @param {string} text
+**/
+ChatSystem.prototype.sendMeMsg = function(chan, text) {};
+
+/**
+ * Async fetch history
+ * @param {Room} chan
+ * @param {string} text
+ * @param {Object=} attachments
+**/
+ChatSystem.prototype.sendMsg = function(chan, text, attachments) {};
+
+/**
+ * Async fetch history
+ * @param {Room} chan
+ * @param {number} ts
+**/
+ChatSystem.prototype.removeMsg = function(chan, ts) {};
+
+/**
+ * Edit msg
+ * @param {Room} chan
+ * @param {number} ts
+ * @param {string} newText
+**/
+ChatSystem.prototype.editMsg = function(chan, ts, newText) {};
+
+/**
+ * Add reaction
+ * @param {Room} chan
+ * @param {number} ts
+ * @param {string} reaction
+**/
+ChatSystem.prototype.addReaction = function(chan, ts, reaction) {};
+
+/**
+ * Add reaction
+ * @param {Room} chan
+ * @param {number} ts
+ * @param {string} reaction
+**/
+ChatSystem.prototype.removeReaction = function(chan, ts, reaction) {};
+
+/**
+ * Add reaction
+ * @param {Room} chan
+ * @param {string} contentType
+ * @param {function((string|null))} callback
+**/
+ChatSystem.prototype.openUploadFileStream = function(chan, contentType, callback) {};
+
+/**
+ * Async fetch history
+ * @param {Room} chan
+**/
+ChatSystem.prototype.fetchHistory = function(chan) {};
+
+/**
+ * @param {Room} chan
+**/
+ChatSystem.prototype.sendTyping = function(chan) {};
+
+/**
+ * @param {Room} chan
+ * @param {number} ts
+**/
+ChatSystem.prototype.markRead = function(chan, ts) {};
+
+/**
+ * @param {Room} chan
+ * @param {string} cmd
+ * @param {string} args
+**/
+ChatSystem.prototype.sendCommand = function(chan, cmd, args) {};
+
+/**
+ * @param {number} knownVersion
+ * @param {number} nowTs
+ * @return {Object|null}
+**/
+ChatSystem.prototype.poll = function(knownVersion, nowTs) {};
+
+/**
+ * @constructor
+**/
+function MultiChatManager() {
+    /** @type {Array<ChatSystem>} */
+    this.contexts = [];
+}
+
+/**
+ * @param {ChatSystem} chatSystem
+**/
+MultiChatManager.prototype.push = function(chatSystem) {
+    this.contexts.push(chatSystem);
+};
+
+/**
+ * @param {string} id
+ * @return {ChatSystem|null}
+**/
+MultiChatManager.prototype.getById = function(id) {
+    for (var i = 0, nbContexts = this.contexts.length; i < nbContexts; i++)
+        if (id === this.contexts[i].getId())
+            return this.contexts[i];
+    return null;
+};
+
+/**
+ * You may want to return true to break the loop
+**/
+MultiChatManager.prototype.foreachChannels = function(cb) {
+    for (var i =0, nbCtx =this.contexts.length; i < nbCtx; i++) {
+        var ctx = this.contexts[i].getChatContext();
+        for (var chanId in ctx.channels)
+            if (cb(ctx.channels[chanId], chanId) === true)
+                return;
+    }
+};
+
+/**
+ * You may want to return true to break the loop
+**/
+MultiChatManager.prototype.foreachContext = function(cb) {
+    for (var i =0, nbCtx =this.contexts.length; i < nbCtx; i++)
+        if (cb(this.contexts[i].getChatContext()) === true)
+            return;
+};
+
+/**
+ * @param {string} chanId
+ * @return {ChatSystem|null}
+**/
+MultiChatManager.prototype.getChannelContext = function(chanId) {
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var ctx = this.contexts[i].getChatContext();
+        if (ctx.channels[chanId])
+            return this.contexts[i];
+    }
+    return null;
+};
+
+/**
+ * @param {string} userId
+ * @return {ChatSystem|null}
+**/
+MultiChatManager.prototype.getUserContext = function(userId) {
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var ctx = this.contexts[i].getChatContext();
+        if (ctx.users[userId])
+            return this.contexts[i];
+    }
+    return null;
+};
+
+/**
+ * @param {string} chanId
+ * @return {Room|null}
+**/
+MultiChatManager.prototype.getChannel = function(chanId) {
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var chan = this.contexts[i].getChatContext().channels[chanId];
+        if (chan)
+            return chan;
+    }
+    return null;
+};
+
+/**
+ * @param {function(Room, ChatSystem, string):boolean=} filter (room, roomId)
+ * @return {Array<string>}
+**/
+MultiChatManager.prototype.getChannelIds = function(filter) {
+    var ids = [];
+
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var channels = this.contexts[i].getChatContext().channels;
+        for (var chanId in channels) {
+            if (!filter || filter(channels[chanId], this.contexts[i], chanId)) {
+                ids.push(chanId);
+            }
+        }
+    }
+    return ids;
+};
+
+/**
+ * @param {string} userId
+ * @return {Chatter|null}
+**/
+MultiChatManager.prototype.getUser = function(userId) {
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++) {
+        var user = this.contexts[i].getChatContext().users[userId];
+        if (user)
+            return user;
+    }
+    return null;
+};
+
+/**
+ * @param {string} userId
+ * @return {boolean}
+**/
+MultiChatManager.prototype.isMe = function(userId) {
+    for (var i =0, nbCtx = this.contexts.length; i < nbCtx; i++)
+        if (this.contexts[i].getChatContext().self.id === userId)
+            return true;
+    return false;
+};
+
+MultiChatManager.prototype.poll = function(knownVersion, callback) {
+    this.contexts.forEach(function(ctx) {
+        ctx.onRequest();
+    });
+    setTimeout((function() {
+        //FIXME polling
+        var liveFeed
+            ,staticFeed
+            ,allTyping
+            ,v = 0
+            ,updated = false
+            ,now = Date.now();
+
+        this.contexts.forEach(function(ctx) {
+            var id = ctx.getId();
+            if (id) {
+                var res = ctx.poll(knownVersion, now);
+
+                if (res) {
+                    if (res["static"]) {
+                        if (!staticFeed)
+                            staticFeed = {};
+                        staticFeed[id] = res["static"];
+                    }
+                    if (res["live"])
+                        if (!liveFeed)
+                            liveFeed = {};
+                        for (var i in res["live"]) {
+                            liveFeed[i] = res["live"][i];
+                        }
+                    if (res["typing"]) {
+                        if (!allTyping)
+                            allTyping = {};
+                        for (var i in res["typing"])
+                            allTyping[i] = res["typing"][i];
+                    }
+                    v = Math.max(v, (res["v"] || 0));
+                    updated = true;
+                }
+            }
+        });
+        if (updated)
+            callback({
+                "live": liveFeed,
+                "static": staticFeed,
+                "typing": allTyping,
+                "v": Math.max(v, knownVersion)
+            });
+        else
+            callback({
+                "v": knownVersion
+            });
+    }).bind(this), 2000);
+};
+
+/** @suppress {undefinedVars,checkTypes} */
+(function() {
+    if (typeof module !== "undefined") {
+        module.exports.MultiChatManager = MultiChatManager;
+    }
+})();
+

+ 7 - 6
srv/src/room.js

@@ -20,7 +20,7 @@ function Room(id) {
     /** @type {number} */
     this.lastRead = 0;
     /** @type {number} */
-    this.lastMsg = 0;
+    this.lastMsg;
     /** @type {Object.<string, Chatter>} */
     this.users = {};
     /** @type {string|undefined} */
@@ -92,8 +92,10 @@ Room.prototype.toStatic = function(t) {
  * @param {*} chanData
  * @param {ChatContext} ctx
  * @param {number} t
+ * @param {string=} idPrefix
 **/
-Room.prototype.update = function(chanData, ctx, t) {
+Room.prototype.update = function(chanData, ctx, t, idPrefix) {
+    idPrefix = idPrefix || "";
     if (chanData["name"] !== undefined) this.name = chanData["name"];
     if (chanData["created"] !== undefined) this.created = chanData["created"];
     if (chanData["creator"] !== undefined) this.creator = ctx.users[chanData["creator"]];
@@ -102,24 +104,23 @@ Room.prototype.update = function(chanData, ctx, t) {
     if (chanData["last_read"] !== undefined) this.lastRead = Math.max(parseFloat(chanData["last_read"]), this.lastRead);
     if (chanData["last_msg"] !== undefined) this.lastMsg = parseFloat(chanData["last_msg"]);
     if (chanData["is_private"] !== undefined) this.isPrivate = chanData["is_private"];
-    if (chanData["latest"]) this.lastMsg = parseFloat(chanData["latest"]["ts"]);
     if (chanData["is_starred"] !== undefined) this.starred = chanData["is_starred"];
     if (chanData["members"]) {
         this.users = {};
         if (chanData["members"]) for (var i =0, nbMembers = chanData["members"].length; i < nbMembers; i++) {
-            var member = ctx.users[chanData["members"][i]];
+            var member = ctx.users[idPrefix +chanData["members"][i]];
             this.users[member.id] = member;
             member.channels[this.id] = this;
         }
     }
     if (chanData["topic"]) {
         this.topic = chanData["topic"]["value"];
-        this.topicCreator = ctx.users[chanData["topic"]["creator"]];
+        this.topicCreator = ctx.users[idPrefix +chanData["topic"]["creator"]];
         this.topicTs = chanData["topic"]["last_set"];
     }
     if (chanData["purpose"]) {
         this.purpose = chanData["purpose"]["value"];
-        this.purposeCreator = ctx.users[chanData["purpose"]["creator"]];
+        this.purposeCreator = ctx.users[idPrefix +chanData["purpose"]["creator"]];
         this.purposeTs = chanData["purpose"]["last_set"];
     }
     this.version = Math.max(this.version, t);

+ 243 - 126
srv/src/slack.js

@@ -52,28 +52,61 @@ const SLACK_ENDPOINT = "https://slack.com/api/"
     ] // Message type that affect live history
 ;
 
+/**
+ * @implements {ChatSystem}
+**/
 function Slack(sess, manager) {
     this.token = sess.slackToken;
     this.sessId = sess.id;
     this.manager = manager;
     this.rtm = null;
+    this.rtmId = 1;
     this.data = new SlackData(this);
     this.history = {};
     this.pendingRtm = {};
+    this.pendingMessages = [];
+    this.pendingPing = false;
     this.connected = false;
     this.closing = false;
 }
 
-Slack.prototype.onRequest = function(knownVersion, cb) {
-    this.lastCb = cb;
+Slack.prototype.getId = function() {
+    return this.data.team ? this.data.team.id : null;
+};
+
+Slack.prototype.onRequest = function() {
     if (this.connected === false) {
-        this.connect(knownVersion);
-    } else {
-        this.waitForEvent(knownVersion);
+        this.connect();
     }
 };
 
-Slack.prototype.connect = function(knownVersion) {
+function httpsRequest(url, cb) {
+    try {
+        https.get(url, (res) => {
+            if (res.statusCode !== 200) {
+                cb(res.statusCode, null);
+                return;
+            }
+            var body = [];
+            res.on('data', (chunk) => {
+                body.push(chunk);
+            });
+            res.on('end', () => {
+                body = Buffer.concat(body).toString();
+                try {
+                    body = JSON.parse(body);
+                } catch (e) {}
+                cb && cb(res.statusCode, body);
+            });
+        });
+    }
+    catch (e) {
+        console.error("Error in https request: ", e);
+        cb && cb(0, null);
+    }
+}
+
+Slack.prototype.connect = function(cb) {
     var _this = this;
 
     this.connected = undefined;
@@ -82,31 +115,20 @@ Slack.prototype.connect = function(knownVersion) {
             _this.error = body.error;
             _this.connected = false;
             console.error("Slack api responded " +status);
-            _this.lastCb(_this);
-            return;
-        }
-        if (!body) {
+            cb && cb(_this);
+        } else if (!body || !body.ok) {
             _this.error = "Slack API error";
             _this.connected = false;
-            _this.lastCb(_this);
-            return;
-        }
-        if (!body.ok) {
-            _this.error = body.error;
-            _this.connected = false;
-            console.error("Slack api responded !ok with ", body);
-            _this.lastCb(_this);
-            return;
+            cb && cb(_this);
+        } else {
+            _this.data.updateStatic({
+                team: body["team"],
+                users: body["users"],
+                bots: body["bots"],
+                self: body["self"]
+            }, Date.now());
+            _this.connectRtm(body.url);
         }
-        _this.getEmojis((emojis) => {
-            _this.getSlashCommands((commands) => {
-                body.emojis = emojis;
-                body.commands = commands;
-                _this.data.updateStatic(body, Date.now());
-                _this.connectRtm(body.url);
-                _this.waitForEvent(knownVersion);
-            });
-        });
     });
 };
 
@@ -117,12 +139,12 @@ Slack.prototype.sendCommand = function(room, cmd, arg) {
         +"?token=" +this.token
         +"&command=" +encodeURIComponent(cmd.name)
         +"&disp=" +encodeURIComponent(cmd.name)
-        +"&channel=" +room.id
+        +"&channel=" +room.remoteId
         +"&text=" +arg);
 }
 
 Slack.prototype.sendTyping = function(room) {
-    this.rtm.send('{"id":' +this.rtmId++ +',"type":"typing","channel":"' +room.id +'"}');
+    this.rtm.send('{"id":' +this.rtmId++ +',"type":"typing","channel":"' +room.remoteId +'"}');
 }
 
 Slack.prototype.getSlashCommands = function(cb) {
@@ -143,40 +165,21 @@ Slack.prototype.getEmojis = function(cb) {
     });
 };
 
-Slack.prototype.waitForEvent = function(knownVersion) {
-    var tick = 0
-        ,_this = this
-        ,interval;
-
-    interval = setInterval(() => {
-        tick++;
-        if (!_this.lastCb) {
-            clearInterval(interval);
-            return;
-        }
-        if (_this.connected) {
-            var updatedCtx = _this.data.getUpdates(knownVersion, Date.now())
-                ,updatedLive = _this.getLiveUpdates(knownVersion)
-                ,updated;
-
-            if (updatedCtx || updatedLive) {
-                updated = {};
-                updated["static"] = updatedCtx;
-                updated["live"] = updatedLive;
-                updated["v"] = Math.max(_this.data.liveV, _this.data.staticV);
-            }
-            if (updated) {
-                clearInterval(interval);
-                _this.lastCb(_this, updated);
-                return;
-            }
-        }
-        if (tick >= 55) { // < 1 minute timeout
-            clearInterval(interval);
-            _this.lastCb(_this, { v: knownVersion });
-            return;
+Slack.prototype.poll = function(knownVersion, now) {
+    if (this.connected) {
+        var updatedCtx = this.data.getUpdates(knownVersion)
+            ,updatedTyping = this.data.getWhoIsTyping(now)
+            ,updatedLive = this.getLiveUpdates(knownVersion);
+
+        if (updatedCtx || updatedLive || updatedTyping) {
+            return {
+                "static": updatedCtx,
+                "live": updatedLive,
+                "typing": updatedTyping,
+                "v": Math.max(this.data.liveV, this.data.staticV)
+            };
         }
-    }, 1000);
+    }
 };
 
 /** @return {Object|undefined} */
@@ -201,65 +204,101 @@ Slack.prototype.getLiveUpdates = function(knownVersion) {
     return undefined;
 };
 
+Slack.prototype.unstackPendingMessages = function() {
+    for (var i = this.pendingMessages.length -1; i >= 0; i--) {
+        this.onMessage(this.pendingMessages[0], Date.now());
+        this.pendingMessages.unshift();
+    }
+};
+
 Slack.prototype.onMessage = function(msg, t) {
-    if (msg["ok"] === true && msg["reply_to"] && this.pendingRtm[msg["reply_to"]]) {
+    if (msg["reply_to"] && this.pendingRtm[msg["reply_to"]]) {
         var ts = msg["ts"]
             ,rtmId = msg["reply_to"];
         msg = this.pendingRtm[rtmId];
         msg["ts"] = ts;
         delete this.pendingRtm[rtmId];
     }
-    this.data.onMessage(msg, t);
-    if ((msg["channel"] || msg["channel_id"] || (msg["item"] && msg["item"]["channel"])) && msg["type"] && UPDATE_LIVE.indexOf(msg["type"]) !== -1) {
-        var channelId = msg["channel"] || msg["channel_id"] || msg["item"]["channel"]
-            ,channel = this.data.channels[msg["channel"]]
-            ,histo = this.history[channelId];
-        if (histo) {
-            var lastMsg = histo.push(msg, t);
-            if (lastMsg)
-                this.data.liveV = t;
-            histo.resort();
-            if (channel)
-                channel.setLastMsg(lastMsg, t);
-        } else if (channel) {
-            this.fetchHistory(msg["channel"]);
+    if (msg["type"] === "hello" && msg["start"] && msg["start"]["rtm_start"]) {
+        var _this = this;
+        _this.getEmojis((emojis) => {
+            _this.getSlashCommands((commands) => {
+                var msgContent = msg.start.rtm_start;
+                msgContent.self = msg.self;
+                msgContent.emojis = emojis;
+                msgContent.commands = commands;
+
+                _this.data.updateStatic(msgContent, Date.now());
+                _this.unstackPendingMessages();
+                _this.connected = true;
+                _this.unstackPendingMessages();
+            });
+        });
+    } else if (this.connected) {
+        this.data.onMessage(msg, t);
+        if ((msg["channel"] || msg["channel_id"] || (msg["item"] && msg["item"]["channel"])) && msg["type"] && UPDATE_LIVE.indexOf(msg["type"]) !== -1) {
+            var channelId = this.data.team.id +'|' +(msg["channel"] || msg["channel_id"] || msg["item"]["channel"])
+                ,channel = this.data.channels[channelId]
+                ,histo = this.history[channelId];
+            // FIXME remove typing for user
+            if (histo) {
+                var lastMsg = histo.push(msg, t);
+                if (lastMsg)
+                    this.data.liveV = t;
+                histo.resort();
+                if (channel)
+                    channel.setLastMsg(lastMsg, t);
+            } else if (channel) {
+                this.fetchHistory(channel);
+            }
         }
+    } else {
+        this.pendingMessages.push(msg);
     }
 };
 
 /**
  * @param {SlackChan|SlackGroup|SlackIms} chan
+ * @param {string} id
  * @param {number} ts
 **/
-Slack.prototype.markRead = function(chan, ts) {
+Slack.prototype.markRead = function(chan, id, ts) {
     var apiURI;
 
-    if (chan.id[0] === 'C')
+    if (chan.remoteId[0] === 'C')
         apiURI = SLACK_ENDPOINT+GETAPI.read.channel;
-    else if (chan.id[0] === 'G')
+    else if (chan.remoteId[0] === 'G')
         apiURI = SLACK_ENDPOINT+GETAPI.read.group;
-    else if (chan.id[0] === 'D')
+    else if (chan.remoteId[0] === 'D')
         apiURI = SLACK_ENDPOINT+GETAPI.read.im;
 
     httpsRequest(apiURI
         +"?token=" +this.token
-        +"&channel="+chan.id
-        +"&ts="+ts);
+        +"&channel="+chan.remoteId
+        +"&ts="+id);
 };
 
 Slack.prototype.connectRtm = function(url, cb) {
     var _this = this;
 
-    this.rtmId = 0;
+    this.rtmId = 1;
+    var protocol = url.substr(0, url.indexOf('://') +3);
+    url = url.substr(protocol.length);
+    url = protocol +url.substr(0, url.indexOf('/'))+
+        "/?flannel=1&token=" +this.token+
+        "&start_args="+
+        encodeURIComponent("?simple_latest=true&presence_sub=true&mpim_aware=false&canonical_avatars=true")
     this.rtm = new WebSocket(url);
     this.rtm.on("message", function(msg) {
         if (!_this.connected && cb) {
             cb();
         }
-        if (!_this.connected) {
-            _this.connected = true;
+        try {
+            msg = JSON.parse(msg);
+        } catch (ex) {
+            console.error("WTF invalid JSON ", msg);
         }
-        _this.onMessage(JSON.parse(msg), Date.now());
+        _this.onMessage(msg, Date.now());
     });
     this.rtm.once("error", function(e) {
         _this.connected = false;
@@ -281,6 +320,20 @@ Slack.prototype.ping = function() {
         +"?token=" +this.token);
 };
 
+Slack.prototype.rtmPing = function() {
+    if (this.connected) {
+        if (this.pendingPing && this.pendingRtm[this.pendingPing]) {
+            //FIXME timeout
+            console.error("Ping timeout");
+        } else {
+            var rtmId = this.rtmId++;
+            this.pendingRtm[rtmId] = { type: 'ping' };
+            this.pendingPing = rtmId;
+            this.rtm.send('{"id":' +rtmId +',"type":"ping"}');
+        }
+    }
+};
+
 Slack.prototype.close = function() {
     if (!this.closing) {
         this.closing = true;
@@ -333,7 +386,7 @@ Slack.prototype.openUploadFileStream = function(channel, contentType, callback)
         ,method: 'POST'
         ,path: SLACK_ENDPOINT_PATH +GETAPI.postFile
             +"?token=" +this.token
-            +"&channels=" +channel.id
+            +"&channels=" +channel.remoteId
         ,headers: {
             "Content-Type": contentType
         }
@@ -359,30 +412,79 @@ Slack.prototype.openUploadFileStream = function(channel, contentType, callback)
     return req;
 };
 
+function findBoundary() {
+    const prefix = '-'.repeat(15)
+        ,alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+        ,doYouKnowWhoManyTheyAre = alphabet.length // 26 letters in da alphabet
+        ,nbArg = arguments.length;
+
+    for (let i =0; i < doYouKnowWhoManyTheyAre; i++)
+        bLoop: for (let j =0; j < doYouKnowWhoManyTheyAre; j++) {
+            const boundary = prefix +alphabet[i] +alphabet[j];
+            for (let argIndex =0; argIndex < nbArg; argIndex++) {
+                if (arguments[argIndex].indexOf(boundary) >= 0)
+                    continue bLoop;
+            }
+            return boundary;
+        }
+}
+
+function encodeWithBoundary(boundary, data) {
+    var resp = "";
+    for (var k in data) {
+        resp += '--' +boundary +'\r\n';
+        resp += 'Content-Disposition: form-data; name="' +k +'"\r\n\r\n'
+            +data[k]
+            +'\r\n';
+    };
+    return resp +'--' +boundary +'--\r\n';
+}
+
 /**
- * @param {SlackChan|SlackGroup|SlackIms} channel
- * @param {string} contentType
- * @param {function(string|null)} callback
+ * @param {string} serviceId
+ * @param {Object} payload
+ * @param {function(string|null)=} callback
 **/
-Slack.prototype.sendAction = function(contentType, payloadStream, callback) {
-    var req = https.request({
-        hostname: SLACK_HOSTNAME
-        ,method: 'POST'
-        ,path: SLACK_ENDPOINT_PATH +GETAPI.sendAction
-            +"?token=" +this.token
-        ,headers: {
-            "Content-Type": contentType
-        }
-    }, (res) => {
-        var resp;
-        res.on("data", (chunk) => {
-            resp = resp ? Buffer.concat([resp, chunk], resp.length +chunk.length) : Buffer.from(chunk);
-        });
-        res.once("end", () => {
-            callback(resp);
+Slack.prototype.sendAction = function(serviceId, payload, callback) {
+    var channel = this.data.channels[payload["channel_id"]]
+        ,service = this.data.users[serviceId];
+
+    if (channel && service) {
+        payload["channel_id"] = channel.remoteId;
+        var payloadString = JSON.stringify(payload)
+            ,boundary = findBoundary(service.remoteId, payloadString)
+            ,body = encodeWithBoundary(boundary, {
+                "service_id": service.remoteId
+                ,"payload": payloadString
+            });
+
+        var req = https.request({
+            hostname: SLACK_HOSTNAME
+            ,method: 'POST'
+            ,path: SLACK_ENDPOINT_PATH +GETAPI.sendAction +"?token=" +this.token
+            ,headers: {
+                "Content-Type": "multipart/form-data; boundary=" +boundary,
+                "Content-Length": body.length
+            }
+        }, (res) => {
+            if (callback) {
+                var resp = [];
+                res.on("data", (chunk) => { resp.push(chunk); });
+                res.once("end", () => {
+                    resp = Buffer.concat(resp).toString();
+                    try {
+                        resp = JSON.parse(resp);
+                    } catch (e) {
+                        resp = null;
+                    }
+                    callback(resp && resp.ok ? resp : false);
+                });
+            }
         });
-    });
-    payloadStream.pipe(req, { end: true });
+        req.end(body);
+        return true;
+    }
+    return false;
 };
 
 /**
@@ -396,7 +498,7 @@ Slack.prototype.openUploadFileStream = function(channel, contentType, callback)
         ,method: 'POST'
         ,path: SLACK_ENDPOINT_PATH +GETAPI.postFile
             +"?token=" +this.token
-            +"&channels=" +channel.id
+            +"&channels=" +channel.remoteId
         ,headers: {
             "Content-Type": contentType
         }
@@ -430,7 +532,7 @@ Slack.prototype.addReaction = function(channel, msgId, reaction) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.addReaction
         +"?token=" +this.token
         +"&name=" +reaction
-        +"&channel="+channel.id
+        +"&channel="+channel.remoteId
         +"&timestamp="+msgId);
 }
 
@@ -443,7 +545,7 @@ Slack.prototype.removeReaction = function(channel, msgId, reaction) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.removeReaction
         +"?token=" +this.token
         +"&name=" +reaction
-        +"&channel="+channel.id
+        +"&channel="+channel.remoteId
         +"&timestamp="+msgId);
 }
 
@@ -454,7 +556,7 @@ Slack.prototype.removeReaction = function(channel, msgId, reaction) {
 Slack.prototype.sendMeMsg = function(channel, text) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.postMeMsg
         +"?token=" +this.token
-        +"&channel=" +channel.id
+        +"&channel=" +channel.remoteId
         +"&text=" +text.join("\n")
         +"&as_user=true");
 };
@@ -466,9 +568,13 @@ Slack.prototype.sendMeMsg = function(channel, text) {
 **/
 Slack.prototype.sendMsg = function(channel, text, attachments) {
     if (attachments) {
+        attachments.forEach((attachmentObj) => {
+            if (attachmentObj["ts"])
+                attachmentObj["ts"] /= 1000;
+        });
         httpsRequest(SLACK_ENDPOINT +GETAPI.postMsg
             +"?token=" +this.token
-            +"&channel=" +channel.id
+            +"&channel=" +channel.remoteId
             +"&text=" +text.join("\n")
             + (attachments ? ("&attachments=" +encodeURIComponent(JSON.stringify(attachments))) : "")
             +"&as_user=true");
@@ -480,11 +586,11 @@ Slack.prototype.sendMsg = function(channel, text, attachments) {
         var fullDecodedText = decodedText.join("\n");
         this.pendingRtm[this.rtmId] = {
             type: 'message',
-            channel: channel.id,
-            user: this.data.self.id,
+            channel: channel.remoteId,
+            user: this.data.self.remoteId,
             text: fullDecodedText
         };
-        this.rtm.send('{"id":' +this.rtmId++ +',"type":"message","channel":"' +channel.id +'", "text":' +JSON.stringify(fullDecodedText) +'}');
+        this.rtm.send('{"id":' +this.rtmId++ +',"type":"message","channel":"' +channel.remoteId +'", "text":' +JSON.stringify(fullDecodedText) +'}');
     }
 };
 
@@ -495,7 +601,7 @@ Slack.prototype.sendMsg = function(channel, text, attachments) {
 Slack.prototype.removeMsg = function(channel, msgId) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.removeMsg
         +"?token=" +this.token
-        +"&channel=" +channel.id
+        +"&channel=" +channel.remoteId
         +"&ts=" +msgId
         +"&as_user=true");
 };
@@ -508,15 +614,19 @@ Slack.prototype.removeMsg = function(channel, msgId) {
 Slack.prototype.editMsg = function(channel, msgId, text) {
     httpsRequest(SLACK_ENDPOINT +GETAPI.editMsg
         +"?token=" +this.token
-        +"&channel=" +channel.id
+        +"&channel=" +channel.remoteId
         +"&ts=" +msgId
         +"&text=" +text.join("\n")
         +"&as_user=true");
 };
 
-Slack.prototype.fetchHistory = function(targetId) {
+/**
+ * @param {SlackChan|SlackGroup|SlackIms} target
+**/
+Slack.prototype.fetchHistory = function(target) {
     var _this = this
-        ,baseUrl = "";
+        ,baseUrl = ""
+        ,targetId = target.remoteId;
 
     if (targetId[0] === 'D') {
         baseUrl = SLACK_ENDPOINT +GETAPI.directHistory;
@@ -531,18 +641,25 @@ Slack.prototype.fetchHistory = function(targetId) {
         +"&count=" +HISTORY_LENGTH,
     (status, resp) => {
         if (status === 200 && resp && resp.ok) {
-            var history = _this.history[targetId]
+            var history = _this.history[target.id]
                 ,now = Date.now();
             if (!history) {
-                history = _this.history[targetId] = new SlackHistory(targetId, HISTORY_LENGTH);
+                history = _this.history[target.id] = new SlackHistory(target.remoteId, target.id, this.data.team.id +'|', HISTORY_LENGTH);
                 history.isNew = true;
             }
-            var now = Date.now();
-            if (history.pushAll(resp.messages, now))
+            var now = Date.now()
+                ,lastTs = history.pushAll(resp.messages, now);
+
+            if (lastTs)
                 this.data.liveV = now;
+            target.setLastMsg(lastTs, now);
         }
     });
 };
 
+Slack.prototype.getChatContext = function() {
+    return this.data;
+};
+
 module.exports.Slack = Slack;
 

+ 115 - 62
srv/src/slackData.js

@@ -6,12 +6,15 @@ const ChatContext = require('./context.js').ChatContext
     ,Chatter = require('./chatter.js').Chatter
     ,PrivateMessageRoom = require('./room.js').PrivateMessageRoom;
 
+const SLACK_TYPING_DELAY = 6000;
+
 /**
  * @constructor
  * @extends {ChatInfo}
 **/
 function SlackTeam(teamId) {
     ChatInfo.call(this, teamId);
+
     /** @type {string} */
     this.domain;
     /** @type {string} */
@@ -33,25 +36,6 @@ function SlackTeam(teamId) {
 SlackTeam.prototype = Object.create(ChatInfo.prototype);
 SlackTeam.prototype.constructor = SlackTeam;
 
-SlackTeam.prototype.toStatic = function(t) {
-    var res = ChatInfo.prototype.toStatic.call(this, t);
-    if (res) {
-        res["domain"] = this.domain
-        res["prefs"] = {
-            "calling_app_id": this.callApp
-            ,"calling_app_name": this.callAppName
-            ,"disable_file_uploads": this.fileUploadPermission
-            ,"disable_file_editing": this.fileEditPermission
-            ,"disable_file_deleting": this.fileDeletePermission
-        }
-        res["icon"] = {
-            "small": this.icons.small
-            ,"large": this.icons.large
-        };
-    }
-    return res;
-};
-
 SlackTeam.prototype.update = function(teamData, t) {
     ChatInfo.prototype.update.call(this, teamData, t);
     if (teamData["domain"] !== undefined) this.domain = teamData["domain"];
@@ -71,8 +55,10 @@ SlackTeam.prototype.update = function(teamData, t) {
 /**
  * @constructor
 **/
-function SlackChan(chanId, isGroup) {
-    Room.call(this, chanId);
+function SlackChan(teamId, chanId, isGroup) {
+    Room.call(this, teamId +chanId);
+    /** @const @type {string} */
+    this.remoteId = chanId;
     this.isPrivate = isGroup;
 };
 
@@ -88,24 +74,64 @@ SlackChan.prototype.setNameFromMembers = function() {
     this.name = userNames.join(", ");
 };
 
+/**
+ * @param {*} chanData
+ * @param {ChatContext} ctx
+ * @param {number} t
+ * @param {string=} idPrefix
+**/
+SlackChan.prototype.update = function(chanData, ctx, t, idPrefix) {
+    if (chanData["last_read"] !== undefined)
+        chanData["last_read"] = parseFloat(chanData["last_read"]) * 1000;
+    if (chanData["latest"] !== undefined)
+        chanData["last_msg"] = parseFloat(chanData["latest"]) * 1000;
+    return Room.prototype.update.call(this, chanData, ctx, t, idPrefix);
+};
+
 /**
  * @constructor
  * @extends {PrivateMessageRoom}
  * @param {string} id
  * @param {SlackChatter} user
 **/
-function SlackIms(id, user) {
-    PrivateMessageRoom.call(this, id, user);
+function SlackIms(teamId, id, user) {
+    PrivateMessageRoom.call(this, teamId +id, user);
+
+    /** @const @type {string} */
+    this.remoteId = id;
 };
 SlackIms.prototype = Object.create(PrivateMessageRoom.prototype);
 SlackIms.prototype.constructor = SlackIms;
 
+/**
+ * @param {*} chanData
+ * @param {ChatContext} ctx
+ * @param {number} t
+ * @param {string=} idPrefix
+**/
+SlackIms.prototype.update = function(chanData, ctx, t, idPrefix) {
+    if (chanData["last_read"] !== undefined)
+        chanData["last_read"] = parseFloat(chanData["last_read"]) * 1000;
+    if (chanData["latest"] !== undefined)
+        chanData["last_msg"] = parseFloat(chanData["latest"]) * 1000;
+    return PrivateMessageRoom.prototype.update.call(this, chanData, ctx, t, idPrefix);
+};
+
 /**
  * @constructor
  * @extends {Chatter}
 **/
-function SlackChatter(id) {
-    Chatter.call(this, id);
+function SlackChatter(teamId, id) {
+    Chatter.call(this, teamId +id);
+
+    /** @type {string} */
+    this.remoteId = id;
+
+    /** @type {Object.<string, string>} */
+    this.icons = {
+        small: ""
+        ,large: ""
+    };
 };
 SlackChatter.prototype = Object.create(Chatter.prototype);
 SlackChatter.prototype.constructor = SlackChatter;
@@ -115,24 +141,40 @@ SlackChatter.prototype.setPresence = function(presenceStr, t) {
     this.version = Math.max(t, this.version);
 };
 
+SlackChatter.prototype.update = function(userData, t) {
+    Chatter.prototype.update.call(this, userData, t);
+
+    if (userData["profile"]) {
+        this.email = userData["profile"]["email"];
+        this.firstName = userData["profile"]["first_name"];
+        this.lastName = userData["profile"]["last_name"];
+    }
+};
+
+SlackChatter.prototype.getSmallIcon = function() {
+    return this.icons.small;
+};
+
+SlackChatter.prototype.getLargeIcon = function() {
+    return this.icons.large;
+};
+
 /**
  * @constructor
  * @extends {SlackChatter}
 **/
-function SlackUser(id) {
-    SlackChatter.call(this, id);
+function SlackUser(teamId, id) {
+    SlackChatter.call(this, teamId, id);
 };
 SlackUser.prototype = Object.create(SlackChatter.prototype);
 SlackUser.prototype.constructor = SlackUser;
 
-SlackUser.prototype.update = function(data, t) {
-    SlackChatter.prototype.update.call(this, data, t);
-    if (data["profile"]) {
-        this.icons.small = data["profile"]["image_48"];
-        this.icons.large = data["profile"]["image_512"];
-        this.email = data["profile"]["email"];
-        this.firstName = data["profile"]["first_name"];
-        this.lastName = data["profile"]["last_name"];
+SlackUser.prototype.update = function(userData, t) {
+    SlackChatter.prototype.update.call(this, userData, t);
+
+    if (userData["profile"]) {
+        this.icons.small = userData["profile"]["image_48"] || this.icons.small;
+        this.icons.large = userData["profile"]["image_512"] || this.icons.large;
     }
 };
 
@@ -140,8 +182,8 @@ SlackUser.prototype.update = function(data, t) {
  * @constructor
  * @extends {SlackChatter}
 **/
-function SlackBot(id) {
-    SlackChatter.call(this, id);
+function SlackBot(teamId, id) {
+    SlackChatter.call(this, teamId, id);
     /** @type {string} */
     this.appId;
     this.isBot = true;
@@ -155,13 +197,14 @@ SlackBot.prototype.update = function(botData, t) {
     if (botData["app_id"] !== undefined) this.appId = botData["app_id"];
     this.isBot = true;
     if (botData["icons"]) {
-        this.icons.small = botData["icons"]["image_48"];
-        this.icons.large = botData["icons"]["image_72"];
+        this.icons.small = botData["icons"]["image_48"] || this.icons.small;
+        this.icons.large = botData["icons"]["image_72"] || this.icons.large;
     }
 };
 
 /**
  * @constructor
+ * @extends {ChatContext}
 **/
 function SlackData(slack) {
     ChatContext.call(this);
@@ -179,28 +222,36 @@ SlackData.prototype.constructor = SlackData;
  * @param {*} data
 **/
 SlackData.prototype.updateStatic = function(data, t) {
+    if (data["team"] && !this.team) this.team = this.teamFactory(data["team"]["id"]);
     if (data["bots"]) for (var i =0, nbBots = data["bots"].length; i < nbBots; i++) {
-        var botObj = this.users[data["bots"][i]["id"]];
+        var botObj = this.users[this.team.id +'|' +data["bots"][i]["id"]];
         if (!botObj)
-            botObj = this.users[data["bots"][i]["id"]] = new SlackBot(data["bots"][i]["id"]);
+            botObj = this.users[this.team.id +'|' +data["bots"][i]["id"]] = new SlackBot(this.team.id +'|', data["bots"][i]["id"]);
         botObj.update(data["bots"][i], t);
     }
-    ChatContext.prototype.updateStatic.call(this, data, t);
+    ChatContext.prototype.updateStatic.call(this, data, t, this.team.id +'|');
     if (data["ims"]) for (var i =0, nbIms = data["ims"].length; i < nbIms; i++) {
-        var user = this.users[data["ims"][i]["user"]];
+        var user = this.users[this.team.id +'|' +data["ims"][i]["user"]];
         if (user) {
-            if (!this.channels[data["ims"][i]["id"]])
-                this.channels[data["ims"][i]["id"]] = new SlackIms(data["ims"][i]["id"], user);
-            this.channels[data["ims"][i]["id"]].update(data["ims"][i], this, t);
+            if (!this.channels[this.team.id +'|' +data["ims"][i]["id"]])
+                this.channels[this.team.id +'|' +data["ims"][i]["id"]] = new SlackIms(this.team.id +'|', data["ims"][i]["id"], user);
+            this.channels[this.team.id +'|' +data["ims"][i]["id"]].update(data["ims"][i], this, t, this.team.id +'|');
         }
     }
-    for (var i =0, nbGroups = data["groups"].length; i < nbGroups; i++) {
-        var groupData = data["groups"][i]
-            ,groupObj = this.channels[groupData["id"]];
+    if (data["groups"])
+        this.updateGroups(data["groups"], t);
+    if (data["mpims"])
+        this.updateGroups(data["mpims"], t);
+};
+
+SlackData.prototype.updateGroups = function(groupsData, t) {
+    for (var i =0, nbGroups = groupsData.length; i < nbGroups; i++) {
+        var groupData = groupsData[i]
+            ,groupObj = this.channels[this.team.id +'|' +groupData["id"]];
 
         if (!groupObj)
-            groupObj = this.channels[groupData["id"]] = new SlackChan(groupData["id"], true);
-        groupObj.update(groupData, this, t);
+            groupObj = this.channels[this.team.id +'|' +groupData["id"]] = new SlackChan(this.team.id +'|', groupData["id"], true);
+        groupObj.update(groupData, this, t, this.team.id +'|');
         groupObj.archived |= groupData["is_open"] === false;
         groupObj.isMember = true;
         if (groupData["is_mpim"])
@@ -208,16 +259,16 @@ SlackData.prototype.updateStatic = function(data, t) {
     }
 };
 
-SlackData.prototype.userFactory = function(userData) {
-    return new SlackUser(userData["id"]);
+SlackData.prototype.teamFactory = function(id) {
+    return new SlackTeam('SLACK|' +id);
 };
 
-SlackData.prototype.roomFactory = function(roomData) {
-    return new SlackChan(roomData["id"], false);
+SlackData.prototype.userFactory = function(userData) {
+    return new SlackUser(this.team.id +'|', userData["id"]);
 };
 
-SlackData.prototype.teamFactory = function(id) {
-    return new SlackTeam(id);
+SlackData.prototype.roomFactory = function(roomData) {
+    return new SlackChan(this.team.id +'|', roomData["id"], false);
 };
 
 SlackData.prototype.commandFactory = function(data) {
@@ -243,18 +294,20 @@ SlackData.prototype.commandFactory = function(data) {
 **/
 SlackData.prototype.onMessage = function(msg, t) {
     if (msg["type"] === "presence_change") {
-        var member = this.users[msg["user"]];
+        var member = this.users[this.team.id +'|' +msg["user"]];
         if (member)
             member.setPresence(msg["presence"], t);
         this.staticV = Math.max(this.staticV, t);
     } else if (msg["type"] === "user_typing") {
-        this.typing[msg["channel"]] = this.typing[msg["channel"]] || {};
-        this.typing[msg["channel"]][msg["user"]] = t;
+        var chanId = this.team.id +'|' +msg["channel"];
+        if (!this.typing[chanId])
+            this.typing[chanId] = {};
+        this.typing[chanId][this.team.id +'|' +msg["user"]] = t +SLACK_TYPING_DELAY;
         this.staticV = Math.max(this.staticV, t);
     } else if (msg["type"] === "im_marked" || msg["type"] === "channel_marked" || msg["type"] === "group_marked") {
-        var channel = this.channels[msg["channel"]];
+        var channel = this.channels[this.team.id +'|' +msg["channel"]];
         if (channel) {
-            channel.lastRead = parseFloat(msg["ts"]);
+            channel.lastRead = parseFloat(msg["ts"]) * 1000;
             this.staticV = channel.version = Math.max(channel.version, t);
         }
     }

+ 36 - 79
srv/src/slackHistory.js

@@ -6,13 +6,21 @@ const Message = require('./message.js').Message
 /**
  * @constructor
  * @extends {RoomHistory}
+ * @param {string} serviceId
  * @param {Room|string} room or roomId
+ * @param {string} idPrefix
  * @param {number} keepMessages number of messages to keep in memory
- * @param {Array|undefined} evts
- * @param {number|undefined} now
+ * @param {Array=} evts
+ * @param {number=} now
 **/
-function SlackHistory(room, keepMessages, evts, now) {
+function SlackHistory(remoteId, room, idPrefix, keepMessages, evts, now) {
     RoomHistory.call(this, room, keepMessages, evts, now);
+
+    /** @const @type {string} */
+    this.remoteId = remoteId;
+
+    /** @const @type {string} */
+    this.idPrefix = idPrefix;
 }
 SlackHistory.prototype = Object.create(RoomHistory.prototype);
 SlackHistory.prototype.constructor = SlackHistory;
@@ -20,12 +28,19 @@ SlackHistory.prototype.constructor = SlackHistory;
 SlackHistory.prototype.messageFactory = function(ev, ts) {
     if (!ev["user"])
         ev["user"] = ev["bot_id"];
+    ev["user"] = this.idPrefix +ev["user"];
+    if (ev["reactions"]) {
+        ev["reactions"].forEach((r) => {
+            for (var i =0, nbUsers =r["users"].length; i < nbUsers; i++)
+                r["users"][i] = this.idPrefix +r["users"][i];
+        });
+    }
     if (ev["is_ephemeral"] === true)
         return new NoticeMessage(ev, ts);
     if (ev["subtype"] === "me_message")
         return new MeMessage(ev, ts);
     return new Message(ev, ts);
-}
+};
 
 /**
  * @return {number} ts
@@ -38,11 +53,10 @@ SlackHistory.prototype.push = function(ev, t) {
             ,modifArg = ev;
 
         if (ev["subtype"] === "message_changed") {
-            if (ev["previous_message"]) {
+            if (ev["previous_message"])
                 targetId = ev["previous_message"]["ts"];
-            } else if (ev["message"]) {
+            else if (ev["message"])
                 targetId = ev["message"]["ts"];
-            }
             modifArg = ev["message"]
         } else if (ev["subtype"] === "message_deleted" && (ev["previous_message"] || ev["deleted_ts"])) {
             if (ev["previous_message"])
@@ -51,6 +65,18 @@ SlackHistory.prototype.push = function(ev, t) {
                 targetId = ev["deleted_ts"];
             modifArg = null;
         }
+        if (modifArg) {
+            if (modifArg["edited"])
+                modifArg["edited"] = Math.round(parseFloat(modifArg["edited"]["ts"]) * 1000);
+            if (modifArg["ts"])
+                modifArg["ts"] = parseFloat(modifArg["ts"]) * 1000;
+            if (!modifArg["id"])
+                modifArg["id"] = targetId;
+            if (modifArg["attachments"] && modifArg["attachments"].length)
+                modifArg["attachments"].forEach((a) => {
+                    a.ts *= 1000;
+                });
+        }
         for (var i =0, nbMsg = this.messages.length; i < nbMsg; i++) {
             msg = this.messages[i];
             if (msg.id === targetId) {
@@ -64,85 +90,16 @@ SlackHistory.prototype.push = function(ev, t) {
             this.messages.push(msg);
         }
     } else if (ev["type"] === "reaction_added") {
-        msg = this.addReaction(ev["reaction"], ev["user"], ev["item"]["ts"], t);
+        msg = this.addReaction(ev["reaction"], this.idPrefix +ev["user"], ev["item"]["ts"], t);
     } else if (ev["type"] === "reaction_removed") {
-        msg = this.removeReaction(ev["reaction"], ev["user"], ev["item"]["ts"], t);
+        msg = this.removeReaction(ev["reaction"], this.idPrefix +ev["user"], ev["item"]["ts"], t);
     } else {
         return 0;
     }
     while (this.messages.length > this.keepMessages)
         this.messages.shift();
     return msg ? msg.ts : 0;
-}
-
-SlackHistory.prototype.lastMessage = function() {
-    return this.messages[this.messages.length -1];
-}
-
-/**
- * @param {string} reaction
- * @param {string} userId
- * @param {string} msgId
- * @param {number} ts
- * @return {SlackMessage|null}
-**/
-SlackHistory.prototype.addReaction = function(reaction, userId, msgId, ts) {
-    var msg = this.getMessageById(msgId);
-    if (msg)
-        msg.addReaction(reaction, userId, ts);
-    return msg;
-}
-
-/**
- * @param {string} reaction
- * @param {string} userId
- * @param {string} msgId
- * @param {number} ts
- * @return {SlackMessage|null}
-**/
-SlackHistory.prototype.removeReaction = function(reaction, userId, msgId, ts) {
-    var msg = this.getMessageById(msgId);
-    if (msg)
-        msg.removeReaction(reaction, userId, ts);
-    return msg;
-}
-
-/**
- * @param {number} ts
- * @return {SlackMessage|null}
-**/
-SlackHistory.prototype.getMessage = function(ts) {
-    for (var i =0, nbMessages = this.messages.length; i < nbMessages && ts >= this.messages[i].ts; i++) {
-        if (this.messages[i].ts === ts)
-            return this.messages[i];
-    }
-    return null;
-}
-
-/**
- * @param {string} id
- * @return {SlackMessage|null}
-**/
-SlackHistory.prototype.getMessageById = function(id) {
-    for (var i =0, nbMessages = this.messages.length; i < nbMessages; i++) {
-        if (this.messages[i].id == id)
-            return this.messages[i];
-    }
-    return null;
-}
-
-/**
- * @return {Array.<*>}
-**/
-SlackHistory.prototype.toStatic = function(knownVersion) {
-    var result = [];
-
-    for (var i = this.messages.length -1; i >= 0; i--) {
-        if (this.messages[i].version > knownVersion)
-            result.push(this.messages[i].toStatic());
-    }
-    return result;
-}
+};
 
 SlackHistory.prototype.resort = function() {
     this.messages.sort(function(a, b) {

+ 7 - 0
srv/src/slackManager.js

@@ -2,6 +2,7 @@
 const Slack = require('./slack.js').Slack
     ,INACTIVE_DELAY = 120000
     ,PING_DELAY = 600000
+    ,RTM_PING_DELAY = 15000
 ;
 
 function SlackManager() {
@@ -23,6 +24,12 @@ SlackManager.prototype.getAuthScope = function() {
         "identity.email"
     ];
 };
+setInterval(function() {
+    var keys = instance.cache.keys();
+    keys.forEach(function(instKey) {
+        instance.cache.get(instKey).rtmPing();
+    });
+}, RTM_PING_DELAY);
 
 SlackManager.prototype.getScope = function() {
     return [

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно