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:
Lin Jen-Shin 2017-02-15 16:13:14 +08:00
commit a065ee341c
855 changed files with 17792 additions and 15257 deletions

View File

@ -12,12 +12,18 @@
"localStorage": false
},
"plugins": [
"filenames"
"filenames",
"import"
],
"settings": {
"import/resolver": {
"webpack": {
"config": "./config/webpack.config.js"
}
}
},
"rules": {
"filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"],
"no-multiple-empty-lines": ["error", { "max": 1 }],
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off"
"no-multiple-empty-lines": ["error", { "max": 1 }]
}
}

View File

@ -1,3 +1,4 @@
*.erb
lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb
app/policies/project_policy.rb

View File

@ -107,7 +107,10 @@ setup-test-env:
<<: *dedicated-runner
stage: prepare
script:
- npm install
- node --version
- yarn --version
- yarn install --pure-lockfile
- yarn check # ensure that yarn.lock matches package.json
- bundle exec rake gitlab:assets:compile
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
@ -163,64 +166,7 @@ spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.1
.ruby-21: &ruby-21
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
<<: *use-db
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
cache:
key: "ruby21"
paths:
- vendor/ruby
.rspec-knapsack-ruby21: &rspec-knapsack-ruby21
<<: *rspec-knapsack
<<: *dedicated-runner
<<: *ruby-21
.spinach-knapsack-ruby21: &spinach-knapsack-ruby21
<<: *spinach-knapsack
<<: *dedicated-runner
<<: *ruby-21
rspec 0 20 ruby21: *rspec-knapsack-ruby21
rspec 1 20 ruby21: *rspec-knapsack-ruby21
rspec 2 20 ruby21: *rspec-knapsack-ruby21
rspec 3 20 ruby21: *rspec-knapsack-ruby21
rspec 4 20 ruby21: *rspec-knapsack-ruby21
rspec 5 20 ruby21: *rspec-knapsack-ruby21
rspec 6 20 ruby21: *rspec-knapsack-ruby21
rspec 7 20 ruby21: *rspec-knapsack-ruby21
rspec 8 20 ruby21: *rspec-knapsack-ruby21
rspec 9 20 ruby21: *rspec-knapsack-ruby21
rspec 10 20 ruby21: *rspec-knapsack-ruby21
rspec 11 20 ruby21: *rspec-knapsack-ruby21
rspec 12 20 ruby21: *rspec-knapsack-ruby21
rspec 13 20 ruby21: *rspec-knapsack-ruby21
rspec 14 20 ruby21: *rspec-knapsack-ruby21
rspec 15 20 ruby21: *rspec-knapsack-ruby21
rspec 16 20 ruby21: *rspec-knapsack-ruby21
rspec 17 20 ruby21: *rspec-knapsack-ruby21
rspec 18 20 ruby21: *rspec-knapsack-ruby21
rspec 19 20 ruby21: *rspec-knapsack-ruby21
spinach 0 10 ruby21: *spinach-knapsack-ruby21
spinach 1 10 ruby21: *spinach-knapsack-ruby21
spinach 2 10 ruby21: *spinach-knapsack-ruby21
spinach 3 10 ruby21: *spinach-knapsack-ruby21
spinach 4 10 ruby21: *spinach-knapsack-ruby21
spinach 5 10 ruby21: *spinach-knapsack-ruby21
spinach 6 10 ruby21: *spinach-knapsack-ruby21
spinach 7 10 ruby21: *spinach-knapsack-ruby21
spinach 8 10 ruby21: *spinach-knapsack-ruby21
spinach 9 10 ruby21: *spinach-knapsack-ruby21
# Other generic tests
.ruby-static-analysis: &ruby-static-analysis
variables:
SIMPLECOV: "false"
@ -243,6 +189,7 @@ rubocop:
rake haml_lint: *exec
rake scss_lint: *exec
rake config_lint: *exec
rake brakeman: *exec
rake flay: *exec
license_finder: *exec
@ -302,13 +249,12 @@ karma:
<<: *use-db
<<: *dedicated-runner
script:
- npm link istanbul
- bundle exec rake karma
artifacts:
name: coverage-javascript
expire_in: 31d
paths:
- coverage-javascript/default/
- coverage-javascript/
lint-doc:
stage: test
@ -381,11 +327,9 @@ lint:javascript:
paths:
- node_modules/
stage: test
image: "node:7.1"
before_script:
- npm install
before_script: []
script:
- npm --silent run eslint
- yarn run eslint
lint:javascript:report:
<<: *dedicated-runner
@ -393,12 +337,10 @@ lint:javascript:report:
paths:
- node_modules/
stage: post-test
image: "node:7.1"
before_script:
- npm install
before_script: []
script:
- find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
- npm --silent run eslint-report || true # ignore exit code
- yarn run eslint-report || true # ignore exit code
artifacts:
name: eslint-report
expire_in: 31d
@ -450,9 +392,9 @@ pages:
script:
- mv public/ .public/
- mkdir public/
- mv coverage public/coverage-ruby
- mv coverage-javascript/default/ public/coverage-javascript/
- mv eslint-report.html public/
- mv coverage/ public/coverage-ruby/ || true
- mv coverage-javascript/ public/coverage-javascript/ || true
- mv eslint-report.html public/ || true
artifacts:
paths:
- public

View File

