debian-mirror-gitlab/app/assets/javascripts/merge_request_tabs.js

427 lines
14 KiB
JavaScript
Raw Normal View History

2016-09-29 09:46:39 +05:30
// MergeRequestTabs
//
// Handles persisting and restoring the current tab selection and lazily-loading
// content on the MergeRequests#show page.
//
2016-09-13 17:45:13 +05:30
/*= require jquery.cookie */
2016-09-29 09:46:39 +05:30
//
// ### Example Markup
//
// <ul class="nav-links merge-request-tabs">
// <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>
//
2016-09-13 17:45:13 +05:30
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.MergeRequestTabs = (function() {
MergeRequestTabs.prototype.diffsLoaded = false;
MergeRequestTabs.prototype.buildsLoaded = false;
MergeRequestTabs.prototype.pipelinesLoaded = false;
MergeRequestTabs.prototype.commitsLoaded = false;
2016-11-03 12:29:30 +05:30
MergeRequestTabs.prototype.fixedLayoutPref = null;
2016-09-13 17:45:13 +05:30
function MergeRequestTabs(opts) {
this.opts = opts != null ? opts : {};
this.opts.setUrl = this.opts.setUrl !== undefined ? this.opts.setUrl : true;
2016-11-03 12:29:30 +05:30
this.buildsLoaded = this.opts.buildsLoaded || false;
2016-09-13 17:45:13 +05:30
this.setCurrentAction = bind(this.setCurrentAction, this);
this.tabShown = bind(this.tabShown, this);
this.showTab = bind(this.showTab, this);
2016-09-29 09:46:39 +05:30
// Store the `location` object, allowing for easier stubbing in tests
2016-09-13 17:45:13 +05:30
this._location = location;
this.bindEvents();
this.activateTab(this.opts.action);
2016-11-03 12:29:30 +05:30
this.initAffix();
2016-09-13 17:45:13 +05:30
}
MergeRequestTabs.prototype.bindEvents = function() {
$(document).on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown);
2016-11-03 12:29:30 +05:30
$(document).on('click', '.js-show-tab', this.showTab);
};
MergeRequestTabs.prototype.unbindEvents = function() {
$(document).off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown);
$(document).off('click', '.js-show-tab', this.showTab);
2016-09-13 17:45:13 +05:30
};
MergeRequestTabs.prototype.showTab = function(event) {
event.preventDefault();
return this.activateTab($(event.target).data('action'));
};
MergeRequestTabs.prototype.tabShown = function(event) {
var $target, action, navBarHeight;
$target = $(event.target);
action = $target.data('action');
if (action === 'commits') {
this.loadCommits($target.attr('href'));
this.expandView();
2016-11-03 12:29:30 +05:30
this.resetViewContainer();
} else if (this.isDiffAction(action)) {
2016-09-13 17:45:13 +05:30
this.loadDiff($target.attr('href'));
if ((typeof bp !== "undefined" && bp !== null) && bp.getBreakpointSize() !== 'lg') {
this.shrinkView();
}
2016-11-03 12:29:30 +05:30
if (this.diffViewType() === 'parallel') {
this.expandViewContainer();
}
2016-09-13 17:45:13 +05:30
navBarHeight = $('.navbar-gitlab').outerHeight();
$.scrollTo(".merge-request-details .merge-request-tabs", {
offset: -navBarHeight
});
} else if (action === 'builds') {
this.loadBuilds($target.attr('href'));
this.expandView();
2016-11-03 12:29:30 +05:30
this.resetViewContainer();
2016-09-13 17:45:13 +05:30
} else if (action === 'pipelines') {
this.loadPipelines($target.attr('href'));
this.expandView();
2016-11-03 12:29:30 +05:30
this.resetViewContainer();
2016-09-13 17:45:13 +05:30
} else {
this.expandView();
2016-11-03 12:29:30 +05:30
this.resetViewContainer();
2016-09-13 17:45:13 +05:30
}
if (this.opts.setUrl) {
this.setCurrentAction(action);
}
};
MergeRequestTabs.prototype.scrollToElement = function(container) {
var $el, navBarHeight;
if (window.location.hash) {
2016-11-24 13:41:30 +05:30
navBarHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + document.querySelector('.js-tabs-affix').offsetHeight;
2016-09-13 17:45:13 +05:30
$el = $(container + " " + window.location.hash + ":not(.match)");
if ($el.length) {
return $.scrollTo(container + " " + window.location.hash + ":not(.match)", {
offset: -navBarHeight
});
}
}
};
2016-09-29 09:46:39 +05:30
// Activate a tab based on the current action
2016-09-13 17:45:13 +05:30
MergeRequestTabs.prototype.activateTab = function(action) {
if (action === 'show') {
action = 'notes';
}
2016-11-03 12:29:30 +05:30
$(".merge-request-tabs a[data-action='" + action + "']").tab('show').trigger('shown.bs.tab');
2016-09-13 17:45:13 +05:30
};
2016-09-29 09:46:39 +05:30
// 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
2016-09-13 17:45:13 +05:30
MergeRequestTabs.prototype.setCurrentAction = function(action) {
var new_state;
2016-09-29 09:46:39 +05:30
// Normalize action, just to be safe
2016-09-13 17:45:13 +05:30
if (action === 'show') {
action = 'notes';
}
this.currentAction = action;
2016-11-03 12:29:30 +05:30
// Remove a trailing '/commits' '/diffs' '/builds' '/pipelines' '/new' '/new/diffs'
new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines|new|new\/diffs)(\.html)?\/?$/, '');
2016-09-29 09:46:39 +05:30
// Append the new action if we're on a tab other than 'notes'
2016-09-13 17:45:13 +05:30
if (action !== 'notes') {
new_state += "/" + action;
}
2016-09-29 09:46:39 +05:30
// Ensure parameters and hash come along for the ride
2016-09-13 17:45:13 +05:30
new_state += this._location.search + this._location.hash;
history.replaceState({
turbolinks: true,
url: new_state
2016-09-29 09:46:39 +05:30
// Replace the current history state with the new one without breaking
// Turbolinks' history.
//
// See https://github.com/rails/turbolinks/issues/363
2016-09-13 17:45:13 +05:30
}, document.title, new_state);
return new_state;
};
MergeRequestTabs.prototype.loadCommits = function(source) {
if (this.commitsLoaded) {
return;
}
return this._get({
url: source + ".json",
success: (function(_this) {
return function(data) {
document.querySelector("div#commits").innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#commits'));
_this.commitsLoaded = true;
return _this.scrollToElement("#commits");
};
})(this)
});
};
MergeRequestTabs.prototype.loadDiff = function(source) {
if (this.diffsLoaded) {
return;
}
2016-11-03 12:29:30 +05:30
// We extract pathname for the current Changes tab anchor href
// some pages like MergeRequestsController#new has query parameters on that anchor
var url = gl.utils.parseUrl(source);
2016-09-13 17:45:13 +05:30
return this._get({
2016-11-03 12:29:30 +05:30
url: (url.pathname + ".json") + this._location.search,
2016-09-13 17:45:13 +05:30
success: (function(_this) {
return function(data) {
$('#diffs').html(data.html);
if (typeof DiffNotesApp !== 'undefined') {
DiffNotesApp.compileComponents();
}
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
$('#diffs .js-syntax-highlight').syntaxHighlight();
$('#diffs .diff-file').singleFileDiff();
2016-11-03 12:29:30 +05:30
if (_this.diffViewType() === 'parallel' && (_this.isDiffAction(_this.currentAction)) ) {
2016-09-13 17:45:13 +05:30
_this.expandViewContainer();
}
_this.diffsLoaded = true;
_this.scrollToElement("#diffs");
_this.highlighSelectedLine();
_this.filesCommentButton = $('.files .diff-file').filesCommentButton();
return $(document).off('click', '.diff-line-num a').on('click', '.diff-line-num a', function(e) {
e.preventDefault();
window.location.hash = $(e.currentTarget).attr('href');
_this.highlighSelectedLine();
return _this.scrollToElement("#diffs");
});
};
})(this)
});
};
MergeRequestTabs.prototype.highlighSelectedLine = function() {
var $diffLine, diffLineTop, hashClassString, locationHash, navBarHeight;
$('.hll').removeClass('hll');
locationHash = window.location.hash;
if (locationHash !== '') {
2016-09-29 09:46:39 +05:30
dataLineString = '[data-line-code="' + locationHash.replace('#', '') + '"]';
2016-09-13 17:45:13 +05:30
$diffLine = $(locationHash + ":not(.match)", $('#diffs'));
if (!$diffLine.is('tr')) {
2016-09-29 09:46:39 +05:30
$diffLine = $('#diffs').find("td" + locationHash + ", td" + dataLineString);
2016-09-13 17:45:13 +05:30
} else {
$diffLine = $diffLine.find('td');
}
if ($diffLine.length) {
$diffLine.addClass('hll');
diffLineTop = $diffLine.offset().top;
return navBarHeight = $('.navbar-gitlab').outerHeight();
}
}
};
MergeRequestTabs.prototype.loadBuilds = function(source) {
if (this.buildsLoaded) {
return;
}
return this._get({
url: source + ".json",
success: (function(_this) {
return function(data) {
document.querySelector("div#builds").innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'));
_this.buildsLoaded = true;
2016-11-03 12:29:30 +05:30
if (!this.pipelines) this.pipelines = new gl.Pipelines();
2016-09-13 17:45:13 +05:30
return _this.scrollToElement("#builds");
};
})(this)
});
};
MergeRequestTabs.prototype.loadPipelines = function(source) {
if (this.pipelinesLoaded) {
return;
}
return this._get({
url: source + ".json",
success: function(data) {
$('#pipelines').html(data.html);
gl.utils.localTimeAgo($('.js-timeago', '#pipelines'));
this.pipelinesLoaded = true;
return this.scrollToElement("#pipelines");
}.bind(this)
});
};
2016-09-29 09:46:39 +05:30
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide
2016-09-13 17:45:13 +05:30
MergeRequestTabs.prototype.toggleLoading = function(status) {
return $('.mr-loading-status .loading').toggle(status);
};
MergeRequestTabs.prototype._get = function(options) {
var defaults;
defaults = {
beforeSend: (function(_this) {
return function() {
return _this.toggleLoading(true);
};
})(this),
complete: (function(_this) {
return function() {
return _this.toggleLoading(false);
};
})(this),
dataType: 'json',
type: 'GET'
};
options = $.extend({}, defaults, options);
return $.ajax(options);
};
MergeRequestTabs.prototype.diffViewType = function() {
return $('.inline-parallel-buttons a.active').data('view-type');
2016-11-03 12:29:30 +05:30
};
MergeRequestTabs.prototype.isDiffAction = function(action) {
return action === 'diffs' || action === 'new/diffs'
2016-09-13 17:45:13 +05:30
};
MergeRequestTabs.prototype.expandViewContainer = function() {
2016-11-03 12:29:30 +05:30
var $wrapper = $('.content-wrapper .container-fluid');
if (this.fixedLayoutPref === null) {
this.fixedLayoutPref = $wrapper.hasClass('container-limited');
}
$wrapper.removeClass('container-limited');
};
MergeRequestTabs.prototype.resetViewContainer = function() {
if (this.fixedLayoutPref !== null) {
$('.content-wrapper .container-fluid')
.toggleClass('container-limited', this.fixedLayoutPref);
}
2016-09-13 17:45:13 +05:30
};
MergeRequestTabs.prototype.shrinkView = function() {
var $gutterIcon;
$gutterIcon = $('.js-sidebar-toggle i:visible');
return setTimeout(function() {
if ($gutterIcon.is('.fa-angle-double-right')) {
return $gutterIcon.closest('a').trigger('click', [true]);
}
2016-09-29 09:46:39 +05:30
// Wait until listeners are set
// Only when sidebar is expanded
2016-09-13 17:45:13 +05:30
}, 0);
};
MergeRequestTabs.prototype.expandView = function() {
var $gutterIcon;
if ($.cookie('collapsed_gutter') === 'true') {
return;
}
$gutterIcon = $('.js-sidebar-toggle i:visible');
return setTimeout(function() {
if ($gutterIcon.is('.fa-angle-double-left')) {
return $gutterIcon.closest('a').trigger('click', [true]);
}
}, 0);
2016-09-29 09:46:39 +05:30
// Expand the issuable sidebar unless the user explicitly collapsed it
// Wait until listeners are set
// Only when sidebar is collapsed
2016-09-13 17:45:13 +05:30
};
2016-11-03 12:29:30 +05:30
MergeRequestTabs.prototype.initAffix = function () {
var $tabs = $('.js-tabs-affix');
// Screen space on small screens is usually very sparse
// So we dont affix the tabs on these
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
var $diffTabs = $('#diff-notes-app'),
$fixedNav = $('.navbar-fixed-top'),
$layoutNav = $('.layout-nav');
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
top: function () {
var tabsTop = $diffTabs.offset().top - $tabs.height();
tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height());
return tabsTop;
}
}
}).on('affix.bs.affix', function () {
$diffTabs.css({
marginTop: $tabs.height()
});
}).on('affix-top.bs.affix', function () {
$diffTabs.css({
marginTop: ''
});
});
// Fix bug when reloading the page already scrolling
if ($tabs.hasClass('affix')) {
$tabs.trigger('affix.bs.affix');
}
};
2016-09-13 17:45:13 +05:30
return MergeRequestTabs;
})();
}).call(this);