awards_handler.coffee 9.85 KB
Newer Older
Valery Sizov's avatar
Valery Sizov committed
1
class @AwardsHandler
Fatih Acet's avatar
Fatih Acet committed
2

3
  constructor: ->
Fatih Acet's avatar
Fatih Acet committed
4

Fatih Acet's avatar
Fatih Acet committed
5
    @aliases = gl.emojiAliases()
6

7
    $(document)
Fatih Acet's avatar
Fatih Acet committed
8
      .off 'click', '.js-add-award'
9 10 11
      .on  'click', '.js-add-award', (e) =>
        e.stopPropagation()
        e.preventDefault()
12

13
        @showEmojiMenu $(e.currentTarget)
14

15 16 17 18 19 20 21
    $('html').on 'click', (e) ->
      $target = $ e.target

      unless $target.closest('.emoji-menu-content').length
        $('.js-awards-block.current').removeClass 'current'

      unless $target.closest('.emoji-menu').length
Fatih Acet's avatar
Fatih Acet committed
22
        if $('.emoji-menu').is(':visible')
23
          $('.js-add-award.is-active').removeClass 'is-active'
Fatih Acet's avatar
Fatih Acet committed
24
          $('.emoji-menu').removeClass 'is-visible'
25

26
    $(document)
Fatih Acet's avatar
Fatih Acet committed
27
      .off 'click', '.js-emoji-btn'
28 29
      .on  'click', '.js-emoji-btn', (e) =>
        e.preventDefault()
30

31 32
        $target = $ e.currentTarget
        emoji   = $target.find('.icon').data 'emoji'
33

34
        $target.closest('.js-awards-block').addClass 'current'
35
        @addAward @getVotesBlock(), @getAwardUrl(), emoji
36

37

38
  showEmojiMenu: ($addBtn) ->
39

40 41
    $menu = $ '.emoji-menu'

Fatih Acet's avatar
Fatih Acet committed
42
    if $addBtn.hasClass 'js-note-emoji'
Fatih Acet's avatar
Fatih Acet committed
43
      $addBtn.closest('.note').find('.js-awards-block').addClass 'current'
44 45
    else
      $addBtn.closest('.js-awards-block').addClass 'current'
46

47 48
    if $menu.length
      $holder = $addBtn.closest('.js-award-holder')
49

Fatih Acet's avatar
Fatih Acet committed
50 51 52 53
      if $menu.is '.is-visible'
        $addBtn.removeClass 'is-active'
        $menu.removeClass 'is-visible'
        $('#emoji_search').blur()
54
      else
Fatih Acet's avatar
Fatih Acet committed
55
        $addBtn.addClass 'is-active'
56 57
        @positionMenu($menu, $addBtn)

Fatih Acet's avatar
Fatih Acet committed
58 59
        $menu.addClass 'is-visible'
        $('#emoji_search').focus()
60
    else
61
      $addBtn.addClass 'is-loading is-active'
Fatih Acet's avatar
Fatih Acet committed
62
      url = @getAwardMenuUrl()
63

64 65 66
      @createEmojiMenu url, =>
        $addBtn.removeClass 'is-loading'
        $menu = $('.emoji-menu')
67
        @positionMenu($menu, $addBtn)
68
        @renderFrequentlyUsedBlock() unless @frequentEmojiBlockRendered
69

Phil Hughes's avatar
Phil Hughes committed
70
        setTimeout =>
71 72
          $menu.addClass 'is-visible'
          $('#emoji_search').focus()
Phil Hughes's avatar
Phil Hughes committed
73 74
          @setupSearch()
        , 200
75

76 77 78

  createEmojiMenu: (awardMenuUrl, callback) ->

79
    $.get awardMenuUrl, (response) ->
80 81 82 83
      $('body').append response
      callback()


84
  positionMenu: ($menu, $addBtn) ->
Fatih Acet's avatar
Fatih Acet committed
85

86 87 88 89 90 91 92 93 94
    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"
Fatih Acet's avatar
Fatih Acet committed
95
      $menu.addClass 'is-aligned-right'
96 97
    else
      css.left = "#{$addBtn.offset().left}px"
Fatih Acet's avatar
Fatih Acet committed
98
      $menu.removeClass 'is-aligned-right'
99 100 101

    $menu.css(css)

102

Fatih Acet's avatar
Fatih Acet committed
103
  addAward: (votesBlock, awardUrl, emoji, checkMutuality = true, callback) ->
104

105
    emoji = @normilizeEmojiName emoji
106

107
    @postEmoji awardUrl, emoji, =>
108
      @addAwardToEmojiBar votesBlock, emoji, checkMutuality
109
      callback?()
110

111
    $('.emoji-menu').removeClass 'is-visible'
112

113

