Files
Jacob Schatz daf60957d9 Fix javascript error with deleting messages in issue or merge request
Elements searched for by jQuery no longer existed in the DOM.
Javascript was also unnecessarily complex, for using jQuery
Fixes #8109
2016-01-14 19:09:46 -05:00

544 lines
16 KiB
CoffeeScript

#= require autosave
#= require autosize
#= require dropzone
#= require dropzone_input
#= require gfm_auto_complete
#= require jquery.atwho
#= require task_list
class @Notes
@interval: null
constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url
@note_ids = note_ids
@last_fetched_at = last_fetched_at
@view = view
@noteable_url = document.URL
@initRefresh()
@setupMainTargetNoteForm()
@cleanBinding()
@addBinding()
@initTaskList()
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
# Reopen and close actions for Issue/MR combined with note form submit
$(document).on "click", ".js-comment-button", @updateCloseButton
$(document).on "keyup", ".js-note-text", @updateTargetButtons
# 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
$(document).on "ajax:complete", ".js-main-target-form", @reenableTargetFormSubmitButton
$(document).on "ajax:success", ".js-main-target-form", @resetMainTargetForm
# update the file name when an attachment is selected
$(document).on "change", ".js-note-attachment-input", @updateFormAttachment
# 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
# hide diff note form
$(document).on "click", ".js-close-discussion-note-form", @cancelDiscussionForm
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
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 "ajax:complete", ".js-main-target-form"
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
$(document).off "keyup", ".js-note-text"
$(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close"
$('.note .js-task-list-container').taskList('disable')
$(document).off 'tasklist:changed', '.note .js-task-list-container'
initRefresh: ->
clearInterval(Notes.interval)
Notes.interval = setInterval =>
@refresh()
, 15000
refresh: ->
unless document.hidden or (@noteable_url != document.URL)
@getContent()
getContent: ->
$.ajax
url: @notes_url
data: "last_fetched_at=" + @last_fetched_at
dataType: "json"
success: (data) =>
notes = data.notes
@last_fetched_at = data.last_fetched_at
$.each notes, (i, note) =>
@renderNote(note)
###
Render note in main comments area.
Note: for rendering inline notes use renderDiscussionNote
###
renderNote: (note) ->
unless note.valid
if note.award
flash = new Flash('You have already used this award emoji!', 'alert')
flash.pinTo('.header-content')
return
# render note if it not present in loaded list
# or skip if rendered
if @isNewNote(note) && !note.award
@note_ids.push(note.id)
$('ul.main-notes-list').
append(note.html).
syntaxHighlight()
@initTaskList()
if note.award
awards_handler.addAwardToEmojiBar(note.note)
awards_handler.scrollToAwards()
###
Check if note does not exists on page
###
isNewNote: (note) ->
$.inArray(note.id, @note_ids) == -1
isParallelView: ->
@view == 'parallel'
###
Render note in discussion area.
Note: for rendering inline notes use renderDiscussionNote
###
renderDiscussionNote: (note) ->
@note_ids.push(note.id)
form = $("form[rel='" + note.discussion_id + "']")
row = form.closest("tr")
note_html = $(note.html)
note_html.syntaxHighlight()
# 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()
# Add note to 'Changes' page discussions
$(".notes[rel='" + note.discussion_id + "']").append note_html
# Init discussion on 'Discussion' page if it is merge request page
if $('body').attr('data-page').indexOf('projects:merge_request') == 0
discussion_html = $(note.discussion_with_diff_html)
discussion_html.syntaxHighlight()
$('ul.main-notes-list').append(discussion_html)
else
# 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)
###
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
form.find(".js-md-write-button").click()
form.find(".js-note-text").val("").trigger "input"
form.find(".js-note-text").data("autosave").reset()
reenableTargetFormSubmitButton: ->
form = $(".js-main-target-form")
form.find(".js-note-text").trigger "input"
###
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"
form.find('.div-dropzone').remove()
# setup preview buttons
form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
previewButton = form.find(".js-md-preview-button")
textarea = form.find(".js-note-text")
textarea.on "input", ->
if $(this).val().trim() isnt ""
previewButton.removeClass("turn-off").addClass "turn-on"
else
previewButton.removeClass("turn-on").addClass "turn-off"
autosize(textarea)
new Autosave textarea, [
"Note"
form.find("#note_commit_id").val()
form.find("#note_line_code").val()
form.find("#note_noteable_type").val()
form.find("#note_noteable_id").val()
]
# 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()
new DropzoneInput(form)
form.show()
###
Called in response to the new note form being submitted
Adds new note to list.
###
addNote: (xhr, note, status) =>
@renderNote(note)
###
Called in response to the new note form being submitted
Adds new note to list.
###
addDiscussionNote: (xhr, note, status) =>
@renderDiscussionNote(note)
###
Called in response to the edit note form being submitted
Updates the current note field.
###
updateNote: (_xhr, note, _status) =>
# Convert returned HTML to a jQuery object so we can modify it further
$html = $(note.html)
$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
$note_li = $('.note-row-' + note.id)
$note_li.replaceWith($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-body > .note-text").hide()
note.find(".note-header").hide()
base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form)
form.addClass('current-note-edit-form gfm-form')
form.find('.div-dropzone').remove()
# Show the attachment delete link
note.find(".js-note-attachment-delete").show()
# Setup markdown form
GitLab.GfmAutoComplete.setup()
new DropzoneInput(form)
form.show()
textarea = form.find("textarea")
textarea.focus()
# HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
# The textarea has the correct value, Chrome just won't show it unless we
# modify it, so let's clear it and re-set it!
value = textarea.val()
textarea.val ""
textarea.val value
disableButtonIfEmptyField textarea, form.find(".js-comment-button")
###
Called in response to clicking the edit note link
Hides edit form
###
cancelEdit: (e) ->
e.preventDefault()
note = $(this).closest(".note")
note.find(".note-body > .note-text").show()
note.find(".note-header").show()
note.find(".current-note-edit-form").remove()
###
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")
note_id = note.attr('id')
$('.note[id="' + note_id + '"]').each ->
note = $(this)
notes = note.closest(".notes")
count = notes.closest(".issuable-details").find(".notes-tab .badge")
# 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()
# update notes count
oldNum = parseInt(count.text())
count.text(oldNum - 1)
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-body > .note-text").show()
note.find(".note-header").show()
note.find(".current-note-edit-form").remove()
###
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).closest(".js-discussion-reply-button")
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("#line_type").val dataHolder.data("lineType")
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"
###
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.currentTarget
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
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()
lineType = $(link).data("lineType")
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
else
# add a notes row and insert the form
row.after rowCssToAdd
addForm = true
if addForm
newForm = form.clone()
newForm.appendTo row.next().find(targetContent)
# show the form
@setupDiscussionNoteForm $(link), newForm
###
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")
form.find(".js-note-text").data("autosave").reset()
# 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()
cancelDiscussionForm: (e) =>
e.preventDefault()
form = $(".js-new-note-form")
form = $(e.target).closest(".js-discussion-note-form")
@removeDiscussionNoteForm(form)
###
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
###
Called when the tab visibility changes
###
visibilityChange: =>
@refresh()
updateCloseButton: (e) =>
textarea = $(e.target)
form = textarea.parents('form')
form.find('.js-note-target-close').text('Close')
updateTargetButtons: (e) =>
textarea = $(e.target)
form = textarea.parents('form')
if textarea.val().trim().length > 0
form.find('.js-note-target-reopen').text('Comment & reopen')
form.find('.js-note-target-close').text('Comment & close')
form.find('.js-note-target-reopen').addClass('btn-comment-and-reopen')
form.find('.js-note-target-close').addClass('btn-comment-and-close')
else
form.find('.js-note-target-reopen').text('Reopen')
form.find('.js-note-target-close').text('Close')
form.find('.js-note-target-reopen').removeClass('btn-comment-and-reopen')
form.find('.js-note-target-close').removeClass('btn-comment-and-close')
initTaskList: ->
@enableTaskList()
$(document).on 'tasklist:changed', '.note .js-task-list-container', @updateTaskList
enableTaskList: ->
$('.note .js-task-list-container').taskList('enable')
updateTaskList: ->
$('form', this).submit()