awards_handler.coffee 8.68 KB
Newer Older
Valery Sizov's avatar
Valery Sizov committed
1
class @AwardsHandler
2 3 4
  constructor: ->
    @aliases = emojiAliases()

5 6 7 8 9
    $(document)
      .off "click", ".js-add-award"
      .on "click", ".js-add-award", (event) =>
        event.stopPropagation()
        event.preventDefault()
10

11
        @showEmojiMenu $(event.currentTarget)
12

13 14 15
    $("html").on 'click', (event) ->
      if !$(event.target).closest(".emoji-menu").length
        if $(".emoji-menu").is(":visible")
16
          $('.js-add-award.is-active').removeClass 'is-active'
17
          $(".emoji-menu").removeClass "is-visible"
18

19 20
    $(document)
      .off "click", ".js-emoji-btn"
21
      .on "click", ".js-emoji-btn", @handleClick
Valery Sizov's avatar
Valery Sizov committed
22

23

24
  handleClick: (e) =>
25

Phil Hughes's avatar
Phil Hughes committed
26
    e.preventDefault()
27

28
    $emojiBtn = $(e.currentTarget)
29 30
    $addAwardBtn = $('.js-add-award.is-active')
    $votesBlock = $($addAwardBtn.closest('.js-award-holder').data('target'))
31

32
    if $addAwardBtn.length is 0
33
      $votesBlock = $emojiBtn.closest('.js-awards-block')
34 35
    else if $votesBlock.length is 0
      $votesBlock = $addAwardBtn.closest('.js-awards-block')
36

37
    $votesBlock.addClass 'js-awards-block'
38
    awardUrl = $votesBlock.data 'award-url'
39
    emoji = $emojiBtn.find('.icon').data('emoji')
40 41 42 43 44 45 46

    if emoji in [ 'thumbsup', 'thumbsdown' ]
      mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'

      isAlreadyVoted = $("[data-emoji=#{mutualVote}]").parent().hasClass 'active'
      @addAward awardUrl, mutualVote if isAlreadyVoted

47
    @addAward awardUrl, emoji
48

49

50
  showEmojiMenu: ($addBtn) ->
51

52
    $menu = $('.emoji-menu')
53

54 55
    if $menu.length
      $holder = $addBtn.closest('.js-award-holder')
56

57 58 59
      if $menu.is ".is-visible"
        $addBtn.removeClass "is-active"
        $menu.removeClass "is-visible"
60
        $("#emoji_search").blur()
61
      else
62 63 64 65
        $addBtn.addClass "is-active"
        @positionMenu($menu, $addBtn)

        $menu.addClass "is-visible"
66
        $("#emoji_search").focus()
67
    else
68 69
      $addBtn.addClass 'is-loading is-active'
      url = $addBtn.data 'award-menu-url'
70

71 72 73
      @createEmojiMenu url, =>
        $addBtn.removeClass 'is-loading'
        $menu = $('.emoji-menu')
74
        @positionMenu($menu, $addBtn)
75
        @renderFrequentlyUsedBlock()
76

Phil Hughes's avatar
Phil Hughes committed
77
        setTimeout =>
78 79
          $menu.addClass 'is-visible'
          $('#emoji_search').focus()
Phil Hughes's avatar
Phil Hughes committed
80 81
          @setupSearch()
        , 200
82

83 84 85 86 87 88 89 90

  createEmojiMenu: (awardMenuUrl, callback) ->

    $.get awardMenuUrl, (response) =>
      $('body').append response
      callback()


91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  positionMenu: ($menu, $addBtn) ->
    position = $addBtn.data('position')

    # The menu could potentially be off-screen or in a hidden overflow element
    # So we position the element absolute in the body
    css =
      top: "#{$addBtn.offset().top + $addBtn.outerHeight()}px"

    if position? and position is 'right'
      css.left = "#{($addBtn.offset().left - $menu.outerWidth()) + 20}px"
      $menu.addClass "is-aligned-right"
    else
      css.left = "#{$addBtn.offset().left}px"
      $menu.removeClass "is-aligned-right"

    $menu.css(css)

