Commit 7767ceef authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into ide

* master: (177 commits)
  Add changelog
  Bump gitlab-shell version to 5.8.0 to fix Git for Windows 2.14
  Make contextual sidebar collapsible
  Fixed sidebar context header hover colors
  Use correct `Environment`-class within `Gitlab` namespace
  Remove gl.Activities from Commits page
  Move `let` calls inside the `describe` block using them
  Add `/assign me` alias support for assigning issuables to oneself
  GRPC::Unavailable (< GRPC::BadStatus) is wrapped in a CommandError
  Use `broken_storage` in the fs_shards_spec.
  Eager load project creators for project dashboards
  Memoize a user's personal projects count
  Remove redundant query from User#recent_push
  Improve checking if projects would be returned
  Change spelling of gitlab-shell
  Remove unused #tree-holder
  Add custom linter for inline JavaScript to haml_lint
  Rename user_can_admin? because it's more accurate
  Synchronous zanata community contribution translation
  Add Korean translation to i18n
  ...
parents 1d5a3065 b12107a0
...@@ -66,16 +66,18 @@ stages: ...@@ -66,16 +66,18 @@ stages:
- mysql:latest - mysql:latest
- redis:alpine - redis:alpine
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql .only-if-want-mysql: &only-if-want-mysql
only: only:
- /mysql/ - /mysql/
- /-stable/ - /-stable/
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
- tags@gitlab-org/gitlab-ce - tags@gitlab-org/gitlab-ce
- tags@gitlab-org/gitlab-ee
- tags@gitlab/gitlabhq - tags@gitlab/gitlabhq
- //@gitlab-org/gitlab-ee - tags@gitlab/gitlab-ee
- //@gitlab/gitlab-ee
# Skip all jobs except the ones that begin with 'docs/'. # Skip all jobs except the ones that begin with 'docs/'.
# Used for commits including ONLY documentation changes. # Used for commits including ONLY documentation changes.
...@@ -114,7 +116,7 @@ stages: ...@@ -114,7 +116,7 @@ stages:
.rspec-knapsack-mysql: &rspec-knapsack-mysql .rspec-knapsack-mysql: &rspec-knapsack-mysql
<<: *rspec-knapsack <<: *rspec-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
...@@ -146,7 +148,7 @@ stages: ...@@ -146,7 +148,7 @@ stages:
.spinach-knapsack-mysql: &spinach-knapsack-mysql .spinach-knapsack-mysql: &spinach-knapsack-mysql
<<: *spinach-knapsack <<: *spinach-knapsack
<<: *use-mysql <<: *use-mysql
<<: *only-master-and-ee-or-mysql <<: *only-if-want-mysql
<<: *except-docs <<: *except-docs
.only-canonical-masters: &only-canonical-masters .only-canonical-masters: &only-canonical-masters
......
...@@ -35,9 +35,21 @@ linters: ...@@ -35,9 +35,21 @@ linters:
HtmlAttributes: HtmlAttributes:
enabled: true enabled: true
IdNames:
enabled: false
ImplicitDiv: ImplicitDiv:
enabled: true enabled: true
InlineJavaScript:
enabled: true
InlineStyles:
enabled: false
InstanceVariables:
enabled: false
LeadingCommentSpace: LeadingCommentSpace:
enabled: false enabled: false
...@@ -54,6 +66,9 @@ linters: ...@@ -54,6 +66,9 @@ linters:
ObjectReferenceAttributes: ObjectReferenceAttributes:
enabled: true enabled: true
RepeatedId:
enabled: false
RuboCop: RuboCop:
enabled: false enabled: false
# These cops are incredibly noisy when it comes to HAML templates, so we # These cops are incredibly noisy when it comes to HAML templates, so we
...@@ -101,3 +116,6 @@ linters: ...@@ -101,3 +116,6 @@ linters:
UnnecessaryStringOutput: UnnecessaryStringOutput:
enabled: true enabled: true
ViewLength:
enabled: false
...@@ -1708,8 +1708,6 @@ entry. ...@@ -1708,8 +1708,6 @@ entry.
## 8.16.7 (2017-02-27) ## 8.16.7 (2017-02-27)
- No changes.
- No changes.
- Fix MR changes tab size count when there are over 100 files in the diff. - Fix MR changes tab size count when there are over 100 files in the diff.
## 8.16.6 (2017-02-17) ## 8.16.6 (2017-02-17)
...@@ -1923,6 +1921,14 @@ entry. ...@@ -1923,6 +1921,14 @@ entry.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support. - Patch XSS vulnerability in RDOC support.
## 8.15.5 (2017-01-20)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.15.4 (2017-01-09) ## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176 - Make successful pipeline emails off for watchers. !8176
...@@ -2205,6 +2211,14 @@ entry. ...@@ -2205,6 +2211,14 @@ entry.
- Speed up group milestone index by passing group_id to IssuesFinder. !8363 - Speed up group milestone index by passing group_id to IssuesFinder. !8363
- Ensure issuable state changes only fire webhooks once. - Ensure issuable state changes only fire webhooks once.
## 8.14.7 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.14.6 (2017-01-10) ## 8.14.6 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509 - Update the gitlab-markup gem to the version 1.5.1. !8509
...@@ -2487,6 +2501,14 @@ entry. ...@@ -2487,6 +2501,14 @@ entry.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page - Fix 404 when visit /projects page
## 8.13.12 (2017-01-21)
- Ensure export files are removed after a namespace is deleted.
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
- Prevent users from creating notes on resources they can't access.
- Prevent users from deleting system deploy keys via the project deploy key API.
- Upgrade omniauth gem to 1.3.2.
## 8.13.11 (2017-01-10) ## 8.13.11 (2017-01-10)
- Update the gitlab-markup gem to the version 1.5.1. !8509 - Update the gitlab-markup gem to the version 1.5.1. !8509
......
...@@ -314,11 +314,11 @@ group :development, :test do ...@@ -314,11 +314,11 @@ group :development, :test do
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.2.0', require: false
gem 'fuubar', '~> 2.0.0' gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.5.0' gem 'database_cleaner', '~> 1.5.0'
gem 'factory_girl_rails', '~> 4.7.0' gem 'factory_girl_rails', '~> 4.7.0'
gem 'rspec-rails', '~> 3.5.0' gem 'rspec-rails', '~> 3.6.0'
gem 'rspec-retry', '~> 0.4.5' gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
...@@ -342,7 +342,7 @@ group :development, :test do ...@@ -342,7 +342,7 @@ group :development, :test do
gem 'rubocop', '~> 0.49.1', require: false gem 'rubocop', '~> 0.49.1', require: false
gem 'rubocop-rspec', '~> 1.15.1', require: false gem 'rubocop-rspec', '~> 1.15.1', require: false
gem 'scss_lint', '~> 0.54.0', require: false gem 'scss_lint', '~> 0.54.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false gem 'haml_lint', '~> 0.26.0', require: false
gem 'simplecov', '~> 0.14.0', require: false gem 'simplecov', '~> 0.14.0', require: false
gem 'flay', '~> 2.8.0', require: false gem 'flay', '~> 2.8.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
...@@ -391,7 +391,7 @@ gem 'vmstat', '~> 2.3.0' ...@@ -391,7 +391,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.21.0' gem 'gitaly', '~> 0.24.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -156,7 +156,7 @@ GEM ...@@ -156,7 +156,7 @@ GEM
devise (~> 4.0) devise (~> 4.0)
railties railties
rotp (~> 2.0) rotp (~> 2.0)
diff-lcs (1.2.5) diff-lcs (1.3)
diffy (3.1.0) diffy (3.1.0)
docile (1.1.5) docile (1.1.5)
domain_name (0.5.20161021) domain_name (0.5.20161021)
...@@ -250,8 +250,8 @@ GEM ...@@ -250,8 +250,8 @@ GEM
foreman (0.78.0) foreman (0.78.0)
thor (~> 0.19.1) thor (~> 0.19.1)
formatador (0.2.5) formatador (0.2.5)
fuubar (2.0.0) fuubar (2.2.0)
rspec (~> 3.0) rspec-core (~> 3.0)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
gemnasium-gitlab-service (0.2.6) gemnasium-gitlab-service (0.2.6)
rugged (~> 0.21) rugged (~> 0.21)
...@@ -269,7 +269,7 @@ GEM ...@@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.21.0) gitaly (0.24.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -356,10 +356,11 @@ GEM ...@@ -356,10 +356,11 @@ GEM
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
haml (4.0.7) haml (4.0.7)
tilt tilt
haml_lint (0.21.0) haml_lint (0.26.0)
haml (~> 4.0) haml (>= 4.0, < 5.1)
rainbow
rake (>= 10, < 13) rake (>= 10, < 13)
rubocop (>= 0.47.0) rubocop (>= 0.49.0)
sysexits (~> 1.1) sysexits (~> 1.1)
hamlit (2.6.1) hamlit (2.6.1)
temple (~> 0.7.6) temple (~> 0.7.6)
...@@ -393,7 +394,7 @@ GEM ...@@ -393,7 +394,7 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpclient (2.8.2) httpclient (2.8.2)
i18n (0.8.1) i18n (0.8.6)
ice_nine (0.11.2) ice_nine (0.11.2)
influxdb (0.2.3) influxdb (0.2.3)
cause cause
...@@ -610,7 +611,7 @@ GEM ...@@ -610,7 +611,7 @@ GEM
pry-rails (0.3.5) pry-rails (0.3.5)
pry (>= 0.9.10) pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
rack (1.6.5) rack (1.6.8)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.4.1) rack-attack (4.4.1)
...@@ -658,7 +659,7 @@ GEM ...@@ -658,7 +659,7 @@ GEM
rainbow (2.2.2) rainbow (2.2.2)
rake rake
raindrops (0.18.0) raindrops (0.18.0)
rake (10.5.0) rake (12.0.0)
rblineprof (0.3.6) rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2) rdoc (4.2.2)
...@@ -702,30 +703,26 @@ GEM ...@@ -702,30 +703,26 @@ GEM
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
rspec (3.5.0) rspec-core (3.6.0)
rspec-core (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-expectations (~> 3.5.0) rspec-expectations (3.6.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.0)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-mocks (3.5.0) rspec-mocks (3.6.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-rails (3.5.0) rspec-rails (3.6.0)
actionpack (>= 3.0) actionpack (>= 3.0)
activesupport (>= 3.0) activesupport (>= 3.0)
railties (>= 3.0) railties (>= 3.0)
rspec-core (~> 3.5.0) rspec-core (~> 3.6.0)
rspec-expectations (~> 3.5.0) rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.5.0) rspec-mocks (~> 3.6.0)
rspec-support (~> 3.5.0) rspec-support (~> 3.6.0)
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-set (0.1.3) rspec-set (0.1.3)
rspec-support (3.5.0) rspec-support (3.6.0)
rspec_profiling (0.0.5) rspec_profiling (0.0.5)
activerecord activerecord
pg pg
...@@ -860,7 +857,7 @@ GEM ...@@ -860,7 +857,7 @@ GEM
truncato (0.7.8) truncato (0.7.8)
htmlentities (~> 4.3.1) htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1) nokogiri (~> 1.6.1)
tzinfo (1.2.2) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1) u2f (0.2.1)
uglifier (2.7.2) uglifier (2.7.2)
...@@ -972,13 +969,13 @@ DEPENDENCIES ...@@ -972,13 +969,13 @@ DEPENDENCIES
fog-rackspace (~> 0.1.1) fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7) font-awesome-rails (~> 4.7)
foreman (~> 0.78.0) foreman (~> 0.78.0)
fuubar (~> 2.0.0) fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0) gemojione (~> 3.0)
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.21.0) gitaly (~> 0.24.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -991,7 +988,7 @@ DEPENDENCIES ...@@ -991,7 +988,7 @@ DEPENDENCIES
grape (~> 0.19.2) grape (~> 0.19.2)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.0.0) grape-route-helpers (~> 2.0.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.26.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
hashie-forbidden_attributes hashie-forbidden_attributes
health_check (~> 2.6.0) health_check (~> 2.6.0)
...@@ -1076,7 +1073,7 @@ DEPENDENCIES ...@@ -1076,7 +1073,7 @@ DEPENDENCIES
responders (~> 2.0) responders (~> 2.0)
rouge (~> 2.0) rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.6.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3) rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5) rspec_profiling (~> 0.0.5)
...@@ -1130,4 +1127,4 @@ DEPENDENCIES ...@@ -1130,4 +1127,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.15.1 1.15.3
...@@ -10,7 +10,7 @@ class AjaxLoadingSpinner { ...@@ -10,7 +10,7 @@ class AjaxLoadingSpinner {
e.target.setAttribute('disabled', ''); e.target.setAttribute('disabled', '');
const iconElement = e.target.querySelector('i'); const iconElement = e.target.querySelector('i');
// get first fa- icon // get first fa- icon
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first(); const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g)[0];
iconElement.dataset.icon = originalIcon; iconElement.dataset.icon = originalIcon;
AjaxLoadingSpinner.toggleLoadingIcon(iconElement); AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend); $(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
......
/* eslint-disable class-methods-use-this */ /* eslint-disable class-methods-use-this */
/* global Flash */ /* global Flash */
import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd'; const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
......
import _ from 'underscore';
import '../commons/bootstrap'; import '../commons/bootstrap';
// Requires Input behavior // Requires Input behavior
...@@ -48,7 +49,9 @@ function hideOrShowHelpBlock(form) { ...@@ -48,7 +49,9 @@ function hideOrShowHelpBlock(form) {
$(() => { $(() => {
const $form = $('form.js-requires-input'); const $form = $('form.js-requires-input');
if ($form) {
$form.requiresInput(); $form.requiresInput();
hideOrShowHelpBlock($form); hideOrShowHelpBlock($form);
$('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form)); $('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
}
}); });
// Toggle button. Show/hide content inside parent container. // Toggle button. Show/hide content inside parent container.
// Button does not change visibility. If button has icon - it changes chevron style. // Button does not change visibility. If button has icon - it changes chevron style.
// //
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* global BoardService */ /* global BoardService */
/* global Flash */ /* global Flash */
import _ from 'underscore';
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import VueResource from 'vue-resource';
import FilteredSearchBoards from './filtered_search_boards'; import FilteredSearchBoards from './filtered_search_boards';
......
/* global ListLabel */ /* global ListLabel */
import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
......
/* global ListIssue */ /* global ListIssue */
import Vue from 'vue'; import Vue from 'vue';
import queryData from '../../utils/query_data'; import queryData from '~/boards/utils/query_data';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import './header'; import './header';
import './list'; import './list';
import './footer'; import './footer';
......
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var, /* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var,
promise/catch-or-return */ promise/catch-or-return */
import _ from 'underscore';
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {}; window.gl.issueBoards = window.gl.issueBoards || {};
......
/* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */ /* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */
/* global List */ /* global List */
import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
window.gl = window.gl || {}; window.gl = window.gl || {};
......
...@@ -9,6 +9,7 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/tab'; ...@@ -9,6 +9,7 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/tab';
import 'bootstrap-sass/assets/javascripts/bootstrap/transition'; import 'bootstrap-sass/assets/javascripts/bootstrap/transition';
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
import 'bootstrap-sass/assets/javascripts/bootstrap/button';
// custom jQuery functions // custom jQuery functions
$.fn.extend({ $.fn.extend({
......
import 'underscore';
import './polyfills'; import './polyfills';
import './jquery'; import './jquery';
import './bootstrap'; import './bootstrap';
/* 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 */ /* 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 */
import _ from 'underscore';
import './lib/utils/common_utils'; import './lib/utils/common_utils';
import { placeholderImage } from './lazy_loader'; import { placeholderImage } from './lazy_loader';
......
...@@ -92,7 +92,7 @@ $(() => { ...@@ -92,7 +92,7 @@ $(() => {
}); });
}, },
selectDefaultStage() { selectDefaultStage() {
const stage = this.state.stages.first(); const stage = this.state.stages[0];
this.selectStage(stage); this.selectStage(stage);
}, },
selectStage(stage) { selectStage(stage) {
......
...@@ -94,7 +94,7 @@ const JumpToDiscussion = Vue.extend({ ...@@ -94,7 +94,7 @@ const JumpToDiscussion = Vue.extend({
hasDiscussionsToJumpTo = false; hasDiscussionsToJumpTo = false;
} }
} }
} else if (activeTab !== 'notes') { } else if (activeTab !== 'show') {
// If we are on the commits or builds tabs, // If we are on the commits or builds tabs,
// there are no discussions to jump to. // there are no discussions to jump to.
hasDiscussionsToJumpTo = false; hasDiscussionsToJumpTo = false;
...@@ -103,12 +103,12 @@ const JumpToDiscussion = Vue.extend({ ...@@ -103,12 +103,12 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) { if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page, // If there are no discussions to jump to on the current page,
// switch to the notes tab and jump to the first disucssion there. // switch to the notes tab and jump to the first disucssion there.
window.mrTabs.activateTab('notes'); window.mrTabs.activateTab('show');
activeTab = 'notes'; activeTab = 'show';
jumpToFirstDiscussion = true; jumpToFirstDiscussion = true;
} }
if (activeTab === 'notes') { if (activeTab === 'show') {
discussionsSelector = '.discussion[data-discussion-id]'; discussionsSelector = '.discussion[data-discussion-id]';
discussionIdsInScope = discussionIdsForElements($(discussionsSelector)); discussionIdsInScope = discussionIdsForElements($(discussionsSelector));
} }
...@@ -156,7 +156,7 @@ const JumpToDiscussion = Vue.extend({ ...@@ -156,7 +156,7 @@ const JumpToDiscussion = Vue.extend({
let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`); let $target = $(`${discussionsSelector}[data-discussion-id="${nextUnresolvedDiscussionId}"]`);
if (activeTab === 'notes') { if (activeTab === 'show') {
$target = $target.closest('.note-discussion'); $target = $target.closest('.note-discussion');
// If the next discussion is closed, toggle it open. // If the next discussion is closed, toggle it open.
......
...@@ -80,10 +80,6 @@ import UserFeatureHelper from './helpers/user_feature_helper'; ...@@ -80,10 +80,6 @@ import UserFeatureHelper from './helpers/user_feature_helper';
(function() { (function() {
var Dispatcher; var Dispatcher;
$(function() {
return new Dispatcher();
});
Dispatcher = (function() { Dispatcher = (function() {
function Dispatcher() { function Dispatcher() {
this.initSearch(); this.initSearch();
...@@ -338,7 +334,6 @@ import UserFeatureHelper from './helpers/user_feature_helper'; ...@@ -338,7 +334,6 @@ import UserFeatureHelper from './helpers/user_feature_helper';
break; break;
case 'projects:commits:show': case 'projects:commits:show':
CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit); CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
new gl.Activities();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
GpgBadges.fetch(); GpgBadges.fetch();
break; break;
...@@ -351,6 +346,8 @@ import UserFeatureHelper from './helpers/user_feature_helper'; ...@@ -351,6 +346,8 @@ import UserFeatureHelper from './helpers/user_feature_helper';
break; break;
case 'projects:edit': case 'projects:edit':
setupProjectEdit(); setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
break; break;
case 'projects:imports:show': case 'projects:imports:show':
new ProjectImport(); new ProjectImport();
...@@ -511,7 +508,7 @@ import UserFeatureHelper from './helpers/user_feature_helper'; ...@@ -511,7 +508,7 @@ import UserFeatureHelper from './helpers/user_feature_helper';
new gl.DueDateSelectors(); new gl.DueDateSelectors();
break; break;
} }
switch (path.first()) { switch (path[0]) {
case 'sessions': case 'sessions':
case 'omniauth_callbacks': case 'omniauth_callbacks':
if (!gon.u2f) break; if (!gon.u2f) break;
...@@ -640,4 +637,8 @@ import UserFeatureHelper from './helpers/user_feature_helper'; ...@@ -640,4 +637,8 @@ import UserFeatureHelper from './helpers/user_feature_helper';
return Dispatcher; return Dispatcher;
})(); })();
$(function() {
new Dispatcher();
});
}).call(window); }).call(window);
/* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, max-len, one-var, no-var, one-var-declaration-per-line, no-unused-vars, camelcase, quotes, no-useless-concat, prefer-template, quote-props, comma-dangle, object-shorthand, consistent-return, prefer-arrow-callback */
/* global Dropzone */ /* global Dropzone */
import _ from 'underscore';
import './preview_markdown'; import './preview_markdown';
window.DropzoneInput = (function() { window.DropzoneInput = (function() {
function DropzoneInput(form) { function DropzoneInput(form) {
Dropzone.autoDiscover = false;
const divHover = '<div class="div-dropzone-hover"></div>'; const divHover = '<div class="div-dropzone-hover"></div>';
const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>'; const iconPaperclip = '<i class="fa fa-paperclip div-dropzone-icon"></i>';
const $attachButton = form.find('.button-attach-file'); const $attachButton = form.find('.button-attach-file');
...@@ -218,7 +217,7 @@ window.DropzoneInput = (function() { ...@@ -218,7 +217,7 @@ window.DropzoneInput = (function() {
value = e.clipboardData.getData('text/plain'); value = e.clipboardData.getData('text/plain');
} }
value = value.split("\r"); value = value.split("\r");
return value.first(); return value[0];
}; };
const showSpinner = function(e) { const showSpinner = function(e) {
......
import _ from 'underscore';
import emojiMap from 'emojis/digests.json'; import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json'; import emojiAliases from 'emojis/aliases.json';
......
// TODO: remove this
// eslint-disable-next-line no-extend-native
Array.prototype.first = function first() {
return this[0];
};
// eslint-disable-next-line no-extend-native
Array.prototype.last = function last() {
return this[this.length - 1];
};
import _ from 'underscore';
/** /**
* Makes search request for content when user types a value in the search input. * Makes search request for content when user types a value in the search input.
* Updates the html content of the page with the received one. * Updates the html content of the page with the received one.
......
import _ from 'underscore';
import FilteredSearchContainer from './container'; import FilteredSearchContainer from './container';
class DropdownUtils { class DropdownUtils {
...@@ -122,11 +123,11 @@ class DropdownUtils { ...@@ -122,11 +123,11 @@ class DropdownUtils {
if (!allowMultiple && itemInExistingTokens) { if (!allowMultiple && itemInExistingTokens) {
updatedItem.droplab_hidden = true; updatedItem.droplab_hidden = true;
} else if (!lastKey || searchInput.split('').last() === ' ') { } else if (!lastKey || _.last(searchInput.split('')) === ' ') {
updatedItem.droplab_hidden = false; updatedItem.droplab_hidden = false;
} else if (lastKey) { } else if (lastKey) {
const split = lastKey.split(':'); const split = lastKey.split(':');
const tokenName = split[0].split(' ').last(); const tokenName = _.last(split[0].split(' '));
const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1; const match = updatedItem.hint.indexOf(tokenName.toLowerCase()) === -1;
updatedItem.droplab_hidden = tokenName ? match : false; updatedItem.droplab_hidden = tokenName ? match : false;
......
...@@ -167,7 +167,7 @@ class FilteredSearchDropdownManager { ...@@ -167,7 +167,7 @@ class FilteredSearchDropdownManager {
// Eg. token = 'label:' // Eg. token = 'label:'
const split = lastToken.split(':'); const split = lastToken.split(':');
const dropdownName = split[0].split(' ').last(); const dropdownName = _.last(split[0].split(' '));
this.loadDropdown(split.length > 1 ? dropdownName : ''); this.loadDropdown(split.length > 1 ? dropdownName : '');
} else if (lastToken) { } else if (lastToken) {
// Token has been initialized into an object because it has a value // Token has been initialized into an object because it has a value
......
...@@ -367,7 +367,7 @@ class FilteredSearchManager { ...@@ -367,7 +367,7 @@ class FilteredSearchManager {
const fragments = searchToken.split(':'); const fragments = searchToken.split(':');
if (fragments.length > 1) { if (fragments.length > 1) {
const inputValues = fragments[0].split(' '); const inputValues = fragments[0].split(' ');
const tokenKey = inputValues.last(); const tokenKey = _.last(inputValues);
if (inputValues.length > 1) { if (inputValues.length > 1) {
inputValues.pop(); inputValues.pop();
......
...@@ -23,6 +23,7 @@ export const showSubLevelItems = (el) => { ...@@ -23,6 +23,7 @@ export const showSubLevelItems = (el) => {
const top = calculateTop(boundingRect, subItems.offsetHeight); const top = calculateTop(boundingRect, subItems.offsetHeight);
const isAbove = top < boundingRect.top; const isAbove = top < boundingRect.top;
subItems.classList.add('fly-out-list');
subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`; subItems.style.transform = `translate3d(0, ${Math.floor(top)}px, 0)`;
if (isAbove) { if (isAbove) {
......
import _ from 'underscore';
import glRegexp from './lib/utils/regexp'; import glRegexp from './lib/utils/regexp';
import AjaxCache from './lib/utils/ajax_cache'; import AjaxCache from './lib/utils/ajax_cache';
......
/* eslint-disable func-names, no-underscore-dangle, 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 */ /* eslint-disable func-names, no-underscore-dangle, 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 */ /* global fuzzaldrinPlus */
import _ from 'underscore';
import { isObject } from './lib/utils/type_utility'; import { isObject } from './lib/utils/type_utility';
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput; var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput;
...@@ -158,7 +159,7 @@ GitLabDropdownFilter = (function() { ...@@ -158,7 +159,7 @@ GitLabDropdownFilter = (function() {
} else { } else {
elements = this.options.elements(); elements = this.options.elements();
if (search_text) { if (search_text) {
return elements.each(function() { elements.each(function() {
var $el, matches; var $el, matches;
$el = $(this); $el = $(this);
matches = fuzzaldrinPlus.match($el.text().trim(), search_text); matches = fuzzaldrinPlus.match($el.text().trim(), search_text);
...@@ -171,8 +172,10 @@ GitLabDropdownFilter = (function() { ...@@ -171,8 +172,10 @@ GitLabDropdownFilter = (function() {
} }
}); });
} else { } else {
return elements.show().removeClass('option-hidden'); elements.show().removeClass('option-hidden');
} }
elements.parent().find('.dropdown-menu-empty-link').toggleClass('hidden', elements.is(':visible'));
} }
}; };
...@@ -782,9 +785,15 @@ GitLabDropdown = (function() { ...@@ -782,9 +785,15 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) { GitLabDropdown.prototype.focusTextInput = function(triggerFocus = false) {
if (this.options.filterable) { if (this.options.filterable) {
this.dropdown.one('transitionend', () => { this.dropdown.one('transitionend', () => {
const initialScrollTop = $(window).scrollTop();
if (this.dropdown.is('.open')) { if (this.dropdown.is('.open')) {
this.filterInput.focus(); this.filterInput.focus();
} }
if ($(window).scrollTop() < initialScrollTop) {
$(window).scrollTop(initialScrollTop);
}
}); });
if (triggerFocus) { if (triggerFocus) {
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
import _ from 'underscore';
import d3 from 'd3'; import d3 from 'd3';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph'; import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util'; import ContributorsStatGraphUtil from './stat_graph_contributors_util';
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, max-len, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, comma-dangle, no-return-assign, prefer-arrow-callback, quotes, prefer-template, newline-per-chained-call, no-else-return, no-shadow */
import _ from 'underscore';
import d3 from 'd3'; import d3 from 'd3';
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 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; };
......
/* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */ /* eslint-disable func-names, space-before-function-paren, object-shorthand, no-var, one-var, camelcase, one-var-declaration-per-line, comma-dangle, no-param-reassign, no-return-assign, quotes, prefer-arrow-callback, wrap-iife, consistent-return, no-unused-vars, max-len, no-cond-assign, no-else-return, max-len */
import _ from 'underscore';
export default { export default {
parse_log: function(log) { parse_log: function(log) {
......
/* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */ /* eslint-disable comma-dangle, quotes, consistent-return, func-names, array-callback-return, space-before-function-paren, prefer-arrow-callback, max-len, no-unused-expressions, no-sequences, no-underscore-dangle, no-unused-vars, no-param-reassign */
/* global IssuableIndex */ /* global IssuableIndex */
/* global Flash */ /* global Flash */
import _ from 'underscore';
export default { export default {
init({ container, form, issues, prefixId } = {}) { init({ container, form, issues, prefixId } = {}) {
......
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */ /* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, wrap-iife, max-len */
/* global IssuableIndex */ /* global IssuableIndex */
import _ from 'underscore';
import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar'; import IssuableBulkUpdateSidebar from './issuable_bulk_update_sidebar';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
......
/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */ /* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread */
/* global Issuable */ /* global Issuable */
/* global ListLabel */ /* global ListLabel */
import _ from 'underscore';
import IssuableBulkUpdateActions from './issuable_bulk_update_actions'; import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
import DropdownUtils from './filtered_search/dropdown_utils'; import DropdownUtils from './filtered_search/dropdown_utils';
......
...@@ -86,8 +86,9 @@ ...@@ -86,8 +86,9 @@
// This is required to handle non-unicode characters in hash // This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash); hash = decodeURIComponent(hash);
var fixedTabs = document.querySelector('.js-tabs-affix'); const fixedTabs = document.querySelector('.js-tabs-affix');
var fixedNav = document.querySelector('.navbar-gitlab'); const fixedDiffStats = document.querySelector('.js-diff-files-changed.is-stuck');
const fixedNav = document.querySelector('.navbar-gitlab');
var adjustment = 0; var adjustment = 0;
if (fixedNav) adjustment -= fixedNav.offsetHeight; if (fixedNav) adjustment -= fixedNav.offsetHeight;
...@@ -104,6 +105,11 @@ ...@@ -104,6 +105,11 @@
if (fixedTabs) { if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight; adjustment -= fixedTabs.offsetHeight;
} }
if (fixedDiffStats) {
adjustment -= fixedDiffStats.offsetHeight;
}
window.scrollBy(0, adjustment); window.scrollBy(0, adjustment);
} }
}; };
......
import _ from 'underscore';
(() => { (() => {
/* /*
* TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints, * TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,
......
export const isSticky = (el, scrollY, stickyTop) => {
const top = el.offsetTop - scrollY;
if (top === stickyTop) {
el.classList.add('is-stuck');
} else {
el.classList.remove('is-stuck');
}
};
export default (el) => {
if (!el) return;
const computedStyle = window.getComputedStyle(el);
if (!/sticky/.test(computedStyle.position)) return;
const stickyTop = parseInt(computedStyle.top, 10);
document.addEventListener('scroll', () => isSticky(el, window.scrollY, stickyTop), {
passive: true,
});
};
...@@ -16,9 +16,6 @@ import 'mousetrap'; ...@@ -16,9 +16,6 @@ import 'mousetrap';
import 'mousetrap/plugins/pause/mousetrap-pause'; import 'mousetrap/plugins/pause/mousetrap-pause';
import 'vendor/fuzzaldrin-plus'; import 'vendor/fuzzaldrin-plus';
// extensions
import './extensions/array';
// expose common libraries as globals (TODO: remove these) // expose common libraries as globals (TODO: remove these)
window.jQuery = jQuery; window.jQuery = jQuery;
window.$ = jQuery; window.$ = jQuery;
...@@ -36,9 +33,6 @@ import './shortcuts_find_file'; ...@@ -36,9 +33,6 @@ import './shortcuts_find_file';
import './shortcuts_issuable'; import './shortcuts_issuable';
import './shortcuts_network'; import './shortcuts_network';
// behaviors
import './behaviors/';
// templates // templates
import './templates/issuable_template_selector'; import './templates/issuable_template_selector';
import './templates/issuable_template_selectors'; import './templates/issuable_template_selectors';
...@@ -56,6 +50,9 @@ import './lib/utils/pretty_time'; ...@@ -56,6 +50,9 @@ import './lib/utils/pretty_time';
import './lib/utils/text_utility'; import './lib/utils/text_utility';
import './lib/utils/url_utility'; import './lib/utils/url_utility';
// behaviors
import './behaviors/';
// u2f // u2f
import './u2f/authenticate'; import './u2f/authenticate';
import './u2f/error'; import './u2f/error';
...@@ -86,7 +83,6 @@ import './copy_as_gfm'; ...@@ -86,7 +83,6 @@ import './copy_as_gfm';
import './copy_to_clipboard'; import './copy_to_clipboard';
import './create_label'; import './create_label';
import './diff'; import './diff';
import './dispatcher';
import './dropzone_input'; import './dropzone_input';
import './due_date_select'; import './due_date_select';
import './files_comment_button'; import './files_comment_button';
...@@ -150,9 +146,13 @@ import './subscription'; ...@@ -150,9 +146,13 @@ import './subscription';
import './subscription_select'; import './subscription_select';
import './syntax_highlight'; import './syntax_highlight';
import './dispatcher';
// eslint-disable-next-line global-require, import/no-commonjs // eslint-disable-next-line global-require, import/no-commonjs
if (process.env.NODE_ENV !== 'production') require('./test_utils/'); if (process.env.NODE_ENV !== 'production') require('./test_utils/');
Dropzone.autoDiscover = false;
document.addEventListener('beforeunload', function () { document.addEventListener('beforeunload', function () {
// Unbind scroll events // Unbind scroll events
$(document).off('scroll'); $(document).off('scroll');
......
...@@ -7,6 +7,7 @@ import Cookies from 'js-cookie'; ...@@ -7,6 +7,7 @@ import Cookies from 'js-cookie';
import './breakpoints'; import './breakpoints';
import './flash'; import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion'; import BlobForkSuggestion from './blob/blob_fork_suggestion';
import stickyMonitor from './lib/utils/sticky';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
...@@ -266,6 +267,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; ...@@ -266,6 +267,10 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
const $container = $('#diffs'); const $container = $('#diffs');
$container.html(data.html); $container.html(data.html);
this.initChangesDropdown();
stickyMonitor(document.querySelector('.js-diff-files-changed'));
if (typeof gl.diffNotesCompileComponents !== 'undefined') { if (typeof gl.diffNotesCompileComponents !== 'undefined') {
gl.diffNotesCompileComponents(); gl.diffNotesCompileComponents();
} }
...@@ -314,6 +319,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion'; ...@@ -314,6 +319,13 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
}); });
} }
initChangesDropdown() {
$('.js-diff-stats-dropdown').glDropdown({
filterable: true,
remoteFilter: false,
});
}
// Show or hide the loading spinner // Show or hide the loading spinner
// //
// status - Boolean, true to show, false to hide // status - Boolean, true to show, false to hide
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */ /* global Issuable */
/* global ListMilestone */ /* global ListMilestone */
import _ from 'underscore';
(function() { (function() {
this.MilestoneSelect = (function() { this.MilestoneSelect = (function() {
......
import Cookies from 'js-cookie';
import _ from 'underscore';
/* global bp */
import './breakpoints';
export default class NewNavSidebar { export default class NewNavSidebar {
constructor() { constructor() {
this.initDomElements(); this.initDomElements();
this.render();
} }
initDomElements() { initDomElements() {
this.$page = $('.page-with-sidebar');
this.$sidebar = $('.nav-sidebar'); this.$sidebar = $('.nav-sidebar');
this.$overlay = $('.mobile-overlay'); this.$overlay = $('.mobile-overlay');
this.$openSidebar = $('.toggle-mobile-nav'); this.$openSidebar = $('.toggle-mobile-nav');
this.$closeSidebar = $('.close-nav-button'); this.$closeSidebar = $('.close-nav-button');
this.$sidebarToggle = $('.js-toggle-sidebar');
} }
bindEvents() { bindEvents() {
this.$openSidebar.on('click', () => this.toggleSidebarNav(true)); this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false)); this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
this.$overlay.on('click', () => this.toggleSidebarNav(false)); this.$overlay.on('click', () => this.toggleSidebarNav(false));
this.$sidebarToggle.on('click', () => {
const value = !this.$sidebar.hasClass('sidebar-icons-only');
this.toggleCollapsedSidebar(value);
});
$(window).on('resize', () => _.debounce(this.render(), 100));
}
static setCollapsedCookie(value) {
if (bp.getBreakpointSize() !== 'lg') {
return;
}
Cookies.set('sidebar_collapsed', value, { expires: 365 * 10 });
} }
toggleSidebarNav(show) { toggleSidebarNav(show) {
this.$sidebar.toggleClass('nav-sidebar-expanded', show); this.$sidebar.toggleClass('nav-sidebar-expanded', show);
this.$overlay.toggleClass('mobile-nav-open', show); this.$overlay.toggleClass('mobile-nav-open', show);
this.$sidebar.removeClass('sidebar-icons-only');
}
toggleCollapsedSidebar(collapsed) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
this.$page.toggleClass('page-with-new-sidebar', !collapsed);
this.$page.toggleClass('page-with-icon-sidebar', collapsed);
NewNavSidebar.setCollapsedCookie(collapsed);
}
render() {
const breakpoint = bp.getBreakpointSize();
if (breakpoint === 'sm' || breakpoint === 'md') {
this.toggleCollapsedSidebar(true);
} else if (breakpoint === 'lg') {
const collapse = Cookies.get('sidebar_collapsed') === 'true';
this.toggleCollapsedSidebar(collapse);
}
} }
} }
...@@ -11,6 +11,7 @@ newline-per-chained-call, no-useless-escape, class-methods-use-this */ ...@@ -11,6 +11,7 @@ newline-per-chained-call, no-useless-escape, class-methods-use-this */
/* global mrRefreshWidgetUrl */ /* global mrRefreshWidgetUrl */
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import autosize from 'vendor/autosize'; import autosize from 'vendor/autosize';
import Dropzone from 'dropzone'; import Dropzone from 'dropzone';
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
</template> </template>
<script> <script>
import pdfjsLib from 'pdfjs-dist'; import pdfjsLib from 'vendor/pdf';
import workerSrc from 'vendor/pdf.worker'; import workerSrc from 'vendor/pdf.worker.min';
import page from './page/index.vue'; import page from './page/index.vue';
......
<script> <script>
import _ from 'underscore';
export default { export default {
props: { props: {
initialCronInterval: { initialCronInterval: {
......
<script> <script>
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import '~/flash';
import stageColumnComponent from './stage_column_component.vue'; import stageColumnComponent from './stage_column_component.vue';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import '../../../flash';
export default { export default {
props: { props: {
......
/* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */ /* eslint-disable no-useless-escape, max-len, quotes, no-var, no-underscore-dangle, func-names, space-before-function-paren, no-unused-vars, no-return-assign, object-shorthand, one-var, one-var-declaration-per-line, comma-dangle, consistent-return, class-methods-use-this, new-parens */
import 'cropper'; import 'cropper';
import _ from 'underscore';
((global) => { ((global) => {
// Matches everything but the file name // Matches everything but the file name
......
...@@ -10,14 +10,19 @@ import Cookies from 'js-cookie'; ...@@ -10,14 +10,19 @@ import Cookies from 'js-cookie';
const $projectCloneField = $('#project_clone'); const $projectCloneField = $('#project_clone');
const $cloneBtnText = $('a.clone-dropdown-btn span'); const $cloneBtnText = $('a.clone-dropdown-btn span');
const selectedCloneOption = $cloneBtnText.text().trim();
if (selectedCloneOption.length > 0) {
$(`a:contains('${selectedCloneOption}')`, $cloneOptions).addClass('is-active');
}
$('a', $cloneOptions).on('click', (e) => { $('a', $cloneOptions).on('click', (e) => {
const $this = $(e.currentTarget); const $this = $(e.currentTarget);
const url = $this.attr('href'); const url = $this.attr('href');
e.preventDefault(); e.preventDefault();
$('.active', $cloneOptions).not($this).removeClass('active'); $('.is-active', $cloneOptions).not($this).removeClass('is-active');
$this.toggleClass('active'); $this.toggleClass('is-active');
$projectCloneField.val(url); $projectCloneField.val(url);
$cloneBtnText.text($this.text()); $cloneBtnText.text($this.text());
......
export default function setupProjectEdit() { export default function setupProjectEdit() {
const $transferForm = $('.js-project-transfer-form'); const $transferForm = $('.js-project-transfer-form');
const $selectNamespace = $transferForm.find('.select2'); const $selectNamespace = $transferForm.find('select.select2');
$selectNamespace.on('change', () => { $selectNamespace.on('change', () => {
$transferForm.find(':submit').prop('disabled', !$selectNamespace.val()); $transferForm.find(':submit').prop('disabled', !$selectNamespace.val());
......
import _ from 'underscore';
export default class ProtectedBranchDropdown { export default class ProtectedBranchDropdown {
/** /**
* @param {Object} options containing * @param {Object} options containing
......
import _ from 'underscore';
export default class ProtectedTagDropdown { export default class ProtectedTagDropdown {
/** /**
* @param {Object} options containing * @param {Object} options containing
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */
import _ from 'underscore';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import SidebarHeightManager from './sidebar_height_manager'; import SidebarHeightManager from './sidebar_height_manager';
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
/* global ShortcutsNavigation */ /* global ShortcutsNavigation */
/* global sidebar */ /* global sidebar */
import _ from 'underscore';
import 'mousetrap'; import 'mousetrap';
import './shortcuts_navigation'; import './shortcuts_navigation';
...@@ -58,7 +59,7 @@ import './shortcuts_navigation'; ...@@ -58,7 +59,7 @@ import './shortcuts_navigation';
}); });
// If replyField already has some content, add a newline before our quote // If replyField already has some content, add a newline before our quote
separator = replyField.val().trim() !== "" && "\n\n" || ''; separator = replyField.val().trim() !== "" && "\n\n" || '';
replyField.val(function(_, current) { replyField.val(function(a, current) {
return current + separator + quote.join('') + "\n"; return current + separator + quote.join('') + "\n";
}); });
......
import _ from 'underscore';
import '~/smart_interval'; import '~/smart_interval';
import timeTracker from './time_tracker'; import timeTracker from './time_tracker';
......
import _ from 'underscore';
export default { export default {
init() { init() {
if (!this.initialized) { if (!this.initialized) {
...@@ -30,4 +32,3 @@ export default { ...@@ -30,4 +32,3 @@ export default {
} }
}, },
}; };
...@@ -52,6 +52,7 @@ export default class Todos { ...@@ -52,6 +52,7 @@ export default class Todos {
} }
updateRowStateClicked(e) { updateRowStateClicked(e) {
e.stopPropagation();
e.preventDefault(); e.preventDefault();
const target = e.target; const target = e.target;
...@@ -92,6 +93,7 @@ export default class Todos { ...@@ -92,6 +93,7 @@ export default class Todos {
} }
updateAllStateClicked(e) { updateAllStateClicked(e) {
e.stopPropagation();
e.preventDefault(); e.preventDefault();
const target = e.currentTarget; const target = e.currentTarget;
...@@ -142,6 +144,7 @@ export default class Todos { ...@@ -142,6 +144,7 @@ export default class Todos {
if (gl.utils.isMetaClick(e)) { if (gl.utils.isMetaClick(e)) {
const windowTarget = '_blank'; const windowTarget = '_blank';
const selected = e.target; const selected = e.target;
e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (selected.tagName === 'IMG') { if (selected.tagName === 'IMG') {
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
/* global U2FError */ /* global U2FError */
/* global U2FUtil */ /* global U2FUtil */
import _ from 'underscore';
// Authenticate U2F (universal 2nd factor) devices for users to authenticate with. // Authenticate U2F (universal 2nd factor) devices for users to authenticate with.
// //
// State Flow #1: setup -> in_progress -> authenticated -> POST to server // State Flow #1: setup -> in_progress -> authenticated -> POST to server
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
/* global U2FError */ /* global U2FError */
/* global U2FUtil */ /* global U2FUtil */
import _ from 'underscore';
// Register U2F (universal 2nd factor) devices for users to authenticate with. // Register U2F (universal 2nd factor) devices for users to authenticate with.
// //
// State Flow #1: setup -> in_progress -> registered -> POST to server // State Flow #1: setup -> in_progress -> registered -> POST to server
......
/* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */ /* eslint-disable comma-dangle, consistent-return, class-methods-use-this, arrow-parens, no-param-reassign, max-len */
import _ from 'underscore';
const debounceTimeoutDuration = 1000; const debounceTimeoutDuration = 1000;
const invalidInputClass = 'gl-field-error-outline'; const invalidInputClass = 'gl-field-error-outline';
const successInputClass = 'gl-field-success-outline'; const successInputClass = 'gl-field-success-outline';
......
import _ from 'underscore';
import d3 from 'd3'; import d3 from 'd3';
const LOADING_HTML = ` const LOADING_HTML = `
...@@ -6,6 +7,14 @@ const LOADING_HTML = ` ...@@ -6,6 +7,14 @@ const LOADING_HTML = `
</div> </div>
`; `;
function getSystemDate(systemUtcOffsetSeconds) {
const date = new Date();
const localUtcOffsetMinutes = 0 - date.getTimezoneOffset();
const systemUtcOffsetMinutes = systemUtcOffsetSeconds / 60;
date.setMinutes((date.getMinutes() - localUtcOffsetMinutes) + systemUtcOffsetMinutes);
return date;
}
function formatTooltipText({ date, count }) { function formatTooltipText({ date, count }) {
const dateObject = new Date(date); const dateObject = new Date(date);
const dateDayName = gl.utils.getDayName(dateObject); const dateDayName = gl.utils.getDayName(dateObject);
...@@ -21,7 +30,7 @@ function formatTooltipText({ date, count }) { ...@@ -21,7 +30,7 @@ function formatTooltipText({ date, count }) {
const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
export default class ActivityCalendar { export default class ActivityCalendar {
constructor(container, timestamps, calendarActivitiesPath) { constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0) {
this.calendarActivitiesPath = calendarActivitiesPath; this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this); this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = ''; this.currentSelectedDate = '';
...@@ -36,7 +45,7 @@ export default class ActivityCalendar { ...@@ -36,7 +45,7 @@ export default class ActivityCalendar {
this.timestampsTmp = []; this.timestampsTmp = [];
let group = 0; let group = 0;
const today = new Date(); const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0); today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today); const oneYearAgo = new Date(today);
......
...@@ -150,15 +150,21 @@ export default class UserTabs { ...@@ -150,15 +150,21 @@ export default class UserTabs {
const $calendarWrap = this.$parentEl.find('.user-calendar'); const $calendarWrap = this.$parentEl.find('.user-calendar');
const calendarPath = $calendarWrap.data('calendarPath'); const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC';
if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`;
}
$.ajax({ $.ajax({
dataType: 'json', dataType: 'json',
url: calendarPath, url: calendarPath,
success: (activityData) => { success: (activityData) => {
$calendarWrap.html(CALENDAR_TEMPLATE); $calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath); new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath, utcOffset);
}, },
}); });
......
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */ /* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
/* global Issuable */ /* global Issuable */
/* global emitSidebarEvent */ /* global emitSidebarEvent */
import _ from 'underscore';
// TODO: remove eventHub hack after code splitting refactor // TODO: remove eventHub hack after code splitting refactor
window.emitSidebarEvent = window.emitSidebarEvent || $.noop; window.emitSidebarEvent = window.emitSidebarEvent || $.noop;
......
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
name: 'MRWidgetAuthor', name: 'MRWidgetAuthor',
props: { props: {
...@@ -5,11 +7,14 @@ export default { ...@@ -5,11 +7,14 @@ export default {
showAuthorName: { type: Boolean, required: false, default: true }, showAuthorName: { type: Boolean, required: false, default: true },
showAuthorTooltip: { type: Boolean, required: false, default: false }, showAuthorTooltip: { type: Boolean, required: false, default: false },
}, },
directives: {
tooltip,
},
template: ` template: `
<a <a
:href="author.webUrl || author.web_url" :href="author.webUrl || author.web_url"
class="author-link" class="author-link inline"
:class="{ 'has-tooltip': showAuthorTooltip }" :v-tooltip="showAuthorTooltip"
:title="author.name"> :title="author.name">
<img <img
:src="author.avatarUrl || author.avatar_url" :src="author.avatarUrl || author.avatar_url"
......
/* global Flash */ /* global Flash */
import '~/lib/utils/datetime_utility'; import '~/lib/utils/datetime_utility';
import { statusIconEntityMap } from '../../vue_shared/ci_status_icons';
import MemoryUsage from './mr_widget_memory_usage'; import MemoryUsage from './mr_widget_memory_usage';
import StatusIcon from './mr_widget_status_icon';
import MRWidgetService from '../services/mr_widget_service'; import MRWidgetService from '../services/mr_widget_service';
export default { export default {
...@@ -13,11 +13,7 @@ export default { ...@@ -13,11 +13,7 @@ export default {
}, },
components: { components: {
'mr-widget-memory-usage': MemoryUsage, 'mr-widget-memory-usage': MemoryUsage,
}, 'status-icon': StatusIcon,
computed: {
svg() {
return statusIconEntityMap.icon_status_success;
},
}, },
methods: { methods: {
formatDate(date) { formatDate(date) {
...@@ -51,16 +47,15 @@ export default { ...@@ -51,16 +47,15 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-heading"> <div class="mr-widget-heading deploy-heading">
<div v-for="deployment in mr.deployments"> <div v-for="deployment in mr.deployments">
<div class="ci-widget"> <div class="ci-widget media">
<div class="ci-status-icon ci-status-icon-success"> <div class="ci-status-icon ci-status-icon-success">
<span class="js-icon-link icon-link"> <span class="js-icon-link icon-link">
<span class="ci-status-icon" <status-icon status="success" />
v-html="svg"
aria-hidden="true"></span>
</span> </span>
</div> </div>
<div class="media-body space-children">
<span> <span>
<span <span
v-if="hasDeploymentMeta(deployment)"> v-if="hasDeploymentMeta(deployment)">
...@@ -71,7 +66,7 @@ export default { ...@@ -71,7 +66,7 @@ export default {
:href="deployment.url" :href="deployment.url"
target="_blank" target="_blank"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
class="js-deploy-meta"> class="js-deploy-meta inline">
{{deployment.name}} {{deployment.name}}
</a> </a>
<span <span
...@@ -83,7 +78,7 @@ export default { ...@@ -83,7 +78,7 @@ export default {
:href="deployment.external_url" :href="deployment.external_url"
target="_blank" target="_blank"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
class="js-deploy-url"> class="js-deploy-url inline">
<i <i
class="fa fa-external-link" class="fa fa-external-link"
aria-hidden="true" /> aria-hidden="true" />
...@@ -97,6 +92,7 @@ export default { ...@@ -97,6 +92,7 @@ export default {
data-placement="top"> data-placement="top">
{{formatDate(deployment.deployed_at)}} {{formatDate(deployment.deployed_at)}}
</span> </span>
</span>
<button <button
type="button" type="button"
v-if="deployment.stop_url" v-if="deployment.stop_url"
...@@ -104,8 +100,6 @@ export default { ...@@ -104,8 +100,6 @@ export default {
class="btn btn-default btn-xs"> class="btn btn-default btn-xs">
Stop environment Stop environment
</button> </button>
</span>
</div>
<mr-widget-memory-usage <mr-widget-memory-usage
v-if="deployment.metrics_url" v-if="deployment.metrics_url"
:metrics-url="deployment.metrics_url" :metrics-url="deployment.metrics_url"
...@@ -113,5 +107,7 @@ export default { ...@@ -113,5 +107,7 @@ export default {
/> />
</div> </div>
</div> </div>
</div>
</div>
`, `,
}; };
import tooltip from '../../vue_shared/directives/tooltip';
import '../../lib/utils/text_utility'; import '../../lib/utils/text_utility';
export default { export default {
...@@ -5,6 +6,9 @@ export default { ...@@ -5,6 +6,9 @@ export default {
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
directives: {
tooltip,
},
computed: { computed: {
shouldShowCommitsBehindText() { shouldShowCommitsBehindText() {
return this.mr.divergedCommitsCount > 0; return this.mr.divergedCommitsCount > 0;
...@@ -29,18 +33,51 @@ export default { ...@@ -29,18 +33,51 @@ export default {
}, },
template: ` template: `
<div class="mr-source-target"> <div class="mr-source-target">
<div <div class="normal">
v-if="mr.isOpen" <strong>
class="pull-right"> Request to merge
<span
class="label-branch"
:class="{'label-truncated': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
v-html="mr.sourceBranchLink"></span>
<button
v-tooltip
class="btn btn-transparent btn-clipboard"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:v-tooltip="isBranchTitleLong(mr.sourceBranch)"
:class="{'label-truncatedtooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
<div v-if="mr.isOpen">
<a <a
href="#modal_merge_info" href="#modal_merge_info"
data-toggle="modal" data-toggle="modal"
class="btn inline btn-grouped btn-sm"> class="btn btn-small inline">
Check out branch Check out branch
</a> </a>
<span class="dropdown inline prepend-left-5"> <span class="dropdown inline prepend-left-10">
<a <a
class="btn btn-sm dropdown-toggle" class="btn btn-xs dropdown-toggle"
data-toggle="dropdown" data-toggle="dropdown"
aria-label="Download as" aria-label="Download as"
role="button"> role="button">
...@@ -69,38 +106,6 @@ export default { ...@@ -69,38 +106,6 @@ export default {
</ul> </ul>
</span> </span>
</div> </div>
<div class="normal">
<strong>
Request to merge
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.sourceBranch)}"
:title="isBranchTitleLong(mr.sourceBranch) ? mr.sourceBranch : ''"
data-placement="bottom"
v-html="mr.sourceBranchLink"></span>
<button
class="btn btn-transparent btn-clipboard has-tooltip"
data-title="Copy branch name to clipboard"
:data-clipboard-text="branchNameClipboardData">
<i
aria-hidden="true"
class="fa fa-clipboard"></i>
</button>
into
<span
class="label-branch"
:class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}"
:title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''"
data-placement="bottom">
<a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a>
</span>
</strong>
<span
v-if="shouldShowCommitsBehindText"
class="diverged-commits-count">
(<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>)
</span>
</div>
</div> </div>
`, `,
}; };
...@@ -120,13 +120,12 @@ export default { ...@@ -120,13 +120,12 @@ export default {
}, },
template: ` template: `
<div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage"> <div class="mr-info-list clearfix mr-memory-usage js-mr-memory-usage">
<div class="legend"></div>
<p <p
v-if="shouldShowLoading" v-if="shouldShowLoading"
class="usage-info js-usage-info usage-info-loading"> class="usage-info js-usage-info usage-info-loading">
<i <i
class="fa fa-spinner fa-spin usage-info-load-spinner" class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true" />Loading deployment statistics. aria-hidden="true" />Loading deployment statistics
</p> </p>
<p <p
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
...@@ -136,12 +135,12 @@ export default { ...@@ -136,12 +135,12 @@ export default {
<p <p
v-if="shouldShowLoadFailure" v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed"> class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics. Failed to load deployment statistics
</p> </p>
<p <p
v-if="shouldShowMetricsUnavailable" v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable"> class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently. Deployment statistics are not available currently
</p> </p>
<mr-memory-graph <mr-memory-graph
v-if="shouldShowMemoryGraph" v-if="shouldShowMemoryGraph"
......
...@@ -16,7 +16,7 @@ export default { ...@@ -16,7 +16,7 @@ export default {
<a <a
data-toggle="modal" data-toggle="modal"
href="#modal_merge_info"> href="#modal_merge_info">
command line. command line
</a> </a>
</section> </section>
`, `,
......
...@@ -29,48 +29,44 @@ export default { ...@@ -29,48 +29,44 @@ export default {
}, },
template: ` template: `
<div class="mr-widget-heading"> <div class="mr-widget-heading">
<div class="ci-widget"> <div class="ci-widget media">
<template v-if="hasCIError"> <template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error"> <div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
<span class="js-icon-link icon-link">
<span <span
v-html="svg" v-html="svg"
aria-hidden="true"></span> aria-hidden="true"></span>
</span>
</div> </div>
<span>Could not connect to the CI server. Please check your settings and try again.</span> <div class="media-body">
Could not connect to the CI server. Please check your settings and try again
</div>
</template> </template>
<template v-else> <template v-else>
<div> <div class="ci-status-icon append-right-10">
<a <a
class="icon-link" class="icon-link"
:href="this.status.details_path"> :href="this.status.details_path">
<ci-icon :status="status" /> <ci-icon :status="status" />
</a> </a>
</div> </div>
<div class="media-body">
<span> <span>
Pipeline Pipeline
<a <a
:href="mr.pipeline.path" :href="mr.pipeline.path"
class="pipeline-id">#{{mr.pipeline.id}}</a> class="pipeline-id">#{{mr.pipeline.id}}</a>
{{mr.pipeline.details.status.label}}
</span> </span>
<span <span class="mr-widget-pipeline-graph">
v-if="mr.pipeline.details.stages.length > 0"> <span class="stage-cell">
with {{stageText}}
</span>
<div class="mr-widget-pipeline-graph">
<div class="stage-cell">
<div <div
v-if="mr.pipeline.details.stages.length > 0" v-if="mr.pipeline.details.stages.length > 0"
v-for="stage in mr.pipeline.details.stages" v-for="stage in mr.pipeline.details.stages"
class="stage-container dropdown js-mini-pipeline-graph"> class="stage-container dropdown js-mini-pipeline-graph">
<pipeline-stage :stage="stage" /> <pipeline-stage :stage="stage" />
</div> </div>
</div> </span>
</div> </span>
<span> <span>
for {{mr.pipeline.details.status.label}} for
<a <a
:href="mr.pipeline.commit.commit_path" :href="mr.pipeline.commit.commit_path"
class="commit-sha js-commit-link"> class="commit-sha js-commit-link">
...@@ -79,8 +75,9 @@ export default { ...@@ -79,8 +75,9 @@ export default {
<span <span
v-if="mr.pipeline.coverage" v-if="mr.pipeline.coverage"
class="js-mr-coverage"> class="js-mr-coverage">
Coverage {{mr.pipeline.coverage}}%. Coverage {{mr.pipeline.coverage}}%
</span> </span>
</div>
</template> </template>
</div> </div>
</div> </div>
......
...@@ -2,37 +2,32 @@ export default { ...@@ -2,37 +2,32 @@ export default {
name: 'MRWidgetRelatedLinks', name: 'MRWidgetRelatedLinks',
props: { props: {
relatedLinks: { type: Object, required: true }, relatedLinks: { type: Object, required: true },
state: { type: String, required: false },
}, },
computed: { computed: {
hasLinks() { hasLinks() {
const { closing, mentioned, assignToMe } = this.relatedLinks; const { closing, mentioned, assignToMe } = this.relatedLinks;
return closing || mentioned || assignToMe; return closing || mentioned || assignToMe;
}, },
}, closesText() {
methods: { if (this.state === 'merged') {
hasMultipleIssues(text) { return 'Closed';
return !text ? false : text.match(/<\/a> and <a/); }
}, if (this.state === 'closed') {
issueLabel(field) { return 'Did not close';
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'issues' : 'issue'; }
}, return 'Closes';
verbLabel(field) {
return this.hasMultipleIssues(this.relatedLinks[field]) ? 'are' : 'is';
}, },
}, },
template: ` template: `
<section <section
v-if="hasLinks" v-if="hasLinks"
class="mr-info-list mr-links"> class="mr-info-list mr-links">
<div class="legend"></div>
<p v-if="relatedLinks.closing"> <p v-if="relatedLinks.closing">
Closes {{issueLabel('closing')}} {{closesText}} <span v-html="relatedLinks.closing"></span>
<span v-html="relatedLinks.closing"></span>.
</p> </p>
<p v-if="relatedLinks.mentioned"> <p v-if="relatedLinks.mentioned">
<span class="capitalize">{{issueLabel('mentioned')}}</span> Mentions <span v-html="relatedLinks.mentioned"></span>
<span v-html="relatedLinks.mentioned"></span>
{{verbLabel('mentioned')}} mentioned but will not be closed.
</p> </p>
<p v-if="relatedLinks.assignToMe"> <p v-if="relatedLinks.assignToMe">
<span v-html="relatedLinks.assignToMe"></span> <span v-html="relatedLinks.assignToMe"></span>
......
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
props: {
status: { type: String, required: true },
showDisabledButton: { type: Boolean, required: false },
},
components: {
ciIcon,
loadingIcon,
},
computed: {
statusObj() {
return {
group: this.status,
icon: `icon_status_${this.status}`,
};
},
},
template: `
<div class="space-children flex-container-block append-right-10">
<div v-if="status === 'loading'" class="mr-widget-icon">
<loading-icon />
</div>
<ci-icon v-else :status="statusObj" />
<button
v-if="showDisabledButton"
type="button"
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
</div>
`,
};
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetArchived', name: 'MRWidgetArchived',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<div class="space-children">
<status-icon status="failed" />
<button <button
type="button" type="button"
class="btn btn-success btn-small" class="btn btn-success btn-small"
disabled="true"> disabled="true">
Merge Merge
</button> </button>
</div>
<div class="media-body">
<span class="bold"> <span class="bold">
This project is archived, write access has been disabled. This project is archived, write access has been disabled
</span> </span>
</div> </div>
</div>
`, `,
}; };
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetAutoMergeFailed', name: 'MRWidgetAutoMergeFailed',
...@@ -10,6 +11,9 @@ export default { ...@@ -10,6 +11,9 @@ export default {
isRefreshing: false, isRefreshing: false,
}; };
}, },
components: {
statusIcon,
},
methods: { methods: {
refreshWidget() { refreshWidget() {
this.isRefreshing = true; this.isRefreshing = true;
...@@ -19,18 +23,16 @@ export default { ...@@ -19,18 +23,16 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" />
class="btn btn-success btn-small" <div class="media-body space-children">
disabled="true" <span class="bold">
type="button"> <template v-if="mr.mergeError">{{mr.mergeError}}.</template>
Merge This merge request failed to be merged automatically
</button> </span>
<span class="bold danger">
This merge request failed to be merged automatically.
<button <button
@click="refreshWidget" @click="refreshWidget"
:class="{ disabled: isRefreshing }" :disabled="isRefreshing"
type="button" type="button"
class="btn btn-xs btn-default"> class="btn btn-xs btn-default">
<i <i
...@@ -39,9 +41,6 @@ export default { ...@@ -39,9 +41,6 @@ export default {
aria-hidden="true" /> aria-hidden="true" />
Refresh Refresh
</button> </button>
</span>
<div class="merge-error-text danger bold">
{{mr.mergeError}}
</div> </div>
</div> </div>
`, `,
......
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetChecking', name: 'MRWidgetChecking',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="loading" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
Checking ability to merge automatically. Checking ability to merge automatically
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</span> </span>
</div> </div>
</div>
`, `,
}; };
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetClosed', name: 'MRWidgetClosed',
...@@ -7,24 +8,28 @@ export default { ...@@ -7,24 +8,28 @@ export default {
}, },
components: { components: {
'mr-widget-author-and-time': mrWidgetAuthorTime, 'mr-widget-author-and-time': mrWidgetAuthorTime,
statusIcon,
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<status-icon status="failed" />
<div class="media-body">
<mr-widget-author-and-time <mr-widget-author-and-time
actionText="Closed by" actionText="Closed by"
:author="mr.closedBy" :author="mr.closedBy"
:dateTitle="mr.updatedAt" :dateTitle="mr.updatedAt"
:dateReadable="mr.closedAt" :dateReadable="mr.closedAt"
/> />
<section> <section class="mr-info-list">
<p> <p>
The changes were not merged into The changes were not merged into
<a <a
:href="mr.targetBranchPath" :href="mr.targetBranchPath"
class="label-branch"> class="label-branch">
{{mr.targetBranch}}</a>. {{mr.targetBranch}}</a>
</p> </p>
</section> </section>
</div> </div>
</div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetConflicts', name: 'MRWidgetConflicts',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
There are merge conflicts. There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge"> <span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally. Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span> </span>
</span> </span>
<div
v-if="mr.canMerge"
class="btn-group">
<a <a
v-if="mr.conflictResolutionPath" v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath" :href="mr.conflictResolutionPath"
class="btn btn-default btn-xs js-resolve-conflicts-button"> class="btn btn-default btn-xs js-resolve-conflicts-button">
Resolve conflicts Resolve conflicts
......
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -38,39 +39,40 @@ export default { ...@@ -38,39 +39,40 @@ export default {
} }
}, },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <template v-if="isRefreshing">
class="btn btn-success btn-small" <status-icon status="loading" />
disabled="true" <span class="media-body bold js-refresh-label">
type="button"> Refreshing now
Merge </span>
</button> </template>
<span <template v-else>
v-if="!isRefreshing" <status-icon status="failed" showDisabledButton />
class="bold danger"> <div class="media-body space-children">
<span class="bold">
<span <span
class="has-error-message" class="has-error-message"
v-if="mr.mergeError"> v-if="mr.mergeError">
{{mr.mergeError}} {{mr.mergeError}}.
</span> </span>
<span v-else>Merge failed.</span> <span v-else>Merge failed.</span>
<span <span
:class="{ 'has-custom-error': mr.mergeError }"> :class="{ 'has-custom-error': mr.mergeError }">
Refreshing in {{timerText}} to show the updated status... Refreshing in {{timerText}} to show the updated status...
</span> </span>
</span>
<button <button
@click="refresh" @click="refresh"
class="btn btn-default btn-xs js-refresh-button" class="btn btn-default btn-xs js-refresh-button"
type="button"> type="button">
Refresh now Refresh now
</button> </button>
</span> </div>
<span </template>
v-if="isRefreshing"
class="bold js-refresh-label">
Refreshing now...
</span>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetLocked', name: 'MRWidgetLocked',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body mr-state-locked"> <div class="mr-widget-body mr-state-locked media">
<span class="state-label">Locked</span> <status-icon status="loading" />
This merge request is in the process of being merged, during which time it is locked and cannot be closed. <div class="media-body">
<i <h4>
class="fa fa-spinner fa-spin" This merge request is in the process of being merged, during which time it is locked and cannot be closed
aria-hidden="true" /> </h4>
<section class="mr-info-list mr-links"> <section class="mr-info-list">
<div class="legend"></div>
<p> <p>
The changes will be merged into The changes will be merged into
<span class="label-branch"> <span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a> <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>. </span>
</p> </p>
</section> </section>
</div> </div>
</div>
`, `,
}; };
/* global Flash */ /* global Flash */
import statusIcon from '../mr_widget_status_icon';
import MRWidgetAuthor from '../../components/mr_widget_author'; import MRWidgetAuthor from '../../components/mr_widget_author';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
...@@ -11,6 +11,7 @@ export default { ...@@ -11,6 +11,7 @@ export default {
}, },
components: { components: {
'mr-widget-author': MRWidgetAuthor, 'mr-widget-author': MRWidgetAuthor,
statusIcon,
}, },
data() { data() {
return { return {
...@@ -61,11 +62,13 @@ export default { ...@@ -61,11 +62,13 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<h4> <h4>
Set by Set by
<mr-widget-author :author="mr.setToMWPSBy" /> <mr-widget-author :author="mr.setToMWPSBy" />
to be merged automatically when the pipeline succeeds. to be merged automatically when the pipeline succeeds
<a <a
v-if="mr.canCancelAutomaticMerge" v-if="mr.canCancelAutomaticMerge"
@click.prevent="cancelAutomaticMerge" @click.prevent="cancelAutomaticMerge"
...@@ -81,21 +84,18 @@ export default { ...@@ -81,21 +84,18 @@ export default {
</a> </a>
</h4> </h4>
<section class="mr-info-list"> <section class="mr-info-list">
<div class="legend"></div>
<p>The changes will be merged into <p>The changes will be merged into
<a <a
:href="mr.targetBranchPath" :href="mr.targetBranchPath"
class="label-branch"> class="label-branch">
{{mr.targetBranch}} {{mr.targetBranch}}
</a>. </a>
</p> </p>
<p v-if="mr.shouldRemoveSourceBranch"> <p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed. The source branch will be removed
</p> </p>
<p <p v-else>
v-else The source branch will not be removed
class="with-button">
The source branch will not be removed.
<a <a
v-if="canRemoveSourceBranch" v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch" :disabled="isRemovingSourceBranch"
...@@ -112,5 +112,6 @@ export default { ...@@ -112,5 +112,6 @@ export default {
</p> </p>
</section> </section>
</div> </div>
</div>
`, `,
}; };
/* global Flash */ /* global Flash */
import mrWidgetAuthorTime from '../../components/mr_widget_author_time'; import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import tooltip from '../../../vue_shared/directives/tooltip';
import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -9,14 +12,19 @@ export default { ...@@ -9,14 +12,19 @@ export default {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
service: { type: Object, required: true }, service: { type: Object, required: true },
}, },
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
},
data() { data() {
return { return {
isMakingRequest: false, isMakingRequest: false,
}; };
}, },
directives: {
tooltip,
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
loadingIcon,
statusIcon,
},
computed: { computed: {
shouldShowRemoveSourceBranch() { shouldShowRemoveSourceBranch() {
const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr; const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
...@@ -55,44 +63,19 @@ export default { ...@@ -55,44 +63,19 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<div class="space-children">
<mr-widget-author-and-time <mr-widget-author-and-time
actionText="Merged by" actionText="Merged by"
:author="mr.mergedBy" :author="mr.mergedBy"
:dateTitle="mr.updatedAt" :dateTitle="mr.updatedAt"
:dateReadable="mr.mergedAt" /> :dateReadable="mr.mergedAt" />
<section class="mr-info-list">
<div class="legend"></div>
<p>
The changes were merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
<p v-if="mr.sourceBranchRemoved">The source branch has been removed.</p>
<p v-if="shouldShowRemoveSourceBranch">
You can remove source branch now.
<button
@click="removeSourceBranch"
:class="{ disabled: isMakingRequest }"
type="button"
class="btn btn-xs btn-default js-remove-branch-button">
Remove Source Branch
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
The source branch is being removed.
</p>
</section>
<div
v-if="shouldShowMergedButtons"
class="merged-buttons clearfix">
<a <a
v-if="mr.canRevertInCurrentMR" v-if="mr.canRevertInCurrentMR"
class="btn btn-close btn-sm has-tooltip" v-tooltip
class="btn btn-close btn-xs"
href="#modal-revert-commit" href="#modal-revert-commit"
data-toggle="modal" data-toggle="modal"
data-container="body" data-container="body"
...@@ -101,7 +84,8 @@ export default { ...@@ -101,7 +84,8 @@ export default {
</a> </a>
<a <a
v-else-if="mr.revertInForkPath" v-else-if="mr.revertInForkPath"
class="btn btn-close btn-sm has-tooltip" v-tooltip
class="btn btn-close btn-xs"
data-method="post" data-method="post"
:href="mr.revertInForkPath" :href="mr.revertInForkPath"
title="Revert this merge request in a new merge request"> title="Revert this merge request in a new merge request">
...@@ -109,7 +93,8 @@ export default { ...@@ -109,7 +93,8 @@ export default {
</a> </a>
<a <a
v-if="mr.canCherryPickInCurrentMR" v-if="mr.canCherryPickInCurrentMR"
class="btn btn-default btn-sm has-tooltip" v-tooltip
class="btn btn-default btn-xs"
href="#modal-cherry-pick-commit" href="#modal-cherry-pick-commit"
data-toggle="modal" data-toggle="modal"
data-container="body" data-container="body"
...@@ -118,13 +103,38 @@ export default { ...@@ -118,13 +103,38 @@ export default {
</a> </a>
<a <a
v-else-if="mr.cherryPickInForkPath" v-else-if="mr.cherryPickInForkPath"
class="btn btn-default btn-sm has-tooltip" v-tooltip
class="btn btn-default btn-xs"
data-method="post" data-method="post"
:href="mr.cherryPickInForkPath" :href="mr.cherryPickInForkPath"
title="Cherry-pick this merge request in a new merge request"> title="Cherry-pick this merge request in a new merge request">
Cherry-pick Cherry-pick
</a> </a>
</div> </div>
<section class="mr-info-list">
<p>
The changes were merged into
<span class="label-branch">
<a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
</span>
</p>
<p v-if="mr.sourceBranchRemoved">The source branch has been removed</p>
<p v-if="shouldShowRemoveSourceBranch" class="space-children">
<span>You can remove source branch now</span>
<button
@click="removeSourceBranch"
:disabled="isMakingRequest"
type="button"
class="btn btn-xs btn-default js-remove-branch-button">
Remove Source Branch
</button>
</p>
<p v-if="shouldShowSourceBranchRemoving">
<loading-icon inline />
<span>The source branch is being removed</span>
</p>
</section>
</div>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import mrWidgetMergeHelp from '../../components/mr_widget_merge_help'; import mrWidgetMergeHelp from '../../components/mr_widget_merge_help';
export default { export default {
...@@ -5,30 +7,37 @@ export default { ...@@ -5,30 +7,37 @@ export default {
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
directives: {
tooltip,
},
components: { components: {
'mr-widget-merge-help': mrWidgetMergeHelp, 'mr-widget-merge-help': mrWidgetMergeHelp,
statusIcon,
}, },
computed: { computed: {
missingBranchName() { missingBranchName() {
return this.mr.sourceBranchRemoved ? 'source' : 'target'; return this.mr.sourceBranchRemoved ? 'source' : 'target';
}, },
message() {
return `If the ${this.missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line`;
},
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold js-branch-text"> <span class="bold js-branch-text">
<span class="capitalize"> <span class="capitalize">
{{missingBranchName}} {{missingBranchName}}
</span> branch does not exist. </span> branch does not exist.
Please restore the {{missingBranchName}} branch or use a different {{missingBranchName}} branch. Please restore it or use a different {{missingBranchName}} branch
<i
v-tooltip
class="fa fa-question-circle"
:title="message"
:aria-label="message"></i>
</span> </span>
<mr-widget-merge-help </div>
:missing-branch="missingBranchName" />
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetNotAllowed', name: 'MRWidgetNotAllowed',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="success" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
Ready to be merged automatically. Ready to be merged automatically.
Ask someone with write access to this repository to merge this request. Ask someone with write access to this repository to merge this request
</span> </span>
</div> </div>
</div>
`, `,
}; };
...@@ -12,7 +12,7 @@ export default { ...@@ -12,7 +12,7 @@ export default {
return { emptyStateSVG }; return { emptyStateSVG };
}, },
template: ` template: `
<div class="mr-widget-body empty-state"> <div class="mr-widget-body mr-widget-empty-state">
<div class="row"> <div class="row">
<div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center"> <div class="artwork col-sm-5 col-sm-push-7 col-xs-12 text-center">
<span v-html="emptyStateSVG"></span> <span v-html="emptyStateSVG"></span>
...@@ -29,6 +29,7 @@ export default { ...@@ -29,6 +29,7 @@ export default {
Currently there are no changes in this merge request's source branch. Currently there are no changes in this merge request's source branch.
Please push new commits or use a different branch. Please push new commits or use a different branch.
</p> </p>
<div>
<a <a
v-if="mr.newBlobPath" v-if="mr.newBlobPath"
:href="mr.newBlobPath" :href="mr.newBlobPath"
...@@ -38,5 +39,6 @@ export default { ...@@ -38,5 +39,6 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
Pipeline blocked. The pipeline for this merge request requires a manual action to proceed. Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
</span> </span>
</div> </div>
</div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetPipelineBlocked', name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
class="btn btn-success btn-small" <div class="media-body space-children">
disabled="true"
type="button">
Merge
</button>
<span class="bold"> <span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure. The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span> </span>
</div> </div>
</div>
`, `,
}; };
/* global Flash */ /* global Flash */
import successSvg from 'icons/_icon_status_success.svg'; import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg'; import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll'; import simplePoll from '~/lib/utils/simple_poll';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -25,6 +25,9 @@ export default { ...@@ -25,6 +25,9 @@ export default {
warningSvg, warningSvg,
}; };
}, },
components: {
statusIcon,
},
computed: { computed: {
commitMessageLinkTitle() { commitMessageLinkTitle() {
const withDesc = 'Include description in commit message'; const withDesc = 'Include description in commit message';
...@@ -196,7 +199,10 @@ export default { ...@@ -196,7 +199,10 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
<div class="media space-children">
<span class="btn-group"> <span class="btn-group">
<button <button
@click="handleMergeButtonClick()" @click="handleMergeButtonClick()"
...@@ -213,14 +219,12 @@ export default { ...@@ -213,14 +219,12 @@ export default {
v-if="shouldShowMergeOptionsDropdown" v-if="shouldShowMergeOptionsDropdown"
:disabled="isMergeButtonDisabled" :disabled="isMergeButtonDisabled"
type="button" type="button"
class="btn btn-small btn-info dropdown-toggle" class="btn btn-small btn-info dropdown-toggle js-merge-moment"
data-toggle="dropdown"> data-toggle="dropdown"
aria-label="Select merge moment">
<i <i
class="fa fa-caret-down" class="fa fa-chevron-down"
aria-hidden="true" /> aria-hidden="true" />
<span class="sr-only">
Select merge moment
</span>
</button> </button>
<ul <ul
v-if="shouldShowMergeOptionsDropdown" v-if="shouldShowMergeOptionsDropdown"
...@@ -231,11 +235,13 @@ export default { ...@@ -231,11 +235,13 @@ export default {
@click.prevent="handleMergeButtonClick(true)" @click.prevent="handleMergeButtonClick(true)"
class="merge_when_pipeline_succeeds" class="merge_when_pipeline_succeeds"
href="#"> href="#">
<span class="media">
<span <span
v-html="successSvg" v-html="successSvg"
class="merge-opt-icon" class="merge-opt-icon"
aria-hidden="true"></span> aria-hidden="true"></span>
<span class="merge-opt-title">Merge when pipeline succeeds</span> <span class="media-body merge-opt-title">Merge when pipeline succeeds</span>
</span>
</a> </a>
</li> </li>
<li> <li>
...@@ -243,17 +249,20 @@ export default { ...@@ -243,17 +249,20 @@ export default {
@click.prevent="handleMergeButtonClick(false, true)" @click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request" class="accept-merge-request"
href="#"> href="#">
<span class="media">
<span <span
v-html="warningSvg" v-html="warningSvg"
class="merge-opt-icon" class="merge-opt-icon"
aria-hidden="true"></span> aria-hidden="true"></span>
<span class="merge-opt-title">Merge immediately</span> <span class="media-body merge-opt-title">Merge immediately</span>
</span>
</a> </a>
</li> </li>
</ul> </ul>
</span> </span>
<div class="media-body space-children">
<template v-if="isMergeAllowed()"> <template v-if="isMergeAllowed()">
<label class="spacing"> <label>
<input <input
id="remove-source-branch-input" id="remove-source-branch-input"
v-model="removeSourceBranch" v-model="removeSourceBranch"
...@@ -274,6 +283,14 @@ export default { ...@@ -274,6 +283,14 @@ export default {
type="button"> type="button">
Modify commit message Modify commit message
</button> </button>
</template>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span>
</template>
</div>
</div>
<div <div
v-if="showCommitMessageEditor" v-if="showCommitMessageEditor"
class="prepend-top-default commit-message-editor"> class="prepend-top-default commit-message-editor">
...@@ -293,7 +310,7 @@ export default { ...@@ -293,7 +310,7 @@ export default {
rows="14" rows="14"
name="Commit message"></textarea> name="Commit message"></textarea>
</div> </div>
<p class="hint">Try to keep the first line under 52 characters and the others under 72.</p> <p class="hint">Try to keep the first line under 52 characters and the others under 72</p>
<div class="hint"> <div class="hint">
<a <a
@click.prevent="updateCommitMessage" @click.prevent="updateCommitMessage"
...@@ -302,12 +319,7 @@ export default { ...@@ -302,12 +319,7 @@ export default {
</div> </div>
</div> </div>
</div> </div>
</template> </div>
<template v-else>
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure.
</span>
</template>
</div> </div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetSHAMismatch', name: 'MRWidgetSHAMismatch',
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
The source branch HEAD has recently changed. Please reload the page and review the changes before merging. The source branch HEAD has recently changed. Please reload the page and review the changes before merging
</span> </span>
</div> </div>
</div>
`, `,
}; };
import statusIcon from '../mr_widget_status_icon';
export default { export default {
name: 'MRWidgetUnresolvedDiscussions', name: 'MRWidgetUnresolvedDiscussions',
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
}, },
components: {
statusIcon,
},
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" showDisabledButton />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge
</button>
<span class="bold"> <span class="bold">
There are unresolved discussions. Please resolve these discussions There are unresolved discussions. Please resolve these discussions
<span v-if="mr.canCreateIssue">or</span>
<span v-else>.</span>
</span> </span>
<a <a
v-if="mr.createIssueToResolveDiscussionsPath" v-if="mr.createIssueToResolveDiscussionsPath"
...@@ -23,5 +22,6 @@ export default { ...@@ -23,5 +22,6 @@ export default {
Create an issue to resolve them later Create an issue to resolve them later
</a> </a>
</div> </div>
</div>
`, `,
}; };
/* global Flash */ /* global Flash */
import statusIcon from '../mr_widget_status_icon';
import tooltip from '../../../vue_shared/directives/tooltip';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
...@@ -7,11 +9,17 @@ export default { ...@@ -7,11 +9,17 @@ export default {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
service: { type: Object, required: true }, service: { type: Object, required: true },
}, },
directives: {
tooltip,
},
data() { data() {
return { return {
isMakingRequest: false, isMakingRequest: false,
}; };
}, },
components: {
statusIcon,
},
methods: { methods: {
removeWIP() { removeWIP() {
this.isMakingRequest = true; this.isMakingRequest = true;
...@@ -29,20 +37,20 @@ export default { ...@@ -29,20 +37,20 @@ export default {
}, },
}, },
template: ` template: `
<div class="mr-widget-body"> <div class="mr-widget-body media">
<button <status-icon status="failed" :showDisabledButton="Boolean(mr.removeWIPPath)" />
type="button" <div class="media-body space-children">
class="btn btn-success btn-small"
disabled="true">
Merge</button>
<span class="bold"> <span class="bold">
This merge request is currently Work In Progress and therefore unable to merge This is a Work in Progress
</span>
<template v-if="mr.removeWIPPath">
<i <i
class="fa fa-question-circle has-tooltip" v-tooltip
title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged." /> class="fa fa-question-circle"
title="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged"
aria-label="When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged">
</i>
</span>
<button <button
v-if="mr.removeWIPPath"
@click="removeWIP" @click="removeWIP"
:disabled="isMakingRequest" :disabled="isMakingRequest"
type="button" type="button"
...@@ -53,7 +61,7 @@ export default { ...@@ -53,7 +61,7 @@ export default {
aria-hidden="true" /> aria-hidden="true" />
Resolve WIP status Resolve WIP status
</button> </button>
</template> </div>
</div> </div>
`, `,
}; };
/** /**
* This file is the centerpiece of an attempt to reduce potential conflicts * This file is the centerpiece of an attempt to reduce potential conflicts
* between the CE and EE versions of the MR widget. EE additions to the MR widget should * between the CE and EE versions of the MR widget. EE additions to the MR widget should
* be contained in the ./vue_merge_request_widget/ee directory, and should **extend** * be contained in the ee/vue_merge_request_widget directory, and should **extend**
* rather than mutate CE MR Widget code. * rather than mutate CE MR Widget code.
* *
* This file should be the only source of conflicts between EE and CE. EE-only components should * This file should be the only source of conflicts between EE and CE. EE-only components should
......
...@@ -35,8 +35,14 @@ import { ...@@ -35,8 +35,14 @@ import {
export default { export default {
el: '#js-vue-mr-widget', el: '#js-vue-mr-widget',
name: 'MRWidget', name: 'MRWidget',
props: {
mrData: {
type: Object,
required: false,
},
},
data() { data() {
const store = new MRWidgetStore(gl.mrWidgetData); const store = new MRWidgetStore(this.mrData || window.gl.mrWidgetData);
const service = this.createService(store); const service = this.createService(store);
return { return {
mr: store, mr: store,
...@@ -234,14 +240,21 @@ export default { ...@@ -234,14 +240,21 @@ export default {
v-if="shouldRenderDeployments" v-if="shouldRenderDeployments"
:mr="mr" :mr="mr"
:service="service" /> :service="service" />
<div class="mr-widget-section">
<component <component
:is="componentName" :is="componentName"
:mr="mr" :mr="mr"
:service="service" /> :service="service" />
<mr-widget-related-links <mr-widget-related-links
v-if="shouldRenderRelatedLinks" v-if="shouldRenderRelatedLinks"
:state="mr.state"
:related-links="mr.relatedLinks" /> :related-links="mr.relatedLinks" />
<mr-widget-merge-help v-if="shouldRenderMergeHelp" /> </div>
<div
class="mr-widget-footer"
v-if="shouldRenderMergeHelp">
<mr-widget-merge-help />
</div>
</div> </div>
`, `,
}; };
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
@import "framework/lists"; @import "framework/lists";
@import "framework/logo"; @import "framework/logo";
@import "framework/markdown_area"; @import "framework/markdown_area";
@import "framework/media_object";
@import "framework/mobile"; @import "framework/mobile";
@import "framework/modal"; @import "framework/modal";
@import "framework/nav"; @import "framework/nav";
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
margin-top: -23px; margin-top: -23px;
float: right; float: right;
font-size: 12px; font-size: 12px;
direction: ltr;
} }
.pika-single.gitlab-theme { .pika-single.gitlab-theme {
......
...@@ -574,6 +574,7 @@ ...@@ -574,6 +574,7 @@
.dropdown-input-field, .dropdown-input-field,
.default-dropdown-input { .default-dropdown-input {
display: block;
width: 100%; width: 100%;
min-height: 30px; min-height: 30px;
padding: 0 7px; padding: 0 7px;
...@@ -722,3 +723,57 @@ ...@@ -722,3 +723,57 @@
@include set-invisible; @include set-invisible;
overflow: hidden; overflow: hidden;
} }
// TODO: change global style and remove mixin
@mixin new-style-dropdown {
.dropdown-menu,
.dropdown-menu-nav {
.divider {
margin: 6px 0;
}
li {
padding: 0 1px;
&.dropdown-header {
padding: 8px 16px;
}
a {
border-radius: 0;
padding: 8px 16px;
&.is-focused,
&:hover,
&:active,
&:focus {
background-color: $gray-darker;
}
&.is-active {
font-weight: inherit;
&::before {
top: 16px;
}
}
}
}
&.dropdown-menu-selectable {
li {
a {
padding: 8px 40px;
&.is-active::before {
left: 16px;
}
}
}
}
}
.dropdown-menu-align-right {
margin-top: 2px;
}
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
*/ */
header { header {
@include new-style-dropdown;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
&.navbar-empty { &.navbar-empty {
...@@ -24,7 +26,7 @@ header { ...@@ -24,7 +26,7 @@ header {
&.navbar-gitlab { &.navbar-gitlab {
padding: 0 16px; padding: 0 16px;
z-index: 400; z-index: 2000;
margin-bottom: 0; margin-bottom: 0;
min-height: $header-height; min-height: $header-height;
background-color: $gray-light; background-color: $gray-light;
...@@ -313,25 +315,6 @@ header { ...@@ -313,25 +315,6 @@ header {
.impersonation i { .impersonation i {
color: $red-500; color: $red-500;
} }
// TODO: fallback to global style
.dropdown-menu,
.dropdown-menu-nav {
li {
padding: 0 1px;
a {
border-radius: 0;
padding: 8px 16px;
&:hover,
&:active,
&:focus {
background-color: $gray-darker;
}
}
}
}
} }
.with-performance-bar header.navbar-gitlab { .with-performance-bar header.navbar-gitlab {
...@@ -342,9 +325,9 @@ header { ...@@ -342,9 +325,9 @@ header {
li { li {
.badge { .badge {
position: inherit; position: inherit;
top: -3px; top: -8px;
font-weight: normal; font-weight: normal;
margin-left: -12px; margin-left: -11px;
font-size: 11px; font-size: 11px;
color: $white-light; color: $white-light;
padding: 1px 5px 2px; padding: 1px 5px 2px;
......
.media {
display: flex;
align-items: flex-start;
}
.media-body {
flex: 1;
}
...@@ -207,7 +207,6 @@ $general-hover-transition-curve: linear; ...@@ -207,7 +207,6 @@ $general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232); $highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px; $performance-bar-height: 35px;
/* /*
* Common component specific colors * Common component specific colors
*/ */
...@@ -316,6 +315,12 @@ $btn-white-active: #848484; ...@@ -316,6 +315,12 @@ $btn-white-active: #848484;
$badge-bg: rgba(0, 0, 0, 0.07); $badge-bg: rgba(0, 0, 0, 0.07);
$badge-color: $gl-text-color-secondary; $badge-color: $gl-text-color-secondary;
/*
* Status icons
*/
$status-icon-size: 22px;
$status-icon-margin: $gl-btn-padding;
/* /*
* Award emoji * Award emoji
*/ */
......
...@@ -312,6 +312,10 @@ header.navbar-gitlab-new { ...@@ -312,6 +312,10 @@ header.navbar-gitlab-new {
// TODO: fallback to global style // TODO: fallback to global style
.dropdown-menu { .dropdown-menu {
.divider {
margin: 6px 0;
}
li { li {
padding: 0 1px; padding: 0 1px;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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