Skip to content
Snippets Groups Projects
Verified Commit 7dabca1b authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett
Browse files

Merge branch 'master' into droplab-templating-xss-fix

parents a695b855 c7680264
No related branches found
No related tags found
No related merge requests found
Showing
with 763 additions and 713 deletions
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
 
(() => {
class FilteredSearchDropdownManager {
constructor(baseEndpoint = '', page) {
this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page;
this.setupMapping();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('beforeunload', this.cleanupWrapper);
class FilteredSearchDropdownManager {
constructor(baseEndpoint = '', page) {
this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page;
this.setupMapping();
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('beforeunload', this.cleanupWrapper);
}
cleanup() {
if (this.droplab) {
this.droplab.destroy();
this.droplab = null;
}
 
cleanup() {
if (this.droplab) {
this.droplab.destroy();
this.droplab = null;
}
this.setupMapping();
 
this.setupMapping();
document.removeEventListener('beforeunload', this.cleanupWrapper);
}
 
document.removeEventListener('beforeunload', this.cleanupWrapper);
}
setupMapping() {
this.mapping = {
author: {
reference: null,
gl: 'DropdownUser',
element: this.container.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
gl: 'DropdownUser',
element: this.container.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
element: this.container.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
element: this.container.querySelector('#js-dropdown-label'),
},
hint: {
reference: null,
gl: 'DropdownHint',
element: this.container.querySelector('#js-dropdown-hint'),
},
};
}
 
setupMapping() {
this.mapping = {
author: {
reference: null,
gl: 'DropdownUser',
element: this.container.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
gl: 'DropdownUser',
element: this.container.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
element: this.container.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
element: this.container.querySelector('#js-dropdown-label'),
},
hint: {
reference: null,
gl: 'DropdownHint',
element: this.container.querySelector('#js-dropdown-hint'),
},
};
static addWordToInput(tokenName, tokenValue = '', clicked = false) {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
input.value = '';
if (clicked) {
gl.FilteredSearchVisualTokens.moveInputToTheRight();
}
}
 
static addWordToInput(tokenName, tokenValue = '', clicked = false) {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
updateCurrentDropdownOffset() {
this.updateDropdownOffset(this.currentDropdown);
}
 
gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
input.value = '';
updateDropdownOffset(key) {
// Always align dropdown with the input field
let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
 
if (clicked) {
gl.FilteredSearchVisualTokens.moveInputToTheRight();
}
}
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
 
updateCurrentDropdownOffset() {
this.updateDropdownOffset(this.currentDropdown);
// Make sure offset never exceeds the input container
const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
 
updateDropdownOffset(key) {
// Always align dropdown with the input field
let offset = this.filteredSearchInput.getBoundingClientRect().left - this.container.querySelector('.scroll-container').getBoundingClientRect().left;
this.mapping[key].reference.setOffset(offset);
}
 
const maxInputWidth = 240;
const currentDropdownWidth = this.mapping[key].element.clientWidth || maxInputWidth;
load(key, firstLoad = false) {
const mappingKey = this.mapping[key];
const glClass = mappingKey.gl;
const element = mappingKey.element;
let forceShowList = false;
 
// Make sure offset never exceeds the input container
const offsetMaxWidth = this.container.querySelector('.scroll-container').clientWidth - currentDropdownWidth;
if (offsetMaxWidth < offset) {
offset = offsetMaxWidth;
}
if (!mappingKey.reference) {
const dl = this.droplab;
const defaultArguments = [null, dl, element, this.filteredSearchInput, key];
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
 
this.mapping[key].reference.setOffset(offset);
// Passing glArguments to `new gl[glClass](<arguments>)`
mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))();
}
 
load(key, firstLoad = false) {
const mappingKey = this.mapping[key];
const glClass = mappingKey.gl;
const element = mappingKey.element;
let forceShowList = false;
if (!mappingKey.reference) {
const dl = this.droplab;
const defaultArguments = [null, dl, element, this.filteredSearchInput, key];
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
if (firstLoad) {
mappingKey.reference.init();
}
 
// Passing glArguments to `new gl[glClass](<arguments>)`
mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))();
}
if (this.currentDropdown === 'hint') {
// Force the dropdown to show if it was clicked from the hint dropdown
forceShowList = true;
}
 
if (firstLoad) {
mappingKey.reference.init();
}
this.updateDropdownOffset(key);
mappingKey.reference.render(firstLoad, forceShowList);
 
if (this.currentDropdown === 'hint') {
// Force the dropdown to show if it was clicked from the hint dropdown
forceShowList = true;
}
this.currentDropdown = key;
}
 
this.updateDropdownOffset(key);
mappingKey.reference.render(firstLoad, forceShowList);
loadDropdown(dropdownName = '') {
let firstLoad = false;
 
this.currentDropdown = key;
if (!this.droplab) {
firstLoad = true;
this.droplab = new DropLab();
}
 
loadDropdown(dropdownName = '') {
let firstLoad = false;
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
 
if (!this.droplab) {
firstLoad = true;
this.droplab = new DropLab();
}
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
const key = match && match.key ? match.key : 'hint';
this.load(key, firstLoad);
}
}
 
const match = this.filteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&& this.mapping[match.key];
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
setDropdown() {
const query = gl.DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } = this.tokenizer.processTokens(query);
 
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
const key = match && match.key ? match.key : 'hint';
this.load(key, firstLoad);
}
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
}
 
setDropdown() {
const query = gl.DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } = this.tokenizer.processTokens(query);
if (this.currentDropdown) {
this.updateCurrentDropdownOffset();
}
if (lastToken === searchToken && lastToken !== null) {
// Token is not fully initialized yet because it has no value
// Eg. token = 'label:'
const split = lastToken.split(':');
const dropdownName = split[0].split(' ').last();
this.loadDropdown(split.length > 1 ? dropdownName : '');
} else if (lastToken) {
// Token has been initialized into an object because it has a value
this.loadDropdown(lastToken.key);
} else {
this.loadDropdown('hint');
}
if (lastToken === searchToken && lastToken !== null) {
// Token is not fully initialized yet because it has no value
// Eg. token = 'label:'
const split = lastToken.split(':');
const dropdownName = split[0].split(' ').last();
this.loadDropdown(split.length > 1 ? dropdownName : '');
} else if (lastToken) {
// Token has been initialized into an object because it has a value
this.loadDropdown(lastToken.key);
} else {
this.loadDropdown('hint');
}
}
 
resetDropdowns() {
if (!this.currentDropdown) {
return;
}
resetDropdowns() {
if (!this.currentDropdown) {
return;
}
 
// Force current dropdown to hide
this.mapping[this.currentDropdown].reference.hideDropdown();
// Force current dropdown to hide
this.mapping[this.currentDropdown].reference.hideDropdown();
 
// Re-Load dropdown
this.setDropdown();
// Re-Load dropdown
this.setDropdown();
 
// Reset filters for current dropdown
this.mapping[this.currentDropdown].reference.resetFilters();
// Reset filters for current dropdown
this.mapping[this.currentDropdown].reference.resetFilters();
 
// Reposition dropdown so that it is aligned with cursor
this.updateDropdownOffset(this.currentDropdown);
}
// Reposition dropdown so that it is aligned with cursor
this.updateDropdownOffset(this.currentDropdown);
}
 
destroyDroplab() {
this.droplab.destroy();
}
destroyDroplab() {
this.droplab.destroy();
}
}
 
window.gl = window.gl || {};
gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
})();
window.gl = window.gl || {};
gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
(() => {
const tokenKeys = [{
key: 'author',
type: 'string',
param: 'username',
symbol: '@',
}, {
key: 'assignee',
type: 'string',
param: 'username',
symbol: '@',
}, {
key: 'milestone',
type: 'string',
param: 'title',
symbol: '%',
}, {
key: 'label',
type: 'array',
param: 'name[]',
symbol: '~',
}];
const tokenKeys = [{
key: 'author',
type: 'string',
param: 'username',
symbol: '@',
}, {
key: 'assignee',
type: 'string',
param: 'username',
symbol: '@',
}, {
key: 'milestone',
type: 'string',
param: 'title',
symbol: '%',
}, {
key: 'label',
type: 'array',
param: 'name[]',
symbol: '~',
}];
 
const alternativeTokenKeys = [{
key: 'label',
type: 'string',
param: 'name',
symbol: '~',
}];
const alternativeTokenKeys = [{
key: 'label',
type: 'string',
param: 'name',
symbol: '~',
}];
 
const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
const tokenKeysWithAlternative = tokenKeys.concat(alternativeTokenKeys);
 
const conditions = [{
url: 'assignee_id=0',
tokenKey: 'assignee',
value: 'none',
}, {
url: 'milestone_title=No+Milestone',
tokenKey: 'milestone',
value: 'none',
}, {
url: 'milestone_title=%23upcoming',
tokenKey: 'milestone',
value: 'upcoming',
}, {
url: 'milestone_title=%23started',
tokenKey: 'milestone',
value: 'started',
}, {
url: 'label_name[]=No+Label',
tokenKey: 'label',
value: 'none',
}];
const conditions = [{
url: 'assignee_id=0',
tokenKey: 'assignee',
value: 'none',
}, {
url: 'milestone_title=No+Milestone',
tokenKey: 'milestone',
value: 'none',
}, {
url: 'milestone_title=%23upcoming',
tokenKey: 'milestone',
value: 'upcoming',
}, {
url: 'milestone_title=%23started',
tokenKey: 'milestone',
value: 'started',
}, {
url: 'label_name[]=No+Label',
tokenKey: 'label',
value: 'none',
}];
 
class FilteredSearchTokenKeys {
static get() {
return tokenKeys;
}
class FilteredSearchTokenKeys {
static get() {
return tokenKeys;
}
 
static getAlternatives() {
return alternativeTokenKeys;
}
static getAlternatives() {
return alternativeTokenKeys;
}
 
static getConditions() {
return conditions;
}
static getConditions() {
return conditions;
}
 
static searchByKey(key) {
return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
}
static searchByKey(key) {
return tokenKeys.find(tokenKey => tokenKey.key === key) || null;
}
 
static searchBySymbol(symbol) {
return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
}
static searchBySymbol(symbol) {
return tokenKeys.find(tokenKey => tokenKey.symbol === symbol) || null;
}
 
static searchByKeyParam(keyParam) {
return tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key;
static searchByKeyParam(keyParam) {
return tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key;
 
if (tokenKey.param) {
tokenKeyParam += `_${tokenKey.param}`;
}
if (tokenKey.param) {
tokenKeyParam += `_${tokenKey.param}`;
}
 
return keyParam === tokenKeyParam;
}) || null;
}
return keyParam === tokenKeyParam;
}) || null;
}
 
