1 // From our Gitter fork: https://github.com/gitterHQ/emojify.js
4 (function(root
, factory
) {
7 if (typeof define
=== 'function' && define
.amd
) {
8 // AMD. Register as an anonymous module.
10 } else if (typeof exports
=== 'object') {
11 // Node. Does not work with strict CommonJS, but
12 // only CommonJS-like environments that support module.exports,
14 module
.exports
= factory();
16 // Browser globals (root is window)
17 root
.emojify
= factory();
22 var emojify
= (function() {
23 // Get DOM as local variable for simplicity's sake
24 var document
= typeof window
!== 'undefined' && window
.document
;
28 * The namedEmojiString variable is updated automatically by the
29 * `update.sh` script. Do not remove the markers as this will
30 * cause `update.sh` to stop working.
32 var namedEmojiString
=
33 /*##EMOJILIST*/ '+1,-1,100,1234,8ball,a,ab,abc,abcd,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,anger,angry,anguished,ant,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,arrow_lower_right,arrow_right,arrow_right_hook,arrow_up,arrow_up_down,arrow_up_small,arrow_upper_left,arrow_upper_right,arrows_clockwise,arrows_counterclockwise,art,articulated_lorry,astonished,atm,b,baby,baby_bottle,baby_chick,baby_symbol,back,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,bank,bar_chart,barber,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_medium_small_square,black_medium_square,black_nib,black_small_square,black_square,black_square_button,blossom,blowfish,blue_book,blue_car,blue_heart,blush,boar,boat,bomb,book,bookmark,bookmark_tabs,books,boom,boot,bouquet,bow,bowling,bowtie,boy,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,capricorn,car,card_index,carousel_horse,cat,cat2,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,cinema,circus_tent,city_sunrise,city_sunset,cl,clap,clapper,clipboard,clock1,clock10,clock1030,clock11,clock1130,clock12,clock1230,clock130,clock2,clock230,clock3,clock330,clock4,clock430,clock5,clock530,clock6,clock630,clock7,clock730,clock8,clock830,clock9,clock930,closed_book,closed_lock_with_key,closed_umbrella,cloud,clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couple,couple_with_heart,couplekiss,cow,cow2,credit_card,crescent_moon,crocodile,crossed_flags,crown,cry,crying_cat_face,crystal_ball,cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,de,deciduous_tree,department_store,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,dizzy,dizzy_face,do_not_litter,dog,dog2,dollar,dolls,dolphin,donut,door,doughnut,dragon,dragon_face,dress,dromedary_camel,droplet,dvd,e-mail,ear,ear_of_rice,earth_africa,earth_americas,earth_asia,egg,eggplant,eight,eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,email,end,envelope,es,euro,european_castle,european_post_office,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire,fire_engine,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish,fish_cake,fishing_pole_and_fish,fist,five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four,four_leaf_clover,fr,free,fried_shrimp,fries,frog,frowning,fu,fuelpump,full_moon,full_moon_with_face,game_die,gb,gem,gemini,ghost,gift,gift_heart,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,green_book,green_heart,grey_exclamation,grey_question,grimacing,grin,grinning,guardsman,guitar,gun,haircut,hamburger,hammer,hamster,hand,handbag,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heart,heart_decoration,heart_eyes,heart_eyes_cat,heartbeat,heartpulse,hearts,heavy_check_mark,heavy_division_sign,heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,herb,hibiscus,high_brightness,high_heel,hocho,honey_pot,honeybee,horse,horse_racing,hospital,hotel,hotsprings,hourglass,hourglass_flowing_sand,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,id,ideograph_advantage,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japan,japanese_castle,japanese_goblin,japanese_ogre,jeans,joy,joy_cat,jp,key,keycap_ten,kimono,kiss,kissing,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,kissing_smiling_eyes,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,lemon,leo,leopard,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,love_hotel,love_letter,low_brightness,m,mag,mag_right,mahjong,mailbox,mailbox_closed,mailbox_with_mail,mailbox_with_no_mail,man,man_with_gua_pi_mao,man_with_turban,mans_shoe,maple_leaf,mask,massage,meat_on_bone,mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,money_with_wings,moneybag,monkey,monkey_face,monorail,mortar_board,mount_fuji,mountain_bicyclist,mountain_cableway,mountain_railway,mouse,mouse2,movie_camera,moyai,muscle,mushroom,musical_keyboard,musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,neutral_face,new,new_moon,new_moon_with_face,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,no_good,no_mobile_phones,no_mouth,no_pedestrians,no_smoking,non-potable_water,nose,notebook,notebook_with_decorative_cover,notes,nut_and_bolt,o,o2,ocean,octocat,octopus,oden,office,ok,ok_hand,ok_woman,older_man,older_woman,on,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,open_file_folder,open_hands,open_mouth,ophiuchus,orange_book,outbox_tray,ox,package,page_facing_up,page_with_curl,pager,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,passport_control,paw_prints,peach,pear,pencil,pencil2,penguin,pensive,performing_arts,persevere,person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig,pig2,pig_nose,pill,pineapple,pisces,pizza,plus1,point_down,point_left,point_right,point_up,point_up_2,police_car,poodle,poop,post_office,postal_horn,postbox,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,pushpin,put_litter_in_its_place,question,rabbit,rabbit2,racehorse,radio,radio_button,rage,rage1,rage2,rage3,rage4,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ram,ramen,rat,recycle,red_car,red_circle,registered,relaxed,relieved,repeat,repeat_one,restroom,revolving_hearts,rewind,ribbon,rice,rice_ball,rice_cracker,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,ru,rugby_football,runner,running,running_shirt_with_sash,sa,sagittarius,sailboat,sake,sandal,santa,satellite,satisfied,saxophone,school,school_satchel,scissors,scorpius,scream,scream_cat,scroll,seat,secret,see_no_evil,seedling,seven,shaved_ice,sheep,shell,ship,shipit,shirt,shit,shoe,shower,signal_strength,slight_smile,six,six_pointed_star,ski,skull,sleeping,sleepy,slot_machine,small_blue_diamond,small_orange_diamond,small_red_triangle,small_red_triangle_down,smile,smile_cat,smiley,smiley_cat,smiling_imp,smirk,smirk_cat,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,spaghetti,sparkle,sparkler,sparkles,sparkling_heart,speak_no_evil,speaker,speech_balloon,speedboat,squirrel,star,star2,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue,stuck_out_tongue_closed_eyes,stuck_out_tongue_winking_eye,sun_with_face,sunflower,sunglasses,sunny,sunrise,sunrise_over_mountains,surfer,sushi,suspect,suspension_railway,sweat,sweat_drops,sweat_smile,sweet_potato,swimmer,symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,thought_balloon,three,thumbsdown,thumbsup,ticket,tiger,tiger2,tired_face,tm,toilet,tokyo_tower,tomato,tongue,top,tophat,tractor,traffic_light,train,train2,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,two,two_hearts,two_men_holding_hands,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,v,vertical_traffic_light,vhs,vibration_mode,video_camera,video_game,violin,virgo,volcano,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale,whale2,wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_medium_small_square,white_medium_square,white_small_square,white_square_button,wind_chime,wine_glass,wink,wolf,woman,womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz';
35 var namedEmoji
= namedEmojiString
.split(/,/);
37 /* A hash with the named emoji as keys */
38 var namedMatchHash
= namedEmoji
.reduce(function(memo
, v
) {
43 var emoticonsProcessed
;
46 function initEmoticonsProcessed() {
47 /* List of emoticons used in the regular expression */
49 /* :..: */ named
: /:([a-z0-9A-Z_-]+):/,
50 /* :-) */ slight_smile
: /:-?\)/g,
51 /* :-D */ smiley
: /[:;]-?d/gi,
52 /* ;-) */ wink
: /;-?\)/g,
53 /* :-( */ worried
: /:-?\(/g,
54 /* :'-( */ sob
: /:['’]-?\(|:'\(/g,
55 /* :-/ */ confused
: /:-?\//g,
56 /* :-s */ confounded
: /:-?s/gi,
57 /* :-p */ stuck_out_tongue
: /:-?p/gi,
58 /* ;-p */ stuck_out_tongue_winking_eye
: /;-?p/gi,
59 /* :-o */ open_mouth
: /:-?o/gi,
60 /* >:-( */ angry
: />:-?\(/gi,
61 /* :-* */ kissing
: /:-?\*/g,
62 /* :-| */ expressionless
: /:-?\|/g,
63 /* :-x */ mask
: /:-x/gi,
64 /* <3 */ heart
: /<3|<3/g,
65 /* </3 */ broken_heart: /<\/3|</3/g,
66 /* :+1: */ thumbsup
: /:\+1:/g,
67 /* :-1: */ thumbsdown
: /:\-1:/g
70 if (defaultConfig
.ignore_emoticons
) {
72 /* :..: */ named
: /:([a-z0-9A-Z_-]+):/,
73 /* :+1: */ thumbsup
: /:\+1:/g,
74 /* :-1: */ thumbsdown
: /:\-1:/g
78 return Object
.keys(emoticons
).map(function(key
) {
79 return [emoticons
[key
], key
];
83 function initMegaRe() {
84 /* The source for our mega-regex */
85 var mega
= emoticonsProcessed
88 var val
= re
.source
|| re
;
89 val
= val
.replace(/(^|[^\[])\^/g, '$1');
90 return '(' + val
+ ')';
94 /* The regex used to find emoji */
95 return new RegExp(mega
, 'gi');
99 emojify_tag_type
: null,
101 img_dir
: 'images/emoji',
102 ignore_emoticons
: false,
113 /* Returns true if the given char is whitespace */
114 function isWhitespace(s
) {
115 return s
=== ' ' || s
=== '\t' || s
=== '\r' || s
=== '\n' || s
=== '';
118 /* Given a match in a node, replace the text with an image */
119 function insertEmojicon(node
, match
, emojiName
) {
120 var emojiElement
= document
.createElement(defaultConfig
.emojify_tag_type
|| 'img');
122 if (defaultConfig
.emojify_tag_type
&& defaultConfig
.emojify_tag_type
!== 'img') {
123 emojiElement
.setAttribute('class', 'emoji emoji-' + emojiName
);
125 emojiElement
.setAttribute('class', 'emoji');
126 emojiElement
.setAttribute('src', defaultConfig
.img_dir
+ '/' + emojiName
+ '.png');
130 * mutantjs doesn't have the ability to resolve CSS properties (we could add it in, but it would slow it down a lot)
131 * so it detects that an image will cause a reflow when:
132 * - The image is in a loading state
133 * - The image does not have height and width attributes
134 * Then, when the image state changes to loaded, it emits a reflow notification.
136 * For this reason, make sure we set the height and width on the emoji image.
138 emojiElement
.setAttribute('height', '20');
139 emojiElement
.setAttribute('width', '20');
141 emojiElement
.setAttribute('title', ':' + emojiName
+ ':');
142 emojiElement
.setAttribute('alt', ':' + emojiName
+ ':');
143 emojiElement
.setAttribute('align', 'absmiddle');
144 node
.splitText(match
.index
);
145 node
.nextSibling
.nodeValue
= node
.nextSibling
.nodeValue
.substr(
147 node
.nextSibling
.nodeValue
.length
149 emojiElement
.appendChild(node
.splitText(match
.index
));
150 node
.parentNode
.insertBefore(emojiElement
, node
.nextSibling
);
153 /* Given an regex match, return the name of the matching emoji */
154 function getEmojiNameForMatch(match
) {
155 /* Special case for named emoji */
156 if (match
[1] && match
[2]) {
157 var named
= match
[2];
158 if (namedMatchHash
[named
]) {
163 for (var i
= 3; i
< match
.length
- 1; i
++) {
165 return emoticonsProcessed
[i
- 2][1];
170 function defaultReplacer(emoji
, name
) {
171 /*jshint validthis: true */
172 if (this.config
.emojify_tag_type
&& this.config
.emojify_tag_type
!== 'img') {
175 this.config
.emojify_tag_type
+
180 ":' class='emoji emoji-" +
183 this.config
.emojify_tag_type
+
192 ":' class='emoji' src='" +
193 this.config
.img_dir
+
196 ".png' align='absmiddle' />"
201 function Validator() {
202 this.lastEmojiTerminatedAt
= -1;
205 Validator
.prototype = {
206 validate: function(match
, index
, input
) {
210 var emojiName
= getEmojiNameForMatch(match
);
216 var length
= m
.length
;
217 // var index = match.index;
218 // var input = match.input;
221 self
.lastEmojiTerminatedAt
= length
+ index
;
225 /* At the beginning? */
231 if (input
.length
=== m
.length
+ index
) {
235 var hasEmojiBefore
= this.lastEmojiTerminatedAt
=== index
;
236 if (hasEmojiBefore
) {
240 /* Has a whitespace before? */
241 if (isWhitespace(input
.charAt(index
- 1))) {
245 var hasWhitespaceAfter
= isWhitespace(input
.charAt(m
.length
+ index
));
246 /* Has a whitespace after? */
247 if (hasWhitespaceAfter
&& hasEmojiBefore
) {
255 function emojifyString(htmlString
, replacer
) {
260 replacer
= defaultReplacer
;
263 emoticonsProcessed
= initEmoticonsProcessed();
264 emojiMegaRe
= initMegaRe();
266 var validator
= new Validator();
268 return htmlString
.replace(emojiMegaRe
, function() {
269 var matches
= Array
.prototype.slice
.call(arguments
, 0, -2);
270 var index
= arguments
[arguments
.length
- 2];
271 var input
= arguments
[arguments
.length
- 1];
272 var emojiName
= validator
.validate(matches
, index
, input
);
274 return replacer
.apply(
276 config
: defaultConfig
278 [arguments
[0], emojiName
]
281 /* Did not validate, return the original value */
287 emoticonsProcessed
= initEmoticonsProcessed();
288 emojiMegaRe
= initMegaRe();
290 // Check if an element was not passed.
291 if (typeof el
=== 'undefined') {
292 // Check if an element was configured. If not, default to the body.
293 if (defaultConfig
.only_crawl_id
) {
294 el
= document
.getElementById(defaultConfig
.only_crawl_id
);
300 var ignoredTags
= defaultConfig
.ignored_tags
;
302 var nodeIterator
= document
.createTreeWalker(
304 NodeFilter
.SHOW_TEXT
| NodeFilter
.SHOW_ELEMENT
,
306 if (node
.nodeType
!== 1) {
307 /* Text Node? Good! */
308 return NodeFilter
.FILTER_ACCEPT
;
311 if (ignoredTags
[node
.tagName
]) {
312 return NodeFilter
.FILTER_REJECT
;
315 if (node
.classList
&& node
.classList
.contains('no-emojify')) {
316 return NodeFilter
.FILTER_REJECT
;
319 return NodeFilter
.FILTER_SKIP
;
326 while ((node
= nodeIterator
.nextNode()) !== null) {
330 nodeList
.forEach(function(node
) {
333 var validator
= new Validator();
335 while ((match
= emojiMegaRe
.exec(node
.data
)) !== null) {
336 if (validator
.validate(match
, match
.index
, match
.input
)) {
341 for (var i
= matches
.length
; i
-- > 0; ) {
342 /* Replace the text with the emoji */
343 var emojiName
= getEmojiNameForMatch(matches
[i
]);
344 insertEmojicon(node
, matches
[i
], emojiName
);
351 defaultConfig
: defaultConfig
,
352 emojiNames
: namedEmoji
,
353 setConfig: function(newConfig
) {
354 Object
.keys(defaultConfig
).forEach(function(f
) {
355 if (f
in newConfig
) {
356 defaultConfig
[f
] = newConfig
[f
];
361 replace
: emojifyString
,