108 109 110
  addAward: (awardUrl, emoji) ->
    emoji = @normilizeEmojiName(emoji)
    @postEmoji awardUrl, emoji, =>
111
      @addAwardToEmojiBar(emoji)
112

113 114
      $('.js-awards-block-current').removeClass 'js-awards-block-current'

115
    $(".emoji-menu").removeClass "is-visible"
116

117
  addAwardToEmojiBar: (emoji) ->
118 119
    @addEmojiToFrequentlyUsedList(emoji)

120
    emoji = @normilizeEmojiName(emoji)
121
    $emojiBtn = @findEmojiIcon(emoji).parent()
122 123 124

    if $emojiBtn.length > 0
      if @isActive($emojiBtn)
125
        @decrementCounter($emojiBtn, emoji)
126
      else
Fatih Acet's avatar
Fatih Acet committed
127
        counter = $emojiBtn.find('.js-counter')
128
        counter.text(parseInt(counter.text()) + 1)
129
        $emojiBtn.addClass("active")
130
        @addMeToUserList(emoji)
131
    else
132
      @createEmoji(emoji)
Valery Sizov's avatar
Valery Sizov committed
133

134
  isActive: ($emojiBtn) ->
135 136 137 138 139 140 141
    $emojiBtn.hasClass("active")

  decrementCounter: ($emojiBtn, emoji) ->
    isntNoteBody = $emojiBtn.closest('.note-body').length is 0
    counter = $('.js-counter', $emojiBtn)
    counterNumber = parseInt(counter.text())

142 143 144 145
    if !isntNoteBody
      # If this is a note body, we just hide the award emoji row like the initial state
      $emojiBtn.closest('.js-awards-block').addClass 'hidden'

146 147 148 149 150 151 152
    if counterNumber > 1
      counter.text(counterNumber - 1)
      @removeMeFromUserList($emojiBtn, emoji)
    else if (emoji == "thumbsup" || emoji == "thumbsdown") && isntNoteBody
      $emojiBtn.tooltip("destroy")
      counter.text('0')
      @removeMeFromUserList($emojiBtn, emoji)
Valery Sizov's avatar
Valery Sizov committed
153
    else
154 155
      $emojiBtn.tooltip("destroy")
      $emojiBtn.remove()
156

157 158
    $emojiBtn.removeClass("active")

159 160 161 162 163 164

  getAwardTooltip: ($awardBlock) ->

    return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title')


165
  removeMeFromUserList: ($emojiBtn, emoji) ->
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

    awardBlock    = $emojiBtn
    originalTitle = @getAwardTooltip awardBlock

    authors = originalTitle.split ', '
    authors.splice authors.indexOf('me'), 1

    newAuthors = authors.join ', '

    awardBlock
      .closest '.js-emoji-btn'
      .removeData 'original-title'
      .removeData 'title'
      .attr 'data-original-title', newAuthors
      .attr 'data-title', newAuthors

    @resetTooltip(awardBlock)

184 185

  addMeToUserList: (emoji) ->
186 187 188 189 190

    awardBlock = @findEmojiIcon(emoji).parent()
    origTitle  = @getAwardTooltip awardBlock
    users      = []

191
    if origTitle
192 193 194 195 196 197 198
      users = origTitle.trim().split(', ')

    users.push('me')
    awardBlock.attr('title', users.join(", "))

    @resetTooltip(awardBlock)

Valery Sizov's avatar
Valery Sizov committed
199 200

  resetTooltip: (award) ->
201
    award.tooltip("destroy")
Valery Sizov's avatar
Valery Sizov committed
202

Valery Sizov's avatar
Valery Sizov committed
203
    # "destroy" call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
Valery Sizov's avatar
Valery Sizov committed
204 205 206
    setTimeout (->
      award.tooltip()
    ), 200
207

208

209 210 211
  createEmoji_: (emoji) ->

    emojiCssClass = @resolveNameToCssClass emoji
212

213
    buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
214 215 216 217 218
      <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
      <span class='award-control-text js-counter'>1</span>
    </button>"

    emoji_node = $(buttonHtml)