Fatih Acet's avatar
Fatih Acet committed
114
  addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = true) ->
115

116 117
    @checkMutuality votesBlock, emoji  if checkForMutuality
    @addEmojiToFrequentlyUsedList emoji
118

Fatih Acet's avatar
Fatih Acet committed
119 120
    emoji        = @normilizeEmojiName emoji
    $emojiButton = @findEmojiIcon(votesBlock, emoji).parent()
121

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

135

136 137 138 139
  getVotesBlock: ->

    currentBlock = $ '.js-awards-block.current'
    return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0
140 141


142
  getAwardUrl: -> return @getVotesBlock().data 'award-url'
143 144


145
  checkMutuality: (votesBlock, emoji) ->
146 147 148 149

    awardUrl = @getAwardUrl()

    if emoji in [ 'thumbsup', 'thumbsdown' ]
150 151 152
      mutualVote     = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'
      $emojiButton   = votesBlock.find("[data-emoji=#{mutualVote}]").parent()
      isAlreadyVoted = $emojiButton.hasClass 'active'
153

154 155
      if isAlreadyVoted
        @showEmojiLoader $emojiButton
Fatih Acet's avatar
Fatih Acet committed
156
        @addAward votesBlock, awardUrl, mutualVote, false, ->
157 158 159 160 161 162 163 164 165 166 167
          $emojiButton.removeClass 'is-loading'


  showEmojiLoader: ($emojiButton) ->

    $loader = $emojiButton.find '.fa-spinner'

    unless $loader.length
      $emojiButton.append '<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>'

    $emojiButton.addClass 'is-loading'
168 169


Fatih Acet's avatar
Fatih Acet committed
170
  isActive: ($emojiButton) -> $emojiButton.hasClass 'active'
Fatih Acet's avatar
Fatih Acet committed
171

172

Fatih Acet's avatar
Fatih Acet committed
173
  decrementCounter: ($emojiButton, emoji) ->
174

Fatih Acet's avatar
Fatih Acet committed
175
    counter       = $ '.js-counter', $emojiButton
176
    counterNumber = parseInt counter.text(), 10
177

178
    if counterNumber > 1
179
      counter.text counterNumber - 1
Fatih Acet's avatar
Fatih Acet committed
180
      @removeMeFromUserList $emojiButton, emoji
181
    else if emoji is 'thumbsup' or emoji is 'thumbsdown'
Fatih Acet's avatar
Fatih Acet committed
182
      $emojiButton.tooltip 'destroy'
183
      counter.text '0'
Fatih Acet's avatar
Fatih Acet committed
184 185
      @removeMeFromUserList $emojiButton, emoji
      @removeEmoji $emojiButton if $emojiButton.parents('.note').length
Valery Sizov's avatar
Valery Sizov committed
186
    else
Fatih Acet's avatar
Fatih Acet committed
187
      @removeEmoji $emojiButton
188

Fatih Acet's avatar
Fatih Acet committed
189
    $emojiButton.removeClass 'active'
190

191

Fatih Acet's avatar
Fatih Acet committed
192
  removeEmoji: ($emojiButton) ->
193

Fatih Acet's avatar
Fatih Acet committed
194 195
    $emojiButton.tooltip('destroy')
    $emojiButton.remove()
196 197 198 199 200 201 202

    $votesBlock = @getVotesBlock()

    if $votesBlock.find('.js-emoji-btn').length is 0
      $votesBlock.addClass 'hidden'


203 204
  getAwardTooltip: ($awardBlock) ->

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


Fatih Acet's avatar
Fatih Acet committed
208
  removeMeFromUserList: ($emojiButton, emoji) ->
209

Fatih Acet's avatar
Fatih Acet committed
210
    awardBlock    = $emojiButton
211 212 213 214 215 216 217 218 219 220 221 222
    originalTitle = @getAwardTooltip awardBlock

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

    newAuthors = authors.join ', '

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

223
    @resetTooltip awardBlock
224

225

226
  addMeToUserList: (votesBlock, emoji) ->
227

228
    awardBlock = @findEmojiIcon(votesBlock, emoji).parent()
229 230 231
    origTitle  = @getAwardTooltip awardBlock
    users      = []

232
    if origTitle
233
      users = origTitle.trim().split ', '
234

235 236
    users.push 'me'
    awardBlock.attr 'title', users.join ', '
237

238
    @resetTooltip awardBlock
239

Valery Sizov's avatar
Valery Sizov committed
240 241

  resetTooltip: (award) ->
242 243

    award.tooltip 'destroy'
Valery Sizov's avatar
Valery Sizov committed
244

Fatih Acet's avatar
Fatih Acet committed
245
    # 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
246 247
    cb = -> award.tooltip()
    setTimeout cb, 200
248

249

