New upstream version 8.12.1+dfsg1
This commit is contained in:
parent
2f43f6b133
commit
ab0093fc8a
1096 changed files with 26288 additions and 9715 deletions
|
@ -1 +1,2 @@
|
|||
*.erb
|
||||
lib/gitlab/sanitizers/svg/whitelist.rb
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -48,3 +48,4 @@
|
|||
/vendor/bundle/*
|
||||
/builds/*
|
||||
/shared/*
|
||||
/.gitlab_workhorse_secret
|
||||
|
|
|
@ -82,7 +82,7 @@ update-knapsack:
|
|||
- export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
|
||||
- export KNAPSACK_GENERATE_REPORT=true
|
||||
- cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH}
|
||||
- knapsack rspec
|
||||
- knapsack rspec "--color --format documentation"
|
||||
artifacts:
|
||||
expire_in: 31d
|
||||
paths:
|
||||
|
@ -206,10 +206,15 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
|
|||
- bundle exec $CI_BUILD_NAME
|
||||
|
||||
rubocop: *exec
|
||||
rake haml_lint: *exec
|
||||
rake scss_lint: *exec
|
||||
rake brakeman: *exec
|
||||
rake flog: *exec
|
||||
rake flay: *exec
|
||||
rake flog:
|
||||
<<: *exec
|
||||
allow_failure: yes
|
||||
rake flay:
|
||||
<<: *exec
|
||||
allow_failure: yes
|
||||
license_finder: *exec
|
||||
rake downtime_check: *exec
|
||||
|
||||
|
@ -248,6 +253,21 @@ bundler:audit:
|
|||
script:
|
||||
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
|
||||
|
||||
migration paths:
|
||||
stage: test
|
||||
<<: *use-db
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
script:
|
||||
- git checkout HEAD .
|
||||
- git fetch --tags
|
||||
- git checkout v8.5.9
|
||||
- 'echo test: unix:/var/opt/gitlab/redis/redis.socket > config/resque.yml'
|
||||
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" --retry=3
|
||||
- rake db:drop db:create db:schema:load db:seed_fu
|
||||
- git checkout $CI_BUILD_REF
|
||||
- rake db:migrate
|
||||
|
||||
coverage:
|
||||
stage: post-test
|
||||
services: []
|
||||
|
@ -263,7 +283,6 @@ coverage:
|
|||
- coverage/index.html
|
||||
- coverage/assets/
|
||||
|
||||
|
||||
# Notify slack in the end
|
||||
|
||||
notify:slack:
|
||||
|
|
44
.gitlab/issue_templates/Bug.md
Normal file
44
.gitlab/issue_templates/Bug.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
### Summary
|
||||
|
||||
(Summarize the bug encountered concisely)
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
(How one can reproduce the issue - this is very important)
|
||||
|
||||
### Expected behavior
|
||||
|
||||
(What you should see instead)
|
||||
|
||||
### Actual behavior
|
||||
|
||||
(What actually happens)
|
||||
|
||||
### Relevant logs and/or screenshots
|
||||
|
||||
(Paste any relevant logs - please use code blocks (```) to format console output,
|
||||
logs, and code as it's very hard to read otherwise.)
|
||||
|
||||
### Output of checks
|
||||
|
||||
#### Results of GitLab application Check
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
`sudo gitlab-rake gitlab:check SANITIZE=true`)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`)
|
||||
|
||||
(we will only investigate if the tests are passing)
|
||||
|
||||
#### Results of GitLab environment info
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
`sudo gitlab-rake gitlab:env:info`)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
|
||||
|
||||
### Possible fixes
|
||||
|
||||
(If you can, link to the line of code that might be responsible for the problem)
|
7
.gitlab/issue_templates/Feature Proposal.md
Normal file
7
.gitlab/issue_templates/Feature Proposal.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
### Description
|
||||
|
||||
(Include problem, use cases, benefits, and/or goals)
|
||||
|
||||
### Proposal
|
||||
|
||||
### Links / references
|
14
.gitlab/merge_request_templates/Documentation.md
Normal file
14
.gitlab/merge_request_templates/Documentation.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html.
|
||||
|
||||
## What does this MR do?
|
||||
|
||||
(briefly describe what this MR is about)
|
||||
|
||||
## Moving docs to a new location?
|
||||
|
||||
See the guidelines: http://docs.gitlab.com/ce/development/doc_styleguide.html#changing-document-location
|
||||
|
||||
- [ ] Make sure the old link is not removed and has its contents replaced with a link to the new location.
|
||||
- [ ] Make sure internal links pointing to the document in question are not broken.
|
||||
- [ ] Search and replace any links referring to old docs in GitLab Rails app, specifically under the `app/views/` directory.
|
||||
- [ ] If working on CE, submit an MR to EE with the changes as well.
|
103
.haml-lint.yml
Normal file
103
.haml-lint.yml
Normal file
|
@ -0,0 +1,103 @@
|
|||
# Whether to ignore frontmatter at the beginning of HAML documents for
|
||||
# frameworks such as Jekyll/Middleman
|
||||
skip_frontmatter: false
|
||||
exclude:
|
||||
- 'vendor/**/*'
|
||||
- 'spec/**/*'
|
||||
|
||||
linters:
|
||||
AltText:
|
||||
enabled: false
|
||||
|
||||
ClassAttributeWithStaticValue:
|
||||
enabled: false
|
||||
|
||||
ClassesBeforeIds:
|
||||
enabled: false
|
||||
|
||||
ConsecutiveComments:
|
||||
enabled: false
|
||||
|
||||
ConsecutiveSilentScripts:
|
||||
enabled: false
|
||||
max_consecutive: 2
|
||||
|
||||
EmptyObjectReference:
|
||||
enabled: true
|
||||
|
||||
EmptyScript:
|
||||
enabled: true
|
||||
|
||||
FinalNewline:
|
||||
enabled: false
|
||||
present: true
|
||||
|
||||
HtmlAttributes:
|
||||
enabled: false
|
||||
|
||||
ImplicitDiv:
|
||||
enabled: false
|
||||
|
||||
LeadingCommentSpace:
|
||||
enabled: false
|
||||
|
||||
LineLength:
|
||||
enabled: false
|
||||
max: 80
|
||||
|
||||
MultilinePipe:
|
||||
enabled: false
|
||||
|
||||
MultilineScript:
|
||||
enabled: true
|
||||
|
||||
ObjectReferenceAttributes:
|
||||
enabled: true
|
||||
|
||||
RuboCop:
|
||||
enabled: false
|
||||
# These cops are incredibly noisy when it comes to HAML templates, so we
|
||||
# ignore them.
|
||||
ignored_cops:
|
||||
- Lint/BlockAlignment
|
||||
- Lint/EndAlignment
|
||||
- Lint/Void
|
||||
- Metrics/LineLength
|
||||
- Style/AlignParameters
|
||||
- Style/BlockNesting
|
||||
- Style/ElseAlignment
|
||||
- Style/FileName
|
||||
- Style/FinalNewline
|
||||
- Style/FrozenStringLiteralComment
|
||||
- Style/IfUnlessModifier
|
||||
- Style/IndentationWidth
|
||||
- Style/Next
|
||||
- Style/TrailingBlankLines
|
||||
- Style/TrailingWhitespace
|
||||
- Style/WhileUntilModifier
|
||||
|
||||
RubyComments:
|
||||
enabled: false
|
||||
|
||||
SpaceBeforeScript:
|
||||
enabled: false
|
||||
|
||||
SpaceInsideHashAttributes:
|
||||
enabled: false
|
||||
style: space
|
||||
|
||||
Indentation:
|
||||
enabled: true
|
||||
character: space # or tab
|
||||
|
||||
TagName:
|
||||
enabled: true
|
||||
|
||||
TrailingWhitespace:
|
||||
enabled: false
|
||||
|
||||
UnnecessaryInterpolation:
|
||||
enabled: false
|
||||
|
||||
UnnecessaryStringOutput:
|
||||
enabled: false
|
12
.pkgr.yml
12
.pkgr.yml
|
@ -3,6 +3,8 @@ group: git
|
|||
services:
|
||||
- postgres
|
||||
before_precompile: ./bin/pkgr_before_precompile.sh
|
||||
env:
|
||||
- SKIP_STORAGE_VALIDATION=true
|
||||
targets:
|
||||
debian-7: &wheezy
|
||||
build_dependencies:
|
||||
|
@ -25,6 +27,16 @@ targets:
|
|||
- libicu52
|
||||
- libpcre3
|
||||
- git
|
||||
ubuntu-16.04:
|
||||
build_dependencies:
|
||||
- libkrb5-dev
|
||||
- libicu-dev
|
||||
- cmake
|
||||
- pkg-config
|
||||
dependencies:
|
||||
- libicu55
|
||||
- libpcre3
|
||||
- git
|
||||
centos-6:
|
||||
build_dependencies:
|
||||
- krb5-devel
|
||||
|
|
|
@ -5,8 +5,8 @@ require:
|
|||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.1
|
||||
# Cop names are not displayed in offense messages by default. Change behavior
|
||||
TargetRubyVersion: 2.3
|
||||
# Cop names are not d§splayed in offense messages by default. Change behavior
|
||||
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
||||
# option.
|
||||
DisplayCopNames: true
|
||||
|
@ -192,6 +192,9 @@ Style/FlipFlop:
|
|||
Style/For:
|
||||
Enabled: true
|
||||
|
||||
# Checks if there is a magic comment to enforce string literals
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
# Do not introduce global variables.
|
||||
Style/GlobalVars:
|
||||
Enabled: true
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --exclude-limit 0`
|
||||
# on 2016-07-13 12:36:08 -0600 using RuboCop version 0.41.2.
|
||||
# on 2016-09-14 15:44:53 -0400 using RuboCop version 0.42.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 154
|
||||
# Offense count: 158
|
||||
Lint/AmbiguousRegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 43
|
||||
# Offense count: 41
|
||||
# Configuration parameters: AllowSafeAssignment.
|
||||
Lint/AssignmentInCondition:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 14
|
||||
# Offense count: 16
|
||||
Lint/HandleExceptions:
|
||||
Enabled: false
|
||||
|
||||
|
@ -23,28 +23,28 @@ Lint/HandleExceptions:
|
|||
Lint/Loop:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 15
|
||||
# Offense count: 16
|
||||
Lint/ShadowingOuterLocalVariable:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Lint/StringConversionInInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 44
|
||||
# Offense count: 49
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
|
||||
Lint/UnusedBlockArgument:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 129
|
||||
# Offense count: 144
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
||||
Lint/UnusedMethodArgument:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
Performance/PushSplat:
|
||||
Enabled: false
|
||||
|
@ -59,51 +59,51 @@ Performance/RedundantBlockCall:
|
|||
Performance/RedundantMatch:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 27
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: MaxKeyValuePairs.
|
||||
Performance/RedundantMerge:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 60
|
||||
# Offense count: 61
|
||||
Rails/OutputSafety:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 128
|
||||
# Offense count: 129
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: strict, flexible
|
||||
Rails/TimeZone:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Offense count: 15
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/Validation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 217
|
||||
# Offense count: 273
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: with_first_parameter, with_fixed_indentation
|
||||
Style/AlignParameters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 30
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: always, conditionals
|
||||
Style/AndOr:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 47
|
||||
# Offense count: 50
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: percent_q, bare_percent
|
||||
Style/BarePercentLiterals:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 258
|
||||
# Offense count: 289
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: braces, no_braces, context_dependent
|
||||
|
@ -126,14 +126,14 @@ Style/ColonMethodCall:
|
|||
Style/CommentAnnotation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 33
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly.
|
||||
# SupportedStyles: assign_to_condition, assign_inside_condition
|
||||
Style/ConditionalAssignment:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 789
|
||||
# Offense count: 881
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: leading, trailing
|
||||
|
@ -144,11 +144,12 @@ Style/DotPosition:
|
|||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
Style/EachWithObject:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 30
|
||||
# Offense count: 25
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: empty, nil, both
|
||||
|
@ -160,7 +161,7 @@ Style/EmptyElse:
|
|||
Style/EmptyLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 123
|
||||
# Offense count: 135
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
||||
Style/ExtraSpacing:
|
||||
|
@ -172,16 +173,16 @@ Style/ExtraSpacing:
|
|||
Style/FormatString:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 48
|
||||
# Offense count: 51
|
||||
# Configuration parameters: MinBodyLength.
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 11
|
||||
# Offense count: 9
|
||||
Style/IfInsideElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 177
|
||||
# Offense count: 174
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: MaxLineLength.
|
||||
Style/IfUnlessModifier:
|
||||
|
@ -194,7 +195,7 @@ Style/IfUnlessModifier:
|
|||
Style/IndentArray:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 89
|
||||
# Offense count: 97
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
||||
|
@ -208,7 +209,7 @@ Style/IndentHash:
|
|||
Style/Lambda:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
Style/LineEndConcatenation:
|
||||
Enabled: false
|
||||
|
@ -218,17 +219,21 @@ Style/LineEndConcatenation:
|
|||
Style/MethodCallParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 62
|
||||
# Offense count: 8
|
||||
Style/MethodMissing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 85
|
||||
# Cop supports --auto-correct.
|
||||
Style/MutableConstant:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 10
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
Style/NestedParenthesizedCalls:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Offense count: 13
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
||||
# SupportedStyles: skip_modifier_ifs, always
|
||||
|
@ -242,12 +247,19 @@ Style/Next:
|
|||
Style/NumericLiteralPrefix:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 64
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: predicate, comparison
|
||||
Style/NumericPredicate:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 29
|
||||
# Cop supports --auto-correct.
|
||||
Style/ParallelAssignment:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 208
|
||||
# Offense count: 264
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: PreferredDelimiters.
|
||||
Style/PercentLiteralDelimiters:
|
||||
|
@ -265,7 +277,7 @@ Style/PercentQLiterals:
|
|||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 35
|
||||
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
||||
# NamePrefix: is_, has_, have_
|
||||
# NamePrefixBlacklist: is_, has_, have_
|
||||
|
@ -273,7 +285,7 @@ Style/PerlBackrefs:
|
|||
Style/PredicateName:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 28
|
||||
# Offense count: 27
|
||||
# Cop supports --auto-correct.
|
||||
Style/PreferredHashMethods:
|
||||
Enabled: false
|
||||
|
@ -283,14 +295,14 @@ Style/PreferredHashMethods:
|
|||
Style/Proc:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 20
|
||||
# Offense count: 22
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, exploded
|
||||
Style/RaiseArgs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
@ -300,29 +312,29 @@ Style/RedundantBegin:
|
|||
Style/RedundantException:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 23
|
||||
# Offense count: 24
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantFreeze:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 377
|
||||
# Offense count: 408
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantSelf:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 94
|
||||
# Offense count: 93
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
||||
# SupportedStyles: slashes, percent_r, mixed
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 17
|
||||
# Offense count: 18
|
||||
# Cop supports --auto-correct.
|
||||
Style/RescueModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 2
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
Style/SelfAssignment:
|
||||
Enabled: false
|
||||
|
@ -339,42 +351,42 @@ Style/SingleLineBlockParams:
|
|||
Style/SingleLineMethods:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 119
|
||||
# Offense count: 124
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: space, no_space
|
||||
Style/SpaceBeforeBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 11
|
||||
# Offense count: 10
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Style/SpaceBeforeFirstArg:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 130
|
||||
# Offense count: 141
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
|
||||
# SupportedStyles: space, no_space
|
||||
Style/SpaceInsideBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 98
|
||||
# Offense count: 96
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsideBrackets:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 60
|
||||
# Offense count: 62
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsideParens:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 7
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 36
|
||||
# Offense count: 40
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: SupportedStyles.
|
||||
# SupportedStyles: use_perl_names, use_english_names
|
||||
|
@ -388,21 +400,28 @@ Style/SpecialGlobalVars:
|
|||
Style/StringLiteralsInInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 32
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoredMethods.
|
||||
# IgnoredMethods: respond_to, define_method
|
||||
Style/SymbolProc:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 23
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses
|
||||
Style/TernaryParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
|
||||
# SupportedStyles: comma, consistent_comma, no_comma
|
||||
Style/TrailingCommaInArguments:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 113
|
||||
# Offense count: 102
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyleForMultiline, SupportedStyles.
|
||||
# SupportedStyles: comma, consistent_comma, no_comma
|
||||
|
@ -415,7 +434,7 @@ Style/TrailingCommaInLiteral:
|
|||
Style/TrailingUnderscoreVariable:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 90
|
||||
# Offense count: 76
|
||||
# Cop supports --auto-correct.
|
||||
Style/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
@ -427,12 +446,12 @@ Style/TrailingWhitespace:
|
|||
Style/TrivialAccessors:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnlessElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 13
|
||||
# Offense count: 14
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnneededInterpolation:
|
||||
Enabled: false
|
||||
|
|
|
@ -91,19 +91,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
|
|||
|
||||
## Implement design & UI elements
|
||||
|
||||
### Design reference
|
||||
|
||||
The GitLab design reference can be found in the [gitlab-design] project.
|
||||
The designs are made using Antetype (`.atype` files). You can use the
|
||||
[free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
|
||||
(the PNG is 1:1).
|
||||
|
||||
The current designs can be found in the [`gitlab8.atype` file].
|
||||
|
||||
### UI development kit
|
||||
|
||||
Implemented UI elements can also be found at https://gitlab.com/help/ui. Please
|
||||
note that this page isn't comprehensive at this time.
|
||||
Please see the [UI Guide for building GitLab].
|
||||
|
||||
## Issue tracker
|
||||
|
||||
|
@ -129,7 +117,7 @@ request that potentially fixes it.
|
|||
|
||||
### Feature proposals
|
||||
|
||||
To create a feature proposal for CE and CI, open an issue on the
|
||||
To create a feature proposal for CE, open an issue on the
|
||||
[issue tracker of CE][ce-tracker].
|
||||
|
||||
For feature proposals for EE, open an issue on the
|
||||
|
@ -144,16 +132,7 @@ code snippet right after your description in a new line: `~"feature proposal"`.
|
|||
Please keep feature proposals as small and simple as possible, complex ones
|
||||
might be edited to make them small and simple.
|
||||
|
||||
You are encouraged to use the template below for feature proposals.
|
||||
|
||||
```
|
||||
## Description
|
||||
Include problem, use cases, benefits, and/or goals
|
||||
|
||||
## Proposal
|
||||
|
||||
## Links / references
|
||||
```
|
||||
Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker.
|
||||
|
||||
For changes in the interface, it can be helpful to create a mockup first.
|
||||
If you want to create something yourself, consider opening an issue first to
|
||||
|
@ -166,55 +145,11 @@ submitting your own, there's a good chance somebody else had the same issue or
|
|||
feature proposal. Show your support with an award emoji and/or join the
|
||||
discussion.
|
||||
|
||||
Please submit bugs using the following template in the issue description area.
|
||||
Please submit bugs using the ['Bug' issue template](.gitlab/issue_templates/Bug.md) provided on the issue tracker.
|
||||
The text in the parenthesis is there to help you with what to include. Omit it
|
||||
when submitting the actual issue. You can copy-paste it and then edit as you
|
||||
see fit.
|
||||
|
||||
```
|
||||
## Summary
|
||||
|
||||
(Summarize your issue in one sentence - what goes wrong, what did you expect to happen)
|
||||
|
||||
## Steps to reproduce
|
||||
|
||||
(How one can reproduce the issue - this is very important)
|
||||
|
||||
## Expected behavior
|
||||
|
||||
(What you should see instead)
|
||||
|
||||
## Relevant logs and/or screenshots
|
||||
|
||||
(Paste any relevant logs - please use code blocks (```) to format console output,
|
||||
logs, and code as it's very hard to read otherwise.)
|
||||
|
||||
## Output of checks
|
||||
|
||||
### Results of GitLab Application Check
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
sudo gitlab-rake gitlab:check SANITIZE=true)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true)
|
||||
|
||||
(we will only investigate if the tests are passing)
|
||||
|
||||
### Results of GitLab Environment Info
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
sudo gitlab-rake gitlab:env:info)
|
||||
|
||||
(For installations from source run and paste the output of:
|
||||
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production)
|
||||
|
||||
## Possible fixes
|
||||
|
||||
(If you can, link to the line of code that might be responsible for the problem)
|
||||
|
||||
```
|
||||
|
||||
### Issue weight
|
||||
|
||||
Issue weight allows us to get an idea of the amount of work required to solve
|
||||
|
@ -340,6 +275,10 @@ request is as follows:
|
|||
migrations on a fresh database before the MR is reviewed. If the review leads
|
||||
to large changes in the MR, do this again once the review is complete.
|
||||
1. For more complex migrations, write tests.
|
||||
1. Merge requests **must** adhere to the [merge request performance
|
||||
guidelines](doc/development/merge_request_performance_guidelines.md).
|
||||
1. For tests that use Capybara or PhantomJS, see this [article on how
|
||||
to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
|
||||
|
||||
The **official merge window** is in the beginning of the month from the 1st to
|
||||
the 7th day of the month. This is the best time to submit an MR and get
|
||||
|
@ -387,7 +326,8 @@ description area. Copy-paste it to retain the markdown format.
|
|||
|
||||
1. The change is as small as possible
|
||||
1. Include proper tests and make all tests pass (unless it contains a test
|
||||
exposing a bug in existing code)
|
||||
exposing a bug in existing code). Every new class should have corresponding
|
||||
unit tests, even if the class is exercised at a higher level, such as a feature test.
|
||||
1. If you suspect a failing CI build is unrelated to your contribution, you may
|
||||
try and restart the failing CI job or ask a developer to fix the
|
||||
aforementioned failing test
|
||||
|
@ -539,7 +479,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
|
|||
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
|
||||
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
|
||||
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
|
||||
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
|
||||
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
|
||||
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
|
||||
[UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md
|
||||
[license-finder-doc]: doc/development/licensing.md
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.4.0
|
||||
3.6.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.7.11
|
||||
0.8.2
|
||||
|
|
14
Gemfile
14
Gemfile
|
@ -26,7 +26,7 @@ gem 'omniauth-auth0', '~> 1.4.1'
|
|||
gem 'omniauth-azure-oauth2', '~> 0.0.6'
|
||||
gem 'omniauth-bitbucket', '~> 0.0.2'
|
||||
gem 'omniauth-cas3', '~> 1.1.2'
|
||||
gem 'omniauth-facebook', '~> 3.0.0'
|
||||
gem 'omniauth-facebook', '~> 4.0.0'
|
||||
gem 'omniauth-github', '~> 1.1.1'
|
||||
gem 'omniauth-gitlab', '~> 1.0.0'
|
||||
gem 'omniauth-google-oauth2', '~> 0.4.1'
|
||||
|
@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
|
|||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
gem 'gitlab_git', '~> 10.4.7'
|
||||
gem 'gitlab_git', '~> 10.6.6'
|
||||
|
||||
# LDAP Auth
|
||||
# GitLab fork with several improvements to original library. For full list of changes
|
||||
|
@ -97,9 +97,6 @@ gem 'fog-rackspace', '~> 0.1.1'
|
|||
# for aws storage
|
||||
gem 'unf', '~> 0.1.4'
|
||||
|
||||
# Authorization
|
||||
gem 'six', '~> 0.2.0'
|
||||
|
||||
# Seed data
|
||||
gem 'seed-fu', '~> 2.3.5'
|
||||
|
||||
|
@ -209,6 +206,9 @@ gem 'mousetrap-rails', '~> 1.4.6'
|
|||
# Detect and convert string character encoding
|
||||
gem 'charlock_holmes', '~> 0.7.3'
|
||||
|
||||
# Faster JSON
|
||||
gem 'oj', '~> 2.17.4'
|
||||
|
||||
# Parse time & duration
|
||||
gem 'chronic', '~> 0.10.2'
|
||||
gem 'chronic_duration', '~> 0.10.6'
|
||||
|
@ -298,9 +298,10 @@ group :development, :test do
|
|||
gem 'spring-commands-spinach', '~> 1.1.0'
|
||||
gem 'spring-commands-teaspoon', '~> 0.0.2'
|
||||
|
||||
gem 'rubocop', '~> 0.41.2', require: false
|
||||
gem 'rubocop', '~> 0.42.0', require: false
|
||||
gem 'rubocop-rspec', '~> 1.5.0', require: false
|
||||
gem 'scss_lint', '~> 0.47.0', require: false
|
||||
gem 'haml_lint', '~> 0.18.2', require: false
|
||||
gem 'simplecov', '0.12.0', require: false
|
||||
gem 'flog', '~> 4.3.2', require: false
|
||||
gem 'flay', '~> 2.6.1', require: false
|
||||
|
@ -319,6 +320,7 @@ group :test do
|
|||
gem 'webmock', '~> 1.21.0'
|
||||
gem 'test_after_commit', '~> 0.4.2'
|
||||
gem 'sham_rack', '~> 1.3.6'
|
||||
gem 'timecop', '~> 0.8.0'
|
||||
end
|
||||
|
||||
group :production do
|
||||
|
|
42
Gemfile.lock
42
Gemfile.lock
|
@ -189,7 +189,7 @@ GEM
|
|||
erubis (2.7.0)
|
||||
escape_utils (1.1.1)
|
||||
eventmachine (1.0.8)
|
||||
excon (0.49.0)
|
||||
excon (0.52.0)
|
||||
execjs (2.6.0)
|
||||
expression_parser (0.9.0)
|
||||
factory_girl (4.5.0)
|
||||
|
@ -215,8 +215,8 @@ GEM
|
|||
flowdock (0.7.1)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
fog-aws (0.9.2)
|
||||
fog-core (~> 1.27)
|
||||
fog-aws (0.11.0)
|
||||
fog-core (~> 1.38)
|
||||
fog-json (~> 1.0)
|
||||
fog-xml (~> 0.1)
|
||||
ipaddress (~> 0.8)
|
||||
|
@ -225,7 +225,7 @@ GEM
|
|||
fog-core (~> 1.27)
|
||||
fog-json (~> 1.0)
|
||||
fog-xml (~> 0.1)
|
||||
fog-core (1.40.0)
|
||||
fog-core (1.42.0)
|
||||
builder
|
||||
excon (~> 0.49)
|
||||
formatador (~> 0.2)
|
||||
|
@ -279,7 +279,7 @@ GEM
|
|||
diff-lcs (~> 1.1)
|
||||
mime-types (>= 1.16, < 3)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab_git (10.4.7)
|
||||
gitlab_git (10.6.6)
|
||||
activesupport (~> 4.0)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
github-linguist (~> 4.7.0)
|
||||
|
@ -322,11 +322,18 @@ GEM
|
|||
grape-entity (0.4.8)
|
||||
activesupport
|
||||
multi_json (>= 1.3.2)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
haml_lint (0.18.2)
|
||||
haml (~> 4.0)
|
||||
rake (>= 10, < 12)
|
||||
rubocop (>= 0.36.0)
|
||||
sysexits (~> 1.1)
|
||||
hamlit (2.6.1)
|
||||
temple (~> 0.7.6)
|
||||
thor
|
||||
tilt
|
||||
hashie (3.4.3)
|
||||
hashie (3.4.4)
|
||||
health_check (2.1.0)
|
||||
rails (>= 4.0)
|
||||
hipchat (1.5.2)
|
||||
|
@ -394,7 +401,7 @@ GEM
|
|||
mime-types (>= 1.16, < 4)
|
||||
mail_room (0.8.0)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.99.2)
|
||||
mime-types (2.99.3)
|
||||
mimemagic (0.3.0)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.7.0)
|
||||
|
@ -420,6 +427,7 @@ GEM
|
|||
rack (>= 1.2, < 3)
|
||||
octokit (4.3.0)
|
||||
sawyer (~> 0.7.0, >= 0.5.3)
|
||||
oj (2.17.4)
|
||||
omniauth (1.3.1)
|
||||
hashie (>= 1.2, < 4)
|
||||
rack (>= 1.0, < 3)
|
||||
|
@ -437,7 +445,7 @@ GEM
|
|||
addressable (~> 2.3)
|
||||
nokogiri (~> 1.6.6)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-facebook (3.0.0)
|
||||
omniauth-facebook (4.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
omniauth-github (1.1.2)
|
||||
omniauth (~> 1.0)
|
||||
|
@ -584,7 +592,7 @@ GEM
|
|||
railties (>= 4.2.0, < 5.1)
|
||||
rinku (2.0.0)
|
||||
rotp (2.1.2)
|
||||
rouge (2.0.5)
|
||||
rouge (2.0.6)
|
||||
rqrcode (0.7.0)
|
||||
chunky_png
|
||||
rqrcode-rails3 (0.1.7)
|
||||
|
@ -612,7 +620,7 @@ GEM
|
|||
rspec-retry (0.4.5)
|
||||
rspec-core
|
||||
rspec-support (3.5.0)
|
||||
rubocop (0.41.2)
|
||||
rubocop (0.42.0)
|
||||
parser (>= 2.3.1.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
|
@ -683,7 +691,6 @@ GEM
|
|||
rack (~> 1.5)
|
||||
rack-protection (~> 1.4)
|
||||
tilt (>= 1.3, < 3)
|
||||
six (0.2.0)
|
||||
slack-notifier (1.2.1)
|
||||
slop (3.6.0)
|
||||
spinach (0.8.10)
|
||||
|
@ -724,6 +731,7 @@ GEM
|
|||
stringex (2.5.2)
|
||||
sys-filesystem (1.1.6)
|
||||
ffi
|
||||
sysexits (1.2.0)
|
||||
systemu (2.6.5)
|
||||
task_list (1.0.2)
|
||||
html-pipeline
|
||||
|
@ -755,7 +763,7 @@ GEM
|
|||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.2)
|
||||
unicode-display_width (1.1.0)
|
||||
unicode-display_width (1.1.1)
|
||||
unicorn (4.9.0)
|
||||
kgio (~> 2.6)
|
||||
rack
|
||||
|
@ -859,7 +867,7 @@ DEPENDENCIES
|
|||
github-linguist (~> 4.7.0)
|
||||
github-markup (~> 1.4)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab_git (~> 10.4.7)
|
||||
gitlab_git (~> 10.6.6)
|
||||
gitlab_meta (= 7.0)
|
||||
gitlab_omniauth-ldap (~> 1.2.1)
|
||||
gollum-lib (~> 4.2)
|
||||
|
@ -867,6 +875,7 @@ DEPENDENCIES
|
|||
gon (~> 6.1.0)
|
||||
grape (~> 0.15.0)
|
||||
grape-entity (~> 0.4.2)
|
||||
haml_lint (~> 0.18.2)
|
||||
hamlit (~> 2.6.1)
|
||||
health_check (~> 2.1.0)
|
||||
hipchat (~> 1.5.0)
|
||||
|
@ -896,12 +905,13 @@ DEPENDENCIES
|
|||
nokogiri (~> 1.6.7, >= 1.6.7.2)
|
||||
oauth2 (~> 1.2.0)
|
||||
octokit (~> 4.3.0)
|
||||
oj (~> 2.17.4)
|
||||
omniauth (~> 1.3.1)
|
||||
omniauth-auth0 (~> 1.4.1)
|
||||
omniauth-azure-oauth2 (~> 0.0.6)
|
||||
omniauth-bitbucket (~> 0.0.2)
|
||||
omniauth-cas3 (~> 1.1.2)
|
||||
omniauth-facebook (~> 3.0.0)
|
||||
omniauth-facebook (~> 4.0.0)
|
||||
omniauth-github (~> 1.1.1)
|
||||
omniauth-gitlab (~> 1.0.0)
|
||||
omniauth-google-oauth2 (~> 0.4.1)
|
||||
|
@ -936,7 +946,7 @@ DEPENDENCIES
|
|||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-rails (~> 3.5.0)
|
||||
rspec-retry (~> 0.4.5)
|
||||
rubocop (~> 0.41.2)
|
||||
rubocop (~> 0.42.0)
|
||||
rubocop-rspec (~> 1.5.0)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
ruby-prof (~> 0.15.9)
|
||||
|
@ -954,7 +964,6 @@ DEPENDENCIES
|
|||
sidekiq-cron (~> 0.4.0)
|
||||
simplecov (= 0.12.0)
|
||||
sinatra (~> 1.4.4)
|
||||
six (~> 0.2.0)
|
||||
slack-notifier (~> 1.2.0)
|
||||
spinach-rails (~> 0.2.1)
|
||||
spinach-rerun-reporter (~> 0.0.2)
|
||||
|
@ -971,6 +980,7 @@ DEPENDENCIES
|
|||
teaspoon-jasmine (~> 2.2.0)
|
||||
test_after_commit (~> 0.4.2)
|
||||
thin (~> 1.7.0)
|
||||
timecop (~> 0.8.0)
|
||||
turbolinks (~> 2.5.0)
|
||||
u2f (~> 0.2.1)
|
||||
uglifier (~> 2.7.2)
|
||||
|
|
|
@ -50,7 +50,7 @@ etc.).
|
|||
|
||||
The most important thing is making sure valid issues receive feedback from the
|
||||
development team. Therefore the priority is mentioning developers that can help
|
||||
on those issue. Please select someone with relevant experience from
|
||||
on those issues. Please select someone with relevant experience from
|
||||
[GitLab core team][core-team]. If there is nobody mentioned with that expertise
|
||||
look in the commit history for the affected files to find someone. Avoid
|
||||
mentioning the lead developer, this is the person that is least likely to give a
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# GitLab
|
||||
|
||||
[![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
|
||||
[![coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
|
||||
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
|
||||
## Canonical source
|
||||
|
@ -69,7 +70,7 @@ Instructions on how to start GitLab and how to run the tests can be found in the
|
|||
GitLab is a Ruby on Rails application that runs on the following software:
|
||||
|
||||
- Ubuntu/Debian/CentOS/RHEL
|
||||
- Ruby (MRI) 2.1
|
||||
- Ruby (MRI) 2.3
|
||||
- Git 2.7.4+
|
||||
- Redis 2.8+
|
||||
- MySQL or PostgreSQL
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.11.3
|
||||
8.12.1
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 729 B |
1
app/assets/images/icon_anchor.svg
Normal file
1
app/assets/images/icon_anchor.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="#333" fill-rule="evenodd" d="M9.683 6.676l-.047-.048C8.27 5.26 6.07 5.243 4.726 6.588l-2.29 2.29c-1.344 1.344-1.328 3.544.04 4.91 1.366 1.368 3.564 1.385 4.908.04l1.753-1.752c-.695.074-1.457-.078-2.176-.444L5.934 12.66c-.634.634-1.67.625-2.312-.017-.642-.643-.65-1.677-.017-2.312L6.035 7.9c.634-.634 1.67-.625 2.312.017.024.024.048.05.07.075l.003-.002c.36.36.943.366 1.3.01.355-.356.35-.938-.01-1.3l-.027-.024zM6.58 9.586l.048.05c1.367 1.366 3.565 1.384 4.91.04l2.29-2.292c1.344-1.343 1.328-3.542-.04-4.91-1.366-1.366-3.564-1.384-4.908-.04L7.127 4.187c.695-.074 1.457.078 2.176.444l1.028-1.027c.635-.634 1.67-.624 2.313.017.643.644.652 1.678.018 2.312l-2.43 2.432c-.635.634-1.67.624-2.313-.018-.024-.024-.048-.05-.07-.075l-.003.004c-.36-.362-.943-.367-1.3-.01-.355.355-.35.937.01 1.3.01.007.018.015.027.023z"/></svg>
|
After Width: | Height: | Size: 911 B |
|
@ -3,6 +3,7 @@
|
|||
LabelManager.prototype.errorMessage = 'Unable to update label prioritization at this time';
|
||||
|
||||
function LabelManager(opts) {
|
||||
// Defaults
|
||||
var ref, ref1, ref2;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
|
@ -28,6 +29,7 @@
|
|||
$btn = $(e.currentTarget);
|
||||
$label = $("#" + ($btn.data('domId')));
|
||||
action = $btn.parents('.js-prioritized-labels').length ? 'remove' : 'add';
|
||||
// Make sure tooltip will hide
|
||||
$tooltip = $("#" + ($btn.find('.has-tooltip:visible').attr('aria-describedby')));
|
||||
$tooltip.tooltip('destroy');
|
||||
return _this.toggleLabelPriority($label, action);
|
||||
|
@ -42,6 +44,7 @@
|
|||
url = $label.find('.js-toggle-priority').data('url');
|
||||
$target = this.prioritizedLabels;
|
||||
$from = this.otherLabels;
|
||||
// Optimistic update
|
||||
if (action === 'remove') {
|
||||
$target = this.otherLabels;
|
||||
$from = this.prioritizedLabels;
|
||||
|
@ -53,6 +56,7 @@
|
|||
$target.find('.empty-message').addClass('hidden');
|
||||
}
|
||||
$label.detach().appendTo($target);
|
||||
// Return if we are not persisting state
|
||||
if (!persistState) {
|
||||
return;
|
||||
}
|
||||
|
@ -61,6 +65,7 @@
|
|||
url: url,
|
||||
type: 'DELETE'
|
||||
});
|
||||
// Restore empty message
|
||||
if (!$from.find('li').length) {
|
||||
$from.find('.empty-message').removeClass('hidden');
|
||||
}
|
||||
|
|
38
app/assets/javascripts/abuse_reports.js.es6
Normal file
38
app/assets/javascripts/abuse_reports.js.es6
Normal file
|
@ -0,0 +1,38 @@
|
|||
((global) => {
|
||||
const MAX_MESSAGE_LENGTH = 500;
|
||||
const MESSAGE_CELL_SELECTOR = '.abuse-reports .message';
|
||||
|
||||
class AbuseReports {
|
||||
constructor() {
|
||||
$(MESSAGE_CELL_SELECTOR).each(this.truncateLongMessage);
|
||||
$(document)
|
||||
.off('click', MESSAGE_CELL_SELECTOR)
|
||||
.on('click', MESSAGE_CELL_SELECTOR, this.toggleMessageTruncation);
|
||||
}
|
||||
|
||||
truncateLongMessage() {
|
||||
const $messageCellElement = $(this);
|
||||
const reportMessage = $messageCellElement.text();
|
||||
if (reportMessage.length > MAX_MESSAGE_LENGTH) {
|
||||
$messageCellElement.data('original-message', reportMessage);
|
||||
$messageCellElement.data('message-truncated', 'true');
|
||||
$messageCellElement.text(global.text.truncate(reportMessage, MAX_MESSAGE_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
toggleMessageTruncation() {
|
||||
const $messageCellElement = $(this);
|
||||
const originalMessage = $messageCellElement.data('original-message');
|
||||
if (!originalMessage) return;
|
||||
if ($messageCellElement.data('message-truncated') === 'true') {
|
||||
$messageCellElement.data('message-truncated', 'false');
|
||||
$messageCellElement.text(originalMessage);
|
||||
} else {
|
||||
$messageCellElement.data('message-truncated', 'true');
|
||||
$messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
global.AbuseReports = AbuseReports;
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -12,7 +12,7 @@
|
|||
}
|
||||
|
||||
Activities.prototype.updateTooltips = function() {
|
||||
return gl.utils.localTimeAgo($('.js-timeago', '#activity'));
|
||||
return gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
|
||||
};
|
||||
|
||||
Activities.prototype.reloadActivities = function() {
|
||||
|
@ -26,7 +26,7 @@
|
|||
event_filters = $.cookie("event_filter");
|
||||
filter = sender.attr("id").split("_")[0];
|
||||
$.cookie("event_filter", (event_filters !== filter ? filter : ""), {
|
||||
path: '/'
|
||||
path: gon.relative_url_root || '/'
|
||||
});
|
||||
if (event_filters !== filter) {
|
||||
return sender.closest('li').toggleClass("active");
|
||||
|
|
|
@ -16,20 +16,18 @@
|
|||
.replace(':id', group_id);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
private_token: gon.api_token
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function(group) {
|
||||
return callback(group);
|
||||
});
|
||||
},
|
||||
// Return groups list. Filtered by query
|
||||
// Only active groups retrieved
|
||||
groups: function(query, skip_ldap, callback) {
|
||||
var url = Api.buildUrl(Api.groupsPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
private_token: gon.api_token,
|
||||
search: query,
|
||||
per_page: 20
|
||||
},
|
||||
|
@ -38,12 +36,12 @@
|
|||
return callback(groups);
|
||||
});
|
||||
},
|
||||
// Return namespaces list. Filtered by query
|
||||
namespaces: function(query, callback) {
|
||||
var url = Api.buildUrl(Api.namespacesPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
private_token: gon.api_token,
|
||||
search: query,
|
||||
per_page: 20
|
||||
},
|
||||
|
@ -52,12 +50,12 @@
|
|||
return callback(namespaces);
|
||||
});
|
||||
},
|
||||
// Return projects list. Filtered by query
|
||||
projects: function(query, order, callback) {
|
||||
var url = Api.buildUrl(Api.projectsPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
private_token: gon.api_token,
|
||||
search: query,
|
||||
order_by: order,
|
||||
per_page: 20
|
||||
|
@ -70,7 +68,6 @@
|
|||
newLabel: function(project_id, data, callback) {
|
||||
var url = Api.buildUrl(Api.labelsPath)
|
||||
.replace(':id', project_id);
|
||||
data.private_token = gon.api_token;
|
||||
return $.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
|
@ -82,13 +79,13 @@
|
|||
return callback(message.responseJSON);
|
||||
});
|
||||
},
|
||||
// Return group projects list. Filtered by query
|
||||
groupProjects: function(group_id, query, callback) {
|
||||
var url = Api.buildUrl(Api.groupProjectsPath)
|
||||
.replace(':id', group_id);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
private_token: gon.api_token,
|
||||
search: query,
|
||||
per_page: 20
|
||||
},
|
||||
|
@ -97,6 +94,7 @@
|
|||
return callback(projects);
|
||||
});
|
||||
},
|
||||
// Return text for a specific license
|
||||
licenseText: function(key, data, callback) {
|
||||
var url = Api.buildUrl(Api.licensePath)
|
||||
.replace(':key', key);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
/*= require jquery2 */
|
||||
/*= require jquery-ui/autocomplete */
|
||||
/*= require jquery-ui/datepicker */
|
||||
|
@ -76,6 +82,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Disable button if text field is empty
|
||||
window.disableButtonIfEmptyField = function(field_selector, button_selector) {
|
||||
var closest_submit, field;
|
||||
field = $(field_selector);
|
||||
|
@ -92,6 +99,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
// Disable button if any input field with given selector is empty
|
||||
window.disableButtonIfAnyEmptyField = function(form, form_selector, button_selector) {
|
||||
var closest_submit, updateButtons;
|
||||
closest_submit = form.find(button_selector);
|
||||
|
@ -128,6 +136,8 @@
|
|||
window.addEventListener("hashchange", shiftWindow);
|
||||
|
||||
window.onload = function() {
|
||||
// Scroll the window to avoid the topnav bar
|
||||
// https://github.com/twitter/bootstrap/issues/1768
|
||||
if (location.hash) {
|
||||
return setTimeout(shiftWindow, 100);
|
||||
}
|
||||
|
@ -149,9 +159,13 @@
|
|||
return $(this).select().one('mouseup', function(e) {
|
||||
return e.preventDefault();
|
||||
});
|
||||
// Click a .js-select-on-focus field, select the contents
|
||||
// Prevent a mouseup event from deselecting the input
|
||||
});
|
||||
$('.remove-row').bind('ajax:success', function() {
|
||||
return $(this).closest('li').fadeOut();
|
||||
$(this).tooltip('destroy')
|
||||
.closest('li')
|
||||
.fadeOut();
|
||||
});
|
||||
$('.js-remove-tr').bind('ajax:before', function() {
|
||||
return $(this).hide();
|
||||
|
@ -161,6 +175,7 @@
|
|||
});
|
||||
$('select.select2').select2({
|
||||
width: 'resolve',
|
||||
// Initialize select2 selects
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$('.js-select2').bind('select2-close', function() {
|
||||
|
@ -168,25 +183,28 @@
|
|||
$('.select2-container-active').removeClass('select2-container-active');
|
||||
return $(':focus').blur();
|
||||
}), 1);
|
||||
// Close select2 on escape
|
||||
});
|
||||
// Initialize tooltips
|
||||
$body.tooltip({
|
||||
selector: '.has-tooltip, [data-toggle="tooltip"]',
|
||||
placement: function(_, el) {
|
||||
var $el;
|
||||
$el = $(el);
|
||||
return $el.data('placement') || 'bottom';
|
||||
return $(el).data('placement') || 'bottom';
|
||||
}
|
||||
});
|
||||
$('.trigger-submit').on('change', function() {
|
||||
return $(this).parents('form').submit();
|
||||
// Form submitter
|
||||
});
|
||||
gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
|
||||
// Flash
|
||||
if ((flash = $(".flash-container")).length > 0) {
|
||||
flash.click(function() {
|
||||
return $(this).fadeOut();
|
||||
});
|
||||
flash.show();
|
||||
}
|
||||
// Disable form buttons while a form is submitting
|
||||
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function(e) {
|
||||
var buttons;
|
||||
buttons = $('[type="submit"]', this);
|
||||
|
@ -207,6 +225,7 @@
|
|||
}
|
||||
});
|
||||
$('.account-box').hover(function() {
|
||||
// Show/Hide the profile menu when hovering the account box
|
||||
return $(this).toggleClass('hover');
|
||||
});
|
||||
$document.on('click', '.diff-content .js-show-suppressed-diff', function() {
|
||||
|
@ -214,6 +233,7 @@
|
|||
$container = $(this).parent();
|
||||
$container.next('table').show();
|
||||
return $container.remove();
|
||||
// Commit show suppressed diff
|
||||
});
|
||||
$('.navbar-toggle').on('click', function() {
|
||||
$('.header-content .title').toggle();
|
||||
|
@ -221,6 +241,7 @@
|
|||
$('.header-content .navbar-collapse').toggle();
|
||||
return $('.navbar-toggle').toggleClass('active');
|
||||
});
|
||||
// Show/hide comments on diff
|
||||
$body.on("click", ".js-toggle-diff-comments", function(e) {
|
||||
var $this = $(this);
|
||||
$this.toggleClass('active');
|
||||
|
@ -230,6 +251,7 @@
|
|||
} else {
|
||||
notesHolders.hide();
|
||||
}
|
||||
$this.trigger('blur');
|
||||
return e.preventDefault();
|
||||
});
|
||||
$document.off("click", '.js-confirm-danger');
|
||||
|
@ -284,42 +306,9 @@
|
|||
gl.awardsHandler = new AwardsHandler();
|
||||
checkInitialSidebarSize();
|
||||
new Aside();
|
||||
if ($window.width() < 1024 && $.cookie('pin_nav') === 'true') {
|
||||
$.cookie('pin_nav', 'false', {
|
||||
path: '/',
|
||||
expires: 365 * 10
|
||||
});
|
||||
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
|
||||
$('.navbar-fixed-top').removeClass('header-pinned-nav');
|
||||
}
|
||||
$document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
|
||||
var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
|
||||
e.preventDefault();
|
||||
$pinBtn = $(e.currentTarget);
|
||||
$page = $('.page-with-sidebar');
|
||||
$topNav = $('.navbar-fixed-top');
|
||||
$tooltip = $("#" + ($pinBtn.attr('aria-describedby')));
|
||||
doPinNav = !$page.is('.page-sidebar-pinned');
|
||||
tooltipText = 'Pin navigation';
|
||||
$(this).toggleClass('is-active');
|
||||
if (doPinNav) {
|
||||
$page.addClass('page-sidebar-pinned');
|
||||
$topNav.addClass('header-pinned-nav');
|
||||
} else {
|
||||
$tooltip.remove();
|
||||
$page.removeClass('page-sidebar-pinned').toggleClass('page-sidebar-collapsed page-sidebar-expanded');
|
||||
$topNav.removeClass('header-pinned-nav').toggleClass('header-collapsed header-expanded');
|
||||
}
|
||||
$.cookie('pin_nav', doPinNav, {
|
||||
path: '/',
|
||||
expires: 365 * 10
|
||||
});
|
||||
if ($.cookie('pin_nav') === 'true' || doPinNav) {
|
||||
tooltipText = 'Unpin navigation';
|
||||
}
|
||||
$tooltip.find('.tooltip-inner').text(tooltipText);
|
||||
return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
|
||||
});
|
||||
|
||||
// bind sidebar events
|
||||
new gl.Sidebar();
|
||||
|
||||
// Custom time ago
|
||||
gl.utils.shortTimeAgo($('.js-short-timeago'));
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
}
|
||||
|
||||
Autosave.prototype.restore = function() {
|
||||
var e, error, text;
|
||||
var e, text;
|
||||
if (window.localStorage == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@
|
|||
if ((text != null ? text.length : void 0) > 0) {
|
||||
try {
|
||||
return window.localStorage.setItem(this.key, text);
|
||||
} catch (undefined) {}
|
||||
} catch (error) {}
|
||||
} else {
|
||||
return this.reset();
|
||||
}
|
||||
|
@ -53,7 +53,7 @@
|
|||
}
|
||||
try {
|
||||
return window.localStorage.removeItem(this.key);
|
||||
} catch (undefined) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
return Autosave;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(function() {
|
||||
this.AwardsHandler = (function() {
|
||||
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; //For separating lists produced by ruby's Array#toSentence
|
||||
function AwardsHandler() {
|
||||
this.aliases = gl.emojiAliases();
|
||||
$(document).off('click', '.js-add-award').on('click', '.js-add-award', (function(_this) {
|
||||
|
@ -85,6 +86,8 @@
|
|||
AwardsHandler.prototype.positionMenu = function($menu, $addBtn) {
|
||||
var css, position;
|
||||
position = $addBtn.data('position');
|
||||
// The menu could potentially be off-screen or in a hidden overflow element
|
||||
// So we position the element absolute in the body
|
||||
css = {
|
||||
top: ($addBtn.offset().top + $addBtn.outerHeight()) + "px"
|
||||
};
|
||||
|
@ -130,7 +133,7 @@
|
|||
counter = $emojiButton.find('.js-counter');
|
||||
counter.text(parseInt(counter.text()) + 1);
|
||||
$emojiButton.addClass('active');
|
||||
this.addMeToUserList(votesBlock, emoji);
|
||||
this.addYouToUserList(votesBlock, emoji);
|
||||
return this.animateEmoji($emojiButton);
|
||||
}
|
||||
} else {
|
||||
|
@ -176,11 +179,11 @@
|
|||
counterNumber = parseInt(counter.text(), 10);
|
||||
if (counterNumber > 1) {
|
||||
counter.text(counterNumber - 1);
|
||||
this.removeMeFromUserList($emojiButton, emoji);
|
||||
this.removeYouFromUserList($emojiButton, emoji);
|
||||
} else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
|
||||
$emojiButton.tooltip('destroy');
|
||||
counter.text('0');
|
||||
this.removeMeFromUserList($emojiButton, emoji);
|
||||
this.removeYouFromUserList($emojiButton, emoji);
|
||||
if ($emojiButton.parents('.note').length) {
|
||||
this.removeEmoji($emojiButton);
|
||||
}
|
||||
|
@ -204,43 +207,48 @@
|
|||
return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.removeMeFromUserList = function($emojiButton, emoji) {
|
||||
AwardsHandler.prototype.toSentence = function(list) {
|
||||
if(list.length <= 2){
|
||||
return list.join(' and ');
|
||||
}
|
||||
else{
|
||||
return list.slice(0, -1).join(', ') + ', and ' + list[list.length - 1];
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.removeYouFromUserList = function($emojiButton, emoji) {
|
||||
var authors, awardBlock, newAuthors, originalTitle;
|
||||
awardBlock = $emojiButton;
|
||||
originalTitle = this.getAwardTooltip(awardBlock);
|
||||
authors = originalTitle.split(', ');
|
||||
authors.splice(authors.indexOf('me'), 1);
|
||||
newAuthors = authors.join(', ');
|
||||
awardBlock.closest('.js-emoji-btn').removeData('original-title').attr('data-original-title', newAuthors);
|
||||
return this.resetTooltip(awardBlock);
|
||||
authors = originalTitle.split(FROM_SENTENCE_REGEX);
|
||||
authors.splice(authors.indexOf('You'), 1);
|
||||
return awardBlock
|
||||
.closest('.js-emoji-btn')
|
||||
.removeData('title')
|
||||
.removeAttr('data-title')
|
||||
.removeAttr('data-original-title')
|
||||
.attr('title', this.toSentence(authors))
|
||||
.tooltip('fixTitle');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.addMeToUserList = function(votesBlock, emoji) {
|
||||
AwardsHandler.prototype.addYouToUserList = function(votesBlock, emoji) {
|
||||
var awardBlock, origTitle, users;
|
||||
awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
|
||||
origTitle = this.getAwardTooltip(awardBlock);
|
||||
users = [];
|
||||
if (origTitle) {
|
||||
users = origTitle.trim().split(', ');
|
||||
users = origTitle.trim().split(FROM_SENTENCE_REGEX);
|
||||
}
|
||||
users.push('me');
|
||||
awardBlock.attr('title', users.join(', '));
|
||||
return this.resetTooltip(awardBlock);
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.resetTooltip = function(award) {
|
||||
var cb;
|
||||
award.tooltip('destroy');
|
||||
cb = function() {
|
||||
return award.tooltip();
|
||||
};
|
||||
return setTimeout(cb, 200);
|
||||
users.unshift('You');
|
||||
return awardBlock
|
||||
.attr('title', this.toSentence(users))
|
||||
.tooltip('fixTitle');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.createEmoji_ = function(votesBlock, emoji) {
|
||||
var $emojiButton, buttonHtml, emojiCssClass;
|
||||
emojiCssClass = this.resolveNameToCssClass(emoji);
|
||||
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'> <div class='icon emoji-icon " + emojiCssClass + "' data-emoji='" + emoji + "'></div> <span class='award-control-text js-counter'>1</span> </button>";
|
||||
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='You' data-placement='bottom'> <div class='icon emoji-icon " + emojiCssClass + "' data-emoji='" + emoji + "'></div> <span class='award-control-text js-counter'>1</span> </button>";
|
||||
$emojiButton = $(buttonHtml);
|
||||
$emojiButton.insertBefore(votesBlock.find('.js-award-holder')).find('.emoji-icon').data('emoji', emoji);
|
||||
this.animateEmoji($emojiButton);
|
||||
|
@ -249,12 +257,12 @@
|
|||
};
|
||||
|
||||
AwardsHandler.prototype.animateEmoji = function($emoji) {
|
||||
var className;
|
||||
className = 'pulse animated';
|
||||
var className = 'pulse animated once short';
|
||||
$emoji.addClass(className);
|
||||
return setTimeout((function() {
|
||||
return $emoji.removeClass(className);
|
||||
}), 321);
|
||||
|
||||
$emoji.on('webkitAnimationEnd animationEnd', function() {
|
||||
$(this).removeClass(className);
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.createEmoji = function(votesBlock, emoji) {
|
||||
|
@ -278,6 +286,7 @@
|
|||
if (emojiIcon.length > 0) {
|
||||
unicodeName = emojiIcon.data('unicode-name');
|
||||
} else {
|
||||
// Find by alias
|
||||
unicodeName = $(".emoji-menu-content [data-aliases*=':" + emoji + ":']").data('unicode-name');
|
||||
}
|
||||
return "emoji-" + unicodeName;
|
||||
|
@ -314,6 +323,7 @@
|
|||
frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
|
||||
frequentlyUsedEmojis.push(emoji);
|
||||
return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), {
|
||||
path: gon.relative_url_root || '/',
|
||||
expires: 365
|
||||
});
|
||||
};
|
||||
|
@ -343,8 +353,10 @@
|
|||
return function(ev) {
|
||||
var found_emojis, h5, term, ul;
|
||||
term = $(ev.target).val();
|
||||
// Clean previous search results
|
||||
$('ul.emoji-menu-search, h5.emoji-search').remove();
|
||||
if (term) {
|
||||
// Generate a search result block
|
||||
h5 = $('<h5>').text('Search results');
|
||||
found_emojis = _this.searchEmojis(term).show();
|
||||
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
/*= require jquery.ba-resize */
|
||||
|
||||
|
||||
/*= require autosize */
|
||||
|
||||
(function() {
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
container = $(this).closest(".js-details-container");
|
||||
return container.toggleClass("open");
|
||||
});
|
||||
// Show details content. Hides link after click.
|
||||
//
|
||||
// %div
|
||||
// %a.js-details-expand
|
||||
// %div.js-details-content
|
||||
//
|
||||
return $("body").on("click", ".js-details-expand", function(e) {
|
||||
$(this).next('.js-details-content').removeClass("hide");
|
||||
$(this).hide();
|
||||
|
|
|
@ -1,6 +1,20 @@
|
|||
|
||||
// Quick Submit behavior
|
||||
//
|
||||
// When a child field of a form with a `js-quick-submit` class receives a
|
||||
// "Meta+Enter" (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, the form
|
||||
// is submitted.
|
||||
//
|
||||
/*= require extensions/jquery */
|
||||
|
||||
//
|
||||
// ### Example Markup
|
||||
//
|
||||
// <form action="/foo" class="js-quick-submit">
|
||||
// <input type="text" />
|
||||
// <textarea></textarea>
|
||||
// <input type="submit" value="Submit" />
|
||||
// </form>
|
||||
//
|
||||
(function() {
|
||||
var isMac, keyCodeIs;
|
||||
|
||||
|
@ -17,6 +31,7 @@
|
|||
|
||||
$(document).on('keydown.quick_submit', '.js-quick-submit', function(e) {
|
||||
var $form, $submit_button;
|
||||
// Enter
|
||||
if (!keyCodeIs(e, 13)) {
|
||||
return;
|
||||
}
|
||||
|
@ -33,8 +48,11 @@
|
|||
return $form.submit();
|
||||
});
|
||||
|
||||
// If the user tabs to a submit button on a `js-quick-submit` form, display a
|
||||
// tooltip to let them know they could've used the hotkey
|
||||
$(document).on('keyup.quick_submit', '.js-quick-submit input[type=submit], .js-quick-submit button[type=submit]', function(e) {
|
||||
var $this, title;
|
||||
// Tab
|
||||
if (!keyCodeIs(e, 9)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
|
||||
// Requires Input behavior
|
||||
//
|
||||
// When called on a form with input fields with the `required` attribute, the
|
||||
// form's submit button will be disabled until all required fields have values.
|
||||
//
|
||||
/*= require extensions/jquery */
|
||||
|
||||
//
|
||||
// ### Example Markup
|
||||
//
|
||||
// <form class="js-requires-input">
|
||||
// <input type="text" required="required">
|
||||
// <input type="submit" value="Submit">
|
||||
// </form>
|
||||
//
|
||||
(function() {
|
||||
$.fn.requiresInput = function() {
|
||||
var $button, $form, fieldSelector, requireInput, required;
|
||||
|
@ -11,14 +23,17 @@
|
|||
requireInput = function() {
|
||||
var values;
|
||||
values = _.map($(fieldSelector, $form), function(field) {
|
||||
// Collect the input values of *all* required fields
|
||||
return field.value;
|
||||
});
|
||||
// Disable the button if any required fields are empty
|
||||
if (values.length && _.any(values, _.isEmpty)) {
|
||||
return $button.disable();
|
||||
} else {
|
||||
return $button.enable();
|
||||
}
|
||||
};
|
||||
// Set initial button state
|
||||
requireInput();
|
||||
return $form.on('change input', fieldSelector, requireInput);
|
||||
};
|
||||
|
@ -27,6 +42,8 @@
|
|||
var $form, hideOrShowHelpBlock;
|
||||
$form = $('form.js-requires-input');
|
||||
$form.requiresInput();
|
||||
// Hide or Show the help block when creating a new project
|
||||
// based on the option selected
|
||||
hideOrShowHelpBlock = function(form) {
|
||||
var selected;
|
||||
selected = $('.js-select-namespace option:selected');
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
(function(w) {
|
||||
$(function() {
|
||||
$('.js-toggle-button').on('click', function(e) {
|
||||
// Toggle button. Show/hide content inside parent container.
|
||||
// Button does not change visibility. If button has icon - it changes chevron style.
|
||||
//
|
||||
// %div.js-toggle-container
|
||||
// %a.js-toggle-button
|
||||
// %div.js-toggle-content
|
||||
//
|
||||
$('body').on('click', '.js-toggle-button', function(e) {
|
||||
e.preventDefault();
|
||||
$(this)
|
||||
.find('.fa')
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
autoDiscover: false,
|
||||
autoProcessQueue: false,
|
||||
url: form.attr('action'),
|
||||
// Rails uses a hidden input field for PUT
|
||||
// http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
|
||||
method: method,
|
||||
clickable: true,
|
||||
uploadMultiple: false,
|
||||
|
@ -36,6 +38,7 @@
|
|||
formData.append('commit_message', form.find('.js-commit-message').val());
|
||||
});
|
||||
},
|
||||
// Override behavior of adding error underneath preview
|
||||
error: function(file, errorMessage) {
|
||||
var stripped;
|
||||
stripped = $("<div/>").html(errorMessage).text();
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
this.buildDropdown();
|
||||
this.bindEvents();
|
||||
this.onFilenameUpdate();
|
||||
|
||||
this.autosizeUpdateEvent = document.createEvent('Event');
|
||||
this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
|
||||
}
|
||||
|
||||
TemplateSelector.prototype.buildDropdown = function() {
|
||||
|
@ -66,9 +69,16 @@
|
|||
// be added by all subclasses.
|
||||
};
|
||||
|
||||
// To be implemented on the extending class
|
||||
// e.g.
|
||||
// Api.gitignoreText item.name, @requestFileSuccess.bind(@)
|
||||
TemplateSelector.prototype.requestFileSuccess = function(file, skipFocus) {
|
||||
this.editor.setValue(file.content, 1);
|
||||
if (!skipFocus) this.editor.focus();
|
||||
|
||||
if (this.editor instanceof jQuery) {
|
||||
this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
|
||||
}
|
||||
};
|
||||
|
||||
TemplateSelector.prototype.startLoadingSpinner = function() {
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
return function() {
|
||||
return $("#file-content").val(_this.editor.getValue());
|
||||
};
|
||||
// Before a form submission, move the content from the Ace editor into the
|
||||
// submitted textarea
|
||||
})(this));
|
||||
this.initModePanesAndLinks();
|
||||
new BlobLicenseSelectors({
|
||||
|
|
|
@ -54,4 +54,11 @@ $(() => {
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
gl.IssueBoardsSearch = new Vue({
|
||||
el: '#js-boards-seach',
|
||||
data: {
|
||||
filters: Store.state.filters
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,15 +21,10 @@
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
query: '',
|
||||
filters: Store.state.filters
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
query () {
|
||||
this.list.filters = this.getFilterData();
|
||||
this.list.getIssues(true);
|
||||
},
|
||||
filters: {
|
||||
handler () {
|
||||
this.list.page = 1;
|
||||
|
@ -38,16 +33,6 @@
|
|||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFilterData () {
|
||||
const filters = this.filters;
|
||||
let queryData = { search: this.query };
|
||||
|
||||
Object.keys(filters).forEach((key) => { queryData[key] = filters[key]; });
|
||||
|
||||
return queryData;
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
const options = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
disabled: this.disabled,
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
data () {
|
||||
return {
|
||||
scrollOffset: 250,
|
||||
filters: Store.state.filters
|
||||
filters: Store.state.filters,
|
||||
showCount: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -30,6 +31,20 @@
|
|||
this.$els.list.scrollTop = 0;
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
issues () {
|
||||
this.$nextTick(() => {
|
||||
if (this.scrollHeight() <= this.listHeight() && this.list.issuesSize > this.list.issues.length) {
|
||||
this.list.page++;
|
||||
this.list.getIssues(false);
|
||||
}
|
||||
|
||||
if (this.scrollHeight() > this.listHeight()) {
|
||||
this.showCount = true;
|
||||
} else {
|
||||
this.showCount = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -58,6 +73,7 @@
|
|||
group: 'issues',
|
||||
sort: false,
|
||||
disabled: this.disabled,
|
||||
filter: '.board-list-count',
|
||||
onStart: (e) => {
|
||||
const card = this.$refs.issue[e.oldIndex];
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ class List {
|
|||
this.loading = true;
|
||||
this.loadingMore = false;
|
||||
this.issues = [];
|
||||
this.issuesSize = 0;
|
||||
|
||||
if (obj.label) {
|
||||
this.label = new ListLabel(obj.label);
|
||||
|
@ -51,17 +52,13 @@ class List {
|
|||
}
|
||||
|
||||
nextPage () {
|
||||
if (Math.floor(this.issues.length / 20) === this.page) {
|
||||
if (this.issuesSize > this.issues.length) {
|
||||
this.page++;
|
||||
|
||||
return this.getIssues(false);
|
||||
}
|
||||
}
|
||||
|
||||
canSearch () {
|
||||
return this.type === 'backlog';
|
||||
}
|
||||
|
||||
getIssues (emptyIssues = true) {
|
||||
const filters = this.filters;
|
||||
let data = { page: this.page };
|
||||
|
@ -80,12 +77,13 @@ class List {
|
|||
.then((resp) => {
|
||||
const data = resp.json();
|
||||
this.loading = false;
|
||||
this.issuesSize = data.size;
|
||||
|
||||
if (emptyIssues) {
|
||||
this.issues = [];
|
||||
}
|
||||
|
||||
this.createIssues(data);
|
||||
this.createIssues(data.issues);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -96,14 +94,20 @@ class List {
|
|||
}
|
||||
|
||||
addIssue (issue, listFrom) {
|
||||
this.issues.push(issue);
|
||||
if (!this.findIssue(issue.id)) {
|
||||
this.issues.push(issue);
|
||||
|
||||
if (this.label) {
|
||||
issue.addLabel(this.label);
|
||||
}
|
||||
if (this.label) {
|
||||
issue.addLabel(this.label);
|
||||
}
|
||||
|
||||
if (listFrom) {
|
||||
gl.boardService.moveIssue(issue.id, listFrom.id, this.id);
|
||||
if (listFrom) {
|
||||
this.issuesSize++;
|
||||
gl.boardService.moveIssue(issue.id, listFrom.id, this.id)
|
||||
.then(() => {
|
||||
listFrom.getIssues(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,6 +120,7 @@ class List {
|
|||
const matchesRemove = removeIssue.id === issue.id;
|
||||
|
||||
if (matchesRemove) {
|
||||
this.issuesSize--;
|
||||
issue.removeLabel(this.label);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
author_id: gl.utils.getParameterValues('author_id')[0],
|
||||
assignee_id: gl.utils.getParameterValues('assignee_id')[0],
|
||||
milestone_title: gl.utils.getParameterValues('milestone_title')[0],
|
||||
label_name: gl.utils.getParameterValues('label_name[]')
|
||||
label_name: gl.utils.getParameterValues('label_name[]'),
|
||||
search: ''
|
||||
};
|
||||
},
|
||||
addList (listObj) {
|
||||
|
|
0
app/assets/javascripts/boards/test_utils/simulate_drag.js
Executable file → Normal file
0
app/assets/javascripts/boards/test_utils/simulate_drag.js
Executable file → Normal file
|
@ -1,10 +1,7 @@
|
|||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
|
||||
|
||||
Vue.nextTick(() => {
|
||||
setTimeout(() => {
|
||||
Vue.activeResources--;
|
||||
}, 500);
|
||||
next(function (response) {
|
||||
Vue.activeResources--;
|
||||
});
|
||||
next();
|
||||
});
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
if ($(allDeviceSelector.join(",")).length) {
|
||||
return;
|
||||
}
|
||||
// Create all the elements
|
||||
els = $.map(BREAKPOINTS, function(breakpoint) {
|
||||
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
|
||||
});
|
||||
|
@ -40,6 +41,7 @@
|
|||
BreakpointInstance.prototype.getBreakpointSize = function() {
|
||||
var $visibleDevice;
|
||||
$visibleDevice = this.visibleDevice;
|
||||
// the page refreshed via turbolinks
|
||||
if (!$visibleDevice().length) {
|
||||
this.setup();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
this.toggleSidebar = bind(this.toggleSidebar, this);
|
||||
this.updateDropdown = bind(this.updateDropdown, this);
|
||||
clearInterval(Build.interval);
|
||||
// Init breakpoint checker
|
||||
this.bp = Breakpoints.get();
|
||||
$('.js-build-sidebar').niceScroll();
|
||||
|
||||
|
@ -26,10 +27,11 @@
|
|||
$(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
|
||||
$(window).off('resize.build').on('resize.build', this.hideSidebar);
|
||||
$(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
|
||||
$('#js-build-scroll > a').off('click').on('click', this.stepTrace);
|
||||
this.updateArtifactRemoveDate();
|
||||
if ($('#build-trace').length) {
|
||||
this.getInitialBuildTrace();
|
||||
this.initScrollButtonAffix();
|
||||
this.initScrollButtons();
|
||||
}
|
||||
if (this.build_status === "running" || this.build_status === "pending") {
|
||||
$('#autoscroll-button').on('click', function() {
|
||||
|
@ -42,6 +44,9 @@
|
|||
$(this).data("state", "enabled");
|
||||
return $(this).text("disable autoscroll");
|
||||
}
|
||||
//
|
||||
// Bind autoscroll button to follow build output
|
||||
//
|
||||
});
|
||||
Build.interval = setInterval((function(_this) {
|
||||
return function() {
|
||||
|
@ -49,17 +54,23 @@
|
|||
return _this.getBuildTrace();
|
||||
}
|
||||
};
|
||||
//
|
||||
// Check for new build output if user still watching build page
|
||||
// Only valid for runnig build when output changes during time
|
||||
//
|
||||
})(this), 4000);
|
||||
}
|
||||
}
|
||||
|
||||
Build.prototype.getInitialBuildTrace = function() {
|
||||
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']
|
||||
|
||||
return $.ajax({
|
||||
url: this.build_url,
|
||||
dataType: 'json',
|
||||
success: function(build_data) {
|
||||
$('.js-build-output').html(build_data.trace_html);
|
||||
if (build_data.status === 'success' || build_data.status === 'failed') {
|
||||
if (removeRefreshStatuses.indexOf(build_data.status) >= 0) {
|
||||
return $('.js-build-refresh').remove();
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +107,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
Build.prototype.initScrollButtonAffix = function() {
|
||||
Build.prototype.initScrollButtons = function() {
|
||||
var $body, $buildScroll, $buildTrace;
|
||||
$buildScroll = $('#js-build-scroll');
|
||||
$body = $('body');
|
||||
|
@ -155,6 +166,14 @@
|
|||
this.populateJobs(stage);
|
||||
};
|
||||
|
||||
Build.prototype.stepTrace = function(e) {
|
||||
e.preventDefault();
|
||||
$currentTarget = $(e.currentTarget);
|
||||
$.scrollTo($currentTarget.attr('href'), {
|
||||
offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight())
|
||||
});
|
||||
};
|
||||
|
||||
return Build;
|
||||
|
||||
})();
|
||||
|
|
6
app/assets/javascripts/build_variables.js.es6
Normal file
6
app/assets/javascripts/build_variables.js.es6
Normal file
|
@ -0,0 +1,6 @@
|
|||
$(function(){
|
||||
$('.reveal-variables').off('click').on('click',function(){
|
||||
$('.js-build').toggle().niceScroll();
|
||||
$(this).hide();
|
||||
});
|
||||
});
|
|
@ -2,6 +2,7 @@
|
|||
this.ImageFile = (function() {
|
||||
var prepareFrames;
|
||||
|
||||
// Width where images must fits in, for 2-up this gets divided by 2
|
||||
ImageFile.availWidth = 900;
|
||||
|
||||
ImageFile.viewModes = ['two-up', 'swipe'];
|
||||
|
@ -9,6 +10,7 @@
|
|||
function ImageFile(file) {
|
||||
this.file = file;
|
||||
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), (function(_this) {
|
||||
// Determine if old and new file has same dimensions, if not show 'two-up' view
|
||||
return function(deletedWidth, deletedHeight) {
|
||||
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(width, height) {
|
||||
if (width === deletedWidth && height === deletedHeight) {
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
CommitsList.content.html(data.html);
|
||||
return history.replaceState({
|
||||
page: commitsUrl
|
||||
// Change url so if user reload a page - search results are saved
|
||||
}, document.title, commitsUrl);
|
||||
},
|
||||
dataType: "json"
|
||||
|
|
|
@ -6,14 +6,19 @@
|
|||
|
||||
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 = '⌘';
|
||||
key = '⌘'; // Command
|
||||
} else {
|
||||
key = 'Ctrl';
|
||||
}
|
||||
|
|
93
app/assets/javascripts/cycle-analytics.js.es6
Normal file
93
app/assets/javascripts/cycle-analytics.js.es6
Normal file
|
@ -0,0 +1,93 @@
|
|||
((global) => {
|
||||
|
||||
const COOKIE_NAME = 'cycle_analytics_help_dismissed';
|
||||
const store = gl.cycleAnalyticsStore = {
|
||||
isLoading: true,
|
||||
hasError: false,
|
||||
isHelpDismissed: $.cookie(COOKIE_NAME),
|
||||
analytics: {}
|
||||
};
|
||||
|
||||
gl.CycleAnalytics = class CycleAnalytics {
|
||||
constructor() {
|
||||
const that = this;
|
||||
|
||||
this.vue = new Vue({
|
||||
el: '#cycle-analytics',
|
||||
name: 'CycleAnalytics',
|
||||
created: this.fetchData(),
|
||||
data: store,
|
||||
methods: {
|
||||
dismissLanding() {
|
||||
that.dismissLanding();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchData(options) {
|
||||
store.isLoading = true;
|
||||
options = options || { startDate: 30 };
|
||||
|
||||
$.ajax({
|
||||
url: $('#cycle-analytics').data('request-path'),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
data: { start_date: options.startDate }
|
||||
}).done((data) => {
|
||||
this.decorateData(data);
|
||||
this.initDropdown();
|
||||
})
|
||||
.error((data) => {
|
||||
this.handleError(data);
|
||||
})
|
||||
.always(() => {
|
||||
store.isLoading = false;
|
||||
})
|
||||
}
|
||||
|
||||
decorateData(data) {
|
||||
data.summary = data.summary || [];
|
||||
data.stats = data.stats || [];
|
||||
|
||||
data.summary.forEach((item) => {
|
||||
item.value = item.value || '-';
|
||||
});
|
||||
|
||||
data.stats.forEach((item) => {
|
||||
item.value = item.value || '- - -';
|
||||
});
|
||||
|
||||
store.analytics = data;
|
||||
}
|
||||
|
||||
handleError(data) {
|
||||
store.hasError = true;
|
||||
new Flash('There was an error while fetching cycle analytics data.', 'alert');
|
||||
}
|
||||
|
||||
dismissLanding() {
|
||||
store.isHelpDismissed = true;
|
||||
$.cookie(COOKIE_NAME, true, {
|
||||
path: gon.relative_url_root || '/'
|
||||
});
|
||||
}
|
||||
|
||||
initDropdown() {
|
||||
const $dropdown = $('.js-ca-dropdown');
|
||||
const $label = $dropdown.find('.dropdown-label');
|
||||
|
||||
$dropdown.find('li a').off('click').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
const $target = $(e.currentTarget);
|
||||
const value = $target.data('value');
|
||||
|
||||
$label.text($target.text().trim());
|
||||
this.fetchData({ startDate: value });
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})(window.gl || (window.gl = {}));
|
|
@ -39,6 +39,9 @@
|
|||
bottom: unfoldBottom,
|
||||
offset: offset,
|
||||
unfold: unfold,
|
||||
// indent is used to compensate for single space indent to fit
|
||||
// '+' and '-' prepended to diff lines,
|
||||
// see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
|
||||
indent: 1,
|
||||
view: file.data('view')
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
case 'projects:boards:show':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
break;
|
||||
case 'projects:merge_requests:index':
|
||||
case 'projects:issues:index':
|
||||
Issuable.init();
|
||||
new IssuableBulkActions();
|
||||
|
@ -93,6 +94,7 @@
|
|||
break;
|
||||
case "projects:merge_requests:conflicts":
|
||||
window.mcui = new MergeConflictResolver()
|
||||
break;
|
||||
case 'projects:merge_requests:index':
|
||||
shortcut_handler = new ShortcutsNavigation();
|
||||
Issuable.init();
|
||||
|
@ -167,6 +169,8 @@
|
|||
}
|
||||
break;
|
||||
case 'projects:network:show':
|
||||
// Ensure we don't create a particular shortcut handler here. This is
|
||||
// already created, where the network graph is created.
|
||||
shortcut_handler = true;
|
||||
break;
|
||||
case 'projects:forks:new':
|
||||
|
@ -186,6 +190,9 @@
|
|||
new gl.ProtectedBranchCreate();
|
||||
new gl.ProtectedBranchEditList();
|
||||
break;
|
||||
case 'projects:cycle_analytics:show':
|
||||
new gl.CycleAnalytics();
|
||||
break;
|
||||
}
|
||||
switch (path.first()) {
|
||||
case 'admin':
|
||||
|
@ -199,9 +206,13 @@
|
|||
break;
|
||||
case 'labels':
|
||||
switch (path[2]) {
|
||||
case 'new':
|
||||
case 'edit':
|
||||
new Labels();
|
||||
}
|
||||
case 'abuse_reports':
|
||||
new gl.AbuseReports();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'dashboard':
|
||||
|
@ -259,12 +270,14 @@
|
|||
shortcut_handler = new ShortcutsNavigation();
|
||||
}
|
||||
}
|
||||
// If we haven't installed a custom shortcut handler, install the default one
|
||||
if (!shortcut_handler) {
|
||||
return new Shortcuts();
|
||||
}
|
||||
};
|
||||
|
||||
Dispatcher.prototype.initSearch = function() {
|
||||
// Only when search form is present
|
||||
if ($('.search').length) {
|
||||
return new SearchAutocomplete();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
this.DueDateSelect = (function() {
|
||||
function DueDateSelect() {
|
||||
var $datePicker, $dueDate, $loading;
|
||||
// Milestone edit/new form
|
||||
$datePicker = $('.datepicker');
|
||||
if ($datePicker.length) {
|
||||
$dueDate = $('#milestone_due_date');
|
||||
|
@ -16,6 +17,7 @@
|
|||
e.preventDefault();
|
||||
return $.datepicker._clearDate($datePicker);
|
||||
});
|
||||
// Issuable sidebar
|
||||
$loading = $('.js-issuable-update .due_date').find('.block-loading').hide();
|
||||
$('.js-due-date-select').each(function(i, dropdown) {
|
||||
var $block, $dropdown, $dropdownParent, $selectbox, $sidebarValue, $value, $valueContent, abilityName, addDueDate, fieldName, issueUpdateURL;
|
||||
|
@ -38,6 +40,7 @@
|
|||
});
|
||||
addDueDate = function(isDropdown) {
|
||||
var data, date, mediumDate, value;
|
||||
// Create the post date
|
||||
value = $("input[name='" + fieldName + "']").val();
|
||||
if (value !== '') {
|
||||
date = new Date(value.replace(new RegExp('-', 'g'), ','));
|
||||
|
|
2
app/assets/javascripts/extensions/jquery.js
vendored
2
app/assets/javascripts/extensions/jquery.js
vendored
|
@ -1,3 +1,4 @@
|
|||
// Disable an element and add the 'disabled' Bootstrap class
|
||||
(function() {
|
||||
$.fn.extend({
|
||||
disable: function() {
|
||||
|
@ -5,6 +6,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
// Enable an element and remove the 'disabled' Bootstrap class
|
||||
$.fn.extend({
|
||||
enable: function() {
|
||||
return $(this).removeAttr('disabled').removeClass('disabled');
|
||||
|
|
|
@ -39,12 +39,13 @@
|
|||
FilesCommentButton.prototype.render = function(e) {
|
||||
var $currentTarget, buttonParentElement, lineContentElement, textFileElement;
|
||||
$currentTarget = $(e.currentTarget);
|
||||
|
||||
buttonParentElement = this.getButtonParent($currentTarget);
|
||||
if (!this.shouldRender(e, buttonParentElement)) {
|
||||
return;
|
||||
}
|
||||
textFileElement = this.getTextFileElement($currentTarget);
|
||||
if (!this.validateButtonParent(buttonParentElement)) return;
|
||||
lineContentElement = this.getLineContent($currentTarget);
|
||||
if (!this.validateLineContent(lineContentElement)) return;
|
||||
|
||||
textFileElement = this.getTextFileElement($currentTarget);
|
||||
buttonParentElement.append(this.buildButton({
|
||||
noteableType: textFileElement.attr('data-noteable-type'),
|
||||
noteableID: textFileElement.attr('data-noteable-id'),
|
||||
|
@ -119,10 +120,14 @@
|
|||
return newButtonParent.is(this.getButtonParent($(e.currentTarget)));
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.shouldRender = function(e, buttonParentElement) {
|
||||
FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
|
||||
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) && $(COMMENT_BUTTON_CLASS, buttonParentElement).length === 0;
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
|
||||
return lineContentElement.attr('data-discussion-id') && lineContentElement.attr('data-discussion-id') !== '';
|
||||
};
|
||||
|
||||
return FilesCommentButton;
|
||||
|
||||
})();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Creates the variables for setting up GFM auto-completion
|
||||
(function() {
|
||||
if (window.GitLab == null) {
|
||||
window.GitLab = {};
|
||||
|
@ -8,18 +9,22 @@
|
|||
dataLoaded: false,
|
||||
cachedData: {},
|
||||
dataSource: '',
|
||||
// Emoji
|
||||
Emoji: {
|
||||
template: '<li>${name} <img alt="${name}" height="20" src="${path}" width="20" /></li>'
|
||||
},
|
||||
// Team Members
|
||||
Members: {
|
||||
template: '<li>${username} <small>${title}</small></li>'
|
||||
},
|
||||
Labels: {
|
||||
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
|
||||
},
|
||||
// Issues and MergeRequests
|
||||
Issues: {
|
||||
template: '<li><small>${id}</small> ${title}</li>'
|
||||
},
|
||||
// Milestones
|
||||
Milestones: {
|
||||
template: '<li>${title}</li>'
|
||||
},
|
||||
|
@ -48,8 +53,11 @@
|
|||
}
|
||||
},
|
||||
setup: function(input) {
|
||||
// Add GFM auto-completion to all input fields, that accept GFM input.
|
||||
this.input = input || $('.js-gfm-input');
|
||||
// destroy previous instances
|
||||
this.destroyAtWho();
|
||||
// set up instances
|
||||
this.setupAtWho();
|
||||
if (this.dataSource) {
|
||||
if (!this.dataLoading && !this.cachedData) {
|
||||
|
@ -63,6 +71,11 @@
|
|||
return _this.loadData(data);
|
||||
});
|
||||
};
|
||||
// We should wait until initializations are done
|
||||
// and only trigger the last .setup since
|
||||
// The previous .dataSource belongs to the previous issuable
|
||||
// and the last one will have the **proper** .dataSource property
|
||||
// TODO: Make this a singleton and turn off events when moving to another page
|
||||
})(this), 1000);
|
||||
}
|
||||
if (this.cachedData != null) {
|
||||
|
@ -71,6 +84,7 @@
|
|||
}
|
||||
},
|
||||
setupAtWho: function() {
|
||||
// Emoji
|
||||
this.input.atwho({
|
||||
at: ':',
|
||||
displayTpl: (function(_this) {
|
||||
|
@ -90,6 +104,7 @@
|
|||
beforeInsert: this.DefaultOptions.beforeInsert
|
||||
}
|
||||
});
|
||||
// Team Members
|
||||
this.input.atwho({
|
||||
at: '@',
|
||||
displayTpl: (function(_this) {
|
||||
|
@ -321,13 +336,22 @@
|
|||
loadData: function(data) {
|
||||
this.cachedData = data;
|
||||
this.dataLoaded = true;
|
||||
// load members
|
||||
this.input.atwho('load', '@', data.members);
|
||||
// load issues
|
||||
this.input.atwho('load', 'issues', data.issues);
|
||||
// load milestones
|
||||
this.input.atwho('load', 'milestones', data.milestones);
|
||||
// load merge requests
|
||||
this.input.atwho('load', 'mergerequests', data.mergerequests);
|
||||
// load emojis
|
||||
this.input.atwho('load', ':', data.emojis);
|
||||
// load labels
|
||||
this.input.atwho('load', '~', data.labels);
|
||||
// load commands
|
||||
this.input.atwho('load', '/', data.commands);
|
||||
// This trigger at.js again
|
||||
// otherwise we would be stuck with loading until the user types
|
||||
return $(':focus').trigger('keyup');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,12 +21,14 @@
|
|||
$clearButton = $inputContainer.find('.js-dropdown-input-clear');
|
||||
this.indeterminateIds = [];
|
||||
$clearButton.on('click', (function(_this) {
|
||||
// Clear click
|
||||
return function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return _this.input.val('').trigger('keyup').focus();
|
||||
};
|
||||
})(this));
|
||||
// Key events
|
||||
timeout = "";
|
||||
this.input
|
||||
.on('keydown', function (e) {
|
||||
|
@ -49,6 +51,7 @@
|
|||
if (keyCode === 13 && !options.elIsInput) {
|
||||
return false;
|
||||
}
|
||||
// Only filter asynchronously only if option remote is set
|
||||
if (this.options.remote) {
|
||||
clearTimeout(timeout);
|
||||
return timeout = setTimeout(function() {
|
||||
|
@ -79,11 +82,27 @@
|
|||
if ((data != null) && !this.options.filterByText) {
|
||||
results = data;
|
||||
if (search_text !== '') {
|
||||
// When data is an array of objects therefore [object Array] e.g.
|
||||
// [
|
||||
// { prop: 'foo' },
|
||||
// { prop: 'baz' }
|
||||
// ]
|
||||
if (_.isArray(data)) {
|
||||
results = fuzzaldrinPlus.filter(data, search_text, {
|
||||
key: this.options.keys
|
||||
});
|
||||
} else {
|
||||
// If data is grouped therefore an [object Object]. e.g.
|
||||
// {
|
||||
// groupName1: [
|
||||
// { prop: 'foo' },
|
||||
// { prop: 'baz' }
|
||||
// ],
|
||||
// groupName2: [
|
||||
// { prop: 'abc' },
|
||||
// { prop: 'def' }
|
||||
// ]
|
||||
// }
|
||||
if (gl.utils.isObject(data)) {
|
||||
results = {};
|
||||
for (key in data) {
|
||||
|
@ -117,7 +136,7 @@
|
|||
}
|
||||
});
|
||||
} else {
|
||||
return elements.show();
|
||||
return elements.show().removeClass('option-hidden');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -140,6 +159,7 @@
|
|||
this.options.beforeSend();
|
||||
}
|
||||
return this.dataEndpoint("", (function(_this) {
|
||||
// Fetch the data by calling the data funcfion
|
||||
return function(data) {
|
||||
if (_this.options.success) {
|
||||
_this.options.success(data);
|
||||
|
@ -171,6 +191,7 @@
|
|||
};
|
||||
})(this)
|
||||
});
|
||||
// Fetch the data through ajax if the data is a string
|
||||
};
|
||||
|
||||
return GitLabDropdownRemote;
|
||||
|
@ -190,9 +211,9 @@
|
|||
|
||||
currentIndex = -1;
|
||||
|
||||
NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link, .option-hidden';
|
||||
NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link';
|
||||
|
||||
SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ")";
|
||||
SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)";
|
||||
|
||||
CURSOR_SELECT_SCROLL_PADDING = 5
|
||||
|
||||
|
@ -209,13 +230,18 @@
|
|||
self = this;
|
||||
selector = $(this.el).data("target");
|
||||
this.dropdown = selector != null ? $(selector) : $(this.el).parent();
|
||||
// Set Defaults
|
||||
ref = this.options, this.filterInput = (ref1 = ref.filterInput) != null ? ref1 : this.getElement(FILTER_INPUT), this.highlight = (ref2 = ref.highlight) != null ? ref2 : false, this.filterInputBlur = (ref3 = ref.filterInputBlur) != null ? ref3 : true;
|
||||
// If no input is passed create a default one
|
||||
self = this;
|
||||
// If selector was passed
|
||||
if (_.isString(this.filterInput)) {
|
||||
this.filterInput = this.getElement(this.filterInput);
|
||||
}
|
||||
searchFields = this.options.search ? this.options.search.fields : [];
|
||||
if (this.options.data) {
|
||||
// If we provided data
|
||||
// data could be an array of objects or a group of arrays
|
||||
if (_.isObject(this.options.data) && !_.isFunction(this.options.data)) {
|
||||
this.fullData = this.options.data;
|
||||
currentIndex = -1;
|
||||
|
@ -232,10 +258,12 @@
|
|||
return _this.filter.input.trigger('keyup');
|
||||
}
|
||||
};
|
||||
// Remote data
|
||||
})(this)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Init filterable
|
||||
if (this.options.filterable) {
|
||||
this.filter = new GitLabDropdownFilter(this.filterInput, {
|
||||
elIsInput: $(this.el).is('input'),
|
||||
|
@ -278,12 +306,14 @@
|
|||
})(this)
|
||||
});
|
||||
}
|
||||
// Event listeners
|
||||
this.dropdown.on("shown.bs.dropdown", this.opened);
|
||||
this.dropdown.on("hidden.bs.dropdown", this.hidden);
|
||||
$(this.el).on("update.label", this.updateLabel);
|
||||
this.dropdown.on("click", ".dropdown-menu, .dropdown-menu-close", this.shouldPropagate);
|
||||
this.dropdown.on('keyup', (function(_this) {
|
||||
return function(e) {
|
||||
// Escape key
|
||||
if (e.which === 27) {
|
||||
return $('.dropdown-menu-close', _this.dropdown).trigger('click');
|
||||
}
|
||||
|
@ -322,11 +352,18 @@
|
|||
if (self.options.clicked) {
|
||||
self.options.clicked(selected, $el, e);
|
||||
}
|
||||
return $el.trigger('blur');
|
||||
|
||||
// Update label right after all modifications in dropdown has been done
|
||||
if (self.options.toggleLabel) {
|
||||
self.updateLabel(selected, $el, self);
|
||||
}
|
||||
|
||||
$el.trigger('blur');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Finds an element inside wrapper element
|
||||
GitLabDropdown.prototype.getElement = function(selector) {
|
||||
return this.dropdown.find(selector);
|
||||
};
|
||||
|
@ -344,6 +381,7 @@
|
|||
}
|
||||
}
|
||||
menu.toggleClass(PAGE_TWO_CLASS);
|
||||
// Focus first visible input on active page
|
||||
return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus();
|
||||
};
|
||||
|
||||
|
@ -351,23 +389,28 @@
|
|||
var full_html, groupData, html, name;
|
||||
this.renderedData = data;
|
||||
if (this.options.filterable && data.length === 0) {
|
||||
// render no matching results
|
||||
html = [this.noResults()];
|
||||
} else {
|
||||
// Handle array groups
|
||||
if (gl.utils.isObject(data)) {
|
||||
html = [];
|
||||
for (name in data) {
|
||||
groupData = data[name];
|
||||
html.push(this.renderItem({
|
||||
header: name
|
||||
// Add header for each group
|
||||
}, name));
|
||||
this.renderData(groupData, name).map(function(item) {
|
||||
return html.push(item);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Render each row
|
||||
html = this.renderData(data);
|
||||
}
|
||||
}
|
||||
// Render the full menu
|
||||
full_html = this.renderMenu(html);
|
||||
return this.appendMenu(full_html);
|
||||
};
|
||||
|
@ -406,6 +449,7 @@
|
|||
if (this.options.setActiveIds) {
|
||||
this.options.setActiveIds.call(this);
|
||||
}
|
||||
// Makes indeterminate items effective
|
||||
if (this.fullData && this.dropdown.find('.dropdown-menu-toggle').hasClass('js-filter-bulk-update')) {
|
||||
this.parseData(this.fullData);
|
||||
}
|
||||
|
@ -427,6 +471,8 @@
|
|||
if (this.options.filterable) {
|
||||
$input.blur().val("");
|
||||
}
|
||||
// Triggering 'keyup' will re-render the dropdown which is not always required
|
||||
// specially if we want to keep the state of the dropdown needed for bulk-assignment
|
||||
if (!this.options.persistWhenHide) {
|
||||
$input.trigger("keyup");
|
||||
}
|
||||
|
@ -439,6 +485,7 @@
|
|||
return this.dropdown.trigger('hidden.gl.dropdown');
|
||||
};
|
||||
|
||||
// Render the full menu
|
||||
GitLabDropdown.prototype.renderMenu = function(html) {
|
||||
var menu_html;
|
||||
menu_html = "";
|
||||
|
@ -450,6 +497,7 @@
|
|||
return menu_html;
|
||||
};
|
||||
|
||||
// Append the menu into the dropdown
|
||||
GitLabDropdown.prototype.appendMenu = function(html) {
|
||||
var selector;
|
||||
selector = '.dropdown-content';
|
||||
|
@ -465,35 +513,42 @@
|
|||
group = false;
|
||||
}
|
||||
if (index == null) {
|
||||
// Render the row
|
||||
index = false;
|
||||
}
|
||||
html = "";
|
||||
// Divider
|
||||
if (data === "divider") {
|
||||
return "<li class='divider'></li>";
|
||||
}
|
||||
// Separator is a full-width divider
|
||||
if (data === "separator") {
|
||||
return "<li class='separator'></li>";
|
||||
}
|
||||
// Header
|
||||
if (data.header != null) {
|
||||
return _.template('<li class="dropdown-header"><%- header %></li>')({ header: data.header });
|
||||
}
|
||||
if (this.options.renderRow) {
|
||||
// Call the render function
|
||||
html = this.options.renderRow.call(this.options, data, this);
|
||||
} else {
|
||||
if (!selected) {
|
||||
value = this.options.id ? this.options.id(data) : data.id;
|
||||
fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName() : this.options.fieldName;
|
||||
fieldName = this.options.fieldName;
|
||||
|
||||
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']");
|
||||
if (field.length) {
|
||||
selected = true;
|
||||
}
|
||||
}
|
||||
// Set URL
|
||||
if (this.options.url != null) {
|
||||
url = this.options.url(data);
|
||||
} else {
|
||||
url = data.url != null ? data.url : '#';
|
||||
}
|
||||
// Set Text
|
||||
if (this.options.text != null) {
|
||||
text = this.options.text(data);
|
||||
} else {
|
||||
|
@ -540,6 +595,7 @@
|
|||
|
||||
GitLabDropdown.prototype.rowClicked = function(el) {
|
||||
var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value;
|
||||
fieldName = this.options.fieldName;
|
||||
isInput = $(this.el).is('input');
|
||||
if (this.renderedData) {
|
||||
groupName = el.data('group');
|
||||
|
@ -551,34 +607,31 @@
|
|||
selectedObject = this.renderedData[selectedIndex];
|
||||
}
|
||||
}
|
||||
fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName(selectedObject) : this.options.fieldName;
|
||||
field = [];
|
||||
value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
|
||||
if (isInput) {
|
||||
field = $(this.el);
|
||||
} else {
|
||||
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']");
|
||||
} else if(value) {
|
||||
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']");
|
||||
}
|
||||
if (el.hasClass(ACTIVE_CLASS)) {
|
||||
el.removeClass(ACTIVE_CLASS);
|
||||
if (isInput) {
|
||||
field.val('');
|
||||
} else {
|
||||
field.remove();
|
||||
if (field && field.length) {
|
||||
if (isInput) {
|
||||
field.val('');
|
||||
} else {
|
||||
field.remove();
|
||||
}
|
||||
}
|
||||
if (this.options.toggleLabel) {
|
||||
this.updateLabel(selectedObject, el, this);
|
||||
}
|
||||
return selectedObject;
|
||||
} else if (el.hasClass(INDETERMINATE_CLASS)) {
|
||||
el.addClass(ACTIVE_CLASS);
|
||||
el.removeClass(INDETERMINATE_CLASS);
|
||||
if (value == null) {
|
||||
if (field && field.length && value == null) {
|
||||
field.remove();
|
||||
}
|
||||
if (!field.length && fieldName) {
|
||||
if ((!field || !field.length) && fieldName) {
|
||||
this.addInput(fieldName, value, selectedObject);
|
||||
}
|
||||
return selectedObject;
|
||||
} else {
|
||||
if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) {
|
||||
this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS);
|
||||
|
@ -586,33 +639,30 @@
|
|||
this.dropdown.parent().find("input[name='" + fieldName + "']").remove();
|
||||
}
|
||||
}
|
||||
if (value == null) {
|
||||
if (field && field.length && value == null) {
|
||||
field.remove();
|
||||
}
|
||||
// Toggle active class for the tick mark
|
||||
el.addClass(ACTIVE_CLASS);
|
||||
if (this.options.toggleLabel) {
|
||||
this.updateLabel(selectedObject, el, this);
|
||||
}
|
||||
if (value != null) {
|
||||
if (!field.length && fieldName) {
|
||||
if ((!field || !field.length) && fieldName) {
|
||||
this.addInput(fieldName, value, selectedObject);
|
||||
} else {
|
||||
} else if (field && field.length) {
|
||||
field.val(value).trigger('change');
|
||||
}
|
||||
}
|
||||
return selectedObject;
|
||||
}
|
||||
|
||||
return selectedObject;
|
||||
};
|
||||
|
||||
GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) {
|
||||
var $input;
|
||||
// Create hidden input for form
|
||||
$input = $('<input>').attr('type', 'hidden').attr('name', fieldName).val(value);
|
||||
if (this.options.inputId != null) {
|
||||
$input.attr('id', this.options.inputId);
|
||||
}
|
||||
if (selectedObject && selectedObject.type) {
|
||||
$input.attr('data-type', selectedObject.type);
|
||||
}
|
||||
return this.dropdown.before($input);
|
||||
};
|
||||
|
||||
|
@ -627,6 +677,7 @@
|
|||
if (this.dropdown.find(".dropdown-toggle-page").length) {
|
||||
selector = ".dropdown-page-one " + selector;
|
||||
}
|
||||
// simulate a click on the first link
|
||||
$el = $(selector, this.dropdown);
|
||||
if ($el.length) {
|
||||
var href = $el.attr('href');
|
||||
|
@ -655,11 +706,15 @@
|
|||
e.stopImmediatePropagation();
|
||||
PREV_INDEX = currentIndex;
|
||||
$listItems = $(selector, _this.dropdown);
|
||||
// if @options.filterable
|
||||
// $input.blur()
|
||||
if (currentKeyCode === 40) {
|
||||
// Move down
|
||||
if (currentIndex < ($listItems.length - 1)) {
|
||||
currentIndex += 1;
|
||||
}
|
||||
} else if (currentKeyCode === 38) {
|
||||
// Move up
|
||||
if (currentIndex > 0) {
|
||||
currentIndex -= 1;
|
||||
}
|
||||
|
@ -687,24 +742,32 @@
|
|||
|
||||
GitLabDropdown.prototype.highlightRowAtIndex = function($listItems, index) {
|
||||
var $dropdownContent, $listItem, dropdownContentBottom, dropdownContentHeight, dropdownContentTop, dropdownScrollTop, listItemBottom, listItemHeight, listItemTop;
|
||||
// Remove the class for the previously focused row
|
||||
$('.is-focused', this.dropdown).removeClass('is-focused');
|
||||
// Update the class for the row at the specific index
|
||||
$listItem = $listItems.eq(index);
|
||||
$listItem.find('a:first-child').addClass("is-focused");
|
||||
// Dropdown content scroll area
|
||||
$dropdownContent = $listItem.closest('.dropdown-content');
|
||||
dropdownScrollTop = $dropdownContent.scrollTop();
|
||||
dropdownContentHeight = $dropdownContent.outerHeight();
|
||||
dropdownContentTop = $dropdownContent.prop('offsetTop');
|
||||
dropdownContentBottom = dropdownContentTop + dropdownContentHeight;
|
||||
// Get the offset bottom of the list item
|
||||
listItemHeight = $listItem.outerHeight();
|
||||
listItemTop = $listItem.prop('offsetTop');
|
||||
listItemBottom = listItemTop + listItemHeight;
|
||||
if (!index) {
|
||||
// Scroll the dropdown content to the top
|
||||
$dropdownContent.scrollTop(0)
|
||||
} else if (index === ($listItems.length - 1)) {
|
||||
// Scroll the dropdown content to the bottom
|
||||
$dropdownContent.scrollTop($dropdownContent.prop('scrollHeight'));
|
||||
} else if (listItemBottom > (dropdownContentBottom + dropdownScrollTop)) {
|
||||
// Scroll the dropdown content down
|
||||
$dropdownContent.scrollTop(listItemBottom - dropdownContentBottom + CURSOR_SELECT_SCROLL_PADDING);
|
||||
} else if (listItemTop < (dropdownContentTop + dropdownScrollTop)) {
|
||||
// Scroll the dropdown content up
|
||||
return $dropdownContent.scrollTop(listItemTop - dropdownContentTop - CURSOR_SELECT_SCROLL_PADDING);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
function GLForm(form) {
|
||||
this.form = form;
|
||||
this.textarea = this.form.find('textarea.js-gfm-input');
|
||||
// Before we start, we should clean up any previous data for this form
|
||||
this.destroy();
|
||||
// Setup the form
|
||||
this.setupForm();
|
||||
this.form.data('gl-form', this);
|
||||
}
|
||||
|
||||
GLForm.prototype.destroy = function() {
|
||||
// Clean form listeners
|
||||
this.clearEventListeners();
|
||||
return this.form.data('gl-form', null);
|
||||
};
|
||||
|
@ -21,12 +24,15 @@
|
|||
this.form.find('.div-dropzone').remove();
|
||||
this.form.addClass('gfm-form');
|
||||
disableButtonIfEmptyField(this.form.find('.js-note-text'), this.form.find('.js-comment-button'));
|
||||
// remove notify commit author checkbox for non-commit notes
|
||||
GitLab.GfmAutoComplete.setup(this.form.find('.js-gfm-input'));
|
||||
new DropzoneInput(this.form);
|
||||
autosize(this.textarea);
|
||||
// form and textarea event listeners
|
||||
this.addEventListeners();
|
||||
gl.text.init(this.form);
|
||||
}
|
||||
// hide discard button
|
||||
this.form.find('.js-note-discard').hide();
|
||||
return this.form.show();
|
||||
};
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
|
||||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
/*= require_tree . */
|
||||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -204,6 +204,7 @@
|
|||
|
||||
function ContributorsAuthorGraph(data1) {
|
||||
this.data = data1;
|
||||
// Don't split graph size in half for mobile devices.
|
||||
if ($(window).width() < 768) {
|
||||
this.width = $('.content').width() - 80;
|
||||
} else {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
return _this.formatSelection.apply(_this, args);
|
||||
},
|
||||
dropdownCssClass: "ajax-groups-dropdown",
|
||||
// we do not want to escape markup since we are displaying html in results
|
||||
escapeMarkup: function(m) {
|
||||
return m;
|
||||
}
|
||||
|
|
|
@ -10,21 +10,24 @@
|
|||
ImporterStatus.prototype.initStatusPage = function() {
|
||||
$('.js-add-to-import').off('click').on('click', (function(_this) {
|
||||
return function(e) {
|
||||
var $btn, $namespace_input, $target_field, $tr, id, new_namespace;
|
||||
var $btn, $namespace_input, $target_field, $tr, id, target_namespace, newName;
|
||||
$btn = $(e.currentTarget);
|
||||
$tr = $btn.closest('tr');
|
||||
$target_field = $tr.find('.import-target');
|
||||
$namespace_input = $target_field.find('input');
|
||||
$namespace_input = $target_field.find('.js-select-namespace option:selected');
|
||||
id = $tr.attr('id').replace('repo_', '');
|
||||
new_namespace = null;
|
||||
target_namespace = null;
|
||||
newName = null;
|
||||
if ($namespace_input.length > 0) {
|
||||
new_namespace = $namespace_input.prop('value');
|
||||
$target_field.empty().append(new_namespace + "/" + ($target_field.data('project_name')));
|
||||
target_namespace = $namespace_input[0].innerHTML;
|
||||
newName = $target_field.find('#path').prop('value');
|
||||
$target_field.empty().append(target_namespace + "/" + newName);
|
||||
}
|
||||
$btn.disable().addClass('is-loading');
|
||||
return $.post(_this.import_url, {
|
||||
repo_id: id,
|
||||
new_namespace: new_namespace
|
||||
target_namespace: target_namespace,
|
||||
new_name: newName
|
||||
}, {
|
||||
dataType: 'script'
|
||||
});
|
||||
|
@ -70,7 +73,7 @@
|
|||
if ($('.js-importer-status').length) {
|
||||
var jobsImportPath = $('.js-importer-status').data('jobs-import-path');
|
||||
var importPath = $('.js-importer-status').data('import-path');
|
||||
|
||||
|
||||
new ImporterStatus(jobsImportPath, importPath);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,38 +8,48 @@
|
|||
Issuable.initTemplates();
|
||||
Issuable.initSearch();
|
||||
Issuable.initChecks();
|
||||
Issuable.initResetFilters();
|
||||
return Issuable.initLabelFilterRemove();
|
||||
},
|
||||
initTemplates: function() {
|
||||
return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <span class="label-row btn-group" role="group" aria-label="<%- label.title %>" style="color: <%- label.text_color %>;"> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%- label.color %>;" title="<%- label.description %>" data-container="body"> <%- label.title %> </a> <button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%- label.color %>;" data-label="<%- label.title %>"> <i class="fa fa-times"></i> </button> </span> <% }); %>');
|
||||
},
|
||||
initSearch: function() {
|
||||
this.timer = null;
|
||||
return $('#issue_search').off('keyup').on('keyup', function() {
|
||||
clearTimeout(this.timer);
|
||||
return this.timer = setTimeout(function() {
|
||||
var $form, $input, $search;
|
||||
$search = $('#issue_search');
|
||||
$form = $('.js-filter-form');
|
||||
$input = $("input[name='" + ($search.attr('name')) + "']", $form);
|
||||
if ($input.length === 0) {
|
||||
$form.append("<input type='hidden' name='" + ($search.attr('name')) + "' value='" + (_.escape($search.val())) + "'/>");
|
||||
} else {
|
||||
$input.val($search.val());
|
||||
}
|
||||
if ($search.val() !== '') {
|
||||
return Issuable.filterResults($form);
|
||||
}
|
||||
}, 500);
|
||||
// `immediate` param set to false debounces on the `trailing` edge, lets user finish typing
|
||||
const debouncedExecSearch = _.debounce(Issuable.executeSearch, 500, false);
|
||||
|
||||
$('#issuable_search').off('keyup').on('keyup', debouncedExecSearch);
|
||||
|
||||
// ensures existing filters are preserved when manually submitted
|
||||
$('#issue_search_form').on('submit', (e) => {
|
||||
e.preventDefault();
|
||||
debouncedExecSearch(e);
|
||||
});
|
||||
},
|
||||
executeSearch: function(e) {
|
||||
const $search = $('#issuable_search');
|
||||
const $searchName = $search.attr('name');
|
||||
const $searchValue = $search.val();
|
||||
const $filtersForm = $('.js-filter-form');
|
||||
const $input = $(`input[name='${$searchName}']`, $filtersForm);
|
||||
|
||||
if (!$input.length) {
|
||||
$filtersForm.append(`<input type='hidden' name='${$searchName}' value='${_.escape($searchValue)}'/>`);
|
||||
} else {
|
||||
$input.val($searchValue);
|
||||
}
|
||||
|
||||
Issuable.filterResults($filtersForm);
|
||||
},
|
||||
initLabelFilterRemove: function() {
|
||||
return $(document).off('click', '.js-label-filter-remove').on('click', '.js-label-filter-remove', function(e) {
|
||||
var $button;
|
||||
$button = $(this);
|
||||
// Remove the label input box
|
||||
$('input[name="label_name[]"]').filter(function() {
|
||||
return this.value === $button.data('label');
|
||||
}).remove();
|
||||
// Submit the form to get new data
|
||||
Issuable.filterResults($('.filter-form'));
|
||||
return $('.js-label-select').trigger('update.label');
|
||||
});
|
||||
|
@ -55,6 +65,17 @@
|
|||
return Turbolinks.visit(issuesUrl);
|
||||
};
|
||||
})(this),
|
||||
initResetFilters: function() {
|
||||
$('.reset-filters').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const target = e.target;
|
||||
const $form = $(target).parents('.js-filter-form');
|
||||
const baseIssuesUrl = target.href;
|
||||
|
||||
$form.attr('action', baseIssuesUrl);
|
||||
Turbolinks.visit(baseIssuesUrl);
|
||||
});
|
||||
},
|
||||
initChecks: function() {
|
||||
this.issuableBulkActions = $('.bulk-update').data('bulkActions');
|
||||
$('.check_all_issues').off('click').on('click', function() {
|
||||
|
@ -64,19 +85,22 @@
|
|||
return $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(this));
|
||||
},
|
||||
checkChanged: function() {
|
||||
var checked_issues, ids;
|
||||
checked_issues = $('.selected_issue:checked');
|
||||
if (checked_issues.length > 0) {
|
||||
ids = $.map(checked_issues, function(value) {
|
||||
const $checkedIssues = $('.selected_issue:checked');
|
||||
const $updateIssuesIds = $('#update_issuable_ids');
|
||||
const $issuesOtherFilters = $('.issues-other-filters');
|
||||
const $issuesBulkUpdate = $('.issues_bulk_update');
|
||||
|
||||
if ($checkedIssues.length > 0) {
|
||||
let ids = $.map($checkedIssues, function(value) {
|
||||
return $(value).data('id');
|
||||
});
|
||||
$('#update_issues_ids').val(ids);
|
||||
$('.issues-other-filters').hide();
|
||||
$('.issues_bulk_update').show();
|
||||
$updateIssuesIds.val(ids);
|
||||
$issuesOtherFilters.hide();
|
||||
$issuesBulkUpdate.show();
|
||||
} else {
|
||||
$('#update_issues_ids').val([]);
|
||||
$('.issues_bulk_update').hide();
|
||||
$('.issues-other-filters').show();
|
||||
$updateIssuesIds.val([]);
|
||||
$issuesBulkUpdate.hide();
|
||||
$issuesOtherFilters.show();
|
||||
this.issuableBulkActions.willUpdateLabels = false;
|
||||
}
|
||||
return true;
|
|
@ -102,20 +102,34 @@
|
|||
};
|
||||
|
||||
IssuableForm.prototype.initMoveDropdown = function() {
|
||||
var $moveDropdown;
|
||||
var $moveDropdown, pageSize;
|
||||
$moveDropdown = $('.js-move-dropdown');
|
||||
if ($moveDropdown.length) {
|
||||
pageSize = $moveDropdown.data('page-size');
|
||||
return $('.js-move-dropdown').select2({
|
||||
ajax: {
|
||||
url: $moveDropdown.data('projects-url'),
|
||||
results: function(data) {
|
||||
quietMillis: 125,
|
||||
data: function(term, page, context) {
|
||||
return {
|
||||
results: data
|
||||
search: term,
|
||||
offset_id: context
|
||||
};
|
||||
},
|
||||
data: function(query) {
|
||||
results: function(data) {
|
||||
var context,
|
||||
more;
|
||||
|
||||
if (data.length >= pageSize)
|
||||
more = true;
|
||||
|
||||
if (data[data.length - 1])
|
||||
context = data[data.length - 1].id;
|
||||
|
||||
return {
|
||||
search: query
|
||||
results: data,
|
||||
more: more,
|
||||
context: context
|
||||
};
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
|
||||
/*= require flash */
|
||||
|
||||
|
||||
/*= require jquery.waitforimages */
|
||||
|
||||
|
||||
/*= require task_list */
|
||||
|
||||
(function() {
|
||||
|
@ -13,6 +9,7 @@
|
|||
this.Issue = (function() {
|
||||
function Issue() {
|
||||
this.submitNoteForm = bind(this.submitNoteForm, this);
|
||||
// Prevent duplicate event bindings
|
||||
this.disableTaskList();
|
||||
if ($('a.btn-close').length) {
|
||||
this.initTaskList();
|
||||
|
@ -99,6 +96,8 @@
|
|||
url: $('form.js-issuable-update').attr('action'),
|
||||
data: patchData
|
||||
});
|
||||
// TODO (rspeicher): Make the issue description inline-editable like a note so
|
||||
// that we can re-use its form here
|
||||
};
|
||||
|
||||
Issue.prototype.initMergeRequests = function() {
|
||||
|
@ -127,7 +126,9 @@
|
|||
|
||||
Issue.prototype.initCanCreateBranch = function() {
|
||||
var $container;
|
||||
$container = $('div#new-branch');
|
||||
$container = $('#new-branch');
|
||||
// If the user doesn't have the required permissions the container isn't
|
||||
// rendered at all.
|
||||
if ($container.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -139,7 +140,6 @@
|
|||
if (data.can_create_branch) {
|
||||
$container.find('.checking').hide();
|
||||
$container.find('.available').show();
|
||||
return $container.find('a').attr('disabled', false);
|
||||
} else {
|
||||
$container.find('.checking').hide();
|
||||
return $container.find('.unavailable').show();
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
(function() {
|
||||
this.IssuableBulkActions = (function() {
|
||||
function IssuableBulkActions(opts) {
|
||||
// Set defaults
|
||||
var ref, ref1, ref2;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issues-list .issue');
|
||||
this.container = (ref = opts.container) != null ? ref : $('.content'), this.form = (ref1 = opts.form) != null ? ref1 : this.getElement('.bulk-update'), this.issues = (ref2 = opts.issues) != null ? ref2 : this.getElement('.issuable-list > li');
|
||||
// Save instance
|
||||
this.form.data('bulkActions', this);
|
||||
this.willUpdateLabels = false;
|
||||
this.bindEvents();
|
||||
// Fixes bulk-assign not working when navigating through pages
|
||||
Issuable.initChecks();
|
||||
}
|
||||
|
||||
|
@ -86,6 +89,7 @@
|
|||
ref1 = this.getLabelsFromSelection();
|
||||
for (j = 0, len1 = ref1.length; j < len1; j++) {
|
||||
id = ref1[j];
|
||||
// Only the ones that we are not going to keep
|
||||
if (labelsToKeep.indexOf(id) === -1) {
|
||||
result.push(id);
|
||||
}
|
||||
|
@ -106,7 +110,7 @@
|
|||
state_event: this.form.find('input[name="update[state_event]"]').val(),
|
||||
assignee_id: this.form.find('input[name="update[assignee_id]"]').val(),
|
||||
milestone_id: this.form.find('input[name="update[milestone_id]"]').val(),
|
||||
issues_ids: this.form.find('input[name="update[issues_ids]"]').val(),
|
||||
issuable_ids: this.form.find('input[name="update[issuable_ids]"]').val(),
|
||||
subscription_event: this.form.find('input[name="update[subscription_event]"]').val(),
|
||||
add_label_ids: [],
|
||||
remove_label_ids: []
|
||||
|
@ -147,6 +151,8 @@
|
|||
indeterminatedLabels = this.getUnmarkedIndeterminedLabels();
|
||||
labelsToApply = this.getLabelsToApply();
|
||||
indeterminatedLabels.map(function(id) {
|
||||
// We need to exclude label IDs that will be applied
|
||||
// By not doing this will cause issues from selection to not add labels at all
|
||||
if (labelsToApply.indexOf(id) === -1) {
|
||||
return result.push(id);
|
||||
}
|
||||
|
|
|
@ -26,13 +26,16 @@
|
|||
var previewColor;
|
||||
previewColor = $('input#label_color').val();
|
||||
return $('div.label-color-preview').css('background-color', previewColor);
|
||||
// Updates the the preview color with the hex-color input
|
||||
};
|
||||
|
||||
// Updates the preview color with a click on a suggested color
|
||||
Labels.prototype.setSuggestedColor = function(e) {
|
||||
var color;
|
||||
color = $(e.currentTarget).data('color');
|
||||
$('input#label_color').val(color);
|
||||
this.updateColorPreview();
|
||||
// Notify the form, that color has changed
|
||||
$('.label-form').trigger('keyup');
|
||||
return e.preventDefault();
|
||||
};
|
||||
|
|
|
@ -156,15 +156,17 @@
|
|||
selectedClass.push('is-indeterminate');
|
||||
}
|
||||
if (active.indexOf(label.id) !== -1) {
|
||||
// Remove is-indeterminate class if the item will be marked as active
|
||||
i = selectedClass.indexOf('is-indeterminate');
|
||||
if (i !== -1) {
|
||||
selectedClass.splice(i, 1);
|
||||
}
|
||||
selectedClass.push('is-active');
|
||||
// Add input manually
|
||||
instance.addInput(this.fieldName, label.id);
|
||||
}
|
||||
}
|
||||
if ($form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + (this.id(label)) + "']").length) {
|
||||
if (this.id(label) && $form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + this.id(label).toString().replace(/'/g, '\\\'') + "']").length) {
|
||||
selectedClass.push('is-active');
|
||||
}
|
||||
if ($dropdown.hasClass('js-multiselect') && removesAll) {
|
||||
|
@ -172,6 +174,7 @@
|
|||
}
|
||||
if (label.duplicate) {
|
||||
spacing = 100 / label.color.length;
|
||||
// Reduce the colors to 4
|
||||
label.color = label.color.filter(function(color, i) {
|
||||
return i < 4;
|
||||
});
|
||||
|
@ -192,11 +195,13 @@
|
|||
} else {
|
||||
colorEl = '';
|
||||
}
|
||||
// We need to identify which items are actually labels
|
||||
if (label.id) {
|
||||
selectedClass.push('label-item');
|
||||
$a.attr('data-label-id', label.id);
|
||||
}
|
||||
$a.addClass(selectedClass.join(' ')).html(colorEl + " " + label.title);
|
||||
// Return generated html
|
||||
return $li.html($a).prop('outerHTML');
|
||||
},
|
||||
persistWhenHide: $dropdown.data('persistWhenHide'),
|
||||
|
@ -238,6 +243,7 @@
|
|||
isIssueIndex = page === 'projects:issues:index';
|
||||
isMRIndex = page === 'projects:merge_requests:index';
|
||||
$selectbox.hide();
|
||||
// display:block overrides the hide-collapse rule
|
||||
$value.removeAttr('style');
|
||||
if (page === 'projects:boards:show') {
|
||||
return;
|
||||
|
@ -255,6 +261,7 @@
|
|||
}
|
||||
}
|
||||
if ($dropdown.hasClass('js-filter-bulk-update')) {
|
||||
// If we are persisting state we need the classes
|
||||
if (!this.options.persistWhenHide) {
|
||||
return $dropdown.parent().find('.is-active, .is-indeterminate').removeClass();
|
||||
}
|
||||
|
@ -324,7 +331,9 @@
|
|||
if ($('.selected_issue:checked').length) {
|
||||
return;
|
||||
}
|
||||
// Remove inputs
|
||||
$('.issues_bulk_update .labels-filter input[type="hidden"]').remove();
|
||||
// Also restore button text
|
||||
return $('.issues_bulk_update .labels-filter .dropdown-toggle-text').text('Label');
|
||||
};
|
||||
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
};
|
||||
|
||||
$(function() {
|
||||
hideEndFade($('.scrolling-tabs'));
|
||||
var $scrollingTabs = $('.scrolling-tabs');
|
||||
|
||||
hideEndFade($scrollingTabs);
|
||||
$(window).off('resize.nav').on('resize.nav', function() {
|
||||
return hideEndFade($('.scrolling-tabs'));
|
||||
return hideEndFade($scrollingTabs);
|
||||
});
|
||||
return $('.scrolling-tabs').on('scroll', function(event) {
|
||||
$scrollingTabs.off('scroll').on('scroll', function(event) {
|
||||
var $this, currentPosition, maxPosition;
|
||||
$this = $(this);
|
||||
currentPosition = $this.scrollLeft();
|
||||
|
@ -22,6 +24,23 @@
|
|||
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
|
||||
return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
|
||||
});
|
||||
|
||||
$scrollingTabs.each(function () {
|
||||
var $this = $(this),
|
||||
scrollingTabWidth = $this.width(),
|
||||
$active = $this.find('.active'),
|
||||
activeWidth = $active.width();
|
||||
|
||||
if ($active.length) {
|
||||
var offset = $active.offset().left + activeWidth;
|
||||
|
||||
if (offset > scrollingTabWidth - 30) {
|
||||
var scrollLeft = scrollingTabWidth / 2;
|
||||
scrollLeft = (offset - scrollLeft) - (activeWidth / 2);
|
||||
$this.scrollLeft(scrollLeft);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -3,5 +3,4 @@
|
|||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -3,5 +3,4 @@
|
|||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
1
app/assets/javascripts/lib/d3.js
vendored
1
app/assets/javascripts/lib/d3.js
vendored
|
@ -3,5 +3,4 @@
|
|||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
|
||||
/*= require raphael */
|
||||
|
||||
|
||||
/*= require g.raphael */
|
||||
|
||||
|
||||
/*= require g.bar */
|
||||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
if (setTimeago) {
|
||||
$timeagoEls.timeago();
|
||||
$timeagoEls.tooltip('destroy');
|
||||
// Recreate with custom template
|
||||
return $timeagoEls.tooltip({
|
||||
template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
|
||||
});
|
||||
|
@ -67,6 +68,14 @@
|
|||
$.timeago.settings.strings = tmpLocale;
|
||||
};
|
||||
|
||||
w.gl.utils.getDayDifference = function(a, b) {
|
||||
var millisecondsPerDay = 1000 * 60 * 60 * 24;
|
||||
var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
|
||||
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
|
||||
|
||||
return Math.floor((date2 - date1) / millisecondsPerDay);
|
||||
}
|
||||
|
||||
})(window);
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
gl.emojiAliases = ->
|
||||
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
|
6
app/assets/javascripts/lib/utils/emoji_aliases.js.erb
Normal file
6
app/assets/javascripts/lib/utils/emoji_aliases.js.erb
Normal file
|
@ -0,0 +1,6 @@
|
|||
(function() {
|
||||
gl.emojiAliases = function() {
|
||||
return JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>');
|
||||
};
|
||||
|
||||
}).call(this);
|
|
@ -6,6 +6,7 @@
|
|||
notification = new Notification(message, opts);
|
||||
setTimeout(function() {
|
||||
return notification.close();
|
||||
// Hide the notification after X amount of seconds
|
||||
}, 8000);
|
||||
if (onclick) {
|
||||
return notification.onclick = onclick;
|
||||
|
@ -22,12 +23,16 @@
|
|||
body: body,
|
||||
icon: icon
|
||||
};
|
||||
// Let's check if the browser supports notifications
|
||||
if (!('Notification' in window)) {
|
||||
|
||||
// do nothing
|
||||
} else if (Notification.permission === 'granted') {
|
||||
// If it's okay let's create a notification
|
||||
return notificationGranted(message, opts, onclick);
|
||||
} else if (Notification.permission !== 'denied') {
|
||||
return Notification.requestPermission(function(permission) {
|
||||
// If the user accepts, let's create a notification
|
||||
if (permission === 'granted') {
|
||||
return notificationGranted(message, opts, onclick);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
lineBefore = this.lineBefore(text, textArea);
|
||||
lineAfter = this.lineAfter(text, textArea);
|
||||
if (lineBefore === blockTag && lineAfter === blockTag) {
|
||||
// To remove the block tag we have to select the line before & after
|
||||
if (blockTag != null) {
|
||||
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
|
||||
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
|
||||
|
@ -63,11 +64,11 @@
|
|||
if (!inserted) {
|
||||
try {
|
||||
document.execCommand("ms-beginUndoUnit");
|
||||
} catch (undefined) {}
|
||||
} catch (error) {}
|
||||
textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
|
||||
try {
|
||||
document.execCommand("ms-endUndoUnit");
|
||||
} catch (undefined) {}
|
||||
} catch (error) {}
|
||||
}
|
||||
return this.moveCursor(textArea, tag, wrap);
|
||||
};
|
||||
|
@ -104,9 +105,12 @@
|
|||
return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
|
||||
});
|
||||
};
|
||||
return gl.text.removeListeners = function(form) {
|
||||
gl.text.removeListeners = function(form) {
|
||||
return $('.js-md', form).off();
|
||||
};
|
||||
return gl.text.truncate = function(string, maxLength) {
|
||||
return string.substr(0, (maxLength - 3)) + '...';
|
||||
};
|
||||
})(window);
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
if ((base = w.gl).utils == null) {
|
||||
base.utils = {};
|
||||
}
|
||||
// Returns an array containing the value(s) of the
|
||||
// of the key passed as an argument
|
||||
w.gl.utils.getParameterValues = function(sParam) {
|
||||
var i, sPageURL, sParameterName, sURLVariables, values;
|
||||
sPageURL = decodeURIComponent(window.location.search.substring(1));
|
||||
|
@ -23,6 +25,8 @@
|
|||
}
|
||||
return values;
|
||||
};
|
||||
// @param {Object} params - url keys and value to merge
|
||||
// @param {String} url
|
||||
w.gl.utils.mergeUrlParams = function(params, url) {
|
||||
var lastChar, newUrl, paramName, paramValue, pattern;
|
||||
newUrl = decodeURIComponent(url);
|
||||
|
@ -37,12 +41,14 @@
|
|||
newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
|
||||
}
|
||||
}
|
||||
// Remove a trailing ampersand
|
||||
lastChar = newUrl[newUrl.length - 1];
|
||||
if (lastChar === '&') {
|
||||
newUrl = newUrl.slice(0, -1);
|
||||
}
|
||||
return newUrl;
|
||||
};
|
||||
// removes parameter query string from url. returns the modified url
|
||||
w.gl.utils.removeParamQueryString = function(url, param) {
|
||||
var urlVariables, variables;
|
||||
url = decodeURIComponent(url);
|
||||
|
|
|
@ -1,17 +1,49 @@
|
|||
|
||||
// LineHighlighter
|
||||
//
|
||||
// Handles single- and multi-line selection and highlight for blob views.
|
||||
//
|
||||
/*= require jquery.scrollTo */
|
||||
|
||||
//
|
||||
// ### Example Markup
|
||||
//
|
||||
// <div id="blob-content-holder">
|
||||
// <div class="file-content">
|
||||
// <div class="line-numbers">
|
||||
// <a href="#L1" id="L1" data-line-number="1">1</a>
|
||||
// <a href="#L2" id="L2" data-line-number="2">2</a>
|
||||
// <a href="#L3" id="L3" data-line-number="3">3</a>
|
||||
// <a href="#L4" id="L4" data-line-number="4">4</a>
|
||||
// <a href="#L5" id="L5" data-line-number="5">5</a>
|
||||
// </div>
|
||||
// <pre class="code highlight">
|
||||
// <code>
|
||||
// <span id="LC1" class="line">...</span>
|
||||
// <span id="LC2" class="line">...</span>
|
||||
// <span id="LC3" class="line">...</span>
|
||||
// <span id="LC4" class="line">...</span>
|
||||
// <span id="LC5" class="line">...</span>
|
||||
// </code>
|
||||
// </pre>
|
||||
// </div>
|
||||
// </div>
|
||||
//
|
||||
(function() {
|
||||
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
this.LineHighlighter = (function() {
|
||||
// CSS class applied to highlighted lines
|
||||
LineHighlighter.prototype.highlightClass = 'hll';
|
||||
|
||||
// Internal copy of location.hash so we're not dependent on `location` in tests
|
||||
LineHighlighter.prototype._hash = '';
|
||||
|
||||
function LineHighlighter(hash) {
|
||||
var range;
|
||||
if (hash == null) {
|
||||
// Initialize a LineHighlighter object
|
||||
//
|
||||
// hash - String URL hash for dependency injection in tests
|
||||
hash = location.hash;
|
||||
}
|
||||
this.setHash = bind(this.setHash, this);
|
||||
|
@ -24,6 +56,8 @@
|
|||
if (range[0]) {
|
||||
this.highlightRange(range);
|
||||
$.scrollTo("#L" + range[0], {
|
||||
// Scroll to the first highlighted line on initial load
|
||||
// Offset -50 for the sticky top bar, and another -100 for some context
|
||||
offset: -150
|
||||
});
|
||||
}
|
||||
|
@ -32,6 +66,12 @@
|
|||
|
||||
LineHighlighter.prototype.bindEvents = function() {
|
||||
$('#blob-content-holder').on('mousedown', 'a[data-line-number]', this.clickHandler);
|
||||
// While it may seem odd to bind to the mousedown event and then throw away
|
||||
// the click event, there is a method to our madness.
|
||||
//
|
||||
// If not done this way, the line number anchor will sometimes keep its
|
||||
// active state even when the event is cancelled, resulting in an ugly border
|
||||
// around the link and/or a persisted underline text decoration.
|
||||
return $('#blob-content-holder').on('click', 'a[data-line-number]', function(event) {
|
||||
return event.preventDefault();
|
||||
});
|
||||
|
@ -44,6 +84,8 @@
|
|||
lineNumber = $(event.target).closest('a').data('line-number');
|
||||
current = this.hashToRange(this._hash);
|
||||
if (!(current[0] && event.shiftKey)) {
|
||||
// If there's no current selection, or there is but Shift wasn't held,
|
||||
// treat this like a single-line selection.
|
||||
this.setHash(lineNumber);
|
||||
return this.highlightLine(lineNumber);
|
||||
} else if (event.shiftKey) {
|
||||
|
@ -59,10 +101,23 @@
|
|||
|
||||
LineHighlighter.prototype.clearHighlight = function() {
|
||||
return $("." + this.highlightClass).removeClass(this.highlightClass);
|
||||
// Unhighlight previously highlighted lines
|
||||
};
|
||||
|
||||
// Convert a URL hash String into line numbers
|
||||
//
|
||||
// hash - Hash String
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// hashToRange('#L5') # => [5, null]
|
||||
// hashToRange('#L5-15') # => [5, 15]
|
||||
// hashToRange('#foo') # => [null, null]
|
||||
//
|
||||
// Returns an Array
|
||||
LineHighlighter.prototype.hashToRange = function(hash) {
|
||||
var first, last, matches;
|
||||
//?L(\d+)(?:-(\d+))?$/)
|
||||
matches = hash.match(/^#?L(\d+)(?:-(\d+))?$/);
|
||||
if (matches && matches.length) {
|
||||
first = parseInt(matches[1]);
|
||||
|
@ -73,10 +128,16 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Highlight a single line
|
||||
//
|
||||
// lineNumber - Line number to highlight
|
||||
LineHighlighter.prototype.highlightLine = function(lineNumber) {
|
||||
return $("#LC" + lineNumber).addClass(this.highlightClass);
|
||||
};
|
||||
|
||||
// Highlight all lines within a range
|
||||
//
|
||||
// range - Array containing the starting and ending line numbers
|
||||
LineHighlighter.prototype.highlightRange = function(range) {
|
||||
var i, lineNumber, ref, ref1, results;
|
||||
if (range[1]) {
|
||||
|
@ -90,6 +151,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Set the URL hash string
|
||||
LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
|
||||
var hash;
|
||||
if (lastLineNumber) {
|
||||
|
@ -101,10 +163,15 @@
|
|||
return this.__setLocationHash__(hash);
|
||||
};
|
||||
|
||||
// Make the actual hash change in the browser
|
||||
//
|
||||
// This method is stubbed in tests.
|
||||
LineHighlighter.prototype.__setLocationHash__ = function(value) {
|
||||
return history.pushState({
|
||||
turbolinks: false,
|
||||
url: value
|
||||
// We're using pushState instead of assigning location.hash directly to
|
||||
// prevent the page from scrolling on the hashchange event
|
||||
}, document.title, value);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,54 +1,12 @@
|
|||
(function() {
|
||||
var clearHighlights, currentTimer, defaultClass, delay, firstPiece, pieceIndex, pieces, start, stop, work;
|
||||
|
||||
Turbolinks.enableProgressBar();
|
||||
|
||||
defaultClass = 'tanuki-shape';
|
||||
$(document).on('page:fetch', function() {
|
||||
$('.tanuki-logo').addClass('animate');
|
||||
});
|
||||
|
||||
pieces = ['path#tanuki-right-cheek', 'path#tanuki-right-eye, path#tanuki-right-ear', 'path#tanuki-nose', 'path#tanuki-left-eye, path#tanuki-left-ear', 'path#tanuki-left-cheek'];
|
||||
|
||||
pieceIndex = 0;
|
||||
|
||||
firstPiece = pieces[0];
|
||||
|
||||
currentTimer = null;
|
||||
|
||||
delay = 150;
|
||||
|
||||
clearHighlights = function() {
|
||||
return $("." + defaultClass + ".highlight").attr('class', defaultClass);
|
||||
};
|
||||
|
||||
start = function() {
|
||||
clearHighlights();
|
||||
pieceIndex = 0;
|
||||
if (pieces[0] !== firstPiece) {
|
||||
pieces.reverse();
|
||||
}
|
||||
if (currentTimer) {
|
||||
clearInterval(currentTimer);
|
||||
}
|
||||
return currentTimer = setInterval(work, delay);
|
||||
};
|
||||
|
||||
stop = function() {
|
||||
clearInterval(currentTimer);
|
||||
return clearHighlights();
|
||||
};
|
||||
|
||||
work = function() {
|
||||
clearHighlights();
|
||||
$(pieces[pieceIndex]).attr('class', defaultClass + " highlight");
|
||||
if (pieceIndex === pieces.length - 1) {
|
||||
pieceIndex = 0;
|
||||
return pieces.reverse();
|
||||
} else {
|
||||
return pieceIndex++;
|
||||
}
|
||||
};
|
||||
|
||||
$(document).on('page:fetch', start);
|
||||
|
||||
$(document).on('page:change', stop);
|
||||
$(document).on('page:change', function() {
|
||||
$('.tanuki-logo').removeClass('animate');
|
||||
});
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -75,10 +75,8 @@ class MergeConflictResolver {
|
|||
window.location.href = data.redirect_to;
|
||||
})
|
||||
.error(() => {
|
||||
new Flash('Something went wrong!');
|
||||
})
|
||||
.always(() => {
|
||||
this.vue.isSubmitting = false;
|
||||
new Flash('Something went wrong!');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
|
||||
/*= require jquery.waitforimages */
|
||||
|
||||
|
||||
/*= require task_list */
|
||||
|
||||
|
||||
/*= require merge_request_tabs */
|
||||
|
||||
(function() {
|
||||
|
@ -12,6 +8,11 @@
|
|||
|
||||
this.MergeRequest = (function() {
|
||||
function MergeRequest(opts) {
|
||||
// Initialize MergeRequest behavior
|
||||
//
|
||||
// Options:
|
||||
// action - String, current controller action
|
||||
//
|
||||
this.opts = opts != null ? opts : {};
|
||||
this.submitNoteForm = bind(this.submitNoteForm, this);
|
||||
this.$el = $('.merge-request');
|
||||
|
@ -21,6 +22,7 @@
|
|||
};
|
||||
})(this));
|
||||
this.initTabs();
|
||||
// Prevent duplicate event bindings
|
||||
this.disableTaskList();
|
||||
this.initMRBtnListeners();
|
||||
if ($("a.btn-close").length) {
|
||||
|
@ -28,14 +30,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Local jQuery finder
|
||||
MergeRequest.prototype.$ = function(selector) {
|
||||
return this.$el.find(selector);
|
||||
};
|
||||
|
||||
MergeRequest.prototype.initTabs = function() {
|
||||
if (this.opts.action !== 'new') {
|
||||
// `MergeRequests#new` has no tab-persisting or lazy-loading behavior
|
||||
window.mrTabs = new MergeRequestTabs(this.opts);
|
||||
} else {
|
||||
// Show the first tab (Commits)
|
||||
return $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show');
|
||||
}
|
||||
};
|
||||
|
@ -96,6 +101,8 @@
|
|||
url: $('form.js-issuable-update').attr('action'),
|
||||
data: patchData
|
||||
});
|
||||
// TODO (rspeicher): Make the merge request description inline-editable like a
|
||||
// note so that we can re-use its form here
|
||||
};
|
||||
|
||||
return MergeRequest;
|
||||
|
|
|
@ -1,6 +1,49 @@
|
|||
|
||||
// MergeRequestTabs
|
||||
//
|
||||
// Handles persisting and restoring the current tab selection and lazily-loading
|
||||
// content on the MergeRequests#show page.
|
||||
//
|
||||
/*= require jquery.cookie */
|
||||
|
||||
//
|
||||
// ### Example Markup
|
||||
//
|
||||
// <ul class="nav-links merge-request-tabs">
|
||||
// <li class="notes-tab active">
|
||||
// <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
|
||||
// Discussion
|
||||
// </a>
|
||||
// </li>
|
||||
// <li class="commits-tab">
|
||||
// <a data-action="commits" data-target="#commits" data-toggle="tab" href="/foo/bar/merge_requests/1/commits">
|
||||
// Commits
|
||||
// </a>
|
||||
// </li>
|
||||
// <li class="diffs-tab">
|
||||
// <a data-action="diffs" data-target="#diffs" data-toggle="tab" href="/foo/bar/merge_requests/1/diffs">
|
||||
// Diffs
|
||||
// </a>
|
||||
// </li>
|
||||
// </ul>
|
||||
//
|
||||
// <div class="tab-content">
|
||||
// <div class="notes tab-pane active" id="notes">
|
||||
// Notes Content
|
||||
// </div>
|
||||
// <div class="commits tab-pane" id="commits">
|
||||
// Commits Content
|
||||
// </div>
|
||||
// <div class="diffs tab-pane" id="diffs">
|
||||
// Diffs Content
|
||||
// </div>
|
||||
// </div>
|
||||
//
|
||||
// <div class="mr-loading-status">
|
||||
// <div class="loading">
|
||||
// Loading Animation
|
||||
// </div>
|
||||
// </div>
|
||||
//
|
||||
(function() {
|
||||
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
||||
|
||||
|
@ -19,6 +62,7 @@
|
|||
this.setCurrentAction = bind(this.setCurrentAction, this);
|
||||
this.tabShown = bind(this.tabShown, this);
|
||||
this.showTab = bind(this.showTab, this);
|
||||
// Store the `location` object, allowing for easier stubbing in tests
|
||||
this._location = location;
|
||||
this.bindEvents();
|
||||
this.activateTab(this.opts.action);
|
||||
|
@ -77,6 +121,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Activate a tab based on the current action
|
||||
MergeRequestTabs.prototype.activateTab = function(action) {
|
||||
if (action === 'show') {
|
||||
action = 'notes';
|
||||
|
@ -84,20 +129,48 @@
|
|||
return $(".merge-request-tabs a[data-action='" + action + "']").tab('show');
|
||||
};
|
||||
|
||||
// Replaces the current Merge Request-specific action in the URL with a new one
|
||||
//
|
||||
// If the action is "notes", the URL is reset to the standard
|
||||
// `MergeRequests#show` route.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// location.pathname # => "/namespace/project/merge_requests/1"
|
||||
// setCurrentAction('diffs')
|
||||
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
||||
//
|
||||
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
||||
// setCurrentAction('notes')
|
||||
// location.pathname # => "/namespace/project/merge_requests/1"
|
||||
//
|
||||
// location.pathname # => "/namespace/project/merge_requests/1/diffs"
|
||||
// setCurrentAction('commits')
|
||||
// location.pathname # => "/namespace/project/merge_requests/1/commits"
|
||||
//
|
||||
// Returns the new URL String
|
||||
MergeRequestTabs.prototype.setCurrentAction = function(action) {
|
||||
var new_state;
|
||||
// Normalize action, just to be safe
|
||||
if (action === 'show') {
|
||||
action = 'notes';
|
||||
}
|
||||
this.currentAction = action;
|
||||
// Remove a trailing '/commits' or '/diffs'
|
||||
new_state = this._location.pathname.replace(/\/(commits|diffs|builds|pipelines)(\.html)?\/?$/, '');
|
||||
// Append the new action if we're on a tab other than 'notes'
|
||||
if (action !== 'notes') {
|
||||
new_state += "/" + action;
|
||||
}
|
||||
// Ensure parameters and hash come along for the ride
|
||||
new_state += this._location.search + this._location.hash;
|
||||
history.replaceState({
|
||||
turbolinks: true,
|
||||
url: new_state
|
||||
// Replace the current history state with the new one without breaking
|
||||
// Turbolinks' history.
|
||||
//
|
||||
// See https://github.com/rails/turbolinks/issues/363
|
||||
}, document.title, new_state);
|
||||
return new_state;
|
||||
};
|
||||
|
@ -159,10 +232,10 @@
|
|||
$('.hll').removeClass('hll');
|
||||
locationHash = window.location.hash;
|
||||
if (locationHash !== '') {
|
||||
hashClassString = "." + (locationHash.replace('#', ''));
|
||||
dataLineString = '[data-line-code="' + locationHash.replace('#', '') + '"]';
|
||||
$diffLine = $(locationHash + ":not(.match)", $('#diffs'));
|
||||
if (!$diffLine.is('tr')) {
|
||||
$diffLine = $('#diffs').find("td" + locationHash + ", td" + hashClassString);
|
||||
$diffLine = $('#diffs').find("td" + locationHash + ", td" + dataLineString);
|
||||
} else {
|
||||
$diffLine = $diffLine.find('td');
|
||||
}
|
||||
|
@ -206,6 +279,9 @@
|
|||
});
|
||||
};
|
||||
|
||||
// Show or hide the loading spinner
|
||||
//
|
||||
// status - Boolean, true to show, false to hide
|
||||
MergeRequestTabs.prototype.toggleLoading = function(status) {
|
||||
return $('.mr-loading-status .loading').toggle(status);
|
||||
};
|
||||
|
@ -232,6 +308,7 @@
|
|||
|
||||
MergeRequestTabs.prototype.diffViewType = function() {
|
||||
return $('.inline-parallel-buttons a.active').data('view-type');
|
||||
// Returns diff view type
|
||||
};
|
||||
|
||||
MergeRequestTabs.prototype.expandViewContainer = function() {
|
||||
|
@ -245,6 +322,8 @@
|
|||
if ($gutterIcon.is('.fa-angle-double-right')) {
|
||||
return $gutterIcon.closest('a').trigger('click', [true]);
|
||||
}
|
||||
// Wait until listeners are set
|
||||
// Only when sidebar is expanded
|
||||
}, 0);
|
||||
};
|
||||
|
||||
|
@ -259,6 +338,9 @@
|
|||
return $gutterIcon.closest('a').trigger('click', [true]);
|
||||
}
|
||||
}, 0);
|
||||
// Expand the issuable sidebar unless the user explicitly collapsed it
|
||||
// Wait until listeners are set
|
||||
// Only when sidebar is collapsed
|
||||
};
|
||||
|
||||
return MergeRequestTabs;
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
this.MergeRequestWidget = (function() {
|
||||
function MergeRequestWidget(opts) {
|
||||
// Initialize MergeRequestWidget behavior
|
||||
//
|
||||
// check_enable - Boolean, whether to check automerge status
|
||||
// merge_check_url - String, URL to use to check automerge status
|
||||
// ci_status_url - String, URL to use to check CI status
|
||||
//
|
||||
this.opts = opts;
|
||||
$('#modal_merge_info').modal({
|
||||
show: false
|
||||
|
@ -118,6 +124,8 @@
|
|||
if (data.coverage) {
|
||||
_this.showCICoverage(data.coverage);
|
||||
}
|
||||
// The first check should only update the UI, a notification
|
||||
// should only be displayed on status changes
|
||||
if (showNotification && !_this.firstCICheck) {
|
||||
status = _this.ciLabelForStatus(data.status);
|
||||
if (status === "preparing") {
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
},
|
||||
update: function(event, ui) {
|
||||
var data;
|
||||
// Prevents sorting from container which element has been removed.
|
||||
if ($(this).find(ui.item).length > 0) {
|
||||
data = $(this).sortable("serialize");
|
||||
return Milestone.sortIssues(data);
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
},
|
||||
hidden: function() {
|
||||
$selectbox.hide();
|
||||
// display:block overrides the hide-collapse rule
|
||||
return $value.css('display', '');
|
||||
},
|
||||
clicked: function(selected, $el, e) {
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
results = [];
|
||||
while (k < this.mspace) {
|
||||
this.colors.push(Raphael.getColor(.8));
|
||||
// Skipping a few colors in the spectrum to get more contrast between colors
|
||||
Raphael.getColor();
|
||||
Raphael.getColor();
|
||||
results.push(k++);
|
||||
|
@ -112,6 +113,7 @@
|
|||
for (mm = j = 0, len = ref.length; j < len; mm = ++j) {
|
||||
day = ref[mm];
|
||||
if (cuday !== day[0] || cumonth !== day[1]) {
|
||||
// Dates
|
||||
r.text(55, this.offsetY + this.unitTime * mm, day[0]).attr({
|
||||
font: "12px Monaco, monospace",
|
||||
fill: "#BBB"
|
||||
|
@ -119,6 +121,7 @@
|
|||
cuday = day[0];
|
||||
}
|
||||
if (cumonth !== day[1]) {
|
||||
// Months
|
||||
r.text(20, this.offsetY + this.unitTime * mm, day[1]).attr({
|
||||
font: "12px Monaco, monospace",
|
||||
fill: "#EEE"
|
||||
|
@ -207,6 +210,7 @@
|
|||
}
|
||||
r = this.r;
|
||||
shortrefs = commit.refs;
|
||||
// Truncate if longer than 15 chars
|
||||
if (shortrefs.length > 17) {
|
||||
shortrefs = shortrefs.substr(0, 15) + "…";
|
||||
}
|
||||
|
@ -217,6 +221,7 @@
|
|||
title: commit.refs
|
||||
});
|
||||
textbox = text.getBBox();
|
||||
// Create rectangle based on the size of the textbox
|
||||
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr({
|
||||
fill: "#000",
|
||||
"fill-opacity": .5,
|
||||
|
@ -229,6 +234,7 @@
|
|||
});
|
||||
label = r.set(rect, text);
|
||||
label.transform(["t", -rect.getBBox().width - 15, 0]);
|
||||
// Set text to front
|
||||
return text.toFront();
|
||||
};
|
||||
|
||||
|
@ -283,11 +289,13 @@
|
|||
parentY = this.offsetY + this.unitTime * parentCommit.time;
|
||||
parentX1 = this.offsetX + this.unitSpace * (this.mspace - parentCommit.space);
|
||||
parentX2 = this.offsetX + this.unitSpace * (this.mspace - parent[1]);
|
||||
// Set line color
|
||||
if (parentCommit.space <= commit.space) {
|
||||
color = this.colors[commit.space];
|
||||
} else {
|
||||
color = this.colors[parentCommit.space];
|
||||
}
|
||||
// Build line shape
|
||||
if (parent[1] === commit.space) {
|
||||
offset = [0, 5];
|
||||
arrow = "l-2,5,4,0,-2,-5,0,5";
|
||||
|
@ -298,13 +306,17 @@
|
|||
offset = [-3, 3];
|
||||
arrow = "l-5,0,2,4,3,-4,-4,2";
|
||||
}
|
||||
// Start point
|
||||
route = ["M", x + offset[0], y + offset[1]];
|
||||
// Add arrow if not first parent
|
||||
if (i > 0) {
|
||||
route.push(arrow);
|
||||
}
|
||||
// Circumvent if overlap
|
||||
if (commit.space !== parentCommit.space || commit.space !== parent[1]) {
|
||||
route.push("L", parentX2, y + 10, "L", parentX2, parentY - 5);
|
||||
}
|
||||
// End point
|
||||
route.push("L", parentX1, parentY);
|
||||
results.push(r.path(route).attr({
|
||||
stroke: color,
|
||||
|
@ -325,6 +337,7 @@
|
|||
"fill-opacity": .5,
|
||||
stroke: "none"
|
||||
});
|
||||
// Displayed in the center
|
||||
return this.element.scrollTop(y - this.graphHeight / 2);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
|
||||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
/*= require_tree . */
|
||||
|
||||
(function() {
|
||||
|
|
|
@ -1,22 +1,10 @@
|
|||
|
||||
/*= require autosave */
|
||||
|
||||
|
||||
/*= require autosize */
|
||||
|
||||
|
||||
/*= require dropzone */
|
||||
|
||||
|
||||
/*= require dropzone_input */
|
||||
|
||||
|
||||
/*= require gfm_auto_complete */
|
||||
|
||||
|
||||
/*= require jquery.atwho */
|
||||
|
||||
|
||||
/*= require task_list */
|
||||
|
||||
(function() {
|
||||
|
@ -60,26 +48,43 @@
|
|||
}
|
||||
|
||||
Notes.prototype.addBinding = function() {
|
||||
// add note to UI after creation
|
||||
$(document).on("ajax:success", ".js-main-target-form", this.addNote);
|
||||
$(document).on("ajax:success", ".js-discussion-note-form", this.addDiscussionNote);
|
||||
// catch note ajax errors
|
||||
$(document).on("ajax:error", ".js-main-target-form", this.addNoteError);
|
||||
// change note in UI after update
|
||||
$(document).on("ajax:success", "form.edit-note", this.updateNote);
|
||||
// Edit note link
|
||||
$(document).on("click", ".js-note-edit", this.showEditForm);
|
||||
$(document).on("click", ".note-edit-cancel", this.cancelEdit);
|
||||
// Reopen and close actions for Issue/MR combined with note form submit
|
||||
$(document).on("click", ".js-comment-button", this.updateCloseButton);
|
||||
$(document).on("keyup input", ".js-note-text", this.updateTargetButtons);
|
||||
// resolve a discussion
|
||||
$(document).on('click', '.js-comment-resolve-button', this.resolveDiscussion);
|
||||
// remove a note (in general)
|
||||
$(document).on("click", ".js-note-delete", this.removeNote);
|
||||
// delete note attachment
|
||||
$(document).on("click", ".js-note-attachment-delete", this.removeAttachment);
|
||||
// reset main target form after submit
|
||||
$(document).on("ajax:complete", ".js-main-target-form", this.reenableTargetFormSubmitButton);
|
||||
$(document).on("ajax:success", ".js-main-target-form", this.resetMainTargetForm);
|
||||
// reset main target form when clicking discard
|
||||
$(document).on("click", ".js-note-discard", this.resetMainTargetForm);
|
||||
// update the file name when an attachment is selected
|
||||
$(document).on("change", ".js-note-attachment-input", this.updateFormAttachment);
|
||||
// reply to diff/discussion notes
|
||||
$(document).on("click", ".js-discussion-reply-button", this.replyToDiscussionNote);
|
||||
// add diff note
|
||||
$(document).on("click", ".js-add-diff-note-button", this.addDiffNote);
|
||||
// hide diff note form
|
||||
$(document).on("click", ".js-close-discussion-note-form", this.cancelDiscussionForm);
|
||||
// fetch notes when tab becomes visible
|
||||
$(document).on("visibilitychange", this.visibilityChange);
|
||||
// when issue status changes, we need to refresh data
|
||||
$(document).on("issuable:change", this.refresh);
|
||||
// when a key is clicked on the notes
|
||||
return $(document).on("keydown", ".js-note-text", this.keydownNoteText);
|
||||
};
|
||||
|
||||
|
@ -112,6 +117,7 @@
|
|||
return;
|
||||
}
|
||||
$textarea = $(e.target);
|
||||
// Edit previous note when UP arrow is hit
|
||||
switch (e.which) {
|
||||
case 38:
|
||||
if ($textarea.val() !== '') {
|
||||
|
@ -123,6 +129,7 @@
|
|||
return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
|
||||
}
|
||||
break;
|
||||
// Cancel creating diff note or editing any note when ESCAPE is hit
|
||||
case 27:
|
||||
discussionNoteForm = $textarea.closest('.js-discussion-note-form');
|
||||
if (discussionNoteForm.length) {
|
||||
|
@ -247,10 +254,13 @@
|
|||
votesBlock = $('.js-awards-block').eq(0);
|
||||
gl.awardsHandler.addAwardToEmojiBar(votesBlock, note.name);
|
||||
return gl.awardsHandler.scrollToAwards();
|
||||
// render note if it not present in loaded list
|
||||
// or skip if rendered
|
||||
} else if (this.isNewNote(note)) {
|
||||
this.note_ids.push(note.id);
|
||||
$notesList = $('ul.main-notes-list');
|
||||
$notesList.append(note.html).syntaxHighlight();
|
||||
// Update datetime format on the recent note
|
||||
gl.utils.localTimeAgo($notesList.find("#note_" + note.id + " .js-timeago"), false);
|
||||
this.initTaskList();
|
||||
this.refresh();
|
||||
|
@ -291,19 +301,26 @@
|
|||
row = form.closest("tr");
|
||||
note_html = $(note.html);
|
||||
note_html.syntaxHighlight();
|
||||
// is this the first note of discussion?
|
||||
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
|
||||
if ((note.original_discussion_id != null) && discussionContainer.length === 0) {
|
||||
discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']");
|
||||
}
|
||||
if (discussionContainer.length === 0) {
|
||||
// insert the note and the reply button after the temp row
|
||||
row.after(note.diff_discussion_html);
|
||||
// remove the note (will be added again below)
|
||||
row.next().find(".note").remove();
|
||||
// Before that, the container didn't exist
|
||||
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
|
||||
// Add note to 'Changes' page discussions
|
||||
discussionContainer.append(note_html);
|
||||
// Init discussion on 'Discussion' page if it is merge request page
|
||||
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0) {
|
||||
$('ul.main-notes-list').append(note.discussion_html).syntaxHighlight();
|
||||
}
|
||||
} else {
|
||||
// append new note to all matching discussions
|
||||
discussionContainer.append(note_html);
|
||||
}
|
||||
|
||||
|
@ -327,11 +344,18 @@
|
|||
Notes.prototype.resetMainTargetForm = function(e) {
|
||||
var form;
|
||||
form = $(".js-main-target-form");
|
||||
// remove validation errors
|
||||
form.find(".js-errors").remove();
|
||||
// reset text and preview
|
||||
form.find(".js-md-write-button").click();
|
||||
form.find(".js-note-text").val("").trigger("input");
|
||||
form.find(".js-note-text").data("autosave").reset();
|
||||
return this.updateTargetButtons(e);
|
||||
|
||||
var event = document.createEvent('Event');
|
||||
event.initEvent('autosize:update', true, false);
|
||||
form.find('.js-autosize')[0].dispatchEvent(event);
|
||||
|
||||
this.updateTargetButtons(e);
|
||||
};
|
||||
|
||||
Notes.prototype.reenableTargetFormSubmitButton = function() {
|
||||
|
@ -349,9 +373,13 @@
|
|||
|
||||
Notes.prototype.setupMainTargetNoteForm = function() {
|
||||
var form;
|
||||
// find the form
|
||||
form = $(".js-new-note-form");
|
||||
// Set a global clone of the form for later cloning
|
||||
this.formClone = form.clone();
|
||||
// show the form
|
||||
this.setupNoteForm(form);
|
||||
// fix classes
|
||||
form.removeClass("js-new-note-form");
|
||||
form.addClass("js-main-target-form");
|
||||
form.find("#note_line_code").remove();
|
||||
|
@ -416,6 +444,7 @@
|
|||
}
|
||||
|
||||
this.renderDiscussionNote(note);
|
||||
// cleanup after successfully creating a diff/discussion note
|
||||
this.removeDiscussionNoteForm($form);
|
||||
};
|
||||
|
||||
|
@ -428,10 +457,12 @@
|
|||
|
||||
Notes.prototype.updateNote = function(_xhr, note, _status) {
|
||||
var $html, $note_li;
|
||||
// Convert returned HTML to a jQuery object so we can modify it further
|
||||
$html = $(note.html);
|
||||
gl.utils.localTimeAgo($('.js-timeago', $html));
|
||||
$html.syntaxHighlight();
|
||||
$html.find('.js-task-list-container').taskList('enable');
|
||||
// Find the note's `li` element by ID and replace it with the updated HTML
|
||||
$note_li = $('.note-row-' + note.id);
|
||||
|
||||
$note_li.replaceWith($html);
|
||||
|
@ -456,15 +487,20 @@
|
|||
note.addClass("is-editting");
|
||||
form = note.find(".note-edit-form");
|
||||
form.addClass('current-note-edit-form');
|
||||
// Show the attachment delete link
|
||||
note.find(".js-note-attachment-delete").show();
|
||||
done = function($noteText) {
|
||||
var noteTextVal;
|
||||
// Neat little trick to put the cursor at the end
|
||||
noteTextVal = $noteText.val();
|
||||
// Store the original note text in a data attribute to retrieve if a user cancels edit.
|
||||
form.find('form.edit-note').data('original-note', noteTextVal);
|
||||
return $noteText.val('').val(noteTextVal);
|
||||
};
|
||||
new GLForm(form);
|
||||
if ((scrollTo != null) && (myLastNote != null)) {
|
||||
// scroll to the bottom
|
||||
// so the open of the last element doesn't make a jump
|
||||
$('html, body').scrollTop($(document).height());
|
||||
return $('html, body').animate({
|
||||
scrollTop: myLastNote.offset().top - 150
|
||||
|
@ -500,6 +536,7 @@
|
|||
form = note.find(".current-note-edit-form");
|
||||
note.removeClass("is-editting");
|
||||
form.removeClass("current-note-edit-form");
|
||||
// Replace markdown textarea text with original note text.
|
||||
return form.find(".js-note-text").val(form.find('form.edit-note').data('original-note'));
|
||||
};
|
||||
|
||||
|
@ -515,6 +552,9 @@
|
|||
var noteId;
|
||||
noteId = $(e.currentTarget).closest(".note").attr("id");
|
||||
$(".note[id='" + noteId + "']").each((function(_this) {
|
||||
// A same note appears in the "Discussion" and in the "Changes" tab, we have
|
||||
// to remove all. Using $(".note[id='noteId']") ensure we get all the notes,
|
||||
// where $("#noteId") would return only one.
|
||||
return function(i, el) {
|
||||
var note, notes;
|
||||
note = $(el);
|
||||
|
@ -528,13 +568,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
// check if this is the last note for this line
|
||||
if (notes.find(".note").length === 1) {
|
||||
// "Discussions" tab
|
||||
notes.closest(".timeline-entry").remove();
|
||||
// "Changes" tab / commit view
|
||||
notes.closest("tr").remove();
|
||||
}
|
||||
return note.remove();
|
||||
};
|
||||
})(this));
|
||||
// Decrement the "Discussions" counter only once
|
||||
return this.updateNotesCount(-1);
|
||||
};
|
||||
|
||||
|
@ -566,10 +610,12 @@
|
|||
var form, replyLink;
|
||||
form = this.formClone.clone();
|
||||
replyLink = $(e.target).closest(".js-discussion-reply-button");
|
||||
// insert the form after the button
|
||||
replyLink
|
||||
.closest('.discussion-reply-holder')
|
||||
.hide()
|
||||
.after(form);
|
||||
// show the form
|
||||
return this.setupDiscussionNoteForm(replyLink, form);
|
||||
};
|
||||
|
||||
|
@ -584,6 +630,7 @@
|
|||
*/
|
||||
|
||||
Notes.prototype.setupDiscussionNoteForm = function(dataHolder, form) {
|
||||
// setup note target
|
||||
form.attr('id', "new-discussion-note-form-" + (dataHolder.data("discussionId")));
|
||||
form.attr("data-line-code", dataHolder.data("lineCode"));
|
||||
form.find("#note_type").val(dataHolder.data("noteType"));
|
||||
|
@ -631,6 +678,7 @@
|
|||
addForm = false;
|
||||
notesContentSelector = ".notes_content";
|
||||
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"><div class=\"content\"></div></td></tr>";
|
||||
// In parallel view, look inside the correct left/right pane
|
||||
if (this.isParallelView()) {
|
||||
lineType = $link.data("lineType");
|
||||
notesContentSelector += "." + lineType;
|
||||
|
@ -647,6 +695,7 @@
|
|||
e.target = replyButton[0];
|
||||
$.proxy(this.replyToDiscussionNote, replyButton[0], e).call();
|
||||
} else {
|
||||
// In parallel view, the form may not be present in one of the panes
|
||||
noteForm = notesContent.find(".js-discussion-note-form");
|
||||
if (noteForm.length === 0) {
|
||||
addForm = true;
|
||||
|
@ -654,6 +703,7 @@
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// add a notes row and insert the form
|
||||
row.after(rowCssToAdd);
|
||||
nextRow = row.next();
|
||||
notesContent = nextRow.find(notesContentSelector);
|
||||
|
@ -662,6 +712,7 @@
|
|||
if (addForm) {
|
||||
newForm = this.formClone.clone();
|
||||
newForm.appendTo(notesContent);
|
||||
// show the form
|
||||
return this.setupDiscussionNoteForm($link, newForm);
|
||||
}
|
||||
};
|
||||
|
@ -680,12 +731,15 @@
|
|||
glForm = form.data('gl-form');
|
||||
glForm.destroy();
|
||||
form.find(".js-note-text").data("autosave").reset();
|
||||
// show the reply button (will only work for replies)
|
||||
form
|
||||
.prev('.discussion-reply-holder')
|
||||
.show();
|
||||
if (row.is(".js-temp-notes-holder")) {
|
||||
// remove temporary row for diff lines
|
||||
return row.remove();
|
||||
} else {
|
||||
// only remove the form
|
||||
return form.remove();
|
||||
}
|
||||
};
|
||||
|
@ -707,6 +761,7 @@
|
|||
Notes.prototype.updateFormAttachment = function() {
|
||||
var filename, form;
|
||||
form = $(this).closest("form");
|
||||
// get only the basename
|
||||
filename = $(this).val().replace(/^.*[\\\/]/, "");
|
||||
return form.find(".js-attachment-filename").text(filename);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
// MarkdownPreview
|
||||
//
|
||||
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
|
||||
// and showing a warning when more than `x` users are referenced.
|
||||
//
|
||||
(function() {
|
||||
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector;
|
||||
|
||||
this.MarkdownPreview = (function() {
|
||||
function MarkdownPreview() {}
|
||||
|
||||
// Minimum number of users referenced before triggering a warning
|
||||
MarkdownPreview.prototype.referenceThreshold = 10;
|
||||
|
||||
MarkdownPreview.prototype.ajaxCache = {};
|
||||
|
@ -101,8 +107,10 @@
|
|||
return;
|
||||
}
|
||||
lastTextareaPreviewed = $form.find('textarea.markdown-area');
|
||||
// toggle tabs
|
||||
$form.find(writeButtonSelector).parent().removeClass('active');
|
||||
$form.find(previewButtonSelector).parent().addClass('active');
|
||||
// toggle content
|
||||
$form.find('.md-write-holder').hide();
|
||||
$form.find('.md-preview-holder').show();
|
||||
return markdownPreview.showPreview($form);
|
||||
|
@ -113,8 +121,10 @@
|
|||
return;
|
||||
}
|
||||
lastTextareaPreviewed = null;
|
||||
// toggle tabs
|
||||
$form.find(writeButtonSelector).parent().addClass('active');
|
||||
$form.find(previewButtonSelector).parent().removeClass('active');
|
||||
// toggle content
|
||||
$form.find('.md-write-holder').show();
|
||||
$form.find('textarea.markdown-area').focus();
|
||||
return $form.find('.md-preview-holder').hide();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
GitLabCrop = (function() {
|
||||
var FILENAMEREGEX;
|
||||
|
||||
// Matches everything but the file name
|
||||
FILENAMEREGEX = /^.*[\\\/]/;
|
||||
|
||||
function GitLabCrop(input, opts) {
|
||||
|
@ -17,11 +18,18 @@
|
|||
this.onModalShow = bind(this.onModalShow, this);
|
||||
this.onPickImageClick = bind(this.onPickImageClick, this);
|
||||
this.fileInput = $(input);
|
||||
// We should rename to avoid spec to fail
|
||||
// Form will submit the proper input filed with a file using FormData
|
||||
this.fileInput.attr('name', (this.fileInput.attr('name')) + "-trigger").attr('id', (this.fileInput.attr('id')) + "-trigger");
|
||||
// Set defaults
|
||||
this.exportWidth = (ref = opts.exportWidth) != null ? ref : 200, this.exportHeight = (ref1 = opts.exportHeight) != null ? ref1 : 200, this.cropBoxWidth = (ref2 = opts.cropBoxWidth) != null ? ref2 : 200, this.cropBoxHeight = (ref3 = opts.cropBoxHeight) != null ? ref3 : 200, this.form = (ref4 = opts.form) != null ? ref4 : this.fileInput.parents('form'), this.filename = opts.filename, this.previewImage = opts.previewImage, this.modalCrop = opts.modalCrop, this.pickImageEl = opts.pickImageEl, this.uploadImageBtn = opts.uploadImageBtn, this.modalCropImg = opts.modalCropImg;
|
||||
// Required params
|
||||
// Ensure needed elements are jquery objects
|
||||
// If selector is provided we will convert them to a jQuery Object
|
||||
this.filename = this.getElement(this.filename);
|
||||
this.previewImage = this.getElement(this.previewImage);
|
||||
this.pickImageEl = this.getElement(this.pickImageEl);
|
||||
// Modal elements usually are outside the @form element
|
||||
this.modalCrop = _.isString(this.modalCrop) ? $(this.modalCrop) : this.modalCrop;
|
||||
this.uploadImageBtn = _.isString(this.uploadImageBtn) ? $(this.uploadImageBtn) : this.uploadImageBtn;
|
||||
this.modalCropImg = _.isString(this.modalCropImg) ? $(this.modalCropImg) : this.modalCropImg;
|
||||
|
@ -93,8 +101,8 @@
|
|||
return this.modalCropImg.attr('src', '').cropper('destroy');
|
||||
};
|
||||
|
||||
GitLabCrop.prototype.onUploadImageBtnClick = function(e) {
|
||||
e.preventDefault();
|
||||
GitLabCrop.prototype.onUploadImageBtnClick = function(e) { // Remove attached image
|
||||
e.preventDefault(); // Destroy cropper instance
|
||||
this.setBlob();
|
||||
this.setPreview();
|
||||
this.modalCrop.modal('hide');
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
this.form = (ref = opts.form) != null ? ref : $('.edit-user');
|
||||
$('.js-preferences-form').on('change.preference', 'input[type=radio]', function() {
|
||||
return $(this).parents('form').submit();
|
||||
// Automatically submit the Preferences form when any of its radio buttons change
|
||||
});
|
||||
$('#user_notification_email').on('change', function() {
|
||||
return $(this).parents('form').submit();
|
||||
// Automatically submit email form when it changes
|
||||
});
|
||||
$('.update-username').on('ajax:before', function() {
|
||||
$('.loading-username').show();
|
||||
|
@ -76,6 +78,7 @@
|
|||
},
|
||||
complete: function() {
|
||||
window.scrollTo(0, 0);
|
||||
// Enable submit button after requests ends
|
||||
return self.form.find(':input[disabled]').enable();
|
||||
}
|
||||
});
|
||||
|
@ -93,6 +96,7 @@
|
|||
if (comment && comment.length > 1 && $title.val() === '') {
|
||||
return $title.val(comment[1]).change();
|
||||
}
|
||||
// Extract the SSH Key title from its comment
|
||||
});
|
||||
if (gl.utils.getPagePath() === 'profiles') {
|
||||
return new Profile();
|
||||
|
|
|
@ -3,5 +3,4 @@
|
|||
|
||||
(function() {
|
||||
|
||||
|
||||
}).call(this);
|
||||
|
|
|
@ -11,25 +11,27 @@
|
|||
url = $("#project_clone").val();
|
||||
$('#project_clone').val(url);
|
||||
return $('.clone').text(url);
|
||||
// Git protocol switcher
|
||||
// Remove the active class for all buttons (ssh, http, kerberos if shown)
|
||||
// Add the active class for the clicked button
|
||||
// Update the input field
|
||||
// Update the command line instructions
|
||||
});
|
||||
// Ref switcher
|
||||
this.initRefSwitcher();
|
||||
$('.project-refs-select').on('change', function() {
|
||||
return $(this).parents('form').submit();
|
||||
});
|
||||
$('.hide-no-ssh-message').on('click', function(e) {
|
||||
var path;
|
||||
path = '/';
|
||||
$.cookie('hide_no_ssh_message', 'false', {
|
||||
path: path
|
||||
path: gon.relative_url_root || '/'
|
||||
});
|
||||
$(this).parents('.no-ssh-key-message').remove();
|
||||
return e.preventDefault();
|
||||
});
|
||||
$('.hide-no-password-message').on('click', function(e) {
|
||||
var path;
|
||||
path = '/';
|
||||
$.cookie('hide_no_password_message', 'false', {
|
||||
path: path
|
||||
path: gon.relative_url_root || '/'
|
||||
});
|
||||
$(this).parents('.no-password-message').remove();
|
||||
return e.preventDefault();
|
||||
|
@ -65,7 +67,8 @@
|
|||
url: $dropdown.data('refs-url'),
|
||||
data: {
|
||||
ref: $dropdown.data('ref')
|
||||
}
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function(refs) {
|
||||
return callback(refs);
|
||||
});
|
||||
|
@ -73,7 +76,7 @@
|
|||
selectable: true,
|
||||
filterable: true,
|
||||
filterByText: true,
|
||||
fieldName: 'ref',
|
||||
fieldName: $dropdown.data('field-name'),
|
||||
renderRow: function(ref) {
|
||||
var link;
|
||||
if (ref.header != null) {
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
this.selectRowUp = bind(this.selectRowUp, this);
|
||||
this.filePaths = {};
|
||||
this.inputElement = this.element.find(".file-finder-input");
|
||||
// init event
|
||||
this.initEvent();
|
||||
// focus text input box
|
||||
this.inputElement.focus();
|
||||
// load file list
|
||||
this.load(this.options.url);
|
||||
}
|
||||
|
||||
|
@ -42,6 +45,7 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
// init event
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.findFile = function() {
|
||||
|
@ -49,8 +53,10 @@
|
|||
searchText = this.inputElement.val();
|
||||
result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
|
||||
return this.renderList(result, searchText);
|
||||
// find file
|
||||
};
|
||||
|
||||
// files pathes load
|
||||
ProjectFindFile.prototype.load = function(url) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
|
@ -67,6 +73,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
// render result
|
||||
ProjectFindFile.prototype.renderList = function(filePaths, searchText) {
|
||||
var blobItemUrl, filePath, html, i, j, len, matches, results;
|
||||
this.element.find(".tree-table > tbody").empty();
|
||||
|
@ -86,6 +93,7 @@
|
|||
return results;
|
||||
};
|
||||
|
||||
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
|
||||
highlighter = function(element, text, matches) {
|
||||
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
|
||||
lastIndex = 0;
|
||||
|
@ -110,6 +118,7 @@
|
|||
return element.append(document.createTextNode(text.substring(lastIndex)));
|
||||
};
|
||||
|
||||
// make tbody row html
|
||||
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
|
||||
var $tr;
|
||||
$tr = $("<tr class='tree-item'><td class='tree-item-file-name'><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'><a></a></span></td></tr>");
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
this.ProjectNew = (function() {
|
||||
function ProjectNew() {
|
||||
this.toggleSettings = bind(this.toggleSettings, this);
|
||||
this.$selects = $('.features select');
|
||||
|
||||
$('.project-edit-container').on('ajax:before', (function(_this) {
|
||||
return function() {
|
||||
$('.project-edit-container').hide();
|
||||
|
@ -15,18 +17,24 @@
|
|||
}
|
||||
|
||||
ProjectNew.prototype.toggleSettings = function() {
|
||||
this._showOrHide('#project_builds_enabled', '.builds-feature');
|
||||
return this._showOrHide('#project_merge_requests_enabled', '.merge-requests-feature');
|
||||
var self = this;
|
||||
|
||||
this.$selects.each(function () {
|
||||
var $select = $(this),
|
||||
className = $select.data('field').replace(/_/g, '-')
|
||||
.replace('access-level', 'feature');
|
||||
self._showOrHide($select, '.' + className);
|
||||
});
|
||||
};
|
||||
|
||||
ProjectNew.prototype.toggleSettingsOnclick = function() {
|
||||
return $('#project_builds_enabled, #project_merge_requests_enabled').on('click', this.toggleSettings);
|
||||
this.$selects.on('change', this.toggleSettings);
|
||||
};
|
||||
|
||||
ProjectNew.prototype._showOrHide = function(checkElement, container) {
|
||||
var $container;
|
||||
$container = $(container);
|
||||
if ($(checkElement).prop('checked')) {
|
||||
var $container = $(container);
|
||||
|
||||
if ($(checkElement).val() !== '0') {
|
||||
return $container.show();
|
||||
} else {
|
||||
return $container.hide();
|
||||
|
|
|
@ -7,3 +7,5 @@
|
|||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
// I kept class for future
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
$('.projects-list-holder').replaceWith(data.html);
|
||||
return history.replaceState({
|
||||
page: project_filter_url
|
||||
// Change url so if user reload a page - search results are saved
|
||||
}, document.title, project_filter_url);
|
||||
},
|
||||
dataType: "json"
|
||||
|
|
|
@ -45,6 +45,7 @@ class ProtectedBranchDropdown {
|
|||
}
|
||||
|
||||
onClickCreateWildcard() {
|
||||
// Refresh the dropdown's data, which ends up calling `getProtectedBranches`
|
||||
this.$dropdown.data('glDropdown').remote.execute();
|
||||
this.$dropdown.data('glDropdown').selectRowAtIndex(0);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue