# frozen_string_literal: true module SvgSprite SVG_ICONS ||= Set.new( %w[ address-book align-left anchor angle-down angle-right angle-up angles-down angles-left angles-right angles-up arrow-down arrow-left arrow-right arrow-rotate-left arrow-rotate-right arrow-up arrows-rotate asterisk at backward backward-fast backward-step ban bars bars-staggered bed bell bell-slash bold book book-open-reader bookmark bookmark-delete box-archive briefcase bullseye calendar-days caret-down caret-left caret-right caret-up certificate chart-bar chart-pie check chevron-down chevron-left chevron-right chevron-up circle circle-check circle-chevron-down circle-exclamation circle-half-stroke circle-info circle-minus circle-plus circle-question circle-xmark clock-rotate-left cloud-arrow-up code comment compress copy crosshairs cube desktop diagram-project discourse-amazon discourse-bell-exclamation discourse-bell-one discourse-bell-slash discourse-bookmark-clock discourse-chevron-collapse discourse-chevron-expand discourse-compress discourse-dnd discourse-emojis discourse-expand discourse-other-tab discourse-sidebar discourse-sparkles discourse-threads download earth-americas ellipsis ellipsis-vertical envelope eye fab-android fab-apple fab-chrome fab-discord fab-discourse fab-facebook fab-facebook-square fab-github fab-instagram fab-linkedin-in fab-linux fab-threads fab-threads-square fab-twitter fab-twitter-square fab-x-twitter fab-wikipedia-w fab-windows far-bell far-bell-slash far-calendar-plus far-chart-bar far-circle far-circle-dot far-clipboard far-clock far-comment far-comments far-copyright far-envelope far-eye far-eye-slash far-face-frown far-face-meh far-face-smile far-file-lines far-heart far-image far-moon far-pen-to-square far-rectangle-list far-square far-square-check far-star far-sun far-thumbs-down far-thumbs-up far-trash-can file file-lines filter flag folder folder-open forward forward-fast forward-step gavel gear gift globe grip-lines hand-point-right handshake-angle heart hourglass-start house id-card image images inbox italic key keyboard language layer-group left-right link link-slash list list-check list-ol list-ul location-dot lock magnifying-glass magnifying-glass-minus magnifying-glass-plus microphone-slash minus mobile-screen-button moon paintbrush palette paper-plane pause pencil play plug plus power-off puzzle-piece question quote-left quote-right reply right-from-bracket right-left right-to-bracket robot rocket rotate scroll share shield-halved shuffle signal sliders spinner square-check square-envelope square-full square-plus star table table-cells table-columns tag tags temperature-three-quarters thumbs-down thumbs-up thumbtack tippy-rounded-arrow toggle-off toggle-on trash-can triangle-exclamation truck-medical unlock unlock-keyhole up-down up-right-from-square upload user user-gear user-group user-pen user-plus user-secret user-shield user-xmark users wand-magic wrench xmark ], ) FA_4_ICON_MAP = { "far fa-" => "far-", "fab fa-" => "fab-", "fas fa-" => "", "fa-" => "" } CORE_SVG_SPRITES = Dir.glob("#{Rails.root}/vendor/assets/svg-icons/**/*.svg") THEME_SPRITE_VAR_NAME = "icons-sprite" MAX_THEME_SPRITE_SIZE = 1024.kilobytes def self.preload settings_icons group_icons badge_icons end def self.symbols_for(svg_filename, sprite, strict:) if strict Nokogiri.XML(sprite) { |config| config.options = Nokogiri::XML::ParseOptions::NOBLANKS } else Nokogiri.XML(sprite) end.css("symbol") .filter_map do |sym| icon_id = prepare_symbol(sym, svg_filename) if icon_id.present? sym.attributes["id"].value = icon_id sym.css("title").each(&:remove) [icon_id, sym.to_xml] end end .to_h end def self.core_svgs @core_svgs ||= CORE_SVG_SPRITES.reduce({}) do |symbols, path| symbols.merge!(symbols_for(File.basename(path, ".svg"), File.read(path), strict: true)) end end # Just used in tests def self.clear_plugin_svg_sprite_cache! @plugin_svgs = nil end def self.plugin_svgs @plugin_svgs ||= begin plugin_paths = [] Discourse .plugins .map { |plugin| File.dirname(plugin.path) } .each { |path| plugin_paths << "#{path}/svg-icons/*.svg" } custom_sprite_paths = Dir.glob(plugin_paths) custom_sprite_paths.reduce({}) do |symbols, path| symbols.merge!(symbols_for(File.basename(path, ".svg"), File.read(path), strict: true)) end end end def self.theme_svgs(theme_id) if theme_id.present? cache .defer_get_set_bulk( Theme.transform_ids(theme_id), lambda { |theme_id| "theme_svg_sprites_#{theme_id}" }, ) do |theme_ids| theme_field_uploads = ThemeField.where( type_id: ThemeField.types[:theme_upload_var], name: THEME_SPRITE_VAR_NAME, theme_id: theme_ids, ).pluck(:upload_id) theme_sprites = ThemeSvgSprite.where(theme_id: theme_ids).pluck(:theme_id, :upload_id, :sprite) missing_sprites = (theme_field_uploads - theme_sprites.map(&:second)) if missing_sprites.present? Rails.logger.warn( "Missing ThemeSvgSprites for theme #{theme_id}, uploads #{missing_sprites.join(", ")}", ) end theme_sprites .map do |(theme_id, upload_id, sprite)| begin [theme_id, symbols_for("theme_#{theme_id}_#{upload_id}.svg", sprite, strict: false)] rescue => e Rails.logger.warn( "Bad XML in custom sprite in theme with ID=#{theme_id}. Error info: #{e.inspect}", ) end end .compact .to_h .values_at(*theme_ids) end .values .compact .reduce({}) { |a, b| a.merge!(b) } else {} end end def self.custom_svgs(theme_id) plugin_svgs.merge(theme_svgs(theme_id)) end def self.all_icons(theme_id = nil) get_set_cache("icons_#{Theme.transform_ids(theme_id).join(",")}") do Set .new() .merge(settings_icons) .merge(plugin_icons) .merge(badge_icons) .merge(group_icons) .merge(theme_icons(theme_id)) .merge(custom_icons(theme_id)) .delete_if { |i| i.blank? || i.include?("/") } .map! { |i| process(i.dup) } .merge(SVG_ICONS) .sort end end def self.version(theme_id = nil) get_set_cache("version_#{Theme.transform_ids(theme_id).join(",")}") do Digest::SHA1.hexdigest(bundle(theme_id)) end end def self.path(theme_id = nil) "/svg-sprite/#{Discourse.current_hostname}/svg-#{theme_id}-#{version(theme_id)}.js" end def self.expire_cache cache&.clear end def self.svgs_for(theme_id) svgs = core_svgs svgs = svgs.merge(custom_svgs(theme_id)) if theme_id.present? svgs end def self.bundle(theme_id = nil) icons = all_icons(theme_id) svg_subset = "" \ " " \ "".dup svg_subset << core_svgs.slice(*icons).values.join svg_subset << custom_svgs(theme_id).values.join svg_subset << "" end def self.search(searched_icon) searched_icon = process(searched_icon.dup) svgs_for(SiteSetting.default_theme_id)[searched_icon] || false end def self.icon_picker_search(keyword, only_available = false) symbols = svgs_for(SiteSetting.default_theme_id) symbols.slice!(*all_icons(SiteSetting.default_theme_id)) if only_available symbols.reject! { |icon_id, _sym| !icon_id.include?(keyword) } if keyword.present? symbols.sort_by(&:first).map { |id, symbol| { id:, symbol: } } end # For use in no_ember .html.erb layouts def self.raw_svg(name) get_set_cache("raw_svg_#{name}") do symbol = search(name) break "" unless symbol symbol = Nokogiri.XML(symbol).children.first symbol.name = "svg" <<~HTML HTML end.html_safe end def self.theme_sprite_variable_name THEME_SPRITE_VAR_NAME end def self.prepare_symbol(symbol, svg_filename = nil) icon_id = symbol.attr("id") case svg_filename when "regular" icon_id = icon_id.prepend("far-") when "brands" icon_id = icon_id.prepend("fab-") end icon_id end def self.settings_icons get_set_cache("settings_icons") do # includes svg_icon_subset and any settings containing _icon (incl. plugin settings) site_setting_icons = [] SiteSetting.settings_hash.select do |key, value| site_setting_icons |= value.split("|") if key.to_s.include?("_icon") && String === value end site_setting_icons end end def self.plugin_icons DiscoursePluginRegistry.svg_icons end def self.badge_icons get_set_cache("badge_icons") { Badge.pluck(:icon).uniq } end def self.group_icons get_set_cache("group_icons") { Group.pluck(:flair_icon).uniq } end def self.theme_icons(theme_id) return [] if theme_id.blank? theme_icon_settings = [] theme_ids = Theme.transform_ids(theme_id) # Need to load full records for default values Theme .where(id: theme_ids) .each do |theme| _settings = theme.cached_settings.each do |key, value| if key.to_s.include?("_icon") && String === value theme_icon_settings |= value.split("|") end end end theme_icon_settings |= ThemeModifierHelper.new(theme_ids: theme_ids).svg_icons theme_icon_settings end def self.custom_icons(theme_id) # Automatically register icons in sprites added via themes or plugins custom_svgs(theme_id).keys end def self.process(icon_name) new_name = icon_name.strip FA_4_ICON_MAP.each { |k, v| new_name = new_name.sub(k, v) } new_name = remap_from_fa5(new_name) if icon_name != new_name # TODO: Enable message once core and official themes/plugins are updated # Discourse.deprecate("The icon `#{icon_name}` is deprecated. Use `#{new_name}` instead.") return new_name end icon_name end def self.get_set_cache(key, &block) cache.defer_get_set(key, &block) end def self.cache @cache ||= DistributedCache.new("svg_sprite") end def self.remap_from_fa5(icon_name) lookup_name = icon_name prefix = nil if lookup_name.start_with?("far-") prefix = "far" lookup_name = lookup_name[4..-1] elsif lookup_name.start_with?("fab-") prefix = "fab" lookup_name = lookup_name[4..-1] end if FA5_REMAPS.key?(lookup_name) new_name = FA5_REMAPS[lookup_name] new_name = "#{prefix}-#{new_name}" if prefix return new_name end icon_name end FA5_REMAPS = { "adjust" => "circle-half-stroke", "air-freshener" => "spray-can-sparkles", "alien-monster" => "alien-8bit", "allergies" => "hand-dots", "ambulance" => "truck-medical", "american-sign-language-interpreting" => "hands-asl-interpreting", "analytics" => "chart-mixed", "angle-double-down" => "angles-down", "angle-double-left" => "angles-left", "angle-double-right" => "angles-right", "angle-double-up" => "angles-up", "angry" => "face-angry", "apple-alt" => "apple-whole", "apple-crate" => "crate-apple", "archive" => "box-archive", "arrow-alt-circle-down" => "circle-down", "arrow-alt-circle-left" => "circle-left", "arrow-alt-circle-right" => "circle-right", "arrow-alt-circle-up" => "circle-up", "arrow-alt-down" => "down", "arrow-alt-from-bottom" => "up-from-line", "arrow-alt-from-left" => "right-from-line", "arrow-alt-from-right" => "left-from-line", "arrow-alt-from-top" => "down-from-line", "arrow-alt-left" => "left", "arrow-alt-right" => "right", "arrow-alt-square-down" => "square-down", "arrow-alt-square-left" => "square-left", "arrow-alt-square-right" => "square-right", "arrow-alt-square-up" => "square-up", "arrow-alt-to-bottom" => "down-to-line", "arrow-alt-to-left" => "left-to-line", "arrow-alt-to-right" => "right-to-line", "arrow-alt-to-top" => "up-to-line", "arrow-alt-up" => "up", "arrow-circle-down" => "circle-arrow-down", "arrow-circle-left" => "circle-arrow-left", "arrow-circle-right" => "circle-arrow-right", "arrow-circle-up" => "circle-arrow-up", "arrow-from-bottom" => "arrow-up-from-line", "arrow-from-left" => "arrow-right-from-line", "arrow-from-right" => "arrow-left-from-line", "arrow-from-top" => "arrow-down-from-line", "arrow-square-down" => "square-arrow-down", "arrow-square-left" => "square-arrow-left", "arrow-square-right" => "square-arrow-right", "arrow-square-up" => "square-arrow-up", "arrow-to-bottom" => "arrow-down-to-line", "arrow-to-left" => "arrow-left-to-line", "arrow-to-right" => "arrow-right-to-line", "arrow-to-top" => "arrow-up-to-line", "arrows" => "arrows-up-down-left-right", "arrows-alt" => "up-down-left-right", "arrows-alt-h" => "left-right", "arrows-alt-v" => "up-down", "arrows-h" => "arrows-left-right", "arrows-v" => "arrows-up-down", "assistive-listening-systems" => "ear-listen", "atlas" => "book-atlas", "atom-alt" => "atom-simple", "backspace" => "delete-left", "balance-scale" => "scale-balanced", "balance-scale-left" => "scale-unbalanced", "balance-scale-right" => "scale-unbalanced-flip", "band-aid" => "bandage", "barcode-alt" => "rectangle-barcode", "baseball-ball" => "baseball", "basketball-ball" => "basketball", "bed-alt" => "bed-front", "beer" => "beer-mug-empty", "betamax" => "cassette-betamax", "bible" => "book-bible", "biking" => "person-biking", "biking-mountain" => "person-biking-mountain", "birthday-cake" => "cake-candles", "blind" => "person-walking-with-cane", "book-alt" => "book-blank", "book-dead" => "book-skull", "book-reader" => "book-open-reader", "book-spells" => "book-sparkles", "border-style" => "border-top-left", "border-style-alt" => "border-bottom-right", "box-alt" => "box-taped", "box-fragile" => "square-fragile", "box-full" => "box-open-full", "box-up" => "square-this-way-up", "box-usd" => "box-dollar", "boxes" => "boxes-stacked", "boxes-alt" => "boxes-stacked", "brackets" => "brackets-square", "broadcast-tower" => "tower-broadcast", "burn" => "fire-flame-simple", "bus-alt" => "bus-simple", "calculator-alt" => "calculator-simple", "calendar-alt" => "calendar-days", "calendar-edit" => "calendar-pen", "calendar-times" => "calendar-xmark", "camera-alt" => "camera", "camera-home" => "camera-security", "car-alt" => "car-rear", "car-crash" => "car-burst", "car-mechanic" => "car-wrench", "caravan-alt" => "caravan-simple", "caret-circle-down" => "circle-caret-down", "caret-circle-left" => "circle-caret-left", "caret-circle-right" => "circle-caret-right", "caret-circle-up" => "circle-caret-up", "caret-square-down" => "square-caret-down", "caret-square-left" => "square-caret-left", "caret-square-right" => "square-caret-right", "caret-square-up" => "square-caret-up", "cctv" => "camera-cctv", "chalkboard-teacher" => "chalkboard-user", "chart-pie-alt" => "chart-pie-simple", "check-circle" => "circle-check", "check-square" => "square-check", "cheeseburger" => "burger-cheese", "chess-bishop-alt" => "chess-bishop-piece", "chess-clock-alt" => "chess-clock-flip", "chess-king-alt" => "chess-king-piece", "chess-knight-alt" => "chess-knight-piece", "chess-pawn-alt" => "chess-pawn-piece", "chess-queen-alt" => "chess-queen-piece", "chess-rook-alt" => "chess-rook-piece", "chevron-circle-down" => "circle-chevron-down", "chevron-circle-left" => "circle-chevron-left", "chevron-circle-right" => "circle-chevron-right", "chevron-circle-up" => "circle-chevron-up", "chevron-double-down" => "chevrons-down", "chevron-double-left" => "chevrons-left", "chevron-double-right" => "chevrons-right", "chevron-double-up" => "chevrons-up", "chevron-square-down" => "square-chevron-down", "chevron-square-left" => "square-chevron-left", "chevron-square-right" => "square-chevron-right", "chevron-square-up" => "square-chevron-up", "clinic-medical" => "house-chimney-medical", "cloud-download" => "cloud-arrow-down", "cloud-download-alt" => "cloud-arrow-down", "cloud-upload" => "cloud-arrow-up", "cloud-upload-alt" => "cloud-arrow-up", "cocktail" => "martini-glass-citrus", "coffee" => "mug-saucer", "coffee-togo" => "cup-togo", "cog" => "gear", "cogs" => "gears", "columns" => "table-columns", "comment-alt" => "message", "comment-alt-check" => "message-check", "comment-alt-dollar" => "message-dollar", "comment-alt-dots" => "message-dots", "comment-alt-edit" => "message-pen", "comment-alt-exclamation" => "message-exclamation", "comment-alt-lines" => "message-lines", "comment-alt-medical" => "message-medical", "comment-alt-minus" => "message-minus", "comment-alt-music" => "message-music", "comment-alt-plus" => "message-plus", "comment-alt-slash" => "message-slash", "comment-alt-smile" => "message-smile", "comment-alt-times" => "message-xmark", "comment-edit" => "comment-pen", "comment-times" => "comment-xmark", "comments-alt" => "messages", "comments-alt-dollar" => "messages-dollar", "compress-alt" => "down-left-and-up-right-to-center", "compress-arrows-alt" => "minimize", "concierge-bell" => "bell-concierge", "construction" => "triangle-person-digging", "conveyor-belt-alt" => "conveyor-belt-boxes", "cowbell-more" => "cowbell-circle-plus", "cricket" => "cricket-bat-ball", "crop-alt" => "crop-simple", "curling" => "curling-stone", "cut" => "scissors", "deaf" => "ear-deaf", "debug" => "ban-bug", "desktop-alt" => "desktop", "dewpoint" => "droplet-degree", "diagnoses" => "person-dots-from-line", "digging" => "person-digging", "digital-tachograph" => "tachograph-digital", "directions" => "diamond-turn-right", "dizzy" => "face-dizzy", "dolly-flatbed" => "cart-flatbed", "dolly-flatbed-alt" => "cart-flatbed-boxes", "dolly-flatbed-empty" => "cart-flatbed-empty", "donate" => "circle-dollar-to-slot", "dot-circle" => "circle-dot", "drafting-compass" => "compass-drafting", "drone-alt" => "drone-front", "dryer-alt" => "dryer-heat", "eclipse-alt" => "moon-over-sun", "edit" => "pen-to-square", "ellipsis-h" => "ellipsis", "ellipsis-h-alt" => "ellipsis-stroke", "ellipsis-v" => "ellipsis-vertical", "ellipsis-v-alt" => "ellipsis-stroke-vertical", "envelope-square" => "square-envelope", "exchange" => "arrow-right-arrow-left", "exchange-alt" => "right-left", "exclamation-circle" => "circle-exclamation", "exclamation-square" => "square-exclamation", "exclamation-triangle" => "triangle-exclamation", "expand-alt" => "up-right-and-down-left-from-center", "expand-arrows" => "arrows-maximize", "expand-arrows-alt" => "maximize", "external-link" => "arrow-up-right-from-square", "external-link-alt" => "up-right-from-square", "external-link-square" => "square-arrow-up-right", "external-link-square-alt" => "square-up-right", "eyedropper" => "eye-dropper", "fast-backward" => "backward-fast", "fast-forward" => "forward-fast", "feather-alt" => "feather-pointed", "female" => "person-dress", "field-hockey" => "field-hockey-stick-ball", "fighter-jet" => "jet-fighter", "file-alt" => "file-lines", "file-archive" => "file-zipper", "file-chart-line" => "file-chart-column", "file-download" => "file-arrow-down", "file-edit" => "file-pen", "file-medical-alt" => "file-waveform", "file-search" => "file-magnifying-glass", "file-times" => "file-xmark", "file-upload" => "file-arrow-up", "film-alt" => "film-simple", "fire-alt" => "fire-flame-curved", "first-aid" => "kit-medical", "fist-raised" => "hand-fist", "flag-alt" => "flag-swallowtail", "flame" => "fire-flame", "flask-poison" => "flask-round-poison", "flask-potion" => "flask-round-potion", "flushed" => "face-flushed", "fog" => "cloud-fog", "folder-download" => "folder-arrow-down", "folder-times" => "folder-xmark", "folder-upload" => "folder-arrow-up", "font-awesome-alt" => "square-font-awesome-stroke", "font-awesome-flag" => "font-awesome", "font-awesome-logo-full" => "font-awesome", "football-ball" => "football", "fragile" => "wine-glass-crack", "frosty-head" => "snowman-head", "frown" => "face-frown", "frown-open" => "face-frown-open", "funnel-dollar" => "filter-circle-dollar", "game-board-alt" => "game-board-simple", "gamepad-alt" => "gamepad-modern", "glass-champagne" => "champagne-glass", "glass-cheers" => "champagne-glasses", "glass-martini" => "martini-glass-empty", "glass-martini-alt" => "martini-glass", "glass-whiskey" => "whiskey-glass", "glass-whiskey-rocks" => "whiskey-glass-ice", "glasses-alt" => "glasses-round", "globe-africa" => "earth-africa", "globe-americas" => "earth-americas", "globe-asia" => "earth-asia", "globe-europe" => "earth-europe", "golf-ball" => "golf-ball-tee", "grimace" => "face-grimace", "grin" => "face-grin", "grin-alt" => "face-grin-wide", "grin-beam" => "face-grin-beam", "grin-beam-sweat" => "face-grin-beam-sweat", "grin-hearts" => "face-grin-hearts", "grin-squint" => "face-grin-squint", "grin-squint-tears" => "face-grin-squint-tears", "grin-stars" => "face-grin-stars", "grin-tears" => "face-grin-tears", "grin-tongue" => "face-grin-tongue", "grin-tongue-squint" => "face-grin-tongue-squint", "grin-tongue-wink" => "face-grin-tongue-wink", "grin-wink" => "face-grin-wink", "grip-horizontal" => "grip", "h-square" => "square-h", "hamburger" => "burger", "hand-holding-usd" => "hand-holding-dollar", "hand-holding-water" => "hand-holding-droplet", "hand-paper" => "hand", "hand-receiving" => "hands-holding-diamond", "hand-rock" => "hand-back-fist", "hands-heart" => "hands-holding-heart", "hands-helping" => "handshake-angle", "hands-usd" => "hands-holding-dollar", "hands-wash" => "hands-bubbles", "handshake-alt" => "handshake-simple", "handshake-alt-slash" => "handshake-simple-slash", "hard-hat" => "helmet-safety", "hdd" => "hard-drive", "head-vr" => "head-side-goggles", "headphones-alt" => "headphones-simple", "heart-broken" => "heart-crack", "heart-circle" => "circle-heart", "heart-rate" => "wave-pulse", "heart-square" => "square-heart", "heartbeat" => "heart-pulse", "hiking" => "person-hiking", "history" => "clock-rotate-left", "home" => "house", "home-alt" => "house", "home-heart" => "house-heart", "home-lg" => "house-chimney", "home-lg-alt" => "house", "hospital-alt" => "hospital", "hospital-symbol" => "circle-h", "hot-tub" => "hot-tub-person", "hourglass-half" => "hourglass", "house-damage" => "house-chimney-crack", "house-leave" => "house-person-leave", "house-return" => "house-person-return", "hryvnia" => "hryvnia-sign", "humidity" => "droplet-percent", "icons-alt" => "symbols", "id-card-alt" => "id-card-clip", "industry-alt" => "industry-windows", "info-circle" => "circle-info", "info-square" => "square-info", "innosoft" => "42-group", "inventory" => "shelves", "journal-whills" => "book-journal-whills", "kiss" => "face-kiss", "kiss-beam" => "face-kiss-beam", "kiss-wink-heart" => "face-kiss-wink-heart", "landmark-alt" => "landmark-dome", "laptop-house" => "house-laptop", "laugh" => "face-laugh", "laugh-beam" => "face-laugh-beam", "laugh-squint" => "face-laugh-squint", "laugh-wink" => "face-laugh-wink", "level-down" => "arrow-turn-down", "level-down-alt" => "turn-down", "level-up" => "arrow-turn-up", "level-up-alt" => "turn-up", "list-alt" => "rectangle-list", "location" => "location-crosshairs", "location-circle" => "circle-location-arrow", "location-slash" => "location-crosshairs-slash", "lock-alt" => "lock-keyhole", "lock-open-alt" => "lock-keyhole-open", "long-arrow-alt-down" => "down-long", "long-arrow-alt-left" => "left-long", "long-arrow-alt-right" => "right-long", "long-arrow-alt-up" => "up-long", "long-arrow-down" => "arrow-down-long", "long-arrow-left" => "arrow-left-long", "long-arrow-right" => "arrow-right-long", "long-arrow-up" => "arrow-up-long", "low-vision" => "eye-low-vision", "luchador" => "luchador-mask", "luggage-cart" => "cart-flatbed-suitcase", "magic" => "wand-magic", "mail-bulk" => "envelopes-bulk", "male" => "person", "map-marked" => "map-location", "map-marked-alt" => "map-location-dot", "map-marker" => "location-pin", "map-marker-alt" => "location-dot", "map-marker-alt-slash" => "location-dot-slash", "map-marker-check" => "location-check", "map-marker-edit" => "location-pen", "map-marker-exclamation" => "location-exclamation", "map-marker-minus" => "location-minus", "map-marker-plus" => "location-plus", "map-marker-question" => "location-question", "map-marker-slash" => "location-pin-slash", "map-marker-smile" => "location-smile", "map-marker-times" => "location-xmark", "map-signs" => "signs-post", "mars-stroke-h" => "mars-stroke-right", "mars-stroke-v" => "mars-stroke-up", "medium-m" => "medium", "medkit" => "suitcase-medical", "meh" => "face-meh", "meh-blank" => "face-meh-blank", "meh-rolling-eyes" => "face-rolling-eyes", "microphone-alt" => "microphone-lines", "microphone-alt-slash" => "microphone-lines-slash", "mind-share" => "brain-arrow-curved-right", "minus-circle" => "circle-minus", "minus-hexagon" => "hexagon-minus", "minus-octagon" => "octagon-minus", "minus-square" => "square-minus", "mobile-alt" => "mobile-screen-button", "mobile-android" => "mobile", "mobile-android-alt" => "mobile-screen", "money-bill-alt" => "money-bill-1", "money-bill-wave-alt" => "money-bill-1-wave", "money-check-alt" => "money-check-dollar", "money-check-edit" => "money-check-pen", "money-check-edit-alt" => "money-check-dollar-pen", "monitor-heart-rate" => "monitor-waveform", "mouse" => "computer-mouse", "mouse-alt" => "computer-mouse-scrollwheel", "mouse-pointer" => "arrow-pointer", "music-alt" => "music-note", "music-alt-slash" => "music-note-slash", "oil-temp" => "oil-temperature", "page-break" => "file-dashed-line", "paint-brush" => "paintbrush", "paint-brush-alt" => "paintbrush-fine", "paint-brush-fine" => "paintbrush-fine", "pallet-alt" => "pallet-boxes", "paragraph-rtl" => "paragraph-left", "parking" => "square-parking", "parking-circle" => "circle-parking", "parking-circle-slash" => "ban-parking", "parking-slash" => "square-parking-slash", "pastafarianism" => "spaghetti-monster-flying", "pause-circle" => "circle-pause", "paw-alt" => "paw-simple", "pen-alt" => "pen-clip", "pen-square" => "square-pen", "pencil-alt" => "pencil", "pencil-paintbrush" => "pen-paintbrush", "pencil-ruler" => "pen-ruler", "pennant" => "flag-pennant", "people-arrows" => "people-arrows-left-right", "people-carry" => "people-carry-box", "percentage" => "percent", "person-carry" => "person-carry-box", "phone-alt" => "phone-flip", "phone-laptop" => "laptop-mobile", "phone-square" => "square-phone", "phone-square-alt" => "square-phone-flip", "photo-video" => "photo-film", "plane-alt" => "plane-engines", "play-circle" => "circle-play", "plus-circle" => "circle-plus", "plus-hexagon" => "hexagon-plus", "plus-octagon" => "octagon-plus", "plus-square" => "square-plus", "poll" => "square-poll-vertical", "poll-h" => "square-poll-horizontal", "portal-enter" => "person-to-portal", "portal-exit" => "person-from-portal", "portrait" => "image-portrait", "pound-sign" => "sterling-sign", "pray" => "person-praying", "praying-hands" => "hands-praying", "prescription-bottle-alt" => "prescription-bottle-medical", "presentation" => "presentation-screen", "print-search" => "print-magnifying-glass", "procedures" => "bed-pulse", "project-diagram" => "diagram-project", "question-circle" => "circle-question", "question-square" => "square-question", "quran" => "book-quran", "rabbit-fast" => "rabbit-running", "radiation-alt" => "circle-radiation", "radio-alt" => "radio-tuner", "random" => "shuffle", "rectangle-landscape" => "rectangle", "rectangle-portrait" => "rectangle-vertical", "redo" => "arrow-rotate-right", "redo-alt" => "rotate-right", "remove-format" => "text-slash", "repeat-1-alt" => "arrows-repeat-1", "repeat-alt" => "arrows-repeat", "retweet-alt" => "arrows-retweet", "rss-square" => "square-rss", "running" => "person-running", "sad-cry" => "face-sad-cry", "sad-tear" => "face-sad-tear", "save" => "floppy-disk", "sax-hot" => "saxophone-fire", "scalpel-path" => "scalpel-line-dashed", "scanner-image" => "scanner", "search" => "magnifying-glass", "search-dollar" => "magnifying-glass-dollar", "search-location" => "magnifying-glass-location", "search-minus" => "magnifying-glass-minus", "search-plus" => "magnifying-glass-plus", "sensor-alert" => "sensor-triangle-exclamation", "sensor-smoke" => "sensor-cloud", "share-alt" => "share-nodes", "share-alt-square" => "square-share-nodes", "share-square" => "share-from-square", "shield-alt" => "shield-halved", "shipping-fast" => "truck-fast", "shipping-timed" => "truck-clock", "shopping-bag" => "bag-shopping", "shopping-basket" => "basket-shopping", "shopping-cart" => "cart-shopping", "shuttle-van" => "van-shuttle", "sign" => "sign-hanging", "sign-in" => "arrow-right-to-bracket", "sign-in-alt" => "right-to-bracket", "sign-language" => "hands", "sign-out" => "arrow-right-from-bracket", "sign-out-alt" => "right-from-bracket", "signal-1" => "signal-weak", "signal-2" => "signal-fair", "signal-3" => "signal-good", "signal-4" => "signal-strong", "signal-alt" => "signal-bars", "signal-alt-1" => "signal-bars-weak", "signal-alt-2" => "signal-bars-fair", "signal-alt-3" => "signal-bars-good", "signal-alt-slash" => "signal-bars-slash", "skating" => "person-skating", "ski-jump" => "person-ski-jumping", "ski-lift" => "person-ski-lift", "skiing" => "person-skiing", "skiing-nordic" => "person-skiing-nordic", "slack-hash" => "slack", "sledding" => "person-sledding", "sliders-h" => "sliders", "sliders-h-square" => "square-sliders", "sliders-v" => "sliders-up", "sliders-v-square" => "square-sliders-vertical", "smile" => "face-smile", "smile-beam" => "face-smile-beam", "smile-plus" => "face-smile-plus", "smile-wink" => "face-smile-wink", "smoking-ban" => "ban-smoking", "sms" => "comment-sms", "snapchat-ghost" => "snapchat", "snowboarding" => "person-snowboarding", "snowmobile" => "person-snowmobiling", "sort-alpha-down" => "arrow-down-a-z", "sort-alpha-down-alt" => "arrow-down-z-a", "sort-alpha-up" => "arrow-up-a-z", "sort-alpha-up-alt" => "arrow-up-z-a", "sort-alt" => "arrow-down-arrow-up", "sort-amount-down" => "arrow-down-wide-short", "sort-amount-down-alt" => "arrow-down-short-wide", "sort-amount-up" => "arrow-up-wide-short", "sort-amount-up-alt" => "arrow-up-short-wide", "sort-circle" => "circle-sort", "sort-circle-down" => "circle-sort-down", "sort-circle-up" => "circle-sort-up", "sort-numeric-down" => "arrow-down-1-9", "sort-numeric-down-alt" => "arrow-down-9-1", "sort-numeric-up" => "arrow-up-1-9", "sort-numeric-up-alt" => "arrow-up-9-1", "sort-shapes-down" => "arrow-down-triangle-square", "sort-shapes-down-alt" => "arrow-down-square-triangle", "sort-shapes-up" => "arrow-up-triangle-square", "sort-shapes-up-alt" => "arrow-up-square-triangle", "sort-size-down" => "arrow-down-big-small", "sort-size-down-alt" => "arrow-down-small-big", "sort-size-up" => "arrow-up-big-small", "sort-size-up-alt" => "arrow-up-small-big", "soup" => "bowl-hot", "space-shuttle" => "shuttle-space", "space-station-moon-alt" => "space-station-moon-construction", "square-root-alt" => "square-root-variable", "star-half-alt" => "star-half-stroke", "starfighter-alt" => "starfighter-twin-ion-engine", "step-backward" => "backward-step", "step-forward" => "forward-step", "sticky-note" => "note-sticky", "stop-circle" => "circle-stop", "store-alt" => "shop", "store-alt-slash" => "shop-slash", "stream" => "bars-staggered", "subway" => "train-subway", "surprise" => "face-surprise", "swimmer" => "person-swimming", "swimming-pool" => "water-ladder", "sync" => "arrows-rotate", "sync-alt" => "rotate", "table-tennis" => "table-tennis-paddle-ball", "tablet-alt" => "tablet-screen-button", "tablet-android" => "tablet", "tablet-android-alt" => "tablet-screen", "tachometer" => "gauge-simple", "tachometer-alt" => "gauge", "tachometer-alt-average" => "gauge-med", "tachometer-alt-fast" => "gauge", "tachometer-alt-fastest" => "gauge-max", "tachometer-alt-slow" => "gauge-low", "tachometer-alt-slowest" => "gauge-min", "tachometer-average" => "gauge-simple-med", "tachometer-fast" => "gauge-simple", "tachometer-fastest" => "gauge-simple-max", "tachometer-slow" => "gauge-simple-low", "tachometer-slowest" => "gauge-simple-min", "tanakh" => "book-tanakh", "tasks" => "list-check", "tasks-alt" => "bars-progress", "telegram-plane" => "telegram", "temperature-down" => "temperature-arrow-down", "temperature-frigid" => "temperature-snow", "temperature-hot" => "temperature-sun", "temperature-up" => "temperature-arrow-up", "tenge" => "tenge-sign", "th" => "table-cells", "th-large" => "table-cells-large", "th-list" => "table-list", "theater-masks" => "masks-theater", "thermometer-empty" => "temperature-empty", "thermometer-full" => "temperature-full", "thermometer-half" => "temperature-half", "thermometer-quarter" => "temperature-quarter", "thermometer-three-quarters" => "temperature-three-quarters", "thumb-tack" => "thumbtack", "thunderstorm" => "cloud-bolt", "thunderstorm-moon" => "cloud-bolt-moon", "thunderstorm-sun" => "cloud-bolt-sun", "ticket-alt" => "ticket-simple", "times" => "xmark", "times-circle" => "circle-xmark", "times-hexagon" => "hexagon-xmark", "times-octagon" => "octagon-xmark", "times-square" => "square-xmark", "tint" => "droplet", "tint-slash" => "droplet-slash", "tired" => "face-tired", "toilet-paper-alt" => "toilet-paper-blank", "tombstone-alt" => "tombstone-blank", "tools" => "screwdriver-wrench", "torah" => "scroll-torah", "tram" => "train-tram", "transgender-alt" => "transgender", "trash-alt" => "trash-can", "trash-restore" => "trash-arrow-up", "trash-restore-alt" => "trash-can-arrow-up", "trash-undo-alt" => "trash-can-undo", "tree-alt" => "tree-deciduous", "triangle-music" => "triangle-instrument", "trophy-alt" => "trophy-star", "truck-couch" => "truck-ramp-couch", "truck-loading" => "truck-ramp-box", "tshirt" => "shirt", "tv-alt" => "tv", "undo" => "arrow-rotate-left", "undo-alt" => "rotate-left", "university" => "building-columns", "unlink" => "link-slash", "unlock-alt" => "unlock-keyhole", "usd-circle" => "circle-dollar", "usd-square" => "square-dollar", "user-alt" => "user-large", "user-alt-slash" => "user-large-slash", "user-chart" => "chart-user", "user-circle" => "circle-user", "user-cog" => "user-gear", "user-edit" => "user-pen", "user-friends" => "user-group", "user-hard-hat" => "user-helmet-safety", "user-md" => "user-doctor", "user-md-chat" => "user-doctor-message", "user-times" => "user-xmark", "users-class" => "screen-users", "users-cog" => "users-gear", "users-crown" => "user-group-crown", "utensil-fork" => "fork", "utensil-knife" => "knife", "utensil-spoon" => "spoon", "utensils-alt" => "fork-knife", "vhs" => "cassette-vhs", "volleyball-ball" => "volleyball", "volume-down" => "volume-low", "volume-mute" => "volume-xmark", "volume-up" => "volume-high", "vote-nay" => "xmark-to-slot", "vote-yea" => "check-to-slot", "walking" => "person-walking", "warehouse-alt" => "warehouse-full", "washer" => "washing-machine", "water-lower" => "water-arrow-down", "water-rise" => "water-arrow-up", "waveform-path" => "waveform-lines", "webcam" => "camera-web", "webcam-slash" => "camera-web-slash", "weight" => "weight-scale", "wifi-1" => "wifi-weak", "wifi-2" => "wifi-fair", "window-alt" => "window-flip", "window-close" => "rectangle-xmark", "wine-glass-alt" => "wine-glass-empty", } end