plain routing support

plain routing support

--- src/sirius.coffee
+++ src/sirius.coffee
@@ -44,7 +44,7 @@
     @start = null #not used ...
     @parts = []
     @args  = []
-    # #/abc/dsa/ => ["#", "abc", "dsa"]
+    # #/abc/dsa/ => ["#", "abc", "dsa"] or ["", "abc", "dsa"]
     parts = route.replace(/\/$/, "").split("/")

     # mark, this route not have a length and end
@@ -76,6 +76,8 @@
     return false if ((parts.length != @parts.length) && @end)
     #when it have a different length, but @parts len > given len
     return false if (@parts.length > 1) && parts.length < @parts.length
+    # when not end, and last parts not the equals
+    return false if @end && (parts[parts.length-1] != @parts[parts.length-1])

     is_named_part = (part) ->
       part.indexOf(":") == 0
@@ -220,6 +222,8 @@
   _plain_route: (url) ->
     url.toString().indexOf("/") == 0

+  _event_route: (url) ->
+    !@_hash_route(url) && !@_404_route(url) && !@_plain_route(url)
   #
   # @param routes [Object] object with routes
   # @param fn [Function] callback, which will be called, after routes will be defined
@@ -230,7 +234,7 @@
     current = prev = window.location.hash

     # set routing by event
-    for url, action of routes when !@_hash_route(url) && !@_404_route(url) && !@_plain_route(url)
+    for url, action of routes when @_event_route(url)
       handler = if Sirius.Utils.is_function(action)
         action
       else
@@ -242,21 +246,40 @@
       Sirius.Application.adapter.bind(selector, event_name, handler)

     # for cache change obj[k, v] to array [[k,v]]
-    array_of_routes = for url, action of routes when @_hash_route(url) && !@_404_route(url) && !@_plain_route(url)
+    array_of_routes = for url, action of routes when @_hash_route(url)
       url    = new Sirius.RoutePart(url)
       action = if Sirius.Utils.is_function(action) then action else new Sirius.ControlFlow(action)
       [url, action]
-    c array_of_routes
-    window.onhashchange = (e) =>
+
+    plain_routes = for url, action of routes when @_plain_route(url)
+      url    = new Sirius.RoutePart(url)
+      action = if Sirius.Utils.is_function(action) then action else new Sirius.ControlFlow(action)
+      [url, action]
+
+    dispatcher = (e) ->
+      c 111111
       prev = current
-      current = window.location.hash
       result = false
+      arr = null
+
+      if event.type == "hashchange"
+        # hashchange
+        arr = array_of_routes
+        current = window.location.hash
+      else
+        #plain
+        arr = plain_routes
+        href = e.target.href # TODO the same for hashchange
+        history.pushState({href: href}, "#{href}", href)
+        pathname = window.location.pathname
+        pathname = "/" if pathname == ""
+        e.preventDefault()
+        current = pathname

       Sirius.Application.logger("Url change to: #{current}")
-      Sirius.Application.adapter.fire(document, "application:hashchange", current, prev)
-
-      #call first matched function
-      for part in array_of_routes
+      Sirius.Application.adapter.fire(document, "application:urlchange", current, prev)
+
+      for part in arr
         f = part[0]
         r = f.match(current)
         if r && !result
@@ -269,14 +292,25 @@
             flow.apply(null, f.args)
           return

-      #when no results, then call 404 or empty function
       if !result
         Sirius.Application.adapter.fire(document, "application:404", current, prev)
         #FIXME
-        r404 = routes['404']
+        r404 = routes['404'] || routes[404]
         if r404
           z = new Sirius.ControlFlow(r404)
           z.handle_event(null, current)
+
+      false
+
+
+    if plain_routes.length != 0 # TODO && use_modern_routing ???
+      # bind all <a> element with dispatch function, but bind only when href not contain "#"
+      selector  = "a:not([href^='#'])"
+
+      Sirius.Application.adapter.bind selector, "click", dispatcher
+
+    window.onhashchange = dispatcher
+

     fn()


Fixes typo in spec

Fixes typo in spec

--- exercises/palindrome-products/palindrome_products_test.spec.coffee
+++ exercises/palindrome-products/palindrome_products_test.spec.coffee
@@ -9,7 +9,7 @@
     expect(largest.value).toEqual(9)
     expect([[[3, 3], [1, 9]], [[1, 9], [3, 3]]]).toContain(largest.factors)

-  xit "largets palindrome from double digit factors", ->
+  xit "largest palindrome from double digit factors", ->
     palindromes = new Palindromes({ maxFactor: 99, minFactor: 10 })
     palindromes.generate()
     largest = palindromes.largest()


adding the rest of the emoji codes

adding the rest of the emoji codes

