summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/dispatcher.js10
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js36
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js28
-rw-r--r--app/assets/javascripts/lib/utils/poll.js8
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js2
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js4
-rw-r--r--app/assets/javascripts/main.js2
-rw-r--r--app/assets/javascripts/members.js12
-rw-r--r--app/assets/javascripts/pipelines/components/navigation_tabs.vue87
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue163
-rw-r--r--app/assets/javascripts/project.js246
-rw-r--r--app/assets/javascripts/project_avatar.js31
-rw-r--r--app/assets/javascripts/project_import.js17
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js2
15 files changed, 362 insertions, 293 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 44606989395..d716218d9a4 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -20,15 +20,15 @@ import groupsSelect from './groups_select';
import NamespaceSelect from './namespace_select';
/* global NewCommitForm */
/* global NewBranchForm */
-/* global Project */
-/* global ProjectAvatar */
+import Project from './project';
+import projectAvatar from './project_avatar';
/* global MergeRequest */
/* global Compare */
/* global CompareAutocomplete */
/* global ProjectFindFile */
/* global ProjectNew */
/* global ProjectShow */
-/* global ProjectImport */
+import projectImport from './project_import';
import Labels from './labels';
import LabelManager from './label_manager';
/* global Sidebar */
@@ -378,7 +378,7 @@ import Diff from './diff';
initSettingsPanels();
break;
case 'projects:imports:show':
- new ProjectImport();
+ projectImport();
break;
case 'projects:pipelines:new':
new NewBranchForm($('.js-new-pipeline-form'));
@@ -604,7 +604,7 @@ import Diff from './diff';
break;
case 'projects':
new Project();
- new ProjectAvatar();
+ projectAvatar();
switch (path[1]) {
case 'compare':
new CompareAutocomplete();
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 5c4926d6ac8..195e2ca6a78 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -310,6 +310,42 @@ export const setParamInURL = (param, value) => {
};
/**
+ * Given a string of query parameters creates an object.
+ *
+ * @example
+ * `scope=all&page=2` -> { scope: 'all', page: '2'}
+ * `scope=all` -> { scope: 'all' }
+ * ``-> {}
+ * @param {String} query
+ * @returns {Object}
+ */
+export const parseQueryStringIntoObject = (query = '') => {
+ if (query === '') return {};
+
+ return query
+ .split('&')
+ .reduce((acc, element) => {
+ const val = element.split('=');
+ Object.assign(acc, {
+ [val[0]]: decodeURIComponent(val[1]),
+ });
+ return acc;
+ }, {});
+};
+
+export const buildUrlWithCurrentLocation = param => (param ? `${window.location.pathname}${param}` : window.location.pathname);
+
+/**
+ * Based on the current location and the string parameters provided
+ * creates a new entry in the history without reloading the page.
+ *
+ * @param {String} param
+ */
+export const historyPushState = (newUrl) => {
+ window.history.pushState({}, document.title, newUrl);
+};
+
+/**
* Converts permission provided as strings to booleans.
*
* @param {String} string
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index 917a45eb06b..a02c79b787e 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -52,3 +52,31 @@ export function bytesToKiB(number) {
export function bytesToMiB(number) {
return number / (BYTES_IN_KIB * BYTES_IN_KIB);
}
+
+/**
+ * Utility function that calculates GiB of the given bytes.
+ * @param {Number} number
+ * @returns {Number}
+ */
+export function bytesToGiB(number) {
+ return number / (BYTES_IN_KIB * BYTES_IN_KIB * BYTES_IN_KIB);
+}
+
+/**
+ * Port of rails number_to_human_size
+ * Formats the bytes in number into a more understandable
+ * representation (e.g., giving it 1500 yields 1.5 KB).
+ *
+ * @param {Number} size
+ * @returns {String}
+ */
+export function numberToHumanSize(size) {
+ if (size < BYTES_IN_KIB) {
+ return `${size} bytes`;
+ } else if (size < BYTES_IN_KIB * BYTES_IN_KIB) {
+ return `${bytesToKiB(size).toFixed(2)} KiB`;
+ } else if (size < BYTES_IN_KIB * BYTES_IN_KIB * BYTES_IN_KIB) {
+ return `${bytesToMiB(size).toFixed(2)} MiB`;
+ }
+ return `${bytesToGiB(size).toFixed(2)} GiB`;
+}
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 1485e900945..65a8cf2c891 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -60,7 +60,6 @@ export default class Poll {
checkConditions(response) {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
-
if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
this.timeoutID = setTimeout(() => {
this.makeRequest();
@@ -102,7 +101,12 @@ export default class Poll {
/**
* Restarts polling after it has been stoped
*/
- restart() {
+ restart(options) {
+ // update data
+ if (options && options.data) {
+ this.options.data = options.data;
+ }
+
this.canPoll = true;
this.makeRequest();
}
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 28ab9dddc4c..a1475b92c7e 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -18,7 +18,7 @@ export const addDelimiter = text => (text ? text.toString().replace(/\B(?=(\d{3}
export const highCountTrim = count => (count > 99 ? '99+' : count);
/**
- * Converst first char to uppercase and replaces undercores with spaces
+ * Converts first char to uppercase and replaces undercores with spaces
* @param {String} string
* @requires {String}
*/
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 1aa63216baf..17236c91490 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -100,6 +100,10 @@ export function visitUrl(url, external = false) {
}
}
+export function redirectTo(url) {
+ return window.location.assign(url);
+}
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 127fddcf8d3..0035dd23011 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -69,8 +69,6 @@ import './notifications_dropdown';
import './notifications_form';
import './pager';
import './preview_markdown';
-import './project';
-import './project_avatar';
import './project_find_file';
import './project_import';
import './project_label_subscription';
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 6264750a4fb..52315e969d1 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -5,7 +5,6 @@ export default class Members {
}
addListeners() {
- $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow);
$('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this));
$('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this));
gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
@@ -33,17 +32,6 @@ export default class Members {
});
});
}
- // eslint-disable-next-line class-methods-use-this
- removeRow(e) {
- const $target = $(e.target);
-
- if ($target.hasClass('btn-remove')) {
- $target.closest('.member')
- .fadeOut(function fadeOutMemberRow() {
- $(this).remove();
- });
- }
- }
formSubmit(e, $el = null) {
const $this = e ? $(e.currentTarget) : $el;
diff --git a/app/assets/javascripts/pipelines/components/navigation_tabs.vue b/app/assets/javascripts/pipelines/components/navigation_tabs.vue
index 73f7e3a0cad..07befd23500 100644
--- a/app/assets/javascripts/pipelines/components/navigation_tabs.vue
+++ b/app/assets/javascripts/pipelines/components/navigation_tabs.vue
@@ -2,16 +2,8 @@
export default {
name: 'PipelineNavigationTabs',
props: {
- scope: {
- type: String,
- required: true,
- },
- count: {
- type: Object,
- required: true,
- },
- paths: {
- type: Object,
+ tabs: {
+ type: Array,
required: true,
},
},
@@ -23,68 +15,37 @@
// 0 is valid in a badge, but evaluates to false, we need to check for undefined
return count !== undefined;
},
+
+ onTabClick(tab) {
+ this.$emit('onChangeTab', tab.scope);
+ },
},
};
</script>
<template>
<ul class="nav-links scrolling-tabs">
<li
- class="js-pipelines-tab-all"
- :class="{ active: scope === 'all'}">
- <a :href="paths.allPath">
- All
- <span
- v-if="shouldRenderBadge(count.all)"
- class="badge js-totalbuilds-count">
- {{count.all}}
- </span>
- </a>
- </li>
- <li
- class="js-pipelines-tab-pending"
- :class="{ active: scope === 'pending'}">
- <a :href="paths.pendingPath">
- Pending
- <span
- v-if="shouldRenderBadge(count.pending)"
- class="badge">
- {{count.pending}}
- </span>
- </a>
- </li>
- <li
- class="js-pipelines-tab-running"
- :class="{ active: scope === 'running'}">
- <a :href="paths.runningPath">
- Running
- <span
- v-if="shouldRenderBadge(count.running)"
- class="badge">
- {{count.running}}
- </span>
- </a>
- </li>
- <li
- class="js-pipelines-tab-finished"
- :class="{ active: scope === 'finished'}">
- <a :href="paths.finishedPath">
- Finished
+ v-for="(tab, i) in tabs"
+ :key="i"
+ :class="{
+ active: tab.isActive,
+ }"
+ >
+ <a
+ role="button"
+ @click="onTabClick(tab)"
+ :class="`js-pipelines-tab-${tab.scope}`"
+ >
+ {{ tab.name }}
+
<span
- v-if="shouldRenderBadge(count.finished)"
- class="badge">
- {{count.finished}}
+ v-if="shouldRenderBadge(tab.count)"
+ class="badge"
+ >
+ {{tab.count}}
</span>
+
</a>
</li>
- <li
- class="js-pipelines-tab-branches"
- :class="{ active: scope === 'branches'}">
- <a :href="paths.branchesPath">Branches</a>
- </li>
- <li
- class="js-pipelines-tab-tags"
- :class="{ active: scope === 'tags'}">
- <a :href="paths.tagsPath">Tags</a>
- </li>
</ul>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index 3da60e88474..cf241c8ffed 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -1,10 +1,17 @@
<script>
+ import _ from 'underscore';
import PipelinesService from '../services/pipelines_service';
import pipelinesMixin from '../mixins/pipelines';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import navigationTabs from './navigation_tabs.vue';
import navigationControls from './nav_controls.vue';
- import { convertPermissionToBoolean, getParameterByName, setParamInURL } from '../../lib/utils/common_utils';
+ import {
+ convertPermissionToBoolean,
+ getParameterByName,
+ historyPushState,
+ buildUrlWithCurrentLocation,
+ parseQueryStringIntoObject,
+ } from '../../lib/utils/common_utils';
export default {
props: {
@@ -41,27 +48,18 @@
autoDevopsPath: pipelinesData.helpAutoDevopsPath,
newPipelinePath: pipelinesData.newPipelinePath,
canCreatePipeline: pipelinesData.canCreatePipeline,
- allPath: pipelinesData.allPath,
- pendingPath: pipelinesData.pendingPath,
- runningPath: pipelinesData.runningPath,
- finishedPath: pipelinesData.finishedPath,
- branchesPath: pipelinesData.branchesPath,
- tagsPath: pipelinesData.tagsPath,
hasCi: pipelinesData.hasCi,
ciLintPath: pipelinesData.ciLintPath,
state: this.store.state,
- apiScope: 'all',
- pagenum: 1,
+ scope: getParameterByName('scope') || 'all',
+ page: getParameterByName('page') || '1',
+ requestData: {},
};
},
computed: {
canCreatePipelineParsed() {
return convertPermissionToBoolean(this.canCreatePipeline);
},
- scope() {
- const scope = getParameterByName('scope');
- return scope === null ? 'all' : scope;
- },
/**
* The empty state should only be rendered when the request is made to fetch all pipelines
@@ -106,46 +104,112 @@
hasCiEnabled() {
return this.hasCi !== undefined;
},
- paths() {
- return {
- allPath: this.allPath,
- pendingPath: this.pendingPath,
- finishedPath: this.finishedPath,
- runningPath: this.runningPath,
- branchesPath: this.branchesPath,
- tagsPath: this.tagsPath,
- };
- },
- pageParameter() {
- return getParameterByName('page') || this.pagenum;
- },
- scopeParameter() {
- return getParameterByName('scope') || this.apiScope;
+
+ tabs() {
+ const { count } = this.state;
+ return [
+ {
+ name: 'All',
+ scope: 'all',
+ count: count.all,
+ isActive: this.scope === 'all',
+ },
+ {
+ name: 'Pending',
+ scope: 'pending',
+ count: count.pending,
+ isActive: this.scope === 'pending',
+ },
+ {
+ name: 'Running',
+ scope: 'running',
+ count: count.running,
+ isActive: this.scope === 'running',
+ },
+ {
+ name: 'Finished',
+ scope: 'finished',
+ count: count.finished,
+ isActive: this.scope === 'finished',
+ },
+ {
+ name: 'Branches',
+ scope: 'branches',
+ isActive: this.scope === 'branches',
+ },
+ {
+ name: 'Tags',
+ scope: 'tags',
+ isActive: this.scope === 'tags',
+ },
+ ];
},
},
created() {
this.service = new PipelinesService(this.endpoint);
- this.requestData = { page: this.pageParameter, scope: this.scopeParameter };
+ this.requestData = { page: this.page, scope: this.scope };
},
methods: {
+ successCallback(resp) {
+ return resp.json().then((response) => {
+ // Because we are polling & the user is interacting verify if the response received
+ // matches the last request made
+ if (_.isEqual(parseQueryStringIntoObject(resp.url.split('?')[1]), this.requestData)) {
+ this.store.storeCount(response.count);
+ this.store.storePagination(resp.headers);
+ this.setCommonData(response.pipelines);
+ }
+ });
+ },
/**
- * Will change the page number and update the URL.
- *
- * @param {Number} pageNumber desired page to go to.
+ * Handles URL and query parameter changes.
+ * When the user uses the pagination or the tabs,
+ * - update URL
+ * - Make API request to the server with new parameters
+ * - Update the polling function
+ * - Update the internal state
*/
- change(pageNumber) {
- const param = setParamInURL('page', pageNumber);
+ updateContent(parameters) {
+ // stop polling
+ this.poll.stop();
+
+ const queryString = Object.keys(parameters).map((parameter) => {
+ const value = parameters[parameter];
+ // update internal state for UI
+ this[parameter] = value;
+ return `${parameter}=${encodeURIComponent(value)}`;
+ }).join('&');
- gl.utils.visitUrl(param);
- return param;
+ // update polling parameters
+ this.requestData = parameters;
+
+ historyPushState(buildUrlWithCurrentLocation(`?${queryString}`));
+
+ this.isLoading = true;
+ // fetch new data
+ return this.service.getPipelines(this.requestData)
+ .then((response) => {
+ this.isLoading = false;
+ this.successCallback(response);
+
+ // restart polling
+ this.poll.restart({ data: this.requestData });
+ })
+ .catch(() => {
+ this.isLoading = false;
+ this.errorCallback();
+
+ // restart polling
+ this.poll.restart();
+ });
},
- successCallback(resp) {
- return resp.json().then((response) => {
- this.store.storeCount(response.count);
- this.store.storePagination(resp.headers);
- this.setCommonData(response.pipelines);
- });
+ onChangeTab(scope) {
+ this.updateContent({ scope, page: '1' });
+ },
+ onChangePage(page) {
+ /* URLS parameters are strings, we need to parse to match types */
+ this.updateContent({ scope: this.scope, page: Number(page).toString() });
},
},
};
@@ -154,7 +218,7 @@
<div class="pipelines-container">
<div
class="top-area scrolling-tabs-container inner-page-scroll-tabs"
- v-if="!isLoading && !shouldRenderEmptyState">
+ v-if="!shouldRenderEmptyState">
<div class="fade-left">
<i
class="fa fa-angle-left"
@@ -167,17 +231,17 @@
aria-hidden="true">
</i>
</div>
+
<navigation-tabs
- :scope="scope"
- :count="state.count"
- :paths="paths"
+ :tabs="tabs"
+ @onChangeTab="onChangeTab"
/>
<navigation-controls
:new-pipeline-path="newPipelinePath"
:has-ci-enabled="hasCiEnabled"
:help-page-path="helpPagePath"
- :ciLintPath="ciLintPath"
+ :ci-lint-path="ciLintPath"
:can-create-pipeline="canCreatePipelineParsed "
/>
</div>
@@ -188,6 +252,7 @@
label="Loading Pipelines"
size="3"
v-if="isLoading"
+ class="prepend-top-20"
/>
<empty-state
@@ -221,8 +286,8 @@
<table-pagination
v-if="shouldRenderPagination"
- :change="change"
- :pageInfo="state.pageInfo"
+ :change="onChangePage"
+ :page-info="state.pageInfo"
/>
</div>
</div>
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index fe6602259e2..ddb78aaeea1 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,139 +1,131 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
+/* eslint-disable func-names, space-before-function-paren, no-var, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
/* global ProjectSelect */
import Cookies from 'js-cookie';
-(function() {
- this.Project = (function() {
- function Project() {
- const $cloneOptions = $('ul.clone-options-dropdown');
- const $projectCloneField = $('#project_clone');
- const $cloneBtnText = $('a.clone-dropdown-btn span');
+export default class Project {
+ constructor() {
+ const $cloneOptions = $('ul.clone-options-dropdown');
+ const $projectCloneField = $('#project_clone');
+ const $cloneBtnText = $('a.clone-dropdown-btn span');
- const selectedCloneOption = $cloneBtnText.text().trim();
- if (selectedCloneOption.length > 0) {
- $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
- }
-
- $('a', $cloneOptions).on('click', (e) => {
- const $this = $(e.currentTarget);
- const url = $this.attr('href');
-
- e.preventDefault();
-
- $('.is-active', $cloneOptions).not($this).removeClass('is-active');
- $this.toggleClass('is-active');
- $projectCloneField.val(url);
- $cloneBtnText.text($this.text());
-
- return $('.clone').text(url);
- });
- // Ref switcher
- this.initRefSwitcher();
- $('.project-refs-select').on('change', function() {
- return $(this).parents('form').submit();
- });
- $('.hide-no-ssh-message').on('click', function(e) {
- Cookies.set('hide_no_ssh_message', 'false');
- $(this).parents('.no-ssh-key-message').remove();
- return e.preventDefault();
- });
- $('.hide-no-password-message').on('click', function(e) {
- Cookies.set('hide_no_password_message', 'false');
- $(this).parents('.no-password-message').remove();
- return e.preventDefault();
- });
- this.projectSelectDropdown();
+ const selectedCloneOption = $cloneBtnText.text().trim();
+ if (selectedCloneOption.length > 0) {
+ $(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
- Project.prototype.projectSelectDropdown = function() {
- new ProjectSelect();
- $('.project-item-select').on('click', (function(_this) {
- return function(e) {
- return _this.changeProject($(e.currentTarget).val());
- };
- })(this));
- };
-
- Project.prototype.changeProject = function(url) {
- return window.location = url;
- };
-
- Project.prototype.initRefSwitcher = function() {
- var refListItem = document.createElement('li');
- var refLink = document.createElement('a');
-
- refLink.href = '#';
-
- return $('.js-project-refs-dropdown').each(function() {
- var $dropdown, selected;
- $dropdown = $(this);
- selected = $dropdown.data('selected');
- return $dropdown.glDropdown({
- data: function(term, callback) {
- return $.ajax({
- url: $dropdown.data('refs-url'),
- data: {
- ref: $dropdown.data('ref'),
- search: term
- },
- dataType: "json"
- }).done(function(refs) {
- return callback(refs);
- });
- },
- selectable: true,
- filterable: true,
- filterRemote: true,
- filterByText: true,
- inputFieldName: $dropdown.data('input-field-name'),
- fieldName: $dropdown.data('field-name'),
- renderRow: function(ref) {
- var li = refListItem.cloneNode(false);
-
- if (ref.header != null) {
- li.className = 'dropdown-header';
- li.textContent = ref.header;
- } else {
- var link = refLink.cloneNode(false);
-
- if (ref === selected) {
- link.className = 'is-active';
- }
-
- link.textContent = ref;
- link.dataset.ref = ref;
-
- li.appendChild(link);
+ $('a', $cloneOptions).on('click', (e) => {
+ const $this = $(e.currentTarget);
+ const url = $this.attr('href');
+
+ e.preventDefault();
+
+ $('.is-active', $cloneOptions).not($this).removeClass('is-active');
+ $this.toggleClass('is-active');
+ $projectCloneField.val(url);
+ $cloneBtnText.text($this.text());
+
+ return $('.clone').text(url);
+ });
+ // Ref switcher
+ Project.initRefSwitcher();
+ $('.project-refs-select').on('change', function() {
+ return $(this).parents('form').submit();
+ });
+ $('.hide-no-ssh-message').on('click', function(e) {
+ Cookies.set('hide_no_ssh_message', 'false');
+ $(this).parents('.no-ssh-key-message').remove();
+ return e.preventDefault();
+ });
+ $('.hide-no-password-message').on('click', function(e) {
+ Cookies.set('hide_no_password_message', 'false');
+ $(this).parents('.no-password-message').remove();
+ return e.preventDefault();
+ });
+ Project.projectSelectDropdown();
+ }
+
+ static projectSelectDropdown () {
+ new ProjectSelect();
+ $('.project-item-select').on('click', e => Project.changeProject($(e.currentTarget).val()));
+ }
+
+ static changeProject(url) {
+ return window.location = url;
+ }
+
+ static initRefSwitcher() {
+ var refListItem = document.createElement('li');
+ var refLink = document.createElement('a');
+
+ refLink.href = '#';
+
+ return $('.js-project-refs-dropdown').each(function() {
+ var $dropdown, selected;
+ $dropdown = $(this);
+ selected = $dropdown.data('selected');
+ return $dropdown.glDropdown({
+ data: function(term, callback) {
+ return $.ajax({
+ url: $dropdown.data('refs-url'),
+ data: {
+ ref: $dropdown.data('ref'),
+ search: term,
+ },
+ dataType: 'json',
+ }).done(function(refs) {
+ return callback(refs);
+ });
+ },
+ selectable: true,
+ filterable: true,
+ filterRemote: true,
+ filterByText: true,
+ inputFieldName: $dropdown.data('input-field-name'),
+ fieldName: $dropdown.data('field-name'),
+ renderRow: function(ref) {
+ var li = refListItem.cloneNode(false);
+
+ if (ref.header != null) {
+ li.className = 'dropdown-header';
+ li.textContent = ref.header;
+ } else {
+ var link = refLink.cloneNode(false);
+
+ if (ref === selected) {
+ link.className = 'is-active';
}
- return li;
- },
- id: function(obj, $el) {
- return $el.attr('data-ref');
- },
- toggleLabel: function(obj, $el) {
- return $el.text().trim();
- },
- clicked: function(options) {
- const { e } = options;
- e.preventDefault();
- if ($('input[name="ref"]').length) {
- var $form = $dropdown.closest('form');
-
- var $visit = $dropdown.data('visit');
- var shouldVisit = $visit ? true : $visit;
- var action = $form.attr('action');
- var divider = action.indexOf('?') === -1 ? '?' : '&';
- if (shouldVisit) {
- gl.utils.visitUrl(`${action}${divider}${$form.serialize()}`);
- }
+ link.textContent = ref;
+ link.dataset.ref = ref;
+
+ li.appendChild(link);
+ }
+
+ return li;
+ },
+ id: function(obj, $el) {
+ return $el.attr('data-ref');
+ },
+ toggleLabel: function(obj, $el) {
+ return $el.text().trim();
+ },
+ clicked: function(options) {
+ const { e } = options;
+ e.preventDefault();
+ if ($('input[name="ref"]').length) {
+ var $form = $dropdown.closest('form');
+
+ var $visit = $dropdown.data('visit');
+ var shouldVisit = $visit ? true : $visit;
+ var action = $form.attr('action');
+ var divider = action.indexOf('?') === -1 ? '?' : '&';
+ if (shouldVisit) {
+ gl.utils.visitUrl(`${action}${divider}${$form.serialize()}`);
}
}
- });
+ },
});
- };
-
- return Project;
- })();
-}).call(window);
+ });
+ }
+}
diff --git a/app/assets/javascripts/project_avatar.js b/app/assets/javascripts/project_avatar.js
index aabdfbf65e2..56627aa155c 100644
--- a/app/assets/javascripts/project_avatar.js
+++ b/app/assets/javascripts/project_avatar.js
@@ -1,20 +1,13 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, no-useless-escape, max-len */
-(function() {
- this.ProjectAvatar = (function() {
- function ProjectAvatar() {
- $('.js-choose-project-avatar-button').bind('click', function() {
- var form;
- form = $(this).closest('form');
- return form.find('.js-project-avatar-input').click();
- });
- $('.js-project-avatar-input').bind('change', function() {
- var filename, form;
- form = $(this).closest('form');
- filename = $(this).val().replace(/^.*[\\\/]/, '');
- return form.find('.js-avatar-filename').text(filename);
- });
- }
+export default function projectAvatar() {
+ $('.js-choose-project-avatar-button').bind('click', function onClickAvatar() {
+ const form = $(this).closest('form');
+ return form.find('.js-project-avatar-input').click();
+ });
- return ProjectAvatar;
- })();
-}).call(window);
+ $('.js-project-avatar-input').bind('change', function onClickAvatarInput() {
+ const form = $(this).closest('form');
+ // eslint-disable-next-line no-useless-escape
+ const filename = $(this).val().replace(/^.*[\\\/]/, '');
+ return form.find('.js-avatar-filename').text(filename);
+ });
+}
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index 08334bf1ec5..d2d26d6f67e 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -1,13 +1,8 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, prefer-arrow-callback, max-len */
+import { visitUrl } from './lib/utils/url_utility';
-(function() {
- this.ProjectImport = (function() {
- function ProjectImport() {
- setTimeout(function() {
- return gl.utils.visitUrl(location.href);
- }, 5000);
- }
+export default function projectImport() {
+ setTimeout(() => {
+ visitUrl(location.href);
+ }, 5000);
+}
- return ProjectImport;
- })();
-}).call(window);
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index e917279947e..14d43e135fe 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -8,6 +8,7 @@
import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago';
import { errorMessages, errorMessagesTypes } from '../constants';
+ import { numberToHumanSize } from '../../lib/utils/number_utils';
export default {
props: {
@@ -41,6 +42,10 @@
return item.layers ? n__('%d layer', '%d layers', item.layers) : '';
},
+ formatSize(size) {
+ return numberToHumanSize(size);
+ },
+
handleDeleteRegistry(registry) {
this.deleteRegistry(registry)
.then(() => this.fetchList({ repo: this.repo }))
@@ -97,7 +102,7 @@
</span>
</td>
<td>
- {{item.size}}
+ {{formatSize(item.size)}}
<template v-if="item.size && item.layers">
&middot;
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index f82938aa8a9..1274db2c4c8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -61,7 +61,7 @@ export default {
return this.mr.hasCI;
},
shouldRenderRelatedLinks() {
- return this.mr.relatedLinks;
+ return !!this.mr.relatedLinks;
},
shouldRenderDeployments() {
return this.mr.deployments.length;