2015-09-11 14:41:01 +05:30
|
|
|
# MergeRequestTabs
|
|
|
|
#
|
|
|
|
# Handles persisting and restoring the current tab selection and lazily-loading
|
|
|
|
# content on the MergeRequests#show page.
|
|
|
|
#
|
2016-06-02 11:05:42 +05:30
|
|
|
#= require jquery.cookie
|
|
|
|
#
|
2015-09-11 14:41:01 +05:30
|
|
|
# ### Example Markup
|
|
|
|
#
|
2016-01-19 16:12:03 +05:30
|
|
|
# <ul class="nav-links merge-request-tabs">
|
2015-09-11 14:41:01 +05:30
|
|
|
# <li class="notes-tab active">
|
|
|
|
# <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
|
|
|
|
# Discussion
|
|
|
|
# </a>
|
|
|
|
# </li>
|
|
|
|
# <li class="commits-tab">
|
|
|
|
# <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
|
|
|
|
# Commits
|
|
|
|
# </a>
|
|
|
|
# </li>
|
|
|
|
# <li class="diffs-tab">
|
|
|
|
# <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
|
|
|
|
# Diffs
|
|
|
|
# </a>
|
|
|
|
# </li>
|
|
|
|
# </ul>
|
|
|
|
#
|
|
|
|
# <div class="tab-content">
|
|
|
|
# <div class="notes tab-pane active" id="notes">
|
|
|
|
# Notes Content
|
|
|
|
# </div>
|
|
|
|
# <div class="commits tab-pane" id="commits">
|
|
|
|
# Commits Content
|
|
|
|
# </div>
|
|
|
|
# <div class="diffs tab-pane" id="diffs">
|
|
|
|
# Diffs Content
|
|
|
|
# </div>
|
|
|
|
# </div>
|
|
|
|
#
|
|
|
|
# <div class="mr-loading-status">
|
|
|
|
# <div class="loading">
|
|
|
|
# Loading Animation
|
|
|
|
# </div>
|
|
|
|
# </div>
|
|
|
|
#
|
|
|
|
class @MergeRequestTabs
|
|
|
|
diffsLoaded: false
|
2015-12-23 02:04:40 +05:30
|
|
|
buildsLoaded: false
|
2015-09-11 14:41:01 +05:30
|
|
|
commitsLoaded: false
|
|
|
|
|
|
|
|
constructor: (@opts = {}) ->
|
|
|
|
# Store the `location` object, allowing for easier stubbing in tests
|
|
|
|
@_location = location
|
|
|
|
|
|
|
|
@bindEvents()
|
|
|
|
@activateTab(@opts.action)
|
|
|
|
|
|
|
|
bindEvents: ->
|
|
|
|
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
|
2015-12-23 02:04:40 +05:30
|
|
|
$(document).on 'click', '.js-show-tab', @showTab
|
|
|
|
|
|
|
|
showTab: (event) =>
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
@activateTab $(event.target).data('action')
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
tabShown: (event) =>
|
|
|
|
$target = $(event.target)
|
|
|
|
action = $target.data('action')
|
|
|
|
|
|
|
|
if action == 'commits'
|
|
|
|
@loadCommits($target.attr('href'))
|
2016-06-02 11:05:42 +05:30
|
|
|
@expandView()
|
2015-09-11 14:41:01 +05:30
|
|
|
else if action == 'diffs'
|
|
|
|
@loadDiff($target.attr('href'))
|
2016-06-02 11:05:42 +05:30
|
|
|
if bp? and bp.getBreakpointSize() isnt 'lg'
|
|
|
|
@shrinkView()
|
2016-06-16 23:09:34 +05:30
|
|
|
|
|
|
|
navBarHeight = $('.navbar-gitlab').outerHeight()
|
|
|
|
$.scrollTo(".merge-request-details .merge-request-tabs", offset: -navBarHeight)
|
2015-12-23 02:04:40 +05:30
|
|
|
else if action == 'builds'
|
|
|
|
@loadBuilds($target.attr('href'))
|
2016-06-02 11:05:42 +05:30
|
|
|
@expandView()
|
|
|
|
else
|
|
|
|
@expandView()
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
@setCurrentAction(action)
|
|
|
|
|
2015-10-24 18:46:33 +05:30
|
|
|
scrollToElement: (container) ->
|
|
|
|
if window.location.hash
|
2016-06-22 15:30:34 +05:30
|
|
|
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
|
2016-06-02 11:05:42 +05:30
|
|
|
|
|
|
|
$el = $("#{container} #{window.location.hash}:not(.match)")
|
|
|
|
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
2015-10-24 18:46:33 +05:30
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
# Activate a tab based on the current action
|
|
|
|
activateTab: (action) ->
|
|
|
|
action = 'notes' if action == 'show'
|
|
|
|
$(".merge-request-tabs a[data-action='#{action}']").tab('show')
|
|
|
|
|
|
|
|
# Replaces the current Merge Request-specific action in the URL with a new one
|
|
|
|
#
|
|
|
|
# If the action is "notes", the URL is reset to the standard
|
|
|
|
# `MergeRequests#show` route.
|
|
|
|
#
|
|
|
|
# Examples:
|
|
|
|
#
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1"
|
|
|
|
# setCurrentAction('diffs')
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
|
|
|
#
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
|
|
|
# setCurrentAction('notes')
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1"
|
|
|
|
#
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
|
|
|
# setCurrentAction('commits')
|
|
|
|
# location.pathname # => "/namespace/project/merge_requests/1/commits"
|
|
|
|
#
|
|
|
|
# Returns the new URL String
|
|
|
|
setCurrentAction: (action) =>
|
|
|
|
# Normalize action, just to be safe
|
|
|
|
action = 'notes' if action == 'show'
|
|
|
|
|
|
|
|
# Remove a trailing '/commits' or '/diffs'
|
2015-12-23 02:04:40 +05:30
|
|
|
new_state = @_location.pathname.replace(/\/(commits|diffs|builds)(\.html)?\/?$/, '')
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
# Append the new action if we're on a tab other than 'notes'
|
|
|
|
unless action == 'notes'
|
|
|
|
new_state += "/#{action}"
|
|
|
|
|
|
|
|
# Ensure parameters and hash come along for the ride
|
|
|
|
new_state += @_location.search + @_location.hash
|
|
|
|
|
|
|
|
# Replace the current history state with the new one without breaking
|
|
|
|
# Turbolinks' history.
|
|
|
|
#
|
|
|
|
# See https://github.com/rails/turbolinks/issues/363
|
|
|
|
history.replaceState {turbolinks: true, url: new_state}, document.title, new_state
|
|
|
|
|
|
|
|
new_state
|
|
|
|
|
|
|
|
loadCommits: (source) ->
|
|
|
|
return if @commitsLoaded
|
|
|
|
|
|
|
|
@_get
|
|
|
|
url: "#{source}.json"
|
|
|
|
success: (data) =>
|
2015-12-23 02:04:40 +05:30
|
|
|
document.querySelector("div#commits").innerHTML = data.html
|
2016-06-02 11:05:42 +05:30
|
|
|
gl.utils.localTimeAgo($('.js-timeago', 'div#commits'))
|
2015-09-11 14:41:01 +05:30
|
|
|
@commitsLoaded = true
|
2015-10-24 18:46:33 +05:30
|
|
|
@scrollToElement("#commits")
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
loadDiff: (source) ->
|
|
|
|
return if @diffsLoaded
|
|
|
|
@_get
|
|
|
|
url: "#{source}.json" + @_location.search
|
|
|
|
success: (data) =>
|
2016-06-02 11:05:42 +05:30
|
|
|
$('#diffs').html data.html
|
|
|
|
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
|
|
|
|
$('#diffs .js-syntax-highlight').syntaxHighlight()
|
2016-08-24 12:49:21 +05:30
|
|
|
$('#diffs .diff-file').singleFileDiff()
|
2016-04-02 18:10:28 +05:30
|
|
|
@expandViewContainer() if @diffViewType() is 'parallel'
|
2015-09-11 14:41:01 +05:30
|
|
|
@diffsLoaded = true
|
2015-10-24 18:46:33 +05:30
|
|
|
@scrollToElement("#diffs")
|
2016-06-02 11:05:42 +05:30
|
|
|
@highlighSelectedLine()
|
2016-08-24 12:49:21 +05:30
|
|
|
@filesCommentButton = $('.files .diff-file').filesCommentButton()
|
2016-06-02 11:05:42 +05:30
|
|
|
|
|
|
|
$(document)
|
|
|
|
.off 'click', '.diff-line-num a'
|
|
|
|
.on 'click', '.diff-line-num a', (e) =>
|
|
|
|
e.preventDefault()
|
|
|
|
window.location.hash = $(e.currentTarget).attr 'href'
|
|
|
|
@highlighSelectedLine()
|
|
|
|
@scrollToElement("#diffs")
|
|
|
|
|
|
|
|
highlighSelectedLine: ->
|
|
|
|
$('.hll').removeClass 'hll'
|
|
|
|
locationHash = window.location.hash
|
|
|
|
|
|
|
|
if locationHash isnt ''
|
|
|
|
hashClassString = ".#{locationHash.replace('#', '')}"
|
|
|
|
$diffLine = $("#{locationHash}:not(.match)", $('#diffs'))
|
|
|
|
|
|
|
|
if not $diffLine.is 'tr'
|
|
|
|
$diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}")
|
|
|
|
else
|
|
|
|
$diffLine = $diffLine.find('td')
|
|
|
|
|
|
|
|
if $diffLine.length
|
|
|
|
$diffLine.addClass 'hll'
|
|
|
|
diffLineTop = $diffLine.offset().top
|
|
|
|
navBarHeight = $('.navbar-gitlab').outerHeight()
|
2015-09-11 14:41:01 +05:30
|
|
|
|
2015-12-23 02:04:40 +05:30
|
|
|
loadBuilds: (source) ->
|
|
|
|
return if @buildsLoaded
|
|
|
|
|
|
|
|
@_get
|
|
|
|
url: "#{source}.json"
|
|
|
|
success: (data) =>
|
|
|
|
document.querySelector("div#builds").innerHTML = data.html
|
2016-06-02 11:05:42 +05:30
|
|
|
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'))
|
2015-12-23 02:04:40 +05:30
|
|
|
@buildsLoaded = true
|
|
|
|
@scrollToElement("#builds")
|
|
|
|
|
2015-10-24 18:46:33 +05:30
|
|
|
# Show or hide the loading spinner
|
|
|
|
#
|
|
|
|
# status - Boolean, true to show, false to hide
|
|
|
|
toggleLoading: (status) ->
|
|
|
|
$('.mr-loading-status .loading').toggle(status)
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
_get: (options) ->
|
|
|
|
defaults = {
|
2015-10-24 18:46:33 +05:30
|
|
|
beforeSend: => @toggleLoading(true)
|
|
|
|
complete: => @toggleLoading(false)
|
2015-09-11 14:41:01 +05:30
|
|
|
dataType: 'json'
|
|
|
|
type: 'GET'
|
|
|
|
}
|
|
|
|
|
|
|
|
options = $.extend({}, defaults, options)
|
|
|
|
|
|
|
|
$.ajax(options)
|
2016-04-02 18:10:28 +05:30
|
|
|
|
|
|
|
# Returns diff view type
|
|
|
|
diffViewType: ->
|
|
|
|
$('.inline-parallel-buttons a.active').data('view-type')
|
|
|
|
|
|
|
|
expandViewContainer: ->
|
|
|
|
$('.container-fluid').removeClass('container-limited')
|
2016-06-02 11:05:42 +05:30
|
|
|
|
|
|
|
shrinkView: ->
|
|
|
|
$gutterIcon = $('.js-sidebar-toggle i:visible')
|
|
|
|
|
|
|
|
# Wait until listeners are set
|
|
|
|
setTimeout( ->
|
|
|
|
# Only when sidebar is expanded
|
|
|
|
if $gutterIcon.is('.fa-angle-double-right')
|
|
|
|
$gutterIcon.closest('a').trigger('click', [true])
|
|
|
|
, 0)
|
|
|
|
|
|
|
|
# Expand the issuable sidebar unless the user explicitly collapsed it
|
|
|
|
expandView: ->
|
|
|
|
return if $.cookie('collapsed_gutter') == 'true'
|
|
|
|
|
|
|
|
$gutterIcon = $('.js-sidebar-toggle i:visible')
|
|
|
|
|
|
|
|
# Wait until listeners are set
|
|
|
|
setTimeout( ->
|
|
|
|
# Only when sidebar is collapsed
|
|
|
|
if $gutterIcon.is('.fa-angle-double-left')
|
|
|
|
$gutterIcon.closest('a').trigger('click', [true])
|
|
|
|
, 0)
|