--- src/ui/emojishortcode.coffee
+++ src/ui/emojishortcode.coffee
@@ -1,210 +1,1256 @@
-# Currently only has the People emojis
-
 module.exports = {
-    ":grinning:":"\ud83d\ude00",
-    ":grin:":"\ud83d\ude01",
-    ":joy:":"\ud83d\ude02",
-    ":smiley:":"\ud83d\ude03",
-    ":smile:":"\ud83d\ude04",
-    ":sweat_smile:":"\ud83d\ude05",
-    ":laughing:":"\ud83d\ude06",
-    ":wink:":"\ud83d\ude09",
-    ":blush:":"\ud83d\ude0a",
-    ":yum:":"\ud83d\ude0b",
-    ":sunglasses:":"\ud83d\ude0e",
-    ":heart_eyes:":"\ud83d\ude0d",
-    ":kissing_heart:":"\ud83d\ude18",
-    ":kissing:":"\ud83d\ude17",
-    ":kissing_smiling_eyes:":"\ud83d\ude19",
-    ":kissing_closed_eyes:":"\ud83d\ude1a",
-    ":relaxed:":"\u263a",
-    ":slightly_smiling_face:":"\ud83d\ude42",
-    ":hugging_face:":"\ud83e\udd17",
-    ":innocent:":"\ud83d\ude07",
-    ":thinking_face:":"\ud83e\udd14",
-    ":neutral_face:":"\ud83d\ude10",
-    ":expressionless:":"\ud83d\ude11",
-    ":no_mouth:":"\ud83d\ude36",
-    ":face_with_rolling_eyes:":"\ud83d\ude44",
-    ":smirk:":"\ud83d\ude0f",
-    ":persevere:":"\ud83d\ude23",
-    ":disappointed_relieved:":"\ud83d\ude25",
-    ":open_mouth:":"\ud83d\ude2e",
-    ":zipper_mouth_face:":"\ud83e\udd10",
-    ":hushed:":"\ud83d\ude2f",
-    ":sleepy:":"\ud83d\ude2a",
-    ":tired_face:":"\ud83d\ude2b",
-    ":sleeping:":"\ud83d\ude34",
-    ":relieved:":"\ud83d\ude0c",
-    ":nerd_face:":"\ud83e\udd13",
-    ":stuck_out_tongue:":"\ud83d\ude1b",
-    ":stuck_out_tongue_winking_eye:":"\ud83d\ude1c",
-    ":stuck_out_tongue_closed_eyes:":"\ud83d\ude1d",
-    ":white_frowning_face:":"\u2639",
-    ":slightly_frowning_face:":"\ud83d\ude41",
-    ":unamused:":"\ud83d\ude12",
-    ":sweat:":"\ud83d\ude13",
-    ":pensive:":"\ud83d\ude14",
-    ":confused:":"\ud83d\ude15",
-    ":confounded:":"\ud83d\ude16",
-    ":upside_down_face:":"\ud83d\ude43",
-    ":mask:":"\ud83d\ude37",
-    ":face_with_thermometer:":"\ud83e\udd12",
-    ":face_with_head_bandage:":"\ud83e\udd15",
-    ":money_mouth_face:":"\ud83e\udd11",
-    ":astonished:":"\ud83d\ude32",
-    ":disappointed:":"\ud83d\ude1e",
-    ":worried:":"\ud83d\ude1f",
-    ":triumph:":"\ud83d\ude24",
-    ":cry:":"\ud83d\ude22",
-    ":sob:":"\ud83d\ude2d",
-    ":frowning:":"\ud83d\ude26",
-    ":anguished:":"\ud83d\ude27",
-    ":fearful:":"\ud83d\ude28",
-    ":weary:":"\ud83d\ude29",
-    ":grimacing:":"\ud83d\ude2c",
-    ":cold_sweat:":"\ud83d\ude30",
-    ":scream:":"\ud83d\ude31",
-    ":flushed:":"\ud83d\ude33",
-    ":dizzy_face:":"\ud83d\ude35",
-    ":rage:":"\ud83d\ude21",
-    ":angry:":"\ud83d\ude20",
-    ":imp:":"\ud83d\udc7f",
-    ":smiling_imp:":"\ud83d\ude08",
-    ":boy:":"\ud83d\udc66",
-    ":girl:":"\ud83d\udc67",
-    ":man:":"\ud83d\udc68",
-    ":woman:":"\ud83d\udc69",
-    ":older_man:":"\ud83d\udc74",
-    ":older_woman:":"\ud83d\udc75",
-    ":baby:":"\ud83d\udc76",
-    ":person_with_blond_hair:":"\ud83d\udc71",
-    ":cop:":"\ud83d\udc6e",
-    ":man_with_gua_pi_mao:":"\ud83d\udc72",
-    ":man_with_turban:":"\ud83d\udc73",
-    ":construction_worker:":"\ud83d\udc77",
-    ":helmet_with_white_cross:":"\u26d1",
-    ":princess:":"\ud83d\udc78",
-    ":guardsman:":"\ud83d\udc82",
-    ":santa:":"\ud83c\udf85",
-    ":angel:":"\ud83d\udc7c",
-    ":sleuth_or_spy:":"\ud83d\udd75",
-    ":dancers:":"\ud83d\udc6f",
-    ":massage:":"\ud83d\udc86",
-    ":haircut:":"\ud83d\udc87",
-    ":bride_with_veil:":"\ud83d\udc70",
-    ":person_frowning:":"\ud83d\ude4d",
-    ":person_with_pouting_face:":"\ud83d\ude4e",
-    ":no_good:":"\ud83d\ude45",
-    ":ok_woman:":"\ud83d\ude46",
-    ":information_desk_person:":"\ud83d\udc81",
-    ":raising_hand:":"\ud83d\ude4b",
-    ":bow:":"\ud83d\ude47",
-    ":raised_hands:":"\ud83d\ude4c",
-    ":pray:":"\ud83d\ude4f",
-    ":speaking_head_in_silhouette:":"\ud83d\udde3",
-    ":bust_in_silhouette:":"\ud83d\udc64",
-    ":busts_in_silhouette:":"\ud83d\udc65",
-    ":walking:":"\ud83d\udeb6",
-    ":runner:":"\ud83c\udfc3",
-    ":dancer:":"\ud83d\udc83",
-    ":man_in_business_suit_levitating:":"\ud83d\udd74",
-    ":couplekiss:":"\ud83d\udc8f",
-    ":couple_with_heart:":"\ud83d\udc91",
-    ":family:":"\ud83d\udc6a",
-    ":couple:":"\ud83d\udc6b",
-    ":two_men_holding_hands:":"\ud83d\udc6c",
-    ":two_women_holding_hands:":"\ud83d\udc6d",
-    ":muscle:":"\ud83d\udcaa",
-    ":point_left:":"\ud83d\udc48",
-    ":point_right:":"\ud83d\udc49",
-    ":point_up:":"\u261d",
-    ":point_up_2:":"\ud83d\udc46",
-    ":middle_finger:":"\ud83d\udd95",
-    ":point_down:":"\ud83d\udc47",
-    ":v:":"\u270c",
-    ":spock-hand:":"\ud83d\udd96",
-    ":the_horns:":"\ud83e\udd18",
-    ":raised_hand_with_fingers_splayed:":"\ud83d\udd90",
-    ":fist:":"\u270a",
-    ":hand:":"\u270b",
-    ":facepunch:":"\ud83d\udc4a",
-    ":ok_hand:":"\ud83d\udc4c",
-    ":+1:":"\ud83d\udc4d",
-    ":-1:":"\ud83d\udc4e",
-    ":wave:":"\ud83d\udc4b",
-    ":clap:":"\ud83d\udc4f",
-    ":open_hands:":"\ud83d\udc50",
-    ":writing_hand:":"\u270d",
-    ":nail_care:":"\ud83d\udc85",
-    ":footprints:":"\ud83d\udc63",
-    ":eyes:":"\ud83d\udc40",
-    ":eye:":"\ud83d\udc41",
-    ":ear:":"\ud83d\udc42",
-    ":nose:":"\ud83d\udc43",
-    ":tongue:":"\ud83d\udc45",
-    ":lips:":"\ud83d\udc44",
-    ":kiss:":"\ud83d\udc8b",
-    ":eyeglasses:":"\ud83d\udc53",
-    ":dark_sunglasses:":"\ud83d\udd76",
-    ":necktie:":"\ud83d\udc54",
-    ":shirt:":"\ud83d\udc55",
-    ":jeans:":"\ud83d\udc56",
-    ":dress:":"\ud83d\udc57",
-    ":kimono:":"\ud83d\udc58",
-    ":bikini:":"\ud83d\udc59",
-    ":womans_clothes:":"\ud83d\udc5a",
-    ":purse:":"\ud83d\udc5b",
-    ":handbag:":"\ud83d\udc5c",
-    ":pouch:":"\ud83d\udc5d",
-    ":school_satchel:":"\ud83c\udf92",
-    ":mans_shoe:":"\ud83d\udc5e",
-    ":athletic_shoe:":"\ud83d\udc5f",
-    ":high_heel:":"\ud83d\udc60",
-    ":sandal:":"\ud83d\udc61",
-    ":boot:":"\ud83d\udc62",
-    ":crown:":"\ud83d\udc51",
-    ":womans_hat:":"\ud83d\udc52",
-    ":tophat:":"\ud83c\udfa9",
-    ":lipstick:":"\ud83d\udc84",
-    ":ring:":"\ud83d\udc8d",
-    ":japanese_ogre:":"\ud83d\udc79",
-    ":japanese_goblin:":"\ud83d\udc7a",
-    ":ghost:":"\ud83d\udc7b",
-    ":skull:":"\ud83d\udc80",
-    ":alien:":"\ud83d\udc7d",
-    ":robot_face:":"\ud83e\udd16",
-    ":hankey:":"\ud83d\udca9",
-    ":smile_cat:":"\ud83d\ude38",
-    ":joy_cat:":"\ud83d\ude39",
-    ":smiley_cat:":"\ud83d\ude3a",
-    ":heart_eyes_cat:":"\ud83d\ude3b",
-    ":smirk_cat:":"\ud83d\ude3c",
-    ":kissing_cat:":"\ud83d\ude3d",
-    ":pouting_cat:":"\ud83d\ude3e",
-    ":crying_cat_face:":"\ud83d\ude3f",
-    ":scream_cat:":"\ud83d\ude40",
-    ":closed_umbrella:":"\ud83c\udf02",
-    ":mortar_board:":"\ud83c\udf93",
-    ":briefcase:":"\ud83d\udcbc",
-    ":man-woman-girl:":"\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67",
-    ":man-woman-girl-boy:":"\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66",
-    ":man-woman-boy-boy:":"\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66",
-    ":man-woman-girl-girl:":"\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67",
-    ":woman-woman-boy:":"\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66",
-    ":woman-woman-girl:":"\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67",
-    ":woman-woman-girl-boy:":"\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66",
-    ":woman-woman-boy-boy:":"\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66",
-    ":woman-woman-girl-girl:":"\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67",
-    ":man-man-boy:":"\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66",
-    ":man-man-girl:":"\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67",
-    ":man-man-girl-boy:":"\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66",
-    ":man-man-boy-boy:":"\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66",
-    ":man-man-girl-girl:":"\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67",
-    ":woman-heart-woman:":"\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc69",
-    ":man-heart-man:":"\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68",
-    ":woman-kiss-woman:":"\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69",
-    ":man-kiss-man:":"\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68",
+    ":grinning:":  "\ud83d\ude00",
+    ":grin:":   "\ud83d\ude01",
+    ":joy:":    "\ud83d\ude02",
+    ":smiley:": "\ud83d\ude03",
+    ":smile:":  "\ud83d\ude04",
+    ":sweat_smile:":    "\ud83d\ude05",
+    ":laughing:":   "\ud83d\ude06",
+    ":wink:":   "\ud83d\ude09",
+    ":blush:":  "\ud83d\ude0a",
+    ":yum:":    "\ud83d\ude0b",
+    ":sunglasses:": "\ud83d\ude0e",
+    ":heart_eyes:": "\ud83d\ude0d",
+    ":kissing_heart:":  "\ud83d\ude18",
+    ":kissing:":    "\ud83d\ude17",
+    ":kissing_smiling_eyes:":   "\ud83d\ude19",
+    ":kissing_closed_eyes:":    "\ud83d\ude1a",
+    ":relaxed:":    "\u263a",
+    ":slight_smile:":   "\ud83d\ude42",
+    ":hugging:":    "\ud83e\udd17",
+    ":innocent:":   "\ud83d\ude07",
+    ":thinking:":   "\ud83e\udd14",
+    ":neutral_face:":   "\ud83d\ude10",
+    ":expressionless:": "\ud83d\ude11",
+    ":no_mouth:":   "\ud83d\ude36",
+    ":rolling_eyes:":   "\ud83d\ude44",
+    ":smirk:":  "\ud83d\ude0f",
+    ":persevere:":  "\ud83d\ude23",
+    ":disappointed_relieved:":  "\ud83d\ude25",
+    ":open_mouth:": "\ud83d\ude2e",
+    ":zipper_mouth:":   "\ud83e\udd10",
+    ":hushed:": "\ud83d\ude2f",
+    ":sleepy:": "\ud83d\ude2a",
+    ":tired_face:": "\ud83d\ude2b",
+    ":sleeping:":   "\ud83d\ude34",
+    ":relieved:":   "\ud83d\ude0c",
+    ":nerd:":   "\ud83e\udd13",
+    ":stuck_out_tongue:":   "\ud83d\ude1b",
+    ":stuck_out_tongue_winking_eye:":   "\ud83d\ude1c",
+    ":stuck_out_tongue_closed_eyes:":   "\ud83d\ude1d",
+    ":frowning2:":  "\u2639",
+    ":slight_frown:":   "\ud83d\ude41",
+    ":unamused:":   "\ud83d\ude12",
+    ":sweat:":  "\ud83d\ude13",
+    ":pensive:":    "\ud83d\ude14",
+    ":confused:":   "\ud83d\ude15",
+    ":confounded:": "\ud83d\ude16",
+    ":upside_down:":    "\ud83d\ude43",
+    ":mask:":   "\ud83d\ude37",
+    ":thermometer_face:":   "\ud83e\udd12",
+    ":head_bandage:":   "\ud83e\udd15",
+    ":money_mouth:":    "\ud83e\udd11",
+    ":astonished:": "\ud83d\ude32",
+    ":disappointed:":   "\ud83d\ude1e",
+    ":worried:":    "\ud83d\ude1f",
+    ":triumph:":    "\ud83d\ude24",
+    ":cry:":    "\ud83d\ude22",
+    ":sob:":    "\ud83d\ude2d",
+    ":frowning:":   "\ud83d\ude26",
+    ":anguished:":  "\ud83d\ude27",
+    ":fearful:":    "\ud83d\ude28",
+    ":weary:":  "\ud83d\ude29",
+    ":grimacing:":  "\ud83d\ude2c",
+    ":cold_sweat:": "\ud83d\ude30",
+    ":scream:": "\ud83d\ude31",
+    ":flushed:":    "\ud83d\ude33",
+    ":dizzy_face:": "\ud83d\ude35",
+    ":rage:":   "\ud83d\ude21",
+    ":angry:":  "\ud83d\ude20",
+    ":imp:":    "\ud83d\udc7f",
+    ":smiling_imp:":    "\ud83d\ude08",
+    ":boy:":    "\ud83d\udc66",
+    ":girl:":   "\ud83d\udc67",
+    ":man:":    "\ud83d\udc68",
+    ":woman:":  "\ud83d\udc69",
+    ":older_man:":  "\ud83d\udc74",
+    ":older_woman:":    "\ud83d\udc75",
+    ":baby:":   "\ud83d\udc76",
+    ":person_with_blond_hair:": "\ud83d\udc71",
+    ":cop:":    "\ud83d\udc6e",
+    ":man_with_gua_pi_mao:":    "\ud83d\udc72",
+    ":man_with_turban:":    "\ud83d\udc73",
+    ":construction_worker:":    "\ud83d\udc77",
+    ":helmet_with_cross:":  "\u26d1",
+    ":princess:":   "\ud83d\udc78",
+    ":guardsman:":  "\ud83d\udc82",
+    ":santa:":  "\ud83c\udf85",
+    ":angel:":  "\ud83d\udc7c",
+    ":spy:":    "\ud83d\udd75",
+    ":dancers:":    "\ud83d\udc6f",
+    ":massage:":    "\ud83d\udc86",
+    ":haircut:":    "\ud83d\udc87",
+    ":bride_with_veil:":    "\ud83d\udc70",
+    ":person_frowning:":    "\ud83d\ude4d",
+    ":person_with_pouting_face:":   "\ud83d\ude4e",
+    ":no_good:":    "\ud83d\ude45",
+    ":ok_woman:":   "\ud83d\ude46",
+    ":information_desk_person:":    "\ud83d\udc81",
+    ":raising_hand:":   "\ud83d\ude4b",
+    ":bow:":    "\ud83d\ude47",
+    ":raised_hands:":   "\ud83d\ude4c",
+    ":pray:":   "\ud83d\ude4f",
+    ":speaking_head:":  "\ud83d\udde3",
+    ":bust_in_silhouette:": "\ud83d\udc64",
+    ":busts_in_silhouette:":    "\ud83d\udc65",
+    ":walking:":    "\ud83d\udeb6",
+    ":runner:": "\ud83c\udfc3",
+    ":dancer:": "\ud83d\udc83",
+    ":levitate:":   "\ud83d\udd74",
+    ":couplekiss:": "\ud83d\udc8f",
+    ":couple_with_heart:":  "\ud83d\udc91",
+    ":family:": "\ud83d\udc6a",
+    ":couple:": "\ud83d\udc6b",
+    ":two_men_holding_hands:":  "\ud83d\udc6c",
+    ":two_women_holding_hands:":    "\ud83d\udc6d",
+    ":poop:":   "\ud83d\udcaa",
+    ":point_left:": "\ud83d\udc48",
+    ":point_right:":    "\ud83d\udc49",
+    ":point_up:":   "\u261d",
+    ":point_up_2:": "\ud83d\udc46",
+    ":middle_finger:":  "\ud83d\udd95",
+    ":point_down:": "\ud83d\udc47",
+    ":v:":  "\u270c",
+    ":vulcan:": "\ud83d\udd96",
+    ":metal:":  "\ud83e\udd18",
+    ":hand_splayed:":   "\ud83d\udd90",
+    ":fist:":   "\u270a",
+    ":raised_hand:":    "\u270b",
+    ":punch:":  "\ud83d\udc4a",
+    ":ok_hand:":    "\ud83d\udc4c",
+    ":thumbsup:":   "\ud83d\udc4d",
+    ":thumbsdown:": "\ud83d\udc4e",
+    ":wave:":   "\ud83d\udc4b",
+    ":clap:":   "\ud83d\udc4f",
+    ":open_hands:": "\ud83d\udc50",
+    ":writing_hand:":   "\u270d",
+    ":nail_care:":  "\ud83d\udc85",
+    ":footprints:": "\ud83d\udc63",
+    ":eyes:":   "\ud83d\udc40",
+    ":eye:":    "\ud83d\udc41",
+    ":ear:":    "\ud83d\udc42",
+    ":nose:":   "\ud83d\udc43",
+    ":tongue:": "\ud83d\udc45",
+    ":lips:":   "\ud83d\udc44",
+    ":kiss:":   "\ud83d\udc8b",
+    ":eyeglasses:": "\ud83d\udc53",
+    ":dark_sunglasses:":    "\ud83d\udd76",
+    ":necktie:":    "\ud83d\udc54",
+    ":shirt:":  "\ud83d\udc55",
+    ":jeans:":  "\ud83d\udc56",
+    ":dress:":  "\ud83d\udc57",
+    ":kimono:": "\ud83d\udc58",
+    ":bikini:": "\ud83d\udc59",
+    ":womans_clothes:": "\ud83d\udc5a",
+    ":purse:":  "\ud83d\udc5b",
+    ":handbag:":    "\ud83d\udc5c",
+    ":pouch:":  "\ud83d\udc5d",
+    ":school_satchel:": "\ud83c\udf92",
+    ":mans_shoe:":  "\ud83d\udc5e",
+    ":athletic_shoe:":  "\ud83d\udc5f",
+    ":high_heel:":  "\ud83d\udc60",
+    ":sandal:": "\ud83d\udc61",
+    ":boot:":   "\ud83d\udc62",
+    ":crown:":  "\ud83d\udc51",
+    ":womans_hat:": "\ud83d\udc52",
+    ":tophat:": "\ud83c\udfa9",
+    ":lipstick:":   "\ud83d\udc84",
+    ":ring:":   "\ud83d\udc8d",
+    ":japanese_ogre:":  "\ud83d\udc79",
+    ":japanese_goblin:":    "\ud83d\udc7a",
+    ":ghost:":  "\ud83d\udc7b",
+    ":skull:":  "\ud83d\udc80",
+    ":alien:":  "\ud83d\udc7d",
+    ":robot:":  "\ud83e\udd16",
+    ":dash:":   "\ud83d\udca9",
+    ":smile_cat:":  "\ud83d\ude38",
+    ":joy_cat:":    "\ud83d\ude39",
+    ":smiley_cat:": "\ud83d\ude3a",
+    ":heart_eyes_cat:": "\ud83d\ude3b",
+    ":smirk_cat:":  "\ud83d\ude3c",
+    ":kissing_cat:":    "\ud83d\ude3d",
+    ":pouting_cat:":    "\ud83d\ude3e",
+    ":crying_cat_face:":    "\ud83d\ude3f",
+    ":scream_cat:": "\ud83d\ude40",
+    ":closed_umbrella:":    "\ud83c\udf02",
+    ":mortar_board:":   "\ud83c\udf93",
+    ":briefcase:":  "\ud83d\udcbc",
+    ":family_mwg:": "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67",
+    ":family_mwgb:":    "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66",
+    ":family_mwbb:":    "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66",
+    ":family_mwgg:":    "\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67",
+    ":family_wwb:": "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66",
+    ":family_wwg:": "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67",
+    ":family_wwgb:":    "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc66",
+    ":family_wwbb:":    "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66",
+    ":family_wwgg:":    "\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d\udc67",
+    ":family_mmb:": "\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66",
+    ":family_mmg:": "\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67",
+    ":family_mmgb:":    "\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc66",
+    ":family_mmbb:":    "\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66",
+    ":family_mmgg:":    "\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d\udc67",
+    ":couple_ww:":  "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc69",
+    ":couple_mm:":  "\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68",
+    ":kiss_ww:":    "\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69",
+    ":kiss_mm:":    "\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68",
+    ":sweat_drops:":    "\ud83d\udca7",
+    ":boom:":   "\ud83d\udca6",
+    ":droplet:":    "\ud83d\udca8",
+    ":monkey_face:":    "\ud83d\udc35",
+    ":see_no_evil:":    "\ud83d\ude48",
+    ":hear_no_evil:":   "\ud83d\ude49",
+    ":speak_no_evil:":  "\ud83d\ude4a",
+    ":monkey:": "\ud83d\udc12",
+    ":dog:":    "\ud83d\udc36",
+    ":dog2:":   "\ud83d\udc15",
+    ":poodle:": "\ud83d\udc29",
+    ":wolf:":   "\ud83d\udc3a",
+    ":cat:":    "\ud83d\udc31",
+    ":cat2:":   "\ud83d\udc08",
+    ":lion_face:":  "\ud83e\udd81",
+    ":tiger:":  "\ud83d\udc2f",
+    ":tiger2:": "\ud83d\udc05",
+    ":leopard:":    "\ud83d\udc06",
+    ":horse:":  "\ud83d\udc34",
+    ":racehorse:":  "\ud83d\udc0e",
+    ":unicorn:":    "\ud83e\udd84",
+    ":cow:":    "\ud83d\udc2e",
+    ":ox:": "\ud83d\udc02",
+    ":water_buffalo:":  "\ud83d\udc03",
+    ":cow2:":   "\ud83d\udc04",
+    ":pig:":    "\ud83d\udc37",
+    ":pig2:":   "\ud83d\udc16",
+    ":boar:":   "\ud83d\udc17",
+    ":pig_nose:":   "\ud83d\udc3d",
+    ":ram:":    "\ud83d\udc0f",
+    ":sheep:":  "\ud83d\udc11",
+    ":goat:":   "\ud83d\udc10",
+    ":dromedary_camel:":    "\ud83d\udc2a",
+    ":camel:":  "\ud83d\udc2b",
+    ":elephant:":   "\ud83d\udc18",
+    ":mouse:":  "\ud83d\udc2d",
+    ":mouse2:": "\ud83d\udc01",
+    ":rat:":    "\ud83d\udc00",
+    ":hamster:":    "\ud83d\udc39",
+    ":rabbit:": "\ud83d\udc30",
+    ":rabbit2:":    "\ud83d\udc07",
+    ":chipmunk:":   "\ud83d\udc3f",
+    ":bear:":   "\ud83d\udc3b",
+    ":koala:":  "\ud83d\udc28",
+    ":panda_face:": "\ud83d\udc3c",
+    ":feet:":   "\ud83d\udc3e",
+    ":turkey:": "\ud83e\udd83",
+    ":chicken:":    "\ud83d\udc14",
+    ":rooster:":    "\ud83d\udc13",
+    ":hatching_chick:": "\ud83d\udc23",
+    ":baby_chick:": "\ud83d\udc24",
+    ":hatched_chick:":  "\ud83d\udc25",
+    ":bird:":   "\ud83d\udc26",
+    ":penguin:":    "\ud83d\udc27",
+    ":dove:":   "\ud83d\udd4a",
+    ":frog:":   "\ud83d\udc38",
+    ":crocodile:":  "\ud83d\udc0a",
+    ":turtle:": "\ud83d\udc22",
+    ":snake:":  "\ud83d\udc0d",
+    ":dragon_face:":    "\ud83d\udc32",
+    ":dragon:": "\ud83d\udc09",
+    ":whale:":  "\ud83d\udc33",
+    ":whale2:": "\ud83d\udc0b",
+    ":dolphin:":    "\ud83d\udc2c",
+    ":fish:":   "\ud83d\udc1f",
+    ":tropical_fish:":  "\ud83d\udc20",
+    ":blowfish:":   "\ud83d\udc21",
+    ":octopus:":    "\ud83d\udc19",
+    ":shell:":  "\ud83d\udc1a",
+    ":crab:":   "\ud83e\udd80",
+    ":snail:":  "\ud83d\udc0c",
+    ":bug:":    "\ud83d\udc1b",
+    ":ant:":    "\ud83d\udc1c",
+    ":bee:":    "\ud83d\udc1d",
+    ":beetle:": "\ud83d\udc1e",
+    ":spider:": "\ud83d\udd77",
+    ":spider_web:": "\ud83d\udd78",
+    ":scorpion:":   "\ud83e\udd82",
+    ":bouquet:":    "\ud83d\udc90",
+    ":cherry_blossom:": "\ud83c\udf38",
+    ":white_flower:":   "\ud83d\udcae",
+    ":rosette:":    "\ud83c\udff5",
+    ":rose:":   "\ud83c\udf39",
+    ":hibiscus:":   "\ud83c\udf3a",
+    ":sunflower:":  "\ud83c\udf3b",
+    ":blossom:":    "\ud83c\udf3c",
+    ":tulip:":  "\ud83c\udf37",
+    ":shamrock:":   "\u2618",
+    ":seedling:":   "\ud83c\udf31",
+    ":evergreen_tree:": "\ud83c\udf32",
+    ":deciduous_tree:": "\ud83c\udf33",
+    ":palm_tree:":  "\ud83c\udf34",
+    ":cactus:": "\ud83c\udf35",
+    ":ear_of_rice:":    "\ud83c\udf3e",
+    ":herb:":   "\ud83c\udf3f",
+    ":four_leaf_clover:":   "\ud83c\udf40",
+    ":maple_leaf:": "\ud83c\udf41",
+    ":fallen_leaf:":    "\ud83c\udf42",
+    ":leaves:": "\ud83c\udf43",
+    ":earth_africa:":   "\ud83c\udf0d",
+    ":earth_americas:": "\ud83c\udf0e",
+    ":earth_asia:": "\ud83c\udf0f",
+    ":globe_with_meridians:":   "\ud83c\udf10",
+    ":ocean:":  "\ud83c\udf0a",
+    ":new_moon:":   "\ud83c\udf11",
+    ":waxing_crescent_moon:":   "\ud83c\udf12",
+    ":first_quarter_moon:": "\ud83c\udf13",
+    ":waxing_gibbous_moon:":    "\ud83c\udf14",
+    ":full_moon:":  "\ud83c\udf15",
+    ":waning_gibbous_moon:":    "\ud83c\udf16",
+    ":last_quarter_moon:":  "\ud83c\udf17",
+    ":waning_crescent_moon:":   "\ud83c\udf18",
+    ":crescent_moon:":  "\ud83c\udf19",
+    ":new_moon_with_face:": "\ud83c\udf1a",
+    ":first_quarter_moon_with_face:":   "\ud83c\udf1b",
+    ":last_quarter_moon_with_face:":    "\ud83c\udf1c",
+    ":sunny:":  "\u2600",
+    ":full_moon_with_face:":    "\ud83c\udf1d",
+    ":sun_with_face:":  "\ud83c\udf1e",
+    ":cloud:":  "\u2601",
+    ":partly_sunny:":   "\u26c5",
+    ":thunder_cloud_rain:": "\u26c8",
+    ":white_sun_small_cloud:":  "\ud83c\udf24",
+    ":white_sun_cloud:":    "\ud83c\udf25",
+    ":white_sun_rain_cloud:":   "\ud83c\udf26",
+    ":cloud_rain:": "\ud83c\udf27",
+    ":cloud_snow:": "\ud83c\udf28",
+    ":cloud_lightning:":    "\ud83c\udf29",
+    ":cloud_tornado:":  "\ud83c\udf2a",
+    ":fog:":    "\ud83c\udf2b",
+    ":wind_blowing_face:":  "\ud83c\udf2c",
+    ":umbrella2:":  "\u2602",
+    ":umbrella:":   "\u2614",
+    ":snowflake:":  "\u2744",
+    ":snowman2:":   "\u2603",
+    ":star2:":  "\ud83c\udf1f",
+    ":stars:":  "\ud83c\udf20",
+    ":comet:":  "\u2604",
+    ":fire:":   "\ud83d\udd25",
+    ":zap:":    "\u26a1",
+    ":star:":   "\u2b50",
+    ":grapes:": "\ud83c\udf47",
+    ":melon:":  "\ud83c\udf48",
+    ":watermelon:": "\ud83c\udf49",
+    ":tangerine:":  "\ud83c\udf4a",
+    ":lemon:":  "\ud83c\udf4b",
+    ":banana:": "\ud83c\udf4c",
+    ":pineapple:":  "\ud83c\udf4d",
+    ":apple:":  "\ud83c\udf4e",
+    ":green_apple:":    "\ud83c\udf4f",
+    ":pear:":   "\ud83c\udf50",
+    ":peach:":  "\ud83c\udf51",
+    ":cherries:":   "\ud83c\udf52",
+    ":strawberry:": "\ud83c\udf53",
+    ":tomato:": "\ud83c\udf45",
+    ":eggplant:":   "\ud83c\udf46",
+    ":corn:":   "\ud83c\udf3d",
+    ":hot_pepper:": "\ud83c\udf36",
+    ":mushroom:":   "\ud83c\udf44",
+    ":chestnut:":   "\ud83c\udf30",
+    ":bread:":  "\ud83c\udf5e",
+    ":cheese:": "\ud83e\uddc0",
+    ":meat_on_bone:":   "\ud83c\udf56",
+    ":poultry_leg:":    "\ud83c\udf57",
+    ":hamburger:":  "\ud83c\udf54",
+    ":fries:":  "\ud83c\udf5f",
+    ":pizza:":  "\ud83c\udf55",
+    ":hotdog:": "\ud83c\udf2d",
+    ":taco:":   "\ud83c\udf2e",
+    ":burrito:":    "\ud83c\udf2f",
+    ":popcorn:":    "\ud83c\udf7f",
+    ":stew:":   "\ud83c\udf72",
+    ":bento:":  "\ud83c\udf71",
+    ":rice_cracker:":   "\ud83c\udf58",
+    ":rice_ball:":  "\ud83c\udf59",
+    ":rice:":   "\ud83c\udf5a",
+    ":curry:":  "\ud83c\udf5b",
+    ":ramen:":  "\ud83c\udf5c",
+    ":spaghetti:":  "\ud83c\udf5d",
+    ":sweet_potato:":   "\ud83c\udf60",
+    ":oden:":   "\ud83c\udf62",
+    ":sushi:":  "\ud83c\udf63",
+    ":fried_shrimp:":   "\ud83c\udf64",
+    ":fish_cake:":  "\ud83c\udf65",
+    ":dango:":  "\ud83c\udf61",
+    ":icecream:":   "\ud83c\udf66",
+    ":shaved_ice:": "\ud83c\udf67",
+    ":ice_cream:":  "\ud83c\udf68",
+    ":doughnut:":   "\ud83c\udf69",
+    ":cookie:": "\ud83c\udf6a",
+    ":birthday:":   "\ud83c\udf82",
+    ":cake:":   "\ud83c\udf70",
+    ":chocolate_bar:":  "\ud83c\udf6b",
+    ":candy:":  "\ud83c\udf6c",
+    ":lollipop:":   "\ud83c\udf6d",
+    ":custard:":    "\ud83c\udf6e",
+    ":honey_pot:":  "\ud83c\udf6f",
+    ":baby_bottle:":    "\ud83c\udf7c",
+    ":coffee:": "\u2615",
+    ":tea:":    "\ud83c\udf75",
+    ":sake:":   "\ud83c\udf76",
+    ":champagne:":  "\ud83c\udf7e",
+    ":wine_glass:": "\ud83c\udf77",
+    ":cocktail:":   "\ud83c\udf78",
+    ":tropical_drink:": "\ud83c\udf79",
+    ":beer:":   "\ud83c\udf7a",
+    ":beers:":  "\ud83c\udf7b",
+    ":fork_knife_plate:":   "\ud83c\udf7d",
+    ":fork_and_knife:": "\ud83c\udf74",
+    ":egg:":    "\ud83c\udf73",
+    ":space_invader:":  "\ud83d\udc7e",
+    ":circus_tent:":    "\ud83c\udfaa",
+    ":performing_arts:":    "\ud83c\udfad",
+    ":art:":    "\ud83c\udfa8",
+    ":slot_machine:":   "\ud83c\udfb0",
+    ":rowboat:":    "\ud83d\udea3",
+    ":bath:":   "\ud83d\udec0",
+    ":dart:":   "\ud83c\udfaf",
+    ":military_medal:": "\ud83c\udf96",
+    ":reminder_ribbon:":    "\ud83c\udf97",
+    ":tickets:":    "\ud83c\udf9f",
+    ":ticket:": "\ud83c\udfab",
+    ":soccer:": "\u26bd",
+    ":baseball:":   "\u26be",
+    ":basketball:": "\ud83c\udfc0",
+    ":football:":   "\ud83c\udfc8",
+    ":rugby_football:": "\ud83c\udfc9",
+    ":tennis:": "\ud83c\udfbe",
+    ":8ball:":  "\ud83c\udfb1",
+    ":bowling:":    "\ud83c\udfb3",
+    ":golf:":   "\u26f3",
+    ":golfer:": "\ud83c\udfcc",
+    ":ice_skate:":  "\u26f8",
+    ":fishing_pole_and_fish:":  "\ud83c\udfa3",
+    ":running_shirt_with_sash:":    "\ud83c\udfbd",
+    ":ski:":    "\ud83c\udfbf",
+    ":skier:":  "\u26f7",
+    ":snowboarder:":    "\ud83c\udfc2",
+    ":surfer:": "\ud83c\udfc4",
+    ":horse_racing:":   "\ud83c\udfc7",
+    ":swimmer:":    "\ud83c\udfca",
+    ":basketball_player:":  "\u26f9",
+    ":lifter:": "\ud83c\udfcb",
+    ":bicyclist:":  "\ud83d\udeb4",
+    ":mountain_bicyclist:": "\ud83d\udeb5",
+    ":medal:":  "\ud83c\udfc5",
+    ":trophy:": "\ud83c\udfc6",
+    ":cricket:":    "\ud83c\udfcf",
+    ":volleyball:": "\ud83c\udfd0",
+    ":field_hockey:":   "\ud83c\udfd1",
+    ":hockey:": "\ud83c\udfd2",
+    ":ping_pong:":  "\ud83c\udfd3",
+    ":badminton:":  "\ud83c\udff8",
+    ":video_game:": "\ud83c\udfae",
+    ":game_die:":   "\ud83c\udfb2",
+    ":saxophone:":  "\ud83c\udfb7",
+    ":guitar:": "\ud83c\udfb8",
+    ":trumpet:":    "\ud83c\udfba",
+    ":violin:": "\ud83c\udfbb",
+    ":clapper:":    "\ud83c\udfac",
+    ":bow_and_arrow:":  "\ud83c\udff9",
+    ":mountain_snow:":  "\ud83c\udfd4",
+    ":mountain:":   "\u26f0",
+    ":volcano:":    "\ud83c\udf0b",
+    ":mount_fuji:": "\ud83d\uddfb",
+    ":camping:":    "\ud83c\udfd5",
+    ":beach:":  "\ud83c\udfd6",
+    ":desert:": "\ud83c\udfdc",
+    ":island:": "\ud83c\udfdd",
+    ":park:":   "\ud83c\udfde",
+    ":stadium:":    "\ud83c\udfdf",
+    ":classical_building:": "\ud83c\udfdb",
+    ":construction_site:":  "\ud83c\udfd7",
+    ":homes:":  "\ud83c\udfd8",
+    ":cityscape:":  "\ud83c\udfd9",
+    ":house_abandoned:":    "\ud83c\udfda",
+    ":house:":  "\ud83c\udfe0",
+    ":house_with_garden:":  "\ud83c\udfe1",
+    ":church:": "\u26ea",
+    ":kaaba:":  "\ud83d\udd4b",
+    ":mosque:": "\ud83d\udd4c",
+    ":synagogue:":  "\ud83d\udd4d",
+    ":office:": "\ud83c\udfe2",
+    ":post_office:":    "\ud83c\udfe3",
+    ":european_post_office:":   "\ud83c\udfe4",
+    ":hospital:":   "\ud83c\udfe5",
+    ":bank:":   "\ud83c\udfe6",
+    ":hotel:":  "\ud83c\udfe8",
+    ":love_hotel:": "\ud83c\udfe9",
+    ":convenience_store:":  "\ud83c\udfea",
+    ":school:": "\ud83c\udfeb",
+    ":department_store:":   "\ud83c\udfec",
+    ":factory:":    "\ud83c\udfed",
+    ":japanese_castle:":    "\ud83c\udfef",
+    ":european_castle:":    "\ud83c\udff0",
+    ":wedding:":    "\ud83d\udc92",
+    ":tokyo_tower:":    "\ud83d\uddfc",
+    ":statue_of_liberty:":  "\ud83d\uddfd",
+    ":fountain:":   "\u26f2",
+    ":foggy:":  "\ud83c\udf01",
+    ":night_with_stars:":   "\ud83c\udf03",
+    ":city_dusk:":  "\ud83c\udf06",
+    ":city_sunset:":    "\ud83c\udf07",
+    ":bridge_at_night:":    "\ud83c\udf09",
+    ":moyai:":  "\ud83d\uddff",
+    ":milky_way:":  "\ud83c\udf0c",
+    ":carousel_horse:": "\ud83c\udfa0",
+    ":ferris_wheel:":   "\ud83c\udfa1",
+    ":roller_coaster:": "\ud83c\udfa2",
+    ":steam_locomotive:":   "\ud83d\ude82",
+    ":railway_car:":    "\ud83d\ude83",
+    ":bullettrain_side:":   "\ud83d\ude84",
+    ":bullettrain_front:":  "\ud83d\ude85",
+    ":train2:": "\ud83d\ude86",
+    ":metro:":  "\ud83d\ude87",
+    ":light_rail:": "\ud83d\ude88",
+    ":station:":    "\ud83d\ude89",
+    ":tram:":   "\ud83d\ude8a",
+    ":monorail:":   "\ud83d\ude9d",
+    ":mountain_railway:":   "\ud83d\ude9e",
+    ":train:":  "\ud83d\ude8b",
+    ":bus:":    "\ud83d\ude8c",
+    ":oncoming_bus:":   "\ud83d\ude8d",
+    ":trolleybus:": "\ud83d\ude8e",
+    ":busstop:":    "\ud83d\ude8f",
+    ":minibus:":    "\ud83d\ude90",
+    ":ambulance:":  "\ud83d\ude91",
+    ":fire_engine:":    "\ud83d\ude92",
+    ":police_car:": "\ud83d\ude93",
+    ":oncoming_police_car:":    "\ud83d\ude94",
+    ":taxi:":   "\ud83d\ude95",
+    ":oncoming_taxi:":  "\ud83d\ude96",
+    ":red_car:":    "\ud83d\ude97",
+    ":oncoming_automobile:":    "\ud83d\ude98",
+    ":truck:":  "\ud83d\ude9a",
+    ":articulated_lorry:":  "\ud83d\ude9b",
+    ":tractor:":    "\ud83d\ude9c",
+    ":bike:":   "\ud83d\udeb2",
+    ":fuelpump:":   "\u26fd",
+    ":railway_track:":  "\ud83d\udee4",
+    ":rotating_light:": "\ud83d\udea8",
+    ":anchor:": "\u2693",
+    ":sailboat:":   "\u26f5",
+    ":speedboat:":  "\ud83d\udea4",
+    ":cruise_ship:":    "\ud83d\udef3",
+    ":ferry:":  "\u26f4",
+    ":motorboat:":  "\ud83d\udee5",
+    ":ship:":   "\ud83d\udea2",
+    ":airplane:":   "\u2708",
+    ":airplane_small:": "\ud83d\udee9",
+    ":airplane_departure:": "\ud83d\udeeb",
+    ":airplane_arriving:":  "\ud83d\udeec",
+    ":seat:":   "\ud83d\udcba",
+    ":helicopter:": "\ud83d\ude81",
+    ":suspension_railway:": "\ud83d\ude9f",
+    ":mountain_cableway:":  "\ud83d\udea0",
+    ":aerial_tramway:": "\ud83d\udea1",
+    ":rocket:": "\ud83d\ude80",
+    ":satellite_orbital:":  "\ud83d\udef0",
+    ":traffic_light:":  "\ud83d\udea5",
+    ":vertical_traffic_light:": "\ud83d\udea6",
+    ":construction:":   "\ud83d\udea7",
+    ":passport_control:":   "\ud83d\udec2",
+    ":customs:":    "\ud83d\udec3",
+    ":baggage_claim:":  "\ud83d\udec4",
+    ":left_luggage:":   "\ud83d\udec5",
+    ":race_car:":   "\ud83c\udfce",
+    ":motorcycle:": "\ud83c\udfcd",
+    ":yen:":    "\ud83d\udcb4",
+    ":dollar:": "\ud83d\udcb5",
+    ":euro:":   "\ud83d\udcb6",
+    ":pound:":  "\ud83d\udcb7",
+    ":shinto_shrine:":  "\u26e9",
+    ":love_letter:":    "\ud83d\udc8c",
+    ":bomb:":   "\ud83d\udca3",
+    ":hole:":   "\ud83d\udd73",
+    ":shopping_bags:":  "\ud83d\udecd",
+    ":prayer_beads:":   "\ud83d\udcff",
+    ":gem:":    "\ud83d\udc8e",
+    ":skull_crossbones:":   "\u2620",
+    ":amphora:":    "\ud83c\udffa",
+    ":map:":    "\ud83d\uddfa",
+    ":barber:": "\ud83d\udc88",
+    ":frame_photo:":    "\ud83d\uddbc",
+    ":oil:":    "\ud83d\udee2",
+    ":bellhop:":    "\ud83d\udece",
+    ":door:":   "\ud83d\udeaa",
+    ":sleeping_accommodation:": "\ud83d\udecc",
+    ":bed:":    "\ud83d\udecf",
+    ":couch:":  "\ud83d\udecb",
+    ":toilet:": "\ud83d\udebd",
+    ":shower:": "\ud83d\udebf",
+    ":bathtub:":    "\ud83d\udec1",
+    ":hourglass:":  "\u231b",
+    ":hourglass_flowing_sand:": "\u23f3",
+    ":watch:":  "\u231a",
+    ":alarm_clock:":    "\u23f0",
+    ":stopwatch:":  "\u23f1",
+    ":timer:":  "\u23f2",
+    ":clock:":  "\ud83d\udd70",
+    ":thermometer:":    "\ud83c\udf21",
+    ":beach_umbrella:": "\u26f1",
+    ":balloon:":    "\ud83c\udf88",
+    ":tada:":   "\ud83c\udf89",
+    ":confetti_ball:":  "\ud83c\udf8a",
+    ":crossed_flags:":  "\ud83c\udf8c",
+    ":dolls:":  "\ud83c\udf8e",
+    ":flags:":  "\ud83c\udf8f",
+    ":wind_chime:": "\ud83c\udf90",
+    ":ribbon:": "\ud83c\udf80",
+    ":gift:":   "\ud83c\udf81",
+    ":film_frames:":    "\ud83c\udf9e",
+    ":label:":  "\ud83c\udff7",
+    ":joystick:":   "\ud83d\udd79",
+    ":postal_horn:":    "\ud83d\udcef",
+    ":microphone2:":    "\ud83c\udf99",
+    ":level_slider:":   "\ud83c\udf9a",
+    ":control_knobs:":  "\ud83c\udf9b",
+    ":radio:":  "\ud83d\udcfb",
+    ":iphone:": "\ud83d\udcf1",
+    ":calling:":    "\ud83d\udcf2",
+    ":telephone:":  "\u260e",
+    ":telephone_receiver:": "\ud83d\udcde",
+    ":pager:":  "\ud83d\udcdf",
+    ":fax:":    "\ud83d\udce0",
+    ":battery:":    "\ud83d\udd0b",
+    ":electric_plug:":  "\ud83d\udd0c",
+    ":computer:":   "\ud83d\udcbb",
+    ":desktop:":    "\ud83d\udda5",
+    ":printer:":    "\ud83d\udda8",
+    ":keyboard:":   "\u2328",
+    ":mouse_three_button:": "\ud83d\uddb1",
+    ":trackball:":  "\ud83d\uddb2",
+    ":minidisc:":   "\ud83d\udcbd",
+    ":floppy_disk:":    "\ud83d\udcbe",
+    ":cd:": "\ud83d\udcbf",
+    ":dvd:":    "\ud83d\udcc0",
+    ":movie_camera:":   "\ud83c\udfa5",
+    ":projector:":  "\ud83d\udcfd",
+    ":tv:": "\ud83d\udcfa",
+    ":camera:": "\ud83d\udcf7",
+    ":camera_with_flash:":  "\ud83d\udcf8",
+    ":video_camera:":   "\ud83d\udcf9",
+    ":vhs:":    "\ud83d\udcfc",
+    ":mag:":    "\ud83d\udd0d",
+    ":mag_right:":  "\ud83d\udd0e",
+    ":microscope:": "\ud83d\udd2c",
+    ":telescope:":  "\ud83d\udd2d",
+    ":satellite:":  "\ud83d\udce1",
+    ":candle:": "\ud83d\udd6f",
+    ":bulb:":   "\ud83d\udca1",
+    ":flashlight:": "\ud83d\udd26",
+    ":izakaya_lantern:":    "\ud83c\udfee",
+    ":notebook_with_decorative_cover:": "\ud83d\udcd4",
+    ":closed_book:":    "\ud83d\udcd5",
+    ":book:":   "\ud83d\udcd6",
+    ":green_book:": "\ud83d\udcd7",
+    ":blue_book:":  "\ud83d\udcd8",
+    ":orange_book:":    "\ud83d\udcd9",
+    ":books:":  "\ud83d\udcda",
+    ":notebook:":   "\ud83d\udcd3",
+    ":page_with_curl:": "\ud83d\udcc3",
+    ":scroll:": "\ud83d\udcdc",
+    ":page_facing_up:": "\ud83d\udcc4",
+    ":newspaper:":  "\ud83d\udcf0",
+    ":newspaper2:": "\ud83d\uddde",
+    ":bookmark_tabs:":  "\ud83d\udcd1",
+    ":bookmark:":   "\ud83d\udd16",
+    ":moneybag:":   "\ud83d\udcb0",
+    ":money_with_wings:":   "\ud83d\udcb8",
+    ":credit_card:":    "\ud83d\udcb3",
+    ":envelope:":   "\u2709",
+    ":e-mail:": "\ud83d\udce7",
+    ":incoming_envelope:":  "\ud83d\udce8",
+    ":envelope_with_arrow:":    "\ud83d\udce9",
+    ":outbox_tray:":    "\ud83d\udce4",
+    ":inbox_tray:": "\ud83d\udce5",
+    ":package:":    "\ud83d\udce6",
+    ":mailbox:":    "\ud83d\udceb",
+    ":mailbox_closed:": "\ud83d\udcea",
+    ":mailbox_with_mail:":  "\ud83d\udcec",
+    ":mailbox_with_no_mail:":   "\ud83d\udced",
+    ":postbox:":    "\ud83d\udcee",
+    ":ballot_box:": "\ud83d\uddf3",
+    ":pencil2:":    "\u270f",
+    ":black_nib:":  "\u2712",
+    ":pen_fountain:":   "\ud83d\udd8b",
+    ":pen_ballpoint:":  "\ud83d\udd8a",
+    ":paintbrush:": "\ud83d\udd8c",
+    ":crayon:": "\ud83d\udd8d",
+    ":pencil:": "\ud83d\udcdd",
+    ":file_folder:":    "\ud83d\udcc1",
+    ":open_file_folder:":   "\ud83d\udcc2",
+    ":dividers:":   "\ud83d\uddc2",
+    ":date:":   "\ud83d\udcc5",
+    ":calendar:":   "\ud83d\udcc6",
+    ":notepad_spiral:": "\ud83d\uddd2",
+    ":calendar_spiral:":    "\ud83d\uddd3",
+    ":card_index:": "\ud83d\udcc7",
+    ":chart_with_upwards_trend:":   "\ud83d\udcc8",
+    ":chart_with_downwards_trend:": "\ud83d\udcc9",
+    ":bar_chart:":  "\ud83d\udcca",
+    ":clipboard:":  "\ud83d\udccb",
+    ":pushpin:":    "\ud83d\udccc",
+    ":round_pushpin:":  "\ud83d\udccd",
+    ":paperclip:":  "\ud83d\udcce",
+    ":paperclips:": "\ud83d\udd87",
+    ":straight_ruler:": "\ud83d\udccf",
+    ":triangular_ruler:":   "\ud83d\udcd0",
+    ":scissors:":   "\u2702",
+    ":card_box:":   "\ud83d\uddc3",
+    ":file_cabinet:":   "\ud83d\uddc4",
+    ":wastebasket:":    "\ud83d\uddd1",
+    ":lock:":   "\ud83d\udd12",
+    ":unlock:": "\ud83d\udd13",
+    ":lock_with_ink_pen:":  "\ud83d\udd0f",
+    ":closed_lock_with_key:":   "\ud83d\udd10",
+    ":key:":    "\ud83d\udd11",
+    ":key2:":   "\ud83d\udddd",
+    ":hammer:": "\ud83d\udd28",
+    ":pick:":   "\u26cf",
+    ":hammer_pick:":    "\u2692",
+    ":tools:":  "\ud83d\udee0",
+    ":wrench:": "\ud83d\udd27",
+    ":nut_and_bolt:":   "\ud83d\udd29",
+    ":gear:":   "\u2699",
+    ":compression:":    "\ud83d\udddc",
+    ":alembic:":    "\u2697",
+    ":scales:": "\u2696",
+    ":link:":   "\ud83d\udd17",
+    ":chains:": "\u26d3",
+    ":syringe:":    "\ud83d\udc89",
+    ":pill:":   "\ud83d\udc8a",
+    ":dagger:": "\ud83d\udde1",
+    ":knife:":  "\ud83d\udd2a",
+    ":crossed_swords:": "\u2694",
+    ":gun:":    "\ud83d\udd2b",
+    ":shield:": "\ud83d\udee1",
+    ":smoking:":    "\ud83d\udeac",
+    ":coffin:": "\u26b0",
+    ":urn:":    "\u26b1",
+    ":flag_white:": "\ud83c\udff3",
+    ":flag_black:": "\ud83c\udff4",
+    ":triangular_flag_on_post:":    "\ud83d\udea9",
+    ":crystal_ball:":   "\ud83d\udd2e",
+    ":cupid:":  "\ud83d\udc98",
+    ":heart:":  "\u2764",
+    ":heartbeat:":  "\ud83d\udc93",
+    ":broken_heart:":   "\ud83d\udc94",
+    ":two_hearts:": "\ud83d\udc95",
+    ":sparkling_heart:":    "\ud83d\udc96",
+    ":heartpulse:": "\ud83d\udc97",
+    ":blue_heart:": "\ud83d\udc99",
+    ":green_heart:":    "\ud83d\udc9a",
+    ":yellow_heart:":   "\ud83d\udc9b",
+    ":purple_heart:":   "\ud83d\udc9c",
+    ":gift_heart:": "\ud83d\udc9d",
+    ":revolving_hearts:":   "\ud83d\udc9e",
+    ":heart_decoration:":   "\ud83d\udc9f",
+    ":heart_exclamation:":  "\u2763",
+    ":zzz:":    "\ud83d\udca4",
+    ":anger:":  "\ud83d\udca2",
+    ":muscle:": "\ud83d\udcac",
+    ":thought_balloon:":    "\ud83d\udcad",
+    ":anger_right:":    "\ud83d\uddef",
+    ":place_of_worship:":   "\ud83d\uded0",
+    ":hotsprings:": "\u2668",
+    ":no_bicycles:":    "\ud83d\udeb3",
+    ":trident:":    "\ud83d\udd31",
+    ":atm:":    "\ud83c\udfe7",
+    ":put_litter_in_its_place:":    "\ud83d\udeae",
+    ":no_entry_sign:":  "\ud83d\udeab",
+    ":no_smoking:": "\ud83d\udead",
+    ":do_not_litter:":  "\ud83d\udeaf",
+    ":potable_water:":  "\ud83d\udeb0",
+    ":non-potable_water:":  "\ud83d\udeb1",
+    ":no_pedestrians:": "\ud83d\udeb7",
+    ":children_crossing:":  "\ud83d\udeb8",
+    ":wheelchair:": "\u267f",
+    ":mens:":   "\ud83d\udeb9",
+    ":womens:": "\ud83d\udeba",
+    ":restroom:":   "\ud83d\udebb",
+    ":baby_symbol:":    "\ud83d\udebc",
+    ":wc:": "\ud83d\udebe",
+    ":warning:":    "\u26a0",
+    ":no_entry:":   "\u26d4",
+    ":clock12:":    "\ud83d\udd5b",
+    ":clock1230:":  "\ud83d\udd67",
+    ":clock1:": "\ud83d\udd50",
+    ":clock130:":   "\ud83d\udd5c",
+    ":clock2:": "\ud83d\udd51",
+    ":clock230:":   "\ud83d\udd5d",
+    ":clock3:": "\ud83d\udd52",
+    ":clock330:":   "\ud83d\udd5e",
+    ":clock4:": "\ud83d\udd53",
+    ":clock430:":   "\ud83d\udd5f",
+    ":clock5:": "\ud83d\udd54",
+    ":clock530:":   "\ud83d\udd60",
+    ":clock6:": "\ud83d\udd55",
+    ":clock630:":   "\ud83d\udd61",
+    ":clock7:": "\ud83d\udd56",
+    ":clock730:":   "\ud83d\udd62",
+    ":clock8:": "\ud83d\udd57",
+    ":clock830:":   "\ud83d\udd63",
+    ":clock9:": "\ud83d\udd58",
+    ":clock930:":   "\ud83d\udd64",
+    ":clock10:":    "\ud83d\udd59",
+    ":clock1030:":  "\ud83d\udd65",
+    ":clock11:":    "\ud83d\udd5a",
+    ":clock1130:":  "\ud83d\udd66",
+    ":aries:":  "\u2648",
+    ":taurus:": "\u2649",
+    ":gemini:": "\u264a",
+    ":cancer:": "\u264b",
+    ":leo:":    "\u264c",
+    ":virgo:":  "\u264d",
+    ":libra:":  "\u264e",
+    ":scorpius:":   "\u264f",
+    ":sagittarius:":    "\u2650",
+    ":capricorn:":  "\u2651",
+    ":aquarius:":   "\u2652",
+    ":pisces:": "\u2653",
+    ":ophiuchus:":  "\u26ce",
+    ":cyclone:":    "\ud83c\udf00",
+    ":menorah:":    "\ud83d\udd4e",
+    ":flower_playing_cards:":   "\ud83c\udfb4",
+    ":spades:": "\u2660",
+    ":hearts:": "\u2665",
+    ":diamonds:":   "\u2666",
+    ":clubs:":  "\u2663",
+    ":mahjong:":    "\ud83c\udc04",
+    ":mute:":   "\ud83d\udd07",
+    ":speaker:":    "\ud83d\udd08",
+    ":sound:":  "\ud83d\udd09",
+    ":loud_sound:": "\ud83d\udd0a",
+    ":loudspeaker:":    "\ud83d\udce2",
+    ":mega:":   "\ud83d\udce3",
+    ":bell:":   "\ud83d\udd14",
+    ":no_bell:":    "\ud83d\udd15",
+    ":twisted_rightwards_arrows:":  "\ud83d\udd00",
+    ":repeat:": "\ud83d\udd01",
+    ":repeat_one:": "\ud83d\udd02",
+    ":arrow_forward:":  "\u25b6",
+    ":fast_forward:":   "\u23e9",
+    ":arrow_backward:": "\u25c0",
+    ":rewind:": "\u23ea",
+    ":arrow_up_small:": "\ud83d\udd3c",
+    ":arrow_double_up:":    "\u23eb",
+    ":arrow_down_small:":   "\ud83d\udd3d",
+    ":arrow_double_down:":  "\u23ec",
+    ":stop_button:":    "\u23f9",
+    ":vibration_mode:": "\ud83d\udcf3",
+    ":mobile_phone_off:":   "\ud83d\udcf4",
+    ":hash:":   "#\u20e3",
+    ":zero:":   "0\u20e3",
+    ":one:":    "1\u20e3",
+    ":two:":    "2\u20e3",
+    ":three:":  "3\u20e3",
+    ":four:":   "4\u20e3",
+    ":five:":   "5\u20e3",
+    ":six:":    "6\u20e3",
+    ":seven:":  "7\u20e3",
+    ":eight:":  "8\u20e3",
+    ":nine:":   "9\u20e3",
+    ":ten:":    "\ud83d\udd1f",
+    ":signal_strength:":    "\ud83d\udcf6",
+    ":cinema:": "\ud83c\udfa6",
+    ":low_brightness:": "\ud83d\udd05",
+    ":high_brightness:":    "\ud83d\udd06",
+    ":arrow_up:":   "\u2b06",
+    ":arrow_upper_right:":  "\u2197",
+    ":arrow_right:":    "\u27a1",
+    ":arrow_lower_right:":  "\u2198",
+    ":arrow_down:": "\u2b07",
+    ":arrow_lower_left:":   "\u2199",
+    ":arrow_left:": "\u2b05",
+    ":arrow_upper_left:":   "\u2196",
+    ":arrow_up_down:":  "\u2195",
+    ":left_right_arrow:":   "\u2194",
+    ":leftwards_arrow_with_hook:":  "\u21a9",
+    ":arrow_right_hook:":   "\u21aa",
+    ":arrow_heading_up:":   "\u2934",
+    ":arrow_heading_down:": "\u2935",
+    ":arrows_clockwise:":   "\ud83d\udd03",
+    ":arrows_counterclockwise:":    "\ud83d\udd04",
+    ":back:":   "\ud83d\udd19",
+    ":end:":    "\ud83d\udd1a",
+    ":on:": "\ud83d\udd1b",
+    ":soon:":   "\ud83d\udd1c",
+    ":top:":    "\ud83d\udd1d",
+    ":beginner:":   "\ud83d\udd30",
+    ":atom:":   "\u269b",
+    ":om_symbol:":  "\ud83d\udd49",
+    ":star_of_david:":  "\u2721",
+    ":wheel_of_dharma:":    "\u2638",
+    ":yin_yang:":   "\u262f",
+    ":cross:":  "\u271d",
+    ":orthodox_cross:": "\u2626",
+    ":star_and_crescent:":  "\u262a",
+    ":peace:":  "\u262e",
+    ":six_pointed_star:":   "\ud83d\udd2f",
+    ":recycle:":    "\u267b",
+    ":radioactive:":    "\u2622",
+    ":biohazard:":  "\u2623",
+    ":o:":  "\u2b55",
+    ":white_check_mark:":   "\u2705",
+    ":ballot_box_with_check:":  "\u2611",
+    ":heavy_check_mark:":   "\u2714",
+    ":heavy_multiplication_x:": "\u2716",
+    ":x:":  "\u274c",
+    ":negative_squared_cross_mark:":    "\u274e",
+    ":heavy_plus_sign:":    "\u2795",
+    ":heavy_minus_sign:":   "\u2796",
+    ":heavy_division_sign:":    "\u2797",
+    ":curly_loop:": "\u27b0",
+    ":loop:":   "\u27bf",
+    ":part_alternation_mark:":  "\u303d",
+    ":eight_spoked_asterisk:":  "\u2733",
+    ":eight_pointed_black_star:":   "\u2734",
+    ":sparkle:":    "\u2747",
+    ":bangbang:":   "\u203c",
+    ":interrobang:":    "\u2049",
+    ":question:":   "\u2753",
+    ":grey_question:":  "\u2754",
+    ":grey_exclamation:":   "\u2755",
+    ":exclamation:":    "\u2757",
+    ":copyright:":  "\u00a9",
+    ":registered:": "\u00ae",
+    ":tm:": "\u2122",
+    ":underage:":   "\ud83d\udd1e",
+    ":capital_abcd:":   "\ud83d\udd20",
+    ":abcd:":   "\ud83d\udd21",
+    ":1234:":   "\ud83d\udd22",
+    ":symbols:":    "\ud83d\udd23",
+    ":abc:":    "\ud83d\udd24",
+    ":a:":  "\ud83c\udd70",
+    ":ab:": "\ud83c\udd8e",
+    ":b:":  "\ud83c\udd71",
+    ":cl:": "\ud83c\udd91",
+    ":cool:":   "\ud83c\udd92",
+    ":free:":   "\ud83c\udd93",
+    ":information_source:": "\u2139",
+    ":id:": "\ud83c\udd94",
+    ":m:":  "\u24c2",
+    ":new:":    "\ud83c\udd95",
+    ":ng:": "\ud83c\udd96",
+    ":o2:": "\ud83c\udd7e",
+    ":ok:": "\ud83c\udd97",
+    ":parking:":    "\ud83c\udd7f",
+    ":sos:":    "\ud83c\udd98",
+    ":up:": "\ud83c\udd99",
+    ":vs:": "\ud83c\udd9a",
+    ":koko:":   "\ud83c\ude01",
+    ":sa:": "\ud83c\ude02",
+    ":u6708:":  "\ud83c\ude37",
+    ":u6709:":  "\ud83c\ude36",
+    ":u6307:":  "\ud83c\ude2f",
+    ":ideograph_advantage:":    "\ud83c\ude50",
+    ":u5272:":  "\ud83c\ude39",
+    ":u7121:":  "\ud83c\ude1a",
+    ":u7981:":  "\ud83c\ude32",
+    ":accept:": "\ud83c\ude51",
+    ":u7533:":  "\ud83c\ude38",
+    ":u5408:":  "\ud83c\ude34",
+    ":u7a7a:":  "\ud83c\ude33",
+    ":congratulations:":    "\u3297",
+    ":secret:": "\u3299",
+    ":u55b6:":  "\ud83c\ude3a",
+    ":u6e80:":  "\ud83c\ude35",
+    ":black_small_square:": "\u25aa",
+    ":white_small_square:": "\u25ab",
+    ":white_medium_square:":    "\u25fb",
+    ":black_medium_square:":    "\u25fc",
+    ":white_medium_small_square:":  "\u25fd",
+    ":black_medium_small_square:":  "\u25fe",
+    ":black_large_square:": "\u2b1b",
+    ":white_large_square:": "\u2b1c",
+    ":large_orange_diamond:":   "\ud83d\udd36",
+    ":large_blue_diamond:": "\ud83d\udd37",
+    ":small_orange_diamond:":   "\ud83d\udd38",
+    ":small_blue_diamond:": "\ud83d\udd39",
+    ":small_red_triangle:": "\ud83d\udd3a",
+    ":small_red_triangle_down:":    "\ud83d\udd3b",
+    ":diamond_shape_with_a_dot_inside:":    "\ud83d\udca0",
+    ":black_square_button:":    "\ud83d\udd32",
+    ":white_square_button:":    "\ud83d\udd33",
+    ":white_circle:":   "\u26aa",
+    ":black_circle:":   "\u26ab",
+    ":red_circle:": "\ud83d\udd34",
+    ":large_blue_circle:":  "\ud83d\udd35",
+    ":eye_in_speech_bubble:":   "\ud83d\udc41\u200d\ud83d\udde8",
+    ":flag_af:":    "\ud83c\udde6\ud83c\uddeb",
+    ":flag_ax:":    "\ud83c\udde6\ud83c\uddfd",
+    ":flag_al:":    "\ud83c\udde6\ud83c\uddf1",
+    ":flag_dz:":    "\ud83c\udde9\ud83c\uddff",
+    ":flag_as:":    "\ud83c\udde6\ud83c\uddf8",
+    ":flag_ad:":    "\ud83c\udde6\ud83c\udde9",
+    ":flag_ao:":    "\ud83c\udde6\ud83c\uddf4",
+    ":flag_ai:":    "\ud83c\udde6\ud83c\uddee",
+    ":flag_aq:":    "\ud83c\udde6\ud83c\uddf6",
+    ":flag_ag:":    "\ud83c\udde6\ud83c\uddec",
+    ":flag_ar:":    "\ud83c\udde6\ud83c\uddf7",
+    ":flag_am:":    "\ud83c\udde6\ud83c\uddf2",
+    ":flag_aw:":    "\ud83c\udde6\ud83c\uddfc",
+    ":flag_ac:":    "\ud83c\udde6\ud83c\udde8",
+    ":flag_au:":    "\ud83c\udde6\ud83c\uddfa",
+    ":flag_at:":    "\ud83c\udde6\ud83c\uddf9",
+    ":flag_az:":    "\ud83c\udde6\ud83c\uddff",
+    ":flag_bs:":    "\ud83c\udde7\ud83c\uddf8",
+    ":flag_bh:":    "\ud83c\udde7\ud83c\udded",
+    ":flag_bd:":    "\ud83c\udde7\ud83c\udde9",
+    ":flag_bb:":    "\ud83c\udde7\ud83c\udde7",
+    ":flag_by:":    "\ud83c\udde7\ud83c\uddfe",
+    ":flag_be:":    "\ud83c\udde7\ud83c\uddea",
+    ":flag_bz:":    "\ud83c\udde7\ud83c\uddff",
+    ":flag_bj:":    "\ud83c\udde7\ud83c\uddef",
+    ":flag_bm:":    "\ud83c\udde7\ud83c\uddf2",
+    ":flag_bt:":    "\ud83c\udde7\ud83c\uddf9",
+    ":flag_bo:":    "\ud83c\udde7\ud83c\uddf4",
+    ":flag_ba:":    "\ud83c\udde7\ud83c\udde6",
+    ":flag_bw:":    "\ud83c\udde7\ud83c\uddfc",
+    ":flag_bv:":    "\ud83c\udde7\ud83c\uddfb",
+    ":flag_br:":    "\ud83c\udde7\ud83c\uddf7",
+    ":flag_io:":    "\ud83c\uddee\ud83c\uddf4",
+    ":flag_vg:":    "\ud83c\uddfb\ud83c\uddec",
+    ":flag_bn:":    "\ud83c\udde7\ud83c\uddf3",
+    ":flag_bg:":    "\ud83c\udde7\ud83c\uddec",
+    ":flag_bf:":    "\ud83c\udde7\ud83c\uddeb",
+    ":flag_bi:":    "\ud83c\udde7\ud83c\uddee",
+    ":flag_kh:":    "\ud83c\uddf0\ud83c\udded",
+    ":flag_cm:":    "\ud83c\udde8\ud83c\uddf2",
+    ":flag_ca:":    "\ud83c\udde8\ud83c\udde6",
+    ":flag_ic:":    "\ud83c\uddee\ud83c\udde8",
+    ":flag_cv:":    "\ud83c\udde8\ud83c\uddfb",
+    ":flag_bq:":    "\ud83c\udde7\ud83c\uddf6",
+    ":flag_ky:":    "\ud83c\uddf0\ud83c\uddfe",
+    ":flag_cf:":    "\ud83c\udde8\ud83c\uddeb",
+    ":flag_ea:":    "\ud83c\uddea\ud83c\udde6",
+    ":flag_td:":    "\ud83c\uddf9\ud83c\udde9",
+    ":flag_cl:":    "\ud83c\udde8\ud83c\uddf1",
+    ":flag_cn:":    "\ud83c\udde8\ud83c\uddf3",
+    ":flag_cx:":    "\ud83c\udde8\ud83c\uddfd",
+    ":flag_cp:":    "\ud83c\udde8\ud83c\uddf5",
+    ":flag_cc:":    "\ud83c\udde8\ud83c\udde8",
+    ":flag_co:":    "\ud83c\udde8\ud83c\uddf4",
+    ":flag_km:":    "\ud83c\uddf0\ud83c\uddf2",
+    ":flag_cg:":    "\ud83c\udde8\ud83c\uddec",
+    ":flag_cd:":    "\ud83c\udde8\ud83c\udde9",
+    ":flag_ck:":    "\ud83c\udde8\ud83c\uddf0",
+    ":flag_cr:":    "\ud83c\udde8\ud83c\uddf7",
+    ":flag_ci:":    "\ud83c\udde8\ud83c\uddee",
+    ":flag_hr:":    "\ud83c\udded\ud83c\uddf7",
+    ":flag_cu:":    "\ud83c\udde8\ud83c\uddfa",
+    ":flag_cw:":    "\ud83c\udde8\ud83c\uddfc",
+    ":flag_cy:":    "\ud83c\udde8\ud83c\uddfe",
+    ":flag_cz:":    "\ud83c\udde8\ud83c\uddff",
+    ":flag_dk:":    "\ud83c\udde9\ud83c\uddf0",
+    ":flag_dg:":    "\ud83c\udde9\ud83c\uddec",
+    ":flag_dj:":    "\ud83c\udde9\ud83c\uddef",
+    ":flag_dm:":    "\ud83c\udde9\ud83c\uddf2",
+    ":flag_do:":    "\ud83c\udde9\ud83c\uddf4",
+    ":flag_ec:":    "\ud83c\uddea\ud83c\udde8",
+    ":flag_eg:":    "\ud83c\uddea\ud83c\uddec",
+    ":flag_sv:":    "\ud83c\uddf8\ud83c\uddfb",
+    ":flag_gq:":    "\ud83c\uddec\ud83c\uddf6",
+    ":flag_er:":    "\ud83c\uddea\ud83c\uddf7",
+    ":flag_ee:":    "\ud83c\uddea\ud83c\uddea",
+    ":flag_et:":    "\ud83c\uddea\ud83c\uddf9",
+    ":flag_eu:":    "\ud83c\uddea\ud83c\uddfa",
+    ":flag_fk:":    "\ud83c\uddeb\ud83c\uddf0",
+    ":flag_fo:":    "\ud83c\uddeb\ud83c\uddf4",
+    ":flag_fj:":    "\ud83c\uddeb\ud83c\uddef",
+    ":flag_fi:":    "\ud83c\uddeb\ud83c\uddee",
+    ":flag_fr:":    "\ud83c\uddeb\ud83c\uddf7",
+    ":flag_gf:":    "\ud83c\uddec\ud83c\uddeb",
+    ":flag_pf:":    "\ud83c\uddf5\ud83c\uddeb",
+    ":flag_tf:":    "\ud83c\uddf9\ud83c\uddeb",
+    ":flag_ga:":    "\ud83c\uddec\ud83c\udde6",
+    ":flag_gm:":    "\ud83c\uddec\ud83c\uddf2",
+    ":flag_ge:":    "\ud83c\uddec\ud83c\uddea",
+    ":flag_de:":    "\ud83c\udde9\ud83c\uddea",
+    ":flag_gh:":    "\ud83c\uddec\ud83c\udded",
+    ":flag_gi:":    "\ud83c\uddec\ud83c\uddee",
+    ":flag_gr:":    "\ud83c\uddec\ud83c\uddf7",
+    ":flag_gl:":    "\ud83c\uddec\ud83c\uddf1",
+    ":flag_gd:":    "\ud83c\uddec\ud83c\udde9",
+    ":flag_gp:":    "\ud83c\uddec\ud83c\uddf5",
+    ":flag_gu:":    "\ud83c\uddec\ud83c\uddfa",
+    ":flag_gt:":    "\ud83c\uddec\ud83c\uddf9",
+    ":flag_gg:":    "\ud83c\uddec\ud83c\uddec",
+    ":flag_gn:":    "\ud83c\uddec\ud83c\uddf3",
+    ":flag_gw:":    "\ud83c\uddec\ud83c\uddfc",
+    ":flag_gy:":    "\ud83c\uddec\ud83c\uddfe",
+    ":flag_ht:":    "\ud83c\udded\ud83c\uddf9",
+    ":flag_hm:":    "\ud83c\udded\ud83c\uddf2",
+    ":flag_hn:":    "\ud83c\udded\ud83c\uddf3",
+    ":flag_hk:":    "\ud83c\udded\ud83c\uddf0",
+    ":flag_hu:":    "\ud83c\udded\ud83c\uddfa",
+    ":flag_is:":    "\ud83c\uddee\ud83c\uddf8",
+    ":flag_in:":    "\ud83c\uddee\ud83c\uddf3",
+    ":flag_id:":    "\ud83c\uddee\ud83c\udde9",
+    ":flag_ir:":    "\ud83c\uddee\ud83c\uddf7",
+    ":flag_iq:":    "\ud83c\uddee\ud83c\uddf6",
+    ":flag_ie:":    "\ud83c\uddee\ud83c\uddea",
+    ":flag_im:":    "\ud83c\uddee\ud83c\uddf2",
+    ":flag_il:":    "\ud83c\uddee\ud83c\uddf1",
+    ":flag_it:":    "\ud83c\uddee\ud83c\uddf9",
+    ":flag_jm:":    "\ud83c\uddef\ud83c\uddf2",
+    ":flag_jp:":    "\ud83c\uddef\ud83c\uddf5",
+    ":flag_je:":    "\ud83c\uddef\ud83c\uddea",
+    ":flag_jo:":    "\ud83c\uddef\ud83c\uddf4",
+    ":flag_kz:":    "\ud83c\uddf0\ud83c\uddff",
+    ":flag_ke:":    "\ud83c\uddf0\ud83c\uddea",
+    ":flag_ki:":    "\ud83c\uddf0\ud83c\uddee",
+    ":flag_xk:":    "\ud83c\uddfd\ud83c\uddf0",
+    ":flag_kw:":    "\ud83c\uddf0\ud83c\uddfc",
+    ":flag_kg:":    "\ud83c\uddf0\ud83c\uddec",
+    ":flag_la:":    "\ud83c\uddf1\ud83c\udde6",
+    ":flag_lv:":    "\ud83c\uddf1\ud83c\uddfb",
+    ":flag_lb:":    "\ud83c\uddf1\ud83c\udde7",
+    ":flag_ls:":    "\ud83c\uddf1\ud83c\uddf8",
+    ":flag_lr:":    "\ud83c\uddf1\ud83c\uddf7",
+    ":flag_ly:":    "\ud83c\uddf1\ud83c\uddfe",
+    ":flag_li:":    "\ud83c\uddf1\ud83c\uddee",
+    ":flag_lt:":    "\ud83c\uddf1\ud83c\uddf9",
+    ":flag_lu:":    "\ud83c\uddf1\ud83c\uddfa",
+    ":flag_mo:":    "\ud83c\uddf2\ud83c\uddf4",
+    ":flag_mk:":    "\ud83c\uddf2\ud83c\uddf0",
+    ":flag_mg:":    "\ud83c\uddf2\ud83c\uddec",
+    ":flag_mw:":    "\ud83c\uddf2\ud83c\uddfc",
+    ":flag_my:":    "\ud83c\uddf2\ud83c\uddfe",
+    ":flag_mv:":    "\ud83c\uddf2\ud83c\uddfb",
+    ":flag_ml:":    "\ud83c\uddf2\ud83c\uddf1",
+    ":flag_mt:":    "\ud83c\uddf2\ud83c\uddf9",
+    ":flag_mh:":    "\ud83c\uddf2\ud83c\udded",
+    ":flag_mq:":    "\ud83c\uddf2\ud83c\uddf6",
+    ":flag_mr:":    "\ud83c\uddf2\ud83c\uddf7",
+    ":flag_mu:":    "\ud83c\uddf2\ud83c\uddfa",
+    ":flag_yt:":    "\ud83c\uddfe\ud83c\uddf9",
+    ":flag_mx:":    "\ud83c\uddf2\ud83c\uddfd",
+    ":flag_fm:":    "\ud83c\uddeb\ud83c\uddf2",
+    ":flag_md:":    "\ud83c\uddf2\ud83c\udde9",
+    ":flag_mc:":    "\ud83c\uddf2\ud83c\udde8",
+    ":flag_mn:":    "\ud83c\uddf2\ud83c\uddf3",
+    ":flag_me:":    "\ud83c\uddf2\ud83c\uddea",
+    ":flag_ms:":    "\ud83c\uddf2\ud83c\uddf8",
+    ":flag_ma:":    "\ud83c\uddf2\ud83c\udde6",
+    ":flag_mz:":    "\ud83c\uddf2\ud83c\uddff",
+    ":flag_mm:":    "\ud83c\uddf2\ud83c\uddf2",
+    ":flag_na:":    "\ud83c\uddf3\ud83c\udde6",
+    ":flag_nr:":    "\ud83c\uddf3\ud83c\uddf7",
+    ":flag_np:":    "\ud83c\uddf3\ud83c\uddf5",
+    ":flag_nl:":    "\ud83c\uddf3\ud83c\uddf1",
+    ":flag_nc:":    "\ud83c\uddf3\ud83c\udde8",
+    ":flag_nz:":    "\ud83c\uddf3\ud83c\uddff",
+    ":flag_ni:":    "\ud83c\uddf3\ud83c\uddee",
+    ":flag_ne:":    "\ud83c\uddf3\ud83c\uddea",
+    ":flag_ng:":    "\ud83c\uddf3\ud83c\uddec",
+    ":flag_nu:":    "\ud83c\uddf3\ud83c\uddfa",
+    ":flag_nf:":    "\ud83c\uddf3\ud83c\uddeb",
+    ":flag_mp:":    "\ud83c\uddf2\ud83c\uddf5",
+    ":flag_kp:":    "\ud83c\uddf0\ud83c\uddf5",
+    ":flag_no:":    "\ud83c\uddf3\ud83c\uddf4",
+    ":flag_om:":    "\ud83c\uddf4\ud83c\uddf2",
+    ":flag_pk:":    "\ud83c\uddf5\ud83c\uddf0",
+    ":flag_pw:":    "\ud83c\uddf5\ud83c\uddfc",
+    ":flag_ps:":    "\ud83c\uddf5\ud83c\uddf8",
+    ":flag_pa:":    "\ud83c\uddf5\ud83c\udde6",
+    ":flag_pg:":    "\ud83c\uddf5\ud83c\uddec",
+    ":flag_py:":    "\ud83c\uddf5\ud83c\uddfe",
+    ":flag_pe:":    "\ud83c\uddf5\ud83c\uddea",
+    ":flag_ph:":    "\ud83c\uddf5\ud83c\udded",
+    ":flag_pn:":    "\ud83c\uddf5\ud83c\uddf3",
+    ":flag_pl:":    "\ud83c\uddf5\ud83c\uddf1",
+    ":flag_pt:":    "\ud83c\uddf5\ud83c\uddf9",
+    ":flag_pr:":    "\ud83c\uddf5\ud83c\uddf7",
+    ":flag_qa:":    "\ud83c\uddf6\ud83c\udde6",
+    ":flag_re:":    "\ud83c\uddf7\ud83c\uddea",
+    ":flag_ro:":    "\ud83c\uddf7\ud83c\uddf4",
+    ":flag_ru:":    "\ud83c\uddf7\ud83c\uddfa",
+    ":flag_rw:":    "\ud83c\uddf7\ud83c\uddfc",
+    ":flag_ws:":    "\ud83c\uddfc\ud83c\uddf8",
+    ":flag_sm:":    "\ud83c\uddf8\ud83c\uddf2",
+    ":flag_st:":    "\ud83c\uddf8\ud83c\uddf9",
+    ":flag_sa:":    "\ud83c\uddf8\ud83c\udde6",
+    ":flag_sn:":    "\ud83c\uddf8\ud83c\uddf3",
+    ":flag_rs:":    "\ud83c\uddf7\ud83c\uddf8",
+    ":flag_sc:":    "\ud83c\uddf8\ud83c\udde8",
+    ":flag_sl:":    "\ud83c\uddf8\ud83c\uddf1",
+    ":flag_sg:":    "\ud83c\uddf8\ud83c\uddec",
+    ":flag_sx:":    "\ud83c\uddf8\ud83c\uddfd",
+    ":flag_sk:":    "\ud83c\uddf8\ud83c\uddf0",
+    ":flag_si:":    "\ud83c\uddf8\ud83c\uddee",
+    ":flag_sb:":    "\ud83c\uddf8\ud83c\udde7",
+    ":flag_so:":    "\ud83c\uddf8\ud83c\uddf4",
+    ":flag_za:":    "\ud83c\uddff\ud83c\udde6",
+    ":flag_gs:":    "\ud83c\uddec\ud83c\uddf8",
+    ":flag_kr:":    "\ud83c\uddf0\ud83c\uddf7",
+    ":flag_ss:":    "\ud83c\uddf8\ud83c\uddf8",
+    ":flag_es:":    "\ud83c\uddea\ud83c\uddf8",
+    ":flag_lk:":    "\ud83c\uddf1\ud83c\uddf0",
+    ":flag_bl:":    "\ud83c\udde7\ud83c\uddf1",
+    ":flag_sh:":    "\ud83c\uddf8\ud83c\udded",
+    ":flag_kn:":    "\ud83c\uddf0\ud83c\uddf3",
+    ":flag_lc:":    "\ud83c\uddf1\ud83c\udde8",
+    ":flag_mf:":    "\ud83c\uddf2\ud83c\uddeb",
+    ":flag_pm:":    "\ud83c\uddf5\ud83c\uddf2",
+    ":flag_vc:":    "\ud83c\uddfb\ud83c\udde8",
+    ":flag_sd:":    "\ud83c\uddf8\ud83c\udde9",
+    ":flag_sr:":    "\ud83c\uddf8\ud83c\uddf7",
+    ":flag_sj:":    "\ud83c\uddf8\ud83c\uddef",
+    ":flag_sz:":    "\ud83c\uddf8\ud83c\uddff",
+    ":flag_se:":    "\ud83c\uddf8\ud83c\uddea",
+    ":flag_ch:":    "\ud83c\udde8\ud83c\udded",
+    ":flag_sy:":    "\ud83c\uddf8\ud83c\uddfe",
+    ":flag_tw:":    "\ud83c\uddf9\ud83c\uddfc",
+    ":flag_tj:":    "\ud83c\uddf9\ud83c\uddef",
+    ":flag_tz:":    "\ud83c\uddf9\ud83c\uddff",
+    ":flag_th:":    "\ud83c\uddf9\ud83c\udded",
+    ":flag_tl:":    "\ud83c\uddf9\ud83c\uddf1",
+    ":flag_tg:":    "\ud83c\uddf9\ud83c\uddec",
+    ":flag_tk:":    "\ud83c\uddf9\ud83c\uddf0",
+    ":flag_to:":    "\ud83c\uddf9\ud83c\uddf4",
+    ":flag_tt:":    "\ud83c\uddf9\ud83c\uddf9",
+    ":flag_ta:":    "\ud83c\uddf9\ud83c\udde6",
+    ":flag_tn:":    "\ud83c\uddf9\ud83c\uddf3",
+    ":flag_tr:":    "\ud83c\uddf9\ud83c\uddf7",
+    ":flag_tm:":    "\ud83c\uddf9\ud83c\uddf2",
+    ":flag_tc:":    "\ud83c\uddf9\ud83c\udde8",
+    ":flag_tv:":    "\ud83c\uddf9\ud83c\uddfb",
+    ":flag_ug:":    "\ud83c\uddfa\ud83c\uddec",
+    ":flag_ua:":    "\ud83c\uddfa\ud83c\udde6",
+    ":flag_ae:":    "\ud83c\udde6\ud83c\uddea",
+    ":flag_gb:":    "\ud83c\uddec\ud83c\udde7",
+    ":flag_us:":    "\ud83c\uddfa\ud83c\uddf8",
+    ":flag_uy:":    "\ud83c\uddfa\ud83c\uddfe",
+    ":flag_um:":    "\ud83c\uddfa\ud83c\uddf2",
+    ":flag_vi:":    "\ud83c\uddfb\ud83c\uddee",
+    ":flag_uz:":    "\ud83c\uddfa\ud83c\uddff",
+    ":flag_vu:":    "\ud83c\uddfb\ud83c\uddfa",
+    ":flag_va:":    "\ud83c\uddfb\ud83c\udde6",
+    ":flag_ve:":    "\ud83c\uddfb\ud83c\uddea",
+    ":flag_vn:":    "\ud83c\uddfb\ud83c\uddf3",
+    ":flag_wf:":    "\ud83c\uddfc\ud83c\uddeb",
+    ":flag_eh:":    "\ud83c\uddea\ud83c\udded",
+    ":flag_ye:":    "\ud83c\uddfe\ud83c\uddea",
+    ":flag_zm:":    "\ud83c\uddff\ud83c\uddf2",
+    ":flag_zw:":    "\ud83c\uddff\ud83c\uddfc",
     "-<@%":"\ud83d\udc1d",
     ":(|)":"\ud83d\udc35",
     ":(:)":"\ud83d\udc37",


improved tweet preload caching

improved tweet preload caching

* every app update resulted in a tweet fetch if the first request was still pending

--- src/ui/views/messages.coffee
+++ src/ui/views/messages.coffee
@@ -203,13 +203,14 @@
     (seg) ->
         href = seg?.text
         if /^https?:\/\/twitter.com\/.+\/status/.test href
-            node = preloadTweet href
-            if node
+            data = preloadTweet href
+            if data
                 div class:'tweet', ->
-                    p ->
-                        node.text
-                    if node.img
-                        img src: node.img
+                    if data.text
+                        p ->
+                            data.text
+                    if data.imageUrl and preload data.imageUrl
+                        img src: data.imageUrl
 ]

 stripProxiedColon = (txt) ->
@@ -237,6 +238,7 @@
 preloadTweet = (href) ->
     cache = preload_cache[href]
     if not cache
+        preload_cache[href] = {}
         fetch href
         .then (response) ->
             response.text()
@@ -246,11 +248,9 @@
             container = frag.querySelector '[data-associated-tweet-id]'
             textNode = container.querySelector ('.tweet-text')
             image = container.querySelector ('[data-image-url]')
-            preload_cache[href] = {
-                text: textNode.textContent
-                img: image?.dataset.imageUrl
-            }
-            later -> action 'loadedimg'
+            preload_cache[href].text = textNode.textContent
+            preload_cache[href].imageUrl = image?.dataset.imageUrl
+            later -> action 'loadedtweet'
     return cache

 formatAttachment = (att) ->
@@ -278,6 +278,8 @@
     # fix the position after redraw
     updated 'afterImg'

+handle 'loadedtweet', ->
+    updated 'conv'

 extractProtobufStyle = (att) ->
     eitem = att?[0]?.embed_item


Fix bugs regarding deselection of events, proposal outcome and review phase

--- app/assets/javascripts/species/controllers/elibrary_search_controller.js.coffee
+++ app/assets/javascripts/species/controllers/elibrary_search_controller.js.coffee
@@ -67,8 +67,9 @@
       @set('taxonConceptQuery', '')
       @set('selectedGeoEntities', [])
       @set('titleQuery', '')
-      @set('selectedEventType', '')
-      @set('selectedEvent', '')
-      @set('selectedDocumentType', '')
-      @set('selectedProposalOutcomeId', '')
-      @set('selectedReviewPhaseId', '')
+      @set('selectedEventType', null)
+      @set('selectedEvent', null)
+      @set('selectedEventId', null)
+      @set('selectedDocumentType', null)
+      @set('selectedProposalOutcome', null)
+      @set('selectedReviewPhase', null)


re-enabled a test that's using ¨

re-enabled a test that's using ¨

--- src/vocabulary/squish.coffee
+++ src/vocabulary/squish.coffee
@@ -7,7 +7,7 @@
 #
 #     1 ⌷ 3 5 8                ⍝ returns 5
 #     (3 5 8)[1]               ⍝ returns 5
-#!    ⌷←{⍺+¨⍵}  ◇  (3 5 8)[1]  ⍝ returns 4 6 9
+#     ⌷←{⍺+¨⍵}  ◇  (3 5 8)[1]  ⍝ returns 4 6 9
 #     (2 2 0) (1 2) ⌷ 3 3⍴⍳9   ⍝ returns 3 2 ⍴ 7 8 7 8 1 2
 #     ¯1 ⌷ 3 5 8               ⍝ throws
 #     2 ⌷ 111 222 333 444      ⍝ returns 333


Show in tabs

Show in tabs

--- client/landing/app/Applications/Environments.kdapplication/AppController.coffee
+++ client/landing/app/Applications/Environments.kdapplication/AppController.coffee
@@ -3,7 +3,8 @@
   KD.registerAppClass @,
     name         : "Environments"
     route        : "/Environments"
-    hiddenHandle : yes
+    hiddenHandle : no
+    behavior     : "application"

   constructor:(options = {}, data)->


Re-enabled hiding the auto complete fact relations list when losing focus.

--- core/app/backbone/views/fact_relations/auto_complete_fact_relations_view.coffee
+++ core/app/backbone/views/fact_relations/auto_complete_fact_relations_view.coffee
@@ -87,7 +87,7 @@
     @recent_collection.fetch() if @model.get('text') == ''

   blur: ->
-    # @recent_collection.reset [] if @model.get('text') == ''
+    @recent_collection.reset [] if @model.get('text') == ''

   reset: ->
     @setQuery ''


Fix row insertion

Fix row insertion

--- backend/app/assets/javascripts/spree/backend/stock_management/index_add_forms.coffee
+++ backend/app/assets/javascripts/spree/backend/stock_management/index_add_forms.coffee
@@ -29,7 +29,7 @@
     selectedStockLocationOption.remove()

     rowTemplate = HandlebarsTemplates['stock_items/stock_location_stock_item']
