Merge remote-tracking branch 'upstream/master' into use-update-runner-service
* upstream/master: (488 commits) Remove duplicate CHANGELOG.md entries for 8.16.5 Update CHANGELOG.md for 8.14.9 Update CHANGELOG.md for 8.15.6 #27631: Add missing top-area div to activity header page Update CHANGELOG.md for 8.16.5 Update CHANGELOG.md for 8.16.5 Update CHANGELOG.md for 8.16.5 Fix yarn lock and package.json mismatch caused by MR 9133 sync yarn.lock with recent changes to package.json Add changelog Fix z index bugs Add Links to Branches in Calendar Activity SidekiqStatus need to be qualified in some cases Replace static fixture for behaviors/requires_input_spec.js (!9162) API: Consolidate /projects endpoint Add MySQL info in install requirements Fix timezone on issue boards due date Use Gitlab::Database.with_connection_pool from !9192 Disconnect the pool after done Use threads directly, introduce pool later: ...
This commit is contained in:
commit
a065ee341c
14
.eslintrc
14
.eslintrc
|
@ -12,12 +12,18 @@
|
|||
"localStorage": false
|
||||
},
|
||||
"plugins": [
|
||||
"filenames"
|
||||
"filenames",
|
||||
"import"
|
||||
],
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": "./config/webpack.config.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
|
||||
"no-multiple-empty-lines": ["error", { "max": 1 }],
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/no-unresolved": "off"
|
||||
"no-multiple-empty-lines": ["error", { "max": 1 }]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
*.erb
|
||||
lib/gitlab/sanitizers/svg/whitelist.rb
|
||||
lib/gitlab/diff/position_tracer.rb
|
||||
app/policies/project_policy.rb
|
||||
|
|
|
@ -107,7 +107,10 @@ setup-test-env:
|
|||
<<: *dedicated-runner
|
||||
stage: prepare
|
||||
script:
|
||||
- npm install
|
||||
- node --version
|
||||
- yarn --version
|
||||
- yarn install --pure-lockfile
|
||||
- yarn check # ensure that yarn.lock matches package.json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||
artifacts:
|
||||
|
@ -163,64 +166,7 @@ spinach 7 10: *spinach-knapsack
|
|||
spinach 8 10: *spinach-knapsack
|
||||
spinach 9 10: *spinach-knapsack
|
||||
|
||||
# Execute all testing suites against Ruby 2.1
|
||||
.ruby-21: &ruby-21
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
|
||||
<<: *use-db
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
cache:
|
||||
key: "ruby21"
|
||||
paths:
|
||||
- vendor/ruby
|
||||
|
||||
.rspec-knapsack-ruby21: &rspec-knapsack-ruby21
|
||||
<<: *rspec-knapsack
|
||||
<<: *dedicated-runner
|
||||
<<: *ruby-21
|
||||
|
||||
.spinach-knapsack-ruby21: &spinach-knapsack-ruby21
|
||||
<<: *spinach-knapsack
|
||||
<<: *dedicated-runner
|
||||
<<: *ruby-21
|
||||
|
||||
rspec 0 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 1 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 2 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 3 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 4 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 5 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 6 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 7 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 8 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 9 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 10 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 11 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 12 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 13 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 14 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 15 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 16 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 17 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 18 20 ruby21: *rspec-knapsack-ruby21
|
||||
rspec 19 20 ruby21: *rspec-knapsack-ruby21
|
||||
|
||||
spinach 0 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 1 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 2 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 3 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 4 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 5 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 6 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 7 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 8 10 ruby21: *spinach-knapsack-ruby21
|
||||
spinach 9 10 ruby21: *spinach-knapsack-ruby21
|
||||
|
||||
# Other generic tests
|
||||
|
||||
.ruby-static-analysis: &ruby-static-analysis
|
||||
variables:
|
||||
SIMPLECOV: "false"
|
||||
|
@ -243,6 +189,7 @@ rubocop:
|
|||
|
||||
rake haml_lint: *exec
|
||||
rake scss_lint: *exec
|
||||
rake config_lint: *exec
|
||||
rake brakeman: *exec
|
||||
rake flay: *exec
|
||||
license_finder: *exec
|
||||
|
@ -302,13 +249,12 @@ karma:
|
|||
<<: *use-db
|
||||
<<: *dedicated-runner
|
||||
script:
|
||||
- npm link istanbul
|
||||
- bundle exec rake karma
|
||||
artifacts:
|
||||
name: coverage-javascript
|
||||
expire_in: 31d
|
||||
paths:
|
||||
- coverage-javascript/default/
|
||||
- coverage-javascript/
|
||||
|
||||
lint-doc:
|
||||
stage: test
|
||||
|
@ -381,11 +327,9 @@ lint:javascript:
|
|||
paths:
|
||||
- node_modules/
|
||||
stage: test
|
||||
image: "node:7.1"
|
||||
before_script:
|
||||
- npm install
|
||||
before_script: []
|
||||
script:
|
||||
- npm --silent run eslint
|
||||
- yarn run eslint
|
||||
|
||||
lint:javascript:report:
|
||||
<<: *dedicated-runner
|
||||
|
@ -393,12 +337,10 @@ lint:javascript:report:
|
|||
paths:
|
||||
- node_modules/
|
||||
stage: post-test
|
||||
image: "node:7.1"
|
||||
before_script:
|
||||
- npm install
|
||||
before_script: []
|
||||
script:
|
||||
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
|
||||
- npm --silent run eslint-report || true # ignore exit code
|
||||
- yarn run eslint-report || true # ignore exit code
|
||||
artifacts:
|
||||
name: eslint-report
|
||||
expire_in: 31d
|
||||
|
@ -450,9 +392,9 @@ pages:
|
|||
script:
|
||||
- mv public/ .public/
|
||||
- mkdir public/
|
||||
- mv coverage public/coverage-ruby
|
||||
- mv coverage-javascript/default/ public/coverage-javascript/
|
||||
- mv eslint-report.html public/
|
||||
- mv coverage/ public/coverage-ruby/ || true
|
||||
- mv coverage-javascript/ public/coverage-javascript/ || true
|
||||
- mv eslint-report.html public/ || true
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
(How one can reproduce the issue - this is very important)
|
||||
|
||||
### Expected behavior
|
||||
|
||||
(What you should see instead)
|
||||
|
||||
### Actual behavior
|
||||
### What is the current *bug* behavior?
|
||||
|
||||
(What actually happens)
|
||||
|
||||
### What is the expected *correct* behavior?
|
||||
|
||||
(What you should see instead)
|
||||
|
||||
### Relevant logs and/or screenshots
|
||||
|
||||
(Paste any relevant logs - please use code blocks (```) to format console output,
|
||||
|
@ -23,6 +23,14 @@ logs, and code as it's very hard to read otherwise.)
|
|||
|
||||
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
|
||||
|
||||
#### Results of GitLab environment info
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
`sudo gitlab-rake gitlab:env:info`)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
|
||||
|
||||
#### Results of GitLab application Check
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
|
@ -33,14 +41,6 @@ logs, and code as it's very hard to read otherwise.)
|
|||
|
||||
(we will only investigate if the tests are passing)
|
||||
|
||||
#### Results of GitLab environment info
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
`sudo gitlab-rake gitlab:env:info`)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
|
||||
|
||||
### Possible fixes
|
||||
|
||||
(If you can, link to the line of code that might be responsible for the problem)
|
||||
|
|
40
.rubocop.yml
40
.rubocop.yml
|
@ -5,7 +5,7 @@ require:
|
|||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.1
|
||||
TargetRubyVersion: 2.3
|
||||
# Cop names are not d§splayed in offense messages by default. Change behavior
|
||||
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
||||
# option.
|
||||
|
@ -31,8 +31,7 @@ AllCops:
|
|||
- 'lib/gitlab/seeder.rb'
|
||||
- 'generator_templates/**/*'
|
||||
|
||||
|
||||
##################### Style ##################################
|
||||
# Style #######################################################################
|
||||
|
||||
# Check indentation of private/protected visibility modifiers.
|
||||
Style/AccessModifierIndentation:
|
||||
|
@ -340,6 +339,10 @@ Style/OpMethod:
|
|||
Style/ParenthesesAroundCondition:
|
||||
Enabled: true
|
||||
|
||||
# Checks for an obsolete RuntimeException argument in raise/fail.
|
||||
Style/RedundantException:
|
||||
Enabled: true
|
||||
|
||||
# Checks for parentheses that seem not to serve any purpose.
|
||||
Style/RedundantParentheses:
|
||||
Enabled: true
|
||||
|
@ -471,7 +474,7 @@ Style/WhileUntilModifier:
|
|||
Style/WordArray:
|
||||
Enabled: false
|
||||
|
||||
#################### Metrics ################################
|
||||
# Metrics #####################################################################
|
||||
|
||||
# A calculated magnitude based on number of assignments,
|
||||
# branches, and conditions.
|
||||
|
@ -516,8 +519,7 @@ Metrics/PerceivedComplexity:
|
|||
Enabled: true
|
||||
Max: 18
|
||||
|
||||
|
||||
#################### Lint ################################
|
||||
# Lint ########################################################################
|
||||
|
||||
# Checks for useless access modifiers.
|
||||
Lint/UselessAccessModifier:
|
||||
|
@ -570,6 +572,10 @@ Lint/ElseLayout:
|
|||
Lint/EmptyEnsure:
|
||||
Enabled: true
|
||||
|
||||
# Checks for the presence of `when` branches without a body.
|
||||
Lint/EmptyWhen:
|
||||
Enabled: true
|
||||
|
||||
# Align ends correctly.
|
||||
Lint/EndAlignment:
|
||||
Enabled: true
|
||||
|
@ -679,8 +685,7 @@ Lint/UselessSetterCall:
|
|||
Lint/Void:
|
||||
Enabled: true
|
||||
|
||||
|
||||
##################### Performance ############################
|
||||
# Performance #################################################################
|
||||
|
||||
# Use `casecmp` rather than `downcase ==`.
|
||||
Performance/Casecmp:
|
||||
|
@ -718,8 +723,7 @@ Performance/StringReplacement:
|
|||
Performance/TimesMap:
|
||||
Enabled: true
|
||||
|
||||
|
||||
##################### Rails ##################################
|
||||
# Rails #######################################################################
|
||||
|
||||
# Enables Rails cops.
|
||||
Rails:
|
||||
|
@ -767,12 +771,16 @@ Rails/ReadWriteAttribute:
|
|||
Rails/ScopeArgs:
|
||||
Enabled: true
|
||||
|
||||
##################### RSpec ##################################
|
||||
# RSpec #######################################################################
|
||||
|
||||
# Check that instances are not being stubbed globally.
|
||||
RSpec/AnyInstance:
|
||||
Enabled: false
|
||||
|
||||
# Check for expectations where `be(...)` can replace `eql(...)`.
|
||||
RSpec/BeEql:
|
||||
Enabled: false
|
||||
|
||||
# Check that the first argument to the top level describe is the tested class or
|
||||
# module.
|
||||
RSpec/DescribeClass:
|
||||
|
@ -801,6 +809,10 @@ RSpec/ExampleWording:
|
|||
not: does not
|
||||
IgnoredWords: []
|
||||
|
||||
# Checks for `expect(...)` calls containing literal values.
|
||||
RSpec/ExpectActual:
|
||||
Enabled: true
|
||||
|
||||
# Checks the file and folder naming of the spec file.
|
||||
RSpec/FilePath:
|
||||
Enabled: false
|
||||
|
@ -828,3 +840,9 @@ RSpec/NotToNot:
|
|||
# Prefer using verifying doubles over normal doubles.
|
||||
RSpec/VerifiedDoubles:
|
||||
Enabled: false
|
||||
|
||||
# Custom ######################################################################
|
||||
|
||||
# Disallow the `git` and `github` arguments in the Gemfile.
|
||||
GemFetcher:
|
||||
Enabled: true
|
||||
|
|
|
@ -21,10 +21,6 @@ Lint/AmbiguousRegexpLiteral:
|
|||
Lint/AssignmentInCondition:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 1
|
||||
Lint/EmptyWhen:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 20
|
||||
Lint/HandleExceptions:
|
||||
Enabled: false
|
||||
|
@ -80,19 +76,11 @@ Performance/RedundantMatch:
|
|||
Performance/RedundantMerge:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 7
|
||||
RSpec/BeEql:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 15
|
||||
# Configuration parameters: CustomIncludeMethods.
|
||||
RSpec/EmptyExampleGroup:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
RSpec/ExpectActual:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 58
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: implicit, each, example
|
||||
|
@ -424,11 +412,6 @@ Style/RaiseArgs:
|
|||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 1
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantException:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 29
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantFreeze:
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -2,6 +2,13 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 8.16.5 (2017-02-14)
|
||||
|
||||
- Patch Asciidocs rendering to block XSS.
|
||||
- Fix XSS vulnerability in SVG attachments.
|
||||
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
|
||||
- Patch XSS vulnerability in RDOC support.
|
||||
|
||||
## 8.16.4 (2017-02-02)
|
||||
|
||||
- Support non-ASCII characters in GFM autocomplete. !8729
|
||||
|
@ -174,6 +181,13 @@ entry.
|
|||
- Add margin to markdown math blocks.
|
||||
- Add hover state to MR comment reply button.
|
||||
|
||||
## 8.15.6 (2017-02-14)
|
||||
|
||||
- Patch Asciidocs rendering to block XSS.
|
||||
- Fix XSS vulnerability in SVG attachments.
|
||||
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
|
||||
- Patch XSS vulnerability in RDOC support.
|
||||
|
||||
## 8.15.4 (2017-01-09)
|
||||
|
||||
- Make successful pipeline emails off for watchers. !8176
|
||||
|
@ -437,6 +451,13 @@ entry.
|
|||
- Whitelist next project names: help, ci, admin, search. !8227
|
||||
- Adds back CSS for progress-bars. !8237
|
||||
|
||||
## 8.14.9 (2017-02-14)
|
||||
|
||||
- Patch Asciidocs rendering to block XSS.
|
||||
- Fix XSS vulnerability in SVG attachments.
|
||||
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
|
||||
- Patch XSS vulnerability in RDOC support.
|
||||
|
||||
## 8.14.8 (2017-01-25)
|
||||
|
||||
- Accept environment variables from the `pre-receive` script. !7967
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical debt](#technical-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Merge requests](#merge-requests)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
||||
|
@ -230,6 +231,21 @@ for a release by the appropriate person.
|
|||
Make sure to mention the merge request that the `technical debt` issue is
|
||||
associated with in the description of the issue.
|
||||
|
||||
### Stewardship
|
||||
|
||||
For issues related to the open source stewardship of GitLab,
|
||||
there is the ~"stewardship" label.
|
||||
|
||||
This label is to be used for issues in which the stewardship of GitLab
|
||||
is a topic of discussion. For instance if GitLab Inc. is planning to remove
|
||||
features from GitLab CE to make exclusive in GitLab EE, related issues
|
||||
would be labelled with ~"stewardship".
|
||||
|
||||
A recent example of this was the issue for
|
||||
[bringing the time tracking API to GitLab CE][time-tracking-issue].
|
||||
|
||||
[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
|
||||
|
||||
## Merge requests
|
||||
|
||||
We welcome merge requests with fixes and improvements to GitLab code, tests,
|
||||
|
@ -414,7 +430,7 @@ merge request:
|
|||
1. [Newlines styleguide][newlines-styleguide]
|
||||
1. [Testing](doc/development/testing.md)
|
||||
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
|
||||
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
|
||||
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5)
|
||||
1. [SCSS styleguide][scss-styleguide]
|
||||
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
|
||||
contributors to enhance security
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.2.4
|
||||
0.3.0
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -29,6 +29,7 @@ gem 'omniauth-github', '~> 1.1.1'
|
|||
gem 'omniauth-gitlab', '~> 1.0.2'
|
||||
gem 'omniauth-google-oauth2', '~> 0.4.1'
|
||||
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
|
||||
gem 'omniauth-oauth2-generic', '~> 0.2.2'
|
||||
gem 'omniauth-saml', '~> 1.7.0'
|
||||
gem 'omniauth-shibboleth', '~> 1.2.0'
|
||||
gem 'omniauth-twitter', '~> 1.2.0'
|
||||
|
@ -284,7 +285,7 @@ group :development, :test do
|
|||
gem 'rspec-retry', '~> 0.4.5'
|
||||
gem 'spinach-rails', '~> 0.2.1'
|
||||
gem 'spinach-rerun-reporter', '~> 0.0.2'
|
||||
gem 'rspec_profiling'
|
||||
gem 'rspec_profiling', '~> 0.0.5'
|
||||
|
||||
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
|
||||
gem 'minitest', '~> 5.7.0'
|
||||
|
|
11
Gemfile.lock
11
Gemfile.lock
|
@ -483,6 +483,8 @@ GEM
|
|||
omniauth-oauth2 (1.3.1)
|
||||
oauth2 (~> 1.0)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-oauth2-generic (0.2.2)
|
||||
omniauth-oauth2 (~> 1.0)
|
||||
omniauth-saml (1.7.0)
|
||||
omniauth (~> 1.3)
|
||||
ruby-saml (~> 1.4)
|
||||
|
@ -638,7 +640,7 @@ GEM
|
|||
rspec-retry (0.4.5)
|
||||
rspec-core
|
||||
rspec-support (3.5.0)
|
||||
rspec_profiling (0.0.4)
|
||||
rspec_profiling (0.0.5)
|
||||
activerecord
|
||||
pg
|
||||
rails
|
||||
|
@ -738,7 +740,7 @@ GEM
|
|||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.3.11)
|
||||
sqlite3 (1.3.13)
|
||||
stackprof (0.2.10)
|
||||
state_machines (0.4.0)
|
||||
state_machines-activemodel (0.4.0)
|
||||
|
@ -931,6 +933,7 @@ DEPENDENCIES
|
|||
omniauth-gitlab (~> 1.0.2)
|
||||
omniauth-google-oauth2 (~> 0.4.1)
|
||||
omniauth-kerberos (~> 0.3.0)
|
||||
omniauth-oauth2-generic (~> 0.2.2)
|
||||
omniauth-saml (~> 1.7.0)
|
||||
omniauth-shibboleth (~> 1.2.0)
|
||||
omniauth-twitter (~> 1.2.0)
|
||||
|
@ -962,7 +965,7 @@ DEPENDENCIES
|
|||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-rails (~> 3.5.0)
|
||||
rspec-retry (~> 0.4.5)
|
||||
rspec_profiling
|
||||
rspec_profiling (~> 0.0.5)
|
||||
rubocop (~> 0.46.0)
|
||||
rubocop-rspec (~> 1.9.1)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
|
@ -1011,4 +1014,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.14.2
|
||||
1.14.3
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2011-2016 GitLab B.V.
|
||||
Copyright (c) 2011-2017 GitLab B.V.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
64
PROCESS.md
64
PROCESS.md
|
@ -33,7 +33,7 @@ core team members will mention this person.
|
|||
### Merge request coaching
|
||||
|
||||
Several people from the [GitLab team][team] are helping community members to get
|
||||
their contributions accepted by meeting our [Definition of done][CONTRIBUTING.md#definition-of-done].
|
||||
their contributions accepted by meeting our [Definition of done](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#definition-of-done).
|
||||
|
||||
What you can expect from them is described at https://about.gitlab.com/jobs/merge-request-coach/.
|
||||
|
||||
|
@ -59,67 +59,85 @@ star, smile, etc.). Some good tips about code reviews can be found in our
|
|||
|
||||
## Feature Freeze
|
||||
|
||||
On the 7th of each month, the stable branches for the upcoming release will
|
||||
be frozen for major changes. Merge requests may still be merged into master
|
||||
during this period. By freezing the stable branches prior to a release there's
|
||||
no need to worry about last minute merge requests potentially breaking a lot of
|
||||
things.
|
||||
After the 7th (Pacific Standard Time Zone) of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
|
||||
Merge requests may still be merged into master during this period,
|
||||
but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
|
||||
By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
|
||||
|
||||
What is considered to be a major change is determined on a case by case basis as
|
||||
this definition depends very much on the context of changes. For example, a 5
|
||||
line change might have a big impact on the entire application. Ultimately the
|
||||
decision will be made by the maintainers and the release managers.
|
||||
Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release)
|
||||
and security issues will be cherry-picked into the stable branch.
|
||||
Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch.
|
||||
These fixes will be released in the next RC (before the 22nd) or patch release (after the 22nd).
|
||||
|
||||
If you think a merge request should go into the upcoming release even though it does not meet these requirements,
|
||||
you can ask for an exception to be made. Exceptions require sign-off from 3 people besides the developer:
|
||||
|
||||
1. a Release Manager
|
||||
2. an Engineering Lead
|
||||
3. an Engineering Director, the VP of Engineering, or the CTO
|
||||
|
||||
You can find who is who on the [team page](https://about.gitlab.com/team/).
|
||||
|
||||
Whether an exception is made is determined by weighing the benefit and urgency of the change
|
||||
(how important it is to the company that this is released _right now_ instead of in a month)
|
||||
against the potential negative impact
|
||||
(things breaking without enough time to comfortably find and fix them before the release on the 22nd).
|
||||
When in doubt, we err on the side of _not_ cherry-picking.
|
||||
|
||||
For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
|
||||
(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
|
||||
|
||||
During the feature freeze all merge requests that are meant to go into the upcoming
|
||||
release should have the correct milestone assigned _and_ have the label
|
||||
~"Pick into Stable" set. Merge requests without a milestone and this label will
|
||||
~"Pick into Stable" set, so that release managers can find and pick them.
|
||||
Merge requests without a milestone and this label will
|
||||
not be merged into any stable branches.
|
||||
|
||||
## Copy & paste responses
|
||||
|
||||
### Improperly formatted issue
|
||||
|
||||
Thanks for the issue report. Please reformat your issue to conform to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
Thanks for the issue report. Please reformat your issue to conform to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Issue report for old version
|
||||
|
||||
Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Support requests and configuration questions
|
||||
|
||||
Thanks for your interest in GitLab. We don't use the issue tracker for support
|
||||
requests and configuration questions. Please check our
|
||||
\[getting help\]\(https://about.gitlab.com/getting-help/) page to see all of the available
|
||||
support options. Also, have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
|
||||
[getting help](https://about.gitlab.com/getting-help/) page to see all of the available
|
||||
support options. Also, have a look at the [contribution guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
|
||||
for more information.
|
||||
|
||||
### Code format
|
||||
|
||||
Please use ``` to format console output, logs, and code as it's very hard to read otherwise.
|
||||
Please use \`\`\` to format console output, logs, and code as it's very hard to read otherwise.
|
||||
|
||||
### Issue fixed in newer version
|
||||
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please [upgrade](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Improperly formatted merge request
|
||||
|
||||
Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-request-guidelines).
|
||||
Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-request-guidelines).
|
||||
|
||||
### Inactivity close of an issue
|
||||
|
||||
It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Inactivity close of a merge request
|
||||
|
||||
This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this merge request.
|
||||
This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this merge request.
|
||||
|
||||
### Accepting merge requests
|
||||
|
||||
Is there an issue on the
|
||||
\[issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues) that is
|
||||
[issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) that is
|
||||
similar to this? Could you please link it here?
|
||||
Please be aware that new functionality that is not marked
|
||||
\[accepting merge requests\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests)
|
||||
[accepting merge requests](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests)
|
||||
might not make it into GitLab.
|
||||
|
||||
### Only accepting merge requests with green tests
|
||||
|
@ -134,7 +152,7 @@ rebase with master to see if that solves the issue.
|
|||
We are currently in the process of closing down the issue tracker on GitHub, to
|
||||
prevent duplication with the GitLab.com issue tracker.
|
||||
Since this is an older issue I'll be closing this for now. If you think this is
|
||||
still an issue I encourage you to open it on the \[GitLab.com issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues).
|
||||
still an issue I encourage you to open it on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues).
|
||||
|
||||
[team]: https://about.gitlab.com/team/
|
||||
[contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
licensePath: "/api/:version/templates/licenses/:key",
|
||||
gitignorePath: "/api/:version/templates/gitignores/:key",
|
||||
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
|
||||
dockerfilePath: "/api/:version/dockerfiles/:key",
|
||||
dockerfilePath: "/api/:version/templates/dockerfiles/:key",
|
||||
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
|
||||
group: function(group_id, callback) {
|
||||
var url = Api.buildUrl(Api.groupPath)
|
||||
|
|
|
@ -10,7 +10,6 @@ function requireAll(context) { return context.keys().map(context); }
|
|||
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
require('jquery-ui/ui/autocomplete');
|
||||
require('jquery-ui/ui/datepicker');
|
||||
require('jquery-ui/ui/draggable');
|
||||
require('jquery-ui/ui/effect-highlight');
|
||||
require('jquery-ui/ui/sortable');
|
||||
|
@ -21,7 +20,7 @@ require('vendor/jquery.waitforimages');
|
|||
require('vendor/jquery.caret');
|
||||
require('vendor/jquery.atwho');
|
||||
require('vendor/jquery.scrollTo');
|
||||
window.Cookies = require('vendor/js.cookie');
|
||||
window.Cookies = require('js-cookie');
|
||||
require('./autosave');
|
||||
require('bootstrap/js/affix');
|
||||
require('bootstrap/js/alert');
|
||||
|
@ -35,8 +34,10 @@ require('bootstrap/js/transition');
|
|||
require('bootstrap/js/tooltip');
|
||||
require('bootstrap/js/popover');
|
||||
require('select2/select2.js');
|
||||
window.Pikaday = require('pikaday');
|
||||
window._ = require('underscore');
|
||||
window.Dropzone = require('dropzone');
|
||||
window.Sortable = require('vendor/Sortable');
|
||||
require('mousetrap');
|
||||
require('mousetrap/plugins/pause/mousetrap-pause');
|
||||
require('./shortcuts');
|
||||
|
@ -55,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
|
|||
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
|
||||
require('vendor/fuzzaldrin-plus');
|
||||
window.ES6Promise = require('vendor/es6-promise.auto');
|
||||
window.ES6Promise.polyfill();
|
||||
require('es6-promise').polyfill();
|
||||
|
||||
(function () {
|
||||
document.addEventListener('beforeunload', function () {
|
||||
|
@ -247,5 +247,7 @@ window.ES6Promise.polyfill();
|
|||
new Aside();
|
||||
// bind sidebar events
|
||||
new gl.Sidebar();
|
||||
|
||||
gl.utils.initTimeagoTimeout();
|
||||
});
|
||||
}).call(this);
|
||||
|
|
|
@ -6,7 +6,6 @@ function requireAll(context) { return context.keys().map(context); }
|
|||
|
||||
window.Vue = require('vue');
|
||||
window.Vue.use(require('vue-resource'));
|
||||
window.Sortable = require('vendor/Sortable');
|
||||
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
|
||||
|
@ -16,7 +15,7 @@ require('./components/board');
|
|||
require('./components/board_sidebar');
|
||||
require('./components/new_list_dropdown');
|
||||
require('./components/modal/index');
|
||||
require('./vue_resource_interceptor');
|
||||
require('../vue_shared/vue_resource_interceptor');
|
||||
|
||||
$(() => {
|
||||
const $boardApp = document.getElementById('board-app');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* global Vue */
|
||||
/* global dateFormat */
|
||||
|
||||
Vue.filter('due-date', (value) => {
|
||||
const date = new Date(value);
|
||||
return $.datepicker.formatDate('M d, yy', date);
|
||||
return dateFormat(date, 'mmm d, yyyy', true);
|
||||
});
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars */
|
||||
/* global Vue */
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
|
||||
|
||||
next(function (response) {
|
||||
Vue.activeResources -= 1;
|
||||
});
|
||||
});
|
|
@ -67,16 +67,8 @@
|
|||
|
||||
Build.prototype.initSidebar = function() {
|
||||
this.$sidebar = $('.js-build-sidebar');
|
||||
this.sidebarTranslationLimits = {
|
||||
min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
|
||||
};
|
||||
this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
|
||||
this.$sidebar.css({
|
||||
top: this.sidebarTranslationLimits.max
|
||||
});
|
||||
this.$sidebar.niceScroll();
|
||||
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
|
||||
this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
|
||||
};
|
||||
|
||||
Build.prototype.location = function() {
|
||||
|
@ -231,14 +223,6 @@
|
|||
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
|
||||
};
|
||||
|
||||
Build.prototype.translateSidebar = function(e) {
|
||||
var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop);
|
||||
if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
|
||||
this.$sidebar.css({
|
||||
top: newPosition
|
||||
});
|
||||
};
|
||||
|
||||
Build.prototype.toggleSidebar = function(shouldHide) {
|
||||
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
|
||||
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
|
||||
|
@ -285,7 +269,7 @@
|
|||
e.preventDefault();
|
||||
$currentTarget = $(e.currentTarget);
|
||||
$.scrollTo($currentTarget.attr('href'), {
|
||||
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
|
||||
offset: 0
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/* eslint-disable no-new, no-param-reassign */
|
||||
/* global Vue, CommitsPipelineStore, PipelinesService, Flash */
|
||||
|
||||
window.Vue = require('vue');
|
||||
require('./pipelines_table');
|
||||
/**
|
||||
* Commits View > Pipelines Tab > Pipelines Table.
|
||||
* Merge Request View > Pipelines Tab > Pipelines Table.
|
||||
*
|
||||
* Renders Pipelines table in pipelines tab in the commits show view.
|
||||
* Renders Pipelines table in pipelines tab in the merge request show view.
|
||||
*/
|
||||
|
||||
$(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.commits = gl.commits || {};
|
||||
gl.commits.pipelines = gl.commits.pipelines || {};
|
||||
|
||||
if (gl.commits.PipelinesTableBundle) {
|
||||
gl.commits.PipelinesTableBundle.$destroy(true);
|
||||
}
|
||||
|
||||
gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView({
|
||||
el: document.querySelector('#commit-pipeline-table-view'),
|
||||
});
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
/* globals Vue */
|
||||
/* eslint-disable no-unused-vars, no-param-reassign */
|
||||
|
||||
/**
|
||||
* Pipelines service.
|
||||
*
|
||||
* Used to fetch the data used to render the pipelines table.
|
||||
* Uses Vue.Resource
|
||||
*/
|
||||
class PipelinesService {
|
||||
|
||||
/**
|
||||
* FIXME: The url provided to request the pipelines in the new merge request
|
||||
* page already has `.json`.
|
||||
* This should be fixed when the endpoint is improved.
|
||||
*
|
||||
* @param {String} root
|
||||
*/
|
||||
constructor(root) {
|
||||
let endpoint;
|
||||
|
||||
if (root.indexOf('.json') === -1) {
|
||||
endpoint = `${root}.json`;
|
||||
} else {
|
||||
endpoint = root;
|
||||
}
|
||||
this.pipelines = Vue.resource(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the root param provided when the class is initialized, will
|
||||
* make a GET request.
|
||||
*
|
||||
* @return {Promise}
|
||||
*/
|
||||
all() {
|
||||
return this.pipelines.get();
|
||||
}
|
||||
}
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.commits = gl.commits || {};
|
||||
gl.commits.pipelines = gl.commits.pipelines || {};
|
||||
gl.commits.pipelines.PipelinesService = PipelinesService;
|
|
@ -0,0 +1,50 @@
|
|||
/* eslint-disable no-underscore-dangle*/
|
||||
/**
|
||||
* Pipelines' Store for commits view.
|
||||
*
|
||||
* Used to store the Pipelines rendered in the commit view in the pipelines table.
|
||||
*/
|
||||
|
||||
class PipelinesStore {
|
||||
constructor() {
|
||||
this.state = {};
|
||||
this.state.pipelines = [];
|
||||
}
|
||||
|
||||
storePipelines(pipelines = []) {
|
||||
this.state.pipelines = pipelines;
|
||||
|
||||
return pipelines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the data is received we will start the time ago loops.
|
||||
*
|
||||
* Everytime a request is made like retry or cancel a pipeline, every 10 seconds we
|
||||
* update the time to show how long as passed.
|
||||
*
|
||||
*/
|
||||
startTimeAgoLoops() {
|
||||
const startTimeLoops = () => {
|
||||
this.timeLoopInterval = setInterval(() => {
|
||||
this.$children[0].$children.reduce((acc, component) => {
|
||||
const timeAgoComponent = component.$children.filter(el => el.$options._componentTag === 'time-ago')[0];
|
||||
acc.push(timeAgoComponent);
|
||||
return acc;
|
||||
}, []).forEach(e => e.changeTime());
|
||||
}, 10000);
|
||||
};
|
||||
|
||||
startTimeLoops();
|
||||
|
||||
const removeIntervals = () => clearInterval(this.timeLoopInterval);
|
||||
const startIntervals = () => startTimeLoops();
|
||||
|
||||
gl.VueRealtimeListener(removeIntervals, startIntervals);
|
||||
}
|
||||
}
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.commits = gl.commits || {};
|
||||
gl.commits.pipelines = gl.commits.pipelines || {};
|
||||
gl.commits.pipelines.PipelinesStore = PipelinesStore;
|
|
@ -0,0 +1,107 @@
|
|||
/* eslint-disable no-new, no-param-reassign */
|
||||
/* global Vue, CommitsPipelineStore, PipelinesService, Flash */
|
||||
|
||||
window.Vue = require('vue');
|
||||
window.Vue.use(require('vue-resource'));
|
||||
require('../../lib/utils/common_utils');
|
||||
require('../../vue_shared/vue_resource_interceptor');
|
||||
require('../../vue_shared/components/pipelines_table');
|
||||
require('../../vue_realtime_listener/index');
|
||||
require('./pipelines_service');
|
||||
require('./pipelines_store');
|
||||
|
||||
/**
|
||||
*
|
||||
* Uses `pipelines-table-component` to render Pipelines table with an API call.
|
||||
* Endpoint is provided in HTML and passed as `endpoint`.
|
||||
* We need a store to store the received environemnts.
|
||||
* We need a service to communicate with the server.
|
||||
*
|
||||
* Necessary SVG in the table are provided as props. This should be refactored
|
||||
* as soon as we have Webpack and can load them directly into JS files.
|
||||
*/
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.commits = gl.commits || {};
|
||||
gl.commits.pipelines = gl.commits.pipelines || {};
|
||||
|
||||
gl.commits.pipelines.PipelinesTableView = Vue.component('pipelines-table', {
|
||||
|
||||
components: {
|
||||
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
|
||||
},
|
||||
|
||||
/**
|
||||
* Accesses the DOM to provide the needed data.
|
||||
* Returns the necessary props to render `pipelines-table-component` component.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
data() {
|
||||
const pipelinesTableData = document.querySelector('#commit-pipeline-table-view').dataset;
|
||||
const svgsData = document.querySelector('.pipeline-svgs').dataset;
|
||||
const store = new gl.commits.pipelines.PipelinesStore();
|
||||
|
||||
// Transform svgs DOMStringMap to a plain Object.
|
||||
const svgsObject = gl.utils.DOMStringMapToObject(svgsData);
|
||||
|
||||
return {
|
||||
endpoint: pipelinesTableData.endpoint,
|
||||
svgs: svgsObject,
|
||||
store,
|
||||
state: store.state,
|
||||
isLoading: false,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* When the component is created the service to fetch the data will be
|
||||
* initialized with the correct endpoint.
|
||||
*
|
||||
* A request to fetch the pipelines will be made.
|
||||
* In case of a successfull response we will store the data in the provided
|
||||
* store, in case of a failed response we need to warn the user.
|
||||
*
|
||||
*/
|
||||
created() {
|
||||
const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint);
|
||||
|
||||
this.isLoading = true;
|
||||
return pipelinesService.all()
|
||||
.then(response => response.json())
|
||||
.then((json) => {
|
||||
this.store.storePipelines(json);
|
||||
this.store.startTimeAgoLoops.call(this, Vue);
|
||||
this.isLoading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.isLoading = false;
|
||||
new Flash('An error occurred while fetching the pipelines, please reload the page again.', 'alert');
|
||||
});
|
||||
},
|
||||
|
||||
template: `
|
||||
<div>
|
||||
<div class="pipelines realtime-loading" v-if="isLoading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
|
||||
<div class="blank-state blank-state-no-icon"
|
||||
v-if="!isLoading && state.pipelines.length === 0">
|
||||
<h2 class="blank-state-title js-blank-state-title">
|
||||
No pipelines to show
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="table-holder pipelines"
|
||||
v-if="!isLoading && state.pipelines.length > 0">
|
||||
<pipelines-table-component
|
||||
:pipelines="state.pipelines"
|
||||
:svgs="svgs">
|
||||
</pipelines-table-component>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
})();
|
|
@ -91,6 +91,9 @@ require('./lib/utils/common_utils');
|
|||
},
|
||||
},
|
||||
SanitizationFilter: {
|
||||
'a[name]:not([href]):empty'(el, text) {
|
||||
return el.outerHTML;
|
||||
},
|
||||
'dl'(el, text) {
|
||||
let lines = text.trim().split('\n');
|
||||
// Add two spaces to the front of subsequent list items lines,
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
<div>
|
||||
<div class="events-description">
|
||||
{{ stage.description }}
|
||||
<span v-if="items.length === 50" class="events-info pull-right">
|
||||
<i class="fa fa-warning has-tooltip"
|
||||
title="Limited to showing 50 events at most"
|
||||
data-placement="top"></i>
|
||||
Showing 50 events
|
||||
</span>
|
||||
</div>
|
||||
<ul class="stage-event-list">
|
||||
<li v-for="commit in items" class="stage-event-item">
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/* global Flash */
|
||||
|
||||
window.Vue = require('vue');
|
||||
window.Cookies = require('vendor/js.cookie');
|
||||
window.Cookies = require('js-cookie');
|
||||
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
|
||||
|
|
|
@ -76,7 +76,7 @@ require('./lib/utils/url_utility');
|
|||
const diffFile = diffTitle.closest('.diff-file');
|
||||
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
|
||||
if (nothingHereBlock.length) {
|
||||
const clickTarget = $('.file-title, .click-to-expand', diffFile);
|
||||
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
|
||||
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
|
||||
this.highlighSelectedLine();
|
||||
if (cb) cb();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
|
||||
/* global Vue */
|
||||
/* global CommentsStore */
|
||||
const Vue = require('vue');
|
||||
|
||||
(() => {
|
||||
const CommentAndResolveBtn = Vue.extend({
|
||||
|
@ -9,13 +9,11 @@
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
textareaIsEmpty: true
|
||||
textareaIsEmpty: true,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
discussion: function () {
|
||||
return CommentsStore.state[this.discussionId];
|
||||
},
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
|
@ -42,6 +40,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
},
|
||||
mounted: function () {
|
||||
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
|
||||
this.textareaIsEmpty = $textarea.val() === '';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */
|
||||
/* global Vue */
|
||||
/* global DiscussionMixins */
|
||||
/* global CommentsStore */
|
||||
const Vue = require('vue');
|
||||
|
||||
(() => {
|
||||
const JumpToDiscussion = Vue.extend({
|
||||
|
@ -12,12 +12,10 @@
|
|||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
discussion: function () {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
allResolved: function () {
|
||||
return this.unresolvedDiscussionCount === 0;
|
||||
},
|
||||
|
@ -183,10 +181,13 @@
|
|||
}
|
||||
|
||||
$.scrollTo($target, {
|
||||
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
|
||||
offset: 0
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.discussion = this.discussions[this.discussionId];
|
||||
},
|
||||
});
|
||||
|
||||
Vue.component('jump-to-discussion', JumpToDiscussion);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
|
||||
/* global Vue */
|
||||
/* global CommentsStore */
|
||||
/* global ResolveService */
|
||||
/* global Flash */
|
||||
const Vue = require('vue');
|
||||
|
||||
(() => {
|
||||
const ResolveBtn = Vue.extend({
|
||||
|
@ -10,14 +10,14 @@
|
|||
noteId: Number,
|
||||
discussionId: String,
|
||||
resolved: Boolean,
|
||||
projectPath: String,
|
||||
canResolve: Boolean,
|
||||
resolvedBy: String
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
discussions: CommentsStore.state,
|
||||
loading: false
|
||||
loading: false,
|
||||
note: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -30,13 +30,6 @@
|
|||
discussion: function () {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
note: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.getNote(this.noteId);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
buttonText: function () {
|
||||
if (this.isResolved) {
|
||||
return `Resolved by ${this.resolvedByName}`;
|
||||
|
@ -73,10 +66,10 @@
|
|||
|
||||
if (this.isResolved) {
|
||||
promise = ResolveService
|
||||
.unresolve(this.projectPath, this.noteId);
|
||||
.unresolve(this.noteId);
|
||||
} else {
|
||||
promise = ResolveService
|
||||
.resolve(this.projectPath, this.noteId);
|
||||
.resolve(this.noteId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
|
@ -106,6 +99,8 @@
|
|||
},
|
||||
created: function () {
|
||||
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
|
||||
|
||||
this.note = this.discussion.getNote(this.noteId);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
|
||||
/* global Vue */
|
||||
/* global DiscussionMixins */
|
||||
/* global CommentsStore */
|
||||
const Vue = require('vue');
|
||||
|
||||
((w) => {
|
||||
w.ResolveCount = Vue.extend({
|
||||
|
|
|
@ -1,25 +1,22 @@
|
|||
/* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */
|
||||
/* global Vue */
|
||||
/* global CommentsStore */
|
||||
/* global ResolveService */
|
||||
|
||||
const Vue = require('vue');
|
||||
|
||||
(() => {
|
||||
const ResolveDiscussionBtn = Vue.extend({
|
||||
props: {
|
||||
discussionId: String,
|
||||
mergeRequestId: Number,
|
||||
projectPath: String,
|
||||
canResolve: Boolean,
|
||||
},
|
||||
data: function() {
|
||||
return {
|
||||
discussions: CommentsStore.state
|
||||
discussion: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
discussion: function () {
|
||||
return this.discussions[this.discussionId];
|
||||
},
|
||||
showButton: function () {
|
||||
if (this.discussion) {
|
||||
return this.discussion.isResolvable();
|
||||
|
@ -51,11 +48,13 @@
|
|||
},
|
||||
methods: {
|
||||
resolve: function () {
|
||||
ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId);
|
||||
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
|
||||
|
||||
this.discussion = CommentsStore.state[this.discussionId];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
/* global ResolveCount */
|
||||
|
||||
function requireAll(context) { return context.keys().map(context); }
|
||||
const Vue = require('vue');
|
||||
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
|
||||
requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
|
||||
|
@ -10,11 +11,14 @@ requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
|
|||
requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
|
||||
|
||||
$(() => {
|
||||
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
|
||||
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
|
||||
|
||||
window.gl = window.gl || {};
|
||||
window.gl.diffNoteApps = {};
|
||||
|
||||
window.ResolveService = new gl.DiffNotesResolveServiceClass(projectPath);
|
||||
|
||||
gl.diffNotesCompileComponents = () => {
|
||||
const $components = $(COMPONENT_SELECTOR).filter(function () {
|
||||
return $(this).closest('resolve-count').length !== 1;
|
||||
|
|
|
@ -1,45 +1,37 @@
|
|||
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
|
||||
/* global Vue */
|
||||
/* global Flash */
|
||||
/* global CommentsStore */
|
||||
|
||||
((w) => {
|
||||
const Vue = window.Vue = require('vue');
|
||||
window.Vue.use(require('vue-resource'));
|
||||
require('../../vue_shared/vue_resource_interceptor');
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
|
||||
class ResolveServiceClass {
|
||||
constructor() {
|
||||
this.noteResource = Vue.resource('notes{/noteId}/resolve');
|
||||
this.discussionResource = Vue.resource('merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve');
|
||||
constructor(root) {
|
||||
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
|
||||
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
|
||||
}
|
||||
|
||||
setCSRF() {
|
||||
Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken();
|
||||
}
|
||||
|
||||
prepareRequest(root) {
|
||||
this.setCSRF();
|
||||
Vue.http.options.root = root;
|
||||
}
|
||||
|
||||
resolve(projectPath, noteId) {
|
||||
this.prepareRequest(projectPath);
|
||||
|
||||
resolve(noteId) {
|
||||
return this.noteResource.save({ noteId }, {});
|
||||
}
|
||||
|
||||
unresolve(projectPath, noteId) {
|
||||
this.prepareRequest(projectPath);
|
||||
|
||||
unresolve(noteId) {
|
||||
return this.noteResource.delete({ noteId }, {});
|
||||
}
|
||||
|
||||
toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) {
|
||||
toggleResolveForDiscussion(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
const isResolved = discussion.isResolved();
|
||||
let promise;
|
||||
|
||||
if (isResolved) {
|
||||
promise = this.unResolveAll(projectPath, mergeRequestId, discussionId);
|
||||
promise = this.unResolveAll(mergeRequestId, discussionId);
|
||||
} else {
|
||||
promise = this.resolveAll(projectPath, mergeRequestId, discussionId);
|
||||
promise = this.resolveAll(mergeRequestId, discussionId);
|
||||
}
|
||||
|
||||
promise.then((response) => {
|
||||
|
@ -62,11 +54,9 @@
|
|||
});
|
||||
}
|
||||
|
||||
resolveAll(projectPath, mergeRequestId, discussionId) {
|
||||
resolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
this.prepareRequest(projectPath);
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.save({
|
||||
|
@ -75,11 +65,9 @@
|
|||
}, {});
|
||||
}
|
||||
|
||||
unResolveAll(projectPath, mergeRequestId, discussionId) {
|
||||
unResolveAll(mergeRequestId, discussionId) {
|
||||
const discussion = CommentsStore.state[discussionId];
|
||||
|
||||
this.prepareRequest(projectPath);
|
||||
|
||||
discussion.loading = true;
|
||||
|
||||
return this.discussionResource.delete({
|
||||
|
@ -89,5 +77,5 @@
|
|||
}
|
||||
}
|
||||
|
||||
w.ResolveService = new ResolveServiceClass();
|
||||
})(window);
|
||||
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
|
||||
})();
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
/* global UsersSelect */
|
||||
/* global GroupAvatar */
|
||||
/* global LineHighlighter */
|
||||
/* global ShortcutsBlob */
|
||||
/* global ProjectFork */
|
||||
/* global BuildArtifacts */
|
||||
/* global GroupsSelect */
|
||||
|
@ -36,6 +35,8 @@
|
|||
/* global Labels */
|
||||
/* global Shortcuts */
|
||||
|
||||
const ShortcutsBlob = require('./shortcuts_blob');
|
||||
|
||||
(function() {
|
||||
var Dispatcher;
|
||||
|
||||
|
@ -96,6 +97,7 @@
|
|||
break;
|
||||
case 'projects:milestones:new':
|
||||
case 'projects:milestones:edit':
|
||||
case 'projects:milestones:update':
|
||||
new ZenMode();
|
||||
new gl.DueDateSelectors();
|
||||
new gl.GLForm($('.milestone-form'));
|
||||
|
@ -162,7 +164,7 @@
|
|||
case 'projects:commit:pipelines':
|
||||
new gl.MiniPipelineGraph({
|
||||
container: '.js-pipeline-table',
|
||||
});
|
||||
}).bindEvents();
|
||||
break;
|
||||
case 'projects:commits:show':
|
||||
case 'projects:activity':
|
||||
|
@ -225,7 +227,12 @@
|
|||
case 'projects:blame:show':
|
||||
new LineHighlighter();
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
new ShortcutsBlob(true);
|
||||
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
|
||||
const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
|
||||
new ShortcutsBlob({
|
||||
skipResetBindings: true,
|
||||
fileBlobPermalinkUrl,
|
||||
});
|
||||
break;
|
||||
case 'groups:labels:new':
|
||||
case 'groups:labels:edit':
|
||||
|
@ -259,7 +266,7 @@
|
|||
new gl.ProtectedBranchCreate();
|
||||
new gl.ProtectedBranchEditList();
|
||||
break;
|
||||
case 'projects:variables:index':
|
||||
case 'projects:ci_cd:show':
|
||||
new gl.ProjectVariables();
|
||||
break;
|
||||
case 'ci:lints:create':
|
||||
|
|
|
@ -78,8 +78,8 @@ require('../window')(function(w){
|
|||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.listTemplate) {
|
||||
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
|
||||
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
|
||||
if (this.listTemplate && dynamicList) {
|
||||
dynamicList.outerHTML = this.listTemplate;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/* eslint-disable wrap-iife, func-names, space-before-function-paren, comma-dangle, prefer-template, consistent-return, class-methods-use-this, arrow-body-style, no-unused-vars, no-underscore-dangle, no-new, max-len, no-sequences, no-unused-expressions, no-param-reassign */
|
||||
/* global dateFormat */
|
||||
/* global Pikaday */
|
||||
|
||||
(function(global) {
|
||||
class DueDateSelect {
|
||||
|
@ -25,11 +27,14 @@
|
|||
this.initGlDropdown();
|
||||
this.initRemoveDueDate();
|
||||
this.initDatePicker();
|
||||
this.initStopPropagation();
|
||||
}
|
||||
|
||||
initGlDropdown() {
|
||||
this.$dropdown.glDropdown({
|
||||
opened: () => {
|
||||
const calendar = this.$datePicker.data('pikaday');
|
||||
calendar.show();
|
||||
},
|
||||
hidden: () => {
|
||||
this.$selectbox.hide();
|
||||
this.$value.css('display', '');
|
||||
|
@ -38,25 +43,37 @@
|
|||
}
|
||||
|
||||
initDatePicker() {
|
||||
this.$datePicker.datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
defaultDate: $("input[name='" + this.fieldName + "']").val(),
|
||||
altField: "input[name='" + this.fieldName + "']",
|
||||
onSelect: () => {
|
||||
const $dueDateInput = $(`input[name='${this.fieldName}']`);
|
||||
|
||||
const calendar = new Pikaday({
|
||||
field: $dueDateInput.get(0),
|
||||
theme: 'gitlab-theme',
|
||||
format: 'YYYY-MM-DD',
|
||||
onSelect: (dateText) => {
|
||||
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
|
||||
|
||||
$dueDateInput.val(formattedDate);
|
||||
|
||||
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
|
||||
gl.issueBoards.BoardsStore.detail.issue.dueDate = $(`input[name='${this.fieldName}']`).val();
|
||||
gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val();
|
||||
this.updateIssueBoardIssue();
|
||||
} else {
|
||||
return this.saveDueDate(true);
|
||||
this.saveDueDate(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$datePicker.append(calendar.el);
|
||||
this.$datePicker.data('pikaday', calendar);
|
||||
}
|
||||
|
||||
initRemoveDueDate() {
|
||||
this.$block.on('click', '.js-remove-due-date', (e) => {
|
||||
const calendar = this.$datePicker.data('pikaday');
|
||||
e.preventDefault();
|
||||
|
||||
calendar.setDate(null);
|
||||
|
||||
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
|
||||
gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
|
||||
this.updateIssueBoardIssue();
|
||||
|
@ -67,12 +84,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
initStopPropagation() {
|
||||
$(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', (e) => {
|
||||
return e.stopImmediatePropagation();
|
||||
});
|
||||
}
|
||||
|
||||
saveDueDate(isDropdown) {
|
||||
this.parseSelectedDate();
|
||||
this.prepSelectedDate();
|
||||
|
@ -86,7 +97,7 @@
|
|||
// Construct Date object manually to avoid buggy dateString support within Date constructor
|
||||
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
|
||||
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
|
||||
this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj);
|
||||
this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy');
|
||||
} else {
|
||||
this.displayedDate = 'No due date';
|
||||
}
|
||||
|
@ -153,14 +164,24 @@
|
|||
}
|
||||
|
||||
initMilestoneDatePicker() {
|
||||
$('.datepicker').datepicker({
|
||||
dateFormat: 'yy-mm-dd'
|
||||
$('.datepicker').each(function() {
|
||||
const $datePicker = $(this);
|
||||
const calendar = new Pikaday({
|
||||
field: $datePicker.get(0),
|
||||
theme: 'gitlab-theme',
|
||||
format: 'YYYY-MM-DD',
|
||||
onSelect(dateText) {
|
||||
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
|
||||
}
|
||||
});
|
||||
|
||||
$datePicker.data('pikaday', calendar);
|
||||
});
|
||||
|
||||
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
const datepicker = $(e.target).siblings('.datepicker');
|
||||
$.datepicker._clearDate(datepicker);
|
||||
const calendar = $(e.target).siblings('.datepicker').data('pikaday');
|
||||
calendar.setDate(null);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
/* global timeago */
|
||||
|
||||
window.Vue = require('vue');
|
||||
window.timeago = require('vendor/timeago');
|
||||
window.timeago = require('timeago.js');
|
||||
require('../../lib/utils/text_utility');
|
||||
require('../../vue_common_component/commit');
|
||||
require('../../vue_shared/components/commit');
|
||||
require('./environment_actions');
|
||||
require('./environment_external_url');
|
||||
require('./environment_stop');
|
||||
|
@ -147,12 +147,12 @@ require('./environment_terminal_button');
|
|||
},
|
||||
|
||||
/**
|
||||
* Returns the value of the `stoppable?` key provided in the response.
|
||||
* Returns the value of the `stop_action?` key provided in the response.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
isStoppable() {
|
||||
return this.model['stoppable?'];
|
||||
hasStopAction() {
|
||||
return this.model['stop_action?'];
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -508,7 +508,7 @@ require('./environment_terminal_button');
|
|||
</external-url-component>
|
||||
</div>
|
||||
|
||||
<div v-if="isStoppable && canCreateDeployment"
|
||||
<div v-if="hasStopAction && canCreateDeployment"
|
||||
class="inline js-stop-component-container">
|
||||
<stop-component
|
||||
:stop-url="model.stop_path">
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
window.Vue = require('vue');
|
||||
|
||||
require('./stores/environments_store');
|
||||
require('./components/environment');
|
||||
require('./vue_resource_interceptor');
|
||||
require('../vue_shared/vue_resource_interceptor');
|
||||
|
||||
$(() => {
|
||||
window.gl = window.gl || {};
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/* global Vue */
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
|
||||
|
||||
next((response) => {
|
||||
if (typeof response.data === 'string') {
|
||||
response.data = JSON.parse(response.data); // eslint-disable-line
|
||||
}
|
||||
|
||||
Vue.activeResources--; // eslint-disable-line
|
||||
});
|
||||
});
|
|
@ -8,7 +8,7 @@ require('./filtered_search_dropdown');
|
|||
super(droplab, dropdown, input, filter);
|
||||
this.config = {
|
||||
droplabAjaxFilter: {
|
||||
endpoint: '/autocomplete/users.json',
|
||||
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
|
||||
searchKey: 'search',
|
||||
params: {
|
||||
per_page: 20,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
class FilteredSearchDropdown {
|
||||
constructor(droplab, dropdown, input, filter) {
|
||||
this.droplab = droplab;
|
||||
this.hookId = input.getAttribute('data-id');
|
||||
this.hookId = input && input.getAttribute('data-id');
|
||||
this.input = input;
|
||||
this.filter = filter;
|
||||
this.dropdown = dropdown;
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
(() => {
|
||||
class FilteredSearchDropdownManager {
|
||||
constructor() {
|
||||
constructor(baseEndpoint = '') {
|
||||
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
|
||||
this.tokenizer = gl.FilteredSearchTokenizer;
|
||||
this.filteredSearchInput = document.querySelector('.filtered-search');
|
||||
|
||||
|
@ -38,13 +39,13 @@
|
|||
milestone: {
|
||||
reference: null,
|
||||
gl: 'DropdownNonUser',
|
||||
extraArguments: ['milestones.json', '%'],
|
||||
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
|
||||
element: document.querySelector('#js-dropdown-milestone'),
|
||||
},
|
||||
label: {
|
||||
reference: null,
|
||||
gl: 'DropdownNonUser',
|
||||
extraArguments: ['labels.json', '~'],
|
||||
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
|
||||
element: document.querySelector('#js-dropdown-label'),
|
||||
},
|
||||
hint: {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
if (this.filteredSearchInput) {
|
||||
this.tokenizer = gl.FilteredSearchTokenizer;
|
||||
this.dropdownManager = new gl.FilteredSearchDropdownManager();
|
||||
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
|
||||
|
||||
this.bindEvents();
|
||||
this.loadSearchParamsFromURL();
|
||||
|
|
|
@ -47,9 +47,11 @@
|
|||
}
|
||||
// Only filter asynchronously only if option remote is set
|
||||
if (this.options.remote) {
|
||||
$inputContainer.parent().addClass('is-loading');
|
||||
clearTimeout(timeout);
|
||||
return timeout = setTimeout(function() {
|
||||
return this.options.query(this.input.val(), function(data) {
|
||||
$inputContainer.parent().removeClass('is-loading');
|
||||
return this.options.callback(data);
|
||||
}.bind(this));
|
||||
}.bind(this), 250);
|
||||
|
@ -437,7 +439,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
GitLabDropdown.prototype.opened = function() {
|
||||
GitLabDropdown.prototype.opened = function(e) {
|
||||
var contentHtml;
|
||||
this.resetRows();
|
||||
this.addArrowKeyEvent();
|
||||
|
@ -457,6 +459,10 @@
|
|||
this.positionMenuAbove();
|
||||
}
|
||||
|
||||
if (this.options.opened) {
|
||||
this.options.opened.call(this, e);
|
||||
}
|
||||
|
||||
return this.dropdown.trigger('shown.gl.dropdown');
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
/* global UsersSelect */
|
||||
/* global ZenMode */
|
||||
/* global Autosave */
|
||||
/* global dateFormat */
|
||||
/* global Pikaday */
|
||||
|
||||
(function() {
|
||||
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
||||
|
@ -13,7 +15,7 @@
|
|||
IssuableForm.prototype.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
|
||||
|
||||
function IssuableForm(form) {
|
||||
var $issuableDueDate;
|
||||
var $issuableDueDate, calendar;
|
||||
this.form = form;
|
||||
this.toggleWip = bind(this.toggleWip, this);
|
||||
this.renderWipExplanation = bind(this.renderWipExplanation, this);
|
||||
|
@ -35,12 +37,14 @@
|
|||
this.initMoveDropdown();
|
||||
$issuableDueDate = $('#issuable-due-date');
|
||||
if ($issuableDueDate.length) {
|
||||
$('.datepicker').datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
onSelect: function(dateText, inst) {
|
||||
return $issuableDueDate.val(dateText);
|
||||
calendar = new Pikaday({
|
||||
field: $issuableDueDate.get(0),
|
||||
theme: 'gitlab-theme',
|
||||
format: 'YYYY-MM-DD',
|
||||
onSelect: function(dateText) {
|
||||
$issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
|
||||
}
|
||||
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $issuableDueDate.val()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, max-len */
|
||||
/* global Flash */
|
||||
/* global Sortable */
|
||||
|
||||
((global) => {
|
||||
class LabelManager {
|
||||
|
@ -9,11 +10,12 @@
|
|||
this.otherLabels = otherLabels || $('.js-other-labels');
|
||||
this.errorMessage = 'Unable to update label prioritization at this time';
|
||||
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
|
||||
this.prioritizedLabels.sortable({
|
||||
items: 'li',
|
||||
placeholder: 'list-placeholder',
|
||||
axis: 'y',
|
||||
update: this.onPrioritySortUpdate.bind(this)
|
||||
this.sortable = Sortable.create(this.prioritizedLabels.get(0), {
|
||||
filter: '.empty-message',
|
||||
forceFallback: true,
|
||||
fallbackClass: 'is-dragging',
|
||||
dataIdAttr: 'data-id',
|
||||
onUpdate: this.onPrioritySortUpdate.bind(this),
|
||||
});
|
||||
this.bindEvents();
|
||||
}
|
||||
|
@ -51,13 +53,13 @@
|
|||
$target = this.otherLabels;
|
||||
$from = this.prioritizedLabels;
|
||||
}
|
||||
if ($from.find('li').length === 1) {
|
||||
$label.detach().appendTo($target);
|
||||
if ($from.find('li').length) {
|
||||
$from.find('.empty-message').removeClass('hidden');
|
||||
}
|
||||
if (!$target.find('li').length) {
|
||||
if ($target.find('> li:not(.empty-message)').length) {
|
||||
$target.find('.empty-message').addClass('hidden');
|
||||
}
|
||||
$label.detach().appendTo($target);
|
||||
// Return if we are not persisting state
|
||||
if (!persistState) {
|
||||
return;
|
||||
|
@ -101,8 +103,12 @@
|
|||
|
||||
getSortedLabelsIds() {
|
||||
const sortedIds = [];
|
||||
this.prioritizedLabels.find('li').each(function() {
|
||||
sortedIds.push($(this).data('id'));
|
||||
this.prioritizedLabels.find('> li').each(function() {
|
||||
const id = $(this).data('id');
|
||||
|
||||
if (id) {
|
||||
sortedIds.push(id);
|
||||
}
|
||||
});
|
||||
return sortedIds;
|
||||
}
|
||||
|
|
|
@ -69,27 +69,21 @@
|
|||
var hash = w.gl.utils.getLocationHash();
|
||||
if (!hash) return;
|
||||
|
||||
var navbar = document.querySelector('.navbar-gitlab');
|
||||
var subnav = document.querySelector('.layout-nav');
|
||||
var fixedTabs = document.querySelector('.js-tabs-affix');
|
||||
|
||||
var adjustment = 0;
|
||||
if (navbar) adjustment -= navbar.offsetHeight;
|
||||
if (subnav) adjustment -= subnav.offsetHeight;
|
||||
// This is required to handle non-unicode characters in hash
|
||||
hash = decodeURIComponent(hash);
|
||||
|
||||
// scroll to user-generated markdown anchor if we cannot find a match
|
||||
if (document.getElementById(hash) === null) {
|
||||
var target = document.getElementById('user-content-' + hash);
|
||||
if (target && target.scrollIntoView) {
|
||||
target.scrollIntoView(true);
|
||||
window.scrollBy(0, adjustment);
|
||||
}
|
||||
} else {
|
||||
// only adjust for fixedTabs when not targeting user-generated content
|
||||
var fixedTabs = document.querySelector('.js-tabs-affix');
|
||||
if (fixedTabs) {
|
||||
adjustment -= fixedTabs.offsetHeight;
|
||||
window.scrollBy(0, -fixedTabs.offsetHeight);
|
||||
}
|
||||
window.scrollBy(0, adjustment);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -134,14 +128,20 @@
|
|||
return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
|
||||
};
|
||||
|
||||
gl.utils.isMetaClick = function(e) {
|
||||
// Identify following special clicks
|
||||
// 1) Cmd + Click on Mac (e.metaKey)
|
||||
// 2) Ctrl + Click on PC (e.ctrlKey)
|
||||
// 3) Middle-click or Mouse Wheel Click (e.which is 2)
|
||||
return e.metaKey || e.ctrlKey || e.which === 2;
|
||||
};
|
||||
|
||||
gl.utils.scrollToElement = function($el) {
|
||||
var top = $el.offset().top;
|
||||
gl.navBarHeight = gl.navBarHeight || $('.navbar-gitlab').height();
|
||||
gl.navLinksHeight = gl.navLinksHeight || $('.nav-links').height();
|
||||
gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
|
||||
|
||||
return $('body, html').animate({
|
||||
scrollTop: top - (gl.navBarHeight + gl.navLinksHeight + gl.mrTabsHeight)
|
||||
scrollTop: top - (gl.mrTabsHeight)
|
||||
}, 200);
|
||||
};
|
||||
|
||||
|
@ -230,5 +230,16 @@
|
|||
|
||||
return upperCaseHeaders;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms a DOMStringMap into a plain object.
|
||||
*
|
||||
* @param {DOMStringMap} DOMStringMapObject
|
||||
* @returns {Object}
|
||||
*/
|
||||
w.gl.utils.DOMStringMapToObject = DOMStringMapObject => Object.keys(DOMStringMapObject).reduce((acc, element) => {
|
||||
acc[element] = DOMStringMapObject[element];
|
||||
return acc;
|
||||
}, {});
|
||||
})(window);
|
||||
}).call(this);
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
|
||||
/* global timeago */
|
||||
/* global dateFormat */
|
||||
|
||||
window.timeago = require('vendor/timeago');
|
||||
window.dateFormat = require('vendor/date.format');
|
||||
|
||||
(function() {
|
||||
(function(w) {
|
||||
var base;
|
||||
if (w.gl == null) {
|
||||
w.gl = {};
|
||||
}
|
||||
if ((base = w.gl).utils == null) {
|
||||
base.utils = {};
|
||||
}
|
||||
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
|
||||
w.gl.utils.formatDate = function(datetime) {
|
||||
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
|
||||
};
|
||||
|
||||
w.gl.utils.getDayName = function(date) {
|
||||
return this.days[date.getDay()];
|
||||
};
|
||||
|
||||
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
|
||||
if (setTimeago == null) {
|
||||
setTimeago = true;
|
||||
}
|
||||
|
||||
$timeagoEls.filter(':not([data-timeago-rendered])').each(function() {
|
||||
var $el = $(this);
|
||||
$el.attr('title', gl.utils.formatDate($el.attr('datetime')));
|
||||
|
||||
if (setTimeago) {
|
||||
// Recreate with custom template
|
||||
$el.tooltip({
|
||||
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
});
|
||||
}
|
||||
|
||||
$el.attr('data-timeago-rendered', true);
|
||||
gl.utils.renderTimeago($el);
|
||||
});
|
||||
};
|
||||
|
||||
w.gl.utils.getTimeago = function() {
|
||||
var locale = function(number, index) {
|
||||
return [
|
||||
['less than a minute ago', 'a while'],
|
||||
['less than a minute ago', 'in %s seconds'],
|
||||
['about a minute ago', 'in 1 minute'],
|
||||
['%s minutes ago', 'in %s minutes'],
|
||||
['about an hour ago', 'in 1 hour'],
|
||||
['about %s hours ago', 'in %s hours'],
|
||||
['a day ago', 'in 1 day'],
|
||||
['%s days ago', 'in %s days'],
|
||||
['a week ago', 'in 1 week'],
|
||||
['%s weeks ago', 'in %s weeks'],
|
||||
['a month ago', 'in 1 month'],
|
||||
['%s months ago', 'in %s months'],
|
||||
['a year ago', 'in 1 year'],
|
||||
['%s years ago', 'in %s years']
|
||||
][index];
|
||||
};
|
||||
|
||||
timeago.register('gl_en', locale);
|
||||
return timeago();
|
||||
};
|
||||
|
||||
w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
|
||||
var timefor;
|
||||
if (!time) {
|
||||
return '';
|
||||
}
|
||||
suffix || (suffix = 'remaining');
|
||||
expiredLabel || (expiredLabel = 'Past due');
|
||||
timefor = gl.utils.getTimeago().format(time).replace('in', '');
|
||||
if (timefor.indexOf('ago') > -1) {
|
||||
timefor = expiredLabel;
|
||||
} else {
|
||||
timefor = timefor.trim() + ' ' + suffix;
|
||||
}
|
||||
return timefor;
|
||||
};
|
||||
|
||||
w.gl.utils.renderTimeago = function($element) {
|
||||
var timeagoInstance = gl.utils.getTimeago();
|
||||
timeagoInstance.render($element, 'gl_en');
|
||||
};
|
||||
|
||||
w.gl.utils.getDayDifference = function(a, b) {
|
||||
var millisecondsPerDay = 1000 * 60 * 60 * 24;
|
||||
var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
||||
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
||||
|
||||
return Math.floor((date2 - date1) / millisecondsPerDay);
|
||||
};
|
||||
})(window);
|
||||
}).call(this);
|
|
@ -0,0 +1,126 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
|
||||
/* global timeago */
|
||||
/* global dateFormat */
|
||||
|
||||
window.timeago = require('timeago.js');
|
||||
window.dateFormat = require('vendor/date.format');
|
||||
|
||||
(function() {
|
||||
(function(w) {
|
||||
var base;
|
||||
var timeagoInstance;
|
||||
|
||||
if (w.gl == null) {
|
||||
w.gl = {};
|
||||
}
|
||||
if ((base = w.gl).utils == null) {
|
||||
base.utils = {};
|
||||
}
|
||||
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
|
||||
w.gl.utils.formatDate = function(datetime) {
|
||||
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
|
||||
};
|
||||
|
||||
w.gl.utils.getDayName = function(date) {
|
||||
return this.days[date.getDay()];
|
||||
};
|
||||
|
||||
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
|
||||
$timeagoEls.each((i, el) => {
|
||||
el.setAttribute('title', gl.utils.formatDate(el.getAttribute('datetime')));
|
||||
|
||||
if (setTimeago) {
|
||||
// Recreate with custom template
|
||||
$(el).tooltip({
|
||||
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
});
|
||||
}
|
||||
|
||||
el.classList.add('js-timeago-render');
|
||||
});
|
||||
|
||||
gl.utils.renderTimeago($timeagoEls);
|
||||
};
|
||||
|
||||
w.gl.utils.getTimeago = function() {
|
||||
var locale;
|
||||
|
||||
if (!timeagoInstance) {
|
||||
locale = function(number, index) {
|
||||
return [
|
||||
['less than a minute ago', 'a while'],
|
||||
['less than a minute ago', 'in %s seconds'],
|
||||
['about a minute ago', 'in 1 minute'],
|
||||
['%s minutes ago', 'in %s minutes'],
|
||||
['about an hour ago', 'in 1 hour'],
|
||||
['about %s hours ago', 'in %s hours'],
|
||||
['a day ago', 'in 1 day'],
|
||||
['%s days ago', 'in %s days'],
|
||||
['a week ago', 'in 1 week'],
|
||||
['%s weeks ago', 'in %s weeks'],
|
||||
['a month ago', 'in 1 month'],
|
||||
['%s months ago', 'in %s months'],
|
||||
['a year ago', 'in 1 year'],
|
||||
['%s years ago', 'in %s years']
|
||||
][index];
|
||||
};
|
||||
|
||||
timeago.register('gl_en', locale);
|
||||
timeagoInstance = timeago();
|
||||
}
|
||||
|
||||
return timeagoInstance;
|
||||
};
|
||||
|
||||
w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
|
||||
var timefor;
|
||||
if (!time) {
|
||||
return '';
|
||||
}
|
||||
suffix || (suffix = 'remaining');
|
||||
expiredLabel || (expiredLabel = 'Past due');
|
||||
timefor = gl.utils.getTimeago().format(time).replace('in', '');
|
||||
if (timefor.indexOf('ago') > -1) {
|
||||
timefor = expiredLabel;
|
||||
} else {
|
||||
timefor = timefor.trim() + ' ' + suffix;
|
||||
}
|
||||
return timefor;
|
||||
};
|
||||
|
||||
w.gl.utils.cachedTimeagoElements = [];
|
||||
w.gl.utils.renderTimeago = function($els) {
|
||||
if (!$els && !w.gl.utils.cachedTimeagoElements.length) {
|
||||
w.gl.utils.cachedTimeagoElements = [].slice.call(document.querySelectorAll('.js-timeago-render'));
|
||||
} else if ($els) {
|
||||
w.gl.utils.cachedTimeagoElements = w.gl.utils.cachedTimeagoElements.concat($els.toArray());
|
||||
}
|
||||
|
||||
w.gl.utils.cachedTimeagoElements.forEach(gl.utils.updateTimeagoText);
|
||||
};
|
||||
|
||||
w.gl.utils.updateTimeagoText = function(el) {
|
||||
const timeago = gl.utils.getTimeago();
|
||||
const formattedDate = timeago.format(el.getAttribute('datetime'), 'gl_en');
|
||||
|
||||
if (el.textContent !== formattedDate) {
|
||||
el.textContent = formattedDate;
|
||||
}
|
||||
};
|
||||
|
||||
w.gl.utils.initTimeagoTimeout = function() {
|
||||
gl.utils.renderTimeago();
|
||||
|
||||
gl.utils.timeagoTimeout = setTimeout(gl.utils.initTimeagoTimeout, 1000);
|
||||
};
|
||||
|
||||
w.gl.utils.getDayDifference = function(a, b) {
|
||||
var millisecondsPerDay = 1000 * 60 * 60 * 24;
|
||||
var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
||||
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
||||
|
||||
return Math.floor((date2 - date1) / millisecondsPerDay);
|
||||
};
|
||||
})(window);
|
||||
}).call(this);
|
|
@ -1,3 +1,5 @@
|
|||
/* global Pikaday */
|
||||
/* global dateFormat */
|
||||
(() => {
|
||||
// Add datepickers to all `js-access-expiration-date` elements. If those elements are
|
||||
// children of an element with the `clearable-input` class, and have a sibling
|
||||
|
@ -11,21 +13,34 @@
|
|||
}
|
||||
const inputs = $(selector);
|
||||
|
||||
inputs.datepicker({
|
||||
dateFormat: 'yy-mm-dd',
|
||||
minDate: 1,
|
||||
onSelect: function onSelect() {
|
||||
$(this).trigger('change');
|
||||
toggleClearInput.call(this);
|
||||
},
|
||||
inputs.each((i, el) => {
|
||||
const $input = $(el);
|
||||
|
||||
const calendar = new Pikaday({
|
||||
field: $input.get(0),
|
||||
theme: 'gitlab-theme',
|
||||
format: 'YYYY-MM-DD',
|
||||
minDate: new Date(),
|
||||
onSelect(dateText) {
|
||||
$input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
|
||||
|
||||
$input.trigger('change');
|
||||
|
||||
toggleClearInput.call($input);
|
||||
},
|
||||
});
|
||||
|
||||
$input.data('pikaday', calendar);
|
||||
});
|
||||
|
||||
inputs.next('.js-clear-input').on('click', function clicked(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const input = $(this).closest('.clearable-input').find(selector);
|
||||
input.datepicker('setDate', null)
|
||||
.trigger('change');
|
||||
const calendar = input.data('pikaday');
|
||||
|
||||
calendar.setDate(null);
|
||||
input.trigger('change');
|
||||
toggleClearInput.call(input);
|
||||
});
|
||||
|
||||
|
|
|
@ -115,8 +115,8 @@ require('./merge_request_tabs');
|
|||
e.preventDefault();
|
||||
|
||||
textarea.val(textarea.data('messageWithDescription'));
|
||||
$('p.js-with-description-hint').hide();
|
||||
$('p.js-without-description-hint').show();
|
||||
$('.js-with-description-hint').hide();
|
||||
$('.js-without-description-hint').show();
|
||||
});
|
||||
|
||||
$(document).on('click', 'a.js-without-description-link', function(e) {
|
||||
|
@ -124,8 +124,8 @@ require('./merge_request_tabs');
|
|||
e.preventDefault();
|
||||
|
||||
textarea.val(textarea.data('messageWithoutDescription'));
|
||||
$('p.js-with-description-hint').show();
|
||||
$('p.js-without-description-hint').hide();
|
||||
$('.js-with-description-hint').show();
|
||||
$('.js-without-description-hint').hide();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
/* global Flash */
|
||||
|
||||
require('./breakpoints');
|
||||
window.Cookies = require('vendor/js.cookie');
|
||||
window.Cookies = require('js-cookie');
|
||||
require('./flash');
|
||||
|
||||
/* eslint-disable max-len */
|
||||
|
@ -61,7 +61,6 @@ require('./flash');
|
|||
|
||||
constructor({ action, setUrl, stubLocation } = {}) {
|
||||
this.diffsLoaded = false;
|
||||
this.pipelinesLoaded = false;
|
||||
this.commitsLoaded = false;
|
||||
this.fixedLayoutPref = null;
|
||||
|
||||
|
@ -83,12 +82,18 @@ require('./flash');
|
|||
$(document)
|
||||
.on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
||||
.on('click', '.js-show-tab', this.showTab);
|
||||
|
||||
$('.merge-request-tabs a[data-toggle="tab"]')
|
||||
.on('click', this.clickTab);
|
||||
}
|
||||
|
||||
unbindEvents() {
|
||||
$(document)
|
||||
.off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
||||
.off('click', '.js-show-tab', this.showTab);
|
||||
|
||||
$('.merge-request-tabs a[data-toggle="tab"]')
|
||||
.off('click', this.clickTab);
|
||||
}
|
||||
|
||||
showTab(e) {
|
||||
|
@ -96,6 +101,14 @@ require('./flash');
|
|||
this.activateTab($(e.target).data('action'));
|
||||
}
|
||||
|
||||
clickTab(e) {
|
||||
if (e.target && gl.utils.isMetaClick(e)) {
|
||||
const targetLink = e.target.getAttribute('href');
|
||||
e.stopImmediatePropagation();
|
||||
window.open(targetLink, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
tabShown(e) {
|
||||
const $target = $(e.target);
|
||||
const action = $target.data('action');
|
||||
|
@ -112,14 +125,9 @@ require('./flash');
|
|||
if (this.diffViewType() === 'parallel') {
|
||||
this.expandViewContainer();
|
||||
}
|
||||
const navBarHeight = $('.navbar-gitlab').outerHeight();
|
||||
$.scrollTo('.merge-request-details .merge-request-tabs', {
|
||||
offset: -navBarHeight,
|
||||
offset: 0,
|
||||
});
|
||||
} else if (action === 'pipelines') {
|
||||
this.loadPipelines($target.attr('href'));
|
||||
this.expandView();
|
||||
this.resetViewContainer();
|
||||
} else {
|
||||
this.expandView();
|
||||
this.resetViewContainer();
|
||||
|
@ -131,11 +139,7 @@ require('./flash');
|
|||
|
||||
scrollToElement(container) {
|
||||
if (location.hash) {
|
||||
const offset = 0 - (
|
||||
$('.navbar-gitlab').outerHeight() +
|
||||
$('.layout-nav').outerHeight() +
|
||||
$('.js-tabs-affix').outerHeight()
|
||||
);
|
||||
const offset = -$('.js-tabs-affix').outerHeight();
|
||||
const $el = $(`${container} ${location.hash}:not(.match)`);
|
||||
if ($el.length) {
|
||||
$.scrollTo($el[0], { offset });
|
||||
|
@ -244,25 +248,6 @@ require('./flash');
|
|||
});
|
||||
}
|
||||
|
||||
loadPipelines(source) {
|
||||
if (this.pipelinesLoaded) {
|
||||
return;
|
||||
}
|
||||
this.ajaxGet({
|
||||
url: `${source}.json`,
|
||||
success: (data) => {
|
||||
$('#pipelines').html(data.html);
|
||||
gl.utils.localTimeAgo($('.js-timeago', '#pipelines'));
|
||||
this.pipelinesLoaded = true;
|
||||
this.scrollToElement('#pipelines');
|
||||
|
||||
new gl.MiniPipelineGraph({
|
||||
container: '.js-pipeline-table',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Show or hide the loading spinner
|
||||
//
|
||||
// status - Boolean, true to show, false to hide
|
||||
|
@ -340,14 +325,12 @@ require('./flash');
|
|||
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
|
||||
|
||||
const $diffTabs = $('#diff-notes-app');
|
||||
const $fixedNav = $('.navbar-fixed-top');
|
||||
const $layoutNav = $('.layout-nav');
|
||||
|
||||
$tabs.off('affix.bs.affix affix-top.bs.affix')
|
||||
.affix({
|
||||
offset: {
|
||||
top: () => (
|
||||
$diffTabs.offset().top - $tabs.height() - $fixedNav.height() - $layoutNav.height()
|
||||
$diffTabs.offset().top - $tabs.height()
|
||||
),
|
||||
},
|
||||
})
|
||||
|
|
|
@ -51,6 +51,8 @@ require('./smart_interval');
|
|||
this.getCIStatus(false);
|
||||
this.retrieveSuccessIcon();
|
||||
|
||||
this.initMiniPipelineGraph();
|
||||
|
||||
this.ciStatusInterval = new global.SmartInterval({
|
||||
callback: this.getCIStatus.bind(this, true),
|
||||
startingInterval: 10000,
|
||||
|
@ -66,6 +68,7 @@ require('./smart_interval');
|
|||
incrementByFactorOf: 15000,
|
||||
immediateExecution: true,
|
||||
});
|
||||
|
||||
notifyPermissions();
|
||||
}
|
||||
|
||||
|
@ -151,7 +154,7 @@ require('./smart_interval');
|
|||
return $.getJSON(this.opts.ci_status_url, (function(_this) {
|
||||
return function(data) {
|
||||
var message, status, title;
|
||||
if (data.status === '') {
|
||||
if (!data.status) {
|
||||
return;
|
||||
}
|
||||
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
||||
|
@ -236,17 +239,20 @@ require('./smart_interval');
|
|||
case "failed":
|
||||
case "canceled":
|
||||
case "not_found":
|
||||
return this.setMergeButtonClass('btn-danger');
|
||||
this.setMergeButtonClass('btn-danger');
|
||||
break;
|
||||
case "running":
|
||||
return this.setMergeButtonClass('btn-info');
|
||||
this.setMergeButtonClass('btn-info');
|
||||
break;
|
||||
case "success":
|
||||
case "success_with_warnings":
|
||||
return this.setMergeButtonClass('btn-create');
|
||||
this.setMergeButtonClass('btn-create');
|
||||
}
|
||||
} else {
|
||||
$('.ci_widget.ci-error').show();
|
||||
return this.setMergeButtonClass('btn-danger');
|
||||
this.setMergeButtonClass('btn-danger');
|
||||
}
|
||||
this.initMiniPipelineGraph();
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
|
||||
|
@ -269,6 +275,12 @@ require('./smart_interval');
|
|||
$('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/'));
|
||||
};
|
||||
|
||||
MergeRequestWidget.prototype.initMiniPipelineGraph = function() {
|
||||
new gl.MiniPipelineGraph({
|
||||
container: '.js-pipeline-inline-mr-widget-graph:visible',
|
||||
}).bindEvents();
|
||||
};
|
||||
|
||||
return MergeRequestWidget;
|
||||
})();
|
||||
})(window.gl || (window.gl = {}));
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, max-len */
|
||||
/* global Flash */
|
||||
/* global Sortable */
|
||||
|
||||
(function() {
|
||||
this.Milestone = (function() {
|
||||
|
@ -8,11 +9,9 @@
|
|||
type: "PUT",
|
||||
url: issue_url,
|
||||
data: data,
|
||||
success: (function(_this) {
|
||||
return function(_data) {
|
||||
return _this.successCallback(_data, li);
|
||||
};
|
||||
})(this),
|
||||
success: function(_data) {
|
||||
return Milestone.successCallback(_data, li);
|
||||
},
|
||||
error: function(data) {
|
||||
return new Flash("Issue update failed", 'alert');
|
||||
},
|
||||
|
@ -27,11 +26,9 @@
|
|||
type: "PUT",
|
||||
url: sort_issues_url,
|
||||
data: data,
|
||||
success: (function(_this) {
|
||||
return function(_data) {
|
||||
return _this.successCallback(_data);
|
||||
};
|
||||
})(this),
|
||||
success: function(_data) {
|
||||
return Milestone.successCallback(_data);
|
||||
},
|
||||
error: function() {
|
||||
return new Flash("Issues update failed", 'alert');
|
||||
},
|
||||
|
@ -46,11 +43,9 @@
|
|||
type: "PUT",
|
||||
url: sort_mr_url,
|
||||
data: data,
|
||||
success: (function(_this) {
|
||||
return function(_data) {
|
||||
return _this.successCallback(_data);
|
||||
};
|
||||
})(this),
|
||||
success: function(_data) {
|
||||
return Milestone.successCallback(_data);
|
||||
},
|
||||
error: function(data) {
|
||||
return new Flash("Issue update failed", 'alert');
|
||||
},
|
||||
|
@ -63,11 +58,9 @@
|
|||
type: "PUT",
|
||||
url: merge_request_url,
|
||||
data: data,
|
||||
success: (function(_this) {
|
||||
return function(_data) {
|
||||
return _this.successCallback(_data, li);
|
||||
};
|
||||
})(this),
|
||||
success: function(_data) {
|
||||
return Milestone.successCallback(_data, li);
|
||||
},
|
||||
error: function(data) {
|
||||
return new Flash("Issue update failed", 'alert');
|
||||
},
|
||||
|
@ -81,65 +74,30 @@
|
|||
img_tag = $('<img/>');
|
||||
img_tag.attr('src', data.assignee.avatar_url);
|
||||
img_tag.addClass('avatar s16');
|
||||
$(element).find('.assignee-icon').html(img_tag);
|
||||
$(element).find('.assignee-icon img').replaceWith(img_tag);
|
||||
} else {
|
||||
$(element).find('.assignee-icon').html('');
|
||||
$(element).find('.assignee-icon').empty();
|
||||
}
|
||||
return $(element).effect('highlight');
|
||||
};
|
||||
|
||||
function Milestone() {
|
||||
var oldMouseStart;
|
||||
oldMouseStart = $.ui.sortable.prototype._mouseStart;
|
||||
$.ui.sortable.prototype._mouseStart = function(event, overrideHandle, noActivation) {
|
||||
this._trigger("beforeStart", event, this._uiHash());
|
||||
return oldMouseStart.apply(this, [event, overrideHandle, noActivation]);
|
||||
};
|
||||
this.bindIssuesSorting();
|
||||
this.bindMergeRequestSorting();
|
||||
this.bindTabsSwitching();
|
||||
}
|
||||
|
||||
Milestone.prototype.bindIssuesSorting = function() {
|
||||
return $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable({
|
||||
connectWith: ".issues-sortable-list",
|
||||
dropOnEmpty: true,
|
||||
items: "li:not(.ui-sort-disabled)",
|
||||
beforeStart: function(event, ui) {
|
||||
return $(".issues-sortable-list").css("min-height", ui.item.outerHeight());
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
return $(".issues-sortable-list").css("min-height", "0px");
|
||||
},
|
||||
update: function(event, ui) {
|
||||
var data;
|
||||
// Prevents sorting from container which element has been removed.
|
||||
if ($(this).find(ui.item).length > 0) {
|
||||
data = $(this).sortable("serialize");
|
||||
return Milestone.sortIssues(data);
|
||||
}
|
||||
},
|
||||
receive: function(event, ui) {
|
||||
var data, issue_id, issue_url, new_state;
|
||||
new_state = $(this).data('state');
|
||||
issue_id = ui.item.data('iid');
|
||||
issue_url = ui.item.data('url');
|
||||
data = (function() {
|
||||
switch (new_state) {
|
||||
case 'ongoing':
|
||||
return "issue[assignee_id]=" + gon.current_user_id;
|
||||
case 'unassigned':
|
||||
return "issue[assignee_id]=";
|
||||
case 'closed':
|
||||
return "issue[state_event]=close";
|
||||
}
|
||||
})();
|
||||
if ($(ui.sender).data('state') === "closed") {
|
||||
data += "&issue[state_event]=reopen";
|
||||
}
|
||||
return Milestone.updateIssue(ui.item, issue_url, data);
|
||||
}
|
||||
}).disableSelection();
|
||||
$('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
|
||||
this.createSortable(el, {
|
||||
group: 'issue-list',
|
||||
listEls: $('.issues-sortable-list'),
|
||||
fieldName: 'issue',
|
||||
sortCallback: Milestone.sortIssues,
|
||||
updateCallback: Milestone.updateIssue,
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Milestone.prototype.bindTabsSwitching = function() {
|
||||
|
@ -154,42 +112,62 @@
|
|||
};
|
||||
|
||||
Milestone.prototype.bindMergeRequestSorting = function() {
|
||||
return $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable({
|
||||
connectWith: ".merge_requests-sortable-list",
|
||||
dropOnEmpty: true,
|
||||
items: "li:not(.ui-sort-disabled)",
|
||||
beforeStart: function(event, ui) {
|
||||
return $(".merge_requests-sortable-list").css("min-height", ui.item.outerHeight());
|
||||
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
|
||||
this.createSortable(el, {
|
||||
group: 'merge-request-list',
|
||||
listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
|
||||
fieldName: 'merge_request',
|
||||
sortCallback: Milestone.sortMergeRequests,
|
||||
updateCallback: Milestone.updateMergeRequest,
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Milestone.prototype.createSortable = function(el, opts) {
|
||||
return Sortable.create(el, {
|
||||
group: opts.group,
|
||||
filter: '.is-disabled',
|
||||
forceFallback: true,
|
||||
onStart: function(e) {
|
||||
opts.listEls.css('min-height', e.item.offsetHeight);
|
||||
},
|
||||
stop: function(event, ui) {
|
||||
return $(".merge_requests-sortable-list").css("min-height", "0px");
|
||||
onEnd: function () {
|
||||
opts.listEls.css("min-height", "0px");
|
||||
},
|
||||
update: function(event, ui) {
|
||||
var data;
|
||||
data = $(this).sortable("serialize");
|
||||
return Milestone.sortMergeRequests(data);
|
||||
onUpdate: function(e) {
|
||||
var ids = this.toArray(),
|
||||
data;
|
||||
|
||||
if (ids.length) {
|
||||
data = ids.map(function(id) {
|
||||
return 'sortable_' + opts.fieldName + '[]=' + id;
|
||||
}).join('&');
|
||||
|
||||
opts.sortCallback(data);
|
||||
}
|
||||
},
|
||||
receive: function(event, ui) {
|
||||
var data, merge_request_id, merge_request_url, new_state;
|
||||
new_state = $(this).data('state');
|
||||
merge_request_id = ui.item.data('iid');
|
||||
merge_request_url = ui.item.data('url');
|
||||
onAdd: function (e) {
|
||||
var data, issuableId, issuableUrl, newState;
|
||||
newState = e.to.dataset.state;
|
||||
issuableUrl = e.item.dataset.url;
|
||||
data = (function() {
|
||||
switch (new_state) {
|
||||
switch (newState) {
|
||||
case 'ongoing':
|
||||
return "merge_request[assignee_id]=" + gon.current_user_id;
|
||||
return opts.fieldName + '[assignee_id]=' + gon.current_user_id;
|
||||
case 'unassigned':
|
||||
return "merge_request[assignee_id]=";
|
||||
return opts.fieldName + '[assignee_id]=';
|
||||
case 'closed':
|
||||
return "merge_request[state_event]=close";
|
||||
return opts.fieldName + '[state_event]=close';
|
||||
}
|
||||
})();
|
||||
if ($(ui.sender).data('state') === "closed") {
|
||||
data += "&merge_request[state_event]=reopen";
|
||||
if (e.from.dataset.state === 'closed') {
|
||||
data += '&' + opts.fieldName + '[state_event]=reopen';
|
||||
}
|
||||
return Milestone.updateMergeRequest(ui.item, merge_request_url, data);
|
||||
|
||||
opts.updateCallback(e.item, issuableUrl, data);
|
||||
this.options.onUpdate.call(this, e);
|
||||
}
|
||||
}).disableSelection();
|
||||
});
|
||||
};
|
||||
|
||||
return Milestone;
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
this.container = opts.container || '';
|
||||
this.dropdownListSelector = '.js-builds-dropdown-container';
|
||||
this.getBuildsList = this.getBuildsList.bind(this);
|
||||
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +28,7 @@
|
|||
* All dropdown events are fired at the .dropdown-menu's parent element.
|
||||
*/
|
||||
bindEvents() {
|
||||
$(this.container).on('shown.bs.dropdown', this.getBuildsList);
|
||||
$(document).on('shown.bs.dropdown', this.container, this.getBuildsList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -455,7 +455,7 @@ require('vendor/task_list');
|
|||
var mergeRequestId = $form.data('noteable-iid');
|
||||
|
||||
if (ResolveService != null) {
|
||||
ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId);
|
||||
ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
bindEvents() {
|
||||
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
|
||||
$('#user_notification_email').on('change', this.submitForm);
|
||||
$('#user_notified_of_own_activity').on('change', this.submitForm);
|
||||
$('.update-username').on('ajax:before', this.beforeUpdateUsername);
|
||||
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
|
||||
$('.update-notifications').on('ajax:success', this.onUpdateNotifs);
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, consistent-return */
|
||||
/* global Shortcuts */
|
||||
/* global Mousetrap */
|
||||
|
||||
require('./shortcuts');
|
||||
|
||||
(function() {
|
||||
var 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; },
|
||||
hasProp = {}.hasOwnProperty;
|
||||
|
||||
this.ShortcutsBlob = (function(superClass) {
|
||||
extend(ShortcutsBlob, superClass);
|
||||
|
||||
function ShortcutsBlob(skipResetBindings) {
|
||||
ShortcutsBlob.__super__.constructor.call(this, skipResetBindings);
|
||||
Mousetrap.bind('y', ShortcutsBlob.copyToClipboard);
|
||||
}
|
||||
|
||||
ShortcutsBlob.copyToClipboard = function() {
|
||||
var clipboardButton;
|
||||
clipboardButton = $('.btn-clipboard');
|
||||
if (clipboardButton) {
|
||||
return clipboardButton.click();
|
||||
}
|
||||
};
|
||||
|
||||
return ShortcutsBlob;
|
||||
})(Shortcuts);
|
||||
}).call(this);
|
|
@ -0,0 +1,29 @@
|
|||
/* global Mousetrap */
|
||||
/* global Shortcuts */
|
||||
|
||||
require('./shortcuts');
|
||||
|
||||
const defaults = {
|
||||
skipResetBindings: false,
|
||||
fileBlobPermalinkUrl: null,
|
||||
};
|
||||
|
||||
class ShortcutsBlob extends Shortcuts {
|
||||
constructor(opts) {
|
||||
const options = Object.assign({}, defaults, opts);
|
||||
super(options.skipResetBindings);
|
||||
this.options = options;
|
||||
|
||||
Mousetrap.bind('y', this.moveToFilePermalink.bind(this));
|
||||
}
|
||||
|
||||
moveToFilePermalink() {
|
||||
if (this.options.fileBlobPermalinkUrl) {
|
||||
const hash = gl.utils.getLocationHash();
|
||||
const hashUrlString = hash ? `#${hash}` : '';
|
||||
gl.utils.visitUrl(`${this.options.fileBlobPermalinkUrl}${hashUrlString}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ShortcutsBlob;
|
|
@ -1,14 +1,12 @@
|
|||
/* eslint-disable arrow-parens, class-methods-use-this, no-param-reassign */
|
||||
/* global Cookies */
|
||||
|
||||
((global) => {
|
||||
let singleton;
|
||||
|
||||
(() => {
|
||||
const pinnedStateCookie = 'pin_nav';
|
||||
const sidebarBreakpoint = 1024;
|
||||
|
||||
const pageSelector = '.page-with-sidebar';
|
||||
const navbarSelector = '.navbar-fixed-top';
|
||||
const navbarSelector = '.navbar-gitlab';
|
||||
const sidebarWrapperSelector = '.sidebar-wrapper';
|
||||
const sidebarContentSelector = '.nav-sidebar';
|
||||
|
||||
|
@ -23,11 +21,12 @@
|
|||
|
||||
class Sidebar {
|
||||
constructor() {
|
||||
if (!singleton) {
|
||||
singleton = this;
|
||||
singleton.init();
|
||||
if (!Sidebar.singleton) {
|
||||
Sidebar.singleton = this;
|
||||
Sidebar.singleton.init();
|
||||
}
|
||||
return singleton;
|
||||
|
||||
return Sidebar.singleton;
|
||||
}
|
||||
|
||||
init() {
|
||||
|
@ -36,13 +35,16 @@
|
|||
window.innerWidth >= sidebarBreakpoint &&
|
||||
$(pageSelector).hasClass(expandedPageClass)
|
||||
);
|
||||
$(window).on('resize', () => this.setSidebarHeight());
|
||||
$(document)
|
||||
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
|
||||
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
|
||||
.on('click', 'html, body', (e) => this.handleClickEvent(e))
|
||||
.on('click', 'html, body, a, button', (e) => this.handleClickEvent(e))
|
||||
.on('DOMContentLoaded', () => this.renderState())
|
||||
.on('scroll', () => this.setSidebarHeight())
|
||||
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
|
||||
this.renderState();
|
||||
this.setSidebarHeight();
|
||||
}
|
||||
|
||||
handleClickEvent(e) {
|
||||
|
@ -65,6 +67,16 @@
|
|||
this.renderState();
|
||||
}
|
||||
|
||||
setSidebarHeight() {
|
||||
const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
|
||||
const diff = $navHeight - $('body').scrollTop();
|
||||
if (diff > 0) {
|
||||
$('.js-right-sidebar').outerHeight($(window).height() - diff);
|
||||
} else {
|
||||
$('.js-right-sidebar').outerHeight('100%');
|
||||
}
|
||||
}
|
||||
|
||||
togglePinnedState() {
|
||||
this.isPinned = !this.isPinned;
|
||||
if (!this.isPinned) {
|
||||
|
@ -88,10 +100,12 @@
|
|||
$pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
|
||||
|
||||
if (this.isExpanded) {
|
||||
setTimeout(() => $(sidebarContentSelector).niceScroll().updateScrollBar(), 200);
|
||||
const sidebarContent = $(sidebarContentSelector);
|
||||
setTimeout(() => { sidebarContent.niceScroll().updateScrollBar(); }, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global.Sidebar = Sidebar;
|
||||
})(window.gl || (window.gl = {}));
|
||||
window.gl = window.gl || {};
|
||||
gl.Sidebar = Sidebar;
|
||||
})();
|
||||
|
|
|
@ -33,13 +33,13 @@
|
|||
this.$toggleIcon.addClass('fa-caret-down');
|
||||
}
|
||||
|
||||
$('.file-title, .click-to-expand', this.file).on('click', (function (e) {
|
||||
$('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
|
||||
this.toggleDiff($(e.target));
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
SingleFileDiff.prototype.toggleDiff = function($target, cb) {
|
||||
if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
|
||||
if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
|
||||
this.isOpen = !this.isOpen;
|
||||
if (!this.isOpen && !this.hasError) {
|
||||
this.content.hide();
|
||||
|
|
|
@ -50,14 +50,15 @@
|
|||
return (
|
||||
children[target.index] ||
|
||||
children[target.index === 'first' ? 0 : -1] ||
|
||||
children[target.index === 'last' ? children.length - 1 : -1]
|
||||
children[target.index === 'last' ? children.length - 1 : -1] ||
|
||||
el
|
||||
);
|
||||
}
|
||||
|
||||
function getRect(el) {
|
||||
var rect = el.getBoundingClientRect();
|
||||
var width = rect.right - rect.left;
|
||||
var height = rect.bottom - rect.top;
|
||||
var height = rect.bottom - rect.top + 10;
|
||||
|
||||
return {
|
||||
x: rect.left,
|
|
@ -146,14 +146,26 @@
|
|||
}
|
||||
|
||||
goToTodoUrl(e) {
|
||||
const todoLink = $(this).data('url');
|
||||
const todoLink = this.dataset.url;
|
||||
let targetLink = e.target.getAttribute('href');
|
||||
|
||||
if (e.target.tagName === 'IMG') { // See if clicked target was Avatar
|
||||
targetLink = e.target.parentElement.getAttribute('href'); // Parent of Avatar is link
|
||||
}
|
||||
|
||||
if (!todoLink) {
|
||||
return;
|
||||
}
|
||||
// Allow Meta-Click or Mouse3-click to open in a new tab
|
||||
if (e.metaKey || e.which === 2) {
|
||||
|
||||
if (gl.utils.isMetaClick(e)) {
|
||||
e.preventDefault();
|
||||
return window.open(todoLink, '_blank');
|
||||
// Meta-Click on username leads to different URL than todoLink.
|
||||
// Turbolinks can resolve that URL, but window.open requires URL manually.
|
||||
if (targetLink !== todoLink) {
|
||||
return window.open(targetLink, '_blank');
|
||||
} else {
|
||||
return window.open(todoLink, '_blank');
|
||||
}
|
||||
} else {
|
||||
return gl.utils.visitUrl(todoLink);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,36 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue, VueResource, gl */
|
||||
window.Vue = require('vue');
|
||||
window.Vue.use(require('vue-resource'));
|
||||
require('../vue_common_component/commit');
|
||||
require('../vue_pagination/index');
|
||||
require('../boards/vue_resource_interceptor');
|
||||
require('./status');
|
||||
require('./store');
|
||||
require('./pipeline_url');
|
||||
require('./stage');
|
||||
require('./stages');
|
||||
require('./pipeline_actions');
|
||||
require('./time_ago');
|
||||
require('../lib/utils/common_utils');
|
||||
require('../vue_shared/vue_resource_interceptor');
|
||||
require('./pipelines');
|
||||
|
||||
(() => {
|
||||
const project = document.querySelector('.pipelines');
|
||||
const entry = document.querySelector('.vue-pipelines-index');
|
||||
const svgs = document.querySelector('.pipeline-svgs');
|
||||
$(() => new Vue({
|
||||
el: document.querySelector('.vue-pipelines-index'),
|
||||
|
||||
if (!entry) return null;
|
||||
return new Vue({
|
||||
el: entry,
|
||||
data: {
|
||||
data() {
|
||||
const project = document.querySelector('.pipelines');
|
||||
const svgs = document.querySelector('.pipeline-svgs').dataset;
|
||||
|
||||
// Transform svgs DOMStringMap to a plain Object.
|
||||
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
|
||||
|
||||
return {
|
||||
scope: project.dataset.url,
|
||||
store: new gl.PipelineStore(),
|
||||
svgs: svgs.dataset,
|
||||
},
|
||||
components: {
|
||||
'vue-pipelines': gl.VuePipelines,
|
||||
},
|
||||
template: `
|
||||
<vue-pipelines
|
||||
:scope='scope'
|
||||
:store='store'
|
||||
:svgs='svgs'
|
||||
>
|
||||
</vue-pipelines>
|
||||
`,
|
||||
});
|
||||
})();
|
||||
svgs: svgsObject,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
'vue-pipelines': gl.VuePipelines,
|
||||
},
|
||||
template: `
|
||||
<vue-pipelines
|
||||
:scope='scope'
|
||||
:store='store'
|
||||
:svgs='svgs'
|
||||
>
|
||||
</vue-pipelines>
|
||||
`,
|
||||
}));
|
||||
|
|
|
@ -50,9 +50,9 @@
|
|||
<button
|
||||
v-if='artifacts'
|
||||
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
|
||||
data-toggle="dropdown"
|
||||
title="Artifacts"
|
||||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
aria-label="Artifacts"
|
||||
>
|
||||
<i class="fa fa-download" aria-hidden="true"></i>
|
||||
|
@ -81,8 +81,7 @@
|
|||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.retry_path'
|
||||
aria-label="Retry"
|
||||
>
|
||||
aria-label="Retry">
|
||||
<i class="fa fa-repeat" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
|
@ -94,8 +93,7 @@
|
|||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.cancel_path'
|
||||
aria-label="Cancel"
|
||||
>
|
||||
aria-label="Cancel">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
/* global Vue, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
window.Vue = require('vue');
|
||||
require('../vue_shared/components/table_pagination');
|
||||
require('./store');
|
||||
require('../vue_shared/components/pipelines_table');
|
||||
|
||||
((gl) => {
|
||||
gl.VuePipelines = Vue.extend({
|
||||
|
||||
components: {
|
||||
runningPipeline: gl.VueRunningPipeline,
|
||||
pipelineActions: gl.VuePipelineActions,
|
||||
stages: gl.VueStages,
|
||||
commit: gl.CommitComponent,
|
||||
pipelineUrl: gl.VuePipelineUrl,
|
||||
pipelineHead: gl.VuePipelineHead,
|
||||
glPagination: gl.VueGlPagination,
|
||||
statusScope: gl.VueStatusScope,
|
||||
timeAgo: gl.VueTimeAgo,
|
||||
'gl-pagination': gl.VueGlPagination,
|
||||
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pipelines: [],
|
||||
|
@ -38,87 +38,29 @@
|
|||
change(pagenum, apiScope) {
|
||||
gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
|
||||
},
|
||||
author(pipeline) {
|
||||
if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' };
|
||||
if (pipeline.commit.author) return pipeline.commit.author;
|
||||
return {
|
||||
avatar_url: pipeline.commit.author_gravatar_url,
|
||||
web_url: `mailto:${pipeline.commit.author_email}`,
|
||||
username: pipeline.commit.author_name,
|
||||
};
|
||||
},
|
||||
ref(pipeline) {
|
||||
const { ref } = pipeline;
|
||||
return { name: ref.name, tag: ref.tag, ref_url: ref.path };
|
||||
},
|
||||
commitTitle(pipeline) {
|
||||
return pipeline.commit ? pipeline.commit.title : '';
|
||||
},
|
||||
commitSha(pipeline) {
|
||||
return pipeline.commit ? pipeline.commit.short_id : '';
|
||||
},
|
||||
commitUrl(pipeline) {
|
||||
return pipeline.commit ? pipeline.commit.commit_path : '';
|
||||
},
|
||||
match(string) {
|
||||
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="pipelines realtime-loading" v-if='pipelines.length < 1'>
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
<div class="table-holder" v-if='pipelines.length'>
|
||||
<table class="table ci-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="pipeline-status">Status</th>
|
||||
<th class="pipeline-info">Pipeline</th>
|
||||
<th class="pipeline-commit">Commit</th>
|
||||
<th class="pipeline-stages">Stages</th>
|
||||
<th class="pipeline-date"></th>
|
||||
<th class="pipeline-actions hidden-xs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="commit" v-for='pipeline in pipelines'>
|
||||
<status-scope
|
||||
:pipeline='pipeline'
|
||||
:match='match'
|
||||
:svgs='svgs'
|
||||
>
|
||||
</status-scope>
|
||||
<pipeline-url :pipeline='pipeline'></pipeline-url>
|
||||
<td>
|
||||
<commit
|
||||
:commit-icon-svg='svgs.commitIconSvg'
|
||||
:author='author(pipeline)'
|
||||
:tag="pipeline.ref.tag"
|
||||
:title='commitTitle(pipeline)'
|
||||
:commit-ref='ref(pipeline)'
|
||||
:short-sha='commitSha(pipeline)'
|
||||
:commit-url='commitUrl(pipeline)'
|
||||
>
|
||||
</commit>
|
||||
</td>
|
||||
<stages
|
||||
:pipeline='pipeline'
|
||||
:svgs='svgs'
|
||||
:match='match'
|
||||
>
|
||||
</stages>
|
||||
<time-ago :pipeline='pipeline' :svgs='svgs'></time-ago>
|
||||
<pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pipelines realtime-loading" v-if='pageRequest'>
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
|
||||
<div class="blank-state blank-state-no-icon"
|
||||
v-if="!pageRequest && pipelines.length === 0">
|
||||
<h2 class="blank-state-title js-blank-state-title">
|
||||
No pipelines to show
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="table-holder" v-if='!pageRequest && pipelines.length'>
|
||||
<pipelines-table-component
|
||||
:pipelines='pipelines'
|
||||
:svgs='svgs'>
|
||||
</pipelines-table-component>
|
||||
</div>
|
||||
|
||||
<gl-pagination
|
||||
v-if='pageInfo.total > pageInfo.perPage'
|
||||
v-if='!pageRequest && pipelines.length && pageInfo.total > pageInfo.perPage'
|
||||
:pagenum='pagenum'
|
||||
:change='change'
|
||||
:count='count.all'
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
required: true,
|
||||
},
|
||||
svgs: {
|
||||
type: DOMStringMap,
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
match: {
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/* global Vue, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
((gl) => {
|
||||
gl.VueStages = Vue.extend({
|
||||
components: {
|
||||
'vue-stage': gl.VueStage,
|
||||
},
|
||||
props: ['pipeline', 'svgs', 'match'],
|
||||
template: `
|
||||
<td class="stage-cell">
|
||||
<div
|
||||
class="stage-container dropdown js-mini-pipeline-graph"
|
||||
v-for='stage in pipeline.details.stages'
|
||||
>
|
||||
<vue-stage :stage='stage' :svgs='svgs' :match='match'></vue-stage>
|
||||
</div>
|
||||
</td>
|
||||
`,
|
||||
});
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -20,6 +20,7 @@ require('../vue_realtime_listener');
|
|||
|
||||
gl.PipelineStore = class {
|
||||
fetchDataLoop(Vue, pageNum, url, apiScope) {
|
||||
this.pageRequest = true;
|
||||
const updatePipelineNums = (count) => {
|
||||
const { all } = count;
|
||||
const running = count.running_or_pending;
|
||||
|
@ -41,16 +42,18 @@ require('../vue_realtime_listener');
|
|||
this.pageRequest = false;
|
||||
}, () => {
|
||||
this.pageRequest = false;
|
||||
return new Flash('Something went wrong on our end.');
|
||||
return new Flash('An error occurred while fetching the pipelines, please reload the page again.');
|
||||
});
|
||||
|
||||
goFetch();
|
||||
|
||||
const startTimeLoops = () => {
|
||||
this.timeLoopInterval = setInterval(() => {
|
||||
this.$children
|
||||
.filter(e => e.$options._componentTag === 'time-ago')
|
||||
.forEach(e => e.changeTime());
|
||||
this.$children[0].$children.reduce((acc, component) => {
|
||||
const timeAgoComponent = component.$children.filter(el => el.$options._componentTag === 'time-ago')[0];
|
||||
acc.push(timeAgoComponent);
|
||||
return acc;
|
||||
}, []).forEach(e => e.changeTime());
|
||||
}, 10000);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
/* global Vue, gl */
|
||||
/* eslint-disable no-param-reassign */
|
||||
|
||||
window.Vue = require('vue');
|
||||
require('../lib/utils/datetime_utility');
|
||||
|
||||
((gl) => {
|
||||
gl.VueTimeAgo = Vue.extend({
|
||||
data() {
|
||||
|
|
|
@ -14,5 +14,16 @@
|
|||
window.addEventListener('focus', startIntervals);
|
||||
window.addEventListener('blur', removeIntervals);
|
||||
document.addEventListener('beforeunload', removeAll);
|
||||
|
||||
// add removeAll methods to stack
|
||||
const stack = gl.VueRealtimeListener.reset;
|
||||
gl.VueRealtimeListener.reset = () => {
|
||||
gl.VueRealtimeListener.reset = stack;
|
||||
removeAll();
|
||||
stack();
|
||||
};
|
||||
};
|
||||
|
||||
// remove all event listeners and intervals
|
||||
gl.VueRealtimeListener.reset = () => undefined; // noop
|
||||
})(window.gl || (window.gl = {}));
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/* global Vue */
|
||||
|
||||
window.Vue = require('vue');
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue */
|
||||
|
||||
require('./pipelines_table_row');
|
||||
/**
|
||||
* Pipelines Table Component.
|
||||
*
|
||||
* Given an array of objects, renders a table.
|
||||
*/
|
||||
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.pipelines = gl.pipelines || {};
|
||||
|
||||
gl.pipelines.PipelinesTableComponent = Vue.component('pipelines-table-component', {
|
||||
|
||||
props: {
|
||||
pipelines: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => ([]),
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Remove this when we have webpack.
|
||||
*/
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
'pipelines-table-row-component': gl.pipelines.PipelinesTableRowComponent,
|
||||
},
|
||||
|
||||
template: `
|
||||
<table class="table ci-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="js-pipeline-status pipeline-status">Status</th>
|
||||
<th class="js-pipeline-info pipeline-info">Pipeline</th>
|
||||
<th class="js-pipeline-commit pipeline-commit">Commit</th>
|
||||
<th class="js-pipeline-stages pipeline-stages">Stages</th>
|
||||
<th class="js-pipeline-date pipeline-date"></th>
|
||||
<th class="js-pipeline-actions pipeline-actions hidden-xs"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="model in pipelines"
|
||||
v-bind:model="model">
|
||||
<tr is="pipelines-table-row-component"
|
||||
:pipeline="model"
|
||||
:svgs="svgs"></tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
`,
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,234 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
/* global Vue */
|
||||
|
||||
require('../../vue_pipelines_index/status');
|
||||
require('../../vue_pipelines_index/pipeline_url');
|
||||
require('../../vue_pipelines_index/stage');
|
||||
require('../../vue_pipelines_index/pipeline_actions');
|
||||
require('../../vue_pipelines_index/time_ago');
|
||||
require('./commit');
|
||||
/**
|
||||
* Pipeline table row.
|
||||
*
|
||||
* Given the received object renders a table row in the pipelines' table.
|
||||
*/
|
||||
(() => {
|
||||
window.gl = window.gl || {};
|
||||
gl.pipelines = gl.pipelines || {};
|
||||
|
||||
gl.pipelines.PipelinesTableRowComponent = Vue.component('pipelines-table-row-component', {
|
||||
|
||||
props: {
|
||||
pipeline: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO: Remove this when we have webpack;
|
||||
*/
|
||||
svgs: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
'commit-component': gl.CommitComponent,
|
||||
'pipeline-actions': gl.VuePipelineActions,
|
||||
'dropdown-stage': gl.VueStage,
|
||||
'pipeline-url': gl.VuePipelineUrl,
|
||||
'status-scope': gl.VueStatusScope,
|
||||
'time-ago': gl.VueTimeAgo,
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* If provided, returns the commit tag.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* This field needs a lot of verification, because of different possible cases:
|
||||
*
|
||||
* 1. person who is an author of a commit might be a GitLab user
|
||||
* 2. if person who is an author of a commit is a GitLab user he/she can have a GitLab avatar
|
||||
* 3. If GitLab user does not have avatar he/she might have a Gravatar
|
||||
* 4. If committer is not a GitLab User he/she can have a Gravatar
|
||||
* 5. We do not have consistent API object in this case
|
||||
* 6. We should improve API and the code
|
||||
*
|
||||
* @returns {Object|Undefined}
|
||||
*/
|
||||
commitAuthor() {
|
||||
let commitAuthorInformation;
|
||||
|
||||
// 1. person who is an author of a commit might be a GitLab user
|
||||
if (this.pipeline &&
|
||||
this.pipeline.commit &&
|
||||
this.pipeline.commit.author) {
|
||||
// 2. if person who is an author of a commit is a GitLab user
|
||||
// he/she can have a GitLab avatar
|
||||
if (this.pipeline.commit.author.avatar_url) {
|
||||
commitAuthorInformation = this.pipeline.commit.author;
|
||||
|
||||
// 3. If GitLab user does not have avatar he/she might have a Gravatar
|
||||
} else if (this.pipeline.commit.author_gravatar_url) {
|
||||
commitAuthorInformation = Object.assign({}, this.pipeline.commit.author, {
|
||||
avatar_url: this.pipeline.commit.author_gravatar_url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If committer is not a GitLab User he/she can have a Gravatar
|
||||
if (this.pipeline &&
|
||||
this.pipeline.commit) {
|
||||
commitAuthorInformation = {
|
||||
avatar_url: this.pipeline.commit.author_gravatar_url,
|
||||
web_url: `mailto:${this.pipeline.commit.author_email}`,
|
||||
username: this.pipeline.commit.author_name,
|
||||
};
|
||||
}
|
||||
|
||||
return commitAuthorInformation;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit tag.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitTag() {
|
||||
if (this.pipeline.ref &&
|
||||
this.pipeline.ref.tag) {
|
||||
return this.pipeline.ref.tag;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit ref.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* Matches `path` prop sent in the API to `ref_url` prop needed
|
||||
* in the commit component.
|
||||
*
|
||||
* @returns {Object|Undefined}
|
||||
*/
|
||||
commitRef() {
|
||||
if (this.pipeline.ref) {
|
||||
return Object.keys(this.pipeline.ref).reduce((accumulator, prop) => {
|
||||
if (prop === 'path') {
|
||||
accumulator.ref_url = this.pipeline.ref[prop];
|
||||
} else {
|
||||
accumulator[prop] = this.pipeline.ref[prop];
|
||||
}
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit url.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitUrl() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.commit_path) {
|
||||
return this.pipeline.commit.commit_path;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit short sha.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitShortSha() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.short_id) {
|
||||
return this.pipeline.commit.short_id;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* If provided, returns the commit title.
|
||||
* Needed to render the commit component column.
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
commitTitle() {
|
||||
if (this.pipeline.commit &&
|
||||
this.pipeline.commit.title) {
|
||||
return this.pipeline.commit.title;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* FIXME: This should not be in this component but in the components that
|
||||
* need this function.
|
||||
*
|
||||
* Used to render SVGs in the following components:
|
||||
* - status-scope
|
||||
* - dropdown-stage
|
||||
*
|
||||
* @param {String} string
|
||||
* @return {String}
|
||||
*/
|
||||
match(string) {
|
||||
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
|
||||
},
|
||||
},
|
||||
|
||||
template: `
|
||||
<tr class="commit">
|
||||
<status-scope
|
||||
:pipeline="pipeline"
|
||||
:svgs="svgs"
|
||||
:match="match">
|
||||
</status-scope>
|
||||
|
||||
<pipeline-url :pipeline="pipeline"></pipeline-url>
|
||||
|
||||
<td>
|
||||
<commit-component
|
||||
:tag="commitTag"
|
||||
:commit-ref="commitRef"
|
||||
:commit-url="commitUrl"
|
||||
:short-sha="commitShortSha"
|
||||
:title="commitTitle"
|
||||
:author="commitAuthor"
|
||||
:commit-icon-svg="svgs.commitIconSvg">
|
||||
</commit-component>
|
||||
</td>
|
||||
|
||||
<td class="stage-cell">
|
||||
<div class="stage-container dropdown js-mini-pipeline-graph"
|
||||
v-if="pipeline.details.stages.length > 0"
|
||||
v-for="stage in pipeline.details.stages">
|
||||
<dropdown-stage
|
||||
:stage="stage"
|
||||
:svgs="svgs"
|
||||
:match="match">
|
||||
</dropdown-stage>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<time-ago :pipeline="pipeline" :svgs="svgs"></time-ago>
|
||||
|
||||
<pipeline-actions :pipeline="pipeline" :svgs="svgs"></pipeline-actions>
|
||||
</tr>
|
||||
`,
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,23 @@
|
|||
/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars,
|
||||
no-param-reassign, no-plusplus */
|
||||
/* global Vue */
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
|
||||
|
||||
next((response) => {
|
||||
if (typeof response.data === 'string') {
|
||||
response.data = JSON.parse(response.data);
|
||||
}
|
||||
|
||||
Vue.activeResources--;
|
||||
});
|
||||
});
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
// needed in order to not break the tests.
|
||||
if ($.rails) {
|
||||
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
|
||||
}
|
||||
next();
|
||||
});
|
|
@ -2,7 +2,6 @@
|
|||
* This is a manifest file that'll automatically include all the stylesheets available in this directory
|
||||
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
|
||||
* the top of the compiled file, but it's generally better to create a new file per style scope.
|
||||
*= require jquery-ui/datepicker
|
||||
*= require jquery-ui/autocomplete
|
||||
*= require jquery.atwho
|
||||
*= require select2
|
||||
|
@ -19,6 +18,8 @@
|
|||
* directory.
|
||||
*/
|
||||
|
||||
@import "../../../node_modules/pikaday/scss/pikaday";
|
||||
|
||||
/*
|
||||
* GitLab UI framework
|
||||
*/
|
||||
|
|
|
@ -128,8 +128,7 @@
|
|||
|
||||
.note-action-button .link-highlight,
|
||||
.toolbar-btn,
|
||||
.dropdown-toggle-caret,
|
||||
.fa:not(.fa-bell) {
|
||||
.dropdown-toggle-caret {
|
||||
@include transition(color);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
.avatar {
|
||||
@extend .avatar-circle;
|
||||
@include transition-property(none);
|
||||
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
}
|
||||
|
||||
.user-calendar-activities {
|
||||
direction: ltr;
|
||||
|
||||
.str-truncated {
|
||||
max-width: 70%;
|
||||
}
|
||||
|
@ -43,3 +45,56 @@
|
|||
float: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pika-single.gitlab-theme {
|
||||
.pika-label {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 2px 0;
|
||||
color: $note-disabled-comment-color;
|
||||
font-weight: normal;
|
||||
text-transform: lowercase;
|
||||
border-top: 1px solid $calendar-border-color;
|
||||
}
|
||||
|
||||
abbr {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid $calendar-border-color;
|
||||
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-day {
|
||||
border-radius: 0;
|
||||
background-color: $white-light;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.is-today {
|
||||
.pika-day {
|
||||
color: inherit;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.is-selected .pika-day,
|
||||
.pika-day:hover,
|
||||
.is-today .pika-day:hover {
|
||||
background: $gl-primary;
|
||||
color: $white-light;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -253,6 +253,8 @@ li.note {
|
|||
.progress {
|
||||
margin-bottom: 0;
|
||||
margin-top: 4px;
|
||||
box-shadow: none;
|
||||
background-color: $border-gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,7 +125,6 @@
|
|||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
max-width: 280px;
|
||||
min-width: 240px;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0;
|
||||
|
@ -137,6 +136,10 @@
|
|||
border-radius: $border-radius-base;
|
||||
box-shadow: 0 2px 4px $dropdown-shadow-color;
|
||||
|
||||
.filtered-search-input-container & {
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
&.is-loading {
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
|
@ -502,119 +505,16 @@
|
|||
max-height: 230px;
|
||||
}
|
||||
|
||||
.ui-widget {
|
||||
table {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&.ui-datepicker-inline {
|
||||
padding: 0 10px;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ui-datepicker-header {
|
||||
padding: 0 8px 10px;
|
||||
border: 0;
|
||||
|
||||
.ui-icon {
|
||||
background: none;
|
||||
font-size: 20px;
|
||||
text-indent: 0;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
color: $dropdown-title-btn-color;
|
||||
font: normal normal normal 14px/1 FontAwesome;
|
||||
font-size: inherit;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-calendar {
|
||||
.ui-state-hover,
|
||||
.ui-state-active {
|
||||
color: $white-light;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-prev,
|
||||
.ui-datepicker-next {
|
||||
top: 0;
|
||||
height: 15px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
|
||||
.ui-icon::before {
|
||||
color: $md-link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-prev {
|
||||
left: 0;
|
||||
|
||||
.ui-icon::before {
|
||||
content: '\f104';
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-next {
|
||||
right: 0;
|
||||
|
||||
.ui-icon::before {
|
||||
content: '\f105';
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0;
|
||||
border: 1px solid $calendar-border-color;
|
||||
|
||||
&:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
line-height: 17px;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-title {
|
||||
color: $gl-text-color;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
font-weight: normal;
|
||||
}
|
||||
.pika-single {
|
||||
position: relative!important;
|
||||
top: 0!important;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 2px 0;
|
||||
color: $note-disabled-comment-color;
|
||||
font-weight: normal;
|
||||
text-transform: lowercase;
|
||||
border-top: 1px solid $calendar-border-color;
|
||||
}
|
||||
|
||||
.ui-datepicker-unselectable {
|
||||
background-color: $gray-light;
|
||||
.pika-lendar {
|
||||
margin-top: -5px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -231,3 +231,46 @@ span.idiff {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-title-flex-parent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: $gray-light;
|
||||
border-bottom: 1px solid $border-color;
|
||||
padding: 5px $gl-padding;
|
||||
margin: 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
|
||||
.file-header-content {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.btn-clipboard {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
small {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.file-actions {
|
||||
white-space: nowrap;
|
||||
|
||||
.btn {
|
||||
padding: 0 10px;
|
||||
font-size: 13px;
|
||||
line-height: 28px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,6 +222,10 @@ header {
|
|||
float: right;
|
||||
border-top: none;
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
float: none;
|
||||
}
|
||||
|
@ -272,7 +276,7 @@ header {
|
|||
|
||||
.header-user {
|
||||
.dropdown-menu-nav {
|
||||
width: 140px;
|
||||
min-width: 140px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,42 +2,6 @@
|
|||
font-family: $regular_font;
|
||||
font-size: $font-size-base;
|
||||
|
||||
&.ui-datepicker,
|
||||
&.ui-datepicker-inline {
|
||||
border: 1px solid $jq-ui-border;
|
||||
padding: 10px;
|
||||
width: 270px;
|
||||
|
||||
.ui-datepicker-header {
|
||||
background: $white-light;
|
||||
border-color: $jq-ui-border;
|
||||
|
||||
.ui-datepicker-prev,
|
||||
.ui-datepicker-next {
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.ui-datepicker-prev {
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
.ui-datepicker-next {
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.ui-state-hover {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-datepicker-calendar td a {
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-autocomplete {
|
||||
border-color: $jq-ui-border;
|
||||
padding: 0;
|
||||
|
@ -59,25 +23,4 @@
|
|||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ui-datepicker-calendar {
|
||||
.ui-state-active,
|
||||
.ui-state-hover,
|
||||
.ui-state-focus {
|
||||
border: 1px solid $gl-primary;
|
||||
background: $gl-primary;
|
||||
color: $white-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-sortable-handle {
|
||||
cursor: move;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
|
||||
&:active {
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,3 +307,7 @@ ul.controls {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.indent-list {
|
||||
padding: 10px 0 0 30px;
|
||||
}
|
||||
|
|
|
@ -283,10 +283,7 @@
|
|||
}
|
||||
|
||||
.layout-nav {
|
||||
position: fixed;
|
||||
top: $header-height;
|
||||
width: 100%;
|
||||
z-index: 11;
|
||||
background: $gray-light;
|
||||
border-bottom: 1px solid $border-color;
|
||||
transition: padding $sidebar-transition-duration;
|
||||
|
@ -419,15 +416,20 @@
|
|||
}
|
||||
|
||||
.page-with-layout-nav {
|
||||
margin-top: $header-height + 2;
|
||||
|
||||
.right-sidebar {
|
||||
top: ($header-height * 2) + 2;
|
||||
}
|
||||
|
||||
.build-sidebar {
|
||||
top: ($header-height * 3) + 3;
|
||||
|
||||
&.affix {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.activities {
|
||||
|
||||
.nav-block {
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
|
|
|
@ -6,8 +6,22 @@
|
|||
|
||||
.pagination {
|
||||
padding: 0;
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.separator,
|
||||
.separator:hover {
|
||||
a {
|
||||
cursor: default;
|
||||
background-color: $gray-light;
|
||||
padding: $gl-vert-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.gap,
|
||||
.gap:hover {
|
||||
background-color: $gray-light;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.page-with-sidebar {
|
||||
padding: $header-height 0 25px;
|
||||
padding-bottom: 25px;
|
||||
transition: padding $sidebar-transition-duration;
|
||||
|
||||
&.page-sidebar-pinned {
|
||||
|
@ -208,7 +208,9 @@ header.header-sidebar-pinned {
|
|||
padding-right: 0;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
padding-right: $sidebar_collapsed_width;
|
||||
.content-wrapper {
|
||||
padding-right: $sidebar_collapsed_width;
|
||||
}
|
||||
|
||||
.merge-request-tabs-holder.affix {
|
||||
right: $sidebar_collapsed_width;
|
||||
|
@ -234,7 +236,9 @@ header.header-sidebar-pinned {
|
|||
}
|
||||
|
||||
@media (min-width: $screen-md-min) {
|
||||
padding-right: $gutter_width;
|
||||
.content-wrapper {
|
||||
padding-right: $gutter_width;
|
||||
}
|
||||
|
||||
&:not(.with-overlay) .merge-request-tabs-holder.affix {
|
||||
right: $gutter_width;
|
||||
|
@ -252,4 +256,9 @@ header.header-sidebar-pinned {
|
|||
|
||||
.right-sidebar {
|
||||
border-left: 1px solid $border-color;
|
||||
|
||||
&.affix {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,6 +138,13 @@ pre {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: $gl-grayish-blue;
|
||||
padding: 0 0 0 15px;
|
||||
margin: 0;
|
||||
border-left: 3px solid $white-dark;
|
||||
}
|
||||
|
||||
span.highlight_word {
|
||||
background-color: $highlighted-highlight-word !important;
|
||||
}
|
||||
|
|
|
@ -298,12 +298,8 @@
|
|||
|
||||
.issue-boards-sidebar {
|
||||
&.right-sidebar {
|
||||
top: 153px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
top: 220px;
|
||||
}
|
||||
}
|
||||
|
||||
.issuable-sidebar-header {
|
||||
|
|
|
@ -159,7 +159,6 @@
|
|||
|
||||
.commit-row-description {
|
||||
font-size: 14px;
|
||||
border-left: 1px solid $white-normal;
|
||||
padding: 10px 15px;
|
||||
margin: 10px 0;
|
||||
background: $gray-light;
|
||||
|
|
|
@ -284,7 +284,11 @@
|
|||
|
||||
.events-description {
|
||||
line-height: 65px;
|
||||
padding-left: $gl-padding;
|
||||
padding: 0 $gl-padding;
|
||||
}
|
||||
|
||||
.events-info {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,9 +34,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.file-title {
|
||||
.file-title,
|
||||
.file-title-flex-parent {
|
||||
cursor: pointer;
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $gray-normal;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
word-wrap: break-word;
|
||||
|
||||
.md {
|
||||
color: $gl-grayish-blue;
|
||||
font-size: $gl-font-size;
|
||||
|
||||
.label {
|
||||
|
|
|
@ -189,11 +189,10 @@
|
|||
}
|
||||
|
||||
.right-sidebar {
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
top: $header-height;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
transition: width .3s;
|
||||
background: $gray-light;
|
||||
padding: 10px 20px;
|
||||
|
@ -461,8 +460,19 @@
|
|||
|
||||
.issuable-list {
|
||||
li {
|
||||
|
||||
.issue-box {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.issue-info-container {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
padding-right: $gl-padding;
|
||||
}
|
||||
|
||||
.issue-check {
|
||||
float: left;
|
||||
padding-right: $gl-padding;
|
||||
margin-bottom: 10px;
|
||||
min-width: 15px;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.issues-list {
|
||||
.issue {
|
||||
padding: 10px $gl-padding;
|
||||
padding: 10px 0 10px $gl-padding;
|
||||
position: relative;
|
||||
|
||||
.title {
|
||||
|
@ -148,3 +148,7 @@ ul.related-merge-requests > li {
|
|||
border: 1px solid $border-gray-normal;
|
||||
}
|
||||
}
|
||||
|
||||
.recaptcha {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,22 @@
|
|||
}
|
||||
|
||||
.manage-labels-list {
|
||||
> li:not(.empty-message) {
|
||||
background-color: $white-light;
|
||||
cursor: move;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
|
||||
&:active {
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
}
|
||||
|
||||
&.sortable-ghost {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-action {
|
||||
color: $gl-text-color;
|
||||
|
||||
|
@ -259,3 +275,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-link {
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
|
|
@ -80,19 +80,28 @@
|
|||
.ci_widget {
|
||||
border-bottom: 1px solid $well-inner-border;
|
||||
color: $gl-text-color;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
|
||||
i,
|
||||
svg {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-right: 4px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&.ci-success_with_warnings {
|
||||
& > span {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
i {
|
||||
color: $gl-warning;
|
||||
}
|
||||
@media (max-width: $screen-xs-max) {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,6 +111,43 @@
|
|||
padding: $gl-padding;
|
||||
}
|
||||
|
||||
.mr-widget-pipeline-graph {
|
||||
flex-shrink: 0;
|
||||
|
||||
.dropdown-menu {
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.ci-action-icon-wrapper {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) {
|
||||
.stage-cell {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
order: 1;
|
||||
margin-top: $gl-padding-top;
|
||||
border-radius: 3px;
|
||||
background-color: $white-light;
|
||||
border: 1px solid $gray-darker;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.dropdown-menu {
|
||||
margin-left: -97.5px;
|
||||
}
|
||||
|
||||
.arrow-up::before,
|
||||
.arrow-up::after, {
|
||||
margin-left: 97.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.normal {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
@ -223,8 +269,15 @@
|
|||
|
||||
.mr-list {
|
||||
.merge-request {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 0 10px 15px;
|
||||
position: relative;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
.issue-info-container {
|
||||
-webkit-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.merge-request-title {
|
||||
margin-bottom: 2px;
|
||||
|
@ -430,7 +483,7 @@
|
|||
background-color: $white-light;
|
||||
|
||||
&.affix {
|
||||
top: 100px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
transition: right .15s;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue