Commit 2158bedd authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into 17489-hide-code-from-guests

parents 719e30c5 12e77890

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

......@@ -27,6 +27,7 @@
},
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+$"],
"import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error"
}
......
......@@ -18,6 +18,7 @@ eslint-report.html
.sass-cache/
/.secret
/.vagrant
/.yarn-cache
/.byebug_history
/Vagrantfile
/backups/*
......@@ -48,6 +49,7 @@ eslint-report.html
/public/uploads/
/shared/artifacts/
/spec/javascripts/fixtures/blob/pdf/
/spec/javascripts/fixtures/blob/balsamiq/
/rails_best_practices_output.html
/tags
/tmp/*
......
This diff is collapsed.
......@@ -40,6 +40,7 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info
<details>
<summary>Expand for output related to GitLab environment info</summary>
<pre>
(For installations with omnibus-gitlab package run and paste the output of:
......@@ -54,6 +55,7 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab application Check
<details>
<summary>Expand for output related to the GitLab application check</summary>
<pre>
(For installations with omnibus-gitlab package run and paste the output of:
......
......@@ -494,7 +494,13 @@ Style/TrailingBlankLines:
# This cop checks for trailing comma in array and hash literals.
Style/TrailingCommaInLiteral:
Enabled: false
Enabled: true
EnforcedStyleForMultiline: no_comma
# This cop checks for trailing comma in argument lists.
Style/TrailingCommaInArguments:
Enabled: true
EnforcedStyleForMultiline: no_comma
# Checks for %W when interpolation is not needed.
Style/UnneededCapitalW:
......@@ -963,6 +969,12 @@ RSpec/DescribeSymbol:
RSpec/DescribedClass:
Enabled: true
# Checks if an example group does not include any tests.
RSpec/EmptyExampleGroup:
Enabled: true
CustomIncludeMethods:
- run_permission_checks
# Checks for long example.
RSpec/ExampleLength:
Enabled: false
......@@ -981,6 +993,10 @@ RSpec/ExampleWording:
RSpec/ExpectActual:
Enabled: true
# Checks for opportunities to use `expect { … }.to output`.
RSpec/ExpectOutput:
Enabled: true
# Checks the file and folder naming of the spec file.
RSpec/FilePath:
Enabled: true
......
......@@ -10,11 +10,6 @@
RSpec/BeforeAfterAll:
Enabled: false
# Offense count: 15
# Configuration parameters: CustomIncludeMethods.
RSpec/EmptyExampleGroup:
Enabled: false
# Offense count: 233
RSpec/EmptyLineAfterFinalLet:
Enabled: false
......@@ -23,10 +18,6 @@ RSpec/EmptyLineAfterFinalLet:
RSpec/EmptyLineAfterSubject:
Enabled: false
# Offense count: 3
RSpec/ExpectOutput:
Enabled: false
# Offense count: 72
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: implicit, each, example
......@@ -369,13 +360,6 @@ Style/SymbolProc:
Style/TernaryParentheses:
Enabled: false
# Offense count: 53
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInArguments:
Enabled: false
# Offense count: 18
# Cop supports --auto-correct.
# Configuration parameters: AllowNamedUnderscoreVariables.
......
This diff is collapsed.
......@@ -145,12 +145,12 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs
gem 'sidekiq', '~> 5.0'
gem 'sidekiq-cron', '~> 0.4.4'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4'
# Cron Parser
gem 'rufus-scheduler', '~> 3.1.10'
gem 'rufus-scheduler', '~> 3.4'
# HTTP requests
gem 'httparty', '~> 0.13.3'
......@@ -367,6 +367,6 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client
gem 'gitaly', '~> 0.5.0'
gem 'gitaly', '~> 0.7.0'
gem 'toml-rb', '~> 0.3.15', require: false
......@@ -181,6 +181,8 @@ GEM
equalizer (0.0.11)
erubis (2.7.0)
escape_utils (1.1.1)
et-orbi (1.0.3)
tzinfo
eventmachine (1.0.8)
excon (0.55.0)
execjs (2.6.0)
......@@ -263,7 +265,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
gitaly (0.5.0)
gitaly (0.7.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
......@@ -434,7 +436,7 @@ GEM
rugged (~> 0.24)
little-plugger (1.1.4)
locale (2.1.2)
logging (2.1.0)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
loofah (2.0.3)
......@@ -697,7 +699,8 @@ GEM
rubyntlm (0.5.2)
rubypants (0.2.0)
rubyzip (1.2.1)
rufus-scheduler (3.1.10)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.25.1.1)
safe_yaml (1.0.4)
sanitize (2.1.0)
......@@ -734,9 +737,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
redis (~> 3.3, >= 3.3.3)
sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24)
sidekiq-cron (0.6.0)
rufus-scheduler (>= 3.3.0)
sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4)
......@@ -922,7 +924,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.5.0)
gitaly (~> 0.7.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1)
......@@ -1013,7 +1015,7 @@ DEPENDENCIES
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
ruby_parser (~> 3.8.4)
rufus-scheduler (~> 3.1.10)
rufus-scheduler (~> 3.4)
rugged (~> 0.25.1.1)
sanitize (~> 2.0)
sass-rails (~> 5.0.6)
......@@ -1025,7 +1027,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 5.0)
sidekiq-cron (~> 0.4.4)
sidekiq-cron (~> 0.6.0)
sidekiq-limit_fetch (~> 3.4)
simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1)
......
9.2.0-pre
9.3.0-pre
/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */
var Api = {
groupsPath: "/api/:version/groups.json",
groupPath: "/api/:version/groups/:id.json",
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
labelsPath: "/:namespace_path/:project_path/labels",
licensePath: "/api/:version/templates/licenses/:key",
gitignorePath: "/api/:version/templates/gitignores/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
dockerfilePath: "/api/:version/templates/dockerfiles/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath)
.replace(':id', group_id);
import $ from 'jquery';
const Api = {
groupsPath: '/api/:version/groups.json',
groupPath: '/api/:version/groups/:id.json',
namespacesPath: '/api/:version/namespaces.json',
groupProjectsPath: '/api/:version/groups/:id/projects.json',
projectsPath: '/api/:version/projects.json?simple=true',
labelsPath: '/:namespace_path/:project_path/labels',
licensePath: '/api/:version/templates/licenses/:key',
gitignorePath: '/api/:version/templates/gitignores/:key',
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
dockerfilePath: '/api/:version/templates/dockerfiles/:key',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
usersPath: '/api/:version/users.json',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath)
.replace(':id', groupId);
return $.ajax({
url: url,
dataType: "json"
}).done(function(group) {
return callback(group);
});
url,
dataType: 'json',
})
.done(group => callback(group));
},
// Return groups list. Filtered by query
groups: function(query, options, callback) {
var url = Api.buildUrl(Api.groupsPath);
groups(query, options, callback) {
const url = Api.buildUrl(Api.groupsPath);
return $.ajax({
url: url,
data: $.extend({
url,
data: Object.assign({
search: query,
per_page: 20
per_page: 20,
}, options),
dataType: "json"
}).done(function(groups) {
return callback(groups);
});
dataType: 'json',
})
.done(groups => callback(groups));
},
// Return namespaces list. Filtered by query
namespaces: function(query, callback) {
var url = Api.buildUrl(Api.namespacesPath);
namespaces(query, callback) {
const url = Api.buildUrl(Api.namespacesPath);
return $.ajax({
url: url,
url,
data: {
search: query,
per_page: 20
per_page: 20,
},
dataType: "json"
}).done(function(namespaces) {
return callback(namespaces);
});
dataType: 'json',
}).done(namespaces => callback(namespaces));
},
// Return projects list. Filtered by query
projects: function(query, options, callback) {
var url = Api.buildUrl(Api.projectsPath);
projects(query, options, callback) {
const url = Api.buildUrl(Api.projectsPath);
return $.ajax({
url: url,
data: $.extend({
url,
data: Object.assign({
search: query,
per_page: 20,
membership: true
membership: true,
}, options),
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
dataType: 'json',
})
.done(projects => callback(projects));
},
newLabel: function(namespace_path, project_path, data, callback) {
var url = Api.buildUrl(Api.labelsPath)
.replace(':namespace_path', namespace_path)
.replace(':project_path', project_path);
newLabel(namespacePath, projectPath, data, callback) {
const url = Api.buildUrl(Api.labelsPath)
.replace(':namespace_path', namespacePath)
.replace(':project_path', projectPath);
return $.ajax({
url: url,
type: "POST",
data: { 'label': data },
dataType: "json"
}).done(function(label) {
return callback(label);
}).error(function(message) {
return callback(message.responseJSON);
});
url,
type: 'POST',
data: { label: data },
dataType: 'json',
})
.done(label => callback(label))
.error(message => callback(message.responseJSON));
},
// Return group projects list. Filtered by query
groupProjects: function(group_id, query, callback) {
var url = Api.buildUrl(Api.groupProjectsPath)
.replace(':id', group_id);
groupProjects(groupId, query, callback) {
const url = Api.buildUrl(Api.groupProjectsPath)
.replace(':id', groupId);
return $.ajax({
url: url,
url,
data: {
search: query,
per_page: 20
per_page: 20,
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
dataType: 'json',
})
.done(projects => callback(projects));
},
// Return text for a specific license
licenseText: function(key, data, callback) {
var url = Api.buildUrl(Api.licensePath)
licenseText(key, data, callback) {
const url = Api.buildUrl(Api.licensePath)
.replace(':key', key);
return $.ajax({
url: url,
data: data
}).done(function(license) {
return callback(license);
});
url,
data,
})
.done(license => callback(license));
},
gitignoreText: function(key, callback) {
var url = Api.buildUrl(Api.gitignorePath)
gitignoreText(key, callback) {
const url = Api.buildUrl(Api.gitignorePath)
.replace(':key', key);
return $.get(url, function(gitignore) {
return callback(gitignore);
});
return $.get(url, gitignore => callback(gitignore));
},
gitlabCiYml: function(key, callback) {
var url = Api.buildUrl(Api.gitlabCiYmlPath)
gitlabCiYml(key, callback) {
const url = Api.buildUrl(Api.gitlabCiYmlPath)
.replace(':key', key);
return $.get(url, function(file) {
return callback(file);
});
return $.get(url, file => callback(file));
},
dockerfileYml: function(key, callback) {
var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
dockerfileYml(key, callback) {
const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
$.get(url, callback);
},
issueTemplate: function(namespacePath, projectPath, key, type, callback) {
var url = Api.buildUrl(Api.issuableTemplatePath)
issueTemplate(namespacePath, projectPath, key, type, callback) {
const url = Api.buildUrl(Api.issuableTemplatePath)
.replace(':key', key)
.replace(':type', type)
.replace(':project_path', projectPath)
.replace(':namespace_path', namespacePath);
$.ajax({
url: url,
dataType: 'json'
}).done(function(file) {
callback(null, file);
}).error(callback);
url,
dataType: 'json',
})
.done(file => callback(null, file))
.error(callback);
},
buildUrl: function(url) {
users(query, options) {
const url = Api.buildUrl(this.usersPath);
return Api.wrapAjaxCall({
url,
data: Object.assign({
search: query,
per_page: 20,
}, options),
dataType: 'json',
});
},
buildUrl(url) {
let urlRoot = '';
if (gon.relative_url_root != null) {
url = gon.relative_url_root + url;
urlRoot = gon.relative_url_root;
}
return url.replace(':version', gon.api_version);
}
return urlRoot + url.replace(':version', gon.api_version);
},
wrapAjaxCall(options) {
return new Promise((resolve, reject) => {
// jQuery 2 is not Promises/A+ compatible (missing catch)
$.ajax(options) // eslint-disable-line promise/catch-or-return
.then(data => resolve(data),
(jqXHR, textStatus, errorThrown) => {
const error = new Error(`${options.url}: ${errorThrown}`);
error.textStatus = textStatus;
reject(error);
},
);
});
},
};
window.Api = Api;
export default Api;
/* global Flash */
import sqljs from 'sql.js';
import { template as _template } from 'underscore';
......@@ -15,19 +13,27 @@ const PREVIEW_TEMPLATE = _template(`
class BalsamiqViewer {
constructor(viewer) {
this.viewer = viewer;
this.endpoint = this.viewer.dataset.endpoint;
}
loadFile() {
const xhr = new XMLHttpRequest();
loadFile(endpoint) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', endpoint, true);
xhr.responseType = 'arraybuffer';
xhr.onload = loadEvent => this.fileLoaded(loadEvent, resolve, reject);
xhr.onerror = reject;
xhr.send();
});
}
xhr.open('GET', this.endpoint, true);
xhr.responseType = 'arraybuffer';
fileLoaded(loadEvent, resolve, reject) {
if (loadEvent.target.status !== 200) return reject();
xhr.onload = this.renderFile.bind(this);
xhr.onerror = BalsamiqViewer.onError;
this.renderFile(loadEvent);
xhr.send();
return resolve();
}
renderFile(loadEvent) {
......@@ -103,12 +109,6 @@ class BalsamiqViewer {
static parseTitle(resource) {
return JSON.parse(resource.values[0][2]).name;
}
static onError() {
const flash = new Flash('Balsamiq file could not be loaded.');
return flash;
}
}
export default BalsamiqViewer;
/* global Flash */
import BalsamiqViewer from './balsamiq/balsamiq_viewer';
document.addEventListener('DOMContentLoaded', () => {
const balsamiqViewer = new BalsamiqViewer(document.getElementById('js-balsamiq-viewer'));
balsamiqViewer.loadFile();
});
function onError() {
const flash = new window.Flash('Balsamiq file could not be loaded.');
return flash;
}
function loadBalsamiqFile() {
const viewer = document.getElementById('js-balsamiq-viewer');
if (!(viewer instanceof Element)) return;
const endpoint = viewer.dataset.endpoint;
const balsamiqViewer = new BalsamiqViewer(viewer);
balsamiqViewer.loadFile(endpoint).catch(onError);
}
$(loadBalsamiqFile);
/* global Api */
export default class FileTemplateSelector {
constructor(mediator) {
this.mediator = mediator;
......@@ -65,4 +63,3 @@ export default class FileTemplateSelector {
this.reportSelection(opts);
}
}
/* global Api */
import Api from '../../api';
import FileTemplateSelector from '../file_template_selector';
......
/* global Api */
import Api from '../../api';
import FileTemplateSelector from '../file_template_selector';
......
/* global Api */
import Api from '../../api';
import FileTemplateSelector from '../file_template_selector';
......
/* global Api */
import Api from '../../api';
import FileTemplateSelector from '../file_template_selector';
......
/* global Flash */
export default class BlobViewer {
constructor() {
BlobViewer.initAuxiliaryViewer();
this.initMainViewers();
}
static initAuxiliaryViewer() {
const auxiliaryViewer = document.querySelector('.blob-viewer[data-type="auxiliary"]');
if (!auxiliaryViewer) return;
BlobViewer.loadViewer(auxiliaryViewer);
}
initMainViewers() {
this.$fileHolder = $('.file-holder');
if (!this.$fileHolder.length) return;
this.switcher = document.querySelector('.js-blob-viewer-switcher');
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
this.simpleViewer = document.querySelector('.blob-viewer[data-type="simple"]');
this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
this.$fileHolder = $('.file-holder');
let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type');
this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
this.initBindings();
this.switchToInitialViewer();
}
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.getAttribute('data-type');
if (this.switcher && location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
}
......@@ -29,9 +50,9 @@ export default class BlobViewer {
if (this.copySourceBtn) {
this.copySourceBtn.addEventListener('click', () => {
if (this.copySourceBtn.classList.contains('disabled')) return;
if (this.copySourceBtn.classList.contains('disabled')) return this.copySourceBtn.blur();
this.switchToViewer('simple');
return this.switchToViewer('simple');
});
}
}
......@@ -61,40 +82,13 @@ export default class BlobViewer {
$(this.copySourceBtn).tooltip('fixTitle');
}
loadViewer(viewerParam) {
const viewer = viewerParam;
const url = viewer.getAttribute('data-url');
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
return;
}
viewer.setAttribute('data-loading', 'true');
$.ajax({
url,
dataType: 'JSON',
})
.fail(() => new Flash('Error loading source view'))
.done((data) => {
viewer.innerHTML = data.html;
$(viewer).syntaxHighlight();
viewer.setAttribute('data-loaded', 'true');
this.$fileHolder.trigger('highlight:line');
this.toggleCopyButtonState();
});
}
switchToViewer(name) {
const newViewer = document.querySelector(`.blob-viewer[data-type='${name}']`);
const newViewer = this.$fileHolder[0].querySelector(`.blob-viewer[data-type='${name}']`);
if (this.activeViewer === newViewer) return;
const oldButton = document.querySelector('.js-blob-viewer-switch-btn.active');
const newButton = document.querySelector(`.js-blob-viewer-switch-btn[data-viewer='${name}']`);
const oldViewer = document.querySelector(`.blob-viewer:not([data-type='${name}'])`);
const oldViewer = this.$fileHolder[0].querySelector(`.blob-viewer:not([data-type='${name}'])`);
if (oldButton) {
oldButton.classList.remove('active');
......@@ -115,6 +109,41 @@ export default class BlobViewer {
this.toggleCopyButtonState();
this.loadViewer(newViewer);
BlobViewer.loadViewer(newViewer)
.then((viewer) => {
$(viewer).syntaxHighlight();
this.$fileHolder.trigger('highlight:line');
gl.utils.handleLocationHash();
this.toggleCopyButtonState();
})
.catch(() => new Flash('Error loading viewer'));
}
static loadViewer(viewerParam) {
const viewer = viewerParam;
const url = viewer.getAttribute('data-url');
return new Promise((resolve, reject) => {
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
resolve(viewer);
return;
}
viewer.setAttribute('data-loading', 'true');
$.ajax({
url,
dataType: 'JSON',
})
.fail(reject)
.done((data) => {
viewer.innerHTML = data.html;
viewer.setAttribute('data-loaded', 'true');
resolve(viewer);
});
});
}
}
......@@ -6,23 +6,22 @@ import Vue from 'vue';
import VueResource from 'vue-resource';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
require('./models/issue');
require('./models/label');
require('./models/list');
require('./models/milestone');
require('./models/assignee');
require('./stores/boards_store');
require('./stores/modal_store');
require('./services/board_service');
require('./mixins/modal_mixins');
require('./mixins/sortable_default_options');
require('./filters/due_date_filters');
require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
require('./components/modal/index');
require('../vue_shared/vue_resource_interceptor');
import './models/issue';
import './models/label';
import './models/list';
import './models/milestone';
import './models/assignee';
import './stores/boards_store';
import './stores/modal_store';
import './services/board_service';
import './mixins/modal_mixins';
import './mixins/sortable_default_options';
import './filters/due_date_filters';
import './components/board';
import './components/board_sidebar';
import './components/new_list_dropdown';
import './components/modal/index';
import '../vue_shared/vue_resource_interceptor';
Vue.use(VueResource);
......
......@@ -3,9 +3,7 @@
import Vue from 'vue';
import boardList from './board_list';
import boardBlankState from './board_blank_state';
require('./board_delete');
require('./board_list');
import './board_delete';
const Store = gl.issueBoards.BoardsStore;
......@@ -35,7 +33,10 @@ gl.issueBoards.Board = Vue.extend({
filter: {
handler() {
this.list.page = 1;
this.list.getIssues(true);
this.list.getIssues(true)
.catch(() => {
// TODO: handle request error
});
},
deep: true,
},
......
......@@ -70,7 +70,10 @@ export default {
list.id = listObj.id;
list.label.id = listObj.label.id;
list.getIssues();
list.getIssues()
.catch(() => {
// TODO: handle request error
});
});
})
.catch(() => {
......
require('./issue_card_inner');
import './issue_card_inner';
const Store = gl.issueBoards.BoardsStore;
......
......@@ -2,6 +2,7 @@
import boardNewIssue from './board_new_issue';
import boardCard from './board_card';
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
const Store = gl.issueBoards.BoardsStore;
......@@ -44,6 +45,7 @@ export default {
components: {
boardCard,
boardNewIssue,
loadingIcon,
},
methods: {
listHeight() {
......@@ -90,7 +92,10 @@ export default {
if (this.scrollHeight() <= this.listHeight() &&
this.list.issuesSize > this.list.issues.length) {
this.list.page += 1;
this.list.getIssues(false);
this.list.getIssues(false)
.catch(() => {
// TODO: handle request error
});
}
if (this.scrollHeight() > Math.ceil(this.listHeight())) {
......@@ -153,10 +158,7 @@ export default {
class="board-list-loading text-center"
aria-label="Loading issues"
v-if="loading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true">
</i>
<loading-icon />
</div>
<board-new-issue
:list="list"
......@@ -181,12 +183,12 @@ export default {
class="board-list-count text-center"
v-if="showCount"
data-id="-1">
<i
class="fa fa-spinner fa-spin"
aria-label="Loading more issues"
aria-hidden="true"
v-show="list.loadingMore">
</i>
<loading-icon
v-show="list.loadingMore"
label="Loading more issues"
/>
<span v-if="list.issues.length === list.issuesSize">
Showing all issues
</span>
......
......@@ -7,11 +7,9 @@
import Vue from 'vue';
import eventHub from '../../sidebar/event_hub';
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title';
import Assignees from '../../sidebar/components/assignees/assignees';
require('./sidebar/remove_issue');
import './sidebar/remove_issue';
const Store = gl.issueBoards.BoardsStore;
......@@ -36,12 +34,21 @@ gl.issueBoards.BoardSidebar = Vue.extend({
},
assigneeId() {
return this.issue.assignee ? this.issue.assignee.id : 0;
},
milestoneTitle() {
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
}
},
watch: {
detail: {
handler () {
if (this.issue.id !== this.detail.issue.id) {
$('.block.assignee')
.find('input:not(.js-vue)[name="issue[assignee_ids][]"]')
.each((i, el) => {
$(el).remove();
});
$('.js-issue-board-sidebar', this.$el).each((i, el) => {
$(el).data('glDropdown').clearMenu();
});
......@@ -56,18 +63,6 @@ gl.issueBoards.BoardSidebar = Vue.extend({
},
deep: true
},
issue () {
if (this.showSidebar) {
this.$nextTick(() => {
$('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
$('.right-sidebar').getNiceScroll().resize();
});
}
this.issue = this.detail.issue;
this.list = this.detail.list;
},
deep: true
},
methods: {
closeSidebar () {
......
import Vue from 'vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore;
......@@ -38,6 +39,9 @@ gl.issueBoards.IssueCardInner = Vue.extend({
maxCounter: 99,
};
},
components: {
userAvatarLink,
},
computed: {
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
......@@ -146,23 +150,16 @@ gl.issueBoards.IssueCardInner = Vue.extend({
</span>
</h4>
<div class="card-assignee">
<a
class="has-tooltip js-no-trigger"
:href="assigneeUrl(assignee)"
:title="assigneeUrlTitle(assignee)"
<user-avatar-link
v-for="(assignee, index) in issue.assignees"
v-if="shouldRenderAssignee(index)"
data-container="body"
data-placement="bottom"
>
<img
class="avatar avatar-inline s20"
:src="assignee.avatar"
width="20"
height="20"
:alt="avatarUrlTitle(assignee)"
/>
</a>
class="js-no-trigger"
:link-href="assigneeUrl(assignee)"
:img-alt="avatarUrlTitle(assignee)"
:img-src="assignee.avatar"
:tooltip-text="assigneeUrlTitle(assignee)"
tooltip-placement="bottom"
/>
<span
class="avatar-counter has-tooltip"
:title="assigneeCounterTooltip"
......
......@@ -2,8 +2,7 @@
/* global Flash */
import Vue from 'vue';
require('./lists_dropdown');
import './lists_dropdown';
const ModalStore = gl.issueBoards.ModalStore;
......
import Vue from 'vue';
import modalFilters from './filters';
require('./tabs');
import './tabs';
const ModalStore = gl.issueBoards.ModalStore;
......
......@@ -2,11 +2,11 @@
import Vue from 'vue';
import queryData from '../../utils/query_data';
require('./header');
require('./list');
require('./footer');
require('./empty_state');
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import './header';
import './list';
import './footer';
import './empty_state';
const ModalStore = gl.issueBoards.ModalStore;
......@@ -108,6 +108,8 @@ gl.issueBoards.IssuesModal = Vue.extend({
if (!this.issuesCount) {
this.issuesCount = data.size;
}
}).catch(() => {
// TODO: handle request error
});
},
},
......@@ -135,6 +137,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
'modal-list': gl.issueBoards.ModalList,
'modal-footer': gl.issueBoards.ModalFooter,
'empty-state': gl.issueBoards.ModalEmptyState,
loadingIcon,
},
template: `
<div
......@@ -159,7 +162,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
class="add-issues-list text-center"
v-if="loading || filterLoading">
<div class="add-issues-list-loading">
<i class="fa fa-spinner fa-spin"></i>
<loading-icon />
</div>
</section>
<modal-footer></modal-footer>
......
......@@ -25,7 +25,9 @@ class List {
}
if (this.type !== 'blank' && this.id) {
this.getIssues();
this.getIssues().catch(() => {
// TODO: handle request error
});
}
}
......@@ -52,11 +54,17 @@ class List {
gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
gl.boardService.destroyList(this.id);
gl.boardService.destroyList(this.id)
.catch(() => {
// TODO: handle request error
});
}
update () {
gl.boardService.updateList(this.id, this.position);
gl.boardService.updateList(this.id, this.position)
.catch(() => {
// TODO: handle request error
});
}
nextPage () {
......@@ -146,11 +154,17 @@ class List {
this.issues.splice(oldIndex, 1);
this.issues.splice(newIndex, 0, issue);
gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid);
gl.boardService.moveIssue(issue.id, null, null, moveBeforeIid, moveAfterIid)
.catch(() => {
// TODO: handle request error
});
}
updateIssueLabel(issue, listFrom, moveBeforeIid, moveAfterIid) {
gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid);
gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeIid, moveAfterIid)
.catch(() => {
// TODO: handle request error
});
}
findIssue (id) {
......
const MODAL_SELECTOR = '#modal-delete-branch';
class DeleteModal {
constructor() {
this.$modal = $(MODAL_SELECTOR);
this.$toggleBtns = $(`[data-target="${MODAL_SELECTOR}"]`);
this.$branchName = $('.js-branch-name', this.$modal);
this.$confirmInput = $('.js-delete-branch-input', this.$modal);
this.$deleteBtn = $('.js-delete-branch', this.$modal);
this.bindEvents();
}
bindEvents() {
this.$toggleBtns.on('click', this.setModalData.bind(this));
this.$confirmInput.on('input', this.setDeleteDisabled.bind(this));
}
setModalData(e) {
this.branchName = e.currentTarget.dataset.branchName || '';
this.deletePath = e.currentTarget.dataset.deletePath || '';
this.updateModal();
}
setDeleteDisabled(e) {
this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName);
}
updateModal() {
this.$branchName.text(this.branchName);
this.$confirmInput.val('');
this.$deleteBtn.attr('href', this.deletePath);
this.$deleteBtn.attr('disabled', true);
}
}
export default DeleteModal;
import CANCELED_SVG from 'icons/_icon_status_canceled_borderless.svg';
import CREATED_SVG from 'icons/_icon_status_created_borderless.svg';
import FAILED_SVG from 'icons/_icon_status_failed_borderless.svg';
import MANUAL_SVG from 'icons/_icon_status_manual_borderless.svg';
import PENDING_SVG from 'icons/_icon_status_pending_borderless.svg';
import RUNNING_SVG from 'icons/_icon_status_running_borderless.svg';
import SKIPPED_SVG from 'icons/_icon_status_skipped_borderless.svg';
import SUCCESS_SVG from 'icons/_icon_status_success_borderless.svg';
import WARNING_SVG from 'icons/_icon_status_warning_borderless.svg';
const StatusIconEntityMap = {
icon_status_canceled: CANCELED_SVG,
icon_status_created: CREATED_SVG,
icon_status_failed: FAILED_SVG,
icon_status_manual: MANUAL_SVG,
icon_status_pending: PENDING_SVG,
icon_status_running: RUNNING_SVG,
icon_status_skipped: SKIPPED_SVG,
icon_status_success: SUCCESS_SVG,
icon_status_warning: WARNING_SVG,
};
export {
CANCELED_SVG,
CREATED_SVG,
FAILED_SVG,
MANUAL_SVG,
PENDING_SVG,
RUNNING_SVG,
SKIPPED_SVG,
SUCCESS_SVG,
WARNING_SVG,
StatusIconEntityMap as default,
};
import Vue from 'vue';
import Visibility from 'visibilityjs';
import PipelinesTableComponent from '../../vue_shared/components/pipelines_table';
import pipelinesTableComponent from '../../vue_shared/components/pipelines_table';
import PipelinesService from '../../pipelines/services/pipelines_service';
import PipelineStore from '../../pipelines/stores/pipelines_store';
import eventHub from '../../pipelines/event_hub';
import EmptyState from '../../pipelines/components/empty_state.vue';
import ErrorState from '../../pipelines/components/error_state.vue';
import emptyState from '../../pipelines/components/empty_state.vue';
import errorState from '../../pipelines/components/error_state.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
import Poll from '../../lib/utils/poll';
......@@ -17,16 +18,15 @@ import Poll from '../../lib/utils/poll';
* We need a store to store the received environemnts.
* We need a service to communicate with the server.
*
* Necessary SVG in the table are provided as props. This should be refactored
* as soon as we have Webpack and can load them directly into JS files.
*/
export default Vue.component('pipelines-table', {
components: {
'pipelines-table-component': PipelinesTableComponent,
'error-state': ErrorState,
'empty-state': EmptyState,
pipelinesTableComponent,
errorState,
emptyState,
loadingIcon,
},
/**
......@@ -47,6 +47,7 @@ export default Vue.component('pipelines-table', {
hasError: false,
isMakingRequest: false,
updateGraphDropdown: false,
hasMadeRequest: false,
};
},
......@@ -55,9 +56,15 @@ export default Vue.component('pipelines-table', {
return this.hasError && !this.isLoading;
},
/**
* Empty state is only rendered if after the first request we receive no pipelines.
*
* @return {Boolean}
*/
shouldRenderEmptyState() {
return !this.state.pipelines.length &&
!this.isLoading &&
this.hasMadeRequest &&
!this.hasError;
},
......@@ -94,6 +101,10 @@ export default Vue.component('pipelines-table', {
if (!Visibility.hidden()) {
this.isLoading = true;
this.poll.makeRequest();
} else {
// If tab is not visible we need to make the first request so we don't show the empty
// state without knowing if there are any pipelines
this.fetchPipelines();
}
Visibility.change(() => {
......@@ -127,6 +138,8 @@ export default Vue.component('pipelines-table', {
successCallback(resp) {
const response = resp.json();
this.hasMadeRequest = true;
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response;
this.store.storePipelines(pipelines);
......@@ -151,13 +164,12 @@ export default Vue.component('pipelines-table', {
template: `
<div class="content-list pipelines">
<div
class="realtime-loading"
v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
<loading-icon
label="Loading pipelines"
size="3"
v-if="isLoading"
/>
<empty-state
v-if="shouldRenderEmptyState"
......
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
require('./lib/utils/common_utils');
import './lib/utils/common_utils';
const gfmRules = {
// The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert
......
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */
/* global Api */
import Api from './api';
class CreateLabelDropdown {
constructor ($el, namespacePath, projectPath) {
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -10,6 +11,9 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
items: Array,
stage: Object,
},
components: {
userAvatarImage,
},
template: `
<div>
<div class="events-description">
......@@ -19,7 +23,8 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
......@@ -28,11 +33,11 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
<a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
&middot;
<span>
{{ __('OpenedNDaysAgo|Opened') }}
{{ s__('OpenedNDaysAgo|Opened') }}
<a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
</span>
<span>
{{ __('ByAuthor|by') }}
{{ s__('ByAuthor|by') }}
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
</span>
</div>
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -10,6 +10,9 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
items: Array,
stage: Object,
},
components: {
userAvatarImage,
},
template: `
<div>
<div class="events-description">
......@@ -19,7 +22,8 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
......@@ -28,11 +32,11 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
{{ __('OpenedNDaysAgo|Opened') }}
{{ s__('OpenedNDaysAgo|Opened') }}
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
{{ __('ByAuthor|by') }}
{{ s__('ByAuthor|by') }}
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import iconCommit from '../svg/icon_commit.svg';
const global = window.gl || (window.gl = {});
......@@ -10,11 +11,12 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
items: Array,
stage: Object,
},
components: {
userAvatarImage,
},
data() {
return { iconCommit };
},
template: `
<div>
<div class="events-description">
......@@ -24,17 +26,18 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item">
<div class="item-details item-conmmit-component">
<img class="avatar" :src="commit.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="commit.author.avatarUrl"/>
<h5 class="item-title commit-title">
<a :href="commit.commitUrl">
{{ commit.title }}
</a>
</h5>
<span>
{{ __('FirstPushedBy|First') }}
{{ s__('FirstPushedBy|First') }}
<span class="commit-icon">${iconCommit}</span>
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
{{ __('FirstPushedBy|pushed by') }}
<a :href="commit.commitUrl" class="commit-hash-link commit-sha">{{ commit.shortSha }}</a>
{{ s__('FirstPushedBy|pushed by') }}
<a :href="commit.author.webUrl" class="commit-author-link">
{{ commit.author.name }}
</a>
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -10,6 +10,9 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
items: Array,
stage: Object,
},
components: {
userAvatarImage,
},
template: `
<div>
<div class="events-description">
......@@ -19,7 +22,8 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
......@@ -28,11 +32,11 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
{{ __('OpenedNDaysAgo|Opened') }}
{{ s__('OpenedNDaysAgo|Opened') }}
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
{{ __('ByAuthor|by') }}
{{ s__('ByAuthor|by') }}
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
......@@ -10,6 +10,9 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
items: Array,
stage: Object,
},
components: {
userAvatarImage,
},
template: `
<div>
<div class="events-description">
......@@ -19,7 +22,8 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
......@@ -28,11 +32,11 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
<a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
&middot;
<span>
{{ __('OpenedNDaysAgo|Opened') }}
{{ s__('OpenedNDaysAgo|Opened') }}
<a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
</span>
<span>
{{ __('ByAuthor|by') }}
{{ s__('ByAuthor|by') }}
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
</span>
<template v-if="mergeRequest.state === 'closed'">
......
/* eslint-disable no-param-reassign */
import Vue from 'vue';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
import iconBranch from '../svg/icon_branch.svg';
const global = window.gl || (window.gl = {});
......@@ -13,6 +14,9 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
data() {
return { iconBranch };
},
components: {
userAvatarImage,
},
template: `
<div>
<div class="events-description">
......@@ -22,17 +26,18 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details">
<img class="avatar" :src="build.author.avatarUrl">
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image :img-src="build.author.avatarUrl"/>
<h5 class="item-title">
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
<a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="build-date">{{ build.date }}</a>
{{ __('ByAuthor|by') }}
{{ s__('ByAuthor|by') }}
<a :href="build.author.webUrl" class="issue-author-link">
{{ build.author.name }}
</a>
......
......@@ -29,9 +29,9 @@ global.cycleAnalytics.StageTestComponent = Vue.extend({
&middot;
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
<a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="issue-date">
......
......@@ -4,18 +4,16 @@ import Vue from 'vue';
import Cookies from 'js-cookie';
import Translate from '../vue_shared/translate';
import LimitWarningComponent from './components/limit_warning_component';
require('./components/stage_code_component');
require('./components/stage_issue_component');
require('./components/stage_plan_component');
require('./components/stage_production_component');
require('./components/stage_review_component');
require('./components/stage_staging_component');
require('./components/stage_test_component');
require('./components/total_time_component');
require('./cycle_analytics_service');
require('./cycle_analytics_store');
require('./default_event_objects');
import './components/stage_code_component';
import './components/stage_issue_component';
import './components/stage_plan_component';
import './components/stage_production_component';
import './components/stage_review_component';
import './components/stage_staging_component';
import './components/stage_test_component';
import './components/total_time_component';
import './cycle_analytics_service';
import './cycle_analytics_store';
Vue.use(Translate);
......
/* eslint-disable no-param-reassign */
import { __ } from '../locale';
require('../lib/utils/text_utility');
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
import { __ } from '../locale';
import '../lib/utils/text_utility';
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {};
......
module.exports = {
export default {
issue: {
created_at: '',
url: '',
......
<script>
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
data() {
......@@ -22,6 +23,11 @@
default: 'btn-default',
},
},
components: {
loadingIcon,
},
methods: {
doAction() {
this.isLoading = true;
......@@ -44,11 +50,6 @@
:disabled="isLoading"
@click="doAction">
{{ text }}
<i
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="Loading">
</i>
<loading-icon v-if="isLoading" />
</button>
</template>
......@@ -4,6 +4,7 @@
import DeployKeysService from '../service';
import DeployKeysStore from '../store';
import keysPanel from './keys_panel.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
data() {
......@@ -28,6 +29,7 @@
},
components: {
keysPanel,
loadingIcon,
},
methods: {
fetchKeys() {
......@@ -74,15 +76,11 @@
<template>
<div class="col-lg-9 col-lg-offset-3 append-bottom-default deploy-keys">
<div
class="text-center"
v-if="isLoading && !hasKeys">
<i
class="fa fa-spinner fa-spin fa-2x"
aria-hidden="true"
aria-label="Loading deploy keys">
</i>
</div>
<loading-icon
v-if="isLoading && !hasKeys"
size="2"
label="Loading deploy keys"
/>
<div v-else-if="hasKeys">
<keys-panel
title="Enabled deploy keys for this project"
......
/* eslint-disable class-methods-use-this */
require('./lib/utils/url_utility');
import './lib/utils/url_utility';
const UNFOLD_COUNT = 20;
let isBound = false;
......
......@@ -3,6 +3,7 @@
import Vue from 'vue';
import collapseIcon from '../icons/collapse_icon.svg';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const DiffNoteAvatars = Vue.extend({
props: ['discussionId'],
......@@ -15,22 +16,24 @@ const DiffNoteAvatars = Vue.extend({
collapseIcon,
};
},
components: {
userAvatarImage,
},
template: `
<div class="diff-comment-avatar-holders"
v-show="notesCount !== 0">
<div v-if="!isVisible">
<img v-for="note in notesSubset"
class="avatar diff-comment-avatar has-tooltip js-diff-comment-avatar"
width="19"
height="19"
role="button"
data-container="body"
data-placement="top"
data-html="true"
<!-- FIXME: Pass an alt attribute here for accessibility -->
<user-avatar-image
v-for="note in notesSubset"
class="diff-comment-avatar js-diff-comment-avatar"
@click.native="clickedAvatar($event)"
:img-src="note.authorAvatar"
:tooltip-text="getTooltipText(note)"
:data-line-type="lineType"
:title="note.authorName + ': ' + note.noteTruncated"
:src="note.authorAvatar"
@click="clickedAvatar($event)" />
:size="19"
data-html="true"
/>
<span v-if="notesCount > shownAvatars"
class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
data-container="body"
......@@ -120,7 +123,7 @@ const DiffNoteAvatars = Vue.extend({
},
methods: {
clickedAvatar(e) {
notes.addDiffNote(e);
notes.onAddDiffNote(e);
// Toggle the active state of the toggle all button
this.toggleDiscussionsToggleState();
......@@ -150,6 +153,9 @@ const DiffNoteAvatars = Vue.extend({
setDiscussionVisible() {
this.isVisible = $(`.diffs .notes[data-discussion-id="${this.discussion.id}"]`).is(':visible');
},
getTooltipText(note) {
return `${note.authorName}: ${note.noteTruncated}`;
},
},
});
......
......@@ -88,6 +88,7 @@ const ResolveBtn = Vue.extend({
CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by);
this.discussion.updateHeadline(data);
gl.mrWidget.checkStatus();
} else {
new Flash(errorFlashMsg);
}
......
......@@ -2,19 +2,18 @@
/* global ResolveCount */
import Vue from 'vue';
require('./models/discussion');
require('./models/note');
require('./stores/comments');
require('./services/resolve');
require('./mixins/discussion');
require('./components/comment_resolve_btn');
require('./components/jump_to_discussion');
require('./components/resolve_btn');
require('./components/resolve_count');
require('./components/resolve_discussion_btn');
require('./components/diff_note_avatars');
require('./components/new_issue_for_discussion');
import './models/discussion';
import './models/note';
import './stores/comments';
import './services/resolve';
import './mixins/discussion';
import './components/comment_resolve_btn';
import './components/jump_to_discussion';
import './components/resolve_btn';
import './components/resolve_count';
import './components/resolve_discussion_btn';
import './components/diff_note_avatars';
import './components/new_issue_for_discussion';
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
......@@ -65,4 +64,6 @@ $(() => {
'resolve-count': ResolveCount
}
});
$(window).trigger('resize.nav');
});
......@@ -3,11 +3,7 @@
/* global CommentsStore */
import Vue from 'vue';
import VueResource from 'vue-resource';
require('../../vue_shared/vue_resource_interceptor');
Vue.use(VueResource);
import '../../vue_shared/vue_resource_interceptor';
window.gl = window.gl || {};
......@@ -49,6 +45,7 @@ class ResolveServiceClass {
discussion.resolveAllNotes(resolved_by);
}
gl.mrWidget.checkStatus();
discussion.updateHeadline(data);
} else {
throw new Error('An error occurred when trying to resolve discussion.');
......
......@@ -10,12 +10,10 @@
/* global IssuableForm */
/* global LabelsSelect */
/* global MilestoneSelect */
/* global MergedButtons */
/* global Commit */
/* global NotificationsForm */
/* global TreeView */
/* global NotificationsDropdown */
/* global UsersSelect */
/* global GroupAvatar */
/* global LineHighlighter */
/* global ProjectFork */
......@@ -36,12 +34,13 @@
/* global ShortcutsWiki */
import Issue from './issue';
import BindInOut from './behaviors/bind_in_out';
import DeleteModal from './branches/branches_delete_modal';
import Group from './group';
import GroupName from './group_name';
import GroupsList from './groups_list';
import ProjectsList from './projects_list';
import setupProjectEdit from './project_edit';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing';
......@@ -49,10 +48,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
import UserCallout from './user_callout';
import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags';
import ShortcutsWiki from './shortcuts_wiki';
import Pipelines from './pipelines';
import BlobViewer from './blob/viewer/index';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
const ShortcutsBlob = require('./shortcuts_blob');
import UsersSelect from './users_select';
import RefSelectDropdown from './ref_select_dropdown';
import GfmAutoComplete from './gfm_auto_complete';
import ShortcutsBlob from './shortcuts_blob';
(function() {
var Dispatcher;
......@@ -77,6 +79,8 @@ const ShortcutsBlob = require('./shortcuts_blob');
path = page.split(':');
shortcut_handler = null;
new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
function initBlob() {
new LineHighlighter();
......@@ -112,6 +116,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:boards:show':
case 'projects:boards:index':
shortcut_handler = new ShortcutsNavigation();
new UsersSelect();
break;
case 'projects:builds:show':
new Build();
......@@ -126,6 +131,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
prefixId: page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_',
});
shortcut_handler = new ShortcutsNavigation();
new UsersSelect();
break;
case 'projects:issues:show':
new Issue();
......@@ -138,6 +144,10 @@ const ShortcutsBlob = require('./shortcuts_blob');
new Milestone();
new Sidebar();
break;
case 'groups:issues':
case 'groups:merge_requests':
new UsersSelect();
break;
case 'dashboard:todos:index':
new gl.Todos();
break;
......@@ -179,6 +189,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
break;
case 'projects:branches:index':
gl.AjaxLoadingSpinner.init();
new DeleteModal();
break;
case 'projects:issues:new':
case 'projects:issues:edit':
......@@ -204,6 +215,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:tags:new':
new ZenMode();
new gl.GLForm($('.tag-form'));
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
break;
case 'projects:releases:edit':
new ZenMode();
......@@ -213,19 +225,18 @@ const ShortcutsBlob = require('./shortcuts_blob');
new gl.Diff();
shortcut_handler = new ShortcutsIssuable(true);
new ZenMode();
new MergedButtons();
break;
case 'projects:merge_requests:commits':
new MergedButtons();
break;
case "projects:merge_requests:diffs":
new gl.Diff();
new ZenMode();
new MergedButtons();
break;
case 'dashboard:activity':
new gl.Activities();
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
new UsersSelect();
break;
case 'projects:commit:show':
new Commit();
new gl.Diff();
......@@ -250,6 +261,12 @@ const ShortcutsBlob = require('./shortcuts_blob');
if ($('#tree-slider').length) {
new TreeView();
}
if ($('.blob-viewer').length) {
new BlobViewer();
}
break;
case 'projects:edit':
setupProjectEdit();
break;
case 'projects:pipelines:builds':
case 'projects:pipelines:failures':
......@@ -257,7 +274,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`;
new gl.Pipelines({
new Pipelines({
initTabs: true,
pipelineStatusUrl,
tabsOptions: {
......@@ -303,6 +320,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation();
new TreeView();
new BlobViewer();
gl.TargetBranchDropDown.bootstrap();
break;
case 'projects:find_file:show':
......@@ -378,6 +396,9 @@ const ShortcutsBlob = require('./shortcuts_blob');
new LineHighlighter();
new BlobViewer();
break;
case 'import:fogbugz:new_user_map':
new UsersSelect();
break;
}
switch (path.first()) {
case 'sessions':
......
/* eslint-disable */
import utils from './utils';
import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
var DropDown = function(list) {
this.currentIndex = 0;
this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list;
this.items = [];
class DropDown {
constructor(list) {
this.currentIndex = 0;
this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list;
this.items = [];
this.eventWrapper = {};
this.eventWrapper = {};
this.getItems();
this.initTemplateString();
this.addEvents();
this.getItems();
this.initTemplateString();
this.addEvents();
this.initialState = list.innerHTML;
};
this.initialState = list.innerHTML;
}
Object.assign(DropDown.prototype, {
getItems: function() {
getItems() {
this.items = [].slice.call(this.list.querySelectorAll('li'));
return this.items;
},
}
initTemplateString: function() {
var items = this.items || this.getItems();
initTemplateString() {
const items = this.items || this.getItems();
var templateString = '';
let templateString = '';
if (items.length > 0) templateString = items[items.length - 1].outerHTML;
this.templateString = templateString;
return this.templateString;
},
}
clickEvent: function(e) {
clickEvent(e) {
if (e.target.tagName === 'UL') return;
if (e.target.classList.contains(IGNORE_CLASS)) return;
var selected = utils.closest(e.target, 'LI');
const selected = utils.closest(e.target, 'LI');
if (!selected) return;
this.addSelectedClass(selected);
......@@ -46,95 +44,95 @@ Object.assign(DropDown.prototype, {
e.preventDefault();
this.hide();
var listEvent = new CustomEvent('click.dl', {
const listEvent = new CustomEvent('click.dl', {
detail: {
list: this,
selected: selected,
selected,
data: e.target.dataset,
},
});
this.list.dispatchEvent(listEvent);
},
}
addSelectedClass: function (selected) {
addSelectedClass(selected) {
this.removeSelectedClasses();
selected.classList.add(SELECTED_CLASS);
},
}
removeSelectedClasses: function () {
removeSelectedClasses() {
const items = this.items || this.getItems();
items.forEach(item => item.classList.remove(SELECTED_CLASS));
},
}
addEvents: function() {
this.eventWrapper.clickEvent = this.clickEvent.bind(this)
addEvents() {
this.eventWrapper.clickEvent = this.clickEvent.bind(this);
this.list.addEventListener('click', this.eventWrapper.clickEvent);
},
toggle: function() {
this.hidden ? this.show() : this.hide();
},
}
setData: function(data) {
setData(data) {
this.data = data;
this.render(data);
},
}
addData: function(data) {
addData(data) {
this.data = (this.data || []).concat(data);
this.render(this.data);
},
}
render: function(data) {
render(data) {
const children = data ? data.map(this.renderChildren.bind(this)) : [];
const renderableList = this.list.querySelector('ul[data-dynamic]') || this.list;
renderableList.innerHTML = children.join('');
},
}
renderChildren: function(data) {
var html = utils.template(this.templateString, data);
var template = document.createElement('div');
renderChildren(data) {
const html = utils.template(this.templateString, data);
const template = document.createElement('div');
template.innerHTML = html;
this.setImagesSrc(template);
DropDown.setImagesSrc(template);
template.firstChild.style.display = data.droplab_hidden ? 'none' : 'block';
return template.firstChild.outerHTML;
},
setImagesSrc: function(template) {
const images = [].slice.call(template.querySelectorAll('img[data-src]'));
images.forEach((image) => {
image.src = image.getAttribute('data-src');
image.removeAttribute('data-src');
});
},
}
show: function() {
show() {
if (!this.hidden) return;
this.list.style.display = 'block';
this.currentIndex = 0;
this.hidden = false;
},
}
hide: function() {
hide() {
if (this.hidden) return;
this.list.style.display = 'none';
this.currentIndex = 0;
this.hidden = true;
},
}
toggle: function () {
this.hidden ? this.show() : this.hide();
},
toggle() {
if (this.hidden) return this.show();
destroy: function() {
return this.hide();
}
destroy() {
this.hide();
this.list.removeEventListener('click', this.eventWrapper.clickEvent);
}
});
static setImagesSrc(template) {
const images = [...template.querySelectorAll('img[data-src]')];
images.forEach((image) => {
const img = image;
img.src = img.getAttribute('data-src');
img.removeAttribute('data-src');
});
}
}
export default DropDown;
/* eslint-disable */
import HookButton from './hook_button';
import HookInput from './hook_input';
import utils from './utils';
import Keyboard from './keyboard';
import { DATA_TRIGGER } from './constants';
var DropLab = function() {
this.ready = false;
this.hooks = [];
this.queuedData = [];
this.config = {};
class DropLab {
constructor() {
this.ready = false;
this.hooks = [];
this.queuedData = [];
this.config = {};
this.eventWrapper = {};
};
this.eventWrapper = {};
}
Object.assign(DropLab.prototype, {
loadStatic: function(){
var dropdownTriggers = [].slice.apply(document.querySelectorAll(`[${DATA_TRIGGER}]`));
loadStatic() {
const dropdownTriggers = [].slice.apply(document.querySelectorAll(`[${DATA_TRIGGER}]`));
this.addHooks(dropdownTriggers);
},
}
addData: function () {
var args = [].slice.apply(arguments);
this.applyArgs(args, '_addData');
},
addData(...args) {
this.applyArgs(args, 'processAddData');
}
setData: function() {
var args = [].slice.apply(arguments);
this.applyArgs(args, '_setData');
},
setData(...args) {
this.applyArgs(args, 'processSetData');
}
destroy: function() {
destroy() {
this.hooks.forEach(hook => hook.destroy());
this.hooks = [];
this.removeEvents();
},
}
applyArgs: function(args, methodName) {
if (this.ready) return this[methodName].apply(this, args);
applyArgs(args, methodName) {
if (this.ready) return this[methodName](...args);
this.queuedData = this.queuedData || [];
this.queuedData.push(args);
},
_addData: function(trigger, data) {
this._processData(trigger, data, 'addData');
},
return this.ready;
}
processAddData(trigger, data) {
this.processData(trigger, data, 'addData');
}
_setData: function(trigger, data) {
this._processData(trigger, data, 'setData');
},
processSetData(trigger, data) {
this.processData(trigger, data, 'setData');
}
_processData: function(trigger, data, methodName) {
processData(trigger, data, methodName) {
this.hooks.forEach((hook) => {
if (Array.isArray(trigger)) hook.list[methodName](trigger);
if (hook.trigger.id === trigger) hook.list[methodName](data);
});
},
}
addEvents: function() {
this.eventWrapper.documentClicked = this.documentClicked.bind(this)
addEvents() {
this.eventWrapper.documentClicked = this.documentClicked.bind(this);
document.addEventListener('click', this.eventWrapper.documentClicked);
},
}
documentClicked: function(e) {
documentClicked(e) {
let thisTag = e.target;
if (thisTag.tagName !== 'UL') thisTag = utils.closest(thisTag, 'UL');
if (utils.isDropDownParts(thisTag, this.hooks) || utils.isDropDownParts(e.target, this.hooks)) return;
if (utils.isDropDownParts(thisTag, this.hooks)) return;
if (utils.isDropDownParts(e.target, this.hooks)) return;
this.hooks.forEach(hook => hook.list.hide());
},
}
removeEvents: function(){
removeEvents() {
document.removeEventListener('click', this.eventWrapper.documentClicked);
},
changeHookList: function(trigger, list, plugins, config) {
const availableTrigger = typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
}
changeHookList(trigger, list, plugins, config) {
const availableTrigger = typeof trigger === 'string' ? document.getElementById(trigger) : trigger;
this.hooks.forEach((hook, i) => {
hook.list.list.dataset.dropdownActive = false;
const aHook = hook;
aHook.list.list.dataset.dropdownActive = false;
if (hook.trigger !== availableTrigger) return;
if (aHook.trigger !== availableTrigger) return;
hook.destroy();
aHook.destroy();
this.hooks.splice(i, 1);
this.addHook(availableTrigger, list, plugins, config);
});
},
}
addHook: function(hook, list, plugins, config) {
addHook(hook, list, plugins, config) {
const availableHook = typeof hook === 'string' ? document.querySelector(hook) : hook;
let availableList;
......@@ -111,18 +111,18 @@ Object.assign(DropLab.prototype, {
this.hooks.push(new HookObject(availableHook, availableList, plugins, config));
return this;
},
}
addHooks: function(hooks, plugins, config) {
addHooks(hooks, plugins, config) {
hooks.forEach(hook => this.addHook(hook, null, plugins, config));
return this;
},
}
setConfig: function(obj){
setConfig(obj) {
this.config = obj;
},
}
fireReady: function() {
fireReady() {
const readyEvent = new CustomEvent('ready.dl', {
detail: {
dropdown: this,
......@@ -131,10 +131,14 @@ Object.assign(DropLab.prototype, {
document.dispatchEvent(readyEvent);
this.ready = true;
},
}
init: function (hook, list, plugins, config) {
hook ? this.addHook(hook, list, plugins, config) : this.loadStatic();
init(hook, list, plugins, config) {
if (hook) {
this.addHook(hook, list, plugins, config);
} else {
this.loadStatic();
}
this.addEvents();
......@@ -146,7 +150,7 @@ Object.assign(DropLab.prototype, {
this.queuedData = [];
return this;
},
});
}
}
export default DropLab;
/* eslint-disable */
import DropDown from './drop_down';
var Hook = function(trigger, list, plugins, config){
this.trigger = trigger;
this.list = new DropDown(list);
this.type = 'Hook';
this.event = 'click';
this.plugins = plugins || [];
this.config = config || {};
this.id = trigger.id;
};
Object.assign(Hook.prototype, {
addEvents: function(){},
constructor: Hook,
});
class Hook {
constructor(trigger, list, plugins, config) {
this.trigger = trigger;
this.list = new DropDown(list);
this.type = 'Hook';
this.event = 'click';
this.plugins = plugins || [];
this.config = config || {};
this.id = trigger.id;
}
}
export default Hook;
/* eslint-disable */
import Hook from './hook';
var HookButton = function(trigger, list, plugins, config) {
Hook.call(this, trigger, list, plugins, config);
this.type = 'button';
this.event = 'click';
class HookButton extends Hook {
constructor(trigger, list, plugins, config) {
super(trigger, list, plugins, config);
this.eventWrapper = {};
this.type = 'button';
this.event = 'click';
this.addEvents();
this.addPlugins();
};
this.eventWrapper = {};
HookButton.prototype = Object.create(Hook.prototype);
this.addEvents();
this.addPlugins();
}
Object.assign(HookButton.prototype, {
addPlugins: function() {
addPlugins() {
this.plugins.forEach(plugin => plugin.init(this));
},
}
clicked: function(e){
var buttonEvent = new CustomEvent('click.dl', {
clicked(e) {
const buttonEvent = new CustomEvent('click.dl', {
detail: {
hook: this,
},
bubbles: true,
cancelable: true
cancelable: true,
});
e.target.dispatchEvent(buttonEvent);
this.list.toggle();
},
}
addEvents: function(){
addEvents() {
this.eventWrapper.clicked = this.clicked.bind(this);
this.trigger.addEventListener('click', this.eventWrapper.clicked);
},
}
removeEvents: function(){
removeEvents() {
this.trigger.removeEventListener('click', this.eventWrapper.clicked);
},
}
restoreInitialState: function() {
restoreInitialState() {
this.list.list.innerHTML = this.list.initialState;
},
}
removePlugins: function() {
removePlugins() {
this.plugins.forEach(plugin => plugin.destroy());
},
}
destroy: function() {
destroy() {
this.restoreInitialState();
this.removeEvents();
this.removePlugins();
},
constructor: HookButton,
});
}
}
export default HookButton;
/* eslint-disable */
import Hook from './hook';
var HookInput = function(trigger, list, plugins, config) {
Hook.call(this, trigger, list, plugins, config);
class HookInput extends Hook {
constructor(trigger, list, plugins, config) {
super(trigger, list, plugins, config);
this.type = 'input';
this.event = 'input';
this.type = 'input';
this.event = 'input';
this.eventWrapper = {};
this.eventWrapper = {};
this.addEvents();
this.addPlugins();
};
this.addEvents();
this.addPlugins();
}
Object.assign(HookInput.prototype, {
addPlugins: function() {
addPlugins() {
this.plugins.forEach(plugin => plugin.init(this));
},
}
addEvents: function(){
addEvents() {
this.eventWrapper.mousedown = this.mousedown.bind(this);
this.eventWrapper.input = this.input.bind(this);
this.eventWrapper.keyup = this.keyup.bind(this);
......@@ -29,19 +27,19 @@ Object.assign(HookInput.prototype, {
this.trigger.addEventListener('input', this.eventWrapper.input);
this.trigger.addEventListener('keyup', this.eventWrapper.keyup);
this.trigger.addEventListener('keydown', this.eventWrapper.keydown);
},
}
removeEvents: function() {
removeEvents() {
this.hasRemovedEvents = true;
this.trigger.removeEventListener('mousedown', this.eventWrapper.mousedown);
this.trigger.removeEventListener('input', this.eventWrapper.input);
this.trigger.removeEventListener('keyup', this.eventWrapper.keyup);
this.trigger.removeEventListener('keydown', this.eventWrapper.keydown);
},
}
input: function(e) {
if(this.hasRemovedEvents) return;
input(e) {
if (this.hasRemovedEvents) return;
this.list.show();
......@@ -51,12 +49,12 @@ Object.assign(HookInput.prototype, {
text: e.target.value,
},
bubbles: true,
cancelable: true
cancelable: true,
});
e.target.dispatchEvent(inputEvent);
},
}
mousedown: function(e) {
mousedown(e) {
if (this.hasRemovedEvents) return;
const mouseEvent = new CustomEvent('mousedown.dl', {
......@@ -68,21 +66,21 @@ Object.assign(HookInput.prototype, {
cancelable: true,
});
e.target.dispatchEvent(mouseEvent);
},
}
keyup: function(e) {
keyup(e) {
if (this.hasRemovedEvents) return;
this.keyEvent(e, 'keyup.dl');
},
}
keydown: function(e) {
keydown(e) {
if (this.hasRemovedEvents) return;
this.keyEvent(e, 'keydown.dl');
},
}
keyEvent: function(e, eventName) {
keyEvent(e, eventName) {
this.list.show();
const keyEvent = new CustomEvent(eventName, {
......@@ -96,17 +94,17 @@ Object.assign(HookInput.prototype, {
cancelable: true,
});
e.target.dispatchEvent(keyEvent);
},
}
restoreInitialState: function() {
restoreInitialState() {
this.list.list.innerHTML = this.list.initialState;
},
}
removePlugins: function() {
removePlugins() {
this.plugins.forEach(plugin => plugin.destroy());
},
}
destroy: function() {
destroy() {
this.restoreInitialState();
this.removeEvents();
......@@ -114,6 +112,6 @@ Object.assign(HookInput.prototype, {
this.list.destroy();
}
});
}
export default HookInput;
/* eslint-disable */
import AjaxCache from '~/lib/utils/ajax_cache';
const Ajax = {
_loadUrlData: function _loadUrlData(url) {
var self = this;
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if(xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
var data = JSON.parse(xhr.responseText);
self.cache[url] = data;
return resolve(data);
} else {
return reject([xhr.responseText, xhr.status]);
}
}
};
xhr.send();
});
},
_loadData: function _loadData(data, config, self) {
if (config.loadingTemplate) {
var dataLoadingTemplate = self.hook.list.list.querySelector('[data-loading-template]');
......@@ -31,7 +14,6 @@ const Ajax = {
init: function init(hook) {
var self = this;
self.destroyed = false;
self.cache = self.cache || {};
var config = hook.config.Ajax;
this.hook = hook;
if (!config || !config.endpoint || !config.method) {
......@@ -48,14 +30,10 @@ const Ajax = {
this.listTemplate = dynamicList.outerHTML;
dynamicList.outerHTML = loadingTemplate.outerHTML;
}
if (self.cache[config.endpoint]) {
self._loadData(self.cache[config.endpoint], config, self);
} else {
this._loadUrlData(config.endpoint)
.then(function(d) {
self._loadData(d, config, self);
}, config.onError).catch(config.onError);
}
AjaxCache.retrieve(config.endpoint)
.then((data) => self._loadData(data, config, self))
.catch(config.onError);
},
destroy: function() {
this.destroyed = true;
......
This diff is collapsed.
<script>
/* global Flash */
import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from './environments_table.vue';
import environmentTable from './environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
import TablePaginationComponent from '../../vue_shared/components/table_pagination';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import '../../lib/utils/common_utils';
import eventHub from '../event_hub';
export default {
components: {
'environment-table': EnvironmentTable,
'table-pagination': TablePaginationComponent,
environmentTable,
tablePagination,
loadingIcon,
},
data() {
......@@ -186,14 +188,11 @@ export default {
</div>
<div class="content-list environments-container">
<div
class="environments-list-loading text-center"
v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
<loading-icon
label="Loading environments"
size="3"
v-if="isLoading"
/>
<div
class="blank-state blank-state-no-icon"
......
<script>
import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
......@@ -11,6 +12,10 @@ export default {
},
},
components: {
loadingIcon,
},
data() {
return {
playIconSvg,
......@@ -61,10 +66,7 @@ export default {
<i
class="fa fa-caret-down"
aria-hidden="true"/>
<i
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true"/>
<loading-icon v-if="isLoading" />
</span>
</button>
......
<script>
import Timeago from 'timeago.js';
import _ from 'underscore';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import '../../lib/utils/text_utility';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
......@@ -19,6 +21,7 @@ const timeagoInstance = new Timeago();
export default {
components: {
userAvatarLink,
'commit-component': CommitComponent,
'actions-component': ActionsComponent,
'external-url-component': ExternalUrlComponent,
......@@ -59,7 +62,7 @@ export default {
hasLastDeploymentKey() {
if (this.model &&
this.model.last_deployment &&
!this.$options.isObjectEmpty(this.model.last_deployment)) {
!_.isEmpty(this.model.last_deployment)) {
return true;
}
return false;
......@@ -310,8 +313,8 @@ export default {
*/
deploymentHasUser() {
return this.model &&
!this.$options.isObjectEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.user);
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user);
},
/**
......@@ -322,8 +325,8 @@ export default {
*/
deploymentUser() {
if (this.model &&
!this.$options.isObjectEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.user)) {
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)) {
return this.model.last_deployment.user;
}
return {};
......@@ -338,8 +341,8 @@ export default {
*/
shouldRenderBuildName() {
return !this.model.isFolder &&
!this.$options.isObjectEmpty(this.model.last_deployment) &&
!this.$options.isObjectEmpty(this.model.last_deployment.deployable);
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.deployable);
},
/**
......@@ -380,7 +383,7 @@ export default {
*/
shouldRenderDeploymentID() {
return !this.model.isFolder &&
!this.$options.isObjectEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined;
},
......@@ -410,21 +413,6 @@ export default {
},
},
/**
* Helper to verify if certain given object are empty.
* Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty
* @param {Object} object
* @returns {Bollean}
*/
isObjectEmpty(object) {
for (const key in object) { // eslint-disable-line
if (hasOwnProperty.call(object, key)) {
return false;
}
}
return true;
},
methods: {
onClickFolder() {
eventHub.$emit('toggleFolder', this.model, this.folderUrl);
......@@ -482,15 +470,13 @@ export default {
<span v-if="!model.isFolder && deploymentHasUser">
by
<a
:href="deploymentUser.web_url"
class="js-deploy-user-container">
<img
class="avatar has-tooltip s20"
:src="deploymentUser.avatar_url"
:alt="userImageAltDescription"
:title="deploymentUser.username" />
</a>
<user-avatar-link
class="js-deploy-user-container"
:link-href="deploymentUser.web_url"
:img-src="deploymentUser.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="deploymentUser.username"
/>
</span>
</td>
......
......@@ -21,7 +21,6 @@ export default {
<a
class="btn monitoring-url has-tooltip"
data-container="body"
target="_blank"
rel="noopener noreferrer nofollow"
:href="monitoringUrl"
:title="title"
......
......@@ -6,6 +6,7 @@
* Makes a post request when the button is clicked.
*/
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
......@@ -20,6 +21,10 @@ export default {
},
},
components: {
loadingIcon,
},
data() {
return {
isLoading: false,
......@@ -49,9 +54,6 @@ export default {
Rollback
</span>
<i
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
<loading-icon v-if="isLoading" />
</button>
</template>
......@@ -4,6 +4,7 @@
* Used in environments table.
*/
import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
......@@ -19,6 +20,10 @@ export default {
};
},
components: {
loadingIcon,
},
computed: {
title() {
return 'Stop';
......@@ -51,9 +56,6 @@ export default {
<i
class="fa fa-stop stop-env-icon"
aria-hidden="true" />
<i
v-if="isLoading"
class="fa fa-spinner fa-spin"
aria-hidden="true" />
<loading-icon v-if="isLoading" />
</button>
</template>
......@@ -3,10 +3,12 @@
* Render environments table.
*/
import EnvironmentTableRowComponent from './environment_item.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
'environment-item': EnvironmentTableRowComponent,
loadingIcon,
},
props: {
......@@ -77,10 +79,8 @@ export default {
<template v-if="model.isFolder && model.isOpen && model.children && model.children.length > 0">
<tr v-if="isLoadingFolderContent">
<td colspan="6" class="text-center">
<i
class="fa fa-spin fa-spinner fa-2x"
aria-hidden="true" />
<td colspan="6">
<loading-icon size="2" />
</td>
</tr>
......
<script>
/* global Flash */
import EnvironmentsService from '../services/environments_service';
import EnvironmentTable from '../components/environments_table.vue';
import environmentTable from '../components/environments_table.vue';
import EnvironmentsStore from '../stores/environments_store';
import TablePaginationComponent from '../../vue_shared/components/table_pagination';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
export default {
components: {
'environment-table': EnvironmentTable,
'table-pagination': TablePaginationComponent,
environmentTable,
tablePagination,
loadingIcon,
},
data() {
......@@ -153,13 +155,12 @@ export default {
</div>
<div class="environments-container">
<div
class="environments-list-loading text-center"
v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"/>
</div>
<loading-icon
label="Loading environments"
v-if="isLoading"
size="3"
/>
<div
class="table-holder"
......
......@@ -3,7 +3,6 @@
/* global notes */
let $commentButtonTemplate;
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
window.FilesCommentButton = (function() {
var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
......@@ -27,8 +26,8 @@ window.FilesCommentButton = (function() {
TEXT_FILE_SELECTOR = '.text-file';
function FilesCommentButton(filesContainerElement) {
this.render = bind(this.render, this);
this.hideButton = bind(this.hideButton, this);
this.render = this.render.bind(this);
this.hideButton = this.hideButton.bind(this);
this.isParallelView = notes.isParallelView();
filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
......
import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown');
import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) {
......
......@@ -2,8 +2,7 @@
import Ajax from '~/droplab/plugins/ajax';
import Filter from '~/droplab/plugins/filter';
require('./filtered_search_dropdown');
import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter, endpoint, symbol) {
......
/* global Flash */
import AjaxFilter from '~/droplab/plugins/ajax_filter';
require('./filtered_search_dropdown');
import './filtered_search_dropdown';
class DropdownUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) {
......
require('./dropdown_hint');
require('./dropdown_non_user');
require('./dropdown_user');
require('./dropdown_utils');
require('./filtered_search_dropdown_manager');
require('./filtered_search_dropdown');
require('./filtered_search_manager');
require('./filtered_search_token_keys');
require('./filtered_search_tokenizer');
require('./filtered_search_visual_tokens');
import './dropdown_hint';
import './dropdown_non_user';
import './dropdown_user';
import './dropdown_utils';
import './filtered_search_dropdown_manager';
import './filtered_search_dropdown';
import './filtered_search_manager';
import './filtered_search_token_keys';
import './filtered_search_tokenizer';
import './filtered_search_visual_tokens';
......@@ -16,10 +16,14 @@ class FilteredSearchManager {
this.recentSearchesStore = new RecentSearchesStore({
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
});
let recentSearchesKey = 'issue-recent-searches';
const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
const projectPath = searchHistoryDropdownElement ?
searchHistoryDropdownElement.dataset.projectFullPath : 'project';
let recentSearchesPagePrefix = 'issue-recent-searches';
if (page === 'merge_requests') {
recentSearchesKey = 'merge-request-recent-searches';
recentSearchesPagePrefix = 'merge-request-recent-searches';
}
const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
// Fetch recent searches from localStorage
......@@ -47,7 +51,7 @@ class FilteredSearchManager {
this.recentSearchesRoot = new RecentSearchesRoot(
this.recentSearchesStore,
this.recentSearchesService,
document.querySelector('.js-filtered-search-history-dropdown'),
searchHistoryDropdownElement,
);
this.recentSearchesRoot.init();
......
require('./filtered_search_token_keys');
import './filtered_search_token_keys';
class FilteredSearchTokenizer {
static processTokens(input) {
......
......@@ -3,6 +3,7 @@ import _ from 'underscore';
class RecentSearchesStore {
constructor(initialState = {}) {
this.state = Object.assign({
isLocalStorageAvailable: true,
recentSearches: [],
}, initialState);
}
......
This diff is collapsed.
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, prefer-rest-params, max-len, vars-on-top, wrap-iife, no-unused-vars, quotes, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, comma-dangle, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func, no-mixed-operators */
/* global fuzzaldrinPlus */
import { isObject } from './lib/utils/type_utility';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote,
bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i += 1) { if (i in this && this[i] === item) return i; } return -1; };
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote;
GitLabDropdownFilter = (function() {
var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS;
......@@ -95,7 +94,7 @@ GitLabDropdownFilter = (function() {
// { prop: 'def' }
// ]
// }
if (gl.utils.isObject(data)) {
if (isObject(data)) {
results = {};
for (key in data) {
group = data[key];
......@@ -213,10 +212,10 @@ GitLabDropdown = (function() {
var searchFields, selector, self;
this.el = el1;
this.options = options;
this.updateLabel = bind(this.updateLabel, this);
this.hidden = bind(this.hidden, this);
this.opened = bind(this.opened, this);
this.shouldPropagate = bind(this.shouldPropagate, this);
this.updateLabel = this.updateLabel.bind(this);
this.hidden = this.hidden.bind(this);
this.opened = this.opened.bind(this);
this.shouldPropagate = this.shouldPropagate.bind(this);
self = this;
selector = $(this.el).data("target");
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
......@@ -398,7 +397,7 @@ GitLabDropdown = (function() {
html = [this.noResults()];
} else {
// Handle array groups
if (gl.utils.isObject(data)) {
if (isObject(data)) {
html = [];
for (name in data) {
groupData = data[name];
......@@ -610,7 +609,12 @@ GitLabDropdown = (function() {
var link = document.createElement('a');
link.href = url;
link.innerHTML = text;
if (this.highlight) {
link.innerHTML = text;
} else {
link.textContent = text;
}
if (selected) {
link.className = 'is-active';
......@@ -627,8 +631,8 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.highlightTextMatches = function(text, term) {
var occurrences;
occurrences = fuzzaldrinPlus.match(text, term);
const occurrences = fuzzaldrinPlus.match(text, term);
const indexOf = [].indexOf;
return text.split('').map(function(character, i) {
if (indexOf.call(occurrences, i) !== -1) {
return "<b>" + character + "</b>";
......
......@@ -65,6 +65,7 @@ class GlFieldError {
this.state = {
valid: false,
empty: true,
submitted: false,
};
this.initFieldValidation();
......@@ -108,9 +109,10 @@ class GlFieldError {
const currentValue = this.accessCurrentValue();
this.state.valid = false;
this.state.empty = currentValue === '';
this.state.submitted = true;
this.renderValidity();
this.form.focusOnFirstInvalid.apply(this.form);
// For UX, wait til after first invalid submission to check each keyup
this.inputElement.off('keyup.fieldValidator')
.on('keyup.fieldValidator', this.updateValidity.bind(this));
......
/* eslint-disable comma-dangle, class-methods-use-this, max-len, space-before-function-paren, arrow-parens, no-param-reassign */
require('./gl_field_error');
import './gl_field_error';
const customValidationFlag = 'gl-field-error-ignore';
......@@ -37,6 +37,15 @@ class GlFieldErrors {
}
}
/* Public method for triggering validity updates manually */
updateFormValidityState() {
this.state.inputs.forEach((field) => {
if (field.state.submitted) {
field.updateValidity();
}
});
}
focusOnFirstInvalid () {
const firstInvalid = this.state.inputs.filter((input) => !input.inputDomElement.validity.valid)[0];
firstInvalid.inputElement.focus();
......
......@@ -3,11 +3,14 @@
/* global DropzoneInput */
/* global autosize */
import GfmAutoComplete from './gfm_auto_complete';
window.gl = window.gl || {};
function GLForm(form) {
function GLForm(form, enableGFM = false) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
this.enableGFM = enableGFM;
// Before we start, we should clean up any previous data for this form
this.destroy();
// Setup the form
......@@ -30,8 +33,14 @@ GLForm.prototype.setupForm = function() {
this.form.addClass('gfm-form');
// remove notify commit author checkbox for non-commit notes
gl.utils.disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button, .js-note-new-discussion'));
gl.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup(this.form.find('.js-gfm-input'), {
emojis: true,
members: this.enableGFM,
issues: this.enableGFM,
milestones: this.enableGFM,
mergeRequests: this.enableGFM,
labels: this.enableGFM,
});
new DropzoneInput(this.form);
autosize(this.textarea);
}
......
......@@ -2,7 +2,6 @@
import d3 from 'd3';
const bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
const extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
const hasProp = {}.hasOwnProperty;
......@@ -95,7 +94,7 @@ export const ContributorsMasterGraph = (function(superClass) {
function ContributorsMasterGraph(data1) {
this.data = data1;
this.update_content = bind(this.update_content, this);
this.update_content = this.update_content.bind(this);
this.width = $('.content').width() - 70;
this.height = 200;
this.x = null;
......
......@@ -3,7 +3,7 @@
prefer-arrow-callback, comma-dangle, consistent-return, yoda,
prefer-rest-params, prefer-spread, no-unused-vars, prefer-template,
promise/catch-or-return */
/* global Api */
import Api from './api';
var slice = [].slice;
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
/* global UsersSelect */
/* global bp */
import Cookies from 'js-cookie';
import UsersSelect from './users_select';
(function() {
this.IssuableContext = (function() {
......@@ -47,7 +47,6 @@ import Cookies from 'js-cookie';
Cookies.set('collapsed_gutter', true);
}
});
$(".right-sidebar").niceScroll();
}
IssuableContext.prototype.initParticipants = function() {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-useless-escape, no-new, quotes, object-shorthand, no-unused-vars, comma-dangle, no-alert, consistent-return, no-else-return, prefer-template, one-var, one-var-declaration-per-line, curly, max-len */
/* global GitLab */
/* global UsersSelect */
/* global ZenMode */
/* global Autosave */
/* global dateFormat */
/* global Pikaday */
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
import UsersSelect from './users_select';
import GfmAutoComplete from './gfm_auto_complete';
(function() {
this.IssuableForm = (function() {
IssuableForm.prototype.issueMoveConfirmMsg = 'Are you sure you want to move this issue to another project?';
......@@ -17,11 +17,11 @@
function IssuableForm(form) {
var $issuableDueDate, calendar;
this.form = form;
this.toggleWip = bind(this.toggleWip, this);
this.renderWipExplanation = bind(this.renderWipExplanation, this);
this.resetAutosave = bind(this.resetAutosave, this);
this.handleSubmit = bind(this.handleSubmit, this);
gl.GfmAutoComplete.setup();
this.toggleWip = this.toggleWip.bind(this);
this.renderWipExplanation = this.renderWipExplanation.bind(this);
this.resetAutosave = this.resetAutosave.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
new UsersSelect();
new ZenMode();
this.titleField = this.form.find("input[name*='[title]']");
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
/* global Flash */
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
/* global Flash */
require('./flash');
require('~/lib/utils/text_utility');
require('vendor/jquery.waitforimages');
require('./task_list');
import 'vendor/jquery.waitforimages';
import '~/lib/utils/text_utility';
import './flash';
import './task_list';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
class Issue {
constructor() {
......
export default (newStateData, tasks) => {
const $tasks = $('#task_status');
const $tasksShort = $('#task_status_short');
const $issueableHeader = $('.issuable-header');
const tasksStates = { newState: null, currentState: null };
if ($tasks.length === 0) {
if (!(newStateData.task_status.indexOf('0 of 0') === 0)) {
$issueableHeader.append(`<span id="task_status">${newStateData.task_status}</span>`);
} else {
$issueableHeader.append('<span id="task_status"></span>');
}
} else {
tasksStates.newState = newStateData.task_status.indexOf('0 of 0') === 0;
tasksStates.currentState = tasks.indexOf('0 of 0') === 0;
}
if ($tasks.length !== 0 && !tasksStates.newState) {
$tasks.text(newStateData.task_status);
$tasksShort.text(newStateData.task_status);
} else if (tasksStates.currentState) {
$issueableHeader.append(`<span id="task_status">${newStateData.task_status}</span>`);
} else if (tasksStates.newState) {
$tasks.remove();
$tasksShort.remove();
}
};
<script>
import Visibility from 'visibilityjs';
import Poll from '../../lib/utils/poll';
import Service from '../services/index';
import Store from '../stores';
import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
export default {
props: {
endpoint: {
required: true,
type: String,
},
canUpdate: {
required: true,
type: Boolean,
},
issuableRef: {
type: String,
required: true,
},
initialTitle: {
type: String,
required: true,
},
initialDescriptionHtml: {
type: String,
required: false,
default: '',
},
initialDescriptionText: {
type: String,
required: false,
default: '',
},
},
data() {
const store = new Store({
titleHtml: this.initialTitle,
descriptionHtml: this.initialDescriptionHtml,
descriptionText: this.initialDescriptionText,
});
return {
store,
state: store.state,
};
},
components: {
descriptionComponent,
titleComponent,
},
created() {
const resource = new Service(this.endpoint);
const poll = new Poll({
resource,
method: 'getData',
successCallback: (res) => {
this.store.updateState(res.json());
},
errorCallback(err) {
throw new Error(err);
},
});
if (!Visibility.hidden()) {
poll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
poll.restart();
} else {
poll.stop();
}
});
},
};
</script>
<template>
<div>
<title-component
:issuable-ref="issuableRef"
:title-html="state.titleHtml"
:title-text="state.titleText" />
<description-component
v-if="state.descriptionHtml"
:can-update="canUpdate"
:description-html="state.descriptionHtml"
:description-text="state.descriptionText"
:updated-at="state.updatedAt"
:task-status="state.taskStatus" />
</div>
</template>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export default {
methods: {
animateChange() {
this.preAnimation = true;
this.pulseAnimation = false;
this.$nextTick(() => {
this.preAnimation = false;
this.pulseAnimation = true;
});
},
},
};
import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
export default class Service {
constructor(resource, endpoint) {
this.resource = resource;
constructor(endpoint) {
this.endpoint = endpoint;
this.resource = Vue.resource(this.endpoint);
}
getTitle() {
return this.resource.get(this.endpoint);
getData() {
return this.resource.get();
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment