notes.js.coffee 22.4 KB
Newer Older
1
#= require autosave
P
Phil Hughes 已提交
2
#= require autosize
3 4 5 6 7 8
#= require dropzone
#= require dropzone_input
#= require gfm_auto_complete
#= require jquery.atwho
#= require task_list

C
Ciro Santilli 已提交
9
class @Notes
10 11
  @interval: null

12
  constructor: (notes_url, note_ids, last_fetched_at, view) ->
13 14
    @notes_url = notes_url
    @note_ids = note_ids
15
    @last_fetched_at = last_fetched_at
16
    @view = view
17
    @noteable_url = document.URL
18
    @notesCountBadge ||= $(".issuable-details").find(".notes-tab .badge")
19 20
    @basePollingInterval = 15000
    @maxPollingSteps = 4
21

22 23
    @cleanBinding()
    @addBinding()
24
    @setPollingInterval()
25
    @setupMainTargetNoteForm()
26
    @initTaskList()
27 28 29 30 31 32

  addBinding: ->
    # add note to UI after creation
    $(document).on "ajax:success", ".js-main-target-form", @addNote
    $(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote

33 34 35
    # catch note ajax errors
    $(document).on "ajax:error", ".js-main-target-form", @addNoteError

36
    # change note in UI after update
P
Phil Hughes 已提交
37
    $(document).on "ajax:success", "form.edit-note", @updateNote
38 39

    # Edit note link
40
    $(document).on "click", ".js-note-edit", @showEditForm
41 42
    $(document).on "click", ".note-edit-cancel", @cancelEdit

43
    # Reopen and close actions for Issue/MR combined with note form submit
44
    $(document).on "click", ".js-comment-button", @updateCloseButton
45
    $(document).on "keyup input", ".js-note-text", @updateTargetButtons
46
    $(document).on 'click', '.js-comment-resolve-button', @resolveDiscussion
47

48 49 50 51 52 53 54
    # remove a note (in general)
    $(document).on "click", ".js-note-delete", @removeNote

    # delete note attachment
    $(document).on "click", ".js-note-attachment-delete", @removeAttachment

    # reset main target form after submit
55 56
    $(document).on "ajax:complete", ".js-main-target-form", @reenableTargetFormSubmitButton
    $(document).on "ajax:success", ".js-main-target-form", @resetMainTargetForm
57

58 59 60
    # reset main target form when clicking discard
    $(document).on "click", ".js-note-discard", @resetMainTargetForm

61 62 63
    # update the file name when an attachment is selected
    $(document).on "change", ".js-note-attachment-input", @updateFormAttachment

64 65 66 67 68 69
    # reply to diff/discussion notes
    $(document).on "click", ".js-discussion-reply-button", @replyToDiscussionNote

    # add diff note
    $(document).on "click", ".js-add-diff-note-button", @addDiffNote

70 71 72
    # hide diff note form
    $(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm

73 74 75
    # fetch notes when tab becomes visible
    $(document).on "visibilitychange", @visibilityChange

J
Jacob Schatz 已提交
76
    # when issue status changes, we need to refresh data
77 78
    $(document).on "issuable:change", @refresh

79 80 81
    # when a key is clicked on the notes
    $(document).on "keydown", ".js-note-text", @keydownNoteText

82 83 84
  cleanBinding: ->
    $(document).off "ajax:success", ".js-main-target-form"
    $(document).off "ajax:success", ".js-discussion-note-form"
P
Phil Hughes 已提交
85
    $(document).off "ajax:success", "form.edit-note"
86
    $(document).off "click", ".js-note-edit"
87 88 89 90
    $(document).off "click", ".note-edit-cancel"
    $(document).off "click", ".js-note-delete"
    $(document).off "click", ".js-note-attachment-delete"
    $(document).off "ajax:complete", ".js-main-target-form"
91
    $(document).off "ajax:success", ".js-main-target-form"
92 93
    $(document).off "click", ".js-discussion-reply-button"
    $(document).off "click", ".js-add-diff-note-button"
94
    $(document).off "visibilitychange"
95 96 97
    $(document).off "keyup", ".js-note-text"
    $(document).off "click", ".js-note-target-reopen"
    $(document).off "click", ".js-note-target-close"
98
    $(document).off "click", ".js-note-discard"
99
    $(document).off "keydown", ".js-note-text"
100
    $(document).off 'click', '.js-comment-resolve-button'
101

102 103 104
    $('.note .js-task-list-container').taskList('disable')
    $(document).off 'tasklist:changed', '.note .js-task-list-container'

105 106 107 108
  keydownNoteText: (e) =>
    return if isMetaKey e

    $textarea = $(e.target)
109 110

    # Edit previous note when UP arrow is hit
D
Douwe Maan 已提交
111 112 113
    switch e.which
      when 38
        return unless $textarea.val() is ''
114

D
Douwe Maan 已提交
115 116 117 118
        myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
        if myLastNote.length
          myLastNoteEditBtn = myLastNote.find('.js-note-edit')
          myLastNoteEditBtn.trigger('click', [true, myLastNote])
119

D
Douwe Maan 已提交
120 121 122 123 124 125
      # Cancel creating diff note or editing any note when ESCAPE is hit
      when 27
        discussionNoteForm = $textarea.closest('.js-discussion-note-form')
        if discussionNoteForm.length
          if $textarea.val() isnt ''
            return unless confirm('Are you sure you want to cancel creating this comment?')
126

D
Douwe Maan 已提交
127 128
          @removeDiscussionNoteForm(discussionNoteForm)
          return
129

D
Douwe Maan 已提交
130 131 132 133 134 135 136 137
        editNote = $textarea.closest('.note')
        if editNote.length
          originalText = $textarea.closest('form').data('original-note')
          newText = $textarea.val()
          if originalText isnt newText
            return unless confirm('Are you sure you want to cancel editing this comment?')

          @removeNoteEditForm(editNote)
138

139

140 141 142
  isMetaKey = (e) ->
    (e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)

143
  initRefresh: ->
144 145
    clearInterval(Notes.interval)
    Notes.interval = setInterval =>
146
      @refresh()
147
    , @pollingInterval
148

149
  refresh: =>
150
    if not document.hidden and document.URL.indexOf(@noteable_url) is 0
151
      @getContent()
152 153

  getContent: ->
154 155 156 157
    return if @refreshing

    @refreshing = true

158 159
    $.ajax
      url: @notes_url
160
      data: "last_fetched_at=" + @last_fetched_at
161 162 163
      dataType: "json"
      success: (data) =>
        notes = data.notes
164
        @last_fetched_at = data.last_fetched_at
165
        @setPollingInterval(data.notes.length)
166
        $.each notes, (i, note) =>
167
          if note.discussion_html?
168 169 170
            @renderDiscussionNote(note)
          else
            @renderNote(note)
171 172
    .always () =>
      @refreshing = false
173

174
  ###
175
  Increase @pollingInterval up to 120 seconds on every function call,
176
  if `shouldReset` has a truthy value, 'null' or 'undefined' the variable
177
  will reset to @basePollingInterval.
178 179 180 181 182

  Note: this function is used to gradually increase the polling interval
  if there aren't new notes coming from the server
  ###
  setPollingInterval: (shouldReset = true) ->
183
    nthInterval = @basePollingInterval * Math.pow(2, @maxPollingSteps - 1)
184
    if shouldReset
185 186 187
      @pollingInterval = @basePollingInterval
    else if @pollingInterval < nthInterval
      @pollingInterval *= 2
188 189

    @initRefresh()
190 191 192 193 194 195 196

  ###
  Render note in main comments area.

  Note: for rendering inline notes use renderDiscussionNote
  ###
  renderNote: (note) ->
197
    unless note.valid
198
      if note.award
199
        new Flash('You have already awarded this emoji!', 'alert')
200 201
      return

202
    if note.award
203
      votesBlock = $('.js-awards-block').eq 0
F
Fatih Acet 已提交
204 205
      gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name
      gl.awardsHandler.scrollToAwards()
206

207 208
    # render note if it not present in loaded list
    # or skip if rendered
209
    else if @isNewNote(note)
210
      @note_ids.push(note.id)
211

212 213 214
      $notesList = $('ul.main-notes-list')

      $notesList
215 216
        .append(note.html)
        .syntaxHighlight()
217 218

      # Update datetime format on the recent note
219
      gl.utils.localTimeAgo($notesList.find("#note_#{note.id} .js-timeago"), false)
220

221
      @initTaskList()
222
      @updateNotesCount(1)
223

V
Valery Sizov 已提交
224

225 226 227 228 229 230
  ###
  Check if note does not exists on page
  ###
  isNewNote: (note) ->
    $.inArray(note.id, @note_ids) == -1

231 232
  isParallelView: ->
    @view == 'parallel'
233 234 235 236 237 238 239

  ###
  Render note in discussion area.

  Note: for rendering inline notes use renderDiscussionNote
  ###
  renderDiscussionNote: (note) ->
240 241
    return unless @isNewNote(note)

242
    @note_ids.push(note.id)
243
    form = $("#new-discussion-note-form-#{note.discussion_id}")
244 245
    if note.original_discussion_id? and form.length is 0
      form = $("#new-discussion-note-form-#{note.original_discussion_id}")
246
    row = form.closest("tr")
247 248
    note_html = $(note.html)
    note_html.syntaxHighlight()
249 250

    # is this the first note of discussion?
251
    discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']")
252 253
    if note.original_discussion_id? and discussionContainer.length is 0
      discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']")
254
    if discussionContainer.length is 0
255
      # insert the note and the reply button after the temp row
256
      row.after note.diff_discussion_html
257 258 259 260

      # remove the note (will be added again below)
      row.next().find(".note").remove()

261
      # Before that, the container didn't exist
262
      discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']")
263

264
      # Add note to 'Changes' page discussions
265
      discussionContainer.append note_html
266

267
      # Init discussion on 'Discussion' page if it is merge request page
268
      if $('body').attr('data-page').indexOf('projects:merge_request') is 0
269
        $('ul.main-notes-list')
270
          .append(note.discussion_html)
271
          .syntaxHighlight()
272 273
    else
      # append new note to all matching discussions
274
      discussionContainer.append note_html
275

276 277
    if $('resolve-btn, resolve-all').length and DiffNotesApp?
      $('resolve-btn, resolve-all').each ->
278 279
        DiffNotesApp.$compile $(this).get(0)

280
    gl.utils.localTimeAgo($('.js-timeago', note_html), false)
281

282
    @updateNotesCount(1)
283 284 285 286 287 288 289 290

  ###
  Called in response the main target form has been successfully submitted.

  Removes any errors.
  Resets text and preview.
  Resets buttons.
  ###
291
  resetMainTargetForm: (e) =>
292 293 294 295 296 297
    form = $(".js-main-target-form")

    # remove validation errors
    form.find(".js-errors").remove()

    # reset text and preview
V
Vinnie Okada 已提交
298
    form.find(".js-md-write-button").click()
299 300
    form.find(".js-note-text").val("").trigger "input"

301 302
    form.find(".js-note-text").data("autosave").reset()

303 304
    @updateTargetButtons(e)

305 306 307 308 309
  reenableTargetFormSubmitButton: ->
    form = $(".js-main-target-form")

    form.find(".js-note-text").trigger "input"

310 311 312 313 314 315 316 317 318
  ###
  Shows the main form and does some setup on it.

  Sets some hidden fields in the form.
  ###
  setupMainTargetNoteForm: ->
    # find the form
    form = $(".js-new-note-form")

P
Phil Hughes 已提交
319 320 321
    # Set a global clone of the form for later cloning
    @formClone = form.clone()

322 323 324 325 326 327 328
    # show the form
    @setupNoteForm(form)

    # fix classes
    form.removeClass "js-new-note-form"
    form.addClass "js-main-target-form"

P
Phil Hughes 已提交
329
    form.find("#note_line_code").remove()
330
    form.find("#note_position").remove()
D
Douwe Maan 已提交
331
    form.find("#note_type").remove()
332
    form.find('.js-comment-resolve-button').remove()
P
Phil Hughes 已提交
333

334 335
    @parentTimeline = form.parents('.timeline')

336 337 338 339 340 341 342 343 344
  ###
  General note form setup.

  deactivates the submit button when text is empty
  hides the preview button when text is empty
  setup GFM auto complete
  show the form
  ###
  setupNoteForm: (form) ->
P
Phil Hughes 已提交
345
    new GLForm form
346 347 348 349 350 351 352

    textarea = form.find(".js-note-text")

    new Autosave textarea, [
      "Note"
      form.find("#note_noteable_type").val()
      form.find("#note_noteable_id").val()
353 354 355 356
      form.find("#note_commit_id").val()
      form.find("#note_type").val()
      form.find("#note_line_code").val()
      form.find("#note_position").val()
357
    ]
358 359 360 361 362 363 364 365 366

  ###
  Called in response to the new note form being submitted

  Adds new note to list.
  ###
  addNote: (xhr, note, status) =>
    @renderNote(note)

367
  addNoteError: (xhr, note, status) =>
368
    new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
369

370 371 372 373 374 375
  ###
  Called in response to the new note form being submitted

  Adds new note to list.
  ###
  addDiscussionNote: (xhr, note, status) =>
376
    $form = $(xhr.target)
377 378
    @renderDiscussionNote(note)

379
    # cleanup after successfully creating a diff/discussion note
380 381 382 383 384 385 386 387
    @removeDiscussionNoteForm($form)

    if $form.attr('data-resolve-all')?
      namespace = $form.attr('data-namespace')
      discussionId = $form.attr('data-discussion-id')

      if ResolveService?
        ResolveService.resolveAll(namespace, discussionId, false)
388

389 390 391 392 393
  ###
  Called in response to the edit note form being submitted

  Updates the current note field.
  ###
394 395 396
  updateNote: (_xhr, note, _status) =>
    # Convert returned HTML to a jQuery object so we can modify it further
    $html = $(note.html)
397

398
    gl.utils.localTimeAgo($('.js-timeago', $html))
399

400 401 402 403
    $html.syntaxHighlight()
    $html.find('.js-task-list-container').taskList('enable')

    # Find the note's `li` element by ID and replace it with the updated HTML
404
    $note_li = $('.note-row-' + note.id)
405
    $note_li.replaceWith($html)
406 407 408 409 410

  ###
  Called in response to clicking the edit note link

  Replaces the note text with the note edit form
411
  Adds a data attribute to the form with the original content of the note for cancellations
412
  ###
413
  showEditForm: (e, scrollTo, myLastNote) ->
414 415
    e.preventDefault()
    note = $(this).closest(".note")
P
Phil Hughes 已提交
416
    note.addClass "is-editting"
P
Phil Hughes 已提交
417
    form = note.find(".note-edit-form")
P
Phil Hughes 已提交
418

P
Phil Hughes 已提交
419
    form.addClass('current-note-edit-form')
420 421 422

    # Show the attachment delete link
    note.find(".js-note-attachment-delete").show()
423

424 425 426
    done = ($noteText) ->
      # Neat little trick to put the cursor at the end
      noteTextVal = $noteText.val()
427 428
      # Store the original note text in a data attribute to retrieve if a user cancels edit.
      form.find('form.edit-note').data 'original-note', noteTextVal
429 430
      $noteText.val('').val(noteTextVal);

P
Phil Hughes 已提交
431
    new GLForm form
432
    if scrollTo? and myLastNote?
433
      # scroll to the bottom
434 435 436
      # so the open of the last element doesn't make a jump
      $('html, body').scrollTop($(document).height());
      $('html, body').animate({
437
        scrollTop: myLastNote.offset().top - 150
438
      }, 500, ->
439 440
        $noteText = form.find(".js-note-text")
        $noteText.focus()
441
        done($noteText)
442 443
      );
    else
444 445 446
      $noteText = form.find('.js-note-text')
      $noteText.focus()
      done($noteText)
447 448 449 450

  ###
  Called in response to clicking the edit note link

451
  Hides edit form and restores the original note text to the editor textarea.
452
  ###
453
  cancelEdit: (e) =>
454
    e.preventDefault()
D
Douwe Maan 已提交
455
    note = $(e.target).closest('.note')
456 457 458
    @removeNoteEditForm(note)

  removeNoteEditForm: (note) ->
459
    form = note.find(".current-note-edit-form")
P
Phil Hughes 已提交
460
    note.removeClass "is-editting"
461 462 463
    form.removeClass("current-note-edit-form")
    # Replace markdown textarea text with original note text.
    form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'))
464 465 466 467 468 469 470

  ###
  Called in response to deleting a note of any kind.

  Removes the actual note from view.
  Removes the whole discussion if the last note is being removed.
  ###
471
  removeNote: (e) =>
472
    noteId = $(e.currentTarget)
473 474
               .closest(".note")
               .attr("id")
475 476 477 478 479 480

    # A same note appears in the "Discussion" and in the "Changes" tab, we have
    # to remove all. Using $(".note[id='noteId']") ensure we get all the notes,
    # where $("#noteId") would return only one.
    $(".note[id='#{noteId}']").each (i, el) =>
      note  = $(el)
481
      notes = note.closest(".notes")
482

483
      if DiffNotesApp?
P
Phil Hughes 已提交
484
        ref = DiffNotesApp.$refs["#{noteId}"]?.$destroy(true)
485

486 487
      # check if this is the last note for this line
      if notes.find(".note").length is 1
488

489 490
        # "Discussions" tab
        notes.closest(".timeline-entry").remove()
491

492
        # "Changes" tab / commit view
493
        notes.closest("tr").remove()
494

495
      note.remove()
496

497 498
    # Decrement the "Discussions" counter only once
    @updateNotesCount(-1)
499

500 501 502 503 504 505 506 507 508
  ###
  Called in response to clicking the delete attachment link

  Removes the attachment wrapper view, including image tag if it exists
  Resets the note editing form
  ###
  removeAttachment: ->
    note = $(this).closest(".note")
    note.find(".note-attachment").remove()
509
    note.find(".note-body > .note-text").show()
510 511
    note.find(".note-header").show()
    note.find(".current-note-edit-form").remove()
512 513 514 515 516 517 518

  ###
  Called when clicking on the "reply" button for a diff line.

  Shows the note form below the notes.
  ###
  replyToDiscussionNote: (e) =>
P
Phil Hughes 已提交
519
    form = @formClone.clone()
520
    replyLink = $(e.target).closest(".js-discussion-reply-button")
521 522 523 524
    replyLink
      .closest('.discussion-reply-holder')
      .hide()
      .after form
525 526

    # show the form
P
Phil Hughes 已提交
527
    @setupDiscussionNoteForm(replyLink, form)
528 529 530 531 532 533 534 535 536 537 538

  ###
  Shows the diff or discussion form and does some setup on it.

  Sets some hidden fields in the form.

  Note: dataHolder must have the "discussionId", "lineCode", "noteableType"
  and "noteableId" data attributes set.
  ###
  setupDiscussionNoteForm: (dataHolder, form) =>
    # setup note target
539
    form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}"
540
    form.attr "data-line-code", dataHolder.data("lineCode")
D
Douwe Maan 已提交
541
    form.find("#note_type").val dataHolder.data("noteType")
542
    form.find("#line_type").val dataHolder.data("lineType")
543 544
    form.find("#note_commit_id").val dataHolder.data("commitId")
    form.find("#note_line_code").val dataHolder.data("lineCode")
545
    form.find("#note_position").val dataHolder.attr("data-position")
546 547
    form.find("#note_noteable_type").val dataHolder.data("noteableType")
    form.find("#note_noteable_id").val dataHolder.data("noteableId")
P
Phil Hughes 已提交
548 549 550 551 552
    form.find('.js-note-discard')
        .show()
        .removeClass('js-note-discard')
        .addClass('js-close-discussion-note-form')
        .text(form.find('.js-close-discussion-note-form').data('cancel-text'))
553 554
    @setupNoteForm form
    form.find(".js-note-text").focus()
555 556 557
    form
      .find('.js-comment-resolve-button')
      .attr('data-discussion-id', dataHolder.data('discussionId'))
P
Phil Hughes 已提交
558 559 560
    form
      .removeClass('js-main-target-form')
      .addClass("discussion-form js-discussion-note-form")
561 562 563 564 565 566 567 568 569

  ###
  Called when clicking on the "add a comment" button on the side of a diff line.

  Inserts a temporary row for the form below the line.
  Sets up the form and shows it.
  ###
  addDiffNote: (e) =>
    e.preventDefault()
P
Phil Hughes 已提交
570 571
    $link = $(e.currentTarget)
    row = $link.closest("tr")
572
    nextRow = row.next()
573 574 575 576 577 578 579
    hasNotes = nextRow.is(".notes_holder")
    addForm = false
    targetContent = ".notes_content"
    rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"

    # In parallel view, look inside the correct left/right pane
    if @isParallelView()
P
Phil Hughes 已提交
580
      lineType = $link.data("lineType")
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
      targetContent += "." + lineType
      rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\"></td><td class=\"notes_content parallel old\"></td><td class=\"notes_line\"></td><td class=\"notes_content parallel new\"></td></tr>"

    if hasNotes
      notesContent = nextRow.find(targetContent)
      if notesContent.length
        replyButton = notesContent.find(".js-discussion-reply-button:visible")
        if replyButton.length
          e.target = replyButton[0]
          $.proxy(@replyToDiscussionNote, replyButton[0], e).call()
        else
          # In parallel view, the form may not be present in one of the panes
          noteForm = notesContent.find(".js-discussion-note-form")
          if noteForm.length == 0
            addForm = true
596 597
    else
      # add a notes row and insert the form
598 599 600 601
      row.after rowCssToAdd
      addForm = true

    if addForm
P
Phil Hughes 已提交
602
      newForm = @formClone.clone()
603
      newForm.appendTo row.next().find(targetContent)
604 605

      # show the form
P
Phil Hughes 已提交
606
      @setupDiscussionNoteForm $link, newForm
607 608 609 610 611 612 613 614 615 616

  ###
  Called in response to "cancel" on a diff note form.

  Shows the reply button again.
  Removes the form and if necessary it's temporary row.
  ###
  removeDiscussionNoteForm: (form)->
    row = form.closest("tr")

P
Phil Hughes 已提交
617 618 619
    glForm = form.data 'gl-form'
    glForm.destroy()

620 621
    form.find(".js-note-text").data("autosave").reset()

622
    # show the reply button (will only work for replies)
623 624 625
    form
      .prev('.discussion-reply-holder')
      .show()
626 627 628 629 630 631 632
    if row.is(".js-temp-notes-holder")
      # remove temporary row for diff lines
      row.remove()
    else
      # only remove the form
      form.remove()

633 634 635 636 637
  cancelDiscussionForm: (e) =>
    e.preventDefault()
    form = $(e.target).closest(".js-discussion-note-form")
    @removeDiscussionNoteForm(form)

638 639 640 641 642 643 644 645 646 647 648 649
  ###
  Called after an attachment file has been selected.

  Updates the file name for the selected attachment.
  ###
  updateFormAttachment: ->
    form = $(this).closest("form")

    # get only the basename
    filename = $(this).val().replace(/^.*[\\\/]/, "")
    form.find(".js-attachment-filename").text filename

650 651 652 653 654 655
  ###
  Called when the tab visibility changes
  ###
  visibilityChange: =>
    @refresh()

656 657 658
  updateCloseButton: (e) =>
    textarea = $(e.target)
    form = textarea.parents('form')
659 660
    closebtn = form.find('.js-note-target-close')
    closebtn.text(closebtn.data('original-text'))
661

662 663 664
  updateTargetButtons: (e) =>
    textarea = $(e.target)
    form = textarea.parents('form')
665 666 667 668
    reopenbtn = form.find('.js-note-target-reopen')
    closebtn = form.find('.js-note-target-close')
    discardbtn = form.find('.js-note-discard')

669
    if textarea.val().trim().length > 0
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
      reopentext = reopenbtn.data('alternative-text')
      closetext = closebtn.data('alternative-text')

      if reopenbtn.text() isnt reopentext
        reopenbtn.text(reopentext)

      if closebtn.text() isnt closetext
        closebtn.text(closetext)

      if reopenbtn.is(':not(.btn-comment-and-reopen)')
        reopenbtn.addClass('btn-comment-and-reopen')

      if closebtn.is(':not(.btn-comment-and-close)')
        closebtn.addClass('btn-comment-and-close')

      if discardbtn.is(':hidden')
        discardbtn.show()
687
    else
688 689 690 691 692 693 694
      reopentext = reopenbtn.data('original-text')
      closetext = closebtn.data('original-text')

      if reopenbtn.text() isnt reopentext
        reopenbtn.text(reopentext)

      if closebtn.text() isnt closetext
695
        closebtn.text(closetext)
696

697
      if reopenbtn.is('.btn-comment-and-reopen')
698 699
        reopenbtn.removeClass('btn-comment-and-reopen')

700
      if closebtn.is('.btn-comment-and-close')
701 702 703 704
        closebtn.removeClass('btn-comment-and-close')

      if discardbtn.is(':visible')
        discardbtn.hide()
705 706 707 708 709

  initTaskList: ->
    @enableTaskList()
    $(document).on 'tasklist:changed', '.note .js-task-list-container', @updateTaskList

710 711 712
  enableTaskList: ->
    $('.note .js-task-list-container').taskList('enable')

713 714
  updateTaskList: ->
    $('form', this).submit()
715

716 717
  updateNotesCount: (updateCount) ->
    @notesCountBadge.text(parseInt(@notesCountBadge.text()) + updateCount)
718 719 720 721 722 723 724 725 726 727

  resolveDiscussion: ->
    $this = $(this)
    discussionId = $this.attr('data-discussion-id')

    $this
      .closest('form')
      .attr('data-discussion-id', discussionId)
      .attr('data-resolve-all', 'true')
      .attr('data-namespace', $this.attr('data-namespace'))