250
  createEmoji_: (votesBlock, emoji) ->
251 252

    emojiCssClass = @resolveNameToCssClass emoji
253
    buttonHtml    = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
254 255 256 257
      <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
      <span class='award-control-text js-counter'>1</span>
    </button>"

Fatih Acet's avatar
Fatih Acet committed
258
    $emojiButton = $ buttonHtml
Fatih Acet's avatar
Fatih Acet committed
259 260
    $emojiButton
      .insertBefore votesBlock.find '.js-award-holder'
261 262 263
      .find '.emoji-icon'
      .data 'emoji', emoji

Fatih Acet's avatar
Fatih Acet committed
264
    @animateEmoji $emojiButton
Phil Hughes's avatar
Phil Hughes committed
265
    $('.award-control').tooltip()
266
    votesBlock.removeClass 'current'
267

268

Fatih Acet's avatar
Fatih Acet committed
269 270 271 272 273 274 275 276
  animateEmoji: ($emoji) ->

    className = 'pulse animated'

    $emoji.addClass className
    setTimeout (-> $emoji.removeClass className), 321


277
  createEmoji: (votesBlock, emoji) ->
278

279 280
    if $('.emoji-menu').length
      return @createEmoji_ votesBlock, emoji
281

Fatih Acet's avatar
Fatih Acet committed
282
    @createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
Fatih Acet's avatar
Fatih Acet committed
283 284


285
  getAwardMenuUrl: -> return gon.award_menu_url
286 287


288
  resolveNameToCssClass: (emoji) ->
289

Fatih Acet's avatar
Fatih Acet committed
290
    emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']"
291

Fatih Acet's avatar
Fatih Acet committed
292 293
    if emojiIcon.length > 0
      unicodeName = emojiIcon.data 'unicode-name'
294 295
    else
      # Find by alias
Fatih Acet's avatar
Fatih Acet committed
296
      unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name'
297

298 299
    return "emoji-#{unicodeName}"

Valery Sizov's avatar
Valery Sizov committed
300

301
  postEmoji: (awardUrl, emoji, callback) ->
302

303
    $.post awardUrl, { name: emoji }, (data) ->
Fatih Acet's avatar
Fatih Acet committed
304
      callback() if data.ok
305

306

307
  findEmojiIcon: (votesBlock, emoji) ->
308

309
    return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']"
310

311 312

  scrollToAwards: ->
Valery Sizov's avatar
Valery Sizov committed
313

314
    options = scrollTop: $('.awards').offset().top - 110
315 316 317 318 319
    $('body, html').animate options, 200


  normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji

320

321
  addEmojiToFrequentlyUsedList: (emoji) ->
322 323 324 325 326

    frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
    frequentlyUsedEmojis.push emoji
    $.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }

327 328

  getFrequentlyUsedEmojis: ->
329 330 331 332

    frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',')
    return _.compact _.uniq frequentlyUsedEmojis

333 334

  renderFrequentlyUsedBlock: ->
335 336 337

    if $.cookie 'frequently_used_emojis'
      frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
338

Fatih Acet's avatar
Fatih Acet committed
339
      ul = $("<ul class='clearfix emoji-menu-list frequent-emojis'>")
340

341
      for emoji in frequentlyUsedEmojis
Fatih Acet's avatar
Fatih Acet committed
342
        $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
343

Fatih Acet's avatar
Fatih Acet committed
344
      $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
345

Fatih Acet's avatar
Fatih Acet committed
346
    @frequentEmojiBlockRendered = true
347

348

Valery Sizov's avatar
Valery Sizov committed
349
  setupSearch: ->
350

Fatih Acet's avatar
Fatih Acet committed
351
    $('input.emoji-search').on 'keyup', (ev) =>
Valery Sizov's avatar
Valery Sizov committed
352 353 354
      term = $(ev.target).val()

      # Clean previous search results
Fatih Acet's avatar
Fatih Acet committed
355
      $('ul.emoji-menu-search, h5.emoji-search').remove()
Valery Sizov's avatar
Valery Sizov committed
356 357

      if term
358
        # Generate a search result block
Fatih Acet's avatar
Fatih Acet committed
359
        h5 = $('<h5>').text('Search results').addClass('emoji-search')
360
        found_emojis = @searchEmojis(term).show()
Fatih Acet's avatar
Fatih Acet committed
361 362 363
        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
364
      else
Fatih Acet's avatar
Fatih Acet committed
365
        $('.emoji-menu-content').children().show()
Valery Sizov's avatar
Valery Sizov committed
366

367 368 369

  searchEmojis: (term) ->

Fatih Acet's avatar
Fatih Acet committed
370
    $(".emoji-menu-list:not(.frequent-emojis) [data-emoji*='#{term}']").closest('li').clone()