static searchByConditionUrl(url) {
return conditions.find(condition => condition.url === url) || null;
}
static searchByConditionUrl(url) {
return conditions.find(condition => condition.url === url) || null;
}
 
static searchByConditionKeyValue(key, value) {
return conditions
.find(condition => condition.tokenKey === key && condition.value === value) || null;
}
static searchByConditionKeyValue(key, value) {
return conditions
.find(condition => condition.tokenKey === key && condition.value === value) || null;
}
}
 
window.gl = window.gl || {};
gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys;
})();
window.gl = window.gl || {};
gl.FilteredSearchTokenKeys = FilteredSearchTokenKeys;
require('./filtered_search_token_keys');
 
(() => {
class FilteredSearchTokenizer {
static processTokens(input) {
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
const tokenIndexes = []; // stores key+value for simple search
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
let tokenValue = v1 || v2 || v3;
let tokenSymbol = symbol;
let tokenIndex = '';
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
tokenSymbol = tokenValue;
tokenValue = '';
}
tokenIndex = `${key}:${tokenValue}`;
// Prevent adding duplicates
if (tokenIndexes.indexOf(tokenIndex) === -1) {
tokenIndexes.push(tokenIndex);
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
}
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
lastToken = input.lastIndexOf(lastString) ===
input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
class FilteredSearchTokenizer {
static processTokens(input) {
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
const tokens = [];
const tokenIndexes = []; // stores key+value for simple search
let lastToken = null;
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
let tokenValue = v1 || v2 || v3;
let tokenSymbol = symbol;
let tokenIndex = '';
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
tokenSymbol = tokenValue;
tokenValue = '';
}
 
return {
tokens,
lastToken,
searchToken,
};
tokenIndex = `${key}:${tokenValue}`;
// Prevent adding duplicates
if (tokenIndexes.indexOf(tokenIndex) === -1) {
tokenIndexes.push(tokenIndex);
tokens.push({
key,
value: tokenValue || '',
symbol: tokenSymbol || '',
});
}
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
lastToken = input.lastIndexOf(lastString) ===
input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
}
return {
tokens,
lastToken,
searchToken,
};
}
}
 
window.gl = window.gl || {};
gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
})();
window.gl = window.gl || {};
gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var, camelcase, one-var-declaration-per-line, quotes, object-shorthand, prefer-arrow-callback, comma-dangle, consistent-return, yoda, prefer-rest-params, prefer-spread, no-unused-vars, prefer-template, max-len */
/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, one-var,
camelcase, one-var-declaration-per-line, quotes, object-shorthand,
prefer-arrow-callback, comma-dangle, consistent-return, yoda,
prefer-rest-params, prefer-spread, no-unused-vars, prefer-template,
promise/catch-or-return */
/* global Api */
 