@ -6,14 +6,14 @@
(How one can reproduce the issue - this is very important)
### Expected behavior
(What you should see instead)
### Actual behavior
### What is the current *bug* behavior?
(What actually happens)
### What is the expected *correct* behavior?
(What you should see instead)
### Relevant logs and/or screenshots
(Paste any relevant logs - please use code blocks (```) to format console output,
@ -23,6 +23,14 @@ logs, and code as it's very hard to read otherwise.)
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
#### Results of GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
#### Results of GitLab application Check
(For installations with omnibus-gitlab package run and paste the output of:
@ -33,14 +41,6 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing)
#### Results of GitLab environment info
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
### Possible fixes
(If you can, link to the line of code that might be responsible for the problem)

View File

@ -5,7 +5,7 @@ require:
inherit_from: .rubocop_todo.yml
AllCops:
TargetRubyVersion: 2.1
TargetRubyVersion: 2.3
# Cop names are not d§splayed in offense messages by default. Change behavior
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
# option.
@ -31,8 +31,7 @@ AllCops:
- 'lib/gitlab/seeder.rb'
- 'generator_templates/**/*'
##################### Style ##################################
# Style #######################################################################
# Check indentation of private/protected visibility modifiers.
Style/AccessModifierIndentation:
@ -340,6 +339,10 @@ Style/OpMethod:
Style/ParenthesesAroundCondition:
Enabled: true
# Checks for an obsolete RuntimeException argument in raise/fail.
Style/RedundantException:
Enabled: true
# Checks for parentheses that seem not to serve any purpose.
Style/RedundantParentheses:
Enabled: true
@ -471,7 +474,7 @@ Style/WhileUntilModifier:
Style/WordArray:
Enabled: false
#################### Metrics ################################
# Metrics #####################################################################
# A calculated magnitude based on number of assignments,
# branches, and conditions.
@ -516,8 +519,7 @@ Metrics/PerceivedComplexity:
Enabled: true
Max: 18
#################### Lint ################################
# Lint ########################################################################
# Checks for useless access modifiers.
Lint/UselessAccessModifier:
@ -570,6 +572,10 @@ Lint/ElseLayout:
Lint/EmptyEnsure:
Enabled: true
# Checks for the presence of `when` branches without a body.
Lint/EmptyWhen:
Enabled: true
# Align ends correctly.
Lint/EndAlignment:
Enabled: true
@ -679,8 +685,7 @@ Lint/UselessSetterCall:
Lint/Void:
Enabled: true
##################### Performance ############################
# Performance #################################################################
# Use `casecmp` rather than `downcase ==`.
Performance/Casecmp:
@ -718,8 +723,7 @@ Performance/StringReplacement:
Performance/TimesMap:
Enabled: true
##################### Rails ##################################
# Rails #######################################################################
# Enables Rails cops.
Rails:
@ -767,12 +771,16 @@ Rails/ReadWriteAttribute:
Rails/ScopeArgs:
Enabled: true
##################### RSpec ##################################
# RSpec #######################################################################
# Check that instances are not being stubbed globally.
RSpec/AnyInstance:
Enabled: false
# Check for expectations where `be(...)` can replace `eql(...)`.
RSpec/BeEql:
Enabled: false
# Check that the first argument to the top level describe is the tested class or
# module.
RSpec/DescribeClass:
@ -801,6 +809,10 @@ RSpec/ExampleWording:
not: does not
IgnoredWords: []
# Checks for `expect(...)` calls containing literal values.
RSpec/ExpectActual:
Enabled: true
# Checks the file and folder naming of the spec file.
RSpec/FilePath:
Enabled: false
@ -828,3 +840,9 @@ RSpec/NotToNot:
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false
# Custom ######################################################################
# Disallow the `git` and `github` arguments in the Gemfile.
GemFetcher:
Enabled: true

View File

@ -21,10 +21,6 @@ Lint/AmbiguousRegexpLiteral:
Lint/AssignmentInCondition:
Enabled: false
# Offense count: 1
Lint/EmptyWhen:
Enabled: false
# Offense count: 20
Lint/HandleExceptions:
Enabled: false
@ -80,19 +76,11 @@ Performance/RedundantMatch:
Performance/RedundantMerge:
Enabled: false
# Offense count: 7
RSpec/BeEql:
Enabled: false
# Offense count: 15
# Configuration parameters: CustomIncludeMethods.
RSpec/EmptyExampleGroup:
Enabled: false
# Offense count: 24
RSpec/ExpectActual:
Enabled: false
# Offense count: 58
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: implicit, each, example
@ -424,11 +412,6 @@ Style/RaiseArgs:
Style/RedundantBegin:
Enabled: false
# Offense count: 1
# Cop supports --auto-correct.
Style/RedundantException:
Enabled: false
# Offense count: 29
# Cop supports --auto-correct.
Style/RedundantFreeze:

View File

@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 8.16.5 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.16.4 (2017-02-02)
- Support non-ASCII characters in GFM autocomplete. !8729
@ -174,6 +181,13 @@ entry.
- Add margin to markdown math blocks.
- Add hover state to MR comment reply button.
## 8.15.6 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.15.4 (2017-01-09)
- Make successful pipeline emails off for watchers. !8176
@ -437,6 +451,13 @@ entry.
- Whitelist next project names: help, ci, admin, search. !8227
- Adds back CSS for progress-bars. !8237
## 8.14.9 (2017-02-14)
- Patch Asciidocs rendering to block XSS.
- Fix XSS vulnerability in SVG attachments.
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
- Patch XSS vulnerability in RDOC support.
## 8.14.8 (2017-01-25)
- Accept environment variables from the `pre-receive` script. !7967

View File

@ -15,6 +15,7 @@
- [Issue weight](#issue-weight)
- [Regression issues](#regression-issues)
- [Technical debt](#technical-debt)
- [Stewardship](#stewardship)
- [Merge requests](#merge-requests)
- [Merge request guidelines](#merge-request-guidelines)
- [Contribution acceptance criteria](#contribution-acceptance-criteria)
@ -230,6 +231,21 @@ for a release by the appropriate person.
Make sure to mention the merge request that the `technical debt` issue is
associated with in the description of the issue.
### Stewardship
For issues related to the open source stewardship of GitLab,
there is the ~"stewardship" label.
This label is to be used for issues in which the stewardship of GitLab
is a topic of discussion. For instance if GitLab Inc. is planning to remove
features from GitLab CE to make exclusive in GitLab EE, related issues
would be labelled with ~"stewardship".
A recent example of this was the issue for
[bringing the time tracking API to GitLab CE][time-tracking-issue].
[time-tracking-issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/25517#note_20019084
## Merge requests
We welcome merge requests with fixes and improvements to GitLab code, tests,
@ -414,7 +430,7 @@ merge request:
1. [Newlines styleguide][newlines-styleguide]
1. [Testing](doc/development/testing.md)
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5)
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security

View File

@ -1 +1 @@
0.2.4
0.3.0

View File

@ -29,6 +29,7 @@ gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.2'
gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
@ -284,7 +285,7 @@ group :development, :test do
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling'
gem 'rspec_profiling', '~> 0.0.5'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'

View File

@ -483,6 +483,8 @@ GEM
omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0)
omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
omniauth-saml (1.7.0)
omniauth (~> 1.3)
ruby-saml (~> 1.4)
@ -638,7 +640,7 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rspec_profiling (0.0.4)
rspec_profiling (0.0.5)
activerecord
pg
rails
@ -738,7 +740,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
sqlite3 (1.3.13)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
@ -931,6 +933,7 @@ DEPENDENCIES
omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.4.1)
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.7.0)
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
@ -962,7 +965,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rspec_profiling
rspec_profiling (~> 0.0.5)
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
ruby-fogbugz (~> 0.2.1)
@ -1011,4 +1014,4 @@ DEPENDENCIES
wikicloth (= 0.8.1)
BUNDLED WITH
1.14.2
1.14.3

View File

@ -1,4 +1,4 @@
Copyright (c) 2011-2016 GitLab B.V.
Copyright (c) 2011-2017 GitLab B.V.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -33,7 +33,7 @@ core team members will mention this person.
### Merge request coaching
Several people from the [GitLab team][team] are helping community members to get
their contributions accepted by meeting our [Definition of done][CONTRIBUTING.md#definition-of-done].
their contributions accepted by meeting our [Definition of done](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#definition-of-done).
What you can expect from them is described at https://about.gitlab.com/jobs/merge-request-coach/.
@ -59,67 +59,85 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature Freeze
On the 7th of each month, the stable branches for the upcoming release will
be frozen for major changes. Merge requests may still be merged into master
during this period. By freezing the stable branches prior to a release there's
no need to worry about last minute merge requests potentially breaking a lot of
things.
After the 7th (Pacific Standard Time Zone) of each month, RC1 of the upcoming release is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
Merge requests may still be merged into master during this period,
but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
What is considered to be a major change is determined on a case by case basis as
this definition depends very much on the context of changes. For example, a 5
line change might have a big impact on the entire application. Ultimately the
decision will be made by the maintainers and the release managers.
Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release)
and security issues will be cherry-picked into the stable branch.
Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch.
These fixes will be released in the next RC (before the 22nd) or patch release (after the 22nd).
If you think a merge request should go into the upcoming release even though it does not meet these requirements,
you can ask for an exception to be made. Exceptions require sign-off from 3 people besides the developer:
1. a Release Manager
2. an Engineering Lead
3. an Engineering Director, the VP of Engineering, or the CTO
You can find who is who on the [team page](https://about.gitlab.com/team/).
Whether an exception is made is determined by weighing the benefit and urgency of the change
(how important it is to the company that this is released _right now_ instead of in a month)
against the potential negative impact
(things breaking without enough time to comfortably find and fix them before the release on the 22nd).
When in doubt, we err on the side of _not_ cherry-picking.
For example, it is likely that an exception will be made for a trivial 1-5 line performance improvement
(e.g. adding a database index or adding `includes` to a query), but not for a new feature, no matter how relatively small or thoroughly tested.
During the feature freeze all merge requests that are meant to go into the upcoming
release should have the correct milestone assigned _and_ have the label
~"Pick into Stable" set. Merge requests without a milestone and this label will
~"Pick into Stable" set, so that release managers can find and pick them.
Merge requests without a milestone and this label will
not be merged into any stable branches.
## Copy & paste responses
### Improperly formatted issue
Thanks for the issue report. Please reformat your issue to conform to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
Thanks for the issue report. Please reformat your issue to conform to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Issue report for old version
Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Support requests and configuration questions
Thanks for your interest in GitLab. We don't use the issue tracker for support
requests and configuration questions. Please check our
\[getting help\]\(https://about.gitlab.com/getting-help/) page to see all of the available
support options. Also, have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
[getting help](https://about.gitlab.com/getting-help/) page to see all of the available
support options. Also, have a look at the [contribution guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md)
for more information.
### Code format
Please use ``` to format console output, logs, and code as it's very hard to read otherwise.
Please use \`\`\` to format console output, logs, and code as it's very hard to read otherwise.
### Issue fixed in newer version
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please [upgrade](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Improperly formatted merge request
Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-request-guidelines).
Thanks for your interest in improving the GitLab codebase! Please update your merge request according to the [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-request-guidelines).
### Inactivity close of an issue
It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
It's been at least 2 weeks (and a new release) since we heard from you. I'm closing this issue but if you still experience this problem, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
### Inactivity close of a merge request
This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this merge request.
This merge request has been closed because a request for more information has not been reacted to for more than 2 weeks. If you respond and conform to the merge request guidelines in our [contributing guidelines](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#pull-requests) we will reopen this merge request.
### Accepting merge requests
Is there an issue on the
\[issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues) that is
[issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) that is
similar to this? Could you please link it here?
Please be aware that new functionality that is not marked
\[accepting merge requests\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests)
[accepting merge requests](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests)
might not make it into GitLab.
### Only accepting merge requests with green tests
@ -134,7 +152,7 @@ rebase with master to see if that solves the issue.
We are currently in the process of closing down the issue tracker on GitHub, to
prevent duplication with the GitLab.com issue tracker.
Since this is an older issue I'll be closing this for now. If you think this is
still an issue I encourage you to open it on the \[GitLab.com issue tracker\]\(https://gitlab.com/gitlab-org/gitlab-ce/issues).
still an issue I encourage you to open it on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues).
[team]: https://about.gitlab.com/team/
[contribution acceptance criteria]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#contribution-acceptance-criteria

View File

@ -11,7 +11,7 @@
licensePath: "/api/:version/templates/licenses/:key",
gitignorePath: "/api/:version/templates/gitignores/:key",
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
dockerfilePath: "/api/:version/dockerfiles/:key",
dockerfilePath: "/api/:version/templates/dockerfiles/:key",
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
group: function(group_id, callback) {
var url = Api.buildUrl(Api.groupPath)

View File

@ -10,7 +10,6 @@ function requireAll(context) { return context.keys().map(context); }
window.$ = window.jQuery = require('jquery');
require('jquery-ui/ui/autocomplete');
require('jquery-ui/ui/datepicker');
require('jquery-ui/ui/draggable');
require('jquery-ui/ui/effect-highlight');
require('jquery-ui/ui/sortable');
@ -21,7 +20,7 @@ require('vendor/jquery.waitforimages');
require('vendor/jquery.caret');
require('vendor/jquery.atwho');
require('vendor/jquery.scrollTo');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
require('./autosave');
require('bootstrap/js/affix');
require('bootstrap/js/alert');
@ -35,8 +34,10 @@ require('bootstrap/js/transition');
require('bootstrap/js/tooltip');
require('bootstrap/js/popover');
require('select2/select2.js');
window.Pikaday = require('pikaday');
window._ = require('underscore');
window.Dropzone = require('dropzone');
window.Sortable = require('vendor/Sortable');
require('mousetrap');
require('mousetrap/plugins/pause/mousetrap-pause');
require('./shortcuts');
@ -55,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/));
require('vendor/fuzzaldrin-plus');
window.ES6Promise = require('vendor/es6-promise.auto');
window.ES6Promise.polyfill();
require('es6-promise').polyfill();
(function () {
document.addEventListener('beforeunload', function () {
@ -247,5 +247,7 @@ window.ES6Promise.polyfill();
new Aside();
// bind sidebar events
new gl.Sidebar();
gl.utils.initTimeagoTimeout();
});
}).call(this);

View File

@ -6,7 +6,6 @@ function requireAll(context) { return context.keys().map(context); }
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
window.Sortable = require('vendor/Sortable');
requireAll(require.context('./models', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', true, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', true, /^\.\/.*\.(js|es6)$/));
@ -16,7 +15,7 @@ require('./components/board');
require('./components/board_sidebar');
require('./components/new_list_dropdown');
require('./components/modal/index');
require('./vue_resource_interceptor');
require('../vue_shared/vue_resource_interceptor');
$(() => {
const $boardApp = document.getElementById('board-app');

View File

@ -1,6 +1,7 @@
/* global Vue */
/* global dateFormat */
Vue.filter('due-date', (value) => {
const date = new Date(value);
return $.datepicker.formatDate('M d, yy', date);
return dateFormat(date, 'mmm d, yyyy', true);
});

View File

@ -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;
});
});

View File

@ -67,16 +67,8 @@
Build.prototype.initSidebar = function() {
this.$sidebar = $('.js-build-sidebar');
this.sidebarTranslationLimits = {
min: $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()
};
this.sidebarTranslationLimits.max = this.sidebarTranslationLimits.min + $('.scrolling-tabs-container').outerHeight();
this.$sidebar.css({
top: this.sidebarTranslationLimits.max
});
this.$sidebar.niceScroll();
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
this.$document.off('scroll.translateSidebar').on('scroll.translateSidebar', this.translateSidebar.bind(this));
};
Build.prototype.location = function() {
@ -231,14 +223,6 @@
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
};
Build.prototype.translateSidebar = function(e) {
var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop);
if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min;
this.$sidebar.css({
top: newPosition
});
};
Build.prototype.toggleSidebar = function(shouldHide) {
var shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
@ -285,7 +269,7 @@
e.preventDefault();
$currentTarget = $(e.currentTarget);
$.scrollTo($currentTarget.attr('href'), {
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
offset: 0
});
};

View File

@ -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'),
});
});

View File

@ -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;

View File

@ -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;

View File

@ -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>
`,
});
})();

View File

@ -91,6 +91,9 @@ require('./lib/utils/common_utils');
},
},
SanitizationFilter: {
'a[name]:not([href]):empty'(el, text) {
return el.outerHTML;
},
'dl'(el, text) {
let lines = text.trim().split('\n');
// Add two spaces to the front of subsequent list items lines,

View File

@ -13,6 +13,12 @@
<div>
<div class="events-description">
{{ stage.description }}
<span v-if="items.length === 50" class="events-info pull-right">
<i class="fa fa-warning has-tooltip"
title="Limited to showing 50 events at most"
data-placement="top"></i>
Showing 50 events
</span>
</div>
<ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item">

View File

@ -3,7 +3,7 @@
/* global Flash */
window.Vue = require('vue');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
function requireAll(context) { return context.keys().map(context); }
requireAll(require.context('./svg', false, /^\.\/.*\.(js|es6)$/));

View File

@ -76,7 +76,7 @@ require('./lib/utils/url_utility');
const diffFile = diffTitle.closest('.diff-file');
const nothingHereBlock = $('.nothing-here-block:visible', diffFile);
if (nothingHereBlock.length) {
const clickTarget = $('.file-title, .click-to-expand', diffFile);
const clickTarget = $('.js-file-title, .click-to-expand', diffFile);
diffFile.data('singleFileDiff').toggleDiff(clickTarget, () => {
this.highlighSelectedLine();
if (cb) cb();

View File

@ -1,6 +1,6 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
/* global Vue */
/* global CommentsStore */
const Vue = require('vue');
(() => {
const CommentAndResolveBtn = Vue.extend({
@ -9,13 +9,11 @@
},
data() {
return {
textareaIsEmpty: true
textareaIsEmpty: true,
discussion: {},
};
},
computed: {
discussion: function () {
return CommentsStore.state[this.discussionId];
},
showButton: function () {
if (this.discussion) {
return this.discussion.isResolvable();
@ -42,6 +40,9 @@
}
}
},
created() {
this.discussion = CommentsStore.state[this.discussionId];
},
mounted: function () {
const $textarea = $(`#new-discussion-note-form-${this.discussionId} .note-textarea`);
this.textareaIsEmpty = $textarea.val() === '';

View File

@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */
/* global Vue */
/* global DiscussionMixins */
/* global CommentsStore */
const Vue = require('vue');
(() => {
const JumpToDiscussion = Vue.extend({
@ -12,12 +12,10 @@
data: function () {
return {
discussions: CommentsStore.state,
discussion: {},
};
},
computed: {
discussion: function () {
return this.discussions[this.discussionId];
},
allResolved: function () {
return this.unresolvedDiscussionCount === 0;
},
@ -183,10 +181,13 @@
}
$.scrollTo($target, {
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
offset: 0
});
}
}
},
created() {
this.discussion = this.discussions[this.discussionId];
},
});
Vue.component('jump-to-discussion', JumpToDiscussion);

View File

@ -1,8 +1,8 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */
/* global Vue */
/* global CommentsStore */
/* global ResolveService */
/* global Flash */
const Vue = require('vue');
(() => {
const ResolveBtn = Vue.extend({
@ -10,14 +10,14 @@
noteId: Number,
discussionId: String,
resolved: Boolean,
projectPath: String,
canResolve: Boolean,
resolvedBy: String
},
data: function () {
return {
discussions: CommentsStore.state,
loading: false
loading: false,
note: {},
};
},
watch: {
@ -30,13 +30,6 @@
discussion: function () {
return this.discussions[this.discussionId];
},
note: function () {
if (this.discussion) {
return this.discussion.getNote(this.noteId);
} else {
return undefined;
}
},
buttonText: function () {
if (this.isResolved) {
return `Resolved by ${this.resolvedByName}`;
@ -73,10 +66,10 @@
if (this.isResolved) {
promise = ResolveService
.unresolve(this.projectPath, this.noteId);
.unresolve(this.noteId);
} else {
promise = ResolveService
.resolve(this.projectPath, this.noteId);
.resolve(this.noteId);
}
promise.then((response) => {
@ -106,6 +99,8 @@
},
created: function () {
CommentsStore.create(this.discussionId, this.noteId, this.canResolve, this.resolved, this.resolvedBy);
this.note = this.discussion.getNote(this.noteId);
}
});

View File

@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
/* global Vue */
/* global DiscussionMixins */
/* global CommentsStore */
const Vue = require('vue');
((w) => {
w.ResolveCount = Vue.extend({

View File

@ -1,25 +1,22 @@
/* eslint-disable object-shorthand, func-names, space-before-function-paren, comma-dangle, no-else-return, quotes, max-len */
/* global Vue */
/* global CommentsStore */
/* global ResolveService */
const Vue = require('vue');
(() => {
const ResolveDiscussionBtn = Vue.extend({
props: {
discussionId: String,
mergeRequestId: Number,
projectPath: String,
canResolve: Boolean,
},
data: function() {
return {
discussions: CommentsStore.state
discussion: {},
};
},
computed: {
discussion: function () {
return this.discussions[this.discussionId];
},
showButton: function () {
if (this.discussion) {
return this.discussion.isResolvable();
@ -51,11 +48,13 @@
},
methods: {
resolve: function () {
ResolveService.toggleResolveForDiscussion(this.projectPath, this.mergeRequestId, this.discussionId);
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
}
},
created: function () {
CommentsStore.createDiscussion(this.discussionId, this.canResolve);
this.discussion = CommentsStore.state[this.discussionId];
}
});

View File

@ -3,6 +3,7 @@
/* global ResolveCount */
function requireAll(context) { return context.keys().map(context); }
const Vue = require('vue');
requireAll(require.context('./models', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./stores', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./services', false, /^\.\/.*\.(js|es6)$/));
@ -10,11 +11,14 @@ requireAll(require.context('./mixins', false, /^\.\/.*\.(js|es6)$/));
requireAll(require.context('./components', false, /^\.\/.*\.(js|es6)$/));
$(() => {
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn';
window.gl = window.gl || {};
window.gl.diffNoteApps = {};
window.ResolveService = new gl.DiffNotesResolveServiceClass(projectPath);
gl.diffNotesCompileComponents = () => {
const $components = $(COMPONENT_SELECTOR).filter(function () {
return $(this).closest('resolve-count').length !== 1;

View File

@ -1,45 +1,37 @@
/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */
/* global Vue */
/* global Flash */
/* global CommentsStore */
((w) => {
const Vue = window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../../vue_shared/vue_resource_interceptor');
(() => {
window.gl = window.gl || {};
class ResolveServiceClass {
constructor() {
this.noteResource = Vue.resource('notes{/noteId}/resolve');
this.discussionResource = Vue.resource('merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve');
constructor(root) {
this.noteResource = Vue.resource(`${root}/notes{/noteId}/resolve`);
this.discussionResource = Vue.resource(`${root}/merge_requests{/mergeRequestId}/discussions{/discussionId}/resolve`);
}
setCSRF() {
Vue.http.headers.common['X-CSRF-Token'] = $.rails.csrfToken();
}
prepareRequest(root) {
this.setCSRF();
Vue.http.options.root = root;
}
resolve(projectPath, noteId) {
this.prepareRequest(projectPath);
resolve(noteId) {
return this.noteResource.save({ noteId }, {});
}
unresolve(projectPath, noteId) {
this.prepareRequest(projectPath);
unresolve(noteId) {
return this.noteResource.delete({ noteId }, {});
}
toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId) {
toggleResolveForDiscussion(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
const isResolved = discussion.isResolved();
let promise;
if (isResolved) {
promise = this.unResolveAll(projectPath, mergeRequestId, discussionId);
promise = this.unResolveAll(mergeRequestId, discussionId);
} else {
promise = this.resolveAll(projectPath, mergeRequestId, discussionId);
promise = this.resolveAll(mergeRequestId, discussionId);
}
promise.then((response) => {
@ -62,11 +54,9 @@
});
}
resolveAll(projectPath, mergeRequestId, discussionId) {
resolveAll(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
this.prepareRequest(projectPath);
discussion.loading = true;
return this.discussionResource.save({
@ -75,11 +65,9 @@
}, {});
}
unResolveAll(projectPath, mergeRequestId, discussionId) {
unResolveAll(mergeRequestId, discussionId) {
const discussion = CommentsStore.state[discussionId];
this.prepareRequest(projectPath);
discussion.loading = true;
return this.discussionResource.delete({
@ -89,5 +77,5 @@
}
}
w.ResolveService = new ResolveServiceClass();
})(window);
gl.DiffNotesResolveServiceClass = ResolveServiceClass;
})();

View File

@ -19,7 +19,6 @@
/* global UsersSelect */
/* global GroupAvatar */
/* global LineHighlighter */
/* global ShortcutsBlob */
/* global ProjectFork */
/* global BuildArtifacts */
/* global GroupsSelect */
@ -36,6 +35,8 @@
/* global Labels */
/* global Shortcuts */
const ShortcutsBlob = require('./shortcuts_blob');
(function() {
var Dispatcher;
@ -96,6 +97,7 @@
break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
case 'projects:milestones:update':
new ZenMode();
new gl.DueDateSelectors();
new gl.GLForm($('.milestone-form'));
@ -162,7 +164,7 @@
case 'projects:commit:pipelines':
new gl.MiniPipelineGraph({
container: '.js-pipeline-table',
});
}).bindEvents();
break;
case 'projects:commits:show':
case 'projects:activity':
@ -225,7 +227,12 @@
case 'projects:blame:show':
new LineHighlighter();
shortcut_handler = new ShortcutsNavigation();
new ShortcutsBlob(true);
const fileBlobPermalinkUrlElement = document.querySelector('.js-data-file-blob-permalink-url');
const fileBlobPermalinkUrl = fileBlobPermalinkUrlElement && fileBlobPermalinkUrlElement.getAttribute('href');
new ShortcutsBlob({
skipResetBindings: true,
fileBlobPermalinkUrl,
});
break;
case 'groups:labels:new':
case 'groups:labels:edit':
@ -259,7 +266,7 @@
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
case 'projects:variables:index':
case 'projects:ci_cd:show':
new gl.ProjectVariables();
break;
case 'ci:lints:create':

View File

@ -78,8 +78,8 @@ require('../window')(function(w){
},
destroy: function() {
if (this.listTemplate) {
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
var dynamicList = this.hook.list.list.querySelector('[data-dynamic]');
if (this.listTemplate && dynamicList) {
dynamicList.outerHTML = this.listTemplate;
}
}

View File

@ -1,4 +1,6 @@
/* eslint-disable wrap-iife, func-names, space-before-function-paren, comma-dangle, prefer-template, consistent-return, class-methods-use-this, arrow-body-style, no-unused-vars, no-underscore-dangle, no-new, max-len, no-sequences, no-unused-expressions, no-param-reassign */
/* global dateFormat */
/* global Pikaday */
(function(global) {
class DueDateSelect {
@ -25,11 +27,14 @@
this.initGlDropdown();
this.initRemoveDueDate();
this.initDatePicker();
this.initStopPropagation();
}
initGlDropdown() {
this.$dropdown.glDropdown({
opened: () => {
const calendar = this.$datePicker.data('pikaday');
calendar.show();
},
hidden: () => {
this.$selectbox.hide();
this.$value.css('display', '');
@ -38,25 +43,37 @@
}
initDatePicker() {
this.$datePicker.datepicker({
dateFormat: 'yy-mm-dd',
defaultDate: $("input[name='" + this.fieldName + "']").val(),
altField: "input[name='" + this.fieldName + "']",
onSelect: () => {
const $dueDateInput = $(`input[name='${this.fieldName}']`);
const calendar = new Pikaday({
field: $dueDateInput.get(0),
theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
onSelect: (dateText) => {
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
$dueDateInput.val(formattedDate);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
gl.issueBoards.BoardsStore.detail.issue.dueDate = $(`input[name='${this.fieldName}']`).val();
gl.issueBoards.BoardsStore.detail.issue.dueDate = $dueDateInput.val();
this.updateIssueBoardIssue();
} else {
return this.saveDueDate(true);
this.saveDueDate(true);
}
}
});
this.$datePicker.append(calendar.el);
this.$datePicker.data('pikaday', calendar);
}
initRemoveDueDate() {
this.$block.on('click', '.js-remove-due-date', (e) => {
const calendar = this.$datePicker.data('pikaday');
e.preventDefault();
calendar.setDate(null);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
gl.issueBoards.BoardsStore.detail.issue.dueDate = '';
this.updateIssueBoardIssue();
@ -67,12 +84,6 @@
});
}
initStopPropagation() {
$(document).off('click', '.ui-datepicker-header a').on('click', '.ui-datepicker-header a', (e) => {
return e.stopImmediatePropagation();
});
}
saveDueDate(isDropdown) {
this.parseSelectedDate();
this.prepSelectedDate();
@ -86,7 +97,7 @@
// Construct Date object manually to avoid buggy dateString support within Date constructor
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj);
this.displayedDate = dateFormat(dateObj, 'mmm d, yyyy');
} else {
this.displayedDate = 'No due date';
}
@ -153,14 +164,24 @@
}
initMilestoneDatePicker() {
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd'
$('.datepicker').each(function() {
const $datePicker = $(this);
const calendar = new Pikaday({
field: $datePicker.get(0),
theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
onSelect(dateText) {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
});
$datePicker.data('pikaday', calendar);
});
$('.js-clear-due-date,.js-clear-start-date').on('click', (e) => {
e.preventDefault();
const datepicker = $(e.target).siblings('.datepicker');
$.datepicker._clearDate(datepicker);
const calendar = $(e.target).siblings('.datepicker').data('pikaday');
calendar.setDate(null);
});
}

View File

@ -2,9 +2,9 @@
/* global timeago */
window.Vue = require('vue');
window.timeago = require('vendor/timeago');
window.timeago = require('timeago.js');
require('../../lib/utils/text_utility');
require('../../vue_common_component/commit');
require('../../vue_shared/components/commit');
require('./environment_actions');
require('./environment_external_url');
require('./environment_stop');
@ -147,12 +147,12 @@ require('./environment_terminal_button');
},
/**
* Returns the value of the `stoppable?` key provided in the response.
* Returns the value of the `stop_action?` key provided in the response.
*
* @returns {Boolean}
*/
isStoppable() {
return this.model['stoppable?'];
hasStopAction() {
return this.model['stop_action?'];
},
/**
@ -508,7 +508,7 @@ require('./environment_terminal_button');
</external-url-component>
</div>
<div v-if="isStoppable && canCreateDeployment"
<div v-if="hasStopAction && canCreateDeployment"
class="inline js-stop-component-container">
<stop-component
:stop-url="model.stop_path">

View File

@ -1,8 +1,7 @@
window.Vue = require('vue');
require('./stores/environments_store');
require('./components/environment');
require('./vue_resource_interceptor');
require('../vue_shared/vue_resource_interceptor');
$(() => {
window.gl = window.gl || {};

View File

@ -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
});
});

View File

@ -8,7 +8,7 @@ require('./filtered_search_dropdown');
super(droplab, dropdown, input, filter);
this.config = {
droplabAjaxFilter: {
endpoint: '/autocomplete/users.json',
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
searchKey: 'search',
params: {
per_page: 20,

View File

@ -4,7 +4,7 @@
class FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) {
this.droplab = droplab;
this.hookId = input.getAttribute('data-id');
this.hookId = input && input.getAttribute('data-id');
this.input = input;
this.filter = filter;
this.dropdown = dropdown;

View File

@ -2,7 +2,8 @@
(() => {
class FilteredSearchDropdownManager {
constructor() {
constructor(baseEndpoint = '') {
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer;
this.filteredSearchInput = document.querySelector('.filtered-search');
@ -38,13 +39,13 @@
milestone: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['milestones.json', '%'],
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'],
element: document.querySelector('#js-dropdown-milestone'),
},
label: {
reference: null,
gl: 'DropdownNonUser',
extraArguments: ['labels.json', '~'],
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'],
element: document.querySelector('#js-dropdown-label'),
},
hint: {

View File

@ -6,7 +6,7 @@
if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer;
this.dropdownManager = new gl.FilteredSearchDropdownManager();
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '');
this.bindEvents();
this.loadSearchParamsFromURL();

View File

@ -47,9 +47,11 @@
}
// Only filter asynchronously only if option remote is set
if (this.options.remote) {
$inputContainer.parent().addClass('is-loading');
clearTimeout(timeout);
return timeout = setTimeout(function() {
return this.options.query(this.input.val(), function(data) {
$inputContainer.parent().removeClass('is-loading');
return this.options.callback(data);
}.bind(this));
}.bind(this), 250);
@ -437,7 +439,7 @@
}
};
GitLabDropdown.prototype.opened = function() {
GitLabDropdown.prototype.opened = function(e) {
var contentHtml;
this.resetRows();
this.addArrowKeyEvent();
@ -457,6 +459,10 @@
this.positionMenuAbove();
}
if (this.options.opened) {
this.options.opened.call(this, e);
}
return this.dropdown.trigger('shown.gl.dropdown');
};

View File

@ -3,6 +3,8 @@
/* global UsersSelect */
/* global ZenMode */
/* global Autosave */
/* global dateFormat */
/* global Pikaday */
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@ -13,7 +15,7 @@
IssuableForm.prototype.wipRegex = /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i;
function IssuableForm(form) {
var $issuableDueDate;
var $issuableDueDate, calendar;
this.form = form;
this.toggleWip = bind(this.toggleWip, this);
this.renderWipExplanation = bind(this.renderWipExplanation, this);
@ -35,12 +37,14 @@
this.initMoveDropdown();
$issuableDueDate = $('#issuable-due-date');
if ($issuableDueDate.length) {
$('.datepicker').datepicker({
dateFormat: 'yy-mm-dd',
onSelect: function(dateText, inst) {
return $issuableDueDate.val(dateText);
calendar = new Pikaday({
field: $issuableDueDate.get(0),
theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
onSelect: function(dateText) {
$issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
}
}).datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', $issuableDueDate.val()));
});
}
}

View File

@ -1,5 +1,6 @@
/* eslint-disable comma-dangle, class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, consistent-return, func-names, space-before-function-paren, max-len */
/* global Flash */
/* global Sortable */
((global) => {
class LabelManager {
@ -9,11 +10,12 @@
this.otherLabels = otherLabels || $('.js-other-labels');
this.errorMessage = 'Unable to update label prioritization at this time';
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
this.prioritizedLabels.sortable({
items: 'li',
placeholder: 'list-placeholder',
axis: 'y',
update: this.onPrioritySortUpdate.bind(this)
this.sortable = Sortable.create(this.prioritizedLabels.get(0), {
filter: '.empty-message',
forceFallback: true,
fallbackClass: 'is-dragging',
dataIdAttr: 'data-id',
onUpdate: this.onPrioritySortUpdate.bind(this),
});
this.bindEvents();
}
@ -51,13 +53,13 @@
$target = this.otherLabels;
$from = this.prioritizedLabels;
}
if ($from.find('li').length === 1) {
$label.detach().appendTo($target);
if ($from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
if (!$target.find('li').length) {
if ($target.find('> li:not(.empty-message)').length) {
$target.find('.empty-message').addClass('hidden');
}
$label.detach().appendTo($target);
// Return if we are not persisting state
if (!persistState) {
return;
@ -101,8 +103,12 @@
getSortedLabelsIds() {
const sortedIds = [];
this.prioritizedLabels.find('li').each(function() {
sortedIds.push($(this).data('id'));
this.prioritizedLabels.find('> li').each(function() {
const id = $(this).data('id');
if (id) {
sortedIds.push(id);
}
});
return sortedIds;
}

View File

@ -69,27 +69,21 @@
var hash = w.gl.utils.getLocationHash();
if (!hash) return;
var navbar = document.querySelector('.navbar-gitlab');
var subnav = document.querySelector('.layout-nav');
var fixedTabs = document.querySelector('.js-tabs-affix');
var adjustment = 0;
if (navbar) adjustment -= navbar.offsetHeight;
if (subnav) adjustment -= subnav.offsetHeight;
// This is required to handle non-unicode characters in hash
hash = decodeURIComponent(hash);
// scroll to user-generated markdown anchor if we cannot find a match
if (document.getElementById(hash) === null) {
var target = document.getElementById('user-content-' + hash);
if (target && target.scrollIntoView) {
target.scrollIntoView(true);
window.scrollBy(0, adjustment);
}
} else {
// only adjust for fixedTabs when not targeting user-generated content
var fixedTabs = document.querySelector('.js-tabs-affix');
if (fixedTabs) {
adjustment -= fixedTabs.offsetHeight;
window.scrollBy(0, -fixedTabs.offsetHeight);
}
window.scrollBy(0, adjustment);
}
};
@ -134,14 +128,20 @@
return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
};
gl.utils.isMetaClick = function(e) {
// Identify following special clicks
// 1) Cmd + Click on Mac (e.metaKey)
// 2) Ctrl + Click on PC (e.ctrlKey)
// 3) Middle-click or Mouse Wheel Click (e.which is 2)
return e.metaKey || e.ctrlKey || e.which === 2;
};
gl.utils.scrollToElement = function($el) {
var top = $el.offset().top;
gl.navBarHeight = gl.navBarHeight || $('.navbar-gitlab').height();
gl.navLinksHeight = gl.navLinksHeight || $('.nav-links').height();
gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
return $('body, html').animate({
scrollTop: top - (gl.navBarHeight + gl.navLinksHeight + gl.mrTabsHeight)
scrollTop: top - (gl.mrTabsHeight)
}, 200);
};
@ -230,5 +230,16 @@
return upperCaseHeaders;
};
/**
* Transforms a DOMStringMap into a plain object.
*
* @param {DOMStringMap} DOMStringMapObject
* @returns {Object}
*/
w.gl.utils.DOMStringMapToObject = DOMStringMapObject => Object.keys(DOMStringMapObject).reduce((acc, element) => {
acc[element] = DOMStringMapObject[element];
return acc;
}, {});
})(window);
}).call(this);

View File

@ -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);

View File

@ -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);

View File

@ -1,3 +1,5 @@
/* global Pikaday */
/* global dateFormat */
(() => {
// Add datepickers to all `js-access-expiration-date` elements. If those elements are
// children of an element with the `clearable-input` class, and have a sibling
@ -11,21 +13,34 @@
}
const inputs = $(selector);
inputs.datepicker({
dateFormat: 'yy-mm-dd',
minDate: 1,
onSelect: function onSelect() {
$(this).trigger('change');
toggleClearInput.call(this);
},
inputs.each((i, el) => {
const $input = $(el);
const calendar = new Pikaday({
field: $input.get(0),
theme: 'gitlab-theme',
format: 'YYYY-MM-DD',
minDate: new Date(),
onSelect(dateText) {
$input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
$input.trigger('change');
toggleClearInput.call($input);
},
});
$input.data('pikaday', calendar);
});
inputs.next('.js-clear-input').on('click', function clicked(event) {
event.preventDefault();
const input = $(this).closest('.clearable-input').find(selector);
input.datepicker('setDate', null)
.trigger('change');
const calendar = input.data('pikaday');
calendar.setDate(null);
input.trigger('change');
toggleClearInput.call(input);
});

View File

@ -115,8 +115,8 @@ require('./merge_request_tabs');
e.preventDefault();
textarea.val(textarea.data('messageWithDescription'));
$('p.js-with-description-hint').hide();
$('p.js-without-description-hint').show();
$('.js-with-description-hint').hide();
$('.js-without-description-hint').show();
});
$(document).on('click', 'a.js-without-description-link', function(e) {
@ -124,8 +124,8 @@ require('./merge_request_tabs');
e.preventDefault();
textarea.val(textarea.data('messageWithoutDescription'));
$('p.js-with-description-hint').show();
$('p.js-without-description-hint').hide();
$('.js-with-description-hint').show();
$('.js-without-description-hint').hide();
});
};

View File

@ -4,7 +4,7 @@
/* global Flash */
require('./breakpoints');
window.Cookies = require('vendor/js.cookie');
window.Cookies = require('js-cookie');
require('./flash');
/* eslint-disable max-len */
@ -61,7 +61,6 @@ require('./flash');
constructor({ action, setUrl, stubLocation } = {}) {
this.diffsLoaded = false;
this.pipelinesLoaded = false;
this.commitsLoaded = false;
this.fixedLayoutPref = null;
@ -83,12 +82,18 @@ require('./flash');
$(document)
.on('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
.on('click', '.js-show-tab', this.showTab);
$('.merge-request-tabs a[data-toggle="tab"]')
.on('click', this.clickTab);
}
unbindEvents() {
$(document)
.off('shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', this.tabShown)
.off('click', '.js-show-tab', this.showTab);
$('.merge-request-tabs a[data-toggle="tab"]')
.off('click', this.clickTab);
}
showTab(e) {
@ -96,6 +101,14 @@ require('./flash');
this.activateTab($(e.target).data('action'));
}
clickTab(e) {
if (e.target && gl.utils.isMetaClick(e)) {
const targetLink = e.target.getAttribute('href');
e.stopImmediatePropagation();
window.open(targetLink, '_blank');
}
}
tabShown(e) {
const $target = $(e.target);
const action = $target.data('action');
@ -112,14 +125,9 @@ require('./flash');
if (this.diffViewType() === 'parallel') {
this.expandViewContainer();
}
const navBarHeight = $('.navbar-gitlab').outerHeight();
$.scrollTo('.merge-request-details .merge-request-tabs', {
offset: -navBarHeight,
offset: 0,
});
} else if (action === 'pipelines') {
this.loadPipelines($target.attr('href'));
this.expandView();
this.resetViewContainer();
} else {
this.expandView();
this.resetViewContainer();
@ -131,11 +139,7 @@ require('./flash');
scrollToElement(container) {
if (location.hash) {
const offset = 0 - (
$('.navbar-gitlab').outerHeight() +
$('.layout-nav').outerHeight() +
$('.js-tabs-affix').outerHeight()
);
const offset = -$('.js-tabs-affix').outerHeight();
const $el = $(`${container} ${location.hash}:not(.match)`);
if ($el.length) {
$.scrollTo($el[0], { offset });
@ -244,25 +248,6 @@ require('./flash');
});
}
loadPipelines(source) {
if (this.pipelinesLoaded) {
return;
}
this.ajaxGet({
url: `${source}.json`,
success: (data) => {
$('#pipelines').html(data.html);
gl.utils.localTimeAgo($('.js-timeago', '#pipelines'));
this.pipelinesLoaded = true;
this.scrollToElement('#pipelines');
new gl.MiniPipelineGraph({
container: '.js-pipeline-table',
});
},
});
}
// Show or hide the loading spinner
//
// status - Boolean, true to show, false to hide
@ -340,14 +325,12 @@ require('./flash');
if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return;
const $diffTabs = $('#diff-notes-app');
const $fixedNav = $('.navbar-fixed-top');
const $layoutNav = $('.layout-nav');
$tabs.off('affix.bs.affix affix-top.bs.affix')
.affix({
offset: {
top: () => (
$diffTabs.offset().top - $tabs.height() - $fixedNav.height() - $layoutNav.height()
$diffTabs.offset().top - $tabs.height()
),
},
})

View File

@ -51,6 +51,8 @@ require('./smart_interval');
this.getCIStatus(false);
this.retrieveSuccessIcon();
this.initMiniPipelineGraph();
this.ciStatusInterval = new global.SmartInterval({
callback: this.getCIStatus.bind(this, true),
startingInterval: 10000,
@ -66,6 +68,7 @@ require('./smart_interval');
incrementByFactorOf: 15000,
immediateExecution: true,
});
notifyPermissions();
}
@ -151,7 +154,7 @@ require('./smart_interval');
return $.getJSON(this.opts.ci_status_url, (function(_this) {
return function(data) {
var message, status, title;
if (data.status === '') {
if (!data.status) {
return;
}
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
@ -236,17 +239,20 @@ require('./smart_interval');
case "failed":
case "canceled":
case "not_found":
return this.setMergeButtonClass('btn-danger');
this.setMergeButtonClass('btn-danger');
break;
case "running":
return this.setMergeButtonClass('btn-info');
this.setMergeButtonClass('btn-info');
break;
case "success":
case "success_with_warnings":
return this.setMergeButtonClass('btn-create');
this.setMergeButtonClass('btn-create');
}
} else {
$('.ci_widget.ci-error').show();
return this.setMergeButtonClass('btn-danger');
this.setMergeButtonClass('btn-danger');
}
this.initMiniPipelineGraph();
};
MergeRequestWidget.prototype.showCICoverage = function(coverage) {
@ -269,6 +275,12 @@ require('./smart_interval');
$('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/'));
};
MergeRequestWidget.prototype.initMiniPipelineGraph = function() {
new gl.MiniPipelineGraph({
container: '.js-pipeline-inline-mr-widget-graph:visible',
}).bindEvents();
};
return MergeRequestWidget;
})();
})(window.gl || (window.gl = {}));

View File

@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-use-before-define, camelcase, quotes, object-shorthand, no-shadow, no-unused-vars, comma-dangle, no-var, prefer-template, no-underscore-dangle, consistent-return, one-var, one-var-declaration-per-line, default-case, prefer-arrow-callback, max-len */
/* global Flash */
/* global Sortable */
(function() {
this.Milestone = (function() {
@ -8,11 +9,9 @@
type: "PUT",
url: issue_url,
data: data,
success: (function(_this) {
return function(_data) {
return _this.successCallback(_data, li);
};
})(this),
success: function(_data) {
return Milestone.successCallback(_data, li);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
@ -27,11 +26,9 @@
type: "PUT",
url: sort_issues_url,
data: data,
success: (function(_this) {
return function(_data) {
return _this.successCallback(_data);
};
})(this),
success: function(_data) {
return Milestone.successCallback(_data);
},
error: function() {
return new Flash("Issues update failed", 'alert');
},
@ -46,11 +43,9 @@
type: "PUT",
url: sort_mr_url,
data: data,
success: (function(_this) {
return function(_data) {
return _this.successCallback(_data);
};
})(this),
success: function(_data) {
return Milestone.successCallback(_data);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
@ -63,11 +58,9 @@
type: "PUT",
url: merge_request_url,
data: data,
success: (function(_this) {
return function(_data) {
return _this.successCallback(_data, li);
};
})(this),
success: function(_data) {
return Milestone.successCallback(_data, li);
},
error: function(data) {
return new Flash("Issue update failed", 'alert');
},
@ -81,65 +74,30 @@
img_tag = $('<img/>');
img_tag.attr('src', data.assignee.avatar_url);
img_tag.addClass('avatar s16');
$(element).find('.assignee-icon').html(img_tag);
$(element).find('.assignee-icon img').replaceWith(img_tag);
} else {
$(element).find('.assignee-icon').html('');
$(element).find('.assignee-icon').empty();
}
return $(element).effect('highlight');
};
function Milestone() {
var oldMouseStart;
oldMouseStart = $.ui.sortable.prototype._mouseStart;
$.ui.sortable.prototype._mouseStart = function(event, overrideHandle, noActivation) {
this._trigger("beforeStart", event, this._uiHash());
return oldMouseStart.apply(this, [event, overrideHandle, noActivation]);
};
this.bindIssuesSorting();
this.bindMergeRequestSorting();
this.bindTabsSwitching();
}
Milestone.prototype.bindIssuesSorting = function() {
return $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable({
connectWith: ".issues-sortable-list",
dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)",
beforeStart: function(event, ui) {
return $(".issues-sortable-list").css("min-height", ui.item.outerHeight());
},
stop: function(event, ui) {
return $(".issues-sortable-list").css("min-height", "0px");
},
update: function(event, ui) {
var data;
// Prevents sorting from container which element has been removed.
if ($(this).find(ui.item).length > 0) {
data = $(this).sortable("serialize");
return Milestone.sortIssues(data);
}
},
receive: function(event, ui) {
var data, issue_id, issue_url, new_state;
new_state = $(this).data('state');
issue_id = ui.item.data('iid');
issue_url = ui.item.data('url');
data = (function() {
switch (new_state) {
case 'ongoing':
return "issue[assignee_id]=" + gon.current_user_id;
case 'unassigned':
return "issue[assignee_id]=";
case 'closed':
return "issue[state_event]=close";
}
})();
if ($(ui.sender).data('state') === "closed") {
data += "&issue[state_event]=reopen";
}
return Milestone.updateIssue(ui.item, issue_url, data);
}
}).disableSelection();
$('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
this.createSortable(el, {
group: 'issue-list',
listEls: $('.issues-sortable-list'),
fieldName: 'issue',
sortCallback: Milestone.sortIssues,
updateCallback: Milestone.updateIssue,
});
}.bind(this));
};
Milestone.prototype.bindTabsSwitching = function() {
@ -154,42 +112,62 @@
};
Milestone.prototype.bindMergeRequestSorting = function() {
return $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable({
connectWith: ".merge_requests-sortable-list",
dropOnEmpty: true,
items: "li:not(.ui-sort-disabled)",
beforeStart: function(event, ui) {
return $(".merge_requests-sortable-list").css("min-height", ui.item.outerHeight());
$("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
this.createSortable(el, {
group: 'merge-request-list',
listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
fieldName: 'merge_request',
sortCallback: Milestone.sortMergeRequests,
updateCallback: Milestone.updateMergeRequest,
});
}.bind(this));
};
Milestone.prototype.createSortable = function(el, opts) {
return Sortable.create(el, {
group: opts.group,
filter: '.is-disabled',
forceFallback: true,
onStart: function(e) {
opts.listEls.css('min-height', e.item.offsetHeight);
},
stop: function(event, ui) {
return $(".merge_requests-sortable-list").css("min-height", "0px");
onEnd: function () {
opts.listEls.css("min-height", "0px");
},
update: function(event, ui) {
var data;
data = $(this).sortable("serialize");
return Milestone.sortMergeRequests(data);
onUpdate: function(e) {
var ids = this.toArray(),
data;
if (ids.length) {
data = ids.map(function(id) {
return 'sortable_' + opts.fieldName + '[]=' + id;
}).join('&');
opts.sortCallback(data);
}
},
receive: function(event, ui) {
var data, merge_request_id, merge_request_url, new_state;
new_state = $(this).data('state');
merge_request_id = ui.item.data('iid');
merge_request_url = ui.item.data('url');
onAdd: function (e) {
var data, issuableId, issuableUrl, newState;
newState = e.to.dataset.state;
issuableUrl = e.item.dataset.url;
data = (function() {
switch (new_state) {
switch (newState) {
case 'ongoing':
return "merge_request[assignee_id]=" + gon.current_user_id;
return opts.fieldName + '[assignee_id]=' + gon.current_user_id;
case 'unassigned':
return "merge_request[assignee_id]=";
return opts.fieldName + '[assignee_id]=';
case 'closed':
return "merge_request[state_event]=close";
return opts.fieldName + '[state_event]=close';
}
})();
if ($(ui.sender).data('state') === "closed") {
data += "&merge_request[state_event]=reopen";
if (e.from.dataset.state === 'closed') {
data += '&' + opts.fieldName + '[state_event]=reopen';
}
return Milestone.updateMergeRequest(ui.item, merge_request_url, data);
opts.updateCallback(e.item, issuableUrl, data);
this.options.onUpdate.call(this, e);
}
}).disableSelection();
});
};
return Milestone;

View File

@ -21,8 +21,6 @@
this.container = opts.container || '';
this.dropdownListSelector = '.js-builds-dropdown-container';
this.getBuildsList = this.getBuildsList.bind(this);
this.bindEvents();
}
/**
@ -30,7 +28,7 @@
* All dropdown events are fired at the .dropdown-menu's parent element.
*/
bindEvents() {
$(this.container).on('shown.bs.dropdown', this.getBuildsList);
$(document).on('shown.bs.dropdown', this.container, this.getBuildsList);
}
/**

View File

@ -455,7 +455,7 @@ require('vendor/task_list');
var mergeRequestId = $form.data('noteable-iid');
if (ResolveService != null) {
ResolveService.toggleResolveForDiscussion(projectPath, mergeRequestId, discussionId);
ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
}
}

View File

@ -25,6 +25,7 @@
bindEvents() {
$('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm);
$('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
$('.update-username').on('ajax:before', this.beforeUpdateUsername);
$('.update-username').on('ajax:complete', this.afterUpdateUsername);
$('.update-notifications').on('ajax:success', this.onUpdateNotifs);

View File

@ -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);

View File

@ -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;

View File

@ -1,14 +1,12 @@
/* eslint-disable arrow-parens, class-methods-use-this, no-param-reassign */
/* global Cookies */
((global) => {
let singleton;
(() => {
const pinnedStateCookie = 'pin_nav';
const sidebarBreakpoint = 1024;
const pageSelector = '.page-with-sidebar';
const navbarSelector = '.navbar-fixed-top';
const navbarSelector = '.navbar-gitlab';
const sidebarWrapperSelector = '.sidebar-wrapper';
const sidebarContentSelector = '.nav-sidebar';
@ -23,11 +21,12 @@
class Sidebar {
constructor() {
if (!singleton) {
singleton = this;
singleton.init();
if (!Sidebar.singleton) {
Sidebar.singleton = this;
Sidebar.singleton.init();
}
return singleton;
return Sidebar.singleton;
}
init() {
@ -36,13 +35,16 @@
window.innerWidth >= sidebarBreakpoint &&
$(pageSelector).hasClass(expandedPageClass)
);
$(window).on('resize', () => this.setSidebarHeight());
$(document)
.on('click', sidebarToggleSelector, () => this.toggleSidebar())
.on('click', pinnedToggleSelector, () => this.togglePinnedState())
.on('click', 'html, body', (e) => this.handleClickEvent(e))
.on('click', 'html, body, a, button', (e) => this.handleClickEvent(e))
.on('DOMContentLoaded', () => this.renderState())
.on('scroll', () => this.setSidebarHeight())
.on('todo:toggle', (e, count) => this.updateTodoCount(count));
this.renderState();
this.setSidebarHeight();
}
handleClickEvent(e) {
@ -65,6 +67,16 @@
this.renderState();
}
setSidebarHeight() {
const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
const diff = $navHeight - $('body').scrollTop();
if (diff > 0) {
$('.js-right-sidebar').outerHeight($(window).height() - diff);
} else {
$('.js-right-sidebar').outerHeight('100%');
}
}
togglePinnedState() {
this.isPinned = !this.isPinned;
if (!this.isPinned) {
@ -88,10 +100,12 @@
$pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState);
if (this.isExpanded) {
setTimeout(() => $(sidebarContentSelector).niceScroll().updateScrollBar(), 200);
const sidebarContent = $(sidebarContentSelector);
setTimeout(() => { sidebarContent.niceScroll().updateScrollBar(); }, 200);
}
}
}
global.Sidebar = Sidebar;
})(window.gl || (window.gl = {}));
window.gl = window.gl || {};
gl.Sidebar = Sidebar;
})();

View File

@ -33,13 +33,13 @@
this.$toggleIcon.addClass('fa-caret-down');
}
$('.file-title, .click-to-expand', this.file).on('click', (function (e) {
$('.js-file-title, .click-to-expand', this.file).on('click', (function (e) {
this.toggleDiff($(e.target));
}).bind(this));
}
SingleFileDiff.prototype.toggleDiff = function($target, cb) {
if (!$target.hasClass('file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
if (!$target.hasClass('js-file-title') && !$target.hasClass('click-to-expand') && !$target.hasClass('diff-toggle-caret')) return;
this.isOpen = !this.isOpen;
if (!this.isOpen && !this.hasError) {
this.content.hide();

View File

@ -50,14 +50,15 @@
return (
children[target.index] ||
children[target.index === 'first' ? 0 : -1] ||
children[target.index === 'last' ? children.length - 1 : -1]
children[target.index === 'last' ? children.length - 1 : -1] ||
el
);
}
function getRect(el) {
var rect = el.getBoundingClientRect();
var width = rect.right - rect.left;
var height = rect.bottom - rect.top;
var height = rect.bottom - rect.top + 10;
return {
x: rect.left,

View File

@ -146,14 +146,26 @@
}
goToTodoUrl(e) {
const todoLink = $(this).data('url');
const todoLink = this.dataset.url;
let targetLink = e.target.getAttribute('href');
if (e.target.tagName === 'IMG') { // See if clicked target was Avatar
targetLink = e.target.parentElement.getAttribute('href'); // Parent of Avatar is link
}
if (!todoLink) {
return;
}
// Allow Meta-Click or Mouse3-click to open in a new tab
if (e.metaKey || e.which === 2) {
if (gl.utils.isMetaClick(e)) {
e.preventDefault();
return window.open(todoLink, '_blank');
// Meta-Click on username leads to different URL than todoLink.
// Turbolinks can resolve that URL, but window.open requires URL manually.
if (targetLink !== todoLink) {
return window.open(targetLink, '_blank');
} else {
return window.open(todoLink, '_blank');
}
} else {
return gl.utils.visitUrl(todoLink);
}

View File

@ -1,41 +1,36 @@
/* eslint-disable no-param-reassign */
/* global Vue, VueResource, gl */
window.Vue = require('vue');
window.Vue.use(require('vue-resource'));
require('../vue_common_component/commit');
require('../vue_pagination/index');
require('../boards/vue_resource_interceptor');
require('./status');
require('./store');
require('./pipeline_url');
require('./stage');
require('./stages');
require('./pipeline_actions');
require('./time_ago');
require('../lib/utils/common_utils');
require('../vue_shared/vue_resource_interceptor');
require('./pipelines');
(() => {
const project = document.querySelector('.pipelines');
const entry = document.querySelector('.vue-pipelines-index');
const svgs = document.querySelector('.pipeline-svgs');
$(() => new Vue({
el: document.querySelector('.vue-pipelines-index'),
if (!entry) return null;
return new Vue({
el: entry,
data: {
data() {
const project = document.querySelector('.pipelines');
const svgs = document.querySelector('.pipeline-svgs').dataset;
// Transform svgs DOMStringMap to a plain Object.
const svgsObject = gl.utils.DOMStringMapToObject(svgs);
return {
scope: project.dataset.url,
store: new gl.PipelineStore(),
svgs: svgs.dataset,
},
components: {
'vue-pipelines': gl.VuePipelines,
},
template: `
<vue-pipelines
:scope='scope'
:store='store'
:svgs='svgs'
>
</vue-pipelines>
`,
});
})();
svgs: svgsObject,
};
},
components: {
'vue-pipelines': gl.VuePipelines,
},
template: `
<vue-pipelines
:scope='scope'
:store='store'
:svgs='svgs'
>
</vue-pipelines>
`,
}));

View File

@ -50,9 +50,9 @@
<button
v-if='artifacts'
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
data-toggle="dropdown"
title="Artifacts"
data-placement="top"
data-toggle="dropdown"
aria-label="Artifacts"
>
<i class="fa fa-download" aria-hidden="true"></i>
@ -81,8 +81,7 @@
data-placement="top"
data-toggle="dropdown"
:href='pipeline.retry_path'
aria-label="Retry"
>
aria-label="Retry">
<i class="fa fa-repeat" aria-hidden="true"></i>
</a>
<a
@ -94,8 +93,7 @@
data-placement="top"
data-toggle="dropdown"
:href='pipeline.cancel_path'
aria-label="Cancel"
>
aria-label="Cancel">
<i class="fa fa-remove" aria-hidden="true"></i>
</a>
</div>

View File

@ -1,19 +1,19 @@
/* global Vue, gl */
/* eslint-disable no-param-reassign */
window.Vue = require('vue');
require('../vue_shared/components/table_pagination');
require('./store');
require('../vue_shared/components/pipelines_table');
((gl) => {
gl.VuePipelines = Vue.extend({
components: {
runningPipeline: gl.VueRunningPipeline,
pipelineActions: gl.VuePipelineActions,
stages: gl.VueStages,
commit: gl.CommitComponent,
pipelineUrl: gl.VuePipelineUrl,
pipelineHead: gl.VuePipelineHead,
glPagination: gl.VueGlPagination,
statusScope: gl.VueStatusScope,
timeAgo: gl.VueTimeAgo,
'gl-pagination': gl.VueGlPagination,
'pipelines-table-component': gl.pipelines.PipelinesTableComponent,
},
data() {
return {
pipelines: [],
@ -38,87 +38,29 @@
change(pagenum, apiScope) {
gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`);
},
author(pipeline) {
if (!pipeline.commit) return { avatar_url: '', web_url: '', username: '' };
if (pipeline.commit.author) return pipeline.commit.author;
return {
avatar_url: pipeline.commit.author_gravatar_url,
web_url: `mailto:${pipeline.commit.author_email}`,
username: pipeline.commit.author_name,
};
},
ref(pipeline) {
const { ref } = pipeline;
return { name: ref.name, tag: ref.tag, ref_url: ref.path };
},
commitTitle(pipeline) {
return pipeline.commit ? pipeline.commit.title : '';
},
commitSha(pipeline) {
return pipeline.commit ? pipeline.commit.short_id : '';
},
commitUrl(pipeline) {
return pipeline.commit ? pipeline.commit.commit_path : '';
},
match(string) {
return string.replace(/_([a-z])/g, (m, w) => w.toUpperCase());
},
},
template: `
<div>
<div class="pipelines realtime-loading" v-if='pipelines.length < 1'>
<i class="fa fa-spinner fa-spin"></i>
</div>
<div class="table-holder" v-if='pipelines.length'>
<table class="table ci-table">
<thead>
<tr>
<th class="pipeline-status">Status</th>
<th class="pipeline-info">Pipeline</th>
<th class="pipeline-commit">Commit</th>
<th class="pipeline-stages">Stages</th>
<th class="pipeline-date"></th>
<th class="pipeline-actions hidden-xs"></th>
</tr>
</thead>
<tbody>
<tr class="commit" v-for='pipeline in pipelines'>
<status-scope
:pipeline='pipeline'
:match='match'
:svgs='svgs'
>
</status-scope>
<pipeline-url :pipeline='pipeline'></pipeline-url>
<td>
<commit
:commit-icon-svg='svgs.commitIconSvg'
:author='author(pipeline)'
:tag="pipeline.ref.tag"
:title='commitTitle(pipeline)'
:commit-ref='ref(pipeline)'
:short-sha='commitSha(pipeline)'
:commit-url='commitUrl(pipeline)'
>
</commit>
</td>
<stages
:pipeline='pipeline'
:svgs='svgs'
:match='match'
>
</stages>
<time-ago :pipeline='pipeline' :svgs='svgs'></time-ago>
<pipeline-actions :pipeline='pipeline' :svgs='svgs'></pipeline-actions>
</tr>
</tbody>
</table>
</div>
<div class="pipelines realtime-loading" v-if='pageRequest'>
<i class="fa fa-spinner fa-spin"></i>
</div>
<div class="blank-state blank-state-no-icon"
v-if="!pageRequest && pipelines.length === 0">
<h2 class="blank-state-title js-blank-state-title">
No pipelines to show
</h2>
</div>
<div class="table-holder" v-if='!pageRequest && pipelines.length'>
<pipelines-table-component
:pipelines='pipelines'
:svgs='svgs'>
</pipelines-table-component>
</div>
<gl-pagination
v-if='pageInfo.total > pageInfo.perPage'
v-if='!pageRequest && pipelines.length && pageInfo.total > pageInfo.perPage'
:pagenum='pagenum'
:change='change'
:count='count.all'

View File

@ -15,7 +15,7 @@
required: true,
},
svgs: {
type: DOMStringMap,
type: Object,
required: true,
},
match: {

View File

@ -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 = {}));

View File

@ -20,6 +20,7 @@ require('../vue_realtime_listener');
gl.PipelineStore = class {
fetchDataLoop(Vue, pageNum, url, apiScope) {
this.pageRequest = true;
const updatePipelineNums = (count) => {
const { all } = count;
const running = count.running_or_pending;
@ -41,16 +42,18 @@ require('../vue_realtime_listener');
this.pageRequest = false;
}, () => {
this.pageRequest = false;
return new Flash('Something went wrong on our end.');
return new Flash('An error occurred while fetching the pipelines, please reload the page again.');
});
goFetch();
const startTimeLoops = () => {
this.timeLoopInterval = setInterval(() => {
this.$children
.filter(e => e.$options._componentTag === 'time-ago')
.forEach(e => e.changeTime());
this.$children[0].$children.reduce((acc, component) => {
const timeAgoComponent = component.$children.filter(el => el.$options._componentTag === 'time-ago')[0];
acc.push(timeAgoComponent);
return acc;
}, []).forEach(e => e.changeTime());
}, 10000);
};

View File

@ -1,6 +1,9 @@
/* global Vue, gl */
/* eslint-disable no-param-reassign */
window.Vue = require('vue');
require('../lib/utils/datetime_utility');
((gl) => {
gl.VueTimeAgo = Vue.extend({
data() {

View File

@ -14,5 +14,16 @@
window.addEventListener('focus', startIntervals);
window.addEventListener('blur', removeIntervals);
document.addEventListener('beforeunload', removeAll);
// add removeAll methods to stack
const stack = gl.VueRealtimeListener.reset;
gl.VueRealtimeListener.reset = () => {
gl.VueRealtimeListener.reset = stack;
removeAll();
stack();
};
};
// remove all event listeners and intervals
gl.VueRealtimeListener.reset = () => undefined; // noop
})(window.gl || (window.gl = {}));

View File

@ -1,7 +1,5 @@
/* global Vue */
window.Vue = require('vue');
(() => {
window.gl = window.gl || {};

View File

@ -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>
`,
});
})();

View File

@ -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>
`,
});
})();

View File

@ -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();
});

View File

@ -2,7 +2,6 @@
* This is a manifest file that'll automatically include all the stylesheets available in this directory
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
*= require jquery-ui/datepicker
*= require jquery-ui/autocomplete
*= require jquery.atwho
*= require select2
@ -19,6 +18,8 @@
* directory.
*/
@import "../../../node_modules/pikaday/scss/pikaday";
/*
* GitLab UI framework
*/

View File

@ -128,8 +128,7 @@
.note-action-button .link-highlight,
.toolbar-btn,
.dropdown-toggle-caret,
.fa:not(.fa-bell) {
.dropdown-toggle-caret {
@include transition(color);
}

View File

@ -28,6 +28,8 @@
.avatar {
@extend .avatar-circle;
@include transition-property(none);
width: 40px;
height: 40px;
padding: 0;

View File

@ -9,6 +9,8 @@
}
.user-calendar-activities {
direction: ltr;
.str-truncated {
max-width: 70%;
}
@ -43,3 +45,56 @@
float: right;
font-size: 12px;
}
.pika-single.gitlab-theme {
.pika-label {
color: $gl-text-color-secondary;
font-size: 14px;
font-weight: normal;
}
th {
padding: 2px 0;
color: $note-disabled-comment-color;
font-weight: normal;
text-transform: lowercase;
border-top: 1px solid $calendar-border-color;
}
abbr {
cursor: default;
}
td {
border: 1px solid $calendar-border-color;
&:first-child {
border-left: 0;
}
&:last-child {
border-right: 0;
}
}
.pika-day {
border-radius: 0;
background-color: $white-light;
text-align: center;
}
.is-today {
.pika-day {
color: inherit;
font-weight: normal;
}
}
.is-selected .pika-day,
.pika-day:hover,
.is-today .pika-day:hover {
background: $gl-primary;
color: $white-light;
box-shadow: none;
}
}

View File

@ -253,6 +253,8 @@ li.note {
.progress {
margin-bottom: 0;
margin-top: 4px;
box-shadow: none;
background-color: $border-gray-light;
}
}

View File

@ -125,7 +125,6 @@
top: 100%;
left: 0;
z-index: 9;
max-width: 280px;
min-width: 240px;
margin-top: 2px;
margin-bottom: 0;
@ -137,6 +136,10 @@
border-radius: $border-radius-base;
box-shadow: 0 2px 4px $dropdown-shadow-color;
.filtered-search-input-container & {
max-width: 280px;
}
&.is-loading {
.dropdown-content {
display: none;
@ -502,119 +505,16 @@
max-height: 230px;
}
.ui-widget {
table {
margin: 0;
}
&.ui-datepicker-inline {
padding: 0 10px;
border: 0;
width: 100%;
}
.ui-datepicker-header {
padding: 0 8px 10px;
border: 0;
.ui-icon {
background: none;
font-size: 20px;
text-indent: 0;
&::before {
display: block;
position: relative;
top: -2px;
color: $dropdown-title-btn-color;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
}
.ui-datepicker-calendar {
.ui-state-hover,
.ui-state-active {
color: $white-light;
border: 0;
}
}
.ui-datepicker-prev,
.ui-datepicker-next {
top: 0;
height: 15px;
cursor: pointer;
&:hover {
background-color: transparent;
border: 0;
.ui-icon::before {
color: $md-link-color;
}
}
}
.ui-datepicker-prev {
left: 0;
.ui-icon::before {
content: '\f104';
text-align: left;
}
}
.ui-datepicker-next {
right: 0;
.ui-icon::before {
content: '\f105';
text-align: right;
}
}
td {
padding: 0;
border: 1px solid $calendar-border-color;
&:first-child {
border-left: 0;
}
&:last-child {
border-right: 0;
}
a {
line-height: 17px;
border: 0;
border-radius: 0;
}
}
.ui-datepicker-title {
color: $gl-text-color;
font-size: 14px;
line-height: 1;
font-weight: normal;
}
.pika-single {
position: relative!important;
top: 0!important;
border: 0;
box-shadow: none;
}
th {
padding: 2px 0;
color: $note-disabled-comment-color;
font-weight: normal;
text-transform: lowercase;
border-top: 1px solid $calendar-border-color;
}
.ui-datepicker-unselectable {
background-color: $gray-light;
.pika-lendar {
margin-top: -5px;
margin-bottom: 0;
}
}

View File

@ -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;
}
}
}

View File

@ -222,6 +222,10 @@ header {
float: right;
border-top: none;
@media (min-width: $screen-md-min) {
padding: 0;
}
@media (max-width: $screen-xs-max) {
float: none;
}
@ -272,7 +276,7 @@ header {
.header-user {
.dropdown-menu-nav {
width: 140px;
min-width: 140px;
margin-top: -5px;
}
}

View File

@ -2,42 +2,6 @@
font-family: $regular_font;
font-size: $font-size-base;
&.ui-datepicker,
&.ui-datepicker-inline {
border: 1px solid $jq-ui-border;
padding: 10px;
width: 270px;
.ui-datepicker-header {
background: $white-light;
border-color: $jq-ui-border;
.ui-datepicker-prev,
.ui-datepicker-next {
top: 4px;
}
.ui-datepicker-prev {
left: 2px;
}
.ui-datepicker-next {
right: 2px;
}
.ui-state-hover {
background: transparent;
border: 0;
cursor: pointer;
}
}
.ui-datepicker-calendar td a {
padding: 5px;
text-align: center;
}
}
&.ui-autocomplete {
border-color: $jq-ui-border;
padding: 0;
@ -59,25 +23,4 @@
border: 0;
background: transparent;
}
.ui-datepicker-calendar {
.ui-state-active,
.ui-state-hover,
.ui-state-focus {
border: 1px solid $gl-primary;
background: $gl-primary;
color: $white-light;
}
}
}
.ui-sortable-handle {
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
&:active {
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
}

View File

@ -307,3 +307,7 @@ ul.controls {
}
}
}
ul.indent-list {
padding: 10px 0 0 30px;
}

View File

@ -283,10 +283,7 @@
}
.layout-nav {
position: fixed;
top: $header-height;
width: 100%;
z-index: 11;
background: $gray-light;
border-bottom: 1px solid $border-color;
transition: padding $sidebar-transition-duration;
@ -419,15 +416,20 @@
}
.page-with-layout-nav {
margin-top: $header-height + 2;
.right-sidebar {
top: ($header-height * 2) + 2;
}
.build-sidebar {
top: ($header-height * 3) + 3;
&.affix {
top: 0;
}
}
}
.activities {
.nav-block {
border-bottom: 1px solid $border-color;

View File

@ -6,8 +6,22 @@
.pagination {
padding: 0;
a {
cursor: pointer;
}
.separator,
.separator:hover {
a {
cursor: default;
background-color: $gray-light;
padding: $gl-vert-padding;
}
}
}
.gap,
.gap:hover {
background-color: $gray-light;

View File

@ -1,5 +1,5 @@
.page-with-sidebar {
padding: $header-height 0 25px;
padding-bottom: 25px;
transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
@ -208,7 +208,9 @@ header.header-sidebar-pinned {
padding-right: 0;
@media (min-width: $screen-sm-min) {
padding-right: $sidebar_collapsed_width;
.content-wrapper {
padding-right: $sidebar_collapsed_width;
}
.merge-request-tabs-holder.affix {
right: $sidebar_collapsed_width;
@ -234,7 +236,9 @@ header.header-sidebar-pinned {
}
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
.content-wrapper {
padding-right: $gutter_width;
}
&:not(.with-overlay) .merge-request-tabs-holder.affix {
right: $gutter_width;
@ -252,4 +256,9 @@ header.header-sidebar-pinned {
.right-sidebar {
border-left: 1px solid $border-color;
&.affix {
position: fixed;
top: 0;
}
}

View File

@ -138,6 +138,13 @@ pre {
margin: 0;
}
blockquote {
color: $gl-grayish-blue;
padding: 0 0 0 15px;
margin: 0;
border-left: 3px solid $white-dark;
}
span.highlight_word {
background-color: $highlighted-highlight-word !important;
}

View File

@ -298,12 +298,8 @@
.issue-boards-sidebar {
&.right-sidebar {
top: 153px;
top: 0;
bottom: 0;
@media (min-width: $screen-sm-min) {
top: 220px;
}
}
.issuable-sidebar-header {

View File

@ -159,7 +159,6 @@
.commit-row-description {
font-size: 14px;
border-left: 1px solid $white-normal;
padding: 10px 15px;
margin: 10px 0;
background: $gray-light;

View File

@ -284,7 +284,11 @@
.events-description {
line-height: 65px;
padding-left: $gl-padding;
padding: 0 $gl-padding;
}
.events-info {
color: $gl-text-color-secondary;
}
}

View File

@ -34,9 +34,14 @@
}
}
.file-title {
.file-title,
.file-title-flex-parent {
cursor: pointer;
a:hover {
text-decoration: none;
}
&:hover {
background-color: $gray-normal;
}

View File

@ -41,7 +41,6 @@
word-wrap: break-word;
.md {
color: $gl-grayish-blue;
font-size: $gl-font-size;
.label {

View File

@ -189,11 +189,10 @@
}
.right-sidebar {
position: fixed;
position: absolute;
top: $header-height;
bottom: 0;
right: 0;
z-index: 10;
transition: width .3s;
background: $gray-light;
padding: 10px 20px;
@ -461,8 +460,19 @@
.issuable-list {
li {
.issue-box {
display: -webkit-flex;
display: flex;
}
.issue-info-container {
-webkit-flex: 1;
flex: 1;
padding-right: $gl-padding;
}
.issue-check {
float: left;
padding-right: $gl-padding;
margin-bottom: 10px;
min-width: 15px;

View File

@ -1,6 +1,6 @@
.issues-list {
.issue {
padding: 10px $gl-padding;
padding: 10px 0 10px $gl-padding;
position: relative;
.title {
@ -148,3 +148,7 @@ ul.related-merge-requests > li {
border: 1px solid $border-gray-normal;
}
}
.recaptcha {
margin-bottom: 30px;
}

View File

@ -116,6 +116,22 @@
}
.manage-labels-list {
> li:not(.empty-message) {
background-color: $white-light;
cursor: move;
cursor: -webkit-grab;
cursor: -moz-grab;
&:active {
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
&.sortable-ghost {
opacity: 0.3;
}
}
.btn-action {
color: $gl-text-color;
@ -259,3 +275,8 @@
}
}
}
.label-link {
display: inline-block;
vertical-align: text-top;
}

View File

@ -80,19 +80,28 @@
.ci_widget {
border-bottom: 1px solid $well-inner-border;
color: $gl-text-color;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
i,
svg {
margin-right: 8px;
}
svg {
margin-right: 4px;
position: relative;
top: 1px;
overflow: visible;
}
&.ci-success_with_warnings {
& > span {
padding-right: 4px;
}
i {
color: $gl-warning;
}
@media (max-width: $screen-xs-max) {
flex-wrap: wrap;
}
}
@ -102,6 +111,43 @@
padding: $gl-padding;
}
.mr-widget-pipeline-graph {
flex-shrink: 0;
.dropdown-menu {
margin-top: 11px;
}
.ci-action-icon-wrapper {
line-height: 16px;
}
@media (min-width: $screen-sm-min) {
.stage-cell {
padding: 0 4px;
}
}
@media (max-width: $screen-xs-max) {
order: 1;
margin-top: $gl-padding-top;
border-radius: 3px;
background-color: $white-light;
border: 1px solid $gray-darker;
width: 100%;
text-align: center;
.dropdown-menu {
margin-left: -97.5px;
}
.arrow-up::before,
.arrow-up::after, {
margin-left: 97.5px;
}
}
}
.normal {
color: $gl-text-color;
}
@ -223,8 +269,15 @@
.mr-list {
.merge-request {
padding: 10px 15px;
padding: 10px 0 10px 15px;
position: relative;
display: -webkit-flex;
display: flex;
.issue-info-container {
-webkit-flex: 1;
flex: 1;
}
.merge-request-title {
margin-bottom: 2px;
@ -430,7 +483,7 @@
background-color: $white-light;
&.affix {
top: 100px;
top: 0;
left: 0;
z-index: 10;
transition: right .15s;

Some files were not shown because too many files have changed in this diff Show More