Merge remote-tracking branch 'upstream/master' into no-ivar-in-modules
* upstream/master: (671 commits) Make rubocop happy Use guard clause Improve language Prettify Use temp branch Pass info about who started the job and which job triggered it Docs: add indexes for monitoring and performance monitoring clearer-documentation-on-inline-diffs Add docs for commit diff discussion in merge requests sorting for tags api Clear BatchLoader after each spec to prevent holding onto records longer than necessary Include project in BatchLoader key to prevent returning blobs for the wrong project moved lfs_blob_ids method into ExtractsPath module Converted JS modules into exported modules spec fixes Bump gitlab-shell version to 5.10.3 Clear caches before updating MR diffs Use new Ruby version 2.4 in GitLab QA images moved lfs blob fetch from extractspath file Update GitLab QA dependencies ...
|
|
@ -586,6 +586,7 @@ codequality:
|
||||||
paths: [codeclimate.json]
|
paths: [codeclimate.json]
|
||||||
|
|
||||||
qa:internal:
|
qa:internal:
|
||||||
|
<<: *except-docs
|
||||||
stage: test
|
stage: test
|
||||||
variables:
|
variables:
|
||||||
SETUP_DB: "false"
|
SETUP_DB: "false"
|
||||||
|
|
|
||||||
52
CHANGELOG.md
|
|
@ -2,6 +2,36 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
entry.
|
||||||
|
|
||||||
|
## 10.2.4 (2017-12-07)
|
||||||
|
|
||||||
|
### Security (5 changes)
|
||||||
|
|
||||||
|
- Fix e-mail address disclosure through member search fields
|
||||||
|
- Prevent creating issues through API when user does not have permissions
|
||||||
|
- Prevent an information disclosure in the Groups API
|
||||||
|
- Fix user without access to private Wiki being able to see it on the project page
|
||||||
|
- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
|
||||||
|
|
||||||
|
|
||||||
|
## 10.2.3 (2017-11-30)
|
||||||
|
|
||||||
|
### Fixed (7 changes)
|
||||||
|
|
||||||
|
- Fix hashed storage for Import/Export uploads. !15482
|
||||||
|
- Ensure that rake gitlab:cleanup:repos task does not mess with hashed repositories. !15520
|
||||||
|
- Ensure that rake gitlab:cleanup:dirs task does not mess with hashed repositories. !15600
|
||||||
|
- Fix WIP system note not being created.
|
||||||
|
- Fix link text from group context.
|
||||||
|
- Fix defaults for MR states and merge statuses.
|
||||||
|
- Fix pulling and pushing using a personal access token with the sudo scope.
|
||||||
|
|
||||||
|
### Performance (3 changes)
|
||||||
|
|
||||||
|
- Drastically improve project search performance by no longer searching namespace name.
|
||||||
|
- Reuse authors when rendering event Atom feeds.
|
||||||
|
- Optimise StuckCiJobsWorker using cheap SQL query outside, and expensive inside.
|
||||||
|
|
||||||
|
|
||||||
## 10.2.2 (2017-11-23)
|
## 10.2.2 (2017-11-23)
|
||||||
|
|
||||||
### Fixed (5 changes)
|
### Fixed (5 changes)
|
||||||
|
|
@ -218,6 +248,17 @@ entry.
|
||||||
- Add Gitaly metrics to the performance bar.
|
- Add Gitaly metrics to the performance bar.
|
||||||
|
|
||||||
|
|
||||||
|
## 10.1.5 (2017-12-07)
|
||||||
|
|
||||||
|
### Security (5 changes)
|
||||||
|
|
||||||
|
- Fix e-mail address disclosure through member search fields
|
||||||
|
- Prevent creating issues through API when user does not have permissions
|
||||||
|
- Prevent an information disclosure in the Groups API
|
||||||
|
- Fix user without access to private Wiki being able to see it on the project page
|
||||||
|
- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
|
||||||
|
|
||||||
|
|
||||||
## 10.1.4 (2017-11-14)
|
## 10.1.4 (2017-11-14)
|
||||||
|
|
||||||
### Fixed (4 changes)
|
### Fixed (4 changes)
|
||||||
|
|
@ -466,6 +507,17 @@ entry.
|
||||||
- creation of keys moved to services. !13331 (haseebeqx)
|
- creation of keys moved to services. !13331 (haseebeqx)
|
||||||
- Add username as GL_USERNAME in hooks.
|
- Add username as GL_USERNAME in hooks.
|
||||||
|
|
||||||
|
## 10.0.7 (2017-12-07)
|
||||||
|
|
||||||
|
### Security (5 changes)
|
||||||
|
|
||||||
|
- Fix e-mail address disclosure through member search fields
|
||||||
|
- Prevent creating issues through API when user does not have permissions
|
||||||
|
- Prevent an information disclosure in the Groups API
|
||||||
|
- Fix user without access to private Wiki being able to see it on the project page
|
||||||
|
- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
|
||||||
|
|
||||||
|
|
||||||
## 10.0.5 (2017-11-03)
|
## 10.0.5 (2017-11-03)
|
||||||
|
|
||||||
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
|
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
|
||||||
|
|
|
||||||
|
|
@ -598,6 +598,7 @@ merge request:
|
||||||
present time and never use past tense (has been/was). For example instead
|
present time and never use past tense (has been/was). For example instead
|
||||||
of _prohibited this user from being saved due to the following errors:_ the
|
of _prohibited this user from being saved due to the following errors:_ the
|
||||||
text should be _sorry, we could not create your account because:_
|
text should be _sorry, we could not create your account because:_
|
||||||
|
1. Code should be written in [US English][us-english]
|
||||||
|
|
||||||
This is also the style used by linting tools such as
|
This is also the style used by linting tools such as
|
||||||
[RuboCop](https://github.com/bbatsov/rubocop),
|
[RuboCop](https://github.com/bbatsov/rubocop),
|
||||||
|
|
@ -663,6 +664,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
|
||||||
[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
|
[GitLab Inc engineering workflow]: https://about.gitlab.com/handbook/engineering/workflow/#labelling-issues
|
||||||
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
|
[polling-etag]: https://docs.gitlab.com/ce/development/polling.html
|
||||||
[testing]: doc/development/testing_guide/index.md
|
[testing]: doc/development/testing_guide/index.md
|
||||||
|
[us-english]: https://en.wikipedia.org/wiki/American_English
|
||||||
|
|
||||||
[^1]: Please note that specs other than JavaScript specs are considered backend
|
[^1]: Please note that specs other than JavaScript specs are considered backend
|
||||||
code.
|
code.
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
0.55.0
|
0.60.0
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
5.9.4
|
5.10.3
|
||||||
|
|
|
||||||
18
Gemfile
|
|
@ -1,6 +1,6 @@
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'rails', '4.2.8'
|
gem 'rails', '4.2.10'
|
||||||
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
|
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
|
||||||
|
|
||||||
# Responders respond_to and respond_with
|
# Responders respond_to and respond_with
|
||||||
|
|
@ -111,7 +111,7 @@ gem 'google-api-client', '~> 0.13.6'
|
||||||
gem 'unf', '~> 0.1.4'
|
gem 'unf', '~> 0.1.4'
|
||||||
|
|
||||||
# Seed data
|
# Seed data
|
||||||
gem 'seed-fu', '~> 2.3.7'
|
gem 'seed-fu', '2.3.6' # Upgrade to > 2.3.7 once https://github.com/mbleigh/seed-fu/issues/123 is solved
|
||||||
|
|
||||||
# Markdown and HTML processing
|
# Markdown and HTML processing
|
||||||
gem 'html-pipeline', '~> 1.11.0'
|
gem 'html-pipeline', '~> 1.11.0'
|
||||||
|
|
@ -171,7 +171,7 @@ gem 're2', '~> 1.1.1'
|
||||||
gem 'version_sorter', '~> 2.1.0'
|
gem 'version_sorter', '~> 2.1.0'
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
gem 'redis-rails', '~> 5.0.1'
|
gem 'redis-rails', '~> 5.0.2'
|
||||||
|
|
||||||
# Redis
|
# Redis
|
||||||
gem 'redis', '~> 3.2'
|
gem 'redis', '~> 3.2'
|
||||||
|
|
@ -283,7 +283,7 @@ group :metrics do
|
||||||
gem 'influxdb', '~> 0.2', require: false
|
gem 'influxdb', '~> 0.2', require: false
|
||||||
|
|
||||||
# Prometheus
|
# Prometheus
|
||||||
gem 'prometheus-client-mmap', '~> 0.7.0.beta39'
|
gem 'prometheus-client-mmap', '~> 0.7.0.beta43'
|
||||||
gem 'raindrops', '~> 0.18'
|
gem 'raindrops', '~> 0.18'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -400,14 +400,18 @@ group :ed25519 do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gitaly GRPC client
|
# Gitaly GRPC client
|
||||||
gem 'gitaly-proto', '~> 0.54.0', require: 'gitaly'
|
gem 'gitaly-proto', '~> 0.61.0', require: 'gitaly'
|
||||||
|
|
||||||
gem 'toml-rb', '~> 0.3.15', require: false
|
gem 'toml-rb', '~> 0.3.15', require: false
|
||||||
|
|
||||||
# Feature toggles
|
# Feature toggles
|
||||||
gem 'flipper', '~> 0.10.2'
|
gem 'flipper', '~> 0.11.0'
|
||||||
gem 'flipper-active_record', '~> 0.10.2'
|
gem 'flipper-active_record', '~> 0.11.0'
|
||||||
|
gem 'flipper-active_support_cache_store', '~> 0.11.0'
|
||||||
|
|
||||||
# Structured logging
|
# Structured logging
|
||||||
gem 'lograge', '~> 0.5'
|
gem 'lograge', '~> 0.5'
|
||||||
gem 'grape_logging', '~> 1.7'
|
gem 'grape_logging', '~> 1.7'
|
||||||
|
|
||||||
|
# Asset synchronization
|
||||||
|
gem 'asset_sync', '~> 2.2.0'
|
||||||
|
|
|
||||||
150
Gemfile.lock
|
|
@ -4,38 +4,38 @@ GEM
|
||||||
RedCloth (4.3.2)
|
RedCloth (4.3.2)
|
||||||
abstract_type (0.0.7)
|
abstract_type (0.0.7)
|
||||||
ace-rails-ap (4.1.2)
|
ace-rails-ap (4.1.2)
|
||||||
actionmailer (4.2.8)
|
actionmailer (4.2.10)
|
||||||
actionpack (= 4.2.8)
|
actionpack (= 4.2.10)
|
||||||
actionview (= 4.2.8)
|
actionview (= 4.2.10)
|
||||||
activejob (= 4.2.8)
|
activejob (= 4.2.10)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||||
actionpack (4.2.8)
|
actionpack (4.2.10)
|
||||||
actionview (= 4.2.8)
|
actionview (= 4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
rack (~> 1.6)
|
rack (~> 1.6)
|
||||||
rack-test (~> 0.6.2)
|
rack-test (~> 0.6.2)
|
||||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (4.2.8)
|
actionview (4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
activejob (4.2.8)
|
activejob (4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
globalid (>= 0.3.0)
|
globalid (>= 0.3.0)
|
||||||
activemodel (4.2.8)
|
activemodel (4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
activerecord (4.2.8)
|
activerecord (4.2.10)
|
||||||
activemodel (= 4.2.8)
|
activemodel (= 4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
arel (~> 6.0)
|
arel (~> 6.0)
|
||||||
activerecord_sane_schema_dumper (0.2)
|
activerecord_sane_schema_dumper (0.2)
|
||||||
rails (>= 4, < 5)
|
rails (>= 4, < 5)
|
||||||
activesupport (4.2.8)
|
activesupport (4.2.10)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
thread_safe (~> 0.3, >= 0.3.4)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
|
|
@ -58,6 +58,11 @@ GEM
|
||||||
asciidoctor (1.5.3)
|
asciidoctor (1.5.3)
|
||||||
asciidoctor-plantuml (0.0.7)
|
asciidoctor-plantuml (0.0.7)
|
||||||
asciidoctor (~> 1.5)
|
asciidoctor (~> 1.5)
|
||||||
|
asset_sync (2.2.0)
|
||||||
|
activemodel (>= 4.1.0)
|
||||||
|
fog-core
|
||||||
|
mime-types (>= 2.99)
|
||||||
|
unf
|
||||||
ast (2.3.0)
|
ast (2.3.0)
|
||||||
atomic (1.1.99)
|
atomic (1.1.99)
|
||||||
attr_encrypted (3.0.3)
|
attr_encrypted (3.0.3)
|
||||||
|
|
@ -210,10 +215,13 @@ GEM
|
||||||
path_expander (~> 1.0)
|
path_expander (~> 1.0)
|
||||||
ruby_parser (~> 3.0)
|
ruby_parser (~> 3.0)
|
||||||
sexp_processor (~> 4.0)
|
sexp_processor (~> 4.0)
|
||||||
flipper (0.10.2)
|
flipper (0.11.0)
|
||||||
flipper-active_record (0.10.2)
|
flipper-active_record (0.11.0)
|
||||||
activerecord (>= 3.2, < 6)
|
activerecord (>= 3.2, < 6)
|
||||||
flipper (~> 0.10.2)
|
flipper (~> 0.11.0)
|
||||||
|
flipper-active_support_cache_store (0.11.0)
|
||||||
|
activesupport (>= 3.2, < 6)
|
||||||
|
flipper (~> 0.11.0)
|
||||||
flowdock (0.7.1)
|
flowdock (0.7.1)
|
||||||
httparty (~> 0.7)
|
httparty (~> 0.7)
|
||||||
multi_json
|
multi_json
|
||||||
|
|
@ -276,7 +284,7 @@ GEM
|
||||||
po_to_json (>= 1.0.0)
|
po_to_json (>= 1.0.0)
|
||||||
rails (>= 3.2.0)
|
rails (>= 3.2.0)
|
||||||
gherkin-ruby (0.3.2)
|
gherkin-ruby (0.3.2)
|
||||||
gitaly-proto (0.54.0)
|
gitaly-proto (0.61.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
grpc (~> 1.0)
|
grpc (~> 1.0)
|
||||||
github-linguist (4.7.6)
|
github-linguist (4.7.6)
|
||||||
|
|
@ -300,8 +308,8 @@ GEM
|
||||||
omniauth (~> 1.3)
|
omniauth (~> 1.3)
|
||||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||||
rubyntlm (~> 0.5)
|
rubyntlm (~> 0.5)
|
||||||
globalid (0.3.7)
|
globalid (0.4.1)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.2.0)
|
||||||
gollum-grit_adapter (1.0.1)
|
gollum-grit_adapter (1.0.1)
|
||||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
gitlab-grit (~> 2.7, >= 2.7.1)
|
||||||
gollum-lib (4.2.7)
|
gollum-lib (4.2.7)
|
||||||
|
|
@ -328,8 +336,6 @@ GEM
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.0)
|
retriable (>= 2.0, < 4.0)
|
||||||
google-protobuf (3.4.1.1)
|
google-protobuf (3.4.1.1)
|
||||||
googleapis-common-protos-types (1.0.0)
|
|
||||||
google-protobuf (~> 3.0)
|
|
||||||
googleauth (0.5.3)
|
googleauth (0.5.3)
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
jwt (~> 1.4)
|
jwt (~> 1.4)
|
||||||
|
|
@ -356,10 +362,9 @@ GEM
|
||||||
rake
|
rake
|
||||||
grape_logging (1.7.0)
|
grape_logging (1.7.0)
|
||||||
grape
|
grape
|
||||||
grpc (1.7.2)
|
grpc (1.4.5)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
googleapis-common-protos-types (~> 1.0.0)
|
googleauth (~> 0.5.1)
|
||||||
googleauth (>= 0.5.1, < 0.7)
|
|
||||||
haml (4.0.7)
|
haml (4.0.7)
|
||||||
tilt
|
tilt
|
||||||
haml_lint (0.26.0)
|
haml_lint (0.26.0)
|
||||||
|
|
@ -400,7 +405,8 @@ GEM
|
||||||
json (~> 1.8)
|
json (~> 1.8)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
httpclient (2.8.2)
|
httpclient (2.8.2)
|
||||||
i18n (0.8.6)
|
i18n (0.9.1)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
influxdb (0.2.3)
|
influxdb (0.2.3)
|
||||||
cause
|
cause
|
||||||
|
|
@ -474,8 +480,8 @@ GEM
|
||||||
railties (>= 4, < 5.2)
|
railties (>= 4, < 5.2)
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.6.6)
|
mail (2.7.0)
|
||||||
mime-types (>= 1.16, < 4)
|
mini_mime (>= 0.1.1)
|
||||||
mail_room (0.9.1)
|
mail_room (0.9.1)
|
||||||
memoist (0.16.0)
|
memoist (0.16.0)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
|
|
@ -488,7 +494,6 @@ GEM
|
||||||
mini_mime (0.1.4)
|
mini_mime (0.1.4)
|
||||||
mini_portile2 (2.3.0)
|
mini_portile2 (2.3.0)
|
||||||
minitest (5.7.0)
|
minitest (5.7.0)
|
||||||
mmap2 (2.2.9)
|
|
||||||
mousetrap-rails (1.4.6)
|
mousetrap-rails (1.4.6)
|
||||||
multi_json (1.12.2)
|
multi_json (1.12.2)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
|
|
@ -573,8 +578,8 @@ GEM
|
||||||
parallel (1.12.0)
|
parallel (1.12.0)
|
||||||
paranoia (2.3.1)
|
paranoia (2.3.1)
|
||||||
activerecord (>= 4.0, < 5.2)
|
activerecord (>= 4.0, < 5.2)
|
||||||
parser (2.4.0.0)
|
parser (2.4.0.2)
|
||||||
ast (~> 2.2)
|
ast (~> 2.3)
|
||||||
parslet (1.5.0)
|
parslet (1.5.0)
|
||||||
blankslate (~> 2.0)
|
blankslate (~> 2.0)
|
||||||
path_expander (1.0.1)
|
path_expander (1.0.1)
|
||||||
|
|
@ -625,8 +630,7 @@ GEM
|
||||||
parser
|
parser
|
||||||
unparser
|
unparser
|
||||||
procto (0.0.3)
|
procto (0.0.3)
|
||||||
prometheus-client-mmap (0.7.0.beta39)
|
prometheus-client-mmap (0.7.0.beta43)
|
||||||
mmap2 (~> 2.2, >= 2.2.9)
|
|
||||||
pry (0.10.4)
|
pry (0.10.4)
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
|
|
@ -656,16 +660,16 @@ GEM
|
||||||
rack
|
rack
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (4.2.8)
|
rails (4.2.10)
|
||||||
actionmailer (= 4.2.8)
|
actionmailer (= 4.2.10)
|
||||||
actionpack (= 4.2.8)
|
actionpack (= 4.2.10)
|
||||||
actionview (= 4.2.8)
|
actionview (= 4.2.10)
|
||||||
activejob (= 4.2.8)
|
activejob (= 4.2.10)
|
||||||
activemodel (= 4.2.8)
|
activemodel (= 4.2.10)
|
||||||
activerecord (= 4.2.8)
|
activerecord (= 4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
bundler (>= 1.3.0, < 2.0)
|
bundler (>= 1.3.0, < 2.0)
|
||||||
railties (= 4.2.8)
|
railties (= 4.2.10)
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
rails-deprecated_sanitizer (1.0.3)
|
rails-deprecated_sanitizer (1.0.3)
|
||||||
activesupport (>= 4.2.0.alpha)
|
activesupport (>= 4.2.0.alpha)
|
||||||
|
|
@ -678,15 +682,15 @@ GEM
|
||||||
rails-i18n (4.0.9)
|
rails-i18n (4.0.9)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
railties (~> 4.0)
|
railties (~> 4.0)
|
||||||
railties (4.2.8)
|
railties (4.2.10)
|
||||||
actionpack (= 4.2.8)
|
actionpack (= 4.2.10)
|
||||||
activesupport (= 4.2.8)
|
activesupport (= 4.2.10)
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (2.2.2)
|
rainbow (2.2.2)
|
||||||
rake
|
rake
|
||||||
raindrops (0.18.0)
|
raindrops (0.18.0)
|
||||||
rake (12.1.0)
|
rake (12.3.0)
|
||||||
rblineprof (0.3.6)
|
rblineprof (0.3.6)
|
||||||
debugger-ruby_core_source (~> 1.3)
|
debugger-ruby_core_source (~> 1.3)
|
||||||
rbnacl (4.0.2)
|
rbnacl (4.0.2)
|
||||||
|
|
@ -701,24 +705,24 @@ GEM
|
||||||
recursive-open-struct (1.0.0)
|
recursive-open-struct (1.0.0)
|
||||||
redcarpet (3.4.0)
|
redcarpet (3.4.0)
|
||||||
redis (3.3.3)
|
redis (3.3.3)
|
||||||
redis-actionpack (5.0.1)
|
redis-actionpack (5.0.2)
|
||||||
actionpack (>= 4.0, < 6)
|
actionpack (>= 4.0, < 6)
|
||||||
redis-rack (>= 1, < 3)
|
redis-rack (>= 1, < 3)
|
||||||
redis-store (>= 1.1.0, < 1.4.0)
|
redis-store (>= 1.1.0, < 2)
|
||||||
redis-activesupport (5.0.1)
|
redis-activesupport (5.0.4)
|
||||||
activesupport (>= 3, < 6)
|
activesupport (>= 3, < 6)
|
||||||
redis-store (~> 1.2.0)
|
redis-store (>= 1.3, < 2)
|
||||||
redis-namespace (1.5.2)
|
redis-namespace (1.5.2)
|
||||||
redis (~> 3.0, >= 3.0.4)
|
redis (~> 3.0, >= 3.0.4)
|
||||||
redis-rack (1.6.0)
|
redis-rack (2.0.3)
|
||||||
rack (~> 1.5)
|
rack (>= 1.5, < 3)
|
||||||
redis-store (~> 1.2.0)
|
redis-store (>= 1.2, < 2)
|
||||||
redis-rails (5.0.1)
|
redis-rails (5.0.2)
|
||||||
redis-actionpack (~> 5.0.0)
|
redis-actionpack (>= 5.0, < 6)
|
||||||
redis-activesupport (~> 5.0.0)
|
redis-activesupport (>= 5.0, < 6)
|
||||||
redis-store (~> 1.2.0)
|
redis-store (>= 1.2, < 2)
|
||||||
redis-store (1.2.0)
|
redis-store (1.4.1)
|
||||||
redis (>= 2.2)
|
redis (>= 2.2, < 5)
|
||||||
representable (3.0.4)
|
representable (3.0.4)
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
declarative-option (< 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
|
|
@ -815,7 +819,7 @@ GEM
|
||||||
rake (>= 0.9, < 13)
|
rake (>= 0.9, < 13)
|
||||||
sass (~> 3.4.20)
|
sass (~> 3.4.20)
|
||||||
securecompare (1.0.0)
|
securecompare (1.0.0)
|
||||||
seed-fu (2.3.7)
|
seed-fu (2.3.6)
|
||||||
activerecord (>= 3.1)
|
activerecord (>= 3.1)
|
||||||
activesupport (>= 3.1)
|
activesupport (>= 3.1)
|
||||||
select2-rails (3.5.9.3)
|
select2-rails (3.5.9.3)
|
||||||
|
|
@ -873,7 +877,7 @@ GEM
|
||||||
sprockets (3.7.1)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.0)
|
sprockets-rails (3.2.1)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
|
|
@ -911,7 +915,7 @@ GEM
|
||||||
truncato (0.7.10)
|
truncato (0.7.10)
|
||||||
htmlentities (~> 4.3.1)
|
htmlentities (~> 4.3.1)
|
||||||
nokogiri (~> 1.8.0, >= 1.7.0)
|
nokogiri (~> 1.8.0, >= 1.7.0)
|
||||||
tzinfo (1.2.3)
|
tzinfo (1.2.4)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
u2f (0.2.1)
|
u2f (0.2.1)
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
|
|
@ -979,6 +983,7 @@ DEPENDENCIES
|
||||||
asana (~> 0.6.0)
|
asana (~> 0.6.0)
|
||||||
asciidoctor (~> 1.5.2)
|
asciidoctor (~> 1.5.2)
|
||||||
asciidoctor-plantuml (= 0.0.7)
|
asciidoctor-plantuml (= 0.0.7)
|
||||||
|
asset_sync (~> 2.2.0)
|
||||||
attr_encrypted (~> 3.0.0)
|
attr_encrypted (~> 3.0.0)
|
||||||
awesome_print (~> 1.2.0)
|
awesome_print (~> 1.2.0)
|
||||||
babosa (~> 1.0.2)
|
babosa (~> 1.0.2)
|
||||||
|
|
@ -1019,8 +1024,9 @@ DEPENDENCIES
|
||||||
faraday (~> 0.12)
|
faraday (~> 0.12)
|
||||||
ffaker (~> 2.4)
|
ffaker (~> 2.4)
|
||||||
flay (~> 2.8.0)
|
flay (~> 2.8.0)
|
||||||
flipper (~> 0.10.2)
|
flipper (~> 0.11.0)
|
||||||
flipper-active_record (~> 0.10.2)
|
flipper-active_record (~> 0.11.0)
|
||||||
|
flipper-active_support_cache_store (~> 0.11.0)
|
||||||
fog-aliyun (~> 0.2.0)
|
fog-aliyun (~> 0.2.0)
|
||||||
fog-aws (~> 1.4)
|
fog-aws (~> 1.4)
|
||||||
fog-core (~> 1.44)
|
fog-core (~> 1.44)
|
||||||
|
|
@ -1036,7 +1042,7 @@ DEPENDENCIES
|
||||||
gettext (~> 3.2.2)
|
gettext (~> 3.2.2)
|
||||||
gettext_i18n_rails (~> 1.8.0)
|
gettext_i18n_rails (~> 1.8.0)
|
||||||
gettext_i18n_rails_js (~> 1.2.0)
|
gettext_i18n_rails_js (~> 1.2.0)
|
||||||
gitaly-proto (~> 0.54.0)
|
gitaly-proto (~> 0.61.0)
|
||||||
github-linguist (~> 4.7.0)
|
github-linguist (~> 4.7.0)
|
||||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||||
gitlab-markup (~> 1.6.2)
|
gitlab-markup (~> 1.6.2)
|
||||||
|
|
@ -1111,14 +1117,14 @@ DEPENDENCIES
|
||||||
peek-sidekiq (~> 1.0.3)
|
peek-sidekiq (~> 1.0.3)
|
||||||
pg (~> 0.18.2)
|
pg (~> 0.18.2)
|
||||||
premailer-rails (~> 1.9.7)
|
premailer-rails (~> 1.9.7)
|
||||||
prometheus-client-mmap (~> 0.7.0.beta39)
|
prometheus-client-mmap (~> 0.7.0.beta43)
|
||||||
pry-byebug (~> 3.4.1)
|
pry-byebug (~> 3.4.1)
|
||||||
pry-rails (~> 0.3.4)
|
pry-rails (~> 0.3.4)
|
||||||
rack-attack (~> 4.4.1)
|
rack-attack (~> 4.4.1)
|
||||||
rack-cors (~> 0.4.0)
|
rack-cors (~> 0.4.0)
|
||||||
rack-oauth2 (~> 1.2.1)
|
rack-oauth2 (~> 1.2.1)
|
||||||
rack-proxy (~> 0.6.0)
|
rack-proxy (~> 0.6.0)
|
||||||
rails (= 4.2.8)
|
rails (= 4.2.10)
|
||||||
rails-deprecated_sanitizer (~> 1.0.3)
|
rails-deprecated_sanitizer (~> 1.0.3)
|
||||||
rails-i18n (~> 4.0.9)
|
rails-i18n (~> 4.0.9)
|
||||||
rainbow (~> 2.2)
|
rainbow (~> 2.2)
|
||||||
|
|
@ -1132,7 +1138,7 @@ DEPENDENCIES
|
||||||
redcarpet (~> 3.4)
|
redcarpet (~> 3.4)
|
||||||
redis (~> 3.2)
|
redis (~> 3.2)
|
||||||
redis-namespace (~> 1.5.2)
|
redis-namespace (~> 1.5.2)
|
||||||
redis-rails (~> 5.0.1)
|
redis-rails (~> 5.0.2)
|
||||||
request_store (~> 1.3)
|
request_store (~> 1.3)
|
||||||
responders (~> 2.0)
|
responders (~> 2.0)
|
||||||
rouge (~> 2.0)
|
rouge (~> 2.0)
|
||||||
|
|
@ -1153,7 +1159,7 @@ DEPENDENCIES
|
||||||
sanitize (~> 2.0)
|
sanitize (~> 2.0)
|
||||||
sass-rails (~> 5.0.6)
|
sass-rails (~> 5.0.6)
|
||||||
scss_lint (~> 0.54.0)
|
scss_lint (~> 0.54.0)
|
||||||
seed-fu (~> 2.3.7)
|
seed-fu (= 2.3.6)
|
||||||
select2-rails (~> 3.5.9)
|
select2-rails (~> 3.5.9)
|
||||||
selenium-webdriver (~> 3.5)
|
selenium-webdriver (~> 3.5)
|
||||||
sentry-raven (~> 2.5.3)
|
sentry-raven (~> 2.5.3)
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,8 @@ freeze date (the 7th) should have a corresponding Enterprise Edition merge
|
||||||
request, even if there are no conflicts. This is to reduce the size of the
|
request, even if there are no conflicts. This is to reduce the size of the
|
||||||
subsequent EE merge, as we often merge a lot to CE on the release date. For more
|
subsequent EE merge, as we often merge a lot to CE on the release date. For more
|
||||||
information, see
|
information, see
|
||||||
[limit conflicts with EE when developing on CE][limit_ee_conflicts].
|
[Automatic CE->EE merge][automatic_ce_ee_merge] and
|
||||||
|
[Guidelines for implementing Enterprise Edition features][ee_features].
|
||||||
|
|
||||||
### After the 7th
|
### After the 7th
|
||||||
|
|
||||||
|
|
@ -281,4 +282,5 @@ still an issue I encourage you to open it on the [GitLab.com issue tracker](http
|
||||||
["Implement design & UI elements" guidelines]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#implement-design-ui-elements
|
["Implement design & UI elements" guidelines]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#implement-design-ui-elements
|
||||||
[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
|
[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
|
||||||
[done]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#definition-of-done
|
[done]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#definition-of-done
|
||||||
[limit_ee_conflicts]: https://docs.gitlab.com/ce/development/limit_ee_conflicts.html
|
[automatic_ce_ee_merge]: https://docs.gitlab.com/ce/development/automatic_ce_ee_merge.html
|
||||||
|
[ee_features]: https://docs.gitlab.com/ce/development/ee_features.html
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg width="24" height="30" viewBox="0 0 24 30" xmlns="http://www.w3.org/2000/svg"><title>cursor</title><g fill="none" fill-rule="evenodd"><path d="M24 12.105c0 6.686-5.74 11.58-12 17.895C5.74 23.684 0 18.79 0 12.105 0 5.42 5.373 0 12 0s12 5.42 12 12.105z" fill="#1F78D1" fill-rule="nonzero"/><path d="M15.28 25.249c1.458-1.475 2.539-2.635 3.474-3.747 2.851-3.394 4.203-6.265 4.203-9.397 0-6.111-4.908-11.062-10.957-11.062-6.05 0-10.957 4.951-10.957 11.062 0 3.132 1.352 6.003 4.203 9.397.935 1.112 2.016 2.272 3.474 3.747.511.517 2.216 2.213 3.28 3.275 1.064-1.062 2.769-2.758 3.28-3.275z" fill="#FFF"/><path d="M14.551 8.256A6.874 6.874 0 0 0 12 7.787c-.91 0-1.763.156-2.558.469-.79.308-1.42.725-1.888 1.252-.465.527-.697 1.096-.697 1.708 0 .5.159.977.476 1.433.321.45.772.841 1.352 1.172l.583.334-.181.643c-.107.407-.263.79-.469 1.152a6.604 6.604 0 0 0 1.842-1.145l.288-.254.381.04c.309.035.599.053.871.053.91 0 1.761-.154 2.551-.462.795-.312 1.424-.732 1.889-1.259.468-.526.703-1.096.703-1.707 0-.612-.235-1.181-.703-1.708-.465-.527-1.094-.944-1.889-1.252zm2.645.81c.536.656.804 1.373.804 2.15 0 .776-.268 1.495-.804 2.156-.535.656-1.263 1.176-2.183 1.56-.92.38-1.924.57-3.013.57a9.16 9.16 0 0 1-.971-.054 7.32 7.32 0 0 1-3.08 1.62 5.044 5.044 0 0 1-.764.148h-.033a.26.26 0 0 1-.181-.074.324.324 0 0 1-.107-.18v-.007c-.014-.018-.016-.045-.007-.08.014-.037.018-.059.014-.068 0-.009.01-.031.033-.067a.645.645 0 0 0 .04-.06 1.73 1.73 0 0 0 .047-.054l.054-.06a53.034 53.034 0 0 1 .435-.489c.049-.049.118-.136.207-.26.094-.126.168-.24.221-.342.054-.103.114-.235.181-.395.067-.161.125-.33.174-.51-.7-.397-1.254-.888-1.66-1.473A3.261 3.261 0 0 1 6 11.216c0-.777.268-1.494.804-2.15.535-.66 1.263-1.18 2.183-1.56.92-.384 1.924-.576 3.013-.576 1.09 0 2.094.192 3.013.576.92.38 1.648.9 2.183 1.56z" fill="#1F78D1" fill-rule="nonzero"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg width="48" height="60" viewBox="0 0 48 60" xmlns="http://www.w3.org/2000/svg"><title>cursor_2x</title><g fill="none" fill-rule="evenodd"><path d="M48 24.21C48 37.583 36.522 47.369 24 60 11.478 47.368 0 37.582 0 24.21 0 10.84 10.745 0 24 0s24 10.84 24 24.21z" fill="#1F78D1" fill-rule="nonzero"/><path d="M30.56 50.497c2.915-2.95 5.078-5.268 6.947-7.493 5.703-6.788 8.406-12.53 8.406-18.793 0-12.223-9.815-22.124-21.913-22.124S2.087 11.988 2.087 24.211c0 6.263 2.703 12.005 8.406 18.793 1.87 2.225 4.032 4.544 6.947 7.493 1.022 1.035 4.432 4.426 6.56 6.55 2.128-2.124 5.538-5.515 6.56-6.55z" fill="#FFF"/><path d="M29.103 16.512c-1.58-.625-3.282-.938-5.103-.938-1.821 0-3.527.313-5.116.938-1.58.616-2.84 1.45-3.777 2.504-.928 1.054-1.393 2.192-1.393 3.415 0 1 .317 1.956.951 2.866.643.902 1.545 1.684 2.706 2.344l1.165.67-.362 1.286a9.603 9.603 0 0 1-.937 2.303 13.208 13.208 0 0 0 3.683-2.29l.576-.509.763.08c.616.072 1.196.108 1.741.108 1.821 0 3.522-.308 5.103-.925 1.589-.625 2.848-1.464 3.776-2.517.938-1.054 1.407-2.192 1.407-3.416 0-1.223-.469-2.361-1.407-3.415-.928-1.053-2.187-1.888-3.776-2.504zm5.29 1.62c1.071 1.313 1.607 2.746 1.607 4.3 0 1.553-.536 2.99-1.607 4.312-1.072 1.312-2.527 2.353-4.366 3.12-1.84.76-3.848 1.139-6.027 1.139a18.32 18.32 0 0 1-1.942-.107c-1.768 1.562-3.821 2.643-6.16 3.24-.438.126-.947.224-1.527.295h-.067a.521.521 0 0 1-.362-.147.649.649 0 0 1-.214-.362v-.013c-.027-.036-.032-.09-.014-.16.027-.072.036-.117.027-.135 0-.017.022-.062.067-.133a1.29 1.29 0 0 0 .08-.121c.01-.009.04-.045.094-.107a106.068 106.068 0 0 1 .522-.59c.215-.232.367-.401.456-.508.098-.099.236-.273.415-.523.188-.25.335-.477.442-.683.107-.205.228-.468.362-.79.134-.321.25-.66.348-1.018-1.402-.794-2.51-1.777-3.322-2.946C12.402 25.025 12 23.77 12 22.43c0-1.553.536-2.986 1.607-4.299 1.072-1.321 2.527-2.361 4.366-3.12 1.84-.768 3.848-1.152 6.027-1.152 2.179 0 4.188.384 6.027 1.152 1.84.759 3.294 1.799 4.366 3.12z" fill="#1F78D1" fill-rule="nonzero"/></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
|
@ -1 +1 @@
|
||||||
{"iconCount":179,"spriteSize":81882,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-right","assignee","bold","book","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","import","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
|
{"iconCount":181,"spriteSize":81482,"icons":["abuse","account","admin","angle-double-left","angle-double-right","angle-down","angle-left","angle-right","angle-up","appearance","applications","approval","arrow-down","arrow-right","assignee","bold","book","branch","bullhorn","calendar","cancel","chart","chevron-down","chevron-left","chevron-right","chevron-up","clock","close","code","collapse","comment-dots","comment-next","comment","comments","commit","credit-card","cut","dashboard","disk","doc_code","doc_image","doc_text","double-headed-arrow","download","duplicate","earth","ellipsis_v","emoji_slightly_smiling_face","emoji_smile","emoji_smiley","epic","external-link","eye-slash","eye","file-addition","file-deletion","file-modified","filter","folder","fork","geo-nodes","git-merge","group","history","home","hook","hourglass","image-comment-dark","image-comment-light","import","issue-block","issue-child","issue-close","issue-duplicate","issue-new","issue-open-m","issue-open","issue-parent","issues","italic","key-2","key","label","labels","leave","level-up","license","link","list-bulleted","list-numbered","location-dot","location","lock-open","lock","log","mail","menu","merge-request-close","messages","mobile-issue-close","monitor","more","notifications-off","notifications","overview","pencil-square","pencil","pipeline","play","plus-square-o","plus-square","plus","preferences","profile","project","push-rules","question-o","question","quote","redo","remove","repeat","retry","scale","screen-full","screen-normal","scroll_down","scroll_up","search","settings","shield","slight-frown","slight-smile","smile","smiley","snippet","spam","spinner","star-o","star","status_canceled_borderless","status_canceled","status_closed","status_created_borderless","status_created","status_failed_borderless","status_failed","status_manual_borderless","status_manual","status_notfound_borderless","status_open","status_pending_borderless","status_pending","status_running_borderless","status_running","status_skipped_borderless","status_skipped","status_success_borderless","status_success_solid","status_success","status_warning_borderless","status_warning","stop","task-done","template","terminal","thumb-down","thumb-up","thumbtack","timer","todo-add","todo-done","token","unapproval","unassignee","unlink","user","users","volume-up","warning","work"]}
|
||||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
|
@ -1 +1 @@
|
||||||
<svg height="128" viewBox="0 0 142 128" width="142" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M94 62h20v4H94z" fill="#f0edf8"/><path d="M84.828 84l17.678 17.678-2.828 2.828L82 86.828z" fill="#fee1d3"/><path d="M42.828 24l17.678 17.678-2.828 2.828L40 26.828zM40 101.678L57.678 84l2.828 2.828-17.678 17.678z" fill="#f0edf8"/><g fill="#fee1d3"><path d="M82 41.678L99.678 24l2.828 2.828-17.678 17.678zM28 62h20v4H28z"/><rect height="30" rx="5" width="30" y="49"/></g><rect height="26" rx="5" stroke="#fdc4a8" stroke-width="4" width="26" x="2" y="51"/><rect fill="#c3b8e3" height="50" rx="10" width="50" x="46" y="39"/><rect height="46" rx="10" stroke="#6b4fbb" stroke-width="4" width="46" x="48" y="41"/><rect fill="#fef0e8" height="30" rx="5" width="30" x="84"/><rect height="26" rx="5" stroke="#fee1d3" stroke-width="4" width="26" x="86" y="2"/><rect fill="#fee1d3" height="30" rx="5" width="30" x="84" y="98"/><rect height="26" rx="5" stroke="#fdc4a8" stroke-width="4" width="26" x="86" y="100"/><rect fill="#f0edf8" height="30" rx="5" width="30" x="112" y="49"/><rect height="26" rx="5" stroke="#e1dbf1" stroke-width="4" width="26" x="114" y="51"/><rect fill="#f0edf8" height="30" rx="5" width="30" x="28" y="98"/><rect height="26" rx="5" stroke="#e1dbf1" stroke-width="4" width="26" x="30" y="100"/><rect fill="#f0edf8" height="30" rx="5" width="30" x="28"/><rect height="26" rx="5" stroke="#e1dbf1" stroke-width="4" width="26" x="30" y="2"/></g></svg>
|
<svg height="128" viewBox="0 0 142 128" width="142" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M94 62h20v4H94z" fill="#f0edf8"/><path d="M84.828 84l17.678 17.678-2.828 2.828L82 86.828z" fill="#fee1d3"/><path d="M42.828 24l17.678 17.678-2.828 2.828L40 26.828zM40 101.678L57.678 84l2.828 2.828-17.678 17.678z" fill="#f0edf8"/><path d="M82 41.678L99.678 24l2.828 2.828-17.678 17.678zM28 62h20v4H28zM3 52h24v24H3z" fill="#fee1d3"/><path d="M31 3h24v24H31z" fill="#f0edf8"/><path d="M87 3h24v24H87z" fill="#fef0e8"/><path d="M115 52h24v24h-24z" fill="#f0edf8"/><path d="M87 101h24v24H87z" fill="#fee1d3"/><path d="M31 101h24v24H31z" fill="#f0edf8"/><path d="M49 42h44v44H49z" fill="#c3b8e3"/><g fill-rule="nonzero"><path d="M5 53a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V54a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5H5a5 5 0 0 1-5-5V54a5 5 0 0 1 5-5z" fill="#fdc4a8"/><path d="M56 43a6 6 0 0 0-6 6v30a6 6 0 0 0 6 6h30a6 6 0 0 0 6-6V49a6 6 0 0 0-6-6zm0-4h30c5.523 0 10 4.477 10 10v30c0 5.523-4.477 10-10 10H56c-5.523 0-10-4.477-10-10V49c0-5.523 4.477-10 10-10z" fill="#6b4fbb"/><path d="M89 4a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5H89a5 5 0 0 1-5-5V5a5 5 0 0 1 5-5z" fill="#fee1d3"/><path d="M89 102a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1v-20a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5H89a5 5 0 0 1-5-5v-20a5 5 0 0 1 5-5z" fill="#fdc4a8"/><path d="M117 53a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V54a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5h-20a5 5 0 0 1-5-5V54a5 5 0 0 1 5-5zM33 102a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1v-20a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5H33a5 5 0 0 1-5-5v-20a5 5 0 0 1 5-5zM33 4a1 1 0 0 0-1 1v20a1 1 0 0 0 1 1h20a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1zm0-4h20a5 5 0 0 1 5 5v20a5 5 0 0 1-5 5H33a5 5 0 0 1-5-5V5a5 5 0 0 1 5-5z" fill="#e1dbf1"/></g></g></svg>
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 38 38"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 570 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 38 38"><g fill="none" fill-rule="evenodd"><circle cx="19" cy="19" r="18" fill="#FFF"/><path fill="#1F78D1" fill-rule="nonzero" d="M19 38C8.507 38 0 29.493 0 19S8.507 0 19 0s19 8.507 19 19-8.507 19-19 19zm0-2c9.389 0 17-7.611 17-17S28.389 2 19 2 2 9.611 2 19s7.611 17 17 17zm-6.293-8.293c-.63.63-1.707.184-1.707-.707V15a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-7.586l-3.707 3.707zM13 24.586l2.293-2.293A1 1 0 0 1 16 22h8a1 1 0 0 0 1-1v-6a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1v9.586z"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 570 B |
|
After Width: | Height: | Size: 5.4 KiB |
|
|
@ -2,8 +2,9 @@
|
||||||
/* global Pager */
|
/* global Pager */
|
||||||
|
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
class Activities {
|
export default class Activities {
|
||||||
constructor() {
|
constructor() {
|
||||||
Pager.init(20, true, false, data => data, this.updateTooltips);
|
Pager.init(20, true, false, data => data, this.updateTooltips);
|
||||||
|
|
||||||
|
|
@ -15,7 +16,7 @@ class Activities {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTooltips() {
|
updateTooltips() {
|
||||||
gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
|
localTimeAgo($('.js-timeago', '.content_list'));
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadActivities() {
|
reloadActivities() {
|
||||||
|
|
@ -33,6 +34,3 @@ class Activities {
|
||||||
$sender.closest('li').toggleClass('active');
|
$sender.closest('li').toggleClass('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
window.gl.Activities = Activities;
|
|
||||||
|
|
|
||||||
|
|
@ -1,62 +1,59 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */
|
import { refreshCurrentPage } from './lib/utils/url_utility';
|
||||||
|
|
||||||
window.Admin = (function() {
|
function showBlacklistType() {
|
||||||
function Admin() {
|
if ($('input[name="blacklist_type"]:checked').val() === 'file') {
|
||||||
var modal, showBlacklistType;
|
$('.blacklist-file').show();
|
||||||
$('input#user_force_random_password').on('change', function(elem) {
|
$('.blacklist-raw').hide();
|
||||||
var elems;
|
} else {
|
||||||
elems = $('#user_password, #user_password_confirmation');
|
$('.blacklist-file').hide();
|
||||||
if ($(this).attr('checked')) {
|
$('.blacklist-raw').show();
|
||||||
return elems.val('').attr('disabled', true);
|
|
||||||
} else {
|
|
||||||
return elems.removeAttr('disabled');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$('body').on('click', '.js-toggle-colors-link', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
return $('.js-toggle-colors-container').toggle();
|
|
||||||
});
|
|
||||||
$('.log-tabs a').click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
return $(this).tab('show');
|
|
||||||
});
|
|
||||||
$('.log-bottom').click(function(e) {
|
|
||||||
var visible_log;
|
|
||||||
e.preventDefault();
|
|
||||||
visible_log = $(".file-content:visible");
|
|
||||||
return visible_log.animate({
|
|
||||||
scrollTop: visible_log.find('ol').height()
|
|
||||||
}, "fast");
|
|
||||||
});
|
|
||||||
modal = $('.change-owner-holder');
|
|
||||||
$('.change-owner-link').bind("click", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$(this).hide();
|
|
||||||
return modal.show();
|
|
||||||
});
|
|
||||||
$('.change-owner-cancel-link').bind("click", function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
modal.hide();
|
|
||||||
return $('.change-owner-link').show();
|
|
||||||
});
|
|
||||||
$('li.project_member').bind('ajax:success', function() {
|
|
||||||
return gl.utils.refreshCurrentPage();
|
|
||||||
});
|
|
||||||
$('li.group_member').bind('ajax:success', function() {
|
|
||||||
return gl.utils.refreshCurrentPage();
|
|
||||||
});
|
|
||||||
showBlacklistType = function() {
|
|
||||||
if ($("input[name='blacklist_type']:checked").val() === 'file') {
|
|
||||||
$('.blacklist-file').show();
|
|
||||||
return $('.blacklist-raw').hide();
|
|
||||||
} else {
|
|
||||||
$('.blacklist-file').hide();
|
|
||||||
return $('.blacklist-raw').show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
$("input[name='blacklist_type']").click(showBlacklistType);
|
|
||||||
showBlacklistType();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Admin;
|
export default function adminInit() {
|
||||||
})();
|
const modal = $('.change-owner-holder');
|
||||||
|
|
||||||
|
$('input#user_force_random_password').on('change', function randomPasswordClick() {
|
||||||
|
const $elems = $('#user_password, #user_password_confirmation');
|
||||||
|
if ($(this).attr('checked')) {
|
||||||
|
$elems.val('').attr('disabled', true);
|
||||||
|
} else {
|
||||||
|
$elems.removeAttr('disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('body').on('click', '.js-toggle-colors-link', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
$('.js-toggle-colors-container').toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.log-tabs a').on('click', function logTabsClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).tab('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.log-bottom').on('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const $visibleLog = $('.file-content:visible');
|
||||||
|
$visibleLog.animate({
|
||||||
|
scrollTop: $visibleLog.find('ol').height(),
|
||||||
|
}, 'fast');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.change-owner-link').on('click', function changeOwnerLinkClick(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).hide();
|
||||||
|
modal.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.change-owner-cancel-link').on('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
modal.hide();
|
||||||
|
$('.change-owner-link').show();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('li.project_member, li.group_member').on('ajax:success', refreshCurrentPage);
|
||||||
|
|
||||||
|
$("input[name='blacklist_type']").on('click', showBlacklistType);
|
||||||
|
showBlacklistType();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */
|
|
||||||
|
|
||||||
window.Aside = (function() {
|
|
||||||
function Aside() {
|
|
||||||
$(document).off("click", "a.show-aside");
|
|
||||||
$(document).on("click", 'a.show-aside', function(e) {
|
|
||||||
var btn, icon;
|
|
||||||
e.preventDefault();
|
|
||||||
btn = $(e.currentTarget);
|
|
||||||
icon = btn.find('i');
|
|
||||||
if (icon.hasClass('fa-angle-left')) {
|
|
||||||
btn.parent().find('section').hide();
|
|
||||||
btn.parent().find('aside').fadeIn();
|
|
||||||
return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
|
|
||||||
} else {
|
|
||||||
btn.parent().find('aside').hide();
|
|
||||||
btn.parent().find('section').fadeIn();
|
|
||||||
return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return Aside;
|
|
||||||
})();
|
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import Clipboard from 'clipboard';
|
||||||
|
|
||||||
|
function showTooltip(target, title) {
|
||||||
|
const $target = $(target);
|
||||||
|
const originalTitle = $target.data('original-title');
|
||||||
|
|
||||||
|
if (!$target.data('hideTooltip')) {
|
||||||
|
$target
|
||||||
|
.attr('title', title)
|
||||||
|
.tooltip('fixTitle')
|
||||||
|
.tooltip('show')
|
||||||
|
.attr('title', originalTitle)
|
||||||
|
.tooltip('fixTitle');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function genericSuccess(e) {
|
||||||
|
showTooltip(e.trigger, 'Copied');
|
||||||
|
// Clear the selection and blur the trigger so it loses its border
|
||||||
|
e.clearSelection();
|
||||||
|
$(e.trigger).blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safari > 10 doesn't support `execCommand`, so instead we inform the user to copy manually.
|
||||||
|
* See http://clipboardjs.com/#browser-support
|
||||||
|
*/
|
||||||
|
function genericError(e) {
|
||||||
|
let key;
|
||||||
|
if (/Mac/i.test(navigator.userAgent)) {
|
||||||
|
key = '⌘'; // Command
|
||||||
|
} else {
|
||||||
|
key = 'Ctrl';
|
||||||
|
}
|
||||||
|
showTooltip(e.trigger, `Press ${key}-C to copy`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function initCopyToClipboard() {
|
||||||
|
const clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
|
||||||
|
clipboard.on('success', genericSuccess);
|
||||||
|
clipboard.on('error', genericError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This a workaround around ClipboardJS limitations to allow the context-specific copy/pasting
|
||||||
|
* of plain text or GFM. The Ruby `clipboard_button` helper sneaks a JSON hash with `text` and
|
||||||
|
* `gfm` keys into the `data-clipboard-text` attribute that ClipboardJS reads from.
|
||||||
|
* When ClipboardJS creates a new `textarea` (directly inside `body`, with a `readonly`
|
||||||
|
* attribute`), sets its value to the value of this data attribute, focusses on it, and finally
|
||||||
|
* programmatically issues the 'Copy' command, this code intercepts the copy command/event at
|
||||||
|
* the last minute to deconstruct this JSON hash and set the `text/plain` and `text/x-gfm` copy
|
||||||
|
* data types to the intended values.
|
||||||
|
*/
|
||||||
|
$(document).on('copy', 'body > textarea[readonly]', (e) => {
|
||||||
|
const clipboardData = e.originalEvent.clipboardData;
|
||||||
|
if (!clipboardData) return;
|
||||||
|
|
||||||
|
const text = e.target.value;
|
||||||
|
|
||||||
|
let json;
|
||||||
|
try {
|
||||||
|
json = JSON.parse(text);
|
||||||
|
} catch (ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json.text || !json.gfm) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
clipboardData.setData('text/plain', json.text);
|
||||||
|
clipboardData.setData('text/x-gfm', json.gfm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import './autosize';
|
import './autosize';
|
||||||
import './bind_in_out';
|
import './bind_in_out';
|
||||||
import initCopyAsGFM from './copy_as_gfm';
|
import initCopyAsGFM from './copy_as_gfm';
|
||||||
|
import initCopyToClipboard from './copy_to_clipboard';
|
||||||
import './details_behavior';
|
import './details_behavior';
|
||||||
import installGlEmojiElement from './gl_emoji';
|
import installGlEmojiElement from './gl_emoji';
|
||||||
import './quick_submit';
|
import './quick_submit';
|
||||||
|
|
@ -9,3 +10,4 @@ import './toggler_behavior';
|
||||||
|
|
||||||
installGlEmojiElement();
|
installGlEmojiElement();
|
||||||
initCopyAsGFM();
|
initCopyAsGFM();
|
||||||
|
initCopyToClipboard();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// %button.js-toggle-button
|
// %button.js-toggle-button
|
||||||
// %div.js-toggle-content
|
// %div.js-toggle-content
|
||||||
//
|
//
|
||||||
|
import { getLocationHash } from '../lib/utils/url_utility';
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
function toggleContainer(container, toggleState) {
|
function toggleContainer(container, toggleState) {
|
||||||
|
|
@ -32,7 +33,7 @@ $(() => {
|
||||||
|
|
||||||
// If we're accessing a permalink, ensure it is not inside a
|
// If we're accessing a permalink, ensure it is not inside a
|
||||||
// closed js-toggle-container!
|
// closed js-toggle-container!
|
||||||
const hash = window.gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
const anchor = hash && document.getElementById(hash);
|
const anchor = hash && document.getElementById(hash);
|
||||||
const container = anchor && $(anchor).closest('.js-toggle-container');
|
const container = anchor && $(anchor).closest('.js-toggle-container');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
|
/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
|
||||||
import Dropzone from 'dropzone';
|
import Dropzone from 'dropzone';
|
||||||
import '../lib/utils/url_utility';
|
import { visitUrl } from '../lib/utils/url_utility';
|
||||||
import { HIDDEN_CLASS } from '../lib/utils/constants';
|
import { HIDDEN_CLASS } from '../lib/utils/constants';
|
||||||
import csrf from '../lib/utils/csrf';
|
import csrf from '../lib/utils/csrf';
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ export default class BlobFileDropzone {
|
||||||
});
|
});
|
||||||
this.on('success', function (header, response) {
|
this.on('success', function (header, response) {
|
||||||
$('#modal-upload-blob').modal('hide');
|
$('#modal-upload-blob').modal('hide');
|
||||||
window.gl.utils.visitUrl(response.filePath);
|
visitUrl(response.filePath);
|
||||||
});
|
});
|
||||||
this.on('maxfilesexceeded', function (file) {
|
this.on('maxfilesexceeded', function (file) {
|
||||||
dropzoneMessage.addClass(HIDDEN_CLASS);
|
dropzoneMessage.addClass(HIDDEN_CLASS);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { getLocationHash } from '../lib/utils/url_utility';
|
||||||
|
|
||||||
const lineNumberRe = /^L[0-9]+/;
|
const lineNumberRe = /^L[0-9]+/;
|
||||||
|
|
||||||
const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
|
const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
|
||||||
const hash = gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
if (hash && lineNumberRe.test(hash)) {
|
if (hash && lineNumberRe.test(hash)) {
|
||||||
const hashUrlString = `#${hash}`;
|
const hashUrlString = `#${hash}`;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ class ListIssue {
|
||||||
this.isFetching = {
|
this.isFetching = {
|
||||||
subscriptions: true,
|
subscriptions: true,
|
||||||
};
|
};
|
||||||
|
this.isLoading = {};
|
||||||
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
|
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
|
||||||
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
|
||||||
|
|
||||||
|
|
@ -86,6 +87,10 @@ class ListIssue {
|
||||||
this.isFetching[key] = value;
|
this.isFetching[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoadingState(key, value) {
|
||||||
|
this.isLoading[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
update (url) {
|
update (url) {
|
||||||
const data = {
|
const data = {
|
||||||
issue: {
|
issue: {
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ export default class Clusters {
|
||||||
|
|
||||||
this.toggle = this.toggle.bind(this);
|
this.toggle = this.toggle.bind(this);
|
||||||
this.installApplication = this.installApplication.bind(this);
|
this.installApplication = this.installApplication.bind(this);
|
||||||
|
this.showToken = this.showToken.bind(this);
|
||||||
|
|
||||||
this.toggleButton = document.querySelector('.js-toggle-cluster');
|
this.toggleButton = document.querySelector('.js-toggle-cluster');
|
||||||
this.toggleInput = document.querySelector('.js-toggle-input');
|
this.toggleInput = document.querySelector('.js-toggle-input');
|
||||||
|
|
@ -56,6 +57,8 @@ export default class Clusters {
|
||||||
this.creatingContainer = document.querySelector('.js-cluster-creating');
|
this.creatingContainer = document.querySelector('.js-cluster-creating');
|
||||||
this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
|
this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
|
||||||
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
|
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
|
||||||
|
this.showTokenButton = document.querySelector('.js-show-cluster-token');
|
||||||
|
this.tokenField = document.querySelector('.js-cluster-token');
|
||||||
|
|
||||||
initSettingsPanels();
|
initSettingsPanels();
|
||||||
this.initApplications();
|
this.initApplications();
|
||||||
|
|
@ -97,11 +100,13 @@ export default class Clusters {
|
||||||
|
|
||||||
addListeners() {
|
addListeners() {
|
||||||
this.toggleButton.addEventListener('click', this.toggle);
|
this.toggleButton.addEventListener('click', this.toggle);
|
||||||
|
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
|
||||||
eventHub.$on('installApplication', this.installApplication);
|
eventHub.$on('installApplication', this.installApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeListeners() {
|
removeListeners() {
|
||||||
this.toggleButton.removeEventListener('click', this.toggle);
|
this.toggleButton.removeEventListener('click', this.toggle);
|
||||||
|
if (this.showTokenButton) this.showTokenButton.removeEventListener('click', this.showToken);
|
||||||
eventHub.$off('installApplication', this.installApplication);
|
eventHub.$off('installApplication', this.installApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,8 +150,18 @@ export default class Clusters {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
this.toggleButton.classList.toggle('checked');
|
this.toggleButton.classList.toggle('is-checked');
|
||||||
this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('checked').toString());
|
this.toggleInput.setAttribute('value', this.toggleButton.classList.contains('is-checked').toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
showToken() {
|
||||||
|
const type = this.tokenField.getAttribute('type');
|
||||||
|
|
||||||
|
if (type === 'password') {
|
||||||
|
this.tokenField.setAttribute('type', 'text');
|
||||||
|
} else {
|
||||||
|
this.tokenField.setAttribute('type', 'password');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hideAll() {
|
hideAll() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import Flash from '../flash';
|
||||||
|
import { s__ } from '../locale';
|
||||||
|
import ClustersService from './services/clusters_service';
|
||||||
|
/**
|
||||||
|
* Toggles loading and disabled classes.
|
||||||
|
* @param {HTMLElement} button
|
||||||
|
*/
|
||||||
|
const toggleLoadingButton = (button) => {
|
||||||
|
if (button.getAttribute('disabled')) {
|
||||||
|
button.removeAttribute('disabled');
|
||||||
|
} else {
|
||||||
|
button.setAttribute('disabled', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.classList.toggle('is-loading');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles checked class for the given button
|
||||||
|
* @param {HTMLElement} button
|
||||||
|
*/
|
||||||
|
const toggleValue = (button) => {
|
||||||
|
button.classList.toggle('is-checked');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles toggle buttons in the cluster's table.
|
||||||
|
*
|
||||||
|
* When the user clicks the toggle button for each cluster, it:
|
||||||
|
* - toggles the button
|
||||||
|
* - shows a loading and disables button
|
||||||
|
* - Makes a put request to the given endpoint
|
||||||
|
* Once we receive the response, either:
|
||||||
|
* 1) Show updated status in case of successfull response
|
||||||
|
* 2) Show initial status in case of failed response
|
||||||
|
*/
|
||||||
|
export default function setClusterTableToggles() {
|
||||||
|
document.querySelectorAll('.js-toggle-cluster-list')
|
||||||
|
.forEach(button => button.addEventListener('click', (e) => {
|
||||||
|
const toggleButton = e.currentTarget;
|
||||||
|
const endpoint = toggleButton.getAttribute('data-endpoint');
|
||||||
|
|
||||||
|
toggleValue(toggleButton);
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
|
||||||
|
const value = toggleButton.classList.contains('is-checked');
|
||||||
|
|
||||||
|
ClustersService.updateCluster(endpoint, { cluster: { enabled: value } })
|
||||||
|
.then(() => {
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toggleLoadingButton(toggleButton);
|
||||||
|
toggleValue(toggleButton);
|
||||||
|
Flash(s__('ClusterIntegration|Something went wrong on our end.'));
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
@ -17,4 +17,8 @@ export default class ClusterService {
|
||||||
installApplication(appId) {
|
installApplication(appId) {
|
||||||
return axios.post(this.appInstallEndpointMap[appId]);
|
return axios.post(this.appInstallEndpointMap[appId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static updateCluster(endpoint, data) {
|
||||||
|
return axios.put(endpoint, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,217 +1,208 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
|
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
|
||||||
import 'vendor/jquery.waitforimages';
|
import 'vendor/jquery.waitforimages';
|
||||||
|
|
||||||
(function() {
|
// Width where images must fits in, for 2-up this gets divided by 2
|
||||||
gl.ImageFile = (function() {
|
const availWidth = 900;
|
||||||
var prepareFrames;
|
const viewModes = ['two-up', 'swipe'];
|
||||||
|
|
||||||
// Width where images must fits in, for 2-up this gets divided by 2
|
export default class ImageFile {
|
||||||
ImageFile.availWidth = 900;
|
constructor(file) {
|
||||||
|
this.file = file;
|
||||||
|
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
|
||||||
|
return function(deletedWidth, deletedHeight) {
|
||||||
|
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
|
||||||
|
_this.initViewModes();
|
||||||
|
|
||||||
ImageFile.viewModes = ['two-up', 'swipe'];
|
// Load two-up view after images are loaded
|
||||||
|
// so that we can display the correct width and height information
|
||||||
|
const $images = $('.two-up.view img', _this.file);
|
||||||
|
|
||||||
function ImageFile(file) {
|
$images.waitForImages(function() {
|
||||||
this.file = file;
|
_this.initView('two-up');
|
||||||
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
|
});
|
||||||
return function(deletedWidth, deletedHeight) {
|
});
|
||||||
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
|
};
|
||||||
_this.initViewModes();
|
})(this));
|
||||||
|
}
|
||||||
|
|
||||||
// Load two-up view after images are loaded
|
initViewModes() {
|
||||||
// so that we can display the correct width and height information
|
const viewMode = viewModes[0];
|
||||||
const $images = $('.two-up.view img', _this.file);
|
$('.view-modes', this.file).removeClass('hide');
|
||||||
|
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
|
||||||
|
return function(event) {
|
||||||
|
if (!$(event.currentTarget).hasClass('active')) {
|
||||||
|
return _this.activateViewMode(event.currentTarget.className);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
return this.activateViewMode(viewMode);
|
||||||
|
}
|
||||||
|
|
||||||
$images.waitForImages(function() {
|
activateViewMode(viewMode) {
|
||||||
_this.initView('two-up');
|
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
|
||||||
});
|
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
|
||||||
|
return function() {
|
||||||
|
$(".view." + viewMode, _this.file).fadeIn(200);
|
||||||
|
return _this.initView(viewMode);
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
initView(viewMode) {
|
||||||
|
return this.views[viewMode].call(this);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
initDraggable($el, padding, callback) {
|
||||||
|
var dragging = false;
|
||||||
|
var $body = $('body');
|
||||||
|
var $offsetEl = $el.parent();
|
||||||
|
|
||||||
|
$el.off('mousedown').on('mousedown', function() {
|
||||||
|
dragging = true;
|
||||||
|
$body.css('user-select', 'none');
|
||||||
|
});
|
||||||
|
|
||||||
|
$body.off('mouseup').off('mousemove').on('mouseup', function() {
|
||||||
|
dragging = false;
|
||||||
|
$body.css('user-select', '');
|
||||||
|
})
|
||||||
|
.on('mousemove', function(e) {
|
||||||
|
var left;
|
||||||
|
if (!dragging) return;
|
||||||
|
|
||||||
|
left = e.pageX - ($offsetEl.offset().left + padding);
|
||||||
|
|
||||||
|
callback(e, left);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareFrames(view) {
|
||||||
|
var maxHeight, maxWidth;
|
||||||
|
maxWidth = 0;
|
||||||
|
maxHeight = 0;
|
||||||
|
$('.frame', view).each((function(_this) {
|
||||||
|
return function(index, frame) {
|
||||||
|
var height, width;
|
||||||
|
width = $(frame).width();
|
||||||
|
height = $(frame).height();
|
||||||
|
maxWidth = width > maxWidth ? width : maxWidth;
|
||||||
|
return maxHeight = height > maxHeight ? height : maxHeight;
|
||||||
|
};
|
||||||
|
})(this)).css({
|
||||||
|
width: maxWidth,
|
||||||
|
height: maxHeight
|
||||||
|
});
|
||||||
|
return [maxWidth, maxHeight];
|
||||||
|
}
|
||||||
|
|
||||||
|
views = {
|
||||||
|
'two-up': function() {
|
||||||
|
return $('.two-up.view .wrap', this.file).each((function(_this) {
|
||||||
|
return function(index, wrap) {
|
||||||
|
$('img', wrap).each(function() {
|
||||||
|
var currentWidth;
|
||||||
|
currentWidth = $(this).width();
|
||||||
|
if (currentWidth > availWidth / 2) {
|
||||||
|
return $(this).width(availWidth / 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return _this.requestImageInfo($('img', wrap), function(width, height) {
|
||||||
|
$('.image-info .meta-width', wrap).text(width + "px");
|
||||||
|
$('.image-info .meta-height', wrap).text(height + "px");
|
||||||
|
return $('.image-info', wrap).removeClass('hide');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
},
|
||||||
|
'swipe': function() {
|
||||||
|
var maxHeight, maxWidth;
|
||||||
|
maxWidth = 0;
|
||||||
|
maxHeight = 0;
|
||||||
|
return $('.swipe.view', this.file).each((function(_this) {
|
||||||
|
return function(index, view) {
|
||||||
|
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
||||||
|
ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
||||||
|
$swipeFrame = $('.swipe-frame', view);
|
||||||
|
$swipeWrap = $('.swipe-wrap', view);
|
||||||
|
$swipeBar = $('.swipe-bar', view);
|
||||||
|
|
||||||
|
$swipeFrame.css({
|
||||||
|
width: maxWidth + 16,
|
||||||
|
height: maxHeight + 28
|
||||||
|
});
|
||||||
|
$swipeWrap.css({
|
||||||
|
width: maxWidth + 1,
|
||||||
|
height: maxHeight + 2
|
||||||
|
});
|
||||||
|
// Set swipeBar left position to match image frame
|
||||||
|
$swipeBar.css({
|
||||||
|
left: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
||||||
|
|
||||||
|
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
|
||||||
|
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
|
||||||
|
$swipeWrap.width((maxWidth + 1) - left);
|
||||||
|
$swipeBar.css('left', left);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})(this));
|
||||||
|
},
|
||||||
|
'onion-skin': function() {
|
||||||
|
var dragTrackWidth, maxHeight, maxWidth;
|
||||||
|
maxWidth = 0;
|
||||||
|
maxHeight = 0;
|
||||||
|
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
||||||
|
return $('.onion-skin.view', this.file).each((function(_this) {
|
||||||
|
return function(index, view) {
|
||||||
|
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
|
||||||
|
ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
||||||
|
$frame = $('.onion-skin-frame', view);
|
||||||
|
$frameAdded = $('.frame.added', view);
|
||||||
|
$track = $('.drag-track', view);
|
||||||
|
$dragger = $('.dragger', $track);
|
||||||
|
|
||||||
|
$frame.css({
|
||||||
|
width: maxWidth + 16,
|
||||||
|
height: maxHeight + 28
|
||||||
|
});
|
||||||
|
$('.swipe-wrap', view).css({
|
||||||
|
width: maxWidth + 1,
|
||||||
|
height: maxHeight + 2
|
||||||
|
});
|
||||||
|
$dragger.css({
|
||||||
|
left: dragTrackWidth
|
||||||
|
});
|
||||||
|
|
||||||
|
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
|
||||||
|
|
||||||
|
_this.initDraggable($dragger, framePadding, function(e, left) {
|
||||||
|
var opacity = left / dragTrackWidth;
|
||||||
|
|
||||||
|
if (opacity >= 0 && opacity <= 1) {
|
||||||
|
$dragger.css('left', left);
|
||||||
|
$frameAdded.css('opacity', opacity);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImageFile.prototype.initViewModes = function() {
|
requestImageInfo(img, callback) {
|
||||||
var viewMode;
|
const domImg = img.get(0);
|
||||||
viewMode = ImageFile.viewModes[0];
|
if (domImg) {
|
||||||
$('.view-modes', this.file).removeClass('hide');
|
if (domImg.complete) {
|
||||||
$('.view-modes-menu', this.file).on('click', 'li', (function(_this) {
|
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
|
||||||
return function(event) {
|
} else {
|
||||||
if (!$(event.currentTarget).hasClass('active')) {
|
return img.on('load', (function(_this) {
|
||||||
return _this.activateViewMode(event.currentTarget.className);
|
return function() {
|
||||||
}
|
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
return this.activateViewMode(viewMode);
|
|
||||||
};
|
|
||||||
|
|
||||||
ImageFile.prototype.activateViewMode = function(viewMode) {
|
|
||||||
$('.view-modes-menu li', this.file).removeClass('active').filter("." + viewMode).addClass('active');
|
|
||||||
return $(".view:visible:not(." + viewMode + ")", this.file).fadeOut(200, (function(_this) {
|
|
||||||
return function() {
|
|
||||||
$(".view." + viewMode, _this.file).fadeIn(200);
|
|
||||||
return _this.initView(viewMode);
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
};
|
|
||||||
|
|
||||||
ImageFile.prototype.initView = function(viewMode) {
|
|
||||||
return this.views[viewMode].call(this);
|
|
||||||
};
|
|
||||||
|
|
||||||
ImageFile.prototype.initDraggable = function($el, padding, callback) {
|
|
||||||
var dragging = false;
|
|
||||||
var $body = $('body');
|
|
||||||
var $offsetEl = $el.parent();
|
|
||||||
|
|
||||||
$el.off('mousedown').on('mousedown', function() {
|
|
||||||
dragging = true;
|
|
||||||
$body.css('user-select', 'none');
|
|
||||||
});
|
|
||||||
|
|
||||||
$body.off('mouseup').off('mousemove').on('mouseup', function() {
|
|
||||||
dragging = false;
|
|
||||||
$body.css('user-select', '');
|
|
||||||
})
|
|
||||||
.on('mousemove', function(e) {
|
|
||||||
var left;
|
|
||||||
if (!dragging) return;
|
|
||||||
|
|
||||||
left = e.pageX - ($offsetEl.offset().left + padding);
|
|
||||||
|
|
||||||
callback(e, left);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
prepareFrames = function(view) {
|
|
||||||
var maxHeight, maxWidth;
|
|
||||||
maxWidth = 0;
|
|
||||||
maxHeight = 0;
|
|
||||||
$('.frame', view).each((function(_this) {
|
|
||||||
return function(index, frame) {
|
|
||||||
var height, width;
|
|
||||||
width = $(frame).width();
|
|
||||||
height = $(frame).height();
|
|
||||||
maxWidth = width > maxWidth ? width : maxWidth;
|
|
||||||
return maxHeight = height > maxHeight ? height : maxHeight;
|
|
||||||
};
|
|
||||||
})(this)).css({
|
|
||||||
width: maxWidth,
|
|
||||||
height: maxHeight
|
|
||||||
});
|
|
||||||
return [maxWidth, maxHeight];
|
|
||||||
};
|
|
||||||
|
|
||||||
ImageFile.prototype.views = {
|
|
||||||
'two-up': function() {
|
|
||||||
return $('.two-up.view .wrap', this.file).each((function(_this) {
|
|
||||||
return function(index, wrap) {
|
|
||||||
$('img', wrap).each(function() {
|
|
||||||
var currentWidth;
|
|
||||||
currentWidth = $(this).width();
|
|
||||||
if (currentWidth > ImageFile.availWidth / 2) {
|
|
||||||
return $(this).width(ImageFile.availWidth / 2);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return _this.requestImageInfo($('img', wrap), function(width, height) {
|
|
||||||
$('.image-info .meta-width', wrap).text(width + "px");
|
|
||||||
$('.image-info .meta-height', wrap).text(height + "px");
|
|
||||||
return $('.image-info', wrap).removeClass('hide');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
},
|
|
||||||
'swipe': function() {
|
|
||||||
var maxHeight, maxWidth;
|
|
||||||
maxWidth = 0;
|
|
||||||
maxHeight = 0;
|
|
||||||
return $('.swipe.view', this.file).each((function(_this) {
|
|
||||||
return function(index, view) {
|
|
||||||
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
|
||||||
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
|
||||||
$swipeFrame = $('.swipe-frame', view);
|
|
||||||
$swipeWrap = $('.swipe-wrap', view);
|
|
||||||
$swipeBar = $('.swipe-bar', view);
|
|
||||||
|
|
||||||
$swipeFrame.css({
|
|
||||||
width: maxWidth + 16,
|
|
||||||
height: maxHeight + 28
|
|
||||||
});
|
|
||||||
$swipeWrap.css({
|
|
||||||
width: maxWidth + 1,
|
|
||||||
height: maxHeight + 2
|
|
||||||
});
|
|
||||||
// Set swipeBar left position to match image frame
|
|
||||||
$swipeBar.css({
|
|
||||||
left: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
|
||||||
|
|
||||||
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
|
|
||||||
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
|
|
||||||
$swipeWrap.width((maxWidth + 1) - left);
|
|
||||||
$swipeBar.css('left', left);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
},
|
|
||||||
'onion-skin': function() {
|
|
||||||
var dragTrackWidth, maxHeight, maxWidth;
|
|
||||||
maxWidth = 0;
|
|
||||||
maxHeight = 0;
|
|
||||||
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
|
||||||
return $('.onion-skin.view', this.file).each((function(_this) {
|
|
||||||
return function(index, view) {
|
|
||||||
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
|
|
||||||
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
|
||||||
$frame = $('.onion-skin-frame', view);
|
|
||||||
$frameAdded = $('.frame.added', view);
|
|
||||||
$track = $('.drag-track', view);
|
|
||||||
$dragger = $('.dragger', $track);
|
|
||||||
|
|
||||||
$frame.css({
|
|
||||||
width: maxWidth + 16,
|
|
||||||
height: maxHeight + 28
|
|
||||||
});
|
|
||||||
$('.swipe-wrap', view).css({
|
|
||||||
width: maxWidth + 1,
|
|
||||||
height: maxHeight + 2
|
|
||||||
});
|
|
||||||
$dragger.css({
|
|
||||||
left: dragTrackWidth
|
|
||||||
});
|
|
||||||
|
|
||||||
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
|
|
||||||
|
|
||||||
_this.initDraggable($dragger, framePadding, function(e, left) {
|
|
||||||
var opacity = left / dragTrackWidth;
|
|
||||||
|
|
||||||
if (opacity >= 0 && opacity <= 1) {
|
|
||||||
$dragger.css('left', left);
|
|
||||||
$frameAdded.css('opacity', opacity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
})(this));
|
})(this));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
}
|
||||||
ImageFile.prototype.requestImageInfo = function(img, callback) {
|
}
|
||||||
var domImg;
|
|
||||||
domImg = img.get(0);
|
|
||||||
if (domImg) {
|
|
||||||
if (domImg.complete) {
|
|
||||||
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
|
|
||||||
} else {
|
|
||||||
return img.on('load', (function(_this) {
|
|
||||||
return function() {
|
|
||||||
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
|
|
||||||
};
|
|
||||||
})(this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return ImageFile;
|
|
||||||
})();
|
|
||||||
}).call(window);
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
/* global Pager */
|
/* global Pager */
|
||||||
|
|
||||||
import { pluralize } from './lib/utils/text_utility';
|
import { pluralize } from './lib/utils/text_utility';
|
||||||
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
export default (function () {
|
export default (function () {
|
||||||
const CommitsList = {};
|
const CommitsList = {};
|
||||||
|
|
@ -91,7 +92,7 @@ export default (function () {
|
||||||
$commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
|
$commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.utils.localTimeAgo($processedData.find('.js-timeago'));
|
localTimeAgo($processedData.find('.js-timeago'));
|
||||||
|
|
||||||
return processedData;
|
return processedData;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ import './polyfills';
|
||||||
import './jquery';
|
import './jquery';
|
||||||
import './bootstrap';
|
import './bootstrap';
|
||||||
import './vue';
|
import './vue';
|
||||||
|
import '../lib/utils/axios_utils';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
|
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
|
||||||
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
window.Compare = (function() {
|
export default class Compare {
|
||||||
function Compare(opts) {
|
constructor(opts) {
|
||||||
this.opts = opts;
|
this.opts = opts;
|
||||||
this.source_loading = $(".js-source-loading");
|
this.source_loading = $(".js-source-loading");
|
||||||
this.target_loading = $(".js-target-loading");
|
this.target_loading = $(".js-target-loading");
|
||||||
|
|
@ -34,12 +35,12 @@ window.Compare = (function() {
|
||||||
this.initialState();
|
this.initialState();
|
||||||
}
|
}
|
||||||
|
|
||||||
Compare.prototype.initialState = function() {
|
initialState() {
|
||||||
this.getSourceHtml();
|
this.getSourceHtml();
|
||||||
return this.getTargetHtml();
|
this.getTargetHtml();
|
||||||
};
|
}
|
||||||
|
|
||||||
Compare.prototype.getTargetProject = function() {
|
getTargetProject() {
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url: this.opts.targetProjectUrl,
|
url: this.opts.targetProjectUrl,
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -52,22 +53,22 @@ window.Compare = (function() {
|
||||||
return $('.js-target-branch-dropdown .dropdown-content').html(html);
|
return $('.js-target-branch-dropdown .dropdown-content').html(html);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Compare.prototype.getSourceHtml = function() {
|
getSourceHtml() {
|
||||||
return this.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', {
|
return this.constructor.sendAjax(this.opts.sourceBranchUrl, this.source_loading, '.mr_source_commit', {
|
||||||
ref: $("input[name='merge_request[source_branch]']").val()
|
ref: $("input[name='merge_request[source_branch]']").val()
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Compare.prototype.getTargetHtml = function() {
|
getTargetHtml() {
|
||||||
return this.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', {
|
return this.constructor.sendAjax(this.opts.targetBranchUrl, this.target_loading, '.mr_target_commit', {
|
||||||
target_project_id: $("input[name='merge_request[target_project_id]']").val(),
|
target_project_id: $("input[name='merge_request[target_project_id]']").val(),
|
||||||
ref: $("input[name='merge_request[target_branch]']").val()
|
ref: $("input[name='merge_request[target_branch]']").val()
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
Compare.prototype.sendAjax = function(url, loading, target, data) {
|
static sendAjax(url, loading, target, data) {
|
||||||
var $target;
|
var $target;
|
||||||
$target = $(target);
|
$target = $(target);
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
|
|
@ -81,10 +82,8 @@ window.Compare = (function() {
|
||||||
loading.hide();
|
loading.hide();
|
||||||
$target.html(html);
|
$target.html(html);
|
||||||
var className = '.' + $target[0].className.replace(' ', '.');
|
var className = '.' + $target[0].className.replace(' ', '.');
|
||||||
gl.utils.localTimeAgo($('.js-timeago', className));
|
localTimeAgo($('.js-timeago', className));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}
|
||||||
return Compare;
|
|
||||||
})();
|
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,60 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */
|
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */
|
||||||
|
|
||||||
window.CompareAutocomplete = (function() {
|
export default function initCompareAutocomplete() {
|
||||||
function CompareAutocomplete() {
|
$('.js-compare-dropdown').each(function() {
|
||||||
this.initDropdown();
|
var $dropdown, selected;
|
||||||
}
|
$dropdown = $(this);
|
||||||
|
selected = $dropdown.data('selected');
|
||||||
CompareAutocomplete.prototype.initDropdown = function() {
|
const $dropdownContainer = $dropdown.closest('.dropdown');
|
||||||
return $('.js-compare-dropdown').each(function() {
|
const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
|
||||||
var $dropdown, selected;
|
const $filterInput = $('input[type="search"]', $dropdownContainer);
|
||||||
$dropdown = $(this);
|
$dropdown.glDropdown({
|
||||||
selected = $dropdown.data('selected');
|
data: function(term, callback) {
|
||||||
const $dropdownContainer = $dropdown.closest('.dropdown');
|
return $.ajax({
|
||||||
const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
|
url: $dropdown.data('refs-url'),
|
||||||
const $filterInput = $('input[type="search"]', $dropdownContainer);
|
data: {
|
||||||
$dropdown.glDropdown({
|
ref: $dropdown.data('ref'),
|
||||||
data: function(term, callback) {
|
search: term,
|
||||||
return $.ajax({
|
|
||||||
url: $dropdown.data('refs-url'),
|
|
||||||
data: {
|
|
||||||
ref: $dropdown.data('ref'),
|
|
||||||
search: term,
|
|
||||||
}
|
|
||||||
}).done(function(refs) {
|
|
||||||
return callback(refs);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
selectable: true,
|
|
||||||
filterable: true,
|
|
||||||
filterRemote: true,
|
|
||||||
fieldName: $dropdown.data('field-name'),
|
|
||||||
filterInput: 'input[type="search"]',
|
|
||||||
renderRow: function(ref) {
|
|
||||||
var link;
|
|
||||||
if (ref.header != null) {
|
|
||||||
return $('<li />').addClass('dropdown-header').text(ref.header);
|
|
||||||
} else {
|
|
||||||
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
|
|
||||||
return $('<li />').append(link);
|
|
||||||
}
|
}
|
||||||
},
|
}).done(function(refs) {
|
||||||
id: function(obj, $el) {
|
return callback(refs);
|
||||||
return $el.attr('data-ref');
|
});
|
||||||
},
|
},
|
||||||
toggleLabel: function(obj, $el) {
|
selectable: true,
|
||||||
return $el.text().trim();
|
filterable: true,
|
||||||
|
filterRemote: true,
|
||||||
|
fieldName: $dropdown.data('field-name'),
|
||||||
|
filterInput: 'input[type="search"]',
|
||||||
|
renderRow: function(ref) {
|
||||||
|
var link;
|
||||||
|
if (ref.header != null) {
|
||||||
|
return $('<li />').addClass('dropdown-header').text(ref.header);
|
||||||
|
} else {
|
||||||
|
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', escape(ref));
|
||||||
|
return $('<li />').append(link);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
$filterInput.on('keyup', (e) => {
|
id: function(obj, $el) {
|
||||||
const keyCode = e.keyCode || e.which;
|
return $el.attr('data-ref');
|
||||||
if (keyCode !== 13) return;
|
},
|
||||||
const text = $filterInput.val();
|
toggleLabel: function(obj, $el) {
|
||||||
$fieldInput.val(text);
|
return $el.text().trim();
|
||||||
$('.dropdown-toggle-text', $dropdown).text(text);
|
}
|
||||||
$dropdownContainer.removeClass('open');
|
});
|
||||||
});
|
$filterInput.on('keyup', (e) => {
|
||||||
|
const keyCode = e.keyCode || e.which;
|
||||||
$dropdownContainer.on('click', '.dropdown-content a', (e) => {
|
if (keyCode !== 13) return;
|
||||||
$dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
|
const text = $filterInput.val();
|
||||||
if ($dropdown.hasClass('has-tooltip')) {
|
$fieldInput.val(text);
|
||||||
$dropdown.tooltip('fixTitle');
|
$('.dropdown-toggle-text', $dropdown).text(text);
|
||||||
}
|
$dropdownContainer.removeClass('open');
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
return CompareAutocomplete;
|
$dropdownContainer.on('click', '.dropdown-content a', (e) => {
|
||||||
})();
|
$dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
|
||||||
|
if ($dropdown.hasClass('has-tooltip')) {
|
||||||
|
$dropdown.tooltip('fixTitle');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default class ContextualSidebar {
|
||||||
}
|
}
|
||||||
|
|
||||||
initDomElements() {
|
initDomElements() {
|
||||||
this.$page = $('.page-with-sidebar');
|
this.$page = $('.layout-page');
|
||||||
this.$sidebar = $('.nav-sidebar');
|
this.$sidebar = $('.nav-sidebar');
|
||||||
this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar);
|
this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar);
|
||||||
this.$overlay = $('.mobile-overlay');
|
this.$overlay = $('.mobile-overlay');
|
||||||
|
|
@ -28,7 +28,7 @@ export default class ContextualSidebar {
|
||||||
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
|
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
|
||||||
this.$overlay.on('click', () => this.toggleSidebarNav(false));
|
this.$overlay.on('click', () => this.toggleSidebarNav(false));
|
||||||
this.$sidebarToggle.on('click', () => {
|
this.$sidebarToggle.on('click', () => {
|
||||||
const value = !this.$sidebar.hasClass('sidebar-icons-only');
|
const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop');
|
||||||
this.toggleCollapsedSidebar(value);
|
this.toggleCollapsedSidebar(value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -43,16 +43,16 @@ export default class ContextualSidebar {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSidebarNav(show) {
|
toggleSidebarNav(show) {
|
||||||
this.$sidebar.toggleClass('nav-sidebar-expanded', show);
|
this.$sidebar.toggleClass('sidebar-expanded-mobile', show);
|
||||||
this.$overlay.toggleClass('mobile-nav-open', show);
|
this.$overlay.toggleClass('mobile-nav-open', show);
|
||||||
this.$sidebar.removeClass('sidebar-icons-only');
|
this.$sidebar.removeClass('sidebar-collapsed-desktop');
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapsedSidebar(collapsed) {
|
toggleCollapsedSidebar(collapsed) {
|
||||||
const breakpoint = bp.getBreakpointSize();
|
const breakpoint = bp.getBreakpointSize();
|
||||||
|
|
||||||
if (this.$sidebar.length) {
|
if (this.$sidebar.length) {
|
||||||
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
|
this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed);
|
||||||
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
|
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
|
||||||
}
|
}
|
||||||
ContextualSidebar.setCollapsedCookie(collapsed);
|
ContextualSidebar.setCollapsedCookie(collapsed);
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, prefer-arrow-callback, max-len */
|
|
||||||
|
|
||||||
import Clipboard from 'vendor/clipboard';
|
|
||||||
|
|
||||||
var genericError, genericSuccess, showTooltip;
|
|
||||||
|
|
||||||
genericSuccess = function(e) {
|
|
||||||
showTooltip(e.trigger, 'Copied');
|
|
||||||
// Clear the selection and blur the trigger so it loses its border
|
|
||||||
e.clearSelection();
|
|
||||||
return $(e.trigger).blur();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Safari doesn't support `execCommand`, so instead we inform the user to
|
|
||||||
// copy manually.
|
|
||||||
//
|
|
||||||
// See http://clipboardjs.com/#browser-support
|
|
||||||
genericError = function(e) {
|
|
||||||
var key;
|
|
||||||
if (/Mac/i.test(navigator.userAgent)) {
|
|
||||||
key = '⌘'; // Command
|
|
||||||
} else {
|
|
||||||
key = 'Ctrl';
|
|
||||||
}
|
|
||||||
return showTooltip(e.trigger, "Press " + key + "-C to copy");
|
|
||||||
};
|
|
||||||
|
|
||||||
showTooltip = function(target, title) {
|
|
||||||
var $target = $(target);
|
|
||||||
var originalTitle = $target.data('original-title');
|
|
||||||
|
|
||||||
if (!$target.data('hideTooltip')) {
|
|
||||||
$target
|
|
||||||
.attr('title', 'Copied')
|
|
||||||
.tooltip('fixTitle')
|
|
||||||
.tooltip('show')
|
|
||||||
.attr('title', originalTitle)
|
|
||||||
.tooltip('fixTitle');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
const clipboard = new Clipboard('[data-clipboard-target], [data-clipboard-text]');
|
|
||||||
clipboard.on('success', genericSuccess);
|
|
||||||
clipboard.on('error', genericError);
|
|
||||||
|
|
||||||
// This a workaround around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
|
|
||||||
// The Ruby `clipboard_button` helper sneaks a JSON hash with `text` and `gfm` keys into the `data-clipboard-text`
|
|
||||||
// attribute that ClipboardJS reads from.
|
|
||||||
// When ClipboardJS creates a new `textarea` (directly inside `body`, with a `readonly` attribute`), sets its value
|
|
||||||
// to the value of this data attribute, focusses on it, and finally programmatically issues the 'Copy' command,
|
|
||||||
// this code intercepts the copy command/event at the last minute to deconstruct this JSON hash and set the
|
|
||||||
// `text/plain` and `text/x-gfm` copy data types to the intended values.
|
|
||||||
$(document).on('copy', 'body > textarea[readonly]', function(e) {
|
|
||||||
const clipboardData = e.originalEvent.clipboardData;
|
|
||||||
if (!clipboardData) return;
|
|
||||||
|
|
||||||
const text = e.target.value;
|
|
||||||
|
|
||||||
let json;
|
|
||||||
try {
|
|
||||||
json = JSON.parse(text);
|
|
||||||
} catch (ex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json.text || !json.gfm) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
clipboardData.setData('text/plain', json.text);
|
|
||||||
clipboardData.setData('text/x-gfm', json.gfm);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -32,7 +32,9 @@
|
||||||
doAction() {
|
doAction() {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
|
||||||
eventHub.$emit(`${this.type}.key`, this.deployKey);
|
eventHub.$emit(`${this.type}.key`, this.deployKey, () => {
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -50,6 +52,9 @@
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
@click="doAction">
|
@click="doAction">
|
||||||
{{ text }}
|
{{ text }}
|
||||||
<loading-icon v-if="isLoading" />
|
<loading-icon
|
||||||
|
v-if="isLoading"
|
||||||
|
:inline="true"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,15 @@
|
||||||
.then(() => this.fetchKeys())
|
.then(() => this.fetchKeys())
|
||||||
.catch(() => new Flash('Error enabling deploy key'));
|
.catch(() => new Flash('Error enabling deploy key'));
|
||||||
},
|
},
|
||||||
disableKey(deployKey) {
|
disableKey(deployKey, callback) {
|
||||||
// eslint-disable-next-line no-alert
|
// eslint-disable-next-line no-alert
|
||||||
if (confirm('You are going to remove this deploy key. Are you sure?')) {
|
if (confirm('You are going to remove this deploy key. Are you sure?')) {
|
||||||
this.service.disableKey(deployKey.id)
|
this.service.disableKey(deployKey.id)
|
||||||
.then(() => this.fetchKeys())
|
.then(() => this.fetchKeys())
|
||||||
|
.then(callback)
|
||||||
.catch(() => new Flash('Error removing deploy key'));
|
.catch(() => new Flash('Error removing deploy key'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import actionBtn from './action_btn.vue';
|
import actionBtn from './action_btn.vue';
|
||||||
|
import { getTimeago } from '../../lib/utils/datetime_utility';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -21,7 +22,7 @@
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
timeagoDate() {
|
timeagoDate() {
|
||||||
return gl.utils.getTimeago().format(this.deployKey.created_at);
|
return getTimeago().format(this.deployKey.created_at);
|
||||||
},
|
},
|
||||||
editDeployKeyPath() {
|
editDeployKeyPath() {
|
||||||
return `${this.endpoint}/${this.deployKey.id}/edit`;
|
return `${this.endpoint}/${this.deployKey.id}/edit`;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import './lib/utils/url_utility';
|
import { getLocationHash } from './lib/utils/url_utility';
|
||||||
import FilesCommentButton from './files_comment_button';
|
import FilesCommentButton from './files_comment_button';
|
||||||
import SingleFileDiff from './single_file_diff';
|
import SingleFileDiff from './single_file_diff';
|
||||||
import imageDiffHelper from './image_diff/helpers/index';
|
import imageDiffHelper from './image_diff/helpers/index';
|
||||||
|
|
@ -31,7 +31,7 @@ export default class Diff {
|
||||||
isBound = true;
|
isBound = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gl.utils.getLocationHash()) {
|
if (getLocationHash()) {
|
||||||
this.highlightSelectedLine();
|
this.highlightSelectedLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +73,7 @@ export default class Diff {
|
||||||
}
|
}
|
||||||
|
|
||||||
openAnchoredDiff(cb) {
|
openAnchoredDiff(cb) {
|
||||||
const locationHash = gl.utils.getLocationHash();
|
const locationHash = getLocationHash();
|
||||||
const anchoredDiff = locationHash && locationHash.split('_')[0];
|
const anchoredDiff = locationHash && locationHash.split('_')[0];
|
||||||
|
|
||||||
if (!anchoredDiff) return;
|
if (!anchoredDiff) return;
|
||||||
|
|
@ -128,7 +128,7 @@ export default class Diff {
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line class-methods-use-this
|
// eslint-disable-next-line class-methods-use-this
|
||||||
highlightSelectedLine() {
|
highlightSelectedLine() {
|
||||||
const hash = gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
const $diffFiles = $('.diff-file');
|
const $diffFiles = $('.diff-file');
|
||||||
$diffFiles.find('.hll').removeClass('hll');
|
$diffFiles.find('.hll').removeClass('hll');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ import './components/diff_note_avatars';
|
||||||
import './components/new_issue_for_discussion';
|
import './components/new_issue_for_discussion';
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
const projectPath = document.querySelector('.merge-request').dataset.projectPath;
|
const projectPathHolder = document.querySelector('.merge-request') || document.querySelector('.commit-box');
|
||||||
|
const projectPath = projectPathHolder.dataset.projectPath;
|
||||||
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn';
|
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn';
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
window.gl = window.gl || {};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* global NoteModel */
|
/* global NoteModel */
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { localTimeAgo } from '../../lib/utils/datetime_utility';
|
||||||
|
|
||||||
class DiscussionModel {
|
class DiscussionModel {
|
||||||
constructor (discussionId) {
|
constructor (discussionId) {
|
||||||
|
|
@ -71,7 +72,7 @@ class DiscussionModel {
|
||||||
$(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html);
|
$(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`));
|
localTimeAgo($('.js-timeago', `${discussionSelector}`));
|
||||||
} else {
|
} else {
|
||||||
$discussionHeadline.remove();
|
$discussionHeadline.remove();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ class ResolveServiceClass {
|
||||||
discussion.resolveAllNotes(resolvedBy);
|
discussion.resolveAllNotes(resolvedBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.mrWidget.checkStatus();
|
if (gl.mrWidget) gl.mrWidget.checkStatus();
|
||||||
discussion.updateHeadline(data);
|
discussion.updateHeadline(data);
|
||||||
})
|
})
|
||||||
.catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.'));
|
.catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.'));
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,22 @@ import GroupLabelSubscription from './group_label_subscription';
|
||||||
import BuildArtifacts from './build_artifacts';
|
import BuildArtifacts from './build_artifacts';
|
||||||
import CILintEditor from './ci_lint_editor';
|
import CILintEditor from './ci_lint_editor';
|
||||||
import groupsSelect from './groups_select';
|
import groupsSelect from './groups_select';
|
||||||
/* global Search */
|
import Search from './search';
|
||||||
/* global Admin */
|
import initAdmin from './admin';
|
||||||
import NamespaceSelect from './namespace_select';
|
import NamespaceSelect from './namespace_select';
|
||||||
import NewCommitForm from './new_commit_form';
|
import NewCommitForm from './new_commit_form';
|
||||||
import Project from './project';
|
import Project from './project';
|
||||||
import projectAvatar from './project_avatar';
|
import projectAvatar from './project_avatar';
|
||||||
/* global MergeRequest */
|
/* global MergeRequest */
|
||||||
/* global Compare */
|
import Compare from './compare';
|
||||||
/* global CompareAutocomplete */
|
import initCompareAutocomplete from './compare_autocomplete';
|
||||||
/* global ProjectFindFile */
|
import ProjectFindFile from './project_find_file';
|
||||||
import ProjectNew from './project_new';
|
import ProjectNew from './project_new';
|
||||||
import projectImport from './project_import';
|
import projectImport from './project_import';
|
||||||
import Labels from './labels';
|
import Labels from './labels';
|
||||||
import LabelManager from './label_manager';
|
import LabelManager from './label_manager';
|
||||||
/* global Sidebar */
|
/* global Sidebar */
|
||||||
|
import IssuableTemplateSelectors from './templates/issuable_template_selectors';
|
||||||
import Flash from './flash';
|
import Flash from './flash';
|
||||||
import CommitsList from './commits';
|
import CommitsList from './commits';
|
||||||
import Issue from './issue';
|
import Issue from './issue';
|
||||||
|
|
@ -91,6 +91,8 @@ import DueDateSelectors from './due_date_select';
|
||||||
import Diff from './diff';
|
import Diff from './diff';
|
||||||
import ProjectLabelSubscription from './project_label_subscription';
|
import ProjectLabelSubscription from './project_label_subscription';
|
||||||
import ProjectVariables from './project_variables';
|
import ProjectVariables from './project_variables';
|
||||||
|
import SearchAutocomplete from './search_autocomplete';
|
||||||
|
import Activities from './activities';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var Dispatcher;
|
var Dispatcher;
|
||||||
|
|
@ -264,7 +266,7 @@ import ProjectVariables from './project_variables';
|
||||||
new IssuableForm($('.issue-form'));
|
new IssuableForm($('.issue-form'));
|
||||||
new LabelsSelect();
|
new LabelsSelect();
|
||||||
new MilestoneSelect();
|
new MilestoneSelect();
|
||||||
new gl.IssuableTemplateSelectors();
|
new IssuableTemplateSelectors();
|
||||||
break;
|
break;
|
||||||
case 'projects:merge_requests:creations:new':
|
case 'projects:merge_requests:creations:new':
|
||||||
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
|
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
|
||||||
|
|
@ -288,7 +290,7 @@ import ProjectVariables from './project_variables';
|
||||||
new IssuableForm($('.merge-request-form'));
|
new IssuableForm($('.merge-request-form'));
|
||||||
new LabelsSelect();
|
new LabelsSelect();
|
||||||
new MilestoneSelect();
|
new MilestoneSelect();
|
||||||
new gl.IssuableTemplateSelectors();
|
new IssuableTemplateSelectors();
|
||||||
new AutoWidthDropdownSelect($('.js-target-branch-select')).init();
|
new AutoWidthDropdownSelect($('.js-target-branch-select')).init();
|
||||||
break;
|
break;
|
||||||
case 'projects:tags:new':
|
case 'projects:tags:new':
|
||||||
|
|
@ -298,18 +300,21 @@ import ProjectVariables from './project_variables';
|
||||||
break;
|
break;
|
||||||
case 'projects:snippets:show':
|
case 'projects:snippets:show':
|
||||||
initNotes();
|
initNotes();
|
||||||
|
new ZenMode();
|
||||||
break;
|
break;
|
||||||
case 'projects:snippets:new':
|
case 'projects:snippets:new':
|
||||||
case 'projects:snippets:edit':
|
case 'projects:snippets:edit':
|
||||||
case 'projects:snippets:create':
|
case 'projects:snippets:create':
|
||||||
case 'projects:snippets:update':
|
case 'projects:snippets:update':
|
||||||
new GLForm($('.snippet-form'), true);
|
new GLForm($('.snippet-form'), true);
|
||||||
|
new ZenMode();
|
||||||
break;
|
break;
|
||||||
case 'snippets:new':
|
case 'snippets:new':
|
||||||
case 'snippets:edit':
|
case 'snippets:edit':
|
||||||
case 'snippets:create':
|
case 'snippets:create':
|
||||||
case 'snippets:update':
|
case 'snippets:update':
|
||||||
new GLForm($('.snippet-form'), false);
|
new GLForm($('.snippet-form'), false);
|
||||||
|
new ZenMode();
|
||||||
break;
|
break;
|
||||||
case 'projects:releases:edit':
|
case 'projects:releases:edit':
|
||||||
new ZenMode();
|
new ZenMode();
|
||||||
|
|
@ -330,7 +335,7 @@ import ProjectVariables from './project_variables';
|
||||||
shortcut_handler = new ShortcutsIssuable(true);
|
shortcut_handler = new ShortcutsIssuable(true);
|
||||||
break;
|
break;
|
||||||
case 'dashboard:activity':
|
case 'dashboard:activity':
|
||||||
new gl.Activities();
|
new Activities();
|
||||||
break;
|
break;
|
||||||
case 'projects:commit:show':
|
case 'projects:commit:show':
|
||||||
new Diff();
|
new Diff();
|
||||||
|
|
@ -351,7 +356,7 @@ import ProjectVariables from './project_variables';
|
||||||
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
|
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
|
||||||
break;
|
break;
|
||||||
case 'projects:activity':
|
case 'projects:activity':
|
||||||
new gl.Activities();
|
new Activities();
|
||||||
shortcut_handler = new ShortcutsNavigation();
|
shortcut_handler = new ShortcutsNavigation();
|
||||||
break;
|
break;
|
||||||
case 'projects:commits:show':
|
case 'projects:commits:show':
|
||||||
|
|
@ -369,7 +374,7 @@ import ProjectVariables from './project_variables';
|
||||||
|
|
||||||
if ($('#tree-slider').length) new TreeView();
|
if ($('#tree-slider').length) new TreeView();
|
||||||
if ($('.blob-viewer').length) new BlobViewer();
|
if ($('.blob-viewer').length) new BlobViewer();
|
||||||
if ($('.project-show-activity').length) new gl.Activities();
|
if ($('.project-show-activity').length) new Activities();
|
||||||
$('#tree-slider').waitForImages(function() {
|
$('#tree-slider').waitForImages(function() {
|
||||||
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
|
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
|
||||||
});
|
});
|
||||||
|
|
@ -403,7 +408,7 @@ import ProjectVariables from './project_variables';
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'groups:activity':
|
case 'groups:activity':
|
||||||
new gl.Activities();
|
new Activities();
|
||||||
break;
|
break;
|
||||||
case 'groups:show':
|
case 'groups:show':
|
||||||
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
|
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
|
||||||
|
|
@ -522,13 +527,6 @@ import ProjectVariables from './project_variables';
|
||||||
case 'projects:settings:ci_cd:show':
|
case 'projects:settings:ci_cd:show':
|
||||||
// Initialize expandable settings panels
|
// Initialize expandable settings panels
|
||||||
initSettingsPanels();
|
initSettingsPanels();
|
||||||
|
|
||||||
import(/* webpackChunkName: "ci-cd-settings" */ './projects/ci_cd_settings_bundle')
|
|
||||||
.then(ciCdSettings => ciCdSettings.default())
|
|
||||||
.catch((err) => {
|
|
||||||
Flash(s__('ProjectSettings|Problem setting up the CI/CD settings JavaScript'));
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
case 'groups:settings:ci_cd:show':
|
case 'groups:settings:ci_cd:show':
|
||||||
new ProjectVariables();
|
new ProjectVariables();
|
||||||
break;
|
break;
|
||||||
|
|
@ -546,6 +544,7 @@ import ProjectVariables from './project_variables';
|
||||||
new LineHighlighter();
|
new LineHighlighter();
|
||||||
new BlobViewer();
|
new BlobViewer();
|
||||||
initNotes();
|
initNotes();
|
||||||
|
new ZenMode();
|
||||||
break;
|
break;
|
||||||
case 'import:fogbugz:new_user_map':
|
case 'import:fogbugz:new_user_map':
|
||||||
new UsersSelect();
|
new UsersSelect();
|
||||||
|
|
@ -558,7 +557,15 @@ import ProjectVariables from './project_variables';
|
||||||
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
|
import(/* webpackChunkName: "clusters" */ './clusters/clusters_bundle')
|
||||||
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
|
.then(cluster => new cluster.default()) // eslint-disable-line new-cap
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
Flash(s__('ClusterIntegration|Problem setting up the cluster JavaScript'));
|
Flash(s__('ClusterIntegration|Problem setting up the cluster'));
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'projects:clusters:index':
|
||||||
|
import(/* webpackChunkName: "clusters_index" */ './clusters/clusters_index')
|
||||||
|
.then(clusterIndex => clusterIndex.default())
|
||||||
|
.catch((err) => {
|
||||||
|
Flash(s__('ClusterIntegration|Problem setting up the clusters list'));
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
@ -578,7 +585,7 @@ import ProjectVariables from './project_variables';
|
||||||
// needed in rspec
|
// needed in rspec
|
||||||
gl.u2fAuthenticate = u2fAuthenticate;
|
gl.u2fAuthenticate = u2fAuthenticate;
|
||||||
case 'admin':
|
case 'admin':
|
||||||
new Admin();
|
initAdmin();
|
||||||
switch (path[1]) {
|
switch (path[1]) {
|
||||||
case 'broadcast_messages':
|
case 'broadcast_messages':
|
||||||
initBroadcastMessagesForm();
|
initBroadcastMessagesForm();
|
||||||
|
|
@ -617,7 +624,7 @@ import ProjectVariables from './project_variables';
|
||||||
projectAvatar();
|
projectAvatar();
|
||||||
switch (path[1]) {
|
switch (path[1]) {
|
||||||
case 'compare':
|
case 'compare':
|
||||||
new CompareAutocomplete();
|
initCompareAutocomplete();
|
||||||
break;
|
break;
|
||||||
case 'edit':
|
case 'edit':
|
||||||
shortcut_handler = new ShortcutsNavigation();
|
shortcut_handler = new ShortcutsNavigation();
|
||||||
|
|
@ -678,7 +685,7 @@ import ProjectVariables from './project_variables';
|
||||||
Dispatcher.prototype.initSearch = function() {
|
Dispatcher.prototype.initSearch = function() {
|
||||||
// Only when search form is present
|
// Only when search form is present
|
||||||
if ($('.search').length) {
|
if ($('.search').length) {
|
||||||
return new gl.SearchAutocomplete();
|
return new SearchAutocomplete();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { visitUrl } from '../lib/utils/url_utility';
|
||||||
import Flash from '../flash';
|
import Flash from '../flash';
|
||||||
import FilteredSearchContainer from './container';
|
import FilteredSearchContainer from './container';
|
||||||
import RecentSearchesRoot from './recent_searches_root';
|
import RecentSearchesRoot from './recent_searches_root';
|
||||||
|
|
@ -566,7 +567,7 @@ class FilteredSearchManager {
|
||||||
if (this.updateObject) {
|
if (this.updateObject) {
|
||||||
this.updateObject(parameterizedUrl);
|
this.updateObject(parameterizedUrl);
|
||||||
} else {
|
} else {
|
||||||
gl.utils.visitUrl(parameterizedUrl);
|
visitUrl(parameterizedUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ let headerHeight = 50;
|
||||||
|
|
||||||
export const getHeaderHeight = () => headerHeight;
|
export const getHeaderHeight = () => headerHeight;
|
||||||
|
|
||||||
export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-icons-only');
|
export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop');
|
||||||
|
|
||||||
export const canShowActiveSubItems = (el) => {
|
export const canShowActiveSubItems = (el) => {
|
||||||
if (el.classList.contains('active') && !isSidebarCollapsed()) {
|
if (el.classList.contains('active') && !isSidebarCollapsed()) {
|
||||||
|
|
|
||||||
|
|
@ -287,6 +287,10 @@ class GfmAutoComplete {
|
||||||
}
|
}
|
||||||
|
|
||||||
setupLabels($input) {
|
setupLabels($input) {
|
||||||
|
const fetchData = this.fetchData.bind(this);
|
||||||
|
const LABEL_COMMAND = { LABEL: '/label', UNLABEL: '/unlabel', RELABEL: '/relabel' };
|
||||||
|
let command = '';
|
||||||
|
|
||||||
$input.atwho({
|
$input.atwho({
|
||||||
at: '~',
|
at: '~',
|
||||||
alias: 'labels',
|
alias: 'labels',
|
||||||
|
|
@ -309,8 +313,45 @@ class GfmAutoComplete {
|
||||||
title: sanitize(m.title),
|
title: sanitize(m.title),
|
||||||
color: m.color,
|
color: m.color,
|
||||||
search: m.title,
|
search: m.title,
|
||||||
|
set: m.set,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
matcher(flag, subtext) {
|
||||||
|
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
|
||||||
|
const subtextNodes = subtext.split(/\n+/g).pop().split(GfmAutoComplete.regexSubtext);
|
||||||
|
|
||||||
|
// Check if ~ is followed by '/label', '/relabel' or '/unlabel' commands.
|
||||||
|
command = subtextNodes.find((node) => {
|
||||||
|
if (node === LABEL_COMMAND.LABEL ||
|
||||||
|
node === LABEL_COMMAND.RELABEL ||
|
||||||
|
node === LABEL_COMMAND.UNLABEL) { return node; }
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return match && match.length ? match[1] : null;
|
||||||
|
},
|
||||||
|
filter(query, data, searchKey) {
|
||||||
|
if (GfmAutoComplete.isLoading(data)) {
|
||||||
|
fetchData(this.$inputor, this.at);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data === GfmAutoComplete.defaultLoadingData) {
|
||||||
|
return $.fn.atwho.default.callbacks.filter(query, data, searchKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `LABEL_COMMAND.RELABEL` is intentionally skipped
|
||||||
|
// because we want to return all the labels (unfiltered) for that command.
|
||||||
|
if (command === LABEL_COMMAND.LABEL) {
|
||||||
|
// Return labels with set: undefined.
|
||||||
|
return data.filter(label => !label.set);
|
||||||
|
} else if (command === LABEL_COMMAND.UNLABEL) {
|
||||||
|
// Return labels with set: true.
|
||||||
|
return data.filter(label => label.set);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -346,20 +387,7 @@ class GfmAutoComplete {
|
||||||
return resultantValue;
|
return resultantValue;
|
||||||
},
|
},
|
||||||
matcher(flag, subtext) {
|
matcher(flag, subtext) {
|
||||||
// The below is taken from At.js source
|
const match = GfmAutoComplete.defaultMatcher(flag, subtext, this.app.controllers);
|
||||||
// Tweaked to commands to start without a space only if char before is a non-word character
|
|
||||||
// https://github.com/ichord/At.js
|
|
||||||
const atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
|
|
||||||
const atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
|
|
||||||
const targetSubtext = subtext.split(/\s+/g).pop();
|
|
||||||
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
||||||
|
|
||||||
const accentAChar = decodeURI('%C3%80');
|
|
||||||
const accentYChar = decodeURI('%C3%BF');
|
|
||||||
|
|
||||||
const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi');
|
|
||||||
|
|
||||||
const match = regexp.exec(targetSubtext);
|
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return match[1];
|
return match[1];
|
||||||
|
|
@ -420,8 +448,27 @@ class GfmAutoComplete {
|
||||||
return dataToInspect &&
|
return dataToInspect &&
|
||||||
(dataToInspect === loadingState || dataToInspect.name === loadingState);
|
(dataToInspect === loadingState || dataToInspect.name === loadingState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static defaultMatcher(flag, subtext, controllers) {
|
||||||
|
// The below is taken from At.js source
|
||||||
|
// Tweaked to commands to start without a space only if char before is a non-word character
|
||||||
|
// https://github.com/ichord/At.js
|
||||||
|
const atSymbolsWithBar = Object.keys(controllers).join('|');
|
||||||
|
const atSymbolsWithoutBar = Object.keys(controllers).join('');
|
||||||
|
const targetSubtext = subtext.split(GfmAutoComplete.regexSubtext).pop();
|
||||||
|
const resultantFlag = flag.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
||||||
|
|
||||||
|
const accentAChar = decodeURI('%C3%80');
|
||||||
|
const accentYChar = decodeURI('%C3%BF');
|
||||||
|
|
||||||
|
const regexp = new RegExp(`^(?:\\B|[^a-zA-Z0-9_${atSymbolsWithoutBar}]|\\s)${resultantFlag}(?!${atSymbolsWithBar})((?:[A-Za-z${accentAChar}-${accentYChar}0-9_'.+-]|[^\\x00-\\x7a])*)$`, 'gi');
|
||||||
|
|
||||||
|
return regexp.exec(targetSubtext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GfmAutoComplete.regexSubtext = new RegExp(/\s+/g);
|
||||||
|
|
||||||
GfmAutoComplete.defaultLoadingData = ['loading'];
|
GfmAutoComplete.defaultLoadingData = ['loading'];
|
||||||
|
|
||||||
GfmAutoComplete.atTypeMap = {
|
GfmAutoComplete.atTypeMap = {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* global fuzzaldrinPlus */
|
/* global fuzzaldrinPlus */
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
||||||
|
import { visitUrl } from './lib/utils/url_utility';
|
||||||
import { isObject } from './lib/utils/type_utility';
|
import { isObject } from './lib/utils/type_utility';
|
||||||
|
|
||||||
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput;
|
var GitLabDropdown, GitLabDropdownFilter, GitLabDropdownRemote, GitLabDropdownInput;
|
||||||
|
|
@ -514,10 +515,11 @@ GitLabDropdown = (function() {
|
||||||
|
|
||||||
const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle');
|
const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle');
|
||||||
const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update');
|
const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update');
|
||||||
|
const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open');
|
||||||
const hasMultiSelect = dropdownToggle.hasClass('js-multiselect');
|
const hasMultiSelect = dropdownToggle.hasClass('js-multiselect');
|
||||||
|
|
||||||
// Makes indeterminate items effective
|
// Makes indeterminate items effective
|
||||||
if (this.fullData && hasFilterBulkUpdate) {
|
if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) {
|
||||||
this.parseData(this.fullData);
|
this.parseData(this.fullData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,7 +853,7 @@ GitLabDropdown = (function() {
|
||||||
if ($el.length) {
|
if ($el.length) {
|
||||||
var href = $el.attr('href');
|
var href = $el.attr('href');
|
||||||
if (href && href !== '#') {
|
if (href && href !== '#') {
|
||||||
gl.utils.visitUrl(href);
|
visitUrl(href);
|
||||||
} else {
|
} else {
|
||||||
$el.trigger('click');
|
$el.trigger('click');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import eventHub from '../event_hub';
|
||||||
import { getParameterByName } from '../../lib/utils/common_utils';
|
import { getParameterByName } from '../../lib/utils/common_utils';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||||
import { COMMON_STR } from '../constants';
|
import { COMMON_STR } from '../constants';
|
||||||
|
import { mergeUrlParams } from '../../lib/utils/url_utility';
|
||||||
import groupsComponent from './groups.vue';
|
import groupsComponent from './groups.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -93,7 +93,7 @@ export default {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
$.scrollTo(0);
|
$.scrollTo(0);
|
||||||
|
|
||||||
const currentPath = gl.utils.mergeUrlParams({ page }, window.location.href);
|
const currentPath = mergeUrlParams({ page }, window.location.href);
|
||||||
window.history.replaceState({
|
window.history.replaceState({
|
||||||
page: currentPath,
|
page: currentPath,
|
||||||
}, document.title, currentPath);
|
}, document.title, currentPath);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { visitUrl } from '../../lib/utils/url_utility';
|
||||||
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
import identicon from '../../vue_shared/components/identicon.vue';
|
import identicon from '../../vue_shared/components/identicon.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
|
|
||||||
|
|
@ -8,6 +10,9 @@ import itemStats from './item_stats.vue';
|
||||||
import itemActions from './item_actions.vue';
|
import itemActions from './item_actions.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
directives: {
|
||||||
|
tooltip,
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
identicon,
|
identicon,
|
||||||
itemCaret,
|
itemCaret,
|
||||||
|
|
@ -56,7 +61,7 @@ export default {
|
||||||
if (this.hasChildren) {
|
if (this.hasChildren) {
|
||||||
eventHub.$emit('toggleChildren', this.group);
|
eventHub.$emit('toggleChildren', this.group);
|
||||||
} else {
|
} else {
|
||||||
gl.utils.visitUrl(this.group.relativePath);
|
visitUrl(this.group.relativePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -112,19 +117,30 @@ export default {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="title">
|
class="title namespace-title">
|
||||||
<a
|
<a
|
||||||
|
v-tooltip
|
||||||
:href="group.relativePath"
|
:href="group.relativePath"
|
||||||
class="no-expand">{{group.fullName}}</a>
|
:title="group.fullName"
|
||||||
|
class="no-expand"
|
||||||
|
data-placement="top"
|
||||||
|
>{{
|
||||||
|
// ending bracket must be by closing tag to prevent
|
||||||
|
// link hover text-decoration from over-extending
|
||||||
|
group.name
|
||||||
|
}}</a>
|
||||||
<span
|
<span
|
||||||
v-if="group.permission"
|
v-if="group.permission"
|
||||||
class="access-type"
|
class="user-access-role"
|
||||||
>
|
>
|
||||||
{{s__('GroupsTreeRole|as')}} {{group.permission}}
|
{{group.permission}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="description">{{group.description}}</div>
|
v-if="group.description"
|
||||||
|
class="description">
|
||||||
|
{{group.description}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<group-folder
|
<group-folder
|
||||||
v-if="group.isOpen && hasChildren"
|
v-if="group.isOpen && hasChildren"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { s__ } from '../../locale';
|
import { s__ } from '../../locale';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '../../vue_shared/directives/tooltip';
|
||||||
import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
|
import modal from '../../vue_shared/components/modal.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import { COMMON_STR } from '../constants';
|
import { COMMON_STR } from '../constants';
|
||||||
import Icon from '../../vue_shared/components/icon.vue';
|
import Icon from '../../vue_shared/components/icon.vue';
|
||||||
|
|
@ -9,7 +9,7 @@ import Icon from '../../vue_shared/components/icon.vue';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
PopupDialog,
|
modal,
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
tooltip,
|
tooltip,
|
||||||
|
|
@ -27,7 +27,7 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dialogStatus: false,
|
modalStatus: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -43,10 +43,10 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onLeaveGroup() {
|
onLeaveGroup() {
|
||||||
this.dialogStatus = true;
|
this.modalStatus = true;
|
||||||
},
|
},
|
||||||
leaveGroup(leaveConfirmed) {
|
leaveGroup(leaveConfirmed) {
|
||||||
this.dialogStatus = false;
|
this.modalStatus = false;
|
||||||
if (leaveConfirmed) {
|
if (leaveConfirmed) {
|
||||||
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
|
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
|
||||||
}
|
}
|
||||||
|
|
@ -82,8 +82,8 @@ export default {
|
||||||
class="fa fa-sign-out"
|
class="fa fa-sign-out"
|
||||||
aria-hidden="true"/>
|
aria-hidden="true"/>
|
||||||
</a>
|
</a>
|
||||||
<popup-dialog
|
<modal
|
||||||
v-show="dialogStatus"
|
v-show="modalStatus"
|
||||||
:primary-button-label="__('Leave')"
|
:primary-button-label="__('Leave')"
|
||||||
kind="warning"
|
kind="warning"
|
||||||
:title="__('Are you sure?')"
|
:title="__('Are you sure?')"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { visitUrl } from '../lib/utils/url_utility';
|
||||||
import DropLab from '../droplab/drop_lab';
|
import DropLab from '../droplab/drop_lab';
|
||||||
import ISetter from '../droplab/plugins/input_setter';
|
import ISetter from '../droplab/plugins/input_setter';
|
||||||
|
|
||||||
|
|
@ -54,9 +55,9 @@ export default class NewGroupChild {
|
||||||
|
|
||||||
onClickNewGroupChildButton(e) {
|
onClickNewGroupChildButton(e) {
|
||||||
if (e.target.dataset.action === NEW_PROJECT) {
|
if (e.target.dataset.action === NEW_PROJECT) {
|
||||||
gl.utils.visitUrl(this.newGroupPath);
|
visitUrl(this.newGroupPath);
|
||||||
} else if (e.target.dataset.action === NEW_SUBGROUP) {
|
} else if (e.target.dataset.action === NEW_SUBGROUP) {
|
||||||
gl.utils.visitUrl(this.subgroupPath);
|
visitUrl(this.subgroupPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,9 @@ export function addImageBadge(containerEl, { coordinate, badgeText, noteId }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addImageCommentBadge(containerEl, { coordinate, noteId }) {
|
export function addImageCommentBadge(containerEl, { coordinate, noteId }) {
|
||||||
const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge', 'inverted']);
|
const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge']);
|
||||||
const iconEl = document.createElement('i');
|
buttonEl.innerHTML = gl.utils.spriteIcon('image-comment-dark');
|
||||||
iconEl.className = 'fa fa-comment-o';
|
|
||||||
iconEl.setAttribute('aria-label', 'comment');
|
|
||||||
|
|
||||||
buttonEl.appendChild(iconEl);
|
|
||||||
containerEl.appendChild(buttonEl);
|
containerEl.appendChild(buttonEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import ImageBadge from '../image_badge';
|
import ImageBadge from '../image_badge';
|
||||||
import ImageDiff from '../image_diff';
|
import ImageDiff from '../image_diff';
|
||||||
import ReplacedImageDiff from '../replaced_image_diff';
|
import ReplacedImageDiff from '../replaced_image_diff';
|
||||||
import '../../commit/image_file';
|
import ImageFile from '../../commit/image_file';
|
||||||
|
|
||||||
export function resizeCoordinatesToImageElement(imageEl, meta) {
|
export function resizeCoordinatesToImageElement(imageEl, meta) {
|
||||||
const { x, y, width, height } = meta;
|
const { x, y, width, height } = meta;
|
||||||
|
|
@ -81,7 +81,7 @@ export function initImageDiff(fileEl, canCreateNote, renderCommentBadge) {
|
||||||
|
|
||||||
// ImageFile needs to be invoked before initImageDiff so that badges
|
// ImageFile needs to be invoked before initImageDiff so that badges
|
||||||
// can mount to the correct location
|
// can mount to the correct location
|
||||||
new gl.ImageFile(fileEl); // eslint-disable-line no-new
|
new ImageFile(fileEl); // eslint-disable-line no-new
|
||||||
|
|
||||||
if (fileEl.querySelector('.diff-file .js-single-image')) {
|
if (fileEl.querySelector('.diff-file .js-single-image')) {
|
||||||
diff = new ImageDiff(fileEl, options);
|
diff = new ImageDiff(fileEl, options);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export default class IssuableBulkUpdateSidebar {
|
||||||
}
|
}
|
||||||
|
|
||||||
initDomElements() {
|
initDomElements() {
|
||||||
this.$page = $('.page-with-sidebar');
|
this.$page = $('.layout-page');
|
||||||
this.$sidebar = $('.right-sidebar');
|
this.$sidebar = $('.right-sidebar');
|
||||||
this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
|
this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
|
||||||
this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
|
this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export default class IssuableIndex {
|
||||||
url: $('.incoming-email-token-reset').attr('href'),
|
url: $('.incoming-email-token-reset').attr('href'),
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success(response) {
|
success(response) {
|
||||||
$('#issue_email').val(response.new_issue_address).focus();
|
$('#issuable_email').val(response.new_address).focus();
|
||||||
},
|
},
|
||||||
beforeSend() {
|
beforeSend() {
|
||||||
$('.incoming-email-token-reset').text('resetting...');
|
$('.incoming-email-token-reset').text('resetting...');
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,7 @@ import IssuablesHelper from './helpers/issuables_helper';
|
||||||
|
|
||||||
export default class Issue {
|
export default class Issue {
|
||||||
constructor() {
|
constructor() {
|
||||||
if ($('a.btn-close').length) {
|
if ($('a.btn-close').length) this.initIssueBtnEventListeners();
|
||||||
this.taskList = new TaskList({
|
|
||||||
dataType: 'issue',
|
|
||||||
fieldName: 'description',
|
|
||||||
selector: '.detail-page-description',
|
|
||||||
onSuccess: (result) => {
|
|
||||||
document.querySelector('#task_status').innerText = result.task_status;
|
|
||||||
document.querySelector('#task_status_short').innerText = result.task_status_short;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.initIssueBtnEventListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
Issue.$btnNewBranch = $('#new-branch');
|
Issue.$btnNewBranch = $('#new-branch');
|
||||||
Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
|
Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
|
||||||
|
|
@ -59,7 +48,7 @@ export default class Issue {
|
||||||
})
|
})
|
||||||
.fail(() => new Flash(issueFailMessage))
|
.fail(() => new Flash(issueFailMessage))
|
||||||
.done((data) => {
|
.done((data) => {
|
||||||
const isClosedBadge = $('div.status-box-closed');
|
const isClosedBadge = $('div.status-box-issue-closed');
|
||||||
const isOpenBadge = $('div.status-box-open');
|
const isOpenBadge = $('div.status-box-open');
|
||||||
const projectIssuesCounter = $('.issue_counter');
|
const projectIssuesCounter = $('.issue_counter');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import Visibility from 'visibilityjs';
|
import Visibility from 'visibilityjs';
|
||||||
|
import { visitUrl } from '../../lib/utils/url_utility';
|
||||||
import Poll from '../../lib/utils/poll';
|
import Poll from '../../lib/utils/poll';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import Service from '../services/index';
|
import Service from '../services/index';
|
||||||
|
|
@ -8,7 +9,7 @@ import titleComponent from './title.vue';
|
||||||
import descriptionComponent from './description.vue';
|
import descriptionComponent from './description.vue';
|
||||||
import editedComponent from './edited.vue';
|
import editedComponent from './edited.vue';
|
||||||
import formComponent from './form.vue';
|
import formComponent from './form.vue';
|
||||||
import '../../lib/utils/url_utility';
|
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -149,6 +150,11 @@ export default {
|
||||||
editedComponent,
|
editedComponent,
|
||||||
formComponent,
|
formComponent,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mixins: [
|
||||||
|
recaptchaModalImplementor,
|
||||||
|
],
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
openForm() {
|
openForm() {
|
||||||
if (!this.showForm) {
|
if (!this.showForm) {
|
||||||
|
|
@ -164,12 +170,14 @@ export default {
|
||||||
closeForm() {
|
closeForm() {
|
||||||
this.showForm = false;
|
this.showForm = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateIssuable() {
|
updateIssuable() {
|
||||||
this.service.updateIssuable(this.store.formState)
|
this.service.updateIssuable(this.store.formState)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
|
.then(data => this.checkForSpam(data))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (location.pathname !== data.web_url) {
|
if (location.pathname !== data.web_url) {
|
||||||
gl.utils.visitUrl(data.web_url);
|
visitUrl(data.web_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.service.getData();
|
return this.service.getData();
|
||||||
|
|
@ -179,11 +187,24 @@ export default {
|
||||||
this.store.updateState(data);
|
this.store.updateState(data);
|
||||||
eventHub.$emit('close.form');
|
eventHub.$emit('close.form');
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((error) => {
|
||||||
eventHub.$emit('close.form');
|
if (error && error.name === 'SpamError') {
|
||||||
window.Flash(`Error updating ${this.issuableType}`);
|
this.openRecaptcha();
|
||||||
|
} else {
|
||||||
|
eventHub.$emit('close.form');
|
||||||
|
window.Flash(`Error updating ${this.issuableType}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
closeRecaptchaModal() {
|
||||||
|
this.store.setFormState({
|
||||||
|
updateLoading: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeRecaptcha();
|
||||||
|
},
|
||||||
|
|
||||||
deleteIssuable() {
|
deleteIssuable() {
|
||||||
this.service.deleteIssuable()
|
this.service.deleteIssuable()
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
|
|
@ -191,7 +212,7 @@ export default {
|
||||||
// Stop the poll so we don't get 404's with the issuable not existing
|
// Stop the poll so we don't get 404's with the issuable not existing
|
||||||
this.poll.stop();
|
this.poll.stop();
|
||||||
|
|
||||||
gl.utils.visitUrl(data.web_url);
|
visitUrl(data.web_url);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
eventHub.$emit('close.form');
|
eventHub.$emit('close.form');
|
||||||
|
|
@ -237,9 +258,9 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<div v-if="canUpdate && showForm">
|
||||||
<form-component
|
<form-component
|
||||||
v-if="canUpdate && showForm"
|
|
||||||
:form-state="formState"
|
:form-state="formState"
|
||||||
:can-destroy="canDestroy"
|
:can-destroy="canDestroy"
|
||||||
:issuable-templates="issuableTemplates"
|
:issuable-templates="issuableTemplates"
|
||||||
|
|
@ -251,30 +272,37 @@ export default {
|
||||||
:can-attach-file="canAttachFile"
|
:can-attach-file="canAttachFile"
|
||||||
:enable-autocomplete="enableAutocomplete"
|
:enable-autocomplete="enableAutocomplete"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
|
||||||
<title-component
|
<recaptcha-modal
|
||||||
:issuable-ref="issuableRef"
|
v-show="showRecaptcha"
|
||||||
:can-update="canUpdate"
|
:html="recaptchaHTML"
|
||||||
:title-html="state.titleHtml"
|
@close="closeRecaptchaModal"
|
||||||
:title-text="state.titleText"
|
/>
|
||||||
:show-inline-edit-button="showInlineEditButton"
|
|
||||||
/>
|
|
||||||
<description-component
|
|
||||||
v-if="state.descriptionHtml"
|
|
||||||
:can-update="canUpdate"
|
|
||||||
:description-html="state.descriptionHtml"
|
|
||||||
:description-text="state.descriptionText"
|
|
||||||
:updated-at="state.updatedAt"
|
|
||||||
:task-status="state.taskStatus"
|
|
||||||
:issuable-type="issuableType"
|
|
||||||
:update-url="updateEndpoint"
|
|
||||||
/>
|
|
||||||
<edited-component
|
|
||||||
v-if="hasUpdated"
|
|
||||||
:updated-at="state.updatedAt"
|
|
||||||
:updated-by-name="state.updatedByName"
|
|
||||||
:updated-by-path="state.updatedByPath"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<title-component
|
||||||
|
:issuable-ref="issuableRef"
|
||||||
|
:can-update="canUpdate"
|
||||||
|
:title-html="state.titleHtml"
|
||||||
|
:title-text="state.titleText"
|
||||||
|
:show-inline-edit-button="showInlineEditButton"
|
||||||
|
/>
|
||||||
|
<description-component
|
||||||
|
v-if="state.descriptionHtml"
|
||||||
|
:can-update="canUpdate"
|
||||||
|
:description-html="state.descriptionHtml"
|
||||||
|
:description-text="state.descriptionText"
|
||||||
|
:updated-at="state.updatedAt"
|
||||||
|
:task-status="state.taskStatus"
|
||||||
|
:issuable-type="issuableType"
|
||||||
|
:update-url="updateEndpoint"
|
||||||
|
/>
|
||||||
|
<edited-component
|
||||||
|
v-if="hasUpdated"
|
||||||
|
:updated-at="state.updatedAt"
|
||||||
|
:updated-by-name="state.updatedByName"
|
||||||
|
:updated-by-path="state.updatedByPath"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import animateMixin from '../mixins/animate';
|
import animateMixin from '../mixins/animate';
|
||||||
import TaskList from '../../task_list';
|
import TaskList from '../../task_list';
|
||||||
|
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [animateMixin],
|
mixins: [
|
||||||
|
animateMixin,
|
||||||
|
recaptchaModalImplementor,
|
||||||
|
],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
canUpdate: {
|
canUpdate: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|
@ -51,6 +56,7 @@
|
||||||
this.updateTaskStatusText();
|
this.updateTaskStatusText();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
renderGFM() {
|
renderGFM() {
|
||||||
$(this.$refs['gfm-content']).renderGFM();
|
$(this.$refs['gfm-content']).renderGFM();
|
||||||
|
|
@ -61,9 +67,19 @@
|
||||||
dataType: this.issuableType,
|
dataType: this.issuableType,
|
||||||
fieldName: 'description',
|
fieldName: 'description',
|
||||||
selector: '.detail-page-description',
|
selector: '.detail-page-description',
|
||||||
|
onSuccess: this.taskListUpdateSuccess.bind(this),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
taskListUpdateSuccess(data) {
|
||||||
|
try {
|
||||||
|
this.checkForSpam(data);
|
||||||
|
} catch (error) {
|
||||||
|
if (error && error.name === 'SpamError') this.openRecaptcha();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateTaskStatusText() {
|
updateTaskStatusText() {
|
||||||
const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
|
const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
|
||||||
const $issuableHeader = $('.issuable-meta');
|
const $issuableHeader = $('.issuable-meta');
|
||||||
|
|
@ -109,5 +125,11 @@
|
||||||
:data-update-url="updateUrl"
|
:data-update-url="updateUrl"
|
||||||
>
|
>
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
|
<recaptcha-modal
|
||||||
|
v-show="showRecaptcha"
|
||||||
|
:html="recaptchaHTML"
|
||||||
|
@close="closeRecaptcha"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
<textarea
|
<textarea
|
||||||
id="issue-description"
|
id="issue-description"
|
||||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||||
data-supports-quick-actionss="false"
|
data-supports-quick-actions="false"
|
||||||
aria-label="Description"
|
aria-label="Description"
|
||||||
v-model="formState.description"
|
v-model="formState.description"
|
||||||
ref="textarea"
|
ref="textarea"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
|
import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
formState: {
|
formState: {
|
||||||
|
|
@ -32,7 +34,7 @@
|
||||||
};
|
};
|
||||||
editor.getValue = () => this.formState.description;
|
editor.getValue = () => this.formState.description;
|
||||||
|
|
||||||
this.issuableTemplate = new gl.IssuableTemplateSelectors({
|
this.issuableTemplate = new IssuableTemplateSelectors({
|
||||||
$dropdowns: $(this.$refs.toggle),
|
$dropdowns: $(this.$refs.toggle),
|
||||||
editor,
|
editor,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import '../vue_shared/vue_resource_interceptor';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
|
const initialDataEl = document.getElementById('js-issuable-app-initial-data');
|
||||||
const initialData = JSON.parse(initialDataEl.innerHTML.replace(/"/g, '"'));
|
const props = JSON.parse(initialDataEl.innerHTML.replace(/"/g, '"'));
|
||||||
|
|
||||||
$('.issuable-edit').on('click', (e) => {
|
$('.js-issuable-edit').on('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
eventHub.$emit('open.form');
|
eventHub.$emit('open.form');
|
||||||
|
|
@ -18,32 +18,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
components: {
|
components: {
|
||||||
issuableApp,
|
issuableApp,
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
...initialData,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement('issuable-app', {
|
return createElement('issuable-app', {
|
||||||
props: {
|
props,
|
||||||
canUpdate: this.canUpdate,
|
|
||||||
canDestroy: this.canDestroy,
|
|
||||||
endpoint: this.endpoint,
|
|
||||||
issuableRef: this.issuableRef,
|
|
||||||
initialTitleHtml: this.initialTitleHtml,
|
|
||||||
initialTitleText: this.initialTitleText,
|
|
||||||
initialDescriptionHtml: this.initialDescriptionHtml,
|
|
||||||
initialDescriptionText: this.initialDescriptionText,
|
|
||||||
issuableTemplates: this.issuableTemplates,
|
|
||||||
markdownPreviewPath: this.markdownPreviewPath,
|
|
||||||
markdownDocsPath: this.markdownDocsPath,
|
|
||||||
projectPath: this.projectPath,
|
|
||||||
projectNamespace: this.projectNamespace,
|
|
||||||
updatedAt: this.updatedAt,
|
|
||||||
updatedByName: this.updatedByName,
|
|
||||||
updatedByPath: this.updatedByPath,
|
|
||||||
initialTaskStatus: this.initialTaskStatus,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
import { visitUrl } from './lib/utils/url_utility';
|
||||||
import bp from './breakpoints';
|
import bp from './breakpoints';
|
||||||
import { bytesToKiB } from './lib/utils/number_utils';
|
import { bytesToKiB } from './lib/utils/number_utils';
|
||||||
import { setCiStatusFavicon } from './lib/utils/common_utils';
|
import { setCiStatusFavicon } from './lib/utils/common_utils';
|
||||||
|
import { timeFor } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
export default class Job {
|
export default class Job {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
|
@ -9,7 +11,7 @@ export default class Job {
|
||||||
this.state = null;
|
this.state = null;
|
||||||
this.options = options || $('.js-build-options').data();
|
this.options = options || $('.js-build-options').data();
|
||||||
|
|
||||||
this.pageUrl = this.options.pageUrl;
|
this.pagePath = this.options.pagePath;
|
||||||
this.buildStatus = this.options.buildStatus;
|
this.buildStatus = this.options.buildStatus;
|
||||||
this.state = this.options.logState;
|
this.state = this.options.logState;
|
||||||
this.buildStage = this.options.buildStage;
|
this.buildStage = this.options.buildStage;
|
||||||
|
|
@ -167,11 +169,11 @@ export default class Job {
|
||||||
|
|
||||||
getBuildTrace() {
|
getBuildTrace() {
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url: `${this.pageUrl}/trace.json`,
|
url: `${this.pagePath}/trace.json`,
|
||||||
data: { state: this.state },
|
data: { state: this.state },
|
||||||
})
|
})
|
||||||
.done((log) => {
|
.done((log) => {
|
||||||
setCiStatusFavicon(`${this.pageUrl}/status.json`);
|
setCiStatusFavicon(`${this.pagePath}/status.json`);
|
||||||
|
|
||||||
if (log.state) {
|
if (log.state) {
|
||||||
this.state = log.state;
|
this.state = log.state;
|
||||||
|
|
@ -209,7 +211,7 @@ export default class Job {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.status !== this.buildStatus) {
|
if (log.status !== this.buildStatus) {
|
||||||
gl.utils.visitUrl(this.pageUrl);
|
visitUrl(this.pagePath);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail(() => {
|
||||||
|
|
@ -260,7 +262,7 @@ export default class Job {
|
||||||
if ($date.length) {
|
if ($date.length) {
|
||||||
const date = $date.text();
|
const date = $date.text();
|
||||||
return $date.text(
|
return $date.text(
|
||||||
gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
|
timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3'))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
class Cache {
|
export default class Cache {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.internalStorage = { };
|
this.internalStorage = { };
|
||||||
}
|
}
|
||||||
|
|
@ -15,5 +15,3 @@ class Cache {
|
||||||
delete this.internalStorage[key];
|
delete this.internalStorage[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Cache;
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getLocationHash } from './url_utility';
|
||||||
|
|
||||||
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
|
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
|
||||||
|
|
||||||
|
|
@ -65,7 +66,7 @@ export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventNa
|
||||||
// automatically adjust scroll position for hash urls taking the height of the navbar into account
|
// automatically adjust scroll position for hash urls taking the height of the navbar into account
|
||||||
// https://github.com/twitter/bootstrap/issues/1768
|
// https://github.com/twitter/bootstrap/issues/1768
|
||||||
export const handleLocationHash = () => {
|
export const handleLocationHash = () => {
|
||||||
let hash = window.gl.utils.getLocationHash();
|
let hash = getLocationHash();
|
||||||
if (!hash) return;
|
if (!hash) return;
|
||||||
|
|
||||||
// This is required to handle non-unicode characters in hash
|
// This is required to handle non-unicode characters in hash
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
/* eslint-disable import/prefer-default-export */
|
|
||||||
export const BYTES_IN_KIB = 1024;
|
export const BYTES_IN_KIB = 1024;
|
||||||
export const HIDDEN_CLASS = 'hidden';
|
export const HIDDEN_CLASS = 'hidden';
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
/* 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 */
|
|
||||||
|
|
||||||
import timeago from 'timeago.js';
|
import timeago from 'timeago.js';
|
||||||
import dateFormat from 'vendor/date.format';
|
import dateFormat from 'vendor/date.format';
|
||||||
import { pluralize } from './text_utility';
|
import { pluralize } from './text_utility';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
lang,
|
lang,
|
||||||
s__,
|
s__,
|
||||||
|
|
@ -12,123 +9,125 @@ import {
|
||||||
window.timeago = timeago;
|
window.timeago = timeago;
|
||||||
window.dateFormat = dateFormat;
|
window.dateFormat = dateFormat;
|
||||||
|
|
||||||
(function() {
|
/**
|
||||||
(function(w) {
|
* Given a date object returns the day of the week in English
|
||||||
var base;
|
* @param {date} date
|
||||||
var timeagoInstance;
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export const getDayName = date => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()];
|
||||||
|
|
||||||
if (w.gl == null) {
|
/**
|
||||||
w.gl = {};
|
* @example
|
||||||
}
|
* dateFormat('2017-12-05','mmm d, yyyy h:MMtt Z' ) -> "Dec 5, 2017 12:00am GMT+0000"
|
||||||
if ((base = w.gl).utils == null) {
|
* @param {date} datetime
|
||||||
base.utils = {};
|
* @returns {String}
|
||||||
}
|
*/
|
||||||
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
export const formatDate = datetime => dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
|
||||||
|
|
||||||
w.gl.utils.formatDate = function(datetime) {
|
let timeagoInstance;
|
||||||
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
|
/**
|
||||||
|
* Sets a timeago Instance
|
||||||
|
*/
|
||||||
|
export function getTimeago() {
|
||||||
|
if (!timeagoInstance) {
|
||||||
|
const localeRemaining = function getLocaleRemaining(number, index) {
|
||||||
|
return [
|
||||||
|
[s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
|
||||||
|
[s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
|
||||||
|
[s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
|
||||||
|
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
|
||||||
|
[s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
|
||||||
|
[s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
|
||||||
|
[s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
|
||||||
|
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
|
||||||
|
[s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
|
||||||
|
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
|
||||||
|
[s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
|
||||||
|
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
|
||||||
|
[s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
|
||||||
|
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
|
||||||
|
][index];
|
||||||
|
};
|
||||||
|
const locale = function getLocale(number, index) {
|
||||||
|
return [
|
||||||
|
[s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
|
||||||
|
[s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
|
||||||
|
[s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
|
||||||
|
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
|
||||||
|
[s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
|
||||||
|
[s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
|
||||||
|
[s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
|
||||||
|
[s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
|
||||||
|
[s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
|
||||||
|
[s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
|
||||||
|
[s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
|
||||||
|
[s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
|
||||||
|
[s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
|
||||||
|
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
|
||||||
|
][index];
|
||||||
};
|
};
|
||||||
|
|
||||||
w.gl.utils.getDayName = function(date) {
|
timeago.register(lang, locale);
|
||||||
return this.days[date.getDay()];
|
timeago.register(`${lang}-remaining`, localeRemaining);
|
||||||
};
|
timeagoInstance = timeago();
|
||||||
|
}
|
||||||
|
|
||||||
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
|
return timeagoInstance;
|
||||||
$timeagoEls.each((i, el) => {
|
}
|
||||||
el.setAttribute('title', el.getAttribute('title'));
|
|
||||||
|
|
||||||
if (setTimeago) {
|
/**
|
||||||
// Recreate with custom template
|
* For the given element, renders a timeago instance.
|
||||||
$(el).tooltip({
|
* @param {jQuery} $els
|
||||||
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
*/
|
||||||
});
|
export const renderTimeago = ($els) => {
|
||||||
}
|
const timeagoEls = $els || document.querySelectorAll('.js-timeago-render');
|
||||||
|
|
||||||
el.classList.add('js-timeago-render');
|
// timeago.js sets timeouts internally for each timeago value to be updated in real time
|
||||||
|
getTimeago().render(timeagoEls, lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For the given elements, sets a tooltip with a formatted date.
|
||||||
|
* @param {jQuery}
|
||||||
|
* @param {Boolean} setTimeago
|
||||||
|
*/
|
||||||
|
export const localTimeAgo = ($timeagoEls, setTimeago = true) => {
|
||||||
|
$timeagoEls.each((i, el) => {
|
||||||
|
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>',
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
gl.utils.renderTimeago($timeagoEls);
|
el.classList.add('js-timeago-render');
|
||||||
};
|
});
|
||||||
|
|
||||||
w.gl.utils.getTimeago = function() {
|
renderTimeago($timeagoEls);
|
||||||
var locale;
|
};
|
||||||
|
|
||||||
if (!timeagoInstance) {
|
/**
|
||||||
const localeRemaining = function(number, index) {
|
* Returns remaining or passed time over the given time.
|
||||||
return [
|
* @param {*} time
|
||||||
[s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
|
* @param {*} expiredLabel
|
||||||
[s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
|
*/
|
||||||
[s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
|
export const timeFor = (time, expiredLabel) => {
|
||||||
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
|
if (!time) {
|
||||||
[s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
|
return '';
|
||||||
[s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
|
}
|
||||||
[s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
|
if (new Date(time) < new Date()) {
|
||||||
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
|
return expiredLabel || s__('Timeago|Past due');
|
||||||
[s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
|
}
|
||||||
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
|
return getTimeago().format(time, `${lang}-remaining`).trim();
|
||||||
[s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
|
};
|
||||||
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
|
|
||||||
[s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
|
|
||||||
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')]
|
|
||||||
][index];
|
|
||||||
};
|
|
||||||
locale = function(number, index) {
|
|
||||||
return [
|
|
||||||
[s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
|
|
||||||
[s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
|
|
||||||
[s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
|
|
||||||
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
|
|
||||||
[s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
|
|
||||||
[s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
|
|
||||||
[s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
|
|
||||||
[s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
|
|
||||||
[s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
|
|
||||||
[s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
|
|
||||||
[s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
|
|
||||||
[s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
|
|
||||||
[s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
|
|
||||||
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')]
|
|
||||||
][index];
|
|
||||||
};
|
|
||||||
|
|
||||||
timeago.register(lang, locale);
|
export const getDayDifference = (a, b) => {
|
||||||
timeago.register(`${lang}-remaining`, localeRemaining);
|
const millisecondsPerDay = 1000 * 60 * 60 * 24;
|
||||||
timeagoInstance = timeago();
|
const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
||||||
}
|
const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
||||||
|
|
||||||
return timeagoInstance;
|
return Math.floor((date2 - date1) / millisecondsPerDay);
|
||||||
};
|
};
|
||||||
|
|
||||||
w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
|
|
||||||
var timefor;
|
|
||||||
if (!time) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if (new Date(time) < new Date()) {
|
|
||||||
expiredLabel || (expiredLabel = s__('Timeago|Past due'));
|
|
||||||
timefor = expiredLabel;
|
|
||||||
} else {
|
|
||||||
timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim();
|
|
||||||
}
|
|
||||||
return timefor;
|
|
||||||
};
|
|
||||||
|
|
||||||
w.gl.utils.renderTimeago = function($els) {
|
|
||||||
const timeagoEls = $els || document.querySelectorAll('.js-timeago-render');
|
|
||||||
|
|
||||||
// timeago.js sets timeouts internally for each timeago value to be updated in real time
|
|
||||||
gl.utils.getTimeago().render(timeagoEls, lang);
|
|
||||||
};
|
|
||||||
|
|
||||||
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(window);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Port of ruby helper time_interval_in_words.
|
* Port of ruby helper time_interval_in_words.
|
||||||
|
|
@ -164,3 +163,10 @@ export function dateInWords(date, abbreviated = false) {
|
||||||
|
|
||||||
return `${monthName} ${date.getDate()}, ${year}`;
|
return `${monthName} ${date.getDate()}, ${year}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.gl = window.gl || {};
|
||||||
|
window.gl.utils = {
|
||||||
|
...(window.gl.utils || {}),
|
||||||
|
getTimeago,
|
||||||
|
localTimeAgo,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,93 +1,69 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */
|
|
||||||
|
|
||||||
var base;
|
|
||||||
var w = window;
|
|
||||||
if (w.gl == null) {
|
|
||||||
w.gl = {};
|
|
||||||
}
|
|
||||||
if ((base = w.gl).utils == null) {
|
|
||||||
base.utils = {};
|
|
||||||
}
|
|
||||||
// Returns an array containing the value(s) of the
|
// Returns an array containing the value(s) of the
|
||||||
// of the key passed as an argument
|
// of the key passed as an argument
|
||||||
w.gl.utils.getParameterValues = function(sParam) {
|
export function getParameterValues(sParam) {
|
||||||
var i, sPageURL, sParameterName, sURLVariables, values;
|
const sPageURL = decodeURIComponent(window.location.search.substring(1));
|
||||||
sPageURL = decodeURIComponent(window.location.search.substring(1));
|
|
||||||
sURLVariables = sPageURL.split('&');
|
return sPageURL.split('&').reduce((acc, urlParam) => {
|
||||||
sParameterName = void 0;
|
const sParameterName = urlParam.split('=');
|
||||||
values = [];
|
|
||||||
i = 0;
|
|
||||||
while (i < sURLVariables.length) {
|
|
||||||
sParameterName = sURLVariables[i].split('=');
|
|
||||||
if (sParameterName[0] === sParam) {
|
if (sParameterName[0] === sParam) {
|
||||||
values.push(sParameterName[1].replace(/\+/g, ' '));
|
acc.push(sParameterName[1].replace(/\+/g, ' '));
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
}
|
return acc;
|
||||||
return values;
|
}, []);
|
||||||
};
|
}
|
||||||
|
|
||||||
// @param {Object} params - url keys and value to merge
|
// @param {Object} params - url keys and value to merge
|
||||||
// @param {String} url
|
// @param {String} url
|
||||||
w.gl.utils.mergeUrlParams = function(params, url) {
|
export function mergeUrlParams(params, url) {
|
||||||
var lastChar, newUrl, paramName, paramValue, pattern;
|
let newUrl = Object.keys(params).reduce((acc, paramName) => {
|
||||||
newUrl = decodeURIComponent(url);
|
const paramValue = params[paramName];
|
||||||
for (paramName in params) {
|
const pattern = new RegExp(`\\b(${paramName}=).*?(&|$)`);
|
||||||
paramValue = params[paramName];
|
|
||||||
pattern = new RegExp("\\b(" + paramName + "=).*?(&|$)");
|
if (paramValue === null) {
|
||||||
if (paramValue == null) {
|
return acc.replace(pattern, '');
|
||||||
newUrl = newUrl.replace(pattern, '');
|
|
||||||
} else if (url.search(pattern) !== -1) {
|
} else if (url.search(pattern) !== -1) {
|
||||||
newUrl = newUrl.replace(pattern, "$1" + paramValue + "$2");
|
return acc.replace(pattern, `$1${paramValue}$2`);
|
||||||
} else {
|
|
||||||
newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return `${acc}${acc.indexOf('?') > 0 ? '&' : '?'}${paramName}=${paramValue}`;
|
||||||
|
}, decodeURIComponent(url));
|
||||||
|
|
||||||
// Remove a trailing ampersand
|
// Remove a trailing ampersand
|
||||||
lastChar = newUrl[newUrl.length - 1];
|
const lastChar = newUrl[newUrl.length - 1];
|
||||||
|
|
||||||
if (lastChar === '&') {
|
if (lastChar === '&') {
|
||||||
newUrl = newUrl.slice(0, -1);
|
newUrl = newUrl.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newUrl;
|
return newUrl;
|
||||||
};
|
}
|
||||||
// removes parameter query string from url. returns the modified url
|
|
||||||
w.gl.utils.removeParamQueryString = function(url, param) {
|
export function removeParamQueryString(url, param) {
|
||||||
var urlVariables, variables;
|
const decodedUrl = decodeURIComponent(url);
|
||||||
url = decodeURIComponent(url);
|
const urlVariables = decodedUrl.split('&');
|
||||||
urlVariables = url.split('&');
|
|
||||||
return ((function() {
|
return urlVariables.filter(variable => variable.indexOf(param) === -1).join('&');
|
||||||
var j, len, results;
|
}
|
||||||
results = [];
|
|
||||||
for (j = 0, len = urlVariables.length; j < len; j += 1) {
|
export function removeParams(params) {
|
||||||
variables = urlVariables[j];
|
|
||||||
if (variables.indexOf(param) === -1) {
|
|
||||||
results.push(variables);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
})()).join('&');
|
|
||||||
};
|
|
||||||
w.gl.utils.removeParams = (params) => {
|
|
||||||
const url = document.createElement('a');
|
const url = document.createElement('a');
|
||||||
url.href = window.location.href;
|
url.href = window.location.href;
|
||||||
|
|
||||||
params.forEach((param) => {
|
params.forEach((param) => {
|
||||||
url.search = w.gl.utils.removeParamQueryString(url.search, param);
|
url.search = removeParamQueryString(url.search, param);
|
||||||
});
|
});
|
||||||
|
|
||||||
return url.href;
|
return url.href;
|
||||||
};
|
}
|
||||||
w.gl.utils.getLocationHash = function(url) {
|
|
||||||
var hashIndex;
|
export function getLocationHash(url = window.location.href) {
|
||||||
if (typeof url === 'undefined') {
|
const hashIndex = url.indexOf('#');
|
||||||
// Note: We can't use window.location.hash here because it's
|
|
||||||
// not consistent across browsers - Firefox will pre-decode it
|
|
||||||
url = window.location.href;
|
|
||||||
}
|
|
||||||
hashIndex = url.indexOf('#');
|
|
||||||
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
|
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
|
||||||
};
|
}
|
||||||
|
|
||||||
w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(window.location.href);
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
|
||||||
export function visitUrl(url, external = false) {
|
export function visitUrl(url, external = false) {
|
||||||
if (external) {
|
if (external) {
|
||||||
// Simulate `target="blank" rel="noopener noreferrer"`
|
// Simulate `target="blank" rel="noopener noreferrer"`
|
||||||
|
|
@ -100,12 +76,10 @@ export function visitUrl(url, external = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function refreshCurrentPage() {
|
||||||
|
visitUrl(window.location.href);
|
||||||
|
}
|
||||||
|
|
||||||
export function redirectTo(url) {
|
export function redirectTo(url) {
|
||||||
return window.location.assign(url);
|
return window.location.assign(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
|
||||||
window.gl.utils = {
|
|
||||||
...(window.gl.utils || {}),
|
|
||||||
visitUrl,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */
|
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */
|
||||||
/* global ConfirmDangerModal */
|
/* global ConfirmDangerModal */
|
||||||
/* global Aside */
|
|
||||||
|
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
|
@ -28,8 +27,8 @@ import './commit/image_file';
|
||||||
|
|
||||||
// lib/utils
|
// lib/utils
|
||||||
import { handleLocationHash } from './lib/utils/common_utils';
|
import { handleLocationHash } from './lib/utils/common_utils';
|
||||||
import './lib/utils/datetime_utility';
|
import { localTimeAgo, renderTimeago } from './lib/utils/datetime_utility';
|
||||||
import './lib/utils/url_utility';
|
import { getLocationHash, visitUrl } from './lib/utils/url_utility';
|
||||||
|
|
||||||
// behaviors
|
// behaviors
|
||||||
import './behaviors/';
|
import './behaviors/';
|
||||||
|
|
@ -37,14 +36,9 @@ import './behaviors/';
|
||||||
// everything else
|
// everything else
|
||||||
import './activities';
|
import './activities';
|
||||||
import './admin';
|
import './admin';
|
||||||
import './aside';
|
|
||||||
import loadAwardsHandler from './awards_handler';
|
import loadAwardsHandler from './awards_handler';
|
||||||
import bp from './breakpoints';
|
import bp from './breakpoints';
|
||||||
import './commits';
|
|
||||||
import './compare';
|
|
||||||
import './compare_autocomplete';
|
|
||||||
import './confirm_danger_modal';
|
import './confirm_danger_modal';
|
||||||
import './copy_to_clipboard';
|
|
||||||
import Flash, { removeFlashClickListener } from './flash';
|
import Flash, { removeFlashClickListener } from './flash';
|
||||||
import './gl_dropdown';
|
import './gl_dropdown';
|
||||||
import './gl_field_error';
|
import './gl_field_error';
|
||||||
|
|
@ -64,15 +58,10 @@ import './notifications_dropdown';
|
||||||
import './notifications_form';
|
import './notifications_form';
|
||||||
import './pager';
|
import './pager';
|
||||||
import './preview_markdown';
|
import './preview_markdown';
|
||||||
import './project_find_file';
|
|
||||||
import './project_import';
|
import './project_import';
|
||||||
import './projects_dropdown';
|
import './projects_dropdown';
|
||||||
import './projects_list';
|
|
||||||
import './syntax_highlight';
|
|
||||||
import './render_gfm';
|
import './render_gfm';
|
||||||
import './right_sidebar';
|
import './right_sidebar';
|
||||||
import './search';
|
|
||||||
import './search_autocomplete';
|
|
||||||
import initBreadcrumbs from './breadcrumb';
|
import initBreadcrumbs from './breadcrumb';
|
||||||
|
|
||||||
import './dispatcher';
|
import './dispatcher';
|
||||||
|
|
@ -123,13 +112,13 @@ $(function () {
|
||||||
// `hashchange` is not triggered when link target is already in window.location
|
// `hashchange` is not triggered when link target is already in window.location
|
||||||
$body.on('click', 'a[href^="#"]', function() {
|
$body.on('click', 'a[href^="#"]', function() {
|
||||||
var href = this.getAttribute('href');
|
var href = this.getAttribute('href');
|
||||||
if (href.substr(1) === gl.utils.getLocationHash()) {
|
if (href.substr(1) === getLocationHash()) {
|
||||||
setTimeout(handleLocationHash, 1);
|
setTimeout(handleLocationHash, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bootstrapBreakpoint === 'xs') {
|
if (bootstrapBreakpoint === 'xs') {
|
||||||
const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar');
|
const $rightSidebar = $('aside.right-sidebar, .layout-page');
|
||||||
|
|
||||||
$rightSidebar
|
$rightSidebar
|
||||||
.removeClass('right-sidebar-expanded')
|
.removeClass('right-sidebar-expanded')
|
||||||
|
|
@ -189,13 +178,13 @@ $(function () {
|
||||||
trigger: 'focus',
|
trigger: 'focus',
|
||||||
// set the viewport to the main content, excluding the navigation bar, so
|
// set the viewport to the main content, excluding the navigation bar, so
|
||||||
// the navigation can't overlap the popover
|
// the navigation can't overlap the popover
|
||||||
viewport: '.page-with-sidebar'
|
viewport: '.layout-page'
|
||||||
});
|
});
|
||||||
$('.trigger-submit').on('change', function () {
|
$('.trigger-submit').on('change', function () {
|
||||||
return $(this).parents('form').submit();
|
return $(this).parents('form').submit();
|
||||||
// Form submitter
|
// Form submitter
|
||||||
});
|
});
|
||||||
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
|
localTimeAgo($('abbr.timeago, .js-timeago'), true);
|
||||||
// Disable form buttons while a form is submitting
|
// Disable form buttons while a form is submitting
|
||||||
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
|
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
|
||||||
var buttons;
|
var buttons;
|
||||||
|
|
@ -282,9 +271,8 @@ $(function () {
|
||||||
return fitSidebarForSize();
|
return fitSidebarForSize();
|
||||||
});
|
});
|
||||||
loadAwardsHandler();
|
loadAwardsHandler();
|
||||||
new Aside();
|
|
||||||
|
|
||||||
gl.utils.renderTimeago();
|
renderTimeago();
|
||||||
|
|
||||||
$(document).trigger('init.scrolling-tabs');
|
$(document).trigger('init.scrolling-tabs');
|
||||||
|
|
||||||
|
|
@ -295,7 +283,7 @@ $(function () {
|
||||||
const action = `${this.action}${link.search === '' ? '?' : '&'}`;
|
const action = `${this.action}${link.search === '' ? '?' : '&'}`;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
gl.utils.visitUrl(`${action}${$(this).serialize()}`);
|
visitUrl(`${action}${$(this).serialize()}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
const flashContainer = document.querySelector('.flash-container');
|
const flashContainer = document.querySelector('.flash-container');
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import './mixins/line_conflict_actions';
|
||||||
import './components/diff_file_editor';
|
import './components/diff_file_editor';
|
||||||
import './components/inline_conflict_lines';
|
import './components/inline_conflict_lines';
|
||||||
import './components/parallel_conflict_lines';
|
import './components/parallel_conflict_lines';
|
||||||
|
import syntaxHighlight from '../syntax_highlight';
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
const INTERACTIVE_RESOLVE_MODE = 'interactive';
|
const INTERACTIVE_RESOLVE_MODE = 'interactive';
|
||||||
|
|
@ -53,7 +54,7 @@ $(() => {
|
||||||
mergeConflictsStore.setLoadingState(false);
|
mergeConflictsStore.setLoadingState(false);
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
$('.js-syntax-highlight').syntaxHighlight();
|
syntaxHighlight($('.js-syntax-highlight'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ import { addDelimiter } from './lib/utils/text_utility';
|
||||||
};
|
};
|
||||||
|
|
||||||
MergeRequest.prototype.hideCloseButton = function() {
|
MergeRequest.prototype.hideCloseButton = function() {
|
||||||
const el = document.querySelector('.merge-request .issuable-actions');
|
const el = document.querySelector('.merge-request .js-issuable-actions');
|
||||||
const closeDropdownItem = el.querySelector('li.close-item');
|
const closeDropdownItem = el.querySelector('li.close-item');
|
||||||
if (closeDropdownItem) {
|
if (closeDropdownItem) {
|
||||||
closeDropdownItem.classList.add('hidden');
|
closeDropdownItem.classList.add('hidden');
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,11 @@ import {
|
||||||
handleLocationHash,
|
handleLocationHash,
|
||||||
isMetaClick,
|
isMetaClick,
|
||||||
} from './lib/utils/common_utils';
|
} from './lib/utils/common_utils';
|
||||||
|
import { getLocationHash } from './lib/utils/url_utility';
|
||||||
import initDiscussionTab from './image_diff/init_discussion_tab';
|
import initDiscussionTab from './image_diff/init_discussion_tab';
|
||||||
import Diff from './diff';
|
import Diff from './diff';
|
||||||
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
import syntaxHighlight from './syntax_highlight';
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
/* eslint-disable max-len */
|
||||||
// MergeRequestTabs
|
// MergeRequestTabs
|
||||||
|
|
@ -246,7 +249,7 @@ import Diff from './diff';
|
||||||
url: `${source}.json`,
|
url: `${source}.json`,
|
||||||
success: (data) => {
|
success: (data) => {
|
||||||
document.querySelector('div#commits').innerHTML = data.html;
|
document.querySelector('div#commits').innerHTML = data.html;
|
||||||
gl.utils.localTimeAgo($('.js-timeago', 'div#commits'));
|
localTimeAgo($('.js-timeago', 'div#commits'));
|
||||||
this.commitsLoaded = true;
|
this.commitsLoaded = true;
|
||||||
this.scrollToElement('#commits');
|
this.scrollToElement('#commits');
|
||||||
},
|
},
|
||||||
|
|
@ -293,8 +296,8 @@ import Diff from './diff';
|
||||||
gl.diffNotesCompileComponents();
|
gl.diffNotesCompileComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
|
localTimeAgo($('.js-timeago', 'div#diffs'));
|
||||||
$('#diffs .js-syntax-highlight').syntaxHighlight();
|
syntaxHighlight($('#diffs .js-syntax-highlight'));
|
||||||
|
|
||||||
if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) {
|
if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) {
|
||||||
this.expandViewContainer();
|
this.expandViewContainer();
|
||||||
|
|
@ -317,7 +320,7 @@ import Diff from './diff';
|
||||||
|
|
||||||
// Scroll any linked note into view
|
// Scroll any linked note into view
|
||||||
// Similar to `toggler_behavior` in the discussion tab
|
// Similar to `toggler_behavior` in the discussion tab
|
||||||
const hash = window.gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
const anchor = hash && $container.find(`.note[id="${hash}"]`);
|
const anchor = hash && $container.find(`.note[id="${hash}"]`);
|
||||||
if (anchor && anchor.length > 0) {
|
if (anchor && anchor.length > 0) {
|
||||||
const notesContent = anchor.closest('.notes_content');
|
const notesContent = anchor.closest('.notes_content');
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* global Issuable */
|
/* global Issuable */
|
||||||
/* global ListMilestone */
|
/* global ListMilestone */
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
|
import { timeFor } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
this.MilestoneSelect = (function() {
|
this.MilestoneSelect = (function() {
|
||||||
|
|
@ -216,7 +217,7 @@ import _ from 'underscore';
|
||||||
$value.css('display', '');
|
$value.css('display', '');
|
||||||
if (data.milestone != null) {
|
if (data.milestone != null) {
|
||||||
data.milestone.full_path = _this.currentProject.full_path;
|
data.milestone.full_path = _this.currentProject.full_path;
|
||||||
data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
|
data.milestone.remaining = timeFor(data.milestone.due_date);
|
||||||
data.milestone.name = data.milestone.title;
|
data.milestone.name = data.milestone.title;
|
||||||
$value.html(milestoneLinkTemplate(data.milestone));
|
$value.html(milestoneLinkTemplate(data.milestone));
|
||||||
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
|
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
|
hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
|
||||||
documentationPath: metricsData.documentationPath,
|
documentationPath: metricsData.documentationPath,
|
||||||
settingsPath: metricsData.settingsPath,
|
settingsPath: metricsData.settingsPath,
|
||||||
|
tagsPath: metricsData.tagsPath,
|
||||||
|
projectPath: metricsData.projectPath,
|
||||||
metricsEndpoint: metricsData.additionalMetrics,
|
metricsEndpoint: metricsData.additionalMetrics,
|
||||||
deploymentEndpoint: metricsData.deploymentEndpoint,
|
deploymentEndpoint: metricsData.deploymentEndpoint,
|
||||||
emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
|
emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
|
||||||
|
|
@ -112,6 +114,8 @@
|
||||||
:hover-data="hoverData"
|
:hover-data="hoverData"
|
||||||
:update-aspect-ratio="updateAspectRatio"
|
:update-aspect-ratio="updateAspectRatio"
|
||||||
:deployment-data="store.deploymentData"
|
:deployment-data="store.deploymentData"
|
||||||
|
:project-path="projectPath"
|
||||||
|
:tags-path="tagsPath"
|
||||||
/>
|
/>
|
||||||
</graph-group>
|
</graph-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,14 @@
|
||||||
required: false,
|
required: false,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
projectPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
tagsPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [MonitoringMixin],
|
mixins: [MonitoringMixin],
|
||||||
|
|
@ -251,6 +259,14 @@
|
||||||
:line-color="path.lineColor"
|
:line-color="path.lineColor"
|
||||||
:area-color="path.areaColor"
|
:area-color="path.areaColor"
|
||||||
/>
|
/>
|
||||||
|
<rect
|
||||||
|
class="prometheus-graph-overlay"
|
||||||
|
:width="(graphWidth - 70)"
|
||||||
|
:height="(graphHeight - 100)"
|
||||||
|
transform="translate(-5, 20)"
|
||||||
|
ref="graphOverlay"
|
||||||
|
@mousemove="handleMouseOverGraph($event)">
|
||||||
|
</rect>
|
||||||
<graph-deployment
|
<graph-deployment
|
||||||
:show-deploy-info="showDeployInfo"
|
:show-deploy-info="showDeployInfo"
|
||||||
:deployment-data="reducedDeploymentData"
|
:deployment-data="reducedDeploymentData"
|
||||||
|
|
@ -267,14 +283,6 @@
|
||||||
:graph-height-offset="graphHeightOffset"
|
:graph-height-offset="graphHeightOffset"
|
||||||
:show-flag-content="showFlagContent"
|
:show-flag-content="showFlagContent"
|
||||||
/>
|
/>
|
||||||
<rect
|
|
||||||
class="prometheus-graph-overlay"
|
|
||||||
:width="(graphWidth - 70)"
|
|
||||||
:height="(graphHeight - 100)"
|
|
||||||
transform="translate(-5, 20)"
|
|
||||||
ref="graphOverlay"
|
|
||||||
@mousemove="handleMouseOverGraph($event)">
|
|
||||||
</rect>
|
|
||||||
</svg>
|
</svg>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
|
import { dateFormatWithName, timeFormat } from '../../utils/date_time_formatters';
|
||||||
|
import Icon from '../../../vue_shared/components/icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -25,6 +26,10 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
calculatedHeight() {
|
calculatedHeight() {
|
||||||
return this.graphHeight - this.graphHeightOffset;
|
return this.graphHeight - this.graphHeightOffset;
|
||||||
|
|
@ -33,7 +38,7 @@
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
refText(d) {
|
refText(d) {
|
||||||
return d.tag ? d.ref : d.sha.slice(0, 6);
|
return d.tag ? d.ref : d.sha.slice(0, 8);
|
||||||
},
|
},
|
||||||
|
|
||||||
formatTime(deploymentTime) {
|
formatTime(deploymentTime) {
|
||||||
|
|
@ -41,7 +46,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
formatDate(deploymentTime) {
|
formatDate(deploymentTime) {
|
||||||
return dateFormat(deploymentTime);
|
return dateFormatWithName(deploymentTime);
|
||||||
},
|
},
|
||||||
|
|
||||||
nameDeploymentClass(deployment) {
|
nameDeploymentClass(deployment) {
|
||||||
|
|
@ -54,11 +59,19 @@
|
||||||
|
|
||||||
positionFlag(deployment) {
|
positionFlag(deployment) {
|
||||||
let xPosition = 3;
|
let xPosition = 3;
|
||||||
if (deployment.xPos > (this.graphWidth - 200)) {
|
if (deployment.xPos > (this.graphWidth - 225)) {
|
||||||
xPosition = -97;
|
xPosition = -142;
|
||||||
}
|
}
|
||||||
return xPosition;
|
return xPosition;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
svgContainerHeight(tag) {
|
||||||
|
let svgHeight = 80;
|
||||||
|
if (!tag) {
|
||||||
|
svgHeight -= 20;
|
||||||
|
}
|
||||||
|
return svgHeight;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -91,35 +104,75 @@
|
||||||
class="js-deploy-info-box"
|
class="js-deploy-info-box"
|
||||||
:x="positionFlag(deployment)"
|
:x="positionFlag(deployment)"
|
||||||
y="0"
|
y="0"
|
||||||
width="92"
|
width="134"
|
||||||
height="60">
|
:height="svgContainerHeight(deployment.tag)">
|
||||||
<rect
|
<rect
|
||||||
class="rect-text-metric deploy-info-rect rect-metric"
|
class="rect-text-metric deploy-info-rect rect-metric"
|
||||||
x="1"
|
x="1"
|
||||||
y="1"
|
y="1"
|
||||||
rx="2"
|
rx="2"
|
||||||
width="90"
|
width="132"
|
||||||
height="58">
|
:height="svgContainerHeight(deployment.tag) - 2">
|
||||||
</rect>
|
</rect>
|
||||||
<g
|
|
||||||
transform="translate(5, 2)">
|
|
||||||
<text
|
|
||||||
class="deploy-info-text text-metric-bold">
|
|
||||||
{{refText(deployment)}}
|
|
||||||
</text>
|
|
||||||
</g>
|
|
||||||
<text
|
|
||||||
class="deploy-info-text"
|
|
||||||
y="18"
|
|
||||||
transform="translate(5, 2)">
|
|
||||||
{{formatDate(deployment.time)}}
|
|
||||||
</text>
|
|
||||||
<text
|
<text
|
||||||
class="deploy-info-text text-metric-bold"
|
class="deploy-info-text text-metric-bold"
|
||||||
y="38"
|
|
||||||
transform="translate(5, 2)">
|
transform="translate(5, 2)">
|
||||||
{{formatTime(deployment.time)}}
|
Deployed
|
||||||
</text>
|
</text>
|
||||||
|
<!--The date info-->
|
||||||
|
<g transform="translate(5, 20)">
|
||||||
|
<text class="deploy-info-text">
|
||||||
|
{{formatDate(deployment.time)}}
|
||||||
|
</text>
|
||||||
|
<text
|
||||||
|
class="deploy-info-text text-metric-bold"
|
||||||
|
x="62">
|
||||||
|
{{formatTime(deployment.time)}}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<line
|
||||||
|
class="divider-line"
|
||||||
|
x1="0"
|
||||||
|
y1="38"
|
||||||
|
x2="132"
|
||||||
|
:y2="38"
|
||||||
|
stroke="#000">
|
||||||
|
</line>
|
||||||
|
<!--Commit information-->
|
||||||
|
<g transform="translate(5, 40)">
|
||||||
|
<icon
|
||||||
|
name="commit"
|
||||||
|
:width="12"
|
||||||
|
:height="12"
|
||||||
|
:y="3">
|
||||||
|
</icon>
|
||||||
|
<a :xlink:href="deployment.commitUrl">
|
||||||
|
<text
|
||||||
|
class="deploy-info-text deploy-info-text-link"
|
||||||
|
transform="translate(20, 2)">
|
||||||
|
{{refText(deployment)}}
|
||||||
|
</text>
|
||||||
|
</a>
|
||||||
|
</g>
|
||||||
|
<!--Tag information-->
|
||||||
|
<g
|
||||||
|
transform="translate(5, 55)"
|
||||||
|
v-if="deployment.tag">
|
||||||
|
<icon
|
||||||
|
name="label"
|
||||||
|
:width="12"
|
||||||
|
:height="12"
|
||||||
|
:y="5">
|
||||||
|
</icon>
|
||||||
|
<a :xlink:href="deployment.tagUrl">
|
||||||
|
<text
|
||||||
|
class="deploy-info-text deploy-info-text-link"
|
||||||
|
transform="translate(20, 2)"
|
||||||
|
y="2">
|
||||||
|
{{deployment.tag}}
|
||||||
|
</text>
|
||||||
|
</a>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</g>
|
</g>
|
||||||
<svg
|
<svg
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,9 @@ const mixins = {
|
||||||
id: deployment.id,
|
id: deployment.id,
|
||||||
time,
|
time,
|
||||||
sha: deployment.sha,
|
sha: deployment.sha,
|
||||||
|
commitUrl: `${this.projectPath}/commit/${deployment.sha}`,
|
||||||
tag: deployment.tag,
|
tag: deployment.tag,
|
||||||
|
tagUrl: `${this.tagsPath}/${deployment.tag}`,
|
||||||
ref: deployment.ref.name,
|
ref: deployment.ref.name,
|
||||||
xPos,
|
xPos,
|
||||||
showDeploymentFlag: false,
|
showDeploymentFlag: false,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import d3 from 'd3';
|
import d3 from 'd3';
|
||||||
|
|
||||||
export const dateFormat = d3.time.format('%b %-d, %Y');
|
export const dateFormat = d3.time.format('%b %-d, %Y');
|
||||||
|
export const dateFormatWithName = d3.time.format('%a, %b %-d');
|
||||||
export const timeFormat = d3.time.format('%-I:%M%p');
|
export const timeFormat = d3.time.format('%-I:%M%p');
|
||||||
export const bisectDate = d3.bisector(d => d.time).left;
|
export const bisectDate = d3.bisector(d => d.time).left;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, no-var, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */
|
/* eslint-disable func-names, space-before-function-paren, no-var, comma-dangle, object-shorthand, no-else-return, prefer-template, quotes, prefer-arrow-callback, max-len */
|
||||||
import Api from './api';
|
import Api from './api';
|
||||||
import './lib/utils/url_utility';
|
import { mergeUrlParams } from './lib/utils/url_utility';
|
||||||
|
|
||||||
export default class NamespaceSelect {
|
export default class NamespaceSelect {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
|
|
@ -50,7 +50,7 @@ export default class NamespaceSelect {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url(namespace) {
|
url(namespace) {
|
||||||
return gl.utils.mergeUrlParams({ [fieldName]: namespace.id }, window.location.href);
|
return mergeUrlParams({ [fieldName]: namespace.id }, window.location.href);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import Autosize from 'autosize';
|
||||||
import 'vendor/jquery.caret'; // required by jquery.atwho
|
import 'vendor/jquery.caret'; // required by jquery.atwho
|
||||||
import 'vendor/jquery.atwho';
|
import 'vendor/jquery.atwho';
|
||||||
import AjaxCache from '~/lib/utils/ajax_cache';
|
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||||
|
import { getLocationHash } from './lib/utils/url_utility';
|
||||||
import Flash from './flash';
|
import Flash from './flash';
|
||||||
import CommentTypeToggle from './comment_type_toggle';
|
import CommentTypeToggle from './comment_type_toggle';
|
||||||
import GLForm from './gl_form';
|
import GLForm from './gl_form';
|
||||||
|
|
@ -24,6 +25,7 @@ import Autosave from './autosave';
|
||||||
import TaskList from './task_list';
|
import TaskList from './task_list';
|
||||||
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
|
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
|
||||||
import imageDiffHelper from './image_diff/helpers/index';
|
import imageDiffHelper from './image_diff/helpers/index';
|
||||||
|
import { localTimeAgo } from './lib/utils/datetime_utility';
|
||||||
|
|
||||||
window.autosize = Autosize;
|
window.autosize = Autosize;
|
||||||
|
|
||||||
|
|
@ -310,7 +312,7 @@ export default class Notes {
|
||||||
|
|
||||||
setupNewNote($note) {
|
setupNewNote($note) {
|
||||||
// Update datetime format on the recent note
|
// Update datetime format on the recent note
|
||||||
gl.utils.localTimeAgo($note.find('.js-timeago'), false);
|
localTimeAgo($note.find('.js-timeago'), false);
|
||||||
|
|
||||||
this.collapseLongCommitList();
|
this.collapseLongCommitList();
|
||||||
this.taskList.init();
|
this.taskList.init();
|
||||||
|
|
@ -330,7 +332,7 @@ export default class Notes {
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateNoteTargetSelector($note) {
|
static updateNoteTargetSelector($note) {
|
||||||
const hash = gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
// Needs to be an explicit true/false for the jQuery `toggleClass(force)`
|
// Needs to be an explicit true/false for the jQuery `toggleClass(force)`
|
||||||
const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
|
const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
|
||||||
$note.toggleClass('target', addTargetClass);
|
$note.toggleClass('target', addTargetClass);
|
||||||
|
|
@ -462,7 +464,7 @@ export default class Notes {
|
||||||
this.renderDiscussionAvatar(diffAvatarContainer, noteEntity);
|
this.renderDiscussionAvatar(diffAvatarContainer, noteEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
gl.utils.localTimeAgo($('.js-timeago'), false);
|
localTimeAgo($('.js-timeago'), false);
|
||||||
Notes.checkMergeRequestStatus();
|
Notes.checkMergeRequestStatus();
|
||||||
return this.updateNotesCount(1);
|
return this.updateNotesCount(1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,29 @@
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
|
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
|
||||||
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
|
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||||
import issueDiscussionLockedWidget from './issue_discussion_locked_widget.vue';
|
import discussionLockedWidget from './discussion_locked_widget.vue';
|
||||||
import markdownField from '../../vue_shared/components/markdown/field.vue';
|
import markdownField from '../../vue_shared/components/markdown/field.vue';
|
||||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
import issuableStateMixin from '../mixins/issuable_state';
|
import issuableStateMixin from '../mixins/issuable_state';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'issueCommentForm',
|
name: 'commentForm',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
note: '',
|
note: '',
|
||||||
noteType: constants.COMMENT,
|
noteType: constants.COMMENT,
|
||||||
// Can't use mapGetters,
|
// Can't use mapGetters,
|
||||||
// this needs to be in the data object because it belongs to the state
|
// this needs to be in the data object because it belongs to the state
|
||||||
issueState: this.$store.getters.getIssueData.state,
|
issueState: this.$store.getters.getNoteableData.state,
|
||||||
isSubmitting: false,
|
isSubmitting: false,
|
||||||
isSubmitButtonDisabled: true,
|
isSubmitButtonDisabled: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
issueWarning,
|
issueWarning,
|
||||||
issueNoteSignedOutWidget,
|
noteSignedOutWidget,
|
||||||
issueDiscussionLockedWidget,
|
discussionLockedWidget,
|
||||||
markdownField,
|
markdownField,
|
||||||
userAvatarLink,
|
userAvatarLink,
|
||||||
},
|
},
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'getCurrentUserLastNote',
|
'getCurrentUserLastNote',
|
||||||
'getUserData',
|
'getUserData',
|
||||||
'getIssueData',
|
'getNoteableData',
|
||||||
'getNotesData',
|
'getNotesData',
|
||||||
]),
|
]),
|
||||||
isLoggedIn() {
|
isLoggedIn() {
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
return this.issueState === constants.OPENED || this.issueState === constants.REOPENED;
|
return this.issueState === constants.OPENED || this.issueState === constants.REOPENED;
|
||||||
},
|
},
|
||||||
canCreateNote() {
|
canCreateNote() {
|
||||||
return this.getIssueData.current_user.can_create_note;
|
return this.getNoteableData.current_user.can_create_note;
|
||||||
},
|
},
|
||||||
issueActionButtonTitle() {
|
issueActionButtonTitle() {
|
||||||
if (this.note.length) {
|
if (this.note.length) {
|
||||||
|
|
@ -85,16 +85,16 @@
|
||||||
return this.getNotesData.quickActionsDocsPath;
|
return this.getNotesData.quickActionsDocsPath;
|
||||||
},
|
},
|
||||||
markdownPreviewPath() {
|
markdownPreviewPath() {
|
||||||
return this.getIssueData.preview_note_path;
|
return this.getNoteableData.preview_note_path;
|
||||||
},
|
},
|
||||||
author() {
|
author() {
|
||||||
return this.getUserData;
|
return this.getUserData;
|
||||||
},
|
},
|
||||||
canUpdateIssue() {
|
canUpdateIssue() {
|
||||||
return this.getIssueData.current_user.can_update;
|
return this.getNoteableData.current_user.can_update;
|
||||||
},
|
},
|
||||||
endpoint() {
|
endpoint() {
|
||||||
return this.getIssueData.create_note_path;
|
return this.getNoteableData.create_note_path;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
data: {
|
data: {
|
||||||
note: {
|
note: {
|
||||||
noteable_type: constants.NOTEABLE_TYPE,
|
noteable_type: constants.NOTEABLE_TYPE,
|
||||||
noteable_id: this.getIssueData.id,
|
noteable_id: this.getNoteableData.id,
|
||||||
note: this.note,
|
note: this.note,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -207,7 +207,7 @@
|
||||||
},
|
},
|
||||||
initAutoSave() {
|
initAutoSave() {
|
||||||
if (this.isLoggedIn) {
|
if (this.isLoggedIn) {
|
||||||
this.autosave = new Autosave($(this.$refs.textarea), ['Note', 'Issue', this.getIssueData.id], 'issue');
|
this.autosave = new Autosave($(this.$refs.textarea), ['Note', 'Issue', this.getNoteableData.id], 'issue');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
initTaskList() {
|
initTaskList() {
|
||||||
|
|
@ -240,8 +240,11 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<issue-note-signed-out-widget v-if="!isLoggedIn" />
|
<note-signed-out-widget v-if="!isLoggedIn" />
|
||||||
<issue-discussion-locked-widget v-else-if="!canCreateNote" />
|
<discussion-locked-widget
|
||||||
|
issuable-type="issue"
|
||||||
|
v-else-if="!canCreateNote"
|
||||||
|
/>
|
||||||
<ul
|
<ul
|
||||||
v-else
|
v-else
|
||||||
class="notes notes-form timeline">
|
class="notes notes-form timeline">
|
||||||
|
|
@ -266,9 +269,9 @@
|
||||||
<div class="error-alert"></div>
|
<div class="error-alert"></div>
|
||||||
|
|
||||||
<issue-warning
|
<issue-warning
|
||||||
v-if="hasWarning(getIssueData)"
|
v-if="hasWarning(getNoteableData)"
|
||||||
:is-locked="isLocked(getIssueData)"
|
:is-locked="isLocked(getNoteableData)"
|
||||||
:is-confidential="isConfidential(getIssueData)"
|
:is-confidential="isConfidential(getNoteableData)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<markdown-field
|
<markdown-field
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import Icon from '../../vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import Issuable from '~/vue_shared/mixins/issuable';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
component: {
|
mixins: [
|
||||||
|
Issuable,
|
||||||
|
],
|
||||||
|
components: {
|
||||||
Icon,
|
Icon,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -16,7 +20,7 @@
|
||||||
:size="16"
|
:size="16"
|
||||||
class="icon">
|
class="icon">
|
||||||
</icon>
|
</icon>
|
||||||
<span>This issue is locked. Only <b>project members</b> can comment.</span>
|
<span>This {{ issuableDisplayName }} is locked. Only <b>project members</b> can comment.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
import emojiSmiley from 'icons/_emoji_smiley.svg';
|
import emojiSmiley from 'icons/_emoji_smiley.svg';
|
||||||
import editSvg from 'icons/_icon_pencil.svg';
|
import editSvg from 'icons/_icon_pencil.svg';
|
||||||
import ellipsisSvg from 'icons/_ellipsis_v.svg';
|
import ellipsisSvg from 'icons/_ellipsis_v.svg';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||||
import tooltip from '../../vue_shared/directives/tooltip';
|
import tooltip from '~/vue_shared/directives/tooltip';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'issueNoteActions',
|
name: 'noteActions',
|
||||||
props: {
|
props: {
|
||||||
authorId: {
|
authorId: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
<div class="note-actions">
|
<div class="note-actions">
|
||||||
<span
|
<span
|
||||||
v-if="accessLevel"
|
v-if="accessLevel"
|
||||||
class="note-role note-role-access">{{accessLevel}}</span>
|
class="note-role user-access-role">{{accessLevel}}</span>
|
||||||
<div
|
<div
|
||||||
v-if="canAddAwardEmoji"
|
v-if="canAddAwardEmoji"
|
||||||
class="note-actions-item">
|
class="note-actions-item">
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'issueNoteAttachment',
|
name: 'noteAttachment',
|
||||||
props: {
|
props: {
|
||||||
attachment: {
|
attachment: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
import issueNoteEditedText from './issue_note_edited_text.vue';
|
import noteEditedText from './note_edited_text.vue';
|
||||||
import issueNoteAwardsList from './issue_note_awards_list.vue';
|
import noteAwardsList from './note_awards_list.vue';
|
||||||
import issueNoteAttachment from './issue_note_attachment.vue';
|
import noteAttachment from './note_attachment.vue';
|
||||||
import issueNoteForm from './issue_note_form.vue';
|
import noteForm from './note_form.vue';
|
||||||
import TaskList from '../../task_list';
|
import TaskList from '../../task_list';
|
||||||
import autosave from '../mixins/autosave';
|
import autosave from '../mixins/autosave';
|
||||||
|
|
||||||
|
|
@ -26,10 +26,10 @@
|
||||||
autosave,
|
autosave,
|
||||||
],
|
],
|
||||||
components: {
|
components: {
|
||||||
issueNoteEditedText,
|
noteEditedText,
|
||||||
issueNoteAwardsList,
|
noteAwardsList,
|
||||||
issueNoteAttachment,
|
noteAttachment,
|
||||||
issueNoteForm,
|
noteForm,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
noteBody() {
|
noteBody() {
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
<div
|
<div
|
||||||
v-html="note.note_html"
|
v-html="note.note_html"
|
||||||
class="note-text md"></div>
|
class="note-text md"></div>
|
||||||
<issue-note-form
|
<note-form
|
||||||
v-if="isEditing"
|
v-if="isEditing"
|
||||||
ref="noteForm"
|
ref="noteForm"
|
||||||
@handleFormUpdate="handleFormUpdate"
|
@handleFormUpdate="handleFormUpdate"
|
||||||
|
|
@ -101,20 +101,20 @@
|
||||||
v-model="note.note"
|
v-model="note.note"
|
||||||
:data-update-url="note.path"
|
:data-update-url="note.path"
|
||||||
class="hidden js-task-list-field"></textarea>
|
class="hidden js-task-list-field"></textarea>
|
||||||
<issue-note-edited-text
|
<note-edited-text
|
||||||
v-if="note.last_edited_at"
|
v-if="note.last_edited_at"
|
||||||
:edited-at="note.last_edited_at"
|
:edited-at="note.last_edited_at"
|
||||||
:edited-by="note.last_edited_by"
|
:edited-by="note.last_edited_by"
|
||||||
action-text="Edited"
|
action-text="Edited"
|
||||||
/>
|
/>
|
||||||
<issue-note-awards-list
|
<note-awards-list
|
||||||
v-if="note.award_emoji.length"
|
v-if="note.award_emoji.length"
|
||||||
:note-id="note.id"
|
:note-id="note.id"
|
||||||
:note-author-id="note.author.id"
|
:note-author-id="note.author.id"
|
||||||
:awards="note.award_emoji"
|
:awards="note.award_emoji"
|
||||||
:toggle-award-path="note.toggle_award_path"
|
:toggle-award-path="note.toggle_award_path"
|
||||||
/>
|
/>
|
||||||
<issue-note-attachment
|
<note-attachment
|
||||||
v-if="note.attachment"
|
v-if="note.attachment"
|
||||||
:attachment="note.attachment"
|
:attachment="note.attachment"
|
||||||
/>
|
/>
|
||||||
|
|
@ -46,8 +46,8 @@
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'getDiscussionLastNote',
|
'getDiscussionLastNote',
|
||||||
'getIssueData',
|
'getNoteableData',
|
||||||
'getIssueDataByProp',
|
'getNoteableDataByProp',
|
||||||
'getNotesDataByProp',
|
'getNotesDataByProp',
|
||||||
'getUserDataByProp',
|
'getUserDataByProp',
|
||||||
]),
|
]),
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
return `#note_${this.noteId}`;
|
return `#note_${this.noteId}`;
|
||||||
},
|
},
|
||||||
markdownPreviewPath() {
|
markdownPreviewPath() {
|
||||||
return this.getIssueDataByProp('preview_note_path');
|
return this.getNoteableDataByProp('preview_note_path');
|
||||||
},
|
},
|
||||||
markdownDocsPath() {
|
markdownDocsPath() {
|
||||||
return this.getNotesDataByProp('markdownDocsPath');
|
return this.getNotesDataByProp('markdownDocsPath');
|
||||||
|
|
@ -129,9 +129,9 @@
|
||||||
class="edit-note common-note-form js-quick-submit gfm-form">
|
class="edit-note common-note-form js-quick-submit gfm-form">
|
||||||
|
|
||||||
<issue-warning
|
<issue-warning
|
||||||
v-if="hasWarning(getIssueData)"
|
v-if="hasWarning(getNoteableData)"
|
||||||
:is-locked="isLocked(getIssueData)"
|
:is-locked="isLocked(getNoteableData)"
|
||||||
:is-confidential="isConfidential(getIssueData)"
|
:is-confidential="isConfidential(getNoteableData)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<markdown-field
|
<markdown-field
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'singInLinksNotes',
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'getNotesDataByProp',
|
'getNotesDataByProp',
|
||||||
|
|
@ -2,13 +2,12 @@
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import Flash from '../../flash';
|
import Flash from '../../flash';
|
||||||
import { SYSTEM_NOTE } from '../constants';
|
import { SYSTEM_NOTE } from '../constants';
|
||||||
import issueNote from './issue_note.vue';
|
|
||||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
import issueNoteHeader from './issue_note_header.vue';
|
import noteableNote from './noteable_note.vue';
|
||||||
import issueNoteActions from './issue_note_actions.vue';
|
import noteHeader from './note_header.vue';
|
||||||
import issueNoteSignedOutWidget from './issue_note_signed_out_widget.vue';
|
import noteSignedOutWidget from './note_signed_out_widget.vue';
|
||||||
import issueNoteEditedText from './issue_note_edited_text.vue';
|
import noteEditedText from './note_edited_text.vue';
|
||||||
import issueNoteForm from './issue_note_form.vue';
|
import noteForm from './note_form.vue';
|
||||||
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
|
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
|
||||||
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
|
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
|
||||||
import autosave from '../mixins/autosave';
|
import autosave from '../mixins/autosave';
|
||||||
|
|
@ -26,13 +25,12 @@
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
issueNote,
|
noteableNote,
|
||||||
userAvatarLink,
|
userAvatarLink,
|
||||||
issueNoteHeader,
|
noteHeader,
|
||||||
issueNoteActions,
|
noteSignedOutWidget,
|
||||||
issueNoteSignedOutWidget,
|
noteEditedText,
|
||||||
issueNoteEditedText,
|
noteForm,
|
||||||
issueNoteForm,
|
|
||||||
placeholderNote,
|
placeholderNote,
|
||||||
placeholderSystemNote,
|
placeholderSystemNote,
|
||||||
},
|
},
|
||||||
|
|
@ -41,7 +39,7 @@
|
||||||
],
|
],
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'getIssueData',
|
'getNoteableData',
|
||||||
]),
|
]),
|
||||||
discussion() {
|
discussion() {
|
||||||
return this.note.notes[0];
|
return this.note.notes[0];
|
||||||
|
|
@ -50,10 +48,10 @@
|
||||||
return this.discussion.author;
|
return this.discussion.author;
|
||||||
},
|
},
|
||||||
canReply() {
|
canReply() {
|
||||||
return this.getIssueData.current_user.can_create_note;
|
return this.getNoteableData.current_user.can_create_note;
|
||||||
},
|
},
|
||||||
newNotePath() {
|
newNotePath() {
|
||||||
return this.getIssueData.create_note_path;
|
return this.getNoteableData.create_note_path;
|
||||||
},
|
},
|
||||||
lastUpdatedBy() {
|
lastUpdatedBy() {
|
||||||
const { notes } = this.note;
|
const { notes } = this.note;
|
||||||
|
|
@ -88,7 +86,7 @@
|
||||||
return placeholderNote;
|
return placeholderNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
return issueNote;
|
return noteableNote;
|
||||||
},
|
},
|
||||||
componentData(note) {
|
componentData(note) {
|
||||||
return note.isPlaceholderNote ? note.notes[0] : note;
|
return note.isPlaceholderNote ? note.notes[0] : note;
|
||||||
|
|
@ -171,7 +169,7 @@
|
||||||
<div class="timeline-content">
|
<div class="timeline-content">
|
||||||
<div class="discussion">
|
<div class="discussion">
|
||||||
<div class="discussion-header">
|
<div class="discussion-header">
|
||||||
<issue-note-header
|
<note-header
|
||||||
:author="author"
|
:author="author"
|
||||||
:created-at="discussion.created_at"
|
:created-at="discussion.created_at"
|
||||||
:note-id="discussion.id"
|
:note-id="discussion.id"
|
||||||
|
|
@ -179,8 +177,8 @@
|
||||||
@toggleHandler="toggleDiscussionHandler"
|
@toggleHandler="toggleDiscussionHandler"
|
||||||
action-text="started a discussion"
|
action-text="started a discussion"
|
||||||
class="discussion"
|
class="discussion"
|
||||||
/>
|
/>
|
||||||
<issue-note-edited-text
|
<note-edited-text
|
||||||
v-if="lastUpdatedAt"
|
v-if="lastUpdatedAt"
|
||||||
:edited-at="lastUpdatedAt"
|
:edited-at="lastUpdatedAt"
|
||||||
:edited-by="lastUpdatedBy"
|
:edited-by="lastUpdatedBy"
|
||||||
|
|
@ -211,7 +209,7 @@
|
||||||
type="button"
|
type="button"
|
||||||
class="js-vue-discussion-reply btn btn-text-field"
|
class="js-vue-discussion-reply btn btn-text-field"
|
||||||
title="Add a reply">Reply...</button>
|
title="Add a reply">Reply...</button>
|
||||||
<issue-note-form
|
<note-form
|
||||||
v-if="isReplying"
|
v-if="isReplying"
|
||||||
save-button-title="Comment"
|
save-button-title="Comment"
|
||||||
:discussion="note"
|
:discussion="note"
|
||||||
|
|
@ -220,7 +218,7 @@
|
||||||
@cancelFormEdition="cancelReplyForm"
|
@cancelFormEdition="cancelReplyForm"
|
||||||
ref="noteForm"
|
ref="noteForm"
|
||||||
/>
|
/>
|
||||||
<issue-note-signed-out-widget v-if="!canReply" />
|
<note-signed-out-widget v-if="!canReply" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { escape } from 'underscore';
|
||||||
import Flash from '../../flash';
|
import Flash from '../../flash';
|
||||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
import issueNoteHeader from './issue_note_header.vue';
|
import noteHeader from './note_header.vue';
|
||||||
import issueNoteActions from './issue_note_actions.vue';
|
import noteActions from './note_actions.vue';
|
||||||
import issueNoteBody from './issue_note_body.vue';
|
import noteBody from './note_body.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -23,9 +24,9 @@
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
userAvatarLink,
|
userAvatarLink,
|
||||||
issueNoteHeader,
|
noteHeader,
|
||||||
issueNoteActions,
|
noteActions,
|
||||||
issueNoteBody,
|
noteBody,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
|
|
@ -85,7 +86,7 @@
|
||||||
};
|
};
|
||||||
this.isRequesting = true;
|
this.isRequesting = true;
|
||||||
this.oldContent = this.note.note_html;
|
this.oldContent = this.note.note_html;
|
||||||
this.note.note_html = noteText;
|
this.note.note_html = escape(noteText);
|
||||||
|
|
||||||
this.updateNote(data)
|
this.updateNote(data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -122,9 +123,7 @@
|
||||||
// we need to do this to prevent noteForm inconsistent content warning
|
// we need to do this to prevent noteForm inconsistent content warning
|
||||||
// this is something we intentionally do so we need to recover the content
|
// this is something we intentionally do so we need to recover the content
|
||||||
this.note.note = noteText;
|
this.note.note = noteText;
|
||||||
if (this.$refs.noteBody) {
|
this.$refs.noteBody.$refs.noteForm.note = noteText;
|
||||||
this.$refs.noteBody.$refs.noteForm.note = noteText; // TODO: This could be better
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|
@ -155,13 +154,13 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="timeline-content">
|
<div class="timeline-content">
|
||||||
<div class="note-header">
|
<div class="note-header">
|
||||||
<issue-note-header
|
<note-header
|
||||||
:author="author"
|
:author="author"
|
||||||
:created-at="note.created_at"
|
:created-at="note.created_at"
|
||||||
:note-id="note.id"
|
:note-id="note.id"
|
||||||
action-text="commented"
|
action-text="commented"
|
||||||
/>
|
/>
|
||||||
<issue-note-actions
|
<note-actions
|
||||||
:author-id="author.id"
|
:author-id="author.id"
|
||||||
:note-id="note.id"
|
:note-id="note.id"
|
||||||
:access-level="note.human_access"
|
:access-level="note.human_access"
|
||||||
|
|
@ -173,7 +172,7 @@
|
||||||
@handleDelete="deleteHandler"
|
@handleDelete="deleteHandler"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<issue-note-body
|
<note-body
|
||||||
:note="note"
|
:note="note"
|
||||||
:can-edit="note.current_user.can_edit"
|
:can-edit="note.current_user.can_edit"
|
||||||
:is-editing="isEditing"
|
:is-editing="isEditing"
|
||||||
|
|
@ -1,20 +1,21 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { getLocationHash } from '../../lib/utils/url_utility';
|
||||||
import Flash from '../../flash';
|
import Flash from '../../flash';
|
||||||
import store from '../stores/';
|
import store from '../stores/';
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
import issueNote from './issue_note.vue';
|
import noteableNote from './noteable_note.vue';
|
||||||
import issueDiscussion from './issue_discussion.vue';
|
import noteableDiscussion from './noteable_discussion.vue';
|
||||||
import systemNote from '../../vue_shared/components/notes/system_note.vue';
|
import systemNote from '../../vue_shared/components/notes/system_note.vue';
|
||||||
import issueCommentForm from './issue_comment_form.vue';
|
import commentForm from './comment_form.vue';
|
||||||
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
|
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
|
||||||
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
|
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
|
||||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'issueNotesApp',
|
name: 'notesApp',
|
||||||
props: {
|
props: {
|
||||||
issueData: {
|
noteableData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
|
@ -35,10 +36,10 @@
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
issueNote,
|
noteableNote,
|
||||||
issueDiscussion,
|
noteableDiscussion,
|
||||||
systemNote,
|
systemNote,
|
||||||
issueCommentForm,
|
commentForm,
|
||||||
loadingIcon,
|
loadingIcon,
|
||||||
placeholderNote,
|
placeholderNote,
|
||||||
placeholderSystemNote,
|
placeholderSystemNote,
|
||||||
|
|
@ -56,7 +57,7 @@
|
||||||
actionToggleAward: 'toggleAward',
|
actionToggleAward: 'toggleAward',
|
||||||
scrollToNoteIfNeeded: 'scrollToNoteIfNeeded',
|
scrollToNoteIfNeeded: 'scrollToNoteIfNeeded',
|
||||||
setNotesData: 'setNotesData',
|
setNotesData: 'setNotesData',
|
||||||
setIssueData: 'setIssueData',
|
setNoteableData: 'setNoteableData',
|
||||||
setUserData: 'setUserData',
|
setUserData: 'setUserData',
|
||||||
setLastFetchedAt: 'setLastFetchedAt',
|
setLastFetchedAt: 'setLastFetchedAt',
|
||||||
setTargetNoteHash: 'setTargetNoteHash',
|
setTargetNoteHash: 'setTargetNoteHash',
|
||||||
|
|
@ -68,10 +69,10 @@
|
||||||
}
|
}
|
||||||
return placeholderNote;
|
return placeholderNote;
|
||||||
} else if (note.individual_note) {
|
} else if (note.individual_note) {
|
||||||
return note.notes[0].system ? systemNote : issueNote;
|
return note.notes[0].system ? systemNote : noteableNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
return issueDiscussion;
|
return noteableDiscussion;
|
||||||
},
|
},
|
||||||
getComponentData(note) {
|
getComponentData(note) {
|
||||||
return note.individual_note ? note.notes[0] : note;
|
return note.individual_note ? note.notes[0] : note;
|
||||||
|
|
@ -86,7 +87,7 @@
|
||||||
.then(() => this.checkLocationHash())
|
.then(() => this.checkLocationHash())
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
Flash('Something went wrong while fetching issue comments. Please try again.');
|
Flash('Something went wrong while fetching comments. Please try again.');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initPolling() {
|
initPolling() {
|
||||||
|
|
@ -95,7 +96,7 @@
|
||||||
this.poll();
|
this.poll();
|
||||||
},
|
},
|
||||||
checkLocationHash() {
|
checkLocationHash() {
|
||||||
const hash = gl.utils.getLocationHash();
|
const hash = getLocationHash();
|
||||||
const element = document.getElementById(hash);
|
const element = document.getElementById(hash);
|
||||||
|
|
||||||
if (hash && element) {
|
if (hash && element) {
|
||||||
|
|
@ -106,7 +107,7 @@
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.setNotesData(this.notesData);
|
this.setNotesData(this.notesData);
|
||||||
this.setIssueData(this.issueData);
|
this.setNoteableData(this.noteableData);
|
||||||
this.setUserData(this.userData);
|
this.setUserData(this.userData);
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -146,6 +147,6 @@
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<issue-comment-form />
|
<comment-form />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import issueNotesApp from './components/issue_notes_app.vue';
|
import notesApp from './components/notes_app.vue';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => new Vue({
|
document.addEventListener('DOMContentLoaded', () => new Vue({
|
||||||
el: '#js-vue-notes',
|
el: '#js-vue-notes',
|
||||||
components: {
|
components: {
|
||||||
issueNotesApp,
|
notesApp,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const notesDataset = document.getElementById('js-vue-notes').dataset;
|
const notesDataset = document.getElementById('js-vue-notes').dataset;
|
||||||
|
const parsedUserData = JSON.parse(notesDataset.currentUserData);
|
||||||
|
const currentUserData = parsedUserData ? {
|
||||||
|
id: parsedUserData.id,
|
||||||
|
name: parsedUserData.name,
|
||||||
|
username: parsedUserData.username,
|
||||||
|
avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
|
||||||
|
path: parsedUserData.path,
|
||||||
|
} : {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
issueData: JSON.parse(notesDataset.issueData),
|
noteableData: JSON.parse(notesDataset.noteableData),
|
||||||
currentUserData: JSON.parse(notesDataset.currentUserData),
|
currentUserData,
|
||||||
notesData: {
|
notesData: {
|
||||||
lastFetchedAt: notesDataset.lastFetchedAt,
|
lastFetchedAt: notesDataset.lastFetchedAt,
|
||||||
discussionsPath: notesDataset.discussionsPath,
|
discussionsPath: notesDataset.discussionsPath,
|
||||||
|
|
@ -24,9 +32,9 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement('issue-notes-app', {
|
return createElement('notes-app', {
|
||||||
props: {
|
props: {
|
||||||
issueData: this.issueData,
|
noteableData: this.noteableData,
|
||||||
notesData: this.notesData,
|
notesData: this.notesData,
|
||||||
userData: this.currentUserData,
|
userData: this.currentUserData,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import Poll from '../../lib/utils/poll';
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
import * as utils from './utils';
|
import * as utils from './utils';
|
||||||
import * as constants from '../constants';
|
import * as constants from '../constants';
|
||||||
import service from '../services/issue_notes_service';
|
import service from '../services/notes_service';
|
||||||
import loadAwardsHandler from '../../awards_handler';
|
import loadAwardsHandler from '../../awards_handler';
|
||||||
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
|
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
|
||||||
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
|
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
|
||||||
|
|
@ -12,7 +12,7 @@ import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
|
||||||
let eTagPoll;
|
let eTagPoll;
|
||||||
|
|
||||||
export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
|
export const setNotesData = ({ commit }, data) => commit(types.SET_NOTES_DATA, data);
|
||||||
export const setIssueData = ({ commit }, data) => commit(types.SET_ISSUE_DATA, data);
|
export const setNoteableData = ({ commit }, data) => commit(types.SET_NOTEABLE_DATA, data);
|
||||||
export const setUserData = ({ commit }, data) => commit(types.SET_USER_DATA, data);
|
export const setUserData = ({ commit }, data) => commit(types.SET_USER_DATA, data);
|
||||||
export const setLastFetchedAt = ({ commit }, data) => commit(types.SET_LAST_FETCHED_AT, data);
|
export const setLastFetchedAt = ({ commit }, data) => commit(types.SET_LAST_FETCHED_AT, data);
|
||||||
export const setInitialNotes = ({ commit }, data) => commit(types.SET_INITIAL_NOTES, data);
|
export const setInitialNotes = ({ commit }, data) => commit(types.SET_INITIAL_NOTES, data);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ export const targetNoteHash = state => state.targetNoteHash;
|
||||||
export const getNotesData = state => state.notesData;
|
export const getNotesData = state => state.notesData;
|
||||||
export const getNotesDataByProp = state => prop => state.notesData[prop];
|
export const getNotesDataByProp = state => prop => state.notesData[prop];
|
||||||
|
|
||||||
export const getIssueData = state => state.issueData;
|
export const getNoteableData = state => state.noteableData;
|
||||||
export const getIssueDataByProp = state => prop => state.issueData[prop];
|
export const getNoteableDataByProp = state => prop => state.noteableData[prop];
|
||||||
|
|
||||||
export const getUserData = state => state.userData || {};
|
export const getUserData = state => state.userData || {};
|
||||||
export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
|
export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export default new Vuex.Store({
|
||||||
// holds endpoints and permissions provided through haml
|
// holds endpoints and permissions provided through haml
|
||||||
notesData: {},
|
notesData: {},
|
||||||
userData: {},
|
userData: {},
|
||||||
issueData: {},
|
noteableData: {},
|
||||||
},
|
},
|
||||||
actions,
|
actions,
|
||||||
getters,
|
getters,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ export const ADD_NEW_REPLY_TO_DISCUSSION = 'ADD_NEW_REPLY_TO_DISCUSSION';
|
||||||
export const DELETE_NOTE = 'DELETE_NOTE';
|
export const DELETE_NOTE = 'DELETE_NOTE';
|
||||||
export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES';
|
export const REMOVE_PLACEHOLDER_NOTES = 'REMOVE_PLACEHOLDER_NOTES';
|
||||||
export const SET_NOTES_DATA = 'SET_NOTES_DATA';
|
export const SET_NOTES_DATA = 'SET_NOTES_DATA';
|
||||||
export const SET_ISSUE_DATA = 'SET_ISSUE_DATA';
|
export const SET_NOTEABLE_DATA = 'SET_NOTEABLE_DATA';
|
||||||
export const SET_USER_DATA = 'SET_USER_DATA';
|
export const SET_USER_DATA = 'SET_USER_DATA';
|
||||||
export const SET_INITIAL_NOTES = 'SET_INITIAL_NOTES';
|
export const SET_INITIAL_NOTES = 'SET_INITIAL_NOTES';
|
||||||
export const SET_LAST_FETCHED_AT = 'SET_LAST_FETCHED_AT';
|
export const SET_LAST_FETCHED_AT = 'SET_LAST_FETCHED_AT';
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,8 @@ export default {
|
||||||
Object.assign(state, { notesData: data });
|
Object.assign(state, { notesData: data });
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.SET_ISSUE_DATA](state, data) {
|
[types.SET_NOTEABLE_DATA](state, data) {
|
||||||
Object.assign(state, { issueData: data });
|
Object.assign(state, { noteableData: data });
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.SET_USER_DATA](state, data) {
|
[types.SET_USER_DATA](state, data) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { getParameterByName } from '~/lib/utils/common_utils';
|
import { getParameterByName } from '~/lib/utils/common_utils';
|
||||||
import '~/lib/utils/url_utility';
|
import { removeParams } from './lib/utils/url_utility';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const ENDLESS_SCROLL_BOTTOM_PX = 400;
|
const ENDLESS_SCROLL_BOTTOM_PX = 400;
|
||||||
|
|
@ -7,7 +7,7 @@ import '~/lib/utils/url_utility';
|
||||||
|
|
||||||
const Pager = {
|
const Pager = {
|
||||||
init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
|
init(limit = 0, preload = false, disable = false, prepareData = $.noop, callback = $.noop) {
|
||||||
this.url = $('.content_list').data('href') || gl.utils.removeParams(['limit', 'offset']);
|
this.url = $('.content_list').data('href') || removeParams(['limit', 'offset']);
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
|
this.offset = parseInt(getParameterByName('offset'), 10) || this.limit;
|
||||||
this.disable = disable;
|
this.disable = disable;
|
||||||
|
|
|
||||||