<commit_before>class @PrivateMessagesView extends Backbone.View
  messageTemplate: JST['private_message']

  events:
    "keypress .chat_input"  : "sendMessageEvent"
    "click .challenge_button, .cancel_challenge" : "toggleChallengeEvent"
    "click .popup_messages" : "focusChatEvent"
    "click .title_minimize" : "minimizePopupEvent"
    "click .title_close" : "closePopupEvent"
    "challenge .popup" : "sendChallengeEvent"
    "cancelChallenge .popup" : "challengeCanceledEvent"
    "focus .popup" : "focusPopupEvent"

  initialize: =>
    @numPopups = 0
    @listenTo(@collection, 'open', @createPopup)
    @listenTo(@collection, 'receive', @receiveMessage)
    @listenTo(@collection, 'close', @closePopup)
    @listenTo(@collection, 'minimize', @minimizePopup)
    @listenTo(@collection, 'openChallenge', @openChallenge)
    @listenTo(@collection, 'cancelChallenge', @cancelChallenge)
    @listenTo(@collection, 'closeChallenge', @closeChallenge)

    @listenTo(PokeBattle.userList, 'add', @notifyJoin)
    @listenTo(PokeBattle.userList, 'remove', @notifyLeave)

  createPopup: (message) =>
    title = id = message.id
    $html = @$findPopup(id)
    if @$findPopup(id).length == 0
      $html = $(@messageTemplate({window, id, title}))

      @$el.append($html)
      @positionPopup($html, @numPopups)
      @numPopups += 1
    $html.find('input').focus()
    $html

  closePopup: (message) =>
    username = message.id
    @$findPopup(username).remove()
    @repositionPopups()
    @numPopups -= 1

  minimizePopup: (message) =>
    username = message.id
    $popup = @$findPopup(username)
    $body = $popup.find('.popup_body')
    $body.toggleClass('hidden')

  receiveMessage: (messageId, username, message, options) =>
    $popup = @$findOrCreatePopup(messageId)
    $messages = $popup.find('.popup_messages')
    wasAtBottom = @isAtBottom()
    if options.type == 'error'
      $messages.append("<p class='privmsg-error'>#{message}</p>")
    else if options.type == 'alert'
      $messages.append("<p class='privmsg-alert'>#{message}</p>")
    else
      $popup.addClass('new_message')  if username != "Me"
      $messages.append("<p><strong>#{username}:</strong> #{message}</p>")
    if wasAtBottom then @scrollToBottom()

  openChallenge: (messageId, generation, options = {}) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = @createChallenge($popup, generation, options)
    if generation
      $challenge.find('.is_not_challenger').addClass('hidden')
      $challenge.find('.is_challenger').removeClass('hidden')

  cancelChallenge: (messageId) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = $popup.find('.challenge')
    $challenge.find('.icon-spinner').addClass('hidden')
    $challenge.find('.send_challenge, .select').removeClass('disabled')
    $challenge.find('.challenge_text').text("Challenge")
    $challenge.find(".cancel_challenge").text('Close')

  closeChallenge: (messageId) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = $popup.find('.challenge')
    $challenge.addClass('hidden')

  notifyJoin: (user) =>
    message = @collection.get(user.id)
    message?.add(user.id, "#{user.id} is now online!", type: "alert")

  notifyLeave: (user) =>
    message = @collection.get(user.id)
    message?.add(user.id, "#{user.id} is now offline.", type: "alert")

  # Returns true if the chat is scrolled to the bottom of the screen.
  # This also returns true if the messages are hidden.
  isAtBottom: =>
    $el = @$('.popup_messages')
    ($el[0].scrollHeight - $el.scrollTop() <= $el.outerHeight())

  scrollToBottom: =>
    messages = @$('.popup_messages')[0]
    messages.scrollTop = messages.scrollHeight
    false

  positionPopup: ($popup, index) =>
    leftOffset = $('#content').position().left
    $popup.css(left: leftOffset + index * $popup.outerWidth(true))

  repositionPopups: =>
    @$(".popup").each (index, self) =>
      @positionPopup($(self), index)

  $findPopup: (id) =>
    @$(".popup[data-user-id='#{id}']")

  $findOrCreatePopup: (messageId) =>
    $popup = @$findPopup(messageId)
    $popup = @createPopup(@collection.get(messageId))  if $popup.length == 0
    $popup

  $closestPopup: (target) =>
    $target = $(target)
    return $target  if $target.hasClass("popup")
    return $target.closest(".popup")

  messageFromPopup: (target) =>
    $popup = @$closestPopup(target)
    message = @collection.get($popup.data('user-id'))
    return message

  createChallenge: ($popup, generation, options = {}) =>
    $challenge = $popup.find('.challenge')
    $challenge.html(JST['challenge']())
    createChallengePane
      eventName: "challenge"
      button: $popup.find('.send_challenge')
      cancel_button: $popup.find('.cancel_challenge')
      acceptButton: $popup.find('.accept_challenge')
      rejectButton: $popup.find('.reject_challenge')
      populate: $popup.find(".challenge_data")
      generation: generation
      personId: $popup.data('user-id')
    $challenge.removeClass('hidden')
    $challenge

  ##########
  # EVENTS #
  ##########

  sendMessageEvent: (e) =>
    switch e.which
      when 13 # [ Enter ]
        $input = $(e.currentTarget)
        message = @messageFromPopup(e.currentTarget)
        text = $input.val()
        return  if text.length == 0
        PokeBattle.socket.send('privateMessage', message.id, text)
        message.add("Me", text)
        $input.val('')

  minimizePopupEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('minimize', message)

  closePopupEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('close', message)

  focusChatEvent: (e) =>
    @$closestPopup(e.currentTarget).find('input').focus()

  toggleChallengeEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $challenge = $popup.find('.challenge')
    if $challenge.hasClass("hidden")
      @createChallenge($popup)
    else if $challenge.find('.cancel_challenge').text() == 'Cancel'
      $popup.find('.send_challenge').click()
    else
      $challenge.addClass('hidden')

  sendChallengeEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $challenge = $popup.find('.challenge')
    $challenge.find(".icon-spinner").removeClass('hidden')
    $challenge.find(".challenge_text").text('Challenging...')
    $challenge.find(".cancel_challenge").text('Cancel')

  challengeCanceledEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('cancelChallenge', message.id)

  focusPopupEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $popup.removeClass('new_message')
<commit_msg>Client: Only blink red if not focused.<commit_after>class @PrivateMessagesView extends Backbone.View
  messageTemplate: JST['private_message']

  events:
    "keypress .chat_input"  : "sendMessageEvent"
    "click .challenge_button, .cancel_challenge" : "toggleChallengeEvent"
    "click .popup_messages" : "focusChatEvent"
    "click .title_minimize" : "minimizePopupEvent"
    "click .title_close" : "closePopupEvent"
    "challenge .popup" : "sendChallengeEvent"
    "cancelChallenge .popup" : "challengeCanceledEvent"
    "focus .popup" : "focusPopupEvent"

  initialize: =>
    @numPopups = 0
    @listenTo(@collection, 'open', @createPopup)
    @listenTo(@collection, 'receive', @receiveMessage)
    @listenTo(@collection, 'close', @closePopup)
    @listenTo(@collection, 'minimize', @minimizePopup)
    @listenTo(@collection, 'openChallenge', @openChallenge)
    @listenTo(@collection, 'cancelChallenge', @cancelChallenge)
    @listenTo(@collection, 'closeChallenge', @closeChallenge)

    @listenTo(PokeBattle.userList, 'add', @notifyJoin)
    @listenTo(PokeBattle.userList, 'remove', @notifyLeave)

  createPopup: (message) =>
    title = id = message.id
    $html = @$findPopup(id)
    if @$findPopup(id).length == 0
      $html = $(@messageTemplate({window, id, title}))

      @$el.append($html)
      @positionPopup($html, @numPopups)
      @numPopups += 1
    $html.find('input').focus()
    $html

  closePopup: (message) =>
    username = message.id
    @$findPopup(username).remove()
    @repositionPopups()
    @numPopups -= 1

  minimizePopup: (message) =>
    username = message.id
    $popup = @$findPopup(username)
    $body = $popup.find('.popup_body')
    $body.toggleClass('hidden')

  receiveMessage: (messageId, username, message, options) =>
    $popup = @$findOrCreatePopup(messageId)
    $messages = $popup.find('.popup_messages')
    wasAtBottom = @isAtBottom()
    if options.type == 'error'
      $messages.append("<p class='privmsg-error'>#{message}</p>")
    else if options.type == 'alert'
      $messages.append("<p class='privmsg-alert'>#{message}</p>")
    else
      if username != "Me" && !$popup.find('.chat_input').is(":focus")
        $popup.addClass('new_message')
      $messages.append("<p><strong>#{username}:</strong> #{message}</p>")
    if wasAtBottom then @scrollToBottom()

  openChallenge: (messageId, generation, options = {}) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = @createChallenge($popup, generation, options)
    if generation
      $challenge.find('.is_not_challenger').addClass('hidden')
      $challenge.find('.is_challenger').removeClass('hidden')

  cancelChallenge: (messageId) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = $popup.find('.challenge')
    $challenge.find('.icon-spinner').addClass('hidden')
    $challenge.find('.send_challenge, .select').removeClass('disabled')
    $challenge.find('.challenge_text').text("Challenge")
    $challenge.find(".cancel_challenge").text('Close')

  closeChallenge: (messageId) =>
    $popup = @$findOrCreatePopup(messageId)
    $challenge = $popup.find('.challenge')
    $challenge.addClass('hidden')

  notifyJoin: (user) =>
    message = @collection.get(user.id)
    message?.add(user.id, "#{user.id} is now online!", type: "alert")

  notifyLeave: (user) =>
    message = @collection.get(user.id)
    message?.add(user.id, "#{user.id} is now offline.", type: "alert")

  # Returns true if the chat is scrolled to the bottom of the screen.
  # This also returns true if the messages are hidden.
  isAtBottom: =>
    $el = @$('.popup_messages')
    ($el[0].scrollHeight - $el.scrollTop() <= $el.outerHeight())

  scrollToBottom: =>
    messages = @$('.popup_messages')[0]
    messages.scrollTop = messages.scrollHeight
    false

  positionPopup: ($popup, index) =>
    leftOffset = $('#content').position().left
    $popup.css(left: leftOffset + index * $popup.outerWidth(true))

  repositionPopups: =>
    @$(".popup").each (index, self) =>
      @positionPopup($(self), index)

  $findPopup: (id) =>
    @$(".popup[data-user-id='#{id}']")

  $findOrCreatePopup: (messageId) =>
    $popup = @$findPopup(messageId)
    $popup = @createPopup(@collection.get(messageId))  if $popup.length == 0
    $popup

  $closestPopup: (target) =>
    $target = $(target)
    return $target  if $target.hasClass("popup")
    return $target.closest(".popup")

  messageFromPopup: (target) =>
    $popup = @$closestPopup(target)
    message = @collection.get($popup.data('user-id'))
    return message

  createChallenge: ($popup, generation, options = {}) =>
    $challenge = $popup.find('.challenge')
    $challenge.html(JST['challenge']())
    createChallengePane
      eventName: "challenge"
      button: $popup.find('.send_challenge')
      cancel_button: $popup.find('.cancel_challenge')
      acceptButton: $popup.find('.accept_challenge')
      rejectButton: $popup.find('.reject_challenge')
      populate: $popup.find(".challenge_data")
      generation: generation
      personId: $popup.data('user-id')
    $challenge.removeClass('hidden')
    $challenge

  ##########
  # EVENTS #
  ##########

  sendMessageEvent: (e) =>
    switch e.which
      when 13 # [ Enter ]
        $input = $(e.currentTarget)
        message = @messageFromPopup(e.currentTarget)
        text = $input.val()
        return  if text.length == 0
        PokeBattle.socket.send('privateMessage', message.id, text)
        message.add("Me", text)
        $input.val('')

  minimizePopupEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('minimize', message)

  closePopupEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('close', message)

  focusChatEvent: (e) =>
    @$closestPopup(e.currentTarget).find('input').focus()

  toggleChallengeEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $challenge = $popup.find('.challenge')
    if $challenge.hasClass("hidden")
      @createChallenge($popup)
    else if $challenge.find('.cancel_challenge').text() == 'Cancel'
      $popup.find('.send_challenge').click()
    else
      $challenge.addClass('hidden')

  sendChallengeEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $challenge = $popup.find('.challenge')
    $challenge.find(".icon-spinner").removeClass('hidden')
    $challenge.find(".challenge_text").text('Challenging...')
    $challenge.find(".cancel_challenge").text('Cancel')

  challengeCanceledEvent: (e) =>
    message = @messageFromPopup(e.currentTarget)
    message.trigger('cancelChallenge', message.id)

  focusPopupEvent: (e) =>
    $popup = @$closestPopup(e.currentTarget)
    $popup.removeClass('new_message')

{'fraction_non_alphanumeric': 0.170797656602073, 'fraction_numerical': 0.0015021781583295778, 'mean_word_length': 3.9943735933983495, 'pattern_counts': {'":': 1, '<': 21, '<?xml version=': 0, '>': 75, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1078255', 'n_tokens_mistral': 4409, 'n_tokens_neox': 4208, 'n_words': 743}