summaryrefslogtreecommitdiffstats
path: root/webapp/utils/emoticons.jsx
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2016-03-23 15:36:43 -0400
committerHarrison Healey <harrisonmhealey@gmail.com>2016-03-23 17:49:15 -0400
commit016c179f0134f7f292944e9016e1983cd41774fb (patch)
tree076eb16c324a1bf6909e0414098ba5ff1ff53d90 /webapp/utils/emoticons.jsx
parentc24a5c5a9ac16fdb249656f0c238b8e44727123c (diff)
downloadchat-016c179f0134f7f292944e9016e1983cd41774fb.tar.gz
chat-016c179f0134f7f292944e9016e1983cd41774fb.tar.bz2
chat-016c179f0134f7f292944e9016e1983cd41774fb.zip
Change emoticon rendering code to use image files with unicode names
Diffstat (limited to 'webapp/utils/emoticons.jsx')
-rw-r--r--webapp/utils/emoticons.jsx191
1 files changed, 87 insertions, 104 deletions
diff --git a/webapp/utils/emoticons.jsx b/webapp/utils/emoticons.jsx
index b675ca3cc..d7ba66388 100644
--- a/webapp/utils/emoticons.jsx
+++ b/webapp/utils/emoticons.jsx
@@ -2,17 +2,21 @@
// See License.txt for license information.
import $ from 'jquery';
+
+import Constants from './constants.jsx';
+import emojis from './emoji.json';
+
const emoticonPatterns = {
- slightly_smiling_face: /(^|\s)(:-?\))(?=$|\s)/g, // :)
+ smile: /(^|\s)(:-?\))(?=$|\s)/g, // :)
wink: /(^|\s)(;-?\))(?=$|\s)/g, // ;)
open_mouth: /(^|\s)(:o)(?=$|\s)/gi, // :o
scream: /(^|\s)(:-o)(?=$|\s)/gi, // :-o
smirk: /(^|\s)(:-?])(?=$|\s)/g, // :]
- smile: /(^|\s)(:-?d)(?=$|\s)/gi, // :D
+ grinning: /(^|\s)(:-?d)(?=$|\s)/gi, // :D
stuck_out_tongue_closed_eyes: /(^|\s)(x-d)(?=$|\s)/gi, // x-d
stuck_out_tongue: /(^|\s)(:-?p)(?=$|\s)/gi, // :p
rage: /(^|\s)(:-?[\[@])(?=$|\s)/g, // :@
- slightly_frowning_face: /(^|\s)(:-?\()(?=$|\s)/g, // :(
+ frowning: /(^|\s)(:-?\()(?=$|\s)/g, // :(
cry: /(^|\s)(:['’]-?\(|:&#x27;\(|:&#39;\()(?=$|\s)/g, // :`(
confused: /(^|\s)(:-?\/)(?=$|\s)/g, // :/
confounded: /(^|\s)(:-?s)(?=$|\s)/gi, // :s
@@ -25,115 +29,97 @@ const emoticonPatterns = {
thumbsdown: /(^|\s)(:\-1:)(?=$|\s)/g // :-1:
};
-function initializeEmoticonMap() {
- const emoticonNames =
- ('+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_large_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,six,six_pointed_star,' +
- 'ski,skull,sleeping,sleepy,slightly_smiling_face,slightly_frowning_face,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').split(',');
-
- // use a map to help make lookups faster instead of having to use indexOf on an array
- const out = new Map();
-
- for (let i = 0; i < emoticonNames.length; i++) {
- out.set(emoticonNames[i], true);
+export const emoticons = initializeEmoticons();
+
+function initializeEmoticons() {
+ const emoticons = new Map();
+
+ for (const emoji of emojis) {
+ const unicode = emoji.emoji;
+
+ let filename = '';
+ if (emoji.emoji) {
+ // this is a unicode emoji so the character code determines the file name
+ const code = fixedCharCodeAt(emoji.emoji, 0).toString(16);
+ filename = pad(code.toString(16));
+
+ if (emoji.emoji.length > 2) {
+ // some emojis like the country flags span multiple utf-16 characters
+ for (let i = 2; i < emoji.emoji.length; i += 2) {
+ const code = fixedCharCodeAt(emoji.emoji, i);
+
+ // ignore variation selectors
+ if (code < 0xfe00 || code > 0xfe0f) {
+ filename += '-' + pad(code.toString(16));
+ }
+ }
+ }
+ } else {
+ // this isn't a unicode emoji so the first alias determines the file name
+ filename = emoji.aliases[0];
+ }
+
+ for (const alias of emoji.aliases) {
+ emoticons.set(alias, {
+ alias,
+ path: getImagePathForEmoticon(filename)
+ });
+ }
}
- return out;
+ return emoticons;
}
-export const emoticonMap = initializeEmoticonMap();
+// Pads a hexadecimal number with zeroes to be at least 4 digits long
+function pad(n) {
+ if (n.length >= 4) {
+ return n;
+ }
+
+ // http://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
+ return ('0000' + n).slice(-4);
+}
+
+// Gets the unicode character code of a character starting at the given index in the string
+// Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
+function fixedCharCodeAt(str, idx = 0) {
+ // ex. fixedCharCodeAt('\uD800\uDC00', 0); // 65536
+ // ex. fixedCharCodeAt('\uD800\uDC00', 1); // false
+ const code = str.charCodeAt(idx);
+
+ // High surrogate (could change last hex to 0xDB7F to treat high
+ // private surrogates as single characters)
+ if (0xD800 <= code && code <= 0xDBFF) {
+ const hi = code;
+ const low = str.charCodeAt(idx + 1);
+
+ if (isNaN(low)) {
+ console.log('High surrogate not followed by low surrogate in fixedCharCodeAt()'); // eslint-ignore-line
+ }
+
+ return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
+ }
+
+ if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate
+ // We return false to allow loops to skip this iteration since should have
+ // already handled high surrogate above in the previous iteration
+ return false;
+ }
+
+ return code;
+}
export function handleEmoticons(text, tokens) {
let output = text;
function replaceEmoticonWithToken(fullMatch, prefix, matchText, name) {
- if (emoticonMap.has(name)) {
+ if (emoticons.has(name)) {
const index = tokens.size;
const alias = `MM_EMOTICON${index}`;
+ const path = emoticons.get(name).path;
tokens.set(alias, {
- value: `<img align="absmiddle" alt="${matchText}" class="emoticon" src="${getImagePathForEmoticon(name)}" title="${matchText}" />`,
+ value: `<img align="absmiddle" alt="${matchText}" class="emoticon" src="${path}" title="${matchText}" />`,
originalText: fullMatch
});
@@ -154,9 +140,6 @@ export function handleEmoticons(text, tokens) {
return output;
}
-export function getImagePathForEmoticon(name) {
- if (name) {
- return `/static/emoji/${name}.png`;
- }
- return '/static/emoji';
+function getImagePathForEmoticon(name) {
+ return Constants.EMOJI_PATH + '/' + name + '.png';
}