219 220 221 222
      .insertBefore '.js-awards-block .js-award-holder:not(.js-award-action-btn)'
      .find '.emoji-icon'
      .data 'emoji', emoji

Phil Hughes's avatar
Phil Hughes committed
223
    $('.award-control').tooltip()
Valery Sizov's avatar
Valery Sizov committed
224

225 226 227
    $currentBlock = $ '.js-awards-block'

    if $currentBlock.is '.hidden'
228 229
      $currentBlock.removeClass 'hidden'

230 231 232 233 234 235 236 237 238

  createEmoji: (emoji) ->

    return @createEmoji_ emoji if $('.emoji-menu').length

    awardMenuUrl = $('[data-award-menu-url]').data 'award-menu-url'
    @createEmojiMenu awardMenuUrl, => @createEmoji emoji


239
  resolveNameToCssClass: (emoji) ->
240

241
    emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']")
242

243 244
    if emoji_icon.length > 0
      unicodeName = emoji_icon.data("unicode-name")
245 246
    else
      # Find by alias
247
      unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data("unicode-name")
248

249 250
    return "emoji-#{unicodeName}"

Valery Sizov's avatar
Valery Sizov committed
251

252 253
  postEmoji: (awardUrl, emoji, callback) ->
    $.post awardUrl, { name: emoji }, (data) ->
Valery Sizov's avatar
Valery Sizov committed
254
      if data.ok
255 256 257
        callback.call()

  findEmojiIcon: (emoji) ->
258
    $(".js-awards-block.awards > .js-emoji-btn [data-emoji='#{emoji}']")
259 260 261 262 263

  scrollToAwards: ->
    $('body, html').animate({
      scrollTop: $('.awards').offset().top - 80
    }, 200)
Valery Sizov's avatar
Valery Sizov committed
264

265 266 267
  normilizeEmojiName: (emoji) ->
    @aliases[emoji] || emoji

268
  addEmojiToFrequentlyUsedList: (emoji) ->
269 270 271
    frequently_used_emojis = @getFrequentlyUsedEmojis()
    frequently_used_emojis.push(emoji)
    $.cookie('frequently_used_emojis', frequently_used_emojis.join(","), { expires: 365 })
272 273

  getFrequentlyUsedEmojis: ->
274 275
    frequently_used_emojis = ($.cookie('frequently_used_emojis') || "").split(",")
    _.compact(_.uniq(frequently_used_emojis))
276 277

  renderFrequentlyUsedBlock: ->
278
    if $.cookie('frequently_used_emojis')
279
      frequently_used_emojis = @getFrequentlyUsedEmojis()
280

281
      ul = $("<ul class='clearfix emoji-menu-list'>")
282

283
      for emoji in frequently_used_emojis
284
        $(".emoji-menu-content [data-emoji='#{emoji}']").closest("li").clone().appendTo(ul)
285

286
      $("input.emoji-search").after(ul).after($("<h5>").text("Frequently used"))
287

Valery Sizov's avatar
Valery Sizov committed
288
  setupSearch: ->
289
    $("input.emoji-search").on 'keyup', (ev) =>
Valery Sizov's avatar
Valery Sizov committed
290 291 292
      term = $(ev.target).val()

      # Clean previous search results
293
      $("ul.emoji-menu-search, h5.emoji-search").remove()
Valery Sizov's avatar
Valery Sizov committed
294 295

      if term
296
        # Generate a search result block
297 298 299 300 301
        h5 = $("<h5>").text("Search results").addClass("emoji-search")
        found_emojis = @searchEmojis(term).show()
        ul = $("<ul>").addClass("emoji-menu-list emoji-menu-search").append(found_emojis)
        $(".emoji-menu-content ul, .emoji-menu-content h5").hide()
        $(".emoji-menu-content").append(h5).append(ul)
Valery Sizov's avatar
Valery Sizov committed
302
      else
303
        $(".emoji-menu-content").children().show()
Valery Sizov's avatar
Valery Sizov committed
304 305

  searchEmojis: (term)->
Valery Sizov's avatar
Valery Sizov committed
306
    $(".emoji-menu-content [data-emoji*='#{term}']").closest("li").clone()