var slice = [].slice;
Loading
Loading
Loading
Loading
@@ -39,8 +39,9 @@
if ($issuableDueDate.length) {
calendar = new Pikaday({
field: $issuableDueDate.get(0),
theme: 'gitlab-theme',
theme: 'gitlab-theme animate-picker',
format: 'yyyy-mm-dd',
container: $issuableDueDate.parent().get(0),
onSelect: function(dateText) {
$issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
Loading
Loading
Loading
Loading
@@ -34,17 +34,6 @@ export default {
};
},
methods: {
fetch() {
this.poll.makeRequest();
Visibility.change(() => {
if (!Visibility.hidden()) {
this.poll.restart();
} else {
this.poll.stop();
}
});
},
renderResponse(res) {
const body = JSON.parse(res.body);
this.triggerAnimation(body);
Loading
Loading
@@ -71,7 +60,17 @@ export default {
},
},
created() {
this.fetch();
if (!Visibility.hidden()) {
this.poll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
this.poll.restart();
} else {
this.poll.stop();
}
});
},
};
</script>
Loading
Loading
Loading
Loading
@@ -332,6 +332,9 @@
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(label, $el, e, isMarking) {
var isIssueIndex, isMRIndex, page, boardsModel;
var fadeOutLoader = () => {
$loading.fadeOut();
};
 
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
Loading
Loading
@@ -396,9 +399,8 @@
$loading.fadeIn();
 
gl.issueBoards.BoardsStore.detail.issue.update($dropdown.attr('data-issue-update'))
.then(function () {
$loading.fadeOut();
});
.then(fadeOutLoader)
.catch(fadeOutLoader);
}
else {
if ($dropdown.hasClass('js-multiselect')) {
Loading
Loading
Loading
Loading
@@ -169,7 +169,10 @@
w.gl.utils.getSelectedFragment = () => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
const documentFragment = selection.getRangeAt(0).cloneContents();
const documentFragment = document.createDocumentFragment();
for (let i = 0; i < selection.rangeCount; i += 1) {
documentFragment.appendChild(selection.getRangeAt(i).cloneContents());
}
if (documentFragment.textContent.length === 0) return null;
 
return documentFragment;
Loading
Loading
@@ -368,9 +371,9 @@
});
};
 
w.gl.utils.setFavicon = (iconName) => {
if (faviconEl && iconName) {
faviconEl.setAttribute('href', `/assets/${iconName}.ico`);
w.gl.utils.setFavicon = (faviconPath) => {
if (faviconEl && faviconPath) {
faviconEl.setAttribute('href', faviconPath);
}
};
 
Loading
Loading
@@ -385,8 +388,8 @@
url: pageUrl,
dataType: 'json',
success: function(data) {
if (data && data.icon) {
gl.utils.setFavicon(`ci_favicons/${data.icon}`);
if (data && data.favicon) {
gl.utils.setFavicon(data.favicon);
} else {
gl.utils.resetFavicon();
}
Loading
Loading
/* eslint-disable import/prefer-default-export */
export const BYTES_IN_KIB = 1024;
/* eslint-disable import/prefer-default-export */
import { BYTES_IN_KIB } from './constants';
 
/**
* Function that allows a number with an X amount of decimals
Loading
Loading
@@ -32,3 +32,13 @@ export function formatRelevantDigits(number) {
}
return formattedNumber;
}
/**
* Utility function that calculates KiB of the given bytes.
*
* @param {Number} number bytes
* @return {Number} KiB
*/
export function bytesToKiB(number) {
return number / BYTES_IN_KIB;
}
Loading
Loading
@@ -165,6 +165,7 @@ import './syntax_highlight';
import './task_list';
import './todos';
import './tree';
import './usage_ping';
import './user';
import './user_tabs';
import './username_validator';
Loading
Loading
@@ -210,6 +211,14 @@ $(function () {
}
});
 
if (bootstrapBreakpoint === 'xs') {
const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar');
$rightSidebar
.removeClass('right-sidebar-expanded')
.addClass('right-sidebar-collapsed');
}
// prevent default action for disabled buttons
$('.btn').click(function(e) {
if ($(this).hasClass('disabled')) {
Loading
Loading
Loading
Loading
@@ -18,9 +18,10 @@
 
const calendar = new Pikaday({
field: $input.get(0),
theme: 'gitlab-theme',
theme: 'gitlab-theme animate-picker',
format: 'yyyy-mm-dd',
minDate: new Date(),
container: $input.parent().get(0),
onSelect(dateText) {
$input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
 
Loading
Loading
Loading
Loading
@@ -157,7 +157,7 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
$('.ci-widget-fetching').show();
return $.getJSON(this.opts.ci_status_url, (function(_this) {
return function(data) {
var message, status, title;
var message, status, title, callback;
_this.status = data.status;
_this.hasCi = data.has_ci;
_this.updateMergeButton(_this.status, _this.hasCi);
Loading
Loading
@@ -179,6 +179,12 @@ import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
_this.opts.ci_sha = data.sha;
_this.updateCommitUrls(data.sha);
}
if (data.status === "success" || data.status === "failed") {
callback = function() {
return _this.getMergeStatus();
};
return setTimeout(callback, 2000);
}
if (showNotification && data.status) {
status = _this.ciLabelForStatus(data.status);
if (status === "preparing") {
Loading
Loading
Loading
Loading
@@ -164,6 +164,9 @@
.then(function () {
$dropdown.trigger('loaded.gl.dropdown');
$loading.fadeOut();
})
.catch(() => {
$loading.fadeOut();
});
} else {
selected = $selectbox.find('input[type="hidden"]').val();
Loading
Loading
Loading
Loading
@@ -71,6 +71,8 @@ class PrometheusGraph {
this.transformData(metricsResponse);
this.createGraph();
}
}).catch(() => {
new Flash('An error occurred when trying to load metrics. Please try again.');
});
}
 
Loading
Loading
Loading
Loading
@@ -308,8 +308,10 @@ require('./task_list');
 
if (this.isNewNote(note)) {
this.note_ids.push(note.id);
$notesList = $('ul.main-notes-list');
$notesList.append(note.html).syntaxHighlight();
$notesList = window.$('ul.main-notes-list');
Notes.animateAppendNote(note.html, $notesList);
// Update datetime format on the recent note
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
this.collapseLongCommitList();
Loading
Loading
@@ -348,7 +350,7 @@ require('./task_list');
lineType = this.isParallelView() ? form.find('#line_type').val() : 'old';
diffAvatarContainer = row.prevAll('.line_holder').first().find('.js-avatar-container.' + lineType + '_line');
// is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
discussionContainer = window.$(`.notes[data-discussion-id="${note.discussion_id}"]`);
if (!discussionContainer.length) {
discussionContainer = form.closest('.discussion').find('.notes');
}
Loading
Loading
@@ -370,14 +372,13 @@ require('./task_list');
row.find(contentContainerClass + ' .content').append($notes.closest('.content').children());
}
}
// Init discussion on 'Discussion' page if it is merge request page
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0 || !note.diff_discussion_html) {
$('ul.main-notes-list').append($(note.discussion_html).renderGFM());
if (window.$('body').attr('data-page').indexOf('projects:merge_request') === 0 || !note.diff_discussion_html) {
Notes.animateAppendNote(note.discussion_html, window.$('ul.main-notes-list'));
}
} else {
// append new note to all matching discussions
discussionContainer.append($(note.html).renderGFM());
Notes.animateAppendNote(note.html, discussionContainer);
}
 
if (typeof gl.diffNotesCompileComponents !== 'undefined' && note.discussion_resolvable) {
Loading
Loading
@@ -1063,6 +1064,13 @@ require('./task_list');
return $form;
};
 
Notes.animateAppendNote = function(noteHTML, $notesList) {
const $note = window.$(noteHTML);
$note.addClass('fade-in').renderGFM();
$notesList.append($note);
};
return Notes;
})();
}).call(window);
Loading
Loading
@@ -65,6 +65,8 @@ export default {
makeRequest() {
this.isLoading = true;
 
$(this.$el).tooltip('destroy');
this.service.postAction(this.endpoint)
.then(() => {
this.isLoading = false;
Loading
Loading
@@ -88,9 +90,13 @@ export default {
:aria-label="title"
data-container="body"
data-placement="top"
:disabled="isLoading"
>
<i :class="iconClass" aria-hidden="true"></i>
<i class="fa fa-spinner fa-spin" aria-hidden="true" v-if="isLoading"></i>
:disabled="isLoading">
<i
:class="iconClass"
aria-hidden="true" />
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
v-if="isLoading" />
</button>
</template>
Loading
Loading
@@ -13,7 +13,7 @@ export default {
</script>
 
<template>
<div class="row empty-state">
<div class="row empty-state js-empty-state">
<div class="col-xs-12">
<div class="svg-content" v-html="pipelinesEmptyStateSVG" />
</div>
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment