require "active_support/core_ext/object/blank" require "active_support/core_ext/hash/deep_merge" require "base64" require "fileutils" require "image_optim" require "json" require "nokogiri" require "open-uri" require "pathname" $debugging_output = [] EMOJI_GROUPS_PATH ||= "app/assets/javascripts/discourse/lib/emoji/groups.js.es6" EMOJI_DB_PATH ||= "lib/emoji/db.json" EMOJI_IMAGES_PATH ||= "public/images/emoji" EMOJI_LIST_URL ||= "http://unicode.org/emoji/charts/full-emoji-list.html" EMOJI_KEYWORDS_URL ||= "https://raw.githubusercontent.com/muan/emojilib/master/emojis.json" EMOJI_ALIASES ||= { "right_anger_bubble" => [ "anger_right" ], "ballot_box" => [ "ballot_box_with_ballot" ], "basketball_man" => [ "basketball_player", "person_with_ball" ], "beach_umbrella" => [ "umbrella_on_ground", "beach", "beach_with_umbrella" ], "parasol_on_ground" => [ "umbrella_on_ground" ], "bellhop_bell" => [ "bellhop" ], "biohazard" => [ "biohazard_sign" ], "bow_and_arrow" => [ "archery" ], "spiral_calendar" => [ "calendar_spiral", "spiral_calendar_pad" ], "card_file_box" => [ "card_box" ], "champagne" => [ "bottle_with_popping_cork" ], "cheese" => [ "cheese_wedge" ], "city_sunset" => [ "city_dusk" ], "clock" => [ "mantlepiece_clock" ], "couch_and_lamp" => [ "couch" ], "crayon" => [ "lower_left_crayon" ], "cricket" => [ "cricket_bat_ball" ], "latin_cross" => [ "cross" ], "dagger" => [ "dagger_knife" ], "desktop_computer" => [ "desktop" ], "card_index_dividers" => [ "dividers" ], "dove" => [ "dove_of_peace" ], "footprints" => [ "feet" ], "fire" => [ "flame" ], "black_flag" => [ "flag_black", "waving_black_flag" ], "cn" => [ "flag_cn" ], "de" => [ "flag_de" ], "es" => [ "flag_es" ], "fr" => [ "flag_fr" ], "uk" => [ "gb", "flag_gb" ], "it" => [ "flag_it" ], "jp" => [ "flag_jp" ], "kr" => [ "flag_kr" ], "ru" => [ "flag_ru" ], "us" => [ "flag_us" ], "white_flag" => [ "flag_white", "waving_white_flag" ], "plate_with_cutlery" => [ "fork_knife_plate", "fork_and_knife_with_plate" ], "framed_picture" => [ "frame_photo", "frame_with_picture" ], "hammer_and_pick" => [ "hammer_pick" ], "heavy_heart_exclamation" => [ "heart_exclamation", "heavy_heart_exclamation_mark_ornament" ], "houses" => [ "homes", "house_buildings" ], "hotdog" => [ "hot_dog" ], "derelict_house" => [ "house_abandoned", "derelict_house_building" ], "desert_island" => [ "island" ], "old_key" => [ "key2" ], "laughing" => [ "satisfied" ], "business_suit_levitating" => [ "levitate", "man_in_business_suit_levitating" ], "weight_lifting_man" => [ "lifter", "weight_lifter" ], "medal_sports" => [ "medal", "sports_medal" ], "metal" => [ "sign_of_the_horns" ], "fu" => [ "middle_finger", "reversed_hand_with_middle_finger_extended" ], "motorcycle" => [ "racing_motorcycle" ], "mountain_snow" => [ "snow_capped_mountain" ], "newspaper_roll" => [ "newspaper2", "rolled_up_newspaper" ], "spiral_notepad" => [ "notepad_spiral", "spiral_note_pad" ], "oil_drum" => [ "oil" ], "older_woman" => [ "grandma" ], "paintbrush" => [ "lower_left_paintbrush" ], "paperclips" => [ "linked_paperclips" ], "pause_button" => [ "double_vertical_bar" ], "peace_symbol" => [ "peace" ], "pen_ballpoint" => [ "lower_left_ballpoint_pen" ], "fountain_pen" => [ "pen_fountain", "lower_left_fountain_pen" ], "ping_pong" => [ "table_tennis" ], "place_of_worship" => [ "worship_symbol" ], "poop" => [ "shit", "hankey", "poo" ], "radioactive" => [ "radioactive_sign" ], "railway_track" => [ "railroad_track" ], "robot" => [ "robot_face" ], "skull" => [ "skeleton" ], "skull_and_crossbones" => [ "skull_crossbones" ], "speaking_head" => [ "speaking_head_in_silhouette" ], "male_detective" => [ "spy", "sleuth_or_spy" ], "thinking" => [ "thinking_face" ], "thumbsdown" => [ "-1" ], "thumbsup" => [ "+1" ], "cloud_with_lightning_and_rain" => [ "thunder_cloud_rain", "thunder_cloud_and_rain" ], "tickets" => [ "admission_tickets" ], "next_track_button" => [ "track_next", "next_track" ], "previous_track_button" => [ "track_previous", "previous_track" ], "unicorn" => [ "unicorn_face" ], "funeral_urn" => [ "urn" ], "sun_behind_large_cloud" => [ "white_sun_cloud", "white_sun_behind_cloud" ], "sun_behind_rain_cloud" => [ "white_sun_rain_cloud", "white_sun_behind_cloud_with_rain" ], "white_sun_small_cloud" => [ "white_sun_small_cloud", "white_sun_with_small_cloud" ], "open_umbrella" => [ "umbrella2" ], "hammer_and_wrench" => [ "tools" ], "face_with_thermometer" => [ "thermometer_face" ], "timer_clock" => [ "timer" ], "keycap_ten" => [ "ten" ], "memo" => [ "pencil" ], "rescue_worker_helmet" => [ "helmet_with_cross", "helmet_with_white_cross" ], "slightly_smiling_face" => [ "slightly_smiling", "slight_smile"], "construction_worker_man" => [ "construction_worker" ], "upside_down_face" => [ "upside_down" ], "money_mouth_face" => [ "money_mouth" ], "nerd_face" => [ "nerd" ], "hugs" => [ "hugging", "hugging_face" ], "roll_eyes" => [ "rolling_eyes", "face_with_rolling_eyes" ], "slightly_frowning_face" => [ "slight_frown" ], "frowning_face" => [ "frowning2", "white_frowning_face" ], "zipper_mouth_face" => [ "zipper_mouth" ], "face_with_head_bandage" => [ "head_bandage" ], "raised_hand_with_fingers_splayed" => [ "hand_splayed" ], "raised_hand" => [ "hand" ], "vulcan_salute" => [ "vulcan", "raised_hand_with_part_between_middle_and_ring_fingers" ], "policeman" => [ "cop" ], "running_man" => [ "runner" ], "walking_man" => [ "walking" ], "bowing_man" => [ "bow" ], "no_good_woman" => [ "no_good" ], "raising_hand_woman" => [ "raising_hand" ], "pouting_woman" => [ "person_with_pouting_face" ], "frowning_woman" => [ "person_frowning" ], "haircut_woman" => [ "haircut" ], "massage_woman" => [ "massage" ], "tshirt" => [ "shirt" ], "biking_man" => [ "bicyclist" ], "mountain_biking_man" => [ "mountain_bicyclist" ], "passenger_ship" => [ "cruise_ship" ], "motor_boat" => [ "motorboat", "boat" ], "flight_arrival" => [ "airplane_arriving" ], "flight_departure" => [ "airplane_departure" ], "small_airplane" => [ "airplane_small" ], "racing_car" => [ "race_car" ], "family_man_woman_boy_boy" => [ "family_man_woman_boys" ], "family_man_woman_girl_girl" => [ "family_man_woman_girls" ], "family_woman_woman_boy" => [ "family_women_boy" ], "family_woman_woman_girl" => [ "family_women_girl" ], "family_woman_woman_girl_boy" => [ "family_women_girl_boy" ], "family_woman_woman_boy_boy" => [ "family_women_boys" ], "family_woman_woman_girl_girl" => [ "family_women_girls" ], "family_man_man_boy" => [ "family_men_boy" ], "family_man_man_girl" => [ "family_men_girl" ], "family_man_man_girl_boy" => [ "family_men_girl_boy" ], "family_man_man_boy_boy" => [ "family_men_boys" ], "family_man_man_girl_girl" => [ "family_men_girls" ], "cloud_with_lightning" => [ "cloud_lightning" ], "tornado" => [ "cloud_tornado", "cloud_with_tornado" ], "cloud_with_rain" => [ "cloud_rain" ], "cloud_with_snow" => [ "cloud_snow" ], "asterisk" => [ "keycap_star" ], "studio_microphone" => [ "microphone2" ], "medal_military" => [ "military_medal" ], "couple_with_heart_woman_woman" => [ "female_couple_with_heart" ], "couple_with_heart_man_man" => [ "male_couple_with_heart" ], "couplekiss_woman_woman" => [ "female_couplekiss" ], "couplekiss_man_man" => [ "male_couplekiss" ], "honeybee" => [ "bee" ], "lion" => [ "lion_face" ], "artificial_satellite" => [ "satellite_orbital" ], "three_button_mouse" => [ "mouse_three_button" ], "hocho" => [ "knife" ], "swimming_man" => [ "swimmer" ], "wind_face" => [ "wind_blowing_face" ], "golfing_man" => [ "golfer" ], "facepunch" => [ "punch" ], "building_construction" => [ "construction_site" ], "family_man_woman_girl_boy" => [ "family" ], "ice_hockey" => [ "hockey" ], "snowman_with_snow" => [ "snowman2" ], "play_or_pause_button" => [ "play_pause" ], "film_projector" => [ "projector" ], "shopping" => [ "shopping_bags" ], "open_book" => [ "book" ], "national_park" => [ "park" ], "world_map" => [ "map" ], "pen" => [ "pen_ballpoint" ], "email" => [ "envelope", "e-mail" ], "phone" => [ "telephone" ], "atom_symbol" => [ "atom" ], "mantelpiece_clock" => [ "clock" ], "camera_flash" => [ "camera_with_flash" ], "film_strip" => [ "film_frames" ], "balance_scale" => [ "scales" ], "surfing_man" => [ "surfer" ], "couplekiss_man_woman" => [ "couplekiss" ], "couple_with_heart_woman_man" => [ "couple_with_heart" ], "clamp" => [ "compression" ], "dancing_women" => [ "dancers" ], "blonde_man" => [ "person_with_blond_hair" ], "sleeping_bed" => [ "sleeping_accommodation" ], "om" => [ "om_symbol" ], "tipping_hand_woman" => [ "information_desk_person" ], "rowing_man" => [ "rowboat" ], "new_moon" => [ "moon" ], "automobile" => [ "car" ], "fleur_de_lis" => [ "fleur-de-lis" ], } EMOJI_GROUPS = [ { "name" => "people", "fullname" => "People", "tabicon" => "grinning", "icons" => [ "slightly_smiling_face", "grinning", "grin", "joy", "rofl", "smiley", "smile", "sweat_smile", "laughing", "innocent", "smiling_imp", "imp", "wink", "blush", "relaxed", "yum", "relieved", "heart_eyes", "sunglasses", "smirk", "neutral_face", "expressionless", "unamused", "sweat", "pensive", "confused", "confounded", "kissing", "kissing_heart", "kissing_smiling_eyes", "kissing_closed_eyes", "stuck_out_tongue", "stuck_out_tongue_winking_eye", "stuck_out_tongue_closed_eyes", "disappointed", "worried", "angry", "rage", "cry", "persevere", "triumph", "disappointed_relieved", "frowning", "anguished", "fearful", "weary", "sleepy", "tired_face", "grimacing", "sob", "open_mouth", "hushed", "cold_sweat", "nauseated_face", "sneezing_face", "scream", "astonished", "flushed", "sleeping", "dizzy_face", "no_mouth", "mask", "smile_cat", "joy_cat", "smiley_cat", "heart_eyes_cat", "smirk_cat", "kissing_cat", "pouting_cat", "crying_cat_face", "scream_cat", "footprints", "bust_in_silhouette", "busts_in_silhouette", "baby", "boy", "girl", "man", "woman", "couple", "two_men_holding_hands", "two_women_holding_hands", "dancing_women", "dancing_men", "man_dancing", "bride_with_veil", "blonde_woman", "blonde_man", "man_with_gua_pi_mao", "woman_with_turban", "man_with_turban", "older_woman", "older_man", "policewoman", "policeman", "female_detective", "male_detective", "woman_farmer", "man_farmer", "woman_cook", "man_cook", "woman_student", "man_student", "woman_singer", "man_singer", "woman_teacher", "man_teacher", "woman_factory_worker", "man_factory_worker", "woman_health_worker", "man_health_worker", "woman_pilot", "man_pilot", "woman_judge", "man_judge", "woman_technologist", "man_technologist", "woman_office_worker", "man_office_worker", "woman_mechanic", "man_mechanic", "woman_scientist", "man_scientist", "woman_artist", "man_artist", "woman_firefighter", "man_firefighter", "woman_astronaut", "man_astronaut", "construction_worker_woman", "construction_worker_man", "princess", "prince", "guardswoman", "guardsman", "clown_face", "cowboy_hat_face", "angel", "santa", "mrs_claus", "ghost", "japanese_ogre", "japanese_goblin", "hankey", "skull", "alien", "space_invader", "bowing_woman", "bowing_man", "woman_facepalming", "man_facepalming", "woman_shrugging", "man_shrugging", "tipping_hand_woman", "tipping_hand_man", "no_good_woman", "no_good_man", "ok_woman", "ok_man", "raising_hand_woman", "raising_hand_man", "pouting_woman", "pouting_man", "frowning_woman", "frowning_man", "massage_woman", "massage_man", "haircut_woman", "haircut_man", "couple_with_heart_woman_woman", "couple_with_heart_woman_man", "couple_with_heart_man_man", "clap", "ear", "eyes", "nose", "lips", "kiss", "tongue", "nail_care", "wave", "thumbsup", "thumbsdown", "point_up", "point_up_2", "point_down", "point_left", "point_right", "ok_hand", "handshake", "v", "crossed_fingers", "call_me_hand", "fist", "fist_left", "fist_right", "raised_hand", "raised_back_of_hand", "raised_hands", "muscle", "open_hands", "pray", "right_anger_bubble", "eye", "frowning_face", "raised_hand_with_fingers_splayed", "face_with_head_bandage", "hugs", "fu", "money_mouth_face", "nerd_face", "drooling_face", "poop", "facepunch", "robot", "roll_eyes", "skull_and_crossbones", "slightly_frowning_face", "speaking_head", "spy", "thinking", "upside_down_face", "lying_face", "funeral_urn", "vulcan_salute", "metal", "wind_face", "writing_hand", "zipper_mouth_face", "male_couple_with_heart", "couplekiss_man_woman", "couplekiss_woman_woman", "couplekiss_man_man", "family_man_woman_girl", "family_man_woman_boy", "family_man_woman_girl_boy", "family_man_woman_girl_girl", "family_man_woman_boy_boy", "family_woman_woman_girl", "family_woman_woman_boy", "family_woman_woman_girl_boy", "family_woman_woman_girl_girl", "family_woman_woman_boy_boy", "family_man_man_girl", "family_man_man_boy", "family_man_man_girl_boy", "family_man_man_girl_girl", "family_man_man_boy_boy", "family_woman_girl", "family_woman_boy", "family_woman_girl_boy", "family_woman_girl_girl", "family_woman_boy_boy", "family_man_girl", "family_man_boy", "family_man_girl_boy", "family_man_girl_girl", "family_man_boy_boy", "selfie", "man_in_tuxedo", "pregnant_woman" ] }, { "name" => "nature", "fullname" => "Nature", "tabicon" => "evergreen_tree", "icons" => [ "seedling", "evergreen_tree", "deciduous_tree", "palm_tree", "cactus", "tulip", "cherry_blossom", "rose", "hibiscus", "sunflower", "fleur_de_lis", "blossom", "bouquet", "ear_of_rice", "herb", "four_leaf_clover", "maple_leaf", "fallen_leaf", "leaves", "mushroom", "chestnut", "rat", "mouse2", "mouse", "hamster", "ox", "water_buffalo", "cow2", "cow", "tiger2", "leopard", "tiger", "rabbit2", "rabbit", "cat2", "cat", "racehorse", "horse", "ram", "sheep", "goat", "rooster", "chicken", "baby_chick", "hatching_chick", "hatched_chick", "bird", "penguin", "elephant", "dromedary_camel", "camel", "boar", "pig2", "pig", "pig_nose", "dog2", "poodle", "dog", "wolf", "bear", "koala", "panda_face", "monkey_face", "see_no_evil", "hear_no_evil", "speak_no_evil", "monkey", "dragon", "dragon_face", "crocodile", "snake", "turtle", "frog", "fox_face", "lion", "gorilla", "duck", "eagle", "owl", "bat", "lizard", "shark", "deer", "rhinoceros", "paw_prints", "whale2", "whale", "dolphin", "octopus", "fish", "squid", "shrimp", "tropical_fish", "blowfish", "shell", "snail", "bug", "ant", "bee", "beetle", "honeybee", "butterfly", "feet", "zap", "fire", "crescent_moon", "sunny", "partly_sunny", "sun_behind_small_cloud", "cloud", "droplet", "sweat_drops", "umbrella", "dash", "snowflake", "star2", "star", "stars", "sunrise_over_mountains", "sunrise", "rainbow", "ocean", "volcano", "milky_way", "mount_fuji", "japan", "globe_with_meridians", "earth_africa", "earth_americas", "earth_asia", "new_moon", "waxing_crescent_moon", "first_quarter_moon", "full_moon", "waning_gibbous_moon", "waxing_gibbous_moon", "last_quarter_moon", "waning_crescent_moon", "new_moon_with_face", "full_moon_with_face", "first_quarter_moon_with_face", "last_quarter_moon_with_face", "sun_with_face", "chipmunk", "cloud_with_lightning", "cloud_with_lightning_and_rain", "cloud_with_rain", "cloud_with_snow", "tornado", "sun_behind_large_cloud", "sun_behind_rain_cloud", "white_sun_small_cloud", "comet", "crab", "dove", "fog", "lion_face", "scorpion", "spider", "spider_web", "turkey", "unicorn", ] }, { "name" => "food", "fullname" => "Food & Drink", "tabicon" => "hamburger", "icons" => [ "tomato", "eggplant", "corn", "sweet_potato", "grapes", "melon", "watermelon", "tangerine", "lemon", "banana", "pineapple", "apple", "green_apple", "pear", "peach", "cherries", "strawberry", "kiwi_fruit", "avocado", "cucumber", "carrot", "potato", "hamburger", "pizza", "meat_on_bone", "poultry_leg", "rice_cracker", "rice_ball", "rice", "curry", "ramen", "spaghetti", "bread", "fries", "dango", "oden", "sushi", "fried_shrimp", "fish_cake", "icecream", "shaved_ice", "ice_cream", "doughnut", "cookie", "chocolate_bar", "candy", "lollipop", "custard", "honey_pot", "cake", "bento", "stew", "egg", "fork_and_knife", "tea", "coffee", "milk_glass", "sake", "wine_glass", "clinking_glasses", "tumbler_glass", "cocktail", "tropical_drink", "beer", "beers", "baby_bottle", "burrito", "champagne", "cheese", "hot_pepper", "hotdog", "taco", "peanuts", "croissant", "baguette_bread", "bacon", "pancakes", "fried_egg", "stuffed_flatbread", "green_salad", "shallow_pan_of_food", ] }, { "name" => "celebration", "fullname" => "Celebration", "tabicon" => "gift", "icons" => [ "ribbon", "gift", "birthday", "jack_o_lantern", "christmas_tree", "tanabata_tree", "bamboo", "rice_scene", "fireworks", "sparkler", "tada", "confetti_ball", "balloon", "dizzy", "sparkles", "boom", "mortar_board", "crown", "dolls", "flags", "wind_chime", "crossed_flags", "izakaya_lantern", "ring", "heart", "broken_heart", "love_letter", "two_hearts", "revolving_hearts", "heartbeat", "heartpulse", "sparkling_heart", "cupid", "gift_heart", "heart_decoration", "purple_heart", "yellow_heart", "green_heart", "blue_heart", "black_heart", "heavy_heart_exclamation", ] }, { "name" => "activity", "fullname" => "Activities", "tabicon" => "soccer", "icons" => [ "running_woman", "running_man", "walking_woman", "walking_man", "dancer", "rowing_woman", "rowing_man", "swimming_woman", "swimming_man", "surfing_woman", "surfing_man", "bath", "snowboarder", "ski", "snowman", "snowman_with_snow", "biking_woman", "biking_man", "mountain_biking_woman", "mountain_biking_man", "horse_racing", "tent", "fishing_pole_and_fish", "soccer", "basketball", "football", "baseball", "tennis", "rugby_football", "golf", "trophy", "running_shirt_with_sash", "checkered_flag", "musical_keyboard", "guitar", "violin", "saxophone", "trumpet", "drum", "musical_note", "notes", "musical_score", "headphones", "microphone", "performing_arts", "ticket", "tophat", "circus_tent", "clapper", "art", "dart", "8ball", "bowling", "slot_machine", "game_die", "video_game", "flower_playing_cards", "black_joker", "mahjong", "carousel_horse", "ferris_wheel", "roller_coaster", "badminton", "ballot_box", "basketball_woman", "basketball_man", "bow_and_arrow", "crossed_swords", "cricket", "ice_hockey", "field_hockey", "golfing_woman", "golfing_man", "ice_skate", "paintbrush", "skier", "stadium", "volleyball", "person_fencing", "goal_net", "boxing_glove", "martial_arts_uniform", "women_wrestling", "men_wrestling", "woman_cartwheeling", "man_cartwheeling", "woman_playing_handball", "man_playing_handball", "woman_playing_water_polo", "man_playing_water_polo", "woman_juggling", "man_juggling", ] }, { "name" => "travel", "fullname" => "Travel & Places", "tabicon" => "airplane", "icons" => [ "train", "mountain_railway", "railway_car", "steam_locomotive", "monorail", "bullettrain_side", "bullettrain_front", "train2", "metro", "light_rail", "station", "tram", "bus", "oncoming_bus", "trolleybus", "minibus", "ambulance", "fire_engine", "police_car", "oncoming_police_car", "rotating_light", "taxi", "oncoming_taxi", "automobile", "oncoming_automobile", "blue_car", "truck", "articulated_lorry", "tractor", "bike", "kick_scooter", "motor_scooter", "busstop", "fuelpump", "construction", "vertical_traffic_light", "traffic_light", "rocket", "helicopter", "airplane", "seat", "anchor", "ship", "speedboat", "canoe", "aerial_tramway", "mountain_cableway", "suspension_railway", "passport_control", "customs", "baggage_claim", "left_luggage", "yen", "euro", "pound", "dollar", "statue_of_liberty", "moyai", "foggy", "tokyo_tower", "fountain", "european_castle", "japanese_castle", "city_sunrise", "city_sunset", "night_with_stars", "bridge_at_night", "house", "house_with_garden", "office", "department_store", "factory", "post_office", "european_post_office", "hospital", "bank", "hotel", "love_hotel", "wedding", "church", "convenience_store", "school", "cn", "de", "es", "fr", "gb", "it", "jp", "kr", "ru", "us", "flight_arrival", "flight_departure", "small_airplane", "beach", "beach_umbrella", "parasol_on_ground", "camping", "city_dusk", "cityscape", "classical_building", "building_construction", "passenger_ship", "desert", "ferry", "hole", "houses", "derelict_house", "desert_island", "kaaba", "world_map", "mosque", "motor_boat", "motorcycle", "motorway", "mountain", "mountain_snow", "national_park", "place_of_worship", "racing_car", "railway_track", "red_car", "sailboat", "shinto_shrine", "sleeping_accommodation", "synagogue", "rainbow_flag", "white_flag", "black_flag", "flag_cn", "flag_de", "flag_es", "flag_fr", "flag_gb", "flag_it", "flag_jp", "flag_kr", "flag_ru", "flag_us", "afghanistan", "aland_islands", "albania", "algeria", "american_samoa", "andorra", "angola", "anguilla", "antarctica", "antigua_barbuda", "argentina", "armenia", "aruba", "australia", "austria", "azerbaijan", "bahamas", "bahrain", "bangladesh", "barbados", "belarus", "belgium", "belize", "benin", "bermuda", "bhutan", "bolivia", "caribbean_netherlands", "bosnia_herzegovina", "botswana", "brazil", "british_indian_ocean_territory", "british_virgin_islands", "brunei", "bulgaria", "burkina_faso", "burundi", "cape_verde", "cambodia", "cameroon", "canada", "canary_islands", "cayman_islands", "central_african_republic", "chad", "chile", "christmas_island", "cocos_islands", "colombia", "comoros", "congo_brazzaville", "congo_kinshasa", "cook_islands", "costa_rica", "croatia", "cuba", "curacao", "cyprus", "czech_republic", "denmark", "djibouti", "dominica", "dominican_republic", "ecuador", "egypt", "el_salvador", "equatorial_guinea", "eritrea", "estonia", "ethiopia", "eu", "falkland_islands", "faroe_islands", "fiji", "finland", "french_guiana", "french_polynesia", "french_southern_territories", "gabon", "gambia", "georgia", "ghana", "gibraltar", "greece", "greenland", "grenada", "guadeloupe", "guam", "guatemala", "guernsey", "guinea", "guinea_bissau", "guyana", "haiti", "honduras", "hong_kong", "hungary", "iceland", "india", "indonesia", "iran", "iraq", "ireland", "isle_of_man", "israel", "cote_divoire", "jamaica", "jersey", "jordan", "kazakhstan", "kenya", "kiribati", "kosovo", "kuwait", "kyrgyzstan", "laos", "latvia", "lebanon", "lesotho", "liberia", "libya", "liechtenstein", "lithuania", "luxembourg", "macau", "macedonia", "madagascar", "malawi", "malaysia", "maldives", "mali", "malta", "marshall_islands", "martinique", "mauritania", "mauritius", "mayotte", "mexico", "micronesia", "moldova", "monaco", "mongolia", "montenegro", "montserrat", "morocco", "mozambique", "myanmar", "namibia", "nauru", "nepal", "netherlands", "new_caledonia", "new_zealand", "nicaragua", "niger", "nigeria", "niue", "norfolk_island", "northern_mariana_islands", "north_korea", "norway", "oman", "pakistan", "palau", "palestinian_territories", "panama", "papua_new_guinea", "paraguay", "peru", "philippines", "pitcairn_islands", "poland", "portugal", "puerto_rico", "qatar", "reunion", "romania", "rwanda", "st_barthelemy", "st_helena", "st_kitts_nevis", "st_lucia", "st_pierre_miquelon", "st_vincent_grenadines", "samoa", "san_marino", "sao_tome_principe", "saudi_arabia", "senegal", "serbia", "seychelles", "sierra_leone", "singapore", "sint_maarten", "slovakia", "slovenia", "solomon_islands", "somalia", "south_africa", "south_georgia_south_sandwich_islands", "south_sudan", "sri_lanka", "sudan", "suriname", "swaziland", "sweden", "switzerland", "syria", "taiwan", "tajikistan", "tanzania", "thailand", "timor_leste", "togo", "tokelau", "tonga", "trinidad_tobago", "tunisia", "tr", "turkmenistan", "turks_caicos_islands", "tuvalu", "uganda", "ukraine", "united_arab_emirates", "uk", "us_virgin_islands", "uruguay", "uzbekistan", "vanuatu", "vatican_city", "venezuela", "vietnam", "wallis_futuna", "western_sahara", "yemen", "zambia", "zimbabwe", ] }, { "name" => "objects", "fullname" => "Objects & Symbols", "tabicon" => "eyeglasses", "icons" => [ "watch", "iphone", "calling", "computer", "alarm_clock", "hourglass_flowing_sand", "hourglass", "camera", "video_camera", "movie_camera", "tv", "radio", "pager", "telephone_receiver", "phone", "fax", "minidisc", "floppy_disk", "cd", "dvd", "vhs", "battery", "electric_plug", "bulb", "flashlight", "satellite", "credit_card", "money_with_wings", "moneybag", "gem", "closed_umbrella", "pouch", "purse", "handbag", "briefcase", "school_satchel", "lipstick", "eyeglasses", "womans_hat", "sandal", "high_heel", "boot", "mans_shoe", "athletic_shoe", "bikini", "dress", "kimono", "womans_clothes", "tshirt", "necktie", "jeans", "door", "shower", "bathtub", "toilet", "barber", "syringe", "pill", "microscope", "telescope", "crystal_ball", "wrench", "hocho", "nut_and_bolt", "hammer", "bomb", "smoking", "gun", "bookmark", "newspaper", "key", "email", "envelope_with_arrow", "incoming_envelope", "email", "inbox_tray", "outbox_tray", "package", "postal_horn", "postbox", "mailbox_closed", "mailbox", "mailbox_with_mail", "mailbox_with_no_mail", "page_facing_up", "page_with_curl", "bookmark_tabs", "chart_with_upwards_trend", "chart_with_downwards_trend", "bar_chart", "date", "calendar", "low_brightness", "high_brightness", "scroll", "clipboard", "open_book", "notebook", "notebook_with_decorative_cover", "ledger", "closed_book", "green_book", "blue_book", "orange_book", "books", "card_index", "link", "paperclip", "pushpin", "scissors", "triangular_ruler", "round_pushpin", "straight_ruler", "triangular_flag_on_post", "file_folder", "open_file_folder", "black_nib", "pencil2", "lock_with_ink_pen", "closed_lock_with_key", "lock", "unlock", "mega", "loudspeaker", "sound", "loud_sound", "speaker", "mute", "zzz", "bell", "no_bell", "thought_balloon", "speech_balloon", "children_crossing", "mag", "mag_right", "no_entry_sign", "no_entry", "stop_sign", "name_badge", "no_pedestrians", "do_not_litter", "no_bicycles", "non-potable_water", "no_mobile_phones", "underage", "accept", "ideograph_advantage", "white_flower", "wilted_flower", "secret", "congratulations", "u5408", "u6e80", "u7981", "u6709", "u7121", "u7533", "u55b6", "u6708", "u5272", "u7a7a", "sa", "koko", "u6307", "chart", "sparkle", "eight_spoked_asterisk", "negative_squared_cross_mark", "white_check_mark", "eight_pointed_black_star", "vibration_mode", "mobile_phone_off", "vs", "a", "b", "ab", "cl", "o2", "sos", "parking", "wc", "cool", "free", "new", "ng", "ok", "up", "atm", "id", "aries", "taurus", "gemini", "cancer", "leo", "virgo", "libra", "scorpius", "sagittarius", "capricorn", "aquarius", "pisces", "ophiuchus", "six_pointed_star", "atom_symbol", "latin_cross", "orthodox_cross", "star_and_crescent", "om", "star_of_david", "wheel_of_dharma", "yin_yang", "menorah", "peace_symbol", "restroom", "mens", "womens", "baby_symbol", "wheelchair", "potable_water", "no_smoking", "put_litter_in_its_place", "arrow_forward", "arrow_backward", "arrow_up_small", "arrow_down_small", "fast_forward", "rewind", "arrow_double_up", "arrow_double_down", "arrow_right", "arrow_left", "arrow_up", "arrow_down", "arrow_upper_right", "arrow_lower_right", "arrow_lower_left", "arrow_upper_left", "arrow_up_down", "left_right_arrow", "arrows_counterclockwise", "arrow_right_hook", "leftwards_arrow_with_hook", "arrow_heading_up", "arrow_heading_down", "twisted_rightwards_arrows", "repeat", "repeat_one", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "keycap_ten", "asterisk", "1234", "hash", "abc", "abcd", "capital_abcd", "information_source", "signal_strength", "cinema", "symbols", "heavy_plus_sign", "heavy_minus_sign", "wavy_dash", "heavy_division_sign", "heavy_multiplication_x", "heavy_check_mark", "arrows_clockwise", "tm", "copyright", "registered", "currency_exchange", "heavy_dollar_sign", "curly_loop", "loop", "part_alternation_mark", "exclamation", "bangbang", "question", "grey_exclamation", "grey_question", "interrobang", "x", "o", "100", "end", "back", "on", "top", "soon", "cyclone", "m", "beginner", "trident", "warning", "hotsprings", "recycle", "anger", "diamond_shape_with_a_dot_inside", "spades", "clubs", "hearts", "diamonds", "ballot_box_with_check", "white_circle", "black_circle", "radio_button", "red_circle", "large_blue_circle", "small_red_triangle", "small_red_triangle_down", "small_orange_diamond", "small_blue_diamond", "large_orange_diamond", "large_blue_diamond", "black_small_square", "white_small_square", "black_large_square", "white_large_square", "black_medium_square", "white_medium_square", "black_medium_small_square", "white_medium_small_square", "black_square_button", "white_square_button", "clock1", "clock2", "clock3", "clock4", "clock5", "clock6", "clock7", "clock8", "clock9", "clock10", "clock11", "clock12", "clock130", "clock230", "clock330", "clock430", "clock530", "clock630", "clock730", "clock830", "clock930", "clock1030", "clock1130", "clock1230", "alembic", "amphora", "biohazard", "bed", "sleeping_bed", "bellhop_bell", "spiral_calendar", "camera_flash", "candle", "card_file_box", "chains", "mantelpiece_clock", "coffin", "clamp", "control_knobs", "couch_and_lamp", "crayon", "dagger", "dark_sunglasses", "desktop_computer", "card_index_dividers", "envelope", "file_cabinet", "film_strip", "plate_with_cutlery", "framed_picture", "gear", "hammer_and_pick", "rescue_worker_helmet", "joystick", "old_key", "keyboard", "knife", "spoon", "label", "level_slider", "business_suit_levitating", "weight_lifting_woman", "weight_lifting_man", "medal_sports", "1st_place_medal", "2nd_place_medal", "3rd_place_medal", "studio_microphone", "medal_military", "computer_mouse", "mouse_three_button", "newspaper_roll", "spiral_notepad", "oil_drum", "paperclips", "pause_button", "record_button", "stop_button", "play_or_pause_button", "next_track_button", "previous_track_button", "pen", "fountain_pen", "memo", "pick", "ping_pong", "popcorn", "prayer_beads", "printer", "film_projector", "radioactive", "reminder_ribbon", "rosette", "artificial_satellite", "balance_scale", "shamrock", "shield", "shopping", "shopping_cart", "stopwatch", "telephone", "thermometer", "face_with_thermometer", "tickets", "timer_clock", "hammer_and_wrench", "trackball", "open_umbrella", "wastebasket", "left_speech_bubble", ] } ] FITZPATRICK_SCALE ||= [ "1f3fb", "1f3fc", "1f3fd", "1f3fe", "1f3ff" ] VARIATION_SELECTOR ||= "fe0f" # Patch content of EMOJI_KEYWORDS_URL EMOJI_KEYWORDS_PATCH ||= { "thumbsup" => { "char" => "👍", "fitzpatrick_scale" => true }, "thumbsdown" => { "char" => "👎", "fitzpatrick_scale" => true }, "asterisk" => { "char" => "*️⃣" }, "dancing_men" => { "char" => "👯‍♂️️" }, "women_wrestling" => { "char" => "🤼‍♀️️", "fitzpatrick_scale" => false }, "men_wrestling" => { "char" => "🤼‍♂️️", "fitzpatrick_scale" => false }, "female_detective" => { "char" => "🕵️‍♀️", "fitzpatrick_scale" => true }, "blonde_woman" => { "fitzpatrick_scale" => true }, "woman_with_turban" => { "fitzpatrick_scale" => true }, "policewoman" => { "fitzpatrick_scale" => true }, "construction_worker_woman" => { "fitzpatrick_scale" => true }, "guardswoman" => { "fitzpatrick_scale" => true }, "woman_health_worker" => { "fitzpatrick_scale" => true }, "man_health_worker" => { "fitzpatrick_scale" => true }, "woman_pilot" => { "fitzpatrick_scale" => true }, "man_pilot" => { "fitzpatrick_scale" => true }, "woman_judge" => { "fitzpatrick_scale" => true }, "man_judge" => { "fitzpatrick_scale" => true }, "running_woman" => { "fitzpatrick_scale" => true }, "walking_woman" => { "fitzpatrick_scale" => true }, "woman_facepalming" => { "fitzpatrick_scale" => true }, "bowing_woman" => { "fitzpatrick_scale" => true }, "man_shrugging" => { "fitzpatrick_scale" => true }, "tipping_hand_man" => { "fitzpatrick_scale" => true }, "no_good_man" => { "fitzpatrick_scale" => true }, "ok_man" => { "fitzpatrick_scale" => true }, "raising_hand_man" => { "fitzpatrick_scale" => true }, "pouting_man" => { "fitzpatrick_scale" => true }, "frowning_man" => { "fitzpatrick_scale" => true }, "haircut_man" => { "fitzpatrick_scale" => true }, "massage_man" => { "fitzpatrick_scale" => true }, "golfing_woman" => { "fitzpatrick_scale" => true }, "rowing_woman" => { "fitzpatrick_scale" => true }, "swimming_woman" => { "fitzpatrick_scale" => true }, "surfing_woman" => { "fitzpatrick_scale" => true }, "basketball_woman" => { "fitzpatrick_scale" => true }, "weight_lifting_woman" => { "fitzpatrick_scale" => true }, "biking_woman" => { "fitzpatrick_scale" => true }, "mountain_biking_woman" => { "fitzpatrick_scale" => true }, "handshake" => { "fitzpatrick_scale" => false }, "dancing_women" => { "fitzpatrick_scale" => false }, "couple" => { "fitzpatrick_scale" => false }, "two_men_holding_hands" => { "fitzpatrick_scale" => false }, "two_women_holding_hands" => { "fitzpatrick_scale" => false }, "couple_with_heart_woman_man" => { "fitzpatrick_scale" => false }, "couplekiss_man_woman" => { "fitzpatrick_scale" => false }, "family_man_woman_boy" => { "fitzpatrick_scale" => false }, "rescue_worker_helmet" => { "fitzpatrick_scale" => false }, "skier" => { "fitzpatrick_scale" => false } } # Exclude keywords from EMOJI_KEYWORDS_URL EMOJI_KEYWORDS_EXCLUDE_LIST ||= [ "+1", "-1" ] # Cell index of each platform in EMOJI_LIST_URL EICI ||= EMOJI_IMAGES_CELLS_INDEX ||= { :windows => 10, :apple => 3, :google => 4, :twitter => 5, :one => 6 } # Replace the platform by another when downloading the image EMOJI_IMAGES_CELLS_INDEX_PATCH ||= { :apple => { "snowboarder" => EICI[:twitter], "snowboarder/2" => EICI[:twitter], "snowboarder/3" => EICI[:twitter], "snowboarder/4" => EICI[:twitter], "snowboarder/5" => EICI[:twitter], "snowboarder/6" => EICI[:twitter], "sleeping_bed" => EICI[:twitter], "sleeping_bed/2" => EICI[:twitter], "sleeping_bed/3" => EICI[:twitter], "sleeping_bed/4" => EICI[:twitter], "sleeping_bed/5" => EICI[:twitter], "sleeping_bed/6" => EICI[:twitter], }, :one => { # dot not use emoji-one rounded flags "afghanistan" => EICI[:twitter], "aland_islands" => EICI[:twitter], "albania" => EICI[:twitter], "algeria" => EICI[:twitter], "american_samoa" => EICI[:twitter], "andorra" => EICI[:twitter], "angola" => EICI[:twitter], "anguilla" => EICI[:twitter], "antarctica" => EICI[:twitter], "antigua_barbuda" => EICI[:twitter], "argentina" => EICI[:twitter], "armenia" => EICI[:twitter], "aruba" => EICI[:twitter], "australia" => EICI[:twitter], "austria" => EICI[:twitter], "azerbaijan" => EICI[:twitter], "bahamas" => EICI[:twitter], "bahrain" => EICI[:twitter], "bangladesh" => EICI[:twitter], "barbados" => EICI[:twitter], "belarus" => EICI[:twitter], "belgium" => EICI[:twitter], "belize" => EICI[:twitter], "benin" => EICI[:twitter], "bermuda" => EICI[:twitter], "bhutan" => EICI[:twitter], "bolivia" => EICI[:twitter], "caribbean_netherlands" => EICI[:twitter], "bosnia_herzegovina" => EICI[:twitter], "botswana" => EICI[:twitter], "brazil" => EICI[:twitter], "british_indian_ocean_territory" => EICI[:twitter], "british_virgin_islands" => EICI[:twitter], "brunei" => EICI[:twitter], "bulgaria" => EICI[:twitter], "burkina_faso" => EICI[:twitter], "burundi" => EICI[:twitter], "cape_verde" => EICI[:twitter], "cambodia" => EICI[:twitter], "cameroon" => EICI[:twitter], "canada" => EICI[:twitter], "canary_islands" => EICI[:twitter], "cayman_islands" => EICI[:twitter], "central_african_republic" => EICI[:twitter], "chad" => EICI[:twitter], "chile" => EICI[:twitter], "cn" => EICI[:twitter], "christmas_island" => EICI[:twitter], "cocos_islands" => EICI[:twitter], "colombia" => EICI[:twitter], "comoros" => EICI[:twitter], "congo_brazzaville" => EICI[:twitter], "congo_kinshasa" => EICI[:twitter], "cook_islands" => EICI[:twitter], "costa_rica" => EICI[:twitter], "croatia" => EICI[:twitter], "cuba" => EICI[:twitter], "curacao" => EICI[:twitter], "cyprus" => EICI[:twitter], "czech_republic" => EICI[:twitter], "denmark" => EICI[:twitter], "djibouti" => EICI[:twitter], "dominica" => EICI[:twitter], "dominican_republic" => EICI[:twitter], "ecuador" => EICI[:twitter], "egypt" => EICI[:twitter], "el_salvador" => EICI[:twitter], "equatorial_guinea" => EICI[:twitter], "eritrea" => EICI[:twitter], "estonia" => EICI[:twitter], "ethiopia" => EICI[:twitter], "eu" => EICI[:twitter], "falkland_islands" => EICI[:twitter], "faroe_islands" => EICI[:twitter], "fiji" => EICI[:twitter], "finland" => EICI[:twitter], "fr" => EICI[:twitter], "french_guiana" => EICI[:twitter], "french_polynesia" => EICI[:twitter], "french_southern_territories" => EICI[:twitter], "gabon" => EICI[:twitter], "gambia" => EICI[:twitter], "georgia" => EICI[:twitter], "de" => EICI[:twitter], "ghana" => EICI[:twitter], "gibraltar" => EICI[:twitter], "greece" => EICI[:twitter], "greenland" => EICI[:twitter], "grenada" => EICI[:twitter], "guadeloupe" => EICI[:twitter], "guam" => EICI[:twitter], "guatemala" => EICI[:twitter], "guernsey" => EICI[:twitter], "guinea" => EICI[:twitter], "guinea_bissau" => EICI[:twitter], "guyana" => EICI[:twitter], "haiti" => EICI[:twitter], "honduras" => EICI[:twitter], "hong_kong" => EICI[:twitter], "hungary" => EICI[:twitter], "iceland" => EICI[:twitter], "india" => EICI[:twitter], "indonesia" => EICI[:twitter], "iran" => EICI[:twitter], "iraq" => EICI[:twitter], "ireland" => EICI[:twitter], "isle_of_man" => EICI[:twitter], "israel" => EICI[:twitter], "it" => EICI[:twitter], "cote_divoire" => EICI[:twitter], "jamaica" => EICI[:twitter], "jp" => EICI[:twitter], "jersey" => EICI[:twitter], "jordan" => EICI[:twitter], "kazakhstan" => EICI[:twitter], "kenya" => EICI[:twitter], "kiribati" => EICI[:twitter], "kosovo" => EICI[:twitter], "kuwait" => EICI[:twitter], "kyrgyzstan" => EICI[:twitter], "laos" => EICI[:twitter], "latvia" => EICI[:twitter], "lebanon" => EICI[:twitter], "lesotho" => EICI[:twitter], "liberia" => EICI[:twitter], "libya" => EICI[:twitter], "liechtenstein" => EICI[:twitter], "lithuania" => EICI[:twitter], "luxembourg" => EICI[:twitter], "macau" => EICI[:twitter], "macedonia" => EICI[:twitter], "madagascar" => EICI[:twitter], "malawi" => EICI[:twitter], "malaysia" => EICI[:twitter], "maldives" => EICI[:twitter], "mali" => EICI[:twitter], "malta" => EICI[:twitter], "marshall_islands" => EICI[:twitter], "martinique" => EICI[:twitter], "mauritania" => EICI[:twitter], "mauritius" => EICI[:twitter], "mayotte" => EICI[:twitter], "mexico" => EICI[:twitter], "micronesia" => EICI[:twitter], "moldova" => EICI[:twitter], "monaco" => EICI[:twitter], "mongolia" => EICI[:twitter], "montenegro" => EICI[:twitter], "montserrat" => EICI[:twitter], "morocco" => EICI[:twitter], "mozambique" => EICI[:twitter], "myanmar" => EICI[:twitter], "namibia" => EICI[:twitter], "nauru" => EICI[:twitter], "nepal" => EICI[:twitter], "netherlands" => EICI[:twitter], "new_caledonia" => EICI[:twitter], "new_zealand" => EICI[:twitter], "nicaragua" => EICI[:twitter], "niger" => EICI[:twitter], "nigeria" => EICI[:twitter], "niue" => EICI[:twitter], "norfolk_island" => EICI[:twitter], "northern_mariana_islands" => EICI[:twitter], "north_korea" => EICI[:twitter], "norway" => EICI[:twitter], "oman" => EICI[:twitter], "pakistan" => EICI[:twitter], "palau" => EICI[:twitter], "palestinian_territories" => EICI[:twitter], "panama" => EICI[:twitter], "papua_new_guinea" => EICI[:twitter], "paraguay" => EICI[:twitter], "peru" => EICI[:twitter], "philippines" => EICI[:twitter], "pitcairn_islands" => EICI[:twitter], "poland" => EICI[:twitter], "portugal" => EICI[:twitter], "puerto_rico" => EICI[:twitter], "qatar" => EICI[:twitter], "reunion" => EICI[:twitter], "romania" => EICI[:twitter], "ru" => EICI[:twitter], "rwanda" => EICI[:twitter], "st_barthelemy" => EICI[:twitter], "st_helena" => EICI[:twitter], "st_kitts_nevis" => EICI[:twitter], "st_lucia" => EICI[:twitter], "st_pierre_miquelon" => EICI[:twitter], "st_vincent_grenadines" => EICI[:twitter], "samoa" => EICI[:twitter], "san_marino" => EICI[:twitter], "sao_tome_principe" => EICI[:twitter], "saudi_arabia" => EICI[:twitter], "senegal" => EICI[:twitter], "serbia" => EICI[:twitter], "seychelles" => EICI[:twitter], "sierra_leone" => EICI[:twitter], "singapore" => EICI[:twitter], "sint_maarten" => EICI[:twitter], "slovakia" => EICI[:twitter], "slovenia" => EICI[:twitter], "solomon_islands" => EICI[:twitter], "somalia" => EICI[:twitter], "south_africa" => EICI[:twitter], "south_georgia_south_sandwich_islands" => EICI[:twitter], "kr" => EICI[:twitter], "south_sudan" => EICI[:twitter], "es" => EICI[:twitter], "sri_lanka" => EICI[:twitter], "sudan" => EICI[:twitter], "suriname" => EICI[:twitter], "swaziland" => EICI[:twitter], "sweden" => EICI[:twitter], "switzerland" => EICI[:twitter], "syria" => EICI[:twitter], "taiwan" => EICI[:twitter], "tajikistan" => EICI[:twitter], "tanzania" => EICI[:twitter], "thailand" => EICI[:twitter], "timor_leste" => EICI[:twitter], "togo" => EICI[:twitter], "tokelau" => EICI[:twitter], "tonga" => EICI[:twitter], "trinidad_tobago" => EICI[:twitter], "tunisia" => EICI[:twitter], "tr" => EICI[:twitter], "turkmenistan" => EICI[:twitter], "turks_caicos_islands" => EICI[:twitter], "tuvalu" => EICI[:twitter], "uganda" => EICI[:twitter], "ukraine" => EICI[:twitter], "united_arab_emirates" => EICI[:twitter], "uk" => EICI[:twitter], "us" => EICI[:twitter], "us_virgin_islands" => EICI[:twitter], "uruguay" => EICI[:twitter], "uzbekistan" => EICI[:twitter], "vanuatu" => EICI[:twitter], "vatican_city" => EICI[:twitter], "venezuela" => EICI[:twitter], "vietnam" => EICI[:twitter], "wallis_futuna" => EICI[:twitter], "western_sahara" => EICI[:twitter], "yemen" => EICI[:twitter], "zambia" => EICI[:twitter], "zimbabwe" => EICI[:twitter], }, :windows => { "hash" => EICI[:apple], "zero" => EICI[:apple], "one" => EICI[:apple], "two" => EICI[:apple], "three" => EICI[:apple], "four" => EICI[:apple], "five" => EICI[:apple], "six" => EICI[:apple], "seven" => EICI[:apple], "eight" => EICI[:apple], "nine" => EICI[:apple], "asterisk" => EICI[:apple], "afghanistan" => EICI[:twitter], "aland_islands" => EICI[:twitter], "albania" => EICI[:twitter], "algeria" => EICI[:twitter], "american_samoa" => EICI[:twitter], "andorra" => EICI[:twitter], "angola" => EICI[:twitter], "anguilla" => EICI[:twitter], "antarctica" => EICI[:twitter], "antigua_barbuda" => EICI[:twitter], "argentina" => EICI[:twitter], "armenia" => EICI[:twitter], "aruba" => EICI[:twitter], "australia" => EICI[:twitter], "austria" => EICI[:twitter], "azerbaijan" => EICI[:twitter], "bahamas" => EICI[:twitter], "bahrain" => EICI[:twitter], "bangladesh" => EICI[:twitter], "barbados" => EICI[:twitter], "belarus" => EICI[:twitter], "belgium" => EICI[:twitter], "belize" => EICI[:twitter], "benin" => EICI[:twitter], "bermuda" => EICI[:twitter], "bhutan" => EICI[:twitter], "bolivia" => EICI[:twitter], "caribbean_netherlands" => EICI[:twitter], "bosnia_herzegovina" => EICI[:twitter], "botswana" => EICI[:twitter], "brazil" => EICI[:twitter], "british_indian_ocean_territory" => EICI[:twitter], "british_virgin_islands" => EICI[:twitter], "brunei" => EICI[:twitter], "bulgaria" => EICI[:twitter], "burkina_faso" => EICI[:twitter], "burundi" => EICI[:twitter], "cape_verde" => EICI[:twitter], "cambodia" => EICI[:twitter], "cameroon" => EICI[:twitter], "canada" => EICI[:twitter], "canary_islands" => EICI[:twitter], "cayman_islands" => EICI[:twitter], "central_african_republic" => EICI[:twitter], "chad" => EICI[:twitter], "chile" => EICI[:twitter], "cn" => EICI[:twitter], "christmas_island" => EICI[:twitter], "cocos_islands" => EICI[:twitter], "colombia" => EICI[:twitter], "comoros" => EICI[:twitter], "congo_brazzaville" => EICI[:twitter], "congo_kinshasa" => EICI[:twitter], "cook_islands" => EICI[:twitter], "costa_rica" => EICI[:twitter], "croatia" => EICI[:twitter], "cuba" => EICI[:twitter], "curacao" => EICI[:twitter], "cyprus" => EICI[:twitter], "czech_republic" => EICI[:twitter], "denmark" => EICI[:twitter], "djibouti" => EICI[:twitter], "dominica" => EICI[:twitter], "dominican_republic" => EICI[:twitter], "ecuador" => EICI[:twitter], "egypt" => EICI[:twitter], "el_salvador" => EICI[:twitter], "equatorial_guinea" => EICI[:twitter], "eritrea" => EICI[:twitter], "estonia" => EICI[:twitter], "ethiopia" => EICI[:twitter], "eu" => EICI[:twitter], "falkland_islands" => EICI[:twitter], "faroe_islands" => EICI[:twitter], "fiji" => EICI[:twitter], "finland" => EICI[:twitter], "fr" => EICI[:twitter], "french_guiana" => EICI[:twitter], "french_polynesia" => EICI[:twitter], "french_southern_territories" => EICI[:twitter], "gabon" => EICI[:twitter], "gambia" => EICI[:twitter], "georgia" => EICI[:twitter], "de" => EICI[:twitter], "ghana" => EICI[:twitter], "gibraltar" => EICI[:twitter], "greece" => EICI[:twitter], "greenland" => EICI[:twitter], "grenada" => EICI[:twitter], "guadeloupe" => EICI[:twitter], "guam" => EICI[:twitter], "guatemala" => EICI[:twitter], "guernsey" => EICI[:twitter], "guinea" => EICI[:twitter], "guinea_bissau" => EICI[:twitter], "guyana" => EICI[:twitter], "haiti" => EICI[:twitter], "honduras" => EICI[:twitter], "hong_kong" => EICI[:twitter], "hungary" => EICI[:twitter], "iceland" => EICI[:twitter], "india" => EICI[:twitter], "indonesia" => EICI[:twitter], "iran" => EICI[:twitter], "iraq" => EICI[:twitter], "ireland" => EICI[:twitter], "isle_of_man" => EICI[:twitter], "israel" => EICI[:twitter], "it" => EICI[:twitter], "cote_divoire" => EICI[:twitter], "jamaica" => EICI[:twitter], "jp" => EICI[:twitter], "jersey" => EICI[:twitter], "jordan" => EICI[:twitter], "kazakhstan" => EICI[:twitter], "kenya" => EICI[:twitter], "kiribati" => EICI[:twitter], "kosovo" => EICI[:twitter], "kuwait" => EICI[:twitter], "kyrgyzstan" => EICI[:twitter], "laos" => EICI[:twitter], "latvia" => EICI[:twitter], "lebanon" => EICI[:twitter], "lesotho" => EICI[:twitter], "liberia" => EICI[:twitter], "libya" => EICI[:twitter], "liechtenstein" => EICI[:twitter], "lithuania" => EICI[:twitter], "luxembourg" => EICI[:twitter], "macau" => EICI[:twitter], "macedonia" => EICI[:twitter], "madagascar" => EICI[:twitter], "malawi" => EICI[:twitter], "malaysia" => EICI[:twitter], "maldives" => EICI[:twitter], "mali" => EICI[:twitter], "malta" => EICI[:twitter], "marshall_islands" => EICI[:twitter], "martinique" => EICI[:twitter], "mauritania" => EICI[:twitter], "mauritius" => EICI[:twitter], "mayotte" => EICI[:twitter], "mexico" => EICI[:twitter], "micronesia" => EICI[:twitter], "moldova" => EICI[:twitter], "monaco" => EICI[:twitter], "mongolia" => EICI[:twitter], "montenegro" => EICI[:twitter], "montserrat" => EICI[:twitter], "morocco" => EICI[:twitter], "mozambique" => EICI[:twitter], "myanmar" => EICI[:twitter], "namibia" => EICI[:twitter], "nauru" => EICI[:twitter], "nepal" => EICI[:twitter], "netherlands" => EICI[:twitter], "new_caledonia" => EICI[:twitter], "new_zealand" => EICI[:twitter], "nicaragua" => EICI[:twitter], "niger" => EICI[:twitter], "nigeria" => EICI[:twitter], "niue" => EICI[:twitter], "norfolk_island" => EICI[:twitter], "northern_mariana_islands" => EICI[:twitter], "north_korea" => EICI[:twitter], "norway" => EICI[:twitter], "oman" => EICI[:twitter], "pakistan" => EICI[:twitter], "palau" => EICI[:twitter], "palestinian_territories" => EICI[:twitter], "panama" => EICI[:twitter], "papua_new_guinea" => EICI[:twitter], "paraguay" => EICI[:twitter], "peru" => EICI[:twitter], "philippines" => EICI[:twitter], "pitcairn_islands" => EICI[:twitter], "poland" => EICI[:twitter], "portugal" => EICI[:twitter], "puerto_rico" => EICI[:twitter], "qatar" => EICI[:twitter], "reunion" => EICI[:twitter], "romania" => EICI[:twitter], "ru" => EICI[:twitter], "rwanda" => EICI[:twitter], "st_barthelemy" => EICI[:twitter], "st_helena" => EICI[:twitter], "st_kitts_nevis" => EICI[:twitter], "st_lucia" => EICI[:twitter], "st_pierre_miquelon" => EICI[:twitter], "st_vincent_grenadines" => EICI[:twitter], "samoa" => EICI[:twitter], "san_marino" => EICI[:twitter], "sao_tome_principe" => EICI[:twitter], "saudi_arabia" => EICI[:twitter], "senegal" => EICI[:twitter], "serbia" => EICI[:twitter], "seychelles" => EICI[:twitter], "sierra_leone" => EICI[:twitter], "singapore" => EICI[:twitter], "sint_maarten" => EICI[:twitter], "slovakia" => EICI[:twitter], "slovenia" => EICI[:twitter], "solomon_islands" => EICI[:twitter], "somalia" => EICI[:twitter], "south_africa" => EICI[:twitter], "south_georgia_south_sandwich_islands" => EICI[:twitter], "kr" => EICI[:twitter], "south_sudan" => EICI[:twitter], "es" => EICI[:twitter], "sri_lanka" => EICI[:twitter], "sudan" => EICI[:twitter], "suriname" => EICI[:twitter], "swaziland" => EICI[:twitter], "sweden" => EICI[:twitter], "switzerland" => EICI[:twitter], "syria" => EICI[:twitter], "taiwan" => EICI[:twitter], "tajikistan" => EICI[:twitter], "tanzania" => EICI[:twitter], "thailand" => EICI[:twitter], "timor_leste" => EICI[:twitter], "togo" => EICI[:twitter], "tokelau" => EICI[:twitter], "tonga" => EICI[:twitter], "trinidad_tobago" => EICI[:twitter], "tunisia" => EICI[:twitter], "tr" => EICI[:twitter], "turkmenistan" => EICI[:twitter], "turks_caicos_islands" => EICI[:twitter], "tuvalu" => EICI[:twitter], "uganda" => EICI[:twitter], "ukraine" => EICI[:twitter], "united_arab_emirates" => EICI[:twitter], "uk" => EICI[:twitter], "us" => EICI[:twitter], "us_virgin_islands" => EICI[:twitter], "uruguay" => EICI[:twitter], "uzbekistan" => EICI[:twitter], "vanuatu" => EICI[:twitter], "vatican_city" => EICI[:twitter], "venezuela" => EICI[:twitter], "vietnam" => EICI[:twitter], "wallis_futuna" => EICI[:twitter], "western_sahara" => EICI[:twitter], "yemen" => EICI[:twitter], "zambia" => EICI[:twitter], "zimbabwe" => EICI[:twitter], } } PLATFORM_STYLES ||= { :apple => "apple", :google => "google", :twitter => "twitter", :one => "emoji_one", :windows => "win10" } desc "update emoji images" task "emoji:update" do emojis = build_emojis_list(EMOJI_KEYWORDS_URL) images = build_images_list(EMOJI_LIST_URL, emojis) emojis.each { |code, emoji| emoji[:images] = ( images[code] || {} ) } write_emojis(emojis) write_db_json(emojis) write_groups_js_es6(emojis) puts "\r\n" $debugging_output.each { |debug| puts debug } end desc "test the emoji generation script" task "emoji:test" do ENV['EMOJI_TEST'] = "1" require "active_support/test_case" class TestEmojiUpdate < MiniTest::Test def setup Rake::Task["emoji:update"].invoke end def image_path(style, name) File.join("public", "images", "emoji", style, "#{name}.png") end def test_code_to_emoji assert_equal "😎", code_to_emoji("1f60e") end def test_codepoints_to_code assert_equal "1f6b5_200d_2640", codepoints_to_code([128693, 8205, 9792, 65039], false) end def test_codepoints_to_code_with_scale assert_equal "1f6b5_200d_2640_fe0f", codepoints_to_code([128693, 8205, 9792, 65039], true) end def test_groups_js_es6_creation assert File.exists?(EMOJI_GROUPS_PATH) assert File.size?(EMOJI_GROUPS_PATH) end def test_db_json_creation assert File.exists?(EMOJI_DB_PATH) assert File.size?(EMOJI_DB_PATH) end def test_alias_creation original_image = image_path("apple", "right_anger_bubble") alias_image = image_path("apple", "anger_right") assert_equal File.size(original_image), File.size(alias_image) end def test_cell_index_patch original_image = image_path("apple", "snowboarder") alias_image = image_path("twitter", "snowboarder") assert_equal File.size(original_image), File.size(alias_image) end def test_scales original_image = image_path("apple", "blonde_woman") assert File.exists?(original_image) assert File.size?(original_image) (2..6).each do |scale| image = image_path("apple", "blonde_woman/#{scale}") assert File.exists?(image) assert File.size?(image) end end def test_generate_emoji_scales actual = generate_emoji_scales("sleeping_bed", "1f6cc") expected = { "1f6cc_1f3fb" => { :name => "sleeping_bed/2", :fitzpatrick_scale => false }, "1f6cc_1f3fc" => { :name => "sleeping_bed/3", :fitzpatrick_scale => false }, "1f6cc_1f3fd" => { :name => "sleeping_bed/4", :fitzpatrick_scale => false }, "1f6cc_1f3fe" => { :name => "sleeping_bed/5", :fitzpatrick_scale => false }, "1f6cc_1f3ff" => { :name => "sleeping_bed/6", :fitzpatrick_scale => false } } assert_equal expected, actual end end reporter = Minitest::SummaryReporter.new TestEmojiUpdate.run(reporter) puts reporter.to_s end def write_emojis(emojis) check_pngout path = "#{EMOJI_IMAGES_PATH}/**/*" confirm_overwrite(path) puts "Cleaning emoji folder..." FileUtils.rm_rf(Dir.glob(path)) puts "Writing emojis to disk..." emojis.each do |code, emoji| images = emoji[:images] if images.values.all? { |image| !image.nil? } PLATFORM_STYLES.each do |platform, style| style_path = File.join(EMOJI_IMAGES_PATH, style) image_path = File.join(style_path, "#{emoji[:name]}.png") FileUtils.mkdir_p(File.expand_path("..", image_path)) image = images[platform] write_emoji(image_path, image) if aliases = EMOJI_ALIASES[emoji[:name]] aliases.each do |alias_name| alias_image_path = File.join(style_path, "#{alias_name}.png") write_emoji(alias_image_path, image) end end end else platforms = images.select { |_, v| v.nil? }.keys.join(',') $debugging_output << "[!] Skipping `#{emoji[:name]} #{code_to_emoji(code)}`, undefined platforms: #{platforms}" end end puts "\r\n" end def build_emojis_list(url) puts "Downloading remote emoji list..." list = open(url).read emojis = {} keywords = JSON.parse(list).deep_merge(EMOJI_KEYWORDS_PATCH) EMOJI_KEYWORDS_EXCLUDE_LIST.each { |x| keywords.delete(x) } keywords.keys.each do |name| keyword = keywords[name] next unless char = keyword["char"].presence code = codepoints_to_code(char.codepoints, keyword["fitzpatrick_scale"]) emojis[code] ||= { :name => name, :fitzpatrick_scale => keyword["fitzpatrick_scale"] } if keyword["fitzpatrick_scale"] emojis.merge!(generate_emoji_scales(name, code)) end end emojis end def build_images_list(url, emojis) puts "Downloading remote emoji images list..." list = open(url).read puts "Parsing remote emoji images list..." images = {} doc = Nokogiri::HTML(list) table = doc.css("table")[0] table.css("tr").each do |row| cells = row.css("td") # skip header and section rows next if cells.size != 16 code = cells[1].at_css("a")["name"] cell_to_img = lambda { |cell| return unless img = cell.at_css("img") Base64.decode64(img["src"][/base64,(.+)$/, 1]) } images[code] = {} PLATFORM_STYLES.keys.each do |platform| default_cell_index = EMOJI_IMAGES_CELLS_INDEX[platform] if emoji = emojis[code] name = emoji[:name] patch_index = EMOJI_IMAGES_CELLS_INDEX_PATCH.fetch(platform, {})[name] if patch_index && cell_to_img.call(cells[default_cell_index]) $debugging_output << "[!] Found existing image `#{name}` for platform: #{platform}, might want to remove the patch." end end index = patch_index || default_cell_index images[code][platform] = cell_to_img.call(cells[index]) end putc "." end puts "\r\n" images end def write_db_json(emojis) puts "Writing #{EMOJI_DB_PATH}..." confirm_overwrite(EMOJI_DB_PATH) FileUtils.mkdir_p(File.expand_path("..", EMOJI_DB_PATH)) # skin tones variations of emojis shouldn’t appear in autocomplete emojis_without_tones = emojis .select { |code, _| !FITZPATRICK_SCALE.any? {|scale| code[scale] } } .keys .map { |code| { "code" => code.tr("_", "-"), "name" => emojis[code][:name] } } emoji_with_tones = emojis .select { |code, emoji| emoji[:fitzpatrick_scale] } .keys .map { |code| emojis[code][:name] } db = { "emojis" => emojis_without_tones, "tonableEmojis" => emoji_with_tones, "aliases" => EMOJI_ALIASES } File.write(EMOJI_DB_PATH, JSON.pretty_generate(db)) end def write_groups_js_es6(emojis) puts "Writing #{EMOJI_GROUPS_PATH}..." confirm_overwrite(EMOJI_GROUPS_PATH) check_groups(emojis) template = <