From 923bd2ecd28f633ef7e5a789ba3889a550cf45c7 Mon Sep 17 00:00:00 2001
From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Date: Wed, 25 Dec 2013 22:31:18 +0200
Subject: [PATCH] Write new Notes coffee class to handle comments

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
---
 app/assets/javascripts/notes.js.coffee | 397 +++++++++++++++++++++++++
 1 file changed, 397 insertions(+)
 create mode 100644 app/assets/javascripts/notes.js.coffee

diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
new file mode 100644
index 0000000000..58720cbc3d
--- /dev/null
+++ b/app/assets/javascripts/notes.js.coffee
@@ -0,0 +1,397 @@
+class Notes
+  constructor: (notes_url, note_ids) ->
+    @notes_url = notes_url
+    @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
+    @note_ids = note_ids
+    @initRefresh()
+    @setupMainTargetNoteForm()
+    @cleanBinding()
+    @addBinding()
+
+  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
+
+        # change note in UI after update
+    $(document).on "ajax:success", "form.edit_note", @updateNote
+
+    # Edit note link
+    $(document).on "click", ".js-note-edit", @showEditForm
+    $(document).on "click", ".note-edit-cancel", @cancelEdit
+
+    # remove a note (in general)
+    $(document).on "click", ".js-note-delete", @removeNote
+
+    # delete note attachment
+    $(document).on "click", ".js-note-attachment-delete", @removeAttachment
+
+    # Preview button
+    $(document).on "click", ".js-note-preview-button", @previewNote
+
+    # reset main target form after submit
+    $(document).on "ajax:complete", ".js-main-target-form", @resetMainTargetForm
+
+    # attachment button
+    $(document).on "click", ".js-choose-note-attachment-button", @chooseNoteAttachment
+
+    # 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
+
+  cleanBinding: ->
+    $(document).off "ajax:success", ".js-main-target-form"
+    $(document).off "ajax:success", ".js-discussion-note-form"
+    $(document).off "ajax:success", "form.edit_note"
+    $(document).off "click", ".js-note-edit"
+    $(document).off "click", ".note-edit-cancel"
+    $(document).off "click", ".js-note-delete"
+    $(document).off "click", ".js-note-attachment-delete"
+    $(document).off "click", ".js-note-preview-button"
+    $(document).off "ajax:complete", ".js-main-target-form"
+    $(document).off "click", ".js-choose-note-attachment-button"
+    $(document).off "click", ".js-discussion-reply-button"
+    $(document).off "click", ".js-add-diff-note-button"
+
+
+  initRefresh: ->
+    setInterval =>
+      @refresh()
+    , 15000
+
+  refresh: ->
+    @getContent()
+
+  getContent: ->
+    $.ajax
+      url: @notes_url
+      dataType: "json"
+      success: (data) =>
+        notes = data.notes
+        $.each notes, (i, note) =>
+          # render note if it not present in loaded list
+          # or skip if rendered
+          if $.inArray(note.id, @note_ids) == -1
+            @note_ids.push(note.id)
+            @renderNote(note)
+
+
+  ###
+  Render note in main comments area.
+
+  Note: for rendering inline notes use renderDiscussionNote
+  ###
+  renderNote: (note) ->
+    $('ul.main-notes-list').append(note.html)
+
+  ###
+  Render note in discussion area.
+
+  Note: for rendering inline notes use renderDiscussionNote
+  ###
+  renderDiscussionNote: (note) ->
+    form = $("form[rel='" + note.discussion_id + "']")
+    row = form.closest("tr")
+
+    # is this the first note of discussion?
+    if row.is(".js-temp-notes-holder")
+      # insert the note and the reply button after the temp row
+      row.after note.discussion_html
+
+      # remove the note (will be added again below)
+      row.next().find(".note").remove()
+
+    # append new note to all matching discussions
+    $(".notes[rel='" + note.discussion_id + "']").append note.html
+
+    # cleanup after successfully creating a diff/discussion note
+    @removeDiscussionNoteForm(form)
+
+  ###
+  Shows the note preview.
+
+  Lets the server render GFM into Html and displays it.
+
+  Note: uses the Toggler behavior to toggle preview/edit views/buttons
+  ###
+  previewNote: (e) ->
+    e.preventDefault()
+    form = $(this).closest("form")
+    preview = form.find(".js-note-preview")
+    noteText = form.find(".js-note-text").val()
+    if noteText.trim().length is 0
+      preview.text "Nothing to preview."
+    else
+      preview.text "Loading..."
+      $.post($(this).data("url"),
+        note: noteText
+      ).success (previewData) ->
+        preview.html previewData
+
+  ###
+  Called in response the main target form has been successfully submitted.
+
+  Removes any errors.
+  Resets text and preview.
+  Resets buttons.
+  ###
+  resetMainTargetForm: ->
+    form = $(".js-main-target-form")
+
+    # remove validation errors
+    form.find(".js-errors").remove()
+
+    # reset text and preview
+    previewContainer = form.find(".js-toggler-container.note_text_and_preview")
+    previewContainer.removeClass "on"  if previewContainer.is(".on")
+    form.find(".js-note-text").val("").trigger "input"
+
+  ###
+  Called when clicking the "Choose File" button.
+
+  Opens the file selection dialog.
+  ###
+  chooseNoteAttachment: ->
+    form = $(this).closest("form")
+    form.find(".js-note-attachment-input").click()
+
+  ###
+  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")
+
+    # insert the form after the button
+    form.clone().replaceAll $(".js-main-target-form")
+    form = form.prev("form")
+
+    # show the form
+    @setupNoteForm(form)
+
+    # fix classes
+    form.removeClass "js-new-note-form"
+    form.addClass "js-main-target-form"
+
+    # remove unnecessary fields and buttons
+    form.find("#note_line_code").remove()
+    form.find(".js-close-discussion-note-form").remove()
+
+  ###
+  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) ->
+    disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+    form.removeClass "js-new-note-form"
+
+    # setup preview buttons
+    form.find(".js-note-edit-button, .js-note-preview-button").tooltip placement: "left"
+    previewButton = form.find(".js-note-preview-button")
+    form.find(".js-note-text").on "input", ->
+      if $(this).val().trim() isnt ""
+        previewButton.removeClass("turn-off").addClass "turn-on"
+      else
+        previewButton.removeClass("turn-on").addClass "turn-off"
+
+
+    # remove notify commit author checkbox for non-commit notes
+    form.find(".js-notify-commit-author").remove()  if form.find("#note_noteable_type").val() isnt "Commit"
+    GitLab.GfmAutoComplete.setup()
+    form.show()
+
+
+  ###
+  Called in response to the new note form being submitted
+
+  Adds new note to list.
+  ###
+  addNote: (xhr, note, status) =>
+    @note_ids.push(note.id)
+    @renderNote(note)
+
+  ###
+  Called in response to the new note form being submitted
+
+  Adds new note to list.
+  ###
+  addDiscussionNote: (xhr, note, status) =>
+    @note_ids.push(note.id)
+    @renderDiscussionNote(note)
+
+  ###
+  Called in response to the edit note form being submitted
+
+  Updates the current note field.
+  ###
+  updateNote: (xhr, note, status) =>
+    note_li = $("#note_" + note.id)
+    note_li.replaceWith(note.html)
+
+  ###
+  Called in response to clicking the edit note link
+
+  Replaces the note text with the note edit form
+  Adds a hidden div with the original content of the note to fill the edit note form with
+  if the user cancels
+  ###
+  showEditForm: (e) ->
+    e.preventDefault()
+    note = $(this).closest(".note")
+    note.find(".note-text").hide()
+
+    # Show the attachment delete link
+    note.find(".js-note-attachment-delete").show()
+    GitLab.GfmAutoComplete.setup()
+    form = note.find(".note-edit-form")
+    form.show()
+    form.find("textarea").focus()
+
+  ###
+  Called in response to clicking the edit note link
+
+  Hides edit form
+  ###
+  cancelEdit: (e) ->
+    e.preventDefault()
+    note = $(this).closest(".note")
+    note.find(".note-text").show()
+    note.find(".js-note-attachment-delete").hide()
+    note.find(".note-edit-form").hide()
+
+  ###
+  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.
+  ###
+  removeNote: ->
+    note = $(this).closest(".note")
+    notes = note.closest(".notes")
+
+    # check if this is the last note for this line
+    if notes.find(".note").length is 1
+
+      # for discussions
+      notes.closest(".discussion").remove()
+
+      # for diff lines
+      notes.closest("tr").remove()
+
+    note.remove()
+
+  ###
+  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()
+    note.find(".note-text").show()
+    note.find(".js-note-attachment-delete").hide()
+    note.find(".note-edit-form").hide()
+
+  ###
+  Called when clicking on the "reply" button for a diff line.
+
+  Shows the note form below the notes.
+  ###
+  replyToDiscussionNote: (e) =>
+    form = $(".js-new-note-form")
+    replyLink = $(e.target)
+    replyLink.hide()
+
+    # insert the form after the button
+    form.clone().insertAfter replyLink
+
+    # show the form
+    @setupDiscussionNoteForm(replyLink, replyLink.next("form"))
+
+  ###
+  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
+    form.attr "rel", dataHolder.data("discussionId")
+    form.find("#note_commit_id").val dataHolder.data("commitId")
+    form.find("#note_line_code").val dataHolder.data("lineCode")
+    form.find("#note_noteable_type").val dataHolder.data("noteableType")
+    form.find("#note_noteable_id").val dataHolder.data("noteableId")
+    @setupNoteForm form
+    form.find(".js-note-text").focus()
+    form.addClass "js-discussion-note-form"
+
+  ###
+  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) =>
+    disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
+    form.removeClass "js-new-note-form"
+    form.removeClass "js-new-note-form"
+    GitLab.GfmAutoComplete.setup()
+    form.show()
+
+  ###
+  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()
+    link = e.target
+    form = $(".js-new-note-form")
+    row = $(link).closest("tr")
+    nextRow = row.next()
+
+    # does it already have notes?
+    if nextRow.is(".notes_holder")
+      $.proxy(@replyToDiscussionNote, nextRow.find(".js-discussion-reply-button")).call()
+    else
+      # add a notes row and insert the form
+      row.after "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
+      form.clone().appendTo row.next().find(".notes_content")
+
+      # show the form
+      @setupDiscussionNoteForm $(link), row.next().find("form")
+
+  ###
+  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")
+
+    # show the reply button (will only work for replies)
+    form.prev(".js-discussion-reply-button").show()
+    if row.is(".js-temp-notes-holder")
+      # remove temporary row for diff lines
+      row.remove()
+    else
+      # only remove the form
+      form.remove()
+
+@Notes = Notes
-- 
2.30.9