diff --git a/elpa/circe-20160608.1315/circe-autoloads.el b/elpa/circe-20160608.1315/circe-autoloads.el deleted file mode 100644 index 1fda997..0000000 --- a/elpa/circe-20160608.1315/circe-autoloads.el +++ /dev/null @@ -1,225 +0,0 @@ -;;; circe-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "circe" "circe.el" (22533 17540 927532 375000)) -;;; Generated autoloads from circe.el - -(autoload 'circe-version "circe" "\ -Display Circe's version. - -\(fn)" t nil) - -(autoload 'circe "circe" "\ -Connect to IRC. - -Connect to the given network specified by NETWORK-OR-SERVER. - -When this function is called, it collects options from the -SERVER-OPTIONS argument, the user variable -`circe-network-options', and the defaults found in -`circe-network-defaults', in this order. - -If NETWORK-OR-SERVER is not found in any of these variables, the -argument is assumed to be the host name for the server, and all -relevant settings must be passed via SERVER-OPTIONS. - -All SERVER-OPTIONS are treated as variables by getting the string -\"circe-\" prepended to their name. This variable is then set -locally in the server buffer. - -See `circe-network-options' for a list of common options. - -\(fn NETWORK-OR-SERVER &rest SERVER-OPTIONS)" t nil) - -;;;*** - -;;;### (autoloads nil "circe-color-nicks" "circe-color-nicks.el" -;;;;;; (22533 17540 792529 295000)) -;;; Generated autoloads from circe-color-nicks.el - -(autoload 'enable-circe-color-nicks "circe-color-nicks" "\ -Enable the Color Nicks module for Circe. -This module colors all encountered nicks in a cross-server fashion. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "circe-highlight-all-nicks" "circe-highlight-all-nicks.el" -;;;;;; (22533 17541 95536 208000)) -;;; Generated autoloads from circe-highlight-all-nicks.el - -(autoload 'enable-circe-highlight-all-nicks "circe-highlight-all-nicks" "\ -Enable the Highlight Nicks module for Circe. -This module highlights all occurances of nicks in the current -channel in messages of other people. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "circe-lagmon" "circe-lagmon.el" (22533 17540 -;;;;;; 882531 348000)) -;;; Generated autoloads from circe-lagmon.el - -(defvar circe-lagmon-mode nil "\ -Non-nil if Circe-Lagmon mode is enabled. -See the `circe-lagmon-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `circe-lagmon-mode'.") - -(custom-autoload 'circe-lagmon-mode "circe-lagmon" nil) - -(autoload 'circe-lagmon-mode "circe-lagmon" "\ -Circe-lagmon-mode monitors the amount of lag on your -connection to each server, and displays the lag time in seconds -in the mode-line. - -\(fn &optional ARG)" t nil) - -;;;*** - -;;;### (autoloads nil "circe-new-day-notifier" "circe-new-day-notifier.el" -;;;;;; (22533 17541 242539 562000)) -;;; Generated autoloads from circe-new-day-notifier.el - -(autoload 'enable-circe-new-day-notifier "circe-new-day-notifier" "\ - - -\(fn)" t nil) - -(autoload 'disable-circe-new-day-notifier "circe-new-day-notifier" "\ - - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "lui-autopaste" "lui-autopaste.el" (22533 17541 -;;;;;; 5534 154000)) -;;; Generated autoloads from lui-autopaste.el - -(autoload 'enable-lui-autopaste "lui-autopaste" "\ -Enable the lui autopaste feature. - -If you enter more than `lui-autopaste-lines' at once, Lui will -ask if you would prefer to use a paste service instead. If you -agree, Lui will paste your input to `lui-autopaste-function' and -replace it with the resulting URL. - -\(fn)" t nil) - -(autoload 'disable-lui-autopaste "lui-autopaste" "\ -Disable the lui autopaste feature. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "lui-irc-colors" "lui-irc-colors.el" (22533 -;;;;;; 17541 310541 113000)) -;;; Generated autoloads from lui-irc-colors.el - -(autoload 'enable-lui-irc-colors "lui-irc-colors" "\ -Enable IRC color interpretation for Lui. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "lui-track-bar" "lui-track-bar.el" (22533 17540 -;;;;;; 837530 321000)) -;;; Generated autoloads from lui-track-bar.el - -(autoload 'enable-lui-track-bar "lui-track-bar" "\ -Enable a bar in Lui buffers that shows where you stopped reading. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil "shorten" "shorten.el" (22533 17541 129536 -;;;;;; 984000)) -;;; Generated autoloads from shorten.el - -(autoload 'shorten-strings "shorten" "\ -Takes a list of strings and returns an alist ((STRING -. SHORTENED-STRING) ...). Uses `shorten-split-function' to split -the strings, and `shorten-join-function' to join shortened -components back together into SHORTENED-STRING. See also -`shorten-validate-component-function'. - -\(fn STRINGS)" nil nil) - -;;;*** - -;;;### (autoloads nil "tracking" "tracking.el" (22533 17540 713527 -;;;;;; 492000)) -;;; Generated autoloads from tracking.el - -(defvar tracking-mode nil "\ -Non-nil if Tracking mode is enabled. -See the `tracking-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `tracking-mode'.") - -(custom-autoload 'tracking-mode "tracking" nil) - -(autoload 'tracking-mode "tracking" "\ -Allow cycling through modified buffers. -This mode in itself does not track buffer modification, but -provides an API for programs to add buffers as modified (using -`tracking-add-buffer'). - -Once this mode is active, modified buffers are shown in the mode -line. The user can cycle through them using -\\[tracking-next-buffer]. - -\(fn &optional ARG)" t nil) - -(autoload 'tracking-add-buffer "tracking" "\ -Add BUFFER as being modified with FACES. -This does check whether BUFFER is currently visible. - -If FACES is given, it lists the faces that might be appropriate -for BUFFER in the mode line. The highest-priority face of these -and the current face of the buffer, if any, is used. Priority is -decided according to `tracking-faces-priorities'. - -\(fn BUFFER &optional FACES)" nil nil) - -(autoload 'tracking-remove-buffer "tracking" "\ -Remove BUFFER from being tracked. - -\(fn BUFFER)" nil nil) - -(autoload 'tracking-next-buffer "tracking" "\ -Switch to the next active buffer. - -\(fn)" t nil) - -(autoload 'tracking-previous-buffer "tracking" "\ -Switch to the last active buffer. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil nil ("circe-chanop.el" "circe-compat.el" "circe-pkg.el" -;;;;;; "irc.el" "lcs.el" "lui-format.el" "lui-logging.el" "lui.el" -;;;;;; "make-tls-process.el") (22533 17541 344541 889000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; circe-autoloads.el ends here diff --git a/elpa/circe-20160608.1315/circe-chanop.el b/elpa/circe-20160608.1315/circe-chanop.el deleted file mode 100644 index a5880e5..0000000 --- a/elpa/circe-20160608.1315/circe-chanop.el +++ /dev/null @@ -1,97 +0,0 @@ -;;; circe-chanop.el --- Provide common channel operator commands - -;; Copyright (C) 2006, 2015 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This file is part of Circe. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This Circe module provides some often-used chanop commands. I was -;; very reluctant to add this. None of these commands will make it in -;; the core, or even be provided by default. You should have to go to -;; great lengths to use them. - -;; Always remember the Tao of IRC: -;; -;; IGNORE is the weapon of an IRC knight. Not as clumsy or as -;; random as a kickban. - -;;; Code: - -(require 'circe) - -(defun circe-command-MODE (mode) - "Set MODE in the current channel." - (interactive "sMode change: ") - (cond - ((not (string-match "^[+-]" mode)) - (irc-send-raw (circe-server-process) - (format "MODE %s" mode))) - ((eq major-mode 'circe-channel-mode) - (irc-send-raw (circe-server-process) - (format "MODE %s %s" circe-chat-target mode))) - (t - (circe-display-server-message "Not in a channel buffer.")))) - -(defun circe-command-BANS (&optional ignored) - "Show channel bans" - (if (not circe-chat-target) - (circe-display-server-message "No target for current buffer") - (irc-send-raw (circe-server-process) - (format "MODE %s +b" circe-chat-target)))) - -(defun circe-command-KICK (nick &optional reason) - "Kick WHO from the current channel with optional REASON." - (interactive "sKick who: \nsWhy: ") - (if (not (eq major-mode 'circe-channel-mode)) - (circe-display-server-message "Not in a channel buffer.") - (when (not reason) - (if (string-match "^\\([^ ]*\\) +\\(.+\\)" nick) - (setq reason (match-string 2 nick) - nick (match-string 1 nick)) - (setq reason "-"))) - (irc-send-raw (circe-server-process) - (format "KICK %s %s :%s" - circe-chat-target nick reason)))) - -(defun circe-command-GETOP (&optional ignored) - "Ask chanserv for op on the current channel." - (interactive) - (if (not (eq major-mode 'circe-channel-mode)) - (circe-display-server-message "Not in a channel buffer.") - (irc-send-PRIVMSG (circe-server-process) - "chanserv" - (format "op %s" circe-chat-target)))) - -(defun circe-command-DROPOP (&optional ignored) - "Lose op mode on the current channel." - (interactive) - (if (not (eq major-mode 'circe-channel-mode)) - (circe-display-server-message "Not in a channel buffer.") - (irc-send-raw (circe-server-process) - (format "MODE %s -o %s" - circe-chat-target - (circe-nick))))) - -;; For KICKBAN (requested by Riastradh), we'd need a callback on a -;; USERHOST command. - -(provide 'circe-chanop) -;;; circe-chanop.el ends here diff --git a/elpa/circe-20160608.1315/circe-color-nicks.el b/elpa/circe-20160608.1315/circe-color-nicks.el deleted file mode 100644 index 409af62..0000000 --- a/elpa/circe-20160608.1315/circe-color-nicks.el +++ /dev/null @@ -1,345 +0,0 @@ -;;; circe-color-nicks.el --- Color nicks in the channel - -;; Copyright (C) 2012 Taylan Ulrich Bayırlı/Kammer - -;; Author: Taylan Ulrich Bayırlı/Kammer - -;; This file is part of Circe. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This Circe module adds the ability to assign a color to each -;; nick in a channel. - -;; Some ideas/code copied from rcirc-colors.el. - -;; To use it, put the following into your .emacs: - -;; (require 'circe-color-nicks) -;; (enable-circe-color-nicks) - -;;; Code: - -(require 'circe) -(require 'color) -(require 'cl-lib) - -;;;###autoload -(defun enable-circe-color-nicks () - "Enable the Color Nicks module for Circe. -This module colors all encountered nicks in a cross-server fashion." - (interactive) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (eq major-mode 'circe-channel-mode) - (add-circe-color-nicks)))) - (add-hook 'circe-channel-mode-hook - 'add-circe-color-nicks)) - -(defun disable-circe-color-nicks () - "Disable the Color Nicks module for Circe. -See `enable-circe-color-nicks'." - (interactive) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (eq major-mode 'circe-channel-mode) - (remove-circe-color-nicks)))) - (remove-hook 'circe-channel-mode-hook - 'add-circe-color-nicks)) - -(defun add-circe-color-nicks () - "Add `circe-color-nicks' to `lui-pre-output-hook'." - (add-hook 'lui-pre-output-hook 'circe-color-nicks)) - -(defun remove-circe-color-nicks () - "Remove `circe-color-nicks' from `lui-pre-output-hook'." - (remove-hook 'lui-pre-output-hook 'circe-color-nicks)) - - -(defgroup circe-color-nicks nil - "Nicks colorization for Circe" - :prefix "circe-color-nicks-" - :group 'circe) - -(defcustom circe-color-nicks-min-contrast-ratio 7 - "Minimum contrast ratio from background for generated colors; -recommended is 7:1, or at least 4.5:1 (7 stands for 7:1 here). -Lower value allows higher color spread, but could lead to less -readability." - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-min-difference 17 - "Minimum difference from each other for generated colors." - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-min-fg-difference 17 - "Minimum difference from foreground for generated colors." - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-min-my-message-difference 0 - "Minimum difference from own nick color for generated colors." - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-everywhere nil - "Whether nicks should be colored in message bodies too." - :type 'boolean - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-message-blacklist nil - "Blacklist for nicks that shall never be highlighted inside - images." - :type '(repeat string) - :group 'circe-color-nicks) - -(defcustom circe-color-nicks-pool-type 'adaptive - "Type of the color nick pool. -Must be one of the following: - -'adaptive: Generate colors based on the current theme. - -List of strings: Pick colors from the specified list of hex codes -or color names (see `color-name-rgb-alist')." - :type '(choice (const :tag "Adaptive" adaptive) - (repeat string)) - :group 'circe-color-nicks) - - -;;; See http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G18 - -(defsubst circe-w3-contrast-c-to-l (c) - (if (<= c 0.03928) - (/ c 12.92) - (expt (/ (+ c 0.055) 1.055) 2.4))) - -(defsubst circe-w3-contrast-relative-luminance (rgb) - (apply #'+ - (cl-mapcar (lambda (color coefficient) - (* coefficient - (circe-w3-contrast-c-to-l color))) - rgb - '(0.2126 0.7152 0.0722)))) - -(defsubst circe-w3-contrast-contrast-ratio (color1 color2) - (let ((l1 (+ 0.05 (circe-w3-contrast-relative-luminance color1))) - (l2 (+ 0.05 (circe-w3-contrast-relative-luminance color2)))) - (if (> l1 l2) - (/ l1 l2) - (/ l2 l1)))) - - -(defun circe-color-alist () - "Return list of colors (name rgb lab) where rgb is 0 to 1." - (let ((alist (if (display-graphic-p) - color-name-rgb-alist - (mapcar (lambda (c) - (cons (car c) (cddr c))) - (tty-color-alist)))) - (valmax (float (car (color-values "#ffffff"))))) - (mapcar (lambda (c) - (let* ((name (car c)) - (rgb (mapcar (lambda (v) - (/ v valmax)) - (cdr c))) - (lab (apply #'color-srgb-to-lab rgb))) - (list name rgb lab))) - alist))) - -(defun circe-color-canonicalize-format (color) - "Turns COLOR into (name rgb lab) format. Avoid calling this in -a loop, it's very slow on a tty!" - (let* ((name color) - (rgb (circe-color-name-to-rgb color)) - (lab (apply #'color-srgb-to-lab rgb))) - (list name rgb lab))) - -(defun circe-color-contrast-ratio (color1 color2) - "Gives the contrast ratio between two colors." - (circe-w3-contrast-contrast-ratio (nth 1 color1) (nth 1 color2))) - -(defun circe-color-diff (color1 color2) - "Gives the difference between two colors per CIEDE2000." - (color-cie-de2000 (nth 2 color1) (nth 2 color2))) - -(defun circe-color-name-to-rgb (color) - "Like `color-name-to-rgb' but also handles \"unspecified-bg\" -and \"unspecified-fg\"." - (cond ((equal color "unspecified-bg") '(0 0 0)) - ((equal color "unspecified-fg") '(1 1 1)) - (t (color-name-to-rgb color)))) - - -(defun circe-nick-color-appropriate-p (color bg fg my-msg) - "Tells whether COLOR is appropriate for being a nick color. -BG, FG, and MY-MSG are the background, foreground, and my-message -colors; these are expected as parameters instead of computed here -because computing them repeatedly is a heavy operation." - (and (>= (circe-color-contrast-ratio color bg) - circe-color-nicks-min-contrast-ratio) - (>= (circe-color-diff color fg) - circe-color-nicks-min-fg-difference) - (>= (circe-color-diff color my-msg) - circe-color-nicks-min-my-message-difference))) - -(defun circe-nick-colors-delete-similar (colors) - "Return list COLORS with pairs of colors filtered out that are -too similar per `circe-color-nicks-min-difference'. COLORS may -be mutated." - (cl-mapl (lambda (rest) - (let ((color (car rest))) - (setcdr rest (cl-delete-if - (lambda (c) - (< (circe-color-diff color c) - circe-color-nicks-min-difference)) - (cdr rest))))) - colors) - colors) - -(defun circe-nick-color-generate-pool () - "Return a list of appropriate nick colors." - (if (consp circe-color-nicks-pool-type) - circe-color-nicks-pool-type - (let ((bg (circe-color-canonicalize-format (face-background 'default))) - (fg (circe-color-canonicalize-format (face-foreground 'default))) - (my-msg (circe-color-canonicalize-format - (face-attribute - 'circe-my-message-face :foreground nil 'default)))) - (mapcar #'car (circe-nick-colors-delete-similar - (cl-remove-if-not - (lambda (c) - (circe-nick-color-appropriate-p c bg fg my-msg)) - (circe-color-alist))))))) - -(defun circe-nick-color-pool-test () - "Display all appropriate nick colors in a temp buffer." - (interactive) - (switch-to-buffer (get-buffer-create "*Circe color test*")) - (erase-buffer) - (let ((pool (circe-nick-color-generate-pool))) - (while pool - (let ((pt (point))) - (insert "The quick brown fox jumped over the lazy dog.\n") - (put-text-property pt (point) 'face `(:foreground ,(pop pool))))))) - -(defvar circe-nick-color-pool nil - "Pool of yet unused nick colors.") - -(defvar circe-nick-color-mapping (make-hash-table :test 'equal) - "Hash-table from nicks to colors.") - -(defun circe-nick-color-nick-list () - "Return list of all nicks that have a color assigned to them. -Own and blacklisted nicks are excluded." - (let ((our-nick (circe-nick)) - (channel-nicks (circe-channel-nicks)) - nicks) - (maphash - (lambda (nick color) - (when (and (member nick channel-nicks) - (not (string= our-nick nick)) - (not (member nick circe-color-nicks-message-blacklist))) - (push nick nicks))) - circe-nick-color-mapping) - nicks)) - -(defvar circe-nick-color-timestamps (make-hash-table :test 'equal) - "Hash-table from colors to the timestamp of their last use.") - -(defun circe-nick-color-for-nick (nick) - "Return the color for NICK. Assigns a color to NICK if one -wasn't assigned already." - (let ((color (gethash nick circe-nick-color-mapping))) - (when (not color) - ;; NOTE use this as entry point for taking NICK into account for - ;; picking the new color - (setq color (circe-nick-color-pick)) - (puthash nick color circe-nick-color-mapping)) - (puthash color (float-time) circe-nick-color-timestamps) - color)) - -(defun circe-nick-color-pick () - "Picks either a color from the pool of unused colors, or the -color that was used least recently (i.e. nicks that have it -assigned have been least recently active)." - (if (zerop (hash-table-count circe-nick-color-mapping)) - (setq circe-nick-color-pool (circe-nick-color-generate-pool))) - (or (pop circe-nick-color-pool) - (circe-nick-color-pick-least-recent))) - -(defun circe-nick-color-pick-least-recent () - "Pick the color that was used least recently. -See `circe-nick-color-pick', which is where this is used." - (let ((least-recent-color nil) - (oldest-time (float-time))) - (maphash - (lambda (color time) - (if (< time oldest-time) - (progn - (setq least-recent-color color) - (setq oldest-time time)))) - circe-nick-color-timestamps) - (if least-recent-color - least-recent-color - ;; Someone must have messed with `circe-nick-color-mapping', recover by - ;; re-filling the pool. - (setq circe-nick-color-pool (circe-nick-color-generate-pool)) - (pop circe-nick-color-pool)))) - -(defun circe-color-nicks () - "Color nicks on this lui output line." - (when (eq major-mode 'circe-channel-mode) - (let ((nickstart (text-property-any (point-min) (point-max) - 'lui-format-argument 'nick))) - (when nickstart - (goto-char nickstart) - (let ((nickend (next-single-property-change nickstart - 'lui-format-argument)) - (nick (plist-get (plist-get (text-properties-at nickstart) - 'lui-keywords) - :nick))) - (when (not (circe-server-my-nick-p nick)) - (let ((color (circe-nick-color-for-nick nick))) - (add-face-text-property nickstart nickend - `(:foreground ,color))))))) - (when circe-color-nicks-everywhere - (let ((body (text-property-any (point-min) (point-max) - 'lui-format-argument 'body))) - (when body - (with-syntax-table circe-nick-syntax-table - (goto-char body) - (let* ((nicks (circe-nick-color-nick-list)) - (regex (regexp-opt nicks 'words))) - (let (case-fold-search) - (while (re-search-forward regex nil t) - (let* ((nick (match-string-no-properties 0)) - (color (circe-nick-color-for-nick nick))) - (add-face-text-property (match-beginning 0) (match-end 0) - `(:foreground ,color)))))))))))) - -(defun circe-nick-color-reset () - "Reset the nick color mapping (and some internal data). - -This is useful if you switched between frames supporting -different color ranges and would like nicks to get new colors -appropriate to the new color range." - (interactive) - (setq circe-nick-color-pool (circe-nick-color-generate-pool)) - (setq circe-nick-color-mapping (make-hash-table :test 'equal)) - (setq circe-nick-color-timestamps (make-hash-table :test 'equal))) - -(provide 'circe-color-nicks) -;;; circe-color-nicks.el ends here diff --git a/elpa/circe-20160608.1315/circe-compat.el b/elpa/circe-20160608.1315/circe-compat.el deleted file mode 100644 index f509c66..0000000 --- a/elpa/circe-20160608.1315/circe-compat.el +++ /dev/null @@ -1,53 +0,0 @@ -;;; circe-compat.el --- Compatibility definitions - -;; Copyright (C) 2015 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Define functions and variables as needed by Circe to remain -;; compatible with older Emacsen. - -;;; Code: - -(when (not (fboundp 'string-trim)) - (defun string-trim (string) - "Remove leading and trailing whitespace from STRING." - (if (string-match "\\` *\\(.*[^[:space:]]\\) *\\'" string) - (match-string 1 string) - string))) - -(when (not (fboundp 'add-face-text-property)) - (defun add-face-text-property (start end face &optional append object) - (while (/= start end) - (let* ((next (next-single-property-change start 'face object end)) - (prev (get-text-property start 'face object)) - (value (if (listp prev) prev (list prev)))) - (put-text-property start next 'face - (if append - (append value (list face)) - (append (list face) value)) - object) - (setq start next))))) - -(when (not (boundp 'mode-line-misc-info)) - (defvar mode-line-misc-info nil - "Misc info in the mode line.") - (add-to-list 'mode-line-format 'mode-line-misc-info t)) - -(provide 'circe-compat) -;;; circe-compat.el ends here diff --git a/elpa/circe-20160608.1315/circe-highlight-all-nicks.el b/elpa/circe-20160608.1315/circe-highlight-all-nicks.el deleted file mode 100644 index e06098c..0000000 --- a/elpa/circe-20160608.1315/circe-highlight-all-nicks.el +++ /dev/null @@ -1,100 +0,0 @@ -;;; circe-highlight-all-nicks.el --- Highlight all nicks in the current channel - -;; Copyright (C) 2005 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This file is part of Circe. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This Circe module adds the ability to highlight every occurance of -;; a nick in the current channel in a message by other people. - -;; To use it, put the following into your .emacs: - -;; (require 'circe-highlight-all-nicks) -;; (enable-circe-highlight-all-nicks) - -;;; Code: - -(require 'circe) - -(defface circe-highlight-all-nicks-face - '((t (:foreground "green"))) - "The face used for nicks from the current channel. -See `enable-circe-highlight-all-nicks'." - :group 'circe) - -;;;###autoload -(defun enable-circe-highlight-all-nicks () - "Enable the Highlight Nicks module for Circe. -This module highlights all occurances of nicks in the current -channel in messages of other people." - (interactive) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (eq major-mode 'circe-channel-mode) - (add-circe-highlight-all-nicks)))) - (add-hook 'circe-channel-mode-hook - 'add-circe-highlight-all-nicks)) - -(defun disable-circe-highlight-all-nicks () - "Disable the Highlight Nicks module for Circe. -See `enable-circe-highlight-all-nicks'." - (interactive) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (eq major-mode 'circe-channel-mode) - (remove-circe-highlight-all-nicks)))) - (remove-hook 'circe-channel-mode-hook - 'add-circe-highlight-all-nicks)) - -(defun add-circe-highlight-all-nicks () - "Add `circe-highlight-all-nicks' to `lui-pre-output-hook'." - (add-hook 'lui-pre-output-hook 'circe-highlight-all-nicks - nil t)) - -(defun remove-circe-highlight-all-nicks () - "Remove `circe-highlight-all-nicks' from `lui-pre-output-hook'." - (remove-hook 'lui-pre-output-hook 'circe-highlight-all-nicks - t)) - -(defun circe-highlight-all-nicks () - "Highlight all occurances of nicks of the current channel in the message." - (when (eq major-mode 'circe-channel-mode) - (let ((body (text-property-any (point-min) (point-max) - 'lui-format-argument 'body)) - (nicks '()) - (regex nil)) - (when body - (let ((channel-nicks (circe-channel-nicks))) - (when channel-nicks - (mapc (lambda (nick) - (when (not (circe-server-my-nick-p nick)) - (setq nicks (cons nick nicks)))) - channel-nicks))) - (setq regex (regexp-opt nicks 'words)) - (goto-char body) - (while (re-search-forward regex nil t) - (add-text-properties (match-beginning 0) - (match-end 0) - '(face circe-highlight-all-nicks-face))))))) - -(provide 'circe-highlight-all-nicks) -;;; circe-highlight-all-nicks.el ends here diff --git a/elpa/circe-20160608.1315/circe-lagmon.el b/elpa/circe-20160608.1315/circe-lagmon.el deleted file mode 100644 index 42a3732..0000000 --- a/elpa/circe-20160608.1315/circe-lagmon.el +++ /dev/null @@ -1,243 +0,0 @@ -;;; circe-lagmon.el --- Lag Monitor for Circe - -;; Copyright (C) 2011-2012 Jorgen Schaefer - -;; Author: John J Foerch , -;; Jorgen Schaefer - -;; This file is part of Circe. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -;; 02110-1301, USA. - -;;; Commentary: -;;; -;;; Circe-lagmon-mode monitors the amount of lag on your connection to -;;; each server, and displays the lag time in seconds in the mode-line. -;;; It works by managing two timers. Timer1 sends CTCP LAGMON to yourself -;;; on each server every 60 seconds. Each time around, timer1 starts -;;; timer2 to monitor for timeouts of these messages. Timer2 cancels -;;; itself when all of the pings in the round have been answered. -;;; - -;;; Code: - -(require 'circe) - -;;; User variables - -(defgroup circe-lagmon nil - "Lag Monitor for Circe" - :prefix "circe-lagmon-" - :group 'circe) - -(defcustom circe-lagmon-timer-tick 5 - "How often to check for lag. - -Increase this to improve performance at the cost of accuracy." - :type 'number - :group 'circe-lagmon) - -(defcustom circe-lagmon-check-interval 60 - "Interval in seconds at which to send the CTCP message." - :type 'number - :group 'circe-lagmon) - -(defcustom circe-lagmon-reconnect-interval 120 - "Seconds after which to automatically reconnect upon a timeout -of a lag monitor message. A value of nil disables the feature." - :type '(choice (const :tag "Disable auto-reconnect" nil) - number) - :group 'circe-lagmon) - -(defcustom circe-lagmon-mode-line-format-string "lag:%.1f " - "Format string for displaying the lag in the mode-line." - :type 'string - :group 'circe-lagmon) - -(defcustom circe-lagmon-mode-line-unknown-lag-string "lag:? " - "Indicator string for displaying unknown lag in the mode-line." - :type 'string - :group 'circe-lagmon) - -(defvar circe-lagmon-disabled nil - "A boolean value if lagmon should be disabled on this network. - -Don't set this by hand, use `circe-network-options'.") -(make-variable-buffer-local 'circe-lagmon-disabled) - - -;;; Internal variables -;;; -(defvar circe-lagmon-timer nil) - -(defvar circe-lagmon-server-lag nil) -(make-variable-buffer-local 'circe-lagmon-server-lag) - -(defvar circe-lagmon-last-send-time nil) -(make-variable-buffer-local 'circe-lagmon-last-send-time) - -(defvar circe-lagmon-last-receive-time nil) -(make-variable-buffer-local 'circe-lagmon-last-receive-time) - -(defun circe-lagmon-timer-tick () - "Function run periodically to check lag. - -This will call `circe-lagmon-server-check' in every active server -buffer. You can call it yourself if you like to force an update, -there is no harm in running it too often, but it really should be -run sufficiently often with the timer." - (dolist (buffer (circe-server-buffers)) - (with-current-buffer buffer - (when (and (eq major-mode 'circe-server-mode) - circe-server-process - (eq (irc-connection-state circe-server-process) - 'registered) - (not circe-lagmon-disabled)) - (circe-lagmon-server-check))))) - -(defun circe-lagmon-server-check () - "Check the current server for lag. - -This will reconnect if we haven't heard back for too long, or -send a request if it's time for that. See -`circe-lagmon-reconnect-interval' and -`circe-lagmon-check-interval' to configure the behavior.." - (let ((now (float-time))) - (cond - ;; No answer so far... - ((and circe-lagmon-last-send-time - (not circe-lagmon-last-receive-time)) - ;; Count up until the answer comes. - (let ((lag (/ (- now circe-lagmon-last-send-time) 2))) - (when (or (not circe-lagmon-server-lag) - (> lag circe-lagmon-server-lag)) - (setq circe-lagmon-server-lag lag) - (circe-lagmon-force-mode-line-update))) - ;; Check for timeout. - (when (and circe-lagmon-reconnect-interval - (> now - (+ circe-lagmon-last-send-time - circe-lagmon-reconnect-interval))) - (setq circe-lagmon-last-send-time nil - circe-lagmon-last-receive-time nil) - (circe-reconnect))) - ;; Nothing sent so far, or last send was too long ago. - ((or (not circe-lagmon-last-send-time) - (> now - (+ circe-lagmon-last-send-time - circe-lagmon-check-interval))) - (irc-send-raw (circe-server-process) - (format "PRIVMSG %s :\C-aLAGMON %s\C-a" - (circe-nick) now) - :nowait) - (setq circe-lagmon-last-send-time now - circe-lagmon-last-receive-time nil)) - ))) - -(defun circe-lagmon-force-mode-line-update () - "Call force-mode-line-update on a circe server buffer and all -of its chat buffers." - (force-mode-line-update) - (dolist (b (circe-server-chat-buffers)) - (with-current-buffer b - (force-mode-line-update)))) - -(defun circe-lagmon-format-mode-line-entry () - "Format the mode-line entry for displaying the lag." - (let ((buf (cond - ((eq major-mode 'circe-server-mode) - (current-buffer)) - (circe-server-buffer - circe-server-buffer) - (t - nil)))) - (when buf - (with-current-buffer buf - (cond - (circe-lagmon-disabled - nil) - (circe-lagmon-server-lag - (format circe-lagmon-mode-line-format-string - circe-lagmon-server-lag)) - (t - circe-lagmon-mode-line-unknown-lag-string)))))) - -(defun circe-lagmon-init () - "Initialize the values of the lag monitor for one server, and -start the lag monitor if it has not been started." - (setq circe-lagmon-server-lag nil - circe-lagmon-last-send-time nil - circe-lagmon-last-receive-time nil) - (circe-lagmon-force-mode-line-update) - (unless circe-lagmon-timer - (setq circe-lagmon-timer - (run-at-time nil circe-lagmon-timer-tick - 'circe-lagmon-timer-tick)))) - -(defun circe-lagmon--rpl-welcome-handler (conn &rest ignored) - (with-current-buffer (irc-connection-get conn :server-buffer) - (circe-lagmon-init))) - -(defun circe-lagmon--ctcp-lagmon-handler (conn event sender target argument) - (when (irc-current-nick-p conn (irc-userstring-nick sender)) - (with-current-buffer (irc-connection-get conn :server-buffer) - (let* ((now (float-time)) - (lag (/ (- now (string-to-number argument)) - 2))) - (setq circe-lagmon-server-lag lag - circe-lagmon-last-receive-time now) - (circe-lagmon-force-mode-line-update))))) - -(defun circe-lagmon--nick-handler (conn event sender new-nick) - (when (irc-current-nick-p conn (irc-userstring-nick sender)) - (with-current-buffer (irc-connection-get conn :server-buffer) - (setq circe-lagmon-last-send-time nil)))) - -;;;###autoload -(define-minor-mode circe-lagmon-mode - "Circe-lagmon-mode monitors the amount of lag on your -connection to each server, and displays the lag time in seconds -in the mode-line." - :global t - (let ((mode-line-entry '(:eval (circe-lagmon-format-mode-line-entry)))) - (remove-hook 'mode-line-modes mode-line-entry) - (let ((table (circe-irc-handler-table))) - (irc-handler-remove table "001" 'circe-lagmon--rpl-welcome-handler) - (irc-handler-remove table "irc.ctcp.LAGMON" - 'circe-lagmon--ctcp-lagmon-handler) - (irc-handler-remove table "NICK" 'circe-lagmon--nick-handler)) - (circe-set-display-handler "irc.ctcp.LAGMON" nil) - (when circe-lagmon-timer - (cancel-timer circe-lagmon-timer) - (setq circe-lagmon-timer nil)) - (when circe-lagmon-mode - (add-hook 'mode-line-modes mode-line-entry) - (let ((table (circe-irc-handler-table))) - (irc-handler-add table "001" 'circe-lagmon--rpl-welcome-handler) - (irc-handler-add table "irc.ctcp.LAGMON" - 'circe-lagmon--ctcp-lagmon-handler) - (irc-handler-add table "NICK" 'circe-lagmon--nick-handler)) - (circe-set-display-handler "irc.ctcp.LAGMON" 'circe-display-ignore) - (dolist (buffer (circe-server-buffers)) - (with-current-buffer buffer - (setq circe-lagmon-server-lag nil) - (when (and circe-server-process - (eq (irc-connection-state circe-server-process) - 'registered)) - (circe-lagmon-init))))))) - -(provide 'circe-lagmon) -;;; circe-lagmon.el ends here diff --git a/elpa/circe-20160608.1315/circe-new-day-notifier.el b/elpa/circe-20160608.1315/circe-new-day-notifier.el deleted file mode 100644 index 88d9a4b..0000000 --- a/elpa/circe-20160608.1315/circe-new-day-notifier.el +++ /dev/null @@ -1,86 +0,0 @@ -;;; circe-new-day-notifier.el --- Send a message every midnight to all -;;; channels - -;; Copyright (C) 2015 Pásztor János - -;; Author: Pásztor János - -;; This file is part of Circe. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 2 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This Circe module adds the ability to send a notification to all -;; channels every midnight - -;; Some ideas/code copied from circe-lagmon.el and -;; circe-color-nicks.el - -;; To use it, put the following into your .emacs: - -;; (require 'circe-new-day-notifier) -;; (enable-circe-new-day-notifier) - -;;; Code: - -(require 'circe) - -(defgroup circe-new-day-notifier nil - "Midnight notification to Circe" - :prefix "circe-new-day-notifier-" - :group 'circe) - -(defcustom circe-new-day-notifier-format-message "*** Day changed to {day}" - "The format string which will be printed to the channels. It -should contain {day} to print the date. See `circe-display' for -further documentation" - :type 'string - :group 'circe-new-day-notifier) - -(defcustom circe-new-day-notifier-date-format "%Y-%m-%d, %A" - "The date format, which will be used at -circe-new-day-notifier-format-message. See `format-time-string' for -documentation" - :type 'string - :group 'circe-new-day-notifier) - -(defvar circe-new-day-notifier-timer nil) - -;;;###autoload -(defun enable-circe-new-day-notifier () - (interactive) - (unless circe-new-day-notifier-timer - (setq circe-new-day-notifier-timer - (run-at-time "24:00:00" (* 24 60 60) 'circe-new-day-notification)))) - -;;;###autoload -(defun disable-circe-new-day-notifier () - (interactive) - (when circe-new-day-notifier-timer - (cancel-timer circe-new-day-notifier-timer) - (setq circe-new-day-notifier-timer nil))) - -(defun circe-new-day-notification () - "This function prints the new day notification to each query and chat buffer" - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (derived-mode-p 'circe-chat-mode) - (circe-display 'circe-new-day-notifier-format-message - :day (format-time-string circe-new-day-notifier-date-format)))))) - -(provide 'circe-new-day-notifier) -;;; circe-new-day-notifier.el ends here diff --git a/elpa/circe-20160608.1315/circe-pkg.el b/elpa/circe-20160608.1315/circe-pkg.el deleted file mode 100644 index 8470d8c..0000000 --- a/elpa/circe-20160608.1315/circe-pkg.el +++ /dev/null @@ -1,6 +0,0 @@ -(define-package "circe" "20160608.1315" "Client for IRC in Emacs" - '((cl-lib "0.5")) - :url "https://github.com/jorgenschaefer/circe") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/circe-20160608.1315/circe.el b/elpa/circe-20160608.1315/circe.el deleted file mode 100644 index 53ee461..0000000 --- a/elpa/circe-20160608.1315/circe.el +++ /dev/null @@ -1,3586 +0,0 @@ -;;; circe.el --- Client for IRC in Emacs -*- lexical-binding: t -*- - -;; Copyright (C) 2005 - 2015 Jorgen Schaefer - -;; Version: 2.3 -;; Keywords: IRC, chat -;; Author: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe - -;; This file is part of Circe. - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Circe is a Client for IRC in Emacs. It integrates well with the rest -;; of the editor, using standard Emacs key bindings and indicating -;; activity in channels in the status bar so it stays out of your way -;; unless you want to use it. - -;;; Code: - -(defvar circe-version "2.3" - "Circe version string.") - -(require 'circe-compat) - -(require 'ring) -(require 'timer) -(require 'lui) -(require 'lui-format) -(require 'lcs) -(require 'irc) - -;; Used to be optional. But sorry, we're in the 21st century already. -(require 'lui-irc-colors) - -;; necessary for inheriting from diff-added and diff-removed faces -(require 'diff-mode) - -(defgroup circe nil - "Yet Another Emacs IRC Client." - :prefix "circe-" - :group 'applications) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Customization Options ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;;;;;;;;;;;;;;; -;;;; Faces ;;;; -;;;;;;;;;;;;;;; - -(defface circe-prompt-face - '((t (:weight bold :foreground "Black" :background "LightSeaGreen"))) - "The face for the Circe prompt." - :group 'circe) - -(defface circe-server-face - '((((type tty)) (:foreground "blue" :weight bold)) - (((background dark)) (:foreground "#5095cf")) - (((background light)) (:foreground "#3840b0")) - (t (:foreground "SteelBlue"))) - "The face used to highlight server messages." - :group 'circe) - -(defface circe-highlight-nick-face - '((default (:weight bold)) - (((type tty)) (:foreground "cyan")) - (((background dark)) (:foreground "#82e2ed")) - (((background light)) (:foreground "#0445b7")) - (t (:foreground "CadetBlue3"))) - "The face used to highlight messages directed to us." - :group 'circe) - -(defface circe-my-message-face '((t)) - "The face used to highlight our own messages." - :group 'circe) - -(defface circe-originator-face '((t)) - "The face used to highlight the originator of a message." - :group 'circe) - -(defface circe-topic-diff-new-face '((t (:inherit diff-added))) - "The face used for text added to a topic. -See the {topic-diff} parameter to `circe-format-server-topic'." - :group 'circe) - -(defface circe-topic-diff-removed-face '((t (:inherit diff-removed))) - "The face used for text removed from a topic. -See the {topic-diff} parameter to `circe-format-server-topic'." - :group 'circe) - -(defface circe-fool-face - '((((type tty)) (:foreground "grey40" :bold t)) - (t (:foreground "grey40"))) - "The face used for fools. -See `circe-fool-list'." - :group 'circe) - -;;;;;;;;;;;;;;;;;;; -;;;; Variables ;;;; -;;;;;;;;;;;;;;;;;;; - -(defcustom circe-default-nick (user-login-name) - "The default nick for circe." - :type 'string - :group 'circe) - -(defcustom circe-default-user circe-default-nick - "The default user name for circe." - :type 'string - :group 'circe) - -(defcustom circe-default-realname (if (string= (user-full-name) "") - circe-default-nick - (user-full-name)) - "The default real name for circe." - :type 'string - :group 'circe) - -(defcustom circe-default-ip-family nil - "Default IP family to use. - - 'nil - Use either IPv4 or IPv6. - - 'ipv4 - Use IPv4 - - 'ipv6 - Use IPv6" - :type '(choice (const :tag "Both" nil) - (const :tag "IPv4" ipv4) - (const :tag "IPv6" ipv6)) - :group 'circe) - -(defcustom circe-default-directory "~/" - "The value of `default-directory' for Circe buffers." - :type 'string - :group 'circe) - -(defcustom circe-network-options nil - "Network options. - -This alist maps network names to respective options. - -Common options: - - :pass - The IRC server password to use for this network, or a - function to fetch it. - :nick - The nick name to use (defaults to `circe-default-nick') - :user - The user name to use (defaults to `circe-default-user') - :realname - The real name to use (defaults to `circe-default-realname') - - :channels - A plist of channels to join (see `circe-channels'). - :server-buffer-name - Format to be used for the server buffer name - (see `circe-server-buffer-name') - - :host - The host name of the server to connect to. - :port - The port or service name for the server. - :use-tls - A boolean indicating as to whether to use TLS or - not (defaults to nil). If you set this, you'll likely - have to set :port as well. - :ip-family - Option to enforce a specific IP version - (defaults to `circe-default-ip-family') - - :nickserv-nick - The nick to authenticate with to nickserv, if configured. - (defaults to the value of :nick) - :nickserv-password - The password to use for nickserv - authentication or a function to fetch it. - - :sasl-username - The username for SASL authentication. - :sasl-password - The password for SASL authentication." - :type '(alist :key-type string :value-type plist) - :group 'circe) - -(defvar circe-network-defaults - '(("Freenode" :host "irc.freenode.net" :port (6667 . 6697) - :tls t - :nickserv-mask "^NickServ!NickServ@services\\.$" - :nickserv-identify-challenge "\C-b/msg\\s-NickServ\\s-identify\\s-\C-b" - :nickserv-identify-command "PRIVMSG NickServ :IDENTIFY {nick} {password}" - :nickserv-identify-confirmation "^You are now identified for .*\\.$" - :nickserv-ghost-command "PRIVMSG NickServ :GHOST {nick} {password}" - :nickserv-ghost-confirmation "has been ghosted\\.$\\|is not online\\.$" - ) - ("Coldfront" :host "irc.coldfront.net" :port 6667 - :nickserv-mask "^NickServ!services@coldfront\\.net$" - :nickserv-identify-challenge "/msg\\s-NickServ\\s-IDENTIFY\\s-\C-_password\C-_" - :nickserv-identify-command "PRIVMSG NickServ :IDENTIFY {password}" - ) - ("Bitlbee" :host "localhost" :port 6667 - :nickserv-mask "\\(bitlbee\\|root\\)!\\(bitlbee\\|root\\)@" - :nickserv-identify-challenge "use the \x02identify\x02 command to identify yourself" - :nickserv-identify-command "PRIVMSG &bitlbee :identify {password}" - :nickserv-identify-confirmation "Password accepted, settings and accounts loaded" - :lagmon-disabled t - ) - ("OFTC" :host "irc.oftc.net" :port (6667 . 6697) - :nickserv-mask "^NickServ!services@services\\.oftc\\.net$" - :nickserv-identify-challenge "This nickname is registered and protected." - :nickserv-identify-command "PRIVMSG NickServ :IDENTIFY {password} {nick}" - :nickserv-identify-confirmation "^You are successfully identified as .*\\.$" - ) - ) - "Alist of networks and connection settings. - -See the `circe' command for details of this variable.") - -(defcustom circe-default-quit-message "Using Circe, the loveliest of all IRC clients" - "The default quit message when no other is given. - -This is sent when the server buffer is killed or when /QUIT is -given with no argument." - :type 'string - :group 'circe) - -(defcustom circe-default-part-message "Using Circe, the loveliest of all IRC clients" - "How to part when a channel buffer is killed, or when no -argument is given to /PART." - :type 'string - :group 'circe) - -(defcustom circe-auto-query-max 23 - "The maximum number of queries which are opened automatically. -If more messages arrive - typically in a flood situation - they -are displayed in the server buffer." - :type 'integer - :group 'circe) - -(defcustom circe-use-cycle-completion nil - "Whether Circe should use cycle completion. - -If this is not nil, Circe will set `completion-cycle-threshold' -to t locally in Circe buffers, enabling cycle completion for -nicks no matter what completion style you use in the rest of -Emacs. If you set this to nil, Circe will not touch your default -completion style." - :type 'boolean - :group 'circe) - -(defcustom circe-reduce-lurker-spam nil - "If enabled, Circe will stop showing some messages. - -This means that JOIN, PART, QUIT and NICK messages are not shown -for users on channels that have not spoken yet (\"lurker\"), or -haven't spoken in `circe-active-users-timeout' seconds. When they -speak for the first time, Circe displays their join time." - :type 'boolean - :group 'circe) - -(defcustom circe-active-users-timeout nil - "When non-nil, should be the number of seconds after which -active users are regarded as inactive again after speaking." - :type 'integer - :group 'circe) - -(defcustom circe-prompt-string (concat (propertize ">" - 'face 'circe-prompt-face) - " ") - "The string to initialize the prompt with. -To change the prompt dynamically or just in specific buffers, use -`lui-set-prompt' in the appropriate hooks." - :type 'string - :group 'circe) - -(defcustom circe-extra-nicks nil - "List of other nicks than your current one to highlight." - :type '(repeat string) - :group 'circe) - -(defcustom circe-highlight-nick-type 'sender - "How to highlight occurrences of our own nick. - - 'sender - Highlight the nick of the sender - (messages without a sender and your - own are highlighted with the occurrence - type instead) - 'occurrence - Highlight the occurrences of the nick - 'message - Highlight the message without the sender - 'all - Highlight the whole line" - :type '(choice (const :tag "Sender" sender) - (const :tag "Occurrences" occurrence) - (const :tag "Message" message) - (const :tag "Whole line" all)) - :group 'circe) - -(defcustom circe-inhibit-nick-highlight-function nil - "Function for inhibiting nick highlighting. -If non-nil, its value is called with the respective buffer -selected and point in the line that's about to get highlighted. -A non-nil return value inhibits any highlighting." - :type '(choice (const :tag "None" nil) - function) - :group 'circe) - -(defcustom circe-completion-suffix ": " - "A suffix for completed nicks at the beginning of a line." - :type '(choice (const :tag "The standard suffix" ": ")) - :group 'circe) - -(defcustom circe-ignore-list nil - "List of regular expressions to ignore. - -Each regular expression is matched against nick!user@host." - :type '(repeat regexp) - :group 'circe) - -(defcustom circe-fool-list nil - "List of regular expressions for fools. - -Each regular expression is matched against nick!user@host. - -Messages from such people are still inserted, but not shown. They -can be displayed using \\[lui-toggle-ignored]." - :type '(repeat regexp) - :group 'circe) - -(defcustom circe-ignore-functions nil - "A list of functions to check whether we should ignore a message. - -These functions get three arguments: NICK, USERHOST, and BODY. If -one of them returns a non-nil value, the message is ignored." - :type 'hook - :group 'circe) - -(defcustom circe-split-line-length 440 - "The maximum length of a single message. -If a message exceeds this size, it is broken into multiple ones. - -IRC allows for lines up to 512 bytes. Two of them are CR LF. -And a typical message looks like this: - - :nicky!uhuser@host212223.dialin.fnordisp.net PRIVMSG #lazybastards :Hello! - -You can limit here the maximum length of the \"Hello!\" part. -Good luck." - :type 'integer - :group 'circe) - -(defcustom circe-server-max-reconnect-attempts 5 - "How often Circe should attempt to reconnect to the server. -If this is 0, Circe will not reconnect at all. If this is nil, -it will try to reconnect forever (not recommended)." - :type 'integer - :group 'circe) - -(defcustom circe-netsplit-delay 60 - "The number of seconds a netsplit may be dormant. -If anything happens with a netsplit after this amount of time, -the user is re-notified." - :type 'number - :group 'circe) - -(defcustom circe-server-killed-confirmation 'ask-and-kill-all - "How to ask for confirmation when a server buffer is killed. -This can be one of the following values: - ask - Ask the user for confirmation - ask-and-kill-all - Ask the user, and kill all associated buffers - nil - Kill first, ask never" - :type '(choice (const :tag "Ask before killing" ask) - (const :tag "Ask, then kill all associated buffers" - ask-and-kill-all) - (const :tag "Don't ask" nil)) - :group 'circe) - -(defcustom circe-channel-killed-confirmation 'ask - "How to ask for confirmation when a channel buffer is killed. -This can be one of the following values: - ask - Ask the user for confirmation - nil - Don't ask, just kill" - :type '(choice (const :tag "Ask before killing" ask) - (const :tag "Don't ask" nil)) - :group 'circe) - -(defcustom circe-track-faces-priorities '(circe-highlight-nick-face - lui-highlight-face - circe-my-message-face - circe-server-face) - "A list of faces which should show up in the tracking. -The first face is kept if the new message has only lower faces, -or faces that don't show up at all." - :type '(repeat face) - :group 'circe) - -(defcustom circe-server-send-unknown-command-p nil - "Non-nil when Circe should just pass on commands it doesn't know. -E.g. /fnord foo bar would then just send \"fnord foo bar\" to the -server." - :type 'boolean - :group 'circe) - -(defcustom circe-server-connected-hook nil - "Hook run when we successfully connected to a server. -This is run from a 001 (RPL_WELCOME) message handler." - :type 'hook - :group 'circe) - -(defcustom circe-server-auto-join-default-type :immediate - "The default auto-join type to use. - -Possible options: - -:immediate - Immediately after registering on the server -:after-auth - After nickserv authentication succeeded -:after-cloak - After we have acquired a cloaked host name -:after-nick - After we regained our preferred nick, or after - nickserv authentication if we don't need to regain - it. See `circe-nickserv-ghost-style'. - -See `circe-channels' for more details." - :type '(choice (const :tag "Immediately" :immediate) - (const :tag "After Authentication" :after-auth) - (const :tag "After Cloaking" :after-cloak) - (const :tag "After Nick Regain" :after-nick)) - :group 'circe) - -;;;;;;;;;;;;;;;;; -;;;; Formats ;;;; -;;;;;;;;;;;;;;;;; - -(defgroup circe-format nil - "Format strings for Circe. -All these formats always allow the {mynick} and {chattarget} format -strings." - :prefix "circe-format-" - :group 'circe) - -(defcustom circe-format-not-tracked - '(circe-format-server-message - circe-format-server-notice - circe--irc-format-server-numeric - circe-format-server-topic - circe-format-server-rejoin - circe-format-server-lurker-activity - circe-format-server-topic-time - circe-format-server-topic-time-for-channel - circe-format-server-netmerge - circe-format-server-join - circe-format-server-join-in-channel - circe-format-server-mode-change - circe-format-server-nick-change-self - circe-format-server-nick-change - circe-format-server-nick-regain - circe-format-server-part - circe-format-server-netsplit - circe-format-server-quit-channel - circe-format-server-quit) - "A list of formats that should not trigger tracking." - :type '(repeat symbol) - :group 'circe-format) - -(defcustom circe-format-server-message "*** {body}" - "The format for generic server messages. -{body} - The body of the message." - :type 'string - :group 'circe-format) - -(defcustom circe-format-self-say "> {body}" - "The format for messages to queries or channels. -{nick} - Your nick. -{body} - The body of the message." - :type 'string - :group 'circe-format) - -(defcustom circe-format-self-action "* {nick} {body}" - "The format for actions to queries or channels. -{nick} - Your nick. -{body} - The body of the action." - :type 'string - :group 'circe-format) - -(defcustom circe-format-self-message "-> *{chattarget}* {body}" - "The format for messages sent to other people outside of queries. -{chattarget} - The target nick. -{body} - The body of the message." - :type 'string - :group 'circe-format) - -(defcustom circe-format-action "* {nick} {body}" - "The format for actions in queries or channels. -{nick} - The nick doing the action. -{body} - The body of the action." - :type 'string - :group 'circe-format) - -(defcustom circe-format-message-action "* *{nick}* {body}" - "The format for actions in messages outside of queries. -{nick} - The nick doing the action. -{body} - The body of the action." - :type 'string - :group 'circe-format) - -(defcustom circe-format-say "<{nick}> {body}" - "The format for normal channel or query talk. -{nick} - The nick talking. -{body} - The message." - :type 'string - :group 'circe-format) - -(defcustom circe-format-message "*{nick}* {body}" - "The format for a message outside of a query. -{nick} - The originator. -{body} - The message." - :type 'string - :group 'circe-format) - -(defcustom circe-format-notice "-{nick}- {body}" - "The format for a notice. -{nick} - The originator. -{body} - The notice." - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-notice "-Server Notice- {body}" - "The format for a server notice. -{body} - The notice." - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-topic "*** Topic change by {nick} ({userhost}): {new-topic}" - "The format for topic changes. - -The following format arguments are available: - - nick - The nick of the user who changed the topic - userhost - The user@host string of that user - channel - Where the topic change happened - new-topic - The new topic - old-topic - The previous topic - topic-diff - A colorized diff of the topics" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-lurker-activity - "*** First activity: {nick} joined {joindelta} ago." - "The format for the first-activity notice of a user. -{nick} - The originator. -{jointime} - The join time of the user (in seconds). -{joindelta} - The duration from joining until now." - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-rejoin - "*** Re-join: {nick} ({userinfo}), left {departuredelta} ago" - "The format for the re-join notice of a user. - -The following format arguments are available: - - nick - The nick of the user who joined - userhost - The user@host string of the user who joined - accountname - The account name, if the server supports this - realname - The real name, if the server supports this - userinfo - A combination of userhost, accountname, and realname - channel - A date string describing this time - departuretime - Time in seconds when the originator had left. - departuredelta - Description of the time delta since the originator left." - :type 'string - :group 'circe-format) - -(defcustom circe-server-buffer-name "{host}:{port}" - "The format for the server buffer name. - -The following format arguments are available: - - network - The name of the network - host - The host name of the server - port - The port number or service name - service - Alias for port" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-whois-idle-with-signon "*** {whois-nick} is {idle-duration} idle (signon on {signon-date}, {signon-ago} ago)" - "Format for RPL_WHOISIDLE messages. - -The following format arguments are available: - - whois-nick - The nick this is about - idle-seconds - The number of seconds this nick has been idle - idle-duration - A textual description of the duration of the idle time - signon-time - The time (in seconds since the epoch) when this user - signed on - signon-date - A date string describing this time - signon-ago - A textual description of the duraction since signon" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-whois-idle "*** {whois-nick} is {idle-duration} idle" - "Format for RPL_WHOISIDLE messages. - -The following format arguments are available: - - whois-nick - The nick this is about - idle-seconds - The number of seconds this nick has been idle - idle-duration - A textual description of the duration of the idle time" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-topic-time "*** Topic set by {setter} on {topic-date}, {topic-ago} ago" - "Format for RPL_TOPICWHOTIME messages for the current channel. - -The following format arguments are available: - - channel - The channel the topic is for - setter - The nick of the person who set the topic - setter-userhost - The user@host string of the person who set the topic - topic-time - The time the topic was set, in seconds since the epoch - topic-date - A date string describing this time - topic-ago - A textual description of the duration since the topic - was set" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-topic-time-for-channel "*** Topic for {channel} set by {setter} on {topic-date}, {topic-ago} ago" - "Format for RPL_TOPICWHOTIME messages for a channel we are not on. - -The following format arguments are available: - - channel - The channel the topic is for - setter - The nick of the person who set the topic - setter-userhost - The user@host string of the person who set the topic - topic-time - The time the topic was set, in seconds since the epoch - topic-date - A date string describing this time - topic-ago - A textual description of the duration since the topic - was set" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-channel-creation-time "*** Channel {channel} created on {date}, {ago} ago" - "Format for RPL_CREATIONTIME messages for the current channel. - -The following format arguments are available: - - channel - The channel the topic is for - date - A date string describing this time - ago - A textual description of the duration since the channel - was created" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-ctcp-ping "*** CTCP PING request from {nick} ({userhost}) to {target}: {body} ({ago} ago)" - "Format for CTCP PING requests. - -The following format arguments are available: - - nick - The nick of the user who sent this PING request - userhost - The user@host string of the user who sent this request - target - The target of the message, usually us, but can be a channel - body - The argument of the PING request, usually a number - ago - A textual description of the duration since the request - was sent, if parseable" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-ctcp-ping-reply "*** CTCP PING reply from {nick} ({userhost}) to {target}: {ago} ago ({body})" - "Format for CTCP PING replies. - -The following format arguments are available: - - nick - The nick of the user who sent this PING request - userhost - The user@host string of the user who sent this request - target - The target of the message, usually us, but can be a channel - body - The argument of the PING request, usually a number - ago - A textual description of the duration since the request - was sent, if parseable" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-ctcp "*** CTCP {command} request from {nick} ({userhost}) to {target}: {body}" - "Format for CTCP requests. - -The following format arguments are available: - - nick - The nick of the user who sent this PING request - userhost - The user@host string of the user who sent this request - target - The target of the message, usually us, but can be a channel - command - The CTCP command used - body - The argument of the PING request, usually a number" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-netsplit "*** Netsplit: {split} (Use /WL to see who left)" - "Format for netsplit notifications. - -The following format arguments are available: - - split - The name of the split, usually describing the servers involved" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-netmerge "*** Netmerge: {split}, split {ago} ago (Use /WL to see who's still missing)" - "Format for netmerge notifications. - -The following format arguments are available: - - split - The name of the split, usually describing the servers involved - time - The time when this split happened, in seconds - date - A date string describing this time - ago - A textual description of the duration since the split happened" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-join "*** Join: {nick} ({userinfo})" - "Format for join messages in a channel buffer. - -The following format arguments are available: - - nick - The nick of the user joining - userhost - The user@host string for the user - accountname - The account name, if the server supports this - realname - The real name, if the server supports this - userinfo - A combination of userhost, accountname, and realname - channel - The channel this user is joining" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-join-in-channel "*** Join: {nick} ({userinfo}) joined {channel}" - "Format for join messages in query buffers of the joining user. - -The following format arguments are available: - - nick - The nick of the user joining - userhost - The user@host string for the user - accountname - The account name, if the server supports this - realname - The real name, if the server supports this - userinfo - A combination of userhost, accountname, and realname - channel - The channel this user is joining" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-mode-change "*** Mode change: {change} on {target} by {setter} ({userhost})" - "Format for mode changes. - -The following format arguments are available: - - setter - The name of the split, usually describing the servers involved - userhost - The user@host string for the user - target - The target of this mode change - change - The actual changed modes" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-nick-change-self "*** Nick change: You are now known as {new-nick}" - "Format for nick changes of the current user. - -The following format arguments are available: - - old-nick - The old nick this change was from - new-nick - The new nick this change was to - userhost - The user@host string for the user" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-nick-change "*** Nick change: {old-nick} ({userhost}) is now known as {new-nick}" - "Format for nick changes of the current user. - -The following format arguments are available: - - old-nick - The old nick this change was from - new-nick - The new nick this change was to - userhost - The user@host string for the user" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-nick-regain "*** Nick regain: {old-nick} ({userhost}) is now known as {new-nick}" - "Format for nick changes of the current user. - -The following format arguments are available: - - old-nick - The old nick this change was from - new-nick - The new nick this change was to - userhost - The user@host string for the user" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-part "*** Part: {nick} ({userhost}) left {channel}: {reason}" - "Format for users parting a channel. - -The following format arguments are available: - - nick - The nick of the user who left - userhost - The user@host string for this user - channel - The channel they left - reason - The reason they gave for leaving" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-quit-channel "*** Quit: {nick} ({userhost}) left {channel}: {reason}" - "Format for users quitting from a channel. - -The following format arguments are available: - - nick - The nick of the user who left - userhost - The user@host string for this user - channel - The channel they left - reason - The reason they gave for leaving" - :type 'string - :group 'circe-format) - -(defcustom circe-format-server-quit "*** Quit: {nick} ({userhost}) left IRC: {reason}" - "Format for users quitting. - -The following format arguments are available: - - nick - The nick of the user who left - userhost - The user@host string for this user - reason - The reason they gave for leaving" - :type 'string - :group 'circe-format) - -;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Private variables ;;; -;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar circe-source-url "https://github.com/jorgenschaefer/circe" - "URL to Circe's source repository") - -(defvar circe-host nil - "The name of the server we're currently connected to.") -(make-variable-buffer-local 'circe-host) - -(defvar circe-port nil - "The port number or service name of the server.") -(make-variable-buffer-local 'circe-host) - -(defvar circe-network nil - "The network name of the server we're currently connected to.") -(make-variable-buffer-local 'circe-network) - -(defvar circe-ip-family nil - "The IP family in use. -See `make-network-process' and :family for valid values.") -(make-variable-buffer-local 'circe-ip-family) - -(defvar circe-nick nil - "Our current nick.") -(make-variable-buffer-local 'circe-nick) - -(defvar circe-user nil - "The current user name.") -(make-variable-buffer-local 'circe-user) - -(defvar circe-realname nil - "The current real name.") -(make-variable-buffer-local 'circe-realname) - -(defvar circe-pass nil - "The password for the current server or a function to recall it. - -If a function is set it will be called with the value of `circe-host'.") -(make-variable-buffer-local 'circe-pass) - -(defvar circe-sasl-username nil - "The username for SASL authentication.") -(make-variable-buffer-local 'circe-sasl-username) - -(defvar circe-sasl-password nil - "The password for SASL authentication. - -If a function is set it will be called with the value of -`circe-host'.") -(make-variable-buffer-local 'circe-sasl-password) - -(defvar circe-use-tls nil - "If non-nil, use `open-tls-stream' to connect to the server.") -(make-variable-buffer-local 'circe-use-tls) - -(defvar circe-server-process nil - "The process of the server connection.") -(make-variable-buffer-local 'circe-server-process) - -(defvar circe-server-last-active-buffer nil - "The last active circe buffer.") -(make-variable-buffer-local 'circe-server-last-active-buffer) - -(defvar circe-display-table nil - "A hash table mapping commands to their display functions.") - -(defvar circe-server-inhibit-auto-reconnect-p nil - "Non-nil when Circe should not reconnect. - -This can be set from commands to avoid reconnecting when the -server disconnects.") -(make-variable-buffer-local 'circe-server-inhibit-auto-reconnect-p) - -(defvar circe-chat-calling-server-buffer-and-target nil - "Internal variable to pass the server buffer and target to chat modes.") - -(defvar circe-chat-target nil - "The current target for the buffer. -This is either a channel or a nick name.") -(make-variable-buffer-local 'circe-chat-target) - -(defvar circe-nick-syntax-table - (let ((table (make-syntax-table text-mode-syntax-table)) - (special (string-to-list "[]\`_^{}|-"))) - (dolist (char special) - (modify-syntax-entry char "w" table)) - table) - "Syntax table to treat nicks as words. -This is not entirely accurate, as exact chars constituting a nick -can vary between networks.") - -(defvar circe-nickserv-mask nil - "The regular expression to identify the nickserv on this network. - -Matched against nick!user@host.") -(make-variable-buffer-local 'circe-nickserv-mask) - -(defvar circe-nickserv-identify-challenge nil - "A regular expression matching the nickserv challenge to identify.") -(make-variable-buffer-local 'circe-nickserv-identify-challenge) - -(defvar circe-nickserv-identify-command nil - "The IRC command to send to identify with nickserv. - -This must be a full IRC command. It accepts the following -formatting options: - - {nick} - The nick to identify as - {password} - The configured nickserv password") -(make-variable-buffer-local 'circe-nickserv-identify-command) - -(defvar circe-nickserv-identify-confirmation nil - "A regular expression matching a confirmation of authentication.") -(make-variable-buffer-local 'circe-nickserv-identify-confirmation) - -(defvar circe-nickserv-ghost-command nil - "The IRC command to send to regain/ghost your nick. - -This must be a full IRC command. It accepts the following -formatting options: - - {nick} - The nick to ghost - {password} - The configured nickserv password") -(make-variable-buffer-local 'circe-nickserv-ghost-command) - -(defvar circe-nickserv-ghost-confirmation nil - "A regular expression matching a confirmation for the GHOST command. - -This is used to know when we can set our nick to the regained one -Leave nil if regaining automatically sets your nick") -(make-variable-buffer-local 'circe-nickserv-ghost-confirmation) - -(defvar circe-nickserv-nick nil - "The nick we are registered with for nickserv. - -Do not set this variable directly. Use `circe-network-options' or -pass an argument to the `circe' function for this.") -(make-variable-buffer-local 'circe-nickserv-nick) - -(defvar circe-nickserv-password nil - "The password we use for nickserv on this network. - -Can be either a string or a unary function of the nick returning -a string. - -Do not set this variable directly. Use `circe-network-options' or -pass an argument to the `circe' function for this.") -(make-variable-buffer-local 'circe-nickserv-password) - -(defvar circe-channels nil - "The default channels to join on this server. - -Don't set this variable by hand, use `circe-network-options'. - -The value should be a list of channels to join, with optional -keywords to configure the behavior of the following channels. - -Best explained in an example: - -\(\"#emacs\" :after-auth \"#channel\" \"#channel2\") - -Possible keyword options are: - -:immediate - Immediately after registering on the server -:after-auth - After nickserv authentication succeeded -:after-cloak - After we have acquired a cloaked host name -:after-nick - After we regained our preferred nick, or after - nickserv authentication if we don't need to regain - it. See `circe-nickserv-ghost-style'. - -The default is set in `circe-server-auto-join-default-type'. - -A keyword in the first position of the channels list overrides -`circe-server-auto-join-default-type' for re-joining manually -joined channels.") -(make-variable-buffer-local 'circe-channels) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; Server Buffer Management ;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Every Circe buffer has an associated server buffer (which might be -;; the buffer itself). Circe buffers should set the -;; `circe-server-buffer' variable to the associated server buffer. - -(defun circe-server-buffer () - "Return the server buffer for the current buffer." - (let ((buf (if (eq major-mode 'circe-server-mode) - (current-buffer) - circe-server-buffer))) - (cond - ((not buf) - (error "Not in a Circe buffer")) - ((not (buffer-live-p buf)) - (error "The server buffer died, functionality is limited")) - (t - buf)))) - -(defmacro with-circe-server-buffer (&rest body) - "Run BODY with the current buffer being the current server buffer." - (declare (indent 0)) - `(with-current-buffer (circe-server-buffer) - ,@body)) - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Editor Commands ;;; -;;;;;;;;;;;;;;;;;;;;;;; - -;;;###autoload -(defun circe-version () - "Display Circe's version." - (interactive) - (message "Circe %s" (circe--version))) - -(defun circe--version () - "Return Circe's version" - (let ((circe-git-version (circe--git-version))) - (if circe-git-version - (format "%s-%s" circe-version circe-git-version) - (format "%s" circe-version)))) - -(defun circe--git-version () - (let* ((current-file-path (or load-file-name buffer-file-name - (locate-library "circe.el"))) - (vcs-path (locate-dominating-file current-file-path ".git"))) - (when vcs-path - (let ((default-directory vcs-path)) - ;; chop off the trailing newline - (substring (shell-command-to-string "git rev-parse --short HEAD") - 0 -1))))) - -;;;###autoload -(defun circe (network-or-server &rest server-options) - "Connect to IRC. - -Connect to the given network specified by NETWORK-OR-SERVER. - -When this function is called, it collects options from the -SERVER-OPTIONS argument, the user variable -`circe-network-options', and the defaults found in -`circe-network-defaults', in this order. - -If NETWORK-OR-SERVER is not found in any of these variables, the -argument is assumed to be the host name for the server, and all -relevant settings must be passed via SERVER-OPTIONS. - -All SERVER-OPTIONS are treated as variables by getting the string -\"circe-\" prepended to their name. This variable is then set -locally in the server buffer. - -See `circe-network-options' for a list of common options." - (interactive (circe--read-network-and-options)) - (let* ((options (circe--server-get-network-options network-or-server - server-options)) - (buffer (circe--server-generate-buffer options))) - (with-current-buffer buffer - (circe-server-mode) - (circe--server-set-variables options) - (circe-reconnect)) - (pop-to-buffer-same-window buffer))) - -(defun circe--read-network-and-options () - "Read a host or network name with completion. - -If it's not a network, also read some extra options. - -This uses `circe-network-defaults' and `circe-network-options' for -network names." - (let ((default-network (if (null circe-network-options) - (caar circe-network-defaults) - (caar circe-network-options))) - (networks nil) - (completion-ignore-case t) - network-or-host) - (dolist (network-spec (append circe-network-options - circe-network-defaults)) - (when (not (member (car network-spec) networks)) - (push (car network-spec) networks))) - (setq networks (sort networks 'string-lessp)) - (setq network-or-host (completing-read "Network or host: " - networks - nil nil nil nil - default-network)) - (dolist (network-name networks) - (when (equal (downcase network-or-host) - (downcase network-name)) - (setq network-or-host network-name))) - (if (member network-or-host networks) - (list network-or-host) - (list network-or-host - :host network-or-host - :port (read-number "Port: " 6667))))) - -(defun circe--server-get-network-options (network server-options) - "Combine server and network options with network defaults. - -See `circe-network-options' and `circe-network-defaults'." - (let ((options (mapcar 'circe--translate-option-names - (append server-options - (cdr (assoc network circe-network-options)) - (cdr (assoc network circe-network-defaults)) - (list :network network))))) - (when (not (plist-get options :host)) - (plist-put options :host network)) - (let ((port (plist-get options :port)) - (use-tls (plist-get options :use-tls))) - (when (consp port) - (if use-tls - (plist-put options :port (cdr port)) - (plist-put options :port (car port))))) - (dolist (required-option '(:host :port)) - (when (not (plist-get options required-option)) - (error (format "Network option %s not specified" required-option)))) - options)) - -(defun circe--translate-option-names (option) - "Translate option names to make them unique. - -Some options have multiple names, mainly for historical reasons. -Unify them here." - (cond - ((eq option :service) :port) - ((eq option :tls) :use-tls) - ((eq option :family) :ip-family) - (t option))) - -(defun circe--server-generate-buffer (options) - "Return the server buffer for the connection described in OPTIONS." - (let* ((network (plist-get options :network)) - (host (plist-get options :host)) - (port (plist-get options :port)) - (buffer-name (lui-format (or (plist-get options :server-buffer-name) - circe-server-buffer-name) - :network network - :host host - :port port - :service port))) - (generate-new-buffer buffer-name))) - -(defun circe--server-set-variables (options) - "Set buffer-local variables described in OPTIONS. - -OPTIONS is a plist as passed to `circe'. All options therein are -set as buffer-local variables. Only the first occurrence of each -variable is set." - (setq circe-nick circe-default-nick - circe-user circe-default-user - circe-realname circe-default-realname - circe-ip-family circe-default-ip-family) - (let ((done nil) - (todo options)) - (while todo - (when (not (memq (car todo) done)) - (push (car todo) done) - (let ((var (intern (format "circe-%s" - (substring (symbol-name (car todo)) 1)))) - (val (cadr todo))) - (if (boundp var) - (set (make-local-variable var) val) - (warn "Unknown option %s, ignored" (car todo))))) - (setq todo (cddr todo))))) - -(defvar circe-server-reconnect-attempts 0 - "The number of reconnect attempts that Circe has done so far. -See `circe-server-max-reconnect-attempts'.") -(make-variable-buffer-local 'circe-server-reconnect-attempts) - -(defun circe-reconnect () - "Reconnect the current server." - (interactive) - (with-circe-server-buffer - (when (or (called-interactively-p 'any) - (circe--reconnect-p)) - (setq circe-server-inhibit-auto-reconnect-p t - circe-server-reconnect-attempts (+ circe-server-reconnect-attempts - 1)) - (unwind-protect - (circe-reconnect--internal) - (setq circe-server-inhibit-auto-reconnect-p nil))))) - -(defun circe--reconnect-p () - (cond - (circe-server-inhibit-auto-reconnect-p - nil) - ((not circe-server-max-reconnect-attempts) - t) - ((<= circe-server-reconnect-attempts - circe-server-max-reconnect-attempts) - t) - (t - nil))) - -(defun circe-reconnect--internal () - "The internal function called for reconnecting unconditionally. - -Do not use this directly, use `circe-reconnect'" - (when (and circe-server-process - (process-live-p circe-server-process)) - (delete-process circe-server-process)) - (circe-display-server-message "Connecting...") - (dolist (buf (circe-server-chat-buffers)) - (with-current-buffer buf - (circe-display-server-message "Connecting..."))) - (setq circe-server-process - (irc-connect - :host circe-host - :service circe-port - :tls circe-use-tls - :ip-family circe-ip-family - :handler-table (circe-irc-handler-table) - :server-buffer (current-buffer) - :nick circe-nick - :nick-alternatives (list (circe--nick-next circe-nick) - (circe--nick-next - (circe--nick-next circe-nick))) - :user circe-user - :mode 8 - :realname circe-realname - :pass (if (functionp circe-pass) - (funcall circe-pass circe-host) - circe-pass) - :cap-req (append (when (and circe-sasl-username - circe-sasl-password) - '("sasl")) - '("extended-join")) - :nickserv-nick (or circe-nickserv-nick - circe-nick) - :nickserv-password (if (functionp circe-nickserv-password) - (funcall circe-nickserv-password circe-host) - circe-nickserv-password) - :nickserv-mask circe-nickserv-mask - :nickserv-identify-challenge circe-nickserv-identify-challenge - :nickserv-identify-command circe-nickserv-identify-command - :nickserv-identify-confirmation - circe-nickserv-identify-confirmation - :nickserv-ghost-command circe-nickserv-ghost-command - :nickserv-ghost-confirmation circe-nickserv-ghost-confirmation - :sasl-username circe-sasl-username - :sasl-password (if (functionp circe-sasl-password) - (funcall circe-sasl-password - circe-host) - circe-sasl-password) - :ctcp-version (format "Circe: Client for IRC in Emacs, version %s" - circe-version) - :ctcp-source circe-source-url - :ctcp-clientinfo "CLIENTINFO PING SOURCE TIME VERSION" - :auto-join-after-registration - (append (circe--auto-join-channel-buffers) - (circe--auto-join-list :immediate)) - :auto-join-after-host-hiding - (circe--auto-join-list :after-cloak) - :auto-join-after-nick-acquisition - (circe--auto-join-list :after-nick) - :auto-join-after-nickserv-identification - (circe--auto-join-list :after-auth) - :auto-join-after-sasl-login - (circe--auto-join-list :after-auth)))) - -(defun circe-reconnect-all () - "Reconnect all Circe connections." - (interactive) - (dolist (buf (circe-server-buffers)) - (with-current-buffer buf - (if (called-interactively-p 'any) - (call-interactively 'circe-reconnect) - (circe-reconnect))))) - -(defun circe--auto-join-list (type) - "Return the list of channels to join for type TYPE." - (let ((result nil) - (current-type circe-server-auto-join-default-type)) - (dolist (channel circe-channels) - (cond - ((keywordp channel) - (setq current-type channel)) - ((eq current-type type) - (push channel result)))) - (nreverse result))) - -(defun circe--auto-join-channel-buffers () - "Return a list of channels to join based on channel buffers. - -This includes all channel buffers of the current server, but -excludes and channel that is already listed in -`circe-channels'." - (let ((channels nil)) - (dolist (buf (circe-server-chat-buffers)) - (let ((name (with-current-buffer buf - (when (derived-mode-p 'circe-channel-mode) - circe-chat-target)))) - (when (and name - (not (member name circe-channels))) - (push name channels)))) - channels)) - -;;;;;;;;;;;;;;;;; -;;; Base Mode ;;; -;;;;;;;;;;;;;;;;; - -(defvar circe-mode-hook nil - "Hook run for any Circe mode.") - -(defvar circe-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-j") 'circe-command-JOIN) - (define-key map (kbd "C-c C-r") 'circe-reconnect) - map) - "The base keymap for all Circe modes (server, channel, query)") - -(defvar circe-server-buffer nil - "The buffer of the server associated with the current chat buffer.") -(make-variable-buffer-local 'circe-server-buffer) - -(define-derived-mode circe-mode lui-mode "Circe" - "Base mode for all Circe buffers. - -A buffer should never be in this mode directly, but rather in -modes that derive from this. - -The mode inheritance hierarchy looks like this: - -lui-mode -`-circe-mode - `-circe-server-mode - `-circe-chat-mode - `-circe-channel-mode - `-circe-query-mode" - (add-hook 'lui-pre-output-hook 'lui-irc-colors - t t) - (add-hook 'lui-pre-output-hook 'circe--output-highlight-nick - t t) - (add-hook 'completion-at-point-functions 'circe--completion-at-point - nil t) - (lui-set-prompt circe-prompt-string) - (goto-char (point-max)) - (setq lui-input-function 'circe--input - default-directory (expand-file-name circe-default-directory) - circe-server-last-active-buffer (current-buffer) - flyspell-generic-check-word-p 'circe--flyspell-check-word-p) - (when circe-use-cycle-completion - (set (make-local-variable 'completion-cycle-threshold) - t)) - ;; Tab completion should be case-insensitive - (set (make-local-variable 'completion-ignore-case) - t) - (set (make-local-variable 'tracking-faces-priorities) - circe-track-faces-priorities)) - -;;;;;;;;;;;;;;;;;;;; -;;;; Displaying ;;;; -;;;;;;;;;;;;;;;;;;;; - -(defun circe-display (format &rest keywords) - "Display FORMAT formatted with KEYWORDS in the current Circe buffer. -See `lui-format' for a description of the format. - -If FORMAT contains the word server, the resulting string receives -a `circe-server-face'. If FORMAT contains the word self, the -whole string receives a `circe-my-message-face'. If FORMAT is in -`circe-format-not-tracked', a message of this type is never -tracked by Lui. - -Keywords with the name :nick receive a `circe-originator-face'. - -It is always possible to use the mynick or target formats." - (when (not (circe--display-ignored-p format keywords)) - (let* ((name (symbol-name format)) - (face (cond - ((string-match "\\" name) - 'circe-server-face) - ((string-match "\\" name) - 'circe-my-message-face))) - (keywords (append `(:mynick ,(circe-nick) - :chattarget ,circe-chat-target) - (circe--display-add-nick-property - (if (and (not (null keywords)) - (null (cdr keywords))) - (car keywords) - keywords)))) - (text (lui-format format keywords))) - (when (circe--display-fool-p format keywords) - (add-face-text-property 0 (length text) - 'circe-fool-face t text) - (put-text-property 0 (length text) - 'lui-fool t - text)) - (when face - (add-face-text-property 0 (length text) - face t text)) - (lui-insert text - (memq format circe-format-not-tracked))))) - -(defun circe-display-server-message (message) - "Display MESSAGE as a server message." - (circe-display 'circe-format-server-message - :body message)) - -(defun circe--display-add-nick-property (keywords) - "Add a face to the value of the :nick property in KEYWORDS." - (let ((keyword nil)) - (mapcar (lambda (entry) - (cond - ((or (eq keyword :nick) - (eq keyword 'nick)) - (setq keyword nil) - (propertize entry 'face 'circe-originator-face)) - (t - (setq keyword entry) - entry))) - keywords))) - -(defun circe--display-ignored-p (_format keywords) - (let ((nick (plist-get keywords :nick)) - (userhost (plist-get keywords :userhost)) - (body (plist-get keywords :body))) - (circe--ignored-p nick userhost body))) - -(defun circe--display-fool-p (_format keywords) - (let ((nick (plist-get keywords :nick)) - (userhost (plist-get keywords :userhost)) - (body (plist-get keywords :body))) - (circe--fool-p nick userhost body))) - -(defun circe--ignored-p (nick userhost body) - "True if this user or message is being ignored. - -See `circe-ignore-functions' and `circe-ignore-list'. - -NICK, USER and HOST should be the sender of a the command -COMMAND, which had the arguments ARGS." - (or (run-hook-with-args-until-success 'circe-ignore-functions - nick userhost body) - (circe--ignore-matches-p nick userhost body circe-ignore-list))) - -(defun circe--fool-p (nick userhost body) - "True if this user or message is a fool. - -See `circe-fool-list'. - -NICK, USER and HOST should be the sender of a the command -COMMAND, which had the arguments ARGS." - (circe--ignore-matches-p nick userhost body circe-fool-list)) - -(defun circe--ignore-matches-p (nick userhost body patterns) - "Check if a given command does match an ignore pattern. - -A pattern matches if it either matches the user NICK!USER@HOST, -or if it matches the first word in BODY. - -PATTERNS should be the list of regular expressions." - (let ((string (format "%s!%s" nick userhost)) - (target (when (and body - (string-match "^\\([^ ]*\\)[:,]" body)) - (match-string 1 body)))) - (catch 'return - (dolist (regex patterns) - (when (string-match regex string) - (throw 'return t)) - (when (and (stringp target) - (string-match regex target)) - (throw 'return t))) - nil))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; Nick Highlighting ;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun circe--output-highlight-nick () - "Highlight the nick of the user in the buffer. - -This is used in `lui-pre-output-hook'." - (goto-char (or (text-property-any (point-min) (point-max) - 'lui-format-argument 'body) - (point-min))) - (when (or (not circe-inhibit-nick-highlight-function) - (not (funcall circe-inhibit-nick-highlight-function))) - (let* ((nick (circe-nick)) - (nicks (append (and nick (list nick)) - circe-extra-nicks))) - (when nicks - ;; Can't use \<...\> because that won't match for \ We - ;; might eventually use \_< ... \_> if we define symbols to be - ;; nicks \\= is necessary, because it might be found right where we - ;; are, and that might not be the beginning of a line... (We start - ;; searching from the beginning of the body) - (let ((nick-regex (concat "\\(?:^\\|\\W\\|\\=\\)" - "\\(" (regexp-opt nicks) "\\)" - "\\(?:$\\|\\W\\)"))) - (cond - ((eq circe-highlight-nick-type 'sender) - (if (text-property-any (point-min) - (point-max) - 'face 'circe-originator-face) - (when (re-search-forward nick-regex nil t) - (circe--extend-text-having-face - (point-min) (point-max) - 'circe-originator-face - 'circe-highlight-nick-face)) - (let ((circe-highlight-nick-type 'occurrence)) - (circe--output-highlight-nick)))) - ((eq circe-highlight-nick-type 'occurrence) - (while (re-search-forward nick-regex nil t) - (add-face-text-property (match-beginning 1) - (match-end 1) - 'circe-highlight-nick-face))) - ((eq circe-highlight-nick-type 'message) - (when (re-search-forward nick-regex nil t) - (let* ((start (text-property-any (point-min) - (point-max) - 'lui-format-argument 'body)) - (end (when start - (next-single-property-change start - 'lui-format-argument)))) - (when (and start end) - (add-face-text-property start end - 'circe-highlight-nick-face))))) - ((eq circe-highlight-nick-type 'all) - (when (re-search-forward nick-regex nil t) - (add-face-text-property (point-min) (point-max) - 'circe-highlight-nick-face))))))))) - -(defun circe--extend-text-having-face (from to existing new) - "Extend property values. - -In the text between FROM and TO, find any text that has its face -property set to EXISTING, and prepend NEW to the value of its -face property, when necessary by turning it into a list." - (let ((beg (text-property-any from to 'face existing))) - (while beg - (let ((end (next-single-property-change beg 'face))) - (add-face-text-property beg end new) - (setq beg (text-property-any end to 'face existing)))))) - -;;;;;;;;;;;;;;; -;;;; Input ;;;; -;;;;;;;;;;;;;;; - -(defun circe--input (str) - "Process STR as input. - -This detects commands and interprets them, or sends the input -using the /SAY command." - (set-text-properties 0 (length str) nil str) - (cond - ((string= str "") - nil) - ;; Ignore commands in multiline input - ((and (not (string-match "\n" str)) - (string-match "\\`/\\([^/ ][^ ]*\\|[^/ ]*\\) ?\\([^\n]*\\)\\'" str)) - (let* ((command (match-string 1 str)) - (args (match-string 2 str)) - (handler (intern-soft (format "circe-command-%s" - (upcase command))))) - (cond - ((string= command "") - (circe-command-SAY args)) - (handler - (funcall handler args)) - (circe-server-send-unknown-command-p - (irc-send-raw (circe-server-process) - (format "%s %s" - (upcase command) - args))) - (t - (circe-display-server-message (format "Unknown command: %s" - command)))))) - (t - (mapc #'circe-command-SAY - (circe--list-drop-right (split-string str "\n") - "^ *$"))))) - -;;;;;;;;;;;;;;;;;; -;;;; Flyspell ;;;; -;;;;;;;;;;;;;;;;;; - -(defun circe--flyspell-check-word-p () - "Return a true value if flyspell check the word before point. - -This is a suitable value for `flyspell-generic-check-word-p'. It -will also call `lui-flyspell-check-word-p'." - (cond - ((not (lui-flyspell-check-word-p)) - nil) - ((circe-channel-user-p (circe--flyspell-nick-before-point)) - nil) - (t - t))) - -(defun circe--flyspell-nick-before-point () - "Return the IRC nick before point" - (with-syntax-table circe-nick-syntax-table - (let (beg end) - (save-excursion - (forward-word -1) - (setq beg (point)) - (forward-word 1) - (setq end (point))) - (buffer-substring beg end)))) - -;;;;;;;;;;;;;;;;;;;; -;;;; Completion ;;;; -;;;;;;;;;;;;;;;;;;;; - -(defun circe--completion-at-point () - "Return a list of possible completions for the current buffer. - -This is used in `completion-at-point-functions'." - ;; Use markers so they move when input happens - (let ((start (make-marker)) - (end (make-marker))) - (set-marker end (point)) - (set-marker start - (save-excursion - (when (or (looking-back (regexp-quote - circe-completion-suffix) - (length circe-completion-suffix)) - (looking-back " " 1)) - (goto-char (match-beginning 0))) - (cond - ((<= (point) lui-input-marker) - lui-input-marker) - ((re-search-backward "\\s-" lui-input-marker t) - (1+ (point))) - (t - lui-input-marker)))) - (list start end 'circe--completion-table))) - -(defun circe--completion-table (string pred action) - "Completion table to use for Circe buffers. - -See `minibuffer-completion-table' for details." - (cond - ;; Best completion of STRING - ((eq action nil) - (try-completion string - (circe--completion-candidates - (if (= (- (point) (length string)) - lui-input-marker) - circe-completion-suffix - " ")) - pred)) - ;; A list of possible completions of STRING - ((eq action t) - (all-completions string - (circe--completion-candidates - (if (= (- (point) (length string)) - lui-input-marker) - circe-completion-suffix - " ")) - pred)) - ;; t iff STRING is a valid completion as it stands - ((eq action 'lambda) - (test-completion string - (circe--completion-candidates - (if (= (- (point) (length string)) - lui-input-marker) - circe-completion-suffix - " ")) - pred)) - ;; Boundaries - ((eq (car-safe action) 'boundaries) - `(boundaries 0 . ,(length (cdr action)))) - ;; Metadata - ((eq action 'metadata) - '(metadata (cycle-sort-function . circe--completion-sort))))) - -(defun circe--completion-clean-nick (string) - (with-temp-buffer - (insert string) - (goto-char (point-max)) - (when (or (looking-back circe-completion-suffix nil) - (looking-back " " nil)) - (replace-match "")) - (buffer-string))) - -(defun circe--completion-sort (collection) - "Sort the COLLECTION by channel activity for nicks." - (let* ((proc (circe-server-process)) - (channel (when (and circe-chat-target proc) - (irc-connection-channel proc circe-chat-target))) - (decorated (mapcar (lambda (entry) - (let* ((nick (circe--completion-clean-nick - entry)) - (user (when channel - (irc-channel-user channel nick)))) - (list (when user - (irc-user-last-activity-time user)) - (length entry) - entry))) - collection)) - (sorted (sort decorated - (lambda (a b) - (cond - ((and (car a) - (car b)) - (> (car a) - (car b))) - ((and (not (car a)) - (not (car b))) - (< (cadr a) - (cadr b))) - ((car a) - t) - (t - nil)))))) - (mapcar (lambda (entry) - (nth 2 entry)) - sorted))) - -;; FIXME: I do not know why this is here. -(defvar circe--completion-old-completion-all-sorted-completions nil - "Variable to know if we can return a cached result.") -(make-variable-buffer-local - 'circe--completion-old-completion-all-sorted-completions) -(defvar circe--completion-cache nil - "The results we can cache.") -(make-variable-buffer-local 'circe--completion-cache) - -(defun circe--completion-candidates (nick-suffix) - (if (and circe--completion-old-completion-all-sorted-completions - (eq completion-all-sorted-completions - circe--completion-old-completion-all-sorted-completions)) - circe--completion-cache - (let ((completions (append (circe--commands-list) - (mapcar (lambda (buf) - (with-current-buffer buf - circe-chat-target)) - (circe-server-channel-buffers))))) - (cond - ;; In a server buffer, complete all nicks in all channels - ((eq major-mode 'circe-server-mode) - (dolist (buf (circe-server-channel-buffers)) - (with-current-buffer buf - (dolist (nick (circe-channel-nicks)) - (setq completions (cons (concat nick nick-suffix) - completions)))))) - ;; In a channel buffer, only complete nicks in this channel - ((eq major-mode 'circe-channel-mode) - (setq completions (append (delete (concat (circe-nick) - nick-suffix) - (mapcar (lambda (nick) - (concat nick nick-suffix)) - (circe-channel-nicks))) - completions))) - ;; In a query buffer, only complete this query partner - ((eq major-mode 'circe-query-mode) - (setq completions (cons (concat circe-chat-target nick-suffix) - completions))) - ;; Else, we're doing something wrong - (t - (error "`circe-possible-completions' called outside of Circe"))) - (setq circe--completion-old-completion-all-sorted-completions - completion-all-sorted-completions - circe--completion-cache completions) - completions))) - -(defun circe--commands-list () - "Return a list of possible Circe commands." - (mapcar (lambda (symbol) - (let ((str (symbol-name symbol))) - (if (string-match "^circe-command-\\(.*\\)" str) - (concat "/" (match-string 1 str) " ") - str))) - (apropos-internal "^circe-command-"))) - -;;;;;;;;;;;;;;;;;;; -;;; Server Mode ;;; -;;;;;;;;;;;;;;;;;;; - -(defvar circe-server-mode-hook nil - "Hook run when a new Circe server buffer is created.") - -(defvar circe-server-mode-map (make-sparse-keymap) - "The key map for server mode buffers.") - -(define-derived-mode circe-server-mode circe-mode "Circe Server" - "The mode for circe server buffers. - -This buffer represents a server connection. When you kill it, the -server connection is closed. This will make all associated -buffers unusable. Be sure to use \\[circe-reconnect] if you want -to reconnect to the server. - -\\{circe-server-mode-map}" - (add-hook 'kill-buffer-hook 'circe-server-killed nil t)) - -(defun circe-server-killed () - "Run when the server buffer got killed. - -This will IRC, and ask the user whether to kill all of the -server's chat buffers." - (when circe-server-killed-confirmation - (when (not (y-or-n-p - (if (eq circe-server-killed-confirmation 'ask-and-kill-all) - "Really kill all buffers of this server? (if not, try `circe-reconnect') " - "Really kill the IRC connection? (if not, try `circe-reconnect') "))) - (error "Buffer not killed as per user request"))) - (setq circe-server-inhibit-auto-reconnect-p t) - (ignore-errors - (irc-send-QUIT circe-server-process circe-default-quit-message)) - (ignore-errors - (delete-process circe-server-process)) - (when (eq circe-server-killed-confirmation 'ask-and-kill-all) - (dolist (buf (circe-server-chat-buffers)) - (let ((circe-channel-killed-confirmation nil)) - (kill-buffer buf))))) - -(defun circe-server-buffers () - "Return a list of all server buffers in this Emacs instance." - (let ((result nil)) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when (eq major-mode 'circe-server-mode) - (setq result (cons buf result))))) - (nreverse result))) - -(defun circe-server-process () - "Return the current server process." - (with-circe-server-buffer - circe-server-process)) - -(defun circe-server-my-nick-p (nick) - "Return non-nil when NICK is our current nick." - (let ((proc (circe-server-process))) - (when proc - (irc-current-nick-p proc nick)))) - -(defun circe-nick () - "Return our current nick." - (let ((proc (circe-server-process))) - (when proc - (irc-current-nick proc)))) - -(defun circe-server-last-active-buffer () - "Return the last active buffer of this server." - (with-circe-server-buffer - (if (and circe-server-last-active-buffer - (bufferp circe-server-last-active-buffer) - (buffer-live-p circe-server-last-active-buffer)) - circe-server-last-active-buffer - (current-buffer)))) - -;; There really ought to be a hook for this -(defadvice select-window (after circe-server-track-select-window - (window &optional norecord)) - "Remember the current buffer as the last active buffer. -This is used by Circe to know where to put spurious messages." - (with-current-buffer (window-buffer window) - (when (derived-mode-p 'circe-mode) - (let ((buf (current-buffer))) - (ignore-errors - (with-circe-server-buffer - (setq circe-server-last-active-buffer buf))))))) -(ad-activate 'select-window) - -(defun circe-reduce-lurker-spam () - "Return the value of `circe-reduce-lurker-spam'. - -This uses a buffer-local value first, then the one in the server -buffer. - -Use this instead of accessing the variable directly to enable -setting the variable through network options." - (if (local-variable-p 'circe-reduce-lurker-spam) - circe-reduce-lurker-spam - (with-circe-server-buffer - circe-reduce-lurker-spam))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; Chat Buffer Management ;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Server buffers keep track of associated chat buffers. This enables -;; us to not rely on buffer names staying the same, as well as keeping -;; buffers from different servers and even server connections apart -;; cleanly. - -(defvar circe-server-chat-buffer-table nil - "A hash table of chat buffers associated with this server.") -(make-variable-buffer-local 'circe-server-chat-buffer-table) - -(defun circe-server-get-chat-buffer (target) - "Return the chat buffer addressing TARGET, or nil if none." - (with-circe-server-buffer - (when circe-server-chat-buffer-table - (let* ((target-name (irc-isupport--case-fold (circe-server-process) - target)) - (buf (gethash target-name circe-server-chat-buffer-table))) - (if (buffer-live-p buf) - buf - (remhash target-name circe-server-chat-buffer-table) - nil))))) - -(defun circe-server-create-chat-buffer (target chat-mode) - "Return a new buffer addressing TARGET in CHAT-MODE." - (with-circe-server-buffer - (let* ((target-name (irc-isupport--case-fold (circe-server-process) - target)) - (chat-buffer (generate-new-buffer target)) - (server-buffer (current-buffer)) - (circe-chat-calling-server-buffer-and-target (cons server-buffer - target-name))) - (when (not circe-server-chat-buffer-table) - (setq circe-server-chat-buffer-table (make-hash-table :test 'equal))) - (puthash target-name chat-buffer circe-server-chat-buffer-table) - (with-current-buffer chat-buffer - (funcall chat-mode)) - chat-buffer))) - -(defun circe-server-get-or-create-chat-buffer (target chat-mode) - "Return a buffer addressing TARGET; create one in CHAT-MODE if none exists." - (let ((buf (circe-server-get-chat-buffer target))) - (if buf - buf - (circe-server-create-chat-buffer target chat-mode)))) - -(defun circe-server-remove-chat-buffer (target-or-buffer) - "Remove the buffer addressing TARGET-OR-BUFFER." - (with-circe-server-buffer - (let* ((target (if (bufferp target-or-buffer) - (circe-server-chat-buffer-target target-or-buffer) - target-or-buffer)) - (target-name (irc-isupport--case-fold (circe-server-process) - target))) - (remhash target-name circe-server-chat-buffer-table)))) - -(defun circe-server-rename-chat-buffer (old-name new-name) - "Note that the chat buffer addressing OLD-NAME now addresses NEW-NAME." - (with-circe-server-buffer - (let* ((old-target-name (irc-isupport--case-fold (circe-server-process) - old-name)) - (new-target-name (irc-isupport--case-fold (circe-server-process) - new-name)) - (buf (gethash old-target-name circe-server-chat-buffer-table))) - (when buf - (remhash old-target-name circe-server-chat-buffer-table) - (puthash new-target-name buf circe-server-chat-buffer-table) - (with-current-buffer buf - (setq circe-chat-target new-name) - (rename-buffer new-name t)))))) - -(defun circe-server-chat-buffer-target (&optional buffer) - "Return the chat target of BUFFER, or the current buffer if none." - (if buffer - (with-current-buffer buffer - circe-chat-target) - circe-chat-target)) - -(defun circe-server-chat-buffers () - "Return the list of chat buffers on this server." - (with-circe-server-buffer - (when circe-server-chat-buffer-table - (let ((buffer-list nil)) - (maphash (lambda (target-name buffer) - (if (buffer-live-p buffer) - (push buffer buffer-list) - (remhash target-name circe-server-chat-buffer-table))) - circe-server-chat-buffer-table) - buffer-list)))) - -(defun circe-server-channel-buffers () - "Return a list of all channel buffers of this server." - (let ((result nil)) - (dolist (buf (circe-server-chat-buffers)) - (with-current-buffer buf - (when (eq major-mode 'circe-channel-mode) - (setq result (cons buf result))))) - (nreverse result))) - -;;;;;;;;;;;;;;;;; -;;; Chat Mode ;;; -;;;;;;;;;;;;;;;;; - -(defvar circe-chat-mode-hook nil - "Hook run when a new chat buffer (channel or query) is created.") - -(defvar circe-chat-mode-map (make-sparse-keymap) - "Base key map for all Circe chat buffers (channel, query).") - -;; Defined here as we use it, but do not necessarily want to use the -;; full module. -(defvar lui-logging-format-arguments nil - "A list of arguments to be passed to `lui-format'. -This can be used to extend the formatting possibilities of the -file name for lui applications.") -(make-variable-buffer-local 'lui-logging-format-arguments) - -(define-derived-mode circe-chat-mode circe-mode "Circe Chat" - "The circe chat major mode. - -This is the common mode used for both queries and channels. -It should not be used directly. -TARGET is the default target to send data to. -SERVER-BUFFER is the server buffer of this chat buffer." - (setq circe-server-buffer (car circe-chat-calling-server-buffer-and-target) - circe-chat-target (cdr circe-chat-calling-server-buffer-and-target)) - (let ((network (with-circe-server-buffer - circe-network))) - (make-local-variable 'mode-line-buffer-identification) - (setq mode-line-buffer-identification - (list (format "%%b@%-8s" network))) - (setq lui-logging-format-arguments - `(:target ,circe-chat-target :network ,network))) - (when (equal circe-chat-target "#emacs-circe") - (set (make-local-variable 'lui-button-issue-tracker) - "https://github.com/jorgenschaefer/circe/issues/%s"))) - -(defun circe-chat-disconnected () - "The current buffer got disconnected." - (circe-display-server-message "Disconnected")) - -;;;;;;;;;;;;;;;;;;;; -;;; Channel Mode ;;; -;;;;;;;;;;;;;;;;;;;; - -(defvar circe-channel-mode-hook nil - "Hook run in a new channel buffer.") - -(defvar circe-channel-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-n") 'circe-command-NAMES) - (define-key map (kbd "C-c C-t") 'circe-command-CHTOPIC) - map) - "The key map for channel mode buffers.") - -(define-derived-mode circe-channel-mode circe-chat-mode "Circe Channel" - "The circe channel chat major mode. -This mode represents a channel you are talking in. - -TARGET is the default target to send data to. -SERVER-BUFFER is the server buffer of this chat buffer. - -\\{circe-channel-mode-map}" - (add-hook 'kill-buffer-hook 'circe-channel-killed nil t)) - -(defun circe-channel-killed () - "Called when the channel buffer got killed. - -If we are not on the channel being killed, do nothing. Otherwise, -if the server is live, and the user wants to kill the buffer, -send PART to the server and clean up the channel's remaining -state." - (when (buffer-live-p circe-server-buffer) - (when (and circe-channel-killed-confirmation - (not (y-or-n-p "Really leave this channel? "))) - (error "Channel not left.")) - (ignore-errors - (irc-send-PART (circe-server-process) - circe-chat-target - circe-default-part-message)) - (ignore-errors - (circe-server-remove-chat-buffer circe-chat-target)))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; Channel User Tracking ;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; Channel mode buffers provide some utility functions to check if a -;; given user is idle or not. - -(defun circe-channel-user-nick-regain-p (_old new) - "Return true if a nick change from OLD to NEW constitutes a nick regain. - -A nick was regained if the NEW nick was also a recent user." - (let ((channel (irc-connection-channel (circe-server-process) - circe-chat-target))) - (when channel - (irc-channel-recent-user channel new)))) - -(defun circe-channel-user-p (nick) - "Return non-nil when NICK belongs to a channel user." - (cond - ((eq major-mode 'circe-query-mode) - (irc-string-equal-p (circe-server-process) - nick - circe-chat-target)) - ((eq major-mode 'circe-channel-mode) - (let ((channel (irc-connection-channel (circe-server-process) - circe-chat-target))) - (when channel - (if (irc-channel-user channel nick) - t - nil)))))) - -(defun circe-channel-nicks () - "Return a list of nicks in the current channel." - (let* ((channel (irc-connection-channel (circe-server-process) - circe-chat-target)) - (nicks nil)) - (when channel - (maphash (lambda (_folded-nick user) - (push (irc-user-nick user) nicks)) - (irc-channel-users channel))) - nicks)) - -(defun circe-user-channels (nick) - "Return a list of channel buffers for the user named NICK." - (let* ((result nil)) - (dolist (channel (irc-connection-channel-list (circe-server-process))) - (when (irc-channel-user channel nick) - (let* ((name (irc-channel-name channel)) - (buf (circe-server-get-chat-buffer name))) - (when buf - (push buf result))))) - result)) - -(defun circe-lurker-p (nick) - "Return a true value if this nick is regarded inactive." - (let* ((channel (irc-connection-channel (circe-server-process) - circe-chat-target)) - (user (when channel - (irc-channel-user channel nick))) - (recent-user (when channel - (irc-channel-recent-user channel nick))) - (last-active (cond - (user - (irc-user-last-activity-time user)) - (recent-user - (irc-user-last-activity-time recent-user))))) - (cond - ;; If we do not track lurkers, no one is ever a lurker. - ((not (circe-reduce-lurker-spam)) - nil) - ;; We ourselves are never lurkers (in this sense). - ((circe-server-my-nick-p nick) - nil) - ;; Someone who isn't even on the channel (e.g. NickServ) isn't a - ;; lurker, either. - ((and (not user) - (not recent-user)) - nil) - ;; If someone has never been active, they most definitely *are* a - ;; lurker. - ((not last-active) - t) - ;; But if someone has been active, and we mark active users - ;; inactive again after a timeout ... - (circe-active-users-timeout - ;; They are still lurkers if their activity has been too long - ;; ago. - (> (- (float-time) - last-active) - circe-active-users-timeout)) - ;; Otherwise, they have been active and we don't mark active - ;; users inactive again, so nope, not a lurker. - (t - nil)))) - -(defun circe-lurker-rejoin-p (nick channel) - "Return true if NICK is rejoining CHANNEL. - -A user is considered to be rejoining if they were on the channel -shortly before, and were active then." - (let* ((channel (irc-connection-channel (circe-server-process) - channel)) - (user (when channel - (irc-channel-recent-user channel nick)))) - (when user - (irc-user-last-activity-time user)))) - -(defun circe-lurker-display-active (nick userhost) - "Show that this user is active if they are a lurker." - (let* ((channel (irc-connection-channel (circe-server-process) - circe-chat-target)) - (user (when channel - (irc-channel-user channel nick))) - (join-time (when user - (irc-user-join-time user)))) - (when (and (circe-lurker-p nick) - ;; If we saw them when we joined the channel, no need to - ;; say "they're suddenly active!!111one". - join-time) - (circe-display 'circe-format-server-lurker-activity - :nick nick - :userhost (or userhost "server") - :jointime join-time - :joindelta (circe-duration-string - (- (float-time) - join-time)))))) - -;;;;;;;;;;;;;;;;;; -;;; Query Mode ;;; -;;;;;;;;;;;;;;;;;; - -(defvar circe-query-mode-hook nil - "Hook run when query mode is activated.") - -(defvar circe-query-mode-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map circe-chat-mode-map) - map) - "The key map for query mode buffers.") - -(define-derived-mode circe-query-mode circe-chat-mode "Circe Query" - "The circe query chat major mode. -This mode represents a query you are talking in. - -TARGET is the default target to send data to. -SERVER-BUFFER is the server buffer of this chat buffer. - -\\{circe-query-mode-map}" - (add-hook 'kill-buffer-hook 'circe-query-killed nil t)) - -(defun circe-query-killed () - "Called when the query buffer got killed." - (ignore-errors - (circe-server-remove-chat-buffer circe-chat-target))) - -(defun circe-query-auto-query-buffer (who) - "Return a buffer for a query with `WHO'. - -This adheres to `circe-auto-query-max'." - (or (circe-server-get-chat-buffer who) - (when (< (circe--query-count) - circe-auto-query-max) - (circe-server-get-or-create-chat-buffer who 'circe-query-mode)))) - -(defun circe--query-count () - "Return the number of queries on the current server." - (let ((num 0)) - (dolist (buf (circe-server-chat-buffers)) - (with-current-buffer buf - (when (eq major-mode 'circe-query-mode) - (setq num (+ num 1))))) - num)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; IRC Protocol Handling ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defvar circe--irc-handler-table nil - "The handler table for Circe's IRC connections. - -Do not use this directly. Instead, call `circe-irc-handler-table'.") - -(defun circe-irc-handler-table () - (when (not circe--irc-handler-table) - (let ((table (irc-handler-table))) - (irc-handler-add table "irc.registered" #'circe--irc-conn-registered) - (irc-handler-add table "conn.disconnected" #'circe--irc-conn-disconnected) - (irc-handler-add table nil #'circe--irc-display-event) - (irc-handle-registration table) - (irc-handle-ping-pong table) - (irc-handle-isupport table) - (irc-handle-initial-nick-acquisition table) - (irc-handle-ctcp table) - (irc-handle-state-tracking table) - (irc-handle-nickserv table) - (irc-handle-auto-join table) - (setq circe--irc-handler-table table))) - circe--irc-handler-table) - -(defun circe--irc-conn-registered (conn _event _nick) - (with-current-buffer (irc-connection-get conn :server-buffer) - (setq circe-server-reconnect-attempts 0) - (run-hooks 'circe-server-connected-hook))) - -(defun circe--irc-conn-disconnected (conn _event) - (with-current-buffer (irc-connection-get conn :server-buffer) - (dolist (buf (circe-server-chat-buffers)) - (with-current-buffer buf - (circe-chat-disconnected))) - - (circe-reconnect))) - -(defun circe--irc-display-event (conn event &optional sender &rest args) - "Display an IRC message. - -NICK, USER and HOST specify the originator of COMMAND with ARGS -as arguments." - (with-current-buffer (irc-connection-get conn :server-buffer) - (let* ((display (circe-get-display-handler event)) - (nick (when sender - (irc-userstring-nick sender))) - (userhost (when sender - (irc-userstring-userhost sender)))) - (cond - ;; Functions get called - ((functionp display) - (apply display nick userhost event args)) - ;; Lists describe patterns - ((consp display) - (circe--irc-display-format (elt display 1) - (elt display 0) - nick userhost event args)) - ;; No configured display handler, show a default - (t - (circe--irc-display-default nick userhost event args)))))) - -(defvar circe--irc-format-server-numeric "*** %s" - "The format to use for server messages. Do not set this.") - -(defun circe--irc-display-format (format target nick userhost event args) - (let* ((target+name (circe--irc-display-target target nick args)) - (target (car target+name)) - (name (cdr target+name)) - (origin (if userhost - (format "%s (%s)" nick userhost) - (format "%s" nick)))) - (with-current-buffer (or target - (circe-server-last-active-buffer)) - (let ((circe--irc-format-server-numeric - (if target - (format "*** %s" format) - (format "*** [%s] %s" name format)))) - (circe-display 'circe--irc-format-server-numeric - :nick (or nick "(unknown)") - :userhost (or userhost "server") - :origin origin - :event event - :command event - :target name - :indexed-args args))))) - -(defun circe--irc-display-target (target nick args) - "Return the target buffer and name. -The buffer might be nil if it is not alive. - -See `circe-set-display-handler' for a description of target. - -NICK and USERHOST are the originator of COMMAND which had ARGS as -arguments." - (cond - ((eq target 'nick) - (cons (circe-server-get-chat-buffer nick) - nick)) - ((numberp target) - (let ((name (nth target - args))) - (cons (circe-server-get-chat-buffer name) - name))) - ((eq target 'active) - (let ((buf (circe-server-last-active-buffer))) - (cons buf - (buffer-name buf)))) - ((eq target 'server) - (cons (current-buffer) (buffer-name))) - (t - (error "Bad target in format string: %s" target)))) - -(defun circe--irc-display-default (nick userhost event args) - (with-current-buffer (circe-server-last-active-buffer) - (let ((target (if (circe-server-my-nick-p (car args)) - "" - (format " to %s" (car args))))) - (cond - ((string-match "\\`irc.ctcpreply.\\(.*\\)\\'" event) - (circe-display-server-message - (format "CTCP %s reply from %s (%s)%s: %s" - (match-string 1 event) nick userhost target (cadr args)))) - ((string-match "\\`irc.ctcp.\\(.*\\)\\'" event) - (circe-display-server-message - (format "Unknown CTCP request %s from %s (%s)%s: %s" - (match-string 1 event) nick userhost target (cadr args)))) - (t - (circe-display-server-message - (format "[%s from %s%s] %s" - event - nick - (if userhost - (format " (%s)" userhost) - "") - (mapconcat #'identity args " ")))))))) - -(defun circe-set-display-handler (command handler) - "Set the display handler for COMMAND to HANDLER. - -A handler is either a function or a list. - -A function gets called in the server buffer with at least three -arguments, but possibly more. There's at least NICK and USERHOST -of the sender, which can be nil, and COMMAND, which is the event -which triggered this. Further arguments are arguments to the -event. - -Alternatively, the handler can be a list of two elements: - - target - The target of this message - format - The format for this string - -The target can be any of: - - 'active - The last active buffer of this server - 'nick - The nick who sent this message - 'server - The server buffer for this server - number - The index of the argument of the target - -The format is passed to `lui-format'. Possible format string -substitutions are {mynick}, {target}, {nick}, {userhost}, -{origin}, {command}, {target}, and indexed arguments for the -arguments to the IRC message." - (when (not circe-display-table) - (setq circe-display-table (make-hash-table :test 'equal))) - (puthash command handler circe-display-table)) - -(defun circe-get-display-handler (command) - "Return the display handler for COMMAND. - -See `circe-set-display-handler' for more information." - (when circe-display-table - (gethash command circe-display-table))) - -;;;;;;;;;;;;;;;; -;;; Commands ;;; -;;;;;;;;;;;;;;;; - -(defun circe-command-AWAY (reason) - "Set yourself away with REASON." - (interactive "sReason: ") - (irc-send-AWAY (circe-server-process) reason)) - -(defun circe-command-BACK (&optional ignored) - "Mark yourself not away anymore. - -Arguments are IGNORED." - (interactive) - (irc-send-AWAY (circe-server-process))) - -(defun circe-command-CHTOPIC (&optional ignored) - "Insert the topic of the current channel. - -Arguments are IGNORED." - (interactive) - (if (not circe-chat-target) - (circe-display-server-message "No target for current buffer") - (let* ((channel (irc-connection-channel (circe-server-process) - circe-chat-target)) - (topic (when channel - (irc-channel-topic channel)))) - (lui-replace-input (format "/TOPIC %s %s" - circe-chat-target (or topic "")))) - (goto-char (point-max)))) - -(defun circe-command-CLEAR (&optional ignored) - "Delete all buffer contents before the lui prompt." - (let ((inhibit-read-only t)) - (delete-region (point-min) lui-output-marker))) - -(defun circe-command-CTCP (who &optional command argument) - "Send a CTCP message to WHO containing COMMAND with ARGUMENT. -If COMMAND is not given, WHO is parsed to contain all of who, -command and argument. -If ARGUMENT is nil, it is interpreted as no argument." - (when (not command) - (if (string-match "^\\([^ ]*\\) *\\([^ ]*\\) *\\(.*\\)" who) - (setq command (upcase (match-string 2 who)) - argument (match-string 3 who) - who (match-string 1 who)) - (circe-display-server-message "Usage: /CTCP "))) - (when (not (string= "" command)) - (irc-send-ctcp (circe-server-process) - who - command - (if (and argument (not (equal argument ""))) - argument - nil)))) - -(defun circe-command-FOOL (line) - "Add the regex on LINE to the `circe-fool-list'." - (with-current-buffer (circe-server-last-active-buffer) - (cond - ((string-match "\\S-+" line) - (let ((regex (match-string 0 line))) - (add-to-list 'circe-fool-list regex) - (circe-display-server-message (format "Recognizing %s as a fool" - regex)))) - ((not circe-fool-list) - (circe-display-server-message "Your do not know any fools")) - (t - (circe-display-server-message "Your list of fools:") - (dolist (regex circe-fool-list) - (circe-display-server-message (format "- %s" regex))))))) - -(defun circe-command-GAWAY (reason) - "Set yourself away on all servers with reason REASON." - (interactive "sReason: ") - (dolist (buf (circe-server-buffers)) - (with-current-buffer buf - (irc-send-AWAY circe-server-process reason)))) - -(defun circe-command-GQUIT (reason) - "Quit all servers with reason REASON." - (interactive "sReason: ") - (dolist (buf (circe-server-buffers)) - (with-current-buffer buf - (when (eq (process-status circe-server-process) - 'open) - (irc-send-QUIT circe-server-process reason))))) - -(defun circe-command-HELP (&optional ignored) - "Display a list of recognized commands, nicely formatted." - (circe-display-server-message - (concat "Recognized commands are: " - (mapconcat (lambda (s) s) (circe--commands-list) "")))) - -(defun circe-command-IGNORE (line) - "Add the regex on LINE to the `circe-ignore-list'." - (with-current-buffer (circe-server-last-active-buffer) - (cond - ((string-match "\\S-+" line) - (let ((regex (match-string 0 line))) - (add-to-list 'circe-ignore-list regex) - (circe-display-server-message (format "Ignore list, meet %s" - regex)))) - ((not circe-ignore-list) - (circe-display-server-message "Your ignore list is empty")) - (t - (circe-display-server-message "Your ignore list:") - (dolist (regex circe-ignore-list) - (circe-display-server-message (format "- %s" regex))))))) - -(defun circe-command-INVITE (nick &optional channel) - "Invite NICK to CHANNEL. -When CHANNEL is not given, NICK is assumed to be a string -consisting of two words, the nick and the channel." - (interactive "sInvite who: \nsWhere: ") - (when (not channel) - (if (string-match "^\\([^ ]+\\) +\\([^ ]+\\)" nick) - (setq channel (match-string 2 nick) - nick (match-string 1 nick)) - (when (or (string= "" nick) (null nick)) - (circe-display-server-message "Usage: /INVITE ")))) - (irc-send-INVITE (circe-server-process) - nick - (if (and (null channel) - (not (null nick))) - circe-chat-target - channel))) - -(defun circe-command-JOIN (channel) - "Join CHANNEL. This can also contain a key." - (interactive "sChannel: ") - (let (channels keys) - (when (string-match "^\\s-*\\([^ ]+\\)\\(:? \\([^ ]+\\)\\)?$" channel) - (setq channels (match-string 1 channel) - keys (match-string 3 channel)) - (dolist (channel (split-string channels ",")) - (pop-to-buffer - (circe-server-get-or-create-chat-buffer channel - 'circe-channel-mode))) - (irc-send-JOIN (circe-server-process) channels keys)))) - -(defun circe-command-ME (line) - "Send LINE to IRC as an action." - (interactive "sAction: ") - (if (not circe-chat-target) - (circe-display-server-message "No target for current buffer") - (circe-display 'circe-format-self-action - :body line - :nick (circe-nick)) - (irc-send-ctcp (circe-server-process) - circe-chat-target - "ACTION" line))) - -(defun circe-command-MSG (who &optional what) - "Send a message. - -Send WHO a message containing WHAT. - -If WHAT is not given, WHO should contain both the nick and the -message separated by a space." - (when (not what) - (if (string-match "^\\([^ ]*\\) \\(.*\\)" who) - (setq what (match-string 2 who) - who (match-string 1 who)) - (circe-display-server-message "Usage: /MSG "))) - (when what - (let ((buf (circe-query-auto-query-buffer who))) - (if buf - (with-current-buffer buf - (circe-command-SAY what) - (lui-add-input what)) - (with-current-buffer (circe-server-last-active-buffer) - (irc-send-PRIVMSG (circe-server-process) - who what) - (circe-display 'circe-format-self-message - :target who - :body what)))))) - -(defun circe-command-NAMES (&optional channel) - "List the names of the current channel or CHANNEL." - (interactive) - (let ((target (when channel - (string-trim channel)))) - (when (or (not target) - (equal target "")) - (setq target circe-chat-target)) - (if (not target) - (circe-display-server-message "No target for current buffer") - (irc-send-NAMES (circe-server-process) - target)))) - -(defun circe-command-NICK (newnick) - "Change nick to NEWNICK." - (interactive "sNew nick: ") - (let ((newnick (string-trim newnick))) - (irc-send-NICK (circe-server-process) newnick))) - -(defun circe-command-PART (reason) - "Part the current channel because of REASON." - (interactive "sReason: ") - (if (not circe-chat-target) - (circe-display-server-message "No target for current buffer") - (irc-send-PART (circe-server-process) - circe-chat-target - (if (equal "" reason) - circe-default-part-message - reason)))) - -(defun circe-command-PING (target) - "Send a CTCP PING request to TARGET." - (interactive "sWho: ") - (let ((target (string-trim target))) - (irc-send-ctcp (circe-server-process) - target - "PING" (format "%s" (float-time))))) - -(defun circe-command-QUERY (arg) - "Open a query with WHO." - ;; Eventually, this should probably be just the same as - ;; circe-command-MSG - (interactive "sQuery with: ") - (let* (who what) - (if (string-match "\\`\\s-*\\(\\S-+\\)\\s-\\(\\s-*\\S-.*\\)\\'" arg) - (setq who (match-string 1 arg) - what (match-string 2 arg)) - (setq who (string-trim arg))) - (when (string= who "") - (circe-display-server-message "Usage: /query [something to say]")) - (pop-to-buffer - (circe-server-get-or-create-chat-buffer who 'circe-query-mode)) - (when what - (circe-command-SAY what) - (lui-add-input what)))) - -(defun circe-command-QUIT (reason) - "Quit the current server giving REASON." - (interactive "sReason: ") - (with-circe-server-buffer - (setq circe-server-inhibit-auto-reconnect-p t) - (irc-send-QUIT (circe-server-process) - (if (equal "" reason) - circe-default-quit-message - reason)))) - -(defun circe-command-QUOTE (line) - "Send LINE verbatim to the server." - (interactive "Line: ") - (irc-send-raw (circe-server-process) line) - (with-current-buffer (circe-server-last-active-buffer) - (circe-display-server-message (format "Sent to server: %s" - line)))) - -(defun circe-command-SAY (line) - "Say LINE to the current target." - (interactive "sSay: ") - (if (not circe-chat-target) - (circe-display-server-message "No target for current buffer") - (dolist (line (circe--split-line line)) - (circe-display 'circe-format-self-say - :body line - :nick (circe-nick)) - (irc-send-PRIVMSG (circe-server-process) - circe-chat-target - ;; Some IRC servers give an error if there is - ;; no text at all. - (if (string= line "") - " " - line))))) - -(defun circe--split-line (longline) - "Splits LONGLINE into smaller components. - -IRC silently truncates long lines. This splits a long line into -parts that each are not longer than `circe-split-line-length'." - (if (< (length longline) - circe-split-line-length) - (list longline) - (with-temp-buffer - (insert longline) - (let ((fill-column circe-split-line-length)) - (fill-region (point-min) (point-max) - nil t)) - (split-string (buffer-string) "\n")))) - -(defun circe-command-SV (&optional ignored) - "Tell the current channel about your client and Emacs version. - -Arguments are IGNORED." - (interactive) - (circe-command-SAY (format (concat "I'm using Circe version %s " - "with %s %s (of %s)") - (circe--version) - "GNU Emacs" - emacs-version - (format-time-string "%Y-%m-%d" - emacs-build-time)))) - -(defun circe-command-TOPIC (channel &optional newtopic) - "Change the topic of CHANNEL to NEWTOPIC." - (interactive "sChannel: \nsNew topic: ") - (when (string-match "^\\s-*$" channel) - (setq channel nil)) - (when (and channel - (not newtopic) - (string-match "^\\s-*\\(\\S-+\\)\\( \\(.*\\)\\)?" channel)) - (setq newtopic (match-string 3 channel) - channel (match-string 1 channel))) - (cond - ((and channel newtopic) - (irc-send-TOPIC (circe-server-process) channel newtopic)) - (channel - (irc-send-TOPIC (circe-server-process) channel)) - (circe-chat-target - (irc-send-TOPIC (circe-server-process) circe-chat-target)) - (t - (circe-display-server-message "No channel given, and no default target.")))) - -(defun circe-command-UNFOOL (line) - "Remove the entry LINE from `circe-fool-list'." - (with-current-buffer (circe-server-last-active-buffer) - (cond - ((string-match "\\S-+" line) - (let ((regex (match-string 0 line))) - (setq circe-fool-list (delete regex circe-fool-list)) - (circe-display-server-message (format "Assuming %s is not a fool anymore" - regex)))) - (t - (circe-display-server-message - "No one is not a fool anymore? UNFOOL requires one argument"))))) - -(defun circe-command-UNIGNORE (line) - "Remove the entry LINE from `circe-ignore-list'." - (with-current-buffer (circe-server-last-active-buffer) - (cond - ((string-match "\\S-+" line) - (let ((regex (match-string 0 line))) - (setq circe-ignore-list (delete regex circe-ignore-list)) - (circe-display-server-message (format "Ignore list forgot about %s" - regex)))) - (t - (circe-display-server-message - "Who do you want to unignore? UNIGNORE requires one argument"))))) - -(defun circe-command-WHOAMI (&optional ignored) - "Request WHOIS information about yourself. - -Arguments are IGNORED." - (interactive) - (irc-send-WHOIS (circe-server-process) - (circe-nick))) - -(defun circe-command-WHOIS (whom) - "Request WHOIS information about WHOM." - (interactive "sWhois: ") - (let* ((whom-server-name (split-string whom)) - (whom (car whom-server-name)) - (server-or-name (cadr whom-server-name))) - (irc-send-WHOIS (circe-server-process) whom server-or-name))) - -(defun circe-command-WHOWAS (whom) - "Request WHOWAS information about WHOM." - (interactive "sWhois: ") - (let ((whom (string-trim whom))) - (irc-send-WHOWAS (circe-server-process) whom))) - -(defun circe-command-WL (&optional split) - "Show the people who left in a netsplit. -Without any arguments, shows shows the current netsplits and how -many people are missing. With an argument SPLIT, which must be a -number, it shows the missing people due to that split." - (let ((circe-netsplit-list (with-circe-server-buffer - circe-netsplit-list))) - (if (or (not split) - (and (stringp split) - (string= split ""))) - (if (null circe-netsplit-list) - (circe-display-server-message "No net split at the moment") - (let ((n 0)) - (dolist (entry circe-netsplit-list) - (circe-display-server-message (format "(%d) Missing %d people due to %s" - n - (hash-table-count (nth 3 entry)) - (car entry))) - (setq n (+ n 1))))) - (let* ((index (if (numberp split) - split - (string-to-number split))) - (entry (nth index circe-netsplit-list))) - (if (not entry) - (circe-display-server-message (format "No split number %s - use /WL to see a list" - split)) - (let ((missing nil)) - (maphash (lambda (_key value) - (setq missing (cons value missing))) - (nth 3 entry)) - (circe-display-server-message - (format "Missing people due to %s: %s" - (car entry) - (mapconcat 'identity - (sort missing - (lambda (a b) - (string< (downcase a) - (downcase b)))) - ", "))))))))) - -;;;;;;;;;;;;;;;;;;;;;;;; -;;; Display Handlers ;;; -;;;;;;;;;;;;;;;;;;;;;;;; - -(defun circe-display-ignore (_nick _userhost _command &rest _args) - "Don't show a this message. - -NICK and USERHOST are the originator of COMMAND which had ARGS as -arguments." - 'noop) - -(circe-set-display-handler "317" 'circe-display-317) -(defun circe-display-317 (_sender ignored _numeric _target nick - idletime &optional signon-time body) - "Show a 317 numeric (RPL_WHOISIDLE). - -Arguments are either of the two: - -: 317 :seconds idle -: 317 :seconds idle, signon time" - (with-current-buffer (circe-server-last-active-buffer) - (let ((seconds-idle (string-to-number idletime)) - (signon-time (when body - (string-to-number signon-time)))) - (if signon-time - (circe-display 'circe-format-server-whois-idle-with-signon - :whois-nick nick - :idle-seconds seconds-idle - :idle-duration (circe-duration-string seconds-idle) - :signon-time signon-time - :signon-date (current-time-string - (seconds-to-time signon-time)) - :signon-ago (circe-duration-string (- (float-time) - signon-time))) - (circe-display 'circe-format-server-whois-idle - :whois-nick nick - :idle-seconds seconds-idle - :idle-duration (circe-duration-string seconds-idle)))))) - -(circe-set-display-handler "329" 'circe-display-329) -(defun circe-display-329 (_server ignored _numeric _target channel timestamp) - "Show a 329 numeric (RPL_CREATIONTIME)." - (with-current-buffer (or (circe-server-get-chat-buffer channel) - (circe-server-last-active-buffer)) - (let ((creation-time (string-to-number timestamp))) - (circe-display 'circe-format-server-channel-creation-time - :channel channel - :date (current-time-string - (seconds-to-time creation-time)) - :ago (circe-duration-string (- (float-time) - creation-time)))))) - -(circe-set-display-handler "333" 'circe-display-333) -(defun circe-display-333 (_server ignored _numeric target - channel setter topic-time) - "Show a 333 numeric (RPL_TOPICWHOTIME). - -Arguments are either of the two: - -: 333 1434996762 -: 333 !@ 1434996803" - (let ((channel-buffer (circe-server-get-chat-buffer channel)) - (topic-time (string-to-number topic-time))) - (with-current-buffer (or channel-buffer - (circe-server-last-active-buffer)) - (circe-display (if channel-buffer - 'circe-format-server-topic-time - 'circe-format-server-topic-time-for-channel) - :nick target - :channel channel - :setter (irc-userstring-nick setter) - :setter-userhost (or (irc-userstring-userhost setter) - "(unknown)") - :topic-time topic-time - :topic-date (current-time-string - (seconds-to-time topic-time)) - :topic-ago (circe-duration-string (- (float-time) - topic-time)))))) - -(circe-set-display-handler "AUTHENTICATE" 'circe-display-ignore) -(circe-set-display-handler "CAP" 'circe-display-ignore) -(circe-set-display-handler "conn.connected" 'circe-display-ignore) -(circe-set-display-handler "conn.disconnected" 'circe-display-ignore) - -(circe-set-display-handler "irc.ctcp" 'circe-display-ignore) -(circe-set-display-handler "irc.ctcpreply" 'circe-display-ignore) - -(circe-set-display-handler "irc.ctcp.ACTION" 'circe-display-ctcp-action) -(defun circe-display-ctcp-action (nick userhost _command target text) - "Show an ACTION." - (cond - ;; Query - ((circe-server-my-nick-p target) - (let ((query-buffer (circe-query-auto-query-buffer nick))) - (with-current-buffer (or query-buffer - (circe-server-last-active-buffer)) - (circe-display (if query-buffer - 'circe-format-action - 'circe-format-message-action) - :nick nick - :userhost (or userhost "server") - :body text)))) - ;; Channel - (t - (with-current-buffer (circe-server-get-or-create-chat-buffer - target 'circe-channel-mode) - (circe-lurker-display-active nick userhost) - (circe-display 'circe-format-action - :nick nick - :userhost (or userhost "server") - :body text))))) - -(circe-set-display-handler "irc.ctcp.CLIENTINFO" 'circe-display-ctcp) - -(circe-set-display-handler "irc.ctcp.PING" 'circe-display-ctcp-ping) -(defun circe-display-ctcp-ping (nick userhost _command target text) - "Show a CTCP PING request." - (with-current-buffer (circe-server-last-active-buffer) - (circe-display 'circe-format-server-ctcp-ping - :nick nick - :userhost (or userhost "server") - :target target - :body (or text "") - :ago (let ((time (string-to-number text))) - (if time - (format "%.2f seconds" (- (float-time) time)) - "unknown seconds"))))) - -(circe-set-display-handler "irc.ctcpreply.PING" 'circe-display-ctcp-ping-reply) -(defun circe-display-ctcp-ping-reply (nick userhost _command target text) - "Show a CTCP PING reply." - (with-current-buffer (circe-server-last-active-buffer) - (circe-display 'circe-format-server-ctcp-ping-reply - :nick nick - :userhost (or userhost "server") - :target target - :body text - :ago (let ((time (string-to-number text))) - (if time - (format "%.2f seconds" (- (float-time) time)) - "unknown seconds"))))) - -(circe-set-display-handler "irc.ctcp.SOURCE" 'circe-display-ctcp) -(circe-set-display-handler "irc.ctcp.TIME" 'circe-display-ctcp) -(circe-set-display-handler "irc.ctcp.VERSION" 'circe-display-ctcp) -(defun circe-display-ctcp (nick userhost command target text) - "Show a CTCP request that does not require special handling." - (with-current-buffer (circe-server-last-active-buffer) - (circe-display 'circe-format-server-ctcp - :nick nick - :userhost (or userhost "server") - :target target - :command (substring command 9) - :body (or text "")))) - -(circe-set-display-handler "irc.registered" 'circe-display-ignore) - -(circe-set-display-handler "JOIN" 'circe-display-JOIN) -(defun circe-display-JOIN (nick userhost _command channel - &optional accountname realname) - "Show a JOIN message. - -The command receives an extra argument, the account name, on some -IRC servers." - (let* ((accountname (if (equal accountname "*") - "(unauthenticated)" - accountname)) - (userinfo (if accountname - (format "%s, %s: %s" userhost accountname realname) - userhost)) - (split (circe--netsplit-join nick))) - ;; First, update the channel - (with-current-buffer (circe-server-get-or-create-chat-buffer - channel 'circe-channel-mode) - (cond - (split - (let ((split-time (cadr split))) - (when (< (+ split-time circe-netsplit-delay) - (float-time)) - (circe-display 'circe-format-server-netmerge - :split (car split) - :time (cadr split) - :date (current-time-string - (seconds-to-time (cadr split))) - :ago (circe-duration-string - (- (float-time) (cadr split))))))) - ((and (circe-reduce-lurker-spam) - (circe-lurker-rejoin-p nick circe-chat-target)) - (let* ((channel (irc-connection-channel (circe-server-process) - circe-chat-target)) - (user (when channel - (irc-channel-recent-user channel nick))) - (departed (when user - (irc-user-part-time user)))) - (circe-display 'circe-format-server-rejoin - :nick nick - :userhost (or userhost "server") - :accountname accountname - :realname realname - :userinfo userinfo - :departuretime departed - :departuredelta (circe-duration-string - (- (float-time) - departed))))) - ((not (circe-reduce-lurker-spam)) - (circe-display 'circe-format-server-join - :nick nick - :userhost (or userhost "server") - :accountname accountname - :realname realname - :userinfo userinfo - :channel circe-chat-target)))) - ;; Next, a possible query buffer. We do this even when the message - ;; should be ignored by a netsplit, since this can't flood. - (let ((buf (circe-server-get-chat-buffer nick))) - (when buf - (with-current-buffer buf - (circe-display 'circe-format-server-join-in-channel - :nick nick - :userhost (or userhost "server") - :accountname accountname - :realname realname - :userinfo userinfo - :channel circe-chat-target)))))) - -(circe-set-display-handler "MODE" 'circe-display-MODE) -(defun circe-display-MODE (setter userhost _command target &rest modes) - "Show a MODE message." - (with-current-buffer (or (circe-server-get-chat-buffer target) - (circe-server-last-active-buffer)) - (circe-display 'circe-format-server-mode-change - :setter setter - :userhost (or userhost "server") - :target target - :change (mapconcat #'identity modes " ")))) - -(circe-set-display-handler "NICK" 'circe-display-NICK) -(defun circe-display-NICK (old-nick userhost _command new-nick) - "Show a nick change." - (if (circe-server-my-nick-p new-nick) - (dolist (buf (cons (or circe-server-buffer - (current-buffer)) - (circe-server-chat-buffers))) - (with-current-buffer buf - (circe-display 'circe-format-server-nick-change-self - :old-nick old-nick - :userhost (or userhost "server") - :new-nick new-nick))) - (let ((query-buffer (circe-server-get-chat-buffer old-nick))) - (when query-buffer - (with-current-buffer query-buffer - (circe-server-rename-chat-buffer old-nick new-nick) - (circe-display 'circe-format-server-nick-change - :old-nick old-nick - :new-nick new-nick - :userhost (or userhost "server"))))) - (dolist (buf (circe-user-channels new-nick)) - (with-current-buffer buf - (cond - ((and (circe-reduce-lurker-spam) - (circe-lurker-p new-nick)) - nil) - ((circe-channel-user-nick-regain-p old-nick new-nick) - (circe-display 'circe-format-server-nick-regain - :old-nick old-nick - :new-nick new-nick - :userhost (or userhost "server"))) - (t - (circe-display 'circe-format-server-nick-change - :old-nick old-nick - :new-nick new-nick - :userhost (or userhost "server")))))))) - -(circe-set-display-handler "nickserv.identified" 'circe-display-ignore) - -;; NOTICE is also used to encode CTCP replies. irc.el will send -;; irc.notice events for NOTICEs without CTCP replies, so we show -;; that, not the raw notice. -(circe-set-display-handler "NOTICE" 'circe-display-ignore) -(circe-set-display-handler "irc.notice" 'circe-display-NOTICE) -(defun circe-display-NOTICE (nick userhost _command target text) - "Show a NOTICE message." - (cond - ((not userhost) - (with-current-buffer (circe-server-last-active-buffer) - (circe-display 'circe-format-server-notice - :server nick - :body text))) - ((circe-server-my-nick-p target) - (with-current-buffer (or (circe-server-get-chat-buffer nick) - (circe-server-last-active-buffer)) - (circe-display 'circe-format-notice - :nick nick - :userhost (or userhost "server") - :body text))) - (t - (with-current-buffer (or (circe-server-get-chat-buffer target) - (circe-server-last-active-buffer)) - (circe-display 'circe-format-notice - :nick nick - :userhost (or userhost "server") - :body text))))) - -(circe-set-display-handler "PART" 'circe-display-PART) -(defun circe-display-PART (nick userhost _command channel &optional reason) - "Show a PART message." - (with-current-buffer (or (circe-server-get-chat-buffer channel) - (circe-server-last-active-buffer)) - (when (or (not circe-chat-target) - (not (circe-lurker-p nick))) - (circe-display 'circe-format-server-part - :nick nick - :userhost (or userhost "server") - :channel channel - :reason (or reason "[No reason given]"))))) - -(circe-set-display-handler "PING" 'circe-display-ignore) -(circe-set-display-handler "PONG" 'circe-display-ignore) - -;; PRIVMSG is also used to encode CTCP requests. irc.el will send -;; irc.message events for PRIVMSGs without CTCP messages, so we show -;; that, not the raw message. -(circe-set-display-handler "PRIVMSG" 'circe-display-ignore) -(circe-set-display-handler "irc.message" 'circe-display-PRIVMSG) -(defun circe-display-PRIVMSG (nick userhost _command target text) - "Show a PRIVMSG message." - (cond - ((circe-server-my-nick-p target) - (let ((buf (circe-query-auto-query-buffer nick))) - (if buf - (with-current-buffer buf - (circe-display 'circe-format-say - :nick nick - :userhost (or userhost "server") - :body text)) - (with-current-buffer (circe-server-last-active-buffer) - (circe-display 'circe-format-message - :nick nick - :userhost (or userhost "server") - :body text))))) - (t - (with-current-buffer (circe-server-get-or-create-chat-buffer - target 'circe-channel-mode) - (circe-lurker-display-active nick userhost) - (circe-display 'circe-format-say - :nick nick - :userhost (or userhost "server") - :body text))))) - -(circe-set-display-handler "TOPIC" 'circe-display-topic) -(defun circe-display-topic (nick userhost _command channel new-topic) - "Show a TOPIC change." - (with-current-buffer (circe-server-get-or-create-chat-buffer - channel 'circe-channel-mode) - (let* ((channel-obj (irc-connection-channel (circe-server-process) - channel)) - (old-topic (or (when channel - (irc-channel-last-topic channel-obj)) - ""))) - (circe-display 'circe-format-server-topic - :nick nick - :userhost (or userhost "server") - :channel channel - :new-topic new-topic - :old-topic old-topic - :topic-diff (circe--topic-diff old-topic new-topic))))) - -(defun circe--topic-diff (old new) - "Return a colored topic diff between OLD and NEW." - (mapconcat (lambda (elt) - (cond - ((eq '+ (car elt)) - (let ((s (cadr elt))) - (add-face-text-property 0 (length s) - 'circe-topic-diff-new-face nil s) - s)) - ((eq '- (car elt)) - (let ((s (cadr elt))) - (add-face-text-property 0 (length s) - 'circe-topic-diff-removed-face nil s) - s)) - (t - (cadr elt)))) - (lcs-unified-diff (circe--topic-diff-split old) - (circe--topic-diff-split new) - 'string=) - "")) - -(defun circe--topic-diff-split (str) - "Split STR into a list of components. -The list consists of words and spaces." - (let ((lis nil)) - (with-temp-buffer - (insert str) - (goto-char (point-min)) - (while (< (point) - (point-max)) - (if (or (looking-at "\\w+\\W*") - (looking-at ".\\s-*")) - (progn - (setq lis (cons (match-string 0) - lis)) - (replace-match "")) - (error "Can't happen")))) - (nreverse lis))) - -(circe-set-display-handler "channel.quit" 'circe-display-channel-quit) -(defun circe-display-channel-quit (nick userhost _command channel - &optional reason) - "Show a QUIT message." - (let ((split (circe--netsplit-quit reason nick))) - (with-current-buffer (circe-server-get-or-create-chat-buffer - channel 'circe-channel-mode) - (cond - (split - (when (< (+ split circe-netsplit-delay) - (float-time)) - (circe-display 'circe-format-server-netsplit - :split reason))) - ((not (circe-lurker-p nick)) - (circe-display 'circe-format-server-quit-channel - :nick nick - :userhost (or userhost "server") - :channel channel - :reason (or reason "[no reason given]"))))))) - -(circe-set-display-handler "QUIT" 'circe-display-QUIT) -(defun circe-display-QUIT (nick userhost _command &optional reason) - "Show a QUIT message. - -Channel quits are shown already, so just show quits in queries." - (let ((buf (circe-server-get-chat-buffer nick))) - (when buf - (with-current-buffer buf - (circe-display 'circe-format-server-quit - :nick nick - :userhost (or userhost "server") - :reason (or reason "[no reason given]")))))) - -(defvar circe-netsplit-list nil - "A list of recorded netsplits. -Every item is a list with four elements: -- The quit message for this split. -- The time when last we heard about a join in this split -- The time when last we heard about a quit in this split -- A hash table noting which nicks did leave") -(make-variable-buffer-local 'circe-netsplit-list) - -(defun circe--netsplit-join (nick) - "Search for NICK in the netsplit lists. -This either returns a pair whose car is the quit message of this -split, and the cadr the time we last heard anything of the split -of that user. If the NICK isn't split, this returns nil." - (with-circe-server-buffer - (catch 'return - (dolist (entry circe-netsplit-list) - (let ((table (nth 3 entry))) - (when (gethash nick table) - (let ((name (nth 0 entry)) - (time (nth 1 entry))) - (remhash nick table) - (when (= 0 (hash-table-count table)) - (setq circe-netsplit-list - (delq entry circe-netsplit-list))) - (setcar (cdr entry) - (float-time)) - (throw 'return (list name time)))))) - nil))) - -(defun circe--netsplit-quit (reason nick) - "If REASON indicates a netsplit, mark NICK as splitted. -This either returns the time when last we heard about this split, -or nil when this isn't a split." - (when (circe--netsplit-reason-p reason) - (with-circe-server-buffer - (let ((entry (assoc reason circe-netsplit-list))) - (if entry - (let ((time (nth 2 entry)) - (table (nth 3 entry))) - (setcar (cddr entry) - (float-time)) - (puthash nick nick table) - time) - ;; New split! - (let ((table (make-hash-table :test 'equal))) - (puthash nick nick table) - (setq circe-netsplit-list - (cons (list reason 0 (float-time) table) - circe-netsplit-list)) - 0)))))) - -(defun circe--netsplit-reason-p (reason) - "Return non-nil if REASON is the quit message of a netsplit. -This is true when it contains exactly two hosts, with a single -space in between them. The hosts must include at least one dot, -and must not include colons or slashes (else they might be -URLs). (Thanks to irssi for this criteria list)" - (if (string-match "^[^ :/]+\\.[^ :/]* [^ :/]+\\.[^ :/]*$" - reason) - t - nil)) - -(let ((simple-format-specifiers - '(("INVITE" active "Invite: {origin} invites you to {1}") - ("KICK" 0 "Kick: {1} kicked by {origin}: {2}") - ("ERROR" active "Error: {0-}") - ("001" server "{1}") - ("002" server "{1}") - ("003" server "{1}") - ("004" server "{1-}") - ("005" server "{1-}") - ;; IRCnet: * Please wait while we process your connection. - ("020" server "{0-}") - ;; IRCnet - ("042" server "Your unique ID is {1}") - ("200" active "{1-}") - ("201" active "{1-}") - ("203" active "{1-}") - ("204" active "{1-}") - ("205" active "{1-}") - ("206" active "{1-}") - ("207" active "{1-}") - ("208" active "{1-}") - ("209" active "{1-}") - ("211" active "{1-}") - ("212" active "{1-}") - ("219" active "{1-}") - ("221" active "User mode: {1-}") - ("234" active "Service: {1-}") - ("235" active "{1-}") - ("242" active "{1}") - ("243" active "{1-}") - ("250" server "{1}") - ("251" server "{1}") - ("252" server "{1-}") - ("253" server "{1-}") - ("254" server "{1-}") - ("255" server "{1}") - ("256" active "{1-}") - ("257" active "{1}") - ("258" active "{1}") - ("259" active "{1}") - ("261" active "{1-}") - ("262" active "{1-}") - ("263" active "{1-}") - ("265" server "{1-}") - ("266" server "{1-}") - ;; This is returned on both WHOIS and PRIVMSG. It - ;; should go to the active window for the former, and - ;; the query window for the latter. Oh well. - ("301" active "User away: {1}") - ("302" active "User hosts: {1}") - ("303" active "Users online: {1}") - ("305" active "{1}") - ("306" active "{1}") - ("307" active "{1-}") - ;; Coldfront: 310 is available for help. - ("310" active "{1-}") - ("311" active "{1} is {2}@{3} ({5})") - ("312" active "{1} is on {2} ({3})") - ("313" active "{1} {2}") - ("314" active "{1} was {2}@{3} ({5})") - ("315" active "{2}") - ("318" active "{2}") - ("319" active "{1} is on {2}") - ("320" active "{1-}") - ("322" active "{1-}") - ("323" active "{1-}") - ("324" 1 "Channel mode for {1}: {2-}") - ("325" 1 "Unique operator on {1} is {2}") - ("328" 1 "Channel homepage for {1}: {2-}") - ("330" active "{1} is logged in as {2}") - ("331" 1 "No topic for {1} set") - ("332" 1 "Topic for {1}: {2}") - ("341" active "Inviting {1} to {2}") - ("346" 1 "Invite mask: {2}") - ("347" 1 "{2}") - ("348" 1 "Except mask: {2}") - ("349" 1 "{2}") - ("351" active "{1-}") - ("352" active "{5} ({2}@{3}) in {1} on {4}: {6-}") - ("353" 2 "Names: {3}") - ("364" active "{1-}") - ("365" active "{1-}") - ("366" 1 "{2}") - ("367" 1 "Ban mask: {2}") - ("368" 1 "{2}") - ("369" active "{1} {2}") - ("371" active "{1}") - ("372" server "{1}") - ("374" active "{1}") - ("375" server "{1}") - ("376" server "{1}") - ("378" active "{1-}") - ("381" active "{1}") - ("382" active "{1-}") - ("391" active "Time on {1}: {2}") - ("401" active "No such nick: {1}") - ("402" active "No such server: {1}") - ("403" active "No such channel: {1}") - ("404" 1 "Can not send to channel {1}") - ("405" active "Can not join {1}: {2}") - ("406" active "{1-}") - ("407" active "{1-}") - ("408" active "No such service: {1}") - ("422" active "{1}") - ("432" active "Erroneous nick name: {1}") - ("433" active "Nick name in use: {1}") - ("437" active "Nick/channel is temporarily unavailable: {1}") - ("441" 2 "User not on channel: {1}") - ("442" active "You are not on {1}") - ("443" 2 "User {1} is already on channel {2}") - ;; Coldfront: 451 * :You have not registered - ("451" active "{1-}") - ("467" 1 "{2}") - ("470" 1 "{1} made you join {2}: {3-}") - ("471" 1 "{2}") - ("472" active "{1-}") - ("473" active "{1-}") - ("474" active "{1-}") - ("475" active "{1-}") - ("476" active "{1-}") - ("477" active "{1-}") - ("481" 1 "{2-}") - ("484" active "{1-}") - ;; Coldfront: 671 is using a Secure Connection - ("671" active "{1-}") - ("728" 1 "Quiet mask: {3}") - ("729" 1 "{3-}") - ;; Freenode SASL auth - ("900" active "SASL: {3-}") - ("903" active "{1-}")))) - (dolist (fmt simple-format-specifiers) - (circe-set-display-handler (car fmt) (cdr fmt)))) - -(defun circe-set-message-target (command target) - "Set the target of COMMAND to TARGET. - -This can be used to change format-based display handlers more -easily." - (let ((handler (circe-get-display-handler command))) - (when (not (consp handler)) - (error "Handler of command %s is not a list" command)) - (setcar handler target))) - -;;;;;;;;;;;;;;;;;;;;;;;; -;;; Helper Functions ;;; -;;;;;;;;;;;;;;;;;;;;;;;; - -(defun circe--list-drop-right (list pattern) - "Drop elements from the right of LIST that match PATTERN. - -LIST should be a list of strings, and PATTERN is used as a -regular expression." - (let ((list (reverse list))) - (while (and list - (string-match pattern (car list))) - (setq list (cdr list))) - (nreverse list))) - -(defun circe--nick-next (oldnick) - "Return a new nick to try for OLDNICK." - (cond - ;; If the nick ends with -+, replace those with _ - ((string-match "^\\(.*[^-]\\)\\(-+\\)$" oldnick) - (concat (match-string 1 oldnick) - (make-string (- (match-end 2) - (match-beginning 2)) - ?_))) - ;; If the nick is 9 chars long, take prefix and rotate. - ((>= (length oldnick) - 9) - (when (string-match "^\\(.*[^-_]\\)[-_]*$" oldnick) - (let ((nick (match-string 1 oldnick))) - (concat (substring nick 1) - (string (aref nick 0)))))) - ;; If the nick ends with _+ replace those with - and add one - ((string-match "^\\(.*[^_]\\)\\(_+\\)$" oldnick) - (concat (match-string 1 oldnick) - (make-string (- (match-end 2) - (match-beginning 2)) - ?-) - "-")) - ;; Else, just append - - (t - (concat oldnick "-")))) - -(defun circe-duration-string (duration) - "Return a description of a DURATION in seconds." - (let ((parts `((,(* 12 30 24 60 60) "year") - (,(* 30 24 60 60) "month") - (,(* 24 60 60) "day") - (,(* 60 60) "hour") - (60 "minute") - (1 "second"))) - (duration (round duration)) - (result nil)) - (dolist (part parts) - (let* ((seconds-per-part (car part)) - (description (cadr part)) - (count (/ duration seconds-per-part))) - (when (not (zerop count)) - (setq result (cons (format "%d %s%s" - count description - (if (= count 1) "" "s")) - result))) - (setq duration (- duration (* count seconds-per-part))))) - (if result - (mapconcat #'identity - (nreverse result) - " ") - "a moment"))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Deprecated functions and variables - -(define-obsolete-function-alias 'circe-server-nick 'circe-nick - "Circe 2.0") - -(define-obsolete-function-alias 'circe-server-message - 'circe-display-server-message - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-networks 'circe-network-defaults - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-name 'circe-host - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-service 'circe-port - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-network 'circe-network - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-ip-family 'circe-ip-family - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-nick 'circe-nick - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-user 'circe-user - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-pass 'circe-pass - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-realname 'circe-realname - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-use-tls 'circe-use-tls - "Circe 2.0") - -(define-obsolete-variable-alias 'circe-server-auto-join-channels - 'circe-channels - "Circe 2.0") - -(provide 'circe) -;;; circe.el ends here diff --git a/elpa/circe-20160608.1315/irc.el b/elpa/circe-20160608.1315/irc.el deleted file mode 100644 index d830e02..0000000 --- a/elpa/circe-20160608.1315/irc.el +++ /dev/null @@ -1,1406 +0,0 @@ -;;; irc.el --- Library to handle IRC connections -*- lexical-binding: t -*- - -;; Copyright (C) 2015 Jorgen Schaefer - -;; Author: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; The main entry function is `irc-connect'. This creates a new -;; connection to an IRC server, and also takes an event handler table -;; which is used to run various event handlers. Handlers receive a -;; connection object which can be used for other API calls. - -;; IRC connection objects also accept connection options. These can be -;; queried using `irc-connection-get', and are set by `irc-connect' or -;; later using `irc-connection-put'. - -;; Event handler tables are simple maps of names to functions. See -;; `irc-handler-table', `irc-handler-add' and `irc-handler-run' for -;; the API. - -;; To send commands to the server, use `irc-send-raw' or -;; `irc-send-command'. - -;; The rest of the library are handler packs that add support for -;; various IRC features. - -;;; Code: - -(require 'cl-lib) -(require 'make-tls-process) - -(defvar irc-debug-log nil - "Emit protocol debug info if this is non-nil.") - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Connection function - -(defun irc-connect (&rest keywords) - "Connect to an IRC server. - -Supported keyword arguments: - -:name NAME -- The name for the process -:host HOST -- The host to connect to -:service SERVICE -- The service or port to connect to -:tls BOOL -- Whether to use TLS -:family IP-FAMILY -- Force using of ipv4 or ipv6 -:handler-table HANDLER -- The event handler table to send events to. - -The following events are supported: - -conn.connected conn -- The connection was established -conn.failed conn -- The connection could not be established -conn.disconnected conn -- A previously established connection was lost - -NNN conn sender args... -- A numeric reply from IRC was received -COMMAND conn sender args... -- An IRC command message was received" - (let ((proc (funcall (if (plist-get keywords :tls) - #'make-tls-process - #'make-network-process) - :name (or (plist-get keywords :name) - (plist-get keywords :host)) - :host (or (plist-get keywords :host) - (error "Must specify a :host to connect to")) - :service (or (plist-get keywords :service) - (error "Must specify a :service to connect to")) - :family (plist-get keywords :family) - :coding 'no-conversion - :nowait (featurep 'make-network-process '(:nowait t)) - :noquery t - :filter #'irc--filter - :sentinel #'irc--sentinel - :plist keywords - :keepalive t))) - ;; When we used `make-network-process' without :nowait, the - ;; sentinel is not called with the open event, so we do this - ;; manually. - (when (eq (process-status proc) 'open) - (irc--sentinel proc "open manually")) - proc)) - -(defun irc-connection-get (conn propname) - "Return the value of CONN's PROPNAME property." - (process-get conn propname)) - -(defun irc-connection-put (conn propname value) - "Change CONN's PROPNAME property to VALUE." - (process-put conn propname value)) - -(defun irc--sentinel (proc event) - (cond - ((string-match "\\`failed" event) - (irc-event-emit proc "conn.failed")) - ((string-match "\\`open" event) - (irc-event-emit proc "conn.connected")) - ((string-match "\\`\\(connection broken\\|finished\\|exited abnormally\\)" - event) - (irc-event-emit proc "conn.disconnected")) - ((string-match "\\`\\(deleted\\|killed\\)" event) - nil) - (t - (error "Unknown event in IRC sentinel: %S" event)))) - -(defvar irc--filter-running-p nil - "Non-nil when we're currently processing a message. - -Yep, this is a mutex. Why would one need a mutex in Emacs, a -single-threaded application, you ask? Easy! - -When, during the execution of a process filter, any piece of code -waits for process output - e.g. because they started a some -external program - Emacs will process any input from external -processes. Including the one for the filter that is currently -running. - -If that process does emit output, the filter is run again, while -it is already running. If the filter is not careful, this can -cause data to arrive out of order, or get lost.") - -(defun irc--filter (proc data) - "Handle data from the process." - (irc-connection-put proc :conn-data - (concat (or (irc-connection-get proc :conn-data) - "") - data)) - (when (not irc--filter-running-p) - (let ((irc--filter-running-p t) - (data (irc-connection-get proc :conn-data))) - (while (string-match "\r?\n" data) - (let ((line (substring data 0 (match-beginning 0)))) - (setq data (substring data (match-end 0))) - (irc-connection-put proc :conn-data data) - (irc--handle-line proc line) - (setq data (irc-connection-get proc :conn-data))))))) - -(defun irc--handle-line (proc line) - "Handle a single line from the IRC server. - -The command is simply passed to the event handler of the IRC -connection." - (irc-debug-out proc "S: %s" line) - (let* ((parsed (irc--parse line)) - (sender (car parsed)) - (command (cadr parsed)) - (args (cddr parsed))) - (apply #'irc-event-emit proc command sender args))) - -(defun irc--parse (line) - "Parse a line from IRC. - -Returns a list: (sender command args...) - -A line from IRC is a space-separated list of arguments. If the -first word starts with a colon, that's the sender. The first or -second word is the command. All further words are arguments. The -first word to start with a colon ends the argument list. - -Examples: - -COMMAND -COMMAND arg -COMMAND arg1 arg2 -COMMAND arg1 arg2 :arg3 still arg3 -:sender COMMAND arg1 arg2 :arg3 still arg3" - (with-temp-buffer - (insert line) - (goto-char (point-min)) - (let ((sender nil) - (args nil)) - ;; Optional sender. - (when (looking-at ":\\([^ ]+\\) ") - (setq sender (decode-coding-string - (match-string 1) - 'undecided)) - (goto-char (match-end 0))) - - ;; COMMAND. - (unless (looking-at "\\([^ ]+\\)") - (error "Invalid message: %s" line)) - (push (decode-coding-string (match-string 1) 'undecided) - args) - (goto-char (match-end 0)) - - ;; Arguments. - (while (re-search-forward " :\\(.*\\)\\| \\([^ ]*\\)" nil t) - (push (decode-coding-string - (or (match-string 1) - (match-string 2)) - 'undecided) - args)) - - (cons sender (nreverse args))))) - -(defun irc-userstring-nick (userstring) - "Return the nick in a given USERSTRING. - -USERSTRING is a typical nick!user@host prefix as used by IRC." - (if (string-match "\\`\\([^!]+\\)!\\([^@]+\\)@\\(.*\\)\\'" userstring) - (match-string 1 userstring) - userstring)) - -(defun irc-userstring-userhost (userstring) - "Return the nick in a given USERSTRING. - -USERSTRING is a typical nick!user@host prefix as used by IRC." - (if (string-match "\\`\\([^!]+\\)!\\([^@]+@.*\\)\\'" userstring) - (match-string 2 userstring) - nil)) - -(defun irc-event-emit (conn event &rest args) - "Run the event handlers for EVENT in CONN with ARGS." - (irc-debug-out conn - "E: %S %s" - event - (mapconcat (lambda (elt) (format "%S" elt)) - args - " ")) - (let ((handler-table (irc-connection-get conn :handler-table))) - (when handler-table - (apply #'irc-handler-run handler-table event conn event args) - (apply #'irc-handler-run handler-table nil conn event args)))) - -;;;;;;;;;;;;;;;;;;;;;;; -;;; Event handler table - -(defun irc-handler-table () - "Return a new event handler table." - (make-hash-table :test 'equal)) - -(defun irc-handler-add (table event handler) - "Add HANDLER for EVENT to the event handler table TABLE." - (puthash event - (append (gethash event table) - (list handler)) - table)) - -(defun irc-handler-remove (table event handler) - "Remove HANDLER for EVENT to the event handler table TABLE." - (puthash event - (delete handler - (gethash event table)) - table)) - -(defun irc-handler-run (table event &rest args) - "Run the handlers for EVENT in TABLE, passing ARGS to each." - (dolist (handler (gethash event table)) - (if debug-on-error - (apply handler args) - (condition-case err - (apply handler args) - (error - (message "Error running event %S handler %S: %s (args were %S)" - event handler err args)))))) - -;;;;;;;;;;; -;;; Sending - -(defun irc-send-raw (conn line &optional flood-handling) - "Send a line LINE to the IRC connection CONN. - -LINE should not include the trailing newline. - -FLOOD-HANDLING defines how to handle the situation when we are -sending too much data. It can have three values: - -nil -- Add the message to a queue and send it later -:nowait -- Send the message immediately, circumventing flood protection -:drop -- Send the message only if we are not flooding, and drop it if - we have queued up messages. - -The flood protection algorithm works like the one detailed in RFC -2813, section 5.8 \"Flood control of clients\". - - * If `flood-last-message' is less than the current - time, set it equal. - * While `flood-last-message' is less than `flood-margin' - seconds ahead of the current time, send a message, and - increase `flood-last-message' by `flood-penalty'." - (cond - ((null flood-handling) - (irc-connection-put conn - :flood-queue - (append (irc-connection-get conn :flood-queue) - (list line))) - (irc-send--queue conn)) - ((eq flood-handling :nowait) - (irc-send--internal conn line)) - ((eq flood-handling :drop) - (let ((queue (irc-connection-get conn :flood-queue))) - (when (not queue) - (irc-connection-put conn :flood-queue (list line)) - (irc-send--queue conn)))))) - -(defun irc-send--queue (conn) - "Send messages from the flood queue in CONN. - -See `irc-send-raw' for the algorithm." - (let ((queue (irc-connection-get conn :flood-queue)) - (last-message (or (irc-connection-get conn :flood-last-message) - 0)) - (margin (or (irc-connection-get conn :flood-margin) - 10)) - (penalty (or (irc-connection-get conn :flood-penalty) - 3)) - (now (float-time))) - (when (< last-message now) - (setq last-message now)) - (while (and queue - (< last-message (+ now margin))) - (irc-send--internal conn (car queue)) - (setq queue (cdr queue) - last-message (+ last-message penalty))) - (irc-connection-put conn :flood-queue queue) - (irc-connection-put conn :flood-last-message last-message) - (let ((timer (irc-connection-get conn :flood-timer))) - (when timer - (cancel-timer timer) - (irc-connection-put conn :flood-timer nil)) - (when queue - (irc-connection-put conn - :flood-timer - (run-at-time 1 nil #'irc-send--queue conn)))))) - -(defun irc-send--internal (conn line) - "Send LINE to CONN." - (irc-debug-out conn "C: %s" line) - (process-send-string conn - (concat (encode-coding-string line 'utf-8) - "\r\n"))) - -(defun irc-send-command (conn command &rest args) - "Send COMMAND with ARGS to IRC connection CONN." - (irc-send-raw conn (apply #'irc--format-command command args))) - -(defun irc--format-command (command &rest args) - "Format COMMAND and ARGS for IRC. - -The last value in ARGS will be escaped with a leading colon if it -contains a space. All other arguments are checked to make sure -they do not contain a space." - (dolist (arg (cons command args)) - (when (not (stringp arg)) - (error "Argument must be a string"))) - (let* ((prefix (cons command (butlast args))) - (last (last args))) - (dolist (arg prefix) - (when (string-match " " arg) - (error "IRC protocol error: Argument %S must not contain space" - arg))) - (when (and last (or (string-match " " (car last)) - (string-match "^:" (car last)) - (equal "" (car last)))) - (setcar last (concat ":" (car last)))) - (mapconcat #'identity - (append prefix last) - " "))) - -(defun irc-send-AUTHENTICATE (conn arg) - "Send an AUTHENTICATE message with ARG. - -See https://github.com/atheme/charybdis/blob/master/doc/sasl.txt -for details." - (irc-send-command conn "AUTHENTICATE" arg)) - -(defun irc-send-AWAY (conn &optional reason) - "Mark yourself as AWAY with reason REASON, or back if reason is nil." - (if reason - (irc-send-command conn "AWAY" reason) - (irc-send-command conn "AWAY"))) - -(defun irc-send-CAP (conn &rest args) - "Send a CAP message. - -See https://tools.ietf.org/html/draft-mitchell-irc-capabilities-01 -for details." - (apply #'irc-send-command conn "CAP" args)) - -(defun irc-send-INVITE (conn nick channel) - "Invite NICK to CHANNEL." - (irc-send-command conn "INVITE" nick channel)) - -(defun irc-send-JOIN (conn channel &optional key) - "Join CHANNEL. - -If KEY is given, use it to join the password-protected channel." - (if key - (irc-send-command conn "JOIN" channel key) - (irc-send-command conn "JOIN" channel))) - -(defun irc-send-NAMES (conn &optional channel) - "Retrieve user names from the server, optionally limited to CHANNEL." - (if channel - (irc-send-command conn "NAMES" channel) - (irc-send-command conn "NAMES"))) - -(defun irc-send-NICK (conn nick) - "Change your own nick to NICK." - (irc-send-command conn "NICK" nick)) - -(defun irc-send-NOTICE (conn msgtarget text-to-be-sent) - "Send a private notice containing TEXT-TO-BE-SENT to MSGTARGET. - -MSGTARGET can be either a nick or a channel." - (irc-send-command conn "NOTICE" msgtarget text-to-be-sent)) - -(defun irc-send-PART (conn channel reason) - "Leave CHANNEL with reason REASON." - (irc-send-command conn "PART" channel reason)) - -(defun irc-send-PASS (conn password) - "Authenticate to the server using PASSWORD." - (irc-send-command conn "PASS" password)) - -(defun irc-send-PONG (conn server) - "Respond to a PING message." - (irc-send-raw conn - (irc--format-command "PONG" server) - :nowait)) - -(defun irc-send-PRIVMSG (conn msgtarget text-to-be-sent) - "Send a private message containing TEXT-TO-BE-SENT to MSGTARGET. - -MSGTARGET can be either a nick or a channel." - (irc-send-command conn "PRIVMSG" msgtarget text-to-be-sent)) - -(defun irc-send-QUIT (conn reason) - "Leave IRC with reason REASON." - (irc-send-command conn "QUIT" reason)) - -(defun irc-send-TOPIC (conn channel &optional new-topic) - "Retrieve or set the topic of CHANNEL - -If NEW-TOPIC is given, set this as the new topic. If it is -omitted, retrieve the current topic." - (if new-topic - (irc-send-command conn "TOPIC" channel new-topic) - (irc-send-command conn "TOPIC" channel))) - -(defun irc-send-USER (conn user mode realname) - "Send a USER message for registration. - -MODE should be an integer as per RFC 2812" - (irc-send-command conn "USER" user (format "%s" mode) "*" realname)) - -(defun irc-send-WHOIS (conn target &optional server-or-name) - "Retrieve current whois information on TARGET." - (if server-or-name - (irc-send-command conn "WHOIS" target server-or-name) - (irc-send-command conn "WHOIS" target))) - -(defun irc-send-WHOWAS (conn target) - "Retrieve past whois information on TARGET." - (irc-send-command conn "WHOWAS" target)) - -;;;;;;;;;;;;;;; -;;; Debug stuff - -(defun irc-debug-out (conn fmt &rest args) - (when irc-debug-log - (let ((name (format "*IRC Protocol %s:%s*" - (irc-connection-get conn :host) - (irc-connection-get conn :service)))) - (with-current-buffer (get-buffer-create name) - (save-excursion - (goto-char (point-max)) - (insert (apply #'format fmt args) "\n")))))) - -;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Registration - -(defun irc-handle-registration (table) - "Add command handlers to TABLE to handle registration. - -This will send the usual startup messages after we are connected. - -Events emitted: - -\"irc.registered\" current-nick -- We have successfully - registered with the IRC server. Most commands can be used now. - In particular, joining channels is only possible now. - -\"sasl.login\" nick!user@host account -- SASL log in was - successful. - -Connection options used: - -:nick -- The nick to use to register with the server -:user -- The user name to use -:mode -- The initial mode to use; an integer. See RFC 2812 for - the meaning. -:realname -- The realname to use for the registration -:pass -- The server password to send -:cap-req -- CAP protocol capabilities to request, if available -:sasl-username -- The SASL username to send, if sasl is available -:sasl-password -- The SASL password to send, if sasl is available - -Connection options set: - -:connection-state -- One of nil, connected, registered, disconnected - See `irc-connection-state' for an interface to this. -:cap-supported-p -- Non-nil if the server supports the CAP protocol -:cap-ack -- The list of active capabilities negotiated with the server" - (irc-handler-add table "conn.connected" - #'irc-handle-registration--connected) - (irc-handler-add table "conn.disconnected" - #'irc-handle-registration--disconnected) - (irc-handler-add table "001" ;; RPL_WELCOME - #'irc-handle-registration--rpl-welcome) - (irc-handler-add table "CAP" - #'irc-handle-registration--cap) - (irc-handler-add table "AUTHENTICATE" - #'irc-handle-registration--authenticate) - (irc-handler-add table "900" ;; RPL_LOGGEDIN - #'irc-handle-registration--logged-in)) - -(defun irc-handle-registration--connected (conn _event) - (irc-connection-put conn :connection-state 'connected) - (when (irc-connection-get conn :cap-req) - (irc-send-CAP conn "LS")) - (let ((password (irc-connection-get conn :pass))) - (when password - (irc-send-PASS conn password))) - (irc-send-NICK conn (irc-connection-get conn :nick)) - (irc-send-USER conn - (irc-connection-get conn :user) - (irc-connection-get conn :mode) - (irc-connection-get conn :realname))) - -(defun irc-handle-registration--disconnected (conn _event) - (irc-connection-put conn :connection-state 'disconnected)) - -(defun irc-handle-registration--rpl-welcome (conn _event _sender target - &rest ignored) - (irc-connection-put conn :connection-state 'registered) - (irc-event-emit conn "irc.registered" target)) - -(defun irc-handle-registration--cap (conn _event _sender _target - subcommand arg) - (cond - ((equal subcommand "LS") - (let ((supported (split-string arg)) - (wanted nil)) - (dolist (cap (irc-connection-get conn :cap-req)) - (when (member cap supported) - (setq wanted (append wanted (list cap))))) - (if wanted - (irc-send-CAP conn "REQ" (mapconcat #'identity wanted " ")) - (irc-send-CAP conn "END")))) - ((equal subcommand "ACK") - (let ((acked (split-string arg))) - (irc-connection-put conn :cap-ack acked) - (if (and (member "sasl" acked) - (irc-connection-get conn :sasl-username) - (irc-connection-get conn :sasl-password)) - (irc-send-AUTHENTICATE conn "PLAIN") - (irc-send-CAP conn "END")))) - (t - (message "Unknown CAP response from server: %s %s" subcommand arg)))) - -(defun irc-handle-registration--authenticate (conn _event _sender arg) - (if (equal arg "+") - (let ((username (irc-connection-get conn :sasl-username)) - (password (irc-connection-get conn :sasl-password))) - (irc-send-AUTHENTICATE conn (base64-encode-string - (format "%s\x00%s\x00%s" - username username password))) - (irc-send-CAP conn "END")) - (message "Unknown AUTHENTICATE response from server: %s" arg))) - -(defun irc-handle-registration--logged-in (conn _event _sender _target - userhost account _message) - (irc-event-emit conn "sasl.login" userhost account)) - -(defun irc-connection-state (conn) - "connecting connected registered disconnected" - (let ((state (irc-connection-get conn :connection-state))) - (if (null state) - 'connecting - state))) - -;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Ping-Pong - -(defun irc-handle-ping-pong (table) - "Add command handlers to respond to PING requests." - (irc-handler-add table "PING" #'irc-handle-ping-pong--ping)) - -(defun irc-handle-ping-pong--ping (conn _event _sender argument) - (irc-send-PONG conn argument)) - -;;;;;;;;;;;;;;;;;;;;; -;;; Handler: ISUPPORT - -(defun irc-handle-isupport (table) - "Add command handlers to track 005 RPL_ISUPPORT capabilities." - (irc-handler-add table "005" #'irc-handle-isupport--005)) - -(defun irc-handle-isupport--005 (conn _event _sender _target &rest args) - (irc-connection-put - conn :isupport - (append (irc-connection-get conn :isupport) - (irc-handle-isupport--capabilities-to-alist args)))) - -(defun irc-handle-isupport--capabilities-to-alist (capabilities) - (mapcar (lambda (cap) - (if (string-match "\\`\\([^=]+\\)=\\(.*\\)\\'" cap) - (cons (match-string 1 cap) - (match-string 2 cap)) - (cons cap t))) - capabilities)) - -(defun irc-isupport (conn capability) - "Return the value of CAPABILITY of CONN. - -These capabilities are set when the server sends a 005 -RPL_ISUPPORT message. The return value is either the value of the -capability, or t if it is a boolean capability that is present. -If the capability is not present, the return value is nil." - (cdr (assoc capability - (irc-connection-get conn :isupport)))) - -(defun irc-string-equal-p (conn s1 s2) - "Compare S1 to S2 case-insensitively. - -What case means is defined by the server of CONN." - (equal (irc-isupport--case-fold conn s1) - (irc-isupport--case-fold conn s2))) - -(defvar irc-isupport--ascii-table - (let ((table (make-string 128 0)) - (char 0)) - (while (<= char 127) - (if (and (<= ?A char) - (<= char ?Z)) - (aset table char (+ char (- ?a ?A))) - (aset table char char)) - (setq char (1+ char))) - table) - "A case mapping table for the ascii CASEMAPPING.") - -(defvar irc-isupport--rfc1459-table - (let ((table (concat irc-isupport--ascii-table))) ; copy string - (aset table ?\[ ?\{) - (aset table ?\] ?\}) - (aset table ?\\ ?\|) - (aset table ?^ ?\~) - table) - "A case mapping table for the rfc1459 CASEMAPPING.") - -(defvar irc-isupport--rfc1459-strict-table - (let ((table (concat irc-isupport--ascii-table))) ; copy string - (aset table ?\[ ?\{) - (aset table ?\] ?\}) - (aset table ?\\ ?\|) - table) - "A case mapping table for the rfc1459-strict CASEMAPPING.") - -(defun irc-isupport--case-fold (conn s) - "Translate S to be a lower-case. - -This uses the case mapping defined by the IRC server for CONN." - (with-temp-buffer - (insert s) - (let ((mapping (or (irc-isupport conn "CASEMAPPING") - "rfc1459"))) - (cond - ((equal mapping "rfc1459") - (translate-region (point-min) - (point-max) - irc-isupport--rfc1459-table)) - ((equal mapping "ascii") - (translate-region (point-min) - (point-max) - irc-isupport--ascii-table)) - ((equal mapping "rfc1459-strict") - (translate-region (point-min) - (point-max) - irc-isupport--rfc1459-strict-table)))) - (buffer-string))) - -(defun irc-channel-name-p (conn string) - "True iff STRING is a valid channel name for CONN. - -This depends on the CHANTYPES setting set by the server of CONN." - (let ((chantypes (string-to-list - (or (irc-isupport conn "CHANTYPES") - "#")))) - (if (and (> (length string) 0) - (member (aref string 0) chantypes)) - t - nil))) - -(defun irc-nick-without-prefix (conn nick) - "Return NICK without any mode prefixes. - -For example, a user with op status might be shown as @Nick. This -function would return Nick without the prefix. This uses the 005 -RPL_ISUPPORT setting of PREFIX set by the IRC server for CONN." - (let ((prefixes (irc-connection-get conn :nick-prefixes))) - (when (not prefixes) - (let ((prefix-string (or (irc-isupport conn "PREFIX") - "(qaohv)~&@%+"))) - (setq prefixes (string-to-list - (if (string-match "(.*)\\(.*\\)" prefix-string) - (match-string 1 prefix-string) - "~&@%+"))) - (irc-connection-put conn :nick-prefixes prefixes))) - (while (and (> (length nick) 0) - (member (aref nick 0) prefixes)) - (setq nick (substring nick 1))) - nick)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Initial nick acquisition - -(defun irc-handle-initial-nick-acquisition (table) - "Track the current nick of the user. - -Connection options used: - -:nick-alternatives -- A list of nicks to try if the first attempt - does not succeed." - (irc-handler-add table "432" ;; ERR_ERRONEUSNICKNAME - #'irc-handle-initial-nick-acquisition--get-initial-nick) - (irc-handler-add table "433" ;; ERR_NICKNAMEINUSE - #'irc-handle-initial-nick-acquisition--get-initial-nick) - (irc-handler-add table "437" ;; ERR_UNAVAILRESOURCE - #'irc-handle-initial-nick-acquisition--get-initial-nick)) - -(defun irc-handle-initial-nick-acquisition--get-initial-nick - (conn _event _sender current-nick _attempted-nick _reason) - (when (equal current-nick "*") - (let ((alternatives (irc-connection-get conn :nick-alternatives))) - (if (not alternatives) - (irc-send-NICK conn (irc-generate-nick)) - (irc-connection-put conn :nick-alternatives (cdr alternatives)) - (irc-send-NICK conn (car alternatives)))))) - -(defun irc-generate-nick () - "Return a random, valid IRC nick name. - -Valid nick names are at least (RFC 1459): - - ::= { | | } - ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'" - (let ((chars "abcdefghijklmnopqrstuvwxyz")) - (mapconcat (lambda (_) - (make-string 1 (aref chars (random (length chars))))) - (make-string 9 0) - ""))) - -;;;;;;;;;;;;;;;;; -;;; Handler: CTCP - -(defun irc-handle-ctcp (table) - "Add command handlers to TABLE to handle the CTCP protocol. - -Connection options used: - -:ctcp-version -- The response to a CTCP VERSION request. -:ctcp-clientinfo -- The response to a CTCP CLIENTINFO request. -:ctcp-source -- The response to a CTCP SOURCE request. - -Events emitted: - -\"irc.message\" sender target body -- A non-CTCP PRIVMSG -\"irc.notice\" sender target body -- A non-CTCP NOTICE -\"irc.ctcp\" sender target verb argument -- A CTCP request. ARGUMENT - can be nil if there was no argument, or the empty string if the - argument was empty. -\"irc.ctcpreply\" sender target verb argument -- A CTCP reply. - ARGUMENT is similar to above. -\"irc.ctcp.VERB\" sender target argument -- A CTCP request of - this specific type. -\"irc.ctcpreply.VERB\" sender target argument -- A CTCP reply of - this specific type." - (irc-handler-add table "PRIVMSG" - #'irc-handle-ctcp--privmsg) - (irc-handler-add table "irc.ctcp" - #'irc-handle-ctcp--ctcp) - (irc-handler-add table "NOTICE" - #'irc-handle-ctcp--notice) - (irc-handler-add table "irc.ctcpreply" - #'irc-handle-ctcp--ctcpreply) - (irc-handler-add table "irc.ctcp.VERSION" - #'irc-handle-ctcp--ctcp-version) - (irc-handler-add table "irc.ctcp.CLIENTINFO" - #'irc-handle-ctcp--ctcp-clientinfo) - (irc-handler-add table "irc.ctcp.SOURCE" - #'irc-handle-ctcp--ctcp-source) - (irc-handler-add table "irc.ctcp.PING" - #'irc-handle-ctcp--ctcp-ping) - (irc-handler-add table "irc.ctcp.TIME" - #'irc-handle-ctcp--ctcp-time) - ) - -(defun irc-handle-ctcp--privmsg (conn _event sender target body) - (if (string-match "\\`\x01\\([^ ]+\\)\\(?: \\(.*\\)\\)?\x01\\'" - body) - (irc-event-emit conn "irc.ctcp" sender target - (match-string 1 body) - (match-string 2 body)) - (irc-event-emit conn "irc.message" sender target body))) - -(defun irc-handle-ctcp--ctcp (conn _event sender target verb argument) - (irc-event-emit conn - (format "irc.ctcp.%s" (upcase verb)) - sender - target - argument)) - -(defun irc-handle-ctcp--notice (conn _event sender target body) - (if (string-match "\\`\x01\\([^ ]+\\)\\(?: \\(.*\\)\\)?\x01\\'" - body) - (irc-event-emit conn "irc.ctcpreply" sender target - (match-string 1 body) - (match-string 2 body)) - (irc-event-emit conn "irc.notice" sender target body))) - -(defun irc-handle-ctcp--ctcpreply (conn _event sender target verb argument) - (irc-event-emit conn - (format "irc.ctcpreply.%s" (upcase verb)) - sender - target - argument)) - -(defun irc-handle-ctcp--ctcp-version (conn _event sender _target _argument) - (let ((version (irc-connection-get conn :ctcp-version))) - (when version - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "VERSION" - version)))) - -(defun irc-handle-ctcp--ctcp-clientinfo (conn _event sender _target _argument) - (let ((clientinfo (irc-connection-get conn :ctcp-clientinfo))) - (when clientinfo - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "CLIENTINFO" - clientinfo)))) - -(defun irc-handle-ctcp--ctcp-source (conn _event sender _target _argument) - (let ((source (irc-connection-get conn :ctcp-source))) - (when source - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "SOURCE" - source)))) - -(defun irc-handle-ctcp--ctcp-ping (conn _event sender _target argument) - (when argument - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "PING" - argument))) - -(defun irc-handle-ctcp--ctcp-time (conn _event sender _target _argument) - (irc-send-ctcpreply conn - (irc-userstring-nick sender) - "TIME" - (current-time-string))) - -(defun irc-send-ctcp (conn target verb &optional argument) - "Send a CTCP VERB request to TARGET, optionally with ARGUMENT." - (irc-send-PRIVMSG conn - target - (format "\x01%s%s\x01" - verb - (if argument - (concat " " argument) - "")))) - -(defun irc-send-ctcpreply (conn target verb &optional argument) - "Send a CTCP VERB reply to TARGET, optionally with ARGUMENT." - (irc-send-raw conn - (irc--format-command "NOTICE" - target - (format "\x01%s%s\x01" - verb - (if argument - (concat " " argument) - ""))) - :drop)) - -;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: State tracking - -(defun irc-handle-state-tracking (table) - "Add command handlers to TABLE to track the IRC state. - -Connection options used: - -:current-nick -- The current nick, or nil if not known/set yet. - -Use helper functions to access the information tracked by this -handler: - -- `irc-current-nick' -- `irc-current-nick-p' - -Events emitted: - -\"channel.quit\" sender channel reason -- A user quit IRC and - left this channel that way." - (irc-handler-add table "001" ;; RPL_WELCOME - #'irc-handle-state-tracking--rpl-welcome) - (irc-handler-add table "JOIN" - #'irc-handle-state-tracking--JOIN) - (irc-handler-add table "PART" - #'irc-handle-state-tracking--PART) - (irc-handler-add table "KICK" - #'irc-handle-state-tracking--KICK) - (irc-handler-add table "QUIT" - #'irc-handle-state-tracking--QUIT) - (irc-handler-add table "NICK" - #'irc-handle-state-tracking--NICK) - (irc-handler-add table "PRIVMSG" - #'irc-handle-state-tracking--PRIVMSG) - (irc-handler-add table "353" ;; RPL_NAMREPLY - #'irc-handle-state-tracking--rpl-namreply) - (irc-handler-add table "366" ;; RPL_ENDOFNAMES - #'irc-handle-state-tracking--rpl-endofnames) - - (irc-handler-add table "TOPIC" - #'irc-handle-state-tracking--TOPIC) - (irc-handler-add table "331" ;; RPL_NOTOPIC - #'irc-handle-state-tracking--rpl-notopic) - (irc-handler-add table "332" ;; RPL_TOPIC - #'irc-handle-state-tracking--rpl-topic) - ) - -(cl-defstruct irc-channel - name - topic - last-topic - folded-name - users - recent-users - receiving-names - connection) - -(defun irc-channel-from-name (conn name) - "Create a new IRC channel object on CONN, named NAME." - (make-irc-channel :name name - :folded-name (irc-isupport--case-fold conn name) - :users (make-hash-table :test 'equal) - :recent-users (make-hash-table :test 'equal) - :connection conn)) - -(defun irc-connection-channel (conn channel-name) - "Return the channel object for CHANNEL-NAME on CONN." - (let ((channel-table (irc--connection-channel-table conn)) - (folded-name (irc-isupport--case-fold conn channel-name))) - (gethash folded-name channel-table))) - -(defun irc-connection-channel-list (conn) - "Return the list of channel object on CONN." - (let ((channel-list nil)) - (maphash (lambda (_folded-name channel) - (push channel channel-list)) - (irc--connection-channel-table conn)) - channel-list)) - -(defun irc-connection-add-channel (conn channel-name) - "Add CHANNEL-NAME to the channel table of CONN." - (let* ((channel-table (irc--connection-channel-table conn)) - (channel (irc-channel-from-name conn channel-name)) - (folded-name (irc-channel-folded-name channel))) - (when (not (gethash folded-name channel-table)) - (puthash folded-name channel channel-table)))) - -(defun irc-connection-remove-channel (conn channel-name) - "Remove CHANNEL-NAME from the channel table of CONN." - (let* ((channel-table (irc--connection-channel-table conn)) - (folded-name (irc-isupport--case-fold conn channel-name))) - (remhash folded-name channel-table))) - -(defun irc-current-nick (conn) - "Return the current nick on IRC connection CONN, or nil if not set yet." - (irc-connection-get conn :current-nick)) - -(defun irc-current-nick-p (conn nick) - "Return t if NICK is our current nick on IRC connection CONN." - (let ((current-nick (irc-current-nick conn))) - (if (and (stringp nick) - (stringp current-nick)) - (irc-string-equal-p conn current-nick nick) - nil))) - -(defun irc--connection-channel-table (conn) - (let ((table (irc-connection-get conn :channel-table))) - (when (not table) - (setq table (make-hash-table :test 'equal)) - (irc-connection-put conn :channel-table table)) - table)) - -(cl-defstruct irc-user - nick - folded-nick - userhost - join-time - last-activity-time - part-time - connection) - -(defun irc-user-from-userstring (conn userstring) - "Create an irc-user struct on CONN from USERSTRING. - -USERSTRING should be a s tring of the form \"nick!user@host\"." - (let ((nick (irc-userstring-nick userstring))) - (make-irc-user :nick nick - :folded-nick (irc-isupport--case-fold conn nick) - :userhost (let ((nick-len (length nick))) - (if (>= nick-len (length userstring)) - nil - (substring userstring (1+ nick-len)))) - :connection conn))) - -(defun irc-channel-user (channel nick) - "Return a user named NICK on channel CHANNEL." - (let ((user-table (irc-channel-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick))) - (gethash folded-nick user-table))) - -(defun irc-channel-recent-user (channel nick) - "Return a recent user named NICK on channel CHANNEL." - (let ((user-table (irc-channel-recent-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick))) - (gethash folded-nick user-table))) - -(defun irc-channel-add-user (channel userstring) - "Add USER to CHANNEL." - (let* ((user-table (irc-channel-users channel)) - (user (irc-user-from-userstring (irc-channel-connection channel) - userstring)) - (folded-nick (irc-user-folded-nick user)) - (recent-user (irc-channel-recent-user channel (irc-user-nick user)))) - (when (not (gethash folded-nick user-table)) - (when (and recent-user - (equal (irc-user-userhost recent-user) - (irc-user-userhost user))) - (setf (irc-user-last-activity-time user) - (irc-user-last-activity-time recent-user))) - (puthash folded-nick user user-table) - user))) - -(defun irc-channel-remove-user (channel nick) - "Remove NICK from CHANNEL." - (let* ((user-table (irc-channel-users channel)) - (recent-user-table (irc-channel-recent-users channel)) - (folded-nick (irc-isupport--case-fold (irc-channel-connection channel) - nick)) - (user (gethash folded-nick user-table))) - (remhash folded-nick user-table) - (when user - (setf (irc-user-part-time user) (float-time)) - (puthash folded-nick user recent-user-table) - (maphash (lambda (folded-nick user) - (when (< (irc-user-part-time user) - (- (float-time) - (* 60 60))) - (remhash folded-nick recent-user-table))) - recent-user-table)))) - -(defun irc-channel-rename-user (channel oldnick newnick) - "Update CHANNEL so that the user with nick OLDNICK now has nick NEWNICK." - (let ((user-table (irc-channel-users channel)) - (user (irc-channel-user channel oldnick)) - (newnick-folded (irc-isupport--case-fold - (irc-channel-connection channel) - newnick)) - (recent-user (irc-channel-recent-user channel newnick))) - (when user - (when (and recent-user - (equal (irc-user-userhost recent-user) - (irc-user-userhost user))) - (setf (irc-user-last-activity-time user) - (irc-user-last-activity-time recent-user))) - (remhash (irc-user-folded-nick user) user-table) - (setf (irc-user-nick user) newnick) - (setf (irc-user-folded-nick user) newnick-folded) - (puthash (irc-user-folded-nick user) user user-table)))) - -(defun irc-handle-state-tracking--rpl-welcome (conn _event _sender target - &rest ignored) - (irc-connection-put conn :current-nick target)) - -(defun irc-handle-state-tracking--JOIN (conn _event sender target - &optional _account _realname) - (let ((nick (irc-userstring-nick sender))) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-add-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (let ((user (irc-channel-add-user channel sender))) - (when user - (setf (irc-user-join-time user) (float-time)))))))))) - -(defun irc-handle-state-tracking--PART (conn _event sender target - &optional _reason) - (let ((nick (irc-userstring-nick sender))) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-remove-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (irc-channel-remove-user channel nick))))))) - -(defun irc-handle-state-tracking--KICK (conn _event _sender target nick - &optional _reason) - (cond - ((irc-current-nick-p conn nick) - (irc-connection-remove-channel conn target)) - (t - (let ((channel (irc-connection-channel conn target))) - (when channel - (irc-channel-remove-user channel nick)))))) - -(defun irc-handle-state-tracking--QUIT (conn _event sender - &optional reason) - (let ((nick (irc-userstring-nick sender))) - (if (irc-current-nick-p conn nick) - (dolist (channel (irc-connection-channel-list conn)) - (irc-connection-remove-channel conn - (irc-channel-folded-name channel))) - (dolist (channel (irc-connection-channel-list conn)) - (when (irc-channel-user channel nick) - (irc-event-emit conn "channel.quit" - sender - (irc-channel-name channel) - reason)) - (irc-channel-remove-user channel nick))))) - -(defun irc-handle-state-tracking--NICK (conn _event sender new-nick) - ;; Update channels - (let ((nick (irc-userstring-nick sender))) - (dolist (channel (irc-connection-channel-list conn)) - (irc-channel-rename-user channel nick new-nick))) - ;; Update our own nick - (when (irc-current-nick-p conn (irc-userstring-nick sender)) - (irc-connection-put conn :current-nick new-nick))) - -(defun irc-handle-state-tracking--PRIVMSG (conn _event sender target _message) - (let ((channel (irc-connection-channel conn target)) - (nick (irc-userstring-nick sender))) - (when channel - (let ((user (irc-channel-user channel nick))) - (when user - (setf (irc-user-last-activity-time user) (float-time))))))) - -(defun irc-handle-state-tracking--rpl-namreply - (conn _event _sender _current-nick _channel-type channel-name nicks) - (let ((channel (irc-connection-channel conn channel-name))) - (when channel - (setf (irc-channel-receiving-names channel) - (append (irc-channel-receiving-names channel) - (mapcar (lambda (nick) - (irc-nick-without-prefix - conn - (string-trim nick))) - (split-string nicks))))))) - -(defun irc-handle-state-tracking--rpl-endofnames - (conn _event _sender _current-nick channel-name _description) - (let ((channel (irc-connection-channel conn channel-name))) - (when channel - (irc-channel--synchronize-nicks channel - (irc-channel-receiving-names channel)) - (setf (irc-channel-receiving-names channel) nil)))) - -(defun irc-channel--synchronize-nicks (channel nicks) - "Update the user list of CHANNEL to match NICKS." - (let ((have (irc-channel-users channel)) - (want (make-hash-table :test 'equal))) - (dolist (nick nicks) - (puthash (irc-isupport--case-fold (irc-channel-connection channel) - nick) - nick - want)) - (maphash (lambda (nick-folded user) - (when (not (gethash nick-folded want)) - (irc-channel-remove-user channel - (irc-user-nick user)))) - have) - (maphash (lambda (_nick-folded nick) - (irc-channel-add-user channel nick)) - want))) - -(defun irc-handle-state-tracking--TOPIC (conn _event _sender channel new-topic) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-last-topic channel) - (irc-channel-topic channel)) - (setf (irc-channel-topic channel) new-topic)))) - -(defun irc-handle-state-tracking--rpl-notopic (conn _event _sender - _current-nick channel - _no-topic-desc) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-topic channel) nil)))) - -(defun irc-handle-state-tracking--rpl-topic (conn _event _sender _current-nick - channel topic) - (let ((channel (irc-connection-channel conn channel))) - (when channel - (setf (irc-channel-topic channel) topic)))) - -;;;;;;;;;;;;;;,;;;;;; -;;; Handler: NickServ - -(defun irc-handle-nickserv (table) - "Add command handlers to TABLE to deal with NickServ. - -Connection options used: - -:nickserv-nick -- The nick to register as - -:nickserv-password -- The password for nickserv; can be a function and - is then called with the IRC connection as its sole argument - -:nickserv-mask -- A regular expression matching the correct NickServ's - nick!user@host string to avoid fakes - -:nickserv-identify-challenge -- A regular expression matching the - challenge sent by NickServ to request identification - -:nickserv-identify-command -- The raw IRC command to send to identify; - expands {nick} and {password} when present - -:nickserv-identify-confirmation -- A regular expression matching the - confirmation message from NickServ after successful identification - -:nickserv-ghost-command -- The raw IRC comment to ghost your - original nick; expands {nick} and {password}. Set this to nil - to disable ghosting and nick regaining. - -:nickserv-ghost-confirmation -- A regular expression matching the - confirmation message that the nick was ghosted - -Events emitted: - -\"nickserv.identified\" -- We have successfully identified with nickserv. - -\"nickserv.ghosted\" -- We have ghosted a nick." - (irc-handler-add table "irc.registered" #'irc-handle-nickserv--registered) - (irc-handler-add table "NOTICE" #'irc-handle-nickserv--NOTICE) - (irc-handler-add table "PRIVMSG" #'irc-handle-nickserv--NOTICE) - (irc-handler-add table "NICK" #'irc-handle-nickserv--NICK)) - -(defun irc-handle-nickserv--password (conn) - (let ((password (irc-connection-get conn :nickserv-password))) - (if (functionp password) - (funcall password conn) - password))) - -(defun irc-handle-nickserv--registered (conn _event current-nick) - (let ((ghost-command (irc-connection-get conn :nickserv-ghost-command)) - (wanted-nick (irc-connection-get conn :nickserv-nick)) - (password (irc-handle-nickserv--password conn))) - (when (and ghost-command - wanted-nick - password - (not (irc-string-equal-p conn current-nick wanted-nick))) - (irc-send-raw conn - (irc-format ghost-command - 'nick wanted-nick - 'password password))))) - -(defun irc-handle-nickserv--NOTICE (conn _event sender _target message) - (let ((nickserv-mask (irc-connection-get conn :nickserv-mask)) - identify-challenge identify-command identify-confirmation - ghost-confirmation - nickserv-nick nickserv-password) - (when (and nickserv-mask (string-match nickserv-mask sender)) - (setq identify-challenge - (irc-connection-get conn :nickserv-identify-challenge)) - (setq identify-command - (irc-connection-get conn :nickserv-identify-command)) - (setq identify-confirmation - (irc-connection-get conn :nickserv-identify-confirmation)) - (setq ghost-confirmation - (irc-connection-get conn :nickserv-ghost-confirmation)) - (setq nickserv-nick (irc-connection-get conn :nickserv-nick)) - (setq nickserv-password (irc-handle-nickserv--password conn)) - (cond - ;; Identify - ((and identify-challenge - identify-command - nickserv-nick - nickserv-password - (string-match identify-challenge message)) - (irc-send-raw conn - (irc-format identify-command - 'nick nickserv-nick - 'password nickserv-password))) - ;; Identification confirmed - ((and identify-confirmation - (string-match identify-confirmation message)) - (irc-event-emit conn "nickserv.identified")) - ;; Ghosting confirmed - ((and ghost-confirmation - (string-match ghost-confirmation message)) - (irc-event-emit conn "nickserv.ghosted") - (irc-connection-put conn :nickserv-regaining-nick t) - (when nickserv-nick - (irc-send-NICK conn nickserv-nick))))))) - -(defun irc-handle-nickserv--NICK (conn _event _sender new-nick) - (when (and (irc-connection-get conn :nickserv-regaining-nick) - (irc-string-equal-p conn new-nick - (irc-connection-get conn :nickserv-nick))) - (irc-connection-put conn :nickserv-regaining-nick nil) - (irc-event-emit conn "nickserv.regained"))) - -(defun irc-format (format &rest args) - "Return a formatted version of FORMAT, using substitutions from ARGS. - -The substitutions are identified by braces ('{' and '}')." - (with-temp-buffer - (insert format) - (goto-char (point-min)) - (while (re-search-forward "{\\([^}]*\\)}" nil t) - (replace-match (format "%s" (plist-get args (intern (match-string 1)))))) - (buffer-string))) - -;;;;;;;;;;;;;;;;;;;;;; -;;; Handler: Auto-Join - -(defun irc-handle-auto-join (table) - "Add command handlers to TABLE to deal with NickServ. - -Connection options used: - -:auto-join-after-registration -- List of channels to join - immediately after registration with the server - -:auto-join-after-host-hiding -- List of channels to join - after our host was hidden - -:auto-join-after-nick-acquisition -- List of channels to join - after we gained our desired nick - -:auto-join-after-nickserv-identification -- List of channels - to join after we identified successfully with NickServ" - (irc-handler-add table "irc.registered" #'irc-handle-auto-join--registered) - (irc-handler-add table "396" ;; RPL_HOSTHIDDEN - #'irc-handle-auto-join--rpl-hosthidden) - (irc-handler-add table "nickserv.regained" - #'irc-handle-auto-join--nickserv-regained) - (irc-handler-add table "nickserv.identified" - #'irc-handle-auto-join--nickserv-identified) - (irc-handler-add table "sasl.login" - #'irc-handle-auto-join--sasl-login)) - -(defun irc-handle-auto-join--registered (conn _event _current-nick) - (dolist (channel (irc-connection-get conn :auto-join-after-registration)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--rpl-hosthidden (conn _event _sender _target _host - _description) - (dolist (channel (irc-connection-get conn :auto-join-after-host-hiding)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--nickserv-regained (conn _event) - (dolist (channel (irc-connection-get - conn :auto-join-after-nick-acquisition)) - (irc-send-JOIN conn channel))) - -(defun irc-handle-auto-join--nickserv-identified (conn event) - (dolist (channel (irc-connection-get - conn :auto-join-after-nickserv-identification)) - (irc-send-JOIN conn channel)) - (if (irc-string-equal-p conn - (irc-connection-get conn :nick) - (irc-connection-get conn :nickserv-nick)) - (irc-handle-auto-join--nickserv-regained conn event))) - -(defun irc-handle-auto-join--sasl-login (conn _event &rest ignored) - (dolist (channel (irc-connection-get - conn :auto-join-after-sasl-login)) - (irc-send-JOIN conn channel))) - -(provide 'irc) -;;; irc.el ends here diff --git a/elpa/circe-20160608.1315/lcs.el b/elpa/circe-20160608.1315/lcs.el deleted file mode 100644 index b5beb12..0000000 --- a/elpa/circe-20160608.1315/lcs.el +++ /dev/null @@ -1,202 +0,0 @@ -;;; lcs.el --- find out the longest common sequence - -;; Copyright (c) 2002-2003 by Alex Shinn, All rights reserved. -;; Copyright (c) 2002-2003 by Shiro Kawai, All rights reserved. -;; Copyright (c) 2006, 2012 by Jorgen Schaefer, All rights reserved. - -;; Authors: Alex Shinn, Shiro Kawai -;; Maintainer: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe/wiki/lcs - -;; Redistribution and use in source and binary forms, with or without -;; modification, are permitted provided that the following conditions -;; are met: - -;; 1. Redistributions of source code must retain the above copyright -;; notice, this list of conditions and the following disclaimer. - -;; 2. Redistributions in binary form must reproduce the above copyright -;; notice, this list of conditions and the following disclaimer in the -;; documentation and/or other materials provided with the distribution. - -;; 3. Neither the name of the authors nor the names of its contributors -;; may be used to endorse or promote products derived from this -;; software without specific prior written permission. - -;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -;; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -;; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -;; LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -;;; Commentary: - -;; lcs.el is a library for other Emacs Lisp programs not useful by -;; itself. - -;; This library provides functions to find the Longest Common Sequence -;; (LCS) of two sequences. This is used to create a unified diff of to -;; two lists. See `lcs-unified-diff' for a useful function to be -;; called. - -;; The code is more or less a literal translation of (part of) -;; Gauche's util/lcs.scm module to Emacs Lisp. - -;;; Code: - -(put 'lcs-for 'lisp-indent-function 4) -(defmacro lcs-for (var from to step &rest body) - "A simple FOR loop macro. -Count VAR from FROM to TO by stepsize STEP. Evaluate BODY in each -iteration." - (let ((sto (make-symbol "to")) - (sstep (make-symbol "step"))) - `(let ((,var ,from) - (,sto ,to) - (,sstep ,step)) - (while (<= ,var ,sto) - (progn - ,@body) - (setq ,var (+ ,var ,sstep)))))) - -(defun lcs-split-at (lis pos) - "Return a cons cell of the first POS elements of LIS and the rest." - (let ((head nil)) - (while (> pos 0) - (setq head (cons (car lis) - head) - pos (- pos 1) - lis (cdr lis))) - (cons (reverse head) - lis))) - -(defun lcs-finish (M+N V_l vl V_r vr) - "Finalize the LCS algorithm. -Should be used only by `lcs-with-positions'." - (let ((maxl 0) - (r '())) - (lcs-for i (- M+N) M+N 1 - (when (> (funcall vl i) - maxl) - (setq maxl (funcall vl i) - r (funcall vr i)))) - (list maxl (reverse r)))) - -(defun lcs-with-positions (a-ls b-ls &optional equalp) - "Return the longest common subsequence (LCS) of A-LS and B-LS. -EQUALP can be any procedure which returns non-nil when two -elements should be considered equal." - (let* ((A (vconcat a-ls)) - (B (vconcat b-ls)) - (N (length A)) - (M (length B)) - (M+N (+ M N)) - (V_d (make-vector (+ 1 (* 2 M+N)) - 0)) - (V_r (make-vector (+ 1 (* 2 M+N)) - nil)) - (V_l (make-vector (+ 1 (* 2 M+N)) - 0)) - (vd (lambda (i &optional x) - (if x - (aset V_d (+ i M+N) x) - (aref V_d (+ i M+N))))) - (vr (lambda (i &optional x) - (if x - (aset V_r (+ i M+N) x) - (aref V_r (+ i M+N))))) - (vl (lambda (i &optional x) - (if x - (aset V_l (+ i M+N) x) - (aref V_l (+ i M+N)))))) - (when (not equalp) - (setq equalp 'equal)) - (catch 'return - (if (= M+N 0) - (throw 'return '(0 ())) - (lcs-for d 0 M+N 1 - (lcs-for k (- d) d 2 - (let ((x nil) - (y nil) - (l nil) - (r nil)) - (if (or (= k (- d)) - (and (not (= k d)) - (< (funcall vd (- k 1)) - (funcall vd (+ k 1))))) - (setq x (funcall vd (+ k 1)) - l (funcall vl (+ k 1)) - r (funcall vr (+ k 1))) - (setq x (+ 1 (funcall vd (- k 1))) - l (funcall vl (- k 1)) - r (funcall vr (- k 1)))) - (setq y (- x k)) - (while (and (< x N) - (< y M) - (funcall equalp (aref A x) (aref B y))) - (setq r (cons (list (aref A x) x y) - r) - x (+ x 1) - y (+ y 1) - l (+ l 1))) - (funcall vd k x) - (funcall vr k r) - (funcall vl k l) - (when (and (>= x N) - (>= y M)) - (throw 'return(lcs-finish M+N V_l vl V_r vr))))))) - (error "Can't happen")))) - -(defun lcs-unified-diff (a b &optional equalp) - "Return a unified diff of the lists A and B. -EQUALP should can be a procedure that returns non-nil when two -elements of A and B should be considered equal. It's `equal' by -default." - (let ((common (cadr (lcs-with-positions a b equalp))) - (a a) - (a-pos 0) - (b b) - (b-pos 0) - (diff '())) - (while common - (let* ((elt (car common)) - (a-off (nth 1 elt)) - (a-skip (- a-off a-pos)) - (b-off (nth 2 elt)) - (b-skip (- b-off b-pos)) - (a-split (lcs-split-at a a-skip)) - (a-head (car a-split)) - (a-tail (cdr a-split)) - (b-split (lcs-split-at b b-skip)) - (b-head (car b-split)) - (b-tail (cdr b-split))) - (setq diff (append diff - (mapcar (lambda (a) - `(- ,a)) - a-head) - (mapcar (lambda (b) - `(+ ,b)) - b-head) - `((! ,(car elt)))) - - common (cdr common) - a (cdr a-tail) - a-pos (+ a-off 1) - b (cdr b-tail) - b-pos (+ b-off 1)))) - (append diff - (mapcar (lambda (a) - `(- ,a)) - a) - (mapcar (lambda (b) - `(+ ,b)) - b)))) - -(provide 'lcs) -;;; lcs.el ends here diff --git a/elpa/circe-20160608.1315/lui-autopaste.el b/elpa/circe-20160608.1315/lui-autopaste.el deleted file mode 100644 index 7582839..0000000 --- a/elpa/circe-20160608.1315/lui-autopaste.el +++ /dev/null @@ -1,115 +0,0 @@ -;;; lui-autopaste.el --- Extension for lui for long text input - -;; Copyright (C) 2012 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This file is part of Lui. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This extension for lui will intercept long input and replace it by -;; an URL to a paste service. - -;; What is considered "long" is defined by `lui-autopaste-lines'. You -;; can configure which paste service to use by changing -;; `lui-autopaste-function'. - -;; Run `enable-lui-autopaste' to enable this. - -;;; Code: - -(defgroup lui-autopaste nil - "The Lui autopaste extension." - :prefix "lui-autopaste-" - :group 'lui) - -(defcustom lui-autopaste-lines 3 - "Starting at this number of lines, Lui will ask to paste the input." - :type 'integer - :group 'lui-autopaste) - -(defcustom lui-autopaste-function 'lui-autopaste-service-ixio - "Which paste service to use. - -This function will be called with some text as its only argument, -and is expected to return an URL to view the contents." - :type '(choice (const :tag "ix.io" lui-autopaste-service-ixio) - (const :tag "ptpb.pw" lui-autopaste-service-ptpb-pw)) - :group 'lui-autopaste) - -;;;###autoload -(defun enable-lui-autopaste () - "Enable the lui autopaste feature. - -If you enter more than `lui-autopaste-lines' at once, Lui will -ask if you would prefer to use a paste service instead. If you -agree, Lui will paste your input to `lui-autopaste-function' and -replace it with the resulting URL." - (interactive) - (add-hook 'lui-pre-input-hook 'lui-autopaste)) - -;;;###autoload -(defun disable-lui-autopaste () - "Disable the lui autopaste feature." - (interactive) - (remove-hook 'lui-pre-input-hook 'lui-autopaste)) - -(defun lui-autopaste () - "Check if the lui input is too large. If so, paste it instead." - (when (and (>= (count-lines (point-min) (point-max)) - lui-autopaste-lines) - (y-or-n-p "That's pretty long, would you like to use a paste service instead? ")) - (let ((url (funcall lui-autopaste-function - (buffer-substring (point-min) - (point-max))))) - (delete-region (point-min) (point-max)) - (insert url)))) - -(defun lui-autopaste-service-ptpb-pw (text) - "Paste TEXT to ptpb.pw and return the paste url." - (let ((url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded"))) - (url-request-data (format "c=%s" (url-hexify-string text))) - (url-http-attempt-keepalives nil)) - (let ((buf (url-retrieve-synchronously "https://ptpb.pw/"))) - (unwind-protect - (with-current-buffer buf - (goto-char (point-min)) - (if (re-search-forward "^url: \\(.*\\)" nil t) - (match-string 1) - (error "Error during pasting to ptpb.pw"))) - (kill-buffer buf))))) - -(defun lui-autopaste-service-ixio (text) - "Paste TEXT to ix.io and return the paste url." - (let ((url-request-method "POST") - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded"))) - (url-request-data (format "f:1=%s" (url-hexify-string text))) - (url-http-attempt-keepalives nil)) - (let ((buf (url-retrieve-synchronously "http://ix.io/"))) - (unwind-protect - (with-current-buffer buf - (goto-char (point-min)) - (if (re-search-forward "\n\n" nil t) - (buffer-substring (point) (point-at-eol)) - (error "Error during pasting to ix.io"))) - (kill-buffer buf))))) - -(provide 'lui-autopaste) -;;; lui-autopaste.el ends here diff --git a/elpa/circe-20160608.1315/lui-format.el b/elpa/circe-20160608.1315/lui-format.el deleted file mode 100644 index 68cc0ff..0000000 --- a/elpa/circe-20160608.1315/lui-format.el +++ /dev/null @@ -1,198 +0,0 @@ -;;; lui-format.el --- A formatting function for use with Lui - -;; Copyright (C) 2005, 2012 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This file is part of Lui. - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; An improved formatting function using named parameters. -;; -;; See the docstring of `lui-format' for more details. -;; -;; Most of the design is borrowed from Python's string.format. - -;;; Code: - -(require 'lui) - -(defun lui-display (format not-tracked-p &rest keywords) - "Display a formatted string in the current Lui interface. - -The string is formatted using FORMAT and `lui-format'. - -If NOT-TRACKED-P is given, the inserted string won't trigger -tracking. See `lui-insert' for a description. - -KEYWORDS are the keyword arguments passed to `lui-format'. - -See `lui-format' for a full description of the arguments." - (lui-insert (lui-format format keywords) - not-tracked-p)) - -(defun lui-format (format &rest keywords) - "Display FORMAT formatted with KEYWORDS. -FORMAT should be a symbol whose value is taken. If the value is a -procedure, the keyword list is passed as a single argument to it, -and it should return the formatted string. If the value is a -string, it is formatted according to the rules below. - -KEYWORDS is a plist of keywords and strings, or symbols and -strings. They are used as format arguments. - -The string is taken verbatim, unless there is are opening or -closing braces. - -Double opening or closing braces are replaced by single -occurrences of those characters. Otherwise, the contents between -opening and closing braces is a format description and replaced -by a formatted string. - -The string between opening and closing braces is taken as a name -of a keyword argument, and replaced by that argument's value. If -there is a colon in the string, the keyword name is the part -before the colon. The part after the colon is used to format the -argument using standard `format' - -Example: - - (lui-format \"Hello {foo:.1f}\" :foo 3.1415) - -is equivalent to - - (format \"Hello %.1f\" 3.1415) - -If the name is either a number, a number followed by a dash, or -two numbers with a dash in between them, this is taken as a -special name that is looked up in the list given using the list -argument to the :indexed-args keyword. - -{1} refers to the second element (element 1) -{1-} refers to the second and all following elements -{1-3} refers to the second through fourth element - -If more than one element is selected, the elements are separated -by a single space character. - -All named arguments receive a property of `lui-format-argument' -with the respective name as value. The whole string receives a -`lui-format' property with FORMAT as a value, and a -`lui-keywords' argument with KEYWORDS as a value." - ;; If it's only a single argument, that argument is a list. - (when (not (cdr keywords)) - (setq keywords (car keywords))) - (cond - ((functionp format) - (apply format keywords)) - ((and (symbolp format) - (functionp (symbol-value format))) - (apply (symbol-value format) keywords)) - (t - (let* ((format-string (if (symbolp format) - (symbol-value format) - format)) - (plist (mapcar (lambda (entry) - (if (keywordp entry) - ;; Keyword -> symbol - (intern (substring (symbol-name entry) - 1)) - entry)) - keywords))) - (propertize (lui-format-internal format-string plist) - 'lui-format format - 'lui-keywords keywords))))) - -(defun lui-format-internal (fmt keywords) - "Internal function for `lui-format'. - -FMT is the format string and KEYWORDS is the symbol-based plist. - -See `lui-format'." - (with-temp-buffer - (insert fmt) - (goto-char (point-min)) - (while (re-search-forward "{{\\|}}\\|{\\([^}]*\\)}" nil t) - (cond - ((string-equal (match-string 0) "3.1") - (replace-match "{")) - ((string-equal (match-string 0) "}}") - (replace-match "}")) - (t ;; (match-string 1) - (replace-match (save-match-data - (lui-format-single (match-string 1) keywords)) - t t)))) - (buffer-string))) - -(defun lui-format-single (specifier keywords) - "Format a single braced SPECIFIER according to KEYWORDS. -See `lui-format' for details. - -This adds `lui-format-argument' as necessary." - (let* ((split (split-string specifier ":")) - (identifier (car split)) - (format (cadr split))) - (when (not format) - (setq format "s")) - (propertize (format (concat "%" format) - (lui-format-lookup identifier keywords)) - 'lui-format-argument (intern identifier)))) - -(defun lui-format-lookup (identifier keywords) - "Lookup the format IDENTIFIER in KEYWORDS. - -See `lui-format' for details." - (cond - ((string-match "^\\([0-9]+\\)\\(-\\([0-9]+\\)?\\)?$" identifier) - (let ((from (match-string 1 identifier)) - (rangep (match-string 2 identifier)) - (to (match-string 3 identifier)) - (indexed-args (plist-get keywords 'indexed-args))) - (if rangep - (mapconcat (lambda (element) - (if (stringp element) - element - (format "%s" element))) - (lui-sublist indexed-args - (string-to-number from) - (when to (string-to-number to))) - " ") - (or (nth (string-to-number from) - indexed-args) - "")))) - (t - (or (plist-get keywords (intern identifier)) - (error "Unknown keyword argument %S" identifier))))) - -(defun lui-sublist (list from &optional to) - "Return the sublist from LIST starting at FROM and ending at TO." - (if (not to) - (nthcdr from list) - (let ((from-list (nthcdr from list)) - (i (- to from)) - (to-list nil)) - (while (>= i 0) - (when (null from-list) - (error "Argument out of range: %S" to)) - (setq to-list (cons (car from-list) - to-list) - i (- i 1) - from-list (cdr from-list))) - (nreverse to-list)))) - -(provide 'lui-format) -;;; lui-format.el ends here diff --git a/elpa/circe-20160608.1315/lui-irc-colors.el b/elpa/circe-20160608.1315/lui-irc-colors.el deleted file mode 100644 index 9b16ead..0000000 --- a/elpa/circe-20160608.1315/lui-irc-colors.el +++ /dev/null @@ -1,182 +0,0 @@ -;;; lui-irc-colors.el --- Add IRC color support to LUI - -;; Copyright (C) 2005 Jorgen Schaefer - -;; Author: Jorgen Schaefer - -;; This file is part of Lui. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This tells LUI how to display IRC colors: -;; ^B - Bold -;; ^_ - Underline -;; ^V - Inverse -;; ^] - Italic -;; ^O - Return to normal -;; ^C1,2 - Colors - -;; The colors are documented at http://www.mirc.co.uk/help/color.txt - -;;; Code: - -(require 'lui) - -(defgroup lui-irc-colors nil - "LUI IRC colors faces." - :group 'circe) - -(defface lui-irc-colors-inverse-face - '((t (:inverse-video t))) - "Face used for inverse video." - :group 'lui-irc-colors) - -(defun lui-irc-defface (face property on-dark on-light rest doc) - (custom-declare-face - face - `((((type graphic) (class color) (background dark)) - (,property ,on-dark)) - (((type graphic) (class color) (background light)) - (,property ,on-light)) - (t (,property ,rest))) - doc - :group 'lui-irc-colors)) - -(defun lui-irc-defface-pair (number on-dark on-light rest name) - (lui-irc-defface - (intern (format "lui-irc-colors-fg-%d-face" number)) - :foreground - on-dark on-light rest - (concat "Face used for foreground IRC color " - (number-to-string number) " (" name ").")) - (lui-irc-defface - (intern (format "lui-irc-colors-bg-%d-face" number)) - :background - on-light on-dark rest - (concat "Face used for background IRC color " - (number-to-string number) " (" name ")."))) - -(defun lui-irc-defface-bulk (colors) - (dotimes (n (length colors)) - (apply 'lui-irc-defface-pair n (nth n colors)))) - -(lui-irc-defface-bulk - '(("#ffffff" "#585858" "white" "white") - ("#a5a5a5" "#000000" "black" "black") - ("#9b9bff" "#0000ff" "blue4" "blue") - ("#40eb51" "#006600" "green4" "green") - ("#ff9696" "#b60000" "red" "red") - ("#d19999" "#8f3d3d" "red4" "brown") - ("#d68fff" "#9c009c" "magenta4" "purple") - ("#ffb812" "#7a4f00" "yellow4" "orange") - ("#ffff00" "#5c5c00" "yellow" "yellow") - ("#80ff95" "#286338" "green" "light green") - ("#00b8b8" "#006078" "cyan4" "teal") - ("#00ffff" "#006363" "cyan" "light cyan") - ("#a8aeff" "#3f568c" "blue" "light blue") - ("#ff8bff" "#853885" "magenta" "pink") - ("#cfcfcf" "#171717" "dimgray" "grey") - ("#e6e6e6" "#303030" "gray" "light grey"))) - -(defvar lui-irc-colors-regex - "\\(\x02\\|\x1F\\|\x16\\|\x1D\\|\x0F\\|\x03\\)" - "A regular expression matching IRC control codes.") - -;;;###autoload -(defun enable-lui-irc-colors () - "Enable IRC color interpretation for Lui." - (interactive) - (add-hook 'lui-pre-output-hook 'lui-irc-colors)) - -(defun disable-lui-irc-colors () - "Disable IRC color interpretation for Lui." - (interactive) - (remove-hook 'lui-pre-output-hook 'lui-irc-colors)) - -(defun lui-irc-colors () - "Add color faces for IRC colors. -This is an appropriate function for `lui-pre-output-hook'." - (goto-char (point-min)) - (let ((start (point)) - (boldp nil) - (inversep nil) - (italicp nil) - (underlinep nil) - (fg nil) - (bg nil)) - (while (re-search-forward lui-irc-colors-regex nil t) - (lui-irc-propertize start (point) - boldp inversep italicp underlinep - fg bg) - (let ((code (match-string 1))) - (replace-match "") - (setq start (point)) - (cond - ((string= code "") - (setq boldp (not boldp))) - ((string= code "") - (setq inversep (not inversep))) - ((string= code "") - (setq italicp (not italicp))) - ((string= code "") - (setq underlinep (not underlinep))) - ((string= code "") - (setq boldp nil - inversep nil - italicp nil - underlinep nil - fg nil - bg nil)) - ((string= code "") - (if (looking-at "\\([0-9][0-9]?\\)\\(,\\([0-9][0-9]?\\)\\)?") - (progn - (setq fg (string-to-number (match-string 1)) - bg (if (match-string 2) - (string-to-number (match-string 3)) - bg)) - (setq fg (if (and fg (not (= fg 99))) (mod fg 16) nil) - bg (if (and bg (not (= bg 99))) (mod bg 16) nil)) - (replace-match "")) - (setq fg nil - bg nil))) - (t - (error "lui-irc-colors: Can't happen!"))))) - (lui-irc-propertize (point) (point-max) - boldp inversep italicp underlinep fg bg))) - -(defun lui-irc-propertize (start end boldp inversep italicp underlinep fg bg) - "Propertize the region between START and END." - (let ((faces (append (and boldp '(bold)) - (and inversep '(lui-irc-colors-inverse-face)) - (and italicp '(italic)) - (and underlinep '(underline)) - (and fg (list (lui-irc-colors-face 'fg fg))) - (and bg (list (lui-irc-colors-face 'bg bg)))))) - (when faces - (add-face-text-property start end faces)))) - -(defun lui-irc-colors-face (type n) - "Return a face appropriate for face number N. -TYPE is either 'fg or 'bg." - (if (and (<= 0 n) - (<= n 15)) - (intern (format "lui-irc-colors-%s-%s-face" type n)) - 'default-face)) - -(provide 'lui-irc-colors) -;;; lui-irc-colors.el ends here diff --git a/elpa/circe-20160608.1315/lui-logging.el b/elpa/circe-20160608.1315/lui-logging.el deleted file mode 100644 index d24e051..0000000 --- a/elpa/circe-20160608.1315/lui-logging.el +++ /dev/null @@ -1,201 +0,0 @@ -;;; lui-logging.el --- Logging support for lui - -;; Copyright (C) 2006 Jorgen Schaefer, -;; 2012 Anthony Martinez - -;; Author: Anthony Martinez - -;; This file is part of Lui. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This lui module enables logging. Lui applications can change the -;; values of `lui-logging-format-arguments' to provide further -;; possibilities of customizing `lui-logging-file-format' for users. - -;;; Code: - -(require 'lui-format) -(require 'url-util) - -(defgroup lui-logging nil - "Logging support." - :prefix "lui-logging-" - :group 'lui) - -(defcustom lui-logging-format "[%T] {text}" - "The format used for log file entries. -This is first passed through `format-time-string' and then through -`lui-format'. The following format strings exist: - - {text} - the text to be logged" - :type 'string - :group 'lui-logging) - -(defcustom lui-logging-directory "~/.logs" - "The directory where log files are stored." - :type 'directory - :group 'lui-logging) - -(defcustom lui-logging-file-format "{buffer}_%Y-%m-%d.txt" - "The format to be used for the log file name. -This is first passed through `format-time-string', and then -through `lui-format'. Possible lui format strings are: - - {buffer} - the buffer name where the logging happened. - -Lui applications can provide further format strings. See -`lui-logging-format-arguments' in the appropriate buffer." - :type 'string - :group 'lui-logging) - -(defcustom lui-logging-flush-delay 0 - "The number of seconds to delay writing newly-received messages -to disk. This can increase performance/decrease IO-wait at the -cost of a little bit of safety." - :type 'integer - :group 'lui-logging) - -(defvar lui-logging-format-arguments nil - "A list of arguments to be passed to `lui-format'. -This can be used to extend the formatting possibilities of the -file name for lui applications.") -(make-variable-buffer-local 'lui-logging-format-arguments) - -(defvar lui-logging-file-name-unreserved-chars - ;; All but '/' is fine actually, but also omit '%' because otherwise there's - ;; ambiguity between one introduced by encoding and a literal one. - '(?! ?\" ?# ?$ ?& ?` ?\( ?\) ?* ?+ ?,?: ?\; ?< ?= ?> ?? ?@?\[ ?\\ ?\] ?^ ?` - ?\{ ?| ?\}) - "A list of characters that should not be percent-encoded by -`url-hexify-string' while generating a logging file name.") - -(defvar lui-pending-logs - (make-hash-table :test 'equal) - "Storage for log messages awaiting write. It is structured as a -hash table mapping filenames to a list-of-strings, which serves as -a queue.") - -(defvar lui-logging-timer nil - "The timer used to flush lui-logged buffers") - -(defun lui-logging-delayed-p () - (> lui-logging-flush-delay 0)) - -(defun enable-lui-logging () - "Enable lui logging for this buffer. Also create the log -file's directory, should it not exist." - (interactive) - (add-hook 'lui-pre-output-hook 'lui-logging - nil t)) - -(defun disable-lui-logging () - "Disable lui logging for this buffer, and flush any pending -logs to disk." - (interactive) - (remove-hook 'lui-pre-output-hook 'lui-logging t) - (lui-logging-flush)) - -(defun enable-lui-logging-globally () - "Enable lui logging for all Lui buffers. - -This affects current as well as future buffers." - (interactive) - (add-hook 'lui-mode-hook 'enable-lui-logging) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when lui-input-marker - (enable-lui-logging))))) - -(defun disable-lui-logging-globally () - "Disable logging in all future Lui buffers. - -This affects current as well as future buffers." - (interactive) - (remove-hook 'lui-mode-hook 'enable-lui-logging) - (dolist (buf (buffer-list)) - (with-current-buffer buf - (when lui-input-marker - (disable-lui-logging))))) - -(defun lui-logging-file-name () - "Create the name of the log file based on `lui-logging-file-format'." - (let* ((time-formatted (format-time-string lui-logging-file-format)) - (buffer (let ((url-unreserved-chars - (append url-unreserved-chars - lui-logging-file-name-unreserved-chars)) - (downcased (downcase (buffer-name (current-buffer))))) - (url-hexify-string downcased))) - (filename (apply 'lui-format - time-formatted - :buffer buffer - lui-logging-format-arguments))) - (concat lui-logging-directory "/" filename))) - -(defun lui-logging-flush () - "Flush out the lui-logging queue, and clear the timer set by -`lui-logging'." - (maphash #'lui-logging-flush-file lui-pending-logs) - (clrhash lui-pending-logs) - (cancel-timer lui-logging-timer) - (setq lui-logging-timer nil)) - -(defun lui-logging-write-to-log (file-name content) - "Actually perform a write to the logfile." - (let ((coding-system-for-write 'raw-text) - (dir (file-name-directory file-name))) - (when (not (file-directory-p dir)) - (make-directory dir t)) - (write-region content nil file-name t 'nomessage))) - -(defun lui-logging-flush-file (file-name queue) - "Consume the logging queue and write the content to the log -file." - (let ((content (apply #'concat (nreverse queue)))) - (lui-logging-write-to-log file-name content))) - -(defun lui-logging-format-string (text) - "Generate a string to be either directly written or enqueued." - (substring-no-properties - (lui-format - (format-time-string lui-logging-format) - :text text))) - -(defun lui-logging-enqueue (file-name text) - "Given a filename, push text onto its queue, and tickle the -timer, if necessary." - (puthash file-name - (cons text (gethash file-name lui-pending-logs)) - lui-pending-logs) - (when (null lui-logging-timer) - (setq lui-logging-timer - (run-with-timer lui-logging-flush-delay nil - #'lui-logging-flush)))) - -(defun lui-logging () - "If output-queueing is enabled, append the to-be-logged string -to the output queue. Otherwise, write directly to the logfile. -This should be added to `lui-pre-output-hook' by way of -`enable-lui-logging'." - (let ((text (lui-logging-format-string (buffer-string)))) - (if (lui-logging-delayed-p) - (lui-logging-enqueue (lui-logging-file-name) text) - (lui-logging-write-to-log (lui-logging-file-name) text)))) - -(provide 'lui-logging) -;;; lui-logging.el ends here diff --git a/elpa/circe-20160608.1315/lui-track-bar.el b/elpa/circe-20160608.1315/lui-track-bar.el deleted file mode 100644 index 360ecf6..0000000 --- a/elpa/circe-20160608.1315/lui-track-bar.el +++ /dev/null @@ -1,110 +0,0 @@ -;;; lui-track-bar.el --- Provides a bar to track the last read position - -;; Copyright (C) 2016 Vasilij Schneidermann - -;; Author: Vasilij Schneidermann - -;; This file is part of LUI. - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program; if not, write to the Free Software -;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -;; 02110-1301 USA - -;;; Commentary: - -;; This allows you to track where you've last left off a buffer. - -;; Use (enable-lui-track-bar) to enable this mode globally. You can -;; customize `lui-track-bar-behavior' to change when the track bar -;; moves. You can also use M-x lui-track-bar-move to move the track -;; bar manually. - -;;; Code: - -(require 'lui) -(require 'tracking) - -(defgroup lui-track-bar nil - "Last read position tracking for LUI" - :prefix "lui-track-bar-" - :group 'lui) - -(defcustom lui-track-bar-behavior 'before-switch-to-buffer - "When to move the track bar. - -The following values are possible. - -before-switch-to-buffer (default) - Move the bar to the bottom of the buffer when switching away - from a buffer. - -before-tracking-next-buffer - Move the bar when switching to the next buffer using - \\[tracking-next-buffer]. - -after-send - Move the bar after sending a message." - :type '(choice (const :tag "Before switching buffers" - before-switch-to-buffer) - (const :tag "Before tracking switch" - before-tracking-next-buffer) - (const :tag "After sending" - after-send)) - :group 'lui-track-bar) - -(defface lui-track-bar - '((((type graphic) (background light)) - :inherit default :background "dim gray" :height 0.1) - (((type graphic) (background dark)) - :inherit default :background "light gray" :height 0.1) - (((type tty)) - :inherit (font-lock-comment-face default) :underline t)) - "Track bar face" - :group 'lui-track-bar) - -(defvar lui-track-bar-overlay nil) -(make-variable-buffer-local 'lui-track-bar-overlay) - -;;;###autoload -(defun enable-lui-track-bar () - "Enable a bar in Lui buffers that shows where you stopped reading." - (interactive) - (defadvice switch-to-buffer (before lui-track-bar activate) - (when (and (eq lui-track-bar-behavior 'before-switch-to-buffer) - ;; Do not move the bar if the buffer is displayed still - (<= (length (get-buffer-window-list (current-buffer))) - 1)) - (lui-track-bar-move))) - (defadvice tracking-next-buffer (before lui-track-bar activate) - (when (eq lui-track-bar-behavior 'before-tracking-next-buffer) - (lui-track-bar-move))) - (add-hook 'lui-pre-input-hook 'lui-track-bar--move-pre-input)) - -(defun lui-track-bar--move-pre-input () - (when (eq lui-track-bar-behavior 'after-send) - (lui-track-bar-move))) - -(defun lui-track-bar-move () - "Move the track bar down." - (interactive) - (when (derived-mode-p 'lui-mode) - (when (not lui-track-bar-overlay) - (setq lui-track-bar-overlay (make-overlay (point-min) (point-min))) - (overlay-put lui-track-bar-overlay 'after-string - (propertize "\n" 'face 'lui-track-bar))) - (move-overlay lui-track-bar-overlay - lui-output-marker lui-output-marker))) - -(provide 'lui-track-bar) -;;; lui-track-bar.el ends here diff --git a/elpa/circe-20160608.1315/lui.el b/elpa/circe-20160608.1315/lui.el deleted file mode 100644 index 6f11c3f..0000000 --- a/elpa/circe-20160608.1315/lui.el +++ /dev/null @@ -1,1353 +0,0 @@ -;;; lui.el --- Linewise User Interface -*- lexical-binding: t -*- - -;; Copyright (C) 2005 - 2016 Jorgen Schaefer - -;; Author: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe/wiki/Lui - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; Lui is a library for other Emacs Lisp programs and not useful by -;; itself. - -;; This major mode provides a user interface for applications. The -;; user interface is quite simple, consisting of an input line, a -;; prompt, and some output area, but Lui includes a lot of common -;; options, such as time stamps, filling, colorization, etc. - -;; Application programs should create modes derived from lui-mode. - -;; The application API consists of: - -;; lui-mode -;; lui-set-prompt -;; lui-insert -;; lui-input-function -;; lui-completion-function -;; lui-time-stamp-time -;; lui-time-stamp-zone -;; and the 'lui-fool and 'lui-do-not-track text properties - -;;; Code: - -(require 'button) -(require 'flyspell) -(require 'help-mode) -(require 'ispell) -(require 'paren) -(require 'ring) -(require 'thingatpt) -(require 'rx) - -(require 'tracking) - - -;;;;;;;;;;;;;;;;;;;;; -;;; Customization ;;; -;;;;;;;;;;;;;;;;;;;;; - -(defgroup lui nil - "The Linewise User Interface." - :prefix "lui-" - :group 'applications) - -(defcustom lui-scroll-behavior t - "Set the behavior lui should exhibit for scrolling. - -The following values are possible. If in doubt, use post-output. - -nil - Use default Emacs scrolling. - -post-command - Keep the input line at the end of the window if point is - after the input mark. - -post-output - Keep the input line at the end of the window only after output. - -t - Combine both post-command and post-output. - -post-scroll - Keep the input line at the end of the window on every scroll - event. Careful, this might interact badly with other functions - on `window-scroll-functions'. - - -It would be entirely sensible for Emacs to provide a setting to -do this kind of scrolling by default in a buffer. It seems rather -intuitive and sensible. But as noted on emacs-devel: - - [T]hose who know the code know that it's going to be a pain to - implement, especially if you want acceptable performance. IOW, - patches welcome - -The full discussion can be found here: - -https://lists.gnu.org/archive/html/emacs-devel/2012-10/msg00652.html - -These settings are all hacks that try to give the user the choice -between most correct behavior (post-scroll) and most compliant -behavior (post-output)." - :type '(choice (const :tag "Post Command" t) - (const :tag "Post Output" post-output) - (const :tag "Post Scroll" post-scroll) - (const :tag "Use default scrolling" nil)) - :group 'lui) -(defvaralias 'lui-scroll-to-bottom-p 'lui-scroll-behavior) - -(defcustom lui-flyspell-p nil - "Non-nil if Lui should spell-check your input. -See the function `flyspell-mode' for more information." - :type 'boolean - :group 'lui) - -(defcustom lui-flyspell-alist nil - "Alist of buffer dictionaries. - -This is a list of mappings from buffers to dictionaries to use -for the function `flyspell-mode'. The appropriate dictionary is -automatically used when Lui is activated in a buffer with a -matching buffer name. - -The entries are of the form (REGEXP DICTIONARY), where REGEXP -must match a buffer name, and DICTIONARY specifies an existing -dictionary for the function `flyspell-mode'. See -`ispell-local-dictionary-alist' and `ispell-dictionary-alist' for -a valid list of dictionaries." - :type 'string - :group 'lui) - -(defcustom lui-highlight-keywords nil - "A list of keywords to highlight. - -This specifies a list of keywords that Lui should highlight. Each -entry is of one of the following forms (similar to -`font-lock-keywords'): - - REGEXP - Highlight every match in `lui-highlight-face' - (REGEXP SUBMATCH) - Highlight the SUBMATCH (a number) in REGEXP in - `lui-highlight-face' - (REGEXP FACE) - Highlight everything matching REGEXP in FACE (a symbol) - (REGEXP SUBMATCH FACE) - Highlight the SUBMATCH in REGEXP in FACE - -In all of these cases, the FACE can also be a property list which -is then associated with the match. - -All matches are run, which means later matches can override -changes by earlier ones." - :type '(repeat (choice - (string :tag "Regular Expression") - (list :tag "Submatch" - (string :tag "Regular Expression") - (integer :tag "Submatch")) - (list :tag "Regular Expression in Specific Face" - (string :tag "Regular Expression") - (face :tag "Face")) - (list :tag "Submatch in Specific Face" - (string :tag "Regular Expression") - (integer :tag "Submatch") - (face :tag "Face")))) - :group 'lui) - -(defface lui-strong-face - '((t (:inherit bold))) - "Face used for strong markup." - :group 'lui-irc-colors) - -(defface lui-emphasis-face - '((t (:inherit italic))) - "Face for emphasis markup." - :group 'lui-irc-colors) - -(defcustom lui-formatting-list nil - "List of enabled formatting types. -Each list item is a list consisting of a regular expression -matching the highlighted text, an integer for the submatch and a -face for highlighting the match." - :type `(set - (const :tag "*Strong* text" - (,(rx-to-string - '(: (: (or bol (any " \t"))) - "*" - (group (not (any " \t*")) - (* (: (+ (any " \t")) - (+ (not (any " \t*")))))) - "*" - (: (or eol (any " \t"))))) - 1 'lui-strong-face)) - (const :tag "_Emphasized_ text" - (,(rx-to-string - '(: (: (or bol (any " \t"))) - "_" - (group (not (any " \t*")) - (* (: (+ (any " \t")) - (+ (not (any " \t*")))))) - "_" - (: (or eol (any " \t"))))) - 1 'lui-emphasis-face))) - :group 'lui) - -(defcustom lui-buttons-list - `(("`\\([A-Za-z0-9+=*/-]+\\)'" 1 - lui-button-elisp-symbol 1) - ("\\") 'lui-previous-button) - (define-key map (kbd "") 'lui-previous-button) - (define-key map (kbd "M-p") 'lui-previous-input) - (define-key map (kbd "M-n") 'lui-next-input) - (define-key map (kbd "C-c C-u") 'lui-kill-to-beginning-of-line) - (define-key map (kbd "C-c C-i") 'lui-fool-toggle-display) - map) - "The key map used in Lui modes.") - -(defvar lui-input-marker nil - "The marker where input should be inserted.") -(make-variable-buffer-local 'lui-input-marker) - -(defvar lui-output-marker nil - "The marker where output should be inserted. -Use `lui-insert' instead of accessing this marker directly.") -(make-variable-buffer-local 'lui-output-marker) - -(defvar lui-input-ring nil - "The input history ring.") -(make-variable-buffer-local 'lui-input-ring) - -(defvar lui-input-ring-index nil - "The index to the current item in the input ring.") -(make-variable-buffer-local 'lui-input-ring-index) - - -;;;;;;;;;;;;;; -;;; Macros ;;; -;;;;;;;;;;;;;; - -(defmacro lui-save-undo-list (&rest body) - "Run BODY without modifying the undo list." - (let ((old-marker-sym (make-symbol "old-marker"))) - `(let ((,old-marker-sym (marker-position lui-input-marker)) - (val nil)) - ;; Don't modify the undo list. The undo list is for the user's - ;; input only. - (let ((buffer-undo-list t)) - (setq val (progn ,@body))) - (when (consp buffer-undo-list) - ;; Not t :-) - (setq buffer-undo-list (lui-adjust-undo-list buffer-undo-list - ,old-marker-sym - (- lui-input-marker - ,old-marker-sym)))) - val))) - - -;;;;;;;;;;;;;;;;;; -;;; Major Mode ;;; -;;;;;;;;;;;;;;;;;; - -(define-derived-mode lui-mode nil "LUI" - "The Linewise User Interface mode. -This can be used as a user interface for various applications. -Those should define derived modes of this, so this function -should never be called directly. - -It can be customized for an application by specifying a -`lui-input-function'." - (setq lui-input-marker (make-marker) - lui-output-marker (make-marker) - lui-input-ring (make-ring lui-input-ring-size) - lui-input-ring-index nil - flyspell-generic-check-word-p 'lui-flyspell-check-word-p) - (set-marker lui-input-marker (point-max)) - (set-marker lui-output-marker (point-max)) - (add-hook 'window-scroll-functions 'lui-scroll-window nil t) - (add-hook 'post-command-hook 'lui-scroll-post-command) - (add-hook 'change-major-mode-hook 'lui-change-major-mode nil t) - (lui-paren-highlighting) - (lui-time-stamp-enable-filtering) - (tracking-mode 1) - (auto-fill-mode 0) - (when (fboundp 'cursor-intangible-mode) - (cursor-intangible-mode 1)) - (when lui-flyspell-p - (require 'flyspell) - (lui-flyspell-change-dictionary))) - -(defun lui-change-major-mode () - "Assure that the user really wants to change the major mode. -This is a good value for a buffer-local `change-major-mode-hook'." - (when (not (y-or-n-p "Really change major mode in a Lui buffer? ")) - (error "User disallowed mode change"))) - -(defun lui-scroll-window (window _display-start) - "Scroll the input line to the bottom of the WINDOW. - -DISPLAY-START is passed by the hook `window-scroll-functions' and -is ignored. - -See `lui-scroll-behavior' for how to customize this." - (when (and (eq lui-scroll-behavior 'post-scroll) - window - (window-live-p window)) - (with-selected-window window - (when (or (>= (point) lui-input-marker) - (equal (point-max) - (window-end nil t))) - (let ((resize-mini-windows nil)) - (save-excursion - (goto-char (point-max)) - (recenter -1))))))) - -(defun lui-scroll-post-command () - "Scroll the input line to the bottom of the window. - -This is called from `post-command-hook'. - -See `lui-scroll-behavior' for how to customize this." - (condition-case err - (dolist (w (window-list)) - (with-current-buffer (window-buffer w) - (when (and lui-input-marker - (memq lui-scroll-behavior '(t post-command))) - ;; Code from ERC's erc-goodies.el. I think this was originally - ;; mine anyhow, not sure though. - (save-restriction - (widen) - (when (>= (point) lui-input-marker) - (save-excursion - (goto-char (point-max)) - (with-selected-window w - (recenter -1)))))))) - (error - (message "Error in lui-scroll-post-command: %S" err) - ))) - -(defun lui-scroll-post-output () - "Scroll the input line to the bottom of the window. - -This is called when lui output happens. - -See `lui-scroll-behavior' for how to customize this." - (when (memq lui-scroll-behavior '(t post-output)) - (let ((resize-mini-windows nil)) - (dolist (window (get-buffer-window-list (current-buffer) nil t)) - (when (or (>= (point) lui-input-marker) - (equal (point-max) - (window-end window))) - (with-selected-window window - (save-excursion - (goto-char (point-max)) - (recenter -1)))))))) - - -;;;;;;;;;;;;; -;;; Input ;;; -;;;;;;;;;;;;; - -(defun lui-send-input () - "Send the current input to the Lui application. -If point is not in the input area, insert a newline." - (interactive) - (if (< (point) lui-input-marker) - (newline) - (save-restriction - (narrow-to-region lui-input-marker (point-max)) - (run-hooks 'lui-pre-input-hook)) - (let ((input (buffer-substring lui-input-marker (point-max)))) - (delete-region lui-input-marker (point-max)) - (lui-add-input input) - (if lui-input-function - (funcall lui-input-function input) - (error "No input function specified"))))) - -(defun lui-add-input (input) - "Add INPUT as if entered by the user." - (ring-insert lui-input-ring input) - (setq lui-input-ring-index nil)) - - -;;;;;;;;;;;;;;; -;;; Buttons ;;; -;;;;;;;;;;;;;;; - -(define-button-type 'lui-button - 'supertype 'button - 'follow-link t - 'face 'lui-button-face) - -(defun lui-buttonize () - "Buttonize the current message." - (lui-buttonize-urls) - (lui-buttonize-custom) - (lui-buttonize-issues)) - -(defun lui-buttonize-custom () - "Add custom buttons to the current message. - -This uses `lui-buttons-list'." - (dolist (entry lui-buttons-list) - (let ((regex (nth 0 entry)) - (submatch (nth 1 entry)) - (function-or-url (nth 2 entry)) - (arg-matches (nthcdr 3 entry))) - (goto-char (point-min)) - (while (re-search-forward regex nil t) - ;; Ensure we're not inserting a button inside a URL button - (when (not (button-at (match-beginning 0))) - (let* ((function (if (functionp function-or-url) - function-or-url - 'browse-url)) - (matches (mapcar (lambda (n) - (match-string-no-properties n)) - arg-matches)) - (arguments (if (functionp function-or-url) - matches - (list (apply #'format function-or-url - matches))))) - (make-button (match-beginning submatch) - (match-end submatch) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function function - 'lui-button-arguments arguments))))))) - -(defun lui-buttonize-issues () - "Buttonize issue references in the current message, if configured." - (when lui-button-issue-tracker - (goto-char (point-min)) - (while (re-search-forward "\\(?:^\\|\\W\\)\\(#\\([0-9]+\\)\\)" nil t) - ;; Ensure we're not inserting a button inside a URL button - (when (not (button-at (point))) - (make-button (match-beginning 1) - (match-end 1) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function 'browse-url - 'lui-button-arguments - (list (format lui-button-issue-tracker - (match-string 2)))))))) - -(defun lui-buttonize-urls () - "Buttonize URLs in the current message." - (let ((regex (regexp-opt thing-at-point-uri-schemes))) - (goto-char (point-min)) - (while (re-search-forward regex nil t) - (let ((bounds (bounds-of-thing-at-point 'url))) - (when bounds - (make-button (car bounds) - (cdr bounds) - 'type 'lui-button - 'action 'lui-button-activate - 'lui-button-function 'browse-url - 'lui-button-arguments - (list (buffer-substring-no-properties - (car bounds) - (cdr bounds))))))))) - -(defun lui-button-activate (button) - "Activate BUTTON. -This calls the function stored in the `lui-button-function' -property with the argument stored in `lui-button-arguments'." - (apply (button-get button 'lui-button-function) - (button-get button 'lui-button-arguments))) - -(defun lui-next-button-or-complete () - "Go to the next button, or complete at point. -When point is in the input line, call `lui-completion-function'. -Otherwise, we move to the next button." - (interactive) - (if (>= (point) - lui-input-marker) - (funcall lui-completion-function) - (forward-button 1))) - -(defun lui-previous-button () - "Go to the previous button." - (interactive) - (backward-button 1)) - -(defun lui-button-elisp-symbol (name) - "Show the documentation for the symbol named NAME." - (let ((sym (intern-soft name))) - (cond - ((not sym) - (message "No such symbol %s" name) - (ding)) - (t - (help-xref-interned sym))))) - -(defun lui-button-pep (number) - "Browse the PEP NUMBER." - (browse-url (format "https://www.python.org/dev/peps/pep-%04i" - (string-to-number number)))) - -(defun lui-button-issue (issue) - "Browse the issue tracker number ISSUE, if configured." - (if lui-button-issue-tracker - (browse-url (format lui-button-issue-tracker issue)) - (error "No issue tracker configured, see `lui-button-issue-tracker'"))) - - -;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Input Line Killing ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun lui-kill-to-beginning-of-line () - "Kill the input from point to the beginning of the input." - (interactive) - (let* ((beg (point-at-bol)) - (end (point)) - (str (buffer-substring beg end))) - (delete-region beg end) - (kill-new str))) - - -;;;;;;;;;;;;;;;;;;;;; -;;; Input History ;;; -;;;;;;;;;;;;;;;;;;;;; - -;; FIXME! -;; These need some better algorithm. They clobber input when it is not -;; in the ring! -(defun lui-previous-input () - "Cycle through the input history to the last input." - (interactive) - (when (> (ring-length lui-input-ring) 0) - (if (and lui-input-ring-index - (= (1- (ring-length lui-input-ring)) - lui-input-ring-index)) - ;; last item - insert a single empty line - (progn - (lui-replace-input "") - (setq lui-input-ring-index nil)) - ;; If any input is left, store it in the input ring - (when (and (null lui-input-ring-index) - (> (point-max) lui-input-marker)) - (ring-insert lui-input-ring - (buffer-substring lui-input-marker (point-max))) - (setq lui-input-ring-index 0)) - ;; Increment the index - (setq lui-input-ring-index - (if lui-input-ring-index - (ring-plus1 lui-input-ring-index (ring-length lui-input-ring)) - 0)) - ;; And insert the last input - (lui-replace-input (ring-ref lui-input-ring lui-input-ring-index)) - (goto-char (point-max))))) - -(defun lui-next-input () - "Cycle through the input history to the next input." - (interactive) - (when (> (ring-length lui-input-ring) 0) - (if (and lui-input-ring-index - (= 0 lui-input-ring-index)) - ;; first item - insert a single empty line - (progn - (lui-replace-input "") - (setq lui-input-ring-index nil)) - ;; If any input is left, store it in the input ring - (when (and (null lui-input-ring-index) - (> (point-max) lui-input-marker)) - (ring-insert lui-input-ring - (buffer-substring lui-input-marker (point-max))) - (setq lui-input-ring-index 0)) - ;; Decrement the index - (setq lui-input-ring-index (ring-minus1 (or lui-input-ring-index 0) - (ring-length lui-input-ring))) - ;; And insert the next input - (lui-replace-input (ring-ref lui-input-ring lui-input-ring-index)) - (goto-char (point-max))))) - -(defun lui-replace-input (str) - "Replace input with STR." - (save-excursion - (goto-char lui-input-marker) - (delete-region lui-input-marker (point-max)) - (insert str))) - - -;;;;;;;;;;;;; -;;; Fools ;;; -;;;;;;;;;;;;; - -(defun lui-fools () - "Propertize the current narrowing according to foolhardiness. -That is, if any part of it has the text property 'lui-fool set, -make the whole thing invisible." - (when (text-property-any (point-min) - (point-max) - 'lui-fool t) - (add-text-properties (point-min) - (point-max) - '(invisible lui-fool)))) - -(defun lui-fools-hidden-p () - "Return whether fools are currently hidden." - (if (or (eq t buffer-invisibility-spec) - (memq 'lui-fool buffer-invisibility-spec)) - t - nil)) - -(defun lui-fool-toggle-display () - "Display what fools have said." - (interactive) - (when (eq buffer-invisibility-spec t) - (add-to-invisibility-spec 'lui-fool)) - (cond - ((lui-fools-hidden-p) - (message "Now showing the gibberish of fools") - (remove-from-invisibility-spec 'lui-fool)) - (t - (message "Now hiding fools again *phew*") - (add-to-invisibility-spec 'lui-fool))) - ;; For some reason, after this, the display does not always update - ;; (issue #31). Force an update just in case. - (force-mode-line-update t)) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; Blink Paren and Show Paren Mode ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defun lui-paren-highlighting () - "Enable sane parenthesis highlighting in this buffer." - (set (make-local-variable 'blink-paren-function) - 'lui-blink-paren-function) - (when (boundp 'show-paren-data-function) - (set (make-local-variable 'show-paren-data-function) - 'lui-show-paren-data-function))) - -(defun lui-blink-paren-function () - "Do not blink opening parens outside of the lui input area. - -When point is within the lui input area, inserting a closing -parenthesis should only blink parens within the input area, not -outside of it. - -This is a suitable value for `blink-paren-function', which see." - (if (> (point) lui-input-marker) - (let ((blink-matching-paren-distance (- (point) - lui-input-marker))) - (blink-matching-open)) - (blink-matching-open))) - -(defun lui-show-paren-data-function () - "Show parens only within the input area. - -When `show-paren-mode' is enabled, and point is in the input -area, parenthesis highlighting should only happen within the -input area, not include the rest of the buffer. - -This is a suitable value for `show-paren-data-function', which see." - (when (fboundp 'show-paren--default) - (let ((range (show-paren--default))) - (if (or (< (point) lui-input-marker) - (not (elt range 2)) - (>= (elt range 2) lui-input-marker)) - range - nil)))) - - -;;;;;;;;;;;;;;;; -;;; Flyspell ;;; -;;;;;;;;;;;;;;;; - -(defun lui-flyspell-change-dictionary (&optional dictionary) - "*Change flyspell to DICTIONARY. -If DICTIONARY is nil, set a default dictionary according to -`lui-flyspell-alist'. -If it is \"\", disable flyspell." - (interactive (list (completing-read - "Use new dictionary (RET for none, SPC to complete): " - (and (fboundp 'ispell-valid-dictionary-list) - (mapcar 'list (ispell-valid-dictionary-list))) - nil t))) - (let ((dictionary (or dictionary - (lui-find-dictionary (buffer-name))))) - (when flyspell-mode - (flyspell-mode 0)) - (when (and dictionary - (not (equal dictionary ""))) - (ispell-change-dictionary dictionary)) - (flyspell-mode 1))) - - -(defun lui-find-dictionary (buffer-name) - "Return a dictionary appropriate for BUFFER-NAME." - (let ((lis lui-flyspell-alist) - (result nil)) - (while lis - (if (string-match (caar lis) buffer-name) - (setq result (cadr (car lis)) - lis nil) - (setq lis (cdr lis)))) - result)) - -(defun lui-flyspell-check-word-p () - "Return non-nil when flyspell should verify at this position. -This is the value of Lui for `flyspell-generic-check-word-p'." - (>= (point) - lui-input-marker)) - - -;;;;;;;;;;;;;; -;;; Output ;;; -;;;;;;;;;;;;;; - -(defun lui-insert (str &optional not-tracked-p) - "Insert STR into the current Lui buffer. - -If NOT-TRACKED-P is given, this insertion won't trigger tracking -of the buffer." - (lui-save-undo-list - (save-excursion - (save-restriction - (let ((inhibit-read-only t) - (inhibit-point-motion-hooks t)) - (widen) - (goto-char lui-output-marker) - (let ((beg (point)) - (end nil)) - (insert str "\n") - (setq end (point)) - (set-marker lui-output-marker (point)) - (narrow-to-region beg end)) - (goto-char (point-min)) - (add-text-properties (point-min) - (point-max) - `(lui-raw-text ,str)) - (run-hooks 'lui-pre-output-hook) - (lui-apply-formatting) - (lui-highlight-keywords) - (lui-buttonize) - (lui-fill) - (lui-time-stamp) - (goto-char (point-min)) - (run-hooks 'lui-post-output-hook) - (lui-fools) - (goto-char (point-min)) - (let ((faces (lui-faces-in-region (point-min) - (point-max))) - (foolish (text-property-any (point-min) - (point-max) - 'lui-fool t)) - (not-tracked-p - (or not-tracked-p - (text-property-any (point-min) - (point-max) - 'lui-do-not-track t)))) - (widen) - (lui-truncate) - (lui-read-only) - (when (and (not not-tracked-p) - (not foolish)) - (tracking-add-buffer (current-buffer) - faces))) - (lui-scroll-post-output)))))) - -(defun lui-adjust-undo-list (list old-begin shift) - "Adjust undo positions in list. -LIST is in the format of `buffer-undo-list'. -Only positions after OLD-BEGIN are affected. -The positions are adjusted by SHIFT positions." - ;; This is necessary because the undo-list keeps exact buffer - ;; positions. - ;; Thanks to ERC for the idea of the code. - ;; ERC's code doesn't take care of an OLD-BEGIN value, which is - ;; necessary if you allow modification of the buffer. - (let* ((gc-cons-threshold most-positive-fixnum) ;; See debbugs#22120#47 - (adjust-position (lambda (pos) - (if (and (numberp pos) - ;; After the boundary: Adjust - (>= (abs pos) - old-begin)) - (* (if (< pos 0) - -1 - 1) - (+ (abs pos) - shift)) - pos))) - (adjust (lambda (entry) - (cond - ;; POSITION - ((numberp entry) - (funcall adjust-position entry)) - ((not (consp entry)) - entry) - ;; (BEG . END) - ((numberp (car entry)) - (cons (funcall adjust-position (car entry)) - (funcall adjust-position (cdr entry)))) - ;; (TEXT . POSITION) - ((stringp (car entry)) - (cons (car entry) - (funcall adjust-position (cdr entry)))) - ;; (nil PROPERTY VALUE BEG . END) - ((not (car entry)) - `(nil ,(nth 1 entry) - ,(nth 2 entry) - ,(funcall adjust-position (nth 3 entry)) - . - ,(funcall adjust-position (nthcdr 4 entry)))) - ;; (apply DELTA BEG END FUN-NAME . ARGS) - ((and (eq 'apply (car entry)) - (numberp (cadr entry))) - `(apply ,(nth 1 entry) - ,(funcall adjust-position (nth 2 entry)) - ,(funcall adjust-position (nth 3 entry)) - ,(nth 4 entry) - . - ,(nthcdr 5 entry))) - (t - entry))))) - (mapcar adjust list))) - -(defvar lui-prompt-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "") 'lui-prompt-end-of-line) - (define-key map (kbd "C-e") 'lui-prompt-end-of-line) - map) - "Keymap for Lui prompts. -Since \\[end-of-line] can't move out of fields, this DTRT for an -unexpecting user.") - -(defun lui-set-prompt (prompt) - "Set PROMPT as the current Lui prompt." - (let ((inhibit-read-only t)) - (lui-save-undo-list - (save-excursion - (goto-char lui-output-marker) - (insert prompt) - (if (> lui-input-marker (point)) - (delete-region (point) lui-input-marker) - (set-marker lui-input-marker (point))) - (add-text-properties lui-output-marker lui-input-marker - `(read-only t - rear-nonsticky t - field lui-prompt - keymap ,lui-prompt-map - front-sticky t - )))))) - -(defun lui-prompt-end-of-line (&optional _N) - "Move past the prompt, and then to the end of the line. -This uses `end-of-line'. - -The argument N is ignored." - (interactive "p") - (goto-char lui-input-marker) - (call-interactively 'end-of-line)) - -(defun lui-faces-in-region (beg end) - "Return a face that describes the region between BEG and END." - (goto-char beg) - (let ((faces nil)) - (while (not (= (point) end)) - (let ((face (get-text-property (point) 'face))) - (dolist (face (if (consp face) - face - (list face))) - (when (and face - (facep face) - (face-differs-from-default-p face)) - (push face faces))) - (goto-char (next-single-property-change (point) 'face - nil end)))) - faces)) - - - -;;;;;;;;;;;;;;;;;;;; -;;; Highlighting ;;; -;;;;;;;;;;;;;;;;;;;; - -(defun lui-highlight-keywords () - "Highlight the entries in the variable `lui-highlight-keywords'. - -This is called automatically when new text is inserted." - (let ((regex (lambda (entry) - (if (stringp entry) - entry - (car entry)))) - (submatch (lambda (entry) - (if (and (consp entry) - (numberp (cadr entry))) - (cadr entry) - 0))) - (properties (lambda (entry) - (let ((face (cond - ;; REGEXP - ((stringp entry) - 'lui-highlight-face) - ;; (REGEXP SUBMATCH) - ((and (numberp (cadr entry)) - (null (cddr entry))) - 'lui-highlight-face) - ;; (REGEXP FACE) - ((null (cddr entry)) - (cadr entry)) - ;; (REGEXP SUBMATCH FACE) - (t - (nth 2 entry))))) - (if (facep face) - `(face ,face) - face))))) - (dolist (entry lui-highlight-keywords) - (goto-char (point-min)) - (while (re-search-forward (funcall regex entry) nil t) - (let* ((exp (funcall submatch entry)) - (beg (match-beginning exp)) - (end (match-end exp))) - (when (not (text-property-any beg end 'lui-highlight-fontified-p t)) - (add-text-properties beg end - (append (funcall properties entry) - '(lui-highlight-fontified-p t))))))))) - -(defun lui-apply-formatting () - "Highlight the entries in `lui-formatting-list'." - (dolist (entry lui-formatting-list) - (goto-char (point-min)) - (let ((re (car entry)) - (subgroup (cadr entry)) - (face (nth 2 entry))) - (while (re-search-forward re nil t) - (when face - (add-face-text-property (match-beginning subgroup) (match-end subgroup) - face nil (current-buffer))))))) - - -;;;;;;;;;;;;;;; -;;; Filling ;;; -;;;;;;;;;;;;;;; - -(defun lui-fill () - "Fill the text in the buffer. -This is called automatically when new text is inserted. See -`lui-fill-type' and `lui-fill-column' on how to customize this -function." - (cond - ((stringp lui-fill-type) - (let ((fill-prefix lui-fill-type) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))) - ((eq lui-fill-type 'variable) - (let ((fill-prefix (save-excursion - (goto-char (point-min)) - (let ((beg (point))) - (re-search-forward "\\s-" nil t) - (make-string (- (point) beg) ? )))) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))) - ((numberp lui-fill-type) - (let ((right-end (save-excursion - (goto-char (point-min)) - (re-search-forward "\\s-" nil t) - (- (point) - (point-at-bol))))) - (goto-char (point-min)) - (when (< right-end lui-fill-type) - (insert (make-string (- lui-fill-type - right-end) - ? ))) - (let ((fill-prefix (make-string lui-fill-type ? )) - (fill-column (or lui-fill-column - fill-column))) - (fill-region (point-min) (point-max) - nil t))))) - (when lui-fill-remove-face-from-newline - (goto-char (point-min)) - (while (re-search-forward "\n" nil t) - (put-text-property (match-beginning 0) - (match-end 0) - 'face - nil)))) - - -;;;;;;;;;;;;;;;;;;; -;;; Time Stamps ;;; -;;;;;;;;;;;;;;;;;;; - -(defvar lui-time-stamp-last nil - "The last time stamp.") -(make-variable-buffer-local 'lui-time-stamp-last) - -(defvar lui-time-stamp-time nil - "A custom time to use as the time stamp for `lui-insert'. - -This variable should be let-bound when you wish to provide a -custom time to be printed by `lui-time-stamp'. If this variable -is nil the current time is used. See the TIME argument to -`format-time-string' for more information.") - -(defvar lui-time-stamp-zone nil - "A custom timezone to use for the time stamp for `lui-insert'. - -This variable should be let-bound when you wish to provide a -custom time zone when printing the time stamp with -`lui-time-stamp'. If this variable is nil local time is used. -See the ZONE argument to `format-time-string' for more -information.") - -(defun lui-time-stamp () - "Add a time stamp to the current buffer." - (let ((ts (format-time-string lui-time-stamp-format - lui-time-stamp-time - lui-time-stamp-zone))) - (cond - ;; Time stamps right - ((or (numberp lui-time-stamp-position) - (eq lui-time-stamp-position 'right)) - (when (or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (goto-char (point-min)) - (goto-char (point-at-eol)) - (let* ((curcol (current-column)) - (col (if (numberp lui-time-stamp-position) - lui-time-stamp-position - (+ 2 (or lui-fill-column - fill-column - (point))))) - (indent (if (> col curcol) - (- col curcol) - 1)) - (ts-string (propertize - (concat (make-string indent ?\s) - (propertize - ts - 'face 'lui-time-stamp-face)) - 'lui-time-stamp t)) - (start (point))) - (insert ts-string) - (add-text-properties start (1+ (point)) '(intangible t)) - (add-text-properties (1+ start) (point) '(cursor-intangible t))))) - ;; Time stamps left - ((eq lui-time-stamp-position 'left) - (let ((indent-string (propertize (make-string (length ts) ?\s) - 'lui-time-stamp t))) - (goto-char (point-min)) - (cond - ;; Time stamp - ((or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (insert (propertize ts - 'face 'lui-time-stamp-face - 'lui-time-stamp t))) - ;; Just indentation - (t - (insert indent-string))) - (forward-line 1) - (while (< (point) (point-max)) - (insert indent-string) - (forward-line 1)))) - ;; Time stamps in margin - ((or (eq lui-time-stamp-position 'right-margin) - (eq lui-time-stamp-position 'left-margin)) - (when (or (not lui-time-stamp-only-when-changed-p) - (not lui-time-stamp-last) - (not (string= ts lui-time-stamp-last))) - (goto-char (point-min)) - (goto-char (point-at-eol)) - (let* ((ts (propertize ts 'face 'lui-time-stamp-face)) - (ts-margin (propertize - " " - 'display `((margin ,lui-time-stamp-position) - ,ts) - 'lui-time-stamp t))) - (insert ts-margin))))) - (setq lui-time-stamp-last ts))) - -(defun lui-time-stamp-enable-filtering () - "Enable filtering of timestamps from copied text." - (set (make-local-variable 'filter-buffer-substring-functions) - '(lui-filter-buffer-time-stamps))) - -(defun lui-filter-buffer-time-stamps (fun beg end delete) - "Filter text from copied strings. - -This is meant for the variable `filter-buffer-substring-functions', -which see for an explanation of the argument FUN, BEG, END and -DELETE." - (let ((string (funcall fun beg end delete)) - (inhibit-point-motion-hooks t) - (inhibit-read-only t) - ;; Emacs 24.4, 24.5 - deactivate-mark) - (with-temp-buffer - (insert string) - (let ((start (text-property-any (point-min) - (point-max) - 'lui-time-stamp t))) - (while start - (let ((end (next-single-property-change start 'lui-time-stamp - nil (point-max)))) - (delete-region start end) - (setq start (text-property-any (point-min) (point-max) - 'lui-time-stamp t)))) - (buffer-string))))) - -(defun lui-time-stamp-buffer-substring (buffer-string) - "Filter text from copied strings. - -This is meant for the variable `buffer-substring-filters', -which see for an explanation of the argument BUFFER-STRING." - (lui-filter-buffer-time-stamps (lambda (_beg _end _delete) - buffer-string) - nil nil nil)) - - -;;;;;;;;;;;;;;;;;; -;;; Truncating ;;; -;;;;;;;;;;;;;;;;;; - -(defun lui-truncate () - "Truncate the current buffer if it exceeds `lui-max-buffer-size'." - (when (and lui-max-buffer-size - (> (point-max) - lui-max-buffer-size)) - (goto-char (- (point-max) - lui-max-buffer-size)) - (forward-line 0) - (let ((inhibit-read-only t)) - (delete-region (point-min) (point))))) - - -;;;;;;;;;;;;;;;;; -;;; Read-Only ;;; -;;;;;;;;;;;;;;;;; - -(defun lui-read-only () - "Make the current output read-only if `lui-read-only-output-p' is non-nil." - (when lui-read-only-output-p - (add-text-properties (point-min) lui-output-marker - '(read-only t - front-sticky t)))) - - -(provide 'lui) -;;; lui.el ends here diff --git a/elpa/circe-20160608.1315/make-tls-process.el b/elpa/circe-20160608.1315/make-tls-process.el deleted file mode 100644 index 71a2618..0000000 --- a/elpa/circe-20160608.1315/make-tls-process.el +++ /dev/null @@ -1,194 +0,0 @@ -;;; make-tls-process.el --- A non-blocking TLS connection function - -;; Copyright (C) 2015 Jorgen Schaefer - -;; Author: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License -;; as published by the Free Software Foundation; either version 3 -;; of the License, or (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; A `make-tls-process' function like `make-network-process', in -;; particular supporting non-blocking connects. - -;;; Code: - -(require 'tls) - -(defcustom tls-connection-command - (if (executable-find "gnutls-cli") - "gnutls-cli --insecure -p %p %h" - "openssl s_client -connect %h:%p -no_ssl2 -ign_eof") - "The command to use to create a TLS connection. - -%h is replaced with server hostname, %p with port to connect to. -The program should read input on stdin and write output to -stdout. - -Also see `tls-success' for what the program should output after -successful negotiation." - :group 'tls - :type 'string) - -(defvar tls-debug-output nil - "Non-nil if you want to see lots of debug messages.") - -(defun tls--debug (format-string &rest args) - "Display a message if debug output is enabled. - -If `tls-debug-output' is non-nil, this acts like `message'. -Otherwise, it's a no-op." - (when tls-debug-output - (apply #'message format-string args))) - -(defun make-tls-process (&rest args) - "Create a TLS client process. - -A TLS network process is a command process that runs a command -line program like gnutls or openssl, not a full network process. -Network communication should work as usual, but the sentinel -might receive process-specific events. - -Different from a process sentinel, but like a network sentinel, -the sentinel is called with an event \"open\\n\" when the -connection is established. - -This function uses `tls-connection-command' to connect to a -server. - -Do NOT use `set-process-filter' or `set-process-sentinel' on the -return value of this function. The connection setup uses special -sentinels and filters to be deal with the program output used -here. Use the :sentinel and :filter keyword arguments to set them -once the connection is fully established. - -Arguments are specified as keyword/argument pairs, similar to -`make-network-process'. The following arguments are defined: - -:name NAME -- NAME is name for process. It is modified if necessary -to make it unique. - -:buffer BUFFER -- BUFFER is the buffer (or buffer-name) to associate -with the process. Process output goes at end of that buffer, unless -you specify an output stream or filter function to handle the output. -BUFFER may be also nil, meaning that this process is not associated -with any buffer. - -:host HOST -- HOST is name of the host to connect to, or its IP -address. The symbol `local' specifies the local host. If specified -for a server process, it must be a valid name or address for the local -host, and only clients connecting to that address will be accepted. - -:service SERVICE -- SERVICE is name of the service desired, or an -integer specifying a port number to connect to. If SERVICE is t, -a random port number is selected for the server. (If Emacs was -compiled with getaddrinfo, a port number can also be specified as -a string, e.g. \"80\", as well as an integer. This is not -portable.) - -:coding CODING -- If CODING is a symbol, it specifies the coding -system used for both reading and writing for this process. If CODING -is a cons (DECODING . ENCODING), DECODING is used for reading, and -ENCODING is used for writing. - -:noquery BOOL -- Query the user unless BOOL is non-nil, and process is -running when Emacs is exited. - -:filter FILTER -- Install FILTER as the process filter. - -:sentinel SENTINEL -- Install SENTINEL as the process sentinel. - -:plist PLIST -- Install PLIST as the new process's initial plist." - (let* ((name (plist-get args :name)) - (host (plist-get args :host)) - (service (plist-get args :service)) - (proc (tls--start-process name tls-connection-command host service))) - (process-put proc :tls-args args) - (set-process-sentinel proc #'tls--sentinel) - (set-process-filter proc #'tls--filter) - proc)) - -(defun tls--sentinel (proc event) - "The default sentinel for TLS connections. - -Try the next command in the list, or fail if there are none -left." - (tls--debug "tls--sentinel %S %S" - (process-status proc) - event) - (tls--debug "Failed TLS output: %s" - (process-get proc :tls-data)) - (if (eq (process-status proc) - 'exit) - (let ((sentinel (plist-get (process-get proc :tls-args) - :sentinel))) - (when sentinel - (funcall sentinel proc (format "failed with %s\n" event)))) - (error "Unexpected event in tls sentinel: %S" event))) - -(defun tls--filter (proc data) - "The default filter for TLS connections. - -We wait until both `tls-success' and `tls-end-of-info' have been -received. Once that happens, we are done and we can switch over -to the real connection." - (let ((data (concat (or (process-get proc :tls-data) - "") - data))) - (if (and (string-match tls-success data) - (string-match tls-end-of-info data)) - (let* ((remaining-data (substring data (match-end 0))) - (args (process-get proc :tls-args)) - (buffer (plist-get args :buffer)) - (coding (plist-get args :coding)) - (noquery (plist-get args :noquery)) - (filter (plist-get args :filter)) - (sentinel (plist-get args :sentinel)) - (plist (plist-get args :plist))) - (set-process-plist proc plist) - (set-process-sentinel proc sentinel) - (set-process-filter proc filter) - (set-process-buffer proc buffer) - (if (consp coding) - (set-process-coding-system proc (car coding) (cdr coding)) - (set-process-coding-system proc coding coding)) - (set-process-query-on-exit-flag proc (not noquery)) - (when sentinel - (funcall sentinel proc "open\n")) - (when (and (not (equal remaining-data "")) - filter) - (funcall filter proc remaining-data))) - (process-put proc :tls-data data)))) - -(defun tls--start-process (name cmd host port) - "Start a single process for network communication. - -This code is mostly taken from tls.el." - (let ((process-connection-type tls-process-connection-type) - (formatted-cmd - (format-spec - cmd - (format-spec-make - ?h host - ?p (if (integerp port) - (int-to-string port) - port))))) - (tls--debug "TLS starting process: %s" formatted-cmd) - (start-process name nil - shell-file-name shell-command-switch - formatted-cmd))) - -(provide 'make-tls-process) -;;; make-tls-process.el ends here diff --git a/elpa/circe-20160608.1315/shorten.el b/elpa/circe-20160608.1315/shorten.el deleted file mode 100644 index 1ba6085..0000000 --- a/elpa/circe-20160608.1315/shorten.el +++ /dev/null @@ -1,223 +0,0 @@ -;;; shorten.el --- component-wise string shortener - -;; Copyright (C) 2013 John J Foerch - -;; Keywords: extensions -;; Author: John J Foerch -;; URL: https://github.com/jorgenschaefer/circe/blob/master/shorten.el - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This is a component-wise string shortener, meaning that, given a list -;; of strings, it breaks each string into parts, then computes shortest -;; prefix of each part with respect to others of the same 'depth', such -;; that when joined back together, the shortened form of the whole string -;; remains unique within the resulting list. Many styles of shortening -;; are made possible via three functions that the caller may provide: the -;; split function, the join function, and the validate-component function. -;; -;; Strings are broken with the value of `shorten-split-function' (a -;; procedure string->list), and shortened components are rejoined with the -;; value of `shorten-join-function' (a procedure list->string[*]). The -;; default split and join functions break the string on word boundaries, -;; and rejoin on the empty string. Potential shortened forms of -;; components are tested with `shorten-validate-component-function'; its -;; default value passes only if its argument contains at least one -;; word-constituent character (regexp \w), meaning that by default, -;; components consisting entirely of non-word characters will not be -;; shortened, and components that start with non-word characters will only -;; be shortened so much that they have at least one word-constituent -;; character in them. -;; -;; The main entry point is `shorten-strings', which takes a list of strings -;; as its argument and returns an alist ((STRING . SHORTENED-STRING) ...). -;; -;; [*] Also takes a second argument; see docstring of -;; `shorten-join-function'. - -;;; History: - -;; - Version 0.1 (March 7, 2013): initial release - -;;; Code: - -;; Tree utils -;; -(defsubst shorten-make-tree-root () - (cons nil nil)) - -(defsubst shorten-tree-make-entry (token short full) - (list token short full nil)) - -(defsubst shorten-tree-token (entry) - (car entry)) - -(defsubst shorten-tree-fullname (entry) - (nth 2 entry)) - -(defsubst shorten-tree-descendants (entry) - (nthcdr 3 entry)) - -(defsubst shorten-tree-set-shortened (entry short) - (setcar (cdr entry) short)) - -(defsubst shorten-tree-set-fullname (entry full) - (setcar (nthcdr 2 entry) full)) - -(defsubst shorten-tree-insert (node item) - (when (car node) - (setcdr node (cons (car node) (cdr node)))) - (setcar node item)) - - -;; Caller configuration -;; -(defun shorten-split (s) - (split-string s "\\b" t)) - -(defun shorten-join (lst &optional tail-count) - (mapconcat #'identity lst "")) - -(defun shorten-join-sans-tail (lst tail-count) - "A shorten-join that drops unnecessary tail components." - (shorten-join (butlast lst tail-count))) - -(defun shorten-validate-component (str) - (string-match-p "\\w" str)) - -(defvar shorten-split-function #'shorten-split - "Value should be a function of string->list that breaks a -string into components. The default breaks on word-boundaries. -To get simple prefix shortening, bind this to `list'. - -Users should not generally change the global value of this -variable; instead, bind it dynamically around calls to -`shorten-strings'.") - -(defvar shorten-join-function #'shorten-join - "A function that takes a list of components and a tail-count, -and returns a joined string. Tail-count is the number of -components on the end of the list that are not needed to uniquify -the result, and so may be safely dropped if aggressive shortening -is desired. The default preserves tail components, and joins the -list on the empty string. - -Users should not generally change the global value of this -variable; instead, bind it dynamically around calls to -`shorten-strings'.") - -(defvar shorten-validate-component-function #'shorten-validate-component - "Predicate that returns t if a proposed shortened form of a -single component is acceptable, nil if a longer one should be -tried. The default validates only when the candidate contains at -least one word-constituent character, thus strings consisting of -punctuation will not be shortened. For aggressive shortening, -bind to a procedure that always returns t. - -Users should not generally change the global value of this -variable; instead, bind it dynamically around calls to -`shorten-strings'.") - - -;; Main procedures -;; -(defun shorten-one (str others) - "Return shortest unique prefix of STR among OTHERS, or STR if -it cannot be shortened. If STR is a member of OTHERS (tested -with `eq') that entry is ignored. The value of -`shorten-validate-component-function' will be used to validate -any prefix." - (let ((max (length str)) - (len 1)) - (or (catch 'return - (while (< len max) - (let ((prefix (substring str 0 len))) - (when (funcall shorten-validate-component-function prefix) - (when (catch 'return - (dolist (other others t) - (when (and (>= (length other) len) - (string= (substring other 0 len) prefix) - (not (eq other str))) - (throw 'return nil)))) - (throw 'return prefix))) - (setq len (1+ len))))) - str))) - -(defun shorten-walk-internal (node path tail-count result-out) - (let ((others (mapcar #'car node))) - (setq tail-count (if (cdr node) 0 (1+ tail-count))) - (dolist (entry node) - (let* ((token (shorten-tree-token entry)) - (shortened (shorten-one token others)) - (path (cons shortened path)) - (fullname (shorten-tree-fullname entry)) - (descendants (shorten-tree-descendants entry)) - (have-descendants (not (equal '(nil) descendants)))) - (shorten-tree-set-shortened entry shortened) - ;; if this entry has a fullname, add to result-out - (when fullname - (let ((joined (funcall shorten-join-function - (reverse path) - (if have-descendants 0 tail-count)))) - (shorten-tree-insert result-out (cons fullname joined)))) - ;; if this entry has descendants, recurse - (when have-descendants - (shorten-walk-internal descendants path - (if fullname -1 tail-count) - result-out)))))) - -(defun shorten-walk (tree) - "Takes a tree of the type made by `shorten-make-tree' and -returns an alist ((STRING . SHORTENED-STRING) ...). Uses -`shorten-join-function' to join shortened components back -together into SHORTENED-STRING. See also -`shorten-validate-component-function'." - (let ((result-out (shorten-make-tree-root))) - (shorten-walk-internal tree '() -1 result-out) - (if (equal '(nil) result-out) nil result-out))) - -(defun shorten-make-tree (strings) - "Takes a list of strings and returns a tree of the type used by -`shorten-walk' to generate shortened strings. Uses -`shorten-split-function' to split the strings." - (let ((tree (shorten-make-tree-root))) - (dolist (s strings) - (let ((node tree) - (tokens (funcall shorten-split-function s)) - (entry nil)) - ;; create a path in tree for tokens - (dolist (token tokens) - (setq entry (assoc token node)) - (when (not entry) - (setq entry (shorten-tree-make-entry token nil nil)) - (shorten-tree-insert node entry)) - (setq node (shorten-tree-descendants entry))) - ;; for the last token, set 'fullname' - (shorten-tree-set-fullname entry s))) - (if (equal tree '(nil)) nil tree))) - -;;;###autoload -(defun shorten-strings (strings) - "Takes a list of strings and returns an alist ((STRING -. SHORTENED-STRING) ...). Uses `shorten-split-function' to split -the strings, and `shorten-join-function' to join shortened -components back together into SHORTENED-STRING. See also -`shorten-validate-component-function'." - (shorten-walk (shorten-make-tree strings))) - - -(provide 'shorten) -;;; shorten.el ends here diff --git a/elpa/circe-20160608.1315/tracking.el b/elpa/circe-20160608.1315/tracking.el deleted file mode 100644 index 028090d..0000000 --- a/elpa/circe-20160608.1315/tracking.el +++ /dev/null @@ -1,391 +0,0 @@ -;;; tracking.el --- Buffer modification tracking - -;; Copyright (C) 2006, 2012 - 2015 Jorgen Schaefer - -;; Author: Jorgen Schaefer -;; URL: https://github.com/jorgenschaefer/circe/wiki/Tracking - -;; This program is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; tracking.el is a library for other Emacs Lisp programs not useful -;; by itself. - -;; The library provides a way to globally register buffers as being -;; modified and scheduled for user review. The user can cycle through -;; the buffers using C-c C-SPC. This is especially useful for buffers -;; that interact with external sources, such as chat clients and -;; similar programs. - -;;; Code: - -(require 'easy-mmode) -(require 'shorten) -(require 'cl-lib) - -;;; User customization -(defgroup tracking nil - "Tracking of buffer activities." - :prefix "tracking-" - :group 'applications) - -(defcustom tracking-shorten-buffer-names-p t - "Whether to shorten buffer names in the mode line. -A non-nil value will cause tracked buffer names to be shortened -as much as possible to stay unambiguous when displaying them in -the mode line." - :type 'boolean - :group 'tracking) - -(defcustom tracking-frame-behavior 'visible - "How to deal with frams to determine visibility of buffers. -This is passed as the second argument to `get-buffer-window', -see there for further explanation." - :type '(choice (const :tag "All visible frames" visible) - (const :tag "Visible and iconified frames" 0) - (const :tag "All frames" t) - (const :tag "Selected frame only" nil)) - :group 'tracking) - -(defcustom tracking-position 'before-modes - "Where tracked buffers should appear in the mode line. - - 'before-modes - Before the mode indicators - 'after-modes - After the mode indicators - 'end - At the end of the mode line" - :type '(choice (const :tag "Before the Mode Indicators" before-modes) - (const :tag "Afterthe Mode Indicators" after-modes) - (const :tag "At the End of the Mode Line" end)) - :group 'tracking) - -(defcustom tracking-faces-priorities nil - "A list of faces which should be shown by tracking in the mode line. -The first face found in this list is used." - :type '(repeat face) - :group 'tracking) - -(defcustom tracking-ignored-buffers nil - "A list of buffers that are never tracked. -Each element of this list has one of the following forms: - - regexp - Any buffer matching won't be tracked. - function - Any buffer matching won't be tracked. - (regexp faces ...) - Any buffer matching won't be tracked, - unless it has a face in FACES ... associated with it. - If no faces are given, `tracking-faces-priorities' is - used. - (function faces ...) - As per above, but with a function - as predicate instead of a regexp." - :type '(repeat (choice regexp - function - (list (choice regexp function) - (repeat face)))) - :group 'tracking) - -(defcustom tracking-most-recent-first nil - "When non-nil, newly tracked buffers will go to the front of the -list, rather than to the end." - :type 'boolean - :group 'tracking) - -(defcustom tracking-buffer-added-hook nil - "Hook run when a buffer has some activity. - -The functions are run in the context of the buffer. - -This can also happen when the buffer is already tracked. Check if the -buffer name is in `tracking-buffers' if you want to see if it was -added before." - :type 'hook - :group 'tracking) - -(defcustom tracking-buffer-removed-hook nil - "Hook run when a buffer becomes active and is removed. - -The functions are run in the context of the buffer." - :type 'hook - :group 'tracking) - -;;; Internal variables -(defvar tracking-buffers nil - "The list of currently tracked buffers.") - -(defvar tracking-mode-line-buffers "" - "The entry to the mode line.") -(put 'tracking-mode-line-buffers 'risky-local-variable t) - -(defvar tracking-start-buffer nil - "The buffer we started from when cycling through the active buffers.") - -(defvar tracking-last-buffer nil - "The buffer we last switched to with `tracking-next-buffer'. -When this is not the current buffer when we continue switching, a -new `tracking-start-buffer' is created.") - -(defvar tracking-mode-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "C-c C-SPC") 'tracking-next-buffer) - (define-key map (kbd "C-c C-@") 'tracking-next-buffer) - map) - "The keymap used for tracking mode.") - -;;;###autoload -(define-minor-mode tracking-mode - "Allow cycling through modified buffers. -This mode in itself does not track buffer modification, but -provides an API for programs to add buffers as modified (using -`tracking-add-buffer'). - -Once this mode is active, modified buffers are shown in the mode -line. The user can cycle through them using -\\[tracking-next-buffer]." - :group 'tracking - :global t - (cond - (tracking-mode - (cond - ((eq tracking-position 'before-modes) - (let ((head nil) - (tail (default-value 'mode-line-format))) - (when (not (memq 'tracking-mode-line-buffers tail)) - (catch 'return - (while tail - (if (not (eq (car tail) - 'mode-line-modes)) - (setq head (cons (car tail) - head) - tail (cdr tail)) - (setq-default mode-line-format - (append (reverse head) - '(tracking-mode-line-buffers) - tail)) - (throw 'return t))))))) - ((eq tracking-position 'after-modes) - (add-to-list 'mode-line-misc-info - 'tracking-mode-line-buffers)) - ((eq tracking-position 'end) - (add-to-list 'mode-line-misc-info - 'tracking-mode-line-buffers - t)) - (t - (error "Invalid value for `tracking-position' (%s)" tracking-position))) - (add-hook 'window-configuration-change-hook - 'tracking-remove-visible-buffers)) - (t - (setq mode-line-misc-info (delq 'tracking-mode-line-buffers - mode-line-misc-info)) - (setq-default mode-line-format (delq 'tracking-mode-line-buffers - (default-value 'mode-line-format))) - (remove-hook 'window-configuration-change-hook - 'tracking-remove-visible-buffers)))) - -;;;###autoload -(defun tracking-add-buffer (buffer &optional faces) - "Add BUFFER as being modified with FACES. -This does check whether BUFFER is currently visible. - -If FACES is given, it lists the faces that might be appropriate -for BUFFER in the mode line. The highest-priority face of these -and the current face of the buffer, if any, is used. Priority is -decided according to `tracking-faces-priorities'." - (when (and (not (get-buffer-window buffer tracking-frame-behavior)) - (not (tracking-ignored-p buffer faces))) - (with-current-buffer buffer - (run-hooks 'tracking-buffer-added-hook)) - (let* ((entry (member (buffer-name buffer) - tracking-buffers))) - (if entry - (setcar entry (tracking-faces-merge (car entry) - faces)) - (setq tracking-buffers - (if tracking-most-recent-first - (cons (tracking-faces-merge (buffer-name buffer) - faces) - tracking-buffers) - (nconc tracking-buffers - (list (tracking-faces-merge (buffer-name buffer) - faces))))))) - (setq tracking-mode-line-buffers (tracking-status)) - (force-mode-line-update t) - )) - -;;;###autoload -(defun tracking-remove-buffer (buffer) - "Remove BUFFER from being tracked." - (when (member (buffer-name buffer) - tracking-buffers) - (with-current-buffer buffer - (run-hooks 'tracking-buffer-removed-hook))) - (setq tracking-buffers (delete (buffer-name buffer) - tracking-buffers)) - (setq tracking-mode-line-buffers (tracking-status)) - (sit-for 0) ;; Update mode line - ) - -;;;###autoload -(defun tracking-next-buffer () - "Switch to the next active buffer." - (interactive) - (cond - ((and (not tracking-buffers) - tracking-start-buffer) - (let ((buf tracking-start-buffer)) - (setq tracking-start-buffer nil) - (if (buffer-live-p buf) - (switch-to-buffer buf) - (message "Original buffer does not exist anymore") - (ding)))) - ((not tracking-buffers) - nil) - (t - (when (not (eq tracking-last-buffer - (current-buffer))) - (setq tracking-start-buffer (current-buffer))) - (let ((new (car tracking-buffers))) - (when (buffer-live-p (get-buffer new)) - (with-current-buffer new - (run-hooks 'tracking-buffer-removed-hook))) - (setq tracking-buffers (cdr tracking-buffers) - tracking-mode-line-buffers (tracking-status)) - (if (buffer-live-p (get-buffer new)) - (switch-to-buffer new) - (message "Buffer %s does not exist anymore" new) - (ding) - (setq tracking-mode-line-buffers (tracking-status)))) - (setq tracking-last-buffer (current-buffer)) - ;; Update mode line. See `force-mode-line-update' for the idea for - ;; this code. Using `sit-for' can be quite inefficient for larger - ;; buffers. - (dolist (w (window-list)) - (with-current-buffer (window-buffer w))) - ))) - -;;;###autoload -(defun tracking-previous-buffer () - "Switch to the last active buffer." - (interactive) - (when tracking-buffers - (switch-to-buffer (car (last tracking-buffers))))) - -(defun tracking-ignored-p (buffer faces) - "Return non-nil when BUFFER with FACES shouldn't be tracked. -This uses `tracking-ignored-buffers'. Actual returned value is -the entry from tracking-ignored-buffers that causes this buffer -to be ignored." - (catch 'return - (let ((buffer-name (buffer-name buffer))) - (dolist (entry tracking-ignored-buffers) - (cond - ((stringp entry) - (and (string-match entry buffer-name) - (throw 'return entry))) - ((functionp entry) - (and (funcall entry buffer-name) - (throw 'return entry))) - ((or (and (stringp (car entry)) - (string-match (car entry) buffer-name)) - (and (functionp (car entry)) - (funcall (car entry) buffer-name))) - (when (not (tracking-any-in (or (cdr entry) - tracking-faces-priorities) - faces)) - (throw 'return entry)))))) - nil)) - -(defun tracking-status () - "Return the current track status. - -This returns a list suitable for `mode-line-format'." - (if (not tracking-buffers) - "" - (let* ((buffer-names (cl-remove-if-not #'get-buffer tracking-buffers)) - (shortened-names (tracking-shorten tracking-buffers)) - (result (list " ["))) - (while buffer-names - (push `(:propertize - ,(car shortened-names) - face ,(get-text-property 0 'face (car buffer-names)) - keymap ,(let ((map (make-sparse-keymap))) - (define-key map [mode-line down-mouse-1] - `(lambda () - (interactive) - (pop-to-buffer ,(car buffer-names)))) - map) - mouse-face mode-line-highlight - help-echo ,(format (concat "New activity in %s\n" - "mouse-1: pop to the buffer") - (car buffer-names))) - result) - (setq buffer-names (cdr buffer-names) - shortened-names (cdr shortened-names)) - (when buffer-names - (push "," result))) - (push "] " result) - (nreverse result)))) - -(defun tracking-remove-visible-buffers () - "Remove visible buffers from the tracked buffers. -This is usually called via `window-configuration-changed-hook'." - (interactive) - (dolist (buffer-name tracking-buffers) - (let ((buffer (get-buffer buffer-name))) - (cond - ((not buffer) - (setq tracking-buffers (delete buffer-name tracking-buffers)) - (setq tracking-mode-line-buffers (tracking-status)) - (sit-for 0)) - ((get-buffer-window buffer tracking-frame-behavior) - (tracking-remove-buffer buffer)))))) - -;;; Helper functions -(defun tracking-shorten (buffers) - "Shorten BUFFERS according to `tracking-shorten-buffer-names-p'." - (if tracking-shorten-buffer-names-p - (let ((all (shorten-strings (mapcar #'buffer-name (buffer-list))))) - (mapcar (lambda (buffer) - (let ((short (cdr (assoc buffer all)))) - (set-text-properties - 0 (length short) - (text-properties-at 0 buffer) - short) - short)) - buffers)) - buffers)) - -(defun tracking-any-in (lista listb) - "Return non-nil when any element in LISTA is in LISTB" - (catch 'return - (dolist (entry lista) - (when (memq entry listb) - (throw 'return t))) - nil)) - -(defun tracking-faces-merge (string faces) - "Merge faces into string, adhering to `tracking-faces-priorities'. -This returns STRING with the new face." - (let ((faces (cons (get-text-property 0 'face string) - faces))) - (catch 'return - (dolist (candidate tracking-faces-priorities) - (when (memq candidate faces) - (throw 'return - (propertize string 'face candidate)))) - string))) - -(provide 'tracking) -;;; tracking.el ends here diff --git a/elpa/emojify-20160928.550/data/emoji-sets.json b/elpa/emojify-20160928.550/data/emoji-sets.json deleted file mode 100644 index f501830..0000000 --- a/elpa/emojify-20160928.550/data/emoji-sets.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "emojione-v2-22" : { - "description" : "Emojis provided by Emoji One (version 2), resized to 22px", - "website" : "http://emojione.com", - "url" : "https://github.com/iqbalansari/emacs-emojify/blob/a81cfd11cdd0eb5b6840d2a7fe95a9505195c1a3/emojione-v2-22.tar?raw=true", - "sha256" : "adbe3cf2c776fe7daf375d8e8dbd4c40567a1dbb753dce1d05e61a2f815572d3" - }, - "emojione-v2" : { - "description" : "Emojis provided by Emoji One (version 2)", - "website" : "http://emojione.com", - "url" : "https://github.com/iqbalansari/emacs-emojify/blob/a81cfd11cdd0eb5b6840d2a7fe95a9505195c1a3/emojione-v2.tar?raw=true", - "sha256" : "46c5a600a148897da22d42d36f42ad764868568943e96917c33e0fe44113afef" - }, - "emojione-v2.2.6-22" : { - "description" : "Emojis provided by Emoji One (version 2.2.6), resized to 22px", - "website" : "http://emojione.com", - "url" : "https://github.com/iqbalansari/emacs-emojify/blob/4e91ba8c2b3415cd78f53e7026fc76b9ac935fc3/emojione-v2.2.6-22.tar?raw=true", - "sha256" : "56dede1c77ad690eebc21e00913b9c7525d290f1a936f87aad282014b04bf2a7" - }, - "emojione-v2.2.6" : { - "description" : "Emojis provided by Emoji One (version 2.2.6)", - "website" : "http://emojione.com", - "url" : "https://github.com/iqbalansari/emacs-emojify/blob/4e91ba8c2b3415cd78f53e7026fc76b9ac935fc3/emojione-v2.2.6.tar?raw=true", - "sha256" : "416b5807d9836a7030434710c9b859accce1e2e5c3c0dcae8ef2a0d9483ff2e9" - } -} diff --git a/elpa/emojify-20160928.550/data/emoji.json b/elpa/emojify-20160928.550/data/emoji.json deleted file mode 100644 index 020a637..0000000 --- a/elpa/emojify-20160928.550/data/emoji.json +++ /dev/null @@ -1,31306 +0,0 @@ -{ - ":man-with-gua-pi-mao-tone3:": { - "style": "github", - "image": "1f472-1f3fd.png", - "unicode": "👲🏽", - "name": "Man With Gua Pi Mao - Tone 3" - }, - ":couplekiss_mm:": { - "style": "github", - "image": "1f468-2764-1f48b-1f468.png", - "unicode": "👨❤💋👨", - "name": "Kiss (man,man)" - }, - "🎍": { - "style": "unicode", - "image": "1f38d.png", - "name": "Pine Decoration" - }, - ":thumbsup-tone5:": { - "style": "github", - "image": "1f44d-1f3ff.png", - "unicode": "👍🏿", - "name": "Thumbs Up Sign - Tone 5" - }, - "🦑": { - "style": "unicode", - "image": "1f991.png", - "name": "Squid" - }, - ":mp:": { - "style": "github", - "image": "1f1f2-1f1f5.png", - "unicode": "🇲🇵", - "name": "Northern Mariana Islands" - }, - ":person_frowning_tone2:": { - "style": "github", - "image": "1f64d-1f3fc.png", - "unicode": "🙍🏼", - "name": "Person Frowning - Tone 2" - }, - ":point-up-tone2:": { - "style": "github", - "image": "261d-1f3fc.png", - "unicode": "☝🏼", - "name": "White Up Pointing Index - Tone 2" - }, - "🤦": { - "style": "unicode", - "image": "1f926.png", - "name": "Face Palm" - }, - ":raised_hands_tone3:": { - "style": "github", - "image": "1f64c-1f3fd.png", - "unicode": "🙌🏽", - "name": "Person Raising Both Hands In Celebration - Tone 3" - }, - ":face_with_head_bandage:": { - "style": "github", - "image": "1f915.png", - "unicode": "🤕", - "name": "Face With Head-bandage" - }, - ":school_satchel:": { - "style": "github", - "image": "1f392.png", - "unicode": "🎒", - "name": "School Satchel" - }, - "✅": { - "style": "unicode", - "image": "2705.png", - "name": "White Heavy Check Mark" - }, - ":guardsman_tone5:": { - "style": "github", - "image": "1f482-1f3ff.png", - "unicode": "💂🏿", - "name": "Guardsman - Tone 5" - }, - ":yum:": { - "style": "github", - "image": "1f60b.png", - "unicode": "😋", - "name": "Face Savouring Delicious Food" - }, - "😏": { - "style": "unicode", - "image": "1f60f.png", - "name": "Smirking Face" - }, - ":red_car:": { - "style": "github", - "image": "1f697.png", - "unicode": "🚗", - "name": "Automobile" - }, - "↖": { - "style": "unicode", - "image": "2196.png", - "name": "North West Arrow" - }, - "🚤": { - "style": "unicode", - "image": "1f6a4.png", - "name": "Speedboat" - }, - "☯": { - "style": "unicode", - "image": "262f.png", - "name": "Yin Yang" - }, - ":v_tone5:": { - "style": "github", - "image": "270c-1f3ff.png", - "unicode": "✌🏿", - "name": "Victory Hand - Tone 5" - }, - ":bar-chart:": { - "style": "github", - "image": "1f4ca.png", - "unicode": "📊", - "name": "Bar Chart" - }, - ":flag-ls:": { - "style": "github", - "image": "1f1f1-1f1f8.png", - "unicode": "🇱🇸", - "name": "Lesotho" - }, - "🔹": { - "style": "unicode", - "image": "1f539.png", - "name": "Small Blue Diamond" - }, - ":slight_smile:": { - "style": "github", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - ":secret:": { - "style": "github", - "image": "3299.png", - "unicode": "㊙", - "name": "Circled Ideograph Secret" - }, - "⛄": { - "style": "unicode", - "image": "26c4.png", - "name": "Snowman Without Snow" - }, - ":hand_splayed_tone2:": { - "style": "github", - "image": "1f590-1f3fc.png", - "unicode": "🖐🏼", - "name": "Raised Hand With Fingers Splayed - Tone 2" - }, - ":family_mwg:": { - "style": "github", - "image": "1f468-1f469-1f467.png", - "unicode": "👨👩👧", - "name": "Family (man,woman,girl)" - }, - ":flag_ae:": { - "style": "github", - "image": "1f1e6-1f1ea.png", - "unicode": "🇦🇪", - "name": "The United Arab Emirates" - }, - "👣": { - "style": "unicode", - "image": "1f463.png", - "name": "Footprints" - }, - ":blue-car:": { - "style": "github", - "image": "1f699.png", - "unicode": "🚙", - "name": "Recreational Vehicle" - }, - ":man_dancing_tone3:": { - "style": "github", - "image": "1f57a-1f3fd.png", - "unicode": "🕺🏽", - "name": "Man Dancing - Tone 3" - }, - ":basketball-player-tone5:": { - "style": "github", - "image": "26f9-1f3ff.png", - "unicode": "⛹🏿", - "name": "Person With Ball - Tone 5" - }, - ":man_tone2:": { - "style": "github", - "image": "1f468-1f3fc.png", - "unicode": "👨🏼", - "name": "Man - Tone 2" - }, - "📸": { - "style": "unicode", - "image": "1f4f8.png", - "name": "Camera With Flash" - }, - ":kiwifruit:": { - "style": "github", - "image": "1f95d.png", - "unicode": "🥝", - "name": "Kiwifruit" - }, - ":studio_microphone:": { - "style": "github", - "image": "1f399.png", - "unicode": "🎙", - "name": "Studio Microphone" - }, - ":flag_ve:": { - "style": "github", - "image": "1f1fb-1f1ea.png", - "unicode": "🇻🇪", - "name": "Venezuela" - }, - ":sm:": { - "style": "github", - "image": "1f1f8-1f1f2.png", - "unicode": "🇸🇲", - "name": "San Marino" - }, - ":wilted-rose:": { - "style": "github", - "image": "1f940.png", - "unicode": "🥀", - "name": "Wilted Flower" - }, - "⬜": { - "style": "unicode", - "image": "2b1c.png", - "name": "White Large Square" - }, - ":family_mwbb:": { - "style": "github", - "image": "1f468-1f469-1f466-1f466.png", - "unicode": "👨👩👦👦", - "name": "Family (man,woman,boy,boy)" - }, - "🎷": { - "style": "unicode", - "image": "1f3b7.png", - "name": "Saxophone" - }, - ":prayer_beads:": { - "style": "github", - "image": "1f4ff.png", - "unicode": "📿", - "name": "Prayer Beads" - }, - ":regional_indicator_b:": { - "style": "github", - "image": "1f1e7.png", - "unicode": "🇧", - "name": "Regional Indicator Symbol Letter B" - }, - "🍌": { - "style": "unicode", - "image": "1f34c.png", - "name": "Banana" - }, - "🥐": { - "style": "unicode", - "image": "1f950.png", - "name": "Croissant" - }, - ":middle_finger_tone3:": { - "style": "github", - "image": "1f595-1f3fd.png", - "unicode": "🖕🏽", - "name": "Reversed Hand With Middle Finger Extended - Tone 3" - }, - ":sl:": { - "style": "github", - "image": "1f1f8-1f1f1.png", - "unicode": "🇸🇱", - "name": "Sierra Leone" - }, - "🛥": { - "style": "unicode", - "image": "1f6e5.png", - "name": "Motorboat" - }, - ":left_right_arrow:": { - "style": "github", - "image": "2194.png", - "unicode": "↔", - "name": "Left Right Arrow" - }, - ":flag-tc:": { - "style": "github", - "image": "1f1f9-1f1e8.png", - "unicode": "🇹🇨", - "name": "Turks And Caicos Islands" - }, - ":place-of-worship:": { - "style": "github", - "image": "1f6d0.png", - "unicode": "🛐", - "name": "Place Of Worship" - }, - ":wave_tone5:": { - "style": "github", - "image": "1f44b-1f3ff.png", - "unicode": "👋🏿", - "name": "Waving Hand Sign - Tone 5" - }, - ":tired_face:": { - "style": "github", - "image": "1f62b.png", - "unicode": "😫", - "name": "Tired Face" - }, - "💖": { - "style": "unicode", - "image": "1f496.png", - "name": "Sparkling Heart" - }, - ":pen_ballpoint:": { - "style": "github", - "image": "1f58a.png", - "unicode": "🖊", - "name": "Lower Left Ballpoint Pen" - }, - ":two_women_holding_hands:": { - "style": "github", - "image": "1f46d.png", - "unicode": "👭", - "name": "Two Women Holding Hands" - }, - ":relaxed:": { - "style": "github", - "image": "263a.png", - "unicode": "☺", - "name": "White Smiling Face" - }, - ":large_orange_diamond:": { - "style": "github", - "image": "1f536.png", - "unicode": "🔶", - "name": "Large Orange Diamond" - }, - ":flag_ao:": { - "style": "github", - "image": "1f1e6-1f1f4.png", - "unicode": "🇦🇴", - "name": "Angola" - }, - ":flag_pw:": { - "style": "github", - "image": "1f1f5-1f1fc.png", - "unicode": "🇵🇼", - "name": "Palau" - }, - "🐹": { - "style": "unicode", - "image": "1f439.png", - "name": "Hamster Face" - }, - ":flag-mt:": { - "style": "github", - "image": "1f1f2-1f1f9.png", - "unicode": "🇲🇹", - "name": "Malta" - }, - ":pregnant-woman-tone1:": { - "style": "github", - "image": "1f930-1f3fb.png", - "unicode": "🤰🏻", - "name": "Pregnant Woman - Tone 1" - }, - ":writing-hand:": { - "style": "github", - "image": "270d.png", - "unicode": "✍", - "name": "Writing Hand" - }, - ":water_polo_tone5:": { - "style": "github", - "image": "1f93d-1f3ff.png", - "unicode": "🤽🏿", - "name": "Water Polo - Tone 5" - }, - ":deciduous_tree:": { - "style": "github", - "image": "1f333.png", - "unicode": "🌳", - "name": "Deciduous Tree" - }, - "📎": { - "style": "unicode", - "image": "1f4ce.png", - "name": "Paperclip" - }, - ":no-entry-sign:": { - "style": "github", - "image": "1f6ab.png", - "unicode": "🚫", - "name": "No Entry Sign" - }, - ":flag-sc:": { - "style": "github", - "image": "1f1f8-1f1e8.png", - "unicode": "🇸🇨", - "name": "The Seychelles" - }, - ":flag-kr:": { - "style": "github", - "image": "1f1f0-1f1f7.png", - "unicode": "🇰🇷", - "name": "Korea" - }, - "🕣": { - "style": "unicode", - "image": "1f563.png", - "name": "Clock Face Eight-thirty" - }, - ":raising-hand-tone2:": { - "style": "github", - "image": "1f64b-1f3fc.png", - "unicode": "🙋🏼", - "name": "Happy Person Raising One Hand Tone2" - }, - ":flag-pr:": { - "style": "github", - "image": "1f1f5-1f1f7.png", - "unicode": "🇵🇷", - "name": "Puerto Rico" - }, - ":circus-tent:": { - "style": "github", - "image": "1f3aa.png", - "unicode": "🎪", - "name": "Circus Tent" - }, - ":bow_tone3:": { - "style": "github", - "image": "1f647-1f3fd.png", - "unicode": "🙇🏽", - "name": "Person Bowing Deeply - Tone 3" - }, - ":money_mouth_face:": { - "style": "github", - "image": "1f911.png", - "unicode": "🤑", - "name": "Money-mouth Face" - }, - "☘": { - "style": "unicode", - "image": "2618.png", - "name": "Shamrock" - }, - ":date:": { - "style": "github", - "image": "1f4c5.png", - "unicode": "📅", - "name": "Calendar" - }, - ":flag_nu:": { - "style": "github", - "image": "1f1f3-1f1fa.png", - "unicode": "🇳🇺", - "name": "Niue" - }, - ":stuffed_pita:": { - "style": "github", - "image": "1f959.png", - "unicode": "🥙", - "name": "Stuffed Flatbread" - }, - ":tropical_drink:": { - "style": "github", - "image": "1f379.png", - "unicode": "🍹", - "name": "Tropical Drink" - }, - ":flag_za:": { - "style": "github", - "image": "1f1ff-1f1e6.png", - "unicode": "🇿🇦", - "name": "South Africa" - }, - "👩👩👧👧": { - "style": "unicode", - "image": "1f469-1f469-1f467-1f467.png", - "name": "Family (woman,woman,girl,girl)" - }, - "👩👩👧👦": { - "style": "unicode", - "image": "1f469-1f469-1f467-1f466.png", - "name": "Family (woman,woman,girl,boy)" - }, - "🏡": { - "style": "unicode", - "image": "1f3e1.png", - "name": "House With Garden" - }, - ":kaaba:": { - "style": "github", - "image": "1f54b.png", - "unicode": "🕋", - "name": "Kaaba" - }, - "🍶": { - "style": "unicode", - "image": "1f376.png", - "name": "Sake Bottle And Cup" - }, - "🕺": { - "style": "unicode", - "image": "1f57a.png", - "name": "Man Dancing" - }, - "🤽🏽": { - "style": "unicode", - "image": "1f93d-1f3fd.png", - "name": "Water Polo - Tone 3" - }, - "🤽🏼": { - "style": "unicode", - "image": "1f93d-1f3fc.png", - "name": "Water Polo - Tone 2" - }, - "🤽🏿": { - "style": "unicode", - "image": "1f93d-1f3ff.png", - "name": "Water Polo - Tone 5" - }, - "🤽🏾": { - "style": "unicode", - "image": "1f93d-1f3fe.png", - "name": "Water Polo - Tone 4" - }, - "🐏": { - "style": "unicode", - "image": "1f40f.png", - "name": "Ram" - }, - ":fork_knife_plate:": { - "style": "github", - "image": "1f37d.png", - "unicode": "🍽", - "name": "Fork And Knife With Plate" - }, - ":high-brightness:": { - "style": "github", - "image": "1f506.png", - "unicode": "🔆", - "name": "High Brightness Symbol" - }, - ":zipper-mouth:": { - "style": "github", - "image": "1f910.png", - "unicode": "🤐", - "name": "Zipper-mouth Face" - }, - ":regional-indicator-g:": { - "style": "github", - "image": "1f1ec.png", - "unicode": "🇬", - "name": "Regional Indicator Symbol Letter G" - }, - ":blue_car:": { - "style": "github", - "image": "1f699.png", - "unicode": "🚙", - "name": "Recreational Vehicle" - }, - "🤽": { - "style": "unicode", - "image": "1f93d.png", - "name": "Water Polo" - }, - ":cartwheel-tone1:": { - "style": "github", - "image": "1f938-1f3fb.png", - "unicode": "🤸🏻", - "name": "Person Doing Cartwheel - Tone 1" - }, - ":speaking_head_in_silhouette:": { - "style": "github", - "image": "1f5e3.png", - "unicode": "🗣", - "name": "Speaking Head In Silhouette" - }, - ":mailbox:": { - "style": "github", - "image": "1f4eb.png", - "unicode": "📫", - "name": "Closed Mailbox With Raised Flag" - }, - ":department-store:": { - "style": "github", - "image": "1f3ec.png", - "unicode": "🏬", - "name": "Department Store" - }, - ":fk:": { - "style": "github", - "image": "1f1eb-1f1f0.png", - "unicode": "🇫🇰", - "name": "Falkland Islands" - }, - ":slight-frown:": { - "style": "github", - "image": "1f641.png", - "unicode": "🙁", - "name": "Slightly Frowning Face" - }, - ":eg:": { - "style": "github", - "image": "1f1ea-1f1ec.png", - "unicode": "🇪🇬", - "name": "Egypt" - }, - ":ok-hand-tone5:": { - "style": "github", - "image": "1f44c-1f3ff.png", - "unicode": "👌🏿", - "name": "Ok Hand Sign - Tone 5" - }, - ":calendar_spiral:": { - "style": "github", - "image": "1f5d3.png", - "unicode": "🗓", - "name": "Spiral Calendar Pad" - }, - ":virgo:": { - "style": "github", - "image": "264d.png", - "unicode": "♍", - "name": "Virgo" - }, - ":boy-tone2:": { - "style": "github", - "image": "1f466-1f3fc.png", - "unicode": "👦🏼", - "name": "Boy - Tone 2" - }, - ":womens:": { - "style": "github", - "image": "1f6ba.png", - "unicode": "🚺", - "name": "Womens Symbol" - }, - ":couple_with_heart_mm:": { - "style": "github", - "image": "1f468-2764-1f468.png", - "unicode": "👨❤👨", - "name": "Couple (man,man)" - }, - ":man_in_tuxedo_tone2:": { - "style": "github", - "image": "1f935-1f3fc.png", - "unicode": "🤵🏼", - "name": "Man In Tuxedo - Tone 2" - }, - ":flag_mq:": { - "style": "github", - "image": "1f1f2-1f1f6.png", - "unicode": "🇲🇶", - "name": "Martinique" - }, - ":raised_back_of_hand:": { - "style": "github", - "image": "1f91a.png", - "unicode": "🤚", - "name": "Raised Back Of Hand" - }, - ":bo:": { - "style": "github", - "image": "1f1e7-1f1f4.png", - "unicode": "🇧🇴", - "name": "Bolivia" - }, - "📥": { - "style": "unicode", - "image": "1f4e5.png", - "name": "Inbox Tray" - }, - "💤": { - "style": "unicode", - "image": "1f4a4.png", - "name": "Sleeping Symbol" - }, - "👺": { - "style": "unicode", - "image": "1f47a.png", - "name": "Japanese Goblin" - }, - ":flag-cg:": { - "style": "github", - "image": "1f1e8-1f1ec.png", - "unicode": "🇨🇬", - "name": "The Republic Of The Congo" - }, - ":shrug_tone2:": { - "style": "github", - "image": "1f937-1f3fc.png", - "unicode": "🤷🏼", - "name": "Shrug - Tone 2" - }, - ":cancer:": { - "style": "github", - "image": "264b.png", - "unicode": "♋", - "name": "Cancer" - }, - "🌋": { - "style": "unicode", - "image": "1f30b.png", - "name": "Volcano" - }, - "🔏": { - "style": "unicode", - "image": "1f50f.png", - "name": "Lock With Ink Pen" - }, - ":jo:": { - "style": "github", - "image": "1f1ef-1f1f4.png", - "unicode": "🇯🇴", - "name": "Jordan" - }, - ":video-camera:": { - "style": "github", - "image": "1f4f9.png", - "unicode": "📹", - "name": "Video Camera" - }, - ":man-tone3:": { - "style": "github", - "image": "1f468-1f3fd.png", - "unicode": "👨🏽", - "name": "Man - Tone 3" - }, - "🎠": { - "style": "unicode", - "image": "1f3a0.png", - "name": "Carousel Horse" - }, - "🖤": { - "style": "unicode", - "image": "1f5a4.png", - "name": "Black Heart" - }, - ":baby-tone4:": { - "style": "github", - "image": "1f476-1f3fe.png", - "unicode": "👶🏾", - "name": "Baby - Tone 4" - }, - ":angel_tone3:": { - "style": "github", - "image": "1f47c-1f3fd.png", - "unicode": "👼🏽", - "name": "Baby Angel - Tone 3" - }, - ":loudspeaker:": { - "style": "github", - "image": "1f4e2.png", - "unicode": "📢", - "name": "Public Address Loudspeaker" - }, - "😹": { - "style": "unicode", - "image": "1f639.png", - "name": "Cat Face With Tears Of Joy" - }, - ":dancer_tone3:": { - "style": "github", - "image": "1f483-1f3fd.png", - "unicode": "💃🏽", - "name": "Dancer - Tone 3" - }, - "🇲🇴": { - "style": "unicode", - "image": "1f1f2-1f1f4.png", - "name": "Macau" - }, - ":raised-hands:": { - "style": "github", - "image": "1f64c.png", - "unicode": "🙌", - "name": "Person Raising Both Hands In Celebration" - }, - ":monkey:": { - "style": "github", - "image": "1f412.png", - "unicode": "🐒", - "name": "Monkey" - }, - "🛎": { - "style": "unicode", - "image": "1f6ce.png", - "name": "Bellhop Bell" - }, - "🇲🇷": { - "style": "unicode", - "image": "1f1f2-1f1f7.png", - "name": "Mauritania" - }, - ":flag_tf:": { - "style": "github", - "image": "1f1f9-1f1eb.png", - "unicode": "🇹🇫", - "name": "French Southern Territories" - }, - ":flag_bt:": { - "style": "github", - "image": "1f1e7-1f1f9.png", - "unicode": "🇧🇹", - "name": "Bhutan" - }, - ":bath_tone4:": { - "style": "github", - "image": "1f6c0-1f3fe.png", - "unicode": "🛀🏾", - "name": "Bath - Tone 4" - }, - ":robot:": { - "style": "github", - "image": "1f916.png", - "unicode": "🤖", - "name": "Robot Face" - }, - ":muscle-tone2:": { - "style": "github", - "image": "1f4aa-1f3fc.png", - "unicode": "💪🏼", - "name": "Flexed Biceps - Tone 2" - }, - ":construction-worker-tone1:": { - "style": "github", - "image": "1f477-1f3fb.png", - "unicode": "👷🏻", - "name": "Construction Worker - Tone 1" - }, - ":eye_in_speech_bubble:": { - "style": "github", - "image": "1f441-1f5e8.png", - "unicode": "👁🗨", - "name": "Eye In Speech Bubble" - }, - "🇲🇼": { - "style": "unicode", - "image": "1f1f2-1f1fc.png", - "name": "Malawi" - }, - ":stuck_out_tongue:": { - "style": "github", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":family_mmg:": { - "style": "github", - "image": "1f468-1f468-1f467.png", - "unicode": "👨👨👧", - "name": "Family (man,man,girl)" - }, - ":gift:": { - "style": "github", - "image": "1f381.png", - "unicode": "🎁", - "name": "Wrapped Present" - }, - ":arrow-upper-left:": { - "style": "github", - "image": "2196.png", - "unicode": "↖", - "name": "North West Arrow" - }, - ":gl:": { - "style": "github", - "image": "1f1ec-1f1f1.png", - "unicode": "🇬🇱", - "name": "Greenland" - }, - ":white-check-mark:": { - "style": "github", - "image": "2705.png", - "unicode": "✅", - "name": "White Heavy Check Mark" - }, - ":fox:": { - "style": "github", - "image": "1f98a.png", - "unicode": "🦊", - "name": "Fox Face" - }, - "🚷": { - "style": "unicode", - "image": "1f6b7.png", - "name": "No Pedestrians" - }, - ":whale2:": { - "style": "github", - "image": "1f40b.png", - "unicode": "🐋", - "name": "Whale" - }, - "🙌": { - "style": "unicode", - "image": "1f64c.png", - "name": "Person Raising Both Hands In Celebration" - }, - "🇲🇬": { - "style": "unicode", - "image": "1f1f2-1f1ec.png", - "name": "Madagascar" - }, - ":smile:": { - "style": "github", - "image": "1f604.png", - "unicode": "😄", - "name": "Smiling Face With Open Mouth And Smiling Eyes" - }, - ":male_dancer_tone4:": { - "style": "github", - "image": "1f57a-1f3fe.png", - "unicode": "🕺🏾", - "name": "Man Dancing - Tone 4" - }, - ":bath-tone3:": { - "style": "github", - "image": "1f6c0-1f3fd.png", - "unicode": "🛀🏽", - "name": "Bath - Tone 3" - }, - "🗡": { - "style": "unicode", - "image": "1f5e1.png", - "name": "Dagger Knife" - }, - "🏥": { - "style": "unicode", - "image": "1f3e5.png", - "name": "Hospital" - }, - "*\\0/*": { - "style": "ascii", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - ":dvd:": { - "style": "github", - "image": "1f4c0.png", - "unicode": "📀", - "name": "Dvd" - }, - ":cherries:": { - "style": "github", - "image": "1f352.png", - "unicode": "🍒", - "name": "Cherries" - }, - "🕶": { - "style": "unicode", - "image": "1f576.png", - "name": "Dark Sunglasses" - }, - "🍺": { - "style": "unicode", - "image": "1f37a.png", - "name": "Beer Mug" - }, - ":man-with-gua-pi-mao-tone1:": { - "style": "github", - "image": "1f472-1f3fb.png", - "unicode": "👲🏻", - "name": "Man With Gua Pi Mao - Tone 1" - }, - ":man-in-tuxedo-tone5:": { - "style": "github", - "image": "1f935-1f3ff.png", - "unicode": "🤵🏿", - "name": "Man In Tuxedo - Tone 5" - }, - "🐋": { - "style": "unicode", - "image": "1f40b.png", - "name": "Whale" - }, - ":flag_lv:": { - "style": "github", - "image": "1f1f1-1f1fb.png", - "unicode": "🇱🇻", - "name": "Latvia" - }, - ":mr:": { - "style": "github", - "image": "1f1f2-1f1f7.png", - "unicode": "🇲🇷", - "name": "Mauritania" - }, - ":ch:": { - "style": "github", - "image": "1f1e8-1f1ed.png", - "unicode": "🇨🇭", - "name": "Switzerland" - }, - ":currency-exchange:": { - "style": "github", - "image": "1f4b1.png", - "unicode": "💱", - "name": "Currency Exchange" - }, - ":point-up-tone4:": { - "style": "github", - "image": "261d-1f3fe.png", - "unicode": "☝🏾", - "name": "White Up Pointing Index - Tone 4" - }, - "💠": { - "style": "unicode", - "image": "1f4a0.png", - "name": "Diamond Shape With A Dot Inside" - }, - ":walking:": { - "style": "github", - "image": "1f6b6.png", - "unicode": "🚶", - "name": "Pedestrian" - }, - ":back_of_hand_tone5:": { - "style": "github", - "image": "1f91a-1f3ff.png", - "unicode": "🤚🏿", - "name": "Raised Back Of Hand - Tone 5" - }, - ":airplane-small:": { - "style": "github", - "image": "1f6e9.png", - "unicode": "🛩", - "name": "Small Airplane" - }, - ":haircut-tone4:": { - "style": "github", - "image": "1f487-1f3fe.png", - "unicode": "💇🏾", - "name": "Haircut - Tone 4" - }, - ":kh:": { - "style": "github", - "image": "1f1f0-1f1ed.png", - "unicode": "🇰🇭", - "name": "Cambodia" - }, - "👋🏻": { - "style": "unicode", - "image": "1f44b-1f3fb.png", - "name": "Waving Hand Sign - Tone 1" - }, - "👋🏿": { - "style": "unicode", - "image": "1f44b-1f3ff.png", - "name": "Waving Hand Sign - Tone 5" - }, - "🏻": { - "style": "unicode", - "image": "1f3fb.png", - "name": "Emoji Modifier Fitzpatrick Type-1-2" - }, - "👋🏽": { - "style": "unicode", - "image": "1f44b-1f3fd.png", - "name": "Waving Hand Sign - Tone 3" - }, - "👋🏼": { - "style": "unicode", - "image": "1f44b-1f3fc.png", - "name": "Waving Hand Sign - Tone 2" - }, - ":older_woman:": { - "style": "github", - "image": "1f475.png", - "unicode": "👵", - "name": "Older Woman" - }, - ":raised_hands_tone5:": { - "style": "github", - "image": "1f64c-1f3ff.png", - "unicode": "🙌🏿", - "name": "Person Raising Both Hands In Celebration - Tone 5" - }, - ":suspension-railway:": { - "style": "github", - "image": "1f69f.png", - "unicode": "🚟", - "name": "Suspension Railway" - }, - ":ping-pong:": { - "style": "github", - "image": "1f3d3.png", - "unicode": "🏓", - "name": "Table Tennis Paddle And Ball" - }, - "🚍": { - "style": "unicode", - "image": "1f68d.png", - "name": "Oncoming Bus" - }, - ":black_circle:": { - "style": "github", - "image": "26ab.png", - "unicode": "⚫", - "name": "Medium Black Circle" - }, - ":seat:": { - "style": "github", - "image": "1f4ba.png", - "unicode": "💺", - "name": "Seat" - }, - "😢": { - "style": "unicode", - "ascii": ":'(", - "image": "1f622.png", - "name": "Crying Face" - }, - ":lemon:": { - "style": "github", - "image": "1f34b.png", - "unicode": "🍋", - "name": "Lemon" - }, - ":flag-lu:": { - "style": "github", - "image": "1f1f1-1f1fa.png", - "unicode": "🇱🇺", - "name": "Luxembourg" - }, - ":py:": { - "style": "github", - "image": "1f1f5-1f1fe.png", - "unicode": "🇵🇾", - "name": "Paraguay" - }, - ":swimmer-tone1:": { - "style": "github", - "image": "1f3ca-1f3fb.png", - "unicode": "🏊🏻", - "name": "Swimmer - Tone 1" - }, - "📡": { - "style": "unicode", - "image": "1f4e1.png", - "name": "Satellite Antenna" - }, - ":classical_building:": { - "style": "github", - "image": "1f3db.png", - "unicode": "🏛", - "name": "Classical Building" - }, - "👶": { - "style": "unicode", - "image": "1f476.png", - "name": "Baby" - }, - ":mother_christmas_tone2:": { - "style": "github", - "image": "1f936-1f3fc.png", - "unicode": "🤶🏼", - "name": "Mother Christmas - Tone 2" - }, - ":man_dancing_tone1:": { - "style": "github", - "image": "1f57a-1f3fb.png", - "unicode": "🕺🏻", - "name": "Man Dancing - Tone 1" - }, - ":relieved:": { - "style": "github", - "image": "1f60c.png", - "unicode": "😌", - "name": "Relieved Face" - }, - "🔋": { - "style": "unicode", - "image": "1f50b.png", - "name": "Battery" - }, - "🌏": { - "style": "unicode", - "image": "1f30f.png", - "name": "Earth Globe Asia-australia" - }, - ":raised_back_of_hand_tone1:": { - "style": "github", - "image": "1f91a-1f3fb.png", - "unicode": "🤚🏻", - "name": "Raised Back Of Hand - Tone 1" - }, - "🤓": { - "style": "unicode", - "image": "1f913.png", - "name": "Nerd Face" - }, - ":100:": { - "style": "github", - "image": "1f4af.png", - "unicode": "💯", - "name": "Hundred Points Symbol" - }, - "🎤": { - "style": "unicode", - "image": "1f3a4.png", - "name": "Microphone" - }, - ":shower:": { - "style": "github", - "image": "1f6bf.png", - "unicode": "🚿", - "name": "Shower" - }, - ":postal_horn:": { - "style": "github", - "image": "1f4ef.png", - "unicode": "📯", - "name": "Postal Horn" - }, - ":last-quarter-moon-with-face:": { - "style": "github", - "image": "1f31c.png", - "unicode": "🌜", - "name": "Last Quarter Moon With Face" - }, - "◀": { - "style": "unicode", - "image": "25c0.png", - "name": "Black Left-pointing Triangle" - }, - ":+1:": { - "style": "github", - "image": "1f44d.png", - "unicode": "👍", - "name": "Thumbs Up Sign" - }, - ":bat:": { - "style": "github", - "image": "1f987.png", - "unicode": "🦇", - "name": "Bat" - }, - ":thumbdown:": { - "style": "github", - "image": "1f44e.png", - "unicode": "👎", - "name": "Thumbs Down Sign" - }, - ":flag_vg:": { - "style": "github", - "image": "1f1fb-1f1ec.png", - "unicode": "🇻🇬", - "name": "British Virgin Islands" - }, - "🇸": { - "style": "unicode", - "image": "1f1f8.png", - "name": "Regional Indicator Symbol Letter S" - }, - ":middle_finger_tone1:": { - "style": "github", - "image": "1f595-1f3fb.png", - "unicode": "🖕🏻", - "name": "Reversed Hand With Middle Finger Extended - Tone 1" - }, - ":point_up_tone1:": { - "style": "github", - "image": "261d-1f3fb.png", - "unicode": "☝🏻", - "name": "White Up Pointing Index - Tone 1" - }, - ":kissing-closed-eyes:": { - "style": "github", - "image": "1f61a.png", - "unicode": "😚", - "name": "Kissing Face With Closed Eyes" - }, - ":face_with_rolling_eyes:": { - "style": "github", - "image": "1f644.png", - "unicode": "🙄", - "name": "Face With Rolling Eyes" - }, - "🖍": { - "style": "unicode", - "image": "1f58d.png", - "name": "Lower Left Crayon" - }, - "🔢": { - "style": "unicode", - "image": "1f522.png", - "name": "Input Symbol For Numbers" - }, - ":flag-mr:": { - "style": "github", - "image": "1f1f2-1f1f7.png", - "unicode": "🇲🇷", - "name": "Mauritania" - }, - ":np:": { - "style": "github", - "image": "1f1f3-1f1f5.png", - "unicode": "🇳🇵", - "name": "Nepal" - }, - ":flag_ai:": { - "style": "github", - "image": "1f1e6-1f1ee.png", - "unicode": "🇦🇮", - "name": "Anguilla" - }, - "💷": { - "style": "unicode", - "image": "1f4b7.png", - "name": "Banknote With Pound Sign" - }, - ":prince_tone5:": { - "style": "github", - "image": "1f934-1f3ff.png", - "unicode": "🤴🏿", - "name": "Prince - Tone 5" - }, - ":man_with_turban_tone2:": { - "style": "github", - "image": "1f473-1f3fc.png", - "unicode": "👳🏼", - "name": "Man With Turban - Tone 2" - }, - "👌": { - "style": "unicode", - "image": "1f44c.png", - "name": "Ok Hand Sign" - }, - ":rabbit:": { - "style": "github", - "image": "1f430.png", - "unicode": "🐰", - "name": "Rabbit Face" - }, - ":gear:": { - "style": "github", - "image": "2699.png", - "unicode": "⚙", - "name": "Gear" - }, - ":truck:": { - "style": "github", - "image": "1f69a.png", - "unicode": "🚚", - "name": "Delivery Truck" - }, - ":mega:": { - "style": "github", - "image": "1f4e3.png", - "unicode": "📣", - "name": "Cheering Megaphone" - }, - ":bow_tone1:": { - "style": "github", - "image": "1f647-1f3fb.png", - "unicode": "🙇🏻", - "name": "Person Bowing Deeply - Tone 1" - }, - ":person_with_blond_hair_tone5:": { - "style": "github", - "image": "1f471-1f3ff.png", - "unicode": "👱🏿", - "name": "Person With Blond Hair - Tone 5" - }, - "😋": { - "style": "unicode", - "image": "1f60b.png", - "name": "Face Savouring Delicious Food" - }, - "➖": { - "style": "unicode", - "image": "2796.png", - "name": "Heavy Minus Sign" - }, - ":water_polo:": { - "style": "github", - "image": "1f93d.png", - "unicode": "🤽", - "name": "Water Polo" - }, - ":seedling:": { - "style": "github", - "image": "1f331.png", - "unicode": "🌱", - "name": "Seedling" - }, - "🚠": { - "style": "unicode", - "image": "1f6a0.png", - "name": "Mountain Cableway" - }, - ":right-facing-fist:": { - "style": "github", - "image": "1f91c.png", - "unicode": "🤜", - "name": "Right-facing Fist" - }, - "👆🏻": { - "style": "unicode", - "image": "1f446-1f3fb.png", - "name": "White Up Pointing Backhand Index - Tone 1" - }, - "👆🏼": { - "style": "unicode", - "image": "1f446-1f3fc.png", - "name": "White Up Pointing Backhand Index - Tone 2" - }, - "👆🏽": { - "style": "unicode", - "image": "1f446-1f3fd.png", - "name": "White Up Pointing Backhand Index - Tone 3" - }, - ":flag_ga:": { - "style": "github", - "image": "1f1ec-1f1e6.png", - "unicode": "🇬🇦", - "name": "Gabon" - }, - "👆🏿": { - "style": "unicode", - "image": "1f446-1f3ff.png", - "name": "White Up Pointing Backhand Index - Tone 5" - }, - ":admission_tickets:": { - "style": "github", - "image": "1f39f.png", - "unicode": "🎟", - "name": "Admission Tickets" - }, - ":au:": { - "style": "github", - "image": "1f1e6-1f1fa.png", - "unicode": "🇦🇺", - "name": "Australia" - }, - "🌹": { - "style": "unicode", - "image": "1f339.png", - "name": "Rose" - }, - "🏎": { - "style": "unicode", - "image": "1f3ce.png", - "name": "Racing Car" - }, - "🚣🏻": { - "style": "unicode", - "image": "1f6a3-1f3fb.png", - "name": "Rowboat - Tone 1" - }, - "🚣🏿": { - "style": "unicode", - "image": "1f6a3-1f3ff.png", - "name": "Rowboat - Tone 5" - }, - "🚣🏾": { - "style": "unicode", - "image": "1f6a3-1f3fe.png", - "name": "Rowboat - Tone 4" - }, - "🚣🏽": { - "style": "unicode", - "image": "1f6a3-1f3fd.png", - "name": "Rowboat - Tone 3" - }, - "🚣🏼": { - "style": "unicode", - "image": "1f6a3-1f3fc.png", - "name": "Rowboat - Tone 2" - }, - ":burrito:": { - "style": "github", - "image": "1f32f.png", - "unicode": "🌯", - "name": "Burrito" - }, - ":ie:": { - "style": "github", - "image": "1f1ee-1f1ea.png", - "unicode": "🇮🇪", - "name": "Ireland" - }, - "⏮": { - "style": "unicode", - "image": "23ee.png", - "name": "Black Left-pointing Double Triangle With Vertical Bar" - }, - ":speak-no-evil:": { - "style": "github", - "image": "1f64a.png", - "unicode": "🙊", - "name": "Speak-no-evil Monkey" - }, - ":man_with_gua_pi_mao_tone1:": { - "style": "github", - "image": "1f472-1f3fb.png", - "unicode": "👲🏻", - "name": "Man With Gua Pi Mao - Tone 1" - }, - ":regional-indicator-e:": { - "style": "github", - "image": "1f1ea.png", - "unicode": "🇪", - "name": "Regional Indicator Symbol Letter E" - }, - "💍": { - "style": "unicode", - "image": "1f48d.png", - "name": "Ring" - }, - ":horse:": { - "style": "github", - "image": "1f434.png", - "unicode": "🐴", - "name": "Horse Face" - }, - ":dragon:": { - "style": "github", - "image": "1f409.png", - "unicode": "🐉", - "name": "Dragon" - }, - ":flag-bz:": { - "style": "github", - "image": "1f1e7-1f1ff.png", - "unicode": "🇧🇿", - "name": "Belize" - }, - "🐢": { - "style": "unicode", - "image": "1f422.png", - "name": "Turtle" - }, - ":fog:": { - "style": "github", - "image": "1f32b.png", - "unicode": "🌫", - "name": "Fog" - }, - ":house-with-garden:": { - "style": "github", - "image": "1f3e1.png", - "unicode": "🏡", - "name": "House With Garden" - }, - "🕌": { - "style": "unicode", - "image": "1f54c.png", - "name": "Mosque" - }, - "🥔": { - "style": "unicode", - "image": "1f954.png", - "name": "Potato" - }, - "🛡": { - "style": "unicode", - "image": "1f6e1.png", - "name": "Shield" - }, - ":fm:": { - "style": "github", - "image": "1f1eb-1f1f2.png", - "unicode": "🇫🇲", - "name": "Micronesia" - }, - ":hear-no-evil:": { - "style": "github", - "image": "1f649.png", - "unicode": "🙉", - "name": "Hear-no-evil Monkey" - }, - ":poo:": { - "style": "github", - "image": "1f4a9.png", - "unicode": "💩", - "name": "Pile Of Poo" - }, - ":family_wwb:": { - "style": "github", - "image": "1f469-1f469-1f466.png", - "unicode": "👩👩👦", - "name": "Family (woman,woman,boy)" - }, - "☁": { - "style": "unicode", - "image": "2601.png", - "name": "Cloud" - }, - "👧🏿": { - "style": "unicode", - "image": "1f467-1f3ff.png", - "name": "Girl - Tone 5" - }, - "👧🏾": { - "style": "unicode", - "image": "1f467-1f3fe.png", - "name": "Girl - Tone 4" - }, - ":family-mmgg:": { - "style": "github", - "image": "1f468-1f468-1f467-1f467.png", - "unicode": "👨👨👧👧", - "name": "Family (man,man,girl,girl)" - }, - "👧🏼": { - "style": "unicode", - "image": "1f467-1f3fc.png", - "name": "Girl - Tone 2" - }, - "👧🏻": { - "style": "unicode", - "image": "1f467-1f3fb.png", - "name": "Girl - Tone 1" - }, - ":flag_li:": { - "style": "github", - "image": "1f1f1-1f1ee.png", - "unicode": "🇱🇮", - "name": "Liechtenstein" - }, - ":clock1:": { - "style": "github", - "image": "1f550.png", - "unicode": "🕐", - "name": "Clock Face One Oclock" - }, - ":arrows-counterclockwise:": { - "style": "github", - "image": "1f504.png", - "unicode": "🔄", - "name": "Anticlockwise Downwards And Upwards Open Circle Arrows" - }, - ":my:": { - "style": "github", - "image": "1f1f2-1f1fe.png", - "unicode": "🇲🇾", - "name": "Malaysia" - }, - ":mouse2:": { - "style": "github", - "image": "1f401.png", - "unicode": "🐁", - "name": "Mouse" - }, - ":musical-keyboard:": { - "style": "github", - "image": "1f3b9.png", - "unicode": "🎹", - "name": "Musical Keyboard" - }, - ":santa_tone2:": { - "style": "github", - "image": "1f385-1f3fc.png", - "unicode": "🎅🏼", - "name": "Father Christmas - Tone 2" - }, - ":warning:": { - "style": "github", - "image": "26a0.png", - "unicode": "⚠", - "name": "Warning Sign" - }, - ":flag_ph:": { - "style": "github", - "image": "1f1f5-1f1ed.png", - "unicode": "🇵🇭", - "name": "The Philippines" - }, - ":kayak:": { - "style": "github", - "image": "1f6f6.png", - "unicode": "🛶", - "name": "Canoe" - }, - "🈹": { - "style": "unicode", - "image": "1f239.png", - "name": "Squared Cjk Unified Ideograph-5272" - }, - ":japanese-ogre:": { - "style": "github", - "image": "1f479.png", - "unicode": "👹", - "name": "Japanese Ogre" - }, - ":partly-sunny:": { - "style": "github", - "image": "26c5.png", - "unicode": "⛅", - "name": "Sun Behind Cloud" - }, - ":flag_ms:": { - "style": "github", - "image": "1f1f2-1f1f8.png", - "unicode": "🇲🇸", - "name": "Montserrat" - }, - "🇷🇪": { - "style": "unicode", - "image": "1f1f7-1f1ea.png", - "name": "Réunion" - }, - "🙏🏿": { - "style": "unicode", - "image": "1f64f-1f3ff.png", - "name": "Person With Folded Hands - Tone 5" - }, - "🙏🏾": { - "style": "unicode", - "image": "1f64f-1f3fe.png", - "name": "Person With Folded Hands - Tone 4" - }, - "🙏🏽": { - "style": "unicode", - "image": "1f64f-1f3fd.png", - "name": "Person With Folded Hands - Tone 3" - }, - "🇷🇴": { - "style": "unicode", - "image": "1f1f7-1f1f4.png", - "name": "Romania" - }, - "🙏🏻": { - "style": "unicode", - "image": "1f64f-1f3fb.png", - "name": "Person With Folded Hands - Tone 1" - }, - ":bi:": { - "style": "github", - "image": "1f1e7-1f1ee.png", - "unicode": "🇧🇮", - "name": "Burundi" - }, - "🇷🇼": { - "style": "unicode", - "image": "1f1f7-1f1fc.png", - "name": "Rwanda" - }, - "🇷🇺": { - "style": "unicode", - "image": "1f1f7-1f1fa.png", - "name": "Russia" - }, - "🇷🇸": { - "style": "unicode", - "image": "1f1f7-1f1f8.png", - "name": "Serbia" - }, - "🍣": { - "style": "unicode", - "image": "1f363.png", - "name": "Sushi" - }, - ":family-wwb:": { - "style": "github", - "image": "1f469-1f469-1f466.png", - "unicode": "👩👩👦", - "name": "Family (woman,woman,boy)" - }, - "🏸": { - "style": "unicode", - "image": "1f3f8.png", - "name": "Badminton Racquet" - }, - ":fork-and-knife:": { - "style": "github", - "image": "1f374.png", - "unicode": "🍴", - "name": "Fork And Knife" - }, - ":basketball_player:": { - "style": "github", - "image": "26f9.png", - "unicode": "⛹", - "name": "Person With Ball" - }, - ":/": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ":flag-ca:": { - "style": "github", - "image": "1f1e8-1f1e6.png", - "unicode": "🇨🇦", - "name": "Canada" - }, - ":man-tone5:": { - "style": "github", - "image": "1f468-1f3ff.png", - "unicode": "👨🏿", - "name": "Man - Tone 5" - }, - ":*": { - "style": "ascii", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":$": { - "style": "ascii", - "ascii": ":$", - "image": "1f633.png", - "unicode": "😳", - "name": "Flushed Face" - }, - ":#": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - "🆑": { - "style": "unicode", - "image": "1f191.png", - "name": "Squared Cl" - }, - ":dancer_tone1:": { - "style": "github", - "image": "1f483-1f3fb.png", - "unicode": "💃🏻", - "name": "Dancer - Tone 1" - }, - ":flag-pt:": { - "style": "github", - "image": "1f1f5-1f1f9.png", - "unicode": "🇵🇹", - "name": "Portugal" - }, - "🚿": { - "style": "unicode", - "image": "1f6bf.png", - "name": "Shower" - }, - ":flag_bv:": { - "style": "github", - "image": "1f1e7-1f1fb.png", - "unicode": "🇧🇻", - "name": "Bouvet Island" - }, - ":o": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":b": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":x": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":p": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":flag_td:": { - "style": "github", - "image": "1f1f9-1f1e9.png", - "unicode": "🇹🇩", - "name": "Chad" - }, - ":L": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ":face_with_thermometer:": { - "style": "github", - "image": "1f912.png", - "unicode": "🤒", - "name": "Face With Thermometer" - }, - ":O": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":cartwheel_tone1:": { - "style": "github", - "image": "1f938-1f3fb.png", - "unicode": "🤸🏻", - "name": "Person Doing Cartwheel - Tone 1" - }, - ":muscle-tone4:": { - "style": "github", - "image": "1f4aa-1f3fe.png", - "unicode": "💪🏾", - "name": "Flexed Biceps - Tone 4" - }, - ":@": { - "style": "ascii", - "ascii": ">:(", - "image": "1f620.png", - "unicode": "😠", - "name": "Angry Face" - }, - ":\\": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ":]": { - "style": "ascii", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - ":X": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":[": { - "style": "ascii", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - ":P": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":maple-leaf:": { - "style": "github", - "image": "1f341.png", - "unicode": "🍁", - "name": "Maple Leaf" - }, - ":tokyo_tower:": { - "style": "github", - "image": "1f5fc.png", - "unicode": "🗼", - "name": "Tokyo Tower" - }, - "🛀": { - "style": "unicode", - "image": "1f6c0.png", - "name": "Bath" - }, - "🐓": { - "style": "unicode", - "image": "1f413.png", - "name": "Rooster" - }, - ":gb:": { - "style": "github", - "image": "1f1ec-1f1e7.png", - "unicode": "🇬🇧", - "name": "Great Britain" - }, - ":sv:": { - "style": "github", - "image": "1f1f8-1f1fb.png", - "unicode": "🇸🇻", - "name": "El Salvador" - }, - "💨": { - "style": "unicode", - "image": "1f4a8.png", - "name": "Dash Symbol" - }, - "🙆🏻": { - "style": "unicode", - "image": "1f646-1f3fb.png", - "name": "Face With Ok Gesture Tone1" - }, - "🙆🏼": { - "style": "unicode", - "image": "1f646-1f3fc.png", - "name": "Face With Ok Gesture Tone2" - }, - "🙆🏽": { - "style": "unicode", - "image": "1f646-1f3fd.png", - "name": "Face With Ok Gesture Tone3" - }, - "🙆🏾": { - "style": "unicode", - "image": "1f646-1f3fe.png", - "name": "Face With Ok Gesture Tone4" - }, - "🙆🏿": { - "style": "unicode", - "image": "1f646-1f3ff.png", - "name": "Face With Ok Gesture Tone5" - }, - "'=)": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - "'=(": { - "style": "ascii", - "ascii": "':(", - "image": "1f613.png", - "unicode": "😓", - "name": "Face With Cold Sweat" - }, - "🌽": { - "style": "unicode", - "image": "1f33d.png", - "name": "Ear Of Maize" - }, - ":first_place_medal:": { - "style": "github", - "image": "1f947.png", - "unicode": "🥇", - "name": "First Place Medal" - }, - ":green-heart:": { - "style": "github", - "image": "1f49a.png", - "unicode": "💚", - "name": "Green Heart" - }, - ":right_fist_tone2:": { - "style": "github", - "image": "1f91c-1f3fc.png", - "unicode": "🤜🏼", - "name": "Right Facing Fist - Tone 2" - }, - ":þ": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - "🏒": { - "style": "unicode", - "image": "1f3d2.png", - "name": "Ice Hockey Stick And Puck" - }, - ":cop_tone3:": { - "style": "github", - "image": "1f46e-1f3fd.png", - "unicode": "👮🏽", - "name": "Police Officer - Tone 3" - }, - ":bride_with_veil_tone4:": { - "style": "github", - "image": "1f470-1f3fe.png", - "unicode": "👰🏾", - "name": "Bride With Veil - Tone 4" - }, - "'=D": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - "👨👨👧👧": { - "style": "unicode", - "image": "1f468-1f468-1f467-1f467.png", - "name": "Family (man,man,girl,girl)" - }, - "👨👨👧👦": { - "style": "unicode", - "image": "1f468-1f468-1f467-1f466.png", - "name": "Family (man,man,girl,boy)" - }, - ":flag-hk:": { - "style": "github", - "image": "1f1ed-1f1f0.png", - "unicode": "🇭🇰", - "name": "Hong Kong" - }, - ":Þ": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - "⏲": { - "style": "unicode", - "image": "23f2.png", - "name": "Timer Clock" - }, - ":man-in-tuxedo-tone3:": { - "style": "github", - "image": "1f935-1f3fd.png", - "unicode": "🤵🏽", - "name": "Man In Tuxedo - Tone 3" - }, - ":mt:": { - "style": "github", - "image": "1f1f2-1f1f9.png", - "unicode": "🇲🇹", - "name": "Malta" - }, - ":cv:": { - "style": "github", - "image": "1f1e8-1f1fb.png", - "unicode": "🇨🇻", - "name": "Cape Verde" - }, - ":handbag:": { - "style": "github", - "image": "1f45c.png", - "unicode": "👜", - "name": "Handbag" - }, - "🚕": { - "style": "unicode", - "image": "1f695.png", - "name": "Taxi" - }, - ":rabbit2:": { - "style": "github", - "image": "1f407.png", - "unicode": "🐇", - "name": "Rabbit" - }, - "😪": { - "style": "unicode", - "image": "1f62a.png", - "name": "Sleepy Face" - }, - ":mailbox-closed:": { - "style": "github", - "image": "1f4ea.png", - "unicode": "📪", - "name": "Closed Mailbox With Lowered Flag" - }, - ":flag-mo:": { - "style": "github", - "image": "1f1f2-1f1f4.png", - "unicode": "🇲🇴", - "name": "Macau" - }, - ":star-of-david:": { - "style": "github", - "image": "2721.png", - "unicode": "✡", - "name": "Star Of David" - }, - "♊": { - "style": "unicode", - "image": "264a.png", - "name": "Gemini" - }, - ":+1_tone2:": { - "style": "github", - "image": "1f44d-1f3fc.png", - "unicode": "👍🏼", - "name": "Thumbs Up Sign - Tone 2" - }, - ":dove:": { - "style": "github", - "image": "1f54a.png", - "unicode": "🕊", - "name": "Dove Of Peace" - }, - "📩": { - "style": "unicode", - "image": "1f4e9.png", - "name": "Envelope With Downwards Arrow Above" - }, - "😼": { - "style": "unicode", - "image": "1f63c.png", - "name": "Cat Face With Wry Smile" - }, - ":flag_fj:": { - "style": "github", - "image": "1f1eb-1f1ef.png", - "unicode": "🇫🇯", - "name": "Fiji" - }, - ":flag_lt:": { - "style": "github", - "image": "1f1f1-1f1f9.png", - "unicode": "🇱🇹", - "name": "Lithuania" - }, - ":levitate:": { - "style": "github", - "image": "1f574.png", - "unicode": "🕴", - "name": "Man In Business Suit Levitating" - }, - ":man-with-gua-pi-mao:": { - "style": "github", - "image": "1f472.png", - "unicode": "👲", - "name": "Man With Gua Pi Mao" - }, - "👾": { - "style": "unicode", - "image": "1f47e.png", - "name": "Alien Monster" - }, - "🔓": { - "style": "unicode", - "image": "1f513.png", - "name": "Open Lock" - }, - ":envelope_with_arrow:": { - "style": "github", - "image": "1f4e9.png", - "unicode": "📩", - "name": "Envelope With Downwards Arrow Above" - }, - ":tiger:": { - "style": "github", - "image": "1f42f.png", - "unicode": "🐯", - "name": "Tiger Face" - }, - ":thumbup_tone2:": { - "style": "github", - "image": "1f44d-1f3fc.png", - "unicode": "👍🏼", - "name": "Thumbs Up Sign - Tone 2" - }, - "🖨": { - "style": "unicode", - "image": "1f5a8.png", - "name": "Printer" - }, - "🏄🏾": { - "style": "unicode", - "image": "1f3c4-1f3fe.png", - "name": "Surfer - Tone 4" - }, - "🏄🏿": { - "style": "unicode", - "image": "1f3c4-1f3ff.png", - "name": "Surfer - Tone 5" - }, - "🏄🏼": { - "style": "unicode", - "image": "1f3c4-1f3fc.png", - "name": "Surfer - Tone 2" - }, - "🏄🏽": { - "style": "unicode", - "image": "1f3c4-1f3fd.png", - "name": "Surfer - Tone 3" - }, - "🏄🏻": { - "style": "unicode", - "image": "1f3c4-1f3fb.png", - "name": "Surfer - Tone 1" - }, - ":construction-worker:": { - "style": "github", - "image": "1f477.png", - "unicode": "👷", - "name": "Construction Worker" - }, - ":pouch:": { - "style": "github", - "image": "1f45d.png", - "unicode": "👝", - "name": "Pouch" - }, - ":person-with-pouting-face-tone3:": { - "style": "github", - "image": "1f64e-1f3fd.png", - "unicode": "🙎🏽", - "name": "Person With Pouting Face Tone3" - }, - "👏🏿": { - "style": "unicode", - "image": "1f44f-1f3ff.png", - "name": "Clapping Hands Sign - Tone 5" - }, - "👏🏾": { - "style": "unicode", - "image": "1f44f-1f3fe.png", - "name": "Clapping Hands Sign - Tone 4" - }, - "👏🏽": { - "style": "unicode", - "image": "1f44f-1f3fd.png", - "name": "Clapping Hands Sign - Tone 3" - }, - "👏🏼": { - "style": "unicode", - "image": "1f44f-1f3fc.png", - "name": "Clapping Hands Sign - Tone 2" - }, - "👏🏻": { - "style": "unicode", - "image": "1f44f-1f3fb.png", - "name": "Clapping Hands Sign - Tone 1" - }, - "✌🏾": { - "style": "unicode", - "image": "270c-1f3fe.png", - "name": "Victory Hand - Tone 4" - }, - "✌🏿": { - "style": "unicode", - "image": "270c-1f3ff.png", - "name": "Victory Hand - Tone 5" - }, - "✌🏼": { - "style": "unicode", - "image": "270c-1f3fc.png", - "name": "Victory Hand - Tone 2" - }, - "✌🏽": { - "style": "unicode", - "image": "270c-1f3fd.png", - "name": "Victory Hand - Tone 3" - }, - "✌🏻": { - "style": "unicode", - "image": "270c-1f3fb.png", - "name": "Victory Hand - Tone 1" - }, - ":left_luggage:": { - "style": "github", - "image": "1f6c5.png", - "unicode": "🛅", - "name": "Left Luggage" - }, - ":dango:": { - "style": "github", - "image": "1f361.png", - "unicode": "🍡", - "name": "Dango" - }, - ":japanese-castle:": { - "style": "github", - "image": "1f3ef.png", - "unicode": "🏯", - "name": "Japanese Castle" - }, - "🍧": { - "style": "unicode", - "image": "1f367.png", - "name": "Shaved Ice" - }, - ":tangerine:": { - "style": "github", - "image": "1f34a.png", - "unicode": "🍊", - "name": "Tangerine" - }, - ":muscle-tone1:": { - "style": "github", - "image": "1f4aa-1f3fb.png", - "unicode": "💪🏻", - "name": "Flexed Biceps - Tone 1" - }, - ":japanese_ogre:": { - "style": "github", - "image": "1f479.png", - "unicode": "👹", - "name": "Japanese Ogre" - }, - ":basketball-player-tone1:": { - "style": "github", - "image": "26f9-1f3fb.png", - "unicode": "⛹🏻", - "name": "Person With Ball - Tone 1" - }, - "🏼": { - "style": "unicode", - "image": "1f3fc.png", - "name": "Emoji Modifier Fitzpatrick Type-3" - }, - ":si:": { - "style": "github", - "image": "1f1f8-1f1ee.png", - "unicode": "🇸🇮", - "name": "Slovenia" - }, - "🦍": { - "style": "unicode", - "image": "1f98d.png", - "name": "Gorilla" - }, - "🎑": { - "style": "unicode", - "image": "1f391.png", - "name": "Moon Viewing Ceremony" - }, - "🖕": { - "style": "unicode", - "image": "1f595.png", - "name": "Reversed Hand With Middle Finger Extended" - }, - ":raised_back_of_hand_tone3:": { - "style": "github", - "image": "1f91a-1f3fd.png", - "unicode": "🤚🏽", - "name": "Raised Back Of Hand - Tone 3" - }, - ":calendar:": { - "style": "github", - "image": "1f4c6.png", - "unicode": "📆", - "name": "Tear-off Calendar" - }, - "🤢": { - "style": "unicode", - "image": "1f922.png", - "name": "Nauseated Face" - }, - ":girl-tone2:": { - "style": "github", - "image": "1f467-1f3fc.png", - "unicode": "👧🏼", - "name": "Girl - Tone 2" - }, - "🌦": { - "style": "unicode", - "image": "1f326.png", - "name": "White Sun Behind Cloud With Rain" - }, - "🔪": { - "style": "unicode", - "image": "1f52a.png", - "name": "Hocho" - }, - "🤜🏿": { - "style": "unicode", - "image": "1f91c-1f3ff.png", - "name": "Right Facing Fist - Tone 5" - }, - ":bicyclist-tone5:": { - "style": "github", - "image": "1f6b4-1f3ff.png", - "unicode": "🚴🏿", - "name": "Bicyclist - Tone 5" - }, - ":blossom:": { - "style": "github", - "image": "1f33c.png", - "unicode": "🌼", - "name": "Blossom" - }, - "💿": { - "style": "unicode", - "image": "1f4bf.png", - "name": "Optical Disc" - }, - ":regional_indicator_f:": { - "style": "github", - "image": "1f1eb.png", - "unicode": "🇫", - "name": "Regional Indicator Symbol Letter F" - }, - ":va:": { - "style": "github", - "image": "1f1fb-1f1e6.png", - "unicode": "🇻🇦", - "name": "The Vatican City" - }, - "🉐": { - "style": "unicode", - "image": "1f250.png", - "name": "Circled Ideograph Advantage" - }, - "👔": { - "style": "unicode", - "image": "1f454.png", - "name": "Necktie" - }, - ":flag-tg:": { - "style": "github", - "image": "1f1f9-1f1ec.png", - "unicode": "🇹🇬", - "name": "Togo" - }, - ":speech_balloon:": { - "style": "github", - "image": "1f4ac.png", - "unicode": "💬", - "name": "Speech Balloon" - }, - ":drum_with_drumsticks:": { - "style": "github", - "image": "1f941.png", - "unicode": "🥁", - "name": "Drum With Drumsticks" - }, - ":flag_vi:": { - "style": "github", - "image": "1f1fb-1f1ee.png", - "unicode": "🇻🇮", - "name": "U.s. Virgin Islands" - }, - ":children-crossing:": { - "style": "github", - "image": "1f6b8.png", - "unicode": "🚸", - "name": "Children Crossing" - }, - "✉": { - "style": "unicode", - "image": "2709.png", - "name": "Envelope" - }, - ":night-with-stars:": { - "style": "github", - "image": "1f303.png", - "unicode": "🌃", - "name": "Night With Stars" - }, - "😓": { - "style": "unicode", - "ascii": "':(", - "image": "1f613.png", - "name": "Face With Cold Sweat" - }, - ":a:": { - "style": "github", - "image": "1f170.png", - "unicode": "🅰", - "name": "Negative Squared Latin Capital Letter A" - }, - ":shaking_hands_tone3:": { - "style": "github", - "image": "1f91d-1f3fd.png", - "unicode": "🤝🏽", - "name": "Handshake - Tone 3" - }, - ":flag_dg:": { - "style": "github", - "image": "1f1e9-1f1ec.png", - "unicode": "🇩🇬", - "name": "Diego Garcia" - }, - ":nr:": { - "style": "github", - "image": "1f1f3-1f1f7.png", - "unicode": "🇳🇷", - "name": "Nauru" - }, - "🚨": { - "style": "unicode", - "image": "1f6a8.png", - "name": "Police Cars Revolving Light" - }, - ":mag_right:": { - "style": "github", - "image": "1f50e.png", - "unicode": "🔎", - "name": "Right-pointing Magnifying Glass" - }, - ":muscle_tone4:": { - "style": "github", - "image": "1f4aa-1f3fe.png", - "unicode": "💪🏾", - "name": "Flexed Biceps - Tone 4" - }, - ":flag_ps:": { - "style": "github", - "image": "1f1f5-1f1f8.png", - "unicode": "🇵🇸", - "name": "Palestinian Authority" - }, - ":frame-photo:": { - "style": "github", - "image": "1f5bc.png", - "unicode": "🖼", - "name": "Frame With Picture" - }, - ":man_with_turban_tone4:": { - "style": "github", - "image": "1f473-1f3fe.png", - "unicode": "👳🏾", - "name": "Man With Turban - Tone 4" - }, - ":rugby-football:": { - "style": "github", - "image": "1f3c9.png", - "unicode": "🏉", - "name": "Rugby Football" - }, - ":water_polo_tone1:": { - "style": "github", - "image": "1f93d-1f3fb.png", - "unicode": "🤽🏻", - "name": "Water Polo - Tone 1" - }, - ":trophy:": { - "style": "github", - "image": "1f3c6.png", - "unicode": "🏆", - "name": "Trophy" - }, - ":foggy:": { - "style": "github", - "image": "1f301.png", - "unicode": "🌁", - "name": "Foggy" - }, - ":notes:": { - "style": "github", - "image": "1f3b6.png", - "unicode": "🎶", - "name": "Multiple Musical Notes" - }, - "💕": { - "style": "unicode", - "image": "1f495.png", - "name": "Two Hearts" - }, - ":fencer:": { - "style": "github", - "image": "1f93a.png", - "unicode": "🤺", - "name": "Fencer" - }, - ":fox_face:": { - "style": "github", - "image": "1f98a.png", - "unicode": "🦊", - "name": "Fox Face" - }, - ":flag_zm:": { - "style": "github", - "image": "1f1ff-1f1f2.png", - "unicode": "🇿🇲", - "name": "Zambia" - }, - ":aw:": { - "style": "github", - "image": "1f1e6-1f1fc.png", - "unicode": "🇦🇼", - "name": "Aruba" - }, - ":electric_plug:": { - "style": "github", - "image": "1f50c.png", - "unicode": "🔌", - "name": "Electric Plug" - }, - "🎻": { - "style": "unicode", - "image": "1f3bb.png", - "name": "Violin" - }, - ":family_wwgg:": { - "style": "github", - "image": "1f469-1f469-1f467-1f467.png", - "unicode": "👩👩👧👧", - "name": "Family (woman,woman,girl,girl)" - }, - ":man_with_gua_pi_mao_tone3:": { - "style": "github", - "image": "1f472-1f3fd.png", - "unicode": "👲🏽", - "name": "Man With Gua Pi Mao - Tone 3" - }, - "🍐": { - "style": "unicode", - "image": "1f350.png", - "name": "Pear" - }, - ":flag_bm:": { - "style": "github", - "image": "1f1e7-1f1f2.png", - "unicode": "🇧🇲", - "name": "Bermuda" - }, - ":stop_button:": { - "style": "github", - "image": "23f9.png", - "unicode": "⏹", - "name": "Black Square For Stop" - }, - "🕔": { - "style": "unicode", - "image": "1f554.png", - "name": "Clock Face Five Oclock" - }, - ":spaghetti:": { - "style": "github", - "image": "1f35d.png", - "unicode": "🍝", - "name": "Spaghetti" - }, - ":flag-al:": { - "style": "github", - "image": "1f1e6-1f1f1.png", - "unicode": "🇦🇱", - "name": "Albania" - }, - "🛩": { - "style": "unicode", - "image": "1f6e9.png", - "name": "Small Airplane" - }, - ":crossed-flags:": { - "style": "github", - "image": "1f38c.png", - "unicode": "🎌", - "name": "Crossed Flags" - }, - ":runner_tone2:": { - "style": "github", - "image": "1f3c3-1f3fc.png", - "unicode": "🏃🏼", - "name": "Runner - Tone 2" - }, - ":regional-indicator-k:": { - "style": "github", - "image": "1f1f0.png", - "unicode": "🇰", - "name": "Regional Indicator Symbol Letter K" - }, - ":do_not_litter:": { - "style": "github", - "image": "1f6af.png", - "unicode": "🚯", - "name": "Do Not Litter Symbol" - }, - ":bride-with-veil-tone2:": { - "style": "github", - "image": "1f470-1f3fc.png", - "unicode": "👰🏼", - "name": "Bride With Veil - Tone 2" - }, - ":sign_of_the_horns_tone2:": { - "style": "github", - "image": "1f918-1f3fc.png", - "unicode": "🤘🏼", - "name": "Sign Of The Horns - Tone 2" - }, - ":camera:": { - "style": "github", - "image": "1f4f7.png", - "unicode": "📷", - "name": "Camera" - }, - "👊🏼": { - "style": "unicode", - "image": "1f44a-1f3fc.png", - "name": "Fisted Hand Sign - Tone 2" - }, - "👊🏽": { - "style": "unicode", - "image": "1f44a-1f3fd.png", - "name": "Fisted Hand Sign - Tone 3" - }, - "👊🏾": { - "style": "unicode", - "image": "1f44a-1f3fe.png", - "name": "Fisted Hand Sign - Tone 4" - }, - "👊🏿": { - "style": "unicode", - "image": "1f44a-1f3ff.png", - "name": "Fisted Hand Sign - Tone 5" - }, - "👊🏻": { - "style": "unicode", - "image": "1f44a-1f3fb.png", - "name": "Fisted Hand Sign - Tone 1" - }, - "🤜🏾": { - "style": "unicode", - "image": "1f91c-1f3fe.png", - "name": "Right Facing Fist - Tone 4" - }, - ":prince-tone5:": { - "style": "github", - "image": "1f934-1f3ff.png", - "unicode": "🤴🏿", - "name": "Prince - Tone 5" - }, - "🤜🏼": { - "style": "unicode", - "image": "1f91c-1f3fc.png", - "name": "Right Facing Fist - Tone 2" - }, - "🤜🏽": { - "style": "unicode", - "image": "1f91c-1f3fd.png", - "name": "Right Facing Fist - Tone 3" - }, - "🤜🏻": { - "style": "unicode", - "image": "1f91c-1f3fb.png", - "name": "Right Facing Fist - Tone 1" - }, - "✳": { - "style": "unicode", - "image": "2733.png", - "name": "Eight Spoked Asterisk" - }, - ":princess-tone4:": { - "style": "github", - "image": "1f478-1f3fe.png", - "unicode": "👸🏾", - "name": "Princess - Tone 4" - }, - ":dancer-tone5:": { - "style": "github", - "image": "1f483-1f3ff.png", - "unicode": "💃🏿", - "name": "Dancer - Tone 5" - }, - ":ok-hand-tone1:": { - "style": "github", - "image": "1f44c-1f3fb.png", - "unicode": "👌🏻", - "name": "Ok Hand Sign - Tone 1" - }, - ":fo:": { - "style": "github", - "image": "1f1eb-1f1f4.png", - "unicode": "🇫🇴", - "name": "Faroe Islands" - }, - ":cartwheel-tone5:": { - "style": "github", - "image": "1f938-1f3ff.png", - "unicode": "🤸🏿", - "name": "Person Doing Cartwheel - Tone 5" - }, - ":pn:": { - "style": "github", - "image": "1f1f5-1f1f3.png", - "unicode": "🇵🇳", - "name": "Pitcairn" - }, - ":flag_sr:": { - "style": "github", - "image": "1f1f8-1f1f7.png", - "unicode": "🇸🇷", - "name": "Suriname" - }, - ":weight_lifter:": { - "style": "github", - "image": "1f3cb.png", - "unicode": "🏋", - "name": "Weight Lifter" - }, - ":neutral_face:": { - "style": "github", - "image": "1f610.png", - "unicode": "😐", - "name": "Neutral Face" - }, - "🇼": { - "style": "unicode", - "image": "1f1fc.png", - "name": "Regional Indicator Symbol Letter W" - }, - ":rose:": { - "style": "github", - "image": "1f339.png", - "unicode": "🌹", - "name": "Rose" - }, - ":mk:": { - "style": "github", - "image": "1f1f2-1f1f0.png", - "unicode": "🇲🇰", - "name": "Macedonia" - }, - ":clock3:": { - "style": "github", - "image": "1f552.png", - "unicode": "🕒", - "name": "Clock Face Three Oclock" - }, - "🤞🏻": { - "style": "unicode", - "image": "1f91e-1f3fb.png", - "name": "Hand With Index And Middle Fingers Crossed - Tone 1" - }, - "🤞🏼": { - "style": "unicode", - "image": "1f91e-1f3fc.png", - "name": "Hand With Index And Middle Fingers Crossed - Tone 2" - }, - "🤞🏽": { - "style": "unicode", - "image": "1f91e-1f3fd.png", - "name": "Hand With Index And Middle Fingers Crossed - Tone 3" - }, - "🤞🏾": { - "style": "unicode", - "image": "1f91e-1f3fe.png", - "name": "Hand With Index And Middle Fingers Crossed - Tone 4" - }, - "🤞🏿": { - "style": "unicode", - "image": "1f91e-1f3ff.png", - "name": "Hand With Index And Middle Fingers Crossed - Tone 5" - }, - "🌪": { - "style": "unicode", - "image": "1f32a.png", - "name": "Cloud With Tornado" - }, - ":disappointed_relieved:": { - "style": "github", - "image": "1f625.png", - "unicode": "😥", - "name": "Disappointed But Relieved Face" - }, - "💻": { - "style": "unicode", - "image": "1f4bb.png", - "name": "Personal Computer" - }, - "🇹🇩": { - "style": "unicode", - "image": "1f1f9-1f1e9.png", - "name": "Chad" - }, - "🇹🇨": { - "style": "unicode", - "image": "1f1f9-1f1e8.png", - "name": "Turks And Caicos Islands" - }, - "🇹🇫": { - "style": "unicode", - "image": "1f1f9-1f1eb.png", - "name": "French Southern Territories" - }, - "🇹🇭": { - "style": "unicode", - "image": "1f1f9-1f1ed.png", - "name": "Thailand" - }, - "🇹🇬": { - "style": "unicode", - "image": "1f1f9-1f1ec.png", - "name": "Togo" - }, - "🇹🇯": { - "style": "unicode", - "image": "1f1f9-1f1ef.png", - "name": "Tajikistan" - }, - ":flag_mu:": { - "style": "github", - "image": "1f1f2-1f1fa.png", - "unicode": "🇲🇺", - "name": "Mauritius" - }, - "🇹🇦": { - "style": "unicode", - "image": "1f1f9-1f1e6.png", - "name": "Tristan Da Cunha" - }, - "🇹🇹": { - "style": "unicode", - "image": "1f1f9-1f1f9.png", - "name": "Trinidad And Tobago" - }, - "👐": { - "style": "unicode", - "image": "1f450.png", - "name": "Open Hands Sign" - }, - "🇹🇻": { - "style": "unicode", - "image": "1f1f9-1f1fb.png", - "name": "Tuvalu" - }, - "🇹🇼": { - "style": "unicode", - "image": "1f1f9-1f1fc.png", - "name": "The Republic Of China" - }, - "🇹🇿": { - "style": "unicode", - "image": "1f1f9-1f1ff.png", - "name": "Tanzania" - }, - "🇹🇱": { - "style": "unicode", - "image": "1f1f9-1f1f1.png", - "name": "Timor-leste" - }, - "🇹🇰": { - "style": "unicode", - "image": "1f1f9-1f1f0.png", - "name": "Tokelau" - }, - "🇹🇳": { - "style": "unicode", - "image": "1f1f9-1f1f3.png", - "name": "Tunisia" - }, - ":flag_cr:": { - "style": "github", - "image": "1f1e8-1f1f7.png", - "unicode": "🇨🇷", - "name": "Costa Rica" - }, - "🇹🇴": { - "style": "unicode", - "image": "1f1f9-1f1f4.png", - "name": "Tonga" - }, - "🇹🇷": { - "style": "unicode", - "image": "1f1f9-1f1f7.png", - "name": "Turkey" - }, - "🇩": { - "style": "unicode", - "image": "1f1e9.png", - "name": "Regional Indicator Symbol Letter D" - }, - ":stuffed-flatbread:": { - "style": "github", - "image": "1f959.png", - "unicode": "🥙", - "name": "Stuffed Flatbread" - }, - ":family_mmgg:": { - "style": "github", - "image": "1f468-1f468-1f467-1f467.png", - "unicode": "👨👨👧👧", - "name": "Family (man,man,girl,girl)" - }, - ":woman-tone4:": { - "style": "github", - "image": "1f469-1f3fe.png", - "unicode": "👩🏾", - "name": "Woman - Tone 4" - }, - "🅾": { - "style": "unicode", - "image": "1f17e.png", - "name": "Negative Squared Latin Capital Letter O" - }, - ":waning-crescent-moon:": { - "style": "github", - "image": "1f318.png", - "unicode": "🌘", - "name": "Waning Crescent Moon Symbol" - }, - "⬅": { - "style": "unicode", - "image": "2b05.png", - "name": "Leftwards Black Arrow" - }, - ":guardsman:": { - "style": "github", - "image": "1f482.png", - "unicode": "💂", - "name": "Guardsman" - }, - ":flag-cc:": { - "style": "github", - "image": "1f1e8-1f1e8.png", - "unicode": "🇨🇨", - "name": "Cocos (keeling) Islands" - }, - ":clap-tone3:": { - "style": "github", - "image": "1f44f-1f3fd.png", - "unicode": "👏🏽", - "name": "Clapping Hands Sign - Tone 3" - }, - ":rotating-light:": { - "style": "github", - "image": "1f6a8.png", - "unicode": "🚨", - "name": "Police Cars Revolving Light" - }, - ":heavy_plus_sign:": { - "style": "github", - "image": "2795.png", - "unicode": "➕", - "name": "Heavy Plus Sign" - }, - ":desktop_computer:": { - "style": "github", - "image": "1f5a5.png", - "unicode": "🖥", - "name": "Desktop Computer" - }, - "🤹": { - "style": "unicode", - "image": "1f939.png", - "name": "Juggling" - }, - ":flag_tz:": { - "style": "github", - "image": "1f1f9-1f1ff.png", - "unicode": "🇹🇿", - "name": "Tanzania" - }, - ":muscle:": { - "style": "github", - "image": "1f4aa.png", - "unicode": "💪", - "name": "Flexed Biceps" - }, - "🚴🏾": { - "style": "unicode", - "image": "1f6b4-1f3fe.png", - "name": "Bicyclist - Tone 4" - }, - "🚴🏿": { - "style": "unicode", - "image": "1f6b4-1f3ff.png", - "name": "Bicyclist - Tone 5" - }, - "🚴🏼": { - "style": "unicode", - "image": "1f6b4-1f3fc.png", - "name": "Bicyclist - Tone 2" - }, - "🚴🏽": { - "style": "unicode", - "image": "1f6b4-1f3fd.png", - "name": "Bicyclist - Tone 3" - }, - "🚴🏻": { - "style": "unicode", - "image": "1f6b4-1f3fb.png", - "name": "Bicyclist - Tone 1" - }, - ":vibration_mode:": { - "style": "github", - "image": "1f4f3.png", - "unicode": "📳", - "name": "Vibration Mode" - }, - ":runner:": { - "style": "github", - "image": "1f3c3.png", - "unicode": "🏃", - "name": "Runner" - }, - ":shaking_hands:": { - "style": "github", - "image": "1f91d.png", - "unicode": "🤝", - "name": "Handshake" - }, - ":cartwheel_tone3:": { - "style": "github", - "image": "1f938-1f3fd.png", - "unicode": "🤸🏽", - "name": "Person Doing Cartwheel - Tone 3" - }, - ":flag_uy:": { - "style": "github", - "image": "1f1fa-1f1fe.png", - "unicode": "🇺🇾", - "name": "Uruguay" - }, - "💑": { - "style": "unicode", - "image": "1f491.png", - "name": "Couple With Heart" - }, - ":eye-in-speech-bubble:": { - "style": "github", - "image": "1f441-1f5e8.png", - "unicode": "👁🗨", - "name": "Eye In Speech Bubble" - }, - ":ok_hand_tone2:": { - "style": "github", - "image": "1f44c-1f3fc.png", - "unicode": "👌🏼", - "name": "Ok Hand Sign - Tone 2" - }, - ":st:": { - "style": "github", - "image": "1f1f8-1f1f9.png", - "unicode": "🇸🇹", - "name": "São Tomé And Príncipe" - }, - "🤷🏿": { - "style": "unicode", - "image": "1f937-1f3ff.png", - "name": "Shrug - Tone 5" - }, - "🤷🏾": { - "style": "unicode", - "image": "1f937-1f3fe.png", - "name": "Shrug - Tone 4" - }, - "🤷🏽": { - "style": "unicode", - "image": "1f937-1f3fd.png", - "name": "Shrug - Tone 3" - }, - "🤷🏼": { - "style": "unicode", - "image": "1f937-1f3fc.png", - "name": "Shrug - Tone 2" - }, - "🤷🏻": { - "style": "unicode", - "image": "1f937-1f3fb.png", - "name": "Shrug - Tone 1" - }, - "🐦": { - "style": "unicode", - "image": "1f426.png", - "name": "Bird" - }, - ":fried_shrimp:": { - "style": "github", - "image": "1f364.png", - "unicode": "🍤", - "name": "Fried Shrimp" - }, - "🎿": { - "style": "unicode", - "image": "1f3bf.png", - "name": "Ski And Ski Boot" - }, - ":raised-hand-tone3:": { - "style": "github", - "image": "270b-1f3fd.png", - "unicode": "✋🏽", - "name": "Raised Hand - Tone 3" - }, - "🕐": { - "style": "unicode", - "image": "1f550.png", - "name": "Clock Face One Oclock" - }, - "🍔": { - "style": "unicode", - "image": "1f354.png", - "name": "Hamburger" - }, - "🙍🏽": { - "style": "unicode", - "image": "1f64d-1f3fd.png", - "name": "Person Frowning - Tone 3" - }, - "🙍🏼": { - "style": "unicode", - "image": "1f64d-1f3fc.png", - "name": "Person Frowning - Tone 2" - }, - "🙍🏿": { - "style": "unicode", - "image": "1f64d-1f3ff.png", - "name": "Person Frowning - Tone 5" - }, - "🙍🏾": { - "style": "unicode", - "image": "1f64d-1f3fe.png", - "name": "Person Frowning - Tone 4" - }, - ":cop_tone5:": { - "style": "github", - "image": "1f46e-1f3ff.png", - "unicode": "👮🏿", - "name": "Police Officer - Tone 5" - }, - "🙍🏻": { - "style": "unicode", - "image": "1f64d-1f3fb.png", - "name": "Person Frowning - Tone 1" - }, - ":u6709:": { - "style": "github", - "image": "1f236.png", - "unicode": "🈶", - "name": "Squared Cjk Unified Ideograph-6709" - }, - ":girl-tone4:": { - "style": "github", - "image": "1f467-1f3fe.png", - "unicode": "👧🏾", - "name": "Girl - Tone 4" - }, - ":selfie_tone3:": { - "style": "github", - "image": "1f933-1f3fd.png", - "unicode": "🤳🏽", - "name": "Selfie - Tone 3" - }, - ":mv:": { - "style": "github", - "image": "1f1f2-1f1fb.png", - "unicode": "🇲🇻", - "name": "Maldives" - }, - "👨👨👧": { - "style": "unicode", - "image": "1f468-1f468-1f467.png", - "name": "Family (man,man,girl)" - }, - "👨👨👦": { - "style": "unicode", - "image": "1f468-1f468-1f466.png", - "name": "Family (man,man,boy)" - }, - ":card_index_dividers:": { - "style": "github", - "image": "1f5c2.png", - "unicode": "🗂", - "name": "Card Index Dividers" - }, - ":man-in-tuxedo-tone1:": { - "style": "github", - "image": "1f935-1f3fb.png", - "unicode": "🤵🏻", - "name": "Man In Tuxedo - Tone 1" - }, - ":control_knobs:": { - "style": "github", - "image": "1f39b.png", - "unicode": "🎛", - "name": "Control Knobs" - }, - ":bird:": { - "style": "github", - "image": "1f426.png", - "unicode": "🐦", - "name": "Bird" - }, - ":flag_dz:": { - "style": "github", - "image": "1f1e9-1f1ff.png", - "unicode": "🇩🇿", - "name": "Algeria" - }, - ":black-circle:": { - "style": "github", - "image": "26ab.png", - "unicode": "⚫", - "name": "Medium Black Circle" - }, - ":slightly_frowning_face:": { - "style": "github", - "image": "1f641.png", - "unicode": "🙁", - "name": "Slightly Frowning Face" - }, - ":flag-mm:": { - "style": "github", - "image": "1f1f2-1f1f2.png", - "unicode": "🇲🇲", - "name": "Myanmar" - }, - "😽": { - "style": "unicode", - "image": "1f63d.png", - "name": "Kissing Cat Face With Closed Eyes" - }, - ":stop-button:": { - "style": "github", - "image": "23f9.png", - "unicode": "⏹", - "name": "Black Square For Stop" - }, - "🛒": { - "style": "unicode", - "image": "1f6d2.png", - "name": "Shopping Trolley" - }, - ":curly-loop:": { - "style": "github", - "image": "27b0.png", - "unicode": "➰", - "name": "Curly Loop" - }, - ":lock-with-ink-pen:": { - "style": "github", - "image": "1f50f.png", - "unicode": "🔏", - "name": "Lock With Ink Pen" - }, - "⛲": { - "style": "unicode", - "image": "26f2.png", - "name": "Fountain" - }, - ":cloud_tornado:": { - "style": "github", - "image": "1f32a.png", - "unicode": "🌪", - "name": "Cloud With Tornado" - }, - "🇸🇪": { - "style": "unicode", - "image": "1f1f8-1f1ea.png", - "name": "Sweden" - }, - "🇸🇨": { - "style": "unicode", - "image": "1f1f8-1f1e8.png", - "name": "The Seychelles" - }, - "🇸🇩": { - "style": "unicode", - "image": "1f1f8-1f1e9.png", - "name": "Sudan" - }, - "🇸🇮": { - "style": "unicode", - "image": "1f1f8-1f1ee.png", - "name": "Slovenia" - }, - "🇸🇯": { - "style": "unicode", - "image": "1f1f8-1f1ef.png", - "name": "Svalbard And Jan Mayen" - }, - "🇸🇬": { - "style": "unicode", - "image": "1f1f8-1f1ec.png", - "name": "Singapore" - }, - "🇸🇭": { - "style": "unicode", - "image": "1f1f8-1f1ed.png", - "name": "Saint Helena" - }, - "🇸🇦": { - "style": "unicode", - "image": "1f1f8-1f1e6.png", - "name": "Saudi Arabia" - }, - "🇸🇧": { - "style": "unicode", - "image": "1f1f8-1f1e7.png", - "name": "The Solomon Islands" - }, - ":ro:": { - "style": "github", - "image": "1f1f7-1f1f4.png", - "unicode": "🇷🇴", - "name": "Romania" - }, - "🇸🇻": { - "style": "unicode", - "image": "1f1f8-1f1fb.png", - "name": "El Salvador" - }, - "🇸🇸": { - "style": "unicode", - "image": "1f1f8-1f1f8.png", - "name": "South Sudan" - }, - "🇸🇹": { - "style": "unicode", - "image": "1f1f8-1f1f9.png", - "name": "São Tomé And Príncipe" - }, - "🆕": { - "style": "unicode", - "image": "1f195.png", - "name": "Squared New" - }, - "🇸🇿": { - "style": "unicode", - "image": "1f1f8-1f1ff.png", - "name": "Swaziland" - }, - "🇸🇽": { - "style": "unicode", - "image": "1f1f8-1f1fd.png", - "name": "Sint Maarten" - }, - "🇸🇲": { - "style": "unicode", - "image": "1f1f8-1f1f2.png", - "name": "San Marino" - }, - "🇸🇳": { - "style": "unicode", - "image": "1f1f8-1f1f3.png", - "name": "Senegal" - }, - "🇸🇰": { - "style": "unicode", - "image": "1f1f8-1f1f0.png", - "name": "Slovakia" - }, - "🇸🇱": { - "style": "unicode", - "image": "1f1f8-1f1f1.png", - "name": "Sierra Leone" - }, - "🇸🇷": { - "style": "unicode", - "image": "1f1f8-1f1f7.png", - "name": "Suriname" - }, - "🇸🇴": { - "style": "unicode", - "image": "1f1f8-1f1f4.png", - "name": "Somalia" - }, - ":lollipop:": { - "style": "github", - "image": "1f36d.png", - "unicode": "🍭", - "name": "Lollipop" - }, - ":snail:": { - "style": "github", - "image": "1f40c.png", - "unicode": "🐌", - "name": "Snail" - }, - ":flag-ly:": { - "style": "github", - "image": "1f1f1-1f1fe.png", - "unicode": "🇱🇾", - "name": "Libya" - }, - ":station:": { - "style": "github", - "image": "1f689.png", - "unicode": "🚉", - "name": "Station" - }, - ":repeat-one:": { - "style": "github", - "image": "1f502.png", - "unicode": "🔂", - "name": "Clockwise Rightwards And Leftwards Open Circle Arrows With Circled One Overlay" - }, - ":unicorn_face:": { - "style": "github", - "image": "1f984.png", - "unicode": "🦄", - "name": "Unicorn Face" - }, - ":new:": { - "style": "github", - "image": "1f195.png", - "unicode": "🆕", - "name": "Squared New" - }, - "🚻": { - "style": "unicode", - "image": "1f6bb.png", - "name": "Restroom" - }, - ":flag_nl:": { - "style": "github", - "image": "1f1f3-1f1f1.png", - "unicode": "🇳🇱", - "name": "The Netherlands" - }, - ":basketball-player-tone3:": { - "style": "github", - "image": "26f9-1f3fd.png", - "unicode": "⛹🏽", - "name": "Person With Ball - Tone 3" - }, - ":reversed_hand_with_middle_finger_extended:": { - "style": "github", - "image": "1f595.png", - "unicode": "🖕", - "name": "Reversed Hand With Middle Finger Extended" - }, - ":back_of_hand_tone4:": { - "style": "github", - "image": "1f91a-1f3fe.png", - "unicode": "🤚🏾", - "name": "Raised Back Of Hand - Tone 4" - }, - ":hand_splayed_tone4:": { - "style": "github", - "image": "1f590-1f3fe.png", - "unicode": "🖐🏾", - "name": "Raised Hand With Fingers Splayed - Tone 4" - }, - ":liar:": { - "style": "github", - "image": "1f925.png", - "unicode": "🤥", - "name": "Lying Face" - }, - ":man_tone4:": { - "style": "github", - "image": "1f468-1f3fe.png", - "unicode": "👨🏾", - "name": "Man - Tone 4" - }, - "👉🏿": { - "style": "unicode", - "image": "1f449-1f3ff.png", - "name": "White Right Pointing Backhand Index - Tone 5" - }, - "🏩": { - "style": "unicode", - "image": "1f3e9.png", - "name": "Love Hotel" - }, - "👉🏾": { - "style": "unicode", - "image": "1f449-1f3fe.png", - "name": "White Right Pointing Backhand Index - Tone 4" - }, - ":person-with-blond-hair-tone4:": { - "style": "github", - "image": "1f471-1f3fe.png", - "unicode": "👱🏾", - "name": "Person With Blond Hair - Tone 4" - }, - "🍾": { - "style": "unicode", - "image": "1f37e.png", - "name": "Bottle With Popping Cork" - }, - ":first-quarter-moon:": { - "style": "github", - "image": "1f313.png", - "unicode": "🌓", - "name": "First Quarter Moon Symbol" - }, - ":electric-plug:": { - "style": "github", - "image": "1f50c.png", - "unicode": "🔌", - "name": "Electric Plug" - }, - ":bicyclist-tone3:": { - "style": "github", - "image": "1f6b4-1f3fd.png", - "unicode": "🚴🏽", - "name": "Bicyclist - Tone 3" - }, - ":person_with_ball_tone1:": { - "style": "github", - "image": "26f9-1f3fb.png", - "unicode": "⛹🏻", - "name": "Person With Ball - Tone 1" - }, - ":name-badge:": { - "style": "github", - "image": "1f4db.png", - "unicode": "📛", - "name": "Name Badge" - }, - ":raised_hand_with_fingers_splayed_tone1:": { - "style": "github", - "image": "1f590-1f3fb.png", - "unicode": "🖐🏻", - "name": "Raised Hand With Fingers Splayed - Tone 1" - }, - ";]": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ":point_up_2_tone5:": { - "style": "github", - "image": "1f446-1f3ff.png", - "unicode": "👆🏿", - "name": "White Up Pointing Backhand Index - Tone 5" - }, - ";D": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - "🔽": { - "style": "unicode", - "image": "1f53d.png", - "name": "Down-pointing Small Red Triangle" - }, - ":nose-tone3:": { - "style": "github", - "image": "1f443-1f3fd.png", - "unicode": "👃🏽", - "name": "Nose - Tone 3" - }, - ":small_airplane:": { - "style": "github", - "image": "1f6e9.png", - "unicode": "🛩", - "name": "Small Airplane" - }, - "🗒": { - "style": "unicode", - "image": "1f5d2.png", - "name": "Spiral Note Pad" - }, - "🙋🏻": { - "style": "unicode", - "image": "1f64b-1f3fb.png", - "name": "Happy Person Raising One Hand Tone1" - }, - ";)": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ":rage:": { - "style": "github", - "image": "1f621.png", - "unicode": "😡", - "name": "Pouting Face" - }, - "🙋🏿": { - "style": "unicode", - "image": "1f64b-1f3ff.png", - "name": "Happy Person Raising One Hand Tone5" - }, - "🙋🏾": { - "style": "unicode", - "image": "1f64b-1f3fe.png", - "name": "Happy Person Raising One Hand Tone4" - }, - ":regional_indicator_d:": { - "style": "github", - "image": "1f1e9.png", - "unicode": "🇩", - "name": "Regional Indicator Symbol Letter D" - }, - "🙋🏼": { - "style": "unicode", - "image": "1f64b-1f3fc.png", - "name": "Happy Person Raising One Hand Tone2" - }, - ":rice-scene:": { - "style": "github", - "image": "1f391.png", - "unicode": "🎑", - "name": "Moon Viewing Ceremony" - }, - "👧": { - "style": "unicode", - "image": "1f467.png", - "name": "Girl" - }, - ":ideograph-advantage:": { - "style": "github", - "image": "1f250.png", - "unicode": "🉐", - "name": "Circled Ideograph Advantage" - }, - ":heartpulse:": { - "style": "github", - "image": "1f497.png", - "unicode": "💗", - "name": "Growing Heart" - }, - ":wastebasket:": { - "style": "github", - "image": "1f5d1.png", - "unicode": "🗑", - "name": "Wastebasket" - }, - ":raised_hand_tone4:": { - "style": "github", - "image": "270b-1f3fe.png", - "unicode": "✋🏾", - "name": "Raised Hand - Tone 4" - }, - "📼": { - "style": "unicode", - "image": "1f4fc.png", - "name": "Videocassette" - }, - ":person_with_pouting_face_tone4:": { - "style": "github", - "image": "1f64e-1f3fe.png", - "unicode": "🙎🏾", - "name": "Person With Pouting Face Tone4" - }, - "🚑": { - "style": "unicode", - "image": "1f691.png", - "name": "Ambulance" - }, - ":surfer_tone4:": { - "style": "github", - "image": "1f3c4-1f3fe.png", - "unicode": "🏄🏾", - "name": "Surfer - Tone 4" - }, - ":shaking_hands_tone1:": { - "style": "github", - "image": "1f91d-1f3fb.png", - "unicode": "🤝🏻", - "name": "Handshake - Tone 1" - }, - ":white_medium_small_square:": { - "style": "github", - "image": "25fd.png", - "unicode": "◽", - "name": "White Medium Small Square" - }, - ":cherry_blossom:": { - "style": "github", - "image": "1f338.png", - "unicode": "🌸", - "name": "Cherry Blossom" - }, - ":flag_au:": { - "style": "github", - "image": "1f1e6-1f1fa.png", - "unicode": "🇦🇺", - "name": "Australia" - }, - ":mrs-claus:": { - "style": "github", - "image": "1f936.png", - "unicode": "🤶", - "name": "Mother Christmas" - }, - ":prince_tone1:": { - "style": "github", - "image": "1f934-1f3fb.png", - "unicode": "🤴🏻", - "name": "Prince - Tone 1" - }, - "😦": { - "style": "unicode", - "image": "1f626.png", - "name": "Frowning Face With Open Mouth" - }, - ":flag_de:": { - "style": "github", - "image": "1f1e9-1f1ea.png", - "unicode": "🇩🇪", - "name": "Germany" - }, - "⚱": { - "style": "unicode", - "image": "26b1.png", - "name": "Funeral Urn" - }, - ":aries:": { - "style": "github", - "image": "2648.png", - "unicode": "♈", - "name": "Aries" - }, - ":vc:": { - "style": "github", - "image": "1f1fb-1f1e8.png", - "unicode": "🇻🇨", - "name": "Saint Vincent And The Grenadines" - }, - ":flag-sy:": { - "style": "github", - "image": "1f1f8-1f1fe.png", - "unicode": "🇸🇾", - "name": "Syria" - }, - ":crying-cat-face:": { - "style": "github", - "image": "1f63f.png", - "unicode": "😿", - "name": "Crying Cat Face" - }, - ":snake:": { - "style": "github", - "image": "1f40d.png", - "unicode": "🐍", - "name": "Snake" - }, - ":thermometer:": { - "style": "github", - "image": "1f321.png", - "unicode": "🌡", - "name": "Thermometer" - }, - ":mens:": { - "style": "github", - "image": "1f6b9.png", - "unicode": "🚹", - "name": "Mens Symbol" - }, - ":sneezing-face:": { - "style": "github", - "image": "1f927.png", - "unicode": "🤧", - "name": "Sneezing Face" - }, - ":timer:": { - "style": "github", - "image": "23f2.png", - "unicode": "⏲", - "name": "Timer Clock" - }, - ":bow_tone5:": { - "style": "github", - "image": "1f647-1f3ff.png", - "unicode": "🙇🏿", - "name": "Person Bowing Deeply - Tone 5" - }, - ":zero:": { - "style": "github", - "image": "0030-20e3.png", - "unicode": "0⃣", - "name": "Keycap Digit Zero" - }, - "🏃🏿": { - "style": "unicode", - "image": "1f3c3-1f3ff.png", - "name": "Runner - Tone 5" - }, - "🏃🏾": { - "style": "unicode", - "image": "1f3c3-1f3fe.png", - "name": "Runner - Tone 4" - }, - "🏃🏽": { - "style": "unicode", - "image": "1f3c3-1f3fd.png", - "name": "Runner - Tone 3" - }, - "🏃🏼": { - "style": "unicode", - "image": "1f3c3-1f3fc.png", - "name": "Runner - Tone 2" - }, - ":archery:": { - "style": "github", - "image": "1f3f9.png", - "unicode": "🏹", - "name": "Bow And Arrow" - }, - ":ballot_box_with_ballot:": { - "style": "github", - "image": "1f5f3.png", - "unicode": "🗳", - "name": "Ballot Box With Ballot" - }, - ":dagger:": { - "style": "github", - "image": "1f5e1.png", - "unicode": "🗡", - "name": "Dagger Knife" - }, - "🌓": { - "style": "unicode", - "image": "1f313.png", - "name": "First Quarter Moon Symbol" - }, - ":frame_photo:": { - "style": "github", - "image": "1f5bc.png", - "unicode": "🖼", - "name": "Frame With Picture" - }, - ":flag-cx:": { - "style": "github", - "image": "1f1e8-1f1fd.png", - "unicode": "🇨🇽", - "name": "Christmas Island" - }, - ":person_with_blond_hair_tone2:": { - "style": "github", - "image": "1f471-1f3fc.png", - "unicode": "👱🏼", - "name": "Person With Blond Hair - Tone 2" - }, - ":walking_tone3:": { - "style": "github", - "image": "1f6b6-1f3fd.png", - "unicode": "🚶🏽", - "name": "Pedestrian - Tone 3" - }, - ":telephone:": { - "style": "github", - "image": "260e.png", - "unicode": "☎", - "name": "Black Telephone" - }, - "🎨": { - "style": "unicode", - "image": "1f3a8.png", - "name": "Artist Palette" - }, - ":aq:": { - "style": "github", - "image": "1f1e6-1f1f6.png", - "unicode": "🇦🇶", - "name": "Antarctica" - }, - ":dizzy_face:": { - "style": "github", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":pig_nose:": { - "style": "github", - "image": "1f43d.png", - "unicode": "🐽", - "name": "Pig Nose" - }, - ":waxing-crescent-moon:": { - "style": "github", - "image": "1f312.png", - "unicode": "🌒", - "name": "Waxing Crescent Moon Symbol" - }, - "🐽": { - "style": "unicode", - "image": "1f43d.png", - "name": "Pig Nose" - }, - ":flag-ar:": { - "style": "github", - "image": "1f1e6-1f1f7.png", - "unicode": "🇦🇷", - "name": "Argentina" - }, - ":man_with_gua_pi_mao_tone5:": { - "style": "github", - "image": "1f472-1f3ff.png", - "unicode": "👲🏿", - "name": "Man With Gua Pi Mao - Tone 5" - }, - ":dancers:": { - "style": "github", - "image": "1f46f.png", - "unicode": "👯", - "name": "Woman With Bunny Ears" - }, - ":upside_down_face:": { - "style": "github", - "image": "1f643.png", - "unicode": "🙃", - "name": "Upside-down Face" - }, - "📒": { - "style": "unicode", - "image": "1f4d2.png", - "name": "Ledger" - }, - ":flag_bo:": { - "style": "github", - "image": "1f1e7-1f1f4.png", - "unicode": "🇧🇴", - "name": "Bolivia" - }, - ":ballot_box:": { - "style": "github", - "image": "1f5f3.png", - "unicode": "🗳", - "name": "Ballot Box With Ballot" - }, - ":moneybag:": { - "style": "github", - "image": "1f4b0.png", - "unicode": "💰", - "name": "Money Bag" - }, - ":prince-tone1:": { - "style": "github", - "image": "1f934-1f3fb.png", - "unicode": "🤴🏻", - "name": "Prince - Tone 1" - }, - ":alarm_clock:": { - "style": "github", - "image": "23f0.png", - "unicode": "⏰", - "name": "Alarm Clock" - }, - "🕧": { - "style": "unicode", - "image": "1f567.png", - "name": "Clock Face Twelve-thirty" - }, - ":footprints:": { - "style": "github", - "image": "1f463.png", - "unicode": "👣", - "name": "Footprints" - }, - "\\O/": { - "style": "ascii", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - ":feet:": { - "style": "github", - "image": "1f43e.png", - "unicode": "🐾", - "name": "Paw Prints" - }, - ":runner_tone4:": { - "style": "github", - "image": "1f3c3-1f3fe.png", - "unicode": "🏃🏾", - "name": "Runner - Tone 4" - }, - "🗼": { - "style": "unicode", - "image": "1f5fc.png", - "name": "Tokyo Tower" - }, - ":bullettrain-front:": { - "style": "github", - "image": "1f685.png", - "unicode": "🚅", - "name": "High-speed Train With Bullet Nose" - }, - ":point_left_tone1:": { - "style": "github", - "image": "1f448-1f3fb.png", - "unicode": "👈🏻", - "name": "White Left Pointing Backhand Index - Tone 1" - }, - ":white_sun_behind_cloud_with_rain:": { - "style": "github", - "image": "1f326.png", - "unicode": "🌦", - "name": "White Sun Behind Cloud With Rain" - }, - ":arrow_backward:": { - "style": "github", - "image": "25c0.png", - "unicode": "◀", - "name": "Black Left-pointing Triangle" - }, - ":right_facing_fist_tone2:": { - "style": "github", - "image": "1f91c-1f3fc.png", - "unicode": "🤜🏼", - "name": "Right Facing Fist - Tone 2" - }, - ":sign_of_the_horns_tone4:": { - "style": "github", - "image": "1f918-1f3fe.png", - "unicode": "🤘🏾", - "name": "Sign Of The Horns - Tone 4" - }, - ":regional-indicator-i:": { - "style": "github", - "image": "1f1ee.png", - "unicode": "🇮", - "name": "Regional Indicator Symbol Letter I" - }, - ":ear:": { - "style": "github", - "image": "1f442.png", - "unicode": "👂", - "name": "Ear" - }, - "🚯": { - "style": "unicode", - "image": "1f6af.png", - "name": "Do Not Litter Symbol" - }, - ":cowboy:": { - "style": "github", - "image": "1f920.png", - "unicode": "🤠", - "name": "Face With Cowboy Hat" - }, - ":purple-heart:": { - "style": "github", - "image": "1f49c.png", - "unicode": "💜", - "name": "Purple Heart" - }, - ":thumbsup_tone2:": { - "style": "github", - "image": "1f44d-1f3fc.png", - "unicode": "👍🏼", - "name": "Thumbs Up Sign - Tone 2" - }, - ":monkey-face:": { - "style": "github", - "image": "1f435.png", - "unicode": "🐵", - "name": "Monkey Face" - }, - "🙄": { - "style": "unicode", - "image": "1f644.png", - "name": "Face With Rolling Eyes" - }, - ":night_with_stars:": { - "style": "github", - "image": "1f303.png", - "unicode": "🌃", - "name": "Night With Stars" - }, - ":ok-hand-tone3:": { - "style": "github", - "image": "1f44c-1f3fd.png", - "unicode": "👌🏽", - "name": "Ok Hand Sign - Tone 3" - }, - ":jack_o_lantern:": { - "style": "github", - "image": "1f383.png", - "unicode": "🎃", - "name": "Jack-o-lantern" - }, - ":cheese_wedge:": { - "style": "github", - "image": "1f9c0.png", - "unicode": "🧀", - "name": "Cheese Wedge" - }, - ":unlock:": { - "style": "github", - "image": "1f513.png", - "unicode": "🔓", - "name": "Open Lock" - }, - ":juggler:": { - "style": "github", - "image": "1f939.png", - "unicode": "🤹", - "name": "Juggling" - }, - ":large-blue-diamond:": { - "style": "github", - "image": "1f537.png", - "unicode": "🔷", - "name": "Large Blue Diamond" - }, - "🐃": { - "style": "unicode", - "image": "1f403.png", - "name": "Water Buffalo" - }, - ":boy-tone4:": { - "style": "github", - "image": "1f466-1f3fe.png", - "unicode": "👦🏾", - "name": "Boy - Tone 4" - }, - ":mm:": { - "style": "github", - "image": "1f1f2-1f1f2.png", - "unicode": "🇲🇲", - "name": "Myanmar" - }, - "💘": { - "style": "unicode", - "image": "1f498.png", - "name": "Heart With Arrow" - }, - ":clock5:": { - "style": "github", - "image": "1f554.png", - "unicode": "🕔", - "name": "Clock Face Five Oclock" - }, - ":face_with_cowboy_hat:": { - "style": "github", - "image": "1f920.png", - "unicode": "🤠", - "name": "Face With Cowboy Hat" - }, - ":lion:": { - "style": "github", - "image": "1f981.png", - "unicode": "🦁", - "name": "Lion Face" - }, - ":basketball_player_tone5:": { - "style": "github", - "image": "26f9-1f3ff.png", - "unicode": "⛹🏿", - "name": "Person With Ball - Tone 5" - }, - ":running-shirt-with-sash:": { - "style": "github", - "image": "1f3bd.png", - "unicode": "🎽", - "name": "Running Shirt With Sash" - }, - "🌭": { - "style": "unicode", - "image": "1f32d.png", - "name": "Hot Dog" - }, - ":arrow-upper-right:": { - "style": "github", - "image": "2197.png", - "unicode": "↗", - "name": "North East Arrow" - }, - "🏂": { - "style": "unicode", - "image": "1f3c2.png", - "name": "Snowboarder" - }, - ":roller_coaster:": { - "style": "github", - "image": "1f3a2.png", - "unicode": "🎢", - "name": "Roller Coaster" - }, - ":flag_white:": { - "style": "github", - "image": "1f3f3.png", - "unicode": "🏳", - "name": "Waving White Flag" - }, - ":swimmer:": { - "style": "github", - "image": "1f3ca.png", - "unicode": "🏊", - "name": "Swimmer" - }, - ":call_me_tone2:": { - "style": "github", - "image": "1f919-1f3fc.png", - "unicode": "🤙🏼", - "name": "Call Me Hand - Tone 2" - }, - ":flag_cp:": { - "style": "github", - "image": "1f1e8-1f1f5.png", - "unicode": "🇨🇵", - "name": "Clipperton Island" - }, - ":tz:": { - "style": "github", - "image": "1f1f9-1f1ff.png", - "unicode": "🇹🇿", - "name": "Tanzania" - }, - ":upside-down:": { - "style": "github", - "image": "1f643.png", - "unicode": "🙃", - "name": "Upside-down Face" - }, - ":arrow-left:": { - "style": "github", - "image": "2b05.png", - "unicode": "⬅", - "name": "Leftwards Black Arrow" - }, - "🚅": { - "style": "unicode", - "image": "1f685.png", - "name": "High-speed Train With Bullet Nose" - }, - ":je:": { - "style": "github", - "image": "1f1ef-1f1ea.png", - "unicode": "🇯🇪", - "name": "Jersey" - }, - ":sparkle:": { - "style": "github", - "image": "2747.png", - "unicode": "❇", - "name": "Sparkle" - }, - ":flag-io:": { - "style": "github", - "image": "1f1ee-1f1f4.png", - "unicode": "🇮🇴", - "name": "British Indian Ocean Territory" - }, - ":kiwi:": { - "style": "github", - "image": "1f95d.png", - "unicode": "🥝", - "name": "Kiwifruit" - }, - ":clap-tone1:": { - "style": "github", - "image": "1f44f-1f3fb.png", - "unicode": "👏🏻", - "name": "Clapping Hands Sign - Tone 1" - }, - "😚": { - "style": "unicode", - "image": "1f61a.png", - "name": "Kissing Face With Closed Eyes" - }, - ":dancer_tone5:": { - "style": "github", - "image": "1f483-1f3ff.png", - "unicode": "💃🏿", - "name": "Dancer - Tone 5" - }, - ":lifter_tone4:": { - "style": "github", - "image": "1f3cb-1f3fe.png", - "unicode": "🏋🏾", - "name": "Weight Lifter - Tone 4" - }, - "👮🏻": { - "style": "unicode", - "image": "1f46e-1f3fb.png", - "name": "Police Officer - Tone 1" - }, - ":flag_mw:": { - "style": "github", - "image": "1f1f2-1f1fc.png", - "unicode": "🇲🇼", - "name": "Malawi" - }, - ":police_car:": { - "style": "github", - "image": "1f693.png", - "unicode": "🚓", - "name": "Police Car" - }, - ":credit-card:": { - "style": "github", - "image": "1f4b3.png", - "unicode": "💳", - "name": "Credit Card" - }, - ":bath_tone2:": { - "style": "github", - "image": "1f6c0-1f3fc.png", - "unicode": "🛀🏼", - "name": "Bath - Tone 2" - }, - ":flag_br:": { - "style": "github", - "image": "1f1e7-1f1f7.png", - "unicode": "🇧🇷", - "name": "Brazil" - }, - "📙": { - "style": "unicode", - "image": "1f4d9.png", - "name": "Orange Book" - }, - ":rolling-eyes:": { - "style": "github", - "image": "1f644.png", - "unicode": "🙄", - "name": "Face With Rolling Eyes" - }, - "👮": { - "style": "unicode", - "image": "1f46e.png", - "name": "Police Officer" - }, - ":ski:": { - "style": "github", - "image": "1f3bf.png", - "unicode": "🎿", - "name": "Ski And Ski Boot" - }, - ":metal:": { - "style": "github", - "image": "1f918.png", - "unicode": "🤘", - "name": "Sign Of The Horns" - }, - "🔃": { - "style": "unicode", - "image": "1f503.png", - "name": "Clockwise Downwards And Upwards Open Circle Arrows" - }, - ":gf:": { - "style": "github", - "image": "1f1ec-1f1eb.png", - "unicode": "🇬🇫", - "name": "French Guiana" - }, - ":sr:": { - "style": "github", - "image": "1f1f8-1f1f7.png", - "unicode": "🇸🇷", - "name": "Suriname" - }, - ":ok_hand_tone4:": { - "style": "github", - "image": "1f44c-1f3fe.png", - "unicode": "👌🏾", - "name": "Ok Hand Sign - Tone 4" - }, - ":canoe:": { - "style": "github", - "image": "1f6f6.png", - "unicode": "🛶", - "name": "Canoe" - }, - ":older_woman_tone1:": { - "style": "github", - "image": "1f475-1f3fb.png", - "unicode": "👵🏻", - "name": "Older Woman - Tone 1" - }, - ":black-nib:": { - "style": "github", - "image": "2712.png", - "unicode": "✒", - "name": "Black Nib" - }, - ":clock830:": { - "style": "github", - "image": "1f563.png", - "unicode": "🕣", - "name": "Clock Face Eight-thirty" - }, - ":hourglass_flowing_sand:": { - "style": "github", - "image": "23f3.png", - "unicode": "⏳", - "name": "Hourglass With Flowing Sand" - }, - ":flag-sj:": { - "style": "github", - "image": "1f1f8-1f1ef.png", - "unicode": "🇸🇯", - "name": "Svalbard And Jan Mayen" - }, - ":bath-tone5:": { - "style": "github", - "image": "1f6c0-1f3ff.png", - "unicode": "🛀🏿", - "name": "Bath - Tone 5" - }, - "🍗": { - "style": "unicode", - "image": "1f357.png", - "name": "Poultry Leg" - }, - "🥛": { - "style": "unicode", - "image": "1f95b.png", - "name": "Glass Of Milk" - }, - "♥": { - "style": "unicode", - "image": "2665.png", - "name": "Black Heart Suit" - }, - "🏬": { - "style": "unicode", - "image": "1f3ec.png", - "name": "Department Store" - }, - "⛺": { - "style": "unicode", - "image": "26fa.png", - "name": "Tent" - }, - "🎁": { - "style": "unicode", - "image": "1f381.png", - "name": "Wrapped Present" - }, - ":cr:": { - "style": "github", - "image": "1f1e8-1f1f7.png", - "unicode": "🇨🇷", - "name": "Costa Rica" - }, - ":mountain_bicyclist_tone5:": { - "style": "github", - "image": "1f6b5-1f3ff.png", - "unicode": "🚵🏿", - "name": "Mountain Bicyclist - Tone 5" - }, - ":worship_symbol:": { - "style": "github", - "image": "1f6d0.png", - "unicode": "🛐", - "name": "Place Of Worship" - }, - ":poultry-leg:": { - "style": "github", - "image": "1f357.png", - "unicode": "🍗", - "name": "Poultry Leg" - }, - "🌖": { - "style": "unicode", - "image": "1f316.png", - "name": "Waning Gibbous Moon Symbol" - }, - "🔚": { - "style": "unicode", - "image": "1f51a.png", - "name": "End With Leftwards Arrow Above" - }, - ":no-good-tone1:": { - "style": "github", - "image": "1f645-1f3fb.png", - "unicode": "🙅🏻", - "name": "Face With No Good Gesture - Tone 1" - }, - ":flag-ga:": { - "style": "github", - "image": "1f1ec-1f1e6.png", - "unicode": "🇬🇦", - "name": "Gabon" - }, - ":point-right-tone2:": { - "style": "github", - "image": "1f449-1f3fc.png", - "unicode": "👉🏼", - "name": "White Right Pointing Backhand Index - Tone 2" - }, - ":male_dancer_tone2:": { - "style": "github", - "image": "1f57a-1f3fc.png", - "unicode": "🕺🏼", - "name": "Man Dancing - Tone 2" - }, - "💯": { - "style": "unicode", - "image": "1f4af.png", - "name": "Hundred Points Symbol" - }, - ":right_fist:": { - "style": "github", - "image": "1f91c.png", - "unicode": "🤜", - "name": "Right-facing Fist" - }, - ":flag-mk:": { - "style": "github", - "image": "1f1f2-1f1f0.png", - "unicode": "🇲🇰", - "name": "Macedonia" - }, - ":oncoming-taxi:": { - "style": "github", - "image": "1f696.png", - "unicode": "🚖", - "name": "Oncoming Taxi" - }, - ":anguished:": { - "style": "github", - "image": "1f627.png", - "unicode": "😧", - "name": "Anguished Face" - }, - ":zm:": { - "style": "github", - "image": "1f1ff-1f1f2.png", - "unicode": "🇿🇲", - "name": "Zambia" - }, - "👄": { - "style": "unicode", - "image": "1f444.png", - "name": "Mouth" - }, - ":crown:": { - "style": "github", - "image": "1f451.png", - "unicode": "👑", - "name": "Crown" - }, - "❎": { - "style": "unicode", - "image": "274e.png", - "name": "Negative Squared Cross Mark" - }, - ":clock730:": { - "style": "github", - "image": "1f562.png", - "unicode": "🕢", - "name": "Clock Face Seven-thirty" - }, - ":second_place_medal:": { - "style": "github", - "image": "1f948.png", - "unicode": "🥈", - "name": "Second Place Medal" - }, - ":raising_hand_tone5:": { - "style": "github", - "image": "1f64b-1f3ff.png", - "unicode": "🙋🏿", - "name": "Happy Person Raising One Hand Tone5" - }, - ":flag-nu:": { - "style": "github", - "image": "1f1f3-1f1fa.png", - "unicode": "🇳🇺", - "name": "Niue" - }, - ":statue_of_liberty:": { - "style": "github", - "image": "1f5fd.png", - "unicode": "🗽", - "name": "Statue Of Liberty" - }, - "😃": { - "style": "unicode", - "ascii": ":D", - "image": "1f603.png", - "name": "Smiling Face With Open Mouth" - }, - ":juggler_tone1:": { - "style": "github", - "image": "1f939-1f3fb.png", - "unicode": "🤹🏻", - "name": "Juggling - Tone 1" - }, - ":shaking_hands_tone5:": { - "style": "github", - "image": "1f91d-1f3ff.png", - "unicode": "🤝🏿", - "name": "Handshake - Tone 5" - }, - "🚘": { - "style": "unicode", - "image": "1f698.png", - "name": "Oncoming Automobile" - }, - ":japan:": { - "style": "github", - "image": "1f5fe.png", - "unicode": "🗾", - "name": "Silhouette Of Japan" - }, - ":haircut-tone2:": { - "style": "github", - "image": "1f487-1f3fc.png", - "unicode": "💇🏼", - "name": "Haircut - Tone 2" - }, - ":flag-ro:": { - "style": "github", - "image": "1f1f7-1f1f4.png", - "unicode": "🇷🇴", - "name": "Romania" - }, - ":al:": { - "style": "github", - "image": "1f1e6-1f1f1.png", - "unicode": "🇦🇱", - "name": "Albania" - }, - ":flushed:": { - "style": "github", - "ascii": ":$", - "image": "1f633.png", - "unicode": "😳", - "name": "Flushed Face" - }, - ":ps:": { - "style": "github", - "image": "1f1f5-1f1f8.png", - "unicode": "🇵🇸", - "name": "Palestinian Authority" - }, - ":mother_christmas_tone4:": { - "style": "github", - "image": "1f936-1f3fe.png", - "unicode": "🤶🏾", - "name": "Mother Christmas - Tone 4" - }, - ":mrs_claus_tone1:": { - "style": "github", - "image": "1f936-1f3fb.png", - "unicode": "🤶🏻", - "name": "Mother Christmas - Tone 1" - }, - "👑": { - "style": "unicode", - "image": "1f451.png", - "name": "Crown" - }, - ":couplekiss_ww:": { - "style": "github", - "image": "1f469-2764-1f48b-1f469.png", - "unicode": "👩❤💋👩", - "name": "Kiss (woman,woman)" - }, - ":clock630:": { - "style": "github", - "image": "1f561.png", - "unicode": "🕡", - "name": "Clock Face Six-thirty" - }, - ":fish_cake:": { - "style": "github", - "image": "1f365.png", - "unicode": "🍥", - "name": "Fish Cake With Swirl Design" - }, - ":bicyclist-tone1:": { - "style": "github", - "image": "1f6b4-1f3fb.png", - "unicode": "🚴🏻", - "name": "Bicyclist - Tone 1" - }, - "💅": { - "style": "unicode", - "image": "1f485.png", - "name": "Nail Polish" - }, - ":mrs-claus-tone4:": { - "style": "github", - "image": "1f936-1f3fe.png", - "unicode": "🤶🏾", - "name": "Mother Christmas - Tone 4" - }, - "🤐": { - "style": "unicode", - "image": "1f910.png", - "name": "Zipper-mouth Face" - }, - ":person_with_ball_tone3:": { - "style": "github", - "image": "26f9-1f3fd.png", - "unicode": "⛹🏽", - "name": "Person With Ball - Tone 3" - }, - ":confused:": { - "style": "github", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - "🐚": { - "style": "unicode", - "image": "1f41a.png", - "name": "Spiral Shell" - }, - "🎫": { - "style": "unicode", - "image": "1f3ab.png", - "name": "Ticket" - }, - ":point_up_2_tone3:": { - "style": "github", - "image": "1f446-1f3fd.png", - "unicode": "👆🏽", - "name": "White Up Pointing Backhand Index - Tone 3" - }, - ":speak_no_evil:": { - "style": "github", - "image": "1f64a.png", - "unicode": "🙊", - "name": "Speak-no-evil Monkey" - }, - ":nose-tone5:": { - "style": "github", - "image": "1f443-1f3ff.png", - "unicode": "👃🏿", - "name": "Nose - Tone 5" - }, - ":fish-cake:": { - "style": "github", - "image": "1f365.png", - "unicode": "🍥", - "name": "Fish Cake With Swirl Design" - }, - ":flag-za:": { - "style": "github", - "image": "1f1ff-1f1e6.png", - "unicode": "🇿🇦", - "name": "South Africa" - }, - ":right_facing_fist:": { - "style": "github", - "image": "1f91c.png", - "unicode": "🤜", - "name": "Right-facing Fist" - }, - ":capital-abcd:": { - "style": "github", - "image": "1f520.png", - "unicode": "🔠", - "name": "Input Symbol For Latin Capital Letters" - }, - "♎": { - "style": "unicode", - "image": "264e.png", - "name": "Libra" - }, - ":raised_hand_with_fingers_splayed_tone3:": { - "style": "github", - "image": "1f590-1f3fd.png", - "unicode": "🖐🏽", - "name": "Raised Hand With Fingers Splayed - Tone 3" - }, - ":regional_indicator_j:": { - "style": "github", - "image": "1f1ef.png", - "unicode": "🇯", - "name": "Regional Indicator Symbol Letter J" - }, - ":raised_hand_with_part_between_middle_and_ring_fingers_tone2:": { - "style": "github", - "image": "1f596-1f3fc.png", - "unicode": "🖖🏼", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 2" - }, - ":raised_hand_tone2:": { - "style": "github", - "image": "270b-1f3fc.png", - "unicode": "✋🏼", - "name": "Raised Hand - Tone 2" - }, - ":prince_tone3:": { - "style": "github", - "image": "1f934-1f3fd.png", - "unicode": "🤴🏽", - "name": "Prince - Tone 3" - }, - ":flag_aw:": { - "style": "github", - "image": "1f1e6-1f1fc.png", - "unicode": "🇦🇼", - "name": "Aruba" - }, - ":football:": { - "style": "github", - "image": "1f3c8.png", - "unicode": "🏈", - "name": "American Football" - }, - ":film_projector:": { - "style": "github", - "image": "1f4fd.png", - "unicode": "📽", - "name": "Film Projector" - }, - ":ve:": { - "style": "github", - "image": "1f1fb-1f1ea.png", - "unicode": "🇻🇪", - "name": "Venezuela" - }, - ":camera_with_flash:": { - "style": "github", - "image": "1f4f8.png", - "unicode": "📸", - "name": "Camera With Flash" - }, - ":bicyclist_tone5:": { - "style": "github", - "image": "1f6b4-1f3ff.png", - "unicode": "🚴🏿", - "name": "Bicyclist - Tone 5" - }, - ":flag-kz:": { - "style": "github", - "image": "1f1f0-1f1ff.png", - "unicode": "🇰🇿", - "name": "Kazakhstan" - }, - ":first_place:": { - "style": "github", - "image": "1f947.png", - "unicode": "🥇", - "name": "First Place Medal" - }, - ":potable-water:": { - "style": "github", - "image": "1f6b0.png", - "unicode": "🚰", - "name": "Potable Water Symbol" - }, - ":tuxedo_tone1:": { - "style": "github", - "image": "1f935-1f3fb.png", - "unicode": "🤵🏻", - "name": "Man In Tuxedo - Tone 1" - }, - ":post-office:": { - "style": "github", - "image": "1f3e3.png", - "unicode": "🏣", - "name": "Japanese Post Office" - }, - "🇬": { - "style": "unicode", - "image": "1f1ec.png", - "name": "Regional Indicator Symbol Letter G" - }, - ":flag-cz:": { - "style": "github", - "image": "1f1e8-1f1ff.png", - "unicode": "🇨🇿", - "name": "The Czech Republic" - }, - ":musical-note:": { - "style": "github", - "image": "1f3b5.png", - "unicode": "🎵", - "name": "Musical Note" - }, - "🎅": { - "style": "unicode", - "image": "1f385.png", - "name": "Father Christmas" - }, - "🦉": { - "style": "unicode", - "image": "1f989.png", - "name": "Owl" - }, - ":field-hockey:": { - "style": "github", - "image": "1f3d1.png", - "unicode": "🏑", - "name": "Field Hockey Stick And Ball" - }, - ":call_me:": { - "style": "github", - "image": "1f919.png", - "unicode": "🤙", - "name": "Call Me Hand" - }, - "🔖": { - "style": "unicode", - "image": "1f516.png", - "name": "Bookmark" - }, - ":hatching-chick:": { - "style": "github", - "image": "1f423.png", - "unicode": "🐣", - "name": "Hatching Chick" - }, - "🤞": { - "style": "unicode", - "image": "1f91e.png", - "name": "Hand With First And Index Finger Crossed" - }, - ":as:": { - "style": "github", - "image": "1f1e6-1f1f8.png", - "unicode": "🇦🇸", - "name": "American Samoa" - }, - ":person_with_blond_hair_tone4:": { - "style": "github", - "image": "1f471-1f3fe.png", - "unicode": "👱🏾", - "name": "Person With Blond Hair - Tone 4" - }, - "💫": { - "style": "unicode", - "image": "1f4ab.png", - "name": "Dizzy Symbol" - }, - ":notebook:": { - "style": "github", - "image": "1f4d3.png", - "unicode": "📓", - "name": "Notebook" - }, - ":shark:": { - "style": "github", - "image": "1f988.png", - "unicode": "🦈", - "name": "Shark" - }, - "👀": { - "style": "unicode", - "image": "1f440.png", - "name": "Eyes" - }, - ":punch_tone2:": { - "style": "github", - "image": "1f44a-1f3fc.png", - "unicode": "👊🏼", - "name": "Fisted Hand Sign - Tone 2" - }, - ":ic:": { - "style": "github", - "image": "1f1ee-1f1e8.png", - "unicode": "🇮🇨", - "name": "Canary Islands" - }, - ":flag_bi:": { - "style": "github", - "image": "1f1e7-1f1ee.png", - "unicode": "🇧🇮", - "name": "Burundi" - }, - ":bar_chart:": { - "style": "github", - "image": "1f4ca.png", - "unicode": "📊", - "name": "Bar Chart" - }, - ":cloud_with_snow:": { - "style": "github", - "image": "1f328.png", - "unicode": "🌨", - "name": "Cloud With Snow" - }, - "*-)": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ":octagonal-sign:": { - "style": "github", - "image": "1f6d1.png", - "unicode": "🛑", - "name": "Octagonal Sign" - }, - ":fist_tone5:": { - "style": "github", - "image": "270a-1f3ff.png", - "unicode": "✊🏿", - "name": "Raised Fist - Tone 5" - }, - "✍": { - "style": "unicode", - "image": "270d.png", - "name": "Writing Hand" - }, - ":point_left_tone3:": { - "style": "github", - "image": "1f448-1f3fd.png", - "unicode": "👈🏽", - "name": "White Left Pointing Backhand Index - Tone 3" - }, - ":gy:": { - "style": "github", - "image": "1f1ec-1f1fe.png", - "unicode": "🇬🇾", - "name": "Guyana" - }, - ":regional-indicator-o:": { - "style": "github", - "image": "1f1f4.png", - "unicode": "🇴", - "name": "Regional Indicator Symbol Letter O" - }, - ":dancer-tone1:": { - "style": "github", - "image": "1f483-1f3fb.png", - "unicode": "💃🏻", - "name": "Dancer - Tone 1" - }, - ":city_sunrise:": { - "style": "github", - "image": "1f307.png", - "unicode": "🌇", - "name": "Sunset Over Buildings" - }, - ":thumbsdown_tone4:": { - "style": "github", - "image": "1f44e-1f3fe.png", - "unicode": "👎🏾", - "name": "Thumbs Down Sign - Tone 4" - }, - ":video_camera:": { - "style": "github", - "image": "1f4f9.png", - "unicode": "📹", - "name": "Video Camera" - }, - ":christmas_tree:": { - "style": "github", - "image": "1f384.png", - "unicode": "🎄", - "name": "Christmas Tree" - }, - ":flag_kg:": { - "style": "github", - "image": "1f1f0-1f1ec.png", - "unicode": "🇰🇬", - "name": "Kyrgyzstan" - }, - ":ocean:": { - "style": "github", - "image": "1f30a.png", - "unicode": "🌊", - "name": "Water Wave" - }, - ":stuck_out_tongue_closed_eyes:": { - "style": "github", - "image": "1f61d.png", - "unicode": "😝", - "name": "Face With Stuck-out Tongue And Tightly-closed Eyes" - }, - ":flag_sv:": { - "style": "github", - "image": "1f1f8-1f1fb.png", - "unicode": "🇸🇻", - "name": "El Salvador" - }, - "💧": { - "style": "unicode", - "image": "1f4a7.png", - "name": "Droplet" - }, - ":ear-of-rice:": { - "style": "github", - "image": "1f33e.png", - "unicode": "🌾", - "name": "Ear Of Rice" - }, - "🛬": { - "style": "unicode", - "image": "1f6ec.png", - "name": "Airplane Arriving" - }, - ":flag-bt:": { - "style": "github", - "image": "1f1e7-1f1f9.png", - "unicode": "🇧🇹", - "name": "Bhutan" - }, - ":waning_gibbous_moon:": { - "style": "github", - "image": "1f316.png", - "unicode": "🌖", - "name": "Waning Gibbous Moon Symbol" - }, - ":family-mmbb:": { - "style": "github", - "image": "1f468-1f468-1f466-1f466.png", - "unicode": "👨👨👦👦", - "name": "Family (man,man,boy,boy)" - }, - "⏺": { - "style": "unicode", - "image": "23fa.png", - "name": "Black Circle For Record" - }, - "💁": { - "style": "unicode", - "image": "1f481.png", - "name": "Information Desk Person" - }, - ":eight-spoked-asterisk:": { - "style": "github", - "image": "2733.png", - "unicode": "✳", - "name": "Eight Spoked Asterisk" - }, - ":mo:": { - "style": "github", - "image": "1f1f2-1f1f4.png", - "unicode": "🇲🇴", - "name": "Macau" - }, - ":santa_tone3:": { - "style": "github", - "image": "1f385-1f3fd.png", - "unicode": "🎅🏽", - "name": "Father Christmas - Tone 3" - }, - ":japanese_goblin:": { - "style": "github", - "image": "1f47a.png", - "unicode": "👺", - "name": "Japanese Goblin" - }, - "🐖": { - "style": "unicode", - "image": "1f416.png", - "name": "Pig" - }, - "🈚": { - "style": "unicode", - "image": "1f21a.png", - "name": "Squared Cjk Unified Ideograph-7121" - }, - ":grey_question:": { - "style": "github", - "image": "2754.png", - "unicode": "❔", - "name": "White Question Mark Ornament" - }, - "🎯": { - "style": "unicode", - "image": "1f3af.png", - "name": "Direct Hit" - }, - ":bacon:": { - "style": "github", - "image": "1f953.png", - "unicode": "🥓", - "name": "Bacon" - }, - ":thumbsdown-tone4:": { - "style": "github", - "image": "1f44e-1f3fe.png", - "unicode": "👎🏾", - "name": "Thumbs Down Sign - Tone 4" - }, - ":ly:": { - "style": "github", - "image": "1f1f1-1f1fe.png", - "unicode": "🇱🇾", - "name": "Libya" - }, - "🍄": { - "style": "unicode", - "image": "1f344.png", - "name": "Mushroom" - }, - ":flag_cv:": { - "style": "github", - "image": "1f1e8-1f1fb.png", - "unicode": "🇨🇻", - "name": "Cape Verde" - }, - "🥈": { - "style": "unicode", - "image": "1f948.png", - "name": "Second Place Medal" - }, - "👩👩👧": { - "style": "unicode", - "image": "1f469-1f469-1f467.png", - "name": "Family (woman,woman,girl)" - }, - "👩👩👦": { - "style": "unicode", - "image": "1f469-1f469-1f466.png", - "name": "Family (woman,woman,boy)" - }, - ":golf:": { - "style": "github", - "image": "26f3.png", - "unicode": "⛳", - "name": "Flag In Hole" - }, - ":railway_car:": { - "style": "github", - "image": "1f683.png", - "unicode": "🚃", - "name": "Railway Car" - }, - ":wind_blowing_face:": { - "style": "github", - "image": "1f32c.png", - "unicode": "🌬", - "name": "Wind Blowing Face" - }, - ":flag_lc:": { - "style": "github", - "image": "1f1f1-1f1e8.png", - "unicode": "🇱🇨", - "name": "Saint Lucia" - }, - ":^*": { - "style": "ascii", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":shelled_peanut:": { - "style": "github", - "image": "1f95c.png", - "unicode": "🥜", - "name": "Peanuts" - }, - ":non-potable-water:": { - "style": "github", - "image": "1f6b1.png", - "unicode": "🚱", - "name": "Non-potable Water Symbol" - }, - "<3": { - "style": "ascii", - "ascii": "<3", - "image": "2764.png", - "unicode": "❤", - "name": "Heavy Black Heart" - }, - "🆘": { - "style": "unicode", - "image": "1f198.png", - "name": "Squared Sos" - }, - ":swimmer-tone3:": { - "style": "github", - "image": "1f3ca-1f3fd.png", - "unicode": "🏊🏽", - "name": "Swimmer - Tone 3" - }, - "😭": { - "style": "unicode", - "image": "1f62d.png", - "name": "Loudly Crying Face" - }, - ":guardsman-tone3:": { - "style": "github", - "image": "1f482-1f3fd.png", - "unicode": "💂🏽", - "name": "Guardsman - Tone 3" - }, - ":flag_my:": { - "style": "github", - "image": "1f1f2-1f1fe.png", - "unicode": "🇲🇾", - "name": "Malaysia" - }, - ":closed_umbrella:": { - "style": "github", - "image": "1f302.png", - "unicode": "🌂", - "name": "Closed Umbrella" - }, - "🛂": { - "style": "unicode", - "image": "1f6c2.png", - "name": "Passport Control" - }, - ":point_right_tone5:": { - "style": "github", - "image": "1f449-1f3ff.png", - "unicode": "👉🏿", - "name": "White Right Pointing Backhand Index - Tone 5" - }, - ":white_check_mark:": { - "style": "github", - "image": "2705.png", - "unicode": "✅", - "name": "White Heavy Check Mark" - }, - ":shit:": { - "style": "github", - "image": "1f4a9.png", - "unicode": "💩", - "name": "Pile Of Poo" - }, - "💆🏻": { - "style": "unicode", - "image": "1f486-1f3fb.png", - "name": "Face Massage - Tone 1" - }, - "💆🏼": { - "style": "unicode", - "image": "1f486-1f3fc.png", - "name": "Face Massage - Tone 2" - }, - "💆🏽": { - "style": "unicode", - "image": "1f486-1f3fd.png", - "name": "Face Massage - Tone 3" - }, - "💆🏾": { - "style": "unicode", - "image": "1f486-1f3fe.png", - "name": "Face Massage - Tone 4" - }, - "💆🏿": { - "style": "unicode", - "image": "1f486-1f3ff.png", - "name": "Face Massage - Tone 5" - }, - ":sleeping-accommodation:": { - "style": "github", - "image": "1f6cc.png", - "unicode": "🛌", - "name": "Sleeping Accommodation" - }, - ":flag-im:": { - "style": "github", - "image": "1f1ee-1f1f2.png", - "unicode": "🇮🇲", - "name": "Isle Of Man" - }, - ":fireworks:": { - "style": "github", - "image": "1f386.png", - "unicode": "🎆", - "name": "Fireworks" - }, - ":gd:": { - "style": "github", - "image": "1f1ec-1f1e9.png", - "unicode": "🇬🇩", - "name": "Grenada" - }, - ":walking-tone5:": { - "style": "github", - "image": "1f6b6-1f3ff.png", - "unicode": "🚶🏿", - "name": "Pedestrian - Tone 5" - }, - ":white-circle:": { - "style": "github", - "image": "26aa.png", - "unicode": "⚪", - "name": "Medium White Circle" - }, - ":tanabata_tree:": { - "style": "github", - "image": "1f38b.png", - "unicode": "🎋", - "name": "Tanabata Tree" - }, - ":smirk_cat:": { - "style": "github", - "image": "1f63c.png", - "unicode": "😼", - "name": "Cat Face With Wry Smile" - }, - "☠": { - "style": "unicode", - "image": "2620.png", - "name": "Skull And Crossbones" - }, - ":older-woman-tone5:": { - "style": "github", - "image": "1f475-1f3ff.png", - "unicode": "👵🏿", - "name": "Older Woman - Tone 5" - }, - "🚫": { - "style": "unicode", - "image": "1f6ab.png", - "name": "No Entry Sign" - }, - "💉": { - "style": "unicode", - "image": "1f489.png", - "name": "Syringe" - }, - ":beach-umbrella:": { - "style": "github", - "image": "26f1.png", - "unicode": "⛱", - "name": "Umbrella On Ground" - }, - ":basketball:": { - "style": "github", - "image": "1f3c0.png", - "unicode": "🏀", - "name": "Basketball And Hoop" - }, - "🙀": { - "style": "unicode", - "image": "1f640.png", - "name": "Weary Cat Face" - }, - ":handball_tone4:": { - "style": "github", - "image": "1f93e-1f3fe.png", - "unicode": "🤾🏾", - "name": "Handball - Tone 4" - }, - ":purse:": { - "style": "github", - "image": "1f45b.png", - "unicode": "👛", - "name": "Purse" - }, - "🏙": { - "style": "unicode", - "image": "1f3d9.png", - "name": "Cityscape" - }, - "🇨🇵": { - "style": "unicode", - "image": "1f1e8-1f1f5.png", - "name": "Clipperton Island" - }, - "🍮": { - "style": "unicode", - "image": "1f36e.png", - "name": "Custard" - }, - ":wheel-of-dharma:": { - "style": "github", - "image": "2638.png", - "unicode": "☸", - "name": "Wheel Of Dharma" - }, - ":no-good-tone3:": { - "style": "github", - "image": "1f645-1f3fd.png", - "unicode": "🙅🏽", - "name": "Face With No Good Gesture - Tone 3" - }, - ":love_letter:": { - "style": "github", - "image": "1f48c.png", - "unicode": "💌", - "name": "Love Letter" - }, - ":sweat_drops:": { - "style": "github", - "image": "1f4a6.png", - "unicode": "💦", - "name": "Splashing Sweat Symbol" - }, - ":bride_with_veil_tone2:": { - "style": "github", - "image": "1f470-1f3fc.png", - "unicode": "👰🏼", - "name": "Bride With Veil - Tone 2" - }, - "🔭": { - "style": "unicode", - "image": "1f52d.png", - "name": "Telescope" - }, - "🌜": { - "style": "unicode", - "image": "1f31c.png", - "name": "Last Quarter Moon With Face" - }, - "🤵": { - "style": "unicode", - "image": "1f935.png", - "name": "Man In Tuxedo" - }, - "🐞": { - "style": "unicode", - "image": "1f41e.png", - "name": "Lady Beetle" - }, - "🗂": { - "style": "unicode", - "image": "1f5c2.png", - "name": "Card Index Dividers" - }, - ":+1_tone4:": { - "style": "github", - "image": "1f44d-1f3fe.png", - "unicode": "👍🏾", - "name": "Thumbs Up Sign - Tone 4" - }, - "👗": { - "style": "unicode", - "image": "1f457.png", - "name": "Dress" - }, - ":school:": { - "style": "github", - "image": "1f3eb.png", - "unicode": "🏫", - "name": "School" - }, - ":put_litter_in_its_place:": { - "style": "github", - "image": "1f6ae.png", - "unicode": "🚮", - "name": "Put Litter In Its Place Symbol" - }, - "📬": { - "style": "unicode", - "image": "1f4ec.png", - "name": "Open Mailbox With Raised Flag" - }, - ":cp:": { - "style": "github", - "image": "1f1e8-1f1f5.png", - "unicode": "🇨🇵", - "name": "Clipperton Island" - }, - "🚁": { - "style": "unicode", - "image": "1f681.png", - "name": "Helicopter" - }, - "☪": { - "style": "unicode", - "image": "262a.png", - "name": "Star And Crescent" - }, - ":clock1130:": { - "style": "github", - "image": "1f566.png", - "unicode": "🕦", - "name": "Clock Face Eleven-thirty" - }, - "😖": { - "style": "unicode", - "image": "1f616.png", - "name": "Confounded Face" - }, - ":flag-fk:": { - "style": "github", - "image": "1f1eb-1f1f0.png", - "unicode": "🇫🇰", - "name": "Falkland Islands" - }, - ":flag_gt:": { - "style": "github", - "image": "1f1ec-1f1f9.png", - "unicode": "🇬🇹", - "name": "Guatemala" - }, - ":city-dusk:": { - "style": "github", - "image": "1f306.png", - "unicode": "🌆", - "name": "Cityscape At Dusk" - }, - ":call_me_hand_tone4:": { - "style": "github", - "image": "1f919-1f3fe.png", - "unicode": "🤙🏾", - "name": "Call Me Hand - Tone 4" - }, - ":mrs_claus_tone3:": { - "style": "github", - "image": "1f936-1f3fd.png", - "unicode": "🤶🏽", - "name": "Mother Christmas - Tone 3" - }, - ":bank:": { - "style": "github", - "image": "1f3e6.png", - "unicode": "🏦", - "name": "Bank" - }, - ":office:": { - "style": "github", - "image": "1f3e2.png", - "unicode": "🏢", - "name": "Office Building" - }, - ":punch:": { - "style": "github", - "image": "1f44a.png", - "unicode": "👊", - "name": "Fisted Hand Sign" - }, - ":open-hands:": { - "style": "github", - "image": "1f450.png", - "unicode": "👐", - "name": "Open Hands Sign" - }, - "🌃": { - "style": "unicode", - "image": "1f303.png", - "name": "Night With Stars" - }, - ":fingers_crossed:": { - "style": "github", - "image": "1f91e.png", - "unicode": "🤞", - "name": "Hand With First And Index Finger Crossed" - }, - ":regional-indicator-p:": { - "style": "github", - "image": "1f1f5.png", - "unicode": "🇵", - "name": "Regional Indicator Symbol Letter P" - }, - ":point_up_2_tone1:": { - "style": "github", - "image": "1f446-1f3fb.png", - "unicode": "👆🏻", - "name": "White Up Pointing Backhand Index - Tone 1" - }, - ":heartbeat:": { - "style": "github", - "image": "1f493.png", - "unicode": "💓", - "name": "Beating Heart" - }, - ":person_with_ball_tone5:": { - "style": "github", - "image": "26f9-1f3ff.png", - "unicode": "⛹🏿", - "name": "Person With Ball - Tone 5" - }, - "🐭": { - "style": "unicode", - "image": "1f42d.png", - "name": "Mouse Face" - }, - ":wrestlers-tone5:": { - "style": "github", - "image": "1f93c-1f3ff.png", - "unicode": "🤼🏿", - "name": "Wrestlers - Tone 5" - }, - ":tea:": { - "style": "github", - "image": "1f375.png", - "unicode": "🍵", - "name": "Teacup Without Handle" - }, - ":point_up_tone3:": { - "style": "github", - "image": "261d-1f3fd.png", - "unicode": "☝🏽", - "name": "White Up Pointing Index - Tone 3" - }, - ":hushed:": { - "style": "github", - "image": "1f62f.png", - "unicode": "😯", - "name": "Hushed Face" - }, - "📂": { - "style": "unicode", - "image": "1f4c2.png", - "name": "Open File Folder" - }, - ":cop:": { - "style": "github", - "image": "1f46e.png", - "unicode": "👮", - "name": "Police Officer" - }, - ":spider_web:": { - "style": "github", - "image": "1f578.png", - "unicode": "🕸", - "name": "Spider Web" - }, - ":regional_indicator_h:": { - "style": "github", - "image": "1f1ed.png", - "unicode": "🇭", - "name": "Regional Indicator Symbol Letter H" - }, - ":raised_hand_with_fingers_splayed_tone5:": { - "style": "github", - "image": "1f590-1f3ff.png", - "unicode": "🖐🏿", - "name": "Raised Hand With Fingers Splayed - Tone 5" - }, - "🕗": { - "style": "unicode", - "image": "1f557.png", - "name": "Clock Face Eight Oclock" - }, - "🐧": { - "style": "unicode", - "image": "1f427.png", - "name": "Penguin" - }, - ":flag_gu:": { - "style": "github", - "image": "1f1ec-1f1fa.png", - "unicode": "🇬🇺", - "name": "Guam" - }, - ":milky_way:": { - "style": "github", - "image": "1f30c.png", - "unicode": "🌌", - "name": "Milky Way" - }, - ":gift_heart:": { - "style": "github", - "image": "1f49d.png", - "unicode": "💝", - "name": "Heart With Ribbon" - }, - ":expecting_woman_tone1:": { - "style": "github", - "image": "1f930-1f3fb.png", - "unicode": "🤰🏻", - "name": "Pregnant Woman - Tone 1" - }, - "🦁": { - "style": "unicode", - "image": "1f981.png", - "name": "Lion Face" - }, - ":person-frowning-tone4:": { - "style": "github", - "image": "1f64d-1f3fe.png", - "unicode": "🙍🏾", - "name": "Person Frowning - Tone 4" - }, - "🤖": { - "style": "unicode", - "image": "1f916.png", - "name": "Robot Face" - }, - "🔞": { - "style": "unicode", - "image": "1f51e.png", - "name": "No One Under Eighteen Symbol" - }, - ":boy_tone2:": { - "style": "github", - "image": "1f466-1f3fc.png", - "unicode": "👦🏼", - "name": "Boy - Tone 2" - }, - ":flag_aq:": { - "style": "github", - "image": "1f1e6-1f1f6.png", - "unicode": "🇦🇶", - "name": "Antarctica" - }, - ":cloud_lightning:": { - "style": "github", - "image": "1f329.png", - "unicode": "🌩", - "name": "Cloud With Lightning" - }, - "💳": { - "style": "unicode", - "image": "1f4b3.png", - "name": "Credit Card" - }, - ":vg:": { - "style": "github", - "image": "1f1fb-1f1ec.png", - "unicode": "🇻🇬", - "name": "British Virgin Islands" - }, - "👮🏼": { - "style": "unicode", - "image": "1f46e-1f3fc.png", - "name": "Police Officer - Tone 2" - }, - "👮🏽": { - "style": "unicode", - "image": "1f46e-1f3fd.png", - "name": "Police Officer - Tone 3" - }, - "👮🏾": { - "style": "unicode", - "image": "1f46e-1f3fe.png", - "name": "Police Officer - Tone 4" - }, - "👮🏿": { - "style": "unicode", - "image": "1f46e-1f3ff.png", - "name": "Police Officer - Tone 5" - }, - ":family-mwg:": { - "style": "github", - "image": "1f468-1f469-1f467.png", - "unicode": "👨👩👧", - "name": "Family (man,woman,girl)" - }, - ":watch:": { - "style": "github", - "image": "231a.png", - "unicode": "⌚", - "name": "Watch" - }, - "👈": { - "style": "unicode", - "image": "1f448.png", - "name": "White Left Pointing Backhand Index" - }, - ":flag_ck:": { - "style": "github", - "image": "1f1e8-1f1f0.png", - "unicode": "🇨🇰", - "name": "Cook Islands" - }, - ":lb:": { - "style": "github", - "image": "1f1f1-1f1e7.png", - "unicode": "🇱🇧", - "name": "Lebanon" - }, - "🏝": { - "style": "unicode", - "image": "1f3dd.png", - "name": "Desert Island" - }, - ":water-polo-tone2:": { - "style": "github", - "image": "1f93d-1f3fc.png", - "unicode": "🤽🏼", - "name": "Water Polo - Tone 2" - }, - ":tuxedo_tone3:": { - "style": "github", - "image": "1f935-1f3fd.png", - "unicode": "🤵🏽", - "name": "Man In Tuxedo - Tone 3" - }, - "🍲": { - "style": "unicode", - "image": "1f372.png", - "name": "Pot Of Food" - }, - ":santa-tone2:": { - "style": "github", - "image": "1f385-1f3fc.png", - "unicode": "🎅🏼", - "name": "Father Christmas - Tone 2" - }, - "':-D": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - ":two_men_holding_hands:": { - "style": "github", - "image": "1f46c.png", - "unicode": "👬", - "name": "Two Men Holding Hands" - }, - ":eggplant:": { - "style": "github", - "image": "1f346.png", - "unicode": "🍆", - "name": "Aubergine" - }, - ":water_polo_tone4:": { - "style": "github", - "image": "1f93d-1f3fe.png", - "unicode": "🤽🏾", - "name": "Water Polo - Tone 4" - }, - "🙌🏽": { - "style": "unicode", - "image": "1f64c-1f3fd.png", - "name": "Person Raising Both Hands In Celebration - Tone 3" - }, - ":horse_racing_tone2:": { - "style": "github", - "image": "1f3c7-1f3fc.png", - "unicode": "🏇🏼", - "name": "Horse Racing - Tone 2" - }, - ":raising_hand_tone4:": { - "style": "github", - "image": "1f64b-1f3fe.png", - "unicode": "🙋🏾", - "name": "Happy Person Raising One Hand Tone4" - }, - ":selfie:": { - "style": "github", - "image": "1f933.png", - "unicode": "🤳", - "name": "Selfie" - }, - "⛔": { - "style": "unicode", - "image": "26d4.png", - "name": "No Entry" - }, - ":im:": { - "style": "github", - "image": "1f1ee-1f1f2.png", - "unicode": "🇮🇲", - "name": "Isle Of Man" - }, - "':-(": { - "style": "ascii", - "ascii": "':(", - "image": "1f613.png", - "unicode": "😓", - "name": "Face With Cold Sweat" - }, - "':-)": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - ":small-red-triangle:": { - "style": "github", - "image": "1f53a.png", - "unicode": "🔺", - "name": "Up-pointing Red Triangle" - }, - "🛴": { - "style": "unicode", - "image": "1f6f4.png", - "name": "Scooter" - }, - "◾": { - "style": "unicode", - "image": "25fe.png", - "name": "Black Medium Small Square" - }, - ":point_left_tone5:": { - "style": "github", - "image": "1f448-1f3ff.png", - "unicode": "👈🏿", - "name": "White Left Pointing Backhand Index - Tone 5" - }, - ":level-slider:": { - "style": "github", - "image": "1f39a.png", - "unicode": "🎚", - "name": "Level Slider" - }, - ":princess-tone2:": { - "style": "github", - "image": "1f478-1f3fc.png", - "unicode": "👸🏼", - "name": "Princess - Tone 2" - }, - ":regional-indicator-m:": { - "style": "github", - "image": "1f1f2.png", - "unicode": "🇲", - "name": "Regional Indicator Symbol Letter M" - }, - ":cloud-lightning:": { - "style": "github", - "image": "1f329.png", - "unicode": "🌩", - "name": "Cloud With Lightning" - }, - ":bell:": { - "style": "github", - "image": "1f514.png", - "unicode": "🔔", - "name": "Bell" - }, - "👈🏻": { - "style": "unicode", - "image": "1f448-1f3fb.png", - "name": "White Left Pointing Backhand Index - Tone 1" - }, - "👈🏾": { - "style": "unicode", - "image": "1f448-1f3fe.png", - "name": "White Left Pointing Backhand Index - Tone 4" - }, - "👈🏿": { - "style": "unicode", - "image": "1f448-1f3ff.png", - "name": "White Left Pointing Backhand Index - Tone 5" - }, - "👈🏼": { - "style": "unicode", - "image": "1f448-1f3fc.png", - "name": "White Left Pointing Backhand Index - Tone 2" - }, - "👈🏽": { - "style": "unicode", - "image": "1f448-1f3fd.png", - "name": "White Left Pointing Backhand Index - Tone 3" - }, - ":dancer-tone3:": { - "style": "github", - "image": "1f483-1f3fd.png", - "unicode": "💃🏽", - "name": "Dancer - Tone 3" - }, - ":smiley-cat:": { - "style": "github", - "image": "1f63a.png", - "unicode": "😺", - "name": "Smiling Cat Face With Open Mouth" - }, - ":eye:": { - "style": "github", - "image": "1f441.png", - "unicode": "👁", - "name": "Eye" - }, - "🥀": { - "style": "unicode", - "image": "1f940.png", - "name": "Wilted Flower" - }, - ":rowboat-tone1:": { - "style": "github", - "image": "1f6a3-1f3fb.png", - "unicode": "🚣🏻", - "name": "Rowboat - Tone 1" - }, - ":flag_st:": { - "style": "github", - "image": "1f1f8-1f1f9.png", - "unicode": "🇸🇹", - "name": "São Tomé And Príncipe" - }, - ":new_moon_with_face:": { - "style": "github", - "image": "1f31a.png", - "unicode": "🌚", - "name": "New Moon With Face" - }, - ":bride-with-veil-tone4:": { - "style": "github", - "image": "1f470-1f3fe.png", - "unicode": "👰🏾", - "name": "Bride With Veil - Tone 4" - }, - ":cricket_bat_ball:": { - "style": "github", - "image": "1f3cf.png", - "unicode": "🏏", - "name": "Cricket Bat And Ball" - }, - ":flag-br:": { - "style": "github", - "image": "1f1e7-1f1f7.png", - "unicode": "🇧🇷", - "name": "Brazil" - }, - ":six_pointed_star:": { - "style": "github", - "image": "1f52f.png", - "unicode": "🔯", - "name": "Six Pointed Star With Middle Dot" - }, - ":ma:": { - "style": "github", - "image": "1f1f2-1f1e6.png", - "unicode": "🇲🇦", - "name": "Morocco" - }, - ":clock9:": { - "style": "github", - "image": "1f558.png", - "unicode": "🕘", - "name": "Clock Face Nine Oclock" - }, - ":wave-tone2:": { - "style": "github", - "image": "1f44b-1f3fc.png", - "unicode": "👋🏼", - "name": "Waving Hand Sign - Tone 2" - }, - "*\\O/*": { - "style": "ascii", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - "☕": { - "style": "unicode", - "image": "2615.png", - "name": "Hot Beverage" - }, - "⚪": { - "style": "unicode", - "image": "26aa.png", - "name": "Medium White Circle" - }, - ":basketball_player_tone1:": { - "style": "github", - "image": "26f9-1f3fb.png", - "unicode": "⛹🏻", - "name": "Person With Ball - Tone 1" - }, - ":flag_ke:": { - "style": "github", - "image": "1f1f0-1f1ea.png", - "unicode": "🇰🇪", - "name": "Kenya" - }, - ":bq:": { - "style": "github", - "image": "1f1e7-1f1f6.png", - "unicode": "🇧🇶", - "name": "Caribbean Netherlands" - }, - ":flag_uz:": { - "style": "github", - "image": "1f1fa-1f1ff.png", - "unicode": "🇺🇿", - "name": "Uzbekistan" - }, - ":articulated_lorry:": { - "style": "github", - "image": "1f69b.png", - "unicode": "🚛", - "name": "Articulated Lorry" - }, - ":left_fist_tone4:": { - "style": "github", - "image": "1f91b-1f3fe.png", - "unicode": "🤛🏾", - "name": "Left Facing Fist - Tone 4" - }, - ":tv:": { - "style": "github", - "image": "1f4fa.png", - "unicode": "📺", - "name": "Television" - }, - ":selfie_tone2:": { - "style": "github", - "image": "1f933-1f3fc.png", - "unicode": "🤳🏼", - "name": "Selfie - Tone 2" - }, - ":keycap_ten:": { - "style": "github", - "image": "1f51f.png", - "unicode": "🔟", - "name": "Keycap Ten" - }, - ":cop_tone1:": { - "style": "github", - "image": "1f46e-1f3fb.png", - "unicode": "👮🏻", - "name": "Police Officer - Tone 1" - }, - "👓": { - "style": "unicode", - "image": "1f453.png", - "name": "Eyeglasses" - }, - "🇰": { - "style": "unicode", - "image": "1f1f0.png", - "name": "Regional Indicator Symbol Letter K" - }, - ":flag_la:": { - "style": "github", - "image": "1f1f1-1f1e6.png", - "unicode": "🇱🇦", - "name": "Laos" - }, - ":baby-symbol:": { - "style": "github", - "image": "1f6bc.png", - "unicode": "🚼", - "name": "Baby Symbol" - }, - "👩🏻": { - "style": "unicode", - "image": "1f469-1f3fb.png", - "name": "Woman - Tone 1" - }, - "👩🏽": { - "style": "unicode", - "image": "1f469-1f3fd.png", - "name": "Woman - Tone 3" - }, - "👩🏼": { - "style": "unicode", - "image": "1f469-1f3fc.png", - "name": "Woman - Tone 2" - }, - "👩🏿": { - "style": "unicode", - "image": "1f469-1f3ff.png", - "name": "Woman - Tone 5" - }, - "👩🏾": { - "style": "unicode", - "image": "1f469-1f3fe.png", - "name": "Woman - Tone 4" - }, - ":nigeria:": { - "style": "github", - "image": "1f1f3-1f1ec.png", - "unicode": "🇳🇬", - "name": "Nigeria" - }, - "⚓": { - "style": "unicode", - "image": "2693.png", - "name": "Anchor" - }, - ":flag-pk:": { - "style": "github", - "image": "1f1f5-1f1f0.png", - "unicode": "🇵🇰", - "name": "Pakistan" - }, - ":spiral_note_pad:": { - "style": "github", - "image": "1f5d2.png", - "unicode": "🗒", - "name": "Spiral Note Pad" - }, - "💇🏿": { - "style": "unicode", - "image": "1f487-1f3ff.png", - "name": "Haircut - Tone 5" - }, - "💇🏾": { - "style": "unicode", - "image": "1f487-1f3fe.png", - "name": "Haircut - Tone 4" - }, - ":cop-tone2:": { - "style": "github", - "image": "1f46e-1f3fc.png", - "unicode": "👮🏼", - "name": "Police Officer - Tone 2" - }, - "💇🏼": { - "style": "unicode", - "image": "1f487-1f3fc.png", - "name": "Haircut - Tone 2" - }, - ":sunny:": { - "style": "github", - "image": "2600.png", - "unicode": "☀", - "name": "Black Sun With Rays" - }, - "🚳": { - "style": "unicode", - "image": "1f6b3.png", - "name": "No Bicycles" - }, - ":guardsman-tone1:": { - "style": "github", - "image": "1f482-1f3fb.png", - "unicode": "💂🏻", - "name": "Guardsman - Tone 1" - }, - "😵": { - "style": "unicode", - "ascii": "#-)", - "image": "1f635.png", - "name": "Dizzy Face" - }, - ":black_large_square:": { - "style": "github", - "image": "2b1b.png", - "unicode": "⬛", - "name": "Black Large Square" - }, - ":video-game:": { - "style": "github", - "image": "1f3ae.png", - "unicode": "🎮", - "name": "Video Game" - }, - ":old_key:": { - "style": "github", - "image": "1f5dd.png", - "unicode": "🗝", - "name": "Old Key" - }, - "🙈": { - "style": "unicode", - "image": "1f648.png", - "name": "See-no-evil Monkey" - }, - ":point_right_tone3:": { - "style": "github", - "image": "1f449-1f3fd.png", - "unicode": "👉🏽", - "name": "White Right Pointing Backhand Index - Tone 3" - }, - ":ok-woman-tone4:": { - "style": "github", - "image": "1f646-1f3fe.png", - "unicode": "🙆🏾", - "name": "Face With Ok Gesture Tone4" - }, - ":sparkler:": { - "style": "github", - "image": "1f387.png", - "unicode": "🎇", - "name": "Firework Sparkler" - }, - ":flag-ic:": { - "style": "github", - "image": "1f1ee-1f1e8.png", - "unicode": "🇮🇨", - "name": "Canary Islands" - }, - "☺": { - "style": "unicode", - "image": "263a.png", - "name": "White Smiling Face" - }, - ":waving_white_flag:": { - "style": "github", - "image": "1f3f3.png", - "unicode": "🏳", - "name": "Waving White Flag" - }, - ":level_slider:": { - "style": "github", - "image": "1f39a.png", - "unicode": "🎚", - "name": "Level Slider" - }, - ":convenience-store:": { - "style": "github", - "image": "1f3ea.png", - "unicode": "🏪", - "name": "Convenience Store" - }, - ":arrows-clockwise:": { - "style": "github", - "image": "1f503.png", - "unicode": "🔃", - "name": "Clockwise Downwards And Upwards Open Circle Arrows" - }, - ":older-woman-tone3:": { - "style": "github", - "image": "1f475-1f3fd.png", - "unicode": "👵🏽", - "name": "Older Woman - Tone 3" - }, - "🌱": { - "style": "unicode", - "image": "1f331.png", - "name": "Seedling" - }, - "🔵": { - "style": "unicode", - "image": "1f535.png", - "name": "Large Blue Circle" - }, - "🏊🏼": { - "style": "unicode", - "image": "1f3ca-1f3fc.png", - "name": "Swimmer - Tone 2" - }, - ":handball_tone2:": { - "style": "github", - "image": "1f93e-1f3fc.png", - "unicode": "🤾🏼", - "name": "Handball - Tone 2" - }, - ":fax:": { - "style": "github", - "image": "1f4e0.png", - "unicode": "📠", - "name": "Fax Machine" - }, - "🏆": { - "style": "unicode", - "image": "1f3c6.png", - "name": "Trophy" - }, - ":raised-hand-tone5:": { - "style": "github", - "image": "270b-1f3ff.png", - "unicode": "✋🏿", - "name": "Raised Hand - Tone 5" - }, - "👟": { - "style": "unicode", - "image": "1f45f.png", - "name": "Athletic Shoe" - }, - "📴": { - "style": "unicode", - "image": "1f4f4.png", - "name": "Mobile Phone Off" - }, - ":shield:": { - "style": "github", - "image": "1f6e1.png", - "unicode": "🛡", - "name": "Shield" - }, - ":no-good-tone5:": { - "style": "github", - "image": "1f645-1f3ff.png", - "unicode": "🙅🏿", - "name": "Face With No Good Gesture - Tone 5" - }, - "🚉": { - "style": "unicode", - "image": "1f689.png", - "name": "Station" - }, - ":mountain_bicyclist_tone1:": { - "style": "github", - "image": "1f6b5-1f3fb.png", - "unicode": "🚵🏻", - "name": "Mountain Bicyclist - Tone 1" - }, - ":hammer-pick:": { - "style": "github", - "image": "2692.png", - "unicode": "⚒", - "name": "Hammer And Pick" - }, - "😞": { - "style": "unicode", - "ascii": ">:[", - "image": "1f61e.png", - "name": "Disappointed Face" - }, - ":flag-mg:": { - "style": "github", - "image": "1f1f2-1f1ec.png", - "unicode": "🇲🇬", - "name": "Madagascar" - }, - "✨": { - "style": "unicode", - "image": "2728.png", - "name": "Sparkles" - }, - ":flag-ge:": { - "style": "github", - "image": "1f1ec-1f1ea.png", - "unicode": "🇬🇪", - "name": "Georgia" - }, - ":heavy_check_mark:": { - "style": "github", - "image": "2714.png", - "unicode": "✔", - "name": "Heavy Check Mark" - }, - ":surfer-tone4:": { - "style": "github", - "image": "1f3c4-1f3fe.png", - "unicode": "🏄🏾", - "name": "Surfer - Tone 4" - }, - ":clap_tone1:": { - "style": "github", - "image": "1f44f-1f3fb.png", - "unicode": "👏🏻", - "name": "Clapping Hands Sign - Tone 1" - }, - "\\0/": { - "style": "ascii", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - ":green-apple:": { - "style": "github", - "image": "1f34f.png", - "unicode": "🍏", - "name": "Green Apple" - }, - ":ideograph_advantage:": { - "style": "github", - "image": "1f250.png", - "unicode": "🉐", - "name": "Circled Ideograph Advantage" - }, - ":revolving-hearts:": { - "style": "github", - "image": "1f49e.png", - "unicode": "💞", - "name": "Revolving Hearts" - }, - ":scroll:": { - "style": "github", - "image": "1f4dc.png", - "unicode": "📜", - "name": "Scroll" - }, - ":flag_fr:": { - "style": "github", - "image": "1f1eb-1f1f7.png", - "unicode": "🇫🇷", - "name": "France" - }, - "🔼": { - "style": "unicode", - "image": "1f53c.png", - "name": "Up-pointing Small Red Triangle" - }, - ":pray_tone5:": { - "style": "github", - "image": "1f64f-1f3ff.png", - "unicode": "🙏🏿", - "name": "Person With Folded Hands - Tone 5" - }, - ":pick:": { - "style": "github", - "image": "26cf.png", - "unicode": "⛏", - "name": "Pick" - }, - ":motorboat:": { - "style": "github", - "image": "1f6e5.png", - "unicode": "🛥", - "name": "Motorboat" - }, - ":nut-and-bolt:": { - "style": "github", - "image": "1f529.png", - "unicode": "🔩", - "name": "Nut And Bolt" - }, - "☝🏻": { - "style": "unicode", - "image": "261d-1f3fb.png", - "name": "White Up Pointing Index - Tone 1" - }, - ":flag_gr:": { - "style": "github", - "image": "1f1ec-1f1f7.png", - "unicode": "🇬🇷", - "name": "Greece" - }, - ":park:": { - "style": "github", - "image": "1f3de.png", - "unicode": "🏞", - "name": "National Park" - }, - ":flag-fi:": { - "style": "github", - "image": "1f1eb-1f1ee.png", - "unicode": "🇫🇮", - "name": "Finland" - }, - "🐵": { - "style": "unicode", - "image": "1f435.png", - "name": "Monkey Face" - }, - ";-)": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ";-(": { - "style": "ascii", - "ascii": ":'(", - "image": "1f622.png", - "unicode": "😢", - "name": "Crying Face" - }, - ";-]": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - "📊": { - "style": "unicode", - "image": "1f4ca.png", - "name": "Bar Chart" - }, - ":first-quarter-moon-with-face:": { - "style": "github", - "image": "1f31b.png", - "unicode": "🌛", - "name": "First Quarter Moon With Face" - }, - ":person-with-blond-hair-tone2:": { - "style": "github", - "image": "1f471-1f3fc.png", - "unicode": "👱🏼", - "name": "Person With Blond Hair - Tone 2" - }, - ":mrs_claus_tone5:": { - "style": "github", - "image": "1f936-1f3ff.png", - "unicode": "🤶🏿", - "name": "Mother Christmas - Tone 5" - }, - ":icecream:": { - "style": "github", - "image": "1f366.png", - "unicode": "🍦", - "name": "Soft Ice Cream" - }, - "🥗": { - "style": "unicode", - "image": "1f957.png", - "name": "Green Salad" - }, - "🍛": { - "style": "unicode", - "image": "1f35b.png", - "name": "Curry And Rice" - }, - "🕟": { - "style": "unicode", - "image": "1f55f.png", - "name": "Clock Face Four-thirty" - }, - ":wrestlers_tone4:": { - "style": "github", - "image": "1f93c-1f3fe.png", - "unicode": "🤼🏾", - "name": "Wrestlers - Tone 4" - }, - "🏰": { - "style": "unicode", - "image": "1f3f0.png", - "name": "European Castle" - }, - ":regional-indicator-v:": { - "style": "github", - "image": "1f1fb.png", - "unicode": "🇻", - "name": "Regional Indicator Symbol Letter V" - }, - "=p": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - "=x": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":arrow-up-down:": { - "style": "github", - "image": "2195.png", - "unicode": "↕", - "name": "Up Down Arrow" - }, - ":clap-tone5:": { - "style": "github", - "image": "1f44f-1f3ff.png", - "unicode": "👏🏿", - "name": "Clapping Hands Sign - Tone 5" - }, - ":flag_ag:": { - "style": "github", - "image": "1f1e6-1f1ec.png", - "unicode": "🇦🇬", - "name": "Antigua And Barbuda" - }, - "=P": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":point_down_tone4:": { - "style": "github", - "image": "1f447-1f3fe.png", - "unicode": "👇🏾", - "name": "White Down Pointing Backhand Index - Tone 4" - }, - "=]": { - "style": "ascii", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - "=\\": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - "=X": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - "=D": { - "style": "ascii", - "ascii": ":D", - "image": "1f603.png", - "unicode": "😃", - "name": "Smiling Face With Open Mouth" - }, - ":point_up_tone5:": { - "style": "github", - "image": "261d-1f3ff.png", - "unicode": "☝🏿", - "name": "White Up Pointing Index - Tone 5" - }, - ":pw:": { - "style": "github", - "image": "1f1f5-1f1fc.png", - "unicode": "🇵🇼", - "name": "Palau" - }, - ":juggling-tone4:": { - "style": "github", - "image": "1f939-1f3fe.png", - "unicode": "🤹🏾", - "name": "Juggling - Tone 4" - }, - ":flag-zm:": { - "style": "github", - "image": "1f1ff-1f1f2.png", - "unicode": "🇿🇲", - "name": "Zambia" - }, - "=$": { - "style": "ascii", - "ascii": ":$", - "image": "1f633.png", - "unicode": "😳", - "name": "Flushed Face" - }, - ":person_with_pouting_face_tone5:": { - "style": "github", - "image": "1f64e-1f3ff.png", - "unicode": "🙎🏿", - "name": "Person With Pouting Face Tone5" - }, - "=#": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - "=/": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - "=)": { - "style": "ascii", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - "=(": { - "style": "ascii", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - "=*": { - "style": "ascii", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":battery:": { - "style": "github", - "image": "1f50b.png", - "unicode": "🔋", - "name": "Battery" - }, - "🍨": { - "style": "unicode", - "image": "1f368.png", - "name": "Ice Cream" - }, - ":butterfly:": { - "style": "github", - "image": "1f98b.png", - "unicode": "🦋", - "name": "Butterfly" - }, - ":angel-tone3:": { - "style": "github", - "image": "1f47c-1f3fd.png", - "unicode": "👼🏽", - "name": "Baby Angel - Tone 3" - }, - "😇": { - "style": "unicode", - "ascii": "O:-)", - "image": "1f607.png", - "name": "Smiling Face With Halo" - }, - ":saudiarabia:": { - "style": "github", - "image": "1f1f8-1f1e6.png", - "unicode": "🇸🇦", - "name": "Saudi Arabia" - }, - ":nz:": { - "style": "github", - "image": "1f1f3-1f1ff.png", - "unicode": "🇳🇿", - "name": "New Zealand" - }, - ":flag_as:": { - "style": "github", - "image": "1f1e6-1f1f8.png", - "unicode": "🇦🇸", - "name": "American Samoa" - }, - "🚜": { - "style": "unicode", - "image": "1f69c.png", - "name": "Tractor" - }, - ":regional_indicator_n:": { - "style": "github", - "image": "1f1f3.png", - "unicode": "🇳", - "name": "Regional Indicator Symbol Letter N" - }, - ":boy_tone4:": { - "style": "github", - "image": "1f466-1f3fe.png", - "unicode": "👦🏾", - "name": "Boy - Tone 4" - }, - "🔱": { - "style": "unicode", - "image": "1f531.png", - "name": "Trident Emblem" - }, - "🌵": { - "style": "unicode", - "image": "1f335.png", - "name": "Cactus" - }, - ":juggling_tone3:": { - "style": "github", - "image": "1f939-1f3fd.png", - "unicode": "🤹🏽", - "name": "Juggling - Tone 3" - }, - ":bicyclist_tone1:": { - "style": "github", - "image": "1f6b4-1f3fb.png", - "unicode": "🚴🏻", - "name": "Bicyclist - Tone 1" - }, - ":flag-eh:": { - "style": "github", - "image": "1f1ea-1f1ed.png", - "unicode": "🇪🇭", - "name": "Western Sahara" - }, - "🏊": { - "style": "unicode", - "image": "1f3ca.png", - "name": "Swimmer" - }, - ":ok-hand:": { - "style": "github", - "image": "1f44c.png", - "unicode": "👌", - "name": "Ok Hand Sign" - }, - "👛": { - "style": "unicode", - "image": "1f45b.png", - "name": "Purse" - }, - ":boar:": { - "style": "github", - "image": "1f417.png", - "unicode": "🐗", - "name": "Boar" - }, - ":tuxedo_tone5:": { - "style": "github", - "image": "1f935-1f3ff.png", - "unicode": "🤵🏿", - "name": "Man In Tuxedo - Tone 5" - }, - "📰": { - "style": "unicode", - "image": "1f4f0.png", - "name": "Newspaper" - }, - "💇🏽": { - "style": "unicode", - "image": "1f487-1f3fd.png", - "name": "Haircut - Tone 3" - }, - ":bellhop_bell:": { - "style": "github", - "image": "1f6ce.png", - "unicode": "🛎", - "name": "Bellhop Bell" - }, - "💇🏻": { - "style": "unicode", - "image": "1f487-1f3fb.png", - "name": "Haircut - Tone 1" - }, - ":flag-cv:": { - "style": "github", - "image": "1f1e8-1f1fb.png", - "unicode": "🇨🇻", - "name": "Cape Verde" - }, - ":bangbang:": { - "style": "github", - "image": "203c.png", - "unicode": "‼", - "name": "Double Exclamation Mark" - }, - ":bookmark:": { - "style": "github", - "image": "1f516.png", - "unicode": "🔖", - "name": "Bookmark" - }, - "©": { - "style": "unicode", - "image": "00a9.png", - "name": "Copyright Sign" - }, - "👋🏾": { - "style": "unicode", - "image": "1f44b-1f3fe.png", - "name": "Waving Hand Sign - Tone 4" - }, - ":massage:": { - "style": "github", - "image": "1f486.png", - "unicode": "💆", - "name": "Face Massage" - }, - "⚽": { - "style": "unicode", - "image": "26bd.png", - "name": "Soccer Ball" - }, - ":shell:": { - "style": "github", - "image": "1f41a.png", - "unicode": "🐚", - "name": "Spiral Shell" - }, - ":hole:": { - "style": "github", - "image": "1f573.png", - "unicode": "🕳", - "name": "Hole" - }, - ":headphones:": { - "style": "github", - "image": "1f3a7.png", - "unicode": "🎧", - "name": "Headphone" - }, - ":black-square-button:": { - "style": "github", - "image": "1f532.png", - "unicode": "🔲", - "name": "Black Square Button" - }, - ":flag_be:": { - "style": "github", - "image": "1f1e7-1f1ea.png", - "unicode": "🇧🇪", - "name": "Belgium" - }, - ":mailbox-with-no-mail:": { - "style": "github", - "image": "1f4ed.png", - "unicode": "📭", - "name": "Open Mailbox With Lowered Flag" - }, - ":open_hands_tone4:": { - "style": "github", - "image": "1f450-1f3fe.png", - "unicode": "👐🏾", - "name": "Open Hands Sign - Tone 4" - }, - "🐑": { - "style": "unicode", - "image": "1f411.png", - "name": "Sheep" - }, - "♒": { - "style": "unicode", - "image": "2652.png", - "name": "Aquarius" - }, - ":movie-camera:": { - "style": "github", - "image": "1f3a5.png", - "unicode": "🎥", - "name": "Movie Camera" - }, - ":us:": { - "style": "github", - "image": "1f1fa-1f1f8.png", - "unicode": "🇺🇸", - "name": "United States" - }, - ":poop:": { - "style": "github", - "image": "1f4a9.png", - "unicode": "💩", - "name": "Pile Of Poo" - }, - ":no-mouth:": { - "style": "github", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":thunder-cloud-rain:": { - "style": "github", - "image": "26c8.png", - "unicode": "⛈", - "name": "Thunder Cloud And Rain" - }, - ":metal_tone5:": { - "style": "github", - "image": "1f918-1f3ff.png", - "unicode": "🤘🏿", - "name": "Sign Of The Horns - Tone 5" - }, - "x-p": { - "style": "ascii", - "ascii": ">:P", - "image": "1f61c.png", - "unicode": "😜", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - ":ferris-wheel:": { - "style": "github", - "image": "1f3a1.png", - "unicode": "🎡", - "name": "Ferris Wheel" - }, - ":vhs:": { - "style": "github", - "image": "1f4fc.png", - "unicode": "📼", - "name": "Videocassette" - }, - ":fist_tone1:": { - "style": "github", - "image": "270a-1f3fb.png", - "unicode": "✊🏻", - "name": "Raised Fist - Tone 1" - }, - ":construction-worker-tone5:": { - "style": "github", - "image": "1f477-1f3ff.png", - "unicode": "👷🏿", - "name": "Construction Worker - Tone 5" - }, - ":thumbsup_tone4:": { - "style": "github", - "image": "1f44d-1f3fe.png", - "unicode": "👍🏾", - "name": "Thumbs Up Sign - Tone 4" - }, - ":P", - "image": "1f61c.png", - "unicode": "😜", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - ":om_symbol:": { - "style": "github", - "image": "1f549.png", - "unicode": "🕉", - "name": "Om Symbol" - }, - ":knife:": { - "style": "github", - "image": "1f52a.png", - "unicode": "🔪", - "name": "Hocho" - }, - ":roller-coaster:": { - "style": "github", - "image": "1f3a2.png", - "unicode": "🎢", - "name": "Roller Coaster" - }, - ":triangular_ruler:": { - "style": "github", - "image": "1f4d0.png", - "unicode": "📐", - "name": "Triangular Ruler" - }, - ":metal_tone3:": { - "style": "github", - "image": "1f918-1f3fd.png", - "unicode": "🤘🏽", - "name": "Sign Of The Horns - Tone 3" - }, - ":thumbsdown:": { - "style": "github", - "image": "1f44e.png", - "unicode": "👎", - "name": "Thumbs Down Sign" - }, - ":flag-cp:": { - "style": "github", - "image": "1f1e8-1f1f5.png", - "unicode": "🇨🇵", - "name": "Clipperton Island" - }, - "👹": { - "style": "unicode", - "image": "1f479.png", - "name": "Japanese Ogre" - }, - ":broken_heart:": { - "style": "github", - "ascii": ":)": { - "style": "ascii", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ">:(": { - "style": "ascii", - "ascii": ">:(", - "image": "1f620.png", - "unicode": "😠", - "name": "Angry Face" - }, - ">:/": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - "🌼": { - "style": "unicode", - "image": "1f33c.png", - "name": "Blossom" - }, - ":flag-sh:": { - "style": "github", - "image": "1f1f8-1f1ed.png", - "unicode": "🇸🇭", - "name": "Saint Helena" - }, - ":wrestling_tone2:": { - "style": "github", - "image": "1f93c-1f3fc.png", - "unicode": "🤼🏼", - "name": "Wrestlers - Tone 2" - }, - ">:O": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ">:P": { - "style": "ascii", - "ascii": ">:P", - "image": "1f61c.png", - "unicode": "😜", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - ":male_dancer:": { - "style": "github", - "image": "1f57a.png", - "unicode": "🕺", - "name": "Man Dancing" - }, - "=L": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ">:[": { - "style": "ascii", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - ":face_palm_tone1:": { - "style": "github", - "image": "1f926-1f3fb.png", - "unicode": "🤦🏻", - "name": "Face Palm - Tone 1" - }, - ">:\\": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - "🤴🏾": { - "style": "unicode", - "image": "1f934-1f3fe.png", - "name": "Prince - Tone 4" - }, - "🤴🏿": { - "style": "unicode", - "image": "1f934-1f3ff.png", - "name": "Prince - Tone 5" - }, - "🤴🏼": { - "style": "unicode", - "image": "1f934-1f3fc.png", - "name": "Prince - Tone 2" - }, - "🤴🏽": { - "style": "unicode", - "image": "1f934-1f3fd.png", - "name": "Prince - Tone 3" - }, - "🤴🏻": { - "style": "unicode", - "image": "1f934-1f3fb.png", - "name": "Prince - Tone 1" - }, - "⛵": { - "style": "unicode", - "image": "26f5.png", - "name": "Sailboat" - }, - "🇻": { - "style": "unicode", - "image": "1f1fb.png", - "name": "Regional Indicator Symbol Letter V" - }, - ":classical-building:": { - "style": "github", - "image": "1f3db.png", - "unicode": "🏛", - "name": "Classical Building" - }, - "🌗": { - "style": "unicode", - "image": "1f317.png", - "name": "Last Quarter Moon Symbol" - }, - ":floppy-disk:": { - "style": "github", - "image": "1f4be.png", - "unicode": "💾", - "name": "Floppy Disk" - }, - ":hn:": { - "style": "github", - "image": "1f1ed-1f1f3.png", - "unicode": "🇭🇳", - "name": "Honduras" - }, - "🐙": { - "style": "unicode", - "image": "1f419.png", - "name": "Octopus" - }, - ":regional-indicator-x:": { - "style": "github", - "image": "1f1fd.png", - "unicode": "🇽", - "name": "Regional Indicator Symbol Letter X" - }, - ":face_palm:": { - "style": "github", - "image": "1f926.png", - "unicode": "🤦", - "name": "Face Palm" - }, - ":love-letter:": { - "style": "github", - "image": "1f48c.png", - "unicode": "💌", - "name": "Love Letter" - }, - "🐩": { - "style": "unicode", - "image": "1f429.png", - "name": "Poodle" - }, - ":handshake_tone1:": { - "style": "github", - "image": "1f91d-1f3fb.png", - "unicode": "🤝🏻", - "name": "Handshake - Tone 1" - }, - "👂🏽": { - "style": "unicode", - "image": "1f442-1f3fd.png", - "name": "Ear - Tone 3" - }, - "👂🏾": { - "style": "unicode", - "image": "1f442-1f3fe.png", - "name": "Ear - Tone 4" - }, - "👂🏿": { - "style": "unicode", - "image": "1f442-1f3ff.png", - "name": "Ear - Tone 5" - }, - "💾": { - "style": "unicode", - "image": "1f4be.png", - "name": "Floppy Disk" - }, - "🥋": { - "style": "unicode", - "image": "1f94b.png", - "name": "Martial Arts Uniform" - }, - ":juggling-tone2:": { - "style": "github", - "image": "1f939-1f3fc.png", - "unicode": "🤹🏼", - "name": "Juggling - Tone 2" - }, - "🕓": { - "style": "unicode", - "image": "1f553.png", - "name": "Clock Face Four Oclock" - }, - ":koko:": { - "style": "github", - "image": "1f201.png", - "unicode": "🈁", - "name": "Squared Katakana Koko" - }, - ":person_with_pouting_face_tone2:": { - "style": "github", - "image": "1f64e-1f3fc.png", - "unicode": "🙎🏼", - "name": "Person With Pouting Face Tone2" - }, - "🗨": { - "style": "unicode", - "image": "1f5e8.png", - "name": "Left Speech Bubble" - }, - ":satellite_orbital:": { - "style": "github", - "image": "1f6f0.png", - "unicode": "🛰", - "name": "Satellite" - }, - ":middle-finger-tone1:": { - "style": "github", - "image": "1f595-1f3fb.png", - "unicode": "🖕🏻", - "name": "Reversed Hand With Middle Finger Extended - Tone 1" - }, - ":rice_ball:": { - "style": "github", - "image": "1f359.png", - "unicode": "🍙", - "name": "Rice Ball" - }, - ":flag_pe:": { - "style": "github", - "image": "1f1f5-1f1ea.png", - "unicode": "🇵🇪", - "name": "Peru" - }, - "👶🏻": { - "style": "unicode", - "image": "1f476-1f3fb.png", - "name": "Baby - Tone 1" - }, - "👶🏼": { - "style": "unicode", - "image": "1f476-1f3fc.png", - "name": "Baby - Tone 2" - }, - "👶🏽": { - "style": "unicode", - "image": "1f476-1f3fd.png", - "name": "Baby - Tone 3" - }, - "👶🏾": { - "style": "unicode", - "image": "1f476-1f3fe.png", - "name": "Baby - Tone 4" - }, - "👶🏿": { - "style": "unicode", - "image": "1f476-1f3ff.png", - "name": "Baby - Tone 5" - }, - ":flag-zw:": { - "style": "github", - "image": "1f1ff-1f1fc.png", - "unicode": "🇿🇼", - "name": "Zimbabwe" - }, - ":end:": { - "style": "github", - "image": "1f51a.png", - "unicode": "🔚", - "name": "End With Leftwards Arrow Above" - }, - ":rowboat_tone5:": { - "style": "github", - "image": "1f6a3-1f3ff.png", - "unicode": "🚣🏿", - "name": "Rowboat - Tone 5" - }, - ":mouse_three_button:": { - "style": "github", - "image": "1f5b1.png", - "unicode": "🖱", - "name": "Three Button Mouse" - }, - ":regional_indicator_p:": { - "style": "github", - "image": "1f1f5.png", - "unicode": "🇵", - "name": "Regional Indicator Symbol Letter P" - }, - ":track-previous:": { - "style": "github", - "image": "23ee.png", - "unicode": "⏮", - "name": "Black Left-pointing Double Triangle With Vertical Bar" - }, - ":flag_cc:": { - "style": "github", - "image": "1f1e8-1f1e8.png", - "unicode": "🇨🇨", - "name": "Cocos (keeling) Islands" - }, - ":massage-tone4:": { - "style": "github", - "image": "1f486-1f3fe.png", - "unicode": "💆🏾", - "name": "Face Massage - Tone 4" - }, - "🏑": { - "style": "unicode", - "image": "1f3d1.png", - "name": "Field Hockey Stick And Ball" - }, - ":tm:": { - "style": "github", - "image": "2122.png", - "unicode": "™", - "name": "Trade Mark Sign" - }, - ":nail_care_tone2:": { - "style": "github", - "image": "1f485-1f3fc.png", - "unicode": "💅🏼", - "name": "Nail Polish - Tone 2" - }, - "🍦": { - "style": "unicode", - "image": "1f366.png", - "name": "Soft Ice Cream" - }, - ":orthodox-cross:": { - "style": "github", - "image": "2626.png", - "unicode": "☦", - "name": "Orthodox Cross" - }, - ":mountain-bicyclist-tone5:": { - "style": "github", - "image": "1f6b5-1f3ff.png", - "unicode": "🚵🏿", - "name": "Mountain Bicyclist - Tone 5" - }, - "⏱": { - "style": "unicode", - "image": "23f1.png", - "name": "Stopwatch" - }, - ":angel-tone5:": { - "style": "github", - "image": "1f47c-1f3ff.png", - "unicode": "👼🏿", - "name": "Baby Angel - Tone 5" - }, - ":point-left-tone1:": { - "style": "github", - "image": "1f448-1f3fb.png", - "unicode": "👈🏻", - "name": "White Left Pointing Backhand Index - Tone 1" - }, - "📿": { - "style": "unicode", - "image": "1f4ff.png", - "name": "Prayer Beads" - }, - "🌇": { - "style": "unicode", - "image": "1f307.png", - "name": "Sunset Over Buildings" - }, - "💔": { - "style": "unicode", - "ascii": ":[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - ":city_dusk:": { - "style": "github", - "image": "1f306.png", - "unicode": "🌆", - "name": "Cityscape At Dusk" - }, - ":)": { - "style": "ascii", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - ":weight_lifter_tone1:": { - "style": "github", - "image": "1f3cb-1f3fb.png", - "unicode": "🏋🏻", - "name": "Weight Lifter - Tone 1" - }, - ":arrows_counterclockwise:": { - "style": "github", - "image": "1f504.png", - "unicode": "🔄", - "name": "Anticlockwise Downwards And Upwards Open Circle Arrows" - }, - ":chipmunk:": { - "style": "github", - "image": "1f43f.png", - "unicode": "🐿", - "name": "Chipmunk" - }, - ":flag_sb:": { - "style": "github", - "image": "1f1f8-1f1e7.png", - "unicode": "🇸🇧", - "name": "The Solomon Islands" - }, - ":person_frowning_tone5:": { - "style": "github", - "image": "1f64d-1f3ff.png", - "unicode": "🙍🏿", - "name": "Person Frowning - Tone 5" - }, - "⛏": { - "style": "unicode", - "image": "26cf.png", - "name": "Pick" - }, - ":flag_eu:": { - "style": "github", - "image": "1f1ea-1f1fa.png", - "unicode": "🇪🇺", - "name": "European Union" - }, - ":frowning2:": { - "style": "github", - "image": "2639.png", - "unicode": "☹", - "name": "White Frowning Face" - }, - ":dm:": { - "style": "github", - "image": "1f1e9-1f1f2.png", - "unicode": "🇩🇲", - "name": "Dominica" - }, - ":ca:": { - "style": "github", - "image": "1f1e8-1f1e6.png", - "unicode": "🇨🇦", - "name": "Canada" - }, - ":call-me-tone5:": { - "style": "github", - "image": "1f919-1f3ff.png", - "unicode": "🤙🏿", - "name": "Call Me Hand - Tone 5" - }, - ":smirk-cat:": { - "style": "github", - "image": "1f63c.png", - "unicode": "😼", - "name": "Cat Face With Wry Smile" - }, - ":tools:": { - "style": "github", - "image": "1f6e0.png", - "unicode": "🛠", - "name": "Hammer And Wrench" - }, - "🚄": { - "style": "unicode", - "image": "1f684.png", - "name": "High-speed Train" - }, - ":flag-pf:": { - "style": "github", - "image": "1f1f5-1f1eb.png", - "unicode": "🇵🇫", - "name": "French Polynesia" - }, - ":skeleton:": { - "style": "github", - "image": "1f480.png", - "unicode": "💀", - "name": "Skull" - }, - ":double_vertical_bar:": { - "style": "github", - "image": "23f8.png", - "unicode": "⏸", - "name": "Double Vertical Bar" - }, - ":motorway:": { - "style": "github", - "image": "1f6e3.png", - "unicode": "🛣", - "name": "Motorway" - }, - "🔙": { - "style": "unicode", - "image": "1f519.png", - "name": "Back With Leftwards Arrow Above" - }, - ":european-castle:": { - "style": "github", - "image": "1f3f0.png", - "unicode": "🏰", - "name": "European Castle" - }, - ":radio-button:": { - "style": "github", - "image": "1f518.png", - "unicode": "🔘", - "name": "Radio Button" - }, - "👃": { - "style": "unicode", - "image": "1f443.png", - "name": "Nose" - }, - "📘": { - "style": "unicode", - "image": "1f4d8.png", - "name": "Blue Book" - }, - ":pray-tone1:": { - "style": "github", - "image": "1f64f-1f3fb.png", - "unicode": "🙏🏻", - "name": "Person With Folded Hands - Tone 1" - }, - ":flag_wf:": { - "style": "github", - "image": "1f1fc-1f1eb.png", - "unicode": "🇼🇫", - "name": "Wallis And Futuna" - }, - "🍭": { - "style": "unicode", - "image": "1f36d.png", - "name": "Lollipop" - }, - ":bullettrain-side:": { - "style": "github", - "image": "1f684.png", - "unicode": "🚄", - "name": "High-speed Train" - }, - "🐨": { - "style": "unicode", - "image": "1f428.png", - "name": "Koala" - }, - ":ok-woman:": { - "style": "github", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - ":sign_of_the_horns:": { - "style": "github", - "image": "1f918.png", - "unicode": "🤘", - "name": "Sign Of The Horns" - }, - ":motor_scooter:": { - "style": "github", - "image": "1f6f5.png", - "unicode": "🛵", - "name": "Motor Scooter" - }, - "🈂": { - "style": "unicode", - "image": "1f202.png", - "name": "Squared Katakana Sa" - }, - ":ok_woman_tone4:": { - "style": "github", - "image": "1f646-1f3fe.png", - "unicode": "🙆🏾", - "name": "Face With Ok Gesture Tone4" - }, - ":fingers-crossed-tone1:": { - "style": "github", - "image": "1f91e-1f3fb.png", - "unicode": "🤞🏻", - "name": "Hand With Index And Middle Fingers Crossed - Tone 1" - }, - "🎗": { - "style": "unicode", - "image": "1f397.png", - "name": "Reminder Ribbon" - }, - "🇶🇦": { - "style": "unicode", - "image": "1f1f6-1f1e6.png", - "name": "Qatar" - }, - "🌬": { - "style": "unicode", - "image": "1f32c.png", - "name": "Wind Blowing Face" - }, - ":sun_with_face:": { - "style": "github", - "image": "1f31e.png", - "unicode": "🌞", - "name": "Sun With Face" - }, - ":athletic-shoe:": { - "style": "github", - "image": "1f45f.png", - "unicode": "👟", - "name": "Athletic Shoe" - }, - "🤰": { - "style": "unicode", - "image": "1f930.png", - "name": "Pregnant Woman" - }, - ":woman_tone4:": { - "style": "github", - "image": "1f469-1f3fe.png", - "unicode": "👩🏾", - "name": "Woman - Tone 4" - }, - ":vulcan-tone1:": { - "style": "github", - "image": "1f596-1f3fb.png", - "unicode": "🖖🏻", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 1" - }, - "🛅": { - "style": "unicode", - "image": "1f6c5.png", - "name": "Left Luggage" - }, - ":doughnut:": { - "style": "github", - "image": "1f369.png", - "unicode": "🍩", - "name": "Doughnut" - }, - ":handball-tone1:": { - "style": "github", - "image": "1f93e-1f3fb.png", - "unicode": "🤾🏻", - "name": "Handball - Tone 1" - }, - ":selfie-tone4:": { - "style": "github", - "image": "1f933-1f3fe.png", - "unicode": "🤳🏾", - "name": "Selfie - Tone 4" - }, - "❤": { - "style": "unicode", - "ascii": "<3", - "image": "2764.png", - "name": "Heavy Black Heart" - }, - ":sd:": { - "style": "github", - "image": "1f1f8-1f1e9.png", - "unicode": "🇸🇩", - "name": "Sudan" - }, - "🇫": { - "style": "unicode", - "image": "1f1eb.png", - "name": "Regional Indicator Symbol Letter F" - }, - ":speaking_head:": { - "style": "github", - "image": "1f5e3.png", - "unicode": "🗣", - "name": "Speaking Head In Silhouette" - }, - ":flag_dj:": { - "style": "github", - "image": "1f1e9-1f1ef.png", - "unicode": "🇩🇯", - "name": "Djibouti" - }, - ":ng:": { - "style": "github", - "image": "1f196.png", - "unicode": "🆖", - "name": "Squared Ng" - }, - ":older_man_tone1:": { - "style": "github", - "image": "1f474-1f3fb.png", - "unicode": "👴🏻", - "name": "Older Man - Tone 1" - }, - ":fingers_crossed_tone2:": { - "style": "github", - "image": "1f91e-1f3fc.png", - "unicode": "🤞🏼", - "name": "Hand With Index And Middle Fingers Crossed - Tone 2" - }, - ":writing-hand-tone1:": { - "style": "github", - "image": "270d-1f3fb.png", - "unicode": "✍🏻", - "name": "Writing Hand - Tone 1" - }, - "💮": { - "style": "unicode", - "image": "1f4ae.png", - "name": "White Flower" - }, - ":nine:": { - "style": "github", - "image": "0039-20e3.png", - "unicode": "9⃣", - "name": "Keycap Digit Nine" - }, - ":comet:": { - "style": "github", - "image": "2604.png", - "unicode": "☄", - "name": "Comet" - }, - ":flag-tj:": { - "style": "github", - "image": "1f1f9-1f1ef.png", - "unicode": "🇹🇯", - "name": "Tajikistan" - }, - ":skull_crossbones:": { - "style": "github", - "image": "2620.png", - "unicode": "☠", - "name": "Skull And Crossbones" - }, - "🇦🇱": { - "style": "unicode", - "image": "1f1e6-1f1f1.png", - "name": "Albania" - }, - "🇦🇲": { - "style": "unicode", - "image": "1f1e6-1f1f2.png", - "name": "Armenia" - }, - "🇦🇴": { - "style": "unicode", - "image": "1f1e6-1f1f4.png", - "name": "Angola" - }, - "🇦🇶": { - "style": "unicode", - "image": "1f1e6-1f1f6.png", - "name": "Antarctica" - }, - "🇦🇷": { - "style": "unicode", - "image": "1f1e6-1f1f7.png", - "name": "Argentina" - }, - "🇦🇸": { - "style": "unicode", - "image": "1f1e6-1f1f8.png", - "name": "American Samoa" - }, - "🇦🇹": { - "style": "unicode", - "image": "1f1e6-1f1f9.png", - "name": "Austria" - }, - "🇦🇺": { - "style": "unicode", - "image": "1f1e6-1f1fa.png", - "name": "Australia" - }, - "🇦🇼": { - "style": "unicode", - "image": "1f1e6-1f1fc.png", - "name": "Aruba" - }, - "🇦🇽": { - "style": "unicode", - "image": "1f1e6-1f1fd.png", - "name": "Åland Islands" - }, - "🇦🇿": { - "style": "unicode", - "image": "1f1e6-1f1ff.png", - "name": "Azerbaijan" - }, - "🇦🇨": { - "style": "unicode", - "image": "1f1e6-1f1e8.png", - "name": "Ascension" - }, - "🇦🇩": { - "style": "unicode", - "image": "1f1e6-1f1e9.png", - "name": "Andorra" - }, - "🇦🇪": { - "style": "unicode", - "image": "1f1e6-1f1ea.png", - "name": "The United Arab Emirates" - }, - "🇦🇫": { - "style": "unicode", - "image": "1f1e6-1f1eb.png", - "name": "Afghanistan" - }, - "🇦🇬": { - "style": "unicode", - "image": "1f1e6-1f1ec.png", - "name": "Antigua And Barbuda" - }, - "🇦🇮": { - "style": "unicode", - "image": "1f1e6-1f1ee.png", - "name": "Anguilla" - }, - ":flag-uz:": { - "style": "github", - "image": "1f1fa-1f1ff.png", - "unicode": "🇺🇿", - "name": "Uzbekistan" - }, - ":low-brightness:": { - "style": "github", - "image": "1f505.png", - "unicode": "🔅", - "name": "Low Brightness Symbol" - }, - ":blowfish:": { - "style": "github", - "image": "1f421.png", - "unicode": "🐡", - "name": "Blowfish" - }, - ":printer:": { - "style": "github", - "image": "1f5a8.png", - "unicode": "🖨", - "name": "Printer" - }, - ":white_square_button:": { - "style": "github", - "image": "1f533.png", - "unicode": "🔳", - "name": "White Square Button" - }, - ":ab:": { - "style": "github", - "image": "1f18e.png", - "unicode": "🆎", - "name": "Negative Squared Ab" - }, - ":angel:": { - "style": "github", - "image": "1f47c.png", - "unicode": "👼", - "name": "Baby Angel" - }, - ":flag-sd:": { - "style": "github", - "image": "1f1f8-1f1e9.png", - "unicode": "🇸🇩", - "name": "Sudan" - }, - ":eagle:": { - "style": "github", - "image": "1f985.png", - "unicode": "🦅", - "name": "Eagle" - }, - ":no-mobile-phones:": { - "style": "github", - "image": "1f4f5.png", - "unicode": "📵", - "name": "No Mobile Phones" - }, - ":bridge-at-night:": { - "style": "github", - "image": "1f309.png", - "unicode": "🌉", - "name": "Bridge At Night" - }, - "🏁": { - "style": "unicode", - "image": "1f3c1.png", - "name": "Chequered Flag" - }, - ":ir:": { - "style": "github", - "image": "1f1ee-1f1f7.png", - "unicode": "🇮🇷", - "name": "Iran" - }, - "🍖": { - "style": "unicode", - "image": "1f356.png", - "name": "Meat On Bone" - }, - "🕚": { - "style": "unicode", - "image": "1f55a.png", - "name": "Clock Face Eleven Oclock" - }, - ":joy-cat:": { - "style": "github", - "image": "1f639.png", - "unicode": "😹", - "name": "Cat Face With Tears Of Joy" - }, - "📯": { - "style": "unicode", - "image": "1f4ef.png", - "name": "Postal Horn" - }, - ":D": { - "style": "ascii", - "ascii": ":D", - "image": "1f603.png", - "unicode": "😃", - "name": "Smiling Face With Open Mouth" - }, - ":fallen_leaf:": { - "style": "github", - "image": "1f342.png", - "unicode": "🍂", - "name": "Fallen Leaf" - }, - ":flag-li:": { - "style": "github", - "image": "1f1f1-1f1ee.png", - "unicode": "🇱🇮", - "name": "Liechtenstein" - }, - ":track_next:": { - "style": "github", - "image": "23ed.png", - "unicode": "⏭", - "name": "Black Right-pointing Double Triangle With Vertical Bar" - }, - "💄": { - "style": "unicode", - "image": "1f484.png", - "name": "Lipstick" - }, - ":indonesia:": { - "style": "github", - "image": "1f1ee-1f1e9.png", - "unicode": "🇮🇩", - "name": "Indonesia" - }, - "🚙": { - "style": "unicode", - "image": "1f699.png", - "name": "Recreational Vehicle" - }, - ":flag_sj:": { - "style": "github", - "image": "1f1f8-1f1ef.png", - "unicode": "🇸🇯", - "name": "Svalbard And Jan Mayen" - }, - ":pm:": { - "style": "github", - "image": "1f1f5-1f1f2.png", - "unicode": "🇵🇲", - "name": "Saint Pierre And Miquelon" - }, - "🤝": { - "style": "unicode", - "image": "1f91d.png", - "name": "Handshake" - }, - "☣": { - "style": "unicode", - "image": "2623.png", - "name": "Biohazard Sign" - }, - ":flag_hn:": { - "style": "github", - "image": "1f1ed-1f1f3.png", - "unicode": "🇭🇳", - "name": "Honduras" - }, - ":es:": { - "style": "github", - "image": "1f1ea-1f1f8.png", - "unicode": "🇪🇸", - "name": "Spain" - }, - ":handshake_tone5:": { - "style": "github", - "image": "1f91d-1f3ff.png", - "unicode": "🤝🏿", - "name": "Handshake - Tone 5" - }, - "💁🏻": { - "style": "unicode", - "image": "1f481-1f3fb.png", - "name": "Information Desk Person - Tone 1" - }, - "💁🏽": { - "style": "unicode", - "image": "1f481-1f3fd.png", - "name": "Information Desk Person - Tone 3" - }, - "💁🏼": { - "style": "unicode", - "image": "1f481-1f3fc.png", - "name": "Information Desk Person - Tone 2" - }, - "💁🏿": { - "style": "unicode", - "image": "1f481-1f3ff.png", - "name": "Information Desk Person - Tone 5" - }, - "💁🏾": { - "style": "unicode", - "image": "1f481-1f3fe.png", - "name": "Information Desk Person - Tone 4" - }, - "🙃": { - "style": "unicode", - "image": "1f643.png", - "name": "Upside-down Face" - }, - ":shrug-tone1:": { - "style": "github", - "image": "1f937-1f3fb.png", - "unicode": "🤷🏻", - "name": "Shrug - Tone 1" - }, - ":lying_face:": { - "style": "github", - "image": "1f925.png", - "unicode": "🤥", - "name": "Lying Face" - }, - ":projector:": { - "style": "github", - "image": "1f4fd.png", - "unicode": "📽", - "name": "Film Projector" - }, - ":middle-finger-tone5:": { - "style": "github", - "image": "1f595-1f3ff.png", - "unicode": "🖕🏿", - "name": "Reversed Hand With Middle Finger Extended - Tone 5" - }, - ":snow_capped_mountain:": { - "style": "github", - "image": "1f3d4.png", - "unicode": "🏔", - "name": "Snow Capped Mountain" - }, - ":qa:": { - "style": "github", - "image": "1f1f6-1f1e6.png", - "unicode": "🇶🇦", - "name": "Qatar" - }, - ":regional_indicator_t:": { - "style": "github", - "image": "1f1f9.png", - "unicode": "🇹", - "name": "Regional Indicator Symbol Letter T" - }, - ":vs:": { - "style": "github", - "image": "1f19a.png", - "unicode": "🆚", - "name": "Squared Vs" - }, - "🆗": { - "style": "unicode", - "image": "1f197.png", - "name": "Squared Ok" - }, - ":sweet-potato:": { - "style": "github", - "image": "1f360.png", - "unicode": "🍠", - "name": "Roasted Sweet Potato" - }, - ":speech_left:": { - "style": "github", - "image": "1f5e8.png", - "unicode": "🗨", - "name": "Left Speech Bubble" - }, - ":panda-face:": { - "style": "github", - "image": "1f43c.png", - "unicode": "🐼", - "name": "Panda Face" - }, - ":raised-hands-tone5:": { - "style": "github", - "image": "1f64c-1f3ff.png", - "unicode": "🙌🏿", - "name": "Person Raising Both Hands In Celebration - Tone 5" - }, - "📅": { - "style": "unicode", - "image": "1f4c5.png", - "name": "Calendar" - }, - ":boxing-glove:": { - "style": "github", - "image": "1f94a.png", - "unicode": "🥊", - "name": "Boxing Glove" - }, - "👚": { - "style": "unicode", - "image": "1f45a.png", - "name": "Womans Clothes" - }, - "🇵🇼": { - "style": "unicode", - "image": "1f1f5-1f1fc.png", - "name": "Palau" - }, - "🇵🇾": { - "style": "unicode", - "image": "1f1f5-1f1fe.png", - "name": "Paraguay" - }, - "🇵🇹": { - "style": "unicode", - "image": "1f1f5-1f1f9.png", - "name": "Portugal" - }, - "🇵🇸": { - "style": "unicode", - "image": "1f1f5-1f1f8.png", - "name": "Palestinian Authority" - }, - "🏫": { - "style": "unicode", - "image": "1f3eb.png", - "name": "School" - }, - ":space_invader:": { - "style": "github", - "image": "1f47e.png", - "unicode": "👾", - "name": "Alien Monster" - }, - "🇵🇱": { - "style": "unicode", - "image": "1f1f5-1f1f1.png", - "name": "Poland" - }, - "🇵🇰": { - "style": "unicode", - "image": "1f1f5-1f1f0.png", - "name": "Pakistan" - }, - "🗯": { - "style": "unicode", - "image": "1f5ef.png", - "name": "Right Anger Bubble" - }, - ":man_in_tuxedo_tone5:": { - "style": "github", - "image": "1f935-1f3ff.png", - "unicode": "🤵🏿", - "name": "Man In Tuxedo - Tone 5" - }, - "🇵🇭": { - "style": "unicode", - "image": "1f1f5-1f1ed.png", - "name": "The Philippines" - }, - ":flag_pa:": { - "style": "github", - "image": "1f1f5-1f1e6.png", - "unicode": "🇵🇦", - "name": "Panama" - }, - ":tada:": { - "style": "github", - "image": "1f389.png", - "unicode": "🎉", - "name": "Party Popper" - }, - "🇵🇫": { - "style": "unicode", - "image": "1f1f5-1f1eb.png", - "name": "French Polynesia" - }, - "🇵🇪": { - "style": "unicode", - "image": "1f1f5-1f1ea.png", - "name": "Peru" - }, - "⛹": { - "style": "unicode", - "image": "26f9.png", - "name": "Person With Ball" - }, - ":six:": { - "style": "github", - "image": "0036-20e3.png", - "unicode": "6⃣", - "name": "Keycap Digit Six" - }, - "🇵🇦": { - "style": "unicode", - "image": "1f1f5-1f1e6.png", - "name": "Panama" - }, - "🎀": { - "style": "unicode", - "image": "1f380.png", - "name": "Ribbon" - }, - ":angel_tone4:": { - "style": "github", - "image": "1f47c-1f3fe.png", - "unicode": "👼🏾", - "name": "Baby Angel - Tone 4" - }, - "😙": { - "style": "unicode", - "image": "1f619.png", - "name": "Kissing Face With Smiling Eyes" - }, - ":izakaya-lantern:": { - "style": "github", - "image": "1f3ee.png", - "unicode": "🏮", - "name": "Izakaya Lantern" - }, - ":flag-kh:": { - "style": "github", - "image": "1f1f0-1f1ed.png", - "unicode": "🇰🇭", - "name": "Cambodia" - }, - ":lv:": { - "style": "github", - "image": "1f1f1-1f1fb.png", - "unicode": "🇱🇻", - "name": "Latvia" - }, - "🚮": { - "style": "unicode", - "image": "1f6ae.png", - "name": "Put Litter In Its Place Symbol" - }, - ":flag_cg:": { - "style": "github", - "image": "1f1e8-1f1ec.png", - "unicode": "🇨🇬", - "name": "The Republic Of The Congo" - }, - ":nail-care-tone2:": { - "style": "github", - "image": "1f485-1f3fc.png", - "unicode": "💅🏼", - "name": "Nail Polish - Tone 2" - }, - "🥇": { - "style": "unicode", - "image": "1f947.png", - "name": "First Place Medal" - }, - ":runner-tone2:": { - "style": "github", - "image": "1f3c3-1f3fc.png", - "unicode": "🏃🏼", - "name": "Runner - Tone 2" - }, - ":santa_tone4:": { - "style": "github", - "image": "1f385-1f3fe.png", - "unicode": "🎅🏾", - "name": "Father Christmas - Tone 4" - }, - ":fist-tone3:": { - "style": "github", - "image": "270a-1f3fd.png", - "unicode": "✊🏽", - "name": "Raised Fist - Tone 3" - }, - ":stuffed_flatbread:": { - "style": "github", - "image": "1f959.png", - "unicode": "🥙", - "name": "Stuffed Flatbread" - }, - ":person_with_pouting_face_tone3:": { - "style": "github", - "image": "1f64e-1f3fd.png", - "unicode": "🙎🏽", - "name": "Person With Pouting Face Tone3" - }, - ":flag-ir:": { - "style": "github", - "image": "1f1ee-1f1f7.png", - "unicode": "🇮🇷", - "name": "Iran" - }, - ":flag-ch:": { - "style": "github", - "image": "1f1e8-1f1ed.png", - "unicode": "🇨🇭", - "name": "Switzerland" - }, - ":princess_tone1:": { - "style": "github", - "image": "1f478-1f3fb.png", - "unicode": "👸🏻", - "name": "Princess - Tone 1" - }, - ":clapper:": { - "style": "github", - "image": "1f3ac.png", - "unicode": "🎬", - "name": "Clapper Board" - }, - ":hand-splayed-tone4:": { - "style": "github", - "image": "1f590-1f3fe.png", - "unicode": "🖐🏾", - "name": "Raised Hand With Fingers Splayed - Tone 4" - }, - "🚗": { - "style": "unicode", - "image": "1f697.png", - "name": "Automobile" - }, - ":8ball:": { - "style": "github", - "image": "1f3b1.png", - "unicode": "🎱", - "name": "Billiards" - }, - ":flag_tm:": { - "style": "github", - "image": "1f1f9-1f1f2.png", - "unicode": "🇹🇲", - "name": "Turkmenistan" - }, - "😬": { - "style": "unicode", - "image": "1f62c.png", - "name": "Grimacing Face" - }, - "🥜": { - "style": "unicode", - "image": "1f95c.png", - "name": "Peanuts" - }, - ":kissing_closed_eyes:": { - "style": "github", - "image": "1f61a.png", - "unicode": "😚", - "name": "Kissing Face With Closed Eyes" - }, - ":person_with_ball:": { - "style": "github", - "image": "26f9.png", - "unicode": "⛹", - "name": "Person With Ball" - }, - "🏅": { - "style": "unicode", - "image": "1f3c5.png", - "name": "Sports Medal" - }, - ":v-tone4:": { - "style": "github", - "image": "270c-1f3fe.png", - "unicode": "✌🏾", - "name": "Victory Hand - Tone 4" - }, - "🕖": { - "style": "unicode", - "image": "1f556.png", - "name": "Clock Face Seven Oclock" - }, - ":flag-bf:": { - "style": "github", - "image": "1f1e7-1f1eb.png", - "unicode": "🇧🇫", - "name": "Burkina Faso" - }, - ":shopping-cart:": { - "style": "github", - "image": "1f6d2.png", - "unicode": "🛒", - "name": "Shopping Trolley" - }, - "🍚": { - "style": "unicode", - "image": "1f35a.png", - "name": "Cooked Rice" - }, - "🥞": { - "style": "unicode", - "image": "1f95e.png", - "name": "Pancakes" - }, - ":ear-tone1:": { - "style": "github", - "image": "1f442-1f3fb.png", - "unicode": "👂🏻", - "name": "Ear - Tone 1" - }, - ":peanuts:": { - "style": "github", - "image": "1f95c.png", - "unicode": "🥜", - "name": "Peanuts" - }, - "📫": { - "style": "unicode", - "image": "1f4eb.png", - "name": "Closed Mailbox With Raised Flag" - }, - ":gorilla:": { - "style": "github", - "image": "1f98d.png", - "unicode": "🦍", - "name": "Gorilla" - }, - ":-Þ": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":whisky:": { - "style": "github", - "image": "1f943.png", - "unicode": "🥃", - "name": "Tumbler Glass" - }, - "🇵🇷": { - "style": "unicode", - "image": "1f1f5-1f1f7.png", - "name": "Puerto Rico" - }, - "💀": { - "style": "unicode", - "image": "1f480.png", - "name": "Skull" - }, - ":fries:": { - "style": "github", - "image": "1f35f.png", - "unicode": "🍟", - "name": "French Fries" - }, - ":juggler_tone2:": { - "style": "github", - "image": "1f939-1f3fc.png", - "unicode": "🤹🏼", - "name": "Juggling - Tone 2" - }, - "🇵🇳": { - "style": "unicode", - "image": "1f1f5-1f1f3.png", - "name": "Pitcairn" - }, - ":back_of_hand_tone2:": { - "style": "github", - "image": "1f91a-1f3fc.png", - "unicode": "🤚🏼", - "name": "Raised Back Of Hand - Tone 2" - }, - "🇵🇲": { - "style": "unicode", - "image": "1f1f5-1f1f2.png", - "name": "Saint Pierre And Miquelon" - }, - ":bathtub:": { - "style": "github", - "image": "1f6c1.png", - "unicode": "🛁", - "name": "Bathtub" - }, - ":flag-gt:": { - "style": "github", - "image": "1f1ec-1f1f9.png", - "unicode": "🇬🇹", - "name": "Guatemala" - }, - "🇵🇬": { - "style": "unicode", - "image": "1f1f5-1f1ec.png", - "name": "Papua New Guinea" - }, - ":bath-tone1:": { - "style": "github", - "image": "1f6c0-1f3fb.png", - "unicode": "🛀🏻", - "name": "Bath - Tone 1" - }, - ":handshake:": { - "style": "github", - "image": "1f91d.png", - "unicode": "🤝", - "name": "Handshake" - }, - ":older-man:": { - "style": "github", - "image": "1f474.png", - "unicode": "👴", - "name": "Older Man" - }, - ":chicken:": { - "style": "github", - "image": "1f414.png", - "unicode": "🐔", - "name": "Chicken" - }, - ":taurus:": { - "style": "github", - "image": "2649.png", - "unicode": "♉", - "name": "Taurus" - }, - ":flag-nz:": { - "style": "github", - "image": "1f1f3-1f1ff.png", - "unicode": "🇳🇿", - "name": "New Zealand" - }, - ":scorpion:": { - "style": "github", - "image": "1f982.png", - "unicode": "🦂", - "name": "Scorpion" - }, - "⭕": { - "style": "unicode", - "image": "2b55.png", - "name": "Heavy Large Circle" - }, - ":arrow-double-down:": { - "style": "github", - "image": "23ec.png", - "unicode": "⏬", - "name": "Black Down-pointing Double Triangle" - }, - ":dk:": { - "style": "github", - "image": "1f1e9-1f1f0.png", - "unicode": "🇩🇰", - "name": "Denmark" - }, - ":hot_dog:": { - "style": "github", - "image": "1f32d.png", - "unicode": "🌭", - "name": "Hot Dog" - }, - ":co:": { - "style": "github", - "image": "1f1e8-1f1f4.png", - "unicode": "🇨🇴", - "name": "Colombia" - }, - ":flag-ug:": { - "style": "github", - "image": "1f1fa-1f1ec.png", - "unicode": "🇺🇬", - "name": "Uganda" - }, - ":call-me-tone3:": { - "style": "github", - "image": "1f919-1f3fd.png", - "unicode": "🤙🏽", - "name": "Call Me Hand - Tone 3" - }, - ":handshake-tone5:": { - "style": "github", - "image": "1f91d-1f3ff.png", - "unicode": "🤝🏿", - "name": "Handshake - Tone 5" - }, - "😂": { - "style": "unicode", - "ascii": ":')", - "image": "1f602.png", - "name": "Face With Tears Of Joy" - }, - ":space-invader:": { - "style": "github", - "image": "1f47e.png", - "unicode": "👾", - "name": "Alien Monster" - }, - ":zzz:": { - "style": "github", - "image": "1f4a4.png", - "unicode": "💤", - "name": "Sleeping Symbol" - }, - "✌": { - "style": "unicode", - "image": "270c.png", - "name": "Victory Hand" - }, - ":nauseated_face:": { - "style": "github", - "image": "1f922.png", - "unicode": "🤢", - "name": "Nauseated Face" - }, - ":small_red_triangle:": { - "style": "github", - "image": "1f53a.png", - "unicode": "🔺", - "name": "Up-pointing Red Triangle" - }, - "🇴🇲": { - "style": "unicode", - "image": "1f1f4-1f1f2.png", - "name": "Oman" - }, - ":nail-care:": { - "style": "github", - "image": "1f485.png", - "unicode": "💅", - "name": "Nail Polish" - }, - "📁": { - "style": "unicode", - "image": "1f4c1.png", - "name": "File Folder" - }, - ":baby_tone5:": { - "style": "github", - "image": "1f476-1f3ff.png", - "unicode": "👶🏿", - "name": "Baby - Tone 5" - }, - ":post_office:": { - "style": "github", - "image": "1f3e3.png", - "unicode": "🏣", - "name": "Japanese Post Office" - }, - "👖": { - "style": "unicode", - "image": "1f456.png", - "name": "Jeans" - }, - ":white-large-square:": { - "style": "github", - "image": "2b1c.png", - "unicode": "⬜", - "name": "White Large Square" - }, - ":pray-tone3:": { - "style": "github", - "image": "1f64f-1f3fd.png", - "unicode": "🙏🏽", - "name": "Person With Folded Hands - Tone 3" - }, - "🏯": { - "style": "unicode", - "image": "1f3ef.png", - "name": "Japanese Castle" - }, - ":basketball_player_tone3:": { - "style": "github", - "image": "26f9-1f3fd.png", - "unicode": "⛹🏽", - "name": "Person With Ball - Tone 3" - }, - ":point-down-tone4:": { - "style": "github", - "image": "1f447-1f3fe.png", - "unicode": "👇🏾", - "name": "White Down Pointing Backhand Index - Tone 4" - }, - "🎄": { - "style": "unicode", - "image": "1f384.png", - "name": "Christmas Tree" - }, - ":ok_woman_tone2:": { - "style": "github", - "image": "1f646-1f3fc.png", - "unicode": "🙆🏼", - "name": "Face With Ok Gesture Tone2" - }, - "🦈": { - "style": "unicode", - "image": "1f988.png", - "name": "Shark" - }, - ":vulcan-tone3:": { - "style": "github", - "image": "1f596-1f3fd.png", - "unicode": "🖖🏽", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 3" - }, - ":clock530:": { - "style": "github", - "image": "1f560.png", - "unicode": "🕠", - "name": "Clock Face Five-thirty" - }, - ":at:": { - "style": "github", - "image": "1f1e6-1f1f9.png", - "unicode": "🇦🇹", - "name": "Austria" - }, - ":clock930:": { - "style": "github", - "image": "1f564.png", - "unicode": "🕤", - "name": "Clock Face Nine-thirty" - }, - ":juggling:": { - "style": "github", - "image": "1f939.png", - "unicode": "🤹", - "name": "Juggling" - }, - "♍": { - "style": "unicode", - "image": "264d.png", - "name": "Virgo" - }, - ":handball-tone3:": { - "style": "github", - "image": "1f93e-1f3fd.png", - "unicode": "🤾🏽", - "name": "Handball - Tone 3" - }, - ":wf:": { - "style": "github", - "image": "1f1fc-1f1eb.png", - "unicode": "🇼🇫", - "name": "Wallis And Futuna" - }, - "👦🏿": { - "style": "unicode", - "image": "1f466-1f3ff.png", - "name": "Boy - Tone 5" - }, - ":taxi:": { - "style": "github", - "image": "1f695.png", - "unicode": "🚕", - "name": "Taxi" - }, - ":two-hearts:": { - "style": "github", - "image": "1f495.png", - "unicode": "💕", - "name": "Two Hearts" - }, - ":sb:": { - "style": "github", - "image": "1f1f8-1f1e7.png", - "unicode": "🇸🇧", - "name": "The Solomon Islands" - }, - ":spy_tone1:": { - "style": "github", - "image": "1f575-1f3fb.png", - "unicode": "🕵🏻", - "name": "Sleuth Or Spy - Tone 1" - }, - ":mahjong:": { - "style": "github", - "image": "1f004.png", - "unicode": "🀄", - "name": "Mahjong Tile Red Dragon" - }, - ":file-folder:": { - "style": "github", - "image": "1f4c1.png", - "unicode": "📁", - "name": "File Folder" - }, - "🔂": { - "style": "unicode", - "image": "1f502.png", - "name": "Clockwise Rightwards And Leftwards Open Circle Arrows With Circled One Overlay" - }, - ":ni:": { - "style": "github", - "image": "1f1f3-1f1ee.png", - "unicode": "🇳🇮", - "name": "Nicaragua" - }, - ":facepalm_tone1:": { - "style": "github", - "image": "1f926-1f3fb.png", - "unicode": "🤦🏻", - "name": "Face Palm - Tone 1" - }, - ":older_man_tone3:": { - "style": "github", - "image": "1f474-1f3fd.png", - "unicode": "👴🏽", - "name": "Older Man - Tone 3" - }, - ":fire-engine:": { - "style": "github", - "image": "1f692.png", - "unicode": "🚒", - "name": "Fire Engine" - }, - "💗": { - "style": "unicode", - "image": "1f497.png", - "name": "Growing Heart" - }, - ":cityscape:": { - "style": "github", - "image": "1f3d9.png", - "unicode": "🏙", - "name": "Cityscape" - }, - ":face-palm-tone5:": { - "style": "github", - "image": "1f926-1f3ff.png", - "unicode": "🤦🏿", - "name": "Face Palm - Tone 5" - }, - ":bento:": { - "style": "github", - "image": "1f371.png", - "unicode": "🍱", - "name": "Bento Box" - }, - "➡": { - "style": "unicode", - "image": "27a1.png", - "name": "Black Rightwards Arrow" - }, - "🐬": { - "style": "unicode", - "image": "1f42c.png", - "name": "Dolphin" - }, - "O:)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - "O:3": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":flag-tl:": { - "style": "github", - "image": "1f1f9-1f1f1.png", - "unicode": "🇹🇱", - "name": "Timor-leste" - }, - "🤸🏻": { - "style": "unicode", - "image": "1f938-1f3fb.png", - "name": "Person Doing Cartwheel - Tone 1" - }, - "🤸🏾": { - "style": "unicode", - "image": "1f938-1f3fe.png", - "name": "Person Doing Cartwheel - Tone 4" - }, - "🤸🏿": { - "style": "unicode", - "image": "1f938-1f3ff.png", - "name": "Person Doing Cartwheel - Tone 5" - }, - "🤸🏼": { - "style": "unicode", - "image": "1f938-1f3fc.png", - "name": "Person Doing Cartwheel - Tone 2" - }, - "🤸🏽": { - "style": "unicode", - "image": "1f938-1f3fd.png", - "name": "Person Doing Cartwheel - Tone 3" - }, - ":ribbon:": { - "style": "github", - "image": "1f380.png", - "unicode": "🎀", - "name": "Ribbon" - }, - "♠": { - "style": "unicode", - "image": "2660.png", - "name": "Black Spade Suit" - }, - "🛫": { - "style": "unicode", - "image": "1f6eb.png", - "name": "Airplane Departure" - }, - ":curry:": { - "style": "github", - "image": "1f35b.png", - "unicode": "🍛", - "name": "Curry And Rice" - }, - "⏹": { - "style": "unicode", - "image": "23f9.png", - "name": "Black Square For Stop" - }, - "🚀": { - "style": "unicode", - "image": "1f680.png", - "name": "Rocket" - }, - ":kr:": { - "style": "github", - "image": "1f1f0-1f1f7.png", - "unicode": "🇰🇷", - "name": "Korea" - }, - "🌙": { - "style": "unicode", - "image": "1f319.png", - "name": "Crescent Moon" - }, - ":arrow_down_small:": { - "style": "github", - "image": "1f53d.png", - "unicode": "🔽", - "name": "Down-pointing Small Red Triangle" - }, - ":vulcan_tone4:": { - "style": "github", - "image": "1f596-1f3fe.png", - "unicode": "🖖🏾", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 4" - }, - ":helmet-with-cross:": { - "style": "github", - "image": "26d1.png", - "unicode": "⛑", - "name": "Helmet With White Cross" - }, - "🎮": { - "style": "unicode", - "image": "1f3ae.png", - "name": "Video Game" - }, - ":writing_hand_tone5:": { - "style": "github", - "image": "270d-1f3ff.png", - "unicode": "✍🏿", - "name": "Writing Hand - Tone 5" - }, - ":tickets:": { - "style": "github", - "image": "1f39f.png", - "unicode": "🎟", - "name": "Admission Tickets" - }, - ":fleur-de-lis:": { - "style": "github", - "image": "269c.png", - "unicode": "⚜", - "name": "Fleur-de-lis" - }, - ":black-large-square:": { - "style": "github", - "image": "2b1b.png", - "unicode": "⬛", - "name": "Black Large Square" - }, - ":basketball-player-tone4:": { - "style": "github", - "image": "26f9-1f3fe.png", - "unicode": "⛹🏾", - "name": "Person With Ball - Tone 4" - }, - ":flag_gf:": { - "style": "github", - "image": "1f1ec-1f1eb.png", - "unicode": "🇬🇫", - "name": "French Guiana" - }, - ":flag-lk:": { - "style": "github", - "image": "1f1f1-1f1f0.png", - "unicode": "🇱🇰", - "name": "Sri Lanka" - }, - ":open_file_folder:": { - "style": "github", - "image": "1f4c2.png", - "unicode": "📂", - "name": "Open File Folder" - }, - ":construction-site:": { - "style": "github", - "image": "1f3d7.png", - "unicode": "🏗", - "name": "Building Construction" - }, - "🐂": { - "style": "unicode", - "image": "1f402.png", - "name": "Ox" - }, - ":bullettrain_side:": { - "style": "github", - "image": "1f684.png", - "unicode": "🚄", - "name": "High-speed Train" - }, - "㊙": { - "style": "unicode", - "image": "3299.png", - "name": "Circled Ideograph Secret" - }, - ":man_in_tuxedo:": { - "style": "github", - "image": "1f935.png", - "unicode": "🤵", - "name": "Man In Tuxedo" - }, - ":slot-machine:": { - "style": "github", - "image": "1f3b0.png", - "unicode": "🎰", - "name": "Slot Machine" - }, - "⚡": { - "style": "unicode", - "image": "26a1.png", - "name": "High Voltage Sign" - }, - ":grandma_tone5:": { - "style": "github", - "image": "1f475-1f3ff.png", - "unicode": "👵🏿", - "name": "Older Woman - Tone 5" - }, - ":flag-dk:": { - "style": "github", - "image": "1f1e9-1f1f0.png", - "unicode": "🇩🇰", - "name": "Denmark" - }, - ":thumbdown_tone5:": { - "style": "github", - "image": "1f44e-1f3ff.png", - "unicode": "👎🏿", - "name": "Thumbs Down Sign - Tone 5" - }, - ":couch:": { - "style": "github", - "image": "1f6cb.png", - "unicode": "🛋", - "name": "Couch And Lamp" - }, - "🤴": { - "style": "unicode", - "image": "1f934.png", - "name": "Prince" - }, - ":flag-ag:": { - "style": "github", - "image": "1f1e6-1f1ec.png", - "unicode": "🇦🇬", - "name": "Antigua And Barbuda" - }, - "🛁": { - "style": "unicode", - "image": "1f6c1.png", - "name": "Bathtub" - }, - ":family-mwbb:": { - "style": "github", - "image": "1f468-1f469-1f466-1f466.png", - "unicode": "👨👩👦👦", - "name": "Family (man,woman,boy,boy)" - }, - ":maple_leaf:": { - "style": "github", - "image": "1f341.png", - "unicode": "🍁", - "name": "Maple Leaf" - }, - ":shrug-tone3:": { - "style": "github", - "image": "1f937-1f3fd.png", - "unicode": "🤷🏽", - "name": "Shrug - Tone 3" - }, - ":clock230:": { - "style": "github", - "image": "1f55d.png", - "unicode": "🕝", - "name": "Clock Face Two-thirty" - }, - ":wine_glass:": { - "style": "github", - "image": "1f377.png", - "unicode": "🍷", - "name": "Wine Glass" - }, - ":speech-balloon:": { - "style": "github", - "image": "1f4ac.png", - "unicode": "💬", - "name": "Speech Balloon" - }, - ":heavy-check-mark:": { - "style": "github", - "image": "2714.png", - "unicode": "✔", - "name": "Heavy Check Mark" - }, - "🇯": { - "style": "unicode", - "image": "1f1ef.png", - "name": "Regional Indicator Symbol Letter J" - }, - ":regional-indicator-b:": { - "style": "github", - "image": "1f1e7.png", - "unicode": "🇧", - "name": "Regional Indicator Symbol Letter B" - }, - ":regional_indicator_z:": { - "style": "github", - "image": "1f1ff.png", - "unicode": "🇿", - "name": "Regional Indicator Symbol Letter Z" - }, - ":vu:": { - "style": "github", - "image": "1f1fb-1f1fa.png", - "unicode": "🇻🇺", - "name": "Vanuatu" - }, - ":atom_symbol:": { - "style": "github", - "image": "269b.png", - "unicode": "⚛", - "name": "Atom Symbol" - }, - ":baby_symbol:": { - "style": "github", - "image": "1f6bc.png", - "unicode": "🚼", - "name": "Baby Symbol" - }, - ":earth-africa:": { - "style": "github", - "image": "1f30d.png", - "unicode": "🌍", - "name": "Earth Globe Europe-africa" - }, - ":flag_kz:": { - "style": "github", - "image": "1f1f0-1f1ff.png", - "unicode": "🇰🇿", - "name": "Kazakhstan" - }, - "💅🏽": { - "style": "unicode", - "image": "1f485-1f3fd.png", - "name": "Nail Polish - Tone 3" - }, - "💅🏼": { - "style": "unicode", - "image": "1f485-1f3fc.png", - "name": "Nail Polish - Tone 2" - }, - "💅🏿": { - "style": "unicode", - "image": "1f485-1f3ff.png", - "name": "Nail Polish - Tone 5" - }, - "💅🏾": { - "style": "unicode", - "image": "1f485-1f3fe.png", - "name": "Nail Polish - Tone 4" - }, - "💅🏻": { - "style": "unicode", - "image": "1f485-1f3fb.png", - "name": "Nail Polish - Tone 1" - }, - ":on:": { - "style": "github", - "image": "1f51b.png", - "unicode": "🔛", - "name": "On With Exclamation Mark With Left Right Arrow Abo" - }, - ":red-circle:": { - "style": "github", - "image": "1f534.png", - "unicode": "🔴", - "name": "Large Red Circle" - }, - ":postbox:": { - "style": "github", - "image": "1f4ee.png", - "unicode": "📮", - "name": "Postbox" - }, - "🍃": { - "style": "unicode", - "image": "1f343.png", - "name": "Leaf Fluttering In Wind" - }, - ":tg:": { - "style": "github", - "image": "1f1f9-1f1ec.png", - "unicode": "🇹🇬", - "name": "Togo" - }, - ":girl_tone2:": { - "style": "github", - "image": "1f467-1f3fc.png", - "unicode": "👧🏼", - "name": "Girl - Tone 2" - }, - "🏘": { - "style": "unicode", - "image": "1f3d8.png", - "name": "House Buildings" - }, - ":arrow_up_small:": { - "style": "github", - "image": "1f53c.png", - "unicode": "🔼", - "name": "Up-pointing Small Red Triangle" - }, - ":blue-heart:": { - "style": "github", - "image": "1f499.png", - "unicode": "💙", - "name": "Blue Heart" - }, - "👭": { - "style": "unicode", - "image": "1f46d.png", - "name": "Two Women Holding Hands" - }, - ":flag_pr:": { - "style": "github", - "image": "1f1f5-1f1f7.png", - "unicode": "🇵🇷", - "name": "Puerto Rico" - }, - ":nail_care_tone4:": { - "style": "github", - "image": "1f485-1f3fe.png", - "unicode": "💅🏾", - "name": "Nail Polish - Tone 4" - }, - ":bride-with-veil-tone3:": { - "style": "github", - "image": "1f470-1f3fd.png", - "unicode": "👰🏽", - "name": "Bride With Veil - Tone 3" - }, - "🇧🇷": { - "style": "unicode", - "image": "1f1e7-1f1f7.png", - "name": "Brazil" - }, - "🇧🇶": { - "style": "unicode", - "image": "1f1e7-1f1f6.png", - "name": "Caribbean Netherlands" - }, - "🇧🇴": { - "style": "unicode", - "image": "1f1e7-1f1f4.png", - "name": "Bolivia" - }, - "🇧🇳": { - "style": "unicode", - "image": "1f1e7-1f1f3.png", - "name": "Brunei" - }, - "🇧🇲": { - "style": "unicode", - "image": "1f1e7-1f1f2.png", - "name": "Bermuda" - }, - "🇧🇱": { - "style": "unicode", - "image": "1f1e7-1f1f1.png", - "name": "Saint Barthélemy" - }, - "🇧🇿": { - "style": "unicode", - "image": "1f1e7-1f1ff.png", - "name": "Belize" - }, - "🇧🇾": { - "style": "unicode", - "image": "1f1e7-1f1fe.png", - "name": "Belarus" - }, - "🇧🇼": { - "style": "unicode", - "image": "1f1e7-1f1fc.png", - "name": "Botswana" - }, - "🇧🇻": { - "style": "unicode", - "image": "1f1e7-1f1fb.png", - "name": "Bouvet Island" - }, - ":soon:": { - "style": "github", - "image": "1f51c.png", - "unicode": "🔜", - "name": "Soon With Rightwards Arrow Above" - }, - "🇧🇹": { - "style": "unicode", - "image": "1f1e7-1f1f9.png", - "name": "Bhutan" - }, - "🇧🇸": { - "style": "unicode", - "image": "1f1e7-1f1f8.png", - "name": "The Bahamas" - }, - "🇧🇧": { - "style": "unicode", - "image": "1f1e7-1f1e7.png", - "name": "Barbados" - }, - "🇧🇦": { - "style": "unicode", - "image": "1f1e7-1f1e6.png", - "name": "Bosnia And Herzegovina" - }, - ":yen:": { - "style": "github", - "image": "1f4b4.png", - "unicode": "💴", - "name": "Banknote With Yen Sign" - }, - ":flag_ml:": { - "style": "github", - "image": "1f1f2-1f1f1.png", - "unicode": "🇲🇱", - "name": "Mali" - }, - "🇧🇯": { - "style": "unicode", - "image": "1f1e7-1f1ef.png", - "name": "Benin" - }, - "🇧🇮": { - "style": "unicode", - "image": "1f1e7-1f1ee.png", - "name": "Burundi" - }, - "🇧🇭": { - "style": "unicode", - "image": "1f1e7-1f1ed.png", - "name": "Bahrain" - }, - "🇧🇬": { - "style": "unicode", - "image": "1f1e7-1f1ec.png", - "name": "Bulgaria" - }, - "🇧🇫": { - "style": "unicode", - "image": "1f1e7-1f1eb.png", - "name": "Burkina Faso" - }, - "🇧🇪": { - "style": "unicode", - "image": "1f1e7-1f1ea.png", - "name": "Belgium" - }, - "🇧🇩": { - "style": "unicode", - "image": "1f1e7-1f1e9.png", - "name": "Bangladesh" - }, - ":lt:": { - "style": "github", - "image": "1f1f1-1f1f9.png", - "unicode": "🇱🇹", - "name": "Lithuania" - }, - ":bb:": { - "style": "github", - "image": "1f1e7-1f1e7.png", - "unicode": "🇧🇧", - "name": "Barbados" - }, - "🈳": { - "style": "unicode", - "image": "1f233.png", - "name": "Squared Cjk Unified Ideograph-7a7a" - }, - "😴": { - "style": "unicode", - "image": "1f634.png", - "name": "Sleeping Face" - }, - ":massage-tone2:": { - "style": "github", - "image": "1f486-1f3fc.png", - "unicode": "💆🏼", - "name": "Face Massage - Tone 2" - }, - ":flag-qa:": { - "style": "github", - "image": "1f1f6-1f1e6.png", - "unicode": "🇶🇦", - "name": "Qatar" - }, - "🥖": { - "style": "unicode", - "image": "1f956.png", - "name": "Baguette Bread" - }, - ":arrow_up:": { - "style": "github", - "image": "2b06.png", - "unicode": "⬆", - "name": "Upwards Black Arrow" - }, - "🕞": { - "style": "unicode", - "image": "1f55e.png", - "name": "Clock Face Three-thirty" - }, - ":baby-tone1:": { - "style": "github", - "image": "1f476-1f3fb.png", - "unicode": "👶🏻", - "name": "Baby - Tone 1" - }, - ":fist-tone1:": { - "style": "github", - "image": "270a-1f3fb.png", - "unicode": "✊🏻", - "name": "Raised Fist - Tone 1" - }, - ":customs:": { - "style": "github", - "image": "1f6c3.png", - "unicode": "🛃", - "name": "Customs" - }, - "📳": { - "style": "unicode", - "image": "1f4f3.png", - "name": "Vibration Mode" - }, - ":thumbsdown-tone3:": { - "style": "github", - "image": "1f44e-1f3fd.png", - "unicode": "👎🏽", - "name": "Thumbs Down Sign - Tone 3" - }, - ":arrow_left:": { - "style": "github", - "image": "2b05.png", - "unicode": "⬅", - "name": "Leftwards Black Arrow" - }, - "💈": { - "style": "unicode", - "image": "1f488.png", - "name": "Barber Pole" - }, - ":cucumber:": { - "style": "github", - "image": "1f952.png", - "unicode": "🥒", - "name": "Cucumber" - }, - "🌝": { - "style": "unicode", - "image": "1f31d.png", - "name": "Full Moon With Face" - }, - ":person_with_pouting_face_tone1:": { - "style": "github", - "image": "1f64e-1f3fb.png", - "unicode": "🙎🏻", - "name": "Person With Pouting Face Tone1" - }, - ":flag_tc:": { - "style": "github", - "image": "1f1f9-1f1e8.png", - "unicode": "🇹🇨", - "name": "Turks And Caicos Islands" - }, - ":flag_by:": { - "style": "github", - "image": "1f1e7-1f1fe.png", - "unicode": "🇧🇾", - "name": "Belarus" - }, - "🎲": { - "style": "unicode", - "image": "1f3b2.png", - "name": "Game Die" - }, - ":flag-bd:": { - "style": "github", - "image": "1f1e7-1f1e9.png", - "unicode": "🇧🇩", - "name": "Bangladesh" - }, - ":person-with-blond-hair:": { - "style": "github", - "image": "1f471.png", - "unicode": "👱", - "name": "Person With Blond Hair" - }, - ":family-mmb:": { - "style": "github", - "image": "1f468-1f468-1f466.png", - "unicode": "👨👨👦", - "name": "Family (man,man,boy)" - }, - "❕": { - "style": "unicode", - "image": "2755.png", - "name": "White Exclamation Mark Ornament" - }, - ":flag-hr:": { - "style": "github", - "image": "1f1ed-1f1f7.png", - "unicode": "🇭🇷", - "name": "Croatia" - }, - ":gi:": { - "style": "github", - "image": "1f1ec-1f1ee.png", - "unicode": "🇬🇮", - "name": "Gibraltar" - }, - ":ear-tone3:": { - "style": "github", - "image": "1f442-1f3fd.png", - "unicode": "👂🏽", - "name": "Ear - Tone 3" - }, - ":orange-book:": { - "style": "github", - "image": "1f4d9.png", - "unicode": "📙", - "name": "Orange Book" - }, - "🅱": { - "style": "unicode", - "image": "1f171.png", - "name": "Negative Squared Latin Capital Letter B" - }, - ":man-with-turban:": { - "style": "github", - "image": "1f473.png", - "unicode": "👳", - "name": "Man With Turban" - }, - "💂🏼": { - "style": "unicode", - "image": "1f482-1f3fc.png", - "name": "Guardsman - Tone 2" - }, - "💂🏽": { - "style": "unicode", - "image": "1f482-1f3fd.png", - "name": "Guardsman - Tone 3" - }, - "💂🏾": { - "style": "unicode", - "image": "1f482-1f3fe.png", - "name": "Guardsman - Tone 4" - }, - "💂🏿": { - "style": "unicode", - "image": "1f482-1f3ff.png", - "name": "Guardsman - Tone 5" - }, - "♿": { - "style": "unicode", - "image": "267f.png", - "name": "Wheelchair Symbol" - }, - "💂🏻": { - "style": "unicode", - "image": "1f482-1f3fb.png", - "name": "Guardsman - Tone 1" - }, - ":jeans:": { - "style": "github", - "image": "1f456.png", - "unicode": "👖", - "name": "Jeans" - }, - "😊": { - "style": "unicode", - "image": "1f60a.png", - "name": "Smiling Face With Smiling Eyes" - }, - ":orthodox_cross:": { - "style": "github", - "image": "2626.png", - "unicode": "☦", - "name": "Orthodox Cross" - }, - "✔": { - "style": "unicode", - "image": "2714.png", - "name": "Heavy Check Mark" - }, - ":weight_lifter_tone5:": { - "style": "github", - "image": "1f3cb-1f3ff.png", - "unicode": "🏋🏿", - "name": "Weight Lifter - Tone 5" - }, - ":person_frowning_tone1:": { - "style": "github", - "image": "1f64d-1f3fb.png", - "unicode": "🙍🏻", - "name": "Person Frowning - Tone 1" - }, - ":point-up-tone1:": { - "style": "github", - "image": "261d-1f3fb.png", - "unicode": "☝🏻", - "name": "White Up Pointing Index - Tone 1" - }, - ":flag_ls:": { - "style": "github", - "image": "1f1f1-1f1f8.png", - "unicode": "🇱🇸", - "name": "Lesotho" - }, - "📉": { - "style": "unicode", - "image": "1f4c9.png", - "name": "Chart With Downwards Trend" - }, - ":swimmer_tone2:": { - "style": "github", - "image": "1f3ca-1f3fc.png", - "unicode": "🏊🏼", - "name": "Swimmer - Tone 2" - }, - ":camera-with-flash:": { - "style": "github", - "image": "1f4f8.png", - "unicode": "📸", - "name": "Camera With Flash" - }, - ":zw:": { - "style": "github", - "image": "1f1ff-1f1fc.png", - "unicode": "🇿🇼", - "name": "Zimbabwe" - }, - ":cm:": { - "style": "github", - "image": "1f1e8-1f1f2.png", - "unicode": "🇨🇲", - "name": "Cameroon" - }, - ":slot_machine:": { - "style": "github", - "image": "1f3b0.png", - "unicode": "🎰", - "name": "Slot Machine" - }, - "🗳": { - "style": "unicode", - "image": "1f5f3.png", - "name": "Ballot Box With Ballot" - }, - ":couple_with_heart_ww:": { - "style": "github", - "image": "1f469-2764-1f469.png", - "unicode": "👩❤👩", - "name": "Couple (woman,woman)" - }, - ":call-me-tone1:": { - "style": "github", - "image": "1f919-1f3fb.png", - "unicode": "🤙🏻", - "name": "Call Me Hand - Tone 1" - }, - ":handshake-tone3:": { - "style": "github", - "image": "1f91d-1f3fd.png", - "unicode": "🤝🏽", - "name": "Handshake - Tone 3" - }, - "🦀": { - "style": "unicode", - "image": "1f980.png", - "name": "Crab" - }, - ":rowboat:": { - "style": "github", - "image": "1f6a3.png", - "unicode": "🚣", - "name": "Rowboat" - }, - ":km:": { - "style": "github", - "image": "1f1f0-1f1f2.png", - "unicode": "🇰🇲", - "name": "The Comoros" - }, - ":revolving_hearts:": { - "style": "github", - "image": "1f49e.png", - "unicode": "💞", - "name": "Revolving Hearts" - }, - ":mountain-railway:": { - "style": "github", - "image": "1f69e.png", - "unicode": "🚞", - "name": "Mountain Railway" - }, - ":man_with_gua_pi_mao:": { - "style": "github", - "image": "1f472.png", - "unicode": "👲", - "name": "Man With Gua Pi Mao" - }, - "🍇": { - "style": "unicode", - "image": "1f347.png", - "name": "Grapes" - }, - ":mount-fuji:": { - "style": "github", - "image": "1f5fb.png", - "unicode": "🗻", - "name": "Mount Fuji" - }, - "🏜": { - "style": "unicode", - "image": "1f3dc.png", - "name": "Desert" - }, - ":lower_left_paintbrush:": { - "style": "github", - "image": "1f58c.png", - "unicode": "🖌", - "name": "Lower Left Paintbrush" - }, - ":sunrise_over_mountains:": { - "style": "github", - "image": "1f304.png", - "unicode": "🌄", - "name": "Sunrise Over Mountains" - }, - "⛪": { - "style": "unicode", - "image": "26ea.png", - "name": "Church" - }, - ":black_small_square:": { - "style": "github", - "image": "25aa.png", - "unicode": "▪", - "name": "Black Small Square" - }, - ":couple_with_heart:": { - "style": "github", - "image": "1f491.png", - "unicode": "💑", - "name": "Couple With Heart" - }, - "🌆": { - "style": "unicode", - "image": "1f306.png", - "name": "Cityscape At Dusk" - }, - ":motorcycle:": { - "style": "github", - "image": "1f3cd.png", - "unicode": "🏍", - "name": "Racing Motorcycle" - }, - "🔊": { - "style": "unicode", - "image": "1f50a.png", - "name": "Speaker With Three Sound Waves" - }, - ":small_blue_diamond:": { - "style": "github", - "image": "1f539.png", - "unicode": "🔹", - "name": "Small Blue Diamond" - }, - ":couple-ww:": { - "style": "github", - "image": "1f469-2764-1f469.png", - "unicode": "👩❤👩", - "name": "Couple (woman,woman)" - }, - ":cloud:": { - "style": "github", - "image": "2601.png", - "unicode": "☁", - "name": "Cloud" - }, - "💟": { - "style": "unicode", - "image": "1f49f.png", - "name": "Heart Decoration" - }, - ":sparkling_heart:": { - "style": "github", - "image": "1f496.png", - "unicode": "💖", - "name": "Sparkling Heart" - }, - ":boom:": { - "style": "github", - "image": "1f4a5.png", - "unicode": "💥", - "name": "Collision Symbol" - }, - ":point_right:": { - "style": "github", - "image": "1f449.png", - "unicode": "👉", - "name": "White Right Pointing Backhand Index" - }, - ":man_dancing_tone4:": { - "style": "github", - "image": "1f57a-1f3fe.png", - "unicode": "🕺🏾", - "name": "Man Dancing - Tone 4" - }, - ":race-car:": { - "style": "github", - "image": "1f3ce.png", - "unicode": "🏎", - "name": "Racing Car" - }, - "🐴": { - "style": "unicode", - "image": "1f434.png", - "name": "Horse Face" - }, - ":orange_book:": { - "style": "github", - "image": "1f4d9.png", - "unicode": "📙", - "name": "Orange Book" - }, - ":wave_tone2:": { - "style": "github", - "image": "1f44b-1f3fc.png", - "unicode": "👋🏼", - "name": "Waving Hand Sign - Tone 2" - }, - ":point-up-2:": { - "style": "github", - "image": "1f446.png", - "unicode": "👆", - "name": "White Up Pointing Backhand Index" - }, - "⛓": { - "style": "unicode", - "image": "26d3.png", - "name": "Chains" - }, - ":musical_score:": { - "style": "github", - "image": "1f3bc.png", - "unicode": "🎼", - "name": "Musical Score" - }, - ":tone5:": { - "style": "github", - "image": "1f3ff.png", - "unicode": "🏿", - "name": "Emoji Modifier Fitzpatrick Type-6" - }, - ":fingers-crossed-tone5:": { - "style": "github", - "image": "1f91e-1f3ff.png", - "unicode": "🤞🏿", - "name": "Hand With Index And Middle Fingers Crossed - Tone 5" - }, - "♨": { - "style": "unicode", - "image": "2668.png", - "name": "Hot Springs" - }, - ":spy_tone3:": { - "style": "github", - "image": "1f575-1f3fd.png", - "unicode": "🕵🏽", - "name": "Sleuth Or Spy - Tone 3" - }, - ":synagogue:": { - "style": "github", - "image": "1f54d.png", - "unicode": "🕍", - "name": "Synagogue" - }, - ":flag_im:": { - "style": "github", - "image": "1f1ee-1f1f2.png", - "unicode": "🇮🇲", - "name": "Isle Of Man" - }, - "🛳": { - "style": "unicode", - "image": "1f6f3.png", - "name": "Passenger Ship" - }, - ":arrow_up_down:": { - "style": "github", - "image": "2195.png", - "unicode": "↕", - "name": "Up Down Arrow" - }, - ":left_fist:": { - "style": "github", - "image": "1f91b.png", - "unicode": "🤛", - "name": "Left-facing Fist" - }, - "🚈": { - "style": "unicode", - "image": "1f688.png", - "name": "Light Rail" - }, - ":flag_al:": { - "style": "github", - "image": "1f1e6-1f1f1.png", - "unicode": "🇦🇱", - "name": "Albania" - }, - ":older_man_tone5:": { - "style": "github", - "image": "1f474-1f3ff.png", - "unicode": "👴🏿", - "name": "Older Man - Tone 5" - }, - ":gay_pride_flag:": { - "style": "github", - "image": "1f3f3-1f308.png", - "unicode": "🏳🌈", - "name": "Gay_pride_flag" - }, - ":flag-tn:": { - "style": "github", - "image": "1f1f9-1f1f3.png", - "unicode": "🇹🇳", - "name": "Tunisia" - }, - ":middle_finger_tone4:": { - "style": "github", - "image": "1f595-1f3fe.png", - "unicode": "🖕🏾", - "name": "Reversed Hand With Middle Finger Extended - Tone 4" - }, - ":ophiuchus:": { - "style": "github", - "image": "26ce.png", - "unicode": "⛎", - "name": "Ophiuchus" - }, - ":writing-hand-tone5:": { - "style": "github", - "image": "270d-1f3ff.png", - "unicode": "✍🏿", - "name": "Writing Hand - Tone 5" - }, - ":second-place:": { - "style": "github", - "image": "1f948.png", - "unicode": "🥈", - "name": "Second Place Medal" - }, - ":pregnant-woman-tone4:": { - "style": "github", - "image": "1f930-1f3fe.png", - "unicode": "🤰🏾", - "name": "Pregnant Woman - Tone 4" - }, - ":facepalm_tone3:": { - "style": "github", - "image": "1f926-1f3fd.png", - "unicode": "🤦🏽", - "name": "Face Palm - Tone 3" - }, - "🍱": { - "style": "unicode", - "image": "1f371.png", - "name": "Bento Box" - }, - "🕵": { - "style": "unicode", - "image": "1f575.png", - "name": "Sleuth Or Spy" - }, - ":flag-my:": { - "style": "github", - "image": "1f1f2-1f1fe.png", - "unicode": "🇲🇾", - "name": "Malaysia" - }, - ":kp:": { - "style": "github", - "image": "1f1f0-1f1f5.png", - "unicode": "🇰🇵", - "name": "North Korea" - }, - "🐊": { - "style": "unicode", - "image": "1f40a.png", - "name": "Crocodile" - }, - "🎛": { - "style": "unicode", - "image": "1f39b.png", - "name": "Control Knobs" - }, - "*⃣": { - "style": "unicode", - "image": "002a-20e3.png", - "name": "Keycap Asterisk" - }, - ":mobile-phone-off:": { - "style": "github", - "image": "1f4f4.png", - "unicode": "📴", - "name": "Mobile Phone Off" - }, - ":walking_tone5:": { - "style": "github", - "image": "1f6b6-1f3ff.png", - "unicode": "🚶🏿", - "name": "Pedestrian - Tone 5" - }, - "🌰": { - "style": "unicode", - "image": "1f330.png", - "name": "Chestnut" - }, - "🔴": { - "style": "unicode", - "image": "1f534.png", - "name": "Large Red Circle" - }, - ":flag-kw:": { - "style": "github", - "image": "1f1f0-1f1fc.png", - "unicode": "🇰🇼", - "name": "Kuwait" - }, - ":writing_hand_tone3:": { - "style": "github", - "image": "270d-1f3fd.png", - "unicode": "✍🏽", - "name": "Writing Hand - Tone 3" - }, - ":top:": { - "style": "github", - "image": "1f51d.png", - "unicode": "🔝", - "name": "Top With Upwards Arrow Above" - }, - ":construction:": { - "style": "github", - "image": "1f6a7.png", - "unicode": "🚧", - "name": "Construction Sign" - }, - ":bee:": { - "style": "github", - "image": "1f41d.png", - "unicode": "🐝", - "name": "Honeybee" - }, - ":flag_np:": { - "style": "github", - "image": "1f1f3-1f1f5.png", - "unicode": "🇳🇵", - "name": "Nepal" - }, - ":heavy_multiplication_x:": { - "style": "github", - "image": "2716.png", - "unicode": "✖", - "name": "Heavy Multiplication X" - }, - ":flag_gd:": { - "style": "github", - "image": "1f1ec-1f1e9.png", - "unicode": "🇬🇩", - "name": "Grenada" - }, - "😗": { - "style": "unicode", - "image": "1f617.png", - "name": "Kissing Face" - }, - ":badminton:": { - "style": "github", - "image": "1f3f8.png", - "unicode": "🏸", - "name": "Badminton Racquet" - }, - ":flag_qa:": { - "style": "github", - "image": "1f1f6-1f1e6.png", - "unicode": "🇶🇦", - "name": "Qatar" - }, - ":pa:": { - "style": "github", - "image": "1f1f5-1f1e6.png", - "unicode": "🇵🇦", - "name": "Panama" - }, - ":flag-ae:": { - "style": "github", - "image": "1f1e6-1f1ea.png", - "unicode": "🇦🇪", - "name": "The United Arab Emirates" - }, - ":thumbdown_tone3:": { - "style": "github", - "image": "1f44e-1f3fd.png", - "unicode": "👎🏽", - "name": "Thumbs Down Sign - Tone 3" - }, - ":grandma_tone3:": { - "style": "github", - "image": "1f475-1f3fd.png", - "unicode": "👵🏽", - "name": "Older Woman - Tone 3" - }, - ":flag-dm:": { - "style": "github", - "image": "1f1e9-1f1f2.png", - "unicode": "🇩🇲", - "name": "Dominica" - }, - ":hugging_face:": { - "style": "github", - "image": "1f917.png", - "unicode": "🤗", - "name": "Hugging Face" - }, - ":hockey:": { - "style": "github", - "image": "1f3d2.png", - "unicode": "🏒", - "name": "Ice Hockey Stick And Puck" - }, - ":melon:": { - "style": "github", - "image": "1f348.png", - "unicode": "🍈", - "name": "Melon" - }, - ":family_wwg:": { - "style": "github", - "image": "1f469-1f469-1f467.png", - "unicode": "👩👩👧", - "name": "Family (woman,woman,girl)" - }, - ":flag_ir:": { - "style": "github", - "image": "1f1ee-1f1f7.png", - "unicode": "🇮🇷", - "name": "Iran" - }, - ":raised-back-of-hand-tone2:": { - "style": "github", - "image": "1f91a-1f3fc.png", - "unicode": "🤚🏼", - "name": "Raised Back Of Hand - Tone 2" - }, - "👵": { - "style": "unicode", - "image": "1f475.png", - "name": "Older Woman" - }, - ":prince-tone2:": { - "style": "github", - "image": "1f934-1f3fc.png", - "unicode": "🤴🏼", - "name": "Prince - Tone 2" - }, - ":regional_indicator_x:": { - "style": "github", - "image": "1f1fd.png", - "unicode": "🇽", - "name": "Regional Indicator Symbol Letter X" - }, - "🔆": { - "style": "unicode", - "image": "1f506.png", - "name": "High Brightness Symbol" - }, - ":cartwheel-tone2:": { - "style": "github", - "image": "1f938-1f3fc.png", - "unicode": "🤸🏼", - "name": "Person Doing Cartwheel - Tone 2" - }, - ":angry:": { - "style": "github", - "ascii": ">:(", - "image": "1f620.png", - "unicode": "😠", - "name": "Angry Face" - }, - ":older-man-tone2:": { - "style": "github", - "image": "1f474-1f3fc.png", - "unicode": "👴🏼", - "name": "Older Man - Tone 2" - }, - "💛": { - "style": "unicode", - "image": "1f49b.png", - "name": "Yellow Heart" - }, - ":bamboo:": { - "style": "github", - "image": "1f38d.png", - "unicode": "🎍", - "name": "Pine Decoration" - }, - "↩": { - "style": "unicode", - "image": "21a9.png", - "name": "Leftwards Arrow With Hook" - }, - "🐰": { - "style": "unicode", - "image": "1f430.png", - "name": "Rabbit Face" - }, - "🈴": { - "style": "unicode", - "image": "1f234.png", - "name": "Squared Cjk Unified Ideograph-5408" - }, - ":mountain-bicyclist-tone4:": { - "style": "github", - "image": "1f6b5-1f3fe.png", - "unicode": "🚵🏾", - "name": "Mountain Bicyclist - Tone 4" - }, - ":heart-eyes-cat:": { - "style": "github", - "image": "1f63b.png", - "unicode": "😻", - "name": "Smiling Cat Face With Heart-shaped Eyes" - }, - "🎅🏻": { - "style": "unicode", - "image": "1f385-1f3fb.png", - "name": "Father Christmas - Tone 1" - }, - ":woman-tone3:": { - "style": "github", - "image": "1f469-1f3fd.png", - "unicode": "👩🏽", - "name": "Woman - Tone 3" - }, - ":girl_tone4:": { - "style": "github", - "image": "1f467-1f3fe.png", - "unicode": "👧🏾", - "name": "Girl - Tone 4" - }, - ":family-mmgb:": { - "style": "github", - "image": "1f468-1f468-1f467-1f466.png", - "unicode": "👨👨👧👦", - "name": "Family (man,man,girl,boy)" - }, - ":man_in_tuxedo_tone1:": { - "style": "github", - "image": "1f935-1f3fb.png", - "unicode": "🤵🏻", - "name": "Man In Tuxedo - Tone 1" - }, - ":family-mwgb:": { - "style": "github", - "image": "1f468-1f469-1f467-1f466.png", - "unicode": "👨👩👧👦", - "name": "Family (man,woman,girl,boy)" - }, - ":flag_pm:": { - "style": "github", - "image": "1f1f5-1f1f2.png", - "unicode": "🇵🇲", - "name": "Saint Pierre And Miquelon" - }, - ":umbrella:": { - "style": "github", - "image": "2614.png", - "unicode": "☔", - "name": "Umbrella With Rain Drops" - }, - "👸🏻": { - "style": "unicode", - "image": "1f478-1f3fb.png", - "name": "Princess - Tone 1" - }, - "👸🏾": { - "style": "unicode", - "image": "1f478-1f3fe.png", - "name": "Princess - Tone 4" - }, - "👸🏿": { - "style": "unicode", - "image": "1f478-1f3ff.png", - "name": "Princess - Tone 5" - }, - "👸🏼": { - "style": "unicode", - "image": "1f478-1f3fc.png", - "name": "Princess - Tone 2" - }, - "👸🏽": { - "style": "unicode", - "image": "1f478-1f3fd.png", - "name": "Princess - Tone 3" - }, - "🤙": { - "style": "unicode", - "image": "1f919.png", - "name": "Call Me Hand" - }, - ":flag_mn:": { - "style": "github", - "image": "1f1f2-1f1f3.png", - "unicode": "🇲🇳", - "name": "Mongolia" - }, - ":leftwards_arrow_with_hook:": { - "style": "github", - "image": "21a9.png", - "unicode": "↩", - "name": "Leftwards Arrow With Hook" - }, - ":lr:": { - "style": "github", - "image": "1f1f1-1f1f7.png", - "unicode": "🇱🇷", - "name": "Liberia" - }, - ":bl:": { - "style": "github", - "image": "1f1e7-1f1f1.png", - "unicode": "🇧🇱", - "name": "Saint Barthélemy" - }, - ":kissing-cat:": { - "style": "github", - "image": "1f63d.png", - "unicode": "😽", - "name": "Kissing Cat Face With Closed Eyes" - }, - ":radioactive_sign:": { - "style": "github", - "image": "2622.png", - "unicode": "☢", - "name": "Radioactive Sign" - }, - "🖖🏻": { - "style": "unicode", - "image": "1f596-1f3fb.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 1" - }, - "🖖🏼": { - "style": "unicode", - "image": "1f596-1f3fc.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 2" - }, - "🖖🏽": { - "style": "unicode", - "image": "1f596-1f3fd.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 3" - }, - "🖖🏾": { - "style": "unicode", - "image": "1f596-1f3fe.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 4" - }, - "🖖🏿": { - "style": "unicode", - "image": "1f596-1f3ff.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 5" - }, - ":crossed_swords:": { - "style": "github", - "image": "2694.png", - "unicode": "⚔", - "name": "Crossed Swords" - }, - "🙇": { - "style": "unicode", - "image": "1f647.png", - "name": "Person Bowing Deeply" - }, - ":princess_tone5:": { - "style": "github", - "image": "1f478-1f3ff.png", - "unicode": "👸🏿", - "name": "Princess - Tone 5" - }, - ":construction-worker-tone4:": { - "style": "github", - "image": "1f477-1f3fe.png", - "unicode": "👷🏾", - "name": "Construction Worker - Tone 4" - }, - ":flag-cd:": { - "style": "github", - "image": "1f1e8-1f1e9.png", - "unicode": "🇨🇩", - "name": "The Democratic Republic Of The Congo" - }, - ":baby-tone3:": { - "style": "github", - "image": "1f476-1f3fd.png", - "unicode": "👶🏽", - "name": "Baby - Tone 3" - }, - ":sob:": { - "style": "github", - "image": "1f62d.png", - "unicode": "😭", - "name": "Loudly Crying Face" - }, - "⏪": { - "style": "unicode", - "image": "23ea.png", - "name": "Black Left-pointing Double Triangle" - }, - ":thumbsdown-tone1:": { - "style": "github", - "image": "1f44e-1f3fb.png", - "unicode": "👎🏻", - "name": "Thumbs Down Sign - Tone 1" - }, - "🍵": { - "style": "unicode", - "image": "1f375.png", - "name": "Teacup Without Handle" - }, - ":right_anger_bubble:": { - "style": "github", - "image": "1f5ef.png", - "unicode": "🗯", - "name": "Right Anger Bubble" - }, - ":swimmer_tone4:": { - "style": "github", - "image": "1f3ca-1f3fe.png", - "unicode": "🏊🏾", - "name": "Swimmer - Tone 4" - }, - ":flag_id:": { - "style": "github", - "image": "1f1ee-1f1e9.png", - "unicode": "🇮🇩", - "name": "Indonesia" - }, - "🐆": { - "style": "unicode", - "image": "1f406.png", - "name": "Leopard" - }, - "🎟": { - "style": "unicode", - "image": "1f39f.png", - "name": "Admission Tickets" - }, - ":cartwheel_tone4:": { - "style": "github", - "image": "1f938-1f3fe.png", - "unicode": "🤸🏾", - "name": "Person Doing Cartwheel - Tone 4" - }, - ":call_me_hand_tone1:": { - "style": "github", - "image": "1f919-1f3fb.png", - "unicode": "🤙🏻", - "name": "Call Me Hand - Tone 1" - }, - ":ua:": { - "style": "github", - "image": "1f1fa-1f1e6.png", - "unicode": "🇺🇦", - "name": "Ukraine" - }, - "🔰": { - "style": "unicode", - "image": "1f530.png", - "name": "Japanese Symbol For Beginner" - }, - "🌴": { - "style": "unicode", - "image": "1f334.png", - "name": "Palm Tree" - }, - ":nose-tone1:": { - "style": "github", - "image": "1f443-1f3fb.png", - "unicode": "👃🏻", - "name": "Nose - Tone 1" - }, - ":flag-bb:": { - "style": "github", - "image": "1f1e7-1f1e7.png", - "unicode": "🇧🇧", - "name": "Barbados" - }, - ":e-mail:": { - "style": "github", - "image": "1f4e7.png", - "unicode": "📧", - "name": "E-mail Symbol" - }, - "✊🏼": { - "style": "unicode", - "image": "270a-1f3fc.png", - "name": "Raised Fist - Tone 2" - }, - "✊🏽": { - "style": "unicode", - "image": "270a-1f3fd.png", - "name": "Raised Fist - Tone 3" - }, - "✊🏾": { - "style": "unicode", - "image": "270a-1f3fe.png", - "name": "Raised Fist - Tone 4" - }, - ":left_fist_tone3:": { - "style": "github", - "image": "1f91b-1f3fd.png", - "unicode": "🤛🏽", - "name": "Left Facing Fist - Tone 3" - }, - "✊🏻": { - "style": "unicode", - "image": "270a-1f3fb.png", - "name": "Raised Fist - Tone 1" - }, - "🇳": { - "style": "unicode", - "image": "1f1f3.png", - "name": "Regional Indicator Symbol Letter N" - }, - ":flag-vc:": { - "style": "github", - "image": "1f1fb-1f1e8.png", - "unicode": "🇻🇨", - "name": "Saint Vincent And The Grenadines" - }, - "⛽": { - "style": "unicode", - "image": "26fd.png", - "name": "Fuel Pump" - }, - "⚒": { - "style": "unicode", - "image": "2692.png", - "name": "Hammer And Pick" - }, - "😝": { - "style": "unicode", - "image": "1f61d.png", - "name": "Face With Stuck-out Tongue And Tightly-closed Eyes" - }, - ":pineapple:": { - "style": "github", - "image": "1f34d.png", - "unicode": "🍍", - "name": "Pineapple" - }, - ":flag_sd:": { - "style": "github", - "image": "1f1f8-1f1e9.png", - "unicode": "🇸🇩", - "name": "Sudan" - }, - ":kissing-smiling-eyes:": { - "style": "github", - "image": "1f619.png", - "unicode": "😙", - "name": "Kissing Face With Smiling Eyes" - }, - "🚲": { - "style": "unicode", - "image": "1f6b2.png", - "name": "Bicycle" - }, - ":right_fist_tone5:": { - "style": "github", - "image": "1f91c-1f3ff.png", - "unicode": "🤜🏿", - "name": "Right Facing Fist - Tone 5" - }, - ":man-with-gua-pi-mao-tone2:": { - "style": "github", - "image": "1f472-1f3fc.png", - "unicode": "👲🏼", - "name": "Man With Gua Pi Mao - Tone 2" - }, - "🥃": { - "style": "unicode", - "image": "1f943.png", - "name": "Tumbler Glass" - }, - ":point-up-tone3:": { - "style": "github", - "image": "261d-1f3fd.png", - "unicode": "☝🏽", - "name": "White Up Pointing Index - Tone 3" - }, - ":thumbsup-tone2:": { - "style": "github", - "image": "1f44d-1f3fc.png", - "unicode": "👍🏼", - "name": "Thumbs Up Sign - Tone 2" - }, - ":raised_hands_tone2:": { - "style": "github", - "image": "1f64c-1f3fc.png", - "unicode": "🙌🏼", - "name": "Person Raising Both Hands In Celebration - Tone 2" - }, - ":dg:": { - "style": "github", - "image": "1f1e9-1f1ec.png", - "unicode": "🇩🇬", - "name": "Diego Garcia" - }, - ":mq:": { - "style": "github", - "image": "1f1f2-1f1f6.png", - "unicode": "🇲🇶", - "name": "Martinique" - }, - ":ck:": { - "style": "github", - "image": "1f1e8-1f1f0.png", - "unicode": "🇨🇰", - "name": "Cook Islands" - }, - ":ye:": { - "style": "github", - "image": "1f1fe-1f1ea.png", - "unicode": "🇾🇪", - "name": "Yemen" - }, - ":person_frowning_tone3:": { - "style": "github", - "image": "1f64d-1f3fd.png", - "unicode": "🙍🏽", - "name": "Person Frowning - Tone 3" - }, - "👱": { - "style": "unicode", - "image": "1f471.png", - "name": "Person With Blond Hair" - }, - "🤙🏻": { - "style": "unicode", - "image": "1f919-1f3fb.png", - "name": "Call Me Hand - Tone 1" - }, - "🤙🏽": { - "style": "unicode", - "image": "1f919-1f3fd.png", - "name": "Call Me Hand - Tone 3" - }, - "🤙🏼": { - "style": "unicode", - "image": "1f919-1f3fc.png", - "name": "Call Me Hand - Tone 2" - }, - "🤙🏿": { - "style": "unicode", - "image": "1f919-1f3ff.png", - "name": "Call Me Hand - Tone 5" - }, - "🤙🏾": { - "style": "unicode", - "image": "1f919-1f3fe.png", - "name": "Call Me Hand - Tone 4" - }, - ":flag_black:": { - "style": "github", - "image": "1f3f4.png", - "unicode": "🏴", - "name": "Waving Black Flag" - }, - ":baby:": { - "style": "github", - "image": "1f476.png", - "unicode": "👶", - "name": "Baby" - }, - "🔀": { - "style": "unicode", - "image": "1f500.png", - "name": "Twisted Rightwards Arrows" - }, - "🚛": { - "style": "unicode", - "image": "1f69b.png", - "name": "Articulated Lorry" - }, - ":cake:": { - "style": "github", - "image": "1f370.png", - "unicode": "🍰", - "name": "Shortcake" - }, - ":baggage_claim:": { - "style": "github", - "image": "1f6c4.png", - "unicode": "🛄", - "name": "Baggage Claim" - }, - ":grey-exclamation:": { - "style": "github", - "image": "2755.png", - "unicode": "❕", - "name": "White Exclamation Mark Ornament" - }, - "😰": { - "style": "unicode", - "image": "1f630.png", - "name": "Face With Open Mouth And Cold Sweat" - }, - ":star_of_david:": { - "style": "github", - "image": "2721.png", - "unicode": "✡", - "name": "Star Of David" - }, - ":guardsman_tone2:": { - "style": "github", - "image": "1f482-1f3fc.png", - "unicode": "💂🏼", - "name": "Guardsman - Tone 2" - }, - ":disappointed:": { - "style": "github", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - "🏉": { - "style": "unicode", - "image": "1f3c9.png", - "name": "Rugby Football" - }, - ":chains:": { - "style": "github", - "image": "26d3.png", - "unicode": "⛓", - "name": "Chains" - }, - ":heavy_heart_exclamation_mark_ornament:": { - "style": "github", - "image": "2763.png", - "unicode": "❣", - "name": "Heavy Heart Exclamation Mark Ornament" - }, - "🥚": { - "style": "unicode", - "image": "1f95a.png", - "name": "Egg" - }, - "🍞": { - "style": "unicode", - "image": "1f35e.png", - "name": "Bread" - }, - "👳🏻": { - "style": "unicode", - "image": "1f473-1f3fb.png", - "name": "Man With Turban - Tone 1" - }, - "👳🏿": { - "style": "unicode", - "image": "1f473-1f3ff.png", - "name": "Man With Turban - Tone 5" - }, - "👳🏾": { - "style": "unicode", - "image": "1f473-1f3fe.png", - "name": "Man With Turban - Tone 4" - }, - "👳🏽": { - "style": "unicode", - "image": "1f473-1f3fd.png", - "name": "Man With Turban - Tone 3" - }, - "👳🏼": { - "style": "unicode", - "image": "1f473-1f3fc.png", - "name": "Man With Turban - Tone 2" - }, - ":v_tone2:": { - "style": "github", - "image": "270c-1f3fc.png", - "unicode": "✌🏼", - "name": "Victory Hand - Tone 2" - }, - "B)": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - ":meat_on_bone:": { - "style": "github", - "image": "1f356.png", - "unicode": "🍖", - "name": "Meat On Bone" - }, - ":volcano:": { - "style": "github", - "image": "1f30b.png", - "unicode": "🌋", - "name": "Volcano" - }, - ":thumbup_tone5:": { - "style": "github", - "image": "1f44d-1f3ff.png", - "unicode": "👍🏿", - "name": "Thumbs Up Sign - Tone 5" - }, - ":flag-pl:": { - "style": "github", - "image": "1f1f5-1f1f1.png", - "unicode": "🇵🇱", - "name": "Poland" - }, - ":video_game:": { - "style": "github", - "image": "1f3ae.png", - "unicode": "🎮", - "name": "Video Game" - }, - "◽": { - "style": "unicode", - "image": "25fd.png", - "name": "White Medium Small Square" - }, - ":arrow-up:": { - "style": "github", - "image": "2b06.png", - "unicode": "⬆", - "name": "Upwards Black Arrow" - }, - ":hand_splayed_tone3:": { - "style": "github", - "image": "1f590-1f3fd.png", - "unicode": "🖐🏽", - "name": "Raised Hand With Fingers Splayed - Tone 3" - }, - ":oncoming-police-car:": { - "style": "github", - "image": "1f694.png", - "unicode": "🚔", - "name": "Oncoming Police Car" - }, - ":swimmer-tone4:": { - "style": "github", - "image": "1f3ca-1f3fe.png", - "unicode": "🏊🏾", - "name": "Swimmer - Tone 4" - }, - "🔝": { - "style": "unicode", - "image": "1f51d.png", - "name": "Top With Upwards Arrow Above" - }, - ":man_dancing_tone2:": { - "style": "github", - "image": "1f57a-1f3fc.png", - "unicode": "🕺🏼", - "name": "Man Dancing - Tone 2" - }, - ":meat-on-bone:": { - "style": "github", - "image": "1f356.png", - "unicode": "🍖", - "name": "Meat On Bone" - }, - ":spades:": { - "style": "github", - "image": "2660.png", - "unicode": "♠", - "name": "Black Spade Suit" - }, - ":lips:": { - "style": "github", - "image": "1f444.png", - "unicode": "👄", - "name": "Mouth" - }, - ":man_tone3:": { - "style": "github", - "image": "1f468-1f3fd.png", - "unicode": "👨🏽", - "name": "Man - Tone 3" - }, - ":eight_pointed_black_star:": { - "style": "github", - "image": "2734.png", - "unicode": "✴", - "name": "Eight Pointed Black Star" - }, - "🖲": { - "style": "unicode", - "image": "1f5b2.png", - "name": "Trackball" - }, - ":carousel_horse:": { - "style": "github", - "image": "1f3a0.png", - "unicode": "🎠", - "name": "Carousel Horse" - }, - "👇": { - "style": "unicode", - "image": "1f447.png", - "name": "White Down Pointing Backhand Index" - }, - ":third_place:": { - "style": "github", - "image": "1f949.png", - "unicode": "🥉", - "name": "Third Place Medal" - }, - ":sn:": { - "style": "github", - "image": "1f1f8-1f1f3.png", - "unicode": "🇸🇳", - "name": "Senegal" - }, - "📜": { - "style": "unicode", - "image": "1f4dc.png", - "name": "Scroll" - }, - ":tone3:": { - "style": "github", - "image": "1f3fd.png", - "unicode": "🏽", - "name": "Emoji Modifier Fitzpatrick Type-4" - }, - "♻": { - "style": "unicode", - "image": "267b.png", - "name": "Black Universal Recycling Symbol" - }, - ":derelict_house_building:": { - "style": "github", - "image": "1f3da.png", - "unicode": "🏚", - "name": "Derelict House Building" - }, - "😆": { - "style": "unicode", - "ascii": ">:)", - "image": "1f606.png", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ":face-palm-tone1:": { - "style": "github", - "image": "1f926-1f3fb.png", - "unicode": "🤦🏻", - "name": "Face Palm - Tone 1" - }, - ":middle_finger_tone2:": { - "style": "github", - "image": "1f595-1f3fc.png", - "unicode": "🖕🏼", - "name": "Reversed Hand With Middle Finger Extended - Tone 2" - }, - ":clock:": { - "style": "github", - "image": "1f570.png", - "unicode": "🕰", - "name": "Mantlepiece Clock" - }, - ":flower_playing_cards:": { - "style": "github", - "image": "1f3b4.png", - "unicode": "🎴", - "name": "Flower Playing Cards" - }, - ":wave_tone4:": { - "style": "github", - "image": "1f44b-1f3fe.png", - "unicode": "👋🏾", - "name": "Waving Hand Sign - Tone 4" - }, - ":high-heel:": { - "style": "github", - "image": "1f460.png", - "unicode": "👠", - "name": "High-heeled Shoe" - }, - ":yellow-heart:": { - "style": "github", - "image": "1f49b.png", - "unicode": "💛", - "name": "Yellow Heart" - }, - ":first-place:": { - "style": "github", - "image": "1f947.png", - "unicode": "🥇", - "name": "First Place Medal" - }, - ":pregnant-woman-tone2:": { - "style": "github", - "image": "1f930-1f3fc.png", - "unicode": "🤰🏼", - "name": "Pregnant Woman - Tone 2" - }, - ":newspaper:": { - "style": "github", - "image": "1f4f0.png", - "unicode": "📰", - "name": "Newspaper" - }, - ":facepalm_tone5:": { - "style": "github", - "image": "1f926-1f3ff.png", - "unicode": "🤦🏿", - "name": "Face Palm - Tone 5" - }, - "🏳": { - "style": "unicode", - "image": "1f3f3.png", - "name": "Waving White Flag" - }, - ":flag-mw:": { - "style": "github", - "image": "1f1f2-1f1fc.png", - "unicode": "🇲🇼", - "name": "Malawi" - }, - "🙎🏼": { - "style": "unicode", - "image": "1f64e-1f3fc.png", - "name": "Person With Pouting Face Tone2" - }, - ":ax:": { - "style": "github", - "image": "1f1e6-1f1fd.png", - "unicode": "🇦🇽", - "name": "Åland Islands" - }, - "🦄": { - "style": "unicode", - "image": "1f984.png", - "name": "Unicorn Face" - }, - ":raising_hand:": { - "style": "github", - "image": "1f64b.png", - "unicode": "🙋", - "name": "Happy Person Raising One Hand" - }, - "🎈": { - "style": "unicode", - "image": "1f388.png", - "name": "Balloon" - }, - ":last_quarter_moon:": { - "style": "github", - "image": "1f317.png", - "unicode": "🌗", - "name": "Last Quarter Moon Symbol" - }, - ":vertical-traffic-light:": { - "style": "github", - "image": "1f6a6.png", - "unicode": "🚦", - "name": "Vertical Traffic Light" - }, - ":flag-sb:": { - "style": "github", - "image": "1f1f8-1f1e7.png", - "unicode": "🇸🇧", - "name": "The Solomon Islands" - }, - "🐝": { - "style": "unicode", - "image": "1f41d.png", - "name": "Honeybee" - }, - ":flag-eg:": { - "style": "github", - "image": "1f1ea-1f1ec.png", - "unicode": "🇪🇬", - "name": "Egypt" - }, - ":raising-hand-tone1:": { - "style": "github", - "image": "1f64b-1f3fb.png", - "unicode": "🙋🏻", - "name": "Happy Person Raising One Hand Tone1" - }, - "💲": { - "style": "unicode", - "image": "1f4b2.png", - "name": "Heavy Dollar Sign" - }, - ":man_with_turban_tone1:": { - "style": "github", - "image": "1f473-1f3fb.png", - "unicode": "👳🏻", - "name": "Man With Turban - Tone 1" - }, - "♑": { - "style": "unicode", - "image": "2651.png", - "name": "Capricorn" - }, - ":gun:": { - "style": "github", - "image": "1f52b.png", - "unicode": "🔫", - "name": "Pistol" - }, - "⏏": { - "style": "unicode", - "image": "23cf.png", - "name": "Eject Symbol" - }, - ":writing_hand_tone1:": { - "style": "github", - "image": "270d-1f3fb.png", - "unicode": "✍🏻", - "name": "Writing Hand - Tone 1" - }, - "🗜": { - "style": "unicode", - "image": "1f5dc.png", - "name": "Compression" - }, - ":flag_nr:": { - "style": "github", - "image": "1f1f3-1f1f7.png", - "unicode": "🇳🇷", - "name": "Nauru" - }, - ":flag_gb:": { - "style": "github", - "image": "1f1ec-1f1e7.png", - "unicode": "🇬🇧", - "name": "Great Britain" - }, - "0;-)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":fingers-crossed:": { - "style": "github", - "image": "1f91e.png", - "unicode": "🤞", - "name": "Hand With First And Index Finger Crossed" - }, - ":flag-mp:": { - "style": "github", - "image": "1f1f2-1f1f5.png", - "unicode": "🇲🇵", - "name": "Northern Mariana Islands" - }, - ":muscle-tone5:": { - "style": "github", - "image": "1f4aa-1f3ff.png", - "unicode": "💪🏿", - "name": "Flexed Biceps - Tone 5" - }, - ":pg:": { - "style": "github", - "image": "1f1f5-1f1ec.png", - "unicode": "🇵🇬", - "name": "Papua New Guinea" - }, - ":pregnant_woman:": { - "style": "github", - "image": "1f930.png", - "unicode": "🤰", - "name": "Pregnant Woman" - }, - "🚏": { - "style": "unicode", - "image": "1f68f.png", - "name": "Bus Stop" - }, - ":flag-do:": { - "style": "github", - "image": "1f1e9-1f1f4.png", - "unicode": "🇩🇴", - "name": "The Dominican Republic" - }, - ":grandma_tone1:": { - "style": "github", - "image": "1f475-1f3fb.png", - "unicode": "👵🏻", - "name": "Older Woman - Tone 1" - }, - ":arrow_heading_up:": { - "style": "github", - "image": "2934.png", - "unicode": "⤴", - "name": "Arrow Pointing Rightwards Then Curving Upwards" - }, - "😤": { - "style": "unicode", - "image": "1f624.png", - "name": "Face With Look Of Triumph" - }, - ":palm_tree:": { - "style": "github", - "image": "1f334.png", - "unicode": "🌴", - "name": "Palm Tree" - }, - ":crescent_moon:": { - "style": "github", - "image": "1f319.png", - "unicode": "🌙", - "name": "Crescent Moon" - }, - ":thumbdown_tone1:": { - "style": "github", - "image": "1f44e-1f3fb.png", - "unicode": "👎🏻", - "name": "Thumbs Down Sign - Tone 1" - }, - "🕎": { - "style": "unicode", - "image": "1f54e.png", - "name": "Menorah With Nine Branches" - }, - "📣": { - "style": "unicode", - "image": "1f4e3.png", - "name": "Cheering Megaphone" - }, - ":regional-indicator-f:": { - "style": "github", - "image": "1f1eb.png", - "unicode": "🇫", - "name": "Regional Indicator Symbol Letter F" - }, - "👸": { - "style": "unicode", - "image": "1f478.png", - "name": "Princess" - }, - ":symbols:": { - "style": "github", - "image": "1f523.png", - "unicode": "🔣", - "name": "Input Symbol For Symbols" - }, - ":raised-hands-tone3:": { - "style": "github", - "image": "1f64c-1f3fd.png", - "unicode": "🙌🏽", - "name": "Person Raising Both Hands In Celebration - Tone 3" - }, - "🌍": { - "style": "unicode", - "image": "1f30d.png", - "name": "Earth Globe Europe-africa" - }, - "🤑": { - "style": "unicode", - "image": "1f911.png", - "name": "Money-mouth Face" - }, - "👎🏻": { - "style": "unicode", - "image": "1f44e-1f3fb.png", - "name": "Thumbs Down Sign - Tone 1" - }, - "👎🏼": { - "style": "unicode", - "image": "1f44e-1f3fc.png", - "name": "Thumbs Down Sign - Tone 2" - }, - "👎🏽": { - "style": "unicode", - "image": "1f44e-1f3fd.png", - "name": "Thumbs Down Sign - Tone 3" - }, - "👎🏾": { - "style": "unicode", - "image": "1f44e-1f3fe.png", - "name": "Thumbs Down Sign - Tone 4" - }, - "👎🏿": { - "style": "unicode", - "image": "1f44e-1f3ff.png", - "name": "Thumbs Down Sign - Tone 5" - }, - ":fast_forward:": { - "style": "github", - "image": "23e9.png", - "unicode": "⏩", - "name": "Black Right-pointing Double Triangle" - }, - ":thumbup:": { - "style": "github", - "image": "1f44d.png", - "unicode": "👍", - "name": "Thumbs Up Sign" - }, - "🇲🇵": { - "style": "unicode", - "image": "1f1f2-1f1f5.png", - "name": "Northern Mariana Islands" - }, - "🇲🇶": { - "style": "unicode", - "image": "1f1f2-1f1f6.png", - "name": "Martinique" - }, - "🎢": { - "style": "unicode", - "image": "1f3a2.png", - "name": "Roller Coaster" - }, - "🇲🇰": { - "style": "unicode", - "image": "1f1f2-1f1f0.png", - "name": "Macedonia" - }, - "🇲🇱": { - "style": "unicode", - "image": "1f1f2-1f1f1.png", - "name": "Mali" - }, - "🇲🇲": { - "style": "unicode", - "image": "1f1f2-1f1f2.png", - "name": "Myanmar" - }, - "🇲🇳": { - "style": "unicode", - "image": "1f1f2-1f1f3.png", - "name": "Mongolia" - }, - ":fj:": { - "style": "github", - "image": "1f1eb-1f1ef.png", - "unicode": "🇫🇯", - "name": "Fiji" - }, - "🇲🇽": { - "style": "unicode", - "image": "1f1f2-1f1fd.png", - "name": "Mexico" - }, - "🇲🇾": { - "style": "unicode", - "image": "1f1f2-1f1fe.png", - "name": "Malaysia" - }, - "🇲🇿": { - "style": "unicode", - "image": "1f1f2-1f1ff.png", - "name": "Mozambique" - }, - "🇲🇸": { - "style": "unicode", - "image": "1f1f2-1f1f8.png", - "name": "Montserrat" - }, - "🇲🇹": { - "style": "unicode", - "image": "1f1f2-1f1f9.png", - "name": "Malta" - }, - "🇲🇺": { - "style": "unicode", - "image": "1f1f2-1f1fa.png", - "name": "Mauritius" - }, - "🇲🇻": { - "style": "unicode", - "image": "1f1f2-1f1fb.png", - "name": "Maldives" - }, - "🇲🇦": { - "style": "unicode", - "image": "1f1f2-1f1e6.png", - "name": "Morocco" - }, - "🈷": { - "style": "unicode", - "image": "1f237.png", - "name": "Squared Cjk Unified Ideograph-6708" - }, - "⛈": { - "style": "unicode", - "image": "26c8.png", - "name": "Thunder Cloud And Rain" - }, - ":family:": { - "style": "github", - "image": "1f46a.png", - "unicode": "👪", - "name": "Family" - }, - "🇲🇭": { - "style": "unicode", - "image": "1f1f2-1f1ed.png", - "name": "The Marshall Islands" - }, - ":flag_sy:": { - "style": "github", - "image": "1f1f8-1f1fe.png", - "unicode": "🇸🇾", - "name": "Syria" - }, - ":heart-decoration:": { - "style": "github", - "image": "1f49f.png", - "unicode": "💟", - "name": "Heart Decoration" - }, - "🇲🇨": { - "style": "unicode", - "image": "1f1f2-1f1e8.png", - "name": "Monaco" - }, - "🇲🇩": { - "style": "unicode", - "image": "1f1f2-1f1e9.png", - "name": "Moldova" - }, - "🇲🇪": { - "style": "unicode", - "image": "1f1f2-1f1ea.png", - "name": "Montenegro" - }, - "🇲🇫": { - "style": "unicode", - "image": "1f1f2-1f1eb.png", - "name": "Saint Martin" - }, - ":flag-xk:": { - "style": "github", - "image": "1f1fd-1f1f0.png", - "unicode": "🇽🇰", - "name": "Kosovo" - }, - ":woman-tone1:": { - "style": "github", - "image": "1f469-1f3fb.png", - "unicode": "👩🏻", - "name": "Woman - Tone 1" - }, - ":boy-tone3:": { - "style": "github", - "image": "1f466-1f3fd.png", - "unicode": "👦🏽", - "name": "Boy - Tone 3" - }, - ":small-blue-diamond:": { - "style": "github", - "image": "1f539.png", - "unicode": "🔹", - "name": "Small Blue Diamond" - }, - ":flag_pk:": { - "style": "github", - "image": "1f1f5-1f1f0.png", - "unicode": "🇵🇰", - "name": "Pakistan" - }, - "🇶": { - "style": "unicode", - "image": "1f1f6.png", - "name": "Regional Indicator Symbol Letter Q" - }, - ":no-bicycles:": { - "style": "github", - "image": "1f6b3.png", - "unicode": "🚳", - "name": "No Bicycles" - }, - ":man_in_tuxedo_tone3:": { - "style": "github", - "image": "1f935-1f3fd.png", - "unicode": "🤵🏽", - "name": "Man In Tuxedo - Tone 3" - }, - ":flag_mp:": { - "style": "github", - "image": "1f1f2-1f1f5.png", - "unicode": "🇲🇵", - "name": "Northern Mariana Islands" - }, - ":key2:": { - "style": "github", - "image": "1f5dd.png", - "unicode": "🗝", - "name": "Old Key" - }, - ":nail-care-tone4:": { - "style": "github", - "image": "1f485-1f3fe.png", - "unicode": "💅🏾", - "name": "Nail Polish - Tone 4" - }, - ":family_mwgb:": { - "style": "github", - "image": "1f468-1f469-1f467-1f466.png", - "unicode": "👨👩👧👦", - "name": "Family (man,woman,girl,boy)" - }, - ":bn:": { - "style": "github", - "image": "1f1e7-1f1f3.png", - "unicode": "🇧🇳", - "name": "Brunei" - }, - ":flag_cy:": { - "style": "github", - "image": "1f1e8-1f1fe.png", - "unicode": "🇨🇾", - "name": "Cyprus" - }, - ":flag_ru:": { - "style": "github", - "image": "1f1f7-1f1fa.png", - "unicode": "🇷🇺", - "name": "Russia" - }, - ":flag_xk:": { - "style": "github", - "image": "1f1fd-1f1f0.png", - "unicode": "🇽🇰", - "name": "Kosovo" - }, - ":statue-of-liberty:": { - "style": "github", - "image": "1f5fd.png", - "unicode": "🗽", - "name": "Statue Of Liberty" - }, - ":family_mmgb:": { - "style": "github", - "image": "1f468-1f468-1f467-1f466.png", - "unicode": "👨👨👧👦", - "name": "Family (man,man,girl,boy)" - }, - "💹": { - "style": "unicode", - "image": "1f4b9.png", - "name": "Chart With Upwards Trend And Yen Sign" - }, - ":tc:": { - "style": "github", - "image": "1f1f9-1f1e8.png", - "unicode": "🇹🇨", - "name": "Turks And Caicos Islands" - }, - "❄": { - "style": "unicode", - "image": "2744.png", - "name": "Snowflake" - }, - ":shrug_tone5:": { - "style": "github", - "image": "1f937-1f3ff.png", - "unicode": "🤷🏿", - "name": "Shrug - Tone 5" - }, - "🇳🇺": { - "style": "unicode", - "image": "1f1f3-1f1fa.png", - "name": "Niue" - }, - ":shrimp:": { - "style": "github", - "image": "1f990.png", - "unicode": "🦐", - "name": "Shrimp" - }, - "👎": { - "style": "unicode", - "image": "1f44e.png", - "name": "Thumbs Down Sign" - }, - ":camel:": { - "style": "github", - "image": "1f42b.png", - "unicode": "🐫", - "name": "Bactrian Camel" - }, - ":flag-it:": { - "style": "github", - "image": "1f1ee-1f1f9.png", - "unicode": "🇮🇹", - "name": "Italy" - }, - ":flag-cf:": { - "style": "github", - "image": "1f1e8-1f1eb.png", - "unicode": "🇨🇫", - "name": "Central African Republic" - }, - ":construction-worker-tone2:": { - "style": "github", - "image": "1f477-1f3fc.png", - "unicode": "👷🏼", - "name": "Construction Worker - Tone 2" - }, - "🗣": { - "style": "unicode", - "image": "1f5e3.png", - "name": "Speaking Head In Silhouette" - }, - ":three:": { - "style": "github", - "image": "0033-20e3.png", - "unicode": "3⃣", - "name": "Keycap Digit Three" - }, - ":baby-tone5:": { - "style": "github", - "image": "1f476-1f3ff.png", - "unicode": "👶🏿", - "name": "Baby - Tone 5" - }, - ":stuck-out-tongue-closed-eyes:": { - "style": "github", - "image": "1f61d.png", - "unicode": "😝", - "name": "Face With Stuck-out Tongue And Tightly-closed Eyes" - }, - ":angel_tone2:": { - "style": "github", - "image": "1f47c-1f3fc.png", - "unicode": "👼🏼", - "name": "Baby Angel - Tone 2" - }, - "🕸": { - "style": "unicode", - "image": "1f578.png", - "name": "Spider Web" - }, - ":dancer_tone2:": { - "style": "github", - "image": "1f483-1f3fc.png", - "unicode": "💃🏼", - "name": "Dancer - Tone 2" - }, - "🤝🏿": { - "style": "unicode", - "image": "1f91d-1f3ff.png", - "name": "Handshake - Tone 5" - }, - ":ok_hand_tone3:": { - "style": "github", - "image": "1f44c-1f3fd.png", - "unicode": "👌🏽", - "name": "Ok Hand Sign - Tone 3" - }, - "⬇": { - "style": "unicode", - "image": "2b07.png", - "name": "Downwards Black Arrow" - }, - "🤝🏻": { - "style": "unicode", - "image": "1f91d-1f3fb.png", - "name": "Handshake - Tone 1" - }, - ":flag_tg:": { - "style": "github", - "image": "1f1f9-1f1ec.png", - "unicode": "🇹🇬", - "name": "Togo" - }, - ":card_index:": { - "style": "github", - "image": "1f4c7.png", - "unicode": "📇", - "name": "Card Index" - }, - ":straight-ruler:": { - "style": "github", - "image": "1f4cf.png", - "unicode": "📏", - "name": "Straight Ruler" - }, - ":chart_with_downwards_trend:": { - "style": "github", - "image": "1f4c9.png", - "unicode": "📉", - "name": "Chart With Downwards Trend" - }, - ":call_me_hand_tone3:": { - "style": "github", - "image": "1f919-1f3fd.png", - "unicode": "🤙🏽", - "name": "Call Me Hand - Tone 3" - }, - ":owl:": { - "style": "github", - "image": "1f989.png", - "unicode": "🦉", - "name": "Owl" - }, - "🇳🇨": { - "style": "unicode", - "image": "1f1f3-1f1e8.png", - "name": "New Caledonia" - }, - "🌷": { - "style": "unicode", - "image": "1f337.png", - "name": "Tulip" - }, - ":runner-tone4:": { - "style": "github", - "image": "1f3c3-1f3fe.png", - "unicode": "🏃🏾", - "name": "Runner - Tone 4" - }, - ":first_quarter_moon_with_face:": { - "style": "github", - "image": "1f31b.png", - "unicode": "🌛", - "name": "First Quarter Moon With Face" - }, - ":flag_je:": { - "style": "github", - "image": "1f1ef-1f1ea.png", - "unicode": "🇯🇪", - "name": "Jersey" - }, - ":military_medal:": { - "style": "github", - "image": "1f396.png", - "unicode": "🎖", - "name": "Military Medal" - }, - ":flag-yt:": { - "style": "github", - "image": "1f1fe-1f1f9.png", - "unicode": "🇾🇹", - "name": "Mayotte" - }, - ":flag-hn:": { - "style": "github", - "image": "1f1ed-1f1f3.png", - "unicode": "🇭🇳", - "name": "Honduras" - }, - ":trumpet:": { - "style": "github", - "image": "1f3ba.png", - "unicode": "🎺", - "name": "Trumpet" - }, - "🏌": { - "style": "unicode", - "image": "1f3cc.png", - "name": "Golfer" - }, - "🤶🏻": { - "style": "unicode", - "image": "1f936-1f3fb.png", - "name": "Mother Christmas - Tone 1" - }, - "🤶🏼": { - "style": "unicode", - "image": "1f936-1f3fc.png", - "name": "Mother Christmas - Tone 2" - }, - "🤶🏽": { - "style": "unicode", - "image": "1f936-1f3fd.png", - "name": "Mother Christmas - Tone 3" - }, - "🤶🏾": { - "style": "unicode", - "image": "1f936-1f3fe.png", - "name": "Mother Christmas - Tone 4" - }, - "🤶🏿": { - "style": "unicode", - "image": "1f936-1f3ff.png", - "name": "Mother Christmas - Tone 5" - }, - "⏬": { - "style": "unicode", - "image": "23ec.png", - "name": "Black Down-pointing Double Triangle" - }, - ":flag-va:": { - "style": "github", - "image": "1f1fb-1f1e6.png", - "unicode": "🇻🇦", - "name": "The Vatican City" - }, - ":atom:": { - "style": "github", - "image": "269b.png", - "unicode": "⚛", - "name": "Atom Symbol" - }, - ":middle_finger:": { - "style": "github", - "image": "1f595.png", - "unicode": "🖕", - "name": "Reversed Hand With Middle Finger Extended" - }, - ":smiling_imp:": { - "style": "github", - "image": "1f608.png", - "unicode": "😈", - "name": "Smiling Face With Horns" - }, - "💏": { - "style": "unicode", - "image": "1f48f.png", - "name": "Kiss" - }, - ":bath-tone2:": { - "style": "github", - "image": "1f6c0-1f3fc.png", - "unicode": "🛀🏼", - "name": "Bath - Tone 2" - }, - "🐤": { - "style": "unicode", - "image": "1f424.png", - "name": "Baby Chick" - }, - ":woman_tone1:": { - "style": "github", - "image": "1f469-1f3fb.png", - "unicode": "👩🏻", - "name": "Woman - Tone 1" - }, - ":stew:": { - "style": "github", - "image": "1f372.png", - "unicode": "🍲", - "name": "Pot Of Food" - }, - ":womans_clothes:": { - "style": "github", - "image": "1f45a.png", - "unicode": "👚", - "name": "Womans Clothes" - }, - ":flag_fi:": { - "style": "github", - "image": "1f1eb-1f1ee.png", - "unicode": "🇫🇮", - "name": "Finland" - }, - ":thumbsup-tone4:": { - "style": "github", - "image": "1f44d-1f3fe.png", - "unicode": "👍🏾", - "name": "Thumbs Up Sign - Tone 4" - }, - ":de:": { - "style": "github", - "image": "1f1e9-1f1ea.png", - "unicode": "🇩🇪", - "name": "Germany" - }, - ":ms:": { - "style": "github", - "image": "1f1f2-1f1f8.png", - "unicode": "🇲🇸", - "name": "Montserrat" - }, - "🥒": { - "style": "unicode", - "image": "1f952.png", - "name": "Cucumber" - }, - ":ci:": { - "style": "github", - "image": "1f1e8-1f1ee.png", - "unicode": "🇨🇮", - "name": "Côte D’ivoire" - }, - ":kiss-mm:": { - "style": "github", - "image": "1f468-2764-1f48b-1f468.png", - "unicode": "👨❤💋👨", - "name": "Kiss (man,man)" - }, - ":point-up-tone5:": { - "style": "github", - "image": "261d-1f3ff.png", - "unicode": "☝🏿", - "name": "White Up Pointing Index - Tone 5" - }, - "👉🏻": { - "style": "unicode", - "image": "1f449-1f3fb.png", - "name": "White Right Pointing Backhand Index - Tone 1" - }, - "👉🏽": { - "style": "unicode", - "image": "1f449-1f3fd.png", - "name": "White Right Pointing Backhand Index - Tone 3" - }, - "👉🏼": { - "style": "unicode", - "image": "1f449-1f3fc.png", - "name": "White Right Pointing Backhand Index - Tone 2" - }, - ":flag-ua:": { - "style": "github", - "image": "1f1fa-1f1e6.png", - "unicode": "🇺🇦", - "name": "Ukraine" - }, - ":rewind:": { - "style": "github", - "image": "23ea.png", - "unicode": "⏪", - "name": "Black Left-pointing Double Triangle" - }, - "🤛🏻": { - "style": "unicode", - "image": "1f91b-1f3fb.png", - "name": "Left Facing Fist - Tone 1" - }, - "🤛🏿": { - "style": "unicode", - "image": "1f91b-1f3ff.png", - "name": "Left Facing Fist - Tone 5" - }, - "🤛🏾": { - "style": "unicode", - "image": "1f91b-1f3fe.png", - "name": "Left Facing Fist - Tone 4" - }, - "🤛🏽": { - "style": "unicode", - "image": "1f91b-1f3fd.png", - "name": "Left Facing Fist - Tone 3" - }, - "🤛🏼": { - "style": "unicode", - "image": "1f91b-1f3fc.png", - "name": "Left Facing Fist - Tone 2" - }, - ":airplane:": { - "style": "github", - "image": "2708.png", - "unicode": "✈", - "name": "Airplane" - }, - ":spider-web:": { - "style": "github", - "image": "1f578.png", - "unicode": "🕸", - "name": "Spider Web" - }, - ":anger:": { - "style": "github", - "image": "1f4a2.png", - "unicode": "💢", - "name": "Anger Symbol" - }, - "☃": { - "style": "unicode", - "image": "2603.png", - "name": "Snowman" - }, - ":haircut-tone5:": { - "style": "github", - "image": "1f487-1f3ff.png", - "unicode": "💇🏿", - "name": "Haircut - Tone 5" - }, - "⏭": { - "style": "unicode", - "image": "23ed.png", - "name": "Black Right-pointing Double Triangle With Vertical Bar" - }, - ":dark_sunglasses:": { - "style": "github", - "image": "1f576.png", - "unicode": "🕶", - "name": "Dark Sunglasses" - }, - ":ki:": { - "style": "github", - "image": "1f1f0-1f1ee.png", - "unicode": "🇰🇮", - "name": "Kiribati" - }, - ":avocado:": { - "style": "github", - "image": "1f951.png", - "unicode": "🥑", - "name": "Avocado" - }, - ":raised_hands_tone4:": { - "style": "github", - "image": "1f64c-1f3fe.png", - "unicode": "🙌🏾", - "name": "Person Raising Both Hands In Celebration - Tone 4" - }, - ":train:": { - "style": "github", - "image": "1f68b.png", - "unicode": "🚋", - "name": "Tram Car" - }, - ":airplane-arriving:": { - "style": "github", - "image": "1f6ec.png", - "unicode": "🛬", - "name": "Airplane Arriving" - }, - ":guardsman_tone4:": { - "style": "github", - "image": "1f482-1f3fe.png", - "unicode": "💂🏾", - "name": "Guardsman - Tone 4" - }, - ":earth_asia:": { - "style": "github", - "image": "1f30f.png", - "unicode": "🌏", - "name": "Earth Globe Asia-australia" - }, - ":custard:": { - "style": "github", - "image": "1f36e.png", - "unicode": "🍮", - "name": "Custard" - }, - ":middle-finger:": { - "style": "github", - "image": "1f595.png", - "unicode": "🖕", - "name": "Reversed Hand With Middle Finger Extended" - }, - "🍡": { - "style": "unicode", - "image": "1f361.png", - "name": "Dango" - }, - ":flag-pn:": { - "style": "github", - "image": "1f1f5-1f1f3.png", - "unicode": "🇵🇳", - "name": "Pitcairn" - }, - "🕥": { - "style": "unicode", - "image": "1f565.png", - "name": "Clock Face Ten-thirty" - }, - ":v_tone4:": { - "style": "github", - "image": "270c-1f3fe.png", - "unicode": "✌🏾", - "name": "Victory Hand - Tone 4" - }, - ":flag_ni:": { - "style": "github", - "image": "1f1f3-1f1ee.png", - "unicode": "🇳🇮", - "name": "Nicaragua" - }, - ":flag-lr:": { - "style": "github", - "image": "1f1f1-1f1f7.png", - "unicode": "🇱🇷", - "name": "Liberia" - }, - "🗺": { - "style": "unicode", - "image": "1f5fa.png", - "name": "World Map" - }, - ":bear:": { - "style": "github", - "image": "1f43b.png", - "unicode": "🐻", - "name": "Bear Face" - }, - "🐪": { - "style": "unicode", - "image": "1f42a.png", - "name": "Dromedary Camel" - }, - ":hand_splayed_tone1:": { - "style": "github", - "image": "1f590-1f3fb.png", - "unicode": "🖐🏻", - "name": "Raised Hand With Fingers Splayed - Tone 1" - }, - "🎋": { - "style": "unicode", - "image": "1f38b.png", - "name": "Tanabata Tree" - }, - ":vibration-mode:": { - "style": "github", - "image": "1f4f3.png", - "unicode": "📳", - "name": "Vibration Mode" - }, - ":no_mouth:": { - "style": "github", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":outbox-tray:": { - "style": "github", - "image": "1f4e4.png", - "unicode": "📤", - "name": "Outbox Tray" - }, - "🌠": { - "style": "unicode", - "image": "1f320.png", - "name": "Shooting Star" - }, - "🔤": { - "style": "unicode", - "image": "1f524.png", - "name": "Input Symbol For Latin Letters" - }, - ":raised-back-of-hand:": { - "style": "github", - "image": "1f91a.png", - "unicode": "🤚", - "name": "Raised Back Of Hand" - }, - ":man_tone1:": { - "style": "github", - "image": "1f468-1f3fb.png", - "unicode": "👨🏻", - "name": "Man - Tone 1" - }, - "🚹": { - "style": "unicode", - "image": "1f6b9.png", - "name": "Mens Symbol" - }, - ":mother_christmas_tone3:": { - "style": "github", - "image": "1f936-1f3fd.png", - "unicode": "🤶🏽", - "name": "Mother Christmas - Tone 3" - }, - "🤼🏾": { - "style": "unicode", - "image": "1f93c-1f3fe.png", - "name": "Wrestlers - Tone 4" - }, - "🤼🏿": { - "style": "unicode", - "image": "1f93c-1f3ff.png", - "name": "Wrestlers - Tone 5" - }, - "🤼🏼": { - "style": "unicode", - "image": "1f93c-1f3fc.png", - "name": "Wrestlers - Tone 2" - }, - "🤼🏽": { - "style": "unicode", - "image": "1f93c-1f3fd.png", - "name": "Wrestlers - Tone 3" - }, - "🤼🏻": { - "style": "unicode", - "image": "1f93c-1f3fb.png", - "name": "Wrestlers - Tone 1" - }, - "🙎": { - "style": "unicode", - "image": "1f64e.png", - "name": "Person With Pouting Face" - }, - ":tone1:": { - "style": "github", - "image": "1f3fb.png", - "unicode": "🏻", - "name": "Emoji Modifier Fitzpatrick Type-1-2" - }, - ":satellite-orbital:": { - "style": "github", - "image": "1f6f0.png", - "unicode": "🛰", - "name": "Satellite" - }, - ":mag:": { - "style": "github", - "image": "1f50d.png", - "unicode": "🔍", - "name": "Left-pointing Magnifying Glass" - }, - ":third-place:": { - "style": "github", - "image": "1f949.png", - "unicode": "🥉", - "name": "Third Place Medal" - }, - ":couple-with-heart:": { - "style": "github", - "image": "1f491.png", - "unicode": "💑", - "name": "Couple With Heart" - }, - ":vn:": { - "style": "github", - "image": "1f1fb-1f1f3.png", - "unicode": "🇻🇳", - "name": "Vietnam" - }, - ":regional_indicator_c:": { - "style": "github", - "image": "1f1e8.png", - "unicode": "🇨", - "name": "Regional Indicator Symbol Letter C" - }, - ":glass_of_milk:": { - "style": "github", - "image": "1f95b.png", - "unicode": "🥛", - "name": "Glass Of Milk" - }, - ":mrs-claus-tone2:": { - "style": "github", - "image": "1f936-1f3fc.png", - "unicode": "🤶🏼", - "name": "Mother Christmas - Tone 2" - }, - "O_O": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":joy_cat:": { - "style": "github", - "image": "1f639.png", - "unicode": "😹", - "name": "Cat Face With Tears Of Joy" - }, - ":sake:": { - "style": "github", - "image": "1f376.png", - "unicode": "🍶", - "name": "Sake Bottle And Cup" - }, - ":ballot-box-with-check:": { - "style": "github", - "image": "2611.png", - "unicode": "☑", - "name": "Ballot Box With Check" - }, - ":scissors:": { - "style": "github", - "image": "2702.png", - "unicode": "✂", - "name": "Black Scissors" - }, - ":b:": { - "style": "github", - "image": "1f171.png", - "unicode": "🅱", - "name": "Negative Squared Latin Capital Letter B" - }, - ":flag-mu:": { - "style": "github", - "image": "1f1f2-1f1fa.png", - "unicode": "🇲🇺", - "name": "Mauritius" - }, - "👥": { - "style": "unicode", - "image": "1f465.png", - "name": "Busts In Silhouette" - }, - ":railroad_track:": { - "style": "github", - "image": "1f6e4.png", - "unicode": "🛤", - "name": "Railway Track" - }, - ":face-palm-tone3:": { - "style": "github", - "image": "1f926-1f3fd.png", - "unicode": "🤦🏽", - "name": "Face Palm - Tone 3" - }, - ":busstop:": { - "style": "github", - "image": "1f68f.png", - "unicode": "🚏", - "name": "Bus Stop" - }, - ":no:": { - "style": "github", - "image": "1f1f3-1f1f4.png", - "unicode": "🇳🇴", - "name": "Norway" - }, - "📺": { - "style": "unicode", - "image": "1f4fa.png", - "name": "Television" - }, - ":prince_tone4:": { - "style": "github", - "image": "1f934-1f3fe.png", - "unicode": "🤴🏾", - "name": "Prince - Tone 4" - }, - ":vulcan_tone5:": { - "style": "github", - "image": "1f596-1f3ff.png", - "unicode": "🖖🏿", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 5" - }, - "💋": { - "style": "unicode", - "image": "1f48b.png", - "name": "Kiss Mark" - }, - "🙌🏾": { - "style": "unicode", - "image": "1f64c-1f3fe.png", - "name": "Person Raising Both Hands In Celebration - Tone 4" - }, - "🙌🏿": { - "style": "unicode", - "image": "1f64c-1f3ff.png", - "name": "Person Raising Both Hands In Celebration - Tone 5" - }, - "🙌🏼": { - "style": "unicode", - "image": "1f64c-1f3fc.png", - "name": "Person Raising Both Hands In Celebration - Tone 2" - }, - "⌚": { - "style": "unicode", - "image": "231a.png", - "name": "Watch" - }, - "🙌🏻": { - "style": "unicode", - "image": "1f64c-1f3fb.png", - "name": "Person Raising Both Hands In Celebration - Tone 1" - }, - "🐠": { - "style": "unicode", - "image": "1f420.png", - "name": "Tropical Fish" - }, - ":raising-hand-tone3:": { - "style": "github", - "image": "1f64b-1f3fd.png", - "unicode": "🙋🏽", - "name": "Happy Person Raising One Hand Tone3" - }, - ":flag-ee:": { - "style": "github", - "image": "1f1ea-1f1ea.png", - "unicode": "🇪🇪", - "name": "Estonia" - }, - ":bow_tone2:": { - "style": "github", - "image": "1f647-1f3fc.png", - "unicode": "🙇🏼", - "name": "Person Bowing Deeply - Tone 2" - }, - ":man_with_turban_tone3:": { - "style": "github", - "image": "1f473-1f3fd.png", - "unicode": "👳🏽", - "name": "Man With Turban - Tone 3" - }, - ":dress:": { - "style": "github", - "image": "1f457.png", - "unicode": "👗", - "name": "Dress" - }, - ":white_large_square:": { - "style": "github", - "image": "2b1c.png", - "unicode": "⬜", - "name": "White Large Square" - }, - ":baby_chick:": { - "style": "github", - "image": "1f424.png", - "unicode": "🐤", - "name": "Baby Chick" - }, - ":sunrise-over-mountains:": { - "style": "github", - "image": "1f304.png", - "unicode": "🌄", - "name": "Sunrise Over Mountains" - }, - ":water-polo:": { - "style": "github", - "image": "1f93d.png", - "unicode": "🤽", - "name": "Water Polo" - }, - ":unicorn:": { - "style": "github", - "image": "1f984.png", - "unicode": "🦄", - "name": "Unicorn Face" - }, - ":az:": { - "style": "github", - "image": "1f1e6-1f1ff.png", - "unicode": "🇦🇿", - "name": "Azerbaijan" - }, - ":star_and_crescent:": { - "style": "github", - "image": "262a.png", - "unicode": "☪", - "name": "Star And Crescent" - }, - ":pe:": { - "style": "github", - "image": "1f1f5-1f1ea.png", - "unicode": "🇵🇪", - "name": "Peru" - }, - ":love_hotel:": { - "style": "github", - "image": "1f3e9.png", - "unicode": "🏩", - "name": "Love Hotel" - }, - ":flag-ai:": { - "style": "github", - "image": "1f1e6-1f1ee.png", - "unicode": "🇦🇮", - "name": "Anguilla" - }, - ":head_bandage:": { - "style": "github", - "image": "1f915.png", - "unicode": "🤕", - "name": "Face With Head-bandage" - }, - ":runner_tone1:": { - "style": "github", - "image": "1f3c3-1f3fb.png", - "unicode": "🏃🏻", - "name": "Runner - Tone 1" - }, - "😷": { - "style": "unicode", - "image": "1f637.png", - "name": "Face With Medical Mask" - }, - ":european_post_office:": { - "style": "github", - "image": "1f3e4.png", - "unicode": "🏤", - "name": "European Post Office" - }, - ":cloud-rain:": { - "style": "github", - "image": "1f327.png", - "unicode": "🌧", - "name": "Cloud With Rain" - }, - ":black-medium-small-square:": { - "style": "github", - "image": "25fe.png", - "unicode": "◾", - "name": "Black Medium Small Square" - }, - "🛌": { - "style": "unicode", - "image": "1f6cc.png", - "name": "Sleeping Accommodation" - }, - ":regional-indicator-d:": { - "style": "github", - "image": "1f1e9.png", - "unicode": "🇩", - "name": "Regional Indicator Symbol Letter D" - }, - ":sign_of_the_horns_tone1:": { - "style": "github", - "image": "1f918-1f3fb.png", - "unicode": "🤘🏻", - "name": "Sign Of The Horns - Tone 1" - }, - ":mountain_snow:": { - "style": "github", - "image": "1f3d4.png", - "unicode": "🏔", - "name": "Snow Capped Mountain" - }, - "🕡": { - "style": "unicode", - "image": "1f561.png", - "name": "Clock Face Six-thirty" - }, - "🍥": { - "style": "unicode", - "image": "1f365.png", - "name": "Fish Cake With Swirl Design" - }, - ":menorah:": { - "style": "github", - "image": "1f54e.png", - "unicode": "🕎", - "name": "Menorah With Nine Branches" - }, - ":white-sun-rain-cloud:": { - "style": "github", - "image": "1f326.png", - "unicode": "🌦", - "name": "White Sun Behind Cloud With Rain" - }, - ":zipper_mouth_face:": { - "style": "github", - "image": "1f910.png", - "unicode": "🤐", - "name": "Zipper-mouth Face" - }, - "🏺": { - "style": "unicode", - "image": "1f3fa.png", - "name": "Amphora" - }, - "🖋": { - "style": "unicode", - "image": "1f58b.png", - "name": "Lower Left Fountain Pen" - }, - ":heart-eyes:": { - "style": "github", - "image": "1f60d.png", - "unicode": "😍", - "name": "Smiling Face With Heart-shaped Eyes" - }, - "🎏": { - "style": "unicode", - "image": "1f38f.png", - "name": "Carp Streamer" - }, - ":call_me_hand:": { - "style": "github", - "image": "1f919.png", - "unicode": "🤙", - "name": "Call Me Hand" - }, - "🔠": { - "style": "unicode", - "image": "1f520.png", - "name": "Input Symbol For Latin Capital Letters" - }, - "🌤": { - "style": "unicode", - "image": "1f324.png", - "name": "White Sun With Small Cloud" - }, - "▫": { - "style": "unicode", - "image": "25ab.png", - "name": "White Small Square" - }, - ":ok-hand-tone4:": { - "style": "github", - "image": "1f44c-1f3fe.png", - "unicode": "👌🏾", - "name": "Ok Hand Sign - Tone 4" - }, - "💃🏻": { - "style": "unicode", - "image": "1f483-1f3fb.png", - "name": "Dancer - Tone 1" - }, - ":small_orange_diamond:": { - "style": "github", - "image": "1f538.png", - "unicode": "🔸", - "name": "Small Orange Diamond" - }, - "💃🏿": { - "style": "unicode", - "image": "1f483-1f3ff.png", - "name": "Dancer - Tone 5" - }, - "💃🏾": { - "style": "unicode", - "image": "1f483-1f3fe.png", - "name": "Dancer - Tone 4" - }, - "💃🏽": { - "style": "unicode", - "image": "1f483-1f3fd.png", - "name": "Dancer - Tone 3" - }, - "💃🏼": { - "style": "unicode", - "image": "1f483-1f3fc.png", - "name": "Dancer - Tone 2" - }, - ":small-orange-diamond:": { - "style": "github", - "image": "1f538.png", - "unicode": "🔸", - "name": "Small Orange Diamond" - }, - ":musical-score:": { - "style": "github", - "image": "1f3bc.png", - "unicode": "🎼", - "name": "Musical Score" - }, - ":bridge_at_night:": { - "style": "github", - "image": "1f309.png", - "unicode": "🌉", - "name": "Bridge At Night" - }, - ":seven:": { - "style": "github", - "image": "0037-20e3.png", - "unicode": "7⃣", - "name": "Keycap Digit Seven" - }, - ":boy-tone1:": { - "style": "github", - "image": "1f466-1f3fb.png", - "unicode": "👦🏻", - "name": "Boy - Tone 1" - }, - ":cherry-blossom:": { - "style": "github", - "image": "1f338.png", - "unicode": "🌸", - "name": "Cherry Blossom" - }, - ":wine-glass:": { - "style": "github", - "image": "1f377.png", - "unicode": "🍷", - "name": "Wine Glass" - }, - ":bulb:": { - "style": "github", - "image": "1f4a1.png", - "unicode": "💡", - "name": "Electric Light Bulb" - }, - ":flag_mr:": { - "style": "github", - "image": "1f1f2-1f1f7.png", - "unicode": "🇲🇷", - "name": "Mauritania" - }, - "😍": { - "style": "unicode", - "image": "1f60d.png", - "name": "Smiling Face With Heart-shaped Eyes" - }, - "↘": { - "style": "unicode", - "image": "2198.png", - "name": "South East Arrow" - }, - ":bh:": { - "style": "github", - "image": "1f1e7-1f1ed.png", - "unicode": "🇧🇭", - "name": "Bahrain" - }, - "🚢": { - "style": "unicode", - "image": "1f6a2.png", - "name": "Ship" - }, - ":flag_ua:": { - "style": "github", - "image": "1f1fa-1f1e6.png", - "unicode": "🇺🇦", - "name": "Ukraine" - }, - ":flag_rw:": { - "style": "github", - "image": "1f1f7-1f1fc.png", - "unicode": "🇷🇼", - "name": "Rwanda" - }, - "®": { - "style": "unicode", - "image": "00ae.png", - "name": "Registered Sign" - }, - ":ta:": { - "style": "github", - "image": "1f1f9-1f1e6.png", - "unicode": "🇹🇦", - "name": "Tristan Da Cunha" - }, - ":man-tone2:": { - "style": "github", - "image": "1f468-1f3fc.png", - "unicode": "👨🏼", - "name": "Man - Tone 2" - }, - ":thumbsdown-tone5:": { - "style": "github", - "image": "1f44e-1f3ff.png", - "unicode": "👎🏿", - "name": "Thumbs Down Sign - Tone 5" - }, - "❗": { - "style": "unicode", - "image": "2757.png", - "name": "Heavy Exclamation Mark Symbol" - }, - "👡": { - "style": "unicode", - "image": "1f461.png", - "name": "Womans Sandal" - }, - "🙇🏿": { - "style": "unicode", - "image": "1f647-1f3ff.png", - "name": "Person Bowing Deeply - Tone 5" - }, - "🙇🏾": { - "style": "unicode", - "image": "1f647-1f3fe.png", - "name": "Person Bowing Deeply - Tone 4" - }, - ":reminder_ribbon:": { - "style": "github", - "image": "1f397.png", - "unicode": "🎗", - "name": "Reminder Ribbon" - }, - "🙇🏼": { - "style": "unicode", - "image": "1f647-1f3fc.png", - "name": "Person Bowing Deeply - Tone 2" - }, - "🙇🏻": { - "style": "unicode", - "image": "1f647-1f3fb.png", - "name": "Person Bowing Deeply - Tone 1" - }, - "📶": { - "style": "unicode", - "image": "1f4f6.png", - "name": "Antenna With Bars" - }, - ":raising-hand-tone5:": { - "style": "github", - "image": "1f64b-1f3ff.png", - "unicode": "🙋🏿", - "name": "Happy Person Raising One Hand Tone5" - }, - ":play-pause:": { - "style": "github", - "image": "23ef.png", - "unicode": "⏯", - "name": "Black Right-pointing Double Triangle With Double Vertical Bar" - }, - "🚋": { - "style": "unicode", - "image": "1f68b.png", - "name": "Tram Car" - }, - ":head-bandage:": { - "style": "github", - "image": "1f915.png", - "unicode": "🤕", - "name": "Face With Head-bandage" - }, - "✖": { - "style": "unicode", - "image": "2716.png", - "name": "Heavy Multiplication X" - }, - ":white-sun-cloud:": { - "style": "github", - "image": "1f325.png", - "unicode": "🌥", - "name": "White Sun Behind Cloud" - }, - ":flag_bw:": { - "style": "github", - "image": "1f1e7-1f1fc.png", - "unicode": "🇧🇼", - "name": "Botswana" - }, - "😠": { - "style": "unicode", - "ascii": ">:(", - "image": "1f620.png", - "name": "Angry Face" - }, - "⚫": { - "style": "unicode", - "image": "26ab.png", - "name": "Medium Black Circle" - }, - "-__-": { - "style": "ascii", - "ascii": "-_-", - "image": "1f611.png", - "unicode": "😑", - "name": "Expressionless Face" - }, - ":muscle-tone3:": { - "style": "github", - "image": "1f4aa-1f3fd.png", - "unicode": "💪🏽", - "name": "Flexed Biceps - Tone 3" - }, - "🎹": { - "style": "unicode", - "image": "1f3b9.png", - "name": "Musical Keyboard" - }, - ":hibiscus:": { - "style": "github", - "image": "1f33a.png", - "unicode": "🌺", - "name": "Hibiscus" - }, - "🍎": { - "style": "unicode", - "image": "1f34e.png", - "name": "Red Apple" - }, - ":abc:": { - "style": "github", - "image": "1f524.png", - "unicode": "🔤", - "name": "Input Symbol For Latin Letters" - }, - ":pencil2:": { - "style": "github", - "image": "270f.png", - "unicode": "✏", - "name": "Pencil" - }, - ":person-with-pouting-face-tone1:": { - "style": "github", - "image": "1f64e-1f3fb.png", - "unicode": "🙎🏻", - "name": "Person With Pouting Face Tone1" - }, - ":balloon:": { - "style": "github", - "image": "1f388.png", - "unicode": "🎈", - "name": "Balloon" - }, - "-_-": { - "style": "ascii", - "ascii": "-_-", - "image": "1f611.png", - "unicode": "😑", - "name": "Expressionless Face" - }, - ":metal-tone2:": { - "style": "github", - "image": "1f918-1f3fc.png", - "unicode": "🤘🏼", - "name": "Sign Of The Horns - Tone 2" - }, - ":ok_hand_tone1:": { - "style": "github", - "image": "1f44c-1f3fb.png", - "unicode": "👌🏻", - "name": "Ok Hand Sign - Tone 1" - }, - ":flag-vg:": { - "style": "github", - "image": "1f1fb-1f1ec.png", - "unicode": "🇻🇬", - "name": "British Virgin Islands" - }, - ":fast-forward:": { - "style": "github", - "image": "23e9.png", - "unicode": "⏩", - "name": "Black Right-pointing Double Triangle" - }, - "🔍": { - "style": "unicode", - "image": "1f50d.png", - "name": "Left-pointing Magnifying Glass" - }, - "🤕": { - "style": "unicode", - "image": "1f915.png", - "name": "Face With Head-bandage" - }, - ":male_dancer_tone5:": { - "style": "github", - "image": "1f57a-1f3ff.png", - "unicode": "🕺🏿", - "name": "Man Dancing - Tone 5" - }, - ":bride_with_veil_tone5:": { - "style": "github", - "image": "1f470-1f3ff.png", - "unicode": "👰🏿", - "name": "Bride With Veil - Tone 5" - }, - ":evergreen_tree:": { - "style": "github", - "image": "1f332.png", - "unicode": "🌲", - "name": "Evergreen Tree" - }, - ":pushpin:": { - "style": "github", - "image": "1f4cc.png", - "unicode": "📌", - "name": "Pushpin" - }, - "⤵": { - "style": "unicode", - "image": "2935.png", - "name": "Arrow Pointing Rightwards Then Curving Downwards" - }, - "🐷": { - "style": "unicode", - "image": "1f437.png", - "name": "Pig Face" - }, - ":right_fist_tone1:": { - "style": "github", - "image": "1f91c-1f3fb.png", - "unicode": "🤜🏻", - "name": "Right Facing Fist - Tone 1" - }, - "5⃣": { - "style": "unicode", - "image": "0035-20e3.png", - "name": "Keycap Digit Five" - }, - ":keycap_asterisk:": { - "style": "github", - "image": "002a-20e3.png", - "unicode": "*⃣", - "name": "Keycap Asterisk" - }, - ":man-in-tuxedo-tone4:": { - "style": "github", - "image": "1f935-1f3fe.png", - "unicode": "🤵🏾", - "name": "Man In Tuxedo - Tone 4" - }, - ":mu:": { - "style": "github", - "image": "1f1f2-1f1fa.png", - "unicode": "🇲🇺", - "name": "Mauritius" - }, - "📌": { - "style": "unicode", - "image": "1f4cc.png", - "name": "Pushpin" - }, - ":cw:": { - "style": "github", - "image": "1f1e8-1f1fc.png", - "unicode": "🇨🇼", - "name": "Curaçao" - }, - ":file_folder:": { - "style": "github", - "image": "1f4c1.png", - "unicode": "📁", - "name": "File Folder" - }, - ":flag-mn:": { - "style": "github", - "image": "1f1f2-1f1f3.png", - "unicode": "🇲🇳", - "name": "Mongolia" - }, - "🇺": { - "style": "unicode", - "image": "1f1fa.png", - "name": "Regional Indicator Symbol Letter U" - }, - ":dollar:": { - "style": "github", - "image": "1f4b5.png", - "unicode": "💵", - "name": "Banknote With Dollar Sign" - }, - ":biohazard:": { - "style": "github", - "image": "2623.png", - "unicode": "☣", - "name": "Biohazard Sign" - }, - ":kg:": { - "style": "github", - "image": "1f1f0-1f1ec.png", - "unicode": "🇰🇬", - "name": "Kyrgyzstan" - }, - ":+1_tone3:": { - "style": "github", - "image": "1f44d-1f3fd.png", - "unicode": "👍🏽", - "name": "Thumbs Up Sign - Tone 3" - }, - ":rice_cracker:": { - "style": "github", - "image": "1f358.png", - "unicode": "🍘", - "name": "Rice Cracker" - }, - ":ice-cream:": { - "style": "github", - "image": "1f368.png", - "unicode": "🍨", - "name": "Ice Cream" - }, - ":recycle:": { - "style": "github", - "image": "267b.png", - "unicode": "♻", - "name": "Black Universal Recycling Symbol" - }, - ":flag_lu:": { - "style": "github", - "image": "1f1f1-1f1fa.png", - "unicode": "🇱🇺", - "name": "Luxembourg" - }, - ":flag-nr:": { - "style": "github", - "image": "1f1f3-1f1f7.png", - "unicode": "🇳🇷", - "name": "Nauru" - }, - ":point-right:": { - "style": "github", - "image": "1f449.png", - "unicode": "👉", - "name": "White Right Pointing Backhand Index" - }, - "🏣": { - "style": "unicode", - "image": "1f3e3.png", - "name": "Japanese Post Office" - }, - ":flag-ph:": { - "style": "github", - "image": "1f1f5-1f1ed.png", - "unicode": "🇵🇭", - "name": "The Philippines" - }, - "🍸": { - "style": "unicode", - "image": "1f378.png", - "name": "Cocktail Glass" - }, - ":thumbup_tone1:": { - "style": "github", - "image": "1f44d-1f3fb.png", - "unicode": "👍🏻", - "name": "Thumbs Up Sign - Tone 1" - }, - ":flag-lt:": { - "style": "github", - "image": "1f1f1-1f1f9.png", - "unicode": "🇱🇹", - "name": "Lithuania" - }, - "🐍": { - "style": "unicode", - "image": "1f40d.png", - "name": "Snake" - }, - ":baggage-claim:": { - "style": "github", - "image": "1f6c4.png", - "unicode": "🛄", - "name": "Baggage Claim" - }, - "👩👩👦👦": { - "style": "unicode", - "image": "1f469-1f469-1f466-1f466.png", - "name": "Family (woman,woman,boy,boy)" - }, - ":inbox_tray:": { - "style": "github", - "image": "1f4e5.png", - "unicode": "📥", - "name": "Inbox Tray" - }, - "💢": { - "style": "unicode", - "image": "1f4a2.png", - "name": "Anger Symbol" - }, - ":honey-pot:": { - "style": "github", - "image": "1f36f.png", - "unicode": "🍯", - "name": "Honey Pot" - }, - "🔷": { - "style": "unicode", - "image": "1f537.png", - "name": "Large Blue Diamond" - }, - "🛀🏻": { - "style": "unicode", - "image": "1f6c0-1f3fb.png", - "name": "Bath - Tone 1" - }, - ":mother_christmas_tone1:": { - "style": "github", - "image": "1f936-1f3fb.png", - "unicode": "🤶🏻", - "name": "Mother Christmas - Tone 1" - }, - "🛀🏿": { - "style": "unicode", - "image": "1f6c0-1f3ff.png", - "name": "Bath - Tone 5" - }, - "🛀🏼": { - "style": "unicode", - "image": "1f6c0-1f3fc.png", - "name": "Bath - Tone 2" - }, - "🛀🏽": { - "style": "unicode", - "image": "1f6c0-1f3fd.png", - "name": "Bath - Tone 3" - }, - "Ⓜ": { - "style": "unicode", - "image": "24c2.png", - "name": "Circled Latin Capital Letter M" - }, - ":sj:": { - "style": "github", - "image": "1f1f8-1f1ef.png", - "unicode": "🇸🇯", - "name": "Svalbard And Jan Mayen" - }, - ":milk:": { - "style": "github", - "image": "1f95b.png", - "unicode": "🥛", - "name": "Glass Of Milk" - }, - ":three_button_mouse:": { - "style": "github", - "image": "1f5b1.png", - "unicode": "🖱", - "name": "Three Button Mouse" - }, - ":girl-tone3:": { - "style": "github", - "image": "1f467-1f3fd.png", - "unicode": "👧🏽", - "name": "Girl - Tone 3" - }, - ":sunglasses:": { - "style": "github", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - "🛶": { - "style": "unicode", - "image": "1f6f6.png", - "name": "Canoe" - }, - ":calendar-spiral:": { - "style": "github", - "image": "1f5d3.png", - "unicode": "🗓", - "name": "Spiral Calendar Pad" - }, - ":regional_indicator_a:": { - "style": "github", - "image": "1f1e6.png", - "unicode": "🇦", - "name": "Regional Indicator Symbol Letter A" - }, - ":rofl:": { - "style": "github", - "image": "1f923.png", - "unicode": "🤣", - "name": "Rolling On The Floor Laughing" - }, - "💓": { - "style": "unicode", - "image": "1f493.png", - "name": "Beating Heart" - }, - ":flag-td:": { - "style": "github", - "image": "1f1f9-1f1e9.png", - "unicode": "🇹🇩", - "name": "Chad" - }, - "🏋🏿": { - "style": "unicode", - "image": "1f3cb-1f3ff.png", - "name": "Weight Lifter - Tone 5" - }, - ":sandal:": { - "style": "github", - "image": "1f461.png", - "unicode": "👡", - "name": "Womans Sandal" - }, - "🎽": { - "style": "unicode", - "image": "1f3bd.png", - "name": "Running Shirt With Sash" - }, - ":open_hands:": { - "style": "github", - "image": "1f450.png", - "unicode": "👐", - "name": "Open Hands Sign" - }, - "🍒": { - "style": "unicode", - "image": "1f352.png", - "name": "Cherries" - }, - "👨👩👧": { - "style": "unicode", - "image": "1f468-1f469-1f467.png", - "name": "Family (man,woman,girl)" - }, - ":flag-ms:": { - "style": "github", - "image": "1f1f2-1f1f8.png", - "unicode": "🇲🇸", - "name": "Montserrat" - }, - ":flag_pt:": { - "style": "github", - "image": "1f1f5-1f1f9.png", - "unicode": "🇵🇹", - "name": "Portugal" - }, - ":hammer:": { - "style": "github", - "image": "1f528.png", - "unicode": "🔨", - "name": "Hammer" - }, - ":man_with_turban_tone5:": { - "style": "github", - "image": "1f473-1f3ff.png", - "unicode": "👳🏿", - "name": "Man With Turban - Tone 5" - }, - ":water_polo_tone2:": { - "style": "github", - "image": "1f93d-1f3fc.png", - "unicode": "🤽🏼", - "name": "Water Polo - Tone 2" - }, - ":lifter:": { - "style": "github", - "image": "1f3cb.png", - "unicode": "🏋", - "name": "Weight Lifter" - }, - ":flag-ec:": { - "style": "github", - "image": "1f1ea-1f1e8.png", - "unicode": "🇪🇨", - "name": "Ecuador" - }, - "😿": { - "style": "unicode", - "image": "1f63f.png", - "name": "Crying Cat Face" - }, - ":cold-sweat:": { - "style": "github", - "image": "1f630.png", - "unicode": "😰", - "name": "Face With Open Mouth And Cold Sweat" - }, - ":twisted-rightwards-arrows:": { - "style": "github", - "image": "1f500.png", - "unicode": "🔀", - "name": "Twisted Rightwards Arrows" - }, - "⛴": { - "style": "unicode", - "image": "26f4.png", - "name": "Ferry" - }, - "🔦": { - "style": "unicode", - "image": "1f526.png", - "name": "Electric Torch" - }, - "🗾": { - "style": "unicode", - "image": "1f5fe.png", - "name": "Silhouette Of Japan" - }, - ":gay-pride-flag:": { - "style": "github", - "image": "1f3f3-1f308.png", - "unicode": "🏳🌈", - "name": "Gay_pride_flag" - }, - "🦋": { - "style": "unicode", - "image": "1f98b.png", - "name": "Butterfly" - }, - ":volleyball:": { - "style": "github", - "image": "1f3d0.png", - "unicode": "🏐", - "name": "Volleyball" - }, - ":flag-ao:": { - "style": "github", - "image": "1f1e6-1f1f4.png", - "unicode": "🇦🇴", - "name": "Angola" - }, - "✍🏽": { - "style": "unicode", - "image": "270d-1f3fd.png", - "name": "Writing Hand - Tone 3" - }, - "✍🏼": { - "style": "unicode", - "image": "270d-1f3fc.png", - "name": "Writing Hand - Tone 2" - }, - "✍🏿": { - "style": "unicode", - "image": "270d-1f3ff.png", - "name": "Writing Hand - Tone 5" - }, - "✍🏾": { - "style": "unicode", - "image": "270d-1f3fe.png", - "name": "Writing Hand - Tone 4" - }, - "✍🏻": { - "style": "unicode", - "image": "270d-1f3fb.png", - "name": "Writing Hand - Tone 1" - }, - "🤠": { - "style": "unicode", - "image": "1f920.png", - "name": "Face With Cowboy Hat" - }, - ":see-no-evil:": { - "style": "github", - "image": "1f648.png", - "unicode": "🙈", - "name": "See-no-evil Monkey" - }, - "🔨": { - "style": "unicode", - "image": "1f528.png", - "name": "Hammer" - }, - ":id:": { - "style": "github", - "image": "1f194.png", - "unicode": "🆔", - "name": "Squared Id" - }, - ":runner_tone3:": { - "style": "github", - "image": "1f3c3-1f3fd.png", - "unicode": "🏃🏽", - "name": "Runner - Tone 3" - }, - ":man_with_gua_pi_mao_tone2:": { - "style": "github", - "image": "1f472-1f3fc.png", - "unicode": "👲🏼", - "name": "Man With Gua Pi Mao - Tone 2" - }, - ":flag-by:": { - "style": "github", - "image": "1f1e7-1f1fe.png", - "unicode": "🇧🇾", - "name": "Belarus" - }, - ":regional-indicator-j:": { - "style": "github", - "image": "1f1ef.png", - "unicode": "🇯", - "name": "Regional Indicator Symbol Letter J" - }, - ":oncoming-bus:": { - "style": "github", - "image": "1f68d.png", - "unicode": "🚍", - "name": "Oncoming Bus" - }, - ":unamused:": { - "style": "github", - "image": "1f612.png", - "unicode": "😒", - "name": "Unamused Face" - }, - ":sign_of_the_horns_tone3:": { - "style": "github", - "image": "1f918-1f3fd.png", - "unicode": "🤘🏽", - "name": "Sign Of The Horns - Tone 3" - }, - ":deer:": { - "style": "github", - "image": "1f98c.png", - "unicode": "🦌", - "name": "Deer" - }, - "🏧": { - "style": "unicode", - "image": "1f3e7.png", - "name": "Automated Teller Machine" - }, - ":prince-tone4:": { - "style": "github", - "image": "1f934-1f3fe.png", - "unicode": "🤴🏾", - "name": "Prince - Tone 4" - }, - ":film_frames:": { - "style": "github", - "image": "1f39e.png", - "unicode": "🎞", - "name": "Film Frames" - }, - ":black_joker:": { - "style": "github", - "image": "1f0cf.png", - "unicode": "🃏", - "name": "Playing Card Black Joker" - }, - ":dancer-tone4:": { - "style": "github", - "image": "1f483-1f3fe.png", - "unicode": "💃🏾", - "name": "Dancer - Tone 4" - }, - "🍼": { - "style": "unicode", - "image": "1f37c.png", - "name": "Baby Bottle" - }, - "😕": { - "style": "unicode", - "ascii": ">:\\", - "image": "1f615.png", - "name": "Confused Face" - }, - "🛣": { - "style": "unicode", - "image": "1f6e3.png", - "name": "Motorway" - }, - ":cartwheel-tone4:": { - "style": "github", - "image": "1f938-1f3fe.png", - "unicode": "🤸🏾", - "name": "Person Doing Cartwheel - Tone 4" - }, - ":eh:": { - "style": "github", - "image": "1f1ea-1f1ed.png", - "unicode": "🇪🇭", - "name": "Western Sahara" - }, - "🚪": { - "style": "unicode", - "image": "1f6aa.png", - "name": "Door" - }, - ":ferris_wheel:": { - "style": "github", - "image": "1f3a1.png", - "unicode": "🎡", - "name": "Ferris Wheel" - }, - ":u7121:": { - "style": "github", - "image": "1f21a.png", - "unicode": "🈚", - "name": "Squared Cjk Unified Ideograph-7121" - }, - ":hankey:": { - "style": "github", - "image": "1f4a9.png", - "unicode": "💩", - "name": "Pile Of Poo" - }, - ":clock2:": { - "style": "github", - "image": "1f551.png", - "unicode": "🕑", - "name": "Clock Face Two Oclock" - }, - ":1234:": { - "style": "github", - "image": "1f522.png", - "unicode": "🔢", - "name": "Input Symbol For Numbers" - }, - ":mh:": { - "style": "github", - "image": "1f1f2-1f1ed.png", - "unicode": "🇲🇭", - "name": "The Marshall Islands" - }, - "👩": { - "style": "unicode", - "image": "1f469.png", - "name": "Woman" - }, - ":keyboard:": { - "style": "github", - "image": "2328.png", - "unicode": "⌨", - "name": "Keyboard" - }, - ":heart_decoration:": { - "style": "github", - "image": "1f49f.png", - "unicode": "💟", - "name": "Heart Decoration" - }, - ":mosque:": { - "style": "github", - "image": "1f54c.png", - "unicode": "🕌", - "name": "Mosque" - }, - ":flag_mt:": { - "style": "github", - "image": "1f1f2-1f1f9.png", - "unicode": "🇲🇹", - "name": "Malta" - }, - "🚓": { - "style": "unicode", - "image": "1f693.png", - "name": "Police Car" - }, - ":bj:": { - "style": "github", - "image": "1f1e7-1f1ef.png", - "unicode": "🇧🇯", - "name": "Benin" - }, - "🇹🇲": { - "style": "unicode", - "image": "1f1f9-1f1f2.png", - "name": "Turkmenistan" - }, - "😨": { - "style": "unicode", - "ascii": "D:", - "image": "1f628.png", - "name": "Fearful Face" - }, - ":hammer_and_wrench:": { - "style": "github", - "image": "1f6e0.png", - "unicode": "🛠", - "name": "Hammer And Wrench" - }, - ":woman-tone5:": { - "style": "github", - "image": "1f469-1f3ff.png", - "unicode": "👩🏿", - "name": "Woman - Tone 5" - }, - ":man-tone4:": { - "style": "github", - "image": "1f468-1f3fe.png", - "unicode": "👨🏾", - "name": "Man - Tone 4" - }, - "♈": { - "style": "unicode", - "image": "2648.png", - "name": "Aries" - }, - ":shrug_tone1:": { - "style": "github", - "image": "1f937-1f3fb.png", - "unicode": "🤷🏻", - "name": "Shrug - Tone 1" - }, - ":package:": { - "style": "github", - "image": "1f4e6.png", - "unicode": "📦", - "name": "Package" - }, - ":helicopter:": { - "style": "github", - "image": "1f681.png", - "unicode": "🚁", - "name": "Helicopter" - }, - ":bomb:": { - "style": "github", - "image": "1f4a3.png", - "unicode": "💣", - "name": "Bomb" - }, - ":flag-pw:": { - "style": "github", - "image": "1f1f5-1f1fc.png", - "unicode": "🇵🇼", - "name": "Palau" - }, - ":negative_squared_cross_mark:": { - "style": "github", - "image": "274e.png", - "unicode": "❎", - "name": "Negative Squared Cross Mark" - }, - ":flag_bq:": { - "style": "github", - "image": "1f1e7-1f1f6.png", - "unicode": "🇧🇶", - "name": "Caribbean Netherlands" - }, - "🤰🏿": { - "style": "unicode", - "image": "1f930-1f3ff.png", - "name": "Pregnant Woman - Tone 5" - }, - "🌑": { - "style": "unicode", - "image": "1f311.png", - "name": "New Moon Symbol" - }, - "🙎🏻": { - "style": "unicode", - "image": "1f64e-1f3fb.png", - "name": "Person With Pouting Face Tone1" - }, - "🔕": { - "style": "unicode", - "image": "1f515.png", - "name": "Bell With Cancellation Stroke" - }, - "🙎🏽": { - "style": "unicode", - "image": "1f64e-1f3fd.png", - "name": "Person With Pouting Face Tone3" - }, - "🙎🏾": { - "style": "unicode", - "image": "1f64e-1f3fe.png", - "name": "Person With Pouting Face Tone4" - }, - "🙎🏿": { - "style": "unicode", - "image": "1f64e-1f3ff.png", - "name": "Person With Pouting Face Tone5" - }, - ":sweet_potato:": { - "style": "github", - "image": "1f360.png", - "unicode": "🍠", - "name": "Roasted Sweet Potato" - }, - ":eight_spoked_asterisk:": { - "style": "github", - "image": "2733.png", - "unicode": "✳", - "name": "Eight Spoked Asterisk" - }, - "🎦": { - "style": "unicode", - "image": "1f3a6.png", - "name": "Cinema" - }, - ":christmas-tree:": { - "style": "github", - "image": "1f384.png", - "unicode": "🎄", - "name": "Christmas Tree" - }, - ":snowflake:": { - "style": "github", - "image": "2744.png", - "unicode": "❄", - "name": "Snowflake" - }, - ":cartwheel_tone2:": { - "style": "github", - "image": "1f938-1f3fc.png", - "unicode": "🤸🏼", - "name": "Person Doing Cartwheel - Tone 2" - }, - ":cloud_with_rain:": { - "style": "github", - "image": "1f327.png", - "unicode": "🌧", - "name": "Cloud With Rain" - }, - "🐿": { - "style": "unicode", - "image": "1f43f.png", - "name": "Chipmunk" - }, - ":field_hockey:": { - "style": "github", - "image": "1f3d1.png", - "unicode": "🏑", - "name": "Field Hockey Stick And Ball" - }, - ":ga:": { - "style": "github", - "image": "1f1ec-1f1e6.png", - "unicode": "🇬🇦", - "name": "Gabon" - }, - ":left_fist_tone2:": { - "style": "github", - "image": "1f91b-1f3fc.png", - "unicode": "🤛🏼", - "name": "Left Facing Fist - Tone 2" - }, - "📔": { - "style": "unicode", - "image": "1f4d4.png", - "name": "Notebook With Decorative Cover" - }, - ":-1:": { - "style": "github", - "image": "1f44e.png", - "unicode": "👎", - "name": "Thumbs Down Sign" - }, - ":flag-ve:": { - "style": "github", - "image": "1f1fb-1f1ea.png", - "unicode": "🇻🇪", - "name": "Venezuela" - }, - "🇳🇿": { - "style": "unicode", - "image": "1f1f3-1f1ff.png", - "name": "New Zealand" - }, - "🤝🏽": { - "style": "unicode", - "image": "1f91d-1f3fd.png", - "name": "Handshake - Tone 3" - }, - "🤝🏼": { - "style": "unicode", - "image": "1f91d-1f3fc.png", - "name": "Handshake - Tone 2" - }, - "🇳🇱": { - "style": "unicode", - "image": "1f1f3-1f1f1.png", - "name": "The Netherlands" - }, - "🤝🏾": { - "style": "unicode", - "image": "1f91d-1f3fe.png", - "name": "Handshake - Tone 4" - }, - "🇳🇷": { - "style": "unicode", - "image": "1f1f3-1f1f7.png", - "name": "Nauru" - }, - "🇳🇵": { - "style": "unicode", - "image": "1f1f3-1f1f5.png", - "name": "Nepal" - }, - "🇳🇴": { - "style": "unicode", - "image": "1f1f3-1f1f4.png", - "name": "Norway" - }, - "🇳🇫": { - "style": "unicode", - "image": "1f1f3-1f1eb.png", - "name": "Norfolk Island" - }, - "🇳🇪": { - "style": "unicode", - "image": "1f1f3-1f1ea.png", - "name": "Niger" - }, - ":mag-right:": { - "style": "github", - "image": "1f50e.png", - "unicode": "🔎", - "name": "Right-pointing Magnifying Glass" - }, - "🇳🇮": { - "style": "unicode", - "image": "1f1f3-1f1ee.png", - "name": "Nicaragua" - }, - "🇳🇬": { - "style": "unicode", - "image": "1f1f3-1f1ec.png", - "name": "Nigeria" - }, - "🇳🇦": { - "style": "unicode", - "image": "1f1f3-1f1e6.png", - "name": "Namibia" - }, - ":right_fist_tone3:": { - "style": "github", - "image": "1f91c-1f3fd.png", - "unicode": "🤜🏽", - "name": "Right Facing Fist - Tone 3" - }, - "🔬": { - "style": "unicode", - "image": "1f52c.png", - "name": "Microscope" - }, - ":cop_tone2:": { - "style": "github", - "image": "1f46e-1f3fc.png", - "unicode": "👮🏼", - "name": "Police Officer - Tone 2" - }, - ":u6708:": { - "style": "github", - "image": "1f237.png", - "unicode": "🈷", - "name": "Squared Cjk Unified Ideograph-6708" - }, - ":man-in-tuxedo-tone2:": { - "style": "github", - "image": "1f935-1f3fc.png", - "unicode": "🤵🏼", - "name": "Man In Tuxedo - Tone 2" - }, - ":mw:": { - "style": "github", - "image": "1f1f2-1f1fc.png", - "unicode": "🇲🇼", - "name": "Malawi" - }, - ":cu:": { - "style": "github", - "image": "1f1e8-1f1fa.png", - "unicode": "🇨🇺", - "name": "Cuba" - }, - ":flag-um:": { - "style": "github", - "image": "1f1fa-1f1f2.png", - "unicode": "🇺🇲", - "name": "United States Minor Outlying Islands" - }, - ":man-with-gua-pi-mao-tone4:": { - "style": "github", - "image": "1f472-1f3fe.png", - "unicode": "👲🏾", - "name": "Man With Gua Pi Mao - Tone 4" - }, - ":carousel-horse:": { - "style": "github", - "image": "1f3a0.png", - "unicode": "🎠", - "name": "Carousel Horse" - }, - ":wrestling:": { - "style": "github", - "image": "1f93c.png", - "unicode": "🤼", - "name": "Wrestlers" - }, - "🇧": { - "style": "unicode", - "image": "1f1e7.png", - "name": "Regional Indicator Symbol Letter B" - }, - ":flag-ml:": { - "style": "github", - "image": "1f1f2-1f1f1.png", - "unicode": "🇲🇱", - "name": "Mali" - }, - ":frowning:": { - "style": "github", - "image": "1f626.png", - "unicode": "😦", - "name": "Frowning Face With Open Mouth" - }, - ":ke:": { - "style": "github", - "image": "1f1f0-1f1ea.png", - "unicode": "🇰🇪", - "name": "Kenya" - }, - ":clown:": { - "style": "github", - "image": "1f921.png", - "unicode": "🤡", - "name": "Clown Face" - }, - ":haircut-tone1:": { - "style": "github", - "image": "1f487-1f3fb.png", - "unicode": "💇🏻", - "name": "Haircut - Tone 1" - }, - ":skier:": { - "style": "github", - "image": "26f7.png", - "unicode": "⛷", - "name": "Skier" - }, - "🐕": { - "style": "unicode", - "image": "1f415.png", - "name": "Dog" - }, - ":+1_tone1:": { - "style": "github", - "image": "1f44d-1f3fb.png", - "unicode": "👍🏻", - "name": "Thumbs Up Sign - Tone 1" - }, - ":potable_water:": { - "style": "github", - "image": "1f6b0.png", - "unicode": "🚰", - "name": "Potable Water Symbol" - }, - ":flag-np:": { - "style": "github", - "image": "1f1f3-1f1f5.png", - "unicode": "🇳🇵", - "name": "Nepal" - }, - ":u7a7a:": { - "style": "github", - "image": "1f233.png", - "unicode": "🈳", - "name": "Squared Cjk Unified Ideograph-7a7a" - }, - "💪": { - "style": "unicode", - "image": "1f4aa.png", - "name": "Flexed Biceps" - }, - "✋🏾": { - "style": "unicode", - "image": "270b-1f3fe.png", - "name": "Raised Hand - Tone 4" - }, - ":flag-tf:": { - "style": "github", - "image": "1f1f9-1f1eb.png", - "unicode": "🇹🇫", - "name": "French Southern Territories" - }, - "🤷": { - "style": "unicode", - "image": "1f937.png", - "name": "Shrug" - }, - ":flag_fm:": { - "style": "github", - "image": "1f1eb-1f1f2.png", - "unicode": "🇫🇲", - "name": "Micronesia" - }, - ":flag_ea:": { - "style": "github", - "image": "1f1ea-1f1e6.png", - "unicode": "🇪🇦", - "name": "Ceuta, Melilla" - }, - ":karate_uniform:": { - "style": "github", - "image": "1f94b.png", - "unicode": "🥋", - "name": "Martial Arts Uniform" - }, - "🌻": { - "style": "unicode", - "image": "1f33b.png", - "name": "Sunflower" - }, - "〽": { - "style": "unicode", - "image": "303d.png", - "name": "Part Alternation Mark" - }, - ":white-medium-small-square:": { - "style": "github", - "image": "25fd.png", - "unicode": "◽", - "name": "White Medium Small Square" - }, - "🤾🏻": { - "style": "unicode", - "image": "1f93e-1f3fb.png", - "name": "Handball - Tone 1" - }, - "🤾🏼": { - "style": "unicode", - "image": "1f93e-1f3fc.png", - "name": "Handball - Tone 2" - }, - "🤾🏽": { - "style": "unicode", - "image": "1f93e-1f3fd.png", - "name": "Handball - Tone 3" - }, - "🤾🏾": { - "style": "unicode", - "image": "1f93e-1f3fe.png", - "name": "Handball - Tone 4" - }, - "🤾🏿": { - "style": "unicode", - "image": "1f93e-1f3ff.png", - "name": "Handball - Tone 5" - }, - "🏐": { - "style": "unicode", - "image": "1f3d0.png", - "name": "Volleyball" - }, - ":tumbler-glass:": { - "style": "github", - "image": "1f943.png", - "unicode": "🥃", - "name": "Tumbler Glass" - }, - ":police-car:": { - "style": "github", - "image": "1f693.png", - "unicode": "🚓", - "name": "Police Car" - }, - ":thumbup_tone3:": { - "style": "github", - "image": "1f44d-1f3fd.png", - "unicode": "👍🏽", - "name": "Thumbs Up Sign - Tone 3" - }, - ":flag-lv:": { - "style": "github", - "image": "1f1f1-1f1fb.png", - "unicode": "🇱🇻", - "name": "Latvia" - }, - ":fire:": { - "style": "github", - "image": "1f525.png", - "unicode": "🔥", - "name": "Fire" - }, - "⏰": { - "style": "unicode", - "image": "23f0.png", - "name": "Alarm Clock" - }, - "D:": { - "style": "ascii", - "ascii": "D:", - "image": "1f628.png", - "unicode": "😨", - "name": "Fearful Face" - }, - ":swimmer-tone2:": { - "style": "github", - "image": "1f3ca-1f3fc.png", - "unicode": "🏊🏼", - "name": "Swimmer - Tone 2" - }, - ":basketball-player-tone2:": { - "style": "github", - "image": "26f9-1f3fc.png", - "unicode": "⛹🏼", - "name": "Person With Ball - Tone 2" - }, - ":barber:": { - "style": "github", - "image": "1f488.png", - "unicode": "💈", - "name": "Barber Pole" - }, - ":facepalm:": { - "style": "github", - "image": "1f926.png", - "unicode": "🤦", - "name": "Face Palm" - }, - ":couple-mm:": { - "style": "github", - "image": "1f468-2764-1f468.png", - "unicode": "👨❤👨", - "name": "Couple (man,man)" - }, - "👍🏼": { - "style": "unicode", - "image": "1f44d-1f3fc.png", - "name": "Thumbs Up Sign - Tone 2" - }, - ":hand_splayed_tone5:": { - "style": "github", - "image": "1f590-1f3ff.png", - "unicode": "🖐🏿", - "name": "Raised Hand With Fingers Splayed - Tone 5" - }, - ":man_tone5:": { - "style": "github", - "image": "1f468-1f3ff.png", - "unicode": "👨🏿", - "name": "Man - Tone 5" - }, - ":anchor:": { - "style": "github", - "image": "2693.png", - "unicode": "⚓", - "name": "Anchor" - }, - ":sh:": { - "style": "github", - "image": "1f1f8-1f1ed.png", - "unicode": "🇸🇭", - "name": "Saint Helena" - }, - "#⃣": { - "style": "unicode", - "image": "0023-20e3.png", - "name": "Keycap Number Sign" - }, - ":bicyclist-tone4:": { - "style": "github", - "image": "1f6b4-1f3fe.png", - "unicode": "🚴🏾", - "name": "Bicyclist - Tone 4" - }, - "0:3": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":horse-racing-tone4:": { - "style": "github", - "image": "1f3c7-1f3fe.png", - "unicode": "🏇🏾", - "name": "Horse Racing - Tone 4" - }, - "0:)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":raised_back_of_hand_tone2:": { - "style": "github", - "image": "1f91a-1f3fc.png", - "unicode": "🤚🏼", - "name": "Raised Back Of Hand - Tone 2" - }, - ":girl-tone1:": { - "style": "github", - "image": "1f467-1f3fb.png", - "unicode": "👧🏻", - "name": "Girl - Tone 1" - }, - ":flag_ie:": { - "style": "github", - "image": "1f1ee-1f1ea.png", - "unicode": "🇮🇪", - "name": "Ireland" - }, - "🤰🏻": { - "style": "unicode", - "image": "1f930-1f3fb.png", - "name": "Pregnant Woman - Tone 1" - }, - "🤰🏾": { - "style": "unicode", - "image": "1f930-1f3fe.png", - "name": "Pregnant Woman - Tone 4" - }, - ":monorail:": { - "style": "github", - "image": "1f69d.png", - "unicode": "🚝", - "name": "Monorail" - }, - "🤰🏼": { - "style": "unicode", - "image": "1f930-1f3fc.png", - "name": "Pregnant Woman - Tone 2" - }, - "🤰🏽": { - "style": "unicode", - "image": "1f930-1f3fd.png", - "name": "Pregnant Woman - Tone 3" - }, - ":nose-tone2:": { - "style": "github", - "image": "1f443-1f3fc.png", - "unicode": "👃🏼", - "name": "Nose - Tone 2" - }, - ":point_up_2_tone4:": { - "style": "github", - "image": "1f446-1f3fe.png", - "unicode": "👆🏾", - "name": "White Up Pointing Backhand Index - Tone 4" - }, - ":regional_indicator_g:": { - "style": "github", - "image": "1f1ec.png", - "unicode": "🇬", - "name": "Regional Indicator Symbol Letter G" - }, - "X-)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":ferry:": { - "style": "github", - "image": "26f4.png", - "unicode": "⛴", - "name": "Ferry" - }, - "🔑": { - "style": "unicode", - "image": "1f511.png", - "name": "Key" - }, - "🌕": { - "style": "unicode", - "image": "1f315.png", - "name": "Full Moon Symbol" - }, - "✋🏻": { - "style": "unicode", - "image": "270b-1f3fb.png", - "name": "Raised Hand - Tone 1" - }, - "✋🏿": { - "style": "unicode", - "image": "270b-1f3ff.png", - "name": "Raised Hand - Tone 5" - }, - "⚜": { - "style": "unicode", - "image": "269c.png", - "name": "Fleur-de-lis" - }, - "✋🏽": { - "style": "unicode", - "image": "270b-1f3fd.png", - "name": "Raised Hand - Tone 3" - }, - "✋🏼": { - "style": "unicode", - "image": "270b-1f3fc.png", - "name": "Raised Hand - Tone 2" - }, - ":ug:": { - "style": "github", - "image": "1f1fa-1f1ec.png", - "unicode": "🇺🇬", - "name": "Uganda" - }, - ":raised_hand_tone5:": { - "style": "github", - "image": "270b-1f3ff.png", - "unicode": "✋🏿", - "name": "Raised Hand - Tone 5" - }, - "🎪": { - "style": "unicode", - "image": "1f3aa.png", - "name": "Circus Tent" - }, - ":euro:": { - "style": "github", - "image": "1f4b6.png", - "unicode": "💶", - "name": "Banknote With Euro Sign" - }, - ":pencil:": { - "style": "github", - "image": "1f4dd.png", - "unicode": "📝", - "name": "Memo" - }, - "🐻": { - "style": "unicode", - "image": "1f43b.png", - "name": "Bear Face" - }, - ":flag-er:": { - "style": "github", - "image": "1f1ea-1f1f7.png", - "unicode": "🇪🇷", - "name": "Eritrea" - }, - "📐": { - "style": "unicode", - "image": "1f4d0.png", - "name": "Triangular Ruler" - }, - ":shaking_hands_tone2:": { - "style": "github", - "image": "1f91d-1f3fc.png", - "unicode": "🤝🏼", - "name": "Handshake - Tone 2" - }, - ":flag_at:": { - "style": "github", - "image": "1f1e6-1f1f9.png", - "unicode": "🇦🇹", - "name": "Austria" - }, - ":muscle_tone5:": { - "style": "github", - "image": "1f4aa-1f3ff.png", - "unicode": "💪🏿", - "name": "Flexed Biceps - Tone 5" - }, - ":kiss:": { - "style": "github", - "image": "1f48b.png", - "unicode": "💋", - "name": "Kiss Mark" - }, - ":flag-mq:": { - "style": "github", - "image": "1f1f2-1f1f6.png", - "unicode": "🇲🇶", - "name": "Martinique" - }, - ":salad:": { - "style": "github", - "image": "1f957.png", - "unicode": "🥗", - "name": "Green Salad" - }, - "X-P": { - "style": "ascii", - "ascii": ">:P", - "image": "1f61c.png", - "unicode": "😜", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - "🇾": { - "style": "unicode", - "image": "1f1fe.png", - "name": "Regional Indicator Symbol Letter Y" - }, - ":flag-sx:": { - "style": "github", - "image": "1f1f8-1f1fd.png", - "unicode": "🇸🇽", - "name": "Sint Maarten" - }, - "🆓": { - "style": "unicode", - "image": "1f193.png", - "name": "Squared Free" - }, - ":flag-ea:": { - "style": "github", - "image": "1f1ea-1f1e6.png", - "unicode": "🇪🇦", - "name": "Ceuta, Melilla" - }, - ":ice_cream:": { - "style": "github", - "image": "1f368.png", - "unicode": "🍨", - "name": "Ice Cream" - }, - ":turkmenistan:": { - "style": "github", - "image": "1f1f9-1f1f2.png", - "unicode": "🇹🇲", - "name": "Turkmenistan" - }, - "🚽": { - "style": "unicode", - "image": "1f6bd.png", - "name": "Toilet" - }, - ":baby_tone4:": { - "style": "github", - "image": "1f476-1f3fe.png", - "unicode": "👶🏾", - "name": "Baby - Tone 4" - }, - ":no-bell:": { - "style": "github", - "image": "1f515.png", - "unicode": "🔕", - "name": "Bell With Cancellation Stroke" - }, - ":person_with_blond_hair_tone1:": { - "style": "github", - "image": "1f471-1f3fb.png", - "unicode": "👱🏻", - "name": "Person With Blond Hair - Tone 1" - }, - ":poodle:": { - "style": "github", - "image": "1f429.png", - "unicode": "🐩", - "name": "Poodle" - }, - ":pouting-cat:": { - "style": "github", - "image": "1f63e.png", - "unicode": "😾", - "name": "Pouting Cat Face" - }, - ":envelope-with-arrow:": { - "style": "github", - "image": "1f4e9.png", - "unicode": "📩", - "name": "Envelope With Downwards Arrow Above" - }, - ":man_dancing:": { - "style": "github", - "image": "1f57a.png", - "unicode": "🕺", - "name": "Man Dancing" - }, - ":man_with_gua_pi_mao_tone4:": { - "style": "github", - "image": "1f472-1f3fe.png", - "unicode": "👲🏾", - "name": "Man With Gua Pi Mao - Tone 4" - }, - ":flag-de:": { - "style": "github", - "image": "1f1e9-1f1ea.png", - "unicode": "🇩🇪", - "name": "Germany" - }, - ":heavy-dollar-sign:": { - "style": "github", - "image": "1f4b2.png", - "unicode": "💲", - "name": "Heavy Dollar Sign" - }, - ":flag_bl:": { - "style": "github", - "image": "1f1e7-1f1f1.png", - "unicode": "🇧🇱", - "name": "Saint Barthélemy" - }, - ":dolphin:": { - "style": "github", - "image": "1f42c.png", - "unicode": "🐬", - "name": "Dolphin" - }, - ":flag-am:": { - "style": "github", - "image": "1f1e6-1f1f2.png", - "unicode": "🇦🇲", - "name": "Armenia" - }, - "🇨🇿": { - "style": "unicode", - "image": "1f1e8-1f1ff.png", - "name": "The Czech Republic" - }, - ":horse_racing:": { - "style": "github", - "image": "1f3c7.png", - "unicode": "🏇", - "name": "Horse Racing" - }, - "💦": { - "style": "unicode", - "image": "1f4a6.png", - "name": "Splashing Sweat Symbol" - }, - ":runner_tone5:": { - "style": "github", - "image": "1f3c3-1f3ff.png", - "unicode": "🏃🏿", - "name": "Runner - Tone 5" - }, - "🔻": { - "style": "unicode", - "image": "1f53b.png", - "name": "Down-pointing Red Triangle" - }, - ":bow_and_arrow:": { - "style": "github", - "image": "1f3f9.png", - "unicode": "🏹", - "name": "Bow And Arrow" - }, - "🌿": { - "style": "unicode", - "image": "1f33f.png", - "name": "Herb" - }, - ":regional-indicator-h:": { - "style": "github", - "image": "1f1ed.png", - "unicode": "🇭", - "name": "Regional Indicator Symbol Letter H" - }, - "6⃣": { - "style": "unicode", - "image": "0036-20e3.png", - "name": "Keycap Digit Six" - }, - ":cheese:": { - "style": "github", - "image": "1f9c0.png", - "unicode": "🧀", - "name": "Cheese Wedge" - }, - ":flag-bw:": { - "style": "github", - "image": "1f1e7-1f1fc.png", - "unicode": "🇧🇼", - "name": "Botswana" - }, - "🇨🇷": { - "style": "unicode", - "image": "1f1e8-1f1f7.png", - "name": "Costa Rica" - }, - ":bride-with-veil-tone1:": { - "style": "github", - "image": "1f470-1f3fb.png", - "unicode": "👰🏻", - "name": "Bride With Veil - Tone 1" - }, - "🏔": { - "style": "unicode", - "image": "1f3d4.png", - "name": "Snow Capped Mountain" - }, - ":right_facing_fist_tone3:": { - "style": "github", - "image": "1f91c-1f3fd.png", - "unicode": "🤜🏽", - "name": "Right Facing Fist - Tone 3" - }, - ":sign_of_the_horns_tone5:": { - "style": "github", - "image": "1f918-1f3ff.png", - "unicode": "🤘🏿", - "name": "Sign Of The Horns - Tone 5" - }, - ":pig2:": { - "style": "github", - "image": "1f416.png", - "unicode": "🐖", - "name": "Pig" - }, - ":green_salad:": { - "style": "github", - "image": "1f957.png", - "unicode": "🥗", - "name": "Green Salad" - }, - ":princess-tone5:": { - "style": "github", - "image": "1f478-1f3ff.png", - "unicode": "👸🏿", - "name": "Princess - Tone 5" - }, - ":thumbsup_tone3:": { - "style": "github", - "image": "1f44d-1f3fd.png", - "unicode": "👍🏽", - "name": "Thumbs Up Sign - Tone 3" - }, - ":person-with-pouting-face:": { - "style": "github", - "image": "1f64e.png", - "unicode": "🙎", - "name": "Person With Pouting Face" - }, - ":house_with_garden:": { - "style": "github", - "image": "1f3e1.png", - "unicode": "🏡", - "name": "House With Garden" - }, - "👌🏾": { - "style": "unicode", - "image": "1f44c-1f3fe.png", - "name": "Ok Hand Sign - Tone 4" - }, - "👌🏿": { - "style": "unicode", - "image": "1f44c-1f3ff.png", - "name": "Ok Hand Sign - Tone 5" - }, - "👌🏼": { - "style": "unicode", - "image": "1f44c-1f3fc.png", - "name": "Ok Hand Sign - Tone 2" - }, - "👌🏽": { - "style": "unicode", - "image": "1f44c-1f3fd.png", - "name": "Ok Hand Sign - Tone 3" - }, - "👌🏻": { - "style": "unicode", - "image": "1f44c-1f3fb.png", - "name": "Ok Hand Sign - Tone 1" - }, - "👨👨👦👦": { - "style": "unicode", - "image": "1f468-1f468-1f466-1f466.png", - "name": "Family (man,man,boy,boy)" - }, - ":mountain-bicyclist-tone2:": { - "style": "github", - "image": "1f6b5-1f3fc.png", - "unicode": "🚵🏼", - "name": "Mountain Bicyclist - Tone 2" - }, - ":flag_ss:": { - "style": "github", - "image": "1f1f8-1f1f8.png", - "unicode": "🇸🇸", - "name": "South Sudan" - }, - ":airplane_arriving:": { - "style": "github", - "image": "1f6ec.png", - "unicode": "🛬", - "name": "Airplane Arriving" - }, - ":fried-shrimp:": { - "style": "github", - "image": "1f364.png", - "unicode": "🍤", - "name": "Fried Shrimp" - }, - ":boy-tone5:": { - "style": "github", - "image": "1f466-1f3ff.png", - "unicode": "👦🏿", - "name": "Boy - Tone 5" - }, - ":cocktail:": { - "style": "github", - "image": "1f378.png", - "unicode": "🍸", - "name": "Cocktail Glass" - }, - ":dz:": { - "style": "github", - "image": "1f1e9-1f1ff.png", - "unicode": "🇩🇿", - "name": "Algeria" - }, - "🕒": { - "style": "unicode", - "image": "1f552.png", - "name": "Clock Face Three Oclock" - }, - ":clock4:": { - "style": "github", - "image": "1f553.png", - "unicode": "🕓", - "name": "Clock Face Four Oclock" - }, - ":person-with-pouting-face-tone4:": { - "style": "github", - "image": "1f64e-1f3fe.png", - "unicode": "🙎🏾", - "name": "Person With Pouting Face Tone4" - }, - ":u5272:": { - "style": "github", - "image": "1f239.png", - "unicode": "🈹", - "name": "Squared Cjk Unified Ideograph-5272" - }, - "📧": { - "style": "unicode", - "image": "1f4e7.png", - "name": "E-mail Symbol" - }, - ":libra:": { - "style": "github", - "image": "264e.png", - "unicode": "♎", - "name": "Libra" - }, - ":basketball_player_tone4:": { - "style": "github", - "image": "26f9-1f3fe.png", - "unicode": "⛹🏾", - "name": "Person With Ball - Tone 4" - }, - ":lion_face:": { - "style": "github", - "image": "1f981.png", - "unicode": "🦁", - "name": "Lion Face" - }, - ":sleeping_accommodation:": { - "style": "github", - "image": "1f6cc.png", - "unicode": "🛌", - "name": "Sleeping Accommodation" - }, - ":tanabata-tree:": { - "style": "github", - "image": "1f38b.png", - "unicode": "🎋", - "name": "Tanabata Tree" - }, - "👼": { - "style": "unicode", - "image": "1f47c.png", - "name": "Baby Angel" - }, - ":bt:": { - "style": "github", - "image": "1f1e7-1f1f9.png", - "unicode": "🇧🇹", - "name": "Bhutan" - }, - ":taco:": { - "style": "github", - "image": "1f32e.png", - "unicode": "🌮", - "name": "Taco" - }, - ":whale:": { - "style": "github", - "image": "1f433.png", - "unicode": "🐳", - "name": "Spouting Whale" - }, - ":flag_rs:": { - "style": "github", - "image": "1f1f7-1f1f8.png", - "unicode": "🇷🇸", - "name": "Serbia" - }, - ":family-wwg:": { - "style": "github", - "image": "1f469-1f469-1f467.png", - "unicode": "👩👩👧", - "name": "Family (woman,woman,girl)" - }, - "😻": { - "style": "unicode", - "image": "1f63b.png", - "name": "Smiling Cat Face With Heart-shaped Eyes" - }, - ":flag-rw:": { - "style": "github", - "image": "1f1f7-1f1fc.png", - "unicode": "🇷🇼", - "name": "Rwanda" - }, - ":left_fist_tone1:": { - "style": "github", - "image": "1f91b-1f3fb.png", - "unicode": "🤛🏻", - "name": "Left Facing Fist - Tone 1" - }, - ":zap:": { - "style": "github", - "image": "26a1.png", - "unicode": "⚡", - "name": "High Voltage Sign" - }, - ":'-(": { - "style": "ascii", - "ascii": ":'(", - "image": "1f622.png", - "unicode": "😢", - "name": "Crying Face" - }, - ":flag-in:": { - "style": "github", - "image": "1f1ee-1f1f3.png", - "unicode": "🇮🇳", - "name": "India" - }, - "🛐": { - "style": "unicode", - "image": "1f6d0.png", - "name": "Place Of Worship" - }, - ":house_abandoned:": { - "style": "github", - "image": "1f3da.png", - "unicode": "🏚", - "name": "Derelict House Building" - }, - ":crab:": { - "style": "github", - "image": "1f980.png", - "unicode": "🦀", - "name": "Crab" - }, - ":clap-tone2:": { - "style": "github", - "image": "1f44f-1f3fc.png", - "unicode": "👏🏼", - "name": "Clapping Hands Sign - Tone 2" - }, - "🍩": { - "style": "unicode", - "image": "1f369.png", - "name": "Doughnut" - }, - ":lifter_tone5:": { - "style": "github", - "image": "1f3cb-1f3ff.png", - "unicode": "🏋🏿", - "name": "Weight Lifter - Tone 5" - }, - ":dancer_tone4:": { - "style": "github", - "image": "1f483-1f3fe.png", - "unicode": "💃🏾", - "name": "Dancer - Tone 4" - }, - ":flag_mv:": { - "style": "github", - "image": "1f1f2-1f1fb.png", - "unicode": "🇲🇻", - "name": "Maldives" - }, - ":hand-splayed:": { - "style": "github", - "image": "1f590.png", - "unicode": "🖐", - "name": "Raised Hand With Fingers Splayed" - }, - ":left-luggage:": { - "style": "github", - "image": "1f6c5.png", - "unicode": "🛅", - "name": "Left Luggage" - }, - "🏾": { - "style": "unicode", - "image": "1f3fe.png", - "name": "Emoji Modifier Fitzpatrick Type-5" - }, - ":bath_tone1:": { - "style": "github", - "image": "1f6c0-1f3fb.png", - "unicode": "🛀🏻", - "name": "Bath - Tone 1" - }, - ":flag_bs:": { - "style": "github", - "image": "1f1e7-1f1f8.png", - "unicode": "🇧🇸", - "name": "The Bahamas" - }, - "🦏": { - "style": "unicode", - "image": "1f98f.png", - "name": "Rhinoceros" - }, - "🎓": { - "style": "unicode", - "image": "1f393.png", - "name": "Graduation Cap" - }, - ":construction_site:": { - "style": "github", - "image": "1f3d7.png", - "unicode": "🏗", - "name": "Building Construction" - }, - "🤤": { - "style": "unicode", - "image": "1f924.png", - "name": "Drooling Face" - }, - "🌨": { - "style": "unicode", - "image": "1f328.png", - "name": "Cloud With Snow" - }, - "💽": { - "style": "unicode", - "image": "1f4bd.png", - "name": "Minidisc" - }, - ":dragon-face:": { - "style": "github", - "image": "1f432.png", - "unicode": "🐲", - "name": "Dragon Face" - }, - ":gg:": { - "style": "github", - "image": "1f1ec-1f1ec.png", - "unicode": "🇬🇬", - "name": "Guernsey" - }, - ":atm:": { - "style": "github", - "image": "1f3e7.png", - "unicode": "🏧", - "name": "Automated Teller Machine" - }, - ":ok_hand_tone5:": { - "style": "github", - "image": "1f44c-1f3ff.png", - "unicode": "👌🏿", - "name": "Ok Hand Sign - Tone 5" - }, - ":ss:": { - "style": "github", - "image": "1f1f8-1f1f8.png", - "unicode": "🇸🇸", - "name": "South Sudan" - }, - "👒": { - "style": "unicode", - "image": "1f452.png", - "name": "Womans Hat" - }, - ":elephant:": { - "style": "github", - "image": "1f418.png", - "unicode": "🐘", - "name": "Elephant" - }, - ":clock1230:": { - "style": "github", - "image": "1f567.png", - "unicode": "🕧", - "name": "Clock Face Twelve-thirty" - }, - ":hugging:": { - "style": "github", - "image": "1f917.png", - "unicode": "🤗", - "name": "Hugging Face" - }, - ":map:": { - "style": "github", - "image": "1f5fa.png", - "unicode": "🗺", - "name": "World Map" - }, - ":no-pedestrians:": { - "style": "github", - "image": "1f6b7.png", - "unicode": "🚷", - "name": "No Pedestrians" - }, - ":octagonal_sign:": { - "style": "github", - "image": "1f6d1.png", - "unicode": "🛑", - "name": "Octagonal Sign" - }, - ":raised-hand-tone2:": { - "style": "github", - "image": "270b-1f3fc.png", - "unicode": "✋🏼", - "name": "Raised Hand - Tone 2" - }, - ":bath-tone4:": { - "style": "github", - "image": "1f6c0-1f3fe.png", - "unicode": "🛀🏾", - "name": "Bath - Tone 4" - }, - "😑": { - "style": "unicode", - "ascii": "-_-", - "image": "1f611.png", - "name": "Expressionless Face" - }, - ":bride_with_veil_tone1:": { - "style": "github", - "image": "1f470-1f3fb.png", - "unicode": "👰🏻", - "name": "Bride With Veil - Tone 1" - }, - ":negative-squared-cross-mark:": { - "style": "github", - "image": "274e.png", - "unicode": "❎", - "name": "Negative Squared Cross Mark" - }, - ":male_dancer_tone1:": { - "style": "github", - "image": "1f57a-1f3fb.png", - "unicode": "🕺🏻", - "name": "Man Dancing - Tone 1" - }, - ":cop_tone4:": { - "style": "github", - "image": "1f46e-1f3fe.png", - "unicode": "👮🏾", - "name": "Police Officer - Tone 4" - }, - ":two_hearts:": { - "style": "github", - "image": "1f495.png", - "unicode": "💕", - "name": "Two Hearts" - }, - ":motorbike:": { - "style": "github", - "image": "1f6f5.png", - "unicode": "🛵", - "name": "Motor Scooter" - }, - "🚦": { - "style": "unicode", - "image": "1f6a6.png", - "name": "Vertical Traffic Light" - }, - ":turtle:": { - "style": "github", - "image": "1f422.png", - "unicode": "🐢", - "name": "Turtle" - }, - ":rainbow:": { - "style": "github", - "image": "1f308.png", - "unicode": "🌈", - "name": "Rainbow" - }, - ":skull-crossbones:": { - "style": "github", - "image": "2620.png", - "unicode": "☠", - "name": "Skull And Crossbones" - }, - ":white_sun_behind_cloud:": { - "style": "github", - "image": "1f325.png", - "unicode": "🌥", - "name": "White Sun Behind Cloud" - }, - "👇🏿": { - "style": "unicode", - "image": "1f447-1f3ff.png", - "name": "White Down Pointing Backhand Index - Tone 5" - }, - "👇🏾": { - "style": "unicode", - "image": "1f447-1f3fe.png", - "name": "White Down Pointing Backhand Index - Tone 4" - }, - "👇🏽": { - "style": "unicode", - "image": "1f447-1f3fd.png", - "name": "White Down Pointing Backhand Index - Tone 3" - }, - "👇🏼": { - "style": "unicode", - "image": "1f447-1f3fc.png", - "name": "White Down Pointing Backhand Index - Tone 2" - }, - "👇🏻": { - "style": "unicode", - "image": "1f447-1f3fb.png", - "name": "White Down Pointing Backhand Index - Tone 1" - }, - "👰🏻": { - "style": "unicode", - "image": "1f470-1f3fb.png", - "name": "Bride With Veil - Tone 1" - }, - "💃": { - "style": "unicode", - "image": "1f483.png", - "name": "Dancer" - }, - ":heart:": { - "style": "github", - "ascii": "<3", - "image": "2764.png", - "unicode": "❤", - "name": "Heavy Black Heart" - }, - "🐘": { - "style": "unicode", - "image": "1f418.png", - "name": "Elephant" - }, - ":weary:": { - "style": "github", - "image": "1f629.png", - "unicode": "😩", - "name": "Weary Face" - }, - ":laughing:": { - "style": "github", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ":flag_ec:": { - "style": "github", - "image": "1f1ea-1f1e8.png", - "unicode": "🇪🇨", - "name": "Ecuador" - }, - "🎭": { - "style": "unicode", - "image": "1f3ad.png", - "name": "Performing Arts" - }, - ":mushroom:": { - "style": "github", - "image": "1f344.png", - "unicode": "🍄", - "name": "Mushroom" - }, - ":biohazard_sign:": { - "style": "github", - "image": "2623.png", - "unicode": "☣", - "name": "Biohazard Sign" - }, - ":flag_fo:": { - "style": "github", - "image": "1f1eb-1f1f4.png", - "unicode": "🇫🇴", - "name": "Faroe Islands" - }, - ":flag_ly:": { - "style": "github", - "image": "1f1f1-1f1fe.png", - "unicode": "🇱🇾", - "name": "Libya" - }, - "🍂": { - "style": "unicode", - "image": "1f342.png", - "name": "Fallen Leaf" - }, - ":mountain-cableway:": { - "style": "github", - "image": "1f6a0.png", - "unicode": "🚠", - "name": "Mountain Cableway" - }, - ":rowboat-tone5:": { - "style": "github", - "image": "1f6a3-1f3ff.png", - "unicode": "🚣🏿", - "name": "Rowboat - Tone 5" - }, - ":wrestlers_tone3:": { - "style": "github", - "image": "1f93c-1f3fd.png", - "unicode": "🤼🏽", - "name": "Wrestlers - Tone 3" - }, - ":rolling_on_the_floor_laughing:": { - "style": "github", - "image": "1f923.png", - "unicode": "🤣", - "name": "Rolling On The Floor Laughing" - }, - ":ramen:": { - "style": "github", - "image": "1f35c.png", - "unicode": "🍜", - "name": "Steaming Bowl" - }, - ":flag_no:": { - "style": "github", - "image": "1f1f3-1f1f4.png", - "unicode": "🇳🇴", - "name": "Norway" - }, - ":turkey:": { - "style": "github", - "image": "1f983.png", - "unicode": "🦃", - "name": "Turkey" - }, - ":flag_gy:": { - "style": "github", - "image": "1f1ec-1f1fe.png", - "unicode": "🇬🇾", - "name": "Guyana" - }, - ":haircut-tone3:": { - "style": "github", - "image": "1f487-1f3fd.png", - "unicode": "💇🏽", - "name": "Haircut - Tone 3" - }, - ":am:": { - "style": "github", - "image": "1f1e6-1f1f2.png", - "unicode": "🇦🇲", - "name": "Armenia" - }, - "☔": { - "style": "unicode", - "image": "2614.png", - "name": "Umbrella With Rain Drops" - }, - ":pr:": { - "style": "github", - "image": "1f1f5-1f1f7.png", - "unicode": "🇵🇷", - "name": "Puerto Rico" - }, - ":mother_christmas_tone5:": { - "style": "github", - "image": "1f936-1f3ff.png", - "unicode": "🤶🏿", - "name": "Mother Christmas - Tone 5" - }, - "🆖": { - "style": "unicode", - "image": "1f196.png", - "name": "Squared Ng" - }, - ":smoking:": { - "style": "github", - "image": "1f6ac.png", - "unicode": "🚬", - "name": "Smoking Symbol" - }, - "😯": { - "style": "unicode", - "image": "1f62f.png", - "name": "Hushed Face" - }, - "🚟": { - "style": "unicode", - "image": "1f69f.png", - "name": "Suspension Railway" - }, - ":person-with-blond-hair-tone5:": { - "style": "github", - "image": "1f471-1f3ff.png", - "unicode": "👱🏿", - "name": "Person With Blond Hair - Tone 5" - }, - "👨🏻": { - "style": "unicode", - "image": "1f468-1f3fb.png", - "name": "Man - Tone 1" - }, - ":writing_hand:": { - "style": "github", - "image": "270d.png", - "unicode": "✍", - "name": "Writing Hand" - }, - "👨🏾": { - "style": "unicode", - "image": "1f468-1f3fe.png", - "name": "Man - Tone 4" - }, - "👨🏿": { - "style": "unicode", - "image": "1f468-1f3ff.png", - "name": "Man - Tone 5" - }, - "👨🏼": { - "style": "unicode", - "image": "1f468-1f3fc.png", - "name": "Man - Tone 2" - }, - "👨🏽": { - "style": "unicode", - "image": "1f468-1f3fd.png", - "name": "Man - Tone 3" - }, - ":bicyclist-tone2:": { - "style": "github", - "image": "1f6b4-1f3fc.png", - "unicode": "🚴🏼", - "name": "Bicyclist - Tone 2" - }, - "🕙": { - "style": "unicode", - "image": "1f559.png", - "name": "Clock Face Ten Oclock" - }, - ":raised_back_of_hand_tone4:": { - "style": "github", - "image": "1f91a-1f3fe.png", - "unicode": "🤚🏾", - "name": "Raised Back Of Hand - Tone 4" - }, - ":japanese_castle:": { - "style": "github", - "image": "1f3ef.png", - "unicode": "🏯", - "name": "Japanese Castle" - }, - ":pensive:": { - "style": "github", - "image": "1f614.png", - "unicode": "😔", - "name": "Pensive Face" - }, - ":raised_hand_with_fingers_splayed_tone2:": { - "style": "github", - "image": "1f590-1f3fc.png", - "unicode": "🖐🏼", - "name": "Raised Hand With Fingers Splayed - Tone 2" - }, - ":point_up_2_tone2:": { - "style": "github", - "image": "1f446-1f3fc.png", - "unicode": "👆🏼", - "name": "White Up Pointing Backhand Index - Tone 2" - }, - ":round-pushpin:": { - "style": "github", - "image": "1f4cd.png", - "unicode": "📍", - "name": "Round Pushpin" - }, - ":nose-tone4:": { - "style": "github", - "image": "1f443-1f3fe.png", - "unicode": "👃🏾", - "name": "Nose - Tone 4" - }, - ":capital_abcd:": { - "style": "github", - "image": "1f520.png", - "unicode": "🔠", - "name": "Input Symbol For Latin Capital Letters" - }, - ":raised_hands:": { - "style": "github", - "image": "1f64c.png", - "unicode": "🙌", - "name": "Person Raising Both Hands In Celebration" - }, - "🔘": { - "style": "unicode", - "image": "1f518.png", - "name": "Radio Button" - }, - ":house_buildings:": { - "style": "github", - "image": "1f3d8.png", - "unicode": "🏘", - "name": "House Buildings" - }, - ":flag_il:": { - "style": "github", - "image": "1f1ee-1f1f1.png", - "unicode": "🇮🇱", - "name": "Israel" - }, - ":raised_hand:": { - "style": "github", - "image": "270b.png", - "unicode": "✋", - "name": "Raised Hand" - }, - ":raised_hand_tone3:": { - "style": "github", - "image": "270b-1f3fd.png", - "unicode": "✋🏽", - "name": "Raised Hand - Tone 3" - }, - ":raised_hand_with_part_between_middle_and_ring_fingers_tone3:": { - "style": "github", - "image": "1f596-1f3fd.png", - "unicode": "🖖🏽", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 3" - }, - ":no_smoking:": { - "style": "github", - "image": "1f6ad.png", - "unicode": "🚭", - "name": "No Smoking Symbol" - }, - "🏗": { - "style": "unicode", - "image": "1f3d7.png", - "name": "Building Construction" - }, - ":nu:": { - "style": "github", - "image": "1f1f3-1f1fa.png", - "unicode": "🇳🇺", - "name": "Niue" - }, - ":muscle_tone3:": { - "style": "github", - "image": "1f4aa-1f3fd.png", - "unicode": "💪🏽", - "name": "Flexed Biceps - Tone 3" - }, - ":ledger:": { - "style": "github", - "image": "1f4d2.png", - "unicode": "📒", - "name": "Ledger" - }, - "🍬": { - "style": "unicode", - "image": "1f36c.png", - "name": "Candy" - }, - ":prince_tone2:": { - "style": "github", - "image": "1f934-1f3fc.png", - "unicode": "🤴🏼", - "name": "Prince - Tone 2" - }, - ":man-with-gua-pi-mao-tone5:": { - "style": "github", - "image": "1f472-1f3ff.png", - "unicode": "👲🏿", - "name": "Man With Gua Pi Mao - Tone 5" - }, - ":haircut:": { - "style": "github", - "image": "1f487.png", - "unicode": "💇", - "name": "Haircut" - }, - ":green_book:": { - "style": "github", - "image": "1f4d7.png", - "unicode": "📗", - "name": "Green Book" - }, - "😅": { - "style": "unicode", - "ascii": "':)", - "image": "1f605.png", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - "✏": { - "style": "unicode", - "image": "270f.png", - "name": "Pencil" - }, - ":flag-ky:": { - "style": "github", - "image": "1f1f0-1f1fe.png", - "unicode": "🇰🇾", - "name": "Cayman Islands" - }, - "🚚": { - "style": "unicode", - "image": "1f69a.png", - "name": "Delivery Truck" - }, - ":bow_tone4:": { - "style": "github", - "image": "1f647-1f3fe.png", - "unicode": "🙇🏾", - "name": "Person Bowing Deeply - Tone 4" - }, - "0;^)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":bus:": { - "style": "github", - "image": "1f68c.png", - "unicode": "🚌", - "name": "Bus" - }, - "👙": { - "style": "unicode", - "image": "1f459.png", - "name": "Bikini" - }, - ":flag-cy:": { - "style": "github", - "image": "1f1e8-1f1fe.png", - "unicode": "🇨🇾", - "name": "Cyprus" - }, - ":church:": { - "style": "github", - "image": "26ea.png", - "unicode": "⛪", - "name": "Church" - }, - ":family_wwgb:": { - "style": "github", - "image": "1f469-1f469-1f467-1f466.png", - "unicode": "👩👩👧👦", - "name": "Family (woman,woman,girl,boy)" - }, - "🇯🇪": { - "style": "unicode", - "image": "1f1ef-1f1ea.png", - "name": "Jersey" - }, - ":wind-blowing-face:": { - "style": "github", - "image": "1f32c.png", - "unicode": "🌬", - "name": "Wind Blowing Face" - }, - ":person_with_blond_hair_tone3:": { - "style": "github", - "image": "1f471-1f3fd.png", - "unicode": "👱🏽", - "name": "Person With Blond Hair - Tone 3" - }, - "📮": { - "style": "unicode", - "image": "1f4ee.png", - "name": "Postbox" - }, - ":flag-sz:": { - "style": "github", - "image": "1f1f8-1f1ff.png", - "unicode": "🇸🇿", - "name": "Swaziland" - }, - "🇯🇵": { - "style": "unicode", - "image": "1f1ef-1f1f5.png", - "name": "Japan" - }, - "🇯🇴": { - "style": "unicode", - "image": "1f1ef-1f1f4.png", - "name": "Jordan" - }, - "🇯🇲": { - "style": "unicode", - "image": "1f1ef-1f1f2.png", - "name": "Jamaica" - }, - ":flag-as:": { - "style": "github", - "image": "1f1e6-1f1f8.png", - "unicode": "🇦🇸", - "name": "American Samoa" - }, - "🚃": { - "style": "unicode", - "image": "1f683.png", - "name": "Railway Car" - }, - ":flag-je:": { - "style": "github", - "image": "1f1ef-1f1ea.png", - "unicode": "🇯🇪", - "name": "Jersey" - }, - ":light-rail:": { - "style": "github", - "image": "1f688.png", - "unicode": "🚈", - "name": "Light Rail" - }, - ":flag-dg:": { - "style": "github", - "image": "1f1e9-1f1ec.png", - "unicode": "🇩🇬", - "name": "Diego Garcia" - }, - ":punch_tone3:": { - "style": "github", - "image": "1f44a-1f3fd.png", - "unicode": "👊🏽", - "name": "Fisted Hand Sign - Tone 3" - }, - "😘": { - "style": "unicode", - "ascii": ":*", - "image": "1f618.png", - "name": "Face Throwing A Kiss" - }, - ":flag_bn:": { - "style": "github", - "image": "1f1e7-1f1f3.png", - "unicode": "🇧🇳", - "name": "Brunei" - }, - "™": { - "style": "unicode", - "image": "2122.png", - "name": "Trade Mark Sign" - }, - ":table_tennis:": { - "style": "github", - "image": "1f3d3.png", - "unicode": "🏓", - "name": "Table Tennis Paddle And Ball" - }, - ":anger-right:": { - "style": "github", - "image": "1f5ef.png", - "unicode": "🗯", - "name": "Right Anger Bubble" - }, - ":regional-indicator-n:": { - "style": "github", - "image": "1f1f3.png", - "unicode": "🇳", - "name": "Regional Indicator Symbol Letter N" - }, - ":right_facing_fist_tone1:": { - "style": "github", - "image": "1f91c-1f3fb.png", - "unicode": "🤜🏻", - "name": "Right Facing Fist - Tone 1" - }, - ":ballot_box_with_check:": { - "style": "github", - "image": "2611.png", - "unicode": "☑", - "name": "Ballot Box With Check" - }, - ":ticket:": { - "style": "github", - "image": "1f3ab.png", - "unicode": "🎫", - "name": "Ticket" - }, - ":thumbsup_tone1:": { - "style": "github", - "image": "1f44d-1f3fb.png", - "unicode": "👍🏻", - "name": "Thumbs Up Sign - Tone 1" - }, - "💪🏼": { - "style": "unicode", - "image": "1f4aa-1f3fc.png", - "name": "Flexed Biceps - Tone 2" - }, - "💪🏽": { - "style": "unicode", - "image": "1f4aa-1f3fd.png", - "name": "Flexed Biceps - Tone 3" - }, - "💪🏾": { - "style": "unicode", - "image": "1f4aa-1f3fe.png", - "name": "Flexed Biceps - Tone 4" - }, - "💪🏿": { - "style": "unicode", - "image": "1f4aa-1f3ff.png", - "name": "Flexed Biceps - Tone 5" - }, - "🔅": { - "style": "unicode", - "image": "1f505.png", - "name": "Low Brightness Symbol" - }, - "💪🏻": { - "style": "unicode", - "image": "1f4aa-1f3fb.png", - "name": "Flexed Biceps - Tone 1" - }, - ":santa:": { - "style": "github", - "image": "1f385.png", - "unicode": "🎅", - "name": "Father Christmas" - }, - ":two-women-holding-hands:": { - "style": "github", - "image": "1f46d.png", - "unicode": "👭", - "name": "Two Women Holding Hands" - }, - "🎖": { - "style": "unicode", - "image": "1f396.png", - "name": "Military Medal" - }, - ":ok-hand-tone2:": { - "style": "github", - "image": "1f44c-1f3fc.png", - "unicode": "👌🏼", - "name": "Ok Hand Sign - Tone 2" - }, - ":flag-wf:": { - "style": "github", - "image": "1f1fc-1f1eb.png", - "unicode": "🇼🇫", - "name": "Wallis And Futuna" - }, - ":railway-car:": { - "style": "github", - "image": "1f683.png", - "unicode": "🚃", - "name": "Railway Car" - }, - "🐯": { - "style": "unicode", - "image": "1f42f.png", - "name": "Tiger Face" - }, - ":clock6:": { - "style": "github", - "image": "1f555.png", - "unicode": "🕕", - "name": "Clock Face Six Oclock" - }, - "📄": { - "style": "unicode", - "image": "1f4c4.png", - "name": "Page Facing Up" - }, - ":herb:": { - "style": "github", - "image": "1f33f.png", - "unicode": "🌿", - "name": "Herb" - }, - ":hot_pepper:": { - "style": "github", - "image": "1f336.png", - "unicode": "🌶", - "name": "Hot Pepper" - }, - ":ml:": { - "style": "github", - "image": "1f1f2-1f1f1.png", - "unicode": "🇲🇱", - "name": "Mali" - }, - "🥝": { - "style": "unicode", - "image": "1f95d.png", - "name": "Kiwifruit" - }, - "♣": { - "style": "unicode", - "image": "2663.png", - "name": "Black Club Suit" - }, - ":inbox-tray:": { - "style": "github", - "image": "1f4e5.png", - "unicode": "📥", - "name": "Inbox Tray" - }, - "⛸": { - "style": "unicode", - "image": "26f8.png", - "name": "Ice Skate" - }, - "7⃣": { - "style": "unicode", - "image": "0037-20e3.png", - "name": "Keycap Digit Seven" - }, - "🦇": { - "style": "unicode", - "image": "1f987.png", - "name": "Bat" - }, - ":bv:": { - "style": "github", - "image": "1f1e7-1f1fb.png", - "unicode": "🇧🇻", - "name": "Bouvet Island" - }, - ":call_me_tone3:": { - "style": "github", - "image": "1f919-1f3fd.png", - "unicode": "🤙🏽", - "name": "Call Me Hand - Tone 3" - }, - "🤜": { - "style": "unicode", - "image": "1f91c.png", - "name": "Right-facing Fist" - }, - ":flag_ug:": { - "style": "github", - "image": "1f1fa-1f1ec.png", - "unicode": "🇺🇬", - "name": "Uganda" - }, - ":hu:": { - "style": "github", - "image": "1f1ed-1f1fa.png", - "unicode": "🇭🇺", - "name": "Hungary" - }, - "X)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":surfer:": { - "style": "github", - "image": "1f3c4.png", - "unicode": "🏄", - "name": "Surfer" - }, - ":flag-ni:": { - "style": "github", - "image": "1f1f3-1f1ee.png", - "unicode": "🇳🇮", - "name": "Nicaragua" - }, - ":mount_fuji:": { - "style": "github", - "image": "1f5fb.png", - "unicode": "🗻", - "name": "Mount Fuji" - }, - ":flag-ru:": { - "style": "github", - "image": "1f1f7-1f1fa.png", - "unicode": "🇷🇺", - "name": "Russia" - }, - ":flower-playing-cards:": { - "style": "github", - "image": "1f3b4.png", - "unicode": "🎴", - "name": "Flower Playing Cards" - }, - "⭐": { - "style": "unicode", - "image": "2b50.png", - "name": "White Medium Star" - }, - ":round_pushpin:": { - "style": "github", - "image": "1f4cd.png", - "unicode": "📍", - "name": "Round Pushpin" - }, - "👷🏿": { - "style": "unicode", - "image": "1f477-1f3ff.png", - "name": "Construction Worker - Tone 5" - }, - "👷🏾": { - "style": "unicode", - "image": "1f477-1f3fe.png", - "name": "Construction Worker - Tone 4" - }, - "👷🏽": { - "style": "unicode", - "image": "1f477-1f3fd.png", - "name": "Construction Worker - Tone 3" - }, - "👷🏼": { - "style": "unicode", - "image": "1f477-1f3fc.png", - "name": "Construction Worker - Tone 2" - }, - "👷🏻": { - "style": "unicode", - "image": "1f477-1f3fb.png", - "name": "Construction Worker - Tone 1" - }, - ":ru:": { - "style": "github", - "image": "1f1f7-1f1fa.png", - "unicode": "🇷🇺", - "name": "Russia" - }, - ":drooling_face:": { - "style": "github", - "image": "1f924.png", - "unicode": "🤤", - "name": "Drooling Face" - }, - ":flag-ps:": { - "style": "github", - "image": "1f1f5-1f1f8.png", - "unicode": "🇵🇸", - "name": "Palestinian Authority" - }, - "🍉": { - "style": "unicode", - "image": "1f349.png", - "name": "Watermelon" - }, - ":flag_mx:": { - "style": "github", - "image": "1f1f2-1f1fd.png", - "unicode": "🇲🇽", - "name": "Mexico" - }, - "🈁": { - "style": "unicode", - "image": "1f201.png", - "name": "Squared Katakana Koko" - }, - "🇾🇹": { - "style": "unicode", - "image": "1f1fe-1f1f9.png", - "name": "Mayotte" - }, - "🐅": { - "style": "unicode", - "image": "1f405.png", - "name": "Tiger" - }, - ":bath_tone3:": { - "style": "github", - "image": "1f6c0-1f3fd.png", - "unicode": "🛀🏽", - "name": "Bath - Tone 3" - }, - ":record-button:": { - "style": "github", - "image": "23fa.png", - "unicode": "⏺", - "name": "Black Circle For Record" - }, - "🇾🇪": { - "style": "unicode", - "image": "1f1fe-1f1ea.png", - "name": "Yemen" - }, - ":older-woman-tone4:": { - "style": "github", - "image": "1f475-1f3fe.png", - "unicode": "👵🏾", - "name": "Older Woman - Tone 4" - }, - "💚": { - "style": "unicode", - "image": "1f49a.png", - "name": "Green Heart" - }, - ":hot-pepper:": { - "style": "github", - "image": "1f336.png", - "unicode": "🌶", - "name": "Hot Pepper" - }, - "🌫": { - "style": "unicode", - "image": "1f32b.png", - "name": "Fog" - }, - "🔯": { - "style": "unicode", - "image": "1f52f.png", - "name": "Six Pointed Star With Middle Dot" - }, - ":flag-il:": { - "style": "github", - "image": "1f1ee-1f1f1.png", - "unicode": "🇮🇱", - "name": "Israel" - }, - ":apple:": { - "style": "github", - "image": "1f34e.png", - "unicode": "🍎", - "name": "Red Apple" - }, - "☹": { - "style": "unicode", - "image": "2639.png", - "name": "White Frowning Face" - }, - ":rat:": { - "style": "github", - "image": "1f400.png", - "unicode": "🐀", - "name": "Rat" - }, - "🏀": { - "style": "unicode", - "image": "1f3c0.png", - "name": "Basketball And Hoop" - }, - ":ge:": { - "style": "github", - "image": "1f1ec-1f1ea.png", - "unicode": "🇬🇪", - "name": "Georgia" - }, - "🗄": { - "style": "unicode", - "image": "1f5c4.png", - "name": "File Cabinet" - }, - "⛎": { - "style": "unicode", - "image": "26ce.png", - "name": "Ophiuchus" - }, - ":shopping_trolley:": { - "style": "github", - "image": "1f6d2.png", - "unicode": "🛒", - "name": "Shopping Trolley" - }, - "❣": { - "style": "unicode", - "image": "2763.png", - "name": "Heavy Heart Exclamation Mark Ornament" - }, - ":flag-vi:": { - "style": "github", - "image": "1f1fb-1f1ee.png", - "unicode": "🇻🇮", - "name": "U.s. Virgin Islands" - }, - ":desert_island:": { - "style": "github", - "image": "1f3dd.png", - "unicode": "🏝", - "name": "Desert Island" - }, - ":handball_tone5:": { - "style": "github", - "image": "1f93e-1f3ff.png", - "unicode": "🤾🏿", - "name": "Handball - Tone 5" - }, - ":palm-tree:": { - "style": "github", - "image": "1f334.png", - "unicode": "🌴", - "name": "Palm Tree" - }, - ":ok_woman:": { - "style": "github", - "ascii": "*\\0/*", - "image": "1f646.png", - "unicode": "🙆", - "name": "Face With Ok Gesture" - }, - ":track_previous:": { - "style": "github", - "image": "23ee.png", - "unicode": "⏮", - "name": "Black Left-pointing Double Triangle With Vertical Bar" - }, - "🚶🏾": { - "style": "unicode", - "image": "1f6b6-1f3fe.png", - "name": "Pedestrian - Tone 4" - }, - ":flag_jm:": { - "style": "github", - "image": "1f1ef-1f1f2.png", - "unicode": "🇯🇲", - "name": "Jamaica" - }, - ":regional_indicator_m:": { - "style": "github", - "image": "1f1f2.png", - "unicode": "🇲", - "name": "Regional Indicator Symbol Letter M" - }, - "♌": { - "style": "unicode", - "image": "264c.png", - "name": "Leo" - }, - ":mountain_bicyclist_tone4:": { - "style": "github", - "image": "1f6b5-1f3fe.png", - "unicode": "🚵🏾", - "name": "Mountain Bicyclist - Tone 4" - }, - ":no-good-tone2:": { - "style": "github", - "image": "1f645-1f3fc.png", - "unicode": "🙅🏼", - "name": "Face With No Good Gesture - Tone 2" - }, - ":flag-mh:": { - "style": "github", - "image": "1f1f2-1f1ed.png", - "unicode": "🇲🇭", - "name": "The Marshall Islands" - }, - ":flag-gb:": { - "style": "github", - "image": "1f1ec-1f1e7.png", - "unicode": "🇬🇧", - "name": "Great Britain" - }, - ":point-right-tone3:": { - "style": "github", - "image": "1f449-1f3fd.png", - "unicode": "👉🏽", - "name": "White Right Pointing Backhand Index - Tone 3" - }, - ":cactus:": { - "style": "github", - "image": "1f335.png", - "unicode": "🌵", - "name": "Cactus" - }, - ":male_dancer_tone3:": { - "style": "github", - "image": "1f57a-1f3fd.png", - "unicode": "🕺🏽", - "name": "Man Dancing - Tone 3" - }, - ":cry:": { - "style": "github", - "ascii": ":'(", - "image": "1f622.png", - "unicode": "😢", - "name": "Crying Face" - }, - ":bride_with_veil_tone3:": { - "style": "github", - "image": "1f470-1f3fd.png", - "unicode": "👰🏽", - "name": "Bride With Veil - Tone 3" - }, - ":wrestlers_tone2:": { - "style": "github", - "image": "1f93c-1f3fc.png", - "unicode": "🤼🏼", - "name": "Wrestlers - Tone 2" - }, - "🇸🇾": { - "style": "unicode", - "image": "1f1f8-1f1fe.png", - "name": "Syria" - }, - ":tropical-drink:": { - "style": "github", - "image": "1f379.png", - "unicode": "🍹", - "name": "Tropical Drink" - }, - ":man_in_business_suit_levitating:": { - "style": "github", - "image": "1f574.png", - "unicode": "🕴", - "name": "Man In Business Suit Levitating" - }, - ":film-frames:": { - "style": "github", - "image": "1f39e.png", - "unicode": "🎞", - "name": "Film Frames" - }, - "🔁": { - "style": "unicode", - "image": "1f501.png", - "name": "Clockwise Rightwards And Leftwards Open Circle Arrows" - }, - "🌅": { - "style": "unicode", - "image": "1f305.png", - "name": "Sunrise" - }, - ":+1_tone5:": { - "style": "github", - "image": "1f44d-1f3ff.png", - "unicode": "👍🏿", - "name": "Thumbs Up Sign - Tone 5" - }, - "🖖": { - "style": "unicode", - "image": "1f596.png", - "name": "Raised Hand With Part Between Middle And Ring Fingers" - }, - "🎚": { - "style": "unicode", - "image": "1f39a.png", - "name": "Level Slider" - }, - ":flag_ee:": { - "style": "github", - "image": "1f1ea-1f1ea.png", - "unicode": "🇪🇪", - "name": "Estonia" - }, - ":notepad-spiral:": { - "style": "github", - "image": "1f5d2.png", - "unicode": "🗒", - "name": "Spiral Note Pad" - }, - "🐫": { - "style": "unicode", - "image": "1f42b.png", - "name": "Bactrian Camel" - }, - ":point-up:": { - "style": "github", - "image": "261d.png", - "unicode": "☝", - "name": "White Up Pointing Index" - }, - "🈯": { - "style": "unicode", - "image": "1f22f.png", - "name": "Squared Cjk Unified Ideograph-6307" - }, - "👩❤💋👩": { - "style": "unicode", - "image": "1f469-2764-1f48b-1f469.png", - "name": "Kiss (woman,woman)" - }, - "ℹ": { - "style": "unicode", - "image": "2139.png", - "name": "Information Source" - }, - "📀": { - "style": "unicode", - "image": "1f4c0.png", - "name": "Dvd" - }, - ":flag_gw:": { - "style": "github", - "image": "1f1ec-1f1fc.png", - "unicode": "🇬🇼", - "name": "Guinea-bissau" - }, - ":flag_na:": { - "style": "github", - "image": "1f1f3-1f1e6.png", - "unicode": "🇳🇦", - "name": "Namibia" - }, - "🇮": { - "style": "unicode", - "image": "1f1ee.png", - "name": "Regional Indicator Symbol Letter I" - }, - ":busts-in-silhouette:": { - "style": "github", - "image": "1f465.png", - "unicode": "👥", - "name": "Busts In Silhouette" - }, - ":ao:": { - "style": "github", - "image": "1f1e6-1f1f4.png", - "unicode": "🇦🇴", - "name": "Angola" - }, - ":couple:": { - "style": "github", - "image": "1f46b.png", - "unicode": "👫", - "name": "Man And Woman Holding Hands" - }, - ":point_up_2:": { - "style": "github", - "image": "1f446.png", - "unicode": "👆", - "name": "White Up Pointing Backhand Index" - }, - ":flag-dz:": { - "style": "github", - "image": "1f1e9-1f1ff.png", - "unicode": "🇩🇿", - "name": "Algeria" - }, - ":mrs_claus_tone2:": { - "style": "github", - "image": "1f936-1f3fc.png", - "unicode": "🤶🏼", - "name": "Mother Christmas - Tone 2" - }, - "☢": { - "style": "unicode", - "image": "2622.png", - "name": "Radioactive Sign" - }, - ":wrestlers_tone1:": { - "style": "github", - "image": "1f93c-1f3fb.png", - "unicode": "🤼🏻", - "name": "Wrestlers - Tone 1" - }, - "🚭": { - "style": "unicode", - "image": "1f6ad.png", - "name": "No Smoking Symbol" - }, - ":keycap-ten:": { - "style": "github", - "image": "1f51f.png", - "unicode": "🔟", - "name": "Keycap Ten" - }, - "🙂": { - "style": "unicode", - "ascii": ":)", - "image": "1f642.png", - "name": "Slightly Smiling Face" - }, - ":mrs-claus-tone5:": { - "style": "github", - "image": "1f936-1f3ff.png", - "unicode": "🤶🏿", - "name": "Mother Christmas - Tone 5" - }, - "❌": { - "style": "unicode", - "image": "274c.png", - "name": "Cross Mark" - }, - ":regional-indicator-s:": { - "style": "github", - "image": "1f1f8.png", - "unicode": "🇸", - "name": "Regional Indicator Symbol Letter S" - }, - ":girl-tone5:": { - "style": "github", - "image": "1f467-1f3ff.png", - "unicode": "👧🏿", - "name": "Girl - Tone 5" - }, - ":yin_yang:": { - "style": "github", - "image": "262f.png", - "unicode": "☯", - "name": "Yin Yang" - }, - ":person_with_ball_tone2:": { - "style": "github", - "image": "26f9-1f3fc.png", - "unicode": "⛹🏼", - "name": "Person With Ball - Tone 2" - }, - ":sunflower:": { - "style": "github", - "image": "1f33b.png", - "unicode": "🌻", - "name": "Sunflower" - }, - ":wrestlers-tone4:": { - "style": "github", - "image": "1f93c-1f3fe.png", - "unicode": "🤼🏾", - "name": "Wrestlers - Tone 4" - }, - "🐁": { - "style": "unicode", - "image": "1f401.png", - "name": "Mouse" - }, - ":flag-tz:": { - "style": "github", - "image": "1f1f9-1f1ff.png", - "unicode": "🇹🇿", - "name": "Tanzania" - }, - ":raised_hand_with_fingers_splayed_tone4:": { - "style": "github", - "image": "1f590-1f3fe.png", - "unicode": "🖐🏾", - "name": "Raised Hand With Fingers Splayed - Tone 4" - }, - ":regional_indicator_k:": { - "style": "github", - "image": "1f1f0.png", - "unicode": "🇰", - "name": "Regional Indicator Symbol Letter K" - }, - ":bowling:": { - "style": "github", - "image": "1f3b3.png", - "unicode": "🎳", - "name": "Bowling" - }, - ":mailbox_closed:": { - "style": "github", - "image": "1f4ea.png", - "unicode": "📪", - "name": "Closed Mailbox With Lowered Flag" - }, - "🔫": { - "style": "unicode", - "image": "1f52b.png", - "name": "Pistol" - }, - ":fork-knife-plate:": { - "style": "github", - "image": "1f37d.png", - "unicode": "🍽", - "name": "Fork And Knife With Plate" - }, - ":clap:": { - "style": "github", - "image": "1f44f.png", - "unicode": "👏", - "name": "Clapping Hands Sign" - }, - "🌯": { - "style": "unicode", - "image": "1f32f.png", - "name": "Burrito" - }, - "🤳": { - "style": "unicode", - "image": "1f933.png", - "name": "Selfie" - }, - ":raised_hand_tone1:": { - "style": "github", - "image": "270b-1f3fb.png", - "unicode": "✋🏻", - "name": "Raised Hand - Tone 1" - }, - ":kiss_mm:": { - "style": "github", - "image": "1f468-2764-1f48b-1f468.png", - "unicode": "👨❤💋👨", - "name": "Kiss (man,man)" - }, - ":bicyclist_tone2:": { - "style": "github", - "image": "1f6b4-1f3fc.png", - "unicode": "🚴🏼", - "name": "Bicyclist - Tone 2" - }, - ":raised_hand_with_part_between_middle_and_ring_fingers_tone1:": { - "style": "github", - "image": "1f596-1f3fb.png", - "unicode": "🖖🏻", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 1" - }, - ":alien:": { - "style": "github", - "image": "1f47d.png", - "unicode": "👽", - "name": "Extraterrestrial Alien" - }, - ":worried:": { - "style": "github", - "image": "1f61f.png", - "unicode": "😟", - "name": "Worried Face" - }, - "🏄": { - "style": "unicode", - "image": "1f3c4.png", - "name": "Surfer" - }, - ":tuvalu:": { - "style": "github", - "image": "1f1f9-1f1fb.png", - "unicode": "🇹🇻", - "name": "Tuvalu" - }, - ":boy_tone3:": { - "style": "github", - "image": "1f466-1f3fd.png", - "unicode": "👦🏽", - "name": "Boy - Tone 3" - }, - ":muscle_tone1:": { - "style": "github", - "image": "1f4aa-1f3fb.png", - "unicode": "💪🏻", - "name": "Flexed Biceps - Tone 1" - }, - ":crystal_ball:": { - "style": "github", - "image": "1f52e.png", - "unicode": "🔮", - "name": "Crystal Ball" - }, - ">.<": { - "style": "ascii", - "ascii": ">.<", - "image": "1f623.png", - "unicode": "😣", - "name": "Persevering Face" - }, - ":bicyclist_tone4:": { - "style": "github", - "image": "1f6b4-1f3fe.png", - "unicode": "🚴🏾", - "name": "Bicyclist - Tone 4" - }, - ":earth_africa:": { - "style": "github", - "image": "1f30d.png", - "unicode": "🌍", - "name": "Earth Globe Europe-africa" - }, - ":water-polo-tone3:": { - "style": "github", - "image": "1f93d-1f3fd.png", - "unicode": "🤽🏽", - "name": "Water Polo - Tone 3" - }, - ":tuxedo_tone2:": { - "style": "github", - "image": "1f935-1f3fc.png", - "unicode": "🤵🏼", - "name": "Man In Tuxedo - Tone 2" - }, - ":oncoming_bus:": { - "style": "github", - "image": "1f68d.png", - "unicode": "🚍", - "name": "Oncoming Bus" - }, - ":department_store:": { - "style": "github", - "image": "1f3ec.png", - "unicode": "🏬", - "name": "Department Store" - }, - ":wind_chime:": { - "style": "github", - "image": "1f390.png", - "unicode": "🎐", - "name": "Wind Chime" - }, - "🥊": { - "style": "unicode", - "image": "1f94a.png", - "name": "Boxing Glove" - }, - ":city-sunset:": { - "style": "github", - "image": "1f307.png", - "unicode": "🌇", - "name": "Sunset Over Buildings" - }, - "📗": { - "style": "unicode", - "image": "1f4d7.png", - "name": "Green Book" - }, - ":cyclone:": { - "style": "github", - "image": "1f300.png", - "unicode": "🌀", - "name": "Cyclone" - }, - "🇱🇰": { - "style": "unicode", - "image": "1f1f1-1f1f0.png", - "name": "Sri Lanka" - }, - ":ar:": { - "style": "github", - "image": "1f1e6-1f1f7.png", - "unicode": "🇦🇷", - "name": "Argentina" - }, - "🇱🇷": { - "style": "unicode", - "image": "1f1f1-1f1f7.png", - "name": "Liberia" - }, - "🇱🇹": { - "style": "unicode", - "image": "1f1f1-1f1f9.png", - "name": "Lithuania" - }, - "🇱🇸": { - "style": "unicode", - "image": "1f1f1-1f1f8.png", - "name": "Lesotho" - }, - "🇱🇻": { - "style": "unicode", - "image": "1f1f1-1f1fb.png", - "name": "Latvia" - }, - "🇱🇺": { - "style": "unicode", - "image": "1f1f1-1f1fa.png", - "name": "Luxembourg" - }, - "👬": { - "style": "unicode", - "image": "1f46c.png", - "name": "Two Men Holding Hands" - }, - "🇱🇾": { - "style": "unicode", - "image": "1f1f1-1f1fe.png", - "name": "Libya" - }, - ":left_speech_bubble:": { - "style": "github", - "image": "1f5e8.png", - "unicode": "🗨", - "name": "Left Speech Bubble" - }, - ":grey-question:": { - "style": "github", - "image": "2754.png", - "unicode": "❔", - "name": "White Question Mark Ornament" - }, - "🇱🇧": { - "style": "unicode", - "image": "1f1f1-1f1e7.png", - "name": "Lebanon" - }, - "🇱🇦": { - "style": "unicode", - "image": "1f1f1-1f1e6.png", - "name": "Laos" - }, - "🇱🇨": { - "style": "unicode", - "image": "1f1f1-1f1e8.png", - "name": "Saint Lucia" - }, - ":flag-st:": { - "style": "github", - "image": "1f1f8-1f1f9.png", - "unicode": "🇸🇹", - "name": "São Tomé And Príncipe" - }, - ":horse_racing_tone3:": { - "style": "github", - "image": "1f3c7-1f3fd.png", - "unicode": "🏇🏽", - "name": "Horse Racing - Tone 3" - }, - "🇱🇮": { - "style": "unicode", - "image": "1f1f1-1f1ee.png", - "name": "Liechtenstein" - }, - ":flag-aq:": { - "style": "github", - "image": "1f1e6-1f1f6.png", - "unicode": "🇦🇶", - "name": "Antarctica" - }, - ":punch_tone1:": { - "style": "github", - "image": "1f44a-1f3fb.png", - "unicode": "👊🏻", - "name": "Fisted Hand Sign - Tone 1" - }, - ":cloud_snow:": { - "style": "github", - "image": "1f328.png", - "unicode": "🌨", - "name": "Cloud With Snow" - }, - "🆚": { - "style": "unicode", - "image": "1f19a.png", - "name": "Squared Vs" - }, - ":flag_bh:": { - "style": "github", - "image": "1f1e7-1f1ed.png", - "unicode": "🇧🇭", - "name": "Bahrain" - }, - "⚠": { - "style": "unicode", - "image": "26a0.png", - "name": "Warning Sign" - }, - "😫": { - "style": "unicode", - "image": "1f62b.png", - "name": "Tired Face" - }, - ":point_left_tone2:": { - "style": "github", - "image": "1f448-1f3fc.png", - "unicode": "👈🏼", - "name": "White Left Pointing Backhand Index - Tone 2" - }, - ":person_doing_cartwheel:": { - "style": "github", - "image": "1f938.png", - "unicode": "🤸", - "name": "Person Doing Cartwheel" - }, - ":fist_tone4:": { - "style": "github", - "image": "270a-1f3fe.png", - "unicode": "✊🏾", - "name": "Raised Fist - Tone 4" - }, - ":regional-indicator-l:": { - "style": "github", - "image": "1f1f1.png", - "unicode": "🇱", - "name": "Regional Indicator Symbol Letter L" - }, - "🍙": { - "style": "unicode", - "image": "1f359.png", - "name": "Rice Ball" - }, - ":dancer-tone2:": { - "style": "github", - "image": "1f483-1f3fc.png", - "unicode": "💃🏼", - "name": "Dancer - Tone 2" - }, - "🏮": { - "style": "unicode", - "image": "1f3ee.png", - "name": "Izakaya Lantern" - }, - ":princess-tone1:": { - "style": "github", - "image": "1f478-1f3fb.png", - "unicode": "👸🏻", - "name": "Princess - Tone 1" - }, - ":thumbsdown_tone5:": { - "style": "github", - "image": "1f44e-1f3ff.png", - "unicode": "👎🏿", - "name": "Thumbs Down Sign - Tone 5" - }, - "🎃": { - "style": "unicode", - "image": "1f383.png", - "name": "Jack-o-lantern" - }, - ":rowboat-tone2:": { - "style": "github", - "image": "1f6a3-1f3fc.png", - "unicode": "🚣🏼", - "name": "Rowboat - Tone 2" - }, - ":ox:": { - "style": "github", - "image": "1f402.png", - "unicode": "🐂", - "name": "Ox" - }, - ":neutral-face:": { - "style": "github", - "image": "1f610.png", - "unicode": "😐", - "name": "Neutral Face" - }, - ":flag_ye:": { - "style": "github", - "image": "1f1fe-1f1ea.png", - "unicode": "🇾🇪", - "name": "Yemen" - }, - "🌘": { - "style": "unicode", - "image": "1f318.png", - "name": "Waning Crescent Moon Symbol" - }, - ":beers:": { - "style": "github", - "image": "1f37b.png", - "unicode": "🍻", - "name": "Clinking Beer Mugs" - }, - ":bride-with-veil-tone5:": { - "style": "github", - "image": "1f470-1f3ff.png", - "unicode": "👰🏿", - "name": "Bride With Veil - Tone 5" - }, - "💭": { - "style": "unicode", - "image": "1f4ad.png", - "name": "Thought Balloon" - }, - ":flag-bs:": { - "style": "github", - "image": "1f1e7-1f1f8.png", - "unicode": "🇧🇸", - "name": "The Bahamas" - }, - "👂": { - "style": "unicode", - "image": "1f442.png", - "name": "Ear" - }, - ":u6e80:": { - "style": "github", - "image": "1f235.png", - "unicode": "🈵", - "name": "Squared Cjk Unified Ideograph-6e80" - }, - ":clock8:": { - "style": "github", - "image": "1f557.png", - "unicode": "🕗", - "name": "Clock Face Eight Oclock" - }, - ":mn:": { - "style": "github", - "image": "1f1f2-1f1f3.png", - "unicode": "🇲🇳", - "name": "Mongolia" - }, - ":wave-tone5:": { - "style": "github", - "image": "1f44b-1f3ff.png", - "unicode": "👋🏿", - "name": "Waving Hand Sign - Tone 5" - }, - ":fork_and_knife:": { - "style": "github", - "image": "1f374.png", - "unicode": "🍴", - "name": "Fork And Knife" - }, - ":ballot-box:": { - "style": "github", - "image": "1f5f3.png", - "unicode": "🗳", - "name": "Ballot Box With Ballot" - }, - ":military-medal:": { - "style": "github", - "image": "1f396.png", - "unicode": "🎖", - "name": "Military Medal" - }, - "😁": { - "style": "unicode", - "image": "1f601.png", - "name": "Grinning Face With Smiling Eyes" - }, - ":flag_cw:": { - "style": "github", - "image": "1f1e8-1f1fc.png", - "unicode": "🇨🇼", - "name": "Curaçao" - }, - ":flag_ro:": { - "style": "github", - "image": "1f1f7-1f1f4.png", - "unicode": "🇷🇴", - "name": "Romania" - }, - ":left_fist_tone5:": { - "style": "github", - "image": "1f91b-1f3ff.png", - "unicode": "🤛🏿", - "name": "Left Facing Fist - Tone 5" - }, - "✋": { - "style": "unicode", - "image": "270b.png", - "name": "Raised Hand" - }, - ":bookmark-tabs:": { - "style": "github", - "image": "1f4d1.png", - "unicode": "📑", - "name": "Bookmark Tabs" - }, - "🚖": { - "style": "unicode", - "image": "1f696.png", - "name": "Oncoming Taxi" - }, - ":dromedary_camel:": { - "style": "github", - "image": "1f42a.png", - "unicode": "🐪", - "name": "Dromedary Camel" - }, - ":call_me_tone1:": { - "style": "github", - "image": "1f919-1f3fb.png", - "unicode": "🤙🏻", - "name": "Call Me Hand - Tone 1" - }, - ":flag-rs:": { - "style": "github", - "image": "1f1f7-1f1f8.png", - "unicode": "🇷🇸", - "name": "Serbia" - }, - ":raised_back_of_hand_tone5:": { - "style": "github", - "image": "1f91a-1f3ff.png", - "unicode": "🤚🏿", - "name": "Raised Back Of Hand - Tone 5" - }, - ":flag_lb:": { - "style": "github", - "image": "1f1f1-1f1e7.png", - "unicode": "🇱🇧", - "name": "Lebanon" - }, - ":arrow-heading-up:": { - "style": "github", - "image": "2934.png", - "unicode": "⤴", - "name": "Arrow Pointing Rightwards Then Curving Upwards" - }, - ":national_park:": { - "style": "github", - "image": "1f3de.png", - "unicode": "🏞", - "name": "National Park" - }, - ":flag-no:": { - "style": "github", - "image": "1f1f3-1f1f4.png", - "unicode": "🇳🇴", - "name": "Norway" - }, - ":sleeping:": { - "style": "github", - "image": "1f634.png", - "unicode": "😴", - "name": "Sleeping Face" - }, - ":lifter_tone1:": { - "style": "github", - "image": "1f3cb-1f3fb.png", - "unicode": "🏋🏻", - "name": "Weight Lifter - Tone 1" - }, - ":hand_splayed:": { - "style": "github", - "image": "1f590.png", - "unicode": "🖐", - "name": "Raised Hand With Fingers Splayed" - }, - ":rw:": { - "style": "github", - "image": "1f1f7-1f1fc.png", - "unicode": "🇷🇼", - "name": "Rwanda" - }, - ":wrestlers:": { - "style": "github", - "image": "1f93c.png", - "unicode": "🤼", - "name": "Wrestlers" - }, - ":cop-tone3:": { - "style": "github", - "image": "1f46e-1f3fd.png", - "unicode": "👮🏽", - "name": "Police Officer - Tone 3" - }, - ":guardsman-tone2:": { - "style": "github", - "image": "1f482-1f3fc.png", - "unicode": "💂🏼", - "name": "Guardsman - Tone 2" - }, - "⏸": { - "style": "unicode", - "image": "23f8.png", - "name": "Double Vertical Bar" - }, - ":flag_mz:": { - "style": "github", - "image": "1f1f2-1f1ff.png", - "unicode": "🇲🇿", - "name": "Mozambique" - }, - ":um:": { - "style": "github", - "image": "1f1fa-1f1f2.png", - "unicode": "🇺🇲", - "name": "United States Minor Outlying Islands" - }, - ":point_right_tone2:": { - "style": "github", - "image": "1f449-1f3fc.png", - "unicode": "👉🏼", - "name": "White Right Pointing Backhand Index - Tone 2" - }, - "➕": { - "style": "unicode", - "image": "2795.png", - "name": "Heavy Plus Sign" - }, - ":racehorse:": { - "style": "github", - "image": "1f40e.png", - "unicode": "🐎", - "name": "Horse" - }, - "🇰🇲": { - "style": "unicode", - "image": "1f1f0-1f1f2.png", - "name": "The Comoros" - }, - "🇰🇳": { - "style": "unicode", - "image": "1f1f0-1f1f3.png", - "name": "Saint Kitts And Nevis" - }, - ":dog:": { - "style": "github", - "image": "1f436.png", - "unicode": "🐶", - "name": "Dog Face" - }, - ":fish:": { - "style": "github", - "image": "1f41f.png", - "unicode": "🐟", - "name": "Fish" - }, - "🇰🇷": { - "style": "unicode", - "image": "1f1f0-1f1f7.png", - "name": "Korea" - }, - "🇰🇵": { - "style": "unicode", - "image": "1f1f0-1f1f5.png", - "name": "North Korea" - }, - "':D": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - "🇰🇾": { - "style": "unicode", - "image": "1f1f0-1f1fe.png", - "name": "Cayman Islands" - }, - "🇰🇿": { - "style": "unicode", - "image": "1f1f0-1f1ff.png", - "name": "Kazakhstan" - }, - "🇰🇼": { - "style": "unicode", - "image": "1f1f0-1f1fc.png", - "name": "Kuwait" - }, - ":clock430:": { - "style": "github", - "image": "1f55f.png", - "unicode": "🕟", - "name": "Clock Face Four-thirty" - }, - "🇰🇪": { - "style": "unicode", - "image": "1f1f0-1f1ea.png", - "name": "Kenya" - }, - "👞": { - "style": "unicode", - "image": "1f45e.png", - "name": "Mans Shoe" - }, - "🇰🇮": { - "style": "unicode", - "image": "1f1f0-1f1ee.png", - "name": "Kiribati" - }, - "🇰🇬": { - "style": "unicode", - "image": "1f1f0-1f1ec.png", - "name": "Kyrgyzstan" - }, - "🇰🇭": { - "style": "unicode", - "image": "1f1f0-1f1ed.png", - "name": "Cambodia" - }, - "':(": { - "style": "ascii", - "ascii": "':(", - "image": "1f613.png", - "unicode": "😓", - "name": "Face With Cold Sweat" - }, - "':)": { - "style": "ascii", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - ":clock330:": { - "style": "github", - "image": "1f55e.png", - "unicode": "🕞", - "name": "Clock Face Three-thirty" - }, - ":u7981:": { - "style": "github", - "image": "1f232.png", - "unicode": "🈲", - "name": "Squared Cjk Unified Ideograph-7981" - }, - ":gem:": { - "style": "github", - "image": "1f48e.png", - "unicode": "💎", - "name": "Gem Stone" - }, - ":racing_motorcycle:": { - "style": "github", - "image": "1f3cd.png", - "unicode": "🏍", - "name": "Racing Motorcycle" - }, - ":older-woman-tone2:": { - "style": "github", - "image": "1f475-1f3fc.png", - "unicode": "👵🏼", - "name": "Older Woman - Tone 2" - }, - ":flag-mc:": { - "style": "github", - "image": "1f1f2-1f1e8.png", - "unicode": "🇲🇨", - "name": "Monaco" - }, - ":handball_tone3:": { - "style": "github", - "image": "1f93e-1f3fd.png", - "unicode": "🤾🏽", - "name": "Handball - Tone 3" - }, - "🔉": { - "style": "unicode", - "image": "1f509.png", - "name": "Speaker With One Sound Wave" - }, - ":rooster:": { - "style": "github", - "image": "1f413.png", - "unicode": "🐓", - "name": "Rooster" - }, - "↙": { - "style": "unicode", - "image": "2199.png", - "name": "South West Arrow" - }, - ":circus_tent:": { - "style": "github", - "image": "1f3aa.png", - "unicode": "🎪", - "name": "Circus Tent" - }, - ":rice_scene:": { - "style": "github", - "image": "1f391.png", - "unicode": "🎑", - "name": "Moon Viewing Ceremony" - }, - ":raised_hand_with_fingers_splayed:": { - "style": "github", - "image": "1f590.png", - "unicode": "🖐", - "name": "Raised Hand With Fingers Splayed" - }, - "🐳": { - "style": "unicode", - "image": "1f433.png", - "name": "Spouting Whale" - }, - ":flag_jo:": { - "style": "github", - "image": "1f1ef-1f1f4.png", - "unicode": "🇯🇴", - "name": "Jordan" - }, - ":no-good-tone4:": { - "style": "github", - "image": "1f645-1f3fe.png", - "unicode": "🙅🏾", - "name": "Face With No Good Gesture - Tone 4" - }, - ":scream_cat:": { - "style": "github", - "image": "1f640.png", - "unicode": "🙀", - "name": "Weary Cat Face" - }, - "📈": { - "style": "unicode", - "image": "1f4c8.png", - "name": "Chart With Upwards Trend" - }, - ":haircut_tone2:": { - "style": "github", - "image": "1f487-1f3fc.png", - "unicode": "💇🏼", - "name": "Haircut - Tone 2" - }, - ":school-satchel:": { - "style": "github", - "image": "1f392.png", - "unicode": "🎒", - "name": "School Satchel" - }, - ":'-)": { - "style": "ascii", - "ascii": ":')", - "image": "1f602.png", - "unicode": "😂", - "name": "Face With Tears Of Joy" - }, - "🍝": { - "style": "unicode", - "image": "1f35d.png", - "name": "Spaghetti" - }, - ":point-right-tone1:": { - "style": "github", - "image": "1f449-1f3fb.png", - "unicode": "👉🏻", - "name": "White Right Pointing Backhand Index - Tone 1" - }, - ":flag-mf:": { - "style": "github", - "image": "1f1f2-1f1eb.png", - "unicode": "🇲🇫", - "name": "Saint Martin" - }, - ":flag-gd:": { - "style": "github", - "image": "1f1ec-1f1e9.png", - "unicode": "🇬🇩", - "name": "Grenada" - }, - "🎇": { - "style": "unicode", - "image": "1f387.png", - "name": "Firework Sparkler" - }, - ":point_left:": { - "style": "github", - "image": "1f448.png", - "unicode": "👈", - "name": "White Left Pointing Backhand Index" - }, - ":busts_in_silhouette:": { - "style": "github", - "image": "1f465.png", - "unicode": "👥", - "name": "Busts In Silhouette" - }, - ":full_moon_with_face:": { - "style": "github", - "image": "1f31d.png", - "unicode": "🌝", - "name": "Full Moon With Face" - }, - ":gm:": { - "style": "github", - "image": "1f1ec-1f1f2.png", - "unicode": "🇬🇲", - "name": "The Gambia" - }, - ";(": { - "style": "ascii", - "ascii": ":'(", - "image": "1f622.png", - "unicode": "😢", - "name": "Crying Face" - }, - ":homes:": { - "style": "github", - "image": "1f3d8.png", - "unicode": "🏘", - "name": "House Buildings" - }, - ":flag_eg:": { - "style": "github", - "image": "1f1ea-1f1ec.png", - "unicode": "🇪🇬", - "name": "Egypt" - }, - "🚵": { - "style": "unicode", - "image": "1f6b5.png", - "name": "Mountain Bicyclist" - }, - "🙋🏽": { - "style": "unicode", - "image": "1f64b-1f3fd.png", - "name": "Happy Person Raising One Hand Tone3" - }, - "➿": { - "style": "unicode", - "image": "27bf.png", - "name": "Double Curly Loop" - }, - ":passenger_ship:": { - "style": "github", - "image": "1f6f3.png", - "unicode": "🛳", - "name": "Passenger Ship" - }, - "🙊": { - "style": "unicode", - "image": "1f64a.png", - "name": "Speak-no-evil Monkey" - }, - ":arrow-backward:": { - "style": "github", - "image": "25c0.png", - "unicode": "◀", - "name": "Black Left-pointing Triangle" - }, - "⛹🏻": { - "style": "unicode", - "image": "26f9-1f3fb.png", - "name": "Person With Ball - Tone 1" - }, - "⛹🏽": { - "style": "unicode", - "image": "26f9-1f3fd.png", - "name": "Person With Ball - Tone 3" - }, - "❔": { - "style": "unicode", - "image": "2754.png", - "name": "White Question Mark Ornament" - }, - "⛹🏿": { - "style": "unicode", - "image": "26f9-1f3ff.png", - "name": "Person With Ball - Tone 5" - }, - "⛹🏾": { - "style": "unicode", - "image": "26f9-1f3fe.png", - "name": "Person With Ball - Tone 4" - }, - ":flag-fj:": { - "style": "github", - "image": "1f1eb-1f1ef.png", - "unicode": "🇫🇯", - "name": "Fiji" - }, - ":ghost:": { - "style": "github", - "image": "1f47b.png", - "unicode": "👻", - "name": "Ghost" - }, - ":flag_nc:": { - "style": "github", - "image": "1f1f3-1f1e8.png", - "unicode": "🇳🇨", - "name": "New Caledonia" - }, - ":fuelpump:": { - "style": "github", - "image": "26fd.png", - "unicode": "⛽", - "name": "Fuel Pump" - }, - "🅰": { - "style": "unicode", - "image": "1f170.png", - "name": "Negative Squared Latin Capital Letter A" - }, - ":ai:": { - "style": "github", - "image": "1f1e6-1f1ee.png", - "unicode": "🇦🇮", - "name": "Anguilla" - }, - ":sweat-smile:": { - "style": "github", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - ":person-with-blond-hair-tone1:": { - "style": "github", - "image": "1f471-1f3fb.png", - "unicode": "👱🏻", - "name": "Person With Blond Hair - Tone 1" - }, - "🐉": { - "style": "unicode", - "image": "1f409.png", - "name": "Dragon" - }, - ":mrs_claus_tone4:": { - "style": "github", - "image": "1f936-1f3fe.png", - "unicode": "🤶🏾", - "name": "Mother Christmas - Tone 4" - }, - "💞": { - "style": "unicode", - "image": "1f49e.png", - "name": "Revolving Hearts" - }, - ":speedboat:": { - "style": "github", - "image": "1f6a4.png", - "unicode": "🚤", - "name": "Speedboat" - }, - "⛰": { - "style": "unicode", - "image": "26f0.png", - "name": "Mountain" - }, - "🔳": { - "style": "unicode", - "image": "1f533.png", - "name": "White Square Button" - }, - ":first_quarter_moon:": { - "style": "github", - "image": "1f313.png", - "unicode": "🌓", - "name": "First Quarter Moon Symbol" - }, - ":aerial-tramway:": { - "style": "github", - "image": "1f6a1.png", - "unicode": "🚡", - "name": "Aerial Tramway" - }, - ":red-car:": { - "style": "github", - "image": "1f697.png", - "unicode": "🚗", - "name": "Automobile" - }, - "🧀": { - "style": "unicode", - "image": "1f9c0.png", - "name": "Cheese Wedge" - }, - ":regional-indicator-q:": { - "style": "github", - "image": "1f1f6.png", - "unicode": "🇶", - "name": "Regional Indicator Symbol Letter Q" - }, - ":sweat:": { - "style": "github", - "ascii": "':(", - "image": "1f613.png", - "unicode": "😓", - "name": "Face With Cold Sweat" - }, - ":coffee:": { - "style": "github", - "image": "2615.png", - "unicode": "☕", - "name": "Hot Beverage" - }, - ":briefcase:": { - "style": "github", - "image": "1f4bc.png", - "unicode": "💼", - "name": "Briefcase" - }, - ":person_with_ball_tone4:": { - "style": "github", - "image": "26f9-1f3fe.png", - "unicode": "⛹🏾", - "name": "Person With Ball - Tone 4" - }, - ":flag_ic:": { - "style": "github", - "image": "1f1ee-1f1e8.png", - "unicode": "🇮🇨", - "name": "Canary Islands" - }, - "☝🏽": { - "style": "unicode", - "image": "261d-1f3fd.png", - "name": "White Up Pointing Index - Tone 3" - }, - "☝🏼": { - "style": "unicode", - "image": "261d-1f3fc.png", - "name": "White Up Pointing Index - Tone 2" - }, - "☝🏿": { - "style": "unicode", - "image": "261d-1f3ff.png", - "name": "White Up Pointing Index - Tone 5" - }, - "☝🏾": { - "style": "unicode", - "image": "261d-1f3fe.png", - "name": "White Up Pointing Index - Tone 4" - }, - ":stuck-out-tongue:": { - "style": "github", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":point_up_tone2:": { - "style": "github", - "image": "261d-1f3fc.png", - "unicode": "☝🏼", - "name": "White Up Pointing Index - Tone 2" - }, - ":flag_vn:": { - "style": "github", - "image": "1f1fb-1f1f3.png", - "unicode": "🇻🇳", - "name": "Vietnam" - }, - "🎱": { - "style": "unicode", - "image": "1f3b1.png", - "name": "Billiards" - }, - ":prayer-beads:": { - "style": "github", - "image": "1f4ff.png", - "unicode": "📿", - "name": "Prayer Beads" - }, - ":interrobang:": { - "style": "github", - "image": "2049.png", - "unicode": "⁉", - "name": "Exclamation Question Mark" - }, - "🥂": { - "style": "unicode", - "image": "1f942.png", - "name": "Clinking Glasses" - }, - "🍆": { - "style": "unicode", - "image": "1f346.png", - "name": "Aubergine" - }, - "8⃣": { - "style": "unicode", - "image": "0038-20e3.png", - "name": "Keycap Digit Eight" - }, - ":name_badge:": { - "style": "github", - "image": "1f4db.png", - "unicode": "📛", - "name": "Name Badge" - }, - ":flag_ar:": { - "style": "github", - "image": "1f1e6-1f1f7.png", - "unicode": "🇦🇷", - "name": "Argentina" - }, - ":computer:": { - "style": "github", - "image": "1f4bb.png", - "unicode": "💻", - "name": "Personal Computer" - }, - "📟": { - "style": "unicode", - "image": "1f4df.png", - "name": "Pager" - }, - ":nose:": { - "style": "github", - "image": "1f443.png", - "unicode": "👃", - "name": "Nose" - }, - ":wavy-dash:": { - "style": "github", - "image": "3030.png", - "unicode": "〰", - "name": "Wavy Dash" - }, - ":boy_tone5:": { - "style": "github", - "image": "1f466-1f3ff.png", - "unicode": "👦🏿", - "name": "Boy - Tone 5" - }, - "👴": { - "style": "unicode", - "image": "1f474.png", - "name": "Older Man" - }, - ":hand_with_index_and_middle_fingers_crossed_tone2:": { - "style": "github", - "image": "1f91e-1f3fc.png", - "unicode": "🤞🏼", - "name": "Hand With Index And Middle Fingers Crossed - Tone 2" - }, - ":regional-indicator-c:": { - "style": "github", - "image": "1f1e8.png", - "unicode": "🇨", - "name": "Regional Indicator Symbol Letter C" - }, - ":grinning:": { - "style": "github", - "image": "1f600.png", - "unicode": "😀", - "name": "Grinning Face" - }, - ":flag-ke:": { - "style": "github", - "image": "1f1f0-1f1ea.png", - "unicode": "🇰🇪", - "name": "Kenya" - }, - ":juggling_tone2:": { - "style": "github", - "image": "1f939-1f3fc.png", - "unicode": "🤹🏼", - "name": "Juggling - Tone 2" - }, - ":arrow_down:": { - "style": "github", - "image": "2b07.png", - "unicode": "⬇", - "name": "Downwards Black Arrow" - }, - ":moyai:": { - "style": "github", - "image": "1f5ff.png", - "unicode": "🗿", - "name": "Moyai" - }, - "8-D": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - ":lc:": { - "style": "github", - "image": "1f1f1-1f1e8.png", - "unicode": "🇱🇨", - "name": "Saint Lucia" - }, - ":flag_ch:": { - "style": "github", - "image": "1f1e8-1f1ed.png", - "unicode": "🇨🇭", - "name": "Switzerland" - }, - ":water-polo-tone1:": { - "style": "github", - "image": "1f93d-1f3fb.png", - "unicode": "🤽🏻", - "name": "Water Polo - Tone 1" - }, - ":tuxedo_tone4:": { - "style": "github", - "image": "1f935-1f3fe.png", - "unicode": "🤵🏾", - "name": "Man In Tuxedo - Tone 4" - }, - ":shaking_hands_tone4:": { - "style": "github", - "image": "1f91d-1f3fe.png", - "unicode": "🤝🏾", - "name": "Handshake - Tone 4" - }, - ":flag_bf:": { - "style": "github", - "image": "1f1e7-1f1eb.png", - "unicode": "🇧🇫", - "name": "Burkina Faso" - }, - ":couple_mm:": { - "style": "github", - "image": "1f468-2764-1f468.png", - "unicode": "👨❤👨", - "name": "Couple (man,man)" - }, - "😳": { - "style": "unicode", - "ascii": ":$", - "image": "1f633.png", - "name": "Flushed Face" - }, - ":santa-tone3:": { - "style": "github", - "image": "1f385-1f3fd.png", - "unicode": "🎅🏽", - "name": "Father Christmas - Tone 3" - }, - ":x:": { - "style": "github", - "image": "274c.png", - "unicode": "❌", - "name": "Cross Mark" - }, - ":baby-bottle:": { - "style": "github", - "image": "1f37c.png", - "unicode": "🍼", - "name": "Baby Bottle" - }, - ":muscle_tone2:": { - "style": "github", - "image": "1f4aa-1f3fc.png", - "unicode": "💪🏼", - "name": "Flexed Biceps - Tone 2" - }, - ":blue-book:": { - "style": "github", - "image": "1f4d8.png", - "unicode": "📘", - "name": "Blue Book" - }, - ":flag-cu:": { - "style": "github", - "image": "1f1e8-1f1fa.png", - "unicode": "🇨🇺", - "name": "Cuba" - }, - ":metal_tone4:": { - "style": "github", - "image": "1f918-1f3fe.png", - "unicode": "🤘🏾", - "name": "Sign Of The Horns - Tone 4" - }, - "🇹": { - "style": "unicode", - "image": "1f1f9.png", - "name": "Regional Indicator Symbol Letter T" - }, - ":trackball:": { - "style": "github", - "image": "1f5b2.png", - "unicode": "🖲", - "name": "Trackball" - }, - "8-)": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - "🇲": { - "style": "unicode", - "image": "1f1f2.png", - "name": "Regional Indicator Symbol Letter M" - }, - ":bread:": { - "style": "github", - "image": "1f35e.png", - "unicode": "🍞", - "name": "Bread" - }, - ":airplane-departure:": { - "style": "github", - "image": "1f6eb.png", - "unicode": "🛫", - "name": "Airplane Departure" - }, - ":horse_racing_tone1:": { - "style": "github", - "image": "1f3c7-1f3fb.png", - "unicode": "🏇🏻", - "name": "Horse Racing - Tone 1" - }, - ":flag-sv:": { - "style": "github", - "image": "1f1f8-1f1fb.png", - "unicode": "🇸🇻", - "name": "El Salvador" - }, - ":eight-pointed-black-star:": { - "style": "github", - "image": "2734.png", - "unicode": "✴", - "name": "Eight Pointed Black Star" - }, - ":flag_bj:": { - "style": "github", - "image": "1f1e7-1f1ef.png", - "unicode": "🇧🇯", - "name": "Benin" - }, - ":arrow-up-small:": { - "style": "github", - "image": "1f53c.png", - "unicode": "🔼", - "name": "Up-pointing Small Red Triangle" - }, - ":flag-aw:": { - "style": "github", - "image": "1f1e6-1f1fc.png", - "unicode": "🇦🇼", - "name": "Aruba" - }, - ":airplane_departure:": { - "style": "github", - "image": "1f6eb.png", - "unicode": "🛫", - "name": "Airplane Departure" - }, - ":il:": { - "style": "github", - "image": "1f1ee-1f1f1.png", - "unicode": "🇮🇱", - "name": "Israel" - }, - ":up:": { - "style": "github", - "image": "1f199.png", - "unicode": "🆙", - "name": "Squared Up With Exclamation Mark" - }, - "💵": { - "style": "unicode", - "image": "1f4b5.png", - "name": "Banknote With Dollar Sign" - }, - ":convenience_store:": { - "style": "github", - "image": "1f3ea.png", - "unicode": "🏪", - "name": "Convenience Store" - }, - ":star:": { - "style": "github", - "image": "2b50.png", - "unicode": "⭐", - "name": "White Medium Star" - }, - ":point_left_tone4:": { - "style": "github", - "image": "1f448-1f3fe.png", - "unicode": "👈🏾", - "name": "White Left Pointing Backhand Index - Tone 4" - }, - ":thumbsup_tone5:": { - "style": "github", - "image": "1f44d-1f3ff.png", - "unicode": "👍🏿", - "name": "Thumbs Up Sign - Tone 5" - }, - "👊": { - "style": "unicode", - "image": "1f44a.png", - "name": "Fisted Hand Sign" - }, - ":princess-tone3:": { - "style": "github", - "image": "1f478-1f3fd.png", - "unicode": "👸🏽", - "name": "Princess - Tone 3" - }, - "🏛": { - "style": "unicode", - "image": "1f3db.png", - "name": "Classical Building" - }, - ":no_entry_sign:": { - "style": "github", - "image": "1f6ab.png", - "unicode": "🚫", - "name": "No Entry Sign" - }, - ":womans-clothes:": { - "style": "github", - "image": "1f45a.png", - "unicode": "👚", - "name": "Womans Clothes" - }, - ":free:": { - "style": "github", - "image": "1f193.png", - "unicode": "🆓", - "name": "Squared Free" - }, - "⛩": { - "style": "unicode", - "image": "26e9.png", - "name": "Shinto Shrine" - }, - "🍰": { - "style": "unicode", - "image": "1f370.png", - "name": "Shortcake" - }, - "🕴": { - "style": "unicode", - "image": "1f574.png", - "name": "Man In Business Suit Levitating" - }, - ":walking_tone1:": { - "style": "github", - "image": "1f6b6-1f3fb.png", - "unicode": "🚶🏻", - "name": "Pedestrian - Tone 1" - }, - "😉": { - "style": "unicode", - "ascii": ";)", - "image": "1f609.png", - "name": "Winking Face" - }, - ":basketball-player:": { - "style": "github", - "image": "26f9.png", - "unicode": "⛹", - "name": "Person With Ball" - }, - "🚞": { - "style": "unicode", - "image": "1f69e.png", - "name": "Mountain Railway" - }, - ":right_facing_fist_tone5:": { - "style": "github", - "image": "1f91c-1f3ff.png", - "unicode": "🤜🏿", - "name": "Right Facing Fist - Tone 5" - }, - ":flag-bq:": { - "style": "github", - "image": "1f1e7-1f1f6.png", - "unicode": "🇧🇶", - "name": "Caribbean Netherlands" - }, - ":wave-tone3:": { - "style": "github", - "image": "1f44b-1f3fd.png", - "unicode": "👋🏽", - "name": "Waving Hand Sign - Tone 3" - }, - ":person-frowning-tone5:": { - "style": "github", - "image": "1f64d-1f3ff.png", - "unicode": "🙍🏿", - "name": "Person Frowning - Tone 5" - }, - ":anger_right:": { - "style": "github", - "image": "1f5ef.png", - "unicode": "🗯", - "name": "Right Anger Bubble" - }, - ":clinking_glass:": { - "style": "github", - "image": "1f942.png", - "unicode": "🥂", - "name": "Clinking Glasses" - }, - ":flag-bm:": { - "style": "github", - "image": "1f1e7-1f1f2.png", - "unicode": "🇧🇲", - "name": "Bermuda" - }, - ":basketball_player_tone2:": { - "style": "github", - "image": "26f9-1f3fc.png", - "unicode": "⛹🏼", - "name": "Person With Ball - Tone 2" - }, - ":cold_sweat:": { - "style": "github", - "image": "1f630.png", - "unicode": "😰", - "name": "Face With Open Mouth And Cold Sweat" - }, - ":leopard:": { - "style": "github", - "image": "1f406.png", - "unicode": "🐆", - "name": "Leopard" - }, - ":td:": { - "style": "github", - "image": "1f1f9-1f1e9.png", - "unicode": "🇹🇩", - "name": "Chad" - }, - ":loud-sound:": { - "style": "github", - "image": "1f50a.png", - "unicode": "🔊", - "name": "Speaker With Three Sound Waves" - }, - "👴🏾": { - "style": "unicode", - "image": "1f474-1f3fe.png", - "name": "Older Man - Tone 4" - }, - "🚇": { - "style": "unicode", - "image": "1f687.png", - "name": "Metro" - }, - ":person-with-pouting-face-tone5:": { - "style": "github", - "image": "1f64e-1f3ff.png", - "unicode": "🙎🏿", - "name": "Person With Pouting Face Tone5" - }, - ":flag_cu:": { - "style": "github", - "image": "1f1e8-1f1fa.png", - "unicode": "🇨🇺", - "name": "Cuba" - }, - ":control-knobs:": { - "style": "github", - "image": "1f39b.png", - "unicode": "🎛", - "name": "Control Knobs" - }, - ":tw:": { - "style": "github", - "image": "1f1f9-1f1fc.png", - "unicode": "🇹🇼", - "name": "The Republic Of China" - }, - ":scorpius:": { - "style": "github", - "image": "264f.png", - "unicode": "♏", - "name": "Scorpius" - }, - "😜": { - "style": "unicode", - "ascii": ">:P", - "image": "1f61c.png", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - "🖱": { - "style": "unicode", - "image": "1f5b1.png", - "name": "Three Button Mouse" - }, - "🎵": { - "style": "unicode", - "image": "1f3b5.png", - "name": "Musical Note" - }, - ":dromedary-camel:": { - "style": "github", - "image": "1f42a.png", - "unicode": "🐪", - "name": "Dromedary Camel" - }, - ":clap-tone4:": { - "style": "github", - "image": "1f44f-1f3fe.png", - "unicode": "👏🏾", - "name": "Clapping Hands Sign - Tone 4" - }, - ":mantlepiece_clock:": { - "style": "github", - "image": "1f570.png", - "unicode": "🕰", - "name": "Mantlepiece Clock" - }, - "🍊": { - "style": "unicode", - "image": "1f34a.png", - "name": "Tangerine" - }, - "📛": { - "style": "unicode", - "image": "1f4db.png", - "name": "Name Badge" - }, - ":lifter_tone3:": { - "style": "github", - "image": "1f3cb-1f3fd.png", - "unicode": "🏋🏽", - "name": "Weight Lifter - Tone 3" - }, - ":black-heart:": { - "style": "github", - "image": "1f5a4.png", - "unicode": "🖤", - "name": "Black Heart" - }, - ":nose_tone4:": { - "style": "github", - "image": "1f443-1f3fe.png", - "unicode": "👃🏾", - "name": "Nose - Tone 4" - }, - ":door:": { - "style": "github", - "image": "1f6aa.png", - "unicode": "🚪", - "name": "Door" - }, - ":cop-tone1:": { - "style": "github", - "image": "1f46e-1f3fb.png", - "unicode": "👮🏻", - "name": "Police Officer - Tone 1" - }, - "👰": { - "style": "unicode", - "image": "1f470.png", - "name": "Bride With Veil" - }, - ":br:": { - "style": "github", - "image": "1f1e7-1f1f7.png", - "unicode": "🇧🇷", - "name": "Brazil" - }, - ":sparkles:": { - "style": "github", - "image": "2728.png", - "unicode": "✨", - "name": "Sparkles" - }, - ":chart_with_upwards_trend:": { - "style": "github", - "image": "1f4c8.png", - "unicode": "📈", - "name": "Chart With Upwards Trend" - }, - "🏃🏻": { - "style": "unicode", - "image": "1f3c3-1f3fb.png", - "name": "Runner - Tone 1" - }, - ":clock12:": { - "style": "github", - "image": "1f55b.png", - "unicode": "🕛", - "name": "Clock Face Twelve Oclock" - }, - ":xk:": { - "style": "github", - "image": "1f1fd-1f1f0.png", - "unicode": "🇽🇰", - "name": "Kosovo" - }, - "🥙": { - "style": "unicode", - "image": "1f959.png", - "name": "Stuffed Flatbread" - }, - ":soccer:": { - "style": "github", - "image": "26bd.png", - "unicode": "⚽", - "name": "Soccer Ball" - }, - "▪": { - "style": "unicode", - "image": "25aa.png", - "name": "Black Small Square" - }, - ":smirk:": { - "style": "github", - "image": "1f60f.png", - "unicode": "😏", - "name": "Smirking Face" - }, - ":ok-woman-tone5:": { - "style": "github", - "image": "1f646-1f3ff.png", - "unicode": "🙆🏿", - "name": "Face With Ok Gesture Tone5" - }, - ":pouting_cat:": { - "style": "github", - "image": "1f63e.png", - "unicode": "😾", - "name": "Pouting Cat Face" - }, - ":call-me:": { - "style": "github", - "image": "1f919.png", - "unicode": "🤙", - "name": "Call Me Hand" - }, - "🦃": { - "style": "unicode", - "image": "1f983.png", - "name": "Turkey" - }, - ":slight-smile:": { - "style": "github", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - ":raised-hand-tone4:": { - "style": "github", - "image": "270b-1f3fe.png", - "unicode": "✋🏾", - "name": "Raised Hand - Tone 4" - }, - ":handball_tone1:": { - "style": "github", - "image": "1f93e-1f3fb.png", - "unicode": "🤾🏻", - "name": "Handball - Tone 1" - }, - "🤘": { - "style": "unicode", - "image": "1f918.png", - "name": "Sign Of The Horns" - }, - ":joystick:": { - "style": "github", - "image": "1f579.png", - "unicode": "🕹", - "name": "Joystick" - }, - ":flag_vu:": { - "style": "github", - "image": "1f1fb-1f1fa.png", - "unicode": "🇻🇺", - "name": "Vanuatu" - }, - "👆": { - "style": "unicode", - "image": "1f446.png", - "name": "White Up Pointing Backhand Index" - }, - ":flag-gf:": { - "style": "github", - "image": "1f1ec-1f1eb.png", - "unicode": "🇬🇫", - "name": "French Guiana" - }, - ":pregnant_woman_tone5:": { - "style": "github", - "image": "1f930-1f3ff.png", - "unicode": "🤰🏿", - "name": "Pregnant Woman - Tone 5" - }, - ":pisces:": { - "style": "github", - "image": "2653.png", - "unicode": "♓", - "name": "Pisces" - }, - "🏟": { - "style": "unicode", - "image": "1f3df.png", - "name": "Stadium" - }, - ":flag-md:": { - "style": "github", - "image": "1f1f2-1f1e9.png", - "unicode": "🇲🇩", - "name": "Moldova" - }, - "🕰": { - "style": "unicode", - "image": "1f570.png", - "name": "Mantlepiece Clock" - }, - ":ok_hand:": { - "style": "github", - "image": "1f44c.png", - "unicode": "👌", - "name": "Ok Hand Sign" - }, - "🍴": { - "style": "unicode", - "image": "1f374.png", - "name": "Fork And Knife" - }, - ":haircut_tone5:": { - "style": "github", - "image": "1f487-1f3ff.png", - "unicode": "💇🏿", - "name": "Haircut - Tone 5" - }, - ":surfer-tone5:": { - "style": "github", - "image": "1f3c4-1f3ff.png", - "unicode": "🏄🏿", - "name": "Surfer - Tone 5" - }, - ":wavy_dash:": { - "style": "github", - "image": "3030.png", - "unicode": "〰", - "name": "Wavy Dash" - }, - ":next_track:": { - "style": "github", - "image": "23ed.png", - "unicode": "⏭", - "name": "Black Right-pointing Double Triangle With Vertical Bar" - }, - ":mrs_claus:": { - "style": "github", - "image": "1f936.png", - "unicode": "🤶", - "name": "Mother Christmas" - }, - ":hand_with_index_and_middle_fingers_crossed_tone1:": { - "style": "github", - "image": "1f91e-1f3fb.png", - "unicode": "🤞🏻", - "name": "Hand With Index And Middle Fingers Crossed - Tone 1" - }, - ":alarm-clock:": { - "style": "github", - "image": "23f0.png", - "unicode": "⏰", - "name": "Alarm Clock" - }, - ":flag-et:": { - "style": "github", - "image": "1f1ea-1f1f9.png", - "unicode": "🇪🇹", - "name": "Ethiopia" - }, - ":information_desk_person:": { - "style": "github", - "image": "1f481.png", - "unicode": "💁", - "name": "Information Desk Person" - }, - ":chart:": { - "style": "github", - "image": "1f4b9.png", - "unicode": "💹", - "name": "Chart With Upwards Trend And Yen Sign" - }, - ":pray_tone4:": { - "style": "github", - "image": "1f64f-1f3fe.png", - "unicode": "🙏🏾", - "name": "Person With Folded Hands - Tone 4" - }, - "🇿🇼": { - "style": "unicode", - "image": "1f1ff-1f1fc.png", - "name": "Zimbabwe" - }, - "🇿🇲": { - "style": "unicode", - "image": "1f1ff-1f1f2.png", - "name": "Zambia" - }, - ":expecting_woman_tone3:": { - "style": "github", - "image": "1f930-1f3fd.png", - "unicode": "🤰🏽", - "name": "Pregnant Woman - Tone 3" - }, - ":flag_gs:": { - "style": "github", - "image": "1f1ec-1f1f8.png", - "unicode": "🇬🇸", - "name": "South Georgia" - }, - ":currency_exchange:": { - "style": "github", - "image": "1f4b1.png", - "unicode": "💱", - "name": "Currency Exchange" - }, - ":flag_ne:": { - "style": "github", - "image": "1f1f3-1f1ea.png", - "unicode": "🇳🇪", - "name": "Niger" - }, - "🇿🇦": { - "style": "unicode", - "image": "1f1ff-1f1e6.png", - "name": "South Africa" - }, - ":closed-lock-with-key:": { - "style": "github", - "image": "1f510.png", - "unicode": "🔐", - "name": "Closed Lock With Key" - }, - ":martial-arts-uniform:": { - "style": "github", - "image": "1f94b.png", - "unicode": "🥋", - "name": "Martial Arts Uniform" - }, - ":cloud_with_tornado:": { - "style": "github", - "image": "1f32a.png", - "unicode": "🌪", - "name": "Cloud With Tornado" - }, - "💇": { - "style": "unicode", - "image": "1f487.png", - "name": "Haircut" - }, - ":person-with-blond-hair-tone3:": { - "style": "github", - "image": "1f471-1f3fd.png", - "unicode": "👱🏽", - "name": "Person With Blond Hair - Tone 3" - }, - ":wave:": { - "style": "github", - "image": "1f44b.png", - "unicode": "👋", - "name": "Waving Hand Sign" - }, - "↕": { - "style": "unicode", - "image": "2195.png", - "name": "Up Down Arrow" - }, - ":tumbler_glass:": { - "style": "github", - "image": "1f943.png", - "unicode": "🥃", - "name": "Tumbler Glass" - }, - "🐜": { - "style": "unicode", - "image": "1f41c.png", - "name": "Ant" - }, - ":wrestlers_tone5:": { - "style": "github", - "image": "1f93c-1f3ff.png", - "unicode": "🤼🏿", - "name": "Wrestlers - Tone 5" - }, - ":-þ": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":shopping_bags:": { - "style": "github", - "image": "1f6cd.png", - "unicode": "🛍", - "name": "Shopping Bags" - }, - ":regional-indicator-w:": { - "style": "github", - "image": "1f1fc.png", - "unicode": "🇼", - "name": "Regional Indicator Symbol Letter W" - }, - ":mrs-claus-tone1:": { - "style": "github", - "image": "1f936-1f3fb.png", - "unicode": "🤶🏻", - "name": "Mother Christmas - Tone 1" - }, - "👦🏻": { - "style": "unicode", - "image": "1f466-1f3fb.png", - "name": "Boy - Tone 1" - }, - "👦🏼": { - "style": "unicode", - "image": "1f466-1f3fc.png", - "name": "Boy - Tone 2" - }, - "👦🏽": { - "style": "unicode", - "image": "1f466-1f3fd.png", - "name": "Boy - Tone 3" - }, - "👦🏾": { - "style": "unicode", - "image": "1f466-1f3fe.png", - "name": "Boy - Tone 4" - }, - ":white-square-button:": { - "style": "github", - "image": "1f533.png", - "unicode": "🔳", - "name": "White Square Button" - }, - "♐": { - "style": "unicode", - "image": "2650.png", - "name": "Sagittarius" - }, - ":lying-face:": { - "style": "github", - "image": "1f925.png", - "unicode": "🤥", - "name": "Lying Face" - }, - "⏩": { - "style": "unicode", - "image": "23e9.png", - "name": "Black Right-pointing Double Triangle" - }, - ":diamonds:": { - "style": "github", - "image": "2666.png", - "unicode": "♦", - "name": "Black Diamond Suit" - }, - ":left-facing-fist-tone2:": { - "style": "github", - "image": "1f91b-1f3fc.png", - "unicode": "🤛🏼", - "name": "Left Facing Fist - Tone 2" - }, - ":pt:": { - "style": "github", - "image": "1f1f5-1f1f9.png", - "unicode": "🇵🇹", - "name": "Portugal" - }, - ":point_up_tone4:": { - "style": "github", - "image": "261d-1f3fe.png", - "unicode": "☝🏾", - "name": "White Up Pointing Index - Tone 4" - }, - ":point_down_tone5:": { - "style": "github", - "image": "1f447-1f3ff.png", - "unicode": "👇🏿", - "name": "White Down Pointing Backhand Index - Tone 5" - }, - ":juggling-tone5:": { - "style": "github", - "image": "1f939-1f3ff.png", - "unicode": "🤹🏿", - "name": "Juggling - Tone 5" - }, - ":-O": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - "🌉": { - "style": "unicode", - "image": "1f309.png", - "name": "Bridge At Night" - }, - ":-D": { - "style": "ascii", - "ascii": ":D", - "image": "1f603.png", - "unicode": "😃", - "name": "Smiling Face With Open Mouth" - }, - ":-[": { - "style": "ascii", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - ":-X": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":full_moon:": { - "style": "github", - "image": "1f315.png", - "unicode": "🌕", - "name": "Full Moon Symbol" - }, - ":-P": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - "🎞": { - "style": "unicode", - "image": "1f39e.png", - "name": "Film Frames" - }, - ":-o": { - "style": "ascii", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":-b": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":goal:": { - "style": "github", - "image": "1f945.png", - "unicode": "🥅", - "name": "Goal Net" - }, - ":-x": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":sparkling-heart:": { - "style": "github", - "image": "1f496.png", - "unicode": "💖", - "name": "Sparkling Heart" - }, - ":construction_worker_tone3:": { - "style": "github", - "image": "1f477-1f3fd.png", - "unicode": "👷🏽", - "name": "Construction Worker - Tone 3" - }, - ":-p": { - "style": "ascii", - "ascii": ":P", - "image": "1f61b.png", - "unicode": "😛", - "name": "Face With Stuck-out Tongue" - }, - ":ping_pong:": { - "style": "github", - "image": "1f3d3.png", - "unicode": "🏓", - "name": "Table Tennis Paddle And Ball" - }, - ":earth_americas:": { - "style": "github", - "image": "1f30e.png", - "unicode": "🌎", - "name": "Earth Globe Americas" - }, - ":raised_hand_with_part_between_middle_and_ring_fingers_tone5:": { - "style": "github", - "image": "1f596-1f3ff.png", - "unicode": "🖖🏿", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 5" - }, - ":mute:": { - "style": "github", - "image": "1f507.png", - "unicode": "🔇", - "name": "Speaker With Cancellation Stroke" - }, - ":person-frowning-tone1:": { - "style": "github", - "image": "1f64d-1f3fb.png", - "unicode": "🙍🏻", - "name": "Person Frowning - Tone 1" - }, - "🕝": { - "style": "unicode", - "image": "1f55d.png", - "name": "Clock Face Two-thirty" - }, - ":-*": { - "style": "ascii", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":candle:": { - "style": "github", - "image": "1f56f.png", - "unicode": "🕯", - "name": "Candle" - }, - ":-(": { - "style": "ascii", - "ascii": ">:[", - "image": "1f61e.png", - "unicode": "😞", - "name": "Disappointed Face" - }, - ":-)": { - "style": "ascii", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - ":-.": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ":-/": { - "style": "ascii", - "ascii": ">:\\", - "image": "1f615.png", - "unicode": "😕", - "name": "Confused Face" - }, - ":-#": { - "style": "ascii", - "ascii": ":-X", - "image": "1f636.png", - "unicode": "😶", - "name": "Face Without Mouth" - }, - ":regional_indicator_o:": { - "style": "github", - "image": "1f1f4.png", - "unicode": "🇴", - "name": "Regional Indicator Symbol Letter O" - }, - "🖇": { - "style": "unicode", - "image": "1f587.png", - "name": "Linked Paperclips" - }, - ":flag-kg:": { - "style": "github", - "image": "1f1f0-1f1ec.png", - "unicode": "🇰🇬", - "name": "Kyrgyzstan" - }, - ":la:": { - "style": "github", - "image": "1f1f1-1f1e6.png", - "unicode": "🇱🇦", - "name": "Laos" - }, - ":pk:": { - "style": "github", - "image": "1f1f5-1f1f0.png", - "unicode": "🇵🇰", - "name": "Pakistan" - }, - ":black_medium_small_square:": { - "style": "github", - "image": "25fe.png", - "unicode": "◾", - "name": "Black Medium Small Square" - }, - ":diamond_shape_with_a_dot_inside:": { - "style": "github", - "image": "1f4a0.png", - "unicode": "💠", - "name": "Diamond Shape With A Dot Inside" - }, - "8)": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - "🔜": { - "style": "unicode", - "image": "1f51c.png", - "name": "Soon With Rightwards Arrow Above" - }, - ":flag_cn:": { - "style": "github", - "image": "1f1e8-1f1f3.png", - "unicode": "🇨🇳", - "name": "China" - }, - ":wheelchair:": { - "style": "github", - "image": "267f.png", - "unicode": "♿", - "name": "Wheelchair Symbol" - }, - "☦": { - "style": "unicode", - "image": "2626.png", - "name": "Orthodox Cross" - }, - ":urn:": { - "style": "github", - "image": "26b1.png", - "unicode": "⚱", - "name": "Funeral Urn" - }, - "🚱": { - "style": "unicode", - "image": "1f6b1.png", - "name": "Non-potable Water Symbol" - }, - ":v:": { - "style": "github", - "image": "270c.png", - "unicode": "✌", - "name": "Victory Hand" - }, - ":santa-tone1:": { - "style": "github", - "image": "1f385-1f3fb.png", - "unicode": "🎅🏻", - "name": "Father Christmas - Tone 1" - }, - "🙆": { - "style": "unicode", - "ascii": "*\\0/*", - "image": "1f646.png", - "name": "Face With Ok Gesture" - }, - ":lower_left_crayon:": { - "style": "github", - "image": "1f58d.png", - "unicode": "🖍", - "name": "Lower Left Crayon" - }, - ":md:": { - "style": "github", - "image": "1f1f2-1f1e9.png", - "unicode": "🇲🇩", - "name": "Moldova" - }, - ":egg:": { - "style": "github", - "image": "1f95a.png", - "unicode": "🥚", - "name": "Egg" - }, - ":flag_ma:": { - "style": "github", - "image": "1f1f2-1f1e6.png", - "unicode": "🇲🇦", - "name": "Morocco" - }, - ":punch_tone5:": { - "style": "github", - "image": "1f44a-1f3ff.png", - "unicode": "👊🏿", - "name": "Fisted Hand Sign - Tone 5" - }, - ":flag_bd:": { - "style": "github", - "image": "1f1e7-1f1e9.png", - "unicode": "🇧🇩", - "name": "Bangladesh" - }, - "✊🏿": { - "style": "unicode", - "image": "270a-1f3ff.png", - "name": "Raised Fist - Tone 5" - }, - "0:-3": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":in:": { - "style": "github", - "image": "1f1ee-1f1f3.png", - "unicode": "🇮🇳", - "name": "India" - }, - ":open_hands_tone5:": { - "style": "github", - "image": "1f450-1f3ff.png", - "unicode": "👐🏿", - "name": "Open Hands Sign - Tone 5" - }, - "0:-)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":flag-cw:": { - "style": "github", - "image": "1f1e8-1f1fc.png", - "unicode": "🇨🇼", - "name": "Curaçao" - }, - ":metal_tone2:": { - "style": "github", - "image": "1f918-1f3fc.png", - "unicode": "🤘🏼", - "name": "Sign Of The Horns - Tone 2" - }, - ":drum:": { - "style": "github", - "image": "1f941.png", - "unicode": "🥁", - "name": "Drum With Drumsticks" - }, - "🏈": { - "style": "unicode", - "image": "1f3c8.png", - "name": "American Football" - }, - ":poultry_leg:": { - "style": "github", - "image": "1f357.png", - "unicode": "🍗", - "name": "Poultry Leg" - }, - ":slight_frown:": { - "style": "github", - "image": "1f641.png", - "unicode": "🙁", - "name": "Slightly Frowning Face" - }, - "👝": { - "style": "unicode", - "image": "1f45d.png", - "name": "Pouch" - }, - ":mountain:": { - "style": "github", - "image": "26f0.png", - "unicode": "⛰", - "name": "Mountain" - }, - ":notebook_with_decorative_cover:": { - "style": "github", - "image": "1f4d4.png", - "unicode": "📔", - "name": "Notebook With Decorative Cover" - }, - ":information_source:": { - "style": "github", - "image": "2139.png", - "unicode": "ℹ", - "name": "Information Source" - }, - "📲": { - "style": "unicode", - "image": "1f4f2.png", - "name": "Mobile Phone With Rightwards Arrow At Left" - }, - ":flag-au:": { - "style": "github", - "image": "1f1e6-1f1fa.png", - "unicode": "🇦🇺", - "name": "Australia" - }, - ":thumbsdown_tone1:": { - "style": "github", - "image": "1f44e-1f3fb.png", - "unicode": "👎🏻", - "name": "Thumbs Down Sign - Tone 1" - }, - ":flag-jo:": { - "style": "github", - "image": "1f1ef-1f1f4.png", - "unicode": "🇯🇴", - "name": "Jordan" - }, - "◼": { - "style": "unicode", - "image": "25fc.png", - "name": "Black Medium Square" - }, - ":flag_sk:": { - "style": "github", - "image": "1f1f8-1f1f0.png", - "unicode": "🇸🇰", - "name": "Slovakia" - }, - ":er:": { - "style": "github", - "image": "1f1ea-1f1f7.png", - "unicode": "🇪🇷", - "name": "Eritrea" - }, - ":microscope:": { - "style": "github", - "image": "1f52c.png", - "unicode": "🔬", - "name": "Microscope" - }, - "🤳🏻": { - "style": "unicode", - "image": "1f933-1f3fb.png", - "name": "Selfie - Tone 1" - }, - ":flag-bo:": { - "style": "github", - "image": "1f1e7-1f1f4.png", - "unicode": "🇧🇴", - "name": "Bolivia" - }, - "🤳🏿": { - "style": "unicode", - "image": "1f933-1f3ff.png", - "name": "Selfie - Tone 5" - }, - "🤳🏾": { - "style": "unicode", - "image": "1f933-1f3fe.png", - "name": "Selfie - Tone 4" - }, - "🤳🏽": { - "style": "unicode", - "image": "1f933-1f3fd.png", - "name": "Selfie - Tone 3" - }, - "🤳🏼": { - "style": "unicode", - "image": "1f933-1f3fc.png", - "name": "Selfie - Tone 2" - }, - ":reversed_hand_with_middle_finger_extended_tone4:": { - "style": "github", - "image": "1f595-1f3fe.png", - "unicode": "🖕🏾", - "name": "Reversed Hand With Middle Finger Extended - Tone 4" - }, - ":massage_tone2:": { - "style": "github", - "image": "1f486-1f3fc.png", - "unicode": "💆🏼", - "name": "Face Massage - Tone 2" - }, - ":flag-bv:": { - "style": "github", - "image": "1f1e7-1f1fb.png", - "unicode": "🇧🇻", - "name": "Bouvet Island" - }, - ":light_rail:": { - "style": "github", - "image": "1f688.png", - "unicode": "🚈", - "name": "Light Rail" - }, - ":pause-button:": { - "style": "github", - "image": "23f8.png", - "unicode": "⏸", - "name": "Double Vertical Bar" - }, - ":flag-ye:": { - "style": "github", - "image": "1f1fe-1f1ea.png", - "unicode": "🇾🇪", - "name": "Yemen" - }, - ":hammer_and_pick:": { - "style": "github", - "image": "2692.png", - "unicode": "⚒", - "name": "Hammer And Pick" - }, - "🛏": { - "style": "unicode", - "image": "1f6cf.png", - "name": "Bed" - }, - ":family-wwbb:": { - "style": "github", - "image": "1f469-1f469-1f466-1f466.png", - "unicode": "👩👩👦👦", - "name": "Family (woman,woman,boy,boy)" - }, - ":dizzy-face:": { - "style": "github", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":mailbox_with_mail:": { - "style": "github", - "image": "1f4ec.png", - "unicode": "📬", - "name": "Open Mailbox With Raised Flag" - }, - ":flag_kh:": { - "style": "github", - "image": "1f1f0-1f1ed.png", - "unicode": "🇰🇭", - "name": "Cambodia" - }, - ":call_me_tone5:": { - "style": "github", - "image": "1f919-1f3ff.png", - "unicode": "🤙🏿", - "name": "Call Me Hand - Tone 5" - }, - ":bottle_with_popping_cork:": { - "style": "github", - "image": "1f37e.png", - "unicode": "🍾", - "name": "Bottle With Popping Cork" - }, - "🐣": { - "style": "unicode", - "image": "1f423.png", - "name": "Hatching Chick" - }, - ":open-hands-tone2:": { - "style": "github", - "image": "1f450-1f3fc.png", - "unicode": "👐🏼", - "name": "Open Hands Sign - Tone 2" - }, - ":pray:": { - "style": "github", - "image": "1f64f.png", - "unicode": "🙏", - "name": "Person With Folded Hands" - }, - ":flag-nc:": { - "style": "github", - "image": "1f1f3-1f1e8.png", - "unicode": "🇳🇨", - "name": "New Caledonia" - }, - ":wave-tone1:": { - "style": "github", - "image": "1f44b-1f3fb.png", - "unicode": "👋🏻", - "name": "Waving Hand Sign - Tone 1" - }, - "💸": { - "style": "unicode", - "image": "1f4b8.png", - "name": "Money With Wings" - }, - ":punch-tone1:": { - "style": "github", - "image": "1f44a-1f3fb.png", - "unicode": "👊🏻", - "name": "Fisted Hand Sign - Tone 1" - }, - "🇻🇳": { - "style": "unicode", - "image": "1f1fb-1f1f3.png", - "name": "Vietnam" - }, - "🇻🇺": { - "style": "unicode", - "image": "1f1fb-1f1fa.png", - "name": "Vanuatu" - }, - ":rs:": { - "style": "github", - "image": "1f1f7-1f1f8.png", - "unicode": "🇷🇸", - "name": "Serbia" - }, - "🍍": { - "style": "unicode", - "image": "1f34d.png", - "name": "Pineapple" - }, - "🥑": { - "style": "unicode", - "image": "1f951.png", - "name": "Avocado" - }, - ":nose_tone2:": { - "style": "github", - "image": "1f443-1f3fc.png", - "unicode": "👃🏼", - "name": "Nose - Tone 2" - }, - "🇻🇦": { - "style": "unicode", - "image": "1f1fb-1f1e6.png", - "name": "The Vatican City" - }, - ":flag-py:": { - "style": "github", - "image": "1f1f5-1f1fe.png", - "unicode": "🇵🇾", - "name": "Paraguay" - }, - "🇻🇪": { - "style": "unicode", - "image": "1f1fb-1f1ea.png", - "name": "Venezuela" - }, - "🇻🇨": { - "style": "unicode", - "image": "1f1fb-1f1e8.png", - "name": "Saint Vincent And The Grenadines" - }, - "🇻🇮": { - "style": "unicode", - "image": "1f1fb-1f1ee.png", - "name": "U.s. Virgin Islands" - }, - ":closed-umbrella:": { - "style": "github", - "image": "1f302.png", - "unicode": "🌂", - "name": "Closed Umbrella" - }, - "🇻🇬": { - "style": "unicode", - "image": "1f1fb-1f1ec.png", - "name": "British Virgin Islands" - }, - "🏢": { - "style": "unicode", - "image": "1f3e2.png", - "name": "Office Building" - }, - ">:-)": { - "style": "ascii", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ">:-(": { - "style": "ascii", - "ascii": ">:(", - "image": "1f620.png", - "unicode": "😠", - "name": "Angry Face" - }, - ":sleuth_or_spy_tone4:": { - "style": "github", - "image": "1f575-1f3fe.png", - "unicode": "🕵🏾", - "name": "Sleuth Or Spy - Tone 4" - }, - ":see_no_evil:": { - "style": "github", - "image": "1f648.png", - "unicode": "🙈", - "name": "See-no-evil Monkey" - }, - ":sports_medal:": { - "style": "github", - "image": "1f3c5.png", - "unicode": "🏅", - "name": "Sports Medal" - }, - ":-1_tone1:": { - "style": "github", - "image": "1f44e-1f3fb.png", - "unicode": "👎🏻", - "name": "Thumbs Down Sign - Tone 1" - }, - "🌌": { - "style": "unicode", - "image": "1f30c.png", - "name": "Milky Way" - }, - ":paw_prints:": { - "style": "github", - "image": "1f43e.png", - "unicode": "🐾", - "name": "Paw Prints" - }, - ":pudding:": { - "style": "github", - "image": "1f36e.png", - "unicode": "🍮", - "name": "Custard" - }, - "🚥": { - "style": "unicode", - "image": "1f6a5.png", - "name": "Horizontal Traffic Light" - }, - ":ear_tone4:": { - "style": "github", - "image": "1f442-1f3fe.png", - "unicode": "👂🏾", - "name": "Ear - Tone 4" - }, - "😺": { - "style": "unicode", - "image": "1f63a.png", - "name": "Smiling Cat Face With Open Mouth" - }, - "⛅": { - "style": "unicode", - "image": "26c5.png", - "name": "Sun Behind Cloud" - }, - ":new-moon-with-face:": { - "style": "github", - "image": "1f31a.png", - "unicode": "🌚", - "name": "New Moon With Face" - }, - ":clock10:": { - "style": "github", - "image": "1f559.png", - "unicode": "🕙", - "name": "Clock Face Ten Oclock" - }, - ":panda_face:": { - "style": "github", - "image": "1f43c.png", - "unicode": "🐼", - "name": "Panda Face" - }, - ":tulip:": { - "style": "github", - "image": "1f337.png", - "unicode": "🌷", - "name": "Tulip" - }, - ":ok-woman-tone3:": { - "style": "github", - "image": "1f646-1f3fd.png", - "unicode": "🙆🏽", - "name": "Face With Ok Gesture Tone3" - }, - "📹": { - "style": "unicode", - "image": "1f4f9.png", - "name": "Video Camera" - }, - ":lifter-tone5:": { - "style": "github", - "image": "1f3cb-1f3ff.png", - "unicode": "🏋🏿", - "name": "Weight Lifter - Tone 5" - }, - ":family-wwgg:": { - "style": "github", - "image": "1f469-1f469-1f467-1f467.png", - "unicode": "👩👩👧👧", - "name": "Family (woman,woman,girl,girl)" - }, - "💎": { - "style": "unicode", - "image": "1f48e.png", - "name": "Gem Stone" - }, - ":train2:": { - "style": "github", - "image": "1f686.png", - "unicode": "🚆", - "name": "Train" - }, - "👨👩👧👦": { - "style": "unicode", - "image": "1f468-1f469-1f467-1f466.png", - "name": "Family (man,woman,girl,boy)" - }, - "👨👩👧👧": { - "style": "unicode", - "image": "1f468-1f469-1f467-1f467.png", - "name": "Family (man,woman,girl,girl)" - }, - "🔣": { - "style": "unicode", - "image": "1f523.png", - "name": "Input Symbol For Symbols" - }, - ":walking-tone2:": { - "style": "github", - "image": "1f6b6-1f3fc.png", - "unicode": "🚶🏼", - "name": "Pedestrian - Tone 2" - }, - ":baby_bottle:": { - "style": "github", - "image": "1f37c.png", - "unicode": "🍼", - "name": "Baby Bottle" - }, - ":bellhop:": { - "style": "github", - "image": "1f6ce.png", - "unicode": "🛎", - "name": "Bellhop Bell" - }, - "🚊": { - "style": "unicode", - "image": "1f68a.png", - "name": "Tram" - }, - ":flag-us:": { - "style": "github", - "image": "1f1fa-1f1f8.png", - "unicode": "🇺🇸", - "name": "United States" - }, - ":pregnant_woman_tone3:": { - "style": "github", - "image": "1f930-1f3fd.png", - "unicode": "🤰🏽", - "name": "Pregnant Woman - Tone 3" - }, - ":flag-gh:": { - "style": "github", - "image": "1f1ec-1f1ed.png", - "unicode": "🇬🇭", - "name": "Ghana" - }, - ":wink:": { - "style": "github", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ":haircut_tone3:": { - "style": "github", - "image": "1f487-1f3fd.png", - "unicode": "💇🏽", - "name": "Haircut - Tone 3" - }, - ":clap_tone2:": { - "style": "github", - "image": "1f44f-1f3fc.png", - "unicode": "👏🏼", - "name": "Clapping Hands Sign - Tone 2" - }, - "🍷": { - "style": "unicode", - "image": "1f377.png", - "name": "Wine Glass" - }, - ":point-right-tone5:": { - "style": "github", - "image": "1f449-1f3ff.png", - "unicode": "👉🏿", - "name": "White Right Pointing Backhand Index - Tone 5" - }, - ":surfer_tone3:": { - "style": "github", - "image": "1f3c4-1f3fd.png", - "unicode": "🏄🏽", - "name": "Surfer - Tone 3" - }, - ":telescope:": { - "style": "github", - "image": "1f52d.png", - "unicode": "🔭", - "name": "Telescope" - }, - "⬆": { - "style": "unicode", - "image": "2b06.png", - "name": "Upwards Black Arrow" - }, - ":no_good:": { - "style": "github", - "image": "1f645.png", - "unicode": "🙅", - "name": "Face With No Good Gesture" - }, - ":calling:": { - "style": "github", - "image": "1f4f2.png", - "unicode": "📲", - "name": "Mobile Phone With Rightwards Arrow At Left" - }, - ":right-facing-fist-tone4:": { - "style": "github", - "image": "1f91c-1f3fe.png", - "unicode": "🤜🏾", - "name": "Right Facing Fist - Tone 4" - }, - ":hand_with_index_and_middle_fingers_crossed_tone3:": { - "style": "github", - "image": "1f91e-1f3fd.png", - "unicode": "🤞🏽", - "name": "Hand With Index And Middle Fingers Crossed - Tone 3" - }, - ":page_with_curl:": { - "style": "github", - "image": "1f4c3.png", - "unicode": "📃", - "name": "Page With Curl" - }, - ":blue_heart:": { - "style": "github", - "image": "1f499.png", - "unicode": "💙", - "name": "Blue Heart" - }, - "🎡": { - "style": "unicode", - "image": "1f3a1.png", - "name": "Ferris Wheel" - }, - "🖥": { - "style": "unicode", - "image": "1f5a5.png", - "name": "Desktop Computer" - }, - ":mountain_bicyclist_tone2:": { - "style": "github", - "image": "1f6b5-1f3fc.png", - "unicode": "🚵🏼", - "name": "Mountain Bicyclist - Tone 2" - }, - ":za:": { - "style": "github", - "image": "1f1ff-1f1e6.png", - "unicode": "🇿🇦", - "name": "South Africa" - }, - ":man_with_turban:": { - "style": "github", - "image": "1f473.png", - "unicode": "👳", - "name": "Man With Turban" - }, - ":triangular_flag_on_post:": { - "style": "github", - "image": "1f6a9.png", - "unicode": "🚩", - "name": "Triangular Flag On Post" - }, - ":oncoming_taxi:": { - "style": "github", - "image": "1f696.png", - "unicode": "🚖", - "name": "Oncoming Taxi" - }, - ":shaved-ice:": { - "style": "github", - "image": "1f367.png", - "unicode": "🍧", - "name": "Shaved Ice" - }, - "🌶": { - "style": "unicode", - "image": "1f336.png", - "name": "Hot Pepper" - }, - ":pray_tone2:": { - "style": "github", - "image": "1f64f-1f3fc.png", - "unicode": "🙏🏼", - "name": "Person With Folded Hands - Tone 2" - }, - "🔺": { - "style": "unicode", - "image": "1f53a.png", - "name": "Up-pointing Red Triangle" - }, - ":droplet:": { - "style": "github", - "image": "1f4a7.png", - "unicode": "💧", - "name": "Droplet" - }, - ":oncoming_police_car:": { - "style": "github", - "image": "1f694.png", - "unicode": "🚔", - "name": "Oncoming Police Car" - }, - "📏": { - "style": "unicode", - "image": "1f4cf.png", - "name": "Straight Ruler" - }, - ":full-moon-with-face:": { - "style": "github", - "image": "1f31d.png", - "unicode": "🌝", - "name": "Full Moon With Face" - }, - ":flag_ng:": { - "style": "github", - "image": "1f1f3-1f1ec.png", - "unicode": "🇳🇬", - "name": "Nigeria" - }, - ":star-and-crescent:": { - "style": "github", - "image": "262a.png", - "unicode": "☪", - "name": "Star And Crescent" - }, - ":back_of_hand:": { - "style": "github", - "image": "1f91a.png", - "unicode": "🤚", - "name": "Raised Back Of Hand" - }, - ":flag_gq:": { - "style": "github", - "image": "1f1ec-1f1f6.png", - "unicode": "🇬🇶", - "name": "Equatorial Guinea" - }, - "👤": { - "style": "unicode", - "image": "1f464.png", - "name": "Bust In Silhouette" - }, - ":ae:": { - "style": "github", - "image": "1f1e6-1f1ea.png", - "unicode": "🇦🇪", - "name": "The United Arab Emirates" - }, - ":white_small_square:": { - "style": "github", - "image": "25ab.png", - "unicode": "▫", - "name": "White Small Square" - }, - ":page-with-curl:": { - "style": "github", - "image": "1f4c3.png", - "unicode": "📃", - "name": "Page With Curl" - }, - ":flag-sm:": { - "style": "github", - "image": "1f1f8-1f1f2.png", - "unicode": "🇸🇲", - "name": "San Marino" - }, - ":lower_left_ballpoint_pen:": { - "style": "github", - "image": "1f58a.png", - "unicode": "🖊", - "name": "Lower Left Ballpoint Pen" - }, - "😣": { - "style": "unicode", - "ascii": ">.<", - "image": "1f623.png", - "name": "Persevering Face" - }, - ":black-small-square:": { - "style": "github", - "image": "25aa.png", - "unicode": "▪", - "name": "Black Small Square" - }, - ":expecting_woman_tone5:": { - "style": "github", - "image": "1f930-1f3ff.png", - "unicode": "🤰🏿", - "name": "Pregnant Woman - Tone 5" - }, - "👰🏾": { - "style": "unicode", - "image": "1f470-1f3fe.png", - "name": "Bride With Veil - Tone 4" - }, - "👰🏿": { - "style": "unicode", - "image": "1f470-1f3ff.png", - "name": "Bride With Veil - Tone 5" - }, - "👰🏼": { - "style": "unicode", - "image": "1f470-1f3fc.png", - "name": "Bride With Veil - Tone 2" - }, - "👰🏽": { - "style": "unicode", - "image": "1f470-1f3fd.png", - "name": "Bride With Veil - Tone 3" - }, - ":arrow_heading_down:": { - "style": "github", - "image": "2935.png", - "unicode": "⤵", - "name": "Arrow Pointing Rightwards Then Curving Downwards" - }, - ":spy-tone4:": { - "style": "github", - "image": "1f575-1f3fe.png", - "unicode": "🕵🏾", - "name": "Sleuth Or Spy - Tone 4" - }, - "🚸": { - "style": "unicode", - "image": "1f6b8.png", - "name": "Children Crossing" - }, - ":cricket:": { - "style": "github", - "image": "1f3cf.png", - "unicode": "🏏", - "name": "Cricket Bat And Ball" - }, - ":wrestlers-tone2:": { - "style": "github", - "image": "1f93c-1f3fc.png", - "unicode": "🤼🏼", - "name": "Wrestlers - Tone 2" - }, - ":older_man:": { - "style": "github", - "image": "1f474.png", - "unicode": "👴", - "name": "Older Man" - }, - ":hk:": { - "style": "github", - "image": "1f1ed-1f1f0.png", - "unicode": "🇭🇰", - "name": "Hong Kong" - }, - ":regional-indicator-u:": { - "style": "github", - "image": "1f1fa.png", - "unicode": "🇺", - "name": "Regional Indicator Symbol Letter U" - }, - ":mrs-claus-tone3:": { - "style": "github", - "image": "1f936-1f3fd.png", - "unicode": "🤶🏽", - "name": "Mother Christmas - Tone 3" - }, - ":left-facing-fist-tone4:": { - "style": "github", - "image": "1f91b-1f3fe.png", - "unicode": "🤛🏾", - "name": "Left Facing Fist - Tone 4" - }, - ":couplekiss:": { - "style": "github", - "image": "1f48f.png", - "unicode": "💏", - "name": "Kiss" - }, - ":point_down_tone3:": { - "style": "github", - "image": "1f447-1f3fd.png", - "unicode": "👇🏽", - "name": "White Down Pointing Backhand Index - Tone 3" - }, - ":kissing-heart:": { - "style": "github", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":information-desk-person-tone2:": { - "style": "github", - "image": "1f481-1f3fc.png", - "unicode": "💁🏼", - "name": "Information Desk Person - Tone 2" - }, - "↗": { - "style": "unicode", - "image": "2197.png", - "name": "North East Arrow" - }, - "💥": { - "style": "unicode", - "image": "1f4a5.png", - "name": "Collision Symbol" - }, - ":middle-finger-tone2:": { - "style": "github", - "image": "1f595-1f3fc.png", - "unicode": "🖕🏼", - "name": "Reversed Hand With Middle Finger Extended - Tone 2" - }, - ":ice_skate:": { - "style": "github", - "image": "26f8.png", - "unicode": "⛸", - "name": "Ice Skate" - }, - ":cruise-ship:": { - "style": "github", - "image": "1f6f3.png", - "unicode": "🛳", - "name": "Passenger Ship" - }, - "🈶": { - "style": "unicode", - "image": "1f236.png", - "name": "Squared Cjk Unified Ideograph-6709" - }, - "🐺": { - "style": "unicode", - "image": "1f43a.png", - "name": "Wolf Face" - }, - ":construction_worker_tone1:": { - "style": "github", - "image": "1f477-1f3fb.png", - "unicode": "👷🏻", - "name": "Construction Worker - Tone 1" - }, - ":person_doing_cartwheel_tone3:": { - "style": "github", - "image": "1f938-1f3fd.png", - "unicode": "🤸🏽", - "name": "Person Doing Cartwheel - Tone 3" - }, - "🏋": { - "style": "unicode", - "image": "1f3cb.png", - "name": "Weight Lifter" - }, - ":person-frowning-tone3:": { - "style": "github", - "image": "1f64d-1f3fd.png", - "unicode": "🙍🏽", - "name": "Person Frowning - Tone 3" - }, - "🏤": { - "style": "unicode", - "image": "1f3e4.png", - "name": "European Post Office" - }, - "🍠": { - "style": "unicode", - "image": "1f360.png", - "name": "Roasted Sweet Potato" - }, - ":slightly_smiling_face:": { - "style": "github", - "ascii": ":)", - "image": "1f642.png", - "unicode": "🙂", - "name": "Slightly Smiling Face" - }, - "🕤": { - "style": "unicode", - "image": "1f564.png", - "name": "Clock Face Nine-thirty" - }, - "⏫": { - "style": "unicode", - "image": "23eb.png", - "name": "Black Up-pointing Double Triangle" - }, - ":ok-woman-tone1:": { - "style": "github", - "image": "1f646-1f3fb.png", - "unicode": "🙆🏻", - "name": "Face With Ok Gesture Tone1" - }, - "🙅🏽": { - "style": "unicode", - "image": "1f645-1f3fd.png", - "name": "Face With No Good Gesture - Tone 3" - }, - "🙅🏼": { - "style": "unicode", - "image": "1f645-1f3fc.png", - "name": "Face With No Good Gesture - Tone 2" - }, - "🙅🏿": { - "style": "unicode", - "image": "1f645-1f3ff.png", - "name": "Face With No Good Gesture - Tone 5" - }, - "🙅🏾": { - "style": "unicode", - "image": "1f645-1f3fe.png", - "name": "Face With No Good Gesture - Tone 4" - }, - "🙅🏻": { - "style": "unicode", - "image": "1f645-1f3fb.png", - "name": "Face With No Good Gesture - Tone 1" - }, - ":flag_cl:": { - "style": "github", - "image": "1f1e8-1f1f1.png", - "unicode": "🇨🇱", - "name": "Chile" - }, - "🚎": { - "style": "unicode", - "image": "1f68e.png", - "name": "Trolleybus" - }, - ":nut_and_bolt:": { - "style": "github", - "image": "1f529.png", - "unicode": "🔩", - "name": "Nut And Bolt" - }, - ":tn:": { - "style": "github", - "image": "1f1f9-1f1f3.png", - "unicode": "🇹🇳", - "name": "Tunisia" - }, - ":globe-with-meridians:": { - "style": "github", - "image": "1f310.png", - "unicode": "🌐", - "name": "Globe With Meridians" - }, - "🤧": { - "style": "unicode", - "image": "1f927.png", - "name": "Sneezing Face" - }, - ":water-polo-tone5:": { - "style": "github", - "image": "1f93d-1f3ff.png", - "unicode": "🤽🏿", - "name": "Water Polo - Tone 5" - }, - ":point-left-tone4:": { - "style": "github", - "image": "1f448-1f3fe.png", - "unicode": "👈🏾", - "name": "White Left Pointing Backhand Index - Tone 4" - }, - ":low_brightness:": { - "style": "github", - "image": "1f505.png", - "unicode": "🔅", - "name": "Low Brightness Symbol" - }, - ":no_entry:": { - "style": "github", - "image": "26d4.png", - "unicode": "⛔", - "name": "No Entry" - }, - ":man-with-turban-tone5:": { - "style": "github", - "image": "1f473-1f3ff.png", - "unicode": "👳🏿", - "name": "Man With Turban - Tone 5" - }, - ":flan:": { - "style": "github", - "image": "1f36e.png", - "unicode": "🍮", - "name": "Custard" - }, - ":horse_racing_tone5:": { - "style": "github", - "image": "1f3c7-1f3ff.png", - "unicode": "🏇🏿", - "name": "Horse Racing - Tone 5" - }, - ":u5408:": { - "style": "github", - "image": "1f234.png", - "unicode": "🈴", - "name": "Squared Cjk Unified Ideograph-5408" - }, - ":island:": { - "style": "github", - "image": "1f3dd.png", - "unicode": "🏝", - "name": "Desert Island" - }, - ":mans-shoe:": { - "style": "github", - "image": "1f45e.png", - "unicode": "👞", - "name": "Mans Shoe" - }, - ":flag-sr:": { - "style": "github", - "image": "1f1f8-1f1f7.png", - "unicode": "🇸🇷", - "name": "Suriname" - }, - ":flag_mc:": { - "style": "github", - "image": "1f1f2-1f1e8.png", - "unicode": "🇲🇨", - "name": "Monaco" - }, - ":raising_hand_tone1:": { - "style": "github", - "image": "1f64b-1f3fb.png", - "unicode": "🙋🏻", - "name": "Happy Person Raising One Hand Tone1" - }, - "✂": { - "style": "unicode", - "image": "2702.png", - "name": "Black Scissors" - }, - ":running_shirt_with_sash:": { - "style": "github", - "image": "1f3bd.png", - "unicode": "🎽", - "name": "Running Shirt With Sash" - }, - "😌": { - "style": "unicode", - "image": "1f60c.png", - "name": "Relieved Face" - }, - "⚗": { - "style": "unicode", - "image": "2697.png", - "name": "Alembic" - }, - ":one:": { - "style": "github", - "image": "0031-20e3.png", - "unicode": "1⃣", - "name": "Keycap Digit One" - }, - "🎥": { - "style": "unicode", - "image": "1f3a5.png", - "name": "Movie Camera" - }, - "🔶": { - "style": "unicode", - "image": "1f536.png", - "name": "Large Orange Diamond" - }, - "🌺": { - "style": "unicode", - "image": "1f33a.png", - "name": "Hibiscus" - }, - "🤾": { - "style": "unicode", - "image": "1f93e.png", - "name": "Handball" - }, - ":waxing_gibbous_moon:": { - "style": "github", - "image": "1f314.png", - "unicode": "🌔", - "name": "Waxing Gibbous Moon Symbol" - }, - ":gr:": { - "style": "github", - "image": "1f1ec-1f1f7.png", - "unicode": "🇬🇷", - "name": "Greece" - }, - ":hand-splayed-tone3:": { - "style": "github", - "image": "1f590-1f3fd.png", - "unicode": "🖐🏽", - "name": "Raised Hand With Fingers Splayed - Tone 3" - }, - "📋": { - "style": "unicode", - "image": "1f4cb.png", - "name": "Clipboard" - }, - ":thumbsdown_tone3:": { - "style": "github", - "image": "1f44e-1f3fd.png", - "unicode": "👎🏽", - "name": "Thumbs Down Sign - Tone 3" - }, - "🤦🏿": { - "style": "unicode", - "image": "1f926-1f3ff.png", - "name": "Face Palm - Tone 5" - }, - "👠": { - "style": "unicode", - "image": "1f460.png", - "name": "High-heeled Shoe" - }, - ":wilted_rose:": { - "style": "github", - "image": "1f940.png", - "unicode": "🥀", - "name": "Wilted Flower" - }, - "0⃣": { - "style": "unicode", - "image": "0030-20e3.png", - "name": "Keycap Digit Zero" - }, - ":arrow-forward:": { - "style": "github", - "image": "25b6.png", - "unicode": "▶", - "name": "Black Right-pointing Triangle" - }, - ":latin_cross:": { - "style": "github", - "image": "271d.png", - "unicode": "✝", - "name": "Latin Cross" - }, - ":house-abandoned:": { - "style": "github", - "image": "1f3da.png", - "unicode": "🏚", - "name": "Derelict House Building" - }, - ":file-cabinet:": { - "style": "github", - "image": "1f5c4.png", - "unicode": "🗄", - "name": "File Cabinet" - }, - ":flag-jm:": { - "style": "github", - "image": "1f1ef-1f1f2.png", - "unicode": "🇯🇲", - "name": "Jamaica" - }, - ":older_woman_tone5:": { - "style": "github", - "image": "1f475-1f3ff.png", - "unicode": "👵🏿", - "name": "Older Woman - Tone 5" - }, - ":et:": { - "style": "github", - "image": "1f1ea-1f1f9.png", - "unicode": "🇪🇹", - "name": "Ethiopia" - }, - ":cupid:": { - "style": "github", - "image": "1f498.png", - "unicode": "💘", - "name": "Heart With Arrow" - }, - ":flag_si:": { - "style": "github", - "image": "1f1f8-1f1ee.png", - "unicode": "🇸🇮", - "name": "Slovenia" - }, - ":horse-racing-tone3:": { - "style": "github", - "image": "1f3c7-1f3fd.png", - "unicode": "🏇🏽", - "name": "Horse Racing - Tone 3" - }, - "🆎": { - "style": "unicode", - "image": "1f18e.png", - "name": "Negative Squared Ab" - }, - ":uy:": { - "style": "github", - "image": "1f1fa-1f1fe.png", - "unicode": "🇺🇾", - "name": "Uruguay" - }, - ":v-tone3:": { - "style": "github", - "image": "270c-1f3fd.png", - "unicode": "✌🏽", - "name": "Victory Hand - Tone 3" - }, - "🛄": { - "style": "unicode", - "image": "1f6c4.png", - "name": "Baggage Claim" - }, - ":wedding:": { - "style": "github", - "image": "1f492.png", - "unicode": "💒", - "name": "Wedding" - }, - ":person_frowning:": { - "style": "github", - "image": "1f64d.png", - "unicode": "🙍", - "name": "Person Frowning" - }, - ":beginner:": { - "style": "github", - "image": "1f530.png", - "unicode": "🔰", - "name": "Japanese Symbol For Beginner" - }, - ":family_mmb:": { - "style": "github", - "image": "1f468-1f468-1f466.png", - "unicode": "👨👨👦", - "name": "Family (man,man,boy)" - }, - ":fist_tone2:": { - "style": "github", - "image": "270a-1f3fc.png", - "unicode": "✊🏼", - "name": "Raised Fist - Tone 2" - }, - ":grin:": { - "style": "github", - "image": "1f601.png", - "unicode": "😁", - "name": "Grinning Face With Smiling Eyes" - }, - ":mouse-three-button:": { - "style": "github", - "image": "1f5b1.png", - "unicode": "🖱", - "name": "Three Button Mouse" - }, - ":flag-om:": { - "style": "github", - "image": "1f1f4-1f1f2.png", - "unicode": "🇴🇲", - "name": "Oman" - }, - ":man-dancing-tone5:": { - "style": "github", - "image": "1f57a-1f3ff.png", - "unicode": "🕺🏿", - "name": "Man Dancing - Tone 5" - }, - ":right_fist_tone4:": { - "style": "github", - "image": "1f91c-1f3fe.png", - "unicode": "🤜🏾", - "name": "Right Facing Fist - Tone 4" - }, - ":bow-tone1:": { - "style": "github", - "image": "1f647-1f3fb.png", - "unicode": "🙇🏻", - "name": "Person Bowing Deeply - Tone 1" - }, - ":juggler_tone5:": { - "style": "github", - "image": "1f939-1f3ff.png", - "unicode": "🤹🏿", - "name": "Juggling - Tone 5" - }, - ":flag_kn:": { - "style": "github", - "image": "1f1f0-1f1f3.png", - "unicode": "🇰🇳", - "name": "Saint Kitts And Nevis" - }, - ":white-flower:": { - "style": "github", - "image": "1f4ae.png", - "unicode": "💮", - "name": "White Flower" - }, - ":rowboat-tone4:": { - "style": "github", - "image": "1f6a3-1f3fe.png", - "unicode": "🚣🏾", - "name": "Rowboat - Tone 4" - }, - "☂": { - "style": "unicode", - "image": "2602.png", - "name": "Umbrella" - }, - ":selfie_tone5:": { - "style": "github", - "image": "1f933-1f3ff.png", - "unicode": "🤳🏿", - "name": "Selfie - Tone 5" - }, - ":popcorn:": { - "style": "github", - "image": "1f37f.png", - "unicode": "🍿", - "name": "Popcorn" - }, - ":building_construction:": { - "style": "github", - "image": "1f3d7.png", - "unicode": "🏗", - "name": "Building Construction" - }, - ":arrow_right_hook:": { - "style": "github", - "image": "21aa.png", - "unicode": "↪", - "name": "Rightwards Arrow With Hook" - }, - "➗": { - "style": "unicode", - "image": "2797.png", - "name": "Heavy Division Sign" - }, - ":u55b6:": { - "style": "github", - "image": "1f23a.png", - "unicode": "🈺", - "name": "Squared Cjk Unified Ideograph-55b6" - }, - "💡": { - "style": "unicode", - "image": "1f4a1.png", - "name": "Electric Light Bulb" - }, - ":flag-na:": { - "style": "github", - "image": "1f1f3-1f1e6.png", - "unicode": "🇳🇦", - "name": "Namibia" - }, - ":mountain-snow:": { - "style": "github", - "image": "1f3d4.png", - "unicode": "🏔", - "name": "Snow Capped Mountain" - }, - "🐶": { - "style": "unicode", - "image": "1f436.png", - "name": "Dog Face" - }, - ":punch-tone3:": { - "style": "github", - "image": "1f44a-1f3fd.png", - "unicode": "👊🏽", - "name": "Fisted Hand Sign - Tone 3" - }, - "🈺": { - "style": "unicode", - "image": "1f23a.png", - "name": "Squared Cjk Unified Ideograph-55b6" - }, - ":open-hands-tone4:": { - "style": "github", - "image": "1f450-1f3fe.png", - "unicode": "👐🏾", - "name": "Open Hands Sign - Tone 4" - }, - ":cf:": { - "style": "github", - "image": "1f1e8-1f1eb.png", - "unicode": "🇨🇫", - "name": "Central African Republic" - }, - ":guardsman-tone4:": { - "style": "github", - "image": "1f482-1f3fe.png", - "unicode": "💂🏾", - "name": "Guardsman - Tone 4" - }, - ":thermometer-face:": { - "style": "github", - "image": "1f912.png", - "unicode": "🤒", - "name": "Face With Thermometer" - }, - "🏏": { - "style": "unicode", - "image": "1f3cf.png", - "name": "Cricket Bat And Ball" - }, - ":tropical-fish:": { - "style": "github", - "image": "1f420.png", - "unicode": "🐠", - "name": "Tropical Fish" - }, - ":cop-tone5:": { - "style": "github", - "image": "1f46e-1f3ff.png", - "unicode": "👮🏿", - "name": "Police Officer - Tone 5" - }, - "🕠": { - "style": "unicode", - "image": "1f560.png", - "name": "Clock Face Five-thirty" - }, - "🍤": { - "style": "unicode", - "image": "1f364.png", - "name": "Fried Shrimp" - }, - ":persevere:": { - "style": "github", - "ascii": ">.<", - "image": "1f623.png", - "unicode": "😣", - "name": "Persevering Face" - }, - "⏯": { - "style": "unicode", - "image": "23ef.png", - "name": "Black Right-pointing Double Triangle With Double Vertical Bar" - }, - ":flag_re:": { - "style": "github", - "image": "1f1f7-1f1ea.png", - "unicode": "🇷🇪", - "name": "Réunion" - }, - ":no_good_tone2:": { - "style": "github", - "image": "1f645-1f3fc.png", - "unicode": "🙅🏼", - "name": "Face With No Good Gesture - Tone 2" - }, - ":-1_tone3:": { - "style": "github", - "image": "1f44e-1f3fd.png", - "unicode": "👎🏽", - "name": "Thumbs Down Sign - Tone 3" - }, - ":diamond-shape-with-a-dot-inside:": { - "style": "github", - "image": "1f4a0.png", - "unicode": "💠", - "name": "Diamond Shape With A Dot Inside" - }, - ":boxing_glove:": { - "style": "github", - "image": "1f94a.png", - "unicode": "🥊", - "name": "Boxing Glove" - }, - ":sailboat:": { - "style": "github", - "image": "26f5.png", - "unicode": "⛵", - "name": "Sailboat" - }, - ":metal-tone4:": { - "style": "github", - "image": "1f918-1f3fe.png", - "unicode": "🤘🏾", - "name": "Sign Of The Horns - Tone 4" - }, - ":flag-id:": { - "style": "github", - "image": "1f1ee-1f1e9.png", - "unicode": "🇮🇩", - "name": "Indonesia" - }, - ":thinking_face:": { - "style": "github", - "image": "1f914.png", - "unicode": "🤔", - "name": "Thinking Face" - }, - ":swimmer_tone1:": { - "style": "github", - "image": "1f3ca-1f3fb.png", - "unicode": "🏊🏻", - "name": "Swimmer - Tone 1" - }, - ":ear_tone2:": { - "style": "github", - "image": "1f442-1f3fc.png", - "unicode": "👂🏼", - "name": "Ear - Tone 2" - }, - ":penguin:": { - "style": "github", - "image": "1f427.png", - "unicode": "🐧", - "name": "Penguin" - }, - ":thumbsup:": { - "style": "github", - "image": "1f44d.png", - "unicode": "👍", - "name": "Thumbs Up Sign" - }, - "🙍": { - "style": "unicode", - "image": "1f64d.png", - "name": "Person Frowning" - }, - ":cat:": { - "style": "github", - "image": "1f431.png", - "unicode": "🐱", - "name": "Cat Face" - }, - "🛢": { - "style": "unicode", - "image": "1f6e2.png", - "name": "Oil Drum" - }, - ":flag_tw:": { - "style": "github", - "image": "1f1f9-1f1fc.png", - "unicode": "🇹🇼", - "name": "The Republic Of China" - }, - ":flag-black:": { - "style": "github", - "image": "1f3f4.png", - "unicode": "🏴", - "name": "Waving Black Flag" - }, - ":vulcan-tone4:": { - "style": "github", - "image": "1f596-1f3fe.png", - "unicode": "🖖🏾", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 4" - }, - "👩❤👩": { - "style": "unicode", - "image": "1f469-2764-1f469.png", - "name": "Couple (woman,woman)" - }, - ":vulcan_tone2:": { - "style": "github", - "image": "1f596-1f3fc.png", - "unicode": "🖖🏼", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 2" - }, - ":lifter-tone3:": { - "style": "github", - "image": "1f3cb-1f3fd.png", - "unicode": "🏋🏽", - "name": "Weight Lifter - Tone 3" - }, - "🐌": { - "style": "unicode", - "image": "1f40c.png", - "name": "Snail" - }, - "O=)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":raising_hand_tone2:": { - "style": "github", - "image": "1f64b-1f3fc.png", - "unicode": "🙋🏼", - "name": "Happy Person Raising One Hand Tone2" - }, - ":sy:": { - "style": "github", - "image": "1f1f8-1f1fe.png", - "unicode": "🇸🇾", - "name": "Syria" - }, - ":selfie-tone1:": { - "style": "github", - "image": "1f933-1f3fb.png", - "unicode": "🤳🏻", - "name": "Selfie - Tone 1" - }, - "🛋": { - "style": "unicode", - "image": "1f6cb.png", - "name": "Couch And Lamp" - }, - "🃏": { - "style": "unicode", - "image": "1f0cf.png", - "name": "Playing Card Black Joker" - }, - ":clap_tone4:": { - "style": "github", - "image": "1f44f-1f3fe.png", - "unicode": "👏🏾", - "name": "Clapping Hands Sign - Tone 4" - }, - ":surfer-tone1:": { - "style": "github", - "image": "1f3c4-1f3fb.png", - "unicode": "🏄🏻", - "name": "Surfer - Tone 1" - }, - ":surfer_tone1:": { - "style": "github", - "image": "1f3c4-1f3fb.png", - "unicode": "🏄🏻", - "name": "Surfer - Tone 1" - }, - ":pager:": { - "style": "github", - "image": "1f4df.png", - "unicode": "📟", - "name": "Pager" - }, - "👁🗨": { - "style": "unicode", - "image": "1f441-1f5e8.png", - "name": "Eye In Speech Bubble" - }, - ":haircut_tone1:": { - "style": "github", - "image": "1f487-1f3fb.png", - "unicode": "💇🏻", - "name": "Haircut - Tone 1" - }, - ":fountain:": { - "style": "github", - "image": "26f2.png", - "unicode": "⛲", - "name": "Fountain" - }, - "🏹": { - "style": "unicode", - "image": "1f3f9.png", - "name": "Bow And Arrow" - }, - ":thought-balloon:": { - "style": "github", - "image": "1f4ad.png", - "unicode": "💭", - "name": "Thought Balloon" - }, - ":information-source:": { - "style": "github", - "image": "2139.png", - "unicode": "ℹ", - "name": "Information Source" - }, - ":point-up-2-tone1:": { - "style": "github", - "image": "1f446-1f3fb.png", - "unicode": "👆🏻", - "name": "White Up Pointing Backhand Index - Tone 1" - }, - "🎎": { - "style": "unicode", - "image": "1f38e.png", - "name": "Japanese Dolls" - }, - ":regional_indicator_e:": { - "style": "github", - "image": "1f1ea.png", - "unicode": "🇪", - "name": "Regional Indicator Symbol Letter E" - }, - ":play_pause:": { - "style": "github", - "image": "23ef.png", - "unicode": "⏯", - "name": "Black Right-pointing Double Triangle With Double Vertical Bar" - }, - ":cy:": { - "style": "github", - "image": "1f1e8-1f1fe.png", - "unicode": "🇨🇾", - "name": "Cyprus" - }, - ":lower_left_fountain_pen:": { - "style": "github", - "image": "1f58b.png", - "unicode": "🖋", - "name": "Lower Left Fountain Pen" - }, - ":hand_with_index_and_middle_fingers_crossed_tone5:": { - "style": "github", - "image": "1f91e-1f3ff.png", - "unicode": "🤞🏿", - "name": "Hand With Index And Middle Fingers Crossed - Tone 5" - }, - ":pregnant_woman_tone1:": { - "style": "github", - "image": "1f930-1f3fb.png", - "unicode": "🤰🏻", - "name": "Pregnant Woman - Tone 1" - }, - ":shrug-tone5:": { - "style": "github", - "image": "1f937-1f3ff.png", - "unicode": "🤷🏿", - "name": "Shrug - Tone 5" - }, - ":point_up:": { - "style": "github", - "image": "261d.png", - "unicode": "☝", - "name": "White Up Pointing Index" - }, - "📷": { - "style": "unicode", - "image": "1f4f7.png", - "name": "Camera" - }, - ":flag-lb:": { - "style": "github", - "image": "1f1f1-1f1e7.png", - "unicode": "🇱🇧", - "name": "Lebanon" - }, - "🕍": { - "style": "unicode", - "image": "1f54d.png", - "name": "Synagogue" - }, - ":sound:": { - "style": "github", - "image": "1f509.png", - "unicode": "🔉", - "name": "Speaker With One Sound Wave" - }, - "🥕": { - "style": "unicode", - "image": "1f955.png", - "name": "Carrot" - }, - ":ky:": { - "style": "github", - "image": "1f1f0-1f1fe.png", - "unicode": "🇰🇾", - "name": "Cayman Islands" - }, - ":ag:": { - "style": "github", - "image": "1f1e6-1f1ec.png", - "unicode": "🇦🇬", - "name": "Antigua And Barbuda" - }, - "👷": { - "style": "unicode", - "image": "1f477.png", - "name": "Construction Worker" - }, - ":flag-so:": { - "style": "github", - "image": "1f1f8-1f1f4.png", - "unicode": "🇸🇴", - "name": "Somalia" - }, - ":wrestling_tone5:": { - "style": "github", - "image": "1f93c-1f3ff.png", - "unicode": "🤼🏿", - "name": "Wrestlers - Tone 5" - }, - "🔌": { - "style": "unicode", - "image": "1f50c.png", - "name": "Electric Plug" - }, - "🤔": { - "style": "unicode", - "image": "1f914.png", - "name": "Thinking Face" - }, - ":hand-splayed-tone2:": { - "style": "github", - "image": "1f590-1f3fc.png", - "unicode": "🖐🏼", - "name": "Raised Hand With Fingers Splayed - Tone 2" - }, - "🚡": { - "style": "unicode", - "image": "1f6a1.png", - "name": "Aerial Tramway" - }, - ":strawberry:": { - "style": "github", - "image": "1f353.png", - "unicode": "🍓", - "name": "Strawberry" - }, - "🤚🏼": { - "style": "unicode", - "image": "1f91a-1f3fc.png", - "name": "Raised Back Of Hand - Tone 2" - }, - "🤚🏽": { - "style": "unicode", - "image": "1f91a-1f3fd.png", - "name": "Raised Back Of Hand - Tone 3" - }, - "🤚🏾": { - "style": "unicode", - "image": "1f91a-1f3fe.png", - "name": "Raised Back Of Hand - Tone 4" - }, - "🤚🏿": { - "style": "unicode", - "image": "1f91a-1f3ff.png", - "name": "Raised Back Of Hand - Tone 5" - }, - "⤴": { - "style": "unicode", - "image": "2934.png", - "name": "Arrow Pointing Rightwards Then Curving Upwards" - }, - "🤚🏻": { - "style": "unicode", - "image": "1f91a-1f3fb.png", - "name": "Raised Back Of Hand - Tone 1" - }, - ":grapes:": { - "style": "github", - "image": "1f347.png", - "unicode": "🍇", - "name": "Grapes" - }, - ":spy-tone2:": { - "style": "github", - "image": "1f575-1f3fc.png", - "unicode": "🕵🏼", - "name": "Sleuth Or Spy - Tone 2" - }, - ":arrow_upper_left:": { - "style": "github", - "image": "2196.png", - "unicode": "↖", - "name": "North West Arrow" - }, - ":tiger2:": { - "style": "github", - "image": "1f405.png", - "unicode": "🐅", - "name": "Tiger" - }, - ":information_desk_person_tone2:": { - "style": "github", - "image": "1f481-1f3fc.png", - "unicode": "💁🏼", - "name": "Information Desk Person - Tone 2" - }, - ":ph:": { - "style": "github", - "image": "1f1f5-1f1ed.png", - "unicode": "🇵🇭", - "name": "The Philippines" - }, - ":linked_paperclips:": { - "style": "github", - "image": "1f587.png", - "unicode": "🖇", - "name": "Linked Paperclips" - }, - ":point_down_tone1:": { - "style": "github", - "image": "1f447-1f3fb.png", - "unicode": "👇🏻", - "name": "White Down Pointing Backhand Index - Tone 1" - }, - ":paperclip:": { - "style": "github", - "image": "1f4ce.png", - "unicode": "📎", - "name": "Paperclip" - }, - ":om-symbol:": { - "style": "github", - "image": "1f549.png", - "unicode": "🕉", - "name": "Om Symbol" - }, - ":juggling-tone1:": { - "style": "github", - "image": "1f939-1f3fb.png", - "unicode": "🤹🏻", - "name": "Juggling - Tone 1" - }, - ":snowman:": { - "style": "github", - "image": "26c4.png", - "unicode": "⛄", - "name": "Snowman Without Snow" - }, - "🇮🇩": { - "style": "unicode", - "image": "1f1ee-1f1e9.png", - "name": "Indonesia" - }, - "🇮🇪": { - "style": "unicode", - "image": "1f1ee-1f1ea.png", - "name": "Ireland" - }, - "👴🏽": { - "style": "unicode", - "image": "1f474-1f3fd.png", - "name": "Older Man - Tone 3" - }, - "👴🏻": { - "style": "unicode", - "image": "1f474-1f3fb.png", - "name": "Older Man - Tone 1" - }, - ":umbrella2:": { - "style": "github", - "image": "2602.png", - "unicode": "☂", - "name": "Umbrella" - }, - "🇮🇸": { - "style": "unicode", - "image": "1f1ee-1f1f8.png", - "name": "Iceland" - }, - "🇮🇹": { - "style": "unicode", - "image": "1f1ee-1f1f9.png", - "name": "Italy" - }, - "🇮🇱": { - "style": "unicode", - "image": "1f1ee-1f1f1.png", - "name": "Israel" - }, - "🇮🇲": { - "style": "unicode", - "image": "1f1ee-1f1f2.png", - "name": "Isle Of Man" - }, - "🇮🇳": { - "style": "unicode", - "image": "1f1ee-1f1f3.png", - "name": "India" - }, - "🇮🇴": { - "style": "unicode", - "image": "1f1ee-1f1f4.png", - "name": "British Indian Ocean Territory" - }, - "🇮🇶": { - "style": "unicode", - "image": "1f1ee-1f1f6.png", - "name": "Iraq" - }, - "🇮🇷": { - "style": "unicode", - "image": "1f1ee-1f1f7.png", - "name": "Iran" - }, - ":flag_pf:": { - "style": "github", - "image": "1f1f5-1f1eb.png", - "unicode": "🇵🇫", - "name": "French Polynesia" - }, - ":flag_ax:": { - "style": "github", - "image": "1f1e6-1f1fd.png", - "unicode": "🇦🇽", - "name": "Åland Islands" - }, - "👍": { - "style": "unicode", - "image": "1f44d.png", - "name": "Thumbs Up Sign" - }, - ":person_doing_cartwheel_tone1:": { - "style": "github", - "image": "1f938-1f3fb.png", - "unicode": "🤸🏻", - "name": "Person Doing Cartwheel - Tone 1" - }, - ":flag-tr:": { - "style": "github", - "image": "1f1f9-1f1f7.png", - "unicode": "🇹🇷", - "name": "Turkey" - }, - "📢": { - "style": "unicode", - "image": "1f4e2.png", - "name": "Public Address Loudspeaker" - }, - ":syringe:": { - "style": "github", - "image": "1f489.png", - "unicode": "💉", - "name": "Syringe" - }, - ":smiley_cat:": { - "style": "github", - "image": "1f63a.png", - "unicode": "😺", - "name": "Smiling Cat Face With Open Mouth" - }, - ":cow:": { - "style": "github", - "image": "1f42e.png", - "unicode": "🐮", - "name": "Cow Face" - }, - ":rowboat_tone2:": { - "style": "github", - "image": "1f6a3-1f3fc.png", - "unicode": "🚣🏼", - "name": "Rowboat - Tone 2" - }, - "🕷": { - "style": "unicode", - "image": "1f577.png", - "name": "Spider" - }, - ":ice-skate:": { - "style": "github", - "image": "26f8.png", - "unicode": "⛸", - "name": "Ice Skate" - }, - ":regional_indicator_s:": { - "style": "github", - "image": "1f1f8.png", - "unicode": "🇸", - "name": "Regional Indicator Symbol Letter S" - }, - "✊": { - "style": "unicode", - "image": "270a.png", - "name": "Raised Fist" - }, - ":juggling_tone4:": { - "style": "github", - "image": "1f939-1f3fe.png", - "unicode": "🤹🏾", - "name": "Juggling - Tone 4" - }, - "😔": { - "style": "unicode", - "image": "1f614.png", - "name": "Pensive Face" - }, - ":tl:": { - "style": "github", - "image": "1f1f9-1f1f1.png", - "unicode": "🇹🇱", - "name": "Timor-leste" - }, - ":french_bread:": { - "style": "github", - "image": "1f956.png", - "unicode": "🥖", - "name": "Baguette Bread" - }, - ":iphone:": { - "style": "github", - "image": "1f4f1.png", - "unicode": "📱", - "name": "Mobile Phone" - }, - ":santa-tone5:": { - "style": "github", - "image": "1f385-1f3ff.png", - "unicode": "🎅🏿", - "name": "Father Christmas - Tone 5" - }, - ":nail_care_tone3:": { - "style": "github", - "image": "1f485-1f3fd.png", - "unicode": "💅🏽", - "name": "Nail Polish - Tone 3" - }, - ":angel-tone4:": { - "style": "github", - "image": "1f47c-1f3fe.png", - "unicode": "👼🏾", - "name": "Baby Angel - Tone 4" - }, - "🤶": { - "style": "unicode", - "image": "1f936.png", - "name": "Mother Christmas" - }, - ":eyes:": { - "style": "github", - "image": "1f440.png", - "unicode": "👀", - "name": "Eyes" - }, - "🌊": { - "style": "unicode", - "image": "1f30a.png", - "name": "Water Wave" - }, - ":boy_tone1:": { - "style": "github", - "image": "1f466-1f3fb.png", - "unicode": "👦🏻", - "name": "Boy - Tone 1" - }, - ":part_alternation_mark:": { - "style": "github", - "image": "303d.png", - "unicode": "〽", - "name": "Part Alternation Mark" - }, - "📓": { - "style": "unicode", - "image": "1f4d3.png", - "name": "Notebook" - }, - ":factory:": { - "style": "github", - "image": "1f3ed.png", - "unicode": "🏭", - "name": "Factory" - }, - ":man-with-turban-tone3:": { - "style": "github", - "image": "1f473-1f3fd.png", - "unicode": "👳🏽", - "name": "Man With Turban - Tone 3" - }, - "👨": { - "style": "unicode", - "image": "1f468.png", - "name": "Man" - }, - ":dolls:": { - "style": "github", - "image": "1f38e.png", - "unicode": "🎎", - "name": "Japanese Dolls" - }, - "🏽": { - "style": "unicode", - "image": "1f3fd.png", - "name": "Emoji Modifier Fitzpatrick Type-4" - }, - ":european_castle:": { - "style": "github", - "image": "1f3f0.png", - "unicode": "🏰", - "name": "European Castle" - }, - "🎒": { - "style": "unicode", - "image": "1f392.png", - "name": "School Satchel" - }, - ":vulcan_tone3:": { - "style": "github", - "image": "1f596-1f3fd.png", - "unicode": "🖖🏽", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 3" - }, - ":raising_hand_tone3:": { - "style": "github", - "image": "1f64b-1f3fd.png", - "unicode": "🙋🏽", - "name": "Happy Person Raising One Hand Tone3" - }, - "🤵🏽": { - "style": "unicode", - "image": "1f935-1f3fd.png", - "name": "Man In Tuxedo - Tone 3" - }, - "🤵🏼": { - "style": "unicode", - "image": "1f935-1f3fc.png", - "name": "Man In Tuxedo - Tone 2" - }, - "🤵🏿": { - "style": "unicode", - "image": "1f935-1f3ff.png", - "name": "Man In Tuxedo - Tone 5" - }, - "🤵🏾": { - "style": "unicode", - "image": "1f935-1f3fe.png", - "name": "Man In Tuxedo - Tone 4" - }, - "🤵🏻": { - "style": "unicode", - "image": "1f935-1f3fb.png", - "name": "Man In Tuxedo - Tone 1" - }, - ":hand-splayed-tone1:": { - "style": "github", - "image": "1f590-1f3fb.png", - "unicode": "🖐🏻", - "name": "Raised Hand With Fingers Splayed - Tone 1" - }, - ":rugby_football:": { - "style": "github", - "image": "1f3c9.png", - "unicode": "🏉", - "name": "Rugby Football" - }, - ":rolled_up_newspaper:": { - "style": "github", - "image": "1f5de.png", - "unicode": "🗞", - "name": "Rolled-up Newspaper" - }, - ":sleuth_or_spy:": { - "style": "github", - "image": "1f575.png", - "unicode": "🕵", - "name": "Sleuth Or Spy" - }, - ":game_die:": { - "style": "github", - "image": "1f3b2.png", - "unicode": "🎲", - "name": "Game Die" - }, - "🇦": { - "style": "unicode", - "image": "1f1e6.png", - "name": "Regional Indicator Symbol Letter A" - }, - ":open_hands_tone1:": { - "style": "github", - "image": "1f450-1f3fb.png", - "unicode": "👐🏻", - "name": "Open Hands Sign - Tone 1" - }, - ":flag_tj:": { - "style": "github", - "image": "1f1f9-1f1ef.png", - "unicode": "🇹🇯", - "name": "Tajikistan" - }, - ":high_heel:": { - "style": "github", - "image": "1f460.png", - "unicode": "👠", - "name": "High-heeled Shoe" - }, - ":last-quarter-moon:": { - "style": "github", - "image": "1f317.png", - "unicode": "🌗", - "name": "Last Quarter Moon Symbol" - }, - ":closed-book:": { - "style": "github", - "image": "1f4d5.png", - "unicode": "📕", - "name": "Closed Book" - }, - ":astonished:": { - "style": "github", - "image": "1f632.png", - "unicode": "😲", - "name": "Astonished Face" - }, - ":older_woman_tone3:": { - "style": "github", - "image": "1f475-1f3fd.png", - "unicode": "👵🏽", - "name": "Older Woman - Tone 3" - }, - ":horse-racing-tone1:": { - "style": "github", - "image": "1f3c7-1f3fb.png", - "unicode": "🏇🏻", - "name": "Horse Racing - Tone 1" - }, - ":stuck_out_tongue_winking_eye:": { - "style": "github", - "ascii": ">:P", - "image": "1f61c.png", - "unicode": "😜", - "name": "Face With Stuck-out Tongue And Winking Eye" - }, - ":flag_so:": { - "style": "github", - "image": "1f1f8-1f1f4.png", - "unicode": "🇸🇴", - "name": "Somalia" - }, - ":large_blue_circle:": { - "style": "github", - "image": "1f535.png", - "unicode": "🔵", - "name": "Large Blue Circle" - }, - ":v-tone1:": { - "style": "github", - "image": "270c-1f3fb.png", - "unicode": "✌🏻", - "name": "Victory Hand - Tone 1" - }, - ":raised-hands-tone1:": { - "style": "github", - "image": "1f64c-1f3fb.png", - "unicode": "🙌🏻", - "name": "Person Raising Both Hands In Celebration - Tone 1" - }, - "💩": { - "style": "unicode", - "image": "1f4a9.png", - "name": "Pile Of Poo" - }, - ":flag-hu:": { - "style": "github", - "image": "1f1ed-1f1fa.png", - "unicode": "🇭🇺", - "name": "Hungary" - }, - ":helmet_with_white_cross:": { - "style": "github", - "image": "26d1.png", - "unicode": "⛑", - "name": "Helmet With White Cross" - }, - "✴": { - "style": "unicode", - "image": "2734.png", - "name": "Eight Pointed Black Star" - }, - ":children_crossing:": { - "style": "github", - "image": "1f6b8.png", - "unicode": "🚸", - "name": "Children Crossing" - }, - ":flag_jp:": { - "style": "github", - "image": "1f1ef-1f1f5.png", - "unicode": "🇯🇵", - "name": "Japan" - }, - "🐾": { - "style": "unicode", - "image": "1f43e.png", - "name": "Paw Prints" - }, - "🇽🇰": { - "style": "unicode", - "image": "1f1fd-1f1f0.png", - "name": "Kosovo" - }, - ":man-dancing-tone3:": { - "style": "github", - "image": "1f57a-1f3fd.png", - "unicode": "🕺🏽", - "name": "Man Dancing - Tone 3" - }, - ":oncoming-automobile:": { - "style": "github", - "image": "1f698.png", - "unicode": "🚘", - "name": "Oncoming Automobile" - }, - "🗓": { - "style": "unicode", - "image": "1f5d3.png", - "name": "Spiral Calendar Pad" - }, - ":baseball:": { - "style": "github", - "image": "26be.png", - "unicode": "⚾", - "name": "Baseball" - }, - ":bow-tone3:": { - "style": "github", - "image": "1f647-1f3fd.png", - "unicode": "🙇🏽", - "name": "Person Bowing Deeply - Tone 3" - }, - ":rice-ball:": { - "style": "github", - "image": "1f359.png", - "unicode": "🍙", - "name": "Rice Ball" - }, - ":weight_lifter_tone2:": { - "style": "github", - "image": "1f3cb-1f3fc.png", - "unicode": "🏋🏼", - "name": "Weight Lifter - Tone 2" - }, - ":toilet:": { - "style": "github", - "image": "1f6bd.png", - "unicode": "🚽", - "name": "Toilet" - }, - ":coffin:": { - "style": "github", - "image": "26b0.png", - "unicode": "⚰", - "name": "Coffin" - }, - ":mountain-bicyclist:": { - "style": "github", - "image": "1f6b5.png", - "unicode": "🚵", - "name": "Mountain Bicyclist" - }, - ":heart_eyes:": { - "style": "github", - "image": "1f60d.png", - "unicode": "😍", - "name": "Smiling Face With Heart-shaped Eyes" - }, - ":mobile_phone_off:": { - "style": "github", - "image": "1f4f4.png", - "unicode": "📴", - "name": "Mobile Phone Off" - }, - ":flag-ng:": { - "style": "github", - "image": "1f1f3-1f1ec.png", - "unicode": "🇳🇬", - "name": "Nigeria" - }, - ":four-leaf-clover:": { - "style": "github", - "image": "1f340.png", - "unicode": "🍀", - "name": "Four Leaf Clover" - }, - ":cartwheel:": { - "style": "github", - "image": "1f938.png", - "unicode": "🤸", - "name": "Person Doing Cartwheel" - }, - ":punch-tone5:": { - "style": "github", - "image": "1f44a-1f3ff.png", - "unicode": "👊🏿", - "name": "Fisted Hand Sign - Tone 5" - }, - "🌧": { - "style": "unicode", - "image": "1f327.png", - "name": "Cloud With Rain" - }, - ":flag_er:": { - "style": "github", - "image": "1f1ea-1f1f7.png", - "unicode": "🇪🇷", - "name": "Eritrea" - }, - ":mf:": { - "style": "github", - "image": "1f1f2-1f1eb.png", - "unicode": "🇲🇫", - "name": "Saint Martin" - }, - ":cd:": { - "style": "github", - "image": "1f4bf.png", - "unicode": "💿", - "name": "Optical Disc" - }, - "🎼": { - "style": "unicode", - "image": "1f3bc.png", - "name": "Musical Score" - }, - ":flag-pe:": { - "style": "github", - "image": "1f1f5-1f1ea.png", - "unicode": "🇵🇪", - "name": "Peru" - }, - "O:-3": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":boxing_gloves:": { - "style": "github", - "image": "1f94a.png", - "unicode": "🥊", - "name": "Boxing Glove" - }, - "O:-)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":no_good_tone4:": { - "style": "github", - "image": "1f645-1f3fe.png", - "unicode": "🙅🏾", - "name": "Face With No Good Gesture - Tone 4" - }, - ":-1_tone5:": { - "style": "github", - "image": "1f44e-1f3ff.png", - "unicode": "👎🏿", - "name": "Thumbs Down Sign - Tone 5" - }, - "🕺🏼": { - "style": "unicode", - "image": "1f57a-1f3fc.png", - "name": "Man Dancing - Tone 2" - }, - "🕺🏽": { - "style": "unicode", - "image": "1f57a-1f3fd.png", - "name": "Man Dancing - Tone 3" - }, - "🕺🏾": { - "style": "unicode", - "image": "1f57a-1f3fe.png", - "name": "Man Dancing - Tone 4" - }, - "🕺🏿": { - "style": "unicode", - "image": "1f57a-1f3ff.png", - "name": "Man Dancing - Tone 5" - }, - "🐔": { - "style": "unicode", - "image": "1f414.png", - "name": "Chicken" - }, - "🕺🏻": { - "style": "unicode", - "image": "1f57a-1f3fb.png", - "name": "Man Dancing - Tone 1" - }, - ":baby_tone2:": { - "style": "github", - "image": "1f476-1f3fc.png", - "unicode": "👶🏼", - "name": "Baby - Tone 2" - }, - ":point-down-tone3:": { - "style": "github", - "image": "1f447-1f3fd.png", - "unicode": "👇🏽", - "name": "White Down Pointing Backhand Index - Tone 3" - }, - ":flame:": { - "style": "github", - "image": "1f525.png", - "unicode": "🔥", - "name": "Fire" - }, - ":pray-tone4:": { - "style": "github", - "image": "1f64f-1f3fe.png", - "unicode": "🙏🏾", - "name": "Person With Folded Hands - Tone 4" - }, - ":arrow_double_down:": { - "style": "github", - "image": "23ec.png", - "unicode": "⏬", - "name": "Black Down-pointing Double Triangle" - }, - ":hr:": { - "style": "github", - "image": "1f1ed-1f1f7.png", - "unicode": "🇭🇷", - "name": "Croatia" - }, - ":woman_tone3:": { - "style": "github", - "image": "1f469-1f3fd.png", - "unicode": "👩🏽", - "name": "Woman - Tone 3" - }, - "⛳": { - "style": "unicode", - "image": "26f3.png", - "name": "Flag In Hole" - }, - "🇽": { - "style": "unicode", - "image": "1f1fd.png", - "name": "Regional Indicator Symbol Letter X" - }, - ":hash:": { - "style": "github", - "image": "0023-20e3.png", - "unicode": "#⃣", - "name": "Keycap Number Sign" - }, - ":expecting_woman:": { - "style": "github", - "image": "1f930.png", - "unicode": "🤰", - "name": "Pregnant Woman" - }, - ":handball-tone4:": { - "style": "github", - "image": "1f93e-1f3fe.png", - "unicode": "🤾🏾", - "name": "Handball - Tone 4" - }, - ":cruise_ship:": { - "style": "github", - "image": "1f6f3.png", - "unicode": "🛳", - "name": "Passenger Ship" - }, - ":open-file-folder:": { - "style": "github", - "image": "1f4c2.png", - "unicode": "📂", - "name": "Open File Folder" - }, - "🆒": { - "style": "unicode", - "image": "1f192.png", - "name": "Squared Cool" - }, - ":steam-locomotive:": { - "style": "github", - "image": "1f682.png", - "unicode": "🚂", - "name": "Steam Locomotive" - }, - ":beach_umbrella:": { - "style": "github", - "image": "26f1.png", - "unicode": "⛱", - "name": "Umbrella On Ground" - }, - ":waning-gibbous-moon:": { - "style": "github", - "image": "1f316.png", - "unicode": "🌖", - "name": "Waning Gibbous Moon Symbol" - }, - ":raised-hand-tone1:": { - "style": "github", - "image": "270b-1f3fb.png", - "unicode": "✋🏻", - "name": "Raised Hand - Tone 1" - }, - ":sg:": { - "style": "github", - "image": "1f1f8-1f1ec.png", - "unicode": "🇸🇬", - "name": "Singapore" - }, - ":selfie-tone3:": { - "style": "github", - "image": "1f933-1f3fd.png", - "unicode": "🤳🏽", - "name": "Selfie - Tone 3" - }, - ":fingers_crossed_tone5:": { - "style": "github", - "image": "1f91e-1f3ff.png", - "unicode": "🤞🏿", - "name": "Hand With Index And Middle Fingers Crossed - Tone 5" - }, - ":flag-gl:": { - "style": "github", - "image": "1f1ec-1f1f1.png", - "unicode": "🇬🇱", - "name": "Greenland" - }, - "🍑": { - "style": "unicode", - "image": "1f351.png", - "name": "Peach" - }, - ":smiling-imp:": { - "style": "github", - "image": "1f608.png", - "unicode": "😈", - "name": "Smiling Face With Horns" - }, - "🕕": { - "style": "unicode", - "image": "1f555.png", - "name": "Clock Face Six Oclock" - }, - ":hourglass-flowing-sand:": { - "style": "github", - "image": "23f3.png", - "unicode": "⏳", - "name": "Hourglass With Flowing Sand" - }, - ":disappointed-relieved:": { - "style": "github", - "image": "1f625.png", - "unicode": "😥", - "name": "Disappointed But Relieved Face" - }, - "🏦": { - "style": "unicode", - "image": "1f3e6.png", - "name": "Bank" - }, - ":surfer-tone3:": { - "style": "github", - "image": "1f3c4-1f3fd.png", - "unicode": "🏄🏽", - "name": "Surfer - Tone 3" - }, - ":lifter-tone1:": { - "style": "github", - "image": "1f3cb-1f3fb.png", - "unicode": "🏋🏻", - "name": "Weight Lifter - Tone 1" - }, - "👿": { - "style": "unicode", - "image": "1f47f.png", - "name": "Imp" - }, - ":point-up-2-tone3:": { - "style": "github", - "image": "1f446-1f3fd.png", - "unicode": "👆🏽", - "name": "White Up Pointing Backhand Index - Tone 3" - }, - ":walking_tone2:": { - "style": "github", - "image": "1f6b6-1f3fc.png", - "unicode": "🚶🏼", - "name": "Pedestrian - Tone 2" - }, - "🇼🇸": { - "style": "unicode", - "image": "1f1fc-1f1f8.png", - "name": "Samoa" - }, - "🌐": { - "style": "unicode", - "image": "1f310.png", - "name": "Globe With Meridians" - }, - "🔔": { - "style": "unicode", - "image": "1f514.png", - "name": "Bell" - }, - "🇼🇫": { - "style": "unicode", - "image": "1f1fc-1f1eb.png", - "name": "Wallis And Futuna" - }, - ":previous_track:": { - "style": "github", - "image": "23ee.png", - "unicode": "⏮", - "name": "Black Left-pointing Double Triangle With Vertical Bar" - }, - "🚩": { - "style": "unicode", - "image": "1f6a9.png", - "name": "Triangular Flag On Post" - }, - ":left_facing_fist_tone5:": { - "style": "github", - "image": "1f91b-1f3ff.png", - "unicode": "🤛🏿", - "name": "Left Facing Fist - Tone 5" - }, - ":gemini:": { - "style": "github", - "image": "264a.png", - "unicode": "♊", - "name": "Gemini" - }, - ":o:": { - "style": "github", - "image": "2b55.png", - "unicode": "⭕", - "name": "Heavy Large Circle" - }, - ":dove_of_peace:": { - "style": "github", - "image": "1f54a.png", - "unicode": "🕊", - "name": "Dove Of Peace" - }, - "😾": { - "style": "unicode", - "image": "1f63e.png", - "name": "Pouting Cat Face" - }, - ":helmet_with_cross:": { - "style": "github", - "image": "26d1.png", - "unicode": "⛑", - "name": "Helmet With White Cross" - }, - ":flag_gm:": { - "style": "github", - "image": "1f1ec-1f1f2.png", - "unicode": "🇬🇲", - "name": "The Gambia" - }, - "😶": { - "style": "unicode", - "ascii": ":-X", - "image": "1f636.png", - "name": "Face Without Mouth" - }, - ":couple_ww:": { - "style": "github", - "image": "1f469-2764-1f469.png", - "unicode": "👩❤👩", - "name": "Couple (woman,woman)" - }, - ":kw:": { - "style": "github", - "image": "1f1f0-1f1fc.png", - "unicode": "🇰🇼", - "name": "Kuwait" - }, - ":notebook-with-decorative-cover:": { - "style": "github", - "image": "1f4d4.png", - "unicode": "📔", - "name": "Notebook With Decorative Cover" - }, - ">;)": { - "style": "ascii", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ":innocent:": { - "style": "github", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":flag-si:": { - "style": "github", - "image": "1f1f8-1f1ee.png", - "unicode": "🇸🇮", - "name": "Slovenia" - }, - ":wrestling_tone3:": { - "style": "github", - "image": "1f93c-1f3fd.png", - "unicode": "🤼🏽", - "name": "Wrestlers - Tone 3" - }, - ":white-medium-square:": { - "style": "github", - "image": "25fb.png", - "unicode": "◻", - "name": "White Medium Square" - }, - ":iq:": { - "style": "github", - "image": "1f1ee-1f1f6.png", - "unicode": "🇮🇶", - "name": "Iraq" - }, - ":game-die:": { - "style": "github", - "image": "1f3b2.png", - "unicode": "🎲", - "name": "Game Die" - }, - "1⃣": { - "style": "unicode", - "image": "0031-20e3.png", - "name": "Keycap Digit One" - }, - ":information_desk_person_tone4:": { - "style": "github", - "image": "1f481-1f3fe.png", - "unicode": "💁🏾", - "name": "Information Desk Person - Tone 4" - }, - "🉑": { - "style": "unicode", - "image": "1f251.png", - "name": "Circled Ideograph Accept" - }, - "👕": { - "style": "unicode", - "image": "1f455.png", - "name": "T-shirt" - }, - ":raised-back-of-hand-tone5:": { - "style": "github", - "image": "1f91a-1f3ff.png", - "unicode": "🤚🏿", - "name": "Raised Back Of Hand - Tone 5" - }, - "🕵🏽": { - "style": "unicode", - "image": "1f575-1f3fd.png", - "name": "Sleuth Or Spy - Tone 3" - }, - "🕵🏼": { - "style": "unicode", - "image": "1f575-1f3fc.png", - "name": "Sleuth Or Spy - Tone 2" - }, - "🕵🏿": { - "style": "unicode", - "image": "1f575-1f3ff.png", - "name": "Sleuth Or Spy - Tone 5" - }, - "🕵🏾": { - "style": "unicode", - "image": "1f575-1f3fe.png", - "name": "Sleuth Or Spy - Tone 4" - }, - "🕵🏻": { - "style": "unicode", - "image": "1f575-1f3fb.png", - "name": "Sleuth Or Spy - Tone 1" - }, - "📪": { - "style": "unicode", - "image": "1f4ea.png", - "name": "Closed Mailbox With Lowered Flag" - }, - ":ear_tone3:": { - "style": "github", - "image": "1f442-1f3fd.png", - "unicode": "👂🏽", - "name": "Ear - Tone 3" - }, - "👨❤💋👨": { - "style": "unicode", - "image": "1f468-2764-1f48b-1f468.png", - "name": "Kiss (man,man)" - }, - ":bookmark_tabs:": { - "style": "github", - "image": "1f4d1.png", - "unicode": "📑", - "name": "Bookmark Tabs" - }, - "🍻": { - "style": "unicode", - "image": "1f37b.png", - "name": "Clinking Beer Mugs" - }, - ":handshake_tone2:": { - "style": "github", - "image": "1f91d-1f3fc.png", - "unicode": "🤝🏼", - "name": "Handshake - Tone 2" - }, - ":ok:": { - "style": "github", - "image": "1f197.png", - "unicode": "🆗", - "name": "Squared Ok" - }, - ":construction_worker_tone5:": { - "style": "github", - "image": "1f477-1f3ff.png", - "unicode": "👷🏿", - "name": "Construction Worker - Tone 5" - }, - "🐐": { - "style": "unicode", - "image": "1f410.png", - "name": "Goat" - }, - ":juggling-tone3:": { - "style": "github", - "image": "1f939-1f3fd.png", - "unicode": "🤹🏽", - "name": "Juggling - Tone 3" - }, - "B-)": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - ":regional-indicator-y:": { - "style": "github", - "image": "1f1fe.png", - "unicode": "🇾", - "name": "Regional Indicator Symbol Letter Y" - }, - ":shallow_pan_of_food:": { - "style": "github", - "image": "1f958.png", - "unicode": "🥘", - "name": "Shallow Pan Of Food" - }, - "B-D": { - "style": "ascii", - "ascii": "B-)", - "image": "1f60e.png", - "unicode": "😎", - "name": "Smiling Face With Sunglasses" - }, - ":flag_az:": { - "style": "github", - "image": "1f1e6-1f1ff.png", - "unicode": "🇦🇿", - "name": "Azerbaijan" - }, - ":stadium:": { - "style": "github", - "image": "1f3df.png", - "unicode": "🏟", - "name": "Stadium" - }, - ":house:": { - "style": "github", - "image": "1f3e0.png", - "unicode": "🏠", - "name": "House Building" - }, - ":open_mouth:": { - "style": "github", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":flag-tt:": { - "style": "github", - "image": "1f1f9-1f1f9.png", - "unicode": "🇹🇹", - "name": "Trinidad And Tobago" - }, - ":flag_um:": { - "style": "github", - "image": "1f1fa-1f1f2.png", - "unicode": "🇺🇲", - "name": "United States Minor Outlying Islands" - }, - ":'(": { - "style": "ascii", - "ascii": ":'(", - "image": "1f622.png", - "unicode": "😢", - "name": "Crying Face" - }, - ":')": { - "style": "ascii", - "ascii": ":')", - "image": "1f602.png", - "unicode": "😂", - "name": "Face With Tears Of Joy" - }, - ":rowboat_tone4:": { - "style": "github", - "image": "1f6a3-1f3fe.png", - "unicode": "🚣🏾", - "name": "Rowboat - Tone 4" - }, - ":regional_indicator_q:": { - "style": "github", - "image": "1f1f6.png", - "unicode": "🇶", - "name": "Regional Indicator Symbol Letter Q" - }, - "#)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":baby-chick:": { - "style": "github", - "image": "1f424.png", - "unicode": "🐤", - "name": "Baby Chick" - }, - ":massage-tone5:": { - "style": "github", - "image": "1f486-1f3ff.png", - "unicode": "💆🏿", - "name": "Face Massage - Tone 5" - }, - "🦎": { - "style": "unicode", - "image": "1f98e.png", - "name": "Lizard" - }, - ":tj:": { - "style": "github", - "image": "1f1f9-1f1ef.png", - "unicode": "🇹🇯", - "name": "Tajikistan" - }, - ":paintbrush:": { - "style": "github", - "image": "1f58c.png", - "unicode": "🖌", - "name": "Lower Left Paintbrush" - }, - "✝": { - "style": "unicode", - "image": "271d.png", - "name": "Latin Cross" - }, - ":nail_care_tone1:": { - "style": "github", - "image": "1f485-1f3fb.png", - "unicode": "💅🏻", - "name": "Nail Polish - Tone 1" - }, - "😧": { - "style": "unicode", - "image": "1f627.png", - "name": "Anguished Face" - }, - ":paella:": { - "style": "github", - "image": "1f958.png", - "unicode": "🥘", - "name": "Shallow Pan Of Food" - }, - ":banana:": { - "style": "github", - "image": "1f34c.png", - "unicode": "🍌", - "name": "Banana" - }, - "🚼": { - "style": "unicode", - "image": "1f6bc.png", - "name": "Baby Symbol" - }, - "🕑": { - "style": "unicode", - "image": "1f551.png", - "name": "Clock Face Two Oclock" - }, - "🍕": { - "style": "unicode", - "image": "1f355.png", - "name": "Slice Of Pizza" - }, - ":man-with-turban-tone1:": { - "style": "github", - "image": "1f473-1f3fb.png", - "unicode": "👳🏻", - "name": "Man With Turban - Tone 1" - }, - ":flag_mg:": { - "style": "github", - "image": "1f1f2-1f1ec.png", - "unicode": "🇲🇬", - "name": "Madagascar" - }, - ":shirt:": { - "style": "github", - "image": "1f455.png", - "unicode": "👕", - "name": "T-shirt" - }, - "🏪": { - "style": "unicode", - "image": "1f3ea.png", - "name": "Convenience Store" - }, - ":fire_engine:": { - "style": "github", - "image": "1f692.png", - "unicode": "🚒", - "name": "Fire Engine" - }, - ":flag-km:": { - "style": "github", - "image": "1f1f0-1f1f2.png", - "unicode": "🇰🇲", - "name": "The Comoros" - }, - ":lk:": { - "style": "github", - "image": "1f1f1-1f1f0.png", - "unicode": "🇱🇰", - "name": "Sri Lanka" - }, - "👻": { - "style": "unicode", - "image": "1f47b.png", - "name": "Ghost" - }, - ":be:": { - "style": "github", - "image": "1f1e7-1f1ea.png", - "unicode": "🇧🇪", - "name": "Belgium" - }, - "🔐": { - "style": "unicode", - "image": "1f510.png", - "name": "Closed Lock With Key" - }, - "🌔": { - "style": "unicode", - "image": "1f314.png", - "name": "Waxing Gibbous Moon Symbol" - }, - ":vulcan_tone1:": { - "style": "github", - "image": "1f596-1f3fb.png", - "unicode": "🖖🏻", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 1" - }, - ":flag-cm:": { - "style": "github", - "image": "1f1e8-1f1f2.png", - "unicode": "🇨🇲", - "name": "Cameroon" - }, - "👲🏼": { - "style": "unicode", - "image": "1f472-1f3fc.png", - "name": "Man With Gua Pi Mao - Tone 2" - }, - "👲🏽": { - "style": "unicode", - "image": "1f472-1f3fd.png", - "name": "Man With Gua Pi Mao - Tone 3" - }, - "👲🏾": { - "style": "unicode", - "image": "1f472-1f3fe.png", - "name": "Man With Gua Pi Mao - Tone 4" - }, - "👲🏿": { - "style": "unicode", - "image": "1f472-1f3ff.png", - "name": "Man With Gua Pi Mao - Tone 5" - }, - ":crying_cat_face:": { - "style": "github", - "image": "1f63f.png", - "unicode": "😿", - "name": "Crying Cat Face" - }, - "👲🏻": { - "style": "unicode", - "image": "1f472-1f3fb.png", - "name": "Man With Gua Pi Mao - Tone 1" - }, - ":grandma:": { - "style": "github", - "image": "1f475.png", - "unicode": "👵", - "name": "Older Woman" - }, - ":princess_tone2:": { - "style": "github", - "image": "1f478-1f3fc.png", - "unicode": "👸🏼", - "name": "Princess - Tone 2" - }, - ":hamburger:": { - "style": "github", - "image": "1f354.png", - "unicode": "🍔", - "name": "Hamburger" - }, - ":champagne_glass:": { - "style": "github", - "image": "1f942.png", - "unicode": "🥂", - "name": "Clinking Glasses" - }, - ":speech-left:": { - "style": "github", - "image": "1f5e8.png", - "unicode": "🗨", - "name": "Left Speech Bubble" - }, - ":flag_ht:": { - "style": "github", - "image": "1f1ed-1f1f9.png", - "unicode": "🇭🇹", - "name": "Haiti" - }, - ":open_hands_tone3:": { - "style": "github", - "image": "1f450-1f3fd.png", - "unicode": "👐🏽", - "name": "Open Hands Sign - Tone 3" - }, - ":flag_th:": { - "style": "github", - "image": "1f1f9-1f1ed.png", - "unicode": "🇹🇭", - "name": "Thailand" - }, - ":flag_bb:": { - "style": "github", - "image": "1f1e7-1f1e7.png", - "unicode": "🇧🇧", - "name": "Barbados" - }, - ":flag-bi:": { - "style": "github", - "image": "1f1e7-1f1ee.png", - "unicode": "🇧🇮", - "name": "Burundi" - }, - "🚒": { - "style": "unicode", - "image": "1f692.png", - "name": "Fire Engine" - }, - "☝": { - "style": "unicode", - "image": "261d.png", - "name": "White Up Pointing Index" - }, - ":massage_tone4:": { - "style": "github", - "image": "1f486-1f3fe.png", - "unicode": "💆🏾", - "name": "Face Massage - Tone 4" - }, - "🤣": { - "style": "unicode", - "image": "1f923.png", - "name": "Rolling On The Floor Laughing" - }, - ":horse-racing:": { - "style": "github", - "image": "1f3c7.png", - "unicode": "🏇", - "name": "Horse Racing" - }, - ":family-mmg:": { - "style": "github", - "image": "1f468-1f468-1f467.png", - "unicode": "👨👨👧", - "name": "Family (man,man,girl)" - }, - ":reversed_hand_with_middle_finger_extended_tone2:": { - "style": "github", - "image": "1f595-1f3fc.png", - "unicode": "🖕🏼", - "name": "Reversed Hand With Middle Finger Extended - Tone 2" - }, - ":ear-tone4:": { - "style": "github", - "image": "1f442-1f3fe.png", - "unicode": "👂🏾", - "name": "Ear - Tone 4" - }, - "🇮🇨": { - "style": "unicode", - "image": "1f1ee-1f1e8.png", - "name": "Canary Islands" - }, - ":bow-tone5:": { - "style": "github", - "image": "1f647-1f3ff.png", - "unicode": "🙇🏿", - "name": "Person Bowing Deeply - Tone 5" - }, - "❇": { - "style": "unicode", - "image": "2747.png", - "name": "Sparkle" - }, - "👴🏿": { - "style": "unicode", - "image": "1f474-1f3ff.png", - "name": "Older Man - Tone 5" - }, - ":call-me-tone4:": { - "style": "github", - "image": "1f919-1f3fe.png", - "unicode": "🤙🏾", - "name": "Call Me Hand - Tone 4" - }, - "👴🏼": { - "style": "unicode", - "image": "1f474-1f3fc.png", - "name": "Older Man - Tone 2" - }, - ":flag-gq:": { - "style": "github", - "image": "1f1ec-1f1f6.png", - "unicode": "🇬🇶", - "name": "Equatorial Guinea" - }, - ":kiss-ww:": { - "style": "github", - "image": "1f469-2764-1f48b-1f469.png", - "unicode": "👩❤💋👩", - "name": "Kiss (woman,woman)" - }, - ":thunder_cloud_and_rain:": { - "style": "github", - "image": "26c8.png", - "unicode": "⛈", - "name": "Thunder Cloud And Rain" - }, - ":spy:": { - "style": "github", - "image": "1f575.png", - "unicode": "🕵", - "name": "Sleuth Or Spy" - }, - ":man-dancing-tone1:": { - "style": "github", - "image": "1f57a-1f3fb.png", - "unicode": "🕺🏻", - "name": "Man Dancing - Tone 1" - }, - "📦": { - "style": "unicode", - "image": "1f4e6.png", - "name": "Package" - }, - ":flag_sm:": { - "style": "github", - "image": "1f1f8-1f1f2.png", - "unicode": "🇸🇲", - "name": "San Marino" - }, - "🍿": { - "style": "unicode", - "image": "1f37f.png", - "name": "Popcorn" - }, - ":point-down:": { - "style": "github", - "image": "1f447.png", - "unicode": "👇", - "name": "White Down Pointing Backhand Index" - }, - ":new-moon:": { - "style": "github", - "image": "1f311.png", - "unicode": "🌑", - "name": "New Moon Symbol" - }, - "😐": { - "style": "unicode", - "image": "1f610.png", - "name": "Neutral Face" - }, - ":flag_et:": { - "style": "github", - "image": "1f1ea-1f1f9.png", - "unicode": "🇪🇹", - "name": "Ethiopia" - }, - ":flag-ne:": { - "style": "github", - "image": "1f1f3-1f1ea.png", - "unicode": "🇳🇪", - "name": "Niger" - }, - "⚛": { - "style": "unicode", - "image": "269b.png", - "name": "Atom Symbol" - }, - ":movie_camera:": { - "style": "github", - "image": "1f3a5.png", - "unicode": "🎥", - "name": "Movie Camera" - }, - "🎩": { - "style": "unicode", - "image": "1f3a9.png", - "name": "Top Hat" - }, - ":mx:": { - "style": "github", - "image": "1f1f2-1f1fd.png", - "unicode": "🇲🇽", - "name": "Mexico" - }, - ":no_bell:": { - "style": "github", - "image": "1f515.png", - "unicode": "🔕", - "name": "Bell With Cancellation Stroke" - }, - "🤺": { - "style": "unicode", - "image": "1f93a.png", - "name": "Fencer" - }, - "🌾": { - "style": "unicode", - "image": "1f33e.png", - "name": "Ear Of Rice" - }, - ":flag-pg:": { - "style": "github", - "image": "1f1f5-1f1ec.png", - "unicode": "🇵🇬", - "name": "Papua New Guinea" - }, - "🚶": { - "style": "unicode", - "image": "1f6b6.png", - "name": "Pedestrian" - }, - "🎸": { - "style": "unicode", - "image": "1f3b8.png", - "name": "Guitar" - }, - ":flag_us:": { - "style": "github", - "image": "1f1fa-1f1f8.png", - "unicode": "🇺🇸", - "name": "United States" - }, - ":bz:": { - "style": "github", - "image": "1f1e7-1f1ff.png", - "unicode": "🇧🇿", - "name": "Belize" - }, - ":twisted_rightwards_arrows:": { - "style": "github", - "image": "1f500.png", - "unicode": "🔀", - "name": "Twisted Rightwards Arrows" - }, - ":sleuth_or_spy_tone2:": { - "style": "github", - "image": "1f575-1f3fc.png", - "unicode": "🕵🏼", - "name": "Sleuth Or Spy - Tone 2" - }, - ":bike:": { - "style": "github", - "image": "1f6b2.png", - "unicode": "🚲", - "name": "Bicycle" - }, - "🗽": { - "style": "unicode", - "image": "1f5fd.png", - "name": "Statue Of Liberty" - }, - ":notepad_spiral:": { - "style": "github", - "image": "1f5d2.png", - "unicode": "🗒", - "name": "Spiral Note Pad" - }, - ":large-blue-circle:": { - "style": "github", - "image": "1f535.png", - "unicode": "🔵", - "name": "Large Blue Circle" - }, - ":point-down-tone1:": { - "style": "github", - "image": "1f447-1f3fb.png", - "unicode": "👇🏻", - "name": "White Down Pointing Backhand Index - Tone 1" - }, - "#-)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":white_medium_square:": { - "style": "github", - "image": "25fb.png", - "unicode": "◻", - "name": "White Medium Square" - }, - ":heavy-multiplication-x:": { - "style": "github", - "image": "2716.png", - "unicode": "✖", - "name": "Heavy Multiplication X" - }, - "💼": { - "style": "unicode", - "image": "1f4bc.png", - "name": "Briefcase" - }, - ":milky-way:": { - "style": "github", - "image": "1f30c.png", - "unicode": "🌌", - "name": "Milky Way" - }, - ":fingers-crossed-tone2:": { - "style": "github", - "image": "1f91e-1f3fc.png", - "unicode": "🤞🏼", - "name": "Hand With Index And Middle Fingers Crossed - Tone 2" - }, - "🇪": { - "style": "unicode", - "image": "1f1ea.png", - "name": "Regional Indicator Symbol Letter E" - }, - ":woman_tone5:": { - "style": "github", - "image": "1f469-1f3ff.png", - "unicode": "👩🏿", - "name": "Woman - Tone 5" - }, - "🌁": { - "style": "unicode", - "image": "1f301.png", - "name": "Foggy" - }, - ":tennis:": { - "style": "github", - "image": "1f3be.png", - "unicode": "🎾", - "name": "Tennis Racquet And Ball" - }, - ":flag_ta:": { - "style": "github", - "image": "1f1f9-1f1e6.png", - "unicode": "🇹🇦", - "name": "Tristan Da Cunha" - }, - ":hatched-chick:": { - "style": "github", - "image": "1f425.png", - "unicode": "🐥", - "name": "Front-facing Baby Chick" - }, - ":selfie-tone5:": { - "style": "github", - "image": "1f933-1f3ff.png", - "unicode": "🤳🏿", - "name": "Selfie - Tone 5" - }, - "🏊🏽": { - "style": "unicode", - "image": "1f3ca-1f3fd.png", - "name": "Swimmer - Tone 3" - }, - "🏊🏾": { - "style": "unicode", - "image": "1f3ca-1f3fe.png", - "name": "Swimmer - Tone 4" - }, - "🏊🏿": { - "style": "unicode", - "image": "1f3ca-1f3ff.png", - "name": "Swimmer - Tone 5" - }, - "🏊🏻": { - "style": "unicode", - "image": "1f3ca-1f3fb.png", - "name": "Swimmer - Tone 1" - }, - ":se:": { - "style": "github", - "image": "1f1f8-1f1ea.png", - "unicode": "🇸🇪", - "name": "Sweden" - }, - ":flag-vu:": { - "style": "github", - "image": "1f1fb-1f1fa.png", - "unicode": "🇻🇺", - "name": "Vanuatu" - }, - ":spy_tone4:": { - "style": "github", - "image": "1f575-1f3fe.png", - "unicode": "🕵🏾", - "name": "Sleuth Or Spy - Tone 4" - }, - ":man:": { - "style": "github", - "image": "1f468.png", - "unicode": "👨", - "name": "Man" - }, - ":flag_dk:": { - "style": "github", - "image": "1f1e9-1f1f0.png", - "unicode": "🇩🇰", - "name": "Denmark" - }, - ":leaves:": { - "style": "github", - "image": "1f343.png", - "unicode": "🍃", - "name": "Leaf Fluttering In Wind" - }, - ":flag-gn:": { - "style": "github", - "image": "1f1ec-1f1f3.png", - "unicode": "🇬🇳", - "name": "Guinea" - }, - ":nf:": { - "style": "github", - "image": "1f1f3-1f1eb.png", - "unicode": "🇳🇫", - "name": "Norfolk Island" - }, - "🏓": { - "style": "unicode", - "image": "1f3d3.png", - "name": "Table Tennis Paddle And Ball" - }, - ":bust_in_silhouette:": { - "style": "github", - "image": "1f464.png", - "unicode": "👤", - "name": "Bust In Silhouette" - }, - ":fingers_crossed_tone3:": { - "style": "github", - "image": "1f91e-1f3fd.png", - "unicode": "🤞🏽", - "name": "Hand With Index And Middle Fingers Crossed - Tone 3" - }, - ":pig-nose:": { - "style": "github", - "image": "1f43d.png", - "unicode": "🐽", - "name": "Pig Nose" - }, - ":croissant:": { - "style": "github", - "image": "1f950.png", - "unicode": "🥐", - "name": "Croissant" - }, - ":surfer_tone5:": { - "style": "github", - "image": "1f3c4-1f3ff.png", - "unicode": "🏄🏿", - "name": "Surfer - Tone 5" - }, - ":writing-hand-tone2:": { - "style": "github", - "image": "270d-1f3fc.png", - "unicode": "✍🏼", - "name": "Writing Hand - Tone 2" - }, - ":suspension_railway:": { - "style": "github", - "image": "1f69f.png", - "unicode": "🚟", - "name": "Suspension Railway" - }, - "⏳": { - "style": "unicode", - "image": "23f3.png", - "name": "Hourglass With Flowing Sand" - }, - ":flag-tk:": { - "style": "github", - "image": "1f1f9-1f1f0.png", - "unicode": "🇹🇰", - "name": "Tokelau" - }, - "📽": { - "style": "unicode", - "image": "1f4fd.png", - "name": "Film Projector" - }, - ":credit_card:": { - "style": "github", - "image": "1f4b3.png", - "unicode": "💳", - "name": "Credit Card" - }, - ":dragon_face:": { - "style": "github", - "image": "1f432.png", - "unicode": "🐲", - "name": "Dragon Face" - }, - ":point-up-2-tone5:": { - "style": "github", - "image": "1f446-1f3ff.png", - "unicode": "👆🏿", - "name": "White Up Pointing Backhand Index - Tone 5" - }, - "💒": { - "style": "unicode", - "image": "1f492.png", - "name": "Wedding" - }, - "🔧": { - "style": "unicode", - "image": "1f527.png", - "name": "Wrench" - }, - ":clock130:": { - "style": "github", - "image": "1f55c.png", - "unicode": "🕜", - "name": "Clock Face One-thirty" - }, - ":left_facing_fist_tone3:": { - "style": "github", - "image": "1f91b-1f3fd.png", - "unicode": "🤛🏽", - "name": "Left Facing Fist - Tone 3" - }, - ":necktie:": { - "style": "github", - "image": "1f454.png", - "unicode": "👔", - "name": "Necktie" - }, - ":girl:": { - "style": "github", - "image": "1f467.png", - "unicode": "👧", - "name": "Girl" - }, - "🖼": { - "style": "unicode", - "image": "1f5bc.png", - "name": "Frame With Picture" - }, - ":u6307:": { - "style": "github", - "image": "1f22f.png", - "unicode": "🈯", - "name": "Squared Cjk Unified Ideograph-6307" - }, - ":ac:": { - "style": "github", - "image": "1f1e6-1f1e8.png", - "unicode": "🇦🇨", - "name": "Ascension" - }, - ":shinto-shrine:": { - "style": "github", - "image": "26e9.png", - "unicode": "⛩", - "name": "Shinto Shrine" - }, - ":crossed-swords:": { - "style": "github", - "image": "2694.png", - "unicode": "⚔", - "name": "Crossed Swords" - }, - ":right-facing-fist-tone2:": { - "style": "github", - "image": "1f91c-1f3fc.png", - "unicode": "🤜🏼", - "name": "Right Facing Fist - Tone 2" - }, - "🅿": { - "style": "unicode", - "image": "1f17f.png", - "name": "Negative Squared Latin Capital Letter P" - }, - ":pound:": { - "style": "github", - "image": "1f4b7.png", - "unicode": "💷", - "name": "Banknote With Pound Sign" - }, - ":arrow_double_up:": { - "style": "github", - "image": "23eb.png", - "unicode": "⏫", - "name": "Black Up-pointing Double Triangle" - }, - "😄": { - "style": "unicode", - "image": "1f604.png", - "name": "Smiling Face With Open Mouth And Smiling Eyes" - }, - ":is:": { - "style": "github", - "image": "1f1ee-1f1f8.png", - "unicode": "🇮🇸", - "name": "Iceland" - }, - ":face_palm_tone2:": { - "style": "github", - "image": "1f926-1f3fc.png", - "unicode": "🤦🏼", - "name": "Face Palm - Tone 2" - }, - "㊗": { - "style": "unicode", - "image": "3297.png", - "name": "Circled Ideograph Congratulation" - }, - ":wrestling_tone1:": { - "style": "github", - "image": "1f93c-1f3fb.png", - "unicode": "🤼🏻", - "name": "Wrestlers - Tone 1" - }, - "🔮": { - "style": "unicode", - "image": "1f52e.png", - "name": "Crystal Ball" - }, - ":dog2:": { - "style": "github", - "image": "1f415.png", - "unicode": "🐕", - "name": "Dog" - }, - "📃": { - "style": "unicode", - "image": "1f4c3.png", - "name": "Page With Curl" - }, - ":hm:": { - "style": "github", - "image": "1f1ed-1f1f2.png", - "unicode": "🇭🇲", - "name": "Heard Island And Mcdonald Islands" - }, - ":grey_exclamation:": { - "style": "github", - "image": "2755.png", - "unicode": "❕", - "name": "White Exclamation Mark Ornament" - }, - "👘": { - "style": "unicode", - "image": "1f458.png", - "name": "Kimono" - }, - ":pl:": { - "style": "github", - "image": "1f1f5-1f1f1.png", - "unicode": "🇵🇱", - "name": "Poland" - }, - ":goal_net:": { - "style": "github", - "image": "1f945.png", - "unicode": "🥅", - "name": "Goal Net" - }, - ":black-medium-square:": { - "style": "github", - "image": "25fc.png", - "unicode": "◼", - "name": "Black Medium Square" - }, - "🏭": { - "style": "unicode", - "image": "1f3ed.png", - "name": "Factory" - }, - ":handshake_tone4:": { - "style": "github", - "image": "1f91d-1f3fe.png", - "unicode": "🤝🏾", - "name": "Handshake - Tone 4" - }, - ":princess:": { - "style": "github", - "image": "1f478.png", - "unicode": "👸", - "name": "Princess" - }, - "🎂": { - "style": "unicode", - "image": "1f382.png", - "name": "Birthday Cake" - }, - "🦆": { - "style": "unicode", - "image": "1f986.png", - "name": "Duck" - }, - ":flag_yt:": { - "style": "github", - "image": "1f1fe-1f1f9.png", - "unicode": "🇾🇹", - "name": "Mayotte" - }, - ":information-desk-person-tone4:": { - "style": "github", - "image": "1f481-1f3fe.png", - "unicode": "💁🏾", - "name": "Information Desk Person - Tone 4" - }, - ":middle-finger-tone4:": { - "style": "github", - "image": "1f595-1f3fe.png", - "unicode": "🖕🏾", - "name": "Reversed Hand With Middle Finger Extended - Tone 4" - }, - ":scream:": { - "style": "github", - "image": "1f631.png", - "unicode": "😱", - "name": "Face Screaming In Fear" - }, - ":rotating_light:": { - "style": "github", - "image": "1f6a8.png", - "unicode": "🚨", - "name": "Police Cars Revolving Light" - }, - ":older-man-tone5:": { - "style": "github", - "image": "1f474-1f3ff.png", - "unicode": "👴🏿", - "name": "Older Man - Tone 5" - }, - "♏": { - "style": "unicode", - "image": "264f.png", - "name": "Scorpius" - }, - ":person_doing_cartwheel_tone5:": { - "style": "github", - "image": "1f938-1f3ff.png", - "unicode": "🤸🏿", - "name": "Person Doing Cartwheel - Tone 5" - }, - ":fork_and_knife_with_plate:": { - "style": "github", - "image": "1f37d.png", - "unicode": "🍽", - "name": "Fork And Knife With Plate" - }, - ":gift-heart:": { - "style": "github", - "image": "1f49d.png", - "unicode": "💝", - "name": "Heart With Ribbon" - }, - ":duck:": { - "style": "github", - "image": "1f986.png", - "unicode": "🦆", - "name": "Duck" - }, - ":mountain-bicyclist-tone1:": { - "style": "github", - "image": "1f6b5-1f3fb.png", - "unicode": "🚵🏻", - "name": "Mountain Bicyclist - Tone 1" - }, - ":flag-tv:": { - "style": "github", - "image": "1f1f9-1f1fb.png", - "unicode": "🇹🇻", - "name": "Tuvalu" - }, - ":regional_indicator_w:": { - "style": "github", - "image": "1f1fc.png", - "unicode": "🇼", - "name": "Regional Indicator Symbol Letter W" - }, - "🇭🇺": { - "style": "unicode", - "image": "1f1ed-1f1fa.png", - "name": "Hungary" - }, - ":flag_kw:": { - "style": "github", - "image": "1f1f0-1f1fc.png", - "unicode": "🇰🇼", - "name": "Kuwait" - }, - ":raised-hands-tone4:": { - "style": "github", - "image": "1f64c-1f3fe.png", - "unicode": "🙌🏾", - "name": "Person Raising Both Hands In Celebration - Tone 4" - }, - ":face-palm:": { - "style": "github", - "image": "1f926.png", - "unicode": "🤦", - "name": "Face Palm" - }, - ":th:": { - "style": "github", - "image": "1f1f9-1f1ed.png", - "unicode": "🇹🇭", - "name": "Thailand" - }, - ":point-left-tone2:": { - "style": "github", - "image": "1f448-1f3fc.png", - "unicode": "👈🏼", - "name": "White Left Pointing Backhand Index - Tone 2" - }, - "💙": { - "style": "unicode", - "image": "1f499.png", - "name": "Blue Heart" - }, - "🚵🏽": { - "style": "unicode", - "image": "1f6b5-1f3fd.png", - "name": "Mountain Bicyclist - Tone 3" - }, - "🚵🏼": { - "style": "unicode", - "image": "1f6b5-1f3fc.png", - "name": "Mountain Bicyclist - Tone 2" - }, - "🚵🏿": { - "style": "unicode", - "image": "1f6b5-1f3ff.png", - "name": "Mountain Bicyclist - Tone 5" - }, - "🚵🏾": { - "style": "unicode", - "image": "1f6b5-1f3fe.png", - "name": "Mountain Bicyclist - Tone 4" - }, - "🚵🏻": { - "style": "unicode", - "image": "1f6b5-1f3fb.png", - "name": "Mountain Bicyclist - Tone 1" - }, - ":bullettrain_front:": { - "style": "github", - "image": "1f685.png", - "unicode": "🚅", - "name": "High-speed Train With Bullet Nose" - }, - ":girl_tone1:": { - "style": "github", - "image": "1f467-1f3fb.png", - "unicode": "👧🏻", - "name": "Girl - Tone 1" - }, - "🐮": { - "style": "unicode", - "image": "1f42e.png", - "name": "Cow Face" - }, - ":beer:": { - "style": "github", - "image": "1f37a.png", - "unicode": "🍺", - "name": "Beer Mug" - }, - "🗃": { - "style": "unicode", - "image": "1f5c3.png", - "name": "Card File Box" - }, - ":kissing:": { - "style": "github", - "image": "1f617.png", - "unicode": "😗", - "name": "Kissing Face" - }, - "🕘": { - "style": "unicode", - "image": "1f558.png", - "name": "Clock Face Nine Oclock" - }, - ":li:": { - "style": "github", - "image": "1f1f1-1f1ee.png", - "unicode": "🇱🇮", - "name": "Liechtenstein" - }, - ":bg:": { - "style": "github", - "image": "1f1e7-1f1ec.png", - "unicode": "🇧🇬", - "name": "Bulgaria" - }, - ":nail-care-tone3:": { - "style": "github", - "image": "1f485-1f3fd.png", - "unicode": "💅🏽", - "name": "Nail Polish - Tone 3" - }, - "👼🏾": { - "style": "unicode", - "image": "1f47c-1f3fe.png", - "name": "Baby Angel - Tone 4" - }, - "👼🏿": { - "style": "unicode", - "image": "1f47c-1f3ff.png", - "name": "Baby Angel - Tone 5" - }, - "👼🏼": { - "style": "unicode", - "image": "1f47c-1f3fc.png", - "name": "Baby Angel - Tone 2" - }, - "👼🏽": { - "style": "unicode", - "image": "1f47c-1f3fd.png", - "name": "Baby Angel - Tone 3" - }, - ":runner-tone3:": { - "style": "github", - "image": "1f3c3-1f3fd.png", - "unicode": "🏃🏽", - "name": "Runner - Tone 3" - }, - "👼🏻": { - "style": "unicode", - "image": "1f47c-1f3fb.png", - "name": "Baby Angel - Tone 1" - }, - ":abcd:": { - "style": "github", - "image": "1f521.png", - "unicode": "🔡", - "name": "Input Symbol For Latin Small Letters" - }, - "🤛": { - "style": "unicode", - "image": "1f91b.png", - "name": "Left-facing Fist" - }, - ":metro:": { - "style": "github", - "image": "1f687.png", - "unicode": "🚇", - "name": "Metro" - }, - ":fist-tone4:": { - "style": "github", - "image": "270a-1f3fe.png", - "unicode": "✊🏾", - "name": "Raised Fist - Tone 4" - }, - ":flag-co:": { - "style": "github", - "image": "1f1e8-1f1f4.png", - "unicode": "🇨🇴", - "name": "Colombia" - }, - "🎬": { - "style": "unicode", - "image": "1f3ac.png", - "name": "Clapper Board" - }, - ":cloud_rain:": { - "style": "github", - "image": "1f327.png", - "unicode": "🌧", - "name": "Cloud With Rain" - }, - "🙅": { - "style": "unicode", - "image": "1f645.png", - "name": "Face With No Good Gesture" - }, - ":railway_track:": { - "style": "github", - "image": "1f6e4.png", - "unicode": "🛤", - "name": "Railway Track" - }, - ":hand-splayed-tone5:": { - "style": "github", - "image": "1f590-1f3ff.png", - "unicode": "🖐🏿", - "name": "Raised Hand With Fingers Splayed - Tone 5" - }, - ":flag_hr:": { - "style": "github", - "image": "1f1ed-1f1f7.png", - "unicode": "🇭🇷", - "name": "Croatia" - }, - ":pizza:": { - "style": "github", - "image": "1f355.png", - "unicode": "🍕", - "name": "Slice Of Pizza" - }, - ":flag_tn:": { - "style": "github", - "image": "1f1f9-1f1f3.png", - "unicode": "🇹🇳", - "name": "Tunisia" - }, - ":uz:": { - "style": "github", - "image": "1f1fa-1f1ff.png", - "unicode": "🇺🇿", - "name": "Uzbekistan" - }, - "🐄": { - "style": "unicode", - "image": "1f404.png", - "name": "Cow" - }, - ":v-tone5:": { - "style": "github", - "image": "270c-1f3ff.png", - "unicode": "✌🏿", - "name": "Victory Hand - Tone 5" - }, - ":flag-bg:": { - "style": "github", - "image": "1f1e7-1f1ec.png", - "unicode": "🇧🇬", - "name": "Bulgaria" - }, - ":horse-racing-tone5:": { - "style": "github", - "image": "1f3c7-1f3ff.png", - "unicode": "🏇🏿", - "name": "Horse Racing - Tone 5" - }, - ":gt:": { - "style": "github", - "image": "1f1ec-1f1f9.png", - "unicode": "🇬🇹", - "name": "Guatemala" - }, - "☸": { - "style": "unicode", - "image": "2638.png", - "name": "Wheel Of Dharma" - }, - ":non-potable_water:": { - "style": "github", - "image": "1f6b1.png", - "unicode": "🚱", - "name": "Non-potable Water Symbol" - }, - ":regional-indicator-r:": { - "style": "github", - "image": "1f1f7.png", - "unicode": "🇷", - "name": "Regional Indicator Symbol Letter R" - }, - ":crystal-ball:": { - "style": "github", - "image": "1f52e.png", - "unicode": "🔮", - "name": "Crystal Ball" - }, - ":juggler_tone3:": { - "style": "github", - "image": "1f939-1f3fd.png", - "unicode": "🤹🏽", - "name": "Juggling - Tone 3" - }, - ":flag-gs:": { - "style": "github", - "image": "1f1ec-1f1f8.png", - "unicode": "🇬🇸", - "name": "South Georgia" - }, - ":desert:": { - "style": "github", - "image": "1f3dc.png", - "unicode": "🏜", - "name": "Desert" - }, - ":waxing-gibbous-moon:": { - "style": "github", - "image": "1f314.png", - "unicode": "🌔", - "name": "Waxing Gibbous Moon Symbol" - }, - "🇭": { - "style": "unicode", - "image": "1f1ed.png", - "name": "Regional Indicator Symbol Letter H" - }, - ":funeral_urn:": { - "style": "github", - "image": "26b1.png", - "unicode": "⚱", - "name": "Funeral Urn" - }, - ":flag_sc:": { - "style": "github", - "image": "1f1f8-1f1e8.png", - "unicode": "🇸🇨", - "name": "The Seychelles" - }, - ":person_frowning_tone4:": { - "style": "github", - "image": "1f64d-1f3fe.png", - "unicode": "🙍🏾", - "name": "Person Frowning - Tone 4" - }, - ":dj:": { - "style": "github", - "image": "1f1e9-1f1ef.png", - "unicode": "🇩🇯", - "name": "Djibouti" - }, - ":no-entry:": { - "style": "github", - "image": "26d4.png", - "unicode": "⛔", - "name": "No Entry" - }, - ":handshake-tone4:": { - "style": "github", - "image": "1f91d-1f3fe.png", - "unicode": "🤝🏾", - "name": "Handshake - Tone 4" - }, - ":mz:": { - "style": "github", - "image": "1f1f2-1f1ff.png", - "unicode": "🇲🇿", - "name": "Mozambique" - }, - ":swimmer_tone5:": { - "style": "github", - "image": "1f3ca-1f3ff.png", - "unicode": "🏊🏿", - "name": "Swimmer - Tone 5" - }, - "%-)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - ":call-me-tone2:": { - "style": "github", - "image": "1f919-1f3fc.png", - "unicode": "🤙🏼", - "name": "Call Me Hand - Tone 2" - }, - "🍁": { - "style": "unicode", - "image": "1f341.png", - "name": "Maple Leaf" - }, - ":flag-pa:": { - "style": "github", - "image": "1f1f5-1f1e6.png", - "unicode": "🇵🇦", - "name": "Panama" - }, - "🏖": { - "style": "unicode", - "image": "1f3d6.png", - "name": "Beach With Umbrella" - }, - ":hotsprings:": { - "style": "github", - "image": "2668.png", - "unicode": "♨", - "name": "Hot Springs" - }, - ":flag_me:": { - "style": "github", - "image": "1f1f2-1f1ea.png", - "unicode": "🇲🇪", - "name": "Montenegro" - }, - "👯": { - "style": "unicode", - "image": "1f46f.png", - "name": "Woman With Bunny Ears" - }, - "2⃣": { - "style": "unicode", - "image": "0032-20e3.png", - "name": "Keycap Digit Two" - }, - ":sushi:": { - "style": "github", - "image": "1f363.png", - "unicode": "🍣", - "name": "Sushi" - }, - ":fist:": { - "style": "github", - "image": "270a.png", - "unicode": "✊", - "name": "Raised Fist" - }, - "🌀": { - "style": "unicode", - "image": "1f300.png", - "name": "Cyclone" - }, - ":earth-americas:": { - "style": "github", - "image": "1f30e.png", - "unicode": "🌎", - "name": "Earth Globe Americas" - }, - "🔄": { - "style": "unicode", - "image": "1f504.png", - "name": "Anticlockwise Downwards And Upwards Open Circle Arrows" - }, - "☎": { - "style": "unicode", - "image": "260e.png", - "name": "Black Telephone" - }, - ":loop:": { - "style": "github", - "image": "27bf.png", - "unicode": "➿", - "name": "Double Curly Loop" - }, - ":traffic-light:": { - "style": "github", - "image": "1f6a5.png", - "unicode": "🚥", - "name": "Horizontal Traffic Light" - }, - ":pill:": { - "style": "github", - "image": "1f48a.png", - "unicode": "💊", - "name": "Pill" - }, - "😮": { - "style": "unicode", - "ascii": ":-O", - "image": "1f62e.png", - "name": "Face With Open Mouth" - }, - ":ok_woman_tone5:": { - "style": "github", - "image": "1f646-1f3ff.png", - "unicode": "🙆🏿", - "name": "Face With Ok Gesture Tone5" - }, - ":closed_book:": { - "style": "github", - "image": "1f4d5.png", - "unicode": "📕", - "name": "Closed Book" - }, - ":radio_button:": { - "style": "github", - "image": "1f518.png", - "unicode": "🔘", - "name": "Radio Button" - }, - ":vulcan-tone2:": { - "style": "github", - "image": "1f596-1f3fc.png", - "unicode": "🖖🏼", - "name": "Raised Hand With Part Between Middle And Ring Fingers - Tone 2" - }, - ":repeat_one:": { - "style": "github", - "image": "1f502.png", - "unicode": "🔂", - "name": "Clockwise Rightwards And Leftwards Open Circle Arrows With Circled One Overlay" - }, - "🍀": { - "style": "unicode", - "image": "1f340.png", - "name": "Four Leaf Clover" - }, - ":blush:": { - "style": "github", - "image": "1f60a.png", - "unicode": "😊", - "name": "Smiling Face With Smiling Eyes" - }, - ":sc:": { - "style": "github", - "image": "1f1f8-1f1e8.png", - "unicode": "🇸🇨", - "name": "The Seychelles" - }, - ":tophat:": { - "style": "github", - "image": "1f3a9.png", - "unicode": "🎩", - "name": "Top Hat" - }, - "👅": { - "style": "unicode", - "image": "1f445.png", - "name": "Tongue" - }, - ":handshake-tone1:": { - "style": "github", - "image": "1f91d-1f3fb.png", - "unicode": "🤝🏻", - "name": "Handshake - Tone 1" - }, - ":confetti_ball:": { - "style": "github", - "image": "1f38a.png", - "unicode": "🎊", - "name": "Confetti Ball" - }, - ":flag-mz:": { - "style": "github", - "image": "1f1f2-1f1ff.png", - "unicode": "🇲🇿", - "name": "Mozambique" - }, - ":fingers_crossed_tone1:": { - "style": "github", - "image": "1f91e-1f3fb.png", - "unicode": "🤞🏻", - "name": "Hand With Index And Middle Fingers Crossed - Tone 1" - }, - "📚": { - "style": "unicode", - "image": "1f4da.png", - "name": "Books" - }, - ":older_man_tone2:": { - "style": "github", - "image": "1f474-1f3fc.png", - "unicode": "👴🏼", - "name": "Older Man - Tone 2" - }, - ":telephone-receiver:": { - "style": "github", - "image": "1f4de.png", - "unicode": "📞", - "name": "Telephone Receiver" - }, - "🍫": { - "style": "unicode", - "image": "1f36b.png", - "name": "Chocolate Bar" - }, - "🕯": { - "style": "unicode", - "image": "1f56f.png", - "name": "Candle" - }, - ":mouse:": { - "style": "github", - "image": "1f42d.png", - "unicode": "🐭", - "name": "Mouse Face" - }, - ":flag-tm:": { - "style": "github", - "image": "1f1f9-1f1f2.png", - "unicode": "🇹🇲", - "name": "Turkmenistan" - }, - "🐀": { - "style": "unicode", - "image": "1f400.png", - "name": "Rat" - }, - ":raising-hand-tone4:": { - "style": "github", - "image": "1f64b-1f3fe.png", - "unicode": "🙋🏾", - "name": "Happy Person Raising One Hand Tone4" - }, - "🆙": { - "style": "unicode", - "image": "1f199.png", - "name": "Squared Up With Exclamation Mark" - }, - ":crocodile:": { - "style": "github", - "image": "1f40a.png", - "unicode": "🐊", - "name": "Crocodile" - }, - ":heavy_dollar_sign:": { - "style": "github", - "image": "1f4b2.png", - "unicode": "💲", - "name": "Heavy Dollar Sign" - }, - ":champagne-glass:": { - "style": "github", - "image": "1f942.png", - "unicode": "🥂", - "name": "Clinking Glasses" - }, - ":wind-chime:": { - "style": "github", - "image": "1f390.png", - "unicode": "🎐", - "name": "Wind Chime" - }, - ":bicyclist:": { - "style": "github", - "image": "1f6b4.png", - "unicode": "🚴", - "name": "Bicyclist" - }, - ":left_facing_fist_tone1:": { - "style": "github", - "image": "1f91b-1f3fb.png", - "unicode": "🤛🏻", - "name": "Left Facing Fist - Tone 1" - }, - ":pancakes:": { - "style": "github", - "image": "1f95e.png", - "unicode": "🥞", - "name": "Pancakes" - }, - ":lock:": { - "style": "github", - "image": "1f512.png", - "unicode": "🔒", - "name": "Lock" - }, - ":mortar-board:": { - "style": "github", - "image": "1f393.png", - "unicode": "🎓", - "name": "Graduation Cap" - }, - ":flag-se:": { - "style": "github", - "image": "1f1f8-1f1ea.png", - "unicode": "🇸🇪", - "name": "Sweden" - }, - "⛷": { - "style": "unicode", - "image": "26f7.png", - "name": "Skier" - }, - ":skull_and_crossbones:": { - "style": "github", - "image": "2620.png", - "unicode": "☠", - "name": "Skull And Crossbones" - }, - ":face_palm_tone4:": { - "style": "github", - "image": "1f926-1f3fe.png", - "unicode": "🤦🏾", - "name": "Face Palm - Tone 4" - }, - ":rhinoceros:": { - "style": "github", - "image": "1f98f.png", - "unicode": "🦏", - "name": "Rhinoceros" - }, - "🚬": { - "style": "unicode", - "image": "1f6ac.png", - "name": "Smoking Symbol" - }, - "🎅🏽": { - "style": "unicode", - "image": "1f385-1f3fd.png", - "name": "Father Christmas - Tone 3" - }, - "🎅🏼": { - "style": "unicode", - "image": "1f385-1f3fc.png", - "name": "Father Christmas - Tone 2" - }, - "🎅🏿": { - "style": "unicode", - "image": "1f385-1f3ff.png", - "name": "Father Christmas - Tone 5" - }, - "🎅🏾": { - "style": "unicode", - "image": "1f385-1f3fe.png", - "name": "Father Christmas - Tone 4" - }, - ":flag_gi:": { - "style": "github", - "image": "1f1ec-1f1ee.png", - "unicode": "🇬🇮", - "name": "Gibraltar" - }, - "▶": { - "style": "unicode", - "image": "25b6.png", - "name": "Black Right-pointing Triangle" - }, - ":musical_note:": { - "style": "github", - "image": "1f3b5.png", - "unicode": "🎵", - "name": "Musical Note" - }, - "🍅": { - "style": "unicode", - "image": "1f345.png", - "name": "Tomato" - }, - "🥉": { - "style": "unicode", - "image": "1f949.png", - "name": "Third Place Medal" - }, - "🏚": { - "style": "unicode", - "image": "1f3da.png", - "name": "Derelict House Building" - }, - "👫": { - "style": "unicode", - "image": "1f46b.png", - "name": "Man And Woman Holding Hands" - }, - "🇫🇯": { - "style": "unicode", - "image": "1f1eb-1f1ef.png", - "name": "Fiji" - }, - "🇫🇮": { - "style": "unicode", - "image": "1f1eb-1f1ee.png", - "name": "Finland" - }, - "🇫🇲": { - "style": "unicode", - "image": "1f1eb-1f1f2.png", - "name": "Micronesia" - }, - "🇫🇰": { - "style": "unicode", - "image": "1f1eb-1f1f0.png", - "name": "Falkland Islands" - }, - "🇫🇷": { - "style": "unicode", - "image": "1f1eb-1f1f7.png", - "name": "France" - }, - ":flag_hm:": { - "style": "github", - "image": "1f1ed-1f1f2.png", - "unicode": "🇭🇲", - "name": "Heard Island And Mcdonald Islands" - }, - "🇫🇴": { - "style": "unicode", - "image": "1f1eb-1f1f4.png", - "name": "Faroe Islands" - }, - ":arrow_right:": { - "style": "github", - "image": "27a1.png", - "unicode": "➡", - "name": "Black Rightwards Arrow" - }, - ":flag-af:": { - "style": "github", - "image": "1f1e6-1f1eb.png", - "unicode": "🇦🇫", - "name": "Afghanistan" - }, - ":shrug-tone2:": { - "style": "github", - "image": "1f937-1f3fc.png", - "unicode": "🤷🏼", - "name": "Shrug - Tone 2" - }, - "🌄": { - "style": "unicode", - "image": "1f304.png", - "name": "Sunrise Over Mountains" - }, - ":fallen-leaf:": { - "style": "github", - "image": "1f342.png", - "unicode": "🍂", - "name": "Fallen Leaf" - }, - ":cross:": { - "style": "github", - "image": "271d.png", - "unicode": "✝", - "name": "Latin Cross" - }, - ":bride_with_veil:": { - "style": "github", - "image": "1f470.png", - "unicode": "👰", - "name": "Bride With Veil" - }, - ":sun-with-face:": { - "style": "github", - "image": "1f31e.png", - "unicode": "🌞", - "name": "Sun With Face" - }, - ":raised-back-of-hand-tone1:": { - "style": "github", - "image": "1f91a-1f3fb.png", - "unicode": "🤚🏻", - "name": "Raised Back Of Hand - Tone 1" - }, - ":telephone_receiver:": { - "style": "github", - "image": "1f4de.png", - "unicode": "📞", - "name": "Telephone Receiver" - }, - ":regional_indicator_u:": { - "style": "github", - "image": "1f1fa.png", - "unicode": "🇺", - "name": "Regional Indicator Symbol Letter U" - }, - ":family-mwgg:": { - "style": "github", - "image": "1f468-1f469-1f467-1f467.png", - "unicode": "👨👩👧👧", - "name": "Family (man,woman,girl,girl)" - }, - ":sneeze:": { - "style": "github", - "image": "1f927.png", - "unicode": "🤧", - "name": "Sneezing Face" - }, - ":cx:": { - "style": "github", - "image": "1f1e8-1f1fd.png", - "unicode": "🇨🇽", - "name": "Christmas Island" - }, - ":lock_with_ink_pen:": { - "style": "github", - "image": "1f50f.png", - "unicode": "🔏", - "name": "Lock With Ink Pen" - }, - ":mountain-bicyclist-tone3:": { - "style": "github", - "image": "1f6b5-1f3fd.png", - "unicode": "🚵🏽", - "name": "Mountain Bicyclist - Tone 3" - }, - ":ea:": { - "style": "github", - "image": "1f1ea-1f1e6.png", - "unicode": "🇪🇦", - "name": "Ceuta, Melilla" - }, - ":cow2:": { - "style": "github", - "image": "1f404.png", - "unicode": "🐄", - "name": "Cow" - }, - "🚂": { - "style": "unicode", - "image": "1f682.png", - "name": "Steam Locomotive" - }, - ":tf:": { - "style": "github", - "image": "1f1f9-1f1eb.png", - "unicode": "🇹🇫", - "name": "French Southern Territories" - }, - "🤹🏻": { - "style": "unicode", - "image": "1f939-1f3fb.png", - "name": "Juggling - Tone 1" - }, - "🤹🏽": { - "style": "unicode", - "image": "1f939-1f3fd.png", - "name": "Juggling - Tone 3" - }, - "🤹🏼": { - "style": "unicode", - "image": "1f939-1f3fc.png", - "name": "Juggling - Tone 2" - }, - "🤹🏿": { - "style": "unicode", - "image": "1f939-1f3ff.png", - "name": "Juggling - Tone 5" - }, - "🤹🏾": { - "style": "unicode", - "image": "1f939-1f3fe.png", - "name": "Juggling - Tone 4" - }, - "O;-)": { - "style": "ascii", - "ascii": "O:-)", - "image": "1f607.png", - "unicode": "😇", - "name": "Smiling Face With Halo" - }, - ":girl_tone3:": { - "style": "github", - "image": "1f467-1f3fd.png", - "unicode": "👧🏽", - "name": "Girl - Tone 3" - }, - ":man_in_tuxedo_tone4:": { - "style": "github", - "image": "1f935-1f3fe.png", - "unicode": "🤵🏾", - "name": "Man In Tuxedo - Tone 4" - }, - ":nail_care_tone5:": { - "style": "github", - "image": "1f485-1f3ff.png", - "unicode": "💅🏿", - "name": "Nail Polish - Tone 5" - }, - "👁": { - "style": "unicode", - "image": "1f441.png", - "name": "Eye" - }, - ":angel_tone5:": { - "style": "github", - "image": "1f47c-1f3ff.png", - "unicode": "👼🏿", - "name": "Baby Angel - Tone 5" - }, - ":mailbox-with-mail:": { - "style": "github", - "image": "1f4ec.png", - "unicode": "📬", - "name": "Open Mailbox With Raised Flag" - }, - ":flag_mk:": { - "style": "github", - "image": "1f1f2-1f1f0.png", - "unicode": "🇲🇰", - "name": "Macedonia" - }, - "📖": { - "style": "unicode", - "image": "1f4d6.png", - "name": "Open Book" - }, - "🌳": { - "style": "unicode", - "image": "1f333.png", - "name": "Deciduous Tree" - }, - ":flag-ki:": { - "style": "github", - "image": "1f1f0-1f1ee.png", - "unicode": "🇰🇮", - "name": "Kiribati" - }, - ";^)": { - "style": "ascii", - "ascii": ";)", - "image": "1f609.png", - "unicode": "😉", - "name": "Winking Face" - }, - ":microphone:": { - "style": "github", - "image": "1f3a4.png", - "unicode": "🎤", - "name": "Microphone" - }, - "🍯": { - "style": "unicode", - "image": "1f36f.png", - "name": "Honey Pot" - }, - ":ba:": { - "style": "github", - "image": "1f1e7-1f1e6.png", - "unicode": "🇧🇦", - "name": "Bosnia And Herzegovina" - }, - ":massage-tone1:": { - "style": "github", - "image": "1f486-1f3fb.png", - "unicode": "💆🏻", - "name": "Face Massage - Tone 1" - }, - ":birthday:": { - "style": "github", - "image": "1f382.png", - "unicode": "🎂", - "name": "Birthday Cake" - }, - ":flag_cd:": { - "style": "github", - "image": "1f1e8-1f1e9.png", - "unicode": "🇨🇩", - "name": "The Democratic Republic Of The Congo" - }, - ":nail-care-tone1:": { - "style": "github", - "image": "1f485-1f3fb.png", - "unicode": "💅🏻", - "name": "Nail Polish - Tone 1" - }, - "😀": { - "style": "unicode", - "image": "1f600.png", - "name": "Grinning Face" - }, - "🀄": { - "style": "unicode", - "image": "1f004.png", - "name": "Mahjong Tile Red Dragon" - }, - ":runner-tone1:": { - "style": "github", - "image": "1f3c3-1f3fb.png", - "unicode": "🏃🏻", - "name": "Runner - Tone 1" - }, - ":santa_tone5:": { - "style": "github", - "image": "1f385-1f3ff.png", - "unicode": "🎅🏿", - "name": "Father Christmas - Tone 5" - }, - "🎙": { - "style": "unicode", - "image": "1f399.png", - "name": "Studio Microphone" - }, - ":french-bread:": { - "style": "github", - "image": "1f956.png", - "unicode": "🥖", - "name": "Baguette Bread" - }, - ":fist-tone2:": { - "style": "github", - "image": "270a-1f3fc.png", - "unicode": "✊🏼", - "name": "Raised Fist - Tone 2" - }, - ":fencing:": { - "style": "github", - "image": "1f93a.png", - "unicode": "🤺", - "name": "Fencer" - }, - ":oden:": { - "style": "github", - "image": "1f362.png", - "unicode": "🍢", - "name": "Oden" - }, - ":flag-is:": { - "style": "github", - "image": "1f1ee-1f1f8.png", - "unicode": "🇮🇸", - "name": "Iceland" - }, - ":flag-ci:": { - "style": "github", - "image": "1f1e8-1f1ee.png", - "unicode": "🇨🇮", - "name": "Côte D’ivoire" - }, - ":tractor:": { - "style": "github", - "image": "1f69c.png", - "unicode": "🚜", - "name": "Tractor" - }, - "🌮": { - "style": "unicode", - "image": "1f32e.png", - "name": "Taco" - }, - ":page_facing_up:": { - "style": "github", - "image": "1f4c4.png", - "unicode": "📄", - "name": "Page Facing Up" - }, - ":thumbsdown-tone2:": { - "style": "github", - "image": "1f44e-1f3fc.png", - "unicode": "👎🏼", - "name": "Thumbs Down Sign - Tone 2" - }, - ":point_right_tone4:": { - "style": "github", - "image": "1f449-1f3fe.png", - "unicode": "👉🏾", - "name": "White Right Pointing Backhand Index - Tone 4" - }, - ":earth-asia:": { - "style": "github", - "image": "1f30f.png", - "unicode": "🌏", - "name": "Earth Globe Asia-australia" - }, - ":musical_keyboard:": { - "style": "github", - "image": "1f3b9.png", - "unicode": "🎹", - "name": "Musical Keyboard" - }, - ":mother_christmas:": { - "style": "github", - "image": "1f936.png", - "unicode": "🤶", - "name": "Mother Christmas" - }, - ":person_with_pouting_face:": { - "style": "github", - "image": "1f64e.png", - "unicode": "🙎", - "name": "Person With Pouting Face" - }, - ":flag_tl:": { - "style": "github", - "image": "1f1f9-1f1f1.png", - "unicode": "🇹🇱", - "name": "Timor-leste" - }, - ":shinto_shrine:": { - "style": "github", - "image": "26e9.png", - "unicode": "⛩", - "name": "Shinto Shrine" - }, - ":tropical_fish:": { - "style": "github", - "image": "1f420.png", - "unicode": "🐠", - "name": "Tropical Fish" - }, - ":camping:": { - "style": "github", - "image": "1f3d5.png", - "unicode": "🏕", - "name": "Camping" - }, - "🇺🇬": { - "style": "unicode", - "image": "1f1fa-1f1ec.png", - "name": "Uganda" - }, - ":no-good:": { - "style": "github", - "image": "1f645.png", - "unicode": "🙅", - "name": "Face With No Good Gesture" - }, - "🇺🇦": { - "style": "unicode", - "image": "1f1fa-1f1e6.png", - "name": "Ukraine" - }, - "🦊": { - "style": "unicode", - "image": "1f98a.png", - "name": "Fox Face" - }, - ":fi:": { - "style": "github", - "image": "1f1eb-1f1ee.png", - "unicode": "🇫🇮", - "name": "Finland" - }, - "🇺🇾": { - "style": "unicode", - "image": "1f1fa-1f1fe.png", - "name": "Uruguay" - }, - "🇺🇿": { - "style": "unicode", - "image": "1f1fa-1f1ff.png", - "name": "Uzbekistan" - }, - "🇺🇸": { - "style": "unicode", - "image": "1f1fa-1f1f8.png", - "name": "United States" - }, - "🐗": { - "style": "unicode", - "image": "1f417.png", - "name": "Boar" - }, - ":flag-be:": { - "style": "github", - "image": "1f1e7-1f1ea.png", - "unicode": "🇧🇪", - "name": "Belgium" - }, - "🇺🇲": { - "style": "unicode", - "image": "1f1fa-1f1f2.png", - "name": "United States Minor Outlying Islands" - }, - ":walking-tone4:": { - "style": "github", - "image": "1f6b6-1f3fe.png", - "unicode": "🚶🏾", - "name": "Pedestrian - Tone 4" - }, - "✡": { - "style": "unicode", - "image": "2721.png", - "name": "Star Of David" - }, - "💬": { - "style": "unicode", - "image": "1f4ac.png", - "name": "Speech Balloon" - }, - ":open-mouth:": { - "style": "github", - "ascii": ":-O", - "image": "1f62e.png", - "unicode": "😮", - "name": "Face With Open Mouth" - }, - ":person-frowning:": { - "style": "github", - "image": "1f64d.png", - "unicode": "🙍", - "name": "Person Frowning" - }, - "♋": { - "style": "unicode", - "image": "264b.png", - "name": "Cancer" - }, - ":smiley:": { - "style": "github", - "ascii": ":D", - "image": "1f603.png", - "unicode": "😃", - "name": "Smiling Face With Open Mouth" - }, - ":back_of_hand_tone3:": { - "style": "github", - "image": "1f91a-1f3fd.png", - "unicode": "🤚🏽", - "name": "Raised Back Of Hand - Tone 3" - }, - ":sheep:": { - "style": "github", - "image": "1f411.png", - "unicode": "🐑", - "name": "Sheep" - }, - ":flag-gu:": { - "style": "github", - "image": "1f1ec-1f1fa.png", - "unicode": "🇬🇺", - "name": "Guam" - }, - ":regional_indicator_y:": { - "style": "github", - "image": "1f1fe.png", - "unicode": "🇾", - "name": "Regional Indicator Symbol Letter Y" - }, - ":flag_sa:": { - "style": "github", - "image": "1f1f8-1f1e6.png", - "unicode": "🇸🇦", - "name": "Saudi Arabia" - }, - ":arrow-lower-right:": { - "style": "github", - "image": "2198.png", - "unicode": "↘", - "name": "South East Arrow" - }, - ":dash:": { - "style": "github", - "image": "1f4a8.png", - "unicode": "💨", - "name": "Dash Symbol" - }, - ":weight_lifter_tone4:": { - "style": "github", - "image": "1f3cb-1f3fe.png", - "unicode": "🏋🏾", - "name": "Weight Lifter - Tone 4" - }, - ":flag_eh:": { - "style": "github", - "image": "1f1ea-1f1ed.png", - "unicode": "🇪🇭", - "name": "Western Sahara" - }, - ":swimmer_tone3:": { - "style": "github", - "image": "1f3ca-1f3fd.png", - "unicode": "🏊🏽", - "name": "Swimmer - Tone 3" - }, - ":aerial_tramway:": { - "style": "github", - "image": "1f6a1.png", - "unicode": "🚡", - "name": "Aerial Tramway" - }, - ":bug:": { - "style": "github", - "image": "1f41b.png", - "unicode": "🐛", - "name": "Bug" - }, - ":cn:": { - "style": "github", - "image": "1f1e8-1f1f3.png", - "unicode": "🇨🇳", - "name": "China" - }, - ":thumbsup-tone1:": { - "style": "github", - "image": "1f44d-1f3fb.png", - "unicode": "👍🏻", - "name": "Thumbs Up Sign - Tone 1" - }, - ":handshake-tone2:": { - "style": "github", - "image": "1f91d-1f3fc.png", - "unicode": "🤝🏼", - "name": "Handshake - Tone 2" - }, - "🏃": { - "style": "unicode", - "image": "1f3c3.png", - "name": "Runner" - }, - ":bath:": { - "style": "github", - "image": "1f6c0.png", - "unicode": "🛀", - "name": "Bath" - }, - ":heavy_division_sign:": { - "style": "github", - "image": "2797.png", - "unicode": "➗", - "name": "Heavy Division Sign" - }, - "👍🏽": { - "style": "unicode", - "image": "1f44d-1f3fd.png", - "name": "Thumbs Up Sign - Tone 3" - }, - "🍘": { - "style": "unicode", - "image": "1f358.png", - "name": "Rice Cracker" - }, - "👍🏿": { - "style": "unicode", - "image": "1f44d-1f3ff.png", - "name": "Thumbs Up Sign - Tone 5" - }, - "👍🏾": { - "style": "unicode", - "image": "1f44d-1f3fe.png", - "name": "Thumbs Up Sign - Tone 4" - }, - ":kn:": { - "style": "github", - "image": "1f1f0-1f1f3.png", - "unicode": "🇰🇳", - "name": "Saint Kitts And Nevis" - }, - "👍🏻": { - "style": "unicode", - "image": "1f44d-1f3fb.png", - "name": "Thumbs Up Sign - Tone 1" - }, - ":saudi:": { - "style": "github", - "image": "1f1f8-1f1e6.png", - "unicode": "🇸🇦", - "name": "Saudi Arabia" - }, - ":email:": { - "style": "github", - "image": "1f4e7.png", - "unicode": "📧", - "name": "E-mail Symbol" - }, - "📭": { - "style": "unicode", - "image": "1f4ed.png", - "name": "Open Mailbox With Lowered Flag" - }, - ":umbrella_on_ground:": { - "style": "github", - "image": "26f1.png", - "unicode": "⛱", - "name": "Umbrella On Ground" - }, - ":spider:": { - "style": "github", - "image": "1f577.png", - "unicode": "🕷", - "name": "Spider" - }, - ":gp:": { - "style": "github", - "image": "1f1ec-1f1f5.png", - "unicode": "🇬🇵", - "name": "Guadeloupe" - }, - "💂": { - "style": "unicode", - "image": "1f482.png", - "name": "Guardsman" - }, - ":guardsman_tone1:": { - "style": "github", - "image": "1f482-1f3fb.png", - "unicode": "💂🏻", - "name": "Guardsman - Tone 1" - }, - ":arrow-down-small:": { - "style": "github", - "image": "1f53d.png", - "unicode": "🔽", - "name": "Down-pointing Small Red Triangle" - }, - "🔗": { - "style": "unicode", - "image": "1f517.png", - "name": "Link Symbol" - }, - ":city_sunset:": { - "style": "github", - "image": "1f307.png", - "unicode": "🌇", - "name": "Sunset Over Buildings" - }, - "😛": { - "style": "unicode", - "ascii": ":P", - "image": "1f61b.png", - "name": "Face With Stuck-out Tongue" - }, - ":v_tone1:": { - "style": "github", - "image": "270c-1f3fb.png", - "unicode": "✌🏻", - "name": "Victory Hand - Tone 1" - }, - ":postal-horn:": { - "style": "github", - "image": "1f4ef.png", - "unicode": "📯", - "name": "Postal Horn" - }, - ":pray-tone2:": { - "style": "github", - "image": "1f64f-1f3fc.png", - "unicode": "🙏🏼", - "name": "Person With Folded Hands - Tone 2" - }, - ":re:": { - "style": "github", - "image": "1f1f7-1f1ea.png", - "unicode": "🇷🇪", - "name": "Réunion" - }, - ":rice:": { - "style": "github", - "image": "1f35a.png", - "unicode": "🍚", - "name": "Cooked Rice" - }, - ":point-down-tone5:": { - "style": "github", - "image": "1f447-1f3ff.png", - "unicode": "👇🏿", - "name": "White Down Pointing Backhand Index - Tone 5" - }, - "🙁": { - "style": "unicode", - "image": "1f641.png", - "name": "Slightly Frowning Face" - }, - ":ok_woman_tone3:": { - "style": "github", - "image": "1f646-1f3fd.png", - "unicode": "🙆🏽", - "name": "Face With Ok Gesture Tone3" - }, - ":crayon:": { - "style": "github", - "image": "1f58d.png", - "unicode": "🖍", - "name": "Lower Left Crayon" - }, - ":ht:": { - "style": "github", - "image": "1f1ed-1f1f9.png", - "unicode": "🇭🇹", - "name": "Haiti" - }, - ":floppy_disk:": { - "style": "github", - "image": "1f4be.png", - "unicode": "💾", - "name": "Floppy Disk" - }, - ":flag_va:": { - "style": "github", - "image": "1f1fb-1f1e6.png", - "unicode": "🇻🇦", - "name": "The Vatican City" - }, - "🐈": { - "style": "unicode", - "image": "1f408.png", - "name": "Cat" - }, - ":corn:": { - "style": "github", - "image": "1f33d.png", - "unicode": "🌽", - "name": "Ear Of Maize" - }, - ":wave_tone1:": { - "style": "github", - "image": "1f44b-1f3fb.png", - "unicode": "👋🏻", - "name": "Waving Hand Sign - Tone 1" - }, - ":handball-tone2:": { - "style": "github", - "image": "1f93e-1f3fc.png", - "unicode": "🤾🏼", - "name": "Handball - Tone 2" - }, - ":shaved_ice:": { - "style": "github", - "image": "1f367.png", - "unicode": "🍧", - "name": "Shaved Ice" - }, - ":sa:": { - "style": "github", - "image": "1f202.png", - "unicode": "🈂", - "name": "Squared Katakana Sa" - }, - ":raised_hand_with_part_between_middle_and_ring_fingers:": { - "style": "github", - "image": "1f596.png", - "unicode": "🖖", - "name": "Raised Hand With Part Between Middle And Ring Fingers" - }, - ":tone4:": { - "style": "github", - "image": "1f3fe.png", - "unicode": "🏾", - "name": "Emoji Modifier Fitzpatrick Type-5" - }, - "〰": { - "style": "unicode", - "image": "3030.png", - "name": "Wavy Dash" - }, - "🌲": { - "style": "unicode", - "image": "1f332.png", - "name": "Evergreen Tree" - }, - "3⃣": { - "style": "unicode", - "image": "0033-20e3.png", - "name": "Keycap Digit Three" - }, - ":flag_ac:": { - "style": "github", - "image": "1f1e6-1f1e8.png", - "unicode": "🇦🇨", - "name": "Ascension" - }, - ":potato:": { - "style": "github", - "image": "1f954.png", - "unicode": "🥔", - "name": "Potato" - }, - ":arrow_upper_right:": { - "style": "github", - "image": "2197.png", - "unicode": "↗", - "name": "North East Arrow" - }, - ":mask:": { - "style": "github", - "image": "1f637.png", - "unicode": "😷", - "name": "Face With Medical Mask" - }, - ":flag_do:": { - "style": "github", - "image": "1f1e9-1f1f4.png", - "unicode": "🇩🇴", - "name": "The Dominican Republic" - }, - ":spy-tone1:": { - "style": "github", - "image": "1f575-1f3fb.png", - "unicode": "🕵🏻", - "name": "Sleuth Or Spy - Tone 1" - }, - ":facepalm_tone2:": { - "style": "github", - "image": "1f926-1f3fc.png", - "unicode": "🤦🏼", - "name": "Face Palm - Tone 2" - }, - ":older_man_tone4:": { - "style": "github", - "image": "1f474-1f3fe.png", - "unicode": "👴🏾", - "name": "Older Man - Tone 4" - }, - ":face-palm-tone4:": { - "style": "github", - "image": "1f926-1f3fe.png", - "unicode": "🤦🏾", - "name": "Face Palm - Tone 4" - }, - ":vi:": { - "style": "github", - "image": "1f1fb-1f1ee.png", - "unicode": "🇻🇮", - "name": "U.s. Virgin Islands" - }, - ":six-pointed-star:": { - "style": "github", - "image": "1f52f.png", - "unicode": "🔯", - "name": "Six Pointed Star With Middle Dot" - }, - ":flag-to:": { - "style": "github", - "image": "1f1f9-1f1f4.png", - "unicode": "🇹🇴", - "name": "Tonga" - }, - "🏇🏿": { - "style": "unicode", - "image": "1f3c7-1f3ff.png", - "name": "Horse Racing - Tone 5" - }, - "🏇🏾": { - "style": "unicode", - "image": "1f3c7-1f3fe.png", - "name": "Horse Racing - Tone 4" - }, - "🏇🏽": { - "style": "unicode", - "image": "1f3c7-1f3fd.png", - "name": "Horse Racing - Tone 3" - }, - "🏇🏼": { - "style": "unicode", - "image": "1f3c7-1f3fc.png", - "name": "Horse Racing - Tone 2" - }, - "🏇🏻": { - "style": "unicode", - "image": "1f3c7-1f3fb.png", - "name": "Horse Racing - Tone 1" - }, - "🇱": { - "style": "unicode", - "image": "1f1f1.png", - "name": "Regional Indicator Symbol Letter L" - }, - ":satellite:": { - "style": "github", - "image": "1f4e1.png", - "unicode": "📡", - "name": "Satellite Antenna" - }, - ":cloud-tornado:": { - "style": "github", - "image": "1f32a.png", - "unicode": "🌪", - "name": "Cloud With Tornado" - }, - "⚔": { - "style": "unicode", - "image": "2694.png", - "name": "Crossed Swords" - }, - "😟": { - "style": "unicode", - "image": "1f61f.png", - "name": "Worried Face" - }, - "🌚": { - "style": "unicode", - "image": "1f31a.png", - "name": "New Moon With Face" - }, - ":thermometer_face:": { - "style": "github", - "image": "1f912.png", - "unicode": "🤒", - "name": "Face With Thermometer" - }, - ":pregnant-woman-tone5:": { - "style": "github", - "image": "1f930-1f3ff.png", - "unicode": "🤰🏿", - "name": "Pregnant Woman - Tone 5" - }, - ":flag-uy:": { - "style": "github", - "image": "1f1fa-1f1fe.png", - "unicode": "🇺🇾", - "name": "Uruguay" - }, - ":hotdog:": { - "style": "github", - "image": "1f32d.png", - "unicode": "🌭", - "name": "Hot Dog" - }, - ":flag-mx:": { - "style": "github", - "image": "1f1f2-1f1fd.png", - "unicode": "🇲🇽", - "name": "Mexico" - }, - "🚴": { - "style": "unicode", - "image": "1f6b4.png", - "name": "Bicyclist" - }, - ":place_of_worship:": { - "style": "github", - "image": "1f6d0.png", - "unicode": "🛐", - "name": "Place Of Worship" - }, - "🥁": { - "style": "unicode", - "image": "1f941.png", - "name": "Drum With Drumsticks" - }, - "🕉": { - "style": "unicode", - "image": "1f549.png", - "name": "Om Symbol" - }, - ":heart_exclamation:": { - "style": "github", - "image": "2763.png", - "unicode": "❣", - "name": "Heavy Heart Exclamation Mark Ornament" - }, - ":flag-sg:": { - "style": "github", - "image": "1f1f8-1f1ec.png", - "unicode": "🇸🇬", - "name": "Singapore" - }, - ":kissing_smiling_eyes:": { - "style": "github", - "image": "1f619.png", - "unicode": "😙", - "name": "Kissing Face With Smiling Eyes" - }, - "🗞": { - "style": "unicode", - "image": "1f5de.png", - "name": "Rolled-up Newspaper" - }, - "👳": { - "style": "unicode", - "image": "1f473.png", - "name": "Man With Turban" - }, - ":walking_tone4:": { - "style": "github", - "image": "1f6b6-1f3fe.png", - "unicode": "🚶🏾", - "name": "Pedestrian - Tone 4" - }, - ">=)": { - "style": "ascii", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ":writing_hand_tone4:": { - "style": "github", - "image": "270d-1f3fe.png", - "unicode": "✍🏾", - "name": "Writing Hand - Tone 4" - }, - "🔈": { - "style": "unicode", - "image": "1f508.png", - "name": "Speaker" - }, - "🈲": { - "style": "unicode", - "image": "1f232.png", - "name": "Squared Cjk Unified Ideograph-7981" - }, - ":crossed_flags:": { - "style": "github", - "image": "1f38c.png", - "unicode": "🎌", - "name": "Crossed Flags" - }, - ":information_desk_person_tone5:": { - "style": "github", - "image": "1f481-1f3ff.png", - "unicode": "💁🏿", - "name": "Information Desk Person - Tone 5" - }, - ":flag_gg:": { - "style": "github", - "image": "1f1ec-1f1ec.png", - "unicode": "🇬🇬", - "name": "Guernsey" - }, - ":pig:": { - "style": "github", - "image": "1f437.png", - "unicode": "🐷", - "name": "Pig Face" - }, - "🏇": { - "style": "unicode", - "image": "1f3c7.png", - "name": "Horse Racing" - }, - ":motor-scooter:": { - "style": "github", - "image": "1f6f5.png", - "unicode": "🛵", - "name": "Motor Scooter" - }, - "🛀🏾": { - "style": "unicode", - "image": "1f6c0-1f3fe.png", - "name": "Bath - Tone 4" - }, - ":passport-control:": { - "style": "github", - "image": "1f6c2.png", - "unicode": "🛂", - "name": "Passport Control" - }, - ":jack-o-lantern:": { - "style": "github", - "image": "1f383.png", - "unicode": "🎃", - "name": "Jack-o-lantern" - }, - "🍜": { - "style": "unicode", - "image": "1f35c.png", - "name": "Steaming Bowl" - }, - ":bm:": { - "style": "github", - "image": "1f1e7-1f1f2.png", - "unicode": "🇧🇲", - "name": "Bermuda" - }, - ":flag-ad:": { - "style": "github", - "image": "1f1e6-1f1e9.png", - "unicode": "🇦🇩", - "name": "Andorra" - }, - ":thumbdown_tone4:": { - "style": "github", - "image": "1f44e-1f3fe.png", - "unicode": "👎🏾", - "name": "Thumbs Down Sign - Tone 4" - }, - ":flag-jp:": { - "style": "github", - "image": "1f1ef-1f1f5.png", - "unicode": "🇯🇵", - "name": "Japan" - }, - ":flag-dj:": { - "style": "github", - "image": "1f1e9-1f1ef.png", - "unicode": "🇩🇯", - "name": "Djibouti" - }, - ":grandma_tone4:": { - "style": "github", - "image": "1f475-1f3fe.png", - "unicode": "👵🏾", - "name": "Older Woman - Tone 4" - }, - ":alembic:": { - "style": "github", - "image": "2697.png", - "unicode": "⚗", - "name": "Alembic" - }, - "🛵": { - "style": "unicode", - "image": "1f6f5.png", - "name": "Motor Scooter" - }, - ":hearts:": { - "style": "github", - "image": "2665.png", - "unicode": "♥", - "name": "Black Heart Suit" - }, - ":hamster:": { - "style": "github", - "image": "1f439.png", - "unicode": "🐹", - "name": "Hamster Face" - }, - ":flag_hk:": { - "style": "github", - "image": "1f1ed-1f1f0.png", - "unicode": "🇭🇰", - "name": "Hong Kong" - }, - ":cooking:": { - "style": "github", - "image": "1f373.png", - "unicode": "🍳", - "name": "Cooking" - }, - ":shrug-tone4:": { - "style": "github", - "image": "1f937-1f3fe.png", - "unicode": "🤷🏾", - "name": "Shrug - Tone 4" - }, - ":prince:": { - "style": "github", - "image": "1f934.png", - "unicode": "🤴", - "name": "Prince" - }, - ":flag_iq:": { - "style": "github", - "image": "1f1ee-1f1f6.png", - "unicode": "🇮🇶", - "name": "Iraq" - }, - ":raised-back-of-hand-tone3:": { - "style": "github", - "image": "1f91a-1f3fd.png", - "unicode": "🤚🏽", - "name": "Raised Back Of Hand - Tone 3" - }, - ":white_sun_with_small_cloud:": { - "style": "github", - "image": "1f324.png", - "unicode": "🌤", - "name": "White Sun With Small Cloud" - }, - ":ear_of_rice:": { - "style": "github", - "image": "1f33e.png", - "unicode": "🌾", - "name": "Ear Of Rice" - }, - ":older-man-tone1:": { - "style": "github", - "image": "1f474-1f3fb.png", - "unicode": "👴🏻", - "name": "Older Man - Tone 1" - }, - ":lion-face:": { - "style": "github", - "image": "1f981.png", - "unicode": "🦁", - "name": "Lion Face" - }, - "👉": { - "style": "unicode", - "image": "1f449.png", - "name": "White Right Pointing Backhand Index" - }, - ":tongue:": { - "style": "github", - "image": "1f445.png", - "unicode": "👅", - "name": "Tongue" - }, - ":flag_io:": { - "style": "github", - "image": "1f1ee-1f1f4.png", - "unicode": "🇮🇴", - "name": "British Indian Ocean Territory" - }, - "📞": { - "style": "unicode", - "image": "1f4de.png", - "name": "Telephone Receiver" - }, - "🕳": { - "style": "unicode", - "image": "1f573.png", - "name": "Hole" - }, - ":flag_sz:": { - "style": "github", - "image": "1f1f8-1f1ff.png", - "unicode": "🇸🇿", - "name": "Swaziland" - }, - ":om:": { - "style": "github", - "image": "1f1f4-1f1f2.png", - "unicode": "🇴🇲", - "name": "Oman" - }, - ":ec:": { - "style": "github", - "image": "1f1ea-1f1e8.png", - "unicode": "🇪🇨", - "name": "Ecuador" - }, - ":arrow-down:": { - "style": "github", - "image": "2b07.png", - "unicode": "⬇", - "name": "Downwards Black Arrow" - }, - ":crescent-moon:": { - "style": "github", - "image": "1f319.png", - "unicode": "🌙", - "name": "Crescent Moon" - }, - "%)": { - "style": "ascii", - "ascii": "#-)", - "image": "1f635.png", - "unicode": "😵", - "name": "Dizzy Face" - }, - "😈": { - "style": "unicode", - "image": "1f608.png", - "name": "Smiling Face With Horns" - }, - ":shallow-pan-of-food:": { - "style": "github", - "image": "1f958.png", - "unicode": "🥘", - "name": "Shallow Pan Of Food" - }, - ":girl_tone5:": { - "style": "github", - "image": "1f467-1f3ff.png", - "unicode": "👧🏿", - "name": "Girl - Tone 5" - }, - ":small_red_triangle_down:": { - "style": "github", - "image": "1f53b.png", - "unicode": "🔻", - "name": "Down-pointing Red Triangle" - }, - ":flag_pn:": { - "style": "github", - "image": "1f1f5-1f1f3.png", - "unicode": "🇵🇳", - "name": "Pitcairn" - }, - ":no_mobile_phones:": { - "style": "github", - "image": "1f4f5.png", - "unicode": "📵", - "name": "No Mobile Phones" - }, - "‼": { - "style": "unicode", - "image": "203c.png", - "name": "Double Exclamation Mark" - }, - ":tomato:": { - "style": "github", - "image": "1f345.png", - "unicode": "🍅", - "name": "Tomato" - }, - "👂🏼": { - "style": "unicode", - "image": "1f442-1f3fc.png", - "name": "Ear - Tone 2" - }, - ":flag_mm:": { - "style": "github", - "image": "1f1f2-1f1f2.png", - "unicode": "🇲🇲", - "name": "Myanmar" - }, - ":family_mwgg:": { - "style": "github", - "image": "1f468-1f469-1f467-1f467.png", - "unicode": "👨👩👧👧", - "name": "Family (man,woman,girl,girl)" - }, - ":point_down:": { - "style": "github", - "image": "1f447.png", - "unicode": "👇", - "name": "White Down Pointing Backhand Index" - }, - ":flag_cz:": { - "style": "github", - "image": "1f1e8-1f1ff.png", - "unicode": "🇨🇿", - "name": "The Czech Republic" - }, - ":lu:": { - "style": "github", - "image": "1f1f1-1f1fa.png", - "unicode": "🇱🇺", - "name": "Luxembourg" - }, - ":water_buffalo:": { - "style": "github", - "image": "1f403.png", - "unicode": "🐃", - "name": "Water Buffalo" - }, - ":evergreen-tree:": { - "style": "github", - "image": "1f332.png", - "unicode": "🌲", - "name": "Evergreen Tree" - }, - ":card_box:": { - "style": "github", - "image": "1f5c3.png", - "unicode": "🗃", - "name": "Card File Box" - }, - ":massage-tone3:": { - "style": "github", - "image": "1f486-1f3fd.png", - "unicode": "💆🏽", - "name": "Face Massage - Tone 3" - }, - "👂🏻": { - "style": "unicode", - "image": "1f442-1f3fb.png", - "name": "Ear - Tone 1" - }, - ":horse_racing_tone4:": { - "style": "github", - "image": "1f3c7-1f3fe.png", - "unicode": "🏇🏾", - "name": "Horse Racing - Tone 4" - }, - "🦂": { - "style": "unicode", - "image": "1f982.png", - "name": "Scorpion" - }, - "🎆": { - "style": "unicode", - "image": "1f386.png", - "name": "Fireworks" - }, - ":princess_tone4:": { - "style": "github", - "image": "1f478-1f3fe.png", - "unicode": "👸🏾", - "name": "Princess - Tone 4" - }, - "🖊": { - "style": "unicode", - "image": "1f58a.png", - "name": "Lower Left Ballpoint Pen" - }, - ":wolf:": { - "style": "github", - "image": "1f43a.png", - "unicode": "🐺", - "name": "Wolf Face" - }, - ":flag-iq:": { - "style": "github", - "image": "1f1ee-1f1f6.png", - "unicode": "🇮🇶", - "name": "Iraq" - }, - ":flag-ck:": { - "style": "github", - "image": "1f1e8-1f1f0.png", - "unicode": "🇨🇰", - "name": "Cook Islands" - }, - ":man-tone1:": { - "style": "github", - "image": "1f468-1f3fb.png", - "unicode": "👨🏻", - "name": "Man - Tone 1" - }, - "🐟": { - "style": "unicode", - "image": "1f41f.png", - "name": "Fish" - }, - ":jm:": { - "style": "github", - "image": "1f1ef-1f1f2.png", - "unicode": "🇯🇲", - "name": "Jamaica" - }, - ":bride-with-veil:": { - "style": "github", - "image": "1f470.png", - "unicode": "👰", - "name": "Bride With Veil" - }, - "💴": { - "style": "unicode", - "image": "1f4b4.png", - "name": "Banknote With Yen Sign" - }, - "♓": { - "style": "unicode", - "image": "2653.png", - "name": "Pisces" - }, - ":rainbow_flag:": { - "style": "github", - "image": "1f3f3-1f308.png", - "unicode": "🏳🌈", - "name": "Gay_pride_flag" - }, - ":mans_shoe:": { - "style": "github", - "image": "1f45e.png", - "unicode": "👞", - "name": "Mans Shoe" - }, - ":left-right-arrow:": { - "style": "github", - "image": "2194.png", - "unicode": "↔", - "name": "Left Right Arrow" - }, - "👃🏻": { - "style": "unicode", - "image": "1f443-1f3fb.png", - "name": "Nose - Tone 1" - }, - "👃🏿": { - "style": "unicode", - "image": "1f443-1f3ff.png", - "name": "Nose - Tone 5" - }, - "👃🏾": { - "style": "unicode", - "image": "1f443-1f3fe.png", - "name": "Nose - Tone 4" - }, - "👃🏽": { - "style": "unicode", - "image": "1f443-1f3fd.png", - "name": "Nose - Tone 3" - }, - "👃🏼": { - "style": "unicode", - "image": "1f443-1f3fc.png", - "name": "Nose - Tone 2" - }, - ":bow:": { - "style": "github", - "image": "1f647.png", - "unicode": "🙇", - "name": "Person Bowing Deeply" - }, - ":flag-hm:": { - "style": "github", - "image": "1f1ed-1f1f2.png", - "unicode": "🇭🇲", - "name": "Heard Island And Mcdonald Islands" - }, - ":beach:": { - "style": "github", - "image": "1f3d6.png", - "unicode": "🏖", - "name": "Beach With Umbrella" - }, - "🇬🇩": { - "style": "unicode", - "image": "1f1ec-1f1e9.png", - "name": "Grenada" - }, - ":gh:": { - "style": "github", - "image": "1f1ec-1f1ed.png", - "unicode": "🇬🇭", - "name": "Ghana" - }, - "👱🏼": { - "style": "unicode", - "image": "1f471-1f3fc.png", - "name": "Person With Blond Hair - Tone 2" - }, - ":ear-tone2:": { - "style": "github", - "image": "1f442-1f3fc.png", - "unicode": "👂🏼", - "name": "Ear - Tone 2" - }, - "🇬🇵": { - "style": "unicode", - "image": "1f1ec-1f1f5.png", - "name": "Guadeloupe" - }, - ":flag-gw:": { - "style": "github", - "image": "1f1ec-1f1fc.png", - "unicode": "🇬🇼", - "name": "Guinea-bissau" - }, - ":racing_car:": { - "style": "github", - "image": "1f3ce.png", - "unicode": "🏎", - "name": "Racing Car" - }, - ":flag_sg:": { - "style": "github", - "image": "1f1f8-1f1ec.png", - "unicode": "🇸🇬", - "name": "Singapore" - }, - ":white_flower:": { - "style": "github", - "image": "1f4ae.png", - "unicode": "💮", - "name": "White Flower" - }, - ":dancer:": { - "style": "github", - "image": "1f483.png", - "unicode": "💃", - "name": "Dancer" - }, - "📵": { - "style": "unicode", - "image": "1f4f5.png", - "name": "No Mobile Phones" - }, - ":incoming-envelope:": { - "style": "github", - "image": "1f4e8.png", - "unicode": "📨", - "name": "Incoming Envelope" - }, - ":link:": { - "style": "github", - "image": "1f517.png", - "unicode": "🔗", - "name": "Link Symbol" - }, - ":flag_lr:": { - "style": "github", - "image": "1f1f1-1f1f7.png", - "unicode": "🇱🇷", - "name": "Liberia" - }, - ":leftwards-arrow-with-hook:": { - "style": "github", - "image": "21a9.png", - "unicode": "↩", - "name": "Leftwards Arrow With Hook" - }, - ":white_sun_rain_cloud:": { - "style": "github", - "image": "1f326.png", - "unicode": "🌦", - "name": "White Sun Behind Cloud With Rain" - }, - "💊": { - "style": "unicode", - "image": "1f48a.png", - "name": "Pill" - }, - ":raised_hands_tone1:": { - "style": "github", - "image": "1f64c-1f3fb.png", - "unicode": "🙌🏻", - "name": "Person Raising Both Hands In Celebration - Tone 1" - }, - "🤗": { - "style": "unicode", - "image": "1f917.png", - "name": "Hugging Face" - }, - "🌛": { - "style": "unicode", - "image": "1f31b.png", - "name": "First Quarter Moon With Face" - }, - ":white-small-square:": { - "style": "github", - "image": "25ab.png", - "unicode": "▫", - "name": "White Small Square" - }, - "🔟": { - "style": "unicode", - "image": "1f51f.png", - "name": "Keycap Ten" - }, - ":cl:": { - "style": "github", - "image": "1f191.png", - "unicode": "🆑", - "name": "Squared Cl" - }, - ":satisfied:": { - "style": "github", - "ascii": ">:)", - "image": "1f606.png", - "unicode": "😆", - "name": "Smiling Face With Open Mouth And Tightly-closed Eyes" - }, - ":heavy-division-sign:": { - "style": "github", - "image": "2797.png", - "unicode": "➗", - "name": "Heavy Division Sign" - }, - ":back_of_hand_tone1:": { - "style": "github", - "image": "1f91a-1f3fb.png", - "unicode": "🤚🏻", - "name": "Raised Back Of Hand - Tone 1" - }, - "🎰": { - "style": "unicode", - "image": "1f3b0.png", - "name": "Slot Machine" - }, - ":thumbsup-tone3:": { - "style": "github", - "image": "1f44d-1f3fd.png", - "unicode": "👍🏽", - "name": "Thumbs Up Sign - Tone 3" - }, - "⚾": { - "style": "unicode", - "image": "26be.png", - "name": "Baseball" - }, - ":womans_hat:": { - "style": "github", - "image": "1f452.png", - "unicode": "👒", - "name": "Womans Hat" - }, - "🙉": { - "style": "unicode", - "image": "1f649.png", - "name": "Hear-no-evil Monkey" - }, - "❓": { - "style": "unicode", - "image": "2753.png", - "name": "Black Question Mark Ornament" - }, - ":dark-sunglasses:": { - "style": "github", - "image": "1f576.png", - "unicode": "🕶", - "name": "Dark Sunglasses" - }, - ":medal:": { - "style": "github", - "image": "1f3c5.png", - "unicode": "🏅", - "name": "Sports Medal" - }, - ":raising-hand:": { - "style": "github", - "image": "1f64b.png", - "unicode": "🙋", - "name": "Happy Person Raising One Hand" - }, - ":jp:": { - "style": "github", - "image": "1f1ef-1f1f5.png", - "unicode": "🇯🇵", - "name": "Japan" - }, - ":guardsman_tone3:": { - "style": "github", - "image": "1f482-1f3fd.png", - "unicode": "💂🏽", - "name": "Guardsman - Tone 3" - }, - "✒": { - "style": "unicode", - "image": "2712.png", - "name": "Black Nib" - }, - ":deciduous-tree:": { - "style": "github", - "image": "1f333.png", - "unicode": "🌳", - "name": "Deciduous Tree" - }, - ":v_tone3:": { - "style": "github", - "image": "270c-1f3fd.png", - "unicode": "✌🏽", - "name": "Victory Hand - Tone 3" - }, - ":thumbup_tone4:": { - "style": "github", - "image": "1f44d-1f3fe.png", - "unicode": "👍🏾", - "name": "Thumbs Up Sign - Tone 4" - }, - ":flag-pm:": { - "style": "github", - "image": "1f1f5-1f1f2.png", - "unicode": "🇵🇲", - "name": "Saint Pierre And Miquelon" - }, - ":performing_arts:": { - "style": "github", - "image": "1f3ad.png", - "unicode": "🎭", - "name": "Performing Arts" - }, - ":swimmer-tone5:": { - "style": "github", - "image": "1f3ca-1f3ff.png", - "unicode": "🏊🏿", - "name": "Swimmer - Tone 5" - }, - ":beach_with_umbrella:": { - "style": "github", - "image": "1f3d6.png", - "unicode": "🏖", - "name": "Beach With Umbrella" - }, - ":ok_woman_tone1:": { - "style": "github", - "image": "1f646-1f3fb.png", - "unicode": "🙆🏻", - "name": "Face With Ok Gesture Tone1" - }, - ":man_dancing_tone5:": { - "style": "github", - "image": "1f57a-1f3ff.png", - "unicode": "🕺🏿", - "name": "Man Dancing - Tone 5" - }, - ":kissing_heart:": { - "style": "github", - "ascii": ":*", - "image": "1f618.png", - "unicode": "😘", - "name": "Face Throwing A Kiss" - }, - ":cartwheel_tone5:": { - "style": "github", - "image": "1f938-1f3ff.png", - "unicode": "🤸🏿", - "name": "Person Doing Cartwheel - Tone 5" - }, - "🏵": { - "style": "unicode", - "image": "1f3f5.png", - "name": "Rosette" - }, - ":flag_vc:": { - "style": "github", - "image": "1f1fb-1f1e8.png", - "unicode": "🇻🇨", - "name": "Saint Vincent And The Grenadines" - }, - "🎊": { - "style": "unicode", - "image": "1f38a.png", - "name": "Confetti Ball" - }, - ":so:": { - "style": "github", - "image": "1f1f8-1f1f4.png", - "unicode": "🇸🇴", - "name": "Somalia" - }, - ":octopus:": { - "style": "github", - "image": "1f419.png", - "unicode": "🐙", - "name": "Octopus" - }, - "🐛": { - "style": "unicode", - "image": "1f41b.png", - "name": "Bug" - }, - ":fishing_pole_and_fish:": { - "style": "github", - "image": "1f3a3.png", - "unicode": "🎣", - "name": "Fishing Pole And Fish" - }, - ":rowboat-tone3:": { - "style": "github", - "image": "1f6a3-1f3fd.png", - "unicode": "🚣🏽", - "name": "Rowboat - Tone 3" - }, - ":fingers-crossed-tone4:": { - "style": "github", - "image": "1f91e-1f3fe.png", - "unicode": "🤞🏾", - "name": "Hand With Index And Middle Fingers Crossed - Tone 4" - }, - ":tone2:": { - "style": "github", - "image": "1f3fc.png", - "unicode": "🏼", - "name": "Emoji Modifier Fitzpatrick Type-3" - }, - ":love-hotel:": { - "style": "github", - "image": "1f3e9.png", - "unicode": "🏩", - "name": "Love Hotel" - }, - ":spy_tone2:": { - "style": "github", - "image": "1f575-1f3fc.png", - "unicode": "🕵🏼", - "name": "Sleuth Or Spy - Tone 2" - }, - "💰": { - "style": "unicode", - "image": "1f4b0.png", - "name": "Money Bag" - }, - ":flag_in:": { - "style": "github", - "image": "1f1ee-1f1f3.png", - "unicode": "🇮🇳", - "name": "India" - }, - ":last_quarter_moon_with_face:": { - "style": "github", - "image": "1f31c.png", - "unicode": "🌜", - "name": "Last Quarter Moon With Face" - }, - ":violin:": { - "style": "github", - "image": "1f3bb.png", - "unicode": "🎻", - "name": "Violin" - }, - ":flag_am:": { - "style": "github", - "image": "1f1e6-1f1f2.png", - "unicode": "🇦🇲", - "name": "Armenia" - }, - ":star2:": { - "style": "github", - "image": "1f31f.png", - "unicode": "🌟", - "name": "Glowing Star" - }, - ":race_car:": { - "style": "github", - "image": "1f3ce.png", - "unicode": "🏎", - "name": "Racing Car" - }, - ":middle_finger_tone5:": { - "style": "github", - "image": "1f595-1f3ff.png", - "unicode": "🖕🏿", - "name": "Reversed Hand With Middle Finger Extended - Tone 5" - }, - ":flag-ta:": { - "style": "github", - "image": "1f1f9-1f1e6.png", - "unicode": "🇹🇦", - "name": "Tristan Da Cunha" - }, - ":green_heart:": { - "style": "github", - "image": "1f49a.png", - "unicode": "💚", - "name": "Green Heart" - }, - ":ant:": { - "style": "github", - "image": "1f41c.png", - "unicode": "🐜", - "name": "Ant" - }, - ":wave_tone3:": { - "style": "github", - "image": "1f44b-1f3fd.png", - "unicode": "👋🏽", - "name": "Waving Hand Sign - Tone 3" - }, - ":writing-hand-tone4:": { - "style": "github", - "image": "270d-1f3fe.png", - "unicode": "✍🏾", - "name": "Writing Hand - Tone 4" - }, - ":frog:": { - "style": "github", - "image": "1f438.png", - "unicode": "🐸", - "name": "Frog Face" - }, - "🚝": { - "style": "unicode", - "image": "1f69d.png", - "name": "Monorail" - }, - ":pregnant-woman-tone3:": { - "style": "github", - "image": "1f930-1f3fd.png", - "unicode": "🤰🏽", - "name": "Pregnant Woman - Tone 3" - }, - ":blue_book:": { - "style": "github", - "image": "1f4d8.png", - "unicode": "📘", - "name": "Blue Book" - }, - ":facepalm_tone4:": { - "style": "github", - "image": "1f926-1f3fe.png", - "unicode": "🤦🏾", - "name": "Face Palm - Tone 4" - }, - ":flag_dm:": { - "style": "github", - "image": "1f1e9-1f1f2.png", - "unicode": "🇩🇲", - "name": "Dominica" - }, - "😲": { - "style": "unicode", - "image": "1f632.png", - "name": "Astonished Face" - }, - ":nl:": { - "style": "github", - "image": "1f1f3-1f1f1.png", - "unicode": "🇳🇱", - "name": "The Netherlands" - }, - ":flag-mv:": { - "style": "github", - "image": "1f1f2-1f1fb.png", - "unicode": "🇲🇻", - "name": "Maldives" - }, - ":flag_kp:": { - "style": "github", - "image": "1f1f0-1f1f5.png", - "unicode": "🇰🇵", - "name": "North Korea" - }, - ":wc:": { - "style": "github", - "image": "1f6be.png", - "unicode": "🚾", - "name": "Water Closet" - }, - ":flag-sa:": { - "style": "github", - "image": "1f1f8-1f1e6.png", - "unicode": "🇸🇦", - "name": "Saudi Arabia" - }, - "🥘": { - "style": "unicode", - "image": "1f958.png", - "name": "Shallow Pan Of Food" - }, - ":sick:": { - "style": "github", - "image": "1f922.png", - "unicode": "🤢", - "name": "Nauseated Face" - }, - "📱": { - "style": "unicode", - "image": "1f4f1.png", - "name": "Mobile Phone" - }, - "🇭🇷": { - "style": "unicode", - "image": "1f1ed-1f1f7.png", - "name": "Croatia" - }, - "🇭🇰": { - "style": "unicode", - "image": "1f1ed-1f1f0.png", - "name": "Hong Kong" - }, - "🇭🇳": { - "style": "unicode", - "image": "1f1ed-1f1f3.png", - "name": "Honduras" - }, - "🇭🇲": { - "style": "unicode", - "image": "1f1ed-1f1f2.png", - "name": "Heard Island And Mcdonald Islands" - }, - ":speaking-head:": { - "style": "github", - "image": "1f5e3.png", - "unicode": "🗣", - "name": "Speaking Head In Silhouette" - }, - "◻": { - "style": "unicode", - "image": "25fb.png", - "name": "White Medium Square" - }, - "🇭🇹": { - "style": "unicode", - "image": "1f1ed-1f1f9.png", - "name": "Haiti" - }, - ":flag-kp:": { - "style": "github", - "image": "1f1f0-1f1f5.png", - "unicode": "🇰🇵", - "name": "North Korea" - }, - ":writing_hand_tone2:": { - "style": "github", - "image": "270d-1f3fc.png", - "unicode": "✍🏼", - "name": "Writing Hand - Tone 2" - }, - "💆": { - "style": "unicode", - "image": "1f486.png", - "name": "Face Massage" - }, - ":bed:": { - "style": "github", - "image": "1f6cf.png", - "unicode": "🛏", - "name": "Bed" - }, - ":champagne:": { - "style": "github", - "image": "1f37e.png", - "unicode": "🍾", - "name": "Bottle With Popping Cork" - }, - "🔛": { - "style": "unicode", - "image": "1f51b.png", - "name": "On With Exclamation Mark With Left Right Arrow Abo" - }, - ":oncoming_automobile:": { - "style": "github", - "image": "1f698.png", - "unicode": "🚘", - "name": "Oncoming Automobile" - }, - "🌟": { - "style": "unicode", - "image": "1f31f.png", - "name": "Glowing Star" - }, - ":flag_ge:": { - "style": "github", - "image": "1f1ec-1f1ea.png", - "unicode": "🇬🇪", - "name": "Georgia" - }, - ":expecting_woman_tone2:": { - "style": "github", - "image": "1f930-1f3fc.png", - "unicode": "🤰🏼", - "name": "Pregnant Woman - Tone 2" - }, - "🎴": { - "style": "unicode", - "image": "1f3b4.png", - "name": "Flower Playing Cards" - }, - ":pf:": { - "style": "github", - "image": "1f1f5-1f1eb.png", - "unicode": "🇵🇫", - "name": "French Polynesia" - }, - ":triumph:": { - "style": "github", - "image": "1f624.png", - "unicode": "😤", - "name": "Face With Look Of Triumph" - }, - ":thumbdown_tone2:": { - "style": "github", - "image": "1f44e-1f3fc.png", - "unicode": "👎🏼", - "name": "Thumbs Down Sign - Tone 2" - }, - ":construction_worker:": { - "style": "github", - "image": "1f477.png", - "unicode": "👷", - "name": "Construction Worker" - }, - ":grandma_tone2:": { - "style": "github", - "image": "1f475-1f3fc.png", - "unicode": "👵🏼", - "name": "Older Woman - Tone 2" - }, - ":shrug:": { - "style": "github", - "image": "1f937.png", - "unicode": "🤷", - "name": "Shrug" - }, - ":waving_black_flag:": { - "style": "github", - "image": "1f3f4.png", - "unicode": "🏴", - "name": "Waving Black Flag" - }, - ":wave-tone4:": { - "style": "github", - "image": "1f44b-1f3fe.png", - "unicode": "👋🏾", - "name": "Waving Hand Sign - Tone 4" - }, - ":heavy_minus_sign:": { - "style": "github", - "image": "2796.png", - "unicode": "➖", - "name": "Heavy Minus Sign" - }, - ":desktop:": { - "style": "github", - "image": "1f5a5.png", - "unicode": "🖥", - "name": "Desktop Computer" - }, - ":juggling_tone5:": { - "style": "github", - "image": "1f939-1f3ff.png", - "unicode": "🤹🏿", - "name": "Juggling - Tone 5" - }, - ":no-smoking:": { - "style": "github", - "image": "1f6ad.png", - "unicode": "🚭", - "name": "No Smoking Symbol" - }, - ":black_square_button:": { - "style": "github", - "image": "1f532.png", - "unicode": "🔲", - "name": "Black Square Button" - }, - ":closed_lock_with_key:": { - "style": "github", - "image": "1f510.png", - "unicode": "🔐", - "name": "Closed Lock With Key" - }, - ":sweat_smile:": { - "style": "github", - "ascii": "':)", - "image": "1f605.png", - "unicode": "😅", - "name": "Smiling Face With Open Mouth And Cold Sweat" - }, - ":clock1030:": { - "style": "github", - "image": "1f565.png", - "unicode": "🕥", - "name": "Clock Face Ten-thirty" - }, - ":clock7:": { - "style": "github", - "image": "1f556.png", - "unicode": "🕖", - "name": "Clock Face Seven Oclock" - }, - ":flag_is:": { - "style": "github", - "image": "1f1ee-1f1f8.png", - "unicode": "🇮🇸", - "name": "Iceland" - }, - "🔲": { - "style": "unicode", - "image": "1f532.png", - "name": "Black Square Button" - }, - ":regional-indicator-a:": { - "style": "github", - "image": "1f1e6.png", - "unicode": "🇦", - "name": "Regional Indicator Symbol Letter A" - }, - ":prince-tone3:": { - "style": "github", - "image": "1f934-1f3fd.png", - "unicode": "🤴🏽", - "name": "Prince - Tone 3" - }, - "📇": { - "style": "unicode", - "image": "1f4c7.png", - "name": "Card Index" - }, - ":cartwheel-tone3:": { - "style": "github", - "image": "1f938-1f3fd.png", - "unicode": "🤸🏽", - "name": "Person Doing Cartwheel - Tone 3" - }, - ":raised-hands-tone2:": { - "style": "github", - "image": "1f64c-1f3fc.png", - "unicode": "🙌🏼", - "name": "Person Raising Both Hands In Celebration - Tone 2" - }, - ":flag_ky:": { - "style": "github", - "image": "1f1f0-1f1fe.png", - "unicode": "🇰🇾", - "name": "Cayman Islands" - }, - ":point-left:": { - "style": "github", - "image": "1f448.png", - "unicode": "👈", - "name": "White Left Pointing Backhand Index" - }, - ":card_file_box:": { - "style": "github", - "image": "1f5c3.png", - "unicode": "🗃", - "name": "Card File Box" - }, - "👜": { - "style": "unicode", - "image": "1f45c.png", - "name": "Handbag" - }, - ":older-man-tone3:": { - "style": "github", - "image": "1f474-1f3fd.png", - "unicode": "👴🏽", - "name": "Older Man - Tone 3" - }, - ":nerd:": { - "style": "github", - "image": "1f913.png", - "unicode": "🤓", - "name": "Nerd Face" - }, - ":flags:": { - "style": "github", - "image": "1f38f.png", - "unicode": "🎏", - "name": "Carp Streamer" - }, - ":ee:": { - "style": "github", - "image": "1f1ea-1f1ea.png", - "unicode": "🇪🇪", - "name": "Estonia" - }, - "🇵": { - "style": "unicode", - "image": "1f1f5.png", - "name": "Regional Indicator Symbol Letter P" - }, - ":flag_sx:": { - "style": "github", - "image": "1f1f8-1f1fd.png", - "unicode": "🇸🇽", - "name": "Sint Maarten" - }, - ":sneezing_face:": { - "style": "github", - "image": "1f927.png", - "unicode": "🤧", - "name": "Sneezing Face" - }, - "🖕🏽": { - "style": "unicode", - "image": "1f595-1f3fd.png", - "name": "Reversed Hand With Middle Finger Extended - Tone 3" - }, - "🖕🏼": { - "style": "unicode", - "image": "1f595-1f3fc.png", - "name": "Reversed Hand With Middle Finger Extended - Tone 2" - }, - "🖕🏿": { - "style": "unicode", - "image": "1f595-1f3ff.png", - "name": "Reversed Hand With Middle Finger Extended - Tone 5" - }, - "🖕🏾": { - "style": "unicode", - "image": "1f595-1f3fe.png", - "name": "Reversed Hand With Middle Finger Extended - Tone 4" - }, - "🖕🏻": { - "style": "unicode", - "image": "1f595-1f3fb.png", - "name": "Reversed Hand With Middle Finger Extended - Tone 1" - }, - ":woman-tone2:": { - "style": "github", - "image": "1f469-1f3fc.png", - "unicode": "👩🏼", - "name": "Woman - Tone 2" - }, - ":peach:": { - "style": "github", - "image": "1f351.png", - "unicode": "🍑", - "name": "Peach" - }, - ":eyeglasses:": { - "style": "github", - "image": "1f453.png", - "unicode": "👓", - "name": "Eyeglasses" - }, - ":flag_pl:": { - "style": "github", - "image": "1f1f5-1f1f1.png", - "unicode": "🇵🇱", - "name": "Poland" - }, - "🚰": { - "style": "unicode", - "image": "1f6b0.png", - "name": "Potable Water Symbol" - }, - "🤘🏾": { - "style": "unicode", - "image": "1f918-1f3fe.png", - "name": "Sign Of The Horns - Tone 4" - }, - "🤘🏿": { - "style": "unicode", - "image": "1f918-1f3ff.png", - "name": "Sign Of The Horns - Tone 5" - }, - "🤘🏼": { - "style": "unicode", - "image": "1f918-1f3fc.png", - "name": "Sign Of The Horns - Tone 2" - }, - "🤘🏽": { - "style": "unicode", - "image": "1f918-1f3fd.png", - "name": "Sign Of The Horns - Tone 3" - }, - ":chart-with-downwards-trend:": { - "style": "github", - "image": "1f4c9.png", - "unicode": "📉", - "name": "Chart With Downwards Trend" - }, - "🥅": { - "style": "unicode", - "image": "1f945.png", - "name": "Goal Net" - }, - "⛹🏼": { - "style": "unicode", - "image": "26f9-1f3fc.png", - "name": "Person With Ball - Tone 2" - }, - ":angel_tone1:": { - "style": "github", - "image": "1f47c-1f3fb.png", - "unicode": "👼🏻", - "name": "Baby Angel - Tone 1" - }, - ":nail-care-tone5:": { - "style": "github", - "image": "1f485-1f3ff.png", - "unicode": "💅🏿", - "name": "Nail Polish - Tone 5" - }, - ":rosette:": { - "style": "github", - "image": "1f3f5.png", - "unicode": "🏵", - "name": "Rosette" - }, - ":flag_mo:": { - "style": "github", - "image": "1f1f2-1f1f4.png", - "unicode": "🇲🇴", - "name": "Macau" - }, - "🏞": { - "style": "unicode", - "image": "1f3de.png", - "name": "National Park" - }, - ":ls:": { - "style": "github", - "image": "1f1f1-1f1f8.png", - "unicode": "🇱🇸", - "name": "Lesotho" - }, - ":shamrock:": { - "style": "github", - "image": "2618.png", - "unicode": "☘", - "name": "Shamrock" - }, - ":flag_cx:": { - "style": "github", - "image": "1f1e8-1f1fd.png", - "unicode": "🇨🇽", - "name": "Christmas Island" - }, - "🌈": { - "style": "unicode", - "image": "1f308.png", - "name": "Rainbow" - }, - ":pear:": { - "style": "github", - "image": "1f350.png", - "unicode": "🍐", - "name": "Pear" - }, - ":santa_tone1:": { - "style": "github", - "image": "1f385-1f3fb.png", - "unicode": "🎅🏻", - "name": "Father Christmas - Tone 1" - }, - ":shrug_tone4:": { - "style": "github", - "image": "1f937-1f3fe.png", - "unicode": "🤷🏾", - "name": "Shrug - Tone 4" - }, - "💝": { - "style": "unicode", - "image": "1f49d.png", - "name": "Heart With Ribbon" - }, - ":construction-worker-tone3:": { - "style": "github", - "image": "1f477-1f3fd.png", - "unicode": "👷🏽", - "name": "Construction Worker - Tone 3" - }, - ":european-post-office:": { - "style": "github", - "image": "1f3e4.png", - "unicode": "🏤", - "name": "European Post Office" - }, - "🇬🇦": { - "style": "unicode", - "image": "1f1ec-1f1e6.png", - "name": "Gabon" - }, - "🇬🇧": { - "style": "unicode", - "image": "1f1ec-1f1e7.png", - "name": "Great Britain" - }, - "🇬🇮": { - "style": "unicode", - "image": "1f1ec-1f1ee.png", - "name": "Gibraltar" - }, - "🇬🇬": { - "style": "unicode", - "image": "1f1ec-1f1ec.png", - "name": "Guernsey" - }, - "🇬🇭": { - "style": "unicode", - "image": "1f1ec-1f1ed.png", - "name": "Ghana" - }, - "🇬🇪": { - "style": "unicode", - "image": "1f1ec-1f1ea.png", - "name": "Georgia" - }, - "🇬🇫": { - "style": "unicode", - "image": "1f1ec-1f1eb.png", - "name": "French Guiana" - }, - ":baby-tone2:": { - "style": "github", - "image": "1f476-1f3fc.png", - "unicode": "👶🏼", - "name": "Baby - Tone 2" - }, - "🇬🇶": { - "style": "unicode", - "image": "1f1ec-1f1f6.png", - "name": "Equatorial Guinea" - }, - "🇬🇷": { - "style": "unicode", - "image": "1f1ec-1f1f7.png", - "name": "Greece" - }, - "🐲": { - "style": "unicode", - "image": "1f432.png", - "name": "Dragon Face" - }, - "🇬🇲": { - "style": "unicode", - "image": "1f1ec-1f1f2.png", - "name": "The Gambia" - }, - "🇬🇳": { - "style": "unicode", - "image": "1f1ec-1f1f3.png", - "name": "Guinea" - }, - "🇬🇱": { - "style": "unicode", - "image": "1f1ec-1f1f1.png", - "name": "Greenland" - }, - "🇬🇾": { - "style": "unicode", - "image": "1f1ec-1f1fe.png", - "name": "Guyana" - }, - "🇬🇼": { - "style": "unicode", - "image": "1f1ec-1f1fc.png", - "name": "Guinea-bissau" - }, - "🇬🇺": { - "style": "unicode", - "image": "1f1ec-1f1fa.png", - "name": "Guam" - }, - "🇬🇸": { - "style": "unicode", - "image": "1f1ec-1f1f8.png", - "name": "South Georgia" - }, - "🇬🇹": { - "style": "unicode", - "image": "1f1ec-1f1f9.png", - "name": "Guatemala" - }, - "🖐🏻": { - "style": "unicode", - "image": "1f590-1f3fb.png", - "name": "Raised Hand With Fingers Splayed - Tone 1" - }, - ":hand_with_index_and_middle_finger_crossed:": { - "style": "github", - "image": "1f91e.png", - "unicode": "🤞", - "name": "Hand With First And Index Finger Crossed" - }, - "🖐🏾": { - "style": "unicode", - "image": "1f590-1f3fe.png", - "name": "Raised Hand With Fingers Splayed - Tone 4" - }, - "🖐🏿": { - "style": "unicode", - "image": "1f590-1f3ff.png", - "name": "Raised Hand With Fingers Splayed - Tone 5" - }, - "🖐🏼": { - "style": "unicode", - "image": "1f590-1f3fc.png", - "name": "Raised Hand With Fingers Splayed - Tone 2" - }, - "🖐🏽": { - "style": "unicode", - "image": "1f590-1f3fd.png", - "name": "Raised Hand With Fingers Splayed - Tone 3" - }, - "⛑": { - "style": "unicode", - "image": "26d1.png", - "name": "Helmet With White Cross" - }, - ":flag_bz:": { - "style": "github", - "image": "1f1e7-1f1ff.png", - "unicode": "🇧🇿", - "name": "Belize" - }, - "🕜": { - "style": "unicode", - "image": "1f55c.png", - "name": "Clock Face One-thirty" - }, - ":person-with-pouting-face-tone2:": { - "style": "github", - "image": "1f64e-1f3fc.png", - "unicode": "🙎🏼", - "name": "Person With Pouting Face Tone2" - }, - ":passport_control:": { - "style": "github", - "image": "1f6c2.png", - "unicode": "🛂", - "name": "Passport Control" - }, - "♦": { - "style": "unicode", - "image": "2666.png", - "name": "Black Diamond Suit" - }, - ":call_me_hand_tone2:": { - "style": "github", - "image": "1f919-1f3fc.png", - "unicode": "🤙🏼", - "name": "Call Me Hand - Tone 2" - }, - ":large-orange-diamond:": { - "style": "github", - "image": "1f536.png", - "unicode": "🔶", - "name": "Large Orange Diamond" - }, - ":boy:": { - "style": "github", - "image": "1f466.png", - "unicode": "👦", - "name": "Boy" - }, - ":family_wwbb:": { - "style": "github", - "image": "1f469-1f469-1f466-1f466.png", - "unicode": "👩👩👦👦", - "name": "Family (woman,woman,boy,boy)" - }, - "4⃣": { - "style": "unicode", - "image": "0034-20e3.png", - "name": "Keycap Digit Four" - }, - ":runner-tone5:": { - "style": "github", - "image": "1f3c3-1f3ff.png", - "unicode": "🏃🏿", - "name": "Runner - Tone 5" - }, - ":handball:": { - "style": "github", - "image": "1f93e.png", - "unicode": "🤾", - "name": "Handball" - }, - "🚆": { - "style": "unicode", - "image": "1f686.png", - "name": "Train" - }, - ":flag-ba:": { - "style": "github", - "image": "1f1e7-1f1e6.png", - "unicode": "🇧🇦", - "name": "Bosnia And Herzegovina" - }, - ":martial_arts_uniform:": { - "style": "github", - "image": "1f94b.png", - "unicode": "🥋", - "name": "Martial Arts Uniform" - }, - "↔": { - "style": "unicode", - "image": "2194.png", - "name": "Left Right Arrow" - }, - ":gn:": { - "style": "github", - "image": "1f1ec-1f1f3.png", - "unicode": "🇬🇳", - "name": "Guinea" - }, - ":bw:": { - "style": "github", - "image": "1f1e7-1f1fc.png", - "unicode": "🇧🇼", - "name": "Botswana" - }, - ":flag-gy:": { - "style": "github", - "image": "1f1ec-1f1fe.png", - "unicode": "🇬🇾", - "name": "Guyana" - }, - ":black-joker:": { - "style": "github", - "image": "1f0cf.png", - "unicode": "🃏", - "name": "Playing Card Black Joker" - }, - ":kimono:": { - "style": "github", - "image": "1f458.png", - "unicode": "👘", - "name": "Kimono" - }, - ":flag_se:": { - "style": "github", - "image": "1f1f8-1f1ea.png", - "unicode": "🇸🇪", - "name": "Sweden" - }, - "🍳": { - "style": "unicode", - "image": "1f373.png", - "name": "Cooking" - } -} \ No newline at end of file diff --git a/elpa/emojify-20160928.550/emojify-autoloads.el b/elpa/emojify-20160928.550/emojify-autoloads.el deleted file mode 100644 index bd0c949..0000000 --- a/elpa/emojify-20160928.550/emojify-autoloads.el +++ /dev/null @@ -1,68 +0,0 @@ -;;; emojify-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "emojify" "emojify.el" (22533 17536 588433 -;;;;;; 374000)) -;;; Generated autoloads from emojify.el - -(autoload 'emojify-set-emoji-styles "emojify" "\ -Set the type of emojis that should be displayed. - -STYLES is the styles emoji styles that should be used, see `emojify-emoji-styles' - -\(fn STYLES)" nil nil) - -(autoload 'emojify-mode "emojify" "\ -Emojify mode - -\(fn &optional ARG)" t nil) - -(defvar global-emojify-mode nil "\ -Non-nil if Global Emojify mode is enabled. -See the `global-emojify-mode' command -for a description of this minor mode. -Setting this variable directly does not take effect; -either customize it (see the info node `Easy Customization') -or call the function `global-emojify-mode'.") - -(custom-autoload 'global-emojify-mode "emojify" nil) - -(autoload 'global-emojify-mode "emojify" "\ -Toggle Emojify mode in all buffers. -With prefix ARG, enable Global Emojify mode if ARG is positive; -otherwise, disable it. If called from Lisp, enable the mode if -ARG is omitted or nil. - -Emojify mode is enabled in all buffers where -`emojify-mode' would do it. -See `emojify-mode' for more information on Emojify mode. - -\(fn &optional ARG)" t nil) - -(autoload 'emojify-apropos-emoji "emojify" "\ -Show Emojis that match PATTERN. - -\(fn PATTERN)" t nil) - -(autoload 'emojify-insert-emoji "emojify" "\ -Interactively prompt for Emojis and insert them in the current buffer. - -This respects the `emojify-emoji-styles' variable. - -\(fn)" t nil) - -;;;*** - -;;;### (autoloads nil nil ("emojify-pkg.el") (22533 17536 554432 -;;;;;; 598000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; emojify-autoloads.el ends here diff --git a/elpa/emojify-20160928.550/emojify-pkg.el b/elpa/emojify-20160928.550/emojify-pkg.el deleted file mode 100644 index 1b93bfe..0000000 --- a/elpa/emojify-20160928.550/emojify-pkg.el +++ /dev/null @@ -1,9 +0,0 @@ -(define-package "emojify" "20160928.550" "Display emojis in Emacs" - '((seq "1.11") - (ht "2.0") - (emacs "24.3")) - :url "https://github.com/iqbalansari/emacs-emojify" :keywords - '("multimedia" "convenience")) -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/emojify-20160928.550/emojify.el b/elpa/emojify-20160928.550/emojify.el deleted file mode 100644 index 4e93d7f..0000000 --- a/elpa/emojify-20160928.550/emojify.el +++ /dev/null @@ -1,1571 +0,0 @@ -;;; emojify.el --- Display emojis in Emacs -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 Iqbal Ansari - -;; Author: Iqbal Ansari -;; Keywords: multimedia, convenience -;; URL: https://github.com/iqbalansari/emacs-emojify -;; Version: 0.4 -;; Package-Requires: ((seq "1.11") (ht "2.0") (emacs "24.3")) - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This package displays emojis in Emacs similar to how Github, Slack etc do. It -;; can display plain ascii like ':)' as well as Github style emojis like ':smile:' -;; -;; It provides a minor mode `emojify-mode' to enable display of emojis in a buffer. -;; To enable emojify mode globally use `global-emojify-mode' -;; -;; For detailed documentation see the projects README file at -;; https://github.com/iqbalansari/emacs-emojify - - - -;;; Code: - -(require 'seq) -(require 'ht) - -(require 'subr-x nil :no-error) -(require 'json) -(require 'regexp-opt) -(require 'jit-lock) -(require 'pcase) -(require 'tar-mode) -(require 'apropos) - - - -;; Satisfying the byte-compiler -;; We do not "require" these functions but if `org-mode' is active we use them - -;; Required to determine point is in an org-list -(declare-function org-at-item-p "org-list") -(declare-function org-at-heading-p "org") - -;; Required to determine point is in an org-src block -(declare-function org-element-type "org-element") -(declare-function org-element-at-point "org-element") - -;; Shouldn't require 'jit-lock be enough :/ -(defvar jit-lock-start) -(defvar jit-lock-end) - -;; Used while inserting emojis using helm -(defvar helm-buffer) -(defvar helm-after-initialize-hook) - - - -;; Compatibility functions - -(defun emojify-default-font-height () - "Return the height in pixels of the current buffer's default face font. - -`default-font-height' seems to be available only on Emacs versions after 24.3. -This provides a compatibility version for previous versions." - (if (fboundp 'default-font-height) - (default-font-height) - (let ((default-font (face-font 'default))) - (cond - ((and (display-multi-font-p) - ;; Avoid calling font-info if the frame's default font was - ;; not changed since the frame was created. That's because - ;; font-info is expensive for some fonts, see bug #14838. - (not (string= (frame-parameter nil 'font) default-font))) - (aref (font-info default-font) 3)) - (t (frame-char-height)))))) - -(defun emojify-overlays-at (pos &optional sorted) - "Return a list of the overlays that contain the character at POS. -If SORTED is non-nil, then sort them by decreasing priority. - -The SORTED argument was introduced in Emacs 24.4, along with the incompatible -change that overlay priorities can be any Lisp object (earlier they were -restricted to integer and nil). This version uses the SORTED argument of -`overlays-at' on Emacs version 24.4 onwards and manually sorts the overlays by -priority on lower versions." - (if (version< emacs-version "24.4") - (let ((overlays-at-pos (overlays-at pos))) - (if sorted - (seq-sort (lambda (overlay1 overlay2) - (if (and (overlay-get overlay2 'priority) - (overlay-get overlay1 'priority)) - ;; If both overlays have priorities compare them - (< (overlay-get overlay1 'priority) - (overlay-get overlay2 'priority)) - ;; Otherwise overlay with nil priority is sorted below - ;; the one with integer value otherwise preserve order - (not (overlay-get overlay1 'priority)))) - overlays-at-pos) - overlays-at-pos)) - (overlays-at pos sorted))) - -(defun emojify--string-join (strings &optional separator) - "Join all STRINGS using SEPARATOR. - -This function is available on Emacs v24.4 and higher, it has been -backported here for compatibility with older Emacsen." - (if (fboundp 'string-join) - (apply #'string-join (list strings separator)) - (mapconcat 'identity strings separator))) - - - -;; Debugging helpers - -(define-minor-mode emojify-debug-mode - "Enable debugging for emojify-mode. - -By default emojify silences any errors during emoji redisplay. This is done -since emojis are redisplayed using jit-lock (the same mechanism used for -font-lock) as such any bugs in the code can cause other important things to -fail. This also turns on jit-debug-mode so that (e)debugging emojify's redisplay -functions work." - :init-value nil - (if emojify-debug-mode - (when (fboundp 'jit-lock-debug-mode) - (jit-lock-debug-mode +1)) - (when (fboundp 'jit-lock-debug-mode) - (jit-lock-debug-mode -1)))) - -(defmacro emojify-execute-ignoring-errors-unless-debug (&rest forms) - "Execute FORMS ignoring errors unless `emojify-debug-mode' is non-nil." - (declare (debug t) (indent 0)) - `(if emojify-debug-mode - (progn - ,@forms) - (ignore-errors - ,@forms))) - - - -;; Utility functions - -(defmacro emojify-with-saved-buffer-state (&rest forms) - "Execute FORMS saving current buffer state. - -This saves point and mark, `match-data' and buffer modification state it also -inhibits buffer change, point motion hooks." - (declare (debug t) (indent 0)) - `(let ((inhibit-point-motion-hooks t)) - (with-silent-modifications - (save-match-data - (save-excursion - (save-restriction - (widen) - ,@forms)))))) - -(defmacro emojify-do-for-emojis-in-region (beg end &rest forms) - "For all emojis between BEG and END, execute the given FORMS. - -During the execution `emoji-start' and `emoji-end' are bound to the start -and end of the emoji for which the form is being executed." - (declare (debug t) (indent 2)) - `(let ((--emojify-loop-current-pos ,beg) - (--emojify-loop-end ,end) - emoji-start) - (while (and (> --emojify-loop-end --emojify-loop-current-pos) - (setq emoji-start (text-property-any --emojify-loop-current-pos --emojify-loop-end 'emojified t))) - (let ((emoji-end (+ emoji-start - (length (get-text-property emoji-start 'emojify-text))))) - ,@forms - (setq --emojify-loop-current-pos emoji-end))))) - -(defun emojify-message (format-string &rest args) - "Log debugging messages to buffer named 'emojify-log'. - -This is a substitute to `message' since using it during redisplay causes errors. -FORMAT-STRING and ARGS are same as the arguments to `message'." - (when emojify-debug-mode - (emojify-with-saved-buffer-state - (with-current-buffer (get-buffer-create "emojify-log") - (goto-char (point-max)) - (insert (apply #'format format-string args)) - (insert "\n"))))) - -(defun emojify--get-relevant-region () - "Try getting region in buffer that completely covers the current window. - -This is used instead of directly using `window-start' and `window-end', since -they return the values corresponding buffer in currently selected window, which -is incorrect if the buffer where there are called is not actually the buffer -visible in the selected window." - (let* ((window-size (- (window-end) (window-start))) - (start (max (- (point) window-size) (point-min))) - (end (min (+ (point) window-size) (point-max)))) - (cons start end))) - - - -;; Customizations for control how emojis are displayed - -(defgroup emojify nil - "Customization options for emojify" - :group 'display - :prefix "emojify-") - -(defcustom emojify-emoji-json - (expand-file-name "data/emoji.json" - (cond (load-file-name (file-name-directory load-file-name)) - ((locate-library "emojify") (file-name-directory (locate-library "emojify"))) - (t default-directory))) - "The path to JSON file containing the configuration for displaying emojis." - :type 'file - :group 'emojify) - -(defvar emojify-emoji-set-json - (let ((json-array-type 'list) - (json-object-type 'hash-table)) - (json-read-file (expand-file-name "data/emoji-sets.json" - (cond (load-file-name (file-name-directory load-file-name)) - ((locate-library "emojify") (file-name-directory (locate-library "emojify"))) - (t default-directory)))))) - -(defcustom emojify-emoji-set "emojione-v2.2.6-22" - "The emoji set used to display emojis." - :type (append '(radio :tag "Emoji set") - (mapcar (lambda (set) (list 'const set)) - (ht-keys emojify-emoji-set-json))) - :group 'emojify) - -(defcustom emojify-emojis-dir - (locate-user-emacs-file "emojis") - "Path to the directory containing the emoji images." - :type 'directory - :group 'emojify) - -(defcustom emojify-display-style - 'image - "How the emoji's be displayed. - -Possible values are -`image' - Display emojis using images, this requires images are supported by - user's Emacs installation -`unicode' - Display emojis using unicode characters, this works well on - platforms with good emoji fonts. In this case the emoji text - ':wink:' will be substituted with 😉. -`ascii' - Display emojis as ascii characters, this is simplest and does not - require any external dependencies. In this cases emoji text like - ':wink:' are substituted with ascii equivalents like ';)'" - :type '(radio :tag "Emoji display style" - (const :tag "Display emojis as images" image) - (const :tag "Display emojis as unicode characters" unicode) - (const :tag "Display emojis as ascii string" ascii)) - :group 'emojify) - - - -;; Customizations to control the enabling of emojify-mode - -(defcustom emojify-inhibit-major-modes - '(dired-mode - doc-view-mode - debugger-mode - pdf-view-mode - image-mode - help-mode - ibuffer-mode - magit-popup-mode - magit-diff-mode - ert-results-mode - compilation-mode - proced-mode - mu4e-headers-mode) - "Major modes where emojify mode should not be enabled." - :type '(repeat symbol) - :group 'emojify) - -(defcustom emojify-inhibit-in-buffer-functions - '(emojify-minibuffer-p emojify-helm-buffer-p) - "Functions used inhibit emojify-mode in a buffer. - -These functions are called with one argument, the buffer where emojify-mode -is about to be enabled, emojify is not enabled if any of the functions return -a non-nil value." - :type 'hook - :group 'emojify) - -(defvar emojify-inhibit-emojify-in-current-buffer-p nil - "Should emojify be inhibited in current buffer. - -This is a buffer local variable that can be set to inhibit enabling of -emojify in a buffer.") -(make-variable-buffer-local 'emojify-inhibit-emojify-in-current-buffer-p) - -(defvar emojify-in-insertion-command-p nil - "Are we currently executing emojify apropos command?") - -(defun emojify-ephemeral-buffer-p (buffer) - "Determine if BUFFER is an ephemeral/temporary buffer." - (and (not (minibufferp)) - (string-match-p "^ " (buffer-name buffer)))) - -(defun emojify-inhibit-major-mode-p (buffer) - "Determine if user has disabled the `major-mode' enabled for the BUFFER. - -Returns non-nil if the buffer's major mode is part of `emojify-inhibit-major-modes'" - (with-current-buffer buffer - (apply #'derived-mode-p emojify-inhibit-major-modes))) - -(defun emojify-helm-buffer-p (buffer) - "Determine if the current BUFFER is a helm buffer." - (unless emojify-in-insertion-command-p - (string-match-p "\\*helm" (buffer-name buffer)))) - -(defun emojify-minibuffer-p (buffer) - "Determine if the current BUFFER is a minibuffer." - (unless emojify-in-insertion-command-p - (minibufferp buffer))) - -(defun emojify-buffer-p (buffer) - "Determine if `emojify-mode' should be enabled for given BUFFER. - -`emojify-mode' mode is not enabled in temporary buffers. Additionally user -can customize `emojify-inhibit-major-modes' and -`emojify-inhibit-in-buffer-functions' to disabled emojify in additional buffers." - (not (or emojify-inhibit-emojify-in-current-buffer-p - (emojify-ephemeral-buffer-p (current-buffer)) - (emojify-inhibit-major-mode-p (current-buffer)) - (buffer-base-buffer buffer) - (run-hook-with-args-until-success 'emojify-inhibit-in-buffer-functions buffer)))) - - - -;; Customizations to control display of emojis - -(defvar emojify-emoji-style-change-hook nil - "Hooks run when emoji style changes.") - -;;;###autoload -(defun emojify-set-emoji-styles (styles) - "Set the type of emojis that should be displayed. - -STYLES is the styles emoji styles that should be used, see `emojify-emoji-styles'" - (when (not (listp styles)) - (setq styles (list styles)) - (warn "`emojify-emoji-style' has been deprecated use `emojify-emoji-styles' instead!")) - - (setq-default emojify-emoji-styles styles) - - (run-hooks 'emojify-emoji-style-change-hook)) - -(defcustom emojify-emoji-styles - '(ascii unicode github) - "The type of emojis that should be displayed. - -These can have one of the following values - -`ascii' - Display only ascii emojis for example ';)' -`unicode' - Display only unicode emojis for example '😉' -`github' - Display only github style emojis for example ':wink:'" - :type '(set - (const :tag "Display only ascii emojis" ascii) - (const :tag "Display only github emojis" github) - (const :tag "Display only unicode codepoints" unicode)) - :set (lambda (_ value) (emojify-set-emoji-styles value)) - :group 'emojify) - -(defcustom emojify-program-contexts - '(comments string code) - "Contexts where emojis can be displayed in programming modes. - -Possible values are -`comments' - Display emojis in comments -`string' - Display emojis in strings -`code' - Display emojis in code (this is applicable only for unicode emojis)" - :type '(set :tag "Contexts where emojis should be displayed in programming modes" - (const :tag "Display emojis in comments" comments) - (const :tag "Display emojis in string" string) - (const :tag "Display emojis in code" code)) - :group 'emojify) - -(defcustom emojify-inhibit-functions - '(emojify-in-org-tags-p emojify-in-org-list-p) - "Functions used to determine given emoji should displayed at current point. - -These functions are called with 3 arguments, the text to be emojified, the start -of emoji text and the end of emoji text. These functions are called with the -buffer where emojis are going to be displayed selected." - :type 'hook - :group 'emojify) - -(defcustom emojify-composed-text-p t - "Should composed text be emojified." - :type 'boolean - :group 'emojify) - -(defun emojify-in-org-tags-p (match _beg _end) - "Determine whether the point is on `org-mode' tag. - -MATCH, BEG and END are the text currently matched emoji and the start position -and end position of emoji text respectively. - -Easiest would have to inspect face at point but unfortunately, there is no -way to guarantee that we run after font-lock" - (and (memq major-mode '(org-mode org-agenda-mode)) - (string-match-p ":.*:" match) - (org-at-heading-p) - (not (save-excursion - (save-match-data - (search-forward-regexp "\\s-" (line-end-position) t)))))) - -(defun emojify-in-org-list-p (&rest ignored) - "Determine whether the point is in `org-mode' list. - -The arguments IGNORED are, well ignored" - (and (eq major-mode 'org-mode) - (org-at-item-p))) - -(defun emojify-valid-program-context-p (emoji beg end) - "Determine if emoji should be displayed for text between BEG and END. - -This returns non-nil if the region is valid according to `emojify-program-contexts'" - (when emojify-program-contexts - (let* ((syntax-beg (syntax-ppss beg)) - (syntax-end (syntax-ppss end)) - (context (cond ((and (nth 3 syntax-beg) - (nth 3 syntax-end)) 'string) - ((and (nth 4 syntax-beg) - (nth 4 syntax-end)) 'comments) - (t 'code)))) - (and (memql context emojify-program-contexts) - (if (equal context 'code) - (and (string= (ht-get emoji "style") "unicode") - (memql 'unicode emojify-emoji-styles)) - t))))) - -(defun emojify-inside-org-src-p (point) - "Return non-nil if POINT is inside `org-mode' src block. - -This is used to inhibit display of emoji's in `org-mode' src blocks -since our mechanisms do not work in it." - (when (eq major-mode 'org-mode) - (save-excursion - (goto-char point) - (eq (org-element-type (org-element-at-point)) 'src-block)))) - -(defun emojify-looking-at-end-of-list-maybe (point) - "Determine if POINT is end of a list. - -This is not accurate since it restricts the region to scan to -the visible area." - (let* ((area (emojify--get-relevant-region)) - (beg (car area)) - (end (cdr area))) - (save-restriction - (narrow-to-region beg end) - (let ((list-start (ignore-errors (scan-sexps point -1)))) - (when (and list-start - ;; Ignore the starting brace if it is an emoji - (not (get-text-property list-start 'emojified))) - ;; If we got a list start make sure both start and end - ;; belong to same string/comment - (let ((syntax-beg (syntax-ppss list-start)) - (syntax-end (syntax-ppss point))) - (and list-start - (eq (nth 8 syntax-beg) - (nth 8 syntax-end))))))))) - -(defun emojify-valid-ascii-emoji-context-p (beg end) - "Determine if the okay to display ascii emoji between BEG and END." - ;; The text is at the start of the buffer - (and (or (not (char-before beg)) - ;; 32 space since ? (? followed by a space) is not readable - ;; 34 is " since?" confuses font-lock - ;; 41 is ) since?) (extra paren) confuses most packages - (memq (char-syntax (char-before beg)) - ;; space - '(32 - ;; start/end of string - 34 - ;; whitespace syntax - ?- - ;; comment start - ?< - ;; comment end, this handles text at start of line immediately - ;; after comment line in a multiline comment - ?>))) - ;; The text is at the end of the buffer - (or (not (char-after end)) - (memq (char-syntax (char-after end)) - ;; space - '(32 - ;; start/end of string - 34 - ;; whitespace syntax - ?- - ;; punctuation - ?. - ;; closing braces - 41 - ;; comment end - ?>))))) - - - -;; Obsolete vars - -(define-obsolete-variable-alias 'emojify-emoji-style 'emojify-emoji-styles "0.2") -(define-obsolete-function-alias 'emojify-set-emoji-style 'emojify-set-emoji-styles "0.2") - - - -;; Customizations to control the behaviour when point enters emojified text - -(defcustom emojify-point-entered-behaviour 'echo - "The behaviour when point enters, an emojified text. - -It can be one of the following -`echo' - Echo the underlying text in the minibuffer -`uncover' - Display the underlying text while point is on it -function - It is called with 2 arguments (the buffer where emoji appears is - current during execution) - 1) starting position of emoji text - 2) ending position of emoji text - -Does nothing if the value is anything else." - ;; TODO: Mention custom function - :type '(radio :tag "Behaviour when point enters an emoji" - (const :tag "Echo the underlying emoji text in the minibuffer" echo) - (const :tag "Uncover (undisplay) the underlying emoji text" uncover)) - :group 'emojify) - -(defcustom emojify-reveal-on-isearch t - "Should underlying emoji be displayed when point enters emoji while in isearch mode.") - -(defcustom emojify-show-help t - "If non-nil the underlying text is displayed in a popup when mouse moves over it." - :type 'boolean - :group 'emojify) - -(defun emojify-on-emoji-enter (beginning end) - "Executed when point enters emojified text between BEGINNING and END." - (cond ((and (eq emojify-point-entered-behaviour 'echo) - ;; Do not echo in isearch-mode - (not isearch-mode) - (not (active-minibuffer-window)) - (not (current-message))) - (message (substring-no-properties (get-text-property beginning 'emojify-text)))) - ((eq emojify-point-entered-behaviour 'uncover) - (put-text-property beginning end 'display nil)) - ((functionp 'emojify-point-entered-behaviour) - (funcall emojify-point-entered-behaviour beginning end))) - - (when (and isearch-mode emojify-reveal-on-isearch) - (put-text-property beginning end 'display nil))) - -(defun emojify-on-emoji-exit (beginning end) - "Executed when point exits emojified text between BEGINNING and END." - (put-text-property beginning - end - 'display - (get-text-property beginning 'emojify-display))) - -(defvar-local emojify--last-emoji-pos nil) - -(defun emojify-detect-emoji-entry/exit () - "Detect emoji entry and exit and run appropriate handlers. - -This is inspired by `prettify-symbol-mode's logic for -`prettify-symbols-unprettify-at-point'." - (while-no-input - (emojify-with-saved-buffer-state - (when emojify--last-emoji-pos - (emojify-on-emoji-exit (car emojify--last-emoji-pos) (cdr emojify--last-emoji-pos))) - - (when (get-text-property (point) 'emojified) - (let* ((text-props (text-properties-at (point))) - (buffer (plist-get text-props 'emojify-buffer)) - (match-beginning (plist-get text-props 'emojify-beginning)) - (match-end (plist-get text-props 'emojify-end))) - (when (eq buffer (current-buffer)) - (emojify-on-emoji-enter match-beginning match-end) - (setq emojify--last-emoji-pos (cons match-beginning match-end)))))))) - -(defun emojify-help-function (_window _string pos) - "Function to get help string to be echoed when point/mouse into the point. - -To understand WINDOW, STRING and POS see the function documentation for -`help-echo' text-property." - (when (and emojify-show-help - (not isearch-mode) - (not (active-minibuffer-window)) - (not (current-message))) - (plist-get (text-properties-at pos) 'emojify-text))) - - - -;; Core functions and macros - -;; Variables related to user emojis - -(defcustom emojify-user-emojis nil - "User specified custom emojis. - -This is an alist where first element of cons is the text to be displayed as -emoji, while the second element of the cons is an alist containing data about -the emoji. - -The inner alist should have atleast (not all keys are strings) - -`name' - The name of the emoji -`style' - This should be one of \"github\", \"ascii\" or \"github\" - (see `emojify-emoji-styles') - -The alist should contain one of (see `emojify-display-style') -`unicode' - The replacement for the provided emoji for \"unicode\" display style -`image' - The replacement for the provided emoji for \"image\" display style. - This should be the absolute path to the image -`ascii' - The replacement for the provided emoji for \"ascii\" display style - -Example - -The following assumes that custom images are at ~/.emacs.d/emojis/trollface.png and -~/.emacs.d/emojis/neckbeard.png - -'((\":troll:\" . ((\"name\" . \"Troll\") - (\"image\" . \"~/.emacs.d/emojis/trollface.png\") - (\"style\" . \"github\"))) - (\":neckbeard:\" . ((\"name\" . \"Neckbeard\") - (\"image\" . \"~/.emacs.d/emojis/neckbeard.png\") - (\"style\" . \"github\"))))") - -(defvar emojify--user-emojis nil - "User specified custom emojis.") - -(defvar emojify--user-emojis-regexp nil - "Regexp to match user specified custom emojis.") - -;; Variables related to default emojis -(defvar emojify-emojis nil - "Data about the emojis, this contains only the emojis that come with emojify.") - -(defvar emojify-regexps nil - "List of regexps to match text to be emojified.") - -(defun emojify-create-emojify-emojis () - "Create `emojify-emojis' if needed." - (unless emojify-emojis - (emojify-set-emoji-data))) - -(defun emojify-get-emoji (emoji) - "Get data for given EMOJI. - -This first looks for the emoji in `emojify--user-emojis', -and then in `emojify-emojis'." - (or (when emojify--user-emojis - (ht-get emojify--user-emojis emoji)) - (ht-get emojify-emojis emoji))) - -(defun emojify-emojis-each (function) - "Execute FUNCTION for each emoji. - -This first runs function for `emojify--user-emojis', -and then `emojify-emojis'." - (when emojify--user-emojis - (ht-each function emojify--user-emojis)) - (ht-each function emojify-emojis)) - -(defun emojify--verify-user-emojis (emojis) - "Verify the EMOJIS in correct user format." - (seq-every-p (lambda (emoji) - (and (assoc "name" (cdr emoji)) - ;; Make sure style is present is only one of - ;; "unicode", "ascii" and "github". - (assoc "style" (cdr emoji)) - (seq-position '("unicode" "ascii" "github") - (cdr (assoc "style" (cdr emoji)))) - (or (assoc "unicode" (cdr emoji)) - (assoc "image" (cdr emoji)) - (assoc "ascii" (cdr emoji))))) - emojis)) - -(defun emojify-set-emoji-data () - "Read the emoji data for STYLES and set the regexp required to search them." - (setq-default emojify-emojis (let ((json-array-type 'list) - (json-object-type 'hash-table)) - (json-read-file emojify-emoji-json))) - - (let (unicode-emojis ascii-emojis) - (ht-each (lambda (emoji data) - (when (string= (gethash "style" data) "unicode") - (push emoji unicode-emojis)) - - (when (string= (gethash "style" data) "ascii") - (push emoji ascii-emojis))) - emojify-emojis) - - ;; Construct emojify-regexps such that github style are searched first - ;; followed by unicode and then ascii emojis. - (setq emojify-regexps (list ":[[:alnum:]_-]+:" - (regexp-opt unicode-emojis) - (regexp-opt ascii-emojis)))) - - (when emojify-user-emojis - (if (emojify--verify-user-emojis emojify-user-emojis) - ;; Create entries for user emojis - (let ((emoji-pairs (mapcar (lambda (user-emoji) - (cons (car user-emoji) - (ht-from-alist (cdr user-emoji)))) - emojify-user-emojis))) - (setq emojify--user-emojis (ht-from-alist emoji-pairs)) - (setq emojify--user-emojis-regexp (regexp-opt (mapcar #'car emoji-pairs)))) - (message "[emojify] User emojis are not in correct format ignoring them.")))) - -(defvar emojify-emoji-keymap - (let ((map (make-sparse-keymap))) - (define-key map [remap delete-char] #'emojify-delete-emoji-forward) - (define-key map [remap delete-forward-char] #'emojify-delete-emoji-forward) - (define-key map [remap backward-delete-char] #'emojify-delete-emoji-backward) - (define-key map [remap delete-backward-char] #'emojify-delete-emoji-backward) - (define-key map [remap backward-delete-char-untabify] #'emojify-delete-emoji-backward) - map)) - -(defun emojify-image-dir () - "Get the path to directory containing images for currently selected emoji set." - (expand-file-name emojify-emoji-set - emojify-emojis-dir)) - -(defun emojify--get-point-col-and-line (point) - "Return a cons of containing the column number and line at POINT." - (save-excursion - (goto-char point) - (cons (current-column) (line-number-at-pos)))) - -(defun emojify--get-composed-text (point) - "Get the text used as composition property at POINT. - -This does not check if there is composition property at point the callers should -make sure the point has a composition property otherwise this function will -fail." - (emojify--string-join (mapcar #'char-to-string - (decode-composition-components (nth 2 - (find-composition point - nil - nil - t)))))) - -;; These should be bound dynamically by functions calling -;; `emojify--inside-rectangle-selection-p' and -;; `emojify--inside-non-rectangle-selection-p' to region-beginning and -;; region-end respectively. This is needed mark the original region which is -;; impossible to get after point moves during processing. -(defvar emojify-region-beg nil) -(defvar emojify-region-end nil) - -(defun emojify--inside-rectangle-selection-p (beg end) - "Check if region marked by BEG and END is inside a rectangular selection. - -In addition to explicit the parameters BEG and END, calling functions should -also dynamically bind `emojify-region-beg' and `emojify-region-end' to beginning -and end of region respectively." - (when (and emojify-region-beg - (bound-and-true-p rectangle-mark-mode)) - (let ((rect-beg (emojify--get-point-col-and-line emojify-region-beg)) - (rect-end (emojify--get-point-col-and-line emojify-region-end)) - (emoji-start-pos (emojify--get-point-col-and-line beg)) - (emoji-end-pos (emojify--get-point-col-and-line end))) - (or (and (<= (car rect-beg) (car emoji-start-pos)) - (<= (car emoji-start-pos) (car rect-end)) - (<= (cdr rect-beg) (cdr emoji-start-pos)) - (<= (cdr emoji-start-pos) (cdr rect-end))) - (and (<= (car rect-beg) (car emoji-end-pos)) - (<= (car emoji-end-pos) (car rect-end)) - (<= (cdr rect-beg) (cdr emoji-end-pos)) - (<= (cdr emoji-end-pos) (cdr rect-end))))))) - -(defun emojify--inside-non-rectangle-selection-p (beg end) - "Check if region marked by BEG and END is inside a regular selection. - -In addition to the explicit parameters BEG and END, calling functions should -also dynamically bind `emojify-region-beg' and `emojify-region-end' to beginning -and end of region respectively." - (when (and emojify-region-beg - (region-active-p) - (not (bound-and-true-p rectangle-mark-mode))) - (or (and (<= emojify-region-beg beg) - (<= beg emojify-region-end)) - (and (<= emojify-region-beg end) - (<= end emojify-region-end))))) - -(defun emojify--region-background-face-maybe (beg end) - "If the BEG and END falls inside an active region return the region face. - -This returns nil if the emojis between BEG and END do not fall in region." - ;; `redisplay-highlight-region-function' was not defined in Emacs 24.3 - (when (and (or (not (boundp 'redisplay-highlight-region-function)) - (equal (default-value 'redisplay-highlight-region-function) redisplay-highlight-region-function)) - (or (emojify--inside-non-rectangle-selection-p beg end) - (emojify--inside-rectangle-selection-p beg end))) - (face-background 'region))) - -(defun emojify--overlay-face-background (face) - "Get background for given overlay FACE. - -This similar to `face-background' except it handles different values possible -for overlay face including anonymous faces and list of faces. Unlike -`face-background' it always looks up inherited faces if background is not -directly defined on the face." - (if (memq (type-of face) '(string symbol)) - (and (facep face) - (face-background face nil 'default)) - (and (consp face) - ;; Handle anonymous faces - (or (or (plist-get face :background) - (face-background (car (plist-get face :inherit)) nil 'default )) - ;; Possibly a list of faces - (emojify--overlay-face-background (car face)))))) - -(defun emojify--overlay-background (beg) - "Get the overlay face for point BEG." - (let* ((overlay-backgrounds (delq nil (seq-map (lambda (overlay) - (and (overlay-get overlay 'face) - (emojify--overlay-face-background (overlay-get overlay 'face)))) - (emojify-overlays-at beg t))))) - (car (last overlay-backgrounds)))) - -(defun emojify--face-background-at-point (beg) - "Get the background color for emoji at BEG." - (save-excursion - (goto-char beg) - (let ((point-face (face-at-point))) - (when point-face - (face-background point-face))))) - -(defun emojify--get-image-background (beg end) - "Get the color to be used as background for emoji between BEG and END. - -Ideally `emojify--overlay-background' should have been enough to handle -selection, but for some reason it does not work well." - (or (emojify--region-background-face-maybe beg end) - ;; TODO: `emojify--face-background-at-point' might already be - ;; handling overlay faces as such `emojify--overlay-background' - ;; might be redundant, need to verify this though - (emojify--overlay-background beg) - (emojify--face-background-at-point beg) - (face-background 'default))) - -(defun emojify--get-image-display (data beg end) - "Get the display text property to display the emoji as an image. - -DATA holds the emoji data, BEG and END delimit the region where emoji will -be displayed." - (when (ht-get data "image") - (let* ((image-file (expand-file-name (ht-get data "image") - (emojify-image-dir))) - (image-type (intern (upcase (file-name-extension image-file))))) - (when (file-exists-p image-file) - (create-image image-file - ;; use imagemagick if available and supports PNG images - ;; (allows resizing images) - (when (and (fboundp 'imagemagick-types) - (memq image-type (imagemagick-types))) - 'imagemagick) - nil - :ascent 'center - :heuristic-mask t - :background (emojify--get-image-background beg end) - ;; no-op if imagemagick is not available - :height (emojify-default-font-height)))))) - -(defun emojify--get-unicode-display (data _beg _end) - "Get the display text property to display the emoji as an unicode character. - -DATA holds the emoji data, _BEG and _END delimit the region where emoji will -be displayed." - (let* ((unicode (ht-get data "unicode")) - (characters (when unicode - (string-to-vector unicode)))) - (when (seq-every-p #'char-displayable-p characters) - unicode))) - -(defun emojify--get-ascii-display (data _beg _end) - "Get the display text property to display the emoji as an ascii characters. - -DATA holds the emoji data, _BEG and _END delimit the region where emoji will -be displayed." - (ht-get data "ascii")) - -(defun emojify--get-text-display-props (emoji beg end) - "Get the display property for an EMOJI. - -TEXT is the text to be displayed as emoji, BEG and END delimit the -region containing the emoji." - (funcall (pcase emojify-display-style - (`image #'emojify--get-image-display) - (`unicode #'emojify--get-unicode-display) - (`ascii #'emojify--get-ascii-display)) - emoji - beg - end)) - -(defun emojify--display-emoji (emoji text buffer start end) - "Display EMOJI for TEXT in BUFFER between START and END." - (let ((display-prop (emojify--get-text-display-props emoji start end))) - (when display-prop - (add-text-properties start - end - (list 'emojified t - 'emojify-display display-prop - 'display display-prop - 'emojify-buffer buffer - 'emojify-text text - 'emojify-beginning (copy-marker start) - 'emojify-end (copy-marker end) - 'yank-handler (list nil text) - 'keymap emojify-emoji-keymap - 'help-echo #'emojify-help-function))))) - -(defun emojify-display-emojis-in-region (beg end) - "Display emojis in region. - -BEG and END are the beginning and end of the region respectively. - -Displaying happens in two phases, first search based phase displays actual text -appearing in buffer as emojis. In the next phase composed text is searched for -emojis and displayed. - -A minor problem here is that if the text is composed after this display loop it -would not be displayed as emoji, although in practice the two packages that use -the composition property `prettify-symbol-mode' and `org-bullets' use the -font-lock machinery which runs before emojify's display loop, so hopefully this -should not be a problem 🤞." - (emojify-with-saved-buffer-state - ;; Make sure we halt if displaying emojis takes more than a second (this - ;; might be too large duration) - (with-timeout (1 (emojify-message "Failed to display emojis under 1 second")) - (seq-doseq (regexp (apply #'append - (when emojify--user-emojis-regexp - (list emojify--user-emojis-regexp)) - (list emojify-regexps))) - (let (case-fold-search) - (goto-char beg) - (while (and (> end (point)) - (search-forward-regexp regexp end t)) - (let* ((match-beginning (match-beginning 0)) - (match-end (match-end 0)) - (match (match-string-no-properties 0)) - (buffer (current-buffer)) - (emoji (emojify-get-emoji match))) - (when (and emoji - (memql (intern (ht-get emoji "style")) - emojify-emoji-styles) - ;; Skip displaying this emoji if the its bounds are - ;; already part of an existing emoji. Since the emojis - ;; are searched in descending order of length (see - ;; construction of emojify-regexp in `emojify-set-emoji-data'), - ;; this means larger emojis get precedence over smaller - ;; ones - (not (or (get-text-property match-beginning 'emojified) - (get-text-property (1- match-end) 'emojified))) - ;; Display unconditionally in non-prog mode - (or (not (derived-mode-p 'prog-mode 'tuareg--prog-mode 'comint-mode)) - ;; In prog mode enable respecting `emojify-program-contexts' - (emojify-valid-program-context-p emoji match-beginning match-end)) - - ;; Display ascii emojis conservatively, since they have potential - ;; to be annoying consider d: in head:, except while executing apropos - ;; emoji - (or (not (string= (ht-get emoji "style") "ascii")) - (emojify-valid-ascii-emoji-context-p match-beginning match-end)) - - (not (emojify-inside-org-src-p match-beginning)) - - ;; Inhibit possibly inside a list - ;; 41 is ?) but packages get confused by the extra closing paren :) - ;; TODO Report bugs to such packages - (not (and (eq (char-syntax (char-before match-end)) 41) - (emojify-looking-at-end-of-list-maybe match-end))) - - (not (run-hook-with-args-until-success 'emojify-inhibit-functions match match-beginning match-end))) - (emojify--display-emoji emoji match buffer match-beginning match-end)))) - ;; Stop a bit to let `with-timeout' kick in - (sit-for 0 t))) - - ;; Loop to emojify composed text - (when (and emojify-composed-text-p - ;; Skip this if user has disabled unicode style emojis, since - ;; we display only composed text that are unicode emojis - (memql 'unicode emojify-emoji-styles)) - (goto-char beg) - (let ((compose-start (if (get-text-property beg 'composition) - ;; Check `beg' first for composition property - ;; since `next-single-property-change' will - ;; search for region after `beg' for property - ;; change thus skipping any composed text at - ;; `beg' - beg - (next-single-property-change beg - 'composition - nil - end)))) - (while (and (> end (point)) - ;; `end' would be equal to `compose-start' if there was no - ;; text with composition found within `end', this happens - ;; because `next-single-property-change' returns the limit - ;; (and we use `end' as the limit) if no match is found - (> end compose-start) - compose-start) - (let* ((match (emojify--get-composed-text compose-start)) - (emoji (emojify-get-emoji match)) - (compose-end (next-single-property-change compose-start 'composition nil end))) - ;; Display only composed text that is unicode char - (when (and emoji - (string= (gethash "style" emoji) "unicode")) - (emojify--display-emoji emoji match (current-buffer) compose-start compose-end)) - ;; Setup the next loop - (setq compose-start (and compose-end (next-single-property-change compose-end - 'composition - nil - end))) - (goto-char compose-end)) - ;; Stop a bit to let `with-timeout' kick in - (sit-for 0 t))))))) - -(defun emojify-undisplay-emojis-in-region (beg end) - "Undisplay the emojis in region. - -BEG and END are the beginning and end of the region respectively" - (emojify-with-saved-buffer-state - (while (< beg end) - ;; Get the start of emojified region in the region, the region is marked - ;; with text-property `emojified' whose value is `t'. The region is marked - ;; so that we do not inadvertently remove display or other properties - ;; inserted by other packages. This might fail too if a package adds any - ;; of these properties between an emojified text, but that situation is - ;; hopefully very rare and this is better than blindly removing all text - ;; properties - (let* ((emoji-start (text-property-any beg end 'emojified t)) - ;; Get the end emojified text, if we could not find the start set - ;; emoji-end to region `end', this merely to make looping easier. - (emoji-end (or (and emoji-start - (text-property-not-all emoji-start end 'emojified t)) - ;; If the emojified text is at the end of the region - ;; assume that end is the emojified text. - end))) - ;; Proceed only if we got start of emojified text - (when emoji-start - ;; Remove the properties - (remove-text-properties emoji-start emoji-end (append (list 'emojified t - 'display t - 'emojify-display t - 'emojify-buffer t - 'emojify-text t - 'emojify-beginning t - 'emojify-end t - 'yank-handler t - 'keymap t - 'help-echo t - 'rear-nonsticky t)))) - ;; Setup the next iteration - (setq beg emoji-end))))) - -(defun emojify-redisplay-emojis-in-region (&optional beg end) - "Redisplay emojis in region between BEG and END. - -Redisplay emojis in the visible region if BEG and END are not specified" - (let* ((area (emojify--get-relevant-region)) - (beg (save-excursion - (goto-char (or beg (car area))) - (line-beginning-position))) - (end (save-excursion - (goto-char (or end (cdr area))) - (line-end-position)))) - - (emojify-execute-ignoring-errors-unless-debug - (emojify-undisplay-emojis-in-region beg end) - (emojify-display-emojis-in-region beg end)))) - -(defun emojify-after-change-extend-region-function (beg end _len) - "Extend the region to be emojified. - -This simply extends the region to be fontified to the start of line at BEG and -end of line at END. _LEN is ignored. - -The idea is since an emoji cannot span multiple lines, redisplaying complete -lines ensures that all the possibly affected emojis are redisplayed." - (let ((emojify-jit-lock-start (save-excursion - (goto-char beg) - (line-beginning-position))) - (emojify-jit-lock-end (save-excursion - (goto-char end) - (line-end-position)))) - (setq jit-lock-start (if jit-lock-start - (min jit-lock-start emojify-jit-lock-start) - emojify-jit-lock-start)) - (setq jit-lock-end (if jit-lock-end - (max jit-lock-end emojify-jit-lock-end) - emojify-jit-lock-end)))) - - - -;; Electric delete functionality - -(defun emojify--find-key-binding-ignoring-emojify-keymap (key) - "Find the binding for given KEY ignoring the text properties at point. - -This is needed since `key-binding' looks up in keymap text property as well -which is not what we want when falling back in `emojify-delete-emoji'" - (let* ((key-binding (or (minor-mode-key-binding key) - (local-key-binding key) - (global-key-binding key)))) - (when key-binding - (or (command-remapping key-binding - nil - (seq-filter (lambda (keymap) - (not (equal keymap emojify-emoji-keymap))) - (current-active-maps))) - key-binding)))) - -(defun emojify-delete-emoji (point) - "Delete emoji at POINT." - (if (get-text-property point 'emojified) - (delete-region (get-text-property point 'emojify-beginning) - (get-text-property point 'emojify-end)) - (call-interactively (emojify--find-key-binding-ignoring-emojify-keymap (this-command-keys))))) - -(defun emojify-delete-emoji-forward () - "Delete emoji after point." - (interactive) - (emojify-delete-emoji (point))) - -(defun emojify-delete-emoji-backward () - "Delete emoji before point." - (interactive) - (emojify-delete-emoji (1- (point)))) - -;; Integrate with delete-selection-mode -;; Basically instruct delete-selection mode to override our commands -;; if the region is active. -(put 'emojify-delete-emoji-forward 'delete-selection 'supersede) -(put 'emojify-delete-emoji-backward 'delete-selection 'supersede) - - - -;; Updating background color on selection - -(defun emojify--update-emojis-background-in-region (&optional beg end) - "Update the background color for emojis between BEG and END." - (when (equal emojify-display-style 'image) - (emojify-with-saved-buffer-state - (let ((emojify-region-beg (when (region-active-p) (region-beginning))) - (emojify-region-end (when (region-active-p) (region-end)))) - (emojify-do-for-emojis-in-region beg end - (plist-put (cdr (get-text-property emoji-start 'display)) - :background - (emojify--get-image-background emoji-start - emoji-end))))))) - -(defun emojify--update-emojis-background-in-region-starting-at (point) - "Update background color for emojis in buffer starting at POINT. - -This updates the emojis in the region starting from POINT, the end of region is -determined by product of `frame-height' and `frame-width' which roughly -corresponds to the visible area. POINT usually corresponds to the starting -position of the window, see -`emojify-update-visible-emojis-background-after-command' and -`emojify-update-visible-emojis-background-after-window-scroll' - -NOTE: `window-text-height' and `window-text-width' would have been more -appropriate here however they were not defined in Emacs v24.3 and below." - (let* ((region-beginning point) - (region-end (min (+ region-beginning (* (frame-height) - (frame-width))) - (point-max)))) - (emojify--update-emojis-background-in-region region-beginning - region-end))) - -(defun emojify-update-visible-emojis-background-after-command () - "Function added to `post-command-hook' when region is active. - -This function updates the backgrounds of the emojis in the region changed after -the command. - -Ideally this would have been good enough to update emoji backgounds after region -changes, unfortunately this does not work well with commands that scroll the -window specifically `window-start' and `window-end' (sometimes only `window-end') -report incorrect values. - -To work around this -`emojify-update-visible-emojis-background-after-window-scroll' is added to -`window-scroll-functions' to update emojis on window scroll." - (while-no-input (emojify--update-emojis-background-in-region-starting-at (window-start)))) - -(defun emojify-update-visible-emojis-background-after-window-scroll (_window display-start) - "Function added to `window-scroll-functions' when region is active. - -This function updates the backgrounds of the emojis in the newly displayed area -of the window. DISPLAY-START corresponds to the new start of the window." - (while-no-input (emojify--update-emojis-background-in-region-starting-at display-start))) - - - -;; Lazy image downloading - -(defvar emojify--refused-image-download-p nil - "Used to remember that user has refused to download images in this session.") -(defvar emojify--download-in-progress-p nil - "Is emoji download in progress used to avoid multiple emoji download prompts.") - -(defun emojify--emoji-download-emoji-set (data) - "Download the emoji images according to DATA." - (let ((destination (make-temp-name temporary-file-directory))) - (url-copy-file (ht-get data "url") - destination) - (let ((downloaded-sha (with-temp-buffer - (insert-file-contents-literally destination) - (secure-hash 'sha256 (current-buffer))))) - (when (string= downloaded-sha (ht-get data "sha256")) - destination)))) - -(defun emojify--extract-emojis (file) - "Extract the tar FILE in emoji directory." - (let* ((default-directory emojify-emojis-dir)) - (with-temp-buffer - (insert-file-contents-literally file) - (let ((emojify-inhibit-emojify-in-current-buffer-p t)) - (tar-mode)) - (tar-untar-buffer)))) - -(defun emojify-download-emoji (emoji-set) - "Download the provided EMOJI-SET." - (interactive (list (completing-read "Select the emoji set you want to download: " - (ht-keys emojify-emoji-set-json)))) - (let ((emoji-data (ht-get emojify-emoji-set-json emoji-set))) - (cond ((not emoji-data) - (error "No emoji set named %s found" emoji-set)) - ((and (file-exists-p (expand-file-name emoji-set emojify-emojis-dir)) - (called-interactively-p 'any)) - (message "%s emoji-set already downloaded, not downloading again!" emoji-set)) - (t - (emojify--extract-emojis (emojify--emoji-download-emoji-set (ht-get emojify-emoji-set-json emoji-set))))))) - -(defun emojify-download-emoji-maybe () - "Download emoji images if needed." - (when (and (equal emojify-display-style 'image) - (not (file-exists-p (emojify-image-dir))) - (not emojify--refused-image-download-p)) - (unwind-protect - ;; Do not prompt for download if download is in progress - (unless emojify--download-in-progress-p - (setq emojify--download-in-progress-p t) - (if (yes-or-no-p "[emojify] Emoji images not available should I download them now?") - (emojify-download-emoji emojify-emoji-set) - ;; Remember that user has refused to download the emojis so that we - ;; do not ask again in present session - (setq emojify--refused-image-download-p t) - (warn "[emojify] Not downloading emoji images for now. Emojis would -not be displayed since images are not available. If you wish to download emojis, -run the command `emojify-download-emoji'"))) - (setq emojify--download-in-progress-p nil)))) - -(defun emojify-ensure-images () - "Ensure that emoji images are downloaded." - (if after-init-time - (emojify-download-emoji-maybe) - (add-hook 'after-init-hook #'emojify-download-emoji-maybe t))) - - - -(defun emojify-turn-on-emojify-mode () - "Turn on `emojify-mode' in current buffer." - - ;; Calculate emoji data if needed - (emojify-create-emojify-emojis) - - (when (emojify-buffer-p (current-buffer)) - ;; Download images if not available - (emojify-ensure-images) - - ;; Install our jit-lock function - (jit-lock-register #'emojify-redisplay-emojis-in-region) - (add-hook 'jit-lock-after-change-extend-region-functions #'emojify-after-change-extend-region-function t t) - - ;; Handle point entered behaviour - (add-hook 'post-command-hook #'emojify-detect-emoji-entry/exit t t) - - ;; Update emoji backgrounds after each command - (add-hook 'post-command-hook #'emojify-update-visible-emojis-background-after-command t t) - - ;; Update emoji backgrounds after mark is deactivated, this is needed since - ;; deactivation can happen outside the command loop - (add-hook 'deactivate-mark-hook #'emojify-update-visible-emojis-background-after-command t t) - - ;; Update emoji backgrounds after when window scrolls - (add-hook 'window-scroll-functions #'emojify-update-visible-emojis-background-after-window-scroll t t) - - ;; Redisplay emojis after enabling `prettify-symbol-mode' - (add-hook 'prettify-symbols-mode-hook #'emojify-redisplay-emojis-in-region) - - ;; Redisplay visible emojis when emoji style changes - (add-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region))) - -(defun emojify-turn-off-emojify-mode () - "Turn off `emojify-mode' in current buffer." - ;; Remove currently displayed emojis - (save-restriction - (widen) - (emojify-undisplay-emojis-in-region (point-min) (point-max))) - - ;; Uninstall our jit-lock function - (jit-lock-unregister #'emojify-redisplay-emojis-in-region) - (remove-hook 'jit-lock-after-change-extend-region-functions #'emojify-after-change-extend-region-function t) - - (remove-hook 'post-command-hook #'emojify-detect-emoji-entry/exit t) - - ;; Disable hooks to update emoji backgrounds - (remove-hook 'post-command-hook #'emojify-update-visible-emojis-background-after-command t) - (remove-hook 'deactivate-mark-hook #'emojify-update-visible-emojis-background-after-command t) - (remove-hook 'window-scroll-functions #'emojify-update-visible-emojis-background-after-window-scroll t) - - ;; Remove hook to redisplay emojis after enabling `prettify-symbol-mode' - (remove-hook 'prettify-symbols-mode-hook #'emojify-redisplay-emojis-in-region) - - ;; Remove style change hooks - (remove-hook 'emojify-emoji-style-change-hook #'emojify-redisplay-emojis-in-region)) - -;;;###autoload -(define-minor-mode emojify-mode - "Emojify mode" - :init-value nil - (if emojify-mode - ;; Turn on - (emojify-turn-on-emojify-mode) - ;; Turn off - (emojify-turn-off-emojify-mode))) - -;;;###autoload -(define-globalized-minor-mode global-emojify-mode - emojify-mode emojify-mode - :init-value nil) - -(defadvice set-buffer-multibyte (after emojify-disable-for-unibyte-buffers (&rest ignored)) - "Disable emojify when buffer changes to a unibyte encoding, reenable it when -buffer changes back to multibyte encoding." - (ignore-errors - (if enable-multibyte-characters - (when global-emojify-mode - (emojify-mode +1)) - (emojify-mode -1)))) - -(ad-activate #'set-buffer-multibyte) - - - -;; Searching and inserting emojis - -(defvar emojify-apropos-buffer-name "*Apropos Emojis*") - -(defun emojify-apropos-quit () - "Delete the window displaying Emoji search results." - (interactive) - (if (= (length (window-list)) 1) - (bury-buffer) - (quit-window))) - -(defun emojify-apropos-copy-emoji () - "Copy the emoji being displayed at current line in apropos results." - (interactive) - (save-excursion - (goto-char (line-beginning-position)) - (if (not (get-text-property (point) 'emojified)) - (user-error "No emoji at point") - (kill-new (get-text-property (point) 'emojify-text)) - (message "Copied emoji to kill ring!")))) - -(defvar emojify-apropos-mode-map - (let ((map (make-sparse-keymap))) - - (define-key map "q" #'emojify-apropos-quit) - (define-key map "c" #'emojify-apropos-copy-emoji) - (define-key map "w" #'emojify-apropos-copy-emoji) - (define-key map "n" #'next-line) - (define-key map "p" #'previous-line) - (define-key map "r" #'isearch-backward) - (define-key map "s" #'isearch-forward) - (define-key map "g" #'emojify-apropos-emoji) - (define-key map ">" 'end-of-buffer) - (define-key map "<" 'beginning-of-buffer) - - (dolist (key '("?" "h" "H")) - (define-key map key #'describe-mode)) - - (dolist (number (number-sequence 0 9)) - (define-key map (number-to-string number) #'digit-argument)) - - map) - "Keymap used in `emojify-apropos-mode'.") - -(define-derived-mode emojify-apropos-mode fundamental-mode "Apropos Emojis" - "Mode used to display results of `emojify-apropos-emoji' - -\\{emojify-apropos-mode-map}" - (emojify-mode +1) - (read-only-mode +1)) - -(put 'emojify-apropos-mode 'mode-class 'special) - -(defvar emojify--apropos-last-query nil) -(make-variable-buffer-local 'emojify--apropos-last-query) - -(defun emojify-apropos-read-pattern () - "Read apropos pattern with INITIAL-INPUT as the initial input. - -Borrowed from apropos.el" - (let ((pattern (read-string (concat "Search for emoji (word list or regexp): ") - emojify--apropos-last-query))) - (if (string-equal (regexp-quote pattern) pattern) - (or (split-string pattern "[ \t]+" t) - (if (fboundp 'user-error) - (apply #'user-error "No word list given") - (apply #'error "No word list given"))) - pattern))) - -;;;###autoload -(defun emojify-apropos-emoji (pattern) - "Show Emojis that match PATTERN." - (interactive (list (emojify-apropos-read-pattern))) - - (emojify-create-emojify-emojis) - - (let ((in-apropos-buffer-p (equal major-mode 'emojify-apropos-mode)) - matching-emojis - sorted-emojis) - - (unless (listp pattern) - (setq pattern (list pattern))) - - ;; Convert the user entered text to a regex to match the emoji name or - ;; description - (apropos-parse-pattern pattern) - - ;; Collect matching emojis in a list of (list score emoji emoji-data) - ;; elements, where score is the proximity of the emoji to given pattern - ;; calculated using `apropos-score-str' - (emojify-emojis-each (lambda (key value) - (when (or (string-match apropos-regexp key) - (string-match apropos-regexp (ht-get value "name"))) - (push (list (max (apropos-score-str key) - (apropos-score-str (ht-get value "name"))) - key - value) - matching-emojis)))) - - ;; Sort the emojis by the proximity score - (setq sorted-emojis (mapcar #'cdr - (sort matching-emojis - (lambda (emoji1 emoji2) - (> (car emoji1) (car emoji2)))))) - - ;; Insert result in apropos buffer and display it - (with-current-buffer (get-buffer-create emojify-apropos-buffer-name) - (let ((inhibit-read-only t) - (query (mapconcat 'identity pattern " "))) - (erase-buffer) - (insert (propertize "Emojis matching" 'face 'apropos-symbol)) - (insert (format " - \"%s\"" query)) - (insert "\n\nUse `c' or `w' to copy emoji on current line\nUse `g' to rerun apropos\n\n") - (dolist (emoji sorted-emojis) - (insert (format "%s - %s (%s)" - (car emoji) - (ht-get (cadr emoji) "name") - (ht-get (cadr emoji) "style"))) - (insert "\n")) - (goto-char (point-min)) - (forward-line (1- 6)) - (emojify-apropos-mode) - (setq emojify--apropos-last-query (concat query " ")) - (setq-local line-spacing 7))) - - (select-window (display-buffer (get-buffer emojify-apropos-buffer-name) - (when in-apropos-buffer-p - (cons #'display-buffer-same-window nil)))))) - -(defun emojify--insert-minibuffer-setup-hook () - "Enables `emojify-mode' in minbuffer while inserting emojis. - -This ensures `emojify' is enabled even when `global-emojify-mode' is not on." - (emojify-mode +1)) - -(defun emojify--insert-helm-hook () - "Enables `emojify-mode' in helm buffer. - -This ensures `emojify' is enabled in helm buffer displaying completion even when -`global-emojify-mode' is not on." - (with-current-buffer helm-buffer - (emojify-mode +1))) - -;;;###autoload -(defun emojify-insert-emoji () - "Interactively prompt for Emojis and insert them in the current buffer. - -This respects the `emojify-emoji-styles' variable." - (interactive) - (emojify-create-emojify-emojis) - (let* ((emojify-in-insertion-command-p t) - (styles (mapcar #'symbol-name emojify-emoji-styles)) - (line-spacing 7) - (completion-ignore-case t) - (candidates (let (emojis) - (emojify-emojis-each (lambda (key value) - (when (seq-position styles (ht-get value "style")) - (push (format "%s - %s (%s)" - key - (ht-get value "name") - (ht-get value "style")) - emojis)))) - emojis)) - ;; Vanilla Emacs completion and Icicles use the completion list mode to display candidates - ;; the following makes sure emojify is enabled in the completion list - (completion-list-mode-hook (cons #'emojify--insert-minibuffer-setup-hook completion-list-mode-hook)) - ;; (Vertical) Ido and Ivy displays candidates in minibuffer this makes sure candidates are emojified - ;; when Ido or Ivy are used - (minibuffer-setup-hook (cons #'emojify--insert-minibuffer-setup-hook minibuffer-setup-hook)) - (helm-after-initialize-hook (cons #'emojify--insert-helm-hook (bound-and-true-p helm-after-initialize-hook)))) - (insert (car (split-string (completing-read "Insert Emoji: " candidates) - " "))))) - - - -;; Integration with some miscellaneous functionality - -(defadvice mouse--drag-set-mark-and-point (after emojify-update-emoji-background (&rest ignored)) - "Advice to update emoji backgrounds after selection is changed using mouse. - -Currently there are no hooks run after mouse movements, as such the emoji -backgrounds are updated only after the mouse button is released. This advices -`mouse--drag-set-mark-and-point' which is run after selection changes to trigger -an update of emoji backgrounds. Not the cleanest but the only way I can think of." - (when emojify-mode - (emojify-update-visible-emojis-background-after-command))) - -(ad-activate #'mouse--drag-set-mark-and-point) - -(defadvice text-scale-increase (after emojify-resize-emojis (&rest ignored)) - "Advice `text-scale-increase' to resize emojis on text resize." - (when emojify-mode - (let ((new-font-height (emojify-default-font-height))) - (emojify-do-for-emojis-in-region (point-min) (point-max) - (plist-put (cdr (get-text-property emoji-start 'display)) - :height - new-font-height))))) - -(ad-activate #'text-scale-increase) - - - -(provide 'emojify) -;;; emojify.el ends here diff --git a/elpa/ht-20161015.1945/ht-autoloads.el b/elpa/ht-20161015.1945/ht-autoloads.el deleted file mode 100644 index b426061..0000000 --- a/elpa/ht-20161015.1945/ht-autoloads.el +++ /dev/null @@ -1,15 +0,0 @@ -;;; ht-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil nil ("ht.el") (22533 17534 488385 459000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; ht-autoloads.el ends here diff --git a/elpa/ht-20161015.1945/ht-pkg.el b/elpa/ht-20161015.1945/ht-pkg.el deleted file mode 100644 index 9109d99..0000000 --- a/elpa/ht-20161015.1945/ht-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "ht" "20161015.1945" "The missing hash table library for Emacs" '((dash "2.12.0")) :keywords '("hash table" "hash map" "hash")) diff --git a/elpa/ht-20161015.1945/ht.el b/elpa/ht-20161015.1945/ht.el deleted file mode 100644 index 6c23f0c..0000000 --- a/elpa/ht-20161015.1945/ht.el +++ /dev/null @@ -1,288 +0,0 @@ -;;; ht.el --- The missing hash table library for Emacs - -;; Copyright (C) 2013 Wilfred Hughes - -;; Author: Wilfred Hughes -;; Version: 2.2 -;; Package-Version: 20161015.1945 -;; Keywords: hash table, hash map, hash -;; Package-Requires: ((dash "2.12.0")) - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; The missing hash table library for Emacs. -;; -;; See documentation at https://github.com/Wilfred/ht.el - -;;; Code: - -(require 'dash) - -(defmacro ht (&rest pairs) - "Create a hash table with the key-value pairs given. -Keys are compared with `equal'. - -\(fn (KEY-1 VALUE-1) (KEY-2 VALUE-2) ...)" - (let* ((table-symbol (make-symbol "ht-temp")) - (assignments - (mapcar - (lambda (pair) `(ht-set! ,table-symbol ,@pair)) - pairs))) - `(let ((,table-symbol (ht-create))) - ,@assignments - ,table-symbol))) - -(defun ht-create (&optional test) - "Create an empty hash table. - -TEST indicates the function used to compare the hash -keys. Default is `equal'. It can be `eq', `eql', `equal' or a -user-supplied test created via `define-hash-table-test'." - (make-hash-table :test (or test 'equal))) - -(defun ht<-alist (alist &optional test) - "Create a hash table with initial values according to ALIST. - -TEST indicates the function used to compare the hash -keys. Default is `equal'. It can be `eq', `eql', `equal' or a -user-supplied test created via `define-hash-table-test'." - (let ((h (ht-create test))) - ;; the first key-value pair in an alist gets precedence, so we - ;; start from the end of the list: - (dolist (pair (reverse alist) h) - (let ((key (car pair)) - (value (cdr pair))) - (ht-set! h key value))))) - -(defalias 'ht-from-alist 'ht<-alist) - -(defun ht<-plist (plist &optional test) - "Create a hash table with initial values according to PLIST. - -TEST indicates the function used to compare the hash -keys. Default is `equal'. It can be `eq', `eql', `equal' or a -user-supplied test created via `define-hash-table-test'." - (let ((h (ht-create test))) - (dolist (pair (-partition 2 plist) h) - (let ((key (car pair)) - (value (cadr pair))) - (ht-set! h key value))))) - -(defalias 'ht-from-plist 'ht<-plist) - -(defun ht-get (table key &optional default) - "Look up KEY in TABLE, and return the matching value. -If KEY isn't present, return DEFAULT (nil if not specified)." - (gethash key table default)) - -(defun ht-set! (table key value) - "Associate KEY in TABLE with VALUE." - (puthash key value table) - nil) - -(defalias 'ht-set 'ht-set!) - -(defun ht-update! (table from-table) - "Update TABLE according to every key-value pair in FROM-TABLE." - (maphash - (lambda (key value) (puthash key value table)) - from-table) - nil) - -(defalias 'ht-update 'ht-update!) - -(defun ht-merge (&rest tables) - "Crete a new tables that includes all the key-value pairs from TABLES. -If multiple have tables have the same key, the value in the last -table is used." - (let ((merged (ht-create))) - (mapc (lambda (table) (ht-update! merged table)) tables) - merged)) - -(defun ht-remove! (table key) - "Remove KEY from TABLE." - (remhash key table)) - -(defalias 'ht-remove 'ht-remove!) - -(defun ht-clear! (table) - "Remove all keys from TABLE." - (clrhash table) - nil) - -(defalias 'ht-clear 'ht-clear!) - -(defun ht-map (function table) - "Apply FUNCTION to each key-value pair of TABLE, and make a list of the results. -FUNCTION is called with two arguments, KEY and VALUE." - (let (results) - (maphash - (lambda (key value) - (push (funcall function key value) results)) - table) - results)) - -(defmacro ht-amap (form table) - "Anaphoric version of `ht-map'. -For every key-value pair in TABLE, evaluate FORM with the -variables KEY and VALUE bound." - `(ht-map (lambda (key value) ,form) ,table)) - -(defun ht-keys (table) - "Return a list of all the keys in TABLE." - (ht-amap key table)) - -(defun ht-values (table) - "Return a list of all the values in TABLE." - (ht-amap value table)) - -(defun ht-items (table) - "Return a list of two-element lists '(key value) from TABLE." - (ht-amap (list key value) table)) - -(defalias 'ht-each 'maphash - "Apply FUNCTION to each key-value pair of TABLE. -Returns nil, used for side-effects only.") - -(defmacro ht-aeach (form table) - "Anaphoric version of `ht-each'. -For every key-value pair in TABLE, evaluate FORM with the -variables key and value bound." - `(ht-each (lambda (key value) ,form) ,table)) - -(defun ht-select-keys (table keys) - "Return a copy of TABLE with only the specified KEYS." - (let (result) - (setq result (make-hash-table :test (hash-table-test table))) - (dolist (key keys result) - (if (not (equal (gethash key table 'key-not-found) 'key-not-found)) - (puthash key (gethash key table) result))))) - -(defun ht->plist (table) - "Return a flat list '(key1 value1 key2 value2...) from TABLE. - -Note that hash tables are unordered, so this cannot be an exact -inverse of `ht<-plist'. The following is not guaranteed: - -\(let ((data '(a b c d))) - (equalp data - (ht->plist (ht<-plist data))))" - (apply 'append (ht-items table))) - -(defalias 'ht-to-plist 'ht->plist) - -(defun ht-copy (table) - "Return a shallow copy of TABLE (keys and values are shared)." - (copy-hash-table table)) - -(defun ht->alist (table) - "Return a list of two-element lists '(key . value) from TABLE. - -Note that hash tables are unordered, so this cannot be an exact -inverse of `ht<-alist'. The following is not guaranteed: - -\(let ((data '((a . b) (c . d)))) - (equalp data - (ht->alist (ht<-alist data))))" - (ht-amap (cons key value) table)) - -(defalias 'ht-to-alist 'ht->alist) - -(defalias 'ht? 'hash-table-p) - -(defalias 'ht-p 'hash-table-p) - -(defun ht-contains? (table key) - "Return 't if TABLE contains KEY." - (not (eq (ht-get table key 'ht--not-found) 'ht--not-found))) - -(defalias 'ht-contains-p 'ht-contains?) - -(defun ht-size (table) - "Return the actual number of entries in TABLE." - (hash-table-count table)) - -(defun ht-empty? (table) - "Return true if the actual number of entries in TABLE is zero." - (zerop (ht-size table))) - -(defun ht-select (function table) - "Return a hash table containing all entries in TABLE for which -FUNCTION returns a truthy value. - -FUNCTION is called with two arguments, KEY and VALUE." - (let ((results (ht-create))) - (ht-each - (lambda (key value) - (when (funcall function key value) - (ht-set! results key value))) - table) - results)) - -(defun ht-reject (function table) - "Return a hash table containing all entries in TABLE for which -FUNCTION returns a falsy value. - -FUNCTION is called with two arguments, KEY and VALUE." - (let ((results (ht-create))) - (ht-each - (lambda (key value) - (unless (funcall function key value) - (ht-set! results key value))) - table) - results)) - -(defun ht-reject! (function table) - "Delete entries from TABLE for which FUNCTION returns a falsy value. - -FUNCTION is called with two arguments, KEY and VALUE." - (ht-each - (lambda (key value) - (when (funcall function key value) - (remhash key table))) - table) - nil) - -(defalias 'ht-delete-if 'ht-reject!) - -(defun ht-find (function table) - "Return (key, value) from TABLE for which FUNCTION returns a truthy value. -Return nil otherwise. - -FUNCTION is called with two arguments, KEY and VALUE." - (catch 'break - (ht-each - (lambda (key value) - (when (funcall function key value) - (throw 'break (list key value)))) - table))) - -(defun ht-equal? (table1 table2) - "Return t if TABLE1 and TABLE2 have the same keys and values. -Does not compare equality predicates." - (let ((keys1 (ht-keys table1)) - (keys2 (ht-keys table2)) - (sentinel (make-symbol "ht-sentinel"))) - (and (equal (length keys1) (length keys2)) - (--all? - (equal (ht-get table1 it) - (ht-get table2 it sentinel)) - keys1)))) - -(defalias 'ht-equal-p 'ht-equal?) - -(provide 'ht) -;;; ht.el ends here diff --git a/elpa/oauth2-0.11/oauth2-autoloads.el b/elpa/oauth2-0.11/oauth2-autoloads.el deleted file mode 100644 index 893fb0f..0000000 --- a/elpa/oauth2-0.11/oauth2-autoloads.el +++ /dev/null @@ -1,45 +0,0 @@ -;;; oauth2-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "oauth2" "oauth2.el" (22533 17544 732619 192000)) -;;; Generated autoloads from oauth2.el - -(autoload 'oauth2-refresh-access "oauth2" "\ -Refresh OAuth access TOKEN. -TOKEN should be obtained with `oauth2-request-access'. - -\(fn TOKEN)" nil nil) - -(autoload 'oauth2-auth "oauth2" "\ -Authenticate application via OAuth2. - -\(fn AUTH-URL TOKEN-URL CLIENT-ID CLIENT-SECRET &optional SCOPE STATE REDIRECT-URI)" nil nil) - -(autoload 'oauth2-auth-and-store "oauth2" "\ -Request access to a resource and store it using `plstore'. - -\(fn AUTH-URL TOKEN-URL RESOURCE-URL CLIENT-ID CLIENT-SECRET &optional REDIRECT-URI)" nil nil) - -(autoload 'oauth2-url-retrieve-synchronously "oauth2" "\ -Retrieve an URL synchronously using TOKEN to access it. -TOKEN can be obtained with `oauth2-auth'. - -\(fn TOKEN URL &optional REQUEST-METHOD REQUEST-DATA REQUEST-EXTRA-HEADERS)" nil nil) - -(autoload 'oauth2-url-retrieve "oauth2" "\ -Retrieve an URL asynchronously using TOKEN to access it. -TOKEN can be obtained with `oauth2-auth'. CALLBACK gets called with CBARGS -when finished. See `url-retrieve'. - -\(fn TOKEN URL CALLBACK &optional CBARGS REQUEST-METHOD REQUEST-DATA REQUEST-EXTRA-HEADERS)" nil nil) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; oauth2-autoloads.el ends here diff --git a/elpa/oauth2-0.11/oauth2-pkg.el b/elpa/oauth2-0.11/oauth2-pkg.el deleted file mode 100644 index 133716c..0000000 --- a/elpa/oauth2-0.11/oauth2-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "oauth2" "0.11" "OAuth 2.0 Authorization Protocol" 'nil :url "http://elpa.gnu.org/packages/oauth2.html" :keywords '("comm")) diff --git a/elpa/oauth2-0.11/oauth2.el b/elpa/oauth2-0.11/oauth2.el deleted file mode 100644 index 893754c..0000000 --- a/elpa/oauth2-0.11/oauth2.el +++ /dev/null @@ -1,342 +0,0 @@ -;;; oauth2.el --- OAuth 2.0 Authorization Protocol - -;; Copyright (C) 2011-2016 Free Software Foundation, Inc - -;; Author: Julien Danjou -;; Version: 0.11 -;; Keywords: comm - -;; This file is part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: - -;; Implementation of the OAuth 2.0 draft. -;; -;; The main entry point is `oauth2-auth-and-store' which will return a token -;; structure. This token structure can be then used with -;; `oauth2-url-retrieve-synchronously' or `oauth2-url-retrieve' to retrieve -;; any data that need OAuth authentication to be accessed. -;; -;; If the token needs to be refreshed, the code handles it automatically and -;; store the new value of the access token. - -;;; Code: - -(eval-when-compile (require 'cl)) -(require 'plstore) -(require 'json) -(require 'url-http) - -(defun oauth2-request-authorization (auth-url client-id &optional scope state redirect-uri) - "Request OAuth authorization at AUTH-URL by launching `browse-url'. -CLIENT-ID is the client id provided by the provider. -It returns the code provided by the service." - (browse-url (concat auth-url - (if (string-match-p "\?" auth-url) "&" "?") - "client_id=" (url-hexify-string client-id) - "&response_type=code" - "&redirect_uri=" (url-hexify-string (or redirect-uri "urn:ietf:wg:oauth:2.0:oob")) - (if scope (concat "&scope=" (url-hexify-string scope)) "") - (if state (concat "&state=" (url-hexify-string state)) ""))) - (read-string "Enter the code your browser displayed: ")) - -(defun oauth2-request-access-parse () - "Parse the result of an OAuth request." - (goto-char (point-min)) - (when (search-forward-regexp "^$" nil t) - (json-read))) - -(defun oauth2-make-access-request (url data) - "Make an access request to URL using DATA in POST." - (let ((url-request-method "POST") - (url-request-data data) - (url-request-extra-headers - '(("Content-Type" . "application/x-www-form-urlencoded")))) - (with-current-buffer (url-retrieve-synchronously url) - (let ((data (oauth2-request-access-parse))) - (kill-buffer (current-buffer)) - data)))) - -(defstruct oauth2-token - plstore - plstore-id - client-id - client-secret - access-token - refresh-token - token-url - access-response) - -(defun oauth2-request-access (token-url client-id client-secret code &optional redirect-uri) - "Request OAuth access at TOKEN-URL. -The CODE should be obtained with `oauth2-request-authorization'. -Return an `oauth2-token' structure." - (when code - (let ((result - (oauth2-make-access-request - token-url - (concat - "client_id=" client-id - "&client_secret=" client-secret - "&code=" code - "&redirect_uri=" (url-hexify-string (or redirect-uri "urn:ietf:wg:oauth:2.0:oob")) - "&grant_type=authorization_code")))) - (make-oauth2-token :client-id client-id - :client-secret client-secret - :access-token (cdr (assoc 'access_token result)) - :refresh-token (cdr (assoc 'refresh_token result)) - :token-url token-url - :access-response result)))) - -;;;###autoload -(defun oauth2-refresh-access (token) - "Refresh OAuth access TOKEN. -TOKEN should be obtained with `oauth2-request-access'." - (setf (oauth2-token-access-token token) - (cdr (assoc 'access_token - (oauth2-make-access-request - (oauth2-token-token-url token) - (concat "client_id=" (oauth2-token-client-id token) - "&client_secret=" (oauth2-token-client-secret token) - "&refresh_token=" (oauth2-token-refresh-token token) - "&grant_type=refresh_token"))))) - ;; If the token has a plstore, update it - (let ((plstore (oauth2-token-plstore token))) - (when plstore - (plstore-put plstore (oauth2-token-plstore-id token) - nil `(:access-token - ,(oauth2-token-access-token token) - :refresh-token - ,(oauth2-token-refresh-token token) - :access-response - ,(oauth2-token-access-response token) - )) - (plstore-save plstore))) - token) - -;;;###autoload -(defun oauth2-auth (auth-url token-url client-id client-secret &optional scope state redirect-uri) - "Authenticate application via OAuth2." - (oauth2-request-access - token-url - client-id - client-secret - (oauth2-request-authorization - auth-url client-id scope state redirect-uri) - redirect-uri)) - -(defcustom oauth2-token-file (concat user-emacs-directory "oauth2.plstore") - "File path where store OAuth tokens." - :group 'oauth2 - :type 'file) - -(defun oauth2-compute-id (auth-url token-url resource-url) - "Compute an unique id based on URLs. -This allows to store the token in an unique way." - (secure-hash 'md5 (concat auth-url token-url resource-url))) - -;;;###autoload -(defun oauth2-auth-and-store (auth-url token-url resource-url client-id client-secret &optional redirect-uri) - "Request access to a resource and store it using `plstore'." - ;; We store a MD5 sum of all URL - (let* ((plstore (plstore-open oauth2-token-file)) - (id (oauth2-compute-id auth-url token-url resource-url)) - (plist (cdr (plstore-get plstore id)))) - ;; Check if we found something matching this access - (if plist - ;; We did, return the token object - (make-oauth2-token :plstore plstore - :plstore-id id - :client-id client-id - :client-secret client-secret - :access-token (plist-get plist :access-token) - :refresh-token (plist-get plist :refresh-token) - :token-url token-url - :access-response (plist-get plist :access-response)) - (let ((token (oauth2-auth auth-url token-url - client-id client-secret resource-url nil redirect-uri))) - ;; Set the plstore - (setf (oauth2-token-plstore token) plstore) - (setf (oauth2-token-plstore-id token) id) - (plstore-put plstore id nil `(:access-token - ,(oauth2-token-access-token token) - :refresh-token - ,(oauth2-token-refresh-token token) - :access-response - ,(oauth2-token-access-response token))) - (plstore-save plstore) - token)))) - -(defun oauth2-url-append-access-token (token url) - "Append access token to URL." - (concat url - (if (string-match-p "\?" url) "&" "?") - "access_token=" (oauth2-token-access-token token))) - -(defvar oauth--url-advice nil) -(defvar oauth--token-data) - -(defun oauth2-authz-bearer-header (token) - "Return 'Authoriztions: Bearer' header with TOKEN." - (cons "Authorization" (format "Bearer %s" token))) - -(defun oauth2-extra-headers (extra-headers) - "Return EXTRA-HEADERS with 'Authorization: Bearer' added." - (cons (oauth2-authz-bearer-header (oauth2-token-access-token (car oauth--token-data))) - extra-headers)) - - -;; FIXME: We should change URL so that this can be done without an advice. -(defadvice url-http-handle-authentication (around oauth-hack activate) - (if (not oauth--url-advice) - ad-do-it - (let ((url-request-method url-http-method) - (url-request-data url-http-data) - (url-request-extra-headers - (oauth2-extra-headers url-http-extra-headers)))) - (oauth2-refresh-access (car oauth--token-data)) - (url-retrieve-internal (cdr oauth--token-data) - url-callback-function - url-callback-arguments) - ;; This is to make `url' think it's done. - (when (boundp 'success) (setq success t)) ;For URL library in Emacs<24.4. - (setq ad-return-value t))) ;For URL library in Emacs≥24.4. - -;;;###autoload -(defun oauth2-url-retrieve-synchronously (token url &optional request-method request-data request-extra-headers) - "Retrieve an URL synchronously using TOKEN to access it. -TOKEN can be obtained with `oauth2-auth'." - (let* ((oauth--token-data (cons token url))) - (let ((oauth--url-advice t) ;Activate our advice. - (url-request-method request-method) - (url-request-data request-data) - (url-request-extra-headers - (oauth2-extra-headers request-extra-headers))) - (url-retrieve-synchronously url)))) - -;;;###autoload -(defun oauth2-url-retrieve (token url callback &optional - cbargs - request-method request-data request-extra-headers) - "Retrieve an URL asynchronously using TOKEN to access it. -TOKEN can be obtained with `oauth2-auth'. CALLBACK gets called with CBARGS -when finished. See `url-retrieve'." - ;; TODO add support for SILENT and INHIBIT-COOKIES. How to handle this in `url-http-handle-authentication'. - (let* ((oauth--token-data (cons token url))) - (let ((oauth--url-advice t) ;Activate our advice. - (url-request-method request-method) - (url-request-data request-data) - (url-request-extra-headers - (oauth2-extra-headers request-extra-headers))) - (url-retrieve url callback cbargs)))) - -;;;; ChangeLog: - -;; 2016-07-09 Julien Danjou -;; -;; oauth2: send authentication token via Authorization header -;; -;; 2014-01-28 Rüdiger Sonderfeld -;; -;; oauth2.el: Add support for async retrieve. -;; -;; * packages/oauth2/oauth2.el (oauth--tokens-need-renew): Remove. -;; (oauth--token-data): New variable. -;; (url-http-handle-authentication): Call `url-retrieve-internal' -;; directly instead of depending on `oauth--tokens-need-renew'. -;; (oauth2-url-retrieve-synchronously): Call `url-retrieve' once. -;; (oauth2-url-retrieve): New function. -;; -;; Signed-off-by: Rüdiger Sonderfeld -;; Signed-off-by: Julien Danjou -;; -;; 2013-07-22 Stefan Monnier -;; -;; * oauth2.el: Only require CL at compile time and avoid flet. -;; (success): Don't defvar. -;; (oauth--url-advice, oauth--tokens-need-renew): New dynbind variables. -;; (url-http-handle-authentication): Add advice. -;; (oauth2-url-retrieve-synchronously): Use the advice instead of flet. -;; -;; 2013-06-29 Julien Danjou -;; -;; oauth2: release 0.9, require url-http -;; -;; This is needed so that the `flet' calls doesn't restore the overriden -;; function to an unbound one. -;; -;; Signed-off-by: Julien Danjou -;; -;; 2012-08-01 Julien Danjou -;; -;; oauth2: upgrade to 0.8, add missing require on cl -;; -;; 2012-07-03 Julien Danjou -;; -;; oauth2: store access-reponse, bump versino to 0.7 -;; -;; 2012-06-25 Julien Danjou -;; -;; oauth2: add redirect-uri parameter, update to 0.6 -;; -;; 2012-05-29 Julien Danjou -;; -;; * packages/oauth2/oauth2.el: Revert fix URL double escaping, update to -;; 0.5 -;; -;; 2012-05-04 Julien Danjou -;; -;; * packages/oauth2/oauth2.el: Don't use aget, update to 0.4 -;; -;; 2012-04-19 Julien Danjou -;; -;; * packages/oauth2/oauth2.el: Fix URL double escaping, update to 0.3 -;; -;; 2011-12-20 Julien Danjou -;; -;; oauth2: update version 0.2 -;; -;; * oauth2: update version to 0.2 -;; -;; 2011-12-20 Julien Danjou -;; -;; oauth2: allow to use any HTTP request type -;; -;; * oauth2: allow to use any HTTP request type -;; -;; 2011-10-08 Julien Danjou -;; -;; * oauth2.el: Require json. -;; Fix compilation warning with success variable from url.el. -;; -;; 2011-09-26 Julien Danjou -;; -;; * packages/oauth2/oauth2.el (oauth2-request-authorization): Add missing -;; calls to url-hexify-string. -;; -;; 2011-09-26 Julien Danjou -;; -;; * packages/oauth2/oauth2.el: Reformat to avoid long lines. -;; -;; 2011-09-23 Julien Danjou -;; -;; New package oauth2 -;; - - -(provide 'oauth2) - -;;; oauth2.el ends here diff --git a/elpa/request-20160822.1659/request-autoloads.el b/elpa/request-20160822.1659/request-autoloads.el deleted file mode 100644 index 1243279..0000000 --- a/elpa/request-20160822.1659/request-autoloads.el +++ /dev/null @@ -1,15 +0,0 @@ -;;; request-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil nil ("request.el") (22533 17545 888645 569000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; request-autoloads.el ends here diff --git a/elpa/request-20160822.1659/request-pkg.el b/elpa/request-20160822.1659/request-pkg.el deleted file mode 100644 index 6b740ec..0000000 --- a/elpa/request-20160822.1659/request-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "request" "20160822.1659" "Compatible layer for URL request in Emacs" '((emacs "24") (cl-lib "0.5"))) diff --git a/elpa/request-20160822.1659/request.el b/elpa/request-20160822.1659/request.el deleted file mode 100644 index 7a879ee..0000000 --- a/elpa/request-20160822.1659/request.el +++ /dev/null @@ -1,1297 +0,0 @@ -;;; request.el --- Compatible layer for URL request in Emacs -*- lexical-binding: t; -*- - -;; Copyright (C) 2012 Takafumi Arakaki -;; Copyright (C) 1985-1986, 1992, 1994-1995, 1999-2012 -;; Free Software Foundation, Inc. - -;; Author: Takafumi Arakaki -;; Package-Requires: ((emacs "24") (cl-lib "0.5")) -;; Package-Version: 20160822.1659 -;; Version: 0.2.0 - -;; This file is NOT part of GNU Emacs. - -;; request.el is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; request.el is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with request.el. -;; If not, see . - -;;; Commentary: - -;; Request.el is a HTTP request library with multiple backends. It -;; supports url.el which is shipped with Emacs and curl command line -;; program. User can use curl when s/he has it, as curl is more reliable -;; than url.el. Library author can use request.el to avoid imposing -;; external dependencies such as curl to users while giving richer -;; experience for users who have curl. - -;; Following functions are adapted from GNU Emacs source code. -;; Free Software Foundation holds the copyright of them. -;; * `request--process-live-p' -;; * `request--url-default-expander' - -;;; Code: - -(eval-when-compile - (defvar url-http-method) - (defvar url-http-response-status)) - -(require 'cl-lib) -(require 'url) -(require 'mail-utils) - -(defgroup request nil - "Compatible layer for URL request in Emacs." - :group 'comm - :prefix "request-") - -(defconst request-version "0.2.0") - - -;;; Customize variables - -(defcustom request-storage-directory - (concat (file-name-as-directory user-emacs-directory) "request") - "Directory to store data related to request.el." - :type 'directory) - -(defcustom request-curl "curl" - "Executable for curl command." - :type 'string) - -(defcustom request-backend (if (executable-find request-curl) - 'curl - 'url-retrieve) - "Backend to be used for HTTP request. -Automatically set to `curl' if curl command is found." - :type '(choice (const :tag "cURL backend" curl) - (const :tag "url-retrieve backend" url-retrieve))) - -(defcustom request-timeout nil - "Default request timeout in second. -`nil' means no timeout." - :type '(choice (integer :tag "Request timeout seconds") - (boolean :tag "No timeout" nil))) - -(defcustom request-log-level -1 - "Logging level for request. -One of `error'/`warn'/`info'/`verbose'/`debug'. --1 means no logging." - :type '(choice (integer :tag "No logging" -1) - (const :tag "Level error" error) - (const :tag "Level warn" warn) - (const :tag "Level info" info) - (const :tag "Level Verbose" verbose) - (const :tag "Level DEBUG" debug))) - -(defcustom request-message-level 'warn - "Logging level for request. -See `request-log-level'." - :type '(choice (integer :tag "No logging" -1) - (const :tag "Level error" error) - (const :tag "Level warn" warn) - (const :tag "Level info" info) - (const :tag "Level Verbose" verbose) - (const :tag "Level DEBUG" debug))) - - -;;; Utilities - -(defun request--safe-apply (function &rest arguments) - (condition-case err - (apply #'apply function arguments) - ((debug error)))) - -(defun request--safe-call (function &rest arguments) - (request--safe-apply function arguments)) - -;; (defun request--url-no-cache (url) -;; "Imitate `cache=false' of `jQuery.ajax'. -;; See: http://api.jquery.com/jQuery.ajax/" -;; ;; FIXME: parse URL before adding ?_=TIME. -;; (concat url (format-time-string "?_=%s"))) - -(defmacro request--document-function (function docstring) - "Document FUNCTION with DOCSTRING. Use this for defstruct accessor etc." - (declare (indent defun) - (doc-string 2)) - `(put ',function 'function-documentation ,docstring)) - -(defun request--process-live-p (process) - "Copied from `process-live-p' for backward compatibility (Emacs < 24). -Adapted from lisp/subr.el. -FSF holds the copyright of this function: - Copyright (C) 1985-1986, 1992, 1994-1995, 1999-2012 - Free Software Foundation, Inc." - (memq (process-status process) '(run open listen connect stop))) - - -;;; Logging - -(defconst request--log-level-def - '(;; debugging - (blather . 60) (trace . 50) (debug . 40) - ;; information - (verbose . 30) (info . 20) - ;; errors - (warn . 10) (error . 0)) - "Named logging levels.") - -(defun request--log-level-as-int (level) - (if (integerp level) - level - (or (cdr (assq level request--log-level-def)) - 0))) - -(defvar request-log-buffer-name " *request-log*") - -(defun request--log-buffer () - (get-buffer-create request-log-buffer-name)) - -(defmacro request-log (level fmt &rest args) - (declare (indent 1)) - `(let ((level (request--log-level-as-int ,level)) - (log-level (request--log-level-as-int request-log-level)) - (msg-level (request--log-level-as-int request-message-level))) - (when (<= level (max log-level msg-level)) - (let ((msg (format "[%s] %s" ,level - (condition-case err - (format ,fmt ,@args) - (error (format " -!!! Logging error while executing: -%S -!!! Error: -%S" - ',args err)))))) - (when (<= level log-level) - (with-current-buffer (request--log-buffer) - (setq buffer-read-only t) - (let ((inhibit-read-only t)) - (goto-char (point-max)) - (insert msg "\n")))) - (when (<= level msg-level) - (message "REQUEST %s" msg)))))) - - -;;; HTTP specific utilities - -(defconst request--url-unreserved-chars - '(?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z - ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z - ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 - ?- ?_ ?. ?~) - "`url-unreserved-chars' copied from Emacs 24.3 release candidate. -This is used for making `request--urlencode-alist' RFC 3986 compliant -for older Emacs versions.") - -(defun request--urlencode-alist (alist) - ;; FIXME: make monkey patching `url-unreserved-chars' optional - (let ((url-unreserved-chars request--url-unreserved-chars)) - (cl-loop for sep = "" then "&" - for (k . v) in alist - concat sep - concat (url-hexify-string (format "%s" k)) - concat "=" - concat (url-hexify-string (format "%s" v))))) - - -;;; Header parser - -(defun request--parse-response-at-point () - "Parse the first header line such as \"HTTP/1.1 200 OK\"." - (when (re-search-forward "\\=[ \t\n]*HTTP/\\([0-9\\.]+\\) +\\([0-9]+\\)" nil t) - (list :version (match-string 1) - :code (string-to-number (match-string 2))))) - -(defun request--goto-next-body () - (re-search-forward "^\r\n")) - - -;;; Response object - -(cl-defstruct request-response - "A structure holding all relevant information of a request." - status-code history data error-thrown symbol-status url - done-p settings - ;; internal variables - -buffer -raw-header -timer -backend -tempfiles) - -(defmacro request--document-response (function docstring) - (declare (indent defun) - (doc-string 2)) - `(request--document-function ,function ,(concat docstring " - -.. This is an accessor for `request-response' object. - -\(fn RESPONSE)"))) - -(request--document-response request-response-status-code - "Integer HTTP response code (e.g., 200).") - -(request--document-response request-response-history - "Redirection history (a list of response object). -The first element is the oldest redirection. - -You can use restricted portion of functions for the response -objects in the history slot. It also depends on backend. Here -is the table showing what functions you can use for the response -objects in the history slot. - -==================================== ============== ============== -Slots Backends ------------------------------------- ----------------------------- -\\ curl url-retrieve -==================================== ============== ============== -request-response-url yes yes -request-response-header yes no -other functions no no -==================================== ============== ============== -") - -(request--document-response request-response-data - "Response parsed by the given parser.") - -(request--document-response request-response-error-thrown - "Error thrown during request. -It takes the form of ``(ERROR-SYMBOL . DATA)``, which can be -re-raised (`signal'ed) by ``(signal ERROR-SYMBOL DATA)``.") - -(request--document-response request-response-symbol-status - "A symbol representing the status of request (not HTTP response code). -One of success/error/timeout/abort/parse-error.") - -(request--document-response request-response-url - "Final URL location of response.") - -(request--document-response request-response-done-p - "Return t when the request is finished or aborted.") - -(request--document-response request-response-settings - "Keyword arguments passed to `request' function. -Some arguments such as HEADERS is changed to the one actually -passed to the backend. Also, it has additional keywords such -as URL which is the requested URL.") - -(defun request-response-header (response field-name) - "Fetch the values of RESPONSE header field named FIELD-NAME. - -It returns comma separated values when the header has multiple -field with the same name, as :RFC:`2616` specifies. - -Examples:: - - (request-response-header response - \"content-type\") ; => \"text/html; charset=utf-8\" - (request-response-header response - \"unknown-field\") ; => nil -" - (let ((raw-header (request-response--raw-header response))) - (when raw-header - (with-temp-buffer - (erase-buffer) - (insert raw-header) - ;; ALL=t to fetch all fields with the same name to get comma - ;; separated value [#rfc2616-sec4]_. - (mail-fetch-field field-name nil t))))) -;; .. [#rfc2616-sec4] RFC2616 says this is the right thing to do -;; (see http://tools.ietf.org/html/rfc2616.html#section-4.2). -;; Python's requests module does this too. - - -;;; Backend dispatcher - -(defconst request--backend-alist - '((url-retrieve - . ((request . request--url-retrieve) - (request-sync . request--url-retrieve-sync) - (terminate-process . delete-process) - (get-cookies . request--url-retrieve-get-cookies))) - (curl - . ((request . request--curl) - (request-sync . request--curl-sync) - (terminate-process . interrupt-process) - (get-cookies . request--curl-get-cookies)))) - "Map backend and method name to actual method (symbol). - -It's alist of alist, of the following form:: - - ((BACKEND . ((METHOD . FUNCTION) ...)) ...) - -It would be nicer if I can use EIEIO. But as CEDET is included -in Emacs by 23.2, using EIEIO means abandon older Emacs versions. -It is probably necessary if I need to support more backends. But -let's stick to manual dispatch for now.") -;; See: (view-emacs-news "23.2") - -(defun request--choose-backend (method) - "Return `fucall'able object for METHOD of current `request-backend'." - (assoc-default - method - (or (assoc-default request-backend request--backend-alist) - (error "%S is not valid `request-backend'." request-backend)))) - - -;;; Cookie - -(defun request-cookie-string (host &optional localpart secure) - "Return cookie string (like `document.cookie'). - -Example:: - - (request-cookie-string \"127.0.0.1\" \"/\") ; => \"key=value; key2=value2\" -" - (mapconcat (lambda (nv) (concat (car nv) "=" (cdr nv))) - (request-cookie-alist host localpart secure) - "; ")) - -(defun request-cookie-alist (host &optional localpart secure) - "Return cookies as an alist. - -Example:: - - (request-cookie-alist \"127.0.0.1\" \"/\") ; => ((\"key\" . \"value\") ...) -" - (funcall (request--choose-backend 'get-cookies) host localpart secure)) - - -;;; Main - -(cl-defun request-default-error-callback (url &key symbol-status - &allow-other-keys) - (request-log 'error - "Error (%s) while connecting to %s." symbol-status url)) - -(cl-defun request (url &rest settings - &key - (type "GET") - (params nil) - (data nil) - (files nil) - (parser nil) - (headers nil) - (success nil) - (error nil) - (complete nil) - (timeout request-timeout) - (status-code nil) - (sync nil) - (response (make-request-response)) - (unix-socket nil)) - "Send request to URL. - -Request.el has a single entry point. It is `request'. - -==================== ======================================================== -Keyword argument Explanation -==================== ======================================================== -TYPE (string) type of request to make: POST/GET/PUT/DELETE -PARAMS (alist) set \"?key=val\" part in URL -DATA (string/alist) data to be sent to the server -FILES (alist) files to be sent to the server (see below) -PARSER (symbol) a function that reads current buffer and return data -HEADERS (alist) additional headers to send with the request -SUCCESS (function) called on success -ERROR (function) called on error -COMPLETE (function) called on both success and error -TIMEOUT (number) timeout in second -STATUS-CODE (alist) map status code (int) to callback -SYNC (bool) If `t', wait until request is done. Default is `nil'. -==================== ======================================================== - - -* Callback functions - -Callback functions STATUS, ERROR, COMPLETE and `cdr's in element of -the alist STATUS-CODE take same keyword arguments listed below. For -forward compatibility, these functions must ignore unused keyword -arguments (i.e., it's better to use `&allow-other-keys' [#]_).:: - - (CALLBACK ; SUCCESS/ERROR/COMPLETE/STATUS-CODE - :data data ; whatever PARSER function returns, or nil - :error-thrown error-thrown ; (ERROR-SYMBOL . DATA), or nil - :symbol-status symbol-status ; success/error/timeout/abort/parse-error - :response response ; request-response object - ...) - -.. [#] `&allow-other-keys' is a special \"markers\" available in macros - in the CL library for function definition such as `cl-defun' and - `cl-function'. Without this marker, you need to specify all arguments - to be passed. This becomes problem when request.el adds new arguments - when calling callback functions. If you use `&allow-other-keys' - (or manually ignore other arguments), your code is free from this - problem. See info node `(cl) Argument Lists' for more information. - -Arguments data, error-thrown, symbol-status can be accessed by -`request-response-data', `request-response-error-thrown', -`request-response-symbol-status' accessors, i.e.:: - - (request-response-data RESPONSE) ; same as data - -Response object holds other information which can be accessed by -the following accessors: -`request-response-status-code', -`request-response-url' and -`request-response-settings' - -* STATUS-CODE callback - -STATUS-CODE is an alist of the following format:: - - ((N-1 . CALLBACK-1) - (N-2 . CALLBACK-2) - ...) - -Here, N-1, N-2,... are integer status codes such as 200. - - -* FILES - -FILES is an alist of the following format:: - - ((NAME-1 . FILE-1) - (NAME-2 . FILE-2) - ...) - -where FILE-N is a list of the form:: - - (FILENAME &key PATH BUFFER STRING MIME-TYPE) - -FILE-N can also be a string (path to the file) or a buffer object. -In that case, FILENAME is set to the file name or buffer name. - -Example FILES argument:: - - `((\"passwd\" . \"/etc/passwd\") ; filename = passwd - (\"scratch\" . ,(get-buffer \"*scratch*\")) ; filename = *scratch* - (\"passwd2\" . (\"password.txt\" :file \"/etc/passwd\")) - (\"scratch2\" . (\"scratch.txt\" :buffer ,(get-buffer \"*scratch*\"))) - (\"data\" . (\"data.csv\" :data \"1,2,3\\n4,5,6\\n\"))) - -.. note:: FILES is implemented only for curl backend for now. - As furl.el_ supports multipart POST, it should be possible to - support FILES in pure elisp by making furl.el_ another backend. - Contributions are welcome. - - .. _furl.el: http://code.google.com/p/furl-el/ - - -* PARSER function - -PARSER function takes no argument and it is executed in the -buffer with HTTP response body. The current position in the HTTP -response buffer is at the beginning of the buffer. As the HTTP -header is stripped off, the cursor is actually at the beginning -of the response body. So, for example, you can pass `json-read' -to parse JSON object in the buffer. To fetch whole response as a -string, pass `buffer-string'. - -When using `json-read', it is useful to know that the returned -type can be modified by `json-object-type', `json-array-type', -`json-key-type', `json-false' and `json-null'. See docstring of -each function for what it does. For example, to convert JSON -objects to plist instead of alist, wrap `json-read' by `lambda' -like this.:: - - (request - \"http://...\" - :parser (lambda () - (let ((json-object-type 'plist)) - (json-read))) - ...) - -This is analogous to the `dataType' argument of jQuery.ajax_. -Only this function can access to the process buffer, which -is killed immediately after the execution of this function. - -* SYNC - -Synchronous request is functional, but *please* don't use it -other than testing or debugging. Emacs users have better things -to do rather than waiting for HTTP request. If you want a better -way to write callback chains, use `request-deferred'. - -If you can't avoid using it (e.g., you are inside of some hook -which must return some value), make sure to set TIMEOUT to -relatively small value. - -Due to limitation of `url-retrieve-synchronously', response slots -`request-response-error-thrown', `request-response-history' and -`request-response-url' are unknown (always `nil') when using -synchronous request with `url-retrieve' backend. - -* Note - -API of `request' is somewhat mixture of jQuery.ajax_ (Javascript) -and requests.request_ (Python). - -.. _jQuery.ajax: http://api.jquery.com/jQuery.ajax/ -.. _requests.request: http://docs.python-requests.org -" - (request-log 'debug "REQUEST") - ;; FIXME: support CACHE argument (if possible) - ;; (unless cache - ;; (setq url (request--url-no-cache url))) - (unless error - (setq error (apply-partially #'request-default-error-callback url)) - (setq settings (plist-put settings :error error))) - (unless (or (stringp data) - (null data) - (assoc-string "Content-Type" headers t)) - (setq data (request--urlencode-alist data)) - (setq settings (plist-put settings :data data))) - (when params - (cl-assert (listp params) nil "PARAMS must be an alist. Given: %S" params) - (setq url (concat url (if (string-match-p "\\?" url) "&" "?") - (request--urlencode-alist params)))) - (setq settings (plist-put settings :url url)) - (setq settings (plist-put settings :response response)) - (setf (request-response-settings response) settings) - (setf (request-response-url response) url) - (setf (request-response--backend response) request-backend) - ;; Call `request--url-retrieve'(`-sync') or `request--curl'(`-sync'). - (apply (if sync - (request--choose-backend 'request-sync) - (request--choose-backend 'request)) - url settings) - (when timeout - (request-log 'debug "Start timer: timeout=%s sec" timeout) - (setf (request-response--timer response) - (run-at-time timeout nil - #'request-response--timeout-callback response))) - response) - -(defun request--clean-header (response) - "Strip off carriage returns in the header of REQUEST." - (request-log 'debug "-CLEAN-HEADER") - (let ((buffer (request-response--buffer response)) - (backend (request-response--backend response)) - sep-regexp) - (if (eq backend 'url-retrieve) - ;; FIXME: make this workaround optional. - ;; But it looks like sometimes `url-http-clean-headers' - ;; fails to cleanup. So, let's be bit permissive here... - (setq sep-regexp "^\r?$") - (setq sep-regexp "^\r$")) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (request-log 'trace - "(buffer-string) at %S =\n%s" buffer (buffer-string)) - (goto-char (point-min)) - (when (and (re-search-forward sep-regexp nil t) - ;; Are \r characters stripped off already?: - (not (equal (match-string 0) ""))) - (while (re-search-backward "\r$" (point-min) t) - (replace-match ""))))))) - -(defun request--cut-header (response) - "Cut the first header part in the buffer of RESPONSE and move it to -raw-header slot." - (request-log 'debug "-CUT-HEADER") - (let ((buffer (request-response--buffer response))) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (goto-char (point-min)) - (when (re-search-forward "^$" nil t) - (setf (request-response--raw-header response) - (buffer-substring (point-min) (point))) - (delete-region (point-min) (min (1+ (point)) (point-max)))))))) - -(defun request--parse-data (response parser) - "Run PARSER in current buffer if ERROR-THROWN is nil, -then kill the current buffer." - (request-log 'debug "-PARSE-DATA") - (let ((buffer (request-response--buffer response))) - (request-log 'debug "parser = %s" parser) - (when (and (buffer-live-p buffer) parser) - (with-current-buffer buffer - (request-log 'trace - "(buffer-string) at %S =\n%s" buffer (buffer-string)) - (goto-char (point-min)) - (setf (request-response-data response) (funcall parser)))))) - -(cl-defun request--callback (buffer &key parser success error complete - timeout status-code response - &allow-other-keys) - (request-log 'debug "REQUEST--CALLBACK") - (request-log 'debug "(buffer-string) =\n%s" - (when (buffer-live-p buffer) - (with-current-buffer buffer (buffer-string)))) - - ;; Sometimes BUFFER given as the argument is different from the - ;; buffer already set in RESPONSE. That's why it is reset here. - ;; FIXME: Refactor how BUFFER is passed around. - (setf (request-response--buffer response) buffer) - (request-response--cancel-timer response) - (cl-symbol-macrolet - ((error-thrown (request-response-error-thrown response)) - (symbol-status (request-response-symbol-status response)) - (data (request-response-data response)) - (done-p (request-response-done-p response))) - - ;; Parse response header - (request--clean-header response) - (request--cut-header response) - ;; Note: Try to do this even `error-thrown' is set. For example, - ;; timeout error can occur while downloading response body and - ;; header is there in that case. - - ;; Parse response body - (request-log 'debug "error-thrown = %S" error-thrown) - (condition-case err - (request--parse-data response parser) - (error - ;; If there was already an error (e.g. server timeout) do not set the - ;; status to `parse-error'. - (unless error-thrown - (setq symbol-status 'parse-error) - (setq error-thrown err) - (request-log 'error "Error from parser %S: %S" parser err)))) - (kill-buffer buffer) - (request-log 'debug "data = %s" data) - - ;; Determine `symbol-status' - (unless symbol-status - (setq symbol-status (if error-thrown 'error 'success))) - (request-log 'debug "symbol-status = %s" symbol-status) - - ;; Call callbacks - (let ((args (list :data data - :symbol-status symbol-status - :error-thrown error-thrown - :response response))) - (let* ((success-p (eq symbol-status 'success)) - (cb (if success-p success error)) - (name (if success-p "success" "error"))) - (when cb - (request-log 'debug "Executing %s callback." name) - (request--safe-apply cb args))) - - (let ((cb (cdr (assq (request-response-status-code response) - status-code)))) - (when cb - (request-log 'debug "Executing status-code callback.") - (request--safe-apply cb args))) - - (when complete - (request-log 'debug "Executing complete callback.") - (request--safe-apply complete args))) - - (setq done-p t) - - ;; Remove temporary files - ;; FIXME: Make tempfile cleanup more reliable. It is possible - ;; callback is never called. - (request--safe-delete-files (request-response--tempfiles response)))) - -(cl-defun request-response--timeout-callback (response) - (request-log 'debug "-TIMEOUT-CALLBACK") - (setf (request-response-symbol-status response) 'timeout) - (setf (request-response-error-thrown response) '(error . ("Timeout"))) - (let* ((buffer (request-response--buffer response)) - (proc (and (buffer-live-p buffer) (get-buffer-process buffer)))) - (when proc - ;; This will call `request--callback': - (funcall (request--choose-backend 'terminate-process) proc)) - - (cl-symbol-macrolet ((done-p (request-response-done-p response))) - (unless done-p - ;; This code should never be executed. However, it occurs - ;; sometimes with `url-retrieve' backend. - ;; FIXME: In Emacs 24.3.50 or later, this is always executed in - ;; request-get-timeout test. Find out if it is fine. - (request-log 'error "Callback is not called when stopping process! \ -Explicitly calling from timer.") - (when (buffer-live-p buffer) - (cl-destructuring-bind (&key code &allow-other-keys) - (with-current-buffer buffer - (goto-char (point-min)) - (request--parse-response-at-point)) - (setf (request-response-status-code response) code))) - (apply #'request--callback - buffer - (request-response-settings response)) - (setq done-p t))))) - -(defun request-response--cancel-timer (response) - (request-log 'debug "REQUEST-RESPONSE--CANCEL-TIMER") - (cl-symbol-macrolet ((timer (request-response--timer response))) - (when timer - (cancel-timer timer) - (setq timer nil)))) - - -(defun request-abort (response) - "Abort request for RESPONSE (the object returned by `request'). -Note that this function invoke ERROR and COMPLETE callbacks. -Callbacks may not be called immediately but called later when -associated process is exited." - (cl-symbol-macrolet ((buffer (request-response--buffer response)) - (symbol-status (request-response-symbol-status response)) - (done-p (request-response-done-p response))) - (let ((process (get-buffer-process buffer))) - (unless symbol-status ; should I use done-p here? - (setq symbol-status 'abort) - (setq done-p t) - (when (and - (processp process) ; process can be nil when buffer is killed - (request--process-live-p process)) - (funcall (request--choose-backend 'terminate-process) process)))))) - - -;;; Backend: `url-retrieve' - -(cl-defun request--url-retrieve-preprocess-settings - (&rest settings &key type data files headers &allow-other-keys) - (when files - (error "`url-retrieve' backend does not support FILES.")) - (when (and (equal type "POST") - data - (not (assoc-string "Content-Type" headers t))) - (push '("Content-Type" . "application/x-www-form-urlencoded") headers) - (setq settings (plist-put settings :headers headers))) - settings) - -(cl-defun request--url-retrieve (url &rest settings - &key type data timeout response - &allow-other-keys - &aux headers) - (setq settings (apply #'request--url-retrieve-preprocess-settings settings)) - (setq headers (plist-get settings :headers)) - (let* ((url-request-extra-headers headers) - (url-request-method type) - (url-request-data data) - (buffer (url-retrieve url #'request--url-retrieve-callback - (nconc (list :response response) settings))) - (proc (get-buffer-process buffer))) - (setf (request-response--buffer response) buffer) - (process-put proc :request-response response) - (request-log 'debug "Start querying: %s" url) - (set-process-query-on-exit-flag proc nil))) - -(cl-defun request--url-retrieve-callback (status &rest settings - &key response url - &allow-other-keys) - (declare (special url-http-method - url-http-response-status)) - (request-log 'debug "-URL-RETRIEVE-CALLBACK") - (request-log 'debug "status = %S" status) - (request-log 'debug "url-http-method = %s" url-http-method) - (request-log 'debug "url-http-response-status = %s" url-http-response-status) - - (setf (request-response-status-code response) url-http-response-status) - (let ((redirect (plist-get status :redirect))) - (when redirect - (setf (request-response-url response) redirect))) - ;; Construct history slot - (cl-loop for v in - (cl-loop with first = t - with l = nil - for (k v) on status by 'cddr - when (eq k :redirect) - if first - do (setq first nil) - else - do (push v l) - finally do (cons url l)) - do (let ((r (make-request-response :-backend 'url-retrieve))) - (setf (request-response-url r) v) - (push r (request-response-history response)))) - - (cl-symbol-macrolet ((error-thrown (request-response-error-thrown response)) - (status-error (plist-get status :error))) - (when (and error-thrown status-error) - (request-log 'warn - "Error %S thrown already but got another error %S from \ -`url-retrieve'. Ignoring it..." error-thrown status-error)) - (unless error-thrown - (setq error-thrown status-error))) - - (apply #'request--callback (current-buffer) settings)) - -(cl-defun request--url-retrieve-sync (url &rest settings - &key type data timeout response - &allow-other-keys - &aux headers) - (setq settings (apply #'request--url-retrieve-preprocess-settings settings)) - (setq headers (plist-get settings :headers)) - (let* ((url-request-extra-headers headers) - (url-request-method type) - (url-request-data data) - (buffer (if timeout - (with-timeout - (timeout - (setf (request-response-symbol-status response) - 'timeout) - (setf (request-response-done-p response) t) - nil) - (url-retrieve-synchronously url)) - (url-retrieve-synchronously url)))) - (setf (request-response--buffer response) buffer) - ;; It seems there is no way to get redirects and URL here... - (when buffer - ;; Fetch HTTP response code - (with-current-buffer buffer - (goto-char (point-min)) - (cl-destructuring-bind (&key version code) - (request--parse-response-at-point) - (setf (request-response-status-code response) code))) - ;; Parse response body, etc. - (apply #'request--callback buffer settings))) - response) - -(defun request--url-retrieve-get-cookies (host localpart secure) - (mapcar - (lambda (c) (cons (url-cookie-name c) (url-cookie-value c))) - (url-cookie-retrieve host localpart secure))) - - -;;; Backend: curl - -(defvar request--curl-cookie-jar nil - "Override what the function `request--curl-cookie-jar' returns. -Currently it is used only for testing.") - -(defun request--curl-cookie-jar () - "Cookie storage for curl backend." - (or request--curl-cookie-jar - (expand-file-name "curl-cookie-jar" request-storage-directory))) - -(defconst request--curl-write-out-template - (if (eq system-type 'windows-nt) - "\\n(:num-redirects %{num_redirects} :url-effective %{url_effective})" - "\\n(:num-redirects %{num_redirects} :url-effective \"%{url_effective}\")")) - -(defun request--curl-mkdir-for-cookie-jar () - (ignore-errors - (make-directory (file-name-directory (request--curl-cookie-jar)) t))) - -(cl-defun request--curl-command - (url &key type data headers timeout files* unix-socket - &allow-other-keys - &aux - (cookie-jar (convert-standard-filename - (expand-file-name (request--curl-cookie-jar))))) - (append - (list request-curl "--silent" "--include" - "--location" - ;; FIXME: test automatic decompression - "--compressed" - ;; FIMXE: this way of using cookie might be problem when - ;; running multiple requests. - "--cookie" cookie-jar "--cookie-jar" cookie-jar - "--write-out" request--curl-write-out-template) - (when unix-socket (list "--unix-socket" unix-socket)) - (cl-loop for (name filename path mime-type) in files* - collect "--form" - collect (format "%s=@%s;filename=%s%s" name path filename - (if mime-type - (format ";type=%s" mime-type) - ""))) - (when data (list "--data-binary" "@-")) - (when type (list "--request" type)) - (cl-loop for (k . v) in headers - collect "--header" - collect (format "%s: %s" k v)) - (list url))) - -(defun request--curl-normalize-files-1 (files get-temp-file) - (cl-loop for (name . item) in files - collect - (cl-destructuring-bind - (filename &key file buffer data mime-type) - (cond - ((stringp item) (list (file-name-nondirectory item) :file item)) - ((bufferp item) (list (buffer-name item) :buffer item)) - (t item)) - (unless (= (cl-loop for v in (list file buffer data) if v sum 1) 1) - (error "Only one of :file/:buffer/:data must be given. Got: %S" - (cons name item))) - (cond - (file - (list name filename file mime-type)) - (buffer - (let ((tf (funcall get-temp-file))) - (with-current-buffer buffer - (write-region (point-min) (point-max) tf nil 'silent)) - (list name filename tf mime-type))) - (data - (let ((tf (funcall get-temp-file))) - (with-temp-buffer - (erase-buffer) - (insert data) - (write-region (point-min) (point-max) tf nil 'silent)) - (list name filename tf mime-type))))))) - -(defun request--curl-normalize-files (files) - "Change FILES into a list of (NAME FILENAME PATH MIME-TYPE). -This is to make `request--curl-command' cleaner by converting -FILES to a homogeneous list. It returns a list (FILES* TEMPFILES) -where FILES* is a converted FILES and TEMPFILES is a list of -temporary file paths." - (let (tempfiles noerror) - (unwind-protect - (let* ((get-temp-file (lambda () - (let ((tf (make-temp-file "emacs-request-"))) - (push tf tempfiles) - tf))) - (files* (request--curl-normalize-files-1 files get-temp-file))) - (setq noerror t) - (list files* tempfiles)) - (unless noerror - ;; Remove temporary files only when an error occurs - (request--safe-delete-files tempfiles))))) - -(defun request--safe-delete-files (files) - "Remove FILES but do not raise error when failed to do so." - (mapc (lambda (f) (condition-case err - (delete-file f) - (error (request-log 'error - "Failed delete file %s. Got: %S" f err)))) - files)) - -(cl-defun request--curl (url &rest settings - &key type data files headers timeout response - &allow-other-keys) - "cURL-based request backend. - -Redirection handling strategy ------------------------------ - -curl follows redirection when --location is given. However, -all headers are printed when it is used with --include option. -Number of redirects is printed out sexp-based message using ---write-out option (see `request--curl-write-out-template'). -This number is used for removing extra headers and parse -location header from the last redirection header. - -Sexp at the end of buffer and extra headers for redirects are -removed from the buffer before it is shown to the parser function. -" - (request--curl-mkdir-for-cookie-jar) - (let* (;; Use pipe instead of pty. Otherwise, curl process hangs. - (process-connection-type nil) - ;; Avoid starting program in non-existing directory. - (home-directory (if (file-remote-p default-directory) - (with-parsed-tramp-file-name default-directory nil - (tramp-make-tramp-file-name method user host "~/")) - "~/")) - (default-directory (expand-file-name home-directory)) - (buffer (generate-new-buffer " *request curl*")) - (command (cl-destructuring-bind - (files* tempfiles) - (request--curl-normalize-files files) - (setf (request-response--tempfiles response) tempfiles) - (apply #'request--curl-command url :files* files* - settings))) - (proc (apply #'start-file-process "request curl" buffer command))) - (request-log 'debug "Run: %s" (mapconcat 'identity command " ")) - (setf (request-response--buffer response) buffer) - (process-put proc :request-response response) - (set-process-coding-system proc 'binary 'binary) - (set-process-query-on-exit-flag proc nil) - (set-process-sentinel proc #'request--curl-callback) - (when data - (process-send-string proc data) - (process-send-eof proc)))) - -(defun request--curl-read-and-delete-tail-info () - "Read a sexp at the end of buffer and remove it and preceding character. -This function moves the point at the end of buffer by side effect. -See also `request--curl-write-out-template'." - (let (forward-sexp-function) - (goto-char (point-max)) - (forward-sexp -1) - (let ((beg (1- (point)))) - (prog1 - (read (current-buffer)) - (delete-region beg (point-max)))))) - -(defconst request--cookie-reserved-re - (mapconcat - (lambda (x) (concat "\\(^" x "\\'\\)")) - '("comment" "commenturl" "discard" "domain" "max-age" "path" "port" - "secure" "version" "expires") - "\\|") - "Uninterested keys in cookie. -See \"set-cookie-av\" in http://www.ietf.org/rfc/rfc2965.txt") - -(defun request--consume-100-continue () - "Remove \"HTTP/* 100 Continue\" header at the point." - (cl-destructuring-bind (&key code &allow-other-keys) - (save-excursion (request--parse-response-at-point)) - (when (equal code 100) - (delete-region (point) (progn (request--goto-next-body) (point))) - ;; FIXME: Does this make sense? Is it possible to have multiple 100? - (request--consume-100-continue)))) - -(defun request--consume-200-connection-established () - "Remove \"HTTP/* 200 Connection established\" header at the point." - (when (looking-at-p "HTTP/1\\.0 200 Connection established") - (delete-region (point) (progn (request--goto-next-body) (point))))) - -(defun request--curl-preprocess () - "Pre-process current buffer before showing it to user." - (let (history) - (cl-destructuring-bind (&key num-redirects url-effective) - (request--curl-read-and-delete-tail-info) - (goto-char (point-min)) - (request--consume-100-continue) - (request--consume-200-connection-established) - (when (> num-redirects 0) - (cl-loop with case-fold-search = t - repeat num-redirects - ;; Do not store code=100 headers: - do (request--consume-100-continue) - do (let ((response (make-request-response - :-buffer (current-buffer) - :-backend 'curl))) - (request--clean-header response) - (request--cut-header response) - (push response history)))) - - (goto-char (point-min)) - (nconc (list :num-redirects num-redirects :url-effective url-effective - :history (nreverse history)) - (request--parse-response-at-point))))) - -(defun request--curl-absolutify-redirects (start-url redirects) - "Convert relative paths in REDIRECTS to absolute URLs. -START-URL is the URL requested." - (cl-loop for prev-url = start-url then url - for url in redirects - unless (string-match url-nonrelative-link url) - do (setq url (url-expand-file-name url prev-url)) - collect url)) - -(defun request--curl-absolutify-location-history (start-url history) - "Convert relative paths in HISTORY to absolute URLs. -START-URL is the URL requested." - (when history - (setf (request-response-url (car history)) start-url)) - (cl-loop for url in (request--curl-absolutify-redirects - start-url - (mapcar (lambda (response) - (request-response-header response "location")) - history)) - for response in (cdr history) - do (setf (request-response-url response) url))) - -(defun request--curl-callback (proc event) - (let* ((buffer (process-buffer proc)) - (response (process-get proc :request-response)) - (symbol-status (request-response-symbol-status response)) - (settings (request-response-settings response))) - (request-log 'debug "REQUEST--CURL-CALLBACK event = %s" event) - (request-log 'debug "REQUEST--CURL-CALLBACK proc = %S" proc) - (request-log 'debug "REQUEST--CURL-CALLBACK buffer = %S" buffer) - (request-log 'debug "REQUEST--CURL-CALLBACK symbol-status = %S" - symbol-status) - (cond - ((and (memq (process-status proc) '(exit signal)) - (/= (process-exit-status proc) 0)) - (setf (request-response-error-thrown response) (cons 'error event)) - (apply #'request--callback buffer settings)) - ((equal event "finished\n") - (cl-destructuring-bind (&key version code num-redirects history error - url-effective) - (condition-case err - (with-current-buffer buffer - (request--curl-preprocess)) - ((debug error) - (list :error err))) - (request--curl-absolutify-location-history (plist-get settings :url) - history) - (setf (request-response-status-code response) code) - (setf (request-response-url response) url-effective) - (setf (request-response-history response) history) - (setf (request-response-error-thrown response) - (or error (when (>= code 400) `(error . (http ,code))))) - (apply #'request--callback buffer settings)))))) - -(cl-defun request--curl-sync (url &rest settings &key response &allow-other-keys) - ;; To make timeout work, use polling approach rather than using - ;; `call-process'. - (let (finished) - (prog1 (apply #'request--curl url - :complete (lambda (&rest _) (setq finished t)) - settings) - (let ((proc (get-buffer-process (request-response--buffer response)))) - (while (and (not finished) (request--process-live-p proc)) - (accept-process-output proc)))))) - -(defun request--curl-get-cookies (host localpart secure) - (request--netscape-get-cookies (request--curl-cookie-jar) - host localpart secure)) - - -;;; Netscape cookie.txt parser - -(defun request--netscape-cookie-parse () - "Parse Netscape/Mozilla cookie format." - (goto-char (point-min)) - (let ((tsv-re (concat "^\\=" - (cl-loop repeat 6 concat "\\([^\t\n]+\\)\t") - "\\(.*\\)")) - cookies) - (while - (and - (cond - ((re-search-forward "^\\=#" nil t)) - ((re-search-forward "^\\=$" nil t)) - ((re-search-forward tsv-re) - (push (cl-loop for i from 1 to 7 collect (match-string i)) - cookies) - t)) - (= (forward-line 1) 0) - (not (= (point) (point-max))))) - (setq cookies (nreverse cookies)) - (cl-loop for (domain flag path secure expiration name value) in cookies - collect (list domain - (equal flag "TRUE") - path - (equal secure "TRUE") - (string-to-number expiration) - name - value)))) - -(defun request--netscape-filter-cookies (cookies host localpart secure) - (cl-loop for (domain flag path secure-1 expiration name value) in cookies - when (and (equal domain host) - (equal path localpart) - (or secure (not secure-1))) - collect (cons name value))) - -(defun request--netscape-get-cookies (filename host localpart secure) - (when (file-readable-p filename) - (with-temp-buffer - (erase-buffer) - (insert-file-contents filename) - (request--netscape-filter-cookies (request--netscape-cookie-parse) - host localpart secure)))) - - -;;; Monkey patches for url.el - -(defun request--url-default-expander (urlobj defobj) - "Adapted from lisp/url/url-expand.el. -FSF holds the copyright of this function: - Copyright (C) 1999, 2004-2012 Free Software Foundation, Inc." - ;; The default expansion routine - urlobj is modified by side effect! - (if (url-type urlobj) - ;; Well, they told us the scheme, let's just go with it. - nil - (setf (url-type urlobj) (or (url-type urlobj) (url-type defobj))) - (setf (url-port urlobj) (or (url-portspec urlobj) - (and (string= (url-type urlobj) - (url-type defobj)) - (url-port defobj)))) - (if (not (string= "file" (url-type urlobj))) - (setf (url-host urlobj) (or (url-host urlobj) (url-host defobj)))) - (if (string= "ftp" (url-type urlobj)) - (setf (url-user urlobj) (or (url-user urlobj) (url-user defobj)))) - (if (string= (url-filename urlobj) "") - (setf (url-filename urlobj) "/")) - ;; If the object we're expanding from is full, then we are now - ;; full. - (unless (url-fullness urlobj) - (setf (url-fullness urlobj) (url-fullness defobj))) - (if (string-match "^/" (url-filename urlobj)) - nil - (let ((query nil) - (file nil) - (sepchar nil)) - (if (string-match "[?#]" (url-filename urlobj)) - (setq query (substring (url-filename urlobj) (match-end 0)) - file (substring (url-filename urlobj) 0 (match-beginning 0)) - sepchar (substring (url-filename urlobj) (match-beginning 0) (match-end 0))) - (setq file (url-filename urlobj))) - ;; We use concat rather than expand-file-name to combine - ;; directory and file name, since urls do not follow the same - ;; rules as local files on all platforms. - (setq file (url-expander-remove-relative-links - (concat (url-file-directory (url-filename defobj)) file))) - (setf (url-filename urlobj) - (if query (concat file sepchar query) file)))))) - -(defadvice url-default-expander - (around request-monkey-patch-url-default-expander (urlobj defobj)) - "Monkey patch `url-default-expander' to fix bug #12374. -This patch is applied to Emacs trunk at revno 111291: - http://bzr.savannah.gnu.org/lh/emacs/trunk/revision/111291. -Without this patch, port number is not treated when using -`url-expand-file-name'. -See: http://thread.gmane.org/gmane.emacs.devel/155698" - (setq ad-return-value (request--url-default-expander urlobj defobj))) - -(unless (equal (url-expand-file-name "/path" "http://127.0.0.1:8000") - "http://127.0.0.1:8000/path") - (ad-enable-advice 'url-default-expander - 'around - 'request-monkey-patch-url-default-expander) - (ad-activate 'url-default-expander)) - - -(eval-when-compile (require 'url-http) - (defvar url-http-no-retry) - (defvar url-http-extra-headers) - (defvar url-http-data) - (defvar url-callback-function) - (defvar url-callback-arguments)) -(declare-function url-http-idle-sentinel "url-http") -(declare-function url-http-activate-callback "url-http") -(declare-function url-http "url-http") -(declare-function url-http-parse-headers "url-http") - -(defun request--url-http-end-of-document-sentinel (proc why) - "Adapted from lisp/url/url-http.el. -FSF holds the copyright of this function: - Copyright (C) 1999, 2001, 2004-2012 Free Software Foundation, Inc." - (url-http-debug "url-http-end-of-document-sentinel in buffer (%s)" - (process-buffer proc)) - (url-http-idle-sentinel proc why) - (when (buffer-name (process-buffer proc)) - (with-current-buffer (process-buffer proc) - (goto-char (point-min)) - (cond ((not (looking-at "HTTP/")) - (if url-http-no-retry - ;; HTTP/0.9 just gets passed back no matter what - (url-http-activate-callback) - ;; Call `url-http' again if our connection expired. - (erase-buffer) - (let ((url-request-method url-http-method) - (url-request-extra-headers url-http-extra-headers) - (url-request-data url-http-data)) - (url-http url-current-object url-callback-function - url-callback-arguments (current-buffer))))) - ((url-http-parse-headers) - (url-http-activate-callback)))))) - -(defadvice url-http-end-of-document-sentinel - (around request-monkey-patch-url-http-end-of-document-sentinel (proc why)) - "Monkey patch `url-http-end-of-document-sentinel' to fix bug #11469. -This patch is applied to Emacs trunk at revno 111291: - http://bzr.savannah.gnu.org/lh/emacs/trunk/revision/111291. -Without this patch, PUT method fails every two times. -See: http://thread.gmane.org/gmane.emacs.devel/155697" - (setq ad-return-value (request--url-http-end-of-document-sentinel proc why))) - -(when (and (version< "24" emacs-version) - (version< emacs-version "24.3.50.1")) - (ad-enable-advice 'url-http-end-of-document-sentinel - 'around - 'request-monkey-patch-url-http-end-of-document-sentinel) - (ad-activate 'url-http-end-of-document-sentinel)) - - -(provide 'request) - -;;; request.el ends here diff --git a/elpa/slack-20160928.2036/slack-autoloads.el b/elpa/slack-20160928.2036/slack-autoloads.el deleted file mode 100644 index 74b7633..0000000 --- a/elpa/slack-20160928.2036/slack-autoloads.el +++ /dev/null @@ -1,47 +0,0 @@ -;;; slack-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil "slack" "slack.el" (22533 17549 313723 713000)) -;;; Generated autoloads from slack.el - -(autoload 'slack-start "slack" "\ - - -\(fn &optional TEAM)" t nil) - -;;;*** - -;;;### (autoloads nil "slack-team" "slack-team.el" (22533 17549 403725 -;;;;;; 767000)) -;;; Generated autoloads from slack-team.el - -(autoload 'slack-register-team "slack-team" "\ -PLIST must contain :name :client-id :client-secret with value. -setting :token will reduce your configuration step. -you will notified when receive message with channel included in subscribed-chennels. -if :default is t and `slack-prefer-current-team' is t, skip selecting team when channels listed. -you can change current-team with `slack-change-current-team' - -\(fn &rest PLIST)" t nil) - -;;;*** - -;;;### (autoloads nil nil ("slack-bot-message.el" "slack-buffer.el" -;;;;;; "slack-channel.el" "slack-file.el" "slack-group.el" "slack-im.el" -;;;;;; "slack-message-editor.el" "slack-message-formatter.el" "slack-message-notification.el" -;;;;;; "slack-message-reaction.el" "slack-message-sender.el" "slack-message.el" -;;;;;; "slack-pkg.el" "slack-reaction.el" "slack-reminder.el" "slack-reply.el" -;;;;;; "slack-request.el" "slack-room.el" "slack-search.el" "slack-user-message.el" -;;;;;; "slack-user.el" "slack-util.el" "slack-websocket.el") (22533 -;;;;;; 17549 504728 72000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; slack-autoloads.el ends here diff --git a/elpa/slack-20160928.2036/slack-bot-message.el b/elpa/slack-20160928.2036/slack-bot-message.el deleted file mode 100644 index 14ae186..0000000 --- a/elpa/slack-20160928.2036/slack-bot-message.el +++ /dev/null @@ -1,89 +0,0 @@ -;;; slack-bot-message.el --- bot message class -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-message) -(require 'slack-message-formatter) - -(defun slack-find-bot (id team) - (with-slots (bots) team - (cl-find-if (lambda (bot) - (string= id (plist-get bot :id))) - bots))) - -(defmethod slack-bot-name ((m slack-bot-message) team) - (if (slot-boundp m 'bot-id) - (let ((bot (slack-find-bot (oref m bot-id) team))) - (if bot - (plist-get bot :name) - (oref m username))) - (oref m username))) - -(defmethod slack-message-to-alert ((m slack-bot-message) team) - (let ((text (if (slot-boundp m 'text) - (oref m text)))) - (with-slots (attachments) m - (if (and text (< 0 (length text))) - (slack-message-unescape-string text team) - (let ((attachment-string (mapconcat #'slack-attachment-to-alert attachments " "))) - (slack-message-unescape-string attachment-string team)))))) - -(defmethod slack-message-sender-name ((m slack-bot-message) team) - (slack-bot-name m team)) - -(defmethod slack-attachment-to-string ((a slack-attachment)) - (with-slots (fallback text pretext title title-link) a - (if (and pretext title text) - (mapconcat #'identity - (cl-remove-if #'null (list pretext title title-link text)) - "\n") - fallback))) - -(defmethod slack-attachment-to-string((a slack-shared-message)) - (with-slots (fallback text author-name ts channel-name color from-url) a - (let* ((header-property '(:weight bold)) - (footer-property '(:height 0.8)) - (pad-property '(:weight ultra-bold)) - (pad (propertize "|" 'face pad-property)) - (header (concat pad "\t" - (propertize author-name 'face header-property))) - (body (format "%s\t%s" pad (mapconcat #'identity - (split-string text "\n") - (format "\n\t%s\t" pad)))) - (footer (concat pad "\t" - (propertize - (format "%s %s" channel-name (slack-message-time-to-string ts)) - 'face footer-property)))) - (format "\t%s\n \t%s\n \t%s" - header - body - footer)))) - -(defmethod slack-attachment-to-alert ((a slack-attachment)) - (oref a fallback)) - -(provide 'slack-bot-message) -;;; slack-bot-message.el ends here diff --git a/elpa/slack-20160928.2036/slack-buffer.el b/elpa/slack-20160928.2036/slack-buffer.el deleted file mode 100644 index a1c91ee..0000000 --- a/elpa/slack-20160928.2036/slack-buffer.el +++ /dev/null @@ -1,327 +0,0 @@ -;;; slack-buffer.el --- slack buffer -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'lui) -(require 'slack-room) - -(defvar lui-prompt-string "> ") - -(defvar slack-mode-map - (let ((map (make-sparse-keymap))) - ;; (define-key map (kbd "C-s C-r") #'slack-room-update-messages) - ;; (define-key map (kbd "C-s C-b") #'slack-message-write-another-buffer) - map)) - -(define-derived-mode slack-mode lui-mode "Slack" - "" - (lui-set-prompt lui-prompt-string) - (setq lui-input-function 'slack-message--send)) - -(define-derived-mode slack-info-mode lui-mode "Slack Info" - "" - (lui-set-prompt lui-prompt-string)) - -(defvar slack-current-room-id) -(defvar slack-current-team-id) -(defvar slack-current-message nil) -(defcustom slack-buffer-emojify nil - "Show emoji with `emojify' if true." - :group 'slack) - -(defmacro slack-buffer-widen (&rest body) - `(save-excursion - (save-restriction - (widen) - ,@body))) - -(defun slack-get-buffer-create (room) - (let* ((buf-name (slack-room-buffer-name room)) - (buffer (get-buffer buf-name))) - (unless buffer - (setq buffer (generate-new-buffer buf-name)) - (with-current-buffer buffer - (slack-mode) - (slack-buffer-insert-previous-link room) - (add-hook 'kill-buffer-hook 'slack-reset-room-last-read nil t) - (add-hook 'lui-pre-output-hook 'slack-buffer-add-last-ts-property nil t) - (add-hook 'lui-post-output-hook 'slack-buffer-add-ts-property nil t))) - buffer)) - -(defmethod slack-buffer-set-current-room-id ((room slack-room)) - (set (make-local-variable 'slack-current-room-id) (oref room id))) - -(defun slack-buffer-set-current-team-id (team) - (set (make-local-variable 'slack-current-team-id) (oref team id))) - -(defun slack-buffer-enable-emojify () - (if slack-buffer-emojify - (let ((emojify (require 'emojify nil t))) - (unless emojify - (error "Emojify is not installed")) - (emojify-mode t)))) - -(defun slack-buffer-goto (ts) - (let ((point (slack-buffer-ts-eq (point-min) (point-max) ts))) - (when point - (goto-char point)))) - -(defmethod slack-buffer-insert-previous-link ((room slack-room)) - (let ((oldest (slack-room-prev-link-info room))) - (if oldest - (slack-buffer-widen - (let ((inhibit-read-only t)) - (goto-char (point-min)) - (insert - (concat - (propertize "(load more message)" - 'face '(:underline t) - 'oldest oldest - 'keymap (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") - #'slack-room-load-prev-messages) - map)) - "\n\n")) - (set-marker lui-output-marker (point))))))) - -(defmethod slack-buffer-insert-prev-messages ((room slack-room) team oldest-ts) - (slack-buffer-widen - (let ((messages (slack-room-prev-messages room oldest-ts))) - (if messages - (progn - (slack-buffer-insert-previous-link room) - (cl-loop for m in messages - do (slack-buffer-insert m team t))) - (set-marker lui-output-marker (point-min)) - (lui-insert "(no more messages)\n")) - (slack-buffer-recover-lui-output-marker)))) - -(cl-defun slack-buffer-create (room team - &key - (insert-func - #'slack-buffer-insert-messages) - (type 'message)) - (cl-labels - ((get-buffer (type room) - (cl-ecase type - (message (slack-get-buffer-create room)) - (info (slack-get-info-buffer-create room))))) - (let* ((buffer (get-buffer type room))) - (with-current-buffer buffer - (if insert-func - (funcall insert-func room team)) - (slack-buffer-set-current-room-id room) - (slack-buffer-set-current-team-id team) - (slack-buffer-enable-emojify)) - buffer))) - -(defun slack-buffer-add-last-ts-property () - (when slack-current-message - (add-text-properties - (point-min) (point-max) - `(slack-last-ts ,lui-time-stamp-last)))) - -(defun slack-buffer-add-ts-property () - (when slack-current-message - (add-text-properties - (point-min) (point-max) - `(ts ,(oref slack-current-message ts))))) - -(defun slack-buffer-insert (message team &optional not-tracked-p) - (let ((lui-time-stamp-time (slack-message-time-stamp message)) - (beg lui-input-marker) - (inhibit-read-only t)) - (let ((slack-current-message message)) - (lui-insert (slack-message-to-string message team) not-tracked-p)))) - -(defun slack-buffer-insert-messages (room team) - (let* ((sorted (slack-room-sorted-messages room)) - (messages (slack-room-latest-messages room sorted))) - (if messages - (progn - ;; (slack-buffer-insert-previous-link room) - (cl-loop for m in messages - do (slack-buffer-insert m team t)) - (let ((latest-message (car (last messages)))) - (slack-room-update-last-read room latest-message) - (slack-room-update-mark room team latest-message))) - (unless (eq 0 (oref room unread-count-display)) - (let ((latest-message (car (last sorted)))) - (slack-room-update-mark room team latest-message)))))) - -(defun slack-buffer-show-typing-p (buffer) - (cl-case slack-typing-visibility - ('frame (slack-buffer-in-current-frame buffer)) - ('buffer (slack-buffer-current-p buffer)) - ('never nil))) - -(defun slack-buffer-current-p (buffer) - (if buffer - (string= (buffer-name buffer) - (buffer-name (current-buffer))))) - -(defun slack-buffer-in-current-frame (buffer) - (if buffer - (cl-member (buffer-name buffer) - (mapcar #'buffer-name - (mapcar #'window-buffer (window-list))) - :test #'string=))) - -(cl-defun slack-buffer-update (room msg team &key replace) - (let* ((buf-name (slack-room-buffer-name room)) - (buffer (get-buffer buf-name))) - (if buffer - (progn - (if (slack-buffer-in-current-frame buffer) - (slack-room-update-mark room team msg) - (slack-room-inc-unread-count room)) - (if replace - (slack-buffer-replace buffer msg) - (with-current-buffer buffer - (slack-room-update-last-read room msg) - (slack-buffer-insert msg team)))) - (slack-room-inc-unread-count room)))) - -(defmacro slack-buffer-goto-char (find-point &rest else) - `(let* ((cur-point (point)) - (ts (get-text-property cur-point 'ts))) - (let ((next-point ,find-point)) - (if next-point - (goto-char next-point) - (if (< 0 (length ',else)) - ,@else))))) - -(defun slack-buffer-goto-next-message () - (interactive) - (slack-buffer-goto-char - (slack-buffer-next-point cur-point (point-max) ts) - (slack-buffer-goto-first-message))) - -(defun slack-buffer-goto-prev-message () - (interactive) - (slack-buffer-goto-char - (slack-buffer-prev-point cur-point (point-min) ts) - (slack-buffer-goto-last-message))) - -(defun slack-buffer-goto-first-message () - (interactive) - (goto-char - (slack-buffer-next-point (point-min) (point-max) "0"))) - -(defun slack-buffer-goto-last-message () - (interactive) - (goto-char - (slack-buffer-prev-point (point-max) (point-min) (format-time-string "%s")))) - -(defun slack-buffer-header-p (point) - (let ((face (get-text-property point 'face))) - (string= (format "%s" face) "slack-message-output-header"))) - -(defun slack-buffer-next-point (start end ts) - (cl-loop for i from start to end - if (and (string< ts - (get-text-property i 'ts)) - (slack-buffer-header-p i)) - return i)) - -(defun slack-buffer-prev-point (start end ts) - (cl-loop for i from start downto end - if (and (string< (get-text-property i 'ts) - ts) - (slack-buffer-header-p i)) - return i)) - -(defun slack-buffer-ts-eq (start end ts) - (if (and start end) - (cl-loop for i from start to end - if (string= (get-text-property i 'ts) - ts) - return i))) - -(defun slack-buffer-ts-not-eq (start end ts) - (if (and start end) - (cl-loop for i from start to end - if (not (string= (get-text-property i 'ts) - ts)) - return i))) - -(defun slack-buffer-replace (buffer msg) - (with-current-buffer buffer - (slack-buffer-widen - (let* ((cur-point (point)) - (ts (oref msg ts)) - (beg (slack-buffer-ts-eq (point-min) (point-max) ts)) - (end (slack-buffer-ts-not-eq beg (point-max) ts))) - (if (and beg end) - (let ((inhibit-read-only t) - (lui-time-stamp-last (get-text-property beg 'slack-last-ts))) - (delete-region beg end) - (set-marker lui-output-marker beg) - (slack-buffer-insert msg - (slack-team-find slack-current-team-id)) - - (slack-buffer-recover-lui-output-marker) - (slack-buffer-goto ts))))))) - -(defun slack-buffer-recover-lui-output-marker () - (set-marker lui-output-marker (- (marker-position - lui-input-marker) - - (length lui-prompt-string)))) - -(defun slack-get-info-buffer-create (room) - (let* ((buf-name (slack-room-buffer-name room)) - (buffer (get-buffer buf-name))) - (unless buffer - (setq buffer (generate-new-buffer buf-name)) - (with-current-buffer buffer - (slack-info-mode) - (slack-buffer-insert-previous-link room) - (add-hook 'kill-buffer-hook 'slack-reset-room-last-read nil t) - (add-hook 'lui-pre-output-hook 'slack-buffer-add-last-ts-property nil t) - (add-hook 'lui-post-output-hook 'slack-buffer-add-ts-property nil t))) - buffer)) - -(defun slack-buffer-create-info (buf-name insert-func) - (let ((buf (slack-get-info-buffer-create buf-name))) - (with-current-buffer buf - (setq buffer-read-only nil) - (erase-buffer) - (goto-char (point-min)) - (funcall insert-func) - (goto-char (point-max)) - (setq buffer-read-only t) - (slack-buffer-enable-emojify)) - buf)) - -(defun slack-reset-room-last-read () - (let ((room (slack-room-find slack-current-room-id - (slack-team-find slack-current-team-id)))) - (slack-room-update-last-read room - (slack-message "msg" :ts "0")))) - -(provide 'slack-buffer) -;;; slack-buffer.el ends here diff --git a/elpa/slack-20160928.2036/slack-channel.el b/elpa/slack-20160928.2036/slack-channel.el deleted file mode 100644 index d3a2d5d..0000000 --- a/elpa/slack-20160928.2036/slack-channel.el +++ /dev/null @@ -1,236 +0,0 @@ -;;; slack-channel.el ---slack channel implement -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-group) -(require 'slack-buffer) -(require 'slack-util) - -(defvar slack-buffer-function) - -(defconst slack-channel-history-url "https://slack.com/api/channels.history") -(defconst slack-channel-list-url "https://slack.com/api/channels.list") -(defconst slack-channel-buffer-name "*Slack - Channel*") -(defconst slack-channel-update-mark-url "https://slack.com/api/channels.mark") -(defconst slack-create-channel-url "https://slack.com/api/channels.create") -(defconst slack-channel-rename-url "https://slack.com/api/channels.rename") -(defconst slack-channel-invite-url "https://slack.com/api/channels.invite") -(defconst slack-channel-leave-url "https://slack.com/api/channels.leave") -(defconst slack-channel-join-url "https://slack.com/api/channels.join") -(defconst slack-channel-info-url "https://slack.com/api/channels.info") -(defconst slack-channel-archive-url "https://slack.com/api/channels.archive") -(defconst slack-channel-unarchive-url "https://slack.com/api/channels.unarchive") - -(defclass slack-channel (slack-group) - ((is-member :initarg :is_member) - (num-members :initarg :num_members))) - -(defmethod slack-room-buffer-name ((room slack-channel)) - (concat slack-channel-buffer-name - " : " - (slack-room-name-with-team-name room))) - -(defun slack-channel-names (team &optional filter) - (with-slots (channels) team - (slack-room-names channels filter))) - -(defmethod slack-room-member-p ((room slack-channel)) - (oref room is-member)) - -(defun slack-channel-select () - (interactive) - (let ((team (slack-team-select))) - (slack-room-select - (cl-loop for team in (list team) - for channels = (oref team channels) - nconc channels)))) - -(defun slack-channel-list-update () - (interactive) - (let ((team (slack-team-select))) - (cl-labels ((on-list-update - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-list-update") - (oset team channels - (mapcar #'(lambda (d) - (slack-room-create d team 'slack-channel)) - (plist-get data :channels))) - (message "Slack Channel List Updated")))) - (slack-room-list-update slack-channel-list-url - #'on-list-update - team - :sync nil)))) - -(defmethod slack-room-update-mark-url ((_room slack-channel)) - slack-channel-update-mark-url) - -(defun slack-create-channel () - (interactive) - (let ((team (slack-team-select))) - (cl-labels - ((on-create-channel (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-create")))) - (slack-create-room slack-create-channel-url - team - #'on-create-channel)))) - -(defun slack-channel-rename () - (interactive) - (slack-room-rename slack-channel-rename-url - #'slack-channel-names)) - -(defun slack-channel-invite () - (interactive) - (slack-room-invite slack-channel-invite-url - #'slack-channel-names)) - -(defun slack-channel-leave () - (interactive) - (let* ((team (slack-team-select)) - (channel (slack-current-room-or-select - #'(lambda () - (slack-channel-names - team - #'(lambda (channels) - (cl-remove-if-not #'slack-room-member-p - channels))))))) - (cl-labels - ((on-channel-leave (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-leave") - (oset channel is-member nil) - (message "Left Channel: %s" - (slack-room-name channel))))) - (slack-room-request-with-id slack-channel-leave-url - (oref channel id) - team - #'on-channel-leave)))) - -(defun slack-channel-join () - (interactive) - (cl-labels - ((filter-channel (channels) - (cl-remove-if - #'(lambda (c) - (or (slack-room-member-p c) - (slack-room-archived-p c))) - channels))) - (let* ((team (slack-team-select)) - (channel (slack-current-room-or-select - #'(lambda () - (slack-channel-names team - #'filter-channel))))) - (cl-labels - ((on-channel-join (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-join")))) - (slack-request - slack-channel-join-url - team - :params (list (cons "name" (slack-room-name channel))) - :sync nil - :success #'on-channel-join)))) - ) - -(defun slack-channel-create-from-info (id team) - (cl-labels - ((on-create-from-info - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-create-from-info") - (let* ((c-data (plist-get data :channel)) - (latest (plist-get c-data :latest))) - (if latest - (plist-put c-data :latest - (slack-message-create latest))) - (if (plist-get c-data :is_channel) - (let ((channel - (slack-room-create c-data team 'slack-channel))) - (with-slots (channels) team - (push channel channels)) - (message "Channel: %s created" - (slack-room-name-with-team-name channel)))))))) - (slack-channel-fetch-info id team #'on-create-from-info))) - -(defun slack-channel-fetch-info (id team success) - (slack-request - slack-channel-info-url - team - :sync nil - :params (list (cons "channel" id)) - :success success)) - -(defun slack-channel-archive () - (interactive) - (let* ((team (slack-team-select)) - (channel (slack-current-room-or-select - #'(lambda () - (slack-channel-names - team - #'(lambda (channels) - (cl-remove-if #'slack-room-archived-p - channels))))))) - (cl-labels - ((on-channel-archive (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-archive")))) - (slack-room-request-with-id slack-channel-archive-url - (oref channel id) - team - #'on-channel-archive)))) - -(defun slack-channel-unarchive () - (interactive) - (let* ((team (slack-team-select)) - (channel (slack-current-room-or-select - #'(lambda () - (slack-channel-names - team - #'(lambda (channels) - (cl-remove-if-not #'slack-room-archived-p - channels))))))) - (cl-labels - ((on-channel-unarchive (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-channel-unarchive")))) - (slack-room-request-with-id slack-channel-unarchive-url - (oref channel id) - team - #'on-channel-unarchive)))) - -(defmethod slack-room-history-url ((_room slack-channel)) - slack-channel-history-url) - -(defmethod slack-room-subscribedp ((room slack-channel) team) - (with-slots (subscribed-channels) team - (let ((name (slack-room-name room))) - (and name - (memq (intern name) subscribed-channels))))) - -(provide 'slack-channel) -;;; slack-channel.el ends here diff --git a/elpa/slack-20160928.2036/slack-file.el b/elpa/slack-20160928.2036/slack-file.el deleted file mode 100644 index f2d857a..0000000 --- a/elpa/slack-20160928.2036/slack-file.el +++ /dev/null @@ -1,244 +0,0 @@ -;;; slack-file.el --- handle files -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-room) - -(defconst slack-file-list-url "https://slack.com/api/files.list") -(defconst slack-file-upload-url "https://slack.com/api/files.upload") -(defconst slack-file-delete-url "https://slack.com/api/files.delete") - -(defclass slack-file (slack-message) - ((id :initarg :id) - (created :initarg :created) - (name :initarg :name) - (size :initarg :size) - (public :initarg :public) - (filetype :initarg :filetype) - (user :initarg :user) - (preview :initarg :preview) - (initial-comment :initarg :initial_comment :initform nil) - (permalink :initarg :permalink) - (channels :initarg :channels :type list) - (groups :initarg :groups :type list) - (ims :initarg :ims :type list) - (username :initarg :username))) - -(defclass slack-file-room (slack-room) ()) - -(defun slack-file-room-obj (team) - (with-slots (file-room) team - (if file-room - file-room - (setq file-room (slack-file-room "file-room" - :name "Files" - :id "F" - :team-id (oref team id) - :created (format-time-string "%s") - :last_read "0" - :latest nil - :unread_count 0 - :unread_count_display 0 - :messages '()))))) - -(defun slack-file-create (payload) - (plist-put payload :channels (append (plist-get payload :channels) nil)) - (plist-put payload :groups (append (plist-get payload :groups) nil)) - (plist-put payload :ims (append (plist-get payload :ims) nil)) - (plist-put payload :reactions (append (plist-get payload :reactions) nil)) - (plist-put payload :pinned_to (append (plist-get payload :pinned_to) nil)) - (plist-put payload :ts (number-to-string (plist-get payload :timestamp))) - (let ((file (apply #'slack-file "file" - (slack-collect-slots 'slack-file payload)))) - (oset file reactions - (mapcar #'slack-reaction-create (plist-get payload :reactions))) - file)) - -(defmethod slack-message-equal ((f slack-file) other) - (string= (oref f id) (oref other id))) - -(defmethod slack-file-pushnew ((f slack-file) team) - (let ((room (slack-file-room-obj team))) - (with-slots (messages) room - (cl-pushnew f messages - :test #'slack-message-equal)))) - -(defmethod slack-message-body ((file slack-file) team) - (with-slots (initial-comment) file - (let ((body (plist-get initial-comment :comment))) - (slack-message-unescape-string body team)))) - -(defmethod slack-message-to-string ((file slack-file) team) - (with-slots (ts name size filetype permalink user initial-comment reactions) - file - (let* ((header (slack-user-name user team)) - (body (format "name: %s\nsize: %s\ntype: %s\n%s\n" - name size filetype permalink)) - (reactions-str (slack-message-reactions-to-string - reactions))) - (slack-message-put-header-property header) - (slack-message-put-text-property body) - (slack-message-put-reactions-property reactions-str) - (let ((message - (concat header "\n" body - (if initial-comment - (format "comment: %s\n%s\n" - (slack-user-name - (plist-get initial-comment :user) - team) - (slack-message-body file team))) - (if reactions-str - (concat "\n" reactions-str "\n"))))) - (put-text-property 0 (length message) 'ts ts message) - message)))) - -(defmethod slack-room-update-mark ((_room slack-file-room) _team _msg)) - -(defun slack-file-create-buffer (team) - (funcall slack-buffer-function - (slack-buffer-create (slack-file-room-obj team) - team - :type 'info))) - -(defun slack-file-list () - (interactive) - (let* ((team (slack-team-select)) - (room (slack-file-room-obj team))) - (with-slots (messages) room - (if messages - (slack-file-create-buffer team) - (slack-room-history room team nil - #'(lambda () - (slack-file-create-buffer team))))))) - -(defmethod slack-room-history ((room slack-file-room) team - &optional - oldest - after-success - async) - (cl-labels - ((on-file-list - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-file-list") - (let ((files (cl-loop for e across (plist-get data :files) - collect (slack-file-create e)))) - (if oldest - (slack-room-set-prev-messages room files) - (slack-room-update-last-read room - (make-instance 'slack-message - :ts "0")) - (slack-room-set-messages room files))) - (if after-success - (funcall after-success))))) - (slack-request - slack-file-list-url - team - :params (list (if oldest - (cons "ts_to" oldest))) - :success #'on-file-list - :sync (if async nil t)))) - -(defun slack-file-upload () - (interactive) - (cl-labels - ((on-file-upload (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-file-upload"))) - (select-channels (channels acc) - (let ((selected (completing-read "Select Channel: " - channels nil t))) - (if (< 0 (length selected)) - (select-channels channels (push selected acc)) - acc))) - (channel-id (selected channels) - (oref (cdr (cl-assoc selected channels :test #'string=)) - id))) - (let* ((team (slack-team-select)) - (channels (slack-room-names - (append (oref team ims) - (oref team channels) - (oref team groups)))) - (target-channels (select-channels channels '())) - (channel-ids (mapconcat #'(lambda (selected) - (channel-id selected channels)) - (cl-delete-if #'null target-channels) - ",")) - (buf (find-file-noselect - (car (find-file-read-args - "Select File: " - (confirm-nonexistent-file-or-buffer))))) - (filename (read-from-minibuffer "Filename: " - (file-name-nondirectory - (buffer-file-name buf)))) - (filetype (read-from-minibuffer "Filetype: " - (file-name-extension - (buffer-file-name buf)))) - (initial-comment (read-from-minibuffer "Message: "))) - (slack-request - slack-file-upload-url - team - :type "POST" - :params (list (cons "filename" filename) - (cons "channels" channel-ids) - (cons "filetype" filetype) - (if initial-comment - (cons "initial_comment" initial-comment))) - :files (list (cons "file" buf)) - :headers (list (cons "Content-Type" "multipart/form-data")) - :success #'on-file-upload - :sync nil)))) - -(defun slack-file-delete () - (interactive) - (cl-labels - ((on-file-delete (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-file-delete")))) - (let* ((team (slack-team-select)) - (files (oref (slack-file-room-obj team) messages)) - (your-files (cl-remove-if #'(lambda (f) - (not (string= (oref f user) - (oref team self-id)))) - files)) - (candidates (mapcar #'(lambda (f) - (cons (concat - (slack-message-time-to-string (oref f ts)) - " " - (oref f name)) - f)) - your-files)) - (selected (completing-read "Select File: " candidates)) - (deleting-file (cdr (cl-assoc selected candidates :test #'string=)))) - (slack-request - slack-file-delete-url - team - :params (list (cons "file" (oref deleting-file id))) - :sync nil - :success #'on-file-delete)))) - -(provide 'slack-file) -;;; slack-file.el ends here diff --git a/elpa/slack-20160928.2036/slack-group.el b/elpa/slack-20160928.2036/slack-group.el deleted file mode 100644 index ce535e8..0000000 --- a/elpa/slack-20160928.2036/slack-group.el +++ /dev/null @@ -1,192 +0,0 @@ -;;; slack-group.el --- slack private group interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 Yuya Minami - -;; Author: Yuya Minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-room) -(require 'slack-util) -(require 'slack-buffer) - -(defconst slack--group-open-url "https://slack.com/api/groups.open") -(defconst slack-group-history-url "https://slack.com/api/groups.history") -(defconst slack-group-buffer-name "*Slack - Private Group*") -(defconst slack-group-list-url "https://slack.com/api/groups.list") -(defconst slack-group-update-mark-url "https://slack.com/api/groups.mark") -(defconst slack-create-group-url "https://slack.com/api/groups.create") -(defconst slack-group-rename-url "https://slack.com/api/groups.rename") -(defconst slack-group-invite-url "https://slack.com/api/groups.invite") -(defconst slack-group-leave-url "https://slack.com/api/groups.leave") -(defconst slack-group-archive-url "https://slack.com/api/groups.archive") -(defconst slack-group-unarchive-url "https://slack.com/api/groups.unarchive") - -(defvar slack-buffer-function) - -(defclass slack-group (slack-room) - ((name :initarg :name :type string) - (is-group :initarg :is_group) - (creator :initarg :creator) - (is-archived :initarg :is_archived) - (is-mpim :initarg :is_mpim) - (members :initarg :members :type list) - (topic :initarg :topic) - (unread-count-display :initarg :unread_count_display :initform 0 :type integer) - (purpose :initarg :purpose))) - -(defun slack-group-names (team &optional filter) - (with-slots (groups) team - (slack-room-names groups filter))) - -(defmethod slack-room-subscribedp ((room slack-group) team) - (with-slots (subscribed-channels) team - (let ((name (slack-room-name room))) - (and name - (memq (intern name) subscribed-channels))))) - -(defmethod slack-room-buffer-name ((room slack-group)) - (concat slack-group-buffer-name - " : " - (slack-room-name-with-team-name room))) - -(defun slack-group-select () - (interactive) - (let ((team (slack-team-select))) - (slack-room-select - (cl-loop for team in (list team) - for groups = (oref team groups) - nconc groups)))) - -(defun slack-group-list-update () - (interactive) - (let ((team (slack-team-select))) - (cl-labels ((on-list-update - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-group-list-update") - (with-slots (groups) team - (setq groups - (mapcar #'(lambda (g) - (slack-room-create g team 'slack-group)) - (plist-get data :groups)))) - (message "Slack Group List Updated")))) - (slack-room-list-update slack-group-list-url - #'on-list-update - :sync nil)))) - - -(defmethod slack-room-update-mark-url ((_room slack-group)) - slack-group-update-mark-url) - -(defun slack-create-group () - (interactive) - (let ((team (slack-team-select))) - (cl-labels - ((on-create-group (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-create-group")))) - (slack-create-room slack-create-group-url - team - #'on-create-group)))) - -(defun slack-group-rename () - (interactive) - (slack-room-rename slack-group-rename-url - #'slack-group-names)) - -(defun slack-group-invite () - (interactive) - (slack-room-invite slack-group-invite-url - #'slack-group-names)) - -(defun slack-group-leave () - (interactive) - (let* ((team (slack-team-select)) - (group (slack-current-room-or-select - #'(lambda () - (slack-group-names team))))) - (cl-labels - ((on-group-leave - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-group-leave") - (with-slots (groups) team - (setq groups - (cl-delete-if #'(lambda (g) - (slack-room-equal-p group g)) - groups))) - (message "Left Group: %s" - (slack-room-name-with-team-name group))))) - (slack-room-request-with-id slack-group-leave-url - (oref group id) - team - #'on-group-leave)))) - -(defmethod slack-room-archived-p ((room slack-group)) - (oref room is-archived)) - -(defun slack-group-archive () - (interactive) - (let* ((team (slack-team-select)) - (group (slack-current-room-or-select - #'(lambda () - (slack-group-names - team - #'(lambda (groups) - (cl-remove-if #'slack-room-archived-p - groups))))))) - (cl-labels - ((on-group-archive (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-group-archive")))) - (slack-room-request-with-id slack-group-archive-url - (oref group id) - team - #'on-group-archive)))) - -(defun slack-group-unarchive () - (interactive) - (let* ((team (slack-team-select)) - (group (slack-current-room-or-select - #'(lambda () - (slack-group-names - team - #'(lambda (groups) - (cl-remove-if-not #'slack-room-archived-p - groups))))))) - (cl-labels - ((on-group-unarchive (&key _data &allow-other-keys) - (data "slack-group-unarchive"))) - (slack-room-request-with-id slack-group-unarchive-url - (oref group id) - team - #'on-group-unarchive)))) - -(defmethod slack-mpim-p ((room slack-group)) - (oref room is-mpim)) - -(defmethod slack-room-history-url ((_room slack-group)) - slack-group-history-url) - -(provide 'slack-group) -;;; slack-group.el ends here diff --git a/elpa/slack-20160928.2036/slack-im.el b/elpa/slack-20160928.2036/slack-im.el deleted file mode 100644 index b88878b..0000000 --- a/elpa/slack-20160928.2036/slack-im.el +++ /dev/null @@ -1,184 +0,0 @@ -;;; slack-im.el ---slack direct message interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-util) -(require 'slack-room) -(require 'slack-buffer) -(require 'slack-user) - -(defvar slack-buffer-function) - -(defconst slack-im-history-url "https://slack.com/api/im.history") -(defconst slack-im-buffer-name "*Slack - Direct Messages*") -(defconst slack-user-list-url "https://slack.com/api/users.list") -(defconst slack-im-list-url "https://slack.com/api/im.list") -(defconst slack-im-close-url "https://slack.com/api/im.close") -(defconst slack-im-open-url "https://slack.com/api/im.open") -(defconst slack-im-update-mark-url "https://slack.com/api/im.mark") - -(defclass slack-im (slack-room) - ((user :initarg :user) - (is-open :initarg :is_open :initform nil))) - -(defmethod slack-room-open-p ((room slack-im)) - (oref room is-open)) - -(defmethod slack-room-name-with-team-name ((room slack-im)) - (with-slots (team-id user) room - (let* ((team (slack-team-find team-id)) - (user-name (slack-user-name user team))) - (format "%s - %s" (oref team name) user-name)))) - -(defmethod slack-im-user-presence ((room slack-im)) - (with-slots ((user-id user) team-id) room - (let* ((team (slack-team-find team-id)) - (user (slack-user-find user-id team))) - (slack-user-presence-to-string user)))) - -(defmethod slack-room-name ((room slack-im)) - (with-slots (user team-id) room - (slack-user-name user (slack-team-find team-id)))) - -(defun slack-im-user-name (im team) - (with-slots (user) im - (slack-user-name user team))) - -(defun slack-im-names (team) - (with-slots (ims) team - (mapcar #'(lambda (im) (cons (slack-im-user-name im team) im)) - ims))) - -(defmethod slack-room-buffer-name ((room slack-im)) - (concat slack-im-buffer-name - " : " - (slack-room-name-with-team-name room))) - -(defun slack-im-select () - (interactive) - (let ((team (slack-team-select))) - (slack-room-select - (cl-loop for team in (list team) - for ims = (cl-remove-if #'(lambda (im) (not (oref im is-open))) - (oref team ims)) - nconc ims)))) - -(defun slack-user-equal-p (a b) - (string= (plist-get a :id) (plist-get b :id))) - -(defun slack-user-pushnew (user team) - (with-slots (users) team - (cl-pushnew user users :test #'slack-user-equal-p))) - -(defun slack-im-update-room-list (users team) - (cl-labels ((on-update-room-list - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-im-update-room-list") - (mapc #'(lambda (u) (slack-user-pushnew u team)) - (append users nil)) - (oset team ims - (mapcar #'(lambda (d) - (slack-room-create d team 'slack-im)) - (plist-get data :ims))) - (message "Slack Im List Updated")))) - (slack-room-list-update slack-im-list-url - #'on-update-room-list - team - :sync nil))) - -(defun slack-im-list-update () - (interactive) - (let ((team (slack-team-select))) - (slack-request - slack-user-list-url - team - :success (cl-function (lambda (&key data &allow-other-keys) - (slack-request-handle-error (data "slack-im-list-update") - (let ((users (plist-get data :members))) - (slack-im-update-room-list users team))))) - :sync nil))) - -(defmethod slack-room-update-mark-url ((_room slack-im)) - slack-im-update-mark-url) - -(defmethod slack-room-history-url ((_room slack-im)) - slack-im-history-url) - -(defun slack-im-close () - (interactive) - (let* ((team (slack-team-select)) - (alist (cl-remove-if #'(lambda (im-names) - (not (oref (cdr im-names) is-open))) - (slack-im-names team)))) - (slack-select-from-list - (alist "Select User: ") - (cl-labels - ((on-success - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-im-close") - (if (plist-get data :already_closed) - (let ((im (slack-room-find (oref selected id) team))) - (oset im is-open nil) - (message "Direct Message Channel with %s Already Closed" - (slack-user-name (oref im user) team))))))) - (slack-request - slack-im-close-url - team - :type "POST" - :params (list (cons "channel" (oref selected id))) - :success #'on-success - :sync nil))))) - -(defun slack-im-open () - (interactive) - (let* ((team (slack-team-select)) - (alist (cl-remove-if #'(lambda (im-names) - (oref (cdr im-names) is-open)) - (slack-im-names team)))) - (slack-select-from-list - (alist "Select User: ") - (cl-labels - ((on-success - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-im-open") - (if (plist-get data :already_open) - (let ((im (slack-room-find (oref selected id) team))) - (oset im is-open t) - (message "Direct Message Channel with %s Already Open" - (slack-user-name (oref im user) team))))))) - (slack-request - slack-im-open-url - team - :type "POST" - :params (list (cons "user" (oref selected user))) - :success #'on-success - :sync nil))))) - -(provide 'slack-im) -;;; slack-im.el ends here diff --git a/elpa/slack-20160928.2036/slack-message-editor.el b/elpa/slack-20160928.2036/slack-message-editor.el deleted file mode 100644 index f0dc1d8..0000000 --- a/elpa/slack-20160928.2036/slack-message-editor.el +++ /dev/null @@ -1,141 +0,0 @@ -;;; slack-message-editor.el --- edit message interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'slack-message-sender) - -(defconst slack-message-edit-url "https://slack.com/api/chat.update") -(defconst slack-message-edit-buffer-name "*Slack - Edit message*") -(defconst slack-message-write-buffer-name "*Slack - Write message*") -(defvar slack-buffer-function) -(defvar slack-target-ts) -(make-local-variable 'slack-target-ts) -(defvar slack-message-edit-buffer-type) -(make-local-variable 'slack-message-edit-buffer-type) -(defvar slack-current-room-id) -(defvar slack-current-team-id) - -(defvar slack-edit-message-mode-map - (let ((keymap (make-sparse-keymap))) - (define-key keymap (kbd "C-s C-m") #'slack-message-embed-mention) - (define-key keymap (kbd "C-s C-c") #'slack-message-embed-channel) - (define-key keymap (kbd "C-c C-k") #'slack-message-cancel-edit) - (define-key keymap (kbd "C-c C-c") #'slack-message-send-from-buffer) - keymap)) - -(define-derived-mode slack-edit-message-mode fundamental-mode "Slack Edit Msg" - "" - (slack-buffer-enable-emojify)) - - -(defun slack-message-write-another-buffer () - (interactive) - (let* ((team (slack-team-find slack-current-team-id)) - (target-room (if (boundp 'slack-current-room-id) - (slack-room-find slack-current-room-id - team) - (slack-message-read-room team))) - (buf (get-buffer-create slack-message-write-buffer-name))) - (with-current-buffer buf - (slack-message-setup-edit-buf target-room 'new - :team team)) - (funcall slack-buffer-function buf))) - -(defun slack-message-edit () - (interactive) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team)) - (target (thing-at-point 'word)) - (ts (get-text-property 0 'ts target)) - (msg (slack-room-find-message room ts))) - (unless msg - (error "Can't find original message")) - (unless (string= (oref team self-id) (oref msg user)) - (error "Cant't edit other user's message")) - (slack-message-edit-text msg room))) - -(defun slack-message-edit-text (msg room) - (let ((buf (get-buffer-create slack-message-edit-buffer-name)) - (team (slack-team-find slack-current-team-id))) - (with-current-buffer buf - (slack-edit-message-mode) - (slack-message-setup-edit-buf room 'edit - :ts (oref msg ts) - :team team) - (insert (oref msg text))) - (funcall slack-buffer-function buf))) - -(cl-defun slack-message-setup-edit-buf (room buf-type &key ts team) - (slack-edit-message-mode) - (setq buffer-read-only nil) - (erase-buffer) - (if (and (eq buf-type 'edit) ts) - (set (make-local-variable 'slack-target-ts) ts)) - (set (make-local-variable 'slack-message-edit-buffer-type) buf-type) - (slack-buffer-set-current-room-id room) - (slack-buffer-set-current-team-id team) - (message "C-c C-c to send edited msg")) - -(defun slack-message-cancel-edit () - (interactive) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team))) - (erase-buffer) - (delete-window) - (slack-room-make-buffer-with-room room team))) - -(defun slack-message-send-from-buffer () - (interactive) - (let ((buf-string (buffer-substring (point-min) (point-max)))) - (cl-case slack-message-edit-buffer-type - ('edit - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team))) - (slack-message--edit (oref room id) - team - slack-target-ts - buf-string))) - ('new (slack-message--send buf-string))) - (kill-buffer) - (delete-window))) - -(defun slack-message--edit (channel team ts text) - (cl-labels ((on-edit (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-message--edit")))) - (slack-request - slack-message-edit-url - team - :type "POST" - :sync nil - :params (list (cons "channel" channel) - (cons "ts" ts) - (cons "text" text)) - :success #'on-edit))) - -(provide 'slack-message-editor) -;;; slack-message-editor.el ends here diff --git a/elpa/slack-20160928.2036/slack-message-formatter.el b/elpa/slack-20160928.2036/slack-message-formatter.el deleted file mode 100644 index e561102..0000000 --- a/elpa/slack-20160928.2036/slack-message-formatter.el +++ /dev/null @@ -1,182 +0,0 @@ -;;; slack-message-formatter.el --- format message text -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-user) -(require 'slack-room) - -(defface slack-message-output-text - '((t (:weight normal :height 0.9))) - "Face used to text message." - :group 'slack) - -(defface slack-message-output-header - '((t (:foreground "#FFA000" - :weight bold - :height 1.0 - :underline t))) - "Face used to text message." - :group 'slack) - -(defface slack-message-output-reaction - '((t (:overline t))) - "Face used to reactions." - :group 'slack) - -(defface slack-message-deleted-face - '((t (:strike-through t))) - "Face used to deleted message." - :group 'slack) - -(defun slack-message-put-header-property (header) - (if header - (propertize header 'face 'slack-message-output-header))) - -(defun slack-message-put-text-property (text) - (if text - (propertize text 'face 'slack-message-output-text))) - -(defun slack-message-put-reactions-property (text) - (if text - (propertize text 'face 'slack-message-output-reaction))) - -(defun slack-message-put-hard (text) - (if text - (propertize text 'hard t))) - -(defun slack-message-put-deleted-property (text) - (if text - (propertize text 'face 'slack-message-deleted-face))) - -(defmethod slack-message-propertize ((m slack-message) text) - text) - -(defun slack-message-time-to-string (ts) - (if ts - (format-time-string "%Y-%m-%d %H:%M:%S" - (seconds-to-time (string-to-number ts))))) - -(defun slack-message-reactions-to-string (reactions) - (if reactions - (concat "\n" (mapconcat #'slack-reaction-to-string reactions " ")))) - -(defmethod slack-message-header ((m slack-message) team) - (slack-message-sender-name m team)) - -(defun slack-format-message (header body attachment-body reactions) - (let ((messages (list header body attachment-body reactions))) - (concat (mapconcat #'identity - (cl-remove-if #'(lambda (e) (< (length e) 1)) messages) - "\n") - "\n"))) - -(defmethod slack-message-to-string ((m slack-message) team) - (let ((text (if (slot-boundp m 'text) - (oref m text)))) - (let* ((header (slack-message-put-header-property - (slack-message-header m team))) - (row-body (slack-message-body m team)) - (attachment-body (slack-message-attachment-body m team)) - (body (if (oref m deleted-at) - (slack-message-put-deleted-property row-body) - (slack-message-put-text-property row-body))) - (reactions-str - (slack-message-put-reactions-property - (slack-message-reactions-to-string (oref m reactions))))) - (slack-message-propertize - m (slack-format-message header body attachment-body reactions-str))))) - -(defmethod slack-message-body ((m slack-message) team) - (with-slots (text) m - (slack-message-unescape-string text team))) - -(defmethod slack-message-attachment-body ((m slack-message) team) - (with-slots (attachments) m - (let ((body (mapconcat #'slack-attachment-to-string attachments "\n"))) - (if (< 0 (length body)) - (slack-message-unescape-string body team))))) - -(defmethod slack-message-to-alert ((m slack-message) team) - (with-slots (text) m - (slack-message-unescape-string text team))) - -(defun slack-message-unescape-string (text team) - (when text - (let* ((and-unescpaed - (replace-regexp-in-string "&" "&" text)) - (lt-unescaped - (replace-regexp-in-string "<" "<" and-unescpaed)) - (gt-unescaped - (replace-regexp-in-string ">" ">" lt-unescaped))) - (slack-message-unescape-command - (slack-message-unescape-user-id - (slack-message-unescape-channel gt-unescaped) - team))))) - -(defun slack-message-unescape-user-id (text team) - (let ((user-regexp "<@\\(U.*?\\)>")) - (cl-labels ((unescape-user-id - (text) - (concat "@" (or - (slack-message-replace-user-name text) - (slack-user-name (match-string 1 text) team) - (match-string 1 text))))) - (replace-regexp-in-string user-regexp - #'unescape-user-id - text t)))) - -(defun slack-message-replace-user-name (text) - (let ((user-name-regexp "<@U.*?|\\(.*?\\)>")) - (cl-labels ((replace-user-id-with-name (text) - (match-string 1 text))) - (if (string-match-p user-name-regexp text) - (replace-regexp-in-string user-name-regexp - #'replace-user-id-with-name - text))))) - -(defun slack-message-unescape-command (text) - (let ((command-regexp "")) - (cl-labels ((unescape-command - (text) - (concat "@" (match-string 1 text)))) - (replace-regexp-in-string command-regexp - #'unescape-command - text)))) - -(defun slack-message-unescape-channel (text) - (let ((channel-regexp "<#\\(C.*?\\)|\\(.*?\\)>")) - (cl-labels ((unescape-channel - (text) - (concat "#" (or (match-string 2 text) - (slack-room-find - (match-string 1 text)) - (match-string 1 text))))) - (replace-regexp-in-string channel-regexp - #'unescape-channel - text t)))) - -(provide 'slack-message-formatter) -;;; slack-message-formatter.el ends here diff --git a/elpa/slack-20160928.2036/slack-message-notification.el b/elpa/slack-20160928.2036/slack-message-notification.el deleted file mode 100644 index 299e909..0000000 --- a/elpa/slack-20160928.2036/slack-message-notification.el +++ /dev/null @@ -1,79 +0,0 @@ -;;; slack-message-notification.el --- message notification -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'eieio) -(require 'slack-room) -(require 'slack-message) -(require 'slack-message-formatter) -(require 'slack-buffer) -(require 'slack-im) -(require 'alert) - -(defvar alert-default-style) - -(defcustom slack-message-custom-notifier nil - "Custom notification function.\ntake 3 Arguments.\n(lambda (MESSAGE ROOM TEAM) ...)." - :group 'slack) - -(defun slack-message-notify (message room team) - (if slack-message-custom-notifier - (funcall slack-message-custom-notifier message room team) - (slack-message-notify-alert message room team))) - -(defun slack-message-notify-alert (message room team) - (if (and (not (slack-message-minep message team)) - (or (slack-im-p room) - (and (slack-group-p room) (slack-mpim-p room)) - (slack-room-subscribedp room team) - (string-match (format "@%s" (plist-get (oref team self) :name)) - (slack-message-body message team)))) - (let ((team-name (oref team name)) - (room-name (slack-room-name room)) - (text (slack-message-to-alert message team)) - (user-name (slack-message-sender-name message team))) - (if (and (eq alert-default-style 'notifier) - (slack-im-p room) - (or (eq (aref text 0) ?\[) - (eq (aref text 0) ?\{) - (eq (aref text 0) ?\<) - (eq (aref text 0) ?\())) - (setq text (concat "\\" text))) - (alert (if (slack-im-p room) text (format "%s: %s" user-name text)) - :title (if (slack-im-p room) - (format "%s - %s" team-name room-name) - (format "%s - #%s" team-name room-name)) - :category 'slack)))) - -(defmethod slack-message-sender-equalp ((_m slack-message) _sender-id) - nil) - -(defmethod slack-message-minep ((m slack-message) team) - (if team - (with-slots (self-id) team - (slack-message-sender-equalp m self-id)) - (slack-message-sender-equalp m (oref team self-id)))) - -(provide 'slack-message-notification) -;;; slack-message-notification.el ends here diff --git a/elpa/slack-20160928.2036/slack-message-reaction.el b/elpa/slack-20160928.2036/slack-message-reaction.el deleted file mode 100644 index 00f3a61..0000000 --- a/elpa/slack-20160928.2036/slack-message-reaction.el +++ /dev/null @@ -1,155 +0,0 @@ -;;; slack-message-reaction.el --- adding, removing reaction from message -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'slack-message) -(require 'slack-reaction) -(require 'slack-room) - -(defconst slack-message-reaction-add-url "https://slack.com/api/reactions.add") -(defconst slack-message-reaction-remove-url "https://slack.com/api/reactions.remove") -(defvar slack-current-team-id) -(defvar slack-current-room-id) -(defvar slack-emojify-comp-list) -(defcustom slack-invalid-emojis '("^:flag_" "tone[[:digit:]]:$" "-" "^[^:].*[^:]$" "\\Ca") - "Invalid emoji regex. Slack server treated some emojis as Invalid." - :group 'slack) - -(defun slack-message-reaction-load-emojify-comp-list () - (if (and (bound-and-true-p emojify-emojis) - (not (bound-and-true-p slack-emojify-comp-list))) - (setq slack-emojify-comp-list - (let ((invalid-regex (mapconcat #'identity - slack-invalid-emojis - "\\|"))) - (cl-remove-if (lambda (s) (string-match invalid-regex s)) - (hash-table-keys emojify-emojis)))))) - -(defun slack-message-add-reaction () - (interactive) - (let* ((word (thing-at-point 'word)) - (ts (get-text-property 0 'ts word)) - (reaction (slack-message-reaction-input)) - (team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team))) - (slack-message-reaction-add reaction ts room team))) - -(defun slack-message-remove-reaction () - (interactive) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team)) - (word (thing-at-point 'word)) - (ts (get-text-property 0 'ts word)) - (msg (slack-room-find-message room ts)) - (reactions (oref msg reactions)) - (reaction (slack-message-reaction-select reactions))) - (slack-message-reaction-remove reaction ts room team))) - -(defun slack-message-show-reaction-users () - (interactive) - (let* ((team (slack-team-find slack-current-team-id)) - (reaction (ignore-errors (get-text-property (point) 'reaction)))) - (if reaction - (let ((user-names (slack-reaction-user-names reaction team))) - (message "reacted users: %s" (mapconcat #'identity user-names ", "))) - (message "Can't get reaction:")))) - -(defun slack-message-reaction-select (reactions) - (let ((list (mapcar #'(lambda (r) - (cons (oref r name) - (oref r name))) - reactions))) - (slack-select-from-list - (list "Select Reaction: ") - selected))) - -(defun slack-message-reaction-input () - (slack-message-reaction-load-emojify-comp-list) - (let ((reaction (if (bound-and-true-p slack-emojify-comp-list) - (completing-read "Select Emoji: " slack-emojify-comp-list) - (read-from-minibuffer "Emoji: ")))) - (if (and (string-prefix-p ":" reaction) - (string-suffix-p ":" reaction)) - (substring reaction 1 -1) - reaction))) - -(defun slack-message-reaction-add (reaction ts room team) - (cl-labels ((on-reaction-add - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-message-reaction-add")))) - (slack-request - slack-message-reaction-add-url - team - :type "POST" - :sync nil - :params (list (cons "channel" (oref room id)) - (cons "timestamp" ts) - (cons "name" reaction)) - :success #'on-reaction-add))) - -(defun slack-message-reaction-remove (reaction ts room team) - (cl-labels ((on-reaction-remove - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-message-reaction-remove")))) - (slack-request - slack-message-reaction-remove-url - team - :type "POST" - :sync nil - :params (list (cons "channel" (oref room id)) - (cons "timestamp" ts) - (cons "name" reaction)) - :success #'on-reaction-remove))) - -(cl-defmacro slack-message-find-reaction ((m reaction) &body body) - `(let ((same-reaction (cl-find-if #'(lambda (r) (slack-reaction-equalp r ,reaction)) - (oref ,m reactions)))) - ,@body)) - -(defmethod slack-message-append-reaction ((m slack-message) reaction) - (slack-message-find-reaction - (m reaction) - (if same-reaction - (slack-reaction-join same-reaction reaction) - (push reaction (oref m reactions))))) - -(defmethod slack-message-pop-reaction ((m slack-message) reaction) - (slack-message-find-reaction - (m reaction) - (if same-reaction - (if (eq 1 (oref same-reaction count)) - (with-slots (reactions) m - (setq reactions - (cl-delete-if #'(lambda (r) - (slack-reaction-equalp same-reaction r)) - reactions))) - (cl-decf (oref same-reaction count)))))) - -(provide 'slack-message-reaction) -;;; slack-message-reaction.el ends here diff --git a/elpa/slack-20160928.2036/slack-message-sender.el b/elpa/slack-20160928.2036/slack-message-sender.el deleted file mode 100644 index 4de2901..0000000 --- a/elpa/slack-20160928.2036/slack-message-sender.el +++ /dev/null @@ -1,170 +0,0 @@ -;;; slack-message-sender.el --- slack message concern message sending -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'json) -(require 'slack-websocket) -(require 'slack-im) -(require 'slack-group) -(require 'slack-message) -(require 'slack-channel) - -(defvar slack-message-minibuffer-local-map nil) -(defvar slack-buffer-function) - -(defun slack-message-send () - (interactive) - (slack-message--send (slack-message-read-from-minibuffer))) - -(defun slack-message-inc-id (team) - (with-slots (message-id) team - (if (eq message-id (1- most-positive-fixnum)) - (setq message-id 1) - (cl-incf message-id)))) - -(defun slack-escape-message (message) - "Escape '<,' '>' & '&' in MESSAGE." - (replace-regexp-in-string - ">" ">" - (replace-regexp-in-string - "<" "<" - (replace-regexp-in-string "&" "&" message)))) - -(defun slack-link-users (message team) - "Add links to all references to valid users in MESSAGE." - (replace-regexp-in-string - "@\\<\\([A-Za-z0-9.-_]+\\)" - #'(lambda (text) - (let* ((username (match-string 1 text)) - (id (slack-user-get-id username team))) - (if id - (format "<@%s|%s>" id username) - (cond - ((string= username "here") "") - ((find username '("channel" "group") :test #'string=) "") - ((string= username "everyone") "") - (t text))))) - message t)) - -(defun slack-link-channels (message team) - "Add links to all references to valid channels in MESSAGE." - (let ((channel-ids - (mapcar #'(lambda (x) - (let ((channel (cdr x))) - (cons (slack-room-name channel) (slot-value channel 'id)))) - (slack-channel-names team)))) - (replace-regexp-in-string - "#\\<\\([A-Za-z0-9.-_]+\\)" - #'(lambda (text) - (let* ((channel (match-string 1 text)) - (id (cdr (assoc channel channel-ids)))) - (if id - (format "<#%s|%s>" id channel) - text))) - message t))) - -(defun slack-message--send (message) - (if slack-current-team-id - (let* ((team (slack-team-find slack-current-team-id)) - (message (slack-link-channels - (slack-link-users - (slack-escape-message message) - team) - team))) - (slack-message-inc-id team) - (with-slots (message-id sent-message self-id) team - (let* ((m (list :id message-id - :channel (slack-message-get-room-id) - :type "message" - :user self-id - :text message)) - (json (json-encode m)) - (obj (slack-message-create m))) - (slack-ws-send json team) - (puthash message-id obj sent-message)))) - (error "Call from Slack Buffer"))) - -(defun slack-message-get-room-id () - (if (and (boundp 'slack-current-room-id) - (boundp 'slack-current-team-id)) - (oref (slack-room-find slack-current-room-id - (slack-team-find slack-current-team-id)) - id) - (oref (slack-message-read-room (slack-team-select)) id))) - -(defun slack-message-read-room (team) - (let* ((list (slack-message-room-list team)) - (choices (mapcar #'car list)) - (room-name (slack-message-read-room-list "Select Room: " choices)) - (room (cdr (cl-assoc room-name list :test #'string=)))) - room)) - -(defun slack-message-read-room-list (prompt choices) - (let ((completion-ignore-case t)) - (completing-read (format "%s" prompt) - choices nil t nil nil choices))) - -(defun slack-message-room-list (team) - (append (slack-group-names team) - (slack-im-names team) - (slack-channel-names team))) - -(defun slack-message-read-from-minibuffer () - (let ((prompt "Message: ")) - (slack-message-setup-minibuffer-keymap) - (read-from-minibuffer - prompt - nil - slack-message-minibuffer-local-map))) - -(defun slack-message-setup-minibuffer-keymap () - (unless slack-message-minibuffer-local-map - (setq slack-message-minibuffer-local-map - (let ((map (make-sparse-keymap))) - (define-key map (kbd "RET") 'newline) - (set-keymap-parent map minibuffer-local-map) - map)))) - -(defun slack-message-embed-channel () - (interactive) - (let ((team (slack-team-select))) - (let* ((alist (slack-channel-names team))) - (slack-select-from-list - (alist "Select Channel: ") - (insert (concat "#" (slack-room-name selected))))))) - -(defun slack-message-embed-mention () - (interactive) - (let ((team (slack-team-select))) - (let* ((pre-defined (list (list "here" :name "here") - (list "channel" :name "channel"))) - (alist (append pre-defined (slack-user-names team)))) - (slack-select-from-list - (alist "Select User: ") - (insert (concat "@" (plist-get selected :name))))))) - -(provide 'slack-message-sender) -;;; slack-message-sender.el ends here diff --git a/elpa/slack-20160928.2036/slack-message.el b/elpa/slack-20160928.2036/slack-message.el deleted file mode 100644 index 3f4dc24..0000000 --- a/elpa/slack-20160928.2036/slack-message.el +++ /dev/null @@ -1,295 +0,0 @@ -;;; slack-message.el --- slack-message -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-util) -(require 'slack-reaction) - -(defvar slack-current-room-id) -(defvar slack-current-team-id) -(defconst slack-message-pins-add-url "https://slack.com/api/pins.add") -(defconst slack-message-pins-remove-url "https://slack.com/api/pins.remove") -(defconst slack-message-delete-url "https://slack.com/api/chat.delete") - -(defclass slack-message () - ((type :initarg :type :type string) - (subtype :initarg :subtype) - (channel :initarg :channel :initform nil) - (ts :initarg :ts :type string :initform "") - (text :initarg :text :type (or null string) :initform nil) - (item-type :initarg :item_type) - (attachments :initarg :attachments :type (or null list) :initform nil) - (reactions :initarg :reactions :type (or null list)) - (is-starred :initarg :is_starred :type boolean) - (pinned-to :initarg :pinned_to :type (or null list)) - (edited-at :initarg :edited-at :initform nil) - (deleted-at :initarg :deleted-at :initform nil))) - -(defclass slack-file-message (slack-message) - ((file :initarg :file) - ;; (bot-id :initarg :bot_id :type (or null string)) - ;; (username :initarg :username) - ;; (display-as-bot :initarg :display_as_bot) - (upload :initarg :upload) - (user :initarg :user :initform nil))) - -(defclass slack-reply (slack-message) - ((user :initarg :user :initform nil) - (reply-to :initarg :reply_to :type integer) - (id :initarg :id :type integer))) - -(defclass slack-user-message (slack-message) - ((user :initarg :user :type string) - (edited :initarg :edited) - (id :initarg :id) - (inviter :initarg :inviter))) - -(defclass slack-bot-message (slack-message) - ((bot-id :initarg :bot_id :type string) - (username :initarg :username :type string :initform "") - (icons :initarg :icons))) - -(defclass slack-attachment () - ((fallback :initarg :fallback :initform nil) - (title :initarg :title :initform nil) - (title-link :initarg :title_link :initform nil) - (pretext :initarg :pretext :initform nil) - (text :initarg :text :initform nil) - (author-name :initarg :author_name) - (author-link :initarg :author_link) - (author-icon :initarg :author_icon) - (fields :initarg :fields :type (or null list)) - (image-url :initarg :image_url) - (thumb-url :initarg :thumb_url) - (is-share :initarg :is_share :initform nil))) - -(defclass slack-shared-message (slack-attachment) - ((ts :initarg :ts :initform nil) - (color :initarg :color :initform nil) - (channel-id :initarg :channel_id :initform nil) - (channel-name :initarg :channel_name :initform nil) - (from-url :initarg :from_url :initform nil))) - -(defgeneric slack-message-sender-name (slack-message team)) -(defgeneric slack-message-to-string (slack-message)) -(defgeneric slack-message-to-alert (slack-message)) - -(defgeneric slack-room-buffer-name (room)) - -(defun slack-room-find (id team) - (if (and id team) - (cl-labels ((find-room (room) - (string= id (oref room id)))) - (cond - ((string-prefix-p "F" id) (slack-file-room-obj team)) - ((string-prefix-p "C" id) (cl-find-if #'find-room - (oref team channels))) - ((string-prefix-p "G" id) (cl-find-if #'find-room - (oref team groups))) - ((string-prefix-p "D" id) (cl-find-if #'find-room - (oref team ims))) - ((string-prefix-p "Q" id) (cl-find-if #'find-room - (oref team search-results))))))) - -(defun slack-reaction-create (payload) - (apply #'slack-reaction "reaction" - (slack-collect-slots 'slack-reaction payload))) - -(defmethod slack-message-set-reactions ((m slack-message) payload) - (let ((reactions (plist-get payload :reactions))) - (if (< 0 (length reactions)) - (oset m reactions (mapcar #'slack-reaction-create reactions)))) - m) - -(defun slack-attachment-create (payload) - (plist-put payload :fields - (append (plist-get payload :fields) nil)) - (if (plist-get payload :is_share) - (apply #'slack-shared-message "shared-attachment" - (slack-collect-slots 'slack-shared-message payload)) - (apply #'slack-attachment "attachment" - (slack-collect-slots 'slack-attachment payload)))) - -(defmethod slack-message-set-attachments ((m slack-message) payload) - (let ((attachments (plist-get payload :attachments))) - (if (< 0 (length attachments)) - (oset m attachments - (mapcar #'slack-attachment-create attachments)))) - m) - -(cl-defun slack-message-create (payload &key room) - (when payload - (plist-put payload :reactions (append (plist-get payload :reactions) nil)) - (plist-put payload :attachments (append (plist-get payload :attachments) nil)) - (plist-put payload :pinned_to (append (plist-get payload :pinned_to) nil)) - (if room - (plist-put payload :channel (oref room id))) - (cl-labels ((create - (m) - (let ((subtype (plist-get m :subtype))) - (cond - ((plist-member m :reply_to) - (apply #'slack-reply "reply" - (slack-collect-slots 'slack-reply m))) - ((and subtype (string-prefix-p "file" subtype)) - (apply #'slack-file-message "file-msg" - (slack-collect-slots 'slack-file-message m))) - ((plist-member m :user) - (apply #'slack-user-message "user-msg" - (slack-collect-slots 'slack-user-message m))) - ((and subtype (string= "bot_message" subtype)) - (apply #'slack-bot-message "bot-msg" - (slack-collect-slots 'slack-bot-message m))))))) - (let ((message (create payload))) - (when message - (slack-message-set-attachments message payload) - (slack-message-set-reactions message payload)))))) - -(defmethod slack-message-equal ((m slack-message) n) - (string= (oref m ts) (oref n ts))) - -(defmethod slack-message-update ((m slack-message) team &optional replace no-notify) - (cl-labels - ((push-message-to (room msg) - (with-slots (messages) room - (when (< 0 (length messages)) - (cl-pushnew msg messages - :test #'slack-message-equal)) - (update-latest room msg))) - (update-latest (room msg) - (with-slots (latest) room - (if (or (null latest) - (string< (oref latest ts) (oref msg ts))) - (setq latest msg))))) - (with-slots (channel) m - (let ((room (slack-room-find channel team))) - (when room - (push-message-to room m) - (slack-buffer-update room m team :replace replace) - (unless no-notify - (slack-message-notify m room team))))))) - - -(defun slack-message-edited (payload team) - (let* ((edited-message (slack-decode (plist-get payload :message))) - (room (slack-room-find (plist-get payload :channel) team)) - (message (slack-room-find-message room - (plist-get edited-message :ts))) - (edited-info (plist-get edited-message :edited))) - (if message - (progn - (with-slots (text edited-at attachments) message - (setq text (plist-get edited-message :text)) - (setq edited-at (plist-get edited-info :ts)) - (if (plist-get edited-message :attachments) - (setq attachments - (mapcar #'slack-attachment-create - (plist-get edited-message :attachments))))) - (slack-message-update message team t))))) - -(defmethod slack-message-sender-name ((m slack-message) team) - (slack-user-name (oref m user) team)) - -(defun slack-message-pins-add () - (interactive) - (slack-message-pins-request slack-message-pins-add-url)) - -(defun slack-message-pins-remove () - (interactive) - (slack-message-pins-request slack-message-pins-remove-url)) - -(defun slack-message-pins-request (url) - (unless (and (bound-and-true-p slack-current-team-id) - (bound-and-true-p slack-current-room-id)) - (error "Call From Slack Room Buffer")) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team)) - (word (thing-at-point 'word)) - (ts (ignore-errors (get-text-property 0 'ts word)))) - (unless ts - (error "Call From Slack Room Buffer")) - (cl-labels ((on-pins-add - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-message-pins-request")))) - (slack-request - url - team - :params (list (cons "channel" (oref room id)) - (cons "timestamp" ts)) - :success #'on-pins-add - :sync nil)))) - -(defun slack-message-time-stamp (message) - (seconds-to-time (string-to-number (oref message ts)))) - -(defun slack-message-delete () - (interactive) - (unless (and (boundp 'slack-current-team-id) - (boundp 'slack-current-room-id)) - (error "Call From Slack Room Buffer")) - (let* ((team (slack-team-find slack-current-team-id)) - (channel (slack-room-find slack-current-room-id - team)) - (ts (ignore-errors (get-text-property (point) 'ts)))) - (unless ts - (error "Call With Cursor On Message")) - (let ((message (slack-room-find-message channel ts))) - (when message - (cl-labels - ((on-delete - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-message-delete")))) - (if (yes-or-no-p "Are you sure you want to delete this message?") - (slack-request - slack-message-delete-url - team - :type "POST" - :params (list (cons "ts" (oref message ts)) - (cons "channel" (oref channel id))) - :success #'on-delete - :sync nil) - (message "Canceled"))))))) - -(defun slack-message-deleted (payload team) - (let* ((channel-id (plist-get payload :channel)) - (ts (plist-get payload :deleted_ts)) - (deleted-ts (plist-get payload :ts)) - (channel (slack-room-find channel-id team)) - (message (slack-room-find-message channel ts))) - (when message - (oset message deleted-at deleted-ts) - (alert "message deleted" - :title (format "\\[%s] from %s" - (slack-room-name-with-team-name channel) - (slack-message-sender-name message team)) - :category 'slack) - (slack-buffer-update channel message team :replace t)))) - -(provide 'slack-message) -;;; slack-message.el ends here diff --git a/elpa/slack-20160928.2036/slack-pkg.el b/elpa/slack-20160928.2036/slack-pkg.el deleted file mode 100644 index 7b1c01a..0000000 --- a/elpa/slack-20160928.2036/slack-pkg.el +++ /dev/null @@ -1,11 +0,0 @@ -(define-package "slack" "20160928.2036" "Slack client for Emacs" - '((websocket "1.5") - (request "0.2.0") - (oauth2 "0.10") - (circe "2.2") - (alert "1.2") - (emojify "0.2")) - :url "https://github.com/yuya373/emacs-slack") -;; Local Variables: -;; no-byte-compile: t -;; End: diff --git a/elpa/slack-20160928.2036/slack-reaction.el b/elpa/slack-20160928.2036/slack-reaction.el deleted file mode 100644 index fbc7b50..0000000 --- a/elpa/slack-20160928.2036/slack-reaction.el +++ /dev/null @@ -1,66 +0,0 @@ -;;; slack-reaction.el --- deal with reactions -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) - -(defclass slack-reaction () - ((name :initarg :name :type string) - (count :initarg :count :type integer) - (users :initarg :users :initform ()))) - -(defmethod slack-reaction-join ((r slack-reaction) other) - (if (string= (oref r name) (oref other name)) - (progn - (cl-incf (oref r count)) - (oset r users (nconc (oref other users) (oref r users))) - r))) - -(defmethod slack-reaction-user-names ((r slack-reaction) team) - (with-slots (users) r - (mapcar #'(lambda (u) (slack-user-name u team)) - users))) - -(defmethod slack-reaction-equalp ((r slack-reaction) other) - (string= (oref r name) (oref other name))) - -(defmethod slack-reaction-to-string ((r slack-reaction)) - (let ((text (format ":%s:: %d" (oref r name) (oref r count)))) - (put-text-property 0 (length text) 'reaction r text) - text)) - -(defun slack-reaction-notify (payload team) - (let* ((user-id (plist-get payload :user)) - (room (slack-room-find (plist-get (plist-get payload :item) :channel) - team)) - (reaction (plist-get payload :reaction)) - (msg (slack-user-message "msg" - :text (format "added reaction %s" reaction) - :user user-id))) - (slack-message-notify msg room team))) - -(provide 'slack-reaction) -;;; slack-reaction.el ends here - diff --git a/elpa/slack-20160928.2036/slack-reminder.el b/elpa/slack-20160928.2036/slack-reminder.el deleted file mode 100644 index 95869a5..0000000 --- a/elpa/slack-20160928.2036/slack-reminder.el +++ /dev/null @@ -1,264 +0,0 @@ -;;; slack-reminder.el --- -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-team) - -(defconst slack-reminder-list-url "https://slack.com/api/reminders.list") -(defconst slack-reminder-add-url "https://slack.com/api/reminders.add") -(defconst slack-reminder-delete-url "https://slack.com/api/reminders.delete") -(defconst slack-reminder-complete-url "https://slack.com/api/reminders.complete") -(defconst slack-reminder-info-url "https://slack.com/api/reminders.info") - -(defclass slack-reminder-base () - ((id :initarg :id :type string) - (creator :initarg :creator :type string) - (user :initarg :user :type string) - (text :initarg :text :type string))) - -(defclass slack-recurring-reminder (slack-reminder-base) - ()) - -(defclass slack-reminder (slack-reminder-base) - ((time :initarg :time :type integer) - (complete-ts :initarg :complete_ts :type integer))) - -(defmethod slack-reminder-user ((r slack-reminder-base) team) - (slack-user-find (oref r user) team)) - -(defmethod slack-reminder-creator ((r slack-reminder-base) team) - (slack-user-find (oref r creator) team)) - -(defmethod slack-team-add-reminder ((team slack-team) reminder) - (with-slots (reminders) team - (cl-pushnew reminder reminders - :test #'(lambda (a b) (string= (oref a id) (oref b id)))))) - -(defmethod slack-reminder-completedp ((r slack-reminder)) - (not (eq 0 (oref r complete-ts)))) - -(defmethod slack-reminder-completedp ((_r slack-recurring-reminder)) - nil) - -(defun slack-reminder-create (payload) - (let ((klass (if (eq :json-false (plist-get payload :recurring)) - 'slack-reminder - 'slack-recurring-reminder))) - (apply #'make-instance klass - (slack-collect-slots klass payload)))) - -(defun slack-reminder-add () - (interactive) - (let* ((team (slack-team-select)) - (user (slack-select-from-list - ((slack-user-names team) "Select Target User: "))) - (time (read-from-minibuffer - "Time (Ex. \"in 15 minutes,\" or \"every Thursday\"): ")) - (text (read-from-minibuffer "Text: "))) - (cl-labels - ((on-reminder-add (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-reminder-add") - (let ((reminder (slack-reminder-create - (slack-decode - (plist-get data :reminder))))) - (slack-team-add-reminder team reminder) - (message "Reminder Created!"))))) - (slack-request - slack-reminder-add-url - team - :sync nil - :params (list (cons "text" text) - (cons "time" time) - (and user (cons "user" (plist-get user :id)))) - :success #'on-reminder-add)))) - -(defmethod slack-reminder-to-body ((r slack-reminder)) - (with-slots (text time complete-ts) r - (let ((time-str (format "Remind At: %s" - (slack-message-time-to-string - (number-to-string time)))) - (completed (format "Completed: %s" - (if (eq complete-ts 0) - "Not Yet" - (slack-message-time-to-string - (number-to-string complete-ts)))))) - (format "%s\n%s\n\n%s" time-str completed text)))) - -(defmethod slack-reminder-to-body ((r slack-recurring-reminder)) - (oref r text)) - -(defmethod slack-reminder-to-string ((r slack-reminder-base) team) - (with-slots (creator user) r - (let* ((header (slack-message-put-header-property - (format "From: %s To: %s" - (slack-user-name creator team) - (slack-user-name user team)))) - (body (slack-reminder-to-body r))) - (format "%s\n%s\n\n" header body)))) - -(defmethod slack-create-reminder-buffer ((team slack-team)) - (let* ((buf-name "*Slack - Reminders*") - (buf (get-buffer-create buf-name))) - (with-current-buffer buf - (setq buffer-read-only nil) - (erase-buffer) - (goto-char (point-min)) - (with-slots (reminders) team - (cl-loop for reminder in reminders - do (insert (slack-reminder-to-string reminder team)))) - (setq buffer-read-only t)) - buf)) - -(defmethod slack-reminder-sort-key ((r slack-reminder)) - (oref r time)) - -(defmethod slack-reminder-sort-key ((r slack-recurring-reminder)) - 0) - -(defun slack-reminder-sort (team) - (with-slots (reminders) team - (setq reminders - (cl-sort reminders #'< - :key #'(lambda (r) (slack-reminder-sort-key r)))))) - -(defun slack-reminder-list () - (interactive) - (let ((team (slack-team-select))) - (cl-labels - ((on-reminder-list - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-reminder-list") - (oset team reminders - (cl-loop - for payload in (slack-decode - (append (plist-get data :reminders) - nil)) - collect (slack-reminder-create payload))) - (slack-reminder-sort team) - (if (< 0 (length (oref team reminders))) - (funcall - slack-buffer-function - (slack-create-reminder-buffer team)) - (message "No Reminders!"))))) - (slack-request - slack-reminder-list-url - team - :sync nil - :success #'on-reminder-list)))) - -(defmethod slack-reminders-alist ((team slack-team) &optional filter) - (cl-labels ((text (r) - (with-slots (creator user text) r - (format "Creator: %s Target: %s Content: %s" - (slack-user-name creator team) - (slack-user-name user team) - text)))) - (with-slots (reminders) team - (mapcar #'(lambda (r) (cons (text r) r)) - (if filter - (cl-remove-if-not #'(lambda (r) (funcall filter r)) - reminders) - reminders))))) - -(defmethod slack-team-delete-reminder ((team slack-team) r) - (with-slots (reminders) team - (setq reminders - (cl-remove-if #'(lambda (e) - (string= (oref e id) (oref r id))) - reminders)))) - -(defun slack-reminder-select (team &optional filter) - (slack-select-from-list - ((slack-reminders-alist team filter) "Select: "))) - -(defun slack-reminder-delete () - (interactive) - (let* ((team (slack-team-select)) - (reminder (slack-reminder-select team))) - (cl-labels - ((on-reminder-delete (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-reminder-delete") - (slack-team-delete-reminder team reminder) - (message "Reminder Deleted!")))) - (slack-request - slack-reminder-delete-url - team - :sync nil - :params (list (cons "reminder" (oref reminder id))) - :success #'on-reminder-delete)))) - -(defmethod slack-reminder-info ((r slack-reminder-base) team callback) - (cl-labels - ((on-reminder-info (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-reminder-info") - (let ((reminder (slack-reminder-create - (plist-get (slack-decode data) - :reminder)))) - (funcall callback reminder))))) - (slack-request - slack-reminder-info-url - team - :sync nil - :params (list (cons "reminder" (oref r id))) - :success #'on-reminder-info))) - -(defmethod slack-reminder-refresh ((r slack-reminder-base) team) - (slack-reminder-info - r team - #'(lambda (reminder) - (with-slots (reminders) team - (setq reminders - (cl-remove-if #'(lambda (e) (string= (oref e id) - (oref reminder id))) - reminders)) - (push reminder reminders)) - (message "Reminder Updated!")))) - -(defun slack-reminder-complete () - (interactive) - (let* ((team (slack-team-select)) - (reminder (slack-reminder-select - team - #'(lambda (r) - (not (slack-reminder-completedp r)))))) - (cl-labels - ((on-reminder-complete (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-reminder-complete") - (slack-reminder-refresh reminder team)))) - (slack-request - slack-reminder-complete-url - team - :sync nil - :params (list (cons "reminder" (oref reminder id))) - :success #'on-reminder-complete)))) - -(provide 'slack-reminder) -;;; slack-reminder.el ends here diff --git a/elpa/slack-20160928.2036/slack-reply.el b/elpa/slack-20160928.2036/slack-reply.el deleted file mode 100644 index 27ae653..0000000 --- a/elpa/slack-20160928.2036/slack-reply.el +++ /dev/null @@ -1,49 +0,0 @@ -;;; slack-reply.el ---handle reply from slack -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'eieio) -(require 'slack-message) - -(defmethod slack-message-handle-reply ((m slack-reply) team) - (with-slots (reply-to) m - (let ((sent-msg (slack-message-find-sent m team))) - (if sent-msg - (progn - (oset sent-msg ts (oref m ts)) - (slack-message-update sent-msg team)))))) - -(defmethod slack-message-find-sent ((m slack-reply) team) - (with-slots (reply-to) m - (with-slots (sent-message) team - (let ((found (gethash reply-to sent-message))) - (remhash reply-to sent-message) - found)))) - -(defmethod slack-message-sender-equalp ((m slack-reply) sender-id) - (string= (oref m user) sender-id)) - - -(provide 'slack-reply) -;;; slack-reply.el ends here diff --git a/elpa/slack-20160928.2036/slack-request.el b/elpa/slack-20160928.2036/slack-request.el deleted file mode 100644 index 3a182d0..0000000 --- a/elpa/slack-20160928.2036/slack-request.el +++ /dev/null @@ -1,80 +0,0 @@ -;;; slack-request.el ---slack request function -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'json) -(require 'request) - -(defcustom slack-request-timeout 5 - "Request Timeout in seconds." - :group 'slack) - -(defun slack-parse-to-hash () - (let ((json-object-type 'hash-table)) - (let ((res (json-read-from-string (buffer-string)))) - res))) - -(defun slack-parse-to-plist () - (let ((json-object-type 'plist)) - (json-read))) - -(defun slack-request-parse-payload (payload) - (let ((json-object-type 'plist)) - (json-read-from-string payload))) - -(cl-defun slack-request (url team &key - (type "GET") - (success) - (error nil) - (params nil) - (parser #'slack-parse-to-plist) - (sync t) - (files nil) - (headers nil) - (timeout slack-request-timeout)) - (request - url - :type type - :sync sync - :params (cons (cons "token" (oref team token)) - params) - :files files - :headers headers - :parser parser - :success success - :error error - :timeout timeout)) - -(cl-defmacro slack-request-handle-error ((data req-name) &body body) - "Bind error to e if present in DATA." - `(if (eq (plist-get ,data :ok) :json-false) - (message "Failed to request %s: %s" - ,req-name - (plist-get ,data :error)) - (progn - ,@body))) - -(provide 'slack-request) -;;; slack-request.el ends here diff --git a/elpa/slack-20160928.2036/slack-room.el b/elpa/slack-20160928.2036/slack-room.el deleted file mode 100644 index 3f0e41b..0000000 --- a/elpa/slack-20160928.2036/slack-room.el +++ /dev/null @@ -1,472 +0,0 @@ -;;; slack-room.el --- slack generic room interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-request) -(require 'slack-message) - -(defvar slack-current-room-id) -(defvar slack-current-team-id) -(defvar slack-buffer-function) -(defconst slack-room-pins-list-url "https://slack.com/api/pins.list") - -(defclass slack-room () - ((name :initarg :name :type string) - (id :initarg :id) - (created :initarg :created) - (has-pins :initarg :has_pins) - (last-read :initarg :last_read :type string :initform "0") - (latest :initarg :latest) - (oldest :initarg :oldest) - (unread-count :initarg :unread_count) - (unread-count-display :initarg :unread_count_display :initform 0 :type integer) - (messages :initarg :messages :initform ()) - (team-id :initarg :team-id))) - -(defgeneric slack-room-name (room)) -(defgeneric slack-room-history (room team &optional oldest after-success sync)) -(defgeneric slack-room-update-mark-url (room)) - -(defun slack-room-create (payload team class) - (cl-labels - ((prepare (p) - (plist-put p :members - (append (plist-get p :members) nil)) - (plist-put p :latest - (slack-message-create (plist-get p :latest))) - (plist-put p :team-id (oref team id)) - p)) - (let ((attributes (slack-collect-slots class (prepare payload)))) - (apply #'make-instance class attributes)))) - -(defmethod slack-room-subscribedp ((_room slack-room) _team) - nil) - -(defmethod slack-room-buffer-name ((room slack-room)) - (concat "*Slack*" - " : " - (slack-room-name-with-team-name room))) - -(cl-defmacro slack-room-request-update (room team url latest after-success sync) - `(cl-labels - ((on-request-update - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-request-update") - (let* ((datum (plist-get data :messages)) - (messages - (cl-loop for data across datum - collect (slack-message-create data :room ,room)))) - (if ,latest - (slack-room-set-prev-messages ,room messages) - (slack-room-set-messages ,room messages) - (slack-room-update-last-read - room - (make-instance 'slack-message :ts "0"))) - (if (and ,after-success - (functionp ,after-success)) - (funcall ,after-success)))))) - (slack-request - ,url - ,team - :params (list (cons "channel" (oref ,room id)) - (if ,latest - (cons "latest" ,latest))) - :success #'on-request-update - :sync (if ,sync t nil)))) - -(cl-defun slack-room-make-buffer-with-room (room team &key update) - (with-slots (messages latest) room - (if (or update (< (length messages) 1)) - (slack-room-history room team)) - (funcall slack-buffer-function - (slack-buffer-create room team)))) - -(cl-defmacro slack-select-from-list ((alist prompt) &body body) - "Bind candidates from selected." - (let ((key (cl-gensym))) - `(let* ((,key (let ((completion-ignore-case t)) - (completing-read (format "%s" ,prompt) - ,alist nil t))) - (selected (cdr (cl-assoc ,key ,alist :test #'string=)))) - ,@body - selected))) - -(defun slack-room-select (rooms) - (let* ((alist (slack-room-names - rooms - #'(lambda (rs) - (cl-remove-if #'(lambda (r) - (or (not (slack-room-member-p r)) - (slack-room-archived-p r) - (not (slack-room-open-p r)))) - rs))))) - (slack-select-from-list - (alist "Select Channel: ") - (slack-room-make-buffer-with-room - selected - (slack-team-find (oref selected team-id)) - :update nil)))) - -(cl-defun slack-room-list-update (url success team &key (sync t)) - (slack-request - url - team - :success success - :sync sync)) - -(defun slack-room-update-messages () - (interactive) - (unless (and (boundp 'slack-current-room-id) - (boundp 'slack-current-team-id)) - (error "Call From Slack Room Buffer")) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id team)) - (cur-point (point))) - (slack-room-history room team) - (slack-buffer-create - room team :insert-func - #'(lambda (room team) - (slack-buffer-widen - (let ((inhibit-read-only t)) - (delete-region (point-min) (marker-position lui-output-marker)))) - (slack-buffer-insert-previous-link room) - (slack-buffer-insert-messages room team) - (goto-char cur-point))))) - -(defmethod slack-room-render-prev-messages ((room slack-room) team - oldest ts) - (slack-buffer-create - room team - :insert-func - #'(lambda (room team) - (slack-buffer-widen - (let ((inhibit-read-only t) - (loading-message-end - (slack-buffer-ts-eq (point-min) (point-max) oldest))) - (delete-region (point-min) loading-message-end) - (slack-buffer-insert-prev-messages room team oldest))) - (slack-buffer-goto ts)))) - -(defmethod slack-room-prev-link-info ((room slack-room)) - (with-slots (oldest) room - (if oldest - (oref oldest ts)))) - -(defun slack-room-load-prev-messages () - (interactive) - (let* ((cur-point (point)) - (ts (get-text-property (next-single-property-change cur-point 'ts) - 'ts)) - (oldest (ignore-errors (get-text-property 0 'oldest - (thing-at-point 'line)))) - (current-team (slack-team-find slack-current-team-id)) - (current-room (slack-room-find slack-current-room-id - current-team))) - (slack-room-history current-room - current-team - oldest - #'(lambda () - (slack-room-render-prev-messages current-room - current-team - oldest ts))))) - -(defun slack-room-find-message (room ts) - (cl-find-if #'(lambda (m) (string= ts (oref m ts))) - (oref room messages) - :from-end t)) - -(defmethod slack-room-name-with-team-name ((room slack-room)) - (with-slots (team-id name) room - (let ((team (slack-team-find team-id))) - (format "%s - %s" (oref team name) name)))) - -(defmacro slack-room-names (rooms &optional filter) - `(cl-labels - ((latest-ts (room) - (with-slots (latest) room - (if latest (oref latest ts) "0"))) - (unread-count (room) - (with-slots (unread-count-display) room - (if (< 0 unread-count-display) - (concat "(" - (number-to-string unread-count-display) - ")") - ""))) - (sort-rooms (rooms) - (nreverse - (cl-sort rooms #'string< - :key #'(lambda (name-with-room) (latest-ts (cdr name-with-room)))))) - (build-label (room) - (concat (im-presence room) - (format "%s %s" - (slack-room-name-with-team-name room) - (unread-count room)))) - (im-presence (room) - (if (object-of-class-p room 'slack-im) - (slack-im-user-presence room) - " ")) - (build-cons (room) - (cons (build-label room) room))) - (sort-rooms - (cl-loop for room in (if ,filter - (funcall ,filter ,rooms) - ,rooms) - collect (cons (build-label room) room))))) - -(defmethod slack-room-name ((room slack-room)) - (oref room name)) - -(defmethod slack-room-update-last-read ((room slack-room) msg) - (with-slots (ts) msg - (oset room last-read ts))) - -(defmethod slack-room-latest-messages ((room slack-room) messages) - (with-slots (last-read) room - (cl-remove-if #'(lambda (m) - (or (string< (oref m ts) last-read) - (string= (oref m ts) last-read))) - messages))) - -(defun slack-room-sort-messages (messages) - (cl-sort messages - #'string< - :key #'(lambda (m) (oref m ts)))) - -(defmethod slack-room-sorted-messages ((room slack-room)) - (with-slots (messages) room - (slack-room-sort-messages (copy-sequence messages)))) - -(defmethod slack-room-set-prev-messages ((room slack-room) prev-messages) - (slack-room-set-messages - room - (cl-delete-duplicates (append (oref room messages) - prev-messages) - :test #'slack-message-equal))) - -(defmethod slack-room-set-messages ((room slack-room) m) - (let ((sorted (slack-room-sort-messages m))) - (oset room oldest (car sorted)) - (oset room messages sorted) - (oset room latest (car (last sorted))))) - -(defmethod slack-room-prev-messages ((room slack-room) from) - (with-slots (messages) room - (cl-remove-if #'(lambda (m) - (or (string< from (oref m ts)) - (string= from (oref m ts)))) - (slack-room-sort-messages (copy-sequence messages))))) - -(defmethod slack-room-update-mark ((room slack-room) team msg) - (cl-labels ((on-update-mark (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-update-mark")))) - (with-slots (ts) msg - (with-slots (id) room - (slack-request - (slack-room-update-mark-url room) - team - :type "POST" - :params (list (cons "channel" id) - (cons "ts" ts)) - :success #'on-update-mark - :sync nil))))) - -(defun slack-room-pins-list () - (interactive) - (unless (and (bound-and-true-p slack-current-room-id) - (bound-and-true-p slack-current-team-id)) - (error "Call from slack room buffer")) - (let* ((team (slack-team-find slack-current-team-id)) - (room (slack-room-find slack-current-room-id - team)) - (channel (oref room id))) - (cl-labels ((on-pins-list (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-pins-list") - (slack-room-on-pins-list - (plist-get data :items) - room team)))) - (slack-request - slack-room-pins-list-url - team - :params (list (cons "channel" channel)) - :success #'on-pins-list - :sync nil)))) - -(defun slack-room-on-pins-list (items room team) - (cl-labels ((buffer-name (room) - (concat "*Slack - Pinned Items*" - " : " - (slack-room-name-with-team-name room)))) - (let* ((messages (mapcar #'slack-message-create - (mapcar #'(lambda (i) - (plist-get i :message)) - items))) - (buf-header (propertize "Pinned Items" - 'face '(:underline - t - :weight bold)))) - (funcall slack-buffer-function - (slack-buffer-create-info - (buffer-name room) - #'(lambda () - (insert buf-header) - (insert "\n\n") - (mapc #'(lambda (m) (insert - (slack-message-to-string m))) - messages))) - team)))) - -(defun slack-select-rooms () - (interactive) - (let ((team (slack-team-select))) - (slack-room-select - (cl-loop for team in (list team) - append (with-slots (groups ims channels) team - (append ims groups channels)))))) - -(defun slack-create-room (url team success) - (slack-request - url - team - :type "POST" - :params (list (cons "name" (read-from-minibuffer "Name: "))) - :success success - :sync nil)) - -(defun slack-room-rename (url room-alist-func) - (cl-labels - ((on-rename-success (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-rename")))) - (let* ((team (slack-team-select)) - (room-alist (funcall room-alist-func team)) - (room (slack-select-from-list - (room-alist "Select Channel: "))) - (name (read-from-minibuffer "New Name: "))) - (slack-request - url - team - :params (list (cons "channel" (oref room id)) - (cons "name" name)) - :success #'on-rename-success - :sync nil)))) - -(defmacro slack-current-room-or-select (room-alist-func) - `(if (and (boundp 'slack-current-room-id) - (boundp 'slack-current-team-id)) - (slack-room-find slack-current-room-id - (slack-team-find slack-current-team-id)) - (let* ((room-alist (funcall ,room-alist-func))) - (slack-select-from-list - (room-alist "Select Channel: "))))) - -(defmacro slack-room-invite (url room-alist-func) - `(cl-labels - ((on-group-invite (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-invite") - (if (plist-get data :already_in_group) - (message "User already in group") - (message "Invited!"))))) - (let* ((team (slack-team-select)) - (room (slack-current-room-or-select - #'(lambda () - (funcall ,room-alist-func team - #'(lambda (rooms) - (cl-remove-if #'slack-room-archived-p - rooms)))))) - (user-id (plist-get (slack-select-from-list - ((slack-user-names team) - "Select User: ")) :id))) - (slack-request - ,url - team - :params (list (cons "channel" (oref room id)) - (cons "user" user-id)) - :success #'on-group-invite - :sync nil)))) - -(defmethod slack-room-member-p ((_room slack-room)) - t) - -(defmethod slack-room-archived-p ((_room slack-room)) - nil) - -(defmethod slack-room-open-p ((_room slack-room)) - t) - -(defmethod slack-room-equal-p ((room slack-room) other) - (with-slots (id) room - (with-slots ((other-id id)) other - (string= id other-id)))) - -(defun slack-room-deleted (id team) - (let ((room (slack-room-find id team))) - (cond - ((object-of-class-p room 'slack-channel) - (with-slots (channels) team - (setq channels (cl-delete-if #'(lambda (c) (slack-room-equal-p room c)) - channels))) - (message "Channel: %s deleted" - (slack-room-name-with-team-name room)))))) - -(cl-defun slack-room-request-with-id (url id team success) - (slack-request - url - team - :params (list (cons "channel" id)) - :success success - :sync nil)) - -(defmethod slack-room-history ((room slack-room) team - &optional - oldest - after-success - async) - (slack-room-request-update room - team - (slack-room-history-url room) - oldest - after-success - (if async nil t))) - -(defmethod slack-room-inc-unread-count ((room slack-room)) - (cl-incf (oref room unread-count-display))) - -(defun slack-room-find-by-name (name team) - (cl-labels - ((find-by-name (rooms name) - (cl-find-if #'(lambda (e) (string= name - (slack-room-name e))) - rooms))) - (or (find-by-name (oref team groups) name) - (find-by-name (oref team channels) name) - (find-by-name (oref team ims) name)))) - -(provide 'slack-room) -;;; slack-room.el ends here diff --git a/elpa/slack-20160928.2036/slack-search.el b/elpa/slack-20160928.2036/slack-search.el deleted file mode 100644 index e82edde..0000000 --- a/elpa/slack-20160928.2036/slack-search.el +++ /dev/null @@ -1,459 +0,0 @@ -;;; slack-search.el --- -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) -(require 'slack-room) - -(defclass slack-search-result (slack-room) - ((type :initarg :type :type symbol) - (query :initarg :query :type string) - (per-page :initarg :per-page :type integer) - (total-page :initarg :total-page :type integer) - (current-page :initarg :current-page :type integer) - (total-messages :initarg :total-messages :type integer) - (sort :initarg :sort :type string) - (sort-dir :initarg :sort-dir :type string) - (last-channel-id :initarg :last-channel-id :type string :initform ""))) - -(defclass slack-file-search-result (slack-search-result) ()) - -(defclass slack-search-message () - ((user-id :initarg :user-id :type string) - (username :initarg :username :type string) - (ts :initarg :ts :type string) - (text :initarg :text :type string) - (previous-2 :initarg :previous-2) - (previous :initarg :previous) - (next :initarg :next) - (next-2 :initarg :next-2) - (info :initarg :info))) - -(defclass slack-search-message-info () - ((channel-id :initarg :channel-id :type string) - (channel-name :initarg :channel-name :type string) - (permalink :initarg :permalink :type string :initform "") - (result-id :initarg :result-id :type string))) - -(defun slack-search-result-id (type query sort sort-dir) - (format "Q%s%s%s%s" type query sort sort-dir)) - -(defun slack-search-create-message-info (payload) - (let ((channel (plist-get payload :channel))) - (make-instance 'slack-search-message-info - :channel-id (plist-get channel :id) - :channel-name (plist-get channel :name) - :permalink (plist-get payload :permalink)))) - -(defmethod slack-search-create-message ((room slack-search-result) payload) - (cl-labels ((create-message - (params info) - (let ((previous-2 (if (plist-get params :previous_2) - (create-message (plist-get params :previous_2) - info))) - (previous (if (plist-get params :previous) - (create-message (plist-get params :previous) - info))) - (next (if (plist-get params :next) - (create-message (plist-get params :next) - info))) - (next-2 (if (plist-get params :next_2) - (create-message (plist-get params :next_2) - info)))) - (make-instance 'slack-search-message - :info info - :user-id (plist-get params :user) - :username (plist-get params :username) - :text (plist-get params :text) - :ts (plist-get params :ts) - :previous-2 previous-2 - :previous previous - :next next - :next-2 next-2))) - (create-info - (params result) - (let ((channel (plist-get params :channel))) - (make-instance 'slack-search-message-info - :result-id (oref result id) - :channel-id (plist-get channel :id) - :channel-name (plist-get channel :name) - :permalink (plist-get params :permalink))))) - (let ((info (create-info payload room))) - (create-message payload info)))) - -(defmethod slack-search-create-message ((_room slack-file-search-result) payload) - (slack-file-create payload)) - -(defun slack-create-search-result (plist team type) - (let* ((result (cl-case type - ('message (apply #'make-instance 'slack-search-result - (slack-collect-slots 'slack-search-result - plist))) - ('file (apply #'make-instance 'slack-file-search-result - (slack-collect-slots 'slack-file-search-result - plist))))) - (result-messages (cl-loop - for message in (plist-get plist :messages) - collect (slack-search-create-message result message)))) - (slack-room-set-messages result result-messages) - (with-slots (search-results) team - (setq search-results - (cl-remove-if #'(lambda (other) - (slack-room-equal-p result other)) - search-results)) - (push result search-results)) - result)) - -(defun slack-search-create-result-params (data team type sort sort-dir) - (let* ((messages (cl-case type - ('message (plist-get data :messages)) - ('file (plist-get data :files)))) - (paging (plist-get messages :paging)) - (query (plist-get data :query)) - (plist (list :type type - :team-id (oref team id) - :id (slack-search-result-id - type query sort sort-dir) - :sort sort - :sort-dir sort-dir - :query query - :per-page (plist-get paging :count) - :total-page (plist-get paging :pages) - :current-page (plist-get paging :page) - :total-messages (plist-get paging :total) - :messages - (append (plist-get messages :matches) - nil)))) - - plist)) - -(defun slack-search-query-params () - (let ((team (slack-team-select)) - (query (read-from-minibuffer "Query: ")) - (sort (completing-read "Sort: " `("score" "timestamp") - nil t)) - (sort-dir (completing-read "Direction: " `("desc" "asc") - nil t))) - (list team query sort sort-dir))) - -(defun slack-search-pushnew (search-result team) - (cl-pushnew search-result (oref team search-results) - :test #'slack-room-equal-p)) - -(defun slack-search-from-messages () - (interactive) - (cl-destructuring-bind (team query sort sort-dir) (slack-search-query-params) - (let ((type 'message)) - (cl-labels - ((on-search - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-search-from-messages") - (let* ((params (slack-search-create-result-params - data team type sort sort-dir)) - (search-result (slack-create-search-result params team 'message))) - (slack-search-pushnew search-result team) - (funcall slack-buffer-function - (slack-buffer-create search-result - team :type 'info)))))) - (let ((same-search (slack-room-find (slack-search-result-id - type query sort sort-dir) - team))) - (if same-search - (progn - (message "Same Query Already Exist") - (funcall slack-buffer-function - (slack-buffer-create same-search - team - :type 'info))) - (slack-search-request-message team - query - sort - sort-dir - #'on-search))))) - )) - -(defun slack-search-from-files () - (interactive) - (cl-destructuring-bind (team query sort sort-dir) (slack-search-query-params) - (let ((type 'file)) - (cl-labels - ((on-search - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-search-from-files") - (let* ((params (slack-search-create-result-params - data team type sort sort-dir)) - (search-result (slack-create-search-result params team 'file))) - (slack-search-pushnew search-result team) - (funcall slack-buffer-function - (slack-buffer-create search-result - team :type 'info)))))) - (let ((same-search (slack-room-find (slack-search-result-id type query - sort sort-dir) - team))) - (if same-search - (progn - (message "Same Query Already Exist") - (funcall slack-buffer-function - (slack-buffer-create same-search - team - :type 'info))) - (slack-search-request-file team - query - sort - sort-dir - #'on-search))))))) - -(cl-defun slack-search-request-message (team query sort sort-dir success - &optional - (page 1) - (async t)) - (slack-search-request team query sort sort-dir success page async - "https://slack.com/api/search.messages")) - -(cl-defun slack-search-request-file (team query sort sort-dir success - &optional - (page 1) - (async t)) - (slack-search-request team query sort sort-dir success page async - "https://slack.com/api/search.files")) - -(defun slack-search-request (team query sort sort-dir success page async url) - (if (< 0 (length query)) - (slack-request - url - team - :type "POST" - :params (list (cons "query" query) - (cons "sort" sort) - (cons "sort_dir" sort-dir) - (cons "page" (number-to-string page))) - :success success - :sync (not async)))) - -(defun slack-search-alist (team) - (with-slots (search-results) team - (cl-loop for s in search-results - collect (cons (slack-room-buffer-name s) s)))) - -(defun slack-search-select () - (interactive) - (let* ((team (slack-team-select)) - (alist (slack-search-alist team))) - (slack-select-from-list - (alist "Select Search: ") - (funcall slack-buffer-function - (slack-buffer-create selected - team - :type 'info))))) - -;; protocols -(defmethod slack-room-update-mark ((_room slack-search-result) _team _msg)) -(defmethod slack-room-sorted-messages ((room slack-search-result)) - (copy-sequence (oref room messages))) - -(defmethod slack-room-update-last-read ((room slack-search-result) msg) - (if (not (slot-exists-p msg 'info)) - (progn - (oset room last-read (oref msg ts)) - (oset room last-channel-id "")) - (with-slots (ts info) msg - (with-slots (channel-id) info - (oset room last-read ts) - (oset room last-channel-id channel-id))))) - -(defmethod slack-search-get-index ((_search-result slack-file-search-result) - messages last-read &optional _last-chanel-id) - (cl-loop for i from 0 upto (1- (length messages)) - for m = (nth i messages) - if (string= (oref m ts) last-read) - return i)) - -(defmethod slack-search-get-index ((_search-result slack-search-result) - messages last-read &optional last-channel-id) - (cl-loop for i from 0 upto (1- (length messages)) - for m = (nth i messages) - if (and (string= (oref m ts) last-read) - (string= (oref (oref m info) channel-id) - last-channel-id)) - return i)) - -(defmethod slack-room-latest-messages ((room slack-search-result) messages) - (with-slots (type last-read last-channel-id) room - (let* ((r-messages (reverse messages)) - (nth (slack-search-get-index room r-messages - last-read last-channel-id))) - (if nth - (nreverse - (nthcdr (1+ nth) r-messages)) - (copy-sequence messages))))) - -(defmethod slack-room-prev-messages ((room slack-file-search-result) oldest) - (let* ((messages (reverse (oref room messages))) - (nth (slack-search-get-index room messages oldest))) - (if nth - (nreverse (nthcdr (1+ nth) messages))))) - -(defmethod slack-room-prev-messages ((room slack-search-result) param) - (let* ((oldest (car param)) - (channel-id (cdr param)) - (messages (reverse (oref room messages))) - (nth (slack-search-get-index room messages - oldest channel-id))) - (if nth - (nreverse (nthcdr (1+ nth) messages))))) - -(defmethod slack-room-render-prev-messages ((room slack-search-result) - team oldest ts) - (slack-buffer-create - room team - :insert-func - #'(lambda (room team) - (slack-buffer-widen - (let* ((inhibit-read-only t) - (oldest-ts (if (listp oldest) (car oldest) oldest)) - (loading-message-end (slack-buffer-ts-eq (point-min) - (point-max) - oldest-ts))) - (delete-region (point-min) loading-message-end) - (slack-buffer-insert-prev-messages room team oldest))) - (slack-buffer-goto ts)) - :type 'info)) - -(defmethod slack-buffer-insert-prev-messages ((room slack-search-result) team oldest) - (slack-buffer-widen - (let ((messages (slack-room-prev-messages room oldest))) - (if messages - (progn - (slack-buffer-insert-previous-link room) - (cl-loop for m in messages - do (slack-buffer-insert m team t))) - (set-marker lui-output-marker (point-min)) - (lui-insert "(no more messages)\n")) - (slack-buffer-recover-lui-output-marker)))) - -(defmethod slack-room-prev-link-info ((room slack-file-search-result)) - (with-slots (oldest) room - (oref oldest ts))) - -(defmethod slack-room-prev-link-info ((room slack-search-result)) - (with-slots (oldest) room - (with-slots (info ts) oldest - (cons ts (oref info channel-id))))) - -(defmethod slack-message-equal ((m slack-search-message) n) - (with-slots ((m-info info) (m-ts ts)) m - (with-slots ((m-channel-id channel-id)) m-info - (with-slots ((n-info info) (n-ts ts)) n - (with-slots ((n-channel-id channel-id)) n-info - (and (string= m-channel-id n-channel-id) - (string= m-ts n-ts))))))) - -(defmethod slack-room-buffer-name ((room slack-search-result)) - (with-slots (query sort sort-dir team-id type) room - (let ((team (slack-team-find team-id))) - (format "%s - %s Query: %s Sort: %s Order: %s" - (oref team name) - (eieio-object-class room) - query sort sort-dir)))) - -(defmethod slack-message-to-string ((message slack-search-message) team) - (with-slots (info text username) message - (with-slots (channel-id permalink) info - (let* ((header (format "%s" username)) - (channel (slack-room-find channel-id team)) - (body (slack-message-unescape-string - (format "%s\n\n------------\nChanel: %s\nPermalink: %s" - text - (slack-room-name channel) - permalink) - team))) - (slack-message-put-header-property header) - (slack-message-put-text-property body) - (format "%s\n%s\n" header body))))) - -(defmethod slack-room-set-prev-messages ((room slack-search-result) prev) - (slack-room-set-messages room (nreverse - (nconc (nreverse prev) (oref room messages))))) - -(defmethod slack-room-set-messages ((room slack-search-result) messages) - (let ((msgs (nreverse messages))) - (oset room messages msgs) - (oset room latest (car (last msgs))) - (oset room oldest (car msgs)))) - -(defmethod slack-room-history ((room slack-search-result) team - &optional - oldest after-success async) - (cl-labels - ((on-history - (&key data &allow-other-keys) - (slack-request-handle-error - (data "slack-room-history") - (let* ((matches (cl-case (eieio-object-class room) - ('slack-search-result (plist-get data :messages)) - ('slack-file-search-result (plist-get data :files)))) - (messages (cl-loop - for match across (plist-get matches :matches) - collect (slack-search-create-message room match)))) - (oset room current-page - (plist-get (plist-get matches :paging) :page)) - (if oldest - (slack-room-set-prev-messages room messages) - (let ((init-msg (make-instance 'slack-search-message - :ts "0" :info - (make-instance 'slack-search-message-info - :channel-id "")))) - (slack-room-update-last-read room init-msg)) - (slack-room-set-messages room messages)) - (if after-success - (funcall after-success)))))) - (let* ((current-page (oref room current-page)) - (total-page (oref room total-page)) - (next-page (if oldest - (1+ current-page) - 1))) - (with-slots (query sort sort-dir) room - (cl-case (eieio-object-class room) - ('slack-search-result - (slack-search-request-message team - query - sort - sort-dir - #'on-history - next-page - async)) - ('slack-file-search-result - (slack-search-request-file team - query - sort - sort-dir - #'on-history - next-page - async))))))) - -(provide 'slack-search) -;;; slack-search.el ends here diff --git a/elpa/slack-20160928.2036/slack-team.el b/elpa/slack-20160928.2036/slack-team.el deleted file mode 100644 index 109d0aa..0000000 --- a/elpa/slack-20160928.2036/slack-team.el +++ /dev/null @@ -1,202 +0,0 @@ -;;; slack-team.el --- team class -*- lexical-binding: t; -*- - -;; Copyright (C) 2016 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'eieio) -(require 'slack-util) - -(defvar slack-teams nil) -(defvar slack-current-team nil) -(defcustom slack-prefer-current-team nil - "If set to t, using `slack-current-team' for interactive function. -use `slack-change-current-team' to change `slack-current-team'" - :group 'slack) - -(defclass slack-team () - ((id :initarg :id) - (token :initarg :token :initform nil) - (client-id :initarg :client-id) - (client-secret :initarg :client-secret) - (name :initarg :name :initform nil) - (domain :initarg :domain) - (self :initarg :self) - (self-id :initarg :self-id) - (self-name :initarg :self-name) - (channels :initarg :channels) - (groups :initarg :groups) - (ims :initarg :ims) - (file-room :initform nil) - (search-results :initform nil) - (users :initarg :users) - (bots :initarg :bots) - (ws-url :initarg :ws-url) - (ws-conn :initarg :ws-conn :initform nil) - (ping-timer :initform nil) - (check-ping-timeout-timer :initform nil) - (check-ping-timeout-sec :initarg :check-ping-timeout-sec - :initform 20) - (reconnect-auto :initarg :reconnect-auto :initform t) - (reconnect-timer :initform nil) - (reconnect-after-sec :initform 10) - (reconnect-count :initform 0) - (reconnect-count-max :initform 360) - (last-pong :initform nil) - (waiting-send :initform nil) - (sent-message :initform (make-hash-table)) - (message-id :initform 0) - (connected :initform nil) - (subscribed-channels :initarg :subscribed-channels - :type list :initform nil) - (typing :initform nil) - (typing-timer :initform nil) - (reminders :initform nil :type list) - (ping-check-timers :initform (slack-ws-init-ping-check-timers)))) - -(defun slack-team-find (id) - (cl-find-if #'(lambda (team) (string= id (oref team id))) - slack-teams)) - -(defmethod slack-team-disconnect ((team slack-team)) - (slack-ws-close team)) - -(defmethod slack-team-equalp ((team slack-team) other) - (with-slots (client-id) team - (string= client-id (oref other client-id)))) - -(defmethod slack-team-name ((team slack-team)) - (oref team name)) - -;;;###autoload -(defun slack-register-team (&rest plist) - "PLIST must contain :name :client-id :client-secret with value. -setting :token will reduce your configuration step. -you will notified when receive message with channel included in subscribed-chennels. -if :default is t and `slack-prefer-current-team' is t, skip selecting team when channels listed. -you can change current-team with `slack-change-current-team'" - (interactive - (let ((name (read-from-minibuffer "Team Name: ")) - (client-id (read-from-minibuffer "Client Id: ")) - (client-secret (read-from-minibuffer "Cliend Secret: ")) - (token (read-from-minibuffer "Token: "))) - (list :name name :client-id client-id :client-secret client-secret - :token token))) - (cl-labels ((same-client-id - (client-id) - (cl-find-if #'(lambda (team) - (string= client-id (oref team client-id))) - slack-teams)) - (missing (plist) - (cl-remove-if - #'null - (mapcar #'(lambda (key) - (unless (plist-member plist key) - key)) - '(:name :client-id :client-secret))))) - (let ((missing (missing plist))) - (if missing - (error "Missing Keyword: %s" missing))) - (let ((team (apply #'slack-team "team" - (slack-collect-slots 'slack-team plist)))) - (let ((same-team (cl-find-if - #'(lambda (o) (slack-team-equalp team o)) - slack-teams))) - (if same-team - (progn - (slack-team-disconnect same-team) - (slack-start team)))) - - (setq slack-teams - (cons team - (cl-remove-if #'(lambda (other) - (slack-team-equalp team other)) - slack-teams))) - (if (plist-get plist :default) - (setq slack-current-team team))))) - -(defun slack-team-find-by-name (name) - (if name - (cl-find-if #'(lambda (team) (string= name (oref team name))) - slack-teams))) - -(cl-defun slack-team-select (&optional no-default) - (cl-labels ((select-team () - (slack-team-find-by-name - (completing-read - "Select Team: " - (mapcar #'(lambda (team) (oref team name)) - (slack-team-connected-list)))))) - (let ((team (if (and slack-prefer-current-team - slack-current-team - (not no-default)) - slack-current-team - (select-team)))) - ;; (if (and slack-prefer-current-team - ;; (not slack-current-team) - ;; (not no-default)) - ;; (if (yes-or-no-p (format "Set %s to current-team?" - ;; (oref team name))) - ;; (setq slack-current-team team))) - team))) - -(defmethod slack-team-connectedp ((team slack-team)) - (oref team connected)) - -(defun slack-team-connected-list () - (cl-remove-if #'null - (mapcar #'(lambda (team) - (if (slack-team-connectedp team) team)) - slack-teams))) - -(defun slack-change-current-team () - (interactive) - (let ((team (slack-team-find-by-name - (completing-read - "Select Team: " - (mapcar #'(lambda (team) (oref team name)) - slack-teams))))) - (setq slack-current-team team) - (message "Set slack-current-team to %s" (or (and team (oref team name)) - "nil")) - (if team - (slack-team-connect team)))) - -(defmethod slack-team-connect ((team slack-team)) - (unless (slack-team-connectedp team) - (slack-start team))) - -(defun slack-team-delete () - (interactive) - (let ((selected (slack-team-select t))) - (if (yes-or-no-p (format "Delete %s from `slack-teams'?" - (oref selected name))) - (progn - (setq slack-teams - (cl-remove-if #'(lambda (team) - (slack-team-equalp selected team)) - slack-teams)) - (slack-team-disconnect selected) - (message "Delete %s from `slack-teams'" (oref selected name)))))) - -(provide 'slack-team) -;;; slack-team.el ends here diff --git a/elpa/slack-20160928.2036/slack-user-message.el b/elpa/slack-20160928.2036/slack-user-message.el deleted file mode 100644 index f6f9c6a..0000000 --- a/elpa/slack-20160928.2036/slack-user-message.el +++ /dev/null @@ -1,36 +0,0 @@ -;;; package --- Summary -;;; Commentary: - -;;; Code: - -(require 'eieio) -(require 'slack-message-formatter) -(require 'slack-message-reaction) -(require 'slack-message-editor) - -(defvar slack-user-message-keymap - (let ((keymap (make-sparse-keymap))) - keymap)) - -(defmethod slack-message-sender-equalp ((m slack-user-message) sender-id) - (string= (oref m user) sender-id)) - -(defmethod slack-message-header ((m slack-user-message) team) - (with-slots (ts edited-at deleted-at) m - (let* ((name (slack-message-sender-name m team)) - (time (slack-message-time-to-string ts)) - (edited-at (slack-message-time-to-string edited-at)) - (deleted-at (slack-message-time-to-string deleted-at)) - (header (format "%s" name))) - (if deleted-at - (format "%s deleted_at: %s" header deleted-at) - (if edited-at - (format "%s edited_at: %s" header edited-at) - header))))) - -(defmethod slack-message-propertize ((m slack-user-message) text) - (put-text-property 0 (length text) 'keymap slack-user-message-keymap text) - text) - -(provide 'slack-user-message) -;;; slack-user-message.el ends here diff --git a/elpa/slack-20160928.2036/slack-user.el b/elpa/slack-20160928.2036/slack-user.el deleted file mode 100644 index 4a6086c..0000000 --- a/elpa/slack-20160928.2036/slack-user.el +++ /dev/null @@ -1,63 +0,0 @@ -;;; slack-user.el ---slack user interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'slack-request) -(require 'slack-room) - -(defun slack-user-find (id team) - (with-slots (users) team - (cl-find-if (lambda (user) - (string= id (plist-get user :id))) - users))) - -(defun slack-user-find-by-name (name team) - (with-slots (users) team - (cl-find-if (lambda (user) - (string= name (plist-get user :name))) - users))) - -(defun slack-user-get-id (name team) - (let ((user (slack-user-find-by-name name team))) - (if user - (plist-get user :id)))) - -(defun slack-user-name (id team) - (let ((user (slack-user-find id team))) - (if user - (plist-get user :name)))) - -(defun slack-user-names (team) - (with-slots (users) team - (mapcar (lambda (u) (cons (plist-get u :name) u)) - users))) - -(defun slack-user-presence-to-string (user) - (if (string= (plist-get user :presence) "active") - "* " - " ")) - -(provide 'slack-user) -;;; slack-user.el ends here diff --git a/elpa/slack-20160928.2036/slack-util.el b/elpa/slack-20160928.2036/slack-util.el deleted file mode 100644 index 16f7b4c..0000000 --- a/elpa/slack-20160928.2036/slack-util.el +++ /dev/null @@ -1,88 +0,0 @@ -;;; slack-util.el ---utility functions -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: - -(require 'eieio) - -(defun slack-seq-to-list (seq) - (if (listp seq) seq (append seq nil))) - -(defun slack-decode (seq) - (cl-loop for e in (slack-seq-to-list seq) - collect (if (stringp e) - (decode-coding-string e 'utf-8) - e))) - -(defun slack-class-have-slot-p (class slot) - (and (symbolp slot) - (let* ((stripped (substring (symbol-name slot) 1)) - (replaced (replace-regexp-in-string "_" "-" - stripped)) - (symbolized (intern replaced))) - (slot-exists-p class symbolized)))) - -(defun slack-collect-slots (class seq) - (let ((plist (slack-seq-to-list seq))) - (cl-loop for p in plist - if (and (slack-class-have-slot-p class p) - (plist-member plist p)) - nconc (let ((value (plist-get plist p))) - (list p (if (stringp value) - (decode-coding-string value 'utf-8) - (if (eq :json-false value) - nil - value))))))) - -(defun company-slack-backend (command &optional arg &rest ignored) - "Completion backend for slack chats. It currently understands -@USER; adding #CHANNEL should be a simple matter of programming." - (interactive (list 'interactive)) - (cl-labels - ((prefix-type (str) (cond - ((string-prefix-p "@" str) 'user) - ((string-prefix-p "#" str) 'channel))) - (content (str) (substring str 1 nil))) - (cl-case command - (interactive (company-begin-backend 'company-slack-backend)) - (prefix (when (cl-find major-mode '(slack-mode - slack-edit-message-mode)) - (company-grab-line "\\(\\W\\|^\\)\\(@\\w*\\|#\\w*\\)" - 2))) - (candidates (let ((content (content arg))) - (cl-case (prefix-type arg) - (user - (cl-loop for user in (oref slack-current-team users) - if (string-prefix-p content - (plist-get user :name)) - collect (concat "@" (plist-get user :name)))) - (channel - (cl-loop for team in (oref slack-current-team channels) - if (string-prefix-p content - (oref team name)) - collect (concat "#" (oref team name))))))) - ))) - -(provide 'slack-util) -;;; slack-util.el ends here diff --git a/elpa/slack-20160928.2036/slack-websocket.el b/elpa/slack-20160928.2036/slack-websocket.el deleted file mode 100644 index bbba57e..0000000 --- a/elpa/slack-20160928.2036/slack-websocket.el +++ /dev/null @@ -1,510 +0,0 @@ -;;; slack-websocket.el --- slack websocket interface -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 南優也 - -;; Author: 南優也 -;; Keywords: - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'websocket) -(require 'slack-request) -(require 'slack-message) -(require 'slack-reply) - -(defclass slack-typing () - ((room :initarg :room :initform nil) - (limit :initarg :limit :initform nil) - (users :initarg :users :initform nil))) - -(defclass slack-typing-user () - ((limit :initarg :limit :initform nil) - (user-name :initarg :user-name :initform nil))) - -(defun slack-ws-open (team) - (with-slots (ws-url ws-conn reconnect-count) team - (unless ws-conn - (setq ws-conn - (websocket-open - ws-url - :on-message - #'(lambda (websocket frame) - (slack-ws-on-message websocket frame team)))) - (setq reconnect-count 0)))) - -(defun slack-ws-close (&optional team) - (interactive) - (unless team - (setq team slack-teams)) - (cl-labels - ((close (team) - (let ((team-name (oref team name))) - (with-slots (connected ws-conn last-pong) team - (if ws-conn - (progn - (websocket-close ws-conn) - (setq ws-conn nil) - (setq connected nil) - (slack-ws-cancel-ping-timer team) - (slack-ws-cancel-ping-check-timers team) - (message "Slack Websocket Closed - %s" team-name)) - (message "Slack Websocket is not open - %s" team-name)))))) - (if (listp team) - (mapc #'close team) - (close team)))) - - -(defun slack-ws-send (payload team) - (with-slots (waiting-send ws-conn) team - (push payload waiting-send) - (condition-case _e - (progn - (websocket-send-text ws-conn payload) - (setq waiting-send - (cl-remove-if #'(lambda (p) (string= payload p)) - waiting-send))) - (websocket-closed (slack-ws-reconnect team)) - (websocket-illegal-frame (message "Sent illegal frame.") - (slack-ws-close team)) - (error (slack-ws-reconnect team))))) - -(defun slack-ws-resend (team) - (with-slots (waiting-send) team - (let ((candidate waiting-send)) - (setq waiting-send nil) - (cl-loop for msg in candidate - do (sleep-for 1) (slack-ws-send msg team))))) - - -(defun slack-ws-on-message (_websocket frame team) - ;; (message "%s" (slack-request-parse-payload - ;; (websocket-frame-payload frame))) - (when (websocket-frame-completep frame) - (let* ((payload (slack-request-parse-payload - (websocket-frame-payload frame))) - (decoded-payload (slack-decode payload)) - (type (plist-get decoded-payload :type))) - ;; (message "%s" decoded-payload) - (condition-case err - (cond - ((string= type "pong") - (slack-ws-handle-pong decoded-payload team)) - ((string= type "hello") - (slack-ws-cancel-reconnect-timer team) - (slack-cancel-notify-adandon-reconnect) - (slack-ws-set-ping-timer team) - (slack-ws-resend team) - (message "Slack Websocket Is Ready! - %s" - (oref team name))) - ((plist-get decoded-payload :reply_to) - (slack-ws-handle-reply decoded-payload team)) - ((string= type "message") - (slack-ws-handle-message decoded-payload team)) - ((string= type "reaction_added") - (slack-ws-handle-reaction-added decoded-payload team)) - ((string= type "reaction_removed") - (slack-ws-handle-reaction-removed decoded-payload team)) - ((string= type "channel_created") - (slack-ws-handle-channel-created decoded-payload team)) - ((or (string= type "channel_archive") - (string= type "group_archive")) - (slack-ws-handle-room-archive decoded-payload team)) - ((or (string= type "channel_unarchive") - (string= type "group_unarchive")) - (slack-ws-handle-room-unarchive decoded-payload team)) - ((string= type "channel_deleted") - (slack-ws-handle-channel-deleted decoded-payload team)) - ((or (string= type "channel_rename") - (string= type "group_rename")) - (slack-ws-handle-room-rename decoded-payload team)) - ((or (string= type "channel_joined") - (string= type "group_joined")) - (slack-ws-handle-room-joined decoded-payload team)) - ((string= type "presence_change") - (slack-ws-handle-presence-change decoded-payload team)) - ((or (string= type "bot_added") - (string= type "bot_changed")) - (slack-ws-handle-bot decoded-payload team)) - ((or (string= type "file_deleted") - (string= type "file_unshared")) - (slack-ws-handle-file-deleted decoded-payload team)) - ((or (string= type "im_marked") - (string= type "channel_marked") - (string= type "group_marked")) - (slack-ws-handle-room-marked decoded-payload team)) - ((string= type "im_open") - (slack-ws-handle-im-open decoded-payload team)) - ((string= type "im_close") - (slack-ws-handle-im-close decoded-payload team)) - ((string= type "team_join") - (slack-ws-handle-team-join decoded-payload team)) - ((string= type "user_typing") - (slack-ws-handle-user-typing decoded-payload team))) - (error (progn - (warn "%s payload: %s" err decoded-payload) - (signal (car err) (cdr err)))))))) - -(defun slack-user-typing (team) - (with-slots (typing typing-timer) team - (with-slots (limit users room) typing - (let ((current (float-time))) - (if (and typing-timer (timerp typing-timer) - (< limit current)) - (progn - (cancel-timer typing-timer) - (setq typing-timer nil) - (setq typing nil)) - (if (slack-buffer-show-typing-p - (get-buffer (slack-room-buffer-name room))) - (let ((team-name (slack-team-name team)) - (room-name (slack-room-name room)) - (visible-users (cl-remove-if - #'(lambda (u) (< (oref u limit) current)) - users))) - (message "Slack [%s - %s] %s is typing..." - team-name room-name - (mapconcat #'(lambda (u) (oref u user-name)) - visible-users - ", "))))))))) - -(defun slack-ws-handle-user-typing (payload team) - (let* ((user (slack-user-name (plist-get payload :user) team)) - (room (slack-room-find (plist-get payload :channel) team))) - (if (slack-buffer-show-typing-p - (get-buffer (slack-room-buffer-name room))) - (let ((limit (+ 3 (float-time)))) - (with-slots (typing typing-timer) team - (if (and typing (equal room (oref typing room))) - (with-slots ((typing-limit limit) - (typing-room room) users) typing - (setq typing-limit limit) - (let ((typing-user (make-instance 'slack-typing-user - :limit limit - :user-name user))) - (setq users - (cons typing-user - (cl-remove-if #'(lambda (u) - (string= (oref u user-name) - user)) - users)))))) - (unless typing - (let ((new-typing (make-instance 'slack-typing - :room room :limit limit)) - (typing-user (make-instance 'slack-typing-user - :limit limit :user-name user))) - (oset new-typing users (list typing-user)) - (setq typing new-typing)) - (setq typing-timer - (run-with-timer t 1 #'slack-user-typing team)))))))) - -(defun slack-ws-handle-team-join (payload team) - (let ((user (slack-decode (plist-get payload :user)))) - (with-slots (users) team - (setq users - (cons user - (cl-remove-if #'(lambda (u) - (string= (plist-get u :id) - (plist-get user :id))) - users)))) - (message "User %s Joind Team: %s" - (plist-get (slack-user-find (plist-get user :id) - team) - :name) - (slack-team-name team)))) - -(defun slack-ws-handle-im-open (payload team) - (cl-labels - ((notify - (im) - (slack-room-history - im team nil - #'(lambda () - (message "Direct Message Channel with %s is Open" - (slack-user-name (oref im user) team))) - t))) - (let ((exist (slack-room-find (plist-get payload :channel) team))) - (if exist - (progn - (oset exist is-open t) - (notify exist)) - (with-slots (ims) team - (let ((im (slack-room-create - (list :id (plist-get payload :channel) - :user (plist-get payload :user)) - team 'slack-im))) - (setq ims (cons im ims)) - (notify im))))))) - -(defun slack-ws-handle-im-close (payload team) - (let ((im (slack-room-find (plist-get payload :channel) team))) - (oset im is-open nil) - (message "Direct Message Channel with %s is Closed" - (slack-user-name (oref im user) team)))) - -(defun slack-ws-handle-message (payload team) - (let ((subtype (plist-get payload :subtype))) - (cond - ((and subtype (string= subtype "file_share")) - (slack-ws-handle-file-share payload team) - (slack-ws-update-message payload team)) - ((and subtype (string= subtype "message_changed")) - (slack-message-edited payload team)) - ((and subtype (string= subtype "message_deleted")) - (slack-message-deleted payload team)) - (t - (slack-ws-update-message payload team))))) - -(defun slack-ws-update-message (payload team) - (let ((m (slack-message-create payload))) - (when m - (slack-message-update m team)))) - -(defun slack-ws-handle-reply (payload team) - (let ((ok (plist-get payload :ok))) - (if (eq ok :json-false) - (let ((err (plist-get payload :error))) - (message "Error code: %s msg: %s" - (plist-get err :code) - (plist-get err :msg))) - (let ((message-id (plist-get payload :reply_to))) - (if (integerp message-id) - (slack-message-handle-reply - (slack-message-create payload) - team)))))) - -(cl-defmacro slack-ws-handle-reaction ((payload team) &body body) - `(let* ((item (plist-get ,payload :item)) - (room (slack-room-find (plist-get item :channel) - ,team))) - (if room - (let ((msg (slack-room-find-message room (plist-get item :ts)))) - (if msg - (let* ((r-name (plist-get ,payload :reaction)) - (r-count 1) - (r-users (list (plist-get ,payload :user))) - (reaction (make-instance 'slack-reaction - :name r-name - :count r-count - :users r-users))) - - ,@body - (slack-message-update msg ,team t t))))))) - -(defun slack-ws-handle-reaction-added (payload team) - (slack-ws-handle-reaction - (payload team) - (slack-message-append-reaction msg reaction) - (slack-reaction-notify payload team))) - -(defun slack-ws-handle-reaction-removed (payload team) - (slack-ws-handle-reaction - (payload team) - (slack-message-pop-reaction msg reaction))) - -(defun slack-ws-handle-channel-created (payload team) - ;; (let ((id (plist-get (plist-get payload :channel) :id))) - ;; (slack-channel-create-from-info id team)) - ) - -(defun slack-ws-handle-room-archive (payload team) - (let* ((id (plist-get payload :channel)) - (room (slack-room-find id team))) - (oset room is-archived t) - (message "Channel: %s is archived" - (slack-room-name-with-team-name room)))) - -(defun slack-ws-handle-room-unarchive (payload team) - (let* ((id (plist-get payload :channel)) - (room (slack-room-find id team))) - (oset room is-archived nil) - (message "Channel: %s is unarchived" - (slack-room-name-with-team-name room)))) - -(defun slack-ws-handle-channel-deleted (payload team) - (let ((id (plist-get payload :channel))) - (slack-room-deleted id team))) - -(defun slack-ws-handle-room-rename (payload team) - (let* ((c (plist-get payload :channel)) - (room (slack-room-find (plist-get c :id) team)) - (old-name (slack-room-name room)) - (new-name (plist-get c :name))) - (oset room name new-name) - (message "Renamed channel from %s to %s" - old-name - new-name))) - -(defun slack-ws-handle-room-joined (payload team) - (cl-labels - ((replace-room (room rooms) - (cons room (cl-delete-if - #'(lambda (r) - (slack-room-equal-p room r)) - rooms)))) - (let* ((c (plist-get payload :channel))) - (if (plist-get c :is_channel) - (let ((channel (slack-room-create c team 'slack-channel))) - (with-slots (channels) team - (setq channels - (replace-room channel channels))) - (message "Joined channel %s" - (slack-room-name-with-team-name channel))) - (let ((group (slack-room-create c team 'slack-group))) - (with-slots (groups) team - (setq groups - (replace-room group groups))) - (message "Joined group %s" - (slack-room-name-with-team-name group))))))) - -(defun slack-ws-handle-presence-change (payload team) - (let* ((id (plist-get payload :user)) - (user (slack-user-find id team)) - (presence (plist-get payload :presence))) - (plist-put user :presence presence))) - -(defun slack-ws-handle-bot (payload team) - (let ((bot (plist-get payload :bot))) - (with-slots (bots) team - (push bot bots)))) - -(defun slack-ws-handle-file-share (payload team) - (let ((file (slack-file-create (plist-get payload :file)))) - (slack-file-pushnew file team))) - -(defun slack-ws-handle-file-deleted (payload team) - (let ((file-id (plist-get payload :file_id)) - (room (slack-file-room-obj team))) - (with-slots (messages last-read) room - (setq messages (cl-remove-if #'(lambda (f) - (string= file-id (oref f id))) - messages))))) -(defun slack-log-time () - (format-time-string "%Y-%m-%d %H:%M:%S")) - -(defun slack-ws-set-ping-timer (team) - (with-slots (ping-timer) team - (unless ping-timer - (setq ping-timer - (run-at-time t 10 #'(lambda () (slack-ws-ping team))))))) - -(defun slack-ws-current-time-str () - (number-to-string (time-to-seconds (current-time)))) - -(defun slack-ws-ping (team) - (slack-message-inc-id team) - (with-slots (message-id) team - (let* ((time (slack-ws-current-time-str)) - (m (list :id message-id - :type "ping" - :time time)) - (json (json-encode m))) - (slack-ws-set-check-ping-timer team time) - (slack-ws-send json team)))) - -(defun slack-ws-set-check-ping-timer (team time) - (with-slots (ping-check-timers check-ping-timeout-sec) team - (let ((team-id (oref team id))) - (puthash time (run-at-time check-ping-timeout-sec nil - #'(lambda () (slack-ws-ping-timeout team-id))) - ping-check-timers)))) - -(defun slack-ws-ping-timeout (team-id) - (message "Slack Websocket PING Timeout.") - (let ((team (slack-team-find team-id))) - (slack-ws-cancel-ping-check-timers team) - (slack-ws-close team) - (slack-ws-cancel-ping-timer team) - (if (oref team reconnect-auto) - (with-slots (reconnect-timer reconnect-after-sec) team - (setq reconnect-timer - (run-at-time t reconnect-after-sec - #'(lambda () (slack-ws-reconnect team)))))))) - -(defun slack-ws-init-ping-check-timers () - (make-hash-table :test 'equal)) - -(defun slack-ws-cancel-ping-check-timers (team) - (with-slots (ping-check-timers) team - (maphash #'(lambda (key value) - (if (timerp value) - (cancel-timer value))) - ping-check-timers) - (setq ping-check-timers (slack-ws-init-ping-check-timers)))) - -(defun slack-ws-cancel-ping-timer (team) - (with-slots (ping-timer) team - (if (timerp ping-timer) - (cancel-timer ping-timer)) - (setq ping-timer nil))) - -(defvar slack-disconnected-timer nil) -(defun slack-notify-abandon-reconnect () - (unless slack-disconnected-timer - (setq slack-disconnected-timer - (run-with-idle-timer 5 t - #'(lambda () - (message "Reconnect Count Exceeded. Manually invoke `slack-start'.")))))) - -(defun slack-cancel-notify-adandon-reconnect () - (if (and slack-disconnected-timer - (timerp slack-disconnected-timer)) - (progn - (cancel-timer slack-disconnected-timer) - (setq slack-disconnected-timer nil)))) - -(defun slack-ws-reconnect (team &optional force) - (message "Slack Websocket Try To Reconnect") - (with-slots - (reconnect-count (reconnect-max reconnect-count-max)) team - (if (and (not force) reconnect-max (< reconnect-max reconnect-count)) - (progn - (slack-notify-abandon-reconnect) - (slack-ws-cancel-reconnect-timer team)) - (incf reconnect-count) - (slack-ws-close team) - (slack-authorize - team - (cl-function - (lambda - (&key error-thrown &allow-other-keys) - (message "Slack Reconnect Failed: %s" (cdr error-thrown)))))))) - -(defun slack-ws-cancel-reconnect-timer (team) - (with-slots (reconnect-timer) team - (if (timerp reconnect-timer) - (cancel-timer reconnect-timer)) - (setq reconnect-timer nil))) - -(defun slack-ws-handle-pong (payload team) - (let ((key (plist-get payload :time))) - (with-slots (ping-check-timers) team - (let ((timer (gethash key ping-check-timers))) - (when timer - (cancel-timer timer) - (remhash key ping-check-timers)))))) - -(defun slack-ws-handle-room-marked (payload team) - (let ((room (slack-room-find (plist-get payload :channel) - team)) - (new-unread-count-display (plist-get payload :unread_count_display))) - (with-slots (unread-count-display) room - (setq unread-count-display new-unread-count-display)))) - -(provide 'slack-websocket) -;;; slack-websocket.el ends here diff --git a/elpa/slack-20160928.2036/slack.el b/elpa/slack-20160928.2036/slack.el deleted file mode 100644 index f839e2a..0000000 --- a/elpa/slack-20160928.2036/slack.el +++ /dev/null @@ -1,191 +0,0 @@ -;;; slack.el --- slack client for emacs -*- lexical-binding: t; -*- - -;; Copyright (C) 2015 yuya.minami - -;; Author: yuya.minami -;; Keywords: tools -;; Version: 0.0.2 -;; Package-Requires: ((websocket "1.5") (request "0.2.0") (oauth2 "0.10") (circe "2.3") (alert "1.2") (emojify "0.4") (emacs "24.3")) -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; - -;;; Code: -(require 'cl-lib) -(require 'oauth2) - -(require 'slack-team) -(require 'slack-channel) -(require 'slack-im) -(require 'slack-file) -(require 'slack-message-notification) -(require 'slack-message-sender) -(require 'slack-message-editor) -(require 'slack-message-reaction) -(require 'slack-user-message) -(require 'slack-bot-message) -(require 'slack-search) -(require 'slack-reminder) - -(require 'slack-websocket) -(require 'slack-request) - -(defgroup slack nil - "Emacs Slack Client" - :prefix "slack-" - :group 'tools) - -(defcustom slack-redirect-url "http://localhost:8080" - "Redirect url registered for Slack.") -(defcustom slack-buffer-function #'switch-to-buffer-other-window - "Function to print buffer.") - -(defvar slack-use-register-team-string - "use `slack-register-team' instead.") - -(defcustom slack-client-id nil - "Client ID provided by Slack.") -(make-obsolete-variable - 'slack-client-id slack-use-register-team-string - "0.0.2") -(defcustom slack-client-secret nil - "Client Secret Provided by Slack.") -(make-obsolete-variable - 'slack-client-secret slack-use-register-team-string - "0.0.2") -(defcustom slack-token nil - "Slack token provided by Slack. -set this to save request to Slack if already have.") -(make-obsolete-variable - 'slack-token slack-use-register-team-string - "0.0.2") -(defcustom slack-room-subscription '() - "Group or Channel list to subscribe notification." - :group 'slack) -(make-obsolete-variable - 'slack-room-subscription slack-use-register-team-string - "0.0.2") -(defcustom slack-typing-visibility 'frame - "When to show typing indicator. -frame means typing slack buffer is in the current frame, show typing indicator. -buffer means typing slack buffer is the current buffer, show typing indicator. -never means never show typing indicator." - :type '(choice (const frame) - (const buffer) - (const never))) - -(defconst slack-oauth2-authorize "https://slack.com/oauth/authorize") -(defconst slack-oauth2-access "https://slack.com/api/oauth.access") -(defconst slack-authorize-url "https://slack.com/api/rtm.start") - -(defvar slack-authorize-requests nil) -(defun slack-authorize (team &optional error-callback) - (cl-labels - ((abort-previous () (cl-loop for r in (reverse slack-authorize-requests) - do (request-abort r)))) - (setq slack-authorize-requests nil) - (let ((request (slack-request - slack-authorize-url - team - :success (cl-function (lambda (&key data &allow-other-keys) - (slack-on-authorize data team))) - :sync nil - :error error-callback))) - (push request slack-authorize-requests)))) - -(defun slack-update-team (data team) - (cl-labels - ((create-rooms - (datum team class) - (mapcar #'(lambda (data) - (slack-room-create data team class)) - (append datum nil)))) - (let ((self (plist-get data :self)) - (team-data (plist-get data :team))) - (oset team id (plist-get team-data :id)) - (oset team name (plist-get team-data :name)) - (oset team channels - (create-rooms (plist-get data :channels) - team 'slack-channel)) - (oset team groups - (create-rooms (plist-get data :groups) - team 'slack-group)) - (oset team ims - (create-rooms (plist-get data :ims) - team 'slack-im)) - (oset team self self) - (oset team self-id (plist-get self :id)) - (oset team self-name (plist-get self :name)) - (oset team users (append (plist-get data :users) nil)) - (oset team bots (append (plist-get data :bots) nil)) - (oset team ws-url (plist-get data :url)) - (oset team connected t) - team))) - -(cl-defun slack-on-authorize (data team) - (slack-request-handle-error - (data "slack-authorize") - (message "Slack Authorization Finished - %s" - (oref team name)) - (let ((team (slack-update-team data team))) - (with-slots (groups ims channels) team - (cl-loop for room in (append groups ims channels) - do (let ((bufname (slack-room-buffer-name room))) - (when (get-buffer bufname) - (kill-buffer bufname))))) - (slack-ws-open team)))) - -(defun slack-on-authorize-e - (&key error-thrown &allow-other-keys &rest_) - (error "slack-authorize: %s" error-thrown)) - -(defun slack-oauth2-auth (team) - (with-slots (client-id client-secret) team - (oauth2-auth - slack-oauth2-authorize - slack-oauth2-access - client-id - client-secret - "client" - nil - slack-redirect-url))) - -(defun slack-request-token (team) - (with-slots (token) team - (setq token - (oauth2-token-access-token - (slack-oauth2-auth team))))) - -;;;###autoload -(defun slack-start (&optional team) - (interactive) - (cl-labels ((start - (team) - (with-slots (ws-conn token) team - (if ws-conn - (slack-ws-close team)) - (unless token - (slack-request-token team))) - (slack-authorize team))) - (if team - (start team) - (if slack-teams - (cl-loop for team in slack-teams - do (start team)) - (slack-start (call-interactively #'slack-register-team)))))) - -(provide 'slack) -;;; slack.el ends here diff --git a/elpa/websocket-20160720.2051/websocket-autoloads.el b/elpa/websocket-20160720.2051/websocket-autoloads.el deleted file mode 100644 index 3515df0..0000000 --- a/elpa/websocket-20160720.2051/websocket-autoloads.el +++ /dev/null @@ -1,15 +0,0 @@ -;;; websocket-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (directory-file-name (or (file-name-directory #$) (car load-path)))) - -;;;### (autoloads nil nil ("websocket.el") (22533 17547 135674 20000)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; websocket-autoloads.el ends here diff --git a/elpa/websocket-20160720.2051/websocket-pkg.el b/elpa/websocket-20160720.2051/websocket-pkg.el deleted file mode 100644 index a9b89ac..0000000 --- a/elpa/websocket-20160720.2051/websocket-pkg.el +++ /dev/null @@ -1,2 +0,0 @@ -;;; -*- no-byte-compile: t -*- -(define-package "websocket" "20160720.2051" "Emacs WebSocket client and server" 'nil :keywords '("communication" "websocket" "server")) diff --git a/elpa/websocket-20160720.2051/websocket.el b/elpa/websocket-20160720.2051/websocket.el deleted file mode 100644 index 95fd41f..0000000 --- a/elpa/websocket-20160720.2051/websocket.el +++ /dev/null @@ -1,1035 +0,0 @@ -;;; websocket.el --- Emacs WebSocket client and server - -;; Copyright (c) 2013, 2016 Free Software Foundation, Inc. - -;; Author: Andrew Hyatt -;; Keywords: Communication, Websocket, Server -;; Package-Version: 20160720.2051 -;; Version: 1.6 -;; -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 3 of the -;; License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be useful, but -;; WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -;; General Public License for more details. -;; -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: -;; This implements RFC 6455, which can be found at -;; http://tools.ietf.org/html/rfc6455. -;; -;; This library contains code to connect Emacs as a client to a -;; websocket server, and for Emacs to act as a server for websocket -;; connections. -;; -;; Websockets clients are created by calling `websocket-open', which -;; returns a `websocket' struct. Users of this library use the -;; websocket struct, and can call methods `websocket-send-text', which -;; sends text over the websocket, or `websocket-send', which sends a -;; `websocket-frame' struct, enabling finer control of what is sent. -;; A callback is passed to `websocket-open' that will retrieve -;; websocket frames called from the websocket. Websockets are -;; eventually closed with `websocket-close'. -;; -;; Server functionality is similar. A server is started with -;; `websocket-server' called with a port and the callbacks to use, -;; which returns a process. The process can later be closed with -;; `websocket-server-close'. A `websocket' struct is also created -;; for every connection, and is exposed through the callbacks. - -(require 'bindat) -(require 'url-parse) -(require 'url-cookie) -(eval-when-compile (require 'cl)) - -;;; Code: - -(defstruct (websocket - (:constructor nil) - (:constructor websocket-inner-create)) - "A websocket structure. -This follows the W3C Websocket API, except translated to elisp -idioms. The API is implemented in both the websocket struct and -additional methods. Due to how defstruct slots are accessed, all -API methods are prefixed with \"websocket-\" and take a websocket -as an argument, so the distrinction between the struct API and -the additional helper APIs are not visible to the caller. - -A websocket struct is created with `websocket-open'. - -`ready-state' contains one of `connecting', `open', or -`closed', depending on the state of the websocket. - -The W3C API \"bufferedAmount\" call is not currently implemented, -since there is no elisp API to get the buffered amount from the -subprocess. There may, in fact, be output data buffered, -however, when the `on-message' or `on-close' callbacks are -called. - -`on-open', `on-message', `on-close', and `on-error' are described -in `websocket-open'. - -The `negotiated-extensions' slot lists the extensions accepted by -both the client and server, and `negotiated-protocols' does the -same for the protocols. -" - ;; API - (ready-state 'connecting) - client-data - on-open - on-message - on-close - on-error - negotiated-protocols - negotiated-extensions - (server-p nil :read-only t) - - ;; Other data - clients should not have to access this. - (url (assert nil) :read-only t) - (protocols nil :read-only t) - (extensions nil :read-only t) - (conn (assert nil) :read-only t) - ;; Only populated for servers, this is the server connection. - server-conn - accept-string - (inflight-input nil)) - -(defvar websocket-version "1.5" - "Version numbers of this version of websocket.el.") - -(defvar websocket-debug nil - "Set to true to output debugging info to a per-websocket buffer. -The buffer is ` *websocket URL debug*' where URL is the -URL of the connection.") - -(defconst websocket-guid "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - "The websocket GUID as defined in RFC 6455. -Do not change unless the RFC changes.") - -(defvar websocket-callback-debug-on-error nil - "If true, when an error happens in a client callback, invoke the debugger. -Having this on can cause issues with missing frames if the debugger is -exited by quitting instead of continuing, so it's best to have this set -to nil unless it is especially needed.") - -(defmacro websocket-document-function (function docstring) - "Document FUNCTION with DOCSTRING. Use this for defstruct accessor etc." - (declare (indent defun) - (doc-string 2)) - `(put ',function 'function-documentation ,docstring)) - -(websocket-document-function websocket-on-open - "Accessor for websocket on-open callback. -See `websocket-open' for details. - -\(fn WEBSOCKET)") - -(websocket-document-function websocket-on-message - "Accessor for websocket on-message callback. -See `websocket-open' for details. - -\(fn WEBSOCKET)") - -(websocket-document-function websocket-on-close - "Accessor for websocket on-close callback. -See `websocket-open' for details. - -\(fn WEBSOCKET)") - -(websocket-document-function websocket-on-error - "Accessor for websocket on-error callback. -See `websocket-open' for details. - -\(fn WEBSOCKET)") - -(defun websocket-genbytes (nbytes) - "Generate NBYTES random bytes." - (let ((s (make-string nbytes ?\s))) - (dotimes (i nbytes) - (aset s i (random 256))) - s)) - -(defun websocket-try-callback (websocket-callback callback-type websocket - &rest rest) - "Invoke function WEBSOCKET-CALLBACK with WEBSOCKET and REST args. -If an error happens, it is handled according to -`websocket-callback-debug-on-error'." - ;; This looks like it should be able to done more efficiently, but - ;; I'm not sure that's the case. We can't do it as a macro, since - ;; we want it to change whenever websocket-callback-debug-on-error - ;; changes. - (let ((args rest) - (debug-on-error websocket-callback-debug-on-error)) - (push websocket args) - (if websocket-callback-debug-on-error - (condition-case err - (apply (funcall websocket-callback websocket) args) - ((debug error) (funcall (websocket-on-error websocket) - websocket callback-type err))) - (condition-case err - (apply (funcall websocket-callback websocket) args) - (error (funcall (websocket-on-error websocket) websocket - callback-type err)))))) - -(defun websocket-genkey () - "Generate a key suitable for the websocket handshake." - (base64-encode-string (websocket-genbytes 16))) - -(defun websocket-calculate-accept (key) - "Calculate the expect value of the accept header. -This is based on the KEY from the Sec-WebSocket-Key header." - (base64-encode-string - (sha1 (concat key websocket-guid) nil nil t))) - -(defun websocket-get-bytes (s n) - "From string S, retrieve the value of N bytes. -Return the value as an unsigned integer. The value N must be a -power of 2, up to 8. - -We support getting frames up to 536870911 bytes (2^29 - 1), -approximately 537M long." - (if (= n 8) - (let* ((32-bit-parts - (bindat-get-field (bindat-unpack '((:val vec 2 u32)) s) :val)) - (cval - (logior (lsh (aref 32-bit-parts 0) 32) (aref 32-bit-parts 1)))) - (if (and (= (aref 32-bit-parts 0) 0) - (= (lsh (aref 32-bit-parts 1) -29) 0)) - cval - (signal 'websocket-unparseable-frame - "Frame value found too large to parse!"))) - ;; n is not 8 - (bindat-get-field - (condition-case _ - (bindat-unpack - `((:val - ,(cond ((= n 1) 'u8) - ((= n 2) 'u16) - ((= n 4) 'u32) - ;; This is an error with the library, - ;; not a user-facing, meaningful error. - (t (error - "websocket-get-bytes: Unknown N: %s" n))))) - s) - (args-out-of-range (signal 'websocket-unparseable-frame - (format "Frame unexpectedly shortly: %s" s)))) - :val))) - -(defun websocket-to-bytes (val nbytes) - "Encode the integer VAL in NBYTES of data. -NBYTES much be a power of 2, up to 8. - -This supports encoding values up to 536870911 bytes (2^29 - 1), -approximately 537M long." - (when (and (< nbytes 8) - (> val (expt 2 (* 8 nbytes)))) - ;; not a user-facing error, this must be caused from an error in - ;; this library - (error "websocket-to-bytes: Value %d could not be expressed in %d bytes" - val nbytes)) - (if (= nbytes 8) - (progn - (let ((hi-32bits (lsh val -32)) - ;; Test for systems that don't have > 32 bits, and - ;; for those systems just return the value. - (low-32bits (if (= 0 (expt 2 32)) - val - (logand #xffffffff val)))) - (when (or (> hi-32bits 0) (> (lsh low-32bits -29) 0)) - (signal 'websocket-frame-too-large val)) - (bindat-pack `((:val vec 2 u32)) - `((:val . [,hi-32bits ,low-32bits]))))) - (bindat-pack - `((:val ,(cond ((= nbytes 1) 'u8) - ((= nbytes 2) 'u16) - ((= nbytes 4) 'u32) - ;; Library error, not system error - (t (error "websocket-to-bytes: Unknown NBYTES: %s" nbytes))))) - `((:val . ,val))))) - -(defun websocket-get-opcode (s) - "Retrieve the opcode from first byte of string S." - (websocket-ensure-length s 1) - (let ((opcode (logand #xf (websocket-get-bytes s 1)))) - (cond ((= opcode 0) 'continuation) - ((= opcode 1) 'text) - ((= opcode 2) 'binary) - ((= opcode 8) 'close) - ((= opcode 9) 'ping) - ((= opcode 10) 'pong)))) - -(defun websocket-get-payload-len (s) - "Parse out the payload length from the string S. -We start at position 0, and return a cons of the payload length and how -many bytes were consumed from the string." - (websocket-ensure-length s 1) - (let* ((initial-val (logand 127 (websocket-get-bytes s 1)))) - (cond ((= initial-val 127) - (websocket-ensure-length s 9) - (cons (websocket-get-bytes (substring s 1) 8) 9)) - ((= initial-val 126) - (websocket-ensure-length s 3) - (cons (websocket-get-bytes (substring s 1) 2) 3)) - (t (cons initial-val 1))))) - -(defstruct websocket-frame opcode payload length completep) - -(defun websocket-mask (key data) - "Using string KEY, mask string DATA according to the RFC. -This is used to both mask and unmask data." - (apply - 'string - (loop for b across data - for i from 0 to (length data) - collect (logxor (websocket-get-bytes (substring key (mod i 4)) 1) b)))) - -(defun websocket-ensure-length (s n) - "Ensure the string S has at most N bytes. -Otherwise we throw the error `websocket-incomplete-frame'." - (when (< (length s) n) - (throw 'websocket-incomplete-frame nil))) - -(defun websocket-encode-frame (frame should-mask) - "Encode the FRAME struct to the binary representation. -We mask the frame or not, depending on SHOULD-MASK." - (let* ((opcode (websocket-frame-opcode frame)) - (payload (websocket-frame-payload frame)) - (fin (websocket-frame-completep frame)) - (payloadp (and payload - (memq opcode '(continuation ping pong text binary)))) - (mask-key (when should-mask (websocket-genbytes 4)))) - (apply 'unibyte-string - (let ((val (append (list - (logior (cond ((eq opcode 'continuation) 0) - ((eq opcode 'text) 1) - ((eq opcode 'binary) 2) - ((eq opcode 'close) 8) - ((eq opcode 'ping) 9) - ((eq opcode 'pong) 10)) - (if fin 128 0))) - (when payloadp - (list - (logior - (if should-mask 128 0) - (cond ((< (length payload) 126) (length payload)) - ((< (length payload) 65536) 126) - (t 127))))) - (when (and payloadp (>= (length payload) 126)) - (append (websocket-to-bytes - (length payload) - (cond ((< (length payload) 126) 1) - ((< (length payload) 65536) 2) - (t 8))) nil)) - (when (and payloadp should-mask) - (append mask-key nil)) - (when payloadp - (append (if should-mask (websocket-mask mask-key payload) - payload) - nil))))) - ;; We have to make sure the non-payload data is a full 32-bit frame - (if (= 1 (length val)) - (append val '(0)) val))))) - -(defun websocket-read-frame (s) - "Read from string S a `websocket-frame' struct with the contents. -This only gets complete frames. Partial frames need to wait until -the frame finishes. If the frame is not completed, return NIL." - (catch 'websocket-incomplete-frame - (websocket-ensure-length s 1) - (let* ((opcode (websocket-get-opcode s)) - (fin (logand 128 (websocket-get-bytes s 1))) - (payloadp (memq opcode '(continuation text binary ping pong))) - (payload-len (when payloadp - (websocket-get-payload-len (substring s 1)))) - (maskp (and - payloadp - (= 128 (logand 128 (websocket-get-bytes (substring s 1) 1))))) - (payload-start (when payloadp (+ (if maskp 5 1) (cdr payload-len)))) - (payload-end (when payloadp (+ payload-start (car payload-len)))) - (unmasked-payload (when payloadp - (websocket-ensure-length s payload-end) - (substring s payload-start payload-end)))) - (make-websocket-frame - :opcode opcode - :payload - (if maskp - (let ((masking-key (substring s (+ 1 (cdr payload-len)) - (+ 5 (cdr payload-len))))) - (websocket-mask masking-key unmasked-payload)) - unmasked-payload) - :length (if payloadp payload-end 1) - :completep (> fin 0))))) - -(defun websocket-format-error (err) - "Format an error message like command level does. -ERR should be a cons of error symbol and error data." - - ;; Formatting code adapted from `edebug-report-error' - (concat (or (get (car err) 'error-message) - (format "peculiar error (%s)" (car err))) - (when (cdr err) - (format ": %s" - (mapconcat #'prin1-to-string - (cdr err) ", "))))) - -(defun websocket-default-error-handler (_websocket type err) - "The default error handler used to handle errors in callbacks." - (display-warning 'websocket - (format "in callback `%S': %s" - type - (websocket-format-error err)) - :error)) - -;; Error symbols in use by the library -(put 'websocket-unsupported-protocol 'error-conditions - '(error websocket-error websocket-unsupported-protocol)) -(put 'websocket-unsupported-protocol 'error-message "Unsupported websocket protocol") -(put 'websocket-wss-needs-emacs-24 'error-conditions - '(error websocket-error websocket-unsupported-protocol - websocket-wss-needs-emacs-24)) -(put 'websocket-wss-needs-emacs-24 'error-message - "wss protocol is not supported for Emacs before version 24.") -(put 'websocket-received-error-http-response 'error-conditions - '(error websocket-error websocket-received-error-http-response)) -(put 'websocket-received-error-http-response 'error-message - "Error response received from websocket server") -(put 'websocket-invalid-header 'error-conditions - '(error websocket-error websocket-invalid-header)) -(put 'websocket-invalid-header 'error-message - "Invalid HTTP header sent") -(put 'websocket-illegal-frame 'error-conditions - '(error websocket-error websocket-illegal-frame)) -(put 'websocket-illegal-frame 'error-message - "Cannot send illegal frame to websocket") -(put 'websocket-closed 'error-conditions - '(error websocket-error websocket-closed)) -(put 'websocket-closed 'error-message - "Cannot send message to a closed websocket") -(put 'websocket-unparseable-frame 'error-conditions - '(error websocket-error websocket-unparseable-frame)) -(put 'websocket-unparseable-frame 'error-message - "Received an unparseable frame") -(put 'websocket-frame-too-large 'error-conditions - '(error websocket-error websocket-frame-too-large)) -(put 'websocket-frame-too-large 'error-message - "The frame being sent is too large for this emacs to handle") - -(defun websocket-intersect (a b) - "Simple list intersection, should function like Common Lisp's `intersection'." - (let ((result)) - (dolist (elem a (nreverse result)) - (when (member elem b) - (push elem result))))) - -(defun websocket-get-debug-buffer-create (websocket) - "Get or create the buffer corresponding to WEBSOCKET." - (let ((buf (get-buffer-create (format "*websocket %s debug*" - (websocket-url websocket))))) - (when (= 0 (buffer-size buf)) - (buffer-disable-undo buf)) - buf)) - -(defun websocket-debug (websocket msg &rest args) - "In the WEBSOCKET's debug buffer, send MSG, with format ARGS." - (when websocket-debug - (let ((buf (websocket-get-debug-buffer-create websocket))) - (save-excursion - (with-current-buffer buf - (goto-char (point-max)) - (insert "[WS] ") - (insert (apply 'format (append (list msg) args))) - (insert "\n")))))) - -(defun websocket-verify-response-code (output) - "Verify that OUTPUT contains a valid HTTP response code. -The only acceptable one to websocket is responce code 101. -A t value will be returned on success, and an error thrown -if not." - (unless (string-match "^HTTP/1.1 \\([[:digit:]]+\\)" output) - (signal 'websocket-invalid-header "Invalid HTTP status line")) - (unless (equal "101" (match-string 1 output)) - (signal 'websocket-received-error-http-response - (string-to-number (match-string 1 output)))) - t) - -(defun websocket-parse-repeated-field (output field) - "From header-containing OUTPUT, parse out the list from a -possibly repeated field." - (let ((pos 0) - (extensions)) - (while (and pos - (string-match (format "\r\n%s: \\(.*\\)\r\n" field) - output pos)) - (when (setq pos (match-end 1)) - (setq extensions (append extensions (split-string - (match-string 1 output) ", ?"))))) - extensions)) - -(defun websocket-process-frame (websocket frame) - "Using the WEBSOCKET's filter and connection, process the FRAME. -This returns a lambda that should be executed when all frames have -been processed. If the frame has a payload, the lambda has the frame -passed to the filter slot of WEBSOCKET. If the frame is a ping, -the lambda has a reply with a pong. If the frame is a close, the lambda -has connection termination." - (let ((opcode (websocket-frame-opcode frame))) - (lexical-let ((lex-ws websocket) - (lex-frame frame)) - (cond ((memq opcode '(continuation text binary)) - (lambda () (websocket-try-callback 'websocket-on-message 'on-message - lex-ws lex-frame))) - ((eq opcode 'ping) - (lambda () (websocket-send lex-ws - (make-websocket-frame - :opcode 'pong - :payload (websocket-frame-payload lex-frame) - :completep t)))) - ((eq opcode 'close) - (lambda () (delete-process (websocket-conn lex-ws)))) - (t (lambda ())))))) - -(defun websocket-process-input-on-open-ws (websocket text) - "This handles input processing for both the client and server filters." - (let ((current-frame) - (processing-queue) - (start-point 0)) - (while (setq current-frame (websocket-read-frame - (substring text start-point))) - (push (websocket-process-frame websocket current-frame) processing-queue) - (incf start-point (websocket-frame-length current-frame))) - (when (> (length text) start-point) - (setf (websocket-inflight-input websocket) - (substring text start-point))) - (dolist (to-process (nreverse processing-queue)) - (funcall to-process)))) - -(defun websocket-send-text (websocket text) - "To the WEBSOCKET, send TEXT as a complete frame." - (websocket-send - websocket - (make-websocket-frame :opcode 'text - :payload (encode-coding-string - text 'raw-text) - :completep t))) - -(defun websocket-check (frame) - "Check FRAME for correctness, returning true if correct." - (or - ;; Text, binary, and continuation frames need payloads - (and (memq (websocket-frame-opcode frame) '(text binary continuation)) - (websocket-frame-payload frame)) - ;; Pings and pongs may optionally have them - (memq (websocket-frame-opcode frame) '(ping pong)) - ;; And close shouldn't have any payload, and should always be complete. - (and (eq (websocket-frame-opcode frame) 'close) - (not (websocket-frame-payload frame)) - (websocket-frame-completep frame)))) - -(defun websocket-send (websocket frame) - "To the WEBSOCKET server, send the FRAME. -This will raise an error if the frame is illegal. - -The error signaled may be of type `websocket-illegal-frame' if -the frame is malformed in some way, also having the condition -type of `websocket-error'. The data associated with the signal -is the frame being sent. - -If the websocket is closed a signal `websocket-closed' is sent, -also with `websocket-error' condition. The data in the signal is -also the frame. - -The frame may be too large for this buid of Emacs, in which case -`websocket-frame-too-large' is returned, with the data of the -size of the frame which was too large to process. This also has -the `websocket-error' condition." - (unless (websocket-check frame) - (signal 'websocket-illegal-frame frame)) - (websocket-debug websocket "Sending frame, opcode: %s payload: %s" - (websocket-frame-opcode frame) - (websocket-frame-payload frame)) - (websocket-ensure-connected websocket) - (unless (websocket-openp websocket) - (signal 'websocket-closed frame)) - (process-send-string (websocket-conn websocket) - ;; We mask only when we're a client, following the spec. - (websocket-encode-frame frame (not (websocket-server-p websocket))))) - -(defun websocket-openp (websocket) - "Check WEBSOCKET and return non-nil if it is open, and either -connecting or open." - (and websocket - (not (eq 'close (websocket-ready-state websocket))) - (member (process-status (websocket-conn websocket)) '(open run)))) - -(defun websocket-close (websocket) - "Close WEBSOCKET and erase all the old websocket data." - (websocket-debug websocket "Closing websocket") - (websocket-try-callback 'websocket-on-close 'on-close websocket) - (when (websocket-openp websocket) - (websocket-send websocket - (make-websocket-frame :opcode 'close - :completep t)) - (setf (websocket-ready-state websocket) 'closed)) - (delete-process (websocket-conn websocket))) - -(defun websocket-ensure-connected (websocket) - "If the WEBSOCKET connection is closed, open it." - (unless (and (websocket-conn websocket) - (ecase (process-status (websocket-conn websocket)) - ((run open listen) t) - ((stop exit signal closed connect failed nil) nil))) - (websocket-close websocket) - (websocket-open (websocket-url websocket) - :protocols (websocket-protocols websocket) - :extensions (websocket-extensions websocket) - :on-open (websocket-on-open websocket) - :on-message (websocket-on-message websocket) - :on-close (websocket-on-close websocket) - :on-error (websocket-on-error websocket)))) - -;;;;;;;;;;;;;;;;;;;;;; -;; Websocket client ;; -;;;;;;;;;;;;;;;;;;;;;; - -(defun* websocket-open (url &key protocols extensions (on-open 'identity) - (on-message (lambda (_w _f))) (on-close 'identity) - (on-error 'websocket-default-error-handler)) - "Open a websocket connection to URL, returning the `websocket' struct. -The PROTOCOL argument is optional, and setting it will declare to -the server that this client supports the protocols in the list -given. We will require that the server also has to support that -protocols. - -Similar logic applies to EXTENSIONS, which is a list of conses, -the car of which is a string naming the extension, and the cdr of -which is the list of parameter strings to use for that extension. -The parameter strings are of the form \"key=value\" or \"value\". -EXTENSIONS can be NIL if none are in use. An example value would -be (\"deflate-stream\" . (\"mux\" \"max-channels=4\")). - -Cookies that are set via `url-cookie-store' will be used during -communication with the server, and cookies received from the -server will be stored in the same cookie storage that the -`url-cookie' package uses. - -Optionally you can specify -ON-OPEN, ON-MESSAGE and ON-CLOSE callbacks as well. - -The ON-OPEN callback is called after the connection is -established with the websocket as the only argument. The return -value is unused. - -The ON-MESSAGE callback is called after receiving a frame, and is -called with the websocket as the first argument and -`websocket-frame' struct as the second. The return value is -unused. - -The ON-CLOSE callback is called after the connection is closed, or -failed to open. It is called with the websocket as the only -argument, and the return value is unused. - -The ON-ERROR callback is called when any of the other callbacks -have an error. It takes the websocket as the first argument, and -a symbol as the second argument either `on-open', `on-message', -or `on-close', and the error as the third argument. Do NOT -rethrow the error, or else you may miss some websocket messages. -You similarly must not generate any other errors in this method. -If you want to debug errors, set -`websocket-callback-debug-on-error' to t, but this also can be -dangerous is the debugger is quit out of. If not specified, -`websocket-default-error-handler' is used. - -For each of these event handlers, the client code can store -arbitrary data in the `client-data' slot in the returned -websocket. - -The following errors might be thrown in this method or in -websocket processing, all of them having the error-condition -`websocket-error' in addition to their own symbol: - -`websocket-unsupported-protocol': Data in the error signal is the -protocol that is unsupported. For example, giving a URL starting -with http by mistake raises this error. - -`websocket-wss-needs-emacs-24': Trying to connect wss protocol -using Emacs < 24 raises this error. You can catch this error -also by `websocket-unsupported-protocol'. - -`websocket-received-error-http-response': Data in the error -signal is the integer error number. - -`websocket-invalid-header': Data in the error is a string -describing the invalid header received from the server. - -`websocket-unparseable-frame': Data in the error is a string -describing the problem with the frame. -" - (let* ((name (format "websocket to %s" url)) - (url-struct (url-generic-parse-url url)) - (key (websocket-genkey)) - (coding-system-for-read 'binary) - (coding-system-for-write 'binary) - (conn (if (member (url-type url-struct) '("ws" "wss")) - (let* ((type (if (equal (url-type url-struct) "ws") - 'plain 'tls)) - (port (if (= 0 (url-port url-struct)) - (if (eq type 'tls) 443 80) - (url-port url-struct))) - (host (url-host url-struct))) - (if (eq type 'plain) - (make-network-process :name name :buffer nil :host host - :service port :nowait nil) - (condition-case-unless-debug nil - (open-network-stream name nil host port :type type :nowait nil) - (wrong-number-of-arguments - (signal 'websocket-wss-needs-emacs-24 "wss"))))) - (signal 'websocket-unsupported-protocol (url-type url-struct)))) - (websocket (websocket-inner-create - :conn conn - :url url - :on-open on-open - :on-message on-message - :on-close on-close - :on-error on-error - :protocols protocols - :extensions (mapcar 'car extensions) - :accept-string - (websocket-calculate-accept key)))) - (unless conn (error "Could not establish the websocket connection to %s" url)) - (process-put conn :websocket websocket) - (set-process-filter conn - (lambda (process output) - (let ((websocket (process-get process :websocket))) - (websocket-outer-filter websocket output)))) - (set-process-sentinel - conn - (lambda (process change) - (let ((websocket (process-get process :websocket))) - (websocket-debug websocket "State change to %s" change) - (when (and - (member (process-status process) '(closed failed exit signal)) - (not (eq 'closed (websocket-ready-state websocket)))) - (websocket-try-callback 'websocket-on-close 'on-close websocket))))) - (set-process-query-on-exit-flag conn nil) - (process-send-string conn - (format "GET %s HTTP/1.1\r\n" - (let ((path (url-filename url-struct))) - (if (> (length path) 0) path "/")))) - (websocket-debug websocket "Sending handshake, key: %s, acceptance: %s" - key (websocket-accept-string websocket)) - (process-send-string conn - (websocket-create-headers url key protocols extensions)) - (websocket-debug websocket "Websocket opened") - websocket)) - -(defun websocket-process-headers (url headers) - "On opening URL, process the HEADERS sent from the server." - (when (string-match "Set-Cookie: \(.*\)\r\n" headers) - ;; The url-current-object is assumed to be set by - ;; url-cookie-handle-set-cookie. - (let ((url-current-object (url-generic-parse-url url))) - (url-cookie-handle-set-cookie (match-string 1 headers))))) - -(defun websocket-outer-filter (websocket output) - "Filter the WEBSOCKET server's OUTPUT. -This will parse headers and process frames repeatedly until there -is no more output or the connection closes. If the websocket -connection is invalid, the connection will be closed." - (websocket-debug websocket "Received: %s" output) - (let ((start-point) - (text (concat (websocket-inflight-input websocket) output)) - (header-end-pos)) - (setf (websocket-inflight-input websocket) nil) - ;; If we've received the complete header, check to see if we've - ;; received the desired handshake. - (when (and (eq 'connecting (websocket-ready-state websocket))) - (if (and (setq header-end-pos (string-match "\r\n\r\n" text)) - (setq start-point (+ 4 header-end-pos))) - (progn - (condition-case err - (progn - (websocket-verify-response-code text) - (websocket-verify-headers websocket text) - (websocket-process-headers (websocket-url websocket) text)) - (error - (websocket-close websocket) - (signal (car err) (cdr err)))) - (setf (websocket-ready-state websocket) 'open) - (websocket-try-callback 'websocket-on-open 'on-open websocket)) - (setf (websocket-inflight-input websocket) text))) - (when (eq 'open (websocket-ready-state websocket)) - (websocket-process-input-on-open-ws - websocket (substring text (or start-point 0)))))) - -(defun websocket-verify-headers (websocket output) - "Based on WEBSOCKET's data, ensure the headers in OUTPUT are valid. -The output is assumed to have complete headers. This function -will either return t or call `error'. This has the side-effect -of populating the list of server extensions to WEBSOCKET." - (let ((accept-string - (concat "Sec-WebSocket-Accept: " (websocket-accept-string websocket)))) - (websocket-debug websocket "Checking for accept header: %s" accept-string) - (unless (string-match (regexp-quote accept-string) output) - (signal 'websocket-invalid-header - "Incorrect handshake from websocket: is this really a websocket connection?"))) - (let ((case-fold-search t)) - (websocket-debug websocket "Checking for upgrade header") - (unless (string-match "\r\nUpgrade: websocket\r\n" output) - (signal 'websocket-invalid-header - "No 'Upgrade: websocket' header found")) - (websocket-debug websocket "Checking for connection header") - (unless (string-match "\r\nConnection: upgrade\r\n" output) - (signal 'websocket-invalid-header - "No 'Connection: upgrade' header found")) - (when (websocket-protocols websocket) - (dolist (protocol (websocket-protocols websocket)) - (websocket-debug websocket "Checking for protocol match: %s" - protocol) - (let ((protocols - (if (string-match (format "\r\nSec-Websocket-Protocol: %s\r\n" - protocol) - output) - (list protocol) - (signal 'websocket-invalid-header - "Incorrect or missing protocol returned by the server.")))) - (setf (websocket-negotiated-protocols websocket) protocols)))) - (let* ((extensions (websocket-parse-repeated-field - output - "Sec-WebSocket-Extensions")) - (extra-extensions)) - (dolist (ext extensions) - (let ((x (first (split-string ext "; ?")))) - (unless (or (member x (websocket-extensions websocket)) - (member x extra-extensions)) - (push x extra-extensions)))) - (when extra-extensions - (signal 'websocket-invalid-header - (format "Non-requested extensions returned by server: %S" - extra-extensions))) - (setf (websocket-negotiated-extensions websocket) extensions))) - t) - -;;;;;;;;;;;;;;;;;;;;;; -;; Websocket server ;; -;;;;;;;;;;;;;;;;;;;;;; - -(defvar websocket-server-websockets nil - "A list of current websockets live on any server.") - -(defun* websocket-server (port &rest plist) - "Open a websocket server on PORT. -If the plist contains a `:host' HOST pair, this value will be -used to configure the addresses the socket listens on. The symbol -`local' specifies the local host. If unspecified or nil, the -socket will listen on all addresses. - -This also takes a plist of callbacks: `:on-open', `:on-message', -`:on-close' and `:on-error', which operate exactly as documented -in the websocket client function `websocket-open'. Returns the -connection, which should be kept in order to pass to -`websocket-server-close'." - (let* ((conn (make-network-process - :name (format "websocket server on port %s" port) - :server t - :family 'ipv4 - :filter 'websocket-server-filter - :log 'websocket-server-accept - :filter-multibyte nil - :plist plist - :host (plist-get plist :host) - :service port))) - conn)) - -(defun websocket-server-close (conn) - "Closes the websocket, as well as all open websockets for this server." - (let ((to-delete)) - (dolist (ws websocket-server-websockets) - (when (eq (websocket-server-conn ws) conn) - (if (eq (websocket-ready-state ws) 'closed) - (unless (member ws to-delete) - (push ws to-delete)) - (websocket-close ws)))) - (dolist (ws to-delete) - (setq websocket-server-websockets (remove ws websocket-server-websockets)))) - (delete-process conn)) - -(defun websocket-server-accept (server client _message) - "Accept a new websocket connection from a client." - (let ((ws (websocket-inner-create - :server-conn server - :conn client - :url client - :server-p t - :on-open (or (process-get server :on-open) 'identity) - :on-message (or (process-get server :on-message) (lambda (_ws _frame))) - :on-close (lexical-let ((user-method - (or (process-get server :on-close) 'identity))) - (lambda (ws) - (setq websocket-server-websockets - (remove ws websocket-server-websockets)) - (funcall user-method ws))) - :on-error (or (process-get server :on-error) - 'websocket-default-error-handler) - :protocols (process-get server :protocol) - :extensions (mapcar 'car (process-get server :extensions))))) - (unless (member ws websocket-server-websockets) - (push ws websocket-server-websockets)) - (process-put client :websocket ws) - (set-process-coding-system client 'binary 'binary) - (set-process-sentinel client - (lambda (process change) - (let ((websocket (process-get process :websocket))) - (websocket-debug websocket "State change to %s" change) - (when (and - (member (process-status process) '(closed failed exit signal)) - (not (eq 'closed (websocket-ready-state websocket)))) - (websocket-try-callback 'websocket-on-close 'on-close websocket))))))) - -(defun websocket-create-headers (url key protocol extensions) - "Create connections headers for the given URL, KEY, PROTOCOL and EXTENSIONS. -These are defined as in `websocket-open'." - (let* ((parsed-url (url-generic-parse-url url)) - (host-port (if (url-port-if-non-default parsed-url) - (format "%s:%s" (url-host parsed-url) (url-port parsed-url)) - (url-host parsed-url))) - (cookie-header (url-cookie-generate-header-lines - host-port (car (url-path-and-query parsed-url)) - (equal (url-type parsed-url) "wss")))) - (format (concat "Host: %s\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Key: %s\r\n" - "Sec-WebSocket-Version: 13\r\n" - (when protocol - (concat - (mapconcat - (lambda (protocol) - (format "Sec-WebSocket-Protocol: %s" protocol)) - protocol "\r\n") - "\r\n")) - (when extensions - (format "Sec-WebSocket-Extensions: %s\r\n" - (mapconcat - (lambda (ext) - (concat - (car ext) - (when (cdr ext) "; ") - (when (cdr ext) - (mapconcat 'identity (cdr ext) "; ")))) - extensions ", "))) - (when cookie-header cookie-header) - "\r\n") - host-port - key - protocol))) - -(defun websocket-get-server-response (websocket client-protocols client-extensions) - "Get the websocket response from client WEBSOCKET." - (let ((separator "\r\n")) - (concat "HTTP/1.1 101 Switching Protocols" separator - "Upgrade: websocket" separator - "Connection: Upgrade" separator - "Sec-WebSocket-Accept: " - (websocket-accept-string websocket) separator - (let ((protocols - (websocket-intersect client-protocols - (websocket-protocols websocket)))) - (when protocols - (concat - (mapconcat - (lambda (protocol) (format "Sec-WebSocket-Protocol: %s" - protocol)) protocols separator) - separator))) - (let ((extensions (websocket-intersect - client-extensions - (websocket-extensions websocket)))) - (when extensions - (concat - (mapconcat - (lambda (extension) (format "Sec-Websocket-Extensions: %s" - extension)) extensions separator) - separator))) - separator))) - -(defun websocket-server-filter (process output) - "This acts on all OUTPUT from websocket clients PROCESS." - (let* ((ws (process-get process :websocket)) - (text (concat (websocket-inflight-input ws) output))) - (setf (websocket-inflight-input ws) nil) - (cond ((eq (websocket-ready-state ws) 'connecting) - ;; check for connection string - (let ((end-of-header-pos - (let ((pos (string-match "\r\n\r\n" text))) - (when pos (+ 4 pos))))) - (if end-of-header-pos - (progn - (let ((header-info (websocket-verify-client-headers text))) - (if header-info - (progn (setf (websocket-accept-string ws) - (websocket-calculate-accept - (plist-get header-info :key))) - (process-send-string - process - (websocket-get-server-response - ws (plist-get header-info :protocols) - (plist-get header-info :extensions))) - (setf (websocket-ready-state ws) 'open) - (websocket-try-callback 'websocket-on-open - 'on-open ws)) - (message "Invalid client headers found in: %s" output) - (process-send-string process "HTTP/1.1 400 Bad Request\r\n\r\n") - (websocket-close ws))) - (when (> (length text) (+ 1 end-of-header-pos)) - (websocket-server-filter process (substring - text - end-of-header-pos)))) - (setf (websocket-inflight-input ws) text)))) - ((eq (websocket-ready-state ws) 'open) - (websocket-process-input-on-open-ws ws text)) - ((eq (websocket-ready-state ws) 'closed) - (message "WARNING: Should not have received further input on closed websocket"))))) - -(defun websocket-verify-client-headers (output) - "Verify the headers from the WEBSOCKET client connection in OUTPUT. -Unlike `websocket-verify-headers', this is a quieter routine. We -don't want to error due to a bad client, so we just print out -messages and a plist containing `:key', the websocket key, -`:protocols' and `:extensions'." - (block nil - (let ((case-fold-search t) - (plist)) - (unless (string-match "HTTP/1.1" output) - (message "Websocket client connection: HTTP/1.1 not found") - (return nil)) - (unless (string-match "^Host: " output) - (message "Websocket client connection: Host header not found") - (return nil)) - (unless (string-match "^Upgrade: websocket\r\n" output) - (message "Websocket client connection: Upgrade: websocket not found") - (return nil)) - (if (string-match "^Sec-WebSocket-Key: \\([[:graph:]]+\\)\r\n" output) - (setq plist (plist-put plist :key (match-string 1 output))) - (message "Websocket client connect: No key sent") - (return nil)) - (unless (string-match "^Sec-WebSocket-Version: 13" output) - (message "Websocket client connect: Websocket version 13 not found") - (return nil)) - (when (string-match "^Sec-WebSocket-Protocol:" output) - (setq plist (plist-put plist :protocols (websocket-parse-repeated-field - output - "Sec-Websocket-Protocol")))) - (when (string-match "^Sec-WebSocket-Extensions:" output) - (setq plist (plist-put plist :extensions (websocket-parse-repeated-field - output - "Sec-Websocket-Extensions")))) - plist))) - -(provide 'websocket) - -;;; websocket.el ends here diff --git a/init.el b/init.el index 70ef4aa..db41f38 100644 --- a/init.el +++ b/init.el @@ -112,7 +112,6 @@ projectile sass-mode simple-rtm - slack smart-mode-line smart-mode-line-powerline-theme smartparens