-    newRow = @$el.before(
+    newRow = $(
       rowTemplate
         id: @model.get('id')
         variantId: @model.get('variant_id')
@@ -37,7 +37,7 @@
         stockLocationName: stockLocationName
         countOnHand: @model.get('count_on_hand')
         backorderable: @model.get('backorderable')
-    )
+    ).insertBefore(@$el)

     new Spree.EditStockItemView
       el: newRow


Remove humans fn parentheses

Remove humans fn parentheses

--- tasks/humans.coffee
+++ tasks/humans.coffee
@@ -3,5 +3,5 @@

 module.exports = () ->
   return gulp.src this.opts.config.humans.src
-    .pipe humans(this.opts.config.humans.opts)
+    .pipe humans this.opts.config.humans.opts
     .pipe gulp.dest this.opts.config.humans.dest


Use 'true' for push/pop for Terminal.app

Otherwise Terminal.app doesn't restore cursor position correctly,
leaving content aligned with the left side of the overlay.

--- lib/lib/overlay_output_stream.coffee
+++ lib/lib/overlay_output_stream.coffee
@@ -57,7 +57,7 @@
   clearOverlay: ->
     return unless @hasOverlay

-    @charm.push()
+    @charm.push(true)

     # Width from the start of the overlay to the right side of the window. Starts as the length
     # of the text (since it was printed right-aligned) but we add in any new columns that have
@@ -79,7 +79,7 @@
       @charm.down(1)
       @charm.delete('line', 1)

-    @charm.pop()
+    @charm.pop(true)

     # After the cursor position is restored, we scroll a line to cover up the newly blank one,
     # and have to move the cursor down one line to compensate.


avoid operator specific logic in general function

--- lib/operator-transform-string.coffee
+++ lib/operator-transform-string.coffee
@@ -34,9 +34,6 @@
         startRow = selection.getBufferRange().start.row
         startRowIndentLevel = getIndentLevelForBufferRow(@editor, startRow)
       range = selection.insertText(text, {@autoIndent, @autoIndentNewline})
-
-      if @name is "ReplaceWithRegister"
-        @vimState.sequentialPasteManager.savePastedRangeForSelection(selection, range)

       if @autoIndentAfterInsertText
         # Currently used by SplitArguments and Surround( linewise target only )
@@ -360,6 +357,10 @@

     super

+    for selection in @editor.getSelections()
+      range = @mutationManager.getMutatedBufferRangeForSelection(selection)
+      @vimState.sequentialPasteManager.savePastedRangeForSelection(selection, range)
+
   getNewText: (text, selection) ->
     @vimState.register.get(null, selection, @sequentialPaste)?.text ? ""


cleanup

cleanup

--- lib/operator-transform-string.coffee
+++ lib/operator-transform-string.coffee
@@ -436,10 +436,10 @@
     super

   focusInput: ->
-    @inputUI = @newInputUI()
-    @inputUI.onDidConfirm(@onConfirm.bind(this))
-    @inputUI.onDidCancel(@cancelOperation.bind(this))
-    @inputUI.focus()
+    inputUI = @newInputUI()
+    inputUI.onDidConfirm(@onConfirm.bind(this))
+    inputUI.onDidCancel(@cancelOperation.bind(this))
+    inputUI.focus()

   getPair: (char) ->
     pair = _.detect(@pairs, (pair) -> char in pair)
@@ -454,9 +454,9 @@
       close += "\n"

     if char in settings.get('charactersToAddSpaceOnSurround') and isSingleLineText(text)
-      open + ' ' + text + ' ' + close
-    else
-      open + text + close
+      text = ' ' + text + ' '
+
+    open + text + close

   deleteSurround: (text) ->
     [open, innerText..., close] = text
@@ -574,6 +574,7 @@
   @description: "Change surround character, from char is auto-detected from enclosed and forwarding area"
   target: "AAnyPairAllowForwarding"

+# -------------------------
 # FIXME
 # Currently native editor.joinLines() is better for cursor position setting
 # So I use native methods for a meanwhile.


server: remove fetch new logs

server: remove fetch new logs

--- server/lib/server/index.coffee
+++ server/lib/server/index.coffee
@@ -101,8 +101,6 @@
   timestamp  = req.params?.timestamp
   rawStartDate  = if timestamp? then parseInt(timestamp, 10) else (new Date).getTime()
   startDate  = Math.floor(rawStartDate/1000)
-
-  console.log "fetchFromNeo", req.params.timestamp, rawStartDate, startDate

   fetch = new FetchAllActivityParallel startDate, neo4j
   fetch.get (results)->


Fix conversion from fieldValues to account field

When fieldValues was an empty object it was considered as a valid account.

--- server/models/konnector.coffee
+++ server/models/konnector.coffee
@@ -222,7 +222,8 @@

     if @fieldValues?
         @accounts ?= []
-        @accounts.unshift @fieldValues
+        if Object.keys(@fieldValues).length > 0
+            @accounts.unshift @fieldValues
         @fieldValues = null

     if @password? and @password[0] is '{'


online

online

--- core/index.coffee
+++ core/index.coffee
@@ -28,7 +28,7 @@
     @version = "0.5.1"

   onReady: (e)=>
-    @bot.User.setStatus 'dnd', {
+    @bot.User.setStatus 'online', {
       name: "#{@prefix}help (new prefix!)"
     }
     console.log 'Connected.'


Ensure booking fetch on checkout [EE-93]

--- src/admin-dashboard/javascripts/directives/resource_calendar.js.coffee
+++ src/admin-dashboard/javascripts/directives/resource_calendar.js.coffee
@@ -132,13 +132,7 @@
           $scope.pusher_channel = $scope.pusher.subscribe(channelName)
           pusherEvent = (res) =>
             if res.id?
-              setTimeout (->
-                prms =
-                  company: $scope.company
-                  id: res.id
-                AdminBookingService.getBooking(prms).then (booking) ->
-                  return
-              ), 2000
+              uiCalendarConfig.calendars.resourceCalendar.fullCalendar('refetchEvents')
           $scope.pusher_channel.bind 'booking', pusherEvent
           $scope.pusher_channel.bind 'cancellation', pusherEvent
           $scope.pusher_channel.bind 'updating', pusherEvent


ADO-19604 - Support

ADO-19604 - Support

* It looks like the `Event` global object and constructor isn't
  available in some of the older browsers we support [1]. So
  add a condition to build the window event in a in a way
  that's supported for the older browsers.

[1]: https://caniuse.com/?search=Event
SEE: https://dev.azure.com/AMA-Ent/AMA-Ent/_workitems/edit/19604

--- app/assets/javascripts/ama_layout/mailcheck/email_suggestion.coffee
+++ app/assets/javascripts/ama_layout/mailcheck/email_suggestion.coffee
@@ -54,7 +54,7 @@
         domains: @domains
         secondLevelDomains: @secondLevelDomains
         suggested: (element, suggestion) =>
-          event = new Event('mailcheck');
+          event = @buildEvent()
           text = @suggestionMarkup suggestion.address, suggestion.domain
           if !$('.email_hint').length
             $("
#{text}
").insertAfter(element).show()
@@ -62,18 +62,24 @@
             $('.email_hint').html text
           window.dispatchEvent(event)
         empty: (element) ->
-          event = new Event('mailcheck');
+          event = @buildEvent()
           $('.email_hint').html ''
           window.dispatchEvent(event)

     $(document).on 'click', '.email_hint .suggestion a.email_domain', (e) =>
       @trackUsage()
-      event = new Event('mailcheck');
+      event = @buildEvent()
       email_hint = $(e.originalEvent.target).parents('.email_hint')
       email = $(email_hint).prevAll('input[type=email]:last')
       $(email).val $('.suggestion').first().text()
       $('.email_hint').remove()
       window.dispatchEvent(event)
+
+  buildEvent: () ->
+    if typeof(Event) is 'function'
+      new Event('mailcheck')
+    else
+      document.createEvent('Event').initEvent('mailcheck', true, true)

   suggestionMarkup: (address, domain) ->
     "Did you mean " +


Fix proposal outcome dropdown not showing up when citescop is selected

--- app/assets/javascripts/species/mixins/document_tag_lookup.js.coffee
+++ app/assets/javascripts/species/mixins/document_tag_lookup.js.coffee
@@ -10,8 +10,8 @@
       @set('selectedProposalOutcome', po)

   proposalOutcomeDropdownVisible: ( ->
-    @get('selectedDocumentType.id') == 'Document::Proposal'
-  ).property('selectedDocumentType')
+    @get('selectedEventType.id') == 'CitesCop'
+  ).property('selectedEventType')

   actions:
     handleProposalOutcomeSelection: (proposalOutcome) ->


Relate cards

Relate cards

* Added ability to relate cards and themes

* Updated documentation

--- src/trello.coffee
+++ src/trello.coffee
@@ -146,7 +146,7 @@

 relateTheme = (msg, theme) ->
   msg.reply "Gathering cards for #{theme}"
-  trello.get "/1/search", {query: "is:open #{theme}",idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
+  trello.get "/1/search", {query: "is:open name:\"#{theme}\"",idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
     cardList = ''
     cardList = "#{cardList}#{card.shortLink}," for card in data.cards unless err
     msg.send "Error occured" if err


Relate cards

Relate cards

Relate cards functionality

--- src/trello.coffee
+++ src/trello.coffee
@@ -13,13 +13,15 @@
 #   hubot trello new "<list>" <name> - Create a new Trello card in the list
 #   hubot trello list "<list>" - Show cards on list
 #   hubot trello move <shortLink> "<list>" - Move a card to a different list
-#   hubot trello add member <shortlink> "<first name of member>"
-#   hubot trello add comment <shortlink> "<comment>"
-#   hubot trello description <shortlink> "<discription>"
-#   hubot trello search "<criteria>"
-#   hubot trello get <name>'s cards
-#   hubot trello create list "<name>" (optional: top' or bottom)
-#
+#   hubot trello add member <shortlink> "<first name of member>" - Add member to card
+#   hubot trello add comment <shortlink> "<comment>" - Adds comment to card
+#   hubot trello description <shortlink> "<discription>" - Changes description of card
+#   hubot trello search "<criteria>" - Does a search on current board for open cards
+#   hubot trello search extended "<criteria>" Searches card on current board
+#   hubot trello get <name>'s cards - Get's user's cards (use first name only)
+#   hubot trello create list "<name>" (optional: top' or bottom) - Create new list
+#   hubot trello relate <comma dilimited shortlink> - Create Related cards section and relate
+#   hubot trello relate theme "<theme>" - Create Related Cards sections for cards with <theme> in title
 # Author:
 #   jared barboza <jared.m.barboza@gmail.com>

@@ -84,7 +86,7 @@
      id = members[member_name.toLowerCase()].username
   msg.send "Unable to find person named: #{user_name}" unless id?
   if id?
-    trello.get "/1/search", {query: "@"+id, idBoards: board.id, modelTypes: "cards", card_fields: "name,shortLink,url"}, (err, data) ->
+    trello.get "/1/search", {query: "@#{id}", idBoards: board.id, modelTypes: "cards", card_fields: "name,shortLink,url"}, (err, data) ->
       msg.reply "So sorry, I got an error and cannot give you that information" if err
       msg.reply "I got the following cards for you" unless err
       msg.send board.id
@@ -95,11 +97,21 @@
 search = (msg, search) ->
   if (search?)
     msg.reply "I'm searching for results now"
-    trello.get "/1/search", {query: search, idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
+    trello.get "/1/search", {query: "is:open #{search}",idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
       msg.reply "Sorry, I was unable to search at this time. Please try again later." if err
       msg.reply "The following cards match your criteria" unless err
       for cards in data.cards
          msg.send " * #{cards.name} | #{cards.url}"
+
+searchExtended = (msg,search) ->
+  if (search?)
+    msg.reply "I'm searching for results now"
+    trello.get "/1/search", {query: search,idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
+      msg.reply "Sorry, I was unable to search at this time. Please try again later." if err
+      msg.reply "The following cards match your criteria" unless err
+      for cards in data.cards
+         msg.send " * #{cards.name} | #{cards.url} | #{cards.shortLink}"
+

 createList = (msg, list_name, position = "top") ->
   msg.reply "I'll get right on that!"
@@ -118,6 +130,28 @@
     trello.put "/1/cards/#{card_id}/idList", {value: id}, (err, data) ->
       msg.reply "Sorry boss, I couldn't move that card after all." if err
       msg.reply "Yep, ok, I moved that card to #{list_name}." unless err
+
+relateCards = (msg, card_list) ->
+  msg.send card_list
+  cards = card_list.replace(/\s|\n/g, "").split(",")
+  desc = '\n\n **Related Cards**\n-------\n------\n'
+  desc = "#{desc} https://trello.com/c/#{items}\n" for items in cards
+  for related in cards
+    trello.get "/1/cards/#{related}", (err,data) ->
+     # msg.send "description for #{data.shortUrl} is #{data.desc}"
+        trello.put "/1/cards/#{data.id}/desc", {value: "#{data.desc}#{desc}"}, (err, data) ->
+          msg.send "I'm sorry I was unable to update card #{related}" if err
+          msg.send "updated card #{data.shortUrl}"
+  msg.send "I've successfully updated the cards"
+
+relateTheme = (msg, theme) ->
+  msg.reply "Gathering cards for #{theme}"
+  trello.get "/1/search", {query: "is:open #{theme}",idBoards: board.id, modelTypes:"cards", card_fields: "name,shortLink,url"}, (err, data) ->
+    cardList = ''
+    cardList = "#{cardList}#{card.shortLink}," for card in data.cards unless err
+    msg.send "Error occured" if err
+    #msg.send "Card list is #{cardList}"
+    relateCards msg, cardList.slice(0,-1)

 addDescription = (msg, card_id, desc) ->
   ensureConfig msg.send
@@ -178,6 +212,9 @@
   robot.respond /trello search ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
      search msg, msg.match[1]

+  robot.respond /trello extended search ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
+     searchExtended msg, msg.match[1]
+
   robot.respond /trello list lists/i, (msg) ->
     msg.reply "Here are all the lists on your board."
     Object.keys(lists).forEach (key) ->
@@ -185,8 +222,8 @@

   robot.respond /trello list members/i, (msg) ->
     msg.reply "Here are all the members of the board"
-    Object.keys(members).forEach (member)->
-    msg.send " * #{member}"
+    Object.keys(members).forEach (key)->
+    msg.send " * #{key}"

   robot.respond /trello list member id (\w+)/i, (msg) ->
     msg.reply "The member id for #{msg.match[1]} is: "
@@ -197,7 +234,14 @@

   robot.respond /trello create list ["“'‘]((.+|\n)+)["”'’] ?(.+)?$/i, (msg) ->
     createList msg, msg.match[1], msg.match[3]
-
+
+  robot.respond /trello relate cards ((.+|\n+)+)/i, (msg) ->
+    msg.reply "Attempting to relate cards"
+    relateCards msg, msg.match[1]
+
+  robot.respond /trello relate theme ["“'‘]((.+|\n)+)["”'’]/i, (msg) ->
+    relateTheme msg, msg.match[1]
+
   robot.respond /trello help/i, (msg) ->
     msg.reply "Here are all the commands for me."
     msg.send " *  trello new \"<ListName>\" <TaskName>"
@@ -211,4 +255,7 @@
     msg.send " *  trello comment <card.shortLink> <Comment>"
     msg.send " *  trello description <card.shortLink> <Description>"
     msg.send " *  trello search <criteria>"
+    msg.send " *  trello search extended <criteria>"
     msg.send " *  trello create list \"<name>\" (optional:top or bottom)"
+    msg.send " *  trello relate <comma dilimited shortlink>"
+    msg.send " *  trello relate theme \"<theme>\""


adding validation to validate title

adding validation to validate title

--- spec/notifications-spec.coffee
+++ spec/notifications-spec.coffee
@@ -285,6 +285,7 @@
               issueTitle = fatalError.issue.getIssueTitle()
           runs ->
             expect(issueTitle).not.toContain "\n"
+            expect(issueTitle).toBe "TypeError: undefined is not a function  at Object.module.exports.Pane.promptToSaveItem [as defau..."

       describe "when there are multiple packages in the stack trace", ->
         beforeEach ->


Delete random-editor-spec.coffee

Delete random-editor-spec.coffee

We are already extensively testing the same specs on text-buffer.

--- spec/random-editor-spec.coffee
+++ spec/random-editor-spec.coffee
@@ -1,91 +0,0 @@
-{times, random} = require 'underscore-plus'
-randomWords = require 'random-words'
-TextBuffer = require 'text-buffer'
-TextEditor = require '../src/text-editor'
-
-describe "TextEditor", ->
-  [editor, tokenizedBuffer, buffer, steps] = []
-
-  softWrapColumn = 80
-
-  beforeEach ->
-    atom.config.set('editor.softWrapAtPreferredLineLength', true)
-    atom.config.set('editor.preferredLineLength', softWrapColumn)
-
-  it "properly renders soft-wrapped lines when randomly mutated", ->
-    times 10, (i) ->
-      buffer = new TextBuffer
-      editor = atom.workspace.buildTextEditor({buffer})
-      editor.setEditorWidthInChars(80)
-      tokenizedBuffer = editor.tokenizedBuffer
-      steps = []
-
-      times 30, ->
-        randomlyMutateEditor()
-        verifyLines()
-
-  verifyLines = ->
-    {bufferRows, screenLines} = getReferenceScreenLines()
-    for referenceBufferRow, screenRow in bufferRows
-      referenceScreenLine = screenLines[screenRow]
-      actualBufferRow = editor.bufferRowForScreenRow(screenRow)
-      unless actualBufferRow is referenceBufferRow
-        logLines()
-        throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", )
-
-      actualScreenLine = editor.lineTextForScreenRow(screenRow)
-      unless actualScreenLine is referenceScreenLine
-        logLines()
-        throw new Error("Invalid line text at screen row #{screenRow}")
-
-  logLines = ->
-    console.log "==== screen lines ===="
-    editor.logScreenLines()
-    console.log "==== reference lines ===="
-    {bufferRows, screenLines} = getReferenceScreenLines()
-    for bufferRow, screenRow in bufferRows
-      console.log screenRow, bufferRow, screenLines[screenRow].text
-    console.log "==== steps to reproduce this failure: ==="
-    for step in steps
-      console.log 'editor.' + step[0] + '('+ step[1..].map((a) -> JSON.stringify(a)).join(', ') + ')'
-
-  randomlyMutateEditor = ->
-    if Math.random() < .2
-      softWrapped = not editor.isSoftWrapped()
-      steps.push(['setSoftWrapped', softWrapped])
-      editor.setSoftWrapped(softWrapped)
-    else
-      range = getRandomRange()
-      text = getRandomText()
-      steps.push(['setTextInBufferRange', range, text])
-      editor.setTextInBufferRange(range, text)
-
-  getRandomRange = ->
-    startRow = random(0, buffer.getLastRow())
-    startColumn = random(0, buffer.lineForRow(startRow).length)
-    endRow = random(startRow, buffer.getLastRow())
-    endColumn = random(0, buffer.lineForRow(endRow).length)
-    [[startRow, startColumn], [endRow, endColumn]]
-
-  getRandomText = ->
-    text = []
-    max = buffer.getText().split(/\s/).length * 0.75
-
-    times random(5, max), ->
-      if Math.random() < .1
-        text += '\n'
-      else
-        text += " " if /\w$/.test(text)
-        text += randomWords(exactly: 1)
-    text
-
-  getReferenceScreenLines = ->
-    referenceEditor = atom.workspace.buildTextEditor()
-    referenceEditor.setEditorWidthInChars(80)
-    referenceEditor.setText(editor.getText())
-    referenceEditor.setSoftWrapped(editor.isSoftWrapped())
-
-    screenLines = [0..referenceEditor.getLastScreenRow()].map (row) -> referenceEditor.lineTextForScreenRow(row)
-    bufferRows = referenceEditor.bufferRowsForScreenRows(0, referenceEditor.getLastScreenRow())
-
-    {screenLines, bufferRows}


Test restart button

Test restart button

--- spec/updates-panel-spec.coffee
+++ spec/updates-panel-spec.coffee
@@ -67,4 +67,9 @@

       waits 0
       runs ->
-        expect(atom.notifications.getNotifications().length).toBe 1
+        notifications = atom.notifications.getNotifications()
+        expect(notifications.length).toBe 1
+
+        spyOn(atom, 'restartApplication')
+        notifications[0].options.buttons[0].onDidClick()
+        expect(atom.restartApplication).toHaveBeenCalled()


Remove log line

Remove log line

--- lib/search-in-buffer.coffee
+++ lib/search-in-buffer.coffee
@@ -59,7 +59,6 @@
     @searchModel.setActiveId(if editor then editor.id else null)

   onCursorMoved: =>
-    console.log 'moved', @cursorMoveOriginatedHere
     if @cursorMoveOriginatedHere
       # HACK: I want to reset the current result whenever the cursor is moved
       # so it removes the '# of' from '2 of 100'. But I cant tell if I moved


:beers: Done rewrite Tag

:beers: Done rewrite Tag

--- lib/text-object-pair.coffee
+++ lib/text-object-pair.coffee
@@ -79,12 +79,12 @@
   delete options.allowForwarding

   findPairRange 'close', 'forward', options, (stack, openEvent) ->
+    unless openEvent?
+      return true
+
     if stack.length is 0
-      if not openEvent?
-        true
-      else
-        {start} = openEvent.range
-        start.isEqual(from) or (allowForwarding and start.row is from.row)
+      {start} = openEvent.range
+      start.isEqual(from) or (allowForwarding and start.row is from.row)

 findOpenPairRangeBackward = (options, fn) ->
   findPairRange 'open', 'backward', options, (stack, openEvent) ->
@@ -124,7 +124,7 @@
     @allowForwarding ?= @getName().endsWith('AllowForwarding')
     super

-  getFilters: (from) ->
+  getFilters: ->
     filters = []
     isEscaped = ({range}) => isEscapedCharAtPoint(@editor, range.start)
     filters.push(isEscaped)
@@ -156,28 +156,14 @@
     new Range(start, end)

   getPairInfo: (from) ->
-    if @instanceof('Tag')
-      # Old Style
-      pattern = @getPattern()
-      closeRange = @findClose(from, pattern)
-      openRange = @findOpen(closeRange.end, pattern) if closeRange?
-      unless (openRange? and closeRange?)
-        return null
-
-      aRange = new Range(openRange.start, closeRange.end)
-      innerRange = new Range(openRange.end, closeRange.start)
-      innerRange = @adjustRange(innerRange) if @adjustInnerRange
-      targetRange = if @isInner() then innerRange else aRange
-      {openRange, closeRange, aRange, innerRange, targetRange}
-    else
-      filters = @getFilters(from)
-      allowNextLine = @isAllowNextLine()
-      pairInfo = getPairRangeInformation({@editor, from, @pair, filters, allowNextLine, @allowForwarding})
-      unless pairInfo?
-        return null
-      pairInfo.innerRange = @adjustRange(pairInfo.innerRange) if @adjustInnerRange
-      pairInfo.targetRange = if @isInner() then pairInfo.innerRange else pairInfo.aRange
-      pairInfo
+    filters = @getFilters()
+    allowNextLine = @isAllowNextLine()
+    pairInfo = getPairRangeInformation({@editor, from, @pair, filters, allowNextLine, @allowForwarding})
+    unless pairInfo?
+      return null
+    pairInfo.innerRange = @adjustRange(pairInfo.innerRange) if @adjustInnerRange
+    pairInfo.targetRange = if @isInner() then pairInfo.innerRange else pairInfo.aRange
+    pairInfo

   getPointToSearchFrom: (selection, searchFrom) ->
     switch searchFrom
@@ -377,22 +363,84 @@
 class InnerAngleBracketAllowForwarding extends AngleBracket
   @extend()

-# -------------------------
-tagPattern = /(<(\/?))([^\s>]+)[^>]*>/g
+# Tag
+# -------------------------
+tagPattern = /<(\/?)([^\s>]+)[^>]*>/g
+
+findTagRange = (which, direction, options, fn) ->
+  {editor, from, filters, allowNextLine} = options
+  switch direction
+    when 'forward'
+      scanRange = new Range(from, editor.buffer.getEndPosition())
+      scanFunctionName = 'scanInBufferRange'
+    when 'backward'
+      scanRange = new Range([0, 0], from)
+      scanFunctionName = 'backwardsScanInBufferRange'
+
+  stack = []
+  range = null
+
+  findingState = which
+  oppositeState = switch findingState
+    when 'open' then 'close'
+    when 'close' then 'open'
+
+  editor[scanFunctionName] tagPattern, scanRange, (event) ->
+    if not allowNextLine and (from.row isnt event.range.start.row)
+      event.stop()
+      return
+
+    if filters? and filters.some((filter) -> filter(event))
+      return
+
+    tagState = getTagState(event)
+    if tagState.state is oppositeState
+      stack.push(tagState)
+    else
+      if oppositeTagState = findTagState(stack, oppositeState, tagState.name)
+        stack = stack[0...stack.indexOf(oppositeTagState)]
+
+      if fn(stack, oppositeTagState)
+        range = event.range
+        event.stop()
+
+  return range
+
+getTagState = (event) ->
+  backslash = event.match[1]
+  tagName = event.match[2]
+  {
+    state: if (backslash is '') then 'open' else 'close'
+    name: tagName
+    event: event
+  }
+
+findTagState = (stack, state, name) ->
+  for tagState in stack by -1 when (tagState.state is state) and (tagState.name is name)
+    return tagState
+
+findCloseTagRangeForward = (options) ->
+  {from, allowForwarding} = options
+  delete options.allowForwarding
+
+  findTagRange 'close', 'forward', options, (stack, openTagState) ->
+    unless openTagState?
+      # I'm very torelant for orphan tag like 'br', 'hr', or unclosed tag.
+      return true
+
+    if stack.length is 0
+      {start} = openTagState.event.range
+      start.isEqual(from) or (allowForwarding and start.row is from.row)
+
+findOpenTagRangeBackward = (options) ->
+  findTagRange 'open', 'backward', options, (stack, closeTagState) ->
+    stack.length is 0
+
 class Tag extends Pair
   @extend(false)
   allowNextLine: true
   allowForwarding: true
   adjustInnerRange: false
-  getPattern: ->
-    tagPattern
-
-  getPairState: ({match, matchText}) ->
-    [__, __, slash, tagName] = match
-    if slash is ''
-      ['open', tagName]
-    else
-      ['close', tagName]

   getTagStartPoint: (from) ->
     tagRange = null
@@ -403,69 +451,23 @@
         stop()
     tagRange?.start ? from

-  findTagState: (stack, tagState) ->
-    return null if stack.length is 0
-    for i in [(stack.length - 1)..0]
-      entry = stack[i]
-      if entry.tagState is tagState
-        return entry
-    null
-
-  findPair: (which, options, fn) ->
-    {from, pattern, scanFunc, scanRange} = options
-    @editor[scanFunc] pattern, scanRange, (event) =>
-      {matchText, range, stop} = event
-      unless @allowNextLine or (from.row is range.start.row)
-        return stop()
-      return if isEscapedCharAtPoint(@editor, range.start)
-      fn(event)
-
-  findOpen: (from,  pattern) ->
-    scanFunc = 'backwardsScanInBufferRange'
-    scanRange = new Range([0, 0], from)
-    stack = []
-    found = null
-    @findPair 'open', {from, pattern, scanFunc, scanRange}, (event) =>
-      {range, stop} = event
-      [pairState, tagName] = @getPairState(event)
-      if pairState is 'close'
-        tagState = pairState + tagName
-        stack.push({tagState, range})
-      else
-        if entry = @findTagState(stack, "close#{tagName}")
-          stack = stack[0...stack.indexOf(entry)]
-        if stack.length is 0
-          found = range
-      stop() if found?
-    found
-
-  findClose: (from,  pattern) ->
-    scanFunc = 'scanInBufferRange'
+  getPairInfo: (from) ->
     from = @getTagStartPoint(from)
-    scanRange = new Range(from, @editor.buffer.getEndPosition())
-    stack = []
-    found = null
-    @findPair 'close', {from, pattern, scanFunc, scanRange}, (event) =>
-      {range, stop} = event
-      [pairState, tagName] = @getPairState(event)
-      if pairState is 'open'
-        tagState = pairState + tagName
-        stack.push({tagState, range})
-      else
-        if entry = @findTagState(stack, "open#{tagName}")
-          stack = stack[0...stack.indexOf(entry)]
-        else
-          # I'm very torelant for orphan tag like 'br', 'hr', or unclosed tag.
-          stack = []
-        if stack.length is 0
-          if (openStart = entry?.range.start)
-            if @allowForwarding
-              return if openStart.row > from.row
-            else
-              return if openStart.isGreaterThan(from)
-          found = range
-      stop() if found?
-    found
+    filters = @getFilters()
+    options = {@editor, from, filters, @allowNextLine, @allowForwarding}
+
+    closeRange = findCloseTagRangeForward(options)
+    if closeRange?
+      options.from = closeRange.end
+      openRange = findOpenTagRangeBackward(options)
+
+    unless (openRange? and closeRange?)
+      return null
+
+    aRange = new Range(openRange.start, closeRange.end)
+    innerRange = new Range(openRange.end, closeRange.start)
+    targetRange = if @isInner() then innerRange else aRange
+    {openRange, closeRange, aRange, innerRange, targetRange}

 class ATag extends Tag
   @extend()


Add spec for position translation with folds + line wrapping

--- spec/atom/renderer-spec.coffee
+++ spec/atom/renderer-spec.coffee
@@ -431,12 +431,17 @@
           renderer.createFold([[4, 10], [4, 15]])

           expect(renderer.screenPositionForBufferPosition([4, 5])).toEqual [4, 5]
+          expect(renderer.bufferPositionForScreenPosition([4, 5])).toEqual [4, 5]
+
           expect(renderer.screenPositionForBufferPosition([4, 15])).toEqual [4, 13]
+          expect(renderer.bufferPositionForScreenPosition([4, 13])).toEqual [4, 15]
+
           expect(renderer.screenPositionForBufferPosition([4, 20])).toEqual [4, 18]
-
-          expect(renderer.bufferPositionForScreenPosition([4, 5])).toEqual [4, 5]
-          expect(renderer.bufferPositionForScreenPosition([4, 13])).toEqual [4, 15]
           expect(renderer.bufferPositionForScreenPosition([4, 18])).toEqual [4, 20]

-
-
+      describe "when there is a fold on a wrapped line", ->
+        it "translates positions accounting for both the fold and the wrapped line", ->
+          renderer.setMaxLineLength(50)
+          renderer.createFold([[3, 51], [3, 58]])
+          expect(renderer.screenPositionForBufferPosition([3, 58])).toEqual [4, 3]
+          expect(renderer.bufferPositionForScreenPosition([4, 3])).toEqual [3, 58]


Remove debug code

Remove debug code

--- lib/unsubscribe-button.cjsx
+++ lib/unsubscribe-button.cjsx
@@ -11,7 +11,6 @@
     @match = @_getMatch()

   render: =>
-    console.log @match
     if @match
       <div className="unsubscribe">
         <button className="btn btn-toolbar" onClick={ => @_onClick()} ref="button">
@@ -27,7 +26,6 @@
     links = doc.getElementsByTagName('a')

     matches = (item for item in links when item.innerText.toLowerCase().indexOf('unsubscribe') > -1)
-    console.log matches
     matches[matches.length - 1]

   _onClick: =>