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
|
"localStorage": false
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"filenames"
|
"filenames",
|
||||||
|
"import"
|
||||||
],
|
],
|
||||||
|
"settings": {
|
||||||
|
"import/resolver": {
|
||||||
|
"webpack": {
|
||||||
|
"config": "./config/webpack.config.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
|
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
|
||||||
"no-multiple-empty-lines": ["error", { "max": 1 }],
|
"no-multiple-empty-lines": ["error", { "max": 1 }]
|
||||||
"import/no-extraneous-dependencies": "off",
|
|
||||||
"import/no-unresolved": "off"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
*.erb
|
*.erb
|
||||||
lib/gitlab/sanitizers/svg/whitelist.rb
|
lib/gitlab/sanitizers/svg/whitelist.rb
|
||||||
lib/gitlab/diff/position_tracer.rb
|
lib/gitlab/diff/position_tracer.rb
|
||||||
|
app/policies/project_policy.rb
|
||||||
|
|
|
@ -107,7 +107,10 @@ setup-test-env:
|
||||||
<<: *dedicated-runner
|
<<: *dedicated-runner
|
||||||
stage: prepare
|
stage: prepare
|
||||||
script:
|
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 rake gitlab:assets:compile
|
||||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||||
artifacts:
|
artifacts:
|
||||||
|
@ -163,64 +166,7 @@ spinach 7 10: *spinach-knapsack
|
||||||
spinach 8 10: *spinach-knapsack
|
spinach 8 10: *spinach-knapsack
|
||||||
spinach 9 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
|
# Other generic tests
|
||||||
|
|
||||||
.ruby-static-analysis: &ruby-static-analysis
|
.ruby-static-analysis: &ruby-static-analysis
|
||||||
variables:
|
variables:
|
||||||
SIMPLECOV: "false"
|
SIMPLECOV: "false"
|
||||||
|
@ -243,6 +189,7 @@ rubocop:
|
||||||
|
|
||||||
rake haml_lint: *exec
|
rake haml_lint: *exec
|
||||||
rake scss_lint: *exec
|
rake scss_lint: *exec
|
||||||
|
rake config_lint: *exec
|
||||||
rake brakeman: *exec
|
rake brakeman: *exec
|
||||||
rake flay: *exec
|
rake flay: *exec
|
||||||
license_finder: *exec
|
license_finder: *exec
|
||||||
|
@ -302,13 +249,12 @@ karma:
|
||||||
<<: *use-db
|
<<: *use-db
|
||||||
<<: *dedicated-runner
|
<<: *dedicated-runner
|
||||||
script:
|
script:
|
||||||
- npm link istanbul
|
|
||||||
- bundle exec rake karma
|
- bundle exec rake karma
|
||||||
artifacts:
|
artifacts:
|
||||||
name: coverage-javascript
|
name: coverage-javascript
|
||||||
expire_in: 31d
|
expire_in: 31d
|
||||||
paths:
|
paths:
|
||||||
- coverage-javascript/default/
|
- coverage-javascript/
|
||||||
|
|
||||||
lint-doc:
|
lint-doc:
|
||||||
stage: test
|
stage: test
|
||||||
|
@ -381,11 +327,9 @@ lint:javascript:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
stage: test
|
stage: test
|
||||||
image: "node:7.1"
|
before_script: []
|
||||||
before_script:
|
|
||||||
- npm install
|
|
||||||
script:
|
script:
|
||||||
- npm --silent run eslint
|
- yarn run eslint
|
||||||
|
|
||||||
lint:javascript:report:
|
lint:javascript:report:
|
||||||
<<: *dedicated-runner
|
<<: *dedicated-runner
|
||||||
|
@ -393,12 +337,10 @@ lint:javascript:report:
|
||||||
paths:
|
paths:
|
||||||
- node_modules/
|
- node_modules/
|
||||||
stage: post-test
|
stage: post-test
|
||||||
image: "node:7.1"
|
before_script: []
|
||||||
before_script:
|
|
||||||
- npm install
|
|
||||||
script:
|
script:
|
||||||
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
|
- 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:
|
artifacts:
|
||||||
name: eslint-report
|
name: eslint-report
|
||||||
expire_in: 31d
|
expire_in: 31d
|
||||||
|
@ -450,9 +392,9 @@ pages:
|
||||||
script:
|
script:
|
||||||
- mv public/ .public/
|
- mv public/ .public/
|
||||||
- mkdir public/
|
- mkdir public/
|
||||||
- mv coverage public/coverage-ruby
|
- mv coverage/ public/coverage-ruby/ || true
|
||||||
- mv coverage-javascript/default/ public/coverage-javascript/
|
- mv coverage-javascript/ public/coverage-javascript/ || true
|
||||||
- mv eslint-report.html public/
|
- mv eslint-report.html public/ || true
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- public
|
- public
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
|
|
||||||
(How one can reproduce the issue - this is very important)
|
(How one can reproduce the issue - this is very important)
|
||||||
|
|
||||||
### Expected behavior
|
### What is the current *bug* behavior?
|
||||||
|
|
||||||
(What you should see instead)
|
|
||||||
|
|
||||||
### Actual behavior
|
|
||||||
|
|
||||||
(What actually happens)
|
(What actually happens)
|
||||||
|
|
||||||
|
### What is the expected *correct* behavior?
|
||||||
|
|
||||||
|
(What you should see instead)
|
||||||
|
|
||||||
### Relevant logs and/or screenshots
|
### Relevant logs and/or screenshots
|
||||||
|
|
||||||
(Paste any relevant logs - please use code blocks (```) to format console output,
|
(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)
|
(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
|
#### Results of GitLab application Check
|
||||||
|
|
||||||
(For installations with omnibus-gitlab package run and paste the output of:
|
(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)
|
(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
|
### Possible fixes
|
||||||
|
|
||||||
(If you can, link to the line of code that might be responsible for the problem)
|
(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
|
inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.1
|
TargetRubyVersion: 2.3
|
||||||
# Cop names are not d§splayed in offense messages by default. Change behavior
|
# Cop names are not d§splayed in offense messages by default. Change behavior
|
||||||
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
||||||
# option.
|
# option.
|
||||||
|
@ -31,8 +31,7 @@ AllCops:
|
||||||
- 'lib/gitlab/seeder.rb'
|
- 'lib/gitlab/seeder.rb'
|
||||||
- 'generator_templates/**/*'
|
- 'generator_templates/**/*'
|
||||||
|
|
||||||
|
# Style #######################################################################
|
||||||
##################### Style ##################################
|
|
||||||
|
|
||||||
# Check indentation of private/protected visibility modifiers.
|
# Check indentation of private/protected visibility modifiers.
|
||||||
Style/AccessModifierIndentation:
|
Style/AccessModifierIndentation:
|
||||||
|
@ -340,6 +339,10 @@ Style/OpMethod:
|
||||||
Style/ParenthesesAroundCondition:
|
Style/ParenthesesAroundCondition:
|
||||||
Enabled: true
|
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.
|
# Checks for parentheses that seem not to serve any purpose.
|
||||||
Style/RedundantParentheses:
|
Style/RedundantParentheses:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
@ -471,7 +474,7 @@ Style/WhileUntilModifier:
|
||||||
Style/WordArray:
|
Style/WordArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
#################### Metrics ################################
|
# Metrics #####################################################################
|
||||||
|
|
||||||
# A calculated magnitude based on number of assignments,
|
# A calculated magnitude based on number of assignments,
|
||||||
# branches, and conditions.
|
# branches, and conditions.
|
||||||
|
@ -516,8 +519,7 @@ Metrics/PerceivedComplexity:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
Max: 18
|
Max: 18
|
||||||
|
|
||||||
|
# Lint ########################################################################
|
||||||
#################### Lint ################################
|
|
||||||
|
|
||||||
# Checks for useless access modifiers.
|
# Checks for useless access modifiers.
|
||||||
Lint/UselessAccessModifier:
|
Lint/UselessAccessModifier:
|
||||||
|
@ -570,6 +572,10 @@ Lint/ElseLayout:
|
||||||
Lint/EmptyEnsure:
|
Lint/EmptyEnsure:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
# Checks for the presence of `when` branches without a body.
|
||||||
|
Lint/EmptyWhen:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
# Align ends correctly.
|
# Align ends correctly.
|
||||||
Lint/EndAlignment:
|
Lint/EndAlignment:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
@ -679,8 +685,7 @@ Lint/UselessSetterCall:
|
||||||
Lint/Void:
|
Lint/Void:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
# Performance #################################################################
|
||||||
##################### Performance ############################
|
|
||||||
|
|
||||||
# Use `casecmp` rather than `downcase ==`.
|
# Use `casecmp` rather than `downcase ==`.
|
||||||
Performance/Casecmp:
|
Performance/Casecmp:
|
||||||
|
@ -718,8 +723,7 @@ Performance/StringReplacement:
|
||||||
Performance/TimesMap:
|
Performance/TimesMap:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
# Rails #######################################################################
|
||||||
##################### Rails ##################################
|
|
||||||
|
|
||||||
# Enables Rails cops.
|
# Enables Rails cops.
|
||||||
Rails:
|
Rails:
|
||||||
|
@ -767,12 +771,16 @@ Rails/ReadWriteAttribute:
|
||||||
Rails/ScopeArgs:
|
Rails/ScopeArgs:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
##################### RSpec ##################################
|
# RSpec #######################################################################
|
||||||
|
|
||||||
# Check that instances are not being stubbed globally.
|
# Check that instances are not being stubbed globally.
|
||||||
RSpec/AnyInstance:
|
RSpec/AnyInstance:
|
||||||
Enabled: false
|
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
|
# Check that the first argument to the top level describe is the tested class or
|
||||||
# module.
|
# module.
|
||||||
RSpec/DescribeClass:
|
RSpec/DescribeClass:
|
||||||
|
@ -801,6 +809,10 @@ RSpec/ExampleWording:
|
||||||
not: does not
|
not: does not
|
||||||
IgnoredWords: []
|
IgnoredWords: []
|
||||||
|
|
||||||
|
# Checks for `expect(...)` calls containing literal values.
|
||||||
|
RSpec/ExpectActual:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
# Checks the file and folder naming of the spec file.
|
# Checks the file and folder naming of the spec file.
|
||||||
RSpec/FilePath:
|
RSpec/FilePath:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -828,3 +840,9 @@ RSpec/NotToNot:
|
||||||
# Prefer using verifying doubles over normal doubles.
|
# Prefer using verifying doubles over normal doubles.
|
||||||
RSpec/VerifiedDoubles:
|
RSpec/VerifiedDoubles:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# Custom ######################################################################
|
||||||
|
|
||||||
|
# Disallow the `git` and `github` arguments in the Gemfile.
|
||||||
|
GemFetcher:
|
||||||
|
Enabled: true
|
||||||
|
|
|
@ -21,10 +21,6 @@ Lint/AmbiguousRegexpLiteral:
|
||||||
Lint/AssignmentInCondition:
|
Lint/AssignmentInCondition:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
Lint/EmptyWhen:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 20
|
# Offense count: 20
|
||||||
Lint/HandleExceptions:
|
Lint/HandleExceptions:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
@ -80,19 +76,11 @@ Performance/RedundantMatch:
|
||||||
Performance/RedundantMerge:
|
Performance/RedundantMerge:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 7
|
|
||||||
RSpec/BeEql:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 15
|
# Offense count: 15
|
||||||
# Configuration parameters: CustomIncludeMethods.
|
# Configuration parameters: CustomIncludeMethods.
|
||||||
RSpec/EmptyExampleGroup:
|
RSpec/EmptyExampleGroup:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 24
|
|
||||||
RSpec/ExpectActual:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 58
|
# Offense count: 58
|
||||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||||
# SupportedStyles: implicit, each, example
|
# SupportedStyles: implicit, each, example
|
||||||
|
@ -424,11 +412,6 @@ Style/RaiseArgs:
|
||||||
Style/RedundantBegin:
|
Style/RedundantBegin:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
# Offense count: 1
|
|
||||||
# Cop supports --auto-correct.
|
|
||||||
Style/RedundantException:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
# Offense count: 29
|
# Offense count: 29
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
Style/RedundantFreeze:
|
Style/RedundantFreeze:
|
||||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -2,6 +2,13 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
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)
|
## 8.16.4 (2017-02-02)
|
||||||
|
|
||||||
- Support non-ASCII characters in GFM autocomplete. !8729
|
- Support non-ASCII characters in GFM autocomplete. !8729
|
||||||
|
@ -174,6 +181,13 @@ entry.
|
||||||
- Add margin to markdown math blocks.
|
- Add margin to markdown math blocks.
|
||||||
- Add hover state to MR comment reply button.
|
- 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)
|
## 8.15.4 (2017-01-09)
|
||||||
|
|
||||||
- Make successful pipeline emails off for watchers. !8176
|
- Make successful pipeline emails off for watchers. !8176
|
||||||
|
@ -437,6 +451,13 @@ entry.
|
||||||
- Whitelist next project names: help, ci, admin, search. !8227
|
- Whitelist next project names: help, ci, admin, search. !8227
|
||||||
- Adds back CSS for progress-bars. !8237
|
- 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)
|
## 8.14.8 (2017-01-25)
|
||||||
|
|
||||||
- Accept environment variables from the `pre-receive` script. !7967
|
- Accept environment variables from the `pre-receive` script. !7967
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
- [Issue weight](#issue-weight)
|
- [Issue weight](#issue-weight)
|
||||||
- [Regression issues](#regression-issues)
|
- [Regression issues](#regression-issues)
|
||||||
- [Technical debt](#technical-debt)
|
- [Technical debt](#technical-debt)
|
||||||
|
- [Stewardship](#stewardship)
|
||||||
- [Merge requests](#merge-requests)
|
- [Merge requests](#merge-requests)
|
||||||
- [Merge request guidelines](#merge-request-guidelines)
|
- [Merge request guidelines](#merge-request-guidelines)
|
||||||
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
|
- [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
|
Make sure to mention the merge request that the `technical debt` issue is
|
||||||
associated with in the description of the issue.
|
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
|
## Merge requests
|
||||||
|
|
||||||
We welcome merge requests with fixes and improvements to GitLab code, tests,
|
We welcome merge requests with fixes and improvements to GitLab code, tests,
|
||||||
|
@ -414,7 +430,7 @@ merge request:
|
||||||
1. [Newlines styleguide][newlines-styleguide]
|
1. [Newlines styleguide][newlines-styleguide]
|
||||||
1. [Testing](doc/development/testing.md)
|
1. [Testing](doc/development/testing.md)
|
||||||
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
|
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. [SCSS styleguide][scss-styleguide]
|
||||||
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
|
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
|
||||||
contributors to enhance security
|
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-gitlab', '~> 1.0.2'
|
||||||
gem 'omniauth-google-oauth2', '~> 0.4.1'
|
gem 'omniauth-google-oauth2', '~> 0.4.1'
|
||||||
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
|
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
|
||||||
|
gem 'omniauth-oauth2-generic', '~> 0.2.2'
|
||||||
gem 'omniauth-saml', '~> 1.7.0'
|
gem 'omniauth-saml', '~> 1.7.0'
|
||||||
gem 'omniauth-shibboleth', '~> 1.2.0'
|
gem 'omniauth-shibboleth', '~> 1.2.0'
|
||||||
gem 'omniauth-twitter', '~> 1.2.0'
|
gem 'omniauth-twitter', '~> 1.2.0'
|
||||||
|
@ -284,7 +285,7 @@ group :development, :test do
|
||||||
gem 'rspec-retry', '~> 0.4.5'
|
gem 'rspec-retry', '~> 0.4.5'
|
||||||
gem 'spinach-rails', '~> 0.2.1'
|
gem 'spinach-rails', '~> 0.2.1'
|
||||||
gem 'spinach-rerun-reporter', '~> 0.0.2'
|
gem 'spinach-rerun-reporter', '~> 0.0.2'
|
||||||
gem 'rspec_profiling'
|
gem 'rspec_profiling', '~> 0.0.5'
|
||||||
|
|
||||||
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
|
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
|
||||||
gem 'minitest', '~> 5.7.0'
|
gem 'minitest', '~> 5.7.0'
|
||||||
|
|
11
Gemfile.lock
11
Gemfile.lock
|
@ -483,6 +483,8 @@ GEM
|
||||||
omniauth-oauth2 (1.3.1)
|
omniauth-oauth2 (1.3.1)
|
||||||
oauth2 (~> 1.0)
|
oauth2 (~> 1.0)
|
||||||
omniauth (~> 1.2)
|
omniauth (~> 1.2)
|
||||||
|
omniauth-oauth2-generic (0.2.2)
|
||||||
|
omniauth-oauth2 (~> 1.0)
|
||||||
omniauth-saml (1.7.0)
|
omniauth-saml (1.7.0)
|
||||||
omniauth (~> 1.3)
|
omniauth (~> 1.3)
|
||||||
ruby-saml (~> 1.4)
|
ruby-saml (~> 1.4)
|
||||||
|
@ -638,7 +640,7 @@ GEM
|
||||||
rspec-retry (0.4.5)
|
rspec-retry (0.4.5)
|
||||||
rspec-core
|
rspec-core
|
||||||
rspec-support (3.5.0)
|
rspec-support (3.5.0)
|
||||||
rspec_profiling (0.0.4)
|
rspec_profiling (0.0.5)
|
||||||
activerecord
|
activerecord
|
||||||
pg
|
pg
|
||||||
rails
|
rails
|
||||||
|
@ -738,7 +740,7 @@ GEM
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.3.11)
|
sqlite3 (1.3.13)
|
||||||
stackprof (0.2.10)
|
stackprof (0.2.10)
|
||||||
state_machines (0.4.0)
|
state_machines (0.4.0)
|
||||||
state_machines-activemodel (0.4.0)
|
state_machines-activemodel (0.4.0)
|
||||||
|
@ -931,6 +933,7 @@ DEPENDENCIES
|
||||||
omniauth-gitlab (~> 1.0.2)
|
omniauth-gitlab (~> 1.0.2)
|
||||||
omniauth-google-oauth2 (~> 0.4.1)
|
omniauth-google-oauth2 (~> 0.4.1)
|
||||||
omniauth-kerberos (~> 0.3.0)
|
omniauth-kerberos (~> 0.3.0)
|
||||||
|
omniauth-oauth2-generic (~> 0.2.2)
|
||||||
omniauth-saml (~> 1.7.0)
|
omniauth-saml (~> 1.7.0)
|
||||||
omniauth-shibboleth (~> 1.2.0)
|
omniauth-shibboleth (~> 1.2.0)
|
||||||
omniauth-twitter (~> 1.2.0)
|
omniauth-twitter (~> 1.2.0)
|
||||||
|
@ -962,7 +965,7 @@ DEPENDENCIES
|
||||||
rqrcode-rails3 (~> 0.1.7)
|
rqrcode-rails3 (~> 0.1.7)
|
||||||
rspec-rails (~> 3.5.0)
|
rspec-rails (~> 3.5.0)
|
||||||
rspec-retry (~> 0.4.5)
|
rspec-retry (~> 0.4.5)
|
||||||
rspec_profiling
|
rspec_profiling (~> 0.0.5)
|
||||||
rubocop (~> 0.46.0)
|
rubocop (~> 0.46.0)
|
||||||
rubocop-rspec (~> 1.9.1)
|
rubocop-rspec (~> 1.9.1)
|
||||||
ruby-fogbugz (~> 0.2.1)
|
ruby-fogbugz (~> 0.2.1)
|
||||||
|
@ -1011,4 +1014,4 @@ DEPENDENCIES
|
||||||
wikicloth (= 0.8.1)
|
wikicloth (= 0.8.1)
|
||||||
|
|
||||||
BUNDLED WITH
|
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
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
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
|
### Merge request coaching
|
||||||
|
|
||||||
Several people from the [GitLab team][team] are helping community members to get
|
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/.
|
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
|
## Feature Freeze
|
||||||
|
|
||||||
On the 7th of each month, the stable branches for the upcoming release will
|
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.
|
||||||
be frozen for major changes. Merge requests may still be merged into master
|
Merge requests may still be merged into master during this period,
|
||||||
during this period. By freezing the stable branches prior to a release there's
|
but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
|
||||||
no need to worry about last minute merge requests potentially breaking a lot of
|
By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
|
||||||
things.
|
|
||||||
|
|
||||||
What is considered to be a major change is determined on a case by case basis as
|
Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release)
|
||||||
this definition depends very much on the context of changes. For example, a 5
|
and security issues will be cherry-picked into the stable branch.
|
||||||
line change might have a big impact on the entire application. Ultimately the
|
Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch.
|
||||||
decision will be made by the maintainers and the release managers.
|
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
|
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
|
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.
|
not be merged into any stable branches.
|
||||||
|
|
||||||
## Copy & paste responses
|
## Copy & paste responses
|
||||||
|
|
||||||
### Improperly formatted issue
|
### 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
|
### 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
|
### Support requests and configuration questions
|
||||||
|
|
||||||
Thanks for your interest in GitLab. We don't use the issue tracker for support
|
Thanks for your interest in GitLab. We don't use the issue tracker for support
|
||||||
requests and configuration questions. Please check our
|
requests and configuration questions. Please check our
|
||||||
\[getting help\]\(https://about.gitlab.com/getting-help/) page to see all of the available
|
[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)
|
support options. Also, have a look at the [contribution guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
### Code format
|
### 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
|
### 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
|
### 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
|
### 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
|
### 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
|
### Accepting merge requests
|
||||||
|
|
||||||
Is there an issue on the
|
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?
|
similar to this? Could you please link it here?
|
||||||
Please be aware that new functionality that is not marked
|
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.
|
might not make it into GitLab.
|
||||||
|
|
||||||
### Only accepting merge requests with green tests
|
### 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
|
We are currently in the process of closing down the issue tracker on GitHub, to
|
||||||
prevent duplication with the GitLab.com issue tracker.
|
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
|
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/
|
[team]: https://about.gitlab.com/team/
|
||||||
[contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria
|
[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",
|
licensePath: "/api/:version/templates/licenses/:key",
|
||||||
gitignorePath: "/api/:version/templates/gitignores/:key",
|
gitignorePath: "/api/:version/templates/gitignores/:key",
|
||||||
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/: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",
|
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
|
||||||
group: function(group_id, callback) {
|
group: function(group_id, callback) {
|
||||||
var url = Api.buildUrl(Api.groupPath)
|
var url = Api.buildUrl(Api.groupPath)
|
||||||
|
|
|
@ -10,7 +10,6 @@ function requireAll(context) { return context.keys().map(context); }
|
||||||
|
|
||||||
window.$ = window.jQuery = require('jquery');
|
window.$ = window.jQuery = require('jquery');
|
||||||
require('jquery-ui/ui/autocomplete');
|
require('jquery-ui/ui/autocomplete');
|
||||||
require('jquery-ui/ui/datepicker');
|
|
||||||
require('jquery-ui/ui/draggable');
|
require('jquery-ui/ui/draggable');
|
||||||
require('jquery-ui/ui/effect-highlight');
|
require('jquery-ui/ui/effect-highlight');
|
||||||
require('jquery-ui/ui/sortable');
|
require('jquery-ui/ui/sortable');
|
||||||
|
@ -21,7 +20,7 @@ require('vendor/jquery.waitforimages');
|
||||||
require('vendor/jquery.caret');
|
require('vendor/jquery.caret');
|
||||||
require('vendor/jquery.atwho');
|
require('vendor/jquery.atwho');
|
||||||
require('vendor/jquery.scrollTo');
|
require('vendor/jquery.scrollTo');
|
||||||
window.Cookies = require('vendor/js.cookie');
|
window.Cookies = require('js-cookie');
|
||||||
require('./autosave');
|
require('./autosave');
|
||||||
require('bootstrap/js/affix');
|
require('bootstrap/js/affix');
|
||||||
require('bootstrap/js/alert');
|
require('bootstrap/js/alert');
|
||||||
|
@ -35,8 +34,10 @@ require('bootstrap/js/transition');
|
||||||
require('bootstrap/js/tooltip');
|
require('bootstrap/js/tooltip');
|
||||||
require('bootstrap/js/popover');
|
require('bootstrap/js/popover');
|
||||||
require('select2/select2.js');
|
require('select2/select2.js');
|
||||||
|
window.Pikaday = require('pikaday');
|
||||||
window._ = require('underscore');
|
window._ = require('underscore');
|
||||||
window.Dropzone = require('dropzone');
|
window.Dropzone = require('dropzone');
|
||||||
|
window.Sortable = require('vendor/Sortable');
|
||||||
require('mousetrap');
|
require('mousetrap');
|
||||||
require('mousetrap/plugins/pause/mousetrap-pause');
|
require('mousetrap/plugins/pause/mousetrap-pause');
|
||||||
require('./shortcuts');
|
require('./shortcuts');
|
||||||
|
@ -55,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
|
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
|
||||||
require('vendor/fuzzaldrin-plus');
|
require('vendor/fuzzaldrin-plus');
|
||||||
window.ES6Promise = require('vendor/es6-promise.auto');
|
require('es6-promise').polyfill();
|
||||||
window.ES6Promise.polyfill();
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
document.addEventListener('beforeunload', function () {
|
document.addEventListener('beforeunload', function () {
|
||||||
|
@ -247,5 +247,7 @@ window.ES6Promise.polyfill();
|
||||||
new Aside();
|
new Aside();
|
||||||
// bind sidebar events
|
// bind sidebar events
|
||||||
new gl.Sidebar();
|
new gl.Sidebar();
|
||||||
|
|
||||||
|
gl.utils.initTimeagoTimeout();
|
||||||
});
|
});
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|
|
@ -6,7 +6,6 @@ function requireAll(context) { return context.keys().map(context); }
|
||||||
|
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
window.Vue.use(require('vue-resource'));
|
window.Vue.use(require('vue-resource'));
|
||||||
window.Sortable = require('vendor/Sortable');
|
|
||||||
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
|
||||||
|
@ -16,7 +15,7 @@ require('./components/board');
|
||||||
require('./components/board_sidebar');
|
require('./components/board_sidebar');
|
||||||
require('./components/new_list_dropdown');
|
require('./components/new_list_dropdown');
|
||||||
require('./components/modal/index');
|
require('./components/modal/index');
|
||||||
require('./vue_resource_interceptor');
|
require('../vue_shared/vue_resource_interceptor');
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
const $boardApp = document.getElementById('board-app');
|
const $boardApp = document.getElementById('board-app');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* global Vue */
|
/* global Vue */
|
||||||
|
/* global dateFormat */
|
||||||
|
|
||||||
Vue.filter('due-date', (value) => {
|
Vue.filter('due-date', (value) => {
|
||||||
const date = new 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() {
|
Build.prototype.initSidebar = function() {
|
||||||
this.$sidebar = $('.js-build-sidebar');
|
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.$sidebar.niceScroll();
|
||||||
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
|
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() {
|
Build.prototype.location = function() {
|
||||||
|
@ -231,14 +223,6 @@
|
||||||
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
|
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) {
|
Build.prototype.toggleSidebar = function(shouldHide) {
|
||||||
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
|
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
|
||||||
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
|
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
|
||||||
|
@ -285,7 +269,7 @@
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$currentTarget = $(e.currentTarget);
|
$currentTarget = $(e.currentTarget);
|
||||||
$.scrollTo($currentTarget.attr('href'), {
|
$.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: {
|
SanitizationFilter: {
|
||||||
|
'a[name]:not([href]):empty'(el, text) {
|
||||||
|
return el.outerHTML;
|
||||||
|
},
|
||||||
'dl'(el, text) {
|
'dl'(el, text) {
|
||||||
let lines = text.trim().split('\n');
|
let lines = text.trim().split('\n');
|
||||||
// Add two spaces to the front of subsequent list items lines,
|
// Add two spaces to the front of subsequent list items lines,
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="events-description">
|
<div class="events-description">
|
||||||
{{ stage.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>
|
</div>
|
||||||
<ul class="stage-event-list">
|
<ul class="stage-event-list">
|
||||||
<li v-for="commit in items" class="stage-event-item">
|
<li v-for="commit in items" class="stage-event-item">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/* global Flash */
|
/* global Flash */
|
||||||
|
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
window.Cookies = require('vendor/js.cookie');
|
window.Cookies = require('js-cookie');
|
||||||
|
|
||||||
function requireAll(context) { return context.keys().map(context); }
|
function requireAll(context) { return context.keys().map(context); }
|
||||||
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));
|
||||||
|
|
|
@ -76,7 +76,7 @@ require('./lib/utils/url_utility');
|
||||||
const diffFile = diffTitle.closest('.diff-file');
|
const diffFile = diffTitle.closest('.diff-file');
|
||||||
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
|
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
|
||||||
if (nothingHereBlock.length) {
|
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, () => {
|
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
|
||||||
this.highlighSelectedLine();
|
this.highlighSelectedLine();
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
|
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
|
||||||
/* global Vue */
|
|
||||||
/* global CommentsStore */
|
/* global CommentsStore */
|
||||||
|
const Vue = require('vue');
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const CommentAndResolveBtn = Vue.extend({
|
const CommentAndResolveBtn = Vue.extend({
|
||||||
|
@ -9,13 +9,11 @@
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
textareaIsEmpty: true
|
textareaIsEmpty: true,
|
||||||
|
discussion: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
discussion: function () {
|
|
||||||
return CommentsStore.state[this.discussionId];
|
|
||||||
},
|
|
||||||
showButton: function () {
|
showButton: function () {
|
||||||
if (this.discussion) {
|
if (this.discussion) {
|
||||||
return this.discussion.isResolvable();
|
return this.discussion.isResolvable();
|
||||||
|
@ -42,6 +40,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.discussion = CommentsStore.state[this.discussionId];
|
||||||
|
},
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
|
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
|
||||||
this.textareaIsEmpty = $textarea.val() === '';
|
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 */
|
/* 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 DiscussionMixins */
|
||||||
/* global CommentsStore */
|
/* global CommentsStore */
|
||||||
|
const Vue = require('vue');
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const JumpToDiscussion = Vue.extend({
|
const JumpToDiscussion = Vue.extend({
|
||||||
|
@ -12,12 +12,10 @@
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
discussions: CommentsStore.state,
|
discussions: CommentsStore.state,
|
||||||
|
discussion: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
discussion: function () {
|
|
||||||
return this.discussions[this.discussionId];
|
|
||||||
},
|
|
||||||
allResolved: function () {
|
allResolved: function () {
|
||||||
return this.unresolvedDiscussionCount === 0;
|
return this.unresolvedDiscussionCount === 0;
|
||||||
},
|
},
|
||||||
|
@ -183,10 +181,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
$.scrollTo($target, {
|
$.scrollTo($target, {
|
||||||
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
|
offset: 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
created() {
|
||||||
|
this.discussion = this.discussions[this.discussionId];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Vue.component('jump-to-discussion', JumpToDiscussion);
|
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 */
|
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
|
||||||
/* global Vue */
|
|
||||||
/* global CommentsStore */
|
/* global CommentsStore */
|
||||||
/* global ResolveService */
|
/* global ResolveService */
|
||||||
/* global Flash */
|
/* global Flash */
|
||||||
|
const Vue = require('vue');
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const ResolveBtn = Vue.extend({
|
const ResolveBtn = Vue.extend({
|
||||||
|
@ -10,14 +10,14 @@
|
||||||
noteId: Number,
|
noteId: Number,
|
||||||
discussionId: String,
|
discussionId: String,
|
||||||
resolved: Boolean,
|
resolved: Boolean,
|
||||||
projectPath: String,
|
|
||||||
canResolve: Boolean,
|
canResolve: Boolean,
|
||||||
resolvedBy: String
|
resolvedBy: String
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
discussions: CommentsStore.state,
|
discussions: CommentsStore.state,
|
||||||
loading: false
|
loading: false,
|
||||||
|
note: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -30,13 +30,6 @@
|
||||||
discussion: function () {
|
discussion: function () {
|
||||||
return this.discussions[this.discussionId];
|
return this.discussions[this.discussionId];
|
||||||
},
|
},
|
||||||
note: function () {
|
|
||||||
if (this.discussion) {
|
|
||||||
return this.discussion.getNote(this.noteId);
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
buttonText: function () {
|
buttonText: function () {
|
||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
return `Resolved by ${this.resolvedByName}`;
|
return `Resolved by ${this.resolvedByName}`;
|
||||||
|
@ -73,10 +66,10 @@
|
||||||
|
|
||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
promise = ResolveService
|
promise = ResolveService
|
||||||
.unresolve(this.projectPath, this.noteId);
|
.unresolve(this.noteId);
|
||||||
} else {
|
} else {
|
||||||
promise = ResolveService
|
promise = ResolveService
|
||||||
.resolve(this.projectPath, this.noteId);
|
.resolve(this.noteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then((response) => {
|
promise.then((response) => {
|
||||||
|
@ -106,6 +99,8 @@
|
||||||
},
|
},
|
||||||
created: function () {
|
created: function () {
|
||||||
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
|
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 */
|
/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
|
||||||
/* global Vue */
|
|
||||||
/* global DiscussionMixins */
|
/* global DiscussionMixins */
|
||||||
/* global CommentsStore */
|
/* global CommentsStore */
|
||||||
|
const Vue = require('vue');
|
||||||
|
|
||||||
((w) => {
|
((w) => {
|
||||||
w.ResolveCount = Vue.extend({
|
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 */
|
/* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */
|
||||||
/* global Vue */
|
|
||||||
/* global CommentsStore */
|
/* global CommentsStore */
|
||||||
/* global ResolveService */
|
/* global ResolveService */
|
||||||
|
|
||||||
|
const Vue = require('vue');
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const ResolveDiscussionBtn = Vue.extend({
|
const ResolveDiscussionBtn = Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
discussionId: String,
|
discussionId: String,
|
||||||
mergeRequestId: Number,
|
mergeRequestId: Number,
|
||||||
projectPath: String,
|
|
||||||
canResolve: Boolean,
|
canResolve: Boolean,
|
||||||
},
|
},
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
discussions: CommentsStore.state
|
discussion: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
discussion: function () {
|
|
||||||
return this.discussions[this.discussionId];
|
|
||||||
},
|
|
||||||
showButton: function () {
|
showButton: function () {
|
||||||
if (this.discussion) {
|
if (this.discussion) {
|
||||||
return this.discussion.isResolvable();
|
return this.discussion.isResolvable();
|
||||||
|
@ -51,11 +48,13 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resolve: function () {
|
resolve: function () {
|
||||||
ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId);
|
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created: function () {
|
created: function () {
|
||||||
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
|
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
|
||||||
|
|
||||||
|
this.discussion = CommentsStore.state[this.discussionId];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
/* global ResolveCount */
|
/* global ResolveCount */
|
||||||
|
|
||||||
function requireAll(context) { return context.keys().map(context); }
|
function requireAll(context) { return context.keys().map(context); }
|
||||||
|
const Vue = require('vue');
|
||||||
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
|
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
|
||||||
requireAll(require.context('./services', 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)$/));
|
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';
|
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
window.gl = window.gl || {};
|
||||||
window.gl.diffNoteApps = {};
|
window.gl.diffNoteApps = {};
|
||||||
|
|
||||||
|
window.ResolveService = new gl.DiffNotesResolveServiceClass(projectPath);
|
||||||
|
|
||||||
gl.diffNotesCompileComponents = () => {
|
gl.diffNotesCompileComponents = () => {
|
||||||
const $components = $(COMPONENT_SELECTOR).filter(function () {
|
const $components = $(COMPONENT_SELECTOR).filter(function () {
|
||||||
return $(this).closest('resolve-count').length !== 1;
|
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 */
|
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
|
||||||
/* global Vue */
|
|
||||||
/* global Flash */
|
/* global Flash */
|
||||||
/* global CommentsStore */
|
/* 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 {
|
class ResolveServiceClass {
|
||||||
constructor() {
|
constructor(root) {
|
||||||
this.noteResource = Vue.resource('notes{/noteId}/resolve');
|
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
|
||||||
this.discussionResource = Vue.resource('merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve');
|
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCSRF() {
|
resolve(noteId) {
|
||||||
Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareRequest(root) {
|
|
||||||
this.setCSRF();
|
|
||||||
Vue.http.options.root = root;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(projectPath, noteId) {
|
|
||||||
this.prepareRequest(projectPath);
|
|
||||||
|
|
||||||
return this.noteResource.save({ noteId }, {});
|
return this.noteResource.save({ noteId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
unresolve(projectPath, noteId) {
|
unresolve(noteId) {
|
||||||
this.prepareRequest(projectPath);
|
|
||||||
|
|
||||||
return this.noteResource.delete({ noteId }, {});
|
return this.noteResource.delete({ noteId }, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) {
|
toggleResolveForDiscussion(mergeRequestId, discussionId) {
|
||||||
const discussion = CommentsStore.state[discussionId];
|
const discussion = CommentsStore.state[discussionId];
|
||||||
const isResolved = discussion.isResolved();
|
const isResolved = discussion.isResolved();
|
||||||
let promise;
|
let promise;
|
||||||
|
|
||||||
if (isResolved) {
|
if (isResolved) {
|
||||||
promise = this.unResolveAll(projectPath, mergeRequestId, discussionId);
|
promise = this.unResolveAll(mergeRequestId, discussionId);
|
||||||
} else {
|
} else {
|
||||||
promise = this.resolveAll(projectPath, mergeRequestId, discussionId);
|
promise = this.resolveAll(mergeRequestId, discussionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.then((response) => {
|
promise.then((response) => {
|
||||||
|
@ -62,11 +54,9 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveAll(projectPath, mergeRequestId, discussionId) {
|
resolveAll(mergeRequestId, discussionId) {
|
||||||
const discussion = CommentsStore.state[discussionId];
|
const discussion = CommentsStore.state[discussionId];
|
||||||
|
|
||||||
this.prepareRequest(projectPath);
|
|
||||||
|
|
||||||
discussion.loading = true;
|
discussion.loading = true;
|
||||||
|
|
||||||
return this.discussionResource.save({
|
return this.discussionResource.save({
|
||||||
|
@ -75,11 +65,9 @@
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
unResolveAll(projectPath, mergeRequestId, discussionId) {
|
unResolveAll(mergeRequestId, discussionId) {
|
||||||
const discussion = CommentsStore.state[discussionId];
|
const discussion = CommentsStore.state[discussionId];
|
||||||
|
|
||||||
this.prepareRequest(projectPath);
|
|
||||||
|
|
||||||
discussion.loading = true;
|
discussion.loading = true;
|
||||||
|
|
||||||
return this.discussionResource.delete({
|
return this.discussionResource.delete({
|
||||||
|
@ -89,5 +77,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w.ResolveService = new ResolveServiceClass();
|
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
|
||||||
})(window);
|
})();
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
/* global UsersSelect */
|
/* global UsersSelect */
|
||||||
/* global GroupAvatar */
|
/* global GroupAvatar */
|
||||||
/* global LineHighlighter */
|
/* global LineHighlighter */
|
||||||
/* global ShortcutsBlob */
|
|
||||||
/* global ProjectFork */
|
/* global ProjectFork */
|
||||||
/* global BuildArtifacts */
|
/* global BuildArtifacts */
|
||||||
/* global GroupsSelect */
|
/* global GroupsSelect */
|
||||||
|
@ -36,6 +35,8 @@
|
||||||
/* global Labels */
|
/* global Labels */
|
||||||
/* global Shortcuts */
|
/* global Shortcuts */
|
||||||
|
|
||||||
|
const ShortcutsBlob = require('./shortcuts_blob');
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var Dispatcher;
|
var Dispatcher;
|
||||||
|
|
||||||
|
@ -96,6 +97,7 @@
|
||||||
break;
|
break;
|
||||||
case 'projects:milestones:new':
|
case 'projects:milestones:new':
|
||||||
case 'projects:milestones:edit':
|
case 'projects:milestones:edit':
|
||||||
|
case 'projects:milestones:update':
|
||||||
new ZenMode();
|
new ZenMode();
|
||||||
new gl.DueDateSelectors();
|
new gl.DueDateSelectors();
|
||||||
new gl.GLForm($('.milestone-form'));
|
new gl.GLForm($('.milestone-form'));
|
||||||
|
@ -162,7 +164,7 @@
|
||||||
case 'projects:commit:pipelines':
|
case 'projects:commit:pipelines':
|
||||||
new gl.MiniPipelineGraph({
|
new gl.MiniPipelineGraph({
|
||||||
container: '.js-pipeline-table',
|
container: '.js-pipeline-table',
|
||||||
});
|
}).bindEvents();
|
||||||
break;
|
break;
|
||||||
case 'projects:commits:show':
|
case 'projects:commits:show':
|
||||||
case 'projects:activity':
|
case 'projects:activity':
|
||||||
|
@ -225,7 +227,12 @@
|
||||||
case 'projects:blame:show':
|
case 'projects:blame:show':
|
||||||
new LineHighlighter();
|
new LineHighlighter();
|
||||||
shortcut_handler = new ShortcutsNavigation();
|
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;
|
break;
|
||||||
case 'groups:labels:new':
|
case 'groups:labels:new':
|
||||||
case 'groups:labels:edit':
|
case 'groups:labels:edit':
|
||||||
|
@ -259,7 +266,7 @@
|
||||||
new gl.ProtectedBranchCreate();
|
new gl.ProtectedBranchCreate();
|
||||||
new gl.ProtectedBranchEditList();
|
new gl.ProtectedBranchEditList();
|
||||||
break;
|
break;
|
||||||
case 'projects:variables:index':
|
case 'projects:ci_cd:show':
|
||||||
new gl.ProjectVariables();
|
new gl.ProjectVariables();
|
||||||
break;
|
break;
|
||||||
case 'ci:lints:create':
|
case 'ci:lints:create':
|
||||||
|
|
|
@ -78,8 +78,8 @@ require('../window')(function(w){
|
||||||
},
|
},
|
||||||
|
|
||||||
destroy: function() {
|
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;
|
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 */
|
/* 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) {
|
(function(global) {
|
||||||
class DueDateSelect {
|
class DueDateSelect {
|
||||||
|
@ -25,11 +27,14 @@
|
||||||
this.initGlDropdown();
|
this.initGlDropdown();
|
||||||
this.initRemoveDueDate();
|
this.initRemoveDueDate();
|
||||||
this.initDatePicker();
|
this.initDatePicker();
|
||||||
this.initStopPropagation();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initGlDropdown() {
|
initGlDropdown() {
|
||||||
this.$dropdown.glDropdown({
|
this.$dropdown.glDropdown({
|
||||||
|
opened: () => {
|
||||||
|
const calendar = this.$datePicker.data('pikaday');
|
||||||
|
calendar.show();
|
||||||
|
},
|
||||||
hidden: () => {
|
hidden: () => {
|
||||||
this.$selectbox.hide();
|
this.$selectbox.hide();
|
||||||
this.$value.css('display', '');
|
this.$value.css('display', '');
|
||||||
|
@ -38,25 +43,37 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
initDatePicker() {
|
initDatePicker() {
|
||||||
this.$datePicker.datepicker({
|
const $dueDateInput = $(`input[name='${this.fieldName}']`);
|
||||||
dateFormat: 'yy-mm-dd',
|
|
||||||
defaultDate: $("input[name='" + this.fieldName + "']").val(),
|
const calendar = new Pikaday({
|
||||||
altField: "input[name='" + this.fieldName + "']",
|
field: $dueDateInput.get(0),
|
||||||
onSelect: () => {
|
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')) {
|
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();
|
this.updateIssueBoardIssue();
|
||||||
} else {
|
} else {
|
||||||
return this.saveDueDate(true);
|
this.saveDueDate(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.$datePicker.append(calendar.el);
|
||||||
|
this.$datePicker.data('pikaday', calendar);
|
||||||
}
|
}
|
||||||
|
|
||||||
initRemoveDueDate() {
|
initRemoveDueDate() {
|
||||||
this.$block.on('click', '.js-remove-due-date', (e) => {
|
this.$block.on('click', '.js-remove-due-date', (e) => {
|
||||||
|
const calendar = this.$datePicker.data('pikaday');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
calendar.setDate(null);
|
||||||
|
|
||||||
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
|
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
|
||||||
gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
|
gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
|
||||||
this.updateIssueBoardIssue();
|
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) {
|
saveDueDate(isDropdown) {
|
||||||
this.parseSelectedDate();
|
this.parseSelectedDate();
|
||||||
this.prepSelectedDate();
|
this.prepSelectedDate();
|
||||||
|
@ -86,7 +97,7 @@
|
||||||
// Construct Date object manually to avoid buggy dateString support within Date constructor
|
// Construct Date object manually to avoid buggy dateString support within Date constructor
|
||||||
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
|
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
|
||||||
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
|
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 {
|
} else {
|
||||||
this.displayedDate = 'No due date';
|
this.displayedDate = 'No due date';
|
||||||
}
|
}
|
||||||
|
@ -153,14 +164,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
initMilestoneDatePicker() {
|
initMilestoneDatePicker() {
|
||||||
$('.datepicker').datepicker({
|
$('.datepicker').each(function() {
|
||||||
dateFormat: 'yy-mm-dd'
|
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) => {
|
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const datepicker = $(e.target).siblings('.datepicker');
|
const calendar = $(e.target).siblings('.datepicker').data('pikaday');
|
||||||
$.datepicker._clearDate(datepicker);
|
calendar.setDate(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
/* global timeago */
|
/* global timeago */
|
||||||
|
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
window.timeago = require('vendor/timeago');
|
window.timeago = require('timeago.js');
|
||||||
require('../../lib/utils/text_utility');
|
require('../../lib/utils/text_utility');
|
||||||
require('../../vue_common_component/commit');
|
require('../../vue_shared/components/commit');
|
||||||
require('./environment_actions');
|
require('./environment_actions');
|
||||||
require('./environment_external_url');
|
require('./environment_external_url');
|
||||||
require('./environment_stop');
|
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}
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
isStoppable() {
|
hasStopAction() {
|
||||||
return this.model['stoppable?'];
|
return this.model['stop_action?'];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -508,7 +508,7 @@ require('./environment_terminal_button');
|
||||||
</external-url-component>
|
</external-url-component>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isStoppable && canCreateDeployment"
|
<div v-if="hasStopAction && canCreateDeployment"
|
||||||
class="inline js-stop-component-container">
|
class="inline js-stop-component-container">
|
||||||
<stop-component
|
<stop-component
|
||||||
:stop-url="model.stop_path">
|
:stop-url="model.stop_path">
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
|
|
||||||
require('./stores/environments_store');
|
require('./stores/environments_store');
|
||||||
require('./components/environment');
|
require('./components/environment');
|
||||||
require('./vue_resource_interceptor');
|
require('../vue_shared/vue_resource_interceptor');
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
window.gl = window.gl || {};
|
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);
|
super(droplab, dropdown, input, filter);
|
||||||
this.config = {
|
this.config = {
|
||||||
droplabAjaxFilter: {
|
droplabAjaxFilter: {
|
||||||
endpoint: '/autocomplete/users.json',
|
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
|
||||||
searchKey: 'search',
|
searchKey: 'search',
|
||||||
params: {
|
params: {
|
||||||
per_page: 20,
|
per_page: 20,
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
class FilteredSearchDropdown {
|
class FilteredSearchDropdown {
|
||||||
constructor(droplab, dropdown, input, filter) {
|
constructor(droplab, dropdown, input, filter) {
|
||||||
this.droplab = droplab;
|
this.droplab = droplab;
|
||||||
this.hookId = input.getAttribute('data-id');
|
this.hookId = input && input.getAttribute('data-id');
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.dropdown = dropdown;
|
this.dropdown = dropdown;
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
class FilteredSearchDropdownManager {
|
class FilteredSearchDropdownManager {
|
||||||
constructor() {
|
constructor(baseEndpoint = '') {
|
||||||
|
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
|
||||||
this.tokenizer = gl.FilteredSearchTokenizer;
|
this.tokenizer = gl.FilteredSearchTokenizer;
|
||||||
this.filteredSearchInput = document.querySelector('.filtered-search');
|
this.filteredSearchInput = document.querySelector('.filtered-search');
|
||||||
|
|
||||||
|
@ -38,13 +39,13 @@
|
||||||
milestone: {
|
milestone: {
|
||||||
reference: null,
|
reference: null,
|
||||||
gl: 'DropdownNonUser',
|
gl: 'DropdownNonUser',
|
||||||
extraArguments: ['milestones.json', '%'],
|
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
|
||||||
element: document.querySelector('#js-dropdown-milestone'),
|
element: document.querySelector('#js-dropdown-milestone'),
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
reference: null,
|
reference: null,
|
||||||
gl: 'DropdownNonUser',
|
gl: 'DropdownNonUser',
|
||||||
extraArguments: ['labels.json', '~'],
|
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
|
||||||
element: document.querySelector('#js-dropdown-label'),
|
element: document.querySelector('#js-dropdown-label'),
|
||||||
},
|
},
|
||||||
hint: {
|
hint: {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
if (this.filteredSearchInput) {
|
if (this.filteredSearchInput) {
|
||||||
this.tokenizer = gl.FilteredSearchTokenizer;
|
this.tokenizer = gl.FilteredSearchTokenizer;
|
||||||
this.dropdownManager = new gl.FilteredSearchDropdownManager();
|
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
|
||||||
|
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
this.loadSearchParamsFromURL();
|
this.loadSearchParamsFromURL();
|
||||||
|
|
|
@ -47,9 +47,11 @@
|
||||||
}
|
}
|
||||||
// Only filter asynchronously only if option remote is set
|
// Only filter asynchronously only if option remote is set
|
||||||
if (this.options.remote) {
|
if (this.options.remote) {
|
||||||
|
$inputContainer.parent().addClass('is-loading');
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
return timeout = setTimeout(function() {
|
return timeout = setTimeout(function() {
|
||||||
return this.options.query(this.input.val(), function(data) {
|
return this.options.query(this.input.val(), function(data) {
|
||||||
|
$inputContainer.parent().removeClass('is-loading');
|
||||||
return this.options.callback(data);
|
return this.options.callback(data);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}.bind(this), 250);
|
}.bind(this), 250);
|
||||||
|
@ -437,7 +439,7 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
GitLabDropdown.prototype.opened = function() {
|
GitLabDropdown.prototype.opened = function(e) {
|
||||||
var contentHtml;
|
var contentHtml;
|
||||||
this.resetRows();
|
this.resetRows();
|
||||||
this.addArrowKeyEvent();
|
this.addArrowKeyEvent();
|
||||||
|
@ -457,6 +459,10 @@
|
||||||
this.positionMenuAbove();
|
this.positionMenuAbove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.options.opened) {
|
||||||
|
this.options.opened.call(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
return this.dropdown.trigger('shown.gl.dropdown');
|
return this.dropdown.trigger('shown.gl.dropdown');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
/* global UsersSelect */
|
/* global UsersSelect */
|
||||||
/* global ZenMode */
|
/* global ZenMode */
|
||||||
/* global Autosave */
|
/* global Autosave */
|
||||||
|
/* global dateFormat */
|
||||||
|
/* global Pikaday */
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
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;
|
IssuableForm.prototype.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
|
||||||
|
|
||||||
function IssuableForm(form) {
|
function IssuableForm(form) {
|
||||||
var $issuableDueDate;
|
var $issuableDueDate, calendar;
|
||||||
this.form = form;
|
this.form = form;
|
||||||
this.toggleWip = bind(this.toggleWip, this);
|
this.toggleWip = bind(this.toggleWip, this);
|
||||||
this.renderWipExplanation = bind(this.renderWipExplanation, this);
|
this.renderWipExplanation = bind(this.renderWipExplanation, this);
|
||||||
|
@ -35,12 +37,14 @@
|
||||||
this.initMoveDropdown();
|
this.initMoveDropdown();
|
||||||
$issuableDueDate = $('#issuable-due-date');
|
$issuableDueDate = $('#issuable-due-date');
|
||||||
if ($issuableDueDate.length) {
|
if ($issuableDueDate.length) {
|
||||||
$('.datepicker').datepicker({
|
calendar = new Pikaday({
|
||||||
dateFormat: 'yy-mm-dd',
|
field: $issuableDueDate.get(0),
|
||||||
onSelect: function(dateText, inst) {
|
theme: 'gitlab-theme',
|
||||||
return $issuableDueDate.val(dateText);
|
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 */
|
/* 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 Flash */
|
||||||
|
/* global Sortable */
|
||||||
|
|
||||||
((global) => {
|
((global) => {
|
||||||
class LabelManager {
|
class LabelManager {
|
||||||
|
@ -9,11 +10,12 @@
|
||||||
this.otherLabels = otherLabels || $('.js-other-labels');
|
this.otherLabels = otherLabels || $('.js-other-labels');
|
||||||
this.errorMessage = 'Unable to update label prioritization at this time';
|
this.errorMessage = 'Unable to update label prioritization at this time';
|
||||||
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
|
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
|
||||||
this.prioritizedLabels.sortable({
|
this.sortable = Sortable.create(this.prioritizedLabels.get(0), {
|
||||||
items: 'li',
|
filter: '.empty-message',
|
||||||
placeholder: 'list-placeholder',
|
forceFallback: true,
|
||||||
axis: 'y',
|
fallbackClass: 'is-dragging',
|
||||||
update: this.onPrioritySortUpdate.bind(this)
|
dataIdAttr: 'data-id',
|
||||||
|
onUpdate: this.onPrioritySortUpdate.bind(this),
|
||||||
});
|
});
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
|
@ -51,13 +53,13 @@
|
||||||
$target = this.otherLabels;
|
$target = this.otherLabels;
|
||||||
$from = this.prioritizedLabels;
|
$from = this.prioritizedLabels;
|
||||||
}
|
}
|
||||||
if ($from.find('li').length === 1) {
|
$label.detach().appendTo($target);
|
||||||
|
if ($from.find('li').length) {
|
||||||
$from.find('.empty-message').removeClass('hidden');
|
$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');
|
$target.find('.empty-message').addClass('hidden');
|
||||||
}
|
}
|
||||||
$label.detach().appendTo($target);
|
|
||||||
// Return if we are not persisting state
|
// Return if we are not persisting state
|
||||||
if (!persistState) {
|
if (!persistState) {
|
||||||
return;
|
return;
|
||||||
|
@ -101,8 +103,12 @@
|
||||||
|
|
||||||
getSortedLabelsIds() {
|
getSortedLabelsIds() {
|
||||||
const sortedIds = [];
|
const sortedIds = [];
|
||||||
this.prioritizedLabels.find('li').each(function() {
|
this.prioritizedLabels.find('> li').each(function() {
|
||||||
sortedIds.push($(this).data('id'));
|
const id = $(this).data('id');
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
sortedIds.push(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return sortedIds;
|
return sortedIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,27 +69,21 @@
|
||||||
var hash = w.gl.utils.getLocationHash();
|
var hash = w.gl.utils.getLocationHash();
|
||||||
if (!hash) return;
|
if (!hash) return;
|
||||||
|
|
||||||
var navbar = document.querySelector('.navbar-gitlab');
|
// This is required to handle non-unicode characters in hash
|
||||||
var subnav = document.querySelector('.layout-nav');
|
hash = decodeURIComponent(hash);
|
||||||
var fixedTabs = document.querySelector('.js-tabs-affix');
|
|
||||||
|
|
||||||
var adjustment = 0;
|
|
||||||
if (navbar) adjustment -= navbar.offsetHeight;
|
|
||||||
if (subnav) adjustment -= subnav.offsetHeight;
|
|
||||||
|
|
||||||
// scroll to user-generated markdown anchor if we cannot find a match
|
// scroll to user-generated markdown anchor if we cannot find a match
|
||||||
if (document.getElementById(hash) === null) {
|
if (document.getElementById(hash) === null) {
|
||||||
var target = document.getElementById('user-content-' + hash);
|
var target = document.getElementById('user-content-' + hash);
|
||||||
if (target && target.scrollIntoView) {
|
if (target && target.scrollIntoView) {
|
||||||
target.scrollIntoView(true);
|
target.scrollIntoView(true);
|
||||||
window.scrollBy(0, adjustment);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// only adjust for fixedTabs when not targeting user-generated content
|
// only adjust for fixedTabs when not targeting user-generated content
|
||||||
|
var fixedTabs = document.querySelector('.js-tabs-affix');
|
||||||
if (fixedTabs) {
|
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;
|
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) {
|
gl.utils.scrollToElement = function($el) {
|
||||||
var top = $el.offset().top;
|
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();
|
gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
|
||||||
|
|
||||||
return $('body, html').animate({
|
return $('body, html').animate({
|
||||||
scrollTop: top - (gl.navBarHeight + gl.navLinksHeight + gl.mrTabsHeight)
|
scrollTop: top - (gl.mrTabsHeight)
|
||||||
}, 200);
|
}, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -230,5 +230,16 @@
|
||||||
|
|
||||||
return upperCaseHeaders;
|
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);
|
})(window);
|
||||||
}).call(this);
|
}).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
|
// 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
|
// children of an element with the `clearable-input` class, and have a sibling
|
||||||
|
@ -11,21 +13,34 @@
|
||||||
}
|
}
|
||||||
const inputs = $(selector);
|
const inputs = $(selector);
|
||||||
|
|
||||||
inputs.datepicker({
|
inputs.each((i, el) => {
|
||||||
dateFormat: 'yy-mm-dd',
|
const $input = $(el);
|
||||||
minDate: 1,
|
|
||||||
onSelect: function onSelect() {
|
const calendar = new Pikaday({
|
||||||
$(this).trigger('change');
|
field: $input.get(0),
|
||||||
toggleClearInput.call(this);
|
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) {
|
inputs.next('.js-clear-input').on('click', function clicked(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const input = $(this).closest('.clearable-input').find(selector);
|
const input = $(this).closest('.clearable-input').find(selector);
|
||||||
input.datepicker('setDate', null)
|
const calendar = input.data('pikaday');
|
||||||
.trigger('change');
|
|
||||||
|
calendar.setDate(null);
|
||||||
|
input.trigger('change');
|
||||||
toggleClearInput.call(input);
|
toggleClearInput.call(input);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -115,8 +115,8 @@ require('./merge_request_tabs');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
textarea.val(textarea.data('messageWithDescription'));
|
textarea.val(textarea.data('messageWithDescription'));
|
||||||
$('p.js-with-description-hint').hide();
|
$('.js-with-description-hint').hide();
|
||||||
$('p.js-without-description-hint').show();
|
$('.js-without-description-hint').show();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', 'a.js-without-description-link', function(e) {
|
$(document).on('click', 'a.js-without-description-link', function(e) {
|
||||||
|
@ -124,8 +124,8 @@ require('./merge_request_tabs');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
textarea.val(textarea.data('messageWithoutDescription'));
|
textarea.val(textarea.data('messageWithoutDescription'));
|
||||||
$('p.js-with-description-hint').show();
|
$('.js-with-description-hint').show();
|
||||||
$('p.js-without-description-hint').hide();
|
$('.js-without-description-hint').hide();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
/* global Flash */
|
/* global Flash */
|
||||||
|
|
||||||
require('./breakpoints');
|
require('./breakpoints');
|
||||||
window.Cookies = require('vendor/js.cookie');
|
window.Cookies = require('js-cookie');
|
||||||
require('./flash');
|
require('./flash');
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
|
@ -61,7 +61,6 @@ require('./flash');
|
||||||
|
|
||||||
constructor({ action, setUrl, stubLocation } = {}) {
|
constructor({ action, setUrl, stubLocation } = {}) {
|
||||||
this.diffsLoaded = false;
|
this.diffsLoaded = false;
|
||||||
this.pipelinesLoaded = false;
|
|
||||||
this.commitsLoaded = false;
|
this.commitsLoaded = false;
|
||||||
this.fixedLayoutPref = null;
|
this.fixedLayoutPref = null;
|
||||||
|
|
||||||
|
@ -83,12 +82,18 @@ require('./flash');
|
||||||
$(document)
|
$(document)
|
||||||
.on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
.on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
||||||
.on('click', '.js-show-tab', this.showTab);
|
.on('click', '.js-show-tab', this.showTab);
|
||||||
|
|
||||||
|
$('.merge-request-tabs a[data-toggle="tab"]')
|
||||||
|
.on('click', this.clickTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
unbindEvents() {
|
unbindEvents() {
|
||||||
$(document)
|
$(document)
|
||||||
.off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
.off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
|
||||||
.off('click', '.js-show-tab', this.showTab);
|
.off('click', '.js-show-tab', this.showTab);
|
||||||
|
|
||||||
|
$('.merge-request-tabs a[data-toggle="tab"]')
|
||||||
|
.off('click', this.clickTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
showTab(e) {
|
showTab(e) {
|
||||||
|
@ -96,6 +101,14 @@ require('./flash');
|
||||||
this.activateTab($(e.target).data('action'));
|
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) {
|
tabShown(e) {
|
||||||
const $target = $(e.target);
|
const $target = $(e.target);
|
||||||
const action = $target.data('action');
|
const action = $target.data('action');
|
||||||
|
@ -112,14 +125,9 @@ require('./flash');
|
||||||
if (this.diffViewType() === 'parallel') {
|
if (this.diffViewType() === 'parallel') {
|
||||||
this.expandViewContainer();
|
this.expandViewContainer();
|
||||||
}
|
}
|
||||||
const navBarHeight = $('.navbar-gitlab').outerHeight();
|
|
||||||
$.scrollTo('.merge-request-details .merge-request-tabs', {
|
$.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 {
|
} else {
|
||||||
this.expandView();
|
this.expandView();
|
||||||
this.resetViewContainer();
|
this.resetViewContainer();
|
||||||
|
@ -131,11 +139,7 @@ require('./flash');
|
||||||
|
|
||||||
scrollToElement(container) {
|
scrollToElement(container) {
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
const offset = 0 - (
|
const offset = -$('.js-tabs-affix').outerHeight();
|
||||||
$('.navbar-gitlab').outerHeight() +
|
|
||||||
$('.layout-nav').outerHeight() +
|
|
||||||
$('.js-tabs-affix').outerHeight()
|
|
||||||
);
|
|
||||||
const $el = $(`${container} ${location.hash}:not(.match)`);
|
const $el = $(`${container} ${location.hash}:not(.match)`);
|
||||||
if ($el.length) {
|
if ($el.length) {
|
||||||
$.scrollTo($el[0], { offset });
|
$.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
|
// Show or hide the loading spinner
|
||||||
//
|
//
|
||||||
// status - Boolean, true to show, false to hide
|
// status - Boolean, true to show, false to hide
|
||||||
|
@ -340,14 +325,12 @@ require('./flash');
|
||||||
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
|
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
|
||||||
|
|
||||||
const $diffTabs = $('#diff-notes-app');
|
const $diffTabs = $('#diff-notes-app');
|
||||||
const $fixedNav = $('.navbar-fixed-top');
|
|
||||||
const $layoutNav = $('.layout-nav');
|
|
||||||
|
|
||||||
$tabs.off('affix.bs.affix affix-top.bs.affix')
|
$tabs.off('affix.bs.affix affix-top.bs.affix')
|
||||||
.affix({
|
.affix({
|
||||||
offset: {
|
offset: {
|
||||||
top: () => (
|
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.getCIStatus(false);
|
||||||
this.retrieveSuccessIcon();
|
this.retrieveSuccessIcon();
|
||||||
|
|
||||||
|
this.initMiniPipelineGraph();
|
||||||
|
|
||||||
this.ciStatusInterval = new global.SmartInterval({
|
this.ciStatusInterval = new global.SmartInterval({
|
||||||
callback: this.getCIStatus.bind(this, true),
|
callback: this.getCIStatus.bind(this, true),
|
||||||
startingInterval: 10000,
|
startingInterval: 10000,
|
||||||
|
@ -66,6 +68,7 @@ require('./smart_interval');
|
||||||
incrementByFactorOf: 15000,
|
incrementByFactorOf: 15000,
|
||||||
immediateExecution: true,
|
immediateExecution: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
notifyPermissions();
|
notifyPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +154,7 @@ require('./smart_interval');
|
||||||
return $.getJSON(this.opts.ci_status_url, (function(_this) {
|
return $.getJSON(this.opts.ci_status_url, (function(_this) {
|
||||||
return function(data) {
|
return function(data) {
|
||||||
var message, status, title;
|
var message, status, title;
|
||||||
if (data.status === '') {
|
if (!data.status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
||||||
|
@ -236,17 +239,20 @@ require('./smart_interval');
|
||||||
case "failed":
|
case "failed":
|
||||||
case "canceled":
|
case "canceled":
|
||||||
case "not_found":
|
case "not_found":
|
||||||
return this.setMergeButtonClass('btn-danger');
|
this.setMergeButtonClass('btn-danger');
|
||||||
|
break;
|
||||||
case "running":
|
case "running":
|
||||||
return this.setMergeButtonClass('btn-info');
|
this.setMergeButtonClass('btn-info');
|
||||||
|
break;
|
||||||
case "success":
|
case "success":
|
||||||
case "success_with_warnings":
|
case "success_with_warnings":
|
||||||
return this.setMergeButtonClass('btn-create');
|
this.setMergeButtonClass('btn-create');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$('.ci_widget.ci-error').show();
|
$('.ci_widget.ci-error').show();
|
||||||
return this.setMergeButtonClass('btn-danger');
|
this.setMergeButtonClass('btn-danger');
|
||||||
}
|
}
|
||||||
|
this.initMiniPipelineGraph();
|
||||||
};
|
};
|
||||||
|
|
||||||
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
|
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
|
||||||
|
@ -269,6 +275,12 @@ require('./smart_interval');
|
||||||
$('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/'));
|
$('.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;
|
return MergeRequestWidget;
|
||||||
})();
|
})();
|
||||||
})(window.gl || (window.gl = {}));
|
})(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 */
|
/* 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 Flash */
|
||||||
|
/* global Sortable */
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
this.Milestone = (function() {
|
this.Milestone = (function() {
|
||||||
|
@ -8,11 +9,9 @@
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: issue_url,
|
url: issue_url,
|
||||||
data: data,
|
data: data,
|
||||||
success: (function(_this) {
|
success: function(_data) {
|
||||||
return function(_data) {
|
return Milestone.successCallback(_data, li);
|
||||||
return _this.successCallback(_data, li);
|
},
|
||||||
};
|
|
||||||
})(this),
|
|
||||||
error: function(data) {
|
error: function(data) {
|
||||||
return new Flash("Issue update failed", 'alert');
|
return new Flash("Issue update failed", 'alert');
|
||||||
},
|
},
|
||||||
|
@ -27,11 +26,9 @@
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: sort_issues_url,
|
url: sort_issues_url,
|
||||||
data: data,
|
data: data,
|
||||||
success: (function(_this) {
|
success: function(_data) {
|
||||||
return function(_data) {
|
return Milestone.successCallback(_data);
|
||||||
return _this.successCallback(_data);
|
},
|
||||||
};
|
|
||||||
})(this),
|
|
||||||
error: function() {
|
error: function() {
|
||||||
return new Flash("Issues update failed", 'alert');
|
return new Flash("Issues update failed", 'alert');
|
||||||
},
|
},
|
||||||
|
@ -46,11 +43,9 @@
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: sort_mr_url,
|
url: sort_mr_url,
|
||||||
data: data,
|
data: data,
|
||||||
success: (function(_this) {
|
success: function(_data) {
|
||||||
return function(_data) {
|
return Milestone.successCallback(_data);
|
||||||
return _this.successCallback(_data);
|
},
|
||||||
};
|
|
||||||
})(this),
|
|
||||||
error: function(data) {
|
error: function(data) {
|
||||||
return new Flash("Issue update failed", 'alert');
|
return new Flash("Issue update failed", 'alert');
|
||||||
},
|
},
|
||||||
|
@ -63,11 +58,9 @@
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
url: merge_request_url,
|
url: merge_request_url,
|
||||||
data: data,
|
data: data,
|
||||||
success: (function(_this) {
|
success: function(_data) {
|
||||||
return function(_data) {
|
return Milestone.successCallback(_data, li);
|
||||||
return _this.successCallback(_data, li);
|
},
|
||||||
};
|
|
||||||
})(this),
|
|
||||||
error: function(data) {
|
error: function(data) {
|
||||||
return new Flash("Issue update failed", 'alert');
|
return new Flash("Issue update failed", 'alert');
|
||||||
},
|
},
|
||||||
|
@ -81,65 +74,30 @@
|
||||||
img_tag = $('<img/>');
|
img_tag = $('<img/>');
|
||||||
img_tag.attr('src', data.assignee.avatar_url);
|
img_tag.attr('src', data.assignee.avatar_url);
|
||||||
img_tag.addClass('avatar s16');
|
img_tag.addClass('avatar s16');
|
||||||
$(element).find('.assignee-icon').html(img_tag);
|
$(element).find('.assignee-icon img').replaceWith(img_tag);
|
||||||
} else {
|
} else {
|
||||||
$(element).find('.assignee-icon').html('');
|
$(element).find('.assignee-icon').empty();
|
||||||
}
|
}
|
||||||
return $(element).effect('highlight');
|
return $(element).effect('highlight');
|
||||||
};
|
};
|
||||||
|
|
||||||
function Milestone() {
|
function Milestone() {
|
||||||
var oldMouseStart;
|
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.bindIssuesSorting();
|
||||||
this.bindMergeRequestSorting();
|
this.bindMergeRequestSorting();
|
||||||
this.bindTabsSwitching();
|
this.bindTabsSwitching();
|
||||||
}
|
}
|
||||||
|
|
||||||
Milestone.prototype.bindIssuesSorting = function() {
|
Milestone.prototype.bindIssuesSorting = function() {
|
||||||
return $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable({
|
$('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
|
||||||
connectWith: ".issues-sortable-list",
|
this.createSortable(el, {
|
||||||
dropOnEmpty: true,
|
group: 'issue-list',
|
||||||
items: "li:not(.ui-sort-disabled)",
|
listEls: $('.issues-sortable-list'),
|
||||||
beforeStart: function(event, ui) {
|
fieldName: 'issue',
|
||||||
return $(".issues-sortable-list").css("min-height", ui.item.outerHeight());
|
sortCallback: Milestone.sortIssues,
|
||||||
},
|
updateCallback: Milestone.updateIssue,
|
||||||
stop: function(event, ui) {
|
});
|
||||||
return $(".issues-sortable-list").css("min-height", "0px");
|
}.bind(this));
|
||||||
},
|
|
||||||
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();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Milestone.prototype.bindTabsSwitching = function() {
|
Milestone.prototype.bindTabsSwitching = function() {
|
||||||
|
@ -154,42 +112,62 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Milestone.prototype.bindMergeRequestSorting = function() {
|
Milestone.prototype.bindMergeRequestSorting = function() {
|
||||||
return $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable({
|
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
|
||||||
connectWith: ".merge_requests-sortable-list",
|
this.createSortable(el, {
|
||||||
dropOnEmpty: true,
|
group: 'merge-request-list',
|
||||||
items: "li:not(.ui-sort-disabled)",
|
listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
|
||||||
beforeStart: function(event, ui) {
|
fieldName: 'merge_request',
|
||||||
return $(".merge_requests-sortable-list").css("min-height", ui.item.outerHeight());
|
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) {
|
onEnd: function () {
|
||||||
return $(".merge_requests-sortable-list").css("min-height", "0px");
|
opts.listEls.css("min-height", "0px");
|
||||||
},
|
},
|
||||||
update: function(event, ui) {
|
onUpdate: function(e) {
|
||||||
var data;
|
var ids = this.toArray(),
|
||||||
data = $(this).sortable("serialize");
|
data;
|
||||||
return Milestone.sortMergeRequests(data);
|
|
||||||
|
if (ids.length) {
|
||||||
|
data = ids.map(function(id) {
|
||||||
|
return 'sortable_' + opts.fieldName + '[]=' + id;
|
||||||
|
}).join('&');
|
||||||
|
|
||||||
|
opts.sortCallback(data);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
receive: function(event, ui) {
|
onAdd: function (e) {
|
||||||
var data, merge_request_id, merge_request_url, new_state;
|
var data, issuableId, issuableUrl, newState;
|
||||||
new_state = $(this).data('state');
|
newState = e.to.dataset.state;
|
||||||
merge_request_id = ui.item.data('iid');
|
issuableUrl = e.item.dataset.url;
|
||||||
merge_request_url = ui.item.data('url');
|
|
||||||
data = (function() {
|
data = (function() {
|
||||||
switch (new_state) {
|
switch (newState) {
|
||||||
case 'ongoing':
|
case 'ongoing':
|
||||||
return "merge_request[assignee_id]=" + gon.current_user_id;
|
return opts.fieldName + '[assignee_id]=' + gon.current_user_id;
|
||||||
case 'unassigned':
|
case 'unassigned':
|
||||||
return "merge_request[assignee_id]=";
|
return opts.fieldName + '[assignee_id]=';
|
||||||
case 'closed':
|
case 'closed':
|
||||||
return "merge_request[state_event]=close";
|
return opts.fieldName + '[state_event]=close';
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
if ($(ui.sender).data('state') === "closed") {
|
if (e.from.dataset.state === 'closed') {
|
||||||
data += "&merge_request[state_event]=reopen";
|
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;
|
return Milestone;
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
this.container = opts.container || '';
|
this.container = opts.container || '';
|
||||||
this.dropdownListSelector = '.js-builds-dropdown-container';
|
this.dropdownListSelector = '.js-builds-dropdown-container';
|
||||||
this.getBuildsList = this.getBuildsList.bind(this);
|
this.getBuildsList = this.getBuildsList.bind(this);
|
||||||
|
|
||||||
this.bindEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +28,7 @@
|
||||||
* All dropdown events are fired at the .dropdown-menu's parent element.
|
* All dropdown events are fired at the .dropdown-menu's parent element.
|
||||||
*/
|
*/
|
||||||
bindEvents() {
|
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');
|
var mergeRequestId = $form.data('noteable-iid');
|
||||||
|
|
||||||
if (ResolveService != null) {
|
if (ResolveService != null) {
|
||||||
ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId);
|
ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
|
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
|
||||||
$('#user_notification_email').on('change', 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:before', this.beforeUpdateUsername);
|
||||||
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
|
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
|
||||||
$('.update-notifications').on('ajax:success', this.onUpdateNotifs);
|
$('.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 */
|
/* eslint-disable arrow-parens, class-methods-use-this, no-param-reassign */
|
||||||
/* global Cookies */
|
/* global Cookies */
|
||||||
|
|
||||||
((global) => {
|
(() => {
|
||||||
let singleton;
|
|
||||||
|
|
||||||
const pinnedStateCookie = 'pin_nav';
|
const pinnedStateCookie = 'pin_nav';
|
||||||
const sidebarBreakpoint = 1024;
|
const sidebarBreakpoint = 1024;
|
||||||
|
|
||||||
const pageSelector = '.page-with-sidebar';
|
const pageSelector = '.page-with-sidebar';
|
||||||
const navbarSelector = '.navbar-fixed-top';
|
const navbarSelector = '.navbar-gitlab';
|
||||||
const sidebarWrapperSelector = '.sidebar-wrapper';
|
const sidebarWrapperSelector = '.sidebar-wrapper';
|
||||||
const sidebarContentSelector = '.nav-sidebar';
|
const sidebarContentSelector = '.nav-sidebar';
|
||||||
|
|
||||||
|
@ -23,11 +21,12 @@
|
||||||
|
|
||||||
class Sidebar {
|
class Sidebar {
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!singleton) {
|
if (!Sidebar.singleton) {
|
||||||
singleton = this;
|
Sidebar.singleton = this;
|
||||||
singleton.init();
|
Sidebar.singleton.init();
|
||||||
}
|
}
|
||||||
return singleton;
|
|
||||||
|
return Sidebar.singleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -36,13 +35,16 @@
|
||||||
window.innerWidth >= sidebarBreakpoint &&
|
window.innerWidth >= sidebarBreakpoint &&
|
||||||
$(pageSelector).hasClass(expandedPageClass)
|
$(pageSelector).hasClass(expandedPageClass)
|
||||||
);
|
);
|
||||||
|
$(window).on('resize', () => this.setSidebarHeight());
|
||||||
$(document)
|
$(document)
|
||||||
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
|
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
|
||||||
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
|
.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('DOMContentLoaded', () => this.renderState())
|
||||||
|
.on('scroll', () => this.setSidebarHeight())
|
||||||
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
|
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
|
||||||
this.renderState();
|
this.renderState();
|
||||||
|
this.setSidebarHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickEvent(e) {
|
handleClickEvent(e) {
|
||||||
|
@ -65,6 +67,16 @@
|
||||||
this.renderState();
|
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() {
|
togglePinnedState() {
|
||||||
this.isPinned = !this.isPinned;
|
this.isPinned = !this.isPinned;
|
||||||
if (!this.isPinned) {
|
if (!this.isPinned) {
|
||||||
|
@ -88,10 +100,12 @@
|
||||||
$pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
|
$pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
|
||||||
|
|
||||||
if (this.isExpanded) {
|
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');
|
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));
|
this.toggleDiff($(e.target));
|
||||||
}).bind(this));
|
}).bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleFileDiff.prototype.toggleDiff = function($target, cb) {
|
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;
|
this.isOpen = !this.isOpen;
|
||||||
if (!this.isOpen && !this.hasError) {
|
if (!this.isOpen && !this.hasError) {
|
||||||
this.content.hide();
|
this.content.hide();
|
||||||
|
|
|
@ -50,14 +50,15 @@
|
||||||
return (
|
return (
|
||||||
children[target.index] ||
|
children[target.index] ||
|
||||||
children[target.index === 'first' ? 0 : -1] ||
|
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) {
|
function getRect(el) {
|
||||||
var rect = el.getBoundingClientRect();
|
var rect = el.getBoundingClientRect();
|
||||||
var width = rect.right - rect.left;
|
var width = rect.right - rect.left;
|
||||||
var height = rect.bottom - rect.top;
|
var height = rect.bottom - rect.top + 10;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: rect.left,
|
x: rect.left,
|
|
@ -146,14 +146,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
goToTodoUrl(e) {
|
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) {
|
if (!todoLink) {
|
||||||
return;
|
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();
|
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 {
|
} else {
|
||||||
return gl.utils.visitUrl(todoLink);
|
return gl.utils.visitUrl(todoLink);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,36 @@
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
/* global Vue, VueResource, gl */
|
/* global Vue, VueResource, gl */
|
||||||
window.Vue = require('vue');
|
window.Vue = require('vue');
|
||||||
window.Vue.use(require('vue-resource'));
|
window.Vue.use(require('vue-resource'));
|
||||||
require('../vue_common_component/commit');
|
require('../lib/utils/common_utils');
|
||||||
require('../vue_pagination/index');
|
require('../vue_shared/vue_resource_interceptor');
|
||||||
require('../boards/vue_resource_interceptor');
|
|
||||||
require('./status');
|
|
||||||
require('./store');
|
|
||||||
require('./pipeline_url');
|
|
||||||
require('./stage');
|
|
||||||
require('./stages');
|
|
||||||
require('./pipeline_actions');
|
|
||||||
require('./time_ago');
|
|
||||||
require('./pipelines');
|
require('./pipelines');
|
||||||
|
|
||||||
(() => {
|
$(() => new Vue({
|
||||||
const project = document.querySelector('.pipelines');
|
el: document.querySelector('.vue-pipelines-index'),
|
||||||
const entry = document.querySelector('.vue-pipelines-index');
|
|
||||||
const svgs = document.querySelector('.pipeline-svgs');
|
|
||||||
|
|
||||||
if (!entry) return null;
|
data() {
|
||||||
return new Vue({
|
const project = document.querySelector('.pipelines');
|
||||||
el: entry,
|
const svgs = document.querySelector('.pipeline-svgs').dataset;
|
||||||
data: {
|
|
||||||
|
// Transform svgs DOMStringMap to a plain Object.
|
||||||
|
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
|
||||||
|
|
||||||
|
return {
|
||||||
scope: project.dataset.url,
|
scope: project.dataset.url,
|
||||||
store: new gl.PipelineStore(),
|
store: new gl.PipelineStore(),
|
||||||
svgs: svgs.dataset,
|
svgs: svgsObject,
|
||||||
},
|
};
|
||||||
components: {
|
},
|
||||||
'vue-pipelines': gl.VuePipelines,
|
components: {
|
||||||
},
|
'vue-pipelines': gl.VuePipelines,
|
||||||
template: `
|
},
|
||||||
<vue-pipelines
|
template: `
|
||||||
:scope='scope'
|
<vue-pipelines
|
||||||
:store='store'
|
:scope='scope'
|
||||||
:svgs='svgs'
|
:store='store'
|
||||||
>
|
:svgs='svgs'
|
||||||
</vue-pipelines>
|
>
|
||||||
`,
|
</vue-pipelines>
|
||||||
});
|
`,
|
||||||
})();
|
}));
|
||||||
|
|
|
@ -50,9 +50,9 @@
|
||||||
<button
|
<button
|
||||||
v-if='artifacts'
|
v-if='artifacts'
|
||||||
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
|
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
|
||||||
data-toggle="dropdown"
|
|
||||||
title="Artifacts"
|
title="Artifacts"
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
|
data-toggle="dropdown"
|
||||||
aria-label="Artifacts"
|
aria-label="Artifacts"
|
||||||
>
|
>
|
||||||
<i class="fa fa-download" aria-hidden="true"></i>
|
<i class="fa fa-download" aria-hidden="true"></i>
|
||||||
|
@ -81,8 +81,7 @@
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
:href='pipeline.retry_path'
|
:href='pipeline.retry_path'
|
||||||
aria-label="Retry"
|
aria-label="Retry">
|
||||||
>
|
|
||||||
<i class="fa fa-repeat" aria-hidden="true"></i>
|
<i class="fa fa-repeat" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
|
@ -94,8 +93,7 @@
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
:href='pipeline.cancel_path'
|
:href='pipeline.cancel_path'
|
||||||
aria-label="Cancel"
|
aria-label="Cancel">
|
||||||
>
|
|
||||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
/* global Vue, gl */
|
/* global Vue, gl */
|
||||||
/* eslint-disable no-param-reassign */
|
/* 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) => {
|
||||||
gl.VuePipelines = Vue.extend({
|
gl.VuePipelines = Vue.extend({
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
runningPipeline: gl.VueRunningPipeline,
|
'gl-pagination': gl.VueGlPagination,
|
||||||
pipelineActions: gl.VuePipelineActions,
|
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
|
||||||
stages: gl.VueStages,
|
|
||||||
commit: gl.CommitComponent,
|
|
||||||
pipelineUrl: gl.VuePipelineUrl,
|
|
||||||
pipelineHead: gl.VuePipelineHead,
|
|
||||||
glPagination: gl.VueGlPagination,
|
|
||||||
statusScope: gl.VueStatusScope,
|
|
||||||
timeAgo: gl.VueTimeAgo,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pipelines: [],
|
pipelines: [],
|
||||||
|
@ -38,87 +38,29 @@
|
||||||
change(pagenum, apiScope) {
|
change(pagenum, apiScope) {
|
||||||
gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
|
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: `
|
template: `
|
||||||
<div>
|
<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'>
|
<div class="pipelines realtime-loading" v-if='pageRequest'>
|
||||||
<i class="fa fa-spinner fa-spin"></i>
|
<i class="fa fa-spinner fa-spin"></i>
|
||||||
</div>
|
</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
|
<gl-pagination
|
||||||
v-if='pageInfo.total > pageInfo.perPage'
|
v-if='!pageRequest && pipelines.length && pageInfo.total > pageInfo.perPage'
|
||||||
:pagenum='pagenum'
|
:pagenum='pagenum'
|
||||||
:change='change'
|
:change='change'
|
||||||
:count='count.all'
|
:count='count.all'
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
svgs: {
|
svgs: {
|
||||||
type: DOMStringMap,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
match: {
|
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 {
|
gl.PipelineStore = class {
|
||||||
fetchDataLoop(Vue, pageNum, url, apiScope) {
|
fetchDataLoop(Vue, pageNum, url, apiScope) {
|
||||||
|
this.pageRequest = true;
|
||||||
const updatePipelineNums = (count) => {
|
const updatePipelineNums = (count) => {
|
||||||
const { all } = count;
|
const { all } = count;
|
||||||
const running = count.running_or_pending;
|
const running = count.running_or_pending;
|
||||||
|
@ -41,16 +42,18 @@ require('../vue_realtime_listener');
|
||||||
this.pageRequest = false;
|
this.pageRequest = false;
|
||||||
}, () => {
|
}, () => {
|
||||||
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();
|
goFetch();
|
||||||
|
|
||||||
const startTimeLoops = () => {
|
const startTimeLoops = () => {
|
||||||
this.timeLoopInterval = setInterval(() => {
|
this.timeLoopInterval = setInterval(() => {
|
||||||
this.$children
|
this.$children[0].$children.reduce((acc, component) => {
|
||||||
.filter(e => e.$options._componentTag === 'time-ago')
|
const timeAgoComponent = component.$children.filter(el => el.$options._componentTag === 'time-ago')[0];
|
||||||
.forEach(e => e.changeTime());
|
acc.push(timeAgoComponent);
|
||||||
|
return acc;
|
||||||
|
}, []).forEach(e => e.changeTime());
|
||||||
}, 10000);
|
}, 10000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
/* global Vue, gl */
|
/* global Vue, gl */
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
|
|
||||||
|
window.Vue = require('vue');
|
||||||
|
require('../lib/utils/datetime_utility');
|
||||||
|
|
||||||
((gl) => {
|
((gl) => {
|
||||||
gl.VueTimeAgo = Vue.extend({
|
gl.VueTimeAgo = Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -14,5 +14,16 @@
|
||||||
window.addEventListener('focus', startIntervals);
|
window.addEventListener('focus', startIntervals);
|
||||||
window.addEventListener('blur', removeIntervals);
|
window.addEventListener('blur', removeIntervals);
|
||||||
document.addEventListener('beforeunload', removeAll);
|
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 = {}));
|
})(window.gl || (window.gl = {}));
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
/* global Vue */
|
/* global Vue */
|
||||||
|
|
||||||
window.Vue = require('vue');
|
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
window.gl = window.gl || {};
|
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
|
* 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
|
* 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.
|
* 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-ui/autocomplete
|
||||||
*= require jquery.atwho
|
*= require jquery.atwho
|
||||||
*= require select2
|
*= require select2
|
||||||
|
@ -19,6 +18,8 @@
|
||||||
* directory.
|
* directory.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@import "../../../node_modules/pikaday/scss/pikaday";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GitLab UI framework
|
* GitLab UI framework
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -128,8 +128,7 @@
|
||||||
|
|
||||||
.note-action-button .link-highlight,
|
.note-action-button .link-highlight,
|
||||||
.toolbar-btn,
|
.toolbar-btn,
|
||||||
.dropdown-toggle-caret,
|
.dropdown-toggle-caret {
|
||||||
.fa:not(.fa-bell) {
|
|
||||||
@include transition(color);
|
@include transition(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
@extend .avatar-circle;
|
@extend .avatar-circle;
|
||||||
|
@include transition-property(none);
|
||||||
|
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-calendar-activities {
|
.user-calendar-activities {
|
||||||
|
direction: ltr;
|
||||||
|
|
||||||
.str-truncated {
|
.str-truncated {
|
||||||
max-width: 70%;
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
|
@ -43,3 +45,56 @@
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 12px;
|
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 {
|
.progress {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $border-gray-light;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,6 @@
|
||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
max-width: 280px;
|
|
||||||
min-width: 240px;
|
min-width: 240px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -137,6 +136,10 @@
|
||||||
border-radius: $border-radius-base;
|
border-radius: $border-radius-base;
|
||||||
box-shadow: 0 2px 4px $dropdown-shadow-color;
|
box-shadow: 0 2px 4px $dropdown-shadow-color;
|
||||||
|
|
||||||
|
.filtered-search-input-container & {
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
&.is-loading {
|
&.is-loading {
|
||||||
.dropdown-content {
|
.dropdown-content {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -502,119 +505,16 @@
|
||||||
max-height: 230px;
|
max-height: 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-widget {
|
.pika-single {
|
||||||
table {
|
position: relative!important;
|
||||||
margin: 0;
|
top: 0!important;
|
||||||
}
|
border: 0;
|
||||||
|
box-shadow: none;
|
||||||
&.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th {
|
.pika-lendar {
|
||||||
padding: 2px 0;
|
margin-top: -5px;
|
||||||
color: $note-disabled-comment-color;
|
margin-bottom: 0;
|
||||||
font-weight: normal;
|
|
||||||
text-transform: lowercase;
|
|
||||||
border-top: 1px solid $calendar-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-datepicker-unselectable {
|
|
||||||
background-color: $gray-light;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
float: right;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
|
|
||||||
|
@media (min-width: $screen-md-min) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: $screen-xs-max) {
|
@media (max-width: $screen-xs-max) {
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
@ -272,7 +276,7 @@ header {
|
||||||
|
|
||||||
.header-user {
|
.header-user {
|
||||||
.dropdown-menu-nav {
|
.dropdown-menu-nav {
|
||||||
width: 140px;
|
min-width: 140px;
|
||||||
margin-top: -5px;
|
margin-top: -5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,42 +2,6 @@
|
||||||
font-family: $regular_font;
|
font-family: $regular_font;
|
||||||
font-size: $font-size-base;
|
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 {
|
&.ui-autocomplete {
|
||||||
border-color: $jq-ui-border;
|
border-color: $jq-ui-border;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -59,25 +23,4 @@
|
||||||
border: 0;
|
border: 0;
|
||||||
background: transparent;
|
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 {
|
.layout-nav {
|
||||||
position: fixed;
|
|
||||||
top: $header-height;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 11;
|
|
||||||
background: $gray-light;
|
background: $gray-light;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
transition: padding $sidebar-transition-duration;
|
transition: padding $sidebar-transition-duration;
|
||||||
|
@ -419,15 +416,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-with-layout-nav {
|
.page-with-layout-nav {
|
||||||
margin-top: $header-height + 2;
|
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
top: ($header-height * 2) + 2;
|
top: ($header-height * 2) + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.build-sidebar {
|
||||||
|
top: ($header-height * 3) + 3;
|
||||||
|
|
||||||
|
&.affix {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.activities {
|
.activities {
|
||||||
|
|
||||||
.nav-block {
|
.nav-block {
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,22 @@
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator,
|
||||||
|
.separator:hover {
|
||||||
|
a {
|
||||||
|
cursor: default;
|
||||||
|
background-color: $gray-light;
|
||||||
|
padding: $gl-vert-padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.gap,
|
.gap,
|
||||||
.gap:hover {
|
.gap:hover {
|
||||||
background-color: $gray-light;
|
background-color: $gray-light;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.page-with-sidebar {
|
.page-with-sidebar {
|
||||||
padding: $header-height 0 25px;
|
padding-bottom: 25px;
|
||||||
transition: padding $sidebar-transition-duration;
|
transition: padding $sidebar-transition-duration;
|
||||||
|
|
||||||
&.page-sidebar-pinned {
|
&.page-sidebar-pinned {
|
||||||
|
@ -208,7 +208,9 @@ header.header-sidebar-pinned {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
@media (min-width: $screen-sm-min) {
|
||||||
padding-right: $sidebar_collapsed_width;
|
.content-wrapper {
|
||||||
|
padding-right: $sidebar_collapsed_width;
|
||||||
|
}
|
||||||
|
|
||||||
.merge-request-tabs-holder.affix {
|
.merge-request-tabs-holder.affix {
|
||||||
right: $sidebar_collapsed_width;
|
right: $sidebar_collapsed_width;
|
||||||
|
@ -234,7 +236,9 @@ header.header-sidebar-pinned {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $screen-md-min) {
|
@media (min-width: $screen-md-min) {
|
||||||
padding-right: $gutter_width;
|
.content-wrapper {
|
||||||
|
padding-right: $gutter_width;
|
||||||
|
}
|
||||||
|
|
||||||
&:not(.with-overlay) .merge-request-tabs-holder.affix {
|
&:not(.with-overlay) .merge-request-tabs-holder.affix {
|
||||||
right: $gutter_width;
|
right: $gutter_width;
|
||||||
|
@ -252,4 +256,9 @@ header.header-sidebar-pinned {
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
border-left: 1px solid $border-color;
|
border-left: 1px solid $border-color;
|
||||||
|
|
||||||
|
&.affix {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,6 +138,13 @@ pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
color: $gl-grayish-blue;
|
||||||
|
padding: 0 0 0 15px;
|
||||||
|
margin: 0;
|
||||||
|
border-left: 3px solid $white-dark;
|
||||||
|
}
|
||||||
|
|
||||||
span.highlight_word {
|
span.highlight_word {
|
||||||
background-color: $highlighted-highlight-word !important;
|
background-color: $highlighted-highlight-word !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,12 +298,8 @@
|
||||||
|
|
||||||
.issue-boards-sidebar {
|
.issue-boards-sidebar {
|
||||||
&.right-sidebar {
|
&.right-sidebar {
|
||||||
top: 153px;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
@media (min-width: $screen-sm-min) {
|
|
||||||
top: 220px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.issuable-sidebar-header {
|
.issuable-sidebar-header {
|
||||||
|
|
|
@ -159,7 +159,6 @@
|
||||||
|
|
||||||
.commit-row-description {
|
.commit-row-description {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border-left: 1px solid $white-normal;
|
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
background: $gray-light;
|
background: $gray-light;
|
||||||
|
|
|
@ -284,7 +284,11 @@
|
||||||
|
|
||||||
.events-description {
|
.events-description {
|
||||||
line-height: 65px;
|
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;
|
cursor: pointer;
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $gray-normal;
|
background-color: $gray-normal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,6 @@
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
.md {
|
.md {
|
||||||
color: $gl-grayish-blue;
|
|
||||||
font-size: $gl-font-size;
|
font-size: $gl-font-size;
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
|
|
|
@ -189,11 +189,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: $header-height;
|
top: $header-height;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 10;
|
|
||||||
transition: width .3s;
|
transition: width .3s;
|
||||||
background: $gray-light;
|
background: $gray-light;
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
|
@ -461,8 +460,19 @@
|
||||||
|
|
||||||
.issuable-list {
|
.issuable-list {
|
||||||
li {
|
li {
|
||||||
|
|
||||||
|
.issue-box {
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.issue-info-container {
|
||||||
|
-webkit-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
padding-right: $gl-padding;
|
||||||
|
}
|
||||||
|
|
||||||
.issue-check {
|
.issue-check {
|
||||||
float: left;
|
|
||||||
padding-right: $gl-padding;
|
padding-right: $gl-padding;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
min-width: 15px;
|
min-width: 15px;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.issues-list {
|
.issues-list {
|
||||||
.issue {
|
.issue {
|
||||||
padding: 10px $gl-padding;
|
padding: 10px 0 10px $gl-padding;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -148,3 +148,7 @@ ul.related-merge-requests > li {
|
||||||
border: 1px solid $border-gray-normal;
|
border: 1px solid $border-gray-normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.recaptcha {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
|
@ -116,6 +116,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.manage-labels-list {
|
.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 {
|
.btn-action {
|
||||||
color: $gl-text-color;
|
color: $gl-text-color;
|
||||||
|
|
||||||
|
@ -259,3 +275,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-link {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
|
@ -80,19 +80,28 @@
|
||||||
.ci_widget {
|
.ci_widget {
|
||||||
border-bottom: 1px solid $well-inner-border;
|
border-bottom: 1px solid $well-inner-border;
|
||||||
color: $gl-text-color;
|
color: $gl-text-color;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
i,
|
||||||
|
svg {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: 4px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 1px;
|
top: 1px;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ci-success_with_warnings {
|
& > span {
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
i {
|
@media (max-width: $screen-xs-max) {
|
||||||
color: $gl-warning;
|
flex-wrap: wrap;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +111,43 @@
|
||||||
padding: $gl-padding;
|
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 {
|
.normal {
|
||||||
color: $gl-text-color;
|
color: $gl-text-color;
|
||||||
}
|
}
|
||||||
|
@ -223,8 +269,15 @@
|
||||||
|
|
||||||
.mr-list {
|
.mr-list {
|
||||||
.merge-request {
|
.merge-request {
|
||||||
padding: 10px 15px;
|
padding: 10px 0 10px 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.issue-info-container {
|
||||||
|
-webkit-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.merge-request-title {
|
.merge-request-title {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
|
@ -430,7 +483,7 @@
|
||||||
background-color: $white-light;
|
background-color: $white-light;
|
||||||
|
|
||||||
&.affix {
|
&.affix {
|
||||||
top: 100px;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
transition: right .15s;
|
transition: right .15s;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue