New upstream version 8.12.1+dfsg1

This commit is contained in:
Praveen Arimbrathodiyil 2016-09-29 09:46:39 +05:30
parent 2f43f6b133
commit ab0093fc8a
1096 changed files with 26288 additions and 9715 deletions

View file

@ -1 +1,2 @@
*.erb
lib/gitlab/sanitizers/svg/whitelist.rb

1
.gitignore vendored
View file

@ -48,3 +48,4 @@
/vendor/bundle/*
/builds/*
/shared/*
/.gitlab_workhorse_secret

View file

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

View 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)

View file

@ -0,0 +1,7 @@
### Description
(Include problem, use cases, benefits, and/or goals)
### Proposal
### Links / references

View 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
View 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

View file

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

View file

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

View file

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

1966
CHANGELOG

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1 +1 @@
3.4.0
3.6.0

View file

@ -1 +1 @@
0.7.11
0.8.2

14
Gemfile
View file

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

View file

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

View file

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

View file

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

View file

@ -1 +1 @@
8.11.3
8.12.1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

View 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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,5 @@
/*= require jquery.ba-resize */
/*= require autosize */
(function() {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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() {

View file

@ -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({

View file

@ -54,4 +54,11 @@ $(() => {
});
}
});
gl.IssueBoardsSearch = new Vue({
el: '#js-boards-seach',
data: {
filters: Store.state.filters
}
});
});

View file

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

View file

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

View file

@ -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,6 +94,7 @@ class List {
}
addIssue (issue, listFrom) {
if (!this.findIssue(issue.id)) {
this.issues.push(issue);
if (this.label) {
@ -103,7 +102,12 @@ class List {
}
if (listFrom) {
gl.boardService.moveIssue(issue.id, listFrom.id, this.id);
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);
}

View file

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

View file

View file

@ -1,10 +1,7 @@
Vue.http.interceptors.push((request, next) => {
Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
Vue.nextTick(() => {
setTimeout(() => {
next(function (response) {
Vue.activeResources--;
}, 500);
});
next();
});

View file

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

View file

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

View file

@ -0,0 +1,6 @@
$(function(){
$('.reveal-variables').off('click').on('click',function(){
$('.js-build').toggle().niceScroll();
$(this).hide();
});
});

View file

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

View file

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

View file

@ -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 = '&#8984;';
key = '&#8984;'; // Command
} else {
key = 'Ctrl';
}

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,5 +3,4 @@
(function() {
}).call(this);

View file

@ -3,5 +3,4 @@
(function() {
}).call(this);

View file

@ -3,5 +3,4 @@
(function() {
}).call(this);

View file

@ -1,13 +1,8 @@
/*= require raphael */
/*= require g.raphael */
/*= require g.bar */
(function() {
}).call(this);

View file

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

View file

@ -1,2 +0,0 @@
gl.emojiAliases = ->
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')

View file

@ -0,0 +1,6 @@
(function() {
gl.emojiAliases = function() {
return JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>');
};
}).call(this);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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") {

View file

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

View file

@ -92,6 +92,7 @@
},
hidden: function() {
$selectbox.hide();
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
clicked: function(selected, $el, e) {

View file

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

View file

@ -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() {

View file

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

View file

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

View file

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

View file

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

View file

@ -3,5 +3,4 @@
(function() {
}).call(this);

View file

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

View file

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

View file

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

View file

@ -7,3 +7,5 @@
})();
}).call(this);
// I kept class for future

View file

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

View file

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